vserver 1.9.5.x5
[linux-2.6.git] / arch / arm / mach-omap / common.c
index 17d82a7..6cc8c21 100644 (file)
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/console.h>
+#include <linux/serial.h>
+#include <linux/tty.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_reg.h>
 
 #include <asm/hardware.h>
 #include <asm/system.h>
 #include <asm/pgtable.h>
 #include <asm/mach/map.h>
-#include <asm/arch/clocks.h>
-#include <asm/arch/board.h>
+#include <asm/hardware/clock.h>
 #include <asm/io.h>
+#include <asm/mach-types.h>
+
+#include <asm/arch/board.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/fpga.h>
+
+#include "clock.h"
+
+#define DEBUG 1
+
+struct omap_id {
+       u16     jtag_id;        /* Used to determine OMAP type */
+       u8      die_rev;        /* Processor revision */
+       u32     omap_id;        /* OMAP revision */
+       u32     type;           /* Cpu id bits [31:08], cpu class bits [07:00] */
+};
+
+/* Register values to detect the OMAP version */
+static struct omap_id omap_ids[] __initdata = {
+       { .jtag_id = 0x355f, .die_rev = 0x0, .omap_id = 0x03320000, .type = 0x07300100},
+       { .jtag_id = 0xb55f, .die_rev = 0x0, .omap_id = 0x03320000, .type = 0x07300300},
+       { .jtag_id = 0xb470, .die_rev = 0x0, .omap_id = 0x03310100, .type = 0x15100000},
+       { .jtag_id = 0xb576, .die_rev = 0x0, .omap_id = 0x03320000, .type = 0x16100000},
+       { .jtag_id = 0xb576, .die_rev = 0x2, .omap_id = 0x03320100, .type = 0x16110000},
+       { .jtag_id = 0xb576, .die_rev = 0x3, .omap_id = 0x03320100, .type = 0x16100c00},
+       { .jtag_id = 0xb576, .die_rev = 0x0, .omap_id = 0x03320200, .type = 0x16100d00},
+       { .jtag_id = 0xb613, .die_rev = 0x0, .omap_id = 0x03320300, .type = 0x1610ef00},
+       { .jtag_id = 0xb613, .die_rev = 0x0, .omap_id = 0x03320300, .type = 0x1610ef00},
+       { .jtag_id = 0xb576, .die_rev = 0x1, .omap_id = 0x03320100, .type = 0x16110000},
+       { .jtag_id = 0xb58c, .die_rev = 0x2, .omap_id = 0x03320200, .type = 0x16110b00},
+       { .jtag_id = 0xb58c, .die_rev = 0x3, .omap_id = 0x03320200, .type = 0x16110c00},
+       { .jtag_id = 0xb65f, .die_rev = 0x0, .omap_id = 0x03320400, .type = 0x16212300},
+       { .jtag_id = 0xb65f, .die_rev = 0x1, .omap_id = 0x03320400, .type = 0x16212300},
+       { .jtag_id = 0xb65f, .die_rev = 0x1, .omap_id = 0x03320500, .type = 0x16212300},
+       { .jtag_id = 0xb5f7, .die_rev = 0x0, .omap_id = 0x03330000, .type = 0x17100000},
+       { .jtag_id = 0xb5f7, .die_rev = 0x1, .omap_id = 0x03330100, .type = 0x17100000},
+       { .jtag_id = 0xb5f7, .die_rev = 0x2, .omap_id = 0x03330100, .type = 0x17100000},
+};
 
 /*
- * ----------------------------------------------------------------------------
- * OMAP revision check
- *
- * Since we use the cpu_is_omapnnnn() macros, there's a chance that a board
- * switches to an updated core. We want to print out the OMAP revision early.
- *
- * We use the system_serial registers for the revision information so we
- * can see it in /proc/cpuinfo.
- *
- * If the OMAP detection gets more complicated, we may want to expand this
- * to store the OMAP version and replace the current cpu_is_omapnnnn() macros.
- *
- * ----------------------------------------------------------------------------
+ * Get OMAP type from PROD_ID.
+ * 1710 has the PROD_ID in bits 15:00, not in 16:01 as documented in TRM.
+ * 1510 PROD_ID is empty, and 1610 PROD_ID does not make sense.
+ * Undocumented register in TEST BLOCK is used as fallback; This seems to
+ * work on 1510, 1610 & 1710. The official way hopefully will work in future
+ * processors.
+ */
+static u16 __init omap_get_jtag_id(void)
+{
+       u32 prod_id, omap_id;
+
+       prod_id = omap_readl(OMAP_PRODUCTION_ID_1);
+       omap_id = omap_readl(OMAP32_ID_1);
+
+       /* Check for unusable OMAP_PRODUCTION_ID_1 on 1611B/5912 and 730 */
+       if (((prod_id >> 20) == 0) || (prod_id == omap_id))
+               prod_id = 0;
+       else
+               prod_id &= 0xffff;
+
+       if (prod_id)
+               return prod_id;
+
+       /* Use OMAP32_ID_1 as fallback */
+       prod_id = ((omap_id >> 12) & 0xffff);
+
+       return prod_id;
+}
+
+/*
+ * Get OMAP revision from DIE_REV.
+ * Early 1710 processors may have broken OMAP_DIE_ID, it contains PROD_ID.
+ * Undocumented register in the TEST BLOCK is used as fallback.
+ * REVISIT: This does not seem to work on 1510
  */
+static u8 __init omap_get_die_rev(void)
+{
+       u32 die_rev;
+
+       die_rev = omap_readl(OMAP_DIE_ID_1);
+
+       /* Check for broken OMAP_DIE_ID on early 1710 */
+       if (((die_rev >> 12) & 0xffff) == omap_get_jtag_id())
+               die_rev = 0;
+
+       die_rev = (die_rev >> 17) & 0xf;
+       if (die_rev)
+               return die_rev;
+
+       die_rev = (omap_readl(OMAP32_ID_1) >> 28) & 0xf;
+
+       return die_rev;
+}
+
 static void __init omap_check_revision(void)
 {
-       system_serial_high = omap_readl(OMAP_ID_BASE);
-       system_serial_low = OMAP_ID_REG;
-       system_rev = (OMAP_ID_REG >> ID_SHIFT) & ID_MASK;
-
-       printk("OMAP revision: %d.%d (0x%08x) id: 0x%08x detected as OMAP-",
-              (system_serial_high >> 20) & 0xf,
-              (system_serial_high >> 16) & 0xf,
-              system_serial_high, system_serial_low);
-
-       switch (system_rev) {
-       case OMAP_ID_730:
-               printk("730\n");
-               system_rev = 0x730;
-               break;
-       case OMAP_ID_1510:
-               printk("1510\n");
-               system_rev = 0x1510;
+       int i;
+       u16 jtag_id;
+       u8 die_rev;
+       u32 omap_id;
+       u8 cpu_type;
+
+       jtag_id = omap_get_jtag_id();
+       die_rev = omap_get_die_rev();
+       omap_id = omap_readl(OMAP32_ID_0);
+
+#ifdef DEBUG
+       printk("OMAP_DIE_ID_0: 0x%08x\n", omap_readl(OMAP_DIE_ID_0));
+       printk("OMAP_DIE_ID_1: 0x%08x DIE_REV: %i\n",
+               omap_readl(OMAP_DIE_ID_1),
+              (omap_readl(OMAP_DIE_ID_1) >> 17) & 0xf);
+       printk("OMAP_PRODUCTION_ID_0: 0x%08x\n", omap_readl(OMAP_PRODUCTION_ID_0));
+       printk("OMAP_PRODUCTION_ID_1: 0x%08x JTAG_ID: 0x%04x\n",
+               omap_readl(OMAP_PRODUCTION_ID_1),
+               omap_readl(OMAP_PRODUCTION_ID_1) & 0xffff);
+       printk("OMAP32_ID_0: 0x%08x\n", omap_readl(OMAP32_ID_0));
+       printk("OMAP32_ID_1: 0x%08x\n", omap_readl(OMAP32_ID_1));
+       printk("JTAG_ID: 0x%04x DIE_REV: %i\n", jtag_id, die_rev);
+#endif
+
+       system_serial_high = omap_readl(OMAP_DIE_ID_0);
+       system_serial_low = omap_readl(OMAP_DIE_ID_1);
+
+       /* First check only the major version in a safe way */
+       for (i = 0; i < ARRAY_SIZE(omap_ids); i++) {
+               if (jtag_id == (omap_ids[i].jtag_id)) {
+                       system_rev = omap_ids[i].type;
+                       break;
+               }
+       }
+
+       /* Check if we can find the die revision */
+       for (i = 0; i < ARRAY_SIZE(omap_ids); i++) {
+               if (jtag_id == omap_ids[i].jtag_id && die_rev == omap_ids[i].die_rev) {
+                       system_rev = omap_ids[i].type;
+                       break;
+               }
+       }
+
+       /* Finally check also the omap_id */
+       for (i = 0; i < ARRAY_SIZE(omap_ids); i++) {
+               if (jtag_id == omap_ids[i].jtag_id
+                   && die_rev == omap_ids[i].die_rev
+                   && omap_id == omap_ids[i].omap_id) {
+                       system_rev = omap_ids[i].type;
+                       break;
+               }
+       }
+
+       /* Add the cpu class info (7xx, 15xx, 16xx, 24xx) */
+       cpu_type = system_rev >> 24;
+
+       switch (cpu_type) {
+       case 0x07:
+               system_rev |= 0x07;
                break;
-       case OMAP_ID_1610:
-               printk("1610\n");
-               system_rev = 0x1610;
+       case 0x15:
+               system_rev |= 0x15;
                break;
-       case OMAP_ID_1710:
-               printk("1710\n");
-               system_rev = 0x1710;
+       case 0x16:
+       case 0x17:
+               system_rev |= 0x16;
                break;
-       case OMAP_ID_5912:
-               printk("5912/1611B\n");
-               system_rev = 0x5912;
+       case 0x24:
+               system_rev |= 0x24;
                break;
        default:
-               printk("unknown, please add support!\n");
+               printk("Unknown OMAP cpu type: 0x%02x\n", cpu_type);
        }
+
+       printk("OMAP%04x", system_rev >> 16);
+       if ((system_rev >> 8) & 0xff)
+               printk("%x", (system_rev >> 8) & 0xff);
+       printk(" revision %i handled as %02xxx id: %08x%08x\n",
+              die_rev, system_rev & 0xff, system_serial_low,
+              system_serial_high);
 }
 
 /*
@@ -104,25 +232,23 @@ static struct map_desc omap1510_io_desc[] __initdata = {
 };
 #endif
 
-#if defined(CONFIG_ARCH_OMAP1610) || defined(CONFIG_ARCH_OMAP1710)
+#if defined(CONFIG_ARCH_OMAP16XX)
 static struct map_desc omap1610_io_desc[] __initdata = {
- { OMAP1610_DSP_BASE,    OMAP1610_DSP_START,    OMAP1610_DSP_SIZE,    MT_DEVICE },
- { OMAP1610_DSPREG_BASE, OMAP1610_DSPREG_START, OMAP1610_DSPREG_SIZE, MT_DEVICE },
- { OMAP1610_SRAM_BASE,   OMAP1610_SRAM_START,   OMAP1610_SRAM_SIZE,   MT_DEVICE }
+ { OMAP16XX_DSP_BASE,    OMAP16XX_DSP_START,    OMAP16XX_DSP_SIZE,    MT_DEVICE },
+ { OMAP16XX_DSPREG_BASE, OMAP16XX_DSPREG_START, OMAP16XX_DSPREG_SIZE, MT_DEVICE },
+ { OMAP16XX_SRAM_BASE,   OMAP16XX_SRAM_START,   OMAP1610_SRAM_SIZE,   MT_DEVICE }
 };
-#endif
 
-#ifdef CONFIG_ARCH_OMAP5912
 static struct map_desc omap5912_io_desc[] __initdata = {
- { OMAP5912_DSP_BASE,    OMAP5912_DSP_START,    OMAP5912_DSP_SIZE,    MT_DEVICE },
- { OMAP5912_DSPREG_BASE, OMAP5912_DSPREG_START, OMAP5912_DSPREG_SIZE, MT_DEVICE },
+ { OMAP16XX_DSP_BASE,    OMAP16XX_DSP_START,    OMAP16XX_DSP_SIZE,    MT_DEVICE },
+ { OMAP16XX_DSPREG_BASE, OMAP16XX_DSPREG_START, OMAP16XX_DSPREG_SIZE, MT_DEVICE },
 /*
  * The OMAP5912 has 250kByte internal SRAM. Because the mapping is baseed on page
  * size (4kByte), it seems that the last 2kByte (=0x800) of the 250kByte are not mapped.
  * Add additional 2kByte (0x800) so that the last page is mapped and the last 2kByte
  * can be used.
  */
- { OMAP5912_SRAM_BASE,   OMAP5912_SRAM_START,   OMAP5912_SRAM_SIZE + 0x800,   MT_DEVICE }
+ { OMAP16XX_SRAM_BASE,   OMAP16XX_SRAM_START,   OMAP5912_SRAM_SIZE + 0x800,   MT_DEVICE }
 };
 #endif
 
@@ -137,6 +263,9 @@ static void __init _omap_map_io(void)
        iotable_init(omap_io_desc, ARRAY_SIZE(omap_io_desc));
        omap_check_revision();
 
+       /* clear BM to canonicalize CS0 (not CS3) at 0000:0000 */
+       omap_writel(omap_readl(EMIFS_CONFIG) & 0x0d, EMIFS_CONFIG);
+
 #ifdef CONFIG_ARCH_OMAP730
        if (cpu_is_omap730()) {
                iotable_init(omap730_io_desc, ARRAY_SIZE(omap730_io_desc));
@@ -147,12 +276,10 @@ static void __init _omap_map_io(void)
                iotable_init(omap1510_io_desc, ARRAY_SIZE(omap1510_io_desc));
        }
 #endif
-#if defined(CONFIG_ARCH_OMAP1610) || defined(CONFIG_ARCH_OMAP1710)
+#if defined(CONFIG_ARCH_OMAP16XX)
        if (cpu_is_omap1610() || cpu_is_omap1710()) {
                iotable_init(omap1610_io_desc, ARRAY_SIZE(omap1610_io_desc));
        }
-#endif
-#ifdef CONFIG_ARCH_OMAP5912
        if (cpu_is_omap5912()) {
                iotable_init(omap5912_io_desc, ARRAY_SIZE(omap5912_io_desc));
        }
@@ -166,7 +293,7 @@ static void __init _omap_map_io(void)
 
        /* Must init clocks early to assure that timer interrupt works
         */
-       init_ck();
+       clk_init();
 }
 
 /*
@@ -178,26 +305,175 @@ void omap_map_io(void)
                _omap_map_io();
 }
 
+static inline unsigned int omap_serial_in(struct plat_serial8250_port *up,
+                                         int offset)
+{
+       offset <<= up->regshift;
+       return (unsigned int)__raw_readb(up->membase + offset);
+}
+
+static inline void omap_serial_outp(struct plat_serial8250_port *p, int offset,
+                                   int value)
+{
+       offset <<= p->regshift;
+       __raw_writeb(value, p->membase + offset);
+}
+
+/*
+ * Internal UARTs need to be initialized for the 8250 autoconfig to work
+ * properly. Note that the TX watermark initialization may not be needed
+ * once the 8250.c watermark handling code is merged.
+ */
+static void __init omap_serial_reset(struct plat_serial8250_port *p)
+{
+       omap_serial_outp(p, UART_OMAP_MDR1, 0x07);      /* disable UART */
+       omap_serial_outp(p, UART_OMAP_SCR, 0x08);       /* TX watermark */
+       omap_serial_outp(p, UART_OMAP_MDR1, 0x00);      /* enable UART */
+
+       if (!cpu_is_omap1510()) {
+               omap_serial_outp(p, UART_OMAP_SYSC, 0x01);
+               while (!(omap_serial_in(p, UART_OMAP_SYSC) & 0x01));
+       }
+}
+
+static struct plat_serial8250_port serial_platform_data[] = {
+       {
+               .membase        = (char*)IO_ADDRESS(OMAP_UART1_BASE),
+               .mapbase        = (unsigned long)OMAP_UART1_BASE,
+               .irq            = INT_UART1,
+               .flags          = UPF_BOOT_AUTOCONF,
+               .iotype         = UPIO_MEM,
+               .regshift       = 2,
+               .uartclk        = OMAP16XX_BASE_BAUD * 16,
+       },
+       {
+               .membase        = (char*)IO_ADDRESS(OMAP_UART2_BASE),
+               .mapbase        = (unsigned long)OMAP_UART2_BASE,
+               .irq            = INT_UART2,
+               .flags          = UPF_BOOT_AUTOCONF,
+               .iotype         = UPIO_MEM,
+               .regshift       = 2,
+               .uartclk        = OMAP16XX_BASE_BAUD * 16,
+       },
+       {
+               .membase        = (char*)IO_ADDRESS(OMAP_UART3_BASE),
+               .mapbase        = (unsigned long)OMAP_UART3_BASE,
+               .irq            = INT_UART3,
+               .flags          = UPF_BOOT_AUTOCONF,
+               .iotype         = UPIO_MEM,
+               .regshift       = 2,
+               .uartclk        = OMAP16XX_BASE_BAUD * 16,
+       },
+       { },
+};
+
+static struct platform_device serial_device = {
+       .name                   = "serial8250",
+       .id                     = 0,
+       .dev                    = {
+               .platform_data  = serial_platform_data,
+       },
+};
+
+/*
+ * Note that on Innovator-1510 UART2 pins conflict with USB2.
+ * By default UART2 does not work on Innovator-1510 if you have
+ * USB OHCI enabled. To use UART2, you must disable USB2 first.
+ */
+void __init omap_serial_init(int ports[OMAP_MAX_NR_PORTS])
+{
+       int i;
+
+       if (cpu_is_omap730()) {
+               serial_platform_data[0].regshift = 0;
+               serial_platform_data[1].regshift = 0;
+               serial_platform_data[0].irq = INT_730_UART_MODEM_1;
+               serial_platform_data[1].irq = INT_730_UART_MODEM_IRDA_2;
+       }
+
+       if (cpu_is_omap1510()) {
+               serial_platform_data[0].uartclk = OMAP1510_BASE_BAUD * 16;
+               serial_platform_data[1].uartclk = OMAP1510_BASE_BAUD * 16;
+               serial_platform_data[2].uartclk = OMAP1510_BASE_BAUD * 16;
+       }
+
+       for (i = 0; i < OMAP_MAX_NR_PORTS; i++) {
+               unsigned char reg;
+
+               if (ports[i] == 0) {
+                       serial_platform_data[i].membase = 0;
+                       serial_platform_data[i].mapbase = 0;
+                       continue;
+               }
+
+               switch (i) {
+               case 0:
+                       if (cpu_is_omap1510()) {
+                               omap_cfg_reg(UART1_TX);
+                               omap_cfg_reg(UART1_RTS);
+                               if (machine_is_omap_innovator()) {
+                                       reg = fpga_read(OMAP1510_FPGA_POWER);
+                                       reg |= OMAP1510_FPGA_PCR_COM1_EN;
+                                       fpga_write(reg, OMAP1510_FPGA_POWER);
+                                       udelay(10);
+                               }
+                       }
+                       break;
+               case 1:
+                       if (cpu_is_omap1510()) {
+                               omap_cfg_reg(UART2_TX);
+                               omap_cfg_reg(UART2_RTS);
+                               if (machine_is_omap_innovator()) {
+                                       reg = fpga_read(OMAP1510_FPGA_POWER);
+                                       reg |= OMAP1510_FPGA_PCR_COM2_EN;
+                                       fpga_write(reg, OMAP1510_FPGA_POWER);
+                                       udelay(10);
+                               }
+                       }
+                       break;
+               case 2:
+                       if (cpu_is_omap1510()) {
+                               omap_cfg_reg(UART3_TX);
+                               omap_cfg_reg(UART3_RX);
+                       }
+                       break;
+               }
+               omap_serial_reset(&serial_platform_data[i]);
+       }
+}
+
+static int __init omap_init(void)
+{
+       return platform_device_register(&serial_device);
+}
+arch_initcall(omap_init);
+
+#define NO_LENGTH_CHECK 0xffffffff
+
 extern int omap_bootloader_tag_len;
 extern u8 omap_bootloader_tag[];
 
 struct omap_board_config_kernel *omap_board_config;
 int omap_board_config_size = 0;
 
-const void *__omap_get_config(u16 tag, size_t len)
+static const void *get_config(u16 tag, size_t len, int skip, size_t *len_out)
 {
-       struct omap_board_config_entry *info = NULL;
        struct omap_board_config_kernel *kinfo = NULL;
        int i;
 
 #ifdef CONFIG_OMAP_BOOT_TAG
+       struct omap_board_config_entry *info = NULL;
+
        if (omap_bootloader_tag_len > 4)
                info = (struct omap_board_config_entry *) omap_bootloader_tag;
        while (info != NULL) {
                u8 *next;
 
-               if (info->tag == tag)
-                       break;
+               if (info->tag == tag) {
+                       if (skip == 0)
+                               break;
+                       skip--;
+               }
 
                next = (u8 *) info + sizeof(*info) + info->len;
                if (next >= omap_bootloader_tag + omap_bootloader_tag_len)
@@ -208,11 +484,13 @@ const void *__omap_get_config(u16 tag, size_t len)
        if (info != NULL) {
                /* Check the length as a lame attempt to check for
                 * binary inconsistancy. */
-               if (info->len != len) {
+               if (len != NO_LENGTH_CHECK && info->len != len) {
                        printk(KERN_ERR "OMAP peripheral config: Length mismatch with tag %x (want %d, got %d)\n",
                               tag, len, info->len);
                        return NULL;
                }
+               if (len_out != NULL)
+                       *len_out = info->len;
                return info->data;
        }
 #endif
@@ -228,8 +506,19 @@ const void *__omap_get_config(u16 tag, size_t len)
                return NULL;
        return kinfo->data;
 }
+
+const void *__omap_get_config(u16 tag, size_t len, int nr)
+{
+        return get_config(tag, len, nr, NULL);
+}
 EXPORT_SYMBOL(__omap_get_config);
 
+const void *omap_get_var_config(u16 tag, size_t *len)
+{
+        return get_config(tag, NO_LENGTH_CHECK, 0, len);
+}
+EXPORT_SYMBOL(omap_get_var_config);
+
 static int __init omap_add_serial_console(void)
 {
        const struct omap_uart_config *info;