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