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