From afffc24530956387faa51a42a1e5a4eee344d4a1 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 19 Jul 2006 15:29:39 +0300 Subject: [PATCH] ARM: OMAP2: Dynamic allocator for GPMC memory space Add support for assigning memory regions dynamically to peripherals attached to GPMC interface. Platform specific code should now call gpmc_cs_request to get a free GPMC memory region instead of using a fixed address. Make the H4 and Apollon platform initialization use the new API. Signed-off-by: Imre Deak Signed-off-by: Juha Yrjola --- arch/arm/mach-omap2/board-apollon.c | 42 +++-- arch/arm/mach-omap2/board-h4.c | 34 ++-- arch/arm/mach-omap2/gpmc.c | 180 +++++++++++++++++++++- include/asm-arm/arch-omap/board-apollon.h | 9 -- include/asm-arm/arch-omap/board-h4.h | 1 - include/asm-arm/arch-omap/gpmc.h | 4 +- 6 files changed, 238 insertions(+), 32 deletions(-) diff --git a/arch/arm/mach-omap2/board-apollon.c b/arch/arm/mach-omap2/board-apollon.c index 6c6ba172cdf..0005127b99b 100644 --- a/arch/arm/mach-omap2/board-apollon.c +++ b/arch/arm/mach-omap2/board-apollon.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "prcm-regs.h" /* LED & Switch macros */ @@ -45,6 +46,9 @@ #define SW_UP_GPIO17 17 #define SW_DOWN_GPIO58 58 +#define APOLLON_FLASH_CS 0 +#define APOLLON_ETH_CS 1 + static struct mtd_partition apollon_partitions[] = { { .name = "X-Loader + U-Boot", @@ -84,10 +88,10 @@ static struct flash_platform_data apollon_flash_data = { .nr_parts = ARRAY_SIZE(apollon_partitions), }; -static struct resource apollon_flash_resource = { - .start = APOLLON_CS0_BASE, - .end = APOLLON_CS0_BASE + SZ_128K, - .flags = IORESOURCE_MEM, +static struct resource apollon_flash_resource[] = { + [0] = { + .flags = IORESOURCE_MEM, + }, }; static struct platform_device apollon_onenand_device = { @@ -96,14 +100,24 @@ static struct platform_device apollon_onenand_device = { .dev = { .platform_data = &apollon_flash_data, }, - .num_resources = ARRAY_SIZE(&apollon_flash_resource), - .resource = &apollon_flash_resource, + .num_resources = ARRAY_SIZE(apollon_flash_resource), + .resource = apollon_flash_resource, }; +static void __init apollon_flash_init(void) +{ + unsigned long base; + + if (gpmc_cs_request(APOLLON_FLASH_CS, SZ_128K, &base) < 0) { + printk(KERN_ERR "Cannot request OneNAND GPMC CS\n"); + return; + } + apollon_flash_resource[0].start = base; + apollon_flash_resource[0].end = base + SZ_128K - 1; +} + static struct resource apollon_smc91x_resources[] = { [0] = { - .start = APOLLON_ETHR_START, /* Physical */ - .end = APOLLON_ETHR_START + 0xf, .flags = IORESOURCE_MEM, }, [1] = { @@ -133,6 +147,8 @@ static struct platform_device *apollon_devices[] __initdata = { static inline void __init apollon_init_smc91x(void) { + unsigned long base; + /* Make sure CS1 timings are correct */ GPMC_CONFIG1_1 = 0x00011203; GPMC_CONFIG2_1 = 0x001f1f01; @@ -140,13 +156,20 @@ static inline void __init apollon_init_smc91x(void) GPMC_CONFIG4_1 = 0x1c091c09; GPMC_CONFIG5_1 = 0x041f1f1f; GPMC_CONFIG6_1 = 0x000004c4; - GPMC_CONFIG7_1 = 0x00000f40 | (APOLLON_CS1_BASE >> 24); + + if (gpmc_cs_request(APOLLON_ETH_CS, SZ_16M, &base) < 0) { + printk(KERN_ERR "Failed to request GPMC CS for smc91x\n"); + return; + } + apollon_smc91x_resources[0].start = base + 0x300; + apollon_smc91x_resources[1].end = base + 0x30f; udelay(100); omap_cfg_reg(W4__24XX_GPIO74); if (omap_request_gpio(APOLLON_ETHR_GPIO_IRQ) < 0) { printk(KERN_ERR "Failed to request GPIO%d for smc91x IRQ\n", APOLLON_ETHR_GPIO_IRQ); + gpmc_cs_free(APOLLON_ETH_CS); return; } omap_set_gpio_direction(APOLLON_ETHR_GPIO_IRQ, 1); @@ -253,6 +276,7 @@ static void __init omap_apollon_init(void) { apollon_led_init(); apollon_sw_init(); + apollon_flash_init(); /* REVISIT: where's the correct place */ omap_cfg_reg(W19_24XX_SYS_NIRQ); diff --git a/arch/arm/mach-omap2/board-h4.c b/arch/arm/mach-omap2/board-h4.c index 20a5733e962..20e513f0b98 100644 --- a/arch/arm/mach-omap2/board-h4.c +++ b/arch/arm/mach-omap2/board-h4.c @@ -44,6 +44,9 @@ #include #include +#define H4_FLASH_CS 0 +#define H4_SMC91X_CS 1 + static unsigned int row_gpios[6] = { 88, 89, 124, 11, 6, 96 }; static unsigned int col_gpios[7] = { 90, 91, 100, 36, 12, 97, 98 }; @@ -120,8 +123,6 @@ static struct flash_platform_data h4_flash_data = { }; static struct resource h4_flash_resource = { - .start = H4_CS0_BASE, - .end = H4_CS0_BASE + SZ_64M - 1, .flags = IORESOURCE_MEM, }; @@ -297,15 +298,14 @@ static u32 is_gpmc_muxed(void) return 0; } -#define SMC91X_CS 1 - static inline void __init h4_init_smc91x(void) { int eth_cs; + unsigned long cs_mem_base; unsigned int muxed, rate; struct clk *l3ck; - eth_cs = SMC91X_CS; + eth_cs = H4_SMC91X_CS; l3ck = clk_get(NULL, "core_l3_ck"); if (IS_ERR(l3ck)) @@ -342,20 +342,35 @@ static inline void __init h4_init_smc91x(void) gpmc_cs_write_reg(eth_cs, GPMC_CS_CONFIG6, 0x000003C2); } - gpmc_cs_write_reg(eth_cs, GPMC_CS_CONFIG7, - 0x00000f40 | (0x08000000 >> 24)); + if (gpmc_cs_request(eth_cs, SZ_16M, &cs_mem_base) < 0) { + printk(KERN_ERR "Failed to request GPMC mem for smc91x\n"); + return; + } + h4_smc91x_resources[0].start = cs_mem_base + 0x300; + h4_smc91x_resources[0].end = cs_mem_base + 0x30f; udelay(100); omap_cfg_reg(M15_24XX_GPIO92); if (omap_request_gpio(OMAP24XX_ETHR_GPIO_IRQ) < 0) { printk(KERN_ERR "Failed to request GPIO%d for smc91x IRQ\n", OMAP24XX_ETHR_GPIO_IRQ); + gpmc_cs_free(eth_cs); return; } omap_set_gpio_direction(OMAP24XX_ETHR_GPIO_IRQ, 1); - h4_smc91x_resources[0].start = gpmc_cs_get_base_addr(1) + 0x300; - h4_smc91x_resources[0].end = h4_smc91x_resources[0].start + 0xf; +} + +static void __init h4_init_flash(void) +{ + unsigned long base; + + if (gpmc_cs_request(H4_FLASH_CS, SZ_64M, &base) < 0) { + printk("Can't request GPMC CS for flash\n"); + return; + } + h4_flash_resource.start = base; + h4_flash_resource.end = base + SZ_64M - 1; } static void __init omap_h4_init_irq(void) @@ -364,6 +379,7 @@ static void __init omap_h4_init_irq(void) omap_init_irq(); omap_gpio_init(); h4_init_smc91x(); + h4_init_flash(); } static struct omap_uart_config h4_uart_config __initdata = { diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index e1affe7fb6a..8dfb71a6f7f 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include @@ -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 = @@ -195,9 +210,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) @@ -214,4 +388,6 @@ void __init gpmc_init(void) l &= 0x03 << 3; l |= (0x02 << 3) | (1 << 0); gpmc_write_reg(GPMC_SYSCONFIG, l); + + gpmc_mem_init(); } diff --git a/include/asm-arm/arch-omap/board-apollon.h b/include/asm-arm/arch-omap/board-apollon.h index de0c5b792c5..dcb587b311f 100644 --- a/include/asm-arm/arch-omap/board-apollon.h +++ b/include/asm-arm/arch-omap/board-apollon.h @@ -30,16 +30,7 @@ #define __ASM_ARCH_OMAP_APOLLON_H /* Placeholder for APOLLON specific defines */ -/* GPMC CS0 */ -#define APOLLON_CS0_BASE 0x00000000 -/* GPMC CS1 */ -#define APOLLON_CS1_BASE 0x08000000 -#define APOLLON_ETHR_START (APOLLON_CS1_BASE + 0x300) #define APOLLON_ETHR_GPIO_IRQ 74 -/* GPMC CS2 - reserved for OneNAND */ -#define APOLLON_CS2_BASE 0x10000000 -/* GPMC CS3 - reserved for NOR or NAND */ -#define APOLLON_CS3_BASE 0x18000000 #endif /* __ASM_ARCH_OMAP_APOLLON_H */ diff --git a/include/asm-arm/arch-omap/board-h4.h b/include/asm-arm/arch-omap/board-h4.h index bbc52d98c49..7e0efef4bb6 100644 --- a/include/asm-arm/arch-omap/board-h4.h +++ b/include/asm-arm/arch-omap/board-h4.h @@ -31,6 +31,5 @@ /* Placeholder for H4 specific defines */ #define OMAP24XX_ETHR_GPIO_IRQ 92 -#define H4_CS0_BASE 0x04000000 #endif /* __ASM_ARCH_OMAP_H4_H */ diff --git a/include/asm-arm/arch-omap/gpmc.h b/include/asm-arm/arch-omap/gpmc.h index e724d14e440..983929a9ffb 100644 --- a/include/asm-arm/arch-omap/gpmc.h +++ b/include/asm-arm/arch-omap/gpmc.h @@ -87,7 +87,7 @@ extern void gpmc_cs_write_reg(int cs, int idx, u32 val); extern u32 gpmc_cs_read_reg(int cs, int idx); extern int gpmc_cs_calc_divider(int cs, unsigned int sync_clk); extern int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t); -extern unsigned long gpmc_cs_get_base_addr(int cs); - +extern int gpmc_cs_request(int cs, unsigned long size, unsigned long *base); +extern void gpmc_cs_free(int cs); #endif -- 2.41.1