xref: /onnv-gate/usr/src/uts/sun4u/opl/io/oplpanel/oplpanel.c (revision 11311:639e7bc0b42f)
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