Commit baee354e authored by Pete Zaitcev's avatar Pete Zaitcev Committed by David S. Miller

[SUNZILOG]: Get serial console et al. working once more.

- Make sure R9 is really loaded
- Make sure IS_CONS flag is handled properly.
parent 1716b785
...@@ -112,10 +112,7 @@ struct uart_sunzilog_port { ...@@ -112,10 +112,7 @@ struct uart_sunzilog_port {
#define ZILOG_CHANNEL_FROM_PORT(PORT) ((struct zilog_channel *)((PORT)->membase)) #define ZILOG_CHANNEL_FROM_PORT(PORT) ((struct zilog_channel *)((PORT)->membase))
#define UART_ZILOG(PORT) ((struct uart_sunzilog_port *)(PORT)) #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))
#define ZS_IS_KEYB(UP) ((UP)->flags & SUNZILOG_FLAG_CONS_KEYB) #define ZS_IS_KEYB(UP) ((UP)->flags & SUNZILOG_FLAG_CONS_KEYB)
#define ZS_IS_MOUSE(UP) ((UP)->flags & SUNZILOG_FLAG_CONS_MOUSE) #define ZS_IS_MOUSE(UP) ((UP)->flags & SUNZILOG_FLAG_CONS_MOUSE)
#define ZS_IS_CONS(UP) ((UP)->flags & SUNZILOG_FLAG_IS_CONS) #define ZS_IS_CONS(UP) ((UP)->flags & SUNZILOG_FLAG_IS_CONS)
...@@ -278,8 +275,7 @@ static void sunzilog_change_mouse_baud(struct uart_sunzilog_port *up) ...@@ -278,8 +275,7 @@ static void sunzilog_change_mouse_baud(struct uart_sunzilog_port *up)
up->cflag &= ~CBAUD; up->cflag &= ~CBAUD;
up->cflag |= suncore_mouse_baud_cflag_next(cur_cflag, &new_baud); up->cflag |= suncore_mouse_baud_cflag_next(cur_cflag, &new_baud);
brg = BPS_TO_BRG(new_baud, brg = BPS_TO_BRG(new_baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
(ZS_CLOCK / ZS_CLOCK_DIVISOR));
up->curregs[R12] = (brg & 0xff); up->curregs[R12] = (brg & 0xff);
up->curregs[R13] = (brg >> 8) & 0xff; up->curregs[R13] = (brg >> 8) & 0xff;
sunzilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(&up->port)); sunzilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(&up->port));
...@@ -332,7 +328,7 @@ static void sunzilog_receive_chars(struct uart_sunzilog_port *up, ...@@ -332,7 +328,7 @@ static void sunzilog_receive_chars(struct uart_sunzilog_port *up,
struct zilog_channel *channel, struct zilog_channel *channel,
struct pt_regs *regs) struct pt_regs *regs)
{ {
struct tty_struct *tty = up->port.info->tty; struct tty_struct *tty = up->port.info->tty; /* XXX info==NULL? */
while (1) { while (1) {
unsigned char ch, r1; unsigned char ch, r1;
...@@ -340,7 +336,7 @@ static void sunzilog_receive_chars(struct uart_sunzilog_port *up, ...@@ -340,7 +336,7 @@ static void sunzilog_receive_chars(struct uart_sunzilog_port *up,
if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) { if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
tty->flip.work.func((void *)tty); tty->flip.work.func((void *)tty);
if (tty->flip.count >= TTY_FLIPBUF_SIZE) if (tty->flip.count >= TTY_FLIPBUF_SIZE)
return; return; /* XXX Ignores SysRq when we need it most. Fix. */
} }
r1 = read_zsreg(channel, R1); r1 = read_zsreg(channel, R1);
...@@ -474,7 +470,7 @@ static void sunzilog_status_handle(struct uart_sunzilog_port *up, ...@@ -474,7 +470,7 @@ static void sunzilog_status_handle(struct uart_sunzilog_port *up,
static void sunzilog_transmit_chars(struct uart_sunzilog_port *up, static void sunzilog_transmit_chars(struct uart_sunzilog_port *up,
struct zilog_channel *channel) struct zilog_channel *channel)
{ {
struct circ_buf *xmit = &up->port.info->xmit; struct circ_buf *xmit;
if (ZS_IS_CONS(up)) { if (ZS_IS_CONS(up)) {
unsigned char status = sbus_readb(&channel->control); unsigned char status = sbus_readb(&channel->control);
...@@ -499,7 +495,12 @@ static void sunzilog_transmit_chars(struct uart_sunzilog_port *up, ...@@ -499,7 +495,12 @@ static void sunzilog_transmit_chars(struct uart_sunzilog_port *up,
if (ZS_TX_STOPPED(up)) { if (ZS_TX_STOPPED(up)) {
up->flags &= ~SUNZILOG_FLAG_TX_STOPPED; up->flags &= ~SUNZILOG_FLAG_TX_STOPPED;
goto disable_tx_int;
sbus_writeb(RES_Tx_P, &channel->control);
ZSDELAY();
ZS_WSYNC(channel);
return;
} }
if (up->port.x_char) { if (up->port.x_char) {
...@@ -512,8 +513,11 @@ static void sunzilog_transmit_chars(struct uart_sunzilog_port *up, ...@@ -512,8 +513,11 @@ static void sunzilog_transmit_chars(struct uart_sunzilog_port *up,
return; return;
} }
if (up->port.info == NULL)
goto ack_tx_int;
xmit = &up->port.info->xmit;
if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port))
goto disable_tx_int; goto ack_tx_int;
sbus_writeb(xmit->buf[xmit->tail], &channel->data); sbus_writeb(xmit->buf[xmit->tail], &channel->data);
ZSDELAY(); ZSDELAY();
...@@ -525,12 +529,15 @@ static void sunzilog_transmit_chars(struct uart_sunzilog_port *up, ...@@ -525,12 +529,15 @@ static void sunzilog_transmit_chars(struct uart_sunzilog_port *up,
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&up->port); uart_write_wakeup(&up->port);
if (!uart_circ_empty(xmit)) if (uart_circ_empty(xmit))
return; goto ack_tx_int;
disable_tx_int: return;
up->curregs[R5] &= ~TxENAB;
write_zsreg(ZILOG_CHANNEL_FROM_PORT(&up->port), R5, up->curregs[R5]); ack_tx_int:
sbus_writeb(RES_Tx_P, &channel->control);
ZSDELAY();
ZS_WSYNC(channel);
} }
static void sunzilog_interrupt(int irq, void *dev_id, struct pt_regs *regs) static void sunzilog_interrupt(int irq, void *dev_id, struct pt_regs *regs)
...@@ -543,7 +550,7 @@ static void sunzilog_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -543,7 +550,7 @@ static void sunzilog_interrupt(int irq, void *dev_id, struct pt_regs *regs)
unsigned char r3; unsigned char r3;
spin_lock(&up->port.lock); spin_lock(&up->port.lock);
r3 = read_zsreg(channel, 3); r3 = read_zsreg(channel, R3);
/* Channel A */ /* Channel A */
if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
...@@ -679,13 +686,6 @@ static void sunzilog_start_tx(struct uart_port *port, unsigned int tty_start) ...@@ -679,13 +686,6 @@ static void sunzilog_start_tx(struct uart_port *port, unsigned int tty_start)
up->flags |= SUNZILOG_FLAG_TX_ACTIVE; up->flags |= SUNZILOG_FLAG_TX_ACTIVE;
up->flags &= ~SUNZILOG_FLAG_TX_STOPPED; up->flags &= ~SUNZILOG_FLAG_TX_STOPPED;
/* Enable the transmitter. */
if (!(up->curregs[R5] & TxENAB)) {
/* NOTE: Not subject to 'transmitter active' rule. */
up->curregs[R5] |= TxENAB;
write_zsreg(channel, R5, up->curregs[R5]);
}
status = sbus_readb(&channel->control); status = sbus_readb(&channel->control);
ZSDELAY(); ZSDELAY();
...@@ -785,39 +785,69 @@ static void sunzilog_break_ctl(struct uart_port *port, int break_state) ...@@ -785,39 +785,69 @@ static void sunzilog_break_ctl(struct uart_port *port, int break_state)
spin_unlock_irqrestore(&port->lock, flags); spin_unlock_irqrestore(&port->lock, flags);
} }
static int sunzilog_startup(struct uart_port *port) static void __sunzilog_startup(struct uart_sunzilog_port *up)
{ {
struct uart_sunzilog_port *up = UART_ZILOG(port);
struct zilog_channel *channel; struct zilog_channel *channel;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags); channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
channel = ZILOG_CHANNEL_FROM_PORT(port);
up->prev_status = sbus_readb(&channel->control); up->prev_status = sbus_readb(&channel->control);
/* Enable receiver and transmitter. */ /* Enable receiver and transmitter. */
up->curregs[R3] |= RxENAB; up->curregs[R3] |= RxENAB;
up->curregs[R5] |= TxENAB; up->curregs[R5] |= TxENAB;
/* Enable RX and status interrupts. TX interrupts are enabled up->curregs[R1] |= EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB;
* as needed.
*/
up->curregs[R1] |= EXT_INT_ENAB | INT_ALL_Rx;
up->curregs[R9] |= MIE;
sunzilog_maybe_update_regs(up, channel); sunzilog_maybe_update_regs(up, channel);
}
spin_unlock_irqrestore(&port->lock, flags); static int sunzilog_startup(struct uart_port *port)
{
struct uart_sunzilog_port *up = UART_ZILOG(port);
unsigned long flags;
if (ZS_IS_CONS(up))
return 0;
spin_lock_irqsave(&port->lock, flags);
__sunzilog_startup(up);
spin_unlock_irqrestore(&port->lock, flags);
return 0; return 0;
} }
/*
* The test for ZS_IS_CONS is explained by the following e-mail:
*****
* From: Russell King <rmk@arm.linux.org.uk>
* Date: Sun, 8 Dec 2002 10:18:38 +0000
*
* On Sun, Dec 08, 2002 at 02:43:36AM -0500, Pete Zaitcev wrote:
* > I boot my 2.5 boxes using "console=ttyS0,9600" argument,
* > and I noticed that something is not right with reference
* > counting in this case. It seems that when the console
* > is open by kernel initially, this is not accounted
* > as an open, and uart_startup is not called.
*
* That is correct. We are unable to call uart_startup when the serial
* console is initialised because it may need to allocate memory (as
* request_irq does) and the memory allocators may not have been
* initialised.
*
* 1. initialise the port into a state where it can send characters in the
* console write method.
*
* 2. don't do the actual hardware shutdown in your shutdown() method (but
* do the normal software shutdown - ie, free irqs etc)
*****
*/
static void sunzilog_shutdown(struct uart_port *port) static void sunzilog_shutdown(struct uart_port *port)
{ {
struct uart_sunzilog_port *up = UART_ZILOG(port); struct uart_sunzilog_port *up = UART_ZILOG(port);
struct zilog_channel *channel; struct zilog_channel *channel;
unsigned long flags; unsigned long flags;
if (ZS_IS_CONS(up))
return;
spin_lock_irqsave(&port->lock, flags); spin_lock_irqsave(&port->lock, flags);
channel = ZILOG_CHANNEL_FROM_PORT(port); channel = ZILOG_CHANNEL_FROM_PORT(port);
...@@ -829,7 +859,6 @@ static void sunzilog_shutdown(struct uart_port *port) ...@@ -829,7 +859,6 @@ static void sunzilog_shutdown(struct uart_port *port)
/* Disable all interrupts and BRK assertion. */ /* Disable all interrupts and BRK assertion. */
up->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); up->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK);
up->curregs[R5] &= ~SND_BRK; up->curregs[R5] &= ~SND_BRK;
up->curregs[R9] &= ~MIE;
sunzilog_maybe_update_regs(up, channel); sunzilog_maybe_update_regs(up, channel);
spin_unlock_irqrestore(&port->lock, flags); spin_unlock_irqrestore(&port->lock, flags);
...@@ -843,18 +872,15 @@ sunzilog_convert_to_zs(struct uart_sunzilog_port *up, unsigned int cflag, ...@@ -843,18 +872,15 @@ sunzilog_convert_to_zs(struct uart_sunzilog_port *up, unsigned int cflag,
unsigned int iflag, int brg) unsigned int iflag, int brg)
{ {
/* Don't modify MIE. */
up->curregs[R9] |= NV;
up->curregs[R10] = NRZ; up->curregs[R10] = NRZ;
up->curregs[11] = TCBR | RCBR; up->curregs[R11] = TCBR | RCBR;
/* Program BAUD and clock source. */ /* Program BAUD and clock source. */
up->curregs[4] &= ~XCLK_MASK; up->curregs[R4] &= ~XCLK_MASK;
up->curregs[4] |= X16CLK; up->curregs[R4] |= X16CLK;
up->curregs[12] = brg & 0xff; up->curregs[R12] = brg & 0xff;
up->curregs[13] = (brg >> 8) & 0xff; up->curregs[R13] = (brg >> 8) & 0xff;
up->curregs[14] = BRSRC | BRENAB; up->curregs[R14] = BRSRC | BRENAB;
/* Character size, stop bits, and parity. */ /* Character size, stop bits, and parity. */
up->curregs[3] &= ~RxN_MASK; up->curregs[3] &= ~RxN_MASK;
...@@ -1027,7 +1053,7 @@ static void __init sunzilog_alloc_tables(void) ...@@ -1027,7 +1053,7 @@ static void __init sunzilog_alloc_tables(void)
alloc_one_table(NUM_SUNZILOG * sizeof(struct zilog_layout *)); alloc_one_table(NUM_SUNZILOG * sizeof(struct zilog_layout *));
if (sunzilog_port_table == NULL || sunzilog_chip_regs == NULL) { if (sunzilog_port_table == NULL || sunzilog_chip_regs == NULL) {
prom_printf("sunzilog_init: Cannot alloc SunZilog tables.\n"); prom_printf("SunZilog: Cannot allocate tables.\n");
prom_halt(); prom_halt();
} }
} }
...@@ -1333,7 +1359,7 @@ static int __init sunzilog_console_setup(struct console *con, char *options) ...@@ -1333,7 +1359,7 @@ static int __init sunzilog_console_setup(struct console *con, char *options)
unsigned long flags; unsigned long flags;
int baud, brg; int baud, brg;
printk("Console: ttyS%d (Zilog8530)\n", printk("Console: ttyS%d (SunZilog)\n",
(sunzilog_reg.minor - 64) + con->index); (sunzilog_reg.minor - 64) + con->index);
/* Get firmware console settings. */ /* Get firmware console settings. */
...@@ -1356,21 +1382,15 @@ static int __init sunzilog_console_setup(struct console *con, char *options) ...@@ -1356,21 +1382,15 @@ static int __init sunzilog_console_setup(struct console *con, char *options)
brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
/*
* Temporary fix.
*/
spin_lock_init(&up->port.lock);
spin_lock_irqsave(&up->port.lock, flags); spin_lock_irqsave(&up->port.lock, flags);
up->curregs[R15] = BRKIE; up->curregs[R15] = BRKIE;
sunzilog_convert_to_zs(up, con->cflag, 0, brg); sunzilog_convert_to_zs(up, con->cflag, 0, brg);
spin_unlock_irqrestore(&up->port.lock, flags);
sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS);
sunzilog_startup(&up->port); __sunzilog_startup(up);
spin_unlock_irqrestore(&up->port.lock, flags);
return 0; return 0;
} }
...@@ -1401,6 +1421,7 @@ static int __init sunzilog_console_init(void) ...@@ -1401,6 +1421,7 @@ static int __init sunzilog_console_init(void)
return 0; return 0;
sunzilog_console.index = i; sunzilog_console.index = i;
sunzilog_port_table[i].flags |= SUNZILOG_FLAG_IS_CONS;
register_console(&sunzilog_console); register_console(&sunzilog_console);
return 0; return 0;
} }
...@@ -1457,6 +1478,12 @@ static void __init sunzilog_prepare(void) ...@@ -1457,6 +1478,12 @@ static void __init sunzilog_prepare(void)
struct zilog_layout *rp; struct zilog_layout *rp;
int channel, chip; int channel, chip;
/*
* Temporary fix.
*/
for (channel = 0; channel < NUM_CHANNELS - 1; channel++)
spin_lock_init(sunzilog_port_table[channel].port.lock);
sunzilog_irq_chain = up = &sunzilog_port_table[0]; sunzilog_irq_chain = up = &sunzilog_port_table[0];
for (channel = 0; channel < NUM_CHANNELS - 1; channel++) for (channel = 0; channel < NUM_CHANNELS - 1; channel++)
up[channel].next = &up[channel + 1]; up[channel].next = &up[channel + 1];
...@@ -1504,7 +1531,7 @@ static void __init sunzilog_init_kbdms(struct uart_sunzilog_port *up, int channe ...@@ -1504,7 +1531,7 @@ static void __init sunzilog_init_kbdms(struct uart_sunzilog_port *up, int channe
up->cflag = B4800 | CS8 | CLOCAL | CREAD; up->cflag = B4800 | CS8 | CLOCAL | CREAD;
baud = 4800; baud = 4800;
} }
printk(KERN_INFO "zs%d at 0x%p (irq = %s) is a Zilog8530\n", printk(KERN_INFO "zs%d at 0x%p (irq = %s) is a SunZilog\n",
channel, up->port.membase, __irq_itoa(zilog_irq)); channel, up->port.membase, __irq_itoa(zilog_irq));
up->curregs[R15] = BRKIE; up->curregs[R15] = BRKIE;
...@@ -1534,10 +1561,8 @@ static void __init sunzilog_init_kbdms(struct uart_sunzilog_port *up, int channe ...@@ -1534,10 +1561,8 @@ static void __init sunzilog_init_kbdms(struct uart_sunzilog_port *up, int channe
serio_register_port(&up->serio); serio_register_port(&up->serio);
#endif #endif
spin_unlock(&up->port.lock);
sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS);
sunzilog_startup(&up->port); __sunzilog_startup(up);
spin_lock(&up->port.lock);
} }
static void __init sunzilog_init_hw(void) static void __init sunzilog_init_hw(void)
...@@ -1560,8 +1585,6 @@ static void __init sunzilog_init_hw(void) ...@@ -1560,8 +1585,6 @@ static void __init sunzilog_init_hw(void)
if (i == KEYBOARD_LINE || i == MOUSE_LINE) { if (i == KEYBOARD_LINE || i == MOUSE_LINE) {
sunzilog_init_kbdms(up, i); sunzilog_init_kbdms(up, i);
} else if (ZS_IS_CONS(up)) {
/* sunzilog_console_setup takes care of this */
} else { } else {
/* Normal serial TTY. */ /* Normal serial TTY. */
up->parity_mask = 0xff; up->parity_mask = 0xff;
...@@ -1572,11 +1595,12 @@ static void __init sunzilog_init_hw(void) ...@@ -1572,11 +1595,12 @@ static void __init sunzilog_init_hw(void)
up->curregs[R10] = NRZ; up->curregs[R10] = NRZ;
up->curregs[R11] = TCBR | RCBR; up->curregs[R11] = TCBR | RCBR;
baud = 9600; baud = 9600;
brg = BPS_TO_BRG(baud, (ZS_CLOCK / ZS_CLOCK_DIVISOR)); brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
up->curregs[R12] = (brg & 0xff); up->curregs[R12] = (brg & 0xff);
up->curregs[R13] = (brg >> 8) & 0xff; up->curregs[R13] = (brg >> 8) & 0xff;
up->curregs[R14] = BRSRC | BRENAB; up->curregs[R14] = BRSRC | BRENAB;
sunzilog_maybe_update_regs(up, channel); __load_zsregs(channel, up->curregs);
write_zsreg(channel, R9, up->curregs[R9]);
} }
spin_unlock_irqrestore(&up->port.lock, flags); spin_unlock_irqrestore(&up->port.lock, flags);
......
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