diff options
| author | florian <florian@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2007-10-22 20:56:34 +0000 | 
|---|---|---|
| committer | florian <florian@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2007-10-22 20:56:34 +0000 | 
| commit | 663af573eb897f7df39e85a652cd4f7cfb834f29 (patch) | |
| tree | 27d471d824bb7099552b1889328cd863ed88efbb | |
| parent | 01535e65a277f46aa658637a6a9a79f8212b237f (diff) | |
Add preliminary support for the Routerboard 153 CF slot (#2550)
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@9403 3c298f89-4303-0410-b956-a3cf2f4a3e73
9 files changed, 1079 insertions, 2 deletions
| diff --git a/target/linux/adm5120/files/arch/mips/adm5120/boards/mikrotik.c b/target/linux/adm5120/files/arch/mips/adm5120/boards/mikrotik.c index 64c32c623..74dd860dc 100644 --- a/target/linux/adm5120/files/arch/mips/adm5120/boards/mikrotik.c +++ b/target/linux/adm5120/files/arch/mips/adm5120/boards/mikrotik.c @@ -42,6 +42,7 @@  #include <adm5120_nand.h>  #include <adm5120_board.h>  #include <adm5120_platform.h> +#include <adm5120_cf.h>  #define RB1XX_NAND_CHIP_DELAY	25 @@ -178,6 +179,35 @@ static void rb150_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,  }  /*--------------------------------------------------------------------------*/ +static struct resource cf_slot0_res[] = { +        { +                .name = "cf_membase", +                .flags = IORESOURCE_MEM +        }, { +                .name = "cf_irq", +                .start = INTC_IRQ_GPIO4, /* 5 */ +                .end = INTC_IRQ_GPIO4, +                .flags = IORESOURCE_IRQ +        } +}; + +static struct cf_device cf_slot0_data = { +        .gpio_pin = 4 +}; + +static struct platform_device cf_slot0 = { +        .id = 0, +        .name = "rb153-cf", +        .dev.platform_data = &cf_slot0_data, +        .resource = cf_slot0_res, +        .num_resources = ARRAY_SIZE(cf_slot0_res), +}; + +static struct platform_device *rb153_devices[] __initdata = { +        &adm5120_flash0_device, +        &adm5120_nand_device, +	&cf_slot0, +};  static void __init rb1xx_mac_setup(void)  { @@ -299,8 +329,8 @@ static struct adm5120_board rb153_board __initdata = {  	.board_setup	= rb1xx_setup,  	.eth_num_ports	= 5,  	.eth_vlans	= rb15x_vlans, -	.num_devices	= ARRAY_SIZE(rb1xx_devices), -	.devices	= rb1xx_devices, +	.num_devices	= ARRAY_SIZE(rb153_devices), +	.devices	= rb153_devices,  	.pci_nr_irqs	= ARRAY_SIZE(rb1xx_pci_irqs),  	.pci_irq_map	= rb1xx_pci_irqs,  }; diff --git a/target/linux/adm5120/files/drivers/block/rb1xx/Makefile b/target/linux/adm5120/files/drivers/block/rb1xx/Makefile new file mode 100644 index 000000000..672aaa697 --- /dev/null +++ b/target/linux/adm5120/files/drivers/block/rb1xx/Makefile @@ -0,0 +1,2 @@ +## Makefile for the RB1xx CF port +obj-y		+= bdev.o ata.o diff --git a/target/linux/adm5120/files/drivers/block/rb1xx/ata.c b/target/linux/adm5120/files/drivers/block/rb1xx/ata.c new file mode 100644 index 000000000..c73d21958 --- /dev/null +++ b/target/linux/adm5120/files/drivers/block/rb1xx/ata.c @@ -0,0 +1,507 @@ +/* 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 <gpio.h> +#include <adm5120_defs.h> +#include <adm5120_irq.h> +#include <adm5120_switch.h> +#include <adm5120_intc.h> +#include <adm5120_mpmc.h> +#include <adm5120_cf.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; + +#define DBUF32 ((volatile u32 *)((unsigned long)dev->baddr | ATA_DBUF_OFFSET)) + +#define INTC_WRITE(reg, val)    __raw_writel((val), \ +        (void __iomem *)(KSEG1ADDR(ADM5120_INTC_BASE) + reg)) + +#define INTC_READ(reg)          __raw_readl(\ +        (void __iomem *)(KSEG1ADDR(ADM5120_INTC_BASE) + reg)) + +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 cfrdy(struct cf_mips_dev *dev) +{ +	return (SW_READ_REG(GPIO_CONF0) & (1 << ADM5120_GPIO_PIN4)); +} + +static inline void prepare_cf_irq(struct cf_mips_dev *dev) +{ +	/* interrupt on cf ready (not busy) */ +	INTC_WRITE(INTC_REG_INT_LEVEL, INTC_READ(INTC_REG_INT_LEVEL) | ADM5120_CF_IRQ_LEVEL_BIT); + +	/* FIXME: how to 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 +	 */ + +	/* FIXME: enabling of EXTIO will be done by BIOS in future */ +	unsigned cmd = EXTIO_CS0_INT0_EN; +	int i; + +	/* on RB100 WAIT is LOW all the time => read will hang */ +	if (SW_READ_REG(GPIO_CONF0) & (1 << ADM5120_GPIO_PIN0)) +		cmd |= EXTIO_WAIT_EN; + +	SW_WRITE_REG(GPIO_CONF2, cmd); +	SW_WRITE_REG(GPIO_CONF0, (SW_READ_REG(GPIO_CONF0) & ~(1 << (16 + ADM5120_CF_GPIO_NUM)))); +	 +	/* FIXME: timings will be set by BIOS in future - remove this */ +	MPMC_WRITE_REG(SC2, 0x88); +	MPMC_WRITE_REG(WEN2, 0x02); +	MPMC_WRITE_REG(OEN2, 0x03); +	MPMC_WRITE_REG(RD2, 0x1a); +	MPMC_WRITE_REG(PG2, 0x1d); +	MPMC_WRITE_REG(WR2, 0x14); +	MPMC_WRITE_REG(TN2, 0x09); + +	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; + +	if (!cfrdy(dev)) +		return;        // false interrupt (only for ADM5120) + +	INTC_WRITE(INTC_REG_INT_LEVEL, (INTC_READ(INTC_REG_INT_LEVEL) & (~ADM5120_CF_IRQ_LEVEL_BIT))); +		 +	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 --git a/target/linux/adm5120/files/drivers/block/rb1xx/ata.h b/target/linux/adm5120/files/drivers/block/rb1xx/ata.h new file mode 100644 index 000000000..15e882630 --- /dev/null +++ b/target/linux/adm5120/files/drivers/block/rb1xx/ata.h @@ -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 --git a/target/linux/adm5120/files/drivers/block/rb1xx/bdev.c b/target/linux/adm5120/files/drivers/block/rb1xx/bdev.c new file mode 100644 index 000000000..12c938505 --- /dev/null +++ b/target/linux/adm5120/files/drivers/block/rb1xx/bdev.c @@ -0,0 +1,338 @@ +/* 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/mach-adm5120/adm5120_cf.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 = "rb153-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 --git a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_cf.h b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_cf.h new file mode 100644 index 000000000..e26e1d419 --- /dev/null +++ b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_cf.h @@ -0,0 +1,19 @@ +#include <linux/types.h> +#include <linux/io.h> + +#include <adm5120_defs.h> +#include <adm5120_switch.h> + +/* CFRDY is connected to GPIO4/INTX_1 */ +#define ADM5120_CF_GPIO_NUM     4 +#define ADM5120_CF_IRQ_LEVEL_BIT        0x20    /* GPIO4 = 0x20, GPIO2 = 0x10 */ +#define ADM5120_IRQ_CFRDY       5 +#define EXTIO_WAIT_EN		(0x1 << 6) +#define EXTIO_CS1_INT1_EN	(0x1 << 5) +#define EXTIO_CS0_INT0_EN	(0x1 << 4) + +struct cf_device { +        int gpio_pin; +        void *dev; +        struct gendisk *gd; +}; diff --git a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_mpmc.h b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_mpmc.h index 406d6c563..8209d88a4 100644 --- a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_mpmc.h +++ b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_mpmc.h @@ -49,6 +49,12 @@  #define MPMC_REG_SC0	0x0200  /* for F_CS1_N */  #define MPMC_REG_SC1	0x0220  /* for F_CS0_N */  #define MPMC_REG_SC2    0x0240 +#define MPMC_REG_WEN2	0x0244 +#define MPMC_REG_OEN2	0x0248 +#define MPMC_REG_RD2	0x024C +#define MPMC_REG_PG2	0x0250 +#define MPMC_REG_WR2	0x0254 +#define MPMC_REG_TN2	0x0258  #define MPMC_REG_SC3    0x0260  /* Control register bits */ diff --git a/target/linux/adm5120/patches-2.6.23/120-cf.patch b/target/linux/adm5120/patches-2.6.23/120-cf.patch new file mode 100644 index 000000000..cbfe42d6c --- /dev/null +++ b/target/linux/adm5120/patches-2.6.23/120-cf.patch @@ -0,0 +1,31 @@ +Index: linux-2.6.23/drivers/block/Kconfig +=================================================================== +--- linux-2.6.23/drivers/block/Kconfig	2007-10-09 22:31:38.000000000 +0200 ++++ linux-2.6.23.new/drivers/block/Kconfig	2007-10-22 18:07:49.000000000 +0200 +@@ -65,6 +65,14 @@ + 	  To compile this driver as a module, choose M here: the + 	  module will be called z2ram. +  ++config BLK_DEV_CF_MIPS ++	bool "CF slot of RB153 board" ++	depends on MIPS_ADM5120 ++	default y ++	help ++	  The Routerboard 153 has a CF slot on it. Enable the special block ++	  device driver for it. ++ + config BLK_DEV_XD + 	tristate "XT hard disk support" + 	depends on ISA && ISA_DMA_API +Index: linux-2.6.23/drivers/block/Makefile +=================================================================== +--- linux-2.6.23/drivers/block/Makefile	2007-10-09 22:31:38.000000000 +0200 ++++ linux-2.6.23.new/drivers/block/Makefile.new	2007-10-22 18:08:15.000000000 +0200 +@@ -7,6 +7,7 @@ +  + obj-$(CONFIG_MAC_FLOPPY)	+= swim3.o + obj-$(CONFIG_BLK_DEV_FD)	+= floppy.o ++obj-$(CONFIG_BLK_DEV_CF_MIPS)	+= rb1xx/ + obj-$(CONFIG_AMIGA_FLOPPY)	+= amiflop.o + obj-$(CONFIG_PS3_DISK)		+= ps3disk.o + obj-$(CONFIG_ATARI_FLOPPY)	+= ataflop.o diff --git a/target/linux/adm5120/router_le/config-2.6.23 b/target/linux/adm5120/router_le/config-2.6.23 index da96c7306..4267f70a7 100644 --- a/target/linux/adm5120/router_le/config-2.6.23 +++ b/target/linux/adm5120/router_le/config-2.6.23 @@ -10,6 +10,7 @@ CONFIG_BAYCOM_SER_FDX=m  CONFIG_BAYCOM_SER_HDX=m  CONFIG_BINFMT_MISC=m  CONFIG_BITREVERSE=y +CONFIG_BLK_DEV_CF_MIPS=y  CONFIG_CIFS_DEBUG2=y  CONFIG_CIFS_EXPERIMENTAL=y  CONFIG_CIFS_STATS2=y | 
