From: Anton Vorontsov <avorontsov@ru.mvista.com>
To: David Woodhouse <dwmw2@infradead.org>
Cc: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>,
David Brownell <dbrownell@users.sourceforge.net>,
linux-mtd@lists.infradead.org
Subject: [PATCH 3/3] mtd: m25p80: Add support for CAT25xxx serial EEPROMs
Date: Mon, 12 Oct 2009 20:24:40 +0400 [thread overview]
Message-ID: <20091012162440.GC30699@oksana.dev.rtsoft.ru> (raw)
CAT25 chips (as manufactured by On Semiconductor, previously Catalyst
Semiconductor) are similar to the original M25Px0 chips, except:
- Address width can vary (1-2 bytes, in contrast to 3 bytes in M25P
chips). So, implement convenient m25p_addr2cmd() and m25p_cmdsz()
calls, and place address width information into flash_info struct;
- Page size can vary, therefore we shouldn't hardcode it, so get rid
of FLASH_PAGESIZE definition, and place the page size information
into flash_info struct;
- CAT25 EEPROMs don't need to be erased, so add NO_ERASE flag, and
propagate it to the mtd subsystem.
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
drivers/mtd/devices/m25p80.c | 114 +++++++++++++++++++++++++----------------
1 files changed, 69 insertions(+), 45 deletions(-)
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 3c74c3a..d3a27d7 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -29,9 +29,6 @@
#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 */
@@ -61,7 +58,7 @@
/* Define max times to check status register before we give up. */
#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */
-#define CMD_SIZE 4
+#define MAX_CMD_SIZE 4
#ifdef CONFIG_M25PXX_USE_FAST_READ
#define OPCODE_READ OPCODE_FAST_READ
@@ -78,8 +75,10 @@ struct m25p {
struct mutex lock;
struct mtd_info mtd;
unsigned partitioned:1;
+ u16 page_size;
+ u16 addr_width;
u8 erase_opcode;
- u8 command[CMD_SIZE + FAST_READ_DUMMY_BYTE];
+ u8 command[MAX_CMD_SIZE + FAST_READ_DUMMY_BYTE];
};
static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd)
@@ -198,6 +197,19 @@ static int erase_chip(struct m25p *flash)
return 0;
}
+static void m25p_addr2cmd(struct m25p *flash, unsigned int addr, u8 *cmd)
+{
+ /* opcode is in cmd[0] */
+ cmd[1] = addr >> (flash->addr_width * 8 - 8);
+ cmd[2] = addr >> (flash->addr_width * 8 - 16);
+ cmd[3] = addr >> (flash->addr_width * 8 - 24);
+}
+
+static int m25p_cmdsz(struct m25p *flash)
+{
+ return 1 + flash->addr_width;
+}
+
/*
* Erase one sector of flash memory at offset ``offset'' which is any
* address within the sector which should be erased.
@@ -219,11 +231,9 @@ static int erase_sector(struct m25p *flash, u32 offset)
/* Set up command buffer. */
flash->command[0] = flash->erase_opcode;
- flash->command[1] = offset >> 16;
- flash->command[2] = offset >> 8;
- flash->command[3] = offset;
+ m25p_addr2cmd(flash, offset, flash->command);
- spi_write(flash->spi, flash->command, CMD_SIZE);
+ spi_write(flash->spi, flash->command, m25p_cmdsz(flash));
return 0;
}
@@ -325,7 +335,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
* Should add 1 byte DUMMY_BYTE.
*/
t[0].tx_buf = flash->command;
- t[0].len = CMD_SIZE + FAST_READ_DUMMY_BYTE;
+ t[0].len = m25p_cmdsz(flash) + FAST_READ_DUMMY_BYTE;
spi_message_add_tail(&t[0], &m);
t[1].rx_buf = buf;
@@ -352,13 +362,11 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
/* 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;
+ m25p_addr2cmd(flash, from, flash->command);
spi_sync(flash->spi, &m);
- *retlen = m.actual_length - CMD_SIZE - FAST_READ_DUMMY_BYTE;
+ *retlen = m.actual_length - m25p_cmdsz(flash) - FAST_READ_DUMMY_BYTE;
mutex_unlock(&flash->lock);
@@ -396,7 +404,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
memset(t, 0, (sizeof t));
t[0].tx_buf = flash->command;
- t[0].len = CMD_SIZE;
+ t[0].len = m25p_cmdsz(flash);
spi_message_add_tail(&t[0], &m);
t[1].tx_buf = buf;
@@ -414,41 +422,36 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
/* Set up the opcode in the write buffer. */
flash->command[0] = OPCODE_PP;
- flash->command[1] = to >> 16;
- flash->command[2] = to >> 8;
- flash->command[3] = to;
+ m25p_addr2cmd(flash, to, flash->command);
- /* what page do we start with? */
- page_offset = to % FLASH_PAGESIZE;
+ page_offset = to & (flash->page_size - 1);
/* do all the bytes fit onto one page? */
- if (page_offset + len <= FLASH_PAGESIZE) {
+ if (page_offset + len <= flash->page_size) {
t[1].len = len;
spi_sync(flash->spi, &m);
- *retlen = m.actual_length - CMD_SIZE;
+ *retlen = m.actual_length - m25p_cmdsz(flash);
} else {
u32 i;
/* the size of data remaining on the first page */
- page_size = FLASH_PAGESIZE - page_offset;
+ page_size = flash->page_size - page_offset;
t[1].len = page_size;
spi_sync(flash->spi, &m);
- *retlen = m.actual_length - CMD_SIZE;
+ *retlen = m.actual_length - m25p_cmdsz(flash);
- /* write everything in PAGESIZE chunks */
+ /* write everything in flash->page_size chunks */
for (i = page_size; i < len; i += page_size) {
page_size = len - i;
- if (page_size > FLASH_PAGESIZE)
- page_size = FLASH_PAGESIZE;
+ if (page_size > flash->page_size)
+ page_size = flash->page_size;
/* write the next page to flash */
- flash->command[1] = (to + i) >> 16;
- flash->command[2] = (to + i) >> 8;
- flash->command[3] = (to + i);
+ m25p_addr2cmd(flash, to + i, flash->command);
t[1].tx_buf = buf + i;
t[1].len = page_size;
@@ -460,7 +463,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
spi_sync(flash->spi, &m);
if (retlen)
- *retlen += m.actual_length - CMD_SIZE;
+ *retlen += m.actual_length - m25p_cmdsz(flash);
}
}
@@ -492,7 +495,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
memset(t, 0, (sizeof t));
t[0].tx_buf = flash->command;
- t[0].len = CMD_SIZE;
+ t[0].len = m25p_cmdsz(flash);
spi_message_add_tail(&t[0], &m);
t[1].tx_buf = buf;
@@ -511,9 +514,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
/* Start write from odd address. */
if (actual) {
flash->command[0] = OPCODE_BP;
- flash->command[1] = to >> 16;
- flash->command[2] = to >> 8;
- flash->command[3] = to;
+ m25p_addr2cmd(flash, to, flash->command);
/* write one byte. */
t[1].len = 1;
@@ -521,17 +522,15 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
ret = wait_till_ready(flash);
if (ret)
goto time_out;
- *retlen += m.actual_length - CMD_SIZE;
+ *retlen += m.actual_length - m25p_cmdsz(flash);
}
to += actual;
flash->command[0] = OPCODE_AAI_WP;
- flash->command[1] = to >> 16;
- flash->command[2] = to >> 8;
- flash->command[3] = to;
+ m25p_addr2cmd(flash, to, flash->command);
/* Write out most of the data here. */
- cmd_sz = CMD_SIZE;
+ cmd_sz = m25p_cmdsz(flash);
for (; actual < len - 1; actual += 2) {
t[0].len = cmd_sz;
/* write two bytes. */
@@ -555,10 +554,8 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
if (actual != len) {
write_enable(flash);
flash->command[0] = OPCODE_BP;
- flash->command[1] = to >> 16;
- flash->command[2] = to >> 8;
- flash->command[3] = to;
- t[0].len = CMD_SIZE;
+ m25p_addr2cmd(flash, to, flash->command);
+ t[0].len = m25p_cmdsz(flash);
t[1].len = 1;
t[1].tx_buf = buf + actual;
@@ -566,7 +563,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
ret = wait_till_ready(flash);
if (ret)
goto time_out;
- *retlen += m.actual_length - CMD_SIZE;
+ *retlen += m.actual_length - m25p_cmdsz(flash);
write_disable(flash);
}
@@ -595,8 +592,12 @@ struct flash_info {
unsigned sector_size;
u16 n_sectors;
+ u16 page_size;
+ u16 addr_width;
+
u16 flags;
#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */
+#define M25P_NO_ERASE 0x02 /* No erase command needed */
};
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
@@ -605,9 +606,20 @@ struct flash_info {
.ext_id = (_ext_id), \
.sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \
+ .page_size = 256, \
+ .addr_width = 3, \
.flags = (_flags), \
})
+#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width) \
+ ((kernel_ulong_t)&(struct flash_info) { \
+ .sector_size = (_sector_size), \
+ .n_sectors = (_n_sectors), \
+ .page_size = (_page_size), \
+ .addr_width = (_addr_width), \
+ .flags = M25P_NO_ERASE, \
+ })
+
/* NOTE: double check command sets and memory organization when you add
* more flash chips. This current list focusses on newer chips, which
* have been converging on command sets which including JEDEC ID.
@@ -680,6 +692,13 @@ static const struct spi_device_id m25p_ids[] = {
{ "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) },
{ "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
+
+ /* Catalyst / On Semiconductor -- non-JEDEC */
+ { "cat25c11", CAT25_INFO( 16, 8, 16, 1) },
+ { "cat25c03", CAT25_INFO( 32, 8, 16, 2) },
+ { "cat25c09", CAT25_INFO( 128, 8, 32, 2) },
+ { "cat25c17", CAT25_INFO( 256, 8, 32, 2) },
+ { "cat25128", CAT25_INFO(2048, 8, 64, 2) },
{ },
};
MODULE_DEVICE_TABLE(spi, m25p_ids);
@@ -835,7 +854,12 @@ static int __devinit m25p_probe(struct spi_device *spi)
flash->mtd.erasesize = info->sector_size;
}
+ if (info->flags & M25P_NO_ERASE)
+ flash->mtd.flags |= MTD_NO_ERASE;
+
flash->mtd.dev.parent = &spi->dev;
+ flash->page_size = info->page_size;
+ flash->addr_width = info->addr_width;
dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name,
(long long)flash->mtd.size >> 10);
--
1.6.3.3
next reply other threads:[~2009-10-12 16:24 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-10-12 16:24 Anton Vorontsov [this message]
-- strict thread matches above, loose matches on Subject: below --
2009-09-22 23:53 [patch 3/3] mtd: m25p80: add support for CAT25xxx serial EEPROMs akpm
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20091012162440.GC30699@oksana.dev.rtsoft.ru \
--to=avorontsov@ru.mvista.com \
--cc=Artem.Bityutskiy@nokia.com \
--cc=dbrownell@users.sourceforge.net \
--cc=dwmw2@infradead.org \
--cc=linux-mtd@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.