altera_uart: Add support for different address strides
[pandora-kernel.git] / drivers / serial / altera_uart.c
index 0f11896..f1985aa 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/timer.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/console.h>
  */
 struct altera_uart {
        struct uart_port port;
+       struct timer_list tmr;
        unsigned int sigs;      /* Local copy of line sigs */
        unsigned short imr;     /* Local IMR mirror */
 };
 
+static u32 altera_uart_readl(struct uart_port *port, int reg)
+{
+       struct altera_uart_platform_uart *platp = port->private_data;
+
+       return readl(port->membase + (reg << platp->bus_shift));
+}
+
+static void altera_uart_writel(struct uart_port *port, u32 dat, int reg)
+{
+       struct altera_uart_platform_uart *platp = port->private_data;
+
+       writel(dat, port->membase + (reg << platp->bus_shift));
+}
+
 static unsigned int altera_uart_tx_empty(struct uart_port *port)
 {
-       return (readl(port->membase + ALTERA_UART_STATUS_REG) &
+       return (altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
                ALTERA_UART_STATUS_TMT_MSK) ? TIOCSER_TEMT : 0;
 }
 
@@ -91,8 +107,7 @@ static unsigned int altera_uart_get_mctrl(struct uart_port *port)
        struct altera_uart *pp = container_of(port, struct altera_uart, port);
        unsigned int sigs;
 
-       sigs =
-           (readl(port->membase + ALTERA_UART_STATUS_REG) &
+       sigs = (altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
             ALTERA_UART_STATUS_CTS_MSK) ? TIOCM_CTS : 0;
        sigs |= (pp->sigs & TIOCM_RTS);
 
@@ -108,7 +123,7 @@ static void altera_uart_set_mctrl(struct uart_port *port, unsigned int sigs)
                pp->imr |= ALTERA_UART_CONTROL_RTS_MSK;
        else
                pp->imr &= ~ALTERA_UART_CONTROL_RTS_MSK;
-       writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG);
+       altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
 }
 
 static void altera_uart_start_tx(struct uart_port *port)
@@ -116,7 +131,7 @@ static void altera_uart_start_tx(struct uart_port *port)
        struct altera_uart *pp = container_of(port, struct altera_uart, port);
 
        pp->imr |= ALTERA_UART_CONTROL_TRDY_MSK;
-       writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG);
+       altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
 }
 
 static void altera_uart_stop_tx(struct uart_port *port)
@@ -124,7 +139,7 @@ static void altera_uart_stop_tx(struct uart_port *port)
        struct altera_uart *pp = container_of(port, struct altera_uart, port);
 
        pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK;
-       writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG);
+       altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
 }
 
 static void altera_uart_stop_rx(struct uart_port *port)
@@ -132,7 +147,7 @@ static void altera_uart_stop_rx(struct uart_port *port)
        struct altera_uart *pp = container_of(port, struct altera_uart, port);
 
        pp->imr &= ~ALTERA_UART_CONTROL_RRDY_MSK;
-       writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG);
+       altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
 }
 
 static void altera_uart_break_ctl(struct uart_port *port, int break_state)
@@ -145,7 +160,7 @@ static void altera_uart_break_ctl(struct uart_port *port, int break_state)
                pp->imr |= ALTERA_UART_CONTROL_TRBK_MSK;
        else
                pp->imr &= ~ALTERA_UART_CONTROL_TRBK_MSK;
-       writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG);
+       altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
        spin_unlock_irqrestore(&port->lock, flags);
 }
 
@@ -168,7 +183,8 @@ static void altera_uart_set_termios(struct uart_port *port,
        tty_termios_encode_baud_rate(termios, baud, baud);
 
        spin_lock_irqsave(&port->lock, flags);
-       writel(baudclk, port->membase + ALTERA_UART_DIVISOR_REG);
+       uart_update_timeout(port, termios->c_cflag, baud);
+       altera_uart_writel(port, baudclk, ALTERA_UART_DIVISOR_REG);
        spin_unlock_irqrestore(&port->lock, flags);
 }
 
@@ -178,14 +194,15 @@ static void altera_uart_rx_chars(struct altera_uart *pp)
        unsigned char ch, flag;
        unsigned short status;
 
-       while ((status = readl(port->membase + ALTERA_UART_STATUS_REG)) &
+       while ((status = altera_uart_readl(port, ALTERA_UART_STATUS_REG)) &
               ALTERA_UART_STATUS_RRDY_MSK) {
-               ch = readl(port->membase + ALTERA_UART_RXDATA_REG);
+               ch = altera_uart_readl(port, ALTERA_UART_RXDATA_REG);
                flag = TTY_NORMAL;
                port->icount.rx++;
 
                if (status & ALTERA_UART_STATUS_E_MSK) {
-                       writel(status, port->membase + ALTERA_UART_STATUS_REG);
+                       altera_uart_writel(port, status,
+                                          ALTERA_UART_STATUS_REG);
 
                        if (status & ALTERA_UART_STATUS_BRK_MSK) {
                                port->icount.brk++;
@@ -225,18 +242,18 @@ static void altera_uart_tx_chars(struct altera_uart *pp)
 
        if (port->x_char) {
                /* Send special char - probably flow control */
-               writel(port->x_char, port->membase + ALTERA_UART_TXDATA_REG);
+               altera_uart_writel(port, port->x_char, ALTERA_UART_TXDATA_REG);
                port->x_char = 0;
                port->icount.tx++;
                return;
        }
 
-       while (readl(port->membase + ALTERA_UART_STATUS_REG) &
+       while (altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
               ALTERA_UART_STATUS_TRDY_MSK) {
                if (xmit->head == xmit->tail)
                        break;
-               writel(xmit->buf[xmit->tail],
-                      port->membase + ALTERA_UART_TXDATA_REG);
+               altera_uart_writel(port, xmit->buf[xmit->tail],
+                      ALTERA_UART_TXDATA_REG);
                xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
                port->icount.tx++;
        }
@@ -246,7 +263,7 @@ static void altera_uart_tx_chars(struct altera_uart *pp)
 
        if (xmit->head == xmit->tail) {
                pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK;
-               writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG);
+               altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
        }
 }
 
@@ -256,7 +273,7 @@ static irqreturn_t altera_uart_interrupt(int irq, void *data)
        struct altera_uart *pp = container_of(port, struct altera_uart, port);
        unsigned int isr;
 
-       isr = readl(port->membase + ALTERA_UART_STATUS_REG) & pp->imr;
+       isr = altera_uart_readl(port, ALTERA_UART_STATUS_REG) & pp->imr;
 
        spin_lock(&port->lock);
        if (isr & ALTERA_UART_STATUS_RRDY_MSK)
@@ -268,14 +285,23 @@ static irqreturn_t altera_uart_interrupt(int irq, void *data)
        return IRQ_RETVAL(isr);
 }
 
+static void altera_uart_timer(unsigned long data)
+{
+       struct uart_port *port = (void *)data;
+       struct altera_uart *pp = container_of(port, struct altera_uart, port);
+
+       altera_uart_interrupt(0, port);
+       mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port));
+}
+
 static void altera_uart_config_port(struct uart_port *port, int flags)
 {
        port->type = PORT_ALTERA_UART;
 
        /* Clear mask, so no surprise interrupts. */
-       writel(0, port->membase + ALTERA_UART_CONTROL_REG);
+       altera_uart_writel(port, 0, ALTERA_UART_CONTROL_REG);
        /* Clear status register */
-       writel(0, port->membase + ALTERA_UART_STATUS_REG);
+       altera_uart_writel(port, 0, ALTERA_UART_STATUS_REG);
 }
 
 static int altera_uart_startup(struct uart_port *port)
@@ -284,6 +310,12 @@ static int altera_uart_startup(struct uart_port *port)
        unsigned long flags;
        int ret;
 
+       if (!port->irq) {
+               setup_timer(&pp->tmr, altera_uart_timer, (unsigned long)port);
+               mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port));
+               return 0;
+       }
+
        ret = request_irq(port->irq, altera_uart_interrupt, IRQF_DISABLED,
                        DRV_NAME, port);
        if (ret) {
@@ -316,7 +348,10 @@ static void altera_uart_shutdown(struct uart_port *port)
 
        spin_unlock_irqrestore(&port->lock, flags);
 
-       free_irq(port->irq, port);
+       if (port->irq)
+               free_irq(port->irq, port);
+       else
+               del_timer_sync(&pp->tmr);
 }
 
 static const char *altera_uart_type(struct uart_port *port)
@@ -386,6 +421,7 @@ int __init early_altera_uart_setup(struct altera_uart_platform_uart *platp)
                port->uartclk = platp[i].uartclk;
                port->flags = ASYNC_BOOT_AUTOCONF;
                port->ops = &altera_uart_ops;
+               port->private_data = platp;
        }
 
        return 0;
@@ -393,8 +429,8 @@ int __init early_altera_uart_setup(struct altera_uart_platform_uart *platp)
 
 static void altera_uart_console_putc(struct uart_port *port, const char c)
 {
-       while (!(readl(port->membase + ALTERA_UART_STATUS_REG) &
-                ALTERA_UART_STATUS_TRDY_MSK))
+       while (!(altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
+                ALTERA_UART_STATUS_TRDY_MSK))
                cpu_relax();
 
        writel(c, port->membase + ALTERA_UART_TXDATA_REG);
@@ -477,38 +513,55 @@ static int __devinit altera_uart_probe(struct platform_device *pdev)
 {
        struct altera_uart_platform_uart *platp = pdev->dev.platform_data;
        struct uart_port *port;
-       int i;
+       struct resource *res_mem;
+       struct resource *res_irq;
+       int i = pdev->id;
 
-       for (i = 0; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS && platp[i].mapbase; i++) {
-               port = &altera_uart_ports[i].port;
+       /* -1 emphasizes that the platform must have one port, no .N suffix */
+       if (i == -1)
+               i = 0;
 
-               port->line = i;
-               port->type = PORT_ALTERA_UART;
-               port->mapbase = platp[i].mapbase;
-               port->membase = ioremap(port->mapbase, ALTERA_UART_SIZE);
-               port->iotype = SERIAL_IO_MEM;
-               port->irq = platp[i].irq;
-               port->uartclk = platp[i].uartclk;
-               port->ops = &altera_uart_ops;
-               port->flags = ASYNC_BOOT_AUTOCONF;
+       if (i >= CONFIG_SERIAL_ALTERA_UART_MAXPORTS)
+               return -EINVAL;
 
-               uart_add_one_port(&altera_uart_driver, port);
-       }
+       port = &altera_uart_ports[i].port;
+
+       res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res_mem)
+               port->mapbase = res_mem->start;
+       else if (platp->mapbase)
+               port->mapbase = platp->mapbase;
+       else
+               return -EINVAL;
+
+       res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (res_irq)
+               port->irq = res_irq->start;
+       else if (platp->irq)
+               port->irq = platp->irq;
+
+       port->membase = ioremap(port->mapbase, ALTERA_UART_SIZE);
+       if (!port->membase)
+               return -ENOMEM;
+
+       port->line = i;
+       port->type = PORT_ALTERA_UART;
+       port->iotype = SERIAL_IO_MEM;
+       port->uartclk = platp->uartclk;
+       port->ops = &altera_uart_ops;
+       port->flags = ASYNC_BOOT_AUTOCONF;
+       port->private_data = platp;
+
+       uart_add_one_port(&altera_uart_driver, port);
 
        return 0;
 }
 
 static int __devexit altera_uart_remove(struct platform_device *pdev)
 {
-       struct uart_port *port;
-       int i;
-
-       for (i = 0; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS; i++) {
-               port = &altera_uart_ports[i].port;
-               if (port)
-                       uart_remove_one_port(&altera_uart_driver, port);
-       }
+       struct uart_port *port = &altera_uart_ports[pdev->id].port;
 
+       uart_remove_one_port(&altera_uart_driver, port);
        return 0;
 }