linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / serial / mpc52xx_uart.c
index 900f7bb..61dd17d 100644 (file)
@@ -18,7 +18,7 @@
  * Some of the code has been inspired/copied from the 2.4 code written
  * by Dale Farnsworth <dfarnsworth@mvista.com>.
  * 
- * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2004-2005 Sylvain Munaut <tnt@246tNt.com>
  * Copyright (C) 2003 MontaVista, Software, Inc.
  * 
  * This file is licensed under the terms of the GNU General Public License
  * kind, whether express or implied.
  */
  
-/* OCP Usage :
+/* Platform device Usage :
  *
- * This drivers uses the OCP model. To load the serial driver for one of the
- * PSCs, just add this to the core_ocp table :
+ * Since PSCs can have multiple function, the correct driver for each one
+ * is selected by calling mpc52xx_match_psc_function(...). The function
+ * handled by this driver is "uart".
  *
- * {
- *     .vendor         = OCP_VENDOR_FREESCALE,
- *     .function       = OCP_FUNC_PSC_UART,
- *     .index          = 0,
- *     .paddr          = MPC52xx_PSC1,
- *     .irq            = MPC52xx_PSC1_IRQ,
- *     .pm             = OCP_CPM_NA,
- * },
- *
- * This is for PSC1, replace the paddr and irq according to the PSC you want to
- * use. The driver all necessary registers to place the PSC in uart mode without
+ * The driver init all necessary registers to place the PSC in uart mode without
  * DCD. However, the pin multiplexing aren't changed and should be set either
  * by the bootloader or in the platform init code.
- * The index field must be equal to the PSC index ( e.g. 0 for PSC1, 1 for PSC2,
- * and so on). So the PSC1 is mapped to /dev/ttyS0, PSC2 to /dev/ttyS1 and so
- * on. But be warned, it's an ABSOLUTE REQUIREMENT ! This is needed mainly for
- * the console code : without this 1:1 mapping, at early boot time, when we are
- * parsing the kernel args console=ttyS?, we wouldn't know wich PSC it will be
- * mapped to because OCP stuff is not yet initialized.
+ *
+ * The idx field must be equal to the PSC index ( e.g. 0 for PSC1, 1 for PSC2,
+ * and so on). So the PSC1 is mapped to /dev/ttyPSC0, PSC2 to /dev/ttyPSC1 and
+ * so on. But be warned, it's an ABSOLUTE REQUIREMENT ! This is needed mainly
+ * fpr the console code : without this 1:1 mapping, at early boot time, when we
+ * are parsing the kernel args console=ttyPSC?, we wouldn't know wich PSC it
+ * will be mapped to.
  */
 
 #include <linux/config.h>
+#include <linux/platform_device.h>
 #include <linux/module.h>
 #include <linux/tty.h>
 #include <linux/serial.h>
@@ -61,7 +54,6 @@
 
 #include <asm/delay.h>
 #include <asm/io.h>
-#include <asm/ocp.h>
 
 #include <asm/mpc52xx.h>
 #include <asm/mpc52xx_psc.h>
 #include <linux/serial_core.h>
 
 
+/* We've been assigned a range on the "Low-density serial ports" major */
+#define SERIAL_PSC_MAJOR       204
+#define SERIAL_PSC_MINOR       148
+
 
 #define ISR_PASS_LIMIT 256     /* Max number of iteration in the interrupt */
 
@@ -86,7 +82,7 @@ static struct uart_port mpc52xx_uart_ports[MPC52xx_PSC_MAXNUM];
         *        the console_init
         */
 
-#define PSC(port) ((struct mpc52xx_psc *)((port)->membase))
+#define PSC(port) ((struct mpc52xx_psc __iomem *)((port)->membase))
 
 
 /* Forward declaration of the interruption handling routine */
@@ -127,7 +123,7 @@ mpc52xx_uart_get_mctrl(struct uart_port *port)
 }
 
 static void 
-mpc52xx_uart_stop_tx(struct uart_port *port, unsigned int tty_stop)
+mpc52xx_uart_stop_tx(struct uart_port *port)
 {
        /* port->lock taken by caller */
        port->read_status_mask &= ~MPC52xx_PSC_IMR_TXRDY;
@@ -135,7 +131,7 @@ mpc52xx_uart_stop_tx(struct uart_port *port, unsigned int tty_stop)
 }
 
 static void 
-mpc52xx_uart_start_tx(struct uart_port *port, unsigned int tty_start)
+mpc52xx_uart_start_tx(struct uart_port *port)
 {
        /* port->lock taken by caller */
        port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY;
@@ -190,7 +186,14 @@ mpc52xx_uart_break_ctl(struct uart_port *port, int ctl)
 static int
 mpc52xx_uart_startup(struct uart_port *port)
 {
-       struct mpc52xx_psc *psc = PSC(port);
+       struct mpc52xx_psc __iomem *psc = PSC(port);
+       int ret;
+
+       /* Request IRQ */
+       ret = request_irq(port->irq, mpc52xx_uart_int,
+               SA_INTERRUPT | SA_SAMPLE_RANDOM, "mpc52xx_psc_uart", port);
+       if (ret)
+               return ret;
 
        /* Reset/activate the port, clear and enable interrupts */
        out_8(&psc->command,MPC52xx_PSC_RST_RX);
@@ -217,7 +220,7 @@ mpc52xx_uart_startup(struct uart_port *port)
 static void
 mpc52xx_uart_shutdown(struct uart_port *port)
 {
-       struct mpc52xx_psc *psc = PSC(port);
+       struct mpc52xx_psc __iomem *psc = PSC(port);
        
        /* Shut down the port, interrupt and all */
        out_8(&psc->command,MPC52xx_PSC_RST_RX);
@@ -225,13 +228,16 @@ mpc52xx_uart_shutdown(struct uart_port *port)
        
        port->read_status_mask = 0; 
        out_be16(&psc->mpc52xx_psc_imr,port->read_status_mask);
+
+       /* Release interrupt */
+       free_irq(port->irq, port);
 }
 
 static void 
 mpc52xx_uart_set_termios(struct uart_port *port, struct termios *new,
                          struct termios *old)
 {
-       struct mpc52xx_psc *psc = PSC(port);
+       struct mpc52xx_psc __iomem *psc = PSC(port);
        unsigned long flags;
        unsigned char mr1, mr2;
        unsigned short ctr;
@@ -326,15 +332,21 @@ mpc52xx_uart_release_port(struct uart_port *port)
                iounmap(port->membase);
                port->membase = NULL;
        }
+
+       release_mem_region(port->mapbase, MPC52xx_PSC_SIZE);
 }
 
 static int
 mpc52xx_uart_request_port(struct uart_port *port)
 {
        if (port->flags & UPF_IOREMAP) /* Need to remap ? */
-               port->membase = ioremap(port->mapbase, sizeof(struct mpc52xx_psc));
-       
-       return port->membase != NULL ? 0 : -EBUSY;
+               port->membase = ioremap(port->mapbase, MPC52xx_PSC_SIZE);
+
+       if (!port->membase)
+               return -EINVAL;
+
+       return request_mem_region(port->mapbase, MPC52xx_PSC_SIZE,
+                       "mpc52xx_psc_uart") != NULL ? 0 : -EBUSY;
 }
 
 static void
@@ -354,7 +366,7 @@ mpc52xx_uart_verify_port(struct uart_port *port, struct serial_struct *ser)
        if ( (ser->irq != port->irq) ||
             (ser->io_type != SERIAL_IO_MEM) ||
             (ser->baud_base != port->uartclk)  || 
-            // FIXME Should check addresses/irq as well ?
+            (ser->iomem_base != (void*)port->mapbase) ||
             (ser->hub6 != 0 ) )
                return -EINVAL;
 
@@ -393,17 +405,13 @@ static inline int
 mpc52xx_uart_int_rx_chars(struct uart_port *port, struct pt_regs *regs)
 {
        struct tty_struct *tty = port->info->tty;
-       unsigned char ch;
+       unsigned char ch, flag;
        unsigned short status;
 
        /* While we can read, do so ! */
        while ( (status = in_be16(&PSC(port)->mpc52xx_psc_status)) &
                MPC52xx_PSC_SR_RXRDY) {
 
-               /* If we are full, just stop reading */
-               if (tty->flip.count >= TTY_FLIPBUF_SIZE)
-                       break;
-               
                /* Get the char */
                ch = in_8(&PSC(port)->mpc52xx_psc_buffer_8);
 
@@ -416,45 +424,35 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port, struct pt_regs *regs)
 #endif
 
                /* Store it */
-               *tty->flip.char_buf_ptr = ch;
-               *tty->flip.flag_buf_ptr = 0;
+
+               flag = TTY_NORMAL;
                port->icount.rx++;
        
                if ( status & (MPC52xx_PSC_SR_PE |
                               MPC52xx_PSC_SR_FE |
-                              MPC52xx_PSC_SR_RB |
-                              MPC52xx_PSC_SR_OE) ) {
+                              MPC52xx_PSC_SR_RB) ) {
                        
                        if (status & MPC52xx_PSC_SR_RB) {
-                               *tty->flip.flag_buf_ptr = TTY_BREAK;
+                               flag = TTY_BREAK;
                                uart_handle_break(port);
                        } else if (status & MPC52xx_PSC_SR_PE)
-                               *tty->flip.flag_buf_ptr = TTY_PARITY;
+                               flag = TTY_PARITY;
                        else if (status & MPC52xx_PSC_SR_FE)
-                               *tty->flip.flag_buf_ptr = TTY_FRAME;
-                       if (status & MPC52xx_PSC_SR_OE) {
-                               /*
-                                * Overrun is special, since it's
-                                * reported immediately, and doesn't
-                                * affect the current character
-                                */
-                               if (tty->flip.count < (TTY_FLIPBUF_SIZE-1)) {
-                                       tty->flip.flag_buf_ptr++;
-                                       tty->flip.char_buf_ptr++;
-                                       tty->flip.count++;
-                               }
-                               *tty->flip.flag_buf_ptr = TTY_OVERRUN;
-                       }
+                               flag = TTY_FRAME;
 
                        /* Clear error condition */
                        out_8(&PSC(port)->command,MPC52xx_PSC_RST_ERR_STAT);
 
                }
-
-               tty->flip.char_buf_ptr++;
-               tty->flip.flag_buf_ptr++;
-               tty->flip.count++;
-
+               tty_insert_flip_char(tty, ch, flag);
+               if (status & MPC52xx_PSC_SR_OE) {
+                       /*
+                        * Overrun is special, since it's
+                        * reported immediately, and doesn't
+                        * affect the current character
+                        */
+                       tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+               }
        }
 
        tty_flip_buffer_push(tty);
@@ -477,7 +475,7 @@ mpc52xx_uart_int_tx_chars(struct uart_port *port)
 
        /* Nothing to do ? */
        if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
-               mpc52xx_uart_stop_tx(port,0);
+               mpc52xx_uart_stop_tx(port);
                return 0;
        }
 
@@ -496,7 +494,7 @@ mpc52xx_uart_int_tx_chars(struct uart_port *port)
 
        /* Maybe we're done after all */
        if (uart_circ_empty(xmit)) {
-               mpc52xx_uart_stop_tx(port,0);
+               mpc52xx_uart_stop_tx(port);
                return 0;
        }
 
@@ -562,7 +560,7 @@ static void __init
 mpc52xx_console_get_options(struct uart_port *port,
                             int *baud, int *parity, int *bits, int *flow)
 {
-       struct mpc52xx_psc *psc = PSC(port);
+       struct mpc52xx_psc __iomem *psc = PSC(port);
        unsigned char mr1;
 
        /* Read the mode registers */
@@ -592,7 +590,7 @@ static void
 mpc52xx_console_write(struct console *co, const char *s, unsigned int count)
 {
        struct uart_port *port = &mpc52xx_uart_ports[co->index];
-       struct mpc52xx_psc *psc = PSC(port);
+       struct mpc52xx_psc __iomem *psc = PSC(port);
        unsigned int i, j;
        
        /* Disable interrupts */
@@ -630,7 +628,7 @@ mpc52xx_console_setup(struct console *co, char *options)
 {
        struct uart_port *port = &mpc52xx_uart_ports[co->index];
 
-       int baud = 9600;
+       int baud = CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD;
        int bits = 8;
        int parity = 'n';
        int flow = 'n';
@@ -643,14 +641,12 @@ mpc52xx_console_setup(struct console *co, char *options)
        spin_lock_init(&port->lock);
        port->uartclk   = __res.bi_ipbfreq / 2; /* Look at CTLR doc */
        port->ops       = &mpc52xx_uart_ops;
-       port->mapbase   = MPC52xx_PSCx(co->index);
+       port->mapbase   = MPC52xx_PA(MPC52xx_PSCx_OFFSET(co->index+1));
 
-               /* We ioremap ourself */
-       port->membase = ioremap(port->mapbase, sizeof(struct mpc52xx_psc));
-       if (port->membase == NULL) {
-               release_mem_region(port->mapbase, sizeof(struct mpc52xx_psc));
-               return -EBUSY;
-       }
+       /* We ioremap ourself */
+       port->membase = ioremap(port->mapbase, MPC52xx_PSC_SIZE);
+       if (port->membase == NULL)
+               return -EINVAL;
 
        /* Setup the port parameters accoding to options */
        if (options)
@@ -662,15 +658,15 @@ mpc52xx_console_setup(struct console *co, char *options)
 }
 
 
-extern struct uart_driver mpc52xx_uart_driver;
+static struct uart_driver mpc52xx_uart_driver;
 
 static struct console mpc52xx_console = {
-       .name   = "ttyS",
+       .name   = "ttyPSC",
        .write  = mpc52xx_console_write,
        .device = uart_console_device,
        .setup  = mpc52xx_console_setup,
        .flags  = CON_PRINTBUFFER,
-       .index  = -1,   /* Specified on the cmdline (e.g. console=ttyS0 ) */
+       .index  = -1,   /* Specified on the cmdline (e.g. console=ttyPSC0 ) */
        .data   = &mpc52xx_uart_driver,
 };
 
@@ -697,36 +693,41 @@ console_initcall(mpc52xx_console_init);
 static struct uart_driver mpc52xx_uart_driver = {
        .owner          = THIS_MODULE,
        .driver_name    = "mpc52xx_psc_uart",
-       .dev_name       = "ttyS",
-       .devfs_name     = "ttyS",
-       .major          = TTY_MAJOR,
-       .minor          = 64,
+       .dev_name       = "ttyPSC",
+       .devfs_name     = "ttyPSC",
+       .major          = SERIAL_PSC_MAJOR,
+       .minor          = SERIAL_PSC_MINOR,
        .nr             = MPC52xx_PSC_MAXNUM,
        .cons           = MPC52xx_PSC_CONSOLE,
 };
 
 
 /* ======================================================================== */
-/* OCP Driver                                                               */
+/* Platform Driver                                                          */
 /* ======================================================================== */
 
 static int __devinit
-mpc52xx_uart_probe(struct ocp_device *ocp)
+mpc52xx_uart_probe(struct platform_device *dev)
 {
+       struct resource *res = dev->resource;
+
        struct uart_port *port = NULL;
-       int idx, ret;
+       int i, idx, ret;
 
-       /* Get the corresponding port struct */
-       idx = ocp->def->index;
+       /* Check validity & presence */
+       idx = dev->id;
        if (idx < 0 || idx >= MPC52xx_PSC_MAXNUM)
                return -EINVAL;
-       
-       port = &mpc52xx_uart_ports[idx];
+
+       if (!mpc52xx_match_psc_function(idx,"uart"))
+               return -ENODEV;
 
        /* Init the port structure */
+       port = &mpc52xx_uart_ports[idx];
+
+       memset(port, 0x00, sizeof(struct uart_port));
+
        spin_lock_init(&port->lock);
-       port->mapbase   = ocp->def->paddr;
-       port->irq       = ocp->def->irq;
        port->uartclk   = __res.bi_ipbfreq / 2; /* Look at CTLR doc */
        port->fifosize  = 255; /* Should be 512 ! But it can't be */
                               /* stored in a unsigned char       */
@@ -735,101 +736,72 @@ mpc52xx_uart_probe(struct ocp_device *ocp)
                          ( uart_console(port) ? 0 : UPF_IOREMAP );
        port->line      = idx;
        port->ops       = &mpc52xx_uart_ops;
-       port->read_status_mask = 0;
-       
-       /* Requests the mem & irqs */
-       /* Unlike other serial drivers, we reserve the resources here, so we
-        * can detect early if multiple drivers uses the same PSC. Special
-        * care must be taken with the console PSC
-        */
-       ret = request_irq(
-               port->irq, mpc52xx_uart_int,
-               SA_INTERRUPT | SA_SAMPLE_RANDOM, "mpc52xx_psc_uart", port);
-       if (ret)
-               goto error;
 
-       ret = request_mem_region(port->mapbase, sizeof(struct mpc52xx_psc),
-                                "mpc52xx_psc_uart") != NULL ? 0 : -EBUSY;
-       if (ret)
-               goto free_irq;
+       /* Search for IRQ and mapbase */
+       for (i=0 ; i<dev->num_resources ; i++, res++) {
+               if (res->flags & IORESOURCE_MEM)
+                       port->mapbase = res->start;
+               else if (res->flags & IORESOURCE_IRQ)
+                       port->irq = res->start;
+       }
+       if (!port->irq || !port->mapbase)
+               return -EINVAL;
 
        /* Add the port to the uart sub-system */
        ret = uart_add_one_port(&mpc52xx_uart_driver, port);
-       if (ret)
-               goto release_mem;
-
-       ocp_set_drvdata(ocp, (void*)port);
-
-       return 0;
-
-
-free_irq:
-       free_irq(port->irq, mpc52xx_uart_int);
-
-release_mem:
-       release_mem_region(port->mapbase, sizeof(struct mpc52xx_psc));
-
-error:
-       if (uart_console(port))
-               printk( "mpc52xx_uart.c: Error during resource alloction for "
-                       "the console port !!! Check that the console PSC is "
-                       "not used by another OCP driver !!!\n" );
+       if (!ret)
+               platform_set_drvdata(dev, (void*)port);
 
        return ret;
 }
 
-static void
-mpc52xx_uart_remove(struct ocp_device *ocp)
+static int
+mpc52xx_uart_remove(struct platform_device *dev)
 {
-       struct uart_port *port = (struct uart_port *) ocp_get_drvdata(ocp);
+       struct uart_port *port = (struct uart_port *) platform_get_drvdata(dev);
 
-       ocp_set_drvdata(ocp, NULL);
+       platform_set_drvdata(dev, NULL);
 
-       if (port) {
+       if (port)
                uart_remove_one_port(&mpc52xx_uart_driver, port);
-               release_mem_region(port->mapbase, sizeof(struct mpc52xx_psc));
-               free_irq(port->irq, mpc52xx_uart_int);
-       }
+
+       return 0;
 }
 
 #ifdef CONFIG_PM
 static int
-mpc52xx_uart_suspend(struct ocp_device *ocp, u32 state)
+mpc52xx_uart_suspend(struct platform_device *dev, pm_message_t state)
 {
-       struct uart_port *port = (struct uart_port *) ocp_get_drvdata(ocp);
+       struct uart_port *port = (struct uart_port *) platform_get_drvdata(dev);
 
-       uart_suspend_port(&mpc52xx_uart_driver, port);
+       if (sport)
+               uart_suspend_port(&mpc52xx_uart_driver, port);
 
        return 0;
 }
 
 static int
-mpc52xx_uart_resume(struct ocp_device *ocp)
+mpc52xx_uart_resume(struct platform_device *dev)
 {
-       struct uart_port *port = (struct uart_port *) ocp_get_drvdata(ocp);
+       struct uart_port *port = (struct uart_port *) platform_get_drvdata(dev);
 
-       uart_resume_port(&mpc52xx_uart_driver, port);
+       if (port)
+               uart_resume_port(&mpc52xx_uart_driver, port);
 
        return 0;
 }
 #endif
 
-static struct ocp_device_id mpc52xx_uart_ids[] __devinitdata = {
-       { .vendor = OCP_VENDOR_FREESCALE, .function = OCP_FUNC_PSC_UART },
-       { .vendor = OCP_VENDOR_INVALID /* Terminating entry */ }
-};
-
-MODULE_DEVICE_TABLE(ocp, mpc52xx_uart_ids);
-
-static struct ocp_driver mpc52xx_uart_ocp_driver = {
-       .name           = "mpc52xx_psc_uart",
-       .id_table       = mpc52xx_uart_ids,
+static struct platform_driver mpc52xx_uart_platform_driver = {
        .probe          = mpc52xx_uart_probe,
        .remove         = mpc52xx_uart_remove,
 #ifdef CONFIG_PM
        .suspend        = mpc52xx_uart_suspend,
        .resume         = mpc52xx_uart_resume,
 #endif
+       .driver         = {
+               .name   = "mpc52xx-psc",
+       },
 };
 
 
@@ -845,10 +817,11 @@ mpc52xx_uart_init(void)
        printk(KERN_INFO "Serial: MPC52xx PSC driver\n");
 
        ret = uart_register_driver(&mpc52xx_uart_driver);
-       if (ret)
-               return ret;
-
-       ret = ocp_register_driver(&mpc52xx_uart_ocp_driver);
+       if (ret == 0) {
+               ret = platform_driver_register(&mpc52xx_uart_platform_driver);
+               if (ret)
+                       uart_unregister_driver(&mpc52xx_uart_driver);
+       }
 
        return ret;
 }
@@ -856,7 +829,7 @@ mpc52xx_uart_init(void)
 static void __exit
 mpc52xx_uart_exit(void)
 {
-       ocp_unregister_driver(&mpc52xx_uart_ocp_driver);
+       platform_driver_unregister(&mpc52xx_uart_platform_driver);
        uart_unregister_driver(&mpc52xx_uart_driver);
 }