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
|
"ham1" 1-bit Hamming ecc code
|
||||||
"bch4" 4-bit BCH ecc code
|
"bch4" 4-bit BCH ecc code
|
||||||
"bch8" 8-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:
|
- 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.
|
representing partitions.
|
||||||
- compatible : Should be the manufacturer and the name of the chip. Bear in mind
|
- 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
|
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
|
"spi_nor_ids" table in drivers/mtd/spi-nor/spi-nor.c for the list
|
||||||
supported chips.
|
of supported chips.
|
||||||
- reg : Chip-Select number
|
- reg : Chip-Select number
|
||||||
- spi-max-frequency : Maximum frequency of the SPI bus the chip can operate at
|
- 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
|
- num-cs: Number of chipselect lines to usw
|
||||||
- nand-on-flash-bbt: boolean to enable on flash bbt option if
|
- nand-on-flash-bbt: boolean to enable on flash bbt option if
|
||||||
not present false
|
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:
|
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_1 0x244 /* not available on OMAP2 */
|
||||||
#define GPMC_ECC_BCH_RESULT_2 0x248 /* 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_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 */
|
/* GPMC ECC control settings */
|
||||||
#define GPMC_ECC_CTRL_ECCCLEAR 0x100
|
#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;
|
GPMC_BCH_SIZE * i;
|
||||||
reg->gpmc_bch_result3[i] = gpmc_base + GPMC_ECC_BCH_RESULT_3 +
|
reg->gpmc_bch_result3[i] = gpmc_base + GPMC_ECC_BCH_RESULT_3 +
|
||||||
GPMC_BCH_SIZE * i;
|
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
|
else
|
||||||
gpmc_nand_data->ecc_opt =
|
gpmc_nand_data->ecc_opt =
|
||||||
OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
|
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
|
else
|
||||||
pr_err("%s: ti,nand-ecc-opt invalid value\n", __func__);
|
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/lpddr/Kconfig"
|
||||||
|
|
||||||
|
source "drivers/mtd/spi-nor/Kconfig"
|
||||||
|
|
||||||
source "drivers/mtd/ubi/Kconfig"
|
source "drivers/mtd/ubi/Kconfig"
|
||||||
|
|
||||||
endif # MTD
|
endif # MTD
|
||||||
|
@ -32,4 +32,5 @@ inftl-objs := inftlcore.o inftlmount.o
|
|||||||
|
|
||||||
obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
|
obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
|
||||||
|
|
||||||
|
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
|
||||||
obj-$(CONFIG_MTD_UBI) += ubi/
|
obj-$(CONFIG_MTD_UBI) += ubi/
|
||||||
|
@ -169,33 +169,33 @@ config MTD_OTP
|
|||||||
in the programming of OTP bits will waste them.
|
in the programming of OTP bits will waste them.
|
||||||
|
|
||||||
config MTD_CFI_INTELEXT
|
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
|
depends on MTD_GEN_PROBE
|
||||||
select MTD_CFI_UTIL
|
select MTD_CFI_UTIL
|
||||||
help
|
help
|
||||||
The Common Flash Interface defines a number of different command
|
The Common Flash Interface defines a number of different command
|
||||||
sets which a CFI-compliant chip may claim to implement. This code
|
sets which a CFI-compliant chip may claim to implement. This code
|
||||||
provides support for one of those command sets, used on Intel
|
provides support for command set 0001, used on Intel StrataFlash
|
||||||
StrataFlash and other parts.
|
and other parts.
|
||||||
|
|
||||||
config MTD_CFI_AMDSTD
|
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
|
depends on MTD_GEN_PROBE
|
||||||
select MTD_CFI_UTIL
|
select MTD_CFI_UTIL
|
||||||
help
|
help
|
||||||
The Common Flash Interface defines a number of different command
|
The Common Flash Interface defines a number of different command
|
||||||
sets which a CFI-compliant chip may claim to implement. This code
|
sets which a CFI-compliant chip may claim to implement. This code
|
||||||
provides support for one of those command sets, used on chips
|
provides support for command set 0002, used on chips including
|
||||||
including the AMD Am29LV320.
|
the AMD Am29LV320.
|
||||||
|
|
||||||
config MTD_CFI_STAA
|
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
|
depends on MTD_GEN_PROBE
|
||||||
select MTD_CFI_UTIL
|
select MTD_CFI_UTIL
|
||||||
help
|
help
|
||||||
The Common Flash Interface defines a number of different command
|
The Common Flash Interface defines a number of different command
|
||||||
sets which a CFI-compliant chip may claim to implement. This code
|
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
|
config MTD_CFI_UTIL
|
||||||
tristate
|
tristate
|
||||||
|
@ -961,7 +961,7 @@ static int cfi_staa_erase_varsize(struct mtd_info *mtd,
|
|||||||
chipnum++;
|
chipnum++;
|
||||||
|
|
||||||
if (chipnum >= cfi->numchips)
|
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++;
|
chipnum++;
|
||||||
|
|
||||||
if (chipnum >= cfi->numchips)
|
if (chipnum >= cfi->numchips)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -239,7 +239,7 @@ int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
|
|||||||
chipnum++;
|
chipnum++;
|
||||||
|
|
||||||
if (chipnum >= cfi->numchips)
|
if (chipnum >= cfi->numchips)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ config MTD_DATAFLASH_OTP
|
|||||||
|
|
||||||
config MTD_M25P80
|
config MTD_M25P80
|
||||||
tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
|
tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
|
||||||
depends on SPI_MASTER
|
depends on SPI_MASTER && MTD_SPI_NOR
|
||||||
help
|
help
|
||||||
This enables access to most modern SPI flash chips, used for
|
This enables access to most modern SPI flash chips, used for
|
||||||
program and data storage. Series supported include Atmel AT26DF,
|
program and data storage. Series supported include Atmel AT26DF,
|
||||||
@ -212,7 +212,7 @@ config MTD_DOCG3
|
|||||||
|
|
||||||
config MTD_ST_SPI_FSM
|
config MTD_ST_SPI_FSM
|
||||||
tristate "ST Microelectronics SPI FSM Serial Flash Controller"
|
tristate "ST Microelectronics SPI FSM Serial Flash Controller"
|
||||||
depends on ARM || SH
|
depends on ARCH_STI
|
||||||
help
|
help
|
||||||
This provides an MTD device driver for the ST Microelectronics
|
This provides an MTD device driver for the ST Microelectronics
|
||||||
SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
|
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;
|
val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12;
|
||||||
elm_write_reg(info, offset, val);
|
elm_write_reg(info, offset, val);
|
||||||
break;
|
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:
|
default:
|
||||||
pr_err("invalid config bch_type\n");
|
pr_err("invalid config bch_type\n");
|
||||||
}
|
}
|
||||||
@ -418,6 +440,7 @@ static int elm_remove(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
/**
|
/**
|
||||||
* elm_context_save
|
* elm_context_save
|
||||||
* saves ELM configurations to preserve them across Hardware powered-down
|
* 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++) {
|
for (i = 0; i < ERROR_VECTOR_MAX; i++) {
|
||||||
offset = i * SYNDROME_FRAGMENT_REG_SIZE;
|
offset = i * SYNDROME_FRAGMENT_REG_SIZE;
|
||||||
switch (bch_type) {
|
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:
|
case BCH8_ECC:
|
||||||
regs->elm_syndrome_fragment_3[i] = elm_read_reg(info,
|
regs->elm_syndrome_fragment_3[i] = elm_read_reg(info,
|
||||||
ELM_SYNDROME_FRAGMENT_3 + offset);
|
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++) {
|
for (i = 0; i < ERROR_VECTOR_MAX; i++) {
|
||||||
offset = i * SYNDROME_FRAGMENT_REG_SIZE;
|
offset = i * SYNDROME_FRAGMENT_REG_SIZE;
|
||||||
switch (bch_type) {
|
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:
|
case BCH8_ECC:
|
||||||
elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset,
|
elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset,
|
||||||
regs->elm_syndrome_fragment_3[i]);
|
regs->elm_syndrome_fragment_3[i]);
|
||||||
@ -509,6 +546,7 @@ static int elm_resume(struct device *dev)
|
|||||||
elm_context_restore(info);
|
elm_context_restore(info);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume);
|
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
|
#define _MTD_SERIAL_FLASH_CMDS_H
|
||||||
|
|
||||||
/* Generic Flash Commands/OPCODEs */
|
/* Generic Flash Commands/OPCODEs */
|
||||||
#define FLASH_CMD_WREN 0x06
|
#define SPINOR_OP_RDSR2 0x35
|
||||||
#define FLASH_CMD_WRDI 0x04
|
#define SPINOR_OP_WRVCR 0x81
|
||||||
#define FLASH_CMD_RDID 0x9f
|
#define SPINOR_OP_RDVCR 0x85
|
||||||
#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
|
|
||||||
|
|
||||||
/* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
|
/* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
|
||||||
#define FLASH_CMD_READ 0x03 /* READ */
|
#define SPINOR_OP_READ_1_2_2 0xbb /* DUAL I/O READ */
|
||||||
#define FLASH_CMD_READ_FAST 0x0b /* FAST READ */
|
#define SPINOR_OP_READ_1_4_4 0xeb /* QUAD I/O 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 SPINOR_OP_WRITE 0x02 /* PAGE PROGRAM */
|
||||||
#define FLASH_CMD_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */
|
#define SPINOR_OP_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */
|
||||||
#define FLASH_CMD_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */
|
#define SPINOR_OP_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */
|
||||||
#define FLASH_CMD_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */
|
#define SPINOR_OP_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */
|
||||||
#define FLASH_CMD_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */
|
#define SPINOR_OP_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 */
|
/* READ commands with 32-bit addressing */
|
||||||
#define FLASH_CMD_READ4 0x13
|
#define SPINOR_OP_READ4_1_2_2 0xbc
|
||||||
#define FLASH_CMD_READ4_FAST 0x0c
|
#define SPINOR_OP_READ4_1_4_4 0xec
|
||||||
#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
|
|
||||||
|
|
||||||
/* Configuration flags */
|
/* Configuration flags */
|
||||||
#define FLASH_FLAG_SINGLE 0x000000ff
|
#define FLASH_FLAG_SINGLE 0x000000ff
|
||||||
|
@ -280,14 +280,11 @@ __setup("slram=", mtd_slram_setup);
|
|||||||
static int __init init_slram(void)
|
static int __init init_slram(void)
|
||||||
{
|
{
|
||||||
char *devname;
|
char *devname;
|
||||||
int i;
|
|
||||||
|
|
||||||
#ifndef MODULE
|
#ifndef MODULE
|
||||||
char *devstart;
|
char *devstart;
|
||||||
char *devlength;
|
char *devlength;
|
||||||
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
if (!map) {
|
if (!map) {
|
||||||
E("slram: not enough parameters.\n");
|
E("slram: not enough parameters.\n");
|
||||||
return(-EINVAL);
|
return(-EINVAL);
|
||||||
@ -314,6 +311,7 @@ static int __init init_slram(void)
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
int count;
|
int count;
|
||||||
|
int i;
|
||||||
|
|
||||||
for (count = 0; count < SLRAM_MAX_DEVICES_PARAMS && map[count];
|
for (count = 0; count < SLRAM_MAX_DEVICES_PARAMS && map[count];
|
||||||
count++) {
|
count++) {
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include <linux/mfd/syscon.h>
|
#include <linux/mfd/syscon.h>
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
|
#include <linux/mtd/spi-nor.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
@ -201,44 +202,6 @@
|
|||||||
|
|
||||||
#define STFSM_MAX_WAIT_SEQ_MS 1000 /* FSM execution time */
|
#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 */
|
/* S25FLxxxS commands */
|
||||||
#define S25FL_CMD_WRITE4_1_1_4 0x34
|
#define S25FL_CMD_WRITE4_1_1_4 0x34
|
||||||
#define S25FL_CMD_SE4 0xdc
|
#define S25FL_CMD_SE4 0xdc
|
||||||
@ -246,7 +209,7 @@
|
|||||||
#define S25FL_CMD_DYBWR 0xe1
|
#define S25FL_CMD_DYBWR 0xe1
|
||||||
#define S25FL_CMD_DYBRD 0xe0
|
#define S25FL_CMD_DYBRD 0xe0
|
||||||
#define S25FL_CMD_WRITE4 0x12 /* Note, opcode clashes with
|
#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! */
|
* as found on N25Qxxx devices! */
|
||||||
|
|
||||||
/* Status register */
|
/* Status register */
|
||||||
@ -261,6 +224,12 @@
|
|||||||
#define S25FL_STATUS_E_ERR 0x20
|
#define S25FL_STATUS_E_ERR 0x20
|
||||||
#define S25FL_STATUS_P_ERR 0x40
|
#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 256 /* In Bytes */
|
||||||
#define FLASH_PAGESIZE_32 (FLASH_PAGESIZE / 4) /* In uint32_t */
|
#define FLASH_PAGESIZE_32 (FLASH_PAGESIZE / 4) /* In uint32_t */
|
||||||
#define FLASH_MAX_BUSY_WAIT (300 * HZ) /* Maximum 'CHIPERASE' time */
|
#define FLASH_MAX_BUSY_WAIT (300 * HZ) /* Maximum 'CHIPERASE' time */
|
||||||
@ -270,7 +239,6 @@
|
|||||||
*/
|
*/
|
||||||
#define CFG_READ_TOGGLE_32BIT_ADDR 0x00000001
|
#define CFG_READ_TOGGLE_32BIT_ADDR 0x00000001
|
||||||
#define CFG_WRITE_TOGGLE_32BIT_ADDR 0x00000002
|
#define CFG_WRITE_TOGGLE_32BIT_ADDR 0x00000002
|
||||||
#define CFG_WRITE_EX_32BIT_ADDR_DELAY 0x00000004
|
|
||||||
#define CFG_ERASESEC_TOGGLE_32BIT_ADDR 0x00000008
|
#define CFG_ERASESEC_TOGGLE_32BIT_ADDR 0x00000008
|
||||||
#define CFG_S25FL_CHECK_ERROR_FLAGS 0x00000010
|
#define CFG_S25FL_CHECK_ERROR_FLAGS 0x00000010
|
||||||
|
|
||||||
@ -329,7 +297,7 @@ struct flash_info {
|
|||||||
u32 jedec_id;
|
u32 jedec_id;
|
||||||
u16 ext_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.
|
* necessarily called a "sector" by the vendor.
|
||||||
*/
|
*/
|
||||||
unsigned sector_size;
|
unsigned sector_size;
|
||||||
@ -369,17 +337,26 @@ static struct flash_info flash_types[] = {
|
|||||||
{ "m25px32", 0x207116, 0, 64 * 1024, 64, M25PX_FLAG, 75, NULL },
|
{ "m25px32", 0x207116, 0, 64 * 1024, 64, M25PX_FLAG, 75, NULL },
|
||||||
{ "m25px64", 0x207117, 0, 64 * 1024, 128, 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 | \
|
#define MX25_FLAG (FLASH_FLAG_READ_WRITE | \
|
||||||
FLASH_FLAG_READ_FAST | \
|
FLASH_FLAG_READ_FAST | \
|
||||||
FLASH_FLAG_READ_1_1_2 | \
|
FLASH_FLAG_READ_1_1_2 | \
|
||||||
FLASH_FLAG_READ_1_2_2 | \
|
FLASH_FLAG_READ_1_2_2 | \
|
||||||
FLASH_FLAG_READ_1_1_4 | \
|
FLASH_FLAG_READ_1_1_4 | \
|
||||||
FLASH_FLAG_READ_1_4_4 | \
|
|
||||||
FLASH_FLAG_SE_4K | \
|
FLASH_FLAG_SE_4K | \
|
||||||
FLASH_FLAG_SE_32K)
|
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,
|
{ "mx25l25635e", 0xc22019, 0, 64*1024, 512,
|
||||||
(MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70,
|
(MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70,
|
||||||
stfsm_mx25_config },
|
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 | \
|
#define N25Q_FLAG (FLASH_FLAG_READ_WRITE | \
|
||||||
FLASH_FLAG_READ_FAST | \
|
FLASH_FLAG_READ_FAST | \
|
||||||
@ -407,6 +384,8 @@ static struct flash_info flash_types[] = {
|
|||||||
FLASH_FLAG_READ_1_4_4 | \
|
FLASH_FLAG_READ_1_4_4 | \
|
||||||
FLASH_FLAG_WRITE_1_1_4 | \
|
FLASH_FLAG_WRITE_1_1_4 | \
|
||||||
FLASH_FLAG_READ_FAST)
|
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,
|
{ "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, S25FLXXXP_FLAG, 80,
|
||||||
stfsm_s25fl_config },
|
stfsm_s25fl_config },
|
||||||
{ "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, S25FLXXXP_FLAG, 80,
|
{ "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 */
|
/* Default READ configurations, in order of preference */
|
||||||
static struct seq_rw_config default_read_configs[] = {
|
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_4_4, SPINOR_OP_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_1_4, SPINOR_OP_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_2_2, SPINOR_OP_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_1_1_2, SPINOR_OP_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_FAST, SPINOR_OP_READ_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||||
{FLASH_FLAG_READ_WRITE, FLASH_CMD_READ, 0, 1, 1, 0x00, 0, 0},
|
{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ, 0, 1, 1, 0x00, 0, 0},
|
||||||
{0x00, 0, 0, 0, 0, 0x00, 0, 0},
|
{0x00, 0, 0, 0, 0, 0x00, 0, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Default WRITE configurations */
|
/* Default WRITE configurations */
|
||||||
static struct seq_rw_config default_write_configs[] = {
|
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_4_4, SPINOR_OP_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_1_4, SPINOR_OP_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_2_2, SPINOR_OP_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_WRITE_1_1_2, SPINOR_OP_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_READ_WRITE, SPINOR_OP_WRITE, 1, 1, 1, 0x00, 0, 0},
|
||||||
{0x00, 0, 0, 0, 0, 0x00, 0, 0},
|
{0x00, 0, 0, 0, 0, 0x00, 0, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -511,12 +490,12 @@ static struct seq_rw_config default_write_configs[] = {
|
|||||||
* cycles.
|
* cycles.
|
||||||
*/
|
*/
|
||||||
static struct seq_rw_config n25q_read3_configs[] = {
|
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_4_4, SPINOR_OP_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_1_4, SPINOR_OP_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_2_2, SPINOR_OP_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_1_1_2, SPINOR_OP_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_FAST, SPINOR_OP_READ_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||||
{FLASH_FLAG_READ_WRITE, FLASH_CMD_READ, 0, 1, 1, 0x00, 0, 0},
|
{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ, 0, 1, 1, 0x00, 0, 0},
|
||||||
{0x00, 0, 0, 0, 0, 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.)
|
* - 'FAST' variants configured for 8 dummy cycles (see note above.)
|
||||||
*/
|
*/
|
||||||
static struct seq_rw_config n25q_read4_configs[] = {
|
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_4_4, SPINOR_OP_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_1_4, SPINOR_OP_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_2_2, SPINOR_OP_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_1_1_2, SPINOR_OP_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_FAST, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||||
{FLASH_FLAG_READ_WRITE, FLASH_CMD_READ4, 0, 1, 1, 0x00, 0, 0},
|
{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0},
|
||||||
{0x00, 0, 0, 0, 0, 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->seq_opc[0] = (SEQ_OPC_PADS_1 |
|
||||||
SEQ_OPC_CYCLES(8) |
|
SEQ_OPC_CYCLES(8) |
|
||||||
SEQ_OPC_OPCODE(FLASH_CMD_EN4B_ADDR) |
|
SEQ_OPC_OPCODE(SPINOR_OP_EN4B) |
|
||||||
SEQ_OPC_CSDEASSERT);
|
SEQ_OPC_CSDEASSERT);
|
||||||
|
|
||||||
seq->seq[0] = STFSM_INST_CMD1;
|
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.
|
* entering a state that is incompatible with the SPIBoot Controller.
|
||||||
*/
|
*/
|
||||||
static struct seq_rw_config stfsm_s25fl_read4_configs[] = {
|
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_4_4, SPINOR_OP_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_1_4, SPINOR_OP_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_2_2, SPINOR_OP_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_1_1_2, SPINOR_OP_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_FAST, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||||
{FLASH_FLAG_READ_WRITE, FLASH_CMD_READ4, 0, 1, 1, 0x00, 0, 0},
|
{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0},
|
||||||
{0x00, 0, 0, 0, 0, 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
|
* [W25Qxxx] Configuration
|
||||||
*/
|
*/
|
||||||
#define W25Q_STATUS_QE (0x1 << 9)
|
#define W25Q_STATUS_QE (0x1 << 1)
|
||||||
|
|
||||||
static struct stfsm_seq stfsm_seq_read_jedec = {
|
static struct stfsm_seq stfsm_seq_read_jedec = {
|
||||||
.data_size = TRANSFER_SIZE(8),
|
.data_size = TRANSFER_SIZE(8),
|
||||||
.seq_opc[0] = (SEQ_OPC_PADS_1 |
|
.seq_opc[0] = (SEQ_OPC_PADS_1 |
|
||||||
SEQ_OPC_CYCLES(8) |
|
SEQ_OPC_CYCLES(8) |
|
||||||
SEQ_OPC_OPCODE(FLASH_CMD_RDID)),
|
SEQ_OPC_OPCODE(SPINOR_OP_RDID)),
|
||||||
.seq = {
|
.seq = {
|
||||||
STFSM_INST_CMD1,
|
STFSM_INST_CMD1,
|
||||||
STFSM_INST_DATA_READ,
|
STFSM_INST_DATA_READ,
|
||||||
@ -612,7 +591,7 @@ static struct stfsm_seq stfsm_seq_read_status_fifo = {
|
|||||||
.data_size = TRANSFER_SIZE(4),
|
.data_size = TRANSFER_SIZE(4),
|
||||||
.seq_opc[0] = (SEQ_OPC_PADS_1 |
|
.seq_opc[0] = (SEQ_OPC_PADS_1 |
|
||||||
SEQ_OPC_CYCLES(8) |
|
SEQ_OPC_CYCLES(8) |
|
||||||
SEQ_OPC_OPCODE(FLASH_CMD_RDSR)),
|
SEQ_OPC_OPCODE(SPINOR_OP_RDSR)),
|
||||||
.seq = {
|
.seq = {
|
||||||
STFSM_INST_CMD1,
|
STFSM_INST_CMD1,
|
||||||
STFSM_INST_DATA_READ,
|
STFSM_INST_DATA_READ,
|
||||||
@ -628,10 +607,10 @@ static struct stfsm_seq stfsm_seq_erase_sector = {
|
|||||||
/* 'addr_cfg' configured during initialisation */
|
/* 'addr_cfg' configured during initialisation */
|
||||||
.seq_opc = {
|
.seq_opc = {
|
||||||
(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
(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_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||||
SEQ_OPC_OPCODE(FLASH_CMD_SE)),
|
SEQ_OPC_OPCODE(SPINOR_OP_SE)),
|
||||||
},
|
},
|
||||||
.seq = {
|
.seq = {
|
||||||
STFSM_INST_CMD1,
|
STFSM_INST_CMD1,
|
||||||
@ -649,10 +628,10 @@ static struct stfsm_seq stfsm_seq_erase_sector = {
|
|||||||
static struct stfsm_seq stfsm_seq_erase_chip = {
|
static struct stfsm_seq stfsm_seq_erase_chip = {
|
||||||
.seq_opc = {
|
.seq_opc = {
|
||||||
(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
(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_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 = {
|
.seq = {
|
||||||
STFSM_INST_CMD1,
|
STFSM_INST_CMD1,
|
||||||
@ -669,26 +648,9 @@ static struct stfsm_seq stfsm_seq_erase_chip = {
|
|||||||
|
|
||||||
static struct stfsm_seq stfsm_seq_write_status = {
|
static struct stfsm_seq stfsm_seq_write_status = {
|
||||||
.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
.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[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||||
SEQ_OPC_OPCODE(FLASH_CMD_WRSR)),
|
SEQ_OPC_OPCODE(SPINOR_OP_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 = {
|
.seq = {
|
||||||
STFSM_INST_CMD1,
|
STFSM_INST_CMD1,
|
||||||
STFSM_INST_CMD2,
|
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)
|
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->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->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_OPC_CSDEASSERT);
|
||||||
|
|
||||||
seq->seq[0] = STFSM_INST_CMD2;
|
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);
|
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) {
|
while (remaining) {
|
||||||
for (;;) {
|
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);
|
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);
|
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)
|
static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter)
|
||||||
{
|
{
|
||||||
struct stfsm_seq *seq = &fsm->stfsm_seq_en_32bit_addr;
|
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->seq_opc[0] = (SEQ_OPC_PADS_1 |
|
||||||
SEQ_OPC_CYCLES(8) |
|
SEQ_OPC_CYCLES(8) |
|
||||||
@ -851,7 +813,7 @@ static uint8_t stfsm_wait_busy(struct stfsm *fsm)
|
|||||||
/* Use RDRS1 */
|
/* Use RDRS1 */
|
||||||
seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
|
seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
|
||||||
SEQ_OPC_CYCLES(8) |
|
SEQ_OPC_CYCLES(8) |
|
||||||
SEQ_OPC_OPCODE(FLASH_CMD_RDSR));
|
SEQ_OPC_OPCODE(SPINOR_OP_RDSR));
|
||||||
|
|
||||||
/* Load read_status sequence */
|
/* Load read_status sequence */
|
||||||
stfsm_load_seq(fsm, seq);
|
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,
|
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;
|
struct stfsm_seq *seq = &stfsm_seq_read_status_fifo;
|
||||||
uint32_t tmp;
|
uint32_t tmp;
|
||||||
|
uint8_t *t = (uint8_t *)&tmp;
|
||||||
|
int i;
|
||||||
|
|
||||||
dev_dbg(fsm->dev, "reading STA[%s]\n",
|
dev_dbg(fsm->dev, "read 'status' register [0x%02x], %d byte(s)\n",
|
||||||
(cmd == FLASH_CMD_RDSR) ? "1" : "2");
|
cmd, bytes);
|
||||||
|
|
||||||
seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
|
BUG_ON(bytes != 1 && bytes != 2);
|
||||||
SEQ_OPC_CYCLES(8) |
|
|
||||||
|
seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||||
SEQ_OPC_OPCODE(cmd)),
|
SEQ_OPC_OPCODE(cmd)),
|
||||||
|
|
||||||
stfsm_load_seq(fsm, seq);
|
stfsm_load_seq(fsm, seq);
|
||||||
|
|
||||||
stfsm_read_fifo(fsm, &tmp, 4);
|
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);
|
stfsm_wait_seq(fsm);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int stfsm_write_status(struct stfsm *fsm, uint16_t status,
|
static int stfsm_write_status(struct stfsm *fsm, uint8_t cmd,
|
||||||
int sta_bytes)
|
uint16_t data, int bytes, int wait_busy)
|
||||||
{
|
{
|
||||||
struct stfsm_seq *seq = &stfsm_seq_write_status;
|
struct stfsm_seq *seq = &stfsm_seq_write_status;
|
||||||
|
|
||||||
dev_dbg(fsm->dev, "writing STA[%s] 0x%04x\n",
|
dev_dbg(fsm->dev,
|
||||||
(sta_bytes == 1) ? "1" : "1+2", status);
|
"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;
|
BUG_ON(bytes != 1 && bytes != 2);
|
||||||
seq->seq[2] = (sta_bytes == 1) ?
|
|
||||||
STFSM_INST_STA_WR1 : STFSM_INST_STA_WR1_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_load_seq(fsm, seq);
|
||||||
|
|
||||||
stfsm_wait_seq(fsm);
|
stfsm_wait_seq(fsm);
|
||||||
|
|
||||||
return 0;
|
if (wait_busy)
|
||||||
};
|
stfsm_wait_busy(fsm);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1027,7 +986,7 @@ static void stfsm_prepare_rw_seq(struct stfsm *fsm,
|
|||||||
if (cfg->write)
|
if (cfg->write)
|
||||||
seq->seq_opc[i++] = (SEQ_OPC_PADS_1 |
|
seq->seq_opc[i++] = (SEQ_OPC_PADS_1 |
|
||||||
SEQ_OPC_CYCLES(8) |
|
SEQ_OPC_CYCLES(8) |
|
||||||
SEQ_OPC_OPCODE(FLASH_CMD_WREN) |
|
SEQ_OPC_OPCODE(SPINOR_OP_WREN) |
|
||||||
SEQ_OPC_CSDEASSERT);
|
SEQ_OPC_CSDEASSERT);
|
||||||
|
|
||||||
/* Address configuration (24 or 32-bit addresses) */
|
/* 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);
|
stfsm_mx25_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr);
|
||||||
|
|
||||||
soc_reset = stfsm_can_handle_soc_reset(fsm);
|
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
|
/* If we can handle SoC resets, we enable 32-bit address
|
||||||
* mode pervasively */
|
* mode pervasively */
|
||||||
stfsm_enter_32bit_addr(fsm, 1);
|
stfsm_enter_32bit_addr(fsm, 1);
|
||||||
|
|
||||||
} else {
|
else
|
||||||
/* Else, enable/disable 32-bit addressing before/after
|
/* Else, enable/disable 32-bit addressing before/after
|
||||||
* each operation */
|
* each operation */
|
||||||
fsm->configuration = (CFG_READ_TOGGLE_32BIT_ADDR |
|
fsm->configuration = (CFG_READ_TOGGLE_32BIT_ADDR |
|
||||||
CFG_WRITE_TOGGLE_32BIT_ADDR |
|
CFG_WRITE_TOGGLE_32BIT_ADDR |
|
||||||
CFG_ERASESEC_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;
|
data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
|
||||||
if (data_pads == 4) {
|
if (data_pads == 4) {
|
||||||
stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta);
|
if (!(sta & MX25_STATUS_QE)) {
|
||||||
sta |= MX25_STATUS_QE;
|
/* Set 'QE' */
|
||||||
stfsm_write_status(fsm, sta, 1);
|
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;
|
return 0;
|
||||||
@ -1239,7 +1203,7 @@ static int stfsm_n25q_config(struct stfsm *fsm)
|
|||||||
*/
|
*/
|
||||||
vcr = (N25Q_VCR_DUMMY_CYCLES(8) | N25Q_VCR_XIP_DISABLED |
|
vcr = (N25Q_VCR_DUMMY_CYCLES(8) | N25Q_VCR_XIP_DISABLED |
|
||||||
N25Q_VCR_WRAP_CONT);
|
N25Q_VCR_WRAP_CONT);
|
||||||
stfsm_wrvcr(fsm, vcr);
|
stfsm_write_status(fsm, N25Q_CMD_WRVCR, vcr, 1, 0);
|
||||||
|
|
||||||
return 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 = {
|
struct stfsm_seq seq = {
|
||||||
.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
.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_CSDEASSERT),
|
||||||
.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||||
SEQ_OPC_OPCODE(S25FL_CMD_DYBWR)),
|
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_CSDEASSERT),
|
||||||
.seq_opc[1] = (SEQ_OPC_PADS_1 |
|
.seq_opc[1] = (SEQ_OPC_PADS_1 |
|
||||||
SEQ_OPC_CYCLES(8) |
|
SEQ_OPC_CYCLES(8) |
|
||||||
SEQ_OPC_OPCODE(FLASH_CMD_WRDI) |
|
SEQ_OPC_OPCODE(SPINOR_OP_WRDI) |
|
||||||
SEQ_OPC_CSDEASSERT),
|
SEQ_OPC_CSDEASSERT),
|
||||||
.seq = {
|
.seq = {
|
||||||
STFSM_INST_CMD1,
|
STFSM_INST_CMD1,
|
||||||
@ -1367,6 +1331,7 @@ static int stfsm_s25fl_config(struct stfsm *fsm)
|
|||||||
uint32_t offs;
|
uint32_t offs;
|
||||||
uint16_t sta_wr;
|
uint16_t sta_wr;
|
||||||
uint8_t sr1, cr1, dyb;
|
uint8_t sr1, cr1, dyb;
|
||||||
|
int update_sr = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (flags & FLASH_FLAG_32BIT_ADDR) {
|
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;
|
data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
|
||||||
stfsm_read_status(fsm, FLASH_CMD_RDSR2, &cr1);
|
|
||||||
if (data_pads == 4) {
|
if (data_pads == 4) {
|
||||||
if (!(cr1 & STFSM_S25FL_CONFIG_QE)) {
|
if (!(cr1 & STFSM_S25FL_CONFIG_QE)) {
|
||||||
/* Set 'QE' */
|
/* Set 'QE' */
|
||||||
cr1 |= STFSM_S25FL_CONFIG_QE;
|
cr1 |= STFSM_S25FL_CONFIG_QE;
|
||||||
|
|
||||||
stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1);
|
update_sr = 1;
|
||||||
sta_wr = ((uint16_t)cr1 << 8) | sr1;
|
|
||||||
|
|
||||||
stfsm_write_status(fsm, sta_wr, 2);
|
|
||||||
|
|
||||||
stfsm_wait_busy(fsm);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ((cr1 & STFSM_S25FL_CONFIG_QE)) {
|
if (cr1 & STFSM_S25FL_CONFIG_QE) {
|
||||||
/* Clear 'QE' */
|
/* Clear 'QE' */
|
||||||
cr1 &= ~STFSM_S25FL_CONFIG_QE;
|
cr1 &= ~STFSM_S25FL_CONFIG_QE;
|
||||||
|
|
||||||
stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1);
|
update_sr = 1;
|
||||||
sta_wr = ((uint16_t)cr1 << 8) | sr1;
|
|
||||||
|
|
||||||
stfsm_write_status(fsm, sta_wr, 2);
|
|
||||||
|
|
||||||
stfsm_wait_busy(fsm);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
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)
|
static int stfsm_w25q_config(struct stfsm *fsm)
|
||||||
{
|
{
|
||||||
uint32_t data_pads;
|
uint32_t data_pads;
|
||||||
uint16_t sta_wr;
|
uint8_t sr1, sr2;
|
||||||
uint8_t sta1, sta2;
|
uint16_t sr_wr;
|
||||||
|
int update_sr = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = stfsm_prepare_rwe_seqs_default(fsm);
|
ret = stfsm_prepare_rwe_seqs_default(fsm);
|
||||||
if (ret)
|
if (ret)
|
||||||
return 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;
|
data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
|
||||||
if (data_pads == 4) {
|
if (data_pads == 4) {
|
||||||
stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta1);
|
if (!(sr2 & W25Q_STATUS_QE)) {
|
||||||
stfsm_read_status(fsm, FLASH_CMD_RDSR2, &sta2);
|
/* Set 'QE' */
|
||||||
|
sr2 |= W25Q_STATUS_QE;
|
||||||
sta_wr = ((uint16_t)sta2 << 8) | sta1;
|
update_sr = 1;
|
||||||
|
}
|
||||||
sta_wr |= W25Q_STATUS_QE;
|
} else {
|
||||||
|
if (sr2 & W25Q_STATUS_QE) {
|
||||||
stfsm_write_status(fsm, sta_wr, 2);
|
/* Clear 'QE' */
|
||||||
|
sr2 &= ~W25Q_STATUS_QE;
|
||||||
stfsm_wait_busy(fsm);
|
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;
|
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;
|
read_mask = (data_pads << 2) - 1;
|
||||||
|
|
||||||
/* Handle non-aligned buf */
|
/* 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 */
|
/* Handle non-aligned size */
|
||||||
size_ub = (size + read_mask) & ~read_mask;
|
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 */
|
/* Handle non-aligned buf */
|
||||||
if ((uint32_t)buf & 0x3)
|
if ((uintptr_t)buf & 0x3)
|
||||||
memcpy(buf, page_buf, size);
|
memcpy(buf, page_buf, size);
|
||||||
|
|
||||||
/* Wait for sequence to finish */
|
/* 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;
|
write_mask = (data_pads << 2) - 1;
|
||||||
|
|
||||||
/* Handle non-aligned buf */
|
/* Handle non-aligned buf */
|
||||||
if ((uint32_t)buf & 0x3) {
|
if ((uintptr_t)buf & 0x3) {
|
||||||
memcpy(page_buf, buf, size);
|
memcpy(page_buf, buf, size);
|
||||||
p = (uint8_t *)page_buf;
|
p = (uint8_t *)page_buf;
|
||||||
} else {
|
} else {
|
||||||
@ -1628,11 +1596,8 @@ static int stfsm_write(struct stfsm *fsm, const uint8_t *buf,
|
|||||||
stfsm_s25fl_clear_status_reg(fsm);
|
stfsm_s25fl_clear_status_reg(fsm);
|
||||||
|
|
||||||
/* Exit 32-bit address mode, if required */
|
/* 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);
|
stfsm_enter_32bit_addr(fsm, 0);
|
||||||
if (fsm->configuration & CFG_WRITE_EX_32BIT_ADDR_DELAY)
|
|
||||||
udelay(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1736,7 +1701,7 @@ static int stfsm_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||||||
|
|
||||||
while (len) {
|
while (len) {
|
||||||
/* Write up to page boundary */
|
/* 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);
|
ret = stfsm_write(fsm, b, bytes, to);
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -1935,6 +1900,13 @@ static int stfsm_init(struct stfsm *fsm)
|
|||||||
fsm->base + SPI_CONFIGDATA);
|
fsm->base + SPI_CONFIGDATA);
|
||||||
writel(STFSM_DEFAULT_WR_TIME, fsm->base + SPI_STATUS_WR_TIME_REG);
|
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 */
|
/* Clear FIFO, just in case */
|
||||||
stfsm_clear_fifo(fsm);
|
stfsm_clear_fifo(fsm);
|
||||||
|
|
||||||
@ -2086,7 +2058,7 @@ static int stfsm_remove(struct platform_device *pdev)
|
|||||||
return mtd_device_unregister(&fsm->mtd);
|
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", },
|
{ .compatible = "st,spi-fsm", },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
menu "LPDDR flash memory drivers"
|
menu "LPDDR & LPDDR2 PCM memory drivers"
|
||||||
depends on MTD!=n
|
depends on MTD
|
||||||
|
|
||||||
config MTD_LPDDR
|
config MTD_LPDDR
|
||||||
tristate "Support for LPDDR flash chips"
|
tristate "Support for LPDDR flash chips"
|
||||||
@ -17,4 +17,13 @@ config MTD_QINFO_PROBE
|
|||||||
Window QINFO interface, permits software to be used for entire
|
Window QINFO interface, permits software to be used for entire
|
||||||
families of devices. This serves similar purpose of CFI on legacy
|
families of devices. This serves similar purpose of CFI on legacy
|
||||||
Flash products
|
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
|
endmenu
|
||||||
|
@ -4,3 +4,4 @@
|
|||||||
|
|
||||||
obj-$(CONFIG_MTD_QINFO_PROBE) += qinfo_probe.o
|
obj-$(CONFIG_MTD_QINFO_PROBE) += qinfo_probe.o
|
||||||
obj-$(CONFIG_MTD_LPDDR) += lpddr_cmds.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
|
config MTD_SC520CDP
|
||||||
tristate "CFI Flash device mapped on AMD SC520 CDP"
|
tristate "CFI Flash device mapped on AMD SC520 CDP"
|
||||||
depends on X86 && MTD_CFI
|
depends on (MELAN || COMPILE_TEST) && MTD_CFI
|
||||||
help
|
help
|
||||||
The SC520 CDP board has two banks of CFI-compliant chips and one
|
The SC520 CDP board has two banks of CFI-compliant chips and one
|
||||||
Dual-in-line JEDEC chip. This 'mapping' driver supports that
|
Dual-in-line JEDEC chip. This 'mapping' driver supports that
|
||||||
@ -116,7 +116,7 @@ config MTD_SC520CDP
|
|||||||
|
|
||||||
config MTD_NETSC520
|
config MTD_NETSC520
|
||||||
tristate "CFI Flash device mapped on AMD NetSc520"
|
tristate "CFI Flash device mapped on AMD NetSc520"
|
||||||
depends on X86 && MTD_CFI
|
depends on (MELAN || COMPILE_TEST) && MTD_CFI
|
||||||
help
|
help
|
||||||
This enables access routines for the flash chips on the AMD NetSc520
|
This enables access routines for the flash chips on the AMD NetSc520
|
||||||
demonstration board. If you have one of these boards and would like
|
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)
|
static void sc520cdp_setup_par(void)
|
||||||
{
|
{
|
||||||
volatile unsigned long __iomem *mmcr;
|
unsigned long __iomem *mmcr;
|
||||||
unsigned long mmcr_val;
|
unsigned long mmcr_val;
|
||||||
int i, j;
|
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(i = 0; i < NUM_FLASH_BANKS; i++) { /* for each par_table entry */
|
||||||
for(j = 0; j < NUM_SC520_PAR; j++) { /* for each PAR register */
|
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 target device field matches, reprogram the PAR */
|
||||||
if((mmcr_val & SC520_PAR_TRGDEV) == par_table[i].trgdev)
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,28 +33,6 @@ struct map_info soleng_flash_map = {
|
|||||||
|
|
||||||
static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
|
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)
|
static int __init init_soleng_maps(void)
|
||||||
{
|
{
|
||||||
/* First probe at offset 0 */
|
/* 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_register(eprom_mtd, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
mtd_device_parse_register(flash_mtd, probes, NULL,
|
mtd_device_parse_register(flash_mtd, probes, NULL, NULL, 0);
|
||||||
superh_se_partitions, NUM_PARTITIONS);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -87,6 +87,9 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
|
|||||||
if (req->cmd_type != REQ_TYPE_FS)
|
if (req->cmd_type != REQ_TYPE_FS)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
|
if (req->cmd_flags & REQ_FLUSH)
|
||||||
|
return tr->flush(dev);
|
||||||
|
|
||||||
if (blk_rq_pos(req) + blk_rq_cur_sectors(req) >
|
if (blk_rq_pos(req) + blk_rq_cur_sectors(req) >
|
||||||
get_capacity(req->rq_disk))
|
get_capacity(req->rq_disk))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
@ -407,6 +410,9 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
|
|||||||
if (!new->rq)
|
if (!new->rq)
|
||||||
goto error3;
|
goto error3;
|
||||||
|
|
||||||
|
if (tr->flush)
|
||||||
|
blk_queue_flush(new->rq, REQ_FLUSH);
|
||||||
|
|
||||||
new->rq->queuedata = new;
|
new->rq->queuedata = new;
|
||||||
blk_queue_logical_block_size(new->rq, tr->blksize);
|
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_write_req req;
|
||||||
struct mtd_oob_ops ops;
|
struct mtd_oob_ops ops;
|
||||||
void __user *usr_data, *usr_oob;
|
const void __user *usr_data, *usr_oob;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (copy_from_user(&req, argp, sizeof(req)) ||
|
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))
|
|
||||||
return -EFAULT;
|
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)
|
if (!mtd->_write_oob)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
@ -583,10 +588,7 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,
|
|||||||
ops.ooblen = (size_t)req.ooblen;
|
ops.ooblen = (size_t)req.ooblen;
|
||||||
ops.ooboffs = 0;
|
ops.ooboffs = 0;
|
||||||
|
|
||||||
usr_data = (void __user *)(uintptr_t)req.usr_data;
|
if (usr_data) {
|
||||||
usr_oob = (void __user *)(uintptr_t)req.usr_oob;
|
|
||||||
|
|
||||||
if (req.usr_data) {
|
|
||||||
ops.datbuf = memdup_user(usr_data, ops.len);
|
ops.datbuf = memdup_user(usr_data, ops.len);
|
||||||
if (IS_ERR(ops.datbuf))
|
if (IS_ERR(ops.datbuf))
|
||||||
return PTR_ERR(ops.datbuf);
|
return PTR_ERR(ops.datbuf);
|
||||||
@ -594,7 +596,7 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,
|
|||||||
ops.datbuf = NULL;
|
ops.datbuf = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.usr_oob) {
|
if (usr_oob) {
|
||||||
ops.oobbuf = memdup_user(usr_oob, ops.ooblen);
|
ops.oobbuf = memdup_user(usr_oob, ops.ooblen);
|
||||||
if (IS_ERR(ops.oobbuf)) {
|
if (IS_ERR(ops.oobbuf)) {
|
||||||
kfree(ops.datbuf);
|
kfree(ops.datbuf);
|
||||||
|
@ -679,9 +679,6 @@ static int bf5xx_nand_remove(struct platform_device *pdev)
|
|||||||
peripheral_free_list(bfin_nfc_pin_req);
|
peripheral_free_list(bfin_nfc_pin_req);
|
||||||
bf5xx_nand_dma_remove(info);
|
bf5xx_nand_dma_remove(info);
|
||||||
|
|
||||||
/* free the common resources */
|
|
||||||
kfree(info);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -742,10 +739,10 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
|
|||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||||
if (info == NULL) {
|
if (info == NULL) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto out_err_kzalloc;
|
goto out_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, info);
|
platform_set_drvdata(pdev, info);
|
||||||
@ -790,7 +787,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
|
|||||||
/* initialise the hardware */
|
/* initialise the hardware */
|
||||||
err = bf5xx_nand_hw_init(info);
|
err = bf5xx_nand_hw_init(info);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_err_hw_init;
|
goto out_err;
|
||||||
|
|
||||||
/* setup hardware ECC data struct */
|
/* setup hardware ECC data struct */
|
||||||
if (hardware_ecc) {
|
if (hardware_ecc) {
|
||||||
@ -827,9 +824,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
out_err_nand_scan:
|
out_err_nand_scan:
|
||||||
bf5xx_nand_dma_remove(info);
|
bf5xx_nand_dma_remove(info);
|
||||||
out_err_hw_init:
|
out_err:
|
||||||
kfree(info);
|
|
||||||
out_err_kzalloc:
|
|
||||||
peripheral_free_list(bfin_nfc_pin_req);
|
peripheral_free_list(bfin_nfc_pin_req);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -1233,7 +1233,7 @@ static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
|
|||||||
return status;
|
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);
|
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 |
|
irq_status = wait_for_irq(denali, INTR_STATUS__ERASE_COMP |
|
||||||
INTR_STATUS__ERASE_FAIL);
|
INTR_STATUS__ERASE_FAIL);
|
||||||
|
|
||||||
denali->status = (irq_status & INTR_STATUS__ERASE_FAIL) ?
|
return (irq_status & INTR_STATUS__ERASE_FAIL) ? NAND_STATUS_FAIL : PASS;
|
||||||
NAND_STATUS_FAIL : PASS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
|
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.write_page_raw = denali_write_page_raw;
|
||||||
denali->nand.ecc.read_oob = denali_read_oob;
|
denali->nand.ecc.read_oob = denali_read_oob;
|
||||||
denali->nand.ecc.write_oob = denali_write_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)) {
|
if (nand_scan_tail(&denali->mtd)) {
|
||||||
ret = -ENXIO;
|
ret = -ENXIO;
|
||||||
|
@ -872,7 +872,7 @@ static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
|
|||||||
return 0;
|
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 nand_chip *nand = mtd->priv;
|
||||||
struct docg4_priv *doc = nand->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);
|
write_nop(docptr);
|
||||||
poll_status(doc);
|
poll_status(doc);
|
||||||
write_nop(docptr);
|
write_nop(docptr);
|
||||||
|
|
||||||
|
return nand->waitfunc(mtd, nand);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int write_page(struct mtd_info *mtd, struct nand_chip *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->block_markbad = docg4_block_markbad;
|
||||||
nand->read_buf = docg4_read_buf;
|
nand->read_buf = docg4_read_buf;
|
||||||
nand->write_buf = docg4_write_buf16;
|
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.read_page = docg4_read_page;
|
||||||
nand->ecc.write_page = docg4_write_page;
|
nand->ecc.write_page = docg4_write_page;
|
||||||
nand->ecc.read_page_raw = docg4_read_page_raw;
|
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;
|
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)
|
static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
|
||||||
{
|
{
|
||||||
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
|
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.read_page = fsl_elbc_read_page;
|
||||||
chip->ecc.write_page = fsl_elbc_write_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 CS Base Register selects full hardware ECC then use it */
|
||||||
if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
|
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 nand_hw_control controller;
|
||||||
struct fsl_ifc_mtd *chips[FSL_IFC_BANK_COUNT];
|
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 page; /* Last page written to / read from */
|
||||||
unsigned int read_bytes;/* Number of bytes read during command */
|
unsigned int read_bytes;/* Number of bytes read during command */
|
||||||
unsigned int column; /* Saved column from SEQIN */
|
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
|
* The chip always seems to report that it is
|
||||||
* write-protected, even when it is not.
|
* 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;
|
return;
|
||||||
|
|
||||||
case NAND_CMD_RESET:
|
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;
|
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;
|
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 nand_chip *chip = mtd->priv;
|
||||||
struct fsl_ifc_mtd *priv = chip->priv;
|
struct fsl_ifc_mtd *priv = chip->priv;
|
||||||
|
unsigned int offset;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If there are still bytes in the IFC buffer, then use the
|
* If there are still bytes in the IFC buffer, then use the
|
||||||
* next byte.
|
* next byte.
|
||||||
*/
|
*/
|
||||||
if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes)
|
if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) {
|
||||||
return in_8(&ifc_nand_ctrl->addr[ifc_nand_ctrl->index++]);
|
offset = ifc_nand_ctrl->index++;
|
||||||
|
return in_8(ifc_nand_ctrl->addr + offset);
|
||||||
|
}
|
||||||
|
|
||||||
dev_err(priv->dev, "%s: beyond end of buffer\n", __func__);
|
dev_err(priv->dev, "%s: beyond end of buffer\n", __func__);
|
||||||
return ERR_BYTE;
|
return ERR_BYTE;
|
||||||
@ -675,8 +681,7 @@ static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd)
|
|||||||
* next byte.
|
* next byte.
|
||||||
*/
|
*/
|
||||||
if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) {
|
if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) {
|
||||||
data = in_be16((uint16_t __iomem *)&ifc_nand_ctrl->
|
data = in_be16(ifc_nand_ctrl->addr + ifc_nand_ctrl->index);
|
||||||
addr[ifc_nand_ctrl->index]);
|
|
||||||
ifc_nand_ctrl->index += 2;
|
ifc_nand_ctrl->index += 2;
|
||||||
return (uint8_t) data;
|
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,
|
avail = min((unsigned int)len,
|
||||||
ifc_nand_ctrl->read_bytes - ifc_nand_ctrl->index);
|
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;
|
ifc_nand_ctrl->index += avail;
|
||||||
|
|
||||||
if (len > avail)
|
if (len > avail)
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
#define MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0 11
|
#define MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0 11
|
||||||
#define MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0 (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0)
|
#define MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0 (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0)
|
||||||
#define BF_BCH_FLASH0LAYOUT0_ECC0(v, x) \
|
#define BF_BCH_FLASH0LAYOUT0_ECC0(v, x) \
|
||||||
(GPMI_IS_MX6Q(x) \
|
(GPMI_IS_MX6(x) \
|
||||||
? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) \
|
? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) \
|
||||||
& MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0) \
|
& MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0) \
|
||||||
: (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) \
|
: (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) \
|
||||||
@ -65,7 +65,7 @@
|
|||||||
#define MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14 \
|
#define MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14 \
|
||||||
(0x1 << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14)
|
(0x1 << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14)
|
||||||
#define BF_BCH_FLASH0LAYOUT0_GF(v, x) \
|
#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) \
|
? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14) \
|
||||||
& MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14) \
|
& MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14) \
|
||||||
: 0 \
|
: 0 \
|
||||||
@ -77,7 +77,7 @@
|
|||||||
#define MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \
|
#define MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \
|
||||||
(0x3ff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)
|
(0x3ff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)
|
||||||
#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v, x) \
|
#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) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \
|
||||||
: ((v) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \
|
: ((v) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \
|
||||||
)
|
)
|
||||||
@ -96,7 +96,7 @@
|
|||||||
#define MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN 11
|
#define MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN 11
|
||||||
#define MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN)
|
#define MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN)
|
||||||
#define BF_BCH_FLASH0LAYOUT1_ECCN(v, x) \
|
#define BF_BCH_FLASH0LAYOUT1_ECCN(v, x) \
|
||||||
(GPMI_IS_MX6Q(x) \
|
(GPMI_IS_MX6(x) \
|
||||||
? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) \
|
? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) \
|
||||||
& MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN) \
|
& MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN) \
|
||||||
: (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) \
|
: (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) \
|
||||||
@ -107,7 +107,7 @@
|
|||||||
#define MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14 \
|
#define MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14 \
|
||||||
(0x1 << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14)
|
(0x1 << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14)
|
||||||
#define BF_BCH_FLASH0LAYOUT1_GF(v, x) \
|
#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) \
|
? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14) \
|
||||||
& MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14) \
|
& MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14) \
|
||||||
: 0 \
|
: 0 \
|
||||||
@ -119,7 +119,7 @@
|
|||||||
#define MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \
|
#define MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \
|
||||||
(0x3ff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE)
|
(0x3ff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE)
|
||||||
#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v, x) \
|
#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) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \
|
||||||
: ((v) & 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;
|
struct resources *r = &this->resources;
|
||||||
unsigned long rate = clk_get_rate(r->clock[0]);
|
unsigned long rate = clk_get_rate(r->clock[0]);
|
||||||
int mode = this->timing_mode;
|
int mode = this->timing_mode;
|
||||||
int dll_threshold = 16; /* in ns */
|
int dll_threshold = this->devdata->max_chain_delay;
|
||||||
unsigned long delay;
|
unsigned long delay;
|
||||||
unsigned long clk_period;
|
unsigned long clk_period;
|
||||||
int t_rea;
|
int t_rea;
|
||||||
@ -886,9 +886,6 @@ static void gpmi_compute_edo_timing(struct gpmi_nand_data *this,
|
|||||||
/* [3] for GPMI_HW_GPMI_CTRL1 */
|
/* [3] for GPMI_HW_GPMI_CTRL1 */
|
||||||
hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
|
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}.
|
* Enlarge 10 times for the numerator and denominator in {3}.
|
||||||
* This make us to get more accurate result.
|
* 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;
|
struct nand_chip *chip = &this->nand;
|
||||||
|
|
||||||
/* Enable the asynchronous EDO feature. */
|
/* 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);
|
int mode = onfi_get_async_timing_mode(chip);
|
||||||
|
|
||||||
/* We only support the timing mode 4 and mode 5. */
|
/* 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)) {
|
if (GPMI_IS_MX23(this)) {
|
||||||
mask = MX23_BM_GPMI_DEBUG_READY0 << chip;
|
mask = MX23_BM_GPMI_DEBUG_READY0 << chip;
|
||||||
reg = readl(r->gpmi_regs + HW_GPMI_DEBUG);
|
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
|
* In the imx6, all the ready/busy pins are bound
|
||||||
* together. So we only need to check chip 0.
|
* together. So we only need to check chip 0.
|
||||||
*/
|
*/
|
||||||
if (GPMI_IS_MX6Q(this))
|
if (GPMI_IS_MX6(this))
|
||||||
chip = 0;
|
chip = 0;
|
||||||
|
|
||||||
/* MX28 shares the same R/B register as MX6Q. */
|
/* 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} }
|
.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)
|
static irqreturn_t bch_irq(int irq, void *cookie)
|
||||||
{
|
{
|
||||||
struct gpmi_nand_data *this = 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. */
|
/* The mx23/mx28 only support the GF13. */
|
||||||
if (geo->gf_len == 14)
|
if (geo->gf_len == 14)
|
||||||
return false;
|
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."
|
"We can not support this nand chip."
|
||||||
" Its required ecc strength(%d) is beyond our"
|
" Its required ecc strength(%d) is beyond our"
|
||||||
" capability(%d).\n", geo->ecc_strength,
|
" capability(%d).\n", geo->ecc_strength,
|
||||||
(GPMI_IS_MX6Q(this) ? MX6_ECC_STRENGTH_MAX
|
this->devdata->bch_max_ecc_strength);
|
||||||
: MXS_ECC_STRENGTH_MAX));
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -572,7 +589,7 @@ static int gpmi_get_clks(struct gpmi_nand_data *this)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Get extra clocks */
|
/* Get extra clocks */
|
||||||
if (GPMI_IS_MX6Q(this))
|
if (GPMI_IS_MX6(this))
|
||||||
extra_clks = extra_clks_for_mx6q;
|
extra_clks = extra_clks_for_mx6q;
|
||||||
if (!extra_clks)
|
if (!extra_clks)
|
||||||
return 0;
|
return 0;
|
||||||
@ -590,9 +607,9 @@ static int gpmi_get_clks(struct gpmi_nand_data *this)
|
|||||||
r->clock[i] = clk;
|
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
|
* If you want to use the ONFI nand which is in the
|
||||||
* Synchronous Mode, you should change the clock as you need.
|
* 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
|
* (1) the chip is imx6, and
|
||||||
* (2) the size of the ECC parity is byte aligned.
|
* (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) {
|
((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) {
|
||||||
ecc->read_subpage = gpmi_ecc_read_subpage;
|
ecc->read_subpage = gpmi_ecc_read_subpage;
|
||||||
chip->options |= NAND_SUBPAGE_READ;
|
chip->options |= NAND_SUBPAGE_READ;
|
||||||
@ -1711,7 +1728,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto err_out;
|
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)
|
if (ret)
|
||||||
goto err_out;
|
goto err_out;
|
||||||
|
|
||||||
@ -1740,23 +1757,19 @@ err_out:
|
|||||||
return ret;
|
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[] = {
|
static const struct of_device_id gpmi_nand_id_table[] = {
|
||||||
{
|
{
|
||||||
.compatible = "fsl,imx23-gpmi-nand",
|
.compatible = "fsl,imx23-gpmi-nand",
|
||||||
.data = (void *)&gpmi_ids[IS_MX23],
|
.data = (void *)&gpmi_devdata_imx23,
|
||||||
}, {
|
}, {
|
||||||
.compatible = "fsl,imx28-gpmi-nand",
|
.compatible = "fsl,imx28-gpmi-nand",
|
||||||
.data = (void *)&gpmi_ids[IS_MX28],
|
.data = (void *)&gpmi_devdata_imx28,
|
||||||
}, {
|
}, {
|
||||||
.compatible = "fsl,imx6q-gpmi-nand",
|
.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);
|
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;
|
const struct of_device_id *of_id;
|
||||||
int ret;
|
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);
|
of_id = of_match_device(gpmi_nand_id_table, &pdev->dev);
|
||||||
if (of_id) {
|
if (of_id) {
|
||||||
pdev->id_entry = of_id->data;
|
this->devdata = of_id->data;
|
||||||
} else {
|
} else {
|
||||||
dev_err(&pdev->dev, "Failed to find the right device id.\n");
|
dev_err(&pdev->dev, "Failed to find the right device id.\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL);
|
|
||||||
if (!this)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, this);
|
platform_set_drvdata(pdev, this);
|
||||||
this->pdev = pdev;
|
this->pdev = pdev;
|
||||||
this->dev = &pdev->dev;
|
this->dev = &pdev->dev;
|
||||||
@ -1823,7 +1836,6 @@ static struct platform_driver gpmi_nand_driver = {
|
|||||||
},
|
},
|
||||||
.probe = gpmi_nand_probe,
|
.probe = gpmi_nand_probe,
|
||||||
.remove = gpmi_nand_remove,
|
.remove = gpmi_nand_remove,
|
||||||
.id_table = gpmi_ids,
|
|
||||||
};
|
};
|
||||||
module_platform_driver(gpmi_nand_driver);
|
module_platform_driver(gpmi_nand_driver);
|
||||||
|
|
||||||
|
@ -119,11 +119,25 @@ struct nand_timing {
|
|||||||
int8_t tRHOH_in_ns;
|
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 {
|
struct gpmi_nand_data {
|
||||||
/* flags */
|
/* flags */
|
||||||
#define GPMI_ASYNC_EDO_ENABLED (1 << 0)
|
#define GPMI_ASYNC_EDO_ENABLED (1 << 0)
|
||||||
#define GPMI_TIMING_INIT_OK (1 << 1)
|
#define GPMI_TIMING_INIT_OK (1 << 1)
|
||||||
int flags;
|
int flags;
|
||||||
|
const struct gpmi_devdata *devdata;
|
||||||
|
|
||||||
/* System Interface */
|
/* System Interface */
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
@ -281,15 +295,11 @@ extern int gpmi_read_page(struct gpmi_nand_data *,
|
|||||||
#define STATUS_ERASED 0xff
|
#define STATUS_ERASED 0xff
|
||||||
#define STATUS_UNCORRECTABLE 0xfe
|
#define STATUS_UNCORRECTABLE 0xfe
|
||||||
|
|
||||||
/* BCH's bit correction capability. */
|
/* Use the devdata to distinguish different Archs. */
|
||||||
#define MXS_ECC_STRENGTH_MAX 20 /* mx23 and mx28 */
|
#define GPMI_IS_MX23(x) ((x)->devdata->type == IS_MX23)
|
||||||
#define MX6_ECC_STRENGTH_MAX 40
|
#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 GPMI_IS_MX6(x) (GPMI_IS_MX6Q(x) || GPMI_IS_MX6SX(x))
|
||||||
#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)
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/mtd/nand.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.
|
* ecc.pos. Let's make sure that there are no gaps in ECC positions.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < eccfrag_len - 1; i++) {
|
for (i = 0; i < eccfrag_len - 1; i++) {
|
||||||
if (eccpos[i + start_step * chip->ecc.bytes] + 1 !=
|
if (eccpos[i + index] + 1 != eccpos[i + index + 1]) {
|
||||||
eccpos[i + start_step * chip->ecc.bytes + 1]) {
|
|
||||||
gaps = 1;
|
gaps = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1501,6 +1501,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
|||||||
mtd->oobavail : mtd->oobsize;
|
mtd->oobavail : mtd->oobsize;
|
||||||
|
|
||||||
uint8_t *bufpoi, *oob, *buf;
|
uint8_t *bufpoi, *oob, *buf;
|
||||||
|
int use_bufpoi;
|
||||||
unsigned int max_bitflips = 0;
|
unsigned int max_bitflips = 0;
|
||||||
int retry_mode = 0;
|
int retry_mode = 0;
|
||||||
bool ecc_fail = false;
|
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);
|
bytes = min(mtd->writesize - col, readlen);
|
||||||
aligned = (bytes == mtd->writesize);
|
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? */
|
/* Is the current page in the buffer? */
|
||||||
if (realpage != chip->pagebuf || oob) {
|
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:
|
read_retry:
|
||||||
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
|
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
|
||||||
@ -1547,7 +1559,7 @@ read_retry:
|
|||||||
ret = chip->ecc.read_page(mtd, chip, bufpoi,
|
ret = chip->ecc.read_page(mtd, chip, bufpoi,
|
||||||
oob_required, page);
|
oob_required, page);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (!aligned)
|
if (use_bufpoi)
|
||||||
/* Invalidate page cache */
|
/* Invalidate page cache */
|
||||||
chip->pagebuf = -1;
|
chip->pagebuf = -1;
|
||||||
break;
|
break;
|
||||||
@ -1556,7 +1568,7 @@ read_retry:
|
|||||||
max_bitflips = max_t(unsigned int, max_bitflips, ret);
|
max_bitflips = max_t(unsigned int, max_bitflips, ret);
|
||||||
|
|
||||||
/* Transfer not aligned data */
|
/* Transfer not aligned data */
|
||||||
if (!aligned) {
|
if (use_bufpoi) {
|
||||||
if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&
|
if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&
|
||||||
!(mtd->ecc_stats.failed - ecc_failures) &&
|
!(mtd->ecc_stats.failed - ecc_failures) &&
|
||||||
(ops->mode != MTD_OPS_RAW)) {
|
(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 bytes = mtd->writesize;
|
||||||
int cached = writelen > bytes && page != blockmask;
|
int cached = writelen > bytes && page != blockmask;
|
||||||
uint8_t *wbuf = buf;
|
uint8_t *wbuf = buf;
|
||||||
|
int use_bufpoi;
|
||||||
|
int part_pagewr = (column || writelen < (mtd->writesize - 1));
|
||||||
|
|
||||||
/* Partial page write? */
|
if (part_pagewr)
|
||||||
if (unlikely(column || writelen < (mtd->writesize - 1))) {
|
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;
|
cached = 0;
|
||||||
bytes = min_t(int, bytes - column, (int) writelen);
|
if (part_pagewr)
|
||||||
|
bytes = min_t(int, bytes - column, writelen);
|
||||||
chip->pagebuf = -1;
|
chip->pagebuf = -1;
|
||||||
memset(chip->buffers->databuf, 0xff, mtd->writesize);
|
memset(chip->buffers->databuf, 0xff, mtd->writesize);
|
||||||
memcpy(&chip->buffers->databuf[column], buf, bytes);
|
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
|
* @mtd: MTD device structure
|
||||||
* @page: the page address of the block which will be erased
|
* @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;
|
struct nand_chip *chip = mtd->priv;
|
||||||
/* Send commands to erase a block */
|
/* Send commands to erase a block */
|
||||||
chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
|
chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
|
||||||
chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
|
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))
|
(page + pages_per_block))
|
||||||
chip->pagebuf = -1;
|
chip->pagebuf = -1;
|
||||||
|
|
||||||
chip->erase_cmd(mtd, page & chip->pagemask);
|
status = chip->erase(mtd, page & chip->pagemask);
|
||||||
|
|
||||||
status = chip->waitfunc(mtd, chip);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* See if operation failed and additional status checks are
|
* 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;
|
chip->onfi_version = 0;
|
||||||
if (!type->name || !type->pagesize) {
|
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))
|
if (nand_flash_detect_onfi(mtd, chip, &busw))
|
||||||
goto ident_done;
|
goto ident_done;
|
||||||
|
|
||||||
@ -3685,7 +3709,7 @@ ident_done:
|
|||||||
}
|
}
|
||||||
|
|
||||||
chip->badblockbits = 8;
|
chip->badblockbits = 8;
|
||||||
chip->erase_cmd = single_erase_cmd;
|
chip->erase = single_erase;
|
||||||
|
|
||||||
/* Do not replace user supplied command function! */
|
/* Do not replace user supplied command function! */
|
||||||
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
|
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);
|
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
|
* 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;
|
ecc->layout->oobavail += ecc->layout->oobfree[i].length;
|
||||||
mtd->oobavail = ecc->layout->oobavail;
|
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
|
* Set the number of read / write steps for one page depending on ECC
|
||||||
* mode.
|
* mode.
|
||||||
@ -4023,8 +4083,16 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|||||||
chip->pagebuf = -1;
|
chip->pagebuf = -1;
|
||||||
|
|
||||||
/* Large page NAND with SOFT_ECC should support subpage reads */
|
/* Large page NAND with SOFT_ECC should support subpage reads */
|
||||||
if ((ecc->mode == NAND_ECC_SOFT) && (chip->page_shift > 9))
|
switch (ecc->mode) {
|
||||||
chip->options |= NAND_SUBPAGE_READ;
|
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 */
|
/* Fill in remaining MTD driver data */
|
||||||
mtd->type = nand_is_slc(chip) ? MTD_NANDFLASH : MTD_MLCNANDFLASH;
|
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;
|
struct nand_chip *this = mtd->priv;
|
||||||
int i, chips;
|
int i, chips;
|
||||||
int bits, startblock, block, dir;
|
int startblock, block, dir;
|
||||||
int scanlen = mtd->writesize + mtd->oobsize;
|
int scanlen = mtd->writesize + mtd->oobsize;
|
||||||
int bbtblocks;
|
int bbtblocks;
|
||||||
int blocktopage = this->bbt_erase_shift - this->page_shift;
|
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;
|
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++) {
|
for (i = 0; i < chips; i++) {
|
||||||
/* Reset version information */
|
/* Reset version information */
|
||||||
td->version[i] = 0;
|
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)
|
int nand_default_bbt(struct mtd_info *mtd)
|
||||||
{
|
{
|
||||||
struct nand_chip *this = mtd->priv;
|
struct nand_chip *this = mtd->priv;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* Is a flash based bad block table requested? */
|
/* Is a flash based bad block table requested? */
|
||||||
if (this->bbt_options & NAND_BBT_USE_FLASH) {
|
if (this->bbt_options & NAND_BBT_USE_FLASH) {
|
||||||
@ -1303,8 +1301,11 @@ int nand_default_bbt(struct mtd_info *mtd)
|
|||||||
this->bbt_md = NULL;
|
this->bbt_md = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->badblock_pattern)
|
if (!this->badblock_pattern) {
|
||||||
nand_create_badblock_pattern(this);
|
ret = nand_create_badblock_pattern(this);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return nand_scan_bbt(mtd, this->badblock_pattern);
|
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)
|
if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1)
|
||||||
return 1; /* error in ECC data; no action needed */
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(__nand_correct_data);
|
EXPORT_SYMBOL(__nand_correct_data);
|
||||||
|
@ -137,6 +137,10 @@
|
|||||||
#define BADBLOCK_MARKER_LENGTH 2
|
#define BADBLOCK_MARKER_LENGTH 2
|
||||||
|
|
||||||
#ifdef CONFIG_MTD_NAND_OMAP_BCH
|
#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,
|
static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
|
||||||
0xac, 0x6b, 0xff, 0x99, 0x7b};
|
0xac, 0x6b, 0xff, 0x99, 0x7b};
|
||||||
static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
|
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;
|
ecc_size1 = BCH_ECC_SIZE1;
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
return;
|
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;
|
struct gpmc_nand_regs *gpmc_regs = &info->reg;
|
||||||
u8 *ecc_code;
|
u8 *ecc_code;
|
||||||
unsigned long nsectors, bch_val1, bch_val2, bch_val3, bch_val4;
|
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;
|
nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
|
||||||
for (i = 0; i < nsectors; i++) {
|
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 >> 4) & 0xFF);
|
||||||
*ecc_code++ = ((bch_val1 & 0xF) << 4);
|
*ecc_code++ = ((bch_val1 & 0xF) << 4);
|
||||||
break;
|
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:
|
default:
|
||||||
return -EINVAL;
|
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:
|
case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
|
||||||
/* Add constant polynomial to remainder, so that
|
/* Add constant polynomial to remainder, so that
|
||||||
* ECC of blank pages results in 0x0 on reading back */
|
* ECC of blank pages results in 0x0 on reading back */
|
||||||
for (i = 0; i < eccbytes; i++)
|
for (j = 0; j < eccbytes; j++)
|
||||||
ecc_calc[i] ^= bch4_polynomial[i];
|
ecc_calc[j] ^= bch4_polynomial[j];
|
||||||
break;
|
break;
|
||||||
case OMAP_ECC_BCH4_CODE_HW:
|
case OMAP_ECC_BCH4_CODE_HW:
|
||||||
/* Set 8th ECC byte as 0x0 for ROM compatibility */
|
/* 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:
|
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
|
||||||
/* Add constant polynomial to remainder, so that
|
/* Add constant polynomial to remainder, so that
|
||||||
* ECC of blank pages results in 0x0 on reading back */
|
* ECC of blank pages results in 0x0 on reading back */
|
||||||
for (i = 0; i < eccbytes; i++)
|
for (j = 0; j < eccbytes; j++)
|
||||||
ecc_calc[i] ^= bch8_polynomial[i];
|
ecc_calc[j] ^= bch8_polynomial[j];
|
||||||
break;
|
break;
|
||||||
case OMAP_ECC_BCH8_CODE_HW:
|
case OMAP_ECC_BCH8_CODE_HW:
|
||||||
/* Set 14th ECC byte as 0x0 for ROM compatibility */
|
/* Set 14th ECC byte as 0x0 for ROM compatibility */
|
||||||
ecc_calc[eccbytes - 1] = 0x0;
|
ecc_calc[eccbytes - 1] = 0x0;
|
||||||
break;
|
break;
|
||||||
|
case OMAP_ECC_BCH16_CODE_HW:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@ -1237,6 +1292,7 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_MTD_NAND_OMAP_BCH
|
||||||
/**
|
/**
|
||||||
* erased_sector_bitflips - count bit flips
|
* erased_sector_bitflips - count bit flips
|
||||||
* @data: data sector buffer
|
* @data: data sector buffer
|
||||||
@ -1276,7 +1332,6 @@ static int erased_sector_bitflips(u_char *data, u_char *oob,
|
|||||||
return flip_bits;
|
return flip_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_MTD_NAND_OMAP_BCH
|
|
||||||
/**
|
/**
|
||||||
* omap_elm_correct_data - corrects page data area in case error reported
|
* omap_elm_correct_data - corrects page data area in case error reported
|
||||||
* @mtd: MTD device structure
|
* @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;
|
actual_eccbytes = ecc->bytes - 1;
|
||||||
erased_ecc_vec = bch8_vector;
|
erased_ecc_vec = bch8_vector;
|
||||||
break;
|
break;
|
||||||
|
case OMAP_ECC_BCH16_CODE_HW:
|
||||||
|
actual_eccbytes = ecc->bytes;
|
||||||
|
erased_ecc_vec = bch16_vector;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
pr_err("invalid driver configuration\n");
|
pr_err("invalid driver configuration\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -1382,7 +1441,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
|
|||||||
|
|
||||||
/* Check if any error reported */
|
/* Check if any error reported */
|
||||||
if (!is_error_reported)
|
if (!is_error_reported)
|
||||||
return 0;
|
return stat;
|
||||||
|
|
||||||
/* Decode BCH error using ELM module */
|
/* Decode BCH error using ELM module */
|
||||||
elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec);
|
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;
|
BCH4_BIT_PAD;
|
||||||
break;
|
break;
|
||||||
case OMAP_ECC_BCH8_CODE_HW:
|
case OMAP_ECC_BCH8_CODE_HW:
|
||||||
|
case OMAP_ECC_BCH16_CODE_HW:
|
||||||
pos = err_vec[i].error_loc[j];
|
pos = err_vec[i].error_loc[j];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -1912,6 +1972,40 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|||||||
goto return_error;
|
goto return_error;
|
||||||
#endif
|
#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:
|
default:
|
||||||
pr_err("nand: error: invalid or unsupported ECC scheme\n");
|
pr_err("nand: error: invalid or unsupported ECC scheme\n");
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
|
@ -214,7 +214,7 @@ static int orion_nand_remove(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#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", },
|
{ .compatible = "marvell,orion-nand", },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
@ -127,10 +127,10 @@
|
|||||||
|
|
||||||
/* macros for registers read/write */
|
/* macros for registers read/write */
|
||||||
#define nand_writel(info, off, val) \
|
#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) \
|
#define nand_readl(info, off) \
|
||||||
__raw_readl((info)->mmio_base + (off))
|
readl_relaxed((info)->mmio_base + (off))
|
||||||
|
|
||||||
/* error code and state */
|
/* error code and state */
|
||||||
enum {
|
enum {
|
||||||
@ -337,7 +337,7 @@ static struct nand_ecclayout ecc_layout_4KB_bch8bit = {
|
|||||||
/* convert nano-seconds to nand flash controller clock cycles */
|
/* convert nano-seconds to nand flash controller clock cycles */
|
||||||
#define ns2cycle(ns, clk) (int)((ns) * (clk / 1000000) / 1000)
|
#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",
|
.compatible = "marvell,pxa3xx-nand",
|
||||||
.data = (void *)PXA3XX_NAND_VARIANT_PXA,
|
.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->mode = NAND_ECC_HW;
|
||||||
ecc->size = 512;
|
ecc->size = 512;
|
||||||
ecc->strength = 1;
|
ecc->strength = 1;
|
||||||
return 1;
|
|
||||||
|
|
||||||
} else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) {
|
} else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) {
|
||||||
info->chunk_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->mode = NAND_ECC_HW;
|
||||||
ecc->size = 512;
|
ecc->size = 512;
|
||||||
ecc->strength = 1;
|
ecc->strength = 1;
|
||||||
return 1;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Required ECC: 4-bit correction per 512 bytes
|
* 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->size = info->chunk_size;
|
||||||
ecc->layout = &ecc_layout_2KB_bch4bit;
|
ecc->layout = &ecc_layout_2KB_bch4bit;
|
||||||
ecc->strength = 16;
|
ecc->strength = 16;
|
||||||
return 1;
|
|
||||||
|
|
||||||
} else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) {
|
} else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) {
|
||||||
info->ecc_bch = 1;
|
info->ecc_bch = 1;
|
||||||
@ -1389,7 +1386,6 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
|
|||||||
ecc->size = info->chunk_size;
|
ecc->size = info->chunk_size;
|
||||||
ecc->layout = &ecc_layout_4KB_bch4bit;
|
ecc->layout = &ecc_layout_4KB_bch4bit;
|
||||||
ecc->strength = 16;
|
ecc->strength = 16;
|
||||||
return 1;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Required ECC: 8-bit correction per 512 bytes
|
* 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->size = info->chunk_size;
|
||||||
ecc->layout = &ecc_layout_4KB_bch8bit;
|
ecc->layout = &ecc_layout_4KB_bch8bit;
|
||||||
ecc->strength = 16;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1516,8 +1519,13 @@ KEEP_CONFIG:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ecc_strength = chip->ecc_strength_ds;
|
if (pdata->ecc_strength && pdata->ecc_step_size) {
|
||||||
ecc_step = chip->ecc_step_ds;
|
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 */
|
/* Set default ECC strength requirements on non-ONFI devices */
|
||||||
if (ecc_strength < 1 && ecc_step < 1) {
|
if (ecc_strength < 1 && ecc_step < 1) {
|
||||||
@ -1527,12 +1535,8 @@ KEEP_CONFIG:
|
|||||||
|
|
||||||
ret = pxa_ecc_init(info, &chip->ecc, ecc_strength,
|
ret = pxa_ecc_init(info, &chip->ecc, ecc_strength,
|
||||||
ecc_step, mtd->writesize);
|
ecc_step, mtd->writesize);
|
||||||
if (!ret) {
|
if (ret)
|
||||||
dev_err(&info->pdev->dev,
|
return ret;
|
||||||
"ECC strength %d at page size %d is not supported\n",
|
|
||||||
ecc_strength, mtd->writesize);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* calculate addressing information */
|
/* calculate addressing information */
|
||||||
if (mtd->writesize >= 2048)
|
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);
|
of_property_read_u32(np, "num-cs", &pdata->num_cs);
|
||||||
pdata->flash_bbt = of_get_nand_on_flash_bbt(np);
|
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;
|
pdev->dev.platform_data = pdata;
|
||||||
|
|
||||||
return 0;
|
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 */
|
/* write DWORD chinks - faster */
|
||||||
while (len) {
|
while (len >= 4) {
|
||||||
reg = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
|
reg = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
|
||||||
r852_write_reg_dword(dev, R852_DATALINE, reg);
|
r852_write_reg_dword(dev, R852_DATALINE, reg);
|
||||||
buf += 4;
|
buf += 4;
|
||||||
@ -254,8 +254,10 @@ static void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* write rest */
|
/* write rest */
|
||||||
while (len)
|
while (len > 0) {
|
||||||
r852_write_reg(dev, R852_DATALINE, *buf++);
|
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;
|
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;
|
void __iomem *base = onenand->dma_addr;
|
||||||
int status;
|
int status;
|
||||||
@ -605,7 +605,7 @@ static irqreturn_t s5pc110_onenand_irq(int irq, void *data)
|
|||||||
return IRQ_HANDLED;
|
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;
|
void __iomem *base = onenand->dma_addr;
|
||||||
int status;
|
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);
|
dev_err(dev, "Couldn't map a %d byte buffer for DMA\n", count);
|
||||||
goto normal;
|
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);
|
count, S5PC110_DMA_DIR_READ);
|
||||||
|
|
||||||
if (page_dma)
|
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;
|
int err = 0;
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
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) {
|
for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
|
||||||
prandom_bytes_state(&rnd_state, writebuf, use_len);
|
|
||||||
ops.mode = MTD_OPS_AUTO_OOB;
|
ops.mode = MTD_OPS_AUTO_OOB;
|
||||||
ops.len = 0;
|
ops.len = 0;
|
||||||
ops.retlen = 0;
|
ops.retlen = 0;
|
||||||
@ -78,7 +78,7 @@ static int write_eraseblock(int ebnum)
|
|||||||
ops.oobretlen = 0;
|
ops.oobretlen = 0;
|
||||||
ops.ooboffs = use_offset;
|
ops.ooboffs = use_offset;
|
||||||
ops.datbuf = NULL;
|
ops.datbuf = NULL;
|
||||||
ops.oobbuf = writebuf;
|
ops.oobbuf = writebuf + (use_len_max * i) + use_offset;
|
||||||
err = mtd_write_oob(mtd, addr, &ops);
|
err = mtd_write_oob(mtd, addr, &ops);
|
||||||
if (err || ops.oobretlen != use_len) {
|
if (err || ops.oobretlen != use_len) {
|
||||||
pr_err("error: writeoob failed at %#llx\n",
|
pr_err("error: writeoob failed at %#llx\n",
|
||||||
@ -122,8 +122,8 @@ static int verify_eraseblock(int ebnum)
|
|||||||
int err = 0;
|
int err = 0;
|
||||||
loff_t addr = ebnum * mtd->erasesize;
|
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) {
|
for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
|
||||||
prandom_bytes_state(&rnd_state, writebuf, use_len);
|
|
||||||
ops.mode = MTD_OPS_AUTO_OOB;
|
ops.mode = MTD_OPS_AUTO_OOB;
|
||||||
ops.len = 0;
|
ops.len = 0;
|
||||||
ops.retlen = 0;
|
ops.retlen = 0;
|
||||||
@ -139,7 +139,8 @@ static int verify_eraseblock(int ebnum)
|
|||||||
errcnt += 1;
|
errcnt += 1;
|
||||||
return err ? err : -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",
|
pr_err("error: verify failed at %#llx\n",
|
||||||
(long long)addr);
|
(long long)addr);
|
||||||
errcnt += 1;
|
errcnt += 1;
|
||||||
@ -166,7 +167,9 @@ static int verify_eraseblock(int ebnum)
|
|||||||
errcnt += 1;
|
errcnt += 1;
|
||||||
return err ? err : -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",
|
pr_err("error: verify failed at %#llx\n",
|
||||||
(long long)addr);
|
(long long)addr);
|
||||||
errcnt += 1;
|
errcnt += 1;
|
||||||
@ -566,8 +569,8 @@ static int __init mtd_oobtest_init(void)
|
|||||||
if (bbt[i] || bbt[i + 1])
|
if (bbt[i] || bbt[i + 1])
|
||||||
continue;
|
continue;
|
||||||
addr = (i + 1) * mtd->erasesize - mtd->writesize;
|
addr = (i + 1) * mtd->erasesize - mtd->writesize;
|
||||||
|
prandom_bytes_state(&rnd_state, writebuf, sz * cnt);
|
||||||
for (pg = 0; pg < cnt; ++pg) {
|
for (pg = 0; pg < cnt; ++pg) {
|
||||||
prandom_bytes_state(&rnd_state, writebuf, sz);
|
|
||||||
ops.mode = MTD_OPS_AUTO_OOB;
|
ops.mode = MTD_OPS_AUTO_OOB;
|
||||||
ops.len = 0;
|
ops.len = 0;
|
||||||
ops.retlen = 0;
|
ops.retlen = 0;
|
||||||
@ -575,7 +578,7 @@ static int __init mtd_oobtest_init(void)
|
|||||||
ops.oobretlen = 0;
|
ops.oobretlen = 0;
|
||||||
ops.ooboffs = 0;
|
ops.ooboffs = 0;
|
||||||
ops.datbuf = NULL;
|
ops.datbuf = NULL;
|
||||||
ops.oobbuf = writebuf;
|
ops.oobbuf = writebuf + pg * sz;
|
||||||
err = mtd_write_oob(mtd, addr, &ops);
|
err = mtd_write_oob(mtd, addr, &ops);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -175,6 +175,11 @@ typedef enum {
|
|||||||
#define NAND_OWN_BUFFERS 0x00020000
|
#define NAND_OWN_BUFFERS 0x00020000
|
||||||
/* Chip may not exist, so silence any errors in scan */
|
/* Chip may not exist, so silence any errors in scan */
|
||||||
#define NAND_SCAN_SILENT_NODEV 0x00040000
|
#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.
|
* Autodetect nand buswidth with readid/onfi.
|
||||||
* This suppose the driver will configure the hardware in 8 bits mode
|
* This suppose the driver will configure the hardware in 8 bits mode
|
||||||
@ -552,8 +557,7 @@ struct nand_buffers {
|
|||||||
* @ecc: [BOARDSPECIFIC] ECC control structure
|
* @ecc: [BOARDSPECIFIC] ECC control structure
|
||||||
* @buffers: buffer structure for read/write
|
* @buffers: buffer structure for read/write
|
||||||
* @hwcontrol: platform-specific hardware control structure
|
* @hwcontrol: platform-specific hardware control structure
|
||||||
* @erase_cmd: [INTERN] erase command write function, selectable due
|
* @erase: [REPLACEABLE] erase function
|
||||||
* to AND support.
|
|
||||||
* @scan_bbt: [REPLACEABLE] function to scan bad block table
|
* @scan_bbt: [REPLACEABLE] function to scan bad block table
|
||||||
* @chip_delay: [BOARDSPECIFIC] chip dependent delay for transferring
|
* @chip_delay: [BOARDSPECIFIC] chip dependent delay for transferring
|
||||||
* data from array to read regs (tR).
|
* data from array to read regs (tR).
|
||||||
@ -637,7 +641,7 @@ struct nand_chip {
|
|||||||
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,
|
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,
|
||||||
int page_addr);
|
int page_addr);
|
||||||
int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
|
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 (*scan_bbt)(struct mtd_info *mtd);
|
||||||
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
|
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
|
||||||
int status, int page);
|
int status, int page);
|
||||||
|
@ -101,9 +101,6 @@ static inline void send_pfow_command(struct map_info *map,
|
|||||||
unsigned long len, map_word *datum)
|
unsigned long len, map_word *datum)
|
||||||
{
|
{
|
||||||
int bits_per_chip = map_bankwidth(map) * 8;
|
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(cmd_code), map->pfow_base + PFOW_COMMAND_CODE);
|
||||||
map_write(map, CMD(adr & ((1<<bits_per_chip) - 1)),
|
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 {
|
enum bch_ecc {
|
||||||
BCH4_ECC = 0,
|
BCH4_ECC = 0,
|
||||||
BCH8_ECC,
|
BCH8_ECC,
|
||||||
|
BCH16_ECC,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ELM support 8 error syndrome process */
|
/* ELM support 8 error syndrome process */
|
||||||
@ -38,7 +39,7 @@ struct elm_errorvec {
|
|||||||
bool error_reported;
|
bool error_reported;
|
||||||
bool error_uncorrectable;
|
bool error_uncorrectable;
|
||||||
int error_count;
|
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,
|
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,
|
OMAP_ECC_BCH8_CODE_HW_DETECTION_SW,
|
||||||
/* 8-bit ECC calculation by GPMC, Error detection by ELM */
|
/* 8-bit ECC calculation by GPMC, Error detection by ELM */
|
||||||
OMAP_ECC_BCH8_CODE_HW,
|
OMAP_ECC_BCH8_CODE_HW,
|
||||||
|
/* 16-bit ECC calculation by GPMC, Error detection by ELM */
|
||||||
|
OMAP_ECC_BCH16_CODE_HW,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct gpmc_nand_regs {
|
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_result1[GPMC_BCH_NUM_REMAINDER];
|
||||||
void __iomem *gpmc_bch_result2[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_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 {
|
struct omap_nand_platform_data {
|
||||||
|
@ -58,6 +58,9 @@ struct pxa3xx_nand_platform_data {
|
|||||||
/* use an flash-based bad block table */
|
/* use an flash-based bad block table */
|
||||||
bool flash_bbt;
|
bool flash_bbt;
|
||||||
|
|
||||||
|
/* requested ECC strength and ECC step size */
|
||||||
|
int ecc_strength, ecc_step_size;
|
||||||
|
|
||||||
const struct mtd_partition *parts[NUM_CHIP_SELECT];
|
const struct mtd_partition *parts[NUM_CHIP_SELECT];
|
||||||
unsigned int nr_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_RAM (MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE)
|
||||||
#define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE)
|
#define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE)
|
||||||
#define MTD_CAP_NANDFLASH (MTD_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) */
|
/* Obsolete ECC byte placement modes (used with obsolete MEMGETOOBSEL) */
|
||||||
#define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended)
|
#define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user