13446Smrj /*
2*5295Srandyf  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
33446Smrj  * Use is subject to license terms.
43446Smrj  */
53446Smrj 
63446Smrj #pragma ident	"%Z%%M%	%I%	%E% SMI"
73446Smrj 
83446Smrj #include <sys/conf.h>
93446Smrj #include <sys/ddi.h>
103446Smrj #include <sys/sunddi.h>
113446Smrj #include <sys/modctl.h>
123446Smrj #include <sys/stat.h>
133446Smrj #include <sys/sunldi.h>
143446Smrj #include <sys/file.h>
153446Smrj #include <sys/agpgart.h>
163446Smrj #include <sys/agp/agpdefs.h>
173446Smrj #include <sys/agp/agpamd64gart_io.h>
183446Smrj 
193446Smrj #define	MAX_GART_INSTS		8
203446Smrj #define	GETSOFTC(instance)	((amd64_gart_softstate_t *)	\
213446Smrj     ddi_get_soft_state(amd64_gart_glob_soft_handle, (instance)));
223446Smrj #define	DEV2INST(dev)		(getminor(dev))
233446Smrj #define	INST2NODENUM(inst)	(inst)
243446Smrj 
253446Smrj int amd64_debug_var = 0;
263446Smrj #define	AMD64DB_PRINT1(fmt)	if (amd64_debug_var == 1) cmn_err fmt
273446Smrj #define	AMD64DB_PRINT2(fmt)	if (amd64_debug_var >= 1) cmn_err fmt
283446Smrj 
293446Smrj typedef struct amd64_gart_softstate {
303446Smrj 	dev_info_t		*gsoft_dip;
313446Smrj 	ddi_acc_handle_t	gsoft_pcihdl;
323446Smrj 	kmutex_t		gsoft_lock;
333446Smrj }amd64_gart_softstate_t;
343446Smrj 
353446Smrj static void *amd64_gart_glob_soft_handle;
363446Smrj 
373446Smrj static uint64_t
383446Smrj amd64_get_aperbase(amd64_gart_softstate_t *sc)
393446Smrj {
403446Smrj 	uint32_t	value;
413446Smrj 	uint64_t	aper_base;
423446Smrj 
433446Smrj 	/* amd64 aperture base support 40 bits and 32M aligned */
443446Smrj 	value = pci_config_get32(sc->gsoft_pcihdl,
453446Smrj 	    AMD64_APERTURE_BASE) & AMD64_APERBASE_MASK;
463446Smrj 	aper_base = (uint64_t)value << AMD64_APERBASE_SHIFT;
473446Smrj 	return (aper_base);
483446Smrj }
493446Smrj 
503446Smrj static size_t
513446Smrj amd64_get_apersize(amd64_gart_softstate_t *sc)
523446Smrj {
533446Smrj 	uint32_t	value;
543446Smrj 	size_t		size;
553446Smrj 
563446Smrj 	value = pci_config_get32(sc->gsoft_pcihdl, AMD64_APERTURE_CONTROL);
573446Smrj 
583446Smrj 	value = (value & AMD64_APERSIZE_MASK) >> 1;
593446Smrj 
603446Smrj 	/* aper size = 2^value x 32 */
613446Smrj 	switch (value) {
623446Smrj 		case  0x0:
633446Smrj 			size = 32;
643446Smrj 			break;
653446Smrj 		case  0x1:
663446Smrj 			size = 64;
673446Smrj 			break;
683446Smrj 		case  0x2:
693446Smrj 			size = 128;
703446Smrj 			break;
713446Smrj 		case  0x3:
723446Smrj 			size = 256;
733446Smrj 			break;
743446Smrj 		case  0x4:
753446Smrj 			size = 512;
763446Smrj 			break;
773446Smrj 		case  0x5:
783446Smrj 			size = 1024;
793446Smrj 			break;
803446Smrj 		case  0x6:
813446Smrj 			size = 2048;
823446Smrj 			break;
833446Smrj 		default:		/* reserved */
843446Smrj 			size = 0;
853446Smrj 	};
863446Smrj 
873446Smrj 	return (size);
883446Smrj }
893446Smrj 
903446Smrj static void
913446Smrj amd64_invalidate_gtlb(amd64_gart_softstate_t *sc)
923446Smrj {
933446Smrj 	uint32_t value;
943446Smrj 
953446Smrj 	value = pci_config_get32(sc->gsoft_pcihdl, AMD64_GART_CACHE_CTL);
963446Smrj 	value |= AMD64_INVALID_CACHE;
973446Smrj 
983446Smrj 	pci_config_put32(sc->gsoft_pcihdl, AMD64_GART_CACHE_CTL, value);
993446Smrj }
1003446Smrj 
1013446Smrj static void
1023446Smrj amd64_enable_gart(amd64_gart_softstate_t *sc, int enable)
1033446Smrj {
1043446Smrj 	uint32_t aper_ctl;
1053446Smrj 	uint32_t aper_base;
1063446Smrj 	uint32_t gart_ctl;
1073446Smrj 	uint32_t gart_base;
1083446Smrj 
1093446Smrj 	aper_ctl = pci_config_get32(sc->gsoft_pcihdl, AMD64_APERTURE_CONTROL);
1103446Smrj 	AMD64DB_PRINT1((CE_NOTE, "before: aper_ctl = %x", aper_ctl));
1113446Smrj 	aper_base = pci_config_get32(sc->gsoft_pcihdl, AMD64_APERTURE_BASE);
1123446Smrj 	gart_ctl = pci_config_get32(sc->gsoft_pcihdl, AMD64_GART_CACHE_CTL);
1133446Smrj 	gart_base = pci_config_get32(sc->gsoft_pcihdl, AMD64_GART_BASE);
1143446Smrj #ifdef lint
1153446Smrj 	aper_base = aper_base;
1163446Smrj 	gart_ctl = gart_ctl;
1173446Smrj 	gart_base = gart_base;
1183446Smrj #endif /* lint */
1193446Smrj 	AMD64DB_PRINT1((CE_NOTE, "before: aper_base = %x", aper_base));
1203446Smrj 	AMD64DB_PRINT1((CE_NOTE, "before: gart_ctl = %x", gart_ctl));
1213446Smrj 	AMD64DB_PRINT1((CE_NOTE, "before: gart_base = %x", gart_base));
1223446Smrj 	if (enable) {
1233446Smrj 		aper_ctl |= AMD64_GARTEN;
1243446Smrj 		aper_ctl &= ~(AMD64_DISGARTCPU | AMD64_DISGARTIO);
1253446Smrj 	} else
1263446Smrj 		aper_ctl &= (~AMD64_GARTEN);
1273446Smrj 
1283446Smrj 	pci_config_put32(sc->gsoft_pcihdl, AMD64_APERTURE_CONTROL, aper_ctl);
1293446Smrj }
1303446Smrj 
1313446Smrj /*ARGSUSED*/
1323446Smrj static int
1333446Smrj amd64_gart_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
1343446Smrj     void *arg, void **resultp)
1353446Smrj {
1363446Smrj 	amd64_gart_softstate_t *st;
1373446Smrj 	int instance, rval = DDI_FAILURE;
1383446Smrj 	dev_t dev;
1393446Smrj 
1403446Smrj 	switch (cmd) {
1413446Smrj 	case DDI_INFO_DEVT2DEVINFO:
1423446Smrj 		dev = (dev_t)arg;
1433446Smrj 		instance = DEV2INST(dev);
1443446Smrj 		st = ddi_get_soft_state(amd64_gart_glob_soft_handle, instance);
1453446Smrj 		if (st != NULL) {
1463446Smrj 			mutex_enter(&st->gsoft_lock);
1473446Smrj 			*resultp = st->gsoft_dip;
1483446Smrj 			mutex_exit(&st->gsoft_lock);
1493446Smrj 			rval = DDI_SUCCESS;
1503446Smrj 		} else {
1513446Smrj 			*resultp = NULL;
1523446Smrj 		}
1533446Smrj 
1543446Smrj 		break;
1553446Smrj 	case DDI_INFO_DEVT2INSTANCE:
1563446Smrj 		dev = (dev_t)arg;
1573446Smrj 		instance = DEV2INST(dev);
1583446Smrj 		*resultp = (void *)(uintptr_t)instance;
1593446Smrj 		rval = DDI_SUCCESS;
1603446Smrj 		break;
1613446Smrj 	default:
1623446Smrj 		break;
1633446Smrj 	}
1643446Smrj 
1653446Smrj 	return (rval);
1663446Smrj }
1673446Smrj 
1683446Smrj static int
1693446Smrj amd64_gart_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1703446Smrj {
1713446Smrj 	int			instance;
1723446Smrj 	amd64_gart_softstate_t	*sc;
1733446Smrj 	int			status;
1743446Smrj 	char			buf[80];
1753446Smrj 
176*5295Srandyf 	switch (cmd) {
177*5295Srandyf 	default:
1783446Smrj 		return (DDI_FAILURE);
1793446Smrj 
180*5295Srandyf 	case DDI_RESUME:
181*5295Srandyf 		/* Nothing special is needed for resume. */
182*5295Srandyf 		return (DDI_SUCCESS);
183*5295Srandyf 
184*5295Srandyf 	case DDI_ATTACH:
185*5295Srandyf 		break;
186*5295Srandyf 	}
187*5295Srandyf 
1883446Smrj 	instance = ddi_get_instance(dip);
1893446Smrj 
1903446Smrj 	if (ddi_soft_state_zalloc(amd64_gart_glob_soft_handle, instance) !=
1913446Smrj 	    DDI_SUCCESS)
1923446Smrj 		return (DDI_FAILURE);
1933446Smrj 
1943446Smrj 	sc = ddi_get_soft_state(amd64_gart_glob_soft_handle, instance);
1953446Smrj 	mutex_init(&sc->gsoft_lock, NULL, MUTEX_DRIVER, NULL);
1963446Smrj 	sc->gsoft_dip = dip;
1973446Smrj 	status = pci_config_setup(dip, &sc->gsoft_pcihdl);
1983446Smrj 	if (status != DDI_SUCCESS) {
1993446Smrj 		ddi_soft_state_free(amd64_gart_glob_soft_handle, instance);
2003446Smrj 		return (DDI_FAILURE);
2013446Smrj 	}
2023446Smrj 	(void) sprintf(buf, "%s-%d", AMD64GART_NAME, instance);
2033446Smrj 	status = ddi_create_minor_node(dip, buf, S_IFCHR,
2043446Smrj 	    INST2NODENUM(instance), DDI_NT_AGP_CPUGART, 0);
2053446Smrj 	if (status != DDI_SUCCESS) {
2063446Smrj 		pci_config_teardown(&sc->gsoft_pcihdl);
2073446Smrj 		ddi_soft_state_free(amd64_gart_glob_soft_handle, instance);
2083446Smrj 		return (DDI_FAILURE);
2093446Smrj 	}
2103446Smrj 	return (DDI_SUCCESS);
2113446Smrj }
2123446Smrj 
2133446Smrj /*ARGSUSED*/
2143446Smrj static int
2153446Smrj amd64_gart_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2163446Smrj {
2173446Smrj 	int			instance;
2183446Smrj 	amd64_gart_softstate_t	*sc;
2193446Smrj 	char			buf[80];
2203446Smrj 
221*5295Srandyf 	switch (cmd) {
222*5295Srandyf 	default:
2233446Smrj 		return (DDI_FAILURE);
2243446Smrj 
225*5295Srandyf 	case DDI_SUSPEND:
226*5295Srandyf 		/* Nothing special is needed for suspend */
227*5295Srandyf 		return (DDI_SUCCESS);
228*5295Srandyf 
229*5295Srandyf 	case DDI_DETACH:
230*5295Srandyf 		break;
231*5295Srandyf 	}
232*5295Srandyf 
2333446Smrj 	instance = ddi_get_instance(dip);
2343446Smrj 	sc = ddi_get_soft_state(amd64_gart_glob_soft_handle, instance);
2353446Smrj 
2363446Smrj 	(void) sprintf(buf, "%s-%d", AMD64GART_NAME, instance);
2373446Smrj 	ddi_remove_minor_node(dip, buf);
2383446Smrj 	pci_config_teardown(&sc->gsoft_pcihdl);
2393446Smrj 	mutex_destroy(&sc->gsoft_lock);
2403446Smrj 	ddi_soft_state_free(amd64_gart_glob_soft_handle, instance);
2413446Smrj 
2423446Smrj 	return (DDI_SUCCESS);
2433446Smrj }
2443446Smrj 
2453446Smrj /*ARGSUSED*/
2463446Smrj static int
2473446Smrj amd64_gart_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
2483446Smrj     cred_t *cred, int *rval)
2493446Smrj {
2503446Smrj 	int instance;
2513446Smrj 	amd64_gart_softstate_t *sc;
2523446Smrj 	static char kernel_only[] =
2533446Smrj 	    "amd64_gart_ioctl: is a kernel only ioctl";
2543446Smrj 
2553446Smrj 	if (!(mode & FKIOCTL)) {
2563446Smrj 		AMD64DB_PRINT2((CE_CONT, kernel_only));
2573446Smrj 		return (ENXIO);
2583446Smrj 	}
2593446Smrj 	instance = DEV2INST(dev);
2603446Smrj 	sc = GETSOFTC(instance);
2613446Smrj 
2623446Smrj 	if (sc == NULL)
2633446Smrj 		return (ENXIO);
2643446Smrj 	mutex_enter(&sc->gsoft_lock);
2653446Smrj 
2663446Smrj 	switch (cmd) {
2673446Smrj 	case AMD64_GET_INFO:
2683446Smrj 	{
2693446Smrj 		amdgart_info_t info;
2703446Smrj 
2713446Smrj 		info.cgart_aperbase = amd64_get_aperbase(sc);
2723446Smrj 		info.cgart_apersize = amd64_get_apersize(sc);
2733446Smrj 
2743446Smrj 		if (ddi_copyout(&info, (void *)data,
2753446Smrj 		    sizeof (amdgart_info_t), mode)) {
2763446Smrj 			mutex_exit(&sc->gsoft_lock);
2773446Smrj 			return (EFAULT);
2783446Smrj 		}
2793446Smrj 		break;
2803446Smrj 	}
2813446Smrj 	case AMD64_SET_GART_ADDR:
2823446Smrj 	{
2833446Smrj 		uint32_t addr;
2843446Smrj 
2853446Smrj 		if (ddi_copyin((void *)data, &addr, sizeof (uint32_t), mode)) {
2863446Smrj 			mutex_exit(&sc->gsoft_lock);
2873446Smrj 			return (EFAULT);
2883446Smrj 		}
2893446Smrj 
2903446Smrj 		pci_config_put32(sc->gsoft_pcihdl, AMD64_GART_BASE, addr);
2913446Smrj 		amd64_enable_gart(sc, 1);
2923446Smrj 
2933446Smrj 		break;
2943446Smrj 	}
2953446Smrj 	case AMD64_FLUSH_GTLB:
2963446Smrj 	{
2973446Smrj 		amd64_invalidate_gtlb(sc);
2983446Smrj 
2993446Smrj 		break;
3003446Smrj 	}
3013446Smrj 	case AMD64_CONFIGURE:
3023446Smrj 	{
3033446Smrj 		/* reserved */
3043446Smrj 		break;
3053446Smrj 	}
3063446Smrj 	case AMD64_UNCONFIG:
3073446Smrj 	{
3083446Smrj 		amd64_enable_gart(sc, 0);
3093446Smrj 		pci_config_put32(sc->gsoft_pcihdl, AMD64_GART_BASE, 0x00000000);
3103446Smrj 
3113446Smrj 		break;
3123446Smrj 	}
3133446Smrj 	default:
3143446Smrj 		mutex_exit(&sc->gsoft_lock);
3153446Smrj 		return (ENXIO);
3163446Smrj 
3173446Smrj 	}
3183446Smrj 
3193446Smrj 	mutex_exit(&sc->gsoft_lock);
3203446Smrj 
3213446Smrj 	return (0);
3223446Smrj }
3233446Smrj 
3243446Smrj /*ARGSUSED*/
3253446Smrj static int
3263446Smrj amd64_gart_open(dev_t *dev, int flag, int otyp, cred_t *cred)
3273446Smrj {
3283446Smrj 	int			instance;
3293446Smrj 	amd64_gart_softstate_t	*sc;
3303446Smrj 
3313446Smrj 	if (!(flag & FKLYR))
3323446Smrj 		return (ENXIO);
3333446Smrj 
3343446Smrj 	instance = DEV2INST(*dev);
3353446Smrj 	sc = GETSOFTC(instance);
3363446Smrj 
3373446Smrj 	if (sc == NULL)
3383446Smrj 		return (ENXIO);
3393446Smrj 
3403446Smrj 	return (0);
3413446Smrj }
3423446Smrj 
3433446Smrj /*ARGSUSED*/
3443446Smrj static int
3453446Smrj amd64_gart_close(dev_t dev, int flag, int otyp, cred_t *cred)
3463446Smrj {
3473446Smrj 	int			instance;
3483446Smrj 	amd64_gart_softstate_t	*sc;
3493446Smrj 
3503446Smrj 	instance = DEV2INST(dev);
3513446Smrj 	sc = GETSOFTC(instance);
3523446Smrj 
3533446Smrj 	if (sc == NULL)
3543446Smrj 		return (ENXIO);
3553446Smrj 
3563446Smrj 	return (0);
3573446Smrj }
3583446Smrj 
3593446Smrj static  struct  cb_ops  amd64_gart_cb_ops = {
3603446Smrj 	amd64_gart_open,	/* cb_open() */
3613446Smrj 	amd64_gart_close,	/* cb_close() */
3623446Smrj 	nodev,			/* cb_strategy() */
3633446Smrj 	nodev,			/* cb_print */
3643446Smrj 	nodev,			/* cb_dump */
3653446Smrj 	nodev,			/* cb_read() */
3663446Smrj 	nodev,			/* cb_write() */
3673446Smrj 	amd64_gart_ioctl,	/* cb_ioctl */
3683446Smrj 	nodev,			/* cb_devmap */
3693446Smrj 	nodev,			/* cb_mmap */
3703446Smrj 	nodev,			/* cb_segmap */
3713446Smrj 	nochpoll,		/* cb_chpoll */
3723446Smrj 	ddi_prop_op,		/* cb_prop_op */
3733446Smrj 	0,			/* cb_stream */
3743446Smrj 	D_NEW | D_MP,		/* cb_flag */
3753446Smrj 	CB_REV,			/* cb_ops version? */
3763446Smrj 	nodev,			/* cb_aread() */
3773446Smrj 	nodev,			/* cb_awrite() */
3783446Smrj };
3793446Smrj 
3803446Smrj /* device operations */
3813446Smrj static struct dev_ops amd64_gart_ops = {
3823446Smrj 	DEVO_REV,		/* devo_rev */
3833446Smrj 	0,			/* devo_refcnt */
3843446Smrj 	amd64_gart_getinfo,	/* devo_getinfo */
3853446Smrj 	nulldev,		/* devo_identify */
3863446Smrj 	nulldev,		/* devo_probe */
3873446Smrj 	amd64_gart_attach,	/* devo_attach */
3883446Smrj 	amd64_gart_detach,	/* devo_detach */
3893446Smrj 	nodev,			/* devo_reset */
3903446Smrj 	&amd64_gart_cb_ops,	/* devo_cb_ops */
3913446Smrj 	0,			/* devo_bus_ops */
3923446Smrj 	0,			/* devo_power */
3933446Smrj };
3943446Smrj 
3953446Smrj static  struct modldrv modldrv = {
3963446Smrj 	&mod_driverops,
3973446Smrj 	"AGP AMD gart driver v%I%",
3983446Smrj 	&amd64_gart_ops,
3993446Smrj };
4003446Smrj 
4013446Smrj static  struct modlinkage modlinkage = {
4023446Smrj 	MODREV_1,		/* MODREV_1 is indicated by manual */
4033446Smrj 	&modldrv,
4043446Smrj 	NULL
4053446Smrj };
4063446Smrj 
4073446Smrj 
4083446Smrj int
4093446Smrj _init(void)
4103446Smrj {
4113446Smrj 	int ret = DDI_SUCCESS;
4123446Smrj 
4133446Smrj 	ret = ddi_soft_state_init(&amd64_gart_glob_soft_handle,
4143446Smrj 	    sizeof (amd64_gart_softstate_t),
4153446Smrj 	    MAX_GART_INSTS);
4163446Smrj 
4173446Smrj 	if (ret)
4183446Smrj 		return (ret);
4193446Smrj 	if ((ret = mod_install(&modlinkage)) != 0) {
4203446Smrj 		ddi_soft_state_fini(&amd64_gart_glob_soft_handle);
4213446Smrj 		return (ret);
4223446Smrj 	}
4233446Smrj 	return (DDI_SUCCESS);
4243446Smrj }
4253446Smrj 
4263446Smrj int
4273446Smrj _info(struct  modinfo *modinfop)
4283446Smrj {
4293446Smrj 	return (mod_info(&modlinkage, modinfop));
4303446Smrj }
4313446Smrj 
4323446Smrj int
4333446Smrj _fini(void)
4343446Smrj {
4353446Smrj 	int ret;
4363446Smrj 	if ((ret = mod_remove(&modlinkage)) == 0) {
4373446Smrj 		ddi_soft_state_fini(&amd64_gart_glob_soft_handle);
4383446Smrj 	}
4393446Smrj 	return (ret);
4403446Smrj }
441