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