Commit 1a308b2c authored by David S. Miller's avatar David S. Miller

SPARC: Beginning of converting Sparc serial drivers to UART layer.

parent 2df81708
/* sun.c
*
* Generic SUN serial/kbd/ms layer. Based entirely
* upon drivers/sbus/char/sunserial.c which is:
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*
* Port to new UART layer is:
*
* Copyright (C) 2002 David S. Miller (davem@redhat.com)
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/kbd_diacr.h>
#include <linux/init.h>
#include <linux/bootmem.h>
#include <asm/oplib.h>
#include "sun.h"
int serial_console;
int stop_a_enabled = 1;
int __init con_is_present(void)
{
return serial_console ? 0 : 1;
}
static void __init nop_rs_kgdb_hook(int channel)
{ printk("Oops: nop_rs_kgdb_hook called\n"); }
static void nop_rs_change_mouse_baud(int baud)
{ printk("Oops: nop_rs_change_mouse_baud called\n"); }
static int nop_rs_read_proc(char *page, char **start, off_t off, int count,
int *eof, void *data)
{ printk("Oops: nop_rs_read_proc called\n"); return 0; }
struct sunserial_operations rs_ops = {
0,
nop_rs_kgdb_hook,
nop_rs_change_mouse_baud,
nop_rs_read_proc
};
static int __init serial_sun_init(void)
{
struct sun_initfunc *init;
init = rs_ops.rs_init;
while (init) {
(void) init->init();
init = init->next;
}
return 0;
}
static void __exit serial_sun_exit(void)
{
}
module_init(serial_sun_init);
module_exit(serial_sun_exit);
void __init rs_kgdb_hook(int channel)
{ rs_ops.rs_kgdb_hook(channel); }
void rs_change_mouse_baud(int baud)
{ rs_ops.rs_change_mouse_baud(baud); }
int rs_read_proc(char *page, char **start, off_t off, int count,
int *eof, void *data)
{ return rs_ops.rs_read_proc(page, start, off, count, eof, data); }
static void nop_compute_shiftstate (void)
{ printk("Oops: nop_compute_shiftstate called\n"); }
static void nop_setledstate (struct kbd_struct *kbd, unsigned int ledstate)
{ printk("Oops: nop_setledstate called\n"); }
static unsigned char nop_getledstate (void)
{ printk("Oops: nop_getledstate called\n"); return 0; }
static int nop_setkeycode (unsigned int scancode, unsigned int keycode)
{ printk("Oops: nop_setkeycode called\n"); return -EINVAL; }
static int nop_getkeycode (unsigned int scancode)
{ printk("Oops: nop_getkeycode called\n"); return -EINVAL; }
struct sunkbd_operations kbd_ops = {
0,
nop_compute_shiftstate,
nop_setledstate,
nop_getledstate,
nop_setkeycode,
nop_getkeycode
};
#ifdef CONFIG_USB
extern void pci_compute_shiftstate(void);
extern int pci_setkeycode(unsigned int, unsigned int);
extern int pci_getkeycode(unsigned int);
extern void pci_setledstate(struct kbd_struct *, unsigned int);
extern unsigned char pci_getledstate(void);
extern int pcikbd_init(void);
#endif
int kbd_init(void)
{
struct sun_initfunc *init;
int err = -ENODEV;
init = kbd_ops.kbd_init;
while (init) {
err = init->init();
init = init->next;
}
#ifdef CONFIG_USB
if (!serial_console &&
kbd_ops.compute_shiftstate == nop_compute_shiftstate) {
printk("kbd_init: Assuming USB keyboard.\n");
kbd_ops.compute_shiftstate = pci_compute_shiftstate;
kbd_ops.setledstate = pci_setledstate;
kbd_ops.getledstate = pci_getledstate;
kbd_ops.setkeycode = pci_setkeycode;
kbd_ops.getkeycode = pci_getkeycode;
pcikbd_init();
}
#endif
return err;
}
void compute_shiftstate(void)
{ kbd_ops.compute_shiftstate(); }
void setledstate(struct kbd_struct *kbd, unsigned int ledstate)
{ kbd_ops.setledstate(kbd, ledstate); }
unsigned char getledstate(void)
{ return kbd_ops.getledstate(); }
int setkeycode(unsigned int scancode, unsigned int keycode)
{ return kbd_ops.setkeycode(scancode, keycode); }
int getkeycode(unsigned int scancode)
{ return kbd_ops.getkeycode(scancode); }
void * __init sunserial_alloc_bootmem(unsigned long size)
{
void *ret;
ret = __alloc_bootmem(size, SMP_CACHE_BYTES, 0UL);
if (ret != NULL)
memset(ret, 0, size);
return ret;
}
void
sunserial_setsun_initfunc(int (*init) (void))
{
struct sun_initfunc *rs_initfunc;
rs_initfunc = sunserial_alloc_bootmem(sizeof(*rs_initfunc));
if (rs_initfunc == NULL) {
prom_printf("sunserial_setsun_initfunc: Cannot alloc sun_initfunc.\n");
prom_halt();
}
rs_initfunc->init = init;
rs_initfunc->next = rs_ops.rs_init;
rs_ops.rs_init = rs_initfunc;
}
void
sunserial_console_termios(struct console *con)
{
char mode[16], buf[16], *s;
char *mode_prop = "ttyX-mode";
char *cd_prop = "ttyX-ignore-cd";
char *dtr_prop = "ttyX-rts-dtr-off";
int baud, bits, stop, cflag;
char parity;
int carrier = 0;
int rtsdtr = 1;
int topnd, nd;
if (!serial_console)
return;
if (serial_console == 1) {
mode_prop[3] = 'a';
cd_prop[3] = 'a';
dtr_prop[3] = 'a';
} else {
mode_prop[3] = 'b';
cd_prop[3] = 'b';
dtr_prop[3] = 'b';
}
topnd = prom_getchild(prom_root_node);
nd = prom_searchsiblings(topnd, "options");
if (!nd) {
strcpy(mode, "9600,8,n,1,-");
goto no_options;
}
if (!prom_node_has_property(nd, mode_prop)) {
strcpy(mode, "9600,8,n,1,-");
goto no_options;
}
memset(mode, 0, sizeof(mode));
prom_getstring(nd, mode_prop, mode, sizeof(mode));
if (prom_node_has_property(nd, cd_prop)) {
memset(buf, 0, sizeof(buf));
prom_getstring(nd, cd_prop, buf, sizeof(buf));
if (!strcmp(buf, "false"))
carrier = 1;
/* XXX: this is unused below. */
}
if (prom_node_has_property(nd, cd_prop)) {
memset(buf, 0, sizeof(buf));
prom_getstring(nd, cd_prop, buf, sizeof(buf));
if (!strcmp(buf, "false"))
rtsdtr = 0;
/* XXX: this is unused below. */
}
no_options:
cflag = CREAD | HUPCL | CLOCAL;
s = mode;
baud = simple_strtoul(s, 0, 0);
s = strchr(s, ',');
bits = simple_strtoul(++s, 0, 0);
s = strchr(s, ',');
parity = *(++s);
s = strchr(s, ',');
stop = simple_strtoul(++s, 0, 0);
s = strchr(s, ',');
/* XXX handshake is not handled here. */
switch (baud) {
case 150: cflag |= B150; break;
case 300: cflag |= B300; break;
case 600: cflag |= B600; break;
case 1200: cflag |= B1200; break;
case 2400: cflag |= B2400; break;
case 4800: cflag |= B4800; break;
case 9600: cflag |= B9600; break;
case 19200: cflag |= B19200; break;
case 38400: cflag |= B38400; break;
default: baud = 9600; cflag |= B9600; break;
}
switch (bits) {
case 5: cflag |= CS5; break;
case 6: cflag |= CS6; break;
case 7: cflag |= CS7; break;
case 8: cflag |= CS8; break;
default: cflag |= CS8; break;
}
switch (parity) {
case 'o': cflag |= (PARENB | PARODD); break;
case 'e': cflag |= PARENB; break;
case 'n': default: break;
}
switch (stop) {
case 2: cflag |= CSTOPB; break;
case 1: default: break;
}
con->cflag = cflag;
}
void
sunkbd_setsun_initfunc(int (*init)(void))
{
struct sun_initfunc *kbd_initfunc;
kbd_initfunc = sunserial_alloc_bootmem(sizeof(*kbd_initfunc));
if (kbd_initfunc == NULL) {
prom_printf("sunkbd_setsun_initfunc: Cannot alloc sun_initfunc.\n");
prom_halt();
}
kbd_initfunc->init = init;
kbd_initfunc->next = kbd_ops.kbd_init;
kbd_ops.kbd_init = kbd_initfunc;
}
#ifdef CONFIG_PCI
void
sunkbd_install_keymaps(ushort **src_key_maps, unsigned int src_keymap_count,
char *src_func_buf, char **src_func_table,
int src_funcbufsize, int src_funcbufleft,
struct kbdiacr *src_accent_table,
unsigned int src_accent_table_size)
{
extern unsigned int keymap_count;
int i, j;
for (i = 0; i < MAX_NR_KEYMAPS; i++) {
if (src_key_maps[i]) {
if (!key_maps[i]) {
key_maps[i] = (ushort *)
sunserial_alloc_bootmem(NR_KEYS * sizeof(ushort));
if (key_maps[i] == NULL) {
prom_printf("sunkbd_install_keymaps: "
"Cannot alloc key_map(%d).\n", i);
prom_halt();
}
}
for (j = 0; j < NR_KEYS; j++)
key_maps[i][j] = src_key_maps[i][j];
}
key_maps[i] = src_key_maps[i];
}
keymap_count = src_keymap_count;
for (i = 0; i < MAX_NR_FUNC; i++)
func_table[i] = src_func_table[i];
funcbufptr = src_func_buf;
funcbufsize = src_funcbufsize;
funcbufleft = src_funcbufleft;
for (i = 0; i < MAX_DIACR; i++)
accent_table[i] = src_accent_table[i];
accent_table_size = src_accent_table_size;
}
#endif
extern int sunsu_probe(void);
extern int sunzilog_probe(void);
#ifdef CONFIG_SAB82532
extern int sab82532_probe(void);
#endif
#ifdef CONFIG_PCI
extern int ps2kbd_probe(void);
#endif
/* This is called by the sparc32/sparc64 MM init layer right after
* the bootmem allocator has been setup and is ready to use.
*/
void __init sun_serial_setup(void)
{
int ret = 1;
#if defined(CONFIG_PCI) && !defined(CONFIG_SPARC64)
/*
* Probing sequence on sparc differs from sparc64.
* Keyboard is probed ahead of su because we want su function
* when keyboard is active. su is probed ahead of zs in order to
* get console on MrCoffee with fine but disconnected zs.
*/
if (!serial_console)
ps2kbd_probe();
if (sunsu_probe() == 0)
return;
#endif
if (sunzilog_probe() == 0)
return;
#ifdef CONFIG_SAB82532
ret = sab82532_probe();
#endif
#if defined(CONFIG_PCI) && defined(CONFIG_SPARC64)
/*
* Keyboard serial devices.
*
* Well done, Sun, prom_devopen("/pci@1f,4000/ebus@1/su@14,3083f8")
* hangs the machine if no keyboard is connected to the device...
* All PCI PROMs seem to do this, I have seen this on the Ultra 450
* with version 3.5 PROM, and on the Ultra/AX with 3.1.5 PROM.
*
* So be very careful not to probe for keyboards if we are on a
* serial console.
*/
if (!serial_console)
ps2kbd_probe();
if (sunsu_probe() == 0)
return;
#endif
if (!ret)
return;
#ifdef CONFIG_SPARC64
{ extern int this_is_starfire;
/* Hello, Starfire. Pleased to meet you :) */
if (this_is_starfire != 0)
return;
}
#endif
prom_printf("No serial devices found, bailing out.\n");
prom_halt();
}
/* sun.h
*
* Generic SUN serial/kbd/ms layer. Based entirely
* upon drivers/sbus/char/sunserial.h which is:
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*
* Port to new UART layer is:
*
* Copyright (C) 2002 David S. Miller (davem@redhat.com)
*/
#ifndef _SERIAL_SUN_H
#define _SERIAL_SUN_H
#include <linux/config.h>
struct sun_initfunc {
int (*init) (void);
struct sun_initfunc *next;
};
struct sunserial_operations {
struct sun_initfunc *rs_init;
void (*rs_kgdb_hook) (int);
void (*rs_change_mouse_baud) (int);
int (*rs_read_proc) (char *, char **, off_t, int, int *, void *);
};
struct sunkbd_operations {
struct sun_initfunc *kbd_init;
void (*compute_shiftstate) (void);
void (*setledstate) (struct kbd_struct *, unsigned int);
unsigned char (*getledstate) (void);
int (*setkeycode) (unsigned int, unsigned int);
int (*getkeycode) (unsigned int);
};
extern struct sunserial_operations rs_ops;
extern struct sunkbd_operations kbd_ops;
extern void sunserial_setinitfunc(int (*) (void));
extern void sunkbd_setinitfunc(int (*) (void));
extern int serial_console;
extern int stop_a_enabled;
extern void sunserial_console_termios(struct console *);
#ifdef CONFIG_PCI
extern void sunkbd_install_keymaps(ushort **, unsigned int, char *,
char **, int, int, struct kbdiacr *,
unsigned int);
#endif
#endif /* !(_SERIAL_SUN_H) */
/*
* sunzilog.c
*
* Driver for Zilog serial chips found on Sun workstations and
* servers. This driver could actually be made more generic
* and anyone wanting to work on doing that should contact
* me. -DaveM
*
* This is based on the old drivers/sbus/char/zs.c code, a lot
* of code has been simply moved over directly from there but
* much has been rewritten. Credits therefore go out to Eddie
* C. Dost, Peter Zaitcev, Ted Ts'o and Alex Buell for their
* work there.
*
* Copyright (C) 2002 David S. Miller (davem@redhat.com)
*/
#include "sun.h"
#include "sunzilog.h"
/* On 32-bit sparcs we need to delay after register accesses
* to accomodate sun4 systems, but we do not need to flush writes.
* On 64-bit sparc we only need to flush single writes to ensure
* completion.
*/
#ifndef __sparc_v9__
#define ZSDELAY() udelay(5)
#define ZSDELAY_LONG() udelay(20)
#define ZS_WSYNC(channel) do { } while(0)
#else
#define ZSDELAY()
#define ZSDELAY_LONG()
#define ZS_WSYNC(__channel) \
sbus_readb(&((__channel)->control))
#endif
/* Default setting is sun4/sun4c/sun4m, two chips on board. */
static int num_sunzilog = 2;
#define NUM_SUNZILOG num_sunzilog
#define NUM_CHANNELS (NUM_SUNZILOG * 2)
/*
* We wrap our port structure around the generic uart_port.
*/
struct uart_sunzilog_port {
struct uart_port port;
/* IRQ servicing chain. */
struct uart_sunzilog_port *next;
/* Current values of Zilog write registers. */
unsigned char curregs[NUM_ZSREGS];
unsigned int flags;
#define SUNZILOG_FLAG_CONS_KEYB 0x00000001
#define SUNZILOG_FLAG_CONS_MOUSE 0x00000002
#define SUNZILOG_FLAG_IS_CONS 0x00000004
#define SUNZILOG_FLAG_IS_KGDB 0x00000008
#define SUNZILOG_FLAG_MODEM_STATUS 0x00000010
#define SUNZILOG_FLAG_IS_CHANNEL_A 0x00000020
/* L1-A keyboard break state. */
int kbd_id;
int l1_down;
unsigned char parity_mask;
unsigned char prev_status;
};
#define ZILOG_CHANNEL_FROM_PORT(PORT) ((struct zilog_channel *)((PORT)->membase))
#define UART_ZILOG(PORT) ((struct uart_sunzilog_port *)(PORT))
#define SUNZILOG_GET_CURR_REG(PORT, REGNUM) \
(UART_ZILOG(PORT)->curregs[REGNUM])
#define SUNZILOG_SET_CURR_REG(PORT, REGNUM, REGVAL) \
((UART_ZILOG(PORT)->curregs[REGNUM]) = (REGVAL))
static unsigned char sunzilog_initregs_normal[NUM_ZSREGS] = {
RES_EXT_INT, /* R0 */
0, /* R1 */
0, /* R2 */
Rx8, /* R3 */
PAR_EVEN | X16CLK | SB1, /* R4 */
Tx8, /* R5 */
0, /* R6 */
0, /* R7 */
0, /* R8 */
NV | MIE, /* R9 */
NRZ, /* R10 */
RCBR | TCBR, /* R11 */
0, 0, /* R12, R13 Baud Quotient low, high */
BRSRC | BRENAB, /* R14 */
DCDIE | SYNCIE | CTSIE | BRKIE, /* R15 */
};
static unsigned char sunzilog_initregs_console[NUM_ZSREGS] = {
RES_EXT_INT, /* R0 */
EXT_INT_ENAB | INT_ALL_Rx, /* R1 */
0, /* R2 */
Rx8 | RxENAB, /* R3 */
X16CLK, /* R4 */
DTR | Tx8 | TxENAB, /* R5 */
0, /* R6 */
0, /* R7 */
0, /* R8 */
NV | MIE, /* R9 */
NRZ, /* R10 */
RCBR | TCBR, /* R11 */
0, 0, /* R12, R13 Baud Quotient low, high */
BRSRC | BRENAB, /* R14 */
DCDIE | SYNCIE | CTSIE | BRKIE, /* R15 */
};
static unsigned char sunzilog_initregs_kgdb[NUM_ZSREGS] = {
0, 0, 0 /* R0, R1, R2 */
Rx8 | RxENAB, /* R3 */
PAR_EVEN | X16CLK | SB1, /* R4 */
DTR | Tx8 | TxENAB, /* R5 */
0, 0, 0, /* R6, R7, R8 */
NV, /* R9 */
NRZ, /* R10 */
RCBR | TCBR, /* R11 */
0, 0, /* R12, R13 Baud Quotient low, high */
BRSRC | BRENAB, /* R14 */
DCDIE, /* R15 */
};
/* Reading and writing Zilog8530 registers. The delays are to make this
* driver work on the Sun4 which needs a settling delay after each chip
* register access, other machines handle this in hardware via auxiliary
* flip-flops which implement the settle time we do in software.
*
* The port lock must be held and local IRQs must be disabled
* when {read,write}_zsreg is invoked.
*/
static unsigned char read_zsreg(struct zilog_channel *channel,
unsigned char reg)
{
unsigned char retval;
sbus_writeb(reg, &channel->control);
ZSDELAY();
retval = sbus_readb(&channel->control);
ZSDELAY();
return retval;
}
static void write_zsreg(struct zilog_channel *channel,
unsigned char reg, unsigned char value)
{
sbus_writeb(reg, &channel->control);
ZSDELAY();
sbus_writeb(value, &channel->control);
ZSDELAY();
}
static void load_zsregs(struct zilog_channel *channel, unsigned char *regs,
int is_channel_a)
{
int i;
for (i = 0; i < 1000; i++) {
unsigned char stat = read_zsreg(channel, R1);
if (stat & ALL_SNT)
break;
udelay(100);
}
write_zsreg(channel, R3, 0);
ZS_CLEARSTAT(channel);
ZS_CLEARERR(channel);
ZS_CLEARFIFO(channel);
if (is_channel_a)
write_zsreg(channel, R9, CHRA);
else
write_zsreg(channel, R9, CHRB);
ZSDELAY_LONG();
write_zsreg(channel, R4, regs[R4]);
write_zsreg(channel, R3, regs[R3] & ~RxENAB);
write_zsreg(channel, R5, regs[R5] & ~TxENAB);
write_zsreg(channel, R9, regs[R9] & ~MIE);
write_zsreg(channel, R10, regs[R10]);
write_zsreg(channel, R11, regs[R11]);
write_zsreg(channel, R12, regs[R12]);
write_zsreg(channel, R13, regs[R13]);
write_zsreg(channel, R14, regs[R14] & ~BRENAB);
write_zsreg(channel, R14, regs[R14]);
write_zsreg(channel, R14,
(regs[R14] & ~SNRZI) | BRENAB);
write_zsreg(channel, R3, regs[R3]);
write_zsreg(channel, R5, regs[R5]);
write_zsreg(channel, R15, regs[R15]);
write_zsreg(channel, R0, RES_EXT_INT);
write_zsreg(channel, R0, ERR_RES);
write_zsreg(channel, R1, regs[R1]);
write_zsreg(channel, R9, regs[R9]);
}
/* The port->lock must be held and interrupts must be disabled here. */
static __inline__ unsigned char __sunzilog_read_channel_status(struct uart_port *port)
{
struct zilog_channel *channel;
unsigned char status;
channel = ZILOG_CHANNEL_FROM_PORT(port);
status = sbus_read(&channel->control);
ZSDELAY();
return status;
}
/* A convenient way to quickly get R0 status. The caller must _not_ hold the
* port lock, it is acquired here. If you have the lock already invoke the
* double-underscore variant above.
*/
static unsigned char sunzilog_read_channel_status(struct uart_port *port)
{
unsigned long flags;
unsigned char status;
spin_lock_irqsave(&port->lock, flags);
status = __sunzilog_read_channel_status(port);
spin_unlock_irqrestore(&port->lock, flags);
return status;
}
/* A convenient way to set/clear bits in an arbitrary zilog register.
* The caller must the port lock and local interrupts must be disabled.
*/
static void __sunzilog_set_clear_bits(struct uart_port *port, int regnum,
unsigned char set_bits,
unsigned char clear_bits)
{
unsigned char regval;
regval = SUNZILOG_GET_CURR_REG(port, regnum);
regval |= set_bits;
regval &= ~clear_bits;
SUNZILOG_SET_CURR_REG(port, regnum, regval);
write_zsreg(ZILOG_CHANNEL_FROM_PORT(port), regnum, regval);
}
static void sunzilog_receive_chars(struct uart_sunzilog_port *sunzilog_port,
struct sunzilog_channel *channel,
struct pt_regs *regs)
{
struct tty_struct *tty = sunzilog_port->port.info->tty;
while (1) {
unsigned char ch, r1;
if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
tty->flip.tqueue.routine((void *)tty);
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
return;
}
r1 = read_zsreg(channel, R1);
if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) {
sbus_writeb(ERR_RES, &channel->control);
ZSDELAY();
ZS_WSYNC(channel);
}
ch = sbus_readb(&channel->control);
ZSDELAY();
/* This funny hack depends upon BRK_ABRT not interfering
* with the other bits we care about in R1.
*/
if (ch & BRK_ABRT)
r1 |= BRK_ABRT;
ch = sbus_readb(&channel->data);
ZSDELAY();
ch &= sunzilog_port->parity_mask;
if (sunzilog_port->flags & SUNZILOG_FLAG_CONS_KEYB) {
if (ch == SUNKBD_RESET) {
sunzilog_port->kbd_id = 1;
sunzilog_port->l1_down = 0;
} else if (sunzilog_port->kbd_id) {
sunzilog_port->kbd_id = 0;
} else if (ch == SUNKBD_l1) {
sunzilog_port->l1_down = 1;
} else if (ch == (SUNKBD_l1 | SUNKBD_UP)) {
sunzilog_port->l1_down = 0;
} else if (ch == SUNKBD_A && sunzilog_port->l1_down) {
sun_do_break();
sunzilog_port->l1_down = 0;
sunzilog_port->kbd_id = 0;
return;
}
sunkbd_inchar(ch, regs);
goto next_char;
}
if (sunzilog_port->flags & SUNZILOG_FLAG_CONS_MOUSE) {
sun_mouse_inbyte(ch, 0);
goto next_char;
}
if ((sunzilog_port->flags & SUNZILOG_FLAG_IS_CONS) &&
(r1 & BRK_ABRT)) {
sun_do_break();
return;
}
#ifndef CONFIG_SPARC64
/* Look for kgdb 'stop' character. */
if ((sunzilog_port->flags & SUNZILOG_FLAG_IS_KGDB) &&
(ch == '\003')) {
breakpoint();
return;
}
#endif
/* A real serial line, record the character and status. */
*tty->flip.char_buf_ptr = ch;
*tty->flip.flag_buf_ptr = TTY_NORMAL;
sunzilog_port->port.icount.rx++;
if (r1 & (BRK_ABRT | PAR_ERR | Rx_OVR | CRC_ERR)) {
if (r1 & BRK_ABRT) {
r1 &= ~(PAR_ERR | CRC_ERR);
sunzilog_port->port.icount.break++;
if (uart_handle_break(&sunzilog_port->port))
goto next_char;
}
else if (r1 & PAR_ERR)
sunzilog_port->port.icount.parity++;
else if (r1 & CRC_ERR)
sunzilog_port->port.icount.frame++;
if (r1 & Rx_OVR)
sunzilog_port->port.icount.overrun++;
r1 &= sunzilog_port->port.read_status_mask;
if (r1 & BRK_ABRT)
*tty->flip.flag_buf_ptr = TTY_BREAK;
else if (r1 & PAR_ERR)
*tty->flip.flag_buf_ptr = TTY_PARITY;
else if (r1 & CRC_ERR)
*tty->flip.flag_buf_ptr = TTY_FRAME;
}
if (uart_handle_sysrq_char(&sunzilog_port->port, ch, regs))
goto next_char;
if (up->ignore_status_mask == 0xff ||
(r1 & sunzilog_port->port.ignore_status_mask) == 0) {
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
}
if ((r1 & Rx_OVR) &&
tty->flip.count < TTY_FLIPBUF_SIZE) {
*tty->flip.flag_buf_ptr = TTY_OVERRUN;
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
}
next_char:
ch = sbus_readb(&channel->control);
ZSDELAY();
if (!(ch & Rx_CH_AV))
break;
}
tty_flip_buffer_push(tty);
}
static void sunzilog_status_handle(struct uart_sunzilog_port *sunzilog_port,
struct sunzilog_channel *channel)
{
unsigned char status;
status = sbus_readb(&channel->control);
ZSDELAY();
sbus_writeb(RES_EXT_INT, &channel->control);
ZSDELAY();
ZS_WSYNC(channel);
if ((status & BRK_ABRT) &&
(sunzilog_port->flags & SUNZILOG_FLAG_CONS_MOUSE))
sun_mouse_inbyte(0, 1);
if (sunzilog_port->flags & SUNZILOG_FLAG_MODEM_STATUS) {
if (status & SYNC)
sunzilog_port->port.icount.dsr++;
/* The Zilog just gives us an interrupt when DCD/CTS/etc. change.
* But it does not tell us which bit has changed, we have to keep
* track of this ourselves.
*/
if ((status & DCD) ^ sunzilog_port->prev_status)
uart_handle_dcd_change(&sunzilog_port->port,
(status & DCD));
if ((status & CTS) ^ sunzilog_port->prev_status)
uart_handle_cts_change(&sunzilog_port->port,
(status & CTS));
wake_up_interruptible(&sunzilog_port->port.info->delta_msr_wait);
}
sunzilog_port->prev_status = status;
}
#define ZS_PUT_CHAR_MAX_DELAY 2000 /* 10 ms */
static void sunzilog_put_char(struct sunzilog_channel *channel, unsigned char ch)
{
int loops = ZS_PUT_CHAR_MAX_DELAY;
/* This is a timed polling loop so do not switch the explicit
* udelay with ZSDELAY as that is a NOP on some platforms. -DaveM
*/
do {
unsigned char val = sbus_readb(&channel->control);
if (val & Tx_BUF_EMP)
break;
udelay(5);
} while (--loops);
sbus_writeb(ch, &channel->data);
ZSDELAY();
ZS_WSYNC(channel);
}
static void sunzilog_transmit_chars(struct uart_sunzilog_port *sunzilog_port,
struct sunzilog_channel *channel)
{
struct circ_buf *xmit = &sunzilog_port->port.info->xmit;
unsigned char status;
int count;
status = sbus_readb(&channel->control);
ZSDELAY();
/* TX still busy? Just wait for the next TX done interrupt.
* XXX This is a bug bug bug if it happens. It can occur because
* XXX of how we used to do serial consoles on Sparc but we should
* XXX transmit console writes just like we normally would for normal
* XXX UART lines (ie. buffered and TX interrupt driven). -DaveM
*/
if (!(status & Tx_BUF_EMP))
return;
if (sunzilog_port->port.x_char) {
sbus_writeb(sunzilog->port.x_char, &channel->data);
ZSDELAY();
ZS_WSYNC(channel);
sunzilog_port->port.icount.tx++;
sunzilog_port->port.x_char = 0;
return;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(&sunzilog_port->port)) {
sunzilog_stop_tx(&sunzilog_port->port, 0);
return;
}
sbus_writeb(xmit->buf[xmit->tail], &channel->data);
ZSDELAY();
ZS_WSYNC(channel);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
sunzilog_port->port.icount.tx++;
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_event(&sunzilog_port->port, EVT_WRITE_WAKEUP);
if (uart_circ_empty(xmit))
sunzilog_stop_tx(&sunzilog_port->port, 0);
}
static void sunzilog_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct uart_sunzilog_port *sunzilog_port = dev_id;
unsigned long flags;
while (sunzilog_port) {
struct sunzilog_channel *channel
= ZILOG_CHANNEL_FROM_PORT(&sunzilog_port.port);
unsigned char r3;
spin_lock(&sunzilog_port->port.lock);
r3 = read_zsreg(channel, 3);
/* Channel A */
if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
sbus_writeb(RES_H_IUS, &channel->control);
ZSDELAY();
ZS_WSYNC(channel);
if (r3 & CHARxIP)
sunzilog_receive_chars(sunzilog_port, channel, regs);
if (r3 & CHAEXT)
sunzilog_status_handle(sunzilog_port, channel);
if (r3 & CHATxIP)
sunzilog_transmit_chars(sunzilog_port, channel);
}
spin_unlock(&sunzilog_port->port.lock);
/* Channel B */
sunzilog_port = sunzilog_port->next;
channel = ZILOG_CHANNEL_FROM_PORT(&sunzilog_port.port);
spin_lock(&sunzilog_port->port.lock);
if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) {
sbus_writeb(RES_H_IUS, &channel->control);
ZSDELAY();
ZS_WSYNC(channel);
if (r3 & CHBRxIP)
sunzilog_receive_chars(sunzilog_port, channel, regs);
if (r3 & CHBEXT)
sunzilog_status_handle(sunzilog_port, channel);
if (r3 & CHBTxIP)
sunzilog_transmit_chars(sunzilog_port, channel);
}
spin_unlock(&sunzilog_port->port.lock);
sunzilog_port = sunzilog_port->next;
}
spin_lock_irqrestore(&sunzilog_lock, flags);
}
/* The port lock is not held. */
static unsigned int sunzilog_tx_empty(struct uart_port *port)
{
unsigned char status;
unsigned int ret;
status = sunzilog_read_channel_status(port);
if (status & Tx_BUF_EMP)
ret = TIOCSER_TEMPT;
else
ret = 0;
return ret;
}
/* The port lock is not held. */
static unsigned int sunzilog_get_mctrl(struct uart_port *port)
{
unsigned char status;
unsigned int ret;
status = sunzilog_read_channel_status(port);
ret = 0;
if (status & DCD)
ret |= TIOCM_CAR;
if (status & SYNC)
ret |= TIOCM_DSR;
if (status & CTS)
ret |= TIOCM_CTS;
return ret;
}
/* The port lock is held and interrupts are disabled. */
static void sunzilog_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
unsigned char set_bits, clear_bits;
set_bits = clear_bits = 0;
if (mctrl & TIOCM_RTS)
set_bits |= RTS;
else
clear_bits |= RTS;
if (mctrl & TIOCM_DTR)
set_bits |= DTR;
else
clear_bits |= DTR;
__sunzilog_set_clear_bits(port, 5, set_bits, clear_bits);
}
/* The port lock is held and interrupts are disabled. */
static void sunzilog_stop_tx(struct uart_port *port, unsigned int tty_stop)
{
__sunzilog_set_clear_bits(port, 5, 0, TxENAB);
}
/* The port lock is held and interrupts are disabled. */
static void sunzilog_start_tx(struct uart_port *port, unsigned int tty_start)
{
struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port;
struct sunzilog_channel *channel;
unsigned char status;
/* Enable the transmitter. */
__sunzilog_set_clear_bits(port, 5, TxENAB, 0);
channel = ZILOG_CHANNEL_FROM_PORT(port);
status = sbus_readb(&channel->control);
ZSDELAY();
/* TX busy? Just wait for the TX done interrupt. */
if (!(status & Tx_BUF_EMP))
return;
/* Send the first character to jump-start the TX done
* IRQ sending engine.
*/
if (port->x_char) {
sbus_writeb(port->x_char, &channel->data);
ZSDELAY();
ZS_WSYNC(channel);
port->icount.tx++;
port->x_char = 0;
} else {
struct circ_buf *xmit = &port->info->xmit;
sbus_writeb(xmit->but[xmit->tail], &channel->data);
ZSDELAY();
ZS_WSYNC(channel);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_event(&sunzilog_port->port, EVT_WRITE_WAKEUP);
if (uart_circ_empty(xmit))
sunzilog_stop_tx(&sunzilog_port->port, 0);
}
}
/* The port lock is not held. */
static void sunzilog_stop_rx(struct uart_port *port)
{
}
/* The port lock is not held. */
static void sunzilog_enable_ms(struct uart_port *port)
{
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
__sunzilog_set_clear_bits(port, 15,
(DCDIE | SYNCIE | CTSIE), 0);
spin_unlock_irqrestore(&port->lock, flags);
}
/* The port lock is not held. */
static void sunzilog_break_ctl(struct uart_port *port, int break_state)
{
unsigned char set_bits, clear_bits;
unsigned long flags;
set_bits = clear_bits = 0;
if (break_state)
set_bits |= SND_BRK;
else
clear_bits |= SND_BRK;
spin_lock_irqsave(&port->lock, flags);
__sunzilog_set_clear_bits(port, 5, set_bits, clear_bits);
spin_unlock_irqrestore(&port->lock, flags);
}
static int sunzilog_startup(struct uart_port *port)
{
}
static void sunzilog_shutdown(struct uart_port *port)
{
}
/* The port lock is not held. */
static void
sunzilog_change_speed(struct uart_port *port, unsigned int cflag,
unsigned int iflag, unsigned int quot)
{
struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port;
unsigned long flags;
spin_lock_irqsave(&up->port.lock, flags);
/* Program BAUD and clock source. */
up->curregs[4] &= ~XCLK_MASK;
up->curregs[4] |= X16CLK;
up->curregs[11] = TCBR | RCBR;
/* XXX verify this stuff XXX */
up->curregs[12] = quot & 0xff;
up->curregs[13] = (quot >> 8) & 0xff;
up->curregs[14] = BRSRC | BRENAB;
up->curregs[15] |= RTS | DTR;
/* Character size, stop bits, and parity. */
up->curregs[3] &= ~RxN_MASK;
up->curregs[5] &= ~TxN_MASK;
switch (cflag & CSIZE) {
case CS5:
up->curregs[3] |= Rx5;
up->curregs[5] |= Tx5;
up->parity_mask = 0x1f;
break;
case CS6:
up->curregs[3] |= Rx6;
up->curregs[5] |= Tx6;
up->parity_mask = 0x3f;
break;
case CS7:
up->curregs[3] |= Rx7;
up->curregs[5] |= Tx7;
up->parity_mask = 0x7f;
break;
case CS8:
default:
up->curregs[3] |= Rx8;
up->curregs[5] |= Tx8;
up->parity_mask = 0xff;
break;
};
up->curregs[4] &= ~0x0c;
if (cflag & CSTOPB)
up->curregs[4] |= SB2;
else
up->curregs[4] |= SB1;
if (cflag & PARENB)
up->curregs[4] |= PAR_ENAB;
else
up->curregs[4] &= ~PAR_ENAB;
if (!(cflag & PARODD))
up->curregs[4] |= PAR_EVEN;
else
up->curregs[4] &= ~PAR_EVEN;
up->read_status_mask = Rx_OVR;
if (iflag & INPCK)
up->read_status_mask |= CRC_ERR | PAR_ERR;
if (iflag & (BRKINT | PARMRK))
up->read_status_mask |= BRK_ABRT;
up->ignore_status_mask = 0;
if (iflag & IGNPAR)
up->ignore_status_mask |= CRC_ERR | PAR_ERR;
if (iflag & IGNBRK) {
up->ignore_status_mask |= BRK_ABRT;
if (iflag & IGNPAR)
up->ignore_status_mask |= Rx_OVR;
}
if ((cflag & CREAD) == 0)
up->ignore_status_mask = 0xff;
if (UART_ENABLE_MS(&up->port, cflag))
up->flags |= SUNZILOG_FLAG_MODEM_STATUS;
else
up->flags &= ~SUNZILOG_FLAG_MODEM_STATUS;
load_zsregs(up, up->curregs);
spin_unlock_irqrestore(&up->port.lock, flags);
}
static const char *sunzilog_type(struct uart_port *port)
{
return "SunZilog";
}
/* We do not request/release mappings of the registers here, this
* happens at early serial probe time.
*/
static void sunzilog_release_port(struct uart_port *port)
{
}
static int sunzilog_request_port(struct uart_port *port)
{
}
/* These do not need to do anything interesting either. */
static void sunzilog_config_port(struct uart_port *port, int flags)
{
}
static int sunzilog_verify_port(struct uart_port *port, struct serial_struct *ser)
{
return 0;
}
static struct uart_ops sunzilog_pops = {
tx_empty: sunzilog_tx_empty,
set_mctrl: sunzilog_set_mctrl,
get_mctrl: sunzilog_get_mctrl,
stop_tx: sunzilog_stop_tx,
start_tx: sunzilog_start_tx,
stop_rx: sunzilog_stop_rx,
enable_ms: sunzilog_enable_ms,
break_ctl: sunzilog_break_ctl,
startup: sunzilog_startup,
shutdown: sunzilog_shutdown,
change_speed: sunzilog_change_speed,
type: sunzilog_type,
release_port: sunzilog_release_port,
request_port: sunzilog_request_port,
config_port: sunzilog_config_port,
verify_port: sunzilog_verify_port,
};
static struct uart_sunzilog_port sunzilog_ports[UART_NR];
static struct uart_driver sunzilog_reg = {
owner: THIS_MODULE,
driver_name: "ttyS",
#ifdef CONFIG_DEVFS_FS
dev_name: "ttyS%d",
#else
dev_name: "ttyS",
#endif
major: TTY_MAJOR,
minor: 64,
};
static void * __init sunzilog_alloc_bootmem(unsigned long size)
{
void *ret;
ret = __alloc_bootmem(size, SMP_CACHE_BYTES, 0UL);
if (ret != NULL)
memset(ret, 0, size);
return ret;
}
static void __init sunzilog_alloc_tables(void)
{
zs_chips = (struct sun_zslayout **)
zs_alloc_bootmem(NUM_SERIAL * sizeof(struct sun_zslayout *));
if (zs_chips == NULL)
zs_init_alloc_failure("zs_chips");
zs_channels = (struct sun_zschannel **)
zs_alloc_bootmem(NUM_CHANNELS * sizeof(struct sun_zschannel *));
if (zs_channels == NULL)
zs_init_alloc_failure("zs_channels");
zs_nodes = (int *)
zs_alloc_bootmem(NUM_SERIAL * sizeof(int));
if (zs_nodes == NULL)
zs_init_alloc_failure("zs_nodes");
zs_soft = (struct sun_serial *)
zs_alloc_bootmem(NUM_CHANNELS * sizeof(struct sun_serial));
if (zs_soft == NULL)
zs_init_alloc_failure("zs_soft");
zs_ttys = (struct tty_struct *)
zs_alloc_bootmem(NUM_CHANNELS * sizeof(struct tty_struct));
if (zs_ttys == NULL)
zs_init_alloc_failure("zs_ttys");
serial_table = (struct tty_struct **)
zs_alloc_bootmem(NUM_CHANNELS * sizeof(struct tty_struct *));
if (serial_table == NULL)
zs_init_alloc_failure("serial_table");
serial_termios = (struct termios **)
zs_alloc_bootmem(NUM_CHANNELS * sizeof(struct termios *));
if (serial_termios == NULL)
zs_init_alloc_failure("serial_termios");
serial_termios_locked = (struct termios **)
zs_alloc_bootmem(NUM_CHANNELS * sizeof(struct termios *));
if (serial_termios_locked == NULL)
zs_init_alloc_failure("serial_termios_locked");
}
static int __init sunzilog_probe(void)
{
int node;
/* Sun4 Zilog setup is hard coded, no probing to do. */
if (sparc_cpu_model == sun4)
goto no_probe;
NUM_SUNZILOG = 0;
node = prom_getchild(prom_root_node);
if (sparc_cpu_model == sun4d) {
int bbnode;
while (node &&
(node = prom_searchsiblings(node, "cpu-unit"))) {
bbnode = prom_getchild(node);
if (bbnode && prom_searchsiblings(bbnode, "bootbus"))
NUM_SUNZILOG += 2;
node = prom_getsibling(node);
}
goto no_probe;
}
#ifdef CONFIG_SPARC64
else if (sparc_cpu_model == sun4u) {
int central_node;
/* Central bus zilogs must be checked for first,
* since Enterprise boxes might have SBUSes as well.
*/
central_node = prom_finddevice("/central");
if(central_node != 0 && central_node != -1)
node = prom_searchsiblings(prom_getchild(central_node), "fhc");
else
node = prom_searchsiblings(node, "sbus");
if(node != 0 && node != -1)
node = prom_getchild(node);
if(node == 0 || node == -1)
return -ENODEV;
}
#endif /* CONFIG_SPARC64 */
else {
node = prom_searchsiblings(node, "obio");
if(node)
node = prom_getchild(node);
NUM_SERIAL = 2;
goto no_probe;
}
node = prom_searchsiblings(node, "zs");
if (!node)
return -ENODEV;
NUM_SERIAL = 2;
no_probe:
sunzilog_alloc_tables();
}
static int __init sunzilog_init(void)
{
int ret;
printk(KERN_INFO "Serial: Sun Zilog driver.\n");
/* We can only init this once we have probed the Zilogs
* in the system.
*/
sunzilog_reg.nr = NUM_CHANNELS;
/* XXX This is probably where this needs to be setup for
* XXX the same reason.
*/
sunzilog_reg.cons = XXX;
ret = uart_register_driver(&sunzilog_reg);
if (ret == 0) {
int i;
for (i = 0; i < UART_NR; i++) {
/* XXX For each probed Zilog do this... */
uart_add_one_port(&sunzilog_reg,
&sunzilog_ports[i].port);
}
}
return ret;
}
static void __exit sunzilog_exit(void)
{
int i;
for (i = 0; i < UART_NR; i++) {
/* XXX For each probed Zilog do this... */
uart_remove_one_port(&sunzilog_reg,
&sunzilog_ports[i].port);
}
uart_unregister_driver(&sunzilog_reg);
}
module_init(sunzilog_init);
module_exit(sunzilog_exit);
EXPORT_NO_SYMBOLS;
MODULE_AUTHOR("David S. Miller");
MODULE_DESCRIPTION("Sun Zilog serial port driver");
MODULE_LICENSE("GPL");
#ifndef _SUNZILOG_H
#define _SUNZILOG_H
struct zilog_channel {
volatile unsigned char control;
volatile unsigned char __pad1;
volatile unsigned char data;
volatile unsigned char __pad2;
};
struct zilog_layout {
struct zilog_channel channelB;
struct zilog_channel channelA;
};
#define NUM_ZSREGS 16
/* Conversion routines to/from brg time constants from/to bits
* per second.
*/
#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
/* The Zilog register set */
#define FLAG 0x7e
/* Write Register 0 */
#define R0 0 /* Register selects */
#define R1 1
#define R2 2
#define R3 3
#define R4 4
#define R5 5
#define R6 6
#define R7 7
#define R8 8
#define R9 9
#define R10 10
#define R11 11
#define R12 12
#define R13 13
#define R14 14
#define R15 15
#define NULLCODE 0 /* Null Code */
#define POINT_HIGH 0x8 /* Select upper half of registers */
#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */
#define SEND_ABORT 0x18 /* HDLC Abort */
#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */
#define RES_Tx_P 0x28 /* Reset TxINT Pending */
#define ERR_RES 0x30 /* Error Reset */
#define RES_H_IUS 0x38 /* Reset highest IUS */
#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */
#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */
#define RES_EOM_L 0xC0 /* Reset EOM latch */
/* Write Register 1 */
#define EXT_INT_ENAB 0x1 /* Ext Int Enable */
#define TxINT_ENAB 0x2 /* Tx Int Enable */
#define PAR_SPEC 0x4 /* Parity is special condition */
#define RxINT_DISAB 0 /* Rx Int Disable */
#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */
#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */
#define INT_ERR_Rx 0x18 /* Int on error only */
#define RxINT_MASK 0x18
#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */
#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */
#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */
/* Write Register #2 (Interrupt Vector) */
/* Write Register 3 */
#define RxENAB 0x1 /* Rx Enable */
#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */
#define ADD_SM 0x4 /* Address Search Mode (SDLC) */
#define RxCRC_ENAB 0x8 /* Rx CRC Enable */
#define ENT_HM 0x10 /* Enter Hunt Mode */
#define AUTO_ENAB 0x20 /* Auto Enables */
#define Rx5 0x0 /* Rx 5 Bits/Character */
#define Rx7 0x40 /* Rx 7 Bits/Character */
#define Rx6 0x80 /* Rx 6 Bits/Character */
#define Rx8 0xc0 /* Rx 8 Bits/Character */
#define RxN_MASK 0xc0
/* Write Register 4 */
#define PAR_ENAB 0x1 /* Parity Enable */
#define PAR_EVEN 0x2 /* Parity Even/Odd* */
#define SYNC_ENAB 0 /* Sync Modes Enable */
#define SB1 0x4 /* 1 stop bit/char */
#define SB15 0x8 /* 1.5 stop bits/char */
#define SB2 0xc /* 2 stop bits/char */
#define MONSYNC 0 /* 8 Bit Sync character */
#define BISYNC 0x10 /* 16 bit sync character */
#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */
#define EXTSYNC 0x30 /* External Sync Mode */
#define X1CLK 0x0 /* x1 clock mode */
#define X16CLK 0x40 /* x16 clock mode */
#define X32CLK 0x80 /* x32 clock mode */
#define X64CLK 0xC0 /* x64 clock mode */
#define XCLK_MASK 0xC0
/* Write Register 5 */
#define TxCRC_ENAB 0x1 /* Tx CRC Enable */
#define RTS 0x2 /* RTS */
#define SDLC_CRC 0x4 /* SDLC/CRC-16 */
#define TxENAB 0x8 /* Tx Enable */
#define SND_BRK 0x10 /* Send Break */
#define Tx5 0x0 /* Tx 5 bits (or less)/character */
#define Tx7 0x20 /* Tx 7 bits/character */
#define Tx6 0x40 /* Tx 6 bits/character */
#define Tx8 0x60 /* Tx 8 bits/character */
#define TxN_MASK 0x60
#define DTR 0x80 /* DTR */
/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
/* Write Register 8 (transmit buffer) */
/* Write Register 9 (Master interrupt control) */
#define VIS 1 /* Vector Includes Status */
#define NV 2 /* No Vector */
#define DLC 4 /* Disable Lower Chain */
#define MIE 8 /* Master Interrupt Enable */
#define STATHI 0x10 /* Status high */
#define NORESET 0 /* No reset on write to R9 */
#define CHRB 0x40 /* Reset channel B */
#define CHRA 0x80 /* Reset channel A */
#define FHWRES 0xc0 /* Force hardware reset */
/* Write Register 10 (misc control bits) */
#define BIT6 1 /* 6 bit/8bit sync */
#define LOOPMODE 2 /* SDLC Loop mode */
#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */
#define MARKIDLE 8 /* Mark/flag on idle */
#define GAOP 0x10 /* Go active on poll */
#define NRZ 0 /* NRZ mode */
#define NRZI 0x20 /* NRZI mode */
#define FM1 0x40 /* FM1 (transition = 1) */
#define FM0 0x60 /* FM0 (transition = 0) */
#define CRCPS 0x80 /* CRC Preset I/O */
/* Write Register 11 (Clock Mode control) */
#define TRxCXT 0 /* TRxC = Xtal output */
#define TRxCTC 1 /* TRxC = Transmit clock */
#define TRxCBR 2 /* TRxC = BR Generator Output */
#define TRxCDP 3 /* TRxC = DPLL output */
#define TRxCOI 4 /* TRxC O/I */
#define TCRTxCP 0 /* Transmit clock = RTxC pin */
#define TCTRxCP 8 /* Transmit clock = TRxC pin */
#define TCBR 0x10 /* Transmit clock = BR Generator output */
#define TCDPLL 0x18 /* Transmit clock = DPLL output */
#define RCRTxCP 0 /* Receive clock = RTxC pin */
#define RCTRxCP 0x20 /* Receive clock = TRxC pin */
#define RCBR 0x40 /* Receive clock = BR Generator output */
#define RCDPLL 0x60 /* Receive clock = DPLL output */
#define RTxCX 0x80 /* RTxC Xtal/No Xtal */
/* Write Register 12 (lower byte of baud rate generator time constant) */
/* Write Register 13 (upper byte of baud rate generator time constant) */
/* Write Register 14 (Misc control bits) */
#define BRENAB 1 /* Baud rate generator enable */
#define BRSRC 2 /* Baud rate generator source */
#define DTRREQ 4 /* DTR/Request function */
#define AUTOECHO 8 /* Auto Echo */
#define LOOPBAK 0x10 /* Local loopback */
#define SEARCH 0x20 /* Enter search mode */
#define RMC 0x40 /* Reset missing clock */
#define DISDPLL 0x60 /* Disable DPLL */
#define SSBR 0x80 /* Set DPLL source = BR generator */
#define SSRTxC 0xa0 /* Set DPLL source = RTxC */
#define SFMM 0xc0 /* Set FM mode */
#define SNRZI 0xe0 /* Set NRZI mode */
/* Write Register 15 (external/status interrupt control) */
#define ZCIE 2 /* Zero count IE */
#define DCDIE 8 /* DCD IE */
#define SYNCIE 0x10 /* Sync/hunt IE */
#define CTSIE 0x20 /* CTS IE */
#define TxUIE 0x40 /* Tx Underrun/EOM IE */
#define BRKIE 0x80 /* Break/Abort IE */
/* Read Register 0 */
#define Rx_CH_AV 0x1 /* Rx Character Available */
#define ZCOUNT 0x2 /* Zero count */
#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */
#define DCD 0x8 /* DCD */
#define SYNC 0x10 /* Sync/hunt */
#define CTS 0x20 /* CTS */
#define TxEOM 0x40 /* Tx underrun */
#define BRK_ABRT 0x80 /* Break/Abort */
/* Read Register 1 */
#define ALL_SNT 0x1 /* All sent */
/* Residue Data for 8 Rx bits/char programmed */
#define RES3 0x8 /* 0/3 */
#define RES4 0x4 /* 0/4 */
#define RES5 0xc /* 0/5 */
#define RES6 0x2 /* 0/6 */
#define RES7 0xa /* 0/7 */
#define RES8 0x6 /* 0/8 */
#define RES18 0xe /* 1/8 */
#define RES28 0x0 /* 2/8 */
/* Special Rx Condition Interrupts */
#define PAR_ERR 0x10 /* Parity error */
#define Rx_OVR 0x20 /* Rx Overrun Error */
#define CRC_ERR 0x40 /* CRC/Framing Error */
#define END_FR 0x80 /* End of Frame (SDLC) */
/* Read Register 2 (channel b only) - Interrupt vector */
#define CHB_Tx_EMPTY 0x00
#define CHB_EXT_STAT 0x02
#define CHB_Rx_AVAIL 0x04
#define CHB_SPECIAL 0x06
#define CHA_Tx_EMPTY 0x08
#define CHA_EXT_STAT 0x0a
#define CHA_Rx_AVAIL 0x0c
#define CHA_SPECIAL 0x0e
#define STATUS_MASK 0x0e
/* Read Register 3 (interrupt pending register) ch a only */
#define CHBEXT 0x1 /* Channel B Ext/Stat IP */
#define CHBTxIP 0x2 /* Channel B Tx IP */
#define CHBRxIP 0x4 /* Channel B Rx IP */
#define CHAEXT 0x8 /* Channel A Ext/Stat IP */
#define CHATxIP 0x10 /* Channel A Tx IP */
#define CHARxIP 0x20 /* Channel A Rx IP */
/* Read Register 8 (receive data register) */
/* Read Register 10 (misc status bits) */
#define ONLOOP 2 /* On loop */
#define LOOPSEND 0x10 /* Loop sending */
#define CLK2MIS 0x40 /* Two clocks missing */
#define CLK1MIS 0x80 /* One clock missing */
/* Read Register 12 (lower byte of baud rate generator constant) */
/* Read Register 13 (upper byte of baud rate generator constant) */
/* Read Register 15 (value of WR 15) */
/* Misc macros */
#define ZS_CLEARERR(channel) do { sbus_writeb(ERR_RES, &channel->control); \
udelay(5); } while(0)
#define ZS_CLEARSTAT(channel) do { sbus_writeb(RES_EXT_INT, &channel->control); \
udelay(5); } while(0)
#define ZS_CLEARFIFO(channel) do { sbus_readb(&channel->data); \
udelay(2); \
sbus_readb(&channel->data); \
udelay(2); \
sbus_readb(&channel->data); \
udelay(2); } while(0)
#endif /* _SUNZILOG_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment