|
@@ -408,6 +408,34 @@ static struct irq_chip mn10300_serial_pic = {
|
|
|
.irq_unmask = mn10300_serial_nop,
|
|
|
};
|
|
|
|
|
|
+static void mn10300_serial_low_mask(struct irq_data *d)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+ u16 tmp;
|
|
|
+
|
|
|
+ flags = arch_local_cli_save();
|
|
|
+ GxICR(d->irq) = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL);
|
|
|
+ tmp = GxICR(d->irq); /* flush write buffer */
|
|
|
+ arch_local_irq_restore(flags);
|
|
|
+}
|
|
|
+
|
|
|
+static void mn10300_serial_low_unmask(struct irq_data *d)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+ u16 tmp;
|
|
|
+
|
|
|
+ flags = arch_local_cli_save();
|
|
|
+ GxICR(d->irq) =
|
|
|
+ NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL) | GxICR_ENABLE;
|
|
|
+ tmp = GxICR(d->irq); /* flush write buffer */
|
|
|
+ arch_local_irq_restore(flags);
|
|
|
+}
|
|
|
+
|
|
|
+static struct irq_chip mn10300_serial_low_pic = {
|
|
|
+ .name = "mnserial-low",
|
|
|
+ .irq_mask = mn10300_serial_low_mask,
|
|
|
+ .irq_unmask = mn10300_serial_low_unmask,
|
|
|
+};
|
|
|
|
|
|
/*
|
|
|
* serial virtual DMA interrupt jump table
|
|
@@ -416,25 +444,53 @@ struct mn10300_serial_int mn10300_serial_int_tbl[NR_IRQS];
|
|
|
|
|
|
static void mn10300_serial_dis_tx_intr(struct mn10300_serial_port *port)
|
|
|
{
|
|
|
- unsigned long flags;
|
|
|
+ int retries = 100;
|
|
|
u16 x;
|
|
|
|
|
|
- flags = arch_local_cli_save();
|
|
|
- *port->tx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL);
|
|
|
- x = *port->tx_icr;
|
|
|
- arch_local_irq_restore(flags);
|
|
|
+ /* nothing to do if irq isn't set up */
|
|
|
+ if (!mn10300_serial_int_tbl[port->tx_irq].port)
|
|
|
+ return;
|
|
|
+
|
|
|
+ port->tx_flags |= MNSCx_TX_STOP;
|
|
|
+ mb();
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Here we wait for the irq to be disabled. Either it already is
|
|
|
+ * disabled or we wait some number of retries for the VDMA handler
|
|
|
+ * to disable it. The retries give the VDMA handler enough time to
|
|
|
+ * run to completion if it was already in progress. If the VDMA IRQ
|
|
|
+ * is enabled but the handler is not yet running when arrive here,
|
|
|
+ * the STOP flag will prevent the handler from conflicting with the
|
|
|
+ * driver code following this loop.
|
|
|
+ */
|
|
|
+ while ((*port->tx_icr & GxICR_ENABLE) && retries-- > 0)
|
|
|
+ ;
|
|
|
+ if (retries <= 0) {
|
|
|
+ *port->tx_icr =
|
|
|
+ NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL);
|
|
|
+ x = *port->tx_icr;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static void mn10300_serial_en_tx_intr(struct mn10300_serial_port *port)
|
|
|
{
|
|
|
- unsigned long flags;
|
|
|
u16 x;
|
|
|
|
|
|
- flags = arch_local_cli_save();
|
|
|
+ /* nothing to do if irq isn't set up */
|
|
|
+ if (!mn10300_serial_int_tbl[port->tx_irq].port)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* stop vdma irq if not already stopped */
|
|
|
+ if (!(port->tx_flags & MNSCx_TX_STOP))
|
|
|
+ mn10300_serial_dis_tx_intr(port);
|
|
|
+
|
|
|
+ port->tx_flags &= ~MNSCx_TX_STOP;
|
|
|
+ mb();
|
|
|
+
|
|
|
*port->tx_icr =
|
|
|
- NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL) | GxICR_ENABLE;
|
|
|
+ NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL) |
|
|
|
+ GxICR_ENABLE | GxICR_REQUEST | GxICR_DETECT;
|
|
|
x = *port->tx_icr;
|
|
|
- arch_local_irq_restore(flags);
|
|
|
}
|
|
|
|
|
|
static void mn10300_serial_dis_rx_intr(struct mn10300_serial_port *port)
|
|
@@ -487,16 +543,17 @@ static void mn10300_serial_receive_interrupt(struct mn10300_serial_port *port)
|
|
|
|
|
|
try_again:
|
|
|
/* pull chars out of the hat */
|
|
|
- ix = port->rx_outp;
|
|
|
- if (ix == port->rx_inp) {
|
|
|
+ ix = ACCESS_ONCE(port->rx_outp);
|
|
|
+ if (CIRC_CNT(port->rx_inp, ix, MNSC_BUFFER_SIZE) == 0) {
|
|
|
if (push && !tty->low_latency)
|
|
|
tty_flip_buffer_push(tty);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ smp_read_barrier_depends();
|
|
|
ch = port->rx_buffer[ix++];
|
|
|
st = port->rx_buffer[ix++];
|
|
|
- smp_rmb();
|
|
|
+ smp_mb();
|
|
|
port->rx_outp = ix & (MNSC_BUFFER_SIZE - 1);
|
|
|
port->uart.icount.rx++;
|
|
|
|
|
@@ -778,8 +835,6 @@ static void mn10300_serial_start_tx(struct uart_port *_port)
|
|
|
struct mn10300_serial_port *port =
|
|
|
container_of(_port, struct mn10300_serial_port, uart);
|
|
|
|
|
|
- u16 x;
|
|
|
-
|
|
|
_enter("%s{%lu}",
|
|
|
port->name,
|
|
|
CIRC_CNT(&port->uart.state->xmit.head,
|
|
@@ -787,14 +842,7 @@ static void mn10300_serial_start_tx(struct uart_port *_port)
|
|
|
UART_XMIT_SIZE));
|
|
|
|
|
|
/* kick the virtual DMA controller */
|
|
|
- arch_local_cli();
|
|
|
- x = *port->tx_icr;
|
|
|
- x |= GxICR_ENABLE;
|
|
|
-
|
|
|
- if (*port->_status & SC01STR_TBF)
|
|
|
- x &= ~(GxICR_REQUEST | GxICR_DETECT);
|
|
|
- else
|
|
|
- x |= GxICR_REQUEST | GxICR_DETECT;
|
|
|
+ mn10300_serial_en_tx_intr(port);
|
|
|
|
|
|
_debug("CTR=%04hx ICR=%02hx STR=%04x TMD=%02hx TBR=%04hx ICR=%04hx",
|
|
|
*port->_control, *port->_intr, *port->_status,
|
|
@@ -802,10 +850,6 @@ static void mn10300_serial_start_tx(struct uart_port *_port)
|
|
|
(port->div_timer == MNSCx_DIV_TIMER_8BIT) ?
|
|
|
*(volatile u8 *)port->_tmxbr : *port->_tmxbr,
|
|
|
*port->tx_icr);
|
|
|
-
|
|
|
- *port->tx_icr = x;
|
|
|
- x = *port->tx_icr;
|
|
|
- arch_local_sti();
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -815,13 +859,17 @@ static void mn10300_serial_send_xchar(struct uart_port *_port, char ch)
|
|
|
{
|
|
|
struct mn10300_serial_port *port =
|
|
|
container_of(_port, struct mn10300_serial_port, uart);
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
_enter("%s,%02x", port->name, ch);
|
|
|
|
|
|
if (likely(port->gdbstub)) {
|
|
|
port->tx_xchar = ch;
|
|
|
- if (ch)
|
|
|
+ if (ch) {
|
|
|
+ spin_lock_irqsave(&port->uart.lock, flags);
|
|
|
mn10300_serial_en_tx_intr(port);
|
|
|
+ spin_unlock_irqrestore(&port->uart.lock, flags);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -882,18 +930,21 @@ static void mn10300_serial_break_ctl(struct uart_port *_port, int ctl)
|
|
|
{
|
|
|
struct mn10300_serial_port *port =
|
|
|
container_of(_port, struct mn10300_serial_port, uart);
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
_enter("%s,%d", port->name, ctl);
|
|
|
|
|
|
+ spin_lock_irqsave(&port->uart.lock, flags);
|
|
|
if (ctl) {
|
|
|
/* tell the virtual DMA handler to assert BREAK */
|
|
|
- port->tx_break = 1;
|
|
|
+ port->tx_flags |= MNSCx_TX_BREAK;
|
|
|
mn10300_serial_en_tx_intr(port);
|
|
|
} else {
|
|
|
- port->tx_break = 0;
|
|
|
+ port->tx_flags &= ~MNSCx_TX_BREAK;
|
|
|
*port->_control &= ~SC01CTR_BKE;
|
|
|
mn10300_serial_en_tx_intr(port);
|
|
|
}
|
|
|
+ spin_unlock_irqrestore(&port->uart.lock, flags);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -916,6 +967,7 @@ static int mn10300_serial_startup(struct uart_port *_port)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
port->rx_inp = port->rx_outp = 0;
|
|
|
+ port->tx_flags = 0;
|
|
|
|
|
|
/* finally, enable the device */
|
|
|
*port->_intr = SC01ICR_TI;
|
|
@@ -928,22 +980,23 @@ static int mn10300_serial_startup(struct uart_port *_port)
|
|
|
pint->port = port;
|
|
|
pint->vdma = mn10300_serial_vdma_tx_handler;
|
|
|
|
|
|
- set_intr_level(port->rx_irq,
|
|
|
- NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL));
|
|
|
- set_intr_level(port->tx_irq,
|
|
|
- NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL));
|
|
|
+ irq_set_chip(port->rx_irq, &mn10300_serial_low_pic);
|
|
|
+ irq_set_chip(port->tx_irq, &mn10300_serial_low_pic);
|
|
|
irq_set_chip(port->tm_irq, &mn10300_serial_pic);
|
|
|
|
|
|
if (request_irq(port->rx_irq, mn10300_serial_interrupt,
|
|
|
- IRQF_DISABLED, port->rx_name, port) < 0)
|
|
|
+ IRQF_DISABLED | IRQF_NOBALANCING,
|
|
|
+ port->rx_name, port) < 0)
|
|
|
goto error;
|
|
|
|
|
|
if (request_irq(port->tx_irq, mn10300_serial_interrupt,
|
|
|
- IRQF_DISABLED, port->tx_name, port) < 0)
|
|
|
+ IRQF_DISABLED | IRQF_NOBALANCING,
|
|
|
+ port->tx_name, port) < 0)
|
|
|
goto error2;
|
|
|
|
|
|
if (request_irq(port->tm_irq, mn10300_serial_interrupt,
|
|
|
- IRQF_DISABLED, port->tm_name, port) < 0)
|
|
|
+ IRQF_DISABLED | IRQF_NOBALANCING,
|
|
|
+ port->tm_name, port) < 0)
|
|
|
goto error3;
|
|
|
mn10300_serial_mask_ack(port->tm_irq);
|
|
|
|
|
@@ -964,14 +1017,22 @@ error:
|
|
|
*/
|
|
|
static void mn10300_serial_shutdown(struct uart_port *_port)
|
|
|
{
|
|
|
+ unsigned long flags;
|
|
|
u16 x;
|
|
|
struct mn10300_serial_port *port =
|
|
|
container_of(_port, struct mn10300_serial_port, uart);
|
|
|
|
|
|
_enter("%s", port->name);
|
|
|
|
|
|
+ spin_lock_irqsave(&_port->lock, flags);
|
|
|
+ mn10300_serial_dis_tx_intr(port);
|
|
|
+
|
|
|
+ *port->rx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL);
|
|
|
+ x = *port->rx_icr;
|
|
|
+ port->tx_flags = 0;
|
|
|
+ spin_unlock_irqrestore(&_port->lock, flags);
|
|
|
+
|
|
|
/* disable the serial port and its baud rate timer */
|
|
|
- port->tx_break = 0;
|
|
|
*port->_control &= ~(SC01CTR_TXE | SC01CTR_RXE | SC01CTR_BKE);
|
|
|
*port->_tmxmd = 0;
|
|
|
|
|
@@ -986,12 +1047,8 @@ static void mn10300_serial_shutdown(struct uart_port *_port)
|
|
|
free_irq(port->rx_irq, port);
|
|
|
free_irq(port->tx_irq, port);
|
|
|
|
|
|
- arch_local_cli();
|
|
|
- *port->rx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL);
|
|
|
- x = *port->rx_icr;
|
|
|
- *port->tx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL);
|
|
|
- x = *port->tx_icr;
|
|
|
- arch_local_sti();
|
|
|
+ mn10300_serial_int_tbl[port->tx_irq].port = NULL;
|
|
|
+ mn10300_serial_int_tbl[port->rx_irq].port = NULL;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1317,7 +1374,8 @@ timer_okay:
|
|
|
if ((new->c_cflag & CREAD) == 0)
|
|
|
port->uart.ignore_status_mask |= (1 << TTY_NORMAL);
|
|
|
|
|
|
- scxctr |= *port->_control & (SC01CTR_TXE | SC01CTR_RXE | SC01CTR_BKE);
|
|
|
+ scxctr |= SC01CTR_TXE | SC01CTR_RXE;
|
|
|
+ scxctr |= *port->_control & SC01CTR_BKE;
|
|
|
*port->_control = scxctr;
|
|
|
|
|
|
spin_unlock_irqrestore(&port->uart.lock, flags);
|
|
@@ -1519,17 +1577,24 @@ static void mn10300_serial_console_write(struct console *co,
|
|
|
{
|
|
|
struct mn10300_serial_port *port;
|
|
|
unsigned i;
|
|
|
- u16 scxctr, txicr, tmp;
|
|
|
+ u16 scxctr;
|
|
|
u8 tmxmd;
|
|
|
+ unsigned long flags;
|
|
|
+ int locked = 1;
|
|
|
|
|
|
port = mn10300_serial_ports[co->index];
|
|
|
|
|
|
+ local_irq_save(flags);
|
|
|
+ if (port->uart.sysrq) {
|
|
|
+ /* mn10300_serial_interrupt() already took the lock */
|
|
|
+ locked = 0;
|
|
|
+ } else if (oops_in_progress) {
|
|
|
+ locked = spin_trylock(&port->uart.lock);
|
|
|
+ } else
|
|
|
+ spin_lock(&port->uart.lock);
|
|
|
+
|
|
|
/* firstly hijack the serial port from the "virtual DMA" controller */
|
|
|
- arch_local_cli();
|
|
|
- txicr = *port->tx_icr;
|
|
|
- *port->tx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL);
|
|
|
- tmp = *port->tx_icr;
|
|
|
- arch_local_sti();
|
|
|
+ mn10300_serial_dis_tx_intr(port);
|
|
|
|
|
|
/* the transmitter may be disabled */
|
|
|
scxctr = *port->_control;
|
|
@@ -1565,12 +1630,12 @@ static void mn10300_serial_console_write(struct console *co,
|
|
|
|
|
|
while (*port->_status & SC01STR_TBF)
|
|
|
continue;
|
|
|
- *(u8 *) port->_txb = ch;
|
|
|
+ *port->_txb = ch;
|
|
|
|
|
|
if (ch == 0x0a) {
|
|
|
while (*port->_status & SC01STR_TBF)
|
|
|
continue;
|
|
|
- *(u8 *) port->_txb = 0xd;
|
|
|
+ *port->_txb = 0xd;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1583,10 +1648,11 @@ static void mn10300_serial_console_write(struct console *co,
|
|
|
if (!(scxctr & SC01CTR_TXE))
|
|
|
*port->_control = scxctr;
|
|
|
|
|
|
- arch_local_cli();
|
|
|
- *port->tx_icr = txicr;
|
|
|
- tmp = *port->tx_icr;
|
|
|
- arch_local_sti();
|
|
|
+ mn10300_serial_en_tx_intr(port);
|
|
|
+
|
|
|
+ if (locked)
|
|
|
+ spin_unlock(&port->uart.lock);
|
|
|
+ local_irq_restore(flags);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1655,18 +1721,29 @@ static int mn10300_serial_poll_get_char(struct uart_port *_port)
|
|
|
|
|
|
_enter("%s", port->name);
|
|
|
|
|
|
- do {
|
|
|
- /* pull chars out of the hat */
|
|
|
- ix = port->rx_outp;
|
|
|
- if (ix == port->rx_inp)
|
|
|
- return NO_POLL_CHAR;
|
|
|
+ if (mn10300_serial_int_tbl[port->rx_irq].port != NULL) {
|
|
|
+ do {
|
|
|
+ /* pull chars out of the hat */
|
|
|
+ ix = ACCESS_ONCE(port->rx_outp);
|
|
|
+ if (CIRC_CNT(port->rx_inp, ix, MNSC_BUFFER_SIZE) == 0)
|
|
|
+ return NO_POLL_CHAR;
|
|
|
|
|
|
- ch = port->rx_buffer[ix++];
|
|
|
- st = port->rx_buffer[ix++];
|
|
|
- smp_rmb();
|
|
|
- port->rx_outp = ix & (MNSC_BUFFER_SIZE - 1);
|
|
|
+ smp_read_barrier_depends();
|
|
|
+ ch = port->rx_buffer[ix++];
|
|
|
+ st = port->rx_buffer[ix++];
|
|
|
+ smp_mb();
|
|
|
+ port->rx_outp = ix & (MNSC_BUFFER_SIZE - 1);
|
|
|
|
|
|
- } while (st & (SC01STR_FEF | SC01STR_PEF | SC01STR_OEF));
|
|
|
+ } while (st & (SC01STR_FEF | SC01STR_PEF | SC01STR_OEF));
|
|
|
+ } else {
|
|
|
+ do {
|
|
|
+ st = *port->_status;
|
|
|
+ if (st & (SC01STR_FEF | SC01STR_PEF | SC01STR_OEF))
|
|
|
+ continue;
|
|
|
+ } while (!(st & SC01STR_RBF));
|
|
|
+
|
|
|
+ ch = *port->_rxb;
|
|
|
+ }
|
|
|
|
|
|
return ch;
|
|
|
}
|
|
@@ -1693,12 +1770,12 @@ static void mn10300_serial_poll_put_char(struct uart_port *_port,
|
|
|
tmp = *port->_intr;
|
|
|
|
|
|
if (ch == 0x0a) {
|
|
|
- *(u8 *) port->_txb = 0x0d;
|
|
|
+ *port->_txb = 0x0d;
|
|
|
while (*port->_status & SC01STR_TBF)
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- *(u8 *) port->_txb = ch;
|
|
|
+ *port->_txb = ch;
|
|
|
while (*port->_status & SC01STR_TBF)
|
|
|
continue;
|
|
|
|