NAND:
 
   Updates from Boris:
 
     """
     This pull request contains only one notable change:
     * Addition of the MTK NAND controller driver
 
     And a bunch of specific NAND driver improvements/fixes. Here are the
     changes that are worth mentioning:
     * A few fixes/improvements for the xway NAND controller driver
     * A few fixes for the sunxi NAND controller driver
     * Support for DMA in the sunxi NAND driver
     * Support for the sunxi NAND controller IP embedded in A23/A33 SoCs
     * Addition for bitflips detection in erased pages to the brcmnand driver
     * Support for new brcmnand IPs
     * Update of the OMAP-GPMC binding to support DMA channel description
     """
 
   In addition, some small fixes around error handling, etc., as well as one
   long-standing corner case issue (2.6.20, I think?) with writing 1 byte less
   than a page.
 
 NOR:
 
  * Rework some error handling on reads and writes, so we can better handle (for
    instance) SPI controllers which have limitations on their maximum transfer size
 
  * Add new Cadence Quad SPI flash controller driver
 
  * Add new Atmel QSPI flash controller driver
 
  * Add new Hisilicon SPI flash controller driver
 
  * Support a few new flash, and update supported features on others
 
  * Fix the logic used for detecting a fully-unlocked flash
 
 And other miscellaneous small fixes.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJXn/j0AAoJEFySrpd9RFgto58P/j1huB0d21zFen3teo8YKKr1
 dLi65mFbqtpU1BLqD07uc2gsH17kvezFJCtx8H8Jp/yCjLF2kYIKL7wDTf6OJPtn
 aYGS5dG5jhMIq+6CD2olKqy+IVLfL9GvCf44Z3fpVta5lOn09y7Jm0AkBjmJcH45
 SdJi+iUkSKhqRY3O2udyauGyL4JWG7eHUonrG9g8ROrO0GWQkT4Ijm1ZwIBlFDkJ
 e1N960OqPg5ISOzuTeM14Ok9IUyeb7wiXhqRfOJgzNk6iBcN1YC2ono3C7RH2sN7
 wiyCqqUpDDCDBDiFdGOdpc9cjzNysrt02ypWRsZIpQVCm89nPLSutqQEWLuo0qzq
 /eIzdwbk1AxX96CeQohOezqL+n6+RHP9AIvwzL9GeWjipD1LBvfM1l3CmuSKK5jb
 bQ4CA/FVz1tO/25q8tuLJfpFzhFE2PC3pphVf8tREL/U6OR/97NgDMuQIuqiQpRc
 4nJtu79yacAzEiztZh0bsx+t94QFE+kfs/6d8m+llLEyx2sI8HKZeDvNRwEB0OsD
 wQ5bjyd54m7+i4H8njrnOTP+K2YrwNjGlbTo7qrRSpFMDr4mD0VQLap03Srvo5xV
 OYB6uGZhGV2L3k6nG5wywM2z6Hw0QfHxjrpcLyG51xABmni2QF3lOdYSllRtRTXp
 aYLfPUqgIgSUI2GTDHyW
 =HQr0
 -----END PGP SIGNATURE-----

Merge tag 'for-linus-20160801' of git://git.infradead.org/linux-mtd

Pull MTD updates from Brian Norris:
 "NAND:

    Quoting Boris:
     'This pull request contains only one notable change:
       - Addition of the MTK NAND controller driver

      And a bunch of specific NAND driver improvements/fixes. Here are the
      changes that are worth mentioning:
       - A few fixes/improvements for the xway NAND controller driver
       - A few fixes for the sunxi NAND controller driver
       - Support for DMA in the sunxi NAND driver
       - Support for the sunxi NAND controller IP embedded in A23/A33 SoCs
       - Addition for bitflips detection in erased pages to the brcmnand driver
       - Support for new brcmnand IPs
       - Update of the OMAP-GPMC binding to support DMA channel description'

    In addition, some small fixes around error handling, etc., as well
    as one long-standing corner case issue (2.6.20, I think?) with
    writing 1 byte less than a page.

  NOR:

   - rework some error handling on reads and writes, so we can better
     handle (for instance) SPI controllers which have limitations on
     their maximum transfer size

   - add new Cadence Quad SPI flash controller driver

   - add new Atmel QSPI flash controller driver

   - add new Hisilicon SPI flash controller driver

   - support a few new flash, and update supported features on others

   - fix the logic used for detecting a fully-unlocked flash

  And other miscellaneous small fixes"

* tag 'for-linus-20160801' of git://git.infradead.org/linux-mtd: (60 commits)
  mtd: spi-nor: don't build Cadence QuadSPI on non-ARM
  mtd: mtk-nor: remove duplicated include from mtk-quadspi.c
  mtd: nand: fix bug writing 1 byte less than page size
  mtd: update description of MTD_BCM47XXSFLASH symbol
  mtd: spi-nor: Add driver for Cadence Quad SPI Flash Controller
  mtd: spi-nor: Bindings for Cadence Quad SPI Flash Controller driver
  mtd: nand: brcmnand: Change BUG_ON in brcmnand_send_cmd
  mtd: pmcmsp-flash: Allocating too much in init_msp_flash()
  mtd: maps: sa1100-flash: potential NULL dereference
  mtd: atmel-quadspi: add driver for Atmel QSPI controller
  mtd: nand: omap2: fix return value check in omap_nand_probe()
  Documentation: atmel-quadspi: add binding file for Atmel QSPI driver
  mtd: spi-nor: add hisilicon spi-nor flash controller driver
  mtd: spi-nor: support dual, quad, and WP for Gigadevice
  mtd: spi-nor: Added support for n25q00a.
  memory: Update dependency of IFC for Layerscape
  mtd: nand: jz4780: Update MODULE_AUTHOR email address
  mtd: nand: sunxi: prevent a small memory leak
  mtd: nand: sunxi: add reset line support
  mtd: nand: sunxi: update DT bindings
  ...
This commit is contained in:
Linus Torvalds 2016-08-02 17:05:11 -04:00
Родитель 44cee85a88 1dcff2e4ae
Коммит affe8a2abd
45 изменённых файлов: 5840 добавлений и 259 удалений

Просмотреть файл

@ -46,6 +46,10 @@ Required properties:
0 maps to GPMC_WAIT0 pin.
- gpio-cells: Must be set to 2
Required properties when using NAND prefetch dma:
- dmas GPMC NAND prefetch dma channel
- dma-names Must be set to "rxtx"
Timing properties for child nodes. All are optional and default to 0.
- gpmc,sync-clk-ps: Minimum clock period for synchronous mode, in picoseconds
@ -137,7 +141,8 @@ Example for an AM33xx board:
ti,hwmods = "gpmc";
reg = <0x50000000 0x2000>;
interrupts = <100>;
dmas = <&edma 52 0>;
dma-names = "rxtx";
gpmc,num-cs = <8>;
gpmc,num-waitpins = <2>;
#address-cells = <2>;

Просмотреть файл

@ -0,0 +1,32 @@
* Atmel Quad Serial Peripheral Interface (QSPI)
Required properties:
- compatible: Should be "atmel,sama5d2-qspi".
- reg: Should contain the locations and lengths of the base registers
and the mapped memory.
- reg-names: Should contain the resource reg names:
- qspi_base: configuration register address space
- qspi_mmap: memory mapped address space
- interrupts: Should contain the interrupt for the device.
- clocks: The phandle of the clock needed by the QSPI controller.
- #address-cells: Should be <1>.
- #size-cells: Should be <0>.
Example:
spi@f0020000 {
compatible = "atmel,sama5d2-qspi";
reg = <0xf0020000 0x100>, <0xd0000000 0x8000000>;
reg-names = "qspi_base", "qspi_mmap";
interrupts = <52 IRQ_TYPE_LEVEL_HIGH 7>;
clocks = <&spi0_clk>;
#address-cells = <1>;
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_spi0_default>;
status = "okay";
m25p80@0 {
...
};
};

Просмотреть файл

@ -27,6 +27,7 @@ Required properties:
brcm,brcmnand-v6.2
brcm,brcmnand-v7.0
brcm,brcmnand-v7.1
brcm,brcmnand-v7.2
brcm,brcmnand
- reg : the register start and length for NAND register region.
(optional) Flash DMA register range (if present)

Просмотреть файл

@ -0,0 +1,56 @@
* Cadence Quad SPI controller
Required properties:
- compatible : Should be "cdns,qspi-nor".
- reg : Contains two entries, each of which is a tuple consisting of a
physical address and length. The first entry is the address and
length of the controller register set. The second entry is the
address and length of the QSPI Controller data area.
- interrupts : Unit interrupt specifier for the controller interrupt.
- clocks : phandle to the Quad SPI clock.
- cdns,fifo-depth : Size of the data FIFO in words.
- cdns,fifo-width : Bus width of the data FIFO in bytes.
- cdns,trigger-address : 32-bit indirect AHB trigger address.
Optional properties:
- cdns,is-decoded-cs : Flag to indicate whether decoder is used or not.
Optional subnodes:
Subnodes of the Cadence Quad SPI controller are spi slave nodes with additional
custom properties:
- cdns,read-delay : Delay for read capture logic, in clock cycles
- cdns,tshsl-ns : Delay in nanoseconds for the length that the master
mode chip select outputs are de-asserted between
transactions.
- cdns,tsd2d-ns : Delay in nanoseconds between one chip select being
de-activated and the activation of another.
- cdns,tchsh-ns : Delay in nanoseconds between last bit of current
transaction and deasserting the device chip select
(qspi_n_ss_out).
- cdns,tslch-ns : Delay in nanoseconds between setting qspi_n_ss_out low
and first bit transfer.
Example:
qspi: spi@ff705000 {
compatible = "cdns,qspi-nor";
#address-cells = <1>;
#size-cells = <0>;
reg = <0xff705000 0x1000>,
<0xffa00000 0x1000>;
interrupts = <0 151 4>;
clocks = <&qspi_clk>;
cdns,is-decoded-cs;
cdns,fifo-depth = <128>;
cdns,fifo-width = <4>;
cdns,trigger-address = <0x00000000>;
flash0: n25q00@0 {
...
cdns,read-delay = <4>;
cdns,tshsl-ns = <50>;
cdns,tsd2d-ns = <50>;
cdns,tchsh-ns = <4>;
cdns,tslch-ns = <4>;
};
};

Просмотреть файл

@ -39,7 +39,7 @@ Optional properties:
"prefetch-polled" Prefetch polled mode (default)
"polled" Polled mode, without prefetch
"prefetch-dma" Prefetch enabled sDMA mode
"prefetch-dma" Prefetch enabled DMA mode
"prefetch-irq" Prefetch enabled irq mode
- elm_id: <deprecated> use "ti,elm-id" instead

Просмотреть файл

@ -0,0 +1,24 @@
HiSilicon SPI-NOR Flash Controller
Required properties:
- compatible : Should be "hisilicon,fmc-spi-nor" and one of the following strings:
"hisilicon,hi3519-spi-nor"
- address-cells : Should be 1.
- size-cells : Should be 0.
- reg : Offset and length of the register set for the controller device.
- reg-names : Must include the following two entries: "control", "memory".
- clocks : handle to spi-nor flash controller clock.
Example:
spi-nor-controller@10000000 {
compatible = "hisilicon,hi3519-spi-nor", "hisilicon,fmc-spi-nor";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x10000000 0x1000>, <0x14000000 0x1000000>;
reg-names = "control", "memory";
clocks = <&clock HI3519_FMC_CLK>;
spi-nor@0 {
compatible = "jedec,spi-nor";
reg = <0>;
};
};

Просмотреть файл

@ -0,0 +1,160 @@
MTK SoCs NAND FLASH controller (NFC) DT binding
This file documents the device tree bindings for MTK SoCs NAND controllers.
The functional split of the controller requires two drivers to operate:
the nand controller interface driver and the ECC engine driver.
The hardware description for both devices must be captured as device
tree nodes.
1) NFC NAND Controller Interface (NFI):
=======================================
The first part of NFC is NAND Controller Interface (NFI) HW.
Required NFI properties:
- compatible: Should be "mediatek,mtxxxx-nfc".
- reg: Base physical address and size of NFI.
- interrupts: Interrupts of NFI.
- clocks: NFI required clocks.
- clock-names: NFI clocks internal name.
- status: Disabled default. Then set "okay" by platform.
- ecc-engine: Required ECC Engine node.
- #address-cells: NAND chip index, should be 1.
- #size-cells: Should be 0.
Example:
nandc: nfi@1100d000 {
compatible = "mediatek,mt2701-nfc";
reg = <0 0x1100d000 0 0x1000>;
interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_LOW>;
clocks = <&pericfg CLK_PERI_NFI>,
<&pericfg CLK_PERI_NFI_PAD>;
clock-names = "nfi_clk", "pad_clk";
status = "disabled";
ecc-engine = <&bch>;
#address-cells = <1>;
#size-cells = <0>;
};
Platform related properties, should be set in {platform_name}.dts:
- children nodes: NAND chips.
Children nodes properties:
- reg: Chip Select Signal, default 0.
Set as reg = <0>, <1> when need 2 CS.
Optional:
- nand-on-flash-bbt: Store BBT on NAND Flash.
- nand-ecc-mode: the NAND ecc mode (check driver for supported modes)
- nand-ecc-step-size: Number of data bytes covered by a single ECC step.
valid values: 512 and 1024.
1024 is recommended for large page NANDs.
- nand-ecc-strength: Number of bits to correct per ECC step.
The valid values that the controller supports are: 4, 6,
8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, 40, 44,
48, 52, 56, 60.
The strength should be calculated as follows:
E = (S - F) * 8 / 14
S = O / (P / Q)
E : nand-ecc-strength.
S : spare size per sector.
F : FDM size, should be in the range [1,8].
It is used to store free oob data.
O : oob size.
P : page size.
Q : nand-ecc-step-size.
If the result does not match any one of the listed
choices above, please select the smaller valid value from
the list.
(otherwise the driver will do the adjustment at runtime)
- pinctrl-names: Default NAND pin GPIO setting name.
- pinctrl-0: GPIO setting node.
Example:
&pio {
nand_pins_default: nanddefault {
pins_dat {
pinmux = <MT2701_PIN_111_MSDC0_DAT7__FUNC_NLD7>,
<MT2701_PIN_112_MSDC0_DAT6__FUNC_NLD6>,
<MT2701_PIN_114_MSDC0_DAT4__FUNC_NLD4>,
<MT2701_PIN_118_MSDC0_DAT3__FUNC_NLD3>,
<MT2701_PIN_121_MSDC0_DAT0__FUNC_NLD0>,
<MT2701_PIN_120_MSDC0_DAT1__FUNC_NLD1>,
<MT2701_PIN_113_MSDC0_DAT5__FUNC_NLD5>,
<MT2701_PIN_115_MSDC0_RSTB__FUNC_NLD8>,
<MT2701_PIN_119_MSDC0_DAT2__FUNC_NLD2>;
input-enable;
drive-strength = <MTK_DRIVE_8mA>;
bias-pull-up;
};
pins_we {
pinmux = <MT2701_PIN_117_MSDC0_CLK__FUNC_NWEB>;
drive-strength = <MTK_DRIVE_8mA>;
bias-pull-up = <MTK_PUPD_SET_R1R0_10>;
};
pins_ale {
pinmux = <MT2701_PIN_116_MSDC0_CMD__FUNC_NALE>;
drive-strength = <MTK_DRIVE_8mA>;
bias-pull-down = <MTK_PUPD_SET_R1R0_10>;
};
};
};
&nandc {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&nand_pins_default>;
nand@0 {
reg = <0>;
nand-on-flash-bbt;
nand-ecc-mode = "hw";
nand-ecc-strength = <24>;
nand-ecc-step-size = <1024>;
};
};
NAND chip optional subnodes:
- Partitions, see Documentation/devicetree/bindings/mtd/partition.txt
Example:
nand@0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
preloader@0 {
label = "pl";
read-only;
reg = <0x00000000 0x00400000>;
};
android@0x00400000 {
label = "android";
reg = <0x00400000 0x12c00000>;
};
};
};
2) ECC Engine:
==============
Required BCH properties:
- compatible: Should be "mediatek,mtxxxx-ecc".
- reg: Base physical address and size of ECC.
- interrupts: Interrupts of ECC.
- clocks: ECC required clocks.
- clock-names: ECC clocks internal name.
- status: Disabled default. Then set "okay" by platform.
Example:
bch: ecc@1100e000 {
compatible = "mediatek,mt2701-ecc";
reg = <0 0x1100e000 0 0x1000>;
interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_LOW>;
clocks = <&pericfg CLK_PERI_NFI_ECC>;
clock-names = "nfiecc_clk";
status = "disabled";
};

Просмотреть файл

@ -11,10 +11,16 @@ Required properties:
* "ahb" : AHB gating clock
* "mod" : nand controller clock
Optional properties:
- dmas : shall reference DMA channel associated to the NAND controller.
- dma-names : shall be "rxtx".
Optional children nodes:
Children nodes represent the available nand chips.
Optional properties:
- reset : phandle + reset specifier pair
- reset-names : must contain "ahb"
- allwinner,rb : shall contain the native Ready/Busy ids.
or
- rb-gpios : shall contain the gpios used as R/B pins.

Просмотреть файл

@ -397,7 +397,7 @@ static int __init init_axis_flash(void)
if (!romfs_in_flash) {
/* Create an RAM device for the root partition (romfs). */
#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0) || (CONFIG_MTDRAM_ABS_POS != 0)
#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0)
/* No use trying to boot this kernel from RAM. Panic! */
printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM "
"device due to kernel (mis)configuration!\n");

Просмотреть файл

@ -320,7 +320,7 @@ static int __init init_axis_flash(void)
* but its size must be configured as 0 so as not to conflict
* with our usage.
*/
#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0) || (CONFIG_MTDRAM_ABS_POS != 0)
#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0)
if (!romfs_in_flash && !nand_boot) {
printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM "
"device; configure CONFIG_MTD_MTDRAM with size = 0!\n");

Просмотреть файл

@ -115,7 +115,7 @@ config FSL_CORENET_CF
config FSL_IFC
bool
depends on FSL_SOC
depends on FSL_SOC || ARCH_LAYERSCAPE
config JZ4780_NEMC
bool "Ingenic JZ4780 SoC NEMC driver"

Просмотреть файл

@ -31,7 +31,9 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/fsl_ifc.h>
#include <asm/prom.h>
#include <linux/irqdomain.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
struct fsl_ifc_ctrl *fsl_ifc_ctrl_dev;
EXPORT_SYMBOL(fsl_ifc_ctrl_dev);

Просмотреть файл

@ -416,7 +416,7 @@ static int cfi_staa_read (struct mtd_info *mtd, loff_t from, size_t len, size_t
return ret;
}
static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
static int do_write_buffer(struct map_info *map, struct flchip *chip,
unsigned long adr, const u_char *buf, int len)
{
struct cfi_private *cfi = map->fldrv_priv;

Просмотреть файл

@ -113,12 +113,12 @@ config MTD_SST25L
if you want to specify device partitioning.
config MTD_BCM47XXSFLASH
tristate "R/O support for serial flash on BCMA bus"
tristate "Support for serial flash on BCMA bus"
depends on BCMA_SFLASH && (MIPS || ARM)
help
BCMA bus can have various flash memories attached, they are
registered by bcma as platform devices. This enables driver for
serial flash memories (only read-only mode is implemented).
serial flash memories.
config MTD_SLRAM
tristate "Uncached system RAM"
@ -171,18 +171,6 @@ config MTDRAM_ERASE_SIZE
as a module, it is also possible to specify this as a parameter when
loading the module.
#If not a module (I don't want to test it as a module)
config MTDRAM_ABS_POS
hex "SRAM Hexadecimal Absolute position or 0"
depends on MTD_MTDRAM=y
default "0"
help
If you have system RAM accessible by the CPU but not used by Linux
in normal operation, you can give the physical address at which the
available RAM starts, and the MTDRAM driver will use it instead of
allocating space from Linux's available memory. Otherwise, leave
this set to zero. Most people will want to leave this as zero.
config MTD_BLOCK2MTD
tristate "MTD using block device"
depends on BLOCK

Просмотреть файл

@ -73,14 +73,15 @@ static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
return spi_write(spi, flash->command, len + 1);
}
static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
const u_char *buf)
{
struct m25p *flash = nor->priv;
struct spi_device *spi = flash->spi;
struct spi_transfer t[2] = {};
struct spi_message m;
int cmd_sz = m25p_cmdsz(nor);
ssize_t ret;
spi_message_init(&m);
@ -98,9 +99,14 @@ static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
t[1].len = len;
spi_message_add_tail(&t[1], &m);
spi_sync(spi, &m);
ret = spi_sync(spi, &m);
if (ret)
return ret;
*retlen += m.actual_length - cmd_sz;
ret = m.actual_length - cmd_sz;
if (ret < 0)
return -EIO;
return ret;
}
static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
@ -119,21 +125,21 @@ static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
* Read an address range from the nor chip. The address range
* may be any size provided it is within the physical boundaries.
*/
static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
size_t *retlen, u_char *buf)
static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
u_char *buf)
{
struct m25p *flash = nor->priv;
struct spi_device *spi = flash->spi;
struct spi_transfer t[2];
struct spi_message m;
unsigned int dummy = nor->read_dummy;
ssize_t ret;
/* convert the dummy cycles to the number of bytes */
dummy /= 8;
if (spi_flash_read_supported(spi)) {
struct spi_flash_read_message msg;
int ret;
memset(&msg, 0, sizeof(msg));
@ -149,8 +155,9 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
msg.data_nbits = m25p80_rx_nbits(nor);
ret = spi_flash_read(spi, &msg);
*retlen = msg.retlen;
return ret;
if (ret < 0)
return ret;
return msg.retlen;
}
spi_message_init(&m);
@ -165,13 +172,17 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
t[1].rx_buf = buf;
t[1].rx_nbits = m25p80_rx_nbits(nor);
t[1].len = len;
t[1].len = min(len, spi_max_transfer_size(spi));
spi_message_add_tail(&t[1], &m);
spi_sync(spi, &m);
ret = spi_sync(spi, &m);
if (ret)
return ret;
*retlen = m.actual_length - m25p_cmdsz(nor) - dummy;
return 0;
ret = m.actual_length - m25p_cmdsz(nor) - dummy;
if (ret < 0)
return -EIO;
return ret;
}
/*

Просмотреть файл

@ -186,7 +186,7 @@ static int of_flash_probe(struct platform_device *dev)
* consists internally of 2 non-identical NOR chips on one die.
*/
p = of_get_property(dp, "reg", &count);
if (count % reg_tuple_size != 0) {
if (!p || count % reg_tuple_size != 0) {
dev_err(&dev->dev, "Malformed reg property on %s\n",
dev->dev.of_node->full_name);
err = -EINVAL;

Просмотреть файл

@ -75,15 +75,15 @@ static int __init init_msp_flash(void)
printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt);
msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL);
msp_flash = kcalloc(fcnt, sizeof(*msp_flash), GFP_KERNEL);
if (!msp_flash)
return -ENOMEM;
msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL);
msp_parts = kcalloc(fcnt, sizeof(*msp_parts), GFP_KERNEL);
if (!msp_parts)
goto free_msp_flash;
msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL);
msp_maps = kcalloc(fcnt, sizeof(*msp_maps), GFP_KERNEL);
if (!msp_maps)
goto free_msp_parts;

Просмотреть файл

@ -230,8 +230,10 @@ static struct sa_info *sa1100_setup_mtd(struct platform_device *pdev,
info->mtd = mtd_concat_create(cdev, info->num_subdev,
plat->name);
if (info->mtd == NULL)
if (info->mtd == NULL) {
ret = -ENXIO;
goto err;
}
}
info->mtd->dev.parent = &pdev->dev;

Просмотреть файл

@ -438,7 +438,7 @@ config MTD_NAND_FSL_ELBC
config MTD_NAND_FSL_IFC
tristate "NAND support for Freescale IFC controller"
depends on MTD_NAND && FSL_SOC
depends on MTD_NAND && (FSL_SOC || ARCH_LAYERSCAPE)
select FSL_IFC
select MEMORY
help
@ -539,7 +539,6 @@ config MTD_NAND_FSMC
config MTD_NAND_XWAY
tristate "Support for NAND on Lantiq XWAY SoC"
depends on LANTIQ && SOC_TYPE_XWAY
select MTD_NAND_PLATFORM
help
Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
to the External Bus Unit (EBU).
@ -563,4 +562,11 @@ config MTD_NAND_QCOM
Enables support for NAND flash chips on SoCs containing the EBI2 NAND
controller. This controller is found on IPQ806x SoC.
config MTD_NAND_MTK
tristate "Support for NAND controller on MTK SoCs"
depends on HAS_DMA
help
Enables support for NAND controller on MTK SoCs.
This controller is found on mt27xx, mt81xx, mt65xx SoCs.
endif # MTD_NAND

Просмотреть файл

@ -57,5 +57,6 @@ obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o
obj-$(CONFIG_MTD_NAND_MTK) += mtk_nand.o mtk_ecc.o
nand-objs := nand_base.o nand_bbt.o nand_timings.o

Просмотреть файл

@ -340,6 +340,36 @@ static const u16 brcmnand_regs_v71[] = {
[BRCMNAND_FC_BASE] = 0x400,
};
/* BRCMNAND v7.2 */
static const u16 brcmnand_regs_v72[] = {
[BRCMNAND_CMD_START] = 0x04,
[BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
[BRCMNAND_CMD_ADDRESS] = 0x0c,
[BRCMNAND_INTFC_STATUS] = 0x14,
[BRCMNAND_CS_SELECT] = 0x18,
[BRCMNAND_CS_XOR] = 0x1c,
[BRCMNAND_LL_OP] = 0x20,
[BRCMNAND_CS0_BASE] = 0x50,
[BRCMNAND_CS1_BASE] = 0,
[BRCMNAND_CORR_THRESHOLD] = 0xdc,
[BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0,
[BRCMNAND_UNCORR_COUNT] = 0xfc,
[BRCMNAND_CORR_COUNT] = 0x100,
[BRCMNAND_CORR_EXT_ADDR] = 0x10c,
[BRCMNAND_CORR_ADDR] = 0x110,
[BRCMNAND_UNCORR_EXT_ADDR] = 0x114,
[BRCMNAND_UNCORR_ADDR] = 0x118,
[BRCMNAND_SEMAPHORE] = 0x150,
[BRCMNAND_ID] = 0x194,
[BRCMNAND_ID_EXT] = 0x198,
[BRCMNAND_LL_RDATA] = 0x19c,
[BRCMNAND_OOB_READ_BASE] = 0x200,
[BRCMNAND_OOB_READ_10_BASE] = 0,
[BRCMNAND_OOB_WRITE_BASE] = 0x400,
[BRCMNAND_OOB_WRITE_10_BASE] = 0,
[BRCMNAND_FC_BASE] = 0x600,
};
enum brcmnand_cs_reg {
BRCMNAND_CS_CFG_EXT = 0,
BRCMNAND_CS_CFG,
@ -435,7 +465,9 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
}
/* Register offsets */
if (ctrl->nand_version >= 0x0701)
if (ctrl->nand_version >= 0x0702)
ctrl->reg_offsets = brcmnand_regs_v72;
else if (ctrl->nand_version >= 0x0701)
ctrl->reg_offsets = brcmnand_regs_v71;
else if (ctrl->nand_version >= 0x0600)
ctrl->reg_offsets = brcmnand_regs_v60;
@ -480,7 +512,9 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
}
/* Maximum spare area sector size (per 512B) */
if (ctrl->nand_version >= 0x0600)
if (ctrl->nand_version >= 0x0702)
ctrl->max_oob = 128;
else if (ctrl->nand_version >= 0x0600)
ctrl->max_oob = 64;
else if (ctrl->nand_version >= 0x0500)
ctrl->max_oob = 32;
@ -583,14 +617,20 @@ static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val)
enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD;
int cs = host->cs;
if (ctrl->nand_version >= 0x0600)
if (ctrl->nand_version >= 0x0702)
bits = 7;
else if (ctrl->nand_version >= 0x0600)
bits = 6;
else if (ctrl->nand_version >= 0x0500)
bits = 5;
else
bits = 4;
if (ctrl->nand_version >= 0x0600) {
if (ctrl->nand_version >= 0x0702) {
if (cs >= 4)
reg = BRCMNAND_CORR_THRESHOLD_EXT;
shift = (cs % 4) * bits;
} else if (ctrl->nand_version >= 0x0600) {
if (cs >= 5)
reg = BRCMNAND_CORR_THRESHOLD_EXT;
shift = (cs % 5) * bits;
@ -631,19 +671,28 @@ enum {
static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl)
{
if (ctrl->nand_version >= 0x0600)
if (ctrl->nand_version >= 0x0702)
return GENMASK(7, 0);
else if (ctrl->nand_version >= 0x0600)
return GENMASK(6, 0);
else
return GENMASK(5, 0);
}
#define NAND_ACC_CONTROL_ECC_SHIFT 16
#define NAND_ACC_CONTROL_ECC_EXT_SHIFT 13
static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl)
{
u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f;
return mask << NAND_ACC_CONTROL_ECC_SHIFT;
mask <<= NAND_ACC_CONTROL_ECC_SHIFT;
/* v7.2 includes additional ECC levels */
if (ctrl->nand_version >= 0x0702)
mask |= 0x7 << NAND_ACC_CONTROL_ECC_EXT_SHIFT;
return mask;
}
static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en)
@ -667,7 +716,9 @@ static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en)
static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl)
{
if (ctrl->nand_version >= 0x0600)
if (ctrl->nand_version >= 0x0702)
return 9;
else if (ctrl->nand_version >= 0x0600)
return 7;
else if (ctrl->nand_version >= 0x0500)
return 6;
@ -773,10 +824,16 @@ enum brcmnand_llop_type {
* Internal support functions
***********************************************************************/
static inline bool is_hamming_ecc(struct brcmnand_cfg *cfg)
static inline bool is_hamming_ecc(struct brcmnand_controller *ctrl,
struct brcmnand_cfg *cfg)
{
return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 &&
cfg->ecc_level == 15;
if (ctrl->nand_version <= 0x0701)
return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 &&
cfg->ecc_level == 15;
else
return cfg->sector_size_1k == 0 && ((cfg->spare_area_size == 16 &&
cfg->ecc_level == 15) ||
(cfg->spare_area_size == 28 && cfg->ecc_level == 16));
}
/*
@ -931,7 +988,7 @@ static int brcmstb_choose_ecc_layout(struct brcmnand_host *host)
if (p->sector_size_1k)
ecc_level <<= 1;
if (is_hamming_ecc(p)) {
if (is_hamming_ecc(host->ctrl, p)) {
ecc->bytes = 3 * sectors;
mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops);
return 0;
@ -1108,7 +1165,7 @@ static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd)
ctrl->cmd_pending = cmd;
intfc = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS);
BUG_ON(!(intfc & INTFC_CTLR_READY));
WARN_ON(!(intfc & INTFC_CTLR_READY));
mb(); /* flush previous writes */
brcmnand_write_reg(ctrl, BRCMNAND_CMD_START,
@ -1545,6 +1602,56 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
return ret;
}
/*
* Check a page to see if it is erased (w/ bitflips) after an uncorrectable ECC
* error
*
* Because the HW ECC signals an ECC error if an erase paged has even a single
* bitflip, we must check each ECC error to see if it is actually an erased
* page with bitflips, not a truly corrupted page.
*
* On a real error, return a negative error code (-EBADMSG for ECC error), and
* buf will contain raw data.
* Otherwise, buf gets filled with 0xffs and return the maximum number of
* bitflips-per-ECC-sector to the caller.
*
*/
static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd,
struct nand_chip *chip, void *buf, u64 addr)
{
int i, sas;
void *oob = chip->oob_poi;
int bitflips = 0;
int page = addr >> chip->page_shift;
int ret;
if (!buf) {
buf = chip->buffers->databuf;
/* Invalidate page cache */
chip->pagebuf = -1;
}
sas = mtd->oobsize / chip->ecc.steps;
/* read without ecc for verification */
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
ret = chip->ecc.read_page_raw(mtd, chip, buf, true, page);
if (ret)
return ret;
for (i = 0; i < chip->ecc.steps; i++, oob += sas) {
ret = nand_check_erased_ecc_chunk(buf, chip->ecc.size,
oob, sas, NULL, 0,
chip->ecc.strength);
if (ret < 0)
return ret;
bitflips = max(bitflips, ret);
}
return bitflips;
}
static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
u64 addr, unsigned int trans, u32 *buf, u8 *oob)
{
@ -1552,9 +1659,11 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
struct brcmnand_controller *ctrl = host->ctrl;
u64 err_addr = 0;
int err;
bool retry = true;
dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf);
try_dmaread:
brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_COUNT, 0);
if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) {
@ -1575,6 +1684,34 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
}
if (mtd_is_eccerr(err)) {
/*
* On controller version and 7.0, 7.1 , DMA read after a
* prior PIO read that reported uncorrectable error,
* the DMA engine captures this error following DMA read
* cleared only on subsequent DMA read, so just retry once
* to clear a possible false error reported for current DMA
* read
*/
if ((ctrl->nand_version == 0x0700) ||
(ctrl->nand_version == 0x0701)) {
if (retry) {
retry = false;
goto try_dmaread;
}
}
/*
* Controller version 7.2 has hw encoder to detect erased page
* bitflips, apply sw verification for older controllers only
*/
if (ctrl->nand_version < 0x0702) {
err = brcmstb_nand_verify_erased_page(mtd, chip, buf,
addr);
/* erased page bitflips corrected */
if (err > 0)
return err;
}
dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n",
(unsigned long long)err_addr);
mtd->ecc_stats.failed++;
@ -1857,7 +1994,8 @@ static int brcmnand_set_cfg(struct brcmnand_host *host,
return 0;
}
static void brcmnand_print_cfg(char *buf, struct brcmnand_cfg *cfg)
static void brcmnand_print_cfg(struct brcmnand_host *host,
char *buf, struct brcmnand_cfg *cfg)
{
buf += sprintf(buf,
"%lluMiB total, %uKiB blocks, %u%s pages, %uB OOB, %u-bit",
@ -1868,7 +2006,7 @@ static void brcmnand_print_cfg(char *buf, struct brcmnand_cfg *cfg)
cfg->spare_area_size, cfg->device_width);
/* Account for Hamming ECC and for BCH 512B vs 1KiB sectors */
if (is_hamming_ecc(cfg))
if (is_hamming_ecc(host->ctrl, cfg))
sprintf(buf, ", Hamming ECC");
else if (cfg->sector_size_1k)
sprintf(buf, ", BCH-%u (1KiB sector)", cfg->ecc_level << 1);
@ -1987,7 +2125,7 @@ static int brcmnand_setup_dev(struct brcmnand_host *host)
brcmnand_set_ecc_enabled(host, 1);
brcmnand_print_cfg(msg, cfg);
brcmnand_print_cfg(host, msg, cfg);
dev_info(ctrl->dev, "detected %s\n", msg);
/* Configure ACC_CONTROL */
@ -1995,6 +2133,10 @@ static int brcmnand_setup_dev(struct brcmnand_host *host)
tmp = nand_readreg(ctrl, offs);
tmp &= ~ACC_CONTROL_PARTIAL_PAGE;
tmp &= ~ACC_CONTROL_RD_ERASED;
/* We need to turn on Read from erased paged protected by ECC */
if (ctrl->nand_version >= 0x0702)
tmp |= ACC_CONTROL_RD_ERASED;
tmp &= ~ACC_CONTROL_FAST_PGM_RDIN;
if (ctrl->features & BRCMNAND_HAS_PREFETCH) {
/*
@ -2195,6 +2337,7 @@ static const struct of_device_id brcmnand_of_match[] = {
{ .compatible = "brcm,brcmnand-v6.2" },
{ .compatible = "brcm,brcmnand-v7.0" },
{ .compatible = "brcm,brcmnand-v7.1" },
{ .compatible = "brcm,brcmnand-v7.2" },
{},
};
MODULE_DEVICE_TABLE(of, brcmnand_of_match);

Просмотреть файл

@ -375,6 +375,6 @@ static struct platform_driver jz4780_bch_driver = {
module_platform_driver(jz4780_bch_driver);
MODULE_AUTHOR("Alex Smith <alex@alex-smith.me.uk>");
MODULE_AUTHOR("Harvey Hunt <harvey.hunt@imgtec.com>");
MODULE_AUTHOR("Harvey Hunt <harveyhuntnexus@gmail.com>");
MODULE_DESCRIPTION("Ingenic JZ4780 BCH error correction driver");
MODULE_LICENSE("GPL v2");

Просмотреть файл

@ -412,6 +412,6 @@ static struct platform_driver jz4780_nand_driver = {
module_platform_driver(jz4780_nand_driver);
MODULE_AUTHOR("Alex Smith <alex@alex-smith.me.uk>");
MODULE_AUTHOR("Harvey Hunt <harvey.hunt@imgtec.com>");
MODULE_AUTHOR("Harvey Hunt <harveyhuntnexus@gmail.com>");
MODULE_DESCRIPTION("Ingenic JZ4780 NAND driver");
MODULE_LICENSE("GPL v2");

530
drivers/mtd/nand/mtk_ecc.c Normal file
Просмотреть файл

@ -0,0 +1,530 @@
/*
* MTK ECC controller driver.
* Copyright (C) 2016 MediaTek Inc.
* Authors: Xiaolei Li <xiaolei.li@mediatek.com>
* Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/iopoll.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/mutex.h>
#include "mtk_ecc.h"
#define ECC_IDLE_MASK BIT(0)
#define ECC_IRQ_EN BIT(0)
#define ECC_OP_ENABLE (1)
#define ECC_OP_DISABLE (0)
#define ECC_ENCCON (0x00)
#define ECC_ENCCNFG (0x04)
#define ECC_CNFG_4BIT (0)
#define ECC_CNFG_6BIT (1)
#define ECC_CNFG_8BIT (2)
#define ECC_CNFG_10BIT (3)
#define ECC_CNFG_12BIT (4)
#define ECC_CNFG_14BIT (5)
#define ECC_CNFG_16BIT (6)
#define ECC_CNFG_18BIT (7)
#define ECC_CNFG_20BIT (8)
#define ECC_CNFG_22BIT (9)
#define ECC_CNFG_24BIT (0xa)
#define ECC_CNFG_28BIT (0xb)
#define ECC_CNFG_32BIT (0xc)
#define ECC_CNFG_36BIT (0xd)
#define ECC_CNFG_40BIT (0xe)
#define ECC_CNFG_44BIT (0xf)
#define ECC_CNFG_48BIT (0x10)
#define ECC_CNFG_52BIT (0x11)
#define ECC_CNFG_56BIT (0x12)
#define ECC_CNFG_60BIT (0x13)
#define ECC_MODE_SHIFT (5)
#define ECC_MS_SHIFT (16)
#define ECC_ENCDIADDR (0x08)
#define ECC_ENCIDLE (0x0C)
#define ECC_ENCPAR(x) (0x10 + (x) * sizeof(u32))
#define ECC_ENCIRQ_EN (0x80)
#define ECC_ENCIRQ_STA (0x84)
#define ECC_DECCON (0x100)
#define ECC_DECCNFG (0x104)
#define DEC_EMPTY_EN BIT(31)
#define DEC_CNFG_CORRECT (0x3 << 12)
#define ECC_DECIDLE (0x10C)
#define ECC_DECENUM0 (0x114)
#define ERR_MASK (0x3f)
#define ECC_DECDONE (0x124)
#define ECC_DECIRQ_EN (0x200)
#define ECC_DECIRQ_STA (0x204)
#define ECC_TIMEOUT (500000)
#define ECC_IDLE_REG(op) ((op) == ECC_ENCODE ? ECC_ENCIDLE : ECC_DECIDLE)
#define ECC_CTL_REG(op) ((op) == ECC_ENCODE ? ECC_ENCCON : ECC_DECCON)
#define ECC_IRQ_REG(op) ((op) == ECC_ENCODE ? \
ECC_ENCIRQ_EN : ECC_DECIRQ_EN)
struct mtk_ecc {
struct device *dev;
void __iomem *regs;
struct clk *clk;
struct completion done;
struct mutex lock;
u32 sectors;
};
static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc,
enum mtk_ecc_operation op)
{
struct device *dev = ecc->dev;
u32 val;
int ret;
ret = readl_poll_timeout_atomic(ecc->regs + ECC_IDLE_REG(op), val,
val & ECC_IDLE_MASK,
10, ECC_TIMEOUT);
if (ret)
dev_warn(dev, "%s NOT idle\n",
op == ECC_ENCODE ? "encoder" : "decoder");
}
static irqreturn_t mtk_ecc_irq(int irq, void *id)
{
struct mtk_ecc *ecc = id;
enum mtk_ecc_operation op;
u32 dec, enc;
dec = readw(ecc->regs + ECC_DECIRQ_STA) & ECC_IRQ_EN;
if (dec) {
op = ECC_DECODE;
dec = readw(ecc->regs + ECC_DECDONE);
if (dec & ecc->sectors) {
ecc->sectors = 0;
complete(&ecc->done);
} else {
return IRQ_HANDLED;
}
} else {
enc = readl(ecc->regs + ECC_ENCIRQ_STA) & ECC_IRQ_EN;
if (enc) {
op = ECC_ENCODE;
complete(&ecc->done);
} else {
return IRQ_NONE;
}
}
writel(0, ecc->regs + ECC_IRQ_REG(op));
return IRQ_HANDLED;
}
static void mtk_ecc_config(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
{
u32 ecc_bit = ECC_CNFG_4BIT, dec_sz, enc_sz;
u32 reg;
switch (config->strength) {
case 4:
ecc_bit = ECC_CNFG_4BIT;
break;
case 6:
ecc_bit = ECC_CNFG_6BIT;
break;
case 8:
ecc_bit = ECC_CNFG_8BIT;
break;
case 10:
ecc_bit = ECC_CNFG_10BIT;
break;
case 12:
ecc_bit = ECC_CNFG_12BIT;
break;
case 14:
ecc_bit = ECC_CNFG_14BIT;
break;
case 16:
ecc_bit = ECC_CNFG_16BIT;
break;
case 18:
ecc_bit = ECC_CNFG_18BIT;
break;
case 20:
ecc_bit = ECC_CNFG_20BIT;
break;
case 22:
ecc_bit = ECC_CNFG_22BIT;
break;
case 24:
ecc_bit = ECC_CNFG_24BIT;
break;
case 28:
ecc_bit = ECC_CNFG_28BIT;
break;
case 32:
ecc_bit = ECC_CNFG_32BIT;
break;
case 36:
ecc_bit = ECC_CNFG_36BIT;
break;
case 40:
ecc_bit = ECC_CNFG_40BIT;
break;
case 44:
ecc_bit = ECC_CNFG_44BIT;
break;
case 48:
ecc_bit = ECC_CNFG_48BIT;
break;
case 52:
ecc_bit = ECC_CNFG_52BIT;
break;
case 56:
ecc_bit = ECC_CNFG_56BIT;
break;
case 60:
ecc_bit = ECC_CNFG_60BIT;
break;
default:
dev_err(ecc->dev, "invalid strength %d, default to 4 bits\n",
config->strength);
}
if (config->op == ECC_ENCODE) {
/* configure ECC encoder (in bits) */
enc_sz = config->len << 3;
reg = ecc_bit | (config->mode << ECC_MODE_SHIFT);
reg |= (enc_sz << ECC_MS_SHIFT);
writel(reg, ecc->regs + ECC_ENCCNFG);
if (config->mode != ECC_NFI_MODE)
writel(lower_32_bits(config->addr),
ecc->regs + ECC_ENCDIADDR);
} else {
/* configure ECC decoder (in bits) */
dec_sz = (config->len << 3) +
config->strength * ECC_PARITY_BITS;
reg = ecc_bit | (config->mode << ECC_MODE_SHIFT);
reg |= (dec_sz << ECC_MS_SHIFT) | DEC_CNFG_CORRECT;
reg |= DEC_EMPTY_EN;
writel(reg, ecc->regs + ECC_DECCNFG);
if (config->sectors)
ecc->sectors = 1 << (config->sectors - 1);
}
}
void mtk_ecc_get_stats(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats,
int sectors)
{
u32 offset, i, err;
u32 bitflips = 0;
stats->corrected = 0;
stats->failed = 0;
for (i = 0; i < sectors; i++) {
offset = (i >> 2) << 2;
err = readl(ecc->regs + ECC_DECENUM0 + offset);
err = err >> ((i % 4) * 8);
err &= ERR_MASK;
if (err == ERR_MASK) {
/* uncorrectable errors */
stats->failed++;
continue;
}
stats->corrected += err;
bitflips = max_t(u32, bitflips, err);
}
stats->bitflips = bitflips;
}
EXPORT_SYMBOL(mtk_ecc_get_stats);
void mtk_ecc_release(struct mtk_ecc *ecc)
{
clk_disable_unprepare(ecc->clk);
put_device(ecc->dev);
}
EXPORT_SYMBOL(mtk_ecc_release);
static void mtk_ecc_hw_init(struct mtk_ecc *ecc)
{
mtk_ecc_wait_idle(ecc, ECC_ENCODE);
writew(ECC_OP_DISABLE, ecc->regs + ECC_ENCCON);
mtk_ecc_wait_idle(ecc, ECC_DECODE);
writel(ECC_OP_DISABLE, ecc->regs + ECC_DECCON);
}
static struct mtk_ecc *mtk_ecc_get(struct device_node *np)
{
struct platform_device *pdev;
struct mtk_ecc *ecc;
pdev = of_find_device_by_node(np);
if (!pdev || !platform_get_drvdata(pdev))
return ERR_PTR(-EPROBE_DEFER);
get_device(&pdev->dev);
ecc = platform_get_drvdata(pdev);
clk_prepare_enable(ecc->clk);
mtk_ecc_hw_init(ecc);
return ecc;
}
struct mtk_ecc *of_mtk_ecc_get(struct device_node *of_node)
{
struct mtk_ecc *ecc = NULL;
struct device_node *np;
np = of_parse_phandle(of_node, "ecc-engine", 0);
if (np) {
ecc = mtk_ecc_get(np);
of_node_put(np);
}
return ecc;
}
EXPORT_SYMBOL(of_mtk_ecc_get);
int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
{
enum mtk_ecc_operation op = config->op;
int ret;
ret = mutex_lock_interruptible(&ecc->lock);
if (ret) {
dev_err(ecc->dev, "interrupted when attempting to lock\n");
return ret;
}
mtk_ecc_wait_idle(ecc, op);
mtk_ecc_config(ecc, config);
writew(ECC_OP_ENABLE, ecc->regs + ECC_CTL_REG(op));
init_completion(&ecc->done);
writew(ECC_IRQ_EN, ecc->regs + ECC_IRQ_REG(op));
return 0;
}
EXPORT_SYMBOL(mtk_ecc_enable);
void mtk_ecc_disable(struct mtk_ecc *ecc)
{
enum mtk_ecc_operation op = ECC_ENCODE;
/* find out the running operation */
if (readw(ecc->regs + ECC_CTL_REG(op)) != ECC_OP_ENABLE)
op = ECC_DECODE;
/* disable it */
mtk_ecc_wait_idle(ecc, op);
writew(0, ecc->regs + ECC_IRQ_REG(op));
writew(ECC_OP_DISABLE, ecc->regs + ECC_CTL_REG(op));
mutex_unlock(&ecc->lock);
}
EXPORT_SYMBOL(mtk_ecc_disable);
int mtk_ecc_wait_done(struct mtk_ecc *ecc, enum mtk_ecc_operation op)
{
int ret;
ret = wait_for_completion_timeout(&ecc->done, msecs_to_jiffies(500));
if (!ret) {
dev_err(ecc->dev, "%s timeout - interrupt did not arrive)\n",
(op == ECC_ENCODE) ? "encoder" : "decoder");
return -ETIMEDOUT;
}
return 0;
}
EXPORT_SYMBOL(mtk_ecc_wait_done);
int mtk_ecc_encode(struct mtk_ecc *ecc, struct mtk_ecc_config *config,
u8 *data, u32 bytes)
{
dma_addr_t addr;
u32 *p, len, i;
int ret = 0;
addr = dma_map_single(ecc->dev, data, bytes, DMA_TO_DEVICE);
ret = dma_mapping_error(ecc->dev, addr);
if (ret) {
dev_err(ecc->dev, "dma mapping error\n");
return -EINVAL;
}
config->op = ECC_ENCODE;
config->addr = addr;
ret = mtk_ecc_enable(ecc, config);
if (ret) {
dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE);
return ret;
}
ret = mtk_ecc_wait_done(ecc, ECC_ENCODE);
if (ret)
goto timeout;
mtk_ecc_wait_idle(ecc, ECC_ENCODE);
/* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */
len = (config->strength * ECC_PARITY_BITS + 7) >> 3;
p = (u32 *)(data + bytes);
/* write the parity bytes generated by the ECC back to the OOB region */
for (i = 0; i < len; i++)
p[i] = readl(ecc->regs + ECC_ENCPAR(i));
timeout:
dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE);
mtk_ecc_disable(ecc);
return ret;
}
EXPORT_SYMBOL(mtk_ecc_encode);
void mtk_ecc_adjust_strength(u32 *p)
{
u32 ecc[] = {4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36,
40, 44, 48, 52, 56, 60};
int i;
for (i = 0; i < ARRAY_SIZE(ecc); i++) {
if (*p <= ecc[i]) {
if (!i)
*p = ecc[i];
else if (*p != ecc[i])
*p = ecc[i - 1];
return;
}
}
*p = ecc[ARRAY_SIZE(ecc) - 1];
}
EXPORT_SYMBOL(mtk_ecc_adjust_strength);
static int mtk_ecc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_ecc *ecc;
struct resource *res;
int irq, ret;
ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL);
if (!ecc)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ecc->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(ecc->regs)) {
dev_err(dev, "failed to map regs: %ld\n", PTR_ERR(ecc->regs));
return PTR_ERR(ecc->regs);
}
ecc->clk = devm_clk_get(dev, NULL);
if (IS_ERR(ecc->clk)) {
dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(ecc->clk));
return PTR_ERR(ecc->clk);
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "failed to get irq\n");
return -EINVAL;
}
ret = dma_set_mask(dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(dev, "failed to set DMA mask\n");
return ret;
}
ret = devm_request_irq(dev, irq, mtk_ecc_irq, 0x0, "mtk-ecc", ecc);
if (ret) {
dev_err(dev, "failed to request irq\n");
return -EINVAL;
}
ecc->dev = dev;
mutex_init(&ecc->lock);
platform_set_drvdata(pdev, ecc);
dev_info(dev, "probed\n");
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int mtk_ecc_suspend(struct device *dev)
{
struct mtk_ecc *ecc = dev_get_drvdata(dev);
clk_disable_unprepare(ecc->clk);
return 0;
}
static int mtk_ecc_resume(struct device *dev)
{
struct mtk_ecc *ecc = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(ecc->clk);
if (ret) {
dev_err(dev, "failed to enable clk\n");
return ret;
}
mtk_ecc_hw_init(ecc);
return 0;
}
static SIMPLE_DEV_PM_OPS(mtk_ecc_pm_ops, mtk_ecc_suspend, mtk_ecc_resume);
#endif
static const struct of_device_id mtk_ecc_dt_match[] = {
{ .compatible = "mediatek,mt2701-ecc" },
{},
};
MODULE_DEVICE_TABLE(of, mtk_ecc_dt_match);
static struct platform_driver mtk_ecc_driver = {
.probe = mtk_ecc_probe,
.driver = {
.name = "mtk-ecc",
.of_match_table = of_match_ptr(mtk_ecc_dt_match),
#ifdef CONFIG_PM_SLEEP
.pm = &mtk_ecc_pm_ops,
#endif
},
};
module_platform_driver(mtk_ecc_driver);
MODULE_AUTHOR("Xiaolei Li <xiaolei.li@mediatek.com>");
MODULE_DESCRIPTION("MTK Nand ECC Driver");
MODULE_LICENSE("GPL");

Просмотреть файл

@ -0,0 +1,50 @@
/*
* MTK SDG1 ECC controller
*
* Copyright (c) 2016 Mediatek
* Authors: Xiaolei Li <xiaolei.li@mediatek.com>
* Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#ifndef __DRIVERS_MTD_NAND_MTK_ECC_H__
#define __DRIVERS_MTD_NAND_MTK_ECC_H__
#include <linux/types.h>
#define ECC_PARITY_BITS (14)
enum mtk_ecc_mode {ECC_DMA_MODE = 0, ECC_NFI_MODE = 1};
enum mtk_ecc_operation {ECC_ENCODE, ECC_DECODE};
struct device_node;
struct mtk_ecc;
struct mtk_ecc_stats {
u32 corrected;
u32 bitflips;
u32 failed;
};
struct mtk_ecc_config {
enum mtk_ecc_operation op;
enum mtk_ecc_mode mode;
dma_addr_t addr;
u32 strength;
u32 sectors;
u32 len;
};
int mtk_ecc_encode(struct mtk_ecc *, struct mtk_ecc_config *, u8 *, u32);
void mtk_ecc_get_stats(struct mtk_ecc *, struct mtk_ecc_stats *, int);
int mtk_ecc_wait_done(struct mtk_ecc *, enum mtk_ecc_operation);
int mtk_ecc_enable(struct mtk_ecc *, struct mtk_ecc_config *);
void mtk_ecc_disable(struct mtk_ecc *);
void mtk_ecc_adjust_strength(u32 *);
struct mtk_ecc *of_mtk_ecc_get(struct device_node *);
void mtk_ecc_release(struct mtk_ecc *);
#endif

1526
drivers/mtd/nand/mtk_nand.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -2610,7 +2610,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
int cached = writelen > bytes && page != blockmask;
uint8_t *wbuf = buf;
int use_bufpoi;
int part_pagewr = (column || writelen < (mtd->writesize - 1));
int part_pagewr = (column || writelen < mtd->writesize);
if (part_pagewr)
use_bufpoi = 1;

Просмотреть файл

@ -168,6 +168,7 @@ struct nand_flash_dev nand_flash_ids[] = {
/* Manufacturer IDs */
struct nand_manufacturers nand_manuf_ids[] = {
{NAND_MFR_TOSHIBA, "Toshiba"},
{NAND_MFR_ESMT, "ESMT"},
{NAND_MFR_SAMSUNG, "Samsung"},
{NAND_MFR_FUJITSU, "Fujitsu"},
{NAND_MFR_NATIONAL, "National"},

Просмотреть файл

@ -118,8 +118,6 @@
#define PREFETCH_STATUS_FIFO_CNT(val) ((val >> 24) & 0x7F)
#define STATUS_BUFF_EMPTY 0x00000001
#define OMAP24XX_DMA_GPMC 4
#define SECTOR_BYTES 512
/* 4 bit padding to make byte aligned, 56 = 52 + 4 */
#define BCH4_BIT_PAD 4
@ -1811,7 +1809,6 @@ static int omap_nand_probe(struct platform_device *pdev)
struct nand_chip *nand_chip;
int err;
dma_cap_mask_t mask;
unsigned sig;
struct resource *res;
struct device *dev = &pdev->dev;
int min_oobbytes = BADBLOCK_MARKER_LENGTH;
@ -1924,11 +1921,11 @@ static int omap_nand_probe(struct platform_device *pdev)
case NAND_OMAP_PREFETCH_DMA:
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
sig = OMAP24XX_DMA_GPMC;
info->dma = dma_request_channel(mask, omap_dma_filter_fn, &sig);
if (!info->dma) {
info->dma = dma_request_chan(pdev->dev.parent, "rxtx");
if (IS_ERR(info->dma)) {
dev_err(&pdev->dev, "DMA engine request failed\n");
err = -ENXIO;
err = PTR_ERR(info->dma);
goto return_error;
} else {
struct dma_slave_config cfg;

Просмотреть файл

@ -39,6 +39,7 @@
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/reset.h>
#define NFC_REG_CTL 0x0000
#define NFC_REG_ST 0x0004
@ -153,6 +154,7 @@
/* define bit use in NFC_ECC_ST */
#define NFC_ECC_ERR(x) BIT(x)
#define NFC_ECC_ERR_MSK GENMASK(15, 0)
#define NFC_ECC_PAT_FOUND(x) BIT(x + 16)
#define NFC_ECC_ERR_CNT(b, x) (((x) >> (((b) % 4) * 8)) & 0xff)
@ -269,10 +271,12 @@ struct sunxi_nfc {
void __iomem *regs;
struct clk *ahb_clk;
struct clk *mod_clk;
struct reset_control *reset;
unsigned long assigned_cs;
unsigned long clk_rate;
struct list_head chips;
struct completion complete;
struct dma_chan *dmac;
};
static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl)
@ -365,6 +369,67 @@ static int sunxi_nfc_rst(struct sunxi_nfc *nfc)
return ret;
}
static int sunxi_nfc_dma_op_prepare(struct mtd_info *mtd, const void *buf,
int chunksize, int nchunks,
enum dma_data_direction ddir,
struct scatterlist *sg)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct dma_async_tx_descriptor *dmad;
enum dma_transfer_direction tdir;
dma_cookie_t dmat;
int ret;
if (ddir == DMA_FROM_DEVICE)
tdir = DMA_DEV_TO_MEM;
else
tdir = DMA_MEM_TO_DEV;
sg_init_one(sg, buf, nchunks * chunksize);
ret = dma_map_sg(nfc->dev, sg, 1, ddir);
if (!ret)
return -ENOMEM;
dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK);
if (!dmad) {
ret = -EINVAL;
goto err_unmap_buf;
}
writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD,
nfc->regs + NFC_REG_CTL);
writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM);
writel(chunksize, nfc->regs + NFC_REG_CNT);
dmat = dmaengine_submit(dmad);
ret = dma_submit_error(dmat);
if (ret)
goto err_clr_dma_flag;
return 0;
err_clr_dma_flag:
writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
nfc->regs + NFC_REG_CTL);
err_unmap_buf:
dma_unmap_sg(nfc->dev, sg, 1, ddir);
return ret;
}
static void sunxi_nfc_dma_op_cleanup(struct mtd_info *mtd,
enum dma_data_direction ddir,
struct scatterlist *sg)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
dma_unmap_sg(nfc->dev, sg, 1, ddir);
writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
nfc->regs + NFC_REG_CTL);
}
static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
{
struct nand_chip *nand = mtd_to_nand(mtd);
@ -822,17 +887,15 @@ static void sunxi_nfc_hw_ecc_update_stats(struct mtd_info *mtd,
}
static int sunxi_nfc_hw_ecc_correct(struct mtd_info *mtd, u8 *data, u8 *oob,
int step, bool *erased)
int step, u32 status, bool *erased)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct nand_ecc_ctrl *ecc = &nand->ecc;
u32 status, tmp;
u32 tmp;
*erased = false;
status = readl(nfc->regs + NFC_REG_ECC_ST);
if (status & NFC_ECC_ERR(step))
return -EBADMSG;
@ -898,6 +961,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
*cur_off = oob_off + ecc->bytes + 4;
ret = sunxi_nfc_hw_ecc_correct(mtd, data, oob_required ? oob : NULL, 0,
readl(nfc->regs + NFC_REG_ECC_ST),
&erased);
if (erased)
return 1;
@ -967,6 +1031,130 @@ static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd,
*cur_off = mtd->oobsize + mtd->writesize;
}
static int sunxi_nfc_hw_ecc_read_chunks_dma(struct mtd_info *mtd, uint8_t *buf,
int oob_required, int page,
int nchunks)
{
struct nand_chip *nand = mtd_to_nand(mtd);
bool randomized = nand->options & NAND_NEED_SCRAMBLING;
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct nand_ecc_ctrl *ecc = &nand->ecc;
unsigned int max_bitflips = 0;
int ret, i, raw_mode = 0;
struct scatterlist sg;
u32 status;
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
if (ret)
return ret;
ret = sunxi_nfc_dma_op_prepare(mtd, buf, ecc->size, nchunks,
DMA_FROM_DEVICE, &sg);
if (ret)
return ret;
sunxi_nfc_hw_ecc_enable(mtd);
sunxi_nfc_randomizer_config(mtd, page, false);
sunxi_nfc_randomizer_enable(mtd);
writel((NAND_CMD_RNDOUTSTART << 16) | (NAND_CMD_RNDOUT << 8) |
NAND_CMD_READSTART, nfc->regs + NFC_REG_RCMD_SET);
dma_async_issue_pending(nfc->dmac);
writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS,
nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
if (ret)
dmaengine_terminate_all(nfc->dmac);
sunxi_nfc_randomizer_disable(mtd);
sunxi_nfc_hw_ecc_disable(mtd);
sunxi_nfc_dma_op_cleanup(mtd, DMA_FROM_DEVICE, &sg);
if (ret)
return ret;
status = readl(nfc->regs + NFC_REG_ECC_ST);
for (i = 0; i < nchunks; i++) {
int data_off = i * ecc->size;
int oob_off = i * (ecc->bytes + 4);
u8 *data = buf + data_off;
u8 *oob = nand->oob_poi + oob_off;
bool erased;
ret = sunxi_nfc_hw_ecc_correct(mtd, randomized ? data : NULL,
oob_required ? oob : NULL,
i, status, &erased);
/* ECC errors are handled in the second loop. */
if (ret < 0)
continue;
if (oob_required && !erased) {
/* TODO: use DMA to retrieve OOB */
nand->cmdfunc(mtd, NAND_CMD_RNDOUT,
mtd->writesize + oob_off, -1);
nand->read_buf(mtd, oob, ecc->bytes + 4);
sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, i,
!i, page);
}
if (erased)
raw_mode = 1;
sunxi_nfc_hw_ecc_update_stats(mtd, &max_bitflips, ret);
}
if (status & NFC_ECC_ERR_MSK) {
for (i = 0; i < nchunks; i++) {
int data_off = i * ecc->size;
int oob_off = i * (ecc->bytes + 4);
u8 *data = buf + data_off;
u8 *oob = nand->oob_poi + oob_off;
if (!(status & NFC_ECC_ERR(i)))
continue;
/*
* Re-read the data with the randomizer disabled to
* identify bitflips in erased pages.
*/
if (randomized) {
/* TODO: use DMA to read page in raw mode */
nand->cmdfunc(mtd, NAND_CMD_RNDOUT,
data_off, -1);
nand->read_buf(mtd, data, ecc->size);
}
/* TODO: use DMA to retrieve OOB */
nand->cmdfunc(mtd, NAND_CMD_RNDOUT,
mtd->writesize + oob_off, -1);
nand->read_buf(mtd, oob, ecc->bytes + 4);
ret = nand_check_erased_ecc_chunk(data, ecc->size,
oob, ecc->bytes + 4,
NULL, 0,
ecc->strength);
if (ret >= 0)
raw_mode = 1;
sunxi_nfc_hw_ecc_update_stats(mtd, &max_bitflips, ret);
}
}
if (oob_required)
sunxi_nfc_hw_ecc_read_extra_oob(mtd, nand->oob_poi,
NULL, !raw_mode,
page);
return max_bitflips;
}
static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
const u8 *data, int data_off,
const u8 *oob, int oob_off,
@ -1065,6 +1253,23 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
return max_bitflips;
}
static int sunxi_nfc_hw_ecc_read_page_dma(struct mtd_info *mtd,
struct nand_chip *chip, u8 *buf,
int oob_required, int page)
{
int ret;
ret = sunxi_nfc_hw_ecc_read_chunks_dma(mtd, buf, oob_required, page,
chip->ecc.steps);
if (ret >= 0)
return ret;
/* Fallback to PIO mode */
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
return sunxi_nfc_hw_ecc_read_page(mtd, chip, buf, oob_required, page);
}
static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd,
struct nand_chip *chip,
u32 data_offs, u32 readlen,
@ -1098,6 +1303,25 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd,
return max_bitflips;
}
static int sunxi_nfc_hw_ecc_read_subpage_dma(struct mtd_info *mtd,
struct nand_chip *chip,
u32 data_offs, u32 readlen,
u8 *buf, int page)
{
int nchunks = DIV_ROUND_UP(data_offs + readlen, chip->ecc.size);
int ret;
ret = sunxi_nfc_hw_ecc_read_chunks_dma(mtd, buf, false, page, nchunks);
if (ret >= 0)
return ret;
/* Fallback to PIO mode */
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
return sunxi_nfc_hw_ecc_read_subpage(mtd, chip, data_offs, readlen,
buf, page);
}
static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf, int oob_required,
@ -1130,6 +1354,99 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
return 0;
}
static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd,
struct nand_chip *chip,
u32 data_offs, u32 data_len,
const u8 *buf, int oob_required,
int page)
{
struct nand_ecc_ctrl *ecc = &chip->ecc;
int ret, i, cur_off = 0;
sunxi_nfc_hw_ecc_enable(mtd);
for (i = data_offs / ecc->size;
i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) {
int data_off = i * ecc->size;
int oob_off = i * (ecc->bytes + 4);
const u8 *data = buf + data_off;
const u8 *oob = chip->oob_poi + oob_off;
ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob,
oob_off + mtd->writesize,
&cur_off, !i, page);
if (ret)
return ret;
}
sunxi_nfc_hw_ecc_disable(mtd);
return 0;
}
static int sunxi_nfc_hw_ecc_write_page_dma(struct mtd_info *mtd,
struct nand_chip *chip,
const u8 *buf,
int oob_required,
int page)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct nand_ecc_ctrl *ecc = &nand->ecc;
struct scatterlist sg;
int ret, i;
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
if (ret)
return ret;
ret = sunxi_nfc_dma_op_prepare(mtd, buf, ecc->size, ecc->steps,
DMA_TO_DEVICE, &sg);
if (ret)
goto pio_fallback;
for (i = 0; i < ecc->steps; i++) {
const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4));
sunxi_nfc_hw_ecc_set_prot_oob_bytes(mtd, oob, i, !i, page);
}
sunxi_nfc_hw_ecc_enable(mtd);
sunxi_nfc_randomizer_config(mtd, page, false);
sunxi_nfc_randomizer_enable(mtd);
writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG,
nfc->regs + NFC_REG_RCMD_SET);
dma_async_issue_pending(nfc->dmac);
writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD |
NFC_DATA_TRANS | NFC_ACCESS_DIR,
nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
if (ret)
dmaengine_terminate_all(nfc->dmac);
sunxi_nfc_randomizer_disable(mtd);
sunxi_nfc_hw_ecc_disable(mtd);
sunxi_nfc_dma_op_cleanup(mtd, DMA_TO_DEVICE, &sg);
if (ret)
return ret;
if (oob_required || (chip->options & NAND_NEED_SCRAMBLING))
/* TODO: use DMA to transfer extra OOB bytes ? */
sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi,
NULL, page);
return 0;
pio_fallback:
return sunxi_nfc_hw_ecc_write_page(mtd, chip, buf, oob_required, page);
}
static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf, int oob_required,
@ -1497,10 +1814,19 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
int ret;
int i;
if (ecc->size != 512 && ecc->size != 1024)
return -EINVAL;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
/* Prefer 1k ECC chunk over 512 ones */
if (ecc->size == 512 && mtd->writesize > 512) {
ecc->size = 1024;
ecc->strength *= 2;
}
/* Add ECC info retrieval from DT */
for (i = 0; i < ARRAY_SIZE(strengths); i++) {
if (ecc->strength <= strengths[i])
@ -1550,14 +1876,28 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
struct nand_ecc_ctrl *ecc,
struct device_node *np)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
int ret;
ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np);
if (ret)
return ret;
ecc->read_page = sunxi_nfc_hw_ecc_read_page;
ecc->write_page = sunxi_nfc_hw_ecc_write_page;
if (nfc->dmac) {
ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma;
ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma;
ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma;
nand->options |= NAND_USE_BOUNCE_BUFFER;
} else {
ecc->read_page = sunxi_nfc_hw_ecc_read_page;
ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage;
ecc->write_page = sunxi_nfc_hw_ecc_write_page;
}
/* TODO: support DMA for raw accesses and subpage write */
ecc->write_subpage = sunxi_nfc_hw_ecc_write_subpage;
ecc->read_oob_raw = nand_read_oob_std;
ecc->write_oob_raw = nand_write_oob_std;
ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage;
@ -1871,26 +2211,59 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
if (ret)
goto out_ahb_clk_unprepare;
nfc->reset = devm_reset_control_get_optional(dev, "ahb");
if (!IS_ERR(nfc->reset)) {
ret = reset_control_deassert(nfc->reset);
if (ret) {
dev_err(dev, "reset err %d\n", ret);
goto out_mod_clk_unprepare;
}
} else if (PTR_ERR(nfc->reset) != -ENOENT) {
ret = PTR_ERR(nfc->reset);
goto out_mod_clk_unprepare;
}
ret = sunxi_nfc_rst(nfc);
if (ret)
goto out_mod_clk_unprepare;
goto out_ahb_reset_reassert;
writel(0, nfc->regs + NFC_REG_INT);
ret = devm_request_irq(dev, irq, sunxi_nfc_interrupt,
0, "sunxi-nand", nfc);
if (ret)
goto out_mod_clk_unprepare;
goto out_ahb_reset_reassert;
nfc->dmac = dma_request_slave_channel(dev, "rxtx");
if (nfc->dmac) {
struct dma_slave_config dmac_cfg = { };
dmac_cfg.src_addr = r->start + NFC_REG_IO_DATA;
dmac_cfg.dst_addr = dmac_cfg.src_addr;
dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width;
dmac_cfg.src_maxburst = 4;
dmac_cfg.dst_maxburst = 4;
dmaengine_slave_config(nfc->dmac, &dmac_cfg);
} else {
dev_warn(dev, "failed to request rxtx DMA channel\n");
}
platform_set_drvdata(pdev, nfc);
ret = sunxi_nand_chips_init(dev, nfc);
if (ret) {
dev_err(dev, "failed to init nand chips\n");
goto out_mod_clk_unprepare;
goto out_release_dmac;
}
return 0;
out_release_dmac:
if (nfc->dmac)
dma_release_channel(nfc->dmac);
out_ahb_reset_reassert:
if (!IS_ERR(nfc->reset))
reset_control_assert(nfc->reset);
out_mod_clk_unprepare:
clk_disable_unprepare(nfc->mod_clk);
out_ahb_clk_unprepare:
@ -1904,6 +2277,12 @@ static int sunxi_nfc_remove(struct platform_device *pdev)
struct sunxi_nfc *nfc = platform_get_drvdata(pdev);
sunxi_nand_chips_cleanup(nfc);
if (!IS_ERR(nfc->reset))
reset_control_assert(nfc->reset);
if (nfc->dmac)
dma_release_channel(nfc->dmac);
clk_disable_unprepare(nfc->mod_clk);
clk_disable_unprepare(nfc->ahb_clk);

Просмотреть файл

@ -4,6 +4,7 @@
* by the Free Software Foundation.
*
* Copyright © 2012 John Crispin <blogic@openwrt.org>
* Copyright © 2016 Hauke Mehrtens <hauke@hauke-m.de>
*/
#include <linux/mtd/nand.h>
@ -16,20 +17,28 @@
#define EBU_ADDSEL1 0x24
#define EBU_NAND_CON 0xB0
#define EBU_NAND_WAIT 0xB4
#define NAND_WAIT_RD BIT(0) /* NAND flash status output */
#define NAND_WAIT_WR_C BIT(3) /* NAND Write/Read complete */
#define EBU_NAND_ECC0 0xB8
#define EBU_NAND_ECC_AC 0xBC
/* nand commands */
#define NAND_CMD_ALE (1 << 2)
#define NAND_CMD_CLE (1 << 3)
#define NAND_CMD_CS (1 << 4)
#define NAND_WRITE_CMD_RESET 0xff
/*
* nand commands
* The pins of the NAND chip are selected based on the address bits of the
* "register" read and write. There are no special registers, but an
* address range and the lower address bits are used to activate the
* correct line. For example when the bit (1 << 2) is set in the address
* the ALE pin will be activated.
*/
#define NAND_CMD_ALE BIT(2) /* address latch enable */
#define NAND_CMD_CLE BIT(3) /* command latch enable */
#define NAND_CMD_CS BIT(4) /* chip select */
#define NAND_CMD_SE BIT(5) /* spare area access latch */
#define NAND_CMD_WP BIT(6) /* write protect */
#define NAND_WRITE_CMD (NAND_CMD_CS | NAND_CMD_CLE)
#define NAND_WRITE_ADDR (NAND_CMD_CS | NAND_CMD_ALE)
#define NAND_WRITE_DATA (NAND_CMD_CS)
#define NAND_READ_DATA (NAND_CMD_CS)
#define NAND_WAIT_WR_C (1 << 3)
#define NAND_WAIT_RD (0x1)
/* we need to tel the ebu which addr we mapped the nand to */
#define ADDSEL1_MASK(x) (x << 4)
@ -54,31 +63,41 @@
#define NAND_CON_CSMUX (1 << 1)
#define NAND_CON_NANDM 1
static void xway_reset_chip(struct nand_chip *chip)
struct xway_nand_data {
struct nand_chip chip;
unsigned long csflags;
void __iomem *nandaddr;
};
static u8 xway_readb(struct mtd_info *mtd, int op)
{
unsigned long nandaddr = (unsigned long) chip->IO_ADDR_W;
unsigned long flags;
struct nand_chip *chip = mtd_to_nand(mtd);
struct xway_nand_data *data = nand_get_controller_data(chip);
nandaddr &= ~NAND_WRITE_ADDR;
nandaddr |= NAND_WRITE_CMD;
/* finish with a reset */
spin_lock_irqsave(&ebu_lock, flags);
writeb(NAND_WRITE_CMD_RESET, (void __iomem *) nandaddr);
while ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0)
;
spin_unlock_irqrestore(&ebu_lock, flags);
return readb(data->nandaddr + op);
}
static void xway_select_chip(struct mtd_info *mtd, int chip)
static void xway_writeb(struct mtd_info *mtd, int op, u8 value)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct xway_nand_data *data = nand_get_controller_data(chip);
switch (chip) {
writeb(value, data->nandaddr + op);
}
static void xway_select_chip(struct mtd_info *mtd, int select)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct xway_nand_data *data = nand_get_controller_data(chip);
switch (select) {
case -1:
ltq_ebu_w32_mask(NAND_CON_CE, 0, EBU_NAND_CON);
ltq_ebu_w32_mask(NAND_CON_NANDM, 0, EBU_NAND_CON);
spin_unlock_irqrestore(&ebu_lock, data->csflags);
break;
case 0:
spin_lock_irqsave(&ebu_lock, data->csflags);
ltq_ebu_w32_mask(0, NAND_CON_NANDM, EBU_NAND_CON);
ltq_ebu_w32_mask(0, NAND_CON_CE, EBU_NAND_CON);
break;
@ -89,26 +108,16 @@ static void xway_select_chip(struct mtd_info *mtd, int chip)
static void xway_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *this = mtd_to_nand(mtd);
unsigned long nandaddr = (unsigned long) this->IO_ADDR_W;
unsigned long flags;
if (cmd == NAND_CMD_NONE)
return;
if (ctrl & NAND_CTRL_CHANGE) {
nandaddr &= ~(NAND_WRITE_CMD | NAND_WRITE_ADDR);
if (ctrl & NAND_CLE)
nandaddr |= NAND_WRITE_CMD;
else
nandaddr |= NAND_WRITE_ADDR;
this->IO_ADDR_W = (void __iomem *) nandaddr;
}
if (ctrl & NAND_CLE)
xway_writeb(mtd, NAND_WRITE_CMD, cmd);
else if (ctrl & NAND_ALE)
xway_writeb(mtd, NAND_WRITE_ADDR, cmd);
if (cmd != NAND_CMD_NONE) {
spin_lock_irqsave(&ebu_lock, flags);
writeb(cmd, this->IO_ADDR_W);
while ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0)
;
spin_unlock_irqrestore(&ebu_lock, flags);
}
while ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0)
;
}
static int xway_dev_ready(struct mtd_info *mtd)
@ -118,80 +127,122 @@ static int xway_dev_ready(struct mtd_info *mtd)
static unsigned char xway_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd_to_nand(mtd);
unsigned long nandaddr = (unsigned long) this->IO_ADDR_R;
unsigned long flags;
int ret;
spin_lock_irqsave(&ebu_lock, flags);
ret = ltq_r8((void __iomem *)(nandaddr + NAND_READ_DATA));
spin_unlock_irqrestore(&ebu_lock, flags);
return ret;
return xway_readb(mtd, NAND_READ_DATA);
}
static void xway_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
int i;
for (i = 0; i < len; i++)
buf[i] = xway_readb(mtd, NAND_WRITE_DATA);
}
static void xway_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
for (i = 0; i < len; i++)
xway_writeb(mtd, NAND_WRITE_DATA, buf[i]);
}
/*
* Probe for the NAND device.
*/
static int xway_nand_probe(struct platform_device *pdev)
{
struct nand_chip *this = platform_get_drvdata(pdev);
unsigned long nandaddr = (unsigned long) this->IO_ADDR_W;
const __be32 *cs = of_get_property(pdev->dev.of_node,
"lantiq,cs", NULL);
struct xway_nand_data *data;
struct mtd_info *mtd;
struct resource *res;
int err;
u32 cs;
u32 cs_flag = 0;
/* Allocate memory for the device structure (and zero it) */
data = devm_kzalloc(&pdev->dev, sizeof(struct xway_nand_data),
GFP_KERNEL);
if (!data)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
data->nandaddr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(data->nandaddr))
return PTR_ERR(data->nandaddr);
nand_set_flash_node(&data->chip, pdev->dev.of_node);
mtd = nand_to_mtd(&data->chip);
mtd->dev.parent = &pdev->dev;
data->chip.cmd_ctrl = xway_cmd_ctrl;
data->chip.dev_ready = xway_dev_ready;
data->chip.select_chip = xway_select_chip;
data->chip.write_buf = xway_write_buf;
data->chip.read_buf = xway_read_buf;
data->chip.read_byte = xway_read_byte;
data->chip.chip_delay = 30;
data->chip.ecc.mode = NAND_ECC_SOFT;
data->chip.ecc.algo = NAND_ECC_HAMMING;
platform_set_drvdata(pdev, data);
nand_set_controller_data(&data->chip, data);
/* load our CS from the DT. Either we find a valid 1 or default to 0 */
if (cs && (*cs == 1))
err = of_property_read_u32(pdev->dev.of_node, "lantiq,cs", &cs);
if (!err && cs == 1)
cs_flag = NAND_CON_IN_CS1 | NAND_CON_OUT_CS1;
/* setup the EBU to run in NAND mode on our base addr */
ltq_ebu_w32(CPHYSADDR(nandaddr)
| ADDSEL1_MASK(3) | ADDSEL1_REGEN, EBU_ADDSEL1);
ltq_ebu_w32(CPHYSADDR(data->nandaddr)
| ADDSEL1_MASK(3) | ADDSEL1_REGEN, EBU_ADDSEL1);
ltq_ebu_w32(BUSCON1_SETUP | BUSCON1_BCGEN_RES | BUSCON1_WAITWRC2
| BUSCON1_WAITRDC2 | BUSCON1_HOLDC1 | BUSCON1_RECOVC1
| BUSCON1_CMULT4, LTQ_EBU_BUSCON1);
| BUSCON1_WAITRDC2 | BUSCON1_HOLDC1 | BUSCON1_RECOVC1
| BUSCON1_CMULT4, LTQ_EBU_BUSCON1);
ltq_ebu_w32(NAND_CON_NANDM | NAND_CON_CSMUX | NAND_CON_CS_P
| NAND_CON_SE_P | NAND_CON_WP_P | NAND_CON_PRE_P
| cs_flag, EBU_NAND_CON);
| NAND_CON_SE_P | NAND_CON_WP_P | NAND_CON_PRE_P
| cs_flag, EBU_NAND_CON);
/* finish with a reset */
xway_reset_chip(this);
/* Scan to find existence of the device */
err = nand_scan(mtd, 1);
if (err)
return err;
return 0;
err = mtd_device_register(mtd, NULL, 0);
if (err)
nand_release(mtd);
return err;
}
static struct platform_nand_data xway_nand_data = {
.chip = {
.nr_chips = 1,
.chip_delay = 30,
},
.ctrl = {
.probe = xway_nand_probe,
.cmd_ctrl = xway_cmd_ctrl,
.dev_ready = xway_dev_ready,
.select_chip = xway_select_chip,
.read_byte = xway_read_byte,
}
};
/*
* Try to find the node inside the DT. If it is available attach out
* platform_nand_data
* Remove a NAND device.
*/
static int __init xway_register_nand(void)
static int xway_nand_remove(struct platform_device *pdev)
{
struct device_node *node;
struct platform_device *pdev;
struct xway_nand_data *data = platform_get_drvdata(pdev);
nand_release(nand_to_mtd(&data->chip));
node = of_find_compatible_node(NULL, NULL, "lantiq,nand-xway");
if (!node)
return -ENOENT;
pdev = of_find_device_by_node(node);
if (!pdev)
return -EINVAL;
pdev->dev.platform_data = &xway_nand_data;
of_node_put(node);
return 0;
}
subsys_initcall(xway_register_nand);
static const struct of_device_id xway_nand_match[] = {
{ .compatible = "lantiq,nand-xway" },
{},
};
MODULE_DEVICE_TABLE(of, xway_nand_match);
static struct platform_driver xway_nand_driver = {
.probe = xway_nand_probe,
.remove = xway_nand_remove,
.driver = {
.name = "lantiq,nand-xway",
.of_match_table = xway_nand_match,
},
};
module_platform_driver(xway_nand_driver);
MODULE_LICENSE("GPL");

Просмотреть файл

@ -3188,13 +3188,13 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
size_t tmp_retlen;
ret = action(mtd, from, len, &tmp_retlen, buf);
if (ret)
break;
buf += tmp_retlen;
len -= tmp_retlen;
*retlen += tmp_retlen;
if (ret)
break;
}
otp_pages--;
}

Просмотреть файл

@ -29,6 +29,26 @@ config MTD_SPI_NOR_USE_4K_SECTORS
Please note that some tools/drivers/filesystems may not work with
4096 B erase size (e.g. UBIFS requires 15 KiB as a minimum).
config SPI_ATMEL_QUADSPI
tristate "Atmel Quad SPI Controller"
depends on ARCH_AT91 || (ARM && COMPILE_TEST)
depends on OF && HAS_IOMEM
help
This enables support for the Quad SPI controller in master mode.
This driver does not support generic SPI. The implementation only
supports SPI NOR.
config SPI_CADENCE_QUADSPI
tristate "Cadence Quad SPI controller"
depends on OF && ARM
help
Enable support for the Cadence Quad SPI Flash controller.
Cadence QSPI is a specialized controller for connecting an SPI
Flash over 1/2/4-bit wide bus. Enable this option if you have a
device with a Cadence QSPI controller and want to access the
Flash as an MTD device.
config SPI_FSL_QUADSPI
tristate "Freescale Quad SPI controller"
depends on ARCH_MXC || SOC_LS1021A || ARCH_LAYERSCAPE || COMPILE_TEST
@ -38,6 +58,13 @@ config SPI_FSL_QUADSPI
This controller does not support generic SPI. It only supports
SPI NOR.
config SPI_HISI_SFC
tristate "Hisilicon SPI-NOR Flash Controller(SFC)"
depends on ARCH_HISI || COMPILE_TEST
depends on HAS_IOMEM && HAS_DMA
help
This enables support for hisilicon SPI-NOR flash controller.
config SPI_NXP_SPIFI
tristate "NXP SPI Flash Interface (SPIFI)"
depends on OF && (ARCH_LPC18XX || COMPILE_TEST)

Просмотреть файл

@ -1,4 +1,7 @@
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.o
obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o
obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o
obj-$(CONFIG_MTD_MT81xx_NOR) += mtk-quadspi.o
obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o

Просмотреть файл

@ -0,0 +1,732 @@
/*
* Driver for Atmel QSPI Controller
*
* Copyright (C) 2015 Atmel Corporation
*
* Author: Cyrille Pitchen <cyrille.pitchen@atmel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* This driver is based on drivers/mtd/spi-nor/fsl-quadspi.c from Freescale.
*/
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/spi-nor.h>
#include <linux/platform_data/atmel.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/pinctrl/consumer.h>
/* QSPI register offsets */
#define QSPI_CR 0x0000 /* Control Register */
#define QSPI_MR 0x0004 /* Mode Register */
#define QSPI_RD 0x0008 /* Receive Data Register */
#define QSPI_TD 0x000c /* Transmit Data Register */
#define QSPI_SR 0x0010 /* Status Register */
#define QSPI_IER 0x0014 /* Interrupt Enable Register */
#define QSPI_IDR 0x0018 /* Interrupt Disable Register */
#define QSPI_IMR 0x001c /* Interrupt Mask Register */
#define QSPI_SCR 0x0020 /* Serial Clock Register */
#define QSPI_IAR 0x0030 /* Instruction Address Register */
#define QSPI_ICR 0x0034 /* Instruction Code Register */
#define QSPI_IFR 0x0038 /* Instruction Frame Register */
#define QSPI_SMR 0x0040 /* Scrambling Mode Register */
#define QSPI_SKR 0x0044 /* Scrambling Key Register */
#define QSPI_WPMR 0x00E4 /* Write Protection Mode Register */
#define QSPI_WPSR 0x00E8 /* Write Protection Status Register */
#define QSPI_VERSION 0x00FC /* Version Register */
/* Bitfields in QSPI_CR (Control Register) */
#define QSPI_CR_QSPIEN BIT(0)
#define QSPI_CR_QSPIDIS BIT(1)
#define QSPI_CR_SWRST BIT(7)
#define QSPI_CR_LASTXFER BIT(24)
/* Bitfields in QSPI_MR (Mode Register) */
#define QSPI_MR_SSM BIT(0)
#define QSPI_MR_LLB BIT(1)
#define QSPI_MR_WDRBT BIT(2)
#define QSPI_MR_SMRM BIT(3)
#define QSPI_MR_CSMODE_MASK GENMASK(5, 4)
#define QSPI_MR_CSMODE_NOT_RELOADED (0 << 4)
#define QSPI_MR_CSMODE_LASTXFER (1 << 4)
#define QSPI_MR_CSMODE_SYSTEMATICALLY (2 << 4)
#define QSPI_MR_NBBITS_MASK GENMASK(11, 8)
#define QSPI_MR_NBBITS(n) ((((n) - 8) << 8) & QSPI_MR_NBBITS_MASK)
#define QSPI_MR_DLYBCT_MASK GENMASK(23, 16)
#define QSPI_MR_DLYBCT(n) (((n) << 16) & QSPI_MR_DLYBCT_MASK)
#define QSPI_MR_DLYCS_MASK GENMASK(31, 24)
#define QSPI_MR_DLYCS(n) (((n) << 24) & QSPI_MR_DLYCS_MASK)
/* Bitfields in QSPI_SR/QSPI_IER/QSPI_IDR/QSPI_IMR */
#define QSPI_SR_RDRF BIT(0)
#define QSPI_SR_TDRE BIT(1)
#define QSPI_SR_TXEMPTY BIT(2)
#define QSPI_SR_OVRES BIT(3)
#define QSPI_SR_CSR BIT(8)
#define QSPI_SR_CSS BIT(9)
#define QSPI_SR_INSTRE BIT(10)
#define QSPI_SR_QSPIENS BIT(24)
#define QSPI_SR_CMD_COMPLETED (QSPI_SR_INSTRE | QSPI_SR_CSR)
/* Bitfields in QSPI_SCR (Serial Clock Register) */
#define QSPI_SCR_CPOL BIT(0)
#define QSPI_SCR_CPHA BIT(1)
#define QSPI_SCR_SCBR_MASK GENMASK(15, 8)
#define QSPI_SCR_SCBR(n) (((n) << 8) & QSPI_SCR_SCBR_MASK)
#define QSPI_SCR_DLYBS_MASK GENMASK(23, 16)
#define QSPI_SCR_DLYBS(n) (((n) << 16) & QSPI_SCR_DLYBS_MASK)
/* Bitfields in QSPI_ICR (Instruction Code Register) */
#define QSPI_ICR_INST_MASK GENMASK(7, 0)
#define QSPI_ICR_INST(inst) (((inst) << 0) & QSPI_ICR_INST_MASK)
#define QSPI_ICR_OPT_MASK GENMASK(23, 16)
#define QSPI_ICR_OPT(opt) (((opt) << 16) & QSPI_ICR_OPT_MASK)
/* Bitfields in QSPI_IFR (Instruction Frame Register) */
#define QSPI_IFR_WIDTH_MASK GENMASK(2, 0)
#define QSPI_IFR_WIDTH_SINGLE_BIT_SPI (0 << 0)
#define QSPI_IFR_WIDTH_DUAL_OUTPUT (1 << 0)
#define QSPI_IFR_WIDTH_QUAD_OUTPUT (2 << 0)
#define QSPI_IFR_WIDTH_DUAL_IO (3 << 0)
#define QSPI_IFR_WIDTH_QUAD_IO (4 << 0)
#define QSPI_IFR_WIDTH_DUAL_CMD (5 << 0)
#define QSPI_IFR_WIDTH_QUAD_CMD (6 << 0)
#define QSPI_IFR_INSTEN BIT(4)
#define QSPI_IFR_ADDREN BIT(5)
#define QSPI_IFR_OPTEN BIT(6)
#define QSPI_IFR_DATAEN BIT(7)
#define QSPI_IFR_OPTL_MASK GENMASK(9, 8)
#define QSPI_IFR_OPTL_1BIT (0 << 8)
#define QSPI_IFR_OPTL_2BIT (1 << 8)
#define QSPI_IFR_OPTL_4BIT (2 << 8)
#define QSPI_IFR_OPTL_8BIT (3 << 8)
#define QSPI_IFR_ADDRL BIT(10)
#define QSPI_IFR_TFRTYP_MASK GENMASK(13, 12)
#define QSPI_IFR_TFRTYP_TRSFR_READ (0 << 12)
#define QSPI_IFR_TFRTYP_TRSFR_READ_MEM (1 << 12)
#define QSPI_IFR_TFRTYP_TRSFR_WRITE (2 << 12)
#define QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM (3 << 13)
#define QSPI_IFR_CRM BIT(14)
#define QSPI_IFR_NBDUM_MASK GENMASK(20, 16)
#define QSPI_IFR_NBDUM(n) (((n) << 16) & QSPI_IFR_NBDUM_MASK)
/* Bitfields in QSPI_SMR (Scrambling Mode Register) */
#define QSPI_SMR_SCREN BIT(0)
#define QSPI_SMR_RVDIS BIT(1)
/* Bitfields in QSPI_WPMR (Write Protection Mode Register) */
#define QSPI_WPMR_WPEN BIT(0)
#define QSPI_WPMR_WPKEY_MASK GENMASK(31, 8)
#define QSPI_WPMR_WPKEY(wpkey) (((wpkey) << 8) & QSPI_WPMR_WPKEY_MASK)
/* Bitfields in QSPI_WPSR (Write Protection Status Register) */
#define QSPI_WPSR_WPVS BIT(0)
#define QSPI_WPSR_WPVSRC_MASK GENMASK(15, 8)
#define QSPI_WPSR_WPVSRC(src) (((src) << 8) & QSPI_WPSR_WPVSRC)
struct atmel_qspi {
void __iomem *regs;
void __iomem *mem;
struct clk *clk;
struct platform_device *pdev;
u32 pending;
struct spi_nor nor;
u32 clk_rate;
struct completion cmd_completion;
};
struct atmel_qspi_command {
union {
struct {
u32 instruction:1;
u32 address:3;
u32 mode:1;
u32 dummy:1;
u32 data:1;
u32 reserved:25;
} bits;
u32 word;
} enable;
u8 instruction;
u8 mode;
u8 num_mode_cycles;
u8 num_dummy_cycles;
u32 address;
size_t buf_len;
const void *tx_buf;
void *rx_buf;
};
/* Register access functions */
static inline u32 qspi_readl(struct atmel_qspi *aq, u32 reg)
{
return readl_relaxed(aq->regs + reg);
}
static inline void qspi_writel(struct atmel_qspi *aq, u32 reg, u32 value)
{
writel_relaxed(value, aq->regs + reg);
}
static int atmel_qspi_run_transfer(struct atmel_qspi *aq,
const struct atmel_qspi_command *cmd)
{
void __iomem *ahb_mem;
/* Then fallback to a PIO transfer (memcpy() DOES NOT work!) */
ahb_mem = aq->mem;
if (cmd->enable.bits.address)
ahb_mem += cmd->address;
if (cmd->tx_buf)
_memcpy_toio(ahb_mem, cmd->tx_buf, cmd->buf_len);
else
_memcpy_fromio(cmd->rx_buf, ahb_mem, cmd->buf_len);
return 0;
}
#ifdef DEBUG
static void atmel_qspi_debug_command(struct atmel_qspi *aq,
const struct atmel_qspi_command *cmd,
u32 ifr)
{
u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
size_t len = 0;
int i;
if (cmd->enable.bits.instruction)
cmd_buf[len++] = cmd->instruction;
for (i = cmd->enable.bits.address-1; i >= 0; --i)
cmd_buf[len++] = (cmd->address >> (i << 3)) & 0xff;
if (cmd->enable.bits.mode)
cmd_buf[len++] = cmd->mode;
if (cmd->enable.bits.dummy) {
int num = cmd->num_dummy_cycles;
switch (ifr & QSPI_IFR_WIDTH_MASK) {
case QSPI_IFR_WIDTH_SINGLE_BIT_SPI:
case QSPI_IFR_WIDTH_DUAL_OUTPUT:
case QSPI_IFR_WIDTH_QUAD_OUTPUT:
num >>= 3;
break;
case QSPI_IFR_WIDTH_DUAL_IO:
case QSPI_IFR_WIDTH_DUAL_CMD:
num >>= 2;
break;
case QSPI_IFR_WIDTH_QUAD_IO:
case QSPI_IFR_WIDTH_QUAD_CMD:
num >>= 1;
break;
default:
return;
}
for (i = 0; i < num; ++i)
cmd_buf[len++] = 0;
}
/* Dump the SPI command */
print_hex_dump(KERN_DEBUG, "qspi cmd: ", DUMP_PREFIX_NONE,
32, 1, cmd_buf, len, false);
#ifdef VERBOSE_DEBUG
/* If verbose debug is enabled, also dump the TX data */
if (cmd->enable.bits.data && cmd->tx_buf)
print_hex_dump(KERN_DEBUG, "qspi tx : ", DUMP_PREFIX_NONE,
32, 1, cmd->tx_buf, cmd->buf_len, false);
#endif
}
#else
#define atmel_qspi_debug_command(aq, cmd, ifr)
#endif
static int atmel_qspi_run_command(struct atmel_qspi *aq,
const struct atmel_qspi_command *cmd,
u32 ifr_tfrtyp, u32 ifr_width)
{
u32 iar, icr, ifr, sr;
int err = 0;
iar = 0;
icr = 0;
ifr = ifr_tfrtyp | ifr_width;
/* Compute instruction parameters */
if (cmd->enable.bits.instruction) {
icr |= QSPI_ICR_INST(cmd->instruction);
ifr |= QSPI_IFR_INSTEN;
}
/* Compute address parameters */
switch (cmd->enable.bits.address) {
case 4:
ifr |= QSPI_IFR_ADDRL;
/* fall through to the 24bit (3 byte) address case. */
case 3:
iar = (cmd->enable.bits.data) ? 0 : cmd->address;
ifr |= QSPI_IFR_ADDREN;
break;
case 0:
break;
default:
return -EINVAL;
}
/* Compute option parameters */
if (cmd->enable.bits.mode && cmd->num_mode_cycles) {
u32 mode_cycle_bits, mode_bits;
icr |= QSPI_ICR_OPT(cmd->mode);
ifr |= QSPI_IFR_OPTEN;
switch (ifr & QSPI_IFR_WIDTH_MASK) {
case QSPI_IFR_WIDTH_SINGLE_BIT_SPI:
case QSPI_IFR_WIDTH_DUAL_OUTPUT:
case QSPI_IFR_WIDTH_QUAD_OUTPUT:
mode_cycle_bits = 1;
break;
case QSPI_IFR_WIDTH_DUAL_IO:
case QSPI_IFR_WIDTH_DUAL_CMD:
mode_cycle_bits = 2;
break;
case QSPI_IFR_WIDTH_QUAD_IO:
case QSPI_IFR_WIDTH_QUAD_CMD:
mode_cycle_bits = 4;
break;
default:
return -EINVAL;
}
mode_bits = cmd->num_mode_cycles * mode_cycle_bits;
switch (mode_bits) {
case 1:
ifr |= QSPI_IFR_OPTL_1BIT;
break;
case 2:
ifr |= QSPI_IFR_OPTL_2BIT;
break;
case 4:
ifr |= QSPI_IFR_OPTL_4BIT;
break;
case 8:
ifr |= QSPI_IFR_OPTL_8BIT;
break;
default:
return -EINVAL;
}
}
/* Set number of dummy cycles */
if (cmd->enable.bits.dummy)
ifr |= QSPI_IFR_NBDUM(cmd->num_dummy_cycles);
/* Set data enable */
if (cmd->enable.bits.data) {
ifr |= QSPI_IFR_DATAEN;
/* Special case for Continuous Read Mode */
if (!cmd->tx_buf && !cmd->rx_buf)
ifr |= QSPI_IFR_CRM;
}
/* Clear pending interrupts */
(void)qspi_readl(aq, QSPI_SR);
/* Set QSPI Instruction Frame registers */
atmel_qspi_debug_command(aq, cmd, ifr);
qspi_writel(aq, QSPI_IAR, iar);
qspi_writel(aq, QSPI_ICR, icr);
qspi_writel(aq, QSPI_IFR, ifr);
/* Skip to the final steps if there is no data */
if (!cmd->enable.bits.data)
goto no_data;
/* Dummy read of QSPI_IFR to synchronize APB and AHB accesses */
(void)qspi_readl(aq, QSPI_IFR);
/* Stop here for continuous read */
if (!cmd->tx_buf && !cmd->rx_buf)
return 0;
/* Send/Receive data */
err = atmel_qspi_run_transfer(aq, cmd);
/* Release the chip-select */
qspi_writel(aq, QSPI_CR, QSPI_CR_LASTXFER);
if (err)
return err;
#if defined(DEBUG) && defined(VERBOSE_DEBUG)
/*
* If verbose debug is enabled, also dump the RX data in addition to
* the SPI command previously dumped by atmel_qspi_debug_command()
*/
if (cmd->rx_buf)
print_hex_dump(KERN_DEBUG, "qspi rx : ", DUMP_PREFIX_NONE,
32, 1, cmd->rx_buf, cmd->buf_len, false);
#endif
no_data:
/* Poll INSTRuction End status */
sr = qspi_readl(aq, QSPI_SR);
if ((sr & QSPI_SR_CMD_COMPLETED) == QSPI_SR_CMD_COMPLETED)
return err;
/* Wait for INSTRuction End interrupt */
reinit_completion(&aq->cmd_completion);
aq->pending = sr & QSPI_SR_CMD_COMPLETED;
qspi_writel(aq, QSPI_IER, QSPI_SR_CMD_COMPLETED);
if (!wait_for_completion_timeout(&aq->cmd_completion,
msecs_to_jiffies(1000)))
err = -ETIMEDOUT;
qspi_writel(aq, QSPI_IDR, QSPI_SR_CMD_COMPLETED);
return err;
}
static int atmel_qspi_read_reg(struct spi_nor *nor, u8 opcode,
u8 *buf, int len)
{
struct atmel_qspi *aq = nor->priv;
struct atmel_qspi_command cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.enable.bits.instruction = 1;
cmd.enable.bits.data = 1;
cmd.instruction = opcode;
cmd.rx_buf = buf;
cmd.buf_len = len;
return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ,
QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
}
static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
u8 *buf, int len)
{
struct atmel_qspi *aq = nor->priv;
struct atmel_qspi_command cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.enable.bits.instruction = 1;
cmd.enable.bits.data = (buf != NULL && len > 0);
cmd.instruction = opcode;
cmd.tx_buf = buf;
cmd.buf_len = len;
return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
}
static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
const u_char *write_buf)
{
struct atmel_qspi *aq = nor->priv;
struct atmel_qspi_command cmd;
ssize_t ret;
memset(&cmd, 0, sizeof(cmd));
cmd.enable.bits.instruction = 1;
cmd.enable.bits.address = nor->addr_width;
cmd.enable.bits.data = 1;
cmd.instruction = nor->program_opcode;
cmd.address = (u32)to;
cmd.tx_buf = write_buf;
cmd.buf_len = len;
ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM,
QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
return (ret < 0) ? ret : len;
}
static int atmel_qspi_erase(struct spi_nor *nor, loff_t offs)
{
struct atmel_qspi *aq = nor->priv;
struct atmel_qspi_command cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.enable.bits.instruction = 1;
cmd.enable.bits.address = nor->addr_width;
cmd.instruction = nor->erase_opcode;
cmd.address = (u32)offs;
return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
}
static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
u_char *read_buf)
{
struct atmel_qspi *aq = nor->priv;
struct atmel_qspi_command cmd;
u8 num_mode_cycles, num_dummy_cycles;
u32 ifr_width;
ssize_t ret;
switch (nor->flash_read) {
case SPI_NOR_NORMAL:
case SPI_NOR_FAST:
ifr_width = QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
break;
case SPI_NOR_DUAL:
ifr_width = QSPI_IFR_WIDTH_DUAL_OUTPUT;
break;
case SPI_NOR_QUAD:
ifr_width = QSPI_IFR_WIDTH_QUAD_OUTPUT;
break;
default:
return -EINVAL;
}
if (nor->read_dummy >= 2) {
num_mode_cycles = 2;
num_dummy_cycles = nor->read_dummy - 2;
} else {
num_mode_cycles = nor->read_dummy;
num_dummy_cycles = 0;
}
memset(&cmd, 0, sizeof(cmd));
cmd.enable.bits.instruction = 1;
cmd.enable.bits.address = nor->addr_width;
cmd.enable.bits.mode = (num_mode_cycles > 0);
cmd.enable.bits.dummy = (num_dummy_cycles > 0);
cmd.enable.bits.data = 1;
cmd.instruction = nor->read_opcode;
cmd.address = (u32)from;
cmd.mode = 0xff; /* This value prevents from entering the 0-4-4 mode */
cmd.num_mode_cycles = num_mode_cycles;
cmd.num_dummy_cycles = num_dummy_cycles;
cmd.rx_buf = read_buf;
cmd.buf_len = len;
ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ_MEM,
ifr_width);
return (ret < 0) ? ret : len;
}
static int atmel_qspi_init(struct atmel_qspi *aq)
{
unsigned long src_rate;
u32 mr, scr, scbr;
/* Reset the QSPI controller */
qspi_writel(aq, QSPI_CR, QSPI_CR_SWRST);
/* Set the QSPI controller in Serial Memory Mode */
mr = QSPI_MR_NBBITS(8) | QSPI_MR_SSM;
qspi_writel(aq, QSPI_MR, mr);
src_rate = clk_get_rate(aq->clk);
if (!src_rate)
return -EINVAL;
/* Compute the QSPI baudrate */
scbr = DIV_ROUND_UP(src_rate, aq->clk_rate);
if (scbr > 0)
scbr--;
scr = QSPI_SCR_SCBR(scbr);
qspi_writel(aq, QSPI_SCR, scr);
/* Enable the QSPI controller */
qspi_writel(aq, QSPI_CR, QSPI_CR_QSPIEN);
return 0;
}
static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)
{
struct atmel_qspi *aq = (struct atmel_qspi *)dev_id;
u32 status, mask, pending;
status = qspi_readl(aq, QSPI_SR);
mask = qspi_readl(aq, QSPI_IMR);
pending = status & mask;
if (!pending)
return IRQ_NONE;
aq->pending |= pending;
if ((aq->pending & QSPI_SR_CMD_COMPLETED) == QSPI_SR_CMD_COMPLETED)
complete(&aq->cmd_completion);
return IRQ_HANDLED;
}
static int atmel_qspi_probe(struct platform_device *pdev)
{
struct device_node *child, *np = pdev->dev.of_node;
struct atmel_qspi *aq;
struct resource *res;
struct spi_nor *nor;
struct mtd_info *mtd;
int irq, err = 0;
if (of_get_child_count(np) != 1)
return -ENODEV;
child = of_get_next_child(np, NULL);
aq = devm_kzalloc(&pdev->dev, sizeof(*aq), GFP_KERNEL);
if (!aq) {
err = -ENOMEM;
goto exit;
}
platform_set_drvdata(pdev, aq);
init_completion(&aq->cmd_completion);
aq->pdev = pdev;
/* Map the registers */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_base");
aq->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(aq->regs)) {
dev_err(&pdev->dev, "missing registers\n");
err = PTR_ERR(aq->regs);
goto exit;
}
/* Map the AHB memory */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_mmap");
aq->mem = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(aq->mem)) {
dev_err(&pdev->dev, "missing AHB memory\n");
err = PTR_ERR(aq->mem);
goto exit;
}
/* Get the peripheral clock */
aq->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(aq->clk)) {
dev_err(&pdev->dev, "missing peripheral clock\n");
err = PTR_ERR(aq->clk);
goto exit;
}
/* Enable the peripheral clock */
err = clk_prepare_enable(aq->clk);
if (err) {
dev_err(&pdev->dev, "failed to enable the peripheral clock\n");
goto exit;
}
/* Request the IRQ */
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "missing IRQ\n");
err = irq;
goto disable_clk;
}
err = devm_request_irq(&pdev->dev, irq, atmel_qspi_interrupt,
0, dev_name(&pdev->dev), aq);
if (err)
goto disable_clk;
/* Setup the spi-nor */
nor = &aq->nor;
mtd = &nor->mtd;
nor->dev = &pdev->dev;
spi_nor_set_flash_node(nor, child);
nor->priv = aq;
mtd->priv = nor;
nor->read_reg = atmel_qspi_read_reg;
nor->write_reg = atmel_qspi_write_reg;
nor->read = atmel_qspi_read;
nor->write = atmel_qspi_write;
nor->erase = atmel_qspi_erase;
err = of_property_read_u32(child, "spi-max-frequency", &aq->clk_rate);
if (err < 0)
goto disable_clk;
err = atmel_qspi_init(aq);
if (err)
goto disable_clk;
err = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
if (err)
goto disable_clk;
err = mtd_device_register(mtd, NULL, 0);
if (err)
goto disable_clk;
of_node_put(child);
return 0;
disable_clk:
clk_disable_unprepare(aq->clk);
exit:
of_node_put(child);
return err;
}
static int atmel_qspi_remove(struct platform_device *pdev)
{
struct atmel_qspi *aq = platform_get_drvdata(pdev);
mtd_device_unregister(&aq->nor.mtd);
qspi_writel(aq, QSPI_CR, QSPI_CR_QSPIDIS);
clk_disable_unprepare(aq->clk);
return 0;
}
static const struct of_device_id atmel_qspi_dt_ids[] = {
{ .compatible = "atmel,sama5d2-qspi" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, atmel_qspi_dt_ids);
static struct platform_driver atmel_qspi_driver = {
.driver = {
.name = "atmel_qspi",
.of_match_table = atmel_qspi_dt_ids,
},
.probe = atmel_qspi_probe,
.remove = atmel_qspi_remove,
};
module_platform_driver(atmel_qspi_driver);
MODULE_AUTHOR("Cyrille Pitchen <cyrille.pitchen@atmel.com>");
MODULE_DESCRIPTION("Atmel QSPI Controller driver");
MODULE_LICENSE("GPL v2");

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -618,9 +618,9 @@ static inline void fsl_qspi_invalid(struct fsl_qspi *q)
qspi_writel(q, reg, q->iobase + QUADSPI_MCR);
}
static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor,
static ssize_t fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor,
u8 opcode, unsigned int to, u32 *txbuf,
unsigned count, size_t *retlen)
unsigned count)
{
int ret, i, j;
u32 tmp;
@ -647,8 +647,8 @@ static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor,
/* Trigger it */
ret = fsl_qspi_runcmd(q, opcode, to, count);
if (ret == 0 && retlen)
*retlen += count;
if (ret == 0)
return count;
return ret;
}
@ -859,7 +859,9 @@ static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
} else if (len > 0) {
ret = fsl_qspi_nor_write(q, nor, opcode, 0,
(u32 *)buf, len, NULL);
(u32 *)buf, len);
if (ret > 0)
return 0;
} else {
dev_err(q->dev, "invalid cmd %d\n", opcode);
ret = -EINVAL;
@ -868,20 +870,20 @@ static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
return ret;
}
static void fsl_qspi_write(struct spi_nor *nor, loff_t to,
size_t len, size_t *retlen, const u_char *buf)
static ssize_t fsl_qspi_write(struct spi_nor *nor, loff_t to,
size_t len, const u_char *buf)
{
struct fsl_qspi *q = nor->priv;
fsl_qspi_nor_write(q, nor, nor->program_opcode, to,
(u32 *)buf, len, retlen);
ssize_t ret = fsl_qspi_nor_write(q, nor, nor->program_opcode, to,
(u32 *)buf, len);
/* invalid the data in the AHB buffer. */
fsl_qspi_invalid(q);
return ret;
}
static int fsl_qspi_read(struct spi_nor *nor, loff_t from,
size_t len, size_t *retlen, u_char *buf)
static ssize_t fsl_qspi_read(struct spi_nor *nor, loff_t from,
size_t len, u_char *buf)
{
struct fsl_qspi *q = nor->priv;
u8 cmd = nor->read_opcode;
@ -923,8 +925,7 @@ static int fsl_qspi_read(struct spi_nor *nor, loff_t from,
memcpy(buf, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs,
len);
*retlen += len;
return 0;
return len;
}
static int fsl_qspi_erase(struct spi_nor *nor, loff_t offs)

Просмотреть файл

@ -0,0 +1,489 @@
/*
* HiSilicon SPI Nor Flash Controller Driver
*
* Copyright (c) 2015-2016 HiSilicon Technologies Co., Ltd.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/spi-nor.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
/* Hardware register offsets and field definitions */
#define FMC_CFG 0x00
#define FMC_CFG_OP_MODE_MASK BIT_MASK(0)
#define FMC_CFG_OP_MODE_BOOT 0
#define FMC_CFG_OP_MODE_NORMAL 1
#define FMC_CFG_FLASH_SEL(type) (((type) & 0x3) << 1)
#define FMC_CFG_FLASH_SEL_MASK 0x6
#define FMC_ECC_TYPE(type) (((type) & 0x7) << 5)
#define FMC_ECC_TYPE_MASK GENMASK(7, 5)
#define SPI_NOR_ADDR_MODE_MASK BIT_MASK(10)
#define SPI_NOR_ADDR_MODE_3BYTES (0x0 << 10)
#define SPI_NOR_ADDR_MODE_4BYTES (0x1 << 10)
#define FMC_GLOBAL_CFG 0x04
#define FMC_GLOBAL_CFG_WP_ENABLE BIT(6)
#define FMC_SPI_TIMING_CFG 0x08
#define TIMING_CFG_TCSH(nr) (((nr) & 0xf) << 8)
#define TIMING_CFG_TCSS(nr) (((nr) & 0xf) << 4)
#define TIMING_CFG_TSHSL(nr) ((nr) & 0xf)
#define CS_HOLD_TIME 0x6
#define CS_SETUP_TIME 0x6
#define CS_DESELECT_TIME 0xf
#define FMC_INT 0x18
#define FMC_INT_OP_DONE BIT(0)
#define FMC_INT_CLR 0x20
#define FMC_CMD 0x24
#define FMC_CMD_CMD1(cmd) ((cmd) & 0xff)
#define FMC_ADDRL 0x2c
#define FMC_OP_CFG 0x30
#define OP_CFG_FM_CS(cs) ((cs) << 11)
#define OP_CFG_MEM_IF_TYPE(type) (((type) & 0x7) << 7)
#define OP_CFG_ADDR_NUM(addr) (((addr) & 0x7) << 4)
#define OP_CFG_DUMMY_NUM(dummy) ((dummy) & 0xf)
#define FMC_DATA_NUM 0x38
#define FMC_DATA_NUM_CNT(cnt) ((cnt) & GENMASK(13, 0))
#define FMC_OP 0x3c
#define FMC_OP_DUMMY_EN BIT(8)
#define FMC_OP_CMD1_EN BIT(7)
#define FMC_OP_ADDR_EN BIT(6)
#define FMC_OP_WRITE_DATA_EN BIT(5)
#define FMC_OP_READ_DATA_EN BIT(2)
#define FMC_OP_READ_STATUS_EN BIT(1)
#define FMC_OP_REG_OP_START BIT(0)
#define FMC_DMA_LEN 0x40
#define FMC_DMA_LEN_SET(len) ((len) & GENMASK(27, 0))
#define FMC_DMA_SADDR_D0 0x4c
#define HIFMC_DMA_MAX_LEN (4096)
#define HIFMC_DMA_MASK (HIFMC_DMA_MAX_LEN - 1)
#define FMC_OP_DMA 0x68
#define OP_CTRL_RD_OPCODE(code) (((code) & 0xff) << 16)
#define OP_CTRL_WR_OPCODE(code) (((code) & 0xff) << 8)
#define OP_CTRL_RW_OP(op) ((op) << 1)
#define OP_CTRL_DMA_OP_READY BIT(0)
#define FMC_OP_READ 0x0
#define FMC_OP_WRITE 0x1
#define FMC_WAIT_TIMEOUT 1000000
enum hifmc_iftype {
IF_TYPE_STD,
IF_TYPE_DUAL,
IF_TYPE_DIO,
IF_TYPE_QUAD,
IF_TYPE_QIO,
};
struct hifmc_priv {
u32 chipselect;
u32 clkrate;
struct hifmc_host *host;
};
#define HIFMC_MAX_CHIP_NUM 2
struct hifmc_host {
struct device *dev;
struct mutex lock;
void __iomem *regbase;
void __iomem *iobase;
struct clk *clk;
void *buffer;
dma_addr_t dma_buffer;
struct spi_nor *nor[HIFMC_MAX_CHIP_NUM];
u32 num_chip;
};
static inline int wait_op_finish(struct hifmc_host *host)
{
u32 reg;
return readl_poll_timeout(host->regbase + FMC_INT, reg,
(reg & FMC_INT_OP_DONE), 0, FMC_WAIT_TIMEOUT);
}
static int get_if_type(enum read_mode flash_read)
{
enum hifmc_iftype if_type;
switch (flash_read) {
case SPI_NOR_DUAL:
if_type = IF_TYPE_DUAL;
break;
case SPI_NOR_QUAD:
if_type = IF_TYPE_QUAD;
break;
case SPI_NOR_NORMAL:
case SPI_NOR_FAST:
default:
if_type = IF_TYPE_STD;
break;
}
return if_type;
}
static void hisi_spi_nor_init(struct hifmc_host *host)
{
u32 reg;
reg = TIMING_CFG_TCSH(CS_HOLD_TIME)
| TIMING_CFG_TCSS(CS_SETUP_TIME)
| TIMING_CFG_TSHSL(CS_DESELECT_TIME);
writel(reg, host->regbase + FMC_SPI_TIMING_CFG);
}
static int hisi_spi_nor_prep(struct spi_nor *nor, enum spi_nor_ops ops)
{
struct hifmc_priv *priv = nor->priv;
struct hifmc_host *host = priv->host;
int ret;
mutex_lock(&host->lock);
ret = clk_set_rate(host->clk, priv->clkrate);
if (ret)
goto out;
ret = clk_prepare_enable(host->clk);
if (ret)
goto out;
return 0;
out:
mutex_unlock(&host->lock);
return ret;
}
static void hisi_spi_nor_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
{
struct hifmc_priv *priv = nor->priv;
struct hifmc_host *host = priv->host;
clk_disable_unprepare(host->clk);
mutex_unlock(&host->lock);
}
static int hisi_spi_nor_op_reg(struct spi_nor *nor,
u8 opcode, int len, u8 optype)
{
struct hifmc_priv *priv = nor->priv;
struct hifmc_host *host = priv->host;
u32 reg;
reg = FMC_CMD_CMD1(opcode);
writel(reg, host->regbase + FMC_CMD);
reg = FMC_DATA_NUM_CNT(len);
writel(reg, host->regbase + FMC_DATA_NUM);
reg = OP_CFG_FM_CS(priv->chipselect);
writel(reg, host->regbase + FMC_OP_CFG);
writel(0xff, host->regbase + FMC_INT_CLR);
reg = FMC_OP_CMD1_EN | FMC_OP_REG_OP_START | optype;
writel(reg, host->regbase + FMC_OP);
return wait_op_finish(host);
}
static int hisi_spi_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
int len)
{
struct hifmc_priv *priv = nor->priv;
struct hifmc_host *host = priv->host;
int ret;
ret = hisi_spi_nor_op_reg(nor, opcode, len, FMC_OP_READ_DATA_EN);
if (ret)
return ret;
memcpy_fromio(buf, host->iobase, len);
return 0;
}
static int hisi_spi_nor_write_reg(struct spi_nor *nor, u8 opcode,
u8 *buf, int len)
{
struct hifmc_priv *priv = nor->priv;
struct hifmc_host *host = priv->host;
if (len)
memcpy_toio(host->iobase, buf, len);
return hisi_spi_nor_op_reg(nor, opcode, len, FMC_OP_WRITE_DATA_EN);
}
static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off,
dma_addr_t dma_buf, size_t len, u8 op_type)
{
struct hifmc_priv *priv = nor->priv;
struct hifmc_host *host = priv->host;
u8 if_type = 0;
u32 reg;
reg = readl(host->regbase + FMC_CFG);
reg &= ~(FMC_CFG_OP_MODE_MASK | SPI_NOR_ADDR_MODE_MASK);
reg |= FMC_CFG_OP_MODE_NORMAL;
reg |= (nor->addr_width == 4) ? SPI_NOR_ADDR_MODE_4BYTES
: SPI_NOR_ADDR_MODE_3BYTES;
writel(reg, host->regbase + FMC_CFG);
writel(start_off, host->regbase + FMC_ADDRL);
writel(dma_buf, host->regbase + FMC_DMA_SADDR_D0);
writel(FMC_DMA_LEN_SET(len), host->regbase + FMC_DMA_LEN);
reg = OP_CFG_FM_CS(priv->chipselect);
if_type = get_if_type(nor->flash_read);
reg |= OP_CFG_MEM_IF_TYPE(if_type);
if (op_type == FMC_OP_READ)
reg |= OP_CFG_DUMMY_NUM(nor->read_dummy >> 3);
writel(reg, host->regbase + FMC_OP_CFG);
writel(0xff, host->regbase + FMC_INT_CLR);
reg = OP_CTRL_RW_OP(op_type) | OP_CTRL_DMA_OP_READY;
reg |= (op_type == FMC_OP_READ)
? OP_CTRL_RD_OPCODE(nor->read_opcode)
: OP_CTRL_WR_OPCODE(nor->program_opcode);
writel(reg, host->regbase + FMC_OP_DMA);
return wait_op_finish(host);
}
static ssize_t hisi_spi_nor_read(struct spi_nor *nor, loff_t from, size_t len,
u_char *read_buf)
{
struct hifmc_priv *priv = nor->priv;
struct hifmc_host *host = priv->host;
size_t offset;
int ret;
for (offset = 0; offset < len; offset += HIFMC_DMA_MAX_LEN) {
size_t trans = min_t(size_t, HIFMC_DMA_MAX_LEN, len - offset);
ret = hisi_spi_nor_dma_transfer(nor,
from + offset, host->dma_buffer, trans, FMC_OP_READ);
if (ret) {
dev_warn(nor->dev, "DMA read timeout\n");
return ret;
}
memcpy(read_buf + offset, host->buffer, trans);
}
return len;
}
static ssize_t hisi_spi_nor_write(struct spi_nor *nor, loff_t to,
size_t len, const u_char *write_buf)
{
struct hifmc_priv *priv = nor->priv;
struct hifmc_host *host = priv->host;
size_t offset;
int ret;
for (offset = 0; offset < len; offset += HIFMC_DMA_MAX_LEN) {
size_t trans = min_t(size_t, HIFMC_DMA_MAX_LEN, len - offset);
memcpy(host->buffer, write_buf + offset, trans);
ret = hisi_spi_nor_dma_transfer(nor,
to + offset, host->dma_buffer, trans, FMC_OP_WRITE);
if (ret) {
dev_warn(nor->dev, "DMA write timeout\n");
return ret;
}
}
return len;
}
/**
* Get spi flash device information and register it as a mtd device.
*/
static int hisi_spi_nor_register(struct device_node *np,
struct hifmc_host *host)
{
struct device *dev = host->dev;
struct spi_nor *nor;
struct hifmc_priv *priv;
struct mtd_info *mtd;
int ret;
nor = devm_kzalloc(dev, sizeof(*nor), GFP_KERNEL);
if (!nor)
return -ENOMEM;
nor->dev = dev;
spi_nor_set_flash_node(nor, np);
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
ret = of_property_read_u32(np, "reg", &priv->chipselect);
if (ret) {
dev_err(dev, "There's no reg property for %s\n",
np->full_name);
return ret;
}
ret = of_property_read_u32(np, "spi-max-frequency",
&priv->clkrate);
if (ret) {
dev_err(dev, "There's no spi-max-frequency property for %s\n",
np->full_name);
return ret;
}
priv->host = host;
nor->priv = priv;
nor->prepare = hisi_spi_nor_prep;
nor->unprepare = hisi_spi_nor_unprep;
nor->read_reg = hisi_spi_nor_read_reg;
nor->write_reg = hisi_spi_nor_write_reg;
nor->read = hisi_spi_nor_read;
nor->write = hisi_spi_nor_write;
nor->erase = NULL;
ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
if (ret)
return ret;
mtd = &nor->mtd;
mtd->name = np->name;
ret = mtd_device_register(mtd, NULL, 0);
if (ret)
return ret;
host->nor[host->num_chip] = nor;
host->num_chip++;
return 0;
}
static void hisi_spi_nor_unregister_all(struct hifmc_host *host)
{
int i;
for (i = 0; i < host->num_chip; i++)
mtd_device_unregister(&host->nor[i]->mtd);
}
static int hisi_spi_nor_register_all(struct hifmc_host *host)
{
struct device *dev = host->dev;
struct device_node *np;
int ret;
for_each_available_child_of_node(dev->of_node, np) {
ret = hisi_spi_nor_register(np, host);
if (ret)
goto fail;
if (host->num_chip == HIFMC_MAX_CHIP_NUM) {
dev_warn(dev, "Flash device number exceeds the maximum chipselect number\n");
break;
}
}
return 0;
fail:
hisi_spi_nor_unregister_all(host);
return ret;
}
static int hisi_spi_nor_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct hifmc_host *host;
int ret;
host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
if (!host)
return -ENOMEM;
platform_set_drvdata(pdev, host);
host->dev = dev;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control");
host->regbase = devm_ioremap_resource(dev, res);
if (IS_ERR(host->regbase))
return PTR_ERR(host->regbase);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "memory");
host->iobase = devm_ioremap_resource(dev, res);
if (IS_ERR(host->iobase))
return PTR_ERR(host->iobase);
host->clk = devm_clk_get(dev, NULL);
if (IS_ERR(host->clk))
return PTR_ERR(host->clk);
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (ret) {
dev_warn(dev, "Unable to set dma mask\n");
return ret;
}
host->buffer = dmam_alloc_coherent(dev, HIFMC_DMA_MAX_LEN,
&host->dma_buffer, GFP_KERNEL);
if (!host->buffer)
return -ENOMEM;
mutex_init(&host->lock);
clk_prepare_enable(host->clk);
hisi_spi_nor_init(host);
ret = hisi_spi_nor_register_all(host);
if (ret)
mutex_destroy(&host->lock);
clk_disable_unprepare(host->clk);
return ret;
}
static int hisi_spi_nor_remove(struct platform_device *pdev)
{
struct hifmc_host *host = platform_get_drvdata(pdev);
hisi_spi_nor_unregister_all(host);
mutex_destroy(&host->lock);
clk_disable_unprepare(host->clk);
return 0;
}
static const struct of_device_id hisi_spi_nor_dt_ids[] = {
{ .compatible = "hisilicon,fmc-spi-nor"},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, hisi_spi_nor_dt_ids);
static struct platform_driver hisi_spi_nor_driver = {
.driver = {
.name = "hisi-sfc",
.of_match_table = hisi_spi_nor_dt_ids,
},
.probe = hisi_spi_nor_probe,
.remove = hisi_spi_nor_remove,
};
module_platform_driver(hisi_spi_nor_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("HiSilicon SPI Nor Flash Controller Driver");

Просмотреть файл

@ -21,7 +21,6 @@
#include <linux/ioport.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_device.h>
@ -243,8 +242,8 @@ static void mt8173_nor_set_addr(struct mt8173_nor *mt8173_nor, u32 addr)
writeb(addr & 0xff, mt8173_nor->base + MTK_NOR_RADR3_REG);
}
static int mt8173_nor_read(struct spi_nor *nor, loff_t from, size_t length,
size_t *retlen, u_char *buffer)
static ssize_t mt8173_nor_read(struct spi_nor *nor, loff_t from, size_t length,
u_char *buffer)
{
int i, ret;
int addr = (int)from;
@ -255,13 +254,13 @@ static int mt8173_nor_read(struct spi_nor *nor, loff_t from, size_t length,
mt8173_nor_set_read_mode(mt8173_nor);
mt8173_nor_set_addr(mt8173_nor, addr);
for (i = 0; i < length; i++, (*retlen)++) {
for (i = 0; i < length; i++) {
ret = mt8173_nor_execute_cmd(mt8173_nor, MTK_NOR_PIO_READ_CMD);
if (ret < 0)
return ret;
buf[i] = readb(mt8173_nor->base + MTK_NOR_RDATA_REG);
}
return 0;
return length;
}
static int mt8173_nor_write_single_byte(struct mt8173_nor *mt8173_nor,
@ -297,36 +296,44 @@ static int mt8173_nor_write_buffer(struct mt8173_nor *mt8173_nor, int addr,
return mt8173_nor_execute_cmd(mt8173_nor, MTK_NOR_WR_CMD);
}
static void mt8173_nor_write(struct spi_nor *nor, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
static ssize_t mt8173_nor_write(struct spi_nor *nor, loff_t to, size_t len,
const u_char *buf)
{
int ret;
struct mt8173_nor *mt8173_nor = nor->priv;
size_t i;
ret = mt8173_nor_write_buffer_enable(mt8173_nor);
if (ret < 0)
if (ret < 0) {
dev_warn(mt8173_nor->dev, "write buffer enable failed!\n");
return ret;
}
while (len >= SFLASH_WRBUF_SIZE) {
for (i = 0; i + SFLASH_WRBUF_SIZE <= len; i += SFLASH_WRBUF_SIZE) {
ret = mt8173_nor_write_buffer(mt8173_nor, to, buf);
if (ret < 0)
if (ret < 0) {
dev_err(mt8173_nor->dev, "write buffer failed!\n");
len -= SFLASH_WRBUF_SIZE;
return ret;
}
to += SFLASH_WRBUF_SIZE;
buf += SFLASH_WRBUF_SIZE;
(*retlen) += SFLASH_WRBUF_SIZE;
}
ret = mt8173_nor_write_buffer_disable(mt8173_nor);
if (ret < 0)
if (ret < 0) {
dev_warn(mt8173_nor->dev, "write buffer disable failed!\n");
if (len) {
ret = mt8173_nor_write_single_byte(mt8173_nor, to, (int)len,
(u8 *)buf);
if (ret < 0)
dev_err(mt8173_nor->dev, "write single byte failed!\n");
(*retlen) += len;
return ret;
}
if (i < len) {
ret = mt8173_nor_write_single_byte(mt8173_nor, to,
(int)(len - i), (u8 *)buf);
if (ret < 0) {
dev_err(mt8173_nor->dev, "write single byte failed!\n");
return ret;
}
}
return len;
}
static int mt8173_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)

Просмотреть файл

@ -172,8 +172,8 @@ static int nxp_spifi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
return nxp_spifi_wait_for_cmd(spifi);
}
static int nxp_spifi_read(struct spi_nor *nor, loff_t from, size_t len,
size_t *retlen, u_char *buf)
static ssize_t nxp_spifi_read(struct spi_nor *nor, loff_t from, size_t len,
u_char *buf)
{
struct nxp_spifi *spifi = nor->priv;
int ret;
@ -183,24 +183,23 @@ static int nxp_spifi_read(struct spi_nor *nor, loff_t from, size_t len,
return ret;
memcpy_fromio(buf, spifi->flash_base + from, len);
*retlen += len;
return 0;
return len;
}
static void nxp_spifi_write(struct spi_nor *nor, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
static ssize_t nxp_spifi_write(struct spi_nor *nor, loff_t to, size_t len,
const u_char *buf)
{
struct nxp_spifi *spifi = nor->priv;
u32 cmd;
int ret;
size_t i;
ret = nxp_spifi_set_memory_mode_off(spifi);
if (ret)
return;
return ret;
writel(to, spifi->io_base + SPIFI_ADDR);
*retlen += len;
cmd = SPIFI_CMD_DOUT |
SPIFI_CMD_DATALEN(len) |
@ -209,10 +208,14 @@ static void nxp_spifi_write(struct spi_nor *nor, loff_t to, size_t len,
SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1);
writel(cmd, spifi->io_base + SPIFI_CMD);
while (len--)
writeb(*buf++, spifi->io_base + SPIFI_DATA);
for (i = 0; i < len; i++)
writeb(buf[i], spifi->io_base + SPIFI_DATA);
nxp_spifi_wait_for_cmd(spifi);
ret = nxp_spifi_wait_for_cmd(spifi);
if (ret)
return ret;
return len;
}
static int nxp_spifi_erase(struct spi_nor *nor, loff_t offs)

Просмотреть файл

@ -661,7 +661,7 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
status_new = (status_old & ~mask & ~SR_TB) | val;
/* Don't protect status register if we're fully unlocked */
if (lock_len == mtd->size)
if (lock_len == 0)
status_new &= ~SR_SRWD;
if (!use_top)
@ -830,10 +830,26 @@ static const struct flash_info spi_nor_ids[] = {
{ "mb85rs1mt", INFO(0x047f27, 0, 128 * 1024, 1, SPI_NOR_NO_ERASE) },
/* GigaDevice */
{ "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) },
{ "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) },
{ "gd25lq64c", INFO(0xc86017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "gd25q128", INFO(0xc84018, 0, 64 * 1024, 256, SECT_4K) },
{
"gd25q32", INFO(0xc84016, 0, 64 * 1024, 64,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
},
{
"gd25q64", INFO(0xc84017, 0, 64 * 1024, 128,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
},
{
"gd25lq64c", INFO(0xc86017, 0, 64 * 1024, 128,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
},
{
"gd25q128", INFO(0xc84018, 0, 64 * 1024, 256,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
},
/* Intel/Numonyx -- xxxs33b */
{ "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) },
@ -871,6 +887,7 @@ static const struct flash_info spi_nor_ids[] = {
{ "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
{ "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
{ "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
{ "n25q00a", INFO(0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
/* PMC */
{ "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) },
@ -1031,8 +1048,25 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
if (ret)
return ret;
ret = nor->read(nor, from, len, retlen, buf);
while (len) {
ret = nor->read(nor, from, len, buf);
if (ret == 0) {
/* We shouldn't see 0-length reads */
ret = -EIO;
goto read_err;
}
if (ret < 0)
goto read_err;
WARN_ON(ret > len);
*retlen += ret;
buf += ret;
from += ret;
len -= ret;
}
ret = 0;
read_err:
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ);
return ret;
}
@ -1060,10 +1094,14 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
nor->program_opcode = SPINOR_OP_BP;
/* write one byte. */
nor->write(nor, to, 1, retlen, buf);
ret = nor->write(nor, to, 1, buf);
if (ret < 0)
goto sst_write_err;
WARN(ret != 1, "While writing 1 byte written %i bytes\n",
(int)ret);
ret = spi_nor_wait_till_ready(nor);
if (ret)
goto time_out;
goto sst_write_err;
}
to += actual;
@ -1072,10 +1110,14 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
nor->program_opcode = SPINOR_OP_AAI_WP;
/* write two bytes. */
nor->write(nor, to, 2, retlen, buf + actual);
ret = nor->write(nor, to, 2, buf + actual);
if (ret < 0)
goto sst_write_err;
WARN(ret != 2, "While writing 2 bytes written %i bytes\n",
(int)ret);
ret = spi_nor_wait_till_ready(nor);
if (ret)
goto time_out;
goto sst_write_err;
to += 2;
nor->sst_write_second = true;
}
@ -1084,21 +1126,26 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
write_disable(nor);
ret = spi_nor_wait_till_ready(nor);
if (ret)
goto time_out;
goto sst_write_err;
/* Write out trailing byte if it exists. */
if (actual != len) {
write_enable(nor);
nor->program_opcode = SPINOR_OP_BP;
nor->write(nor, to, 1, retlen, buf + actual);
ret = nor->write(nor, to, 1, buf + actual);
if (ret < 0)
goto sst_write_err;
WARN(ret != 1, "While writing 1 byte written %i bytes\n",
(int)ret);
ret = spi_nor_wait_till_ready(nor);
if (ret)
goto time_out;
goto sst_write_err;
write_disable(nor);
actual += 1;
}
time_out:
sst_write_err:
*retlen += actual;
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
return ret;
}
@ -1112,8 +1159,8 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct spi_nor *nor = mtd_to_spi_nor(mtd);
u32 page_offset, page_size, i;
int ret;
size_t page_offset, page_remain, i;
ssize_t ret;
dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
@ -1121,35 +1168,37 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
if (ret)
return ret;
write_enable(nor);
for (i = 0; i < len; ) {
ssize_t written;
page_offset = to & (nor->page_size - 1);
/* do all the bytes fit onto one page? */
if (page_offset + len <= nor->page_size) {
nor->write(nor, to, len, retlen, buf);
} else {
page_offset = (to + i) & (nor->page_size - 1);
WARN_ONCE(page_offset,
"Writing at offset %zu into a NOR page. Writing partial pages may decrease reliability and increase wear of NOR flash.",
page_offset);
/* the size of data remaining on the first page */
page_size = nor->page_size - page_offset;
nor->write(nor, to, page_size, retlen, buf);
page_remain = min_t(size_t,
nor->page_size - page_offset, len - i);
/* write everything in nor->page_size chunks */
for (i = page_size; i < len; i += page_size) {
page_size = len - i;
if (page_size > nor->page_size)
page_size = nor->page_size;
write_enable(nor);
ret = nor->write(nor, to + i, page_remain, buf + i);
if (ret < 0)
goto write_err;
written = ret;
ret = spi_nor_wait_till_ready(nor);
if (ret)
goto write_err;
write_enable(nor);
nor->write(nor, to + i, page_size, retlen, buf + i);
ret = spi_nor_wait_till_ready(nor);
if (ret)
goto write_err;
*retlen += written;
i += written;
if (written != page_remain) {
dev_err(nor->dev,
"While writing %zu bytes written %zd bytes\n",
page_remain, written);
ret = -EIO;
goto write_err;
}
}
ret = spi_nor_wait_till_ready(nor);
write_err:
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
return ret;

Просмотреть файл

@ -380,8 +380,7 @@ static int ssfdcr_readsect(struct mtd_blktrans_dev *dev,
" block_addr=%d\n", logic_sect_no, sectors_per_block, offset,
block_address);
if (block_address >= ssfdc->map_len)
BUG();
BUG_ON(block_address >= ssfdc->map_len);
block_address = ssfdc->logic_block_map[block_address];

Просмотреть файл

@ -290,7 +290,7 @@ static int overwrite_test(void)
while (opno < max_overwrite) {
err = rewrite_page(0);
err = write_page(0);
if (err)
break;

Просмотреть файл

@ -783,6 +783,7 @@ static inline void nand_set_controller_data(struct nand_chip *chip, void *priv)
* NAND Flash Manufacturer ID Codes
*/
#define NAND_MFR_TOSHIBA 0x98
#define NAND_MFR_ESMT 0xc8
#define NAND_MFR_SAMSUNG 0xec
#define NAND_MFR_FUJITSU 0x04
#define NAND_MFR_NATIONAL 0x8f

Просмотреть файл

@ -173,10 +173,10 @@ struct spi_nor {
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 (*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);
ssize_t (*read)(struct spi_nor *nor, loff_t from,
size_t len, u_char *read_buf);
ssize_t (*write)(struct spi_nor *nor, loff_t to,
size_t len, const u_char *write_buf);
int (*erase)(struct spi_nor *nor, loff_t offs);
int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);