打印

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

[复制链接]
528|22
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
g36xcv|  楼主 | 2023-10-28 01:25 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
flash介绍

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



使用特权

评论回复
沙发
g36xcv|  楼主 | 2023-10-28 01:26 | 只看该作者
代码示例如下:

使用特权

评论回复
板凳
g36xcv|  楼主 | 2023-10-28 01:26 | 只看该作者
Flash命令交互如下:

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

使用特权

评论回复
地板
g36xcv|  楼主 | 2023-10-28 01:26 | 只看该作者
然后是四字节模式下的读写擦除命令


使用特权

评论回复
5
g36xcv|  楼主 | 2023-10-28 01:26 | 只看该作者
最后是ID,用来判断flash是什么型号

使用特权

评论回复
6
g36xcv|  楼主 | 2023-10-28 01:27 | 只看该作者
这个与代码里面是对应上的,使用0x9F命令去读取ID

使用特权

评论回复
7
g36xcv|  楼主 | 2023-10-28 01:27 | 只看该作者

使用特权

评论回复
8
g36xcv|  楼主 | 2023-10-28 01:27 | 只看该作者
内核修改
需要修改的文件如下:

M25p80.c

使用特权

评论回复
9
g36xcv|  楼主 | 2023-10-28 01:27 | 只看该作者
增加gd25s512配置

使用特权

评论回复
10
g36xcv|  楼主 | 2023-10-28 01:28 | 只看该作者
注意:150行左右的dummy值要设置为1,具体看代码

使用特权

评论回复
11
g36xcv|  楼主 | 2023-10-28 01:28 | 只看该作者
/*
* 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 */
        else
                flash_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");

使用特权

评论回复
12
g36xcv|  楼主 | 2023-10-28 01:28 | 只看该作者
Spi-nor.h中增加切换片选命令0xC2

使用特权

评论回复
13
g36xcv|  楼主 | 2023-10-28 01:28 | 只看该作者

使用特权

评论回复
14
g36xcv|  楼主 | 2023-10-28 01:28 | 只看该作者
/*
* 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

使用特权

评论回复
15
g36xcv|  楼主 | 2023-10-28 01:29 | 只看该作者
Spi-nor.c修改

使用特权

评论回复
16
g36xcv|  楼主 | 2023-10-28 01:29 | 只看该作者
/*
* 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        4

struct flash_info {
        char                *name;

        /*
         * This array stores the ID bytes.
         * The first three bytes are the JEDIC ID.
         * JEDEC ID zero means "no ID" (mostly older chips).
         */
        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[0] == opcode)
                        return table[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");
                else
                        dev_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;
        else
                return 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);
        else
                sr = 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 = 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;
        else
                lock_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);
        else
                lock_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        6

struct 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 = le32_to_cpu(bfpt.dwords);

        /* 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;
                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;
                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_SECTORS
                if (erasesize == SZ_4K) {
                        nor->erase_opcode = opcode;
                        mtd->erasesize = erasesize;
                        break;
                }
#endif
                if (!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;

                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;

                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[0] == (int)hwcaps)
                        return table[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;
        else
                mtd->_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;
                else
                        params.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.offset,
                                mtd->eraseregions.erasesize,
                                mtd->eraseregions.erasesize / 1024,
                                mtd->eraseregions.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_COMMAND
        printk("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");

使用特权

评论回复
17
g36xcv|  楼主 | 2023-10-28 01:29 | 只看该作者
设备树修改
设备树中增加节点号,如下:

使用特权

评论回复
18
g36xcv|  楼主 | 2023-10-28 01:29 | 只看该作者
/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>;
                };
        };
};

使用特权

评论回复
19
g36xcv|  楼主 | 2023-10-28 01:29 | 只看该作者
/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>;
                };
        };
};

使用特权

评论回复
20
g36xcv|  楼主 | 2023-10-28 01:29 | 只看该作者
测试
系统启动后能看到识别到了flash,采用ubi文件系统进行操作即可。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

22

主题

194

帖子

0

粉丝