700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Linux内核4.14版本——SPI NOR子系统(2)——spi-nor.c分析

Linux内核4.14版本——SPI NOR子系统(2)——spi-nor.c分析

时间:2019-12-31 12:16:29

相关推荐

Linux内核4.14版本——SPI NOR子系统(2)——spi-nor.c分析

1. 简介

2. spi_nor_scan

2.1 检查结构体struct spi_nor是否合格,匹配支持的nor flash ID得到info

2.1.1 spi_nor_check

2.1.2 spi_nor_read_id

2.1.3 struct flash_info

2.2 初始化结构体struct spi_nor_flash_parameter变量params

2.3 设置保护bit、mtd相关的结构体

2.4spi_nor_setup

2.5 设置地址宽度

2.6 s3an_nor_scan

1. 简介

前面已经介绍了spi-nor子系统的由来,现在我们来看看spi-nor子系统的代码。spi-nor的核心结构体是以下代码:

struct spi_nor {struct mtd_infomtd;struct mutexlock;struct device*dev;u32page_size;u8addr_width;u8erase_opcode;u8read_opcode;u8read_dummy;u8program_opcode;enum spi_nor_protocolread_proto;enum spi_nor_protocolwrite_proto;enum spi_nor_protocolreg_proto;boolsst_write_second;u32flags;u8cmd_buf[SPI_NOR_MAX_CMD_SIZE];int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);ssize_t (*read)(struct spi_nor *nor, loff_t from,size_t len, u_char *read_buf);ssize_t (*write)(struct spi_nor *nor, loff_t to,size_t len, const u_char *write_buf);int (*erase)(struct spi_nor *nor, loff_t offs);int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);void *priv;};

mtd: 指向mtd_info结构体,我们知道所有的存储设备,最终都有可以挂载到mtd子系统中。

lock: 读/写/擦除/锁定/解锁操作的锁定

dev: 指向spi设备或spi nor控制器设备。

page_size: SPI NOR的页面大小

addr_width: 地址字节数

erase_opcode: 用于擦除扇区的操作码

read_opcode: 读操作码

read_dummy: 读取操作所需的dummy数

program_opcode: program操作码

sst_write_second: used by the SST write operation

flags:当前SPI-NOR的标志选项(SNOR\u F\ux*)。

read_proto: 用于读取操作的SPI协议

write_proto: 用于写操作的SPI协议

reg_proto 用于读\写\擦除操作的SPI协议

cmd_buf: used by the write_reg

这里有一个enum spi_nor_protocol说明一下,如下所示。

enum spi_nor_protocol {SNOR_PROTO_1_1_1 = SNOR_PROTO_STR(1, 1, 1),SNOR_PROTO_1_1_2 = SNOR_PROTO_STR(1, 1, 2),SNOR_PROTO_1_1_4 = SNOR_PROTO_STR(1, 1, 4),SNOR_PROTO_1_1_8 = SNOR_PROTO_STR(1, 1, 8),SNOR_PROTO_1_2_2 = SNOR_PROTO_STR(1, 2, 2),SNOR_PROTO_1_4_4 = SNOR_PROTO_STR(1, 4, 4),SNOR_PROTO_1_8_8 = SNOR_PROTO_STR(1, 8, 8),SNOR_PROTO_2_2_2 = SNOR_PROTO_STR(2, 2, 2),SNOR_PROTO_4_4_4 = SNOR_PROTO_STR(4, 4, 4),SNOR_PROTO_8_8_8 = SNOR_PROTO_STR(8, 8, 8),SNOR_PROTO_1_1_1_DTR = SNOR_PROTO_DTR(1, 1, 1),SNOR_PROTO_1_2_2_DTR = SNOR_PROTO_DTR(1, 2, 2),SNOR_PROTO_1_4_4_DTR = SNOR_PROTO_DTR(1, 4, 4),SNOR_PROTO_1_8_8_DTR = SNOR_PROTO_DTR(1, 8, 8),};

这个协议是该spi-nor所使用的协议,是1线、4线、8线等协议,初始值就是默认值。

查看include\linux\mtd\spi-nor.h文件,只有一个对外开放的函数spi_nor_scan,这个也是我们分析的重点。

/*** spi_nor_scan() - scan the SPI NOR* @nor:the spi_nor structure* @name:the chip type name* @hwcaps:the hardware capabilities supported by the controller driver** The drivers can use this fuction to scan the SPI NOR.* In the scanning, it will try to get all the necessary information to* fill the mtd_info{} and the spi_nor{}.** The chip type name can be provided through the @name parameter.** Return: 0 for success, others for failure.*/int spi_nor_scan(struct spi_nor *nor, const char *name,const struct spi_nor_hwcaps *hwcaps);

可以看出,对于spi-nor子系统来说,结构体struct spi_nor和struct spi_nor_hwcaps是最主要的设置目标,这个先不管。spi-nor子系统会根据这两个结构体来进行设置一些东西。下面具体分析这个函数。

2. spi_nor_scan

这个函数比较长,我们分几段来看。

2.1 检查结构体struct spi_nor是否合格,匹配支持的nor flash ID得到info

int spi_nor_scan(struct spi_nor *nor, const char *name,const struct spi_nor_hwcaps *hwcaps){struct spi_nor_flash_parameter params;const struct flash_info *info = NULL;struct device *dev = nor->dev;struct mtd_info *mtd = &nor->mtd;struct device_node *np = spi_nor_get_flash_node(nor);int ret;int i;ret = spi_nor_check(nor);if (ret)return ret;/* Reset SPI protocol for all commands. */nor->reg_proto = SNOR_PROTO_1_1_1;nor->read_proto = SNOR_PROTO_1_1_1;nor->write_proto = SNOR_PROTO_1_1_1;if (name)info = spi_nor_match_id(name);/* Try to auto-detect if chip name wasn't specified or not found */if (!info)info = spi_nor_read_id(nor);if (IS_ERR_OR_NULL(info))return -ENOENT;........}

检查结构体struct spi_nor是否合格,设置读写的协议为SNOR_PROTO_1_1_1,匹配支持的nor flash ID得到info。

2.1.1 spi_nor_check

static int spi_nor_check(struct spi_nor *nor){if (!nor->dev || !nor->read || !nor->write ||!nor->read_reg || !nor->write_reg) {pr_err("spi-nor: please fill all the necessary fields!\n");return -EINVAL;}return 0;}

spi_nor_check函数主要检查上层函数传递下来的变量是否设置了必要的变量,这些是编写驱动必须要实现的部分。

2.1.2 spi_nor_read_id

static const struct flash_info *spi_nor_read_id(struct spi_nor *nor){inttmp;u8id[SPI_NOR_MAX_ID_LEN];const struct flash_info*info;tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);if (tmp < 0) {dev_dbg(nor->dev, "error %d reading JEDEC ID\n", tmp);return ERR_PTR(tmp);}for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) {info = &spi_nor_ids[tmp];if (info->id_len) {if (!memcmp(info->id, id, info->id_len))return &spi_nor_ids[tmp];}}dev_err(nor->dev, "unrecognized JEDEC id bytes: %02x, %02x, %02x\n",id[0], id[1], id[2]);return ERR_PTR(-ENODEV);}

通过调用控制器提供的read_reg函数,读取flash的JEDEC ID,并且从spi_nor_ids匹配是否支持该flash。我们简单看看spi_nor_ids这个全局变量。

static const struct flash_info spi_nor_ids[] = {.........{"gd25q128", INFO(0xc84018, 0, 64 * 1024, 256,SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)},......};

宏INFO定义如下:

宏INFO定义如下。#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)\.id = {\((_jedec_id) >> 16) & 0xff,\((_jedec_id) >> 8) & 0xff,\(_jedec_id) & 0xff,\((_ext_id) >> 8) & 0xff,\(_ext_id) & 0xff,\},\.id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))),\.sector_size = (_sector_size),\.n_sectors = (_n_sectors),\.page_size = 256,\.flags = (_flags),

我们以gd25q128这个flash为例,最终的展开是:

static const struct flash_info spi_nor_ids[] = {.........{"gd25q128", .id = {c8,40,18,0,0},.id_len = 3,.sector_size = 64 * 1024,.n_sectors = 256,.page_size = 256,.flag = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB},......};

2.1.3 struct flash_info

从2.1.2小节我们可以看出,如果我们要写一个nor flash的驱动,我们必须把这个nor flash的基本信息填充到spi_nor_ids这个数组中。下面我们看一下这个结构体,具体的分析我们下面在做。

struct flash_info {char*name;/** This array stores the ID bytes.* The first three bytes are the JEDIC ID.* JEDEC ID zero means "no ID" (mostly older chips).*/u8id[SPI_NOR_MAX_ID_LEN];u8id_len;/* The size listed here is what works with SPINOR_OP_SE, which isn't* necessarily called a "sector" by the vendor.*/unsignedsector_size;u16n_sectors;u16page_size;u16addr_width;u16flags;#define SECT_4KBIT(0)/* SPINOR_OP_BE_4K works uniformly */#define SPI_NOR_NO_ERASEBIT(1)/* No erase command needed */#define SST_WRITEBIT(2)/* use SST byte programming */#define SPI_NOR_NO_FRBIT(3)/* Can't do fastread */#define SECT_4K_PMCBIT(4)/* SPINOR_OP_BE_4K_PMC works uniformly */#define SPI_NOR_DUAL_READBIT(5)/* Flash supports Dual Read */#define SPI_NOR_QUAD_READBIT(6)/* Flash supports Quad Read */#define USE_FSRBIT(7)/* use flag status register */#define SPI_NOR_HAS_LOCKBIT(8)/* Flash supports lock/unlock via SR */#define SPI_NOR_HAS_TBBIT(9)/** Flash SR has Top/Bottom (TB) protect* bit. Must be used with* SPI_NOR_HAS_LOCK.*/#defineSPI_S3ANBIT(10)/** Xilinx Spartan 3AN In-System Flash* (MFR cannot be used for probing* because it has the same value as* ATMEL flashes)*/#define SPI_NOR_4B_OPCODESBIT(11)/** Use dedicated 4byte address op codes* to support memory size above 128Mib.*/#define NO_CHIP_ERASEBIT(12) /* Chip does not support chip erase */#define SPI_NOR_SKIP_SFDPBIT(13)/* Skip parsing of SFDP tables */#define USE_CLSRBIT(14)/* use CLSR command */#define SPI_NOR_OCTAL_READBIT(15)/* Flash supports Octal Read */};

2.2 初始化结构体struct spi_nor_flash_parameter变量params

int spi_nor_scan(struct spi_nor *nor, const char *name,const struct spi_nor_hwcaps *hwcaps){struct spi_nor_flash_parameter params;......../** Make sure the XSR_RDY flag is set before calling* spi_nor_wait_till_ready(). Xilinx S3AN share MFR* with Atmel spi-nor*/if (info->flags & SPI_S3AN)nor->flags |= SNOR_F_READY_XSR_RDY;/* Parse the Serial Flash Discoverable Parameters table. */ret = spi_nor_init_params(nor, info, &params);if (ret)return ret;.......ret = spi_nor_setup(nor, info, &params, hwcaps);.......}

根据2.1节得到的flash info信息,设置初始化params。

struct spi_nor_flash_parameter {u64size;u32page_size;struct spi_nor_hwcapshwcaps;struct spi_nor_read_commandreads[SNOR_CMD_READ_MAX];struct spi_nor_pp_commandpage_programs[SNOR_CMD_PP_MAX];int (*quad_enable)(struct spi_nor *nor);};static int spi_nor_init_params(struct spi_nor *nor,const struct flash_info *info,struct spi_nor_flash_parameter *params){/* Set legacy flash parameters as default. */memset(params, 0, sizeof(*params));/* Set SPI NOR sizes. */params->size = (u64)info->sector_size * info->n_sectors;params->page_size = info->page_size;/* (Fast) Read settings. */params->hwcaps.mask |= SNOR_HWCAPS_READ;spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ],0, 0, SPINOR_OP_READ,SNOR_PROTO_1_1_1);if (!(info->flags & SPI_NOR_NO_FR)) {params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_FAST],0, 8, SPINOR_OP_READ_FAST,SNOR_PROTO_1_1_1);}if (info->flags & SPI_NOR_DUAL_READ) {params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_2],0, 8, SPINOR_OP_READ_1_1_2,SNOR_PROTO_1_1_2);}if (info->flags & SPI_NOR_QUAD_READ) {params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_4],0, 8, SPINOR_OP_READ_1_1_4,SNOR_PROTO_1_1_4);}if (info->flags & SPI_NOR_OCTAL_READ) {params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_8;spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_8],0, 8, SPINOR_OP_READ_1_1_8,SNOR_PROTO_1_1_8);}/* Page Program settings. */params->hwcaps.mask |= SNOR_HWCAPS_PP;spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],SPINOR_OP_PP, SNOR_PROTO_1_1_1);/* Select the procedure to set the Quad Enable bit. */if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |SNOR_HWCAPS_PP_QUAD)) {switch (JEDEC_MFR(info)) {case SNOR_MFR_MACRONIX:params->quad_enable = macronix_quad_enable;break;case SNOR_MFR_MICRON:break;case SNOR_MFR_GIGADEVICE:params->quad_enable = spansion_read_cr_quad_enable;break;default:/* Kept only for backward compatibility purpose. */params->quad_enable = spansion_quad_enable;break;}}/* Override the parameters with data read from SFDP tables. */nor->addr_width = 0;nor->mtd.erasesize = 0;if ((info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_OCTAL_READ)) &&!(info->flags & SPI_NOR_SKIP_SFDP)) {struct spi_nor_flash_parameter sfdp_params;memcpy(&sfdp_params, params, sizeof(sfdp_params));if (spi_nor_parse_sfdp(nor, &sfdp_params)) {nor->addr_width = 0;nor->mtd.erasesize = 0;} else {memcpy(params, &sfdp_params, sizeof(*params));}}return 0;}

这个比较简单,根据flag的标志,设置params中的变量,主要设置的是该nor flash的硬件能力集。例如,上面的例子中我们的flash info的flag为

.flag = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB

有一个地方需要注意的是,使能Quad Enable bit位。

/* Select the procedure to set the Quad Enable bit. */if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |SNOR_HWCAPS_PP_QUAD)) {switch (JEDEC_MFR(info)) {case SNOR_MFR_MACRONIX:params->quad_enable = macronix_quad_enable;break;case SNOR_MFR_MICRON:break;case SNOR_MFR_GIGADEVICE:params->quad_enable = spansion_read_cr_quad_enable;break;default:/* Kept only for backward compatibility purpose. */params->quad_enable = spansion_quad_enable;break;}}

read sfdp相关信息。

/* Override the parameters with data read from SFDP tables. */nor->addr_width = 0;nor->mtd.erasesize = 0;if ((info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_OCTAL_READ)) &&!(info->flags & SPI_NOR_SKIP_SFDP)) {struct spi_nor_flash_parameter sfdp_params;memcpy(&sfdp_params, params, sizeof(sfdp_params));if (spi_nor_parse_sfdp(nor, &sfdp_params)) {nor->addr_width = 0;nor->mtd.erasesize = 0;} else {memcpy(params, &sfdp_params, sizeof(*params));}}

2.3 设置保护bit、mtd相关的结构体

int spi_nor_scan(struct spi_nor *nor, const char *name,const struct spi_nor_hwcaps *hwcaps){...../** Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up* with the software protection bits set*/if (JEDEC_MFR(info) == SNOR_MFR_ATMEL ||JEDEC_MFR(info) == SNOR_MFR_INTEL ||JEDEC_MFR(info) == SNOR_MFR_SST ||info->flags & SPI_NOR_HAS_LOCK) {write_enable(nor);write_sr(nor, 0);spi_nor_wait_till_ready(nor);}if (!mtd->name)mtd->name = dev_name(dev);mtd->priv = nor;mtd->type = MTD_NORFLASH;mtd->writesize = 1;mtd->flags = MTD_CAP_NORFLASH;mtd->size = params.size;mtd->_erase = spi_nor_erase;mtd->_read = spi_nor_read;/* NOR protection support for STmicro/Micron chips and similar */if (JEDEC_MFR(info) == SNOR_MFR_MICRON ||info->flags & SPI_NOR_HAS_LOCK) {nor->flash_lock = stm_lock;nor->flash_unlock = stm_unlock;nor->flash_is_locked = stm_is_locked;}if (nor->flash_lock && nor->flash_unlock && nor->flash_is_locked) {mtd->_lock = spi_nor_lock;mtd->_unlock = spi_nor_unlock;mtd->_is_locked = spi_nor_is_locked;}/* sst nor chips use AAI word program */if (info->flags & SST_WRITE)mtd->_write = sst_write;elsemtd->_write = spi_nor_write;if (info->flags & USE_FSR)nor->flags |= SNOR_F_USE_FSR;if (info->flags & SPI_NOR_HAS_TB)nor->flags |= SNOR_F_HAS_SR_TB;if (info->flags & NO_CHIP_ERASE)nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;if (info->flags & USE_CLSR)nor->flags |= SNOR_F_USE_CLSR;if (info->flags & SPI_NOR_NO_ERASE)mtd->flags |= MTD_NO_ERASE;mtd->dev.parent = dev;nor->page_size = params.page_size;mtd->writebufsize = nor->page_size;if (np) {/* If we were instantiated by DT, use it */if (of_property_read_bool(np, "m25p,fast-read"))params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;elseparams.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;} else {/* If we weren't instantiated by DT, default to fast-read */params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;}/* Some devices cannot do fast-read, no matter what DT tells us */if (info->flags & SPI_NOR_NO_FR)params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;....}

2.4spi_nor_setup

int spi_nor_scan(struct spi_nor *nor, const char *name,const struct spi_nor_hwcaps *hwcaps){...../** Configure the SPI memory:* - select op codes for (Fast) Read, Page Program and Sector Erase.* - set the number of dummy cycles (mode cycles + wait states).* - set the SPI protocols for register and memory accesses.* - set the Quad Enable bit if needed (required by SPI x-y-4 protos).*/ret = spi_nor_setup(nor, info, &params, hwcaps);.......}

2.5 设置地址宽度

int spi_nor_scan(struct spi_nor *nor, const char *name,const struct spi_nor_hwcaps *hwcaps){.....if (nor->addr_width) {/* already configured from SFDP */} else if (info->addr_width) {nor->addr_width = info->addr_width;} else if (mtd->size > 0x1000000) {/* enable 4-byte addressing if the device exceeds 16MiB */nor->addr_width = 4;if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||info->flags & SPI_NOR_4B_OPCODES)spi_nor_set_4byte_opcodes(nor, info);elseset_4byte(nor, info, 1);} else {nor->addr_width = 3;}.......}

如果nor flash的大小超过16M,那么必须使用4根地址线才能访问全部的flash空间。

2.6 s3an_nor_scan

int spi_nor_scan(struct spi_nor *nor, const char *name,const struct spi_nor_hwcaps *hwcaps){.....if (info->flags & SPI_S3AN) {ret = s3an_nor_scan(info, nor);if (ret)return ret;}.......}

s3an_nor_scan后面再说。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。