diff options
author | Gordon Willem Klok <gwk@cvs.openbsd.org> | 2007-04-21 15:43:28 +0000 |
---|---|---|
committer | Gordon Willem Klok <gwk@cvs.openbsd.org> | 2007-04-21 15:43:28 +0000 |
commit | 686b00d8c295e31d7615de0a59642ee439469332 (patch) | |
tree | 1e4c69f8cb2169c5809661f30e4a5285839a483e /sys/arch | |
parent | b374254ac271c486a549616e90c4b8f3f66dea81 (diff) |
Add blinkenlights(4) a driver to control the 16 leds found on the front
panel of the Xserve G4 and G5. The leds will provide an indication of the
system load slowing as the load climbs, and system health e.g. no
lights means its dead or in DDB.
Split some of the shared registers and clock divisors from i2s into
i2sreg.h while there nuke some evil C++ style comments.
Man page to follow shortly.
ok deraadt
Diffstat (limited to 'sys/arch')
-rw-r--r-- | sys/arch/macppc/conf/GENERIC | 3 | ||||
-rw-r--r-- | sys/arch/macppc/conf/files.macppc | 7 | ||||
-rw-r--r-- | sys/arch/macppc/dev/blinkenlights.c | 331 | ||||
-rw-r--r-- | sys/arch/macppc/dev/i2s.c | 58 | ||||
-rw-r--r-- | sys/arch/macppc/dev/i2sreg.h | 64 |
5 files changed, 414 insertions, 49 deletions
diff --git a/sys/arch/macppc/conf/GENERIC b/sys/arch/macppc/conf/GENERIC index c2f92634890..61af66ddae9 100644 --- a/sys/arch/macppc/conf/GENERIC +++ b/sys/arch/macppc/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.162 2007/03/29 16:32:06 claudio Exp $g +# $OpenBSD: GENERIC,v 1.163 2007/04/21 15:43:27 gwk Exp $g # # For further information on compiling OpenBSD kernels, see the config(8) # man page. @@ -139,6 +139,7 @@ pwdog0 at pci? # Quancom PWDOG1 watchdog timer macintr0 at macobio? # old interrupt controller openpic* at macobio? # openpic interrupt controller bm* at macobio? # BMAC ethernet +blinkenlights* at macobio? # Xserve front panel LEDs mc* at macobio? # MACE ethernet #esp* at macobio? flags 0x0000ffff # 53c9x SCSI mesh* at macobio? flags 0xffff # MESH SCSI diff --git a/sys/arch/macppc/conf/files.macppc b/sys/arch/macppc/conf/files.macppc index b2f21e91046..ac402cf67fd 100644 --- a/sys/arch/macppc/conf/files.macppc +++ b/sys/arch/macppc/conf/files.macppc @@ -1,4 +1,4 @@ -# $OpenBSD: files.macppc,v 1.50 2006/12/06 20:38:14 gwk Exp $ +# $OpenBSD: files.macppc,v 1.51 2007/04/21 15:43:27 gwk Exp $ # # macppc-specific configuration info @@ -193,6 +193,11 @@ file arch/macppc/dev/pi2c.c pi2c define i2s file arch/macppc/dev/i2s.c i2s +# blinkenlights +device blinkenlights +attach blinkenlights at macobio +file arch/macppc/dev/blinkenlights.c blinkenlights + # snapper audio device snapper: audio, auconv, mulaw, i2s attach snapper at macobio diff --git a/sys/arch/macppc/dev/blinkenlights.c b/sys/arch/macppc/dev/blinkenlights.c new file mode 100644 index 00000000000..8a4eed60711 --- /dev/null +++ b/sys/arch/macppc/dev/blinkenlights.c @@ -0,0 +1,331 @@ +/* $OpenBSD: */ +/* + * Copyright (c) 2007 Gordon Willem Klok <gwk@openbsd,org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/device.h> +#include <sys/kthread.h> +#include <sys/timeout.h> +#include <dev/ofw/openfirm.h> + +#include <machine/bus.h> +#include <machine/autoconf.h> +#include <macppc/dev/dbdma.h> +#include <macppc/dev/i2sreg.h> + +struct blinkenlights_softc { + struct device sc_dev; + int sc_node; + int sc_intr; + uint32_t sc_freq; + int sc_dmasts; + + u_char *sc_reg; + dbdma_regmap_t *sc_dma; + bus_dma_tag_t sc_dmat; + bus_dmamap_t sc_bufmap; + bus_dma_segment_t sc_bufseg[1]; + dbdma_t sc_dbdma; + dbdma_command_t *sc_dmacmd; + uint32_t *sc_buf; + uint32_t *sc_bufpos; + + struct timeout sc_tmo; +}; + +int blinkenlights_match(struct device *, void *, void *); +void blinkenlights_attach(struct device *, struct device *, void *); +int blinkenlights_intr(void *); +void blinkenlights_startdma(struct blinkenlights_softc *); +void blinkenlights_deferred(void *); +void blinkenlights_theosDOT(void *); +void blinkenlights_timeout(void *); +void blinkenlights_pwm(struct blinkenlights_softc *, u_char *, int); +extern void keylargo_fcr_enable(int, u_int32_t); +extern void keylargo_fcr_disable(int, u_int32_t); + +struct cfattach blinkenlights_ca = { + sizeof(struct blinkenlights_softc), blinkenlights_match, + blinkenlights_attach +}; + +struct cfdriver blinkenlights_cd = { + NULL, "blinkenlights", DV_DULL +}; + +#define BL_BUFSZ PAGE_SIZE +#define BL_DBDMA_CMDS 2 + +int +blinkenlights_match(struct device *parent, void *arg, void *aux) +{ + struct confargs *ca = aux; + int soundbus, soundchip, error; + char compat[32]; + + if (strcmp(ca->ca_name, "i2s") != 0) + return 0; + if ((soundbus = OF_child(ca->ca_node)) == 0) + return 0; + if ((soundchip = OF_child(soundbus)) == 0) + return 0; + + error = OF_getprop(soundchip, "virtual", compat, sizeof(compat)); + if (error == -1) + error = OF_getprop(soundchip, "lightshow", compat, + sizeof(compat)); + if (error == -1) + return 0; + + /* we require at least 4 registers */ + if (ca->ca_nreg / sizeof(int) < 4) + return 0; + /* we require at least 3 interrupts */ + if (ca->ca_nintr / sizeof(int) < 6) + return 0; + + return 1; +} + +void +blinkenlights_attach(struct device *parent, struct device *self, void *aux) +{ + struct blinkenlights_softc *sc = (struct blinkenlights_softc *)self; + struct confargs *ca = aux; + int nseg, error, intr[6]; + u_int32_t reg[4]; + + sc->sc_node = OF_child(ca->ca_node); + + OF_getprop(sc->sc_node, "reg", reg, sizeof(reg)); + ca->ca_reg[0] += ca->ca_baseaddr; + ca->ca_reg[2] += ca->ca_baseaddr; + + if ((sc->sc_reg = mapiodev(ca->ca_reg[0], ca->ca_reg[1])) == NULL) { + printf(": cannot map registers\n"); + return; + } + sc->sc_dmat = ca->ca_dmat; + + if ((sc->sc_dma = mapiodev(ca->ca_reg[2], ca->ca_reg[3])) == NULL) { + printf(": cannot map DMA registers\n"); + goto nodma; + } + + if ((sc->sc_dbdma = dbdma_alloc(sc->sc_dmat, BL_DBDMA_CMDS)) == NULL) { + printf(": cannot alloc DMA descriptors\n"); + goto nodbdma; + } + sc->sc_dmacmd = sc->sc_dbdma->d_addr; + + if ((error = bus_dmamem_alloc(sc->sc_dmat, BL_BUFSZ, 0, 0, + sc->sc_bufseg, 1, &nseg, BUS_DMA_NOWAIT))) { + printf(": cannot allocate DMA mem (%d)\n", error); + goto nodmamem; + } + + if ((error = bus_dmamem_map(sc->sc_dmat, sc->sc_bufseg, nseg, + BL_BUFSZ, (caddr_t *)&sc->sc_buf, BUS_DMA_NOWAIT))) { + printf(": cannot map DMA mem (%d)\n", error); + goto nodmamap; + } + sc->sc_bufpos = sc->sc_buf; + + if ((error = bus_dmamap_create(sc->sc_dmat, BL_BUFSZ, 1, BL_BUFSZ, 0, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &sc->sc_bufmap))) { + printf(": cannot create DMA map (%d)\n", error); + goto nodmacreate; + } + + if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_bufmap, sc->sc_buf, + BL_BUFSZ, NULL, BUS_DMA_NOWAIT))) { + printf(": cannot load DMA map (%d)\n", error); + goto nodmaload; + } + /* XXX: Should probably extract this from the clock data + * property of the soundchip node */ + sc->sc_freq = 44100; + + OF_getprop(sc->sc_node, "interrupts", intr, sizeof(intr)); + sc->sc_intr = intr[2]; + printf(": irq %d\n", sc->sc_intr); + + mac_intr_establish(parent, sc->sc_intr, intr[3] ? IST_LEVEL : + IST_EDGE, IPL_AUDIO, blinkenlights_intr, sc, "blinkenlights"); + + out32rb(sc->sc_reg + I2S_INT, I2S_INT_CLKSTOPPEND); + + keylargo_fcr_disable(I2SClockOffset, I2S0CLKEN); + for (error = 0; error < 1000; error++) { + if (in32rb(sc->sc_reg + I2S_INT) & I2S_INT_CLKSTOPPEND) { + error = 0; + break; + } + delay(1); + } + if (error) { + printf("%s: i2s timeout\n", sc->sc_dev.dv_xname); + goto nodmaload; + } + + out32rb(sc->sc_reg + I2S_FORMAT, CLKSRC_VS); + keylargo_fcr_enable(I2SClockOffset, I2S0CLKEN); + + kthread_create_deferred(blinkenlights_deferred, sc); + timeout_set(&sc->sc_tmo, blinkenlights_timeout, sc); + return; +nodmaload: + bus_dmamap_destroy(sc->sc_dmat, sc->sc_bufmap); +nodmacreate: + bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_buf, BL_BUFSZ); +nodmamap: + bus_dmamem_free(sc->sc_dmat, sc->sc_bufseg, nseg); +nodmamem: + dbdma_free(sc->sc_dbdma); +nodbdma: + unmapiodev((void *)sc->sc_dma, ca->ca_reg[3]); +nodma: + unmapiodev(sc->sc_reg, ca->ca_reg[1]); +} + +void +blinkenlights_deferred(void *v) +{ + kthread_create(blinkenlights_theosDOT, v, NULL, "blinkenlights"); +} + +void +blinkenlights_theosDOT(void *v) +{ + struct blinkenlights_softc *sc = (struct blinkenlights_softc *)v; + u_char leds[16]; + int offset, i, s, speed; + + while (1) { + speed = (averunnable.ldavg[0] / 100) / 2; + for (offset = 8; offset < 16; offset++) { + s = offset - 2; + if (s < 8) { + for (i = 1; i <= (8 - s); i++) + leds[i - 1] = 0x7f / i; + s += 8 - s; + } + for (i = offset; i >= s; i--) + leds[i] = 0xff / (offset + 1 - s); + + blinkenlights_pwm(sc, leds, speed); + bzero(leds, sizeof(leds)); + } + for (offset = 7; offset >= 0; offset--) { + s = offset + 2; + if (s > 7) { + for (i = 1; i <= (s - 7); i++) + leds[15 - (i - 1)] = (0xff / (s - 7)) / i; + s-= s - 7; + } + for (i = offset; i <= s; i++) + leds[i] = 0xff / (offset + 1) - s; + blinkenlights_pwm(sc, leds, speed); + bzero(leds, sizeof(leds)); + } + } +} + +void +blinkenlights_pwm(struct blinkenlights_softc *sc, u_char *leds, int msecs) +{ + uint32_t *p; + int s, l, k, nsamp; + uint32_t freq[16] = {0}; + + p = sc->sc_bufpos; + + nsamp = sc->sc_freq / 1000 * msecs; + + for (k = 1; k < nsamp;) { + if (p - sc->sc_buf < BL_BUFSZ / sizeof(uint32_t)) { + s = 0; + for (l = 0; l < 16; l++) { + s >>= 1; + if (leds[l] && + ((k - freq[l]) == (0xff / leds[l]))) { + s |= 1 << 15; + freq[l] = k; + } + } + *p = (s << 17) | (s >> 15); + p++; + k++; + sc->sc_bufpos++; + } else { + blinkenlights_startdma(sc); + while (sc->sc_dmasts) + tsleep(sc->sc_buf, PWAIT, "blinken", 0); + sc->sc_bufpos = p = sc->sc_buf; + } + } +} + +void +blinkenlights_startdma(struct blinkenlights_softc *sc) +{ + dbdma_command_t *cmdp = sc->sc_dmacmd; + + sc->sc_dmasts = 1; + timeout_add(&sc->sc_tmo, 250); + + DBDMA_BUILD(cmdp, DBDMA_CMD_OUT_LAST, 0, + sc->sc_bufmap->dm_segs[0].ds_len, + sc->sc_bufmap->dm_segs[0].ds_addr, DBDMA_INT_ALWAYS, + DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + cmdp++; + + DBDMA_BUILD(cmdp, DBDMA_CMD_STOP, 0, 0, 0, DBDMA_INT_NEVER, + DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + + dbdma_start(sc->sc_dma, sc->sc_dbdma); +} + +void +blinkenlights_timeout(void *v) +{ + struct blinkenlights_softc *sc = (struct blinkenlights_softc *)v; + + dbdma_reset(sc->sc_dma); + timeout_del(&sc->sc_tmo); + sc->sc_dmasts = 0; + wakeup(sc->sc_buf); +} + +int +blinkenlights_intr(void *v) +{ + struct blinkenlights_softc *sc = (struct blinkenlights_softc *)v; + int status; + dbdma_command_t *cmd; + + cmd = sc->sc_dmacmd; + status = dbdma_ld16(&cmd->d_status); + if (sc->sc_dmasts) { + sc->sc_dmasts = 0; + timeout_del(&sc->sc_tmo); + wakeup(sc->sc_buf); + return (1); + } + return (0); +} diff --git a/sys/arch/macppc/dev/i2s.c b/sys/arch/macppc/dev/i2s.c index 018e02f17e8..b1b466f2e9f 100644 --- a/sys/arch/macppc/dev/i2s.c +++ b/sys/arch/macppc/dev/i2s.c @@ -1,4 +1,4 @@ -/* $OpenBSD: i2s.c,v 1.6 2006/01/08 18:11:03 kettenis Exp $ */ +/* $OpenBSD: i2s.c,v 1.7 2007/04/21 15:43:27 gwk Exp $ */ /* $NetBSD: i2s.c,v 1.1 2003/12/27 02:19:34 grant Exp $ */ /*- @@ -45,6 +45,7 @@ #include <machine/pio.h> #include <macppc/dev/i2svar.h> +#include <macppc/dev/i2sreg.h> #ifdef I2S_DEBUG # define DPRINTF(x) printf x @@ -84,24 +85,6 @@ static int headphone_detect_active; static u_char *lineout_detect; static int lineout_detect_active; - -/* I2S registers */ -#define I2S_INT 0x00 -#define I2S_FORMAT 0x10 -#define I2S_FRAMECOUNT 0x40 -#define I2S_FRAMEMATCH 0x50 -#define I2S_WORDSIZE 0x60 - -/* I2S_INT register definitions */ -#define I2SClockOffset 0x3c -#define I2S_INT_CLKSTOPPEND 0x01000000 - -/* FCR(0x3c) bits */ -#define I2S0CLKEN 0x1000 -#define I2S0EN 0x2000 -#define I2S1CLKEN 0x080000 -#define I2S1EN 0x100000 - /* GPIO bits */ #define GPIO_OUTSEL 0xf0 /* Output select */ /* 0x00 GPIO bit0 is output @@ -824,33 +807,14 @@ i2s_trigger_input(h, start, end, bsize, intr, arg, param) return 1; } -#define CLKSRC_49MHz 0x80000000 /* Use 49152000Hz Osc. */ -#define CLKSRC_45MHz 0x40000000 /* Use 45158400Hz Osc. */ -#define CLKSRC_18MHz 0x00000000 /* Use 18432000Hz Osc. */ -#define MCLK_DIV 0x1f000000 /* MCLK = SRC / DIV */ -#define MCLK_DIV1 0x14000000 /* MCLK = SRC */ -#define MCLK_DIV3 0x13000000 /* MCLK = SRC / 3 */ -#define MCLK_DIV5 0x12000000 /* MCLK = SRC / 5 */ -#define SCLK_DIV 0x00f00000 /* SCLK = MCLK / DIV */ -#define SCLK_DIV1 0x00800000 -#define SCLK_DIV3 0x00900000 -#define SCLK_MASTER 0x00080000 /* Master mode */ -#define SCLK_SLAVE 0x00000000 /* Slave mode */ -#define SERIAL_FORMAT 0x00070000 -#define SERIAL_SONY 0x00000000 -#define SERIAL_64x 0x00010000 -#define SERIAL_32x 0x00020000 -#define SERIAL_DAV 0x00040000 -#define SERIAL_SILICON 0x00050000 - -// rate = fs = LRCLK -// SCLK = 64*LRCLK (I2S) -// MCLK = 256fs (typ. -- changeable) - -// MCLK = clksrc / mdiv -// SCLK = MCLK / sdiv -// rate = SCLK / 64 ( = LRCLK = fs) +/* rate = fs = LRCLK + * SCLK = 64*LRCLK (I2S) + * MCLK = 256fs (typ. -- changeable) + * MCLK = clksrc / mdiv + * SCLK = MCLK / sdiv + * rate = SCLK / 64 ( = LRCLK = fs) + */ int i2s_set_rate(sc, rate) struct i2s_softc *sc; @@ -892,8 +856,8 @@ i2s_set_rate(sc, rate) } MCLK = rate * mclk_fs; - mdiv = clksrc / MCLK; // 4 - sdiv = mclk_fs / 64; // 4 + mdiv = clksrc / MCLK; /* 4 */ + sdiv = mclk_fs / 64; /* 4 */ switch (mdiv) { case 1: diff --git a/sys/arch/macppc/dev/i2sreg.h b/sys/arch/macppc/dev/i2sreg.h new file mode 100644 index 00000000000..de827aa794b --- /dev/null +++ b/sys/arch/macppc/dev/i2sreg.h @@ -0,0 +1,64 @@ +/* $OpenBSD: */ +/*- + * Copyright (c) 2002 Tsubai Masanari. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* I2S registers */ +#define I2S_INT 0x00 +#define I2S_FORMAT 0x10 +#define I2S_FRAMECOUNT 0x40 +#define I2S_FRAMEMATCH 0x50 +#define I2S_WORDSIZE 0x60 + +/* I2S_INT register definitions */ +#define I2SClockOffset 0x3c +#define I2S_INT_CLKSTOPPEND 0x01000000 + +/* FCR(0x3c) bits */ +#define I2S0CLKEN 0x1000 +#define I2S0EN 0x2000 +#define I2S1CLKEN 0x080000 +#define I2S1EN 0x100000 + + +#define CLKSRC_49MHz 0x80000000 /* Use 49152000Hz Osc. */ +#define CLKSRC_45MHz 0x40000000 /* Use 45158400Hz Osc. */ +#define CLKSRC_18MHz 0x00000000 /* Use 18432000Hz Osc. */ +#define CLKSRC_VS 0x01fa0000 /* Magic value of xserve vu-meter */ +#define MCLK_DIV 0x1f000000 /* MCLK = SRC / DIV */ +#define MCLK_DIV1 0x14000000 /* MCLK = SRC */ +#define MCLK_DIV3 0x13000000 /* MCLK = SRC / 3 */ +#define MCLK_DIV5 0x12000000 /* MCLK = SRC / 5 */ +#define SCLK_DIV 0x00f00000 /* SCLK = MCLK / DIV */ +#define SCLK_DIV1 0x00800000 +#define SCLK_DIV3 0x00900000 +#define SCLK_MASTER 0x00080000 /* Master mode */ +#define SCLK_SLAVE 0x00000000 /* Slave mode */ +#define SERIAL_FORMAT 0x00070000 +#define SERIAL_SONY 0x00000000 +#define SERIAL_64x 0x00010000 +#define SERIAL_32x 0x00020000 +#define SERIAL_DAV 0x00040000 +#define SERIAL_SILICON 0x00050000 |