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 * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006 24 */ 25 26 /* 27 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 28 */ 29 30 31 #include <sys/types.h> 32 #include <sys/time.h> 33 #include <sys/errno.h> 34 #include <sys/cmn_err.h> 35 #include <sys/param.h> 36 #include <sys/modctl.h> 37 #include <sys/conf.h> 38 #include <sys/open.h> 39 #include <sys/stat.h> 40 #include <sys/ddi.h> 41 #include <sys/sunddi.h> 42 #include <sys/file.h> 43 #include <sys/intr.h> 44 #include <sys/machsystm.h> 45 46 #define PNLIE_MASK 0x010 /* interrupt enable/disable */ 47 #define PNLINT_MASK 0x001 /* interrupted flag */ 48 49 #ifdef DEBUG 50 int panel_debug = 0; 51 static void panel_ddi_put8(ddi_acc_handle_t, uint8_t *, uint8_t); 52 #define DCMN_ERR(x) if (panel_debug) cmn_err x 53 54 #else 55 56 #define DCMN_ERR(x) 57 #define panel_ddi_put8(x, y, z) ddi_put8(x, y, z) 58 59 #endif 60 61 static int panel_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 62 static int panel_attach(dev_info_t *, ddi_attach_cmd_t); 63 static int panel_detach(dev_info_t *, ddi_detach_cmd_t); 64 static uint_t panel_intr(caddr_t); 65 static int panel_open(dev_t *, int, int, cred_t *); 66 static int panel_close(dev_t, int, int, cred_t *); 67 68 static char *panel_name = "oplpanel"; 69 int panel_enable = 1; /* enable or disable */ 70 71 extern uint64_t cpc_level15_inum; /* in cpc_subr.c */ 72 73 struct panel_state { 74 dev_info_t *dip; 75 ddi_iblock_cookie_t iblock_cookie; 76 ddi_acc_handle_t panel_regs_handle; 77 uint8_t *panelregs; /* mapping address */ 78 uint8_t panelregs_state; /* keeping regs. */ 79 }; 80 81 struct cb_ops panel_cb_ops = { 82 nodev, /* open */ 83 nodev, /* close */ 84 nodev, /* strategy */ 85 nodev, /* print */ 86 nodev, /* dump */ 87 nodev, /* read */ 88 nodev, /* write */ 89 nodev, /* ioctl */ 90 nodev, /* devmap */ 91 nodev, /* mmap */ 92 nodev, /* segmap */ 93 nochpoll, /* poll */ 94 nodev, /* prop_op */ 95 NULL, /* streamtab */ 96 D_NEW | D_MP | D_HOTPLUG, /* flag */ 97 CB_REV, /* cb_rev */ 98 nodev, /* async I/O read entry point */ 99 nodev /* async I/O write entry point */ 100 }; 101 102 static struct dev_ops panel_dev_ops = { 103 DEVO_REV, /* driver build version */ 104 0, /* device reference count */ 105 panel_getinfo, /* getinfo */ 106 nulldev, /* identify */ 107 nulldev, /* probe */ 108 panel_attach, /* attach */ 109 panel_detach, /* detach */ 110 nulldev, /* reset */ 111 &panel_cb_ops, /* cb_ops */ 112 NULL, /* bus_ops */ 113 nulldev, /* power */ 114 ddi_quiesce_not_supported, /* devo_quiesce */ 115 }; 116 117 /* module configuration stuff */ 118 static void *panelstates; 119 extern struct mod_ops mod_driverops; 120 121 static struct modldrv modldrv = { 122 &mod_driverops, 123 "OPL panel driver", 124 &panel_dev_ops 125 }; 126 127 static struct modlinkage modlinkage = { 128 MODREV_1, 129 &modldrv, 130 0 131 }; 132 133 134 int 135 _init(void) 136 { 137 int status; 138 139 DCMN_ERR((CE_CONT, "%s: _init\n", panel_name)); 140 141 status = ddi_soft_state_init(&panelstates, 142 sizeof (struct panel_state), 0); 143 if (status != 0) { 144 cmn_err(CE_WARN, "%s: ddi_soft_state_init failed.", 145 panel_name); 146 return (status); 147 } 148 149 status = mod_install(&modlinkage); 150 if (status != 0) { 151 ddi_soft_state_fini(&panelstates); 152 } 153 154 return (status); 155 } 156 157 int 158 _fini(void) 159 { 160 /* 161 * Can't unload to make sure the panel switch always works. 162 */ 163 return (EBUSY); 164 } 165 166 int 167 _info(struct modinfo *modinfop) 168 { 169 return (mod_info(&modlinkage, modinfop)); 170 } 171 172 static int 173 panel_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 174 { 175 176 int instance; 177 struct panel_state *statep = NULL; 178 179 ddi_device_acc_attr_t access_attr = { 180 DDI_DEVICE_ATTR_V0, 181 DDI_STRUCTURE_BE_ACC, 182 DDI_STRICTORDER_ACC 183 }; 184 185 instance = ddi_get_instance(dip); 186 187 DCMN_ERR((CE_CONT, "%s%d: attach\n", panel_name, instance)); 188 189 switch (cmd) { 190 case DDI_ATTACH: 191 DCMN_ERR((CE_CONT, "%s%d: DDI_ATTACH\n", 192 panel_name, instance)); 193 break; 194 195 case DDI_RESUME: 196 DCMN_ERR((CE_CONT, "%s%d: DDI_RESUME\n", 197 panel_name, instance)); 198 199 if ((statep = (struct panel_state *) 200 ddi_get_soft_state(panelstates, instance)) == NULL) { 201 cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.", 202 panel_name, instance); 203 return (DDI_FAILURE); 204 } 205 206 /* enable the interrupt just in case */ 207 panel_ddi_put8(statep->panel_regs_handle, statep->panelregs, 208 statep->panelregs_state); 209 return (DDI_SUCCESS); 210 211 default: 212 return (DDI_FAILURE); 213 } 214 215 /* 216 * Attach routine 217 */ 218 219 /* alloc and get soft state */ 220 if (ddi_soft_state_zalloc(panelstates, instance) != DDI_SUCCESS) { 221 cmn_err(CE_WARN, "%s%d: ddi_soft_state_zalloc failed.", 222 panel_name, instance); 223 goto attach_failed2; 224 } 225 if ((statep = (struct panel_state *) 226 ddi_get_soft_state(panelstates, instance)) == NULL) { 227 cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.", 228 panel_name, instance); 229 goto attach_failed1; 230 } 231 232 /* set the dip in the soft state */ 233 statep->dip = dip; 234 235 /* mapping register */ 236 if (ddi_regs_map_setup(dip, 0, (caddr_t *)&statep->panelregs, 237 0, 0, /* the entire space is mapped */ 238 &access_attr, &statep->panel_regs_handle) != DDI_SUCCESS) { 239 cmn_err(CE_WARN, "%s%d: ddi_regs_map_setup failed.", 240 panel_name, instance); 241 goto attach_failed1; 242 } 243 244 /* setup the interrupt handler */ 245 (void) ddi_get_iblock_cookie(dip, 0, &statep->iblock_cookie); 246 if (ddi_add_intr(dip, 0, &statep->iblock_cookie, 0, &panel_intr, 247 (caddr_t)statep) != DDI_SUCCESS) { 248 cmn_err(CE_WARN, "%s%d: cannot add interrupt handler.", 249 panel_name, instance); 250 goto attach_failed0; 251 } 252 253 /* ATTACH SUCCESS */ 254 255 /* announce the device */ 256 ddi_report_dev(dip); 257 258 /* turn on interrupt */ 259 statep->panelregs_state = 0 | PNLIE_MASK; 260 panel_ddi_put8(statep->panel_regs_handle, statep->panelregs, 261 statep->panelregs_state); 262 263 return (DDI_SUCCESS); 264 265 attach_failed0: 266 ddi_regs_map_free(&statep->panel_regs_handle); 267 attach_failed1: 268 ddi_soft_state_free(panelstates, instance); 269 attach_failed2: 270 DCMN_ERR((CE_NOTE, "%s%d: attach failed", panel_name, instance)); 271 return (DDI_FAILURE); 272 } 273 274 static int 275 panel_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 276 { 277 int instance; 278 struct panel_state *statep; 279 280 instance = ddi_get_instance(dip); 281 282 DCMN_ERR((CE_CONT, "%s%d: detach\n", panel_name, instance)); 283 284 if ((statep = (struct panel_state *) 285 ddi_get_soft_state(panelstates, instance)) == NULL) { 286 cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.", 287 panel_name, instance); 288 return (DDI_FAILURE); 289 } 290 291 switch (cmd) { 292 case DDI_DETACH: 293 DCMN_ERR((CE_CONT, "%s%d: DDI_DETACH\n", 294 panel_name, instance)); 295 296 /* turn off interrupt */ 297 statep->panelregs_state &= ~PNLIE_MASK; 298 panel_ddi_put8(statep->panel_regs_handle, statep->panelregs, 299 statep->panelregs_state); 300 301 /* free all resources for the dip */ 302 ddi_remove_intr(dip, 0, statep->iblock_cookie); 303 304 /* need not free iblock_cookie */ 305 ddi_regs_map_free(&statep->panel_regs_handle); 306 ddi_soft_state_free(panelstates, instance); 307 308 return (DDI_SUCCESS); 309 310 case DDI_SUSPEND: 311 DCMN_ERR((CE_CONT, "%s%d: DDI_SUSPEND\n", 312 panel_name, instance)); 313 return (DDI_SUCCESS); 314 315 default: 316 return (DDI_FAILURE); 317 318 } 319 /* Not reached */ 320 } 321 322 /*ARGSUSED*/ 323 static int 324 panel_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) 325 { 326 struct panel_state *statep; 327 int instance; 328 dev_t dev = (dev_t)arg; 329 330 instance = getminor(dev); 331 332 DCMN_ERR((CE_CONT, "%s%d: getinfo\n", panel_name, instance)); 333 334 switch (cmd) { 335 case DDI_INFO_DEVT2DEVINFO: 336 if ((statep = (struct panel_state *) 337 ddi_get_soft_state(panelstates, instance)) == NULL) { 338 cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.", 339 panel_name, instance); 340 *resultp = NULL; 341 return (DDI_FAILURE); 342 } 343 *resultp = statep->dip; 344 break; 345 case DDI_INFO_DEVT2INSTANCE: 346 *resultp = (void *)(uintptr_t)instance; 347 break; 348 default: 349 return (DDI_FAILURE); 350 } 351 352 return (DDI_SUCCESS); 353 } 354 355 static uint_t 356 panel_intr(caddr_t arg) 357 { 358 struct panel_state *statep = (struct panel_state *)arg; 359 360 /* to confirm the validity of the interrupt */ 361 if (!(ddi_get8(statep->panel_regs_handle, statep->panelregs) & 362 PNLINT_MASK)) { 363 return (DDI_INTR_UNCLAIMED); 364 } 365 366 /* 367 * Clear the PNLINT bit 368 * HW reported that there might be a delay in the PNLINT bit 369 * clearing. We force synchronization by attempting to read 370 * back the reg after clearing the bit. 371 */ 372 panel_ddi_put8(statep->panel_regs_handle, statep->panelregs, 373 statep->panelregs_state | PNLINT_MASK); 374 (void) ddi_get8(statep->panel_regs_handle, statep->panelregs); 375 376 if (panel_enable) { 377 /* avoid double panic */ 378 panel_enable = 0; 379 380 cmn_err(CE_PANIC, 381 "System Panel Driver: Emergency panic request " 382 "detected!"); 383 /* Not reached */ 384 } 385 386 return (DDI_INTR_CLAIMED); 387 } 388 389 #ifdef DEBUG 390 static void 391 panel_ddi_put8(ddi_acc_handle_t handle, uint8_t *dev_addr, uint8_t value) 392 { 393 if (panel_debug) { 394 cmn_err(CE_CONT, "%s: old value = 0x%x\n", 395 panel_name, ddi_get8(handle, dev_addr)); 396 cmn_err(CE_CONT, "%s: writing value = 0x%x\n", 397 panel_name, value); 398 ddi_put8(handle, dev_addr, value); 399 cmn_err(CE_CONT, "%s: new value = 0x%x\n", 400 panel_name, ddi_get8(handle, dev_addr)); 401 } else { 402 ddi_put8(handle, dev_addr, value); 403 } 404 } 405 #endif 406