This pull-request contains the following notable changes:
Core changes: * Introduce system power management support. * New mechanism to select the proper .quad_enable() hook by JEDEC ID, when needed, instead of only by manufacturer ID. * Add support to new memory parts from Gigadevice, Winbond, Macronix and Everspin. Driver changes: * Maintainance for Cadence, Intel, Mediatek and STM32 drivers. -----BEGIN PGP SIGNATURE----- iQI4BAABCAAiBQJZ+eGDGxxjeXJpbGxlLnBpdGNoZW5Ad2VkZXY0dS5mcgAKCRDn 4OgLHRpJcozND/wK57QO0ZQXU42j6fMk5BM6Aj/YoKJC1jDsX7rRIjUifiydKQ4+ g5yQ4GzUXGMNA/oGJuy4vANmRIMWlvgBhkA+yCE8GnxM5s+RXhtfHKYsRk6pPdXA 6obVCo1eY9lZd/clBBnjAreD4bM94fWjZqupwvJDKMnWQhAvA6FC8kRQsyU0ZpvV RUH8AMY9Pf9F6rZ+3YxFvqov4xDHdH5BhQ9ZjjmPs1kV56rPS+xoqN3S8gIQR/Zk zVMtZ+XBl/n47gEL827GP1ZA42i9+fhWqBJ4PaIWJFPYtvGYdoId0NXTpPyzcMrn Ox93PXR+FSZHFill7FIdJ2qam6eBZrhuhXotKIFthe8uWauE9UweTFn0bKlgwXRI 5bLi9B4VXCYSS3PDEiDg+ohabaJBYfmIingBcr8PK+0vMdRN+A0ovKPkm3Et4dy3 wcPXuSsVOziwqgYHLvAWjo0M3hN9L9Nm+RTs6izhWunW6vTCzfOABBAT9+a6a7Fp v3XNJwV1NxxU9Mcj/LKgIXgQl8r+YAUHHjn6yBbdwh0iWcNRVBqpkaUygXogAJMi 6qeN0znLc3k4k05Vsv/oAm3h/f+dY6yVslXJTVhjWDzC4z3pywRyAfSzMai+Ai1f YFzz19abzJ/FvHMVG8jeDjpn0nk3s14IClK9wFcQfMPS4GUyCyWF6MAGTw== =sCfc -----END PGP SIGNATURE----- Merge tag 'spi-nor/for-4.15' of git://git.infradead.org/l2-mtd This pull-request contains the following notable changes: From Cyrille: " Core changes: * Introduce system power management support. * New mechanism to select the proper .quad_enable() hook by JEDEC ID, when needed, instead of only by manufacturer ID. * Add support to new memory parts from Gigadevice, Winbond, Macronix and Everspin. Driver changes: * Maintainance for Cadence, Intel, Mediatek and STM32 drivers. "
This commit is contained in:
Коммит
20b2fc79a2
|
@ -1,7 +1,9 @@
|
|||
* Cadence Quad SPI controller
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "cdns,qspi-nor".
|
||||
- compatible : should be one of the following:
|
||||
Generic default - "cdns,qspi-nor".
|
||||
For TI 66AK2G SoC - "ti,k2g-qspi", "cdns,qspi-nor".
|
||||
- reg : Contains two entries, each of which is a tuple consisting of a
|
||||
physical address and length. The first entry is the address and
|
||||
length of the controller register set. The second entry is the
|
||||
|
@ -14,6 +16,9 @@ Required properties:
|
|||
|
||||
Optional properties:
|
||||
- cdns,is-decoded-cs : Flag to indicate whether decoder is used or not.
|
||||
- cdns,rclk-en : Flag to indicate that QSPI return clock is used to latch
|
||||
the read data rather than the QSPI clock. Make sure that QSPI return
|
||||
clock is populated on the board before using this property.
|
||||
|
||||
Optional subnodes:
|
||||
Subnodes of the Cadence Quad SPI controller are spi slave nodes with additional
|
||||
|
|
|
@ -13,6 +13,7 @@ Required properties:
|
|||
at25df321a
|
||||
at25df641
|
||||
at26df081a
|
||||
mr25h128
|
||||
mr25h256
|
||||
mr25h10
|
||||
mr25h40
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
* Serial NOR flash controller for MTK MT81xx (and similar)
|
||||
|
||||
Required properties:
|
||||
- compatible: The possible values are:
|
||||
"mediatek,mt2701-nor"
|
||||
"mediatek,mt7623-nor"
|
||||
- compatible: For mt8173, compatible should be "mediatek,mt8173-nor",
|
||||
and it's the fallback compatible for other Soc.
|
||||
For every other SoC, should contain both the SoC-specific compatible
|
||||
string and "mediatek,mt8173-nor".
|
||||
The possible values are:
|
||||
"mediatek,mt2701-nor", "mediatek,mt8173-nor"
|
||||
"mediatek,mt2712-nor", "mediatek,mt8173-nor"
|
||||
"mediatek,mt7622-nor", "mediatek,mt8173-nor"
|
||||
"mediatek,mt7623-nor", "mediatek,mt8173-nor"
|
||||
"mediatek,mt8173-nor"
|
||||
For mt8173, compatible should be "mediatek,mt8173-nor".
|
||||
For every other SoC, should contain both the SoC-specific compatible string
|
||||
and "mediatek,mt8173-nor".
|
||||
- reg: physical base address and length of the controller's register
|
||||
- clocks: the phandle of the clocks needed by the nor controller
|
||||
- clock-names: the names of the clocks
|
||||
|
|
|
@ -359,6 +359,7 @@ static const struct spi_device_id m25p_ids[] = {
|
|||
{"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 */
|
||||
|
|
|
@ -50,7 +50,7 @@ config SPI_ATMEL_QUADSPI
|
|||
|
||||
config SPI_CADENCE_QUADSPI
|
||||
tristate "Cadence Quad SPI controller"
|
||||
depends on OF && (ARM || COMPILE_TEST)
|
||||
depends on OF && (ARM || ARM64 || COMPILE_TEST)
|
||||
help
|
||||
Enable support for the Cadence Quad SPI Flash controller.
|
||||
|
||||
|
@ -90,7 +90,7 @@ config SPI_INTEL_SPI
|
|||
tristate
|
||||
|
||||
config SPI_INTEL_SPI_PCI
|
||||
tristate "Intel PCH/PCU SPI flash PCI driver" if EXPERT
|
||||
tristate "Intel PCH/PCU SPI flash PCI driver"
|
||||
depends on X86 && PCI
|
||||
select SPI_INTEL_SPI
|
||||
help
|
||||
|
@ -106,7 +106,7 @@ config SPI_INTEL_SPI_PCI
|
|||
will be called intel-spi-pci.
|
||||
|
||||
config SPI_INTEL_SPI_PLATFORM
|
||||
tristate "Intel PCH/PCU SPI flash platform driver" if EXPERT
|
||||
tristate "Intel PCH/PCU SPI flash platform driver"
|
||||
depends on X86
|
||||
select SPI_INTEL_SPI
|
||||
help
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/timer.h>
|
||||
|
@ -38,6 +39,9 @@
|
|||
#define CQSPI_NAME "cadence-qspi"
|
||||
#define CQSPI_MAX_CHIPSELECT 16
|
||||
|
||||
/* Quirks */
|
||||
#define CQSPI_NEEDS_WR_DELAY BIT(0)
|
||||
|
||||
struct cqspi_st;
|
||||
|
||||
struct cqspi_flash_pdata {
|
||||
|
@ -75,7 +79,9 @@ struct cqspi_st {
|
|||
bool is_decoded_cs;
|
||||
u32 fifo_depth;
|
||||
u32 fifo_width;
|
||||
bool rclk_en;
|
||||
u32 trigger_address;
|
||||
u32 wr_delay;
|
||||
struct cqspi_flash_pdata f_pdata[CQSPI_MAX_CHIPSELECT];
|
||||
};
|
||||
|
||||
|
@ -608,6 +614,15 @@ static int cqspi_indirect_write_execute(struct spi_nor *nor,
|
|||
reinit_completion(&cqspi->transfer_complete);
|
||||
writel(CQSPI_REG_INDIRECTWR_START_MASK,
|
||||
reg_base + CQSPI_REG_INDIRECTWR);
|
||||
/*
|
||||
* As per 66AK2G02 TRM SPRUHY8F section 11.15.5.3 Indirect Access
|
||||
* Controller programming sequence, couple of cycles of
|
||||
* QSPI_REF_CLK delay is required for the above bit to
|
||||
* be internally synchronized by the QSPI module. Provide 5
|
||||
* cycles of delay.
|
||||
*/
|
||||
if (cqspi->wr_delay)
|
||||
ndelay(cqspi->wr_delay);
|
||||
|
||||
while (remaining > 0) {
|
||||
write_bytes = remaining > page_size ? page_size : remaining;
|
||||
|
@ -775,7 +790,7 @@ static void cqspi_config_baudrate_div(struct cqspi_st *cqspi)
|
|||
}
|
||||
|
||||
static void cqspi_readdata_capture(struct cqspi_st *cqspi,
|
||||
const unsigned int bypass,
|
||||
const bool bypass,
|
||||
const unsigned int delay)
|
||||
{
|
||||
void __iomem *reg_base = cqspi->iobase;
|
||||
|
@ -839,7 +854,8 @@ static void cqspi_configure(struct spi_nor *nor)
|
|||
cqspi->sclk = sclk;
|
||||
cqspi_config_baudrate_div(cqspi);
|
||||
cqspi_delay(nor);
|
||||
cqspi_readdata_capture(cqspi, 1, f_pdata->read_delay);
|
||||
cqspi_readdata_capture(cqspi, !cqspi->rclk_en,
|
||||
f_pdata->read_delay);
|
||||
}
|
||||
|
||||
if (switch_cs || switch_ck)
|
||||
|
@ -1036,6 +1052,8 @@ static int cqspi_of_get_pdata(struct platform_device *pdev)
|
|||
return -ENXIO;
|
||||
}
|
||||
|
||||
cqspi->rclk_en = of_property_read_bool(np, "cdns,rclk-en");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1156,6 +1174,7 @@ static int cqspi_probe(struct platform_device *pdev)
|
|||
struct cqspi_st *cqspi;
|
||||
struct resource *res;
|
||||
struct resource *res_ahb;
|
||||
unsigned long data;
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
|
@ -1206,13 +1225,24 @@ static int cqspi_probe(struct platform_device *pdev)
|
|||
return -ENXIO;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(cqspi->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot enable QSPI clock.\n");
|
||||
pm_runtime_enable(dev);
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(cqspi->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot enable QSPI clock.\n");
|
||||
goto probe_clk_failed;
|
||||
}
|
||||
|
||||
cqspi->master_ref_clk_hz = clk_get_rate(cqspi->clk);
|
||||
data = (unsigned long)of_device_get_match_data(dev);
|
||||
if (data & CQSPI_NEEDS_WR_DELAY)
|
||||
cqspi->wr_delay = 5 * DIV_ROUND_UP(NSEC_PER_SEC,
|
||||
cqspi->master_ref_clk_hz);
|
||||
|
||||
ret = devm_request_irq(dev, irq, cqspi_irq_handler, 0,
|
||||
pdev->name, cqspi);
|
||||
|
@ -1233,10 +1263,13 @@ static int cqspi_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
return ret;
|
||||
probe_irq_failed:
|
||||
cqspi_controller_enable(cqspi, 0);
|
||||
probe_setup_failed:
|
||||
cqspi_controller_enable(cqspi, 0);
|
||||
probe_irq_failed:
|
||||
clk_disable_unprepare(cqspi->clk);
|
||||
probe_clk_failed:
|
||||
pm_runtime_put_sync(dev);
|
||||
pm_runtime_disable(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1253,6 +1286,9 @@ static int cqspi_remove(struct platform_device *pdev)
|
|||
|
||||
clk_disable_unprepare(cqspi->clk);
|
||||
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1284,7 +1320,14 @@ static const struct dev_pm_ops cqspi__dev_pm_ops = {
|
|||
#endif
|
||||
|
||||
static const struct of_device_id cqspi_dt_ids[] = {
|
||||
{.compatible = "cdns,qspi-nor",},
|
||||
{
|
||||
.compatible = "cdns,qspi-nor",
|
||||
.data = (void *)0,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,k2g-qspi",
|
||||
.data = (void *)CQSPI_NEEDS_WR_DELAY,
|
||||
},
|
||||
{ /* end of table */ }
|
||||
};
|
||||
|
||||
|
|
|
@ -63,7 +63,10 @@ static void intel_spi_pci_remove(struct pci_dev *pdev)
|
|||
}
|
||||
|
||||
static const struct pci_device_id intel_spi_pci_ids[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x18e0), (unsigned long)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x19e0), (unsigned long)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0xa1a4), (unsigned long)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0xa224), (unsigned long)&bxt_info },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, intel_spi_pci_ids);
|
||||
|
|
|
@ -67,8 +67,6 @@
|
|||
#define PR_LIMIT_MASK (0x3fff << PR_LIMIT_SHIFT)
|
||||
#define PR_RPE BIT(15)
|
||||
#define PR_BASE_MASK 0x3fff
|
||||
/* Last PR is GPR0 */
|
||||
#define PR_NUM (5 + 1)
|
||||
|
||||
/* Offsets are from @ispi->sregs */
|
||||
#define SSFSTS_CTL 0x00
|
||||
|
@ -90,20 +88,35 @@
|
|||
#define OPMENU0 0x08
|
||||
#define OPMENU1 0x0c
|
||||
|
||||
#define OPTYPE_READ_NO_ADDR 0
|
||||
#define OPTYPE_WRITE_NO_ADDR 1
|
||||
#define OPTYPE_READ_WITH_ADDR 2
|
||||
#define OPTYPE_WRITE_WITH_ADDR 3
|
||||
|
||||
/* CPU specifics */
|
||||
#define BYT_PR 0x74
|
||||
#define BYT_SSFSTS_CTL 0x90
|
||||
#define BYT_BCR 0xfc
|
||||
#define BYT_BCR_WPD BIT(0)
|
||||
#define BYT_FREG_NUM 5
|
||||
#define BYT_PR_NUM 5
|
||||
|
||||
#define LPT_PR 0x74
|
||||
#define LPT_SSFSTS_CTL 0x90
|
||||
#define LPT_FREG_NUM 5
|
||||
#define LPT_PR_NUM 5
|
||||
|
||||
#define BXT_PR 0x84
|
||||
#define BXT_SSFSTS_CTL 0xa0
|
||||
#define BXT_FREG_NUM 12
|
||||
#define BXT_PR_NUM 6
|
||||
|
||||
#define LVSCC 0xc4
|
||||
#define UVSCC 0xc8
|
||||
#define ERASE_OPCODE_SHIFT 8
|
||||
#define ERASE_OPCODE_MASK (0xff << ERASE_OPCODE_SHIFT)
|
||||
#define ERASE_64K_OPCODE_SHIFT 16
|
||||
#define ERASE_64K_OPCODE_MASK (0xff << ERASE_OPCODE_SHIFT)
|
||||
|
||||
#define INTEL_SPI_TIMEOUT 5000 /* ms */
|
||||
#define INTEL_SPI_FIFO_SZ 64
|
||||
|
@ -117,8 +130,11 @@
|
|||
* @pregs: Start of protection registers
|
||||
* @sregs: Start of software sequencer registers
|
||||
* @nregions: Maximum number of regions
|
||||
* @pr_num: Maximum number of protected range registers
|
||||
* @writeable: Is the chip writeable
|
||||
* @swseq: Use SW sequencer in register reads/writes
|
||||
* @locked: Is SPI setting locked
|
||||
* @swseq_reg: Use SW sequencer in register reads/writes
|
||||
* @swseq_erase: Use SW sequencer in erase operation
|
||||
* @erase_64k: 64k erase supported
|
||||
* @opcodes: Opcodes which are supported. This are programmed by BIOS
|
||||
* before it locks down the controller.
|
||||
|
@ -132,8 +148,11 @@ struct intel_spi {
|
|||
void __iomem *pregs;
|
||||
void __iomem *sregs;
|
||||
size_t nregions;
|
||||
size_t pr_num;
|
||||
bool writeable;
|
||||
bool swseq;
|
||||
bool locked;
|
||||
bool swseq_reg;
|
||||
bool swseq_erase;
|
||||
bool erase_64k;
|
||||
u8 opcodes[8];
|
||||
u8 preopcodes[2];
|
||||
|
@ -167,7 +186,7 @@ static void intel_spi_dump_regs(struct intel_spi *ispi)
|
|||
for (i = 0; i < ispi->nregions; i++)
|
||||
dev_dbg(ispi->dev, "FREG(%d)=0x%08x\n", i,
|
||||
readl(ispi->base + FREG(i)));
|
||||
for (i = 0; i < PR_NUM; i++)
|
||||
for (i = 0; i < ispi->pr_num; i++)
|
||||
dev_dbg(ispi->dev, "PR(%d)=0x%08x\n", i,
|
||||
readl(ispi->pregs + PR(i)));
|
||||
|
||||
|
@ -181,8 +200,11 @@ static void intel_spi_dump_regs(struct intel_spi *ispi)
|
|||
if (ispi->info->type == INTEL_SPI_BYT)
|
||||
dev_dbg(ispi->dev, "BCR=0x%08x\n", readl(ispi->base + BYT_BCR));
|
||||
|
||||
dev_dbg(ispi->dev, "LVSCC=0x%08x\n", readl(ispi->base + LVSCC));
|
||||
dev_dbg(ispi->dev, "UVSCC=0x%08x\n", readl(ispi->base + UVSCC));
|
||||
|
||||
dev_dbg(ispi->dev, "Protected regions:\n");
|
||||
for (i = 0; i < PR_NUM; i++) {
|
||||
for (i = 0; i < ispi->pr_num; i++) {
|
||||
u32 base, limit;
|
||||
|
||||
value = readl(ispi->pregs + PR(i));
|
||||
|
@ -214,7 +236,9 @@ static void intel_spi_dump_regs(struct intel_spi *ispi)
|
|||
}
|
||||
|
||||
dev_dbg(ispi->dev, "Using %cW sequencer for register access\n",
|
||||
ispi->swseq ? 'S' : 'H');
|
||||
ispi->swseq_reg ? 'S' : 'H');
|
||||
dev_dbg(ispi->dev, "Using %cW sequencer for erase operation\n",
|
||||
ispi->swseq_erase ? 'S' : 'H');
|
||||
}
|
||||
|
||||
/* Reads max INTEL_SPI_FIFO_SZ bytes from the device fifo */
|
||||
|
@ -278,7 +302,7 @@ static int intel_spi_wait_sw_busy(struct intel_spi *ispi)
|
|||
|
||||
static int intel_spi_init(struct intel_spi *ispi)
|
||||
{
|
||||
u32 opmenu0, opmenu1, val;
|
||||
u32 opmenu0, opmenu1, lvscc, uvscc, val;
|
||||
int i;
|
||||
|
||||
switch (ispi->info->type) {
|
||||
|
@ -286,6 +310,8 @@ static int intel_spi_init(struct intel_spi *ispi)
|
|||
ispi->sregs = ispi->base + BYT_SSFSTS_CTL;
|
||||
ispi->pregs = ispi->base + BYT_PR;
|
||||
ispi->nregions = BYT_FREG_NUM;
|
||||
ispi->pr_num = BYT_PR_NUM;
|
||||
ispi->swseq_reg = true;
|
||||
|
||||
if (writeable) {
|
||||
/* Disable write protection */
|
||||
|
@ -305,12 +331,15 @@ static int intel_spi_init(struct intel_spi *ispi)
|
|||
ispi->sregs = ispi->base + LPT_SSFSTS_CTL;
|
||||
ispi->pregs = ispi->base + LPT_PR;
|
||||
ispi->nregions = LPT_FREG_NUM;
|
||||
ispi->pr_num = LPT_PR_NUM;
|
||||
ispi->swseq_reg = true;
|
||||
break;
|
||||
|
||||
case INTEL_SPI_BXT:
|
||||
ispi->sregs = ispi->base + BXT_SSFSTS_CTL;
|
||||
ispi->pregs = ispi->base + BXT_PR;
|
||||
ispi->nregions = BXT_FREG_NUM;
|
||||
ispi->pr_num = BXT_PR_NUM;
|
||||
ispi->erase_64k = true;
|
||||
break;
|
||||
|
||||
|
@ -318,42 +347,64 @@ static int intel_spi_init(struct intel_spi *ispi)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Disable #SMI generation */
|
||||
/* Disable #SMI generation from HW sequencer */
|
||||
val = readl(ispi->base + HSFSTS_CTL);
|
||||
val &= ~HSFSTS_CTL_FSMIE;
|
||||
writel(val, ispi->base + HSFSTS_CTL);
|
||||
|
||||
/*
|
||||
* BIOS programs allowed opcodes and then locks down the register.
|
||||
* So read back what opcodes it decided to support. That's the set
|
||||
* we are going to support as well.
|
||||
* Determine whether erase operation should use HW or SW sequencer.
|
||||
*
|
||||
* The HW sequencer has a predefined list of opcodes, with only the
|
||||
* erase opcode being programmable in LVSCC and UVSCC registers.
|
||||
* If these registers don't contain a valid erase opcode, erase
|
||||
* cannot be done using HW sequencer.
|
||||
*/
|
||||
opmenu0 = readl(ispi->sregs + OPMENU0);
|
||||
opmenu1 = readl(ispi->sregs + OPMENU1);
|
||||
lvscc = readl(ispi->base + LVSCC);
|
||||
uvscc = readl(ispi->base + UVSCC);
|
||||
if (!(lvscc & ERASE_OPCODE_MASK) || !(uvscc & ERASE_OPCODE_MASK))
|
||||
ispi->swseq_erase = true;
|
||||
/* SPI controller on Intel BXT supports 64K erase opcode */
|
||||
if (ispi->info->type == INTEL_SPI_BXT && !ispi->swseq_erase)
|
||||
if (!(lvscc & ERASE_64K_OPCODE_MASK) ||
|
||||
!(uvscc & ERASE_64K_OPCODE_MASK))
|
||||
ispi->erase_64k = false;
|
||||
|
||||
/*
|
||||
* Some controllers can only do basic operations using hardware
|
||||
* sequencer. All other operations are supposed to be carried out
|
||||
* using software sequencer. If we find that BIOS has programmed
|
||||
* opcodes for the software sequencer we use that over the hardware
|
||||
* sequencer.
|
||||
* using software sequencer.
|
||||
*/
|
||||
if (opmenu0 && opmenu1) {
|
||||
for (i = 0; i < ARRAY_SIZE(ispi->opcodes) / 2; i++) {
|
||||
ispi->opcodes[i] = opmenu0 >> i * 8;
|
||||
ispi->opcodes[i + 4] = opmenu1 >> i * 8;
|
||||
}
|
||||
|
||||
val = readl(ispi->sregs + PREOP_OPTYPE);
|
||||
ispi->preopcodes[0] = val;
|
||||
ispi->preopcodes[1] = val >> 8;
|
||||
|
||||
if (ispi->swseq_reg) {
|
||||
/* Disable #SMI generation from SW sequencer */
|
||||
val = readl(ispi->sregs + SSFSTS_CTL);
|
||||
val &= ~SSFSTS_CTL_FSMIE;
|
||||
writel(val, ispi->sregs + SSFSTS_CTL);
|
||||
}
|
||||
|
||||
ispi->swseq = true;
|
||||
/* Check controller's lock status */
|
||||
val = readl(ispi->base + HSFSTS_CTL);
|
||||
ispi->locked = !!(val & HSFSTS_CTL_FLOCKDN);
|
||||
|
||||
if (ispi->locked) {
|
||||
/*
|
||||
* BIOS programs allowed opcodes and then locks down the
|
||||
* register. So read back what opcodes it decided to support.
|
||||
* That's the set we are going to support as well.
|
||||
*/
|
||||
opmenu0 = readl(ispi->sregs + OPMENU0);
|
||||
opmenu1 = readl(ispi->sregs + OPMENU1);
|
||||
|
||||
if (opmenu0 && opmenu1) {
|
||||
for (i = 0; i < ARRAY_SIZE(ispi->opcodes) / 2; i++) {
|
||||
ispi->opcodes[i] = opmenu0 >> i * 8;
|
||||
ispi->opcodes[i + 4] = opmenu1 >> i * 8;
|
||||
}
|
||||
|
||||
val = readl(ispi->sregs + PREOP_OPTYPE);
|
||||
ispi->preopcodes[0] = val;
|
||||
ispi->preopcodes[1] = val >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
intel_spi_dump_regs(ispi);
|
||||
|
@ -361,18 +412,28 @@ static int intel_spi_init(struct intel_spi *ispi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int intel_spi_opcode_index(struct intel_spi *ispi, u8 opcode)
|
||||
static int intel_spi_opcode_index(struct intel_spi *ispi, u8 opcode, int optype)
|
||||
{
|
||||
int i;
|
||||
int preop;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ispi->opcodes); i++)
|
||||
if (ispi->opcodes[i] == opcode)
|
||||
return i;
|
||||
return -EINVAL;
|
||||
if (ispi->locked) {
|
||||
for (i = 0; i < ARRAY_SIZE(ispi->opcodes); i++)
|
||||
if (ispi->opcodes[i] == opcode)
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* The lock is off, so just use index 0 */
|
||||
writel(opcode, ispi->sregs + OPMENU0);
|
||||
preop = readw(ispi->sregs + PREOP_OPTYPE);
|
||||
writel(optype << 16 | preop, ispi->sregs + PREOP_OPTYPE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, u8 *buf,
|
||||
int len)
|
||||
static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, int len)
|
||||
{
|
||||
u32 val, status;
|
||||
int ret;
|
||||
|
@ -394,6 +455,9 @@ static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, u8 *buf,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (len > INTEL_SPI_FIFO_SZ)
|
||||
return -EINVAL;
|
||||
|
||||
val |= (len - 1) << HSFSTS_CTL_FDBC_SHIFT;
|
||||
val |= HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE;
|
||||
val |= HSFSTS_CTL_FGO;
|
||||
|
@ -412,27 +476,39 @@ static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, u8 *buf,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, u8 *buf,
|
||||
int len)
|
||||
static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, int len,
|
||||
int optype)
|
||||
{
|
||||
u32 val, status;
|
||||
u32 val = 0, status;
|
||||
u16 preop;
|
||||
int ret;
|
||||
|
||||
ret = intel_spi_opcode_index(ispi, opcode);
|
||||
ret = intel_spi_opcode_index(ispi, opcode, optype);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val = (len << SSFSTS_CTL_DBC_SHIFT) | SSFSTS_CTL_DS;
|
||||
if (len > INTEL_SPI_FIFO_SZ)
|
||||
return -EINVAL;
|
||||
|
||||
/* Only mark 'Data Cycle' bit when there is data to be transferred */
|
||||
if (len > 0)
|
||||
val = ((len - 1) << SSFSTS_CTL_DBC_SHIFT) | SSFSTS_CTL_DS;
|
||||
val |= ret << SSFSTS_CTL_COP_SHIFT;
|
||||
val |= SSFSTS_CTL_FCERR | SSFSTS_CTL_FDONE;
|
||||
val |= SSFSTS_CTL_SCGO;
|
||||
preop = readw(ispi->sregs + PREOP_OPTYPE);
|
||||
if (preop) {
|
||||
val |= SSFSTS_CTL_ACS;
|
||||
if (preop >> 8)
|
||||
val |= SSFSTS_CTL_SPOP;
|
||||
}
|
||||
writel(val, ispi->sregs + SSFSTS_CTL);
|
||||
|
||||
ret = intel_spi_wait_sw_busy(ispi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
status = readl(ispi->base + SSFSTS_CTL);
|
||||
status = readl(ispi->sregs + SSFSTS_CTL);
|
||||
if (status & SSFSTS_CTL_FCERR)
|
||||
return -EIO;
|
||||
else if (status & SSFSTS_CTL_AEL)
|
||||
|
@ -449,10 +525,11 @@ static int intel_spi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
|
|||
/* Address of the first chip */
|
||||
writel(0, ispi->base + FADDR);
|
||||
|
||||
if (ispi->swseq)
|
||||
ret = intel_spi_sw_cycle(ispi, opcode, buf, len);
|
||||
if (ispi->swseq_reg)
|
||||
ret = intel_spi_sw_cycle(ispi, opcode, len,
|
||||
OPTYPE_READ_NO_ADDR);
|
||||
else
|
||||
ret = intel_spi_hw_cycle(ispi, opcode, buf, len);
|
||||
ret = intel_spi_hw_cycle(ispi, opcode, len);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -467,10 +544,15 @@ 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
|
||||
* controller so skip it here now.
|
||||
* controller so skip it here now. If the controller is not locked,
|
||||
* program the opcode to the PREOP register for later use.
|
||||
*/
|
||||
if (opcode == SPINOR_OP_WREN)
|
||||
if (opcode == SPINOR_OP_WREN) {
|
||||
if (!ispi->locked)
|
||||
writel(opcode, ispi->sregs + PREOP_OPTYPE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
writel(0, ispi->base + FADDR);
|
||||
|
||||
|
@ -479,9 +561,10 @@ static int intel_spi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ispi->swseq)
|
||||
return intel_spi_sw_cycle(ispi, opcode, buf, len);
|
||||
return intel_spi_hw_cycle(ispi, opcode, buf, len);
|
||||
if (ispi->swseq_reg)
|
||||
return intel_spi_sw_cycle(ispi, opcode, len,
|
||||
OPTYPE_WRITE_NO_ADDR);
|
||||
return intel_spi_hw_cycle(ispi, opcode, len);
|
||||
}
|
||||
|
||||
static ssize_t intel_spi_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||
|
@ -561,12 +644,6 @@ static ssize_t intel_spi_write(struct spi_nor *nor, loff_t to, size_t len,
|
|||
val |= (block_size - 1) << HSFSTS_CTL_FDBC_SHIFT;
|
||||
val |= HSFSTS_CTL_FCYCLE_WRITE;
|
||||
|
||||
/* Write enable */
|
||||
if (ispi->preopcodes[1] == SPINOR_OP_WREN)
|
||||
val |= SSFSTS_CTL_SPOP;
|
||||
val |= SSFSTS_CTL_ACS;
|
||||
writel(val, ispi->base + HSFSTS_CTL);
|
||||
|
||||
ret = intel_spi_write_block(ispi, write_buf, block_size);
|
||||
if (ret) {
|
||||
dev_err(ispi->dev, "failed to write block\n");
|
||||
|
@ -574,8 +651,8 @@ static ssize_t intel_spi_write(struct spi_nor *nor, loff_t to, size_t len,
|
|||
}
|
||||
|
||||
/* Start the write now */
|
||||
val = readl(ispi->base + HSFSTS_CTL);
|
||||
writel(val | HSFSTS_CTL_FGO, ispi->base + HSFSTS_CTL);
|
||||
val |= HSFSTS_CTL_FGO;
|
||||
writel(val, ispi->base + HSFSTS_CTL);
|
||||
|
||||
ret = intel_spi_wait_hw_busy(ispi);
|
||||
if (ret) {
|
||||
|
@ -620,6 +697,22 @@ static int intel_spi_erase(struct spi_nor *nor, loff_t offs)
|
|||
erase_size = SZ_4K;
|
||||
}
|
||||
|
||||
if (ispi->swseq_erase) {
|
||||
while (len > 0) {
|
||||
writel(offs, ispi->base + FADDR);
|
||||
|
||||
ret = intel_spi_sw_cycle(ispi, nor->erase_opcode,
|
||||
0, OPTYPE_WRITE_WITH_ADDR);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
offs += erase_size;
|
||||
len -= erase_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (len > 0) {
|
||||
writel(offs, ispi->base + FADDR);
|
||||
|
||||
|
@ -652,7 +745,7 @@ static bool intel_spi_is_protected(const struct intel_spi *ispi,
|
|||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PR_NUM; i++) {
|
||||
for (i = 0; i < ispi->pr_num; i++) {
|
||||
u32 pr_base, pr_limit, pr_value;
|
||||
|
||||
pr_value = readl(ispi->pregs + PR(i));
|
||||
|
|
|
@ -404,6 +404,29 @@ static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void mt8173_nor_disable_clk(struct mt8173_nor *mt8173_nor)
|
||||
{
|
||||
clk_disable_unprepare(mt8173_nor->spi_clk);
|
||||
clk_disable_unprepare(mt8173_nor->nor_clk);
|
||||
}
|
||||
|
||||
static int mt8173_nor_enable_clk(struct mt8173_nor *mt8173_nor)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(mt8173_nor->spi_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(mt8173_nor->nor_clk);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(mt8173_nor->spi_clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
|
||||
struct device_node *flash_node)
|
||||
{
|
||||
|
@ -468,15 +491,11 @@ static int mtk_nor_drv_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(mt8173_nor->nor_clk);
|
||||
|
||||
mt8173_nor->dev = &pdev->dev;
|
||||
ret = clk_prepare_enable(mt8173_nor->spi_clk);
|
||||
|
||||
ret = mt8173_nor_enable_clk(mt8173_nor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(mt8173_nor->nor_clk);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(mt8173_nor->spi_clk);
|
||||
return ret;
|
||||
}
|
||||
/* only support one attached flash */
|
||||
flash_np = of_get_next_available_child(pdev->dev.of_node, NULL);
|
||||
if (!flash_np) {
|
||||
|
@ -487,10 +506,9 @@ static int mtk_nor_drv_probe(struct platform_device *pdev)
|
|||
ret = mtk_nor_init(mt8173_nor, flash_np);
|
||||
|
||||
nor_free:
|
||||
if (ret) {
|
||||
clk_disable_unprepare(mt8173_nor->spi_clk);
|
||||
clk_disable_unprepare(mt8173_nor->nor_clk);
|
||||
}
|
||||
if (ret)
|
||||
mt8173_nor_disable_clk(mt8173_nor);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -498,11 +516,38 @@ static int mtk_nor_drv_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct mt8173_nor *mt8173_nor = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(mt8173_nor->spi_clk);
|
||||
clk_disable_unprepare(mt8173_nor->nor_clk);
|
||||
mt8173_nor_disable_clk(mt8173_nor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mtk_nor_suspend(struct device *dev)
|
||||
{
|
||||
struct mt8173_nor *mt8173_nor = dev_get_drvdata(dev);
|
||||
|
||||
mt8173_nor_disable_clk(mt8173_nor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_nor_resume(struct device *dev)
|
||||
{
|
||||
struct mt8173_nor *mt8173_nor = dev_get_drvdata(dev);
|
||||
|
||||
return mt8173_nor_enable_clk(mt8173_nor);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mtk_nor_dev_pm_ops = {
|
||||
.suspend = mtk_nor_suspend,
|
||||
.resume = mtk_nor_resume,
|
||||
};
|
||||
|
||||
#define MTK_NOR_DEV_PM_OPS (&mtk_nor_dev_pm_ops)
|
||||
#else
|
||||
#define MTK_NOR_DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct of_device_id mtk_nor_of_ids[] = {
|
||||
{ .compatible = "mediatek,mt8173-nor"},
|
||||
{ /* sentinel */ }
|
||||
|
@ -514,6 +559,7 @@ static struct platform_driver mtk_nor_driver = {
|
|||
.remove = mtk_nor_drv_remove,
|
||||
.driver = {
|
||||
.name = "mtk-nor",
|
||||
.pm = MTK_NOR_DEV_PM_OPS,
|
||||
.of_match_table = mtk_nor_of_ids,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -89,6 +89,8 @@ struct flash_info {
|
|||
#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 */
|
||||
|
||||
int (*quad_enable)(struct spi_nor *nor);
|
||||
};
|
||||
|
||||
#define JEDEC_MFR(info) ((info)->id[0])
|
||||
|
@ -870,6 +872,8 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int macronix_quad_enable(struct spi_nor *nor);
|
||||
|
||||
/* Used when the "_ext_id" is two bytes at most */
|
||||
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
|
||||
.id = { \
|
||||
|
@ -964,6 +968,7 @@ static const struct flash_info spi_nor_ids[] = {
|
|||
{ "f25l64qa", INFO(0x8c4117, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_LOCK) },
|
||||
|
||||
/* Everspin */
|
||||
{ "mr25h128", CAT25_INFO( 16 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
|
||||
{ "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) },
|
||||
|
@ -982,6 +987,11 @@ static const struct flash_info spi_nor_ids[] = {
|
|||
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
|
||||
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
|
||||
},
|
||||
{
|
||||
"gd25lq32", INFO(0xc86016, 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 |
|
||||
|
@ -997,6 +1007,12 @@ static const struct flash_info spi_nor_ids[] = {
|
|||
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
|
||||
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
|
||||
},
|
||||
{
|
||||
"gd25q256", INFO(0xc84019, 0, 64 * 1024, 512,
|
||||
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
|
||||
SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
|
||||
.quad_enable = macronix_quad_enable,
|
||||
},
|
||||
|
||||
/* Intel/Numonyx -- xxxs33b */
|
||||
{ "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) },
|
||||
|
@ -1024,7 +1040,7 @@ static const struct flash_info spi_nor_ids[] = {
|
|||
{ "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) },
|
||||
{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
|
||||
{ "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) },
|
||||
|
@ -1137,6 +1153,11 @@ static const struct flash_info spi_nor_ids[] = {
|
|||
{ "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) },
|
||||
{
|
||||
"w25q16dw", INFO(0xef6015, 0, 64 * 1024, 32,
|
||||
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
|
||||
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
|
||||
},
|
||||
{ "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) },
|
||||
|
@ -2288,8 +2309,7 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
|
|||
|
||||
/* Check the SFDP header version. */
|
||||
if (le32_to_cpu(header.signature) != SFDP_SIGNATURE ||
|
||||
header.major != SFDP_JESD216_MAJOR ||
|
||||
header.minor < SFDP_JESD216_MINOR)
|
||||
header.major != SFDP_JESD216_MAJOR)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
|
@ -2427,6 +2447,15 @@ static int spi_nor_init_params(struct spi_nor *nor,
|
|||
params->quad_enable = spansion_quad_enable;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some manufacturer like GigaDevice may use different
|
||||
* bit to set QE on different memories, so the MFR can't
|
||||
* indicate the quad_enable method for this case, we need
|
||||
* set it in flash info list.
|
||||
*/
|
||||
if (info->quad_enable)
|
||||
params->quad_enable = info->quad_enable;
|
||||
}
|
||||
|
||||
/* Override the parameters with data read from SFDP tables. */
|
||||
|
@ -2630,17 +2659,60 @@ static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
|
|||
/* 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 (enable_quad_io && params->quad_enable)
|
||||
nor->quad_enable = params->quad_enable;
|
||||
else
|
||||
nor->quad_enable = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_nor_init(struct spi_nor *nor)
|
||||
{
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up
|
||||
* with the software protection bits set
|
||||
*/
|
||||
if (JEDEC_MFR(nor->info) == SNOR_MFR_ATMEL ||
|
||||
JEDEC_MFR(nor->info) == SNOR_MFR_INTEL ||
|
||||
JEDEC_MFR(nor->info) == SNOR_MFR_SST ||
|
||||
nor->info->flags & SPI_NOR_HAS_LOCK) {
|
||||
write_enable(nor);
|
||||
write_sr(nor, 0);
|
||||
spi_nor_wait_till_ready(nor);
|
||||
}
|
||||
|
||||
if (nor->quad_enable) {
|
||||
err = nor->quad_enable(nor);
|
||||
if (err) {
|
||||
dev_err(nor->dev, "quad mode not supported\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if ((nor->addr_width == 4) &&
|
||||
(JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) &&
|
||||
!(nor->info->flags & SPI_NOR_4B_OPCODES))
|
||||
set_4byte(nor, nor->info, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* mtd resume handler */
|
||||
static void spi_nor_resume(struct mtd_info *mtd)
|
||||
{
|
||||
struct spi_nor *nor = mtd_to_spi_nor(mtd);
|
||||
struct device *dev = nor->dev;
|
||||
int ret;
|
||||
|
||||
/* re-initialize the nor chip */
|
||||
ret = spi_nor_init(nor);
|
||||
if (ret)
|
||||
dev_err(dev, "resume() failed\n");
|
||||
}
|
||||
|
||||
int spi_nor_scan(struct spi_nor *nor, const char *name,
|
||||
const struct spi_nor_hwcaps *hwcaps)
|
||||
{
|
||||
|
@ -2708,20 +2780,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
|
|||
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;
|
||||
|
@ -2731,6 +2789,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
|
|||
mtd->size = params.size;
|
||||
mtd->_erase = spi_nor_erase;
|
||||
mtd->_read = spi_nor_read;
|
||||
mtd->_resume = spi_nor_resume;
|
||||
|
||||
/* NOR protection support for STmicro/Micron chips and similar */
|
||||
if (JEDEC_MFR(info) == SNOR_MFR_MICRON ||
|
||||
|
@ -2804,8 +2863,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
|
|||
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);
|
||||
} else {
|
||||
nor->addr_width = 3;
|
||||
}
|
||||
|
@ -2822,6 +2879,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Send all the required SPI flash commands to initialize device */
|
||||
nor->info = info;
|
||||
ret = spi_nor_init(nor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(dev, "%s (%lld Kbytes)\n", info->name,
|
||||
(long long)mtd->size >> 10);
|
||||
|
||||
|
|
|
@ -1,9 +1,22 @@
|
|||
/*
|
||||
* stm32_quadspi.c
|
||||
* Driver for stm32 quadspi controller
|
||||
*
|
||||
* Copyright (C) 2017, Ludovic Barre
|
||||
* Copyright (C) 2017, STMicroelectronics - All Rights Reserved
|
||||
* Author(s): Ludovic Barre author <ludovic.barre@st.com>.
|
||||
*
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
* License terms: GPL V2.0.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* This program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/errno.h>
|
||||
|
@ -113,6 +126,7 @@
|
|||
#define STM32_MAX_MMAP_SZ SZ_256M
|
||||
#define STM32_MAX_NORCHIP 2
|
||||
|
||||
#define STM32_QSPI_FIFO_SZ 32
|
||||
#define STM32_QSPI_FIFO_TIMEOUT_US 30000
|
||||
#define STM32_QSPI_BUSY_TIMEOUT_US 100000
|
||||
|
||||
|
@ -124,6 +138,7 @@ struct stm32_qspi_flash {
|
|||
u32 presc;
|
||||
u32 read_mode;
|
||||
bool registered;
|
||||
u32 prefetch_limit;
|
||||
};
|
||||
|
||||
struct stm32_qspi {
|
||||
|
@ -240,12 +255,12 @@ static int stm32_qspi_tx_poll(struct stm32_qspi *qspi,
|
|||
STM32_QSPI_FIFO_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(qspi->dev, "fifo timeout (stat:%#x)\n", sr);
|
||||
break;
|
||||
return ret;
|
||||
}
|
||||
tx_fifo(buf++, qspi->io_base + QUADSPI_DR);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_qspi_tx_mm(struct stm32_qspi *qspi,
|
||||
|
@ -272,6 +287,7 @@ static int stm32_qspi_send(struct stm32_qspi_flash *flash,
|
|||
{
|
||||
struct stm32_qspi *qspi = flash->qspi;
|
||||
u32 ccr, dcr, cr;
|
||||
u32 last_byte;
|
||||
int err;
|
||||
|
||||
err = stm32_qspi_wait_nobusy(qspi);
|
||||
|
@ -314,6 +330,10 @@ static int stm32_qspi_send(struct stm32_qspi_flash *flash,
|
|||
if (err)
|
||||
goto abort;
|
||||
writel_relaxed(FCR_CTCF, qspi->io_base + QUADSPI_FCR);
|
||||
} else {
|
||||
last_byte = cmd->addr + cmd->len;
|
||||
if (last_byte > flash->prefetch_limit)
|
||||
goto abort;
|
||||
}
|
||||
|
||||
return err;
|
||||
|
@ -322,7 +342,9 @@ abort:
|
|||
cr = readl_relaxed(qspi->io_base + QUADSPI_CR) | CR_ABORT;
|
||||
writel_relaxed(cr, qspi->io_base + QUADSPI_CR);
|
||||
|
||||
dev_err(qspi->dev, "%s abort err:%d\n", __func__, err);
|
||||
if (err)
|
||||
dev_err(qspi->dev, "%s abort err:%d\n", __func__, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -550,6 +572,7 @@ static int stm32_qspi_flash_setup(struct stm32_qspi *qspi,
|
|||
}
|
||||
|
||||
flash->fsize = FSIZE_VAL(mtd->size);
|
||||
flash->prefetch_limit = mtd->size - STM32_QSPI_FIFO_SZ;
|
||||
|
||||
flash->read_mode = CCR_FMODE_MM;
|
||||
if (mtd->size > qspi->mm_size)
|
||||
|
|
|
@ -231,11 +231,18 @@ enum spi_nor_option_flags {
|
|||
SNOR_F_USE_CLSR = BIT(5),
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -262,6 +269,7 @@ enum spi_nor_option_flags {
|
|||
* @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
|
||||
* completely locked
|
||||
* @priv: the private data
|
||||
*/
|
||||
|
@ -269,6 +277,7 @@ 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;
|
||||
|
@ -296,6 +305,7 @@ struct spi_nor {
|
|||
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);
|
||||
|
||||
void *priv;
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче