天天看點

RT-Thread下移植添加nand-flash驅動

1:版本

使用的版本上v3.1.4中移除了mtd的相關代碼,如果需要添加就從v3.1.3中擷取。

由于我要移植yaffs是以需要這個架構

RT-Thread下移植添加nand-flash驅動

2:注冊函數

//mtd.c
/*
 * Register MTD driver
 *
 * @parts partion description
 * @np number of partitions
 * @return number of unregistered partitions
 *
*/
int rt_mtd_register(rt_mtd_t *master, const struct mtd_part *parts, int np)
{
    int ret;
    rt_mtd_t *slave;

    master->master = master;
    master->parent.type = RT_Device_Class_MTD;

    if (np > 0)
    {
        master->offset = parts->offset;
        master->size = parts->size;

        ret = rt_device_register((rt_device_t)master, parts->name, 0);
        if (ret != 0)
            goto _out;

        np --;
        parts ++;
    }

    while (np > 0)
    {
        slave = mtd_part_alloc(master, parts);
        if (!slave)
            break;
        ret = rt_device_register((rt_device_t)slave, parts->name, 0);
        if (ret)
            break;
        parts ++;
        np --;
    }

_out:
    return np;
}
           

這個函數會注冊一個mtd裝置

是以要對

rt_mtd_t *master

const struct mtd_part *parts

int np

進行指派

typedef struct mtd_info
{
    struct rt_device parent;  

    const struct mtd_ops *ops; //集體的操作函數,看下個圖

    uint16_t oob_size;
    uint16_t sector_size;   /* Minimal writable flash unit size */
    uint32_t block_size:28; /* Erase size for the device */
    uint32_t type:4;

    size_t size;    /* Total size of the MTD */
    loff_t offset;  /* At which this MTD starts, from the beginning of the MEMORY */
    struct mtd_info *master;

    void *priv;
}rt_mtd_t;
           

這個具體的函數指針,要對這樣指針進行指派具體的函數,如果裝置注冊成功的話。調用裝置裡的讀寫函數就是調用這裡的指向的函數。

struct mtd_ops
{
    int(*erase)(rt_mtd_t *mtd, loff_t addr, size_t len);    /* return 0 if success */
    int(*read) (rt_mtd_t *mtd, loff_t from, struct mtd_io_desc *ops); /* return 0 if success */
    int(*write) (rt_mtd_t *mtd, loff_t to, struct mtd_io_desc *ops);  /* return 0 if success */
    int(*isbad) (rt_mtd_t *mtd, uint32_t block);    /* return 1 if bad, 0 not bad */
    int(*markbad) (rt_mtd_t *mtd, uint32_t block);  /* return 0 if success */
};
           

這個結構體:由裝置的名字,起始位置,大小。

struct mtd_part
{
    const char *name;           /* name of the MTD partion */
    loff_t offset;              /* start addr of partion */
    size_t size;                /* size of partion */
};
           

3:指派

在Rt-thread下自帶的檔案mtdnand.c中已經對操作函數進行指派:

//mtdnand.c

static const struct mtd_ops _ops =
{
    nand_erase,
    nand_read,
    nand_write,
    nand_block_isbad,
    nand_block_markbad,
};

int rt_mtd_nand_init(rt_nand_t *nand, int blk_size, int page_size, int oob_size)
{
    uint8_t *buf;

    buf = rt_malloc(oob_size * 3);
    if (buf == RT_NULL)
        return -ENOMEM;

    nand->oob_poi = buf;
    buf += oob_size;
    nand->buffers.ecccalc = buf;
    buf += oob_size;
    nand->buffers.ecccode = buf;
    nand->pagebuf = 0; /* alloc when unaligen access */

    nand->pages_pb = blk_size / page_size;
    nand->ecc._step = page_size / nand->ecc.stepsize;
    nand->page_size = page_size;
    nand->oobsize = oob_size;

    nand->parent.type = MTD_TYPE_NAND;
    nand->parent.ops = &_ops;
    nand->parent.sector_size = page_size;
    nand->parent.block_size = blk_size;
    nand->parent.oob_size = oob_size;

    switch (nand->ecc.mode)
    {
    case NAND_ECCM_NONE:
    {
        nand->read_page = nand_read_page_raw;
        nand->write_page = nand_write_page_raw;
    }break;
    case NAND_ECCM_HW:
    {
        nand->read_page = nand_read_page_hwecc;
        nand->write_page = nand_write_page_hwecc;
    }break;
    default:
    {
        rt_free(buf);
        return -1;
    }
    }

    return 0;
}
           

用其中的擦除為例nand_erase

static int nand_erase(rt_mtd_t *mtd, loff_t addr, size_t size)
{
    rt_nand_t *chip;
    int status;
    int page;
    uint32_t blksize;

    chip = MTDTONAND(mtd);
    blksize = mtd->block_size;
    page = addr / chip->page_size;

    while (size >= blksize)
    {
        status = chip->ops->cmdfunc(chip, NAND_CMD_BLK_ERASE, page, 0);
        if (status & NAND_STATUS_FAIL)
        {
            break;
        }
        size -= blksize;
        page += chip->pages_pb;
    }

    return size;
}
           

其中由status = chip->ops->cmdfunc(chip, NAND_CMD_BLK_ERASE, page, 0); 表示通過這個語句可以經行具體的指令的操作。這裡就操作到的底層的驅動。這個需要實作。這個函數在下面這個結構體中的const struct nand_ops *ops;

typedef struct nand_chip
{
    rt_mtd_t parent;
    /* driver must init these */
    const struct nand_ops *ops;
    struct nand_ecc ecc;
    const struct mtd_oob_region *freelayout;

    /* driver do not touch */
    struct nand_buffers buffers;
    uint8_t *oob_poi;
    uint8_t *pagebuf;
    uint32_t size;
    uint16_t oobsize;
    uint8_t pages_pb;
    uint16_t page_size;
    int(*read_page)(struct nand_chip *chip, uint8_t *buf, int oob_required, int page);
    int(*write_page)(struct nand_chip *chip, const uint8_t *buf, int oob_required, int page);
}rt_nand_t;
           
struct nand_ops
{
    int(*cmdfunc)(rt_nand_t *nand, int cmd, int page, int offset); /* send nand operation cmd, return Status bits(0 success),
                                                                      if nand is busy please wait in low driver */
    int(*read_buf)(rt_nand_t *nand, uint8_t *buf, int len);        /* read data from nand chip's page buffer */
    int(*write_buf)(rt_nand_t *nand, const  uint8_t *buf, int len);/* write data to nand chip's page buffer */
    int(*isbad)(rt_nand_t *nand, uint32_t blk);   /* if NULL OOB[0] used as bad mark(not 0xff is bad) */
    int(*markbad)(rt_nand_t *nand, uint32_t blk); /* if NULL OOB[0] used as bad mark(set to 0x00) */
};
           

4:底層驅動

是以我們在這裡建立新的檔案對應自己的nand-flash驅動,對上面的函數和結構體進行指派

并進行初始化flash控制器的操作

static const struct nand_ops nuc970_nand_ops =
{
	nuc977_nand_cmdfunc,
    nuc977_nand_read_buf,
    nuc977_nand_write_buf,
    nuc977_nand_isbad,
	nuc977_nand_markbad
 
};
/* nandflash confg */

int nuc970_fmi_nand_init(void)
{
	char *name = "nand1";
	rt_uint32_t eBCHAlgo;
	rt_nand_t *nand	= &nand_chip[0];
	
	nand->ops = &nuc970_nand_ops;
	nand->oobsize = PAGE_OOB_SIZE;
	
    /* initial NAND controller */
    outpw(REG_CLK_HCLKEN, (inpw(REG_CLK_HCLKEN) | 0x300000));

    /* select NAND function pins */
    if (inpw(REG_SYS_PWRON) & 0x08000000)
    {
        /* Set GPI1~15 for NAND */
        outpw(REG_SYS_GPI_MFPL, 0x55555550);
        outpw(REG_SYS_GPI_MFPH, 0x55555555);
    }
    else
    {
        /* Set GPC0~14 for NAND */
        outpw(REG_SYS_GPC_MFPL, 0x55555555);
        outpw(REG_SYS_GPC_MFPH, 0x05555555);
    }

    // Enable SM_EN
    outpw(REG_FMI_CTL, NAND_EN);
    outpw(REG_NANDTMCTL, 0x20305);

    // Enable SM_CS0
    outpw(REG_NANDCTL, (inpw(REG_NANDCTL)&(~0x06000000))|0x04000000);
    outpw(REG_NANDECTL, 0x1); /* un-lock write protect */

    // NAND Reset
    outpw(REG_NANDCTL, inpw(REG_NANDCTL) | 0x1);    // software reset
    while (inpw(REG_NANDCTL) & 0x1);

    /* Detect NAND chips */
    /* first scan to find the device and get the page size */
//	if (nand_scan_ident(&(nuc970_nand->mtd), 1, NULL)) {
//        rt_kprintf("NAND Flash not found !\n");
//        return -1;
//    }
	
    //Set PSize bits of SMCSR register to select NAND card page size
    switch (PAGE_PAGE_SIZE) {
        case 2048:
            outpw(REG_NANDCTL, (inpw(REG_NANDCTL)&(~0x30000)) + 0x10000);
            eBCHAlgo = 0; /* T4 */
           // nuc970_layout_oob_table ( &nuc970_nand_oob, mtd->oobsize, g_i32ParityNum[1][nuc970_nand->eBCHAlgo] );
            break;

        case 4096:
            outpw(REG_NANDCTL, (inpw(REG_NANDCTL)&(~0x30000)) + 0x20000);
            eBCHAlgo = 1; /* T8 */
            //nuc970_layout_oob_table ( &nuc970_nand_oob, mtd->oobsize, g_i32ParityNum[2][nuc970_nand->eBCHAlgo] );
            break;

        case 8192:
            outpw(REG_NANDCTL, (inpw(REG_NANDCTL)&(~0x30000)) + 0x30000);
            eBCHAlgo = 2; /* T12 */
            //nuc970_layout_oob_table ( &nuc970_nand_oob, mtd->oobsize, g_i32ParityNum[3][nuc970_nand->eBCHAlgo] );
            break;

        /* Not support now. */
        case 512:

        default:
            rt_kprintf("NUC970 NAND CONTROLLER IS NOT SUPPORT THE PAGE SIZE. (%d, %d)\n", 2048, 64 );
    }
	
    // Redundant area size
    outpw(REG_NANDRACTL, PAGE_OOB_SIZE);

    // Protect redundant 3 bytes
    // because we need to implement write_oob function to partial data to oob available area.
    // Please note we skip 4 bytes
    outpw(REG_NANDCTL, inpw(REG_NANDCTL) | 0x100);

    // To read/write the ECC parity codes automatically from/to NAND Flash after data area field written.
    outpw(REG_NANDCTL, inpw(REG_NANDCTL) | 0x10);
    // Set BCH algorithm
    outpw(REG_NANDCTL, (inpw(REG_NANDCTL) & (~0x007C0000)) | g_i32BCHAlgoIdx[eBCHAlgo]);
    // Enable H/W ECC, ECC parity check enable bit during read page
    outpw(REG_NANDCTL, inpw(REG_NANDCTL) | 0x00800080);

	rt_mtd_nand_init(nand, PAGE_BLOCK_SIZE, PAGE_PAGE_SIZE, PAGE_OOB_SIZE);
	rt_mtd_register(&(nand->parent), &parts, 1);
    return 0;
}

void nuc970_nand_mtd_init(void)
{

	nuc970_fmi_nand_init();
	nuc970_nand_read_id();
	
}
INIT_DEVICE_EXPORT(nuc970_nand_mtd_init);
           

5:總結

mtd.c提供了注冊函數。mtdnand.c 實作内部的操作函數,但是到底層的具體操作沒有實作。再加上自己的驅動檔案,來實作底層操作來供mtdnand.c 來使用

這樣就能注冊MTD的裝置

RT-Thread下移植添加nand-flash驅動