Core changes:
- Add support for a bunch of SPI NOR chips - Clear EAR reg when switching to 3-byte addressing mode on Winbond chips SPI NOR controller driver changes: - cadence: Add DMA support for direct mode reads - hisi: Prefix a few functions with hisi_ - intel: * Mark the driver as "dangerous" in Kconfig * Fix atomic sequence handling * Pass a 40us delay (instead of 0us) to readl_poll_timeout() - fsl: * fix a typo in a function name * add support for IP variants embedded in the ls2080a and ls1080a SoCs - stm32: request exclusive control of the reset line -----BEGIN PGP SIGNATURE----- iQI5BAABCAAjBQJbFQHqHBxib3Jpcy5icmV6aWxsb25AYm9vdGxpbi5jb20ACgkQ Ze02AX4ItwAIyBAAw1y6oLc4WcA6//0+sxB9RaYxpng+8n2ExTxcvfc1jd23e5te BRc74YJtbBBe27kkfuhrDLHoTzHMYicB4KA7E4o8JIvIKxfUlY8nP508JAexGEpK wcSNzqjk8zvAZrZ3Uc3xk1wZcYlc4uYAI2XuOjRrW7JFDdKDqSV0NbdjI0V0YFUk n/UQ+m5U1xUilp1IszkU/OdJ7ho1qcxWOC0sqCBXIJ2RrOMQzz3kYF/M9CNZzpth C7zXBVZtJVoy4K6Ibzoudvqcl4TiVu1eMjLPO3FPT3wf73OBPoo9gHvDaw8MqAJw 6Zeu1qbIwEXyR5K3/jlRN2CPkl1tNXqkrZ2CqK6OvLHI1cP99IBQFTbN4aWDm86v tf3Gam7zthWALIkqiAug1tZA4TdFI0ZN6MCBYsfGeeZKF+T22LkkyLseCrVYR28i W2UQDohKyvVE8OVimFdPtjvH9KKJE03hbseTU8D0kOGViptqmidb0Bci6hWqNzxg 0FRWu+LVVd6fDdovKTpuEMS5PJJ5jgjpy/Ch9rRvbqVPC7ZTMwOI5i9sjLcrZ8/v 6InJcBkg+HG5fedzBi15hRtm/XRwJgoI4jJbAgmu3F/92nmMP2lO4ACRUqhgEDN/ gi+UnVA8+aDMJt22c1FHveKVtheTiZNkMFtY16qYYJFmnOuS4jWyTWinihQ= =eIAQ -----END PGP SIGNATURE----- Merge tag 'spi-nor/for-4.18' of git://git.infradead.org/linux-mtd into mtd/next Core changes: - Add support for a bunch of SPI NOR chips - Clear EAR reg when switching to 3-byte addressing mode on Winbond chips SPI NOR controller driver changes: - cadence: Add DMA support for direct mode reads - hisi: Prefix a few functions with hisi_ - intel: * Mark the driver as "dangerous" in Kconfig * Fix atomic sequence handling * Pass a 40us delay (instead of 0us) to readl_poll_timeout() - fsl: * fix a typo in a function name * add support for IP variants embedded in the ls2080a and ls1080a SoCs - stm32: request exclusive control of the reset line
This commit is contained in:
Коммит
b771327a45
|
@ -90,7 +90,7 @@ config SPI_INTEL_SPI
|
||||||
tristate
|
tristate
|
||||||
|
|
||||||
config SPI_INTEL_SPI_PCI
|
config SPI_INTEL_SPI_PCI
|
||||||
tristate "Intel PCH/PCU SPI flash PCI driver"
|
tristate "Intel PCH/PCU SPI flash PCI driver (DANGEROUS)"
|
||||||
depends on X86 && PCI
|
depends on X86 && PCI
|
||||||
select SPI_INTEL_SPI
|
select SPI_INTEL_SPI
|
||||||
help
|
help
|
||||||
|
@ -106,7 +106,7 @@ config SPI_INTEL_SPI_PCI
|
||||||
will be called intel-spi-pci.
|
will be called intel-spi-pci.
|
||||||
|
|
||||||
config SPI_INTEL_SPI_PLATFORM
|
config SPI_INTEL_SPI_PLATFORM
|
||||||
tristate "Intel PCH/PCU SPI flash platform driver"
|
tristate "Intel PCH/PCU SPI flash platform driver (DANGEROUS)"
|
||||||
depends on X86
|
depends on X86
|
||||||
select SPI_INTEL_SPI
|
select SPI_INTEL_SPI
|
||||||
help
|
help
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
@ -73,6 +75,10 @@ struct cqspi_st {
|
||||||
struct completion transfer_complete;
|
struct completion transfer_complete;
|
||||||
struct mutex bus_mutex;
|
struct mutex bus_mutex;
|
||||||
|
|
||||||
|
struct dma_chan *rx_chan;
|
||||||
|
struct completion rx_dma_complete;
|
||||||
|
dma_addr_t mmap_phys_base;
|
||||||
|
|
||||||
int current_cs;
|
int current_cs;
|
||||||
int current_page_size;
|
int current_page_size;
|
||||||
int current_erase_size;
|
int current_erase_size;
|
||||||
|
@ -915,11 +921,75 @@ static ssize_t cqspi_write(struct spi_nor *nor, loff_t to,
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cqspi_rx_dma_callback(void *param)
|
||||||
|
{
|
||||||
|
struct cqspi_st *cqspi = param;
|
||||||
|
|
||||||
|
complete(&cqspi->rx_dma_complete);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cqspi_direct_read_execute(struct spi_nor *nor, u_char *buf,
|
||||||
|
loff_t from, size_t len)
|
||||||
|
{
|
||||||
|
struct cqspi_flash_pdata *f_pdata = nor->priv;
|
||||||
|
struct cqspi_st *cqspi = f_pdata->cqspi;
|
||||||
|
enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
|
||||||
|
dma_addr_t dma_src = (dma_addr_t)cqspi->mmap_phys_base + from;
|
||||||
|
int ret = 0;
|
||||||
|
struct dma_async_tx_descriptor *tx;
|
||||||
|
dma_cookie_t cookie;
|
||||||
|
dma_addr_t dma_dst;
|
||||||
|
|
||||||
|
if (!cqspi->rx_chan || !virt_addr_valid(buf)) {
|
||||||
|
memcpy_fromio(buf, cqspi->ahb_base + from, len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_dst = dma_map_single(nor->dev, buf, len, DMA_DEV_TO_MEM);
|
||||||
|
if (dma_mapping_error(nor->dev, dma_dst)) {
|
||||||
|
dev_err(nor->dev, "dma mapping failed\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
tx = dmaengine_prep_dma_memcpy(cqspi->rx_chan, dma_dst, dma_src,
|
||||||
|
len, flags);
|
||||||
|
if (!tx) {
|
||||||
|
dev_err(nor->dev, "device_prep_dma_memcpy error\n");
|
||||||
|
ret = -EIO;
|
||||||
|
goto err_unmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
tx->callback = cqspi_rx_dma_callback;
|
||||||
|
tx->callback_param = cqspi;
|
||||||
|
cookie = tx->tx_submit(tx);
|
||||||
|
reinit_completion(&cqspi->rx_dma_complete);
|
||||||
|
|
||||||
|
ret = dma_submit_error(cookie);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(nor->dev, "dma_submit_error %d\n", cookie);
|
||||||
|
ret = -EIO;
|
||||||
|
goto err_unmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_async_issue_pending(cqspi->rx_chan);
|
||||||
|
ret = wait_for_completion_timeout(&cqspi->rx_dma_complete,
|
||||||
|
msecs_to_jiffies(len));
|
||||||
|
if (ret <= 0) {
|
||||||
|
dmaengine_terminate_sync(cqspi->rx_chan);
|
||||||
|
dev_err(nor->dev, "DMA wait_for_completion_timeout\n");
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
goto err_unmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
err_unmap:
|
||||||
|
dma_unmap_single(nor->dev, dma_dst, len, DMA_DEV_TO_MEM);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t cqspi_read(struct spi_nor *nor, loff_t from,
|
static ssize_t cqspi_read(struct spi_nor *nor, loff_t from,
|
||||||
size_t len, u_char *buf)
|
size_t len, u_char *buf)
|
||||||
{
|
{
|
||||||
struct cqspi_flash_pdata *f_pdata = nor->priv;
|
struct cqspi_flash_pdata *f_pdata = nor->priv;
|
||||||
struct cqspi_st *cqspi = f_pdata->cqspi;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = cqspi_set_protocol(nor, 1);
|
ret = cqspi_set_protocol(nor, 1);
|
||||||
|
@ -931,7 +1001,7 @@ static ssize_t cqspi_read(struct spi_nor *nor, loff_t from,
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (f_pdata->use_direct_mode)
|
if (f_pdata->use_direct_mode)
|
||||||
memcpy_fromio(buf, cqspi->ahb_base + from, len);
|
ret = cqspi_direct_read_execute(nor, buf, from, len);
|
||||||
else
|
else
|
||||||
ret = cqspi_indirect_read_execute(nor, buf, from, len);
|
ret = cqspi_indirect_read_execute(nor, buf, from, len);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -1100,6 +1170,21 @@ static void cqspi_controller_init(struct cqspi_st *cqspi)
|
||||||
cqspi_controller_enable(cqspi, 1);
|
cqspi_controller_enable(cqspi, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cqspi_request_mmap_dma(struct cqspi_st *cqspi)
|
||||||
|
{
|
||||||
|
dma_cap_mask_t mask;
|
||||||
|
|
||||||
|
dma_cap_zero(mask);
|
||||||
|
dma_cap_set(DMA_MEMCPY, mask);
|
||||||
|
|
||||||
|
cqspi->rx_chan = dma_request_chan_by_mask(&mask);
|
||||||
|
if (IS_ERR(cqspi->rx_chan)) {
|
||||||
|
dev_err(&cqspi->pdev->dev, "No Rx DMA available\n");
|
||||||
|
cqspi->rx_chan = NULL;
|
||||||
|
}
|
||||||
|
init_completion(&cqspi->rx_dma_complete);
|
||||||
|
}
|
||||||
|
|
||||||
static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
|
static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
|
||||||
{
|
{
|
||||||
const struct spi_nor_hwcaps hwcaps = {
|
const struct spi_nor_hwcaps hwcaps = {
|
||||||
|
@ -1177,6 +1262,9 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
|
||||||
f_pdata->use_direct_mode = true;
|
f_pdata->use_direct_mode = true;
|
||||||
dev_dbg(nor->dev, "using direct mode for %s\n",
|
dev_dbg(nor->dev, "using direct mode for %s\n",
|
||||||
mtd->name);
|
mtd->name);
|
||||||
|
|
||||||
|
if (!cqspi->rx_chan)
|
||||||
|
cqspi_request_mmap_dma(cqspi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1237,6 +1325,7 @@ static int cqspi_probe(struct platform_device *pdev)
|
||||||
dev_err(dev, "Cannot remap AHB address.\n");
|
dev_err(dev, "Cannot remap AHB address.\n");
|
||||||
return PTR_ERR(cqspi->ahb_base);
|
return PTR_ERR(cqspi->ahb_base);
|
||||||
}
|
}
|
||||||
|
cqspi->mmap_phys_base = (dma_addr_t)res_ahb->start;
|
||||||
cqspi->ahb_size = resource_size(res_ahb);
|
cqspi->ahb_size = resource_size(res_ahb);
|
||||||
|
|
||||||
init_completion(&cqspi->transfer_complete);
|
init_completion(&cqspi->transfer_complete);
|
||||||
|
@ -1307,6 +1396,9 @@ static int cqspi_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
cqspi_controller_enable(cqspi, 0);
|
cqspi_controller_enable(cqspi, 0);
|
||||||
|
|
||||||
|
if (cqspi->rx_chan)
|
||||||
|
dma_release_channel(cqspi->rx_chan);
|
||||||
|
|
||||||
clk_disable_unprepare(cqspi->clk);
|
clk_disable_unprepare(cqspi->clk);
|
||||||
|
|
||||||
pm_runtime_put_sync(&pdev->dev);
|
pm_runtime_put_sync(&pdev->dev);
|
||||||
|
|
|
@ -214,6 +214,7 @@ enum fsl_qspi_devtype {
|
||||||
FSL_QUADSPI_IMX7D,
|
FSL_QUADSPI_IMX7D,
|
||||||
FSL_QUADSPI_IMX6UL,
|
FSL_QUADSPI_IMX6UL,
|
||||||
FSL_QUADSPI_LS1021A,
|
FSL_QUADSPI_LS1021A,
|
||||||
|
FSL_QUADSPI_LS2080A,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fsl_qspi_devtype_data {
|
struct fsl_qspi_devtype_data {
|
||||||
|
@ -267,6 +268,15 @@ static struct fsl_qspi_devtype_data ls1021a_data = {
|
||||||
.driver_data = 0,
|
.driver_data = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct fsl_qspi_devtype_data ls2080a_data = {
|
||||||
|
.devtype = FSL_QUADSPI_LS2080A,
|
||||||
|
.rxfifo = 128,
|
||||||
|
.txfifo = 64,
|
||||||
|
.ahb_buf_size = 1024,
|
||||||
|
.driver_data = QUADSPI_QUIRK_TKT253890,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
#define FSL_QSPI_MAX_CHIP 4
|
#define FSL_QSPI_MAX_CHIP 4
|
||||||
struct fsl_qspi {
|
struct fsl_qspi {
|
||||||
struct spi_nor nor[FSL_QSPI_MAX_CHIP];
|
struct spi_nor nor[FSL_QSPI_MAX_CHIP];
|
||||||
|
@ -661,7 +671,7 @@ static void fsl_qspi_set_map_addr(struct fsl_qspi *q)
|
||||||
* causes the controller to clear the buffer, and use the sequence pointed
|
* causes the controller to clear the buffer, and use the sequence pointed
|
||||||
* by the QUADSPI_BFGENCR[SEQID] to initiate a read from the flash.
|
* by the QUADSPI_BFGENCR[SEQID] to initiate a read from the flash.
|
||||||
*/
|
*/
|
||||||
static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
|
static void fsl_qspi_init_ahb_read(struct fsl_qspi *q)
|
||||||
{
|
{
|
||||||
void __iomem *base = q->iobase;
|
void __iomem *base = q->iobase;
|
||||||
int seqid;
|
int seqid;
|
||||||
|
@ -795,7 +805,7 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q)
|
||||||
fsl_qspi_init_lut(q);
|
fsl_qspi_init_lut(q);
|
||||||
|
|
||||||
/* Init for AHB read */
|
/* Init for AHB read */
|
||||||
fsl_qspi_init_abh_read(q);
|
fsl_qspi_init_ahb_read(q);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -806,6 +816,7 @@ static const struct of_device_id fsl_qspi_dt_ids[] = {
|
||||||
{ .compatible = "fsl,imx7d-qspi", .data = &imx7d_data, },
|
{ .compatible = "fsl,imx7d-qspi", .data = &imx7d_data, },
|
||||||
{ .compatible = "fsl,imx6ul-qspi", .data = &imx6ul_data, },
|
{ .compatible = "fsl,imx6ul-qspi", .data = &imx6ul_data, },
|
||||||
{ .compatible = "fsl,ls1021a-qspi", .data = (void *)&ls1021a_data, },
|
{ .compatible = "fsl,ls1021a-qspi", .data = (void *)&ls1021a_data, },
|
||||||
|
{ .compatible = "fsl,ls2080a-qspi", .data = &ls2080a_data, },
|
||||||
{ /* sentinel */ }
|
{ /* sentinel */ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);
|
MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);
|
||||||
|
|
|
@ -112,7 +112,7 @@ struct hifmc_host {
|
||||||
u32 num_chip;
|
u32 num_chip;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline int wait_op_finish(struct hifmc_host *host)
|
static inline int hisi_spi_nor_wait_op_finish(struct hifmc_host *host)
|
||||||
{
|
{
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ static inline int wait_op_finish(struct hifmc_host *host)
|
||||||
(reg & FMC_INT_OP_DONE), 0, FMC_WAIT_TIMEOUT);
|
(reg & FMC_INT_OP_DONE), 0, FMC_WAIT_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_if_type(enum spi_nor_protocol proto)
|
static int hisi_spi_nor_get_if_type(enum spi_nor_protocol proto)
|
||||||
{
|
{
|
||||||
enum hifmc_iftype if_type;
|
enum hifmc_iftype if_type;
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ static int hisi_spi_nor_op_reg(struct spi_nor *nor,
|
||||||
reg = FMC_OP_CMD1_EN | FMC_OP_REG_OP_START | optype;
|
reg = FMC_OP_CMD1_EN | FMC_OP_REG_OP_START | optype;
|
||||||
writel(reg, host->regbase + FMC_OP);
|
writel(reg, host->regbase + FMC_OP);
|
||||||
|
|
||||||
return wait_op_finish(host);
|
return hisi_spi_nor_wait_op_finish(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hisi_spi_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
|
static int hisi_spi_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
|
||||||
|
@ -259,9 +259,9 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off,
|
||||||
|
|
||||||
reg = OP_CFG_FM_CS(priv->chipselect);
|
reg = OP_CFG_FM_CS(priv->chipselect);
|
||||||
if (op_type == FMC_OP_READ)
|
if (op_type == FMC_OP_READ)
|
||||||
if_type = get_if_type(nor->read_proto);
|
if_type = hisi_spi_nor_get_if_type(nor->read_proto);
|
||||||
else
|
else
|
||||||
if_type = get_if_type(nor->write_proto);
|
if_type = hisi_spi_nor_get_if_type(nor->write_proto);
|
||||||
reg |= OP_CFG_MEM_IF_TYPE(if_type);
|
reg |= OP_CFG_MEM_IF_TYPE(if_type);
|
||||||
if (op_type == FMC_OP_READ)
|
if (op_type == FMC_OP_READ)
|
||||||
reg |= OP_CFG_DUMMY_NUM(nor->read_dummy >> 3);
|
reg |= OP_CFG_DUMMY_NUM(nor->read_dummy >> 3);
|
||||||
|
@ -274,7 +274,7 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off,
|
||||||
: OP_CTRL_WR_OPCODE(nor->program_opcode);
|
: OP_CTRL_WR_OPCODE(nor->program_opcode);
|
||||||
writel(reg, host->regbase + FMC_OP_DMA);
|
writel(reg, host->regbase + FMC_OP_DMA);
|
||||||
|
|
||||||
return wait_op_finish(host);
|
return hisi_spi_nor_wait_op_finish(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t hisi_spi_nor_read(struct spi_nor *nor, loff_t from, size_t len,
|
static ssize_t hisi_spi_nor_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||||
|
|
|
@ -136,6 +136,7 @@
|
||||||
* @swseq_reg: Use SW sequencer in register reads/writes
|
* @swseq_reg: Use SW sequencer in register reads/writes
|
||||||
* @swseq_erase: Use SW sequencer in erase operation
|
* @swseq_erase: Use SW sequencer in erase operation
|
||||||
* @erase_64k: 64k erase supported
|
* @erase_64k: 64k erase supported
|
||||||
|
* @atomic_preopcode: Holds preopcode when atomic sequence is requested
|
||||||
* @opcodes: Opcodes which are supported. This are programmed by BIOS
|
* @opcodes: Opcodes which are supported. This are programmed by BIOS
|
||||||
* before it locks down the controller.
|
* before it locks down the controller.
|
||||||
*/
|
*/
|
||||||
|
@ -153,6 +154,7 @@ struct intel_spi {
|
||||||
bool swseq_reg;
|
bool swseq_reg;
|
||||||
bool swseq_erase;
|
bool swseq_erase;
|
||||||
bool erase_64k;
|
bool erase_64k;
|
||||||
|
u8 atomic_preopcode;
|
||||||
u8 opcodes[8];
|
u8 opcodes[8];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -285,7 +287,7 @@ static int intel_spi_wait_hw_busy(struct intel_spi *ispi)
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
return readl_poll_timeout(ispi->base + HSFSTS_CTL, val,
|
return readl_poll_timeout(ispi->base + HSFSTS_CTL, val,
|
||||||
!(val & HSFSTS_CTL_SCIP), 0,
|
!(val & HSFSTS_CTL_SCIP), 40,
|
||||||
INTEL_SPI_TIMEOUT * 1000);
|
INTEL_SPI_TIMEOUT * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,7 +296,7 @@ static int intel_spi_wait_sw_busy(struct intel_spi *ispi)
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
return readl_poll_timeout(ispi->sregs + SSFSTS_CTL, val,
|
return readl_poll_timeout(ispi->sregs + SSFSTS_CTL, val,
|
||||||
!(val & SSFSTS_CTL_SCIP), 0,
|
!(val & SSFSTS_CTL_SCIP), 40,
|
||||||
INTEL_SPI_TIMEOUT * 1000);
|
INTEL_SPI_TIMEOUT * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -474,7 +476,7 @@ static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, int len,
|
||||||
int optype)
|
int optype)
|
||||||
{
|
{
|
||||||
u32 val = 0, status;
|
u32 val = 0, status;
|
||||||
u16 preop;
|
u8 atomic_preopcode;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = intel_spi_opcode_index(ispi, opcode, optype);
|
ret = intel_spi_opcode_index(ispi, opcode, optype);
|
||||||
|
@ -484,17 +486,42 @@ static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, int len,
|
||||||
if (len > INTEL_SPI_FIFO_SZ)
|
if (len > INTEL_SPI_FIFO_SZ)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Always clear it after each SW sequencer operation regardless
|
||||||
|
* of whether it is successful or not.
|
||||||
|
*/
|
||||||
|
atomic_preopcode = ispi->atomic_preopcode;
|
||||||
|
ispi->atomic_preopcode = 0;
|
||||||
|
|
||||||
/* Only mark 'Data Cycle' bit when there is data to be transferred */
|
/* Only mark 'Data Cycle' bit when there is data to be transferred */
|
||||||
if (len > 0)
|
if (len > 0)
|
||||||
val = ((len - 1) << SSFSTS_CTL_DBC_SHIFT) | SSFSTS_CTL_DS;
|
val = ((len - 1) << SSFSTS_CTL_DBC_SHIFT) | SSFSTS_CTL_DS;
|
||||||
val |= ret << SSFSTS_CTL_COP_SHIFT;
|
val |= ret << SSFSTS_CTL_COP_SHIFT;
|
||||||
val |= SSFSTS_CTL_FCERR | SSFSTS_CTL_FDONE;
|
val |= SSFSTS_CTL_FCERR | SSFSTS_CTL_FDONE;
|
||||||
val |= SSFSTS_CTL_SCGO;
|
val |= SSFSTS_CTL_SCGO;
|
||||||
|
if (atomic_preopcode) {
|
||||||
|
u16 preop;
|
||||||
|
|
||||||
|
switch (optype) {
|
||||||
|
case OPTYPE_WRITE_NO_ADDR:
|
||||||
|
case OPTYPE_WRITE_WITH_ADDR:
|
||||||
|
/* Pick matching preopcode for the atomic sequence */
|
||||||
preop = readw(ispi->sregs + PREOP_OPTYPE);
|
preop = readw(ispi->sregs + PREOP_OPTYPE);
|
||||||
if (preop) {
|
if ((preop & 0xff) == atomic_preopcode)
|
||||||
val |= SSFSTS_CTL_ACS;
|
; /* Do nothing */
|
||||||
if (preop >> 8)
|
else if ((preop >> 8) == atomic_preopcode)
|
||||||
val |= SSFSTS_CTL_SPOP;
|
val |= SSFSTS_CTL_SPOP;
|
||||||
|
else
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Enable atomic sequence */
|
||||||
|
val |= SSFSTS_CTL_ACS;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
writel(val, ispi->sregs + SSFSTS_CTL);
|
writel(val, ispi->sregs + SSFSTS_CTL);
|
||||||
|
|
||||||
|
@ -538,13 +565,31 @@ static int intel_spi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is handled with atomic operation and preop code in Intel
|
* This is handled with atomic operation and preop code in Intel
|
||||||
* controller so skip it here now. If the controller is not locked,
|
* controller so we only verify that it is available. If the
|
||||||
* program the opcode to the PREOP register for later use.
|
* controller is not locked, program the opcode to the PREOP
|
||||||
|
* register for later use.
|
||||||
|
*
|
||||||
|
* When hardware sequencer is used there is no need to program
|
||||||
|
* any opcodes (it handles them automatically as part of a command).
|
||||||
*/
|
*/
|
||||||
if (opcode == SPINOR_OP_WREN) {
|
if (opcode == SPINOR_OP_WREN) {
|
||||||
if (!ispi->locked)
|
u16 preop;
|
||||||
writel(opcode, ispi->sregs + PREOP_OPTYPE);
|
|
||||||
|
|
||||||
|
if (!ispi->swseq_reg)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
preop = readw(ispi->sregs + PREOP_OPTYPE);
|
||||||
|
if ((preop & 0xff) != opcode && (preop >> 8) != opcode) {
|
||||||
|
if (ispi->locked)
|
||||||
|
return -EINVAL;
|
||||||
|
writel(opcode, ispi->sregs + PREOP_OPTYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This enables atomic sequence on next SW sycle. Will
|
||||||
|
* be cleared after next operation.
|
||||||
|
*/
|
||||||
|
ispi->atomic_preopcode = opcode;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -569,6 +614,13 @@ static ssize_t intel_spi_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||||
u32 val, status;
|
u32 val, status;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Atomic sequence is not expected with HW sequencer reads. Make
|
||||||
|
* sure it is cleared regardless.
|
||||||
|
*/
|
||||||
|
if (WARN_ON_ONCE(ispi->atomic_preopcode))
|
||||||
|
ispi->atomic_preopcode = 0;
|
||||||
|
|
||||||
switch (nor->read_opcode) {
|
switch (nor->read_opcode) {
|
||||||
case SPINOR_OP_READ:
|
case SPINOR_OP_READ:
|
||||||
case SPINOR_OP_READ_FAST:
|
case SPINOR_OP_READ_FAST:
|
||||||
|
@ -627,6 +679,9 @@ static ssize_t intel_spi_write(struct spi_nor *nor, loff_t to, size_t len,
|
||||||
u32 val, status;
|
u32 val, status;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
|
/* Not needed with HW sequencer write, make sure it is cleared */
|
||||||
|
ispi->atomic_preopcode = 0;
|
||||||
|
|
||||||
while (len > 0) {
|
while (len > 0) {
|
||||||
block_size = min_t(size_t, len, INTEL_SPI_FIFO_SZ);
|
block_size = min_t(size_t, len, INTEL_SPI_FIFO_SZ);
|
||||||
|
|
||||||
|
@ -707,6 +762,9 @@ static int intel_spi_erase(struct spi_nor *nor, loff_t offs)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Not needed with HW sequencer erase, make sure it is cleared */
|
||||||
|
ispi->atomic_preopcode = 0;
|
||||||
|
|
||||||
while (len > 0) {
|
while (len > 0) {
|
||||||
writel(offs, ispi->base + FADDR);
|
writel(offs, ispi->base + FADDR);
|
||||||
|
|
||||||
|
|
|
@ -284,6 +284,20 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
|
||||||
if (need_wren)
|
if (need_wren)
|
||||||
write_disable(nor);
|
write_disable(nor);
|
||||||
|
|
||||||
|
if (!status && !enable &&
|
||||||
|
JEDEC_MFR(info) == SNOR_MFR_WINBOND) {
|
||||||
|
/*
|
||||||
|
* On Winbond W25Q256FV, leaving 4byte mode causes
|
||||||
|
* the Extended Address Register to be set to 1, so all
|
||||||
|
* 3-byte-address reads come from the second 16M.
|
||||||
|
* We must clear the register to enable normal behavior.
|
||||||
|
*/
|
||||||
|
write_enable(nor);
|
||||||
|
nor->cmd_buf[0] = 0;
|
||||||
|
nor->write_reg(nor, SPINOR_OP_WREAR, nor->cmd_buf, 1);
|
||||||
|
write_disable(nor);
|
||||||
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
default:
|
default:
|
||||||
/* Spansion style */
|
/* Spansion style */
|
||||||
|
@ -980,6 +994,7 @@ static const struct flash_info spi_nor_ids[] = {
|
||||||
{ "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) },
|
{ "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) },
|
||||||
{ "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) },
|
{ "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) },
|
||||||
{ "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) },
|
{ "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) },
|
||||||
|
{ "en25qh32", INFO(0x1c7016, 0, 64 * 1024, 64, 0) },
|
||||||
{ "en25qh128", INFO(0x1c7018, 0, 64 * 1024, 256, 0) },
|
{ "en25qh128", INFO(0x1c7018, 0, 64 * 1024, 256, 0) },
|
||||||
{ "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) },
|
{ "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) },
|
||||||
{ "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, SECT_4K) },
|
{ "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, SECT_4K) },
|
||||||
|
@ -1049,6 +1064,14 @@ static const struct flash_info spi_nor_ids[] = {
|
||||||
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||||
{ "is25lp128", INFO(0x9d6018, 0, 64 * 1024, 256,
|
{ "is25lp128", INFO(0x9d6018, 0, 64 * 1024, 256,
|
||||||
SECT_4K | SPI_NOR_DUAL_READ) },
|
SECT_4K | SPI_NOR_DUAL_READ) },
|
||||||
|
{ "is25lp256", INFO(0x9d6019, 0, 64 * 1024, 512,
|
||||||
|
SECT_4K | SPI_NOR_DUAL_READ) },
|
||||||
|
{ "is25wp032", INFO(0x9d7016, 0, 64 * 1024, 64,
|
||||||
|
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||||
|
{ "is25wp064", INFO(0x9d7017, 0, 64 * 1024, 128,
|
||||||
|
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||||
|
{ "is25wp128", INFO(0x9d7018, 0, 64 * 1024, 256,
|
||||||
|
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||||
|
|
||||||
/* Macronix */
|
/* Macronix */
|
||||||
{ "mx25l512e", INFO(0xc22010, 0, 64 * 1024, 1, SECT_4K) },
|
{ "mx25l512e", INFO(0xc22010, 0, 64 * 1024, 1, SECT_4K) },
|
||||||
|
@ -1087,6 +1110,7 @@ static const struct flash_info spi_nor_ids[] = {
|
||||||
{ "n25q512ax3", INFO(0x20ba20, 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) },
|
{ "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) },
|
{ "n25q00a", INFO(0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
|
||||||
|
{ "mt25qu02g", INFO(0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
|
||||||
|
|
||||||
/* PMC */
|
/* PMC */
|
||||||
{ "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) },
|
{ "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) },
|
||||||
|
@ -1198,6 +1222,11 @@ static const struct flash_info spi_nor_ids[] = {
|
||||||
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
|
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
|
||||||
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
|
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"w25q32jv", INFO(0xef7016, 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) },
|
{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
|
||||||
{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
|
{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
|
||||||
{
|
{
|
||||||
|
@ -1230,6 +1259,10 @@ static const struct flash_info spi_nor_ids[] = {
|
||||||
{ "3S400AN", S3AN_INFO(0x1f2400, 256, 264) },
|
{ "3S400AN", S3AN_INFO(0x1f2400, 256, 264) },
|
||||||
{ "3S700AN", S3AN_INFO(0x1f2500, 512, 264) },
|
{ "3S700AN", S3AN_INFO(0x1f2500, 512, 264) },
|
||||||
{ "3S1400AN", S3AN_INFO(0x1f2600, 512, 528) },
|
{ "3S1400AN", S3AN_INFO(0x1f2600, 512, 528) },
|
||||||
|
|
||||||
|
/* XMC (Wuhan Xinxin Semiconductor Manufacturing Corp.) */
|
||||||
|
{ "XM25QH64A", INFO(0x207017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||||
|
{ "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -656,7 +656,7 @@ static int stm32_qspi_probe(struct platform_device *pdev)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
rstc = devm_reset_control_get(dev, NULL);
|
rstc = devm_reset_control_get_exclusive(dev, NULL);
|
||||||
if (!IS_ERR(rstc)) {
|
if (!IS_ERR(rstc)) {
|
||||||
reset_control_assert(rstc);
|
reset_control_assert(rstc);
|
||||||
udelay(2);
|
udelay(2);
|
||||||
|
|
|
@ -62,6 +62,8 @@
|
||||||
#define SPINOR_OP_RDCR 0x35 /* Read configuration register */
|
#define SPINOR_OP_RDCR 0x35 /* Read configuration register */
|
||||||
#define SPINOR_OP_RDFSR 0x70 /* Read flag status register */
|
#define SPINOR_OP_RDFSR 0x70 /* Read flag status register */
|
||||||
#define SPINOR_OP_CLFSR 0x50 /* Clear flag status register */
|
#define SPINOR_OP_CLFSR 0x50 /* Clear flag status register */
|
||||||
|
#define SPINOR_OP_RDEAR 0xc8 /* Read Extended Address Register */
|
||||||
|
#define SPINOR_OP_WREAR 0xc5 /* Write Extended Address Register */
|
||||||
|
|
||||||
/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
|
/* 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_4B 0x13 /* Read data bytes (low frequency) */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче