1202006Smarius/*-
2202006Smarius * Copyright (c) 2009 Marius Strobl <marius@FreeBSD.org>
3202006Smarius * All rights reserved.
4202006Smarius *
5202006Smarius * Redistribution and use in source and binary forms, with or without
6202006Smarius * modification, are permitted provided that the following conditions
7202006Smarius * are met:
8202006Smarius * 1. Redistributions of source code must retain the above copyright
9202006Smarius *    notice, this list of conditions and the following disclaimer.
10202006Smarius * 2. Redistributions in binary form must reproduce the above copyright
11202006Smarius *    notice, this list of conditions and the following disclaimer in the
12202006Smarius *    documentation and/or other materials provided with the distribution.
13202006Smarius *
14202006Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15202006Smarius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16202006Smarius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17202006Smarius * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18202006Smarius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19202006Smarius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20202006Smarius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21202006Smarius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22202006Smarius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23202006Smarius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24202006Smarius * SUCH DAMAGE.
25202006Smarius */
26202006Smarius
27202006Smarius#include <sys/cdefs.h>
28202006Smarius__FBSDID("$FreeBSD$");
29202006Smarius
30202006Smarius#include <sys/param.h>
31202006Smarius#include <sys/systm.h>
32202006Smarius#include <sys/bus.h>
33202006Smarius#include <sys/kernel.h>
34202006Smarius#include <sys/lock.h>
35202006Smarius#include <sys/module.h>
36202006Smarius#include <sys/mutex.h>
37202006Smarius#include <sys/resource.h>
38202006Smarius#include <sys/rman.h>
39202006Smarius
40202006Smarius#include <dev/led/led.h>
41202006Smarius#include <dev/ofw/ofw_bus.h>
42202006Smarius
43202006Smarius#include <machine/bus.h>
44202006Smarius#include <machine/resource.h>
45202006Smarius
46202006Smarius#define	EPIC_DELAY			10000
47202006Smarius
48202006Smarius#define	EPIC_NREG			1
49202006Smarius#define	EPIC_FW_LED			0
50202006Smarius
51202006Smarius#define	EPIC_FW_LED_DATA		0x40
52202006Smarius#define	EPIC_FW_LED_ADDR		0x41
53202006Smarius#define	EPIC_FW_LED_WRITE_MASK		0x80
54202006Smarius
55202006Smarius#define	EPIC_FW_VERSION			0x05
56202006Smarius#define	EPIC_LED_STATE0			0x06
57202006Smarius
58202006Smarius#define	EPIC_LED_ALERT_MASK		0x0c
59202006Smarius#define	EPIC_LED_ALERT_OFF		0x00
60202006Smarius#define	EPIC_LED_ALERT_ON		0x04
61202006Smarius
62202006Smarius#define	EPIC_LED_POWER_MASK		0x30
63202006Smarius#define	EPIC_LED_POWER_OFF		0x00
64202006Smarius#define	EPIC_LED_POWER_ON		0x10
65202006Smarius#define	EPIC_LED_POWER_SB_BLINK		0x20
66202006Smarius#define	EPIC_LED_POWER_FAST_BLINK	0x30
67202006Smarius
68202006Smariusstatic struct resource_spec epic_res_spec[] = {
69202006Smarius	{ SYS_RES_MEMORY, EPIC_FW_LED, RF_ACTIVE },
70202006Smarius	{ -1, 0 }
71202006Smarius};
72202006Smarius
73202006Smariusstruct epic_softc {
74202006Smarius	struct mtx		sc_mtx;
75202006Smarius	struct resource		*sc_res[EPIC_NREG];
76202006Smarius	struct cdev		*sc_led_dev_alert;
77202006Smarius	struct cdev		*sc_led_dev_power;
78202006Smarius};
79202006Smarius
80202006Smarius#define	EPIC_FW_LED_READ(sc, off) ({					\
81202006Smarius	uint8_t	__val;							\
82202006Smarius	bus_write_1((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_ADDR, (off));\
83202006Smarius	bus_barrier((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_ADDR, 1,	\
84202006Smarius	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);		\
85202006Smarius	DELAY(EPIC_DELAY);						\
86202006Smarius	__val = bus_read_1((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_DATA);\
87202006Smarius	bus_barrier((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_DATA, 1,	\
88202006Smarius	    BUS_SPACE_BARRIER_READ);					\
89202006Smarius	DELAY(EPIC_DELAY);						\
90202006Smarius	__val;								\
91202006Smarius})
92202006Smarius
93202006Smarius#define	EPIC_FW_LED_WRITE(sc, off, mask, val) do {			\
94202006Smarius	bus_write_1((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_ADDR, (off));\
95202006Smarius	bus_barrier((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_ADDR, 1,	\
96202006Smarius	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);		\
97202006Smarius	DELAY(EPIC_DELAY);						\
98202006Smarius	bus_write_1((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_WRITE_MASK,	\
99202006Smarius	    (mask));							\
100202006Smarius	bus_barrier((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_WRITE_MASK,	\
101202006Smarius	    1, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);	\
102202006Smarius	DELAY(EPIC_DELAY);						\
103202006Smarius	bus_write_1((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_DATA, (val));\
104202006Smarius	bus_barrier((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_DATA, 1,	\
105202006Smarius	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);		\
106202006Smarius	DELAY(EPIC_DELAY);						\
107202006Smarius} while (0)
108202006Smarius
109202006Smarius#define	EPIC_LOCK_INIT(sc)						\
110202006Smarius	mtx_init(&(sc)->sc_mtx, "epic mtx", NULL, MTX_DEF)
111202006Smarius#define	EPIC_LOCK_DESTROY(sc)	mtx_destroy(&(sc)->sc_mtx)
112202006Smarius#define	EPIC_LOCK(sc)		mtx_lock(&(sc)->sc_mtx)
113202006Smarius#define	EPIC_UNLOCK(sc)		mtx_unlock(&(sc)->sc_mtx)
114202006Smarius
115202006Smariusstatic device_probe_t epic_probe;
116202006Smariusstatic device_attach_t epic_attach;
117202006Smariusstatic device_detach_t epic_detach;
118202006Smarius
119202006Smariusstatic void epic_led_alert(void *arg, int onoff);
120202006Smariusstatic void epic_led_power(void *arg, int onoff);
121202006Smarius
122202006Smariusstatic device_method_t epic_methods[] = {
123202006Smarius	/* Device interface */
124202006Smarius	DEVMETHOD(device_probe,		epic_probe),
125202006Smarius	DEVMETHOD(device_attach,	epic_attach),
126202006Smarius	DEVMETHOD(device_detach,	epic_detach),
127202006Smarius
128227848Smarius	DEVMETHOD_END
129202006Smarius};
130202006Smarius
131202006Smariusstatic devclass_t epic_devclass;
132202006Smarius
133202006SmariusDEFINE_CLASS_0(epic, epic_driver, epic_methods,
134202006Smarius    sizeof(struct epic_softc));
135202006SmariusDRIVER_MODULE(epic, ebus, epic_driver, epic_devclass, 0, 0);
136202006Smarius
137202006Smariusstatic int
138202006Smariusepic_probe(device_t dev)
139202006Smarius{
140202006Smarius	const char* compat;
141202006Smarius
142202006Smarius	compat = ofw_bus_get_compat(dev);
143202006Smarius	if (compat != NULL && strcmp(ofw_bus_get_name(dev),
144202006Smarius	    "env-monitor") == 0 && strcmp(compat, "epic") == 0) {
145202006Smarius		device_set_desc(dev, "Sun Fire V215/V245 LEDs");
146202006Smarius		return (BUS_PROBE_DEFAULT);
147202006Smarius	}
148202006Smarius	return (ENXIO);
149202006Smarius}
150202006Smarius
151202006Smariusstatic int
152202006Smariusepic_attach(device_t dev)
153202006Smarius{
154202006Smarius	struct epic_softc *sc;
155202006Smarius
156202006Smarius	sc = device_get_softc(dev);
157202006Smarius	if (bus_alloc_resources(dev, epic_res_spec, sc->sc_res)) {
158202006Smarius		device_printf(dev, "failed to allocate resources\n");
159202006Smarius		bus_release_resources(dev, epic_res_spec, sc->sc_res);
160202006Smarius		return (ENXIO);
161202006Smarius	}
162202006Smarius
163202006Smarius	EPIC_LOCK_INIT(sc);
164202006Smarius
165202006Smarius	if (bootverbose)
166202006Smarius		device_printf(dev, "version 0x%x\n",
167202006Smarius		    EPIC_FW_LED_READ(sc, EPIC_FW_VERSION));
168202006Smarius
169202006Smarius	sc->sc_led_dev_alert = led_create(epic_led_alert, sc, "alert");
170202006Smarius	sc->sc_led_dev_power = led_create(epic_led_power, sc, "power");
171202006Smarius
172202006Smarius	return (0);
173202006Smarius}
174202006Smarius
175202006Smariusstatic int
176202006Smariusepic_detach(device_t dev)
177202006Smarius{
178202006Smarius	struct epic_softc *sc;
179202006Smarius
180202006Smarius	sc = device_get_softc(dev);
181202006Smarius
182202006Smarius	led_destroy(sc->sc_led_dev_alert);
183202006Smarius	led_destroy(sc->sc_led_dev_power);
184202006Smarius
185202006Smarius	bus_release_resources(dev, epic_res_spec, sc->sc_res);
186202006Smarius
187202006Smarius	EPIC_LOCK_DESTROY(sc);
188202006Smarius
189202006Smarius	return (0);
190202006Smarius}
191202006Smarius
192202006Smariusstatic void
193202006Smariusepic_led_alert(void *arg, int onoff)
194202006Smarius{
195202006Smarius	struct epic_softc *sc;
196202006Smarius
197202006Smarius	sc = (struct epic_softc *)arg;
198202006Smarius
199202006Smarius	EPIC_LOCK(sc);
200202006Smarius	EPIC_FW_LED_WRITE(sc, EPIC_LED_STATE0, EPIC_LED_ALERT_MASK,
201202006Smarius	    onoff ? EPIC_LED_ALERT_ON : EPIC_LED_ALERT_OFF);
202202006Smarius	EPIC_UNLOCK(sc);
203202006Smarius}
204202006Smarius
205202006Smariusstatic void
206202006Smariusepic_led_power(void *arg, int onoff)
207202006Smarius{
208202006Smarius	struct epic_softc *sc;
209202006Smarius
210202006Smarius	sc = (struct epic_softc *)arg;
211202006Smarius
212202006Smarius	EPIC_LOCK(sc);
213202006Smarius	EPIC_FW_LED_WRITE(sc, EPIC_LED_STATE0, EPIC_LED_POWER_MASK,
214202006Smarius	    onoff ? EPIC_LED_POWER_ON : EPIC_LED_POWER_OFF);
215202006Smarius	EPIC_UNLOCK(sc);
216202006Smarius}
217