NetBSD/sys/arch/evbsh5/dev/alphaled.c

280 lines
7.1 KiB
C

/* $NetBSD: alphaled.c,v 1.1 2002/11/23 09:05:03 scw Exp $ */
/*
* Copyright 2002 Wasabi Systems, Inc.
* All rights reserved.
*
* Written by Steve C. Woodford for Wasabi Systems, Inc.
*
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed for the NetBSD Project by
* Wasabi Systems, Inc.
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
* or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
* 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.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/fcntl.h>
#include <sys/uio.h>
#include <sys/conf.h>
#include <sys/event.h>
#include <machine/intr.h>
#include <machine/bus.h>
#include <evbsh5/dev/sysfpgavar.h>
#include <evbsh5/include/alphaledio.h>
static int alphaled_match(struct device *, struct cfdata *, void *);
static void alphaled_attach(struct device *, struct device *, void *);
struct alphaled_softc {
struct device sc_dev;
bus_space_tag_t sc_bust;
bus_space_handle_t sc_bush;
int sc_open;
};
CFATTACH_DECL(alphaled, sizeof(struct alphaled_softc),
alphaled_match, alphaled_attach, NULL, NULL);
extern struct cfdriver alphaled_cd;
dev_type_open(alphaled_open);
dev_type_close(alphaled_close);
dev_type_write(alphaled_write);
dev_type_ioctl(alphaled_ioctl);
const struct cdevsw alphaled_cdevsw = {
alphaled_open, alphaled_close,
noread, alphaled_write,
alphaled_ioctl,
nostop, notty, nopoll, nommap, nokqfilter
};
#define ALPHALED_UNIT(s) minor(s)
#define ALPHALED_REG_FLASH(n) ((n) & 7)
#define ALPHALED_REG_CHAR_RAM(n) (((n) & 7) + 0x38)
#define ALPHALED_REG_CTRL 0x30
#define ALPHALED_REG_SIZE 0x100
#define ALPHALED_CTRL_BRIGHT(n) (~(n) & 7)
#define ALPHALED_CTRL_BRIGHT_MASK 0x7
#define ALPHALED_CTRL_FLASH (1u << 3)
#define ALPHALED_CTRL_BLINK (1u << 4)
#define ALPHALED_CTRL_SELF_TEST (2u << 5)
#define ALPHALED_CTRL_SELF_TEST_OK (1u << 5)
#define ALPHALED_CTRL_RESET (1u << 7)
#define alphaled_reg_write(sc, r, v) \
bus_space_write_4((sc)->sc_bust, (sc)->sc_bush, \
(r) << 2, (u_int32_t)(v) & 0xff)
#define alphaled_reg_read(sc, r) \
(u_int8_t)bus_space_read_4((sc)->sc_bust, (sc)->sc_bush, (r) << 2)
static void alphaled_write_string(struct alphaled_softc *, const char *);
/*ARGSUSED*/
static int
alphaled_match(struct device *parent, struct cfdata *cf, void *aux)
{
struct sysfpga_attach_args *sa = aux;
struct alphaled_softc sc;
u_int8_t rv;
int i;
if (strcmp(sa->sa_name, alphaled_cd.cd_name) != 0)
return (0);
if (bus_space_map(sa->sa_bust, sa->sa_offset, ALPHALED_REG_SIZE, 0,
&sc.sc_bush))
return (0);
sc.sc_bust = sa->sa_bust;
alphaled_reg_write(&sc, ALPHALED_REG_CTRL, ALPHALED_CTRL_RESET);
for (i = 10; i; i--) {
delay(50);
rv = alphaled_reg_read(&sc, ALPHALED_REG_CTRL);
if ((rv & ALPHALED_CTRL_RESET) == 0)
break;
}
bus_space_unmap(sa->sa_bust, sc.sc_bush, ALPHALED_REG_SIZE);
return (i);
}
/*ARGSUSED*/
static void
alphaled_attach(struct device *parent, struct device *self, void *aux)
{
struct alphaled_softc *sc = (struct alphaled_softc *)self;
struct sysfpga_attach_args *sa = aux;
printf(": Alphanumeric LED display\n");
sc->sc_bust = sa->sa_bust;
sc->sc_open = 0;
/* Map the device. */
bus_space_map(sc->sc_bust, sa->sa_offset, ALPHALED_REG_SIZE, 0,
&sc->sc_bush);
}
/*ARGSUSED*/
int
alphaled_open(dev_t dev, int flag, int mode, struct proc *p)
{
struct alphaled_softc *sc;
if ((sc = device_lookup(&alphaled_cd, ALPHALED_UNIT(dev))) == NULL)
return (ENXIO);
if (sc->sc_open)
return (EBUSY);
if ((flag & FWRITE) == 0)
return (EPERM);
sc->sc_open = 1;
alphaled_write_string(sc, NULL);
return (0);
}
/*ARGSUSED*/
int
alphaled_close(dev_t dev, int flag, int mode, struct proc *p)
{
struct alphaled_softc *sc;
if ((sc = device_lookup(&alphaled_cd, ALPHALED_UNIT(dev))) == NULL)
return (ENXIO);
sc->sc_open = 0;
return (0);
}
/*ARGSUSED*/
int
alphaled_write(dev_t dev, struct uio *uio, int flags)
{
struct alphaled_softc *sc;
char buf[ALPHALED_NUM_CHARS];
size_t n;
if ((sc = device_lookup(&alphaled_cd, ALPHALED_UNIT(dev))) == NULL)
return (ENXIO);
while ((n = min(ALPHALED_NUM_CHARS, uio->uio_resid)) != 0) {
memset(buf, 0, sizeof(buf));
if (uiomove(buf, n, uio))
return (EFAULT);
alphaled_write_string(sc, buf);
}
return (0);
}
/*ARGSUSED*/
int
alphaled_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
struct alphaled_softc *sc;
u_int8_t rv;
int d;
int i;
if ((sc = device_lookup(&alphaled_cd, ALPHALED_UNIT(dev))) == NULL)
return (ENXIO);
switch (cmd) {
case ALPHALEDIOBRIGHT:
d = *(int *)data;
rv = alphaled_reg_read(sc, ALPHALED_REG_CTRL);
rv &= ~ALPHALED_CTRL_BRIGHT_MASK;
rv |= ALPHALED_CTRL_BRIGHT(d);
alphaled_reg_write(sc, ALPHALED_REG_CTRL, rv);
break;
case ALPHALEDIOFLASH:
rv = alphaled_reg_read(sc, ALPHALED_REG_CTRL);
rv &= ~ALPHALED_CTRL_FLASH;
d = *(int *)data;
if ((d & ALPHALED_FLASH_MASK) != 0) {
rv |= ALPHALED_CTRL_FLASH;
for (i = 0; i < ALPHALED_NUM_CHARS; i++) {
alphaled_reg_write(sc, ALPHALED_REG_FLASH(i),
(d & (1 << i)) ? 1 : 0);
}
}
alphaled_reg_write(sc, ALPHALED_REG_CTRL, rv);
break;
case ALPHALEDIOBLINK:
rv = alphaled_reg_read(sc, ALPHALED_REG_CTRL);
d = *(int *)data;
if (d)
rv |= ALPHALED_CTRL_BLINK;
else
rv &= ~ALPHALED_CTRL_BLINK;
alphaled_reg_write(sc, ALPHALED_REG_CTRL, rv);
break;
default:
return (ENODEV);
}
return (0);
}
static void
alphaled_write_string(struct alphaled_softc *sc, const char *str)
{
char ch;
int i;
if (str) {
for (i = 0; i < ALPHALED_NUM_CHARS && str[i]; i++) {
ch = str[i] & 0x7f;
alphaled_reg_write(sc, ALPHALED_REG_CHAR_RAM(i), ch);
}
} else
i = 0;
for (; i < ALPHALED_NUM_CHARS; i++)
alphaled_reg_write(sc, ALPHALED_REG_CHAR_RAM(i), ' ');
}