mirror of
https://github.com/torvalds/linux.git
synced 2026-09-27 11:02:03 +02:00
The MUEx50 UART exposes a programmable RX trigger level via the RTL register. Implement uart_port RX trigger set/get callbacks for the mxpcie driver and wire them up to the generic rx_trig_bytes sysfs interface. Store the configured trigger level in the per-port private data. Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com> Link: https://patch.msgid.link/20260731074820.735619-15-crescentcy.hsieh@moxa.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
628 lines
18 KiB
C
628 lines
18 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Moxa PCIe multiport serial device driver
|
|
*
|
|
* Copyright (C) 2025 Moxa Inc. (support@moxa.com)
|
|
* Author: Crescent Hsieh <crescentcy.hsieh@moxa.com>
|
|
*/
|
|
#include <linux/bitfield.h>
|
|
#include <linux/bits.h>
|
|
#include <linux/device.h>
|
|
#include <linux/dev_printk.h>
|
|
#include <linux/io.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/module.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/tty_flip.h>
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/8250_pci.h>
|
|
#include <linux/serial_8250.h>
|
|
|
|
#include "8250.h"
|
|
|
|
#define PCI_DEVICE_ID_MOXA_CP102E 0x1024
|
|
#define PCI_DEVICE_ID_MOXA_CP102EL 0x1025
|
|
#define PCI_DEVICE_ID_MOXA_CP102N 0x1027
|
|
#define PCI_DEVICE_ID_MOXA_CP104EL_A 0x1045
|
|
#define PCI_DEVICE_ID_MOXA_CP104N 0x1046
|
|
#define PCI_DEVICE_ID_MOXA_CP112N 0x1121
|
|
#define PCI_DEVICE_ID_MOXA_CP114EL 0x1144
|
|
#define PCI_DEVICE_ID_MOXA_CP114N 0x1145
|
|
#define PCI_DEVICE_ID_MOXA_CP116E_A_A 0x1160
|
|
#define PCI_DEVICE_ID_MOXA_CP116E_A_B 0x1161
|
|
#define PCI_DEVICE_ID_MOXA_CP118EL_A 0x1182
|
|
#define PCI_DEVICE_ID_MOXA_CP118E_A_I 0x1183
|
|
#define PCI_DEVICE_ID_MOXA_CP132EL 0x1322
|
|
#define PCI_DEVICE_ID_MOXA_CP132N 0x1323
|
|
#define PCI_DEVICE_ID_MOXA_CP134EL_A 0x1342
|
|
#define PCI_DEVICE_ID_MOXA_CP134N 0x1343
|
|
#define PCI_DEVICE_ID_MOXA_CP138E_A 0x1381
|
|
#define PCI_DEVICE_ID_MOXA_CP168EL_A 0x1683
|
|
|
|
/* Bits in PCI device ID encoding board capabilities */
|
|
#define MOXA_DEV_ID_IFACE_MASK GENMASK(11, 8) /* Supported serial interface */
|
|
#define MOXA_DEV_ID_NPORTS_MASK GENMASK(7, 4) /* Number of UART ports */
|
|
|
|
/* UART */
|
|
#define MOXA_PUART_BASE_BAUD 921600
|
|
#define MOXA_PUART_OFFSET 0x200
|
|
#define MOXA_PUART_TX_TRIG_DEFAULT 0
|
|
#define MOXA_PUART_RX_TRIG_DEFAULT 96
|
|
#define MOXA_PUART_RX_FLOW_LOW_DEFAULT 16
|
|
#define MOXA_PUART_RX_FLOW_HIGH_DEFAULT 110
|
|
|
|
/* Special Function Register (SFR) */
|
|
#define MOXA_PUART_SFR 0x07
|
|
#define MOXA_PUART_SFR_FORCE_TX BIT(0)
|
|
#define MOXA_PUART_SFR_950 BIT(5)
|
|
|
|
/* Enhanced Function Register (EFR) */
|
|
#define MOXA_PUART_EFR 0x0A
|
|
#define MOXA_PUART_EFR_ENHANCED BIT(4)
|
|
#define MOXA_PUART_EFR_AUTO_RTS BIT(6)
|
|
#define MOXA_PUART_EFR_AUTO_CTS BIT(7)
|
|
#define MOXA_PUART_EFR_RX_FLOW_MASK GENMASK(1, 0)
|
|
#define MOXA_PUART_EFR_RX_FLOW_DISABLED 0x0
|
|
#define MOXA_PUART_EFR_RX_FLOW_XON2_XOFF2 0x1
|
|
#define MOXA_PUART_EFR_RX_FLOW_XON1_XOFF1 0x2
|
|
#define MOXA_PUART_EFR_RX_FLOW_COPY_TX 0x3
|
|
#define MOXA_PUART_EFR_TX_FLOW_MASK GENMASK(3, 2)
|
|
#define MOXA_PUART_EFR_TX_FLOW_DISABLED 0x0
|
|
#define MOXA_PUART_EFR_TX_FLOW_XON2_XOFF2 0x1
|
|
#define MOXA_PUART_EFR_TX_FLOW_XON1_XOFF1 0x2
|
|
#define MOXA_PUART_EFR_TX_FLOW_RESERVED 0x3
|
|
|
|
#define MOXA_PUART_XON1 0x0B
|
|
#define MOXA_PUART_XON2 0x0C
|
|
#define MOXA_PUART_XOFF1 0x0D
|
|
#define MOXA_PUART_XOFF2 0x0E
|
|
#define MOXA_PUART_TTL 0x10 /* Tx Interrupt Trigger Level */
|
|
#define MOXA_PUART_RTL 0x11 /* Rx Interrupt Trigger Level */
|
|
#define MOXA_PUART_FCL 0x12 /* Flow Control Low Trigger Level */
|
|
#define MOXA_PUART_FCH 0x13 /* Flow Control High Trigger Level */
|
|
#define MOXA_PUART_RX_FIFO_CNT 0x15 /* Rx FIFO Data Counter */
|
|
#define MOXA_PUART_TX_FIFO_CNT 0x16 /* Tx FIFO Data Counter */
|
|
|
|
#define MOXA_PUART_RX_FIFO_MEM 0x100 /* Memory Space to Rx FIFO Data Register */
|
|
#define MOXA_PUART_TX_FIFO_MEM 0x100 /* Memory Space to Tx FIFO Data Register */
|
|
|
|
#define MOXA_GPIO_DIRECTION 0x09
|
|
#define MOXA_GPIO_OUTPUT 0x0A
|
|
|
|
#define MOXA_GPIO_PIN2 BIT(2)
|
|
|
|
#define MOXA_UIR_OFFSET 0x04
|
|
#define MOXA_UIR_RS232 0x00
|
|
#define MOXA_UIR_RS422 0x01
|
|
#define MOXA_UIR_RS485_4W 0x0B
|
|
#define MOXA_UIR_RS485_2W 0x0F
|
|
|
|
#define MOXA_EVEN_RS_MASK GENMASK(3, 0)
|
|
#define MOXA_ODD_RS_MASK GENMASK(7, 4)
|
|
|
|
struct mxpcie8250_port {
|
|
int line;
|
|
u8 rx_trig_level;
|
|
};
|
|
|
|
struct mxpcie8250 {
|
|
unsigned int supp_rs;
|
|
unsigned int num_ports;
|
|
void __iomem *bar1_base; /* UART registers (MMIO) */
|
|
void __iomem *bar2_base; /* UIR / GPIO / CPLD (IO) */
|
|
struct mxpcie8250_port port[] __counted_by(num_ports);
|
|
};
|
|
|
|
enum {
|
|
MOXA_SUPP_RS232 = BIT(0),
|
|
MOXA_SUPP_RS422 = BIT(1),
|
|
MOXA_SUPP_RS485 = BIT(2),
|
|
};
|
|
|
|
static const struct serial_rs485 mxpcie8250_rs485_supported = {
|
|
.flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX | SER_RS485_MODE_RS422,
|
|
};
|
|
|
|
static bool mxpcie8250_is_mini_pcie(unsigned short device)
|
|
{
|
|
if (device == PCI_DEVICE_ID_MOXA_CP102N ||
|
|
device == PCI_DEVICE_ID_MOXA_CP104N ||
|
|
device == PCI_DEVICE_ID_MOXA_CP112N ||
|
|
device == PCI_DEVICE_ID_MOXA_CP114N ||
|
|
device == PCI_DEVICE_ID_MOXA_CP132N ||
|
|
device == PCI_DEVICE_ID_MOXA_CP134N)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static unsigned int mxpcie8250_get_supp_rs(unsigned short device)
|
|
{
|
|
switch (device & MOXA_DEV_ID_IFACE_MASK) {
|
|
case 0x0000:
|
|
case 0x0600:
|
|
return MOXA_SUPP_RS232;
|
|
case 0x0100:
|
|
return MOXA_SUPP_RS232 | MOXA_SUPP_RS422 | MOXA_SUPP_RS485;
|
|
case 0x0300:
|
|
return MOXA_SUPP_RS422 | MOXA_SUPP_RS485;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static unsigned short mxpcie8250_get_nports(unsigned short device)
|
|
{
|
|
switch (device) {
|
|
case PCI_DEVICE_ID_MOXA_CP116E_A_A:
|
|
case PCI_DEVICE_ID_MOXA_CP116E_A_B:
|
|
return 8;
|
|
default:
|
|
return FIELD_GET(MOXA_DEV_ID_NPORTS_MASK, device);
|
|
}
|
|
}
|
|
|
|
static void mxpcie8250_set_interface(struct mxpcie8250 *priv,
|
|
unsigned int port_idx,
|
|
u8 mode)
|
|
{
|
|
void __iomem *uir_addr = priv->bar2_base + MOXA_UIR_OFFSET + port_idx / 2;
|
|
u8 cval;
|
|
|
|
cval = ioread8(uir_addr);
|
|
|
|
if (port_idx % 2)
|
|
FIELD_MODIFY(MOXA_ODD_RS_MASK, &cval, mode);
|
|
else
|
|
FIELD_MODIFY(MOXA_EVEN_RS_MASK, &cval, mode);
|
|
|
|
iowrite8(cval, uir_addr);
|
|
}
|
|
|
|
/*
|
|
* Moxa PCIe multiport serial boards support switching serial interfaces
|
|
* via the ioctl() command "TIOCSRS485". Supported modes and corresponding
|
|
* flags in "serial_rs485":
|
|
*
|
|
* RS232 = (no flags set)
|
|
* RS422 = SER_RS485_ENABLED | SER_RS485_MODE_RS422
|
|
* RS485_2W (half-duplex) = SER_RS485_ENABLED
|
|
* RS485_4W (full-duplex) = SER_RS485_ENABLED | SER_RS485_RX_DURING_TX
|
|
*/
|
|
static int mxpcie8250_rs485_config(struct uart_port *port,
|
|
struct ktermios *termios,
|
|
struct serial_rs485 *rs485)
|
|
{
|
|
struct mxpcie8250 *priv = dev_get_drvdata(port->dev);
|
|
u8 mode = MOXA_UIR_RS232;
|
|
|
|
if (rs485->flags & SER_RS485_ENABLED) {
|
|
if (rs485->flags & SER_RS485_MODE_RS422)
|
|
mode = MOXA_UIR_RS422;
|
|
else if (rs485->flags & SER_RS485_RX_DURING_TX)
|
|
mode = MOXA_UIR_RS485_4W;
|
|
else
|
|
mode = MOXA_UIR_RS485_2W;
|
|
} else if (!(priv->supp_rs & MOXA_SUPP_RS232)) {
|
|
return -ENODEV;
|
|
}
|
|
mxpcie8250_set_interface(priv, port->port_id, mode);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mxpcie8250_set_termios(struct uart_port *port,
|
|
struct ktermios *new,
|
|
const struct ktermios *old)
|
|
{
|
|
struct uart_8250_port *up = up_to_u8250p(port);
|
|
struct tty_struct *tty = port->state->port.tty;
|
|
unsigned int cflag = tty->termios.c_cflag;
|
|
u8 efr, val;
|
|
|
|
serial8250_do_set_termios(port, new, old);
|
|
|
|
up->port.status &= ~(UPSTAT_AUTORTS | UPSTAT_AUTOCTS | UPSTAT_AUTOXOFF);
|
|
|
|
efr = serial_in(up, MOXA_PUART_EFR);
|
|
efr &= ~(MOXA_PUART_EFR_AUTO_RTS | MOXA_PUART_EFR_AUTO_CTS);
|
|
|
|
if (cflag & CRTSCTS) {
|
|
efr |= (MOXA_PUART_EFR_AUTO_RTS | MOXA_PUART_EFR_AUTO_CTS);
|
|
up->port.status |= (UPSTAT_AUTORTS | UPSTAT_AUTOCTS);
|
|
}
|
|
/* Set on-chip software flow control character */
|
|
serial_out(up, MOXA_PUART_XON1, START_CHAR(tty));
|
|
serial_out(up, MOXA_PUART_XON2, START_CHAR(tty));
|
|
serial_out(up, MOXA_PUART_XOFF1, STOP_CHAR(tty));
|
|
serial_out(up, MOXA_PUART_XOFF2, STOP_CHAR(tty));
|
|
|
|
val = I_IXON(tty) ? MOXA_PUART_EFR_RX_FLOW_XON1_XOFF1 : MOXA_PUART_EFR_RX_FLOW_DISABLED;
|
|
FIELD_MODIFY(MOXA_PUART_EFR_RX_FLOW_MASK, &efr, val);
|
|
|
|
val = I_IXOFF(tty) ? MOXA_PUART_EFR_TX_FLOW_XON1_XOFF1 : MOXA_PUART_EFR_TX_FLOW_DISABLED;
|
|
FIELD_MODIFY(MOXA_PUART_EFR_TX_FLOW_MASK, &efr, val);
|
|
|
|
if (I_IXOFF(tty))
|
|
up->port.status |= UPSTAT_AUTOXOFF;
|
|
|
|
serial_out(up, MOXA_PUART_EFR, efr);
|
|
}
|
|
|
|
static int mxpcie8250_get_rxtrig(struct uart_port *port)
|
|
{
|
|
return serial_port_in(port, MOXA_PUART_RTL);
|
|
}
|
|
|
|
static int mxpcie8250_set_rxtrig(struct uart_port *port, unsigned char bytes)
|
|
{
|
|
struct mxpcie8250 *priv = dev_get_drvdata(port->dev);
|
|
|
|
if (bytes > port->fifosize)
|
|
return -EINVAL;
|
|
|
|
serial_port_out(port, MOXA_PUART_RTL, bytes);
|
|
priv->port[port->port_id].rx_trig_level = bytes;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mxpcie8250_startup(struct uart_port *port)
|
|
{
|
|
struct mxpcie8250 *priv = dev_get_drvdata(port->dev);
|
|
struct uart_8250_port *up = up_to_u8250p(port);
|
|
int ret;
|
|
|
|
ret = serial8250_do_startup(port);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/*
|
|
* The TX FIFO write pointer (w_ptr) and read pointer (r_ptr)
|
|
* are driven by different clocks: w_ptr uses the PCIe clock
|
|
* and r_ptr uses the UART clock. When TX FIFO flush is requested,
|
|
* w_ptr may be cleared before r_ptr, so the UART can still observe
|
|
* pending TX data.
|
|
*
|
|
* It is recommended to clear the FIFOs at least 5 times to ensure
|
|
* both pointers are reset.
|
|
*/
|
|
for (unsigned int i = 0; i < 5; ++i)
|
|
serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
|
|
|
|
serial_out(up, MOXA_PUART_EFR, MOXA_PUART_EFR_ENHANCED);
|
|
serial_out(up, MOXA_PUART_SFR, MOXA_PUART_SFR_950);
|
|
|
|
serial_out(up, MOXA_PUART_TTL, MOXA_PUART_TX_TRIG_DEFAULT);
|
|
serial_out(up, MOXA_PUART_RTL, priv->port[port->port_id].rx_trig_level);
|
|
serial_out(up, MOXA_PUART_FCL, MOXA_PUART_RX_FLOW_LOW_DEFAULT);
|
|
serial_out(up, MOXA_PUART_FCH, MOXA_PUART_RX_FLOW_HIGH_DEFAULT);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mxpcie8250_shutdown(struct uart_port *port)
|
|
{
|
|
struct uart_8250_port *up = up_to_u8250p(port);
|
|
|
|
serial_out(up, MOXA_PUART_EFR, 0);
|
|
serial_out(up, MOXA_PUART_SFR, 0);
|
|
|
|
serial8250_do_shutdown(port);
|
|
}
|
|
|
|
static void mxpcie8250_throttle(struct uart_port *port)
|
|
{
|
|
guard(uart_port_lock_irqsave)(port);
|
|
|
|
port->ops->stop_rx(port);
|
|
}
|
|
|
|
static void mxpcie8250_unthrottle(struct uart_port *port)
|
|
{
|
|
struct uart_8250_port *up = up_to_u8250p(port);
|
|
|
|
guard(uart_port_lock_irqsave)(port);
|
|
|
|
up->ier |= UART_IER_RLSI | UART_IER_RDI;
|
|
port->read_status_mask |= UART_LSR_DR;
|
|
serial_out(up, UART_IER, up->ier);
|
|
}
|
|
|
|
static void mxpcie8250_rx_chars(struct uart_8250_port *up)
|
|
{
|
|
struct uart_port *port = &up->port;
|
|
struct tty_port *tport = &port->state->port;
|
|
unsigned int count;
|
|
u8 *buf;
|
|
|
|
count = serial_in(up, MOXA_PUART_RX_FIFO_CNT);
|
|
count = min(count, port->fifosize);
|
|
count = tty_prepare_flip_string(tport, &buf, count);
|
|
if (!count)
|
|
return;
|
|
|
|
for (unsigned int i = 0; i < count; ++i)
|
|
buf[i] = serial_in(up, MOXA_PUART_RX_FIFO_MEM + i);
|
|
|
|
port->icount.rx += count;
|
|
tty_flip_buffer_push(tport);
|
|
}
|
|
|
|
static bool mxpcie8250_should_rx(struct uart_8250_port *up, u16 lsr)
|
|
{
|
|
struct uart_port *port = &up->port;
|
|
|
|
if (!(lsr & (UART_LSR_DR | UART_LSR_BI)))
|
|
return false;
|
|
|
|
if (!(port->status & (UPSTAT_AUTOCTS | UPSTAT_AUTORTS)))
|
|
return true;
|
|
if (lsr & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS))
|
|
return true;
|
|
if (port->read_status_mask & UART_LSR_DR)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static void mxpcie8250_tx_chars(struct uart_8250_port *up)
|
|
{
|
|
struct uart_port *port = &up->port;
|
|
unsigned int offset = 0;
|
|
unsigned char c;
|
|
|
|
uart_port_tx_limited(port, c, port->fifosize - serial_in(up, MOXA_PUART_TX_FIFO_CNT),
|
|
true,
|
|
serial_out(up, MOXA_PUART_TX_FIFO_MEM + offset++, c),
|
|
({}));
|
|
}
|
|
|
|
static int mxpcie8250_handle_irq(struct uart_port *port)
|
|
{
|
|
struct uart_8250_port *up = up_to_u8250p(port);
|
|
u16 lsr;
|
|
u8 iir;
|
|
|
|
iir = serial_in(up, UART_IIR);
|
|
if (iir & UART_IIR_NO_INT)
|
|
return 0;
|
|
|
|
guard(uart_port_lock_check_sysrq_irqsave)(port);
|
|
|
|
lsr = serial_lsr_in(up);
|
|
if (mxpcie8250_should_rx(up, lsr)) {
|
|
if (!(lsr & UART_LSR_BRK_ERROR_BITS))
|
|
mxpcie8250_rx_chars(up);
|
|
else
|
|
lsr = serial8250_rx_chars(up, lsr);
|
|
}
|
|
|
|
serial8250_modem_status(up);
|
|
|
|
if ((lsr & UART_LSR_THRE) && (up->ier & UART_IER_THRI))
|
|
mxpcie8250_tx_chars(up);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void mxpcie8250_software_break_ctl(struct uart_port *port, int break_state)
|
|
{
|
|
struct uart_8250_port *up = up_to_u8250p(port);
|
|
struct tty_struct *tty = port->state->port.tty;
|
|
unsigned int baud, quot;
|
|
u8 sfr, tx_byte = 0x01;
|
|
|
|
guard(uart_port_lock_irqsave)(port);
|
|
|
|
if (break_state == -1) {
|
|
serial_out(up, UART_LCR, up->lcr | UART_LCR_DLAB);
|
|
serial_dl_write(up, 0);
|
|
serial_out(up, UART_LCR, up->lcr);
|
|
|
|
serial_out(up, MOXA_PUART_TX_FIFO_MEM, tx_byte);
|
|
|
|
sfr = serial_in(up, MOXA_PUART_SFR);
|
|
serial_out(up, MOXA_PUART_SFR, sfr | MOXA_PUART_SFR_FORCE_TX);
|
|
|
|
up->lcr |= UART_LCR_SBC;
|
|
serial_out(up, UART_LCR, up->lcr);
|
|
} else {
|
|
up->lcr &= ~UART_LCR_SBC;
|
|
serial_out(up, UART_LCR, up->lcr);
|
|
|
|
sfr = serial_in(up, MOXA_PUART_SFR);
|
|
serial_out(up, MOXA_PUART_SFR, sfr & ~MOXA_PUART_SFR_FORCE_TX);
|
|
|
|
serial_out(up, UART_FCR, UART_FCR_CLEAR_XMIT);
|
|
|
|
baud = tty_get_baud_rate(tty);
|
|
quot = uart_get_divisor(port, baud);
|
|
serial8250_do_set_divisor(port, baud, quot);
|
|
serial_out(up, UART_LCR, up->lcr);
|
|
}
|
|
}
|
|
|
|
static void mxpcie8250_break_ctl(struct uart_port *port, int break_state)
|
|
{
|
|
if (port->rs485.flags & SER_RS485_ENABLED &&
|
|
!(port->rs485.flags & SER_RS485_MODE_RS422))
|
|
mxpcie8250_software_break_ctl(port, break_state);
|
|
else
|
|
serial8250_do_break_ctl(port, break_state);
|
|
}
|
|
|
|
static void mxpcie8250_init_board(struct pci_dev *pdev, struct mxpcie8250 *priv)
|
|
{
|
|
void __iomem *bar2_base = priv->bar2_base;
|
|
unsigned short device = pdev->device;
|
|
u8 cval;
|
|
|
|
/* Initial terminator */
|
|
if (device == PCI_DEVICE_ID_MOXA_CP114EL ||
|
|
device == PCI_DEVICE_ID_MOXA_CP118EL_A) {
|
|
iowrite8(0xff, bar2_base + MOXA_GPIO_DIRECTION);
|
|
iowrite8(0x00, bar2_base + MOXA_GPIO_OUTPUT);
|
|
}
|
|
/*
|
|
* Enable hardware buffer to prevent break signal output when system boots up.
|
|
* This hardware buffer is only supported on Mini PCIe series.
|
|
*/
|
|
if (mxpcie8250_is_mini_pcie(device)) {
|
|
/* Set GPIO direction */
|
|
cval = ioread8(bar2_base + MOXA_GPIO_DIRECTION);
|
|
cval |= MOXA_GPIO_PIN2;
|
|
iowrite8(cval, bar2_base + MOXA_GPIO_DIRECTION);
|
|
/* Enable low GPIO */
|
|
cval = ioread8(bar2_base + MOXA_GPIO_OUTPUT);
|
|
cval &= ~MOXA_GPIO_PIN2;
|
|
iowrite8(cval, bar2_base + MOXA_GPIO_OUTPUT);
|
|
}
|
|
}
|
|
|
|
static void mxpcie8250_setup_port(struct pci_dev *pdev,
|
|
struct mxpcie8250 *priv,
|
|
struct uart_8250_port *up,
|
|
int idx)
|
|
{
|
|
unsigned short device = pdev->device;
|
|
int offset = idx * MOXA_PUART_OFFSET;
|
|
u8 init_mode = MOXA_UIR_RS232;
|
|
|
|
if (priv->supp_rs & MOXA_SUPP_RS485) {
|
|
up->port.rs485_config = mxpcie8250_rs485_config;
|
|
up->port.rs485_supported = mxpcie8250_rs485_supported;
|
|
}
|
|
if (!(priv->supp_rs & MOXA_SUPP_RS232)) {
|
|
init_mode = MOXA_UIR_RS422;
|
|
up->port.rs485.flags = SER_RS485_ENABLED | SER_RS485_MODE_RS422;
|
|
}
|
|
mxpcie8250_set_interface(priv, idx, init_mode);
|
|
|
|
if (idx == 3 &&
|
|
(device == PCI_DEVICE_ID_MOXA_CP104EL_A ||
|
|
device == PCI_DEVICE_ID_MOXA_CP114EL ||
|
|
device == PCI_DEVICE_ID_MOXA_CP134EL_A))
|
|
offset = 7 * MOXA_PUART_OFFSET;
|
|
|
|
up->port.mapbase = pci_resource_start(pdev, FL_BASE1) + offset;
|
|
up->port.membase = pcim_iomap_table(pdev)[FL_BASE1] + offset;
|
|
}
|
|
|
|
static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct uart_8250_port up = {};
|
|
struct mxpcie8250 *priv;
|
|
unsigned short device = pdev->device;
|
|
unsigned int num_ports;
|
|
int ret;
|
|
|
|
ret = pcim_enable_device(pdev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
num_ports = mxpcie8250_get_nports(device);
|
|
|
|
priv = devm_kzalloc(dev, struct_size(priv, port, num_ports), GFP_KERNEL);
|
|
if (!priv)
|
|
return -ENOMEM;
|
|
|
|
priv->num_ports = num_ports;
|
|
priv->supp_rs = mxpcie8250_get_supp_rs(device);
|
|
|
|
priv->bar1_base = pcim_iomap(pdev, FL_BASE1, 0);
|
|
if (!priv->bar1_base)
|
|
return -ENOMEM;
|
|
|
|
priv->bar2_base = pcim_iomap(pdev, FL_BASE2, 0);
|
|
if (!priv->bar2_base)
|
|
return -ENOMEM;
|
|
|
|
mxpcie8250_init_board(pdev, priv);
|
|
|
|
up.port.dev = dev;
|
|
up.port.irq = pdev->irq;
|
|
up.port.uartclk = MOXA_PUART_BASE_BAUD * 16;
|
|
up.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
|
|
up.port.type = PORT_MUEX50;
|
|
|
|
up.port.iotype = UPIO_MEM;
|
|
up.port.iobase = 0;
|
|
up.port.regshift = 0;
|
|
|
|
up.port.set_termios = mxpcie8250_set_termios;
|
|
up.port.get_rxtrig = mxpcie8250_get_rxtrig;
|
|
up.port.set_rxtrig = mxpcie8250_set_rxtrig;
|
|
up.port.startup = mxpcie8250_startup;
|
|
up.port.shutdown = mxpcie8250_shutdown;
|
|
up.port.throttle = mxpcie8250_throttle;
|
|
up.port.unthrottle = mxpcie8250_unthrottle;
|
|
up.port.handle_irq = mxpcie8250_handle_irq;
|
|
up.port.break_ctl = mxpcie8250_break_ctl;
|
|
|
|
for (unsigned int i = 0; i < num_ports; i++) {
|
|
mxpcie8250_setup_port(pdev, priv, &up, i);
|
|
|
|
dev_dbg(dev, "Setup PCI port: port %lx, irq %d, type %d\n",
|
|
up.port.iobase, up.port.irq, up.port.iotype);
|
|
|
|
priv->port[i].line = serial8250_register_8250_port(&up);
|
|
if (priv->port[i].line < 0) {
|
|
dev_err(dev,
|
|
"Couldn't register serial port %lx, irq %d, type %d, error %d\n",
|
|
up.port.iobase, up.port.irq,
|
|
up.port.iotype, priv->port[i].line);
|
|
break;
|
|
}
|
|
priv->port[i].rx_trig_level = MOXA_PUART_RX_TRIG_DEFAULT;
|
|
}
|
|
pci_set_drvdata(pdev, priv);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mxpcie8250_remove(struct pci_dev *pdev)
|
|
{
|
|
struct mxpcie8250 *priv = pci_get_drvdata(pdev);
|
|
|
|
for (unsigned int i = 0; i < priv->num_ports; i++)
|
|
serial8250_unregister_port(priv->port[i].line);
|
|
}
|
|
|
|
static const struct pci_device_id mxpcie8250_pci_ids[] = {
|
|
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102E) },
|
|
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102EL) },
|
|
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102N) },
|
|
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104EL_A) },
|
|
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104N) },
|
|
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP112N) },
|
|
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114EL) },
|
|
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114N) },
|
|
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_A) },
|
|
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_B) },
|
|
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118EL_A) },
|
|
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118E_A_I) },
|
|
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132EL) },
|
|
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132N) },
|
|
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134EL_A) },
|
|
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134N) },
|
|
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP138E_A) },
|
|
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168EL_A) },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(pci, mxpcie8250_pci_ids);
|
|
|
|
static struct pci_driver mxpcie8250_pci_driver = {
|
|
.name = "8250_mxpcie",
|
|
.id_table = mxpcie8250_pci_ids,
|
|
.probe = mxpcie8250_probe,
|
|
.remove = mxpcie8250_remove,
|
|
};
|
|
module_pci_driver(mxpcie8250_pci_driver);
|
|
|
|
MODULE_AUTHOR("Moxa Inc.");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("Moxa PCIe Multiport Serial Device Driver");
|