diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 7820049aba5af2c068f6c1a3a8ff72cfd797ea68..25e9fe0358ba1dfa7068039f1ecfa9c577598559 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -237,6 +237,8 @@ struct imx_port { enum imx_tx_state tx_state; struct hrtimer trigger_start_tx; struct hrtimer trigger_stop_tx; + + unsigned int parity; }; struct imx_port_ucrs { @@ -399,6 +401,97 @@ static void start_hrtimer_ms(struct hrtimer *hrt, unsigned long msec) hrtimer_start(hrt, ms_to_ktime(msec), HRTIMER_MODE_REL); } +static inline void imx_uart_markspace_rx( struct imx_port *sport, unsigned int * rx_char) +{ + unsigned int rx = *rx_char; + unsigned int markspace, byte_oe, controller_oe, error_oe, paritybit; + + if ( ! ( sport->parity & CMSPAR )) return; + + markspace = !!( sport->parity & UCR2_PROE ); // 1 for MARK, 0 for SPACE + + // As the controller works on ODD and EVEN, we need to + // convert this to MARK and SPACE here + // Count ones in the received byte, first bit is set if the number is odd + // 0 for even, 1 for odd + byte_oe = !!(__sw_hweight8(rx) & 1); + controller_oe = !!(UCR2_PROE & imx_uart_readl(sport, UCR2)); + error_oe = !!( URXD_PRERR & rx ); + + // Get the value of the parity bit + // depends on the PRERR (parity error bit) + // the configured parity mode PROE + // and the number of ones in the data byte + // The parity bit on the line is an xor of those + paritybit = ( controller_oe ^ byte_oe ) ^ error_oe; + + // Check if the value of the parity bit is the expected + if ( markspace == paritybit ){ + // Parity bit received has the expected value + // Clear error bits + rx &= ~URXD_PRERR; + // if no other error bit is set, unset the ERR bit also + if ( 0 == (rx & ( URXD_BRK | URXD_FRMERR | URXD_OVRRUN ) )) + rx &= ~URXD_ERR; + }else{ + // Parity bit received has not the expected value + // Set error bits + rx |= URXD_ERR | URXD_PRERR; + } + *rx_char = rx; +} + +static irqreturn_t __imx_uart_rxint(int irq, void *dev_id); +inline void imx_uart_markspace_tx(struct imx_port *sport, unsigned char tx) +{ + unsigned int ucr2, byte_oe, markspace, controller_oe, needed_controller_oe; + int ret; + + if ( ! ( sport->parity & CMSPAR )) return; + + ucr2 = imx_uart_readl(sport, UCR2); + + byte_oe = !!(__sw_hweight8(tx) & 1); + markspace = !!( sport->parity & UCR2_PROE ); // 1 for MARK, 0 for SPACE + + controller_oe = !!(UCR2_PROE & ucr2); + + // Find out which parity mode (ODD or EVEN) is needed to generate + // the configured output value of the parity bit + // markspace is the value we want. + needed_controller_oe = byte_oe ^ markspace; + + if (needed_controller_oe != controller_oe ){ // Change the parity + + /* Wait until FIFO and shift registers are empty */ + while (!(imx_uart_readl(sport,USR2) & USR2_TXDC)); + + /* Disable TX */ + ucr2 &= ~UCR2_TXEN; + imx_uart_writel(sport, ucr2, UCR2); + + /* Disable RX, needs to be dissabled so the state of + PROE can be matched exactly to the received bytes. + First wait readout the RX Fifo using the RX irq handler, + Disable the RX. + Read again to check if another byte has arrived.*/ + ret = __imx_uart_rxint(0, sport); + ucr2 &= ~UCR2_RXEN; + imx_uart_writel(sport, ucr2 , UCR2); + ret = __imx_uart_rxint(0, sport); + + /* Reconfigure: Set parity and enable RX & TX again */ + if( needed_controller_oe ){ + ucr2 |= UCR2_PROE; + }else{ + ucr2 &= ~UCR2_PROE; + } + ucr2 |= (UCR2_RXEN | UCR2_TXEN); + imx_uart_writel(sport, ucr2, UCR2); + } +} + + /* called with port.lock taken and irqs off */ static void imx_uart_start_rx(struct uart_port *port) { @@ -557,6 +650,10 @@ static inline void imx_uart_transmit_buffer(struct imx_port *sport) while (!uart_circ_empty(xmit) && !(imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL)) { + + /* Special handling for MARK and SPACE parity */ + imx_uart_markspace_tx( sport, xmit->buf[xmit->tail]); + /* send xmit->buf[xmit->tail] * out the port here */ imx_uart_writel(sport, xmit->buf[xmit->tail], URTX0); @@ -809,6 +906,8 @@ static irqreturn_t __imx_uart_rxint(int irq, void *dev_id) if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) continue; + imx_uart_markspace_rx( sport, &rx); + if (unlikely(rx & URXD_ERR)) { if (rx & URXD_BRK) sport->port.icount.brk++; @@ -1410,8 +1509,12 @@ static int imx_uart_startup(struct uart_port *port) imx_uart_writel(sport, ucr4 & ~UCR4_DREN, UCR4); /* Can we enable the DMA support? */ - if (!uart_console(port) && imx_uart_dma_init(sport) == 0) + if (!uart_console(port) + && !( sport->parity & CMSPAR ) // Mark Space parity does not work with dma, but disable dma after open is not implemented. + && imx_uart_dma_init(sport) == 0) + { dma_is_inited = 1; + } spin_lock_irqsave(&sport->port.lock, flags); /* Reset fifo's and state machines */ @@ -1679,10 +1782,17 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, ucr2 &= ~UCR2_IRTS; if (termios->c_cflag & CSTOPB) ucr2 |= UCR2_STPB; + + sport->parity = 0; if (termios->c_cflag & PARENB) { ucr2 |= UCR2_PREN; - if (termios->c_cflag & PARODD) - ucr2 |= UCR2_PROE; + sport->parity |= UCR2_PREN; + if (termios->c_cflag & PARODD){ + ucr2 |= UCR2_PROE; // ODD or MARK parity + sport->parity |= UCR2_PROE; + } + if (termios->c_cflag & CMSPAR) // Enable MARK/SPACE parity + sport->parity |= CMSPAR; } sport->port.read_status_mask = 0; @@ -1758,6 +1868,12 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, imx_uart_writel(sport, denom, UBMR); } + if ( sport->parity & CMSPAR ){ + /* If MARK or SPACE parity is selected, + wait until FIFO and shift registers are empty */ + while (!(imx_uart_readl(sport, USR2) & USR2_TXDC)); + } + if (!imx_uart_is_imx1(sport)) imx_uart_writel(sport, sport->port.uartclk / div / 1000, IMX21_ONEMS);