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/param.h> 328d59ecb2SHans Petter Selasky #include <sys/systm.h> 33937a05baSJustin Hibbits #include <sys/bus.h> 348d59ecb2SHans Petter Selasky #include <sys/malloc.h> 358d59ecb2SHans Petter Selasky #include <sys/kernel.h> 368d59ecb2SHans Petter Selasky #include <sys/sysctl.h> 378d59ecb2SHans Petter Selasky #include <sys/lock.h> 388d59ecb2SHans Petter Selasky #include <sys/mutex.h> 398d59ecb2SHans Petter Selasky #include <sys/fcntl.h> 408d59ecb2SHans Petter Selasky #include <sys/file.h> 418d59ecb2SHans Petter Selasky #include <sys/filio.h> 429275cd0dSWarner Losh #include <sys/pciio.h> 43f211d536STycho Nightingale #include <sys/pctrie.h> 4496ab16ebSVladimir Kondratyev #include <sys/rman.h> 458d59ecb2SHans Petter Selasky #include <sys/rwlock.h> 468d59ecb2SHans Petter Selasky 478d59ecb2SHans Petter Selasky #include <vm/vm.h> 488d59ecb2SHans Petter Selasky #include <vm/pmap.h> 498d59ecb2SHans Petter Selasky 5096ab16ebSVladimir Kondratyev #include <machine/bus.h> 5196ab16ebSVladimir Kondratyev #include <machine/resource.h> 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); 10196ab16ebSVladimir Kondratyev static void lkpi_pcim_iomap_table_release(struct device *, void *); 1028d59ecb2SHans Petter Selasky 1038d59ecb2SHans Petter Selasky static device_method_t pci_methods[] = { 1048d59ecb2SHans Petter Selasky DEVMETHOD(device_probe, linux_pci_probe), 1058d59ecb2SHans Petter Selasky DEVMETHOD(device_attach, linux_pci_attach), 1068d59ecb2SHans Petter Selasky DEVMETHOD(device_detach, linux_pci_detach), 1077d133393SHans Petter Selasky DEVMETHOD(device_suspend, linux_pci_suspend), 1087d133393SHans Petter Selasky DEVMETHOD(device_resume, linux_pci_resume), 1097d133393SHans Petter Selasky DEVMETHOD(device_shutdown, linux_pci_shutdown), 1102928e60eSKonstantin Belousov DEVMETHOD(pci_iov_init, linux_pci_iov_init), 1112928e60eSKonstantin Belousov DEVMETHOD(pci_iov_uninit, linux_pci_iov_uninit), 1122928e60eSKonstantin Belousov DEVMETHOD(pci_iov_add_vf, linux_pci_iov_add_vf), 1132b68c973SEmmanuel Vadot 1142b68c973SEmmanuel Vadot /* backlight interface */ 1152b68c973SEmmanuel Vadot DEVMETHOD(backlight_update_status, linux_backlight_update_status), 1162b68c973SEmmanuel Vadot DEVMETHOD(backlight_get_status, linux_backlight_get_status), 1172b68c973SEmmanuel Vadot DEVMETHOD(backlight_get_info, linux_backlight_get_info), 1188d59ecb2SHans Petter Selasky DEVMETHOD_END 1198d59ecb2SHans Petter Selasky }; 1208d59ecb2SHans Petter Selasky 1214cb3cb2dSJake Freeland const char *pci_power_names[] = { 1224cb3cb2dSJake Freeland "UNKNOWN", "D0", "D1", "D2", "D3hot", "D3cold" 1234cb3cb2dSJake Freeland }; 1244cb3cb2dSJake Freeland 12596ab16ebSVladimir Kondratyev /* We need some meta-struct to keep track of these for devres. */ 12696ab16ebSVladimir Kondratyev struct pci_devres { 12796ab16ebSVladimir Kondratyev bool enable_io; 12896ab16ebSVladimir Kondratyev /* PCIR_MAX_BAR_0 + 1 = 6 => BIT(0..5). */ 12996ab16ebSVladimir Kondratyev uint8_t region_mask; 13096ab16ebSVladimir Kondratyev struct resource *region_table[PCIR_MAX_BAR_0 + 1]; /* Not needed. */ 13196ab16ebSVladimir Kondratyev }; 13296ab16ebSVladimir Kondratyev struct pcim_iomap_devres { 13396ab16ebSVladimir Kondratyev void *mmio_table[PCIR_MAX_BAR_0 + 1]; 13496ab16ebSVladimir Kondratyev struct resource *res_table[PCIR_MAX_BAR_0 + 1]; 13596ab16ebSVladimir Kondratyev }; 13696ab16ebSVladimir Kondratyev 137f211d536STycho Nightingale struct linux_dma_priv { 138f211d536STycho Nightingale uint64_t dma_mask; 139f211d536STycho Nightingale bus_dma_tag_t dmat; 140c39eefe7SBjoern A. Zeeb uint64_t dma_coherent_mask; 141c39eefe7SBjoern A. Zeeb bus_dma_tag_t dmat_coherent; 142c39eefe7SBjoern A. Zeeb struct mtx lock; 143f211d536STycho Nightingale struct pctrie ptree; 144f211d536STycho Nightingale }; 145a6619e8dSHans Petter Selasky #define DMA_PRIV_LOCK(priv) mtx_lock(&(priv)->lock) 146a6619e8dSHans Petter Selasky #define DMA_PRIV_UNLOCK(priv) mtx_unlock(&(priv)->lock) 147f211d536STycho Nightingale 148f211d536STycho Nightingale static int 149f211d536STycho Nightingale linux_pdev_dma_uninit(struct pci_dev *pdev) 150f211d536STycho Nightingale { 151f211d536STycho Nightingale struct linux_dma_priv *priv; 152f211d536STycho Nightingale 153f211d536STycho Nightingale priv = pdev->dev.dma_priv; 154f211d536STycho Nightingale if (priv->dmat) 155f211d536STycho Nightingale bus_dma_tag_destroy(priv->dmat); 156c39eefe7SBjoern A. Zeeb if (priv->dmat_coherent) 157c39eefe7SBjoern A. Zeeb bus_dma_tag_destroy(priv->dmat_coherent); 158a6619e8dSHans Petter Selasky mtx_destroy(&priv->lock); 159f211d536STycho Nightingale pdev->dev.dma_priv = NULL; 160c39eefe7SBjoern A. Zeeb free(priv, M_DEVBUF); 161f211d536STycho Nightingale return (0); 162f211d536STycho Nightingale } 163f211d536STycho Nightingale 164c39eefe7SBjoern A. Zeeb static int 165c39eefe7SBjoern A. Zeeb linux_pdev_dma_init(struct pci_dev *pdev) 166c39eefe7SBjoern A. Zeeb { 167c39eefe7SBjoern A. Zeeb struct linux_dma_priv *priv; 168c39eefe7SBjoern A. Zeeb int error; 169c39eefe7SBjoern A. Zeeb 170c39eefe7SBjoern A. Zeeb priv = malloc(sizeof(*priv), M_DEVBUF, M_WAITOK | M_ZERO); 171c39eefe7SBjoern A. Zeeb 172c39eefe7SBjoern A. Zeeb mtx_init(&priv->lock, "lkpi-priv-dma", NULL, MTX_DEF); 173c39eefe7SBjoern A. Zeeb pctrie_init(&priv->ptree); 174c39eefe7SBjoern A. Zeeb 175c39eefe7SBjoern A. Zeeb pdev->dev.dma_priv = priv; 176c39eefe7SBjoern A. Zeeb 177c39eefe7SBjoern A. Zeeb /* Create a default DMA tags. */ 178c39eefe7SBjoern A. Zeeb error = linux_dma_tag_init(&pdev->dev, DMA_BIT_MASK(64)); 179c39eefe7SBjoern A. Zeeb if (error != 0) 180c39eefe7SBjoern A. Zeeb goto err; 181c39eefe7SBjoern A. Zeeb /* Coherent is lower 32bit only by default in Linux. */ 182c39eefe7SBjoern A. Zeeb error = linux_dma_tag_init_coherent(&pdev->dev, DMA_BIT_MASK(32)); 183c39eefe7SBjoern A. Zeeb if (error != 0) 184c39eefe7SBjoern A. Zeeb goto err; 185c39eefe7SBjoern A. Zeeb 186c39eefe7SBjoern A. Zeeb return (error); 187c39eefe7SBjoern A. Zeeb 188c39eefe7SBjoern A. Zeeb err: 189c39eefe7SBjoern A. Zeeb linux_pdev_dma_uninit(pdev); 190c39eefe7SBjoern A. Zeeb return (error); 191c39eefe7SBjoern A. Zeeb } 192c39eefe7SBjoern A. Zeeb 193f211d536STycho Nightingale int 194f211d536STycho Nightingale linux_dma_tag_init(struct device *dev, u64 dma_mask) 195f211d536STycho Nightingale { 196f211d536STycho Nightingale struct linux_dma_priv *priv; 197f211d536STycho Nightingale int error; 198f211d536STycho Nightingale 199f211d536STycho Nightingale priv = dev->dma_priv; 200f211d536STycho Nightingale 201f211d536STycho Nightingale if (priv->dmat) { 202f211d536STycho Nightingale if (priv->dma_mask == dma_mask) 203f211d536STycho Nightingale return (0); 204f211d536STycho Nightingale 205f211d536STycho Nightingale bus_dma_tag_destroy(priv->dmat); 206f211d536STycho Nightingale } 207f211d536STycho Nightingale 208f211d536STycho Nightingale priv->dma_mask = dma_mask; 209f211d536STycho Nightingale 210f211d536STycho Nightingale error = bus_dma_tag_create(bus_get_dma_tag(dev->bsddev), 211f211d536STycho Nightingale 1, 0, /* alignment, boundary */ 212f211d536STycho Nightingale dma_mask, /* lowaddr */ 213f211d536STycho Nightingale BUS_SPACE_MAXADDR, /* highaddr */ 214f211d536STycho Nightingale NULL, NULL, /* filtfunc, filtfuncarg */ 215b09626b3STycho Nightingale BUS_SPACE_MAXSIZE, /* maxsize */ 216f211d536STycho Nightingale 1, /* nsegments */ 217b09626b3STycho Nightingale BUS_SPACE_MAXSIZE, /* maxsegsz */ 218f211d536STycho Nightingale 0, /* flags */ 219f211d536STycho Nightingale NULL, NULL, /* lockfunc, lockfuncarg */ 220f211d536STycho Nightingale &priv->dmat); 221f211d536STycho Nightingale return (-error); 222f211d536STycho Nightingale } 223f211d536STycho Nightingale 224c39eefe7SBjoern A. Zeeb int 225c39eefe7SBjoern A. Zeeb linux_dma_tag_init_coherent(struct device *dev, u64 dma_mask) 226c39eefe7SBjoern A. Zeeb { 227c39eefe7SBjoern A. Zeeb struct linux_dma_priv *priv; 228c39eefe7SBjoern A. Zeeb int error; 229c39eefe7SBjoern A. Zeeb 230c39eefe7SBjoern A. Zeeb priv = dev->dma_priv; 231c39eefe7SBjoern A. Zeeb 232c39eefe7SBjoern A. Zeeb if (priv->dmat_coherent) { 233c39eefe7SBjoern A. Zeeb if (priv->dma_coherent_mask == dma_mask) 234c39eefe7SBjoern A. Zeeb return (0); 235c39eefe7SBjoern A. Zeeb 236c39eefe7SBjoern A. Zeeb bus_dma_tag_destroy(priv->dmat_coherent); 237c39eefe7SBjoern A. Zeeb } 238c39eefe7SBjoern A. Zeeb 239c39eefe7SBjoern A. Zeeb priv->dma_coherent_mask = dma_mask; 240c39eefe7SBjoern A. Zeeb 241c39eefe7SBjoern A. Zeeb error = bus_dma_tag_create(bus_get_dma_tag(dev->bsddev), 242c39eefe7SBjoern A. Zeeb 1, 0, /* alignment, boundary */ 243c39eefe7SBjoern A. Zeeb dma_mask, /* lowaddr */ 244c39eefe7SBjoern A. Zeeb BUS_SPACE_MAXADDR, /* highaddr */ 245c39eefe7SBjoern A. Zeeb NULL, NULL, /* filtfunc, filtfuncarg */ 246c39eefe7SBjoern A. Zeeb BUS_SPACE_MAXSIZE, /* maxsize */ 247c39eefe7SBjoern A. Zeeb 1, /* nsegments */ 248c39eefe7SBjoern A. Zeeb BUS_SPACE_MAXSIZE, /* maxsegsz */ 249c39eefe7SBjoern A. Zeeb 0, /* flags */ 250c39eefe7SBjoern A. Zeeb NULL, NULL, /* lockfunc, lockfuncarg */ 251c39eefe7SBjoern A. Zeeb &priv->dmat_coherent); 252c39eefe7SBjoern A. Zeeb return (-error); 253c39eefe7SBjoern A. Zeeb } 254c39eefe7SBjoern A. Zeeb 2558d59ecb2SHans Petter Selasky static struct pci_driver * 2568d59ecb2SHans Petter Selasky linux_pci_find(device_t dev, const struct pci_device_id **idp) 2578d59ecb2SHans Petter Selasky { 2588d59ecb2SHans Petter Selasky const struct pci_device_id *id; 2598d59ecb2SHans Petter Selasky struct pci_driver *pdrv; 2608d59ecb2SHans Petter Selasky uint16_t vendor; 2618d59ecb2SHans Petter Selasky uint16_t device; 262232028b3SHans Petter Selasky uint16_t subvendor; 263232028b3SHans Petter Selasky uint16_t subdevice; 2648d59ecb2SHans Petter Selasky 2658d59ecb2SHans Petter Selasky vendor = pci_get_vendor(dev); 2668d59ecb2SHans Petter Selasky device = pci_get_device(dev); 267232028b3SHans Petter Selasky subvendor = pci_get_subvendor(dev); 268232028b3SHans Petter Selasky subdevice = pci_get_subdevice(dev); 2698d59ecb2SHans Petter Selasky 2708d59ecb2SHans Petter Selasky spin_lock(&pci_lock); 271cf899348SBjoern A. Zeeb list_for_each_entry(pdrv, &pci_drivers, node) { 2728d59ecb2SHans Petter Selasky for (id = pdrv->id_table; id->vendor != 0; id++) { 273232028b3SHans Petter Selasky if (vendor == id->vendor && 274232028b3SHans Petter Selasky (PCI_ANY_ID == id->device || device == id->device) && 275232028b3SHans Petter Selasky (PCI_ANY_ID == id->subvendor || subvendor == id->subvendor) && 276232028b3SHans Petter Selasky (PCI_ANY_ID == id->subdevice || subdevice == id->subdevice)) { 2778d59ecb2SHans Petter Selasky *idp = id; 2788d59ecb2SHans Petter Selasky spin_unlock(&pci_lock); 2798d59ecb2SHans Petter Selasky return (pdrv); 2808d59ecb2SHans Petter Selasky } 2818d59ecb2SHans Petter Selasky } 2828d59ecb2SHans Petter Selasky } 2838d59ecb2SHans Petter Selasky spin_unlock(&pci_lock); 2848d59ecb2SHans Petter Selasky return (NULL); 2858d59ecb2SHans Petter Selasky } 2868d59ecb2SHans Petter Selasky 2878f61992dSBjoern A. Zeeb struct pci_dev * 2888f61992dSBjoern A. Zeeb lkpi_pci_get_device(uint16_t vendor, uint16_t device, struct pci_dev *odev) 2898f61992dSBjoern A. Zeeb { 2908f61992dSBjoern A. Zeeb struct pci_dev *pdev; 2918f61992dSBjoern A. Zeeb 2928f61992dSBjoern A. Zeeb KASSERT(odev == NULL, ("%s: odev argument not yet supported\n", __func__)); 2938f61992dSBjoern A. Zeeb 2948f61992dSBjoern A. Zeeb spin_lock(&pci_lock); 2958f61992dSBjoern A. Zeeb list_for_each_entry(pdev, &pci_devices, links) { 2968f61992dSBjoern A. Zeeb if (pdev->vendor == vendor && pdev->device == device) 2978f61992dSBjoern A. Zeeb break; 2988f61992dSBjoern A. Zeeb } 2998f61992dSBjoern A. Zeeb spin_unlock(&pci_lock); 3008f61992dSBjoern A. Zeeb 3018f61992dSBjoern A. Zeeb return (pdev); 3028f61992dSBjoern A. Zeeb } 3038f61992dSBjoern A. Zeeb 304105a37caSEmmanuel Vadot static void 3055f88df77SBjoern A. Zeeb lkpi_pci_dev_release(struct device *dev) 3065f88df77SBjoern A. Zeeb { 3075f88df77SBjoern A. Zeeb 3085f88df77SBjoern A. Zeeb lkpi_devres_release_free_list(dev); 3095f88df77SBjoern A. Zeeb spin_lock_destroy(&dev->devres_lock); 3105f88df77SBjoern A. Zeeb } 3115f88df77SBjoern A. Zeeb 3125f88df77SBjoern A. Zeeb static void 313105a37caSEmmanuel Vadot lkpifill_pci_dev(device_t dev, struct pci_dev *pdev) 314105a37caSEmmanuel Vadot { 315105a37caSEmmanuel Vadot 316105a37caSEmmanuel Vadot pdev->devfn = PCI_DEVFN(pci_get_slot(dev), pci_get_function(dev)); 317105a37caSEmmanuel Vadot pdev->vendor = pci_get_vendor(dev); 318105a37caSEmmanuel Vadot pdev->device = pci_get_device(dev); 3191fac2cb4SBjoern A. Zeeb pdev->subsystem_vendor = pci_get_subvendor(dev); 3201fac2cb4SBjoern A. Zeeb pdev->subsystem_device = pci_get_subdevice(dev); 321105a37caSEmmanuel Vadot pdev->class = pci_get_class(dev); 322105a37caSEmmanuel Vadot pdev->revision = pci_get_revid(dev); 3238a8e86b8SJean-Sébastien Pédron pdev->path_name = kasprintf(GFP_KERNEL, "%04d:%02d:%02d.%d", 324393b0ba2SVal Packett pci_get_domain(dev), pci_get_bus(dev), pci_get_slot(dev), 325393b0ba2SVal Packett pci_get_function(dev)); 3261fac2cb4SBjoern A. Zeeb pdev->bus = malloc(sizeof(*pdev->bus), M_DEVBUF, M_WAITOK | M_ZERO); 327b3b83625SBjoern A. Zeeb /* 328b3b83625SBjoern A. Zeeb * This should be the upstream bridge; pci_upstream_bridge() 329b3b83625SBjoern A. Zeeb * handles that case on demand as otherwise we'll shadow the 330b3b83625SBjoern A. Zeeb * entire PCI hierarchy. 331b3b83625SBjoern A. Zeeb */ 332105a37caSEmmanuel Vadot pdev->bus->self = pdev; 333105a37caSEmmanuel Vadot pdev->bus->number = pci_get_bus(dev); 334105a37caSEmmanuel Vadot pdev->bus->domain = pci_get_domain(dev); 3351fac2cb4SBjoern A. Zeeb pdev->dev.bsddev = dev; 3361fac2cb4SBjoern A. Zeeb pdev->dev.parent = &linux_root_device; 337d4a4960cSBjoern A. Zeeb pdev->dev.release = lkpi_pci_dev_release; 3381fac2cb4SBjoern A. Zeeb INIT_LIST_HEAD(&pdev->dev.irqents); 339b15491b4SBjoern A. Zeeb 340b15491b4SBjoern A. Zeeb if (pci_msi_count(dev) > 0) 341b15491b4SBjoern A. Zeeb pdev->msi_desc = malloc(pci_msi_count(dev) * 342b15491b4SBjoern A. Zeeb sizeof(*pdev->msi_desc), M_DEVBUF, M_WAITOK | M_ZERO); 343b15491b4SBjoern A. Zeeb 3441fac2cb4SBjoern A. Zeeb kobject_init(&pdev->dev.kobj, &linux_dev_ktype); 3451fac2cb4SBjoern A. Zeeb kobject_set_name(&pdev->dev.kobj, device_get_nameunit(dev)); 3461fac2cb4SBjoern A. Zeeb kobject_add(&pdev->dev.kobj, &linux_root_device.kobj, 3471fac2cb4SBjoern A. Zeeb kobject_name(&pdev->dev.kobj)); 348c3518147SBjoern A. Zeeb spin_lock_init(&pdev->dev.devres_lock); 349c3518147SBjoern A. Zeeb INIT_LIST_HEAD(&pdev->dev.devres_head); 3501fac2cb4SBjoern A. Zeeb } 3511fac2cb4SBjoern A. Zeeb 3521fac2cb4SBjoern A. Zeeb static void 3531fac2cb4SBjoern A. Zeeb lkpinew_pci_dev_release(struct device *dev) 3541fac2cb4SBjoern A. Zeeb { 3551fac2cb4SBjoern A. Zeeb struct pci_dev *pdev; 356b15491b4SBjoern A. Zeeb int i; 3571fac2cb4SBjoern A. Zeeb 3581fac2cb4SBjoern A. Zeeb pdev = to_pci_dev(dev); 3598e106c52SBjoern A. Zeeb if (pdev->root != NULL) 3608e106c52SBjoern A. Zeeb pci_dev_put(pdev->root); 361b3b83625SBjoern A. Zeeb if (pdev->bus->self != pdev) 362b3b83625SBjoern A. Zeeb pci_dev_put(pdev->bus->self); 3631fac2cb4SBjoern A. Zeeb free(pdev->bus, M_DEVBUF); 364b15491b4SBjoern A. Zeeb if (pdev->msi_desc != NULL) { 365b15491b4SBjoern A. Zeeb for (i = pci_msi_count(pdev->dev.bsddev) - 1; i >= 0; i--) 366b15491b4SBjoern A. Zeeb free(pdev->msi_desc[i], M_DEVBUF); 3674b56afafSBjoern A. Zeeb free(pdev->msi_desc, M_DEVBUF); 368b15491b4SBjoern A. Zeeb } 369393b0ba2SVal Packett kfree(pdev->path_name); 3701fac2cb4SBjoern A. Zeeb free(pdev, M_DEVBUF); 371105a37caSEmmanuel Vadot } 372105a37caSEmmanuel Vadot 3738e106c52SBjoern A. Zeeb struct pci_dev * 374105a37caSEmmanuel Vadot lkpinew_pci_dev(device_t dev) 375105a37caSEmmanuel Vadot { 376105a37caSEmmanuel Vadot struct pci_dev *pdev; 377105a37caSEmmanuel Vadot 378105a37caSEmmanuel Vadot pdev = malloc(sizeof(*pdev), M_DEVBUF, M_WAITOK|M_ZERO); 379105a37caSEmmanuel Vadot lkpifill_pci_dev(dev, pdev); 3801fac2cb4SBjoern A. Zeeb pdev->dev.release = lkpinew_pci_dev_release; 3811fac2cb4SBjoern A. Zeeb 382105a37caSEmmanuel Vadot return (pdev); 383105a37caSEmmanuel Vadot } 384105a37caSEmmanuel Vadot 385105a37caSEmmanuel Vadot struct pci_dev * 386105a37caSEmmanuel Vadot lkpi_pci_get_class(unsigned int class, struct pci_dev *from) 387105a37caSEmmanuel Vadot { 388105a37caSEmmanuel Vadot device_t dev; 389105a37caSEmmanuel Vadot device_t devfrom = NULL; 390105a37caSEmmanuel Vadot struct pci_dev *pdev; 391105a37caSEmmanuel Vadot 392105a37caSEmmanuel Vadot if (from != NULL) 393105a37caSEmmanuel Vadot devfrom = from->dev.bsddev; 394105a37caSEmmanuel Vadot 395105a37caSEmmanuel Vadot dev = pci_find_class_from(class >> 16, (class >> 8) & 0xFF, devfrom); 396105a37caSEmmanuel Vadot if (dev == NULL) 397105a37caSEmmanuel Vadot return (NULL); 398105a37caSEmmanuel Vadot 399105a37caSEmmanuel Vadot pdev = lkpinew_pci_dev(dev); 400105a37caSEmmanuel Vadot return (pdev); 401105a37caSEmmanuel Vadot } 402105a37caSEmmanuel Vadot 403105a37caSEmmanuel Vadot struct pci_dev * 404105a37caSEmmanuel Vadot lkpi_pci_get_domain_bus_and_slot(int domain, unsigned int bus, 405105a37caSEmmanuel Vadot unsigned int devfn) 406105a37caSEmmanuel Vadot { 407105a37caSEmmanuel Vadot device_t dev; 408105a37caSEmmanuel Vadot struct pci_dev *pdev; 409105a37caSEmmanuel Vadot 410105a37caSEmmanuel Vadot dev = pci_find_dbsf(domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); 411105a37caSEmmanuel Vadot if (dev == NULL) 412105a37caSEmmanuel Vadot return (NULL); 413105a37caSEmmanuel Vadot 414105a37caSEmmanuel Vadot pdev = lkpinew_pci_dev(dev); 415105a37caSEmmanuel Vadot return (pdev); 416105a37caSEmmanuel Vadot } 417105a37caSEmmanuel Vadot 4188d59ecb2SHans Petter Selasky static int 4198d59ecb2SHans Petter Selasky linux_pci_probe(device_t dev) 4208d59ecb2SHans Petter Selasky { 4218d59ecb2SHans Petter Selasky const struct pci_device_id *id; 4228d59ecb2SHans Petter Selasky struct pci_driver *pdrv; 4238d59ecb2SHans Petter Selasky 4248d59ecb2SHans Petter Selasky if ((pdrv = linux_pci_find(dev, &id)) == NULL) 4258d59ecb2SHans Petter Selasky return (ENXIO); 426b38dc0a1SMark Johnston if (device_get_driver(dev) != &pdrv->bsddriver) 4278d59ecb2SHans Petter Selasky return (ENXIO); 4288d59ecb2SHans Petter Selasky device_set_desc(dev, pdrv->name); 429b91dd79bSBjoern A. Zeeb 430b91dd79bSBjoern A. Zeeb /* Assume BSS initialized (should never return BUS_PROBE_SPECIFIC). */ 431b91dd79bSBjoern A. Zeeb if (pdrv->bsd_probe_return == 0) 43239d8c387SBjoern A. Zeeb return (BUS_PROBE_DEFAULT); 433b91dd79bSBjoern A. Zeeb else 434b91dd79bSBjoern A. Zeeb return (pdrv->bsd_probe_return); 4358d59ecb2SHans Petter Selasky } 4368d59ecb2SHans Petter Selasky 4378d59ecb2SHans Petter Selasky static int 4388d59ecb2SHans Petter Selasky linux_pci_attach(device_t dev) 4398d59ecb2SHans Petter Selasky { 440253dbe74SHans Petter Selasky const struct pci_device_id *id; 441253dbe74SHans Petter Selasky struct pci_driver *pdrv; 442253dbe74SHans Petter Selasky struct pci_dev *pdev; 443253dbe74SHans Petter Selasky 444253dbe74SHans Petter Selasky pdrv = linux_pci_find(dev, &id); 445253dbe74SHans Petter Selasky pdev = device_get_softc(dev); 446253dbe74SHans Petter Selasky 447253dbe74SHans Petter Selasky MPASS(pdrv != NULL); 448253dbe74SHans Petter Selasky MPASS(pdev != NULL); 449253dbe74SHans Petter Selasky 450253dbe74SHans Petter Selasky return (linux_pci_attach_device(dev, pdrv, id, pdev)); 451253dbe74SHans Petter Selasky } 452253dbe74SHans Petter Selasky 45396ab16ebSVladimir Kondratyev static struct resource_list_entry * 45496ab16ebSVladimir Kondratyev linux_pci_reserve_bar(struct pci_dev *pdev, struct resource_list *rl, 45596ab16ebSVladimir Kondratyev int type, int rid) 45696ab16ebSVladimir Kondratyev { 45796ab16ebSVladimir Kondratyev device_t dev; 45896ab16ebSVladimir Kondratyev struct resource *res; 45996ab16ebSVladimir Kondratyev 46096ab16ebSVladimir Kondratyev KASSERT(type == SYS_RES_IOPORT || type == SYS_RES_MEMORY, 46196ab16ebSVladimir Kondratyev ("trying to reserve non-BAR type %d", type)); 46296ab16ebSVladimir Kondratyev 46396ab16ebSVladimir Kondratyev dev = pdev->pdrv != NULL && pdev->pdrv->isdrm ? 46496ab16ebSVladimir Kondratyev device_get_parent(pdev->dev.bsddev) : pdev->dev.bsddev; 46596ab16ebSVladimir Kondratyev res = pci_reserve_map(device_get_parent(dev), dev, type, &rid, 0, ~0, 46696ab16ebSVladimir Kondratyev 1, 1, 0); 46796ab16ebSVladimir Kondratyev if (res == NULL) 46896ab16ebSVladimir Kondratyev return (NULL); 46996ab16ebSVladimir Kondratyev return (resource_list_find(rl, type, rid)); 47096ab16ebSVladimir Kondratyev } 47196ab16ebSVladimir Kondratyev 47296ab16ebSVladimir Kondratyev static struct resource_list_entry * 47396ab16ebSVladimir Kondratyev linux_pci_get_rle(struct pci_dev *pdev, int type, int rid, bool reserve_bar) 47496ab16ebSVladimir Kondratyev { 47596ab16ebSVladimir Kondratyev struct pci_devinfo *dinfo; 47696ab16ebSVladimir Kondratyev struct resource_list *rl; 47796ab16ebSVladimir Kondratyev struct resource_list_entry *rle; 47896ab16ebSVladimir Kondratyev 47996ab16ebSVladimir Kondratyev dinfo = device_get_ivars(pdev->dev.bsddev); 48096ab16ebSVladimir Kondratyev rl = &dinfo->resources; 48196ab16ebSVladimir Kondratyev rle = resource_list_find(rl, type, rid); 48296ab16ebSVladimir Kondratyev /* Reserve resources for this BAR if needed. */ 48396ab16ebSVladimir Kondratyev if (rle == NULL && reserve_bar) 48496ab16ebSVladimir Kondratyev rle = linux_pci_reserve_bar(pdev, rl, type, rid); 48596ab16ebSVladimir Kondratyev return (rle); 48696ab16ebSVladimir Kondratyev } 48796ab16ebSVladimir Kondratyev 488253dbe74SHans Petter Selasky int 489253dbe74SHans Petter Selasky linux_pci_attach_device(device_t dev, struct pci_driver *pdrv, 490253dbe74SHans Petter Selasky const struct pci_device_id *id, struct pci_dev *pdev) 491253dbe74SHans Petter Selasky { 4928d59ecb2SHans Petter Selasky struct resource_list_entry *rle; 4930b7bd01aSMark Johnston device_t parent; 494de27805fSKonstantin Belousov uintptr_t rid; 4958d59ecb2SHans Petter Selasky int error; 496de27805fSKonstantin Belousov bool isdrm; 4978d59ecb2SHans Petter Selasky 4981e3db1deSHans Petter Selasky linux_set_current(curthread); 4990b7bd01aSMark Johnston 5000b7bd01aSMark Johnston parent = device_get_parent(dev); 501de27805fSKonstantin Belousov isdrm = pdrv != NULL && pdrv->isdrm; 502de27805fSKonstantin Belousov 503de27805fSKonstantin Belousov if (isdrm) { 5041fac2cb4SBjoern A. Zeeb struct pci_devinfo *dinfo; 5051fac2cb4SBjoern A. Zeeb 5060b7bd01aSMark Johnston dinfo = device_get_ivars(parent); 5070b7bd01aSMark Johnston device_set_ivars(dev, dinfo); 5080b7bd01aSMark Johnston } 5090b7bd01aSMark Johnston 510105a37caSEmmanuel Vadot lkpifill_pci_dev(dev, pdev); 511de27805fSKonstantin Belousov if (isdrm) 512de27805fSKonstantin Belousov PCI_GET_ID(device_get_parent(parent), parent, PCI_ID_RID, &rid); 513de27805fSKonstantin Belousov else 514de27805fSKonstantin Belousov PCI_GET_ID(parent, dev, PCI_ID_RID, &rid); 515de27805fSKonstantin Belousov pdev->devfn = rid; 5168d59ecb2SHans Petter Selasky pdev->pdrv = pdrv; 51782098c8bSJessica Clarke rle = linux_pci_get_rle(pdev, SYS_RES_IRQ, 0, false); 518e996b07cSHans Petter Selasky if (rle != NULL) 5198d59ecb2SHans Petter Selasky pdev->dev.irq = rle->start; 5208d59ecb2SHans Petter Selasky else 5210a61267aSHans Petter Selasky pdev->dev.irq = LINUX_IRQ_INVALID; 5228d59ecb2SHans Petter Selasky pdev->irq = pdev->dev.irq; 523f211d536STycho Nightingale error = linux_pdev_dma_init(pdev); 524f211d536STycho Nightingale if (error) 525eb6f5342SHans Petter Selasky goto out_dma_init; 5260b7bd01aSMark Johnston 5274c274849SEmmanuel Vadot TAILQ_INIT(&pdev->mmio); 528808ae4e2SVladimir Kondratyev spin_lock_init(&pdev->pcie_cap_lock); 5290b7bd01aSMark Johnston 5308d59ecb2SHans Petter Selasky spin_lock(&pci_lock); 5318d59ecb2SHans Petter Selasky list_add(&pdev->links, &pci_devices); 5328d59ecb2SHans Petter Selasky spin_unlock(&pci_lock); 5334b706099SHans Petter Selasky 534253dbe74SHans Petter Selasky if (pdrv != NULL) { 5358d59ecb2SHans Petter Selasky error = pdrv->probe(pdev, id); 536eb6f5342SHans Petter Selasky if (error) 537eb6f5342SHans Petter Selasky goto out_probe; 538253dbe74SHans Petter Selasky } 539eb6f5342SHans Petter Selasky return (0); 540eb6f5342SHans Petter Selasky 541eb6f5342SHans Petter Selasky out_probe: 542e2eb11e5SHans Petter Selasky free(pdev->bus, M_DEVBUF); 543808ae4e2SVladimir Kondratyev spin_lock_destroy(&pdev->pcie_cap_lock); 544eb6f5342SHans Petter Selasky linux_pdev_dma_uninit(pdev); 545eb6f5342SHans Petter Selasky out_dma_init: 5468d59ecb2SHans Petter Selasky spin_lock(&pci_lock); 5478d59ecb2SHans Petter Selasky list_del(&pdev->links); 5488d59ecb2SHans Petter Selasky spin_unlock(&pci_lock); 5498d59ecb2SHans Petter Selasky put_device(&pdev->dev); 550eb6f5342SHans Petter Selasky return (-error); 5518d59ecb2SHans Petter Selasky } 5528d59ecb2SHans Petter Selasky 5538d59ecb2SHans Petter Selasky static int 5548d59ecb2SHans Petter Selasky linux_pci_detach(device_t dev) 5558d59ecb2SHans Petter Selasky { 5568d59ecb2SHans Petter Selasky struct pci_dev *pdev; 5578d59ecb2SHans Petter Selasky 5588d59ecb2SHans Petter Selasky pdev = device_get_softc(dev); 5594b706099SHans Petter Selasky 560253dbe74SHans Petter Selasky MPASS(pdev != NULL); 561253dbe74SHans Petter Selasky 562253dbe74SHans Petter Selasky device_set_desc(dev, NULL); 563253dbe74SHans Petter Selasky 564253dbe74SHans Petter Selasky return (linux_pci_detach_device(pdev)); 565253dbe74SHans Petter Selasky } 566253dbe74SHans Petter Selasky 567253dbe74SHans Petter Selasky int 568253dbe74SHans Petter Selasky linux_pci_detach_device(struct pci_dev *pdev) 569253dbe74SHans Petter Selasky { 570253dbe74SHans Petter Selasky 571253dbe74SHans Petter Selasky linux_set_current(curthread); 572253dbe74SHans Petter Selasky 573253dbe74SHans Petter Selasky if (pdev->pdrv != NULL) 5748d59ecb2SHans Petter Selasky pdev->pdrv->remove(pdev); 575e2eb11e5SHans Petter Selasky 5768e106c52SBjoern A. Zeeb if (pdev->root != NULL) 5778e106c52SBjoern A. Zeeb pci_dev_put(pdev->root); 578e2eb11e5SHans Petter Selasky free(pdev->bus, M_DEVBUF); 579f211d536STycho Nightingale linux_pdev_dma_uninit(pdev); 5804b706099SHans Petter Selasky 5818d59ecb2SHans Petter Selasky spin_lock(&pci_lock); 5828d59ecb2SHans Petter Selasky list_del(&pdev->links); 5838d59ecb2SHans Petter Selasky spin_unlock(&pci_lock); 584808ae4e2SVladimir Kondratyev spin_lock_destroy(&pdev->pcie_cap_lock); 5858d59ecb2SHans Petter Selasky put_device(&pdev->dev); 5868d59ecb2SHans Petter Selasky 5878d59ecb2SHans Petter Selasky return (0); 5888d59ecb2SHans Petter Selasky } 5898d59ecb2SHans Petter Selasky 5907d133393SHans Petter Selasky static int 591d4a4960cSBjoern A. Zeeb lkpi_pci_disable_dev(struct device *dev) 592d4a4960cSBjoern A. Zeeb { 593d4a4960cSBjoern A. Zeeb 594d4a4960cSBjoern A. Zeeb (void) pci_disable_io(dev->bsddev, SYS_RES_MEMORY); 595d4a4960cSBjoern A. Zeeb (void) pci_disable_io(dev->bsddev, SYS_RES_IOPORT); 596d4a4960cSBjoern A. Zeeb return (0); 597d4a4960cSBjoern A. Zeeb } 598d4a4960cSBjoern A. Zeeb 59996ab16ebSVladimir Kondratyev static struct pci_devres * 6003ea682e2SWarner Losh lkpi_pci_devres_get_alloc(struct pci_dev *pdev) 6013ea682e2SWarner Losh { 6023ea682e2SWarner Losh struct pci_devres *dr; 6033ea682e2SWarner Losh 6043ea682e2SWarner Losh dr = lkpi_devres_find(&pdev->dev, lkpi_pci_devres_release, NULL, NULL); 6053ea682e2SWarner Losh if (dr == NULL) { 6063ea682e2SWarner Losh dr = lkpi_devres_alloc(lkpi_pci_devres_release, sizeof(*dr), 6073ea682e2SWarner Losh GFP_KERNEL | __GFP_ZERO); 6083ea682e2SWarner Losh if (dr != NULL) 6093ea682e2SWarner Losh lkpi_devres_add(&pdev->dev, dr); 6103ea682e2SWarner Losh } 6113ea682e2SWarner Losh 6123ea682e2SWarner Losh return (dr); 6133ea682e2SWarner Losh } 6143ea682e2SWarner Losh 61596ab16ebSVladimir Kondratyev static struct pci_devres * 61696ab16ebSVladimir Kondratyev lkpi_pci_devres_find(struct pci_dev *pdev) 61796ab16ebSVladimir Kondratyev { 61896ab16ebSVladimir Kondratyev if (!pdev->managed) 61996ab16ebSVladimir Kondratyev return (NULL); 62096ab16ebSVladimir Kondratyev 62196ab16ebSVladimir Kondratyev return (lkpi_pci_devres_get_alloc(pdev)); 62296ab16ebSVladimir Kondratyev } 62396ab16ebSVladimir Kondratyev 624d4a4960cSBjoern A. Zeeb void 625d4a4960cSBjoern A. Zeeb lkpi_pci_devres_release(struct device *dev, void *p) 626d4a4960cSBjoern A. Zeeb { 627d4a4960cSBjoern A. Zeeb struct pci_devres *dr; 628d4a4960cSBjoern A. Zeeb struct pci_dev *pdev; 629d4a4960cSBjoern A. Zeeb int bar; 630d4a4960cSBjoern A. Zeeb 631d4a4960cSBjoern A. Zeeb pdev = to_pci_dev(dev); 632d4a4960cSBjoern A. Zeeb dr = p; 633d4a4960cSBjoern A. Zeeb 634d4a4960cSBjoern A. Zeeb if (pdev->msix_enabled) 635d4a4960cSBjoern A. Zeeb lkpi_pci_disable_msix(pdev); 636d4a4960cSBjoern A. Zeeb if (pdev->msi_enabled) 637d4a4960cSBjoern A. Zeeb lkpi_pci_disable_msi(pdev); 638d4a4960cSBjoern A. Zeeb 639d4a4960cSBjoern A. Zeeb if (dr->enable_io && lkpi_pci_disable_dev(dev) == 0) 640d4a4960cSBjoern A. Zeeb dr->enable_io = false; 641d4a4960cSBjoern A. Zeeb 642d4a4960cSBjoern A. Zeeb if (dr->region_mask == 0) 643d4a4960cSBjoern A. Zeeb return; 644d4a4960cSBjoern A. Zeeb for (bar = PCIR_MAX_BAR_0; bar >= 0; bar--) { 645d4a4960cSBjoern A. Zeeb 646d4a4960cSBjoern A. Zeeb if ((dr->region_mask & (1 << bar)) == 0) 647d4a4960cSBjoern A. Zeeb continue; 648d4a4960cSBjoern A. Zeeb pci_release_region(pdev, bar); 649d4a4960cSBjoern A. Zeeb } 650d4a4960cSBjoern A. Zeeb } 651d4a4960cSBjoern A. Zeeb 65296ab16ebSVladimir Kondratyev int 65396ab16ebSVladimir Kondratyev linuxkpi_pcim_enable_device(struct pci_dev *pdev) 65496ab16ebSVladimir Kondratyev { 65596ab16ebSVladimir Kondratyev struct pci_devres *dr; 65696ab16ebSVladimir Kondratyev int error; 65796ab16ebSVladimir Kondratyev 65896ab16ebSVladimir Kondratyev /* Here we cannot run through the pdev->managed check. */ 65996ab16ebSVladimir Kondratyev dr = lkpi_pci_devres_get_alloc(pdev); 66096ab16ebSVladimir Kondratyev if (dr == NULL) 66196ab16ebSVladimir Kondratyev return (-ENOMEM); 66296ab16ebSVladimir Kondratyev 66396ab16ebSVladimir Kondratyev /* If resources were enabled before do not do it again. */ 66496ab16ebSVladimir Kondratyev if (dr->enable_io) 66596ab16ebSVladimir Kondratyev return (0); 66696ab16ebSVladimir Kondratyev 66796ab16ebSVladimir Kondratyev error = pci_enable_device(pdev); 66896ab16ebSVladimir Kondratyev if (error == 0) 66996ab16ebSVladimir Kondratyev dr->enable_io = true; 67096ab16ebSVladimir Kondratyev 67196ab16ebSVladimir Kondratyev /* This device is not managed. */ 67296ab16ebSVladimir Kondratyev pdev->managed = true; 67396ab16ebSVladimir Kondratyev 67496ab16ebSVladimir Kondratyev return (error); 67596ab16ebSVladimir Kondratyev } 67696ab16ebSVladimir Kondratyev 67796ab16ebSVladimir Kondratyev static struct pcim_iomap_devres * 6782bf3361dSWarner Losh lkpi_pcim_iomap_devres_find(struct pci_dev *pdev) 6792bf3361dSWarner Losh { 6802bf3361dSWarner Losh struct pcim_iomap_devres *dr; 6812bf3361dSWarner Losh 6822bf3361dSWarner Losh dr = lkpi_devres_find(&pdev->dev, lkpi_pcim_iomap_table_release, 6832bf3361dSWarner Losh NULL, NULL); 6842bf3361dSWarner Losh if (dr == NULL) { 6852bf3361dSWarner Losh dr = lkpi_devres_alloc(lkpi_pcim_iomap_table_release, 6862bf3361dSWarner Losh sizeof(*dr), GFP_KERNEL | __GFP_ZERO); 6872bf3361dSWarner Losh if (dr != NULL) 6882bf3361dSWarner Losh lkpi_devres_add(&pdev->dev, dr); 6892bf3361dSWarner Losh } 6902bf3361dSWarner Losh 6912bf3361dSWarner Losh if (dr == NULL) 6922bf3361dSWarner Losh device_printf(pdev->dev.bsddev, "%s: NULL\n", __func__); 6932bf3361dSWarner Losh 6942bf3361dSWarner Losh return (dr); 6952bf3361dSWarner Losh } 6962bf3361dSWarner Losh 69796ab16ebSVladimir Kondratyev void __iomem ** 69896ab16ebSVladimir Kondratyev linuxkpi_pcim_iomap_table(struct pci_dev *pdev) 69996ab16ebSVladimir Kondratyev { 70096ab16ebSVladimir Kondratyev struct pcim_iomap_devres *dr; 70196ab16ebSVladimir Kondratyev 70296ab16ebSVladimir Kondratyev dr = lkpi_pcim_iomap_devres_find(pdev); 70396ab16ebSVladimir Kondratyev if (dr == NULL) 70496ab16ebSVladimir Kondratyev return (NULL); 70596ab16ebSVladimir Kondratyev 70696ab16ebSVladimir Kondratyev /* 70796ab16ebSVladimir Kondratyev * If the driver has manually set a flag to be able to request the 70896ab16ebSVladimir Kondratyev * resource to use bus_read/write_<n>, return the shadow table. 70996ab16ebSVladimir Kondratyev */ 71096ab16ebSVladimir Kondratyev if (pdev->want_iomap_res) 71196ab16ebSVladimir Kondratyev return ((void **)dr->res_table); 71296ab16ebSVladimir Kondratyev 71396ab16ebSVladimir Kondratyev /* This is the Linux default. */ 71496ab16ebSVladimir Kondratyev return (dr->mmio_table); 71596ab16ebSVladimir Kondratyev } 71696ab16ebSVladimir Kondratyev 71796ab16ebSVladimir Kondratyev static struct resource * 71896ab16ebSVladimir Kondratyev _lkpi_pci_iomap(struct pci_dev *pdev, int bar, int mmio_size __unused) 71996ab16ebSVladimir Kondratyev { 72096ab16ebSVladimir Kondratyev struct pci_mmio_region *mmio, *p; 72196ab16ebSVladimir Kondratyev int type; 72296ab16ebSVladimir Kondratyev 72396ab16ebSVladimir Kondratyev type = pci_resource_type(pdev, bar); 72496ab16ebSVladimir Kondratyev if (type < 0) { 72596ab16ebSVladimir Kondratyev device_printf(pdev->dev.bsddev, "%s: bar %d type %d\n", 72696ab16ebSVladimir Kondratyev __func__, bar, type); 72796ab16ebSVladimir Kondratyev return (NULL); 72896ab16ebSVladimir Kondratyev } 72996ab16ebSVladimir Kondratyev 73096ab16ebSVladimir Kondratyev /* 73196ab16ebSVladimir Kondratyev * Check for duplicate mappings. 73296ab16ebSVladimir Kondratyev * This can happen if a driver calls pci_request_region() first. 73396ab16ebSVladimir Kondratyev */ 73496ab16ebSVladimir Kondratyev TAILQ_FOREACH_SAFE(mmio, &pdev->mmio, next, p) { 73596ab16ebSVladimir Kondratyev if (mmio->type == type && mmio->rid == PCIR_BAR(bar)) { 73696ab16ebSVladimir Kondratyev return (mmio->res); 73796ab16ebSVladimir Kondratyev } 73896ab16ebSVladimir Kondratyev } 73996ab16ebSVladimir Kondratyev 74096ab16ebSVladimir Kondratyev mmio = malloc(sizeof(*mmio), M_DEVBUF, M_WAITOK | M_ZERO); 74196ab16ebSVladimir Kondratyev mmio->rid = PCIR_BAR(bar); 74296ab16ebSVladimir Kondratyev mmio->type = type; 74396ab16ebSVladimir Kondratyev mmio->res = bus_alloc_resource_any(pdev->dev.bsddev, mmio->type, 74496ab16ebSVladimir Kondratyev &mmio->rid, RF_ACTIVE|RF_SHAREABLE); 74596ab16ebSVladimir Kondratyev if (mmio->res == NULL) { 74696ab16ebSVladimir Kondratyev device_printf(pdev->dev.bsddev, "%s: failed to alloc " 74796ab16ebSVladimir Kondratyev "bar %d type %d rid %d\n", 74896ab16ebSVladimir Kondratyev __func__, bar, type, PCIR_BAR(bar)); 74996ab16ebSVladimir Kondratyev free(mmio, M_DEVBUF); 75096ab16ebSVladimir Kondratyev return (NULL); 75196ab16ebSVladimir Kondratyev } 75296ab16ebSVladimir Kondratyev TAILQ_INSERT_TAIL(&pdev->mmio, mmio, next); 75396ab16ebSVladimir Kondratyev 75496ab16ebSVladimir Kondratyev return (mmio->res); 75596ab16ebSVladimir Kondratyev } 75696ab16ebSVladimir Kondratyev 75796ab16ebSVladimir Kondratyev void * 758fcc350c3SVladimir Kondratyev linuxkpi_pci_iomap_range(struct pci_dev *pdev, int mmio_bar, 759fcc350c3SVladimir Kondratyev unsigned long mmio_off, unsigned long mmio_size) 76096ab16ebSVladimir Kondratyev { 76196ab16ebSVladimir Kondratyev struct resource *res; 76296ab16ebSVladimir Kondratyev 76396ab16ebSVladimir Kondratyev res = _lkpi_pci_iomap(pdev, mmio_bar, mmio_size); 76496ab16ebSVladimir Kondratyev if (res == NULL) 76596ab16ebSVladimir Kondratyev return (NULL); 76696ab16ebSVladimir Kondratyev /* This is a FreeBSD extension so we can use bus_*(). */ 76796ab16ebSVladimir Kondratyev if (pdev->want_iomap_res) 76896ab16ebSVladimir Kondratyev return (res); 769fcc350c3SVladimir Kondratyev MPASS(mmio_off < rman_get_size(res)); 770fcc350c3SVladimir Kondratyev return ((void *)(rman_get_bushandle(res) + mmio_off)); 771fcc350c3SVladimir Kondratyev } 772fcc350c3SVladimir Kondratyev 773fcc350c3SVladimir Kondratyev void * 774fcc350c3SVladimir Kondratyev linuxkpi_pci_iomap(struct pci_dev *pdev, int mmio_bar, int mmio_size) 775fcc350c3SVladimir Kondratyev { 776fcc350c3SVladimir Kondratyev return (linuxkpi_pci_iomap_range(pdev, mmio_bar, 0, mmio_size)); 77796ab16ebSVladimir Kondratyev } 77896ab16ebSVladimir Kondratyev 779d4a4960cSBjoern A. Zeeb void 78096ab16ebSVladimir Kondratyev linuxkpi_pci_iounmap(struct pci_dev *pdev, void *res) 78196ab16ebSVladimir Kondratyev { 78296ab16ebSVladimir Kondratyev struct pci_mmio_region *mmio, *p; 78314fc33eaSVladimir Kondratyev bus_space_handle_t bh = (bus_space_handle_t)res; 78496ab16ebSVladimir Kondratyev 78596ab16ebSVladimir Kondratyev TAILQ_FOREACH_SAFE(mmio, &pdev->mmio, next, p) { 78614fc33eaSVladimir Kondratyev if (pdev->want_iomap_res) { 78714fc33eaSVladimir Kondratyev if (res != mmio->res) 78814fc33eaSVladimir Kondratyev continue; 78914fc33eaSVladimir Kondratyev } else { 79014fc33eaSVladimir Kondratyev if (bh < rman_get_bushandle(mmio->res) || 79114fc33eaSVladimir Kondratyev bh >= rman_get_bushandle(mmio->res) + 792fcc350c3SVladimir Kondratyev rman_get_size(mmio->res)) 79396ab16ebSVladimir Kondratyev continue; 79414fc33eaSVladimir Kondratyev } 79596ab16ebSVladimir Kondratyev bus_release_resource(pdev->dev.bsddev, 79696ab16ebSVladimir Kondratyev mmio->type, mmio->rid, mmio->res); 79796ab16ebSVladimir Kondratyev TAILQ_REMOVE(&pdev->mmio, mmio, next); 79896ab16ebSVladimir Kondratyev free(mmio, M_DEVBUF); 79996ab16ebSVladimir Kondratyev return; 80096ab16ebSVladimir Kondratyev } 80196ab16ebSVladimir Kondratyev } 80296ab16ebSVladimir Kondratyev 80396ab16ebSVladimir Kondratyev int 80496ab16ebSVladimir Kondratyev linuxkpi_pcim_iomap_regions(struct pci_dev *pdev, uint32_t mask, const char *name) 80596ab16ebSVladimir Kondratyev { 80696ab16ebSVladimir Kondratyev struct pcim_iomap_devres *dr; 80796ab16ebSVladimir Kondratyev void *res; 80896ab16ebSVladimir Kondratyev uint32_t mappings; 80996ab16ebSVladimir Kondratyev int bar; 81096ab16ebSVladimir Kondratyev 81196ab16ebSVladimir Kondratyev dr = lkpi_pcim_iomap_devres_find(pdev); 81296ab16ebSVladimir Kondratyev if (dr == NULL) 81396ab16ebSVladimir Kondratyev return (-ENOMEM); 81496ab16ebSVladimir Kondratyev 81596ab16ebSVladimir Kondratyev /* Now iomap all the requested (by "mask") ones. */ 81696ab16ebSVladimir Kondratyev for (bar = mappings = 0; mappings != mask; bar++) { 81796ab16ebSVladimir Kondratyev if ((mask & (1 << bar)) == 0) 81896ab16ebSVladimir Kondratyev continue; 81996ab16ebSVladimir Kondratyev 82096ab16ebSVladimir Kondratyev /* Request double is not allowed. */ 82196ab16ebSVladimir Kondratyev if (dr->mmio_table[bar] != NULL) { 82296ab16ebSVladimir Kondratyev device_printf(pdev->dev.bsddev, "%s: bar %d %p\n", 82396ab16ebSVladimir Kondratyev __func__, bar, dr->mmio_table[bar]); 82496ab16ebSVladimir Kondratyev goto err; 82596ab16ebSVladimir Kondratyev } 82696ab16ebSVladimir Kondratyev 82796ab16ebSVladimir Kondratyev res = _lkpi_pci_iomap(pdev, bar, 0); 82896ab16ebSVladimir Kondratyev if (res == NULL) 82996ab16ebSVladimir Kondratyev goto err; 83096ab16ebSVladimir Kondratyev dr->mmio_table[bar] = (void *)rman_get_bushandle(res); 83196ab16ebSVladimir Kondratyev dr->res_table[bar] = res; 83296ab16ebSVladimir Kondratyev 83396ab16ebSVladimir Kondratyev mappings |= (1 << bar); 83496ab16ebSVladimir Kondratyev } 83596ab16ebSVladimir Kondratyev 83696ab16ebSVladimir Kondratyev return (0); 83796ab16ebSVladimir Kondratyev err: 83896ab16ebSVladimir Kondratyev for (bar = PCIR_MAX_BAR_0; bar >= 0; bar--) { 83996ab16ebSVladimir Kondratyev if ((mappings & (1 << bar)) != 0) { 84096ab16ebSVladimir Kondratyev res = dr->mmio_table[bar]; 84196ab16ebSVladimir Kondratyev if (res == NULL) 84296ab16ebSVladimir Kondratyev continue; 84396ab16ebSVladimir Kondratyev pci_iounmap(pdev, res); 84496ab16ebSVladimir Kondratyev } 84596ab16ebSVladimir Kondratyev } 84696ab16ebSVladimir Kondratyev 84796ab16ebSVladimir Kondratyev return (-EINVAL); 84896ab16ebSVladimir Kondratyev } 84996ab16ebSVladimir Kondratyev 85096ab16ebSVladimir Kondratyev static void 851d4a4960cSBjoern A. Zeeb lkpi_pcim_iomap_table_release(struct device *dev, void *p) 852d4a4960cSBjoern A. Zeeb { 853d4a4960cSBjoern A. Zeeb struct pcim_iomap_devres *dr; 854d4a4960cSBjoern A. Zeeb struct pci_dev *pdev; 855d4a4960cSBjoern A. Zeeb int bar; 856d4a4960cSBjoern A. Zeeb 857d4a4960cSBjoern A. Zeeb dr = p; 858d4a4960cSBjoern A. Zeeb pdev = to_pci_dev(dev); 859d4a4960cSBjoern A. Zeeb for (bar = PCIR_MAX_BAR_0; bar >= 0; bar--) { 860d4a4960cSBjoern A. Zeeb 861d4a4960cSBjoern A. Zeeb if (dr->mmio_table[bar] == NULL) 862d4a4960cSBjoern A. Zeeb continue; 863d4a4960cSBjoern A. Zeeb 864d4a4960cSBjoern A. Zeeb pci_iounmap(pdev, dr->mmio_table[bar]); 865d4a4960cSBjoern A. Zeeb } 866d4a4960cSBjoern A. Zeeb } 867d4a4960cSBjoern A. Zeeb 868d4a4960cSBjoern A. Zeeb static int 8697d133393SHans Petter Selasky linux_pci_suspend(device_t dev) 8707d133393SHans Petter Selasky { 871d34188a0SMark Johnston const struct dev_pm_ops *pmops; 8727d133393SHans Petter Selasky struct pm_message pm = { }; 8737d133393SHans Petter Selasky struct pci_dev *pdev; 874d34188a0SMark Johnston int error; 8757d133393SHans Petter Selasky 876d34188a0SMark Johnston error = 0; 8771e3db1deSHans Petter Selasky linux_set_current(curthread); 8787d133393SHans Petter Selasky pdev = device_get_softc(dev); 879d34188a0SMark Johnston pmops = pdev->pdrv->driver.pm; 880d34188a0SMark Johnston 8817d133393SHans Petter Selasky if (pdev->pdrv->suspend != NULL) 882d34188a0SMark Johnston error = -pdev->pdrv->suspend(pdev, pm); 883d34188a0SMark Johnston else if (pmops != NULL && pmops->suspend != NULL) { 884d34188a0SMark Johnston error = -pmops->suspend(&pdev->dev); 885d34188a0SMark Johnston if (error == 0 && pmops->suspend_late != NULL) 886d34188a0SMark Johnston error = -pmops->suspend_late(&pdev->dev); 88759cbead6SJean-Sébastien Pédron if (error == 0 && pmops->suspend_noirq != NULL) 88859cbead6SJean-Sébastien Pédron error = -pmops->suspend_noirq(&pdev->dev); 889d34188a0SMark Johnston } 890d34188a0SMark Johnston return (error); 8917d133393SHans Petter Selasky } 8927d133393SHans Petter Selasky 8937d133393SHans Petter Selasky static int 8947d133393SHans Petter Selasky linux_pci_resume(device_t dev) 8957d133393SHans Petter Selasky { 896d34188a0SMark Johnston const struct dev_pm_ops *pmops; 8977d133393SHans Petter Selasky struct pci_dev *pdev; 898d34188a0SMark Johnston int error; 8997d133393SHans Petter Selasky 900d34188a0SMark Johnston error = 0; 9011e3db1deSHans Petter Selasky linux_set_current(curthread); 9027d133393SHans Petter Selasky pdev = device_get_softc(dev); 903d34188a0SMark Johnston pmops = pdev->pdrv->driver.pm; 904d34188a0SMark Johnston 9057d133393SHans Petter Selasky if (pdev->pdrv->resume != NULL) 906d34188a0SMark Johnston error = -pdev->pdrv->resume(pdev); 907d34188a0SMark Johnston else if (pmops != NULL && pmops->resume != NULL) { 908d34188a0SMark Johnston if (pmops->resume_early != NULL) 909d34188a0SMark Johnston error = -pmops->resume_early(&pdev->dev); 910d34188a0SMark Johnston if (error == 0 && pmops->resume != NULL) 911d34188a0SMark Johnston error = -pmops->resume(&pdev->dev); 912d34188a0SMark Johnston } 913d34188a0SMark Johnston return (error); 9147d133393SHans Petter Selasky } 9157d133393SHans Petter Selasky 9167d133393SHans Petter Selasky static int 9177d133393SHans Petter Selasky linux_pci_shutdown(device_t dev) 9187d133393SHans Petter Selasky { 9197d133393SHans Petter Selasky struct pci_dev *pdev; 9207d133393SHans Petter Selasky 9211e3db1deSHans Petter Selasky linux_set_current(curthread); 9227d133393SHans Petter Selasky pdev = device_get_softc(dev); 9234b706099SHans Petter Selasky if (pdev->pdrv->shutdown != NULL) 9247d133393SHans Petter Selasky pdev->pdrv->shutdown(pdev); 9257d133393SHans Petter Selasky return (0); 9267d133393SHans Petter Selasky } 9277d133393SHans Petter Selasky 9280b7bd01aSMark Johnston static int 9292928e60eSKonstantin Belousov linux_pci_iov_init(device_t dev, uint16_t num_vfs, const nvlist_t *pf_config) 9302928e60eSKonstantin Belousov { 9312928e60eSKonstantin Belousov struct pci_dev *pdev; 9322928e60eSKonstantin Belousov int error; 9332928e60eSKonstantin Belousov 9342928e60eSKonstantin Belousov linux_set_current(curthread); 9352928e60eSKonstantin Belousov pdev = device_get_softc(dev); 9362928e60eSKonstantin Belousov if (pdev->pdrv->bsd_iov_init != NULL) 9372928e60eSKonstantin Belousov error = pdev->pdrv->bsd_iov_init(dev, num_vfs, pf_config); 9382928e60eSKonstantin Belousov else 9392928e60eSKonstantin Belousov error = EINVAL; 9402928e60eSKonstantin Belousov return (error); 9412928e60eSKonstantin Belousov } 9422928e60eSKonstantin Belousov 9432928e60eSKonstantin Belousov static void 9442928e60eSKonstantin Belousov linux_pci_iov_uninit(device_t dev) 9452928e60eSKonstantin Belousov { 9462928e60eSKonstantin Belousov struct pci_dev *pdev; 9472928e60eSKonstantin Belousov 9482928e60eSKonstantin Belousov linux_set_current(curthread); 9492928e60eSKonstantin Belousov pdev = device_get_softc(dev); 9502928e60eSKonstantin Belousov if (pdev->pdrv->bsd_iov_uninit != NULL) 9512928e60eSKonstantin Belousov pdev->pdrv->bsd_iov_uninit(dev); 9522928e60eSKonstantin Belousov } 9532928e60eSKonstantin Belousov 9542928e60eSKonstantin Belousov static int 9552928e60eSKonstantin Belousov linux_pci_iov_add_vf(device_t dev, uint16_t vfnum, const nvlist_t *vf_config) 9562928e60eSKonstantin Belousov { 9572928e60eSKonstantin Belousov struct pci_dev *pdev; 9582928e60eSKonstantin Belousov int error; 9592928e60eSKonstantin Belousov 9602928e60eSKonstantin Belousov linux_set_current(curthread); 9612928e60eSKonstantin Belousov pdev = device_get_softc(dev); 9622928e60eSKonstantin Belousov if (pdev->pdrv->bsd_iov_add_vf != NULL) 9632928e60eSKonstantin Belousov error = pdev->pdrv->bsd_iov_add_vf(dev, vfnum, vf_config); 9642928e60eSKonstantin Belousov else 9652928e60eSKonstantin Belousov error = EINVAL; 9662928e60eSKonstantin Belousov return (error); 9672928e60eSKonstantin Belousov } 9682928e60eSKonstantin Belousov 9692928e60eSKonstantin Belousov static int 9700b7bd01aSMark Johnston _linux_pci_register_driver(struct pci_driver *pdrv, devclass_t dc) 9718d59ecb2SHans Petter Selasky { 9720b7bd01aSMark Johnston int error; 9738d59ecb2SHans Petter Selasky 9741e3db1deSHans Petter Selasky linux_set_current(curthread); 9758d59ecb2SHans Petter Selasky spin_lock(&pci_lock); 976cf899348SBjoern A. Zeeb list_add(&pdrv->node, &pci_drivers); 9778d59ecb2SHans Petter Selasky spin_unlock(&pci_lock); 97881d058dfSBjoern A. Zeeb if (pdrv->bsddriver.name == NULL) 979b38dc0a1SMark Johnston pdrv->bsddriver.name = pdrv->name; 980b38dc0a1SMark Johnston pdrv->bsddriver.methods = pci_methods; 981b38dc0a1SMark Johnston pdrv->bsddriver.size = sizeof(struct pci_dev); 982b38dc0a1SMark Johnston 983c6df6f53SWarner Losh bus_topo_lock(); 9840b7bd01aSMark Johnston error = devclass_add_driver(dc, &pdrv->bsddriver, 985b38dc0a1SMark Johnston BUS_PASS_DEFAULT, &pdrv->bsdclass); 986c6df6f53SWarner Losh bus_topo_unlock(); 9878d59ecb2SHans Petter Selasky return (-error); 9888d59ecb2SHans Petter Selasky } 9898d59ecb2SHans Petter Selasky 9900b7bd01aSMark Johnston int 9910b7bd01aSMark Johnston linux_pci_register_driver(struct pci_driver *pdrv) 9920b7bd01aSMark Johnston { 9930b7bd01aSMark Johnston devclass_t dc; 9940b7bd01aSMark Johnston 9955d20075fSVladimir Kondratyev pdrv->isdrm = strcmp(pdrv->name, "drmn") == 0; 9965d20075fSVladimir Kondratyev dc = pdrv->isdrm ? devclass_create("vgapci") : devclass_find("pci"); 9970b7bd01aSMark Johnston if (dc == NULL) 9980b7bd01aSMark Johnston return (-ENXIO); 9990b7bd01aSMark Johnston return (_linux_pci_register_driver(pdrv, dc)); 10000b7bd01aSMark Johnston } 10010b7bd01aSMark Johnston 100296ab16ebSVladimir Kondratyev static struct resource_list_entry * 100396ab16ebSVladimir Kondratyev lkpi_pci_get_bar(struct pci_dev *pdev, int bar, bool reserve) 100482098c8bSJessica Clarke { 100596ab16ebSVladimir Kondratyev int type; 100682098c8bSJessica Clarke 100796ab16ebSVladimir Kondratyev type = pci_resource_type(pdev, bar); 100896ab16ebSVladimir Kondratyev if (type < 0) 100982098c8bSJessica Clarke return (NULL); 101096ab16ebSVladimir Kondratyev bar = PCIR_BAR(bar); 101196ab16ebSVladimir Kondratyev return (linux_pci_get_rle(pdev, type, bar, reserve)); 101296ab16ebSVladimir Kondratyev } 101396ab16ebSVladimir Kondratyev 101496ab16ebSVladimir Kondratyev struct device * 101596ab16ebSVladimir Kondratyev lkpi_pci_find_irq_dev(unsigned int irq) 101696ab16ebSVladimir Kondratyev { 101796ab16ebSVladimir Kondratyev struct pci_dev *pdev; 101896ab16ebSVladimir Kondratyev struct device *found; 101996ab16ebSVladimir Kondratyev 102096ab16ebSVladimir Kondratyev found = NULL; 102196ab16ebSVladimir Kondratyev spin_lock(&pci_lock); 102296ab16ebSVladimir Kondratyev list_for_each_entry(pdev, &pci_devices, links) { 102396ab16ebSVladimir Kondratyev if (irq == pdev->dev.irq || 102496ab16ebSVladimir Kondratyev (irq >= pdev->dev.irq_start && irq < pdev->dev.irq_end)) { 102596ab16ebSVladimir Kondratyev found = &pdev->dev; 102696ab16ebSVladimir Kondratyev break; 102796ab16ebSVladimir Kondratyev } 102896ab16ebSVladimir Kondratyev } 102996ab16ebSVladimir Kondratyev spin_unlock(&pci_lock); 103096ab16ebSVladimir Kondratyev return (found); 103182098c8bSJessica Clarke } 103282098c8bSJessica Clarke 1033937a05baSJustin Hibbits unsigned long 1034937a05baSJustin Hibbits pci_resource_start(struct pci_dev *pdev, int bar) 1035937a05baSJustin Hibbits { 1036937a05baSJustin Hibbits struct resource_list_entry *rle; 10374eaa2fdeSJustin Hibbits rman_res_t newstart; 1038937a05baSJustin Hibbits device_t dev; 10391fb99e97SMark Johnston int error; 1040937a05baSJustin Hibbits 104196ab16ebSVladimir Kondratyev if ((rle = lkpi_pci_get_bar(pdev, bar, true)) == NULL) 1042937a05baSJustin Hibbits return (0); 1043de27805fSKonstantin Belousov dev = pdev->pdrv != NULL && pdev->pdrv->isdrm ? 1044de27805fSKonstantin Belousov device_get_parent(pdev->dev.bsddev) : pdev->dev.bsddev; 10451fb99e97SMark Johnston error = bus_translate_resource(dev, rle->type, rle->start, &newstart); 10461fb99e97SMark Johnston if (error != 0) { 10471fb99e97SMark Johnston device_printf(pdev->dev.bsddev, 10481fb99e97SMark Johnston "translate of %#jx failed: %d\n", 10491fb99e97SMark Johnston (uintmax_t)rle->start, error); 1050937a05baSJustin Hibbits return (0); 1051937a05baSJustin Hibbits } 1052937a05baSJustin Hibbits return (newstart); 1053937a05baSJustin Hibbits } 1054937a05baSJustin Hibbits 1055937a05baSJustin Hibbits unsigned long 1056937a05baSJustin Hibbits pci_resource_len(struct pci_dev *pdev, int bar) 1057937a05baSJustin Hibbits { 1058937a05baSJustin Hibbits struct resource_list_entry *rle; 1059937a05baSJustin Hibbits 106096ab16ebSVladimir Kondratyev if ((rle = lkpi_pci_get_bar(pdev, bar, true)) == NULL) 1061937a05baSJustin Hibbits return (0); 1062937a05baSJustin Hibbits return (rle->count); 1063937a05baSJustin Hibbits } 1064937a05baSJustin Hibbits 10650b7bd01aSMark Johnston int 10661cdb2534SWarner Losh pci_request_region(struct pci_dev *pdev, int bar, const char *res_name) 10671cdb2534SWarner Losh { 10681cdb2534SWarner Losh struct resource *res; 10691cdb2534SWarner Losh struct pci_devres *dr; 10701cdb2534SWarner Losh struct pci_mmio_region *mmio; 10711cdb2534SWarner Losh int rid; 10721cdb2534SWarner Losh int type; 10731cdb2534SWarner Losh 10741cdb2534SWarner Losh type = pci_resource_type(pdev, bar); 10751cdb2534SWarner Losh if (type < 0) 10761cdb2534SWarner Losh return (-ENODEV); 10771cdb2534SWarner Losh rid = PCIR_BAR(bar); 10781cdb2534SWarner Losh res = bus_alloc_resource_any(pdev->dev.bsddev, type, &rid, 10791cdb2534SWarner Losh RF_ACTIVE|RF_SHAREABLE); 10801cdb2534SWarner Losh if (res == NULL) { 10811cdb2534SWarner Losh device_printf(pdev->dev.bsddev, "%s: failed to alloc " 10821cdb2534SWarner Losh "bar %d type %d rid %d\n", 10831cdb2534SWarner Losh __func__, bar, type, PCIR_BAR(bar)); 10841cdb2534SWarner Losh return (-ENODEV); 10851cdb2534SWarner Losh } 10861cdb2534SWarner Losh 10871cdb2534SWarner Losh /* 10881cdb2534SWarner Losh * It seems there is an implicit devres tracking on these if the device 10891cdb2534SWarner Losh * is managed; otherwise the resources are not automatiaclly freed on 10901cdb2534SWarner Losh * FreeBSD/LinuxKPI tough they should be/are expected to be by Linux 10911cdb2534SWarner Losh * drivers. 10921cdb2534SWarner Losh */ 10931cdb2534SWarner Losh dr = lkpi_pci_devres_find(pdev); 10941cdb2534SWarner Losh if (dr != NULL) { 10951cdb2534SWarner Losh dr->region_mask |= (1 << bar); 10961cdb2534SWarner Losh dr->region_table[bar] = res; 10971cdb2534SWarner Losh } 10981cdb2534SWarner Losh 10991cdb2534SWarner Losh /* Even if the device is not managed we need to track it for iomap. */ 11001cdb2534SWarner Losh mmio = malloc(sizeof(*mmio), M_DEVBUF, M_WAITOK | M_ZERO); 11011cdb2534SWarner Losh mmio->rid = PCIR_BAR(bar); 11021cdb2534SWarner Losh mmio->type = type; 11031cdb2534SWarner Losh mmio->res = res; 11041cdb2534SWarner Losh TAILQ_INSERT_TAIL(&pdev->mmio, mmio, next); 11051cdb2534SWarner Losh 11061cdb2534SWarner Losh return (0); 11071cdb2534SWarner Losh } 11081cdb2534SWarner Losh 110996ab16ebSVladimir Kondratyev int 111096ab16ebSVladimir Kondratyev linuxkpi_pci_request_regions(struct pci_dev *pdev, const char *res_name) 11111cdb2534SWarner Losh { 111296ab16ebSVladimir Kondratyev int error; 111396ab16ebSVladimir Kondratyev int i; 11141cdb2534SWarner Losh 111596ab16ebSVladimir Kondratyev for (i = 0; i <= PCIR_MAX_BAR_0; i++) { 111696ab16ebSVladimir Kondratyev error = pci_request_region(pdev, i, res_name); 111796ab16ebSVladimir Kondratyev if (error && error != -ENODEV) { 111896ab16ebSVladimir Kondratyev pci_release_regions(pdev); 111996ab16ebSVladimir Kondratyev return (error); 11201cdb2534SWarner Losh } 112196ab16ebSVladimir Kondratyev } 112296ab16ebSVladimir Kondratyev return (0); 112396ab16ebSVladimir Kondratyev } 112496ab16ebSVladimir Kondratyev 112596ab16ebSVladimir Kondratyev void 112696ab16ebSVladimir Kondratyev linuxkpi_pci_release_region(struct pci_dev *pdev, int bar) 112796ab16ebSVladimir Kondratyev { 112896ab16ebSVladimir Kondratyev struct resource_list_entry *rle; 112996ab16ebSVladimir Kondratyev struct pci_devres *dr; 113096ab16ebSVladimir Kondratyev struct pci_mmio_region *mmio, *p; 113196ab16ebSVladimir Kondratyev 113296ab16ebSVladimir Kondratyev if ((rle = lkpi_pci_get_bar(pdev, bar, false)) == NULL) 113396ab16ebSVladimir Kondratyev return; 11341cdb2534SWarner Losh 11351cdb2534SWarner Losh /* 113696ab16ebSVladimir Kondratyev * As we implicitly track the requests we also need to clear them on 113796ab16ebSVladimir Kondratyev * release. Do clear before resource release. 11381cdb2534SWarner Losh */ 113996ab16ebSVladimir Kondratyev dr = lkpi_pci_devres_find(pdev); 114096ab16ebSVladimir Kondratyev if (dr != NULL) { 114196ab16ebSVladimir Kondratyev KASSERT(dr->region_table[bar] == rle->res, ("%s: pdev %p bar %d" 114296ab16ebSVladimir Kondratyev " region_table res %p != rel->res %p\n", __func__, pdev, 114396ab16ebSVladimir Kondratyev bar, dr->region_table[bar], rle->res)); 114496ab16ebSVladimir Kondratyev dr->region_table[bar] = NULL; 114596ab16ebSVladimir Kondratyev dr->region_mask &= ~(1 << bar); 114696ab16ebSVladimir Kondratyev } 114796ab16ebSVladimir Kondratyev 11481cdb2534SWarner Losh TAILQ_FOREACH_SAFE(mmio, &pdev->mmio, next, p) { 114996ab16ebSVladimir Kondratyev if (rle->res != (void *)rman_get_bushandle(mmio->res)) 115096ab16ebSVladimir Kondratyev continue; 115196ab16ebSVladimir Kondratyev TAILQ_REMOVE(&pdev->mmio, mmio, next); 11521cdb2534SWarner Losh free(mmio, M_DEVBUF); 11531cdb2534SWarner Losh } 11541cdb2534SWarner Losh 115596ab16ebSVladimir Kondratyev bus_release_resource(pdev->dev.bsddev, rle->type, rle->rid, rle->res); 115696ab16ebSVladimir Kondratyev } 115796ab16ebSVladimir Kondratyev 115896ab16ebSVladimir Kondratyev void 115996ab16ebSVladimir Kondratyev linuxkpi_pci_release_regions(struct pci_dev *pdev) 116096ab16ebSVladimir Kondratyev { 116196ab16ebSVladimir Kondratyev int i; 116296ab16ebSVladimir Kondratyev 116396ab16ebSVladimir Kondratyev for (i = 0; i <= PCIR_MAX_BAR_0; i++) 116496ab16ebSVladimir Kondratyev pci_release_region(pdev, i); 11651cdb2534SWarner Losh } 11661cdb2534SWarner Losh 11671cdb2534SWarner Losh int 11680b7bd01aSMark Johnston linux_pci_register_drm_driver(struct pci_driver *pdrv) 11690b7bd01aSMark Johnston { 11700b7bd01aSMark Johnston devclass_t dc; 11710b7bd01aSMark Johnston 11720b7bd01aSMark Johnston dc = devclass_create("vgapci"); 11730b7bd01aSMark Johnston if (dc == NULL) 11740b7bd01aSMark Johnston return (-ENXIO); 11750b7bd01aSMark Johnston pdrv->isdrm = true; 11760b7bd01aSMark Johnston pdrv->name = "drmn"; 11770b7bd01aSMark Johnston return (_linux_pci_register_driver(pdrv, dc)); 11780b7bd01aSMark Johnston } 11790b7bd01aSMark Johnston 11808d59ecb2SHans Petter Selasky void 11810b7bd01aSMark Johnston linux_pci_unregister_driver(struct pci_driver *pdrv) 11828d59ecb2SHans Petter Selasky { 11838d59ecb2SHans Petter Selasky devclass_t bus; 11848d59ecb2SHans Petter Selasky 11855d20075fSVladimir Kondratyev bus = devclass_find(pdrv->isdrm ? "vgapci" : "pci"); 11868d59ecb2SHans Petter Selasky 11870a930cf0SMark Johnston spin_lock(&pci_lock); 1188cf899348SBjoern A. Zeeb list_del(&pdrv->node); 11890a930cf0SMark Johnston spin_unlock(&pci_lock); 1190c6df6f53SWarner Losh bus_topo_lock(); 11918d59ecb2SHans Petter Selasky if (bus != NULL) 1192b38dc0a1SMark Johnston devclass_delete_driver(bus, &pdrv->bsddriver); 1193c6df6f53SWarner Losh bus_topo_unlock(); 11948d59ecb2SHans Petter Selasky } 1195f211d536STycho Nightingale 11965098ed5fSJohannes Lundberg void 11975098ed5fSJohannes Lundberg linux_pci_unregister_drm_driver(struct pci_driver *pdrv) 11985098ed5fSJohannes Lundberg { 11995098ed5fSJohannes Lundberg devclass_t bus; 12005098ed5fSJohannes Lundberg 12015098ed5fSJohannes Lundberg bus = devclass_find("vgapci"); 12025098ed5fSJohannes Lundberg 12035098ed5fSJohannes Lundberg spin_lock(&pci_lock); 1204cf899348SBjoern A. Zeeb list_del(&pdrv->node); 12055098ed5fSJohannes Lundberg spin_unlock(&pci_lock); 1206c6df6f53SWarner Losh bus_topo_lock(); 12075098ed5fSJohannes Lundberg if (bus != NULL) 12085098ed5fSJohannes Lundberg devclass_delete_driver(bus, &pdrv->bsddriver); 1209c6df6f53SWarner Losh bus_topo_unlock(); 12105098ed5fSJohannes Lundberg } 12115098ed5fSJohannes Lundberg 121236b5c440SWarner Losh int 121396ab16ebSVladimir Kondratyev linuxkpi_pci_enable_msix(struct pci_dev *pdev, struct msix_entry *entries, 121496ab16ebSVladimir Kondratyev int nreq) 121596ab16ebSVladimir Kondratyev { 121696ab16ebSVladimir Kondratyev struct resource_list_entry *rle; 121796ab16ebSVladimir Kondratyev int error; 121896ab16ebSVladimir Kondratyev int avail; 121996ab16ebSVladimir Kondratyev int i; 122096ab16ebSVladimir Kondratyev 122196ab16ebSVladimir Kondratyev avail = pci_msix_count(pdev->dev.bsddev); 122296ab16ebSVladimir Kondratyev if (avail < nreq) { 122396ab16ebSVladimir Kondratyev if (avail == 0) 122496ab16ebSVladimir Kondratyev return -EINVAL; 122596ab16ebSVladimir Kondratyev return avail; 122696ab16ebSVladimir Kondratyev } 122796ab16ebSVladimir Kondratyev avail = nreq; 122896ab16ebSVladimir Kondratyev if ((error = -pci_alloc_msix(pdev->dev.bsddev, &avail)) != 0) 122996ab16ebSVladimir Kondratyev return error; 123096ab16ebSVladimir Kondratyev /* 123196ab16ebSVladimir Kondratyev * Handle case where "pci_alloc_msix()" may allocate less 123296ab16ebSVladimir Kondratyev * interrupts than available and return with no error: 123396ab16ebSVladimir Kondratyev */ 123496ab16ebSVladimir Kondratyev if (avail < nreq) { 123596ab16ebSVladimir Kondratyev pci_release_msi(pdev->dev.bsddev); 123696ab16ebSVladimir Kondratyev return avail; 123796ab16ebSVladimir Kondratyev } 123896ab16ebSVladimir Kondratyev rle = linux_pci_get_rle(pdev, SYS_RES_IRQ, 1, false); 123996ab16ebSVladimir Kondratyev pdev->dev.irq_start = rle->start; 124096ab16ebSVladimir Kondratyev pdev->dev.irq_end = rle->start + avail; 124196ab16ebSVladimir Kondratyev for (i = 0; i < nreq; i++) 124296ab16ebSVladimir Kondratyev entries[i].vector = pdev->dev.irq_start + i; 124396ab16ebSVladimir Kondratyev pdev->msix_enabled = true; 124496ab16ebSVladimir Kondratyev return (0); 124596ab16ebSVladimir Kondratyev } 124696ab16ebSVladimir Kondratyev 124796ab16ebSVladimir Kondratyev int 124896ab16ebSVladimir Kondratyev _lkpi_pci_enable_msi_range(struct pci_dev *pdev, int minvec, int maxvec) 124996ab16ebSVladimir Kondratyev { 125096ab16ebSVladimir Kondratyev struct resource_list_entry *rle; 125196ab16ebSVladimir Kondratyev int error; 125296ab16ebSVladimir Kondratyev int nvec; 125396ab16ebSVladimir Kondratyev 125496ab16ebSVladimir Kondratyev if (maxvec < minvec) 125596ab16ebSVladimir Kondratyev return (-EINVAL); 125696ab16ebSVladimir Kondratyev 125796ab16ebSVladimir Kondratyev nvec = pci_msi_count(pdev->dev.bsddev); 125896ab16ebSVladimir Kondratyev if (nvec < 1 || nvec < minvec) 125996ab16ebSVladimir Kondratyev return (-ENOSPC); 126096ab16ebSVladimir Kondratyev 126196ab16ebSVladimir Kondratyev nvec = min(nvec, maxvec); 126296ab16ebSVladimir Kondratyev if ((error = -pci_alloc_msi(pdev->dev.bsddev, &nvec)) != 0) 126396ab16ebSVladimir Kondratyev return error; 126496ab16ebSVladimir Kondratyev 126596ab16ebSVladimir Kondratyev /* Native PCI might only ever ask for 32 vectors. */ 126696ab16ebSVladimir Kondratyev if (nvec < minvec) { 126796ab16ebSVladimir Kondratyev pci_release_msi(pdev->dev.bsddev); 126896ab16ebSVladimir Kondratyev return (-ENOSPC); 126996ab16ebSVladimir Kondratyev } 127096ab16ebSVladimir Kondratyev 127196ab16ebSVladimir Kondratyev rle = linux_pci_get_rle(pdev, SYS_RES_IRQ, 1, false); 127296ab16ebSVladimir Kondratyev pdev->dev.irq_start = rle->start; 127396ab16ebSVladimir Kondratyev pdev->dev.irq_end = rle->start + nvec; 127496ab16ebSVladimir Kondratyev pdev->irq = rle->start; 127596ab16ebSVladimir Kondratyev pdev->msi_enabled = true; 127696ab16ebSVladimir Kondratyev return (0); 127796ab16ebSVladimir Kondratyev } 127896ab16ebSVladimir Kondratyev 127996ab16ebSVladimir Kondratyev int 128036b5c440SWarner Losh pci_alloc_irq_vectors(struct pci_dev *pdev, int minv, int maxv, 128136b5c440SWarner Losh unsigned int flags) 128236b5c440SWarner Losh { 128336b5c440SWarner Losh int error; 128436b5c440SWarner Losh 128536b5c440SWarner Losh if (flags & PCI_IRQ_MSIX) { 128636b5c440SWarner Losh struct msix_entry *entries; 128736b5c440SWarner Losh int i; 128836b5c440SWarner Losh 128936b5c440SWarner Losh entries = kcalloc(maxv, sizeof(*entries), GFP_KERNEL); 129036b5c440SWarner Losh if (entries == NULL) { 129136b5c440SWarner Losh error = -ENOMEM; 129236b5c440SWarner Losh goto out; 129336b5c440SWarner Losh } 129436b5c440SWarner Losh for (i = 0; i < maxv; ++i) 129536b5c440SWarner Losh entries[i].entry = i; 129636b5c440SWarner Losh error = pci_enable_msix(pdev, entries, maxv); 129736b5c440SWarner Losh out: 129836b5c440SWarner Losh kfree(entries); 129936b5c440SWarner Losh if (error == 0 && pdev->msix_enabled) 130036b5c440SWarner Losh return (pdev->dev.irq_end - pdev->dev.irq_start); 130136b5c440SWarner Losh } 130236b5c440SWarner Losh if (flags & PCI_IRQ_MSI) { 1303e9715b1cSBjoern A. Zeeb if (pci_msi_count(pdev->dev.bsddev) < minv) 1304e9715b1cSBjoern A. Zeeb return (-ENOSPC); 1305b15491b4SBjoern A. Zeeb error = _lkpi_pci_enable_msi_range(pdev, minv, maxv); 130636b5c440SWarner Losh if (error == 0 && pdev->msi_enabled) 130736b5c440SWarner Losh return (pdev->dev.irq_end - pdev->dev.irq_start); 130836b5c440SWarner Losh } 1309*157e93e0SBjoern A. Zeeb if (flags & PCI_IRQ_INTX) { 131036b5c440SWarner Losh if (pdev->irq) 131136b5c440SWarner Losh return (1); 131236b5c440SWarner Losh } 131336b5c440SWarner Losh 131436b5c440SWarner Losh return (-EINVAL); 131536b5c440SWarner Losh } 131636b5c440SWarner Losh 13174b56afafSBjoern A. Zeeb struct msi_desc * 13184b56afafSBjoern A. Zeeb lkpi_pci_msi_desc_alloc(int irq) 13194b56afafSBjoern A. Zeeb { 13204b56afafSBjoern A. Zeeb struct device *dev; 13214b56afafSBjoern A. Zeeb struct pci_dev *pdev; 13224b56afafSBjoern A. Zeeb struct msi_desc *desc; 13234b56afafSBjoern A. Zeeb struct pci_devinfo *dinfo; 13244b56afafSBjoern A. Zeeb struct pcicfg_msi *msi; 1325b15491b4SBjoern A. Zeeb int vec; 13264b56afafSBjoern A. Zeeb 132796ab16ebSVladimir Kondratyev dev = lkpi_pci_find_irq_dev(irq); 13284b56afafSBjoern A. Zeeb if (dev == NULL) 13294b56afafSBjoern A. Zeeb return (NULL); 13304b56afafSBjoern A. Zeeb 13314b56afafSBjoern A. Zeeb pdev = to_pci_dev(dev); 1332b15491b4SBjoern A. Zeeb 1333b15491b4SBjoern A. Zeeb if (pdev->msi_desc == NULL) 1334b15491b4SBjoern A. Zeeb return (NULL); 1335b15491b4SBjoern A. Zeeb 1336b15491b4SBjoern A. Zeeb if (irq < pdev->dev.irq_start || irq >= pdev->dev.irq_end) 1337b15491b4SBjoern A. Zeeb return (NULL); 1338b15491b4SBjoern A. Zeeb 1339b15491b4SBjoern A. Zeeb vec = pdev->dev.irq_start - irq; 1340b15491b4SBjoern A. Zeeb 1341b15491b4SBjoern A. Zeeb if (pdev->msi_desc[vec] != NULL) 1342b15491b4SBjoern A. Zeeb return (pdev->msi_desc[vec]); 13434b56afafSBjoern A. Zeeb 13444b56afafSBjoern A. Zeeb dinfo = device_get_ivars(dev->bsddev); 13454b56afafSBjoern A. Zeeb msi = &dinfo->cfg.msi; 13464b56afafSBjoern A. Zeeb 13474b56afafSBjoern A. Zeeb desc = malloc(sizeof(*desc), M_DEVBUF, M_WAITOK | M_ZERO); 13484b56afafSBjoern A. Zeeb 134917bde9cbSBjoern A. Zeeb desc->pci.msi_attrib.is_64 = 13504b56afafSBjoern A. Zeeb (msi->msi_ctrl & PCIM_MSICTRL_64BIT) ? true : false; 13514b56afafSBjoern A. Zeeb desc->msg.data = msi->msi_data; 13524b56afafSBjoern A. Zeeb 1353b15491b4SBjoern A. Zeeb pdev->msi_desc[vec] = desc; 1354b15491b4SBjoern A. Zeeb 13554b56afafSBjoern A. Zeeb return (desc); 13564b56afafSBjoern A. Zeeb } 13574b56afafSBjoern A. Zeeb 135849b6d5edSJean-Sébastien Pédron bool 135949b6d5edSJean-Sébastien Pédron pci_device_is_present(struct pci_dev *pdev) 136049b6d5edSJean-Sébastien Pédron { 136149b6d5edSJean-Sébastien Pédron device_t dev; 136249b6d5edSJean-Sébastien Pédron 136349b6d5edSJean-Sébastien Pédron dev = pdev->dev.bsddev; 136449b6d5edSJean-Sébastien Pédron 136549b6d5edSJean-Sébastien Pédron return (bus_child_present(dev)); 136649b6d5edSJean-Sébastien Pédron } 136749b6d5edSJean-Sébastien Pédron 1368d4fedb75SHans Petter Selasky CTASSERT(sizeof(dma_addr_t) <= sizeof(uint64_t)); 1369d4fedb75SHans Petter Selasky 1370f211d536STycho Nightingale struct linux_dma_obj { 1371f211d536STycho Nightingale void *vaddr; 1372d4fedb75SHans Petter Selasky uint64_t dma_addr; 1373f211d536STycho Nightingale bus_dmamap_t dmamap; 1374c39eefe7SBjoern A. Zeeb bus_dma_tag_t dmat; 1375f211d536STycho Nightingale }; 1376f211d536STycho Nightingale 1377f211d536STycho Nightingale static uma_zone_t linux_dma_trie_zone; 1378f211d536STycho Nightingale static uma_zone_t linux_dma_obj_zone; 1379f211d536STycho Nightingale 1380f211d536STycho Nightingale static void 1381f211d536STycho Nightingale linux_dma_init(void *arg) 1382f211d536STycho Nightingale { 1383f211d536STycho Nightingale 1384f211d536STycho Nightingale linux_dma_trie_zone = uma_zcreate("linux_dma_pctrie", 1385f211d536STycho Nightingale pctrie_node_size(), NULL, NULL, pctrie_zone_init, NULL, 1386f211d536STycho Nightingale UMA_ALIGN_PTR, 0); 1387f211d536STycho Nightingale linux_dma_obj_zone = uma_zcreate("linux_dma_object", 1388f211d536STycho Nightingale sizeof(struct linux_dma_obj), NULL, NULL, NULL, NULL, 1389f211d536STycho Nightingale UMA_ALIGN_PTR, 0); 1390e8670741SBjoern A. Zeeb lkpi_pci_nseg1_fail = counter_u64_alloc(M_WAITOK); 1391f211d536STycho Nightingale } 1392f211d536STycho Nightingale SYSINIT(linux_dma, SI_SUB_DRIVERS, SI_ORDER_THIRD, linux_dma_init, NULL); 1393f211d536STycho Nightingale 1394f211d536STycho Nightingale static void 1395f211d536STycho Nightingale linux_dma_uninit(void *arg) 1396f211d536STycho Nightingale { 1397f211d536STycho Nightingale 1398e8670741SBjoern A. Zeeb counter_u64_free(lkpi_pci_nseg1_fail); 1399f211d536STycho Nightingale uma_zdestroy(linux_dma_obj_zone); 1400f211d536STycho Nightingale uma_zdestroy(linux_dma_trie_zone); 1401f211d536STycho Nightingale } 1402f211d536STycho Nightingale SYSUNINIT(linux_dma, SI_SUB_DRIVERS, SI_ORDER_THIRD, linux_dma_uninit, NULL); 1403f211d536STycho Nightingale 1404f211d536STycho Nightingale static void * 1405f211d536STycho Nightingale linux_dma_trie_alloc(struct pctrie *ptree) 1406f211d536STycho Nightingale { 1407f211d536STycho Nightingale 140892a15f94SRyan Stone return (uma_zalloc(linux_dma_trie_zone, M_NOWAIT)); 1409f211d536STycho Nightingale } 1410f211d536STycho Nightingale 1411f211d536STycho Nightingale static void 1412f211d536STycho Nightingale linux_dma_trie_free(struct pctrie *ptree, void *node) 1413f211d536STycho Nightingale { 1414f211d536STycho Nightingale 1415f211d536STycho Nightingale uma_zfree(linux_dma_trie_zone, node); 1416f211d536STycho Nightingale } 1417f211d536STycho Nightingale 1418f211d536STycho Nightingale PCTRIE_DEFINE(LINUX_DMA, linux_dma_obj, dma_addr, linux_dma_trie_alloc, 1419f211d536STycho Nightingale linux_dma_trie_free); 1420f211d536STycho Nightingale 1421b961c0f2STycho Nightingale #if defined(__i386__) || defined(__amd64__) || defined(__aarch64__) 1422c39eefe7SBjoern A. Zeeb static dma_addr_t 1423c39eefe7SBjoern A. Zeeb linux_dma_map_phys_common(struct device *dev, vm_paddr_t phys, size_t len, 1424c39eefe7SBjoern A. Zeeb bus_dma_tag_t dmat) 1425f211d536STycho Nightingale { 1426f211d536STycho Nightingale struct linux_dma_priv *priv; 1427f211d536STycho Nightingale struct linux_dma_obj *obj; 1428f211d536STycho Nightingale int error, nseg; 1429f211d536STycho Nightingale bus_dma_segment_t seg; 1430f211d536STycho Nightingale 1431f211d536STycho Nightingale priv = dev->dma_priv; 1432f211d536STycho Nightingale 1433b961c0f2STycho Nightingale /* 1434b961c0f2STycho Nightingale * If the resultant mapping will be entirely 1:1 with the 1435b961c0f2STycho Nightingale * physical address, short-circuit the remainder of the 1436b961c0f2STycho Nightingale * bus_dma API. This avoids tracking collisions in the pctrie 1437b961c0f2STycho Nightingale * with the additional benefit of reducing overhead. 1438b961c0f2STycho Nightingale */ 1439c39eefe7SBjoern A. Zeeb if (bus_dma_id_mapped(dmat, phys, len)) 1440b961c0f2STycho Nightingale return (phys); 1441b961c0f2STycho Nightingale 144292a15f94SRyan Stone obj = uma_zalloc(linux_dma_obj_zone, M_NOWAIT); 144392a15f94SRyan Stone if (obj == NULL) { 144492a15f94SRyan Stone return (0); 144592a15f94SRyan Stone } 1446c39eefe7SBjoern A. Zeeb obj->dmat = dmat; 1447f211d536STycho Nightingale 1448a6619e8dSHans Petter Selasky DMA_PRIV_LOCK(priv); 1449c39eefe7SBjoern A. Zeeb if (bus_dmamap_create(obj->dmat, 0, &obj->dmamap) != 0) { 1450a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1451f211d536STycho Nightingale uma_zfree(linux_dma_obj_zone, obj); 1452f211d536STycho Nightingale return (0); 1453f211d536STycho Nightingale } 1454f211d536STycho Nightingale 1455f211d536STycho Nightingale nseg = -1; 1456c39eefe7SBjoern A. Zeeb if (_bus_dmamap_load_phys(obj->dmat, obj->dmamap, phys, len, 1457f211d536STycho Nightingale BUS_DMA_NOWAIT, &seg, &nseg) != 0) { 1458c39eefe7SBjoern A. Zeeb bus_dmamap_destroy(obj->dmat, obj->dmamap); 1459a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1460f211d536STycho Nightingale uma_zfree(linux_dma_obj_zone, obj); 1461e8670741SBjoern A. Zeeb counter_u64_add(lkpi_pci_nseg1_fail, 1); 1462e8670741SBjoern A. Zeeb if (linuxkpi_debug) 1463e8670741SBjoern A. Zeeb dump_stack(); 1464f211d536STycho Nightingale return (0); 1465f211d536STycho Nightingale } 1466f211d536STycho Nightingale 1467f211d536STycho Nightingale KASSERT(++nseg == 1, ("More than one segment (nseg=%d)", nseg)); 1468f211d536STycho Nightingale obj->dma_addr = seg.ds_addr; 1469f211d536STycho Nightingale 1470f211d536STycho Nightingale error = LINUX_DMA_PCTRIE_INSERT(&priv->ptree, obj); 1471f211d536STycho Nightingale if (error != 0) { 1472c39eefe7SBjoern A. Zeeb bus_dmamap_unload(obj->dmat, obj->dmamap); 1473c39eefe7SBjoern A. Zeeb bus_dmamap_destroy(obj->dmat, obj->dmamap); 1474a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1475f211d536STycho Nightingale uma_zfree(linux_dma_obj_zone, obj); 1476f211d536STycho Nightingale return (0); 1477f211d536STycho Nightingale } 1478a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1479f211d536STycho Nightingale return (obj->dma_addr); 1480f211d536STycho Nightingale } 1481b961c0f2STycho Nightingale #else 148212698731SBjoern A. Zeeb static dma_addr_t 1483c39eefe7SBjoern A. Zeeb linux_dma_map_phys_common(struct device *dev __unused, vm_paddr_t phys, 1484c39eefe7SBjoern A. Zeeb size_t len __unused, bus_dma_tag_t dmat __unused) 1485b961c0f2STycho Nightingale { 1486b961c0f2STycho Nightingale return (phys); 1487b961c0f2STycho Nightingale } 1488b961c0f2STycho Nightingale #endif 1489f211d536STycho Nightingale 1490c39eefe7SBjoern A. Zeeb dma_addr_t 1491c39eefe7SBjoern A. Zeeb linux_dma_map_phys(struct device *dev, vm_paddr_t phys, size_t len) 1492c39eefe7SBjoern A. Zeeb { 1493c39eefe7SBjoern A. Zeeb struct linux_dma_priv *priv; 1494c39eefe7SBjoern A. Zeeb 1495c39eefe7SBjoern A. Zeeb priv = dev->dma_priv; 1496c39eefe7SBjoern A. Zeeb return (linux_dma_map_phys_common(dev, phys, len, priv->dmat)); 1497c39eefe7SBjoern A. Zeeb } 1498c39eefe7SBjoern A. Zeeb 1499b961c0f2STycho Nightingale #if defined(__i386__) || defined(__amd64__) || defined(__aarch64__) 1500f211d536STycho Nightingale void 1501f211d536STycho Nightingale linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len) 1502f211d536STycho Nightingale { 1503f211d536STycho Nightingale struct linux_dma_priv *priv; 1504f211d536STycho Nightingale struct linux_dma_obj *obj; 1505f211d536STycho Nightingale 1506f211d536STycho Nightingale priv = dev->dma_priv; 1507f211d536STycho Nightingale 1508b961c0f2STycho Nightingale if (pctrie_is_empty(&priv->ptree)) 1509b961c0f2STycho Nightingale return; 1510b961c0f2STycho Nightingale 1511a6619e8dSHans Petter Selasky DMA_PRIV_LOCK(priv); 1512f211d536STycho Nightingale obj = LINUX_DMA_PCTRIE_LOOKUP(&priv->ptree, dma_addr); 1513f211d536STycho Nightingale if (obj == NULL) { 1514a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1515f211d536STycho Nightingale return; 1516f211d536STycho Nightingale } 1517f211d536STycho Nightingale LINUX_DMA_PCTRIE_REMOVE(&priv->ptree, dma_addr); 1518c39eefe7SBjoern A. Zeeb bus_dmamap_unload(obj->dmat, obj->dmamap); 1519c39eefe7SBjoern A. Zeeb bus_dmamap_destroy(obj->dmat, obj->dmamap); 1520a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1521f211d536STycho Nightingale 1522f211d536STycho Nightingale uma_zfree(linux_dma_obj_zone, obj); 1523f211d536STycho Nightingale } 1524b961c0f2STycho Nightingale #else 1525b961c0f2STycho Nightingale void 1526b961c0f2STycho Nightingale linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len) 1527b961c0f2STycho Nightingale { 1528b961c0f2STycho Nightingale } 1529b961c0f2STycho Nightingale #endif 1530f211d536STycho Nightingale 1531c39eefe7SBjoern A. Zeeb void * 1532c39eefe7SBjoern A. Zeeb linux_dma_alloc_coherent(struct device *dev, size_t size, 1533c39eefe7SBjoern A. Zeeb dma_addr_t *dma_handle, gfp_t flag) 1534c39eefe7SBjoern A. Zeeb { 1535c39eefe7SBjoern A. Zeeb struct linux_dma_priv *priv; 1536c39eefe7SBjoern A. Zeeb vm_paddr_t high; 1537c39eefe7SBjoern A. Zeeb size_t align; 1538c39eefe7SBjoern A. Zeeb void *mem; 1539c39eefe7SBjoern A. Zeeb 1540c39eefe7SBjoern A. Zeeb if (dev == NULL || dev->dma_priv == NULL) { 1541c39eefe7SBjoern A. Zeeb *dma_handle = 0; 1542c39eefe7SBjoern A. Zeeb return (NULL); 1543c39eefe7SBjoern A. Zeeb } 1544c39eefe7SBjoern A. Zeeb priv = dev->dma_priv; 1545c39eefe7SBjoern A. Zeeb if (priv->dma_coherent_mask) 1546c39eefe7SBjoern A. Zeeb high = priv->dma_coherent_mask; 1547c39eefe7SBjoern A. Zeeb else 1548c39eefe7SBjoern A. Zeeb /* Coherent is lower 32bit only by default in Linux. */ 1549c39eefe7SBjoern A. Zeeb high = BUS_SPACE_MAXADDR_32BIT; 1550c39eefe7SBjoern A. Zeeb align = PAGE_SIZE << get_order(size); 1551c39eefe7SBjoern A. Zeeb /* Always zero the allocation. */ 1552c39eefe7SBjoern A. Zeeb flag |= M_ZERO; 1553f49fd63aSJohn Baldwin mem = kmem_alloc_contig(size, flag & GFP_NATIVE_MASK, 0, high, 1554c39eefe7SBjoern A. Zeeb align, 0, VM_MEMATTR_DEFAULT); 1555c39eefe7SBjoern A. Zeeb if (mem != NULL) { 1556c39eefe7SBjoern A. Zeeb *dma_handle = linux_dma_map_phys_common(dev, vtophys(mem), size, 1557c39eefe7SBjoern A. Zeeb priv->dmat_coherent); 1558c39eefe7SBjoern A. Zeeb if (*dma_handle == 0) { 1559f49fd63aSJohn Baldwin kmem_free(mem, size); 1560c39eefe7SBjoern A. Zeeb mem = NULL; 1561c39eefe7SBjoern A. Zeeb } 1562c39eefe7SBjoern A. Zeeb } else { 1563c39eefe7SBjoern A. Zeeb *dma_handle = 0; 1564c39eefe7SBjoern A. Zeeb } 1565c39eefe7SBjoern A. Zeeb return (mem); 1566c39eefe7SBjoern A. Zeeb } 1567c39eefe7SBjoern A. Zeeb 15687105f0d9SBjoern A. Zeeb struct lkpi_devres_dmam_coherent { 15697105f0d9SBjoern A. Zeeb size_t size; 15707105f0d9SBjoern A. Zeeb dma_addr_t *handle; 15717105f0d9SBjoern A. Zeeb void *mem; 15727105f0d9SBjoern A. Zeeb }; 15737105f0d9SBjoern A. Zeeb 15747105f0d9SBjoern A. Zeeb static void 15757105f0d9SBjoern A. Zeeb lkpi_dmam_free_coherent(struct device *dev, void *p) 15767105f0d9SBjoern A. Zeeb { 15777105f0d9SBjoern A. Zeeb struct lkpi_devres_dmam_coherent *dr; 15787105f0d9SBjoern A. Zeeb 15797105f0d9SBjoern A. Zeeb dr = p; 15807105f0d9SBjoern A. Zeeb dma_free_coherent(dev, dr->size, dr->mem, *dr->handle); 15817105f0d9SBjoern A. Zeeb } 15827105f0d9SBjoern A. Zeeb 15837105f0d9SBjoern A. Zeeb void * 15847105f0d9SBjoern A. Zeeb linuxkpi_dmam_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, 15857105f0d9SBjoern A. Zeeb gfp_t flag) 15867105f0d9SBjoern A. Zeeb { 15877105f0d9SBjoern A. Zeeb struct lkpi_devres_dmam_coherent *dr; 15887105f0d9SBjoern A. Zeeb 15897105f0d9SBjoern A. Zeeb dr = lkpi_devres_alloc(lkpi_dmam_free_coherent, 15907105f0d9SBjoern A. Zeeb sizeof(*dr), GFP_KERNEL | __GFP_ZERO); 15917105f0d9SBjoern A. Zeeb 15927105f0d9SBjoern A. Zeeb if (dr == NULL) 15937105f0d9SBjoern A. Zeeb return (NULL); 15947105f0d9SBjoern A. Zeeb 15957105f0d9SBjoern A. Zeeb dr->size = size; 15967105f0d9SBjoern A. Zeeb dr->mem = linux_dma_alloc_coherent(dev, size, dma_handle, flag); 15977105f0d9SBjoern A. Zeeb dr->handle = dma_handle; 15987105f0d9SBjoern A. Zeeb if (dr->mem == NULL) { 15997105f0d9SBjoern A. Zeeb lkpi_devres_free(dr); 16007105f0d9SBjoern A. Zeeb return (NULL); 16017105f0d9SBjoern A. Zeeb } 16027105f0d9SBjoern A. Zeeb 16037105f0d9SBjoern A. Zeeb lkpi_devres_add(dev, dr); 16047105f0d9SBjoern A. Zeeb return (dr->mem); 16057105f0d9SBjoern A. Zeeb } 16067105f0d9SBjoern A. Zeeb 160795edb10bSBjoern A. Zeeb void 160895edb10bSBjoern A. Zeeb linuxkpi_dma_sync(struct device *dev, dma_addr_t dma_addr, size_t size, 160995edb10bSBjoern A. Zeeb bus_dmasync_op_t op) 161095edb10bSBjoern A. Zeeb { 161195edb10bSBjoern A. Zeeb struct linux_dma_priv *priv; 161295edb10bSBjoern A. Zeeb struct linux_dma_obj *obj; 161395edb10bSBjoern A. Zeeb 161495edb10bSBjoern A. Zeeb priv = dev->dma_priv; 161595edb10bSBjoern A. Zeeb 161695edb10bSBjoern A. Zeeb if (pctrie_is_empty(&priv->ptree)) 161795edb10bSBjoern A. Zeeb return; 161895edb10bSBjoern A. Zeeb 161995edb10bSBjoern A. Zeeb DMA_PRIV_LOCK(priv); 162095edb10bSBjoern A. Zeeb obj = LINUX_DMA_PCTRIE_LOOKUP(&priv->ptree, dma_addr); 162195edb10bSBjoern A. Zeeb if (obj == NULL) { 162295edb10bSBjoern A. Zeeb DMA_PRIV_UNLOCK(priv); 162395edb10bSBjoern A. Zeeb return; 162495edb10bSBjoern A. Zeeb } 162595edb10bSBjoern A. Zeeb 162695edb10bSBjoern A. Zeeb bus_dmamap_sync(obj->dmat, obj->dmamap, op); 162795edb10bSBjoern A. Zeeb DMA_PRIV_UNLOCK(priv); 162895edb10bSBjoern A. Zeeb } 162995edb10bSBjoern A. Zeeb 1630f211d536STycho Nightingale int 1631f211d536STycho Nightingale linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl, int nents, 163295edb10bSBjoern A. Zeeb enum dma_data_direction direction, unsigned long attrs __unused) 1633f211d536STycho Nightingale { 1634f211d536STycho Nightingale struct linux_dma_priv *priv; 1635442d12d8SHans Petter Selasky struct scatterlist *sg; 1636442d12d8SHans Petter Selasky int i, nseg; 1637f211d536STycho Nightingale bus_dma_segment_t seg; 1638f211d536STycho Nightingale 1639f211d536STycho Nightingale priv = dev->dma_priv; 1640f211d536STycho Nightingale 1641a6619e8dSHans Petter Selasky DMA_PRIV_LOCK(priv); 1642442d12d8SHans Petter Selasky 1643442d12d8SHans Petter Selasky /* create common DMA map in the first S/G entry */ 1644442d12d8SHans Petter Selasky if (bus_dmamap_create(priv->dmat, 0, &sgl->dma_map) != 0) { 1645a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1646f211d536STycho Nightingale return (0); 1647f211d536STycho Nightingale } 1648f211d536STycho Nightingale 1649442d12d8SHans Petter Selasky /* load all S/G list entries */ 1650442d12d8SHans Petter Selasky for_each_sg(sgl, sg, nents, i) { 1651f211d536STycho Nightingale nseg = -1; 1652442d12d8SHans Petter Selasky if (_bus_dmamap_load_phys(priv->dmat, sgl->dma_map, 1653442d12d8SHans Petter Selasky sg_phys(sg), sg->length, BUS_DMA_NOWAIT, 1654f211d536STycho Nightingale &seg, &nseg) != 0) { 1655442d12d8SHans Petter Selasky bus_dmamap_unload(priv->dmat, sgl->dma_map); 1656442d12d8SHans Petter Selasky bus_dmamap_destroy(priv->dmat, sgl->dma_map); 1657a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1658f211d536STycho Nightingale return (0); 1659f211d536STycho Nightingale } 1660442d12d8SHans Petter Selasky KASSERT(nseg == 0, 1661442d12d8SHans Petter Selasky ("More than one segment (nseg=%d)", nseg + 1)); 1662f211d536STycho Nightingale 1663442d12d8SHans Petter Selasky sg_dma_address(sg) = seg.ds_addr; 1664f211d536STycho Nightingale } 166595edb10bSBjoern A. Zeeb 166695edb10bSBjoern A. Zeeb switch (direction) { 166795edb10bSBjoern A. Zeeb case DMA_BIDIRECTIONAL: 166895edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREWRITE); 166995edb10bSBjoern A. Zeeb break; 167095edb10bSBjoern A. Zeeb case DMA_TO_DEVICE: 167195edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREREAD); 167295edb10bSBjoern A. Zeeb break; 167395edb10bSBjoern A. Zeeb case DMA_FROM_DEVICE: 167495edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREWRITE); 167595edb10bSBjoern A. Zeeb break; 167695edb10bSBjoern A. Zeeb default: 167795edb10bSBjoern A. Zeeb break; 167895edb10bSBjoern A. Zeeb } 167995edb10bSBjoern A. Zeeb 1680a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1681442d12d8SHans Petter Selasky 1682442d12d8SHans Petter Selasky return (nents); 1683f211d536STycho Nightingale } 1684f211d536STycho Nightingale 1685f211d536STycho Nightingale void 1686f211d536STycho Nightingale linux_dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sgl, 168795edb10bSBjoern A. Zeeb int nents __unused, enum dma_data_direction direction, 168898a6984aSVladimir Kondratyev unsigned long attrs __unused) 1689f211d536STycho Nightingale { 1690f211d536STycho Nightingale struct linux_dma_priv *priv; 1691f211d536STycho Nightingale 1692f211d536STycho Nightingale priv = dev->dma_priv; 1693f211d536STycho Nightingale 1694a6619e8dSHans Petter Selasky DMA_PRIV_LOCK(priv); 169595edb10bSBjoern A. Zeeb 169695edb10bSBjoern A. Zeeb switch (direction) { 169795edb10bSBjoern A. Zeeb case DMA_BIDIRECTIONAL: 169895edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_POSTREAD); 169995edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREREAD); 170095edb10bSBjoern A. Zeeb break; 170195edb10bSBjoern A. Zeeb case DMA_TO_DEVICE: 170295edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_POSTWRITE); 170395edb10bSBjoern A. Zeeb break; 170495edb10bSBjoern A. Zeeb case DMA_FROM_DEVICE: 170595edb10bSBjoern A. Zeeb bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_POSTREAD); 170695edb10bSBjoern A. Zeeb break; 170795edb10bSBjoern A. Zeeb default: 170895edb10bSBjoern A. Zeeb break; 170995edb10bSBjoern A. Zeeb } 171095edb10bSBjoern A. Zeeb 1711442d12d8SHans Petter Selasky bus_dmamap_unload(priv->dmat, sgl->dma_map); 1712442d12d8SHans Petter Selasky bus_dmamap_destroy(priv->dmat, sgl->dma_map); 1713a6619e8dSHans Petter Selasky DMA_PRIV_UNLOCK(priv); 1714f211d536STycho Nightingale } 1715f211d536STycho Nightingale 171693a203eaSHans Petter Selasky struct dma_pool { 171793a203eaSHans Petter Selasky struct device *pool_device; 171893a203eaSHans Petter Selasky uma_zone_t pool_zone; 1719a6619e8dSHans Petter Selasky struct mtx pool_lock; 172093a203eaSHans Petter Selasky bus_dma_tag_t pool_dmat; 172193a203eaSHans Petter Selasky size_t pool_entry_size; 172293a203eaSHans Petter Selasky struct pctrie pool_ptree; 172393a203eaSHans Petter Selasky }; 172493a203eaSHans Petter Selasky 1725a6619e8dSHans Petter Selasky #define DMA_POOL_LOCK(pool) mtx_lock(&(pool)->pool_lock) 1726a6619e8dSHans Petter Selasky #define DMA_POOL_UNLOCK(pool) mtx_unlock(&(pool)->pool_lock) 1727a6619e8dSHans Petter Selasky 1728f211d536STycho Nightingale static inline int 1729f211d536STycho Nightingale dma_pool_obj_ctor(void *mem, int size, void *arg, int flags) 1730f211d536STycho Nightingale { 1731f211d536STycho Nightingale struct linux_dma_obj *obj = mem; 1732f211d536STycho Nightingale struct dma_pool *pool = arg; 1733f211d536STycho Nightingale int error, nseg; 1734f211d536STycho Nightingale bus_dma_segment_t seg; 1735f211d536STycho Nightingale 1736f211d536STycho Nightingale nseg = -1; 1737a6619e8dSHans Petter Selasky DMA_POOL_LOCK(pool); 1738f211d536STycho Nightingale error = _bus_dmamap_load_phys(pool->pool_dmat, obj->dmamap, 1739f211d536STycho Nightingale vtophys(obj->vaddr), pool->pool_entry_size, BUS_DMA_NOWAIT, 1740f211d536STycho Nightingale &seg, &nseg); 1741a6619e8dSHans Petter Selasky DMA_POOL_UNLOCK(pool); 1742f211d536STycho Nightingale if (error != 0) { 1743f211d536STycho Nightingale return (error); 1744f211d536STycho Nightingale } 1745f211d536STycho Nightingale KASSERT(++nseg == 1, ("More than one segment (nseg=%d)", nseg)); 1746f211d536STycho Nightingale obj->dma_addr = seg.ds_addr; 1747f211d536STycho Nightingale 1748f211d536STycho Nightingale return (0); 1749f211d536STycho Nightingale } 1750f211d536STycho Nightingale 1751f211d536STycho Nightingale static void 1752f211d536STycho Nightingale dma_pool_obj_dtor(void *mem, int size, void *arg) 1753f211d536STycho Nightingale { 1754f211d536STycho Nightingale struct linux_dma_obj *obj = mem; 1755f211d536STycho Nightingale struct dma_pool *pool = arg; 1756f211d536STycho Nightingale 1757a6619e8dSHans Petter Selasky DMA_POOL_LOCK(pool); 1758f211d536STycho Nightingale bus_dmamap_unload(pool->pool_dmat, obj->dmamap); 1759a6619e8dSHans Petter Selasky DMA_POOL_UNLOCK(pool); 1760f211d536STycho Nightingale } 1761f211d536STycho Nightingale 1762f211d536STycho Nightingale static int 1763f211d536STycho Nightingale dma_pool_obj_import(void *arg, void **store, int count, int domain __unused, 1764f211d536STycho Nightingale int flags) 1765f211d536STycho Nightingale { 1766f211d536STycho Nightingale struct dma_pool *pool = arg; 1767f211d536STycho Nightingale struct linux_dma_obj *obj; 1768f211d536STycho Nightingale int error, i; 1769f211d536STycho Nightingale 1770f211d536STycho Nightingale for (i = 0; i < count; i++) { 1771f211d536STycho Nightingale obj = uma_zalloc(linux_dma_obj_zone, flags); 1772f211d536STycho Nightingale if (obj == NULL) 1773f211d536STycho Nightingale break; 1774f211d536STycho Nightingale 1775f211d536STycho Nightingale error = bus_dmamem_alloc(pool->pool_dmat, &obj->vaddr, 1776f211d536STycho Nightingale BUS_DMA_NOWAIT, &obj->dmamap); 1777f211d536STycho Nightingale if (error!= 0) { 1778f211d536STycho Nightingale uma_zfree(linux_dma_obj_zone, obj); 1779f211d536STycho Nightingale break; 1780f211d536STycho Nightingale } 1781f211d536STycho Nightingale 1782f211d536STycho Nightingale store[i] = obj; 1783f211d536STycho Nightingale } 1784f211d536STycho Nightingale 1785f211d536STycho Nightingale return (i); 1786f211d536STycho Nightingale } 1787f211d536STycho Nightingale 1788f211d536STycho Nightingale static void 1789f211d536STycho Nightingale dma_pool_obj_release(void *arg, void **store, int count) 1790f211d536STycho Nightingale { 1791f211d536STycho Nightingale struct dma_pool *pool = arg; 1792f211d536STycho Nightingale struct linux_dma_obj *obj; 1793f211d536STycho Nightingale int i; 1794f211d536STycho Nightingale 1795f211d536STycho Nightingale for (i = 0; i < count; i++) { 1796f211d536STycho Nightingale obj = store[i]; 1797f211d536STycho Nightingale bus_dmamem_free(pool->pool_dmat, obj->vaddr, obj->dmamap); 1798f211d536STycho Nightingale uma_zfree(linux_dma_obj_zone, obj); 1799f211d536STycho Nightingale } 1800f211d536STycho Nightingale } 1801f211d536STycho Nightingale 1802f211d536STycho Nightingale struct dma_pool * 1803f211d536STycho Nightingale linux_dma_pool_create(char *name, struct device *dev, size_t size, 1804f211d536STycho Nightingale size_t align, size_t boundary) 1805f211d536STycho Nightingale { 1806f211d536STycho Nightingale struct linux_dma_priv *priv; 1807f211d536STycho Nightingale struct dma_pool *pool; 1808f211d536STycho Nightingale 1809f211d536STycho Nightingale priv = dev->dma_priv; 1810f211d536STycho Nightingale 18118a8e86b8SJean-Sébastien Pédron pool = kzalloc(sizeof(*pool), GFP_KERNEL); 18125a637529SHans Petter Selasky pool->pool_device = dev; 1813f211d536STycho Nightingale pool->pool_entry_size = size; 1814f211d536STycho Nightingale 1815f211d536STycho Nightingale if (bus_dma_tag_create(bus_get_dma_tag(dev->bsddev), 1816f211d536STycho Nightingale align, boundary, /* alignment, boundary */ 1817f211d536STycho Nightingale priv->dma_mask, /* lowaddr */ 1818f211d536STycho Nightingale BUS_SPACE_MAXADDR, /* highaddr */ 1819f211d536STycho Nightingale NULL, NULL, /* filtfunc, filtfuncarg */ 1820f211d536STycho Nightingale size, /* maxsize */ 1821f211d536STycho Nightingale 1, /* nsegments */ 1822f211d536STycho Nightingale size, /* maxsegsz */ 1823f211d536STycho Nightingale 0, /* flags */ 1824f211d536STycho Nightingale NULL, NULL, /* lockfunc, lockfuncarg */ 1825f211d536STycho Nightingale &pool->pool_dmat)) { 1826f211d536STycho Nightingale kfree(pool); 1827f211d536STycho Nightingale return (NULL); 1828f211d536STycho Nightingale } 1829f211d536STycho Nightingale 1830f211d536STycho Nightingale pool->pool_zone = uma_zcache_create(name, -1, dma_pool_obj_ctor, 1831f211d536STycho Nightingale dma_pool_obj_dtor, NULL, NULL, dma_pool_obj_import, 1832f211d536STycho Nightingale dma_pool_obj_release, pool, 0); 1833f211d536STycho Nightingale 1834a6619e8dSHans Petter Selasky mtx_init(&pool->pool_lock, "lkpi-dma-pool", NULL, MTX_DEF); 1835f211d536STycho Nightingale pctrie_init(&pool->pool_ptree); 1836f211d536STycho Nightingale 1837f211d536STycho Nightingale return (pool); 1838f211d536STycho Nightingale } 1839f211d536STycho Nightingale 1840f211d536STycho Nightingale void 1841f211d536STycho Nightingale linux_dma_pool_destroy(struct dma_pool *pool) 1842f211d536STycho Nightingale { 1843f211d536STycho Nightingale 1844f211d536STycho Nightingale uma_zdestroy(pool->pool_zone); 1845f211d536STycho Nightingale bus_dma_tag_destroy(pool->pool_dmat); 1846a6619e8dSHans Petter Selasky mtx_destroy(&pool->pool_lock); 1847f211d536STycho Nightingale kfree(pool); 1848f211d536STycho Nightingale } 1849f211d536STycho Nightingale 18502afeed13SBjoern A. Zeeb void 18512afeed13SBjoern A. Zeeb lkpi_dmam_pool_destroy(struct device *dev, void *p) 18522afeed13SBjoern A. Zeeb { 18532afeed13SBjoern A. Zeeb struct dma_pool *pool; 18542afeed13SBjoern A. Zeeb 18552afeed13SBjoern A. Zeeb pool = *(struct dma_pool **)p; 18562afeed13SBjoern A. Zeeb LINUX_DMA_PCTRIE_RECLAIM(&pool->pool_ptree); 18572afeed13SBjoern A. Zeeb linux_dma_pool_destroy(pool); 18582afeed13SBjoern A. Zeeb } 18592afeed13SBjoern A. Zeeb 1860f211d536STycho Nightingale void * 1861f211d536STycho Nightingale linux_dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags, 1862f211d536STycho Nightingale dma_addr_t *handle) 1863f211d536STycho Nightingale { 1864f211d536STycho Nightingale struct linux_dma_obj *obj; 1865f211d536STycho Nightingale 1866f6e7d67aSBryan Drewery obj = uma_zalloc_arg(pool->pool_zone, pool, mem_flags & GFP_NATIVE_MASK); 1867f211d536STycho Nightingale if (obj == NULL) 1868f211d536STycho Nightingale return (NULL); 1869f211d536STycho Nightingale 1870a6619e8dSHans Petter Selasky DMA_POOL_LOCK(pool); 1871f211d536STycho Nightingale if (LINUX_DMA_PCTRIE_INSERT(&pool->pool_ptree, obj) != 0) { 1872a6619e8dSHans Petter Selasky DMA_POOL_UNLOCK(pool); 1873f211d536STycho Nightingale uma_zfree_arg(pool->pool_zone, obj, pool); 1874f211d536STycho Nightingale return (NULL); 1875f211d536STycho Nightingale } 1876a6619e8dSHans Petter Selasky DMA_POOL_UNLOCK(pool); 1877f211d536STycho Nightingale 1878f211d536STycho Nightingale *handle = obj->dma_addr; 1879f211d536STycho Nightingale return (obj->vaddr); 1880f211d536STycho Nightingale } 1881f211d536STycho Nightingale 1882f211d536STycho Nightingale void 1883f211d536STycho Nightingale linux_dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t dma_addr) 1884f211d536STycho Nightingale { 1885f211d536STycho Nightingale struct linux_dma_obj *obj; 1886f211d536STycho Nightingale 1887a6619e8dSHans Petter Selasky DMA_POOL_LOCK(pool); 1888f211d536STycho Nightingale obj = LINUX_DMA_PCTRIE_LOOKUP(&pool->pool_ptree, dma_addr); 1889f211d536STycho Nightingale if (obj == NULL) { 1890a6619e8dSHans Petter Selasky DMA_POOL_UNLOCK(pool); 1891f211d536STycho Nightingale return; 1892f211d536STycho Nightingale } 1893f211d536STycho Nightingale LINUX_DMA_PCTRIE_REMOVE(&pool->pool_ptree, dma_addr); 1894a6619e8dSHans Petter Selasky DMA_POOL_UNLOCK(pool); 1895f211d536STycho Nightingale 1896f211d536STycho Nightingale uma_zfree_arg(pool->pool_zone, obj, pool); 1897f211d536STycho Nightingale } 18982b68c973SEmmanuel Vadot 18992b68c973SEmmanuel Vadot static int 19002b68c973SEmmanuel Vadot linux_backlight_get_status(device_t dev, struct backlight_props *props) 19012b68c973SEmmanuel Vadot { 19022b68c973SEmmanuel Vadot struct pci_dev *pdev; 19032b68c973SEmmanuel Vadot 19042b68c973SEmmanuel Vadot linux_set_current(curthread); 19052b68c973SEmmanuel Vadot pdev = device_get_softc(dev); 19062b68c973SEmmanuel Vadot 19072b68c973SEmmanuel Vadot props->brightness = pdev->dev.bd->props.brightness; 19082b68c973SEmmanuel Vadot props->brightness = props->brightness * 100 / pdev->dev.bd->props.max_brightness; 19092b68c973SEmmanuel Vadot props->nlevels = 0; 19102b68c973SEmmanuel Vadot 19112b68c973SEmmanuel Vadot return (0); 19122b68c973SEmmanuel Vadot } 19132b68c973SEmmanuel Vadot 19142b68c973SEmmanuel Vadot static int 19152b68c973SEmmanuel Vadot linux_backlight_get_info(device_t dev, struct backlight_info *info) 19162b68c973SEmmanuel Vadot { 19172b68c973SEmmanuel Vadot struct pci_dev *pdev; 19182b68c973SEmmanuel Vadot 19192b68c973SEmmanuel Vadot linux_set_current(curthread); 19202b68c973SEmmanuel Vadot pdev = device_get_softc(dev); 19212b68c973SEmmanuel Vadot 19222b68c973SEmmanuel Vadot info->type = BACKLIGHT_TYPE_PANEL; 19232b68c973SEmmanuel Vadot strlcpy(info->name, pdev->dev.bd->name, BACKLIGHTMAXNAMELENGTH); 19242b68c973SEmmanuel Vadot return (0); 19252b68c973SEmmanuel Vadot } 19262b68c973SEmmanuel Vadot 19272b68c973SEmmanuel Vadot static int 19282b68c973SEmmanuel Vadot linux_backlight_update_status(device_t dev, struct backlight_props *props) 19292b68c973SEmmanuel Vadot { 19302b68c973SEmmanuel Vadot struct pci_dev *pdev; 19312b68c973SEmmanuel Vadot 19322b68c973SEmmanuel Vadot linux_set_current(curthread); 19332b68c973SEmmanuel Vadot pdev = device_get_softc(dev); 19342b68c973SEmmanuel Vadot 19352b68c973SEmmanuel Vadot pdev->dev.bd->props.brightness = pdev->dev.bd->props.max_brightness * 19362b68c973SEmmanuel Vadot props->brightness / 100; 1937b52e3638SVladimir Kondratyev pdev->dev.bd->props.power = props->brightness == 0 ? 1938b52e3638SVladimir Kondratyev 4/* FB_BLANK_POWERDOWN */ : 0/* FB_BLANK_UNBLANK */; 19392b68c973SEmmanuel Vadot return (pdev->dev.bd->ops->update_status(pdev->dev.bd)); 19402b68c973SEmmanuel Vadot } 19412b68c973SEmmanuel Vadot 19422b68c973SEmmanuel Vadot struct backlight_device * 19432b68c973SEmmanuel Vadot linux_backlight_device_register(const char *name, struct device *dev, 19442b68c973SEmmanuel Vadot void *data, const struct backlight_ops *ops, struct backlight_properties *props) 19452b68c973SEmmanuel Vadot { 19462b68c973SEmmanuel Vadot 19472b68c973SEmmanuel Vadot dev->bd = malloc(sizeof(*dev->bd), M_DEVBUF, M_WAITOK | M_ZERO); 19482b68c973SEmmanuel Vadot dev->bd->ops = ops; 19492b68c973SEmmanuel Vadot dev->bd->props.type = props->type; 19502b68c973SEmmanuel Vadot dev->bd->props.max_brightness = props->max_brightness; 19512b68c973SEmmanuel Vadot dev->bd->props.brightness = props->brightness; 19522b68c973SEmmanuel Vadot dev->bd->props.power = props->power; 19532b68c973SEmmanuel Vadot dev->bd->data = data; 19542b68c973SEmmanuel Vadot dev->bd->dev = dev; 19552b68c973SEmmanuel Vadot dev->bd->name = strdup(name, M_DEVBUF); 19562b68c973SEmmanuel Vadot 19572b68c973SEmmanuel Vadot dev->backlight_dev = backlight_register(name, dev->bsddev); 19582b68c973SEmmanuel Vadot 19592b68c973SEmmanuel Vadot return (dev->bd); 19602b68c973SEmmanuel Vadot } 19612b68c973SEmmanuel Vadot 19622b68c973SEmmanuel Vadot void 19632b68c973SEmmanuel Vadot linux_backlight_device_unregister(struct backlight_device *bd) 19642b68c973SEmmanuel Vadot { 19652b68c973SEmmanuel Vadot 19662b68c973SEmmanuel Vadot backlight_destroy(bd->dev->backlight_dev); 19672b68c973SEmmanuel Vadot free(bd->name, M_DEVBUF); 19682b68c973SEmmanuel Vadot free(bd, M_DEVBUF); 19692b68c973SEmmanuel Vadot } 1970