VIVI中MTD驱动的实现(1)

在vivi中使用的flash有nor和nand,而mtd的作用就是提供一个中间层的驱动,实现接口函数的统一管理,这里首先介绍nand flash在mtd中的实现。

在vivi bootloader中,第6步的时候就是实现mtd中间驱动的实现,MTD驱动的函数调用关系如下:

mtd_dev_init()---->mtd_init()---->smc_init()在这里需要说明,mtd_init()函数可以按照配置调用不同的函数,包括cfi_init(),smc_init(),amd_init(),这里不同的函数对应不同的flash设备的初始化。

其中cfi_init()是intel发起的nor flash的接口标准。

    smc_init()是smc智能卡接口,我们使用的nand flash使用的就是这个接口

    amd_init()是AMD flash接口

 

在完成上面初始化以后则是增加flash命令,这部分于后面的增加命令相似(关于命令的部分在后面的章节会有专门的说明)

              ---->add_command(&flash_command)

 

下面来具体看看函数的实现,首先我们要注意到的是两个数据结构,分别是mtd_info(mtd_info是表示MTD设备的结构,每个分区也被表示为一个mtd_info,如果有两个MTD设备,每个设备有三个分区,那么在系统中就一共有6个mtd_info结构),一下是vivi中mtd_info结构

 

struct mtd_info {
    u_char type;
    u_int32_t flags;
    u_int32_t size; // Total size of the MTD


    /* "Major" erase size for the device. Naïve users may take this
     * to be the only erase size available, or may use the more detailed
     * information below if they desire
     */
    u_int32_t erasesize;

    u_int32_t oobblock; // Size of OOB blocks (e.g. 512)

    u_int32_t oobsize; // Amount of OOB data per block (e.g. 16)

    u_int32_t ecctype;
    u_int32_t eccsize;

    // Kernel-only stuff starts here.

    char *name;
    int index;

    /* Data for variable erase regions. If numeraseregions is zero,
     * it means that the whole device has erasesize as given above. 
     */
    int numeraseregions;
    struct mtd_erase_region_info *eraseregions;

    /* This really shouldn't be here. It can go away in 2.5 */
    u_int32_t bank_size;

    struct module *module;
    int (*erase) (struct mtd_info *mtd, struct erase_info *instr);

    /* This stuff for eXecute-In-Place */
    int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf);

    /* We probably shouldn't allow XIP if the unpoint isn't a NULL */
    void (*unpoint) (struct mtd_info *mtd, u_char * addr);
    int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

    int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf);
    int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf);

    int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

    /* 
     * Methods to access the protection register area, present in some 
     * flash devices. The user data is one time programmable but the
     * factory data is read only. 
     */
    int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);

    int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);

    /* This function is not yet implemented */
    int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);


    /* Chip-supported device locking */
    int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);
    int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);

    void *priv;
};


还有一个重要的结构nand_chip,这个结构中包含了nand flash所有的信息

 

/*
 * NAND Private Flash Chip Data
 *
 * Structure overview:
 *
 * IO_ADDR_R - address to read the 8 I/O lines of the flash device 
 *
 * IO_ADDR_W - address to write the 8 I/O lines of the flash device 
 *
 * hwcontrol - hardwarespecific function for accesing control-lines
 *
 * dev_ready - hardwarespecific function for accesing device ready/busy line
 *
 * chip_lock - spinlock used to protect access to this structure
 *
 * wq - wait queue to sleep on if a NAND operation is in progress
 *
 * state - give the current state of the NAND device
 *
 * page_shift - number of address bits in a page (column address bits)
 *
 * data_buf - data buffer passed to/from MTD user modules
 *
 * data_cache - data cache for redundant page access and shadow for
 * ECC failure
 *
 * ecc_code_buf - used only for holding calculated or read ECCs for
 * a page read or written when ECC is in use
 *
 * reserved - padding to make structure fall on word boundary if
 * when ECC is in use
 */
struct nand_chip {
#ifdef CONFIG_MTD_NANDY
    void (*hwcontrol)(int cmd);
    void (*write_cmd)(u_char val);
    void (*write_addr)(u_char val);
    u_char (*read_data)(void);
    void (*write_data)(u_char val);
    void (*wait_for_ready)(void);
    /*spinlock_t chip_lock;*/
    /*wait_queue_head_t wq;*/
    /*nand_state_t state;*/
    int page_shift;
    u_char *data_buf;
    u_char *data_cache;
    int cache_page;
    struct nand_smc_dev *dev;
    u_char spare[SMC_OOB_SIZE];
#else /* CONFIG_MTD_NANDY */
    unsigned long IO_ADDR_R;
    unsigned long IO_ADDR_W;
    void (*hwcontrol)(int cmd);
    int (*dev_ready)(void);
    int chip_delay;
    /*spinlock_t chip_lock;*/
    /*wait_queue_head_t wq;*/
    /*nand_state_t state;*/
    int page_shift;
    u_char *data_buf;
    u_char *data_cache;
    int cache_page;
#ifdef CONFIG_MTD_NAND_ECC
    u_char ecc_code_buf[6];
    u_char reserved[2];
#endif
#endif /* CONFIG_MTD_NANDY */
};


/*
 * NAND Flash Device ID Structure
 *
 * Structure overview:
 *
 * name - Complete name of device
 *
 * manufacture_id - manufacturer ID code of device.
 *
 * model_id - model ID code of device.
 *
 * chipshift - total number of address bits for the device which
 * is used to calculate address offsets and the total
 * number of bytes the device is capable of.
 *
 * page256 - denotes if flash device has 256 byte pages or not.
 *
 * pageadrlen - number of bytes minus one needed to hold the
 * complete address into the flash array. Keep in
 * mind that when a read or write is done to a
 * specific address, the address is input serially
 * 8 bits at a time. This structure member is used
 * by the read/write routines as a loop index for
 * shifting the address out 8 bits at a time.
 *
 * erasesize - size of an erase block in the flash device.
 */
struct nand_flash_dev {
    char * name;
    int manufacture_id;
    int model_id;
    int chipshift;
    char page256;
    char pageadrlen;
    unsigned long erasesize;
};

首先看看上面三个结构,了解结构中包含的信息

在回来继续分析smc_init()函数,首先函数需要申请一个地址空间用来存放struct mtd_info和struct nand_chip,返回地址给mymtd

mymtd = mmalloc(sizeof(struct mtd_info) + sizeof(struct  nand_chip));

定义一个nand_chip结构的变量this; struct nand_chip *this

this = (struct nand_chip *)(&mymtd[1])这个地方有必要解释一下:

其中,mymtd是指向struct mtd_info的指针,那么mymtd[1]实际上是等效于*(mymtd + 1)的数学计算模式,注意mymtd并非数组,这里仅仅利用了编译器翻译的特点。对于指针而言,加1实际上增加的指针对应类型的值,在这里地址实际上增加了sizeof(struct mtd_info),因为前面分配了两块连续的地址空间,所以&(*(mymtd + 1))实际上就是mtd_info数据结构结束的下一个地址,然后实现强制转换,于是this就成为了nand_chip的入口指针了。但是,这里必须要把握好,因为这个地方是不会进行内存的检查的,也就是说,如果你使用了mymtd[2],那么仍然按照上述公式解析,虽然可以运算,可是就是明显的指针泄漏了,可能会出现意料不到的结果。写了一个测试程序,对这点进行了探讨,要小心内存问题。(参考了CalmArrow的解释)

static int
smc_init(void)
{
        struct nand_chip *this;
        //u_int16_t nfconf;

        u_int16_t nfcont;

        /* Allocate memory for MTD device structure and private data */
        mymtd = mmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip));

        if (!mymtd) {
                printk("Unable to allocate S3C2440 NAND MTD device structure./n");
                return -ENOMEM;
        }

        /* Get pointer to private data */
        this = (struct nand_chip *)(&mymtd[1]);

        /* Initialize structures */
        memset((char *)mymtd, 0, sizeof(struct mtd_info));
        memset((char *)this, 0, sizeof(struct nand_chip));

        /* Link the private data with the MTD structure */
        mymtd->priv = this;

        /* set NAND Flash controller */
        nfcont = NFCONT;
        /* NAND Flash controller enable */
        nfcont |= NFCONT_FCTRL_EN;
        nfcont |= NFCONT_ECC_INIT;
        nfcont |= NFCONT_MAINECC_LOCK;
        NFCONT = nfcont;
   

        /* Set flash memory timing */
// nfconf &= ~NFCONF_TWRPH1;

// nfconf |= NFCONF_TWRPH1_7;

            
// nfconf &= ~NFCONF_TWRPH0;

// nfconf |= NFCONF_TWRPH0_7;


// nfconf &= ~NFCONF_TACLS;

// nfconf &= ~NFCONF_TACLS_7; 


// NFCONF = nfconf;


        /* Set address of NAND IO lines */
        this->hwcontrol = smc_hwcontrol;
        this->write_cmd = write_cmd;
        this->write_addr = write_addr;
        this->read_data = read_data;
        this->write_data = write_data;
        this->wait_for_ready = wait_for_ready;

        /* Chip Enable -> RESET -> Wait for Ready -> Chip Disable */
        //this->hwcontrol(NAND_CTL_SETNCE);

// this->write_cmd(NAND_CMD_RESET);

// this->wait_for_ready();

// this->hwcontrol(NAND_CTL_CLRNCE);

        smc_insert(this);
        return 0;
}


紧接着就是初始两个结构

memset((char *)mymtd, 0, sizeof(struct mtd_info));

memset((char *)this, 0, sizeof(struct nand_chip));

使得mymtd->priv指向this结构。也就是使得这两部分联系起来

设置nand flash的寄存器

NFCONT |= ((1<<0) & (1 << 4) & (1 << 5));

由于nand_chip是直接于nanf flash挂钩的,应此在此定义一些函数直接对nand flash进行操作

先看整体,再对每个细节进行分析:

this->hwcontrol = smc_hwcontrol;

this->write_cmd = write_cmd;

this->write_addr = write_addr;

this->read_data = read_data;

this->write_data = write_data;

this->wait_for_ready = wait_for_ready;

我们首先来看smc_hwcontrol函数,也就是nand flash的硬件控制,实现很简单,根据vivi中定义的情况之处理3中情况(1,2,10,其他的命令只是简单的退出)

 

 

static void smc_hwcontrol(int cmd)
{
    swith (cmd)
        {
            
            case NAND_CTL_SETNCE:
                    NFCONT &= NFCONT_nFCE_HIGH;
                    break;

           //enable chip select NFCONT &= ~(1 << 1)


             case NAND_CTL_CLRNCE:
                     NFCONT |= NFCONT_nFCE_HIGH;
                     break;

            //disable chip select NFCONT |= (1 << 1)


              case NAND_CTL_CLRRnB:
                      NFSTAR |= NFSTAT_RnB;
                      break;

              // RnB is detected! NFSTAT |= (1 << 2)
        }
}

 

写命令:

static void write_cmd(u_char val)
{
      NFCMD = (u_char)val;
}

写地址

static void write_addr(u_char val)
{
      NFADDR = (u_char)val;
}

读数据:

static u_char read_data(void)
{
      return (u_char)NFDATA;
}

写数据:

static void write_data(u_char val)
{
      NFDATA = (u_char)val;
}

等待准备好:

 

static void wait_for_ready(void)
{
//NFSTAT bit[2]为0的时候则表示没准备好,当Bit[2]等于1的时候,则准备好

       while (!(NFSTAT & (1 << 2)))
            ;//do nothing

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值