xref: /titanic_50/usr/src/uts/common/io/drm/drm_pci.c (revision a237e38e9161f0acd6451439d4a7dd597e66291d)
1 /*
2  * Copyright 2006 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 
46 #define	PCI_DEVICE(x)	(((x)>>11) & 0x1f)
47 #define	PCI_FUNCTION(x)	(((x) & 0x700) >> 8)
48 #define	PCI_BUS(x)	(((x) & 0xff0000) >> 16)
49 
50 /* Device info struct */
51 typedef struct drm_device_info {
52 	uint16_t	drm_venid;	/* drm devcie's vendor id */
53 	uint16_t	drm_devid;	/* drm device's device id */
54 	uint8_t		drm_irq;	/* drm device's interrupt line */
55 } drm_device_info_t;
56 
57 typedef struct drm_pci_resource {
58 	uint_t	regnum;
59 	unsigned long offset;
60 	unsigned long size;
61 } drm_pci_resource_t;
62 
63 /* Get IRQ line */
64 static int device_get_info(drm_device_info_t *data, ddi_acc_handle_t pch)
65 {
66 	uint8_t irq;
67 
68 	if (!pch)
69 		return (DRM_ERR(EINVAL));
70 
71 	data->drm_venid =
72 	    pci_config_get16(pch, PCI_CONF_VENID);
73 	data->drm_devid =
74 	    pci_config_get16(pch, PCI_CONF_DEVID);
75 	irq = pci_config_get8(pch, PCI_CONF_IPIN);
76 	DRM_DEBUG("!drm: device_get_info: \
77 	    venid is %x, devid is %x,irq is %x \n",
78 	    data->drm_venid, data->drm_devid, irq);
79 
80 	if (irq)
81 		irq = pci_config_get8(pch, PCI_CONF_ILINE);
82 	data->drm_irq = irq;
83 
84 	return (0);
85 }
86 
87 int
88 pci_get_info(drm_softstate_t *softstate, int *bus, int *slot, int *func)
89 {
90 	int *regs_list;
91 	uint_t nregs = 0;
92 
93 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, softstate->dip,
94 	    DDI_PROP_DONTPASS, "reg", (int **)&regs_list, &nregs)
95 	    != DDI_PROP_SUCCESS) {
96 		DRM_ERROR("pci_get_info: get pci function bus device failed");
97 		goto error;
98 	}
99 	*bus  = (int)PCI_BUS(regs_list[0]);
100 	*slot = (int)PCI_DEVICE(regs_list[0]);
101 	*func = (int)PCI_FUNCTION(regs_list[0]);
102 
103 	if (nregs > 0) {
104 		ddi_prop_free(regs_list);
105 	}
106 	return (DDI_SUCCESS);
107 error:
108 	if (nregs > 0) {
109 		ddi_prop_free(regs_list);
110 	}
111 	return (DDI_FAILURE);
112 }
113 
114 int
115 pci_get_irq(drm_softstate_t *softstate)
116 {
117 	drm_device_info_t drm_info;
118 	int ret;
119 
120 	bzero(&drm_info, sizeof (drm_device_info_t));
121 	ret = device_get_info(&drm_info, softstate->pci_cfg_hdl);
122 	if (ret)
123 		return (-1);
124 
125 	return (drm_info.drm_irq);
126 }
127 
128 int
129 pci_get_vendor(drm_softstate_t *softstate)
130 {
131 	drm_device_info_t drm_info;
132 	int ret;
133 
134 	bzero(&drm_info, sizeof (drm_device_info_t));
135 
136 	ret = device_get_info(&drm_info, softstate->pci_cfg_hdl);
137 	if (ret)
138 		return (DDI_FAILURE);
139 
140 	return (drm_info.drm_venid);
141 }
142 
143 int
144 pci_get_device(drm_softstate_t *softstate)
145 {
146 	drm_device_info_t drm_info;
147 	int ret;
148 
149 	bzero(&drm_info, sizeof (drm_device_info_t));
150 
151 	ret = device_get_info(&drm_info, softstate->pci_cfg_hdl);
152 	if (ret)
153 		return (DDI_FAILURE);
154 
155 	return (drm_info.drm_devid);
156 
157 }
158 
159 void
160 agp_remap(struct drm_softstate *softstate, struct drm_local_map *map)
161 {
162 	DRM_DEBUG(
163 	    "agp_remap: map->handle:%lx map->offset %llx, map->size %lx\n",
164 	    (unsigned long)map->handle, map->offset.off,
165 	    (unsigned long)map->size);
166 
167 	map->handle = (void *)((softstate->agp_umem_kvaddr) +
168 	    (unsigned long)(map->offset.off - softstate->agp->base));
169 	map->dev_addr = (caddr_t)map->handle;
170 	DRM_DEBUG("agp_remap: map->dev_addr is %lx",
171 	    (unsigned long) map->dev_addr);
172 
173 }
174 
175 /*ARGSUSED*/
176 void
177 agp_remap_free(struct drm_softstate *softstate, struct drm_local_map *map)
178 {}
179 
180 void
181 drm_core_ioremap(struct drm_local_map *map, struct drm_softstate *softstate)
182 {
183 	if (map->type != _DRM_AGP_UMEM) {
184 		(void) drm_ioremap(softstate, map);
185 	} else {
186 		(void) agp_remap(softstate, map);
187 	}
188 }
189 
190 void
191 drm_core_ioremapfree(struct drm_local_map *map, struct drm_softstate *softstate)
192 {
193 	if (map->type != _DRM_AGP_UMEM) {
194 		if (map->handle && map->size)
195 			drm_ioremapfree(map);
196 	} else {
197 		(void) agp_remap_free(softstate, map);
198 	}
199 }
200 
201 struct drm_local_map *
202 drm_core_findmap(struct drm_softstate *dev, unsigned long offset)
203 {
204 	drm_local_map_t *map;
205 
206 	DRM_SPINLOCK_ASSERT(&dev->dev_lock);
207 	TAILQ_FOREACH(map, &dev->maplist, link) {
208 		if ((unsigned long)map->offset.off == offset)
209 			return (map);
210 	}
211 	return (NULL);
212 }
213 
214 /*
215  * pci_alloc_consistent()
216  */
217 
218 static ddi_dma_attr_t hw_dma_attr = {
219 	DMA_ATTR_V0,
220 	(unsigned long long)0,
221 	(unsigned long long)0xffffffff,
222 	(unsigned long long)0xffffffff,
223 	(unsigned long long)4096,
224 	1,
225 	1,
226 	(unsigned long long)0xffffffff,
227 	(unsigned long long)0xffffffff,
228 	1,
229 	4,
230 	0
231 };
232 
233 static ddi_device_acc_attr_t hw_acc_attr = {
234 	DDI_DEVICE_ATTR_V0,
235 	DDI_NEVERSWAP_ACC,
236 	DDI_STRICTORDER_ACC
237 };
238 
239 void *
240 drm_pci_alloc(drm_softstate_t *dev, uint32_t size,
241 		    dma_addr_t *physaddr)
242 {
243 	int ret = DDI_FAILURE;
244 	uint32_t num_cookies;
245 	ddi_dma_cookie_t cookie;
246 
247 	/* allocat continous physical memory for hw status page */
248 	hw_dma_attr.dma_attr_sgllen = 1;
249 
250 	if (ret = ddi_dma_alloc_handle(dev->dip,
251 	    &hw_dma_attr,
252 	    DDI_DMA_SLEEP,
253 	    NULL, &dev->hw_dma_handle)) {
254 		DRM_ERROR("drm_pci_alloc:ddi_dma_alloc_handle failed\n");
255 		goto err3;
256 	}
257 
258 	if (ret = ddi_dma_mem_alloc(dev->hw_dma_handle,
259 				size,
260 				&hw_acc_attr,
261 				DDI_DMA_CONSISTENT,
262 				DDI_DMA_SLEEP, NULL,
263 				&dev->hw_vbase,
264 				&dev->hw_size,
265 				&dev->hw_dma_acc_handle)) {
266 		DRM_ERROR("drm_pci_alloc: ddi_dma_mem_alloc failed\n");
267 		goto err2;
268 	}
269 
270 	ret = ddi_dma_addr_bind_handle(dev->hw_dma_handle,
271 			NULL, dev->hw_vbase,
272 			size,
273 			DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
274 			DDI_DMA_SLEEP, NULL,
275 			&cookie,  &num_cookies);
276 	dev->hw_pbase = cookie.dmac_address;
277 
278 	if ((ret != DDI_DMA_MAPPED) || (num_cookies != 1)) {
279 		if (num_cookies > 1)
280 			(void) ddi_dma_unbind_handle(dev->hw_dma_handle);
281 		DRM_ERROR("drm_pci_alloc: alloc contiguous phys memory failed");
282 		goto err1;
283 	}
284 	*physaddr = dev->hw_pbase;
285 	return (dev->hw_vbase);
286 
287 err1:
288 	ddi_dma_mem_free(&dev->hw_dma_acc_handle);
289 	dev->hw_dma_acc_handle = NULL;
290 err2:
291 	ddi_dma_free_handle(&dev->hw_dma_handle);
292 	dev->hw_dma_handle = NULL;
293 err3:
294 	dev->hw_pbase = 0;
295 	dev->hw_vbase = 0;
296 	dev->hw_size = 0;
297 	*physaddr = NULL;
298 	return (NULL);
299 }
300 
301 /*
302  * pci_free_consistent()
303  */
304 /*ARGSUSED*/
305 void
306 drm_pci_free(drm_softstate_t *dev)
307 {
308 	if (dev->hw_dma_handle == NULL)
309 		return;
310 	(void) ddi_dma_unbind_handle(dev->hw_dma_handle);
311 	ddi_dma_mem_free(&dev->hw_dma_acc_handle);
312 	dev->hw_dma_acc_handle = NULL;
313 	ddi_dma_free_handle(&dev->hw_dma_handle);
314 	dev->hw_dma_handle = NULL;
315 	dev->hw_pbase = NULL;
316 	dev->hw_vbase = NULL;
317 	dev->hw_size = 0;
318 
319 
320 }
321 
322 int
323 do_get_pci_res(drm_softstate_t *softstate, drm_pci_resource_t *resp)
324 {
325 	int length;
326 	pci_regspec_t	*regs;
327 
328 	if (ddi_getlongprop(
329 	    DDI_DEV_T_ANY, softstate->dip, DDI_PROP_DONTPASS,
330 	    "assigned-addresses", (caddr_t)&regs, &length) !=
331 	    DDI_PROP_SUCCESS) {
332 		DRM_ERROR("do_get_pci_res: ddi_getlongprop failed!\n");
333 		return (DRM_ERR(EFAULT));
334 	}
335 	resp->offset =
336 	    (unsigned long)regs[resp->regnum].pci_phys_low;
337 	resp->size =
338 	    (unsigned long)regs[resp->regnum].pci_size_low;
339 	kmem_free(regs, (size_t)length);
340 
341 	return (DDI_SUCCESS);
342 }
343 
344 /*ARGSUSED*/
345 unsigned long
346 drm_get_resource_start(drm_softstate_t *softstate, unsigned int regnum)
347 {
348 	drm_pci_resource_t res;
349 	int ret;
350 
351 	res.regnum = regnum;
352 
353 	ret = do_get_pci_res(softstate, &res);
354 
355 	if (ret != DDI_SUCCESS) {
356 		DRM_ERROR(
357 			"drm_get_resource_start: "
358 			"DRM_GET_PCI_RESOURCE ioctl failed");
359 		return (0);
360 	}
361 
362 	return (res.offset);
363 
364 }
365 
366 /*ARGSUSED*/
367 unsigned long
368 drm_get_resource_len(drm_softstate_t *softstate, unsigned int regnum)
369 {
370 	drm_pci_resource_t res;
371 	int ret;
372 
373 	res.regnum = regnum;
374 
375 	ret = do_get_pci_res(softstate, &res);
376 
377 	if (ret != DDI_SUCCESS) {
378 		DRM_ERROR(
379 			"drm_get_resource_len: "
380 			"DRM_GET_PCI_RESOURCE ioctl failed");
381 		return (0);
382 	}
383 
384 	return (res.size);
385 }
386