On 18/01/2012 17:32, Wolfgang Grandegger wrote: >> Further investigation indicates that when writing, tx interrupt is not >> asserted as expected (rt_16550_write): >> > /* unmask tx interrupt */ >> > ctx->ier_status |= IER_TX; >> > rt_16550_reg_out(rt_16550_io_mode_from_ctx(ctx), >> > ctx->base_addr, IER, >> > ctx->ier_status, >> > ctx->regshift); >> > >> >>From my understanding this should trigger an irq for data to be put in >> >>rt_16550_tx_interrupt(). >> > It seems this is not always the case. Moreover, TX interrupt seem to be >> > triggered by a new RX interrupt. > The TX interrupt will be enabled as long as there are chars to send, > IIRC. The isr the puts the chars into the FIFO and triggers the xfer. Finally, I find out that UART was in sleep mode. According to omap's reference manual, it enters this mode when conditions are met: rx line is idle, tx fifo and shift register are empty, rx fifo is empty no interrupts pending
One solution that i've tested successfully is to disable sleep mode in rt_16550_open(). TX interrupts are then being triggered as expected. > >> > >> > Would you have an explanation for such a behavior? >> > I'm not sure how to solve this. > Depending on the hardware/uart revision, you may need to take care of > other quirks, see: > > http://lxr.linux.no/#linux+v3.2.1/arch/arm/mach-omap2/serial.c#L742 Thank you for this link! It helps handle the fifo full condition. However, I noticed a strange value regarding version register. My omap3530 reports 0x46? Linux serial driver assume this bug is present on revision >= 0x52 ... But my target freeze when I send more than 16 chars at once (Fifo full without errata handling). It works when using errata handling. > > Wolfgang. > You'll find attached a patch that works for me. Please advise. Maybe we can enhance it and push it? Thanks! Regards, Fabrice
>From 7245af5003d4fa79823b85d982e975a0c9dc363d Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier <fabrice.gasn...@cenosys.com> Date: Thu, 19 Jan 2012 17:58:58 +0100 Subject: [PATCH] add omap3 and omap4 uart support to xeno_16550A Add regshift module parameter. Add omap specific "fifo full" errata handling from linux-3.2 omap-serial Signed-off-by: Fabrice Gasnier <fabrice.gasn...@cenosys.com> --- ksrc/drivers/serial/16550A.c | 130 +++++++++++++++++++++++++------------ ksrc/drivers/serial/16550A_io.h | 136 +++++++++++++++++++++++++++++++++++++- 2 files changed, 219 insertions(+), 47 deletions(-) diff --git a/ksrc/drivers/serial/16550A.c b/ksrc/drivers/serial/16550A.c index 3672539..81c7b70 100644 --- a/ksrc/drivers/serial/16550A.c +++ b/ksrc/drivers/serial/16550A.c @@ -70,6 +70,23 @@ #define LSR 5 /* Line Status Register */ #define MSR 6 /* Modem Status Register */ +#if defined (CONFIG_ARCH_OMAP3) || \ + defined (CONFIG_ARCH_OMAP4) +#define OMAP_SCR 0x10 /* Supplementary control register */ +#define OMAP_MVR 0x14 /* Module Version Register */ +#define OMAP_SYSC 0x15 /* System configuration register */ +#define OMAP_WER 0x17 /* Wake-up enable register */ +#define REGION_MAX OMAP_WER /* end of io/memory region */ + +#define LCR_CONF_MODE_A LCR_DLAB /* Configutation mode A */ +#define LCR_CONF_MODE_B 0xBF /* Configutation mode B */ +#define EFR 2 /* Enhanced feature register (when LCR_CONF_MODE_B) */ +#define EFR_ECB 0x10 /* Enhanced control bit */ +#define IERX_SLEEP 0x10 /* Enable sleep mode */ +#else +#define REGION_MAX MSR /* end of io/memory region */ +#endif + struct rt_16550_context { struct rtser_config config; /* current device configuration */ @@ -80,6 +97,8 @@ struct rt_16550_context { #ifdef CONFIG_XENO_DRIVERS_16550A_ANY int io_mode; /* hardware IO-access mode */ #endif + unsigned char regshift; /* register shift */ + int tx_fifo; /* cached global tx_fifo[<device>] */ int in_head; /* RX ring buffer, head pointer */ @@ -149,13 +168,14 @@ static inline int rt_16550_rx_interrupt(struct rt_16550_context *ctx, uint64_t * timestamp) { unsigned long base = ctx->base_addr; + unsigned char regshift = ctx->regshift; int mode = rt_16550_io_mode_from_ctx(ctx); int rbytes = 0; int lsr = 0; int c; do { - c = rt_16550_reg_in(mode, base, RHR); /* read input char */ + c = rt_16550_reg_in(mode, base, regshift, RHR); /* read input char */ ctx->in_buf[ctx->in_tail] = c; if (ctx->in_history) @@ -169,7 +189,7 @@ static inline int rt_16550_rx_interrupt(struct rt_16550_context *ctx, rbytes++; lsr &= ~RTSER_LSR_DATA; - lsr |= (rt_16550_reg_in(mode, base, LSR) & + lsr |= (rt_16550_reg_in(mode, base, regshift, LSR) & (RTSER_LSR_DATA | RTSER_LSR_OVERRUN_ERR | RTSER_LSR_PARITY_ERR | RTSER_LSR_FRAMING_ERR | RTSER_LSR_BREAK_IND)); @@ -196,6 +216,7 @@ static inline void rt_16550_tx_interrupt(struct rt_16550_context *ctx) int c; int count; unsigned long base = ctx->base_addr; + unsigned char regshift = ctx->regshift; int mode = rt_16550_io_mode_from_ctx(ctx); /* if (uart->modem & MSR_CTS)*/ @@ -204,7 +225,7 @@ static inline void rt_16550_tx_interrupt(struct rt_16550_context *ctx) (count > 0) && (ctx->out_npend > 0); count--, ctx->out_npend--) { c = ctx->out_buf[ctx->out_head++]; - rt_16550_reg_out(mode, base, THR, c); + rt_16550_reg_out(mode, base, regshift, THR, c); ctx->out_head &= (OUT_BUFFER_SIZE - 1); } } @@ -213,9 +234,10 @@ static inline void rt_16550_tx_interrupt(struct rt_16550_context *ctx) static inline void rt_16550_stat_interrupt(struct rt_16550_context *ctx) { unsigned long base = ctx->base_addr; + unsigned char regshift = ctx->regshift; int mode = rt_16550_io_mode_from_ctx(ctx); - ctx->status |= (rt_16550_reg_in(mode, base, LSR) & + ctx->status |= (rt_16550_reg_in(mode, base, regshift, LSR) & (RTSER_LSR_OVERRUN_ERR | RTSER_LSR_PARITY_ERR | RTSER_LSR_FRAMING_ERR | RTSER_LSR_BREAK_IND)); } @@ -224,6 +246,7 @@ static int rt_16550_interrupt(rtdm_irq_t * irq_context) { struct rt_16550_context *ctx; unsigned long base; + unsigned char regshift; int mode; int iir; uint64_t timestamp = rtdm_clock_read(); @@ -234,12 +257,13 @@ static int rt_16550_interrupt(rtdm_irq_t * irq_context) ctx = rtdm_irq_get_arg(irq_context, struct rt_16550_context); base = ctx->base_addr; + regshift = ctx->regshift; mode = rt_16550_io_mode_from_ctx(ctx); rtdm_lock_get(&ctx->lock); while (1) { - iir = rt_16550_reg_in(mode, base, IIR) & IIR_MASK; + iir = rt_16550_reg_in(mode, base, regshift, IIR) & IIR_MASK; if (testbits(iir, IIR_PIRQ)) break; @@ -251,7 +275,7 @@ static int rt_16550_interrupt(rtdm_irq_t * irq_context) else if (iir == IIR_TX) rt_16550_tx_interrupt(ctx); else if (iir == IIR_MODEM) { - modem = rt_16550_reg_in(mode, base, MSR); + modem = rt_16550_reg_in(mode, base, regshift, MSR); if (modem & (modem << 4)) events |= RTSER_EVENT_MODEMHI; if ((modem ^ 0xF0) & (modem << 4)) @@ -292,7 +316,7 @@ static int rt_16550_interrupt(rtdm_irq_t * irq_context) } /* update interrupt mask */ - rt_16550_reg_out(mode, base, IER, ctx->ier_status); + rt_16550_reg_out(mode, base, regshift, IER, ctx->ier_status); rtdm_lock_put(&ctx->lock); @@ -305,6 +329,7 @@ static int rt_16550_set_config(struct rt_16550_context *ctx, { rtdm_lockctx_t lock_ctx; unsigned long base = ctx->base_addr; + unsigned char regshift = ctx->regshift; int mode = rt_16550_io_mode_from_ctx(ctx); int err = 0; @@ -320,9 +345,9 @@ static int rt_16550_set_config(struct rt_16550_context *ctx, ctx->config.baud_rate = config->baud_rate; baud_div = (baud_base[dev_id] + (ctx->config.baud_rate>>1)) / ctx->config.baud_rate; - rt_16550_reg_out(mode, base, LCR, LCR_DLAB); - rt_16550_reg_out(mode, base, DLL, baud_div & 0xff); - rt_16550_reg_out(mode, base, DLM, baud_div >> 8); + rt_16550_reg_out(mode, base, regshift, LCR, LCR_DLAB); + rt_16550_reg_out(mode, base, regshift, DLL, baud_div & 0xff); + rt_16550_reg_out(mode, base, regshift, DLM, baud_div >> 8); } if (testbits(config->config_mask, RTSER_SET_PARITY)) @@ -336,7 +361,7 @@ static int rt_16550_set_config(struct rt_16550_context *ctx, RTSER_SET_DATA_BITS | RTSER_SET_STOP_BITS | RTSER_SET_BAUD)) { - rt_16550_reg_out(mode, base, LCR, + rt_16550_reg_out(mode, base, regshift, LCR, (ctx->config.parity << 3) | (ctx->config.stop_bits << 2) | ctx->config.data_bits); @@ -346,9 +371,9 @@ static int rt_16550_set_config(struct rt_16550_context *ctx, if (testbits(config->config_mask, RTSER_SET_FIFO_DEPTH)) { ctx->config.fifo_depth = config->fifo_depth & FIFO_MASK; - rt_16550_reg_out(mode, base, FCR, + rt_16550_reg_out(mode, base, regshift, FCR, FCR_FIFO | FCR_RESET_RX | FCR_RESET_TX); - rt_16550_reg_out(mode, base, FCR, + rt_16550_reg_out(mode, base, regshift, FCR, FCR_FIFO | ctx->config.fifo_depth); } @@ -405,7 +430,7 @@ static int rt_16550_set_config(struct rt_16550_context *ctx, else /* disable modem status interrupt */ ctx->ier_status &= ~IER_MODEM; - rt_16550_reg_out(mode, base, IER, ctx->ier_status); + rt_16550_reg_out(mode, base, regshift, IER, ctx->ier_status); rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx); } @@ -425,7 +450,7 @@ static int rt_16550_set_config(struct rt_16550_context *ctx, RTSER_MCR_DTR | RTSER_MCR_RTS | RTSER_MCR_OUT2; break; } - rt_16550_reg_out(mode, base, MCR, ctx->mcr_status); + rt_16550_reg_out(mode, base, regshift, MCR, ctx->mcr_status); rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx); } @@ -449,6 +474,10 @@ int rt_16550_open(struct rtdm_dev_context *context, int err; uint64_t *dummy; rtdm_lockctx_t lock_ctx; +#if defined (CONFIG_ARCH_OMAP3) || \ + defined (CONFIG_ARCH_OMAP4) + int ier; +#endif ctx = (struct rt_16550_context *)context->dev_private; @@ -487,19 +516,28 @@ int rt_16550_open(struct rtdm_dev_context *context, if (err) { /* reset DTR and RTS */ rt_16550_reg_out(rt_16550_io_mode_from_ctx(ctx), ctx->base_addr, - MCR, 0); + ctx->regshift, MCR, 0); rt_16550_cleanup_ctx(ctx); return err; } +#if defined (CONFIG_ARCH_OMAP3) || \ + defined (CONFIG_ARCH_OMAP4) + ier = rt_16550_reg_in(rt_16550_io_mode_from_ctx(ctx), + ctx->base_addr, ctx->regshift, IER); + if (ier & IERX_SLEEP) + rt_16550_disable_sleep(rt_16550_io_mode_from_ctx(ctx), ctx->base_addr, + ctx->regshift); +#endif + rtdm_lock_get_irqsave(&ctx->lock, lock_ctx); /* enable interrupts */ ctx->ier_status = IER_RX; - rt_16550_reg_out(rt_16550_io_mode_from_ctx(ctx), ctx->base_addr, IER, - IER_RX); + rt_16550_reg_out(rt_16550_io_mode_from_ctx(ctx), ctx->base_addr, + ctx->regshift, IER, IER_RX); rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx); @@ -511,25 +549,27 @@ int rt_16550_close(struct rtdm_dev_context *context, { struct rt_16550_context *ctx; unsigned long base; + unsigned char regshift; int mode; uint64_t *in_history; rtdm_lockctx_t lock_ctx; ctx = (struct rt_16550_context *)context->dev_private; base = ctx->base_addr; + regshift = ctx->regshift; mode = rt_16550_io_mode_from_ctx(ctx); rtdm_lock_get_irqsave(&ctx->lock, lock_ctx); /* reset DTR and RTS */ - rt_16550_reg_out(mode, base, MCR, 0); + rt_16550_reg_out(mode, base, regshift, MCR, 0); /* mask all UART interrupts and clear pending ones. */ - rt_16550_reg_out(mode, base, IER, 0); - rt_16550_reg_in(mode, base, IIR); - rt_16550_reg_in(mode, base, LSR); - rt_16550_reg_in(mode, base, RHR); - rt_16550_reg_in(mode, base, MSR); + rt_16550_reg_out(mode, base, regshift, IER, 0); + rt_16550_reg_in(mode, base, regshift, IIR); + rt_16550_reg_in(mode, base, regshift, LSR); + rt_16550_reg_in(mode, base, regshift, RHR); + rt_16550_reg_in(mode, base, regshift, MSR); in_history = ctx->in_history; ctx->in_history = NULL; @@ -553,10 +593,12 @@ int rt_16550_ioctl(struct rtdm_dev_context *context, struct rt_16550_context *ctx; int err = 0; unsigned long base; + unsigned char regshift; int mode; ctx = (struct rt_16550_context *)context->dev_private; base = ctx->base_addr; + regshift = ctx->regshift; mode = rt_16550_io_mode_from_ctx(ctx); switch (request) { @@ -638,9 +680,9 @@ int rt_16550_ioctl(struct rtdm_dev_context *context, struct rtser_status status_buf; status_buf.line_status = - rt_16550_reg_in(mode, base, LSR) | status; + rt_16550_reg_in(mode, base, regshift, LSR) | status; status_buf.modem_status = - rt_16550_reg_in(mode, base, MSR); + rt_16550_reg_in(mode, base, regshift, MSR); err = rtdm_safe_copy_to_user(user_info, arg, @@ -649,9 +691,9 @@ int rt_16550_ioctl(struct rtdm_dev_context *context, rtser_status)); } else { ((struct rtser_status *)arg)->line_status = - rt_16550_reg_in(mode, base, LSR) | status; + rt_16550_reg_in(mode, base, regshift, LSR) | status; ((struct rtser_status *)arg)->modem_status = - rt_16550_reg_in(mode, base, MSR); + rt_16550_reg_in(mode, base, regshift, MSR); } break; } @@ -672,7 +714,7 @@ int rt_16550_ioctl(struct rtdm_dev_context *context, rtdm_lock_get_irqsave(&ctx->lock, lock_ctx); ctx->mcr_status = new_mcr; - rt_16550_reg_out(mode, base, MCR, new_mcr); + rt_16550_reg_out(mode, base, regshift, MCR, new_mcr); rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx); break; } @@ -698,7 +740,7 @@ int rt_16550_ioctl(struct rtdm_dev_context *context, if (testbits(ctx->config.event_mask, RTSER_EVENT_ERRPEND)) { ctx->ier_status |= IER_STAT; - rt_16550_reg_out(mode, base, IER, + rt_16550_reg_out(mode, base, regshift, IER, ctx->ier_status); } @@ -752,7 +794,7 @@ int rt_16550_ioctl(struct rtdm_dev_context *context, (ctx->config.parity << 3) | (ctx->config.stop_bits << 2) | ctx->config.data_bits; - rt_16550_reg_out(mode, base, LCR, lcr); + rt_16550_reg_out(mode, base, regshift, LCR, lcr); rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx); break; @@ -768,7 +810,7 @@ int rt_16550_ioctl(struct rtdm_dev_context *context, ctx->in_npend = 0; ctx->status = 0; fcr |= FCR_FIFO | FCR_RESET_RX; - rt_16550_reg_in(mode, base, RHR); + rt_16550_reg_in(mode, base, regshift, RHR); } if ((long)arg & RTDM_PURGE_TX_BUFFER) { ctx->out_head = 0; @@ -777,8 +819,8 @@ int rt_16550_ioctl(struct rtdm_dev_context *context, fcr |= FCR_FIFO | FCR_RESET_TX; } if (fcr) { - rt_16550_reg_out(mode, base, FCR, fcr); - rt_16550_reg_out(mode, base, FCR, + rt_16550_reg_out(mode, base, regshift, FCR, fcr); + rt_16550_reg_out(mode, base, regshift, FCR, FCR_FIFO | ctx->config.fifo_depth); } rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx); @@ -831,8 +873,8 @@ ssize_t rt_16550_read(struct rtdm_dev_context * context, if (!testbits(ctx->ier_status, IER_STAT)) { ctx->ier_status |= IER_STAT; rt_16550_reg_out(rt_16550_io_mode_from_ctx(ctx), - ctx->base_addr, IER, - ctx->ier_status); + ctx->base_addr, ctx->regshift, + IER, ctx->ier_status); } if (ctx->status) { @@ -1046,8 +1088,8 @@ ssize_t rt_16550_write(struct rtdm_dev_context * context, /* unmask tx interrupt */ ctx->ier_status |= IER_TX; rt_16550_reg_out(rt_16550_io_mode_from_ctx(ctx), - ctx->base_addr, IER, - ctx->ier_status); + ctx->base_addr, ctx->regshift, + IER, ctx->ier_status); rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx); continue; @@ -1117,6 +1159,7 @@ int __init rt_16550_init(void) { struct rtdm_device *dev; unsigned long base; + unsigned char regshift; int mode; int err; int i; @@ -1157,11 +1200,12 @@ int __init rt_16550_init(void) /* Mask all UART interrupts and clear pending ones. */ base = rt_16550_base_addr(i); mode = rt_16550_io_mode(i); - rt_16550_reg_out(mode, base, IER, 0); - rt_16550_reg_in(mode, base, IIR); - rt_16550_reg_in(mode, base, LSR); - rt_16550_reg_in(mode, base, RHR); - rt_16550_reg_in(mode, base, MSR); + regshift = rt_16550_regshift(i); + rt_16550_reg_out(mode, base, regshift, IER, 0); + rt_16550_reg_in(mode, base, regshift, IIR); + rt_16550_reg_in(mode, base, regshift, LSR); + rt_16550_reg_in(mode, base, regshift, RHR); + rt_16550_reg_in(mode, base, regshift, MSR); err = rtdm_dev_register(dev); diff --git a/ksrc/drivers/serial/16550A_io.h b/ksrc/drivers/serial/16550A_io.h index 92d21a5..c42bdba 100644 --- a/ksrc/drivers/serial/16550A_io.h +++ b/ksrc/drivers/serial/16550A_io.h @@ -31,15 +31,25 @@ MODULE_PARM_DESC(io, "I/O port addresses of the serial devices"); defined(CONFIG_XENO_DRIVERS_16550A_ANY) static unsigned long mem[MAX_DEVICES]; static void *mapped_io[MAX_DEVICES]; +static unsigned char regshift[MAX_DEVICES]; compat_module_param_array(mem, ulong, MAX_DEVICES, 0400); +compat_module_param_array(regshift, byte, MAX_DEVICES, 0400); MODULE_PARM_DESC(mem, "I/O memory addresses of the serial devices"); +MODULE_PARM_DESC(regshift, "register shift (ex: on some omap, use regshift=2)"); #endif /* CONFIG_XENO_DRIVERS_16550A_MMIO || CONFIG_XENO_DRIVERS_16550A_ANY */ +#if defined (CONFIG_ARCH_OMAP3) || \ + defined (CONFIG_ARCH_OMAP4) +#define UART_OMAP_NO_EMPTY_FIFO_READ_IP_REV 0x46 +#define UART_ERRATA_FIFO_FULL_ABORT (0x1 << 0) +#endif + #ifdef CONFIG_XENO_DRIVERS_16550A_PIO #define RT_16550_IO_INLINE inline extern void *mapped_io[]; /* dummy */ +static unsigned char regshift[]; /* dummy */ static inline unsigned long rt_16550_addr_param(int dev_id) { @@ -61,6 +71,11 @@ static inline io_mode_t rt_16550_io_mode(int dev_id) return MODE_PIO; } +static inline unsigned char rt_16550_regshift(int dev_id) +{ + return 0; +} + static inline io_mode_t rt_16550_io_mode_from_ctx(struct rt_16550_context *ctx) { @@ -99,6 +114,11 @@ static inline io_mode_t rt_16550_io_mode(int dev_id) return MODE_MMIO; } +static inline unsigned char rt_16550_regshift(int dev_id) +{ + return (unsigned char)regshift[dev_id]; +} + static inline io_mode_t rt_16550_io_mode_from_ctx(struct rt_16550_context *ctx) { @@ -109,6 +129,7 @@ static inline void rt_16550_init_io_ctx(int dev_id, struct rt_16550_context *ctx) { ctx->base_addr = (unsigned long)mapped_io[dev_id]; + ctx->regshift = (unsigned char)regshift[dev_id]; } #elif defined(CONFIG_XENO_DRIVERS_16550A_ANY) @@ -135,6 +156,11 @@ static inline io_mode_t rt_16550_io_mode(int dev_id) return (io[dev_id]) ? MODE_PIO : MODE_MMIO; } +static inline unsigned char rt_16550_regshift(int dev_id) +{ + return (io[dev_id]) ? 0 : (unsigned char)regshift[dev_id]; +} + static inline io_mode_t rt_16550_io_mode_from_ctx(struct rt_16550_context *ctx) { @@ -151,15 +177,115 @@ rt_16550_init_io_ctx(int dev_id, struct rt_16550_context *ctx) ctx->base_addr = (unsigned long)mapped_io[dev_id]; ctx->io_mode = MODE_MMIO; } + ctx->regshift = (unsigned char)regshift[dev_id]; } #else # error Unsupported I/O access method #endif + +#if defined (CONFIG_ARCH_OMAP3) || \ + defined (CONFIG_ARCH_OMAP4) + +static RT_16550_IO_INLINE u8 +rt_16550_omap_raw_reg_in(io_mode_t io_mode, unsigned long base, unsigned char rshift, int off) +{ + off <<= rshift; /* regshift */ + switch (io_mode) { + case MODE_PIO: + return inb(base + off); + default: /* MODE_MMIO */ + return readb((void *)base + off); + } +} + +static RT_16550_IO_INLINE void +rt_16550_omap_raw_reg_out(io_mode_t io_mode, unsigned long base, unsigned char rshift, int off, u8 val) +{ + off <<= rshift; /* regshift */ + switch (io_mode) { + case MODE_PIO: + outb(val, base + off); + break; + case MODE_MMIO: + writeb(val, (void *)base + off); + break; + } +} + +static RT_16550_IO_INLINE int +rt_16550_errata(io_mode_t io_mode, unsigned long base, unsigned char rshift) +{ + int errata = 0, rev; + /* + * omap44xx, ti816x: Never read empty UART fifo + * omap3xxx: Never read empty UART fifo on UARTs + * with IP rev >=0x46 + */ + if (cpu_is_omap44xx() /* FIXME: || cpu_is_ti816x() */) + errata |= UART_ERRATA_FIFO_FULL_ABORT; + else if ((rev = rt_16550_omap_raw_reg_in(io_mode, base, rshift, OMAP_MVR)) >= UART_OMAP_NO_EMPTY_FIFO_READ_IP_REV) + errata |= UART_ERRATA_FIFO_FULL_ABORT; + + return errata; +} + +static RT_16550_IO_INLINE void +rt_16550_disable_sleep(io_mode_t io_mode, unsigned long base, unsigned char rshift) +{ + unsigned char lcr, efr, ier; + + lcr = rt_16550_omap_raw_reg_in(io_mode, base, rshift, LCR); + rt_16550_omap_raw_reg_out(io_mode, base, rshift, LCR, LCR_CONF_MODE_B); + efr = rt_16550_omap_raw_reg_in(io_mode, base, rshift, EFR); + rt_16550_omap_raw_reg_out(io_mode, base, rshift, EFR, EFR_ECB); + rt_16550_omap_raw_reg_out(io_mode, base, rshift, LCR, 0x0); /* Operational mode */ + ier = rt_16550_omap_raw_reg_in(io_mode, base, rshift, IER); + ier &= ~IERX_SLEEP; /* disable sleep */ + rt_16550_omap_raw_reg_out(io_mode, base, rshift, IER, ier); + rt_16550_omap_raw_reg_out(io_mode, base, rshift, LCR, LCR_CONF_MODE_B); + rt_16550_omap_raw_reg_out(io_mode, base, rshift, EFR, efr); + rt_16550_omap_raw_reg_out(io_mode, base, rshift, LCR, lcr); +} + +static RT_16550_IO_INLINE u8 +rt_16550_reg_in(io_mode_t io_mode, unsigned long base, unsigned char rshift, int off) +{ + if (rt_16550_errata(io_mode, base, rshift)) { + if (RHR == off) { + unsigned char lsr; + lsr = rt_16550_omap_raw_reg_in(io_mode, base, rshift, LSR); + if (!(lsr & RTSER_LSR_DATA)) /* Receiver data ready */ + return 0; /* FIXME: -EPERM should be returned as error */ + } + } + return rt_16550_omap_raw_reg_in(io_mode, base, rshift, off); +} + +static RT_16550_IO_INLINE void +rt_16550_reg_out(io_mode_t io_mode, unsigned long base, unsigned char rshift, int off, u8 val) +{ + if (rt_16550_errata(io_mode, base, rshift)) { + unsigned char lsr; + unsigned int tmout=10000; + lsr = rt_16550_omap_raw_reg_in(io_mode, base, rshift, LSR); + while (!(lsr & RTSER_LSR_THR_EMTPY)) { + /* Wait up to 10ms for the character(s) to be sent. */ + if(--tmout == 0) + break; + rtdm_task_sleep(1000); + lsr = rt_16550_omap_raw_reg_in(io_mode, base, rshift, LSR); + } + } + rt_16550_omap_raw_reg_out(io_mode, base, rshift, off, val); +} + +#else static RT_16550_IO_INLINE u8 -rt_16550_reg_in(io_mode_t io_mode, unsigned long base, int off) +rt_16550_reg_in(io_mode_t io_mode, unsigned long base, unsigned char rshift, int off) { + off <<= rshift; /* regshift */ switch (io_mode) { case MODE_PIO: return inb(base + off); @@ -169,8 +295,9 @@ rt_16550_reg_in(io_mode_t io_mode, unsigned long base, int off) } static RT_16550_IO_INLINE void -rt_16550_reg_out(io_mode_t io_mode, unsigned long base, int off, u8 val) +rt_16550_reg_out(io_mode_t io_mode, unsigned long base, unsigned char rshift, int off, u8 val) { + off <<= rshift; /* regshift */ switch (io_mode) { case MODE_PIO: outb(val, base + off); @@ -180,16 +307,17 @@ rt_16550_reg_out(io_mode_t io_mode, unsigned long base, int off, u8 val) break; } } +#endif //CONFIG_ARCH_OMAP3/4 static int rt_16550_init_io(int dev_id, char* name) { switch (rt_16550_io_mode(dev_id)) { case MODE_PIO: - if (!request_region(rt_16550_addr_param(dev_id), 8, name)) + if (!request_region(rt_16550_addr_param(dev_id), REGION_MAX, name)) return -EBUSY; break; case MODE_MMIO: - mapped_io[dev_id] = ioremap(rt_16550_addr_param(dev_id), 8); + mapped_io[dev_id] = ioremap(rt_16550_addr_param(dev_id), REGION_MAX << regshift[dev_id]); if (!mapped_io[dev_id]) return -EBUSY; break; -- 1.7.0.4
_______________________________________________ Xenomai-help mailing list Xenomai-help@gna.org https://mail.gna.org/listinfo/xenomai-help