xref: /titanic_44/usr/src/uts/common/io/drm/drm_pci.c (revision d0538f66491267879b7418b21ad78e3dcc2dcc83)
160405de4Skz151634 /*
2*d0538f66Scg149915  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
360405de4Skz151634  * Use is subject to license terms.
460405de4Skz151634  */
560405de4Skz151634 
660405de4Skz151634 /* BEGIN CSTYLED */
760405de4Skz151634 /**
860405de4Skz151634  * \file drm_pci.h
960405de4Skz151634  * \brief PCI consistent, DMA-accessible memory functions.
1060405de4Skz151634  *
1160405de4Skz151634  * \author Eric Anholt <anholt@FreeBSD.org>
1260405de4Skz151634  */
1360405de4Skz151634 
1460405de4Skz151634 /*-
1560405de4Skz151634  * Copyright 2003 Eric Anholt.
1660405de4Skz151634  * All Rights Reserved.
1760405de4Skz151634  *
1860405de4Skz151634  * Permission is hereby granted, free of charge, to any person obtaining a
1960405de4Skz151634  * copy of this software and associated documentation files (the "Software"),
2060405de4Skz151634  * to deal in the Software without restriction, including without limitation
2160405de4Skz151634  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
2260405de4Skz151634  * and/or sell copies of the Software, and to permit persons to whom the
2360405de4Skz151634  * Software is furnished to do so, subject to the following conditions:
2460405de4Skz151634  *
2560405de4Skz151634  * The above copyright notice and this permission notice (including the next
2660405de4Skz151634  * paragraph) shall be included in all copies or substantial portions of the
2760405de4Skz151634  * Software.
2860405de4Skz151634  *
2960405de4Skz151634  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3060405de4Skz151634  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
3160405de4Skz151634  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
3260405de4Skz151634  * AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
3360405de4Skz151634  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
3460405de4Skz151634  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3560405de4Skz151634  */
3660405de4Skz151634 
3760405de4Skz151634 /**********************************************************************/
3860405de4Skz151634 /** \name PCI memory */
3960405de4Skz151634 /*@{*/
4060405de4Skz151634 /* END CSTYLED */
4160405de4Skz151634 
4260405de4Skz151634 #pragma ident	"%Z%%M%	%I%	%E% SMI"
4360405de4Skz151634 
4460405de4Skz151634 #include "drmP.h"
45*d0538f66Scg149915 #include <vm/seg_kmem.h>
4660405de4Skz151634 
4760405de4Skz151634 #define	PCI_DEVICE(x)	(((x)>>11) & 0x1f)
4860405de4Skz151634 #define	PCI_FUNCTION(x)	(((x) & 0x700) >> 8)
4960405de4Skz151634 #define	PCI_BUS(x)	(((x) & 0xff0000) >> 16)
5060405de4Skz151634 
5160405de4Skz151634 typedef struct drm_pci_resource {
5260405de4Skz151634 	uint_t	regnum;
5360405de4Skz151634 	unsigned long offset;
5460405de4Skz151634 	unsigned long size;
5560405de4Skz151634 } drm_pci_resource_t;
5660405de4Skz151634 
5760405de4Skz151634 int
pci_get_info(drm_device_t * softstate,int * bus,int * slot,int * func)58*d0538f66Scg149915 pci_get_info(drm_device_t *softstate, int *bus, int *slot, int *func)
5960405de4Skz151634 {
6060405de4Skz151634 	int *regs_list;
6160405de4Skz151634 	uint_t nregs = 0;
6260405de4Skz151634 
6360405de4Skz151634 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, softstate->dip,
6460405de4Skz151634 	    DDI_PROP_DONTPASS, "reg", (int **)&regs_list, &nregs)
6560405de4Skz151634 	    != DDI_PROP_SUCCESS) {
6660405de4Skz151634 		DRM_ERROR("pci_get_info: get pci function bus device failed");
6760405de4Skz151634 		goto error;
6860405de4Skz151634 	}
6960405de4Skz151634 	*bus  = (int)PCI_BUS(regs_list[0]);
7060405de4Skz151634 	*slot = (int)PCI_DEVICE(regs_list[0]);
7160405de4Skz151634 	*func = (int)PCI_FUNCTION(regs_list[0]);
7260405de4Skz151634 
7360405de4Skz151634 	if (nregs > 0) {
7460405de4Skz151634 		ddi_prop_free(regs_list);
7560405de4Skz151634 	}
7660405de4Skz151634 	return (DDI_SUCCESS);
7760405de4Skz151634 error:
7860405de4Skz151634 	if (nregs > 0) {
7960405de4Skz151634 		ddi_prop_free(regs_list);
8060405de4Skz151634 	}
8160405de4Skz151634 	return (DDI_FAILURE);
8260405de4Skz151634 }
8360405de4Skz151634 
8460405de4Skz151634 int
pci_get_irq(drm_device_t * statep)85*d0538f66Scg149915 pci_get_irq(drm_device_t *statep)
8660405de4Skz151634 {
87*d0538f66Scg149915 	int irq;
8860405de4Skz151634 
89*d0538f66Scg149915 	extern int drm_supp_get_irq(void *);
9060405de4Skz151634 
91*d0538f66Scg149915 	irq = ddi_prop_get_int(DDI_DEV_T_ANY,
92*d0538f66Scg149915 	    statep->dip, DDI_PROP_DONTPASS, "interrupts", -1);
93*d0538f66Scg149915 
94*d0538f66Scg149915 	if (irq > 0) {
95*d0538f66Scg149915 		irq = drm_supp_get_irq(statep->drm_handle);
96*d0538f66Scg149915 	}
97*d0538f66Scg149915 
98*d0538f66Scg149915 	return (irq);
9960405de4Skz151634 }
10060405de4Skz151634 
10160405de4Skz151634 int
pci_get_vendor(drm_device_t * statep)102*d0538f66Scg149915 pci_get_vendor(drm_device_t *statep)
10360405de4Skz151634 {
104*d0538f66Scg149915 	int vendorid;
10560405de4Skz151634 
106*d0538f66Scg149915 	vendorid = ddi_prop_get_int(DDI_DEV_T_ANY,
107*d0538f66Scg149915 	    statep->dip, DDI_PROP_DONTPASS, "vendor-id", 0);
10860405de4Skz151634 
109*d0538f66Scg149915 	return (vendorid);
11060405de4Skz151634 }
11160405de4Skz151634 
11260405de4Skz151634 int
pci_get_device(drm_device_t * statep)113*d0538f66Scg149915 pci_get_device(drm_device_t *statep)
11460405de4Skz151634 {
115*d0538f66Scg149915 	int deviceid;
11660405de4Skz151634 
117*d0538f66Scg149915 	deviceid = ddi_prop_get_int(DDI_DEV_T_ANY,
118*d0538f66Scg149915 	    statep->dip, DDI_PROP_DONTPASS, "device-id", 0);
11960405de4Skz151634 
120*d0538f66Scg149915 	return (deviceid);
12160405de4Skz151634 }
12260405de4Skz151634 
12360405de4Skz151634 void
drm_core_ioremap(struct drm_local_map * map,drm_device_t * dev)124*d0538f66Scg149915 drm_core_ioremap(struct drm_local_map *map, drm_device_t *dev)
12560405de4Skz151634 {
126*d0538f66Scg149915 	if ((map->type == _DRM_AGP) && dev->agp) {
127*d0538f66Scg149915 		/*
128*d0538f66Scg149915 		 * During AGP mapping initialization, we map AGP aperture
129*d0538f66Scg149915 		 * into kernel space. So, when we access the memory which
130*d0538f66Scg149915 		 * managed by agp gart in kernel space, we have to go
131*d0538f66Scg149915 		 * through two-level address translation: kernel virtual
132*d0538f66Scg149915 		 * address --> aperture address --> physical address. For
133*d0538f66Scg149915 		 * improving this, here in opensourced code, agp_remap()
134*d0538f66Scg149915 		 * gets invoking to dispose the mapping between agp aperture
135*d0538f66Scg149915 		 * and kernel space, and directly map the actual physical
136*d0538f66Scg149915 		 * memory which is allocated to agp gart to kernel space.
137*d0538f66Scg149915 		 * After that, access to physical memory managed by agp gart
138*d0538f66Scg149915 		 * hardware in kernel space doesn't go through agp hardware,
139*d0538f66Scg149915 		 * it will be: kernel virtual ---> physical address.
140*d0538f66Scg149915 		 * Obviously, it is more efficient. But in solaris operating
141*d0538f66Scg149915 		 * system, the ioctl AGPIOC_ALLOCATE of apggart driver does
142*d0538f66Scg149915 		 * not return physical address. We are unable to create the
143*d0538f66Scg149915 		 * direct mapping between kernel space and agp memory. So,
144*d0538f66Scg149915 		 * we remove the calling to agp_remap().
145*d0538f66Scg149915 		 */
146*d0538f66Scg149915 		DRM_DEBUG("drm_core_ioremap: skipping agp_remap\n");
147*d0538f66Scg149915 	} else {
148*d0538f66Scg149915 		(void) drm_ioremap(dev, map);
14960405de4Skz151634 
150*d0538f66Scg149915 	}
15160405de4Skz151634 }
15260405de4Skz151634 
15360405de4Skz151634 /*ARGSUSED*/
15460405de4Skz151634 void
drm_core_ioremapfree(struct drm_local_map * map,drm_device_t * dev)155*d0538f66Scg149915 drm_core_ioremapfree(struct drm_local_map *map, drm_device_t *dev)
15660405de4Skz151634 {
157*d0538f66Scg149915 	if (map->type != _DRM_AGP) {
15860405de4Skz151634 		if (map->handle && map->size)
15960405de4Skz151634 			drm_ioremapfree(map);
16060405de4Skz151634 	} else {
161*d0538f66Scg149915 		/*
162*d0538f66Scg149915 		 * Refer to the comments in drm_core_ioremap() where we removed
163*d0538f66Scg149915 		 * the calling to agp_remap(), correspondingly, we remove the
164*d0538f66Scg149915 		 * calling to agp_remap_free(dev, map);
165*d0538f66Scg149915 		 */
166*d0538f66Scg149915 		DRM_DEBUG("drm_core_ioremap: skipping agp_remap_free\n");
16760405de4Skz151634 	}
16860405de4Skz151634 }
16960405de4Skz151634 
17060405de4Skz151634 struct drm_local_map *
drm_core_findmap(drm_device_t * dev,unsigned long handle)171*d0538f66Scg149915 drm_core_findmap(drm_device_t *dev, unsigned long handle)
17260405de4Skz151634 {
17360405de4Skz151634 	drm_local_map_t *map;
17460405de4Skz151634 
17560405de4Skz151634 	DRM_SPINLOCK_ASSERT(&dev->dev_lock);
176*d0538f66Scg149915 
177*d0538f66Scg149915 /*
178*d0538f66Scg149915  * For the time being, we compare the low 32 bit only,
179*d0538f66Scg149915  * We will hash handle to 32-bit to solve this issue later.
180*d0538f66Scg149915  */
18160405de4Skz151634 	TAILQ_FOREACH(map, &dev->maplist, link) {
182*d0538f66Scg149915 		if ((((unsigned long)map->handle) & 0x00000000ffffffff)
183*d0538f66Scg149915 		    == (handle & 0x00000000ffffffff))
18460405de4Skz151634 			return (map);
18560405de4Skz151634 	}
186*d0538f66Scg149915 
18760405de4Skz151634 	return (NULL);
18860405de4Skz151634 }
18960405de4Skz151634 
19060405de4Skz151634 /*
19160405de4Skz151634  * pci_alloc_consistent()
19260405de4Skz151634  */
19360405de4Skz151634 static ddi_dma_attr_t	hw_dma_attr = {
194*d0538f66Scg149915 		DMA_ATTR_V0,		/* version */
195*d0538f66Scg149915 		0,			/* addr_lo */
196*d0538f66Scg149915 		0xffffffff,	/* addr_hi */
197*d0538f66Scg149915 		0xffffffff,	/* count_max */
198*d0538f66Scg149915 		4096, 			/* alignment */
199*d0538f66Scg149915 		0xfff,			/* burstsize */
200*d0538f66Scg149915 		1,			/* minxfer */
201*d0538f66Scg149915 		0xffffffff,		/* maxxfer */
202*d0538f66Scg149915 		0xffffffff,		/* seg */
203*d0538f66Scg149915 		1,			/* sgllen */
204*d0538f66Scg149915 		4,			/* granular */
205*d0538f66Scg149915 		0			/* flags */
20660405de4Skz151634 };
20760405de4Skz151634 
20860405de4Skz151634 static ddi_device_acc_attr_t hw_acc_attr = {
20960405de4Skz151634 	DDI_DEVICE_ATTR_V0,
21060405de4Skz151634 	DDI_NEVERSWAP_ACC,
21160405de4Skz151634 	DDI_STRICTORDER_ACC
21260405de4Skz151634 };
21360405de4Skz151634 
214*d0538f66Scg149915 
21560405de4Skz151634 void *
drm_pci_alloc(drm_device_t * dev,size_t size,size_t align,dma_addr_t maxaddr,int segments)216*d0538f66Scg149915 drm_pci_alloc(drm_device_t *dev, size_t size,
217*d0538f66Scg149915     size_t align,  dma_addr_t maxaddr, int segments)
21860405de4Skz151634 {
219*d0538f66Scg149915 	drm_dma_handle_t	*dmah;
220*d0538f66Scg149915 	uint_t	count;
22160405de4Skz151634 	int ret = DDI_FAILURE;
22260405de4Skz151634 
22360405de4Skz151634 	/* allocat continous physical memory for hw status page */
224*d0538f66Scg149915 	if (align == 0)
225*d0538f66Scg149915 		hw_dma_attr.dma_attr_align =  1;
226*d0538f66Scg149915 	else
227*d0538f66Scg149915 		hw_dma_attr.dma_attr_align = align;
22860405de4Skz151634 
229*d0538f66Scg149915 	hw_dma_attr.dma_attr_addr_hi = maxaddr;
230*d0538f66Scg149915 	hw_dma_attr.dma_attr_sgllen = segments;
231*d0538f66Scg149915 
232*d0538f66Scg149915 	dmah = kmem_zalloc(sizeof (drm_dma_handle_t), KM_SLEEP);
233*d0538f66Scg149915 	if (ret = ddi_dma_alloc_handle(dev->dip, &hw_dma_attr,
234*d0538f66Scg149915 	    DDI_DMA_SLEEP, NULL, &dmah->dma_hdl)) {
23560405de4Skz151634 		DRM_ERROR("drm_pci_alloc:ddi_dma_alloc_handle failed\n");
23660405de4Skz151634 		goto err3;
23760405de4Skz151634 	}
23860405de4Skz151634 
239*d0538f66Scg149915 	if (ret = ddi_dma_mem_alloc(dmah->dma_hdl, size, &hw_acc_attr,
240*d0538f66Scg149915 	    DDI_DMA_CONSISTENT | IOMEM_DATA_UNCACHED,
241*d0538f66Scg149915 	    DDI_DMA_SLEEP, NULL, (caddr_t *)&dmah->vaddr,
242*d0538f66Scg149915 	    &dmah->real_sz, &dmah->acc_hdl)) {
24360405de4Skz151634 		DRM_ERROR("drm_pci_alloc: ddi_dma_mem_alloc failed\n");
24460405de4Skz151634 		goto err2;
24560405de4Skz151634 	}
24660405de4Skz151634 
247*d0538f66Scg149915 	ret = ddi_dma_addr_bind_handle(dmah->dma_hdl, NULL,
248*d0538f66Scg149915 	    (caddr_t)dmah->vaddr, dmah->real_sz,
24960405de4Skz151634 	    DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
250*d0538f66Scg149915 	    DDI_DMA_SLEEP, NULL, &dmah->cookie,  &count);
251*d0538f66Scg149915 	if (ret != DDI_DMA_MAPPED) {
252*d0538f66Scg149915 		DRM_ERROR("drm_pci_alloc: alloc phys memory failed");
25360405de4Skz151634 		goto err1;
25460405de4Skz151634 	}
255*d0538f66Scg149915 
256*d0538f66Scg149915 	if (count > segments) {
257*d0538f66Scg149915 		(void) ddi_dma_unbind_handle(dmah->dma_hdl);
258*d0538f66Scg149915 		goto err1;
259*d0538f66Scg149915 	}
260*d0538f66Scg149915 
261*d0538f66Scg149915 	dmah->cookie_num = count;
262*d0538f66Scg149915 	if (count == 1)
263*d0538f66Scg149915 		dmah->paddr = dmah->cookie.dmac_address;
264*d0538f66Scg149915 
265*d0538f66Scg149915 	return (dmah);
26660405de4Skz151634 
26760405de4Skz151634 err1:
268*d0538f66Scg149915 	ddi_dma_mem_free(&dmah->acc_hdl);
26960405de4Skz151634 err2:
270*d0538f66Scg149915 	ddi_dma_free_handle(&dmah->dma_hdl);
27160405de4Skz151634 err3:
272*d0538f66Scg149915 	kmem_free(dmah, sizeof (*dmah));
27360405de4Skz151634 	return (NULL);
27460405de4Skz151634 }
27560405de4Skz151634 
27660405de4Skz151634 /*
27760405de4Skz151634  * pci_free_consistent()
27860405de4Skz151634  */
27960405de4Skz151634 /*ARGSUSED*/
28060405de4Skz151634 void
drm_pci_free(drm_device_t * dev,drm_dma_handle_t * dmah)281*d0538f66Scg149915 drm_pci_free(drm_device_t *dev, drm_dma_handle_t *dmah)
28260405de4Skz151634 {
283*d0538f66Scg149915 	ASSERT(dmah != NULL);
284*d0538f66Scg149915 	(void) ddi_dma_unbind_handle(dmah->dma_hdl);
285*d0538f66Scg149915 	ddi_dma_mem_free(&dmah->acc_hdl);
286*d0538f66Scg149915 	ddi_dma_free_handle(&dmah->dma_hdl);
287*d0538f66Scg149915 	kmem_free(dmah, sizeof (drm_dma_handle_t));
28860405de4Skz151634 }
28960405de4Skz151634 
29060405de4Skz151634 int
do_get_pci_res(drm_device_t * dev,drm_pci_resource_t * resp)291*d0538f66Scg149915 do_get_pci_res(drm_device_t *dev, drm_pci_resource_t *resp)
29260405de4Skz151634 {
29360405de4Skz151634 	int length;
29460405de4Skz151634 	pci_regspec_t	*regs;
29560405de4Skz151634 
29660405de4Skz151634 	if (ddi_getlongprop(
297*d0538f66Scg149915 	    DDI_DEV_T_ANY, dev->dip, DDI_PROP_DONTPASS,
29860405de4Skz151634 	    "assigned-addresses", (caddr_t)&regs, &length) !=
29960405de4Skz151634 	    DDI_PROP_SUCCESS) {
30060405de4Skz151634 		DRM_ERROR("do_get_pci_res: ddi_getlongprop failed!\n");
301*d0538f66Scg149915 		return (EFAULT);
30260405de4Skz151634 	}
30360405de4Skz151634 	resp->offset =
30460405de4Skz151634 	    (unsigned long)regs[resp->regnum].pci_phys_low;
30560405de4Skz151634 	resp->size =
30660405de4Skz151634 	    (unsigned long)regs[resp->regnum].pci_size_low;
30760405de4Skz151634 	kmem_free(regs, (size_t)length);
30860405de4Skz151634 
309*d0538f66Scg149915 	return (0);
31060405de4Skz151634 }
31160405de4Skz151634 
31260405de4Skz151634 /*ARGSUSED*/
31360405de4Skz151634 unsigned long
drm_get_resource_start(drm_device_t * softstate,unsigned int regnum)314*d0538f66Scg149915 drm_get_resource_start(drm_device_t *softstate, unsigned int regnum)
31560405de4Skz151634 {
31660405de4Skz151634 	drm_pci_resource_t res;
31760405de4Skz151634 	int ret;
31860405de4Skz151634 
31960405de4Skz151634 	res.regnum = regnum;
32060405de4Skz151634 
32160405de4Skz151634 	ret = do_get_pci_res(softstate, &res);
32260405de4Skz151634 
323*d0538f66Scg149915 	if (ret != 0) {
324*d0538f66Scg149915 		DRM_ERROR("drm_get_resource_start: ioctl failed");
32560405de4Skz151634 		return (0);
32660405de4Skz151634 	}
32760405de4Skz151634 
32860405de4Skz151634 	return (res.offset);
32960405de4Skz151634 
33060405de4Skz151634 }
33160405de4Skz151634 
33260405de4Skz151634 /*ARGSUSED*/
33360405de4Skz151634 unsigned long
drm_get_resource_len(drm_device_t * softstate,unsigned int regnum)334*d0538f66Scg149915 drm_get_resource_len(drm_device_t *softstate, unsigned int regnum)
33560405de4Skz151634 {
33660405de4Skz151634 	drm_pci_resource_t res;
33760405de4Skz151634 	int ret;
33860405de4Skz151634 
33960405de4Skz151634 	res.regnum = regnum;
34060405de4Skz151634 
34160405de4Skz151634 	ret = do_get_pci_res(softstate, &res);
34260405de4Skz151634 
343*d0538f66Scg149915 	if (ret != 0) {
344*d0538f66Scg149915 		DRM_ERROR("drm_get_resource_len: ioctl failed");
34560405de4Skz151634 		return (0);
34660405de4Skz151634 	}
34760405de4Skz151634 
34860405de4Skz151634 	return (res.size);
34960405de4Skz151634 }
350