diff options
author | Florian Fainelli <florian@openwrt.org> | 2007-04-02 16:59:59 +0000 |
---|---|---|
committer | Florian Fainelli <florian@openwrt.org> | 2007-04-02 16:59:59 +0000 |
commit | 3e8d73d762c0e473bcfa49dbdc488b48ba57e06a (patch) | |
tree | 49b3d3f10ddedb39df71c2cdac6b597d2115a6cc /target/linux/rb1xx-2.6/patches/120-cf.patch | |
parent | 75262124ba989c05a5840d8ad878429589e68249 (diff) | |
download | upstream-3e8d73d762c0e473bcfa49dbdc488b48ba57e06a.tar.gz upstream-3e8d73d762c0e473bcfa49dbdc488b48ba57e06a.tar.bz2 upstream-3e8d73d762c0e473bcfa49dbdc488b48ba57e06a.zip |
Add preliminary RouterBoard RB1xx support
SVN-Revision: 6839
Diffstat (limited to 'target/linux/rb1xx-2.6/patches/120-cf.patch')
-rw-r--r-- | target/linux/rb1xx-2.6/patches/120-cf.patch | 1011 |
1 files changed, 1011 insertions, 0 deletions
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 index 0000000000..a22c526132 --- /dev/null +++ b/target/linux/rb1xx-2.6/patches/120-cf.patch @@ -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 |