diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2006-09-28 14:40:39 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-09-28 14:40:39 -0700 |
commit | ebdea46fecae40c4d7effcd33f40918a37a1df4b (patch) | |
tree | e4312bf7f1f3d184738963a0ec300aa9fdfd55c1 /arch/arm/mach-omap2/gpmc.c | |
parent | fecf3404f4aba6d0edeba31eeb018cbb6326dff2 (diff) | |
parent | 250d375d1da45a5e08ab8baf5eaa7eb258afd82b (diff) |
Merge branch 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm
* 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm: (130 commits)
[ARM] 3856/1: Add clocksource for Intel IXP4xx platforms
[ARM] 3855/1: Add generic time support
[ARM] 3873/1: S3C24XX: Add irq_chip names
[ARM] 3872/1: S3C24XX: Apply consistant tabbing to irq_chips
[ARM] 3871/1: S3C24XX: Fix ordering of EINT4..23
[ARM] nommu: confirms the CR_V bit in nommu mode
[ARM] nommu: abort handler fixup for !CPU_CP15_MMU cores.
[ARM] 3870/1: AT91: Start removing static memory mappings
[ARM] 3869/1: AT91: NAND support for DK and KB9202 boards
[ARM] 3868/1: AT91 hardware header update
[ARM] 3867/1: AT91 GPIO update
[ARM] 3866/1: AT91 clock update
[ARM] 3865/1: AT91RM9200 header updates
[ARM] 3862/2: S3C2410 - add basic power management support for AML M5900 series
[ARM] kthread: switch arch/arm/kernel/apm.c
[ARM] Off-by-one in arch/arm/common/icst*
[ARM] 3864/1: Refactore sharpsl_pm
[ARM] 3863/1: Add Locomo SPI Device
[ARM] 3847/2: Convert LOMOMO to use struct device for GPIOs
[ARM] Use CPU_CACHE_* where possible in asm/cacheflush.h
...
Diffstat (limited to 'arch/arm/mach-omap2/gpmc.c')
-rw-r--r-- | arch/arm/mach-omap2/gpmc.c | 180 |
1 files changed, 178 insertions, 2 deletions
diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index c7a48f921fe..f4f04d87df3 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c @@ -13,6 +13,8 @@ #include <linux/init.h> #include <linux/err.h> #include <linux/clk.h> +#include <linux/ioport.h> +#include <linux/spinlock.h> #include <asm/io.h> #include <asm/arch/gpmc.h> @@ -41,6 +43,19 @@ #define GPMC_CS0 0x60 #define GPMC_CS_SIZE 0x30 +#define GPMC_CS_NUM 8 +#define GPMC_MEM_START 0x00000000 +#define GPMC_MEM_END 0x3FFFFFFF +#define BOOT_ROM_SPACE 0x100000 /* 1MB */ + +#define GPMC_CHUNK_SHIFT 24 /* 16 MB */ +#define GPMC_SECTION_SHIFT 28 /* 128 MB */ + +static struct resource gpmc_mem_root; +static struct resource gpmc_cs_mem[GPMC_CS_NUM]; +static spinlock_t gpmc_mem_lock = SPIN_LOCK_UNLOCKED; +static unsigned gpmc_cs_map; + static void __iomem *gpmc_base = (void __iomem *) IO_ADDRESS(GPMC_BASE); static void __iomem *gpmc_cs_base = @@ -187,9 +202,168 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t) return 0; } -unsigned long gpmc_cs_get_base_addr(int cs) +static void gpmc_cs_enable_mem(int cs, u32 base, u32 size) +{ + u32 l; + u32 mask; + + mask = (1 << GPMC_SECTION_SHIFT) - size; + l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7); + l &= ~0x3f; + l = (base >> GPMC_CHUNK_SHIFT) & 0x3f; + l &= ~(0x0f << 8); + l |= ((mask >> GPMC_CHUNK_SHIFT) & 0x0f) << 8; + l |= 1 << 6; /* CSVALID */ + gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, l); +} + +static void gpmc_cs_disable_mem(int cs) +{ + u32 l; + + l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7); + l &= ~(1 << 6); /* CSVALID */ + gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, l); +} + +static void gpmc_cs_get_memconf(int cs, u32 *base, u32 *size) +{ + u32 l; + u32 mask; + + l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7); + *base = (l & 0x3f) << GPMC_CHUNK_SHIFT; + mask = (l >> 8) & 0x0f; + *size = (1 << GPMC_SECTION_SHIFT) - (mask << GPMC_CHUNK_SHIFT); +} + +static int gpmc_cs_mem_enabled(int cs) +{ + u32 l; + + l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7); + return l & (1 << 6); +} + +static void gpmc_cs_set_reserved(int cs, int reserved) { - return (gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7) & 0x1f) << 24; + gpmc_cs_map &= ~(1 << cs); + gpmc_cs_map |= (reserved ? 1 : 0) << cs; +} + +static int gpmc_cs_reserved(int cs) +{ + return gpmc_cs_map & (1 << cs); +} + +static unsigned long gpmc_mem_align(unsigned long size) +{ + int order; + + size = (size - 1) >> (GPMC_CHUNK_SHIFT - 1); + order = GPMC_CHUNK_SHIFT - 1; + do { + size >>= 1; + order++; + } while (size); + size = 1 << order; + return size; +} + +static int gpmc_cs_insert_mem(int cs, unsigned long base, unsigned long size) +{ + struct resource *res = &gpmc_cs_mem[cs]; + int r; + + size = gpmc_mem_align(size); + spin_lock(&gpmc_mem_lock); + res->start = base; + res->end = base + size - 1; + r = request_resource(&gpmc_mem_root, res); + spin_unlock(&gpmc_mem_lock); + + return r; +} + +int gpmc_cs_request(int cs, unsigned long size, unsigned long *base) +{ + struct resource *res = &gpmc_cs_mem[cs]; + int r = -1; + + if (cs > GPMC_CS_NUM) + return -ENODEV; + + size = gpmc_mem_align(size); + if (size > (1 << GPMC_SECTION_SHIFT)) + return -ENOMEM; + + spin_lock(&gpmc_mem_lock); + if (gpmc_cs_reserved(cs)) { + r = -EBUSY; + goto out; + } + if (gpmc_cs_mem_enabled(cs)) + r = adjust_resource(res, res->start & ~(size - 1), size); + if (r < 0) + r = allocate_resource(&gpmc_mem_root, res, size, 0, ~0, + size, NULL, NULL); + if (r < 0) + goto out; + + gpmc_cs_enable_mem(cs, res->start, res->end - res->start + 1); + *base = res->start; + gpmc_cs_set_reserved(cs, 1); +out: + spin_unlock(&gpmc_mem_lock); + return r; +} + +void gpmc_cs_free(int cs) +{ + spin_lock(&gpmc_mem_lock); + if (cs >= GPMC_CS_NUM || !gpmc_cs_reserved(cs)) { + printk(KERN_ERR "Trying to free non-reserved GPMC CS%d\n", cs); + BUG(); + spin_unlock(&gpmc_mem_lock); + return; + } + gpmc_cs_disable_mem(cs); + release_resource(&gpmc_cs_mem[cs]); + gpmc_cs_set_reserved(cs, 0); + spin_unlock(&gpmc_mem_lock); +} + +void __init gpmc_mem_init(void) +{ + int cs; + unsigned long boot_rom_space = 0; + + if (cpu_is_omap242x()) { + u32 l; + l = omap_readl(OMAP242X_CONTROL_STATUS); + /* In case of internal boot the 1st MB is redirected to the + * boot ROM memory space. + */ + if (l & (1 << 3)) + boot_rom_space = BOOT_ROM_SPACE; + } else + /* We assume internal boot if the mode can't be + * determined. + */ + boot_rom_space = BOOT_ROM_SPACE; + gpmc_mem_root.start = GPMC_MEM_START + boot_rom_space; + gpmc_mem_root.end = GPMC_MEM_END; + + /* Reserve all regions that has been set up by bootloader */ + for (cs = 0; cs < GPMC_CS_NUM; cs++) { + u32 base, size; + + if (!gpmc_cs_mem_enabled(cs)) + continue; + gpmc_cs_get_memconf(cs, &base, &size); + if (gpmc_cs_insert_mem(cs, base, size) < 0) + BUG(); + } } void __init gpmc_init(void) @@ -206,4 +380,6 @@ void __init gpmc_init(void) l &= 0x03 << 3; l |= (0x02 << 3) | (1 << 0); gpmc_write_reg(GPMC_SYSCONFIG, l); + + gpmc_mem_init(); } |