diff options
author | Anton Vorontsov <avorontsov@ru.mvista.com> | 2009-10-12 12:24:40 -0400 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2009-11-30 06:29:00 -0500 |
commit | 837479d25e221ba616de2c734f58e1decd8cdb95 (patch) | |
tree | d92d2c78579ffe6faf5d6b9bf8341044956f5d07 /drivers/mtd/devices | |
parent | 18c6182bae0acca220ed6611f741034d563cd19f (diff) |
mtd: m25p80: Add support for CAT25xxx serial EEPROMs
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.
[dwmw2: Fix up for conflicts with DMA safety patch]
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'drivers/mtd/devices')
-rw-r--r-- | 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 e6892851e813..8f8c249d7c46 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c | |||
@@ -29,9 +29,6 @@ | |||
29 | #include <linux/spi/spi.h> | 29 | #include <linux/spi/spi.h> |
30 | #include <linux/spi/flash.h> | 30 | #include <linux/spi/flash.h> |
31 | 31 | ||
32 | |||
33 | #define FLASH_PAGESIZE 256 | ||
34 | |||
35 | /* Flash opcodes. */ | 32 | /* Flash opcodes. */ |
36 | #define OPCODE_WREN 0x06 /* Write enable */ | 33 | #define OPCODE_WREN 0x06 /* Write enable */ |
37 | #define OPCODE_RDSR 0x05 /* Read status register */ | 34 | #define OPCODE_RDSR 0x05 /* Read status register */ |
@@ -61,7 +58,7 @@ | |||
61 | 58 | ||
62 | /* Define max times to check status register before we give up. */ | 59 | /* Define max times to check status register before we give up. */ |
63 | #define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ | 60 | #define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ |
64 | #define CMD_SIZE 4 | 61 | #define MAX_CMD_SIZE 4 |
65 | 62 | ||
66 | #ifdef CONFIG_M25PXX_USE_FAST_READ | 63 | #ifdef CONFIG_M25PXX_USE_FAST_READ |
67 | #define OPCODE_READ OPCODE_FAST_READ | 64 | #define OPCODE_READ OPCODE_FAST_READ |
@@ -78,6 +75,8 @@ struct m25p { | |||
78 | struct mutex lock; | 75 | struct mutex lock; |
79 | struct mtd_info mtd; | 76 | struct mtd_info mtd; |
80 | unsigned partitioned:1; | 77 | unsigned partitioned:1; |
78 | u16 page_size; | ||
79 | u16 addr_width; | ||
81 | u8 erase_opcode; | 80 | u8 erase_opcode; |
82 | u8 *command; | 81 | u8 *command; |
83 | }; | 82 | }; |
@@ -198,6 +197,19 @@ static int erase_chip(struct m25p *flash) | |||
198 | return 0; | 197 | return 0; |
199 | } | 198 | } |
200 | 199 | ||
200 | static void m25p_addr2cmd(struct m25p *flash, unsigned int addr, u8 *cmd) | ||
201 | { | ||
202 | /* opcode is in cmd[0] */ | ||
203 | cmd[1] = addr >> (flash->addr_width * 8 - 8); | ||
204 | cmd[2] = addr >> (flash->addr_width * 8 - 16); | ||
205 | cmd[3] = addr >> (flash->addr_width * 8 - 24); | ||
206 | } | ||
207 | |||
208 | static int m25p_cmdsz(struct m25p *flash) | ||
209 | { | ||
210 | return 1 + flash->addr_width; | ||
211 | } | ||
212 | |||
201 | /* | 213 | /* |
202 | * Erase one sector of flash memory at offset ``offset'' which is any | 214 | * Erase one sector of flash memory at offset ``offset'' which is any |
203 | * address within the sector which should be erased. | 215 | * address within the sector which should be erased. |
@@ -219,11 +231,9 @@ static int erase_sector(struct m25p *flash, u32 offset) | |||
219 | 231 | ||
220 | /* Set up command buffer. */ | 232 | /* Set up command buffer. */ |
221 | flash->command[0] = flash->erase_opcode; | 233 | flash->command[0] = flash->erase_opcode; |
222 | flash->command[1] = offset >> 16; | 234 | m25p_addr2cmd(flash, offset, flash->command); |
223 | flash->command[2] = offset >> 8; | ||
224 | flash->command[3] = offset; | ||
225 | 235 | ||
226 | spi_write(flash->spi, flash->command, CMD_SIZE); | 236 | spi_write(flash->spi, flash->command, m25p_cmdsz(flash)); |
227 | 237 | ||
228 | return 0; | 238 | return 0; |
229 | } | 239 | } |
@@ -325,7 +335,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
325 | * Should add 1 byte DUMMY_BYTE. | 335 | * Should add 1 byte DUMMY_BYTE. |
326 | */ | 336 | */ |
327 | t[0].tx_buf = flash->command; | 337 | t[0].tx_buf = flash->command; |
328 | t[0].len = CMD_SIZE + FAST_READ_DUMMY_BYTE; | 338 | t[0].len = m25p_cmdsz(flash) + FAST_READ_DUMMY_BYTE; |
329 | spi_message_add_tail(&t[0], &m); | 339 | spi_message_add_tail(&t[0], &m); |
330 | 340 | ||
331 | t[1].rx_buf = buf; | 341 | t[1].rx_buf = buf; |
@@ -352,13 +362,11 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
352 | 362 | ||
353 | /* Set up the write data buffer. */ | 363 | /* Set up the write data buffer. */ |
354 | flash->command[0] = OPCODE_READ; | 364 | flash->command[0] = OPCODE_READ; |
355 | flash->command[1] = from >> 16; | 365 | m25p_addr2cmd(flash, from, flash->command); |
356 | flash->command[2] = from >> 8; | ||
357 | flash->command[3] = from; | ||
358 | 366 | ||
359 | spi_sync(flash->spi, &m); | 367 | spi_sync(flash->spi, &m); |
360 | 368 | ||
361 | *retlen = m.actual_length - CMD_SIZE - FAST_READ_DUMMY_BYTE; | 369 | *retlen = m.actual_length - m25p_cmdsz(flash) - FAST_READ_DUMMY_BYTE; |
362 | 370 | ||
363 | mutex_unlock(&flash->lock); | 371 | mutex_unlock(&flash->lock); |
364 | 372 | ||
@@ -396,7 +404,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
396 | memset(t, 0, (sizeof t)); | 404 | memset(t, 0, (sizeof t)); |
397 | 405 | ||
398 | t[0].tx_buf = flash->command; | 406 | t[0].tx_buf = flash->command; |
399 | t[0].len = CMD_SIZE; | 407 | t[0].len = m25p_cmdsz(flash); |
400 | spi_message_add_tail(&t[0], &m); | 408 | spi_message_add_tail(&t[0], &m); |
401 | 409 | ||
402 | t[1].tx_buf = buf; | 410 | t[1].tx_buf = buf; |
@@ -414,41 +422,36 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
414 | 422 | ||
415 | /* Set up the opcode in the write buffer. */ | 423 | /* Set up the opcode in the write buffer. */ |
416 | flash->command[0] = OPCODE_PP; | 424 | flash->command[0] = OPCODE_PP; |
417 | flash->command[1] = to >> 16; | 425 | m25p_addr2cmd(flash, to, flash->command); |
418 | flash->command[2] = to >> 8; | ||
419 | flash->command[3] = to; | ||
420 | 426 | ||
421 | /* what page do we start with? */ | 427 | page_offset = to & (flash->page_size - 1); |
422 | page_offset = to % FLASH_PAGESIZE; | ||
423 | 428 | ||
424 | /* do all the bytes fit onto one page? */ | 429 | /* do all the bytes fit onto one page? */ |
425 | if (page_offset + len <= FLASH_PAGESIZE) { | 430 | if (page_offset + len <= flash->page_size) { |
426 | t[1].len = len; | 431 | t[1].len = len; |
427 | 432 | ||
428 | spi_sync(flash->spi, &m); | 433 | spi_sync(flash->spi, &m); |
429 | 434 | ||
430 | *retlen = m.actual_length - CMD_SIZE; | 435 | *retlen = m.actual_length - m25p_cmdsz(flash); |
431 | } else { | 436 | } else { |
432 | u32 i; | 437 | u32 i; |
433 | 438 | ||
434 | /* the size of data remaining on the first page */ | 439 | /* the size of data remaining on the first page */ |
435 | page_size = FLASH_PAGESIZE - page_offset; | 440 | page_size = flash->page_size - page_offset; |
436 | 441 | ||
437 | t[1].len = page_size; | 442 | t[1].len = page_size; |
438 | spi_sync(flash->spi, &m); | 443 | spi_sync(flash->spi, &m); |
439 | 444 | ||
440 | *retlen = m.actual_length - CMD_SIZE; | 445 | *retlen = m.actual_length - m25p_cmdsz(flash); |
441 | 446 | ||
442 | /* write everything in PAGESIZE chunks */ | 447 | /* write everything in flash->page_size chunks */ |
443 | for (i = page_size; i < len; i += page_size) { | 448 | for (i = page_size; i < len; i += page_size) { |
444 | page_size = len - i; | 449 | page_size = len - i; |
445 | if (page_size > FLASH_PAGESIZE) | 450 | if (page_size > flash->page_size) |
446 | page_size = FLASH_PAGESIZE; | 451 | page_size = flash->page_size; |
447 | 452 | ||
448 | /* write the next page to flash */ | 453 | /* write the next page to flash */ |
449 | flash->command[1] = (to + i) >> 16; | 454 | m25p_addr2cmd(flash, to + i, flash->command); |
450 | flash->command[2] = (to + i) >> 8; | ||
451 | flash->command[3] = (to + i); | ||
452 | 455 | ||
453 | t[1].tx_buf = buf + i; | 456 | t[1].tx_buf = buf + i; |
454 | t[1].len = page_size; | 457 | t[1].len = page_size; |
@@ -460,7 +463,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
460 | spi_sync(flash->spi, &m); | 463 | spi_sync(flash->spi, &m); |
461 | 464 | ||
462 | if (retlen) | 465 | if (retlen) |
463 | *retlen += m.actual_length - CMD_SIZE; | 466 | *retlen += m.actual_length - m25p_cmdsz(flash); |
464 | } | 467 | } |
465 | } | 468 | } |
466 | 469 | ||
@@ -492,7 +495,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
492 | memset(t, 0, (sizeof t)); | 495 | memset(t, 0, (sizeof t)); |
493 | 496 | ||
494 | t[0].tx_buf = flash->command; | 497 | t[0].tx_buf = flash->command; |
495 | t[0].len = CMD_SIZE; | 498 | t[0].len = m25p_cmdsz(flash); |
496 | spi_message_add_tail(&t[0], &m); | 499 | spi_message_add_tail(&t[0], &m); |
497 | 500 | ||
498 | t[1].tx_buf = buf; | 501 | t[1].tx_buf = buf; |
@@ -511,9 +514,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
511 | /* Start write from odd address. */ | 514 | /* Start write from odd address. */ |
512 | if (actual) { | 515 | if (actual) { |
513 | flash->command[0] = OPCODE_BP; | 516 | flash->command[0] = OPCODE_BP; |
514 | flash->command[1] = to >> 16; | 517 | m25p_addr2cmd(flash, to, flash->command); |
515 | flash->command[2] = to >> 8; | ||
516 | flash->command[3] = to; | ||
517 | 518 | ||
518 | /* write one byte. */ | 519 | /* write one byte. */ |
519 | t[1].len = 1; | 520 | t[1].len = 1; |
@@ -521,17 +522,15 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
521 | ret = wait_till_ready(flash); | 522 | ret = wait_till_ready(flash); |
522 | if (ret) | 523 | if (ret) |
523 | goto time_out; | 524 | goto time_out; |
524 | *retlen += m.actual_length - CMD_SIZE; | 525 | *retlen += m.actual_length - m25p_cmdsz(flash); |
525 | } | 526 | } |
526 | to += actual; | 527 | to += actual; |
527 | 528 | ||
528 | flash->command[0] = OPCODE_AAI_WP; | 529 | flash->command[0] = OPCODE_AAI_WP; |
529 | flash->command[1] = to >> 16; | 530 | m25p_addr2cmd(flash, to, flash->command); |
530 | flash->command[2] = to >> 8; | ||
531 | flash->command[3] = to; | ||
532 | 531 | ||
533 | /* Write out most of the data here. */ | 532 | /* Write out most of the data here. */ |
534 | cmd_sz = CMD_SIZE; | 533 | cmd_sz = m25p_cmdsz(flash); |
535 | for (; actual < len - 1; actual += 2) { | 534 | for (; actual < len - 1; actual += 2) { |
536 | t[0].len = cmd_sz; | 535 | t[0].len = cmd_sz; |
537 | /* write two bytes. */ | 536 | /* write two bytes. */ |
@@ -555,10 +554,8 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
555 | if (actual != len) { | 554 | if (actual != len) { |
556 | write_enable(flash); | 555 | write_enable(flash); |
557 | flash->command[0] = OPCODE_BP; | 556 | flash->command[0] = OPCODE_BP; |
558 | flash->command[1] = to >> 16; | 557 | m25p_addr2cmd(flash, to, flash->command); |
559 | flash->command[2] = to >> 8; | 558 | t[0].len = m25p_cmdsz(flash); |
560 | flash->command[3] = to; | ||
561 | t[0].len = CMD_SIZE; | ||
562 | t[1].len = 1; | 559 | t[1].len = 1; |
563 | t[1].tx_buf = buf + actual; | 560 | t[1].tx_buf = buf + actual; |
564 | 561 | ||
@@ -566,7 +563,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
566 | ret = wait_till_ready(flash); | 563 | ret = wait_till_ready(flash); |
567 | if (ret) | 564 | if (ret) |
568 | goto time_out; | 565 | goto time_out; |
569 | *retlen += m.actual_length - CMD_SIZE; | 566 | *retlen += m.actual_length - m25p_cmdsz(flash); |
570 | write_disable(flash); | 567 | write_disable(flash); |
571 | } | 568 | } |
572 | 569 | ||
@@ -595,8 +592,12 @@ struct flash_info { | |||
595 | unsigned sector_size; | 592 | unsigned sector_size; |
596 | u16 n_sectors; | 593 | u16 n_sectors; |
597 | 594 | ||
595 | u16 page_size; | ||
596 | u16 addr_width; | ||
597 | |||
598 | u16 flags; | 598 | u16 flags; |
599 | #define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ | 599 | #define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ |
600 | #define M25P_NO_ERASE 0x02 /* No erase command needed */ | ||
600 | }; | 601 | }; |
601 | 602 | ||
602 | #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ | 603 | #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ |
@@ -605,9 +606,20 @@ struct flash_info { | |||
605 | .ext_id = (_ext_id), \ | 606 | .ext_id = (_ext_id), \ |
606 | .sector_size = (_sector_size), \ | 607 | .sector_size = (_sector_size), \ |
607 | .n_sectors = (_n_sectors), \ | 608 | .n_sectors = (_n_sectors), \ |
609 | .page_size = 256, \ | ||
610 | .addr_width = 3, \ | ||
608 | .flags = (_flags), \ | 611 | .flags = (_flags), \ |
609 | }) | 612 | }) |
610 | 613 | ||
614 | #define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width) \ | ||
615 | ((kernel_ulong_t)&(struct flash_info) { \ | ||
616 | .sector_size = (_sector_size), \ | ||
617 | .n_sectors = (_n_sectors), \ | ||
618 | .page_size = (_page_size), \ | ||
619 | .addr_width = (_addr_width), \ | ||
620 | .flags = M25P_NO_ERASE, \ | ||
621 | }) | ||
622 | |||
611 | /* NOTE: double check command sets and memory organization when you add | 623 | /* NOTE: double check command sets and memory organization when you add |
612 | * more flash chips. This current list focusses on newer chips, which | 624 | * more flash chips. This current list focusses on newer chips, which |
613 | * have been converging on command sets which including JEDEC ID. | 625 | * have been converging on command sets which including JEDEC ID. |
@@ -680,6 +692,13 @@ static const struct spi_device_id m25p_ids[] = { | |||
680 | { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, | 692 | { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, |
681 | { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, | 693 | { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, |
682 | { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, | 694 | { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, |
695 | |||
696 | /* Catalyst / On Semiconductor -- non-JEDEC */ | ||
697 | { "cat25c11", CAT25_INFO( 16, 8, 16, 1) }, | ||
698 | { "cat25c03", CAT25_INFO( 32, 8, 16, 2) }, | ||
699 | { "cat25c09", CAT25_INFO( 128, 8, 32, 2) }, | ||
700 | { "cat25c17", CAT25_INFO( 256, 8, 32, 2) }, | ||
701 | { "cat25128", CAT25_INFO(2048, 8, 64, 2) }, | ||
683 | { }, | 702 | { }, |
684 | }; | 703 | }; |
685 | MODULE_DEVICE_TABLE(spi, m25p_ids); | 704 | MODULE_DEVICE_TABLE(spi, m25p_ids); |
@@ -793,7 +812,7 @@ static int __devinit m25p_probe(struct spi_device *spi) | |||
793 | flash = kzalloc(sizeof *flash, GFP_KERNEL); | 812 | flash = kzalloc(sizeof *flash, GFP_KERNEL); |
794 | if (!flash) | 813 | if (!flash) |
795 | return -ENOMEM; | 814 | return -ENOMEM; |
796 | flash->command = kmalloc(CMD_SIZE + FAST_READ_DUMMY_BYTE, GFP_KERNEL); | 815 | flash->command = kmalloc(MAX_CMD_SIZE + FAST_READ_DUMMY_BYTE, GFP_KERNEL); |
797 | if (!flash->command) { | 816 | if (!flash->command) { |
798 | kfree(flash); | 817 | kfree(flash); |
799 | return -ENOMEM; | 818 | return -ENOMEM; |
@@ -841,7 +860,12 @@ static int __devinit m25p_probe(struct spi_device *spi) | |||
841 | flash->mtd.erasesize = info->sector_size; | 860 | flash->mtd.erasesize = info->sector_size; |
842 | } | 861 | } |
843 | 862 | ||
863 | if (info->flags & M25P_NO_ERASE) | ||
864 | flash->mtd.flags |= MTD_NO_ERASE; | ||
865 | |||
844 | flash->mtd.dev.parent = &spi->dev; | 866 | flash->mtd.dev.parent = &spi->dev; |
867 | flash->page_size = info->page_size; | ||
868 | flash->addr_width = info->addr_width; | ||
845 | 869 | ||
846 | dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name, | 870 | dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name, |
847 | (long long)flash->mtd.size >> 10); | 871 | (long long)flash->mtd.size >> 10); |