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