/* $Id: jmcu.c,v 1.1 2008/03/04 16:09:01 nbrk Exp $ */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/proc.h>
#include <sys/conf.h>
#include <sys/endian.h>
#include <machine/intr.h>
#include <machine/bus.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wskbdvar.h>
#include <dev/wscons/wsksymdef.h>
#include <dev/wscons/wsksymvar.h>
#include <dev/spivar.h>
#include <jornada/dev/jmcu_cmd.h>
#include <arm/sa11x0/sa11x0_var.h>
#include <arm/sa11x0/sa11x0_reg.h>
#include <arm/sa11x0/sa11x0_gpiovar.h>
#include <jornada/dev/jmcu_kbdmap.h>
#define JMCU_KBD_GPIOINTR 0
#define JMCU_TS_GPIOINTR 15
#define JMCU_KBD_NEVENTS 8
struct jmcu_softc {
struct device sc_dev;
struct spi_bus *sc_bus;
void *sc_spisc;
uint8_t sc_kbdeventsbuf[JMCU_KBD_NEVENTS];
uint8_t sc_kbdeventsnum;
};
struct jmcu_softc *jmcu_sc;
struct wskbd_mapdata jmcukbd_keymapdata = {
jmcukbd_keydesctab,
KB_US,
};
/*
* Prototypes.
*/
int jmcu_match(struct device *parent, void *cf, void *aux);
void jmcu_attach(struct device *parent, struct device *self, void *aux);
int jmcu_kbdintr(void *arg);
int jmcu_touchintr(void *arg);
void jmcukbd_cngetc(void *v, u_int *type, int *data);
void jmcukbd_enqueue(struct jmcu_softc *sc, uint8_t keycode);
void jmcukbd_dequeue(struct jmcu_softc *sc, uint8_t *keycodep);
void jmcukbd_getcodes(struct jmcu_softc *sc, int block);
void jmcubatt_status(struct jmcu_softc *sc);
/*
* Autoconf glue.
*/
struct cfattach jmcu_ca = {
sizeof(struct jmcu_softc), jmcu_match, jmcu_attach, NULL, NULL
};
struct cfdriver jmcu_cd = {
NULL,
"jmcu",
DV_DULL
};
/*
* Console ops.
*/
struct wskbd_consops jmcukbd_consops = {
jmcukbd_cngetc,
NULL, /* pollc */
NULL /* bell */
};
int
jmcu_match(struct device *parent, void *cf, void *aux)
{
return (1);
}
void
jmcu_attach(struct device *parent, struct device *self, void *aux)
{
struct jmcu_softc *sc = (struct jmcu_softc *)self;
struct spibus_attach_args *sba = aux;
struct wskbddev_attach_args wda;
sc->sc_bus = sba->sba_bus;
sc->sc_spisc = sba->sba_spisc;
printf(": Jornada MCU\n");
jmcu_sc = sc;
/* XXX */
jmcubatt_status(sc);
sa11x0_gpio_intr_establish(JMCU_KBD_GPIOINTR, IST_EDGE_FALLING, IPL_TTY, jmcu_kbdintr, sc, "jmcukbd");
sa11x0_gpio_intr_establish(JMCU_TS_GPIOINTR, IST_EDGE_FALLING, IPL_TTY, jmcu_touchintr, sc, "jmcutouch");
wskbd_cnattach(&jmcukbd_consops, sc, &jmcukbd_keymapdata);
wda.console = 1;
wda.keymap = &jmcukbd_keymapdata;
wda.accessops = NULL; //jmcukbd_accessops;
wda.accesscookie = sc;
config_found(self, &wda, wskbddevprint);
return;
}
int
jmcu_kbdintr(void *arg)
{
struct jmcu_softc *sc = arg;
printf("%s: jmcu_kbdintr\n", sc->sc_dev.dv_xname);
return(0);
}
int
jmcu_touchintr(void *arg)
{
struct jmcu_softc *sc = arg;
printf("%s: jmcu_touchintr\n", sc->sc_dev.dv_xname);
return(0);
}
void
jmcukbd_cngetc(void *v, u_int *type, int *data)
{
struct jmcu_softc *sc = v;
uint8_t ourdata;
printf("jmcukbd_cngetc:\n");
if (sc->sc_kbdeventsnum == 0)
jmcukbd_getcodes(sc, 1); /* note that we've passed blocking flag */
jmcukbd_dequeue(sc, &ourdata);
*data = ourdata & 0x7f;
*type = ourdata & 0x80 ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN;
}
void
jmcukbd_getcodes(struct jmcu_softc *sc, int block)
{
int nkeys;
uint8_t keycode;
/* wake up the MCU */
sa11x0_gpio_clear_bit(25);
spi_acquire(sc->sc_spisc);
do {
/* ask mcu about how many keypress events are there; should get TXDUMMY here */
if (spi_shift_1(sc->sc_spisc, JMCU_CMD_GETKEYCODES) != JMCU_CMD_TXDUMMY)
printf("%s: WARNING, no txdummy received\n", sc->sc_dev.dv_xname);
nkeys = spi_shift_1(sc->sc_spisc, JMCU_CMD_TXDUMMY);
} while(nkeys == 0 && block);
while (nkeys) {
keycode = spi_shift_1(sc->sc_spisc, JMCU_CMD_TXDUMMY);
jmcukbd_enqueue(sc, keycode);
nkeys--;
}
spi_release(sc->sc_spisc);
/* park off the MCU */
sa11x0_gpio_set_bit(25);
}
void
jmcukbd_enqueue(struct jmcu_softc *sc, uint8_t keycode)
{
if (sc->sc_kbdeventsnum >= JMCU_KBD_NEVENTS) {
printf("%s: WARNING, event queue is full; keycode 0x%x will be discarded\n", sc->sc_dev.dv_xname, keycode);
return;
}
sc->sc_kbdeventsbuf[sc->sc_kbdeventsnum] = keycode;
sc->sc_kbdeventsnum++;
}
void
jmcukbd_dequeue(struct jmcu_softc *sc, uint8_t *keycodep)
{
if (sc->sc_kbdeventsnum < 1) {
printf("%s: WARNING, event queue is empty\n", sc->sc_dev.dv_xname);
return;
}
sc->sc_kbdeventsnum--;
*keycodep = sc->sc_kbdeventsbuf[sc->sc_kbdeventsnum];
}
void
jmcubatt_status(struct jmcu_softc *sc)
{
uint16_t mainbatt;
uint16_t backbatt;
uint8_t fraction;
/* wake up the MCU */
sa11x0_gpio_clear_bit(25);
spi_acquire(sc->sc_spisc);
spi_shift_1(sc->sc_spisc, JMCU_CMD_GETBATTERYDATA);
mainbatt = spi_shift_1(sc->sc_spisc, JMCU_CMD_TXDUMMY);
backbatt = spi_shift_1(sc->sc_spisc, JMCU_CMD_TXDUMMY);
fraction = spi_shift_1(sc->sc_spisc, JMCU_CMD_TXDUMMY);
spi_release(sc->sc_spisc);
/* park off the MCU */
sa11x0_gpio_set_bit(25);
/* fraction[0:1] is mainbatt[8:9]; fraction[2:3] is backbatt[8:9] */
mainbatt |= (uint16_t)((fraction & 0x03) << 8);
backbatt |= (uint16_t)((fraction & 0xc0) << 6);
printf("%s: battery status: main %d, backup %d\n", sc->sc_dev.dv_xname, mainbatt, backbatt);
}