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

📄 onenand_base.c

📁 基于linux-2.6.28的mtd驱动
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  linux/drivers/mtd/onenand/onenand_base.c * *  Copyright (C) 2005-2007 Samsung Electronics *  Kyungmin Park <kyungmin.park@samsung.com> * *  Credits: *	Adrian Hunter <ext-adrian.hunter@nokia.com>: *	auto-placement support, read-while load support, various fixes *	Copyright (C) Nokia Corporation, 2007 * * 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/module.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/jiffies.h>#include <linux/mtd/mtd.h>#include <linux/mtd/onenand.h>#include <linux/mtd/partitions.h>#include <asm/io.h>/** * onenand_oob_64 - oob info for large (2KB) page */static struct nand_ecclayout onenand_oob_64 = {	.eccbytes	= 20,	.eccpos		= {		8, 9, 10, 11, 12,		24, 25, 26, 27, 28,		40, 41, 42, 43, 44,		56, 57, 58, 59, 60,		},	.oobfree	= {		{2, 3}, {14, 2}, {18, 3}, {30, 2},		{34, 3}, {46, 2}, {50, 3}, {62, 2}	}};/** * onenand_oob_32 - oob info for middle (1KB) page */static struct nand_ecclayout onenand_oob_32 = {	.eccbytes	= 10,	.eccpos		= {		8, 9, 10, 11, 12,		24, 25, 26, 27, 28,		},	.oobfree	= { {2, 3}, {14, 2}, {18, 3}, {30, 2} }};static const unsigned char ffchars[] = {	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 16 */	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 32 */	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 48 */	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 64 */};/** * onenand_readw - [OneNAND Interface] Read OneNAND register * @param addr		address to read * * Read OneNAND register */static unsigned short onenand_readw(void __iomem *addr){	return readw(addr);}/** * onenand_writew - [OneNAND Interface] Write OneNAND register with value * @param value		value to write * @param addr		address to write * * Write OneNAND register with value */static void onenand_writew(unsigned short value, void __iomem *addr){	writew(value, addr);}/** * onenand_block_address - [DEFAULT] Get block address * @param this		onenand chip data structure * @param block		the block * @return		translated block address if DDP, otherwise same * * Setup Start Address 1 Register (F100h) */static int onenand_block_address(struct onenand_chip *this, int block){	/* Device Flash Core select, NAND Flash Block Address */	if (block & this->density_mask)		return ONENAND_DDP_CHIP1 | (block ^ this->density_mask);	return block;}/** * onenand_bufferram_address - [DEFAULT] Get bufferram address * @param this		onenand chip data structure * @param block		the block * @return		set DBS value if DDP, otherwise 0 * * Setup Start Address 2 Register (F101h) for DDP */static int onenand_bufferram_address(struct onenand_chip *this, int block){	/* Device BufferRAM Select */	if (block & this->density_mask)		return ONENAND_DDP_CHIP1;	return ONENAND_DDP_CHIP0;}/** * onenand_page_address - [DEFAULT] Get page address * @param page		the page address * @param sector	the sector address * @return		combined page and sector address * * Setup Start Address 8 Register (F107h) */static int onenand_page_address(int page, int sector){	/* Flash Page Address, Flash Sector Address */	int fpa, fsa;	fpa = page & ONENAND_FPA_MASK;	fsa = sector & ONENAND_FSA_MASK;	return ((fpa << ONENAND_FPA_SHIFT) | fsa);}/** * onenand_buffer_address - [DEFAULT] Get buffer address * @param dataram1	DataRAM index * @param sectors	the sector address * @param count		the number of sectors * @return		the start buffer value * * Setup Start Buffer Register (F200h) */static int onenand_buffer_address(int dataram1, int sectors, int count){	int bsa, bsc;	/* BufferRAM Sector Address */	bsa = sectors & ONENAND_BSA_MASK;	if (dataram1)		bsa |= ONENAND_BSA_DATARAM1;	/* DataRAM1 */	else		bsa |= ONENAND_BSA_DATARAM0;	/* DataRAM0 */	/* BufferRAM Sector Count */	bsc = count & ONENAND_BSC_MASK;	return ((bsa << ONENAND_BSA_SHIFT) | bsc);}/** * onenand_get_density - [DEFAULT] Get OneNAND density * @param dev_id	OneNAND device ID * * Get OneNAND density from device ID */static inline int onenand_get_density(int dev_id){	int density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;	return (density & ONENAND_DEVICE_DENSITY_MASK);}/** * onenand_command - [DEFAULT] Send command to OneNAND device * @param mtd		MTD device structure * @param cmd		the command to be sent * @param addr		offset to read from or write to * @param len		number of bytes to read or write * * Send command to OneNAND device. This function is used for middle/large page * devices (1KB/2KB Bytes per page) */static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len){	struct onenand_chip *this = mtd->priv;	int value, block, page;	/* Address translation */	switch (cmd) {	case ONENAND_CMD_UNLOCK:	case ONENAND_CMD_LOCK:	case ONENAND_CMD_LOCK_TIGHT:	case ONENAND_CMD_UNLOCK_ALL:		block = -1;		page = -1;		break;	case ONENAND_CMD_ERASE:	case ONENAND_CMD_BUFFERRAM:	case ONENAND_CMD_OTP_ACCESS:		block = (int) (addr >> this->erase_shift);		page = -1;		break;	default:		block = (int) (addr >> this->erase_shift);		page = (int) (addr >> this->page_shift);		if (ONENAND_IS_2PLANE(this)) {			/* Make the even block number */			block &= ~1;			/* Is it the odd plane? */			if (addr & this->writesize)				block++;			page >>= 1;		}		page &= this->page_mask;		break;	}	/* NOTE: The setting order of the registers is very important! */	if (cmd == ONENAND_CMD_BUFFERRAM) {		/* Select DataRAM for DDP */		value = onenand_bufferram_address(this, block);		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);		if (ONENAND_IS_2PLANE(this))			/* It is always BufferRAM0 */			ONENAND_SET_BUFFERRAM0(this);		else			/* Switch to the next data buffer */			ONENAND_SET_NEXT_BUFFERRAM(this);		return 0;	}	if (block != -1) {		/* Write 'DFS, FBA' of Flash */		value = onenand_block_address(this, block);		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);		/* Select DataRAM for DDP */		value = onenand_bufferram_address(this, block);		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);	}	if (page != -1) {		/* Now we use page size operation */		int sectors = 4, count = 4;		int dataram;		switch (cmd) {		case ONENAND_CMD_READ:		case ONENAND_CMD_READOOB:			dataram = ONENAND_SET_NEXT_BUFFERRAM(this);			break;		default:			if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)				cmd = ONENAND_CMD_2X_PROG;			dataram = ONENAND_CURRENT_BUFFERRAM(this);			break;		}		/* Write 'FPA, FSA' of Flash */		value = onenand_page_address(page, sectors);		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS8);		/* Write 'BSA, BSC' of DataRAM */		value = onenand_buffer_address(dataram, sectors, count);		this->write_word(value, this->base + ONENAND_REG_START_BUFFER);	}	/* Interrupt clear */	this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);	/* Write command */	this->write_word(cmd, this->base + ONENAND_REG_COMMAND);	return 0;}/** * onenand_wait - [DEFAULT] wait until the command is done * @param mtd		MTD device structure * @param state		state to select the max. timeout value * * Wait for command done. This applies to all OneNAND command * Read can take up to 30us, erase up to 2ms and program up to 350us * according to general OneNAND specs */static int onenand_wait(struct mtd_info *mtd, int state){	struct onenand_chip * this = mtd->priv;	unsigned long timeout;	unsigned int flags = ONENAND_INT_MASTER;	unsigned int interrupt = 0;	unsigned int ctrl;	/* The 20 msec is enough */	timeout = jiffies + msecs_to_jiffies(20);	while (time_before(jiffies, timeout)) {		interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);		if (interrupt & flags)			break;		if (state != FL_READING)			cond_resched();	}	/* To get correct interrupt status in timeout case */	interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);	ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);	/*	 * In the Spec. it checks the controller status first	 * However if you get the correct information in case of	 * power off recovery (POR) test, it should read ECC status first	 */	if (interrupt & ONENAND_INT_READ) {		int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);		if (ecc) {			if (ecc & ONENAND_ECC_2BIT_ALL) {				printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc);				mtd->ecc_stats.failed++;				return -EBADMSG;			} else if (ecc & ONENAND_ECC_1BIT_ALL) {				printk(KERN_INFO "onenand_wait: correctable ECC error = 0x%04x\n", ecc);				mtd->ecc_stats.corrected++;			}		}	} else if (state == FL_READING) {		printk(KERN_ERR "onenand_wait: read timeout! ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt);		return -EIO;	}	/* If there's controller error, it's a real error */	if (ctrl & ONENAND_CTRL_ERROR) {		printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n",			ctrl);		if (ctrl & ONENAND_CTRL_LOCK)			printk(KERN_ERR "onenand_wait: it's locked error.\n");		return -EIO;	}	return 0;}/* * onenand_interrupt - [DEFAULT] onenand interrupt handler * @param irq		onenand interrupt number * @param dev_id	interrupt data * * complete the work */static irqreturn_t onenand_interrupt(int irq, void *data){	struct onenand_chip *this = data;	/* To handle shared interrupt */	if (!this->complete.done)		complete(&this->complete);	return IRQ_HANDLED;}/* * onenand_interrupt_wait - [DEFAULT] wait until the command is done * @param mtd		MTD device structure * @param state		state to select the max. timeout value * * Wait for command done. */static int onenand_interrupt_wait(struct mtd_info *mtd, int state){	struct onenand_chip *this = mtd->priv;	wait_for_completion(&this->complete);	return onenand_wait(mtd, state);}/* * onenand_try_interrupt_wait - [DEFAULT] try interrupt wait * @param mtd		MTD device structure * @param state		state to select the max. timeout value * * Try interrupt based wait (It is used one-time) */static int onenand_try_interrupt_wait(struct mtd_info *mtd, int state){	struct onenand_chip *this = mtd->priv;	unsigned long remain, timeout;	/* We use interrupt wait first */	this->wait = onenand_interrupt_wait;	timeout = msecs_to_jiffies(100);	remain = wait_for_completion_timeout(&this->complete, timeout);	if (!remain) {		printk(KERN_INFO "OneNAND: There's no interrupt. "				"We use the normal wait\n");		/* Release the irq */		free_irq(this->irq, this);		this->wait = onenand_wait;	}	return onenand_wait(mtd, state);}/* * onenand_setup_wait - [OneNAND Interface] setup onenand wait method * @param mtd		MTD device structure * * There's two method to wait onenand work * 1. polling - read interrupt status register * 2. interrupt - use the kernel interrupt method */static void onenand_setup_wait(struct mtd_info *mtd){	struct onenand_chip *this = mtd->priv;	int syscfg;	init_completion(&this->complete);	if (this->irq <= 0) {		this->wait = onenand_wait;		return;	}	if (request_irq(this->irq, &onenand_interrupt,				IRQF_SHARED, "onenand", this)) {		/* If we can't get irq, use the normal wait */		this->wait = onenand_wait;		return;	}	/* Enable interrupt */	syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);	syscfg |= ONENAND_SYS_CFG1_IOBE;	this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);	this->wait = onenand_try_interrupt_wait;}/** * onenand_bufferram_offset - [DEFAULT] BufferRAM offset * @param mtd		MTD data structure * @param area		BufferRAM area * @return		offset given area * * Return BufferRAM offset given area */static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area){	struct onenand_chip *this = mtd->priv;	if (ONENAND_CURRENT_BUFFERRAM(this)) {		/* Note: the 'this->writesize' is a real page size */		if (area == ONENAND_DATARAM)			return this->writesize;		if (area == ONENAND_SPARERAM)			return mtd->oobsize;	}	return 0;}/** * onenand_read_bufferram - [OneNAND Interface] Read the bufferram area * @param mtd		MTD data structure * @param area		BufferRAM area * @param buffer	the databuffer to put/get data * @param offset	offset to read from or write to * @param count		number of bytes to read/write * * Read the BufferRAM area */static int onenand_read_bufferram(struct mtd_info *mtd, int area,		unsigned char *buffer, int offset, size_t count){	struct onenand_chip *this = mtd->priv;	void __iomem *bufferram;	bufferram = this->base + area;	bufferram += onenand_bufferram_offset(mtd, area);	if (ONENAND_CHECK_BYTE_ACCESS(count)) {

⌨️ 快捷键说明

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