diff options
author | David Woodhouse <dwmw2@infradead.org> | 2006-05-12 23:12:40 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2006-05-12 23:12:40 -0400 |
commit | 9d75414b4fa7390975ef0e2b56ff40425657fe52 (patch) | |
tree | ff21b8e752f83f285c0452c45b3f0c54ccb30550 | |
parent | c3f8abf481c2d2b221b028f7369bc6dd39a9590e (diff) |
[MTD NAND] Update CS553x NAND driver: Hardware ECC support, optimisations.
- Implement HW ECC support,
- Provide read_buf() and write_buf() routines using memcpy
- Use on-flash bad block table
- Fix module refcounting
- Avoid read/modify/write in hwcontrol()
- Minor cosmetic fixes
Partly based on code and ideas from Tom Sylla <tom.sylla@amd.com>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
-rw-r--r-- | drivers/mtd/nand/cs553x_nand.c | 102 |
1 files changed, 75 insertions, 27 deletions
diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index 91207a45ad55..4f0b338f2f30 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c | |||
@@ -4,6 +4,7 @@ | |||
4 | * (C) 2005, 2006 Red Hat Inc. | 4 | * (C) 2005, 2006 Red Hat Inc. |
5 | * | 5 | * |
6 | * Author: David Woodhouse <dwmw2@infradead.org> | 6 | * Author: David Woodhouse <dwmw2@infradead.org> |
7 | * Tom Sylla <tom.sylla@amd.com> | ||
7 | * | 8 | * |
8 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License version 2 as | 10 | * it under the terms of the GNU General Public License version 2 as |
@@ -22,6 +23,7 @@ | |||
22 | #include <linux/pci.h> | 23 | #include <linux/pci.h> |
23 | #include <linux/mtd/mtd.h> | 24 | #include <linux/mtd/mtd.h> |
24 | #include <linux/mtd/nand.h> | 25 | #include <linux/mtd/nand.h> |
26 | #include <linux/mtd/nand_ecc.h> | ||
25 | #include <linux/mtd/partitions.h> | 27 | #include <linux/mtd/partitions.h> |
26 | 28 | ||
27 | #include <asm/msr.h> | 29 | #include <asm/msr.h> |
@@ -87,11 +89,34 @@ | |||
87 | #define CS_NAND_ECC_CLRECC (1<<1) | 89 | #define CS_NAND_ECC_CLRECC (1<<1) |
88 | #define CS_NAND_ECC_ENECC (1<<0) | 90 | #define CS_NAND_ECC_ENECC (1<<0) |
89 | 91 | ||
92 | static void cs553x_read_buf(struct mtd_info *mtd, u_char *buf, int len) | ||
93 | { | ||
94 | struct nand_chip *this = mtd->priv; | ||
95 | |||
96 | while (unlikely(len > 0x800)) { | ||
97 | memcpy_fromio(buf, this->IO_ADDR_R, 0x800); | ||
98 | buf += 0x800; | ||
99 | len -= 0x800; | ||
100 | } | ||
101 | memcpy_fromio(buf, this->IO_ADDR_R, len); | ||
102 | } | ||
103 | |||
104 | static void cs553x_write_buf(struct mtd_info *mtd, const u_char *buf, int len) | ||
105 | { | ||
106 | struct nand_chip *this = mtd->priv; | ||
107 | |||
108 | while (unlikely(len > 0x800)) { | ||
109 | memcpy_toio(this->IO_ADDR_R, buf, 0x800); | ||
110 | buf += 0x800; | ||
111 | len -= 0x800; | ||
112 | } | ||
113 | memcpy_toio(this->IO_ADDR_R, buf, len); | ||
114 | } | ||
115 | |||
90 | static unsigned char cs553x_read_byte(struct mtd_info *mtd) | 116 | static unsigned char cs553x_read_byte(struct mtd_info *mtd) |
91 | { | 117 | { |
92 | struct nand_chip *this = mtd->priv; | 118 | struct nand_chip *this = mtd->priv; |
93 | unsigned char foo = readb(this->IO_ADDR_R); | 119 | return readb(this->IO_ADDR_R); |
94 | return foo; | ||
95 | } | 120 | } |
96 | 121 | ||
97 | static void cs553x_write_byte(struct mtd_info *mtd, u_char byte) | 122 | static void cs553x_write_byte(struct mtd_info *mtd, u_char byte) |
@@ -110,35 +135,29 @@ static void cs553x_hwcontrol(struct mtd_info *mtd, int cmd) | |||
110 | { | 135 | { |
111 | struct nand_chip *this = mtd->priv; | 136 | struct nand_chip *this = mtd->priv; |
112 | void __iomem *mmio_base = this->IO_ADDR_R; | 137 | void __iomem *mmio_base = this->IO_ADDR_R; |
113 | 138 | unsigned char ctl; | |
114 | uint8_t old = readb(mmio_base + MM_NAND_CTL); | ||
115 | 139 | ||
116 | switch(cmd) { | 140 | switch(cmd) { |
117 | case NAND_CTL_SETCLE: | 141 | case NAND_CTL_SETCLE: |
118 | old |= CS_NAND_CTL_CLE; | 142 | ctl = CS_NAND_CTL_CLE; |
119 | break; | 143 | break; |
120 | 144 | ||
121 | case NAND_CTL_CLRCLE: | 145 | case NAND_CTL_CLRCLE: |
122 | old &= ~CS_NAND_CTL_CLE; | ||
123 | break; | ||
124 | |||
125 | case NAND_CTL_SETALE: | ||
126 | old |= CS_NAND_CTL_ALE; | ||
127 | break; | ||
128 | |||
129 | case NAND_CTL_CLRALE: | 146 | case NAND_CTL_CLRALE: |
130 | old &= ~CS_NAND_CTL_ALE; | 147 | case NAND_CTL_SETNCE: |
148 | ctl = 0; | ||
131 | break; | 149 | break; |
132 | 150 | ||
133 | case NAND_CTL_SETNCE: | 151 | case NAND_CTL_SETALE: |
134 | old &= ~CS_NAND_CTL_CE; | 152 | ctl = CS_NAND_CTL_ALE; |
135 | break; | 153 | break; |
136 | 154 | ||
155 | default: | ||
137 | case NAND_CTL_CLRNCE: | 156 | case NAND_CTL_CLRNCE: |
138 | old |= CS_NAND_CTL_CE; | 157 | ctl = CS_NAND_CTL_CE; |
139 | break; | 158 | break; |
140 | } | 159 | } |
141 | writeb(old, mmio_base + MM_NAND_CTL); | 160 | writeb(ctl, mmio_base + MM_NAND_CTL); |
142 | } | 161 | } |
143 | 162 | ||
144 | 163 | ||
@@ -151,6 +170,29 @@ static int cs553x_device_ready(struct mtd_info *mtd) | |||
151 | return (foo & CS_NAND_STS_FLASH_RDY) && !(foo & CS_NAND_CTLR_BUSY); | 170 | return (foo & CS_NAND_STS_FLASH_RDY) && !(foo & CS_NAND_CTLR_BUSY); |
152 | } | 171 | } |
153 | 172 | ||
173 | |||
174 | static void cs_enable_hwecc(struct mtd_info *mtd, int mode) | ||
175 | { | ||
176 | struct nand_chip *this = mtd->priv; | ||
177 | void __iomem *mmio_base = this->IO_ADDR_R; | ||
178 | |||
179 | writeb(0x07, mmio_base + MM_NAND_ECC_CTL); | ||
180 | } | ||
181 | |||
182 | static int cs_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) | ||
183 | { | ||
184 | uint32_t ecc; | ||
185 | struct nand_chip *this = mtd->priv; | ||
186 | void __iomem *mmio_base = this->IO_ADDR_R; | ||
187 | |||
188 | ecc = readl(mmio_base + MM_NAND_STS); | ||
189 | |||
190 | ecc_code[1] = ecc >> 8; | ||
191 | ecc_code[0] = ecc >> 16; | ||
192 | ecc_code[2] = ecc >> 24; | ||
193 | return 0; | ||
194 | } | ||
195 | |||
154 | static struct mtd_info *cs553x_mtd[4]; | 196 | static struct mtd_info *cs553x_mtd[4]; |
155 | 197 | ||
156 | static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) | 198 | static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) |
@@ -167,7 +209,7 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) | |||
167 | } | 209 | } |
168 | 210 | ||
169 | /* Allocate memory for MTD device structure and private data */ | 211 | /* Allocate memory for MTD device structure and private data */ |
170 | new_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL); | 212 | new_mtd = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL); |
171 | if (!new_mtd) { | 213 | if (!new_mtd) { |
172 | printk(KERN_WARNING "Unable to allocate CS553X NAND MTD device structure.\n"); | 214 | printk(KERN_WARNING "Unable to allocate CS553X NAND MTD device structure.\n"); |
173 | err = -ENOMEM; | 215 | err = -ENOMEM; |
@@ -178,8 +220,8 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) | |||
178 | this = (struct nand_chip *) (&new_mtd[1]); | 220 | this = (struct nand_chip *) (&new_mtd[1]); |
179 | 221 | ||
180 | /* Initialize structures */ | 222 | /* Initialize structures */ |
181 | memset((char *) new_mtd, 0, sizeof(struct mtd_info)); | 223 | memset(new_mtd, 0, sizeof(struct mtd_info)); |
182 | memset((char *) this, 0, sizeof(struct nand_chip)); | 224 | memset(this, 0, sizeof(struct nand_chip)); |
183 | 225 | ||
184 | /* Link the private data with the MTD structure */ | 226 | /* Link the private data with the MTD structure */ |
185 | new_mtd->priv = this; | 227 | new_mtd->priv = this; |
@@ -196,27 +238,33 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) | |||
196 | this->dev_ready = cs553x_device_ready; | 238 | this->dev_ready = cs553x_device_ready; |
197 | this->read_byte = cs553x_read_byte; | 239 | this->read_byte = cs553x_read_byte; |
198 | this->write_byte = cs553x_write_byte; | 240 | this->write_byte = cs553x_write_byte; |
241 | this->read_buf = cs553x_read_buf; | ||
242 | this->write_buf = cs553x_write_buf; | ||
199 | 243 | ||
200 | /* 20 us command delay time */ | 244 | this->chip_delay = 0; |
201 | this->chip_delay = 20; | ||
202 | this->eccmode = NAND_ECC_SOFT; | ||
203 | 245 | ||
246 | this->eccmode = NAND_ECC_HW3_256; | ||
247 | this->enable_hwecc = cs_enable_hwecc; | ||
248 | this->calculate_ecc = cs_calculate_ecc; | ||
249 | this->correct_data = nand_correct_data; | ||
250 | |||
204 | /* Enable the following for a flash based bad block table */ | 251 | /* Enable the following for a flash based bad block table */ |
205 | // this->options = NAND_USE_FLASH_BBT; | 252 | this->options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR; |
206 | 253 | ||
207 | /* Scan to find existance of the device */ | 254 | /* Scan to find existance of the device */ |
208 | if (nand_scan (new_mtd, 1)) { | 255 | if (nand_scan(new_mtd, 1)) { |
209 | err = -ENXIO; | 256 | err = -ENXIO; |
210 | goto out_ior; | 257 | goto out_ior; |
211 | } | 258 | } |
212 | 259 | ||
260 | new_mtd->owner = THIS_MODULE; | ||
213 | cs553x_mtd[cs] = new_mtd; | 261 | cs553x_mtd[cs] = new_mtd; |
214 | goto out; | 262 | goto out; |
215 | 263 | ||
216 | out_ior: | 264 | out_ior: |
217 | iounmap((void *)this->IO_ADDR_R); | 265 | iounmap((void *)this->IO_ADDR_R); |
218 | out_mtd: | 266 | out_mtd: |
219 | kfree (new_mtd); | 267 | kfree(new_mtd); |
220 | out: | 268 | out: |
221 | return err; | 269 | return err; |
222 | } | 270 | } |
@@ -283,7 +331,7 @@ static void __exit cs553x_cleanup (void) | |||
283 | iounmap(mmio_base); | 331 | iounmap(mmio_base); |
284 | 332 | ||
285 | /* Free the MTD device structure */ | 333 | /* Free the MTD device structure */ |
286 | kfree (mtd); | 334 | kfree(mtd); |
287 | } | 335 | } |
288 | } | 336 | } |
289 | module_exit(cs553x_cleanup); | 337 | module_exit(cs553x_cleanup); |