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