add mirror
[openwrt.git] / openwrt / package / linux / kernel-source / drivers / mtd / devices / sflash.c
1 /*
2 * Broadcom SiliconBackplane chipcommon serial flash interface
3 *
4 * Copyright 2004, 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.
11 *
12 * $Id$
13 */
14
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 <asm/io.h>
25
26 #include <typedefs.h>
27 #include <bcmdevs.h>
28 #include <bcmutils.h>
29 #include <osl.h>
30 #include <bcmutils.h>
31 #include <bcmnvram.h>
32 #include <sbconfig.h>
33 #include <sbchipc.h>
34 #include <sflash.h>
35
36 #ifdef CONFIG_MTD_PARTITIONS
37 extern struct mtd_partition * init_mtd_partitions(struct mtd_info *mtd, size_t size);
38 #endif
39
40 struct sflash_mtd {
41 chipcregs_t *cc;
42 struct semaphore lock;
43 struct mtd_info mtd;
44 struct mtd_erase_region_info region;
45 };
46
47 /* Private global state */
48 static struct sflash_mtd sflash;
49
50 static int
51 sflash_mtd_poll(struct sflash_mtd *sflash, unsigned int offset, int timeout)
52 {
53 int now = jiffies;
54 int ret = 0;
55
56 for (;;) {
57 if (!sflash_poll(sflash->cc, offset)) {
58 ret = 0;
59 break;
60 }
61 if (time_after(jiffies, now + timeout)) {
62 printk(KERN_ERR "sflash: timeout\n");
63 ret = -ETIMEDOUT;
64 break;
65 }
66 if (current->need_resched) {
67 set_current_state(TASK_UNINTERRUPTIBLE);
68 schedule_timeout(timeout / 10);
69 } else
70 udelay(1);
71 }
72
73 return ret;
74 }
75
76 static int
77 sflash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
78 {
79 struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv;
80 int bytes, ret = 0;
81
82 /* Check address range */
83 if (!len)
84 return 0;
85 if ((from + len) > mtd->size)
86 return -EINVAL;
87
88 down(&sflash->lock);
89
90 *retlen = 0;
91 while (len) {
92 if ((bytes = sflash_read(sflash->cc, (uint) from, len, buf)) < 0) {
93 ret = bytes;
94 break;
95 }
96 from += (loff_t) bytes;
97 len -= bytes;
98 buf += bytes;
99 *retlen += bytes;
100 }
101
102 up(&sflash->lock);
103
104 return ret;
105 }
106
107 static int
108 sflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
109 {
110 struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv;
111 int bytes, ret = 0;
112
113 /* Check address range */
114 if (!len)
115 return 0;
116 if ((to + len) > mtd->size)
117 return -EINVAL;
118
119 down(&sflash->lock);
120
121 *retlen = 0;
122 while (len) {
123 if ((bytes = sflash_write(sflash->cc, (uint) to, len, buf)) < 0) {
124 ret = bytes;
125 break;
126 }
127 if ((ret = sflash_mtd_poll(sflash, (unsigned int) to, HZ / 10)))
128 break;
129 to += (loff_t) bytes;
130 len -= bytes;
131 buf += bytes;
132 *retlen += bytes;
133 }
134
135 up(&sflash->lock);
136
137 return ret;
138 }
139
140 static int
141 sflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
142 {
143 struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv;
144 int i, j, ret = 0;
145 unsigned int addr, len;
146
147 /* Check address range */
148 if (!erase->len)
149 return 0;
150 if ((erase->addr + erase->len) > mtd->size)
151 return -EINVAL;
152
153 addr = erase->addr;
154 len = erase->len;
155
156 down(&sflash->lock);
157
158 /* Ensure that requested region is aligned */
159 for (i = 0; i < mtd->numeraseregions; i++) {
160 for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
161 if (addr == mtd->eraseregions[i].offset + mtd->eraseregions[i].erasesize * j &&
162 len >= mtd->eraseregions[i].erasesize) {
163 if ((ret = sflash_erase(sflash->cc, addr)) < 0)
164 break;
165 if ((ret = sflash_mtd_poll(sflash, addr, 10 * HZ)))
166 break;
167 addr += mtd->eraseregions[i].erasesize;
168 len -= mtd->eraseregions[i].erasesize;
169 }
170 }
171 if (ret)
172 break;
173 }
174
175 up(&sflash->lock);
176
177 /* Set erase status */
178 if (ret)
179 erase->state = MTD_ERASE_FAILED;
180 else
181 erase->state = MTD_ERASE_DONE;
182
183 /* Call erase callback */
184 if (erase->callback)
185 erase->callback(erase);
186
187 return ret;
188 }
189
190 #if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
191 #define sflash_mtd_init init_module
192 #define sflash_mtd_exit cleanup_module
193 #endif
194
195 mod_init_t
196 sflash_mtd_init(void)
197 {
198 struct pci_dev *pdev;
199 int ret = 0;
200 struct sflash *info;
201 uint i;
202 #ifdef CONFIG_MTD_PARTITIONS
203 struct mtd_partition *parts;
204 #endif
205
206 if (!(pdev = pci_find_device(VENDOR_BROADCOM, SB_CC, NULL))) {
207 printk(KERN_ERR "sflash: chipcommon not found\n");
208 return -ENODEV;
209 }
210
211 memset(&sflash, 0, sizeof(struct sflash_mtd));
212 init_MUTEX(&sflash.lock);
213
214 /* Map registers and flash base */
215 if (!(sflash.cc = ioremap_nocache(pci_resource_start(pdev, 0),
216 pci_resource_len(pdev, 0)))) {
217 printk(KERN_ERR "sflash: error mapping registers\n");
218 ret = -EIO;
219 goto fail;
220 }
221
222 /* Initialize serial flash access */
223 info = sflash_init(sflash.cc);
224
225 if (!info) {
226 printk(KERN_ERR "sflash: found no supported devices\n");
227 ret = -ENODEV;
228 goto fail;
229 }
230
231 /* Setup region info */
232 sflash.region.offset = 0;
233 sflash.region.erasesize = info->blocksize;
234 sflash.region.numblocks = info->numblocks;
235 if (sflash.region.erasesize > sflash.mtd.erasesize)
236 sflash.mtd.erasesize = sflash.region.erasesize;
237 sflash.mtd.size = info->size;
238 sflash.mtd.numeraseregions = 1;
239
240 /* Register with MTD */
241 sflash.mtd.name = "sflash";
242 sflash.mtd.type = MTD_NORFLASH;
243 sflash.mtd.flags = MTD_CAP_NORFLASH;
244 sflash.mtd.eraseregions = &sflash.region;
245 sflash.mtd.module = THIS_MODULE;
246 sflash.mtd.erase = sflash_mtd_erase;
247 sflash.mtd.read = sflash_mtd_read;
248 sflash.mtd.write = sflash_mtd_write;
249 sflash.mtd.priv = &sflash;
250
251 #ifdef CONFIG_MTD_PARTITIONS
252 parts = init_mtd_partitions(&sflash.mtd, sflash.mtd.size);
253 for (i = 0; parts[i].name; i++);
254 ret = add_mtd_partitions(&sflash.mtd, parts, i);
255 #else
256 ret = add_mtd_device(&sflash.mtd);
257 #endif
258 if (ret) {
259 printk(KERN_ERR "sflash: add_mtd failed\n");
260 goto fail;
261 }
262
263 return 0;
264
265 fail:
266 if (sflash.cc)
267 iounmap((void *) sflash.cc);
268 return ret;
269 }
270
271 mod_exit_t
272 sflash_mtd_exit(void)
273 {
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 }
281
282 module_init(sflash_mtd_init);
283 module_exit(sflash_mtd_exit);
This page took 0.061422 seconds and 5 git commands to generate.