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