Skip to content
Snippets Groups Projects
Commit 3255bc70 authored by Jonas Höppner's avatar Jonas Höppner Committed by Clemens Terasa
Browse files

tty: serial: imx: Add SW emulation for mark and space parity

Integrate software implementation for mark and space from guf mdb driver.
Rewrite most of the code:
 Use controller_parity from the uart register
 Use available flags instead of new enum.
 Resort some code to remove dupplicated code snippets.
 Rewrite logic, more readable.
 Move mark and space parity to inline functions
 Disable DMA when CMSPAR is set, still port DMA needs to be disable to
 always work, as disable DMA after open (when setting CMSPAR via ioctl)
 is not implemented. Close and reopen after setting the bit does work.

 Also cleanup the reconfiguration of ODD/EVEN during TX, as RX lost
 bytes in the mdb driver variant. Now the rx_interrupt routine is called
 until all buffers are empty before disalbe RX and change ODD/EVEN parity.
 This way the received bytes could be mapped to the parity config used
 during rx. Still a short moment RX is disabled which could lead to
 missed bits.

 Theres also a busy wait until TX queues are empty, which could probably
 lead to high system load on slow datarates, as this might wait one byte
 long.

BCS 746-000322
parent 7430b85b
No related branches found
No related tags found
No related merge requests found
......@@ -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);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment