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