OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [mtd/] [devices/] [doc1000.c] - Blame information for rev 1774

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*======================================================================
2
 
3
  $Id: doc1000.c,v 1.1.1.1 2004-04-15 01:52:01 phoenix Exp $
4
 
5
======================================================================*/
6
 
7
 
8
#include <linux/config.h>
9
#include <linux/module.h>
10
#include <asm/uaccess.h>
11
#include <linux/types.h>
12
#include <linux/kernel.h>
13
#include <linux/sched.h>
14
#include <linux/ptrace.h>
15
#include <linux/slab.h>
16
#include <linux/string.h>
17
#include <linux/timer.h>
18
#include <linux/major.h>
19
#include <linux/fs.h>
20
#include <linux/ioctl.h>
21
#include <asm/io.h>
22
#include <asm/system.h>
23
#include <linux/delay.h>
24
#include <linux/init.h>
25
 
26
#include <linux/mtd/mtd.h>
27
#include <linux/mtd/iflash.h>
28
 
29
/* Parameters that can be set with 'insmod' */
30
 
31
static u_long base              = 0xe0000;
32
static int erase_timeout        = 10*HZ;        /* in ticks */
33
static int retry_limit          = 4;            /* write retries */
34
static u_long max_tries         = 4096;         /* status polling */
35
 
36
MODULE_PARM(base,"l");
37
MODULE_PARM(erase_timeout, "i");
38
MODULE_PARM(retry_limit, "i");
39
MODULE_PARM(max_tries, "i");
40
 
41
#define WINDOW_SIZE 0x2000
42
#define WINDOW_MASK (WINDOW_SIZE - 1)
43
#define PAGEREG_LO (WINDOW_SIZE)
44
#define PAGEREG_HI (WINDOW_SIZE + 2)
45
 
46
static struct mtd_info *mymtd;
47
static struct timer_list flashcard_timer;
48
 
49
#define MAX_CELLS               32
50
#define MAX_FLASH_DEVICES       8
51
 
52
/* A flash region is composed of one or more "cells", where we allow
53
   simultaneous erases if they are in different cells */
54
 
55
 
56
 
57
struct mypriv {
58
        u_char *baseaddr;
59
        u_short curpage;
60
        u_char locked;
61
        u_short numdevices;
62
        u_char interleave;
63
        struct erase_info *cur_erases;
64
        wait_queue_head_t wq;
65
        u_char devstat[MAX_FLASH_DEVICES];
66
        u_long devshift;
67
};
68
 
69
 
70
static void flashcard_periodic(u_long data);
71
static int flashcard_erase (struct mtd_info *mtd, struct erase_info *instr);
72
static int flashcard_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
73
static int flashcard_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
74
static void flashcard_sync (struct mtd_info *mtd);
75
 
76
static inline void resume_erase(volatile u_char *addr);
77
static inline int suspend_erase(volatile u_char *addr);
78
static inline int byte_write (volatile u_char *addr, u_char byte);
79
static inline int word_write (volatile u_char *addr, __u16 word);
80
static inline int check_write(volatile u_char *addr);
81
static inline void block_erase (volatile u_char *addr);
82
static inline int check_erase(volatile u_char *addr);
83
 
84
#ifdef CONFIG_SMP
85
#warning This is definitely not SMP safe. Lock the paging mechanism.
86
#endif
87
 
88
static u_char *pagein(struct mtd_info *mtd, u_long addr)
89
{
90
  struct mypriv *priv=mtd->priv;
91
  u_short page = addr >> 13;
92
 
93
  priv->baseaddr[PAGEREG_LO] = page & 0xff;
94
  priv->baseaddr[PAGEREG_HI] = page >> 8;
95
  priv->curpage = page;
96
 
97
  return &priv->baseaddr[addr & WINDOW_MASK];
98
}
99
 
100
 
101
void flashcard_sync (struct mtd_info *mtd)
102
{
103
        struct mypriv *priv=mtd->priv;
104
 
105
        flashcard_periodic((u_long) mtd);
106
        printk("sync...");
107
        if (priv->cur_erases)
108
                interruptible_sleep_on(&priv->wq);
109
        printk("Done.\n");
110
}
111
 
112
int flashcard_erase (struct mtd_info *mtd, struct erase_info *instr)
113
{
114
        u_char *pageaddr;
115
        struct mypriv *priv=mtd->priv;
116
        struct erase_info **tmp=&priv->cur_erases;
117
 
118
        if (instr->len != mtd->erasesize)
119
                return -EINVAL;
120
        if (instr->addr + instr->len > mtd->size)
121
                return -EINVAL;
122
 
123
        pageaddr=pagein(mtd,instr->addr);
124
        instr->mtd = mtd;
125
        instr->dev = instr->addr >> priv->devshift;
126
        instr->cell = (instr->addr - (instr->dev << priv->devshift)) / mtd->erasesize;
127
        instr->next = NULL;
128
        instr->state = MTD_ERASE_PENDING;
129
 
130
        while (*tmp)
131
        {
132
                tmp = &((*tmp) -> next);
133
        }
134
 
135
        *tmp = instr;
136
        flashcard_periodic((u_long)mtd);
137
        return 0;
138
}
139
 
140
 
141
int flashcard_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
142
{
143
        u_char *pageaddr=pagein(mtd,from);
144
        struct mypriv *priv=mtd->priv;
145
        u_char device = from >> priv->devshift;
146
        u_char cell = (int) (from - (device << priv->devshift)) / mtd->erasesize;
147
        int ret = 0, timeron = 0;
148
 
149
        if ((from & WINDOW_MASK) + len <= WINDOW_SIZE)
150
                *retlen = len;
151
        else
152
                *retlen = WINDOW_SIZE - (from & WINDOW_MASK);
153
 
154
        if (priv->devstat[device])
155
        {
156
 
157
                /* There is an erase in progress or pending for this device. Stop it */
158
                timeron = del_timer(&flashcard_timer);
159
 
160
                if (priv->cur_erases && priv->cur_erases->cell == cell)
161
 
162
                {
163
                        /* The erase is on the current cell. Just return all 0xff */
164
                        add_timer(&flashcard_timer);
165
 
166
 
167
                        printk("Cell %d currently erasing. Setting to all 0xff\n",cell);
168
                        memset(buf, 0xff, *retlen);
169
                        return 0;
170
                }
171
                if (priv->devstat[device] == MTD_ERASING)
172
                {
173
                        ret = suspend_erase(pageaddr);
174
                        priv->devstat[device] = MTD_ERASE_SUSPEND;
175
 
176
                        if (ret)
177
                        {
178
                                printk("flashcard: failed to suspend erase\n");
179
                                add_timer (&flashcard_timer);
180
                                return ret;
181
                        }
182
                }
183
 
184
        }
185
 
186
        writew(IF_READ_ARRAY, (u_long)pageaddr & ~1);
187
 
188
        ret = 0;
189
        memcpy (buf, pageaddr, *retlen);
190
 
191
        writew(IF_READ_CSR, (u_long)pageaddr & ~1);
192
 
193
 
194
        if (priv->devstat[device] & MTD_ERASE_SUSPEND)
195
        {
196
                resume_erase(pageaddr);
197
                priv->devstat[device]=MTD_ERASING;
198
        }
199
 
200
 
201
        if (timeron) add_timer (&flashcard_timer);
202
 
203
        return ret;
204
}
205
 
206
 
207
int flashcard_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
208
{
209
        struct mypriv *priv = (struct mypriv *)mtd->priv;
210
        u_char *endaddr, *startaddr;
211
        register u_char *pageaddr;
212
        u_char device = to >> priv->devshift;
213
/*      jiffies_t oldj=jiffies;*/
214
        int ret;
215
 
216
        while (priv->devstat[device])
217
        {
218
                flashcard_sync(mtd);
219
        }
220
 
221
        if ((to & WINDOW_MASK) + len <= WINDOW_SIZE)
222
                *retlen = len;
223
        else
224
                *retlen = WINDOW_SIZE - (to & WINDOW_MASK);
225
 
226
        pageaddr = pagein(mtd, to);
227
        startaddr = (u_char *)((u_long) pageaddr & ~1);
228
        endaddr = pageaddr+(*retlen);
229
 
230
 
231
 
232
        /* Set up to read */
233
        writew(IF_READ_CSR, startaddr);
234
 
235
        /* Make sure it's aligned by reading the first byte if necessary */
236
        if (to & 1)
237
        {
238
                /* Unaligned access */
239
 
240
                u_char cbuf;
241
 
242
                cbuf = *buf;
243
 
244
                if (!((u_long)pageaddr & 0xf))
245
                        schedule();
246
 
247
                ret = byte_write(pageaddr, cbuf);
248
                if (ret) return ret;
249
 
250
                pageaddr++; buf++;
251
        }
252
 
253
 
254
        for ( ; pageaddr + 1 < endaddr; buf += 2, pageaddr += 2)
255
                {
256
                        /* if ((u_long)pageaddr & 0xf) schedule();*/
257
 
258
                        ret = word_write(pageaddr, *(__u16 *)buf);
259
                        if (ret)
260
                                return ret;
261
                }
262
 
263
        if (pageaddr != endaddr)
264
        {
265
                /* One more byte to write at the end. */
266
                u_char cbuf;
267
 
268
                cbuf = *buf;
269
 
270
                ret = byte_write(pageaddr, cbuf);
271
 
272
                if (ret) return ret;
273
        }
274
 
275
        return check_write(startaddr);
276
/*      printk("Time taken in flashcard_write: %lx jiffies\n",jiffies - oldj);*/
277
}
278
 
279
 
280
 
281
 
282
/*====================================================================*/
283
 
284
static inline int byte_write (volatile u_char *addr, u_char byte)
285
{
286
        register u_char status;
287
        register u_short i = 0;
288
 
289
        do {
290
                status = readb(addr);
291
                if (status & CSR_WR_READY)
292
                {
293
                        writeb(IF_WRITE & 0xff, addr);
294
                        writeb(byte, addr);
295
                        return 0;
296
                }
297
                i++;
298
        } while(i < max_tries);
299
 
300
 
301
        printk(KERN_NOTICE "flashcard: byte_write timed out, status 0x%x\n",status);
302
        return -EIO;
303
}
304
 
305
static inline int word_write (volatile u_char *addr, __u16 word)
306
{
307
        register u_short status;
308
        register u_short i = 0;
309
 
310
        do {
311
                status = readw(addr);
312
                if ((status & CSR_WR_READY) == CSR_WR_READY)
313
                {
314
                        writew(IF_WRITE, addr);
315
                        writew(word, addr);
316
                        return 0;
317
                }
318
                i++;
319
        } while(i < max_tries);
320
 
321
        printk(KERN_NOTICE "flashcard: word_write timed out at %p, status 0x%x\n", addr, status);
322
        return -EIO;
323
}
324
 
325
static inline void block_erase (volatile u_char *addr)
326
{
327
        writew(IF_BLOCK_ERASE, addr);
328
        writew(IF_CONFIRM, addr);
329
}
330
 
331
 
332
static inline int check_erase(volatile u_char *addr)
333
{
334
        __u16 status;
335
 
336
/*      writew(IF_READ_CSR, addr);*/
337
        status = readw(addr);
338
 
339
 
340
        if ((status & CSR_WR_READY) != CSR_WR_READY)
341
                return -EBUSY;
342
 
343
        if (status & (CSR_ERA_ERR | CSR_VPP_LOW | CSR_WR_ERR))
344
        {
345
                printk(KERN_NOTICE "flashcard: erase failed, status 0x%x\n",
346
                       status);
347
                return -EIO;
348
        }
349
 
350
        return 0;
351
}
352
 
353
static inline int suspend_erase(volatile u_char *addr)
354
{
355
        __u16 status;
356
        u_long i = 0;
357
 
358
        writew(IF_ERASE_SUSPEND, addr);
359
        writew(IF_READ_CSR, addr);
360
 
361
        do {
362
                status = readw(addr);
363
                if ((status & CSR_WR_READY) == CSR_WR_READY)
364
                        return 0;
365
                i++;
366
        } while(i < max_tries);
367
 
368
        printk(KERN_NOTICE "flashcard: suspend_erase timed out, status 0x%x\n", status);
369
        return -EIO;
370
 
371
}
372
 
373
static inline void resume_erase(volatile u_char *addr)
374
{
375
        __u16 status;
376
 
377
        writew(IF_READ_CSR, addr);
378
        status = readw(addr);
379
 
380
        /* Only give resume signal if the erase is really suspended */
381
        if (status & CSR_ERA_SUSPEND)
382
                writew(IF_CONFIRM, addr);
383
}
384
 
385
static inline void reset_block(volatile u_char *addr)
386
{
387
        u_short i;
388
        __u16 status;
389
 
390
        writew(IF_CLEAR_CSR, addr);
391
 
392
        for (i = 0; i < 100; i++) {
393
                writew(IF_READ_CSR, addr);
394
                status = readw(addr);
395
                if (status != 0xffff) break;
396
                udelay(1000);
397
        }
398
 
399
        writew(IF_READ_CSR, addr);
400
}
401
 
402
static inline int check_write(volatile u_char *addr)
403
{
404
        u_short status, i = 0;
405
 
406
        writew(IF_READ_CSR, addr);
407
 
408
        do {
409
                status = readw(addr);
410
                if (status & (CSR_WR_ERR | CSR_VPP_LOW))
411
                {
412
                        printk(KERN_NOTICE "flashcard: write failure at %p, status 0x%x\n", addr, status);
413
                        reset_block(addr);
414
                        return -EIO;
415
                }
416
                if ((status & CSR_WR_READY) == CSR_WR_READY)
417
                        return 0;
418
                i++;
419
        } while (i < max_tries);
420
 
421
        printk(KERN_NOTICE "flashcard: write timed out at %p, status 0x%x\n", addr, status);
422
        return -EIO;
423
}
424
 
425
 
426
/*====================================================================*/
427
 
428
 
429
 
430
static void flashcard_periodic(unsigned long data)
431
{
432
        register struct mtd_info *mtd = (struct mtd_info *)data;
433
        register struct mypriv *priv = mtd->priv;
434
        struct erase_info *erase = priv->cur_erases;
435
        u_char *pageaddr;
436
 
437
        del_timer (&flashcard_timer);
438
 
439
        if (!erase)
440
                return;
441
 
442
        pageaddr = pagein(mtd, erase->addr);
443
 
444
        if (erase->state == MTD_ERASE_PENDING)
445
        {
446
                block_erase(pageaddr);
447
                priv->devstat[erase->dev] = erase->state = MTD_ERASING;
448
                erase->time = jiffies;
449
                erase->retries = 0;
450
        }
451
        else if (erase->state == MTD_ERASING)
452
        {
453
                /* It's trying to erase. Check whether it's finished */
454
 
455
                int ret = check_erase(pageaddr);
456
 
457
                if (!ret)
458
                {
459
                        /* It's finished OK */
460
                        priv->devstat[erase->dev] = 0;
461
                        priv->cur_erases = erase->next;
462
                        erase->state = MTD_ERASE_DONE;
463
                        if (erase->callback)
464
                                (*(erase->callback))(erase);
465
                        else
466
                                kfree(erase);
467
                }
468
                else if (ret == -EIO)
469
                {
470
                        if (++erase->retries > retry_limit)
471
                        {
472
                                printk("Failed too many times. Giving up\n");
473
                                priv->cur_erases = erase->next;
474
                                priv->devstat[erase->dev] = 0;
475
                                erase->state = MTD_ERASE_FAILED;
476
                                if (erase->callback)
477
                                        (*(erase->callback))(erase);
478
                                else
479
                                        kfree(erase);
480
                        }
481
                        else
482
                                priv->devstat[erase->dev] = erase->state = MTD_ERASE_PENDING;
483
                }
484
                else if (time_after(jiffies, erase->time + erase_timeout))
485
                {
486
                        printk("Flash erase timed out. The world is broken.\n");
487
 
488
                        /* Just ignore and hope it goes away. For a while, read ops will give the CSR
489
                           and writes won't work. */
490
 
491
                        priv->cur_erases = erase->next;
492
                        priv->devstat[erase->dev] = 0;
493
                        erase->state = MTD_ERASE_FAILED;
494
                        if (erase->callback)
495
                                        (*(erase->callback))(erase);
496
                                else
497
                                        kfree(erase);
498
                }
499
        }
500
 
501
        if (priv->cur_erases)
502
        {
503
                flashcard_timer.expires = jiffies + HZ;
504
                add_timer (&flashcard_timer);
505
        }
506
        else
507
                wake_up_interruptible(&priv->wq);
508
 
509
}
510
 
511
int __init init_doc1000(void)
512
{
513
        struct mypriv *priv;
514
 
515
        if (!base)
516
        {
517
                printk(KERN_NOTICE "flashcard: No start address for memory device.\n");
518
                return -EINVAL;
519
        }
520
 
521
        mymtd  = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
522
 
523
        if (!mymtd)
524
        {
525
                printk(KERN_NOTICE "physmem: Cannot allocate memory for new MTD device.\n");
526
                return -ENOMEM;
527
        }
528
 
529
        memset(mymtd,0,sizeof(struct mtd_info));
530
 
531
        mymtd->priv = (void *) kmalloc (sizeof(struct mypriv), GFP_KERNEL);
532
        if (!mymtd->priv)
533
          {
534
            kfree(mymtd);
535
            printk(KERN_NOTICE "physmem: Cannot allocate memory for new MTD device's private data.\n");
536
            return -ENOMEM;
537
          }
538
 
539
 
540
 
541
 
542
        priv=mymtd->priv;
543
        init_waitqueue_head(&priv->wq);
544
 
545
        memset (priv,0,sizeof(struct mypriv));
546
 
547
        priv->baseaddr = phys_to_virt(base);
548
        priv->numdevices = 4;
549
 
550
        mymtd->name = "M-Systems DiskOnChip 1000";
551
 
552
        mymtd->size = 0x100000;
553
        mymtd->flags = MTD_CLEAR_BITS | MTD_ERASEABLE;
554
        mymtd->erase = flashcard_erase;
555
        mymtd->point = NULL;
556
        mymtd->unpoint = NULL;
557
        mymtd->read = flashcard_read;
558
        mymtd->write = flashcard_write;
559
 
560
        mymtd->sync = flashcard_sync;
561
        mymtd->erasesize = 0x10000;
562
        //      mymtd->interleave = 2;
563
        priv->devshift =  24;
564
        mymtd->type = MTD_NORFLASH;
565
 
566
        if (add_mtd_device(mymtd))
567
        {
568
                printk(KERN_NOTICE "MTD device registration failed!\n");
569
                kfree(mymtd->priv);
570
                kfree(mymtd);
571
                return -EAGAIN;
572
        }
573
 
574
        init_timer(&flashcard_timer);
575
        flashcard_timer.function = flashcard_periodic;
576
        flashcard_timer.data = (u_long)mymtd;
577
        return 0;
578
}
579
 
580
static void __init cleanup_doc1000(void)
581
{
582
        kfree (mymtd->priv);
583
        del_mtd_device(mymtd);
584
        kfree(mymtd);
585
}
586
 
587
module_init(init_doc1000);
588
module_exit(cleanup_doc1000);
589
 
590
MODULE_LICENSE("GPL");
591
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
592
MODULE_DESCRIPTION("MTD driver for DiskOnChip 1000");
593
 

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.