Merge branch 'drm-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied...
[pandora-kernel.git] / arch / arm / mach-omap2 / serial.c
index 2e17b57..8c964be 100644 (file)
@@ -33,6 +33,7 @@
 #include "pm.h"
 #include "prm-regbits-34xx.h"
 
+#define UART_OMAP_NO_EMPTY_FIFO_READ_IP_REV    0x52
 #define UART_OMAP_WER          0x17    /* Wake-up enable register */
 
 #define DEFAULT_TIMEOUT (5 * HZ)
@@ -124,6 +125,13 @@ static struct plat_serial8250_port serial_platform_data3[] = {
        }
 };
 #endif
+static inline unsigned int __serial_read_reg(struct uart_port *up,
+                                          int offset)
+{
+       offset <<= up->regshift;
+       return (unsigned int)__raw_readb(up->membase + offset);
+}
+
 static inline unsigned int serial_read_reg(struct plat_serial8250_port *up,
                                           int offset)
 {
@@ -572,6 +580,24 @@ static struct omap_uart_state omap_uart[] = {
 #endif
 };
 
+/*
+ * Override the default 8250 read handler: mem_serial_in()
+ * Empty RX fifo read causes an abort on omap3630 and omap4
+ * This function makes sure that an empty rx fifo is not read on these silicons
+ * (OMAP1/2/3430 are not affected)
+ */
+static unsigned int serial_in_override(struct uart_port *up, int offset)
+{
+       if (UART_RX == offset) {
+               unsigned int lsr;
+               lsr = __serial_read_reg(up, UART_LSR);
+               if (!(lsr & UART_LSR_DR))
+                       return -EPERM;
+       }
+
+       return __serial_read_reg(up, offset);
+}
+
 void __init omap_serial_early_init(void)
 {
        int i;
@@ -622,33 +648,74 @@ void __init omap_serial_early_init(void)
                uart->num = i;
                p->private_data = uart;
                uart->p = p;
-               list_add_tail(&uart->node, &uart_list);
 
                if (cpu_is_omap44xx())
                        p->irq += 32;
-
-               omap_uart_enable_clocks(uart);
        }
 }
 
-void __init omap_serial_init(void)
+/**
+ * omap_serial_init_port() - initialize single serial port
+ * @port: serial port number (0-3)
+ *
+ * This function initialies serial driver for given @port only.
+ * Platforms can call this function instead of omap_serial_init()
+ * if they don't plan to use all available UARTs as serial ports.
+ *
+ * Don't mix calls to omap_serial_init_port() and omap_serial_init(),
+ * use only one of the two.
+ */
+void __init omap_serial_init_port(int port)
 {
-       int i;
+       struct omap_uart_state *uart;
+       struct platform_device *pdev;
+       struct device *dev;
 
-       for (i = 0; i < ARRAY_SIZE(omap_uart); i++) {
-               struct omap_uart_state *uart = &omap_uart[i];
-               struct platform_device *pdev = &uart->pdev;
-               struct device *dev = &pdev->dev;
+       BUG_ON(port < 0);
+       BUG_ON(port >= ARRAY_SIZE(omap_uart));
 
-               omap_uart_reset(uart);
-               omap_uart_idle_init(uart);
+       uart = &omap_uart[port];
+       pdev = &uart->pdev;
+       dev = &pdev->dev;
 
-               if (WARN_ON(platform_device_register(pdev)))
-                       continue;
-               if ((cpu_is_omap34xx() && uart->padconf) ||
-                   (uart->wk_en && uart->wk_mask)) {
-                       device_init_wakeup(dev, true);
-                       DEV_CREATE_FILE(dev, &dev_attr_sleep_timeout);
-               }
+       omap_uart_enable_clocks(uart);
+
+       omap_uart_reset(uart);
+       omap_uart_idle_init(uart);
+
+       list_add_tail(&uart->node, &uart_list);
+
+       if (WARN_ON(platform_device_register(pdev)))
+               return;
+
+       if ((cpu_is_omap34xx() && uart->padconf) ||
+           (uart->wk_en && uart->wk_mask)) {
+               device_init_wakeup(dev, true);
+               DEV_CREATE_FILE(dev, &dev_attr_sleep_timeout);
        }
+
+               /* omap44xx: Never read empty UART fifo
+                * omap3xxx: Never read empty UART fifo on UARTs
+                * with IP rev >=0x52
+                */
+               if (cpu_is_omap44xx())
+                       uart->p->serial_in = serial_in_override;
+               else if ((serial_read_reg(uart->p, UART_OMAP_MVER) & 0xFF)
+                               >= UART_OMAP_NO_EMPTY_FIFO_READ_IP_REV)
+                       uart->p->serial_in = serial_in_override;
+}
+
+/**
+ * omap_serial_init() - intialize all supported serial ports
+ *
+ * Initializes all available UARTs as serial ports. Platforms
+ * can call this function when they want to have default behaviour
+ * for serial ports (e.g initialize them all as serial ports).
+ */
+void __init omap_serial_init(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(omap_uart); i++)
+               omap_serial_init_port(i);
 }