Module: xenomai-2.6
Branch: master
Commit: 232656af06c480ab0e1a195ae3d3c07ed341a42c
URL:    
http://git.xenomai.org/?p=xenomai-2.6.git;a=commit;h=232656af06c480ab0e1a195ae3d3c07ed341a42c

Author: Wolfgang Grandegger <w...@denx.de>
Date:   Mon Nov 21 10:06:01 2011 +0100

serial: add driver for the MPC52xx PSC UARTs

This driver tries to fake the ioctl interface of the 16650A as good
as possible. The device gets the name "rtserPSCx" where "x" is the
sequential PSC device number similar to the linux MPC52xx UART driver
(using ttyPSCx). You may need to unbind the Linux serial driver first
e.g. for PSC3:

  -bash-3.2# echo f0002400.serial \
  > /sys/devices/f0000000.soc5200/f0002400.serial/driver/unbind

This driver also supports the recently introduced RTSER_EVENT_TXEMPTY
event and the RS485 mode.

Signed-off-by: Wolfgang Grandegger <w...@denx.de>

---

 ksrc/drivers/serial/Kconfig           |    6 +
 ksrc/drivers/serial/Makefile          |    2 +
 ksrc/drivers/serial/rt_mpc52xx_uart.c | 1462 +++++++++++++++++++++++++++++++++
 3 files changed, 1470 insertions(+), 0 deletions(-)

diff --git a/ksrc/drivers/serial/Kconfig b/ksrc/drivers/serial/Kconfig
index 1c76820..edb4dfd 100644
--- a/ksrc/drivers/serial/Kconfig
+++ b/ksrc/drivers/serial/Kconfig
@@ -63,4 +63,10 @@ config XENO_DRIVERS_16550A_PCI_MOXA
          CP-132U, CP-134U, CP-138U
          CP-168U
 
+config XENO_DRIVERS_MPC52XX_UART
+       depends on XENO_SKIN_RTDM && PPC_MPC52xx
+       tristate "MPC52xx PSC UART driver"
+       help
+       Real-time UART driver for the PSC on the MPC5200 processor.
+
 endmenu
diff --git a/ksrc/drivers/serial/Makefile b/ksrc/drivers/serial/Makefile
index 144bf8b..5703907 100644
--- a/ksrc/drivers/serial/Makefile
+++ b/ksrc/drivers/serial/Makefile
@@ -5,8 +5,10 @@ ifneq ($(VERSION).$(PATCHLEVEL),2.4)
 EXTRA_CFLAGS += -D__IN_XENOMAI__ -Iinclude/xenomai
 
 obj-$(CONFIG_XENO_DRIVERS_16550A) += xeno_16550A.o
+obj-$(CONFIG_XENO_DRIVERS_MPC52XX_UART) += xeno_mpc52xx_uart.o
 
 xeno_16550A-y := 16550A.o
+xeno_mpc52xx_uart-y := rt_mpc52xx_uart.o
 
 else
 
diff --git a/ksrc/drivers/serial/rt_mpc52xx_uart.c 
b/ksrc/drivers/serial/rt_mpc52xx_uart.c
new file mode 100644
index 0000000..8f99910
--- /dev/null
+++ b/ksrc/drivers/serial/rt_mpc52xx_uart.c
@@ -0,0 +1,1462 @@
+/*
+ * Copyright (C) 2011 Wolfgang Grandegger <w...@denx.de>.
+ * Copyright (C) 2005-2007 Jan Kiszka <jan.kis...@web.de>.
+ *
+ * Xenomai is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Xenomai is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Xenomai; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+
+#include <asm/mpc52xx.h>
+#include <asm/mpc52xx_psc.h>
+
+#include <rtdm/rtserial.h>
+#include <rtdm/rtdm_driver.h>
+
+MODULE_AUTHOR("Wolfgang Grandegger <w...@denx.de>");
+MODULE_LICENSE("GPL");
+
+#define RT_MPC52XX_UART_DRVNAM "xeno_mpc52xx_uart"
+
+#define IN_BUFFER_SIZE         512
+#define OUT_BUFFER_SIZE                512
+
+#define PARITY_MASK            0x03
+#define DATA_BITS_MASK         0x03
+#define STOP_BITS_MASK         0x01
+#define FIFO_MASK              0xC0
+#define EVENT_MASK             0x0F
+
+
+struct rt_mpc52xx_uart_port {
+       const struct device *dev;
+       struct mpc52xx_psc __iomem *psc;
+       struct mpc52xx_psc_fifo __iomem *fifo;
+       unsigned int uartclk;
+       int irq;
+       int num;
+};
+
+struct rt_mpc52xx_uart_ctx {
+       struct rtser_config config;     /* current device configuration */
+
+       rtdm_irq_t irq_handle;          /* device IRQ handle */
+       rtdm_lock_t lock;               /* lock to protect context struct */
+
+       int in_head;                    /* RX ring buffer, head pointer */
+       int in_tail;                    /* RX ring buffer, tail pointer */
+       size_t in_npend;                /* pending bytes in RX ring */
+       int in_nwait;                   /* bytes the user waits for */
+       rtdm_event_t in_event;          /* raised to unblock reader */
+       char in_buf[IN_BUFFER_SIZE];    /* RX ring buffer */
+       volatile unsigned long in_lock; /* single-reader lock */
+       uint64_t *in_history;           /* RX timestamp buffer */
+
+       int out_head;                   /* TX ring buffer, head pointer */
+       int out_tail;                   /* TX ring buffer, tail pointer */
+       size_t out_npend;               /* pending bytes in TX ring */
+       rtdm_event_t out_event;         /* raised to unblock writer */
+       char out_buf[OUT_BUFFER_SIZE];  /* TX ring buffer */
+       rtdm_mutex_t out_lock;          /* single-writer mutex */
+
+       uint64_t last_timestamp;        /* timestamp of last event */
+       int ioc_events;                 /* recorded events */
+       rtdm_event_t ioc_event;         /* raised to unblock event waiter */
+       volatile unsigned long ioc_event_lock;  /* single-waiter lock */
+
+
+       int mcr_status;                 /* emulated MCR cache */
+       int status;                     /* cache for LSR + soft-states */
+       int saved_errors;               /* error cache for RTIOC_GET_STATUS */
+
+       unsigned int imr_status;        /* interrupt mask register cache */
+       int tx_empty;                   /* shift register empty flag */
+
+       struct rt_mpc52xx_uart_port *port; /* Port related data */
+};
+
+static const struct rtser_config default_config = {
+       .config_mask = 0xFFFF,
+       .baud_rate = RTSER_DEF_BAUD,
+       .parity = RTSER_DEF_PARITY,
+       .data_bits = RTSER_DEF_BITS,
+       .stop_bits = RTSER_DEF_STOPB,
+       .handshake = RTSER_DEF_HAND,
+       .fifo_depth = RTSER_DEF_FIFO_DEPTH,
+       .rx_timeout = RTSER_DEF_TIMEOUT,
+       .tx_timeout = RTSER_DEF_TIMEOUT,
+       .event_timeout = RTSER_DEF_TIMEOUT,
+       .timestamp_history = RTSER_DEF_TIMESTAMP_HISTORY,
+       .event_mask = RTSER_DEF_EVENT_MASK,
+       .rs485 = RTSER_DEF_RS485,
+};
+
+/* lookup table for matching device nodes to index numbers */
+static struct device_node *rt_mpc52xx_uart_nodes[MPC52xx_PSC_MAXNUM];
+
+static inline void psc_fifo_init(struct rt_mpc52xx_uart_ctx *ctx)
+{
+       out_8(&ctx->port->fifo->rfcntl, 0x00);
+       out_be16(&ctx->port->fifo->rfalarm, 0x1ff);
+       out_8(&ctx->port->fifo->tfcntl, 0x07);
+       out_be16(&ctx->port->fifo->tfalarm, 0x80);
+}
+
+static inline int psc_raw_rx_rdy(struct rt_mpc52xx_uart_ctx *ctx)
+{
+       return in_be16(&ctx->port->psc->mpc52xx_psc_status) &
+               MPC52xx_PSC_SR_RXRDY;
+}
+
+static inline int psc_raw_tx_rdy(struct rt_mpc52xx_uart_ctx *ctx)
+{
+       return in_be16(&ctx->port->psc->mpc52xx_psc_status) &
+               MPC52xx_PSC_SR_TXRDY;
+}
+
+static inline int psc_rx_rdy(struct rt_mpc52xx_uart_ctx *ctx)
+{
+       return in_be16(&ctx->port->psc->mpc52xx_psc_isr) &
+               ctx->imr_status & MPC52xx_PSC_IMR_RXRDY;
+}
+
+static int psc_tx_rdy(struct rt_mpc52xx_uart_ctx *ctx)
+{
+       return in_be16(&ctx->port->psc->mpc52xx_psc_isr) &
+               ctx->imr_status & MPC52xx_PSC_IMR_TXRDY;
+}
+
+static inline int psc_tx_empty(struct rt_mpc52xx_uart_ctx *ctx)
+{
+       return in_be16(&ctx->port->psc->mpc52xx_psc_status) &
+               MPC52xx_PSC_SR_TXEMP;
+}
+
+static inline void psc_start_tx(struct rt_mpc52xx_uart_ctx *ctx)
+{
+       ctx->imr_status |= MPC52xx_PSC_IMR_TXRDY;
+       out_be16(&ctx->port->psc->mpc52xx_psc_imr, ctx->imr_status);
+}
+
+static inline void psc_stop_tx(struct rt_mpc52xx_uart_ctx *ctx)
+{
+       ctx->imr_status &= ~MPC52xx_PSC_IMR_TXRDY;
+       out_be16(&ctx->port->psc->mpc52xx_psc_imr, ctx->imr_status);
+}
+
+static inline void psc_stop_rx(struct rt_mpc52xx_uart_ctx *ctx)
+{
+       ctx->imr_status &= ~MPC52xx_PSC_IMR_RXRDY;
+       out_be16(&ctx->port->psc->mpc52xx_psc_imr, ctx->imr_status);
+}
+
+static inline void psc_write_char(struct rt_mpc52xx_uart_ctx *ctx,
+                                 unsigned char c)
+{
+       out_8(&ctx->port->psc->mpc52xx_psc_buffer_8, c);
+}
+
+static inline unsigned char psc_read_char(struct rt_mpc52xx_uart_ctx *ctx)
+{
+       return in_8(&ctx->port->psc->mpc52xx_psc_buffer_8);
+}
+
+static inline void psc_disable_ints(struct rt_mpc52xx_uart_ctx *ctx)
+{
+       ctx->imr_status = 0;
+       out_be16(&ctx->port->psc->mpc52xx_psc_imr, ctx->imr_status);
+}
+
+static void psc_set_mcr(struct rt_mpc52xx_uart_ctx *ctx,
+                       unsigned int mcr)
+{
+       if (mcr & RTSER_MCR_RTS)
+               out_8(&ctx->port->psc->op1, MPC52xx_PSC_OP_RTS);
+       else
+               out_8(&ctx->port->psc->op0, MPC52xx_PSC_OP_RTS);
+}
+
+/* FIXME: status interrupts not yet handled properly */
+static unsigned int psc_get_msr(struct rt_mpc52xx_uart_ctx *ctx)
+{
+       unsigned int msr = RTSER_MSR_DSR;
+       u8 status = in_8(&ctx->port->psc->mpc52xx_psc_ipcr);
+
+       if (!(status & MPC52xx_PSC_CTS))
+               msr |= RTSER_MSR_CTS;
+       if (!(status & MPC52xx_PSC_DCD))
+               msr |= RTSER_MSR_DCD;
+
+       return msr;
+}
+
+static void psc_enable_ms(struct rt_mpc52xx_uart_ctx *ctx)
+{
+       struct mpc52xx_psc *psc = ctx->port->psc;
+
+       /* clear D_*-bits by reading them */
+       in_8(&psc->mpc52xx_psc_ipcr);
+       /* enable CTS and DCD as IPC interrupts */
+       out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD);
+
+       ctx->imr_status |= MPC52xx_PSC_IMR_IPC;
+       out_be16(&psc->mpc52xx_psc_imr, ctx->imr_status);
+}
+
+static void psc_disable_ms(struct rt_mpc52xx_uart_ctx *ctx)
+{
+       struct mpc52xx_psc *psc = ctx->port->psc;
+
+       /* disable CTS and DCD as IPC interrupts */
+       out_8(&psc->mpc52xx_psc_acr, 0);
+
+       ctx->imr_status &= ~MPC52xx_PSC_IMR_IPC;
+       out_be16(&psc->mpc52xx_psc_imr, ctx->imr_status);
+}
+
+static struct of_device_id mpc5200_gpio_ids[] = {
+       { .compatible = "fsl,mpc5200-gpio", },
+       { .compatible = "mpc5200-gpio", },
+       {}
+};
+
+static void __devinit rt_mpc52xx_uart_init_hw(struct rt_mpc52xx_uart_port 
*port)
+{
+       struct mpc52xx_gpio __iomem *gpio;
+       struct device_node *gpio_np;
+       u32 port_config;
+
+       if (port->num == 6) {
+               gpio_np = of_find_matching_node(NULL, mpc5200_gpio_ids);
+               gpio = of_iomap(gpio_np, 0);
+               of_node_put(gpio_np);
+               if (!gpio) {
+                       dev_err(port->dev, "PSC%d port_config: "
+                               "couldn't map gpio ids\n", port->num);
+                       return;
+               }
+               port_config = in_be32(&gpio->port_config);
+               port_config &= 0xFF0FFFFF; /* port config for PSC6 */
+               port_config |= 0x00500000;
+               dev_dbg(port->dev, "PSC%d port_config: old:%x new:%x\n",
+                       port->num, in_be32(&gpio->port_config), port_config);
+               out_be32(&gpio->port_config, port_config);
+               iounmap(gpio);
+       }
+}
+
+static inline void rt_mpc52xx_uart_put_char(struct rt_mpc52xx_uart_ctx *ctx,
+                                           uint64_t *timestamp,
+                                           unsigned char ch)
+{
+       ctx->in_buf[ctx->in_tail] = ch;
+       if (ctx->in_history)
+               ctx->in_history[ctx->in_tail] = *timestamp;
+       ctx->in_tail = (ctx->in_tail + 1) & (IN_BUFFER_SIZE - 1);
+
+       if (++ctx->in_npend > IN_BUFFER_SIZE) {
+               ctx->status |= RTSER_SOFT_OVERRUN_ERR;
+               ctx->in_npend--;
+       }
+}
+
+static inline int rt_mpc52xx_uart_rx_interrupt(struct rt_mpc52xx_uart_ctx *ctx,
+                                              uint64_t *timestamp)
+{
+       int rbytes = 0;
+       int psc_status;
+
+       psc_status = in_be16(&ctx->port->psc->mpc52xx_psc_status);
+       while (testbits(psc_status, MPC52xx_PSC_SR_RXRDY)) {
+               /* read input character */
+               rt_mpc52xx_uart_put_char(ctx, timestamp, psc_read_char(ctx));
+               rbytes++;
+
+               /* save new errors */
+               if (psc_status & (MPC52xx_PSC_SR_OE | MPC52xx_PSC_SR_PE |
+                                 MPC52xx_PSC_SR_FE | MPC52xx_PSC_SR_RB)) {
+                       if (psc_status & MPC52xx_PSC_SR_PE)
+                               ctx->status |= RTSER_LSR_PARITY_ERR;
+                       if (psc_status & MPC52xx_PSC_SR_FE)
+                               ctx->status |= RTSER_LSR_FRAMING_ERR;
+                       if (psc_status & MPC52xx_PSC_SR_RB)
+                               ctx->status |= RTSER_LSR_BREAK_IND;
+
+                       /*
+                        * Overrun is special, since it's reported
+                        * immediately, and doesn't affect the current
+                        * character.
+                        */
+                       if (psc_status & MPC52xx_PSC_SR_OE) {
+                               ctx->status |= RTSER_LSR_OVERRUN_ERR;
+                               rt_mpc52xx_uart_put_char(ctx, timestamp, 0);
+                               rbytes++;
+                       }
+
+                       /* Clear error condition */
+                       out_8(&ctx->port->psc->command,
+                             MPC52xx_PSC_RST_ERR_STAT);
+               }
+
+               psc_status = in_be16(&ctx->port->psc->mpc52xx_psc_status);
+       };
+
+       return rbytes;
+}
+
+static inline int rt_mpc52xx_uart_tx_interrupt(struct rt_mpc52xx_uart_ctx *ctx)
+{
+       while (psc_raw_tx_rdy(ctx) && (ctx->out_npend > 0)) {
+               if (ctx->config.rs485 &&
+                   !testbits(ctx->mcr_status, RTSER_MCR_RTS)) {
+                       /* switch RTS */
+                       ctx->mcr_status |= RTSER_MCR_RTS;
+                       dev_dbg(ctx->port->dev, "Set RTS, mcr_status=%#x\n",
+                               ctx->mcr_status);
+                       psc_set_mcr(ctx, ctx->mcr_status);
+               }
+               if ((ctx->config.rs485 ||
+                    testbits(ctx->config.event_mask, RTSER_EVENT_TXEMPTY)) &&
+                    !testbits(ctx->imr_status, MPC52xx_PSC_IMR_TXEMP)) {
+                       /* enable tx-empty interrupt */
+                       ctx->imr_status |= MPC52xx_PSC_IMR_TXEMP;
+                       dev_dbg(ctx->port->dev, "Enable TXEMP interrupt, "
+                               "imr_status=%#x\n", ctx->imr_status);
+                       out_be16(&ctx->port->psc->mpc52xx_psc_imr,
+                                ctx->imr_status);
+               }
+
+               psc_write_char(ctx, ctx->out_buf[ctx->out_head++]);
+               ctx->out_head &= OUT_BUFFER_SIZE - 1;
+               ctx->out_npend--;
+       }
+
+       return ctx->out_npend;
+}
+
+static int rt_mpc52xx_uart_interrupt(rtdm_irq_t *irq_context)
+{
+       struct rt_mpc52xx_uart_ctx *ctx;
+       uint64_t timestamp = rtdm_clock_read();
+       int rbytes = 0;
+       int events = 0;
+       int ret = RTDM_IRQ_NONE;
+       int goon = 1;
+       int n;
+
+       ctx = rtdm_irq_get_arg(irq_context, struct rt_mpc52xx_uart_ctx);
+
+       rtdm_lock_get(&ctx->lock);
+
+       while (goon) {
+               goon = 0;
+               if (psc_rx_rdy(ctx)) {
+                       dev_dbg(ctx->port->dev, "RX interrupt\n");
+                       n = rt_mpc52xx_uart_rx_interrupt(ctx, &timestamp);
+                       if (n) {
+                               rbytes += n;
+                               events |= RTSER_EVENT_RXPEND;
+                       }
+               }
+               if (psc_tx_rdy(ctx))
+                       goon |= rt_mpc52xx_uart_tx_interrupt(ctx);
+
+               if (psc_tx_empty(ctx)) {
+                       if (ctx->config.rs485 &&
+                           testbits(ctx->mcr_status, RTSER_MCR_RTS)) {
+                               /* reset RTS */
+                               ctx->mcr_status &= ~RTSER_MCR_RTS;
+                               dev_dbg(ctx->port->dev, "Reset RTS, "
+                                       "mcr_status=%#x\n", ctx->mcr_status);
+                               psc_set_mcr(ctx, ctx->mcr_status);
+                       }
+                       /* disable tx-empty interrupt */
+                       ctx->imr_status &= ~MPC52xx_PSC_IMR_TXEMP;
+                       dev_dbg(ctx->port->dev, "Disable TXEMP interrupt, "
+                               "imr_status=%#x\n", ctx->imr_status);
+                       out_be16(&ctx->port->psc->mpc52xx_psc_imr,
+                                ctx->imr_status);
+
+                       events |= RTSER_EVENT_TXEMPTY;
+                       ctx->tx_empty = 1;
+               }
+
+               if (ctx->config.event_mask &
+                   (RTSER_EVENT_MODEMHI | RTSER_EVENT_MODEMLO)) {
+                       u8 status = in_8(&ctx->port->psc->mpc52xx_psc_ipcr);
+
+                       if (status & MPC52xx_PSC_D_DCD)
+                               events |= (status & MPC52xx_PSC_DCD) ?
+                                       RTSER_EVENT_MODEMLO :
+                                       RTSER_EVENT_MODEMHI;
+                       if (status & MPC52xx_PSC_D_CTS)
+                               events |= (status & MPC52xx_PSC_CTS) ?
+                                       RTSER_EVENT_MODEMLO :
+                                       RTSER_EVENT_MODEMHI;
+                       dev_dbg(ctx->port->dev, "Modem line changed, "
+                               "events=%#x\n", events);
+               }
+
+               ret = RTDM_IRQ_HANDLED;
+       }
+
+       if (ctx->in_nwait > 0) {
+               if ((ctx->in_nwait <= rbytes) || ctx->status) {
+                       ctx->in_nwait = 0;
+                       rtdm_event_signal(&ctx->in_event);
+               } else
+                       ctx->in_nwait -= rbytes;
+       }
+
+       if (ctx->status)
+               events |= RTSER_EVENT_ERRPEND;
+
+       if (testbits(events, ctx->config.event_mask)) {
+               int old_events = ctx->ioc_events;
+
+               ctx->last_timestamp = timestamp;
+               ctx->ioc_events = events;
+
+               if (!old_events)
+                       rtdm_event_signal(&ctx->ioc_event);
+       }
+
+       if (testbits(ctx->imr_status, MPC52xx_PSC_IMR_TXRDY) &&
+           (ctx->out_npend == 0)) {
+               psc_stop_tx(ctx);
+               rtdm_event_signal(&ctx->out_event);
+       }
+
+       rtdm_lock_put(&ctx->lock);
+
+       return ret;
+}
+
+
+static int rt_mpc52xx_uart_set_config(struct rt_mpc52xx_uart_ctx *ctx,
+                                     const struct rtser_config *config,
+                                     uint64_t **in_history_ptr)
+{
+       rtdm_lockctx_t lock_ctx;
+       int err = 0;
+
+       /* make line configuration atomic and IRQ-safe */
+       rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
+
+       if (testbits(config->config_mask, RTSER_SET_BAUD))
+               ctx->config.baud_rate = config->baud_rate;
+       if (testbits(config->config_mask, RTSER_SET_PARITY))
+               ctx->config.parity = config->parity & PARITY_MASK;
+       if (testbits(config->config_mask, RTSER_SET_DATA_BITS))
+               ctx->config.data_bits = config->data_bits & DATA_BITS_MASK;
+       if (testbits(config->config_mask, RTSER_SET_STOP_BITS))
+               ctx->config.stop_bits = config->stop_bits & STOP_BITS_MASK;
+       if (testbits(config->config_mask, RTSER_SET_HANDSHAKE))
+               ctx->config.handshake = config->handshake;
+
+       if (testbits(config->config_mask, RTSER_SET_PARITY |
+                    RTSER_SET_DATA_BITS | RTSER_SET_STOP_BITS |
+                    RTSER_SET_BAUD | RTSER_SET_HANDSHAKE)) {
+               struct mpc52xx_psc *psc = ctx->port->psc;
+               unsigned char mr1 = 0, mr2 = 0;
+               unsigned int divisor;
+               u16 prescaler;
+
+               switch (ctx->config.data_bits) {
+               case RTSER_5_BITS:
+                       mr1 |= MPC52xx_PSC_MODE_5_BITS;
+                       break;
+               case RTSER_6_BITS:
+                       mr1 |= MPC52xx_PSC_MODE_6_BITS;
+                       break;
+               case RTSER_7_BITS:
+                       mr1 |= MPC52xx_PSC_MODE_7_BITS;
+                       break;
+               case RTSER_8_BITS:
+               default:
+                       mr1 |= MPC52xx_PSC_MODE_8_BITS;
+                       break;
+               }
+
+               switch (ctx->config.parity) {
+               case RTSER_ODD_PARITY:
+                       mr1 |= MPC52xx_PSC_MODE_PARODD;
+                       break;
+               case RTSER_EVEN_PARITY:
+                       mr1 |= MPC52xx_PSC_MODE_PAREVEN;
+                       break;
+               case RTSER_NO_PARITY:
+               default:
+                       mr1 |= MPC52xx_PSC_MODE_PARNONE;
+                       break;
+               }
+
+               if (ctx->config.stop_bits == RTSER_2_STOPB)
+                       mr2 |= (ctx->config.data_bits == RTSER_5_BITS) ?
+                               MPC52xx_PSC_MODE_ONE_STOP_5_BITS :
+                               MPC52xx_PSC_MODE_TWO_STOP;
+               else
+                       mr2 |= MPC52xx_PSC_MODE_ONE_STOP;
+
+               if (ctx->config.handshake == RTSER_RTSCTS_HAND) {
+                       mr1 |= MPC52xx_PSC_MODE_RXRTS;
+                       mr2 |= MPC52xx_PSC_MODE_TXCTS;
+               } else if (testbits(config->config_mask, RTSER_SET_HANDSHAKE)) {
+                       ctx->mcr_status =
+                               RTSER_MCR_DTR | RTSER_MCR_RTS | RTSER_MCR_OUT2;
+                       psc_set_mcr(ctx, ctx->mcr_status);
+               }
+
+               /* Reset the TX & RX */
+               out_8(&psc->command, MPC52xx_PSC_RST_RX);
+               out_8(&psc->command, MPC52xx_PSC_RST_TX);
+
+               /* Send new mode settings */
+               out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1);
+               out_8(&psc->mode, mr1);
+               out_8(&psc->mode, mr2);
+
+               /* Set baudrate */
+               divisor = (ctx->port->uartclk + 16 * ctx->config.baud_rate) /
+                       (32 * ctx->config.baud_rate);
+               prescaler = 0xdd00;
+               out_be16(&psc->mpc52xx_psc_clock_select, prescaler);
+               out_8(&psc->ctur, divisor >> 8);
+               out_8(&psc->ctlr, divisor & 0xff);
+
+               dev_info(ctx->port->dev,
+                        "mr1=%#x mr2=%#x baud=%d divisor=%d prescaler=%x\n",
+                        mr1, mr2, ctx->config.baud_rate, divisor, prescaler);
+
+               /* Reenable TX & RX */
+               out_8(&psc->command, MPC52xx_PSC_TX_ENABLE);
+               out_8(&psc->command, MPC52xx_PSC_RX_ENABLE);
+
+               /* Enable RX */
+               ctx->imr_status |= MPC52xx_PSC_IMR_RXRDY;
+               out_be16(&ctx->port->psc->mpc52xx_psc_imr, ctx->imr_status);
+
+               ctx->status = 0;
+               ctx->ioc_events &= ~RTSER_EVENT_ERRPEND;
+
+       }
+
+       if (testbits(config->config_mask, RTSER_SET_RS485)) {
+               ctx->config.rs485 = config->rs485;
+               if (config->rs485) {
+                       /* reset RTS */
+                       ctx->mcr_status &= ~RTSER_MCR_RTS;
+                       dev_dbg(ctx->port->dev, "Reset RTS, mcr_status=%#x\n",
+                               ctx->mcr_status);
+                       psc_set_mcr(ctx, ctx->mcr_status);
+               }
+       }
+
+       rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);
+
+       /* Timeout manipulation is not atomic. The user is supposed to take
+          care not to use and change timeouts at the same time. */
+       if (testbits(config->config_mask, RTSER_SET_TIMEOUT_RX))
+               ctx->config.rx_timeout = config->rx_timeout;
+       if (testbits(config->config_mask, RTSER_SET_TIMEOUT_TX))
+               ctx->config.tx_timeout = config->tx_timeout;
+       if (testbits(config->config_mask, RTSER_SET_TIMEOUT_EVENT))
+               ctx->config.event_timeout = config->event_timeout;
+
+       if (testbits(config->config_mask, RTSER_SET_TIMESTAMP_HISTORY)) {
+               /* change timestamp history atomically */
+               rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
+
+               if (testbits
+                   (config->timestamp_history, RTSER_RX_TIMESTAMP_HISTORY)) {
+                       if (!ctx->in_history) {
+                               ctx->in_history = *in_history_ptr;
+                               *in_history_ptr = NULL;
+                               if (!ctx->in_history)
+                                       err = -ENOMEM;
+                       }
+               } else {
+                       *in_history_ptr = ctx->in_history;
+                       ctx->in_history = NULL;
+               }
+
+               rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);
+       }
+
+       if (testbits(config->config_mask, RTSER_SET_EVENT_MASK)) {
+               /* change event mask atomically */
+               rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
+
+               ctx->config.event_mask = config->event_mask & EVENT_MASK;
+               ctx->ioc_events = 0;
+
+               if (testbits(config->event_mask, RTSER_EVENT_RXPEND) &&
+                   (ctx->in_npend > 0))
+                       ctx->ioc_events |= RTSER_EVENT_RXPEND;
+
+               if (testbits(config->event_mask, RTSER_EVENT_ERRPEND) &&
+                   ctx->status)
+                       ctx->ioc_events |= RTSER_EVENT_ERRPEND;
+
+               if (testbits(config->event_mask, RTSER_EVENT_TXEMPTY) &&
+                   !ctx->out_npend && ctx->tx_empty)
+                       ctx->ioc_events |= RTSER_EVENT_TXEMPTY;
+
+               if (testbits(config->event_mask,
+                            RTSER_EVENT_MODEMHI | RTSER_EVENT_MODEMLO))
+                       psc_enable_ms(ctx);
+               else
+                       psc_disable_ms(ctx);
+
+               rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);
+       }
+
+       return err;
+}
+
+void rt_mpc52xx_uart_cleanup_ctx(struct rt_mpc52xx_uart_ctx *ctx)
+{
+       rtdm_event_destroy(&ctx->in_event);
+       rtdm_event_destroy(&ctx->out_event);
+       rtdm_event_destroy(&ctx->ioc_event);
+       rtdm_mutex_destroy(&ctx->out_lock);
+}
+
+static int rt_mpc52xx_uart_open(struct rtdm_dev_context *context,
+                               rtdm_user_info_t *user_info, int oflags)
+{
+       struct rt_mpc52xx_uart_ctx *ctx;
+       rtdm_lockctx_t lock_ctx;
+       uint64_t *dummy;
+       int err;
+
+       ctx = (struct rt_mpc52xx_uart_ctx *)context->dev_private;
+       ctx->port = (struct rt_mpc52xx_uart_port *)context->device->device_data;
+
+       /* IPC initialisation - cannot fail with used parameters */
+       rtdm_lock_init(&ctx->lock);
+       rtdm_event_init(&ctx->in_event, 0);
+       rtdm_event_init(&ctx->out_event, 0);
+       rtdm_event_init(&ctx->ioc_event, 0);
+       rtdm_mutex_init(&ctx->out_lock);
+
+       ctx->in_head = 0;
+       ctx->in_tail = 0;
+       ctx->in_npend = 0;
+       ctx->in_nwait = 0;
+       ctx->in_lock = 0;
+       ctx->in_history = NULL;
+
+       ctx->out_head = 0;
+       ctx->out_tail = 0;
+       ctx->out_npend = 0;
+
+       ctx->ioc_events = 0;
+       ctx->ioc_event_lock = 0;
+       ctx->status = 0;
+       ctx->saved_errors = 0;
+
+       rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
+
+       psc_disable_ints(ctx);
+
+       /* Reset/activate the port, clear and enable interrupts */
+       out_8(&ctx->port->psc->command, MPC52xx_PSC_RST_RX);
+       out_8(&ctx->port->psc->command, MPC52xx_PSC_RST_TX);
+
+       out_be32(&ctx->port->psc->sicr, 0);     /* UART mode DCD ignored */
+
+       psc_fifo_init(ctx);
+
+       out_8(&ctx->port->psc->command, MPC52xx_PSC_TX_ENABLE);
+       out_8(&ctx->port->psc->command, MPC52xx_PSC_RX_ENABLE);
+
+       rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);
+
+       rt_mpc52xx_uart_set_config(ctx, &default_config, &dummy);
+
+       err = rtdm_irq_request(&ctx->irq_handle, ctx->port->irq,
+                              rt_mpc52xx_uart_interrupt, 0,
+                              context->device->proc_name, ctx);
+       if (err) {
+               psc_set_mcr(ctx, 0);
+               rt_mpc52xx_uart_cleanup_ctx(ctx);
+
+               return err;
+       }
+
+       return 0;
+}
+
+static int rt_mpc52xx_uart_close(struct rtdm_dev_context *context,
+                                rtdm_user_info_t *user_info)
+{
+       struct rt_mpc52xx_uart_ctx *ctx;
+       uint64_t *in_history;
+       rtdm_lockctx_t lock_ctx;
+
+       ctx = (struct rt_mpc52xx_uart_ctx *)context->dev_private;
+
+       rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
+
+       /* reset DTR and RTS */
+       psc_set_mcr(ctx, 0);
+
+       psc_disable_ints(ctx);
+
+       in_history = ctx->in_history;
+       ctx->in_history = NULL;
+
+       rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);
+
+       rtdm_irq_free(&ctx->irq_handle);
+
+       rt_mpc52xx_uart_cleanup_ctx(ctx);
+
+       kfree(in_history);
+
+       return 0;
+}
+
+static int rt_mpc52xx_uart_ioctl(struct rtdm_dev_context *context,
+                                rtdm_user_info_t *user_info,
+                                unsigned int request, void *arg)
+{
+       rtdm_lockctx_t lock_ctx;
+       struct rt_mpc52xx_uart_ctx *ctx;
+       int err = 0;
+
+       ctx = (struct rt_mpc52xx_uart_ctx *)context->dev_private;
+
+       switch (request) {
+       case RTSER_RTIOC_GET_CONFIG:
+               if (user_info)
+                       err = rtdm_safe_copy_to_user(user_info, arg,
+                                                    &ctx->config,
+                                                    sizeof(struct
+                                                           rtser_config));
+               else
+                       memcpy(arg, &ctx->config, sizeof(struct rtser_config));
+               break;
+
+       case RTSER_RTIOC_SET_CONFIG: {
+               struct rtser_config *config;
+               struct rtser_config config_buf;
+               uint64_t *hist_buf = NULL;
+
+               config = (struct rtser_config *)arg;
+
+               if (user_info) {
+                       err = rtdm_safe_copy_from_user(user_info, &config_buf,
+                                                      arg,
+                                                      sizeof(struct
+                                                             rtser_config));
+                       if (err)
+                               return err;
+
+                       config = &config_buf;
+               }
+
+               if (testbits(config->config_mask, RTSER_SET_BAUD) &&
+                   (config->baud_rate <= 0))
+                       /* invalid baudrate for this port */
+                       return -EINVAL;
+
+               if (testbits(config->config_mask,
+                            RTSER_SET_TIMESTAMP_HISTORY)) {
+                       /*
+                        * Reflect the call to non-RT as we will likely
+                        * allocate or free the buffer.
+                        */
+                       if (rtdm_in_rt_context())
+                               return -ENOSYS;
+
+                       if (testbits(config->timestamp_history,
+                                    RTSER_RX_TIMESTAMP_HISTORY))
+                               hist_buf = kmalloc(IN_BUFFER_SIZE *
+                                                  sizeof(nanosecs_abs_t),
+                                                  GFP_KERNEL);
+               }
+
+               rt_mpc52xx_uart_set_config(ctx, config, &hist_buf);
+
+               if (hist_buf)
+                       kfree(hist_buf);
+
+               break;
+       }
+
+       case RTSER_RTIOC_GET_STATUS: {
+               int status;
+
+               rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
+
+               status = ctx->saved_errors | ctx->status;
+               ctx->status = 0;
+               ctx->saved_errors = 0;
+               ctx->ioc_events &= ~RTSER_EVENT_ERRPEND;
+
+               rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);
+
+               if (user_info) {
+                       struct rtser_status status_buf;
+
+                       status_buf.line_status = status;
+                       status_buf.modem_status = psc_get_msr(ctx);
+
+                       err = rtdm_safe_copy_to_user(user_info, arg,
+                                                    &status_buf,
+                                                    sizeof(struct
+                                                           rtser_status));
+               } else {
+                       ((struct rtser_status *)arg)->line_status = status;
+                       ((struct rtser_status *)arg)->modem_status =
+                               psc_get_msr(ctx);
+               }
+               break;
+       }
+
+       case RTSER_RTIOC_GET_CONTROL:
+               if (user_info)
+                       err = rtdm_safe_copy_to_user(user_info, arg,
+                                                    &ctx->mcr_status,
+                                                    sizeof(int));
+               else
+                       *(int *)arg = ctx->mcr_status;
+
+               break;
+
+       case RTSER_RTIOC_SET_CONTROL: {
+               int new_mcr = (long)arg;
+
+               if ((new_mcr & RTSER_MCR_RTS) != RTSER_MCR_RTS)
+                       dev_warn(ctx->port->dev,
+                                "MCR: Only RTS is supported\n");
+               rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
+               ctx->mcr_status = new_mcr & RTSER_MCR_RTS;
+               psc_set_mcr(ctx, ctx->mcr_status);
+               rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);
+               break;
+       }
+
+       case RTSER_RTIOC_WAIT_EVENT: {
+               struct rtser_event ev = { .rxpend_timestamp = 0 };
+               rtdm_toseq_t timeout_seq;
+
+               if (!rtdm_in_rt_context())
+                       return -ENOSYS;
+
+               /* Only one waiter allowed, stop any further attempts here. */
+               if (test_and_set_bit(0, &ctx->ioc_event_lock))
+                       return -EBUSY;
+
+               rtdm_toseq_init(&timeout_seq, ctx->config.event_timeout);
+
+               rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
+
+               while (!ctx->ioc_events) {
+                       rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);
+
+                       err = rtdm_event_timedwait(&ctx->ioc_event,
+                                                  ctx->config.event_timeout,
+                                                  &timeout_seq);
+                       if (err) {
+                               /* Device has been closed? */
+                               if (err == -EIDRM)
+                                       err = -EBADF;
+                               goto wait_unlock_out;
+                       }
+
+                       rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
+               }
+
+               ev.events = ctx->ioc_events;
+               ctx->ioc_events &= ~(RTSER_EVENT_MODEMHI | RTSER_EVENT_MODEMLO);
+
+               ev.last_timestamp = ctx->last_timestamp;
+               ev.rx_pending = ctx->in_npend;
+
+               if (ctx->in_history)
+                       ev.rxpend_timestamp = ctx->in_history[ctx->in_head];
+
+               rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);
+
+               if (user_info)
+                       err =
+                           rtdm_safe_copy_to_user(user_info, arg, &ev,
+                                                  sizeof(struct
+                                                         rtser_event));
+                       else
+                               memcpy(arg, &ev, sizeof(struct rtser_event));
+
+             wait_unlock_out:
+               /* release the simple event waiter lock */
+               clear_bit(0, &ctx->ioc_event_lock);
+               break;
+       }
+
+       case RTSER_RTIOC_BREAK_CTL: {
+               rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
+               if ((long)arg & RTSER_BREAK_SET)
+                       out_8(&ctx->port->psc->command,
+                             MPC52xx_PSC_START_BRK);
+               else
+                       out_8(&ctx->port->psc->command,
+                             MPC52xx_PSC_STOP_BRK);
+               rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);
+               break;
+       }
+
+#ifdef ISREADY
+       case RTIOC_PURGE: {
+               int fcr = 0;
+
+               rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
+               if ((long)arg & RTDM_PURGE_RX_BUFFER) {
+                       ctx->in_head = 0;
+                       ctx->in_tail = 0;
+                       ctx->in_npend = 0;
+                       ctx->status = 0;
+                       fcr |= FCR_FIFO | FCR_RESET_RX;
+                       rt_mpc52xx_uart_reg_in(mode, base, RHR);
+               }
+               if ((long)arg & RTDM_PURGE_TX_BUFFER) {
+                       ctx->out_head = 0;
+                       ctx->out_tail = 0;
+                       ctx->out_npend = 0;
+                       fcr |= FCR_FIFO | FCR_RESET_TX;
+               }
+               if (fcr) {
+                       rt_mpc52xx_uart_reg_out(mode, base, FCR, fcr);
+                       rt_mpc52xx_uart_reg_out(mode, base, FCR,
+                                        FCR_FIFO | ctx->config.fifo_depth);
+               }
+               rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);
+               break;
+       }
+#endif
+
+       default:
+               err = -ENOTTY;
+       }
+
+       return err;
+}
+
+static ssize_t rt_mpc52xx_uart_read(struct rtdm_dev_context *context,
+                                   rtdm_user_info_t *user_info, void *buf,
+                                   size_t nbyte)
+{
+       struct rt_mpc52xx_uart_ctx *ctx;
+       rtdm_lockctx_t lock_ctx;
+       size_t read = 0;
+       int pending;
+       int block;
+       int subblock;
+       int in_pos;
+       char *out_pos = (char *)buf;
+       rtdm_toseq_t timeout_seq;
+       ssize_t ret = -EAGAIN;  /* for non-blocking read */
+       int nonblocking;
+
+       if (nbyte == 0)
+               return 0;
+
+       if (user_info && !rtdm_rw_user_ok(user_info, buf, nbyte))
+               return -EFAULT;
+
+       ctx = (struct rt_mpc52xx_uart_ctx *)context->dev_private;
+
+       rtdm_toseq_init(&timeout_seq, ctx->config.rx_timeout);
+
+       /* non-blocking is handled separately here */
+       nonblocking = (ctx->config.rx_timeout < 0);
+
+       /* only one reader allowed, stop any further attempts here */
+       if (test_and_set_bit(0, &ctx->in_lock))
+               return -EBUSY;
+
+       rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
+
+       while (1) {
+               if (ctx->status) {
+                       if (testbits(ctx->status, RTSER_LSR_BREAK_IND))
+                               ret = -EPIPE;
+                       else
+                               ret = -EIO;
+                       ctx->saved_errors = ctx->status &
+                           (RTSER_LSR_OVERRUN_ERR | RTSER_LSR_PARITY_ERR |
+                            RTSER_LSR_FRAMING_ERR | RTSER_SOFT_OVERRUN_ERR);
+                       ctx->status = 0;
+                       break;
+               }
+
+               pending = ctx->in_npend;
+
+               if (pending > 0) {
+                       block = subblock = (pending <= nbyte) ? pending : nbyte;
+                       in_pos = ctx->in_head;
+
+                       rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);
+
+                       /* Do we have to wrap around the buffer end? */
+                       if (in_pos + subblock > IN_BUFFER_SIZE) {
+                               /* Treat the block between head and buffer end
+                                  separately. */
+                               subblock = IN_BUFFER_SIZE - in_pos;
+
+                               if (user_info) {
+                                       if (rtdm_copy_to_user
+                                           (user_info, out_pos,
+                                            &ctx->in_buf[in_pos],
+                                            subblock) != 0) {
+                                               ret = -EFAULT;
+                                               goto break_unlocked;
+                                       }
+                               } else
+                                       memcpy(out_pos, &ctx->in_buf[in_pos],
+                                              subblock);
+
+                               read += subblock;
+                               out_pos += subblock;
+
+                               subblock = block - subblock;
+                               in_pos = 0;
+                       }
+
+                       if (user_info) {
+                               if (rtdm_copy_to_user(user_info, out_pos,
+                                                     &ctx->in_buf[in_pos],
+                                                     subblock) != 0) {
+                                       ret = -EFAULT;
+                                       goto break_unlocked;
+                               }
+                       } else
+                               memcpy(out_pos, &ctx->in_buf[in_pos], subblock);
+
+                       read += subblock;
+                       out_pos += subblock;
+                       nbyte -= block;
+
+                       rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
+
+                       ctx->in_head =
+                           (ctx->in_head + block) & (IN_BUFFER_SIZE - 1);
+                       if ((ctx->in_npend -= block) == 0)
+                               ctx->ioc_events &= ~RTSER_EVENT_RXPEND;
+
+                       if (nbyte == 0)
+                               break; /* All requested bytes read. */
+
+                       continue;
+               }
+
+               if (nonblocking)
+                       /* ret was set to EAGAIN in case of a real
+                          non-blocking call or contains the error
+                          returned by rtdm_event_wait[_until] */
+                       break;
+
+               ctx->in_nwait = nbyte;
+
+               rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);
+
+               ret = rtdm_event_timedwait(&ctx->in_event,
+                                          ctx->config.rx_timeout,
+                                          &timeout_seq);
+               if (ret < 0) {
+                       if (ret == -EIDRM) {
+                               /* Device has been closed -
+                                  return immediately. */
+                               return -EBADF;
+                       }
+
+                       rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
+
+                       nonblocking = 1;
+                       if (ctx->in_npend > 0) {
+                               /* Final turn: collect pending bytes
+                                  before exit. */
+                               continue;
+                       }
+
+                       ctx->in_nwait = 0;
+                       break;
+               }
+
+               rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
+       }
+
+       rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);
+
+break_unlocked:
+       /* Release the simple reader lock, */
+       clear_bit(0, &ctx->in_lock);
+
+       if ((read > 0) && ((ret == 0) || (ret == -EAGAIN) ||
+                          (ret == -ETIMEDOUT) || (ret == -EINTR)))
+               ret = read;
+
+       return ret;
+}
+
+static ssize_t rt_mpc52xx_uart_write(struct rtdm_dev_context *context,
+                                    rtdm_user_info_t  *user_info,
+                                    const void *buf,
+                                    size_t nbyte)
+{
+       struct rt_mpc52xx_uart_ctx *ctx;
+       rtdm_lockctx_t lock_ctx;
+       size_t written = 0;
+       int free;
+       int block;
+       int subblock;
+       int out_pos;
+       char *in_pos = (char *)buf;
+       rtdm_toseq_t timeout_seq;
+       ssize_t ret;
+
+       if (nbyte == 0)
+               return 0;
+
+       if (user_info && !rtdm_read_user_ok(user_info, buf, nbyte))
+               return -EFAULT;
+
+       ctx = (struct rt_mpc52xx_uart_ctx *)context->dev_private;
+
+       rtdm_toseq_init(&timeout_seq, ctx->config.rx_timeout);
+
+       /* Make write operation atomic. */
+       ret = rtdm_mutex_timedlock(&ctx->out_lock, ctx->config.rx_timeout,
+                                  &timeout_seq);
+       if (ret)
+               return ret;
+
+       while (nbyte > 0) {
+               rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
+
+               free = OUT_BUFFER_SIZE - ctx->out_npend;
+
+               if (free > 0) {
+                       block = subblock = (nbyte <= free) ? nbyte : free;
+                       out_pos = ctx->out_tail;
+
+                       rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);
+
+                       /* Do we have to wrap around the buffer end? */
+                       if (out_pos + subblock > OUT_BUFFER_SIZE) {
+                               /* Treat the block between head and buffer
+                                  end separately. */
+                               subblock = OUT_BUFFER_SIZE - out_pos;
+
+                               if (user_info) {
+                                       if (rtdm_copy_from_user
+                                           (user_info,
+                                            &ctx->out_buf[out_pos],
+                                            in_pos, subblock) != 0) {
+                                               ret = -EFAULT;
+                                               break;
+                                       }
+                               } else
+                                       memcpy(&ctx->out_buf[out_pos], in_pos,
+                                              subblock);
+
+                               written += subblock;
+                               in_pos += subblock;
+
+                               subblock = block - subblock;
+                               out_pos = 0;
+                       }
+
+                       if (user_info) {
+                               if (rtdm_copy_from_user
+                                   (user_info, &ctx->out_buf[out_pos],
+                                    in_pos, subblock) != 0) {
+                                       ret = -EFAULT;
+                                       break;
+                               }
+                       } else
+                               memcpy(&ctx->out_buf[out_pos], in_pos, block);
+
+                       written += subblock;
+                       in_pos += subblock;
+                       nbyte -= block;
+
+                       rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
+
+                       ctx->out_tail =
+                           (ctx->out_tail + block) & (OUT_BUFFER_SIZE - 1);
+                       ctx->out_npend += block;
+
+                       /* Mark shift register not empty */
+                       ctx->ioc_events &= ~RTSER_EVENT_TXEMPTY;
+                       ctx->tx_empty = 0;
+
+                       psc_start_tx(ctx);
+
+                       rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);
+                       continue;
+               }
+
+               rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);
+
+               ret = rtdm_event_timedwait(&ctx->out_event,
+                                          ctx->config.tx_timeout,
+                                          &timeout_seq);
+               if (ret < 0) {
+                       if (ret == -EIDRM) {
+                               /* Device has been closed -
+                                  return immediately. */
+                               return -EBADF;
+                       }
+                       if (ret == -EWOULDBLOCK) {
+                               /* Fix error code for non-blocking mode. */
+                               ret = -EAGAIN;
+                       }
+                       break;
+               }
+       }
+
+       rtdm_mutex_unlock(&ctx->out_lock);
+
+       if ((written > 0) && ((ret == 0) || (ret == -EAGAIN) ||
+                             (ret == -ETIMEDOUT) || (ret == -EINTR)))
+               ret = written;
+
+       return ret;
+}
+
+static const struct rtdm_device __devinitdata device_tmpl = {
+       .struct_version         = RTDM_DEVICE_STRUCT_VER,
+
+       .device_flags           = RTDM_NAMED_DEVICE | RTDM_EXCLUSIVE,
+       .context_size           = sizeof(struct rt_mpc52xx_uart_ctx),
+       .device_name            = "",
+
+       .open_nrt               = rt_mpc52xx_uart_open,
+
+       .ops = {
+               .close_nrt      = rt_mpc52xx_uart_close,
+
+               .ioctl_rt       = rt_mpc52xx_uart_ioctl,
+               .ioctl_nrt      = rt_mpc52xx_uart_ioctl,
+
+               .read_rt        = rt_mpc52xx_uart_read,
+
+               .write_rt       = rt_mpc52xx_uart_write,
+       },
+
+       .device_class           = RTDM_CLASS_SERIAL,
+       .device_sub_class       = RTDM_SUBCLASS_16550A,
+       .profile_version        = RTSER_PROFILE_VER,
+       .driver_name            = RT_MPC52XX_UART_DRVNAM,
+       .driver_version         = RTDM_DRIVER_VER(1, 0, 0),
+       .peripheral_name        = "MPC52xx UART",
+       .provider_name          = "Wolfgang Grandegger",
+};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
+
+static int __devinit rt_mpc52xx_uart_of_probe(struct platform_device *op)
+#else
+static int __devinit rt_mpc52xx_uart_of_probe(struct platform_device *op,
+                                             const struct of_device_id *match)
+#endif
+{
+       struct rt_mpc52xx_uart_port *port;
+       struct rtdm_device *dev;
+       struct resource res;
+       int ret, idx;
+
+       dev_dbg(&op->dev, "mpc52xx_uart_probe(op=%p)\n", op);
+
+       /* Check validity & presence */
+       for (idx = 0; idx < MPC52xx_PSC_MAXNUM; idx++)
+               if (rt_mpc52xx_uart_nodes[idx] == op->dev.of_node)
+                       break;
+       if (idx >= MPC52xx_PSC_MAXNUM)
+               return -EINVAL;
+
+       port = kmalloc(sizeof(*port), GFP_KERNEL);
+       if (!port) {
+               dev_err(&op->dev, "Could allocate port space\n");
+               return -ENOMEM;
+       }
+       port->dev = &op->dev;
+
+       /*
+        * Set the uart clock to the input clock of the psc, the different
+        * prescalers are taken into account in the set_baudrate() methods
+        * of the respective chip
+        */
+       port->uartclk = mpc5xxx_get_bus_frequency(op->dev.of_node);
+       if (port->uartclk == 0) {
+               dev_err(&op->dev, "Could not find uart clock frequency\n");
+               ret = -EINVAL;
+               goto out_kfree_port;
+       }
+
+       /* Fetch register locations */
+       ret = of_address_to_resource(op->dev.of_node, 0, &res);
+       if (ret) {
+               dev_err(&op->dev, "Could not get resources\n");
+               goto out_kfree_port;
+       }
+       port->num = ((res.start >> 8) & 0xf) / 2;
+       if (port->num < 6)
+               port->num++;
+
+       if (!request_mem_region(res.start, resource_size(&res),
+                               RT_MPC52XX_UART_DRVNAM)) {
+               ret = -EBUSY;
+               goto out_kfree_port;
+       }
+
+       port->psc = ioremap(res.start, resource_size(&res));
+       if (!port->psc) {
+               dev_err(&op->dev, "Could not map PSC registers\n");
+               ret = -ENOMEM;
+               goto out_release_mem_region;
+       }
+       port->fifo = (struct mpc52xx_psc_fifo __iomem *)(port->psc + 1);
+
+       port->irq = irq_of_parse_and_map(op->dev.of_node, 0);
+       if (port->irq <= 0) {
+               dev_err(&op->dev, "Could not get irq\n");
+               ret = -ENODEV;
+               goto out_iounmap;
+       }
+
+       dev = kmalloc(sizeof(struct rtdm_device), GFP_KERNEL);
+       if (!dev) {
+               dev_err(&op->dev, "Could allocate device context\n");
+               ret = -ENOMEM;
+               goto out_dispose_irq_mapping;
+       }
+
+       memcpy(dev, &device_tmpl, sizeof(struct rtdm_device));
+       snprintf(dev->device_name, RTDM_MAX_DEVNAME_LEN, "rtserPSC%d",
+                idx);
+       dev->device_id = idx;
+       dev->proc_name = dev->device_name;
+       dev->device_data = port;
+
+       rt_mpc52xx_uart_init_hw(port);
+
+       ret = rtdm_dev_register(dev);
+       if (ret)
+               goto out_kfree_dev;
+
+       dev_set_drvdata(&op->dev, dev);
+
+       dev_info(&op->dev, "%s on PSC%d at 0x%p, irq=%d, clk=%i\n",
+                dev->device_name, port->num, port->psc, port->irq,
+                port->uartclk);
+
+       return 0;
+
+out_kfree_dev:
+       kfree(dev);
+out_dispose_irq_mapping:
+       irq_dispose_mapping(port->irq);
+out_iounmap:
+       iounmap(port->psc);
+out_release_mem_region:
+       release_mem_region(res.start, resource_size(&res));
+out_kfree_port:
+       kfree(port);
+
+       return ret;
+}
+
+static int __devinit rt_mpc52xx_uart_of_remove(struct platform_device *op)
+{
+       struct rtdm_device *dev = dev_get_drvdata(&op->dev);
+       struct rt_mpc52xx_uart_port *port = dev->device_data;
+       struct resource res;
+
+       dev_set_drvdata(&op->dev, NULL);
+
+       rtdm_dev_unregister(dev, 1000);
+       irq_dispose_mapping(port->irq);
+       iounmap(port->psc);
+       if (!of_address_to_resource(op->dev.of_node, 0, &res))
+               release_mem_region(res.start, resource_size(&res));
+       kfree(port);
+       kfree(dev);
+
+       return 0;
+}
+
+static struct of_device_id rt_mpc52xx_uart_of_match[] = {
+       { .compatible = "fsl,mpc5200b-psc-uart", },
+       { .compatible = "fsl,mpc5200-psc-uart", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rt_mpc52xx_uart_of_match);
+
+static struct of_platform_driver rt_mpc52xx_uart_of_driver = {
+       .probe = rt_mpc52xx_uart_of_probe,
+       .remove =  __devexit_p(rt_mpc52xx_uart_of_remove),
+       .driver = {
+               .name = "rt-mpc52xx-psc-uart",
+               .owner = THIS_MODULE,
+               .of_match_table = rt_mpc52xx_uart_of_match,
+       },
+};
+
+static void rt_mpc52xx_uart_of_enumerate(void)
+{
+       struct device_node *np;
+       int idx = 0;
+
+       /* Assign index to each PSC in device tree line the linux driver does */
+       for_each_matching_node(np, rt_mpc52xx_uart_of_match) {
+               of_node_get(np);
+               rt_mpc52xx_uart_nodes[idx] = np;
+               idx++;
+       }
+}
+
+static int __init rt_mpc52xx_uart_init(void)
+{
+       int ret;
+
+       printk(KERN_INFO "RTserial: MPC52xx PSC UART driver\n");
+
+       rt_mpc52xx_uart_of_enumerate();
+
+       ret = of_register_platform_driver(&rt_mpc52xx_uart_of_driver);
+       if (ret) {
+               printk(KERN_ERR
+                      "%s; Could not register  driver (err=%d)\n",
+                      __func__, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void __exit rt_mpc52xx_uart_exit(void)
+{
+       of_unregister_platform_driver(&rt_mpc52xx_uart_of_driver);
+}
+
+module_init(rt_mpc52xx_uart_init);
+module_exit(rt_mpc52xx_uart_exit);


_______________________________________________
Xenomai-git mailing list
Xenomai-git@gna.org
https://mail.gna.org/listinfo/xenomai-git

Reply via email to