Chinaunix首页 | 论坛 | 博客
  • 博客访问: 103451
  • 博文数量: 27
  • 博客积分: 2510
  • 博客等级: 少校
  • 技术积分: 246
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-02 22:35
文章分类

全部博文(27)

文章存档

2009年(13)

2008年(14)

我的朋友

分类: LINUX

2008-12-28 21:21:13

A linux compact flash block driver implements the compact flash hardware operation and block layer, which works on PC Card memory mode and bases on the ata driver and cfspec 4.1. The linux kernel version is linux 2.6.19.
 
-------------------------------cfcard.h----------------------------------
#ifndef __CFCARD_H__
#define __CFCARD_H__

/* CPU related register defination */
#define GPOUT0      0x80000000    /* CF reset */
#define GPIN2       0x20000000    /* CF ready */
#define GPIN0       0x80000000    /* CD1 */
#define GPIN1       0x40000000    /* CD2 */

#define CF_READY    GPIN2
#define CF_RESET    GPOUT0
#define CF_CD1      GPIN0
#define CF_CD2      GPIN1

#define CFCARD_GPIO_GPIOCR(base) ((volatile unsigned long *)((base) + 0x30))
#define CFCARD_GPIO_GPOUTDR(base) ((volatile unsigned long *)((base) + 0x40))
#define CFCARD_GPIO_GPINDR(base) ((volatile unsigned long *)((base) + 0x50))

#define CFG_CCSRBAR     0xF0000000 
#define SWAP16(data) ( (data)>>8 | (data)<<8 )

/* compact flash related varibles */
#define CF_REG_FR      0x1
#define CF_REG_SC      0x2
#define CF_REG_SN      0x3
#define CF_REG_CL      0x4
#define CF_REG_CH      0x5
#define CF_REG_DH      0x6
#define   CF_REG_DH_BASE       0xa0
#define   CF_REG_DH_LBA        0x40
#define   CF_REG_DH_DRV        0x10
#define CF_REG_CMD     0x7
#define CF_REG_ST      0x7
#define   CF_REG_ST_BUSY       0x80
#define   CF_REG_ST_RDY        0x40
#define   CF_REG_ST_DWF        0x20
#define   CF_REG_ST_DSC        0x10
#define   CF_REG_ST_DRQ        0x08
#define   CF_REG_ST_CORR       0x04
#define   CF_REG_ST_ERR        0x01
#define CF_REG_ER      0xd
#define CF_REG_DC      0xe
#define   CF_REG_DC_IEN        0x02
#define   CF_REG_DC_SRST       0x04

#define CF_CMD_READ_SECTORS    0x20
#define CF_CMD_WRITE_SECTORS   0x30
#define CF_CMD_EXEC_DRIVE_DIAG 0x90
#define CF_CMD_READ_MULTIPLE   0xC4
#define CF_CMD_WRITE_MULTIPLE  0xC5
#define CF_CMD_SET_MULTIPLE    0xC6
#define CF_CMD_IDENTIFY_DRIVE  0xEC
#define CF_CMD_SET_FEATURES    0xEF

/* system related definition */
#define CFCARD_BASE_ADDR            0xE0000000
#define CFCARD_GPIO_BASE            (CFG_CCSRBAR + 0x000E0000)
#define CFCARD_BASE_SIZE            0x00020000
#define CFCARD_GPIO_SIZE            0x100
#define CF_STANDARD_SECTOR_SIZE     512
#define CF_REGISTER_OFFSET          0x00010000 
#define CF_RAM_OFFSET               0x00010400 
#define CF_ATTRIBUTE_OFFSET         0x00000200

#define CFCARD_SECS     1000000
#define CFCARD_NR_PORT  16
#define COMMAND_SIZE    8
#define DRIVER_CF_NAME "cfcard"

#define CFCARD_TIMEOUT  10 * HZ

/* cf block device related definition */
#define HARDSECT_SIZE       0x200
#define KERNEL_SECTOR_SIZE  0x200
#define CF_SECT_SIZE        0x200
#define CF_SECT_PARA        (32 * 4)
#define CF_MAX_SECT_PER_CMD 0x100
#define CFCARD_QUEUE_HARD_SECTOR_SIZE 0x200
#define QUEUE_REQUEST_FAILED    0
#define QUEUE_REQUEST_SUCCESS   1

#define CF_TRANS_FAILED         0
#define CF_TRANS_OK             1

enum trans_state {
    TS_IDLE = 0,
    TS_READY,
    TS_TRANS
};

typedef struct cfcard_device {
    int size;                       /* Device size in sectors */
    u8 *data;                       /* The data array */
    int users;                      /* How many users */
    spinlock_t lock;                /* For mutual exclusion */
    struct request_queue *queue;    /* The device request queue */
    struct gendisk *gd;             /* The gendisk structure */
    unsigned long start_sector;     /* The partition's start sector */
}CF_DEVICE;

typedef struct CF_INFO_S 
{
    enum trans_state tstate; /* Transaction state */
    char *tbuf;
    unsigned long tbuf_size;
    sector_t tsect_start;
    unsigned long tsector_count;
    unsigned long tsectors_left;
    unsigned long tread;
    unsigned long tcmd;

    unsigned long head;
    unsigned long cyl;
    unsigned long spt;
    unsigned long sectors;
    unsigned long block_size;

    void *base;      /* base address for I/O */
    void *attr_base; /* attribute base address */
    void *reg_base;  /* register base address */
    void *ram_base;  /* ram base address */

    CF_DEVICE *device;
    struct timer_list status_timer;
    spinlock_t lock;
}CF_INFO_T;

typedef struct cfcard_device_info {
    unsigned long start_sector;
    unsigned long nsectors;
    char device_name[32]; /* u-boot-1.3.2's max device namelen is 64, but gendisk's disk name is limited to 32 */
}CFCARD_DEVICE_INFO_T;

#endif /* __CFCARD_H__ */

--------------------------------------------------------------------------
 
-----------------------------cfcard.c---------------------------------
/***********************************************************************
 * cfcard.c : Compact flash block driver, complying with CF 4.1 specification.
 * This driver implements the compact flash hardware operation and block layer.
 * It works on PC Card memory mode
 *
 * Changelog:
 * Jerry.du -- 12/24/2008, writes the driver based on the ata driver
 * 
 ************************************************************************/

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include

#include "partition_table.h" /* which include partition table definition, project related */
#include "cfcard.h"

#define MODULE_NAME     "cfcard"
//#define DEBUG
#undef TRACE
#ifdef DEBUG
#ifdef __KERNEL__
#define TRACE printk(KERN_EMRG "%s -- %s(): %d\n", MODULE_NAME, __FUNCTION__, __LINE__)
#else
#define TRACE printf("%s -- %s(): %d\n", MODULE_NAME, __FUNCTION__, __LINE__)
#endif /* __KERNEL__ */
#else
#define TRACE
#endif /* DEBUG */

static CF_INFO_T *cfcard_info;
static int cfcard_device_nums = 0;
static void __iomem *cfcard_gpio_base = NULL;

static CFCARD_DEVICE_INFO_T cf_device_info[CFCARD_DEVICE_MAXNUM] = {
    {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}},
    {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}},
    {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}},
    {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}},
    {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}},
    {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}},
    {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}},
    {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}}
};

#define CF_REGISTER(x)      ((volatile u8 *)(cfcard_info->reg_base + (x)))
#define CF_ATTRIBUTE(x)     ((volatile u8 *)(cfcard_info->attr_base + (x)))
#define CF_BUFFER           ((volatile u32 *)((unsigned long)cfcard_info->ram_base))

#define cfcard_outb(a, b)   (*(volatile u8 *)(cfcard_info->reg_base + (b)) = (a))
#define cfcard_inb(a)       ((u8)*(volatile u8 *)(cfcard_info->reg_base + (a)))

static int cfcard_hw_init(CF_INFO_T *cf_info);
static int cfcard_identify(CF_INFO_T *cf_info);
static inline int cfcard_isready(CF_INFO_T *cf_info);
static inline int cfcard_wait4ready(CF_INFO_T *cf_info, int to_us, u8 status_mask);
static int cfcard_set_cmd(CF_INFO_T *cf_info);
static int cfcard_do_trans(CF_INFO_T *cf_info);
static int cfcard_do_state(CF_INFO_T *cf_info);
static u32 cfcard_do_transfer(CF_INFO_T *cf_info, sector_t sector, 
    unsigned long nsect, u8 *buffer, int is_write);
static int cfcard_do_reset(CF_INFO_T *cf_info);
static int cfcard_detect(void);
static void cfcard_get_status(unsigned long dev_l);
static int cfcard_bdev_release(struct inode *inode, struct file *filp);
static int cfcard_bdev_transfer(CF_DEVICE *dev, sector_t sector,
    unsigned long nsect, char *buffer, int is_write);
static void cfcard_bdev_request(request_queue_t *q);
static int cfcard_bdev_open(struct inode *inode, struct file *filp);
static int cfcard_bdev_setup_device(CF_DEVICE *dev, int which);
static int __init cfcard_bdev_init(void);
static void cfcard_bdev_cleanup(void);

static int cfcard_hw_init(CF_INFO_T *cf_info)
{
    int ret = RC_ERROR;
    u8 regv;
  
    KDEBUG("cfcard_hw_init\n");

    TRACE;
    cfcard_gpio_base = ioremap(CFCARD_GPIO_BASE, CFCARD_GPIO_SIZE);
    if (cfcard_gpio_base == NULL)
goto MEM_ERR;

    TRACE;
    cf_info->base = ioremap(CFCARD_BASE_ADDR, CFCARD_BASE_SIZE);
    if (cf_info->base == NULL)
goto MEM_ERR;

    KDEBUG("Ioremap:cfcard_cf_base = 0x%x\n", (u32)cf_info->base);
    cf_info->attr_base = cf_info->base + CF_ATTRIBUTE_OFFSET;
    cf_info->reg_base  = cf_info->base + CF_REGISTER_OFFSET;
    cf_info->ram_base  = cf_info->base + CF_RAM_OFFSET;

    TRACE;
    if (cfcard_detect() == RC_ERROR) {
        printk(KERN_ERR "cf card is not inserted!\n");
     goto END;
    }
    
    TRACE;
    if (cfcard_do_reset(cf_info) != CF_TRANS_OK) {
        printk(KERN_ERR "cf reset failed\n");
        goto END;
    }

#if 0 //for test
    printk("*CF_ATTRIBUTE(0) 0x%02x\n", *CF_ATTRIBUTE(0));
    printk("*CF_ATTRIBUTE(2) 0x%02x\n", *CF_ATTRIBUTE(2));
    printk("*CF_ATTRIBUTE(4) 0x%02x\n", *CF_ATTRIBUTE(4));
    printk("*CF_ATTRIBUTE(6) 0x%02x\n", *CF_ATTRIBUTE(6));
    
    printk("CF_REG_FR 0x%02x\n", cfcard_inb(CF_REG_FR));
    printk("CF_REG_SC 0x%02x\n", cfcard_inb(CF_REG_SC));
    printk("CF_REG_SN 0x%02x\n", cfcard_inb(CF_REG_SN));
    printk("CF_REG_CL 0x%02x\n", cfcard_inb(CF_REG_CL));
    printk("CF_REG_CH 0x%02x\n", cfcard_inb(CF_REG_CH));
    printk("CF_REG_DH 0x%02x\n", cfcard_inb(CF_REG_DH));
    printk("CF_REG_ST 0x%02x\n", cfcard_inb(CF_REG_ST));
    printk("CF_REG_ER 0x%02x\n", cfcard_inb(CF_REG_ER));
    printk("CF_REG_DC 0x%02x\n", cfcard_inb(CF_REG_DC));
    printk("CF_REG_CAR 0x%02x\n", cfcard_inb(0x0f));
    printk("\n");
#endif

    TRACE;
    init_timer(&cf_info->status_timer);
    cf_info->status_timer.function = cfcard_get_status;
    cf_info->status_timer.data = (unsigned long)cf_info;
    cf_info->status_timer.expires = jiffies + CFCARD_TIMEOUT; 
    add_timer(&cf_info->status_timer);

    TRACE;
    cf_info->block_size = 1;
    cfcard_outb(0x00, CF_REG_DH);

    /* cfspec 4.1 page 116, diagostics code 0x01 means that no error detected */
    TRACE;
    cfcard_outb(CF_CMD_EXEC_DRIVE_DIAG, CF_REG_ST);
    mdelay(500);
    regv = cfcard_inb(CF_REG_FR);
    if (regv != 0x01) {
        printk(KERN_ERR "cf card diag error %x\n", regv);
        goto END;
    }
    
    TRACE;
    ret = cfcard_identify(cf_info);
    if (!ret) {
        printk(KERN_ERR "cfcard_id_device failed!\n");
goto END;
    }
    
    return RC_OK;

MEM_ERR:
    printk(KERN_ERR "memory error!\n");
END:
    if (cfcard_gpio_base)
        iounmap(cfcard_gpio_base);
    if (cf_info->base)
        iounmap(cf_info->base);
    del_timer(&cf_info->status_timer);
    return ret;
}

static int cfcard_identify(CF_INFO_T *cf_info)
{
    u8 sbuf[CF_SECT_SIZE];
    int ret, vendor;
    char tstr[21]; //serial number

    BUG_ON(cf_info->tstate != TS_IDLE);

    TRACE;
    memset(sbuf, 0, sizeof(sbuf));
    spin_lock(&cf_info->lock);
    cf_info->tbuf = (char *)sbuf;
    cf_info->tbuf_size = CF_SECT_SIZE;
    cf_info->tsect_start = 0;
    cf_info->tsector_count = 0;
    cf_info->tsectors_left = 1;
    cf_info->tread = 1;
    cf_info->tcmd = CF_CMD_IDENTIFY_DRIVE;
    spin_unlock(&cf_info->lock);

    KDEBUG("cfcard_identify tsect_start %llx tsector_count %ld tsectors_left %ld tstate %d tread %ld tcmd %ld\n",
        cf_info->tsect_start, cf_info->tsector_count, cf_info->tsectors_left,
        cf_info->tstate, cf_info->tread, cf_info->tcmd);

    printk(KERN_INFO "%s: identify drive..\n", MODULE_NAME);
    ret = cfcard_do_state(cf_info);
    if (ret != CF_TRANS_OK) {
        printk(KERN_ERR "%s: BUG: identify failed\n", MODULE_NAME);
        return 0;
    }

    TRACE;
    vendor           = (sbuf[1] << 8) | sbuf[0];
    cf_info->head    = (sbuf[109] << 8) | sbuf[108];
    cf_info->cyl     = (sbuf[111] << 8) | sbuf[110];
    cf_info->spt     = (sbuf[113] << 8) | sbuf[112];
    cf_info->sectors = ((sbuf[117] << 24) | (sbuf[116] << 16) | (sbuf[115] <<  8) | sbuf[114]);
    memcpy(tstr, &sbuf[20], 20);
    tstr[21] = 0;
    printk(KERN_INFO "%s: %s detected, C/H/S=%ld/%ld/%ld sectors=%lu (%luMB) Serial=%s\n",
        MODULE_NAME, (vendor == 0x848A ? "CF card" : "ATA drive"), cf_info->cyl, cf_info->head,
        cf_info->spt, cf_info->sectors, cf_info->sectors >> 11, tstr);

    return 1;
}

/* CF ready line is set high when the compact flash is ready to accept a new transfer operation */
static inline int cfcard_isready(CF_INFO_T *cf_info)
{
    return (*CFCARD_GPIO_GPINDR(cfcard_gpio_base) & CF_READY);
}

static inline int cfcard_wait4ready(CF_INFO_T *cf_info, int to_us, u8 status_mask)
{
    int us_passed = 0;
    u8 regv;

    if (!cfcard_isready(cf_info)) {
        /* busy must appear within 400ns,
         * but it may dissapear before we see it
         *  => must not wait for busy in a loop
         */
        ndelay(400);
    }

    /*
     * cfspec 4.1 page 128, write sector is class 2 of command,
     * cf card set up the sector buffer for a write operation and set DRQ within 700 usec
     */
    if (!cf_info->tread)
        udelay(700);

    while (us_passed++ < to_us) {
        regv = cfcard_inb(CF_REG_ST);
        if ((regv & 0xff) == status_mask)
       return CF_TRANS_OK;

        if (us_passed == 1 * CFCARD_SECS)
            printk(KERN_WARNING "%s: wait for ready %dus..\n", MODULE_NAME, to_us);

        udelay(1);
    }

    printk(KERN_ERR "%s: wait for ready timeout (%dus)"
               ", status 0x%02x\n", MODULE_NAME, to_us, cfcard_inb(CF_REG_ST));

    cfcard_do_reset(cf_info);
    return CF_TRANS_FAILED;
}

static int cfcard_set_cmd(CF_INFO_T *cf_info)
{
    /* sector_count should be <=24 bits.. */
    BUG_ON(cf_info->tsect_start >= 0x10000000);

    KDEBUG("cfcard_set_cmd tsect_start %llx tsector_count %ld tsectors_left %ld tstate %d tread %ld tcmd %x\n",
        cf_info->tsect_start, cf_info->tsector_count, cf_info->tsectors_left,
        cf_info->tstate, cf_info->tread, cf_info->tcmd);

    cfcard_outb(0x00, CF_REG_FR);
    if (cf_info->tsector_count) {
        cfcard_outb(cf_info->tsector_count & 0xff, CF_REG_SC);
        cfcard_outb(cf_info->tsect_start & 0xff, CF_REG_SN);
        cfcard_outb((cf_info->tsect_start >> 8) & 0xff, CF_REG_CL);
        cfcard_outb((cf_info->tsect_start >> 16) & 0xff, CF_REG_CH);
    }
    cfcard_outb(((cf_info->tsect_start >> 24) & 0x0f) | CF_REG_DH_BASE | CF_REG_DH_LBA, CF_REG_DH);
    cfcard_outb(cf_info->tcmd, CF_REG_CMD);

    return cfcard_wait4ready(cf_info, 1 * CFCARD_SECS,
        CF_REG_ST_RDY | CF_REG_ST_DSC | CF_REG_ST_DRQ);
}

static int cfcard_do_trans(CF_INFO_T *cf_info)
{
    int ret;
    u8 st;
    int errId;
    u32 *qbuf, *qend;
    volatile u32 *ram_start;

    //printk("cfcard_do_trans: %ld sectors left\n", cf_info->tsectors_left);
    TRACE;
    while (cf_info->tsectors_left) {
        st = cfcard_inb(CF_REG_ST);
        if (!(st & CF_REG_ST_DRQ)) {
            printk(KERN_ERR "%s: do_trans without DRQ (status 0x%x)!\n", MODULE_NAME, st);
            if (st & CF_REG_ST_ERR) {
                errId = cfcard_inb(CF_REG_ER);
                printk(KERN_ERR "%s: %s error, status 0x%x, errid 0x%x\n",
                    MODULE_NAME, (cf_info->tread ? "read" : "write"), st, errId);
            }
            return CF_TRANS_FAILED;
        }

        do { /* fill/read the buffer of one block */
            ram_start = CF_BUFFER;
            qbuf = (u32 *)cf_info->tbuf;
            qend = qbuf + CF_SECT_SIZE / sizeof(u32);
            if (cf_info->tread) {
                while (qbuf != qend)
                    *qbuf++ = *ram_start++;
            }
            else {
                while (qbuf != qend)
                    *ram_start++ = *qbuf++;
            }

            spin_lock(&cf_info->lock);
            cf_info->tsectors_left--;
            cf_info->tbuf += CF_SECT_SIZE;
            cf_info->tbuf_size -= CF_SECT_SIZE;
            spin_unlock(&cf_info->lock);

            if (cf_info->tsectors_left) {
                ret = cfcard_wait4ready(cf_info, 3 * CFCARD_SECS, 
                    CF_REG_ST_RDY | CF_REG_ST_DSC | CF_REG_ST_DRQ);
                if (ret != CF_TRANS_OK)
                    return ret;
            }
            else {
                ret = cfcard_wait4ready(cf_info, 3 * CFCARD_SECS, 
                    CF_REG_ST_RDY | CF_REG_ST_DSC);
                if (ret != CF_TRANS_OK)
                    return ret;
            }
        } while (cf_info->tsectors_left > 0 && cf_info->tbuf_size > 0);
    };

    TRACE;
    st = cfcard_inb(CF_REG_ST);
    if (st & (CF_REG_ST_DRQ | CF_REG_ST_DWF | CF_REG_ST_ERR)) {
        if (st & CF_REG_ST_DRQ) {
            printk(KERN_ERR "%s: DRQ after all %ld sectors are %s"
                ", status 0x%x\n", MODULE_NAME, cf_info->tsector_count,
                (cf_info->tread ? "read" : "written"), st);
        } else if (st & CF_REG_ST_DWF) {
            printk(KERN_ERR "%s: write fault, status 0x%x\n", MODULE_NAME, st);
        } else {
            errId = cfcard_inb(CF_REG_ER);
            printk(KERN_ERR "%s: %s error, status 0x%x, errid 0x%x\n",
                MODULE_NAME, (cf_info->tread ? "read" : "write"), st, errId);
        }
        return CF_TRANS_FAILED;
    }

    //printk("cfcard_do_trans: end\n");
    return CF_TRANS_OK;
 
static int cfcard_do_state(CF_INFO_T *cf_info)
    int ret;

    //printk("cfcard_do_state begin %d\n", cf_info->tstate);
    TRACE;
    switch (cf_info->tstate) {
        case TS_IDLE:
            //printk("TS_IDLE\n");
            cf_info->tstate = TS_READY;
        
        case TS_READY:
            //printk("TS_READY\n");
            cf_info->tstate = TS_TRANS;
            ret = cfcard_set_cmd(cf_info);
            if (ret != CF_TRANS_OK) 
                break;

        case TS_TRANS:
            //printk("TS_TRANS\n");
            ret = cfcard_do_trans(cf_info);
            //printk("cfcard_do_trans end %d\n", ret);
            break;

        default:
            printk(KERN_ERR "%s: BUG: unknown tstate %d\n", MODULE_NAME, cf_info->tstate); 
            return CF_TRANS_FAILED;
    }

    if (cf_info->tstate != TS_TRANS)
        printk(KERN_ERR "cfcard_do_state error %d\n", cf_info->tstate);
    cf_info->tstate = TS_IDLE;
    
    //printk("cfcard_do_state end %d\n", cf_info->tstate);
    return ret; 

static u32 cfcard_do_transfer(CF_INFO_T *cf_info, sector_t sector, 
    unsigned long nsect, u8 *buffer, int is_write)
{
    BUG_ON(cf_info->tstate != TS_IDLE);

    TRACE;
    //printk(KERN_INFO "cfcard_do_transfer sector %llx nsect %ld is_write %d\n\n",
        //sector, nsect, is_write);
    if (nsect > CF_MAX_SECT_PER_CMD) {
        printk(KERN_WARNING "%s: sector count %lu out of range\n", MODULE_NAME, nsect);
        return CF_TRANS_FAILED;
    }
    
    if (sector + nsect > cf_info->sectors) {
        printk(KERN_WARNING "%s: sector %llx out of range\n", MODULE_NAME, sector);
        return CF_TRANS_FAILED;
    }

    TRACE;
    spin_lock(&cf_info->lock);
    cf_info->tbuf          = buffer;
    cf_info->tbuf_size     = nsect * CF_SECT_SIZE;
    cf_info->tsect_start   = sector;
    cf_info->tsector_count = nsect;
    cf_info->tsectors_left = cf_info->tsector_count;
    cf_info->tread         = (is_write) ? 0 : 1;
    cf_info->tcmd          = (cf_info->block_size == 1 ?
        (is_write ? CF_CMD_WRITE_SECTORS : CF_CMD_READ_SECTORS) :
        (is_write ? CF_CMD_WRITE_MULTIPLE : CF_CMD_READ_MULTIPLE));
    spin_unlock(&cf_info->lock);

    TRACE;
    return cfcard_do_state(cf_info);
}

static int cfcard_do_reset(CF_INFO_T *cf_info)
{
    int ret = CF_TRANS_OK;
    
    printk(KERN_INFO "%s: resetting..\n", MODULE_NAME);

    /* conf5~conf0 is set to 0, using memory mapped mode */
    TRACE;
    *CF_ATTRIBUTE(0) = 0x80;
    mdelay(20);
    *CF_ATTRIBUTE(0) = 0x00;
    
    /* disable the interrupt */
    TRACE;
    cfcard_outb(CF_REG_DC_SRST | CF_REG_DC_IEN, CF_REG_DC);
    udelay(100);
    cfcard_outb(CF_REG_DC_IEN, CF_REG_DC);

    TRACE;
    ret = cfcard_wait4ready(cf_info, 2 * CFCARD_SECS, CF_REG_ST_RDY | CF_REG_ST_DSC);
    printk(KERN_INFO "done\n");

    return ret;
}

static int cfcard_detect(void)
{
    volatile u32 cf_detect = *CFCARD_GPIO_GPINDR(cfcard_gpio_base);

    KDEBUG("cfcard_detect\n");
    TRACE;
    //printk("CFCARD_GPIO_GPINDR 0x%08x\n",       *CFCARD_GPIO_GPINDR(cfcard_gpio_base));
    if ((cf_detect & (CF_CD1 | CF_CD2)) == 0) {
        KDEBUG("cfcard_detect success\n");
        return RC_OK;
    }
    else {
       printk(KERN_ERR "cfcard_detect error\n");
       return RC_ERROR;
    }
}

/* print out the register temporarily */
static void cfcard_get_status(unsigned long dev_l)
{
#if 0
    CF_INFO_T *cfinfo = (CF_INFO_T *)dev_l;

    //printk("*CF_ATTRIBUTE(0) 0x%02x\n", *CF_ATTRIBUTE(0));
    //printk("*CF_ATTRIBUTE(2) 0x%02x\n", *CF_ATTRIBUTE(2));
    //printk("*CF_ATTRIBUTE(4) 0x%02x\n", *CF_ATTRIBUTE(4));
    //printk("*CF_ATTRIBUTE(6) 0x%02x\n", *CF_ATTRIBUTE(6));
    
    printk("CF_REG_FR 0x%02x\n", cfcard_inb(CF_REG_FR));
    printk("CF_REG_SC 0x%02x\n", cfcard_inb(CF_REG_SC));
    printk("CF_REG_SN 0x%02x\n", cfcard_inb(CF_REG_SN));
    printk("CF_REG_CL 0x%02x\n", cfcard_inb(CF_REG_CL));
    printk("CF_REG_CH 0x%02x\n", cfcard_inb(CF_REG_CH));
    printk("CF_REG_DH 0x%02x\n", cfcard_inb(CF_REG_DH));
    printk("CF_REG_ST 0x%02x\n", cfcard_inb(CF_REG_ST));
    printk("CF_REG_ER 0x%02x\n", cfcard_inb(CF_REG_ER));
    printk("CF_REG_DC 0x%02x\n", cfcard_inb(CF_REG_DC));
    //printk("CF_REG_CAR 0x%02x\n", cfcard_inb(0x0f));
    printk("\n");

    cfinfo->status_timer.expires = jiffies + CFCARD_TIMEOUT;
    add_timer(&cfinfo->status_timer);
#endif
}

static int cfcard_bdev_release(struct inode *inode, struct file *filp)
{
CF_DEVICE *dev = inode->i_bdev->bd_disk->private_data;
    
    KDEBUG("cfcard_bdev_release\n");
    TRACE;
spin_lock(&dev->lock);
dev->users--;
spin_unlock(&dev->lock);
return RC_OK;
}

static int cfcard_bdev_transfer(CF_DEVICE *dev, sector_t sector,
    unsigned long nsect, char *buffer, int is_write)
{
    sector_t start_sector;
    int ret = RC_ERROR;

    TRACE;
    if (nsect <= 0)
        goto END;

    start_sector = dev->start_sector + sector;
    //printk(KERN_INFO "cfcard_bdev_transfer sector %llx(%lx) nsect %ld is_write %d\n",
        //start_sector, dev->start_sector, nsect, is_write);
    TRACE;
    ret = cfcard_do_transfer(cfcard_info, start_sector, nsect, buffer, is_write);

END:
    return ret;
}

static void cfcard_bdev_request(request_queue_t *q)
{
    struct request  *req = NULL;
    CF_DEVICE       *dev = NULL;
    unsigned retval = 0;
    int rw;

    TRACE;
    while ((req = elv_next_request(q)) != NULL) {
        dev = req->rq_disk->private_data;
        //printk("dev->start_sector 0x%x\n", dev->start_sector);
        if (!blk_fs_request(req)) {
            printk(KERN_NOTICE "cfcard_bdev_request: skip non-fs request\n");
            end_request(req, QUEUE_REQUEST_FAILED);
            continue;
        }

        if ((req->sector + req->nr_sectors) > get_capacity(req->rq_disk)) {
            printk(KERN_NOTICE "Out of range\n");
            end_request(req, QUEUE_REQUEST_FAILED);
            continue;
        }

        rw = rq_data_dir(req);
        if (rw != READ && rw != WRITE) {
            printk(KERN_NOTICE "cfcard_bdev_request: unknown request\n");
            end_request(req, QUEUE_REQUEST_FAILED);
            continue;
        }

        //printk("rw-%d,s-%llx,n-%lx,c-%x,hs-%llx,hn-%lx,hc-%x\n", rw,
            //req->sector, req->nr_sectors, req->current_nr_sectors,
            //req->hard_sector, req->hard_nr_sectors, req->hard_cur_sectors);
        TRACE;
        if (req->current_nr_sectors > 8)
            printk("***%d sectors\n", req->current_nr_sectors);

        retval = cfcard_bdev_transfer(dev, req->sector,
     req->current_nr_sectors, req->buffer, rw);
        if (retval < 0) {
            printk(KERN_NOTICE "cfcard_bdev_request: skip failed request\n");
            end_request(req, QUEUE_REQUEST_FAILED);
            continue;
        }

        end_request(req, QUEUE_REQUEST_SUCCESS);
    }
}

static int cfcard_bdev_open(struct inode *inode, struct file *filp)
{
    CF_DEVICE *dev = inode->i_bdev->bd_disk->private_data;
    
    KDEBUG("cfcard_bdev_open\n");
    TRACE;
    spin_lock(&dev->lock);
    dev->users++;
    filp->private_data = dev;
    spin_unlock(&dev->lock);

    return RC_OK;
}

static struct block_device_operations cfcard_fops = {
    .owner          = THIS_MODULE,
    .open           = cfcard_bdev_open,
    .release        = cfcard_bdev_release,
    .media_changed  = NULL,
    .ioctl          = NULL,
};

/*
 * Set up our internal device.
 */
static int cfcard_bdev_setup_device(CF_DEVICE *dev, int which)
{
    CFCARD_DEVICE_INFO_T *ptr_device;
    int ret = RC_ERROR;

    TRACE;
    if (which < 0 || which > cfcard_device_nums) {
        printk(KERN_ERR "device num should be 0 to %d\n", cfcard_device_nums);
        goto out;
    }

    ptr_device = (CFCARD_DEVICE_INFO_T *)&cf_device_info[which];
    memset(dev, 0, sizeof(CF_DEVICE));
    dev->size = HARDSECT_SIZE * ptr_device->nsectors;
    spin_lock_init(&dev->lock);
    KDEBUG("cfcard_bdev_device device %d size %d\n", which, dev->size);

    /*
     * The I/O queue, depending on whether we are using our own
     * make_request function or not.
     */
    TRACE;
    dev->queue = blk_init_queue(cfcard_bdev_request, &dev->lock);
    if (dev->queue == NULL) {
        printk(KERN_ERR "%s: no mem for queue\n", MODULE_NAME);
goto out;
    }
    blk_queue_hardsect_size(dev->queue, CFCARD_QUEUE_HARD_SECTOR_SIZE);
    dev->queue->queuedata = dev;
    
    /*
     * And the gendisk structure.
     */
    KDEBUG("cfcard_bdev_device alloc_disk\n");
    TRACE;
    dev->gd = alloc_disk(CFCARD_MINORS);
    if (dev->gd == NULL) {
        printk(KERN_ERR "alloc_disk failure\n");
        goto out;
    }
    dev->gd->major = CFCARD_MAJOR;
    dev->gd->first_minor = which * CFCARD_MINORS;
    dev->gd->fops = &cfcard_fops;
    dev->gd->queue = dev->queue;
    dev->gd->private_data = dev;
    dev->start_sector = ptr_device->start_sector;

    TRACE;
    snprintf(dev->gd->disk_name, 32, ptr_device->device_name);
    set_capacity(dev->gd, ptr_device->nsectors * (HARDSECT_SIZE / KERNEL_SECTOR_SIZE));

    KDEBUG("cfcard_bdev_device add_disk\n");
    TRACE;
    add_disk(dev->gd);
    ret = RC_OK;

out:
    return ret;
}

static int __init cfcard_bdev_init(void)
{
    PARTITION_TABLE_T   pt_table;
    FlashRegion_T       *ptr_region;
    int ret = RC_ERROR;
    int i, j;

    KDEBUG("Initialize compact flash block device ...\n");

    TRACE;
    cfcard_info = (CF_INFO_T *)kmalloc(sizeof(CF_INFO_T), GFP_KERNEL);
    if (!cfcard_info)
        goto mem_err;
    memset(cfcard_info, 0, sizeof(CF_INFO_T));
    spin_lock_init(&cfcard_info->lock);
    //printk("cfcard_info 0x%08x\n", cfcard_info);

    /* initial compact flash hardware */
    TRACE;
    if (cfcard_hw_init(cfcard_info) != RC_OK) {
        printk(KERN_ERR DRIVER_CF_NAME": Unable to init cf hardware\n");
        goto out;
    }

    KDEBUG("cfcard_major is %d, driver_name is %s\n", CFCARD_MAJOR, DRIVER_CF_NAME);
    TRACE;
    ret = register_blkdev(CFCARD_MAJOR, DRIVER_CF_NAME);
    if (ret < 0) {
        printk(KERN_ERR DRIVER_CF_NAME": Unable to register block device\n");
        goto out;
    }

    /* 
     * read partition table information from physical address
     */
    TRACE;
    memset(&pt_table, 0, sizeof(PARTITION_TABLE_T));
    if (flash_get_partitiontable(&pt_table) != RC_OK) {
        printk(KERN_ERR "Flash get partition table failed!\n");
        goto out;
    }

    /* 
     * parse the partition table and setup the mtd partitions structure
     */
    TRACE;
    j = 0;
    for (i = 0; i < MAX_PARTITION_NUMBER; i++) {
        ptr_region = &pt_table.flash_region[i];
        if (ptr_region->region_type == TYPE_NOT_USED)
            continue;

        /* 
         * we only parse "cfcard" typed device, the cfcard device is sized in bytes
         *  in u-boot-1.3.2, and the linux block driver sizes the disk as sector
*/
        if (strcmp(ptr_region->device_name, DEVICE_CFCARD_NAME) == 0) {
            snprintf(cf_device_info[j].device_name, 32, ptr_region->region_name);
            cf_device_info[j].start_sector = ptr_region->region_base / HARDSECT_SIZE;
            cf_device_info[j].nsectors= ptr_region->region_size / HARDSECT_SIZE;
            j++;
            cfcard_device_nums++;
        }
    }

    if (cfcard_device_nums == 0) {
        printk(KERN_ERR "System have no cfcard device!\n");
        goto out;
    }

    TRACE;
    cfcard_info->device = (CF_DEVICE *)kmalloc(cfcard_device_nums * sizeof(CF_DEVICE), GFP_KERNEL);
    if (cfcard_info->device == NULL) {
        printk(KERN_ERR "vmalloc failure.\n");
        goto out;
    }

    TRACE;
    for (i = 0; i < cfcard_device_nums; i++)
        cfcard_bdev_setup_device(cfcard_info->device + i, i);
        
    KDEBUG("Done!\n");
    return RC_OK;
    
out:
    unregister_blkdev(CFCARD_MAJOR, DRIVER_CF_NAME);
mem_err:
    if (cfcard_info)
        kfree(cfcard_info);

    return ret;
}

static void cfcard_bdev_cleanup(void)
{
    int i;

    KDEBUG("Remove compact flash block device ...\n");
    TRACE;
    for (i = 0; i < cfcard_device_nums; i++) {
        CF_DEVICE *dev = cfcard_info->base + i;

        if (dev->gd) {
            del_gendisk(dev->gd);
            put_disk(dev->gd);
        }
        if (dev->queue)
            blk_cleanup_queue(dev->queue);
    }
    TRACE;
    unregister_blkdev(CFCARD_MAJOR, DRIVER_CF_NAME);
    if (cfcard_info->base)
        iounmap(cfcard_info->base);
    kfree(cfcard_info->device);
    del_timer(&cfcard_info->status_timer);
    if (cfcard_info)
        kfree(cfcard_info);
    if (cfcard_gpio_base)
        iounmap(cfcard_gpio_base);

    KDEBUG("Done!\n");
}

module_init(cfcard_bdev_init);
module_exit(cfcard_bdev_cleanup);

MODULE_DESCRIPTION("Compact flash block driver");
MODULE_AUTHOR("Jerry.du");
MODULE_LICENSE("Dual BSD/GPL");

---------------------------------------------------------------------------
 
the attached files are source code.
 
文件:cfcard.rar
大小:8KB
下载:下载
阅读(992) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~