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 | |
| 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>
| -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); |
