⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 gpmc.c

📁 linux 内核源代码
💻 C
字号:
/* * GPMC support functions * * Copyright (C) 2005-2006 Nokia Corporation * * Author: Juha Yrjola * * 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/kernel.h>#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/mach-types.h>#include <asm/arch/gpmc.h>#undef DEBUG#ifdef CONFIG_ARCH_OMAP2420#define GPMC_BASE		0x6800a000#endif#ifdef CONFIG_ARCH_OMAP2430#define GPMC_BASE		0x6E000000#endif#define GPMC_REVISION		0x00#define GPMC_SYSCONFIG		0x10#define GPMC_SYSSTATUS		0x14#define GPMC_IRQSTATUS		0x18#define GPMC_IRQENABLE		0x1c#define GPMC_TIMEOUT_CONTROL	0x40#define GPMC_ERR_ADDRESS	0x44#define GPMC_ERR_TYPE		0x48#define GPMC_CONFIG		0x50#define GPMC_STATUS		0x54#define GPMC_PREFETCH_CONFIG1	0x1e0#define GPMC_PREFETCH_CONFIG2	0x1e4#define GPMC_PREFETCH_CONTROL	0x1e8#define GPMC_PREFETCH_STATUS	0x1f0#define GPMC_ECC_CONFIG		0x1f4#define GPMC_ECC_CONTROL	0x1f8#define GPMC_ECC_SIZE_CONFIG	0x1fc#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 DEFINE_SPINLOCK(gpmc_mem_lock);static unsigned		gpmc_cs_map;static void __iomem *gpmc_base =	(void __iomem *) IO_ADDRESS(GPMC_BASE);static void __iomem *gpmc_cs_base =	(void __iomem *) IO_ADDRESS(GPMC_BASE) + GPMC_CS0;static struct clk *gpmc_l3_clk;static void gpmc_write_reg(int idx, u32 val){	__raw_writel(val, gpmc_base + idx);}static u32 gpmc_read_reg(int idx){	return __raw_readl(gpmc_base + idx);}void gpmc_cs_write_reg(int cs, int idx, u32 val){	void __iomem *reg_addr;	reg_addr = gpmc_cs_base + (cs * GPMC_CS_SIZE) + idx;	__raw_writel(val, reg_addr);}u32 gpmc_cs_read_reg(int cs, int idx){	return __raw_readl(gpmc_cs_base + (cs * GPMC_CS_SIZE) + idx);}/* TODO: Add support for gpmc_fck to clock framework and use it */unsigned long gpmc_get_fclk_period(void){	/* In picoseconds */	return 1000000000 / ((clk_get_rate(gpmc_l3_clk)) / 1000);}unsigned int gpmc_ns_to_ticks(unsigned int time_ns){	unsigned long tick_ps;	/* Calculate in picosecs to yield more exact results */	tick_ps = gpmc_get_fclk_period();	return (time_ns * 1000 + tick_ps - 1) / tick_ps;}unsigned int gpmc_round_ns_to_ticks(unsigned int time_ns){	unsigned long ticks = gpmc_ns_to_ticks(time_ns);	return ticks * gpmc_get_fclk_period() / 1000;}#ifdef DEBUGstatic int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,			       int time, const char *name)#elsestatic int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,			       int time)#endif{	u32 l;	int ticks, mask, nr_bits;	if (time == 0)		ticks = 0;	else		ticks = gpmc_ns_to_ticks(time);	nr_bits = end_bit - st_bit + 1;	if (ticks >= 1 << nr_bits) {#ifdef DEBUG		printk(KERN_INFO "GPMC CS%d: %-10s* %3d ns, %3d ticks >= %d\n",				cs, name, time, ticks, 1 << nr_bits);#endif		return -1;	}	mask = (1 << nr_bits) - 1;	l = gpmc_cs_read_reg(cs, reg);#ifdef DEBUG	printk(KERN_INFO		"GPMC CS%d: %-10s: %3d ticks, %3lu ns (was %3i ticks) %3d ns\n",	       cs, name, ticks, gpmc_get_fclk_period() * ticks / 1000,			(l >> st_bit) & mask, time);#endif	l &= ~(mask << st_bit);	l |= ticks << st_bit;	gpmc_cs_write_reg(cs, reg, l);	return 0;}#ifdef DEBUG#define GPMC_SET_ONE(reg, st, end, field) \	if (set_gpmc_timing_reg(cs, (reg), (st), (end),		\			t->field, #field) < 0)			\		return -1#else#define GPMC_SET_ONE(reg, st, end, field) \	if (set_gpmc_timing_reg(cs, (reg), (st), (end), t->field) < 0) \		return -1#endifint gpmc_cs_calc_divider(int cs, unsigned int sync_clk){	int div;	u32 l;	l = sync_clk * 1000 + (gpmc_get_fclk_period() - 1);	div = l / gpmc_get_fclk_period();	if (div > 4)		return -1;	if (div <= 0)		div = 1;	return div;}int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t){	int div;	u32 l;	div = gpmc_cs_calc_divider(cs, t->sync_clk);	if (div < 0)		return -1;	GPMC_SET_ONE(GPMC_CS_CONFIG2,  0,  3, cs_on);	GPMC_SET_ONE(GPMC_CS_CONFIG2,  8, 12, cs_rd_off);	GPMC_SET_ONE(GPMC_CS_CONFIG2, 16, 20, cs_wr_off);	GPMC_SET_ONE(GPMC_CS_CONFIG3,  0,  3, adv_on);	GPMC_SET_ONE(GPMC_CS_CONFIG3,  8, 12, adv_rd_off);	GPMC_SET_ONE(GPMC_CS_CONFIG3, 16, 20, adv_wr_off);	GPMC_SET_ONE(GPMC_CS_CONFIG4,  0,  3, oe_on);	GPMC_SET_ONE(GPMC_CS_CONFIG4,  8, 12, oe_off);	GPMC_SET_ONE(GPMC_CS_CONFIG4, 16, 19, we_on);	GPMC_SET_ONE(GPMC_CS_CONFIG4, 24, 28, we_off);	GPMC_SET_ONE(GPMC_CS_CONFIG5,  0,  4, rd_cycle);	GPMC_SET_ONE(GPMC_CS_CONFIG5,  8, 12, wr_cycle);	GPMC_SET_ONE(GPMC_CS_CONFIG5, 16, 20, access);	GPMC_SET_ONE(GPMC_CS_CONFIG5, 24, 27, page_burst_access);	/* caller is expected to have initialized CONFIG1 to cover	 * at least sync vs async	 */	l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);	if (l & (GPMC_CONFIG1_READTYPE_SYNC | GPMC_CONFIG1_WRITETYPE_SYNC)) {#ifdef DEBUG		printk(KERN_INFO "GPMC CS%d CLK period is %lu ns (div %d)\n",				cs, (div * gpmc_get_fclk_period()) / 1000, div);#endif		l &= ~0x03;		l |= (div - 1);		gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, l);	}	return 0;}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);}int gpmc_cs_set_reserved(int cs, int reserved){	if (cs > GPMC_CS_NUM)		return -ENODEV;	gpmc_cs_map &= ~(1 << cs);	gpmc_cs_map |= (reserved ? 1 : 0) << cs;	return 0;}int gpmc_cs_reserved(int cs){	if (cs > GPMC_CS_NUM)		return -ENODEV;	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;	/* never allocate the first page, to facilitate bug detection;	 * even if we didn't boot from ROM.	 */	boot_rom_space = BOOT_ROM_SPACE;	/* In apollon the CS0 is mapped as 0x0000 0000 */	if (machine_is_omap_apollon())		boot_rom_space = 0;	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){	u32 l;	gpmc_l3_clk = clk_get(NULL, "core_l3_ck");	BUG_ON(IS_ERR(gpmc_l3_clk));	l = gpmc_read_reg(GPMC_REVISION);	printk(KERN_INFO "GPMC revision %d.%d\n", (l >> 4) & 0x0f, l & 0x0f);	/* Set smart idle mode and automatic L3 clock gating */	l = gpmc_read_reg(GPMC_SYSCONFIG);	l &= 0x03 << 3;	l |= (0x02 << 3) | (1 << 0);	gpmc_write_reg(GPMC_SYSCONFIG, l);	gpmc_mem_init();}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -