]> rtime.felk.cvut.cz Git - lisovros/linux_canprio.git/blobdiff - drivers/serial/mpc52xx_uart.c
Update Shark 5200 patch to 2.6.36.1
[lisovros/linux_canprio.git] / drivers / serial / mpc52xx_uart.c
index 8dedb266f143f1cf801d84f34892a999b2dc9934..63cdcd0a6b11ff5ed40947c548f5060a3c95a428 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/clk.h>
+#include <linux/slab.h>
 
 #include <asm/mpc52xx.h>
 #include <asm/mpc52xx_psc.h>
@@ -123,6 +124,10 @@ struct psc_ops {
        irqreturn_t     (*handle_irq)(struct uart_port *port);
 };
 
+struct mpc52xx_private_data {
+       int             mode;           // Determine wether to use port in RS-232 (=0) or RS-485 (=1) mode
+};
+
 /* setting the prescaler and divisor reg is common for all chips */
 static inline void mpc52xx_set_divisor(struct mpc52xx_psc __iomem *psc,
                                       u16 prescaler, unsigned int divisor)
@@ -184,7 +189,11 @@ static int mpc52xx_psc_tx_empty(struct uart_port *port)
 
 static void mpc52xx_psc_start_tx(struct uart_port *port)
 {
+       struct mpc52xx_private_data *private_data = (struct mpc52xx_private_data *) port->private_data;
+       
        port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY;
+       if (private_data->mode == 1)
+               port->read_status_mask |= MPC52xx_PSC_IMR_TXEMP;
        out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask);
 }
 
@@ -500,6 +509,7 @@ static int __init mpc512x_psc_fifoc_init(void)
        psc_fifoc = of_iomap(np, 0);
        if (!psc_fifoc) {
                pr_err("%s: Can't map FIFOC\n", __func__);
+               of_node_put(np);
                return -ENODEV;
        }
 
@@ -642,6 +652,8 @@ mpc52xx_uart_start_tx(struct uart_port *port)
 {
        /* port->lock taken by caller */
        psc_ops->start_tx(port);
+//     if(!uart_console(port))
+//         printk("mpc52xx: start_tx\n");
 }
 
 static void
@@ -665,6 +677,8 @@ mpc52xx_uart_stop_rx(struct uart_port *port)
 {
        /* port->lock taken by caller */
        psc_ops->stop_rx(port);
+//     if(!uart_console(port))
+//         printk("mpc52xx: stop_tx\n");
 }
 
 static void
@@ -772,12 +786,17 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
        }
 
        if (new->c_cflag & PARENB) {
-               mr1 |= (new->c_cflag & PARODD) ?
-                       MPC52xx_PSC_MODE_PARODD : MPC52xx_PSC_MODE_PAREVEN;
+               if (new->c_cflag & CMSPAR) {
+                       mr1 |= MPC52xx_PSC_MODE_PARFORCE;
+                       mr1 |= (new->c_cflag & PARODD) ?
+                               MPC52xx_PSC_MODE_PARODD : MPC52xx_PSC_MODE_PAREVEN;
+               } else { 
+                       mr1 |= (new->c_cflag & PARODD) ?
+                               MPC52xx_PSC_MODE_PARODD : MPC52xx_PSC_MODE_PAREVEN;
+               }
        } else
                mr1 |= MPC52xx_PSC_MODE_PARNONE;
 
-
        mr2 = 0;
 
        if (new->c_cflag & CSTOPB)
@@ -830,6 +849,9 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
        out_8(&psc->command, MPC52xx_PSC_TX_ENABLE);
        out_8(&psc->command, MPC52xx_PSC_RX_ENABLE);
 
+       /* RS-485 direction control - Switch to receiving  TODO: if in 485 mode*/
+       out_8(&PSC(port)->op1, 0x1); 
+
        /* We're all set, release the lock */
        spin_unlock_irqrestore(&port->lock, flags);
 }
@@ -1038,8 +1060,10 @@ mpc52xx_uart_int_tx_chars(struct uart_port *port)
 static irqreturn_t
 mpc5xxx_uart_process_int(struct uart_port *port)
 {
+       struct mpc52xx_private_data *private_data = (struct mpc52xx_private_data *) port->private_data;
        unsigned long pass = ISR_PASS_LIMIT;
        unsigned int keepgoing;
+       struct circ_buf *xmit = &port->state->xmit;
        u8 status;
 
        /* While we have stuff to do, we continue */
@@ -1051,10 +1075,30 @@ mpc5xxx_uart_process_int(struct uart_port *port)
                if (psc_ops->rx_rdy(port))
                        keepgoing |= mpc52xx_uart_int_rx_chars(port);
 
+               /* RS-485 direction control */
+               if (private_data->mode == 1) {
+                       if (!uart_circ_empty(xmit)) {
+                               out_8(&PSC(port)->op0, 0x1); 
+                       }
+               }
+
                psc_ops->tx_clr_irq(port);
                if (psc_ops->tx_rdy(port))
                        keepgoing |= mpc52xx_uart_int_tx_chars(port);
 
+               /* RS-485 direction control */
+               if (private_data->mode == 1) {
+                       //if ( ( status & MPC52xx_PSC_IMR_TXEMP) && ( (status & MPC52xx_PSC_IMR_TXRDY) == 0 ) && (uart_circ_empty(xmit)) ) {
+                       if ( (psc_ops->tx_empty(port)) && (psc_ops->raw_tx_rdy(port) != 0) && (uart_circ_empty(xmit)) ) {
+                               out_8(&PSC(port)->op1, 0x1); 
+
+                               port->read_status_mask &= ~MPC52xx_PSC_IMR_TXEMP;
+                               out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask);
+                       
+                               keepgoing = 0;
+                       }
+               }
+
                status = in_8(&PSC(port)->mpc52xx_psc_ipcr);
                if (status & MPC52xx_PSC_D_DCD)
                        uart_handle_dcd_change(port, !(status & MPC52xx_PSC_DCD));
@@ -1305,6 +1349,9 @@ mpc52xx_uart_of_probe(struct platform_device *op, const struct of_device_id *mat
        struct uart_port *port = NULL;
        struct resource res;
        int ret;
+       struct mpc52xx_private_data *private_data = NULL;
+       const char *port_mode = NULL;
+       int port_mode_len;
 
        dev_dbg(&op->dev, "mpc52xx_uart_probe(op=%p, match=%p)\n", op, match);
 
@@ -1355,6 +1402,22 @@ mpc52xx_uart_of_probe(struct platform_device *op, const struct of_device_id *mat
                dev_dbg(&op->dev, "Could not get irq\n");
                return -EINVAL;
        }
+       
+       private_data = (struct mpc52xx_private_data *) kzalloc(sizeof(struct mpc52xx_private_data), GFP_KERNEL);
+       if (private_data) {
+               port_mode = of_get_property(op->dev.of_node, "uart_mode", &port_mode_len);
+               if (port_mode) {
+                       if (strcmp(port_mode, "RS-485") == 0) {
+                               private_data->mode = 1;
+                       }
+               } else {
+                       private_data->mode = 0;
+               }
+               port->private_data = (void *) private_data;
+       } else {
+               printk(KERN_ERR "Could not allocate private data for PSC\n");
+               return -EINVAL;
+       }
 
        dev_dbg(&op->dev, "mpc52xx-psc uart at %p, irq=%x, freq=%i\n",
                (void *)port->mapbase, port->irq, port->uartclk);
@@ -1375,6 +1438,9 @@ mpc52xx_uart_of_remove(struct platform_device *op)
        dev_set_drvdata(&op->dev, NULL);
 
        if (port)
+               if (port->private_data) {
+                       kfree(port->private_data);
+               }
                uart_remove_one_port(&mpc52xx_uart_driver, port);
 
        return 0;