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