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