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