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