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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27
28 /*
29 * Driver to control Alert and Power LEDs for the Seattle platform.
30 * Alert LED is also known as Service (required).
31 * Power LED is also known as Activity.
32 */
33 #include <sys/types.h>
34 #include <sys/time.h>
35 #include <sys/errno.h>
36 #include <sys/cmn_err.h>
37 #include <sys/param.h>
38 #include <sys/modctl.h>
39 #include <sys/conf.h>
40 #include <sys/open.h>
41 #include <sys/stat.h>
42 #include <sys/clock.h>
43 #include <sys/ddi.h>
44 #include <sys/sunddi.h>
45 #include <sys/file.h>
46 #include <sys/note.h>
47 #include <sys/epic.h>
48
49
50 /*
51 * Some #defs that must be here as they differ for power.c
52 * and epic.c
53 */
54 #define EPIC_REGS_OFFSET 0x00
55 #define EPIC_REGS_LEN 0x80
56
57 #define EPIC_IND_DATA 0x40
58 #define EPIC_IND_ADDR 0x41
59 #define EPIC_WRITE_MASK 0x80
60
61 /* dev_ops and cb_ops entry point function declarations */
62 static int epic_attach(dev_info_t *, ddi_attach_cmd_t);
63 static int epic_detach(dev_info_t *, ddi_detach_cmd_t);
64 static int epic_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
65 static int epic_open(dev_t *, int, int, cred_t *);
66 static int epic_close(dev_t, int, int, cred_t *);
67 static int epic_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
68
69 struct cb_ops epic_cb_ops = {
70 epic_open, /* open */
71 epic_close, /* close */
72 nodev, /* strategy */
73 nodev, /* print */
74 nodev, /* dump */
75 nodev, /* read */
76 nodev, /* write */
77 epic_ioctl, /* ioctl */
78 nodev, /* devmap */
79 nodev, /* mmap */
80 ddi_segmap, /* segmap */
81 nochpoll, /* poll */
82 ddi_prop_op, /* cb_prop_op */
83 NULL, /* streamtab - for STREAMS drivers */
84 D_NEW | D_MP /* driver compatibility flag */
85 };
86
87 static struct dev_ops epic_dev_ops = {
88 DEVO_REV, /* driver build version */
89 0, /* device reference count */
90 epic_getinfo,
91 nulldev,
92 nulldev, /* probe */
93 epic_attach,
94 epic_detach,
95 nulldev, /* reset */
96 &epic_cb_ops,
97 (struct bus_ops *)NULL,
98 nulldev, /* power */
99 ddi_quiesce_not_supported, /* devo_quiesce */
100 };
101
102
103 /*
104 * Soft state
105 */
106 struct epic_softc {
107 dev_info_t *dip;
108 kmutex_t mutex;
109 uint8_t *cmd_reg;
110 ddi_acc_handle_t cmd_handle;
111 };
112
113 #define getsoftc(inst) ((struct epic_softc *)ddi_get_soft_state(statep, \
114 (inst)))
115
116 /* module configuration stuff */
117 static void *statep;
118 extern struct mod_ops mod_driverops;
119
120 static struct modldrv modldrv = {
121 &mod_driverops,
122 "epic_client driver",
123 &epic_dev_ops
124 };
125
126 static struct modlinkage modlinkage = {
127 MODREV_1,
128 &modldrv,
129 0
130 };
131
132 int
_init(void)133 _init(void)
134 {
135 int e;
136
137 if ((e = ddi_soft_state_init(&statep,
138 sizeof (struct epic_softc), 0)) != 0) {
139 return (e);
140 }
141
142 if ((e = mod_install(&modlinkage)) != 0)
143 ddi_soft_state_fini(&statep);
144
145 return (e);
146 }
147
148 int
_fini(void)149 _fini(void)
150 {
151 int e;
152
153 if ((e = mod_remove(&modlinkage)) != 0)
154 return (e);
155
156 ddi_soft_state_fini(&statep);
157
158 return (DDI_SUCCESS);
159 }
160
161 int
_info(struct modinfo * modinfop)162 _info(struct modinfo *modinfop)
163 {
164 return (mod_info(&modlinkage, modinfop));
165 }
166
167 /*ARGSUSED*/
168 static int
epic_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)169 epic_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
170 {
171 int inst;
172 int retval = DDI_SUCCESS;
173 struct epic_softc *softc;
174
175 inst = (getminor((dev_t)arg));
176
177 switch (cmd) {
178 case DDI_INFO_DEVT2DEVINFO:
179 if ((softc = getsoftc(inst)) == NULL) {
180 *result = (void *)NULL;
181 retval = DDI_FAILURE;
182 } else
183 *result = (void *)softc->dip;
184 break;
185
186 case DDI_INFO_DEVT2INSTANCE:
187 *result = (void *)(uintptr_t)inst;
188 break;
189
190 default:
191 retval = DDI_FAILURE;
192 }
193
194 return (retval);
195 }
196
197 static int
epic_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)198 epic_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
199 {
200 int inst;
201 struct epic_softc *softc = NULL;
202 int minor;
203 char name[MAXNAMELEN];
204 ddi_device_acc_attr_t dev_attr;
205 int res;
206
207 switch (cmd) {
208 case DDI_ATTACH:
209 inst = ddi_get_instance(dip);
210 (void) sprintf(name, "env-monitor%d", inst);
211 minor = inst;
212 if (ddi_create_minor_node(dip, name, S_IFCHR, minor,
213 DDI_PSEUDO, NULL) == DDI_FAILURE) {
214 cmn_err(CE_WARN,
215 "ddi_create_minor_node() failed for inst %d\n",
216 inst);
217 return (DDI_FAILURE);
218 }
219
220 /* Allocate a soft state structure for this instance */
221 if (ddi_soft_state_zalloc(statep, inst) != DDI_SUCCESS) {
222 cmn_err(CE_WARN, " ddi_soft_state_zalloc() failed "
223 "for inst %d\n", inst);
224 break;
225 }
226
227 /* Setup soft state */
228 if ((softc = getsoftc(inst)) == NULL) {
229 break;
230 }
231 softc->dip = dip;
232 mutex_init(&softc->mutex, NULL, MUTEX_DRIVER, NULL);
233
234 /* Setup device attributes */
235 dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
236 dev_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
237 dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
238
239 res = ddi_regs_map_setup(dip, 0, (caddr_t *)&softc->cmd_reg,
240 EPIC_REGS_OFFSET, EPIC_REGS_LEN, &dev_attr,
241 &softc->cmd_handle);
242
243 if (res != DDI_SUCCESS) {
244 cmn_err(CE_WARN, "ddi_regs_map_setup() failed\n");
245 break;
246 }
247
248 ddi_report_dev(dip);
249
250
251 return (DDI_SUCCESS);
252
253 case DDI_RESUME:
254 return (DDI_SUCCESS);
255
256 default:
257 return (DDI_FAILURE);
258 }
259
260 /* Attach failed */
261 /* Free soft state, if allocated. remove minor node if added earlier */
262 if (softc)
263 ddi_soft_state_free(statep, inst);
264
265 ddi_remove_minor_node(dip, NULL);
266
267 return (DDI_FAILURE);
268 }
269
270 static int
epic_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)271 epic_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
272 {
273 int inst;
274 struct epic_softc *softc;
275
276 switch (cmd) {
277 case DDI_DETACH:
278 inst = ddi_get_instance(dip);
279 if ((softc = getsoftc(inst)) == NULL)
280 return (ENXIO);
281
282 (void) ddi_regs_map_free(&softc->cmd_handle);
283
284
285 /* Free the soft state and remove minor node added earlier */
286 mutex_destroy(&softc->mutex);
287 ddi_soft_state_free(statep, inst);
288 ddi_remove_minor_node(dip, NULL);
289 return (DDI_SUCCESS);
290
291 case DDI_SUSPEND:
292 return (DDI_SUCCESS);
293
294 default:
295 return (DDI_FAILURE);
296 }
297 }
298
299 /*ARGSUSED*/
300 static int
epic_open(dev_t * devp,int flag,int otyp,cred_t * credp)301 epic_open(dev_t *devp, int flag, int otyp, cred_t *credp)
302 {
303 _NOTE(ARGUNUSED(flag))
304 _NOTE(ARGUNUSED(otyp))
305 _NOTE(ARGUNUSED(credp))
306
307 int inst = getminor(*devp);
308
309 return (getsoftc(inst) == NULL ? ENXIO : 0);
310 }
311
312 /*ARGSUSED*/
313 static int
epic_close(dev_t dev,int flag,int otyp,cred_t * credp)314 epic_close(dev_t dev, int flag, int otyp, cred_t *credp)
315 {
316 _NOTE(ARGUNUSED(flag))
317 _NOTE(ARGUNUSED(otyp))
318 _NOTE(ARGUNUSED(credp))
319
320 int inst = getminor(dev);
321
322 return (getsoftc(inst) == NULL ? ENXIO : 0);
323 }
324
325 /*ARGSUSED*/
326 static int
epic_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)327 epic_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
328 int *rvalp)
329 {
330 _NOTE(ARGUNUSED(credp))
331
332 int inst;
333 struct epic_softc *softc;
334 uint8_t in_command;
335
336 inst = getminor(dev);
337 if ((softc = getsoftc(inst)) == NULL)
338 return (ENXIO);
339
340 mutex_enter(&softc->mutex);
341
342 switch (cmd) {
343 case EPIC_SET_POWER_LED:
344 EPIC_WRITE(softc->cmd_handle, softc->cmd_reg,
345 EPIC_IND_LED_STATE0, EPIC_POWER_LED_MASK,
346 EPIC_POWER_LED_ON);
347 break;
348 case EPIC_RESET_POWER_LED:
349 EPIC_WRITE(softc->cmd_handle, softc->cmd_reg,
350 EPIC_IND_LED_STATE0, EPIC_POWER_LED_MASK,
351 EPIC_POWER_LED_OFF);
352 break;
353 case EPIC_SB_BL_POWER_LED:
354 EPIC_WRITE(softc->cmd_handle, softc->cmd_reg,
355 EPIC_IND_LED_STATE0, EPIC_POWER_LED_MASK,
356 EPIC_POWER_LED_SB_BLINK);
357 break;
358 case EPIC_FAST_BL_POWER_LED:
359 EPIC_WRITE(softc->cmd_handle, softc->cmd_reg,
360 EPIC_IND_LED_STATE0, EPIC_POWER_LED_MASK,
361 EPIC_POWER_LED_FAST_BLINK);
362 break;
363 case EPIC_SET_ALERT_LED:
364 EPIC_WRITE(softc->cmd_handle, softc->cmd_reg,
365 EPIC_IND_LED_STATE0, EPIC_ALERT_LED_MASK,
366 EPIC_ALERT_LED_ON);
367 break;
368 case EPIC_RESET_ALERT_LED:
369 EPIC_WRITE(softc->cmd_handle, softc->cmd_reg,
370 EPIC_IND_LED_STATE0, EPIC_ALERT_LED_MASK,
371 EPIC_ALERT_LED_OFF);
372 break;
373 case EPIC_GET_FW:
374 EPIC_READ(softc->cmd_handle, softc->cmd_reg,
375 in_command, EPIC_IND_FW_VERSION);
376 if (ddi_copyout((void *)(&in_command), (void *)arg,
377 sizeof (in_command), mode) != DDI_SUCCESS) {
378 mutex_exit(&softc->mutex);
379 return (EFAULT);
380 }
381 break;
382 default:
383 mutex_exit(&softc->mutex);
384 cmn_err(CE_WARN, "epic: cmd %d is not valid", cmd);
385 return (EINVAL);
386 }
387
388 mutex_exit(&softc->mutex);
389 return (0);
390 }
391