MosChip/NetMos MCS99xx Serial Cards
The
MosChip MCS9900 series are PCI Express I/O cards, including the 9904 tested here with 4 serial ports and other chips providing different configurations of serial and parallel ports. The cards are compatible with a standard 16450/16550/16650 UART, but do not work with the default serial driver for some reason. A driver from
MosChip for Linux is available but unfortunately it does not compile on newer kernels (after about 2.6.32) due to changes in the core serial code.
Sample of
lspci -v
output (there will be multiple chips/functions shown on each card for the different ports available):
05:00.0 Serial controller: NetMos Technology Device 9904 (prog-if 02 [16550])
Subsystem: Device a000:1000
Flags: bus master, fast devsel, latency 0, IRQ 17
I/O ports at ece0 [size=8]
Memory at dfbf0000 (32-bit, non-prefetchable) [size=4K]
Memory at dfbf2000 (32-bit, non-prefetchable) [size=4K]
Capabilities: [50] MSI: Enable- Count=1/8 Maskable- 64bit+
Capabilities: [78] Power Management version 3
Capabilities: [80] Express Legacy Endpoint, MSI 00
Capabilities: [100] Virtual Channel
Capabilities: [800] Advanced Error Reporting
Kernel driver in use: saturn
saturn
is the driver provided by the
99xx
kernel module. This card requires the "non-cascade" driver.
Driver
The driver published by
MosChip is GPL2 and based on the stock 8250 driver. It can be downloaded from
http://moschip.com/file_download.php?folder=MCS9900&fileid=MCS9900_Linux.tar.gz after registering for an account.
Patch
--- MCS9900_Linux/MCS9900_NonCascade/MCS99XX_V_1.0.0.0/99xx.c 2010-02-09 06:40:28.000000000 -0500
+++ MCS9900_Linux-new/MCS9900_NonCascade/MCS99XX_V_1.0.0.0/99xx.c 2011-07-28 10:46:15.733197673 -0400
@@ -495,7 +495,11 @@ static void serial99xx_start_tx(struct u
#endif
{
struct uart_99xx_port *up = &serial99xx_ports[port->line];
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32)
struct circ_buf *xmit = &up->port.info->xmit;
+#else
+ struct circ_buf *xmit = &up->port.state->xmit;
+#endif
u32 length=0,len2end;
int tail,head;
@@ -608,7 +612,11 @@ static _INLINE_ void check_modem_status(
if (status & UART_MSR_DCTS)
uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32)
wake_up_interruptible(&up->port.info->delta_msr_wait);
+#else
+ wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+#endif
DEBUG("In %s -------------------- END\n",__FUNCTION__);
}
@@ -622,8 +630,10 @@ static _INLINE_ void receive_chars(struc
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,26))
struct tty_struct *tty = up->port.info->tty;
-#else
+#elif (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32))
struct tty_struct *tty = up->port.info->port.tty;
+#else
+ struct tty_struct *tty = up->port.state->port.tty;
#endif
u8 ch,lsr = *status;
int max_count = 256;
@@ -710,7 +720,12 @@ ignore_char:
//Helper function used in ISR to send the data to the UART
static _INLINE_ void transmit_chars(struct uart_99xx_port *up)
{
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32))
struct circ_buf *xmit = &up->port.info->xmit;
+#else
+ struct circ_buf *xmit = &up->port.state->xmit;
+#endif
+
int count;
DEBUG("In %s ---------------------------------------START\n",__FUNCTION__);
@@ -758,7 +773,12 @@ static _INLINE_ void transmit_chars(stru
//Helper function to stop the characters transmission in DMA mode
static void transmit_chars_dma_stop_done(struct uart_99xx_port * up)
{
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32))
struct circ_buf *xmit = &up->port.info->xmit;
+#else
+ struct circ_buf *xmit = &up->port.state->xmit;
+#endif
+
long int transferred;
DEBUG("In %s ---------------------------------------START\n",__FUNCTION__);
//UPDATING THE TRANSMIT FIFO WITH THE AMOUNT OF DATA TRANSFERRED
@@ -774,7 +794,12 @@ static void transmit_chars_dma_stop_done
//Helper function to do the necessary action upon the successful completion of data transfer in DMA mode
static int transmit_chars_dma_done(struct uart_99xx_port * up)
{
- struct circ_buf *xmit = &(up->port.info->xmit);
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32))
+ struct circ_buf *xmit = &up->port.info->xmit;
+#else
+ struct circ_buf *xmit = &up->port.state->xmit;
+#endif
+
int length,tobe_transferred,transferred,len2end;
DEBUG("In %s ---------------------------------------START\n",__FUNCTION__);
@@ -859,8 +884,10 @@ static void receive_chars_dma_done(struc
{
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,26))
struct tty_struct *tty = up->port.info->tty;
-#else
+#elif (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32))
struct tty_struct *tty = up->port.info->port.tty;
+#else
+ struct tty_struct *tty = up->port.state->port.tty;
#endif
int i,rxdma_done=0;
@@ -969,11 +996,12 @@ static inline void serial99xx_handle_por
u8 status = serial_in(up, UART_LSR);
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,26))
- struct tty_struct *tty=up->port.info->tty;
-#else
+ struct tty_struct *tty = up->port.info->tty;
+#elif (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32))
struct tty_struct *tty = up->port.info->port.tty;
+#else
+ struct tty_struct *tty = up->port.state->port.tty;
#endif
-
DEBUG("In %s ---------------------------------------START\n",__FUNCTION__);
DEBUG("UART_LSR = %x...", status);
@@ -1862,7 +1890,7 @@ int serial99xx_match_port(struct uart_po
return 0;
}
-static DECLARE_MUTEX(serial99xx_sem);
+static DEFINE_SEMAPHORE(serial99xx_sem);
static struct uart_driver starex_serial_driver = {
.owner = THIS_MODULE,
This has been tested against 2.6.38.
--
StephenCavilia - 28 Jul 2011