xref: /onnv-gate/usr/src/uts/sun4u/io/epic.c (revision 7656:2621e50fdf4a)
1920Sjbeloro /*
2920Sjbeloro  * CDDL HEADER START
3920Sjbeloro  *
4920Sjbeloro  * The contents of this file are subject to the terms of the
5*7656SSherry.Moore@Sun.COM  * Common Development and Distribution License (the "License").
6*7656SSherry.Moore@Sun.COM  * You may not use this file except in compliance with the License.
7920Sjbeloro  *
8920Sjbeloro  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9920Sjbeloro  * or http://www.opensolaris.org/os/licensing.
10920Sjbeloro  * See the License for the specific language governing permissions
11920Sjbeloro  * and limitations under the License.
12920Sjbeloro  *
13920Sjbeloro  * When distributing Covered Code, include this CDDL HEADER in each
14920Sjbeloro  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15920Sjbeloro  * If applicable, add the following below this CDDL HEADER, with the
16920Sjbeloro  * fields enclosed by brackets "[]" replaced with your own identifying
17920Sjbeloro  * information: Portions Copyright [yyyy] [name of copyright owner]
18920Sjbeloro  *
19920Sjbeloro  * CDDL HEADER END
20920Sjbeloro  */
21920Sjbeloro 
22920Sjbeloro /*
23*7656SSherry.Moore@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24920Sjbeloro  * Use is subject to license terms.
25920Sjbeloro  */
26920Sjbeloro 
27920Sjbeloro 
28920Sjbeloro /*
29920Sjbeloro  * Driver to control Alert and Power LEDs  for the Seattle platform.
30920Sjbeloro  * Alert LED is also known as Service (required).
31920Sjbeloro  * Power LED is also known as Activity.
32920Sjbeloro  */
33920Sjbeloro #include <sys/types.h>
34920Sjbeloro #include <sys/time.h>
35920Sjbeloro #include <sys/errno.h>
36920Sjbeloro #include <sys/cmn_err.h>
37920Sjbeloro #include <sys/param.h>
38920Sjbeloro #include <sys/modctl.h>
39920Sjbeloro #include <sys/conf.h>
40920Sjbeloro #include <sys/open.h>
41920Sjbeloro #include <sys/stat.h>
42920Sjbeloro #include <sys/clock.h>
43920Sjbeloro #include <sys/ddi.h>
44920Sjbeloro #include <sys/sunddi.h>
45920Sjbeloro #include <sys/file.h>
46920Sjbeloro #include <sys/note.h>
47920Sjbeloro #include <sys/epic.h>
48920Sjbeloro 
49920Sjbeloro 
50920Sjbeloro /*
51920Sjbeloro  * Some #defs that must be here as they differ for power.c
52920Sjbeloro  * and epic.c
53920Sjbeloro  */
54920Sjbeloro #define	EPIC_REGS_OFFSET	0x00
55920Sjbeloro #define	EPIC_REGS_LEN		0x80
56920Sjbeloro 
57920Sjbeloro #define	EPIC_IND_DATA		0x40
58920Sjbeloro #define	EPIC_IND_ADDR		0x41
59920Sjbeloro #define	EPIC_WRITE_MASK		0x80
60920Sjbeloro 
61920Sjbeloro /* dev_ops and cb_ops entry point function declarations */
62920Sjbeloro static int	epic_attach(dev_info_t *, ddi_attach_cmd_t);
63920Sjbeloro static int	epic_detach(dev_info_t *, ddi_detach_cmd_t);
64920Sjbeloro static int	epic_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
65920Sjbeloro static int	epic_open(dev_t *, int, int, cred_t *);
66920Sjbeloro static int	epic_close(dev_t, int, int, cred_t *);
67920Sjbeloro static int	epic_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
68920Sjbeloro 
69920Sjbeloro struct cb_ops epic_cb_ops = {
70920Sjbeloro 	epic_open,		/* open */
71920Sjbeloro 	epic_close,		/* close */
72920Sjbeloro 	nodev,			/* strategy */
73920Sjbeloro 	nodev,			/* print */
74920Sjbeloro 	nodev,			/* dump */
75920Sjbeloro 	nodev,			/* read */
76920Sjbeloro 	nodev,			/* write */
77920Sjbeloro 	epic_ioctl,		/* ioctl */
78920Sjbeloro 	nodev,			/* devmap */
79920Sjbeloro 	nodev,			/* mmap */
80920Sjbeloro 	ddi_segmap,		/* segmap */
81920Sjbeloro 	nochpoll,		/* poll */
82920Sjbeloro 	ddi_prop_op,		/* cb_prop_op */
83920Sjbeloro 	NULL,			/* streamtab - for STREAMS drivers */
84920Sjbeloro 	D_NEW | D_MP		/* driver compatibility flag */
85920Sjbeloro };
86920Sjbeloro 
87920Sjbeloro static struct dev_ops epic_dev_ops = {
88920Sjbeloro 	DEVO_REV,		/* driver build version */
89920Sjbeloro 	0,			/* device reference count */
90920Sjbeloro 	epic_getinfo,
91920Sjbeloro 	nulldev,
92920Sjbeloro 	nulldev,		/* probe */
93920Sjbeloro 	epic_attach,
94920Sjbeloro 	epic_detach,
95920Sjbeloro 	nulldev,		/* reset */
96920Sjbeloro 	&epic_cb_ops,
97920Sjbeloro 	(struct bus_ops *)NULL,
98*7656SSherry.Moore@Sun.COM 	nulldev,		/* power */
99*7656SSherry.Moore@Sun.COM 	ddi_quiesce_not_supported,	/* devo_quiesce */
100920Sjbeloro };
101920Sjbeloro 
102920Sjbeloro 
103920Sjbeloro /*
104920Sjbeloro  * Soft state
105920Sjbeloro  */
106920Sjbeloro struct epic_softc {
107920Sjbeloro 	dev_info_t	*dip;
108920Sjbeloro 	kmutex_t	mutex;
109920Sjbeloro 	uint8_t		*cmd_reg;
110920Sjbeloro 	ddi_acc_handle_t cmd_handle;
111920Sjbeloro };
112920Sjbeloro 
113920Sjbeloro #define	getsoftc(inst)	((struct epic_softc *)ddi_get_soft_state(statep, \
114920Sjbeloro (inst)))
115920Sjbeloro 
116920Sjbeloro /* module configuration stuff */
117920Sjbeloro static void    *statep;
118920Sjbeloro extern struct mod_ops mod_driverops;
119920Sjbeloro 
120920Sjbeloro static struct modldrv modldrv = {
121920Sjbeloro 	&mod_driverops,
122*7656SSherry.Moore@Sun.COM 	"epic_client driver",
123920Sjbeloro 	&epic_dev_ops
124920Sjbeloro };
125920Sjbeloro 
126920Sjbeloro static struct modlinkage modlinkage = {
127920Sjbeloro 	MODREV_1,
128920Sjbeloro 	&modldrv,
129920Sjbeloro 	0
130920Sjbeloro };
131920Sjbeloro 
132920Sjbeloro int
_init(void)133920Sjbeloro _init(void)
134920Sjbeloro {
135920Sjbeloro 	int e;
136920Sjbeloro 
137920Sjbeloro 	if ((e = ddi_soft_state_init(&statep,
138920Sjbeloro 		sizeof (struct epic_softc), 0)) != 0) {
139920Sjbeloro 		return (e);
140920Sjbeloro 	}
141920Sjbeloro 
142920Sjbeloro 	if ((e = mod_install(&modlinkage)) != 0)
143920Sjbeloro 		ddi_soft_state_fini(&statep);
144920Sjbeloro 
145920Sjbeloro 	return (e);
146920Sjbeloro }
147920Sjbeloro 
148920Sjbeloro int
_fini(void)149920Sjbeloro _fini(void)
150920Sjbeloro {
151920Sjbeloro 	int e;
152920Sjbeloro 
153920Sjbeloro 	if ((e = mod_remove(&modlinkage)) != 0)
154920Sjbeloro 		return (e);
155920Sjbeloro 
156920Sjbeloro 	ddi_soft_state_fini(&statep);
157920Sjbeloro 
158920Sjbeloro 	return (DDI_SUCCESS);
159920Sjbeloro }
160920Sjbeloro 
161920Sjbeloro int
_info(struct modinfo * modinfop)162920Sjbeloro _info(struct modinfo *modinfop)
163920Sjbeloro {
164920Sjbeloro 	return (mod_info(&modlinkage, modinfop));
165920Sjbeloro }
166920Sjbeloro 
167920Sjbeloro /*ARGSUSED*/
168920Sjbeloro static int
epic_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)169920Sjbeloro epic_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
170920Sjbeloro {
171920Sjbeloro 	int	inst;
172920Sjbeloro 	int	retval = DDI_SUCCESS;
173920Sjbeloro 	struct epic_softc *softc;
174920Sjbeloro 
175920Sjbeloro 	inst = (getminor((dev_t)arg));
176920Sjbeloro 
177920Sjbeloro 	switch (cmd) {
178920Sjbeloro 	case DDI_INFO_DEVT2DEVINFO:
179920Sjbeloro 		if ((softc = getsoftc(inst)) == NULL) {
180920Sjbeloro 			*result = (void *)NULL;
181920Sjbeloro 			retval = DDI_FAILURE;
182920Sjbeloro 		} else
183920Sjbeloro 			*result = (void *)softc->dip;
184920Sjbeloro 		break;
185920Sjbeloro 
186920Sjbeloro 	case DDI_INFO_DEVT2INSTANCE:
187969Sanovick 		*result = (void *)(uintptr_t)inst;
188920Sjbeloro 		break;
189920Sjbeloro 
190920Sjbeloro 	default:
191920Sjbeloro 		retval = DDI_FAILURE;
192920Sjbeloro 	}
193920Sjbeloro 
194920Sjbeloro 	return (retval);
195920Sjbeloro }
196920Sjbeloro 
197920Sjbeloro static int
epic_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)198920Sjbeloro epic_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
199920Sjbeloro {
200920Sjbeloro 	int inst;
201920Sjbeloro 	struct epic_softc *softc = NULL;
202920Sjbeloro 	int minor;
203920Sjbeloro 	char name[MAXNAMELEN];
204920Sjbeloro 	ddi_device_acc_attr_t dev_attr;
205920Sjbeloro 	int res;
206920Sjbeloro 
207920Sjbeloro 	switch (cmd) {
208920Sjbeloro 	case DDI_ATTACH:
209920Sjbeloro 		inst = ddi_get_instance(dip);
210920Sjbeloro 		(void) sprintf(name, "env-monitor%d", inst);
211920Sjbeloro 		minor = inst;
212920Sjbeloro 		if (ddi_create_minor_node(dip, name, S_IFCHR, minor,
213920Sjbeloro 		    DDI_PSEUDO, NULL) == DDI_FAILURE) {
214920Sjbeloro 			cmn_err(CE_WARN,
215920Sjbeloro 			    "ddi_create_minor_node() failed for inst %d\n",
216920Sjbeloro 			    inst);
217920Sjbeloro 			return (DDI_FAILURE);
218920Sjbeloro 		}
219920Sjbeloro 
220920Sjbeloro 		/* Allocate a soft state structure for this instance */
221920Sjbeloro 		if (ddi_soft_state_zalloc(statep, inst) != DDI_SUCCESS) {
222920Sjbeloro 			cmn_err(CE_WARN, " ddi_soft_state_zalloc() failed "
223920Sjbeloro 			    "for inst %d\n", inst);
224920Sjbeloro 			break;
225920Sjbeloro 		}
226920Sjbeloro 
227920Sjbeloro 		/* Setup soft state */
228920Sjbeloro 		if ((softc = getsoftc(inst)) == NULL) {
229920Sjbeloro 			break;
230920Sjbeloro 		}
231920Sjbeloro 		softc->dip = dip;
232920Sjbeloro 		mutex_init(&softc->mutex, NULL, MUTEX_DRIVER, NULL);
233920Sjbeloro 
234920Sjbeloro 		/* Setup device attributes */
235920Sjbeloro 		dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
236920Sjbeloro 		dev_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
237920Sjbeloro 		dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
238920Sjbeloro 
239920Sjbeloro 		res = ddi_regs_map_setup(dip, 0, (caddr_t *)&softc->cmd_reg,
240*7656SSherry.Moore@Sun.COM 		    EPIC_REGS_OFFSET, EPIC_REGS_LEN, &dev_attr,
241*7656SSherry.Moore@Sun.COM 		    &softc->cmd_handle);
242920Sjbeloro 
243920Sjbeloro 		if (res != DDI_SUCCESS) {
244920Sjbeloro 			cmn_err(CE_WARN, "ddi_regs_map_setup() failed\n");
245920Sjbeloro 			break;
246920Sjbeloro 		}
247920Sjbeloro 
248920Sjbeloro 		ddi_report_dev(dip);
249920Sjbeloro 
250920Sjbeloro 
251920Sjbeloro 		return (DDI_SUCCESS);
252920Sjbeloro 
253920Sjbeloro 	case DDI_RESUME:
254920Sjbeloro 		return (DDI_SUCCESS);
255920Sjbeloro 
256920Sjbeloro 	default:
257920Sjbeloro 		return (DDI_FAILURE);
258920Sjbeloro 	}
259920Sjbeloro 
260920Sjbeloro 	/* Attach failed */
261920Sjbeloro 	/* Free soft state, if allocated. remove minor node if added earlier */
262920Sjbeloro 	if (softc)
263920Sjbeloro 		ddi_soft_state_free(statep, inst);
264920Sjbeloro 
265920Sjbeloro 	ddi_remove_minor_node(dip, NULL);
266920Sjbeloro 
267920Sjbeloro 	return (DDI_FAILURE);
268920Sjbeloro }
269920Sjbeloro 
270920Sjbeloro static int
epic_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)271920Sjbeloro epic_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
272920Sjbeloro {
273920Sjbeloro 	int inst;
274920Sjbeloro 	struct epic_softc *softc;
275920Sjbeloro 
276920Sjbeloro 	switch (cmd) {
277920Sjbeloro 	case DDI_DETACH:
278920Sjbeloro 		inst = ddi_get_instance(dip);
279920Sjbeloro 		if ((softc = getsoftc(inst)) == NULL)
280920Sjbeloro 			return (ENXIO);
281920Sjbeloro 
282920Sjbeloro 		(void) ddi_regs_map_free(&softc->cmd_handle);
283920Sjbeloro 
284920Sjbeloro 
285920Sjbeloro 		/* Free the soft state and remove minor node added earlier */
286920Sjbeloro 		mutex_destroy(&softc->mutex);
287920Sjbeloro 		ddi_soft_state_free(statep, inst);
288920Sjbeloro 		ddi_remove_minor_node(dip, NULL);
289920Sjbeloro 		return (DDI_SUCCESS);
290920Sjbeloro 
291920Sjbeloro 	case DDI_SUSPEND:
292920Sjbeloro 		return (DDI_SUCCESS);
293920Sjbeloro 
294920Sjbeloro 	default:
295920Sjbeloro 		return (DDI_FAILURE);
296920Sjbeloro 	}
297920Sjbeloro }
298920Sjbeloro 
299920Sjbeloro /*ARGSUSED*/
300920Sjbeloro static int
epic_open(dev_t * devp,int flag,int otyp,cred_t * credp)301920Sjbeloro epic_open(dev_t *devp, int flag, int otyp, cred_t *credp)
302920Sjbeloro {
303920Sjbeloro 	_NOTE(ARGUNUSED(flag))
304920Sjbeloro 	_NOTE(ARGUNUSED(otyp))
305920Sjbeloro 	_NOTE(ARGUNUSED(credp))
306920Sjbeloro 
307920Sjbeloro 	int	inst = getminor(*devp);
308920Sjbeloro 
309920Sjbeloro 	return (getsoftc(inst) == NULL ? ENXIO : 0);
310920Sjbeloro }
311920Sjbeloro 
312920Sjbeloro /*ARGSUSED*/
313920Sjbeloro static int
epic_close(dev_t dev,int flag,int otyp,cred_t * credp)314920Sjbeloro epic_close(dev_t dev, int flag, int otyp, cred_t *credp)
315920Sjbeloro {
316920Sjbeloro 	_NOTE(ARGUNUSED(flag))
317920Sjbeloro 	_NOTE(ARGUNUSED(otyp))
318920Sjbeloro 	_NOTE(ARGUNUSED(credp))
319920Sjbeloro 
320920Sjbeloro 	int	inst = getminor(dev);
321920Sjbeloro 
322920Sjbeloro 	return (getsoftc(inst) == NULL ? ENXIO : 0);
323920Sjbeloro }
324920Sjbeloro 
325920Sjbeloro /*ARGSUSED*/
326920Sjbeloro static int
epic_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)327920Sjbeloro epic_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
328920Sjbeloro int *rvalp)
329920Sjbeloro {
330920Sjbeloro 	_NOTE(ARGUNUSED(credp))
331920Sjbeloro 
332920Sjbeloro 	int	inst;
333920Sjbeloro 	struct epic_softc *softc;
334920Sjbeloro 	uint8_t	in_command;
335920Sjbeloro 
336920Sjbeloro 	inst = getminor(dev);
337920Sjbeloro 	if ((softc = getsoftc(inst)) == NULL)
338920Sjbeloro 		return (ENXIO);
339920Sjbeloro 
340920Sjbeloro 	mutex_enter(&softc->mutex);
341920Sjbeloro 
342920Sjbeloro 	switch (cmd) {
343920Sjbeloro 	case EPIC_SET_POWER_LED:
344*7656SSherry.Moore@Sun.COM 		EPIC_WRITE(softc->cmd_handle, softc->cmd_reg,
345*7656SSherry.Moore@Sun.COM 		    EPIC_IND_LED_STATE0, EPIC_POWER_LED_MASK,
346*7656SSherry.Moore@Sun.COM 		    EPIC_POWER_LED_ON);
347*7656SSherry.Moore@Sun.COM 		break;
348920Sjbeloro 	case EPIC_RESET_POWER_LED:
349*7656SSherry.Moore@Sun.COM 		EPIC_WRITE(softc->cmd_handle, softc->cmd_reg,
350*7656SSherry.Moore@Sun.COM 		    EPIC_IND_LED_STATE0, EPIC_POWER_LED_MASK,
351*7656SSherry.Moore@Sun.COM 		    EPIC_POWER_LED_OFF);
352*7656SSherry.Moore@Sun.COM 		break;
353920Sjbeloro 	case EPIC_SB_BL_POWER_LED:
354*7656SSherry.Moore@Sun.COM 		EPIC_WRITE(softc->cmd_handle, softc->cmd_reg,
355*7656SSherry.Moore@Sun.COM 		    EPIC_IND_LED_STATE0, EPIC_POWER_LED_MASK,
356*7656SSherry.Moore@Sun.COM 		    EPIC_POWER_LED_SB_BLINK);
357*7656SSherry.Moore@Sun.COM 		break;
358920Sjbeloro 	case EPIC_FAST_BL_POWER_LED:
359*7656SSherry.Moore@Sun.COM 		EPIC_WRITE(softc->cmd_handle, softc->cmd_reg,
360*7656SSherry.Moore@Sun.COM 		    EPIC_IND_LED_STATE0, EPIC_POWER_LED_MASK,
361*7656SSherry.Moore@Sun.COM 		    EPIC_POWER_LED_FAST_BLINK);
362*7656SSherry.Moore@Sun.COM 		break;
363920Sjbeloro 	case EPIC_SET_ALERT_LED:
364*7656SSherry.Moore@Sun.COM 		EPIC_WRITE(softc->cmd_handle, softc->cmd_reg,
365*7656SSherry.Moore@Sun.COM 		    EPIC_IND_LED_STATE0, EPIC_ALERT_LED_MASK,
366*7656SSherry.Moore@Sun.COM 		    EPIC_ALERT_LED_ON);
367*7656SSherry.Moore@Sun.COM 		break;
368920Sjbeloro 	case EPIC_RESET_ALERT_LED:
369*7656SSherry.Moore@Sun.COM 		EPIC_WRITE(softc->cmd_handle, softc->cmd_reg,
370*7656SSherry.Moore@Sun.COM 		    EPIC_IND_LED_STATE0, EPIC_ALERT_LED_MASK,
371*7656SSherry.Moore@Sun.COM 		    EPIC_ALERT_LED_OFF);
372*7656SSherry.Moore@Sun.COM 		break;
373920Sjbeloro 	case EPIC_GET_FW:
374*7656SSherry.Moore@Sun.COM 		EPIC_READ(softc->cmd_handle, softc->cmd_reg,
375*7656SSherry.Moore@Sun.COM 		    in_command, EPIC_IND_FW_VERSION);
376*7656SSherry.Moore@Sun.COM 		if (ddi_copyout((void *)(&in_command), (void *)arg,
377*7656SSherry.Moore@Sun.COM 		    sizeof (in_command), mode) != DDI_SUCCESS) {
378*7656SSherry.Moore@Sun.COM 			mutex_exit(&softc->mutex);
379*7656SSherry.Moore@Sun.COM 			return (EFAULT);
380*7656SSherry.Moore@Sun.COM 		}
381*7656SSherry.Moore@Sun.COM 		break;
382920Sjbeloro 	default:
383920Sjbeloro 		mutex_exit(&softc->mutex);
384920Sjbeloro 		cmn_err(CE_WARN, "epic: cmd %d is not valid", cmd);
385920Sjbeloro 		return (EINVAL);
386920Sjbeloro 	}
387920Sjbeloro 
388920Sjbeloro 	mutex_exit(&softc->mutex);
389920Sjbeloro 	return (0);
390920Sjbeloro }
391