xref: /onnv-gate/usr/src/uts/sun4u/opl/io/oplpanel/oplpanel.c (revision 1772:78cca3d2cc4b)
1*1772Sjl139090 /*
2*1772Sjl139090  * CDDL HEADER START
3*1772Sjl139090  *
4*1772Sjl139090  * The contents of this file are subject to the terms of the
5*1772Sjl139090  * Common Development and Distribution License (the "License").
6*1772Sjl139090  * You may not use this file except in compliance with the License.
7*1772Sjl139090  *
8*1772Sjl139090  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*1772Sjl139090  * or http://www.opensolaris.org/os/licensing.
10*1772Sjl139090  * See the License for the specific language governing permissions
11*1772Sjl139090  * and limitations under the License.
12*1772Sjl139090  *
13*1772Sjl139090  * When distributing Covered Code, include this CDDL HEADER in each
14*1772Sjl139090  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*1772Sjl139090  * If applicable, add the following below this CDDL HEADER, with the
16*1772Sjl139090  * fields enclosed by brackets "[]" replaced with your own identifying
17*1772Sjl139090  * information: Portions Copyright [yyyy] [name of copyright owner]
18*1772Sjl139090  *
19*1772Sjl139090  * CDDL HEADER END
20*1772Sjl139090  */
21*1772Sjl139090 /*
22*1772Sjl139090  * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006
23*1772Sjl139090  */
24*1772Sjl139090 
25*1772Sjl139090 #pragma ident	"%Z%%M%	%I%	%E% SMI"
26*1772Sjl139090 
27*1772Sjl139090 #include <sys/types.h>
28*1772Sjl139090 #include <sys/time.h>
29*1772Sjl139090 #include <sys/errno.h>
30*1772Sjl139090 #include <sys/cmn_err.h>
31*1772Sjl139090 #include <sys/param.h>
32*1772Sjl139090 #include <sys/modctl.h>
33*1772Sjl139090 #include <sys/conf.h>
34*1772Sjl139090 #include <sys/open.h>
35*1772Sjl139090 #include <sys/stat.h>
36*1772Sjl139090 #include <sys/ddi.h>
37*1772Sjl139090 #include <sys/sunddi.h>
38*1772Sjl139090 #include <sys/file.h>
39*1772Sjl139090 #include <sys/intr.h>
40*1772Sjl139090 #include <sys/machsystm.h>
41*1772Sjl139090 
42*1772Sjl139090 #define	PNLIE_MASK	0x010	/* interrupt enable/disable */
43*1772Sjl139090 #define	PNLINT_MASK	0x001	/* interrupted flag */
44*1772Sjl139090 
45*1772Sjl139090 #ifdef DEBUG
46*1772Sjl139090 int panel_debug = 0;
47*1772Sjl139090 static void panel_ddi_put8(ddi_acc_handle_t, uint8_t *, uint8_t);
48*1772Sjl139090 #define	DCMN_ERR(x)	if (panel_debug) cmn_err x
49*1772Sjl139090 
50*1772Sjl139090 #else
51*1772Sjl139090 
52*1772Sjl139090 #define	DCMN_ERR(x)
53*1772Sjl139090 #define	panel_ddi_put8(x, y, z)	ddi_put8(x, y, z)
54*1772Sjl139090 
55*1772Sjl139090 #endif
56*1772Sjl139090 
57*1772Sjl139090 static int	panel_getinfo(dev_info_t *, ddi_info_cmd_t, void *,  void **);
58*1772Sjl139090 static int	panel_attach(dev_info_t *, ddi_attach_cmd_t);
59*1772Sjl139090 static int	panel_detach(dev_info_t *, ddi_detach_cmd_t);
60*1772Sjl139090 static uint_t	panel_intr(caddr_t);
61*1772Sjl139090 static int	panel_open(dev_t *, int, int, cred_t *);
62*1772Sjl139090 static int	panel_close(dev_t, int, int, cred_t *);
63*1772Sjl139090 
64*1772Sjl139090 static char	*panel_name = "oplpanel";
65*1772Sjl139090 int		panel_enable = 1;	/* enable or disable */
66*1772Sjl139090 
67*1772Sjl139090 extern uint32_t		cpc_level15_inum;	/* in cpc_subr.c */
68*1772Sjl139090 
69*1772Sjl139090 struct panel_state {
70*1772Sjl139090 	dev_info_t		*dip;
71*1772Sjl139090 	ddi_iblock_cookie_t	iblock_cookie;
72*1772Sjl139090 	ddi_acc_handle_t	panel_regs_handle;
73*1772Sjl139090 	uint8_t			*panelregs;		/* mapping address */
74*1772Sjl139090 	uint8_t			panelregs_state;	/* keeping regs. */
75*1772Sjl139090 };
76*1772Sjl139090 
77*1772Sjl139090 struct cb_ops panel_cb_ops = {
78*1772Sjl139090 	nodev,		/* open */
79*1772Sjl139090 	nodev,		/* close */
80*1772Sjl139090 	nodev,		/* strategy */
81*1772Sjl139090 	nodev,		/* print */
82*1772Sjl139090 	nodev,		/* dump */
83*1772Sjl139090 	nodev,		/* read */
84*1772Sjl139090 	nodev,		/* write */
85*1772Sjl139090 	nodev,		/* ioctl */
86*1772Sjl139090 	nodev,		/* devmap */
87*1772Sjl139090 	nodev,		/* mmap */
88*1772Sjl139090 	nodev,		/* segmap */
89*1772Sjl139090 	nochpoll,	/* poll */
90*1772Sjl139090 	nodev,		/* prop_op */
91*1772Sjl139090 	NULL,		/* streamtab */
92*1772Sjl139090 	D_NEW | D_MP | D_HOTPLUG,	/* flag */
93*1772Sjl139090 	CB_REV,		/* cb_rev */
94*1772Sjl139090 	nodev,		/* async I/O read entry point */
95*1772Sjl139090 	nodev		/* async I/O write entry point */
96*1772Sjl139090 };
97*1772Sjl139090 
98*1772Sjl139090 static struct dev_ops panel_dev_ops = {
99*1772Sjl139090 	DEVO_REV,		/* driver build version */
100*1772Sjl139090 	0,			/* device reference count */
101*1772Sjl139090 	panel_getinfo,		/* getinfo */
102*1772Sjl139090 	nulldev,		/* identify */
103*1772Sjl139090 	nulldev,		/* probe */
104*1772Sjl139090 	panel_attach,		/* attach */
105*1772Sjl139090 	panel_detach,		/* detach */
106*1772Sjl139090 	nulldev,		/* reset */
107*1772Sjl139090 	&panel_cb_ops,		/* cb_ops */
108*1772Sjl139090 	NULL,			/* bus_ops */
109*1772Sjl139090 	nulldev			/* power */
110*1772Sjl139090 };
111*1772Sjl139090 
112*1772Sjl139090 /* module configuration stuff */
113*1772Sjl139090 static void		*panelstates;
114*1772Sjl139090 extern struct mod_ops	mod_driverops;
115*1772Sjl139090 
116*1772Sjl139090 static struct modldrv modldrv = {
117*1772Sjl139090 	&mod_driverops,
118*1772Sjl139090 	"OPL panel driver %I%",
119*1772Sjl139090 	&panel_dev_ops
120*1772Sjl139090 };
121*1772Sjl139090 
122*1772Sjl139090 static struct modlinkage modlinkage = {
123*1772Sjl139090 	MODREV_1,
124*1772Sjl139090 	&modldrv,
125*1772Sjl139090 	0
126*1772Sjl139090 };
127*1772Sjl139090 
128*1772Sjl139090 
129*1772Sjl139090 int
130*1772Sjl139090 _init(void)
131*1772Sjl139090 {
132*1772Sjl139090 	int	status;
133*1772Sjl139090 
134*1772Sjl139090 	DCMN_ERR((CE_CONT, "%s: _init\n", panel_name));
135*1772Sjl139090 
136*1772Sjl139090 	status = ddi_soft_state_init(&panelstates,
137*1772Sjl139090 	    sizeof (struct panel_state), 0);
138*1772Sjl139090 	if (status != 0) {
139*1772Sjl139090 		cmn_err(CE_WARN, "%s: ddi_soft_state_init failed.",
140*1772Sjl139090 		    panel_name);
141*1772Sjl139090 		return (status);
142*1772Sjl139090 	}
143*1772Sjl139090 
144*1772Sjl139090 	status = mod_install(&modlinkage);
145*1772Sjl139090 	if (status != 0) {
146*1772Sjl139090 		ddi_soft_state_fini(&panelstates);
147*1772Sjl139090 	}
148*1772Sjl139090 
149*1772Sjl139090 	return (status);
150*1772Sjl139090 }
151*1772Sjl139090 
152*1772Sjl139090 int
153*1772Sjl139090 _fini(void)
154*1772Sjl139090 {
155*1772Sjl139090 	/*
156*1772Sjl139090 	 * Can't unload to make sure the panel switch always works.
157*1772Sjl139090 	 */
158*1772Sjl139090 	return (EBUSY);
159*1772Sjl139090 }
160*1772Sjl139090 
161*1772Sjl139090 int
162*1772Sjl139090 _info(struct modinfo *modinfop)
163*1772Sjl139090 {
164*1772Sjl139090 	return (mod_info(&modlinkage, modinfop));
165*1772Sjl139090 }
166*1772Sjl139090 
167*1772Sjl139090 static int
168*1772Sjl139090 panel_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
169*1772Sjl139090 {
170*1772Sjl139090 
171*1772Sjl139090 	int instance;
172*1772Sjl139090 	struct panel_state *statep = NULL;
173*1772Sjl139090 
174*1772Sjl139090 	ddi_device_acc_attr_t access_attr = {
175*1772Sjl139090 		DDI_DEVICE_ATTR_V0,
176*1772Sjl139090 		DDI_STRUCTURE_BE_ACC,
177*1772Sjl139090 		DDI_STRICTORDER_ACC
178*1772Sjl139090 	};
179*1772Sjl139090 
180*1772Sjl139090 	instance = ddi_get_instance(dip);
181*1772Sjl139090 
182*1772Sjl139090 	DCMN_ERR((CE_CONT, "%s%d: attach\n", panel_name, instance));
183*1772Sjl139090 
184*1772Sjl139090 	switch (cmd) {
185*1772Sjl139090 	case DDI_ATTACH:
186*1772Sjl139090 		DCMN_ERR((CE_CONT, "%s%d: DDI_ATTACH\n",
187*1772Sjl139090 		    panel_name, instance));
188*1772Sjl139090 		break;
189*1772Sjl139090 
190*1772Sjl139090 	case DDI_RESUME:
191*1772Sjl139090 		DCMN_ERR((CE_CONT, "%s%d: DDI_RESUME\n",
192*1772Sjl139090 		    panel_name, instance));
193*1772Sjl139090 
194*1772Sjl139090 		if ((statep = (struct panel_state *)
195*1772Sjl139090 		    ddi_get_soft_state(panelstates, instance)) == NULL) {
196*1772Sjl139090 			cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.",
197*1772Sjl139090 			    panel_name, instance);
198*1772Sjl139090 			return (DDI_FAILURE);
199*1772Sjl139090 		}
200*1772Sjl139090 
201*1772Sjl139090 		/* enable the interrupt just in case */
202*1772Sjl139090 		panel_ddi_put8(statep->panel_regs_handle, statep->panelregs,
203*1772Sjl139090 		    statep->panelregs_state);
204*1772Sjl139090 		return (DDI_SUCCESS);
205*1772Sjl139090 
206*1772Sjl139090 	default:
207*1772Sjl139090 		return (DDI_FAILURE);
208*1772Sjl139090 	}
209*1772Sjl139090 
210*1772Sjl139090 	/*
211*1772Sjl139090 	 * Attach routine
212*1772Sjl139090 	 */
213*1772Sjl139090 
214*1772Sjl139090 	/* alloc and get soft state */
215*1772Sjl139090 	if (ddi_soft_state_zalloc(panelstates, instance) != DDI_SUCCESS) {
216*1772Sjl139090 		cmn_err(CE_WARN, "%s%d: ddi_soft_state_zalloc failed.",
217*1772Sjl139090 		    panel_name, instance);
218*1772Sjl139090 		goto attach_failed2;
219*1772Sjl139090 	}
220*1772Sjl139090 	if ((statep = (struct panel_state *)
221*1772Sjl139090 	    ddi_get_soft_state(panelstates, instance)) == NULL) {
222*1772Sjl139090 		cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.",
223*1772Sjl139090 		    panel_name, instance);
224*1772Sjl139090 		goto attach_failed1;
225*1772Sjl139090 	}
226*1772Sjl139090 
227*1772Sjl139090 	/* set the dip in the soft state */
228*1772Sjl139090 	statep->dip = dip;
229*1772Sjl139090 
230*1772Sjl139090 	/* mapping register */
231*1772Sjl139090 	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&statep->panelregs,
232*1772Sjl139090 	    0, 0, /* the entire space is mapped */
233*1772Sjl139090 	    &access_attr, &statep->panel_regs_handle) != DDI_SUCCESS) {
234*1772Sjl139090 		cmn_err(CE_WARN, "%s%d: ddi_regs_map_setup failed.",
235*1772Sjl139090 		    panel_name, instance);
236*1772Sjl139090 		goto attach_failed1;
237*1772Sjl139090 	}
238*1772Sjl139090 
239*1772Sjl139090 	/* setup the interrupt handler */
240*1772Sjl139090 	ddi_get_iblock_cookie(dip, 0, &statep->iblock_cookie);
241*1772Sjl139090 	if (ddi_add_intr(dip, 0, &statep->iblock_cookie, 0, &panel_intr,
242*1772Sjl139090 	    (caddr_t)statep) != DDI_SUCCESS) {
243*1772Sjl139090 		cmn_err(CE_WARN, "%s%d: cannot add interrupt handler.",
244*1772Sjl139090 		    panel_name, instance);
245*1772Sjl139090 		goto attach_failed0;
246*1772Sjl139090 	}
247*1772Sjl139090 
248*1772Sjl139090 	/* ATTACH SUCCESS */
249*1772Sjl139090 
250*1772Sjl139090 	/* announce the device */
251*1772Sjl139090 	ddi_report_dev(dip);
252*1772Sjl139090 
253*1772Sjl139090 	/* turn on interrupt */
254*1772Sjl139090 	statep->panelregs_state = 0 | PNLIE_MASK;
255*1772Sjl139090 	panel_ddi_put8(statep->panel_regs_handle, statep->panelregs,
256*1772Sjl139090 	    statep->panelregs_state);
257*1772Sjl139090 
258*1772Sjl139090 	return (DDI_SUCCESS);
259*1772Sjl139090 
260*1772Sjl139090 attach_failed0:
261*1772Sjl139090 	ddi_regs_map_free(&statep->panel_regs_handle);
262*1772Sjl139090 attach_failed1:
263*1772Sjl139090 	ddi_soft_state_free(panelstates, instance);
264*1772Sjl139090 attach_failed2:
265*1772Sjl139090 	DCMN_ERR((CE_NOTE, "%s%d: attach failed", panel_name, instance));
266*1772Sjl139090 	return (DDI_FAILURE);
267*1772Sjl139090 }
268*1772Sjl139090 
269*1772Sjl139090 static int
270*1772Sjl139090 panel_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
271*1772Sjl139090 {
272*1772Sjl139090 	int instance;
273*1772Sjl139090 	struct panel_state *statep;
274*1772Sjl139090 
275*1772Sjl139090 	instance = ddi_get_instance(dip);
276*1772Sjl139090 
277*1772Sjl139090 	DCMN_ERR((CE_CONT, "%s%d: detach\n", panel_name, instance));
278*1772Sjl139090 
279*1772Sjl139090 	if ((statep = (struct panel_state *)
280*1772Sjl139090 	    ddi_get_soft_state(panelstates, instance)) == NULL) {
281*1772Sjl139090 		cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.",
282*1772Sjl139090 		    panel_name, instance);
283*1772Sjl139090 		return (DDI_FAILURE);
284*1772Sjl139090 	}
285*1772Sjl139090 
286*1772Sjl139090 	switch (cmd) {
287*1772Sjl139090 	case DDI_DETACH:
288*1772Sjl139090 		DCMN_ERR((CE_CONT, "%s%d: DDI_DETACH\n",
289*1772Sjl139090 		    panel_name, instance));
290*1772Sjl139090 
291*1772Sjl139090 		/* turn off interrupt */
292*1772Sjl139090 		statep->panelregs_state &= ~PNLIE_MASK;
293*1772Sjl139090 		panel_ddi_put8(statep->panel_regs_handle, statep->panelregs,
294*1772Sjl139090 		    statep->panelregs_state);
295*1772Sjl139090 
296*1772Sjl139090 		/* free all resources for the dip */
297*1772Sjl139090 		ddi_remove_intr(dip, 0, statep->iblock_cookie);
298*1772Sjl139090 
299*1772Sjl139090 		/* need not free iblock_cookie */
300*1772Sjl139090 		ddi_regs_map_free(&statep->panel_regs_handle);
301*1772Sjl139090 		ddi_soft_state_free(panelstates, instance);
302*1772Sjl139090 
303*1772Sjl139090 		return (DDI_SUCCESS);
304*1772Sjl139090 
305*1772Sjl139090 	case DDI_SUSPEND:
306*1772Sjl139090 		DCMN_ERR((CE_CONT, "%s%d: DDI_SUSPEND\n",
307*1772Sjl139090 		    panel_name, instance));
308*1772Sjl139090 		return (DDI_SUCCESS);
309*1772Sjl139090 
310*1772Sjl139090 	default:
311*1772Sjl139090 		return (DDI_FAILURE);
312*1772Sjl139090 
313*1772Sjl139090 	}
314*1772Sjl139090 	/* Not reached */
315*1772Sjl139090 }
316*1772Sjl139090 
317*1772Sjl139090 /*ARGSUSED*/
318*1772Sjl139090 static int
319*1772Sjl139090 panel_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,  void **resultp)
320*1772Sjl139090 {
321*1772Sjl139090 	struct panel_state *statep;
322*1772Sjl139090 	int	instance;
323*1772Sjl139090 	dev_t	dev = (dev_t)arg;
324*1772Sjl139090 
325*1772Sjl139090 	instance = getminor(dev);
326*1772Sjl139090 
327*1772Sjl139090 	DCMN_ERR((CE_CONT, "%s%d: getinfo\n", panel_name, instance));
328*1772Sjl139090 
329*1772Sjl139090 	switch (cmd) {
330*1772Sjl139090 	case DDI_INFO_DEVT2DEVINFO:
331*1772Sjl139090 		if ((statep = (struct panel_state *)
332*1772Sjl139090 		    ddi_get_soft_state(panelstates, instance)) == NULL) {
333*1772Sjl139090 			cmn_err(CE_WARN, "%s%d: ddi_get_soft_state failed.",
334*1772Sjl139090 			    panel_name, instance);
335*1772Sjl139090 			*resultp = NULL;
336*1772Sjl139090 			return (DDI_FAILURE);
337*1772Sjl139090 		}
338*1772Sjl139090 		*resultp = statep->dip;
339*1772Sjl139090 		break;
340*1772Sjl139090 	case DDI_INFO_DEVT2INSTANCE:
341*1772Sjl139090 		*resultp = (void *)(uintptr_t)instance;
342*1772Sjl139090 		break;
343*1772Sjl139090 	default:
344*1772Sjl139090 		return (DDI_FAILURE);
345*1772Sjl139090 	}
346*1772Sjl139090 
347*1772Sjl139090 	return (DDI_SUCCESS);
348*1772Sjl139090 }
349*1772Sjl139090 
350*1772Sjl139090 static  uint_t
351*1772Sjl139090 panel_intr(caddr_t arg)
352*1772Sjl139090 {
353*1772Sjl139090 	int	instance;
354*1772Sjl139090 	struct panel_state *statep = (struct panel_state *)arg;
355*1772Sjl139090 
356*1772Sjl139090 	instance = ddi_get_instance(statep->dip);
357*1772Sjl139090 
358*1772Sjl139090 	DCMN_ERR((CE_CONT, "%s%d: intr\n", panel_name, instance));
359*1772Sjl139090 
360*1772Sjl139090 	/* to confirm the validity of the interrupt */
361*1772Sjl139090 	if (!(ddi_get8(statep->panel_regs_handle, statep->panelregs) &
362*1772Sjl139090 	    PNLINT_MASK)) {
363*1772Sjl139090 		cmn_err(CE_WARN, "%s%d: spurious interrupt detected.",
364*1772Sjl139090 		    panel_name, instance);
365*1772Sjl139090 		return (DDI_INTR_UNCLAIMED);
366*1772Sjl139090 	}
367*1772Sjl139090 
368*1772Sjl139090 	/* clear the PNLINT bit */
369*1772Sjl139090 	panel_ddi_put8(statep->panel_regs_handle, statep->panelregs,
370*1772Sjl139090 	    statep->panelregs_state | PNLINT_MASK);
371*1772Sjl139090 
372*1772Sjl139090 	if (panel_enable) {
373*1772Sjl139090 		/* avoid double panic */
374*1772Sjl139090 		panel_enable 	= 0;
375*1772Sjl139090 
376*1772Sjl139090 		/*
377*1772Sjl139090 		 * Re-enqueue the cpc interrupt handler for PIL15 here since we
378*1772Sjl139090 		 * are not unwinding back to the interrupt handler subsystem.
379*1772Sjl139090 		 * This is to allow potential cpc overflow interrupts to
380*1772Sjl139090 		 * function while we go thru the panic flow. Note that this
381*1772Sjl139090 		 * logic could be implemented in panic_enter_hw(), we do
382*1772Sjl139090 		 * it here for now as it is less risky. This particular
383*1772Sjl139090 		 * condition is only specific to OPL hardware and we want
384*1772Sjl139090 		 * to minimize exposure of this new logic to other existing
385*1772Sjl139090 		 * platforms.
386*1772Sjl139090 		 */
387*1772Sjl139090 		intr_enqueue_req(PIL_15, cpc_level15_inum);
388*1772Sjl139090 
389*1772Sjl139090 		cmn_err(CE_PANIC,
390*1772Sjl139090 		    "System Panel Driver: Emergency panic request "
391*1772Sjl139090 		    "detected!");
392*1772Sjl139090 		/* Not reached */
393*1772Sjl139090 	}
394*1772Sjl139090 
395*1772Sjl139090 	return (DDI_INTR_CLAIMED);
396*1772Sjl139090 }
397*1772Sjl139090 
398*1772Sjl139090 #ifdef DEBUG
399*1772Sjl139090 static void
400*1772Sjl139090 panel_ddi_put8(ddi_acc_handle_t handle, uint8_t *dev_addr, uint8_t value)
401*1772Sjl139090 {
402*1772Sjl139090 	if (panel_debug) {
403*1772Sjl139090 		cmn_err(CE_CONT, "%s: old value = 0x%x\n",
404*1772Sjl139090 		    panel_name, ddi_get8(handle, dev_addr));
405*1772Sjl139090 		cmn_err(CE_CONT, "%s: writing value = 0x%x\n",
406*1772Sjl139090 		    panel_name, value);
407*1772Sjl139090 		ddi_put8(handle, dev_addr, value);
408*1772Sjl139090 		cmn_err(CE_CONT, "%s: new value = 0x%x\n",
409*1772Sjl139090 		    panel_name, ddi_get8(handle, dev_addr));
410*1772Sjl139090 	} else {
411*1772Sjl139090 		ddi_put8(handle, dev_addr, value);
412*1772Sjl139090 	}
413*1772Sjl139090 }
414*1772Sjl139090 #endif
415