125cf1a30Sjl /*
225cf1a30Sjl  * CDDL HEADER START
325cf1a30Sjl  *
425cf1a30Sjl  * The contents of this file are subject to the terms of the
525cf1a30Sjl  * Common Development and Distribution License (the "License").
625cf1a30Sjl  * You may not use this file except in compliance with the License.
725cf1a30Sjl  *
825cf1a30Sjl  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
925cf1a30Sjl  * or http://www.opensolaris.org/os/licensing.
1025cf1a30Sjl  * See the License for the specific language governing permissions
1125cf1a30Sjl  * and limitations under the License.
1225cf1a30Sjl  *
1325cf1a30Sjl  * When distributing Covered Code, include this CDDL HEADER in each
1425cf1a30Sjl  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1525cf1a30Sjl  * If applicable, add the following below this CDDL HEADER, with the
1625cf1a30Sjl  * fields enclosed by brackets "[]" replaced with your own identifying
1725cf1a30Sjl  * information: Portions Copyright [yyyy] [name of copyright owner]
1825cf1a30Sjl  *
1925cf1a30Sjl  * CDDL HEADER END
2025cf1a30Sjl  */
2119397407SSherry Moore 
2225cf1a30Sjl /*
2325cf1a30Sjl  * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006
2425cf1a30Sjl  */
2525cf1a30Sjl 
2619397407SSherry Moore /*
27*a1bf6e2eSChristopher Baumbauer - Oracle America - San Diego United States  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
2819397407SSherry Moore  */
2919397407SSherry Moore 
3025cf1a30Sjl 
3125cf1a30Sjl #include <sys/types.h>
3225cf1a30Sjl #include <sys/time.h>
3325cf1a30Sjl #include <sys/errno.h>
3425cf1a30Sjl #include <sys/cmn_err.h>
3525cf1a30Sjl #include <sys/param.h>
3625cf1a30Sjl #include <sys/modctl.h>
3725cf1a30Sjl #include <sys/conf.h>
3825cf1a30Sjl #include <sys/open.h>
3925cf1a30Sjl #include <sys/stat.h>
4025cf1a30Sjl #include <sys/ddi.h>
4125cf1a30Sjl #include <sys/sunddi.h>
4225cf1a30Sjl #include <sys/file.h>
4325cf1a30Sjl #include <sys/intr.h>
4425cf1a30Sjl #include <sys/machsystm.h>
4525cf1a30Sjl 
4625cf1a30Sjl #define	PNLIE_MASK	0x010	/* interrupt enable/disable */
4725cf1a30Sjl #define	PNLINT_MASK	0x001	/* interrupted flag */
4825cf1a30Sjl 
4925cf1a30Sjl #ifdef DEBUG
5025cf1a30Sjl int panel_debug = 0;
5125cf1a30Sjl static void panel_ddi_put8(ddi_acc_handle_t, uint8_t *, uint8_t);
5225cf1a30Sjl #define	DCMN_ERR(x)	if (panel_debug) cmn_err x
5325cf1a30Sjl 
5425cf1a30Sjl #else
5525cf1a30Sjl 
5625cf1a30Sjl #define	DCMN_ERR(x)
5725cf1a30Sjl #define	panel_ddi_put8(x, y, z)	ddi_put8(x, y, z)
5825cf1a30Sjl 
5925cf1a30Sjl #endif
6025cf1a30Sjl 
6125cf1a30Sjl static int	panel_getinfo(dev_info_t *, ddi_info_cmd_t, void *,  void **);
6225cf1a30Sjl static int	panel_attach(dev_info_t *, ddi_attach_cmd_t);
6325cf1a30Sjl static int	panel_detach(dev_info_t *, ddi_detach_cmd_t);
6425cf1a30Sjl static uint_t	panel_intr(caddr_t);
6525cf1a30Sjl static int	panel_open(dev_t *, int, int, cred_t *);
6625cf1a30Sjl static int	panel_close(dev_t, int, int, cred_t *);
6725cf1a30Sjl 
6825cf1a30Sjl static char	*panel_name = "oplpanel";
6925cf1a30Sjl int		panel_enable = 1;	/* enable or disable */
7025cf1a30Sjl 
71b0fc0e77Sgovinda extern uint64_t	cpc_level15_inum;	/* in cpc_subr.c */
7225cf1a30Sjl 
7325cf1a30Sjl struct panel_state {
7425cf1a30Sjl 	dev_info_t		*dip;
7525cf1a30Sjl 	ddi_iblock_cookie_t	iblock_cookie;
7625cf1a30Sjl 	ddi_acc_handle_t	panel_regs_handle;
7725cf1a30Sjl 	uint8_t			*panelregs;		/* mapping address */
7825cf1a30Sjl 	uint8_t			panelregs_state;	/* keeping regs. */
7925cf1a30Sjl };
8025cf1a30Sjl 
8125cf1a30Sjl struct cb_ops panel_cb_ops = {
8225cf1a30Sjl 	nodev,		/* open */
8325cf1a30Sjl 	nodev,		/* close */
8425cf1a30Sjl 	nodev,		/* strategy */
8525cf1a30Sjl 	nodev,		/* print */
8625cf1a30Sjl 	nodev,		/* dump */
8725cf1a30Sjl 	nodev,		/* read */
8825cf1a30Sjl 	nodev,		/* write */
8925cf1a30Sjl 	nodev,		/* ioctl */
9025cf1a30Sjl 	nodev,		/* devmap */
9125cf1a30Sjl 	nodev,		/* mmap */
9225cf1a30Sjl 	nodev,		/* segmap */
9325cf1a30Sjl 	nochpoll,	/* poll */
9425cf1a30Sjl 	nodev,		/* prop_op */
9525cf1a30Sjl 	NULL,		/* streamtab */
9625cf1a30Sjl 	D_NEW | D_MP | D_HOTPLUG,	/* flag */
9725cf1a30Sjl 	CB_REV,		/* cb_rev */
9825cf1a30Sjl 	nodev,		/* async I/O read entry point */
9925cf1a30Sjl 	nodev		/* async I/O write entry point */
10025cf1a30Sjl };
10125cf1a30Sjl 
10225cf1a30Sjl static struct dev_ops panel_dev_ops = {
10325cf1a30Sjl 	DEVO_REV,		/* driver build version */
10425cf1a30Sjl 	0,			/* device reference count */
10525cf1a30Sjl 	panel_getinfo,		/* getinfo */
10625cf1a30Sjl 	nulldev,		/* identify */
10725cf1a30Sjl 	nulldev,		/* probe */
10825cf1a30Sjl 	panel_attach,		/* attach */
10925cf1a30Sjl 	panel_detach,		/* detach */
11025cf1a30Sjl 	nulldev,		/* reset */
11125cf1a30Sjl 	&panel_cb_ops,		/* cb_ops */
11225cf1a30Sjl 	NULL,			/* bus_ops */
11319397407SSherry Moore 	nulldev,		/* power */
11419397407SSherry Moore 	ddi_quiesce_not_supported,	/* devo_quiesce */
11525cf1a30Sjl };
11625cf1a30Sjl 
11725cf1a30Sjl /* module configuration stuff */
11825cf1a30Sjl static void		*panelstates;
11925cf1a30Sjl extern struct mod_ops	mod_driverops;
12025cf1a30Sjl 
12125cf1a30Sjl static struct modldrv modldrv = {
12225cf1a30Sjl 	&mod_driverops,
12319397407SSherry Moore 	"OPL panel driver",
12425cf1a30Sjl 	&panel_dev_ops
12525cf1a30Sjl };
12625cf1a30Sjl 
12725cf1a30Sjl static struct modlinkage modlinkage = {
12825cf1a30Sjl 	MODREV_1,
12925cf1a30Sjl 	&modldrv,
13025cf1a30Sjl 	0
13125cf1a30Sjl };
13225cf1a30Sjl 
13325cf1a30Sjl 
13425cf1a30Sjl int
_init(void)13525cf1a30Sjl _init(void)
13625cf1a30Sjl {
13725cf1a30Sjl 	int	status;
13825cf1a30Sjl 
13925cf1a30Sjl 	DCMN_ERR((CE_CONT, "%s: _init\n", panel_name));
14025cf1a30Sjl 
14125cf1a30Sjl 	status = ddi_soft_state_init(&panelstates,
14225cf1a30Sjl 	    sizeof (struct panel_state), 0);
14325cf1a30Sjl 	if (status != 0) {
14425cf1a30Sjl 		cmn_err(CE_WARN, "%s: ddi_soft_state_init failed.",
14525cf1a30Sjl 		    panel_name);
14625cf1a30Sjl 		return (status);
14725cf1a30Sjl 	}
14825cf1a30Sjl 
14925cf1a30Sjl 	status = mod_install(&modlinkage);
15025cf1a30Sjl 	if (status != 0) {
15125cf1a30Sjl 		ddi_soft_state_fini(&panelstates);
15225cf1a30Sjl 	}
15325cf1a30Sjl 
15425cf1a30Sjl 	return (status);
15525cf1a30Sjl }
15625cf1a30Sjl 
15725cf1a30Sjl int
_fini(void)15825cf1a30Sjl _fini(void)
15925cf1a30Sjl {
16025cf1a30Sjl 	/*
16125cf1a30Sjl 	 * Can't unload to make sure the panel switch always works.
16225cf1a30Sjl 	 */
16325cf1a30Sjl 	return (EBUSY);
16425cf1a30Sjl }
16525cf1a30Sjl 
16625cf1a30Sjl int
_info(struct modinfo * modinfop)16725cf1a30Sjl _info(struct modinfo *modinfop)
16825cf1a30Sjl {
16925cf1a30Sjl 	return (mod_info(&modlinkage, modinfop));
17025cf1a30Sjl }
17125cf1a30Sjl 
17225cf1a30Sjl static int
panel_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)17325cf1a30Sjl panel_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
17425cf1a30Sjl {
17525cf1a30Sjl 
17625cf1a30Sjl 	int instance;
17725cf1a30Sjl 	struct panel_state *statep = NULL;
17825cf1a30Sjl 
17925cf1a30Sjl 	ddi_device_acc_attr_t access_attr = {
18025cf1a30Sjl 		DDI_DEVICE_ATTR_V0,
18125cf1a30Sjl 		DDI_STRUCTURE_BE_ACC,
18225cf1a30Sjl 		DDI_STRICTORDER_ACC
18325cf1a30Sjl 	};
18425cf1a30Sjl 
18525cf1a30Sjl 	instance = ddi_get_instance(dip);
18625cf1a30Sjl 
18725cf1a30Sjl 	DCMN_ERR((CE_CONT, "%s%d: attach\n", panel_name, instance));
18825cf1a30Sjl 
18925cf1a30Sjl 	switch (cmd) {
19025cf1a30Sjl 	case DDI_ATTACH:
19125cf1a30Sjl 		DCMN_ERR((CE_CONT, "%s%d: DDI_ATTACH\n",
19225cf1a30Sjl 		    panel_name, instance));
19325cf1a30Sjl 		break;
19425cf1a30Sjl 
19525cf1a30Sjl 	case DDI_RESUME:
19625cf1a30Sjl 		DCMN_ERR((CE_CONT, "%s%d: DDI_RESUME\n",
19725cf1a30Sjl 		    panel_name, instance));
19825cf1a30Sjl 
19925cf1a30Sjl 		if ((statep = (struct panel_state *)
20025cf1a30Sjl 		    ddi_get_soft_state(panelstates, instance)) == NULL) {
20125cf1a30Sjl 			cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.",
20225cf1a30Sjl 			    panel_name, instance);
20325cf1a30Sjl 			return (DDI_FAILURE);
20425cf1a30Sjl 		}
20525cf1a30Sjl 
20625cf1a30Sjl 		/* enable the interrupt just in case */
20725cf1a30Sjl 		panel_ddi_put8(statep->panel_regs_handle, statep->panelregs,
20825cf1a30Sjl 		    statep->panelregs_state);
20925cf1a30Sjl 		return (DDI_SUCCESS);
21025cf1a30Sjl 
21125cf1a30Sjl 	default:
21225cf1a30Sjl 		return (DDI_FAILURE);
21325cf1a30Sjl 	}
21425cf1a30Sjl 
21525cf1a30Sjl 	/*
21625cf1a30Sjl 	 * Attach routine
21725cf1a30Sjl 	 */
21825cf1a30Sjl 
21925cf1a30Sjl 	/* alloc and get soft state */
22025cf1a30Sjl 	if (ddi_soft_state_zalloc(panelstates, instance) != DDI_SUCCESS) {
22125cf1a30Sjl 		cmn_err(CE_WARN, "%s%d: ddi_soft_state_zalloc failed.",
22225cf1a30Sjl 		    panel_name, instance);
22325cf1a30Sjl 		goto attach_failed2;
22425cf1a30Sjl 	}
22525cf1a30Sjl 	if ((statep = (struct panel_state *)
22625cf1a30Sjl 	    ddi_get_soft_state(panelstates, instance)) == NULL) {
22725cf1a30Sjl 		cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.",
22825cf1a30Sjl 		    panel_name, instance);
22925cf1a30Sjl 		goto attach_failed1;
23025cf1a30Sjl 	}
23125cf1a30Sjl 
23225cf1a30Sjl 	/* set the dip in the soft state */
23325cf1a30Sjl 	statep->dip = dip;
23425cf1a30Sjl 
23525cf1a30Sjl 	/* mapping register */
23625cf1a30Sjl 	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&statep->panelregs,
23725cf1a30Sjl 	    0, 0, /* the entire space is mapped */
23825cf1a30Sjl 	    &access_attr, &statep->panel_regs_handle) != DDI_SUCCESS) {
23925cf1a30Sjl 		cmn_err(CE_WARN, "%s%d: ddi_regs_map_setup failed.",
24025cf1a30Sjl 		    panel_name, instance);
24125cf1a30Sjl 		goto attach_failed1;
24225cf1a30Sjl 	}
24325cf1a30Sjl 
24425cf1a30Sjl 	/* setup the interrupt handler */
24507d06da5SSurya Prakki 	(void) ddi_get_iblock_cookie(dip, 0, &statep->iblock_cookie);
24625cf1a30Sjl 	if (ddi_add_intr(dip, 0, &statep->iblock_cookie, 0, &panel_intr,
24725cf1a30Sjl 	    (caddr_t)statep) != DDI_SUCCESS) {
24825cf1a30Sjl 		cmn_err(CE_WARN, "%s%d: cannot add interrupt handler.",
24925cf1a30Sjl 		    panel_name, instance);
25025cf1a30Sjl 		goto attach_failed0;
25125cf1a30Sjl 	}
25225cf1a30Sjl 
25325cf1a30Sjl 	/* ATTACH SUCCESS */
25425cf1a30Sjl 
25525cf1a30Sjl 	/* announce the device */
25625cf1a30Sjl 	ddi_report_dev(dip);
25725cf1a30Sjl 
25825cf1a30Sjl 	/* turn on interrupt */
25925cf1a30Sjl 	statep->panelregs_state = 0 | PNLIE_MASK;
26025cf1a30Sjl 	panel_ddi_put8(statep->panel_regs_handle, statep->panelregs,
26125cf1a30Sjl 	    statep->panelregs_state);
26225cf1a30Sjl 
26325cf1a30Sjl 	return (DDI_SUCCESS);
26425cf1a30Sjl 
26525cf1a30Sjl attach_failed0:
26625cf1a30Sjl 	ddi_regs_map_free(&statep->panel_regs_handle);
26725cf1a30Sjl attach_failed1:
26825cf1a30Sjl 	ddi_soft_state_free(panelstates, instance);
26925cf1a30Sjl attach_failed2:
27025cf1a30Sjl 	DCMN_ERR((CE_NOTE, "%s%d: attach failed", panel_name, instance));
27125cf1a30Sjl 	return (DDI_FAILURE);
27225cf1a30Sjl }
27325cf1a30Sjl 
27425cf1a30Sjl static int
panel_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)27525cf1a30Sjl panel_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
27625cf1a30Sjl {
27725cf1a30Sjl 	int instance;
27825cf1a30Sjl 	struct panel_state *statep;
27925cf1a30Sjl 
28025cf1a30Sjl 	instance = ddi_get_instance(dip);
28125cf1a30Sjl 
28225cf1a30Sjl 	DCMN_ERR((CE_CONT, "%s%d: detach\n", panel_name, instance));
28325cf1a30Sjl 
28425cf1a30Sjl 	if ((statep = (struct panel_state *)
28525cf1a30Sjl 	    ddi_get_soft_state(panelstates, instance)) == NULL) {
28625cf1a30Sjl 		cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.",
28725cf1a30Sjl 		    panel_name, instance);
28825cf1a30Sjl 		return (DDI_FAILURE);
28925cf1a30Sjl 	}
29025cf1a30Sjl 
29125cf1a30Sjl 	switch (cmd) {
29225cf1a30Sjl 	case DDI_DETACH:
29325cf1a30Sjl 		DCMN_ERR((CE_CONT, "%s%d: DDI_DETACH\n",
29425cf1a30Sjl 		    panel_name, instance));
29525cf1a30Sjl 
29625cf1a30Sjl 		/* turn off interrupt */
29725cf1a30Sjl 		statep->panelregs_state &= ~PNLIE_MASK;
29825cf1a30Sjl 		panel_ddi_put8(statep->panel_regs_handle, statep->panelregs,
29925cf1a30Sjl 		    statep->panelregs_state);
30025cf1a30Sjl 
30125cf1a30Sjl 		/* free all resources for the dip */
30225cf1a30Sjl 		ddi_remove_intr(dip, 0, statep->iblock_cookie);
30325cf1a30Sjl 
30425cf1a30Sjl 		/* need not free iblock_cookie */
30525cf1a30Sjl 		ddi_regs_map_free(&statep->panel_regs_handle);
30625cf1a30Sjl 		ddi_soft_state_free(panelstates, instance);
30725cf1a30Sjl 
30825cf1a30Sjl 		return (DDI_SUCCESS);
30925cf1a30Sjl 
31025cf1a30Sjl 	case DDI_SUSPEND:
31125cf1a30Sjl 		DCMN_ERR((CE_CONT, "%s%d: DDI_SUSPEND\n",
31225cf1a30Sjl 		    panel_name, instance));
31325cf1a30Sjl 		return (DDI_SUCCESS);
31425cf1a30Sjl 
31525cf1a30Sjl 	default:
31625cf1a30Sjl 		return (DDI_FAILURE);
31725cf1a30Sjl 
31825cf1a30Sjl 	}
31925cf1a30Sjl 	/* Not reached */
32025cf1a30Sjl }
32125cf1a30Sjl 
32225cf1a30Sjl /*ARGSUSED*/
32325cf1a30Sjl static int
panel_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)32425cf1a30Sjl panel_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,  void **resultp)
32525cf1a30Sjl {
32625cf1a30Sjl 	struct panel_state *statep;
32725cf1a30Sjl 	int	instance;
32825cf1a30Sjl 	dev_t	dev = (dev_t)arg;
32925cf1a30Sjl 
33025cf1a30Sjl 	instance = getminor(dev);
33125cf1a30Sjl 
33225cf1a30Sjl 	DCMN_ERR((CE_CONT, "%s%d: getinfo\n", panel_name, instance));
33325cf1a30Sjl 
33425cf1a30Sjl 	switch (cmd) {
33525cf1a30Sjl 	case DDI_INFO_DEVT2DEVINFO:
33625cf1a30Sjl 		if ((statep = (struct panel_state *)
33725cf1a30Sjl 		    ddi_get_soft_state(panelstates, instance)) == NULL) {
33825cf1a30Sjl 			cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.",
33925cf1a30Sjl 			    panel_name, instance);
34025cf1a30Sjl 			*resultp = NULL;
34125cf1a30Sjl 			return (DDI_FAILURE);
34225cf1a30Sjl 		}
34325cf1a30Sjl 		*resultp = statep->dip;
34425cf1a30Sjl 		break;
34525cf1a30Sjl 	case DDI_INFO_DEVT2INSTANCE:
34625cf1a30Sjl 		*resultp = (void *)(uintptr_t)instance;
34725cf1a30Sjl 		break;
34825cf1a30Sjl 	default:
34925cf1a30Sjl 		return (DDI_FAILURE);
35025cf1a30Sjl 	}
35125cf1a30Sjl 
35225cf1a30Sjl 	return (DDI_SUCCESS);
35325cf1a30Sjl }
35425cf1a30Sjl 
35525cf1a30Sjl static  uint_t
panel_intr(caddr_t arg)35625cf1a30Sjl panel_intr(caddr_t arg)
35725cf1a30Sjl {
35825cf1a30Sjl 	struct panel_state *statep = (struct panel_state *)arg;
35925cf1a30Sjl 
36025cf1a30Sjl 	/* to confirm the validity of the interrupt */
36125cf1a30Sjl 	if (!(ddi_get8(statep->panel_regs_handle, statep->panelregs) &
36225cf1a30Sjl 	    PNLINT_MASK)) {
36325cf1a30Sjl 		return (DDI_INTR_UNCLAIMED);
36425cf1a30Sjl 	}
36525cf1a30Sjl 
36697cc145dShyw 	/*
36797cc145dShyw 	 * Clear the PNLINT bit
36897cc145dShyw 	 * HW reported that there might be a delay in the PNLINT bit
36997cc145dShyw 	 * clearing. We force synchronization by attempting to read
37097cc145dShyw 	 * back the reg after clearing the bit.
37197cc145dShyw 	 */
37225cf1a30Sjl 	panel_ddi_put8(statep->panel_regs_handle, statep->panelregs,
37325cf1a30Sjl 	    statep->panelregs_state | PNLINT_MASK);
37407d06da5SSurya Prakki 	(void) ddi_get8(statep->panel_regs_handle, statep->panelregs);
37525cf1a30Sjl 
37625cf1a30Sjl 	if (panel_enable) {
37725cf1a30Sjl 		/* avoid double panic */
37825cf1a30Sjl 		panel_enable 	= 0;
37925cf1a30Sjl 
38025cf1a30Sjl 		cmn_err(CE_PANIC,
38125cf1a30Sjl 		    "System Panel Driver: Emergency panic request "
38225cf1a30Sjl 		    "detected!");
38325cf1a30Sjl 		/* Not reached */
38425cf1a30Sjl 	}
38525cf1a30Sjl 
38625cf1a30Sjl 	return (DDI_INTR_CLAIMED);
38725cf1a30Sjl }
38825cf1a30Sjl 
38925cf1a30Sjl #ifdef DEBUG
39025cf1a30Sjl static void
panel_ddi_put8(ddi_acc_handle_t handle,uint8_t * dev_addr,uint8_t value)39125cf1a30Sjl panel_ddi_put8(ddi_acc_handle_t handle, uint8_t *dev_addr, uint8_t value)
39225cf1a30Sjl {
39325cf1a30Sjl 	if (panel_debug) {
39425cf1a30Sjl 		cmn_err(CE_CONT, "%s: old value = 0x%x\n",
39525cf1a30Sjl 		    panel_name, ddi_get8(handle, dev_addr));
39625cf1a30Sjl 		cmn_err(CE_CONT, "%s: writing value = 0x%x\n",
39725cf1a30Sjl 		    panel_name, value);
39825cf1a30Sjl 		ddi_put8(handle, dev_addr, value);
39925cf1a30Sjl 		cmn_err(CE_CONT, "%s: new value = 0x%x\n",
40025cf1a30Sjl 		    panel_name, ddi_get8(handle, dev_addr));
40125cf1a30Sjl 	} else {
40225cf1a30Sjl 		ddi_put8(handle, dev_addr, value);
40325cf1a30Sjl 	}
40425cf1a30Sjl }
40525cf1a30Sjl #endif
406