(海思)uboot之mtd模块spi随笔

board.c中 spi_flash_probe(0, 0, 0, 0); 函数调用了 drivers/mtd/spi下对应文件,如这里的spi_compatible.c;

继续调用了hifmc100_spi_nor_probe ;

-> hifmc100_spi_nor_probe 
    -> hifmc_dev_type_switch(FLASH_TYPE_SPI_NOR);//配置当前类型为spi nor 
    -> hifmc100_driver_probe
        -> hifmc_ip_ver_check //检查版本
        -> hifmc100_spi_nor_init
            赋值基地址即存储空间起始地址,normal操作模式切换; spi timing配置
            -> hifmc100_host_init(host);
            -> hifmc_set_fmc_system_clock//FMC MUX IO端时钟选择;

    -> hifmc100_spi_nor_scan
        -> hifmc_spi_nor_probe
            -> hifmc100_read_ids //读取id,如SPI Nor(cs 0) ID: 0xc2 0x20 0x18
            -> hifmc_spi_nor_serach_ids //根据id匹配flash表;
            -> hifmc_spi_nor_search_rw //根据读/写填充spi中的read/write参数;
            -> hifmc_spi_nor_get_erase
            -> hifmc_map_iftype_and_clock //根据iftype找到最好的时钟

        -> hifmc100_get_bp_lock_level //bp转level
        -> spi_nor_flash->erase = hifmc100_reg_erase;
            -> hifmc100_reg_erase_one_block //通过下发指令完成擦除
        -> spi_nor_flash->write = hifmc100_dma_write;
            //下发指令且使能dma开启数据读、写;
            -> hifmc100_dma_transfer(host, to, (u_char *)buf, RW_OP_WRITE, len);
        -> spi_nor_flash->read = hifmc100_dma_read;
            -> hifmc100_dma_transfer(host, from, (u_char *)buf, RW_OP_READ, len);

    -> hifmc100_get_spi_nor_info
    -> hifmc100_probe_spi_size //计算flash总大小
    -> add_shutdown(hifmc100_driver_shutdown); //关机回调

上述流程中重要的几个结构体: (1)struct mtd_info_ex (2)struct spi_flash (3)struct hifmc_host

struct mtd_info_ex
{
    u_char    type;      /* chip type  MTD_NORFLASH / MTD_NANDFLASH */
    uint64_t  chipsize;  /* total size of the nand/spi chip */
    u_int32_t erasesize;
    u_int32_t pagesize;
    u_int32_t numchips;  /* number of nand chips */
 
    //OOB:特殊数据,用于硬件纠错和坏块管理; https://blog.csdn.net/qq_41882586/article/details/123232813
    u_int32_t oobsize;
    u_int32_t addrcycle;
    //ECC校验纠错能力;如8bit/1kb,16bit/1kb,24bit/1kb,28bit/1kb;
    //如8bit/1kb表示1kb数据中最大能纠正8bit错误;
    u_int32_t ecctype;

    u_char    ids[8]; //id 如SPI Nor(cs 0) ID: 0xc2 0x20 0x18
    u_int32_t id_length;//id长度
    char      name[16]; /* chip names 芯片名*/
    int hostver; /* host controller version 主机控制器版本*/
};
struct spi_flash {
    struct spi_slave *spi;

    const char    *name;

    u32        size;

#ifdef CONFIG_SPI_BLOCK_PROTECT
    unsigned int    bp_level_max;

    void        (*lock)(unsigned char cmp, unsigned char level,
                unsigned char op);
#endif
    //读/写/擦除 回调函数
    int        (*read)(struct spi_flash *flash, u32 offset,
                size_t len, void *buf);
    int        (*write)(struct spi_flash *flash, u32 offset,
                size_t len, const void *buf);
    int        (*erase)(struct spi_flash *flash, u32 offset,
                size_t len);
};

//其中 struct spi_slave 如下
struct spi_slave {
    unsigned int    bus; //总线id
    unsigned int    cs;  //芯片片选id
};
struct hifmc_host {
    struct spi_flash spi_nor_flash[1];
    struct mtd_info_ex *spi_nor_info;
    struct hifmc_spi spi[CONFIG_SPI_NOR_MAX_CHIP_NUM];

    void *regbase; //FMC控制寄存器基地址
    void *iobase;//spi flash存储空间起始地址

    void (*set_system_clock)(struct spi_op *op, int clk_en);
    void (*set_host_addr_mode)(struct hifmc_host *host, int enable);

#ifdef CONFIG_SPI_BLOCK_PROTECT
    unsigned int start_addr;
    unsigned int end_addr;
    unsigned char cmp;
    unsigned int bp_num;
    /* the BT bit location, decide the data num count */
    unsigned int bt_loc;
    unsigned char level;
#endif
};

struct hifmc_host 结构体表示海思flash memory control host 部分;

其中最重要的函数为 flashd的读/写/擦除函数; 这里读写看dma版本;

hifmc100_dma_read / hifmc100_dma_write 函数中都调用了 hifmc100_dma_transfer

/*
功能:实现dma数据读/写传输
参数1:主机控制器
参数2:读/写的起始地址
参数3:需要传输的数据/读取后放入数据的buf;
参数4:用于区分读/写操作
参数5:读时表需要读取的长度, 写时表示写入数据的长度;
*/
static void hifmc100_dma_transfer(struct hifmc_host *host,
    unsigned int spi_start_addr, unsigned char *dma_buffer,
    unsigned char rw_op, unsigned int size)
{
    unsigned char if_type = 0, dummy = 0;
    unsigned char w_cmd = 0, r_cmd = 0;
    unsigned int regval;
    struct hifmc_spi *spi = host->spi;

    FMC_PR(DMA_DB, "\t\t *-Start dma transfer => [%#x], len[%#x].\n",
            spi_start_addr, size);

    regval = FMC_INT_CLR_ALL;
    hifmc_write(host, FMC_INT_CLR, regval);//清除所有中断 
    FMC_PR(DMA_DB, "\t\t   Set INT_CLR[%#x]%#x\n", FMC_INT_CLR, regval);

    regval = spi_start_addr;
    hifmc_write(host, FMC_ADDRL, regval); //配置flash器件操作地址的低4bit; nor flash表配置器件地址;
    FMC_PR(DMA_DB, "\t\t   Set ADDRL[%#x]%#x\n", FMC_ADDRL, regval);

    //区分读还是写
    /*if_type: 五种spi接口类型,分别为standard spi,dual-output/input spi, quad-output/input spi,dual i/o spi, quad i/o spi; */
    if (rw_op == RW_OP_WRITE) {
        if_type = spi->write->iftype;
        dummy = spi->write->dummy;
        w_cmd = spi->write->cmd;
    } else if (rw_op == RW_OP_READ) {
        if_type = spi->read->iftype;
        dummy = spi->read->dummy;
        r_cmd = spi->read->cmd;
    }

    regval = OP_CFG_FM_CS(spi->chipselect) //片选 CS0还是CS1
        | OP_CFG_MEM_IF_TYPE(if_type)      //接口类型
        | OP_CFG_ADDR_NUM(spi->addrcycle)  //发送给flash的地址byte数
        | OP_CFG_DUMMY_NUM(dummy);         //对于dummy_en的操作Byte数
    hifmc_write(host, FMC_OP_CFG, regval);
    FMC_PR(DMA_DB, "\t\t   Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);

    regval = FMC_DMA_LEN_SET(size);
    hifmc_write(host, FMC_DMA_LEN, regval);//dma 操作长度寄存器
    FMC_PR(DMA_DB, "\t\t   Set DMA_LEN[%#x]%#x\n", FMC_DMA_LEN, regval);

    regval = (unsigned int)dma_buffer;
    hifmc_write(host, FMC_DMA_SADDR_D0, regval); //对于spi nor 表示dma操作DDR起始地址寄存器;
    FMC_PR(DMA_DB, "\t\t   Set DMA_SADDR_D0[%#x]%#x\n", FMC_DMA_SADDR_D0,
            regval);

    regval = OP_CTRL_RD_OPCODE(r_cmd) //dma读操作命令
        | OP_CTRL_WR_OPCODE(w_cmd)    //dma写操作命令
        | OP_CTRL_RW_OP(rw_op)        //0=读  1=写
        | OP_CTRL_DMA_OP_READY;       //下发操作控制器状态 1=控制器忙
    hifmc_write(host, FMC_OP_CTRL, regval); //操作控制寄存器
    FMC_PR(DMA_DB, "\t\t   Set OP_CTRL[%#x]%#x\n", FMC_OP_CTRL, regval);

    FMC_DMA_WAIT_INT_FINISH(host); //等待控制器本次操作结束

    FMC_PR(DMA_DB, "\t\t *-End dma transfer.\n");

    return;
}

擦除函数 hifmc100_reg_erase 中 核心部分如下:

static int hifmc100_reg_erase(struct spi_flash *spiflash, u_int offset,
    size_t length)
{
    ...
    while (length) {
        ...
    
        if (hifmc100_reg_erase_one_block(host, spi, offset)) {
            hifmc_ip_user--;
            return -1;
        }
    
        offset += spi->erase->size;
        length -= spi->erase->size;
    }
}

依次调用 hifmc100_reg_erase_one_block 擦除块;

//参数1:主机控制器
//参数2:spi flash设备
参数3:偏移
static int hifmc100_reg_erase_one_block(struct hifmc_host *host,
    struct hifmc_spi *spi, unsigned int offset)
{
    unsigned int regval;

    FMC_PR(OP_DBG, "\t\t * Start erase one block, offset:%#x\n", offset);

    regval = spi->driver->wait_ready(spi);
    if (regval) {
        DB_MSG("Error: Erase wait ready fail! reg:%#x\n", regval);
        return 1;
    }

    spi->driver->write_enable(spi);

    host->set_system_clock(spi->erase, ENABLE);

    regval = FMC_CMD_CMD1(spi->erase->cmd);
    hifmc_write(host, FMC_CMD, regval); //控制器发送给flash的操作命令
    FMC_PR(OP_DBG, "\t\t   Set CMD[%#x]%#x\n", FMC_CMD, regval);

    regval = offset;
    hifmc_write(host, FMC_ADDRL, regval);//配置器件地址
    FMC_PR(OP_DBG, "\t\t   Set ADDRL[%#x]%#x\n", FMC_ADDRL, regval);

    regval = OP_CFG_FM_CS(spi->chipselect) //片选
        | OP_CFG_MEM_IF_TYPE(spi->erase->iftype)  //接口类型
        | OP_CFG_ADDR_NUM(spi->addrcycle)  //发送给flash的地址byte数
        | OP_CFG_DUMMY_NUM(spi->erase->dummy);//根据器件读操作时序中dummy周期
    hifmc_write(host, FMC_OP_CFG, regval);
    FMC_PR(OP_DBG, "\t\t   Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);

    regval = FMC_OP_CMD1_EN(ENABLE) //使能向lash发送command1命令
        | FMC_OP_ADDR_EN(ENABLE) //向flash写操作使能
        | FMC_OP_REG_OP_START; //下发op操作控制器状态 1=忙
    hifmc_write(host, FMC_OP, regval);
    FMC_PR(OP_DBG, "\t\t   Set OP[%#x]%#x\n", FMC_OP, regval);

    FMC_CMD_WAIT_CPU_FINISH(host);

    FMC_PR(OP_DBG, "\t\t * End erase one block.\n");

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天未及海宽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值