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:
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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");
|
@@ -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);
|
||||
|
@@ -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.
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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 },
|
||||
{ },
|
||||
|
@@ -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
Reference in New Issue
Block a user