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 * Driver to map the PIC for the chicago platform. 31 */ 32 #include <sys/types.h> 33 #include <sys/time.h> 34 #include <sys/errno.h> 35 #include <sys/cmn_err.h> 36 #include <sys/param.h> 37 #include <sys/modctl.h> 38 #include <sys/conf.h> 39 #include <sys/open.h> 40 #include <sys/stat.h> 41 #include <sys/clock.h> 42 #include <sys/pic.h> 43 #include <sys/pic16f747.h> 44 #include <sys/ddi.h> 45 #include <sys/sunddi.h> 46 #include <sys/file.h> 47 48 /* dev_ops and cb_ops entry point function declarations */ 49 static int pic_attach(dev_info_t *, ddi_attach_cmd_t); 50 static int pic_detach(dev_info_t *, ddi_detach_cmd_t); 51 static int pic_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 52 static int pic_open(dev_t *, int, int, cred_t *); 53 static int pic_close(dev_t, int, int, cred_t *); 54 static int pic_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 55 56 struct cb_ops pic_cb_ops = { 57 pic_open, 58 pic_close, 59 nodev, 60 nodev, 61 nodev, /* dump */ 62 nodev, 63 nodev, 64 pic_ioctl, 65 nodev, /* devmap */ 66 nodev, 67 ddi_segmap, /* segmap */ 68 nochpoll, 69 ddi_prop_op, 70 NULL, /* for STREAMS drivers */ 71 D_NEW | D_MP /* driver compatibility flag */ 72 }; 73 74 static struct dev_ops pic_dev_ops = { 75 DEVO_REV, /* driver build version */ 76 0, /* device reference count */ 77 pic_getinfo, 78 nulldev, 79 nulldev, /* probe */ 80 pic_attach, 81 pic_detach, 82 nulldev, /* reset */ 83 &pic_cb_ops, 84 (struct bus_ops *)NULL, 85 nulldev /* power */ 86 }; 87 88 /* 89 * Fans' and sensors' node names and register offsets 90 */ 91 static struct minor_node_info pic_nodes[N_PIC_NODES] = { 92 {NULL, 0, 0}, /* Reserved */ 93 {"fan_0", RF_FAN0_PERIOD, F0_FLT_BIT}, /* System Fan 0 */ 94 {"fan_1", RF_FAN1_PERIOD, F1_FLT_BIT}, /* System Fan 1 */ 95 {"fan_2", RF_FAN2_PERIOD, F2_FLT_BIT}, /* System Fan 2 */ 96 {"fan_3", RF_FAN3_PERIOD, F3_FLT_BIT}, /* System Fan 3 */ 97 {"fan_4", RF_FAN4_PERIOD, F4_FLT_BIT}, /* System Fan 4 */ 98 {"adt7462", RF_LOCAL_TEMP, 0}, /* ADT7462 Local Temperature */ 99 {"cpu_0", RF_REMOTE1_TEMP, 0}, /* CPU 0 temp */ 100 {"cpu_1", RF_REMOTE2_TEMP, 0}, /* CPU 1 temp */ 101 {"mb", RF_REMOTE3_TEMP, 0}, /* Motherboard temp */ 102 {"lm95221", RF_LM95221_TEMP, 0}, /* LM95221 Local Temperature */ 103 {"fire", RF_FIRE_TEMP, 0}, /* FIRE Temp */ 104 {"lsi1064", RF_LSI1064_TEMP, 0}, /* LSI1064 Temp */ 105 {"front_panel", RF_FRONT_TEMP, 0}, /* Front Panel Temperature */ 106 {"psu", RF_PSU_TEMP, PSUF_FLT_BIT} /* PSU Temp (and ffault) */ 107 }; 108 109 /* 110 * Soft state 111 */ 112 struct pic_softc { 113 dev_info_t *dip; 114 kmutex_t mutex; 115 uint8_t *cmd_reg; 116 ddi_acc_handle_t cmd_handle; 117 }; 118 #define getsoftc(inst) ((struct pic_softc *)ddi_get_soft_state(statep, (inst))) 119 120 /* module configuration stuff */ 121 static void *statep; 122 extern struct mod_ops mod_driverops; 123 124 static struct modldrv modldrv = { 125 &mod_driverops, 126 "pic_client driver (v.%I%) ", 127 &pic_dev_ops 128 }; 129 130 static struct modlinkage modlinkage = { 131 MODREV_1, 132 &modldrv, 133 0 134 }; 135 136 int 137 _init(void) 138 { 139 int e; 140 141 if (e = ddi_soft_state_init(&statep, sizeof (struct pic_softc), 142 MAX_PIC_INSTANCES)) { 143 return (e); 144 } 145 146 if ((e = mod_install(&modlinkage)) != 0) 147 ddi_soft_state_fini(&statep); 148 149 return (e); 150 } 151 152 int 153 _fini(void) 154 { 155 int e; 156 157 if ((e = mod_remove(&modlinkage)) != 0) 158 return (e); 159 160 ddi_soft_state_fini(&statep); 161 162 return (DDI_SUCCESS); 163 } 164 165 int 166 _info(struct modinfo *modinfop) 167 { 168 return (mod_info(&modlinkage, modinfop)); 169 } 170 171 /*ARGSUSED*/ 172 static int 173 pic_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 174 { 175 int inst; 176 int retval = DDI_SUCCESS; 177 struct pic_softc *softc; 178 179 inst = PIC_MINOR_TO_INST(getminor((dev_t)arg)); 180 181 switch (cmd) { 182 case DDI_INFO_DEVT2DEVINFO: 183 if ((softc = getsoftc(inst)) == NULL) { 184 *result = (void *)NULL; 185 retval = DDI_FAILURE; 186 } else 187 *result = (void *)softc->dip; 188 break; 189 190 case DDI_INFO_DEVT2INSTANCE: 191 *result = (void *)((uintptr_t)inst); 192 break; 193 194 default: 195 retval = DDI_FAILURE; 196 } 197 198 return (retval); 199 } 200 201 static int 202 pic_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 203 { 204 int inst; 205 int i; 206 struct pic_softc *softc = NULL; 207 char *minor_name; 208 int minor; 209 char name[80]; 210 ddi_device_acc_attr_t dev_attr; 211 int res; 212 213 switch (cmd) { 214 case DDI_ATTACH: 215 inst = ddi_get_instance(dip); 216 if (inst >= MAX_PIC_INSTANCES) { 217 cmn_err(CE_WARN, "attach failed, too many instances\n"); 218 return (DDI_FAILURE); 219 } 220 221 (void) sprintf(name, "env-monitor%d", inst); 222 minor = PIC_INST_TO_MINOR(inst) | PIC_UNIT_TO_MINOR(0); 223 if (ddi_create_minor_node(dip, name, S_IFCHR, minor, 224 DDI_PSEUDO, NULL) == DDI_FAILURE) { 225 cmn_err(CE_WARN, 226 "ddi_create_minor_node() failed for inst %d\n", 227 inst); 228 return (DDI_FAILURE); 229 } 230 231 /* Allocate a soft state structure for this instance */ 232 if (ddi_soft_state_zalloc(statep, inst) != DDI_SUCCESS) { 233 cmn_err(CE_WARN, " ddi_soft_state_zalloc() failed " 234 "for inst %d\n", inst); 235 goto attach_failed; 236 } 237 238 /* Setup soft state */ 239 softc = getsoftc(inst); 240 softc->dip = dip; 241 mutex_init(&softc->mutex, NULL, MUTEX_DRIVER, NULL); 242 243 /* Setup device attributes */ 244 dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 245 dev_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 246 dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 247 248 /* 249 * The RF_COMMAND/RF_STATUS and RF_IND_DATA/RF_IND_ADDR 250 * register pairs are mapped as one register set starting 251 * from 0x0 and length 0x42. 252 */ 253 res = ddi_regs_map_setup(dip, 0, (caddr_t *)&softc->cmd_reg, 254 0, 0x42, &dev_attr, &softc->cmd_handle); 255 if (res != DDI_SUCCESS) { 256 cmn_err(CE_WARN, "ddi_regs_map_setup() failed\n"); 257 goto attach_failed; 258 } 259 260 /* Set up fans' and sensors' device minor nodes */ 261 for (i = 1; i < N_PIC_NODES; i++) { 262 minor_name = pic_nodes[i].minor_name; 263 minor = PIC_INST_TO_MINOR(inst) | PIC_UNIT_TO_MINOR(i); 264 if (ddi_create_minor_node(dip, minor_name, S_IFCHR, 265 minor, PICDEV_NODE_TYPE, NULL) == DDI_FAILURE) { 266 cmn_err(CE_WARN, 267 "%s:%d ddi_create_minor_node failed", 268 ddi_driver_name(dip), inst); 269 (void) pic_detach(dip, DDI_DETACH); 270 return (DDI_FAILURE); 271 } 272 } 273 274 /* Create main environmental node */ 275 ddi_report_dev(dip); 276 277 return (DDI_SUCCESS); 278 279 case DDI_RESUME: 280 return (DDI_SUCCESS); 281 282 default: 283 return (DDI_FAILURE); 284 } 285 286 attach_failed: 287 288 /* Free soft state, if allocated. remove minor node if added earlier */ 289 if (softc) 290 ddi_soft_state_free(statep, inst); 291 292 ddi_remove_minor_node(dip, NULL); 293 294 return (DDI_FAILURE); 295 } 296 297 static int 298 pic_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 299 { 300 int inst; 301 struct pic_softc *softc; 302 303 switch (cmd) { 304 case DDI_DETACH: 305 inst = ddi_get_instance(dip); 306 if ((softc = getsoftc(inst)) == NULL) 307 return (ENXIO); 308 309 (void) ddi_regs_map_free(&softc->cmd_handle); 310 /* Free the soft state and remove minor node added earlier */ 311 mutex_destroy(&softc->mutex); 312 ddi_soft_state_free(statep, inst); 313 ddi_remove_minor_node(dip, NULL); 314 return (DDI_SUCCESS); 315 316 case DDI_SUSPEND: 317 return (DDI_SUCCESS); 318 319 default: 320 return (DDI_FAILURE); 321 } 322 } 323 324 /*ARGSUSED*/ 325 static int 326 pic_open(dev_t *devp, int flag, int otyp, cred_t *credp) 327 { 328 int inst = PIC_MINOR_TO_INST(getminor(*devp)); 329 330 return (getsoftc(inst) == NULL ? ENXIO : 0); 331 } 332 333 /*ARGSUSED*/ 334 static int 335 pic_close(dev_t dev, int flag, int otyp, cred_t *credp) 336 { 337 int inst = PIC_MINOR_TO_INST(getminor(dev)); 338 339 return (getsoftc(inst) == NULL ? ENXIO : 0); 340 } 341 342 /*ARGSUSED*/ 343 static int 344 pic_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) 345 { 346 int inst; 347 int node; 348 struct pic_softc *softc; 349 uint8_t in_command; 350 int16_t tempr; 351 352 inst = PIC_MINOR_TO_INST(getminor(dev)); 353 if ((softc = getsoftc(inst)) == NULL) 354 return (ENXIO); 355 356 mutex_enter(&softc->mutex); 357 358 if (ddi_copyin((caddr_t)arg, &in_command, sizeof (in_command), 359 mode) != DDI_SUCCESS) { 360 mutex_exit(&softc->mutex); 361 return (EFAULT); 362 } 363 364 node = PIC_MINOR_TO_UNIT(getminor(dev)); 365 if ((node >= N_PIC_NODES) || (node < 1)) { 366 mutex_exit(&softc->mutex); 367 return (ENXIO); 368 } 369 370 switch (cmd) { 371 case PIC_GET_TEMPERATURE: 372 drv_usecwait(10); 373 374 /* select the temp sensor */ 375 (void) ddi_put8(softc->cmd_handle, (uint8_t *)softc->cmd_reg + 376 RF_IND_ADDR, pic_nodes[node].reg_offset); 377 378 /* retrieve temperature data */ 379 tempr = (int16_t)ddi_get8(softc->cmd_handle, 380 (uint8_t *)softc->cmd_reg + RF_IND_DATA); 381 mutex_exit(&softc->mutex); 382 383 if (tempr == 0xff) 384 return (EIO); 385 386 /* 387 * The temp is passed in as a uint8 value, we need to convert 388 * it to a signed 16 bit value to be able to handle the range 389 * of -64 to 190 degrees. 390 */ 391 tempr -= 64; 392 (void) ddi_copyout(&tempr, (caddr_t)arg, sizeof (tempr), mode); 393 return (0); 394 395 case PIC_GET_FAN_SPEED: 396 drv_usecwait(10); 397 398 /* select fan */ 399 (void) ddi_put8(softc->cmd_handle, (uint8_t *)softc->cmd_reg + 400 RF_IND_ADDR, pic_nodes[node].reg_offset); 401 402 /* retrieve fan data */ 403 in_command = ddi_get8(softc->cmd_handle, 404 (uint8_t *)softc->cmd_reg + RF_IND_DATA); 405 mutex_exit(&softc->mutex); 406 407 if (in_command == 0xff) 408 return (EIO); 409 410 (void) ddi_copyout(&in_command, (caddr_t)arg, 1, mode); 411 return (0); 412 413 case PIC_SET_FAN_SPEED: 414 /* select fan */ 415 (void) ddi_put8(softc->cmd_handle, (uint8_t *)softc->cmd_reg + 416 RF_IND_ADDR, pic_nodes[node].reg_offset); 417 418 /* send the fan data */ 419 (void) ddi_put8(softc->cmd_handle, 420 (uint8_t *)softc->cmd_reg + RF_IND_DATA, in_command); 421 422 mutex_exit(&softc->mutex); 423 return (0); 424 425 case PIC_GET_STATUS: 426 mutex_exit(&softc->mutex); 427 428 /* we don't read the status reg anymore */ 429 in_command = 0; 430 (void) ddi_copyout(&in_command, (caddr_t)arg, 1, mode); 431 432 return (0); 433 434 case PIC_GET_FAN_STATUS: 435 drv_usecwait(10); 436 437 /* read ffault register */ 438 (void) ddi_put8(softc->cmd_handle, (uint8_t *)softc->cmd_reg + 439 RF_IND_ADDR, RF_FAN_STATUS); 440 441 /* retrieve fan failure status */ 442 in_command = ddi_get8(softc->cmd_handle, 443 (uint8_t *)softc->cmd_reg + RF_IND_DATA); 444 mutex_exit(&softc->mutex); 445 446 if (in_command == 0xff) 447 return (EIO); 448 449 in_command = (in_command >> pic_nodes[node].ff_shift) & 0x1; 450 (void) ddi_copyout(&in_command, (caddr_t)arg, 1, mode); 451 return (0); 452 453 case PIC_SET_ESTAR_MODE: 454 (void) ddi_put8(softc->cmd_handle, 455 (uint8_t *)softc->cmd_reg + RF_COMMAND, CMD_TO_ESTAR); 456 mutex_exit(&softc->mutex); 457 return (0); 458 459 default: 460 mutex_exit(&softc->mutex); 461 cmn_err(CE_NOTE, "cmd %d isnt valid", cmd); 462 return (EINVAL); 463 } 464 } 465