xref: /titanic_44/usr/src/uts/intel/io/agpgart/agptarget.c (revision 0a48a24e663a04e34e2ed4e55390ad96f178dbea)
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 2008 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 #include <sys/systm.h>
30 #include <sys/conf.h>
31 #include <sys/modctl.h>
32 #include <sys/file.h>
33 #include <sys/stat.h>
34 #include <sys/ddi.h>
35 #include <sys/sunddi.h>
36 #include <sys/modctl.h>
37 #include <sys/sunldi.h>
38 #include <sys/pci.h>
39 #include <sys/agpgart.h>
40 #include <sys/agp/agpdefs.h>
41 #include <sys/agp/agptarget_io.h>
42 
43 int agptarget_debug_var = 0;
44 #define	TARGETDB_PRINT2(fmt)	if (agptarget_debug_var >= 1) cmn_err fmt
45 #define	INST2NODENUM(inst)	(inst)
46 #define	DEV2INST(dev)		(getminor(dev))
47 
48 typedef struct agp_target_softstate {
49 	dev_info_t		*tsoft_dip;
50 	ddi_acc_handle_t	tsoft_pcihdl;
51 	uint32_t		tsoft_devid;
52 	/* The offset of the ACAPID register */
53 	off_t			tsoft_acaptr;
54 	kmutex_t		tsoft_lock;
55 	int			tsoft_gms_off; /* GMS offset in config */
56 	uint32_t		tsoft_gms;
57 }agp_target_softstate_t;
58 
59 /*
60  * To get the pre-allocated graphics mem size using Graphics Mode Select
61  * (GMS) value.
62  */
63 typedef struct gms_mode {
64 	uint32_t	gm_devid;	/* bridge vendor + device id */
65 	off_t		gm_regoff;	/* mode selection register offset */
66 	uint32_t	gm_mask;	/* GMS mask */
67 	uint32_t	gm_num;		/* number of modes in gm_vec */
68 	int 		*gm_vec;	/* modes array */
69 } gms_mode_t;
70 
71 static void *agptarget_glob_soft_handle;
72 
73 #define	GETSOFTC(instance)	((agp_target_softstate_t *)	\
74     ddi_get_soft_state(agptarget_glob_soft_handle, instance));
75 
76 /*
77  * The AMD8151 bridge is the only supported 64 bit hardware
78  */
79 static int
80 is_64bit_aper(agp_target_softstate_t *softstate)
81 {
82 	return (softstate->tsoft_devid == AMD_BR_8151);
83 }
84 
85 /*
86  * Check if it is an intel bridge
87  */
88 static int
89 is_intel_br(agp_target_softstate_t *softstate)
90 {
91 	return ((softstate->tsoft_devid & VENDOR_ID_MASK) ==
92 	    INTEL_VENDOR_ID);
93 }
94 
95 /*
96  * agp_target_cap_find()
97  *
98  * Description:
99  * 	This function searches the linked capability list to find the offset
100  * 	of the AGP capability register. When it was not found, return 0.
101  * 	This works for standard AGP chipsets, but not for some Intel chipsets,
102  * 	like the I830M/I830MP/I852PM/I852GME/I855GME. It will return 0 for
103  * 	these chipsets even if AGP is supported. So the offset of acapid
104  * 	should be set manually in thoses cases.
105  *
106  * Arguments:
107  * 	pci_handle		ddi acc handle of pci config
108  *
109  * Returns:
110  * 	0			No capability pointer register found
111  * 	nexcap			The AGP capability pointer register offset
112  */
113 static off_t
114 agp_target_cap_find(ddi_acc_handle_t pci_handle)
115 {
116 	off_t		nextcap = 0;
117 	uint32_t	ncapid = 0;
118 	uint8_t		value = 0;
119 
120 	/* Check if this device supports the capability pointer */
121 	value = (uint8_t)(pci_config_get16(pci_handle, PCI_CONF_STAT)
122 	    & PCI_CONF_CAP_MASK);
123 
124 	if (!value)
125 		return (0);
126 	/* Get the offset of the first capability pointer from CAPPTR */
127 	nextcap = (off_t)(pci_config_get8(pci_handle, AGP_CONF_CAPPTR));
128 
129 	/* Check the AGP capability from the first capability pointer */
130 	while (nextcap) {
131 		ncapid = pci_config_get32(pci_handle, nextcap);
132 		/*
133 		 * AGP3.0 rev1.0 127  the capid was assigned by the PCI SIG,
134 		 * 845 data sheet page 69
135 		 */
136 		if ((ncapid & PCI_CONF_CAPID_MASK) ==
137 		    AGP_CAP_ID) /* The AGP cap was found */
138 			break;
139 
140 		nextcap = (off_t)((ncapid & PCI_CONF_NCAPID_MASK) >> 8);
141 	}
142 
143 	return (nextcap);
144 
145 }
146 
147 /*
148  * agp_target_get_aperbase()
149  *
150  * Description:
151  * 	This function gets the AGP aperture base address from the AGP target
152  *	register, the AGP aperture base register was programmed by the BIOS.
153  *
154  * Arguments:
155  * 	softstate		driver soft state pointer
156  *
157  * Returns:
158  * 	aper_base 		AGP aperture base address
159  *
160  * Notes:
161  * 	If a 64bit bridge device is available, the AGP aperture base address
162  * 	can be 64 bit.
163  */
164 static uint64_t
165 agp_target_get_apbase(agp_target_softstate_t *softstate)
166 {
167 	uint64_t aper_base;
168 
169 	if (is_intel_br(softstate)) {
170 		aper_base = pci_config_get32(softstate->tsoft_pcihdl,
171 		    AGP_CONF_APERBASE) & AGP_32_APERBASE_MASK;
172 	} else if (is_64bit_aper(softstate)) {
173 		aper_base = pci_config_get64(softstate->tsoft_pcihdl,
174 		    AGP_CONF_APERBASE);
175 		/* 32-bit or 64-bit aperbase base pointer */
176 		if ((aper_base & AGP_APER_TYPE_MASK) == 0)
177 			aper_base &= AGP_32_APERBASE_MASK;
178 		else
179 			aper_base &= AGP_64_APERBASE_MASK;
180 	}
181 
182 	return (aper_base);
183 }
184 
185 /*
186  * agp_target_get_apsize()
187  *
188  * Description:
189  * 	This function gets the AGP aperture size by reading the AGP aperture
190  * 	size register.
191  * Arguments:
192  * 	softstate		driver soft state pointer
193  *
194  * Return:
195  * 	size		The AGP aperture size in megabytes
196  * 	0		an unexpected error
197  */
198 static size_t
199 agp_target_get_apsize(agp_target_softstate_t *softstate)
200 {
201 	off_t cap;
202 	uint16_t value;
203 	size_t size, regsize;
204 
205 	ASSERT(softstate->tsoft_acaptr);
206 	cap = softstate->tsoft_acaptr;
207 
208 	if (is_intel_br(softstate)) {
209 		/* extend this value to 16 bit for later tests */
210 		value = (uint16_t)pci_config_get8(softstate->tsoft_pcihdl,
211 		    cap + AGP_CONF_APERSIZE) | AGP_APER_SIZE_MASK;
212 	} else if (is_64bit_aper(softstate)) {
213 		value = pci_config_get16(softstate->tsoft_pcihdl,
214 		    cap + AGP_CONF_APERSIZE);
215 	}
216 
217 	if (value & AGP_APER_128M_MASK) {
218 		switch (value & AGP_APER_128M_MASK) {
219 			case AGP_APER_4M:
220 				size = 4; /* 4M */
221 				break;
222 			case AGP_APER_8M:
223 				size = 8; /* 8M */
224 				break;
225 			case AGP_APER_16M:
226 				size = 16; /* 16M */
227 				break;
228 			case AGP_APER_32M:
229 				size = 32; /* 32M */
230 				break;
231 			case AGP_APER_64M:
232 				size = 64; /* 64M */
233 				break;
234 			case AGP_APER_128M:
235 				size = 128; /* 128M */
236 				break;
237 			default:
238 				size = 0; /* not true */
239 		}
240 	} else {
241 		switch (value & AGP_APER_4G_MASK) {
242 			case AGP_APER_256M:
243 				size = 256; /* 256 M */
244 				break;
245 			case AGP_APER_512M:
246 				size = 512; /* 512 M */
247 				break;
248 			case AGP_APER_1024M:
249 				size = 1024; /* 1024 M */
250 				break;
251 			case AGP_APER_2048M:
252 				size = 2048; /* 2048 M */
253 				break;
254 			case AGP_APER_4G:
255 				size = 4096; /* 4096 M */
256 				break;
257 			default:
258 				size = 0; /* not true */
259 		}
260 	}
261 	/*
262 	 * In some cases, there is no APSIZE register, so the size value
263 	 * of 256M could be wrong. Check the value by reading the size of
264 	 * the first register which was set in the PCI configuration space.
265 	 */
266 	if (size == 256) {
267 		if (ddi_dev_regsize(softstate->tsoft_dip,
268 		    AGP_TARGET_BAR1, (off_t *)&regsize) == DDI_FAILURE)
269 			return (0);
270 
271 		if (MB2BYTES(size) != regsize) {
272 			TARGETDB_PRINT2((CE_WARN,
273 			    "APSIZE 256M doesn't match regsize %lx",
274 			    regsize));
275 			TARGETDB_PRINT2((CE_WARN, "Use regsize instead"));
276 			size = BYTES2MB(regsize);
277 		}
278 	}
279 
280 	return (size);
281 }
282 
283 static void
284 agp_target_set_gartaddr(agp_target_softstate_t *softstate, uint32_t gartaddr)
285 {
286 	ASSERT(softstate->tsoft_acaptr);
287 
288 	/* Disable the GTLB for Intel chipsets */
289 	pci_config_put16(softstate->tsoft_pcihdl,
290 	    softstate->tsoft_acaptr + AGP_CONF_CONTROL, 0x0000);
291 
292 	pci_config_put32(softstate->tsoft_pcihdl,
293 	    softstate->tsoft_acaptr + AGP_CONF_ATTBASE,
294 	    gartaddr & AGP_ATTBASE_MASK);
295 }
296 
297 /*
298  * Pre-allocated graphics memory for every type of Intel north bridge, mem size
299  * are specified in kbytes.
300  */
301 #define	GMS_MB(n) 	((n) * 1024)
302 #define	GMS_SHIFT 	4
303 #define	GMS_SIZE(a)	(sizeof (a) / sizeof (int))
304 
305 /*
306  * Since value zero always means "No memory pre-allocated", value of (GMS - 1)
307  * is used to index these arrays, i.e. gms_xxx[1] contains the mem size (in kb)
308  * that GMS value 0x1 corresponding to.
309  *
310  * Assuming all "reserved" GMS value as zero bytes of pre-allocated graphics
311  * memory, unless some special BIOS settings exist.
312  */
313 static int gms_810[12] = {0, 0, 0, 0, 0, 0, 0, 512, 0, 0, 0, GMS_MB(1)};
314 static int gms_830_845[4] = {0, 512, GMS_MB(1), GMS_MB(8)};
315 static int gms_855GM[5] = {GMS_MB(1), GMS_MB(4), GMS_MB(8), GMS_MB(16),
316 	GMS_MB(32)};
317 /* There is no modes for 16M in datasheet, but some BIOS add it. */
318 static int gms_865_915GM[4] = {GMS_MB(1), 0, GMS_MB(8), GMS_MB(16)};
319 static int gms_915_945_965[3] = {GMS_MB(1), 0, GMS_MB(8)};
320 static int gms_965GM[7] = {GMS_MB(1), GMS_MB(4), GMS_MB(8), GMS_MB(16),
321 	GMS_MB(32), GMS_MB(48), GMS_MB(64)};
322 static int gms_X33[9] = {GMS_MB(1), GMS_MB(4), GMS_MB(8), GMS_MB(16),
323 	GMS_MB(32), GMS_MB(48), GMS_MB(64), GMS_MB(128), GMS_MB(256)};
324 
325 static gms_mode_t gms_modes[] = {
326 	{INTEL_BR_810, I810_CONF_SMRAM, I810_GMS_MASK,
327 		GMS_SIZE(gms_810), gms_810},
328 	{INTEL_BR_810DC, I810_CONF_SMRAM, I810_GMS_MASK,
329 		GMS_SIZE(gms_810), gms_810},
330 	{INTEL_BR_810E, I810_CONF_SMRAM, I810_GMS_MASK,
331 		GMS_SIZE(gms_810), gms_810},
332 	{INTEL_BR_830M, I8XX_CONF_GC, I8XX_GC_MODE_MASK,
333 		GMS_SIZE(gms_830_845), gms_830_845},
334 	{INTEL_BR_845, I8XX_CONF_GC, I8XX_GC_MODE_MASK,
335 		GMS_SIZE(gms_830_845), gms_830_845},
336 	{INTEL_BR_855GM, I8XX_CONF_GC, I8XX_GC_MODE_MASK,
337 		GMS_SIZE(gms_855GM), gms_855GM},
338 	{INTEL_BR_865, I8XX_CONF_GC, I8XX_GC_MODE_MASK,
339 		GMS_SIZE(gms_865_915GM), gms_865_915GM},
340 	{INTEL_BR_915GM, I8XX_CONF_GC, I8XX_GC_MODE_MASK,
341 		GMS_SIZE(gms_865_915GM), gms_865_915GM},
342 	{INTEL_BR_915, I8XX_CONF_GC, I8XX_GC_MODE_MASK,
343 		GMS_SIZE(gms_915_945_965), gms_915_945_965},
344 	{INTEL_BR_945, I8XX_CONF_GC, I8XX_GC_MODE_MASK,
345 		GMS_SIZE(gms_915_945_965), gms_915_945_965},
346 	{INTEL_BR_945GM, I8XX_CONF_GC, I8XX_GC_MODE_MASK,
347 		GMS_SIZE(gms_915_945_965), gms_915_945_965},
348 	{INTEL_BR_946GZ, I8XX_CONF_GC, I8XX_GC_MODE_MASK,
349 		GMS_SIZE(gms_915_945_965), gms_915_945_965},
350 	{INTEL_BR_965G1, I8XX_CONF_GC, I8XX_GC_MODE_MASK,
351 		GMS_SIZE(gms_915_945_965), gms_915_945_965},
352 	{INTEL_BR_965G2, I8XX_CONF_GC, I8XX_GC_MODE_MASK,
353 		GMS_SIZE(gms_915_945_965), gms_915_945_965},
354 	{INTEL_BR_965Q, I8XX_CONF_GC, I8XX_GC_MODE_MASK,
355 		GMS_SIZE(gms_915_945_965), gms_915_945_965},
356 	{INTEL_BR_965GM, I8XX_CONF_GC, I8XX_GC_MODE_MASK,
357 		GMS_SIZE(gms_965GM), gms_965GM},
358 	{INTEL_BR_965GME, I8XX_CONF_GC, I8XX_GC_MODE_MASK,
359 		GMS_SIZE(gms_965GM), gms_965GM},
360 	{INTEL_BR_Q35, I8XX_CONF_GC, IX33_GC_MODE_MASK,
361 		GMS_SIZE(gms_X33), gms_X33},
362 	{INTEL_BR_G33, I8XX_CONF_GC, IX33_GC_MODE_MASK,
363 		GMS_SIZE(gms_X33), gms_X33},
364 	{INTEL_BR_Q33, I8XX_CONF_GC, IX33_GC_MODE_MASK,
365 		GMS_SIZE(gms_X33), gms_X33},
366 	{INTEL_BR_GM45, I8XX_CONF_GC, I8XX_GC_MODE_MASK,
367 		GMS_SIZE(gms_965GM), gms_965GM}
368 };
369 static int
370 get_chip_gms(uint32_t devid)
371 {
372 	int num_modes;
373 	int i;
374 
375 	num_modes = (sizeof (gms_modes) / sizeof (gms_mode_t));
376 
377 	for (i = 0; i < num_modes; i++) {
378 		if (gms_modes[i].gm_devid == devid)
379 			break;
380 	}
381 
382 	return ((i == num_modes) ? -1 : i);
383 }
384 
385 /* Returns the size (kbytes) of pre-allocated graphics memory */
386 static size_t
387 i8xx_biosmem_detect(agp_target_softstate_t *softstate)
388 {
389 	uint8_t memval;
390 	size_t kbytes;
391 	int	gms_off;
392 
393 	kbytes = 0;
394 	gms_off = softstate->tsoft_gms_off;
395 
396 	/* fetch the GMS value from DRAM controller */
397 	memval = pci_config_get8(softstate->tsoft_pcihdl,
398 	    gms_modes[gms_off].gm_regoff);
399 	TARGETDB_PRINT2((CE_NOTE, "i8xx_biosmem_detect: memval = %x", memval));
400 	memval = (memval & gms_modes[gms_off].gm_mask) >> GMS_SHIFT;
401 	/* assuming zero byte for 0 or "reserved" GMS values */
402 	if (memval == 0 || memval > gms_modes[gms_off].gm_num) {
403 		TARGETDB_PRINT2((CE_WARN, "i8xx_biosmem_detect: "
404 		    "devid = %x, GMS = %x. assuming zero byte of "
405 		    "pre-allocated memory",
406 		    gms_modes[gms_off].gm_devid, memval));
407 		goto done;
408 	}
409 	memval--;	/* use (GMS_value - 1) as index */
410 	kbytes = (gms_modes[gms_off].gm_vec)[memval];
411 
412 done:
413 	TARGETDB_PRINT2((CE_NOTE,
414 	    "i8xx_biosmem_detect: %ldKB BIOS pre-allocated memory detected",
415 	    kbytes));
416 	return (kbytes);
417 }
418 
419 /*ARGSUSED*/
420 static int agptarget_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
421     void *arg, void **resultp)
422 {
423 	agp_target_softstate_t *st;
424 	int instance, rval = DDI_FAILURE;
425 	dev_t dev;
426 
427 	switch (cmd) {
428 	case DDI_INFO_DEVT2DEVINFO:
429 		dev = (dev_t)arg;
430 		instance = DEV2INST(dev);
431 		st = ddi_get_soft_state(agptarget_glob_soft_handle, instance);
432 		if (st != NULL) {
433 			mutex_enter(&st->tsoft_lock);
434 			*resultp = st->tsoft_dip;
435 			mutex_exit(&st->tsoft_lock);
436 			rval = DDI_SUCCESS;
437 		} else
438 			*resultp = NULL;
439 
440 		break;
441 	case DDI_INFO_DEVT2INSTANCE:
442 		dev = (dev_t)arg;
443 		instance = DEV2INST(dev);
444 		*resultp = (void *)(uintptr_t)instance;
445 		rval = DDI_SUCCESS;
446 	default:
447 		break;
448 	}
449 
450 	return (rval);
451 }
452 
453 static int
454 intel_br_resume(agp_target_softstate_t *softstate)
455 {
456 	int gms_off;
457 
458 	gms_off = softstate->tsoft_gms_off;
459 
460 	/*
461 	 * We recover the gmch graphics control register here
462 	 */
463 	pci_config_put16(softstate->tsoft_pcihdl,
464 	    gms_modes[gms_off].gm_regoff, softstate->tsoft_gms);
465 
466 	return (DDI_SUCCESS);
467 }
468 static int
469 intel_br_suspend(agp_target_softstate_t *softstate)
470 {
471 	int gms_off;
472 
473 	gms_off = softstate->tsoft_gms_off;
474 	softstate->tsoft_gms = pci_config_get16(softstate->tsoft_pcihdl,
475 	    gms_modes[gms_off].gm_regoff);
476 
477 	return (DDI_SUCCESS);
478 }
479 
480 static int
481 agp_target_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
482 {
483 	agp_target_softstate_t *softstate;
484 	int instance;
485 	int status;
486 
487 	instance = ddi_get_instance(dip);
488 
489 	switch (cmd) {
490 	case DDI_ATTACH:
491 		break;
492 	case DDI_RESUME:
493 		softstate =
494 		    ddi_get_soft_state(agptarget_glob_soft_handle, instance);
495 		return (intel_br_resume(softstate));
496 	default:
497 		TARGETDB_PRINT2((CE_WARN, "agp_target_attach:"
498 		    "only attach and resume ops are supported"));
499 		return (DDI_FAILURE);
500 	}
501 
502 	if (ddi_soft_state_zalloc(agptarget_glob_soft_handle,
503 	    instance) != DDI_SUCCESS) {
504 		TARGETDB_PRINT2((CE_WARN, "agp_target_attach:"
505 		    "soft state zalloc failed"));
506 		return (DDI_FAILURE);
507 	}
508 
509 	softstate = ddi_get_soft_state(agptarget_glob_soft_handle, instance);
510 	mutex_init(&softstate->tsoft_lock, NULL, MUTEX_DRIVER, NULL);
511 	softstate->tsoft_dip = dip;
512 	status = pci_config_setup(dip, &softstate->tsoft_pcihdl);
513 	if (status != DDI_SUCCESS) {
514 		TARGETDB_PRINT2((CE_WARN, "agp_target_attach:"
515 		    "pci config setup failed"));
516 		ddi_soft_state_free(agptarget_glob_soft_handle,
517 		    instance);
518 		return (DDI_FAILURE);
519 	}
520 
521 	softstate->tsoft_devid = pci_config_get32(softstate->tsoft_pcihdl,
522 	    PCI_CONF_VENID);
523 	softstate->tsoft_gms_off = get_chip_gms(softstate->tsoft_devid);
524 	if (softstate->tsoft_gms_off < 0) {
525 		TARGETDB_PRINT2((CE_WARN, "agp_target_attach:"
526 		    "read gms offset failed"));
527 		pci_config_teardown(&softstate->tsoft_pcihdl);
528 		ddi_soft_state_free(agptarget_glob_soft_handle,
529 		    instance);
530 		return (DDI_FAILURE);
531 	}
532 	softstate->tsoft_acaptr = agp_target_cap_find(softstate->tsoft_pcihdl);
533 	if (softstate->tsoft_acaptr == 0) {
534 		/* Make a correction for some Intel chipsets */
535 		if (is_intel_br(softstate))
536 			softstate->tsoft_acaptr = AGP_CAP_OFF_DEF;
537 		else {
538 			TARGETDB_PRINT2((CE_WARN, "agp_target_attach:"
539 			    "Not a supposed corretion"));
540 			pci_config_teardown(&softstate->tsoft_pcihdl);
541 			ddi_soft_state_free(agptarget_glob_soft_handle,
542 			    instance);
543 			return (DDI_FAILURE);
544 		}
545 	}
546 
547 	status = ddi_create_minor_node(dip, AGPTARGET_NAME, S_IFCHR,
548 	    INST2NODENUM(instance), DDI_NT_AGP_TARGET, 0);
549 
550 	if (status != DDI_SUCCESS) {
551 		TARGETDB_PRINT2((CE_WARN, "agp_target_attach:"
552 		    "Create minor node failed"));
553 		pci_config_teardown(&softstate->tsoft_pcihdl);
554 		ddi_soft_state_free(agptarget_glob_soft_handle, instance);
555 		return (DDI_FAILURE);
556 	}
557 
558 	return (DDI_SUCCESS);
559 }
560 
561 /*ARGSUSED*/
562 static int
563 agp_target_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
564 {
565 	int instance;
566 	agp_target_softstate_t *softstate;
567 
568 	instance = ddi_get_instance(dip);
569 	softstate = ddi_get_soft_state(agptarget_glob_soft_handle, instance);
570 
571 	if (cmd == DDI_SUSPEND) {
572 		/* get GMS modes list entry */
573 		return (intel_br_suspend(softstate));
574 	}
575 
576 	if (cmd != DDI_DETACH) {
577 		TARGETDB_PRINT2((CE_WARN, "agp_target_detach:"
578 		    "only detach and suspend ops are supported"));
579 		return (DDI_FAILURE);
580 	}
581 
582 	ddi_remove_minor_node(dip, AGPTARGET_NAME);
583 	pci_config_teardown(&softstate->tsoft_pcihdl);
584 	mutex_destroy(&softstate->tsoft_lock);
585 	ddi_soft_state_free(agptarget_glob_soft_handle, instance);
586 	return (DDI_SUCCESS);
587 }
588 
589 /*ARGSUSED*/
590 static int
591 agp_target_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
592     cred_t *cred, int *rval)
593 {
594 	int instance = DEV2INST(dev);
595 	agp_target_softstate_t *st;
596 	static char kernel_only[] =
597 	    "amd64_gart_ioctl: is a kernel only ioctl";
598 
599 	if (!(mode & FKIOCTL)) {
600 		TARGETDB_PRINT2((CE_CONT, kernel_only));
601 		return (ENXIO);
602 	}
603 	st = GETSOFTC(instance);
604 
605 	if (st == NULL)
606 		return (ENXIO);
607 
608 	mutex_enter(&st->tsoft_lock);
609 
610 	switch (cmd) {
611 	case CHIP_DETECT:
612 	{
613 		int type = 0;
614 
615 		if (is_intel_br(st))
616 			type = CHIP_IS_INTEL;
617 		else if (is_64bit_aper(st))
618 			type = CHIP_IS_AMD;
619 		else {
620 			type = 0;
621 			TARGETDB_PRINT2((CE_WARN, "Unknown bridge!"));
622 		}
623 
624 		if (ddi_copyout(&type, (void *)data, sizeof (int), mode)) {
625 			mutex_exit(&st->tsoft_lock);
626 			return (EFAULT);
627 		}
628 
629 		break;
630 	}
631 	case I8XX_GET_PREALLOC_SIZE:
632 	{
633 		size_t prealloc_size;
634 
635 		if (!is_intel_br(st)) {
636 			mutex_exit(&st->tsoft_lock);
637 			return (EINVAL);
638 		}
639 
640 		prealloc_size = i8xx_biosmem_detect(st);
641 		if (ddi_copyout(&prealloc_size, (void *)data,
642 		    sizeof (size_t), mode)) {
643 			mutex_exit(&st->tsoft_lock);
644 			return (EFAULT);
645 		}
646 
647 		break;
648 	}
649 	case AGP_TARGET_GETINFO:
650 	{
651 		i_agp_info_t info;
652 		uint32_t value;
653 		off_t cap;
654 
655 		ASSERT(st->tsoft_acaptr);
656 
657 		cap = st->tsoft_acaptr;
658 		value = pci_config_get32(st->tsoft_pcihdl, cap);
659 		info.iagp_ver.agpv_major = (uint16_t)((value >> 20) & 0xf);
660 		info.iagp_ver.agpv_minor = (uint16_t)((value >> 16) & 0xf);
661 		info.iagp_devid = st->tsoft_devid;
662 		info.iagp_mode = pci_config_get32(st->tsoft_pcihdl,
663 		    cap + AGP_CONF_STATUS);
664 		info.iagp_aperbase = agp_target_get_apbase(st);
665 		info.iagp_apersize = agp_target_get_apsize(st);
666 
667 		if (ddi_copyout(&info, (void *)data,
668 		    sizeof (i_agp_info_t), mode)) {
669 			mutex_exit(&st->tsoft_lock);
670 			return (EFAULT);
671 		}
672 		break;
673 
674 	}
675 	/*
676 	 * This ioctl is only for Intel AGP chipsets.
677 	 * It is not necessary for the AMD8151 AGP bridge, because
678 	 * this register in the AMD8151 does not control any hardware.
679 	 * It is only provided for compatibility with an Intel AGP bridge.
680 	 * Please refer to the <<AMD8151 data sheet>> page 24,
681 	 * AGP device GART pointer.
682 	 */
683 	case AGP_TARGET_SET_GATTADDR:
684 	{
685 		uint32_t gartaddr;
686 
687 		if (ddi_copyin((void *)data, &gartaddr,
688 		    sizeof (uint32_t), mode)) {
689 			mutex_exit(&st->tsoft_lock);
690 			return (EFAULT);
691 		}
692 
693 		agp_target_set_gartaddr(st, gartaddr);
694 		break;
695 	}
696 	case AGP_TARGET_SETCMD:
697 	{
698 		uint32_t command;
699 
700 		if (ddi_copyin((void *)data, &command,
701 		    sizeof (uint32_t), mode)) {
702 			mutex_exit(&st->tsoft_lock);
703 			return (EFAULT);
704 		}
705 
706 		ASSERT(st->tsoft_acaptr);
707 
708 		pci_config_put32(st->tsoft_pcihdl,
709 		    st->tsoft_acaptr + AGP_CONF_COMMAND,
710 		    command);
711 		break;
712 
713 	}
714 	case AGP_TARGET_FLUSH_GTLB:
715 	{
716 		uint16_t value;
717 
718 		ASSERT(st->tsoft_acaptr);
719 
720 		value = pci_config_get16(st->tsoft_pcihdl,
721 		    st->tsoft_acaptr + AGP_CONF_CONTROL);
722 		value &= ~AGPCTRL_GTLBEN;
723 		pci_config_put16(st->tsoft_pcihdl,
724 		    st->tsoft_acaptr + AGP_CONF_CONTROL, value);
725 		value |= AGPCTRL_GTLBEN;
726 		pci_config_put16(st->tsoft_pcihdl,
727 		    st->tsoft_acaptr + AGP_CONF_CONTROL, value);
728 
729 		break;
730 	}
731 	case AGP_TARGET_CONFIGURE:
732 	{
733 		uint8_t value;
734 
735 		ASSERT(st->tsoft_acaptr);
736 
737 		/*
738 		 * In Intel agp bridges, agp misc register offset
739 		 * is indexed from 0 instead of capability register.
740 		 * AMD agp bridges have no such misc register
741 		 * to control the aperture access, and they have
742 		 * similar regsiters in CPU gart devices instead.
743 		 */
744 
745 		if (is_intel_br(st)) {
746 			value = pci_config_get8(st->tsoft_pcihdl,
747 			    st->tsoft_acaptr + AGP_CONF_MISC);
748 			value |= AGP_MISC_APEN;
749 			pci_config_put8(st->tsoft_pcihdl,
750 			    st->tsoft_acaptr + AGP_CONF_MISC, value);
751 		}
752 		break;
753 
754 	}
755 	case AGP_TARGET_UNCONFIG:
756 	{
757 		uint32_t value1;
758 		uint8_t value2;
759 
760 		ASSERT(st->tsoft_acaptr);
761 
762 		pci_config_put16(st->tsoft_pcihdl,
763 		    st->tsoft_acaptr + AGP_CONF_CONTROL, 0x0);
764 
765 		if (is_intel_br(st)) {
766 			value2 = pci_config_get8(st->tsoft_pcihdl,
767 			    st->tsoft_acaptr + AGP_CONF_MISC);
768 			value2 &= ~AGP_MISC_APEN;
769 			pci_config_put8(st->tsoft_pcihdl,
770 			    st->tsoft_acaptr + AGP_CONF_MISC, value2);
771 		}
772 
773 		value1 = pci_config_get32(st->tsoft_pcihdl,
774 		    st->tsoft_acaptr + AGP_CONF_COMMAND);
775 		value1 &= ~AGPCMD_AGPEN;
776 		pci_config_put32(st->tsoft_pcihdl,
777 		    st->tsoft_acaptr + AGP_CONF_COMMAND,
778 		    value1);
779 
780 		pci_config_put32(st->tsoft_pcihdl,
781 		    st->tsoft_acaptr + AGP_CONF_ATTBASE, 0x0);
782 
783 		break;
784 	}
785 
786 	default:
787 		mutex_exit(&st->tsoft_lock);
788 		return (ENXIO);
789 	} /* end switch */
790 
791 	mutex_exit(&st->tsoft_lock);
792 
793 	return (0);
794 }
795 
796 /*ARGSUSED*/
797 static int
798 agp_target_open(dev_t *devp, int flag, int otyp, cred_t *cred)
799 {
800 	int instance = DEV2INST(*devp);
801 	agp_target_softstate_t *st;
802 
803 	if (!(flag & FKLYR))
804 		return (ENXIO);
805 
806 	st = GETSOFTC(instance);
807 
808 	if (st == NULL)
809 		return (ENXIO);
810 
811 	return (0);
812 }
813 
814 /*ARGSUSED*/
815 static int
816 agp_target_close(dev_t dev, int flag, int otyp, cred_t *cred)
817 {
818 	int instance = DEV2INST(dev);
819 	agp_target_softstate_t *st;
820 
821 	st = GETSOFTC(instance);
822 
823 	if (st == NULL)
824 		return (ENXIO);
825 
826 	return (0);
827 }
828 
829 static  struct  cb_ops  agp_target_cb_ops = {
830 	agp_target_open,		/* cb_open */
831 	agp_target_close,		/* cb_close */
832 	nodev,				/* cb_strategy */
833 	nodev,				/* cb_print */
834 	nodev,				/* cb_dump */
835 	nodev,				/* cb_read() */
836 	nodev,				/* cb_write() */
837 	agp_target_ioctl,		/* cb_ioctl */
838 	nodev,				/* cb_devmap */
839 	nodev,				/* cb_mmap */
840 	nodev,				/* cb_segmap */
841 	nochpoll,			/* cb_chpoll */
842 	ddi_prop_op,			/* cb_prop_op */
843 	0,				/* cb_stream */
844 	D_NEW | D_MP, 			/* cb_flag */
845 	CB_REV,				/* cb_ops version? */
846 	nodev,				/* cb_aread() */
847 	nodev,				/* cb_awrite() */
848 };
849 
850 /* device operations */
851 static struct dev_ops agp_target_ops = {
852 	DEVO_REV,		/* devo_rev */
853 	0,			/* devo_refcnt */
854 	agptarget_getinfo, 	/* devo_getinfo */
855 	nulldev,		/* devo_identify */
856 	nulldev,		/* devo_probe */
857 	agp_target_attach,	/* devo_attach */
858 	agp_target_detach,	/* devo_detach */
859 	nodev,			/* devo_reset */
860 	&agp_target_cb_ops,	/* devo_cb_ops */
861 	0,			/* devo_bus_ops */
862 	0,			/* devo_power */
863 };
864 
865 static  struct modldrv modldrv = {
866 	&mod_driverops,
867 	"AGP target driver",
868 	&agp_target_ops,
869 };
870 
871 static  struct modlinkage modlinkage = {
872 	MODREV_1,		/* MODREV_1 is indicated by manual */
873 	{&modldrv, NULL, NULL, NULL}
874 };
875 
876 int
877 _init(void)
878 {
879 	int ret;
880 
881 	ret = ddi_soft_state_init(&agptarget_glob_soft_handle,
882 	    sizeof (agp_target_softstate_t), 1);
883 
884 	if (ret)
885 		goto err1;
886 
887 	if ((ret = mod_install(&modlinkage)) != 0) {
888 		goto err2;
889 	}
890 
891 	return (DDI_SUCCESS);
892 err2:
893 	ddi_soft_state_fini(&agptarget_glob_soft_handle);
894 err1:
895 	return (ret);
896 }
897 
898 int
899 _info(struct  modinfo *modinfop)
900 {
901 	return (mod_info(&modlinkage, modinfop));
902 }
903 
904 int
905 _fini(void)
906 {
907 	int	ret;
908 
909 	if ((ret = mod_remove(&modlinkage)) == 0) {
910 		ddi_soft_state_fini(&agptarget_glob_soft_handle);
911 	}
912 	return (ret);
913 }
914