| // 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"); |