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 /* 30 * Schizo Power Management Driver 31 * 32 * This driver deals with Safari bus interface and it is used 33 * as part of the protocol to change the clock speed on Safari bus. 34 * 35 * The routine on this driver is referenced by Platform Power 36 * Management driver of systems like Excalibur. Driver is 37 * loaded because of an explicit dependency defined in PPM driver. 38 * PPM driver also attaches the driver. 39 */ 40 41 #include <sys/types.h> 42 #include <sys/conf.h> 43 #include <sys/ddi.h> 44 #include <sys/sunddi.h> 45 #include <sys/modctl.h> 46 47 48 /* 49 * Function prototypes 50 */ 51 static int spm_attach(dev_info_t *, ddi_attach_cmd_t); 52 static int spm_detach(dev_info_t *, ddi_detach_cmd_t); 53 54 /* 55 * Private data for schizo_pm driver 56 */ 57 struct spm_soft_state { 58 dev_info_t *dip; 59 }; 60 61 /* 62 * Configuration data structures 63 */ 64 static struct dev_ops spm_ops = { 65 DEVO_REV, /* devo_rev, */ 66 0, /* refcnt */ 67 nodev, /* getinfo */ 68 nulldev, /* identify */ 69 nulldev, /* probe */ 70 spm_attach, /* attach */ 71 spm_detach, /* detach */ 72 nodev, /* reset */ 73 (struct cb_ops *)0, /* cb_ops */ 74 (struct bus_ops *)0, /* bus_ops */ 75 NULL /* power */ 76 }; 77 78 /* 79 * Driver globals 80 */ 81 static void *spm_state; 82 static int spm_inst = -1; 83 84 static struct modldrv modldrv = { 85 &mod_driverops, /* Type of module = driver */ 86 "schizo pm driver v%I%", /* name of module */ 87 &spm_ops, /* driver ops */ 88 }; 89 90 static struct modlinkage modlinkage = { 91 MODREV_1, 92 (void *)&modldrv, 93 NULL 94 }; 95 96 /* 97 * Schizo CSR E* bit masks 98 */ 99 #define SCHIZO_SAFARI_ECLK_32 0x20ULL 100 #define SCHIZO_SAFARI_ECLK_2 0x2ULL 101 #define SCHIZO_SAFARI_ECLK_1 0x1ULL 102 #define SCHIZO_SAFARI_ECLK_MASK (SCHIZO_SAFARI_ECLK_32 | \ 103 SCHIZO_SAFARI_ECLK_2 | SCHIZO_SAFARI_ECLK_1) 104 105 /* 106 * bit masks to set schizo clock in parallel with setting cpu clock. 107 * Used when changing cpu speeds. 108 * 109 * NOTE: The order of entries must be from slowest to fastest. 110 */ 111 static const uint64_t schizo_safari_masks[] = { 112 SCHIZO_SAFARI_ECLK_32, 113 SCHIZO_SAFARI_ECLK_2, 114 SCHIZO_SAFARI_ECLK_1 115 }; 116 117 /* 118 * Normally, the address of the registers we use would be accessed from 119 * our "official" private data. However, since the dip is not passed 120 * in when spm_change_speed (see below) is called, and since there is 121 * only one unit of the spm "device", we keep it here as a static. 122 */ 123 static volatile uint64_t *spm_schizo_csr; 124 ddi_acc_handle_t spm_schizo_handle; 125 126 int 127 _init(void) 128 { 129 int error; 130 131 if ((error = ddi_soft_state_init(&spm_state, 132 sizeof (struct spm_soft_state), 0)) != 0) 133 return (error); 134 135 if ((error = mod_install(&modlinkage)) != 0) 136 ddi_soft_state_fini(&spm_state); 137 138 return (error); 139 } 140 141 int 142 _fini(void) 143 { 144 int error; 145 146 if ((error = mod_remove(&modlinkage)) == 0) 147 ddi_soft_state_fini(&spm_state); 148 149 return (error); 150 } 151 152 int 153 _info(struct modinfo *modinfop) 154 { 155 return (mod_info(&modlinkage, modinfop)); 156 } 157 158 static int 159 spm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 160 { 161 int rv; 162 struct spm_soft_state *softsp; 163 ddi_device_acc_attr_t attr; 164 165 switch (cmd) { 166 case DDI_ATTACH: 167 if (spm_inst != -1) { 168 cmn_err(CE_WARN, "spm_attach: " 169 "only one instance is allowed."); 170 return (DDI_FAILURE); 171 } 172 173 break; 174 case DDI_RESUME: 175 return (DDI_SUCCESS); 176 default: 177 return (DDI_FAILURE); 178 } 179 180 spm_inst = ddi_get_instance(dip); 181 182 if (ddi_soft_state_zalloc(spm_state, spm_inst) != DDI_SUCCESS) { 183 cmn_err(CE_WARN, "spm_attach: can't allocate state."); 184 return (DDI_FAILURE); 185 } 186 187 if ((softsp = ddi_get_soft_state(spm_state, spm_inst)) == NULL) { 188 cmn_err(CE_WARN, "spm_attach: can't get state."); 189 return (DDI_FAILURE); 190 } 191 192 softsp->dip = dip; 193 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 194 attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC; 195 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 196 197 /* 198 * Map the Safari E* Control register. 199 */ 200 rv = ddi_regs_map_setup(dip, 0, 201 (caddr_t *)&spm_schizo_csr, 0, 8, &attr, &spm_schizo_handle); 202 if (rv != DDI_SUCCESS) { 203 cmn_err(CE_WARN, "spm_attach: can't map the register."); 204 ddi_soft_state_free(spm_state, spm_inst); 205 return (rv); 206 } 207 208 ddi_report_dev(dip); 209 210 return (DDI_SUCCESS); 211 } 212 213 /*ARGSUSED*/ 214 static int 215 spm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 216 { 217 switch (cmd) { 218 case DDI_SUSPEND: 219 return (DDI_SUCCESS); 220 221 case DDI_DETACH: 222 return (DDI_FAILURE); 223 224 default: 225 return (DDI_FAILURE); 226 } 227 } 228 229 /* 230 * This globally visible function is the main reason this driver exists. 231 * It will be called by a platform power management driver to write to 232 * the schizo ASIC csr which changes schizo's clock rate. This is a 233 * required step when changing the clock of the cpus. 234 * 235 * NOTE - The caller should enter this routine sequentially. 236 */ 237 void 238 spm_change_schizo_speed(int lvl_index) 239 { 240 uint64_t contents; 241 242 ASSERT(lvl_index >= 0 && lvl_index <= 2); 243 contents = ddi_get64(spm_schizo_handle, (uint64_t *)spm_schizo_csr); 244 contents &= ~SCHIZO_SAFARI_ECLK_MASK; 245 contents |= schizo_safari_masks[ lvl_index ]; 246 ddi_put64(spm_schizo_handle, (uint64_t *)spm_schizo_csr, contents); 247 } 248