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