Skip to content

Commit

Permalink
tty: serial: men_z135_uart: Fix driver for changes in hardware
Browse files Browse the repository at this point in the history
16z135 IP Core has changed so the driver needs to be updated to respect
these changes. The following changes have been made:

* Don't invert the 16z135 modem status register when reading.
* Add module parameter to configure the (baud rate dependent) RX timeout.
  Character timeout in seconds = (timeout_reg * baud_reg * 4)/freq_reg.
* Enable the handling of UART core's automatic flow control feature.
  When AFE is active disable generation of modem status IRQs.
* Rework the handling of IRQs to be conform with newer FPGA versions and
  take precautions not to miss an interrupt because of the destructive read
  of the IIR register.
* Correct men_z135_handle_modem_status(), MSR is stat_reg[15:8] not
  stat_reg[7:0]
* Correct calling of uart_handle_{dcd,cts}_change()
* Reset CLOCAL when CRTSCTS is set

Signed-off-by: Johannes Thumshirn <johannes.thumshirn@men.de>
Reviewed-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Johannes Thumshirn authored and Greg Kroah-Hartman committed Feb 2, 2015
1 parent b164c97 commit 01ba8d6
Showing 1 changed file with 109 additions and 46 deletions.
155 changes: 109 additions & 46 deletions drivers/tty/serial/men_z135_uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
#define MEN_Z135_MAX_PORTS 12
#define MEN_Z135_BASECLK 29491200
#define MEN_Z135_FIFO_SIZE 1024
#define MEN_Z135_NUM_MSI_VECTORS 2
#define MEN_Z135_FIFO_WATERMARK 1020

#define MEN_Z135_STAT_REG 0x0
Expand All @@ -34,12 +33,11 @@
#define MEN_Z135_CONF_REG 0x808
#define MEN_Z135_UART_FREQ 0x80c
#define MEN_Z135_BAUD_REG 0x810
#define MENZ135_TIMEOUT 0x814
#define MEN_Z135_TIMEOUT 0x814

#define MEN_Z135_MEM_SIZE 0x818

#define IS_IRQ(x) ((x) & 1)
#define IRQ_ID(x) (((x) >> 1) & 7)
#define IRQ_ID(x) ((x) & 0x1f)

#define MEN_Z135_IER_RXCIEN BIT(0) /* RX Space IRQ */
#define MEN_Z135_IER_TXCIEN BIT(1) /* TX Space IRQ */
Expand Down Expand Up @@ -94,11 +92,11 @@
#define MEN_Z135_LSR_TEXP BIT(6)
#define MEN_Z135_LSR_RXFIFOERR BIT(7)

#define MEN_Z135_IRQ_ID_MST 0
#define MEN_Z135_IRQ_ID_TSA 1
#define MEN_Z135_IRQ_ID_RDA 2
#define MEN_Z135_IRQ_ID_RLS 3
#define MEN_Z135_IRQ_ID_CTI 6
#define MEN_Z135_IRQ_ID_RLS BIT(0)
#define MEN_Z135_IRQ_ID_RDA BIT(1)
#define MEN_Z135_IRQ_ID_CTI BIT(2)
#define MEN_Z135_IRQ_ID_TSA BIT(3)
#define MEN_Z135_IRQ_ID_MST BIT(4)

#define LCR(x) (((x) >> MEN_Z135_LCR_SHIFT) & 0xff)

Expand All @@ -118,12 +116,18 @@ static int align;
module_param(align, int, S_IRUGO);
MODULE_PARM_DESC(align, "Keep hardware FIFO write pointer aligned, default 0");

static uint rx_timeout;
module_param(rx_timeout, uint, S_IRUGO);
MODULE_PARM_DESC(rx_timeout, "RX timeout. "
"Timeout in seconds = (timeout_reg * baud_reg * 4) / freq_reg");

struct men_z135_port {
struct uart_port port;
struct mcb_device *mdev;
unsigned char *rxbuf;
u32 stat_reg;
spinlock_t lock;
bool automode;
};
#define to_men_z135(port) container_of((port), struct men_z135_port, port)

Expand Down Expand Up @@ -180,12 +184,16 @@ static inline void men_z135_reg_clr(struct men_z135_port *uart,
*/
static void men_z135_handle_modem_status(struct men_z135_port *uart)
{
if (uart->stat_reg & MEN_Z135_MSR_DDCD)
u8 msr;

msr = (uart->stat_reg >> 8) & 0xff;

if (msr & MEN_Z135_MSR_DDCD)
uart_handle_dcd_change(&uart->port,
uart->stat_reg & ~MEN_Z135_MSR_DCD);
if (uart->stat_reg & MEN_Z135_MSR_DCTS)
msr & MEN_Z135_MSR_DCD);
if (msr & MEN_Z135_MSR_DCTS)
uart_handle_cts_change(&uart->port,
uart->stat_reg & ~MEN_Z135_MSR_CTS);
msr & MEN_Z135_MSR_CTS);
}

static void men_z135_handle_lsr(struct men_z135_port *uart)
Expand Down Expand Up @@ -322,7 +330,8 @@ static void men_z135_handle_tx(struct men_z135_port *uart)

txfree = MEN_Z135_FIFO_WATERMARK - txc;
if (txfree <= 0) {
pr_err("Not enough room in TX FIFO have %d, need %d\n",
dev_err(&uart->mdev->dev,
"Not enough room in TX FIFO have %d, need %d\n",
txfree, qlen);
goto irq_en;
}
Expand Down Expand Up @@ -373,43 +382,54 @@ static void men_z135_handle_tx(struct men_z135_port *uart)
* @irq: The IRQ number
* @data: Pointer to UART port
*
* Check IIR register to see which tasklet to start.
* Check IIR register to find the cause of the interrupt and handle it.
* It is possible that multiple interrupts reason bits are set and reading
* the IIR is a destructive read, so we always need to check for all possible
* interrupts and handle them.
*/
static irqreturn_t men_z135_intr(int irq, void *data)
{
struct men_z135_port *uart = (struct men_z135_port *)data;
struct uart_port *port = &uart->port;
bool handled = false;
unsigned long flags;
int irq_id;

uart->stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG);
/* IRQ pending is low active */
if (IS_IRQ(uart->stat_reg))
return IRQ_NONE;

irq_id = IRQ_ID(uart->stat_reg);
switch (irq_id) {
case MEN_Z135_IRQ_ID_MST:
men_z135_handle_modem_status(uart);
break;
case MEN_Z135_IRQ_ID_TSA:
men_z135_handle_tx(uart);
break;
case MEN_Z135_IRQ_ID_CTI:
dev_dbg(&uart->mdev->dev, "Character Timeout Indication\n");
/* Fallthrough */
case MEN_Z135_IRQ_ID_RDA:
/* Reading data clears RX IRQ */
men_z135_handle_rx(uart);
break;
case MEN_Z135_IRQ_ID_RLS:

if (!irq_id)
goto out;

spin_lock_irqsave(&port->lock, flags);
/* It's save to write to IIR[7:6] RXC[9:8] */
iowrite8(irq_id, port->membase + MEN_Z135_STAT_REG);

if (irq_id & MEN_Z135_IRQ_ID_RLS) {
men_z135_handle_lsr(uart);
break;
default:
dev_warn(&uart->mdev->dev, "Unknown IRQ id %d\n", irq_id);
return IRQ_NONE;
handled = true;
}

if (irq_id & (MEN_Z135_IRQ_ID_RDA | MEN_Z135_IRQ_ID_CTI)) {
if (irq_id & MEN_Z135_IRQ_ID_CTI)
dev_dbg(&uart->mdev->dev, "Character Timeout Indication\n");
men_z135_handle_rx(uart);
handled = true;
}

if (irq_id & MEN_Z135_IRQ_ID_TSA) {
men_z135_handle_tx(uart);
handled = true;
}

return IRQ_HANDLED;
if (irq_id & MEN_Z135_IRQ_ID_MST) {
men_z135_handle_modem_status(uart);
handled = true;
}

spin_unlock_irqrestore(&port->lock, flags);
out:
return IRQ_RETVAL(handled);
}

/**
Expand Down Expand Up @@ -464,21 +484,37 @@ static unsigned int men_z135_tx_empty(struct uart_port *port)
*/
static void men_z135_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct men_z135_port *uart = to_men_z135(port);
u32 conf_reg = 0;
u32 old;
u32 conf_reg;

conf_reg = old = ioread32(port->membase + MEN_Z135_CONF_REG);
if (mctrl & TIOCM_RTS)
conf_reg |= MEN_Z135_MCR_RTS;
else
conf_reg &= ~MEN_Z135_MCR_RTS;

if (mctrl & TIOCM_DTR)
conf_reg |= MEN_Z135_MCR_DTR;
else
conf_reg &= ~MEN_Z135_MCR_DTR;

if (mctrl & TIOCM_OUT1)
conf_reg |= MEN_Z135_MCR_OUT1;
else
conf_reg &= ~MEN_Z135_MCR_OUT1;

if (mctrl & TIOCM_OUT2)
conf_reg |= MEN_Z135_MCR_OUT2;
else
conf_reg &= ~MEN_Z135_MCR_OUT2;

if (mctrl & TIOCM_LOOP)
conf_reg |= MEN_Z135_MCR_LOOP;
else
conf_reg &= ~MEN_Z135_MCR_LOOP;

men_z135_reg_set(uart, MEN_Z135_CONF_REG, conf_reg);
if (conf_reg != old)
iowrite32(conf_reg, port->membase + MEN_Z135_CONF_REG);
}

/**
Expand All @@ -490,12 +526,9 @@ static void men_z135_set_mctrl(struct uart_port *port, unsigned int mctrl)
static unsigned int men_z135_get_mctrl(struct uart_port *port)
{
unsigned int mctrl = 0;
u32 stat_reg;
u8 msr;

stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG);

msr = ~((stat_reg >> 8) & 0xff);
msr = ioread8(port->membase + MEN_Z135_STAT_REG + 1);

if (msr & MEN_Z135_MSR_CTS)
mctrl |= TIOCM_CTS;
Expand Down Expand Up @@ -524,6 +557,19 @@ static void men_z135_stop_tx(struct uart_port *port)
men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN);
}

/*
* men_z135_disable_ms() - Disable Modem Status
* port: The UART port
*
* Enable Modem Status IRQ.
*/
static void men_z135_disable_ms(struct uart_port *port)
{
struct men_z135_port *uart = to_men_z135(port);

men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_MSIEN);
}

/**
* men_z135_start_tx() - Start transmitting characters
* @port: The UART port
Expand All @@ -535,6 +581,9 @@ static void men_z135_start_tx(struct uart_port *port)
{
struct men_z135_port *uart = to_men_z135(port);

if (uart->automode)
men_z135_disable_ms(port);

men_z135_handle_tx(uart);
}

Expand Down Expand Up @@ -584,6 +633,9 @@ static int men_z135_startup(struct uart_port *port)

iowrite32(conf_reg, port->membase + MEN_Z135_CONF_REG);

if (rx_timeout)
iowrite32(rx_timeout, port->membase + MEN_Z135_TIMEOUT);

return 0;
}

Expand All @@ -603,6 +655,7 @@ static void men_z135_set_termios(struct uart_port *port,
struct ktermios *termios,
struct ktermios *old)
{
struct men_z135_port *uart = to_men_z135(port);
unsigned int baud;
u32 conf_reg;
u32 bd_reg;
Expand Down Expand Up @@ -643,6 +696,16 @@ static void men_z135_set_termios(struct uart_port *port,
} else
lcr |= MEN_Z135_PAR_DIS << MEN_Z135_PEN_SHIFT;

conf_reg |= MEN_Z135_IER_MSIEN;
if (termios->c_cflag & CRTSCTS) {
conf_reg |= MEN_Z135_MCR_RCFC;
uart->automode = true;
termios->c_cflag &= ~CLOCAL;
} else {
conf_reg &= ~MEN_Z135_MCR_RCFC;
uart->automode = false;
}

termios->c_cflag &= ~CMSPAR; /* Mark/Space parity is not supported */

conf_reg |= lcr << MEN_Z135_LCR_SHIFT;
Expand Down

0 comments on commit 01ba8d6

Please sign in to comment.