11772Sjl139090 /* 21772Sjl139090 * CDDL HEADER START 31772Sjl139090 * 41772Sjl139090 * The contents of this file are subject to the terms of the 51772Sjl139090 * Common Development and Distribution License (the "License"). 61772Sjl139090 * You may not use this file except in compliance with the License. 71772Sjl139090 * 81772Sjl139090 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91772Sjl139090 * or http://www.opensolaris.org/os/licensing. 101772Sjl139090 * See the License for the specific language governing permissions 111772Sjl139090 * and limitations under the License. 121772Sjl139090 * 131772Sjl139090 * When distributing Covered Code, include this CDDL HEADER in each 141772Sjl139090 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151772Sjl139090 * If applicable, add the following below this CDDL HEADER, with the 161772Sjl139090 * fields enclosed by brackets "[]" replaced with your own identifying 171772Sjl139090 * information: Portions Copyright [yyyy] [name of copyright owner] 181772Sjl139090 * 191772Sjl139090 * CDDL HEADER END 201772Sjl139090 */ 211772Sjl139090 /* 221772Sjl139090 * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006 231772Sjl139090 */ 241772Sjl139090 251772Sjl139090 #pragma ident "%Z%%M% %I% %E% SMI" 261772Sjl139090 271772Sjl139090 #include <sys/types.h> 281772Sjl139090 #include <sys/time.h> 291772Sjl139090 #include <sys/errno.h> 301772Sjl139090 #include <sys/cmn_err.h> 311772Sjl139090 #include <sys/param.h> 321772Sjl139090 #include <sys/modctl.h> 331772Sjl139090 #include <sys/conf.h> 341772Sjl139090 #include <sys/open.h> 351772Sjl139090 #include <sys/stat.h> 361772Sjl139090 #include <sys/ddi.h> 371772Sjl139090 #include <sys/sunddi.h> 381772Sjl139090 #include <sys/file.h> 391772Sjl139090 #include <sys/intr.h> 401772Sjl139090 #include <sys/machsystm.h> 411772Sjl139090 421772Sjl139090 #define PNLIE_MASK 0x010 /* interrupt enable/disable */ 431772Sjl139090 #define PNLINT_MASK 0x001 /* interrupted flag */ 441772Sjl139090 451772Sjl139090 #ifdef DEBUG 461772Sjl139090 int panel_debug = 0; 471772Sjl139090 static void panel_ddi_put8(ddi_acc_handle_t, uint8_t *, uint8_t); 481772Sjl139090 #define DCMN_ERR(x) if (panel_debug) cmn_err x 491772Sjl139090 501772Sjl139090 #else 511772Sjl139090 521772Sjl139090 #define DCMN_ERR(x) 531772Sjl139090 #define panel_ddi_put8(x, y, z) ddi_put8(x, y, z) 541772Sjl139090 551772Sjl139090 #endif 561772Sjl139090 571772Sjl139090 static int panel_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 581772Sjl139090 static int panel_attach(dev_info_t *, ddi_attach_cmd_t); 591772Sjl139090 static int panel_detach(dev_info_t *, ddi_detach_cmd_t); 601772Sjl139090 static uint_t panel_intr(caddr_t); 611772Sjl139090 static int panel_open(dev_t *, int, int, cred_t *); 621772Sjl139090 static int panel_close(dev_t, int, int, cred_t *); 631772Sjl139090 641772Sjl139090 static char *panel_name = "oplpanel"; 651772Sjl139090 int panel_enable = 1; /* enable or disable */ 661772Sjl139090 672973Sgovinda extern uint64_t cpc_level15_inum; /* in cpc_subr.c */ 681772Sjl139090 691772Sjl139090 struct panel_state { 701772Sjl139090 dev_info_t *dip; 711772Sjl139090 ddi_iblock_cookie_t iblock_cookie; 721772Sjl139090 ddi_acc_handle_t panel_regs_handle; 731772Sjl139090 uint8_t *panelregs; /* mapping address */ 741772Sjl139090 uint8_t panelregs_state; /* keeping regs. */ 751772Sjl139090 }; 761772Sjl139090 771772Sjl139090 struct cb_ops panel_cb_ops = { 781772Sjl139090 nodev, /* open */ 791772Sjl139090 nodev, /* close */ 801772Sjl139090 nodev, /* strategy */ 811772Sjl139090 nodev, /* print */ 821772Sjl139090 nodev, /* dump */ 831772Sjl139090 nodev, /* read */ 841772Sjl139090 nodev, /* write */ 851772Sjl139090 nodev, /* ioctl */ 861772Sjl139090 nodev, /* devmap */ 871772Sjl139090 nodev, /* mmap */ 881772Sjl139090 nodev, /* segmap */ 891772Sjl139090 nochpoll, /* poll */ 901772Sjl139090 nodev, /* prop_op */ 911772Sjl139090 NULL, /* streamtab */ 921772Sjl139090 D_NEW | D_MP | D_HOTPLUG, /* flag */ 931772Sjl139090 CB_REV, /* cb_rev */ 941772Sjl139090 nodev, /* async I/O read entry point */ 951772Sjl139090 nodev /* async I/O write entry point */ 961772Sjl139090 }; 971772Sjl139090 981772Sjl139090 static struct dev_ops panel_dev_ops = { 991772Sjl139090 DEVO_REV, /* driver build version */ 1001772Sjl139090 0, /* device reference count */ 1011772Sjl139090 panel_getinfo, /* getinfo */ 1021772Sjl139090 nulldev, /* identify */ 1031772Sjl139090 nulldev, /* probe */ 1041772Sjl139090 panel_attach, /* attach */ 1051772Sjl139090 panel_detach, /* detach */ 1061772Sjl139090 nulldev, /* reset */ 1071772Sjl139090 &panel_cb_ops, /* cb_ops */ 1081772Sjl139090 NULL, /* bus_ops */ 1091772Sjl139090 nulldev /* power */ 1101772Sjl139090 }; 1111772Sjl139090 1121772Sjl139090 /* module configuration stuff */ 1131772Sjl139090 static void *panelstates; 1141772Sjl139090 extern struct mod_ops mod_driverops; 1151772Sjl139090 1161772Sjl139090 static struct modldrv modldrv = { 1171772Sjl139090 &mod_driverops, 1181772Sjl139090 "OPL panel driver %I%", 1191772Sjl139090 &panel_dev_ops 1201772Sjl139090 }; 1211772Sjl139090 1221772Sjl139090 static struct modlinkage modlinkage = { 1231772Sjl139090 MODREV_1, 1241772Sjl139090 &modldrv, 1251772Sjl139090 0 1261772Sjl139090 }; 1271772Sjl139090 1281772Sjl139090 1291772Sjl139090 int 1301772Sjl139090 _init(void) 1311772Sjl139090 { 1321772Sjl139090 int status; 1331772Sjl139090 1341772Sjl139090 DCMN_ERR((CE_CONT, "%s: _init\n", panel_name)); 1351772Sjl139090 1361772Sjl139090 status = ddi_soft_state_init(&panelstates, 1371772Sjl139090 sizeof (struct panel_state), 0); 1381772Sjl139090 if (status != 0) { 1391772Sjl139090 cmn_err(CE_WARN, "%s: ddi_soft_state_init failed.", 1401772Sjl139090 panel_name); 1411772Sjl139090 return (status); 1421772Sjl139090 } 1431772Sjl139090 1441772Sjl139090 status = mod_install(&modlinkage); 1451772Sjl139090 if (status != 0) { 1461772Sjl139090 ddi_soft_state_fini(&panelstates); 1471772Sjl139090 } 1481772Sjl139090 1491772Sjl139090 return (status); 1501772Sjl139090 } 1511772Sjl139090 1521772Sjl139090 int 1531772Sjl139090 _fini(void) 1541772Sjl139090 { 1551772Sjl139090 /* 1561772Sjl139090 * Can't unload to make sure the panel switch always works. 1571772Sjl139090 */ 1581772Sjl139090 return (EBUSY); 1591772Sjl139090 } 1601772Sjl139090 1611772Sjl139090 int 1621772Sjl139090 _info(struct modinfo *modinfop) 1631772Sjl139090 { 1641772Sjl139090 return (mod_info(&modlinkage, modinfop)); 1651772Sjl139090 } 1661772Sjl139090 1671772Sjl139090 static int 1681772Sjl139090 panel_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 1691772Sjl139090 { 1701772Sjl139090 1711772Sjl139090 int instance; 1721772Sjl139090 struct panel_state *statep = NULL; 1731772Sjl139090 1741772Sjl139090 ddi_device_acc_attr_t access_attr = { 1751772Sjl139090 DDI_DEVICE_ATTR_V0, 1761772Sjl139090 DDI_STRUCTURE_BE_ACC, 1771772Sjl139090 DDI_STRICTORDER_ACC 1781772Sjl139090 }; 1791772Sjl139090 1801772Sjl139090 instance = ddi_get_instance(dip); 1811772Sjl139090 1821772Sjl139090 DCMN_ERR((CE_CONT, "%s%d: attach\n", panel_name, instance)); 1831772Sjl139090 1841772Sjl139090 switch (cmd) { 1851772Sjl139090 case DDI_ATTACH: 1861772Sjl139090 DCMN_ERR((CE_CONT, "%s%d: DDI_ATTACH\n", 1871772Sjl139090 panel_name, instance)); 1881772Sjl139090 break; 1891772Sjl139090 1901772Sjl139090 case DDI_RESUME: 1911772Sjl139090 DCMN_ERR((CE_CONT, "%s%d: DDI_RESUME\n", 1921772Sjl139090 panel_name, instance)); 1931772Sjl139090 1941772Sjl139090 if ((statep = (struct panel_state *) 1951772Sjl139090 ddi_get_soft_state(panelstates, instance)) == NULL) { 1961772Sjl139090 cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.", 1971772Sjl139090 panel_name, instance); 1981772Sjl139090 return (DDI_FAILURE); 1991772Sjl139090 } 2001772Sjl139090 2011772Sjl139090 /* enable the interrupt just in case */ 2021772Sjl139090 panel_ddi_put8(statep->panel_regs_handle, statep->panelregs, 2031772Sjl139090 statep->panelregs_state); 2041772Sjl139090 return (DDI_SUCCESS); 2051772Sjl139090 2061772Sjl139090 default: 2071772Sjl139090 return (DDI_FAILURE); 2081772Sjl139090 } 2091772Sjl139090 2101772Sjl139090 /* 2111772Sjl139090 * Attach routine 2121772Sjl139090 */ 2131772Sjl139090 2141772Sjl139090 /* alloc and get soft state */ 2151772Sjl139090 if (ddi_soft_state_zalloc(panelstates, instance) != DDI_SUCCESS) { 2161772Sjl139090 cmn_err(CE_WARN, "%s%d: ddi_soft_state_zalloc failed.", 2171772Sjl139090 panel_name, instance); 2181772Sjl139090 goto attach_failed2; 2191772Sjl139090 } 2201772Sjl139090 if ((statep = (struct panel_state *) 2211772Sjl139090 ddi_get_soft_state(panelstates, instance)) == NULL) { 2221772Sjl139090 cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.", 2231772Sjl139090 panel_name, instance); 2241772Sjl139090 goto attach_failed1; 2251772Sjl139090 } 2261772Sjl139090 2271772Sjl139090 /* set the dip in the soft state */ 2281772Sjl139090 statep->dip = dip; 2291772Sjl139090 2301772Sjl139090 /* mapping register */ 2311772Sjl139090 if (ddi_regs_map_setup(dip, 0, (caddr_t *)&statep->panelregs, 2321772Sjl139090 0, 0, /* the entire space is mapped */ 2331772Sjl139090 &access_attr, &statep->panel_regs_handle) != DDI_SUCCESS) { 2341772Sjl139090 cmn_err(CE_WARN, "%s%d: ddi_regs_map_setup failed.", 2351772Sjl139090 panel_name, instance); 2361772Sjl139090 goto attach_failed1; 2371772Sjl139090 } 2381772Sjl139090 2391772Sjl139090 /* setup the interrupt handler */ 2401772Sjl139090 ddi_get_iblock_cookie(dip, 0, &statep->iblock_cookie); 2411772Sjl139090 if (ddi_add_intr(dip, 0, &statep->iblock_cookie, 0, &panel_intr, 2421772Sjl139090 (caddr_t)statep) != DDI_SUCCESS) { 2431772Sjl139090 cmn_err(CE_WARN, "%s%d: cannot add interrupt handler.", 2441772Sjl139090 panel_name, instance); 2451772Sjl139090 goto attach_failed0; 2461772Sjl139090 } 2471772Sjl139090 2481772Sjl139090 /* ATTACH SUCCESS */ 2491772Sjl139090 2501772Sjl139090 /* announce the device */ 2511772Sjl139090 ddi_report_dev(dip); 2521772Sjl139090 2531772Sjl139090 /* turn on interrupt */ 2541772Sjl139090 statep->panelregs_state = 0 | PNLIE_MASK; 2551772Sjl139090 panel_ddi_put8(statep->panel_regs_handle, statep->panelregs, 2561772Sjl139090 statep->panelregs_state); 2571772Sjl139090 2581772Sjl139090 return (DDI_SUCCESS); 2591772Sjl139090 2601772Sjl139090 attach_failed0: 2611772Sjl139090 ddi_regs_map_free(&statep->panel_regs_handle); 2621772Sjl139090 attach_failed1: 2631772Sjl139090 ddi_soft_state_free(panelstates, instance); 2641772Sjl139090 attach_failed2: 2651772Sjl139090 DCMN_ERR((CE_NOTE, "%s%d: attach failed", panel_name, instance)); 2661772Sjl139090 return (DDI_FAILURE); 2671772Sjl139090 } 2681772Sjl139090 2691772Sjl139090 static int 2701772Sjl139090 panel_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 2711772Sjl139090 { 2721772Sjl139090 int instance; 2731772Sjl139090 struct panel_state *statep; 2741772Sjl139090 2751772Sjl139090 instance = ddi_get_instance(dip); 2761772Sjl139090 2771772Sjl139090 DCMN_ERR((CE_CONT, "%s%d: detach\n", panel_name, instance)); 2781772Sjl139090 2791772Sjl139090 if ((statep = (struct panel_state *) 2801772Sjl139090 ddi_get_soft_state(panelstates, instance)) == NULL) { 2811772Sjl139090 cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.", 2821772Sjl139090 panel_name, instance); 2831772Sjl139090 return (DDI_FAILURE); 2841772Sjl139090 } 2851772Sjl139090 2861772Sjl139090 switch (cmd) { 2871772Sjl139090 case DDI_DETACH: 2881772Sjl139090 DCMN_ERR((CE_CONT, "%s%d: DDI_DETACH\n", 2891772Sjl139090 panel_name, instance)); 2901772Sjl139090 2911772Sjl139090 /* turn off interrupt */ 2921772Sjl139090 statep->panelregs_state &= ~PNLIE_MASK; 2931772Sjl139090 panel_ddi_put8(statep->panel_regs_handle, statep->panelregs, 2941772Sjl139090 statep->panelregs_state); 2951772Sjl139090 2961772Sjl139090 /* free all resources for the dip */ 2971772Sjl139090 ddi_remove_intr(dip, 0, statep->iblock_cookie); 2981772Sjl139090 2991772Sjl139090 /* need not free iblock_cookie */ 3001772Sjl139090 ddi_regs_map_free(&statep->panel_regs_handle); 3011772Sjl139090 ddi_soft_state_free(panelstates, instance); 3021772Sjl139090 3031772Sjl139090 return (DDI_SUCCESS); 3041772Sjl139090 3051772Sjl139090 case DDI_SUSPEND: 3061772Sjl139090 DCMN_ERR((CE_CONT, "%s%d: DDI_SUSPEND\n", 3071772Sjl139090 panel_name, instance)); 3081772Sjl139090 return (DDI_SUCCESS); 3091772Sjl139090 3101772Sjl139090 default: 3111772Sjl139090 return (DDI_FAILURE); 3121772Sjl139090 3131772Sjl139090 } 3141772Sjl139090 /* Not reached */ 3151772Sjl139090 } 3161772Sjl139090 3171772Sjl139090 /*ARGSUSED*/ 3181772Sjl139090 static int 3191772Sjl139090 panel_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) 3201772Sjl139090 { 3211772Sjl139090 struct panel_state *statep; 3221772Sjl139090 int instance; 3231772Sjl139090 dev_t dev = (dev_t)arg; 3241772Sjl139090 3251772Sjl139090 instance = getminor(dev); 3261772Sjl139090 3271772Sjl139090 DCMN_ERR((CE_CONT, "%s%d: getinfo\n", panel_name, instance)); 3281772Sjl139090 3291772Sjl139090 switch (cmd) { 3301772Sjl139090 case DDI_INFO_DEVT2DEVINFO: 3311772Sjl139090 if ((statep = (struct panel_state *) 3321772Sjl139090 ddi_get_soft_state(panelstates, instance)) == NULL) { 3331772Sjl139090 cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.", 3341772Sjl139090 panel_name, instance); 3351772Sjl139090 *resultp = NULL; 3361772Sjl139090 return (DDI_FAILURE); 3371772Sjl139090 } 3381772Sjl139090 *resultp = statep->dip; 3391772Sjl139090 break; 3401772Sjl139090 case DDI_INFO_DEVT2INSTANCE: 3411772Sjl139090 *resultp = (void *)(uintptr_t)instance; 3421772Sjl139090 break; 3431772Sjl139090 default: 3441772Sjl139090 return (DDI_FAILURE); 3451772Sjl139090 } 3461772Sjl139090 3471772Sjl139090 return (DDI_SUCCESS); 3481772Sjl139090 } 3491772Sjl139090 3501772Sjl139090 static uint_t 3511772Sjl139090 panel_intr(caddr_t arg) 3521772Sjl139090 { 3531772Sjl139090 struct panel_state *statep = (struct panel_state *)arg; 3541772Sjl139090 3551772Sjl139090 /* to confirm the validity of the interrupt */ 3561772Sjl139090 if (!(ddi_get8(statep->panel_regs_handle, statep->panelregs) & 3571772Sjl139090 PNLINT_MASK)) { 3581772Sjl139090 return (DDI_INTR_UNCLAIMED); 3591772Sjl139090 } 3601772Sjl139090 361*3300Shyw /* 362*3300Shyw * Clear the PNLINT bit 363*3300Shyw * HW reported that there might be a delay in the PNLINT bit 364*3300Shyw * clearing. We force synchronization by attempting to read 365*3300Shyw * back the reg after clearing the bit. 366*3300Shyw */ 3671772Sjl139090 panel_ddi_put8(statep->panel_regs_handle, statep->panelregs, 3681772Sjl139090 statep->panelregs_state | PNLINT_MASK); 369*3300Shyw ddi_get8(statep->panel_regs_handle, statep->panelregs); 3701772Sjl139090 3711772Sjl139090 if (panel_enable) { 372*3300Shyw uint_t pstate_save; 373*3300Shyw 3741772Sjl139090 /* avoid double panic */ 3751772Sjl139090 panel_enable = 0; 3761772Sjl139090 3771772Sjl139090 /* 3781772Sjl139090 * Re-enqueue the cpc interrupt handler for PIL15 here since we 3791772Sjl139090 * are not unwinding back to the interrupt handler subsystem. 3801772Sjl139090 * This is to allow potential cpc overflow interrupts to 3811772Sjl139090 * function while we go thru the panic flow. Note that this 3821772Sjl139090 * logic could be implemented in panic_enter_hw(), we do 3831772Sjl139090 * it here for now as it is less risky. This particular 3841772Sjl139090 * condition is only specific to OPL hardware and we want 3851772Sjl139090 * to minimize exposure of this new logic to other existing 3861772Sjl139090 * platforms. 3871772Sjl139090 */ 388*3300Shyw pstate_save = disable_vec_intr(); 3891772Sjl139090 intr_enqueue_req(PIL_15, cpc_level15_inum); 390*3300Shyw enable_vec_intr(pstate_save); 3911772Sjl139090 3921772Sjl139090 cmn_err(CE_PANIC, 3931772Sjl139090 "System Panel Driver: Emergency panic request " 3941772Sjl139090 "detected!"); 3951772Sjl139090 /* Not reached */ 3961772Sjl139090 } 3971772Sjl139090 3981772Sjl139090 return (DDI_INTR_CLAIMED); 3991772Sjl139090 } 4001772Sjl139090 4011772Sjl139090 #ifdef DEBUG 4021772Sjl139090 static void 4031772Sjl139090 panel_ddi_put8(ddi_acc_handle_t handle, uint8_t *dev_addr, uint8_t value) 4041772Sjl139090 { 4051772Sjl139090 if (panel_debug) { 4061772Sjl139090 cmn_err(CE_CONT, "%s: old value = 0x%x\n", 4071772Sjl139090 panel_name, ddi_get8(handle, dev_addr)); 4081772Sjl139090 cmn_err(CE_CONT, "%s: writing value = 0x%x\n", 4091772Sjl139090 panel_name, value); 4101772Sjl139090 ddi_put8(handle, dev_addr, value); 4111772Sjl139090 cmn_err(CE_CONT, "%s: new value = 0x%x\n", 4121772Sjl139090 panel_name, ddi_get8(handle, dev_addr)); 4131772Sjl139090 } else { 4141772Sjl139090 ddi_put8(handle, dev_addr, value); 4151772Sjl139090 } 4161772Sjl139090 } 4171772Sjl139090 #endif 418