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