13446Smrj /*
23446Smrj  * CDDL HEADER START
33446Smrj  *
43446Smrj  * The contents of this file are subject to the terms of the
53446Smrj  * Common Development and Distribution License (the "License").
63446Smrj  * You may not use this file except in compliance with the License.
73446Smrj  *
83446Smrj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93446Smrj  * or http://www.opensolaris.org/os/licensing.
103446Smrj  * See the License for the specific language governing permissions
113446Smrj  * and limitations under the License.
123446Smrj  *
133446Smrj  * When distributing Covered Code, include this CDDL HEADER in each
143446Smrj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153446Smrj  * If applicable, add the following below this CDDL HEADER, with the
163446Smrj  * fields enclosed by brackets "[]" replaced with your own identifying
173446Smrj  * information: Portions Copyright [yyyy] [name of copyright owner]
183446Smrj  *
193446Smrj  * CDDL HEADER END
203446Smrj  */
213446Smrj 
223446Smrj /*
23*8832SMiao.Chen@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
243446Smrj  * Use is subject to license terms.
253446Smrj  */
263446Smrj 
273446Smrj /*
283446Smrj  * Misc module for AGP master device support
293446Smrj  */
303446Smrj 
313446Smrj #include <sys/modctl.h>
323446Smrj #include <sys/pci.h>
333446Smrj #include <sys/stat.h>
343446Smrj #include <sys/file.h>
353446Smrj #include <sys/types.h>
363446Smrj #include <sys/dditypes.h>
373446Smrj #include <sys/sunddi.h>
383446Smrj #include <sys/agpgart.h>
393446Smrj #include <sys/agp/agpdefs.h>
403446Smrj #include <sys/agp/agpmaster_io.h>
413446Smrj 
424478Skz151634 #define	PGTBL_CTL	0x2020	/* Page table control register */
434478Skz151634 #define	I8XX_FB_BAR	1
444478Skz151634 #define	I8XX_MMIO_BAR	2
454478Skz151634 #define	I8XX_PTE_OFFSET	0x10000
464478Skz151634 #define	I915_MMADR	1	/* mem-mapped registers BAR */
474478Skz151634 #define	I915_GMADR	3	/* graphics mem BAR */
484478Skz151634 #define	I915_GTTADDR	4	/* GTT BAR */
494478Skz151634 #define	I965_GTTMMADR	1	/* mem-mapped registers BAR + GTT */
504478Skz151634 /* In 965 1MB GTTMMADR, GTT reside in the latter 512KB */
514478Skz151634 #define	I965_GTT_OFFSET	0x80000
526778Smc196098 #define	GM45_GTT_OFFSET	0x200000
534478Skz151634 #define	GTT_SIZE_MASK	0xe
544478Skz151634 #define	GTT_512KB	(0 << 1)
554478Skz151634 #define	GTT_256KB	(1 << 1)
564478Skz151634 #define	GTT_128KB	(2 << 1)
576778Smc196098 #define	GTT_1MB		(3 << 1)
586778Smc196098 #define	GTT_2MB		(4 << 1)
596778Smc196098 #define	GTT_1_5MB	(5 << 1)
604478Skz151634 
614478Skz151634 #define	MMIO_BASE(x)	(x)->agpm_data.agpm_gtt.gtt_mmio_base
624478Skz151634 #define	MMIO_HANDLE(x)	(x)->agpm_data.agpm_gtt.gtt_mmio_handle
635036Skz151634 #define	GTT_HANDLE(x)	(x)->agpm_data.agpm_gtt.gtt_handle
645036Skz151634 /* Base address of GTT */
654478Skz151634 #define	GTT_ADDR(x)	(x)->agpm_data.agpm_gtt.gtt_addr
665036Skz151634 /* Graphics memory base address */
674478Skz151634 #define	APER_BASE(x)	(x)->agpm_data.agpm_gtt.gtt_info.igd_aperbase
684478Skz151634 
694478Skz151634 #define	AGPM_WRITE(x, off, val) \
704478Skz151634     ddi_put32(MMIO_HANDLE(x), (uint32_t *)(MMIO_BASE(x) + (off)), (val));
714478Skz151634 
724478Skz151634 #define	AGPM_READ(x, off) \
734478Skz151634     ddi_get32(MMIO_HANDLE(x), (uint32_t *)(MMIO_BASE(x) + (off)));
743446Smrj 
753446Smrj #ifdef DEBUG
763446Smrj #define	CONFIRM(value) ASSERT(value)
773446Smrj #else
783446Smrj #define	CONFIRM(value) if (!(value)) return (EINVAL)
793446Smrj #endif
803446Smrj 
813446Smrj int agpm_debug = 0;
823446Smrj #define	AGPM_DEBUG(args)	if (agpm_debug >= 1) cmn_err args
833446Smrj 
843446Smrj /*
853446Smrj  * Whether it is a Intel integrated graphics card
863446Smrj  */
873446Smrj #define	IS_IGD(agpmaster) ((agpmaster->agpm_dev_type == DEVICE_IS_I810) || \
884478Skz151634 	(agpmaster->agpm_dev_type == DEVICE_IS_I830))
893446Smrj 
903446Smrj 
914478Skz151634 /* Intel 915 and 945 series */
924478Skz151634 #define	IS_INTEL_915(agpmaster) ((agpmaster->agpm_id == INTEL_IGD_915) || \
934478Skz151634 	(agpmaster->agpm_id == INTEL_IGD_915GM) || \
944478Skz151634 	(agpmaster->agpm_id == INTEL_IGD_945) || \
958020SMiao.Chen@Sun.COM 	(agpmaster->agpm_id == INTEL_IGD_945GM) || \
968020SMiao.Chen@Sun.COM 	(agpmaster->agpm_id == INTEL_IGD_945GME))
974478Skz151634 
984478Skz151634 /* Intel 965 series */
994478Skz151634 #define	IS_INTEL_965(agpmaster) ((agpmaster->agpm_id == INTEL_IGD_946GZ) || \
1004478Skz151634 	(agpmaster->agpm_id == INTEL_IGD_965G1) || \
1014478Skz151634 	(agpmaster->agpm_id == INTEL_IGD_965Q) || \
1024478Skz151634 	(agpmaster->agpm_id == INTEL_IGD_965G2) || \
1034618Skz151634 	(agpmaster->agpm_id == INTEL_IGD_965GM) || \
1046778Smc196098 	(agpmaster->agpm_id == INTEL_IGD_965GME) || \
1057662SMiao.Chen@Sun.COM 	(agpmaster->agpm_id == INTEL_IGD_GM45) || \
1067662SMiao.Chen@Sun.COM 	IS_INTEL_G4X(agpmaster))
1073446Smrj 
1085036Skz151634 /* Intel G33 series */
1095036Skz151634 #define	IS_INTEL_X33(agpmaster) ((agpmaster->agpm_id == INTEL_IGD_Q35) || \
1105036Skz151634 	(agpmaster->agpm_id == INTEL_IGD_G33) || \
1115036Skz151634 	(agpmaster->agpm_id == INTEL_IGD_Q33))
1125036Skz151634 
1137662SMiao.Chen@Sun.COM /* Intel G4X series */
1147662SMiao.Chen@Sun.COM #define	IS_INTEL_G4X(agpmaster) ((agpmaster->agpm_id == INTEL_IGD_EL) || \
1157662SMiao.Chen@Sun.COM 	(agpmaster->agpm_id == INTEL_IGD_Q45) || \
116*8832SMiao.Chen@Sun.COM 	(agpmaster->agpm_id == INTEL_IGD_G45) || \
117*8832SMiao.Chen@Sun.COM 	(agpmaster->agpm_id == INTEL_IGD_G41))
1185036Skz151634 
1193446Smrj static struct modlmisc modlmisc = {
1207542SRichard.Bean@Sun.COM 	&mod_miscops, "AGP master interfaces"
1213446Smrj };
1223446Smrj 
1233446Smrj static struct modlinkage modlinkage = {
1243446Smrj 	MODREV_1, (void *)&modlmisc, NULL
1253446Smrj };
1263446Smrj 
1273446Smrj static ddi_device_acc_attr_t i8xx_dev_access = {
1283446Smrj 	DDI_DEVICE_ATTR_V0,
1293446Smrj 	DDI_NEVERSWAP_ACC,
1303446Smrj 	DDI_STRICTORDER_ACC
1313446Smrj };
1323446Smrj 
1333446Smrj static off_t agpmaster_cap_find(ddi_acc_handle_t);
1343446Smrj static int detect_i8xx_device(agp_master_softc_t *);
1353446Smrj static int detect_agp_devcice(agp_master_softc_t *, ddi_acc_handle_t);
1363446Smrj static int i8xx_add_to_gtt(gtt_impl_t *, igd_gtt_seg_t);
1373446Smrj static void i8xx_remove_from_gtt(gtt_impl_t *, igd_gtt_seg_t);
1383446Smrj 
1393446Smrj int
1403446Smrj _init(void)
1413446Smrj {
1423446Smrj 	int	err;
1433446Smrj 
1443446Smrj 	if ((err = mod_install(&modlinkage)) != 0)
1453446Smrj 		return (err);
1463446Smrj 
1473446Smrj 	return (0);
1483446Smrj }
1493446Smrj 
1503446Smrj int
1513446Smrj _fini(void)
1523446Smrj {
1533446Smrj 	int	err;
1543446Smrj 
1553446Smrj 	if ((err = mod_remove(&modlinkage)) != 0)
1563446Smrj 		return (err);
1573446Smrj 
1583446Smrj 	return (0);
1593446Smrj }
1603446Smrj 
1613446Smrj int
1623446Smrj _info(struct modinfo *modinfop)
1633446Smrj {
1643446Smrj 	return (mod_info(&modlinkage, modinfop));
1653446Smrj }
1663446Smrj 
1673446Smrj /*
1683446Smrj  * Minor node is not removed here, since the caller (xx_attach) is
1693446Smrj  * responsible for removing all nodes.
1703446Smrj  */
1713446Smrj void
1723446Smrj agpmaster_detach(agp_master_softc_t **master_softcp)
1733446Smrj {
1743446Smrj 	agp_master_softc_t *master_softc;
1753446Smrj 
1763446Smrj 	ASSERT(master_softcp);
1773446Smrj 	master_softc = *master_softcp;
1783446Smrj 
1793446Smrj 	/* intel integrated device */
1805036Skz151634 	if (IS_IGD(master_softc) &&
1815036Skz151634 	    ((MMIO_HANDLE(master_softc) != NULL) ||
1825036Skz151634 	    (GTT_HANDLE(master_softc) != NULL))) {
1835036Skz151634 		/*
1845036Skz151634 		 * for some chipsets, mmap handle is shared between both mmio
1855036Skz151634 		 * and GTT table.
1865036Skz151634 		 */
1875036Skz151634 		if ((GTT_HANDLE(master_softc) != MMIO_HANDLE(master_softc)) &&
1885036Skz151634 		    (GTT_HANDLE(master_softc) != NULL))
1895036Skz151634 			ddi_regs_map_free(&GTT_HANDLE(master_softc));
1905036Skz151634 		if (MMIO_HANDLE(master_softc) != NULL)
1914478Skz151634 			ddi_regs_map_free(&MMIO_HANDLE(master_softc));
1923446Smrj 	}
1933446Smrj 
1943446Smrj 	kmem_free(master_softc, sizeof (agp_master_softc_t));
1953446Smrj 	master_softc = NULL;
1963446Smrj 
1973446Smrj 	return;
1983446Smrj 
1993446Smrj }
2003446Smrj 
2013446Smrj /*
2024478Skz151634  * 965 has a fixed GTT table size (512KB), so check to see the actual aperture
2034478Skz151634  * size. Aperture size = GTT table size * 1024.
2044478Skz151634  */
2054478Skz151634 static off_t
2064478Skz151634 i965_apersize(agp_master_softc_t *agpmaster)
2074478Skz151634 {
2084478Skz151634 	off_t apersize;
2094478Skz151634 
2104478Skz151634 	apersize = AGPM_READ(agpmaster, PGTBL_CTL);
2114478Skz151634 	AGPM_DEBUG((CE_NOTE, "i965_apersize: PGTBL_CTL = %lx", apersize));
2124478Skz151634 	switch (apersize & GTT_SIZE_MASK) {
2136778Smc196098 	case GTT_2MB:
2146778Smc196098 		apersize = 2048;
2156778Smc196098 		break;
2166778Smc196098 	case GTT_1_5MB:
2176778Smc196098 		apersize = 1536;
2186778Smc196098 		break;
2196778Smc196098 	case GTT_1MB:
2206778Smc196098 		apersize = 1024;
2216778Smc196098 		break;
2224478Skz151634 	case GTT_512KB:
2234478Skz151634 		apersize = 512;
2244478Skz151634 		break;
2254478Skz151634 	case GTT_256KB:
2264478Skz151634 		apersize = 256;
2274478Skz151634 		break;
2284478Skz151634 	case GTT_128KB:
2294478Skz151634 		apersize = 128;
2304478Skz151634 		break;
2314478Skz151634 	default:
2325036Skz151634 		apersize = 0;
2334478Skz151634 		AGPM_DEBUG((CE_WARN,
2344478Skz151634 		    "i965_apersize: invalid GTT size in PGTBL_CTL"));
2354478Skz151634 	}
2365036Skz151634 	return (apersize);
2375036Skz151634 }
2385036Skz151634 
2395036Skz151634 /*
2405036Skz151634  * For Intel 3 series, we need to get GTT size from the GGMS field in GMCH
2415036Skz151634  * Graphics Control Register. Return aperture size in MB.
2425036Skz151634  */
2435036Skz151634 static off_t
2445036Skz151634 i3XX_apersize(ddi_acc_handle_t pci_acc_hdl)
2455036Skz151634 {
2465036Skz151634 	uint16_t value;
2475036Skz151634 	off_t apersize;
2485036Skz151634 
2495036Skz151634 	/*
2505036Skz151634 	 * Get the value of configuration register MGGC "Mirror of Dev0 GMCH
2515036Skz151634 	 * Graphics Control" from Internal Graphics #2 (Device2:Function0).
2525036Skz151634 	 */
2535036Skz151634 	value = pci_config_get16(pci_acc_hdl, I8XX_CONF_GC);
2545036Skz151634 	AGPM_DEBUG((CE_NOTE, "i3XX_apersize: MGGC = 0x%x", value));
2555036Skz151634 	/* computing aperture size using the pre-allocated GTT size */
2565036Skz151634 	switch (value & IX33_GGMS_MASK) {
2575036Skz151634 	case IX33_GGMS_1M:
2585036Skz151634 		apersize = 1024;
2595036Skz151634 		break;
2605036Skz151634 	case IX33_GGMS_2M:
2615036Skz151634 		apersize = 2048;
2625036Skz151634 		break;
2635036Skz151634 	default:
2645036Skz151634 		apersize = 0;	/* no memory pre-allocated */
2655036Skz151634 		AGPM_DEBUG((CE_WARN,
2665036Skz151634 		    "i3XX_apersize: no memory allocated for GTT"));
2675036Skz151634 	}
2685036Skz151634 	AGPM_DEBUG((CE_NOTE, "i3xx_apersize: apersize = %ldM", apersize));
2694478Skz151634 	return (apersize);
2704478Skz151634 }
2714478Skz151634 
2724478Skz151634 #define	CHECK_STATUS(status)	\
2734478Skz151634     if (status != DDI_SUCCESS) { \
2744478Skz151634 	    AGPM_DEBUG((CE_WARN, \
2754478Skz151634 		"set_gtt_mmio: regs_map_setup error")); \
2764478Skz151634 	    return (-1); \
2774478Skz151634 }
2784478Skz151634 /*
2794478Skz151634  * Set gtt_addr, gtt_mmio_base, igd_apersize, igd_aperbase and igd_devid
2804478Skz151634  * according to chipset.
2814478Skz151634  */
2824478Skz151634 static int
2835036Skz151634 set_gtt_mmio(dev_info_t *devi, agp_master_softc_t *agpmaster,
2845036Skz151634     ddi_acc_handle_t pci_acc_hdl)
2854478Skz151634 {
2865036Skz151634 	off_t apersize;  /* size of graphics mem (MB) == GTT size (KB) */
2874478Skz151634 	uint32_t value;
2885036Skz151634 	off_t gmadr_off;  /* GMADR offset in PCI config space */
2894478Skz151634 	int status;
2904478Skz151634 
2915036Skz151634 	if (IS_INTEL_X33(agpmaster)) {
2925036Skz151634 		/* Intel 3 series are similar with 915/945 series */
2934478Skz151634 		status = ddi_regs_map_setup(devi, I915_GTTADDR,
2944478Skz151634 		    &GTT_ADDR(agpmaster), 0, 0, &i8xx_dev_access,
2955036Skz151634 		    &GTT_HANDLE(agpmaster));
2964478Skz151634 		CHECK_STATUS(status);
2974478Skz151634 
2984478Skz151634 		status = ddi_regs_map_setup(devi, I915_MMADR,
2994478Skz151634 		    &MMIO_BASE(agpmaster), 0, 0, &i8xx_dev_access,
3004478Skz151634 		    &MMIO_HANDLE(agpmaster));
3014478Skz151634 		CHECK_STATUS(status);
3024478Skz151634 
3036110Sms148562 		gmadr_off = I915_CONF_GMADR;
3046110Sms148562 		/* Different computing method used in getting aperture size. */
3055036Skz151634 		apersize = i3XX_apersize(pci_acc_hdl);
3065036Skz151634 	} else if (IS_INTEL_965(agpmaster)) {
3075036Skz151634 		status = ddi_regs_map_setup(devi, I965_GTTMMADR,
3085036Skz151634 		    &MMIO_BASE(agpmaster), 0, 0, &i8xx_dev_access,
3095036Skz151634 		    &MMIO_HANDLE(agpmaster));
3105036Skz151634 		CHECK_STATUS(status);
3117662SMiao.Chen@Sun.COM 		if ((agpmaster->agpm_id == INTEL_IGD_GM45) ||
3127662SMiao.Chen@Sun.COM 		    IS_INTEL_G4X(agpmaster))
3136778Smc196098 			GTT_ADDR(agpmaster) =
3146778Smc196098 			    MMIO_BASE(agpmaster) + GM45_GTT_OFFSET;
3156778Smc196098 		else
3166778Smc196098 			GTT_ADDR(agpmaster) =
3176778Smc196098 			    MMIO_BASE(agpmaster) + I965_GTT_OFFSET;
3185036Skz151634 		GTT_HANDLE(agpmaster) = MMIO_HANDLE(agpmaster);
3195036Skz151634 
3205036Skz151634 		gmadr_off = I915_CONF_GMADR;
3215036Skz151634 		apersize = i965_apersize(agpmaster);
3225036Skz151634 	} else if (IS_INTEL_915(agpmaster)) {
3235036Skz151634 		/* I915/945 series */
3245036Skz151634 		status = ddi_regs_map_setup(devi, I915_GTTADDR,
3255036Skz151634 		    &GTT_ADDR(agpmaster), 0, 0, &i8xx_dev_access,
3265036Skz151634 		    &GTT_HANDLE(agpmaster));
3275036Skz151634 		CHECK_STATUS(status);
3285036Skz151634 
3295036Skz151634 		status = ddi_regs_map_setup(devi, I915_MMADR,
3305036Skz151634 		    &MMIO_BASE(agpmaster), 0, 0, &i8xx_dev_access,
3315036Skz151634 		    &MMIO_HANDLE(agpmaster));
3325036Skz151634 		CHECK_STATUS(status);
3335036Skz151634 
3345036Skz151634 		gmadr_off = I915_CONF_GMADR;
3354478Skz151634 		status = ddi_dev_regsize(devi, I915_GMADR, &apersize);
3365036Skz151634 		apersize = BYTES2MB(apersize);
3374478Skz151634 	} else {
3384478Skz151634 		/* I8XX series */
3394478Skz151634 		status = ddi_regs_map_setup(devi, I8XX_MMIO_BAR,
3404478Skz151634 		    &MMIO_BASE(agpmaster), 0, 0, &i8xx_dev_access,
3414478Skz151634 		    &MMIO_HANDLE(agpmaster));
3424478Skz151634 		CHECK_STATUS(status);
3434478Skz151634 
3444478Skz151634 		GTT_ADDR(agpmaster) = MMIO_BASE(agpmaster) + I8XX_PTE_OFFSET;
3455036Skz151634 		GTT_HANDLE(agpmaster) = MMIO_HANDLE(agpmaster);
3465036Skz151634 		gmadr_off = I8XX_CONF_GMADR;
3474478Skz151634 		status = ddi_dev_regsize(devi, I8XX_FB_BAR, &apersize);
3485036Skz151634 		apersize = BYTES2MB(apersize);
3494478Skz151634 		CHECK_STATUS(status);
3504478Skz151634 	}
3514478Skz151634 
3524478Skz151634 	/*
3535036Skz151634 	 * If memory size is smaller than a certain value, it means
3544478Skz151634 	 * the register set number for graphics memory range might
3554478Skz151634 	 * be wrong
3564478Skz151634 	 */
3575036Skz151634 	if (status != DDI_SUCCESS || apersize < 4) {
3584478Skz151634 		AGPM_DEBUG((CE_WARN,
3595036Skz151634 		    "set_gtt_mmio: error in getting graphics memory"));
3604478Skz151634 		return (-1);
3614478Skz151634 	}
3624478Skz151634 
3635036Skz151634 	agpmaster->agpm_data.agpm_gtt.gtt_info.igd_apersize = apersize;
3644478Skz151634 
3655036Skz151634 	/* get graphics memory base address from GMADR */
3665036Skz151634 	value = pci_config_get32(pci_acc_hdl, gmadr_off);
3674478Skz151634 	APER_BASE(agpmaster) = value & GTT_BASE_MASK;
3685036Skz151634 	AGPM_DEBUG((CE_NOTE, "set_gtt_mmio: aperbase = 0x%x, apersize = %ldM, "
3694478Skz151634 	    "gtt_addr = %p, mmio_base = %p", APER_BASE(agpmaster), apersize,
3704478Skz151634 	    (void *)GTT_ADDR(agpmaster), (void *)MMIO_BASE(agpmaster)));
3714478Skz151634 	return (0);
3724478Skz151634 }
3734478Skz151634 
3744478Skz151634 /*
3753446Smrj  * Try to initialize agp master.
3763446Smrj  * 0 is returned if the device is successfully initialized. AGP master soft
3773446Smrj  * state is returned in master_softcp if needed.
3783446Smrj  * Otherwise -1 is returned and *master_softcp is set to NULL.
3793446Smrj  */
3803446Smrj int
3813446Smrj agpmaster_attach(dev_info_t *devi, agp_master_softc_t **master_softcp,
3823446Smrj     ddi_acc_handle_t pci_acc_hdl, minor_t minor)
3833446Smrj {
3843446Smrj 	int instance;
3853446Smrj 	int status;
3863446Smrj 	agp_master_softc_t *agpmaster;
3873446Smrj 	char buf[80];
3883446Smrj 
3893446Smrj 
3903446Smrj 	ASSERT(pci_acc_hdl);
3913446Smrj 	*master_softcp = NULL;
3923446Smrj 	agpmaster = (agp_master_softc_t *)
3933446Smrj 	    kmem_zalloc(sizeof (agp_master_softc_t), KM_SLEEP);
3943446Smrj 
3953446Smrj 	agpmaster->agpm_id =
3963446Smrj 	    pci_config_get32(pci_acc_hdl, PCI_CONF_VENID);
3973446Smrj 	agpmaster->agpm_acc_hdl = pci_acc_hdl;
3983446Smrj 
3993446Smrj 	if (!detect_i8xx_device(agpmaster)) {
4004478Skz151634 		/* Intel 8XX, 915, 945 and 965 series */
4014478Skz151634 		if (set_gtt_mmio(devi, agpmaster, pci_acc_hdl) != 0)
4023446Smrj 			goto fail;
4033446Smrj 	} else if (detect_agp_devcice(agpmaster, pci_acc_hdl)) {
4044478Skz151634 		/* non IGD or AGP devices, AMD64 gart */
4053446Smrj 		AGPM_DEBUG((CE_WARN,
4063446Smrj 		    "agpmaster_attach: neither IGD or AGP devices exists"));
4073446Smrj 		agpmaster_detach(&agpmaster);
4083446Smrj 		return (0);
4093446Smrj 	}
4103446Smrj 
4115036Skz151634 	agpmaster->agpm_data.agpm_gtt.gtt_info.igd_devid =
4125036Skz151634 	    agpmaster->agpm_id;
4135036Skz151634 
4143446Smrj 	/* create minor node for IGD or AGP device */
4153446Smrj 	instance = ddi_get_instance(devi);
4163446Smrj 
4173446Smrj 	(void) sprintf(buf, "%s%d", AGPMASTER_NAME, instance);
4183446Smrj 	status = ddi_create_minor_node(devi, buf, S_IFCHR, minor,
4193446Smrj 	    DDI_NT_AGP_MASTER, 0);
4203446Smrj 
4213446Smrj 	if (status != DDI_SUCCESS) {
4223446Smrj 		AGPM_DEBUG((CE_WARN,
4233446Smrj 		    "agpmaster_attach: create agpmaster node failed"));
4243446Smrj 		goto fail;
4253446Smrj 	}
4263446Smrj 
4273446Smrj 	*master_softcp = agpmaster;
4283446Smrj 	return (0);
4293446Smrj fail:
4303446Smrj 	agpmaster_detach(&agpmaster);
4313446Smrj 	return (-1);
4323446Smrj }
4333446Smrj 
4343446Smrj /*
4353446Smrj  * Currently, it handles ioctl requests related with agp master device for
4363446Smrj  * layered driver (agpgart) only.
4373446Smrj  */
4383446Smrj /*ARGSUSED*/
4393446Smrj int
4403446Smrj agpmaster_ioctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *cred,
4413446Smrj     int *rval, agp_master_softc_t *softc)
4423446Smrj {
4433446Smrj 	uint32_t base;
4443446Smrj 	uint32_t addr;
4453446Smrj 	igd_gtt_seg_t seg;
4463446Smrj 	agp_info_t info;
4473446Smrj 	uint32_t value;
4483446Smrj 	off_t cap;
4493446Smrj 	uint32_t command;
4503446Smrj 	static char kernel_only[] =
4513446Smrj 	    "agpmaster_ioctl: %s is a kernel only ioctl";
4523446Smrj 
4533446Smrj 	CONFIRM(softc);
4543446Smrj 
4553446Smrj 	switch (cmd) {
4563446Smrj 	case DEVICE_DETECT:
4573446Smrj 		if (!(mode & FKIOCTL)) {
4583446Smrj 			AGPM_DEBUG((CE_CONT, kernel_only, "DEVICE_DETECT"));
4593446Smrj 			return (ENXIO);
4603446Smrj 		}
4613446Smrj 
4623446Smrj 		if (ddi_copyout(&softc->agpm_dev_type,
4633446Smrj 		    (void *)data, sizeof (int), mode))
4643446Smrj 			return (EFAULT);
4653446Smrj 		break;
4663446Smrj 	case AGP_MASTER_SETCMD:
4673446Smrj 		if (!(mode & FKIOCTL)) {
4683446Smrj 			AGPM_DEBUG((CE_CONT, kernel_only, "AGP_MASTER_SETCMD"));
4693446Smrj 			return (ENXIO);
4703446Smrj 		}
4713446Smrj 
4723446Smrj 		CONFIRM(softc->agpm_dev_type == DEVICE_IS_AGP);
4733446Smrj 		CONFIRM(softc->agpm_data.agpm_acaptr);
4743446Smrj 
4753446Smrj 		if (ddi_copyin((void *)data, &command,
4763446Smrj 		    sizeof (uint32_t), mode))
4773446Smrj 			return (EFAULT);
4783446Smrj 
4793446Smrj 		pci_config_put32(softc->agpm_acc_hdl,
4803446Smrj 		    softc->agpm_data.agpm_acaptr + AGP_CONF_COMMAND,
4813446Smrj 		    command);
4823446Smrj 		break;
4833446Smrj 	case AGP_MASTER_GETINFO:
4843446Smrj 		if (!(mode & FKIOCTL)) {
4853446Smrj 			AGPM_DEBUG((CE_CONT, kernel_only,
4863446Smrj 			    "AGP_MASTER_GETINFO"));
4873446Smrj 			return (ENXIO);
4883446Smrj 		}
4893446Smrj 
4903446Smrj 		CONFIRM(softc->agpm_dev_type == DEVICE_IS_AGP);
4913446Smrj 		CONFIRM(softc->agpm_data.agpm_acaptr);
4923446Smrj 
4933446Smrj 		cap = softc->agpm_data.agpm_acaptr;
4943446Smrj 		value = pci_config_get32(softc->agpm_acc_hdl, cap);
4953446Smrj 		info.agpi_version.agpv_major = (uint16_t)((value >> 20) & 0xf);
4963446Smrj 		info.agpi_version.agpv_minor = (uint16_t)((value >> 16) & 0xf);
4973446Smrj 		info.agpi_devid = softc->agpm_id;
4983446Smrj 		info.agpi_mode = pci_config_get32(
4993446Smrj 		    softc->agpm_acc_hdl, cap + AGP_CONF_STATUS);
5003446Smrj 
5013446Smrj 		if (ddi_copyout(&info, (void *)data,
5023446Smrj 		    sizeof (agp_info_t), mode))
5033446Smrj 			return (EFAULT);
5043446Smrj 		break;
5053446Smrj 	case I810_SET_GTT_BASE:
5063446Smrj 		if (!(mode & FKIOCTL)) {
5073446Smrj 			AGPM_DEBUG((CE_CONT, kernel_only, "I810_SET_GTT_ADDR"));
5083446Smrj 			return (ENXIO);
5093446Smrj 		}
5103446Smrj 
5113446Smrj 		CONFIRM(softc->agpm_dev_type == DEVICE_IS_I810);
5123446Smrj 
5133446Smrj 		if (ddi_copyin((void *)data, &base, sizeof (uint32_t), mode))
5143446Smrj 			return (EFAULT);
5153446Smrj 
5163446Smrj 		/* enables page table */
5173446Smrj 		addr = (base & GTT_BASE_MASK) | GTT_TABLE_VALID;
5183446Smrj 
5194478Skz151634 		AGPM_WRITE(softc, PGTBL_CTL, addr);
5203446Smrj 		break;
5213446Smrj 	case I8XX_GET_INFO:
5223446Smrj 		if (!(mode & FKIOCTL)) {
5233446Smrj 			AGPM_DEBUG((CE_CONT, kernel_only, "I8XX_GET_INFO"));
5243446Smrj 			return (ENXIO);
5253446Smrj 		}
5263446Smrj 
5273446Smrj 		CONFIRM(IS_IGD(softc));
5283446Smrj 
5293446Smrj 		if (ddi_copyout(&softc->agpm_data.agpm_gtt.gtt_info,
5303446Smrj 		    (void *)data, sizeof (igd_info_t), mode))
5313446Smrj 			return (EFAULT);
5323446Smrj 		break;
5333446Smrj 	case I8XX_ADD2GTT:
5343446Smrj 		if (!(mode & FKIOCTL)) {
5353446Smrj 			AGPM_DEBUG((CE_CONT, kernel_only, "I8XX_ADD2GTT"));
5363446Smrj 			return (ENXIO);
5373446Smrj 		}
5383446Smrj 
5393446Smrj 		CONFIRM(IS_IGD(softc));
5403446Smrj 
5413446Smrj 		if (ddi_copyin((void *)data, &seg,
5423446Smrj 		    sizeof (igd_gtt_seg_t), mode))
5433446Smrj 			return (EFAULT);
5443446Smrj 
5453446Smrj 		if (i8xx_add_to_gtt(&softc->agpm_data.agpm_gtt, seg))
5463446Smrj 			return (EINVAL);
5473446Smrj 		break;
5483446Smrj 	case I8XX_REM_GTT:
5493446Smrj 		if (!(mode & FKIOCTL)) {
5503446Smrj 			AGPM_DEBUG((CE_CONT, kernel_only, "I8XX_REM_GTT"));
5513446Smrj 			return (ENXIO);
5523446Smrj 		}
5533446Smrj 
5543446Smrj 		CONFIRM(IS_IGD(softc));
5553446Smrj 
5563446Smrj 		if (ddi_copyin((void *)data, &seg,
5573446Smrj 		    sizeof (igd_gtt_seg_t), mode))
5583446Smrj 			return (EFAULT);
5593446Smrj 
5603446Smrj 		i8xx_remove_from_gtt(&softc->agpm_data.agpm_gtt, seg);
5613446Smrj 		break;
5623446Smrj 	case I8XX_UNCONFIG:
5633446Smrj 		if (!(mode & FKIOCTL)) {
5643446Smrj 			AGPM_DEBUG((CE_CONT, kernel_only, "I8XX_UNCONFIG"));
5653446Smrj 			return (ENXIO);
5663446Smrj 		}
5673446Smrj 
5683446Smrj 		CONFIRM(IS_IGD(softc));
5693446Smrj 
5703446Smrj 		if (softc->agpm_dev_type == DEVICE_IS_I810)
5714478Skz151634 			AGPM_WRITE(softc, PGTBL_CTL, 0);
5723446Smrj 		/*
5733446Smrj 		 * may need to clear all gtt entries here for i830 series,
5743446Smrj 		 * but may not be necessary
5753446Smrj 		 */
5763446Smrj 		break;
5773446Smrj 	}
5783446Smrj 	return (0);
5793446Smrj }
5803446Smrj 
5813446Smrj /*
5823446Smrj  * If AGP cap pointer is successfully found, none-zero value is returned.
5833446Smrj  * Otherwise 0 is returned.
5843446Smrj  */
5853446Smrj static off_t
5863446Smrj agpmaster_cap_find(ddi_acc_handle_t acc_handle)
5873446Smrj {
5883446Smrj 	off_t		nextcap;
5893446Smrj 	uint32_t	ncapid;
5903446Smrj 	uint8_t		value;
5913446Smrj 
5923446Smrj 	/* check if this device supports capibility pointer */
5933446Smrj 	value = (uint8_t)(pci_config_get16(acc_handle, PCI_CONF_STAT)
5944303Skz151634 	    & PCI_CONF_CAP_MASK);
5953446Smrj 
5963446Smrj 	if (!value)
5973446Smrj 		return (0);
5983446Smrj 	/* get the offset of the first capability pointer from CAPPTR */
5993446Smrj 	nextcap = (off_t)(pci_config_get8(acc_handle, AGP_CONF_CAPPTR));
6003446Smrj 
6013446Smrj 	/* check AGP capability from the first capability pointer */
6023446Smrj 	while (nextcap) {
6033446Smrj 		ncapid = pci_config_get32(acc_handle, nextcap);
6043446Smrj 		if ((ncapid & PCI_CONF_CAPID_MASK)
6053446Smrj 		    == AGP_CAP_ID) /* find AGP cap */
6063446Smrj 			break;
6073446Smrj 
6083446Smrj 		nextcap = (off_t)((ncapid & PCI_CONF_NCAPID_MASK) >> 8);
6093446Smrj 	}
6103446Smrj 
6113446Smrj 	return (nextcap);
6123446Smrj 
6133446Smrj }
6143446Smrj 
6153446Smrj /*
6163446Smrj  * If i8xx device is successfully detected, 0 is returned.
6173446Smrj  * Otherwise -1 is returned.
6183446Smrj  */
6193446Smrj static int
6203446Smrj detect_i8xx_device(agp_master_softc_t *master_softc)
6213446Smrj {
6223446Smrj 
6233446Smrj 	switch (master_softc->agpm_id) {
6243446Smrj 	case INTEL_IGD_810:
6253446Smrj 	case INTEL_IGD_810DC:
6263446Smrj 	case INTEL_IGD_810E:
6273446Smrj 	case INTEL_IGD_815:
6283446Smrj 		master_softc->agpm_dev_type = DEVICE_IS_I810;
6293446Smrj 		break;
6303446Smrj 	case INTEL_IGD_830M:
6313446Smrj 	case INTEL_IGD_845G:
6323446Smrj 	case INTEL_IGD_855GM:
6333446Smrj 	case INTEL_IGD_865G:
6344478Skz151634 	case INTEL_IGD_915:
6354478Skz151634 	case INTEL_IGD_915GM:
6363446Smrj 	case INTEL_IGD_945:
6374303Skz151634 	case INTEL_IGD_945GM:
6388020SMiao.Chen@Sun.COM 	case INTEL_IGD_945GME:
6394478Skz151634 	case INTEL_IGD_946GZ:
6404478Skz151634 	case INTEL_IGD_965G1:
6414478Skz151634 	case INTEL_IGD_965G2:
6424478Skz151634 	case INTEL_IGD_965GM:
6434618Skz151634 	case INTEL_IGD_965GME:
6444478Skz151634 	case INTEL_IGD_965Q:
6455036Skz151634 	case INTEL_IGD_Q35:
6465036Skz151634 	case INTEL_IGD_G33:
6475036Skz151634 	case INTEL_IGD_Q33:
6486778Smc196098 	case INTEL_IGD_GM45:
6497662SMiao.Chen@Sun.COM 	case INTEL_IGD_EL:
6507662SMiao.Chen@Sun.COM 	case INTEL_IGD_Q45:
6517662SMiao.Chen@Sun.COM 	case INTEL_IGD_G45:
652*8832SMiao.Chen@Sun.COM 	case INTEL_IGD_G41:
6533446Smrj 		master_softc->agpm_dev_type = DEVICE_IS_I830;
6543446Smrj 		break;
6553446Smrj 	default:		/* unknown id */
6563446Smrj 		return (-1);
6573446Smrj 	}
6583446Smrj 
6593446Smrj 	return (0);
6603446Smrj }
6613446Smrj 
6623446Smrj /*
6633446Smrj  * If agp master is succssfully detected, 0 is returned.
6643446Smrj  * Otherwise -1 is returned.
6653446Smrj  */
6663446Smrj static int
6673446Smrj detect_agp_devcice(agp_master_softc_t *master_softc,
6683446Smrj     ddi_acc_handle_t acc_handle)
6693446Smrj {
6703446Smrj 	off_t cap;
6713446Smrj 
6723446Smrj 	cap = agpmaster_cap_find(acc_handle);
6733446Smrj 	if (cap) {
6743446Smrj 		master_softc->agpm_dev_type = DEVICE_IS_AGP;
6753446Smrj 		master_softc->agpm_data.agpm_acaptr = cap;
6763446Smrj 		return (0);
6773446Smrj 	} else {
6783446Smrj 		return (-1);
6793446Smrj 	}
6803446Smrj 
6813446Smrj }
6823446Smrj 
6833446Smrj /*
6843446Smrj  * Please refer to GART and GTT entry format table in agpdefs.h for
6853446Smrj  * intel GTT entry format.
6863446Smrj  */
6873446Smrj static int
6883446Smrj phys2entry(uint32_t type, uint32_t physaddr, uint32_t *entry)
6893446Smrj {
6903446Smrj 	uint32_t value;
6913446Smrj 
6923446Smrj 	switch (type) {
6933446Smrj 	case AGP_PHYSICAL:
6943446Smrj 	case AGP_NORMAL:
6953446Smrj 		value = (physaddr & GTT_PTE_MASK) | GTT_PTE_VALID;
6963446Smrj 		break;
6973446Smrj 	default:
6983446Smrj 		return (-1);
6993446Smrj 	}
7003446Smrj 
7013446Smrj 	*entry = value;
7023446Smrj 
7033446Smrj 	return (0);
7043446Smrj }
7053446Smrj 
7063446Smrj static int
7073446Smrj i8xx_add_to_gtt(gtt_impl_t *gtt, igd_gtt_seg_t seg)
7083446Smrj {
7093446Smrj 	int i;
7103446Smrj 	uint32_t *paddr;
7113446Smrj 	uint32_t entry;
7123446Smrj 	uint32_t maxpages;
7133446Smrj 
7143446Smrj 	maxpages = gtt->gtt_info.igd_apersize;
7153446Smrj 	maxpages = GTT_MB_TO_PAGES(maxpages);
7163446Smrj 
7173446Smrj 	paddr = seg.igs_phyaddr;
7183446Smrj 
7193446Smrj 	/* check if gtt max page number is reached */
7203446Smrj 	if ((seg.igs_pgstart + seg.igs_npage) > maxpages)
7213446Smrj 		return (-1);
7223446Smrj 
7233446Smrj 	paddr = seg.igs_phyaddr;
7243446Smrj 	for (i = seg.igs_pgstart; i < (seg.igs_pgstart + seg.igs_npage);
7253446Smrj 	    i++, paddr++) {
7263446Smrj 		if (phys2entry(seg.igs_type, *paddr, &entry))
7273446Smrj 			return (-1);
7285036Skz151634 		ddi_put32(gtt->gtt_handle,
7293446Smrj 		    (uint32_t *)(gtt->gtt_addr + i * sizeof (uint32_t)),
7303446Smrj 		    entry);
7313446Smrj 	}
7323446Smrj 
7333446Smrj 	return (0);
7343446Smrj }
7353446Smrj 
7363446Smrj static void
7373446Smrj i8xx_remove_from_gtt(gtt_impl_t *gtt, igd_gtt_seg_t seg)
7383446Smrj {
7393446Smrj 	int i;
7403446Smrj 	uint32_t maxpages;
7413446Smrj 
7423446Smrj 	maxpages = gtt->gtt_info.igd_apersize;
7433446Smrj 	maxpages = GTT_MB_TO_PAGES(maxpages);
7443446Smrj 
7453446Smrj 	/* check if gtt max page number is reached */
7463446Smrj 	if ((seg.igs_pgstart + seg.igs_npage) > maxpages)
7473446Smrj 		return;
7483446Smrj 
7493446Smrj 	for (i = seg.igs_pgstart; i < (seg.igs_pgstart + seg.igs_npage); i++) {
7505036Skz151634 		ddi_put32(gtt->gtt_handle,
7514478Skz151634 		    (uint32_t *)(gtt->gtt_addr + i * sizeof (uint32_t)), 0);
7523446Smrj 	}
7533446Smrj }
754