JFFS for WNR3500Lv2
[tomato.git] / release / src-rt / linux / linux-2.6 / drivers / mtd / devices / sflash.c
blob1523114519a9bb2a041d9536648b2833bda2cbb2
1 /*
2 * Broadcom SiliconBackplane chipcommon serial flash interface
4 * Copyright (C) 2010, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 * $Id: sflash.c,v 1.6 2011-02-10 10:55:57 Exp $
21 #include <linux/config.h>
22 #include <linux/module.h>
23 #include <linux/slab.h>
24 #include <linux/ioport.h>
25 #include <linux/mtd/compatmac.h>
26 #include <linux/mtd/mtd.h>
27 #include <linux/mtd/partitions.h>
28 #include <linux/errno.h>
29 #include <linux/pci.h>
30 #include <linux/delay.h>
31 #include <asm/io.h>
33 #include <typedefs.h>
34 #include <osl.h>
35 #include <bcmutils.h>
36 #include <bcmdevs.h>
37 #include <bcmnvram.h>
38 #include <siutils.h>
39 #include <hndpci.h>
40 #include <pcicfg.h>
41 #include <hndsoc.h>
42 #include <sbchipc.h>
43 #include <sflash.h>
45 #ifdef CONFIG_MTD_PARTITIONS
46 extern struct mtd_partition * init_mtd_partitions(struct mtd_info *mtd, size_t size);
47 #endif
49 extern struct mutex *partitions_mutex_init(void);
51 struct sflash_mtd {
52 si_t *sih;
53 chipcregs_t *cc;
54 struct mtd_info mtd;
55 struct mtd_erase_region_info region;
58 /* Private global state */
59 static struct sflash_mtd sflash;
61 static int
62 sflash_mtd_poll(struct sflash_mtd *sflash, unsigned int offset, int timeout)
64 int now = jiffies;
65 int ret = 0;
67 for (;;) {
68 if (!sflash_poll(sflash->sih, sflash->cc, offset))
69 break;
71 if (time_after((unsigned long)jiffies, (unsigned long)now + timeout)) {
72 if (!sflash_poll(sflash->sih, sflash->cc, offset))
73 break;
75 printk(KERN_ERR "sflash: timeout\n");
76 ret = -ETIMEDOUT;
77 break;
79 udelay(1);
82 return ret;
85 static int
86 sflash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
88 struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv;
89 int bytes, ret = 0;
91 /* Check address range */
92 if (!len)
93 return 0;
94 if ((from + len) > mtd->size)
95 return -EINVAL;
97 mutex_lock(mtd->mutex);
99 *retlen = 0;
100 while (len) {
101 if ((bytes = sflash_read(sflash->sih, sflash->cc, (uint) from, len, buf)) < 0) {
102 ret = bytes;
103 break;
105 from += (loff_t) bytes;
106 len -= bytes;
107 buf += bytes;
108 *retlen += bytes;
111 mutex_unlock(mtd->mutex);
112 return ret;
115 static int
116 sflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
118 struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv;
119 int bytes, ret = 0;
121 /* Check address range */
122 if (!len)
123 return 0;
124 if ((to + len) > mtd->size)
125 return -EINVAL;
127 mutex_lock(mtd->mutex);
128 *retlen = 0;
129 while (len) {
130 if ((bytes = sflash_write(sflash->sih, sflash->cc, (uint) to, len, buf)) < 0) {
131 ret = bytes;
132 break;
134 if ((ret = sflash_mtd_poll(sflash, (unsigned int) to, HZ)))
135 break;
136 to += (loff_t) bytes;
137 len -= bytes;
138 buf += bytes;
139 *retlen += bytes;
142 mutex_unlock(mtd->mutex);
143 return ret;
146 static int
147 sflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
149 struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv;
150 int i, j, ret = 0;
151 unsigned int addr, len;
153 /* Check address range */
154 if (!erase->len)
155 return 0;
156 if ((erase->addr + erase->len) > mtd->size)
157 return -EINVAL;
159 mutex_lock(mtd->mutex);
160 addr = erase->addr;
161 len = erase->len;
163 /* Ensure that requested region is aligned */
164 for (i = 0; i < mtd->numeraseregions; i++) {
165 for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
166 if (addr == mtd->eraseregions[i].offset +
167 mtd->eraseregions[i].erasesize * j &&
168 len >= mtd->eraseregions[i].erasesize) {
169 if ((ret = sflash_erase(sflash->sih, sflash->cc, addr)) < 0)
170 break;
171 if ((ret = sflash_mtd_poll(sflash, addr, 10 * HZ)))
172 break;
173 addr += mtd->eraseregions[i].erasesize;
174 len -= mtd->eraseregions[i].erasesize;
177 if (ret)
178 break;
181 /* Set erase status */
182 if (ret)
183 erase->state = MTD_ERASE_FAILED;
184 else
185 erase->state = MTD_ERASE_DONE;
187 mutex_unlock(mtd->mutex);
189 /* Call erase callback */
190 if (erase->callback)
191 erase->callback(erase);
193 return ret;
196 #if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
197 #define sflash_mtd_init init_module
198 #define sflash_mtd_exit cleanup_module
199 #endif
201 static int __init
202 sflash_mtd_init(void)
204 int ret = 0;
205 struct sflash *info;
206 struct pci_dev *dev = NULL;
207 #ifdef CONFIG_MTD_PARTITIONS
208 struct mtd_partition *parts;
209 int i;
210 #endif
212 list_for_each_entry(dev, &((pci_find_bus(0, 0))->devices), bus_list) {
213 if ((dev != NULL) && (dev->device == CC_CORE_ID))
214 break;
217 if (dev == NULL) {
218 printk(KERN_ERR "sflash: chipcommon not found\n");
219 return -ENODEV;
222 memset(&sflash, 0, sizeof(struct sflash_mtd));
224 /* attach to the backplane */
225 if (!(sflash.sih = si_kattach(SI_OSH))) {
226 printk(KERN_ERR "sflash: error attaching to backplane\n");
227 ret = -EIO;
228 goto fail;
231 /* Map registers and flash base */
232 if (!(sflash.cc = ioremap_nocache(
233 pci_resource_start(dev, 0),
234 pci_resource_len(dev, 0)))) {
235 printk(KERN_ERR "sflash: error mapping registers\n");
236 ret = -EIO;
237 goto fail;
240 /* Initialize serial flash access */
241 if (!(info = sflash_init(sflash.sih, sflash.cc))) {
242 printk(KERN_ERR "sflash: found no supported devices\n");
243 ret = -ENODEV;
244 goto fail;
247 /* Setup region info */
248 sflash.region.offset = 0;
249 sflash.region.erasesize = info->blocksize;
250 sflash.region.numblocks = info->numblocks;
251 if (sflash.region.erasesize > sflash.mtd.erasesize)
252 sflash.mtd.erasesize = sflash.region.erasesize;
253 sflash.mtd.size = info->size;
254 sflash.mtd.numeraseregions = 1;
256 /* Register with MTD */
257 sflash.mtd.name = "sflash";
258 sflash.mtd.type = MTD_NORFLASH;
259 sflash.mtd.flags = MTD_CAP_NORFLASH;
260 sflash.mtd.eraseregions = &sflash.region;
261 sflash.mtd.erase = sflash_mtd_erase;
262 sflash.mtd.read = sflash_mtd_read;
263 sflash.mtd.write = sflash_mtd_write;
264 sflash.mtd.writesize = 1;
265 sflash.mtd.priv = &sflash;
266 sflash.mtd.owner = THIS_MODULE;
267 sflash.mtd.mutex = partitions_mutex_init();
268 if (!sflash.mtd.mutex)
269 return -ENOMEM;
271 #ifdef CONFIG_MTD_PARTITIONS
272 parts = init_mtd_partitions(&sflash.mtd, sflash.mtd.size);
273 for (i = 0; parts[i].name; i++);
274 ret = add_mtd_partitions(&sflash.mtd, parts, i);
275 if (ret) {
276 printk(KERN_ERR "sflash: add_mtd failed\n");
277 goto fail;
279 #endif
281 return 0;
283 fail:
284 if (sflash.cc)
285 iounmap((void *) sflash.cc);
286 if (sflash.sih)
287 si_detach(sflash.sih);
288 return ret;
291 static void __exit
292 sflash_mtd_exit(void)
294 #ifdef CONFIG_MTD_PARTITIONS
295 del_mtd_partitions(&sflash.mtd);
296 #else
297 del_mtd_device(&sflash.mtd);
298 #endif
299 iounmap((void *) sflash.cc);
300 si_detach(sflash.sih);
303 module_init(sflash_mtd_init);
304 module_exit(sflash_mtd_exit);