xref: /titanic_41/usr/src/uts/common/io/drm/drm_pci.c (revision 5fbb41393be5d63f75952b1d72d4df2642d22557)
1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /* BEGIN CSTYLED */
7 /**
8  * \file drm_pci.h
9  * \brief PCI consistent, DMA-accessible memory functions.
10  *
11  * \author Eric Anholt <anholt@FreeBSD.org>
12  */
13 
14 /*-
15  * Copyright 2003 Eric Anholt.
16  * All Rights Reserved.
17  *
18  * Permission is hereby granted, free of charge, to any person obtaining a
19  * copy of this software and associated documentation files (the "Software"),
20  * to deal in the Software without restriction, including without limitation
21  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
22  * and/or sell copies of the Software, and to permit persons to whom the
23  * Software is furnished to do so, subject to the following conditions:
24  *
25  * The above copyright notice and this permission notice (including the next
26  * paragraph) shall be included in all copies or substantial portions of the
27  * Software.
28  *
29  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
32  * AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
33  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35  */
36 
37 /**********************************************************************/
38 /** \name PCI memory */
39 /*@{*/
40 /* END CSTYLED */
41 
42 #pragma ident	"%Z%%M%	%I%	%E% SMI"
43 
44 #include "drmP.h"
45 #include <vm/seg_kmem.h>
46 
47 #define	PCI_DEVICE(x)	(((x)>>11) & 0x1f)
48 #define	PCI_FUNCTION(x)	(((x) & 0x700) >> 8)
49 #define	PCI_BUS(x)	(((x) & 0xff0000) >> 16)
50 
51 typedef struct drm_pci_resource {
52 	uint_t	regnum;
53 	unsigned long offset;
54 	unsigned long size;
55 } drm_pci_resource_t;
56 
57 int
58 pci_get_info(drm_device_t *softstate, int *bus, int *slot, int *func)
59 {
60 	int *regs_list;
61 	uint_t nregs = 0;
62 
63 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, softstate->dip,
64 	    DDI_PROP_DONTPASS, "reg", (int **)&regs_list, &nregs)
65 	    != DDI_PROP_SUCCESS) {
66 		DRM_ERROR("pci_get_info: get pci function bus device failed");
67 		goto error;
68 	}
69 	*bus  = (int)PCI_BUS(regs_list[0]);
70 	*slot = (int)PCI_DEVICE(regs_list[0]);
71 	*func = (int)PCI_FUNCTION(regs_list[0]);
72 
73 	if (nregs > 0) {
74 		ddi_prop_free(regs_list);
75 	}
76 	return (DDI_SUCCESS);
77 error:
78 	if (nregs > 0) {
79 		ddi_prop_free(regs_list);
80 	}
81 	return (DDI_FAILURE);
82 }
83 
84 int
85 pci_get_irq(drm_device_t *statep)
86 {
87 	int irq;
88 
89 	extern int drm_supp_get_irq(void *);
90 
91 	irq = ddi_prop_get_int(DDI_DEV_T_ANY,
92 	    statep->dip, DDI_PROP_DONTPASS, "interrupts", -1);
93 
94 	if (irq > 0) {
95 		irq = drm_supp_get_irq(statep->drm_handle);
96 	}
97 
98 	return (irq);
99 }
100 
101 int
102 pci_get_vendor(drm_device_t *statep)
103 {
104 	int vendorid;
105 
106 	vendorid = ddi_prop_get_int(DDI_DEV_T_ANY,
107 	    statep->dip, DDI_PROP_DONTPASS, "vendor-id", 0);
108 
109 	return (vendorid);
110 }
111 
112 int
113 pci_get_device(drm_device_t *statep)
114 {
115 	int deviceid;
116 
117 	deviceid = ddi_prop_get_int(DDI_DEV_T_ANY,
118 	    statep->dip, DDI_PROP_DONTPASS, "device-id", 0);
119 
120 	return (deviceid);
121 }
122 
123 void
124 drm_core_ioremap(struct drm_local_map *map, drm_device_t *dev)
125 {
126 	if ((map->type == _DRM_AGP) && dev->agp) {
127 		/*
128 		 * During AGP mapping initialization, we map AGP aperture
129 		 * into kernel space. So, when we access the memory which
130 		 * managed by agp gart in kernel space, we have to go
131 		 * through two-level address translation: kernel virtual
132 		 * address --> aperture address --> physical address. For
133 		 * improving this, here in opensourced code, agp_remap()
134 		 * gets invoking to dispose the mapping between agp aperture
135 		 * and kernel space, and directly map the actual physical
136 		 * memory which is allocated to agp gart to kernel space.
137 		 * After that, access to physical memory managed by agp gart
138 		 * hardware in kernel space doesn't go through agp hardware,
139 		 * it will be: kernel virtual ---> physical address.
140 		 * Obviously, it is more efficient. But in solaris operating
141 		 * system, the ioctl AGPIOC_ALLOCATE of apggart driver does
142 		 * not return physical address. We are unable to create the
143 		 * direct mapping between kernel space and agp memory. So,
144 		 * we remove the calling to agp_remap().
145 		 */
146 		DRM_DEBUG("drm_core_ioremap: skipping agp_remap\n");
147 	} else {
148 		(void) drm_ioremap(dev, map);
149 
150 	}
151 }
152 
153 /*ARGSUSED*/
154 void
155 drm_core_ioremapfree(struct drm_local_map *map, drm_device_t *dev)
156 {
157 	if (map->type != _DRM_AGP) {
158 		if (map->handle && map->size)
159 			drm_ioremapfree(map);
160 	} else {
161 		/*
162 		 * Refer to the comments in drm_core_ioremap() where we removed
163 		 * the calling to agp_remap(), correspondingly, we remove the
164 		 * calling to agp_remap_free(dev, map);
165 		 */
166 		DRM_DEBUG("drm_core_ioremap: skipping agp_remap_free\n");
167 	}
168 }
169 
170 struct drm_local_map *
171 drm_core_findmap(drm_device_t *dev, unsigned long handle)
172 {
173 	drm_local_map_t *map;
174 
175 	DRM_SPINLOCK_ASSERT(&dev->dev_lock);
176 
177 /*
178  * For the time being, we compare the low 32 bit only,
179  * We will hash handle to 32-bit to solve this issue later.
180  */
181 	TAILQ_FOREACH(map, &dev->maplist, link) {
182 		if ((((unsigned long)map->handle) & 0x00000000ffffffff)
183 		    == (handle & 0x00000000ffffffff))
184 			return (map);
185 	}
186 
187 	return (NULL);
188 }
189 
190 /*
191  * pci_alloc_consistent()
192  */
193 static ddi_dma_attr_t	hw_dma_attr = {
194 		DMA_ATTR_V0,		/* version */
195 		0,			/* addr_lo */
196 		0xffffffff,	/* addr_hi */
197 		0xffffffff,	/* count_max */
198 		4096, 			/* alignment */
199 		0xfff,			/* burstsize */
200 		1,			/* minxfer */
201 		0xffffffff,		/* maxxfer */
202 		0xffffffff,		/* seg */
203 		1,			/* sgllen */
204 		4,			/* granular */
205 		0			/* flags */
206 };
207 
208 static ddi_device_acc_attr_t hw_acc_attr = {
209 	DDI_DEVICE_ATTR_V0,
210 	DDI_NEVERSWAP_ACC,
211 	DDI_STRICTORDER_ACC
212 };
213 
214 
215 void *
216 drm_pci_alloc(drm_device_t *dev, size_t size,
217     size_t align,  dma_addr_t maxaddr, int segments)
218 {
219 	drm_dma_handle_t	*dmah;
220 	uint_t	count;
221 	int ret = DDI_FAILURE;
222 
223 	/* allocat continous physical memory for hw status page */
224 	if (align == 0)
225 		hw_dma_attr.dma_attr_align =  1;
226 	else
227 		hw_dma_attr.dma_attr_align = align;
228 
229 	hw_dma_attr.dma_attr_addr_hi = maxaddr;
230 	hw_dma_attr.dma_attr_sgllen = segments;
231 
232 	dmah = kmem_zalloc(sizeof (drm_dma_handle_t), KM_SLEEP);
233 	if (ret = ddi_dma_alloc_handle(dev->dip, &hw_dma_attr,
234 	    DDI_DMA_SLEEP, NULL, &dmah->dma_hdl)) {
235 		DRM_ERROR("drm_pci_alloc:ddi_dma_alloc_handle failed\n");
236 		goto err3;
237 	}
238 
239 	if (ret = ddi_dma_mem_alloc(dmah->dma_hdl, size, &hw_acc_attr,
240 	    DDI_DMA_CONSISTENT | IOMEM_DATA_UNCACHED,
241 	    DDI_DMA_SLEEP, NULL, (caddr_t *)&dmah->vaddr,
242 	    &dmah->real_sz, &dmah->acc_hdl)) {
243 		DRM_ERROR("drm_pci_alloc: ddi_dma_mem_alloc failed\n");
244 		goto err2;
245 	}
246 
247 	ret = ddi_dma_addr_bind_handle(dmah->dma_hdl, NULL,
248 	    (caddr_t)dmah->vaddr, dmah->real_sz,
249 	    DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
250 	    DDI_DMA_SLEEP, NULL, &dmah->cookie,  &count);
251 	if (ret != DDI_DMA_MAPPED) {
252 		DRM_ERROR("drm_pci_alloc: alloc phys memory failed");
253 		goto err1;
254 	}
255 
256 	if (count > segments) {
257 		(void) ddi_dma_unbind_handle(dmah->dma_hdl);
258 		goto err1;
259 	}
260 
261 	dmah->cookie_num = count;
262 	if (count == 1)
263 		dmah->paddr = dmah->cookie.dmac_address;
264 
265 	return (dmah);
266 
267 err1:
268 	ddi_dma_mem_free(&dmah->acc_hdl);
269 err2:
270 	ddi_dma_free_handle(&dmah->dma_hdl);
271 err3:
272 	kmem_free(dmah, sizeof (*dmah));
273 	return (NULL);
274 }
275 
276 /*
277  * pci_free_consistent()
278  */
279 /*ARGSUSED*/
280 void
281 drm_pci_free(drm_device_t *dev, drm_dma_handle_t *dmah)
282 {
283 	ASSERT(dmah != NULL);
284 	(void) ddi_dma_unbind_handle(dmah->dma_hdl);
285 	ddi_dma_mem_free(&dmah->acc_hdl);
286 	ddi_dma_free_handle(&dmah->dma_hdl);
287 	kmem_free(dmah, sizeof (drm_dma_handle_t));
288 }
289 
290 int
291 do_get_pci_res(drm_device_t *dev, drm_pci_resource_t *resp)
292 {
293 	int length;
294 	pci_regspec_t	*regs;
295 
296 	if (ddi_getlongprop(
297 	    DDI_DEV_T_ANY, dev->dip, DDI_PROP_DONTPASS,
298 	    "assigned-addresses", (caddr_t)&regs, &length) !=
299 	    DDI_PROP_SUCCESS) {
300 		DRM_ERROR("do_get_pci_res: ddi_getlongprop failed!\n");
301 		return (EFAULT);
302 	}
303 	resp->offset =
304 	    (unsigned long)regs[resp->regnum].pci_phys_low;
305 	resp->size =
306 	    (unsigned long)regs[resp->regnum].pci_size_low;
307 	kmem_free(regs, (size_t)length);
308 
309 	return (0);
310 }
311 
312 /*ARGSUSED*/
313 unsigned long
314 drm_get_resource_start(drm_device_t *softstate, unsigned int regnum)
315 {
316 	drm_pci_resource_t res;
317 	int ret;
318 
319 	res.regnum = regnum;
320 
321 	ret = do_get_pci_res(softstate, &res);
322 
323 	if (ret != 0) {
324 		DRM_ERROR("drm_get_resource_start: ioctl failed");
325 		return (0);
326 	}
327 
328 	return (res.offset);
329 
330 }
331 
332 /*ARGSUSED*/
333 unsigned long
334 drm_get_resource_len(drm_device_t *softstate, unsigned int regnum)
335 {
336 	drm_pci_resource_t res;
337 	int ret;
338 
339 	res.regnum = regnum;
340 
341 	ret = do_get_pci_res(softstate, &res);
342 
343 	if (ret != 0) {
344 		DRM_ERROR("drm_get_resource_len: ioctl failed");
345 		return (0);
346 	}
347 
348 	return (res.size);
349 }
350