Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 340368
b: refs/heads/master
c: 8f0bcbc
h: refs/heads/master
v: v3
  • Loading branch information
Mark Salter authored and David Howells committed Dec 12, 2012
1 parent 446ce47 commit daf4433
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 49 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 8d160027ff234bddea627ba54c2b85efa1884867
refs/heads/master: 8f0bcbcab016324c2a3ba4cc715e8e523c29a578
2 changes: 1 addition & 1 deletion trunk/arch/mn10300/kernel/asm-offsets.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ void foo(void)
OFFSET(__rx_outp, mn10300_serial_port, rx_outp);
OFFSET(__uart_state, mn10300_serial_port, uart.state);
OFFSET(__tx_xchar, mn10300_serial_port, tx_xchar);
OFFSET(__tx_break, mn10300_serial_port, tx_break);
OFFSET(__tx_flags, mn10300_serial_port, tx_flags);
OFFSET(__intr_flags, mn10300_serial_port, intr_flags);
OFFSET(__rx_icr, mn10300_serial_port, rx_icr);
OFFSET(__tx_icr, mn10300_serial_port, tx_icr);
Expand Down
9 changes: 6 additions & 3 deletions trunk/arch/mn10300/kernel/mn10300-serial-low.S
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ ENTRY(mn10300_serial_vdma_tx_handler)
movbu d2,(e3) # ACK the interrupt
movhu (e3),d2 # flush

btst 0x01,(__tx_break,a3) # handle transmit break request
bne mnsc_vdma_tx_break
btst 0xFF,(__tx_flags,a3) # handle transmit flags
bne mnsc_vdma_tx_flags

movbu (SCxSTR,e2),d2 # don't try and transmit a char if the
# buffer is not empty
Expand Down Expand Up @@ -171,10 +171,13 @@ mnsc_vdma_tx_empty:
bset MNSCx_TX_EMPTY,(__intr_flags,a3)
bra mnsc_vdma_tx_done

mnsc_vdma_tx_break:
mnsc_vdma_tx_flags:
btst MNSCx_TX_STOP,(__tx_flags,a3)
bne mnsc_vdma_tx_stop
movhu (SCxCTR,e2),d2 # turn on break mode
or SC01CTR_BKE,d2
movhu d2,(SCxCTR,e2)
mnsc_vdma_tx_stop:
mov +(NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL)|GxICR_DETECT),d2
movhu d2,(e3) # disable transmit interrupts on this
# channel
Expand Down
121 changes: 78 additions & 43 deletions trunk/arch/mn10300/kernel/mn10300-serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -444,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)
Expand Down Expand Up @@ -807,34 +835,21 @@ 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,
&port->uart.state->xmit.tail,
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,
*port->_tmxmd,
(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();
}

/*
Expand All @@ -844,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);
}
}
}

Expand Down Expand Up @@ -911,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);
}

/*
Expand All @@ -945,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;
Expand Down Expand Up @@ -994,14 +1017,22 @@ static int mn10300_serial_startup(struct uart_port *_port)
*/
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;

Expand All @@ -1016,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;
}

/*
Expand Down Expand Up @@ -1549,17 +1576,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;
Expand Down Expand Up @@ -1613,10 +1647,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);
}

/*
Expand Down
6 changes: 5 additions & 1 deletion trunk/arch/mn10300/kernel/mn10300-serial.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,18 @@
#define MNSCx_TX_SPACE 0x04
#define MNSCx_TX_EMPTY 0x08

/* tx_flags bits */
#define MNSCx_TX_BREAK 0x01
#define MNSCx_TX_STOP 0x02

#ifndef __ASSEMBLY__

struct mn10300_serial_port {
char *rx_buffer; /* reception buffer base */
unsigned rx_inp; /* pointer to rx input offset */
unsigned rx_outp; /* pointer to rx output offset */
u8 tx_xchar; /* high-priority XON/XOFF buffer */
u8 tx_break; /* transmit break request */
u8 tx_flags; /* transmit break/stop request */
u8 intr_flags; /* interrupt flags */
volatile u16 *rx_icr; /* Rx interrupt control register */
volatile u16 *tx_icr; /* Tx interrupt control register */
Expand Down

0 comments on commit daf4433

Please sign in to comment.