tty: serial: men_z135_uart: Fix driver for changes in hardware
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>
This commit is contained in:
parent
b164c9721e
commit
01ba8d6af4
@ -23,7 +23,6 @@
|
|||||||
#define MEN_Z135_MAX_PORTS 12
|
#define MEN_Z135_MAX_PORTS 12
|
||||||
#define MEN_Z135_BASECLK 29491200
|
#define MEN_Z135_BASECLK 29491200
|
||||||
#define MEN_Z135_FIFO_SIZE 1024
|
#define MEN_Z135_FIFO_SIZE 1024
|
||||||
#define MEN_Z135_NUM_MSI_VECTORS 2
|
|
||||||
#define MEN_Z135_FIFO_WATERMARK 1020
|
#define MEN_Z135_FIFO_WATERMARK 1020
|
||||||
|
|
||||||
#define MEN_Z135_STAT_REG 0x0
|
#define MEN_Z135_STAT_REG 0x0
|
||||||
@ -34,12 +33,11 @@
|
|||||||
#define MEN_Z135_CONF_REG 0x808
|
#define MEN_Z135_CONF_REG 0x808
|
||||||
#define MEN_Z135_UART_FREQ 0x80c
|
#define MEN_Z135_UART_FREQ 0x80c
|
||||||
#define MEN_Z135_BAUD_REG 0x810
|
#define MEN_Z135_BAUD_REG 0x810
|
||||||
#define MENZ135_TIMEOUT 0x814
|
#define MEN_Z135_TIMEOUT 0x814
|
||||||
|
|
||||||
#define MEN_Z135_MEM_SIZE 0x818
|
#define MEN_Z135_MEM_SIZE 0x818
|
||||||
|
|
||||||
#define IS_IRQ(x) ((x) & 1)
|
#define IRQ_ID(x) ((x) & 0x1f)
|
||||||
#define IRQ_ID(x) (((x) >> 1) & 7)
|
|
||||||
|
|
||||||
#define MEN_Z135_IER_RXCIEN BIT(0) /* RX Space IRQ */
|
#define MEN_Z135_IER_RXCIEN BIT(0) /* RX Space IRQ */
|
||||||
#define MEN_Z135_IER_TXCIEN BIT(1) /* TX Space IRQ */
|
#define MEN_Z135_IER_TXCIEN BIT(1) /* TX Space IRQ */
|
||||||
@ -94,11 +92,11 @@
|
|||||||
#define MEN_Z135_LSR_TEXP BIT(6)
|
#define MEN_Z135_LSR_TEXP BIT(6)
|
||||||
#define MEN_Z135_LSR_RXFIFOERR BIT(7)
|
#define MEN_Z135_LSR_RXFIFOERR BIT(7)
|
||||||
|
|
||||||
#define MEN_Z135_IRQ_ID_MST 0
|
#define MEN_Z135_IRQ_ID_RLS BIT(0)
|
||||||
#define MEN_Z135_IRQ_ID_TSA 1
|
#define MEN_Z135_IRQ_ID_RDA BIT(1)
|
||||||
#define MEN_Z135_IRQ_ID_RDA 2
|
#define MEN_Z135_IRQ_ID_CTI BIT(2)
|
||||||
#define MEN_Z135_IRQ_ID_RLS 3
|
#define MEN_Z135_IRQ_ID_TSA BIT(3)
|
||||||
#define MEN_Z135_IRQ_ID_CTI 6
|
#define MEN_Z135_IRQ_ID_MST BIT(4)
|
||||||
|
|
||||||
#define LCR(x) (((x) >> MEN_Z135_LCR_SHIFT) & 0xff)
|
#define LCR(x) (((x) >> MEN_Z135_LCR_SHIFT) & 0xff)
|
||||||
|
|
||||||
@ -118,12 +116,18 @@ static int align;
|
|||||||
module_param(align, int, S_IRUGO);
|
module_param(align, int, S_IRUGO);
|
||||||
MODULE_PARM_DESC(align, "Keep hardware FIFO write pointer aligned, default 0");
|
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 men_z135_port {
|
||||||
struct uart_port port;
|
struct uart_port port;
|
||||||
struct mcb_device *mdev;
|
struct mcb_device *mdev;
|
||||||
unsigned char *rxbuf;
|
unsigned char *rxbuf;
|
||||||
u32 stat_reg;
|
u32 stat_reg;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
bool automode;
|
||||||
};
|
};
|
||||||
#define to_men_z135(port) container_of((port), struct men_z135_port, port)
|
#define to_men_z135(port) container_of((port), struct men_z135_port, port)
|
||||||
|
|
||||||
@ -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)
|
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_handle_dcd_change(&uart->port,
|
||||||
uart->stat_reg & ~MEN_Z135_MSR_DCD);
|
msr & MEN_Z135_MSR_DCD);
|
||||||
if (uart->stat_reg & MEN_Z135_MSR_DCTS)
|
if (msr & MEN_Z135_MSR_DCTS)
|
||||||
uart_handle_cts_change(&uart->port,
|
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)
|
static void men_z135_handle_lsr(struct men_z135_port *uart)
|
||||||
@ -322,7 +330,8 @@ static void men_z135_handle_tx(struct men_z135_port *uart)
|
|||||||
|
|
||||||
txfree = MEN_Z135_FIFO_WATERMARK - txc;
|
txfree = MEN_Z135_FIFO_WATERMARK - txc;
|
||||||
if (txfree <= 0) {
|
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);
|
txfree, qlen);
|
||||||
goto irq_en;
|
goto irq_en;
|
||||||
}
|
}
|
||||||
@ -373,43 +382,54 @@ out:
|
|||||||
* @irq: The IRQ number
|
* @irq: The IRQ number
|
||||||
* @data: Pointer to UART port
|
* @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)
|
static irqreturn_t men_z135_intr(int irq, void *data)
|
||||||
{
|
{
|
||||||
struct men_z135_port *uart = (struct men_z135_port *)data;
|
struct men_z135_port *uart = (struct men_z135_port *)data;
|
||||||
struct uart_port *port = &uart->port;
|
struct uart_port *port = &uart->port;
|
||||||
|
bool handled = false;
|
||||||
|
unsigned long flags;
|
||||||
int irq_id;
|
int irq_id;
|
||||||
|
|
||||||
uart->stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG);
|
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);
|
irq_id = IRQ_ID(uart->stat_reg);
|
||||||
switch (irq_id) {
|
|
||||||
case MEN_Z135_IRQ_ID_MST:
|
if (!irq_id)
|
||||||
men_z135_handle_modem_status(uart);
|
goto out;
|
||||||
break;
|
|
||||||
case MEN_Z135_IRQ_ID_TSA:
|
spin_lock_irqsave(&port->lock, flags);
|
||||||
men_z135_handle_tx(uart);
|
/* It's save to write to IIR[7:6] RXC[9:8] */
|
||||||
break;
|
iowrite8(irq_id, port->membase + MEN_Z135_STAT_REG);
|
||||||
case MEN_Z135_IRQ_ID_CTI:
|
|
||||||
dev_dbg(&uart->mdev->dev, "Character Timeout Indication\n");
|
if (irq_id & MEN_Z135_IRQ_ID_RLS) {
|
||||||
/* Fallthrough */
|
|
||||||
case MEN_Z135_IRQ_ID_RDA:
|
|
||||||
/* Reading data clears RX IRQ */
|
|
||||||
men_z135_handle_rx(uart);
|
|
||||||
break;
|
|
||||||
case MEN_Z135_IRQ_ID_RLS:
|
|
||||||
men_z135_handle_lsr(uart);
|
men_z135_handle_lsr(uart);
|
||||||
break;
|
handled = true;
|
||||||
default:
|
|
||||||
dev_warn(&uart->mdev->dev, "Unknown IRQ id %d\n", irq_id);
|
|
||||||
return IRQ_NONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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)
|
static void men_z135_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||||
{
|
{
|
||||||
struct men_z135_port *uart = to_men_z135(port);
|
u32 old;
|
||||||
u32 conf_reg = 0;
|
u32 conf_reg;
|
||||||
|
|
||||||
|
conf_reg = old = ioread32(port->membase + MEN_Z135_CONF_REG);
|
||||||
if (mctrl & TIOCM_RTS)
|
if (mctrl & TIOCM_RTS)
|
||||||
conf_reg |= MEN_Z135_MCR_RTS;
|
conf_reg |= MEN_Z135_MCR_RTS;
|
||||||
|
else
|
||||||
|
conf_reg &= ~MEN_Z135_MCR_RTS;
|
||||||
|
|
||||||
if (mctrl & TIOCM_DTR)
|
if (mctrl & TIOCM_DTR)
|
||||||
conf_reg |= MEN_Z135_MCR_DTR;
|
conf_reg |= MEN_Z135_MCR_DTR;
|
||||||
|
else
|
||||||
|
conf_reg &= ~MEN_Z135_MCR_DTR;
|
||||||
|
|
||||||
if (mctrl & TIOCM_OUT1)
|
if (mctrl & TIOCM_OUT1)
|
||||||
conf_reg |= MEN_Z135_MCR_OUT1;
|
conf_reg |= MEN_Z135_MCR_OUT1;
|
||||||
|
else
|
||||||
|
conf_reg &= ~MEN_Z135_MCR_OUT1;
|
||||||
|
|
||||||
if (mctrl & TIOCM_OUT2)
|
if (mctrl & TIOCM_OUT2)
|
||||||
conf_reg |= MEN_Z135_MCR_OUT2;
|
conf_reg |= MEN_Z135_MCR_OUT2;
|
||||||
|
else
|
||||||
|
conf_reg &= ~MEN_Z135_MCR_OUT2;
|
||||||
|
|
||||||
if (mctrl & TIOCM_LOOP)
|
if (mctrl & TIOCM_LOOP)
|
||||||
conf_reg |= MEN_Z135_MCR_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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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)
|
static unsigned int men_z135_get_mctrl(struct uart_port *port)
|
||||||
{
|
{
|
||||||
unsigned int mctrl = 0;
|
unsigned int mctrl = 0;
|
||||||
u32 stat_reg;
|
|
||||||
u8 msr;
|
u8 msr;
|
||||||
|
|
||||||
stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG);
|
msr = ioread8(port->membase + MEN_Z135_STAT_REG + 1);
|
||||||
|
|
||||||
msr = ~((stat_reg >> 8) & 0xff);
|
|
||||||
|
|
||||||
if (msr & MEN_Z135_MSR_CTS)
|
if (msr & MEN_Z135_MSR_CTS)
|
||||||
mctrl |= TIOCM_CTS;
|
mctrl |= TIOCM_CTS;
|
||||||
@ -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_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
|
* men_z135_start_tx() - Start transmitting characters
|
||||||
* @port: The UART port
|
* @port: The UART port
|
||||||
@ -535,6 +581,9 @@ static void men_z135_start_tx(struct uart_port *port)
|
|||||||
{
|
{
|
||||||
struct men_z135_port *uart = to_men_z135(port);
|
struct men_z135_port *uart = to_men_z135(port);
|
||||||
|
|
||||||
|
if (uart->automode)
|
||||||
|
men_z135_disable_ms(port);
|
||||||
|
|
||||||
men_z135_handle_tx(uart);
|
men_z135_handle_tx(uart);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -584,6 +633,9 @@ static int men_z135_startup(struct uart_port *port)
|
|||||||
|
|
||||||
iowrite32(conf_reg, port->membase + MEN_Z135_CONF_REG);
|
iowrite32(conf_reg, port->membase + MEN_Z135_CONF_REG);
|
||||||
|
|
||||||
|
if (rx_timeout)
|
||||||
|
iowrite32(rx_timeout, port->membase + MEN_Z135_TIMEOUT);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,6 +655,7 @@ static void men_z135_set_termios(struct uart_port *port,
|
|||||||
struct ktermios *termios,
|
struct ktermios *termios,
|
||||||
struct ktermios *old)
|
struct ktermios *old)
|
||||||
{
|
{
|
||||||
|
struct men_z135_port *uart = to_men_z135(port);
|
||||||
unsigned int baud;
|
unsigned int baud;
|
||||||
u32 conf_reg;
|
u32 conf_reg;
|
||||||
u32 bd_reg;
|
u32 bd_reg;
|
||||||
@ -643,6 +696,16 @@ static void men_z135_set_termios(struct uart_port *port,
|
|||||||
} else
|
} else
|
||||||
lcr |= MEN_Z135_PAR_DIS << MEN_Z135_PEN_SHIFT;
|
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 */
|
termios->c_cflag &= ~CMSPAR; /* Mark/Space parity is not supported */
|
||||||
|
|
||||||
conf_reg |= lcr << MEN_Z135_LCR_SHIFT;
|
conf_reg |= lcr << MEN_Z135_LCR_SHIFT;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user