Commit aba8290f authored by Rengarajan S's avatar Rengarajan S Committed by Greg Kroah-Hartman

8250: microchip: pci1xxxx: Add Burst mode reception support in uart driver for writing into FIFO

In PCI1XXXX C0 endpoint, support for Burst mode is added.
pci1xxxx_handle_irq checks the burst status and based on that
incoming characters are received in DWORDs, RX handling is done
in pci1xxxx_rx_burst. While reading the burst status the RX error
is checked and the corresponding error statistics are updated.
Signed-off-by: default avatarRengarajan S <rengarajan.s@microchip.com>
Link: https://lore.kernel.org/r/20231215151123.41812-4-rengarajan.s@microchip.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent b7fbca37
......@@ -66,6 +66,9 @@
#define SYSLOCK_SLEEP_TIMEOUT 100
#define SYSLOCK_RETRY_CNT 1000
#define UART_RX_BYTE_FIFO 0x00
#define UART_FIFO_CTL 0x02
#define UART_ACTV_REG 0x11
#define UART_BLOCK_SET_ACTIVE BIT(0)
......@@ -96,8 +99,23 @@
#define UART_RESET_REG 0x94
#define UART_RESET_D3_RESET_DISABLE BIT(16)
#define UART_BURST_STATUS_REG 0x9C
#define UART_RX_BURST_FIFO 0xA4
#define MAX_PORTS 4
#define PORT_OFFSET 0x100
#define RX_BUF_SIZE 512
#define UART_BYTE_SIZE 1
#define UART_BURST_SIZE 4
#define UART_BST_STAT_RX_COUNT_MASK 0x00FF
#define UART_BST_STAT_IIR_INT_PEND 0x100000
#define UART_LSR_OVERRUN_ERR_CLR 0x43
#define UART_BST_STAT_LSR_RX_MASK 0x9F000000
#define UART_BST_STAT_LSR_RX_ERR_MASK 0x9E000000
#define UART_BST_STAT_LSR_OVERRUN_ERR 0x2000000
#define UART_BST_STAT_LSR_PARITY_ERR 0x4000000
#define UART_BST_STAT_LSR_FRAME_ERR 0x8000000
struct pci1xxxx_8250 {
unsigned int nr;
......@@ -249,6 +267,103 @@ static int pci1xxxx_rs485_config(struct uart_port *port,
return 0;
}
static u32 pci1xxxx_read_burst_status(struct uart_port *port)
{
u32 status;
status = readl(port->membase + UART_BURST_STATUS_REG);
if (status & UART_BST_STAT_LSR_RX_ERR_MASK) {
if (status & UART_BST_STAT_LSR_OVERRUN_ERR) {
writeb(UART_LSR_OVERRUN_ERR_CLR,
port->membase + UART_FIFO_CTL);
port->icount.overrun++;
}
if (status & UART_BST_STAT_LSR_FRAME_ERR)
port->icount.frame++;
if (status & UART_BST_STAT_LSR_PARITY_ERR)
port->icount.parity++;
}
return status;
}
static void pci1xxxx_process_read_data(struct uart_port *port,
unsigned char *rx_buff, u32 *buff_index,
u32 *valid_byte_count)
{
u32 valid_burst_count = *valid_byte_count / UART_BURST_SIZE;
u32 *burst_buf;
/*
* Depending on the RX Trigger Level the number of bytes that can be
* stored in RX FIFO at a time varies. Each transaction reads data
* in DWORDs. If there are less than four remaining valid_byte_count
* to read, the data is received one byte at a time.
*/
while (valid_burst_count--) {
if (*buff_index > (RX_BUF_SIZE - UART_BURST_SIZE))
break;
burst_buf = (u32 *)&rx_buff[*buff_index];
*burst_buf = readl(port->membase + UART_RX_BURST_FIFO);
*buff_index += UART_BURST_SIZE;
*valid_byte_count -= UART_BURST_SIZE;
}
while (*valid_byte_count) {
if (*buff_index > RX_BUF_SIZE)
break;
rx_buff[*buff_index] = readb(port->membase +
UART_RX_BYTE_FIFO);
*buff_index += UART_BYTE_SIZE;
*valid_byte_count -= UART_BYTE_SIZE;
}
}
static void pci1xxxx_rx_burst(struct uart_port *port, u32 uart_status)
{
u32 valid_byte_count = uart_status & UART_BST_STAT_RX_COUNT_MASK;
struct tty_port *tty_port = &port->state->port;
unsigned char rx_buff[RX_BUF_SIZE];
u32 buff_index = 0;
u32 copied_len;
if (valid_byte_count != 0 &&
valid_byte_count < RX_BUF_SIZE) {
pci1xxxx_process_read_data(port, rx_buff, &buff_index,
&valid_byte_count);
copied_len = (u32)tty_insert_flip_string(tty_port, rx_buff,
buff_index);
if (copied_len != buff_index)
port->icount.overrun += buff_index - copied_len;
port->icount.rx += buff_index;
tty_flip_buffer_push(tty_port);
}
}
static int pci1xxxx_handle_irq(struct uart_port *port)
{
unsigned long flags;
u32 status;
status = pci1xxxx_read_burst_status(port);
if (status & UART_BST_STAT_IIR_INT_PEND)
return 0;
spin_lock_irqsave(&port->lock, flags);
if (status & UART_BST_STAT_LSR_RX_MASK)
pci1xxxx_rx_burst(port, status);
spin_unlock_irqrestore(&port->lock, flags);
return 1;
}
static bool pci1xxxx_port_suspend(int line)
{
struct uart_8250_port *up = serial8250_get_port(line);
......@@ -360,7 +475,7 @@ static int pci1xxxx_resume(struct device *dev)
}
static int pci1xxxx_setup(struct pci_dev *pdev,
struct uart_8250_port *port, int port_idx)
struct uart_8250_port *port, int port_idx, int rev)
{
int ret;
......@@ -372,6 +487,10 @@ static int pci1xxxx_setup(struct pci_dev *pdev,
port->port.rs485_config = pci1xxxx_rs485_config;
port->port.rs485_supported = pci1xxxx_rs485_supported;
/* From C0 rev Burst operation is supported */
if (rev >= 0xC0)
port->port.handle_irq = pci1xxxx_handle_irq;
ret = serial8250_pci_setup_port(pdev, port, 0, PORT_OFFSET * port_idx, 0);
if (ret < 0)
return ret;
......@@ -491,7 +610,7 @@ static int pci1xxxx_serial_probe(struct pci_dev *pdev,
else
uart.port.irq = pci_irq_vector(pdev, 0);
rc = pci1xxxx_setup(pdev, &uart, port_idx);
rc = pci1xxxx_setup(pdev, &uart, port_idx, priv->dev_rev);
if (rc) {
dev_warn(dev, "Failed to setup port %u\n", i);
continue;
......
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