RT-AC66 3.0.0.4.374.130 core
[tomato.git] / release / src-rt-6.x / linux / linux-2.6 / drivers / mtd / devices / sflash.c
blobafa8a66e2aac2e9d71ca764269ed330da4604ad5
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: sflash.c,v 1.2 2009/11/06 09:55:49 Exp $
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 <sflash.h>
39 #ifdef CONFIG_MTD_PARTITIONS
40 extern struct mtd_partition * init_mtd_partitions(struct mtd_info *mtd, size_t size);
41 #endif
44 struct sflash_mtd {
45 si_t *sih;
46 chipcregs_t *cc;
47 struct mtd_info mtd;
48 struct mtd_erase_region_info region;
51 /* Private global state */
52 static struct sflash_mtd sflash;
54 static int
55 sflash_mtd_poll(struct sflash_mtd *sflash, unsigned int offset, int timeout)
57 int now = jiffies;
58 int ret = 0;
60 for (;;) {
61 if (!sflash_poll(sflash->sih, sflash->cc, offset)) {
62 ret = 0;
63 break;
65 if (time_after((unsigned long)jiffies, (unsigned long)now + timeout)) {
66 printk(KERN_ERR "sflash: timeout\n");
67 ret = -ETIMEDOUT;
68 break;
70 udelay(1);
73 return ret;
76 static int
77 sflash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
79 struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv;
80 int bytes, ret = 0;
82 /* Check address range */
83 if (!len)
84 return 0;
85 if ((from + len) > mtd->size)
86 return -EINVAL;
88 *retlen = 0;
89 while (len) {
90 if ((bytes = sflash_read(sflash->sih, sflash->cc, (uint) from, len, buf)) < 0) {
91 ret = bytes;
92 break;
94 from += (loff_t) bytes;
95 len -= bytes;
96 buf += bytes;
97 *retlen += bytes;
100 return ret;
103 static int
104 sflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
106 struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv;
107 int bytes, ret = 0;
109 /* Check address range */
110 if (!len)
111 return 0;
112 if ((to + len) > mtd->size)
113 return -EINVAL;
115 *retlen = 0;
116 while (len) {
117 if ((bytes = sflash_write(sflash->sih, sflash->cc, (uint) to, len, buf)) < 0) {
118 ret = bytes;
119 break;
121 if ((ret = sflash_mtd_poll(sflash, (unsigned int) to, HZ / 10)))
122 break;
123 to += (loff_t) bytes;
124 len -= bytes;
125 buf += bytes;
126 *retlen += bytes;
129 return ret;
132 static int
133 sflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
135 struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv;
136 int i, j, ret = 0;
137 unsigned int addr, len;
139 /* Check address range */
140 if (!erase->len)
141 return 0;
142 if ((erase->addr + erase->len) > mtd->size)
143 return -EINVAL;
145 addr = erase->addr;
146 len = erase->len;
148 /* Ensure that requested region is aligned */
149 for (i = 0; i < mtd->numeraseregions; i++) {
150 for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
151 if (addr == mtd->eraseregions[i].offset +
152 mtd->eraseregions[i].erasesize * j &&
153 len >= mtd->eraseregions[i].erasesize) {
154 if ((ret = sflash_erase(sflash->sih, sflash->cc, addr)) < 0)
155 break;
156 if ((ret = sflash_mtd_poll(sflash, addr, 10 * HZ)))
157 break;
158 addr += mtd->eraseregions[i].erasesize;
159 len -= mtd->eraseregions[i].erasesize;
162 if (ret)
163 break;
166 /* Set erase status */
167 if (ret)
168 erase->state = MTD_ERASE_FAILED;
169 else
170 erase->state = MTD_ERASE_DONE;
172 /* Call erase callback */
173 if (erase->callback)
174 erase->callback(erase);
176 return ret;
179 #if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
180 #define sflash_mtd_init init_module
181 #define sflash_mtd_exit cleanup_module
182 #endif
184 static int __init
185 sflash_mtd_init(void)
187 int ret = 0;
188 struct sflash *info;
189 struct pci_dev *dev = NULL;
190 #ifdef CONFIG_MTD_PARTITIONS
191 struct mtd_partition *parts;
192 int i;
193 #endif
195 list_for_each_entry(dev, &((pci_find_bus(0, 0))->devices), bus_list) {
196 if ((dev != NULL) && (dev->device == CC_CORE_ID))
197 break;
200 if (dev == NULL) {
201 printk(KERN_ERR "sflash: chipcommon not found\n");
202 return -ENODEV;
205 memset(&sflash, 0, sizeof(struct sflash_mtd));
207 /* attach to the backplane */
208 if (!(sflash.sih = si_kattach(SI_OSH))) {
209 printk(KERN_ERR "sflash: error attaching to backplane\n");
210 ret = -EIO;
211 goto fail;
214 /* Map registers and flash base */
215 if (!(sflash.cc = ioremap_nocache(
216 pci_resource_start(dev, 0),
217 pci_resource_len(dev, 0)))) {
218 printk(KERN_ERR "sflash: error mapping registers\n");
219 ret = -EIO;
220 goto fail;
223 /* Initialize serial flash access */
224 if (!(info = sflash_init(sflash.sih, sflash.cc))) {
225 printk(KERN_ERR "sflash: found no supported devices\n");
226 ret = -ENODEV;
227 goto fail;
230 /* Setup region info */
231 sflash.region.offset = 0;
232 sflash.region.erasesize = info->blocksize;
233 sflash.region.numblocks = info->numblocks;
234 if (sflash.region.erasesize > sflash.mtd.erasesize)
235 sflash.mtd.erasesize = sflash.region.erasesize;
236 sflash.mtd.size = info->size;
237 sflash.mtd.numeraseregions = 1;
239 /* Register with MTD */
240 sflash.mtd.name = "sflash";
241 sflash.mtd.type = MTD_NORFLASH;
242 sflash.mtd.flags = MTD_CAP_NORFLASH;
243 sflash.mtd.eraseregions = &sflash.region;
244 sflash.mtd.erase = sflash_mtd_erase;
245 sflash.mtd.read = sflash_mtd_read;
246 sflash.mtd.write = sflash_mtd_write;
247 sflash.mtd.writesize = 1;
248 sflash.mtd.priv = &sflash;
249 sflash.mtd.owner = THIS_MODULE;
252 #ifdef CONFIG_MTD_PARTITIONS
253 parts = init_mtd_partitions(&sflash.mtd, sflash.mtd.size);
254 for (i = 0; parts[i].name; i++);
255 ret = add_mtd_partitions(&sflash.mtd, parts, i);
256 if (ret) {
257 printk(KERN_ERR "sflash: add_mtd failed\n");
258 goto fail;
260 #endif
261 return 0;
263 fail:
264 if (sflash.cc)
265 iounmap((void *) sflash.cc);
266 if (sflash.sih)
267 si_detach(sflash.sih);
268 return ret;
271 static void __exit
272 sflash_mtd_exit(void)
274 #ifdef CONFIG_MTD_PARTITIONS
275 del_mtd_partitions(&sflash.mtd);
276 #else
277 del_mtd_device(&sflash.mtd);
278 #endif
279 iounmap((void *) sflash.cc);
280 si_detach(sflash.sih);
283 module_init(sflash_mtd_init);
284 module_exit(sflash_mtd_exit);