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