452 lines
8.2 KiB
C
452 lines
8.2 KiB
C
/* $Id: imxuart.c,v 1.2 2008/04/27 18:58:44 matt Exp $ */
|
|
#include <sys/types.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/device.h>
|
|
#include <sys/conf.h>
|
|
|
|
#include <arm/armreg.h>
|
|
|
|
#include <sys/tty.h>
|
|
#include <sys/termios.h>
|
|
#include <dev/cons.h>
|
|
|
|
#include <machine/bus.h>
|
|
#include <arm/imx/imx31var.h>
|
|
#include <arm/imx/imxuartreg.h>
|
|
#include <arm/imx/imxuartvar.h>
|
|
#include <evbarm/imx31/imx31lk_reg.h>
|
|
|
|
#define IMXUART_UNIT_MASK 0x7ffff
|
|
#define IMXUART_DIALOUT_MASK 0x80000
|
|
|
|
#define IMXUART_UNIT(dev) (minor(dev) & IMXUART_UNIT_MASK)
|
|
#define IMXUART_DIALOUT(dev) (minor(dev) & IMXUART_DIALOUT_MASK)
|
|
|
|
|
|
|
|
#define __TRACE imxuart_puts(&imxuart_softc, __FUNCTION__ )
|
|
|
|
extern struct bus_space imx31_bs_tag;
|
|
|
|
imxuart_softc_t imxuart_softc = { { 0, }, };
|
|
|
|
uint32_t imxuart_snapshot_data[32];
|
|
const char *imxuart_test_string =
|
|
"0123456789012345678901234567890123456789\r\n";
|
|
|
|
|
|
cons_decl(imxuart);
|
|
static struct consdev imxuartcntab = cons_init(imxuart);
|
|
|
|
#ifdef NOTYET
|
|
dev_type_open(imxuartopen);
|
|
dev_type_close(imxuartclose);
|
|
dev_type_read(imxuartread);
|
|
dev_type_write(imxuartwrite);
|
|
dev_type_ioctl(imxuartioctl);
|
|
dev_type_stop(imxuartstop);
|
|
dev_type_tty(imxuarttty);
|
|
dev_type_poll(imxuartpoll);
|
|
const struct cdevsw imxuart_cdevsw = {
|
|
imxuartopen, imxuartclose, imxuartread, imxuartwrite,
|
|
imxuartioctl, imxuartstop, imxuarttty, imxuartpoll,
|
|
nommap, ttykqfilter, D_TTY
|
|
};
|
|
#else
|
|
const struct cdevsw imxuart_cdevsw = {
|
|
nullopen, nullclose, nullread, nullwrite,
|
|
nullioctl, nullstop, notty, nullpoll,
|
|
nommap, nullkqfilter, D_TTY
|
|
};
|
|
#endif
|
|
|
|
|
|
int imxuart_cnattach(bus_space_tag_t, bus_addr_t, int, int, int, tcflag_t);
|
|
int imxuart_puts(imxuart_softc_t *, const char *);
|
|
int imxuart_putchar(imxuart_softc_t *, int);
|
|
int imxuart_getchar(imxuart_softc_t *);
|
|
|
|
static int imxuart_urxd_brk(void);
|
|
static void imxuart_urxd_err(imxuart_softc_t *, uint32_t);
|
|
|
|
static int imxuart_match(struct device *, struct cfdata *, void *);
|
|
static void imxuart_attach(struct device *, struct device *, void *);
|
|
|
|
static void imxuart_start(struct tty *);
|
|
static int imxuart_param(struct tty *, struct termios *);
|
|
static void imxuart_shutdownhook(void *arg);
|
|
|
|
CFATTACH_DECL(imxuart, sizeof(struct imxuart_softc),
|
|
imxuart_match, imxuart_attach, NULL, NULL);
|
|
|
|
|
|
/*
|
|
* disable the specified IMX UART interrupt in softc
|
|
* return -1 on error
|
|
* else return previous enabled state
|
|
*/
|
|
static __inline int
|
|
imxuart_intrspec_dis(imxuart_softc_t *sc, imxuart_intrix_t ix)
|
|
{
|
|
const imxuart_intrspec_t *spec = &imxuart_intrspec_tab[ix];
|
|
uint32_t v;
|
|
uint32_t b;
|
|
const uint32_t syncbit = 1 << 31;
|
|
uint n;
|
|
|
|
if (ix >= IMXUART_INTRSPEC_TAB_SZ)
|
|
return -1;
|
|
|
|
v = (1 << ix);
|
|
if ((sc->sc_intrspec_enb & v) == 0)
|
|
return 0;
|
|
sc->sc_intrspec_enb &= ~v;
|
|
|
|
n = spec->enb_reg;
|
|
b = spec->enb_bit;
|
|
v = sc->sc_ucr[n];
|
|
v &= ~b;
|
|
v |= syncbit;
|
|
sc->sc_ucr[n] = v;
|
|
|
|
n = spec->flg_reg;
|
|
b = spec->flg_bit;
|
|
v = sc->sc_usr[n];
|
|
v &= ~b;
|
|
v |= syncbit;
|
|
sc->sc_usr[n] = v;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* enable the specified IMX UART interrupt in softc
|
|
* return -1 on error
|
|
* else return previous enabled state
|
|
*/
|
|
static __inline int
|
|
imxuart_intrspec_enb(imxuart_softc_t *sc, imxuart_intrix_t ix)
|
|
{
|
|
const imxuart_intrspec_t *spec = &imxuart_intrspec_tab[ix];
|
|
uint32_t v;
|
|
uint n;
|
|
const uint32_t syncbit = 1 << 31;
|
|
|
|
if (ix >= IMXUART_INTRSPEC_TAB_SZ)
|
|
return -1;
|
|
|
|
v = (1 << ix);
|
|
if ((sc->sc_intrspec_enb & v) != 0)
|
|
return 1;
|
|
sc->sc_intrspec_enb |= v;
|
|
|
|
|
|
n = spec->enb_reg;
|
|
v = spec->enb_bit;
|
|
v |= syncbit;
|
|
sc->sc_ucr[n] |= v;
|
|
|
|
n = spec->flg_reg;
|
|
v = spec->flg_bit;
|
|
v |= syncbit;
|
|
sc->sc_usr[n] |= v;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sync softc interrupt spec to UART Control regs
|
|
*/
|
|
static __inline void
|
|
imxuart_intrspec_sync(imxuart_softc_t *sc)
|
|
{
|
|
int i;
|
|
uint32_t r;
|
|
uint32_t v;
|
|
const uint32_t syncbit = 1 << 31;
|
|
const uint32_t mask[4] = {
|
|
IMXUART_INTRS_UCR1,
|
|
IMXUART_INTRS_UCR2,
|
|
IMXUART_INTRS_UCR3,
|
|
IMXUART_INTRS_UCR4
|
|
};
|
|
|
|
for (i=0; i < 4; i++) {
|
|
v = sc->sc_ucr[i];
|
|
if (v & syncbit) {
|
|
v &= ~syncbit;
|
|
sc->sc_ucr[i] = v;
|
|
r = bus_space_read_4(sc->sc_bt, sc->sc_bh, IMX_UCRn(i));
|
|
r &= ~mask[i];
|
|
r |= v;
|
|
bus_space_write_4(sc->sc_bt, sc->sc_bh, IMX_UCRn(i), r);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
imxuart_init(imxuart_softc_t *sc, uint bh)
|
|
{
|
|
if (sc == 0)
|
|
sc = &imxuart_softc;
|
|
sc->sc_init_cnt++;
|
|
cn_tab = &imxuartcntab;
|
|
sc->sc_bt = &imx31_bs_tag;
|
|
sc->sc_bh = bh;
|
|
|
|
memset(&sc->sc_errors, 0, sizeof(sc->sc_errors));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
imxuart_puts(imxuart_softc_t *sc, const char *s)
|
|
{
|
|
char c;
|
|
int err = -2;
|
|
|
|
for(;;) {
|
|
c = *s++;
|
|
if (c == '\0')
|
|
break;
|
|
err = imxuart_putchar(sc, c);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int
|
|
imxuart_putchar(imxuart_softc_t *sc, int c)
|
|
{
|
|
uint32_t r;
|
|
|
|
for (;;) {
|
|
r = bus_space_read_4(sc->sc_bt, sc->sc_bh, IMX_UTS);
|
|
if ((r & IMX_UTS_TXFUL) == 0)
|
|
break;
|
|
}
|
|
|
|
r = (uint32_t)c & IMX_UTXD_TX_DATA;
|
|
bus_space_write_4(sc->sc_bt, sc->sc_bh, IMX_UTXD, r);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
imxuart_getchar(imxuart_softc_t *sc)
|
|
{
|
|
uint32_t r;
|
|
int c;
|
|
|
|
for(;;) {
|
|
r = bus_space_read_4(sc->sc_bt, sc->sc_bh, IMX_URXD);
|
|
if (r & IMX_URXD_ERR) {
|
|
imxuart_urxd_err(sc, r);
|
|
continue;
|
|
}
|
|
if (r & IMX_URXD_CHARDY) {
|
|
c = (int)(r & IMX_URXD_RX_DATA);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
static int
|
|
imxuart_urxd_brk(void)
|
|
{
|
|
#ifdef DDB
|
|
if (cn_tab == &imxuartcntab) {
|
|
Debugger();
|
|
return 0;
|
|
}
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
imxuart_urxd_err(imxuart_softc_t *sc, uint32_t r)
|
|
{
|
|
if (r & IMX_URXD_BRK)
|
|
if (imxuart_urxd_brk() == 0)
|
|
return;
|
|
|
|
sc->sc_errors.err++;
|
|
if (r & IMX_URXD_BRK)
|
|
sc->sc_errors.brk++;
|
|
if (r & IMX_URXD_PRERR)
|
|
sc->sc_errors.prerr++;
|
|
if (r & IMX_URXD_FRMERR)
|
|
sc->sc_errors.frmerr++;
|
|
if (r & IMX_URXD_OVRRUN)
|
|
sc->sc_errors.ovrrun++;
|
|
}
|
|
|
|
static int
|
|
imxuart_snapshot(imxuart_softc_t *sc, uint32_t *p)
|
|
{
|
|
int i;
|
|
const uint r[] = { IMX_URXD, IMX_UTXD, IMX_UCR1, IMX_UCR2,
|
|
IMX_UCR3, IMX_UCR4, IMX_UFCR, IMX_USR1,
|
|
IMX_USR2, IMX_UESC, IMX_UTIM, IMX_UBIR,
|
|
IMX_UBMR, IMX_UBRC, IMX_ONEMS, IMX_UTS };
|
|
|
|
for (i=0; i < ((sizeof(r)/sizeof(r[0]))); i++) {
|
|
*p++ = sc->sc_bh + r[i];
|
|
*p++ = bus_space_read_4(sc->sc_bt, sc->sc_bh, r[i]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
imxuart_test(void)
|
|
{
|
|
imxuart_softc_t *sc = &imxuart_softc;
|
|
int n;
|
|
int err;
|
|
|
|
err = imxuart_init(sc, 1);
|
|
if (err != 0)
|
|
return err;
|
|
|
|
if (0) {
|
|
extern u_int cpufunc_id(void);
|
|
err = cpufunc_id();
|
|
return err;
|
|
}
|
|
|
|
#if 0
|
|
err = imxuart_snapshot(sc, imxuart_snapshot_data);
|
|
if (err != 0)
|
|
return err;
|
|
#endif
|
|
|
|
|
|
err = imxuart_putchar(sc, 'x');
|
|
if (err != 0)
|
|
return err;
|
|
|
|
for (n=100; n--; ) {
|
|
err = imxuart_puts(sc, imxuart_test_string);
|
|
if (err != 0)
|
|
break;
|
|
}
|
|
|
|
err = imxuart_snapshot(sc, imxuart_snapshot_data);
|
|
if (err != 0)
|
|
return err;
|
|
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
imxuart_match(struct device *parent, struct cfdata *cf, void *aux)
|
|
{
|
|
struct aips_attach_args * const aipsa = aux;
|
|
|
|
switch (aipsa->aipsa_addr) {
|
|
case IMX_UART1_BASE:
|
|
case IMX_UART2_BASE:
|
|
case IMX_UART3_BASE:
|
|
case IMX_UART4_BASE:
|
|
case IMX_UART5_BASE:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
imxuart_attach(struct device *parent, struct device *self, void *aux)
|
|
{
|
|
imxuart_softc_t *sc = (void *)self;
|
|
struct aips_attach_args * const aipsa = aux;
|
|
struct tty *tp;
|
|
|
|
sc->sc_bt = aipsa->aipsa_memt;
|
|
sc->sc_addr = aipsa->aipsa_addr;
|
|
sc->sc_size = aipsa->aipsa_size;
|
|
sc->sc_intr = aipsa->aipsa_intr;
|
|
|
|
sc->sc_tty = tp = ttymalloc();
|
|
tp->t_oproc = imxuart_start;
|
|
tp->t_param = imxuart_param;
|
|
tty_attach(tp);
|
|
|
|
shutdownhook_establish(imxuart_shutdownhook, sc);
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
void
|
|
imxuartcnprobe(struct consdev *cp)
|
|
{
|
|
int major;
|
|
|
|
__TRACE;
|
|
major = cdevsw_lookup_major(&imxuart_cdevsw);
|
|
cp->cn_dev = makedev(major, 0); /* XXX unit 0 */
|
|
cp->cn_pri = CN_REMOTE;
|
|
}
|
|
|
|
static void
|
|
imxuart_start(struct tty *tp)
|
|
{
|
|
__TRACE;
|
|
}
|
|
|
|
static int
|
|
imxuart_param(struct tty *tp, struct termios *termios)
|
|
{
|
|
__TRACE;
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
imxuart_shutdownhook(void *arg)
|
|
{
|
|
__TRACE;
|
|
}
|
|
|
|
void
|
|
imxuartcninit(struct consdev *cp)
|
|
{
|
|
__TRACE;
|
|
}
|
|
|
|
int
|
|
imxuartcngetc(dev_t dev)
|
|
{
|
|
struct imxuart_softc *sc;
|
|
uint unit;
|
|
int c;
|
|
|
|
unit = IMXUART_UNIT(dev);
|
|
if (unit != 0)
|
|
return 0;
|
|
sc = &imxuart_softc;
|
|
c = imxuart_getchar(sc);
|
|
return c;
|
|
}
|
|
|
|
void
|
|
imxuartcnputc(dev_t dev, int c)
|
|
{
|
|
(void)imxuart_putchar(&imxuart_softc, c);
|
|
}
|
|
|
|
void
|
|
imxuartcnpollc(dev_t dev, int mode)
|
|
{
|
|
/* always polled for now */
|
|
}
|
|
int
|
|
imxuart_cnattach(bus_space_tag_t iot, bus_addr_t iobase,
|
|
int rate, int frequency, int type, tcflag_t cflag)
|
|
{
|
|
cn_tab = &imxuartcntab;
|
|
return 0;
|
|
}
|
|
|