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