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