diff options
Diffstat (limited to 'arch/arm/mach-omap/common.c')
-rw-r--r-- | arch/arm/mach-omap/common.c | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/arch/arm/mach-omap/common.c b/arch/arm/mach-omap/common.c new file mode 100644 index 00000000000..265cde48586 --- /dev/null +++ b/arch/arm/mach-omap/common.c @@ -0,0 +1,549 @@ +/* + * linux/arch/arm/mach-omap/common.c + * + * Code common to all OMAP machines. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#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/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}, +}; + +/* + * 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) +{ + 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 0x15: + system_rev |= 0x15; + break; + case 0x16: + case 0x17: + system_rev |= 0x16; + break; + case 0x24: + system_rev |= 0x24; + break; + default: + 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); +} + +/* + * ---------------------------------------------------------------------------- + * OMAP I/O mapping + * + * The machine specific code may provide the extra mapping besides the + * default mapping provided here. + * ---------------------------------------------------------------------------- + */ + +static struct map_desc omap_io_desc[] __initdata = { + { IO_VIRT, IO_PHYS, IO_SIZE, MT_DEVICE }, +}; + +#ifdef CONFIG_ARCH_OMAP730 +static struct map_desc omap730_io_desc[] __initdata = { + { OMAP730_DSP_BASE, OMAP730_DSP_START, OMAP730_DSP_SIZE, MT_DEVICE }, + { OMAP730_DSPREG_BASE, OMAP730_DSPREG_START, OMAP730_DSPREG_SIZE, MT_DEVICE }, + { OMAP730_SRAM_BASE, OMAP730_SRAM_START, OMAP730_SRAM_SIZE, MT_DEVICE } +}; +#endif + +#ifdef CONFIG_ARCH_OMAP1510 +static struct map_desc omap1510_io_desc[] __initdata = { + { OMAP1510_DSP_BASE, OMAP1510_DSP_START, OMAP1510_DSP_SIZE, MT_DEVICE }, + { OMAP1510_DSPREG_BASE, OMAP1510_DSPREG_START, OMAP1510_DSPREG_SIZE, MT_DEVICE }, + { OMAP1510_SRAM_BASE, OMAP1510_SRAM_START, OMAP1510_SRAM_SIZE, MT_DEVICE } +}; +#endif + +#if defined(CONFIG_ARCH_OMAP16XX) +static struct map_desc omap1610_io_desc[] __initdata = { + { 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 } +}; + +static struct map_desc omap5912_io_desc[] __initdata = { + { 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. + */ + { OMAP16XX_SRAM_BASE, OMAP16XX_SRAM_START, OMAP5912_SRAM_SIZE + 0x800, MT_DEVICE } +}; +#endif + +static int initialized = 0; + +static void __init _omap_map_io(void) +{ + initialized = 1; + + /* We have to initialize the IO space mapping before we can run + * cpu_is_omapxxx() macros. */ + iotable_init(omap_io_desc, ARRAY_SIZE(omap_io_desc)); + omap_check_revision(); + +#ifdef CONFIG_ARCH_OMAP730 + if (cpu_is_omap730()) { + iotable_init(omap730_io_desc, ARRAY_SIZE(omap730_io_desc)); + } +#endif +#ifdef CONFIG_ARCH_OMAP1510 + if (cpu_is_omap1510()) { + iotable_init(omap1510_io_desc, ARRAY_SIZE(omap1510_io_desc)); + } +#endif +#if defined(CONFIG_ARCH_OMAP16XX) + if (cpu_is_omap1610() || cpu_is_omap1710()) { + iotable_init(omap1610_io_desc, ARRAY_SIZE(omap1610_io_desc)); + } + if (cpu_is_omap5912()) { + iotable_init(omap5912_io_desc, ARRAY_SIZE(omap5912_io_desc)); + } +#endif + + /* REVISIT: Refer to OMAP5910 Errata, Advisory SYS_1: "Timeout Abort + * on a Posted Write in the TIPB Bridge". + */ + omap_writew(0x0, MPU_PUBLIC_TIPB_CNTL); + omap_writew(0x0, MPU_PRIVATE_TIPB_CNTL); + + /* Must init clocks early to assure that timer interrupt works + */ + clk_init(); +} + +/* + * This should only get called from board specific init + */ +void omap_map_io(void) +{ + if (!initialized) + _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); + } + if (cpu_is_omap1710()) { + clk_enable(clk_get(0, "uart3_ck")); + } + 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; + +static const void *get_config(u16 tag, size_t len, int skip, size_t *len_out) +{ + 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) { + if (skip == 0) + break; + skip--; + } + + if ((info->len & 0x03) != 0) { + /* We bail out to avoid an alignment fault */ + printk(KERN_ERR "OMAP peripheral config: Length (%d) not word-aligned (tag %04x)\n", + info->len, info->tag); + return NULL; + } + next = (u8 *) info + sizeof(*info) + info->len; + if (next >= omap_bootloader_tag + omap_bootloader_tag_len) + info = NULL; + else + info = (struct omap_board_config_entry *) next; + } + if (info != NULL) { + /* Check the length as a lame attempt to check for + * binary inconsistancy. */ + if (len != NO_LENGTH_CHECK) { + /* Word-align len */ + if (len & 0x03) + len = (len + 3) & ~0x03; + if (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 + /* Try to find the config from the board-specific structures + * in the kernel. */ + for (i = 0; i < omap_board_config_size; i++) { + if (omap_board_config[i].tag == tag) { + kinfo = &omap_board_config[i]; + break; + } + } + if (kinfo == NULL) + 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; + + info = omap_get_config(OMAP_TAG_UART, struct omap_uart_config); + if (info != NULL && info->console_uart) { + static char speed[11], *opt = NULL; + + if (info->console_speed) { + snprintf(speed, sizeof(speed), "%u", info->console_speed); + opt = speed; + } + return add_preferred_console("ttyS", info->console_uart - 1, opt); + } + return 0; +} +console_initcall(omap_add_serial_console); |