1*e49dc77cSmatt /* $NetBSD: nslu2_leds.c,v 1.10 2013/08/19 22:26:09 matt Exp $ */
26f616773Sscw
36f616773Sscw /*-
46f616773Sscw * Copyright (c) 2006 The NetBSD Foundation, Inc.
56f616773Sscw * All rights reserved.
66f616773Sscw *
76f616773Sscw * This code is derived from software contributed to The NetBSD Foundation
86f616773Sscw * by Steve C. Woodford.
96f616773Sscw *
106f616773Sscw * Redistribution and use in source and binary forms, with or without
116f616773Sscw * modification, are permitted provided that the following conditions
126f616773Sscw * are met:
136f616773Sscw * 1. Redistributions of source code must retain the above copyright
146f616773Sscw * notice, this list of conditions and the following disclaimer.
156f616773Sscw * 2. Redistributions in binary form must reproduce the above copyright
166f616773Sscw * notice, this list of conditions and the following disclaimer in the
176f616773Sscw * documentation and/or other materials provided with the distribution.
186f616773Sscw *
196f616773Sscw * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
206f616773Sscw * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
216f616773Sscw * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
226f616773Sscw * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
236f616773Sscw * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
246f616773Sscw * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
256f616773Sscw * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
266f616773Sscw * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
276f616773Sscw * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
286f616773Sscw * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
296f616773Sscw * POSSIBILITY OF SUCH DAMAGE.
306f616773Sscw */
316f616773Sscw
326f616773Sscw #include <sys/cdefs.h>
33*e49dc77cSmatt __KERNEL_RCSID(0, "$NetBSD: nslu2_leds.c,v 1.10 2013/08/19 22:26:09 matt Exp $");
346f616773Sscw
356f616773Sscw #include <sys/param.h>
366f616773Sscw #include <sys/systm.h>
376f616773Sscw #include <sys/kernel.h>
386f616773Sscw #include <sys/device.h>
396f616773Sscw #include <sys/callout.h>
4094359b8fSscw #include <sys/proc.h>
41*e49dc77cSmatt #include <sys/intr.h>
42*e49dc77cSmatt #include <sys/cpu.h>
4394359b8fSscw
4494359b8fSscw #include <dev/usb/usb.h>
4594359b8fSscw #include <dev/usb/usbcdc.h>
4694359b8fSscw #include <dev/usb/usbdi.h> /* XXX: For IPL_USB */
476f616773Sscw
486f616773Sscw #include <arm/xscale/ixp425var.h>
496f616773Sscw
506f616773Sscw #include <evbarm/nslu2/nslu2reg.h>
516f616773Sscw
5294359b8fSscw #define SLUGLED_FLASH_LEN (hz/8) /* How many ticks an LED stays lit */
536f616773Sscw
5494359b8fSscw #define LEDBITS_USB0 (1u << GPIO_LED_DISK1)
5594359b8fSscw #define LEDBITS_USB1 (1u << GPIO_LED_DISK2)
566f616773Sscw
576f616773Sscw /*
586f616773Sscw * The Ready/Status bits control a tricolour LED.
596f616773Sscw * Ready is green, status is red.
606f616773Sscw */
616f616773Sscw #define LEDBITS_READY (1u << GPIO_LED_READY)
626f616773Sscw #define LEDBITS_STATUS (1u << GPIO_LED_STATUS)
636f616773Sscw
646f616773Sscw struct slugled_softc {
6594359b8fSscw void *sc_tmr_ih;
6694359b8fSscw struct callout sc_usb0;
6794359b8fSscw void *sc_usb0_ih;
6894359b8fSscw struct callout sc_usb1;
6994359b8fSscw void *sc_usb1_ih;
7094359b8fSscw struct callout sc_usb2;
7194359b8fSscw void *sc_usb2_ih;
726f616773Sscw };
736f616773Sscw
746f616773Sscw static int slugled_attached;
756f616773Sscw
766f616773Sscw static void
slugled_callout(void * arg)776f616773Sscw slugled_callout(void *arg)
786f616773Sscw {
7994359b8fSscw uint32_t reg, bit;
8094359b8fSscw int is;
816f616773Sscw
8294359b8fSscw bit = (uint32_t)(uintptr_t)arg;
836f616773Sscw
8494359b8fSscw is = disable_interrupts(I32_bit);
856f616773Sscw reg = GPIO_CONF_READ_4(ixp425_softc, IXP425_GPIO_GPOUTR);
8694359b8fSscw GPIO_CONF_WRITE_4(ixp425_softc, IXP425_GPIO_GPOUTR, reg | bit);
8794359b8fSscw restore_interrupts(is);
886f616773Sscw }
896f616773Sscw
9094359b8fSscw static int
slugled_intr0(void * arg)9194359b8fSscw slugled_intr0(void *arg)
9294359b8fSscw {
9394359b8fSscw struct slugled_softc *sc = arg;
9494359b8fSscw uint32_t reg;
9594359b8fSscw int is;
9694359b8fSscw
9794359b8fSscw is = disable_interrupts(I32_bit);
9894359b8fSscw reg = GPIO_CONF_READ_4(ixp425_softc, IXP425_GPIO_GPOUTR);
9994359b8fSscw reg &= ~LEDBITS_USB0;
10094359b8fSscw GPIO_CONF_WRITE_4(ixp425_softc, IXP425_GPIO_GPOUTR, reg);
10194359b8fSscw restore_interrupts(is);
10294359b8fSscw
10394359b8fSscw callout_schedule(&sc->sc_usb0, SLUGLED_FLASH_LEN);
10494359b8fSscw
10594359b8fSscw return (1);
10694359b8fSscw }
10794359b8fSscw
10894359b8fSscw static int
slugled_intr1(void * arg)10994359b8fSscw slugled_intr1(void *arg)
11094359b8fSscw {
11194359b8fSscw struct slugled_softc *sc = arg;
11294359b8fSscw uint32_t reg;
11394359b8fSscw int is;
11494359b8fSscw
11594359b8fSscw is = disable_interrupts(I32_bit);
11694359b8fSscw reg = GPIO_CONF_READ_4(ixp425_softc, IXP425_GPIO_GPOUTR);
11794359b8fSscw reg &= ~LEDBITS_USB1;
11894359b8fSscw GPIO_CONF_WRITE_4(ixp425_softc, IXP425_GPIO_GPOUTR, reg);
11994359b8fSscw restore_interrupts(is);
12094359b8fSscw
12194359b8fSscw callout_schedule(&sc->sc_usb1, SLUGLED_FLASH_LEN);
12294359b8fSscw
12394359b8fSscw return (1);
12494359b8fSscw }
12594359b8fSscw
12694359b8fSscw static int
slugled_intr2(void * arg)12794359b8fSscw slugled_intr2(void *arg)
12894359b8fSscw {
12994359b8fSscw struct slugled_softc *sc = arg;
13094359b8fSscw uint32_t reg;
13194359b8fSscw int is;
13294359b8fSscw
13394359b8fSscw is = disable_interrupts(I32_bit);
13494359b8fSscw reg = GPIO_CONF_READ_4(ixp425_softc, IXP425_GPIO_GPOUTR);
13594359b8fSscw reg &= ~(LEDBITS_USB0 | LEDBITS_USB1);
13694359b8fSscw GPIO_CONF_WRITE_4(ixp425_softc, IXP425_GPIO_GPOUTR, reg);
13794359b8fSscw restore_interrupts(is);
13894359b8fSscw
13994359b8fSscw callout_schedule(&sc->sc_usb2, SLUGLED_FLASH_LEN);
14094359b8fSscw
14194359b8fSscw return (1);
14294359b8fSscw }
14394359b8fSscw
14494359b8fSscw static int
slugled_tmr(void * arg)14594359b8fSscw slugled_tmr(void *arg)
14694359b8fSscw {
147*e49dc77cSmatt struct clockframe *cf = arg;
14894359b8fSscw uint32_t reg, bit;
14994359b8fSscw int is;
15094359b8fSscw
151*e49dc77cSmatt if (CLKF_INTR(cf) || sched_curcpu_runnable_p() ||
15261366a1bSscw (curlwp != NULL && curlwp != curcpu()->ci_data.cpu_idlelwp))
15394359b8fSscw bit = LEDBITS_STATUS;
15494359b8fSscw else
15594359b8fSscw bit = 0;
15694359b8fSscw
15794359b8fSscw is = disable_interrupts(I32_bit);
15894359b8fSscw reg = GPIO_CONF_READ_4(ixp425_softc, IXP425_GPIO_GPOUTR);
15994359b8fSscw reg &= ~LEDBITS_STATUS;
16094359b8fSscw GPIO_CONF_WRITE_4(ixp425_softc, IXP425_GPIO_GPOUTR, reg | bit);
16194359b8fSscw restore_interrupts(is);
16294359b8fSscw
16394359b8fSscw return (1);
1646f616773Sscw }
1656f616773Sscw
1666f616773Sscw static void
slugled_shutdown(void * arg)1676f616773Sscw slugled_shutdown(void *arg)
1686f616773Sscw {
1696f616773Sscw struct slugled_softc *sc = arg;
1706f616773Sscw uint32_t reg;
1716f616773Sscw int s;
1726f616773Sscw
17394359b8fSscw ixp425_intr_disestablish(sc->sc_usb0_ih);
17494359b8fSscw ixp425_intr_disestablish(sc->sc_usb1_ih);
17594359b8fSscw ixp425_intr_disestablish(sc->sc_tmr_ih);
17694359b8fSscw
17794359b8fSscw /* Cancel the callouts */
1786f616773Sscw s = splsoftclock();
17994359b8fSscw callout_stop(&sc->sc_usb0);
18094359b8fSscw callout_stop(&sc->sc_usb1);
1816f616773Sscw splx(s);
1826f616773Sscw
18394359b8fSscw /* Turn off the disk LEDs, and set Ready/Status to amber */
1846f616773Sscw s = splhigh();
1856f616773Sscw reg = GPIO_CONF_READ_4(ixp425_softc,IXP425_GPIO_GPOUTR);
18694359b8fSscw reg |= LEDBITS_USB0 | LEDBITS_USB1 | LEDBITS_STATUS | LEDBITS_READY;
1876f616773Sscw GPIO_CONF_WRITE_4(ixp425_softc,IXP425_GPIO_GPOUTR, reg);
1886f616773Sscw splx(s);
1896f616773Sscw }
1906f616773Sscw
1916f616773Sscw static void
slugled_defer(device_t self)192a2b8c7fbSmsaitoh slugled_defer(device_t self)
1936f616773Sscw {
194a2b8c7fbSmsaitoh struct slugled_softc *sc = device_private(self);
1956f616773Sscw struct ixp425_softc *ixsc = ixp425_softc;
1966f616773Sscw uint32_t reg;
19794359b8fSscw int s;
1986f616773Sscw
1996f616773Sscw s = splhigh();
2006f616773Sscw
2016f616773Sscw /* Configure LED GPIO pins as output */
2026f616773Sscw reg = GPIO_CONF_READ_4(ixsc, IXP425_GPIO_GPOER);
20394359b8fSscw reg &= ~(LEDBITS_USB0 | LEDBITS_USB1);
2046f616773Sscw reg &= ~(LEDBITS_READY | LEDBITS_STATUS);
2056f616773Sscw GPIO_CONF_WRITE_4(ixsc, IXP425_GPIO_GPOER, reg);
2066f616773Sscw
20794359b8fSscw /* All LEDs off */
2086f616773Sscw reg = GPIO_CONF_READ_4(ixsc, IXP425_GPIO_GPOUTR);
20994359b8fSscw reg |= LEDBITS_USB0 | LEDBITS_USB1;
21094359b8fSscw reg &= ~(LEDBITS_STATUS | LEDBITS_READY);
2116f616773Sscw GPIO_CONF_WRITE_4(ixsc, IXP425_GPIO_GPOUTR, reg);
2126f616773Sscw
2136f616773Sscw splx(s);
2146f616773Sscw
2156f616773Sscw if (shutdownhook_establish(slugled_shutdown, sc) == NULL)
216a2b8c7fbSmsaitoh aprint_error_dev(self, "WARNING - Failed to register shutdown hook\n");
2176f616773Sscw
21888ab7da9Sad callout_init(&sc->sc_usb0, 0);
21994359b8fSscw callout_setfunc(&sc->sc_usb0, slugled_callout,
22094359b8fSscw (void *)(uintptr_t)LEDBITS_USB0);
22194359b8fSscw
22288ab7da9Sad callout_init(&sc->sc_usb1, 0);
22394359b8fSscw callout_setfunc(&sc->sc_usb1, slugled_callout,
22494359b8fSscw (void *)(uintptr_t)LEDBITS_USB1);
22594359b8fSscw
22688ab7da9Sad callout_init(&sc->sc_usb2, 0);
22794359b8fSscw callout_setfunc(&sc->sc_usb2, slugled_callout,
22894359b8fSscw (void *)(uintptr_t)(LEDBITS_USB0 | LEDBITS_USB1));
22994359b8fSscw
23094359b8fSscw sc->sc_usb0_ih = ixp425_intr_establish(PCI_INT_A, IPL_USB,
23194359b8fSscw slugled_intr0, sc);
23294359b8fSscw KDASSERT(sc->sc_usb0_ih != NULL);
23394359b8fSscw sc->sc_usb1_ih = ixp425_intr_establish(PCI_INT_B, IPL_USB,
23494359b8fSscw slugled_intr1, sc);
23594359b8fSscw KDASSERT(sc->sc_usb1_ih != NULL);
23694359b8fSscw sc->sc_usb2_ih = ixp425_intr_establish(PCI_INT_C, IPL_USB,
23794359b8fSscw slugled_intr2, sc);
23894359b8fSscw KDASSERT(sc->sc_usb2_ih != NULL);
23994359b8fSscw
24094359b8fSscw sc->sc_tmr_ih = ixp425_intr_establish(IXP425_INT_TMR0, IPL_CLOCK,
24194359b8fSscw slugled_tmr, NULL);
24294359b8fSscw KDASSERT(sc->sc_tmr_ih != NULL);
2436f616773Sscw }
2446f616773Sscw
2456f616773Sscw static int
slugled_match(device_t parent,cfdata_t cf,void * aux)246a2b8c7fbSmsaitoh slugled_match(device_t parent, cfdata_t cf, void *aux)
2476f616773Sscw {
2486f616773Sscw
2496f616773Sscw return (slugled_attached == 0);
2506f616773Sscw }
2516f616773Sscw
2526f616773Sscw static void
slugled_attach(device_t parent,device_t self,void * aux)253a2b8c7fbSmsaitoh slugled_attach(device_t parent, device_t self, void *aux)
2546f616773Sscw {
2556f616773Sscw
2566f616773Sscw aprint_normal(": LED support\n");
2576f616773Sscw
2586f616773Sscw slugled_attached = 1;
2596f616773Sscw
2606f616773Sscw config_interrupts(self, slugled_defer);
2616f616773Sscw }
2626f616773Sscw
263a2b8c7fbSmsaitoh CFATTACH_DECL_NEW(slugled, sizeof(struct slugled_softc),
2646f616773Sscw slugled_match, slugled_attach, NULL, NULL);
265