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