Add preliminary RouterBoard RB1xx support
[openwrt.git] / target / linux / rb1xx-2.6 / patches / 120-cf.patch
diff --git a/target/linux/rb1xx-2.6/patches/120-cf.patch b/target/linux/rb1xx-2.6/patches/120-cf.patch
new file mode 100644 (file)
index 0000000..a22c526
--- /dev/null
@@ -0,0 +1,1011 @@
+diff -urN linux.old/drivers/block/Kconfig linux.dev/drivers/block/Kconfig
+--- linux.old/drivers/block/Kconfig    2006-10-26 02:43:39.000000000 +0200
++++ linux.dev/drivers/block/Kconfig    2006-10-26 00:11:14.000000000 +0200
+@@ -456,4 +456,12 @@
+       This driver provides Support for ATA over Ethernet block
+       devices like the Coraid EtherDrive (R) Storage Blade.
++config BLK_DEV_CF_MIPS
++      bool "CF slot of RB532 board"
++      depends on MIKROTIK_RB500 || MIPS_ADM5120
++      default y
++      help
++        The Routerboard 532 has a CF slot on it. Enable the special block
++        device driver for it.
++
+ endmenu
+diff -urN linux.old/drivers/block/Makefile linux.dev/drivers/block/Makefile
+--- linux.old/drivers/block/Makefile   2006-06-18 03:49:35.000000000 +0200
++++ linux.dev/drivers/block/Makefile   2006-10-26 02:44:10.000000000 +0200
+@@ -29,4 +29,5 @@
+ obj-$(CONFIG_VIODASD)         += viodasd.o
+ obj-$(CONFIG_BLK_DEV_SX8)     += sx8.o
+ obj-$(CONFIG_BLK_DEV_UB)      += ub.o
++obj-$(CONFIG_BLK_DEV_CF_MIPS) += rb500/
+diff -urN linux.old/drivers/block/rb500/ata.c linux.dev/drivers/block/rb500/ata.c
+--- linux.old/drivers/block/rb500/ata.c        1970-01-01 01:00:00.000000000 +0100
++++ linux.dev/drivers/block/rb500/ata.c        2006-10-26 00:11:14.000000000 +0200
+@@ -0,0 +1,485 @@
++/* CF-mips driver
++   This is a block driver for the direct (mmaped) interface to the CF-slot,
++   found in Routerboard.com's RB532 board
++   See SDK provided from routerboard.com.
++   
++   Module adapted By P.Christeas <p_christeas@yahoo.com>, 2005-6.
++   Cleaned up and adapted to platform_device by Felix Fietkau <nbd@openwrt.org>
++
++   This work is redistributed under the terms of the GNU General Public License.
++*/
++
++#include <linux/kernel.h>     /* printk() */
++#include <linux/module.h>     /* module to be loadable */
++#include <linux/delay.h>
++#include <linux/sched.h>
++#include <linux/pci.h>
++#include <linux/ioport.h>     /* request_mem_region() */
++#include <asm/unaligned.h>            /* ioremap() */
++#include <asm/io.h>           /* ioremap() */
++#include <asm/rc32434/rb.h>
++
++#include "ata.h"
++
++#define REQUEST_MEM_REGION 0
++#define DEBUG 1
++
++#if DEBUG
++#define DEBUGP printk
++#else
++#define DEBUGP(format, args...)
++#endif
++
++#define SECS  1000000         /* unit for wait_not_busy() is 1us */
++
++unsigned cf_head = 0;
++unsigned cf_cyl = 0;
++unsigned cf_spt = 0;
++unsigned cf_sectors = 0;
++static unsigned cf_block_size = 1;
++static void *baddr = 0;
++
++#define DBUF32 ((volatile u32 *)((unsigned long)dev->baddr | ATA_DBUF_OFFSET))
++
++
++static void cf_do_tasklet(unsigned long dev_l);
++
++
++static inline void wareg(u8 val, unsigned reg, struct cf_mips_dev* dev)
++{
++      writeb(val, dev->baddr + ATA_REG_OFFSET + reg);
++}
++
++static inline u8 rareg(unsigned reg, struct cf_mips_dev* dev)
++{
++      return readb(dev->baddr + ATA_REG_OFFSET + reg);
++}
++
++static inline int get_gpio_bit(gpio_func ofs, struct cf_mips_dev *dev)
++{
++      return (gpio_get(ofs) >> dev->pin) & 1;
++}
++
++static inline void set_gpio_bit(int bit, gpio_func ofs, struct cf_mips_dev *dev)
++{
++      gpio_set(ofs, (1 << dev->pin), ((bit & 1) << dev->pin));
++}
++
++static inline int cfrdy(struct cf_mips_dev *dev)
++{
++      return get_gpio_bit(DATA, dev);
++}
++
++static inline void prepare_cf_irq(struct cf_mips_dev *dev)
++{
++      set_gpio_bit(1, ILEVEL, dev);   /* interrupt on cf ready (not busy) */
++      set_gpio_bit(0, ISTAT, dev);    /* clear interrupt status */
++}
++
++static inline int cf_present(struct cf_mips_dev* dev)
++{
++      /* TODO: read and configure CIS into memory mapped mode
++       * TODO:   parse CISTPL_CONFIG on CF+ cards to get base address (0x200)
++       * TODO:   maybe adjust power saving setting for Hitachi Microdrive
++       */
++      int i;
++
++      /* setup CFRDY GPIO as input */
++      set_gpio_bit(0, FUNC, dev);
++      set_gpio_bit(0, CFG, dev);
++
++      for (i = 0; i < 0x10; ++i) {
++              if (rareg(i,dev) != 0xff)
++                      return 1;
++      }
++      return 0;
++}
++
++static inline int is_busy(struct cf_mips_dev *dev)
++{
++      return !cfrdy(dev);
++}
++
++static int wait_not_busy(int to_us, int wait_for_busy,struct cf_mips_dev *dev)
++{
++      int us_passed = 0;
++      if (wait_for_busy && !is_busy(dev)) {
++              /* busy must appear within 400ns,
++               * but it may dissapear before we see it
++               *  => must not wait for busy in a loop
++               */
++              ndelay(400);
++      }
++
++      do {
++              if (us_passed)
++                      udelay(1);      /* never reached in async mode */
++              if (!is_busy(dev)) {
++                      if (us_passed > 1 * SECS) {
++                              printk(KERN_WARNING "cf-mips:   not busy ok (after %dus)"
++                                     ", status 0x%02x\n", us_passed, (unsigned) rareg(ATA_REG_ST,dev));
++                      }
++                      return CF_TRANS_OK;
++              }
++              if (us_passed == 1 * SECS) {
++                      printk(KERN_WARNING "cf-mips: wait not busy %dus..\n", to_us);
++              }
++              if (dev->async_mode) {
++                      dev->to_timer.expires = jiffies + (to_us * HZ / SECS);
++                      dev->irq_enable_time = jiffies;
++                      prepare_cf_irq(dev);
++                      if (is_busy(dev)) {
++                              add_timer(&dev->to_timer);
++                              enable_irq(dev->irq);
++                              return CF_TRANS_IN_PROGRESS;
++                      }
++                      continue;
++              }
++              ++us_passed;
++      } while (us_passed < to_us);
++
++      printk(KERN_ERR "cf-mips:  wait not busy timeout (%dus)"
++             ", status 0x%02x, state %d\n",
++             to_us, (unsigned) rareg(ATA_REG_ST,dev), dev->tstate);
++      return CF_TRANS_FAILED;
++}
++
++static irqreturn_t cf_irq_handler(int irq, void *dev_id)
++{
++      /* While tasklet has not disabled irq, irq will be retried all the time
++       * because of ILEVEL matching GPIO pin status => deadlock.
++       * To avoid this, we change ILEVEL to 0.
++       */
++      struct cf_mips_dev *dev=dev_id;
++      
++      set_gpio_bit(0, ILEVEL, dev);
++      set_gpio_bit(0, ISTAT, dev);
++      
++      del_timer(&dev->to_timer);
++      tasklet_schedule(&dev->tasklet);
++      return IRQ_HANDLED;
++}
++
++static int do_reset(struct cf_mips_dev *dev)
++{
++      printk(KERN_INFO "cf-mips: resetting..\n");
++
++      wareg(ATA_REG_DC_SRST, ATA_REG_DC,dev);
++      udelay(1);              /* FIXME: how long should we wait here? */
++      wareg(0, ATA_REG_DC,dev);
++
++      return wait_not_busy(30 * SECS, 1,dev);
++}
++
++static int set_multiple(struct cf_mips_dev *dev)
++{
++      if (dev->block_size <= 1)
++              return CF_TRANS_OK;
++
++      wareg(dev->block_size, ATA_REG_SC,dev);
++      wareg(ATA_REG_DH_BASE | ATA_REG_DH_LBA, ATA_REG_DH,dev);
++      wareg(ATA_CMD_SET_MULTIPLE, ATA_REG_CMD,dev);
++
++      return wait_not_busy(10 * SECS, 1,dev);
++}
++
++static int set_cmd(struct cf_mips_dev *dev)
++{
++      //DEBUGP(KERN_INFO "cf-mips: ata cmd 0x%02x\n", dev->tcmd);
++      // sector_count should be <=24 bits..
++      BUG_ON(dev->tsect_start>=0x10000000);
++      // This way, it addresses 2^24 * 512 = 128G
++
++      if (dev->tsector_count) {
++              wareg(dev->tsector_count & 0xff, ATA_REG_SC,dev);
++              wareg(dev->tsect_start & 0xff, ATA_REG_SN,dev);
++              wareg((dev->tsect_start >> 8) & 0xff, ATA_REG_CL,dev);
++              wareg((dev->tsect_start >> 16) & 0xff, ATA_REG_CH,dev);
++      }
++      wareg(((dev->tsect_start >> 24) & 0x0f) | ATA_REG_DH_BASE | ATA_REG_DH_LBA,
++            ATA_REG_DH,dev);  /* select drive on all commands */
++      wareg(dev->tcmd, ATA_REG_CMD,dev);
++      return wait_not_busy(10 * SECS, 1,dev);
++}
++
++static int do_trans(struct cf_mips_dev *dev)
++{
++      int res;
++      unsigned st;
++      int transfered;
++      
++      //printk("do_trans: %d sectors left\n",dev->tsectors_left);
++      while (dev->tsectors_left) {
++              transfered = 0;
++
++              st = rareg(ATA_REG_ST,dev);
++              if (!(st & ATA_REG_ST_DRQ)) {
++                      printk(KERN_ERR "cf-mips: do_trans without DRQ (status 0x%x)!\n", st);
++                      if (st & ATA_REG_ST_ERR) {
++                              int errId = rareg(ATA_REG_ERR,dev);
++                              printk(KERN_ERR "cf-mips: %s error, status 0x%x, errid 0x%x\n",
++                                     (dev->tread ? "read" : "write"), st, errId);
++                      }
++                      return CF_TRANS_FAILED;
++              }
++              do { /* Fill/read the buffer one block */
++                      u32 *qbuf, *qend;
++                      qbuf = (u32 *)dev->tbuf;
++                      qend = qbuf + CF_SECT_SIZE / sizeof(u32);
++                      if (dev->tread) {
++                          while (qbuf!=qend)
++                              put_unaligned(*DBUF32,qbuf++);
++                              //*(qbuf++) = *DBUF32;
++                      }
++                      else {
++                          while(qbuf!=qend)
++                              *DBUF32 = get_unaligned(qbuf++);
++                      }
++
++                      dev->tsectors_left--;
++                      dev->tbuf += CF_SECT_SIZE;
++                      dev->tbuf_size -= CF_SECT_SIZE;
++                      transfered++;
++              } while (transfered != dev->block_size && dev->tsectors_left > 0);
++
++              res = wait_not_busy(10 * SECS, 1,dev);
++              if (res != CF_TRANS_OK)
++                      return res;
++      };
++
++      st = rareg(ATA_REG_ST,dev);
++      if (st & (ATA_REG_ST_DRQ | ATA_REG_ST_DWF | ATA_REG_ST_ERR)) {
++              if (st & ATA_REG_ST_DRQ) {
++                      printk(KERN_ERR "cf-mips: DRQ after all %d sectors are %s"
++                             ", status 0x%x\n", dev->tsector_count, (dev->tread ? "read" : "written"), st);
++              } else if (st & ATA_REG_ST_DWF) {
++                      printk(KERN_ERR "cf-mips: write fault, status 0x%x\n", st);
++              } else {
++                      int errId = rareg(ATA_REG_ERR,dev);
++                      printk(KERN_ERR "cf-mips: %s error, status 0x%x, errid 0x%x\n",
++                             (dev->tread ? "read" : "write"), st, errId);
++              }
++              return CF_TRANS_FAILED;
++      }
++      return CF_TRANS_OK;
++}
++
++static int cf_do_state(struct cf_mips_dev *dev)
++{
++      int res;
++      switch (dev->tstate) {  /* fall through everywhere */
++      case TS_IDLE:
++              dev->tstate = TS_READY;
++              if (is_busy(dev)) {
++                      dev->tstate = TS_AFTER_RESET;
++                      res = do_reset(dev);
++                      if (res != CF_TRANS_OK)
++                              break;
++              }
++      case TS_AFTER_RESET:
++              if (dev->tstate == TS_AFTER_RESET) {
++                      dev->tstate = TS_READY;
++                      res = set_multiple(dev);
++                      if (res != CF_TRANS_OK)
++                              break;
++              }
++      case TS_READY:
++              dev->tstate = TS_CMD;
++              res = set_cmd(dev);
++              if (res != CF_TRANS_OK)
++                      break;;
++      case TS_CMD:
++              dev->tstate = TS_TRANS;
++      case TS_TRANS:
++              res = do_trans(dev);
++              break;
++      default:
++              printk(KERN_ERR "cf-mips: BUG: unknown tstate %d\n", dev->tstate);
++              return CF_TRANS_FAILED;
++      }
++      if (res != CF_TRANS_IN_PROGRESS)
++              dev->tstate = TS_IDLE;
++      return res;
++}
++
++static void cf_do_tasklet(unsigned long dev_l)
++{
++      struct cf_mips_dev* dev=(struct cf_mips_dev*) dev_l;
++      int res;
++
++      disable_irq(dev->irq);
++
++      if (dev->tstate == TS_IDLE)
++              return;         /* can happen when irq is first registered */
++
++#if 0
++      DEBUGP(KERN_WARNING "cf-mips:   not busy ok (tasklet)  status 0x%02x\n",
++             (unsigned) rareg(ATA_REG_ST,dev));
++#endif
++
++      res = cf_do_state(dev);
++      if (res == CF_TRANS_IN_PROGRESS)
++              return;
++      cf_async_trans_done(dev,res);
++}
++
++static void cf_async_timeout(unsigned long dev_l)
++{
++      struct cf_mips_dev* dev=(struct cf_mips_dev*) dev_l;
++      disable_irq(dev->irq);
++      /* Perhaps send abort to the device? */
++      printk(KERN_ERR "cf-mips:  wait not busy timeout (%lus)"
++             ", status 0x%02x, state %d\n",
++             jiffies - dev->irq_enable_time, (unsigned) rareg(ATA_REG_ST,dev), dev->tstate);
++      dev->tstate = TS_IDLE;
++      cf_async_trans_done(dev,CF_TRANS_FAILED);
++}
++
++int cf_do_transfer(struct cf_mips_dev* dev,sector_t sector, unsigned long nsect, 
++      char* buffer, int is_write)
++{
++      BUG_ON(dev->tstate!=TS_IDLE);
++      if (nsect > ATA_MAX_SECT_PER_CMD) {
++              printk(KERN_WARNING "cf-mips: sector count %lu out of range\n",nsect);
++              return CF_TRANS_FAILED;
++      }
++      if (sector + nsect > dev->sectors) {
++              printk(KERN_WARNING "cf-mips: sector %lu out of range\n",sector);
++              return CF_TRANS_FAILED;
++      }
++      dev->tbuf = buffer;
++      dev->tbuf_size = nsect*512;
++      dev->tsect_start = sector;
++      dev->tsector_count = nsect;
++      dev->tsectors_left = dev->tsector_count;
++      dev->tread = (is_write)?0:1;
++      
++      dev->tcmd = (dev->block_size == 1 ?
++              (is_write ? ATA_CMD_WRITE_SECTORS : ATA_CMD_READ_SECTORS) :
++              (is_write ? ATA_CMD_WRITE_MULTIPLE : ATA_CMD_READ_MULTIPLE));
++
++      return cf_do_state(dev);
++}
++
++static int do_identify(struct cf_mips_dev *dev)
++{
++      u16 sbuf[CF_SECT_SIZE >> 1];
++      int res;
++      char tstr[17]; //serial
++      BUG_ON(dev->tstate!=TS_IDLE);
++      dev->tbuf = (char *) sbuf;
++      dev->tbuf_size = CF_SECT_SIZE;
++      dev->tsect_start = 0;
++      dev->tsector_count = 0;
++      dev->tsectors_left = 1;
++      dev->tread = 1;
++      dev->tcmd = ATA_CMD_IDENTIFY_DRIVE;
++
++      DEBUGP(KERN_INFO "cf-mips: identify drive..\n");
++      res = cf_do_state(dev);
++      if (res == CF_TRANS_IN_PROGRESS) {
++              printk(KERN_ERR "cf-mips: BUG: async identify cmd\n");
++              return CF_TRANS_FAILED;
++      }
++      if (res != CF_TRANS_OK)
++              return 0;
++
++      dev->head = sbuf[3];
++      dev->cyl = sbuf[1];
++      dev->spt = sbuf[6];
++      dev->sectors = ((unsigned long) sbuf[7] << 16) | sbuf[8];
++      dev->dtype=sbuf[0];
++      memcpy(tstr,&sbuf[12],16);
++      tstr[16]=0;
++      printk(KERN_INFO "cf-mips: %s detected, C/H/S=%d/%d/%d sectors=%u (%uMB) Serial=%s\n",
++             (sbuf[0] == 0x848A ? "CF card" : "ATA drive"), dev->cyl, dev->head,
++             dev->spt, dev->sectors, dev->sectors >> 11,tstr);
++      return 1;
++}
++
++static void init_multiple(struct cf_mips_dev * dev)
++{
++      int res;
++      DEBUGP(KERN_INFO "cf-mips: detecting block size\n");
++
++      dev->block_size = 128;  /* max block size = 128 sectors (64KB) */
++      do {
++              wareg(dev->block_size, ATA_REG_SC,dev);
++              wareg(ATA_REG_DH_BASE | ATA_REG_DH_LBA, ATA_REG_DH,dev);
++              wareg(ATA_CMD_SET_MULTIPLE, ATA_REG_CMD,dev);
++
++              res = wait_not_busy(10 * SECS, 1,dev);
++              if (res != CF_TRANS_OK) {
++                      printk(KERN_ERR "cf-mips: failed to detect block size: busy!\n");
++                      dev->block_size = 1;
++                      return;
++              }
++              if ((rareg(ATA_REG_ST,dev) & ATA_REG_ST_ERR) == 0)
++                      break;
++              dev->block_size /= 2;
++      } while (dev->block_size > 1);
++
++      printk(KERN_INFO "cf-mips: multiple sectors = %d\n", dev->block_size);
++}
++
++int cf_init(struct cf_mips_dev *dev)
++{
++      tasklet_init(&dev->tasklet,cf_do_tasklet,(unsigned long)dev);
++      dev->baddr = ioremap_nocache((unsigned long)dev->base, CFDEV_BUF_SIZE);
++      if (!dev->baddr) {
++              printk(KERN_ERR "cf-mips: cf_init: ioremap for (%lx,%x) failed\n",
++                     (unsigned long) dev->base, CFDEV_BUF_SIZE);
++              return -EBUSY;
++      }
++
++      if (!cf_present(dev)) {
++              printk(KERN_WARNING "cf-mips: cf card not present\n");
++              iounmap(dev->baddr);
++              return -ENODEV;
++      }
++
++      if (do_reset(dev) != CF_TRANS_OK) {
++              printk(KERN_ERR "cf-mips: cf reset failed\n");
++              iounmap(dev->baddr);
++              return -EBUSY;
++      }
++
++      if (!do_identify(dev)) {
++              printk(KERN_ERR "cf-mips: cf identify failed\n");
++              iounmap(dev->baddr);
++              return -EBUSY;
++      }
++
++/*    set_apm_level(ATA_APM_WITH_STANDBY); */
++      init_multiple(dev);
++
++      init_timer(&dev->to_timer);
++      dev->to_timer.function = cf_async_timeout;
++      dev->to_timer.data = (unsigned long)dev;
++
++      prepare_cf_irq(dev);
++      if (request_irq(dev->irq, cf_irq_handler, 0, "CF Mips", dev)) {
++              printk(KERN_ERR "cf-mips: failed to get irq\n");
++              iounmap(dev->baddr);
++              return -EBUSY;
++      }
++      /* Disable below would be odd, because request will enable, and the tasklet
++        will disable it itself */
++      //disable_irq(dev->irq);
++      
++      dev->async_mode = 1;
++
++      return 0;
++}
++
++void cf_cleanup(struct cf_mips_dev *dev)
++{
++      iounmap(dev->baddr);
++      free_irq(dev->irq, NULL);
++#if REQUEST_MEM_REGION
++      release_mem_region((unsigned long)dev->base, CFDEV_BUF_SIZE);
++#endif
++}
++
++
++/*eof*/
+diff -urN linux.old/drivers/block/rb500/ata.h linux.dev/drivers/block/rb500/ata.h
+--- linux.old/drivers/block/rb500/ata.h        1970-01-01 01:00:00.000000000 +0100
++++ linux.dev/drivers/block/rb500/ata.h        2006-10-26 00:11:14.000000000 +0200
+@@ -0,0 +1,143 @@
++/* CF-mips driver
++   This is a block driver for the direct (mmaped) interface to the CF-slot,
++   found in Routerboard.com's RB532 board
++   See SDK provided from routerboard.com.
++   
++   Module adapted By P.Christeas <p_christeas@yahoo.com>, 2005-6.
++   Cleaned up and adapted to platform_device by Felix Fietkau <nbd@openwrt.org>
++
++   This work is redistributed under the terms of the GNU General Public License.
++*/
++
++#ifndef __CFMIPS_ATA_H__
++#define __CFMIPS_ATA_H__
++
++#include <linux/interrupt.h>
++
++#define CFG_DC_DEV1   (void*)0xb8010010
++#define   CFG_DC_DEVBASE      0x0
++#define   CFG_DC_DEVMASK      0x4
++#define   CFG_DC_DEVC         0x8
++#define   CFG_DC_DEVTC                0xC
++
++#define CFDEV_BUF_SIZE        0x1000
++#define ATA_CIS_OFFSET        0x200
++#define ATA_REG_OFFSET        0x800
++#define ATA_DBUF_OFFSET       0xC00
++
++#define ATA_REG_FEAT  0x1
++#define ATA_REG_SC    0x2
++#define ATA_REG_SN    0x3
++#define ATA_REG_CL    0x4
++#define ATA_REG_CH    0x5
++#define ATA_REG_DH    0x6
++#define   ATA_REG_DH_BASE     0xa0
++#define   ATA_REG_DH_LBA      0x40
++#define   ATA_REG_DH_DRV      0x10
++#define ATA_REG_CMD   0x7
++#define ATA_REG_ST    0x7
++#define   ATA_REG_ST_BUSY     0x80
++#define   ATA_REG_ST_RDY      0x40
++#define   ATA_REG_ST_DWF      0x20
++#define   ATA_REG_ST_DSC      0x10
++#define   ATA_REG_ST_DRQ      0x08
++#define   ATA_REG_ST_CORR     0x04
++#define   ATA_REG_ST_ERR      0x01
++#define ATA_REG_ERR   0xd
++#define ATA_REG_DC    0xe
++#define   ATA_REG_DC_IEN      0x02
++#define   ATA_REG_DC_SRST     0x04
++
++#define ATA_CMD_READ_SECTORS  0x20
++#define ATA_CMD_WRITE_SECTORS 0x30
++#define ATA_CMD_EXEC_DRIVE_DIAG       0x90
++#define ATA_CMD_READ_MULTIPLE 0xC4
++#define ATA_CMD_WRITE_MULTIPLE        0xC5
++#define ATA_CMD_SET_MULTIPLE  0xC6
++#define ATA_CMD_IDENTIFY_DRIVE        0xEC
++#define ATA_CMD_SET_FEATURES  0xEF
++
++#define ATA_FEATURE_ENABLE_APM        0x05
++#define ATA_FEATURE_DISABLE_APM       0x85
++#define ATA_APM_DISABLED      0x00
++#define ATA_APM_MIN_POWER     0x01
++#define ATA_APM_WITH_STANDBY  0x7f
++#define ATA_APM_WITHOUT_STANDBY       0x80
++#define ATA_APM_MAX_PERFORMANCE       0xfe
++
++#define CF_SECT_SIZE  0x200
++/* That is the ratio CF_SECT_SIZE/512 (the kernel sector size) */
++#define CF_KERNEL_MUL 1
++#define ATA_MAX_SECT_PER_CMD  0x100
++
++#define CF_TRANS_FAILED               0
++#define CF_TRANS_OK           1
++#define CF_TRANS_IN_PROGRESS  2
++
++
++enum trans_state {
++      TS_IDLE = 0,
++      TS_AFTER_RESET,
++      TS_READY,
++      TS_CMD,
++      TS_TRANS
++};
++
++// 
++// #if DEBUG
++// static unsigned long busy_time;
++// #endif
++
++/** Struct to hold the cfdev
++Actually, all the data here only has one instance. However, for 
++reasons of programming conformity, it is passed around as a pointer
++*/
++struct cf_mips_dev {
++      void *base; /* base address for I/O */
++      void *baddr; /* remapped address */
++
++      int pin; /* gpio pin */
++      int irq; /* gpio irq */
++      
++      unsigned head;
++      unsigned cyl;
++      unsigned spt;
++      unsigned sectors;
++      
++      unsigned short block_size;
++      unsigned dtype ; // ATA or CF
++      struct request_queue *queue;
++      struct gendisk  *gd;
++      
++      /* Transaction state */
++      enum trans_state tstate;
++      char *tbuf;
++      unsigned long tbuf_size;
++      sector_t tsect_start;
++      unsigned tsector_count;
++      unsigned tsectors_left;
++      int tread;
++      unsigned tcmd;
++      int async_mode;
++      unsigned long irq_enable_time;
++      
++      struct request *active_req; /* A request is being carried out. Is that different from tstate? */
++      int users;
++      struct timer_list to_timer;
++      struct tasklet_struct tasklet;
++
++      /** This lock ensures that the requests to this device are all done
++      atomically. Transfers can run in parallel, requests are all queued
++      one-by-one */
++      spinlock_t lock;
++};
++
++int cf_do_transfer(struct cf_mips_dev* dev,sector_t sector, unsigned long nsect, 
++      char* buffer, int is_write);
++int cf_init(struct cf_mips_dev* dev);
++void cf_cleanup(struct cf_mips_dev* dev);
++
++void cf_async_trans_done(struct cf_mips_dev* dev, int result);
++// void *cf_get_next_buf(unsigned long *buf_size);
++
++#endif
+diff -urN linux.old/drivers/block/rb500/bdev.c linux.dev/drivers/block/rb500/bdev.c
+--- linux.old/drivers/block/rb500/bdev.c       1970-01-01 01:00:00.000000000 +0100
++++ linux.dev/drivers/block/rb500/bdev.c       2006-10-26 00:11:14.000000000 +0200
+@@ -0,0 +1,339 @@
++/* CF-mips driver
++   This is a block driver for the direct (mmaped) interface to the CF-slot,
++   found in Routerboard.com's RB532 board
++   See SDK provided from routerboard.com.
++   
++   Module adapted By P.Christeas <p_christeas@yahoo.com>, 2005-6.
++   Cleaned up and adapted to platform_device by Felix Fietkau <nbd@openwrt.org>
++
++   This work is redistributed under the terms of the GNU General Public License.
++*/
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/time.h>
++#include <linux/wait.h>
++#include <linux/fs.h>
++#include <linux/genhd.h>
++#include <linux/blkdev.h>
++#include <linux/blkpg.h>
++#include <linux/hdreg.h>
++#include <linux/platform_device.h>
++
++#include <asm/uaccess.h>
++#include <asm/io.h>
++
++#include <asm/rc32434/rb.h>
++
++#ifdef DEBUG
++#define DEBUGP printk
++#define DLEVEL 1
++#else
++#define DEBUGP(format, args...)
++#define DLEVEL 0
++#endif
++
++#define CF_MIPS_MAJOR 13
++#define MAJOR_NR      CF_MIPS_MAJOR
++#define CF_MAX_PART   16              /* max 15 partitions */
++
++#include "ata.h"
++
++//extern struct block_device_operations cf_bdops;
++
++// static struct hd_struct cf_parts[CF_MAX_PART];
++// static int cf_part_sizes[CF_MAX_PART];
++// static int cf_hsect_sizes[CF_MAX_PART];
++// static int cf_max_sectors[CF_MAX_PART];
++// static int cf_blksize_sizes[CF_MAX_PART];
++
++// static spinlock_t lock = SPIN_LOCK_UNLOCKED;
++
++// volatile int cf_busy = 0;
++
++static struct request *active_req = NULL;
++
++static int cf_open (struct inode *, struct file *);
++static int cf_release (struct inode *, struct file *);
++static int cf_ioctl (struct inode *, struct file *, unsigned, unsigned long);
++
++static void cf_request(request_queue_t * q);
++static int cf_transfer(const struct request *req);
++
++/*long (*unlocked_ioctl) (struct file *, unsigned, unsigned long);
++long (*compat_ioctl) (struct file *, unsigned, unsigned long);*/
++// int (*direct_access) (struct block_device *, sector_t, unsigned long *);
++// int (*media_changed) (struct gendisk *);
++// int (*revalidate_disk) (struct gendisk *);
++
++static struct block_device_operations cf_bdops = {
++      .owner = THIS_MODULE,
++      .open = cf_open,
++      .release = cf_release,
++      .ioctl = cf_ioctl,
++      .media_changed = NULL,
++      .unlocked_ioctl = NULL,
++      .revalidate_disk = NULL,
++      .compat_ioctl = NULL,
++      .direct_access = NULL
++};
++
++
++int cf_mips_probe(struct platform_device *pdev)
++{
++      struct gendisk* cf_gendisk=NULL;
++      struct cf_device *cdev = (struct cf_device *) pdev->dev.platform_data;
++      struct cf_mips_dev *dev;
++      struct resource *r;
++      int reg_result;
++
++      reg_result = register_blkdev(MAJOR_NR, "cf-mips");
++      if (reg_result < 0) {
++              printk(KERN_WARNING "cf-mips: can't get major %d\n", MAJOR_NR);
++              return reg_result;
++      }
++
++      dev = (struct cf_mips_dev *)kmalloc(sizeof(struct cf_mips_dev),GFP_KERNEL);
++      if (!dev)
++              goto out_err;
++      memset(dev, 0, sizeof(struct cf_mips_dev));
++      cdev->dev = dev;
++      
++      dev->pin = cdev->gpio_pin;
++      dev->irq = platform_get_irq_byname(pdev, "cf_irq");
++      r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cf_membase");
++      dev->base = (void *) r->start;
++      
++      if (cf_init(dev)) goto out_err;
++      printk("init done");
++      
++      spin_lock_init(&dev->lock);
++      dev->queue = blk_init_queue(cf_request,&dev->lock);
++      if (!dev->queue){
++              printk(KERN_ERR "cf-mips: no mem for queue\n");
++              goto out_err;
++      }
++      blk_queue_max_sectors(dev->queue,ATA_MAX_SECT_PER_CMD);
++
++      /* For memory devices, it is always better to avoid crossing segments
++      inside the same request. */
++/*    if (dev->dtype==0x848A){
++              printk(KERN_INFO "Setting boundary for cf to 0x%x",(dev->block_size*512)-1);
++              blk_queue_segment_boundary(dev->queue, (dev->block_size*512)-1);
++      }*/
++
++      dev->gd = alloc_disk(CF_MAX_PART);
++      cf_gendisk = dev->gd;
++      cdev->gd = dev->gd;
++      if (!cf_gendisk) goto out_err; /* Last of these goto's */
++      
++      cf_gendisk->major = MAJOR_NR;
++      cf_gendisk->first_minor = 0;
++      cf_gendisk->queue=dev->queue;
++      BUG_ON(cf_gendisk->minors != CF_MAX_PART);
++      strcpy(cf_gendisk->disk_name,"cfa");
++      cf_gendisk->fops = &cf_bdops;
++      cf_gendisk->flags = 0 ; /* is not yet GENHD_FL_REMOVABLE */
++      cf_gendisk->private_data=dev;
++      
++      set_capacity(cf_gendisk,dev->sectors * CF_KERNEL_MUL);
++      
++      /* Let the disk go live */
++      add_disk(cf_gendisk);
++#if 0
++      result = cf_init();
++      
++      /* default cfg for all partitions */
++      memset(cf_parts, 0, sizeof (cf_parts[0]) * CF_MAX_PART);
++      memset(cf_part_sizes, 0, sizeof (cf_part_sizes[0]) * CF_MAX_PART);
++      for (i = 0; i < CF_MAX_PART; ++i) {
++              cf_hsect_sizes[i] = CF_SECT_SIZE;
++              cf_max_sectors[i] = ATA_MAX_SECT_PER_CMD;
++              cf_blksize_sizes[i] = BLOCK_SIZE;
++      }
++
++      /* setup info for whole disk (partition 0) */
++      cf_part_sizes[0] = cf_sectors / 2;
++      cf_parts[0].nr_sects = cf_sectors;
++
++      blk_size[MAJOR_NR] = cf_part_sizes;
++      blksize_size[MAJOR_NR] = cf_blksize_sizes;
++      max_sectors[MAJOR_NR] = cf_max_sectors;
++      hardsect_size[MAJOR_NR] = cf_hsect_sizes;
++      read_ahead[MAJOR_NR] = 8;       /* (4kB) */
++
++      blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
++
++      add_gendisk(&cf_gendisk);
++#endif
++//    printk(KERN_INFO "cf-mips partition check: \n");
++//    register_disk(cf_gendisk, MKDEV(MAJOR_NR, 0), CF_MAX_PART,
++//                  &cf_bdops, dev->sectors);
++      return 0;
++
++out_err:
++      if (dev->queue){
++              blk_cleanup_queue(dev->queue);
++      }
++      if (reg_result) {
++              unregister_blkdev(MAJOR_NR, "cf-mips");
++              return reg_result;
++      }
++      if (dev){
++              cf_cleanup(dev);
++              kfree(dev);
++      }
++      return 1;
++}
++
++static int
++cf_mips_remove(struct platform_device *pdev)
++{
++      struct cf_device *cdev = (struct cf_device *) pdev->dev.platform_data;
++      struct cf_mips_dev *dev = (struct cf_mips_dev *) cdev->dev;
++      
++      unregister_blkdev(MAJOR_NR, "cf-mips");
++      blk_cleanup_queue(dev->queue);
++
++      del_gendisk(dev->gd);
++      cf_cleanup(dev);
++      return 0;
++}
++
++
++static struct platform_driver cf_driver = {
++      .driver.name = "rb500-cf",
++      .probe = cf_mips_probe,
++      .remove = cf_mips_remove,
++};
++
++static int __init cf_mips_init(void)
++{
++      printk(KERN_INFO "cf-mips module loaded\n");
++      return platform_driver_register(&cf_driver);
++}
++
++static void cf_mips_cleanup(void)
++{
++      platform_driver_unregister(&cf_driver);
++      printk(KERN_INFO "cf-mips module removed\n");
++}
++
++module_init(cf_mips_init);
++module_exit(cf_mips_cleanup);
++
++MODULE_LICENSE("GPL");
++MODULE_ALIAS_BLOCKDEV_MAJOR(CF_MIPS_MAJOR);
++
++
++static int cf_open(struct inode *inode, struct file *filp)
++{
++      struct cf_mips_dev  *dev=inode->i_bdev->bd_disk->private_data;
++      int minor = MINOR(inode->i_rdev);
++      
++      if (minor >= CF_MAX_PART)
++              return -ENODEV;
++      //DEBUGP(KERN_INFO "cf-mips module opened, minor %d\n", minor);
++      spin_lock(&dev->lock);
++      dev->users++;
++      spin_unlock(&dev->lock);
++      filp->private_data=dev;
++      
++      /* dirty workaround to set CFRDY GPIO as an input when some other
++         program sets it as an output */
++      gpio_set(CFG, (1 << dev->pin), 0);
++      return 0;               /* success */
++}
++
++static int cf_release(struct inode *inode, struct file *filp)
++{
++      int minor = MINOR(inode->i_rdev);
++      struct cf_mips_dev  *dev=inode->i_bdev->bd_disk->private_data;
++      spin_lock(&dev->lock);
++      dev->users--;
++      spin_unlock(&dev->lock);
++      return 0;
++}
++
++static int cf_ioctl(struct inode *inode, struct file *filp,
++       unsigned int cmd, unsigned long arg)
++{
++      unsigned minor = MINOR(inode->i_rdev);
++      struct cf_mips_dev  *dev=inode->i_bdev->bd_disk->private_data;
++
++      DEBUGP(KERN_INFO "cf_ioctl cmd %u\n", cmd);
++      switch (cmd) {
++      case BLKRRPART: /* re-read partition table */
++              if (!capable(CAP_SYS_ADMIN))
++                      return -EACCES;
++              printk(KERN_INFO "cf-mips partition check: \n");
++              register_disk(dev->gd);
++              return 0;
++
++      case HDIO_GETGEO:
++              {
++                      struct hd_geometry geo;
++                      geo.cylinders = dev->cyl;
++                      geo.heads = dev->head;
++                      geo.sectors = dev->spt;
++                      geo.start = (*dev->gd->part)[minor].start_sect;
++                      if (copy_to_user((void *) arg, &geo, sizeof (geo)))
++                              return -EFAULT;
++              }
++              return 0;
++      }
++
++      return -EINVAL;         /* unknown command */
++}
++
++static void cf_request(request_queue_t * q)
++{
++      struct cf_mips_dev* dev;
++      
++      struct request * req;
++      int status;
++
++      /* We could have q->queuedata = dev , but haven't yet. */
++      if (active_req)
++              return;
++
++      while ((req=elv_next_request(q))!=NULL){
++              dev=req->rq_disk->private_data;
++              status=cf_transfer(req);
++              if (status==CF_TRANS_IN_PROGRESS){
++                      active_req=req;
++                      return;
++              }
++              end_request(req,status);
++      }
++}
++
++static int cf_transfer(const struct request *req)
++{
++      struct cf_mips_dev* dev=req->rq_disk->private_data;
++
++      if (!blk_fs_request(req)){      
++              if (printk_ratelimit())
++                      printk(KERN_WARNING "cf-mips: skipping non-fs request 0x%x\n",req->cmd[0]);
++              return CF_TRANS_FAILED;
++      }
++      
++      return cf_do_transfer(dev,req->sector,req->current_nr_sectors,req->buffer,rq_data_dir(req));
++}
++
++void cf_async_trans_done(struct cf_mips_dev * dev,int result)
++{
++      struct request *req;
++      
++      spin_lock(&dev->lock);
++      req=active_req;
++      active_req=NULL;
++      end_request(req,result);
++      spin_unlock(&dev->lock);
++
++      spin_lock(&dev->lock);
++      cf_request(dev->queue);
++      spin_unlock(&dev->lock);
++}
++
+diff -urN linux.old/drivers/block/rb500/Makefile linux.dev/drivers/block/rb500/Makefile
+--- linux.old/drivers/block/rb500/Makefile     1970-01-01 01:00:00.000000000 +0100
++++ linux.dev/drivers/block/rb500/Makefile     2006-10-26 00:11:14.000000000 +0200
+@@ -0,0 +1,3 @@
++## Makefile for the RB532 CF port
++
++obj-y         += bdev.o ata.o