K2.6 patches and update.
[tomato.git] / release / src-rt / linux / linux-2.6 / drivers / mtd / devices / bcm_nflash.c
bloba964bf510825f8ca6b4c579729b8cd24d1bc71e0
1 /*
2 * Broadcom SiliconBackplane chipcommon serial flash interface
4 * Copyright (C) 2009, Broadcom Corporation
5 * All Rights Reserved.
6 *
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
12 * $Id $
15 #include <linux/config.h>
16 #include <linux/module.h>
17 #include <linux/slab.h>
18 #include <linux/ioport.h>
19 #include <linux/mtd/compatmac.h>
20 #include <linux/mtd/mtd.h>
21 #include <linux/mtd/partitions.h>
22 #include <linux/errno.h>
23 #include <linux/pci.h>
24 #include <linux/delay.h>
25 #include <asm/io.h>
27 #include <typedefs.h>
28 #include <osl.h>
29 #include <bcmutils.h>
30 #include <bcmdevs.h>
31 #include <bcmnvram.h>
32 #include <siutils.h>
33 #include <hndpci.h>
34 #include <pcicfg.h>
35 #include <hndsoc.h>
36 #include <sbchipc.h>
37 #include <nflash.h>
39 #ifdef CONFIG_MTD_PARTITIONS
40 extern struct mtd_partition * init_nflash_mtd_partitions(struct mtd_info *mtd, size_t size);
42 struct mtd_partition *nflash_parts;
43 #endif
46 struct nflash_mtd {
47 si_t *sih;
48 chipcregs_t *cc;
49 struct mtd_info mtd;
50 struct mtd_erase_region_info region;
51 unsigned char *map;
54 /* Private global state */
55 static struct nflash_mtd nflash;
57 static int
58 nflash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
60 struct nflash_mtd *nflash = (struct nflash_mtd *) mtd->priv;
61 int bytes, ret = 0;
62 struct mtd_partition *part = NULL;
63 uint extra = 0;
64 uchar *tmpbuf = NULL;
65 int size;
66 uint offset, blocksize, mask, blk_offset, off;
67 uint skip_bytes = 0, good_bytes = 0;
68 int blk_idx, i;
69 int need_copy = 0;
70 uchar *ptr = NULL;
72 /* Locate the part */
73 for (i = 0; nflash_parts[i].name; i++) {
74 if (from >= nflash_parts[i].offset &&
75 ((nflash_parts[i+1].name == NULL) || (from < nflash_parts[i+1].offset))) {
76 part = &nflash_parts[i];
77 break;
80 if (!part)
81 return -EINVAL;
82 /* Check address range */
83 if (!len)
84 return 0;
85 if ((from + len) > mtd->size)
86 return -EINVAL;
87 offset = from;
88 if ((offset & (NFL_SECTOR_SIZE - 1)) != 0) {
89 extra = offset & (NFL_SECTOR_SIZE - 1);
90 offset -= extra;
91 len += extra;
92 need_copy = 1;
94 size = (len + (NFL_SECTOR_SIZE - 1)) & ~(NFL_SECTOR_SIZE - 1);
95 if (size != len)
96 need_copy = 1;
97 if (!need_copy) {
98 ptr = buf;
99 } else {
100 tmpbuf = (uchar *)kmalloc(size, GFP_KERNEL);
101 ptr = tmpbuf;
104 blocksize = mtd->erasesize;
105 mask = blocksize - 1;
106 blk_offset = offset & ~mask;
107 good_bytes = part->offset & ~mask;
108 /* Check and skip bad blocks */
109 for (blk_idx = good_bytes/blocksize; blk_idx < mtd->eraseregions->numblocks; blk_idx++) {
110 if ((nflash->map[blk_idx] != 0) ||
111 (nflash_checkbadb(nflash->sih, nflash->cc, (blocksize*blk_idx)) != 0)) {
112 skip_bytes += blocksize;
113 nflash->map[blk_idx] = 1;
114 } else {
115 if (good_bytes == blk_offset)
116 break;
117 good_bytes += blocksize;
120 if (blk_idx == mtd->eraseregions->numblocks) {
121 ret = -EINVAL;
122 goto done;
124 blk_offset = blocksize * blk_idx;
125 *retlen = 0;
126 while (len > 0) {
127 off = offset + skip_bytes;
129 /* Check and skip bad blocks */
130 if (off >= (blk_offset + blocksize)) {
131 blk_offset += blocksize;
132 blk_idx++;
133 while (((nflash->map[blk_idx] != 0) ||
134 (nflash_checkbadb(nflash->sih, nflash->cc, blk_offset) != 0)) &&
135 (blk_offset < mtd->size)) {
136 skip_bytes += blocksize;
137 nflash->map[blk_idx] = 1;
138 blk_offset += blocksize;
139 blk_idx++;
141 if (blk_offset >= mtd->size) {
142 ret = -EINVAL;
143 goto done;
145 off = offset + skip_bytes;
148 if ((bytes = nflash_read(nflash->sih, nflash->cc, off, NFL_SECTOR_SIZE, ptr)) < 0) {
149 ret = bytes;
150 goto done;
152 if (bytes > len)
153 bytes = len;
154 offset += bytes;
155 len -= bytes;
156 ptr += bytes;
157 *retlen += bytes;
160 done:
161 if (tmpbuf) {
162 *retlen -= extra;
163 memcpy(buf, tmpbuf+extra, *retlen);
164 kfree(tmpbuf);
166 return ret;
169 static int
170 nflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
172 struct nflash_mtd *nflash = (struct nflash_mtd *) mtd->priv;
173 int bytes, ret = 0;
174 struct mtd_partition *part = NULL;
175 u_char *block = NULL;
176 u_char *ptr = buf;
177 uint offset, blocksize, mask, blk_offset, off;
178 uint skip_bytes = 0, good_bytes = 0;
179 int blk_idx, i;
180 int read_len, write_len, copy_len;
181 loff_t from;
182 u_char *write_ptr;
184 /* Locate the part */
185 for (i = 0; nflash_parts[i].name; i++) {
186 if (to >= nflash_parts[i].offset &&
187 ((nflash_parts[i+1].name == NULL) || (to < nflash_parts[i+1].offset))) {
188 part = &nflash_parts[i];
189 break;
192 if (!part)
193 return -EINVAL;
194 /* Check address range */
195 if (!len)
196 return 0;
197 if ((to + len) > (part->offset + part->size))
198 return -EINVAL;
199 offset = to;
200 blocksize = mtd->erasesize;
201 if (!(block = kmalloc(blocksize, GFP_KERNEL)))
202 return -ENOMEM;
203 mask = blocksize - 1;
204 /* Check and skip bad blocks */
205 blk_offset = offset & ~mask;
206 good_bytes = part->offset & ~mask;
207 for (blk_idx = good_bytes/blocksize; blk_idx < (part->offset+part->size)/blocksize; blk_idx++) {
208 if ((nflash->map[blk_idx] != 0) ||
209 (nflash_checkbadb(nflash->sih, nflash->cc, (blocksize*blk_idx)) != 0)) {
210 skip_bytes += blocksize;
211 nflash->map[blk_idx] = 1;
212 } else {
213 if (good_bytes == blk_offset)
214 break;
215 good_bytes += blocksize;
218 if (blk_idx == (part->offset+part->size)/blocksize) {
219 ret = -EINVAL;
220 goto done;
222 blk_offset = blocksize * blk_idx;
223 /* Backup and erase one block at a time */
224 *retlen = 0;
225 while (len) {
226 /* Align offset */
227 from = offset & ~mask;
228 /* Copy existing data into holding block if necessary */
229 if (((offset & (blocksize-1)) != 0) || (len < blocksize)) {
230 if ((ret = nflash_mtd_read(mtd, from, blocksize, &read_len, block)))
231 goto done;
232 if (read_len != blocksize) {
233 ret = -EINVAL;
234 goto done;
237 /* Copy input data into holding block */
238 copy_len = min(len, blocksize - (offset & mask));
239 memcpy(block + (offset & mask), ptr, copy_len);
240 off = (uint) from + skip_bytes;
241 /* Erase block */
242 if ((ret = nflash_erase(nflash->sih, nflash->cc, off)) < 0) {
243 goto done;
245 /* Write holding block */
246 write_ptr = block;
247 write_len = blocksize;
248 while (write_len) {
249 if ((bytes = nflash_write(nflash->sih, nflash->cc,
250 (uint) from + skip_bytes,
251 (uint) write_len,
252 (uchar *) write_ptr)) < 0) {
253 ret = bytes;
254 goto done;
256 from += bytes;
257 write_len -= bytes;
258 write_ptr += bytes;
260 offset += copy_len;
261 len -= copy_len;
262 ptr += copy_len;
263 *retlen += copy_len;
264 /* Check and skip bad blocks */
265 if (len) {
266 blk_offset += blocksize;
267 blk_idx++;
268 while (((nflash->map[blk_idx] != 0) ||
269 (nflash_checkbadb(nflash->sih, nflash->cc, blk_offset) != 0)) &&
270 (blk_offset < (part->offset+part->size))) {
271 skip_bytes += blocksize;
272 nflash->map[blk_idx] = 1;
273 blk_offset += blocksize;
274 blk_idx++;
276 if (blk_offset >= (part->offset+part->size)) {
277 ret = -EINVAL;
278 goto done;
282 done:
283 if (block)
284 kfree(block);
285 return ret;
288 static int
289 nflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
291 struct nflash_mtd *nflash = (struct nflash_mtd *) mtd->priv;
292 int i, j, ret = 0;
293 unsigned int addr, len;
295 /* Check address range */
296 if (!erase->len)
297 return 0;
298 if ((erase->addr + erase->len) > mtd->size)
299 return -EINVAL;
301 addr = erase->addr;
302 len = erase->len;
304 /* Ensure that requested region is aligned */
305 for (i = 0; i < mtd->numeraseregions; i++) {
306 for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
307 if (addr == mtd->eraseregions[i].offset +
308 mtd->eraseregions[i].erasesize * j &&
309 len >= mtd->eraseregions[i].erasesize) {
310 if ((ret = nflash_erase(nflash->sih, nflash->cc, addr)) < 0)
311 break;
312 addr += mtd->eraseregions[i].erasesize;
313 len -= mtd->eraseregions[i].erasesize;
316 if (ret)
317 break;
320 /* Set erase status */
321 if (ret)
322 erase->state = MTD_ERASE_FAILED;
323 else
324 erase->state = MTD_ERASE_DONE;
326 /* Call erase callback */
327 if (erase->callback)
328 erase->callback(erase);
330 return ret;
333 #if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
334 #define nflash_mtd_init init_module
335 #define nflash_mtd_exit cleanup_module
336 #endif
338 static int __init
339 nflash_mtd_init(void)
341 int ret = 0;
342 struct nflash *info;
343 struct pci_dev *dev = NULL;
344 #ifdef CONFIG_MTD_PARTITIONS
345 struct mtd_partition *parts;
346 int i;
347 #endif
349 list_for_each_entry(dev, &((pci_find_bus(0, 0))->devices), bus_list) {
350 if ((dev != NULL) && (dev->device == CC_CORE_ID))
351 break;
354 if (dev == NULL) {
355 printk(KERN_ERR "nflash: chipcommon not found\n");
356 return -ENODEV;
359 memset(&nflash, 0, sizeof(struct nflash_mtd));
361 /* attach to the backplane */
362 if (!(nflash.sih = si_kattach(SI_OSH))) {
363 printk(KERN_ERR "nflash: error attaching to backplane\n");
364 ret = -EIO;
365 goto fail;
368 /* Map registers and flash base */
369 if (!(nflash.cc = ioremap_nocache(
370 pci_resource_start(dev, 0),
371 pci_resource_len(dev, 0)))) {
372 printk(KERN_ERR "nflash: error mapping registers\n");
373 ret = -EIO;
374 goto fail;
377 /* Initialize serial flash access */
378 if (!(info = nflash_init(nflash.sih, nflash.cc))) {
379 printk(KERN_ERR "nflash: found no supported devices\n");
380 ret = -ENODEV;
381 goto fail;
384 /* Setup region info */
385 nflash.region.offset = 0;
386 nflash.region.erasesize = info->blocksize;
387 nflash.region.numblocks = info->numblocks;
388 if (nflash.region.erasesize > nflash.mtd.erasesize)
389 nflash.mtd.erasesize = nflash.region.erasesize;
390 /* At most 2GB is supported */
391 nflash.mtd.size = (info->size >= (1 << 11)) ? (1 << 31) : (info->size << 20);
392 nflash.mtd.numeraseregions = 1;
393 nflash.map = (unsigned char *)kmalloc(info->numblocks, GFP_KERNEL);
394 if (nflash.map)
395 memset(nflash.map, 0, info->numblocks);
397 /* Register with MTD */
398 nflash.mtd.name = "nflash";
399 nflash.mtd.type = MTD_NANDFLASH;
400 nflash.mtd.flags = MTD_CAP_NANDFLASH;
401 nflash.mtd.eraseregions = &nflash.region;
402 nflash.mtd.erase = nflash_mtd_erase;
403 nflash.mtd.read = nflash_mtd_read;
404 nflash.mtd.write = nflash_mtd_write;
405 nflash.mtd.writesize = NFL_SECTOR_SIZE;
406 nflash.mtd.priv = &nflash;
407 nflash.mtd.owner = THIS_MODULE;
410 #ifdef CONFIG_MTD_PARTITIONS
411 parts = init_nflash_mtd_partitions(&nflash.mtd, nflash.mtd.size);
412 if (!parts)
413 goto fail;
414 for (i = 0; parts[i].name; i++);
415 ret = add_mtd_partitions(&nflash.mtd, parts, i);
416 if (ret) {
417 printk(KERN_ERR "nflash: add_mtd failed\n");
418 goto fail;
420 nflash_parts = parts;
421 #endif
422 return 0;
424 fail:
425 if (nflash.cc)
426 iounmap((void *) nflash.cc);
427 if (nflash.sih)
428 si_detach(nflash.sih);
429 return ret;
432 static void __exit
433 nflash_mtd_exit(void)
435 #ifdef CONFIG_MTD_PARTITIONS
436 del_mtd_partitions(&nflash.mtd);
437 #else
438 del_mtd_device(&nflash.mtd);
439 #endif
440 iounmap((void *) nflash.cc);
441 si_detach(nflash.sih);
444 module_init(nflash_mtd_init);
445 module_exit(nflash_mtd_exit);