MTD core changes:

- add debugfs nodes for querying the flash name and id
 
 SPI NOR core changes:
 - always use bounce buffer for register read/writes
 - move m25p80 code in spi-nor.c
 - rework hwcaps selection for the spi-mem case
 - rework the core in order to move the manufacturer specific code
   out of it:
         - regroup flash parameters in 'struct spi_nor_flash_parameter'
         - add default_init() and post_sfdp() hooks to tweak the flash
           parameters
         - introduce the ->set_4byte(), ->convert_addr() and ->setup()
           methods, to deal with manufacturer specific code
         - rework the SPI NOR lock/unlock logic
 - fix an error code in spi_nor_read_raw()
 - fix a memory leak bug
 - enable the debugfs for the partname and partid
 - add support for few flashes
 
 SPI NOR controller drivers changes:
 - intel-spi:
         - Whitelist 4B read commands
         - Add support for Intel Tiger Lake SPI serial flash
 - aspeed-smc: Add of_node_put()
 - hisi-sfc: Add of_node_put()
 - cadence-quadspi: Fix QSPI RCU Schedule Stall
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEEHUIqys8OyG1eHf7fS1VPR6WNFOkFAl1o4SQACgkQS1VPR6WN
 FOnV6ggAgSmUiJi58dzS2n5thNeYeUUW59gjJJptHH152IuP3pBFaEEzDzHA3xB8
 YDT/2RmWQmO0YcE0qY3HYFB6cXrDdO1xyfE0bxXrqDqBj7E4eX3Xw/tm+tMvqrHo
 M2KeI8/BwI7VPVrYwr1uCY1l5eoFmqF4X034DkH8eyQ1xdOM79aewQNmOx0WxHfJ
 mCFhTm2lrYXDMcedRND/rcRZG2taSGrXMANxoA7KFSkMVJP3SuQ+fsqHpngTyu11
 URVo7Xb7VIq0naYzGmpXLVWtHqWrKFfx6cvshAE6eVK/3wPn7aIC2ozapV5yHq06
 w05pwsZB4+xNeN0asRV4SnJsRxOUXA==
 =90pz
 -----END PGP SIGNATURE-----

Merge tag 'spi-nor/for-5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux into mtd/for-5.4

MTD core changes:
- add debugfs nodes for querying the flash name and id

SPI NOR core changes:
- always use bounce buffer for register read/writes
- move m25p80 code in spi-nor.c
- rework hwcaps selection for the spi-mem case
- rework the core in order to move the manufacturer specific code
  out of it:
        - regroup flash parameters in 'struct spi_nor_flash_parameter'
        - add default_init() and post_sfdp() hooks to tweak the flash
          parameters
        - introduce the ->set_4byte(), ->convert_addr() and ->setup()
          methods, to deal with manufacturer specific code
        - rework the SPI NOR lock/unlock logic
- fix an error code in spi_nor_read_raw()
- fix a memory leak bug
- enable the debugfs for the partname and partid
- add support for few flashes

SPI NOR controller drivers changes:
- intel-spi:
        - Whitelist 4B read commands
        - Add support for Intel Tiger Lake SPI serial flash
- aspeed-smc: Add of_node_put()
- hisi-sfc: Add of_node_put()
- cadence-quadspi: Fix QSPI RCU Schedule Stall
This commit is contained in:
Richard Weinberger 2019-09-15 23:00:55 +02:00
commit 560852a1d3
13 changed files with 1634 additions and 973 deletions

View File

@ -79,24 +79,6 @@ config MTD_DATAFLASH_OTP
other key product data. The second half is programmed with a
unique-to-each-chip bit pattern at the factory.
config MTD_M25P80
tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
depends on SPI_MASTER && MTD_SPI_NOR
select SPI_MEM
help
This enables access to most modern SPI flash chips, used for
program and data storage. Series supported include Atmel AT26DF,
Spansion S25SL, SST 25VF, ST M25P, and Winbond W25X. Other chips
are supported as well. See the driver source for the current list,
or to add other chips.
Note that the original DataFlash chips (AT45 series, not AT26DF),
need an entirely different driver.
Set up your spi devices with the right board-specific platform data,
if you want to specify device partitioning or to use a device which
doesn't support the JEDEC ID instruction.
config MTD_MCHP23K256
tristate "Microchip 23K256 SRAM"
depends on SPI_MASTER

View File

@ -12,7 +12,6 @@ obj-$(CONFIG_MTD_MTDRAM) += mtdram.o
obj-$(CONFIG_MTD_LART) += lart.o
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
obj-$(CONFIG_MTD_M25P80) += m25p80.o
obj-$(CONFIG_MTD_MCHP23K256) += mchp23k256.o
obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
obj-$(CONFIG_MTD_SST25L) += sst25l.o

View File

@ -1,347 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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
*/
#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/spi-mem.h>
#include <linux/spi/flash.h>
#include <linux/mtd/spi-nor.h>
struct m25p {
struct spi_mem *spimem;
struct spi_nor spi_nor;
};
static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
{
struct m25p *flash = nor->priv;
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(code, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(len, NULL, 1));
void *scratchbuf;
int ret;
scratchbuf = kmalloc(len, GFP_KERNEL);
if (!scratchbuf)
return -ENOMEM;
op.data.buf.in = scratchbuf;
ret = spi_mem_exec_op(flash->spimem, &op);
if (ret < 0)
dev_err(&flash->spimem->spi->dev, "error %d reading %x\n", ret,
code);
else
memcpy(val, scratchbuf, len);
kfree(scratchbuf);
return ret;
}
static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
{
struct m25p *flash = nor->priv;
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(len, NULL, 1));
void *scratchbuf;
int ret;
scratchbuf = kmemdup(buf, len, GFP_KERNEL);
if (!scratchbuf)
return -ENOMEM;
op.data.buf.out = scratchbuf;
ret = spi_mem_exec_op(flash->spimem, &op);
kfree(scratchbuf);
return ret;
}
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_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
SPI_MEM_OP_ADDR(nor->addr_width, to, 1),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(len, buf, 1));
int ret;
/* get transfer protocols. */
op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto);
op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto);
op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
op.addr.nbytes = 0;
ret = spi_mem_adjust_op_size(flash->spimem, &op);
if (ret)
return ret;
op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes;
ret = spi_mem_exec_op(flash->spimem, &op);
if (ret)
return ret;
return op.data.nbytes;
}
/*
* 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_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
SPI_MEM_OP_ADDR(nor->addr_width, from, 1),
SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
SPI_MEM_OP_DATA_IN(len, buf, 1));
size_t remaining = len;
int ret;
/* get transfer protocols. */
op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto);
op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto);
op.dummy.buswidth = op.addr.buswidth;
op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
/* convert the dummy cycles to the number of bytes */
op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
while (remaining) {
op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
ret = spi_mem_adjust_op_size(flash->spimem, &op);
if (ret)
return ret;
ret = spi_mem_exec_op(flash->spimem, &op);
if (ret)
return ret;
op.addr.val += op.data.nbytes;
remaining -= op.data.nbytes;
op.data.buf.in += op.data.nbytes;
}
return len;
}
/*
* 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_mem *spimem)
{
struct spi_device *spi = spimem->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(&spimem->spi->dev);
flash = devm_kzalloc(&spimem->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 = &spimem->spi->dev;
spi_nor_set_flash_node(nor, spi->dev.of_node);
nor->priv = flash;
spi_mem_set_drvdata(spimem, flash);
flash->spimem = spimem;
if (spi->mode & SPI_RX_OCTAL) {
hwcaps.mask |= SNOR_HWCAPS_READ_1_1_8;
if (spi->mode & SPI_TX_OCTAL)
hwcaps.mask |= (SNOR_HWCAPS_READ_1_8_8 |
SNOR_HWCAPS_PP_1_1_8 |
SNOR_HWCAPS_PP_1_8_8);
} else 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;
if (!nor->mtd.name)
nor->mtd.name = spi_mem_get_name(spimem);
/* 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_mem *spimem)
{
struct m25p *flash = spi_mem_get_drvdata(spimem);
spi_nor_restore(&flash->spi_nor);
/* Clean up MTD stuff. */
return mtd_device_unregister(&flash->spi_nor.mtd);
}
static void m25p_shutdown(struct spi_mem *spimem)
{
struct m25p *flash = spi_mem_get_drvdata(spimem);
spi_nor_restore(&flash->spi_nor);
}
/*
* 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"},
{"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) */
{ "mr25h128" }, /* 128 Kib, 40 MHz */
{ "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" },
{}
};
MODULE_DEVICE_TABLE(of, m25p_of_table);
static struct spi_mem_driver m25p80_driver = {
.spidrv = {
.driver = {
.name = "m25p80",
.of_match_table = m25p_of_table,
},
.id_table = m25p_ids,
},
.probe = m25p_probe,
.remove = m25p_remove,
.shutdown = m25p_shutdown,
/* 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_mem_driver(m25p80_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mike Lavender");
MODULE_DESCRIPTION("MTD SPI driver for ST M25Pxx flash chips");

View File

@ -335,6 +335,82 @@ static const struct device_type mtd_devtype = {
.release = mtd_release,
};
static int mtd_partid_show(struct seq_file *s, void *p)
{
struct mtd_info *mtd = s->private;
seq_printf(s, "%s\n", mtd->dbg.partid);
return 0;
}
static int mtd_partid_debugfs_open(struct inode *inode, struct file *file)
{
return single_open(file, mtd_partid_show, inode->i_private);
}
static const struct file_operations mtd_partid_debug_fops = {
.open = mtd_partid_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int mtd_partname_show(struct seq_file *s, void *p)
{
struct mtd_info *mtd = s->private;
seq_printf(s, "%s\n", mtd->dbg.partname);
return 0;
}
static int mtd_partname_debugfs_open(struct inode *inode, struct file *file)
{
return single_open(file, mtd_partname_show, inode->i_private);
}
static const struct file_operations mtd_partname_debug_fops = {
.open = mtd_partname_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static struct dentry *dfs_dir_mtd;
static void mtd_debugfs_populate(struct mtd_info *mtd)
{
struct device *dev = &mtd->dev;
struct dentry *root, *dent;
if (IS_ERR_OR_NULL(dfs_dir_mtd))
return;
root = debugfs_create_dir(dev_name(dev), dfs_dir_mtd);
if (IS_ERR_OR_NULL(root)) {
dev_dbg(dev, "won't show data in debugfs\n");
return;
}
mtd->dbg.dfs_dir = root;
if (mtd->dbg.partid) {
dent = debugfs_create_file("partid", 0400, root, mtd,
&mtd_partid_debug_fops);
if (IS_ERR_OR_NULL(dent))
dev_err(dev, "can't create debugfs entry for partid\n");
}
if (mtd->dbg.partname) {
dent = debugfs_create_file("partname", 0400, root, mtd,
&mtd_partname_debug_fops);
if (IS_ERR_OR_NULL(dent))
dev_err(dev,
"can't create debugfs entry for partname\n");
}
}
#ifndef CONFIG_MMU
unsigned mtd_mmap_capabilities(struct mtd_info *mtd)
{
@ -512,8 +588,6 @@ static int mtd_nvmem_add(struct mtd_info *mtd)
return 0;
}
static struct dentry *dfs_dir_mtd;
/**
* add_mtd_device - register an MTD device
* @mtd: pointer to new MTD device info structure
@ -607,13 +681,7 @@ int add_mtd_device(struct mtd_info *mtd)
if (error)
goto fail_nvmem_add;
if (!IS_ERR_OR_NULL(dfs_dir_mtd)) {
mtd->dbg.dfs_dir = debugfs_create_dir(dev_name(&mtd->dev), dfs_dir_mtd);
if (IS_ERR_OR_NULL(mtd->dbg.dfs_dir)) {
pr_debug("mtd device %s won't show data in debugfs\n",
dev_name(&mtd->dev));
}
}
mtd_debugfs_populate(mtd);
device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL,
"mtd%dro", i);

View File

@ -2,6 +2,8 @@
menuconfig MTD_SPI_NOR
tristate "SPI-NOR device support"
depends on MTD
depends on MTD && SPI_MASTER
select SPI_MEM
help
This is the framework for the SPI NOR which can be used by the SPI
device drivers and the SPI-NOR device driver.

View File

@ -836,8 +836,10 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
controller->chips[cs] = chip;
}
if (ret)
if (ret) {
of_node_put(child);
aspeed_smc_unregister(controller);
}
return ret;
}

View File

@ -13,6 +13,7 @@
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
@ -241,23 +242,13 @@ struct cqspi_driver_platdata {
#define CQSPI_IRQ_STATUS_MASK 0x1FFFF
static int cqspi_wait_for_bit(void __iomem *reg, const u32 mask, bool clear)
static int cqspi_wait_for_bit(void __iomem *reg, const u32 mask, bool clr)
{
unsigned long end = jiffies + msecs_to_jiffies(CQSPI_TIMEOUT_MS);
u32 val;
while (1) {
val = readl(reg);
if (clear)
val = ~val;
val &= mask;
if (val == mask)
return 0;
if (time_after(jiffies, end))
return -ETIMEDOUT;
}
return readl_relaxed_poll_timeout(reg, val,
(((clr ? ~val : val) & mask) == mask),
10, CQSPI_TIMEOUT_MS * 1000);
}
static bool cqspi_is_idle(struct cqspi_st *cqspi)

View File

@ -401,6 +401,7 @@ static int hisi_spi_nor_register_all(struct hifmc_host *host)
if (host->num_chip == HIFMC_MAX_CHIP_NUM) {
dev_warn(dev, "Flash device number exceeds the maximum chipselect number\n");
of_node_put(np);
break;
}
}

View File

@ -65,6 +65,7 @@ static const struct pci_device_id intel_spi_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x19e0), (unsigned long)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x34a4), (unsigned long)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x4b24), (unsigned long)&bxt_info },
{ PCI_VDEVICE(INTEL, 0xa0a4), (unsigned long)&bxt_info },
{ PCI_VDEVICE(INTEL, 0xa1a4), (unsigned long)&bxt_info },
{ PCI_VDEVICE(INTEL, 0xa224), (unsigned long)&bxt_info },
{ },

View File

@ -621,6 +621,8 @@ static ssize_t intel_spi_read(struct spi_nor *nor, loff_t from, size_t len,
switch (nor->read_opcode) {
case SPINOR_OP_READ:
case SPINOR_OP_READ_FAST:
case SPINOR_OP_READ_4B:
case SPINOR_OP_READ_FAST_4B:
break;
default:
return -EINVAL;

File diff suppressed because it is too large Load Diff

View File

@ -189,6 +189,9 @@ struct module; /* only needed for owner field in mtd_info */
*/
struct mtd_debug_info {
struct dentry *dfs_dir;
const char *partname;
const char *partid;
};
struct mtd_info {

View File

@ -9,6 +9,7 @@
#include <linux/bitops.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/mtd.h>
#include <linux/spi/spi-mem.h>
/*
* Manufacturer IDs
@ -224,7 +225,6 @@ 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,
@ -237,12 +237,12 @@ 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),
SNOR_F_BROKEN_RESET = BIT(6),
SNOR_F_4B_OPCODES = BIT(7),
SNOR_F_HAS_4BAIT = BIT(8),
SNOR_F_READY_XSR_RDY = BIT(3),
SNOR_F_USE_CLSR = BIT(4),
SNOR_F_BROKEN_RESET = BIT(5),
SNOR_F_4B_OPCODES = BIT(6),
SNOR_F_HAS_4BAIT = BIT(7),
SNOR_F_HAS_LOCK = BIT(8),
};
/**
@ -333,130 +333,6 @@ struct spi_nor_erase_map {
u8 uniform_erase_type;
};
/**
* struct flash_info - Forward declaration of a structure used internally by
* spi_nor_scan()
*/
struct flash_info;
/**
* 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.
* @info: spi-nor part JDEC MFR id and other info
* @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
* @erase_map: the erase map of the SPI NOR
* @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
* @quad_enable: [FLASH-SPECIFIC] enables SPI NOR quad mode
* @clear_sr_bp: [FLASH-SPECIFIC] clears the Block Protection Bits from
* the SPI NOR Status Register.
* completely locked
* @priv: the private data
*/
struct spi_nor {
struct mtd_info mtd;
struct mutex lock;
struct device *dev;
const struct flash_info *info;
u32 page_size;
u8 addr_width;
u8 erase_opcode;
u8 read_opcode;
u8 read_dummy;
u8 program_opcode;
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];
struct spi_nor_erase_map erase_map;
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);
int (*quad_enable)(struct spi_nor *nor);
int (*clear_sr_bp)(struct spi_nor *nor);
void *priv;
};
static u64 __maybe_unused
spi_nor_region_is_last(const struct spi_nor_erase_region *region)
{
return region->offset & SNOR_LAST_REGION;
}
static u64 __maybe_unused
spi_nor_region_end(const struct spi_nor_erase_region *region)
{
return (region->offset & ~SNOR_ERASE_FLAGS_MASK) + region->size;
}
static void __maybe_unused
spi_nor_region_mark_end(struct spi_nor_erase_region *region)
{
region->offset |= SNOR_LAST_REGION;
}
static void __maybe_unused
spi_nor_region_mark_overlay(struct spi_nor_erase_region *region)
{
region->offset |= SNOR_OVERLAID_REGION;
}
static bool __maybe_unused spi_nor_has_uniform_erase(const struct spi_nor *nor)
{
return !!nor->erase_map.uniform_erase_type;
}
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).
@ -518,6 +394,257 @@ struct spi_nor_hwcaps {
#define SNOR_HWCAPS_PP_1_8_8 BIT(21)
#define SNOR_HWCAPS_PP_8_8_8 BIT(22)
#define SNOR_HWCAPS_X_X_X (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)
#define SNOR_HWCAPS_DTR (SNOR_HWCAPS_READ_1_1_1_DTR | \
SNOR_HWCAPS_READ_1_2_2_DTR | \
SNOR_HWCAPS_READ_1_4_4_DTR | \
SNOR_HWCAPS_READ_1_8_8_DTR)
#define SNOR_HWCAPS_ALL (SNOR_HWCAPS_READ_MASK | \
SNOR_HWCAPS_PP_MASK)
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,
/* Octal 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,
/* Octal SPI */
SNOR_CMD_PP_1_1_8,
SNOR_CMD_PP_1_8_8,
SNOR_CMD_PP_8_8_8,
SNOR_CMD_PP_MAX
};
/* Forward declaration that will be used in 'struct spi_nor_flash_parameter' */
struct spi_nor;
/**
* struct spi_nor_locking_ops - SPI NOR locking methods
* @lock: lock a region of the SPI NOR.
* @unlock: unlock a region of the SPI NOR.
* @is_locked: check if a region of the SPI NOR is completely locked
*/
struct spi_nor_locking_ops {
int (*lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
int (*unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
int (*is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
};
/**
* struct spi_nor_flash_parameter - SPI NOR flash parameters and settings.
* Includes legacy flash parameters and settings that can be overwritten
* by the spi_nor_fixups hooks, or dynamically when parsing the JESD216
* Serial Flash Discoverable Parameters (SFDP) tables.
*
* @size: the flash memory density in bytes.
* @page_size: the page size of the SPI NOR flash memory.
* @hwcaps: describes the read and page program hardware
* capabilities.
* @reads: read capabilities ordered by priority: the higher index
* in the array, the higher priority.
* @page_programs: page program capabilities ordered by priority: the
* higher index in the array, the higher priority.
* @erase_map: the erase map parsed from the SFDP Sector Map Parameter
* Table.
* @quad_enable: enables SPI NOR quad mode.
* @set_4byte: puts the SPI NOR in 4 byte addressing mode.
* @convert_addr: converts an absolute address into something the flash
* will understand. Particularly useful when pagesize is
* not a power-of-2.
* @setup: configures the SPI NOR memory. Useful for SPI NOR
* flashes that have peculiarities to the SPI NOR standard
* e.g. different opcodes, specific address calculation,
* page size, etc.
* @locking_ops: SPI NOR locking methods.
*/
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];
struct spi_nor_erase_map erase_map;
int (*quad_enable)(struct spi_nor *nor);
int (*set_4byte)(struct spi_nor *nor, bool enable);
u32 (*convert_addr)(struct spi_nor *nor, u32 addr);
int (*setup)(struct spi_nor *nor, const struct spi_nor_hwcaps *hwcaps);
const struct spi_nor_locking_ops *locking_ops;
};
/**
* struct flash_info - Forward declaration of a structure used internally by
* spi_nor_scan()
*/
struct flash_info;
/**
* 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.
* @spimem: point to the spi mem device
* @bouncebuf: bounce buffer used when the buffer passed by the MTD
* layer is not DMA-able
* @bouncebuf_size: size of the bounce buffer
* @info: spi-nor part JDEC MFR id and other info
* @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
* @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()
* @clear_sr_bp: [FLASH-SPECIFIC] clears the Block Protection Bits from
* the SPI NOR Status Register.
* @params: [FLASH-SPECIFIC] SPI-NOR flash parameters and settings.
* The structure includes legacy flash parameters and
* settings that can be overwritten by the spi_nor_fixups
* hooks, or dynamically when parsing the SFDP tables.
* @priv: the private data
*/
struct spi_nor {
struct mtd_info mtd;
struct mutex lock;
struct device *dev;
struct spi_mem *spimem;
u8 *bouncebuf;
size_t bouncebuf_size;
const struct flash_info *info;
u32 page_size;
u8 addr_width;
u8 erase_opcode;
u8 read_opcode;
u8 read_dummy;
u8 program_opcode;
enum spi_nor_protocol read_proto;
enum spi_nor_protocol write_proto;
enum spi_nor_protocol reg_proto;
bool sst_write_second;
u32 flags;
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 (*clear_sr_bp)(struct spi_nor *nor);
struct spi_nor_flash_parameter params;
void *priv;
};
static u64 __maybe_unused
spi_nor_region_is_last(const struct spi_nor_erase_region *region)
{
return region->offset & SNOR_LAST_REGION;
}
static u64 __maybe_unused
spi_nor_region_end(const struct spi_nor_erase_region *region)
{
return (region->offset & ~SNOR_ERASE_FLAGS_MASK) + region->size;
}
static void __maybe_unused
spi_nor_region_mark_end(struct spi_nor_erase_region *region)
{
region->offset |= SNOR_LAST_REGION;
}
static void __maybe_unused
spi_nor_region_mark_overlay(struct spi_nor_erase_region *region)
{
region->offset |= SNOR_OVERLAID_REGION;
}
static bool __maybe_unused spi_nor_has_uniform_erase(const struct spi_nor *nor)
{
return !!nor->params.erase_map.uniform_erase_type;
}
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);
}
/**
* spi_nor_scan() - scan the SPI NOR
* @nor: the spi_nor structure