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 */
62static int	epic_attach(dev_info_t *, ddi_attach_cmd_t);
63static int	epic_detach(dev_info_t *, ddi_detach_cmd_t);
64static int	epic_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
65static int	epic_open(dev_t *, int, int, cred_t *);
66static int	epic_close(dev_t, int, int, cred_t *);
67static int	epic_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
68
69struct 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
87static 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 */
106struct 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 */
117static void    *statep;
118extern struct mod_ops mod_driverops;
119
120static struct modldrv modldrv = {
121	&mod_driverops,
122	"epic_client driver",
123	&epic_dev_ops
124};
125
126static struct modlinkage modlinkage = {
127	MODREV_1,
128	&modldrv,
129	0
130};
131
132int
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
148int
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
161int
162_info(struct modinfo *modinfop)
163{
164	return (mod_info(&modlinkage, modinfop));
165}
166
167/*ARGSUSED*/
168static int
169epic_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
197static int
198epic_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, 0) == 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
270static int
271epic_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*/
300static int
301epic_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*/
313static int
314epic_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*/
326static int
327epic_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