diff options
author | Kyungmin Park <kyungmin.park@samsung.com> | 2006-12-22 02:21:54 -0500 |
---|---|---|
committer | Artem Bityutskiy <dedekind@infradead.org> | 2007-01-10 07:35:00 -0500 |
commit | 60d84f9739a47d0ed8e19805d9056e39fba31c79 (patch) | |
tree | 8a93ba3f6de707446e191328f7190f7669d3619c | |
parent | f62724873652ddb19edf7f92843e9456fe3be3ea (diff) |
[MTD] OneNAND: add subpage write support
OneNAND supports up to 4 writes at one NAND page. Add support of this feature.
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 56 | ||||
-rw-r--r-- | include/linux/mtd/onenand.h | 2 |
2 files changed, 44 insertions, 14 deletions
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 3fab4d1b68bd..51fb84055ba3 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c | |||
@@ -192,8 +192,6 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le | |||
192 | struct onenand_chip *this = mtd->priv; | 192 | struct onenand_chip *this = mtd->priv; |
193 | int value, readcmd = 0, block_cmd = 0; | 193 | int value, readcmd = 0, block_cmd = 0; |
194 | int block, page; | 194 | int block, page; |
195 | /* Now we use page size operation */ | ||
196 | int sectors = 4, count = 4; | ||
197 | 195 | ||
198 | /* Address translation */ | 196 | /* Address translation */ |
199 | switch (cmd) { | 197 | switch (cmd) { |
@@ -245,6 +243,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le | |||
245 | } | 243 | } |
246 | 244 | ||
247 | if (page != -1) { | 245 | if (page != -1) { |
246 | /* Now we use page size operation */ | ||
247 | int sectors = 4, count = 4; | ||
248 | int dataram; | 248 | int dataram; |
249 | 249 | ||
250 | switch (cmd) { | 250 | switch (cmd) { |
@@ -914,6 +914,10 @@ static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr) | |||
914 | void __iomem *dataram0, *dataram1; | 914 | void __iomem *dataram0, *dataram1; |
915 | int ret = 0; | 915 | int ret = 0; |
916 | 916 | ||
917 | /* In partial page write, just skip it */ | ||
918 | if ((addr & (mtd->writesize - 1)) != 0) | ||
919 | return 0; | ||
920 | |||
917 | this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); | 921 | this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); |
918 | 922 | ||
919 | ret = this->wait(mtd, FL_READING); | 923 | ret = this->wait(mtd, FL_READING); |
@@ -936,7 +940,7 @@ static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr) | |||
936 | #define onenand_verify_oob(...) (0) | 940 | #define onenand_verify_oob(...) (0) |
937 | #endif | 941 | #endif |
938 | 942 | ||
939 | #define NOTALIGNED(x) ((x & (mtd->writesize - 1)) != 0) | 943 | #define NOTALIGNED(x) ((x & (this->subpagesize - 1)) != 0) |
940 | 944 | ||
941 | /** | 945 | /** |
942 | * onenand_write - [MTD Interface] write buffer to FLASH | 946 | * onenand_write - [MTD Interface] write buffer to FLASH |
@@ -954,6 +958,7 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
954 | struct onenand_chip *this = mtd->priv; | 958 | struct onenand_chip *this = mtd->priv; |
955 | int written = 0; | 959 | int written = 0; |
956 | int ret = 0; | 960 | int ret = 0; |
961 | int column, subpage; | ||
957 | 962 | ||
958 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_write: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); | 963 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_write: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); |
959 | 964 | ||
@@ -972,45 +977,61 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
972 | return -EINVAL; | 977 | return -EINVAL; |
973 | } | 978 | } |
974 | 979 | ||
980 | column = to & (mtd->writesize - 1); | ||
981 | subpage = column || (len & (mtd->writesize - 1)); | ||
982 | |||
975 | /* Grab the lock and see if the device is available */ | 983 | /* Grab the lock and see if the device is available */ |
976 | onenand_get_device(mtd, FL_WRITING); | 984 | onenand_get_device(mtd, FL_WRITING); |
977 | 985 | ||
978 | /* Loop until all data write */ | 986 | /* Loop until all data write */ |
979 | while (written < len) { | 987 | while (written < len) { |
980 | int thislen = min_t(int, mtd->writesize, len - written); | 988 | int bytes = mtd->writesize; |
981 | 989 | int thislen = min_t(int, bytes, len - written); | |
982 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->writesize); | 990 | u_char *wbuf = (u_char *) buf; |
991 | |||
992 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, bytes); | ||
993 | |||
994 | /* Partial page write */ | ||
995 | if (subpage) { | ||
996 | bytes = min_t(int, bytes - column, (int) len); | ||
997 | memset(this->page_buf, 0xff, mtd->writesize); | ||
998 | memcpy(this->page_buf + column, buf, bytes); | ||
999 | wbuf = this->page_buf; | ||
1000 | /* Even though partial write, we need page size */ | ||
1001 | thislen = mtd->writesize; | ||
1002 | } | ||
983 | 1003 | ||
984 | this->write_bufferram(mtd, ONENAND_DATARAM, buf, 0, thislen); | 1004 | this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, thislen); |
985 | this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); | 1005 | this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); |
986 | 1006 | ||
987 | this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); | 1007 | this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); |
988 | 1008 | ||
989 | onenand_update_bufferram(mtd, to, 1); | 1009 | /* In partial page write we don't update bufferram */ |
1010 | onenand_update_bufferram(mtd, to, !subpage); | ||
990 | 1011 | ||
991 | ret = this->wait(mtd, FL_WRITING); | 1012 | ret = this->wait(mtd, FL_WRITING); |
992 | if (ret) { | 1013 | if (ret) { |
993 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: write filaed %d\n", ret); | 1014 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: write filaed %d\n", ret); |
994 | goto out; | 1015 | break; |
995 | } | 1016 | } |
996 | 1017 | ||
997 | written += thislen; | ||
998 | |||
999 | /* Only check verify write turn on */ | 1018 | /* Only check verify write turn on */ |
1000 | ret = onenand_verify_page(mtd, (u_char *) buf, to); | 1019 | ret = onenand_verify_page(mtd, (u_char *) wbuf, to); |
1001 | if (ret) { | 1020 | if (ret) { |
1002 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: verify failed %d\n", ret); | 1021 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: verify failed %d\n", ret); |
1003 | goto out; | 1022 | break; |
1004 | } | 1023 | } |
1005 | 1024 | ||
1025 | written += thislen; | ||
1026 | |||
1006 | if (written == len) | 1027 | if (written == len) |
1007 | break; | 1028 | break; |
1008 | 1029 | ||
1030 | column = 0; | ||
1009 | to += thislen; | 1031 | to += thislen; |
1010 | buf += thislen; | 1032 | buf += thislen; |
1011 | } | 1033 | } |
1012 | 1034 | ||
1013 | out: | ||
1014 | /* Deselect and wake up anyone waiting on the device */ | 1035 | /* Deselect and wake up anyone waiting on the device */ |
1015 | onenand_release_device(mtd); | 1036 | onenand_release_device(mtd); |
1016 | 1037 | ||
@@ -2021,23 +2042,30 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) | |||
2021 | init_waitqueue_head(&this->wq); | 2042 | init_waitqueue_head(&this->wq); |
2022 | spin_lock_init(&this->chip_lock); | 2043 | spin_lock_init(&this->chip_lock); |
2023 | 2044 | ||
2045 | /* | ||
2046 | * Allow subpage writes up to oobsize. | ||
2047 | */ | ||
2024 | switch (mtd->oobsize) { | 2048 | switch (mtd->oobsize) { |
2025 | case 64: | 2049 | case 64: |
2026 | this->ecclayout = &onenand_oob_64; | 2050 | this->ecclayout = &onenand_oob_64; |
2051 | mtd->subpage_sft = 2; | ||
2027 | break; | 2052 | break; |
2028 | 2053 | ||
2029 | case 32: | 2054 | case 32: |
2030 | this->ecclayout = &onenand_oob_32; | 2055 | this->ecclayout = &onenand_oob_32; |
2056 | mtd->subpage_sft = 1; | ||
2031 | break; | 2057 | break; |
2032 | 2058 | ||
2033 | default: | 2059 | default: |
2034 | printk(KERN_WARNING "No OOB scheme defined for oobsize %d\n", | 2060 | printk(KERN_WARNING "No OOB scheme defined for oobsize %d\n", |
2035 | mtd->oobsize); | 2061 | mtd->oobsize); |
2062 | mtd->subpage_sft = 0; | ||
2036 | /* To prevent kernel oops */ | 2063 | /* To prevent kernel oops */ |
2037 | this->ecclayout = &onenand_oob_32; | 2064 | this->ecclayout = &onenand_oob_32; |
2038 | break; | 2065 | break; |
2039 | } | 2066 | } |
2040 | 2067 | ||
2068 | this->subpagesize = mtd->writesize >> mtd->subpage_sft; | ||
2041 | mtd->ecclayout = this->ecclayout; | 2069 | mtd->ecclayout = this->ecclayout; |
2042 | 2070 | ||
2043 | /* Fill in remaining MTD driver data */ | 2071 | /* Fill in remaining MTD driver data */ |
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 62ca0f429822..fe3500d7d4bb 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h | |||
@@ -88,6 +88,7 @@ struct onenand_bufferram { | |||
88 | * operation is in progress | 88 | * operation is in progress |
89 | * @state: [INTERN] the current state of the OneNAND device | 89 | * @state: [INTERN] the current state of the OneNAND device |
90 | * @page_buf: data buffer | 90 | * @page_buf: data buffer |
91 | * @subpagesize: [INTERN] holds the subpagesize | ||
91 | * @ecclayout: [REPLACEABLE] the default ecc placement scheme | 92 | * @ecclayout: [REPLACEABLE] the default ecc placement scheme |
92 | * @bbm: [REPLACEABLE] pointer to Bad Block Management | 93 | * @bbm: [REPLACEABLE] pointer to Bad Block Management |
93 | * @priv: [OPTIONAL] pointer to private chip date | 94 | * @priv: [OPTIONAL] pointer to private chip date |
@@ -128,6 +129,7 @@ struct onenand_chip { | |||
128 | onenand_state_t state; | 129 | onenand_state_t state; |
129 | unsigned char *page_buf; | 130 | unsigned char *page_buf; |
130 | 131 | ||
132 | int subpagesize; | ||
131 | struct nand_ecclayout *ecclayout; | 133 | struct nand_ecclayout *ecclayout; |
132 | 134 | ||
133 | void *bbm; | 135 | void *bbm; |