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 & (<< 2)))
            ;//do nothing

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章