diff options
| author | florian <florian@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2008-07-15 17:18:28 +0000 | 
|---|---|---|
| committer | florian <florian@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2008-07-15 17:18:28 +0000 | 
| commit | 82da4199c3fefabb6e2ad89bc349eb1dbad94178 (patch) | |
| tree | d9739f3e39aff522c95b81398ef01375c26a11b3 /target/linux/rb532/files-2.6.24/drivers | |
| parent | 42c88ed264e86b3e8287f85e2099bc2ca7c5f51d (diff) | |
Add basic 2.6.24 support for rb532, korina napi code has to be adapted to work
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@11844 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/rb532/files-2.6.24/drivers')
8 files changed, 2595 insertions, 0 deletions
| diff --git a/target/linux/rb532/files-2.6.24/drivers/block/rb500/Makefile b/target/linux/rb532/files-2.6.24/drivers/block/rb500/Makefile new file mode 100644 index 000000000..3e14dc35b --- /dev/null +++ b/target/linux/rb532/files-2.6.24/drivers/block/rb500/Makefile @@ -0,0 +1,3 @@ +## Makefile for the RB532 CF port + +obj-y		+= bdev.o ata.o diff --git a/target/linux/rb532/files-2.6.24/drivers/block/rb500/ata.c b/target/linux/rb532/files-2.6.24/drivers/block/rb500/ata.c new file mode 100644 index 000000000..31e6782fa --- /dev/null +++ b/target/linux/rb532/files-2.6.24/drivers/block/rb500/ata.c @@ -0,0 +1,487 @@ +/* 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/gpio.h> +#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 cfrdy(struct cf_mips_dev *dev) +{ +	return gpio_get_value(dev->pin); +} + +static inline void prepare_cf_irq(struct cf_mips_dev *dev) +{ +	rb500_gpio_set_int_level(1, dev->pin);	/* interrupt on cf ready (not busy) */ +	rb500_gpio_set_int_status(0, dev->pin); 	/* 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 */ +	rb500_gpio_set_func(dev->pin, 0); +	gpio_direction_input(dev->pin); + +	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; + +	rb500_gpio_set_int_level(0, dev->pin); +	rb500_gpio_set_int_status(0, dev->pin); +	 +	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 +	char tmp; +	int i; +	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'; + +	/* Byte-swap the serial number */ +	for (i = 0; i<8; i++) { +		tmp = tstr[i * 2]; +		tstr[i * 2] = tstr[i * 2 +1]; +		tstr[i * 2 + 1] = tmp; +	} + +	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/rb532/files-2.6.24/drivers/block/rb500/ata.h b/target/linux/rb532/files-2.6.24/drivers/block/rb500/ata.h new file mode 100644 index 000000000..15e882630 --- /dev/null +++ b/target/linux/rb532/files-2.6.24/drivers/block/rb500/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/rb532/files-2.6.24/drivers/block/rb500/bdev.c b/target/linux/rb532/files-2.6.24/drivers/block/rb500/bdev.c new file mode 100644 index 000000000..f8a9b02d7 --- /dev/null +++ b/target/linux/rb532/files-2.6.24/drivers/block/rb500/bdev.c @@ -0,0 +1,340 @@ +/* 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/gpio.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_value(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/rb532/files-2.6.24/drivers/char/watchdog/rc32434_wdt.c b/target/linux/rb532/files-2.6.24/drivers/char/watchdog/rc32434_wdt.c new file mode 100644 index 000000000..2266329de --- /dev/null +++ b/target/linux/rb532/files-2.6.24/drivers/char/watchdog/rc32434_wdt.c @@ -0,0 +1,240 @@ +/* + *	RC32434_WDT 0.01: IDT Interprise 79RC32434 watchdog driver  + *	Copyright (c) Ondrej Zajicek <santiago@crfreenet.org>, 2006 + * + *	based on + * + *	SoftDog	0.05:	A Software Watchdog Device + * + *	(c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved. + * + *	This program is free software; you can redistribute it and/or + *	modify it under the terms of the GNU General Public License + *	as published by the Free Software Foundation; either version + *	2 of the License, or (at your option) any later version. + *	 + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/reboot.h> +#include <linux/smp_lock.h> +#include <linux/init.h> +#include <asm/bootinfo.h> +#include <asm/time.h> +#include <asm/uaccess.h> +#include <asm/rc32434/integ.h> + +#define DEFAULT_TIMEOUT	15		/* (secs) Default is 15 seconds */ +#define MAX_TIMEOUT	20		 +/* + * (secs) Max is 20 seconds  + * (max frequency of counter is ~200 MHz, counter is 32-bit unsigned int)  + */ + +#define NAME "rc32434_wdt" +#define VERSION "0.1" + +static INTEG_t rc_wdt = (INTEG_t) INTEG_VirtualAddress;                                                                      + +static int expect_close = 0; +static int access = 0; +static int timeout = 0; + +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +MODULE_LICENSE("GPL"); + + +static inline void start_wdt(void) +{ +	rc_wdt -> wtcount = 0; +	rc_wdt -> errcs |= ERRCS_wre_m; +	rc_wdt -> wtc   |= WTC_en_m; +} + +static inline void stop_wdt(void) +{ +	rc_wdt -> wtc   &= ~WTC_en_m; +	rc_wdt -> errcs &= ~ERRCS_wre_m; +} + +static inline void set_wdt(int new_timeout) +{ +	u32 cmp = new_timeout * mips_hpt_frequency; +	u32 state; +	 +	timeout = new_timeout; +	/* +	 * store and disable WTC +	 */ +	state = rc_wdt -> wtc & WTC_en_m; +	rc_wdt -> wtc   &= ~WTC_en_m; + +	rc_wdt -> wtcount = 0; +	rc_wdt -> wtcompare = cmp; + +	/* +	 * restore WTC +	 */	 +	rc_wdt -> wtc |= state; +} + +static inline void update_wdt(void) +{ +	rc_wdt -> wtcount = 0; +} + +/* + *	Allow only one person to hold it open + */ +  +static int wdt_open(struct inode *inode, struct file *file) +{ +	if (access) +		return -EBUSY; +	if (nowayout) { +		__module_get(THIS_MODULE); +	} +	/* +	 *	Activate timer +	 */ +	start_wdt(); +	printk(KERN_INFO NAME ": enabling watchdog timer\n"); +	access = 1; +	return 0; +} + +static int wdt_release(struct inode *inode, struct file *file) +{ +	/* +	 *	Shut off the timer. +	 * 	Lock it in if it's a module and we set nowayout +	 */ +	if (expect_close && nowayout == 0) { +		stop_wdt (); +		printk(KERN_INFO NAME ": disabling watchdog timer\n"); +		module_put(THIS_MODULE); +	} else { +		printk (KERN_CRIT NAME ": device closed unexpectedly.  WDT will not stop!\n"); +	} +	access = 0; +	return 0; +} + +static ssize_t wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ +	/* +	 *	Refresh the timer. +	 */ +	if (len) { +		if (!nowayout) { +			size_t i; + +			/* In case it was set long ago */ +			expect_close = 0; + +			for (i = 0; i != len; i++) { +				char c; +				if (get_user(c, data + i)) +					return -EFAULT; +				if (c == 'V') +					expect_close = 1; +			} +		} +		update_wdt (); +		return len; +	} +	return 0; +} + +static int wdt_ioctl(struct inode *inode, struct file *file, +	unsigned int cmd, unsigned long arg) +{ +	int new_timeout; +	static struct watchdog_info ident = { +		.options =		WDIOF_SETTIMEOUT | +					WDIOF_KEEPALIVEPING | +					WDIOF_MAGICCLOSE, +		.firmware_version =	0, +		.identity =		"RC32434_WDT Watchdog", +	}; +	switch (cmd) { +		default: +			return -ENOTTY; +		case WDIOC_GETSUPPORT: +			if(copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))) +				return -EFAULT; +			return 0; +		case WDIOC_GETSTATUS: +		case WDIOC_GETBOOTSTATUS: +			return put_user(0,(int *)arg); +		case WDIOC_KEEPALIVE: +			update_wdt(); +			return 0; +		case WDIOC_SETTIMEOUT: +			if (get_user(new_timeout, (int *)arg)) +				return -EFAULT; +			if (new_timeout < 1) +				return -EINVAL; +			if (new_timeout > MAX_TIMEOUT) +				return -EINVAL; +			set_wdt(new_timeout); +			/* Fall */ +		case WDIOC_GETTIMEOUT: +			return put_user(timeout, (int *)arg); +	} +} + +static struct file_operations wdt_fops = { +	owner:		THIS_MODULE, +	llseek:		no_llseek, +	write:		wdt_write, +	ioctl:		wdt_ioctl, +	open:		wdt_open, +	release:	wdt_release, +}; + +static struct miscdevice wdt_miscdev = { +	minor:		WATCHDOG_MINOR, +	name:		"watchdog", +	fops:		&wdt_fops, +}; + +static char banner[] __initdata = KERN_INFO NAME ": Watchdog Timer version " VERSION ", timer margin: %d sec\n"; + +static int __init watchdog_init(void) +{ +	int ret; + +	/* +  	 * There should be check for RC32434 SoC +	 */ +	if (mips_machgroup != MACH_GROUP_MIKROTIK) return -1; + +	ret = misc_register(&wdt_miscdev); + +	if (ret) +		return ret; + +	stop_wdt(); +	set_wdt(DEFAULT_TIMEOUT); + +	printk(banner, timeout); + +	return 0; +} + +static void __exit watchdog_exit(void) +{ +	misc_deregister(&wdt_miscdev); +} + +module_init(watchdog_init); +module_exit(watchdog_exit); diff --git a/target/linux/rb532/files-2.6.24/drivers/leds/leds-rb500.c b/target/linux/rb532/files-2.6.24/drivers/leds/leds-rb500.c new file mode 100644 index 000000000..3ab530a75 --- /dev/null +++ b/target/linux/rb532/files-2.6.24/drivers/leds/leds-rb500.c @@ -0,0 +1,81 @@ +/* + * linux/drivers/leds/leds-rb500.c + * + * Copyright (C) 2006 + *     Twente Institute for Wireless and Mobile Communications BV + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details (see file GPLv2). + * + * Author: Tjalling Hattink <tjalling.hattink@ti-wmc.nl> + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/leds.h> +#include <asm/rc32434/rb.h> + +static void rb500led_amber_set(struct led_classdev *led_cdev, enum led_brightness value) +{ +	if (value) +		changeLatchU5(LO_ULED, 0); +	else +		changeLatchU5(0, LO_ULED); +} + +static struct led_classdev rb500_amber_led = { +	.name			= "rb500led:amber", +	.default_trigger	= "ide-disk", +	.brightness_set		= rb500led_amber_set, +}; + +static int rb500led_probe(struct platform_device *pdev) +{ +	int ret; + +	changeLatchU5(0, LO_ULED); + +	ret = led_classdev_register(&pdev->dev, &rb500_amber_led); + +	return ret; +} + +static int rb500led_remove(struct platform_device *pdev) +{ +	led_classdev_unregister(&rb500_amber_led); + +	return 0; +} + +static struct platform_driver rb500led_driver = { +	.probe		= rb500led_probe, +	.remove		= rb500led_remove, +	.driver		= { +		.name		= "rb500-led", +	}, +}; + +static int __init rb500led_init(void) +{ +	return platform_driver_register(&rb500led_driver); +} + +static void __exit rb500led_exit(void) +{ + 	platform_driver_unregister(&rb500led_driver); +} + +module_init(rb500led_init); +module_exit(rb500led_exit); + +MODULE_AUTHOR("tjalling.hattink@ti-wmc.nl"); +MODULE_DESCRIPTION("Mikrotik RB500 LED driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/rb532/files-2.6.24/drivers/net/korina.c b/target/linux/rb532/files-2.6.24/drivers/net/korina.c new file mode 100644 index 000000000..9dee6d4b0 --- /dev/null +++ b/target/linux/rb532/files-2.6.24/drivers/net/korina.c @@ -0,0 +1,1128 @@ +/************************************************************************** + * + *  BRIEF MODULE DESCRIPTION + *     Driver for the IDT RC32434 on-chip ethernet controller. + * + *  Copyright 2004 IDT Inc. (rischelp@idt.com) + *  Copyright 2006 Felix Fietkau <nbd@openwrt.org> + *          + *  This program is free software; you can redistribute  it and/or modify it + *  under  the terms of  the GNU General  Public License as published by the + *  Free Software Foundation;  either version 2 of the  License, or (at your + *  option) any later version. + * + *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED + *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF + *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN + *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT, + *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF + *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT + *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *  You should have received a copy of the  GNU General Public License along + *  with this program; if not, write  to the Free Software Foundation, Inc., + *  675 Mass Ave, Cambridge, MA 02139, USA. + * + * + ************************************************************************** + * May 2004 rkt, neb + * + * Based on the driver developed by B. Maruthanayakam, H. Kou and others. + * + * Aug 2004 Sadik + * + * Added NAPI + * + ************************************************************************** + */ + +#include <linux/autoconf.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/sched.h> +#include <linux/ctype.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/proc_fs.h> +#include <linux/in.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <asm/bootinfo.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/pgtable.h> +#include <asm/segment.h> +#include <asm/io.h> +#include <asm/dma.h> + +#include <asm/rc32434/rb.h> +#include "rc32434_eth.h" + +#define DRIVER_VERSION "(mar2904)" + +#define DRIVER_NAME "rc32434 Ethernet driver. " DRIVER_VERSION + +#define STATION_ADDRESS_HIGH(dev) (((dev)->dev_addr[0] << 8) | \ +			           ((dev)->dev_addr[1])) +#define STATION_ADDRESS_LOW(dev)  (((dev)->dev_addr[2] << 24) | \ +				   ((dev)->dev_addr[3] << 16) | \ +				   ((dev)->dev_addr[4] << 8)  | \ +				   ((dev)->dev_addr[5])) + +#define MII_CLOCK 1250000 				/* no more than 2.5MHz */ +#define CONFIG_IDT_USE_NAPI 1 + + +static inline void rc32434_abort_tx(struct net_device *dev) +{ +	struct rc32434_local *lp = (struct rc32434_local *)dev->priv; +	rc32434_abort_dma(dev, lp->tx_dma_regs); +	 +} + +static inline void rc32434_abort_rx(struct net_device *dev) +{ +	struct rc32434_local *lp = (struct rc32434_local *)dev->priv; +	rc32434_abort_dma(dev, lp->rx_dma_regs); +	 +} + +static inline void rc32434_start_tx(struct rc32434_local *lp,  volatile DMAD_t td) +{ +	rc32434_start_dma(lp->tx_dma_regs, CPHYSADDR(td)); +} + +static inline void rc32434_start_rx(struct rc32434_local *lp, volatile DMAD_t rd) +{ +	rc32434_start_dma(lp->rx_dma_regs, CPHYSADDR(rd)); +} + +static inline void rc32434_chain_tx(struct rc32434_local *lp, volatile DMAD_t td) +{ +	rc32434_chain_dma(lp->tx_dma_regs, CPHYSADDR(td)); +} + +static inline void rc32434_chain_rx(struct rc32434_local *lp, volatile DMAD_t rd) +{ +	rc32434_chain_dma(lp->rx_dma_regs, CPHYSADDR(rd)); +} + +#ifdef RC32434_PROC_DEBUG +static int rc32434_read_proc(char *buf, char **start, off_t fpos, +			     int length, int *eof, void *data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct rc32434_local *lp = (struct rc32434_local *)dev->priv; +	int len = 0; +	 +	/* print out header */ +	len += sprintf(buf + len, "\n\tKorina Ethernet Debug\n\n"); +	len += sprintf (buf + len, +			"DMA halt count      = %10d, DMA run count = %10d\n", +			lp->dma_halt_cnt, lp->dma_run_cnt); +	 +	if (fpos >= len) { +		*start = buf; +		*eof = 1; +		return 0; +	} +	*start = buf + fpos; +	 +	if ((len -= fpos) > length)  +		return length;	 +	*eof = 1; +	 +	return len; +	 +} +#endif + + +/* + * Restart the RC32434 ethernet controller.  + */ +static int rc32434_restart(struct net_device *dev) +{ +	struct rc32434_local *lp = (struct rc32434_local *)dev->priv; +	 +	/* +	 * Disable interrupts +	 */ +	disable_irq(lp->rx_irq); +	disable_irq(lp->tx_irq); +#ifdef	RC32434_REVISION +	disable_irq(lp->ovr_irq); +#endif	 +	disable_irq(lp->und_irq); +	 +	/* Mask F E bit in Tx DMA */ +	__raw_writel(__raw_readl(&lp->tx_dma_regs->dmasm) | DMASM_f_m | DMASM_e_m, &lp->tx_dma_regs->dmasm); +	/* Mask D H E bit in Rx DMA */ +	__raw_writel(__raw_readl(&lp->rx_dma_regs->dmasm) | DMASM_d_m | DMASM_h_m | DMASM_e_m, &lp->rx_dma_regs->dmasm); +	 +	rc32434_init(dev); +	rc32434_multicast_list(dev); +	 +	enable_irq(lp->und_irq); +#ifdef	RC32434_REVISION +	enable_irq(lp->ovr_irq); +#endif +	enable_irq(lp->tx_irq); +	enable_irq(lp->rx_irq); +	 +	return 0; +} + +static int rc32434_probe(struct platform_device *pdev) +{ +	struct korina_device *bif = (struct korina_device *) pdev->dev.platform_data; +	struct rc32434_local *lp = NULL; +	struct net_device *dev = NULL; +	struct resource *r; +	int i, retval,err; +	 +	dev = alloc_etherdev(sizeof(struct rc32434_local)); +	if(!dev) { +		ERR("Korina_eth: alloc_etherdev failed\n"); +		return -1; +	} + +	platform_set_drvdata(pdev, dev); +	SET_NETDEV_DEV(dev, &pdev->dev); +	bif->dev = dev; +	 +	memcpy(dev->dev_addr, bif->mac, 6); + +	/* Initialize the device structure. */ +	if (dev->priv == NULL) { +		lp = (struct rc32434_local *)kmalloc(sizeof(*lp), GFP_KERNEL); +		memset(lp, 0, sizeof(struct rc32434_local)); +	}  +	else { +		lp = (struct rc32434_local *)dev->priv; +	} +	 +	lp->rx_irq = platform_get_irq_byname(pdev, "korina_rx"); +	lp->tx_irq = platform_get_irq_byname(pdev, "korina_tx"); +	lp->ovr_irq = platform_get_irq_byname(pdev, "korina_ovr"); +	lp->und_irq = platform_get_irq_byname(pdev, "korina_und"); + +	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "korina_regs"); +	dev->base_addr = r->start; +	lp->eth_regs = ioremap_nocache(r->start, r->end - r->start); +	if (!lp->eth_regs) { +		ERR("Can't remap eth registers\n"); +		retval = -ENXIO; +		goto probe_err_out; +	} + +	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "korina_dma_rx"); +	lp->rx_dma_regs = ioremap_nocache(r->start, r->end - r->start); +	if (!lp->rx_dma_regs) { +		ERR("Can't remap Rx DMA registers\n"); +		retval = -ENXIO; +		goto probe_err_out; +	} + +	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "korina_dma_tx"); +	lp->tx_dma_regs = ioremap_nocache(r->start, r->end - r->start); +	if (!lp->tx_dma_regs) { +		ERR("Can't remap Tx DMA registers\n"); +		retval = -ENXIO; +		goto probe_err_out; +	} +	 +#ifdef RC32434_PROC_DEBUG +	lp->ps = create_proc_read_entry (bif->name, 0, proc_net, +					 rc32434_read_proc, dev); +#endif +	 +	lp->td_ring =	(DMAD_t)kmalloc(TD_RING_SIZE + RD_RING_SIZE, GFP_KERNEL); +	if (!lp->td_ring) { +		ERR("Can't allocate descriptors\n"); +		retval = -ENOMEM; +		goto probe_err_out; +	} +	 +	dma_cache_inv((unsigned long)(lp->td_ring), TD_RING_SIZE + RD_RING_SIZE); +	 +	/* now convert TD_RING pointer to KSEG1 */ +	lp->td_ring = (DMAD_t )KSEG1ADDR(lp->td_ring); +	lp->rd_ring = &lp->td_ring[RC32434_NUM_TDS]; +	 +	 +	spin_lock_init(&lp->lock); +	 +	/* just use the rx dma irq */ +	dev->irq = lp->rx_irq; +	 +	dev->priv = lp; +	lp->dev = dev; +	 +	dev->open = rc32434_open; +	dev->stop = rc32434_close; +	dev->hard_start_xmit = rc32434_send_packet; +	dev->get_stats	= rc32434_get_stats; +	dev->set_multicast_list = &rc32434_multicast_list; +	dev->tx_timeout = rc32434_tx_timeout; +	dev->watchdog_timeo = RC32434_TX_TIMEOUT; + +	netif_napi_add(dev, &lp->napi, rc32434_poll, 64); +	lp->tx_tasklet = kmalloc(sizeof(struct tasklet_struct), GFP_KERNEL); +	tasklet_init(lp->tx_tasklet, rc32434_tx_tasklet, (unsigned long)dev); +	 +	if ((err = register_netdev(dev))) { +		printk(KERN_ERR "rc32434 ethernet. Cannot register net device %d\n", err); +		free_netdev(dev); +		retval = -EINVAL; +		goto probe_err_out; +	} +	 +	INFO("Rx IRQ %d, Tx IRQ %d, ", lp->rx_irq, lp->tx_irq); +	for (i = 0; i < 6; i++) { +		printk("%2.2x", dev->dev_addr[i]); +		if (i<5) +			printk(":"); +	} +	printk("\n"); +	 +	return 0; +	 + probe_err_out: +	rc32434_cleanup_module(); +	ERR(" failed.  Returns %d\n", retval); +	return retval; +	 +} + +static int rc32434_remove(struct platform_device *pdev) +{ +	struct korina_device *bif = (struct korina_device *) pdev->dev.platform_data; +	 +	if (bif->dev != NULL) { +		struct rc32434_local *lp = (struct rc32434_local *)bif->dev->priv; +		if (lp != NULL) { +			if (lp->eth_regs) +				iounmap((void*)lp->eth_regs); +			if (lp->rx_dma_regs) +				iounmap((void*)lp->rx_dma_regs); +			if (lp->tx_dma_regs) +				iounmap((void*)lp->tx_dma_regs); +			if (lp->td_ring) +				kfree((void*)KSEG0ADDR(lp->td_ring)); +			 +#ifdef RC32434_PROC_DEBUG +			if (lp->ps) { +				remove_proc_entry(bif->name, proc_net); +			} +#endif +			kfree(lp); +		} +		 +		platform_set_drvdata(pdev, NULL); +		unregister_netdev(bif->dev); +		free_netdev(bif->dev); +		kfree(bif->dev); +	} +	return 0; +} + + +static int rc32434_open(struct net_device *dev) +{ +	struct rc32434_local *lp = (struct rc32434_local *)dev->priv; +	 +	/* Initialize */ +	if (rc32434_init(dev)) { +		ERR("Error: cannot open the Ethernet device\n"); +		return -EAGAIN; +	} +	 +	/* Install the interrupt handler that handles the Done Finished Ovr and Und Events */	 +	if (request_irq(lp->rx_irq, &rc32434_rx_dma_interrupt, +		  IRQF_SHARED | IRQF_DISABLED, +			"Korina ethernet Rx", dev)) { +		ERR(": unable to get Rx DMA IRQ %d\n", +		    lp->rx_irq); +		return -EAGAIN; +	} +	if (request_irq(lp->tx_irq, &rc32434_tx_dma_interrupt, +		  IRQF_SHARED | IRQF_DISABLED, +			"Korina ethernet Tx", dev)) { +		ERR(": unable to get Tx DMA IRQ %d\n", +		    lp->tx_irq); +		free_irq(lp->rx_irq, dev); +		return -EAGAIN; +	} +	 +#ifdef	RC32434_REVISION +	/* Install handler for overrun error. */ +	if (request_irq(lp->ovr_irq, &rc32434_ovr_interrupt, +			IRQF_SHARED | IRQF_DISABLED, +			"Ethernet Overflow", dev)) { +		ERR(": unable to get OVR IRQ %d\n", +		    lp->ovr_irq); +		free_irq(lp->rx_irq, dev); +		free_irq(lp->tx_irq, dev); +		return -EAGAIN; +	} +#endif +	 +	/* Install handler for underflow error. */ +	if (request_irq(lp->und_irq, &rc32434_und_interrupt, +			IRQF_SHARED | IRQF_DISABLED, +			"Ethernet Underflow", dev)) { +		ERR(": unable to get UND IRQ %d\n", +		    lp->und_irq); +		free_irq(lp->rx_irq, dev); +		free_irq(lp->tx_irq, dev); +#ifdef	RC32434_REVISION		 +		free_irq(lp->ovr_irq, dev);		 +#endif +		return -EAGAIN; +	} +	 +	 +	return 0; +} + + + + +static int rc32434_close(struct net_device *dev) +{ +	struct rc32434_local *lp = (struct rc32434_local *)dev->priv; +	u32 tmp; +	 +	/* Disable interrupts */ +	disable_irq(lp->rx_irq); +	disable_irq(lp->tx_irq); +#ifdef	RC32434_REVISION +	disable_irq(lp->ovr_irq); +#endif +	disable_irq(lp->und_irq); +	 +	tmp = __raw_readl(&lp->tx_dma_regs->dmasm); +	tmp = tmp | DMASM_f_m | DMASM_e_m; +	__raw_writel(tmp, &lp->tx_dma_regs->dmasm); +	 +	tmp = __raw_readl(&lp->rx_dma_regs->dmasm); +	tmp = tmp | DMASM_d_m | DMASM_h_m | DMASM_e_m; +	__raw_writel(tmp, &lp->rx_dma_regs->dmasm); +	 +	free_irq(lp->rx_irq, dev); +	free_irq(lp->tx_irq, dev); +#ifdef	RC32434_REVISION	 +	free_irq(lp->ovr_irq, dev); +#endif +	free_irq(lp->und_irq, dev); +	return 0; +} + + +/* transmit packet */ +static int rc32434_send_packet(struct sk_buff *skb, struct net_device *dev) +{ +	struct rc32434_local		*lp = (struct rc32434_local *)dev->priv; +	unsigned long 			flags; +	u32					length; +	DMAD_t				td; +	 +	 +	spin_lock_irqsave(&lp->lock, flags); +	 +	td = &lp->td_ring[lp->tx_chain_tail]; +	 +	/* stop queue when full, drop pkts if queue already full */ +	if(lp->tx_count >= (RC32434_NUM_TDS - 2)) { +		lp->tx_full = 1; +		 +		if(lp->tx_count == (RC32434_NUM_TDS - 2)) { +			netif_stop_queue(dev); +		} +		else { +			lp->stats.tx_dropped++; +			dev_kfree_skb_any(skb); +			spin_unlock_irqrestore(&lp->lock, flags); +			return 1; +		}	    +	}	  +	 +	lp->tx_count ++; +	 +	lp->tx_skb[lp->tx_chain_tail] = skb; +	 +	length = skb->len; +	dma_cache_wback((u32)skb->data, skb->len); +	 +	/* Setup the transmit descriptor. */ +	dma_cache_inv((u32) td, sizeof(*td)); +	td->ca = CPHYSADDR(skb->data); +	 +	if(__raw_readl(&(lp->tx_dma_regs->dmandptr)) == 0) { +		if( lp->tx_chain_status == empty ) { +			td->control = DMA_COUNT(length) |DMAD_cof_m |DMAD_iof_m;                                /*  Update tail      */ +			lp->tx_chain_tail = (lp->tx_chain_tail + 1) & RC32434_TDS_MASK;                          /*   Move tail       */ +			__raw_writel(CPHYSADDR(&lp->td_ring[lp->tx_chain_head]), &(lp->tx_dma_regs->dmandptr)); /* Write to NDPTR    */ +			lp->tx_chain_head = lp->tx_chain_tail;                                                  /* Move head to tail */ +		} +		else { +			td->control = DMA_COUNT(length) |DMAD_cof_m|DMAD_iof_m;                                 /* Update tail */ +			lp->td_ring[(lp->tx_chain_tail-1)& RC32434_TDS_MASK].control &=  ~(DMAD_cof_m);          /* Link to prev */ +			lp->td_ring[(lp->tx_chain_tail-1)& RC32434_TDS_MASK].link =  CPHYSADDR(td);              /* Link to prev */ +			lp->tx_chain_tail = (lp->tx_chain_tail + 1) & RC32434_TDS_MASK;                          /* Move tail */ +			__raw_writel(CPHYSADDR(&lp->td_ring[lp->tx_chain_head]), &(lp->tx_dma_regs->dmandptr)); /* Write to NDPTR */ +			lp->tx_chain_head = lp->tx_chain_tail;                                                  /* Move head to tail */ +			lp->tx_chain_status = empty; +		} +	} +	else { +		if( lp->tx_chain_status == empty ) { +			td->control = DMA_COUNT(length) |DMAD_cof_m |DMAD_iof_m;                                /* Update tail */ +			lp->tx_chain_tail = (lp->tx_chain_tail + 1) & RC32434_TDS_MASK;                          /* Move tail */ +			lp->tx_chain_status = filled; +		} +		else { +			td->control = DMA_COUNT(length) |DMAD_cof_m |DMAD_iof_m;                                /* Update tail */ +			lp->td_ring[(lp->tx_chain_tail-1)& RC32434_TDS_MASK].control &=  ~(DMAD_cof_m);          /* Link to prev */ +			lp->td_ring[(lp->tx_chain_tail-1)& RC32434_TDS_MASK].link =  CPHYSADDR(td);              /* Link to prev */ +			lp->tx_chain_tail = (lp->tx_chain_tail + 1) & RC32434_TDS_MASK;                          /* Move tail */ +		} +	} +	dma_cache_wback((u32) td, sizeof(*td)); +	 +	dev->trans_start = jiffies;				 +	 +	spin_unlock_irqrestore(&lp->lock, flags); +	 +	return 0; +} + + +/* Ethernet MII-PHY Handler */ +static void rc32434_mii_handler(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data;		 +	struct rc32434_local *lp = (struct rc32434_local *)dev->priv; +	unsigned long 	flags; +	unsigned long duplex_status; +	int port_addr = (lp->rx_irq == 0x2c? 1:0) << 8; +	 +	spin_lock_irqsave(&lp->lock, flags); +	 +	/* Two ports are using the same MII, the difference is the PHY address */ +	__raw_writel(0, &rc32434_eth0_regs->miimcfg);   +	__raw_writel(0, &rc32434_eth0_regs->miimcmd);   +	__raw_writel(port_addr |0x05, &rc32434_eth0_regs->miimaddr);   +	__raw_writel(MIIMCMD_scn_m, &rc32434_eth0_regs->miimcmd);   +	while(__raw_readl(&rc32434_eth0_regs->miimind) & MIIMIND_nv_m); +	 +	ERR("irq:%x		port_addr:%x	RDD:%x\n",  +	    lp->rx_irq, port_addr, __raw_readl(&rc32434_eth0_regs->miimrdd)); +	duplex_status = (__raw_readl(&rc32434_eth0_regs->miimrdd) & 0x140)? ETHMAC2_fd_m: 0; +	if(duplex_status != lp->duplex_mode) { +		ERR("The MII-PHY is Auto-negotiated to %s-Duplex mode for Eth-%x\n", duplex_status? "Full":"Half", lp->rx_irq == 0x2c? 1:0);		 +		lp->duplex_mode = duplex_status; +		rc32434_restart(dev);		 +	} +	 +	lp->mii_phy_timer.expires = jiffies + 10 * HZ;	 +	add_timer(&lp->mii_phy_timer); +	 +	spin_unlock_irqrestore(&lp->lock, flags); +	 +} + +#ifdef	RC32434_REVISION	 +/* Ethernet Rx Overflow interrupt */ +static irqreturn_t +rc32434_ovr_interrupt(int irq, void *dev_id) +{ +	struct net_device *dev = (struct net_device *)dev_id; +	struct rc32434_local *lp; +	unsigned int ovr; +	irqreturn_t retval = IRQ_NONE; +	 +	ASSERT(dev != NULL); +	 +	lp = (struct rc32434_local *)dev->priv; +	spin_lock(&lp->lock); +	ovr = __raw_readl(&lp->eth_regs->ethintfc); +	 +	if(ovr & ETHINTFC_ovr_m) { +		netif_stop_queue(dev); +		 +		/* clear OVR bit */ +		__raw_writel((ovr & ~ETHINTFC_ovr_m), &lp->eth_regs->ethintfc); +		 +		/* Restart interface */ +		rc32434_restart(dev); +		retval = IRQ_HANDLED; +	} +	spin_unlock(&lp->lock); +	 +	return retval; +} + +#endif + + +/* Ethernet Tx Underflow interrupt */ +static irqreturn_t +rc32434_und_interrupt(int irq, void *dev_id) +{ +	struct net_device *dev = (struct net_device *)dev_id; +	struct rc32434_local *lp; +	unsigned int und; +	irqreturn_t retval = IRQ_NONE; +	 +	ASSERT(dev != NULL); +	 +	lp = (struct rc32434_local *)dev->priv; +	 +	spin_lock(&lp->lock); +	 +	und = __raw_readl(&lp->eth_regs->ethintfc); +	 +	if(und & ETHINTFC_und_m) { +		netif_stop_queue(dev); +		 +		__raw_writel((und & ~ETHINTFC_und_m), &lp->eth_regs->ethintfc); +		 +		/* Restart interface */ +		rc32434_restart(dev); +		retval = IRQ_HANDLED; +	} +	 +	spin_unlock(&lp->lock); +	 +	return retval; +} + + +/* Ethernet Rx DMA interrupt */ +static irqreturn_t +rc32434_rx_dma_interrupt(int irq, void *dev_id) +{ +	struct net_device *dev = (struct net_device *)dev_id; +	struct rc32434_local* lp; +	volatile u32 dmas,dmasm; +	irqreturn_t retval; +	 +	ASSERT(dev != NULL); +	 +	lp = (struct rc32434_local *)dev->priv; +	 +	spin_lock(&lp->lock); +	dmas = __raw_readl(&lp->rx_dma_regs->dmas); +	if(dmas & (DMAS_d_m|DMAS_h_m|DMAS_e_m)) { +		/* Mask D H E bit in Rx DMA */ +		dmasm = __raw_readl(&lp->rx_dma_regs->dmasm); +		__raw_writel(dmasm | (DMASM_d_m | DMASM_h_m | DMASM_e_m), &lp->rx_dma_regs->dmasm); + +		netif_rx_schedule_prep(dev, &lp->napi); +		 +		if (dmas & DMAS_e_m) +			ERR(": DMA error\n"); +		 +		retval = IRQ_HANDLED; +	} +	else +		retval = IRQ_NONE; +	 +	spin_unlock(&lp->lock); +	return retval; +} + +static int rc32434_poll(struct napi_struct *napi, int budget) +{ +	struct rc32434_local* lp = container_of(napi, struct rc32434_local, napi); +	struct net_device *dev = lp->dev; +	volatile DMAD_t  rd = &lp->rd_ring[lp->rx_next_done]; +	struct sk_buff *skb, *skb_new; +	u8* pkt_buf; +	u32 devcs, count, pkt_len, pktuncrc_len; +	volatile u32 dmas; +	u32 received = 0; +	int rx_work_limit = min(budget, 64); + +	dma_cache_inv((u32)rd, sizeof(*rd)); +	while ( (count = RC32434_RBSIZE - (u32)DMA_COUNT(rd->control)) != 0) { +		if(--rx_work_limit <0) +                { +                        break; +                } +		/* init the var. used for the later operations within the while loop */ +		skb_new = NULL; +		devcs = rd->devcs; +		pkt_len = RCVPKT_LENGTH(devcs); +		skb = lp->rx_skb[lp->rx_next_done]; +       +		if (count < 64) { +			lp->stats.rx_errors++; +			lp->stats.rx_dropped++;			 +		} +		else if ((devcs & ( ETHRX_ld_m)) !=	ETHRX_ld_m) { +			/* check that this is a whole packet */ +			/* WARNING: DMA_FD bit incorrectly set in Rc32434 (errata ref #077) */ +			lp->stats.rx_errors++; +			lp->stats.rx_dropped++; +		} +		else if ( (devcs & ETHRX_rok_m)  ) { +			 +			{ +				/* must be the (first and) last descriptor then */ +				pkt_buf = (u8*)lp->rx_skb[lp->rx_next_done]->data; +				 +				pktuncrc_len = pkt_len - 4; +				/* invalidate the cache */ +				dma_cache_inv((unsigned long)pkt_buf, pktuncrc_len); +				 +				/* Malloc up new buffer. */					   +				skb_new = dev_alloc_skb(RC32434_RBSIZE + 2);					             	 +				 +				if (skb_new != NULL){ +					/* Make room */ +					skb_put(skb, pktuncrc_len);		     +					 +					skb->protocol = eth_type_trans(skb, dev); +					 +					/* pass the packet to upper layers */ +					netif_receive_skb(skb); +					 +					dev->last_rx = jiffies; +					lp->stats.rx_packets++; +					lp->stats.rx_bytes += pktuncrc_len; +					 +					if (IS_RCV_MP(devcs)) +						lp->stats.multicast++; +					 +					/* 16 bit align */						   +					skb_reserve(skb_new, 2);	 +					 +					skb_new->dev = dev; +					lp->rx_skb[lp->rx_next_done] = skb_new; +				} +				else { +					ERR("no memory, dropping rx packet.\n"); +					lp->stats.rx_errors++;		 +					lp->stats.rx_dropped++;					 +				} +			} +			 +		}			 +		else { +			/* This should only happen if we enable accepting broken packets */ +			lp->stats.rx_errors++; +			lp->stats.rx_dropped++; +			 +			/* add statistics counters */ +			if (IS_RCV_CRC_ERR(devcs)) { +				DBG(2, "RX CRC error\n"); +				lp->stats.rx_crc_errors++; +			}  +			else if (IS_RCV_LOR_ERR(devcs)) { +				DBG(2, "RX LOR error\n"); +				lp->stats.rx_length_errors++; +			}				 +			else if (IS_RCV_LE_ERR(devcs)) { +				DBG(2, "RX LE error\n"); +				lp->stats.rx_length_errors++; +			} +			else if (IS_RCV_OVR_ERR(devcs)) { +				lp->stats.rx_over_errors++; +			} +			else if (IS_RCV_CV_ERR(devcs)) { +				/* code violation */ +				DBG(2, "RX CV error\n"); +				lp->stats.rx_frame_errors++; +			} +			else if (IS_RCV_CES_ERR(devcs)) { +				DBG(2, "RX Preamble error\n"); +			} +		} +		 +		rd->devcs = 0; +		 +		/* restore descriptor's curr_addr */ +		if(skb_new) +			rd->ca = CPHYSADDR(skb_new->data);  +		else +			rd->ca = CPHYSADDR(skb->data); +		 +		rd->control = DMA_COUNT(RC32434_RBSIZE) |DMAD_cod_m |DMAD_iod_m; +		lp->rd_ring[(lp->rx_next_done-1)& RC32434_RDS_MASK].control &=  ~(DMAD_cod_m); 	 +		 +		lp->rx_next_done = (lp->rx_next_done + 1) & RC32434_RDS_MASK; +		dma_cache_wback((u32)rd, sizeof(*rd)); +		rd = &lp->rd_ring[lp->rx_next_done]; +		__raw_writel( ~DMAS_d_m, &lp->rx_dma_regs->dmas); +	}	 +        budget =- received; +        if(rx_work_limit < 0) +                goto not_done; +	 +	dmas = __raw_readl(&lp->rx_dma_regs->dmas); +	 +	if(dmas & DMAS_h_m) { +		__raw_writel( ~(DMAS_h_m | DMAS_e_m), &lp->rx_dma_regs->dmas); +#ifdef RC32434_PROC_DEBUG +		lp->dma_halt_cnt++; +#endif +		rd->devcs = 0; +		skb = lp->rx_skb[lp->rx_next_done]; +		rd->ca = CPHYSADDR(skb->data); +		dma_cache_wback((u32)rd, sizeof(*rd)); +		rc32434_chain_rx(lp,rd); +	} +	 +	netif_rx_complete(dev, &lp->napi); +	/* Enable D H E bit in Rx DMA */ +	__raw_writel(__raw_readl(&lp->rx_dma_regs->dmasm) & ~(DMASM_d_m | DMASM_h_m |DMASM_e_m), &lp->rx_dma_regs->dmasm);  +	return 0; + not_done: +	return 1; +}	 + + + +/* Ethernet Tx DMA interrupt */ +static irqreturn_t +rc32434_tx_dma_interrupt(int irq, void *dev_id) +{ +	struct net_device *dev = (struct net_device *)dev_id; +	struct rc32434_local *lp; +	volatile u32 dmas,dmasm; +	irqreturn_t retval; +	 +	ASSERT(dev != NULL); +	 +	lp = (struct rc32434_local *)dev->priv; +	 +	spin_lock(&lp->lock); +	 +	dmas = __raw_readl(&lp->tx_dma_regs->dmas); +	 +	if (dmas & (DMAS_f_m | DMAS_e_m)) { +		dmasm = __raw_readl(&lp->tx_dma_regs->dmasm); +		/* Mask F E bit in Tx DMA */ +		__raw_writel(dmasm | (DMASM_f_m | DMASM_e_m), &lp->tx_dma_regs->dmasm); +		 +		tasklet_hi_schedule(lp->tx_tasklet); +		 +		if(lp->tx_chain_status == filled && (__raw_readl(&(lp->tx_dma_regs->dmandptr)) == 0)) { +			__raw_writel(CPHYSADDR(&lp->td_ring[lp->tx_chain_head]), &(lp->tx_dma_regs->dmandptr));			 +			lp->tx_chain_status = empty; +			lp->tx_chain_head = lp->tx_chain_tail; +			dev->trans_start = jiffies; +		} +		 +		if (dmas & DMAS_e_m) +			ERR(": DMA error\n"); +		 +		retval = IRQ_HANDLED; +	} +	else +		retval = IRQ_NONE; +	 +	spin_unlock(&lp->lock); +	 +	return retval; +} + + +static void rc32434_tx_tasklet(unsigned long tx_data_dev) +{ +	struct net_device *dev = (struct net_device *)tx_data_dev;	 +	struct rc32434_local* lp = (struct rc32434_local *)dev->priv; +	volatile DMAD_t td = &lp->td_ring[lp->tx_next_done]; +	u32 devcs; +	unsigned long 	flags; +	volatile u32 dmas; +	 +	spin_lock_irqsave(&lp->lock, flags); +	 +	/* process all desc that are done */ +	while(IS_DMA_FINISHED(td->control)) { +		if(lp->tx_full == 1) { +			netif_wake_queue(dev); +			lp->tx_full = 0; +		} +		 +		devcs = lp->td_ring[lp->tx_next_done].devcs;     +		if ((devcs & (ETHTX_fd_m | ETHTX_ld_m)) != (ETHTX_fd_m | ETHTX_ld_m)) { +			lp->stats.tx_errors++; +			lp->stats.tx_dropped++;				 +			 +			/* should never happen */ +			DBG(1, __FUNCTION__ ": split tx ignored\n"); +		} +		else if (IS_TX_TOK(devcs)) { +			lp->stats.tx_packets++; +			lp->stats.tx_bytes+=lp->tx_skb[lp->tx_next_done]->len; +		} +		else { +			lp->stats.tx_errors++; +			lp->stats.tx_dropped++;				 +			 +			/* underflow */ +			if (IS_TX_UND_ERR(devcs))  +				lp->stats.tx_fifo_errors++; +			 +			/* oversized frame */ +			if (IS_TX_OF_ERR(devcs)) +				lp->stats.tx_aborted_errors++; +			 +			/* excessive deferrals */ +			if (IS_TX_ED_ERR(devcs)) +				lp->stats.tx_carrier_errors++; +			 +			/* collisions: medium busy */ +			if (IS_TX_EC_ERR(devcs)) +				lp->stats.collisions++; +			 +			/* late collision */ +			if (IS_TX_LC_ERR(devcs)) +				lp->stats.tx_window_errors++; +			 +		} +		 +		/* We must always free the original skb */ +		if (lp->tx_skb[lp->tx_next_done] != NULL) { +			dev_kfree_skb_any(lp->tx_skb[lp->tx_next_done]); +			lp->tx_skb[lp->tx_next_done] = NULL; +		} +		 +		lp->td_ring[lp->tx_next_done].control = DMAD_iof_m; +		lp->td_ring[lp->tx_next_done].devcs = ETHTX_fd_m | ETHTX_ld_m;	 +		lp->td_ring[lp->tx_next_done].link = 0; +		lp->td_ring[lp->tx_next_done].ca = 0; +		lp->tx_count --; +		 +		/* go on to next transmission */ +		lp->tx_next_done = (lp->tx_next_done + 1) & RC32434_TDS_MASK; +		td = &lp->td_ring[lp->tx_next_done]; +		 +	} +	 +	dmas = __raw_readl(&lp->tx_dma_regs->dmas); +	__raw_writel( ~dmas, &lp->tx_dma_regs->dmas); +	 +	/* Enable F E bit in Tx DMA */ +	__raw_writel(__raw_readl(&lp->tx_dma_regs->dmasm) & ~(DMASM_f_m | DMASM_e_m), &lp->tx_dma_regs->dmasm);  +	spin_unlock_irqrestore(&lp->lock, flags); +	 +} + + +static struct net_device_stats * rc32434_get_stats(struct net_device *dev) +{ +	struct rc32434_local *lp = (struct rc32434_local *)dev->priv; +	return &lp->stats; +} + + +/* + * Set or clear the multicast filter for this adaptor. + */ +static void rc32434_multicast_list(struct net_device *dev) +{    +	/* listen to broadcasts always and to treat 	*/ +	/*       IFF bits independantly	*/ +	struct rc32434_local *lp = (struct rc32434_local *)dev->priv; +	unsigned long flags; +	u32 recognise = ETHARC_ab_m; 		/* always accept broadcasts */ +	 +	if (dev->flags & IFF_PROMISC)         		/* set promiscuous mode */ +		recognise |= ETHARC_pro_m; +	 +	if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 15)) +		recognise |= ETHARC_am_m;    	  	/* all multicast & bcast */ +	else if (dev->mc_count > 0) { +		DBG(2, __FUNCTION__ ": mc_count %d\n", dev->mc_count); +		recognise |= ETHARC_am_m;    	  	/* for the time being */ +	} +	 +	spin_lock_irqsave(&lp->lock, flags); +	__raw_writel(recognise, &lp->eth_regs->etharc); +	spin_unlock_irqrestore(&lp->lock, flags); +} + + +static void rc32434_tx_timeout(struct net_device *dev) +{ +	struct rc32434_local *lp = (struct rc32434_local *)dev->priv; +	unsigned long flags; +	 +	spin_lock_irqsave(&lp->lock, flags); +	rc32434_restart(dev); +	spin_unlock_irqrestore(&lp->lock, flags); +	 +} + + +/* + * Initialize the RC32434 ethernet controller. + */ +static int rc32434_init(struct net_device *dev) +{ +	struct rc32434_local *lp = (struct rc32434_local *)dev->priv; +	int i, j; +	 +	/* Disable DMA */        +	rc32434_abort_tx(dev); +	rc32434_abort_rx(dev);  +	 +	/* reset ethernet logic */  +	__raw_writel(0, &lp->eth_regs->ethintfc); +	while((__raw_readl(&lp->eth_regs->ethintfc) & ETHINTFC_rip_m)) +		dev->trans_start = jiffies;	 +	 +	/* Enable Ethernet Interface */  +	__raw_writel(ETHINTFC_en_m, &lp->eth_regs->ethintfc);  +	 +#ifndef CONFIG_IDT_USE_NAPI +	tasklet_disable(lp->rx_tasklet); +#endif +	tasklet_disable(lp->tx_tasklet); +	 +	/* Initialize the transmit Descriptors */ +	for (i = 0; i < RC32434_NUM_TDS; i++) { +		lp->td_ring[i].control = DMAD_iof_m; +		lp->td_ring[i].devcs = ETHTX_fd_m | ETHTX_ld_m; +		lp->td_ring[i].ca = 0; +		lp->td_ring[i].link = 0; +		if (lp->tx_skb[i] != NULL) { +			dev_kfree_skb_any(lp->tx_skb[i]); +			lp->tx_skb[i] = NULL; +		} +	} +	lp->tx_next_done = lp->tx_chain_head = lp->tx_chain_tail = 	lp->tx_full = lp->tx_count = 0; +	lp->	tx_chain_status = empty; +	 +	/* +	 * Initialize the receive descriptors so that they +	 * become a circular linked list, ie. let the last +	 * descriptor point to the first again. +	 */ +	for (i=0; i<RC32434_NUM_RDS; i++) { +		struct sk_buff *skb = lp->rx_skb[i]; +		 +		if (lp->rx_skb[i] == NULL) { +			skb = dev_alloc_skb(RC32434_RBSIZE + 2); +			if (skb == NULL) { +				ERR("No memory in the system\n"); +				for (j = 0; j < RC32434_NUM_RDS; j ++) +					if (lp->rx_skb[j] != NULL)  +						dev_kfree_skb_any(lp->rx_skb[j]); +				 +				return 1; +			} +			else { +				skb->dev = dev; +				skb_reserve(skb, 2); +				lp->rx_skb[i] = skb; +				lp->rd_ring[i].ca = CPHYSADDR(skb->data);  +				 +			} +		} +		lp->rd_ring[i].control =	DMAD_iod_m | DMA_COUNT(RC32434_RBSIZE); +		lp->rd_ring[i].devcs = 0; +		lp->rd_ring[i].ca = CPHYSADDR(skb->data); +		lp->rd_ring[i].link = CPHYSADDR(&lp->rd_ring[i+1]); +		 +	} +	/* loop back */ +	lp->rd_ring[RC32434_NUM_RDS-1].link = CPHYSADDR(&lp->rd_ring[0]); +	lp->rx_next_done   = 0; +	 +	lp->rd_ring[RC32434_NUM_RDS-1].control |= DMAD_cod_m; +	lp->rx_chain_head = 0; +	lp->rx_chain_tail = 0; +	lp->rx_chain_status = empty; +	 +	__raw_writel(0, &lp->rx_dma_regs->dmas); +	/* Start Rx DMA */ +	rc32434_start_rx(lp, &lp->rd_ring[0]); +	 +	/* Enable F E bit in Tx DMA */ +	__raw_writel(__raw_readl(&lp->tx_dma_regs->dmasm) & ~(DMASM_f_m | DMASM_e_m), &lp->tx_dma_regs->dmasm);  +	/* Enable D H E bit in Rx DMA */ +	__raw_writel(__raw_readl(&lp->rx_dma_regs->dmasm) & ~(DMASM_d_m | DMASM_h_m | DMASM_e_m), &lp->rx_dma_regs->dmasm);  +	 +	/* Accept only packets destined for this Ethernet device address */ +	__raw_writel(ETHARC_ab_m, &lp->eth_regs->etharc);  +	 +	/* Set all Ether station address registers to their initial values */  +	__raw_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal0);  +	__raw_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah0); +	 +	__raw_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal1);  +	__raw_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah1); +	 +	__raw_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal2);  +	__raw_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah2); +	 +	__raw_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal3);  +	__raw_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah3);  +	 +	 +	/* Frame Length Checking, Pad Enable, CRC Enable, Full Duplex set */  +	__raw_writel(ETHMAC2_pe_m | ETHMAC2_cen_m | ETHMAC2_fd_m, &lp->eth_regs->ethmac2);   +	//ETHMAC2_flc_m		ETHMAC2_fd_m	lp->duplex_mode +	 +	/* Back to back inter-packet-gap */  +	__raw_writel(0x15, &lp->eth_regs->ethipgt);  +	/* Non - Back to back inter-packet-gap */  +	__raw_writel(0x12, &lp->eth_regs->ethipgr);  +	 +	/* Management Clock Prescaler Divisor */ +	/* Clock independent setting */ +	__raw_writel(((idt_cpu_freq)/MII_CLOCK+1) & ~1, +		       &lp->eth_regs->ethmcp); +	 +	/* don't transmit until fifo contains 48b */ +	__raw_writel(48, &lp->eth_regs->ethfifott); +	 +	__raw_writel(ETHMAC1_re_m, &lp->eth_regs->ethmac1); +	 +	napi_enable(&lp->napi); +	tasklet_enable(lp->tx_tasklet); +	 +	netif_start_queue(dev); +	 +	return 0;  +} + +static struct platform_driver korina_driver = { +	.driver.name = "korina", +	.probe = rc32434_probe, +	.remove = rc32434_remove, +}; + +static int __init rc32434_init_module(void) +{ +	return platform_driver_register(&korina_driver); +} + +static void rc32434_cleanup_module(void) +{ +	return platform_driver_unregister(&korina_driver); +} + +module_init(rc32434_init_module); +module_exit(rc32434_cleanup_module); diff --git a/target/linux/rb532/files-2.6.24/drivers/net/rc32434_eth.h b/target/linux/rb532/files-2.6.24/drivers/net/rc32434_eth.h new file mode 100644 index 000000000..5ac6dc354 --- /dev/null +++ b/target/linux/rb532/files-2.6.24/drivers/net/rc32434_eth.h @@ -0,0 +1,173 @@ +/************************************************************************** + * + *  BRIEF MODULE DESCRIPTION + *     Definitions for IDT RC32434 on-chip ethernet controller. + * + *  Copyright 2004 IDT Inc. (rischelp@idt.com) + *          + *  This program is free software; you can redistribute  it and/or modify it + *  under  the terms of  the GNU General  Public License as published by the + *  Free Software Foundation;  either version 2 of the  License, or (at your + *  option) any later version. + * + *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED + *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF + *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN + *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT, + *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF + *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT + *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *  You should have received a copy of the  GNU General Public License along + *  with this program; if not, write  to the Free Software Foundation, Inc., + *  675 Mass Ave, Cambridge, MA 02139, USA. + * + * + ************************************************************************** + * May 2004 rkt, neb + * + * Initial Release + * + * Aug 2004 + * + * Added NAPI + * + ************************************************************************** + */ + + +#include  <asm/rc32434/rc32434.h> +#include  <asm/rc32434/dma_v.h> +#include  <asm/rc32434/eth_v.h> + +#define CONFIG_IDT_USE_NAPI 1 +#define RC32434_DEBUG	2 +//#define RC32434_PROC_DEBUG +#undef	RC32434_DEBUG + +#ifdef RC32434_DEBUG + +/* use 0 for production, 1 for verification, >2 for debug */ +static int rc32434_debug = RC32434_DEBUG; +#define ASSERT(expr) \ +	if(!(expr)) {	\ +		printk( "Assertion failed! %s,%s,%s,line=%d\n",	\ +		#expr,__FILE__,__FUNCTION__,__LINE__);		} +#define DBG(lvl, format, arg...) if (rc32434_debug > lvl) printk(KERN_INFO "%s: " format, dev->name , ## arg) +#else +#define ASSERT(expr) do {} while (0) +#define DBG(lvl, format, arg...) do {} while (0) +#endif + +#define INFO(format, arg...) printk(KERN_INFO "%s: " format, dev->name , ## arg) +#define ERR(format, arg...) printk(KERN_ERR "%s: " format, dev->name , ## arg) +#define WARN(format, arg...) printk(KERN_WARNING "%s: " format, dev->name , ## arg)		 + +/* the following must be powers of two */ +#ifdef CONFIG_IDT_USE_NAPI +#define RC32434_NUM_RDS    64    		/* number of receive descriptors */ +#define RC32434_NUM_TDS    64    		/* number of transmit descriptors */ +#else +#define RC32434_NUM_RDS    128    		/* number of receive descriptors */ +#define RC32434_NUM_TDS    128    		/* number of transmit descriptors */ +#endif + +#define RC32434_RBSIZE     1536  		/* size of one resource buffer = Ether MTU */ +#define RC32434_RDS_MASK   (RC32434_NUM_RDS-1) +#define RC32434_TDS_MASK   (RC32434_NUM_TDS-1) +#define RD_RING_SIZE (RC32434_NUM_RDS * sizeof(struct DMAD_s)) +#define TD_RING_SIZE (RC32434_NUM_TDS * sizeof(struct DMAD_s)) + +#define RC32434_TX_TIMEOUT HZ * 100 + +#define rc32434_eth0_regs ((ETH_t)(ETH0_VirtualAddress)) +#define rc32434_eth1_regs ((ETH_t)(ETH1_VirtualAddress)) + +enum status	{ filled,	empty}; +#define IS_DMA_FINISHED(X)   (((X) & (DMAD_f_m)) != 0) +#define IS_DMA_DONE(X)   (((X) & (DMAD_d_m)) != 0) + + +/* Information that need to be kept for each board. */ +struct rc32434_local { +	ETH_t  eth_regs; +	DMA_Chan_t  rx_dma_regs; +	DMA_Chan_t  tx_dma_regs; +	volatile DMAD_t   td_ring;			/* transmit descriptor ring */  +	volatile DMAD_t   rd_ring;			/* receive descriptor ring  */ +	 +	struct sk_buff* tx_skb[RC32434_NUM_TDS]; 	/* skbuffs for pkt to trans */ +	struct sk_buff* rx_skb[RC32434_NUM_RDS]; 	/* skbuffs for pkt to trans */ +	 +	struct tasklet_struct * tx_tasklet; +	 +	int	rx_next_done; +	int	rx_chain_head; +	int	rx_chain_tail; +	enum status	rx_chain_status; +	 +	int	tx_next_done; +	int	tx_chain_head; +	int	tx_chain_tail; +	enum status	tx_chain_status; +	int tx_count;			 +	int	tx_full; +	 +	struct timer_list    mii_phy_timer; +	unsigned long duplex_mode; +	 +	int   	rx_irq; +	int    tx_irq; +	int    ovr_irq; +	int    und_irq; +	 +	struct net_device_stats stats; +	spinlock_t lock;  +	 +	/* debug /proc entry */ +	struct proc_dir_entry *ps; +	int dma_halt_cnt;  int dma_run_cnt; +	struct napi_struct napi; +	struct net_device *dev; +}; + +extern unsigned int idt_cpu_freq; + +/* Index to functions, as function prototypes. */ +static int rc32434_open(struct net_device *dev); +static int rc32434_send_packet(struct sk_buff *skb, struct net_device *dev); +static void rc32434_mii_handler(unsigned long data); +static irqreturn_t  rc32434_und_interrupt(int irq, void *dev_id); +static irqreturn_t rc32434_rx_dma_interrupt(int irq, void *dev_id); +static irqreturn_t rc32434_tx_dma_interrupt(int irq, void *dev_id); +#ifdef	RC32434_REVISION	 +static irqreturn_t rc32434_ovr_interrupt(int irq, void *dev_id); +#endif +static int  rc32434_close(struct net_device *dev); +static struct net_device_stats *rc32434_get_stats(struct net_device *dev); +static void rc32434_multicast_list(struct net_device *dev); +static int  rc32434_init(struct net_device *dev); +static void rc32434_tx_timeout(struct net_device *dev); + +static void rc32434_tx_tasklet(unsigned long tx_data_dev); +static int rc32434_poll(struct napi_struct *napi, int budget); +static void rc32434_cleanup_module(void); + + +static inline void rc32434_abort_dma(struct net_device *dev, DMA_Chan_t ch) +{ +	if (__raw_readl(&ch->dmac) & DMAC_run_m) { +		__raw_writel(0x10, &ch->dmac);  +		 +		while (!(__raw_readl(&ch->dmas) & DMAS_h_m)) +			dev->trans_start = jiffies;		 +		 +		__raw_writel(0, &ch->dmas);   +	} +	 +	__raw_writel(0, &ch->dmadptr);  +	__raw_writel(0, &ch->dmandptr);  +} | 
