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