W25X芯片为norflash芯片,存储空间较大,可以进行分区管理。
分区表示将norflash划分为多个盘,类似电脑C盘、D盘、E盘等。对每个盘进行各自的操作。
例如将norflash配置为4个分区:
#define MAX_NORFLASH_PART_NUM      4
static struct norflash_partition nor_part[MAX_NORFLASH_PART_NUM];//nor分区
分区信息结构体如下:
struct norflash_partition {
    const char *name;//名称
    u32 start_addr;//开始地址
    u32 size;//大小
    struct device device;//设备
};
每个分区有字节的盘符name,开始的地址,分区的大小,设备信息。
这里设备信息结构体如下:
struct device {
    atomic_t ref;//设备被引用的计数,涉及设备关闭卸载等
    void *private_data;//私有数据
    const struct device_operations *ops;//设备操作
    void *platform_data;//平台数据
    void *driver_data;//驱动数据
};
typedef struct {
    int counter;
} atomic_t;
ref仅为一个计数信息。
//设备操作函数
struct device_operations {
    bool (*online)(const struct dev_node *node);//在线
    int (*init)(const struct dev_node *node, void *);//初始化
    int (*open)(const char *name, struct device **device, void *arg);//打开
    int (*read)(struct device *device, void *buf, u32 len, u32);//读
    int (*write)(struct device *device, void *buf, u32 len, u32);//写
    int (*seek)(struct device *device, u32 offset, int orig);//设置读取指针位置
    int (*ioctl)(struct device *device, u32 cmd, u32 arg);//io控制
    int (*close)(struct device *device);//关闭设备
};
以上是分区信息和设备接口信息的定义。
对于一块norflash芯片,需要定义芯片级的信息:
struct norflash_info {
    u32 flash_id;//flash芯片ID
    u32 flash_capacity;//flash存储能力
    int spi_num;//spi编号
    int spi_err;//错误码
    u8 spi_cs_io;//CS片选脚
    u8 spi_r_width;//位宽
    u8 part_num;//已使用的分区个数,0-4
    u8 open_cnt;//打开次数
    struct norflash_partition *const part_list;//分区信息表
    OS_MUTEX mutex;//互斥信号量
    u32 max_end_addr;//最大的结束地址
};
这里包括了
芯片的ID号;
芯片的存储能力,即芯片总空间大小;
使用到SPI编号、错误码信息、片选、位宽;
已使用的分区个数;
打开次数;
分区表;
互斥量;
最大的结束地址,即最后一个分区的尾地址。
定义一个norflash:
static struct norflash_info _norflash = {
    .spi_num = (int) - 1,//编号
    .part_list = nor_part,//分区列表
};
由于SPI编号从0开始,因此此处赋值-1。
分区表指向前面定义的分区表。
对于SPI接口的操作,使用了相关的宏定义:
//片选CS初始化
//普通IO输入
//配置为输出
//输出1
#define spi_cs_init() \
    do { \
        gpio_set_die(_norflash.spi_cs_io, 1); \
        gpio_set_direction(_norflash.spi_cs_io, 0); \
        gpio_write(_norflash.spi_cs_io, 1); \
    } while (0)
//片选CS un初始化
//配置为模拟输入
//配置为输入
//不上拉、不下拉
#define spi_cs_uninit() \
    do { \
        gpio_set_die(_norflash.spi_cs_io, 0); \
        gpio_set_direction(_norflash.spi_cs_io, 1); \
        gpio_set_pull_up(_norflash.spi_cs_io, 0); \
        gpio_set_pull_down(_norflash.spi_cs_io, 0); \
    } while (0)
#define spi_cs_h()                  gpio_write(_norflash.spi_cs_io, 1)//CS输出高
#define spi_cs_l()                  gpio_write(_norflash.spi_cs_io, 0)//CS输出低
#define spi_read_byte()            spi_recv_byte(_norflash.spi_num, &_norflash.spi_err)//读字节
#define spi_write_byte(x)          spi_send_byte(_norflash.spi_num, x)//发送字节
#define spi_dma_read(x, y)          spi_dma_recv(_norflash.spi_num, x, y)//dma读
#define spi_dma_write(x, y)        spi_dma_send(_norflash.spi_num, x, y)//dma写
#define spi_set_width(x)            spi_set_bit_mode(_norflash.spi_num, x)//设置spi位宽
对于分区的管理:
通过name查对应分区:
//每个分区有一个name
//根据这个name来到这个分区的位置,即struct norflash_partition结构体
//该结构体位于_norflash.part_list中
static struct norflash_partition *norflash_find_part(const char *name)//根据name查part
{
    struct norflash_partition *part = NULL;
    u32 idx;
    for (idx = 0; idx < MAX_NORFLASH_PART_NUM; idx++) {//扫描整个分区list
        part = &_norflash.part_list[idx];//指向该成员
        if (part->name == NULL) {//名字为空则继续下一个
            continue;
        }
        if (!strcmp(part->name, name)) {//名字不为空则比对
            return part;//比对相同则返回part
        }
    }
    return NULL;
}
//根据地址、大小和name创建一个新的分区
//首先需要确定分区列表中是否还有空闲位置
//有空闲位置,填充分区信息
//地址加大小不可超过norflash最大地址
static struct norflash_partition *norflash_new_part(const char *name, u32 addr, u32 size)//创建一个新的分区
{
    struct norflash_partition *part;
    u32 idx;
    for (idx = 0; idx < MAX_NORFLASH_PART_NUM; idx++) {//扫描分区list,空闲part
        part = &_norflash.part_list[idx];
        if (part->name == NULL) {
            break;
        }
    }
    if (part->name != NULL) {//如果4个分区用完,返回创建分区失败
null官方更新地址        log_error("create norflash part fail\n");
        return NULL;
    }
    memset(part, 0, sizeof(*part));//清零
    part->name = name;//名字
    part->start_addr = addr;//开始地址
    part->size = size;//大小