xref: /illumos-gate/usr/src/uts/sun4u/io/epic.c (revision 3d393ee6c37fa10ac512ed6d36109ad616dc7c1a)
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
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
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
162 _info(struct modinfo *modinfop)
163 {
164 	return (mod_info(&modlinkage, modinfop));
165 }
166 
167 /*ARGSUSED*/
168 static int
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
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
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
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
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
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