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