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