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

📄 m25p80.c

📁 基于linux-2.6.28的mtd驱动
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * MTD SPI driver for ST M25Pxx (and similar) serial flash chips * * Author: Mike Lavender, mike@steroidmicros.com * * Copyright (c) 2005, Intec Automation Inc. * * Some parts are based on lart.c by Abraham Van Der Merwe * * Cleaned up and generalized based on mtd_dataflash.c * * This code 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/init.h>#include <linux/module.h>#include <linux/device.h>#include <linux/interrupt.h>#include <linux/mutex.h>#include <linux/mtd/mtd.h>#include <linux/mtd/partitions.h>#include <linux/spi/spi.h>#include <linux/spi/flash.h>#define FLASH_PAGESIZE		256/* Flash opcodes. */#define	OPCODE_WREN		0x06	/* Write enable */#define	OPCODE_RDSR		0x05	/* Read status register */#define	OPCODE_WRSR		0x01	/* Write status register 1 byte */#define	OPCODE_NORM_READ	0x03	/* Read data bytes (low frequency) */#define	OPCODE_FAST_READ	0x0b	/* Read data bytes (high frequency) */#define	OPCODE_PP		0x02	/* Page program (up to 256 bytes) */#define	OPCODE_BE_4K		0x20	/* Erase 4KiB block */#define	OPCODE_BE_32K		0x52	/* Erase 32KiB block */#define	OPCODE_CHIP_ERASE	0xc7	/* Erase whole flash chip */#define	OPCODE_SE		0xd8	/* Sector erase (usually 64KiB) */#define	OPCODE_RDID		0x9f	/* Read JEDEC ID *//* Status Register bits. */#define	SR_WIP			1	/* Write in progress */#define	SR_WEL			2	/* Write enable latch *//* meaning of other SR_* bits may differ between vendors */#define	SR_BP0			4	/* Block protect 0 */#define	SR_BP1			8	/* Block protect 1 */#define	SR_BP2			0x10	/* Block protect 2 */#define	SR_SRWD			0x80	/* SR write protect *//* Define max times to check status register before we give up. */#define	MAX_READY_WAIT_COUNT	100000#define	CMD_SIZE		4#ifdef CONFIG_M25PXX_USE_FAST_READ#define OPCODE_READ 	OPCODE_FAST_READ#define FAST_READ_DUMMY_BYTE 1#else#define OPCODE_READ 	OPCODE_NORM_READ#define FAST_READ_DUMMY_BYTE 0#endif#ifdef CONFIG_MTD_PARTITIONS#define	mtd_has_partitions()	(1)#else#define	mtd_has_partitions()	(0)#endif/****************************************************************************/struct m25p {	struct spi_device	*spi;	struct mutex		lock;	struct mtd_info		mtd;	unsigned		partitioned:1;	u8			erase_opcode;	u8			command[CMD_SIZE + FAST_READ_DUMMY_BYTE];};static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd){	return container_of(mtd, struct m25p, mtd);}/****************************************************************************//* * Internal helper functions *//* * Read the status register, returning its value in the location * Return the status register value. * Returns negative if error occurred. */static int read_sr(struct m25p *flash){	ssize_t retval;	u8 code = OPCODE_RDSR;	u8 val;	retval = spi_write_then_read(flash->spi, &code, 1, &val, 1);	if (retval < 0) {		dev_err(&flash->spi->dev, "error %d reading SR\n",				(int) retval);		return retval;	}	return val;}/* * Write status register 1 byte * Returns negative if error occurred. */static int write_sr(struct m25p *flash, u8 val){	flash->command[0] = OPCODE_WRSR;	flash->command[1] = val;	return spi_write(flash->spi, flash->command, 2);}/* * Set write enable latch with Write Enable command. * Returns negative if error occurred. */static inline int write_enable(struct m25p *flash){	u8	code = OPCODE_WREN;	return spi_write_then_read(flash->spi, &code, 1, NULL, 0);}/* * Service routine to read status register until ready, or timeout occurs. * Returns non-zero if error. */static int wait_till_ready(struct m25p *flash){	int count;	int sr;	/* one chip guarantees max 5 msec wait here after page writes,	 * but potentially three seconds (!) after page erase.	 */	for (count = 0; count < MAX_READY_WAIT_COUNT; count++) {		if ((sr = read_sr(flash)) < 0)			break;		else if (!(sr & SR_WIP))			return 0;		/* REVISIT sometimes sleeping would be best */	}	return 1;}/* * Erase the whole flash memory * * Returns 0 if successful, non-zero otherwise. */static int erase_chip(struct m25p *flash){	DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB\n",			flash->spi->dev.bus_id, __func__,			flash->mtd.size / 1024);	/* Wait until finished previous write command. */	if (wait_till_ready(flash))		return 1;	/* Send write enable, then erase commands. */	write_enable(flash);	/* Set up command buffer. */	flash->command[0] = OPCODE_CHIP_ERASE;	spi_write(flash->spi, flash->command, 1);	return 0;}/* * Erase one sector of flash memory at offset ``offset'' which is any * address within the sector which should be erased. * * Returns 0 if successful, non-zero otherwise. */static int erase_sector(struct m25p *flash, u32 offset){	DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n",			flash->spi->dev.bus_id, __func__,			flash->mtd.erasesize / 1024, offset);	/* Wait until finished previous write command. */	if (wait_till_ready(flash))		return 1;	/* Send write enable, then erase commands. */	write_enable(flash);	/* Set up command buffer. */	flash->command[0] = flash->erase_opcode;	flash->command[1] = offset >> 16;	flash->command[2] = offset >> 8;	flash->command[3] = offset;	spi_write(flash->spi, flash->command, CMD_SIZE);	return 0;}/****************************************************************************//* * MTD implementation *//* * Erase an address range on the flash chip.  The address range may extend * one or more erase sectors.  Return an error is there is a problem erasing. */static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr){	struct m25p *flash = mtd_to_m25p(mtd);	u32 addr,len;	DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %d\n",			flash->spi->dev.bus_id, __func__, "at",			(u32)instr->addr, instr->len);	/* sanity checks */	if (instr->addr + instr->len > flash->mtd.size)		return -EINVAL;	if ((instr->addr % mtd->erasesize) != 0			|| (instr->len % mtd->erasesize) != 0) {		return -EINVAL;	}	addr = instr->addr;	len = instr->len;	mutex_lock(&flash->lock);	/* whole-chip erase? */	if (len == flash->mtd.size && erase_chip(flash)) {		instr->state = MTD_ERASE_FAILED;		mutex_unlock(&flash->lock);		return -EIO;	/* REVISIT in some cases we could speed up erasing large regions	 * by using OPCODE_SE instead of OPCODE_BE_4K.  We may have set up	 * to use "small sector erase", but that's not always optimal.	 */	/* "sector"-at-a-time erase */	} else {		while (len) {			if (erase_sector(flash, addr)) {				instr->state = MTD_ERASE_FAILED;				mutex_unlock(&flash->lock);				return -EIO;			}			addr += mtd->erasesize;			len -= mtd->erasesize;		}	}	mutex_unlock(&flash->lock);	instr->state = MTD_ERASE_DONE;	mtd_erase_callback(instr);	return 0;}/* * Read an address range from the flash chip.  The address range * may be any size provided it is within the physical boundaries. */static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,	size_t *retlen, u_char *buf){	struct m25p *flash = mtd_to_m25p(mtd);	struct spi_transfer t[2];	struct spi_message m;	DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n",			flash->spi->dev.bus_id, __func__, "from",			(u32)from, len);	/* sanity checks */	if (!len)		return 0;	if (from + len > flash->mtd.size)		return -EINVAL;	spi_message_init(&m);	memset(t, 0, (sizeof t));	/* NOTE:	 * OPCODE_FAST_READ (if available) is faster.	 * Should add 1 byte DUMMY_BYTE.	 */	t[0].tx_buf = flash->command;	t[0].len = CMD_SIZE + FAST_READ_DUMMY_BYTE;	spi_message_add_tail(&t[0], &m);	t[1].rx_buf = buf;	t[1].len = len;	spi_message_add_tail(&t[1], &m);	/* Byte count starts at zero. */	if (retlen)		*retlen = 0;	mutex_lock(&flash->lock);	/* Wait till previous write/erase is done. */	if (wait_till_ready(flash)) {		/* REVISIT status return?? */		mutex_unlock(&flash->lock);		return 1;	}	/* FIXME switch to OPCODE_FAST_READ.  It's required for higher	 * clocks; and at this writing, every chip this driver handles	 * supports that opcode.	 */	/* Set up the write data buffer. */	flash->command[0] = OPCODE_READ;	flash->command[1] = from >> 16;	flash->command[2] = from >> 8;	flash->command[3] = from;	spi_sync(flash->spi, &m);	*retlen = m.actual_length - CMD_SIZE - FAST_READ_DUMMY_BYTE;	mutex_unlock(&flash->lock);	return 0;}/* * Write an address range to the flash chip.  Data must be written in * FLASH_PAGESIZE chunks.  The address range may be any size provided * it is within the physical boundaries. */static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,	size_t *retlen, const u_char *buf){	struct m25p *flash = mtd_to_m25p(mtd);	u32 page_offset, page_size;	struct spi_transfer t[2];	struct spi_message m;	DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n",			flash->spi->dev.bus_id, __func__, "to",			(u32)to, len);	if (retlen)		*retlen = 0;	/* sanity checks */	if (!len)		return(0);	if (to + len > flash->mtd.size)		return -EINVAL;	spi_message_init(&m);	memset(t, 0, (sizeof t));	t[0].tx_buf = flash->command;	t[0].len = CMD_SIZE;	spi_message_add_tail(&t[0], &m);	t[1].tx_buf = buf;	spi_message_add_tail(&t[1], &m);	mutex_lock(&flash->lock);	/* Wait until finished previous write command. */	if (wait_till_ready(flash)) {		mutex_unlock(&flash->lock);

⌨️ 快捷键说明

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