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

📄 omap2.c

📁 基于linux-2.6.28的mtd驱动
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  linux/drivers/mtd/onenand/omap2.c * *  OneNAND driver for OMAP2 / OMAP3 * *  Copyright © 2005-2006 Nokia Corporation * *  Author: Jarkko Lavinen <jarkko.lavinen@nokia.com> and Juha Yrjölä *  IRQ and DMA support written by Timo Teras * * 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. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */#include <linux/device.h>#include <linux/module.h>#include <linux/init.h>#include <linux/mtd/mtd.h>#include <linux/mtd/onenand.h>#include <linux/mtd/partitions.h>#include <linux/platform_device.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/dma-mapping.h>#include <linux/io.h>#include <asm/mach/flash.h>#include <mach/gpmc.h>#include <mach/onenand.h>#include <mach/gpio.h>#include <mach/pm.h>#include <mach/dma.h>#include <mach/board.h>#define DRIVER_NAME "omap2-onenand"#define ONENAND_IO_SIZE		SZ_128K#define ONENAND_BUFRAM_SIZE	(1024 * 5)struct omap2_onenand {	struct platform_device *pdev;	int gpmc_cs;	unsigned long phys_base;	int gpio_irq;	struct mtd_info mtd;	struct mtd_partition *parts;	struct onenand_chip onenand;	struct completion irq_done;	struct completion dma_done;	int dma_channel;	int freq;	int (*setup)(void __iomem *base, int freq);};static void omap2_onenand_dma_cb(int lch, u16 ch_status, void *data){	struct omap2_onenand *c = data;	complete(&c->dma_done);}static irqreturn_t omap2_onenand_interrupt(int irq, void *dev_id){	struct omap2_onenand *c = dev_id;	complete(&c->irq_done);	return IRQ_HANDLED;}static inline unsigned short read_reg(struct omap2_onenand *c, int reg){	return readw(c->onenand.base + reg);}static inline void write_reg(struct omap2_onenand *c, unsigned short value,			     int reg){	writew(value, c->onenand.base + reg);}static void wait_err(char *msg, int state, unsigned int ctrl, unsigned int intr){	printk(KERN_ERR "onenand_wait: %s! state %d ctrl 0x%04x intr 0x%04x\n",	       msg, state, ctrl, intr);}static void wait_warn(char *msg, int state, unsigned int ctrl,		      unsigned int intr){	printk(KERN_WARNING "onenand_wait: %s! state %d ctrl 0x%04x "	       "intr 0x%04x\n", msg, state, ctrl, intr);}static int omap2_onenand_wait(struct mtd_info *mtd, int state){	struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);	unsigned int intr = 0;	unsigned int ctrl;	unsigned long timeout;	u32 syscfg;	if (state == FL_RESETING) {		int i;		for (i = 0; i < 20; i++) {			udelay(1);			intr = read_reg(c, ONENAND_REG_INTERRUPT);			if (intr & ONENAND_INT_MASTER)				break;		}		ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);		if (ctrl & ONENAND_CTRL_ERROR) {			wait_err("controller error", state, ctrl, intr);			return -EIO;		}		if (!(intr & ONENAND_INT_RESET)) {			wait_err("timeout", state, ctrl, intr);			return -EIO;		}		return 0;	}	if (state != FL_READING) {		int result;		/* Turn interrupts on */		syscfg = read_reg(c, ONENAND_REG_SYS_CFG1);		if (!(syscfg & ONENAND_SYS_CFG1_IOBE)) {			syscfg |= ONENAND_SYS_CFG1_IOBE;			write_reg(c, syscfg, ONENAND_REG_SYS_CFG1);			if (cpu_is_omap34xx())				/* Add a delay to let GPIO settle */				syscfg = read_reg(c, ONENAND_REG_SYS_CFG1);		}		INIT_COMPLETION(c->irq_done);		if (c->gpio_irq) {			result = omap_get_gpio_datain(c->gpio_irq);			if (result == -1) {				ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);				intr = read_reg(c, ONENAND_REG_INTERRUPT);				wait_err("gpio error", state, ctrl, intr);				return -EIO;			}		} else			result = 0;		if (result == 0) {			int retry_cnt = 0;retry:			result = wait_for_completion_timeout(&c->irq_done,						    msecs_to_jiffies(20));			if (result == 0) {				/* Timeout after 20ms */				ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);				if (ctrl & ONENAND_CTRL_ONGO) {					/*					 * The operation seems to be still going					 * so give it some more time.					 */					retry_cnt += 1;					if (retry_cnt < 3)						goto retry;					intr = read_reg(c,							ONENAND_REG_INTERRUPT);					wait_err("timeout", state, ctrl, intr);					return -EIO;				}				intr = read_reg(c, ONENAND_REG_INTERRUPT);				if ((intr & ONENAND_INT_MASTER) == 0)					wait_warn("timeout", state, ctrl, intr);			}		}	} else {		int retry_cnt = 0;		/* Turn interrupts off */		syscfg = read_reg(c, ONENAND_REG_SYS_CFG1);		syscfg &= ~ONENAND_SYS_CFG1_IOBE;		write_reg(c, syscfg, ONENAND_REG_SYS_CFG1);		timeout = jiffies + msecs_to_jiffies(20);		while (1) {			if (time_before(jiffies, timeout)) {				intr = read_reg(c, ONENAND_REG_INTERRUPT);				if (intr & ONENAND_INT_MASTER)					break;			} else {				/* Timeout after 20ms */				ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);				if (ctrl & ONENAND_CTRL_ONGO) {					/*					 * The operation seems to be still going					 * so give it some more time.					 */					retry_cnt += 1;					if (retry_cnt < 3) {						timeout = jiffies +							  msecs_to_jiffies(20);						continue;					}				}				break;			}		}	}	intr = read_reg(c, ONENAND_REG_INTERRUPT);	ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);	if (intr & ONENAND_INT_READ) {		int ecc = read_reg(c, ONENAND_REG_ECC_STATUS);		if (ecc) {			unsigned int addr1, addr8;			addr1 = read_reg(c, ONENAND_REG_START_ADDRESS1);			addr8 = read_reg(c, ONENAND_REG_START_ADDRESS8);			if (ecc & ONENAND_ECC_2BIT_ALL) {				printk(KERN_ERR "onenand_wait: ECC error = "				       "0x%04x, addr1 %#x, addr8 %#x\n",				       ecc, addr1, addr8);				mtd->ecc_stats.failed++;				return -EBADMSG;			} else if (ecc & ONENAND_ECC_1BIT_ALL) {				printk(KERN_NOTICE "onenand_wait: correctable "				       "ECC error = 0x%04x, addr1 %#x, "				       "addr8 %#x\n", ecc, addr1, addr8);				mtd->ecc_stats.corrected++;			}		}	} else if (state == FL_READING) {		wait_err("timeout", state, ctrl, intr);		return -EIO;	}	if (ctrl & ONENAND_CTRL_ERROR) {		wait_err("controller error", state, ctrl, intr);		if (ctrl & ONENAND_CTRL_LOCK)			printk(KERN_ERR "onenand_wait: "					"Device is write protected!!!\n");		return -EIO;	}	if (ctrl & 0xFE9F)		wait_warn("unexpected controller status", state, ctrl, intr);	return 0;}static inline int omap2_onenand_bufferram_offset(struct mtd_info *mtd, int area){	struct onenand_chip *this = mtd->priv;	if (ONENAND_CURRENT_BUFFERRAM(this)) {		if (area == ONENAND_DATARAM)			return mtd->writesize;		if (area == ONENAND_SPARERAM)			return mtd->oobsize;	}	return 0;}#if defined(CONFIG_ARCH_OMAP3) || defined(MULTI_OMAP2)static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area,					unsigned char *buffer, int offset,					size_t count){	struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);	struct onenand_chip *this = mtd->priv;	dma_addr_t dma_src, dma_dst;	int bram_offset;	unsigned long timeout;	void *buf = (void *)buffer;	size_t xtra;	volatile unsigned *done;	bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset;	if (bram_offset & 3 || (size_t)buf & 3 || count < 384)		goto out_copy;	if (buf >= high_memory) {		struct page *p1;		if (((size_t)buf & PAGE_MASK) !=		    ((size_t)(buf + count - 1) & PAGE_MASK))			goto out_copy;		p1 = vmalloc_to_page(buf);		if (!p1)			goto out_copy;		buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK);	}	xtra = count & 3;	if (xtra) {		count -= xtra;		memcpy(buf + count, this->base + bram_offset + count, xtra);	}	dma_src = c->phys_base + bram_offset;	dma_dst = dma_map_single(&c->pdev->dev, buf, count, DMA_FROM_DEVICE);	if (dma_mapping_error(&c->pdev->dev, dma_dst)) {		dev_err(&c->pdev->dev,			"Couldn't DMA map a %d byte buffer\n",			count);		goto out_copy;	}	omap_set_dma_transfer_params(c->dma_channel, OMAP_DMA_DATA_TYPE_S32,				     count >> 2, 1, 0, 0, 0);	omap_set_dma_src_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC,				dma_src, 0, 0);	omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC,				 dma_dst, 0, 0);	INIT_COMPLETION(c->dma_done);	omap_start_dma(c->dma_channel);	timeout = jiffies + msecs_to_jiffies(20);	done = &c->dma_done.done;	while (time_before(jiffies, timeout))		if (*done)			break;	dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_FROM_DEVICE);	if (!*done) {		dev_err(&c->pdev->dev, "timeout waiting for DMA\n");		goto out_copy;	}	return 0;out_copy:	memcpy(buf, this->base + bram_offset, count);	return 0;}static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area,					 const unsigned char *buffer,					 int offset, size_t count){	struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);	struct onenand_chip *this = mtd->priv;	dma_addr_t dma_src, dma_dst;	int bram_offset;	unsigned long timeout;	void *buf = (void *)buffer;	volatile unsigned *done;	bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset;	if (bram_offset & 3 || (size_t)buf & 3 || count < 384)		goto out_copy;	/* panic_write() may be in an interrupt context */	if (in_interrupt())		goto out_copy;	if (buf >= high_memory) {		struct page *p1;		if (((size_t)buf & PAGE_MASK) !=		    ((size_t)(buf + count - 1) & PAGE_MASK))			goto out_copy;		p1 = vmalloc_to_page(buf);		if (!p1)			goto out_copy;		buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK);	}	dma_src = dma_map_single(&c->pdev->dev, buf, count, DMA_TO_DEVICE);	dma_dst = c->phys_base + bram_offset;	if (dma_mapping_error(&c->pdev->dev, dma_dst)) {		dev_err(&c->pdev->dev,			"Couldn't DMA map a %d byte buffer\n",			count);		return -1;	}	omap_set_dma_transfer_params(c->dma_channel, OMAP_DMA_DATA_TYPE_S32,				     count >> 2, 1, 0, 0, 0);	omap_set_dma_src_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC,				dma_src, 0, 0);	omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC,				 dma_dst, 0, 0);

⌨️ 快捷键说明

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