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