xref: /titanic_44/usr/src/uts/intel/io/agpmaster/agpmaster.c (revision 24db46411fd54f70c35b94bb952eb7ba040e43b4)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Misc module for AGP master device support
31  */
32 
33 #include <sys/modctl.h>
34 #include <sys/pci.h>
35 #include <sys/stat.h>
36 #include <sys/file.h>
37 #include <sys/types.h>
38 #include <sys/dditypes.h>
39 #include <sys/sunddi.h>
40 #include <sys/agpgart.h>
41 #include <sys/agp/agpdefs.h>
42 #include <sys/agp/agpmaster_io.h>
43 
44 #define	I8XX_MMIO_REGSET	2
45 #define	I8XX_FB_REGSET		1
46 #define	I8XX_PTE_OFFSET		0x10000
47 #define	I8XX_PGTBL_CTL		0x2020
48 #define	I915_GTTADDR_BAR	4
49 #define	I915_FB_REGSET		3
50 
51 #ifdef DEBUG
52 #define	CONFIRM(value) ASSERT(value)
53 #else
54 #define	CONFIRM(value) if (!(value)) return (EINVAL)
55 #endif
56 
57 int agpm_debug = 0;
58 #define	AGPM_DEBUG(args)	if (agpm_debug >= 1) cmn_err args
59 
60 /*
61  * Whether it is a Intel integrated graphics card
62  */
63 #define	IS_IGD(agpmaster) ((agpmaster->agpm_dev_type == DEVICE_IS_I810) || \
64 		    (agpmaster->agpm_dev_type == DEVICE_IS_I830))
65 
66 
67 #define	IS_INTEL_9XX(agpmaster) ((agpmaster->agpm_id == INTEL_IGD_910) || \
68 		    (agpmaster->agpm_id == INTEL_IGD_910M) || \
69 		    (agpmaster->agpm_id == INTEL_IGD_945))
70 
71 static struct modlmisc modlmisc = {
72 	&mod_miscops, "AGP master interfaces v%I%"
73 };
74 
75 static struct modlinkage modlinkage = {
76 	MODREV_1, (void *)&modlmisc, NULL
77 };
78 
79 static ddi_device_acc_attr_t i8xx_dev_access = {
80 	DDI_DEVICE_ATTR_V0,
81 	DDI_NEVERSWAP_ACC,
82 	DDI_STRICTORDER_ACC
83 };
84 
85 static off_t agpmaster_cap_find(ddi_acc_handle_t);
86 static int detect_i8xx_device(agp_master_softc_t *);
87 static int detect_agp_devcice(agp_master_softc_t *, ddi_acc_handle_t);
88 static int i8xx_add_to_gtt(gtt_impl_t *, igd_gtt_seg_t);
89 static void i8xx_remove_from_gtt(gtt_impl_t *, igd_gtt_seg_t);
90 
91 int
92 _init(void)
93 {
94 	int	err;
95 
96 	if ((err = mod_install(&modlinkage)) != 0)
97 		return (err);
98 
99 	return (0);
100 }
101 
102 int
103 _fini(void)
104 {
105 	int	err;
106 
107 	if ((err = mod_remove(&modlinkage)) != 0)
108 		return (err);
109 
110 	return (0);
111 }
112 
113 int
114 _info(struct modinfo *modinfop)
115 {
116 	return (mod_info(&modlinkage, modinfop));
117 }
118 
119 /*
120  * Minor node is not removed here, since the caller (xx_attach) is
121  * responsible for removing all nodes.
122  */
123 void
124 agpmaster_detach(agp_master_softc_t **master_softcp)
125 {
126 	agp_master_softc_t *master_softc;
127 
128 	ASSERT(master_softcp);
129 	master_softc = *master_softcp;
130 
131 	/* intel integrated device */
132 	if (IS_IGD(master_softc)) {
133 		if (master_softc->agpm_data.agpm_gtt.gtt_mmio_handle != NULL) {
134 			ddi_regs_map_free(
135 			    &master_softc->agpm_data.agpm_gtt.gtt_mmio_handle);
136 		}
137 	}
138 
139 	kmem_free(master_softc, sizeof (agp_master_softc_t));
140 	master_softc = NULL;
141 
142 	return;
143 
144 }
145 
146 /*
147  * Try to initialize agp master.
148  * 0 is returned if the device is successfully initialized. AGP master soft
149  * state is returned in master_softcp if needed.
150  * Otherwise -1 is returned and *master_softcp is set to NULL.
151  */
152 int
153 agpmaster_attach(dev_info_t *devi, agp_master_softc_t **master_softcp,
154     ddi_acc_handle_t pci_acc_hdl, minor_t minor)
155 {
156 	int instance;
157 	int status;
158 	agp_master_softc_t *agpmaster;
159 	uint32_t value;
160 	off_t reg_size;
161 	char buf[80];
162 
163 
164 	ASSERT(pci_acc_hdl);
165 	*master_softcp = NULL;
166 	agpmaster = (agp_master_softc_t *)
167 	    kmem_zalloc(sizeof (agp_master_softc_t), KM_SLEEP);
168 
169 	agpmaster->agpm_id =
170 	    pci_config_get32(pci_acc_hdl, PCI_CONF_VENID);
171 	agpmaster->agpm_acc_hdl = pci_acc_hdl;
172 
173 	if (!detect_i8xx_device(agpmaster)) {
174 		/* map mmio register set */
175 		if (IS_INTEL_9XX(agpmaster)) {
176 			status = ddi_regs_map_setup(devi, I915_GTTADDR_BAR,
177 			    &agpmaster->agpm_data.agpm_gtt.gtt_mmio_base,
178 			    0, 0, &i8xx_dev_access,
179 			    &agpmaster->agpm_data.agpm_gtt.gtt_mmio_handle);
180 		} else {
181 			status = ddi_regs_map_setup(devi, I8XX_MMIO_REGSET,
182 			    &agpmaster->agpm_data.agpm_gtt.gtt_mmio_base,
183 			    0, 0, &i8xx_dev_access,
184 			    &agpmaster->agpm_data.agpm_gtt.gtt_mmio_handle);
185 		}
186 
187 		if (status != DDI_SUCCESS) {
188 			AGPM_DEBUG((CE_WARN,
189 			    "agpmaster_attach: ddi_regs_map_setup failed"));
190 			goto fail;
191 		}
192 		/* get GTT range base offset */
193 		if (IS_INTEL_9XX(agpmaster)) {
194 			agpmaster->agpm_data.agpm_gtt.gtt_addr =
195 			    agpmaster->agpm_data.agpm_gtt.gtt_mmio_base;
196 		} else
197 			agpmaster->agpm_data.agpm_gtt.gtt_addr =
198 			agpmaster->agpm_data.agpm_gtt.gtt_mmio_base +
199 			I8XX_PTE_OFFSET;
200 
201 		/* get graphics memory size */
202 		if (IS_INTEL_9XX(agpmaster)) {
203 			status = ddi_dev_regsize(devi, I915_FB_REGSET,
204 			    &reg_size);
205 		} else
206 			status = ddi_dev_regsize(devi, I8XX_FB_REGSET,
207 			    &reg_size);
208 		/*
209 		 * if memory size is smaller than a certain value, it means
210 		 * the register set number for graphics memory range might
211 		 * be wrong
212 		 */
213 		if (status != DDI_SUCCESS || reg_size < 0x400000) {
214 			AGPM_DEBUG((CE_WARN,
215 			    "agpmaster_attach: ddi_dev_regsize error"));
216 			goto fail;
217 		}
218 
219 		agpmaster->agpm_data.agpm_gtt.gtt_info.igd_apersize =
220 		    BYTES2MB(reg_size);
221 		if (IS_INTEL_9XX(agpmaster)) {
222 			value = pci_config_get32(pci_acc_hdl,
223 			    I915_CONF_GMADR);
224 		} else
225 			value = pci_config_get32(pci_acc_hdl,
226 			    I8XX_CONF_GMADR);
227 
228 		agpmaster->agpm_data.agpm_gtt.gtt_info.igd_aperbase =
229 		    value & GTT_BASE_MASK;
230 		agpmaster->agpm_data.agpm_gtt.gtt_info.igd_devid =
231 		    agpmaster->agpm_id;
232 	} else if (detect_agp_devcice(agpmaster, pci_acc_hdl)) {
233 		/*
234 		 * non IGD or AGP devices, AMD64 gart
235 		 */
236 		AGPM_DEBUG((CE_WARN,
237 		    "agpmaster_attach: neither IGD or AGP devices exists"));
238 		agpmaster_detach(&agpmaster);
239 		return (0);
240 	}
241 
242 	/* create minor node for IGD or AGP device */
243 	instance = ddi_get_instance(devi);
244 
245 	(void) sprintf(buf, "%s%d", AGPMASTER_NAME, instance);
246 	status = ddi_create_minor_node(devi, buf, S_IFCHR, minor,
247 	    DDI_NT_AGP_MASTER, 0);
248 
249 	if (status != DDI_SUCCESS) {
250 		AGPM_DEBUG((CE_WARN,
251 		    "agpmaster_attach: create agpmaster node failed"));
252 		goto fail;
253 	}
254 
255 	*master_softcp = agpmaster;
256 	return (0);
257 fail:
258 	agpmaster_detach(&agpmaster);
259 	return (-1);
260 }
261 
262 /*
263  * Currently, it handles ioctl requests related with agp master device for
264  * layered driver (agpgart) only.
265  */
266 /*ARGSUSED*/
267 int
268 agpmaster_ioctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *cred,
269     int *rval, agp_master_softc_t *softc)
270 {
271 	uint32_t base;
272 	uint32_t addr;
273 	igd_gtt_seg_t seg;
274 	agp_info_t info;
275 	uint32_t value;
276 	off_t cap;
277 	uint32_t command;
278 	static char kernel_only[] =
279 	    "agpmaster_ioctl: %s is a kernel only ioctl";
280 
281 	CONFIRM(softc);
282 
283 	switch (cmd) {
284 	case DEVICE_DETECT:
285 		if (!(mode & FKIOCTL)) {
286 			AGPM_DEBUG((CE_CONT, kernel_only, "DEVICE_DETECT"));
287 			return (ENXIO);
288 		}
289 
290 		if (ddi_copyout(&softc->agpm_dev_type,
291 		    (void *)data, sizeof (int), mode))
292 			return (EFAULT);
293 		break;
294 	case AGP_MASTER_SETCMD:
295 		if (!(mode & FKIOCTL)) {
296 			AGPM_DEBUG((CE_CONT, kernel_only, "AGP_MASTER_SETCMD"));
297 			return (ENXIO);
298 		}
299 
300 		CONFIRM(softc->agpm_dev_type == DEVICE_IS_AGP);
301 		CONFIRM(softc->agpm_data.agpm_acaptr);
302 
303 		if (ddi_copyin((void *)data, &command,
304 		    sizeof (uint32_t), mode))
305 			return (EFAULT);
306 
307 		pci_config_put32(softc->agpm_acc_hdl,
308 		    softc->agpm_data.agpm_acaptr + AGP_CONF_COMMAND,
309 		    command);
310 		break;
311 	case AGP_MASTER_GETINFO:
312 		if (!(mode & FKIOCTL)) {
313 			AGPM_DEBUG((CE_CONT, kernel_only,
314 			    "AGP_MASTER_GETINFO"));
315 			return (ENXIO);
316 		}
317 
318 		CONFIRM(softc->agpm_dev_type == DEVICE_IS_AGP);
319 		CONFIRM(softc->agpm_data.agpm_acaptr);
320 
321 		cap = softc->agpm_data.agpm_acaptr;
322 		value = pci_config_get32(softc->agpm_acc_hdl, cap);
323 		info.agpi_version.agpv_major = (uint16_t)((value >> 20) & 0xf);
324 		info.agpi_version.agpv_minor = (uint16_t)((value >> 16) & 0xf);
325 		info.agpi_devid = softc->agpm_id;
326 		info.agpi_mode = pci_config_get32(
327 		    softc->agpm_acc_hdl, cap + AGP_CONF_STATUS);
328 
329 		if (ddi_copyout(&info, (void *)data,
330 		    sizeof (agp_info_t), mode))
331 			return (EFAULT);
332 		break;
333 	case I810_SET_GTT_BASE:
334 		if (!(mode & FKIOCTL)) {
335 			AGPM_DEBUG((CE_CONT, kernel_only, "I810_SET_GTT_ADDR"));
336 			return (ENXIO);
337 		}
338 
339 		CONFIRM(softc->agpm_dev_type == DEVICE_IS_I810);
340 
341 		if (ddi_copyin((void *)data, &base, sizeof (uint32_t), mode))
342 			return (EFAULT);
343 
344 		/* enables page table */
345 		addr = (base & GTT_BASE_MASK) | GTT_TABLE_VALID;
346 
347 		ddi_put32(softc->agpm_data.agpm_gtt.gtt_mmio_handle,
348 		    (uint32_t *)(softc->agpm_data.agpm_gtt.gtt_mmio_base +
349 			I8XX_PGTBL_CTL),
350 		    addr);
351 		break;
352 	case I8XX_GET_INFO:
353 		if (!(mode & FKIOCTL)) {
354 			AGPM_DEBUG((CE_CONT, kernel_only, "I8XX_GET_INFO"));
355 			return (ENXIO);
356 		}
357 
358 		CONFIRM(IS_IGD(softc));
359 
360 		if (ddi_copyout(&softc->agpm_data.agpm_gtt.gtt_info,
361 		    (void *)data, sizeof (igd_info_t), mode))
362 			return (EFAULT);
363 		break;
364 	case I8XX_ADD2GTT:
365 		if (!(mode & FKIOCTL)) {
366 			AGPM_DEBUG((CE_CONT, kernel_only, "I8XX_ADD2GTT"));
367 			return (ENXIO);
368 		}
369 
370 		CONFIRM(IS_IGD(softc));
371 
372 		if (ddi_copyin((void *)data, &seg,
373 		    sizeof (igd_gtt_seg_t), mode))
374 			return (EFAULT);
375 
376 		if (i8xx_add_to_gtt(&softc->agpm_data.agpm_gtt, seg))
377 			return (EINVAL);
378 		break;
379 	case I8XX_REM_GTT:
380 		if (!(mode & FKIOCTL)) {
381 			AGPM_DEBUG((CE_CONT, kernel_only, "I8XX_REM_GTT"));
382 			return (ENXIO);
383 		}
384 
385 		CONFIRM(IS_IGD(softc));
386 
387 		if (ddi_copyin((void *)data, &seg,
388 		    sizeof (igd_gtt_seg_t), mode))
389 			return (EFAULT);
390 
391 		i8xx_remove_from_gtt(&softc->agpm_data.agpm_gtt, seg);
392 		break;
393 	case I8XX_UNCONFIG:
394 		if (!(mode & FKIOCTL)) {
395 			AGPM_DEBUG((CE_CONT, kernel_only, "I8XX_UNCONFIG"));
396 			return (ENXIO);
397 		}
398 
399 		CONFIRM(IS_IGD(softc));
400 
401 		if (softc->agpm_dev_type == DEVICE_IS_I810)
402 			ddi_put32(softc->agpm_data.agpm_gtt.gtt_mmio_handle,
403 			    (uint32_t *)
404 			    (softc->agpm_data.agpm_gtt.gtt_mmio_base +
405 				I8XX_PGTBL_CTL), 0);
406 		/*
407 		 * may need to clear all gtt entries here for i830 series,
408 		 * but may not be necessary
409 		 */
410 		break;
411 	}
412 	return (0);
413 }
414 
415 /*
416  * If AGP cap pointer is successfully found, none-zero value is returned.
417  * Otherwise 0 is returned.
418  */
419 static off_t
420 agpmaster_cap_find(ddi_acc_handle_t acc_handle)
421 {
422 	off_t		nextcap;
423 	uint32_t	ncapid;
424 	uint8_t		value;
425 
426 	/* check if this device supports capibility pointer */
427 	value = (uint8_t)(pci_config_get16(acc_handle, PCI_CONF_STAT)
428 			    & PCI_CONF_CAP_MASK);
429 
430 	if (!value)
431 		return (0);
432 	/* get the offset of the first capability pointer from CAPPTR */
433 	nextcap = (off_t)(pci_config_get8(acc_handle, AGP_CONF_CAPPTR));
434 
435 	/* check AGP capability from the first capability pointer */
436 	while (nextcap) {
437 		ncapid = pci_config_get32(acc_handle, nextcap);
438 		if ((ncapid & PCI_CONF_CAPID_MASK)
439 		    == AGP_CAP_ID) /* find AGP cap */
440 			break;
441 
442 		nextcap = (off_t)((ncapid & PCI_CONF_NCAPID_MASK) >> 8);
443 	}
444 
445 	return (nextcap);
446 
447 }
448 
449 /*
450  * If i8xx device is successfully detected, 0 is returned.
451  * Otherwise -1 is returned.
452  */
453 static int
454 detect_i8xx_device(agp_master_softc_t *master_softc)
455 {
456 
457 	switch (master_softc->agpm_id) {
458 	case INTEL_IGD_810:
459 	case INTEL_IGD_810DC:
460 	case INTEL_IGD_810E:
461 	case INTEL_IGD_815:
462 		master_softc->agpm_dev_type = DEVICE_IS_I810;
463 		break;
464 	case INTEL_IGD_830M:
465 	case INTEL_IGD_845G:
466 	case INTEL_IGD_855GM:
467 	case INTEL_IGD_865G:
468 	case INTEL_IGD_910:
469 	case INTEL_IGD_910M:
470 	case INTEL_IGD_945:
471 		master_softc->agpm_dev_type = DEVICE_IS_I830;
472 		break;
473 	default:		/* unknown id */
474 		return (-1);
475 	}
476 
477 	return (0);
478 }
479 
480 /*
481  * If agp master is succssfully detected, 0 is returned.
482  * Otherwise -1 is returned.
483  */
484 static int
485 detect_agp_devcice(agp_master_softc_t *master_softc,
486     ddi_acc_handle_t acc_handle)
487 {
488 	off_t cap;
489 
490 	cap = agpmaster_cap_find(acc_handle);
491 	if (cap) {
492 		master_softc->agpm_dev_type = DEVICE_IS_AGP;
493 		master_softc->agpm_data.agpm_acaptr = cap;
494 		return (0);
495 	} else {
496 		return (-1);
497 	}
498 
499 }
500 
501 /*
502  * Please refer to GART and GTT entry format table in agpdefs.h for
503  * intel GTT entry format.
504  */
505 static int
506 phys2entry(uint32_t type, uint32_t physaddr, uint32_t *entry)
507 {
508 	uint32_t value;
509 
510 	switch (type) {
511 	case AGP_PHYSICAL:
512 	case AGP_NORMAL:
513 		value = (physaddr & GTT_PTE_MASK) | GTT_PTE_VALID;
514 		break;
515 	default:
516 		return (-1);
517 	}
518 
519 	*entry = value;
520 
521 	return (0);
522 }
523 
524 static int
525 i8xx_add_to_gtt(gtt_impl_t *gtt, igd_gtt_seg_t seg)
526 {
527 	int i;
528 	uint32_t *paddr;
529 	uint32_t entry;
530 	uint32_t maxpages;
531 
532 	maxpages = gtt->gtt_info.igd_apersize;
533 	maxpages = GTT_MB_TO_PAGES(maxpages);
534 
535 	paddr = seg.igs_phyaddr;
536 
537 	/* check if gtt max page number is reached */
538 	if ((seg.igs_pgstart + seg.igs_npage) > maxpages)
539 		return (-1);
540 
541 	paddr = seg.igs_phyaddr;
542 	for (i = seg.igs_pgstart; i < (seg.igs_pgstart + seg.igs_npage);
543 	    i++, paddr++) {
544 		if (phys2entry(seg.igs_type, *paddr, &entry))
545 			return (-1);
546 		ddi_put32(gtt->gtt_mmio_handle,
547 		    (uint32_t *)(gtt->gtt_addr + i * sizeof (uint32_t)),
548 		    entry);
549 	}
550 
551 	return (0);
552 }
553 
554 static void
555 i8xx_remove_from_gtt(gtt_impl_t *gtt, igd_gtt_seg_t seg)
556 {
557 	int i;
558 	uint32_t maxpages;
559 
560 	maxpages = gtt->gtt_info.igd_apersize;
561 	maxpages = GTT_MB_TO_PAGES(maxpages);
562 
563 	/* check if gtt max page number is reached */
564 	if ((seg.igs_pgstart + seg.igs_npage) > maxpages)
565 		return;
566 
567 	for (i = seg.igs_pgstart; i < (seg.igs_pgstart + seg.igs_npage); i++) {
568 		ddi_put32(gtt->gtt_mmio_handle,
569 		    (uint32_t *)(gtt->gtt_addr +
570 		    i * sizeof (uint32_t)),
571 		    0);
572 	}
573 }
574