1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2025-04-21 12:27:27 +03:00

convert rb532 to the new structure

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@7273 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
nbd
2007-05-19 13:14:38 +00:00
parent e6eda13dd5
commit 0771c0af36
34 changed files with 5945 additions and 5909 deletions

View File

@@ -0,0 +1,3 @@
## Makefile for the RB532 CF port
obj-y += bdev.o ata.o

View File

@@ -0,0 +1,485 @@
/* CF-mips driver
This is a block driver for the direct (mmaped) interface to the CF-slot,
found in Routerboard.com's RB532 board
See SDK provided from routerboard.com.
Module adapted By P.Christeas <p_christeas@yahoo.com>, 2005-6.
Cleaned up and adapted to platform_device by Felix Fietkau <nbd@openwrt.org>
This work is redistributed under the terms of the GNU General Public License.
*/
#include <linux/kernel.h> /* printk() */
#include <linux/module.h> /* module to be loadable */
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/ioport.h> /* request_mem_region() */
#include <asm/unaligned.h> /* ioremap() */
#include <asm/io.h> /* ioremap() */
#include <asm/rc32434/rb.h>
#include "ata.h"
#define REQUEST_MEM_REGION 0
#define DEBUG 1
#if DEBUG
#define DEBUGP printk
#else
#define DEBUGP(format, args...)
#endif
#define SECS 1000000 /* unit for wait_not_busy() is 1us */
unsigned cf_head = 0;
unsigned cf_cyl = 0;
unsigned cf_spt = 0;
unsigned cf_sectors = 0;
static unsigned cf_block_size = 1;
static void *baddr = 0;
#define DBUF32 ((volatile u32 *)((unsigned long)dev->baddr | ATA_DBUF_OFFSET))
static void cf_do_tasklet(unsigned long dev_l);
static inline void wareg(u8 val, unsigned reg, struct cf_mips_dev* dev)
{
writeb(val, dev->baddr + ATA_REG_OFFSET + reg);
}
static inline u8 rareg(unsigned reg, struct cf_mips_dev* dev)
{
return readb(dev->baddr + ATA_REG_OFFSET + reg);
}
static inline int get_gpio_bit(gpio_func ofs, struct cf_mips_dev *dev)
{
return (gpio_get(ofs) >> dev->pin) & 1;
}
static inline void set_gpio_bit(int bit, gpio_func ofs, struct cf_mips_dev *dev)
{
gpio_set(ofs, (1 << dev->pin), ((bit & 1) << dev->pin));
}
static inline int cfrdy(struct cf_mips_dev *dev)
{
return get_gpio_bit(DATA, dev);
}
static inline void prepare_cf_irq(struct cf_mips_dev *dev)
{
set_gpio_bit(1, ILEVEL, dev); /* interrupt on cf ready (not busy) */
set_gpio_bit(0, ISTAT, dev); /* clear interrupt status */
}
static inline int cf_present(struct cf_mips_dev* dev)
{
/* TODO: read and configure CIS into memory mapped mode
* TODO: parse CISTPL_CONFIG on CF+ cards to get base address (0x200)
* TODO: maybe adjust power saving setting for Hitachi Microdrive
*/
int i;
/* setup CFRDY GPIO as input */
set_gpio_bit(0, FUNC, dev);
set_gpio_bit(0, CFG, dev);
for (i = 0; i < 0x10; ++i) {
if (rareg(i,dev) != 0xff)
return 1;
}
return 0;
}
static inline int is_busy(struct cf_mips_dev *dev)
{
return !cfrdy(dev);
}
static int wait_not_busy(int to_us, int wait_for_busy,struct cf_mips_dev *dev)
{
int us_passed = 0;
if (wait_for_busy && !is_busy(dev)) {
/* busy must appear within 400ns,
* but it may dissapear before we see it
* => must not wait for busy in a loop
*/
ndelay(400);
}
do {
if (us_passed)
udelay(1); /* never reached in async mode */
if (!is_busy(dev)) {
if (us_passed > 1 * SECS) {
printk(KERN_WARNING "cf-mips: not busy ok (after %dus)"
", status 0x%02x\n", us_passed, (unsigned) rareg(ATA_REG_ST,dev));
}
return CF_TRANS_OK;
}
if (us_passed == 1 * SECS) {
printk(KERN_WARNING "cf-mips: wait not busy %dus..\n", to_us);
}
if (dev->async_mode) {
dev->to_timer.expires = jiffies + (to_us * HZ / SECS);
dev->irq_enable_time = jiffies;
prepare_cf_irq(dev);
if (is_busy(dev)) {
add_timer(&dev->to_timer);
enable_irq(dev->irq);
return CF_TRANS_IN_PROGRESS;
}
continue;
}
++us_passed;
} while (us_passed < to_us);
printk(KERN_ERR "cf-mips: wait not busy timeout (%dus)"
", status 0x%02x, state %d\n",
to_us, (unsigned) rareg(ATA_REG_ST,dev), dev->tstate);
return CF_TRANS_FAILED;
}
static irqreturn_t cf_irq_handler(int irq, void *dev_id)
{
/* While tasklet has not disabled irq, irq will be retried all the time
* because of ILEVEL matching GPIO pin status => deadlock.
* To avoid this, we change ILEVEL to 0.
*/
struct cf_mips_dev *dev=dev_id;
set_gpio_bit(0, ILEVEL, dev);
set_gpio_bit(0, ISTAT, dev);
del_timer(&dev->to_timer);
tasklet_schedule(&dev->tasklet);
return IRQ_HANDLED;
}
static int do_reset(struct cf_mips_dev *dev)
{
printk(KERN_INFO "cf-mips: resetting..\n");
wareg(ATA_REG_DC_SRST, ATA_REG_DC,dev);
udelay(1); /* FIXME: how long should we wait here? */
wareg(0, ATA_REG_DC,dev);
return wait_not_busy(30 * SECS, 1,dev);
}
static int set_multiple(struct cf_mips_dev *dev)
{
if (dev->block_size <= 1)
return CF_TRANS_OK;
wareg(dev->block_size, ATA_REG_SC,dev);
wareg(ATA_REG_DH_BASE | ATA_REG_DH_LBA, ATA_REG_DH,dev);
wareg(ATA_CMD_SET_MULTIPLE, ATA_REG_CMD,dev);
return wait_not_busy(10 * SECS, 1,dev);
}
static int set_cmd(struct cf_mips_dev *dev)
{
//DEBUGP(KERN_INFO "cf-mips: ata cmd 0x%02x\n", dev->tcmd);
// sector_count should be <=24 bits..
BUG_ON(dev->tsect_start>=0x10000000);
// This way, it addresses 2^24 * 512 = 128G
if (dev->tsector_count) {
wareg(dev->tsector_count & 0xff, ATA_REG_SC,dev);
wareg(dev->tsect_start & 0xff, ATA_REG_SN,dev);
wareg((dev->tsect_start >> 8) & 0xff, ATA_REG_CL,dev);
wareg((dev->tsect_start >> 16) & 0xff, ATA_REG_CH,dev);
}
wareg(((dev->tsect_start >> 24) & 0x0f) | ATA_REG_DH_BASE | ATA_REG_DH_LBA,
ATA_REG_DH,dev); /* select drive on all commands */
wareg(dev->tcmd, ATA_REG_CMD,dev);
return wait_not_busy(10 * SECS, 1,dev);
}
static int do_trans(struct cf_mips_dev *dev)
{
int res;
unsigned st;
int transfered;
//printk("do_trans: %d sectors left\n",dev->tsectors_left);
while (dev->tsectors_left) {
transfered = 0;
st = rareg(ATA_REG_ST,dev);
if (!(st & ATA_REG_ST_DRQ)) {
printk(KERN_ERR "cf-mips: do_trans without DRQ (status 0x%x)!\n", st);
if (st & ATA_REG_ST_ERR) {
int errId = rareg(ATA_REG_ERR,dev);
printk(KERN_ERR "cf-mips: %s error, status 0x%x, errid 0x%x\n",
(dev->tread ? "read" : "write"), st, errId);
}
return CF_TRANS_FAILED;
}
do { /* Fill/read the buffer one block */
u32 *qbuf, *qend;
qbuf = (u32 *)dev->tbuf;
qend = qbuf + CF_SECT_SIZE / sizeof(u32);
if (dev->tread) {
while (qbuf!=qend)
put_unaligned(*DBUF32,qbuf++);
//*(qbuf++) = *DBUF32;
}
else {
while(qbuf!=qend)
*DBUF32 = get_unaligned(qbuf++);
}
dev->tsectors_left--;
dev->tbuf += CF_SECT_SIZE;
dev->tbuf_size -= CF_SECT_SIZE;
transfered++;
} while (transfered != dev->block_size && dev->tsectors_left > 0);
res = wait_not_busy(10 * SECS, 1,dev);
if (res != CF_TRANS_OK)
return res;
};
st = rareg(ATA_REG_ST,dev);
if (st & (ATA_REG_ST_DRQ | ATA_REG_ST_DWF | ATA_REG_ST_ERR)) {
if (st & ATA_REG_ST_DRQ) {
printk(KERN_ERR "cf-mips: DRQ after all %d sectors are %s"
", status 0x%x\n", dev->tsector_count, (dev->tread ? "read" : "written"), st);
} else if (st & ATA_REG_ST_DWF) {
printk(KERN_ERR "cf-mips: write fault, status 0x%x\n", st);
} else {
int errId = rareg(ATA_REG_ERR,dev);
printk(KERN_ERR "cf-mips: %s error, status 0x%x, errid 0x%x\n",
(dev->tread ? "read" : "write"), st, errId);
}
return CF_TRANS_FAILED;
}
return CF_TRANS_OK;
}
static int cf_do_state(struct cf_mips_dev *dev)
{
int res;
switch (dev->tstate) { /* fall through everywhere */
case TS_IDLE:
dev->tstate = TS_READY;
if (is_busy(dev)) {
dev->tstate = TS_AFTER_RESET;
res = do_reset(dev);
if (res != CF_TRANS_OK)
break;
}
case TS_AFTER_RESET:
if (dev->tstate == TS_AFTER_RESET) {
dev->tstate = TS_READY;
res = set_multiple(dev);
if (res != CF_TRANS_OK)
break;
}
case TS_READY:
dev->tstate = TS_CMD;
res = set_cmd(dev);
if (res != CF_TRANS_OK)
break;;
case TS_CMD:
dev->tstate = TS_TRANS;
case TS_TRANS:
res = do_trans(dev);
break;
default:
printk(KERN_ERR "cf-mips: BUG: unknown tstate %d\n", dev->tstate);
return CF_TRANS_FAILED;
}
if (res != CF_TRANS_IN_PROGRESS)
dev->tstate = TS_IDLE;
return res;
}
static void cf_do_tasklet(unsigned long dev_l)
{
struct cf_mips_dev* dev=(struct cf_mips_dev*) dev_l;
int res;
disable_irq(dev->irq);
if (dev->tstate == TS_IDLE)
return; /* can happen when irq is first registered */
#if 0
DEBUGP(KERN_WARNING "cf-mips: not busy ok (tasklet) status 0x%02x\n",
(unsigned) rareg(ATA_REG_ST,dev));
#endif
res = cf_do_state(dev);
if (res == CF_TRANS_IN_PROGRESS)
return;
cf_async_trans_done(dev,res);
}
static void cf_async_timeout(unsigned long dev_l)
{
struct cf_mips_dev* dev=(struct cf_mips_dev*) dev_l;
disable_irq(dev->irq);
/* Perhaps send abort to the device? */
printk(KERN_ERR "cf-mips: wait not busy timeout (%lus)"
", status 0x%02x, state %d\n",
jiffies - dev->irq_enable_time, (unsigned) rareg(ATA_REG_ST,dev), dev->tstate);
dev->tstate = TS_IDLE;
cf_async_trans_done(dev,CF_TRANS_FAILED);
}
int cf_do_transfer(struct cf_mips_dev* dev,sector_t sector, unsigned long nsect,
char* buffer, int is_write)
{
BUG_ON(dev->tstate!=TS_IDLE);
if (nsect > ATA_MAX_SECT_PER_CMD) {
printk(KERN_WARNING "cf-mips: sector count %lu out of range\n",nsect);
return CF_TRANS_FAILED;
}
if (sector + nsect > dev->sectors) {
printk(KERN_WARNING "cf-mips: sector %lu out of range\n",sector);
return CF_TRANS_FAILED;
}
dev->tbuf = buffer;
dev->tbuf_size = nsect*512;
dev->tsect_start = sector;
dev->tsector_count = nsect;
dev->tsectors_left = dev->tsector_count;
dev->tread = (is_write)?0:1;
dev->tcmd = (dev->block_size == 1 ?
(is_write ? ATA_CMD_WRITE_SECTORS : ATA_CMD_READ_SECTORS) :
(is_write ? ATA_CMD_WRITE_MULTIPLE : ATA_CMD_READ_MULTIPLE));
return cf_do_state(dev);
}
static int do_identify(struct cf_mips_dev *dev)
{
u16 sbuf[CF_SECT_SIZE >> 1];
int res;
char tstr[17]; //serial
BUG_ON(dev->tstate!=TS_IDLE);
dev->tbuf = (char *) sbuf;
dev->tbuf_size = CF_SECT_SIZE;
dev->tsect_start = 0;
dev->tsector_count = 0;
dev->tsectors_left = 1;
dev->tread = 1;
dev->tcmd = ATA_CMD_IDENTIFY_DRIVE;
DEBUGP(KERN_INFO "cf-mips: identify drive..\n");
res = cf_do_state(dev);
if (res == CF_TRANS_IN_PROGRESS) {
printk(KERN_ERR "cf-mips: BUG: async identify cmd\n");
return CF_TRANS_FAILED;
}
if (res != CF_TRANS_OK)
return 0;
dev->head = sbuf[3];
dev->cyl = sbuf[1];
dev->spt = sbuf[6];
dev->sectors = ((unsigned long) sbuf[7] << 16) | sbuf[8];
dev->dtype=sbuf[0];
memcpy(tstr,&sbuf[12],16);
tstr[16]=0;
printk(KERN_INFO "cf-mips: %s detected, C/H/S=%d/%d/%d sectors=%u (%uMB) Serial=%s\n",
(sbuf[0] == 0x848A ? "CF card" : "ATA drive"), dev->cyl, dev->head,
dev->spt, dev->sectors, dev->sectors >> 11,tstr);
return 1;
}
static void init_multiple(struct cf_mips_dev * dev)
{
int res;
DEBUGP(KERN_INFO "cf-mips: detecting block size\n");
dev->block_size = 128; /* max block size = 128 sectors (64KB) */
do {
wareg(dev->block_size, ATA_REG_SC,dev);
wareg(ATA_REG_DH_BASE | ATA_REG_DH_LBA, ATA_REG_DH,dev);
wareg(ATA_CMD_SET_MULTIPLE, ATA_REG_CMD,dev);
res = wait_not_busy(10 * SECS, 1,dev);
if (res != CF_TRANS_OK) {
printk(KERN_ERR "cf-mips: failed to detect block size: busy!\n");
dev->block_size = 1;
return;
}
if ((rareg(ATA_REG_ST,dev) & ATA_REG_ST_ERR) == 0)
break;
dev->block_size /= 2;
} while (dev->block_size > 1);
printk(KERN_INFO "cf-mips: multiple sectors = %d\n", dev->block_size);
}
int cf_init(struct cf_mips_dev *dev)
{
tasklet_init(&dev->tasklet,cf_do_tasklet,(unsigned long)dev);
dev->baddr = ioremap_nocache((unsigned long)dev->base, CFDEV_BUF_SIZE);
if (!dev->baddr) {
printk(KERN_ERR "cf-mips: cf_init: ioremap for (%lx,%x) failed\n",
(unsigned long) dev->base, CFDEV_BUF_SIZE);
return -EBUSY;
}
if (!cf_present(dev)) {
printk(KERN_WARNING "cf-mips: cf card not present\n");
iounmap(dev->baddr);
return -ENODEV;
}
if (do_reset(dev) != CF_TRANS_OK) {
printk(KERN_ERR "cf-mips: cf reset failed\n");
iounmap(dev->baddr);
return -EBUSY;
}
if (!do_identify(dev)) {
printk(KERN_ERR "cf-mips: cf identify failed\n");
iounmap(dev->baddr);
return -EBUSY;
}
/* set_apm_level(ATA_APM_WITH_STANDBY); */
init_multiple(dev);
init_timer(&dev->to_timer);
dev->to_timer.function = cf_async_timeout;
dev->to_timer.data = (unsigned long)dev;
prepare_cf_irq(dev);
if (request_irq(dev->irq, cf_irq_handler, 0, "CF Mips", dev)) {
printk(KERN_ERR "cf-mips: failed to get irq\n");
iounmap(dev->baddr);
return -EBUSY;
}
/* Disable below would be odd, because request will enable, and the tasklet
will disable it itself */
//disable_irq(dev->irq);
dev->async_mode = 1;
return 0;
}
void cf_cleanup(struct cf_mips_dev *dev)
{
iounmap(dev->baddr);
free_irq(dev->irq, NULL);
#if REQUEST_MEM_REGION
release_mem_region((unsigned long)dev->base, CFDEV_BUF_SIZE);
#endif
}
/*eof*/

View File

@@ -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

View File

@@ -0,0 +1,339 @@
/* CF-mips driver
This is a block driver for the direct (mmaped) interface to the CF-slot,
found in Routerboard.com's RB532 board
See SDK provided from routerboard.com.
Module adapted By P.Christeas <p_christeas@yahoo.com>, 2005-6.
Cleaned up and adapted to platform_device by Felix Fietkau <nbd@openwrt.org>
This work is redistributed under the terms of the GNU General Public License.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/hdreg.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/rc32434/rb.h>
#ifdef DEBUG
#define DEBUGP printk
#define DLEVEL 1
#else
#define DEBUGP(format, args...)
#define DLEVEL 0
#endif
#define CF_MIPS_MAJOR 13
#define MAJOR_NR CF_MIPS_MAJOR
#define CF_MAX_PART 16 /* max 15 partitions */
#include "ata.h"
//extern struct block_device_operations cf_bdops;
// static struct hd_struct cf_parts[CF_MAX_PART];
// static int cf_part_sizes[CF_MAX_PART];
// static int cf_hsect_sizes[CF_MAX_PART];
// static int cf_max_sectors[CF_MAX_PART];
// static int cf_blksize_sizes[CF_MAX_PART];
// static spinlock_t lock = SPIN_LOCK_UNLOCKED;
// volatile int cf_busy = 0;
static struct request *active_req = NULL;
static int cf_open (struct inode *, struct file *);
static int cf_release (struct inode *, struct file *);
static int cf_ioctl (struct inode *, struct file *, unsigned, unsigned long);
static void cf_request(request_queue_t * q);
static int cf_transfer(const struct request *req);
/*long (*unlocked_ioctl) (struct file *, unsigned, unsigned long);
long (*compat_ioctl) (struct file *, unsigned, unsigned long);*/
// int (*direct_access) (struct block_device *, sector_t, unsigned long *);
// int (*media_changed) (struct gendisk *);
// int (*revalidate_disk) (struct gendisk *);
static struct block_device_operations cf_bdops = {
.owner = THIS_MODULE,
.open = cf_open,
.release = cf_release,
.ioctl = cf_ioctl,
.media_changed = NULL,
.unlocked_ioctl = NULL,
.revalidate_disk = NULL,
.compat_ioctl = NULL,
.direct_access = NULL
};
int cf_mips_probe(struct platform_device *pdev)
{
struct gendisk* cf_gendisk=NULL;
struct cf_device *cdev = (struct cf_device *) pdev->dev.platform_data;
struct cf_mips_dev *dev;
struct resource *r;
int reg_result;
reg_result = register_blkdev(MAJOR_NR, "cf-mips");
if (reg_result < 0) {
printk(KERN_WARNING "cf-mips: can't get major %d\n", MAJOR_NR);
return reg_result;
}
dev = (struct cf_mips_dev *)kmalloc(sizeof(struct cf_mips_dev),GFP_KERNEL);
if (!dev)
goto out_err;
memset(dev, 0, sizeof(struct cf_mips_dev));
cdev->dev = dev;
dev->pin = cdev->gpio_pin;
dev->irq = platform_get_irq_byname(pdev, "cf_irq");
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cf_membase");
dev->base = (void *) r->start;
if (cf_init(dev)) goto out_err;
printk("init done");
spin_lock_init(&dev->lock);
dev->queue = blk_init_queue(cf_request,&dev->lock);
if (!dev->queue){
printk(KERN_ERR "cf-mips: no mem for queue\n");
goto out_err;
}
blk_queue_max_sectors(dev->queue,ATA_MAX_SECT_PER_CMD);
/* For memory devices, it is always better to avoid crossing segments
inside the same request. */
/* if (dev->dtype==0x848A){
printk(KERN_INFO "Setting boundary for cf to 0x%x",(dev->block_size*512)-1);
blk_queue_segment_boundary(dev->queue, (dev->block_size*512)-1);
}*/
dev->gd = alloc_disk(CF_MAX_PART);
cf_gendisk = dev->gd;
cdev->gd = dev->gd;
if (!cf_gendisk) goto out_err; /* Last of these goto's */
cf_gendisk->major = MAJOR_NR;
cf_gendisk->first_minor = 0;
cf_gendisk->queue=dev->queue;
BUG_ON(cf_gendisk->minors != CF_MAX_PART);
strcpy(cf_gendisk->disk_name,"cfa");
cf_gendisk->fops = &cf_bdops;
cf_gendisk->flags = 0 ; /* is not yet GENHD_FL_REMOVABLE */
cf_gendisk->private_data=dev;
set_capacity(cf_gendisk,dev->sectors * CF_KERNEL_MUL);
/* Let the disk go live */
add_disk(cf_gendisk);
#if 0
result = cf_init();
/* default cfg for all partitions */
memset(cf_parts, 0, sizeof (cf_parts[0]) * CF_MAX_PART);
memset(cf_part_sizes, 0, sizeof (cf_part_sizes[0]) * CF_MAX_PART);
for (i = 0; i < CF_MAX_PART; ++i) {
cf_hsect_sizes[i] = CF_SECT_SIZE;
cf_max_sectors[i] = ATA_MAX_SECT_PER_CMD;
cf_blksize_sizes[i] = BLOCK_SIZE;
}
/* setup info for whole disk (partition 0) */
cf_part_sizes[0] = cf_sectors / 2;
cf_parts[0].nr_sects = cf_sectors;
blk_size[MAJOR_NR] = cf_part_sizes;
blksize_size[MAJOR_NR] = cf_blksize_sizes;
max_sectors[MAJOR_NR] = cf_max_sectors;
hardsect_size[MAJOR_NR] = cf_hsect_sizes;
read_ahead[MAJOR_NR] = 8; /* (4kB) */
blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
add_gendisk(&cf_gendisk);
#endif
// printk(KERN_INFO "cf-mips partition check: \n");
// register_disk(cf_gendisk, MKDEV(MAJOR_NR, 0), CF_MAX_PART,
// &cf_bdops, dev->sectors);
return 0;
out_err:
if (dev->queue){
blk_cleanup_queue(dev->queue);
}
if (reg_result) {
unregister_blkdev(MAJOR_NR, "cf-mips");
return reg_result;
}
if (dev){
cf_cleanup(dev);
kfree(dev);
}
return 1;
}
static int
cf_mips_remove(struct platform_device *pdev)
{
struct cf_device *cdev = (struct cf_device *) pdev->dev.platform_data;
struct cf_mips_dev *dev = (struct cf_mips_dev *) cdev->dev;
unregister_blkdev(MAJOR_NR, "cf-mips");
blk_cleanup_queue(dev->queue);
del_gendisk(dev->gd);
cf_cleanup(dev);
return 0;
}
static struct platform_driver cf_driver = {
.driver.name = "rb500-cf",
.probe = cf_mips_probe,
.remove = cf_mips_remove,
};
static int __init cf_mips_init(void)
{
printk(KERN_INFO "cf-mips module loaded\n");
return platform_driver_register(&cf_driver);
}
static void cf_mips_cleanup(void)
{
platform_driver_unregister(&cf_driver);
printk(KERN_INFO "cf-mips module removed\n");
}
module_init(cf_mips_init);
module_exit(cf_mips_cleanup);
MODULE_LICENSE("GPL");
MODULE_ALIAS_BLOCKDEV_MAJOR(CF_MIPS_MAJOR);
static int cf_open(struct inode *inode, struct file *filp)
{
struct cf_mips_dev *dev=inode->i_bdev->bd_disk->private_data;
int minor = MINOR(inode->i_rdev);
if (minor >= CF_MAX_PART)
return -ENODEV;
//DEBUGP(KERN_INFO "cf-mips module opened, minor %d\n", minor);
spin_lock(&dev->lock);
dev->users++;
spin_unlock(&dev->lock);
filp->private_data=dev;
/* dirty workaround to set CFRDY GPIO as an input when some other
program sets it as an output */
gpio_set(CFG, (1 << dev->pin), 0);
return 0; /* success */
}
static int cf_release(struct inode *inode, struct file *filp)
{
int minor = MINOR(inode->i_rdev);
struct cf_mips_dev *dev=inode->i_bdev->bd_disk->private_data;
spin_lock(&dev->lock);
dev->users--;
spin_unlock(&dev->lock);
return 0;
}
static int cf_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
unsigned minor = MINOR(inode->i_rdev);
struct cf_mips_dev *dev=inode->i_bdev->bd_disk->private_data;
DEBUGP(KERN_INFO "cf_ioctl cmd %u\n", cmd);
switch (cmd) {
case BLKRRPART: /* re-read partition table */
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
printk(KERN_INFO "cf-mips partition check: \n");
register_disk(dev->gd);
return 0;
case HDIO_GETGEO:
{
struct hd_geometry geo;
geo.cylinders = dev->cyl;
geo.heads = dev->head;
geo.sectors = dev->spt;
geo.start = (*dev->gd->part)[minor].start_sect;
if (copy_to_user((void *) arg, &geo, sizeof (geo)))
return -EFAULT;
}
return 0;
}
return -EINVAL; /* unknown command */
}
static void cf_request(request_queue_t * q)
{
struct cf_mips_dev* dev;
struct request * req;
int status;
/* We could have q->queuedata = dev , but haven't yet. */
if (active_req)
return;
while ((req=elv_next_request(q))!=NULL){
dev=req->rq_disk->private_data;
status=cf_transfer(req);
if (status==CF_TRANS_IN_PROGRESS){
active_req=req;
return;
}
end_request(req,status);
}
}
static int cf_transfer(const struct request *req)
{
struct cf_mips_dev* dev=req->rq_disk->private_data;
if (!blk_fs_request(req)){
if (printk_ratelimit())
printk(KERN_WARNING "cf-mips: skipping non-fs request 0x%x\n",req->cmd[0]);
return CF_TRANS_FAILED;
}
return cf_do_transfer(dev,req->sector,req->current_nr_sectors,req->buffer,rq_data_dir(req));
}
void cf_async_trans_done(struct cf_mips_dev * dev,int result)
{
struct request *req;
spin_lock(&dev->lock);
req=active_req;
active_req=NULL;
end_request(req,result);
spin_unlock(&dev->lock);
spin_lock(&dev->lock);
cf_request(dev->queue);
spin_unlock(&dev->lock);
}

View File

@@ -0,0 +1,137 @@
#include <linux/init.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/bootinfo.h>
#define IDT434_REG_BASE ((volatile void *) KSEG1ADDR(0x18000000))
#define GPIOF 0x050000
#define GPIOC 0x050004
#define GPIOD 0x050008
#define GPIO_RDY (1 << 0x08)
#define GPIO_WPX (1 << 0x09)
#define GPIO_ALE (1 << 0x0a)
#define GPIO_CLE (1 << 0x0b)
#define DEV2BASE 0x010020
#define LO_WPX (1 << 0)
#define LO_ALE (1 << 1)
#define LO_CLE (1 << 2)
#define LO_CEX (1 << 3)
#define LO_FOFF (1 << 5)
#define LO_SPICS (1 << 6)
#define LO_ULED (1 << 7)
#define MEM32(x) *((volatile unsigned *) (x))
static void __iomem *p_nand;
extern void changeLatchU5(unsigned char orMask, unsigned char nandMask);
static int rb500_dev_ready(struct mtd_info *mtd)
{
return MEM32(IDT434_REG_BASE + GPIOD) & GPIO_RDY;
}
/*
* hardware specific access to control-lines
*
* ctrl:
* NAND_CLE: bit 2 -> bit 3
* NAND_ALE: bit 3 -> bit 2
*/
static void rbmips_hwcontrol500(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
struct nand_chip *chip = mtd->priv;
unsigned char orbits, nandbits;
if (ctrl & NAND_CTRL_CHANGE) {
orbits = (ctrl & NAND_CLE) << 1;
orbits |= (ctrl & NAND_ALE) >> 1;
nandbits = (~ctrl & NAND_CLE) << 1;
nandbits |= (~ctrl & NAND_ALE) >> 1;
changeLatchU5(orbits, nandbits);
}
if (cmd != NAND_CMD_NONE)
writeb(cmd, chip->IO_ADDR_W);
}
static struct mtd_partition partition_info[] = {
{
name:"RouterBoard NAND Boot",
offset:0,
size:4 * 1024 * 1024},
{
name:"RouterBoard NAND Main",
offset:MTDPART_OFS_NXTBLK,
size:MTDPART_SIZ_FULL}
};
static struct mtd_info rmtd;
static struct nand_chip rnand;
static unsigned init_ok = 0;
unsigned get_rbnand_block_size(void)
{
if (init_ok)
return rmtd.writesize;
else
return 0;
}
EXPORT_SYMBOL(get_rbnand_block_size);
int __init rbmips_init(void)
{
int *b;
memset(&rmtd, 0, sizeof(rmtd));
memset(&rnand, 0, sizeof(rnand));
printk("RB500 nand\n");
changeLatchU5(LO_WPX | LO_FOFF | LO_CEX,
LO_ULED | LO_ALE | LO_CLE);
rnand.cmd_ctrl = rbmips_hwcontrol500;
rnand.dev_ready = rb500_dev_ready;
rnand.IO_ADDR_W = (unsigned char *)
KSEG1ADDR(MEM32(IDT434_REG_BASE + DEV2BASE));
rnand.IO_ADDR_R = rnand.IO_ADDR_W;
p_nand = (void __iomem *) ioremap((void *) 0x18a20000, 0x1000);
if (!p_nand) {
printk("RBnand Unable ioremap buffer");
return -ENXIO;
}
rnand.ecc.mode = NAND_ECC_SOFT;
rnand.chip_delay = 25;
rnand.options |= NAND_NO_AUTOINCR;
rmtd.priv = &rnand;
b = (int *) KSEG1ADDR(0x18010020);
printk("dev2base 0x%08x mask 0x%08x c 0x%08x tc 0x%08x\n", b[0],
b[1], b[2], b[3]);
if (nand_scan(&rmtd, 1) && nand_scan(&rmtd, 1)
&& nand_scan(&rmtd, 1) && nand_scan(&rmtd, 1)) {
printk("RBxxx nand device not found\n");
iounmap((void *) p_nand);
return -ENXIO;
}
add_mtd_partitions(&rmtd, partition_info, 2);
init_ok = 1;
return 0;
}
module_init(rbmips_init);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,178 @@
/**************************************************************************
*
* 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 */
#ifndef CONFIG_IDT_USE_NAPI
struct tasklet_struct * rx_tasklet;
#endif
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;
};
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);
#ifdef CONFIG_IDT_USE_NAPI
static int rc32434_poll(struct net_device *rx_data_dev, int *budget);
#else
static void rc32434_rx_tasklet(unsigned long rx_data_dev);
#endif
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);
}