📄 m25p80.c
字号:
/* * 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 + -