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 /* 24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 /* 31 * Driver to control Alert and Power LEDs for the Seattle platform. 32 * Alert LED is also known as Service (required). 33 * Power LED is also known as Activity. 34 */ 35 #include <sys/types.h> 36 #include <sys/time.h> 37 #include <sys/errno.h> 38 #include <sys/cmn_err.h> 39 #include <sys/param.h> 40 #include <sys/modctl.h> 41 #include <sys/conf.h> 42 #include <sys/open.h> 43 #include <sys/stat.h> 44 #include <sys/clock.h> 45 #include <sys/ddi.h> 46 #include <sys/sunddi.h> 47 #include <sys/file.h> 48 #include <sys/note.h> 49 #include <sys/epic.h> 50 51 52 /* 53 * Some #defs that must be here as they differ for power.c 54 * and epic.c 55 */ 56 #define EPIC_REGS_OFFSET 0x00 57 #define EPIC_REGS_LEN 0x80 58 59 #define EPIC_IND_DATA 0x40 60 #define EPIC_IND_ADDR 0x41 61 #define EPIC_WRITE_MASK 0x80 62 63 /* dev_ops and cb_ops entry point function declarations */ 64 static int epic_attach(dev_info_t *, ddi_attach_cmd_t); 65 static int epic_detach(dev_info_t *, ddi_detach_cmd_t); 66 static int epic_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 67 static int epic_open(dev_t *, int, int, cred_t *); 68 static int epic_close(dev_t, int, int, cred_t *); 69 static int epic_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 70 71 struct cb_ops epic_cb_ops = { 72 epic_open, /* open */ 73 epic_close, /* close */ 74 nodev, /* strategy */ 75 nodev, /* print */ 76 nodev, /* dump */ 77 nodev, /* read */ 78 nodev, /* write */ 79 epic_ioctl, /* ioctl */ 80 nodev, /* devmap */ 81 nodev, /* mmap */ 82 ddi_segmap, /* segmap */ 83 nochpoll, /* poll */ 84 ddi_prop_op, /* cb_prop_op */ 85 NULL, /* streamtab - for STREAMS drivers */ 86 D_NEW | D_MP /* driver compatibility flag */ 87 }; 88 89 static struct dev_ops epic_dev_ops = { 90 DEVO_REV, /* driver build version */ 91 0, /* device reference count */ 92 epic_getinfo, 93 nulldev, 94 nulldev, /* probe */ 95 epic_attach, 96 epic_detach, 97 nulldev, /* reset */ 98 &epic_cb_ops, 99 (struct bus_ops *)NULL, 100 nulldev /* power */ 101 }; 102 103 104 /* 105 * Soft state 106 */ 107 struct epic_softc { 108 dev_info_t *dip; 109 kmutex_t mutex; 110 uint8_t *cmd_reg; 111 ddi_acc_handle_t cmd_handle; 112 }; 113 114 #define getsoftc(inst) ((struct epic_softc *)ddi_get_soft_state(statep, \ 115 (inst))) 116 117 /* module configuration stuff */ 118 static void *statep; 119 extern struct mod_ops mod_driverops; 120 121 static struct modldrv modldrv = { 122 &mod_driverops, 123 "epic_client driver v%I%", 124 &epic_dev_ops 125 }; 126 127 static struct modlinkage modlinkage = { 128 MODREV_1, 129 &modldrv, 130 0 131 }; 132 133 int 134 _init(void) 135 { 136 int e; 137 138 if ((e = ddi_soft_state_init(&statep, 139 sizeof (struct epic_softc), 0)) != 0) { 140 return (e); 141 } 142 143 if ((e = mod_install(&modlinkage)) != 0) 144 ddi_soft_state_fini(&statep); 145 146 return (e); 147 } 148 149 int 150 _fini(void) 151 { 152 int e; 153 154 if ((e = mod_remove(&modlinkage)) != 0) 155 return (e); 156 157 ddi_soft_state_fini(&statep); 158 159 return (DDI_SUCCESS); 160 } 161 162 int 163 _info(struct modinfo *modinfop) 164 { 165 return (mod_info(&modlinkage, modinfop)); 166 } 167 168 /*ARGSUSED*/ 169 static int 170 epic_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 171 { 172 int inst; 173 int retval = DDI_SUCCESS; 174 struct epic_softc *softc; 175 176 inst = (getminor((dev_t)arg)); 177 178 switch (cmd) { 179 case DDI_INFO_DEVT2DEVINFO: 180 if ((softc = getsoftc(inst)) == NULL) { 181 *result = (void *)NULL; 182 retval = DDI_FAILURE; 183 } else 184 *result = (void *)softc->dip; 185 break; 186 187 case DDI_INFO_DEVT2INSTANCE: 188 *result = (void *)(uintptr_t)inst; 189 break; 190 191 default: 192 retval = DDI_FAILURE; 193 } 194 195 return (retval); 196 } 197 198 static int 199 epic_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 200 { 201 int inst; 202 struct epic_softc *softc = NULL; 203 int minor; 204 char name[MAXNAMELEN]; 205 ddi_device_acc_attr_t dev_attr; 206 int res; 207 208 switch (cmd) { 209 case DDI_ATTACH: 210 inst = ddi_get_instance(dip); 211 (void) sprintf(name, "env-monitor%d", inst); 212 minor = inst; 213 if (ddi_create_minor_node(dip, name, S_IFCHR, minor, 214 DDI_PSEUDO, NULL) == DDI_FAILURE) { 215 cmn_err(CE_WARN, 216 "ddi_create_minor_node() failed for inst %d\n", 217 inst); 218 return (DDI_FAILURE); 219 } 220 221 /* Allocate a soft state structure for this instance */ 222 if (ddi_soft_state_zalloc(statep, inst) != DDI_SUCCESS) { 223 cmn_err(CE_WARN, " ddi_soft_state_zalloc() failed " 224 "for inst %d\n", inst); 225 break; 226 } 227 228 /* Setup soft state */ 229 if ((softc = getsoftc(inst)) == NULL) { 230 break; 231 } 232 softc->dip = dip; 233 mutex_init(&softc->mutex, NULL, MUTEX_DRIVER, NULL); 234 235 /* Setup device attributes */ 236 dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 237 dev_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 238 dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 239 240 res = ddi_regs_map_setup(dip, 0, (caddr_t *)&softc->cmd_reg, 241 EPIC_REGS_OFFSET, EPIC_REGS_LEN, &dev_attr, 242 &softc->cmd_handle); 243 244 if (res != DDI_SUCCESS) { 245 cmn_err(CE_WARN, "ddi_regs_map_setup() failed\n"); 246 break; 247 } 248 249 ddi_report_dev(dip); 250 251 252 return (DDI_SUCCESS); 253 254 case DDI_RESUME: 255 return (DDI_SUCCESS); 256 257 default: 258 return (DDI_FAILURE); 259 } 260 261 /* Attach failed */ 262 /* Free soft state, if allocated. remove minor node if added earlier */ 263 if (softc) 264 ddi_soft_state_free(statep, inst); 265 266 ddi_remove_minor_node(dip, NULL); 267 268 return (DDI_FAILURE); 269 } 270 271 static int 272 epic_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 273 { 274 int inst; 275 struct epic_softc *softc; 276 277 switch (cmd) { 278 case DDI_DETACH: 279 inst = ddi_get_instance(dip); 280 if ((softc = getsoftc(inst)) == NULL) 281 return (ENXIO); 282 283 (void) ddi_regs_map_free(&softc->cmd_handle); 284 285 286 /* Free the soft state and remove minor node added earlier */ 287 mutex_destroy(&softc->mutex); 288 ddi_soft_state_free(statep, inst); 289 ddi_remove_minor_node(dip, NULL); 290 return (DDI_SUCCESS); 291 292 case DDI_SUSPEND: 293 return (DDI_SUCCESS); 294 295 default: 296 return (DDI_FAILURE); 297 } 298 } 299 300 /*ARGSUSED*/ 301 static int 302 epic_open(dev_t *devp, int flag, int otyp, cred_t *credp) 303 { 304 _NOTE(ARGUNUSED(flag)) 305 _NOTE(ARGUNUSED(otyp)) 306 _NOTE(ARGUNUSED(credp)) 307 308 int inst = getminor(*devp); 309 310 return (getsoftc(inst) == NULL ? ENXIO : 0); 311 } 312 313 /*ARGSUSED*/ 314 static int 315 epic_close(dev_t dev, int flag, int otyp, cred_t *credp) 316 { 317 _NOTE(ARGUNUSED(flag)) 318 _NOTE(ARGUNUSED(otyp)) 319 _NOTE(ARGUNUSED(credp)) 320 321 int inst = getminor(dev); 322 323 return (getsoftc(inst) == NULL ? ENXIO : 0); 324 } 325 326 /*ARGSUSED*/ 327 static int 328 epic_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 329 int *rvalp) 330 { 331 _NOTE(ARGUNUSED(credp)) 332 333 int inst; 334 struct epic_softc *softc; 335 uint8_t in_command; 336 337 inst = getminor(dev); 338 if ((softc = getsoftc(inst)) == NULL) 339 return (ENXIO); 340 341 mutex_enter(&softc->mutex); 342 343 switch (cmd) { 344 case EPIC_SET_POWER_LED: 345 EPIC_WRITE(softc->cmd_handle, softc->cmd_reg, 346 EPIC_IND_LED_STATE0, EPIC_POWER_LED_MASK, 347 EPIC_POWER_LED_ON); 348 break; 349 case EPIC_RESET_POWER_LED: 350 EPIC_WRITE(softc->cmd_handle, softc->cmd_reg, 351 EPIC_IND_LED_STATE0, EPIC_POWER_LED_MASK, 352 EPIC_POWER_LED_OFF); 353 break; 354 case EPIC_SB_BL_POWER_LED: 355 EPIC_WRITE(softc->cmd_handle, softc->cmd_reg, 356 EPIC_IND_LED_STATE0, EPIC_POWER_LED_MASK, 357 EPIC_POWER_LED_SB_BLINK); 358 break; 359 case EPIC_FAST_BL_POWER_LED: 360 EPIC_WRITE(softc->cmd_handle, softc->cmd_reg, 361 EPIC_IND_LED_STATE0, EPIC_POWER_LED_MASK, 362 EPIC_POWER_LED_FAST_BLINK); 363 break; 364 case EPIC_SET_ALERT_LED: 365 EPIC_WRITE(softc->cmd_handle, softc->cmd_reg, 366 EPIC_IND_LED_STATE0, EPIC_ALERT_LED_MASK, 367 EPIC_ALERT_LED_ON); 368 break; 369 case EPIC_RESET_ALERT_LED: 370 EPIC_WRITE(softc->cmd_handle, softc->cmd_reg, 371 EPIC_IND_LED_STATE0, EPIC_ALERT_LED_MASK, 372 EPIC_ALERT_LED_OFF); 373 break; 374 case EPIC_GET_FW: 375 EPIC_READ(softc->cmd_handle, softc->cmd_reg, 376 in_command, EPIC_IND_FW_VERSION); 377 if (ddi_copyout((void *)(&in_command), (void *)arg, 378 sizeof (in_command), mode) != DDI_SUCCESS) { 379 mutex_exit(&softc->mutex); 380 return (EFAULT); 381 } 382 break; 383 default: 384 mutex_exit(&softc->mutex); 385 cmn_err(CE_WARN, "epic: cmd %d is not valid", cmd); 386 return (EINVAL); 387 } 388 389 mutex_exit(&softc->mutex); 390 return (0); 391 } 392