mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 21:33:00 +00:00
MTD updates for 3.16:
- refactor m25p80.c driver for use as a general SPI NOR framework for other drivers which may speak to SPI NOR flash without providing full SPI support (i.e., not part of drivers/spi/) - new Freescale QuadSPI driver (utilizing new SPI NOR framework) - updates for the STMicro "FSM" SPI NOR driver - fix sync/flush behavior on mtd_blkdevs - fixup subpage write support on a few NAND drivers - correct the MTD OOB test for odd-sized OOB areas - add BCH-16 support for OMAP NAND - fix warnings and trivial refactoring - utilize new ECC DT bindings in pxa3xx NAND driver - new LPDDR NVM driver - address a few assorted bugs caught by Coverity - add new imx6sx support for GPMI NAND - use a bounce buffer for NAND when non-DMA-able buffers are used -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJTl/9oAAoJEFySrpd9RFgtfnUP+wURZfF1Xnek/3yNZP0Pt90x yToXPDgsK5oteBAAWMUwtnJImDzsUMD8BgLNLU1jvPKuvMo9lwNMaCF+l1wUrTeC F1VgYWq4tub3tk104Dthlguk0Jhj66k61LbvFvKXhkGEYGD9iPFeTPWyARUZTYOv R4eRybuU+l2ZTDd+vNStXx9oWyqzWXekwrhMi10YWoxF694kBMI4C0rZQ2CexjVl B5K0oL2P8JU/yNLgtMgPOfkh8rHZEoXECA3vaQscZzsOnc0evDndKSTkTX1Ls61Y eWFgXV6qyhL+5VKTiHNzi6/J0NeNaTquOs9HoBuWr1DwaS8aoWEhBjeuNrXGYs/s 6++CRoDDcdWunAXBH8hqFSu6IweYB5TQ+QMUa7Z69C9n/fZg82dF4i2RSnp4Y2rs qI19LrPIpdyL1ril5ndp0U2JRYXdxOpX3+jf2anG6u3vYjzI8P8tXEGKUz/uNpnK fpEn2zKpeHAq62eqScuhGsO7MO2bIg7yNKMdSoeeeT9dgbah6fkrQnaDNbtjC+Y1 rXMhgLiVebmm8BVe6w5XSFqCw+76RxmO04TAj/Vy3WVPQ2KNn+OuLc0yVlsqAO9n 7Y19QvHeMZZW4O/w5RQ/OniJpysXN0ESj2cE93DHdgUPQ5aedIN0r5eQA0M1e8c6 W2MQFS5nJPiCxUYia4KP =6UIq -----END PGP SIGNATURE----- Merge tag 'for-linus-20140610' of git://git.infradead.org/linux-mtd Pull MTD updates from Brian Norris: - refactor m25p80.c driver for use as a general SPI NOR framework for other drivers which may speak to SPI NOR flash without providing full SPI support (i.e., not part of drivers/spi/) - new Freescale QuadSPI driver (utilizing new SPI NOR framework) - updates for the STMicro "FSM" SPI NOR driver - fix sync/flush behavior on mtd_blkdevs - fixup subpage write support on a few NAND drivers - correct the MTD OOB test for odd-sized OOB areas - add BCH-16 support for OMAP NAND - fix warnings and trivial refactoring - utilize new ECC DT bindings in pxa3xx NAND driver - new LPDDR NVM driver - address a few assorted bugs caught by Coverity - add new imx6sx support for GPMI NAND - use a bounce buffer for NAND when non-DMA-able buffers are used * tag 'for-linus-20140610' of git://git.infradead.org/linux-mtd: (77 commits) mtd: gpmi: add gpmi support for imx6sx mtd: maps: remove check for CONFIG_MTD_SUPERH_RESERVE mtd: bf5xx_nand: use the managed version of kzalloc mtd: pxa3xx_nand: make the driver work on big-endian systems mtd: nand: omap: fix omap_calculate_ecc_bch() for-loop error mtd: nand: r852: correct write_buf loop bounds mtd: nand_bbt: handle error case for nand_create_badblock_pattern() mtd: nand_bbt: remove unused variable mtd: maps: sc520cdp: fix warnings mtd: slram: fix unused variable warning mtd: pfow: remove unused variable mtd: lpddr: fix Kconfig dependency, for I/O accessors mtd: nand: pxa3xx: Add supported ECC strength and step size to the DT binding mtd: nand: pxa3xx: Use ECC strength and step size devicetree binding mtd: nand: pxa3xx: Clean pxa_ecc_init() error handling mtd: nand: Warn the user if the selected ECC strength is too weak mtd: nand: omap: Documentation: How to select correct ECC scheme for your device ? mtd: nand: omap: add support for BCH16_ECC - NAND driver updates mtd: nand: omap: add support for BCH16_ECC - ELM driver updates mtd: nand: omap: add support for BCH16_ECC - GPMC driver updates ...
This commit is contained in:
commit
e413a19a8e
35
Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
Normal file
35
Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
Normal file
@ -0,0 +1,35 @@
|
||||
* Freescale Quad Serial Peripheral Interface(QuadSPI)
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "fsl,vf610-qspi"
|
||||
- reg : the first contains the register location and length,
|
||||
the second contains the memory mapping address and length
|
||||
- reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory"
|
||||
- interrupts : Should contain the interrupt for the device
|
||||
- clocks : The clocks needed by the QuadSPI controller
|
||||
- clock-names : the name of the clocks
|
||||
|
||||
Optional properties:
|
||||
- fsl,qspi-has-second-chip: The controller has two buses, bus A and bus B.
|
||||
Each bus can be connected with two NOR flashes.
|
||||
Most of the time, each bus only has one NOR flash
|
||||
connected, this is the default case.
|
||||
But if there are two NOR flashes connected to the
|
||||
bus, you should enable this property.
|
||||
(Please check the board's schematic.)
|
||||
|
||||
Example:
|
||||
|
||||
qspi0: quadspi@40044000 {
|
||||
compatible = "fsl,vf610-qspi";
|
||||
reg = <0x40044000 0x1000>, <0x20000000 0x10000000>;
|
||||
reg-names = "QuadSPI", "QuadSPI-memory";
|
||||
interrupts = <0 24 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clks VF610_CLK_QSPI0_EN>,
|
||||
<&clks VF610_CLK_QSPI0>;
|
||||
clock-names = "qspi_en", "qspi";
|
||||
|
||||
flash0: s25fl128s@0 {
|
||||
....
|
||||
};
|
||||
};
|
@ -28,6 +28,8 @@ Optional properties:
|
||||
"ham1" 1-bit Hamming ecc code
|
||||
"bch4" 4-bit BCH ecc code
|
||||
"bch8" 8-bit BCH ecc code
|
||||
"bch16" 16-bit BCH ECC code
|
||||
Refer below "How to select correct ECC scheme for your device ?"
|
||||
|
||||
- ti,nand-xfer-type: A string setting the data transfer type. One of:
|
||||
|
||||
@ -90,3 +92,46 @@ Example for an AM33xx board:
|
||||
};
|
||||
};
|
||||
|
||||
How to select correct ECC scheme for your device ?
|
||||
--------------------------------------------------
|
||||
Higher ECC scheme usually means better protection against bit-flips and
|
||||
increased system lifetime. However, selection of ECC scheme is dependent
|
||||
on various other factors also like;
|
||||
|
||||
(1) support of built in hardware engines.
|
||||
Some legacy OMAP SoC do not have ELM harware engine, so those SoC cannot
|
||||
support ecc-schemes with hardware error-correction (BCHx_HW). However
|
||||
such SoC can use ecc-schemes with software library for error-correction
|
||||
(BCHx_HW_DETECTION_SW). The error correction capability with software
|
||||
library remains equivalent to their hardware counter-part, but there is
|
||||
slight CPU penalty when too many bit-flips are detected during reads.
|
||||
|
||||
(2) Device parameters like OOBSIZE.
|
||||
Other factor which governs the selection of ecc-scheme is oob-size.
|
||||
Higher ECC schemes require more OOB/Spare area to store ECC syndrome,
|
||||
so the device should have enough free bytes available its OOB/Spare
|
||||
area to accomodate ECC for entire page. In general following expression
|
||||
helps in determining if given device can accomodate ECC syndrome:
|
||||
"2 + (PAGESIZE / 512) * ECC_BYTES" >= OOBSIZE"
|
||||
where
|
||||
OOBSIZE number of bytes in OOB/spare area
|
||||
PAGESIZE number of bytes in main-area of device page
|
||||
ECC_BYTES number of ECC bytes generated to protect
|
||||
512 bytes of data, which is:
|
||||
'3' for HAM1_xx ecc schemes
|
||||
'7' for BCH4_xx ecc schemes
|
||||
'14' for BCH8_xx ecc schemes
|
||||
'26' for BCH16_xx ecc schemes
|
||||
|
||||
Example(a): For a device with PAGESIZE = 2048 and OOBSIZE = 64 and
|
||||
trying to use BCH16 (ECC_BYTES=26) ecc-scheme.
|
||||
Number of ECC bytes per page = (2 + (2048 / 512) * 26) = 106 B
|
||||
which is greater than capacity of NAND device (OOBSIZE=64)
|
||||
Hence, BCH16 cannot be supported on given device. But it can
|
||||
probably use lower ecc-schemes like BCH8.
|
||||
|
||||
Example(b): For a device with PAGESIZE = 2048 and OOBSIZE = 128 and
|
||||
trying to use BCH16 (ECC_BYTES=26) ecc-scheme.
|
||||
Number of ECC bytes per page = (2 + (2048 / 512) * 26) = 106 B
|
||||
which can be accomodate in the OOB/Spare area of this device
|
||||
(OOBSIZE=128). So this device can use BCH16 ecc-scheme.
|
||||
|
@ -5,8 +5,8 @@ Required properties:
|
||||
representing partitions.
|
||||
- compatible : Should be the manufacturer and the name of the chip. Bear in mind
|
||||
the DT binding is not Linux-only, but in case of Linux, see the
|
||||
"m25p_ids" table in drivers/mtd/devices/m25p80.c for the list of
|
||||
supported chips.
|
||||
"spi_nor_ids" table in drivers/mtd/spi-nor/spi-nor.c for the list
|
||||
of supported chips.
|
||||
- reg : Chip-Select number
|
||||
- spi-max-frequency : Maximum frequency of the SPI bus the chip can operate at
|
||||
|
||||
|
@ -17,6 +17,14 @@ Optional properties:
|
||||
- num-cs: Number of chipselect lines to usw
|
||||
- nand-on-flash-bbt: boolean to enable on flash bbt option if
|
||||
not present false
|
||||
- nand-ecc-strength: number of bits to correct per ECC step
|
||||
- nand-ecc-step-size: number of data bytes covered by a single ECC step
|
||||
|
||||
The following ECC strength and step size are currently supported:
|
||||
|
||||
- nand-ecc-strength = <1>, nand-ecc-step-size = <512>
|
||||
- nand-ecc-strength = <4>, nand-ecc-step-size = <512>
|
||||
- nand-ecc-strength = <8>, nand-ecc-step-size = <512>
|
||||
|
||||
Example:
|
||||
|
||||
|
62
Documentation/mtd/spi-nor.txt
Normal file
62
Documentation/mtd/spi-nor.txt
Normal file
@ -0,0 +1,62 @@
|
||||
SPI NOR framework
|
||||
============================================
|
||||
|
||||
Part I - Why do we need this framework?
|
||||
---------------------------------------
|
||||
|
||||
SPI bus controllers (drivers/spi/) only deal with streams of bytes; the bus
|
||||
controller operates agnostic of the specific device attached. However, some
|
||||
controllers (such as Freescale's QuadSPI controller) cannot easily handle
|
||||
arbitrary streams of bytes, but rather are designed specifically for SPI NOR.
|
||||
|
||||
In particular, Freescale's QuadSPI controller must know the NOR commands to
|
||||
find the right LUT sequence. Unfortunately, the SPI subsystem has no notion of
|
||||
opcodes, addresses, or data payloads; a SPI controller simply knows to send or
|
||||
receive bytes (Tx and Rx). Therefore, we must define a new layering scheme under
|
||||
which the controller driver is aware of the opcodes, addressing, and other
|
||||
details of the SPI NOR protocol.
|
||||
|
||||
Part II - How does the framework work?
|
||||
--------------------------------------
|
||||
|
||||
This framework just adds a new layer between the MTD and the SPI bus driver.
|
||||
With this new layer, the SPI NOR controller driver does not depend on the
|
||||
m25p80 code anymore.
|
||||
|
||||
Before this framework, the layer is like:
|
||||
|
||||
MTD
|
||||
------------------------
|
||||
m25p80
|
||||
------------------------
|
||||
SPI bus driver
|
||||
------------------------
|
||||
SPI NOR chip
|
||||
|
||||
After this framework, the layer is like:
|
||||
MTD
|
||||
------------------------
|
||||
SPI NOR framework
|
||||
------------------------
|
||||
m25p80
|
||||
------------------------
|
||||
SPI bus driver
|
||||
------------------------
|
||||
SPI NOR chip
|
||||
|
||||
With the SPI NOR controller driver (Freescale QuadSPI), it looks like:
|
||||
MTD
|
||||
------------------------
|
||||
SPI NOR framework
|
||||
------------------------
|
||||
fsl-quadSPI
|
||||
------------------------
|
||||
SPI NOR chip
|
||||
|
||||
Part III - How can drivers use the framework?
|
||||
---------------------------------------------
|
||||
|
||||
The main API is spi_nor_scan(). Before you call the hook, a driver should
|
||||
initialize the necessary fields for spi_nor{}. Please see
|
||||
drivers/mtd/spi-nor/spi-nor.c for detail. Please also refer to fsl-quadspi.c
|
||||
when you want to write a new driver for a SPI NOR controller.
|
@ -68,6 +68,9 @@
|
||||
#define GPMC_ECC_BCH_RESULT_1 0x244 /* not available on OMAP2 */
|
||||
#define GPMC_ECC_BCH_RESULT_2 0x248 /* not available on OMAP2 */
|
||||
#define GPMC_ECC_BCH_RESULT_3 0x24c /* not available on OMAP2 */
|
||||
#define GPMC_ECC_BCH_RESULT_4 0x300 /* not available on OMAP2 */
|
||||
#define GPMC_ECC_BCH_RESULT_5 0x304 /* not available on OMAP2 */
|
||||
#define GPMC_ECC_BCH_RESULT_6 0x308 /* not available on OMAP2 */
|
||||
|
||||
/* GPMC ECC control settings */
|
||||
#define GPMC_ECC_CTRL_ECCCLEAR 0x100
|
||||
@ -677,6 +680,12 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
|
||||
GPMC_BCH_SIZE * i;
|
||||
reg->gpmc_bch_result3[i] = gpmc_base + GPMC_ECC_BCH_RESULT_3 +
|
||||
GPMC_BCH_SIZE * i;
|
||||
reg->gpmc_bch_result4[i] = gpmc_base + GPMC_ECC_BCH_RESULT_4 +
|
||||
i * GPMC_BCH_SIZE;
|
||||
reg->gpmc_bch_result5[i] = gpmc_base + GPMC_ECC_BCH_RESULT_5 +
|
||||
i * GPMC_BCH_SIZE;
|
||||
reg->gpmc_bch_result6[i] = gpmc_base + GPMC_ECC_BCH_RESULT_6 +
|
||||
i * GPMC_BCH_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1412,6 +1421,12 @@ static int gpmc_probe_nand_child(struct platform_device *pdev,
|
||||
else
|
||||
gpmc_nand_data->ecc_opt =
|
||||
OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
|
||||
else if (!strcmp(s, "bch16"))
|
||||
if (gpmc_nand_data->elm_of_node)
|
||||
gpmc_nand_data->ecc_opt =
|
||||
OMAP_ECC_BCH16_CODE_HW;
|
||||
else
|
||||
pr_err("%s: BCH16 requires ELM support\n", __func__);
|
||||
else
|
||||
pr_err("%s: ti,nand-ecc-opt invalid value\n", __func__);
|
||||
|
||||
|
@ -321,6 +321,8 @@ source "drivers/mtd/onenand/Kconfig"
|
||||
|
||||
source "drivers/mtd/lpddr/Kconfig"
|
||||
|
||||
source "drivers/mtd/spi-nor/Kconfig"
|
||||
|
||||
source "drivers/mtd/ubi/Kconfig"
|
||||
|
||||
endif # MTD
|
||||
|
@ -32,4 +32,5 @@ inftl-objs := inftlcore.o inftlmount.o
|
||||
|
||||
obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
|
||||
|
||||
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
|
||||
obj-$(CONFIG_MTD_UBI) += ubi/
|
||||
|
@ -169,33 +169,33 @@ config MTD_OTP
|
||||
in the programming of OTP bits will waste them.
|
||||
|
||||
config MTD_CFI_INTELEXT
|
||||
tristate "Support for Intel/Sharp flash chips"
|
||||
tristate "Support for CFI command set 0001 (Intel/Sharp chips)"
|
||||
depends on MTD_GEN_PROBE
|
||||
select MTD_CFI_UTIL
|
||||
help
|
||||
The Common Flash Interface defines a number of different command
|
||||
sets which a CFI-compliant chip may claim to implement. This code
|
||||
provides support for one of those command sets, used on Intel
|
||||
StrataFlash and other parts.
|
||||
provides support for command set 0001, used on Intel StrataFlash
|
||||
and other parts.
|
||||
|
||||
config MTD_CFI_AMDSTD
|
||||
tristate "Support for AMD/Fujitsu/Spansion flash chips"
|
||||
tristate "Support for CFI command set 0002 (AMD/Fujitsu/Spansion chips)"
|
||||
depends on MTD_GEN_PROBE
|
||||
select MTD_CFI_UTIL
|
||||
help
|
||||
The Common Flash Interface defines a number of different command
|
||||
sets which a CFI-compliant chip may claim to implement. This code
|
||||
provides support for one of those command sets, used on chips
|
||||
including the AMD Am29LV320.
|
||||
provides support for command set 0002, used on chips including
|
||||
the AMD Am29LV320.
|
||||
|
||||
config MTD_CFI_STAA
|
||||
tristate "Support for ST (Advanced Architecture) flash chips"
|
||||
tristate "Support for CFI command set 0020 (ST (Advanced Architecture) chips)"
|
||||
depends on MTD_GEN_PROBE
|
||||
select MTD_CFI_UTIL
|
||||
help
|
||||
The Common Flash Interface defines a number of different command
|
||||
sets which a CFI-compliant chip may claim to implement. This code
|
||||
provides support for one of those command sets.
|
||||
provides support for command set 0020.
|
||||
|
||||
config MTD_CFI_UTIL
|
||||
tristate
|
||||
|
@ -961,7 +961,7 @@ static int cfi_staa_erase_varsize(struct mtd_info *mtd,
|
||||
chipnum++;
|
||||
|
||||
if (chipnum >= cfi->numchips)
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1170,7 +1170,7 @@ static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
chipnum++;
|
||||
|
||||
if (chipnum >= cfi->numchips)
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -239,7 +239,7 @@ int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
|
||||
chipnum++;
|
||||
|
||||
if (chipnum >= cfi->numchips)
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ config MTD_DATAFLASH_OTP
|
||||
|
||||
config MTD_M25P80
|
||||
tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
|
||||
depends on SPI_MASTER
|
||||
depends on SPI_MASTER && MTD_SPI_NOR
|
||||
help
|
||||
This enables access to most modern SPI flash chips, used for
|
||||
program and data storage. Series supported include Atmel AT26DF,
|
||||
@ -212,7 +212,7 @@ config MTD_DOCG3
|
||||
|
||||
config MTD_ST_SPI_FSM
|
||||
tristate "ST Microelectronics SPI FSM Serial Flash Controller"
|
||||
depends on ARM || SH
|
||||
depends on ARCH_STI
|
||||
help
|
||||
This provides an MTD device driver for the ST Microelectronics
|
||||
SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
|
||||
|
@ -213,6 +213,28 @@ static void elm_load_syndrome(struct elm_info *info,
|
||||
val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12;
|
||||
elm_write_reg(info, offset, val);
|
||||
break;
|
||||
case BCH16_ECC:
|
||||
val = cpu_to_be32(*(u32 *) &ecc[22]);
|
||||
elm_write_reg(info, offset, val);
|
||||
offset += 4;
|
||||
val = cpu_to_be32(*(u32 *) &ecc[18]);
|
||||
elm_write_reg(info, offset, val);
|
||||
offset += 4;
|
||||
val = cpu_to_be32(*(u32 *) &ecc[14]);
|
||||
elm_write_reg(info, offset, val);
|
||||
offset += 4;
|
||||
val = cpu_to_be32(*(u32 *) &ecc[10]);
|
||||
elm_write_reg(info, offset, val);
|
||||
offset += 4;
|
||||
val = cpu_to_be32(*(u32 *) &ecc[6]);
|
||||
elm_write_reg(info, offset, val);
|
||||
offset += 4;
|
||||
val = cpu_to_be32(*(u32 *) &ecc[2]);
|
||||
elm_write_reg(info, offset, val);
|
||||
offset += 4;
|
||||
val = cpu_to_be32(*(u32 *) &ecc[0]) >> 16;
|
||||
elm_write_reg(info, offset, val);
|
||||
break;
|
||||
default:
|
||||
pr_err("invalid config bch_type\n");
|
||||
}
|
||||
@ -418,6 +440,7 @@ static int elm_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/**
|
||||
* elm_context_save
|
||||
* saves ELM configurations to preserve them across Hardware powered-down
|
||||
@ -435,6 +458,13 @@ static int elm_context_save(struct elm_info *info)
|
||||
for (i = 0; i < ERROR_VECTOR_MAX; i++) {
|
||||
offset = i * SYNDROME_FRAGMENT_REG_SIZE;
|
||||
switch (bch_type) {
|
||||
case BCH16_ECC:
|
||||
regs->elm_syndrome_fragment_6[i] = elm_read_reg(info,
|
||||
ELM_SYNDROME_FRAGMENT_6 + offset);
|
||||
regs->elm_syndrome_fragment_5[i] = elm_read_reg(info,
|
||||
ELM_SYNDROME_FRAGMENT_5 + offset);
|
||||
regs->elm_syndrome_fragment_4[i] = elm_read_reg(info,
|
||||
ELM_SYNDROME_FRAGMENT_4 + offset);
|
||||
case BCH8_ECC:
|
||||
regs->elm_syndrome_fragment_3[i] = elm_read_reg(info,
|
||||
ELM_SYNDROME_FRAGMENT_3 + offset);
|
||||
@ -473,6 +503,13 @@ static int elm_context_restore(struct elm_info *info)
|
||||
for (i = 0; i < ERROR_VECTOR_MAX; i++) {
|
||||
offset = i * SYNDROME_FRAGMENT_REG_SIZE;
|
||||
switch (bch_type) {
|
||||
case BCH16_ECC:
|
||||
elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset,
|
||||
regs->elm_syndrome_fragment_6[i]);
|
||||
elm_write_reg(info, ELM_SYNDROME_FRAGMENT_5 + offset,
|
||||
regs->elm_syndrome_fragment_5[i]);
|
||||
elm_write_reg(info, ELM_SYNDROME_FRAGMENT_4 + offset,
|
||||
regs->elm_syndrome_fragment_4[i]);
|
||||
case BCH8_ECC:
|
||||
elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset,
|
||||
regs->elm_syndrome_fragment_3[i]);
|
||||
@ -509,6 +546,7 @@ static int elm_resume(struct device *dev)
|
||||
elm_context_restore(info);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -13,43 +13,23 @@
|
||||
#define _MTD_SERIAL_FLASH_CMDS_H
|
||||
|
||||
/* Generic Flash Commands/OPCODEs */
|
||||
#define FLASH_CMD_WREN 0x06
|
||||
#define FLASH_CMD_WRDI 0x04
|
||||
#define FLASH_CMD_RDID 0x9f
|
||||
#define FLASH_CMD_RDSR 0x05
|
||||
#define FLASH_CMD_RDSR2 0x35
|
||||
#define FLASH_CMD_WRSR 0x01
|
||||
#define FLASH_CMD_SE_4K 0x20
|
||||
#define FLASH_CMD_SE_32K 0x52
|
||||
#define FLASH_CMD_SE 0xd8
|
||||
#define FLASH_CMD_CHIPERASE 0xc7
|
||||
#define FLASH_CMD_WRVCR 0x81
|
||||
#define FLASH_CMD_RDVCR 0x85
|
||||
#define SPINOR_OP_RDSR2 0x35
|
||||
#define SPINOR_OP_WRVCR 0x81
|
||||
#define SPINOR_OP_RDVCR 0x85
|
||||
|
||||
/* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
|
||||
#define FLASH_CMD_READ 0x03 /* READ */
|
||||
#define FLASH_CMD_READ_FAST 0x0b /* FAST READ */
|
||||
#define FLASH_CMD_READ_1_1_2 0x3b /* DUAL OUTPUT READ */
|
||||
#define FLASH_CMD_READ_1_2_2 0xbb /* DUAL I/O READ */
|
||||
#define FLASH_CMD_READ_1_1_4 0x6b /* QUAD OUTPUT READ */
|
||||
#define FLASH_CMD_READ_1_4_4 0xeb /* QUAD I/O READ */
|
||||
#define SPINOR_OP_READ_1_2_2 0xbb /* DUAL I/O READ */
|
||||
#define SPINOR_OP_READ_1_4_4 0xeb /* QUAD I/O READ */
|
||||
|
||||
#define FLASH_CMD_WRITE 0x02 /* PAGE PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */
|
||||
|
||||
#define FLASH_CMD_EN4B_ADDR 0xb7 /* Enter 4-byte address mode */
|
||||
#define FLASH_CMD_EX4B_ADDR 0xe9 /* Exit 4-byte address mode */
|
||||
#define SPINOR_OP_WRITE 0x02 /* PAGE PROGRAM */
|
||||
#define SPINOR_OP_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */
|
||||
#define SPINOR_OP_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */
|
||||
#define SPINOR_OP_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */
|
||||
#define SPINOR_OP_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */
|
||||
|
||||
/* READ commands with 32-bit addressing */
|
||||
#define FLASH_CMD_READ4 0x13
|
||||
#define FLASH_CMD_READ4_FAST 0x0c
|
||||
#define FLASH_CMD_READ4_1_1_2 0x3c
|
||||
#define FLASH_CMD_READ4_1_2_2 0xbc
|
||||
#define FLASH_CMD_READ4_1_1_4 0x6c
|
||||
#define FLASH_CMD_READ4_1_4_4 0xec
|
||||
#define SPINOR_OP_READ4_1_2_2 0xbc
|
||||
#define SPINOR_OP_READ4_1_4_4 0xec
|
||||
|
||||
/* Configuration flags */
|
||||
#define FLASH_FLAG_SINGLE 0x000000ff
|
||||
|
@ -280,14 +280,11 @@ __setup("slram=", mtd_slram_setup);
|
||||
static int __init init_slram(void)
|
||||
{
|
||||
char *devname;
|
||||
int i;
|
||||
|
||||
#ifndef MODULE
|
||||
char *devstart;
|
||||
char *devlength;
|
||||
|
||||
i = 0;
|
||||
|
||||
if (!map) {
|
||||
E("slram: not enough parameters.\n");
|
||||
return(-EINVAL);
|
||||
@ -314,6 +311,7 @@ static int __init init_slram(void)
|
||||
}
|
||||
#else
|
||||
int count;
|
||||
int i;
|
||||
|
||||
for (count = 0; count < SLRAM_MAX_DEVICES_PARAMS && map[count];
|
||||
count++) {
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/spi-nor.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
@ -201,44 +202,6 @@
|
||||
|
||||
#define STFSM_MAX_WAIT_SEQ_MS 1000 /* FSM execution time */
|
||||
|
||||
/* Flash Commands */
|
||||
#define FLASH_CMD_WREN 0x06
|
||||
#define FLASH_CMD_WRDI 0x04
|
||||
#define FLASH_CMD_RDID 0x9f
|
||||
#define FLASH_CMD_RDSR 0x05
|
||||
#define FLASH_CMD_RDSR2 0x35
|
||||
#define FLASH_CMD_WRSR 0x01
|
||||
#define FLASH_CMD_SE_4K 0x20
|
||||
#define FLASH_CMD_SE_32K 0x52
|
||||
#define FLASH_CMD_SE 0xd8
|
||||
#define FLASH_CMD_CHIPERASE 0xc7
|
||||
#define FLASH_CMD_WRVCR 0x81
|
||||
#define FLASH_CMD_RDVCR 0x85
|
||||
|
||||
#define FLASH_CMD_READ 0x03 /* READ */
|
||||
#define FLASH_CMD_READ_FAST 0x0b /* FAST READ */
|
||||
#define FLASH_CMD_READ_1_1_2 0x3b /* DUAL OUTPUT READ */
|
||||
#define FLASH_CMD_READ_1_2_2 0xbb /* DUAL I/O READ */
|
||||
#define FLASH_CMD_READ_1_1_4 0x6b /* QUAD OUTPUT READ */
|
||||
#define FLASH_CMD_READ_1_4_4 0xeb /* QUAD I/O READ */
|
||||
|
||||
#define FLASH_CMD_WRITE 0x02 /* PAGE PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */
|
||||
|
||||
#define FLASH_CMD_EN4B_ADDR 0xb7 /* Enter 4-byte address mode */
|
||||
#define FLASH_CMD_EX4B_ADDR 0xe9 /* Exit 4-byte address mode */
|
||||
|
||||
/* READ commands with 32-bit addressing (N25Q256 and S25FLxxxS) */
|
||||
#define FLASH_CMD_READ4 0x13
|
||||
#define FLASH_CMD_READ4_FAST 0x0c
|
||||
#define FLASH_CMD_READ4_1_1_2 0x3c
|
||||
#define FLASH_CMD_READ4_1_2_2 0xbc
|
||||
#define FLASH_CMD_READ4_1_1_4 0x6c
|
||||
#define FLASH_CMD_READ4_1_4_4 0xec
|
||||
|
||||
/* S25FLxxxS commands */
|
||||
#define S25FL_CMD_WRITE4_1_1_4 0x34
|
||||
#define S25FL_CMD_SE4 0xdc
|
||||
@ -246,7 +209,7 @@
|
||||
#define S25FL_CMD_DYBWR 0xe1
|
||||
#define S25FL_CMD_DYBRD 0xe0
|
||||
#define S25FL_CMD_WRITE4 0x12 /* Note, opcode clashes with
|
||||
* 'FLASH_CMD_WRITE_1_4_4'
|
||||
* 'SPINOR_OP_WRITE_1_4_4'
|
||||
* as found on N25Qxxx devices! */
|
||||
|
||||
/* Status register */
|
||||
@ -261,6 +224,12 @@
|
||||
#define S25FL_STATUS_E_ERR 0x20
|
||||
#define S25FL_STATUS_P_ERR 0x40
|
||||
|
||||
#define N25Q_CMD_WRVCR 0x81
|
||||
#define N25Q_CMD_RDVCR 0x85
|
||||
#define N25Q_CMD_RDVECR 0x65
|
||||
#define N25Q_CMD_RDNVCR 0xb5
|
||||
#define N25Q_CMD_WRNVCR 0xb1
|
||||
|
||||
#define FLASH_PAGESIZE 256 /* In Bytes */
|
||||
#define FLASH_PAGESIZE_32 (FLASH_PAGESIZE / 4) /* In uint32_t */
|
||||
#define FLASH_MAX_BUSY_WAIT (300 * HZ) /* Maximum 'CHIPERASE' time */
|
||||
@ -270,7 +239,6 @@
|
||||
*/
|
||||
#define CFG_READ_TOGGLE_32BIT_ADDR 0x00000001
|
||||
#define CFG_WRITE_TOGGLE_32BIT_ADDR 0x00000002
|
||||
#define CFG_WRITE_EX_32BIT_ADDR_DELAY 0x00000004
|
||||
#define CFG_ERASESEC_TOGGLE_32BIT_ADDR 0x00000008
|
||||
#define CFG_S25FL_CHECK_ERROR_FLAGS 0x00000010
|
||||
|
||||
@ -329,7 +297,7 @@ struct flash_info {
|
||||
u32 jedec_id;
|
||||
u16 ext_id;
|
||||
/*
|
||||
* The size listed here is what works with FLASH_CMD_SE, which isn't
|
||||
* The size listed here is what works with SPINOR_OP_SE, which isn't
|
||||
* necessarily called a "sector" by the vendor.
|
||||
*/
|
||||
unsigned sector_size;
|
||||
@ -369,17 +337,26 @@ static struct flash_info flash_types[] = {
|
||||
{ "m25px32", 0x207116, 0, 64 * 1024, 64, M25PX_FLAG, 75, NULL },
|
||||
{ "m25px64", 0x207117, 0, 64 * 1024, 128, M25PX_FLAG, 75, NULL },
|
||||
|
||||
/* Macronix MX25xxx
|
||||
* - Support for 'FLASH_FLAG_WRITE_1_4_4' is omitted for devices
|
||||
* where operating frequency must be reduced.
|
||||
*/
|
||||
#define MX25_FLAG (FLASH_FLAG_READ_WRITE | \
|
||||
FLASH_FLAG_READ_FAST | \
|
||||
FLASH_FLAG_READ_1_1_2 | \
|
||||
FLASH_FLAG_READ_1_2_2 | \
|
||||
FLASH_FLAG_READ_1_1_4 | \
|
||||
FLASH_FLAG_READ_1_4_4 | \
|
||||
FLASH_FLAG_SE_4K | \
|
||||
FLASH_FLAG_SE_32K)
|
||||
{ "mx25l3255e", 0xc29e16, 0, 64 * 1024, 64,
|
||||
(MX25_FLAG | FLASH_FLAG_WRITE_1_4_4), 86,
|
||||
stfsm_mx25_config},
|
||||
{ "mx25l25635e", 0xc22019, 0, 64*1024, 512,
|
||||
(MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70,
|
||||
stfsm_mx25_config },
|
||||
{ "mx25l25655e", 0xc22619, 0, 64*1024, 512,
|
||||
(MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70,
|
||||
stfsm_mx25_config},
|
||||
|
||||
#define N25Q_FLAG (FLASH_FLAG_READ_WRITE | \
|
||||
FLASH_FLAG_READ_FAST | \
|
||||
@ -407,6 +384,8 @@ static struct flash_info flash_types[] = {
|
||||
FLASH_FLAG_READ_1_4_4 | \
|
||||
FLASH_FLAG_WRITE_1_1_4 | \
|
||||
FLASH_FLAG_READ_FAST)
|
||||
{ "s25fl032p", 0x010215, 0x4d00, 64 * 1024, 64, S25FLXXXP_FLAG, 80,
|
||||
stfsm_s25fl_config},
|
||||
{ "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, S25FLXXXP_FLAG, 80,
|
||||
stfsm_s25fl_config },
|
||||
{ "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, S25FLXXXP_FLAG, 80,
|
||||
@ -473,22 +452,22 @@ static struct flash_info flash_types[] = {
|
||||
|
||||
/* Default READ configurations, in order of preference */
|
||||
static struct seq_rw_config default_read_configs[] = {
|
||||
{FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ_1_4_4, 0, 4, 4, 0x00, 2, 4},
|
||||
{FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ_1_1_4, 0, 1, 4, 0x00, 4, 0},
|
||||
{FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ_1_2_2, 0, 2, 2, 0x00, 4, 0},
|
||||
{FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ_1_1_2, 0, 1, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_FAST, FLASH_CMD_READ_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_WRITE, FLASH_CMD_READ, 0, 1, 1, 0x00, 0, 0},
|
||||
{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4, 0, 4, 4, 0x00, 2, 4},
|
||||
{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4, 0, 1, 4, 0x00, 4, 0},
|
||||
{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2, 0, 2, 2, 0x00, 4, 0},
|
||||
{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2, 0, 1, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_FAST, SPINOR_OP_READ_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ, 0, 1, 1, 0x00, 0, 0},
|
||||
{0x00, 0, 0, 0, 0, 0x00, 0, 0},
|
||||
};
|
||||
|
||||
/* Default WRITE configurations */
|
||||
static struct seq_rw_config default_write_configs[] = {
|
||||
{FLASH_FLAG_WRITE_1_4_4, FLASH_CMD_WRITE_1_4_4, 1, 4, 4, 0x00, 0, 0},
|
||||
{FLASH_FLAG_WRITE_1_1_4, FLASH_CMD_WRITE_1_1_4, 1, 1, 4, 0x00, 0, 0},
|
||||
{FLASH_FLAG_WRITE_1_2_2, FLASH_CMD_WRITE_1_2_2, 1, 2, 2, 0x00, 0, 0},
|
||||
{FLASH_FLAG_WRITE_1_1_2, FLASH_CMD_WRITE_1_1_2, 1, 1, 2, 0x00, 0, 0},
|
||||
{FLASH_FLAG_READ_WRITE, FLASH_CMD_WRITE, 1, 1, 1, 0x00, 0, 0},
|
||||
{FLASH_FLAG_WRITE_1_4_4, SPINOR_OP_WRITE_1_4_4, 1, 4, 4, 0x00, 0, 0},
|
||||
{FLASH_FLAG_WRITE_1_1_4, SPINOR_OP_WRITE_1_1_4, 1, 1, 4, 0x00, 0, 0},
|
||||
{FLASH_FLAG_WRITE_1_2_2, SPINOR_OP_WRITE_1_2_2, 1, 2, 2, 0x00, 0, 0},
|
||||
{FLASH_FLAG_WRITE_1_1_2, SPINOR_OP_WRITE_1_1_2, 1, 1, 2, 0x00, 0, 0},
|
||||
{FLASH_FLAG_READ_WRITE, SPINOR_OP_WRITE, 1, 1, 1, 0x00, 0, 0},
|
||||
{0x00, 0, 0, 0, 0, 0x00, 0, 0},
|
||||
};
|
||||
|
||||
@ -511,12 +490,12 @@ static struct seq_rw_config default_write_configs[] = {
|
||||
* cycles.
|
||||
*/
|
||||
static struct seq_rw_config n25q_read3_configs[] = {
|
||||
{FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ_1_4_4, 0, 4, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ_1_1_4, 0, 1, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ_1_2_2, 0, 2, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ_1_1_2, 0, 1, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_FAST, FLASH_CMD_READ_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_WRITE, FLASH_CMD_READ, 0, 1, 1, 0x00, 0, 0},
|
||||
{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4, 0, 4, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4, 0, 1, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2, 0, 2, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2, 0, 1, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_FAST, SPINOR_OP_READ_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ, 0, 1, 1, 0x00, 0, 0},
|
||||
{0x00, 0, 0, 0, 0, 0x00, 0, 0},
|
||||
};
|
||||
|
||||
@ -526,12 +505,12 @@ static struct seq_rw_config n25q_read3_configs[] = {
|
||||
* - 'FAST' variants configured for 8 dummy cycles (see note above.)
|
||||
*/
|
||||
static struct seq_rw_config n25q_read4_configs[] = {
|
||||
{FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ4_1_4_4, 0, 4, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ4_1_2_2, 0, 2, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_FAST, FLASH_CMD_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_WRITE, FLASH_CMD_READ4, 0, 1, 1, 0x00, 0, 0},
|
||||
{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4, 0, 4, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2, 0, 2, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_FAST, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0},
|
||||
{0x00, 0, 0, 0, 0, 0x00, 0, 0},
|
||||
};
|
||||
|
||||
@ -544,7 +523,7 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq)
|
||||
{
|
||||
seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
|
||||
SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_EN4B_ADDR) |
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_EN4B) |
|
||||
SEQ_OPC_CSDEASSERT);
|
||||
|
||||
seq->seq[0] = STFSM_INST_CMD1;
|
||||
@ -572,12 +551,12 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq)
|
||||
* entering a state that is incompatible with the SPIBoot Controller.
|
||||
*/
|
||||
static struct seq_rw_config stfsm_s25fl_read4_configs[] = {
|
||||
{FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ4_1_4_4, 0, 4, 4, 0x00, 2, 4},
|
||||
{FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ4_1_2_2, 0, 2, 2, 0x00, 4, 0},
|
||||
{FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_FAST, FLASH_CMD_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_WRITE, FLASH_CMD_READ4, 0, 1, 1, 0x00, 0, 0},
|
||||
{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4, 0, 4, 4, 0x00, 2, 4},
|
||||
{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2, 0, 2, 2, 0x00, 4, 0},
|
||||
{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_FAST, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0},
|
||||
{0x00, 0, 0, 0, 0, 0x00, 0, 0},
|
||||
};
|
||||
|
||||
@ -590,13 +569,13 @@ static struct seq_rw_config stfsm_s25fl_write4_configs[] = {
|
||||
/*
|
||||
* [W25Qxxx] Configuration
|
||||
*/
|
||||
#define W25Q_STATUS_QE (0x1 << 9)
|
||||
#define W25Q_STATUS_QE (0x1 << 1)
|
||||
|
||||
static struct stfsm_seq stfsm_seq_read_jedec = {
|
||||
.data_size = TRANSFER_SIZE(8),
|
||||
.seq_opc[0] = (SEQ_OPC_PADS_1 |
|
||||
SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_RDID)),
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_RDID)),
|
||||
.seq = {
|
||||
STFSM_INST_CMD1,
|
||||
STFSM_INST_DATA_READ,
|
||||
@ -612,7 +591,7 @@ static struct stfsm_seq stfsm_seq_read_status_fifo = {
|
||||
.data_size = TRANSFER_SIZE(4),
|
||||
.seq_opc[0] = (SEQ_OPC_PADS_1 |
|
||||
SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_RDSR)),
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_RDSR)),
|
||||
.seq = {
|
||||
STFSM_INST_CMD1,
|
||||
STFSM_INST_DATA_READ,
|
||||
@ -628,10 +607,10 @@ static struct stfsm_seq stfsm_seq_erase_sector = {
|
||||
/* 'addr_cfg' configured during initialisation */
|
||||
.seq_opc = {
|
||||
(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT),
|
||||
|
||||
(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_SE)),
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_SE)),
|
||||
},
|
||||
.seq = {
|
||||
STFSM_INST_CMD1,
|
||||
@ -649,10 +628,10 @@ static struct stfsm_seq stfsm_seq_erase_sector = {
|
||||
static struct stfsm_seq stfsm_seq_erase_chip = {
|
||||
.seq_opc = {
|
||||
(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT),
|
||||
|
||||
(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_CHIPERASE) | SEQ_OPC_CSDEASSERT),
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_CHIP_ERASE) | SEQ_OPC_CSDEASSERT),
|
||||
},
|
||||
.seq = {
|
||||
STFSM_INST_CMD1,
|
||||
@ -669,26 +648,9 @@ static struct stfsm_seq stfsm_seq_erase_chip = {
|
||||
|
||||
static struct stfsm_seq stfsm_seq_write_status = {
|
||||
.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT),
|
||||
.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WRSR)),
|
||||
.seq = {
|
||||
STFSM_INST_CMD1,
|
||||
STFSM_INST_CMD2,
|
||||
STFSM_INST_STA_WR1,
|
||||
STFSM_INST_STOP,
|
||||
},
|
||||
.seq_cfg = (SEQ_CFG_PADS_1 |
|
||||
SEQ_CFG_READNOTWRITE |
|
||||
SEQ_CFG_CSDEASSERT |
|
||||
SEQ_CFG_STARTSEQ),
|
||||
};
|
||||
|
||||
static struct stfsm_seq stfsm_seq_wrvcr = {
|
||||
.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
|
||||
.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WRVCR)),
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_WRSR)),
|
||||
.seq = {
|
||||
STFSM_INST_CMD1,
|
||||
STFSM_INST_CMD2,
|
||||
@ -704,9 +666,9 @@ static struct stfsm_seq stfsm_seq_wrvcr = {
|
||||
static int stfsm_n25q_en_32bit_addr_seq(struct stfsm_seq *seq)
|
||||
{
|
||||
seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_EN4B_ADDR));
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_EN4B));
|
||||
seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WREN) |
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_WREN) |
|
||||
SEQ_OPC_CSDEASSERT);
|
||||
|
||||
seq->seq[0] = STFSM_INST_CMD2;
|
||||
@ -793,7 +755,7 @@ static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, uint32_t size)
|
||||
|
||||
dev_dbg(fsm->dev, "Reading %d bytes from FIFO\n", size);
|
||||
|
||||
BUG_ON((((uint32_t)buf) & 0x3) || (size & 0x3));
|
||||
BUG_ON((((uintptr_t)buf) & 0x3) || (size & 0x3));
|
||||
|
||||
while (remaining) {
|
||||
for (;;) {
|
||||
@ -817,7 +779,7 @@ static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf,
|
||||
|
||||
dev_dbg(fsm->dev, "writing %d bytes to FIFO\n", size);
|
||||
|
||||
BUG_ON((((uint32_t)buf) & 0x3) || (size & 0x3));
|
||||
BUG_ON((((uintptr_t)buf) & 0x3) || (size & 0x3));
|
||||
|
||||
writesl(fsm->base + SPI_FAST_SEQ_DATA_REG, buf, words);
|
||||
|
||||
@ -827,7 +789,7 @@ static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf,
|
||||
static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter)
|
||||
{
|
||||
struct stfsm_seq *seq = &fsm->stfsm_seq_en_32bit_addr;
|
||||
uint32_t cmd = enter ? FLASH_CMD_EN4B_ADDR : FLASH_CMD_EX4B_ADDR;
|
||||
uint32_t cmd = enter ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
|
||||
|
||||
seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
|
||||
SEQ_OPC_CYCLES(8) |
|
||||
@ -851,7 +813,7 @@ static uint8_t stfsm_wait_busy(struct stfsm *fsm)
|
||||
/* Use RDRS1 */
|
||||
seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
|
||||
SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_RDSR));
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_RDSR));
|
||||
|
||||
/* Load read_status sequence */
|
||||
stfsm_load_seq(fsm, seq);
|
||||
@ -889,59 +851,56 @@ static uint8_t stfsm_wait_busy(struct stfsm *fsm)
|
||||
}
|
||||
|
||||
static int stfsm_read_status(struct stfsm *fsm, uint8_t cmd,
|
||||
uint8_t *status)
|
||||
uint8_t *data, int bytes)
|
||||
{
|
||||
struct stfsm_seq *seq = &stfsm_seq_read_status_fifo;
|
||||
uint32_t tmp;
|
||||
uint8_t *t = (uint8_t *)&tmp;
|
||||
int i;
|
||||
|
||||
dev_dbg(fsm->dev, "reading STA[%s]\n",
|
||||
(cmd == FLASH_CMD_RDSR) ? "1" : "2");
|
||||
dev_dbg(fsm->dev, "read 'status' register [0x%02x], %d byte(s)\n",
|
||||
cmd, bytes);
|
||||
|
||||
seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
|
||||
SEQ_OPC_CYCLES(8) |
|
||||
BUG_ON(bytes != 1 && bytes != 2);
|
||||
|
||||
seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(cmd)),
|
||||
|
||||
stfsm_load_seq(fsm, seq);
|
||||
|
||||
stfsm_read_fifo(fsm, &tmp, 4);
|
||||
|
||||
*status = (uint8_t)(tmp >> 24);
|
||||
for (i = 0; i < bytes; i++)
|
||||
data[i] = t[i];
|
||||
|
||||
stfsm_wait_seq(fsm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stfsm_write_status(struct stfsm *fsm, uint16_t status,
|
||||
int sta_bytes)
|
||||
static int stfsm_write_status(struct stfsm *fsm, uint8_t cmd,
|
||||
uint16_t data, int bytes, int wait_busy)
|
||||
{
|
||||
struct stfsm_seq *seq = &stfsm_seq_write_status;
|
||||
|
||||
dev_dbg(fsm->dev, "writing STA[%s] 0x%04x\n",
|
||||
(sta_bytes == 1) ? "1" : "1+2", status);
|
||||
dev_dbg(fsm->dev,
|
||||
"write 'status' register [0x%02x], %d byte(s), 0x%04x\n"
|
||||
" %s wait-busy\n", cmd, bytes, data, wait_busy ? "with" : "no");
|
||||
|
||||
seq->status = (uint32_t)status | STA_PADS_1 | STA_CSDEASSERT;
|
||||
seq->seq[2] = (sta_bytes == 1) ?
|
||||
STFSM_INST_STA_WR1 : STFSM_INST_STA_WR1_2;
|
||||
BUG_ON(bytes != 1 && bytes != 2);
|
||||
|
||||
seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(cmd));
|
||||
|
||||
seq->status = (uint32_t)data | STA_PADS_1 | STA_CSDEASSERT;
|
||||
seq->seq[2] = (bytes == 1) ? STFSM_INST_STA_WR1 : STFSM_INST_STA_WR1_2;
|
||||
|
||||
stfsm_load_seq(fsm, seq);
|
||||
|
||||
stfsm_wait_seq(fsm);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int stfsm_wrvcr(struct stfsm *fsm, uint8_t data)
|
||||
{
|
||||
struct stfsm_seq *seq = &stfsm_seq_wrvcr;
|
||||
|
||||
dev_dbg(fsm->dev, "writing VCR 0x%02x\n", data);
|
||||
|
||||
seq->status = (STA_DATA_BYTE1(data) | STA_PADS_1 | STA_CSDEASSERT);
|
||||
|
||||
stfsm_load_seq(fsm, seq);
|
||||
|
||||
stfsm_wait_seq(fsm);
|
||||
if (wait_busy)
|
||||
stfsm_wait_busy(fsm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1027,7 +986,7 @@ static void stfsm_prepare_rw_seq(struct stfsm *fsm,
|
||||
if (cfg->write)
|
||||
seq->seq_opc[i++] = (SEQ_OPC_PADS_1 |
|
||||
SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WREN) |
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_WREN) |
|
||||
SEQ_OPC_CSDEASSERT);
|
||||
|
||||
/* Address configuration (24 or 32-bit addresses) */
|
||||
@ -1149,31 +1108,36 @@ static int stfsm_mx25_config(struct stfsm *fsm)
|
||||
stfsm_mx25_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr);
|
||||
|
||||
soc_reset = stfsm_can_handle_soc_reset(fsm);
|
||||
if (soc_reset || !fsm->booted_from_spi) {
|
||||
if (soc_reset || !fsm->booted_from_spi)
|
||||
/* If we can handle SoC resets, we enable 32-bit address
|
||||
* mode pervasively */
|
||||
stfsm_enter_32bit_addr(fsm, 1);
|
||||
|
||||
} else {
|
||||
else
|
||||
/* Else, enable/disable 32-bit addressing before/after
|
||||
* each operation */
|
||||
fsm->configuration = (CFG_READ_TOGGLE_32BIT_ADDR |
|
||||
CFG_WRITE_TOGGLE_32BIT_ADDR |
|
||||
CFG_ERASESEC_TOGGLE_32BIT_ADDR);
|
||||
/* It seems a small delay is required after exiting
|
||||
* 32-bit mode following a write operation. The issue
|
||||
* is under investigation.
|
||||
*/
|
||||
fsm->configuration |= CFG_WRITE_EX_32BIT_ADDR_DELAY;
|
||||
}
|
||||
}
|
||||
|
||||
/* For QUAD mode, set 'QE' STATUS bit */
|
||||
/* Check status of 'QE' bit, update if required. */
|
||||
stfsm_read_status(fsm, SPINOR_OP_RDSR, &sta, 1);
|
||||
data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
|
||||
if (data_pads == 4) {
|
||||
stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta);
|
||||
sta |= MX25_STATUS_QE;
|
||||
stfsm_write_status(fsm, sta, 1);
|
||||
if (!(sta & MX25_STATUS_QE)) {
|
||||
/* Set 'QE' */
|
||||
sta |= MX25_STATUS_QE;
|
||||
|
||||
stfsm_write_status(fsm, SPINOR_OP_WRSR, sta, 1, 1);
|
||||
}
|
||||
} else {
|
||||
if (sta & MX25_STATUS_QE) {
|
||||
/* Clear 'QE' */
|
||||
sta &= ~MX25_STATUS_QE;
|
||||
|
||||
stfsm_write_status(fsm, SPINOR_OP_WRSR, sta, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1239,7 +1203,7 @@ static int stfsm_n25q_config(struct stfsm *fsm)
|
||||
*/
|
||||
vcr = (N25Q_VCR_DUMMY_CYCLES(8) | N25Q_VCR_XIP_DISABLED |
|
||||
N25Q_VCR_WRAP_CONT);
|
||||
stfsm_wrvcr(fsm, vcr);
|
||||
stfsm_write_status(fsm, N25Q_CMD_WRVCR, vcr, 1, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1297,7 +1261,7 @@ static void stfsm_s25fl_write_dyb(struct stfsm *fsm, uint32_t offs, uint8_t dby)
|
||||
{
|
||||
struct stfsm_seq seq = {
|
||||
.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WREN) |
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_WREN) |
|
||||
SEQ_OPC_CSDEASSERT),
|
||||
.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(S25FL_CMD_DYBWR)),
|
||||
@ -1337,7 +1301,7 @@ static int stfsm_s25fl_clear_status_reg(struct stfsm *fsm)
|
||||
SEQ_OPC_CSDEASSERT),
|
||||
.seq_opc[1] = (SEQ_OPC_PADS_1 |
|
||||
SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WRDI) |
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_WRDI) |
|
||||
SEQ_OPC_CSDEASSERT),
|
||||
.seq = {
|
||||
STFSM_INST_CMD1,
|
||||
@ -1367,6 +1331,7 @@ static int stfsm_s25fl_config(struct stfsm *fsm)
|
||||
uint32_t offs;
|
||||
uint16_t sta_wr;
|
||||
uint8_t sr1, cr1, dyb;
|
||||
int update_sr = 0;
|
||||
int ret;
|
||||
|
||||
if (flags & FLASH_FLAG_32BIT_ADDR) {
|
||||
@ -1414,34 +1379,28 @@ static int stfsm_s25fl_config(struct stfsm *fsm)
|
||||
}
|
||||
}
|
||||
|
||||
/* Check status of 'QE' bit */
|
||||
/* Check status of 'QE' bit, update if required. */
|
||||
stfsm_read_status(fsm, SPINOR_OP_RDSR2, &cr1, 1);
|
||||
data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
|
||||
stfsm_read_status(fsm, FLASH_CMD_RDSR2, &cr1);
|
||||
if (data_pads == 4) {
|
||||
if (!(cr1 & STFSM_S25FL_CONFIG_QE)) {
|
||||
/* Set 'QE' */
|
||||
cr1 |= STFSM_S25FL_CONFIG_QE;
|
||||
|
||||
stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1);
|
||||
sta_wr = ((uint16_t)cr1 << 8) | sr1;
|
||||
|
||||
stfsm_write_status(fsm, sta_wr, 2);
|
||||
|
||||
stfsm_wait_busy(fsm);
|
||||
update_sr = 1;
|
||||
}
|
||||
} else {
|
||||
if ((cr1 & STFSM_S25FL_CONFIG_QE)) {
|
||||
if (cr1 & STFSM_S25FL_CONFIG_QE) {
|
||||
/* Clear 'QE' */
|
||||
cr1 &= ~STFSM_S25FL_CONFIG_QE;
|
||||
|
||||
stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1);
|
||||
sta_wr = ((uint16_t)cr1 << 8) | sr1;
|
||||
|
||||
stfsm_write_status(fsm, sta_wr, 2);
|
||||
|
||||
stfsm_wait_busy(fsm);
|
||||
update_sr = 1;
|
||||
}
|
||||
|
||||
}
|
||||
if (update_sr) {
|
||||
stfsm_read_status(fsm, SPINOR_OP_RDSR, &sr1, 1);
|
||||
sta_wr = ((uint16_t)cr1 << 8) | sr1;
|
||||
stfsm_write_status(fsm, SPINOR_OP_WRSR, sta_wr, 2, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1456,27 +1415,36 @@ static int stfsm_s25fl_config(struct stfsm *fsm)
|
||||
static int stfsm_w25q_config(struct stfsm *fsm)
|
||||
{
|
||||
uint32_t data_pads;
|
||||
uint16_t sta_wr;
|
||||
uint8_t sta1, sta2;
|
||||
uint8_t sr1, sr2;
|
||||
uint16_t sr_wr;
|
||||
int update_sr = 0;
|
||||
int ret;
|
||||
|
||||
ret = stfsm_prepare_rwe_seqs_default(fsm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* If using QUAD mode, set QE STATUS bit */
|
||||
/* Check status of 'QE' bit, update if required. */
|
||||
stfsm_read_status(fsm, SPINOR_OP_RDSR2, &sr2, 1);
|
||||
data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
|
||||
if (data_pads == 4) {
|
||||
stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta1);
|
||||
stfsm_read_status(fsm, FLASH_CMD_RDSR2, &sta2);
|
||||
|
||||
sta_wr = ((uint16_t)sta2 << 8) | sta1;
|
||||
|
||||
sta_wr |= W25Q_STATUS_QE;
|
||||
|
||||
stfsm_write_status(fsm, sta_wr, 2);
|
||||
|
||||
stfsm_wait_busy(fsm);
|
||||
if (!(sr2 & W25Q_STATUS_QE)) {
|
||||
/* Set 'QE' */
|
||||
sr2 |= W25Q_STATUS_QE;
|
||||
update_sr = 1;
|
||||
}
|
||||
} else {
|
||||
if (sr2 & W25Q_STATUS_QE) {
|
||||
/* Clear 'QE' */
|
||||
sr2 &= ~W25Q_STATUS_QE;
|
||||
update_sr = 1;
|
||||
}
|
||||
}
|
||||
if (update_sr) {
|
||||
/* Write status register */
|
||||
stfsm_read_status(fsm, SPINOR_OP_RDSR, &sr1, 1);
|
||||
sr_wr = ((uint16_t)sr2 << 8) | sr1;
|
||||
stfsm_write_status(fsm, SPINOR_OP_WRSR, sr_wr, 2, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1506,7 +1474,7 @@ static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size,
|
||||
read_mask = (data_pads << 2) - 1;
|
||||
|
||||
/* Handle non-aligned buf */
|
||||
p = ((uint32_t)buf & 0x3) ? (uint8_t *)page_buf : buf;
|
||||
p = ((uintptr_t)buf & 0x3) ? (uint8_t *)page_buf : buf;
|
||||
|
||||
/* Handle non-aligned size */
|
||||
size_ub = (size + read_mask) & ~read_mask;
|
||||
@ -1528,7 +1496,7 @@ static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size,
|
||||
}
|
||||
|
||||
/* Handle non-aligned buf */
|
||||
if ((uint32_t)buf & 0x3)
|
||||
if ((uintptr_t)buf & 0x3)
|
||||
memcpy(buf, page_buf, size);
|
||||
|
||||
/* Wait for sequence to finish */
|
||||
@ -1570,7 +1538,7 @@ static int stfsm_write(struct stfsm *fsm, const uint8_t *buf,
|
||||
write_mask = (data_pads << 2) - 1;
|
||||
|
||||
/* Handle non-aligned buf */
|
||||
if ((uint32_t)buf & 0x3) {
|
||||
if ((uintptr_t)buf & 0x3) {
|
||||
memcpy(page_buf, buf, size);
|
||||
p = (uint8_t *)page_buf;
|
||||
} else {
|
||||
@ -1628,11 +1596,8 @@ static int stfsm_write(struct stfsm *fsm, const uint8_t *buf,
|
||||
stfsm_s25fl_clear_status_reg(fsm);
|
||||
|
||||
/* Exit 32-bit address mode, if required */
|
||||
if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR) {
|
||||
if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR)
|
||||
stfsm_enter_32bit_addr(fsm, 0);
|
||||
if (fsm->configuration & CFG_WRITE_EX_32BIT_ADDR_DELAY)
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1736,7 +1701,7 @@ static int stfsm_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
|
||||
while (len) {
|
||||
/* Write up to page boundary */
|
||||
bytes = min(FLASH_PAGESIZE - page_offs, len);
|
||||
bytes = min_t(size_t, FLASH_PAGESIZE - page_offs, len);
|
||||
|
||||
ret = stfsm_write(fsm, b, bytes, to);
|
||||
if (ret)
|
||||
@ -1935,6 +1900,13 @@ static int stfsm_init(struct stfsm *fsm)
|
||||
fsm->base + SPI_CONFIGDATA);
|
||||
writel(STFSM_DEFAULT_WR_TIME, fsm->base + SPI_STATUS_WR_TIME_REG);
|
||||
|
||||
/*
|
||||
* Set the FSM 'WAIT' delay to the minimum workable value. Note, for
|
||||
* our purposes, the WAIT instruction is used purely to achieve
|
||||
* "sequence validity" rather than actually implement a delay.
|
||||
*/
|
||||
writel(0x00000001, fsm->base + SPI_PROGRAM_ERASE_TIME);
|
||||
|
||||
/* Clear FIFO, just in case */
|
||||
stfsm_clear_fifo(fsm);
|
||||
|
||||
@ -2086,7 +2058,7 @@ static int stfsm_remove(struct platform_device *pdev)
|
||||
return mtd_device_unregister(&fsm->mtd);
|
||||
}
|
||||
|
||||
static struct of_device_id stfsm_match[] = {
|
||||
static const struct of_device_id stfsm_match[] = {
|
||||
{ .compatible = "st,spi-fsm", },
|
||||
{},
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
menu "LPDDR flash memory drivers"
|
||||
depends on MTD!=n
|
||||
menu "LPDDR & LPDDR2 PCM memory drivers"
|
||||
depends on MTD
|
||||
|
||||
config MTD_LPDDR
|
||||
tristate "Support for LPDDR flash chips"
|
||||
@ -17,4 +17,13 @@ config MTD_QINFO_PROBE
|
||||
Window QINFO interface, permits software to be used for entire
|
||||
families of devices. This serves similar purpose of CFI on legacy
|
||||
Flash products
|
||||
|
||||
config MTD_LPDDR2_NVM
|
||||
# ARM dependency is only for writel_relaxed()
|
||||
depends on MTD && ARM
|
||||
tristate "Support for LPDDR2-NVM flash chips"
|
||||
help
|
||||
This option enables support of PCM memories with a LPDDR2-NVM
|
||||
(Low power double data rate 2) interface.
|
||||
|
||||
endmenu
|
||||
|
@ -4,3 +4,4 @@
|
||||
|
||||
obj-$(CONFIG_MTD_QINFO_PROBE) += qinfo_probe.o
|
||||
obj-$(CONFIG_MTD_LPDDR) += lpddr_cmds.o
|
||||
obj-$(CONFIG_MTD_LPDDR2_NVM) += lpddr2_nvm.o
|
||||
|
507
drivers/mtd/lpddr/lpddr2_nvm.c
Normal file
507
drivers/mtd/lpddr/lpddr2_nvm.c
Normal file
@ -0,0 +1,507 @@
|
||||
/*
|
||||
* LPDDR2-NVM MTD driver. This module provides read, write, erase, lock/unlock
|
||||
* support for LPDDR2-NVM PCM memories
|
||||
*
|
||||
* Copyright © 2012 Micron Technology, Inc.
|
||||
*
|
||||
* Vincenzo Aliberti <vincenzo.aliberti@gmail.com>
|
||||
* Domenico Manna <domenico.manna@gmail.com>
|
||||
* Many thanks to Andrea Vigilante for initial enabling
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
/* Parameters */
|
||||
#define ERASE_BLOCKSIZE (0x00020000/2) /* in Word */
|
||||
#define WRITE_BUFFSIZE (0x00000400/2) /* in Word */
|
||||
#define OW_BASE_ADDRESS 0x00000000 /* OW offset */
|
||||
#define BUS_WIDTH 0x00000020 /* x32 devices */
|
||||
|
||||
/* PFOW symbols address offset */
|
||||
#define PFOW_QUERY_STRING_P (0x0000/2) /* in Word */
|
||||
#define PFOW_QUERY_STRING_F (0x0002/2) /* in Word */
|
||||
#define PFOW_QUERY_STRING_O (0x0004/2) /* in Word */
|
||||
#define PFOW_QUERY_STRING_W (0x0006/2) /* in Word */
|
||||
|
||||
/* OW registers address */
|
||||
#define CMD_CODE_OFS (0x0080/2) /* in Word */
|
||||
#define CMD_DATA_OFS (0x0084/2) /* in Word */
|
||||
#define CMD_ADD_L_OFS (0x0088/2) /* in Word */
|
||||
#define CMD_ADD_H_OFS (0x008A/2) /* in Word */
|
||||
#define MPR_L_OFS (0x0090/2) /* in Word */
|
||||
#define MPR_H_OFS (0x0092/2) /* in Word */
|
||||
#define CMD_EXEC_OFS (0x00C0/2) /* in Word */
|
||||
#define STATUS_REG_OFS (0x00CC/2) /* in Word */
|
||||
#define PRG_BUFFER_OFS (0x0010/2) /* in Word */
|
||||
|
||||
/* Datamask */
|
||||
#define MR_CFGMASK 0x8000
|
||||
#define SR_OK_DATAMASK 0x0080
|
||||
|
||||
/* LPDDR2-NVM Commands */
|
||||
#define LPDDR2_NVM_LOCK 0x0061
|
||||
#define LPDDR2_NVM_UNLOCK 0x0062
|
||||
#define LPDDR2_NVM_SW_PROGRAM 0x0041
|
||||
#define LPDDR2_NVM_SW_OVERWRITE 0x0042
|
||||
#define LPDDR2_NVM_BUF_PROGRAM 0x00E9
|
||||
#define LPDDR2_NVM_BUF_OVERWRITE 0x00EA
|
||||
#define LPDDR2_NVM_ERASE 0x0020
|
||||
|
||||
/* LPDDR2-NVM Registers offset */
|
||||
#define LPDDR2_MODE_REG_DATA 0x0040
|
||||
#define LPDDR2_MODE_REG_CFG 0x0050
|
||||
|
||||
/*
|
||||
* Internal Type Definitions
|
||||
* pcm_int_data contains memory controller details:
|
||||
* @reg_data : LPDDR2_MODE_REG_DATA register address after remapping
|
||||
* @reg_cfg : LPDDR2_MODE_REG_CFG register address after remapping
|
||||
* &bus_width: memory bus-width (eg: x16 2 Bytes, x32 4 Bytes)
|
||||
*/
|
||||
struct pcm_int_data {
|
||||
void __iomem *ctl_regs;
|
||||
int bus_width;
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(lpdd2_nvm_mutex);
|
||||
|
||||
/*
|
||||
* Build a map_word starting from an u_long
|
||||
*/
|
||||
static inline map_word build_map_word(u_long myword)
|
||||
{
|
||||
map_word val = { {0} };
|
||||
val.x[0] = myword;
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build Mode Register Configuration DataMask based on device bus-width
|
||||
*/
|
||||
static inline u_int build_mr_cfgmask(u_int bus_width)
|
||||
{
|
||||
u_int val = MR_CFGMASK;
|
||||
|
||||
if (bus_width == 0x0004) /* x32 device */
|
||||
val = val << 16;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build Status Register OK DataMask based on device bus-width
|
||||
*/
|
||||
static inline u_int build_sr_ok_datamask(u_int bus_width)
|
||||
{
|
||||
u_int val = SR_OK_DATAMASK;
|
||||
|
||||
if (bus_width == 0x0004) /* x32 device */
|
||||
val = (val << 16)+val;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Evaluates Overlay Window Control Registers address
|
||||
*/
|
||||
static inline u_long ow_reg_add(struct map_info *map, u_long offset)
|
||||
{
|
||||
u_long val = 0;
|
||||
struct pcm_int_data *pcm_data = map->fldrv_priv;
|
||||
|
||||
val = map->pfow_base + offset*pcm_data->bus_width;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable lpddr2-nvm Overlay Window
|
||||
* Overlay Window is a memory mapped area containing all LPDDR2-NVM registers
|
||||
* used by device commands as well as uservisible resources like Device Status
|
||||
* Register, Device ID, etc
|
||||
*/
|
||||
static inline void ow_enable(struct map_info *map)
|
||||
{
|
||||
struct pcm_int_data *pcm_data = map->fldrv_priv;
|
||||
|
||||
writel_relaxed(build_mr_cfgmask(pcm_data->bus_width) | 0x18,
|
||||
pcm_data->ctl_regs + LPDDR2_MODE_REG_CFG);
|
||||
writel_relaxed(0x01, pcm_data->ctl_regs + LPDDR2_MODE_REG_DATA);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable lpddr2-nvm Overlay Window
|
||||
* Overlay Window is a memory mapped area containing all LPDDR2-NVM registers
|
||||
* used by device commands as well as uservisible resources like Device Status
|
||||
* Register, Device ID, etc
|
||||
*/
|
||||
static inline void ow_disable(struct map_info *map)
|
||||
{
|
||||
struct pcm_int_data *pcm_data = map->fldrv_priv;
|
||||
|
||||
writel_relaxed(build_mr_cfgmask(pcm_data->bus_width) | 0x18,
|
||||
pcm_data->ctl_regs + LPDDR2_MODE_REG_CFG);
|
||||
writel_relaxed(0x02, pcm_data->ctl_regs + LPDDR2_MODE_REG_DATA);
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute lpddr2-nvm operations
|
||||
*/
|
||||
static int lpddr2_nvm_do_op(struct map_info *map, u_long cmd_code,
|
||||
u_long cmd_data, u_long cmd_add, u_long cmd_mpr, u_char *buf)
|
||||
{
|
||||
map_word add_l = { {0} }, add_h = { {0} }, mpr_l = { {0} },
|
||||
mpr_h = { {0} }, data_l = { {0} }, cmd = { {0} },
|
||||
exec_cmd = { {0} }, sr;
|
||||
map_word data_h = { {0} }; /* only for 2x x16 devices stacked */
|
||||
u_long i, status_reg, prg_buff_ofs;
|
||||
struct pcm_int_data *pcm_data = map->fldrv_priv;
|
||||
u_int sr_ok_datamask = build_sr_ok_datamask(pcm_data->bus_width);
|
||||
|
||||
/* Builds low and high words for OW Control Registers */
|
||||
add_l.x[0] = cmd_add & 0x0000FFFF;
|
||||
add_h.x[0] = (cmd_add >> 16) & 0x0000FFFF;
|
||||
mpr_l.x[0] = cmd_mpr & 0x0000FFFF;
|
||||
mpr_h.x[0] = (cmd_mpr >> 16) & 0x0000FFFF;
|
||||
cmd.x[0] = cmd_code & 0x0000FFFF;
|
||||
exec_cmd.x[0] = 0x0001;
|
||||
data_l.x[0] = cmd_data & 0x0000FFFF;
|
||||
data_h.x[0] = (cmd_data >> 16) & 0x0000FFFF; /* only for 2x x16 */
|
||||
|
||||
/* Set Overlay Window Control Registers */
|
||||
map_write(map, cmd, ow_reg_add(map, CMD_CODE_OFS));
|
||||
map_write(map, data_l, ow_reg_add(map, CMD_DATA_OFS));
|
||||
map_write(map, add_l, ow_reg_add(map, CMD_ADD_L_OFS));
|
||||
map_write(map, add_h, ow_reg_add(map, CMD_ADD_H_OFS));
|
||||
map_write(map, mpr_l, ow_reg_add(map, MPR_L_OFS));
|
||||
map_write(map, mpr_h, ow_reg_add(map, MPR_H_OFS));
|
||||
if (pcm_data->bus_width == 0x0004) { /* 2x16 devices stacked */
|
||||
map_write(map, cmd, ow_reg_add(map, CMD_CODE_OFS) + 2);
|
||||
map_write(map, data_h, ow_reg_add(map, CMD_DATA_OFS) + 2);
|
||||
map_write(map, add_l, ow_reg_add(map, CMD_ADD_L_OFS) + 2);
|
||||
map_write(map, add_h, ow_reg_add(map, CMD_ADD_H_OFS) + 2);
|
||||
map_write(map, mpr_l, ow_reg_add(map, MPR_L_OFS) + 2);
|
||||
map_write(map, mpr_h, ow_reg_add(map, MPR_H_OFS) + 2);
|
||||
}
|
||||
|
||||
/* Fill Program Buffer */
|
||||
if ((cmd_code == LPDDR2_NVM_BUF_PROGRAM) ||
|
||||
(cmd_code == LPDDR2_NVM_BUF_OVERWRITE)) {
|
||||
prg_buff_ofs = (map_read(map,
|
||||
ow_reg_add(map, PRG_BUFFER_OFS))).x[0];
|
||||
for (i = 0; i < cmd_mpr; i++) {
|
||||
map_write(map, build_map_word(buf[i]), map->pfow_base +
|
||||
prg_buff_ofs + i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Command Execute */
|
||||
map_write(map, exec_cmd, ow_reg_add(map, CMD_EXEC_OFS));
|
||||
if (pcm_data->bus_width == 0x0004) /* 2x16 devices stacked */
|
||||
map_write(map, exec_cmd, ow_reg_add(map, CMD_EXEC_OFS) + 2);
|
||||
|
||||
/* Status Register Check */
|
||||
do {
|
||||
sr = map_read(map, ow_reg_add(map, STATUS_REG_OFS));
|
||||
status_reg = sr.x[0];
|
||||
if (pcm_data->bus_width == 0x0004) {/* 2x16 devices stacked */
|
||||
sr = map_read(map, ow_reg_add(map,
|
||||
STATUS_REG_OFS) + 2);
|
||||
status_reg += sr.x[0] << 16;
|
||||
}
|
||||
} while ((status_reg & sr_ok_datamask) != sr_ok_datamask);
|
||||
|
||||
return (((status_reg & sr_ok_datamask) == sr_ok_datamask) ? 0 : -EIO);
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute lpddr2-nvm operations @ block level
|
||||
*/
|
||||
static int lpddr2_nvm_do_block_op(struct mtd_info *mtd, loff_t start_add,
|
||||
uint64_t len, u_char block_op)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
u_long add, end_add;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&lpdd2_nvm_mutex);
|
||||
|
||||
ow_enable(map);
|
||||
|
||||
add = start_add;
|
||||
end_add = add + len;
|
||||
|
||||
do {
|
||||
ret = lpddr2_nvm_do_op(map, block_op, 0x00, add, add, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
add += mtd->erasesize;
|
||||
} while (add < end_add);
|
||||
|
||||
out:
|
||||
ow_disable(map);
|
||||
mutex_unlock(&lpdd2_nvm_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* verify presence of PFOW string
|
||||
*/
|
||||
static int lpddr2_nvm_pfow_present(struct map_info *map)
|
||||
{
|
||||
map_word pfow_val[4];
|
||||
unsigned int found = 1;
|
||||
|
||||
mutex_lock(&lpdd2_nvm_mutex);
|
||||
|
||||
ow_enable(map);
|
||||
|
||||
/* Load string from array */
|
||||
pfow_val[0] = map_read(map, ow_reg_add(map, PFOW_QUERY_STRING_P));
|
||||
pfow_val[1] = map_read(map, ow_reg_add(map, PFOW_QUERY_STRING_F));
|
||||
pfow_val[2] = map_read(map, ow_reg_add(map, PFOW_QUERY_STRING_O));
|
||||
pfow_val[3] = map_read(map, ow_reg_add(map, PFOW_QUERY_STRING_W));
|
||||
|
||||
/* Verify the string loaded vs expected */
|
||||
if (!map_word_equal(map, build_map_word('P'), pfow_val[0]))
|
||||
found = 0;
|
||||
if (!map_word_equal(map, build_map_word('F'), pfow_val[1]))
|
||||
found = 0;
|
||||
if (!map_word_equal(map, build_map_word('O'), pfow_val[2]))
|
||||
found = 0;
|
||||
if (!map_word_equal(map, build_map_word('W'), pfow_val[3]))
|
||||
found = 0;
|
||||
|
||||
ow_disable(map);
|
||||
|
||||
mutex_unlock(&lpdd2_nvm_mutex);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/*
|
||||
* lpddr2_nvm driver read method
|
||||
*/
|
||||
static int lpddr2_nvm_read(struct mtd_info *mtd, loff_t start_add,
|
||||
size_t len, size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
|
||||
mutex_lock(&lpdd2_nvm_mutex);
|
||||
|
||||
*retlen = len;
|
||||
|
||||
map_copy_from(map, buf, start_add, *retlen);
|
||||
|
||||
mutex_unlock(&lpdd2_nvm_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* lpddr2_nvm driver write method
|
||||
*/
|
||||
static int lpddr2_nvm_write(struct mtd_info *mtd, loff_t start_add,
|
||||
size_t len, size_t *retlen, const u_char *buf)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct pcm_int_data *pcm_data = map->fldrv_priv;
|
||||
u_long add, current_len, tot_len, target_len, my_data;
|
||||
u_char *write_buf = (u_char *)buf;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&lpdd2_nvm_mutex);
|
||||
|
||||
ow_enable(map);
|
||||
|
||||
/* Set start value for the variables */
|
||||
add = start_add;
|
||||
target_len = len;
|
||||
tot_len = 0;
|
||||
|
||||
while (tot_len < target_len) {
|
||||
if (!(IS_ALIGNED(add, mtd->writesize))) { /* do sw program */
|
||||
my_data = write_buf[tot_len];
|
||||
my_data += (write_buf[tot_len+1]) << 8;
|
||||
if (pcm_data->bus_width == 0x0004) {/* 2x16 devices */
|
||||
my_data += (write_buf[tot_len+2]) << 16;
|
||||
my_data += (write_buf[tot_len+3]) << 24;
|
||||
}
|
||||
ret = lpddr2_nvm_do_op(map, LPDDR2_NVM_SW_OVERWRITE,
|
||||
my_data, add, 0x00, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
add += pcm_data->bus_width;
|
||||
tot_len += pcm_data->bus_width;
|
||||
} else { /* do buffer program */
|
||||
current_len = min(target_len - tot_len,
|
||||
(u_long) mtd->writesize);
|
||||
ret = lpddr2_nvm_do_op(map, LPDDR2_NVM_BUF_OVERWRITE,
|
||||
0x00, add, current_len, write_buf + tot_len);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
add += current_len;
|
||||
tot_len += current_len;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
*retlen = tot_len;
|
||||
ow_disable(map);
|
||||
mutex_unlock(&lpdd2_nvm_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* lpddr2_nvm driver erase method
|
||||
*/
|
||||
static int lpddr2_nvm_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
{
|
||||
int ret = lpddr2_nvm_do_block_op(mtd, instr->addr, instr->len,
|
||||
LPDDR2_NVM_ERASE);
|
||||
if (!ret) {
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
mtd_erase_callback(instr);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* lpddr2_nvm driver unlock method
|
||||
*/
|
||||
static int lpddr2_nvm_unlock(struct mtd_info *mtd, loff_t start_add,
|
||||
uint64_t len)
|
||||
{
|
||||
return lpddr2_nvm_do_block_op(mtd, start_add, len, LPDDR2_NVM_UNLOCK);
|
||||
}
|
||||
|
||||
/*
|
||||
* lpddr2_nvm driver lock method
|
||||
*/
|
||||
static int lpddr2_nvm_lock(struct mtd_info *mtd, loff_t start_add,
|
||||
uint64_t len)
|
||||
{
|
||||
return lpddr2_nvm_do_block_op(mtd, start_add, len, LPDDR2_NVM_LOCK);
|
||||
}
|
||||
|
||||
/*
|
||||
* lpddr2_nvm driver probe method
|
||||
*/
|
||||
static int lpddr2_nvm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct map_info *map;
|
||||
struct mtd_info *mtd;
|
||||
struct resource *add_range;
|
||||
struct resource *control_regs;
|
||||
struct pcm_int_data *pcm_data;
|
||||
|
||||
/* Allocate memory control_regs data structures */
|
||||
pcm_data = devm_kzalloc(&pdev->dev, sizeof(*pcm_data), GFP_KERNEL);
|
||||
if (!pcm_data)
|
||||
return -ENOMEM;
|
||||
|
||||
pcm_data->bus_width = BUS_WIDTH;
|
||||
|
||||
/* Allocate memory for map_info & mtd_info data structures */
|
||||
map = devm_kzalloc(&pdev->dev, sizeof(*map), GFP_KERNEL);
|
||||
if (!map)
|
||||
return -ENOMEM;
|
||||
|
||||
mtd = devm_kzalloc(&pdev->dev, sizeof(*mtd), GFP_KERNEL);
|
||||
if (!mtd)
|
||||
return -ENOMEM;
|
||||
|
||||
/* lpddr2_nvm address range */
|
||||
add_range = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
/* Populate map_info data structure */
|
||||
*map = (struct map_info) {
|
||||
.virt = devm_ioremap_resource(&pdev->dev, add_range),
|
||||
.name = pdev->dev.init_name,
|
||||
.phys = add_range->start,
|
||||
.size = resource_size(add_range),
|
||||
.bankwidth = pcm_data->bus_width / 2,
|
||||
.pfow_base = OW_BASE_ADDRESS,
|
||||
.fldrv_priv = pcm_data,
|
||||
};
|
||||
if (IS_ERR(map->virt))
|
||||
return PTR_ERR(map->virt);
|
||||
|
||||
simple_map_init(map); /* fill with default methods */
|
||||
|
||||
control_regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
pcm_data->ctl_regs = devm_ioremap_resource(&pdev->dev, control_regs);
|
||||
if (IS_ERR(pcm_data->ctl_regs))
|
||||
return PTR_ERR(pcm_data->ctl_regs);
|
||||
|
||||
/* Populate mtd_info data structure */
|
||||
*mtd = (struct mtd_info) {
|
||||
.name = pdev->dev.init_name,
|
||||
.type = MTD_RAM,
|
||||
.priv = map,
|
||||
.size = resource_size(add_range),
|
||||
.erasesize = ERASE_BLOCKSIZE * pcm_data->bus_width,
|
||||
.writesize = 1,
|
||||
.writebufsize = WRITE_BUFFSIZE * pcm_data->bus_width,
|
||||
.flags = (MTD_CAP_NVRAM | MTD_POWERUP_LOCK),
|
||||
._read = lpddr2_nvm_read,
|
||||
._write = lpddr2_nvm_write,
|
||||
._erase = lpddr2_nvm_erase,
|
||||
._unlock = lpddr2_nvm_unlock,
|
||||
._lock = lpddr2_nvm_lock,
|
||||
};
|
||||
|
||||
/* Verify the presence of the device looking for PFOW string */
|
||||
if (!lpddr2_nvm_pfow_present(map)) {
|
||||
pr_err("device not recognized\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Parse partitions and register the MTD device */
|
||||
return mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* lpddr2_nvm driver remove method
|
||||
*/
|
||||
static int lpddr2_nvm_remove(struct platform_device *pdev)
|
||||
{
|
||||
return mtd_device_unregister(dev_get_drvdata(&pdev->dev));
|
||||
}
|
||||
|
||||
/* Initialize platform_driver data structure for lpddr2_nvm */
|
||||
static struct platform_driver lpddr2_nvm_drv = {
|
||||
.driver = {
|
||||
.name = "lpddr2_nvm",
|
||||
},
|
||||
.probe = lpddr2_nvm_probe,
|
||||
.remove = lpddr2_nvm_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(lpddr2_nvm_drv);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Vincenzo Aliberti <vincenzo.aliberti@gmail.com>");
|
||||
MODULE_DESCRIPTION("MTD driver for LPDDR2-NVM PCM memories");
|
@ -108,7 +108,7 @@ config MTD_SUN_UFLASH
|
||||
|
||||
config MTD_SC520CDP
|
||||
tristate "CFI Flash device mapped on AMD SC520 CDP"
|
||||
depends on X86 && MTD_CFI
|
||||
depends on (MELAN || COMPILE_TEST) && MTD_CFI
|
||||
help
|
||||
The SC520 CDP board has two banks of CFI-compliant chips and one
|
||||
Dual-in-line JEDEC chip. This 'mapping' driver supports that
|
||||
@ -116,7 +116,7 @@ config MTD_SC520CDP
|
||||
|
||||
config MTD_NETSC520
|
||||
tristate "CFI Flash device mapped on AMD NetSc520"
|
||||
depends on X86 && MTD_CFI
|
||||
depends on (MELAN || COMPILE_TEST) && MTD_CFI
|
||||
help
|
||||
This enables access routines for the flash chips on the AMD NetSc520
|
||||
demonstration board. If you have one of these boards and would like
|
||||
|
@ -183,7 +183,7 @@ static const struct sc520_par_table par_table[NUM_FLASH_BANKS] =
|
||||
|
||||
static void sc520cdp_setup_par(void)
|
||||
{
|
||||
volatile unsigned long __iomem *mmcr;
|
||||
unsigned long __iomem *mmcr;
|
||||
unsigned long mmcr_val;
|
||||
int i, j;
|
||||
|
||||
@ -203,11 +203,11 @@ static void sc520cdp_setup_par(void)
|
||||
*/
|
||||
for(i = 0; i < NUM_FLASH_BANKS; i++) { /* for each par_table entry */
|
||||
for(j = 0; j < NUM_SC520_PAR; j++) { /* for each PAR register */
|
||||
mmcr_val = mmcr[SC520_PAR(j)];
|
||||
mmcr_val = readl(&mmcr[SC520_PAR(j)]);
|
||||
/* if target device field matches, reprogram the PAR */
|
||||
if((mmcr_val & SC520_PAR_TRGDEV) == par_table[i].trgdev)
|
||||
{
|
||||
mmcr[SC520_PAR(j)] = par_table[i].new_par;
|
||||
writel(par_table[i].new_par, &mmcr[SC520_PAR(j)]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -33,28 +33,6 @@ struct map_info soleng_flash_map = {
|
||||
|
||||
static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||
|
||||
#ifdef CONFIG_MTD_SUPERH_RESERVE
|
||||
static struct mtd_partition superh_se_partitions[] = {
|
||||
/* Reserved for boot code, read-only */
|
||||
{
|
||||
.name = "flash_boot",
|
||||
.offset = 0x00000000,
|
||||
.size = CONFIG_MTD_SUPERH_RESERVE,
|
||||
.mask_flags = MTD_WRITEABLE,
|
||||
},
|
||||
/* All else is writable (e.g. JFFS) */
|
||||
{
|
||||
.name = "Flash FS",
|
||||
.offset = MTDPART_OFS_NXTBLK,
|
||||
.size = MTDPART_SIZ_FULL,
|
||||
}
|
||||
};
|
||||
#define NUM_PARTITIONS ARRAY_SIZE(superh_se_partitions)
|
||||
#else
|
||||
#define superh_se_partitions NULL
|
||||
#define NUM_PARTITIONS 0
|
||||
#endif /* CONFIG_MTD_SUPERH_RESERVE */
|
||||
|
||||
static int __init init_soleng_maps(void)
|
||||
{
|
||||
/* First probe at offset 0 */
|
||||
@ -92,8 +70,7 @@ static int __init init_soleng_maps(void)
|
||||
mtd_device_register(eprom_mtd, NULL, 0);
|
||||
}
|
||||
|
||||
mtd_device_parse_register(flash_mtd, probes, NULL,
|
||||
superh_se_partitions, NUM_PARTITIONS);
|
||||
mtd_device_parse_register(flash_mtd, probes, NULL, NULL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -87,6 +87,9 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
|
||||
if (req->cmd_type != REQ_TYPE_FS)
|
||||
return -EIO;
|
||||
|
||||
if (req->cmd_flags & REQ_FLUSH)
|
||||
return tr->flush(dev);
|
||||
|
||||
if (blk_rq_pos(req) + blk_rq_cur_sectors(req) >
|
||||
get_capacity(req->rq_disk))
|
||||
return -EIO;
|
||||
@ -407,6 +410,9 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
|
||||
if (!new->rq)
|
||||
goto error3;
|
||||
|
||||
if (tr->flush)
|
||||
blk_queue_flush(new->rq, REQ_FLUSH);
|
||||
|
||||
new->rq->queuedata = new;
|
||||
blk_queue_logical_block_size(new->rq, tr->blksize);
|
||||
|
||||
|
@ -568,13 +568,18 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,
|
||||
{
|
||||
struct mtd_write_req req;
|
||||
struct mtd_oob_ops ops;
|
||||
void __user *usr_data, *usr_oob;
|
||||
const void __user *usr_data, *usr_oob;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&req, argp, sizeof(req)) ||
|
||||
!access_ok(VERIFY_READ, req.usr_data, req.len) ||
|
||||
!access_ok(VERIFY_READ, req.usr_oob, req.ooblen))
|
||||
if (copy_from_user(&req, argp, sizeof(req)))
|
||||
return -EFAULT;
|
||||
|
||||
usr_data = (const void __user *)(uintptr_t)req.usr_data;
|
||||
usr_oob = (const void __user *)(uintptr_t)req.usr_oob;
|
||||
if (!access_ok(VERIFY_READ, usr_data, req.len) ||
|
||||
!access_ok(VERIFY_READ, usr_oob, req.ooblen))
|
||||
return -EFAULT;
|
||||
|
||||
if (!mtd->_write_oob)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
@ -583,10 +588,7 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,
|
||||
ops.ooblen = (size_t)req.ooblen;
|
||||
ops.ooboffs = 0;
|
||||
|
||||
usr_data = (void __user *)(uintptr_t)req.usr_data;
|
||||
usr_oob = (void __user *)(uintptr_t)req.usr_oob;
|
||||
|
||||
if (req.usr_data) {
|
||||
if (usr_data) {
|
||||
ops.datbuf = memdup_user(usr_data, ops.len);
|
||||
if (IS_ERR(ops.datbuf))
|
||||
return PTR_ERR(ops.datbuf);
|
||||
@ -594,7 +596,7 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,
|
||||
ops.datbuf = NULL;
|
||||
}
|
||||
|
||||
if (req.usr_oob) {
|
||||
if (usr_oob) {
|
||||
ops.oobbuf = memdup_user(usr_oob, ops.ooblen);
|
||||
if (IS_ERR(ops.oobbuf)) {
|
||||
kfree(ops.datbuf);
|
||||
|
@ -679,9 +679,6 @@ static int bf5xx_nand_remove(struct platform_device *pdev)
|
||||
peripheral_free_list(bfin_nfc_pin_req);
|
||||
bf5xx_nand_dma_remove(info);
|
||||
|
||||
/* free the common resources */
|
||||
kfree(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -742,10 +739,10 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (info == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto out_err_kzalloc;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
@ -790,7 +787,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
|
||||
/* initialise the hardware */
|
||||
err = bf5xx_nand_hw_init(info);
|
||||
if (err)
|
||||
goto out_err_hw_init;
|
||||
goto out_err;
|
||||
|
||||
/* setup hardware ECC data struct */
|
||||
if (hardware_ecc) {
|
||||
@ -827,9 +824,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
|
||||
|
||||
out_err_nand_scan:
|
||||
bf5xx_nand_dma_remove(info);
|
||||
out_err_hw_init:
|
||||
kfree(info);
|
||||
out_err_kzalloc:
|
||||
out_err:
|
||||
peripheral_free_list(bfin_nfc_pin_req);
|
||||
|
||||
return err;
|
||||
|
@ -1233,7 +1233,7 @@ static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
|
||||
return status;
|
||||
}
|
||||
|
||||
static void denali_erase(struct mtd_info *mtd, int page)
|
||||
static int denali_erase(struct mtd_info *mtd, int page)
|
||||
{
|
||||
struct denali_nand_info *denali = mtd_to_denali(mtd);
|
||||
|
||||
@ -1250,8 +1250,7 @@ static void denali_erase(struct mtd_info *mtd, int page)
|
||||
irq_status = wait_for_irq(denali, INTR_STATUS__ERASE_COMP |
|
||||
INTR_STATUS__ERASE_FAIL);
|
||||
|
||||
denali->status = (irq_status & INTR_STATUS__ERASE_FAIL) ?
|
||||
NAND_STATUS_FAIL : PASS;
|
||||
return (irq_status & INTR_STATUS__ERASE_FAIL) ? NAND_STATUS_FAIL : PASS;
|
||||
}
|
||||
|
||||
static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
|
||||
@ -1584,7 +1583,7 @@ int denali_init(struct denali_nand_info *denali)
|
||||
denali->nand.ecc.write_page_raw = denali_write_page_raw;
|
||||
denali->nand.ecc.read_oob = denali_read_oob;
|
||||
denali->nand.ecc.write_oob = denali_write_oob;
|
||||
denali->nand.erase_cmd = denali_erase;
|
||||
denali->nand.erase = denali_erase;
|
||||
|
||||
if (nand_scan_tail(&denali->mtd)) {
|
||||
ret = -ENXIO;
|
||||
|
@ -872,7 +872,7 @@ static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void docg4_erase_block(struct mtd_info *mtd, int page)
|
||||
static int docg4_erase_block(struct mtd_info *mtd, int page)
|
||||
{
|
||||
struct nand_chip *nand = mtd->priv;
|
||||
struct docg4_priv *doc = nand->priv;
|
||||
@ -916,6 +916,8 @@ static void docg4_erase_block(struct mtd_info *mtd, int page)
|
||||
write_nop(docptr);
|
||||
poll_status(doc);
|
||||
write_nop(docptr);
|
||||
|
||||
return nand->waitfunc(mtd, nand);
|
||||
}
|
||||
|
||||
static int write_page(struct mtd_info *mtd, struct nand_chip *nand,
|
||||
@ -1236,7 +1238,7 @@ static void __init init_mtd_structs(struct mtd_info *mtd)
|
||||
nand->block_markbad = docg4_block_markbad;
|
||||
nand->read_buf = docg4_read_buf;
|
||||
nand->write_buf = docg4_write_buf16;
|
||||
nand->erase_cmd = docg4_erase_block;
|
||||
nand->erase = docg4_erase_block;
|
||||
nand->ecc.read_page = docg4_read_page;
|
||||
nand->ecc.write_page = docg4_write_page;
|
||||
nand->ecc.read_page_raw = docg4_read_page_raw;
|
||||
|
@ -723,6 +723,19 @@ static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ECC will be calculated automatically, and errors will be detected in
|
||||
* waitfunc.
|
||||
*/
|
||||
static int fsl_elbc_write_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t offset, uint32_t data_len,
|
||||
const uint8_t *buf, int oob_required)
|
||||
{
|
||||
fsl_elbc_write_buf(mtd, buf, mtd->writesize);
|
||||
fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
|
||||
{
|
||||
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
|
||||
@ -761,6 +774,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
|
||||
|
||||
chip->ecc.read_page = fsl_elbc_read_page;
|
||||
chip->ecc.write_page = fsl_elbc_write_page;
|
||||
chip->ecc.write_subpage = fsl_elbc_write_subpage;
|
||||
|
||||
/* If CS Base Register selects full hardware ECC then use it */
|
||||
if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
|
||||
|
@ -56,7 +56,7 @@ struct fsl_ifc_nand_ctrl {
|
||||
struct nand_hw_control controller;
|
||||
struct fsl_ifc_mtd *chips[FSL_IFC_BANK_COUNT];
|
||||
|
||||
u8 __iomem *addr; /* Address of assigned IFC buffer */
|
||||
void __iomem *addr; /* Address of assigned IFC buffer */
|
||||
unsigned int page; /* Last page written to / read from */
|
||||
unsigned int read_bytes;/* Number of bytes read during command */
|
||||
unsigned int column; /* Saved column from SEQIN */
|
||||
@ -591,7 +591,10 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
* The chip always seems to report that it is
|
||||
* write-protected, even when it is not.
|
||||
*/
|
||||
setbits8(ifc_nand_ctrl->addr, NAND_STATUS_WP);
|
||||
if (chip->options & NAND_BUSWIDTH_16)
|
||||
setbits16(ifc_nand_ctrl->addr, NAND_STATUS_WP);
|
||||
else
|
||||
setbits8(ifc_nand_ctrl->addr, NAND_STATUS_WP);
|
||||
return;
|
||||
|
||||
case NAND_CMD_RESET:
|
||||
@ -636,7 +639,7 @@ static void fsl_ifc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
|
||||
len = bufsize - ifc_nand_ctrl->index;
|
||||
}
|
||||
|
||||
memcpy_toio(&ifc_nand_ctrl->addr[ifc_nand_ctrl->index], buf, len);
|
||||
memcpy_toio(ifc_nand_ctrl->addr + ifc_nand_ctrl->index, buf, len);
|
||||
ifc_nand_ctrl->index += len;
|
||||
}
|
||||
|
||||
@ -648,13 +651,16 @@ static uint8_t fsl_ifc_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct fsl_ifc_mtd *priv = chip->priv;
|
||||
unsigned int offset;
|
||||
|
||||
/*
|
||||
* If there are still bytes in the IFC buffer, then use the
|
||||
* next byte.
|
||||
*/
|
||||
if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes)
|
||||
return in_8(&ifc_nand_ctrl->addr[ifc_nand_ctrl->index++]);
|
||||
if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) {
|
||||
offset = ifc_nand_ctrl->index++;
|
||||
return in_8(ifc_nand_ctrl->addr + offset);
|
||||
}
|
||||
|
||||
dev_err(priv->dev, "%s: beyond end of buffer\n", __func__);
|
||||
return ERR_BYTE;
|
||||
@ -675,8 +681,7 @@ static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd)
|
||||
* next byte.
|
||||
*/
|
||||
if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) {
|
||||
data = in_be16((uint16_t __iomem *)&ifc_nand_ctrl->
|
||||
addr[ifc_nand_ctrl->index]);
|
||||
data = in_be16(ifc_nand_ctrl->addr + ifc_nand_ctrl->index);
|
||||
ifc_nand_ctrl->index += 2;
|
||||
return (uint8_t) data;
|
||||
}
|
||||
@ -701,7 +706,7 @@ static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
|
||||
|
||||
avail = min((unsigned int)len,
|
||||
ifc_nand_ctrl->read_bytes - ifc_nand_ctrl->index);
|
||||
memcpy_fromio(buf, &ifc_nand_ctrl->addr[ifc_nand_ctrl->index], avail);
|
||||
memcpy_fromio(buf, ifc_nand_ctrl->addr + ifc_nand_ctrl->index, avail);
|
||||
ifc_nand_ctrl->index += avail;
|
||||
|
||||
if (len > avail)
|
||||
|
@ -54,7 +54,7 @@
|
||||
#define MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0 11
|
||||
#define MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0 (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0)
|
||||
#define BF_BCH_FLASH0LAYOUT0_ECC0(v, x) \
|
||||
(GPMI_IS_MX6Q(x) \
|
||||
(GPMI_IS_MX6(x) \
|
||||
? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) \
|
||||
& MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0) \
|
||||
: (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) \
|
||||
@ -65,7 +65,7 @@
|
||||
#define MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14 \
|
||||
(0x1 << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14)
|
||||
#define BF_BCH_FLASH0LAYOUT0_GF(v, x) \
|
||||
((GPMI_IS_MX6Q(x) && ((v) == 14)) \
|
||||
((GPMI_IS_MX6(x) && ((v) == 14)) \
|
||||
? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14) \
|
||||
& MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14) \
|
||||
: 0 \
|
||||
@ -77,7 +77,7 @@
|
||||
#define MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \
|
||||
(0x3ff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)
|
||||
#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v, x) \
|
||||
(GPMI_IS_MX6Q(x) \
|
||||
(GPMI_IS_MX6(x) \
|
||||
? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \
|
||||
: ((v) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \
|
||||
)
|
||||
@ -96,7 +96,7 @@
|
||||
#define MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN 11
|
||||
#define MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN)
|
||||
#define BF_BCH_FLASH0LAYOUT1_ECCN(v, x) \
|
||||
(GPMI_IS_MX6Q(x) \
|
||||
(GPMI_IS_MX6(x) \
|
||||
? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) \
|
||||
& MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN) \
|
||||
: (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) \
|
||||
@ -107,7 +107,7 @@
|
||||
#define MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14 \
|
||||
(0x1 << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14)
|
||||
#define BF_BCH_FLASH0LAYOUT1_GF(v, x) \
|
||||
((GPMI_IS_MX6Q(x) && ((v) == 14)) \
|
||||
((GPMI_IS_MX6(x) && ((v) == 14)) \
|
||||
? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14) \
|
||||
& MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14) \
|
||||
: 0 \
|
||||
@ -119,7 +119,7 @@
|
||||
#define MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \
|
||||
(0x3ff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE)
|
||||
#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v, x) \
|
||||
(GPMI_IS_MX6Q(x) \
|
||||
(GPMI_IS_MX6(x) \
|
||||
? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \
|
||||
: ((v) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \
|
||||
)
|
||||
|
@ -861,7 +861,7 @@ static void gpmi_compute_edo_timing(struct gpmi_nand_data *this,
|
||||
struct resources *r = &this->resources;
|
||||
unsigned long rate = clk_get_rate(r->clock[0]);
|
||||
int mode = this->timing_mode;
|
||||
int dll_threshold = 16; /* in ns */
|
||||
int dll_threshold = this->devdata->max_chain_delay;
|
||||
unsigned long delay;
|
||||
unsigned long clk_period;
|
||||
int t_rea;
|
||||
@ -886,9 +886,6 @@ static void gpmi_compute_edo_timing(struct gpmi_nand_data *this,
|
||||
/* [3] for GPMI_HW_GPMI_CTRL1 */
|
||||
hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
|
||||
|
||||
if (GPMI_IS_MX6Q(this))
|
||||
dll_threshold = 12;
|
||||
|
||||
/*
|
||||
* Enlarge 10 times for the numerator and denominator in {3}.
|
||||
* This make us to get more accurate result.
|
||||
@ -974,7 +971,7 @@ int gpmi_extra_init(struct gpmi_nand_data *this)
|
||||
struct nand_chip *chip = &this->nand;
|
||||
|
||||
/* Enable the asynchronous EDO feature. */
|
||||
if (GPMI_IS_MX6Q(this) && chip->onfi_version) {
|
||||
if (GPMI_IS_MX6(this) && chip->onfi_version) {
|
||||
int mode = onfi_get_async_timing_mode(chip);
|
||||
|
||||
/* We only support the timing mode 4 and mode 5. */
|
||||
@ -1096,12 +1093,12 @@ int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip)
|
||||
if (GPMI_IS_MX23(this)) {
|
||||
mask = MX23_BM_GPMI_DEBUG_READY0 << chip;
|
||||
reg = readl(r->gpmi_regs + HW_GPMI_DEBUG);
|
||||
} else if (GPMI_IS_MX28(this) || GPMI_IS_MX6Q(this)) {
|
||||
} else if (GPMI_IS_MX28(this) || GPMI_IS_MX6(this)) {
|
||||
/*
|
||||
* In the imx6, all the ready/busy pins are bound
|
||||
* together. So we only need to check chip 0.
|
||||
*/
|
||||
if (GPMI_IS_MX6Q(this))
|
||||
if (GPMI_IS_MX6(this))
|
||||
chip = 0;
|
||||
|
||||
/* MX28 shares the same R/B register as MX6Q. */
|
||||
|
@ -53,6 +53,30 @@ static struct nand_ecclayout gpmi_hw_ecclayout = {
|
||||
.oobfree = { {.offset = 0, .length = 0} }
|
||||
};
|
||||
|
||||
static const struct gpmi_devdata gpmi_devdata_imx23 = {
|
||||
.type = IS_MX23,
|
||||
.bch_max_ecc_strength = 20,
|
||||
.max_chain_delay = 16,
|
||||
};
|
||||
|
||||
static const struct gpmi_devdata gpmi_devdata_imx28 = {
|
||||
.type = IS_MX28,
|
||||
.bch_max_ecc_strength = 20,
|
||||
.max_chain_delay = 16,
|
||||
};
|
||||
|
||||
static const struct gpmi_devdata gpmi_devdata_imx6q = {
|
||||
.type = IS_MX6Q,
|
||||
.bch_max_ecc_strength = 40,
|
||||
.max_chain_delay = 12,
|
||||
};
|
||||
|
||||
static const struct gpmi_devdata gpmi_devdata_imx6sx = {
|
||||
.type = IS_MX6SX,
|
||||
.bch_max_ecc_strength = 62,
|
||||
.max_chain_delay = 12,
|
||||
};
|
||||
|
||||
static irqreturn_t bch_irq(int irq, void *cookie)
|
||||
{
|
||||
struct gpmi_nand_data *this = cookie;
|
||||
@ -102,14 +126,8 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
|
||||
/* The mx23/mx28 only support the GF13. */
|
||||
if (geo->gf_len == 14)
|
||||
return false;
|
||||
|
||||
if (geo->ecc_strength > MXS_ECC_STRENGTH_MAX)
|
||||
return false;
|
||||
} else if (GPMI_IS_MX6Q(this)) {
|
||||
if (geo->ecc_strength > MX6_ECC_STRENGTH_MAX)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return geo->ecc_strength <= this->devdata->bch_max_ecc_strength;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -270,8 +288,7 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
|
||||
"We can not support this nand chip."
|
||||
" Its required ecc strength(%d) is beyond our"
|
||||
" capability(%d).\n", geo->ecc_strength,
|
||||
(GPMI_IS_MX6Q(this) ? MX6_ECC_STRENGTH_MAX
|
||||
: MXS_ECC_STRENGTH_MAX));
|
||||
this->devdata->bch_max_ecc_strength);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -572,7 +589,7 @@ static int gpmi_get_clks(struct gpmi_nand_data *this)
|
||||
}
|
||||
|
||||
/* Get extra clocks */
|
||||
if (GPMI_IS_MX6Q(this))
|
||||
if (GPMI_IS_MX6(this))
|
||||
extra_clks = extra_clks_for_mx6q;
|
||||
if (!extra_clks)
|
||||
return 0;
|
||||
@ -590,9 +607,9 @@ static int gpmi_get_clks(struct gpmi_nand_data *this)
|
||||
r->clock[i] = clk;
|
||||
}
|
||||
|
||||
if (GPMI_IS_MX6Q(this))
|
||||
if (GPMI_IS_MX6(this))
|
||||
/*
|
||||
* Set the default value for the gpmi clock in mx6q:
|
||||
* Set the default value for the gpmi clock.
|
||||
*
|
||||
* If you want to use the ONFI nand which is in the
|
||||
* Synchronous Mode, you should change the clock as you need.
|
||||
@ -1655,7 +1672,7 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
|
||||
* (1) the chip is imx6, and
|
||||
* (2) the size of the ECC parity is byte aligned.
|
||||
*/
|
||||
if (GPMI_IS_MX6Q(this) &&
|
||||
if (GPMI_IS_MX6(this) &&
|
||||
((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) {
|
||||
ecc->read_subpage = gpmi_ecc_read_subpage;
|
||||
chip->options |= NAND_SUBPAGE_READ;
|
||||
@ -1711,7 +1728,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
ret = nand_scan_ident(mtd, GPMI_IS_MX6Q(this) ? 2 : 1, NULL);
|
||||
ret = nand_scan_ident(mtd, GPMI_IS_MX6(this) ? 2 : 1, NULL);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
@ -1740,23 +1757,19 @@ err_out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct platform_device_id gpmi_ids[] = {
|
||||
{ .name = "imx23-gpmi-nand", .driver_data = IS_MX23, },
|
||||
{ .name = "imx28-gpmi-nand", .driver_data = IS_MX28, },
|
||||
{ .name = "imx6q-gpmi-nand", .driver_data = IS_MX6Q, },
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct of_device_id gpmi_nand_id_table[] = {
|
||||
{
|
||||
.compatible = "fsl,imx23-gpmi-nand",
|
||||
.data = (void *)&gpmi_ids[IS_MX23],
|
||||
.data = (void *)&gpmi_devdata_imx23,
|
||||
}, {
|
||||
.compatible = "fsl,imx28-gpmi-nand",
|
||||
.data = (void *)&gpmi_ids[IS_MX28],
|
||||
.data = (void *)&gpmi_devdata_imx28,
|
||||
}, {
|
||||
.compatible = "fsl,imx6q-gpmi-nand",
|
||||
.data = (void *)&gpmi_ids[IS_MX6Q],
|
||||
.data = (void *)&gpmi_devdata_imx6q,
|
||||
}, {
|
||||
.compatible = "fsl,imx6sx-gpmi-nand",
|
||||
.data = (void *)&gpmi_devdata_imx6sx,
|
||||
}, {}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gpmi_nand_id_table);
|
||||
@ -1767,18 +1780,18 @@ static int gpmi_nand_probe(struct platform_device *pdev)
|
||||
const struct of_device_id *of_id;
|
||||
int ret;
|
||||
|
||||
this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL);
|
||||
if (!this)
|
||||
return -ENOMEM;
|
||||
|
||||
of_id = of_match_device(gpmi_nand_id_table, &pdev->dev);
|
||||
if (of_id) {
|
||||
pdev->id_entry = of_id->data;
|
||||
this->devdata = of_id->data;
|
||||
} else {
|
||||
dev_err(&pdev->dev, "Failed to find the right device id.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL);
|
||||
if (!this)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, this);
|
||||
this->pdev = pdev;
|
||||
this->dev = &pdev->dev;
|
||||
@ -1823,7 +1836,6 @@ static struct platform_driver gpmi_nand_driver = {
|
||||
},
|
||||
.probe = gpmi_nand_probe,
|
||||
.remove = gpmi_nand_remove,
|
||||
.id_table = gpmi_ids,
|
||||
};
|
||||
module_platform_driver(gpmi_nand_driver);
|
||||
|
||||
|
@ -119,11 +119,25 @@ struct nand_timing {
|
||||
int8_t tRHOH_in_ns;
|
||||
};
|
||||
|
||||
enum gpmi_type {
|
||||
IS_MX23,
|
||||
IS_MX28,
|
||||
IS_MX6Q,
|
||||
IS_MX6SX
|
||||
};
|
||||
|
||||
struct gpmi_devdata {
|
||||
enum gpmi_type type;
|
||||
int bch_max_ecc_strength;
|
||||
int max_chain_delay; /* See the async EDO mode */
|
||||
};
|
||||
|
||||
struct gpmi_nand_data {
|
||||
/* flags */
|
||||
#define GPMI_ASYNC_EDO_ENABLED (1 << 0)
|
||||
#define GPMI_TIMING_INIT_OK (1 << 1)
|
||||
int flags;
|
||||
const struct gpmi_devdata *devdata;
|
||||
|
||||
/* System Interface */
|
||||
struct device *dev;
|
||||
@ -281,15 +295,11 @@ extern int gpmi_read_page(struct gpmi_nand_data *,
|
||||
#define STATUS_ERASED 0xff
|
||||
#define STATUS_UNCORRECTABLE 0xfe
|
||||
|
||||
/* BCH's bit correction capability. */
|
||||
#define MXS_ECC_STRENGTH_MAX 20 /* mx23 and mx28 */
|
||||
#define MX6_ECC_STRENGTH_MAX 40
|
||||
/* Use the devdata to distinguish different Archs. */
|
||||
#define GPMI_IS_MX23(x) ((x)->devdata->type == IS_MX23)
|
||||
#define GPMI_IS_MX28(x) ((x)->devdata->type == IS_MX28)
|
||||
#define GPMI_IS_MX6Q(x) ((x)->devdata->type == IS_MX6Q)
|
||||
#define GPMI_IS_MX6SX(x) ((x)->devdata->type == IS_MX6SX)
|
||||
|
||||
/* Use the platform_id to distinguish different Archs. */
|
||||
#define IS_MX23 0x0
|
||||
#define IS_MX28 0x1
|
||||
#define IS_MX6Q 0x2
|
||||
#define GPMI_IS_MX23(x) ((x)->pdev->id_entry->driver_data == IS_MX23)
|
||||
#define GPMI_IS_MX28(x) ((x)->pdev->id_entry->driver_data == IS_MX28)
|
||||
#define GPMI_IS_MX6Q(x) ((x)->pdev->id_entry->driver_data == IS_MX6Q)
|
||||
#define GPMI_IS_MX6(x) (GPMI_IS_MX6Q(x) || GPMI_IS_MX6SX(x))
|
||||
#endif
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
@ -1204,8 +1205,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
* ecc.pos. Let's make sure that there are no gaps in ECC positions.
|
||||
*/
|
||||
for (i = 0; i < eccfrag_len - 1; i++) {
|
||||
if (eccpos[i + start_step * chip->ecc.bytes] + 1 !=
|
||||
eccpos[i + start_step * chip->ecc.bytes + 1]) {
|
||||
if (eccpos[i + index] + 1 != eccpos[i + index + 1]) {
|
||||
gaps = 1;
|
||||
break;
|
||||
}
|
||||
@ -1501,6 +1501,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
||||
mtd->oobavail : mtd->oobsize;
|
||||
|
||||
uint8_t *bufpoi, *oob, *buf;
|
||||
int use_bufpoi;
|
||||
unsigned int max_bitflips = 0;
|
||||
int retry_mode = 0;
|
||||
bool ecc_fail = false;
|
||||
@ -1523,9 +1524,20 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
||||
bytes = min(mtd->writesize - col, readlen);
|
||||
aligned = (bytes == mtd->writesize);
|
||||
|
||||
if (!aligned)
|
||||
use_bufpoi = 1;
|
||||
else if (chip->options & NAND_USE_BOUNCE_BUFFER)
|
||||
use_bufpoi = !virt_addr_valid(buf);
|
||||
else
|
||||
use_bufpoi = 0;
|
||||
|
||||
/* Is the current page in the buffer? */
|
||||
if (realpage != chip->pagebuf || oob) {
|
||||
bufpoi = aligned ? buf : chip->buffers->databuf;
|
||||
bufpoi = use_bufpoi ? chip->buffers->databuf : buf;
|
||||
|
||||
if (use_bufpoi && aligned)
|
||||
pr_debug("%s: using read bounce buffer for buf@%p\n",
|
||||
__func__, buf);
|
||||
|
||||
read_retry:
|
||||
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
|
||||
@ -1547,7 +1559,7 @@ read_retry:
|
||||
ret = chip->ecc.read_page(mtd, chip, bufpoi,
|
||||
oob_required, page);
|
||||
if (ret < 0) {
|
||||
if (!aligned)
|
||||
if (use_bufpoi)
|
||||
/* Invalidate page cache */
|
||||
chip->pagebuf = -1;
|
||||
break;
|
||||
@ -1556,7 +1568,7 @@ read_retry:
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, ret);
|
||||
|
||||
/* Transfer not aligned data */
|
||||
if (!aligned) {
|
||||
if (use_bufpoi) {
|
||||
if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&
|
||||
!(mtd->ecc_stats.failed - ecc_failures) &&
|
||||
(ops->mode != MTD_OPS_RAW)) {
|
||||
@ -2376,11 +2388,23 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
||||
int bytes = mtd->writesize;
|
||||
int cached = writelen > bytes && page != blockmask;
|
||||
uint8_t *wbuf = buf;
|
||||
int use_bufpoi;
|
||||
int part_pagewr = (column || writelen < (mtd->writesize - 1));
|
||||
|
||||
/* Partial page write? */
|
||||
if (unlikely(column || writelen < (mtd->writesize - 1))) {
|
||||
if (part_pagewr)
|
||||
use_bufpoi = 1;
|
||||
else if (chip->options & NAND_USE_BOUNCE_BUFFER)
|
||||
use_bufpoi = !virt_addr_valid(buf);
|
||||
else
|
||||
use_bufpoi = 0;
|
||||
|
||||
/* Partial page write?, or need to use bounce buffer */
|
||||
if (use_bufpoi) {
|
||||
pr_debug("%s: using write bounce buffer for buf@%p\n",
|
||||
__func__, buf);
|
||||
cached = 0;
|
||||
bytes = min_t(int, bytes - column, (int) writelen);
|
||||
if (part_pagewr)
|
||||
bytes = min_t(int, bytes - column, writelen);
|
||||
chip->pagebuf = -1;
|
||||
memset(chip->buffers->databuf, 0xff, mtd->writesize);
|
||||
memcpy(&chip->buffers->databuf[column], buf, bytes);
|
||||
@ -2618,18 +2642,20 @@ out:
|
||||
}
|
||||
|
||||
/**
|
||||
* single_erase_cmd - [GENERIC] NAND standard block erase command function
|
||||
* single_erase - [GENERIC] NAND standard block erase command function
|
||||
* @mtd: MTD device structure
|
||||
* @page: the page address of the block which will be erased
|
||||
*
|
||||
* Standard erase command for NAND chips.
|
||||
* Standard erase command for NAND chips. Returns NAND status.
|
||||
*/
|
||||
static void single_erase_cmd(struct mtd_info *mtd, int page)
|
||||
static int single_erase(struct mtd_info *mtd, int page)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
/* Send commands to erase a block */
|
||||
chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
|
||||
chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
|
||||
|
||||
return chip->waitfunc(mtd, chip);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2710,9 +2736,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
||||
(page + pages_per_block))
|
||||
chip->pagebuf = -1;
|
||||
|
||||
chip->erase_cmd(mtd, page & chip->pagemask);
|
||||
|
||||
status = chip->waitfunc(mtd, chip);
|
||||
status = chip->erase(mtd, page & chip->pagemask);
|
||||
|
||||
/*
|
||||
* See if operation failed and additional status checks are
|
||||
@ -3607,7 +3631,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||
|
||||
chip->onfi_version = 0;
|
||||
if (!type->name || !type->pagesize) {
|
||||
/* Check is chip is ONFI compliant */
|
||||
/* Check if the chip is ONFI compliant */
|
||||
if (nand_flash_detect_onfi(mtd, chip, &busw))
|
||||
goto ident_done;
|
||||
|
||||
@ -3685,7 +3709,7 @@ ident_done:
|
||||
}
|
||||
|
||||
chip->badblockbits = 8;
|
||||
chip->erase_cmd = single_erase_cmd;
|
||||
chip->erase = single_erase;
|
||||
|
||||
/* Do not replace user supplied command function! */
|
||||
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
|
||||
@ -3770,6 +3794,39 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
||||
}
|
||||
EXPORT_SYMBOL(nand_scan_ident);
|
||||
|
||||
/*
|
||||
* Check if the chip configuration meet the datasheet requirements.
|
||||
|
||||
* If our configuration corrects A bits per B bytes and the minimum
|
||||
* required correction level is X bits per Y bytes, then we must ensure
|
||||
* both of the following are true:
|
||||
*
|
||||
* (1) A / B >= X / Y
|
||||
* (2) A >= X
|
||||
*
|
||||
* Requirement (1) ensures we can correct for the required bitflip density.
|
||||
* Requirement (2) ensures we can correct even when all bitflips are clumped
|
||||
* in the same sector.
|
||||
*/
|
||||
static bool nand_ecc_strength_good(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
int corr, ds_corr;
|
||||
|
||||
if (ecc->size == 0 || chip->ecc_step_ds == 0)
|
||||
/* Not enough information */
|
||||
return true;
|
||||
|
||||
/*
|
||||
* We get the number of corrected bits per page to compare
|
||||
* the correction density.
|
||||
*/
|
||||
corr = (mtd->writesize * ecc->strength) / ecc->size;
|
||||
ds_corr = (mtd->writesize * chip->ecc_strength_ds) / chip->ecc_step_ds;
|
||||
|
||||
return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_scan_tail - [NAND Interface] Scan for the NAND device
|
||||
@ -3990,6 +4047,9 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||
ecc->layout->oobavail += ecc->layout->oobfree[i].length;
|
||||
mtd->oobavail = ecc->layout->oobavail;
|
||||
|
||||
/* ECC sanity check: warn noisily if it's too weak */
|
||||
WARN_ON(!nand_ecc_strength_good(mtd));
|
||||
|
||||
/*
|
||||
* Set the number of read / write steps for one page depending on ECC
|
||||
* mode.
|
||||
@ -4023,8 +4083,16 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||
chip->pagebuf = -1;
|
||||
|
||||
/* Large page NAND with SOFT_ECC should support subpage reads */
|
||||
if ((ecc->mode == NAND_ECC_SOFT) && (chip->page_shift > 9))
|
||||
chip->options |= NAND_SUBPAGE_READ;
|
||||
switch (ecc->mode) {
|
||||
case NAND_ECC_SOFT:
|
||||
case NAND_ECC_SOFT_BCH:
|
||||
if (chip->page_shift > 9)
|
||||
chip->options |= NAND_SUBPAGE_READ;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Fill in remaining MTD driver data */
|
||||
mtd->type = nand_is_slc(chip) ? MTD_NANDFLASH : MTD_MLCNANDFLASH;
|
||||
|
@ -528,7 +528,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
int i, chips;
|
||||
int bits, startblock, block, dir;
|
||||
int startblock, block, dir;
|
||||
int scanlen = mtd->writesize + mtd->oobsize;
|
||||
int bbtblocks;
|
||||
int blocktopage = this->bbt_erase_shift - this->page_shift;
|
||||
@ -552,9 +552,6 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
||||
bbtblocks = mtd->size >> this->bbt_erase_shift;
|
||||
}
|
||||
|
||||
/* Number of bits for each erase block in the bbt */
|
||||
bits = td->options & NAND_BBT_NRBITS_MSK;
|
||||
|
||||
for (i = 0; i < chips; i++) {
|
||||
/* Reset version information */
|
||||
td->version[i] = 0;
|
||||
@ -1285,6 +1282,7 @@ static int nand_create_badblock_pattern(struct nand_chip *this)
|
||||
int nand_default_bbt(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
int ret;
|
||||
|
||||
/* Is a flash based bad block table requested? */
|
||||
if (this->bbt_options & NAND_BBT_USE_FLASH) {
|
||||
@ -1303,8 +1301,11 @@ int nand_default_bbt(struct mtd_info *mtd)
|
||||
this->bbt_md = NULL;
|
||||
}
|
||||
|
||||
if (!this->badblock_pattern)
|
||||
nand_create_badblock_pattern(this);
|
||||
if (!this->badblock_pattern) {
|
||||
ret = nand_create_badblock_pattern(this);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return nand_scan_bbt(mtd, this->badblock_pattern);
|
||||
}
|
||||
|
@ -506,7 +506,7 @@ int __nand_correct_data(unsigned char *buf,
|
||||
if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1)
|
||||
return 1; /* error in ECC data; no action needed */
|
||||
|
||||
pr_err("%s: uncorrectable ECC error", __func__);
|
||||
pr_err("%s: uncorrectable ECC error\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
EXPORT_SYMBOL(__nand_correct_data);
|
||||
|
@ -137,6 +137,10 @@
|
||||
#define BADBLOCK_MARKER_LENGTH 2
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_OMAP_BCH
|
||||
static u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55,
|
||||
0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78,
|
||||
0x48, 0x76, 0xa9, 0x3b, 0x97, 0xd1, 0x7a, 0x93,
|
||||
0x07, 0x0e};
|
||||
static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
|
||||
0xac, 0x6b, 0xff, 0x99, 0x7b};
|
||||
static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
|
||||
@ -1114,6 +1118,19 @@ static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode)
|
||||
ecc_size1 = BCH_ECC_SIZE1;
|
||||
}
|
||||
break;
|
||||
case OMAP_ECC_BCH16_CODE_HW:
|
||||
bch_type = 0x2;
|
||||
nsectors = chip->ecc.steps;
|
||||
if (mode == NAND_ECC_READ) {
|
||||
wr_mode = 0x01;
|
||||
ecc_size0 = 52; /* ECC bits in nibbles per sector */
|
||||
ecc_size1 = 0; /* non-ECC bits in nibbles per sector */
|
||||
} else {
|
||||
wr_mode = 0x01;
|
||||
ecc_size0 = 0; /* extra bits in nibbles per sector */
|
||||
ecc_size1 = 52; /* OOB bits in nibbles per sector */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
@ -1162,7 +1179,8 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
|
||||
struct gpmc_nand_regs *gpmc_regs = &info->reg;
|
||||
u8 *ecc_code;
|
||||
unsigned long nsectors, bch_val1, bch_val2, bch_val3, bch_val4;
|
||||
int i;
|
||||
u32 val;
|
||||
int i, j;
|
||||
|
||||
nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
|
||||
for (i = 0; i < nsectors; i++) {
|
||||
@ -1201,6 +1219,41 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
|
||||
*ecc_code++ = ((bch_val1 >> 4) & 0xFF);
|
||||
*ecc_code++ = ((bch_val1 & 0xF) << 4);
|
||||
break;
|
||||
case OMAP_ECC_BCH16_CODE_HW:
|
||||
val = readl(gpmc_regs->gpmc_bch_result6[i]);
|
||||
ecc_code[0] = ((val >> 8) & 0xFF);
|
||||
ecc_code[1] = ((val >> 0) & 0xFF);
|
||||
val = readl(gpmc_regs->gpmc_bch_result5[i]);
|
||||
ecc_code[2] = ((val >> 24) & 0xFF);
|
||||
ecc_code[3] = ((val >> 16) & 0xFF);
|
||||
ecc_code[4] = ((val >> 8) & 0xFF);
|
||||
ecc_code[5] = ((val >> 0) & 0xFF);
|
||||
val = readl(gpmc_regs->gpmc_bch_result4[i]);
|
||||
ecc_code[6] = ((val >> 24) & 0xFF);
|
||||
ecc_code[7] = ((val >> 16) & 0xFF);
|
||||
ecc_code[8] = ((val >> 8) & 0xFF);
|
||||
ecc_code[9] = ((val >> 0) & 0xFF);
|
||||
val = readl(gpmc_regs->gpmc_bch_result3[i]);
|
||||
ecc_code[10] = ((val >> 24) & 0xFF);
|
||||
ecc_code[11] = ((val >> 16) & 0xFF);
|
||||
ecc_code[12] = ((val >> 8) & 0xFF);
|
||||
ecc_code[13] = ((val >> 0) & 0xFF);
|
||||
val = readl(gpmc_regs->gpmc_bch_result2[i]);
|
||||
ecc_code[14] = ((val >> 24) & 0xFF);
|
||||
ecc_code[15] = ((val >> 16) & 0xFF);
|
||||
ecc_code[16] = ((val >> 8) & 0xFF);
|
||||
ecc_code[17] = ((val >> 0) & 0xFF);
|
||||
val = readl(gpmc_regs->gpmc_bch_result1[i]);
|
||||
ecc_code[18] = ((val >> 24) & 0xFF);
|
||||
ecc_code[19] = ((val >> 16) & 0xFF);
|
||||
ecc_code[20] = ((val >> 8) & 0xFF);
|
||||
ecc_code[21] = ((val >> 0) & 0xFF);
|
||||
val = readl(gpmc_regs->gpmc_bch_result0[i]);
|
||||
ecc_code[22] = ((val >> 24) & 0xFF);
|
||||
ecc_code[23] = ((val >> 16) & 0xFF);
|
||||
ecc_code[24] = ((val >> 8) & 0xFF);
|
||||
ecc_code[25] = ((val >> 0) & 0xFF);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -1210,8 +1263,8 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
|
||||
case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
|
||||
/* Add constant polynomial to remainder, so that
|
||||
* ECC of blank pages results in 0x0 on reading back */
|
||||
for (i = 0; i < eccbytes; i++)
|
||||
ecc_calc[i] ^= bch4_polynomial[i];
|
||||
for (j = 0; j < eccbytes; j++)
|
||||
ecc_calc[j] ^= bch4_polynomial[j];
|
||||
break;
|
||||
case OMAP_ECC_BCH4_CODE_HW:
|
||||
/* Set 8th ECC byte as 0x0 for ROM compatibility */
|
||||
@ -1220,13 +1273,15 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
|
||||
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
|
||||
/* Add constant polynomial to remainder, so that
|
||||
* ECC of blank pages results in 0x0 on reading back */
|
||||
for (i = 0; i < eccbytes; i++)
|
||||
ecc_calc[i] ^= bch8_polynomial[i];
|
||||
for (j = 0; j < eccbytes; j++)
|
||||
ecc_calc[j] ^= bch8_polynomial[j];
|
||||
break;
|
||||
case OMAP_ECC_BCH8_CODE_HW:
|
||||
/* Set 14th ECC byte as 0x0 for ROM compatibility */
|
||||
ecc_calc[eccbytes - 1] = 0x0;
|
||||
break;
|
||||
case OMAP_ECC_BCH16_CODE_HW:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -1237,6 +1292,7 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_OMAP_BCH
|
||||
/**
|
||||
* erased_sector_bitflips - count bit flips
|
||||
* @data: data sector buffer
|
||||
@ -1276,7 +1332,6 @@ static int erased_sector_bitflips(u_char *data, u_char *oob,
|
||||
return flip_bits;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_OMAP_BCH
|
||||
/**
|
||||
* omap_elm_correct_data - corrects page data area in case error reported
|
||||
* @mtd: MTD device structure
|
||||
@ -1318,6 +1373,10 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
|
||||
actual_eccbytes = ecc->bytes - 1;
|
||||
erased_ecc_vec = bch8_vector;
|
||||
break;
|
||||
case OMAP_ECC_BCH16_CODE_HW:
|
||||
actual_eccbytes = ecc->bytes;
|
||||
erased_ecc_vec = bch16_vector;
|
||||
break;
|
||||
default:
|
||||
pr_err("invalid driver configuration\n");
|
||||
return -EINVAL;
|
||||
@ -1382,7 +1441,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
|
||||
|
||||
/* Check if any error reported */
|
||||
if (!is_error_reported)
|
||||
return 0;
|
||||
return stat;
|
||||
|
||||
/* Decode BCH error using ELM module */
|
||||
elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec);
|
||||
@ -1401,6 +1460,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
|
||||
BCH4_BIT_PAD;
|
||||
break;
|
||||
case OMAP_ECC_BCH8_CODE_HW:
|
||||
case OMAP_ECC_BCH16_CODE_HW:
|
||||
pos = err_vec[i].error_loc[j];
|
||||
break;
|
||||
default:
|
||||
@ -1912,6 +1972,40 @@ static int omap_nand_probe(struct platform_device *pdev)
|
||||
goto return_error;
|
||||
#endif
|
||||
|
||||
case OMAP_ECC_BCH16_CODE_HW:
|
||||
#ifdef CONFIG_MTD_NAND_OMAP_BCH
|
||||
pr_info("using OMAP_ECC_BCH16_CODE_HW ECC scheme\n");
|
||||
nand_chip->ecc.mode = NAND_ECC_HW;
|
||||
nand_chip->ecc.size = 512;
|
||||
nand_chip->ecc.bytes = 26;
|
||||
nand_chip->ecc.strength = 16;
|
||||
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||
nand_chip->ecc.correct = omap_elm_correct_data;
|
||||
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
|
||||
nand_chip->ecc.read_page = omap_read_page_bch;
|
||||
nand_chip->ecc.write_page = omap_write_page_bch;
|
||||
/* This ECC scheme requires ELM H/W block */
|
||||
err = is_elm_present(info, pdata->elm_of_node, BCH16_ECC);
|
||||
if (err < 0) {
|
||||
pr_err("ELM is required for this ECC scheme\n");
|
||||
goto return_error;
|
||||
}
|
||||
/* define ECC layout */
|
||||
ecclayout->eccbytes = nand_chip->ecc.bytes *
|
||||
(mtd->writesize /
|
||||
nand_chip->ecc.size);
|
||||
oob_index = BADBLOCK_MARKER_LENGTH;
|
||||
for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
|
||||
ecclayout->eccpos[i] = oob_index;
|
||||
/* reserved marker already included in ecclayout->eccbytes */
|
||||
ecclayout->oobfree->offset =
|
||||
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
|
||||
break;
|
||||
#else
|
||||
pr_err("nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled\n");
|
||||
err = -EINVAL;
|
||||
goto return_error;
|
||||
#endif
|
||||
default:
|
||||
pr_err("nand: error: invalid or unsupported ECC scheme\n");
|
||||
err = -EINVAL;
|
||||
|
@ -214,7 +214,7 @@ static int orion_nand_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id orion_nand_of_match_table[] = {
|
||||
static const struct of_device_id orion_nand_of_match_table[] = {
|
||||
{ .compatible = "marvell,orion-nand", },
|
||||
{},
|
||||
};
|
||||
|
@ -127,10 +127,10 @@
|
||||
|
||||
/* macros for registers read/write */
|
||||
#define nand_writel(info, off, val) \
|
||||
__raw_writel((val), (info)->mmio_base + (off))
|
||||
writel_relaxed((val), (info)->mmio_base + (off))
|
||||
|
||||
#define nand_readl(info, off) \
|
||||
__raw_readl((info)->mmio_base + (off))
|
||||
readl_relaxed((info)->mmio_base + (off))
|
||||
|
||||
/* error code and state */
|
||||
enum {
|
||||
@ -337,7 +337,7 @@ static struct nand_ecclayout ecc_layout_4KB_bch8bit = {
|
||||
/* convert nano-seconds to nand flash controller clock cycles */
|
||||
#define ns2cycle(ns, clk) (int)((ns) * (clk / 1000000) / 1000)
|
||||
|
||||
static struct of_device_id pxa3xx_nand_dt_ids[] = {
|
||||
static const struct of_device_id pxa3xx_nand_dt_ids[] = {
|
||||
{
|
||||
.compatible = "marvell,pxa3xx-nand",
|
||||
.data = (void *)PXA3XX_NAND_VARIANT_PXA,
|
||||
@ -1354,7 +1354,6 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
|
||||
ecc->mode = NAND_ECC_HW;
|
||||
ecc->size = 512;
|
||||
ecc->strength = 1;
|
||||
return 1;
|
||||
|
||||
} else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) {
|
||||
info->chunk_size = 512;
|
||||
@ -1363,7 +1362,6 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
|
||||
ecc->mode = NAND_ECC_HW;
|
||||
ecc->size = 512;
|
||||
ecc->strength = 1;
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Required ECC: 4-bit correction per 512 bytes
|
||||
@ -1378,7 +1376,6 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
|
||||
ecc->size = info->chunk_size;
|
||||
ecc->layout = &ecc_layout_2KB_bch4bit;
|
||||
ecc->strength = 16;
|
||||
return 1;
|
||||
|
||||
} else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) {
|
||||
info->ecc_bch = 1;
|
||||
@ -1389,7 +1386,6 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
|
||||
ecc->size = info->chunk_size;
|
||||
ecc->layout = &ecc_layout_4KB_bch4bit;
|
||||
ecc->strength = 16;
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Required ECC: 8-bit correction per 512 bytes
|
||||
@ -1404,8 +1400,15 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
|
||||
ecc->size = info->chunk_size;
|
||||
ecc->layout = &ecc_layout_4KB_bch8bit;
|
||||
ecc->strength = 16;
|
||||
return 1;
|
||||
} else {
|
||||
dev_err(&info->pdev->dev,
|
||||
"ECC strength %d at page size %d is not supported\n",
|
||||
strength, page_size);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_info(&info->pdev->dev, "ECC strength %d, ECC step size %d\n",
|
||||
ecc->strength, ecc->size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1516,8 +1519,13 @@ KEEP_CONFIG:
|
||||
}
|
||||
}
|
||||
|
||||
ecc_strength = chip->ecc_strength_ds;
|
||||
ecc_step = chip->ecc_step_ds;
|
||||
if (pdata->ecc_strength && pdata->ecc_step_size) {
|
||||
ecc_strength = pdata->ecc_strength;
|
||||
ecc_step = pdata->ecc_step_size;
|
||||
} else {
|
||||
ecc_strength = chip->ecc_strength_ds;
|
||||
ecc_step = chip->ecc_step_ds;
|
||||
}
|
||||
|
||||
/* Set default ECC strength requirements on non-ONFI devices */
|
||||
if (ecc_strength < 1 && ecc_step < 1) {
|
||||
@ -1527,12 +1535,8 @@ KEEP_CONFIG:
|
||||
|
||||
ret = pxa_ecc_init(info, &chip->ecc, ecc_strength,
|
||||
ecc_step, mtd->writesize);
|
||||
if (!ret) {
|
||||
dev_err(&info->pdev->dev,
|
||||
"ECC strength %d at page size %d is not supported\n",
|
||||
ecc_strength, mtd->writesize);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* calculate addressing information */
|
||||
if (mtd->writesize >= 2048)
|
||||
@ -1730,6 +1734,14 @@ static int pxa3xx_nand_probe_dt(struct platform_device *pdev)
|
||||
of_property_read_u32(np, "num-cs", &pdata->num_cs);
|
||||
pdata->flash_bbt = of_get_nand_on_flash_bbt(np);
|
||||
|
||||
pdata->ecc_strength = of_get_nand_ecc_strength(np);
|
||||
if (pdata->ecc_strength < 0)
|
||||
pdata->ecc_strength = 0;
|
||||
|
||||
pdata->ecc_step_size = of_get_nand_ecc_step_size(np);
|
||||
if (pdata->ecc_step_size < 0)
|
||||
pdata->ecc_step_size = 0;
|
||||
|
||||
pdev->dev.platform_data = pdata;
|
||||
|
||||
return 0;
|
||||
|
@ -245,7 +245,7 @@ static void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
||||
}
|
||||
|
||||
/* write DWORD chinks - faster */
|
||||
while (len) {
|
||||
while (len >= 4) {
|
||||
reg = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
|
||||
r852_write_reg_dword(dev, R852_DATALINE, reg);
|
||||
buf += 4;
|
||||
@ -254,8 +254,10 @@ static void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
||||
}
|
||||
|
||||
/* write rest */
|
||||
while (len)
|
||||
while (len > 0) {
|
||||
r852_write_reg(dev, R852_DATALINE, *buf++);
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -537,9 +537,9 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int (*s5pc110_dma_ops)(void *dst, void *src, size_t count, int direction);
|
||||
static int (*s5pc110_dma_ops)(dma_addr_t dst, dma_addr_t src, size_t count, int direction);
|
||||
|
||||
static int s5pc110_dma_poll(void *dst, void *src, size_t count, int direction)
|
||||
static int s5pc110_dma_poll(dma_addr_t dst, dma_addr_t src, size_t count, int direction)
|
||||
{
|
||||
void __iomem *base = onenand->dma_addr;
|
||||
int status;
|
||||
@ -605,7 +605,7 @@ static irqreturn_t s5pc110_onenand_irq(int irq, void *data)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int s5pc110_dma_irq(void *dst, void *src, size_t count, int direction)
|
||||
static int s5pc110_dma_irq(dma_addr_t dst, dma_addr_t src, size_t count, int direction)
|
||||
{
|
||||
void __iomem *base = onenand->dma_addr;
|
||||
int status;
|
||||
@ -686,7 +686,7 @@ static int s5pc110_read_bufferram(struct mtd_info *mtd, int area,
|
||||
dev_err(dev, "Couldn't map a %d byte buffer for DMA\n", count);
|
||||
goto normal;
|
||||
}
|
||||
err = s5pc110_dma_ops((void *) dma_dst, (void *) dma_src,
|
||||
err = s5pc110_dma_ops(dma_dst, dma_src,
|
||||
count, S5PC110_DMA_DIR_READ);
|
||||
|
||||
if (page_dma)
|
||||
|
17
drivers/mtd/spi-nor/Kconfig
Normal file
17
drivers/mtd/spi-nor/Kconfig
Normal file
@ -0,0 +1,17 @@
|
||||
menuconfig MTD_SPI_NOR
|
||||
tristate "SPI-NOR device support"
|
||||
depends on MTD
|
||||
help
|
||||
This is the framework for the SPI NOR which can be used by the SPI
|
||||
device drivers and the SPI-NOR device driver.
|
||||
|
||||
if MTD_SPI_NOR
|
||||
|
||||
config SPI_FSL_QUADSPI
|
||||
tristate "Freescale Quad SPI controller"
|
||||
depends on ARCH_MXC
|
||||
help
|
||||
This enables support for the Quad SPI controller in master mode.
|
||||
We only connect the NOR to this controller now.
|
||||
|
||||
endif # MTD_SPI_NOR
|
2
drivers/mtd/spi-nor/Makefile
Normal file
2
drivers/mtd/spi-nor/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
|
||||
obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
|
1009
drivers/mtd/spi-nor/fsl-quadspi.c
Normal file
1009
drivers/mtd/spi-nor/fsl-quadspi.c
Normal file
File diff suppressed because it is too large
Load Diff
1107
drivers/mtd/spi-nor/spi-nor.c
Normal file
1107
drivers/mtd/spi-nor/spi-nor.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -69,8 +69,8 @@ static int write_eraseblock(int ebnum)
|
||||
int err = 0;
|
||||
loff_t addr = ebnum * mtd->erasesize;
|
||||
|
||||
prandom_bytes_state(&rnd_state, writebuf, use_len_max * pgcnt);
|
||||
for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
|
||||
prandom_bytes_state(&rnd_state, writebuf, use_len);
|
||||
ops.mode = MTD_OPS_AUTO_OOB;
|
||||
ops.len = 0;
|
||||
ops.retlen = 0;
|
||||
@ -78,7 +78,7 @@ static int write_eraseblock(int ebnum)
|
||||
ops.oobretlen = 0;
|
||||
ops.ooboffs = use_offset;
|
||||
ops.datbuf = NULL;
|
||||
ops.oobbuf = writebuf;
|
||||
ops.oobbuf = writebuf + (use_len_max * i) + use_offset;
|
||||
err = mtd_write_oob(mtd, addr, &ops);
|
||||
if (err || ops.oobretlen != use_len) {
|
||||
pr_err("error: writeoob failed at %#llx\n",
|
||||
@ -122,8 +122,8 @@ static int verify_eraseblock(int ebnum)
|
||||
int err = 0;
|
||||
loff_t addr = ebnum * mtd->erasesize;
|
||||
|
||||
prandom_bytes_state(&rnd_state, writebuf, use_len_max * pgcnt);
|
||||
for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
|
||||
prandom_bytes_state(&rnd_state, writebuf, use_len);
|
||||
ops.mode = MTD_OPS_AUTO_OOB;
|
||||
ops.len = 0;
|
||||
ops.retlen = 0;
|
||||
@ -139,7 +139,8 @@ static int verify_eraseblock(int ebnum)
|
||||
errcnt += 1;
|
||||
return err ? err : -1;
|
||||
}
|
||||
if (memcmp(readbuf, writebuf, use_len)) {
|
||||
if (memcmp(readbuf, writebuf + (use_len_max * i) + use_offset,
|
||||
use_len)) {
|
||||
pr_err("error: verify failed at %#llx\n",
|
||||
(long long)addr);
|
||||
errcnt += 1;
|
||||
@ -166,7 +167,9 @@ static int verify_eraseblock(int ebnum)
|
||||
errcnt += 1;
|
||||
return err ? err : -1;
|
||||
}
|
||||
if (memcmp(readbuf + use_offset, writebuf, use_len)) {
|
||||
if (memcmp(readbuf + use_offset,
|
||||
writebuf + (use_len_max * i) + use_offset,
|
||||
use_len)) {
|
||||
pr_err("error: verify failed at %#llx\n",
|
||||
(long long)addr);
|
||||
errcnt += 1;
|
||||
@ -566,8 +569,8 @@ static int __init mtd_oobtest_init(void)
|
||||
if (bbt[i] || bbt[i + 1])
|
||||
continue;
|
||||
addr = (i + 1) * mtd->erasesize - mtd->writesize;
|
||||
prandom_bytes_state(&rnd_state, writebuf, sz * cnt);
|
||||
for (pg = 0; pg < cnt; ++pg) {
|
||||
prandom_bytes_state(&rnd_state, writebuf, sz);
|
||||
ops.mode = MTD_OPS_AUTO_OOB;
|
||||
ops.len = 0;
|
||||
ops.retlen = 0;
|
||||
@ -575,7 +578,7 @@ static int __init mtd_oobtest_init(void)
|
||||
ops.oobretlen = 0;
|
||||
ops.ooboffs = 0;
|
||||
ops.datbuf = NULL;
|
||||
ops.oobbuf = writebuf;
|
||||
ops.oobbuf = writebuf + pg * sz;
|
||||
err = mtd_write_oob(mtd, addr, &ops);
|
||||
if (err)
|
||||
goto out;
|
||||
|
@ -175,6 +175,11 @@ typedef enum {
|
||||
#define NAND_OWN_BUFFERS 0x00020000
|
||||
/* Chip may not exist, so silence any errors in scan */
|
||||
#define NAND_SCAN_SILENT_NODEV 0x00040000
|
||||
/*
|
||||
* This option could be defined by controller drivers to protect against
|
||||
* kmap'ed, vmalloc'ed highmem buffers being passed from upper layers
|
||||
*/
|
||||
#define NAND_USE_BOUNCE_BUFFER 0x00080000
|
||||
/*
|
||||
* Autodetect nand buswidth with readid/onfi.
|
||||
* This suppose the driver will configure the hardware in 8 bits mode
|
||||
@ -552,8 +557,7 @@ struct nand_buffers {
|
||||
* @ecc: [BOARDSPECIFIC] ECC control structure
|
||||
* @buffers: buffer structure for read/write
|
||||
* @hwcontrol: platform-specific hardware control structure
|
||||
* @erase_cmd: [INTERN] erase command write function, selectable due
|
||||
* to AND support.
|
||||
* @erase: [REPLACEABLE] erase function
|
||||
* @scan_bbt: [REPLACEABLE] function to scan bad block table
|
||||
* @chip_delay: [BOARDSPECIFIC] chip dependent delay for transferring
|
||||
* data from array to read regs (tR).
|
||||
@ -637,7 +641,7 @@ struct nand_chip {
|
||||
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,
|
||||
int page_addr);
|
||||
int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
|
||||
void (*erase_cmd)(struct mtd_info *mtd, int page);
|
||||
int (*erase)(struct mtd_info *mtd, int page);
|
||||
int (*scan_bbt)(struct mtd_info *mtd);
|
||||
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
|
||||
int status, int page);
|
||||
|
@ -101,9 +101,6 @@ static inline void send_pfow_command(struct map_info *map,
|
||||
unsigned long len, map_word *datum)
|
||||
{
|
||||
int bits_per_chip = map_bankwidth(map) * 8;
|
||||
int chipnum;
|
||||
struct lpddr_private *lpddr = map->fldrv_priv;
|
||||
chipnum = adr >> lpddr->chipshift;
|
||||
|
||||
map_write(map, CMD(cmd_code), map->pfow_base + PFOW_COMMAND_CODE);
|
||||
map_write(map, CMD(adr & ((1<<bits_per_chip) - 1)),
|
||||
|
214
include/linux/mtd/spi-nor.h
Normal file
214
include/linux/mtd/spi-nor.h
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MTD_SPI_NOR_H
|
||||
#define __LINUX_MTD_SPI_NOR_H
|
||||
|
||||
/*
|
||||
* Note on opcode nomenclature: some opcodes have a format like
|
||||
* SPINOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number
|
||||
* of I/O lines used for the opcode, address, and data (respectively). The
|
||||
* FUNCTION has an optional suffix of '4', to represent an opcode which
|
||||
* requires a 4-byte (32-bit) address.
|
||||
*/
|
||||
|
||||
/* Flash opcodes. */
|
||||
#define SPINOR_OP_WREN 0x06 /* Write enable */
|
||||
#define SPINOR_OP_RDSR 0x05 /* Read status register */
|
||||
#define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */
|
||||
#define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */
|
||||
#define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */
|
||||
#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */
|
||||
#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */
|
||||
#define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */
|
||||
#define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */
|
||||
#define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */
|
||||
#define SPINOR_OP_BE_32K 0x52 /* Erase 32KiB block */
|
||||
#define SPINOR_OP_CHIP_ERASE 0xc7 /* Erase whole flash chip */
|
||||
#define SPINOR_OP_SE 0xd8 /* Sector erase (usually 64KiB) */
|
||||
#define SPINOR_OP_RDID 0x9f /* Read JEDEC ID */
|
||||
#define SPINOR_OP_RDCR 0x35 /* Read configuration register */
|
||||
|
||||
/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
|
||||
#define SPINOR_OP_READ4 0x13 /* Read data bytes (low frequency) */
|
||||
#define SPINOR_OP_READ4_FAST 0x0c /* Read data bytes (high frequency) */
|
||||
#define SPINOR_OP_READ4_1_1_2 0x3c /* Read data bytes (Dual SPI) */
|
||||
#define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */
|
||||
#define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */
|
||||
#define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */
|
||||
|
||||
/* Used for SST flashes only. */
|
||||
#define SPINOR_OP_BP 0x02 /* Byte program */
|
||||
#define SPINOR_OP_WRDI 0x04 /* Write disable */
|
||||
#define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */
|
||||
|
||||
/* Used for Macronix and Winbond flashes. */
|
||||
#define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */
|
||||
#define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */
|
||||
|
||||
/* Used for Spansion flashes only. */
|
||||
#define SPINOR_OP_BRWR 0x17 /* Bank register write */
|
||||
|
||||
/* Status Register bits. */
|
||||
#define SR_WIP 1 /* Write in progress */
|
||||
#define SR_WEL 2 /* Write enable latch */
|
||||
/* meaning of other SR_* bits may differ between vendors */
|
||||
#define SR_BP0 4 /* Block protect 0 */
|
||||
#define SR_BP1 8 /* Block protect 1 */
|
||||
#define SR_BP2 0x10 /* Block protect 2 */
|
||||
#define SR_SRWD 0x80 /* SR write protect */
|
||||
|
||||
#define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */
|
||||
|
||||
/* Configuration Register bits. */
|
||||
#define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */
|
||||
|
||||
enum read_mode {
|
||||
SPI_NOR_NORMAL = 0,
|
||||
SPI_NOR_FAST,
|
||||
SPI_NOR_DUAL,
|
||||
SPI_NOR_QUAD,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct spi_nor_xfer_cfg - Structure for defining a Serial Flash transfer
|
||||
* @wren: command for "Write Enable", or 0x00 for not required
|
||||
* @cmd: command for operation
|
||||
* @cmd_pins: number of pins to send @cmd (1, 2, 4)
|
||||
* @addr: address for operation
|
||||
* @addr_pins: number of pins to send @addr (1, 2, 4)
|
||||
* @addr_width: number of address bytes
|
||||
* (3,4, or 0 for address not required)
|
||||
* @mode: mode data
|
||||
* @mode_pins: number of pins to send @mode (1, 2, 4)
|
||||
* @mode_cycles: number of mode cycles (0 for mode not required)
|
||||
* @dummy_cycles: number of dummy cycles (0 for dummy not required)
|
||||
*/
|
||||
struct spi_nor_xfer_cfg {
|
||||
u8 wren;
|
||||
u8 cmd;
|
||||
u8 cmd_pins;
|
||||
u32 addr;
|
||||
u8 addr_pins;
|
||||
u8 addr_width;
|
||||
u8 mode;
|
||||
u8 mode_pins;
|
||||
u8 mode_cycles;
|
||||
u8 dummy_cycles;
|
||||
};
|
||||
|
||||
#define SPI_NOR_MAX_CMD_SIZE 8
|
||||
enum spi_nor_ops {
|
||||
SPI_NOR_OPS_READ = 0,
|
||||
SPI_NOR_OPS_WRITE,
|
||||
SPI_NOR_OPS_ERASE,
|
||||
SPI_NOR_OPS_LOCK,
|
||||
SPI_NOR_OPS_UNLOCK,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct spi_nor - Structure for defining a the SPI NOR layer
|
||||
* @mtd: point to a mtd_info structure
|
||||
* @lock: the lock for the read/write/erase/lock/unlock operations
|
||||
* @dev: point to a spi device, or a spi nor controller device.
|
||||
* @page_size: the page size of the SPI NOR
|
||||
* @addr_width: number of address bytes
|
||||
* @erase_opcode: the opcode for erasing a sector
|
||||
* @read_opcode: the read opcode
|
||||
* @read_dummy: the dummy needed by the read operation
|
||||
* @program_opcode: the program opcode
|
||||
* @flash_read: the mode of the read
|
||||
* @sst_write_second: used by the SST write operation
|
||||
* @cfg: used by the read_xfer/write_xfer
|
||||
* @cmd_buf: used by the write_reg
|
||||
* @prepare: [OPTIONAL] do some preparations for the
|
||||
* read/write/erase/lock/unlock operations
|
||||
* @unprepare: [OPTIONAL] do some post work after the
|
||||
* read/write/erase/lock/unlock operations
|
||||
* @read_xfer: [OPTIONAL] the read fundamental primitive
|
||||
* @write_xfer: [OPTIONAL] the writefundamental primitive
|
||||
* @read_reg: [DRIVER-SPECIFIC] read out the register
|
||||
* @write_reg: [DRIVER-SPECIFIC] write data to the register
|
||||
* @read_id: [REPLACEABLE] read out the ID data, and find
|
||||
* the proper spi_device_id
|
||||
* @wait_till_ready: [REPLACEABLE] wait till the NOR becomes ready
|
||||
* @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
|
||||
* @priv: the private data
|
||||
*/
|
||||
struct spi_nor {
|
||||
struct mtd_info *mtd;
|
||||
struct mutex lock;
|
||||
struct device *dev;
|
||||
u32 page_size;
|
||||
u8 addr_width;
|
||||
u8 erase_opcode;
|
||||
u8 read_opcode;
|
||||
u8 read_dummy;
|
||||
u8 program_opcode;
|
||||
enum read_mode flash_read;
|
||||
bool sst_write_second;
|
||||
struct spi_nor_xfer_cfg cfg;
|
||||
u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
|
||||
|
||||
int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
|
||||
void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
|
||||
int (*read_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
|
||||
u8 *buf, size_t len);
|
||||
int (*write_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
|
||||
u8 *buf, size_t len);
|
||||
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,
|
||||
int write_enable);
|
||||
const struct spi_device_id *(*read_id)(struct spi_nor *nor);
|
||||
int (*wait_till_ready)(struct spi_nor *nor);
|
||||
|
||||
int (*read)(struct spi_nor *nor, loff_t from,
|
||||
size_t len, size_t *retlen, u_char *read_buf);
|
||||
void (*write)(struct spi_nor *nor, loff_t to,
|
||||
size_t len, size_t *retlen, const u_char *write_buf);
|
||||
int (*erase)(struct spi_nor *nor, loff_t offs);
|
||||
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* spi_nor_scan() - scan the SPI NOR
|
||||
* @nor: the spi_nor structure
|
||||
* @id: the spi_device_id provided by the driver
|
||||
* @mode: the read mode supported by the driver
|
||||
*
|
||||
* The drivers can use this fuction to scan the SPI NOR.
|
||||
* In the scanning, it will try to get all the necessary information to
|
||||
* fill the mtd_info{} and the spi_nor{}.
|
||||
*
|
||||
* The board may assigns a spi_device_id with @id which be used to compared with
|
||||
* the spi_device_id detected by the scanning.
|
||||
*
|
||||
* Return: 0 for success, others for failure.
|
||||
*/
|
||||
int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id,
|
||||
enum read_mode mode);
|
||||
extern const struct spi_device_id spi_nor_ids[];
|
||||
|
||||
/**
|
||||
* spi_nor_match_id() - find the spi_device_id by the name
|
||||
* @name: the name of the spi_device_id
|
||||
*
|
||||
* The drivers use this function to find the spi_device_id
|
||||
* specified by the @name.
|
||||
*
|
||||
* Return: returns the right spi_device_id pointer on success,
|
||||
* and returns NULL on failure.
|
||||
*/
|
||||
const struct spi_device_id *spi_nor_match_id(char *name);
|
||||
|
||||
#endif
|
@ -21,6 +21,7 @@
|
||||
enum bch_ecc {
|
||||
BCH4_ECC = 0,
|
||||
BCH8_ECC,
|
||||
BCH16_ECC,
|
||||
};
|
||||
|
||||
/* ELM support 8 error syndrome process */
|
||||
@ -38,7 +39,7 @@ struct elm_errorvec {
|
||||
bool error_reported;
|
||||
bool error_uncorrectable;
|
||||
int error_count;
|
||||
int error_loc[ERROR_VECTOR_MAX];
|
||||
int error_loc[16];
|
||||
};
|
||||
|
||||
void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc,
|
||||
|
@ -31,6 +31,8 @@ enum omap_ecc {
|
||||
OMAP_ECC_BCH8_CODE_HW_DETECTION_SW,
|
||||
/* 8-bit ECC calculation by GPMC, Error detection by ELM */
|
||||
OMAP_ECC_BCH8_CODE_HW,
|
||||
/* 16-bit ECC calculation by GPMC, Error detection by ELM */
|
||||
OMAP_ECC_BCH16_CODE_HW,
|
||||
};
|
||||
|
||||
struct gpmc_nand_regs {
|
||||
@ -50,6 +52,9 @@ struct gpmc_nand_regs {
|
||||
void __iomem *gpmc_bch_result1[GPMC_BCH_NUM_REMAINDER];
|
||||
void __iomem *gpmc_bch_result2[GPMC_BCH_NUM_REMAINDER];
|
||||
void __iomem *gpmc_bch_result3[GPMC_BCH_NUM_REMAINDER];
|
||||
void __iomem *gpmc_bch_result4[GPMC_BCH_NUM_REMAINDER];
|
||||
void __iomem *gpmc_bch_result5[GPMC_BCH_NUM_REMAINDER];
|
||||
void __iomem *gpmc_bch_result6[GPMC_BCH_NUM_REMAINDER];
|
||||
};
|
||||
|
||||
struct omap_nand_platform_data {
|
||||
|
@ -58,6 +58,9 @@ struct pxa3xx_nand_platform_data {
|
||||
/* use an flash-based bad block table */
|
||||
bool flash_bbt;
|
||||
|
||||
/* requested ECC strength and ECC step size */
|
||||
int ecc_strength, ecc_step_size;
|
||||
|
||||
const struct mtd_partition *parts[NUM_CHIP_SELECT];
|
||||
unsigned int nr_parts[NUM_CHIP_SELECT];
|
||||
|
||||
|
@ -109,6 +109,7 @@ struct mtd_write_req {
|
||||
#define MTD_CAP_RAM (MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE)
|
||||
#define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE)
|
||||
#define MTD_CAP_NANDFLASH (MTD_WRITEABLE)
|
||||
#define MTD_CAP_NVRAM (MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE)
|
||||
|
||||
/* Obsolete ECC byte placement modes (used with obsolete MEMGETOOBSEL) */
|
||||
#define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended)
|
||||
|
Loading…
Reference in New Issue
Block a user