Home | History | Annotate | Download | only in ppm
      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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 
     27 #include <sys/types.h>
     28 #include <sys/conf.h>
     29 #include <sys/open.h>
     30 #include <sys/modctl.h>
     31 #include <sys/promif.h>
     32 #include <sys/stat.h>
     33 #include <sys/ddi_impldefs.h>
     34 #include <sys/jbusppm.h>
     35 #include <sys/ddi.h>
     36 #include <sys/sunddi.h>
     37 
     38 /*
     39  *	JBus Power Management Driver
     40  *
     41  *	jbusppm driver initiates the JBus clock speed change
     42  *	as part of the protocol to adjust the clock speed on
     43  *	all JBus resident devices.
     44  *
     45  *	jbusppm driver is loaded because of the explicit dependency
     46  *	defined in PPM driver.
     47  */
     48 
     49 /*
     50  * Configuration Function prototypes and data structures
     51  */
     52 static int	jbppm_attach(dev_info_t *, ddi_attach_cmd_t);
     53 static int	jbppm_detach(dev_info_t *, ddi_detach_cmd_t);
     54 static int	jbppm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
     55 static int	jbppm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p);
     56 static int	jbppm_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
     57 static int	jbppm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
     58 
     59 /*
     60  * Configuration data structures
     61  */
     62 static struct cb_ops jbppm_cbops = {
     63 	jbppm_open,		/* open */
     64 	jbppm_close,		/* close */
     65 	nodev,			/* strategy */
     66 	nodev,			/* print */
     67 	nodev,			/* dump */
     68 	nodev,			/* read */
     69 	nodev,			/* write */
     70 	jbppm_ioctl,		/* ioctl */
     71 	nodev,			/* devmap */
     72 	nodev,			/* mmap */
     73 	nodev,			/* segmap */
     74 	nochpoll,		/* chpoll */
     75 	ddi_prop_op,		/* prop_op */
     76 	NULL,			/* stream */
     77 	D_MP | D_NEW,		/* flag */
     78 	CB_REV,			/* rev */
     79 	nodev,			/* aread */
     80 	nodev,			/* awrite */
     81 };
     82 
     83 static struct dev_ops jbppm_ops = {
     84 	DEVO_REV,		/* devo_rev */
     85 	0,			/* refcnt */
     86 	jbppm_getinfo,		/* getinfo */
     87 	nulldev,		/* identify */
     88 	nulldev,		/* probe */
     89 	jbppm_attach,		/* attach */
     90 	jbppm_detach,		/* detach */
     91 	nodev,			/* reset */
     92 	&jbppm_cbops,		/* cb_ops */
     93 	NULL,			/* bus_ops */
     94 	NULL,			/* power */
     95 	ddi_quiesce_not_supported,	/* devo_quiesce */
     96 };
     97 
     98 extern struct mod_ops mod_driverops;
     99 
    100 static struct modldrv modldrv = {
    101 	&mod_driverops,
    102 	"JBus ppm driver",
    103 	&jbppm_ops,
    104 };
    105 
    106 static struct modlinkage modlinkage = {
    107 	MODREV_1,
    108 	&modldrv,
    109 	NULL
    110 };
    111 
    112 /*
    113  * Local functions
    114  */
    115 static void jbppm_next_speed(dev_info_t *, uint_t);
    116 static int jbppm_start_next(dev_info_t *, int);
    117 
    118 /*
    119  * Driver global variables
    120  *
    121  * jbppm_lock synchronize the access of lyr handle to each jbppm
    122  * minor device, therefore write to tomatillo device is
    123  * sequentialized.  Lyr protocol requires pairing up lyr open
    124  * and close, so only a single reference is allowed per minor node.
    125  */
    126 static void	*jbppm_statep;
    127 static kmutex_t  jbppm_lock;
    128 
    129 /*
    130  * bit masks to scale the IO bridge clock in sync with and only with
    131  * scaling CPU clock.
    132  *
    133  * The array index indicates power level (from lowest to highest).
    134  */
    135 static const uint64_t jbus_clock_masks[] = {
    136 	JBUS_ESTAR_CNTL_32,
    137 	JBUS_ESTAR_CNTL_2,
    138 	JBUS_ESTAR_CNTL_1
    139 };
    140 
    141 int
    142 _init(void)
    143 {
    144 	int	error;
    145 
    146 	if ((error = ddi_soft_state_init(&jbppm_statep,
    147 	    sizeof (jbppm_unit), 0)) != DDI_SUCCESS) {
    148 		return (error);
    149 	}
    150 
    151 	mutex_init(&jbppm_lock, NULL, MUTEX_DRIVER, NULL);
    152 
    153 	if ((error = mod_install(&modlinkage)) != DDI_SUCCESS) {
    154 		mutex_destroy(&jbppm_lock);
    155 		ddi_soft_state_fini(&jbppm_statep);
    156 		return (error);
    157 	}
    158 
    159 	return (error);
    160 }
    161 
    162 int
    163 _fini(void)
    164 {
    165 	int	error;
    166 
    167 	if ((error = mod_remove(&modlinkage)) == DDI_SUCCESS) {
    168 		mutex_destroy(&jbppm_lock);
    169 		ddi_soft_state_fini(&jbppm_statep);
    170 	}
    171 
    172 	return (error);
    173 
    174 }
    175 
    176 int
    177 _info(struct modinfo *modinfop)
    178 {
    179 	return (mod_info(&modlinkage, modinfop));
    180 }
    181 
    182 
    183 
    184 /*
    185  * Driver attach(9e) entry point
    186  */
    187 static int
    188 jbppm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
    189 {
    190 	char	*str = "jbppm_attach";
    191 	int		instance;
    192 	jbppm_unit	*unitp;
    193 	uint64_t	data64;
    194 	ddi_device_acc_attr_t	attr;
    195 	int		rv = DDI_SUCCESS;
    196 
    197 	switch (cmd) {
    198 	case DDI_ATTACH:
    199 		break;
    200 	case DDI_RESUME:
    201 		return (DDI_SUCCESS);
    202 	default:
    203 		cmn_err(CE_WARN, "%s: cmd %d unsupported.\n", str, cmd);
    204 		return (DDI_FAILURE);
    205 	}
    206 
    207 	instance = ddi_get_instance(dip);
    208 	rv = ddi_soft_state_zalloc(jbppm_statep, instance);
    209 	if (rv != DDI_SUCCESS) {
    210 		cmn_err(CE_WARN, "%s: failed alloc for dev(%s@%s)",
    211 		    str, ddi_binding_name(dip),
    212 		    ddi_get_name_addr(dip) ? ddi_get_name_addr(dip) : " ");
    213 		return (rv);
    214 	}
    215 
    216 	if ((unitp = ddi_get_soft_state(jbppm_statep, instance)) == NULL) {
    217 		rv = DDI_FAILURE;
    218 		goto doerrs;
    219 	}
    220 
    221 	/*
    222 	 * Export "ddi-kernel-ioctl" property - prepared to support
    223 	 * kernel ioctls (driver layering).
    224 	 */
    225 	rv = ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
    226 	    DDI_KERNEL_IOCTL, NULL, 0);
    227 	if (rv != DDI_PROP_SUCCESS)
    228 		goto doerrs;
    229 
    230 	ddi_report_dev(dip);
    231 	unitp->dip = dip;
    232 
    233 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
    234 	attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
    235 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
    236 
    237 	rv = ddi_regs_map_setup(dip, 0, (caddr_t *)&unitp->devid_csr, 0, 8,
    238 	    &attr, &unitp->devid_hndl);
    239 	if (rv != DDI_SUCCESS)
    240 		goto doerrs;
    241 
    242 	rv = ddi_regs_map_setup(dip, 1, (caddr_t *)&unitp->estar_csr, 0, 16,
    243 	    &attr, &unitp->estar_hndl);
    244 	if (rv != DDI_SUCCESS)
    245 		goto doerrs;
    246 	unitp->j_chng_csr = (uint64_t *)((caddr_t)unitp->estar_csr +
    247 	    J_CHNG_INITIATION_OFFSET);
    248 
    249 	data64 = ddi_get64(unitp->devid_hndl, (uint64_t *)unitp->devid_csr);
    250 	unitp->is_master = (data64 & MASTER_IOBRIDGE_BIT) ? 1 : 0;
    251 	unitp->lyropen = 0;
    252 
    253 	/*
    254 	 * create minor node for kernel_ioctl calls
    255 	 */
    256 	rv = ddi_create_minor_node(dip, "jbus-ppm", S_IFCHR, instance, 0, 0);
    257 	if (rv != DDI_SUCCESS)
    258 		goto doerrs;
    259 
    260 	return (rv);
    261 
    262 doerrs:
    263 	if (unitp->devid_hndl != NULL)
    264 		ddi_regs_map_free(&unitp->devid_hndl);
    265 
    266 	if (unitp->estar_csr != NULL)
    267 		ddi_regs_map_free(&unitp->estar_hndl);
    268 
    269 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS |
    270 	    DDI_PROP_NOTPROM, DDI_KERNEL_IOCTL))
    271 		ddi_prop_remove_all(dip);
    272 
    273 	ddi_soft_state_free(jbppm_statep, instance);
    274 
    275 	return (rv);
    276 }
    277 
    278 
    279 /*
    280  * Driver getinfo(9e) entry routine
    281  */
    282 /* ARGSUSED */
    283 static int
    284 jbppm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
    285 {
    286 	jbppm_unit	*unitp;
    287 	int		instance;
    288 
    289 	switch (cmd) {
    290 	case DDI_INFO_DEVT2DEVINFO:
    291 		instance = getminor((dev_t)arg);
    292 		unitp = ddi_get_soft_state(jbppm_statep, instance);
    293 		if (unitp == NULL) {
    294 			return (DDI_FAILURE);
    295 		}
    296 		*result = (void *) unitp->dip;
    297 		return (DDI_SUCCESS);
    298 
    299 	case DDI_INFO_DEVT2INSTANCE:
    300 		instance = getminor((dev_t)arg);
    301 		*result = (void *)(uintptr_t)instance;
    302 		return (DDI_SUCCESS);
    303 
    304 	default:
    305 		return (DDI_FAILURE);
    306 	}
    307 }
    308 
    309 
    310 /*
    311  * detach(9e)
    312  */
    313 /* ARGSUSED */
    314 static int
    315 jbppm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
    316 {
    317 	char *str = "jbppm_detach";
    318 
    319 	switch (cmd) {
    320 	case DDI_DETACH:
    321 		return (DDI_FAILURE);
    322 	case DDI_SUSPEND:
    323 		return (DDI_SUCCESS);
    324 	default:
    325 		cmn_err(CE_WARN, "%s: cmd %d unsupported", str, cmd);
    326 		return (DDI_FAILURE);
    327 	}
    328 }
    329 
    330 
    331 /* ARGSUSED */
    332 static int
    333 jbppm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
    334 {
    335 	jbppm_unit	*unitp;
    336 
    337 	/* not intended to allow sysadmin level root process to open it */
    338 	if (drv_priv(cred_p) != DDI_SUCCESS)
    339 		return (EPERM);
    340 
    341 	if ((unitp = ddi_get_soft_state(
    342 	    jbppm_statep, getminor(*dev_p))) == NULL) {
    343 		cmn_err(CE_WARN, "jbppm_open: failed to get soft state!");
    344 		return (DDI_FAILURE);
    345 	}
    346 
    347 	mutex_enter(&jbppm_lock);
    348 	if (unitp->lyropen != 0) {
    349 		mutex_exit(&jbppm_lock);
    350 		return (EBUSY);
    351 	}
    352 	unitp->lyropen++;
    353 	mutex_exit(&jbppm_lock);
    354 
    355 	return (DDI_SUCCESS);
    356 }
    357 
    358 
    359 /* ARGSUSED */
    360 static int
    361 jbppm_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
    362 {
    363 	jbppm_unit	*unitp;
    364 
    365 	if ((unitp =
    366 	    ddi_get_soft_state(jbppm_statep, getminor(dev))) == NULL)
    367 		return (DDI_FAILURE);
    368 
    369 	mutex_enter(&jbppm_lock);
    370 	unitp->lyropen = 0;
    371 	mutex_exit(&jbppm_lock);
    372 
    373 	return (DDI_SUCCESS);
    374 }
    375 
    376 
    377 #define	JBPPMIOC		('j' << 8)
    378 #define	JBPPMIOC_ISMASTER	(JBPPMIOC | 1)	/* no 'arg' */
    379 #define	JBPPMIOC_NEXT		(JBPPMIOC | 2)	/* 'arg': next speed level */
    380 #define	JBPPMIOC_GO		(JBPPMIOC | 3)	/* 'arg': jbus chng_delay */
    381 
    382 /* ARGSUSED3 */
    383 static int
    384 jbppm_ioctl(dev_t dev, int cmd, intptr_t arg, int flag,
    385     cred_t *cred_p, int *rval_p)
    386 {
    387 	jbppm_unit	*unitp;
    388 
    389 	if (drv_priv(cred_p) != 0)
    390 		return (EPERM);
    391 
    392 	if ((unitp =
    393 	    ddi_get_soft_state(jbppm_statep, getminor(dev))) == NULL)
    394 		return (EIO);
    395 
    396 	switch (cmd) {
    397 	case JBPPMIOC_ISMASTER:
    398 		if (unitp->is_master)
    399 			return (0);
    400 		else
    401 			return (-1);
    402 
    403 	case  JBPPMIOC_NEXT:
    404 		jbppm_next_speed(unitp->dip, (uint_t)arg);
    405 		return (0);
    406 
    407 	case  JBPPMIOC_GO:
    408 		if (!unitp->is_master)
    409 			return (EINVAL);
    410 		return (jbppm_start_next(unitp->dip, (int)arg));
    411 
    412 	default:
    413 		return (ENOTTY);
    414 	}
    415 }
    416 
    417 
    418 /*
    419  * jbppm_next_speed - program a new speed into IO bridge device prior to
    420  * actual speed transition.
    421  */
    422 static void
    423 jbppm_next_speed(dev_info_t *dip, uint_t lvl_index)
    424 {
    425 	volatile uint64_t	data64;
    426 	static jbppm_unit	*unitp;
    427 
    428 	unitp = ddi_get_soft_state(jbppm_statep, ddi_get_instance(dip));
    429 	ASSERT(unitp);
    430 
    431 	mutex_enter(&jbppm_lock);
    432 	data64 = ddi_get64(unitp->estar_hndl, unitp->estar_csr);
    433 	data64 &= ~JBUS_ESTAR_CNTL_MASK;
    434 	data64 |= jbus_clock_masks[lvl_index];
    435 
    436 	ddi_put64(unitp->estar_hndl, (uint64_t *)unitp->estar_csr, data64);
    437 	data64 = ddi_get64(unitp->estar_hndl, unitp->estar_csr);
    438 	mutex_exit(&jbppm_lock);
    439 }
    440 
    441 
    442 /*
    443  * jbppm_start_next - Initiate JBus speed change on all JBus devices.
    444  * chng_delay indicates after master deassert j_chng signal the number of
    445  * jbus clock delay before all jbus device start to transit to the new
    446  * speed.
    447  * Trigger sequence:
    448  *      wait while j_chng[1:0] == 10
    449  *	write 00 to j_chng
    450  *	trigger by writing 10 to j_chng[1:0]
    451  *	wait while j_chng[1:0] == 10
    452  *	write 00 to j_chng[1:0]
    453  * Note: this sequence is not the same as Enchilada spec described, chiefly
    454  * because else where (e.g. flush E$ code) may have speed change code. If sw
    455  * wait upon j_chng[1:0] == 11 in both places, we'll have problem.  That spec
    456  * requires wait on 11 to ensure that trigger has completed. An alternative
    457  * way to ensure that is to check and wait upon 10. J_chng[1:0] stays as 10
    458  * for only a short period of time that is under HW control, unlike 11 signals
    459  * which has to be cleared by sw.
    460  */
    461 /* ARGSUSED */
    462 static int
    463 jbppm_start_next(dev_info_t *dip, int chng_delay)
    464 {
    465 	volatile uint64_t	data64;
    466 	static jbppm_unit	*unitp;
    467 
    468 	unitp = ddi_get_soft_state(jbppm_statep, ddi_get_instance(dip));
    469 	ASSERT(unitp && unitp->is_master);
    470 
    471 	mutex_enter(&jbppm_lock);
    472 
    473 	/* wait while trigger is incomplete */
    474 	do {
    475 		data64 = ddi_get64(unitp->estar_hndl, unitp->j_chng_csr);
    476 	} while ((J_CHNG_INITIATION_MASK & data64) == J_CHNG_START);
    477 
    478 	/* clear(reset) */
    479 	data64 &= ~J_CHNG_INITIATION_MASK;
    480 	ddi_put64(unitp->estar_hndl, (uint64_t *)unitp->j_chng_csr, data64);
    481 
    482 	/* trigger */
    483 	data64 &= ~J_CHNG_DELAY_MASK;
    484 	data64 |= (J_CHNG_START | chng_delay);
    485 	ddi_put64(unitp->estar_hndl, (uint64_t *)unitp->j_chng_csr, data64);
    486 
    487 	/* wait while trigger is incomplete */
    488 	do {
    489 		data64 = ddi_get64(unitp->estar_hndl, unitp->j_chng_csr);
    490 	} while ((J_CHNG_INITIATION_MASK & data64) == J_CHNG_START);
    491 
    492 	/* clear(reset) */
    493 	data64 &= ~J_CHNG_INITIATION_MASK;
    494 	ddi_put64(unitp->estar_hndl, (uint64_t *)unitp->j_chng_csr, data64);
    495 	(void) ddi_get64(unitp->estar_hndl, unitp->j_chng_csr);
    496 
    497 	mutex_exit(&jbppm_lock);
    498 	return (DDI_SUCCESS);
    499 }
    500