epic.c revision 202006
145386Swpaul/*-
245386Swpaul * Copyright (c) 2009 Marius Strobl <marius@FreeBSD.org>
345386Swpaul * All rights reserved.
445386Swpaul *
545386Swpaul * Redistribution and use in source and binary forms, with or without
645386Swpaul * modification, are permitted provided that the following conditions
745386Swpaul * are met:
845386Swpaul * 1. Redistributions of source code must retain the above copyright
945386Swpaul *    notice, this list of conditions and the following disclaimer.
1045386Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1145386Swpaul *    notice, this list of conditions and the following disclaimer in the
1245386Swpaul *    documentation and/or other materials provided with the distribution.
1345386Swpaul *
1445386Swpaul * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1545386Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1645386Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1745386Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1845386Swpaul * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1945386Swpaul * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2045386Swpaul * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2145386Swpaul * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2245386Swpaul * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2345386Swpaul * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2445386Swpaul * SUCH DAMAGE.
2545386Swpaul */
2645386Swpaul
2745386Swpaul#include <sys/cdefs.h>
2845386Swpaul__FBSDID("$FreeBSD: head/sys/sparc64/ebus/epic.c 202006 2010-01-10 15:44:48Z marius $");
2945386Swpaul
3045386Swpaul#include <sys/param.h>
3145386Swpaul#include <sys/systm.h>
3245386Swpaul#include <sys/bus.h>
3345386Swpaul#include <sys/kernel.h>
3445386Swpaul#include <sys/lock.h>
3545386Swpaul#include <sys/module.h>
3645386Swpaul#include <sys/mutex.h>
3745386Swpaul#include <sys/resource.h>
3845386Swpaul#include <sys/rman.h>
3945386Swpaul
4045386Swpaul#include <dev/led/led.h>
4145386Swpaul#include <dev/ofw/ofw_bus.h>
4245386Swpaul
4345386Swpaul#include <machine/bus.h>
4445386Swpaul#include <machine/resource.h>
4545386Swpaul
4645386Swpaul#define	EPIC_DELAY			10000
4745386Swpaul
4845386Swpaul#define	EPIC_NREG			1
4945386Swpaul#define	EPIC_FW_LED			0
5045386Swpaul
5145386Swpaul#define	EPIC_FW_LED_DATA		0x40
5245386Swpaul#define	EPIC_FW_LED_ADDR		0x41
5345386Swpaul#define	EPIC_FW_LED_WRITE_MASK		0x80
5445386Swpaul
5545386Swpaul#define	EPIC_FW_VERSION			0x05
5645386Swpaul#define	EPIC_LED_STATE0			0x06
5745386Swpaul
5845386Swpaul#define	EPIC_LED_ALERT_MASK		0x0c
5945386Swpaul#define	EPIC_LED_ALERT_OFF		0x00
6045386Swpaul#define	EPIC_LED_ALERT_ON		0x04
6145386Swpaul
6245386Swpaul#define	EPIC_LED_POWER_MASK		0x30
6345386Swpaul#define	EPIC_LED_POWER_OFF		0x00
6445386Swpaul#define	EPIC_LED_POWER_ON		0x10
6545386Swpaul#define	EPIC_LED_POWER_SB_BLINK		0x20
6645386Swpaul#define	EPIC_LED_POWER_FAST_BLINK	0x30
6745386Swpaul
6845386Swpaulstatic struct resource_spec epic_res_spec[] = {
6945386Swpaul	{ SYS_RES_MEMORY, EPIC_FW_LED, RF_ACTIVE },
7045386Swpaul	{ -1, 0 }
7145386Swpaul};
7245386Swpaul
7345386Swpaulstruct epic_softc {
7445386Swpaul	struct mtx		sc_mtx;
7545386Swpaul	struct resource		*sc_res[EPIC_NREG];
7645386Swpaul	struct cdev		*sc_led_dev_alert;
7745386Swpaul	struct cdev		*sc_led_dev_power;
7845386Swpaul};
7945386Swpaul
8045386Swpaul#define	EPIC_FW_LED_READ(sc, off) ({					\
8145386Swpaul	uint8_t	__val;							\
8245386Swpaul	bus_write_1((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_ADDR, (off));\
8345386Swpaul	bus_barrier((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_ADDR, 1,	\
8445386Swpaul	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);		\
8545386Swpaul	DELAY(EPIC_DELAY);						\
8645386Swpaul	__val = bus_read_1((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_DATA);\
8745386Swpaul	bus_barrier((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_DATA, 1,	\
8845386Swpaul	    BUS_SPACE_BARRIER_READ);					\
8945386Swpaul	DELAY(EPIC_DELAY);						\
9045386Swpaul	__val;								\
9145386Swpaul})
9245386Swpaul
9345386Swpaul#define	EPIC_FW_LED_WRITE(sc, off, mask, val) do {			\
9445386Swpaul	bus_write_1((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_ADDR, (off));\
9545386Swpaul	bus_barrier((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_ADDR, 1,	\
9645386Swpaul	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);		\
9745386Swpaul	DELAY(EPIC_DELAY);						\
9845386Swpaul	bus_write_1((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_WRITE_MASK,	\
9945386Swpaul	    (mask));							\
10045386Swpaul	bus_barrier((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_WRITE_MASK,	\
10145386Swpaul	    1, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);	\
10245386Swpaul	DELAY(EPIC_DELAY);						\
10345386Swpaul	bus_write_1((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_DATA, (val));\
10445386Swpaul	bus_barrier((sc)->sc_res[EPIC_FW_LED], EPIC_FW_LED_DATA, 1,	\
10545386Swpaul	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);		\
10645386Swpaul	DELAY(EPIC_DELAY);						\
10745386Swpaul} while (0)
10845386Swpaul
10945386Swpaul#define	EPIC_LOCK_INIT(sc)						\
11045386Swpaul	mtx_init(&(sc)->sc_mtx, "epic mtx", NULL, MTX_DEF)
11145386Swpaul#define	EPIC_LOCK_DESTROY(sc)	mtx_destroy(&(sc)->sc_mtx)
11245386Swpaul#define	EPIC_LOCK(sc)		mtx_lock(&(sc)->sc_mtx)
11345386Swpaul#define	EPIC_UNLOCK(sc)		mtx_unlock(&(sc)->sc_mtx)
11445386Swpaul
11545386Swpaulstatic device_probe_t epic_probe;
11645386Swpaulstatic device_attach_t epic_attach;
11745386Swpaulstatic device_detach_t epic_detach;
11845386Swpaul
11945386Swpaulstatic void epic_led_alert(void *arg, int onoff);
12045386Swpaulstatic void epic_led_power(void *arg, int onoff);
12145386Swpaul
12245386Swpaulstatic device_method_t epic_methods[] = {
12345386Swpaul	/* Device interface */
12445386Swpaul	DEVMETHOD(device_probe,		epic_probe),
12545386Swpaul	DEVMETHOD(device_attach,	epic_attach),
12645386Swpaul	DEVMETHOD(device_detach,	epic_detach),
12745386Swpaul
12845386Swpaul	KOBJMETHOD_END
12945386Swpaul};
13045386Swpaul
13145386Swpaulstatic devclass_t epic_devclass;
13245386Swpaul
13345386SwpaulDEFINE_CLASS_0(epic, epic_driver, epic_methods,
13445386Swpaul    sizeof(struct epic_softc));
13545386SwpaulDRIVER_MODULE(epic, ebus, epic_driver, epic_devclass, 0, 0);
13645386Swpaul
13745386Swpaulstatic int
13845386Swpaulepic_probe(device_t dev)
13945386Swpaul{
14045386Swpaul	const char* compat;
14145386Swpaul
14245386Swpaul	compat = ofw_bus_get_compat(dev);
14345386Swpaul	if (compat != NULL && strcmp(ofw_bus_get_name(dev),
14445386Swpaul	    "env-monitor") == 0 && strcmp(compat, "epic") == 0) {
14545386Swpaul		device_set_desc(dev, "Sun Fire V215/V245 LEDs");
14645386Swpaul		return (BUS_PROBE_DEFAULT);
14745386Swpaul	}
14845386Swpaul	return (ENXIO);
14945386Swpaul}
15045386Swpaul
15145386Swpaulstatic int
15245386Swpaulepic_attach(device_t dev)
15345386Swpaul{
15445386Swpaul	struct epic_softc *sc;
15545386Swpaul
15645386Swpaul	sc = device_get_softc(dev);
15745386Swpaul	if (bus_alloc_resources(dev, epic_res_spec, sc->sc_res)) {
15845386Swpaul		device_printf(dev, "failed to allocate resources\n");
15945386Swpaul		bus_release_resources(dev, epic_res_spec, sc->sc_res);
16045386Swpaul		return (ENXIO);
16145386Swpaul	}
16245386Swpaul
16345386Swpaul	EPIC_LOCK_INIT(sc);
16445386Swpaul
16545386Swpaul	if (bootverbose)
16645386Swpaul		device_printf(dev, "version 0x%x\n",
16745386Swpaul		    EPIC_FW_LED_READ(sc, EPIC_FW_VERSION));
16845386Swpaul
16945386Swpaul	sc->sc_led_dev_alert = led_create(epic_led_alert, sc, "alert");
17045386Swpaul	sc->sc_led_dev_power = led_create(epic_led_power, sc, "power");
17145386Swpaul
17245386Swpaul	return (0);
17345386Swpaul}
17445386Swpaul
17545386Swpaulstatic int
17645386Swpaulepic_detach(device_t dev)
17745386Swpaul{
17845386Swpaul	struct epic_softc *sc;
17945386Swpaul
18045386Swpaul	sc = device_get_softc(dev);
18145386Swpaul
18245386Swpaul	led_destroy(sc->sc_led_dev_alert);
18345386Swpaul	led_destroy(sc->sc_led_dev_power);
18445386Swpaul
18545386Swpaul	bus_release_resources(dev, epic_res_spec, sc->sc_res);
18645386Swpaul
18745386Swpaul	EPIC_LOCK_DESTROY(sc);
18845386Swpaul
18945386Swpaul	return (0);
19045386Swpaul}
19145386Swpaul
19245386Swpaulstatic void
19345386Swpaulepic_led_alert(void *arg, int onoff)
19445386Swpaul{
19545386Swpaul	struct epic_softc *sc;
19645386Swpaul
19745386Swpaul	sc = (struct epic_softc *)arg;
19845386Swpaul
19945386Swpaul	EPIC_LOCK(sc);
20045386Swpaul	EPIC_FW_LED_WRITE(sc, EPIC_LED_STATE0, EPIC_LED_ALERT_MASK,
20145386Swpaul	    onoff ? EPIC_LED_ALERT_ON : EPIC_LED_ALERT_OFF);
20245386Swpaul	EPIC_UNLOCK(sc);
20345386Swpaul}
20445386Swpaul
20545386Swpaulstatic void
20645386Swpaulepic_led_power(void *arg, int onoff)
20745386Swpaul{
20845386Swpaul	struct epic_softc *sc;
20945386Swpaul
21045386Swpaul	sc = (struct epic_softc *)arg;
21145386Swpaul
21245386Swpaul	EPIC_LOCK(sc);
21345386Swpaul	EPIC_FW_LED_WRITE(sc, EPIC_LED_STATE0, EPIC_LED_POWER_MASK,
21445386Swpaul	    onoff ? EPIC_LED_POWER_ON : EPIC_LED_POWER_OFF);
21545386Swpaul	EPIC_UNLOCK(sc);
21645386Swpaul}
21745386Swpaul