Root/target/linux/brcm-2.4/files/drivers/mtd/devices/sflash.c

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

Archive Download this file



interactive