1、背景介绍

ZYNQ PL部分设计参考《ZYNQ PL部分挂载GD25Q256E flash》这篇文章。

2、flash介绍

兆易创新GD25S512MD是两片32MB的flash拼接而成,通过控制命令(software die select 0xC2)进行片选。ID为0就是第一片,ID为1就是第二片

代码示例如下:

Flash命令交互如下:

首先是片选命令,C2再加上Die ID。

然后是四字节模式下的读写擦除命令

最后是ID,用来判断flash是什么型号

这个与代码里面是对应上的,使用0x9F命令去读取ID

3、内核修改

需要修改的文件如下:

M25p80.c

增加gd25s512配置

注意:150行左右的dummy值要设置为1,具体看代码

/** MTD SPI driver for ST M25Pxx (and similar) serial flash chips** Author: Mike Lavender, mike@steroidmicros.com** Copyright (c) 2005, Intec Automation Inc.** Some parts are based on lart.c by Abraham Van Der Merwe** Cleaned up and generalized based on mtd_dataflash.c** This code is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License version 2 as* published by the Free Software Foundation.**/#include <linux/err.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/device.h>#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
#include <linux/mtd/spi-nor.h>#define MAX_CMD_SIZE        6
struct m25p {struct spi_device  *spi;struct spi_nor     spi_nor;u8          command[MAX_CMD_SIZE];
};static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
{struct m25p *flash = nor->priv;struct spi_device *spi = flash->spi;int ret;ret = spi_write_then_read(spi, &code, 1, val, len);
//printk("code is 0x%x,len:%d,---val:%x----\n",code,len,*val);if (ret < 0)dev_err(&spi->dev, "error %d reading %x\n", ret, code);return ret;
}static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd)
{/* opcode is in cmd[0] */cmd[1] = addr >> (nor->addr_width * 8 -  8);cmd[2] = addr >> (nor->addr_width * 8 - 16);cmd[3] = addr >> (nor->addr_width * 8 - 24);cmd[4] = addr >> (nor->addr_width * 8 - 32);
}static int m25p_cmdsz(struct spi_nor *nor)
{return 1 + nor->addr_width;
}static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
{struct m25p *flash = nor->priv;struct spi_device *spi = flash->spi;flash->command[0] = opcode;if (buf)memcpy(&flash->command[1], buf, len);
//printk("flash->command[0]:0x%x,flash->command[1]:0x%d,opcode:0x%x,len:0x%x\n",flash->command[0],flash->command[1],opcode,len);return spi_write(spi, flash->command, len + 1);
}static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,const u_char *buf)
{struct m25p *flash = nor->priv;struct spi_device *spi = flash->spi;unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;struct spi_transfer t[3] = {};struct spi_message m;int cmd_sz = m25p_cmdsz(nor);ssize_t ret;/* get transfer protocols. */inst_nbits = spi_nor_get_protocol_inst_nbits(nor->write_proto);addr_nbits = spi_nor_get_protocol_addr_nbits(nor->write_proto);data_nbits = spi_nor_get_protocol_data_nbits(nor->write_proto);
//printk("m25p80_write write len :%d,%x\n",len,*buf);spi_message_init(&m);if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)cmd_sz = 1;flash->command[0] = nor->program_opcode;
//printk("nor->program_opcode:0x%x",nor->program_opcode);m25p_addr2cmd(nor, to, flash->command);t[0].tx_buf = flash->command;t[0].tx_nbits = inst_nbits;t[0].len = cmd_sz;spi_message_add_tail(&t[0], &m);/* split the op code and address bytes into two transfers if needed. */data_idx = 1;if (addr_nbits != inst_nbits) {t[0].len = 1;t[1].tx_buf = &flash->command[1];t[1].tx_nbits = addr_nbits;t[1].len = cmd_sz - 1;spi_message_add_tail(&t[1], &m);data_idx = 2;}t[data_idx].tx_buf = buf;t[data_idx].tx_nbits = data_nbits;t[data_idx].len = len;spi_message_add_tail(&t[data_idx], &m);ret = spi_sync(spi, &m);if (ret)return ret;ret = m.actual_length - cmd_sz;if (ret < 0)return -EIO;return ret;
}/** Read an address range from the nor chip.  The address range* may be any size provided it is within the physical boundaries.*/
static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,u_char *buf)
{struct m25p *flash = nor->priv;struct spi_device *spi = flash->spi;unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;struct spi_transfer t[3];struct spi_message m;unsigned int dummy = nor->read_dummy;ssize_t ret;int cmd_sz;/* get transfer protocols. */inst_nbits = spi_nor_get_protocol_inst_nbits(nor->read_proto);addr_nbits = spi_nor_get_protocol_addr_nbits(nor->read_proto);data_nbits = spi_nor_get_protocol_data_nbits(nor->read_proto);/* convert the dummy cycles to the number of bytes */dummy = (dummy * addr_nbits) / 8;dummy= 1;//if (spi_flash_read_supported(spi)) {struct spi_flash_read_message msg;memset(&msg, 0, sizeof(msg));msg.buf = buf;msg.from = from;msg.len = len;msg.read_opcode = nor->read_opcode;msg.addr_width = nor->addr_width;msg.dummy_bytes = dummy;msg.opcode_nbits = inst_nbits;msg.addr_nbits = addr_nbits;msg.data_nbits = data_nbits;ret = spi_flash_read(spi, &msg);if (ret < 0)return ret;return msg.retlen;}spi_message_init(&m);memset(t, 0, (sizeof t));flash->command[0] = nor->read_opcode;m25p_addr2cmd(nor, from, flash->command);t[0].tx_buf = flash->command;t[0].tx_nbits = inst_nbits;t[0].len = m25p_cmdsz(nor) + dummy;spi_message_add_tail(&t[0], &m);/** Set all dummy/mode cycle bits to avoid sending some manufacturer* specific pattern, which might make the memory enter its Continuous* Read mode by mistake.* Based on the different mode cycle bit patterns listed and described* in the JESD216B specification, the 0xff value works for all memories* and all manufacturers.*/cmd_sz = t[0].len;memset(flash->command + cmd_sz - dummy, 0xff, dummy);/* split the op code and address bytes into two transfers if needed. */data_idx = 1;if (addr_nbits != inst_nbits) {t[0].len = 1;t[1].tx_buf = &flash->command[1];t[1].tx_nbits = addr_nbits;t[1].len = cmd_sz - 1;spi_message_add_tail(&t[1], &m);data_idx = 2;}t[data_idx].rx_buf = buf;t[data_idx].rx_nbits = data_nbits;t[data_idx].len = min3(len, spi_max_transfer_size(spi),spi_max_message_size(spi) - cmd_sz);spi_message_add_tail(&t[data_idx], &m);ret = spi_sync(spi, &m);if (ret)return ret;ret = m.actual_length - cmd_sz;if (ret < 0)return -EIO;return ret;
}/** board specific setup should have ensured the SPI clock used here* matches what the READ command supports, at least until this driver* understands FAST_READ (for clocks over 25 MHz).*/
static int m25p_probe(struct spi_device *spi)
{struct flash_platform_data *data;struct m25p *flash;struct spi_nor *nor;struct spi_nor_hwcaps hwcaps = {.mask = SNOR_HWCAPS_READ |SNOR_HWCAPS_READ_FAST |SNOR_HWCAPS_PP,};char *flash_name;int ret;data = dev_get_platdata(&spi->dev);flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);if (!flash)return -ENOMEM;nor = &flash->spi_nor;/* install the hooks */nor->read = m25p80_read;nor->write = m25p80_write;nor->write_reg = m25p80_write_reg;nor->read_reg = m25p80_read_reg;nor->dev = &spi->dev;spi_nor_set_flash_node(nor, spi->dev.of_node);nor->priv = flash;spi_set_drvdata(spi, flash);flash->spi = spi;
//printk("spi->mode:0x%x\n",spi->mode);if (spi->mode & SPI_RX_QUAD) {hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;if (spi->mode & SPI_TX_QUAD)hwcaps.mask |= (SNOR_HWCAPS_READ_1_4_4 |SNOR_HWCAPS_PP_1_1_4 |SNOR_HWCAPS_PP_1_4_4);} else if (spi->mode & SPI_RX_DUAL) {hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;if (spi->mode & SPI_TX_DUAL)hwcaps.mask |= SNOR_HWCAPS_READ_1_2_2;}if (data && data->name)nor->mtd.name = data->name;/* For some (historical?) reason many platforms provide two different* names in flash_platform_data: "name" and "type". Quite often name is* set to "m25p80" and then "type" provides a real chip name.* If that's the case, respect "type" and ignore a "name".*/if (data && data->type)flash_name = data->type;else if (!strcmp(spi->modalias, "spi-nor"))flash_name = NULL; /* auto-detect */elseflash_name = spi->modalias;ret = spi_nor_scan(nor, flash_name, &hwcaps);if (ret)return ret;return mtd_device_register(&nor->mtd, data ? data->parts : NULL,data ? data->nr_parts : 0);
}static int m25p_remove(struct spi_device *spi)
{struct m25p    *flash = spi_get_drvdata(spi);/* Clean up MTD stuff. */return mtd_device_unregister(&flash->spi_nor.mtd);
}/** Do NOT add to this array without reading the following:** Historically, many flash devices are bound to this driver by their name. But* since most of these flash are compatible to some extent, and their* differences can often be differentiated by the JEDEC read-ID command, we* encourage new users to add support to the spi-nor library, and simply bind* against a generic string here (e.g., "jedec,spi-nor").** Many flash names are kept here in this list (as well as in spi-nor.c) to* keep them available as module aliases for existing platforms.*/
static const struct spi_device_id m25p_ids[] = {/** Allow non-DT platform devices to bind to the "spi-nor" modalias, and* hack around the fact that the SPI core does not provide uevent* matching for .of_match_table*/{"spi-nor"},/** Entries not used in DTs that should be safe to drop after replacing* them with "spi-nor" in platform data.*/{"s25sl064a"}, {"w25x16"},   {"m25p10"},   {"m25px64"},/** Entries that were used in DTs without "jedec,spi-nor" fallback and* should be kept for backward compatibility.*/{"at25df321a"},   {"at25df641"},    {"at26df081a"},{"mx25l4005a"},  {"mx25l1606e"},   {"mx25l6405d"},   {"mx25l12805d"},{"mx25l25635e"},{"mx66l51235l"},{"n25q064"},    {"n25q128a11"},   {"n25q128a13"},   {"n25q512a"},{"gd25s512"},{"s25fl256s1"}, {"s25fl512s"},    {"s25sl12801"},   {"s25fl008k"},{"s25fl064k"},{"sst25vf040b"},{"sst25vf016b"},{"sst25vf032b"},{"sst25wf040"},{"m25p40"},    {"m25p80"},   {"m25p16"},   {"m25p32"},{"m25p64"},  {"m25p128"},{"w25x80"}, {"w25x32"},   {"w25q32"},   {"w25q32dw"},{"w25q80bl"},  {"w25q128"},  {"w25q256"},/* Flashes that can't be detected using JEDEC */{"m25p05-nonjedec"},   {"m25p10-nonjedec"},  {"m25p20-nonjedec"},{"m25p40-nonjedec"},    {"m25p80-nonjedec"},  {"m25p16-nonjedec"},{"m25p32-nonjedec"},    {"m25p64-nonjedec"},  {"m25p128-nonjedec"},/* Everspin MRAMs (non-JEDEC) */{ "mr25h256" }, /* 256 Kib, 40 MHz */{ "mr25h10" },  /*   1 Mib, 40 MHz */{ "mr25h40" },  /*   4 Mib, 40 MHz */{ },
};
MODULE_DEVICE_TABLE(spi, m25p_ids);static const struct of_device_id m25p_of_table[] = {/** Generic compatibility for SPI NOR that can be identified by the* JEDEC READ ID opcode (0x9F). Use this, if possible.*/{ .compatible = "jedec,spi-nor" },{ .compatible = "gd25s512,spi-nor" },{}
};
MODULE_DEVICE_TABLE(of, m25p_of_table);static struct spi_driver m25p80_driver = {.driver = {.name = "m25p80",.of_match_table = m25p_of_table,},.id_table  = m25p_ids,.probe  = m25p_probe,.remove   = m25p_remove,/* REVISIT: many of these chips have deep power-down modes, which* should clearly be entered on suspend() to minimize power use.* And also when they're otherwise idle...*/
};module_spi_driver(m25p80_driver);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mike Lavender");
MODULE_DESCRIPTION("MTD SPI driver for ST M25Pxx flash chips");

Spi-nor.h中增加切换片选命令0xC2

/** Copyright (C) 2014 Freescale Semiconductor, Inc.** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.*/#ifndef __LINUX_MTD_SPI_NOR_H
#define __LINUX_MTD_SPI_NOR_H#include <linux/bitops.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/mtd.h>/** Manufacturer IDs** The first byte returned from the flash after sending opcode SPINOR_OP_RDID.* Sometimes these are the same as CFI IDs, but sometimes they aren't.*/
#define SNOR_MFR_ATMEL      CFI_MFR_ATMEL
#define SNOR_MFR_GIGADEVICE 0xc8
#define SNOR_MFR_INTEL      CFI_MFR_INTEL
#define SNOR_MFR_MICRON     CFI_MFR_ST /* ST Micro <--> Micron */
#define SNOR_MFR_MACRONIX   CFI_MFR_MACRONIX
#define SNOR_MFR_SPANSION   CFI_MFR_AMD
#define SNOR_MFR_SST        CFI_MFR_SST
#define SNOR_MFR_WINBOND    0xef /* Also used by some Spansion */
#define SPINOR_OP_SDS           0xc2
/** Note on opcode nomenclature: some opcodes have a format like* SPINOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number* of I/O lines used for the opcode, address, and data (respectively). The* FUNCTION has an optional suffix of '4', to represent an opcode which* requires a 4-byte (32-bit) address.*//* Flash opcodes. */
#define SPINOR_OP_WREN      0x06    /* Write enable */
#define SPINOR_OP_RDSR      0x05    /* Read status register */
#define SPINOR_OP_WRSR      0x01    /* Write status register 1 byte */
#define SPINOR_OP_RDSR2     0x3f    /* Read status register 2 */
#define SPINOR_OP_WRSR2     0x3e    /* Write status register 2 */
#define SPINOR_OP_READ      0x03    /* Read data bytes (low frequency) */
#define SPINOR_OP_READ_FAST 0x0b    /* Read data bytes (high frequency) */
#define SPINOR_OP_READ_1_1_2    0x3b    /* Read data bytes (Dual Output SPI) */
#define SPINOR_OP_READ_1_2_2    0xbb    /* Read data bytes (Dual I/O SPI) */
#define SPINOR_OP_READ_1_1_4    0x6b    /* Read data bytes (Quad Output SPI) */
#define SPINOR_OP_READ_1_4_4    0xeb    /* Read data bytes (Quad I/O SPI) */
#define SPINOR_OP_PP        0x02    /* Page program (up to 256 bytes) */
#define SPINOR_OP_PP_1_1_4  0x32    /* Quad page program */
#define SPINOR_OP_PP_1_4_4  0x38    /* Quad page program */
#define SPINOR_OP_BE_4K     0x20    /* Erase 4KiB block */
#define SPINOR_OP_BE_4K_PMC 0xd7    /* Erase 4KiB block on PMC chips */
#define SPINOR_OP_BE_32K    0x52    /* Erase 32KiB block */
#define SPINOR_OP_CHIP_ERASE    0xc7    /* Erase whole flash chip */
#define SPINOR_OP_SE        0xd8    /* Sector erase (usually 64KiB) */
#define SPINOR_OP_RDID      0x9f    /* Read JEDEC ID */
#define SPINOR_OP_RDSFDP    0x5a    /* Read SFDP */
#define SPINOR_OP_RDCR      0x35    /* Read configuration register */
#define SPINOR_OP_RDFSR     0x70    /* Read flag status register */
#define SPINOR_OP_WREAR     0xc5    /* Write Extended Address Register */
#define SPINOR_OP_RDEAR     0xc8    /* Read Extended Address Register */#define SPINOR_OP_RESET1    0x66000001UL    /* Reset flash command1 */
#define SPINOR_OP_RESET2    0x99000001UL    /* Reset flash command2 *//* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
#define SPINOR_OP_READ_4B   0x13    /* Read data bytes (low frequency) */
#define SPINOR_OP_READ_FAST_4B  0x0c    /* Read data bytes (high frequency) */
#define SPINOR_OP_READ_1_1_2_4B 0x3c    /* Read data bytes (Dual Output SPI) */
#define SPINOR_OP_READ_1_2_2_4B 0xbc    /* Read data bytes (Dual I/O SPI) */
#define SPINOR_OP_READ_1_1_4_4B 0x6c    /* Read data bytes (Quad Output SPI) */
#define SPINOR_OP_READ_1_4_4_4B 0xec    /* Read data bytes (Quad I/O SPI) */
#define SPINOR_OP_PP_4B     0x12    /* Page program (up to 256 bytes) */
#define SPINOR_OP_PP_1_1_4_4B   0x34    /* Quad page program */
#define SPINOR_OP_PP_1_4_4_4B   0x3e    /* Quad page program */
#define SPINOR_OP_BE_4K_4B  0x21    /* Erase 4KiB block */
#define SPINOR_OP_BE_32K_4B 0x5c    /* Erase 32KiB block */
#define SPINOR_OP_SE_4B     0xdc    /* Sector erase (usually 64KiB) *//* Double Transfer Rate opcodes - defined in JEDEC JESD216B. */
#define SPINOR_OP_READ_1_1_1_DTR    0x0d
#define SPINOR_OP_READ_1_2_2_DTR    0xbd
#define SPINOR_OP_READ_1_4_4_DTR    0xed#define SPINOR_OP_READ_1_1_1_DTR_4B 0x0e
#define SPINOR_OP_READ_1_2_2_DTR_4B 0xbe
#define SPINOR_OP_READ_1_4_4_DTR_4B 0xee/* Used for SST flashes only. */
#define SPINOR_OP_BP        0x02    /* Byte program */
#define SPINOR_OP_WRDI      0x04    /* Write disable */
#define SPINOR_OP_AAI_WP    0xad    /* Auto address increment word program *//* Used for S3AN flashes only */
#define SPINOR_OP_XSE       0x50    /* Sector erase */
#define SPINOR_OP_XPP       0x82    /* Page program */
#define SPINOR_OP_XRDSR     0xd7    /* Read status register */#define XSR_PAGESIZE      BIT(0)  /* Page size in Po2 or Linear */
#define XSR_RDY         BIT(7)  /* Ready *//* Used for Macronix and Winbond flashes. */
#define SPINOR_OP_EN4B      0xb7    /* Enter 4-byte mode */
#define SPINOR_OP_EX4B      0xe9    /* Exit 4-byte mode *//* Used for Spansion flashes only. */
#define SPINOR_OP_BRWR      0x17    /* Bank register write */
#define SPINOR_OP_BRRD      0x16    /* Bank register read */
#define SPINOR_OP_CLSR      0x30    /* Clear status register 1 */
#define SPAN_OP_RESET1      0xff000001UL    /* Reset flash command1 */
#define SPAN_OP_RESET2      0xf0000001UL    /* Reset flash command2 *//* Used for Micron flashes only. */
#define SPINOR_OP_RD_EVCR      0x65    /* Read EVCR register */
#define SPINOR_OP_WD_EVCR      0x61    /* Write EVCR register *//* Status Register bits. */
#define SR_WIP          BIT(0)  /* Write in progress */
#define SR_WEL          BIT(1)  /* Write enable latch */
/* meaning of other SR_* bits may differ between vendors */
#define SR_BP0          BIT(2)  /* Block protect 0 */
#define SR_BP1          BIT(3)  /* Block protect 1 */
#define SR_BP2          BIT(4)  /* Block protect 2 */
#define SR_TB           BIT(5)  /* Top/Bottom protect */
#define SR_SRWD         BIT(7)  /* SR write protect */
/* Spansion/Cypress specific status bits */
#define SR_E_ERR        BIT(5)
#define SR_P_ERR        BIT(6)#define SR_QUAD_EN_MX     BIT(6)  /* Macronix Quad I/O *//* Enhanced Volatile Configuration Register bits */
#define EVCR_QUAD_EN_MICRON BIT(7)  /* Micron Quad I/O *//* Flag Status Register bits */
#define FSR_READY       BIT(7)/* Configuration Register bits. */
#define CR_QUAD_EN_SPAN     BIT(1)  /* Spansion Quad I/O *//* Extended/Bank Address Register bits */
#define EAR_SEGMENT_MASK    0x7 /* 128 Mb segment mask */enum read_mode {SPI_NOR_NORMAL = 0,SPI_NOR_FAST,SPI_NOR_DUAL,SPI_NOR_QUAD,
};/* Status Register 2 bits. */
#define SR2_QUAD_EN_BIT7    BIT(7)/* Supported SPI protocols */
#define SNOR_PROTO_INST_MASK    GENMASK(23, 16)
#define SNOR_PROTO_INST_SHIFT   16
#define SNOR_PROTO_INST(_nbits) \((((unsigned long)(_nbits)) << SNOR_PROTO_INST_SHIFT) & \SNOR_PROTO_INST_MASK)#define SNOR_PROTO_ADDR_MASK   GENMASK(15, 8)
#define SNOR_PROTO_ADDR_SHIFT   8
#define SNOR_PROTO_ADDR(_nbits) \((((unsigned long)(_nbits)) << SNOR_PROTO_ADDR_SHIFT) & \SNOR_PROTO_ADDR_MASK)#define SNOR_PROTO_DATA_MASK   GENMASK(7, 0)
#define SNOR_PROTO_DATA_SHIFT   0
#define SNOR_PROTO_DATA(_nbits) \((((unsigned long)(_nbits)) << SNOR_PROTO_DATA_SHIFT) & \SNOR_PROTO_DATA_MASK)#define SNOR_PROTO_IS_DTR  BIT(24) /* Double Transfer Rate */#define SNOR_PROTO_STR(_inst_nbits, _addr_nbits, _data_nbits) \(SNOR_PROTO_INST(_inst_nbits) |                \SNOR_PROTO_ADDR(_addr_nbits) |             \SNOR_PROTO_DATA(_data_nbits))
#define SNOR_PROTO_DTR(_inst_nbits, _addr_nbits, _data_nbits)   \(SNOR_PROTO_IS_DTR |                   \SNOR_PROTO_STR(_inst_nbits, _addr_nbits, _data_nbits))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),
};static inline bool spi_nor_protocol_is_dtr(enum spi_nor_protocol proto)
{return !!(proto & SNOR_PROTO_IS_DTR);
}static inline u8 spi_nor_get_protocol_inst_nbits(enum spi_nor_protocol proto)
{return ((unsigned long)(proto & SNOR_PROTO_INST_MASK)) >>SNOR_PROTO_INST_SHIFT;
}static inline u8 spi_nor_get_protocol_addr_nbits(enum spi_nor_protocol proto)
{return ((unsigned long)(proto & SNOR_PROTO_ADDR_MASK)) >>SNOR_PROTO_ADDR_SHIFT;
}static inline u8 spi_nor_get_protocol_data_nbits(enum spi_nor_protocol proto)
{return ((unsigned long)(proto & SNOR_PROTO_DATA_MASK)) >>SNOR_PROTO_DATA_SHIFT;
}static inline u8 spi_nor_get_protocol_width(enum spi_nor_protocol proto)
{return spi_nor_get_protocol_data_nbits(proto);
}#define SPI_NOR_MAX_CMD_SIZE   8
enum spi_nor_ops {SPI_NOR_OPS_READ = 0,SPI_NOR_OPS_WRITE,SPI_NOR_OPS_ERASE,SPI_NOR_OPS_LOCK,SPI_NOR_OPS_UNLOCK,
};enum spi_nor_option_flags {SNOR_F_USE_FSR     = BIT(0),SNOR_F_HAS_SR_TB  = BIT(1),SNOR_F_NO_OP_CHIP_ERASE   = BIT(2),SNOR_F_S3AN_ADDR_DEFAULT = BIT(3),SNOR_F_READY_XSR_RDY   = BIT(4),SNOR_F_USE_CLSR       = BIT(5),
};/*** struct spi_nor - Structure for defining a the SPI NOR layer* @mtd:      point to a mtd_info structure* @lock:      the lock for the read/write/erase/lock/unlock operations* @dev:        point to a spi device, or a spi nor controller device.* @page_size:        the page size of the SPI NOR* @addr_width:     number of address bytes* @erase_opcode:    the opcode for erasing a sector* @read_opcode: the read opcode* @read_dummy:      the dummy needed by the read operation* @program_opcode:   the program opcode* @sst_write_second: used by the SST write operation* @flags:       flag options for the current SPI-NOR (SNOR_F_*)* @read_proto:      the SPI protocol for read operations* @write_proto:    the SPI protocol for write operations* @reg_proto      the SPI protocol for read_reg/write_reg/erase operations* @cmd_buf:        used by the write_reg* @prepare:       [OPTIONAL] do some preparations for the*            read/write/erase/lock/unlock operations* @unprepare:       [OPTIONAL] do some post work after the*         read/write/erase/lock/unlock operations* @read_reg:        [DRIVER-SPECIFIC] read out the register* @write_reg:       [DRIVER-SPECIFIC] write data to the register* @read:       [DRIVER-SPECIFIC] read data from the SPI NOR* @write:      [DRIVER-SPECIFIC] write data to the SPI NOR* @erase:       [DRIVER-SPECIFIC] erase a sector of the SPI NOR*            at the offset @offs; if not provided by the driver,*           spi-nor will send the erase opcode via write_reg()* @flash_lock:       [FLASH-SPECIFIC] lock a region of the SPI NOR* @flash_unlock:  [FLASH-SPECIFIC] unlock a region of the SPI NOR* @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is*           completely locked* @priv:      the private data*/
struct spi_nor {struct mtd_info     mtd;struct mutex        lock;struct device      *dev;u32            page_size;u8            addr_width;u8           erase_opcode;u8         read_opcode;u8          read_dummy;u8           program_opcode;u32          jedec_id;u16            curbank;enum spi_nor_protocol   read_proto;enum spi_nor_protocol    write_proto;enum spi_nor_protocol   reg_proto;bool          sst_write_second;u32            flags;u8            cmd_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;
};static inline void spi_nor_set_flash_node(struct spi_nor *nor,struct device_node *np)
{mtd_set_of_node(&nor->mtd, np);
}static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
{return mtd_get_of_node(&nor->mtd);
}/*** struct spi_nor_hwcaps - Structure for describing the hardware capabilies* supported by the SPI controller (bus master).* @mask:      the bitmask listing all the supported hw capabilies*/
struct spi_nor_hwcaps {u32  mask;
};/**(Fast) Read capabilities.* MUST be ordered by priority: the higher bit position, the higher priority.* As a matter of performances, it is relevant to use Octo SPI protocols first,* then Quad SPI protocols before Dual SPI protocols, Fast Read and lastly* (Slow) Read.*/
#define SNOR_HWCAPS_READ_MASK       GENMASK(14, 0)
#define SNOR_HWCAPS_READ        BIT(0)
#define SNOR_HWCAPS_READ_FAST       BIT(1)
#define SNOR_HWCAPS_READ_1_1_1_DTR  BIT(2)#define SNOR_HWCAPS_READ_DUAL     GENMASK(6, 3)
#define SNOR_HWCAPS_READ_1_1_2      BIT(3)
#define SNOR_HWCAPS_READ_1_2_2      BIT(4)
#define SNOR_HWCAPS_READ_2_2_2      BIT(5)
#define SNOR_HWCAPS_READ_1_2_2_DTR  BIT(6)#define SNOR_HWCAPS_READ_QUAD     GENMASK(10, 7)
#define SNOR_HWCAPS_READ_1_1_4      BIT(7)
#define SNOR_HWCAPS_READ_1_4_4      BIT(8)
#define SNOR_HWCAPS_READ_4_4_4      BIT(9)
#define SNOR_HWCAPS_READ_1_4_4_DTR  BIT(10)#define SNOR_HWCPAS_READ_OCTO        GENMASK(14, 11)
#define SNOR_HWCAPS_READ_1_1_8      BIT(11)
#define SNOR_HWCAPS_READ_1_8_8      BIT(12)
#define SNOR_HWCAPS_READ_8_8_8      BIT(13)
#define SNOR_HWCAPS_READ_1_8_8_DTR  BIT(14)/** Page Program capabilities.* MUST be ordered by priority: the higher bit position, the higher priority.* Like (Fast) Read capabilities, Octo/Quad SPI protocols are preferred to the* legacy SPI 1-1-1 protocol.* Note that Dual Page Programs are not supported because there is no existing* JEDEC/SFDP standard to define them. Also at this moment no SPI flash memory* implements such commands.*/
#define SNOR_HWCAPS_PP_MASK GENMASK(22, 16)
#define SNOR_HWCAPS_PP      BIT(16)#define SNOR_HWCAPS_PP_QUAD  GENMASK(19, 17)
#define SNOR_HWCAPS_PP_1_1_4    BIT(17)
#define SNOR_HWCAPS_PP_1_4_4    BIT(18)
#define SNOR_HWCAPS_PP_4_4_4    BIT(19)#define SNOR_HWCAPS_PP_OCTO  GENMASK(22, 20)
#define SNOR_HWCAPS_PP_1_1_8    BIT(20)
#define SNOR_HWCAPS_PP_1_8_8    BIT(21)
#define SNOR_HWCAPS_PP_8_8_8    BIT(22)/*** 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_shutdown() - prepare for reboot* @nor:    the spi_nor structure** The drivers can use this fuction to get the address back to* 0 as will be required for a ROM boot.*/
void spi_nor_shutdown(struct spi_nor *nor);#endif

Spi-nor.c修改

/** Based on m25p80.c, by Mike Lavender (mike@steroidmicros.com), with* influence from lart.c (Abraham Van Der Merwe) and mtd_dataflash.c** Copyright (C) 2005, Intec Automation Inc.* Copyright (C) 2014, Freescale Semiconductor, Inc.** This code is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License version 2 as* published by the Free Software Foundation.*/#include <linux/err.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/math64.h>
#include <linux/sizes.h>
#include <linux/slab.h>#include <linux/mtd/mtd.h>
#include <linux/of_platform.h>
#include <linux/spi/flash.h>
#include <linux/mtd/spi-nor.h>/* Define max times to check status register before we give up. *//** For everything but full-chip erase; probably could be much smaller, but kept* around for safety for now*/
#define DEFAULT_READY_WAIT_JIFFIES      (40UL * HZ)/** For full-chip erase, calibrated to a 2MB flash (M25P16); should be scaled up* for larger flash*/
#define CHIP_ERASE_2MB_READY_WAIT_JIFFIES   (40UL * HZ)#define SPI_NOR_MAX_ID_LEN   6
#define SPI_NOR_MAX_ADDR_WIDTH  4struct 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).*/u8     id[SPI_NOR_MAX_ID_LEN];u8       id_len;/* The size listed here is what works with SPINOR_OP_SE, which isn't* necessarily called a "sector" by the vendor.*/unsigned  sector_size;u16     n_sectors;u16       page_size;u16       addr_width;u16      flags;
#define SECT_4K         BIT(0)  /* SPINOR_OP_BE_4K works uniformly */
#define SPI_NOR_NO_ERASE    BIT(1)  /* No erase command needed */
#define SST_WRITE       BIT(2)  /* use SST byte programming */
#define SPI_NOR_NO_FR       BIT(3)  /* Can't do fastread */
#define SECT_4K_PMC     BIT(4)  /* SPINOR_OP_BE_4K_PMC works uniformly */
#define SPI_NOR_DUAL_READ   BIT(5)  /* Flash supports Dual Read */
#define SPI_NOR_QUAD_READ   BIT(6)  /* Flash supports Quad Read */
#define USE_FSR         BIT(7)  /* use flag status register */
#define SPI_NOR_HAS_LOCK    BIT(8)  /* Flash supports lock/unlock via SR */
#define SPI_NOR_HAS_TB      BIT(9)  /** Flash SR has Top/Bottom (TB) protect* bit. Must be used with* SPI_NOR_HAS_LOCK.*/
#define SPI_S3AN        BIT(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_OPCODES  BIT(11) /** Use dedicated 4byte address op codes* to support memory size above 128Mib.*/
#define NO_CHIP_ERASE       BIT(12) /* Chip does not support chip erase */
#define SPI_NOR_SKIP_SFDP   BIT(13) /* Skip parsing of SFDP tables */
#define USE_CLSR        BIT(14) /* use CLSR command */
};#define JEDEC_MFR(info)   ((info)->id[0])static const struct flash_info *spi_nor_match_id(const char *name);/** Read the status register, returning its value in the location* Return the status register value.* Returns negative if error occurred.*/
static int read_sr(struct spi_nor *nor)
{int ret;u8 val;ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val, 1);if (ret < 0) {pr_err("error %d reading SR\n", (int) ret);return ret;}return val;
}/** Read the flag status register, returning its value in the location* Return the status register value.* Returns negative if error occurred.*/
static int read_fsr(struct spi_nor *nor)
{int ret;u8 val;ret = nor->read_reg(nor, SPINOR_OP_RDFSR, &val, 1);if (ret < 0) {pr_err("error %d reading FSR\n", ret);return ret;}return val;
}/** Read configuration register, returning its value in the* location. Return the configuration register value.* Returns negative if error occurred.*/
static int read_cr(struct spi_nor *nor)
{int ret;u8 val;ret = nor->read_reg(nor, SPINOR_OP_RDCR, &val, 1);if (ret < 0) {dev_err(nor->dev, "error %d reading CR\n", ret);return ret;}return val;
}/** Write status register 1 byte* Returns negative if error occurred.*/
static inline int write_sr(struct spi_nor *nor, u8 val)
{nor->cmd_buf[0] = val;return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1);
}/** Set write enable latch with Write Enable command.* Returns negative if error occurred.*/
static inline int write_enable(struct spi_nor *nor)
{return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0);
}/** Send write disable instruction to the chip.*/
static inline int write_disable(struct spi_nor *nor)
{return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0);
}static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
{return mtd->priv;
}static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size)
{size_t i;for (i = 0; i < size; i++)if (table[i][0] == opcode)return table[i][1];/* No conversion found, keep input op code. */return opcode;
}static inline u8 spi_nor_convert_3to4_read(u8 opcode)
{static const u8 spi_nor_3to4_read[][2] = {{ SPINOR_OP_READ,   SPINOR_OP_READ_4B },{ SPINOR_OP_READ_FAST,  SPINOR_OP_READ_FAST_4B },{ SPINOR_OP_READ_1_1_2,    SPINOR_OP_READ_1_1_2_4B },{ SPINOR_OP_READ_1_2_2,   SPINOR_OP_READ_1_2_2_4B },{ SPINOR_OP_READ_1_1_4,   SPINOR_OP_READ_1_1_4_4B },{ SPINOR_OP_READ_1_4_4,   SPINOR_OP_READ_1_4_4_4B },{ SPINOR_OP_READ_1_1_1_DTR,   SPINOR_OP_READ_1_1_1_DTR_4B },{ SPINOR_OP_READ_1_2_2_DTR,   SPINOR_OP_READ_1_2_2_DTR_4B },{ SPINOR_OP_READ_1_4_4_DTR,   SPINOR_OP_READ_1_4_4_DTR_4B },};return spi_nor_convert_opcode(opcode, spi_nor_3to4_read,ARRAY_SIZE(spi_nor_3to4_read));
}static inline u8 spi_nor_convert_3to4_program(u8 opcode)
{static const u8 spi_nor_3to4_program[][2] = {{ SPINOR_OP_PP,      SPINOR_OP_PP_4B },{ SPINOR_OP_PP_1_1_4, SPINOR_OP_PP_1_1_4_4B },{ SPINOR_OP_PP_1_4_4,   SPINOR_OP_PP_1_4_4_4B },};return spi_nor_convert_opcode(opcode, spi_nor_3to4_program,ARRAY_SIZE(spi_nor_3to4_program));
}static inline u8 spi_nor_convert_3to4_erase(u8 opcode)
{static const u8 spi_nor_3to4_erase[][2] = {{ SPINOR_OP_BE_4K, SPINOR_OP_BE_4K_4B },{ SPINOR_OP_BE_32K,    SPINOR_OP_BE_32K_4B },{ SPINOR_OP_SE,       SPINOR_OP_SE_4B },};return spi_nor_convert_opcode(opcode, spi_nor_3to4_erase,ARRAY_SIZE(spi_nor_3to4_erase));
}static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,const struct flash_info *info)
{/* Do some manufacturer fixups first */switch (JEDEC_MFR(info)) {case SNOR_MFR_SPANSION:/* No small sector erase for 4-byte command set */nor->erase_opcode = SPINOR_OP_SE;nor->mtd.erasesize = info->sector_size;break;default:break;}nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode);nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
}/* Enable/disable 4-byte addressing mode. */
static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,int enable)
{int status;bool need_wren = false;u8 cmd;switch (SNOR_MFR_WINBOND) {case SNOR_MFR_MICRON:/* Some Micron need WREN command; all will accept it */need_wren = true;case SNOR_MFR_MACRONIX:case SNOR_MFR_WINBOND:if (need_wren)write_enable(nor);cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;status = nor->write_reg(nor, cmd, NULL, 0);if (need_wren)write_disable(nor);return status;default:/* Spansion style */nor->cmd_buf[0] = enable << 7;return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);}
}
static inline int set_4byte_for_two_die(struct spi_nor *nor, const struct flash_info *info,int enable)
{u8 val;nor->read_reg(nor, 0xf8, &val, 1);while(val!=0){nor->cmd_buf[0] = 0x00;nor->write_reg(nor, SPINOR_OP_SDS, nor->cmd_buf, 1);udelay(100);nor->read_reg(nor, 0xf8, &val, 1);}set_4byte(nor, info,enable);nor->read_reg(nor, 0xf8, &val, 1);while(val!=1){nor->cmd_buf[0] = 0x01;nor->write_reg(nor, SPINOR_OP_SDS, nor->cmd_buf, 1);udelay(100);nor->read_reg(nor, 0xf8, &val, 1);}set_4byte(nor, info,enable);
}static int s3an_sr_ready(struct spi_nor *nor)
{int ret;u8 val;ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1);if (ret < 0) {dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret);return ret;}return !!(val & XSR_RDY);
}static inline int spi_nor_sr_ready(struct spi_nor *nor)
{int sr = read_sr(nor);if (sr < 0)return sr;if (nor->flags & SNOR_F_USE_CLSR && sr & (SR_E_ERR | SR_P_ERR)) {if (sr & SR_E_ERR)dev_err(nor->dev, "Erase Error occurred\n");elsedev_err(nor->dev, "Programming Error occurred\n");nor->write_reg(nor, SPINOR_OP_CLSR, NULL, 0);return -EIO;}return !(sr & SR_WIP);
}static inline int spi_nor_fsr_ready(struct spi_nor *nor)
{int fsr = read_fsr(nor);if (fsr < 0)return fsr;elsereturn fsr & FSR_READY;
}static int spi_nor_ready(struct spi_nor *nor)
{int sr, fsr;if (nor->flags & SNOR_F_READY_XSR_RDY)sr = s3an_sr_ready(nor);elsesr = spi_nor_sr_ready(nor);if (sr < 0)return sr;fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1;if (fsr < 0)return fsr;return sr && fsr;
}/** Service routine to read status register until ready, or timeout occurs.* Returns non-zero if error.*/
static int spi_nor_wait_till_ready_with_timeout(struct spi_nor *nor,unsigned long timeout_jiffies)
{unsigned long deadline;int timeout = 0, ret;deadline = jiffies + timeout_jiffies;while (!timeout) {if (time_after_eq(jiffies, deadline))timeout = 1;ret = spi_nor_ready(nor);if (ret < 0)return ret;if (ret)return 0;cond_resched();}dev_err(nor->dev, "flash operation timed out\n");return -ETIMEDOUT;
}static int spi_nor_wait_till_ready(struct spi_nor *nor)
{return spi_nor_wait_till_ready_with_timeout(nor,DEFAULT_READY_WAIT_JIFFIES);
}/** Erase the whole flash memory** Returns 0 if successful, non-zero otherwise.*/
static int erase_chip(struct spi_nor *nor)
{dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10));return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0);
}static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops)
{int ret = 0;mutex_lock(&nor->lock);if (nor->prepare) {ret = nor->prepare(nor, ops);if (ret) {dev_err(nor->dev, "failed in the preparation.\n");mutex_unlock(&nor->lock);return ret;}}return ret;
}static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
{if (nor->unprepare)nor->unprepare(nor, ops);mutex_unlock(&nor->lock);
}/** This code converts an address to the Default Address Mode, that has non* power of two page sizes. We must support this mode because it is the default* mode supported by Xilinx tools, it can access the whole flash area and* changing over to the Power-of-two mode is irreversible and corrupts the* original data.* Addr can safely be unsigned int, the biggest S3AN device is smaller than* 4 MiB.*/
static loff_t spi_nor_s3an_addr_convert(struct spi_nor *nor, unsigned int addr)
{unsigned int offset;unsigned int page;offset = addr % nor->page_size;page = addr / nor->page_size;page <<= (nor->page_size > 512) ? 10 : 9;return page | offset;
}/** Initiate the erasure of a single sector*/
static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
{u8 buf[SPI_NOR_MAX_ADDR_WIDTH];int i;if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)addr = spi_nor_s3an_addr_convert(nor, addr);if (nor->erase)return nor->erase(nor, addr);/** Default implementation, if driver doesn't have a specialized HW* control*/for (i = nor->addr_width - 1; i >= 0; i--) {buf[i] = addr & 0xff;addr >>= 8;}return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width);
}/** Erase an address range on the nor chip.  The address range may extend* one or more erase sectors.  Return an error is there is a problem erasing.*/
static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
{struct spi_nor *nor = mtd_to_spi_nor(mtd);u32 addr, len,addr1=0,addr2=0;uint32_t rem;int ret;u8 val;int flag = 0;dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,(long long)instr->len);div_u64_rem(instr->len, mtd->erasesize, &rem);if (rem)return -EINVAL;addr = instr->addr;len = instr->len;ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_ERASE);if (ret)return ret;/* whole-chip erase? */if (0){//(len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {unsigned long timeout;write_enable(nor);if (erase_chip(nor)) {ret = -EIO;goto erase_err;}/** Scale the timeout linearly with the size of the flash, with* a minimum calibrated to an old 2MB flash. We could try to* pull these from CFI/SFDP, but these values should be good* enough for now.*/timeout = max(CHIP_ERASE_2MB_READY_WAIT_JIFFIES,CHIP_ERASE_2MB_READY_WAIT_JIFFIES *(unsigned long)(mtd->size / SZ_2M));ret = spi_nor_wait_till_ready_with_timeout(nor, timeout);if (ret)goto erase_err;/* REVISIT in some cases we could speed up erasing large regions* by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K.  We may have set up* to use "small sector erase", but that's not always optimal.*//* "sector"-at-a-time erase */} else {if(addr>=0x2000000){addr2 = addr -0x2000000;nor->read_reg(nor, 0xf8, &val, 1);while(val!=1){nor->cmd_buf[0] = 0x01;nor->write_reg(nor, SPINOR_OP_SDS, nor->cmd_buf, 1);udelay(10);nor->read_reg(nor, 0xf8, &val, 1);}}else {nor->read_reg(nor, 0xf8, &val, 1);while(val!=0){nor->cmd_buf[0] = 0x00;nor->write_reg(nor, SPINOR_OP_SDS, nor->cmd_buf, 1);udelay(10);nor->read_reg(nor, 0xf8, &val, 1);}addr1 = addr;}while (len) {write_enable(nor);if(addr>=0x2000000){addr2 = addr - 0x2000000;nor->read_reg(nor, 0xf8, &val, 1);while(val!=1){nor->cmd_buf[0] = 0x01;nor->write_reg(nor, SPINOR_OP_SDS, nor->cmd_buf, 1);udelay(100);nor->read_reg(nor, 0xf8, &val, 1);}ret = spi_nor_erase_sector(nor, addr2);addr += mtd->erasesize;len -= mtd->erasesize;addr2 += mtd->erasesize;}else{ret = spi_nor_erase_sector(nor, addr1);addr += mtd->erasesize;len -= mtd->erasesize;addr1 += mtd->erasesize;flag =1;}if (ret)goto erase_err;ret = spi_nor_wait_till_ready(nor);if (ret)goto erase_err;}}write_disable(nor);erase_err:spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);instr->state = ret ? MTD_ERASE_FAILED : MTD_ERASE_DONE;mtd_erase_callback(instr);return ret;
}static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs,uint64_t *len)
{struct mtd_info *mtd = &nor->mtd;u8 mask = SR_BP2 | SR_BP1 | SR_BP0;int shift = ffs(mask) - 1;int pow;if (!(sr & mask)) {/* No protection */*ofs = 0;*len = 0;} else {pow = ((sr & mask) ^ mask) >> shift;*len = mtd->size >> pow;if (nor->flags & SNOR_F_HAS_SR_TB && sr & SR_TB)*ofs = 0;else*ofs = mtd->size - *len;}
}/** Return 1 if the entire region is locked (if @locked is true) or unlocked (if* @locked is false); 0 otherwise*/
static int stm_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,u8 sr, bool locked)
{loff_t lock_offs;uint64_t lock_len;if (!len)return 1;stm_get_locked_range(nor, sr, &lock_offs, &lock_len);if (locked)/* Requested range is a sub-range of locked range */return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);else/* Requested range does not overlap with locked range */return (ofs >= lock_offs + lock_len) || (ofs + len <= lock_offs);
}static int stm_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,u8 sr)
{return stm_check_lock_status_sr(nor, ofs, len, sr, true);
}static int stm_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,u8 sr)
{return stm_check_lock_status_sr(nor, ofs, len, sr, false);
}/** Lock a region of the flash. Compatible with ST Micro and similar flash.* Supports the block protection bits BP{0,1,2} in the status register* (SR). Does not support these features found in newer SR bitfields:*   - SEC: sector/block protect - only handle SEC=0 (block protect)*   - CMP: complement protect - only support CMP=0 (range is not complemented)** Support for the following is provided conditionally for some flash:*   - TB: top/bottom protect** Sample table portion for 8MB flash (Winbond w25q64fw):**   SEC  |  TB   |  BP2  |  BP1  |  BP0  |  Prot Length  | Protected Portion*  --------------------------------------------------------------------------*    X   |   X   |   0   |   0   |   0   |  NONE         | NONE*    0   |   0   |   0   |   0   |   1   |  128 KB       | Upper 1/64*    0   |   0   |   0   |   1   |   0   |  256 KB       | Upper 1/32*    0   |   0   |   0   |   1   |   1   |  512 KB       | Upper 1/16*    0   |   0   |   1   |   0   |   0   |  1 MB         | Upper 1/8*    0   |   0   |   1   |   0   |   1   |  2 MB         | Upper 1/4*    0   |   0   |   1   |   1   |   0   |  4 MB         | Upper 1/2*    X   |   X   |   1   |   1   |   1   |  8 MB         | ALL*  ------|-------|-------|-------|-------|---------------|-------------------*    0   |   1   |   0   |   0   |   1   |  128 KB       | Lower 1/64*    0   |   1   |   0   |   1   |   0   |  256 KB       | Lower 1/32*    0   |   1   |   0   |   1   |   1   |  512 KB       | Lower 1/16*    0   |   1   |   1   |   0   |   0   |  1 MB         | Lower 1/8*    0   |   1   |   1   |   0   |   1   |  2 MB         | Lower 1/4*    0   |   1   |   1   |   1   |   0   |  4 MB         | Lower 1/2** Returns negative on errors, 0 on success.*/
static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
{struct mtd_info *mtd = &nor->mtd;int status_old, status_new;u8 mask = SR_BP2 | SR_BP1 | SR_BP0;u8 shift = ffs(mask) - 1, pow, val;loff_t lock_len;bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;bool use_top;int ret;status_old = read_sr(nor);if (status_old < 0)return status_old;/* If nothing in our range is unlocked, we don't need to do anything */if (stm_is_locked_sr(nor, ofs, len, status_old))return 0;/* If anything below us is unlocked, we can't use 'bottom' protection */if (!stm_is_locked_sr(nor, 0, ofs, status_old))can_be_bottom = false;/* If anything above us is unlocked, we can't use 'top' protection */if (!stm_is_locked_sr(nor, ofs + len, mtd->size - (ofs + len),status_old))can_be_top = false;if (!can_be_bottom && !can_be_top)return -EINVAL;/* Prefer top, if both are valid */use_top = can_be_top;/* lock_len: length of region that should end up locked */if (use_top)lock_len = mtd->size - ofs;elselock_len = ofs + len;/** Need smallest pow such that:**   1 / (2^pow) <= (len / size)** so (assuming power-of-2 size) we do:**   pow = ceil(log2(size / len)) = log2(size) - floor(log2(len))*/pow = ilog2(mtd->size) - ilog2(lock_len);val = mask - (pow << shift);if (val & ~mask)return -EINVAL;/* Don't "lock" with no region! */if (!(val & mask))return -EINVAL;status_new = (status_old & ~mask & ~SR_TB) | val;/* Disallow further writes if WP pin is asserted */status_new |= SR_SRWD;if (!use_top)status_new |= SR_TB;/* Don't bother if they're the same */if (status_new == status_old)return 0;/* Only modify protection if it will not unlock other areas */if ((status_new & mask) < (status_old & mask))return -EINVAL;write_enable(nor);ret = write_sr(nor, status_new);if (ret)return ret;return spi_nor_wait_till_ready(nor);
}/** Unlock a region of the flash. See stm_lock() for more info** Returns negative on errors, 0 on success.*/
static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
{struct mtd_info *mtd = &nor->mtd;int status_old, status_new;u8 mask = SR_BP2 | SR_BP1 | SR_BP0;u8 shift = ffs(mask) - 1, pow, val;loff_t lock_len;bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;bool use_top;int ret;status_old = read_sr(nor);if (status_old < 0)return status_old;/* If nothing in our range is locked, we don't need to do anything */if (stm_is_unlocked_sr(nor, ofs, len, status_old))return 0;/* If anything below us is locked, we can't use 'top' protection */if (!stm_is_unlocked_sr(nor, 0, ofs, status_old))can_be_top = false;/* If anything above us is locked, we can't use 'bottom' protection */if (!stm_is_unlocked_sr(nor, ofs + len, mtd->size - (ofs + len),status_old))can_be_bottom = false;if (!can_be_bottom && !can_be_top)return -EINVAL;/* Prefer top, if both are valid */use_top = can_be_top;/* lock_len: length of region that should remain locked */if (use_top)lock_len = mtd->size - (ofs + len);elselock_len = ofs;/** Need largest pow such that:**   1 / (2^pow) >= (len / size)** so (assuming power-of-2 size) we do:**   pow = floor(log2(size / len)) = log2(size) - ceil(log2(len))*/pow = ilog2(mtd->size) - order_base_2(lock_len);if (lock_len == 0) {val = 0; /* fully unlocked */} else {val = mask - (pow << shift);/* Some power-of-two sizes are not supported */if (val & ~mask)return -EINVAL;}status_new = (status_old & ~mask & ~SR_TB) | val;/* Don't protect status register if we're fully unlocked */if (lock_len == 0)status_new &= ~SR_SRWD;if (!use_top)status_new |= SR_TB;/* Don't bother if they're the same */if (status_new == status_old)return 0;/* Only modify protection if it will not lock other areas */if ((status_new & mask) > (status_old & mask))return -EINVAL;write_enable(nor);ret = write_sr(nor, status_new);if (ret)return ret;return spi_nor_wait_till_ready(nor);
}/** Check if a region of the flash is (completely) locked. See stm_lock() for* more info.** Returns 1 if entire region is locked, 0 if any portion is unlocked, and* negative on errors.*/
static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
{int status;status = read_sr(nor);if (status < 0)return status;return stm_is_locked_sr(nor, ofs, len, status);
}static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{struct spi_nor *nor = mtd_to_spi_nor(mtd);int ret;ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK);if (ret)return ret;ret = nor->flash_lock(nor, ofs, len);spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK);return ret;
}static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{struct spi_nor *nor = mtd_to_spi_nor(mtd);int ret;ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK);if (ret)return ret;ret = nor->flash_unlock(nor, ofs, len);spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);return ret;
}static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{struct spi_nor *nor = mtd_to_spi_nor(mtd);int ret;ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK);if (ret)return ret;ret = nor->flash_is_locked(nor, ofs, len);spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);return ret;
}/* Used when the "_ext_id" is two bytes at most */
#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),#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \.id = {                           \((_jedec_id) >> 16) & 0xff,          \((_jedec_id) >> 8) & 0xff,           \(_jedec_id) & 0xff,                \((_ext_id) >> 16) & 0xff,            \((_ext_id) >> 8) & 0xff,         \(_ext_id) & 0xff,              \},                     \.id_len = 6,                      \.sector_size = (_sector_size),                \.n_sectors = (_n_sectors),                \.page_size = 256,                 \.flags = (_flags),#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags)   \.sector_size = (_sector_size),                \.n_sectors = (_n_sectors),                \.page_size = (_page_size),                \.addr_width = (_addr_width),              \.flags = (_flags),#define S3AN_INFO(_jedec_id, _n_sectors, _page_size)            \.id = {                           \((_jedec_id) >> 16) & 0xff,          \((_jedec_id) >> 8) & 0xff,           \(_jedec_id) & 0xff             \},                     \.id_len = 3,                      \.sector_size = (8*_page_size),                \.n_sectors = (_n_sectors),                \.page_size = _page_size,              \.addr_width = 3,                  \.flags = SPI_NOR_NO_FR | SPI_S3AN,/* NOTE: double check command sets and memory organization when you add* more nor chips.  This current list focusses on newer chips, which* have been converging on command sets which including JEDEC ID.** All newly added entries should describe *hardware* and should use SECT_4K* (or SECT_4K_PMC) if hardware supports erasing 4 KiB sectors. For usage* scenarios excluding small sectors there is config option that can be* disabled: CONFIG_MTD_SPI_NOR_USE_4K_SECTORS.* For historical (and compatibility) reasons (before we got above config) some* old entries may be missing 4K flag.*/
static const struct flash_info spi_nor_ids[] = {/* Atmel -- some are (confusingly) marketed as "DataFlash" */{ "at25fs010",  INFO(0x1f6601, 0, 32 * 1024,   4, SECT_4K) },{ "at25fs040",  INFO(0x1f6604, 0, 64 * 1024,   8, SECT_4K) },{ "at25df041a", INFO(0x1f4401, 0, 64 * 1024,   8, SECT_4K) },{ "at25df321",  INFO(0x1f4700, 0, 64 * 1024,  64, SECT_4K) },{ "at25df321a", INFO(0x1f4701, 0, 64 * 1024,  64, SECT_4K) },{ "at25df641",  INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) },{ "at26f004",   INFO(0x1f0400, 0, 64 * 1024,  8, SECT_4K) },{ "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) },{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) },{ "at26df321",  INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) },{ "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) },/* EON -- en25xxx */{ "en25f32",    INFO(0x1c3116, 0, 64 * 1024,   64, SECT_4K) },{ "en25p32",    INFO(0x1c2016, 0, 64 * 1024,   64, 0) },{ "en25q32b",   INFO(0x1c3016, 0, 64 * 1024,   64, 0) },{ "en25p64",    INFO(0x1c2017, 0, 64 * 1024,  128, 0) },{ "en25q64",    INFO(0x1c3017, 0, 64 * 1024,  128, SECT_4K) },{ "en25qh128",  INFO(0x1c7018, 0, 64 * 1024,  256, 0) },{ "en25qh256",  INFO(0x1c7019, 0, 64 * 1024,  512, 0) },{ "en25s64",    INFO(0x1c3817, 0, 64 * 1024,  128, SECT_4K) },/* ESMT */{ "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) },{ "f25l32qa", INFO(0x8c4116, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) },{ "f25l64qa", INFO(0x8c4117, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_LOCK) },/* Everspin */{ "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },{ "mr25h10",  CAT25_INFO(128 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },{ "mr25h40",  CAT25_INFO(512 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },/* Fujitsu */{ "mb85rs1mt", INFO(0x047f27, 0, 128 * 1024, 1, SPI_NOR_NO_ERASE) },/* GigaDevice */{"gd25q16", INFO(0xc84015, 0, 64 * 1024,  32,SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)},{"gd25q32", INFO(0xc84016, 0, 64 * 1024,  64,SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)},{"gd25q64", INFO(0xc84017, 0, 64 * 1024, 128,SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)},{"gd25lq64c", INFO(0xc86017, 0, 64 * 1024, 128,SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)},{"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)},{"gd25s512", INFO(0xc84019, 0, 64 * 1024, 1024,SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB|SPI_NOR_SKIP_SFDP)},/* Intel/Numonyx -- xxxs33b */{ "160s33b",  INFO(0x898911, 0, 64 * 1024,  32, 0) },{ "320s33b",  INFO(0x898912, 0, 64 * 1024,  64, 0) },{ "640s33b",  INFO(0x898913, 0, 64 * 1024, 128, 0) },/* ISSI */{ "is25lq512b",INFO(0x9d4010, 0, 32 * 1024,   2, SECT_4K) },{ "is25cd512", INFO(0x7f9d20, 0, 32 * 1024,   2, SECT_4K) },/* Macronix */{ "mx25l512e",   INFO(0xc22010, 0, 64 * 1024,   1, SECT_4K) },{ "mx25l2005a",  INFO(0xc22012, 0, 64 * 1024,   4, SECT_4K) },{ "mx25l4005a",  INFO(0xc22013, 0, 64 * 1024,   8, SECT_4K) },{ "mx25l8005",   INFO(0xc22014, 0, 64 * 1024,  16, 0) },{ "mx25l1606e",  INFO(0xc22015, 0, 64 * 1024,  32, SECT_4K) },{ "mx25l3205d",  INFO(0xc22016, 0, 64 * 1024,  64, SECT_4K) },{ "mx25l3255e",  INFO(0xc29e16, 0, 64 * 1024,  64, SECT_4K) },{ "mx25l6405d",  INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K) },{ "mx25u2033e",  INFO(0xc22532, 0, 64 * 1024,   4, SECT_4K) },{ "mx25u4035",   INFO(0xc22533, 0, 64 * 1024,   8, SECT_4K) },{ "mx25u8035",   INFO(0xc22534, 0, 64 * 1024,  16, SECT_4K) },{ "mx25u6435f",  INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) },{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },{ "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_OPCODES) },{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },{ "mx66u51235f", INFO(0xc2253a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },{ "mx66l1g45g",  INFO(0xc2201b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },{ "mx66l1g55g",  INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },/* Micron */{ "n25q016a",   INFO(0x20bb15, 0, 64 * 1024,   32, SECT_4K | SPI_NOR_QUAD_READ) },{ "n25q032",    INFO(0x20ba16, 0, 64 * 1024,   64, SPI_NOR_QUAD_READ) },{ "n25q032a",     INFO(0x20bb16, 0, 64 * 1024,   64, SPI_NOR_QUAD_READ) },{ "n25q064",     INFO(0x20ba17, 0, 64 * 1024,  128, SECT_4K | SPI_NOR_QUAD_READ) },{ "n25q064a",    INFO(0x20bb17, 0, 64 * 1024,  128, SECT_4K | SPI_NOR_QUAD_READ) },{ "n25q128a11",  INFO(0x20bb18, 0, 64 * 1024,  256, SECT_4K | SPI_NOR_QUAD_READ) },{ "n25q128a13",  INFO(0x20ba18, 0, 64 * 1024,  256, SECT_4K | SPI_NOR_QUAD_READ) },{ "n25q256a",    INFO(0x20ba19, 0, 64 * 1024,  512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },{ "n25q256ax1",  INFO(0x20bb19, 0, 64 * 1024,  512, SECT_4K | SPI_NOR_QUAD_READ) },{ "n25q512a",    INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },{ "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },{ "n25q00",      INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },{ "n25q00a",     INFO(0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },/* PMC */{ "pm25lv512",   INFO(0,        0, 32 * 1024,    2, SECT_4K_PMC) },{ "pm25lv010",   INFO(0,        0, 32 * 1024,    4, SECT_4K_PMC) },{ "pm25lq032",   INFO(0x7f9d46, 0, 64 * 1024,   64, SECT_4K) },/* Spansion -- single (large) sector size only, at least* for the chips listed here (without boot sectors).*/{ "s25sl032p",  INFO(0x010215, 0x4d00,  64 * 1024,  64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },{ "s25sl064p",  INFO(0x010216, 0x4d00,  64 * 1024, 128, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },{ "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, USE_CLSR) },{ "s25fl256s1", INFO(0x010219, 0x4d01,  64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },{ "s25fl512s",  INFO(0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },{ "s70fl01gs",  INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024,  64, 0) },{ "s25sl12801", INFO(0x012018, 0x0301,  64 * 1024, 256, 0) },{ "s25fl128s",  INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024,  64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },{ "s25fl129p1", INFO(0x012018, 0x4d01,  64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },{ "s25sl004a",  INFO(0x010212,      0,  64 * 1024,   8, 0) },{ "s25sl008a",  INFO(0x010213,      0,  64 * 1024,  16, 0) },{ "s25sl016a",  INFO(0x010214,      0,  64 * 1024,  32, 0) },{ "s25sl032a",  INFO(0x010215,      0,  64 * 1024,  64, 0) },{ "s25sl064a",  INFO(0x010216,      0,  64 * 1024, 128, 0) },{ "s25fl004k",  INFO(0xef4013,      0,  64 * 1024,   8, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },{ "s25fl008k",  INFO(0xef4014,      0,  64 * 1024,  16, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },{ "s25fl016k",  INFO(0xef4015,      0,  64 * 1024,  32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },{ "s25fl064k",  INFO(0xef4017,      0,  64 * 1024, 128, SECT_4K) },{ "s25fl116k",  INFO(0x014015,      0,  64 * 1024,  32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },{ "s25fl132k",  INFO(0x014016,      0,  64 * 1024,  64, SECT_4K) },{ "s25fl164k",  INFO(0x014017,      0,  64 * 1024, 128, SECT_4K) },{ "s25fl204k",  INFO(0x014013,      0,  64 * 1024,   8, SECT_4K | SPI_NOR_DUAL_READ) },{ "s25fl208k",  INFO(0x014014,      0,  64 * 1024,  16, SECT_4K | SPI_NOR_DUAL_READ) },{ "s25fl064l",  INFO(0x016017,      0,  64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },/* SST -- large erase sizes are "overlays", "sectors" are 4K */{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) },{ "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },{ "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) },{ "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) },{ "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) },{ "sst25wf512",  INFO(0xbf2501, 0, 64 * 1024,  1, SECT_4K | SST_WRITE) },{ "sst25wf010",  INFO(0xbf2502, 0, 64 * 1024,  2, SECT_4K | SST_WRITE) },{ "sst25wf020",  INFO(0xbf2503, 0, 64 * 1024,  4, SECT_4K | SST_WRITE) },{ "sst25wf020a", INFO(0x621612, 0, 64 * 1024,  4, SECT_4K) },{ "sst25wf040b", INFO(0x621613, 0, 64 * 1024,  8, SECT_4K) },{ "sst25wf040",  INFO(0xbf2504, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) },{ "sst25wf080",  INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },{ "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },/* ST Microelectronics -- newer production may have feature updates */{ "m25p05",  INFO(0x202010,  0,  32 * 1024,   2, 0) },{ "m25p10",  INFO(0x202011,  0,  32 * 1024,   4, 0) },{ "m25p20",  INFO(0x202012,  0,  64 * 1024,   4, 0) },{ "m25p40",  INFO(0x202013,  0,  64 * 1024,   8, 0) },{ "m25p80",  INFO(0x202014,  0,  64 * 1024,  16, 0) },{ "m25p16",  INFO(0x202015,  0,  64 * 1024,  32, 0) },{ "m25p32",  INFO(0x202016,  0,  64 * 1024,  64, 0) },{ "m25p64",  INFO(0x202017,  0,  64 * 1024, 128, 0) },{ "m25p128", INFO(0x202018,  0, 256 * 1024,  64, 0) },{ "m25p05-nonjedec",  INFO(0, 0,  32 * 1024,   2, 0) },{ "m25p10-nonjedec",  INFO(0, 0,  32 * 1024,   4, 0) },{ "m25p20-nonjedec",  INFO(0, 0,  64 * 1024,   4, 0) },{ "m25p40-nonjedec",  INFO(0, 0,  64 * 1024,   8, 0) },{ "m25p80-nonjedec",  INFO(0, 0,  64 * 1024,  16, 0) },{ "m25p16-nonjedec",  INFO(0, 0,  64 * 1024,  32, 0) },{ "m25p32-nonjedec",  INFO(0, 0,  64 * 1024,  64, 0) },{ "m25p64-nonjedec",  INFO(0, 0,  64 * 1024, 128, 0) },{ "m25p128-nonjedec", INFO(0, 0, 256 * 1024,  64, 0) },{ "m45pe10", INFO(0x204011,  0, 64 * 1024,    2, 0) },{ "m45pe80", INFO(0x204014,  0, 64 * 1024,   16, 0) },{ "m45pe16", INFO(0x204015,  0, 64 * 1024,   32, 0) },{ "m25pe20", INFO(0x208012,  0, 64 * 1024,  4,       0) },{ "m25pe80", INFO(0x208014,  0, 64 * 1024, 16,       0) },{ "m25pe16", INFO(0x208015,  0, 64 * 1024, 32, SECT_4K) },{ "m25px16",    INFO(0x207115,  0, 64 * 1024, 32, SECT_4K) },{ "m25px32",    INFO(0x207116,  0, 64 * 1024, 64, SECT_4K) },{ "m25px32-s0", INFO(0x207316,  0, 64 * 1024, 64, SECT_4K) },{ "m25px32-s1", INFO(0x206316,  0, 64 * 1024, 64, SECT_4K) },{ "m25px64",    INFO(0x207117,  0, 64 * 1024, 128, 0) },{ "m25px80",    INFO(0x207114,  0, 64 * 1024, 16, 0) },/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */{ "w25x05", INFO(0xef3010, 0, 64 * 1024,  1,  SECT_4K) },{ "w25x10", INFO(0xef3011, 0, 64 * 1024,  2,  SECT_4K) },{ "w25x20", INFO(0xef3012, 0, 64 * 1024,  4,  SECT_4K) },{ "w25x40", INFO(0xef3013, 0, 64 * 1024,  8,  SECT_4K) },{ "w25x80", INFO(0xef3014, 0, 64 * 1024,  16, SECT_4K) },{ "w25x16", INFO(0xef3015, 0, 64 * 1024,  32, SECT_4K) },{ "w25x32", INFO(0xef3016, 0, 64 * 1024,  64, SECT_4K) },{ "w25q20cl", INFO(0xef4012, 0, 64 * 1024,  4, SECT_4K) },{ "w25q20bw", INFO(0xef5012, 0, 64 * 1024,  4, SECT_4K) },{ "w25q20ew", INFO(0xef6012, 0, 64 * 1024,  4, SECT_4K) },{ "w25q32", INFO(0xef4016, 0, 64 * 1024,  64, SECT_4K) },{"w25q32dw", INFO(0xef6016, 0, 64 * 1024,  64,SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)},{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },{"w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128,SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)},{"w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256,SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)},{ "w25q80", INFO(0xef5014, 0, 64 * 1024,  16, SECT_4K) },{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024,  16, SECT_4K) },{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },{ "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024,SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ) },/* Catalyst / On Semiconductor -- non-JEDEC */{ "cat25c11", CAT25_INFO(  16, 8, 16, 1, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },{ "cat25c03", CAT25_INFO(  32, 8, 16, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },{ "cat25c09", CAT25_INFO( 128, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },{ "cat25c17", CAT25_INFO( 256, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },{ "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },/* Xilinx S3AN Internal Flash */{ "3S50AN", S3AN_INFO(0x1f2200, 64, 264) },{ "3S200AN", S3AN_INFO(0x1f2400, 256, 264) },{ "3S400AN", S3AN_INFO(0x1f2400, 256, 264) },{ "3S700AN", S3AN_INFO(0x1f2500, 512, 264) },{ "3S1400AN", S3AN_INFO(0x1f2600, 512, 528) },{ },
};static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
{int            tmp;u8          id[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);
}static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,size_t *retlen, u_char *buf)
{struct spi_nor *nor = mtd_to_spi_nor(mtd);int ret;u8 val;dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_READ);if (ret)return ret;if(from>=0x2000000){nor->read_reg(nor, 0xf8, &val, 1);while(val!=1){nor->cmd_buf[0] = 0x01;nor->write_reg(nor, SPINOR_OP_SDS, nor->cmd_buf, 1);udelay(10);nor->read_reg(nor, 0xf8, &val, 1);}}else{nor->read_reg(nor, 0xf8, &val, 1);while(val!=0){nor->cmd_buf[0] = 0x00;nor->write_reg(nor, SPINOR_OP_SDS, nor->cmd_buf, 1);udelay(10);nor->read_reg(nor, 0xf8, &val, 1);}}while (len) {loff_t addr = from;if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)addr = spi_nor_s3an_addr_convert(nor, addr);if(addr>=0x2000000){ret = nor->read(nor, addr-0x2000000, len, buf);}else{ret = nor->read(nor, addr, len, buf);}if (ret == 0) {/* We shouldn't see 0-length reads */ret = -EIO;goto read_err;}if (ret < 0)goto read_err;WARN_ON(ret > len);*retlen += ret;buf += ret;from += ret;len -= ret;}ret = 0;read_err:spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ);return ret;
}static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,size_t *retlen, const u_char *buf)
{struct spi_nor *nor = mtd_to_spi_nor(mtd);size_t actual;int ret;dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE);if (ret)return ret;write_enable(nor);nor->sst_write_second = false;if(to >= 0x2000000){nor->cmd_buf[0] = 0x01;nor->write_reg(nor, SPINOR_OP_SDS, nor->cmd_buf, 1);actual = to % 2;/* Start write from odd address. */if (actual) {nor->program_opcode = SPINOR_OP_BP;/* write one byte. */ret = nor->write(nor, to, 1, buf);if (ret < 0)goto sst_write_err;WARN(ret != 1, "While writing 1 byte written %i bytes\n",(int)ret);ret = spi_nor_wait_till_ready(nor);if (ret)goto sst_write_err;}to += actual;/* Write out most of the data here. */for (; actual < len - 1; actual += 2) {nor->program_opcode = SPINOR_OP_AAI_WP;/* write two bytes. */ret = nor->write(nor, to, 2, buf + actual);if (ret < 0)goto sst_write_err;WARN(ret != 2, "While writing 2 bytes written %i bytes\n",(int)ret);ret = spi_nor_wait_till_ready(nor);if (ret)goto sst_write_err;to += 2;nor->sst_write_second = true;}nor->sst_write_second = false;write_disable(nor);ret = spi_nor_wait_till_ready(nor);if (ret)goto sst_write_err;/* Write out trailing byte if it exists. */if (actual != len) {write_enable(nor);nor->program_opcode = SPINOR_OP_BP;ret = nor->write(nor, to, 1, buf + actual);if (ret < 0)goto sst_write_err;WARN(ret != 1, "While writing 1 byte written %i bytes\n",(int)ret);ret = spi_nor_wait_till_ready(nor);if (ret)goto sst_write_err;write_disable(nor);actual += 1;}}else if((to+len)>0x2000000){size_t len0;len0 = 0x2000000-to;len = len-(0x2000000-to);nor->cmd_buf[0] = 0x00;nor->write_reg(nor, SPINOR_OP_SDS, nor->cmd_buf, 1);while(len0){nor->cmd_buf[0] = 0x00;nor->write_reg(nor, SPINOR_OP_SDS, nor->cmd_buf, 1);actual = to % 2;/* Start write from odd address. */if (actual) {nor->program_opcode = SPINOR_OP_BP;/* write one byte. */ret = nor->write(nor, to, 1, buf);if (ret < 0)goto sst_write_err;WARN(ret != 1, "While writing 1 byte written %i bytes\n",(int)ret);ret = spi_nor_wait_till_ready(nor);if (ret)goto sst_write_err;}to += actual;/* Write out most of the data here. */for (; actual < len0 - 1; actual += 2) {nor->program_opcode = SPINOR_OP_AAI_WP;/* write two bytes. */ret = nor->write(nor, to, 2, buf + actual);if (ret < 0)goto sst_write_err;WARN(ret != 2, "While writing 2 bytes written %i bytes\n",(int)ret);ret = spi_nor_wait_till_ready(nor);if (ret)goto sst_write_err;to += 2;nor->sst_write_second = true;}nor->sst_write_second = false;write_disable(nor);ret = spi_nor_wait_till_ready(nor);if (ret)goto sst_write_err;/* Write out trailing byte if it exists. */if (actual != len0) {write_enable(nor);nor->program_opcode = SPINOR_OP_BP;ret = nor->write(nor, to, 1, buf + actual);if (ret < 0)goto sst_write_err;WARN(ret != 1, "While writing 1 byte written %i bytes\n",(int)ret);ret = spi_nor_wait_till_ready(nor);if (ret)goto sst_write_err;write_disable(nor);actual += 1;to += actual;}len0 -= actual;}nor->cmd_buf[0] = 0x01;nor->write_reg(nor, SPINOR_OP_SDS, nor->cmd_buf, 1);actual = to % 2;/* Start write from odd address. */if (actual) {nor->program_opcode = SPINOR_OP_BP;/* write one byte. */ret = nor->write(nor, to, 1, buf);if (ret < 0)goto sst_write_err;WARN(ret != 1, "While writing 1 byte written %i bytes\n",(int)ret);ret = spi_nor_wait_till_ready(nor);if (ret)goto sst_write_err;}to += actual;/* Write out most of the data here. */for (; actual < len - 1; actual += 2) {nor->program_opcode = SPINOR_OP_AAI_WP;/* write two bytes. */ret = nor->write(nor, to, 2, buf + actual);if (ret < 0)goto sst_write_err;WARN(ret != 2, "While writing 2 bytes written %i bytes\n",(int)ret);ret = spi_nor_wait_till_ready(nor);if (ret)goto sst_write_err;to += 2;nor->sst_write_second = true;}nor->sst_write_second = false;write_disable(nor);ret = spi_nor_wait_till_ready(nor);if (ret)goto sst_write_err;/* Write out trailing byte if it exists. */if (actual != len) {write_enable(nor);nor->program_opcode = SPINOR_OP_BP;ret = nor->write(nor, to, 1, buf + actual);if (ret < 0)goto sst_write_err;WARN(ret != 1, "While writing 1 byte written %i bytes\n",(int)ret);ret = spi_nor_wait_till_ready(nor);if (ret)goto sst_write_err;write_disable(nor);actual += 1;}}else{nor->cmd_buf[0] = 0x00;nor->write_reg(nor, SPINOR_OP_SDS, nor->cmd_buf, 1);actual = to % 2;/* Start write from odd address. */if (actual) {nor->program_opcode = SPINOR_OP_BP;/* write one byte. */ret = nor->write(nor, to, 1, buf);if (ret < 0)goto sst_write_err;WARN(ret != 1, "While writing 1 byte written %i bytes\n",(int)ret);ret = spi_nor_wait_till_ready(nor);if (ret)goto sst_write_err;}to += actual;/* Write out most of the data here. */for (; actual < len - 1; actual += 2) {nor->program_opcode = SPINOR_OP_AAI_WP;/* write two bytes. */ret = nor->write(nor, to, 2, buf + actual);if (ret < 0)goto sst_write_err;WARN(ret != 2, "While writing 2 bytes written %i bytes\n",(int)ret);ret = spi_nor_wait_till_ready(nor);if (ret)goto sst_write_err;to += 2;nor->sst_write_second = true;}nor->sst_write_second = false;write_disable(nor);ret = spi_nor_wait_till_ready(nor);if (ret)goto sst_write_err;/* Write out trailing byte if it exists. */if (actual != len) {write_enable(nor);nor->program_opcode = SPINOR_OP_BP;ret = nor->write(nor, to, 1, buf + actual);if (ret < 0)goto sst_write_err;WARN(ret != 1, "While writing 1 byte written %i bytes\n",(int)ret);ret = spi_nor_wait_till_ready(nor);if (ret)goto sst_write_err;write_disable(nor);actual += 1;}}
sst_write_err:*retlen += actual;spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);return ret;
}/** Write an address range to the nor chip.  Data must be written in* FLASH_PAGESIZE chunks.  The address range may be any size provided* it is within the physical boundaries.*/
static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,size_t *retlen, const u_char *buf)
{struct spi_nor *nor = mtd_to_spi_nor(mtd);size_t page_offset, page_remain, i;ssize_t ret;u8 val;dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE);if (ret)return ret;if(to >= 0x2000000){nor->read_reg(nor, 0xf8, &val, 1);while(val!=1){nor->cmd_buf[0] = 0x01;nor->write_reg(nor, SPINOR_OP_SDS, nor->cmd_buf, 1);udelay(100);nor->read_reg(nor, 0xf8, &val, 1);}to= to  - 0x2000000;}else{nor->read_reg(nor, 0xf8, &val, 1);while(val!=0){nor->cmd_buf[0] = 0x00;nor->write_reg(nor, SPINOR_OP_SDS, nor->cmd_buf, 1);udelay(100);nor->read_reg(nor, 0xf8, &val, 1);}}for (i = 0; i < len; ) {ssize_t written;loff_t addr = to + i ;/** If page_size is a power of two, the offset can be quickly* calculated with an AND operation. On the other cases we* need to do a modulus operation (more expensive).* Power of two numbers have only one bit set and we can use* the instruction hweight32 to detect if we need to do a* modulus (do_div()) or not.*/if (hweight32(nor->page_size) == 1) {page_offset = addr & (nor->page_size - 1);} else {uint64_t aux = addr;page_offset = do_div(aux, nor->page_size);}/* the size of data remaining on the first page */page_remain = min_t(size_t,nor->page_size - page_offset, len - i);if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)addr = spi_nor_s3an_addr_convert(nor, addr);write_enable(nor);ret = nor->write(nor, addr, page_remain, buf + i);if (ret < 0)goto write_err;written = ret;ret = spi_nor_wait_till_ready(nor);if (ret)goto write_err;*retlen += written;i += written;if (written != page_remain) {dev_err(nor->dev,"While writing %zu bytes written %zd bytes\n",page_remain, written);ret = -EIO;goto write_err;}}write_err:spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);return ret;
}/*** macronix_quad_enable() - set QE bit in Status Register.* @nor:   pointer to a 'struct spi_nor'** Set the Quad Enable (QE) bit in the Status Register.** bit 6 of the Status Register is the QE bit for Macronix like QSPI memories.** Return: 0 on success, -errno otherwise.*/
static int macronix_quad_enable(struct spi_nor *nor)
{int ret, val;val = read_sr(nor);if (val < 0)return val;if (val & SR_QUAD_EN_MX)return 0;write_enable(nor);write_sr(nor, val | SR_QUAD_EN_MX);ret = spi_nor_wait_till_ready(nor);if (ret)return ret;ret = read_sr(nor);if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) {dev_err(nor->dev, "Macronix Quad bit not set\n");return -EINVAL;}return 0;
}/** Write status Register and configuration register with 2 bytes* The first byte will be written to the status register, while the* second byte will be written to the configuration register.* Return negative if error occurred.*/
static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr)
{int ret;write_enable(nor);ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2);if (ret < 0) {dev_err(nor->dev,"error while writing configuration register\n");return -EINVAL;}ret = spi_nor_wait_till_ready(nor);if (ret) {dev_err(nor->dev,"timeout while writing configuration register\n");return ret;}return 0;
}/*** spansion_quad_enable() - set QE bit in Configuraiton Register.* @nor:    pointer to a 'struct spi_nor'** Set the Quad Enable (QE) bit in the Configuration Register.* This function is kept for legacy purpose because it has been used for a* long time without anybody complaining but it should be considered as* deprecated and maybe buggy.* First, this function doesn't care about the previous values of the Status* and Configuration Registers when it sets the QE bit (bit 1) in the* Configuration Register: all other bits are cleared, which may have unwanted* side effects like removing some block protections.* Secondly, it uses the Read Configuration Register (35h) instruction though* some very old and few memories don't support this instruction. If a pull-up* resistor is present on the MISO/IO1 line, we might still be able to pass the* "read back" test because the QSPI memory doesn't recognize the command,* so leaves the MISO/IO1 line state unchanged, hence read_cr() returns 0xFF.** bit 1 of the Configuration Register is the QE bit for Spansion like QSPI* memories.** Return: 0 on success, -errno otherwise.*/
static int spansion_quad_enable(struct spi_nor *nor)
{u8 sr_cr[2] = {0, CR_QUAD_EN_SPAN};int ret;ret = write_sr_cr(nor, sr_cr);if (ret)return ret;/* read back and check it */ret = read_cr(nor);if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {dev_err(nor->dev, "Spansion Quad bit not set\n");return -EINVAL;}return 0;
}/*** spansion_no_read_cr_quad_enable() - set QE bit in Configuration Register.* @nor: pointer to a 'struct spi_nor'** Set the Quad Enable (QE) bit in the Configuration Register.* This function should be used with QSPI memories not supporting the Read* Configuration Register (35h) instruction.** bit 1 of the Configuration Register is the QE bit for Spansion like QSPI* memories.** Return: 0 on success, -errno otherwise.*/
static int spansion_no_read_cr_quad_enable(struct spi_nor *nor)
{u8 sr_cr[2];int ret;/* Keep the current value of the Status Register. */ret = read_sr(nor);if (ret < 0) {dev_err(nor->dev, "error while reading status register\n");return -EINVAL;}sr_cr[0] = ret;sr_cr[1] = CR_QUAD_EN_SPAN;return write_sr_cr(nor, sr_cr);
}/*** spansion_read_cr_quad_enable() - set QE bit in Configuration Register.* @nor:    pointer to a 'struct spi_nor'** Set the Quad Enable (QE) bit in the Configuration Register.* This function should be used with QSPI memories supporting the Read* Configuration Register (35h) instruction.** bit 1 of the Configuration Register is the QE bit for Spansion like QSPI* memories.** Return: 0 on success, -errno otherwise.*/
static int spansion_read_cr_quad_enable(struct spi_nor *nor)
{struct device *dev = nor->dev;u8 sr_cr[2];int ret;/* Check current Quad Enable bit value. */ret = read_cr(nor);if (ret < 0) {dev_err(dev, "error while reading configuration register\n");return -EINVAL;}if (ret & CR_QUAD_EN_SPAN)return 0;sr_cr[1] = ret | CR_QUAD_EN_SPAN;/* Keep the current value of the Status Register. */ret = read_sr(nor);if (ret < 0) {dev_err(dev, "error while reading status register\n");return -EINVAL;}sr_cr[0] = ret;ret = write_sr_cr(nor, sr_cr);if (ret)return ret;/* Read back and check it. */ret = read_cr(nor);if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {dev_err(nor->dev, "Spansion Quad bit not set\n");return -EINVAL;}return 0;
}/*** sr2_bit7_quad_enable() - set QE bit in Status Register 2.* @nor: pointer to a 'struct spi_nor'** Set the Quad Enable (QE) bit in the Status Register 2.** This is one of the procedures to set the QE bit described in the SFDP* (JESD216 rev B) specification but no manufacturer using this procedure has* been identified yet, hence the name of the function.** Return: 0 on success, -errno otherwise.*/
static int sr2_bit7_quad_enable(struct spi_nor *nor)
{u8 sr2;int ret;/* Check current Quad Enable bit value. */ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1);if (ret)return ret;if (sr2 & SR2_QUAD_EN_BIT7)return 0;/* Update the Quad Enable bit. */sr2 |= SR2_QUAD_EN_BIT7;write_enable(nor);ret = nor->write_reg(nor, SPINOR_OP_WRSR2, &sr2, 1);if (ret < 0) {dev_err(nor->dev, "error while writing status register 2\n");return -EINVAL;}ret = spi_nor_wait_till_ready(nor);if (ret < 0) {dev_err(nor->dev, "timeout while writing status register 2\n");return ret;}/* Read back and check it. */ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1);if (!(ret > 0 && (sr2 & SR2_QUAD_EN_BIT7))) {dev_err(nor->dev, "SR2 Quad bit not set\n");return -EINVAL;}return 0;
}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;
}static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor)
{int ret;u8 val;ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1);if (ret < 0) {dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret);return ret;}nor->erase_opcode = SPINOR_OP_XSE;nor->program_opcode = SPINOR_OP_XPP;nor->read_opcode = SPINOR_OP_READ;nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;/** This flashes have a page size of 264 or 528 bytes (known as* Default addressing mode). It can be changed to a more standard* Power of two mode where the page size is 256/512. This comes* with a price: there is 3% less of space, the data is corrupted* and the page size cannot be changed back to default addressing* mode.** The current addressing mode can be read from the XRDSR register* and should not be changed, because is a destructive operation.*/if (val & XSR_PAGESIZE) {/* Flash in Power of 2 mode */nor->page_size = (nor->page_size == 264) ? 256 : 512;nor->mtd.writebufsize = nor->page_size;nor->mtd.size = 8 * nor->page_size * info->n_sectors;nor->mtd.erasesize = 8 * nor->page_size;} else {/* Flash in Default addressing mode */nor->flags |= SNOR_F_S3AN_ADDR_DEFAULT;}return 0;
}struct spi_nor_read_command {u8            num_mode_clocks;u8          num_wait_states;u8          opcode;enum spi_nor_protocol    proto;
};struct spi_nor_pp_command {u8         opcode;enum spi_nor_protocol    proto;
};enum spi_nor_read_command_index {SNOR_CMD_READ,SNOR_CMD_READ_FAST,SNOR_CMD_READ_1_1_1_DTR,/* Dual SPI */SNOR_CMD_READ_1_1_2,SNOR_CMD_READ_1_2_2,SNOR_CMD_READ_2_2_2,SNOR_CMD_READ_1_2_2_DTR,/* Quad SPI */SNOR_CMD_READ_1_1_4,SNOR_CMD_READ_1_4_4,SNOR_CMD_READ_4_4_4,SNOR_CMD_READ_1_4_4_DTR,/* Octo SPI */SNOR_CMD_READ_1_1_8,SNOR_CMD_READ_1_8_8,SNOR_CMD_READ_8_8_8,SNOR_CMD_READ_1_8_8_DTR,SNOR_CMD_READ_MAX
};enum spi_nor_pp_command_index {SNOR_CMD_PP,/* Quad SPI */SNOR_CMD_PP_1_1_4,SNOR_CMD_PP_1_4_4,SNOR_CMD_PP_4_4_4,/* Octo SPI */SNOR_CMD_PP_1_1_8,SNOR_CMD_PP_1_8_8,SNOR_CMD_PP_8_8_8,SNOR_CMD_PP_MAX
};struct spi_nor_flash_parameter {u64               size;u32                page_size;struct spi_nor_hwcaps     hwcaps;struct spi_nor_read_command  reads[SNOR_CMD_READ_MAX];struct spi_nor_pp_command  page_programs[SNOR_CMD_PP_MAX];int (*quad_enable)(struct spi_nor *nor);
};static void
spi_nor_set_read_settings(struct spi_nor_read_command *read,u8 num_mode_clocks,u8 num_wait_states,u8 opcode,enum spi_nor_protocol proto)
{read->num_mode_clocks = num_mode_clocks;read->num_wait_states = num_wait_states;read->opcode = opcode;read->proto = proto;
}static void
spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,u8 opcode,enum spi_nor_protocol proto)
{pp->opcode = opcode;pp->proto = proto;
}/** Serial Flash Discoverable Parameters (SFDP) parsing.*//*** spi_nor_read_sfdp() - read Serial Flash Discoverable Parameters.* @nor:    pointer to a 'struct spi_nor'* @addr:    offset in the SFDP area to start reading data from* @len:  number of bytes to read* @buf: buffer where the SFDP data are copied into (dma-safe memory)** Whatever the actual numbers of bytes for address and dummy cycles are* for (Fast) Read commands, the Read SFDP (5Ah) instruction is always* followed by a 3-byte address and 8 dummy clock cycles.** Return: 0 on success, -errno otherwise.*/
static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr,size_t len, void *buf)
{u8 addr_width, read_opcode, read_dummy;int ret;read_opcode = nor->read_opcode;addr_width = nor->addr_width;read_dummy = nor->read_dummy;nor->read_opcode = SPINOR_OP_RDSFDP;nor->addr_width = 3;nor->read_dummy = 8;while (len) {ret = nor->read(nor, addr, len, (u8 *)buf);if (!ret || ret > len) {ret = -EIO;goto read_err;}if (ret < 0)goto read_err;buf += ret;addr += ret;len -= ret;}ret = 0;read_err:nor->read_opcode = read_opcode;nor->addr_width = addr_width;nor->read_dummy = read_dummy;return ret;
}/*** spi_nor_read_sfdp_dma_unsafe() - read Serial Flash Discoverable Parameters.* @nor:   pointer to a 'struct spi_nor'* @addr:    offset in the SFDP area to start reading data from* @len:  number of bytes to read* @buf: buffer where the SFDP data are copied into** Wrap spi_nor_read_sfdp() using a kmalloc'ed bounce buffer as @buf is now not* guaranteed to be dma-safe.** Return: -ENOMEM if kmalloc() fails, the return code of spi_nor_read_sfdp()*          otherwise.*/
static int spi_nor_read_sfdp_dma_unsafe(struct spi_nor *nor, u32 addr,size_t len, void *buf)
{void *dma_safe_buf;int ret;dma_safe_buf = kmalloc(len, GFP_KERNEL);if (!dma_safe_buf)return -ENOMEM;ret = spi_nor_read_sfdp(nor, addr, len, dma_safe_buf);memcpy(buf, dma_safe_buf, len);kfree(dma_safe_buf);return ret;
}struct sfdp_parameter_header {u8       id_lsb;u8       minor;u8        major;u8        length; /* in double words */u8     parameter_table_pointer[3]; /* byte address */u8        id_msb;
};#define SFDP_PARAM_HEADER_ID(p)   (((p)->id_msb << 8) | (p)->id_lsb)
#define SFDP_PARAM_HEADER_PTP(p) \(((p)->parameter_table_pointer[2] << 16) | \((p)->parameter_table_pointer[1] <<  8) | \((p)->parameter_table_pointer[0] <<  0))#define SFDP_BFPT_ID        0xff00  /* Basic Flash Parameter Table */
#define SFDP_SECTOR_MAP_ID  0xff81  /* Sector Map Table */#define SFDP_SIGNATURE        0x50444653U
#define SFDP_JESD216_MAJOR  1
#define SFDP_JESD216_MINOR  0
#define SFDP_JESD216A_MINOR 5
#define SFDP_JESD216B_MINOR 6struct sfdp_header {u32        signature; /* Ox50444653U <=> "SFDP" */u8      minor;u8        major;u8        nph; /* 0-base number of parameter headers */u8     unused;/* Basic Flash Parameter Table. */struct sfdp_parameter_header   bfpt_header;
};/* Basic Flash Parameter Table *//** JESD216 rev B defines a Basic Flash Parameter Table of 16 DWORDs.* They are indexed from 1 but C arrays are indexed from 0.*/
#define BFPT_DWORD(i)       ((i) - 1)
#define BFPT_DWORD_MAX      16/* The first version of JESB216 defined only 9 DWORDs. */
#define BFPT_DWORD_MAX_JESD216          9/* 1st DWORD. */
#define BFPT_DWORD1_FAST_READ_1_1_2     BIT(16)
#define BFPT_DWORD1_ADDRESS_BYTES_MASK      GENMASK(18, 17)
#define BFPT_DWORD1_ADDRESS_BYTES_3_ONLY    (0x0UL << 17)
#define BFPT_DWORD1_ADDRESS_BYTES_3_OR_4    (0x1UL << 17)
#define BFPT_DWORD1_ADDRESS_BYTES_4_ONLY    (0x2UL << 17)
#define BFPT_DWORD1_DTR             BIT(19)
#define BFPT_DWORD1_FAST_READ_1_2_2     BIT(20)
#define BFPT_DWORD1_FAST_READ_1_4_4     BIT(21)
#define BFPT_DWORD1_FAST_READ_1_1_4     BIT(22)/* 5th DWORD. */
#define BFPT_DWORD5_FAST_READ_2_2_2     BIT(0)
#define BFPT_DWORD5_FAST_READ_4_4_4     BIT(4)/* 11th DWORD. */
#define BFPT_DWORD11_PAGE_SIZE_SHIFT        4
#define BFPT_DWORD11_PAGE_SIZE_MASK     GENMASK(7, 4)/* 15th DWORD. *//** (from JESD216 rev B)* Quad Enable Requirements (QER):* - 000b: Device does not have a QE bit. Device detects 1-1-4 and 1-4-4*         reads based on instruction. DQ3/HOLD# functions are hold during*         instruction phase.* - 001b: QE is bit 1 of status register 2. It is set via Write Status with*         two data bytes where bit 1 of the second byte is one.*         [...]*         Writing only one byte to the status register has the side-effect of*         clearing status register 2, including the QE bit. The 100b code is*         used if writing one byte to the status register does not modify*         status register 2.* - 010b: QE is bit 6 of status register 1. It is set via Write Status with*         one data byte where bit 6 is one.*         [...]* - 011b: QE is bit 7 of status register 2. It is set via Write status*         register 2 instruction 3Eh with one data byte where bit 7 is one.*         [...]*         The status register 2 is read using instruction 3Fh.* - 100b: QE is bit 1 of status register 2. It is set via Write Status with*         two data bytes where bit 1 of the second byte is one.*         [...]*         In contrast to the 001b code, writing one byte to the status*         register does not modify status register 2.* - 101b: QE is bit 1 of status register 2. Status register 1 is read using*         Read Status instruction 05h. Status register2 is read using*         instruction 35h. QE is set via Writ Status instruction 01h with*         two data bytes where bit 1 of the second byte is one.*         [...]*/
#define BFPT_DWORD15_QER_MASK           GENMASK(22, 20)
#define BFPT_DWORD15_QER_NONE           (0x0UL << 20) /* Micron */
#define BFPT_DWORD15_QER_SR2_BIT1_BUGGY     (0x1UL << 20)
#define BFPT_DWORD15_QER_SR1_BIT6       (0x2UL << 20) /* Macronix */
#define BFPT_DWORD15_QER_SR2_BIT7       (0x3UL << 20)
#define BFPT_DWORD15_QER_SR2_BIT1_NO_RD     (0x4UL << 20)
#define BFPT_DWORD15_QER_SR2_BIT1       (0x5UL << 20) /* Spansion */struct sfdp_bfpt {u32 dwords[BFPT_DWORD_MAX];
};/* Fast Read settings. */static inline void
spi_nor_set_read_settings_from_bfpt(struct spi_nor_read_command *read,u16 half,enum spi_nor_protocol proto)
{read->num_mode_clocks = (half >> 5) & 0x07;read->num_wait_states = (half >> 0) & 0x1f;read->opcode = (half >> 8) & 0xff;read->proto = proto;
}struct sfdp_bfpt_read {/* The Fast Read x-y-z hardware capability in params->hwcaps.mask. */u32         hwcaps;/** The <supported_bit> bit in <supported_dword> BFPT DWORD tells us* whether the Fast Read x-y-z command is supported.*/u32         supported_dword;u32         supported_bit;/** The half-word at offset <setting_shift> in <setting_dword> BFPT DWORD* encodes the op code, the number of mode clocks and the number of wait* states to be used by Fast Read x-y-z command.*/u32          settings_dword;u32          settings_shift;/* The SPI protocol for this Fast Read x-y-z command. */enum spi_nor_protocol    proto;
};static const struct sfdp_bfpt_read sfdp_bfpt_reads[] = {/* Fast Read 1-1-2 */{SNOR_HWCAPS_READ_1_1_2,BFPT_DWORD(1), BIT(16), /* Supported bit */BFPT_DWORD(4), 0,    /* Settings */SNOR_PROTO_1_1_2,},/* Fast Read 1-2-2 */{SNOR_HWCAPS_READ_1_2_2,BFPT_DWORD(1), BIT(20),   /* Supported bit */BFPT_DWORD(4), 16,   /* Settings */SNOR_PROTO_1_2_2,},/* Fast Read 2-2-2 */{SNOR_HWCAPS_READ_2_2_2,BFPT_DWORD(5),  BIT(0),   /* Supported bit */BFPT_DWORD(6), 16,   /* Settings */SNOR_PROTO_2_2_2,},/* Fast Read 1-1-4 */{SNOR_HWCAPS_READ_1_1_4,BFPT_DWORD(1), BIT(22),   /* Supported bit */BFPT_DWORD(3), 16,   /* Settings */SNOR_PROTO_1_1_4,},/* Fast Read 1-4-4 */{SNOR_HWCAPS_READ_1_4_4,BFPT_DWORD(1), BIT(21),   /* Supported bit */BFPT_DWORD(3), 0,    /* Settings */SNOR_PROTO_1_4_4,},/* Fast Read 4-4-4 */{SNOR_HWCAPS_READ_4_4_4,BFPT_DWORD(5), BIT(4),    /* Supported bit */BFPT_DWORD(7), 16,   /* Settings */SNOR_PROTO_4_4_4,},
};struct sfdp_bfpt_erase {/** The half-word at offset <shift> in DWORD <dwoard> encodes the* op code and erase sector size to be used by Sector Erase commands.*/u32            dword;u32           shift;
};static const struct sfdp_bfpt_erase sfdp_bfpt_erases[] = {/* Erase Type 1 in DWORD8 bits[15:0] */{BFPT_DWORD(8), 0},/* Erase Type 2 in DWORD8 bits[31:16] */{BFPT_DWORD(8), 16},/* Erase Type 3 in DWORD9 bits[15:0] */{BFPT_DWORD(9), 0},/* Erase Type 4 in DWORD9 bits[31:16] */{BFPT_DWORD(9), 16},
};static int spi_nor_hwcaps_read2cmd(u32 hwcaps);/*** spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table.* @nor:        pointer to a 'struct spi_nor'* @bfpt_header: pointer to the 'struct sfdp_parameter_header' describing*         the Basic Flash Parameter Table length and version* @params:       pointer to the 'struct spi_nor_flash_parameter' to be*            filled** The Basic Flash Parameter Table is the main and only mandatory table as* defined by the SFDP (JESD216) specification.* It provides us with the total size (memory density) of the data array and* the number of address bytes for Fast Read, Page Program and Sector Erase* commands.* For Fast READ commands, it also gives the number of mode clock cycles and* wait states (regrouped in the number of dummy clock cycles) for each* supported instruction op code.* For Page Program, the page size is now available since JESD216 rev A, however* the supported instruction op codes are still not provided.* For Sector Erase commands, this table stores the supported instruction op* codes and the associated sector sizes.* Finally, the Quad Enable Requirements (QER) are also available since JESD216* rev A. The QER bits encode the manufacturer dependent procedure to be* executed to set the Quad Enable (QE) bit in some internal register of the* Quad SPI memory. Indeed the QE bit, when it exists, must be set before* sending any Quad SPI command to the memory. Actually, setting the QE bit* tells the memory to reassign its WP# and HOLD#/RESET# pins to functions IO2* and IO3 hence enabling 4 (Quad) I/O lines.** Return: 0 on success, -errno otherwise.*/
static int spi_nor_parse_bfpt(struct spi_nor *nor,const struct sfdp_parameter_header *bfpt_header,struct spi_nor_flash_parameter *params)
{struct mtd_info *mtd = &nor->mtd;struct sfdp_bfpt bfpt;size_t len;int i, cmd, err;u32 addr;u16 half;/* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */if (bfpt_header->length < BFPT_DWORD_MAX_JESD216)return -EINVAL;/* Read the Basic Flash Parameter Table. */len = min_t(size_t, sizeof(bfpt),bfpt_header->length * sizeof(u32));addr = SFDP_PARAM_HEADER_PTP(bfpt_header);memset(&bfpt, 0, sizeof(bfpt));err = spi_nor_read_sfdp_dma_unsafe(nor,  addr, len, &bfpt);if (err < 0)return err;/* Fix endianness of the BFPT DWORDs. */for (i = 0; i < BFPT_DWORD_MAX; i++)bfpt.dwords[i] = le32_to_cpu(bfpt.dwords[i]);/* Number of address bytes. */switch (bfpt.dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) {case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY:nor->addr_width = 3;break;case BFPT_DWORD1_ADDRESS_BYTES_4_ONLY:nor->addr_width = 4;break;default:break;}/* Flash Memory Density (in bits). */params->size = bfpt.dwords[BFPT_DWORD(2)];if (params->size & BIT(31)) {params->size &= ~BIT(31);/** Prevent overflows on params->size. Anyway, a NOR of 2^64* bits is unlikely to exist so this error probably means* the BFPT we are reading is corrupted/wrong.*/if (params->size > 63)return -EINVAL;params->size = 1ULL << params->size;} else {params->size++;}params->size >>= 3; /* Convert to bytes. *//* Fast Read settings. */for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_reads); i++) {const struct sfdp_bfpt_read *rd = &sfdp_bfpt_reads[i];struct spi_nor_read_command *read;if (!(bfpt.dwords[rd->supported_dword] & rd->supported_bit)) {params->hwcaps.mask &= ~rd->hwcaps;continue;}params->hwcaps.mask |= rd->hwcaps;cmd = spi_nor_hwcaps_read2cmd(rd->hwcaps);read = &params->reads[cmd];half = bfpt.dwords[rd->settings_dword] >> rd->settings_shift;spi_nor_set_read_settings_from_bfpt(read, half, rd->proto);}/* Sector Erase settings. */for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_erases); i++) {const struct sfdp_bfpt_erase *er = &sfdp_bfpt_erases[i];u32 erasesize;u8 opcode;half = bfpt.dwords[er->dword] >> er->shift;erasesize = half & 0xff;/* erasesize == 0 means this Erase Type is not supported. */if (!erasesize)continue;erasesize = 1U << erasesize;opcode = (half >> 8) & 0xff;
#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORSif (erasesize == SZ_4K) {nor->erase_opcode = opcode;mtd->erasesize = erasesize;break;}
#endifif (!mtd->erasesize || mtd->erasesize < erasesize) {nor->erase_opcode = opcode;mtd->erasesize = erasesize;}}/* Stop here if not JESD216 rev A or later. */if (bfpt_header->length < BFPT_DWORD_MAX)return 0;/* Page size: this field specifies 'N' so the page size = 2^N bytes. */params->page_size = bfpt.dwords[BFPT_DWORD(11)];params->page_size &= BFPT_DWORD11_PAGE_SIZE_MASK;params->page_size >>= BFPT_DWORD11_PAGE_SIZE_SHIFT;params->page_size = 1U << params->page_size;/* Quad Enable Requirements. */switch (bfpt.dwords[BFPT_DWORD(15)] & BFPT_DWORD15_QER_MASK) {case BFPT_DWORD15_QER_NONE:params->quad_enable = NULL;break;case BFPT_DWORD15_QER_SR2_BIT1_BUGGY:case BFPT_DWORD15_QER_SR2_BIT1_NO_RD:params->quad_enable = spansion_no_read_cr_quad_enable;break;case BFPT_DWORD15_QER_SR1_BIT6:params->quad_enable = macronix_quad_enable;break;case BFPT_DWORD15_QER_SR2_BIT7:params->quad_enable = sr2_bit7_quad_enable;break;case BFPT_DWORD15_QER_SR2_BIT1:params->quad_enable = spansion_read_cr_quad_enable;break;default:return -EINVAL;}return 0;
}/*** spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters.* @nor:        pointer to a 'struct spi_nor'* @params:      pointer to the 'struct spi_nor_flash_parameter' to be*            filled** The Serial Flash Discoverable Parameters are described by the JEDEC JESD216* specification. This is a standard which tends to supported by almost all* (Q)SPI memory manufacturers. Those hard-coded tables allow us to learn at* runtime the main parameters needed to perform basic SPI flash operations such* as Fast Read, Page Program or Sector Erase commands.** Return: 0 on success, -errno otherwise.*/
static int spi_nor_parse_sfdp(struct spi_nor *nor,struct spi_nor_flash_parameter *params)
{const struct sfdp_parameter_header *param_header, *bfpt_header;struct sfdp_parameter_header *param_headers = NULL;struct sfdp_header header;struct device *dev = nor->dev;size_t psize;int i, err;/* Get the SFDP header. */err = spi_nor_read_sfdp_dma_unsafe(nor, 0, sizeof(header), &header);if (err < 0)return err;/* Check the SFDP header version. */if (le32_to_cpu(header.signature) != SFDP_SIGNATURE ||header.major != SFDP_JESD216_MAJOR ||header.minor < SFDP_JESD216_MINOR)return -EINVAL;/** Verify that the first and only mandatory parameter header is a* Basic Flash Parameter Table header as specified in JESD216.*/bfpt_header = &header.bfpt_header;if (SFDP_PARAM_HEADER_ID(bfpt_header) != SFDP_BFPT_ID ||bfpt_header->major != SFDP_JESD216_MAJOR)return -EINVAL;/** Allocate memory then read all parameter headers with a single* Read SFDP command. These parameter headers will actually be parsed* twice: a first time to get the latest revision of the basic flash* parameter table, then a second time to handle the supported optional* tables.* Hence we read the parameter headers once for all to reduce the* processing time. Also we use kmalloc() instead of devm_kmalloc()* because we don't need to keep these parameter headers: the allocated* memory is always released with kfree() before exiting this function.*/if (header.nph) {psize = header.nph * sizeof(*param_headers);param_headers = kmalloc(psize, GFP_KERNEL);if (!param_headers)return -ENOMEM;err = spi_nor_read_sfdp(nor, sizeof(header),psize, param_headers);if (err < 0) {dev_err(dev, "failed to read SFDP parameter headers\n");goto exit;}}/** Check other parameter headers to get the latest revision of* the basic flash parameter table.*/for (i = 0; i < header.nph; i++) {param_header = &param_headers[i];if (SFDP_PARAM_HEADER_ID(param_header) == SFDP_BFPT_ID &&param_header->major == SFDP_JESD216_MAJOR &&(param_header->minor > bfpt_header->minor ||(param_header->minor == bfpt_header->minor &&param_header->length > bfpt_header->length)))bfpt_header = param_header;}err = spi_nor_parse_bfpt(nor, bfpt_header, params);if (err)goto exit;/* Parse other parameter headers. */for (i = 0; i < header.nph; i++) {param_header = &param_headers[i];switch (SFDP_PARAM_HEADER_ID(param_header)) {case SFDP_SECTOR_MAP_ID:dev_info(dev, "non-uniform erase sector maps are not supported yet.\n");break;default:break;}if (err)goto exit;}exit:kfree(param_headers);return err;
}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 = 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);}/* 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;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)) &&!(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;
}static int spi_nor_hwcaps2cmd(u32 hwcaps, const int table[][2], size_t size)
{size_t i;for (i = 0; i < size; i++)if (table[i][0] == (int)hwcaps)return table[i][1];return -EINVAL;
}static int spi_nor_hwcaps_read2cmd(u32 hwcaps)
{static const int hwcaps_read2cmd[][2] = {{ SNOR_HWCAPS_READ,      SNOR_CMD_READ },{ SNOR_HWCAPS_READ_FAST,    SNOR_CMD_READ_FAST },{ SNOR_HWCAPS_READ_1_1_1_DTR,  SNOR_CMD_READ_1_1_1_DTR },{ SNOR_HWCAPS_READ_1_1_2, SNOR_CMD_READ_1_1_2 },{ SNOR_HWCAPS_READ_1_2_2, SNOR_CMD_READ_1_2_2 },{ SNOR_HWCAPS_READ_2_2_2, SNOR_CMD_READ_2_2_2 },{ SNOR_HWCAPS_READ_1_2_2_DTR, SNOR_CMD_READ_1_2_2_DTR },{ SNOR_HWCAPS_READ_1_1_4, SNOR_CMD_READ_1_1_4 },{ SNOR_HWCAPS_READ_1_4_4, SNOR_CMD_READ_1_4_4 },{ SNOR_HWCAPS_READ_4_4_4, SNOR_CMD_READ_4_4_4 },{ SNOR_HWCAPS_READ_1_4_4_DTR, SNOR_CMD_READ_1_4_4_DTR },{ SNOR_HWCAPS_READ_1_1_8, SNOR_CMD_READ_1_1_8 },{ SNOR_HWCAPS_READ_1_8_8, SNOR_CMD_READ_1_8_8 },{ SNOR_HWCAPS_READ_8_8_8, SNOR_CMD_READ_8_8_8 },{ SNOR_HWCAPS_READ_1_8_8_DTR, SNOR_CMD_READ_1_8_8_DTR },};return spi_nor_hwcaps2cmd(hwcaps, hwcaps_read2cmd,ARRAY_SIZE(hwcaps_read2cmd));
}static int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
{static const int hwcaps_pp2cmd[][2] = {{ SNOR_HWCAPS_PP,      SNOR_CMD_PP },{ SNOR_HWCAPS_PP_1_1_4,       SNOR_CMD_PP_1_1_4 },{ SNOR_HWCAPS_PP_1_4_4,     SNOR_CMD_PP_1_4_4 },{ SNOR_HWCAPS_PP_4_4_4,     SNOR_CMD_PP_4_4_4 },{ SNOR_HWCAPS_PP_1_1_8,     SNOR_CMD_PP_1_1_8 },{ SNOR_HWCAPS_PP_1_8_8,     SNOR_CMD_PP_1_8_8 },{ SNOR_HWCAPS_PP_8_8_8,     SNOR_CMD_PP_8_8_8 },};return spi_nor_hwcaps2cmd(hwcaps, hwcaps_pp2cmd,ARRAY_SIZE(hwcaps_pp2cmd));
}static int spi_nor_select_read(struct spi_nor *nor,const struct spi_nor_flash_parameter *params,u32 shared_hwcaps)
{int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1;const struct spi_nor_read_command *read;if (best_match < 0)return -EINVAL;cmd = spi_nor_hwcaps_read2cmd(BIT(best_match));if (cmd < 0)return -EINVAL;read = &params->reads[cmd];nor->read_opcode = read->opcode;nor->read_proto = read->proto;/** In the spi-nor framework, we don't need to make the difference* between mode clock cycles and wait state clock cycles.* Indeed, the value of the mode clock cycles is used by a QSPI* flash memory to know whether it should enter or leave its 0-4-4* (Continuous Read / XIP) mode.* eXecution In Place is out of the scope of the mtd sub-system.* Hence we choose to merge both mode and wait state clock cycles* into the so called dummy clock cycles.*/nor->read_dummy = read->num_mode_clocks + read->num_wait_states;return 0;
}static int spi_nor_select_pp(struct spi_nor *nor,const struct spi_nor_flash_parameter *params,u32 shared_hwcaps)
{int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1;const struct spi_nor_pp_command *pp;if (best_match < 0)return -EINVAL;cmd = spi_nor_hwcaps_pp2cmd(BIT(best_match));if (cmd < 0)return -EINVAL;pp = &params->page_programs[cmd];nor->program_opcode = pp->opcode;nor->write_proto = pp->proto;return 0;
}static int spi_nor_select_erase(struct spi_nor *nor,const struct flash_info *info)
{struct mtd_info *mtd = &nor->mtd;/* Do nothing if already configured from SFDP. */if (mtd->erasesize)return 0;#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS/* prefer "small sector" erase if possible */if (info->flags & SECT_4K) {nor->erase_opcode = SPINOR_OP_BE_4K;mtd->erasesize = 4096;} else if (info->flags & SECT_4K_PMC) {nor->erase_opcode = SPINOR_OP_BE_4K_PMC;mtd->erasesize = 4096;} else
#endif{nor->erase_opcode = SPINOR_OP_SE;mtd->erasesize = info->sector_size;}return 0;
}static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,const struct spi_nor_flash_parameter *params,const struct spi_nor_hwcaps *hwcaps)
{u32 ignored_mask, shared_mask;bool enable_quad_io;int err;/** Keep only the hardware capabilities supported by both the SPI* controller and the SPI flash memory.*/shared_mask = hwcaps->mask & params->hwcaps.mask;/* SPI n-n-n protocols are not supported yet. */ignored_mask = (SNOR_HWCAPS_READ_2_2_2 |SNOR_HWCAPS_READ_4_4_4 |SNOR_HWCAPS_READ_8_8_8 |SNOR_HWCAPS_PP_4_4_4 |SNOR_HWCAPS_PP_8_8_8);if (shared_mask & ignored_mask) {dev_dbg(nor->dev,"SPI n-n-n protocols are not supported yet.\n");shared_mask &= ~ignored_mask;}/* Select the (Fast) Read command. */err = spi_nor_select_read(nor, params, shared_mask);if (err) {dev_err(nor->dev,"can't select read settings supported by both the SPI controller and memory.\n");return err;}/* Select the Page Program command. */err = spi_nor_select_pp(nor, params, shared_mask);if (err) {dev_err(nor->dev,"can't select write settings supported by both the SPI controller and memory.\n");return err;}/* Select the Sector Erase command. */err = spi_nor_select_erase(nor, info);if (err) {dev_err(nor->dev,"can't select erase settings supported by both the SPI controller and memory.\n");return err;}/* Enable Quad I/O if needed. */enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 ||spi_nor_get_protocol_width(nor->write_proto) == 4);if (enable_quad_io && params->quad_enable) {err = params->quad_enable(nor);if (err) {dev_err(nor->dev, "quad mode not supported\n");return err;}}return 0;
}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;/** If caller has specified name of flash model that can normally be* detected using JEDEC, let's verify it.*/if (name && info->id_len) {const struct flash_info *jinfo;jinfo = spi_nor_read_id(nor);if (IS_ERR(jinfo)) {return PTR_ERR(jinfo);} else if (jinfo != info) {/** JEDEC knows better, so overwrite platform ID. We* can't trust partitions any longer, but we'll let* mtd apply them anyway, since some partitions may be* marked read-only, and we don't want to lose that* information, even if it's not 100% accurate.*/dev_warn(dev, "found %s, expected %s\n",jinfo->name, info->name);info = jinfo;}}mutex_init(&nor->lock);/** 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;/** 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;/** 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);if (ret)return ret;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);else//set_4byte(nor, info, 1);set_4byte_for_two_die(nor, info, 1);} else {nor->addr_width = 3;}if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) {dev_err(dev, "address width is too large: %u\n",nor->addr_width);return -EINVAL;}if (info->flags & SPI_S3AN) {ret = s3an_nor_scan(info, nor);if (ret)return ret;}dev_info(dev, "%s (%lld Kbytes)\n", info->name,(long long)mtd->size >> 10);if(info->id[0] == 0xc8 && info->id[1] == 0x40) {nor->program_opcode = 0x2;nor->erase_opcode = 0x52;//0x21;nor->read_opcode = 0x0b;//0x13;mtd->erasesize = 32*1024;}if (mtd->numeraseregions)for (i = 0; i < mtd->numeraseregions; i++)dev_dbg(dev,"mtd.eraseregions[%d] = { .offset = 0x%llx, "".erasesize = 0x%.8x (%uKiB), "".numblocks = %d }\n",i, (long long)mtd->eraseregions[i].offset,mtd->eraseregions[i].erasesize,mtd->eraseregions[i].erasesize / 1024,mtd->eraseregions[i].numblocks);return 0;
}
EXPORT_SYMBOL_GPL(spi_nor_scan);static const struct flash_info *spi_nor_match_id(const char *name)
{const struct flash_info *id = spi_nor_ids;while (id->name) {if (!strcmp(name, id->name))return id;id++;}return NULL;
}static inline int send_command(struct spi_nor *nor, u32 cmd)
{u8 code;code = (u8)(cmd >> 24);nor->cmd_buf[0] = (u8)(cmd >> 16);nor->cmd_buf[1] = (u8)(cmd >> 8);nor->cmd_buf[2] = (u8)(cmd);return nor->write_reg(nor, code, nor->cmd_buf, 3);
}static int flash_reset(struct spi_nor *nor)
{int ret;u32    reset_cmd1,reset_cmd2;/* Wait until finished previous write command. */if (spi_nor_wait_till_ready(nor))return 1;if (nor->jedec_id == SNOR_MFR_SPANSION) {reset_cmd1 = SPAN_OP_RESET1;reset_cmd2 = SPAN_OP_RESET2;} else {reset_cmd1 = SPINOR_OP_RESET1;reset_cmd2 = SPINOR_OP_RESET1;}ret = send_command(nor, reset_cmd1);if (ret == 0) {ret = send_command(nor, reset_cmd2);if (ret == 0) {// delay_ms(1);return 0;}}printk("NOR flash reset failed %d\n", ret);return ret;
}void spi_nor_shutdown(struct spi_nor *nor)
{
#ifndef SUPPORT_4BYTE_COMMANDprintk("NOR flash shutdown\n");/** Change cmd to 4byte format maybe cause bootrom read problems,* so, reset flash for fix it.*/flash_reset(nor);
//  if (nor->addr_width == 3 && (nor->mtd.size) > 0x1000000)
//      write_ear(nor, 0);
//  set_4byte(nor, &info, 0);
#endif
}
EXPORT_SYMBOL_GPL(spi_nor_shutdown);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Huang Shijie <shijie8@gmail.com>");
MODULE_AUTHOR("Mike Lavender");
MODULE_DESCRIPTION("framework for SPI NOR");

4、设备树修改

设备树中增加节点号,如下:

/dts-v1/;/ {#address-cells = <0x1>;#size-cells = <0x1>;model = "FMSH PSOC Board";compatible = "fmsh,fmsh-psoc";chosen {bootargs = "console=ttyPS0,115200 earlyprintk loglevel=8 root=/dev/ram rw";stdout-path = "serial0:115200n8";linux,initrd-start = <0x12000000>;linux,initrd-end = <0x12800000>;};aliases {ethernet0 = "/amba@0/ethernet@e0047000";ethernet1 = "/amba@0/ethernet@e0049000";serial0 = "/amba@0/serial@e0004000";serial1 = "/amba@0/serial@e0023000";spi0 = "/amba@0/qspi@e0000000";spi1 = "/amba@0/qspi@e0020000";spi2 = "/amba@0/spi@e0001000";spi3 = "/amba@0/spi@e0021000";mmc0 = "/amba@0/dwmmc@e0043000";mmc1 = "/amba@0/dwmmc@e0044000";i2c0 = "/amba@0/i2c@e0002000";i2c1 = "/amba@0/i2c@e0022000";};memory {device_type = "memory";reg = <0x100000 0x3ff00000>;};cpus {#address-cells = <0x1>;#size-cells = <0x0>;cpu@0 {compatible = "arm,cortex-a7";device_type = "cpu";reg = <0x0>;linux,phandle = <0x2>;phandle = <0x2>;};cpu@1 {compatible = "arm,cortex-a7";device_type = "cpu";reg = <0x1>;linux,phandle = <0x3>;phandle = <0x3>;};cpu@2 {compatible = "arm,cortex-a7";device_type = "cpu";reg = <0x2>;linux,phandle = <0x4>;phandle = <0x4>;};cpu@3 {compatible = "arm,cortex-a7";device_type = "cpu";reg = <0x3>;linux,phandle = <0x5>;phandle = <0x5>;};};pmu {compatible = "arm,cortex-a7-pmu";interrupt-parent = <0x1>;interrupts = <0x0 0x4 0x4 0x0 0x5 0x4 0x0 0x6 0x4 0x0 0x7 0x4>;interrupt-affinity = <0x2 0x3 0x4 0x5>;status = "disabled";};amba@0 {u-boot,dm-pre-reloc;compatible = "simple-bus";#address-cells = <0x1>;#size-cells = <0x1>;interrupt-parent = <0x1>;ranges;interrupt-controller@f8901000 {compatible = "arm,cortex-a7-gic";#interrupt-cells = <0x3>;#address-cells = <0x1>;interrupt-controller;reg = <0xf8901000 0x1000 0xf8902000 0x100>;linux,phandle = <0x1>;phandle = <0x1>;};slcr@e0026000 {u-boot,dm-pre-reloc;#address-cells = <0x1>;#size-cells = <0x1>;compatible = "fmsh,psoc-slcr", "syscon", "simple-mfd";reg = <0xe0026000 0x1000>;ranges;linux,phandle = <0x7>;phandle = <0x7>;clkc@100 {u-boot,dm-pre-reloc;compatible = "fmsh,psoc-clkc";reg = <0x100 0x100>;#clock-cells = <0x1>;ps-clk-frequency = <0x2faf080>;osc-clk-frequency = <0x7ffd>;fclk-enable = <0x0>;clock-output-names = "armpll", "ddrpll", "iopll", "cpu", "axi", "ahb", "apb", "axi_cpu", "ddrx1", "ddrx4", "axi_ddr", "apb_ddr", "gtimer", "gmac0_tx", "gmac1_tx", "fclk0", "fclk1", "fclk2", "fclk3", "gmac0_rx", "gmac1_rx", "axi_gmac0", "axi_gmac1", "ahb_gmac0", "ahb_gmac1", "ahb_smc", "ahb_nfc", "nfc", "qspi", "ahb_qspi", "apb_qspi", "sdio0", "sdio1", "ahb_sdio0", "ahb_sdio1", "uart0", "uart1", "apb_uart0", "apb_uart1", "spi0", "spi1", "apb_spi0", "apb_spi1", "apb_can0", "apb_can1", "apb_gpio", "apb_i2c0", "apb_i2c1", "ahb_usb0", "ahb_usb1", "usb0_phy", "usb1_phy", "ahb_dmac", "wdt", "apb_wdt", "ttc0_ref1", "ttc0_ref2", "ttc0_ref3", "ttc1_ref1", "ttc1_ref2", "ttc1_ref3", "apb_ttc0", "apb_ttc1", "ahb_pcap";linux,phandle = <0x6>;phandle = <0x6>;};};ddr_umc@e0029000 {compatible = "fmsh,psoc-ddr-umc", "syscon", "simple-mfd";#address-cells = <0x1>;#size-cells = <0x1>;reg = <0xe0029000 0x17000>;linux,phandle = <0x8>;phandle = <0x8>;};devcfg@e0040000 {compatible = "fmsh,fmql-devcfg-1.0";reg = <0xe0040000 0x1000>;interrupt-parent = <0x1>;interrupts = <0x0 0x8 0x4>;clocks = <0x6 0x3f>;clock-names = "ref_clk";syscon = <0x7>;ddrcon = <0x8>;status = "okay";};timer {compatible = "arm,armv7-timer";interrupts = <0x1 0xd 0xf04 0x1 0xe 0xf04 0x1 0xb 0xf04 0x1 0xa 0xf04>;arm,cpu-registers-not-fw-configured;status = "okay";};smc@0 {compatible = "fmsh,psoc-smc", "simple-bus";reg = <0xe0041000 0x1000>;clocks = <0x6 0x19>;#address-cells = <0x1>;#size-cells = <0x1>;bank-width = <0x1>;ranges;sram@e2000000 {compatible = "samsung,k6f1616u6a", "mtd-ram";reg = <0xe2000000 0x2000000>;#address-cells = <0x1>;#size-cells = <0x1>;bank-width = <0x1>;fmsh,smc-type = "sram";fmsh,smc-cs = <0x0>;status = "disabled";};sram@e4000000 {compatible = "samsung,k6f1616u6a", "mtd-ram";reg = <0xe4000000 0x2000000>;#address-cells = <0x1>;#size-cells = <0x1>;bank-width = <0x1>;fmsh,smc-type = "sram";fmsh,smc-cs = <0x1>;status = "disabled";};flash@e2000000 {compatible = "amd,am29lv128ml", "cfi-flash";reg = <0xe2000000 0x2000000>;bank-width = <0x1>;device-width = <0x1>;#address-cells = <0x1>;#size-cells = <0x1>;fmsh,smc-type = "flash";fmsh,smc-cs = <0x0>;status = "disabled";};flash@e4000000 {compatible = "amd,am29lv128ml", "cfi-flash";reg = <0xe4000000 0x2000000>;bank-width = <0x1>;device-width = <0x1>;#address-cells = <0x1>;#size-cells = <0x1>;fmsh,smc-type = "flash";fmsh,smc-cs = <0x1>;status = "disabled";};};serial@e0004000 {compatible = "snps,dw-apb-uart";clocks = <0x6 0x23 0x6 0x25>;clock-names = "baudclk", "apb_pclk";reg = <0xe0004000 0x1000>;interrupts = <0x0 0x17 0x4>;reg-shift = <0x2>;reg-io-width = <0x4>;u-boot,dm-pre-reloc;status = "okay";};serial@e0023000 {compatible = "snps,dw-apb-uart";clocks = <0x6 0x24 0x6 0x26>;clock-names = "baudclk", "apb_pclk";reg = <0xe0023000 0x1000>;interrupts = <0x0 0x2c 0x4>;reg-shift = <0x2>;reg-io-width = <0x4>;u-boot,dm-pre-reloc;status = "disabled";};ethernet@e0047000 {compatible = "fmsh,fmql-gmac", "snps,dwmac-3.70a", "snps,dwmac";reg = <0xe0047000 0x2000>;reg-names = "stmmaceth";interrupts = <0x0 0x13 0x0>;interrupt-names = "macirq";mac-address = [00 01 02 03 04 05];clocks = <0x6 0x17 0x6 0x15 0x6 0xd 0x6 0x13>;clock-names = "stmmaceth", "pclk", "fmql-gmac-tx", "fmql-gmac-rx";phy-mode = "rgmii-id";fmsh,gmac-number = <0x0>;snps,multicast-filter-bins = <0x100>;snps,perfect-filter-entries = <0x80>;status = "okay";snps,reset-gpio = <0x9 0xa 0x1>;snps,reset-active-low;snps,reset-delays-us = <0x0 0x2710 0x186a0>;phy-handle = <0xc>;mdio@0 {compatible = "snps,dwmac-mdio";#address-cells = <0x1>;#size-cells = <0x0>;eth-phy@7 {reg = <0x7>;phandle = <0xc>;};eth-phy@0 {reg = <0x0>;phandle = <0xd>;};};};ethernet@e0049000 {compatible = "fmsh,fmql-gmac", "snps,dwmac-3.70a", "snps,dwmac";reg = <0xe0049000 0x2000>;reg-names = "stmmaceth";interrupts = <0x0 0x28 0x0>;interrupt-names = "macirq";mac-address = [00 01 02 03 04 06];clocks = <0x6 0x18 0x6 0x16 0x6 0xe 0x6 0x14>;clock-names = "stmmaceth", "pclk", "fmql-gmac-tx", "fmql-gmac-rx";phy-mode = "rgmii";fmsh,gmac-number = <0x1>;snps,multicast-filter-bins = <0x100>;snps,perfect-filter-entries = <0x80>;status = "okay";snps,reset-gpio = <0x9 0xb 0x1>;snps,reset-active-low;snps,reset-delays-us = <0x0 0x2710 0x186a0>;phy-handle = <0xd>;};gpio@e0003000 {compatible = "snps,dw-apb-gpio";reg = <0xe0003000 0x100>;#address-cells = <0x1>;#size-cells = <0x0>;clocks = <0x6 0x2d>;status = "okay";gpio-controller@0 {compatible = "snps,dw-apb-gpio-port";bank-name = "porta";gpio-controller;#gpio-cells = <0x2>;snps,nr-gpios = <0x20>;reg = <0x0>;interrupt-controller;#interrupt-cells = <0x2>;interrupts = <0x0 0x11 0x4>;};};gpio@e0003100 {compatible = "snps,dw-apb-gpio";reg = <0xe0003100 0x100>;#address-cells = <0x1>;#size-cells = <0x0>;clocks = <0x6 0x2d>;status = "okay";gpio-controller@0 {compatible = "snps,dw-apb-gpio-port";bank-name = "portb";gpio-controller;#gpio-cells = <0x2>;snps,nr-gpios = <0x16>;reg = <0x0>;interrupt-controller;#interrupt-cells = <0x2>;interrupts = <0x0 0x24 0x4>;linux,phandle = <0x9>;phandle = <0x9>;};};gpio@e0003200 {compatible = "snps,dw-apb-gpio";reg = <0xe0003200 0x100>;#address-cells = <0x1>;#size-cells = <0x0>;clocks = <0x6 0x2d>;status = "okay";gpio-controller@0 {compatible = "snps,dw-apb-gpio-port";bank-name = "portc";gpio-controller;#gpio-cells = <0x2>;snps,nr-gpios = <0x20>;reg = <0x0>;interrupt-controller;#interrupt-cells = <0x2>;interrupts = <0x0 0x25 0x4>;};};gpio@e0003400 {compatible = "snps,dw-apb-gpio";reg = <0xe0003400 0x100>;#address-cells = <0x1>;#size-cells = <0x0>;clocks = <0x6 0x2d>;status = "okay";gpio-controller@0 {compatible = "snps,dw-apb-gpio-port";bank-name = "portd";gpio-controller;#gpio-cells = <0x2>;snps,nr-gpios = <0x20>;reg = <0x0>;interrupt-controller;#interrupt-cells = <0x2>;interrupts = <0x0 0x26 0x4>;};};qspi@e0000000 {compatible = "fmsh,qspi-nor", "cadence,qspi";clocks = <0x6 0x1c 0x6 0x1d 0x6 0x1e>;clock-names = "clk_ref", "hclk", "pclk";#address-cells = <0x1>;#size-cells = <0x0>;reg = <0xe0000000 0x1000 0xe8000000 0x1000000>;interrupts = <0x0 0xe 0x4>;cdns,fifo-depth = <0x100>;cdns,fifo-width = <0x4>;cdns,trigger-address = <0xe8000000>;status = "okay";s25fl256s@0 {compatible = "spi-flash", "spansion,s25fl256s1", "jedec,spi-nor";reg = <0x0>;spi-max-frequency = <0x2faf080>;m25p,fast-read;page-size = <0x100>;block-size = <0x10>;cdns,read-delay = <0x2>;cdns,tshsl-ns = <0x0>;cdns,tsd2d-ns = <0x0>;cdns,tchsh-ns = <0x0>;cdns,tslch-ns = <0x0>;};};qspi@e0020000 {compatible = "fmsh,qspi-nor", "cadence,qspi";clocks = <0x6 0x1c 0x6 0x1d 0x6 0x1e>;clock-names = "clk_ref", "hclk", "pclk";#address-cells = <0x1>;#size-cells = <0x0>;reg = <0xe0020000 0x1000 0xe9000000 0x1000000>;interrupts = <0x0 0xf 0x4>;cdns,fifo-depth = <0x100>;cdns,fifo-width = <0x4>;cdns,trigger-address = <0xe9000000>;status = "okay";s25fl256s@1 {compatible = "spi-flash", "spansion,s25fl256s1", "jedec,spi-nor";reg = <0x0>;spi-max-frequency = <0x2faf080>;m25p,fast-read;page-size = <0x100>;block-size = <0x10>;cdns,read-delay = <0x2>;cdns,tshsl-ns = <0x0>;cdns,tsd2d-ns = <0x0>;cdns,tchsh-ns = <0x0>;cdns,tslch-ns = <0x0>;};};spi@e0001000 {compatible = "fmsh,dw-apb-ssi", "snps,dw-apb-ssi";#address-cells = <0x1>;#size-cells = <0x0>;reg = <0xe0001000 0x1000>;interrupts = <0x0 0x16 0x4>;num-cs = <0x3>;clocks = <0x6 0x27 0x6 0x29>;clock-names = "clk_ref", "pclk";reg-io-width = <0x4>;spi-max-frequency = <0xf4240>;status = "disabled";};spi@e0021000 {compatible = "fmsh,dw-apb-ssi", "snps,dw-apb-ssi";#address-cells = <0x1>;#size-cells = <0x0>;reg = <0xe0021000 0x1000>;interrupts = <0x0 0x2b 0x4>;num-cs = <0x3>;clocks = <0x6 0x28 0x6 0x2a>;clock-names = "clk_ref", "pclk";reg-io-width = <0x4>;spi-max-frequency = <0xf4240>;status = "disabled";};dma@e004b000 {compatible = "snps,dma-spear1340";reg = <0xe004b000 0x1000>;interrupts = <0x0 0xd 0x4>;dma-channels = <0x8>;dma-requests = <0x10>;dma-masters = <0x1>;#dma-cells = <0x3>;chan_allocation_order = <0x1>;chan_priority = <0x1>;block_size = <0xfff>;data-width = <0x4>;clocks = <0x6 0x34>;clock-names = "hclk";status = "okay";};usbphy@0 {compatible = "usb-nop-xceiv";#phy-cells = <0x0>;clocks = <0x6 0x32>;clock-names = "main_clk";status = "disabled";linux,phandle = <0xa>;phandle = <0xa>;};usb@e0045000 {compatible = "fmsh,psoc-dwc2-usb", "snps,dwc2";reg = <0xe0045000 0x1000>;interrupts = <0x0 0x12 0x4>;clocks = <0x6 0x30>;clock-names = "otg";#address-cells = <0x1>;#size-cells = <0x0>;phys = <0xa>;phy-names = "usb2-phy";dr_mode = "otg";phy-width = <0x8>;status = "disabled";};dwmmc@e0043000 {compatible = "fmsh,psoc-dw-mshc";reg = <0xe0043000 0x1000>;interrupts = <0x0 0x14 0x4>;clocks = <0x6 0x21 0x6 0x1f>;clock-names = "biu", "ciu";#address-cells = <0x1>;#size-cells = <0x0>;data-addr = <0x100>;fifo-depth = <0x20>;bus-width = <0x4>;status = "disabled";cap-sd-highspeed;cap-mmc-highspeed;};dwmmc@e0044000 {compatible = "fmsh,psoc-dw-mshc";reg = <0xe0044000 0x1000>;interrupts = <0x0 0x29 0x4>;clocks = <0x6 0x22 0x6 0x20>;clock-names = "biu", "ciu";#address-cells = <0x1>;#size-cells = <0x0>;data-addr = <0x100>;fifo-depth = <0x20>;bus-width = <0x4>;status = "disabled";cap-sd-highspeed;cap-mmc-highspeed;broken-cd;};nfc@e0042000 {compatible = "fmsh,psoc-nfc";reg = <0xe0042000 0x1000>;clocks = <0x6 0x1a 0x6 0x1b>;clock-names = "pclk", "nfc_ref";#address-cells = <0x1>;#size-cells = <0x1>;interrupts = <0x0 0x10 0x4>;nand-bus-width = <0x8>;nand-ecc-mode = "hw";nand-ecc-strength = <0x8>;nand-ecc-step-size = <0x200>;nand-use-mode = "dma";status = "disabled";};i2c@e0002000 {compatible = "snps,designware-i2c";reg = <0xe0002000 0x1000>;#address-cells = <0x1>;#size-cells = <0x0>;clocks = <0x6 0x2e>;interrupts = <0x0 0x15 0x4>;i2c-max-frequency = <0xf4240>;status = "okay";};i2c@e0022000 {compatible = "snps,designware-i2c";reg = <0xe0022000 0x1000>;clocks = <0x6 0x2f>;interrupts = <0x0 0x2a 0x4>;#address-cells = <0x1>;#size-cells = <0x0>;i2c-max-frequency = <0xf4240>;status = "okay";};timer@e0007000 {compatible = "snps,dw-apb-timer";interrupts = <0x0 0xa 0x4>;reg = <0xe0007000 0x1000>;clocks = <0x6 0x37 0x6 0x3d>;clock-names = "timer", "pclk";status = "disabled";};timer@e0024000 {compatible = "snps,dw-apb-timer";interrupts = <0x0 0x21 0x4>;reg = <0xe0024000 0x1000>;clocks = <0x6 0x3a 0x6 0x3e>;clock-names = "timer", "pclk";status = "disabled";};watchdog@e0025000 {compatible = "fmql,dw-wdt";reg = <0xe0025000 0x1000>;interrupts = <0x0 0x9 0x4>;clocks = <0x6 0x35 0x6 0x36>;clock-names = "wdt", "pclk";status = "disabled";};can@e0005000 {compatible = "fmql,sja1000";reg = <0xe0005000 0x1000>;clocks = <0x6 0x2b>;clock-names = "pclk";interrupts = <0x0 0x18 0x4>;reg-io-width = <0x4>;nxp,tx-output-mode = <0x1>;nxp,tx-output-config = <0x2>;nxp,no-comparator-bypass;status = "disabled";};can@e0006000 {compatible = "fmql,sja1000";reg = <0xe0006000 0x1000>;clocks = <0x6 0x2c>;clock-names = "pclk";interrupts = <0x0 0x2d 0x4>;reg-io-width = <0x4>;nxp,tx-output-mode = <0x1>;nxp,tx-output-config = <0x2>;nxp,no-comparator-bypass;status = "disabled";};};amba_pl {#address-cells = <0x1>;#size-cells = <0x1>;compatible = "simple-bus";ranges;gpio@41230000 {#gpio-cells = <0x3>;clock-names = "s_axi_aclk";clocks = <0x6 0xf>;compatible = "xlnx,axi-gpio-2.0", "xlnx,xps-gpio-1.00.a";gpio-controller;reg = <0x41230000 0x10000>;xlnx,all-inputs = <0x0>;xlnx,all-inputs-2 = <0x0>;xlnx,all-outputs = <0x1>;xlnx,all-outputs-2 = <0x0>;xlnx,dout-default = <0x3>;xlnx,dout-default-2 = <0x0>;xlnx,gpio-width = <0x2>;xlnx,gpio2-width = <0x20>;xlnx,interrupt-present = <0x0>;xlnx,is-dual = <0x0>;xlnx,tri-default = <0xffffffff>;xlnx,tri-default-2 = <0xffffffff>;};gpio@41250000 {#gpio-cells = <0x3>;clock-names = "s_axi_aclk";clocks = <0x6 0xf>;compatible = "xlnx,axi-gpio-2.0", "xlnx,xps-gpio-1.00.a";gpio-controller;reg = <0x41250000 0x10000>;xlnx,all-inputs = <0x0>;xlnx,all-inputs-2 = <0x0>;xlnx,all-outputs = <0x0>;xlnx,all-outputs-2 = <0x0>;xlnx,dout-default = <0x3>;xlnx,dout-default-2 = <0x0>;xlnx,gpio-width = <0x2>;xlnx,gpio2-width = <0x20>;xlnx,interrupt-present = <0x0>;xlnx,is-dual = <0x0>;xlnx,tri-default = <0xffffffff>;xlnx,tri-default-2 = <0xffffffff>;};gpio@41240000 {#gpio-cells = <0x3>;clock-names = "s_axi_aclk";clocks = <0x6 0xf>;compatible = "xlnx,axi-gpio-2.0", "xlnx,xps-gpio-1.00.a";gpio-controller;reg = <0x41240000 0x10000>;xlnx,all-inputs = <0x1>;xlnx,all-inputs-2 = <0x0>;xlnx,all-outputs = <0x0>;xlnx,all-outputs-2 = <0x0>;xlnx,dout-default = <0x0>;xlnx,dout-default-2 = <0x0>;xlnx,gpio-width = <0x20>;xlnx,gpio2-width = <0x20>;xlnx,interrupt-present = <0x0>;xlnx,is-dual = <0x0>;xlnx,tri-default = <0xffffffff>;xlnx,tri-default-2 = <0xffffffff>;};gpio@41220000 {#gpio-cells = <0x3>;clock-names = "s_axi_aclk";clocks = <0x6 0xf>;compatible = "xlnx,axi-gpio-2.0", "xlnx,xps-gpio-1.00.a";gpio-controller;reg = <0x41220000 0x10000>;xlnx,all-inputs = <0x1>;xlnx,all-inputs-2 = <0x0>;xlnx,all-outputs = <0x0>;xlnx,all-outputs-2 = <0x0>;xlnx,dout-default = <0x0>;xlnx,dout-default-2 = <0x0>;xlnx,gpio-width = <0x2>;xlnx,gpio2-width = <0x20>;xlnx,interrupt-present = <0x0>;xlnx,is-dual = <0x0>;xlnx,tri-default = <0xffffffff>;xlnx,tri-default-2 = <0xffffffff>;};gpio@41210000 {#gpio-cells = <0x3>;clock-names = "s_axi_aclk";clocks = <0x6 0xf>;compatible = "xlnx,axi-gpio-2.0", "xlnx,xps-gpio-1.00.a";gpio-controller;reg = <0x41210000 0x10000>;xlnx,all-inputs = <0x0>;xlnx,all-inputs-2 = <0x0>;xlnx,all-outputs = <0x1>;xlnx,all-outputs-2 = <0x0>;xlnx,dout-default = <0x0>;xlnx,dout-default-2 = <0x0>;xlnx,gpio-width = <0x20>;xlnx,gpio2-width = <0x20>;xlnx,interrupt-present = <0x0>;xlnx,is-dual = <0x0>;xlnx,tri-default = <0xffffffff>;xlnx,tri-default-2 = <0xffffffff>;};gpio@41260000 {#gpio-cells = <0x3>;clock-names = "s_axi_aclk";clocks = <0x6 0xf>;compatible = "xlnx,axi-gpio-2.0", "xlnx,xps-gpio-1.00.a";gpio-controller;reg = <0x41260000 0x10000>;xlnx,all-inputs = <0x1>;xlnx,all-inputs-2 = <0x0>;xlnx,all-outputs = <0x0>;xlnx,all-outputs-2 = <0x0>;xlnx,dout-default = <0x0>;xlnx,dout-default-2 = <0x0>;xlnx,gpio-width = <0x10>;xlnx,gpio2-width = <0x20>;xlnx,interrupt-present = <0x0>;xlnx,is-dual = <0x0>;xlnx,tri-default = <0xffffffff>;xlnx,tri-default-2 = <0xffffffff>;};i2c@41610000 {#address-cells = <0x1>;#size-cells = <0x0>;clock-names = "s_axi_aclk";clocks = <0x6 0xf>;compatible = "xlnx,axi-iic-2.0", "xlnx,xps-iic-2.00.a";interrupt-names = "iic2intc_irpt";interrupt-parent = <0x1>;interrupts = <0x0 0x1a 0x4>;reg = <0x41610000 0x10000>;};axi_quad_spi@41e00000 {bits-per-word = <0x8>;clock-names = "ext_spi_clk", "s_axi_aclk";clocks = <0x6 0xf 0x6 0xf>;compatible = "xlnx,axi-quad-spi-3.2", "xlnx,xps-spi-2.00.a";fifo-size = <0x100>;interrupt-names = "ip2intc_irpt";interrupt-parent = <0x1>;interrupts = <0x0 0x1b 0x1>;num-cs = <0x1>;reg = <0x41e00000 0x10000>;xlnx,num-ss-bits = <0x1>;xlnx,spi-mode = <0x0>;#address-cells = <0x1>;#size-cells = <0x0>;flash@0 {compatible = "gd25s512,spi-nor";reg = <0x0>;spi-max-frequency = <0x9ef21b0>;spi-tx-bus-width = <0x1>;spi-rx-bus-width = <0x1>;#address-cells = <0x1>;#size-cells = <0x1>;partition@0 {label = "spi-flash";reg = <0x0 0x4000000>;};};};axi_rs485@43c60000 {clock-names = "s00_axi_aclk";clocks = <0x6 0xf>;compatible = "xlnx,axi-rs485-2.0";reg = <0x43c60000 0x10000>;xlnx,s00-axi-addr-width = <0x8>;xlnx,s00-axi-data-width = <0x20>;};axi_rs485@43c70000 {clock-names = "s00_axi_aclk";clocks = <0x6 0xf>;compatible = "xlnx,axi-rs485-2.0";reg = <0x43c70000 0x10000>;xlnx,s00-axi-addr-width = <0x8>;xlnx,s00-axi-data-width = <0x20>;};serial@42c00000 {clock-names = "s_axi_aclk";clocks = <0x6 0xf>;compatible = "xlnx,axi-uartlite-2.0", "xlnx,xps-uartlite-1.00.a";current-speed = <0x2580>;device_type = "serial";interrupt-names = "interrupt";interrupt-parent = <0x1>;interrupts = <0x0 0x1c 0x1>;port-number = <0x0>;reg = <0x42c00000 0x10000>;xlnx,baudrate = <0x2580>;xlnx,data-bits = <0x8>;xlnx,odd-parity = <0x0>;xlnx,s-axi-aclk-freq-hz-d = "100.0";xlnx,use-parity = <0x0>;};gpio@41200000 {#gpio-cells = <0x3>;clock-names = "s_axi_aclk";clocks = <0x6 0xf>;compatible = "xlnx,axi-gpio-2.0", "xlnx,xps-gpio-1.00.a";gpio-controller;reg = <0x41200000 0x10000>;xlnx,all-inputs = <0x0>;xlnx,all-inputs-2 = <0x0>;xlnx,all-outputs = <0x1>;xlnx,all-outputs-2 = <0x0>;xlnx,dout-default = <0x0>;xlnx,dout-default-2 = <0x0>;xlnx,gpio-width = <0x1>;xlnx,gpio2-width = <0x20>;xlnx,interrupt-present = <0x0>;xlnx,is-dual = <0x0>;xlnx,tri-default = <0xffffffff>;xlnx,tri-default-2 = <0xffffffff>;};srio_axi_config@43c00000 {clock-names = "s00_axi_aclk";clocks = <0x6 0xf>;compatible = "xlnx,srio-axi-config-1.0";reg = <0x43c00000 0x10000>;xlnx,s00-axi-addr-width = <0x7>;xlnx,s00-axi-data-width = <0x20>;};srio_axi_config@43c10000 {clock-names = "s00_axi_aclk";clocks = <0x6 0xf>;compatible = "xlnx,srio-axi-config-1.0";reg = <0x43c10000 0x10000>;xlnx,s00-axi-addr-width = <0x7>;xlnx,s00-axi-data-width = <0x20>;};xadc_wiz@43c40000 {clock-names = "s_axi_aclk";clocks = <0x6 0xf>;compatible = "xlnx,xadc-wiz-3.3", "xlnx,axi-xadc-1.00.a";reg = <0x43c40000 0x10000>;xlnx,alarm-limit-r0 = <0xb5ed>;xlnx,alarm-limit-r1 = <0x57e4>;xlnx,alarm-limit-r10 = <0x5555>;xlnx,alarm-limit-r11 = <0x5111>;xlnx,alarm-limit-r12 = <0x9999>;xlnx,alarm-limit-r13 = <0x91eb>;xlnx,alarm-limit-r14 = <0x6aaa>;xlnx,alarm-limit-r15 = <0x6666>;xlnx,alarm-limit-r2 = <0xa147>;xlnx,alarm-limit-r3 = <0xca33>;xlnx,alarm-limit-r4 = <0xa93a>;xlnx,alarm-limit-r5 = <0x52c6>;xlnx,alarm-limit-r6 = <0x9555>;xlnx,alarm-limit-r7 = <0xae4e>;xlnx,alarm-limit-r8 = <0x5999>;xlnx,alarm-limit-r9 = <0x5111>;xlnx,configuration-r0 = <0x0>;xlnx,configuration-r1 = <0x21af>;xlnx,configuration-r2 = <0x400>;xlnx,dclk-frequency = <0x64>;xlnx,external-mux = "none";xlnx,external-mux-channel = "VP_VN";xlnx,external-muxaddr-enable = <0x0>;xlnx,fifo-depth = <0x7>;xlnx,has-axi = <0x1>;xlnx,has-axi4stream = <0x0>;xlnx,has-busy = <0x1>;xlnx,has-channel = <0x1>;xlnx,has-convst = <0x0>;xlnx,has-convstclk = <0x0>;xlnx,has-dclk = <0x1>;xlnx,has-drp = <0x0>;xlnx,has-eoc = <0x1>;xlnx,has-eos = <0x1>;xlnx,has-external-mux = <0x0>;xlnx,has-jtagbusy = <0x0>;xlnx,has-jtaglocked = <0x0>;xlnx,has-jtagmodified = <0x0>;xlnx,has-ot-alarm = <0x0>;xlnx,has-reset = <0x0>;xlnx,has-temp-bus = <0x0>;xlnx,has-user-temp-alarm = <0x0>;xlnx,has-vbram-alarm = <0x0>;xlnx,has-vccaux-alarm = <0x0>;xlnx,has-vccddro-alarm = <0x0>;xlnx,has-vccint-alarm = <0x0>;xlnx,has-vccpaux-alarm = <0x0>;xlnx,has-vccpint-alarm = <0x0>;xlnx,has-vn = <0x1>;xlnx,has-vp = <0x1>;xlnx,include-intr = <0x1>;xlnx,sampling-rate = "961538.4615384615";xlnx,sequence-r0 = <0x4fe0>;xlnx,sequence-r1 = <0xf1f>;xlnx,sequence-r2 = <0x4fe0>;xlnx,sequence-r3 = <0xf1f>;xlnx,sequence-r4 = <0x0>;xlnx,sequence-r5 = <0x0>;xlnx,sequence-r6 = <0x800>;xlnx,sequence-r7 = <0x0>;xlnx,sim-file-name = "design";xlnx,sim-file-rel-path = "./";xlnx,sim-file-sel = "Default";xlnx,vaux0 = <0x1>;xlnx,vaux1 = <0x1>;xlnx,vaux10 = <0x1>;xlnx,vaux11 = <0x1>;xlnx,vaux12 = <0x0>;xlnx,vaux13 = <0x0>;xlnx,vaux14 = <0x0>;xlnx,vaux15 = <0x0>;xlnx,vaux2 = <0x1>;xlnx,vaux3 = <0x1>;xlnx,vaux4 = <0x1>;xlnx,vaux5 = <0x0>;xlnx,vaux6 = <0x0>;xlnx,vaux7 = <0x0>;xlnx,vaux8 = <0x1>;xlnx,vaux9 = <0x1>;};};
};

5、测试

系统启动后能看到识别到了flash,采用ubi文件系统进行操作即可。

Zynq-Linux移植学习笔记之57-国产ZYNQ PL挂载兆易创新GD25S512 flash相关推荐

  1. zc706开发板的linux移植,Zynq—Linux移植学习笔记(十)

    在zynq开发板zc706上,网络通路由下面三个设备组成: 其中zynq负责对phy进行配置,当zynq上的网络控制器以及phy完成正确配置时,能够看到RJ45上面的黄灯亮,此时表明链路已经通了.如果 ...

  2. zynq linux找不到flash,Zynq—Linux移植学习笔记(十八):Zynq下NOR_FLASH挂载文件系统...

    1. 背景介绍 板子上的zynq通过emc外接一块nor flash,地址分配如下: Nor flash的起始地址为0x80000000.当zynq上运行Linux后可以通过对该地址起始的区域进行擦除 ...

  3. Zynq-Linux移植学习笔记之49-国产ZYNQ适配国产裕太PHY网络调试

    1.背景介绍 由于元器件国产化大环境的需要,目前项目中采用国产ZYNQ替换xilinx ZYNQ,连接一片国产裕太8521 PHY替换Marvell 88E1111 PHY,通过RJ45转电口连接外部 ...

  4. Zynq-Linux移植学习笔记之52-国产ZYNQ standalone PL-PS中断调试

    1.背景介绍 目前采用国产ZYNQ替代进口xilinx ZYNQ,在调试PL-PS的中断时,和进口Xilinx ZYNQ有区别,记录如下. 2.vivado工程 直接引入一个GPIO输入ZYNQ PL ...

  5. linux挂载硬盘_CentOS「linux」学习笔记12:磁盘管理、分区挂载卸载操作

    linux基础操作:主要介绍了磁盘管理.分区挂载卸载操作. 特别说明linux中磁盘表现形式: IDE硬盘在linux中表示方式为"hdx".SCSI硬盘在linux中表示方式为& ...

  6. centos 卸载_CentOS「linux」学习笔记12:磁盘管理、分区挂载卸载操作

    linux基础操作:主要介绍了磁盘管理.分区挂载卸载操作. 特别说明linux中磁盘表现形式: IDE硬盘在linux中表示方式为"hdx".SCSI硬盘在linux中表示方式为& ...

  7. 【国产MCU学步随笔------兆易创新GD32f103CBT6,及vscode编辑环境配置】

    文章目录 vscode编辑环境配置 官方库例程迷惑解答 时钟在哪里初始化 时钟在哪里配置 NVIC在哪里初始化 NVIC中断分组是什么 printf怎么重映射 vscode编辑环境配置 下载官方库:链 ...

  8. 5、赛灵思-Zynq UltraScale+ MPSoC学习笔记:Petalinux 的设计流程及定制Linux系统

    5.赛灵思-Zynq UltraScale+ MPSoC学习笔记:Petalinux 的设计流程及定制Linux系统 声明:本文是学习赛灵思 Zynq UltraScale+ MPSoC 5EV过程中 ...

  9. Zynq-7000系列之linux开发学习笔记:编译Linux内核和制作设备树(六)

    开发板:Zynq7030数据采集板 PC平台:Ubuntu-18.04 + MobaXterm 开发环境:Xilinx Vivado + SDK -18.3 交叉编译工具:arm-linux-gnue ...

  10. linux系统管理学习笔记之三----软件的安装

    linux系统管理学习笔记之三----软件的安装 2009-12-29 19:10:02 标签:linux 系统管理 [推送到技术圈] 版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章 ...

最新文章

  1. vue router name命名规范_超完整的Vue入门指导
  2. Django模拟新浪微博的@功能
  3. 自学python找到工作-学完python能找到工作么
  4. 人人可懂的机器学习入门要点和阅读路径:学什么?怎样学?
  5. hdu 1520 Anniversary party(第一道树形dp)
  6. 考验你的吉他入门了吗?
  7. 使用python fabric搭建RHEL 7.2大数据基础环境以及部分优化
  8. 合并账号_亚马逊账号最新死法:合并listing和折扣促销
  9. 因 URL 意外地以“/HelloWorld”结束,请求格式无法识别。
  10. BIM族库下载——Revit人物族
  11. 完整的连接器设计手册_连接器材料使用大全
  12. 中国科技统计年鉴Excel版本(1991-2021年)
  13. PHP中smart原则,制定目标时的SMART原则不包括什么
  14. C#员工考勤管理系统源码 考勤工资管理系统源码
  15. win8.1中文版开启远程桌面
  16. 无人驾驶实践进阶——定位
  17. 微信网页开发学习笔记
  18. 电子科大自考c语言试题,2016四川省大学一流学科排行榜,电子科大跃居首位
  19. 51单片机-4G模块
  20. ORACLE安装方法

热门文章

  1. Unity3D陀螺仪的使用
  2. nested exception is java.lang.NumberFormatException: For input string: “swagger-ui.html“]
  3. 图网络深度解析:为什么说图网络是 AI 的未来?
  4. 服务器c盘临时文件在哪里,excel在c盘哪个文件夹|excel临时文件位置
  5. 铁血战士 (美国2018年沙恩·布莱克执导电影)
  6. 保乐力加在四川峨眉山兴建中国首家麦芽威士忌酒厂
  7. 计算机it是什么,IT是什么意思
  8. 视频 | 计算万物的理论
  9. guass-jordan消元法求逆的原理
  10. ps中怎么导出tif_TIF图片太大难以上传?