The main series brought is an update of the Broadcom support to support
 all BCMBCA SoCs and their specificity (ECC, write protection,
 configuration straps), plus a few misc fixes and changes in the main
 driver. Device tree updates are also part of this PR, initially because
 of a misunderstanding on my side.
 
 The STM32_FMC2 controller driver is also upgraded to properly support
 MP1 and MP25 SoCs.
 
 A new compatible is added for an Atmel flavor.
 
 Among all these feature changes, there is as well a load of continuous
 read related fixes, avoiding more corner conditions and clarifying the
 logic. Finally a few miscellaneous fixes are made to the core, the
 lpx32xx_mlc, fsl_lbc, Meson and Atmel controller driver, as well as
 final one in the Hynix vendor driver.
 
 SPI-NAND
 
 The ESMT support has been extended to match 5 bytes ID to avoid
 collisions. Winbond support on its side receives support for W25N04KV
 chips.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAmXzgv4ACgkQJWrqGEe9
 VoTwUAgAqwzuaAbhMuHABS2IJnn9KZHCo4f2goXhtrtRP2oDSgzsT8Yke2vfJTIx
 KBtjWRYdp5n8GRSmDmO1n/oNnKdjkzbOhP0ow8aZqhKYtjQ7O0xxd4gxQu3gCoVz
 ypWq3tpMkKH0MyD8d+HulFhldMmyGv5BqXeioLi2azeALCbpgVcGAHaMxUlRB/p3
 iM3GtBr9hUbai6HlynnPsiAAmyUc1gvGGHJ8t2ShjaW5LzK7uBqJLhSVizfP8I6g
 N6l0C+KKwFmXlprtgMcg/HjW2QWKOyeqrhmgTzcJIHRiPjySnmfOVeHBLu6y+1wl
 MC3BC5kHNSmOfuU76/93LUQUwkWvUA==
 =TaNQ
 -----END PGP SIGNATURE-----

Merge tag 'nand/for-6.9' into mtd/next

Raw NAND

The main series brought is an update of the Broadcom support to support
all BCMBCA SoCs and their specificity (ECC, write protection,
configuration straps), plus a few misc fixes and changes in the main
driver. Device tree updates are also part of this PR, initially because
of a misunderstanding on my side.

The STM32_FMC2 controller driver is also upgraded to properly support
MP1 and MP25 SoCs.

A new compatible is added for an Atmel flavor.

Among all these feature changes, there is as well a load of continuous
read related fixes, avoiding more corner conditions and clarifying the
logic. Finally a few miscellaneous fixes are made to the core, the
lpx32xx_mlc, fsl_lbc, Meson and Atmel controller driver, as well as
final one in the Hynix vendor driver.

SPI-NAND

The ESMT support has been extended to match 5 bytes ID to avoid
collisions. Winbond support on its side receives support for W25N04KV
chips.
This commit is contained in:
Miquel Raynal 2024-03-15 12:00:45 +01:00
commit 09888e973c
54 changed files with 831 additions and 201 deletions

View File

@ -56,6 +56,7 @@ Required properties:
"atmel,sama5d4-pmecc"
"atmel,sama5d2-pmecc"
"microchip,sam9x60-pmecc"
"microchip,sam9x7-pmecc", "atmel,at91sam9g45-pmecc"
- reg: should contain 2 register ranges. The first one is pointing to the PMECC
block, and the second one to the PMECC_ERRLOC block.

View File

@ -9,6 +9,7 @@ title: Broadcom STB NAND Controller
maintainers:
- Brian Norris <computersforpeace@gmail.com>
- Kamal Dasu <kdasu.kdev@gmail.com>
- William Zhang <william.zhang@broadcom.com>
description: |
The Broadcom Set-Top Box NAND controller supports low-level access to raw NAND
@ -18,9 +19,10 @@ description: |
supports basic PROGRAM and READ functions, among other features.
This controller was originally designed for STB SoCs (BCM7xxx) but is now
available on a variety of Broadcom SoCs, including some BCM3xxx, BCM63xx, and
iProc/Cygnus. Its history includes several similar (but not fully register
compatible) versions.
available on a variety of Broadcom SoCs, including some BCM3xxx, MIPS based
Broadband SoC (BCM63xx), ARM based Broadband SoC (BCMBCA) and iProc/Cygnus.
Its history includes several similar (but not fully register compatible)
versions.
-- Additional SoC-specific NAND controller properties --
@ -53,7 +55,7 @@ properties:
- brcm,brcmnand-v7.2
- brcm,brcmnand-v7.3
- const: brcm,brcmnand
- description: BCM63138 SoC-specific NAND controller
- description: BCMBCA SoC-specific NAND controller
items:
- const: brcm,nand-bcm63138
- enum:
@ -111,6 +113,13 @@ properties:
earlier versions of this core that include WP
type: boolean
brcm,wp-not-connected:
description:
Use this property when WP pin is not physically wired to the NAND chip.
Write protection feature cannot be used. By default, controller assumes
the pin is connected and feature is used.
$ref: /schemas/types.yaml#/definitions/flag
patternProperties:
"^nand@[a-f0-9]$":
type: object
@ -137,6 +146,15 @@ patternProperties:
layout.
$ref: /schemas/types.yaml#/definitions/uint32
brcm,nand-ecc-use-strap:
description:
This property requires the host system to get the ECC related
settings from the SoC NAND boot strap configuration instead of
the generic NAND ECC settings. This is a common hardware design
on BCMBCA based boards. This strap ECC option and generic NAND
ECC option can not be specified at the same time.
$ref: /schemas/types.yaml#/definitions/flag
unevaluatedProperties: false
allOf:
@ -177,6 +195,8 @@ allOf:
- const: iproc-idm
- const: iproc-ext
- if:
required:
- interrupts
properties:
interrupts:
minItems: 2
@ -184,12 +204,26 @@ allOf:
required:
- interrupt-names
- if:
patternProperties:
"^nand@[a-f0-9]$":
required:
- brcm,nand-ecc-use-strap
then:
patternProperties:
"^nand@[a-f0-9]$":
properties:
nand-ecc-strength: false
nand-ecc-step-size: false
nand-ecc-maximize: false
nand-ecc-algo: false
brcm,nand-oob-sector-size: false
unevaluatedProperties: false
required:
- reg
- reg-names
- interrupts
examples:
- |

View File

@ -14,10 +14,11 @@ properties:
enum:
- st,stm32mp15-fmc2
- st,stm32mp1-fmc2-nfc
- st,stm32mp25-fmc2-nfc
reg:
minItems: 6
maxItems: 7
maxItems: 12
interrupts:
maxItems: 1
@ -92,6 +93,28 @@ allOf:
- description: Chip select 1 command
- description: Chip select 1 address space
- if:
properties:
compatible:
contains:
const: st,stm32mp25-fmc2-nfc
then:
properties:
reg:
items:
- description: Chip select 0 data
- description: Chip select 0 command
- description: Chip select 0 address space
- description: Chip select 1 data
- description: Chip select 1 command
- description: Chip select 1 address space
- description: Chip select 2 data
- description: Chip select 2 command
- description: Chip select 2 address space
- description: Chip select 3 data
- description: Chip select 3 command
- description: Chip select 3 address space
required:
- compatible
- reg

View File

@ -138,6 +138,20 @@
status = "disabled";
};
nand_controller: nand-controller@1800 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
reg = <0x1800 0x600>, <0x2000 0x10>;
reg-names = "nand", "nand-int-base";
status = "disabled";
nandcs: nand@0 {
compatible = "brcm,nandcs";
reg = <0>;
};
};
uart0: serial@12000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x12000 0x1000>;

View File

@ -229,7 +229,12 @@
reg-names = "nand", "nand-int-base";
status = "disabled";
interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "nand";
interrupt-names = "nand_ctlrdy";
nandcs: nand@0 {
compatible = "brcm,nandcs";
reg = <0>;
};
};
serial@4400 {

View File

@ -119,5 +119,19 @@
num-cs = <8>;
status = "disabled";
};
nand_controller: nand-controller@2000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
reg = <0x2000 0x600>, <0xf0 0x10>;
reg-names = "nand", "nand-int-base";
status = "disabled";
nandcs: nand@0 {
compatible = "brcm,nandcs";
reg = <0>;
};
};
};
};

View File

@ -129,6 +129,20 @@
status = "disabled";
};
nand_controller: nand-controller@1800 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
reg = <0x1800 0x600>, <0x2000 0x10>;
reg-names = "nand", "nand-int-base";
status = "disabled";
nandcs: nand@0 {
compatible = "brcm,nandcs";
reg = <0>;
};
};
uart0: serial@12000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x12000 0x1000>;

View File

@ -139,6 +139,20 @@
status = "disabled";
};
nand_controller: nand-controller@1800 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
reg = <0x1800 0x600>, <0x2000 0x10>;
reg-names = "nand", "nand-int-base";
status = "disabled";
nandcs: nand@0 {
compatible = "brcm,nandcs";
reg = <0>;
};
};
uart0: serial@12000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x12000 0x1000>;

View File

@ -119,5 +119,19 @@
num-cs = <8>;
status = "disabled";
};
nand_controller: nand-controller@1800 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
reg = <0x1800 0x600>, <0x2000 0x10>;
reg-names = "nand", "nand-int-base";
status = "disabled";
nandcs: nand@0 {
compatible = "brcm,nandcs";
reg = <0>;
};
};
};
};

View File

@ -129,6 +129,20 @@
status = "disabled";
};
nand_controller: nand-controller@1800 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
reg = <0x1800 0x600>, <0x2000 0x10>;
reg-names = "nand", "nand-int-base";
status = "disabled";
nandcs: nand@0 {
compatible = "brcm,nandcs";
reg = <0>;
};
};
uart0: serial@12000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x12000 0x1000>;

View File

@ -120,6 +120,20 @@
status = "disabled";
};
nand_controller: nand-controller@1800 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
reg = <0x1800 0x600>, <0x2000 0x10>;
reg-names = "nand", "nand-int-base";
status = "disabled";
nandcs: nand@0 {
compatible = "brcm,nandcs";
reg = <0>;
};
};
uart0: serial@12000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x12000 0x1000>;

View File

@ -32,3 +32,13 @@
&hsspi {
status = "okay";
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-on-flash-bbt;
brcm,nand-ecc-use-strap;
};

View File

@ -29,3 +29,13 @@
&hsspi {
status = "okay";
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-on-flash-bbt;
brcm,nand-ecc-use-strap;
};

View File

@ -32,15 +32,15 @@
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
nand@0 {
compatible = "brcm,nandcs";
reg = <0>;
nand-ecc-strength = <4>;
nand-ecc-step-size = <512>;
brcm,nand-oob-sectors-size = <16>;
};
&nandcs {
nand-ecc-strength = <4>;
nand-ecc-step-size = <512>;
brcm,nand-oob-sector-size = <16>;
nand-on-flash-bbt;
};
&ahci {

View File

@ -32,3 +32,13 @@
&hsspi {
status = "okay";
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-on-flash-bbt;
brcm,nand-ecc-use-strap;
};

View File

@ -32,3 +32,13 @@
&hsspi {
status = "okay";
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-on-flash-bbt;
brcm,nand-ecc-use-strap;
};

View File

@ -32,3 +32,13 @@
&hsspi {
status = "okay";
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-on-flash-bbt;
brcm,nand-ecc-use-strap;
};

View File

@ -32,3 +32,13 @@
&hsspi {
status = "okay";
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-on-flash-bbt;
brcm,nand-ecc-use-strap;
};

View File

@ -32,3 +32,13 @@
&hsspi {
status = "okay";
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-on-flash-bbt;
brcm,nand-ecc-use-strap;
};

View File

@ -32,3 +32,13 @@
&hsspi {
status = "okay";
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-on-flash-bbt;
brcm,nand-ecc-use-strap;
};

View File

@ -125,6 +125,11 @@
};
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-ecc-strength = <4>;
nand-ecc-step-size = <512>;

View File

@ -155,6 +155,11 @@
};
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-ecc-strength = <4>;
nand-ecc-step-size = <512>;

View File

@ -166,11 +166,15 @@
};
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-ecc-strength = <4>;
nand-ecc-step-size = <512>;
nand-on-flash-bbt;
brcm,nand-has-wp;
#address-cells = <1>;
#size-cells = <0>;

View File

@ -589,7 +589,7 @@
status = "disabled";
};
nand-controller@1800 {
nand_controller: nand-controller@1800 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
@ -597,7 +597,7 @@
reg-names = "nand", "nand-int-base";
interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "nand_ctlrdy";
status = "okay";
status = "disabled";
nandcs: nand@0 {
compatible = "brcm,nandcs";

View File

@ -138,6 +138,20 @@
status = "disabled";
};
nand_controller: nand-controller@1800 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
reg = <0x1800 0x600>, <0x2000 0x10>;
reg-names = "nand", "nand-int-base";
status = "disabled";
nandcs: nand@0 {
compatible = "brcm,nandcs";
reg = <0>;
};
};
uart0: serial@12000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x12000 0x1000>;

View File

@ -119,6 +119,20 @@
status = "disabled";
};
nand_controller: nand-controller@1800 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
reg = <0x1800 0x600>, <0x2000 0x10>;
reg-names = "nand", "nand-int-base";
status = "disabled";
nandcs: nand@0 {
compatible = "brcm,nandcs";
reg = <0>;
};
};
uart0: serial@12000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x12000 0x1000>;

View File

@ -137,6 +137,20 @@
status = "disabled";
};
nand_controller: nand-controller@1800 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
reg = <0x1800 0x600>, <0x2000 0x10>;
reg-names = "nand", "nand-int-base";
status = "disabled";
nandcs: nand@0 {
compatible = "brcm,nandcs";
reg = <0>;
};
};
uart0: serial@12000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x12000 0x1000>;

View File

@ -138,6 +138,20 @@
status = "disabled";
};
nand_controller: nand-controller@1800 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
reg = <0x1800 0x600>, <0x2000 0x10>;
reg-names = "nand", "nand-int-base";
status = "disabled";
nandcs: nand@0 {
compatible = "brcm,nandcs";
reg = <0>;
};
};
uart0: serial@12000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x12000 0x1000>;

View File

@ -119,5 +119,19 @@
num-cs = <8>;
status = "disabled";
};
nand_controller: nand-controller@1800 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
reg = <0x1800 0x600>, <0x2000 0x10>;
reg-names = "nand", "nand-int-base";
status = "disabled";
nandcs: nand@0 {
compatible = "brcm,nandcs";
reg = <0>;
};
};
};
};

View File

@ -156,5 +156,19 @@
num-cs = <8>;
status = "disabled";
};
nand_controller: nand-controller@1800 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
reg = <0x1800 0x600>, <0x2000 0x10>;
reg-names = "nand", "nand-int-base";
status = "disabled";
nandcs: nand@0 {
compatible = "brcm,nandcs";
reg = <0>;
};
};
};
};

View File

@ -32,3 +32,13 @@
&hsspi {
status = "okay";
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-on-flash-bbt;
brcm,nand-ecc-use-strap;
};

View File

@ -32,3 +32,13 @@
&hsspi {
status = "okay";
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-on-flash-bbt;
brcm,nand-ecc-use-strap;
};

View File

@ -32,3 +32,13 @@
&hsspi {
status = "okay";
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-on-flash-bbt;
brcm,nand-ecc-use-strap;
};

View File

@ -32,3 +32,13 @@
&hsspi {
status = "okay";
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-on-flash-bbt;
brcm,nand-ecc-use-strap;
};

View File

@ -32,3 +32,13 @@
&hsspi {
status = "okay";
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-on-flash-bbt;
brcm,nand-ecc-use-strap;
};

View File

@ -32,3 +32,13 @@
&hsspi {
status = "okay";
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-on-flash-bbt;
brcm,nand-ecc-use-strap;
};

View File

@ -32,3 +32,13 @@
&hsspi {
status = "okay";
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-on-flash-bbt;
brcm,nand-ecc-use-strap;
};

View File

@ -1378,7 +1378,7 @@ static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand,
return ret;
/*
* The write cycle timing is directly matching tWC, but is also
* The read cycle timing is directly matching tRC, but is also
* dependent on the setup and hold timings we calculated earlier,
* which gives:
*

View File

@ -2,7 +2,7 @@
# link order matters; don't link the more generic brcmstb_nand.o before the
# more specific iproc_nand.o, for instance
obj-$(CONFIG_MTD_NAND_BRCMNAND_IPROC) += iproc_nand.o
obj-$(CONFIG_MTD_NAND_BRCMNAND_BCMBCA) += bcm63138_nand.o
obj-$(CONFIG_MTD_NAND_BRCMNAND_BCMBCA) += bcmbca_nand.o
obj-$(CONFIG_MTD_NAND_BRCMNAND_BCM63XX) += bcm6368_nand.o
obj-$(CONFIG_MTD_NAND_BRCMNAND_BRCMSTB) += brcmstb_nand.o
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand.o

View File

@ -1,99 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright © 2015 Broadcom Corporation
*/
#include <linux/device.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "brcmnand.h"
struct bcm63138_nand_soc {
struct brcmnand_soc soc;
void __iomem *base;
};
#define BCM63138_NAND_INT_STATUS 0x00
#define BCM63138_NAND_INT_EN 0x04
enum {
BCM63138_CTLRDY = BIT(4),
};
static bool bcm63138_nand_intc_ack(struct brcmnand_soc *soc)
{
struct bcm63138_nand_soc *priv =
container_of(soc, struct bcm63138_nand_soc, soc);
void __iomem *mmio = priv->base + BCM63138_NAND_INT_STATUS;
u32 val = brcmnand_readl(mmio);
if (val & BCM63138_CTLRDY) {
brcmnand_writel(val & ~BCM63138_CTLRDY, mmio);
return true;
}
return false;
}
static void bcm63138_nand_intc_set(struct brcmnand_soc *soc, bool en)
{
struct bcm63138_nand_soc *priv =
container_of(soc, struct bcm63138_nand_soc, soc);
void __iomem *mmio = priv->base + BCM63138_NAND_INT_EN;
u32 val = brcmnand_readl(mmio);
if (en)
val |= BCM63138_CTLRDY;
else
val &= ~BCM63138_CTLRDY;
brcmnand_writel(val, mmio);
}
static int bcm63138_nand_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct bcm63138_nand_soc *priv;
struct brcmnand_soc *soc;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
soc = &priv->soc;
priv->base = devm_platform_ioremap_resource_byname(pdev, "nand-int-base");
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
soc->ctlrdy_ack = bcm63138_nand_intc_ack;
soc->ctlrdy_set_enabled = bcm63138_nand_intc_set;
return brcmnand_probe(pdev, soc);
}
static const struct of_device_id bcm63138_nand_of_match[] = {
{ .compatible = "brcm,nand-bcm63138" },
{},
};
MODULE_DEVICE_TABLE(of, bcm63138_nand_of_match);
static struct platform_driver bcm63138_nand_driver = {
.probe = bcm63138_nand_probe,
.remove_new = brcmnand_remove,
.driver = {
.name = "bcm63138_nand",
.pm = &brcmnand_pm_ops,
.of_match_table = bcm63138_nand_of_match,
}
};
module_platform_driver(bcm63138_nand_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Brian Norris");
MODULE_DESCRIPTION("NAND driver for BCM63138");

View File

@ -0,0 +1,126 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright © 2015 Broadcom Corporation
*/
#include <linux/device.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "brcmnand.h"
struct bcmbca_nand_soc {
struct brcmnand_soc soc;
void __iomem *base;
};
#define BCMBCA_NAND_INT_STATUS 0x00
#define BCMBCA_NAND_INT_EN 0x04
enum {
BCMBCA_CTLRDY = BIT(4),
};
#if defined(CONFIG_ARM64)
#define ALIGN_REQ 8
#else
#define ALIGN_REQ 4
#endif
static inline bool bcmbca_nand_is_buf_aligned(void *flash_cache, void *buffer)
{
return IS_ALIGNED((uintptr_t)buffer, ALIGN_REQ) &&
IS_ALIGNED((uintptr_t)flash_cache, ALIGN_REQ);
}
static bool bcmbca_nand_intc_ack(struct brcmnand_soc *soc)
{
struct bcmbca_nand_soc *priv =
container_of(soc, struct bcmbca_nand_soc, soc);
void __iomem *mmio = priv->base + BCMBCA_NAND_INT_STATUS;
u32 val = brcmnand_readl(mmio);
if (val & BCMBCA_CTLRDY) {
brcmnand_writel(val & ~BCMBCA_CTLRDY, mmio);
return true;
}
return false;
}
static void bcmbca_nand_intc_set(struct brcmnand_soc *soc, bool en)
{
struct bcmbca_nand_soc *priv =
container_of(soc, struct bcmbca_nand_soc, soc);
void __iomem *mmio = priv->base + BCMBCA_NAND_INT_EN;
u32 val = brcmnand_readl(mmio);
if (en)
val |= BCMBCA_CTLRDY;
else
val &= ~BCMBCA_CTLRDY;
brcmnand_writel(val, mmio);
}
static void bcmbca_read_data_bus(struct brcmnand_soc *soc,
void __iomem *flash_cache, u32 *buffer, int fc_words)
{
/*
* memcpy can do unaligned aligned access depending on source
* and dest address, which is incompatible with nand cache. Fallback
* to the memcpy_fromio in such case
*/
if (bcmbca_nand_is_buf_aligned((void __force *)flash_cache, buffer))
memcpy((void *)buffer, (void __force *)flash_cache, fc_words * 4);
else
memcpy_fromio((void *)buffer, flash_cache, fc_words * 4);
}
static int bcmbca_nand_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct bcmbca_nand_soc *priv;
struct brcmnand_soc *soc;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
soc = &priv->soc;
priv->base = devm_platform_ioremap_resource_byname(pdev, "nand-int-base");
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
soc->ctlrdy_ack = bcmbca_nand_intc_ack;
soc->ctlrdy_set_enabled = bcmbca_nand_intc_set;
soc->read_data_bus = bcmbca_read_data_bus;
return brcmnand_probe(pdev, soc);
}
static const struct of_device_id bcmbca_nand_of_match[] = {
{ .compatible = "brcm,nand-bcm63138" },
{},
};
MODULE_DEVICE_TABLE(of, bcmbca_nand_of_match);
static struct platform_driver bcmbca_nand_driver = {
.probe = bcmbca_nand_probe,
.remove_new = brcmnand_remove,
.driver = {
.name = "bcmbca_nand",
.pm = &brcmnand_pm_ops,
.of_match_table = bcmbca_nand_of_match,
}
};
module_platform_driver(bcmbca_nand_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Brian Norris");
MODULE_DESCRIPTION("NAND driver for BCMBCA");

View File

@ -625,7 +625,7 @@ enum {
/* Only for v7.2 */
#define ACC_CONTROL_ECC_EXT_SHIFT 13
static u8 brcmnand_status(struct brcmnand_host *host);
static int brcmnand_status(struct brcmnand_host *host);
static inline bool brcmnand_non_mmio_ops(struct brcmnand_controller *ctrl)
{
@ -851,6 +851,20 @@ static inline u32 edu_readl(struct brcmnand_controller *ctrl,
return brcmnand_readl(ctrl->edu_base + offs);
}
static inline void brcmnand_read_data_bus(struct brcmnand_controller *ctrl,
void __iomem *flash_cache, u32 *buffer, int fc_words)
{
struct brcmnand_soc *soc = ctrl->soc;
int i;
if (soc->read_data_bus) {
soc->read_data_bus(soc, flash_cache, buffer, fc_words);
} else {
for (i = 0; i < fc_words; i++)
buffer[i] = brcmnand_read_fc(ctrl, i);
}
}
static void brcmnand_clear_ecc_addr(struct brcmnand_controller *ctrl)
{
@ -1024,6 +1038,22 @@ static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl)
return -1;
}
static bool brcmnand_get_sector_size_1k(struct brcmnand_host *host)
{
struct brcmnand_controller *ctrl = host->ctrl;
int sector_size_bit = brcmnand_sector_1k_shift(ctrl);
u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
BRCMNAND_CS_ACC_CONTROL);
u32 acc_control;
if (sector_size_bit < 0)
return false;
acc_control = nand_readreg(ctrl, acc_control_offs);
return ((acc_control & BIT(sector_size_bit)) != 0);
}
static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val)
{
struct brcmnand_controller *ctrl = host->ctrl;
@ -1041,6 +1071,43 @@ static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val)
nand_writereg(ctrl, acc_control_offs, tmp);
}
static int brcmnand_get_spare_size(struct brcmnand_host *host)
{
struct brcmnand_controller *ctrl = host->ctrl;
u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
BRCMNAND_CS_ACC_CONTROL);
u32 acc = nand_readreg(ctrl, acc_control_offs);
return (acc & brcmnand_spare_area_mask(ctrl));
}
static void brcmnand_get_ecc_settings(struct brcmnand_host *host, struct nand_chip *chip)
{
struct brcmnand_controller *ctrl = host->ctrl;
u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
BRCMNAND_CS_ACC_CONTROL);
bool sector_size_1k = brcmnand_get_sector_size_1k(host);
int spare_area_size, ecc_level;
u32 acc;
spare_area_size = brcmnand_get_spare_size(host);
acc = nand_readreg(ctrl, acc_control_offs);
ecc_level = (acc & brcmnand_ecc_level_mask(ctrl)) >> ctrl->ecc_level_shift;
if (sector_size_1k)
chip->ecc.strength = ecc_level * 2;
else if (spare_area_size == 16 && ecc_level == 15)
chip->ecc.strength = 1; /* hamming */
else
chip->ecc.strength = ecc_level;
if (chip->ecc.size == 0) {
if (sector_size_1k)
chip->ecc.size = 1024;
else
chip->ecc.size = 512;
}
}
/***********************************************************************
* CS_NAND_SELECT
***********************************************************************/
@ -1084,8 +1151,8 @@ static int bcmnand_ctrl_poll_status(struct brcmnand_host *host,
if ((val & mask) == expected_val)
return 0;
dev_warn(ctrl->dev, "timeout on status poll (expected %x got %x)\n",
expected_val, val & mask);
dev_err(ctrl->dev, "timeout on status poll (expected %x got %x)\n",
expected_val, val & mask);
return -ETIMEDOUT;
}
@ -1690,7 +1757,7 @@ static int brcmnand_waitfunc(struct nand_chip *chip)
INTFC_FLASH_STATUS;
}
static u8 brcmnand_status(struct brcmnand_host *host)
static int brcmnand_status(struct brcmnand_host *host)
{
struct nand_chip *chip = &host->chip;
struct mtd_info *mtd = nand_to_mtd(chip);
@ -1701,7 +1768,7 @@ static u8 brcmnand_status(struct brcmnand_host *host)
return brcmnand_waitfunc(chip);
}
static u8 brcmnand_reset(struct brcmnand_host *host)
static int brcmnand_reset(struct brcmnand_host *host)
{
struct nand_chip *chip = &host->chip;
@ -1975,7 +2042,7 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
{
struct brcmnand_host *host = nand_get_controller_data(chip);
struct brcmnand_controller *ctrl = host->ctrl;
int i, j, ret = 0;
int i, ret = 0;
brcmnand_clear_ecc_addr(ctrl);
@ -1988,8 +2055,8 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
if (likely(buf)) {
brcmnand_soc_data_bus_prepare(ctrl->soc, false);
for (j = 0; j < FC_WORDS; j++, buf++)
*buf = brcmnand_read_fc(ctrl, j);
brcmnand_read_data_bus(ctrl, ctrl->nand_fc, buf, FC_WORDS);
buf += FC_WORDS;
brcmnand_soc_data_bus_unprepare(ctrl->soc, false);
}
@ -2137,7 +2204,7 @@ try_dmaread:
return err;
}
dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n",
dev_err(ctrl->dev, "uncorrectable error at 0x%llx\n",
(unsigned long long)err_addr);
mtd->ecc_stats.failed++;
/* NAND layer expects zero on ECC errors */
@ -2339,7 +2406,7 @@ static int brcmnand_write_oob_raw(struct nand_chip *chip, int page)
}
static int brcmnand_exec_instr(struct brcmnand_host *host, int i,
const struct nand_operation *op)
const struct nand_operation *op)
{
const struct nand_op_instr *instr = &op->instrs[i];
struct brcmnand_controller *ctrl = host->ctrl;
@ -2353,7 +2420,7 @@ static int brcmnand_exec_instr(struct brcmnand_host *host, int i,
* (WAITRDY excepted).
*/
last_op = ((i == (op->ninstrs - 1)) && (instr->type != NAND_OP_WAITRDY_INSTR)) ||
((i == (op->ninstrs - 2)) && (op->instrs[i+1].type == NAND_OP_WAITRDY_INSTR));
((i == (op->ninstrs - 2)) && (op->instrs[i + 1].type == NAND_OP_WAITRDY_INSTR));
switch (instr->type) {
case NAND_OP_CMD_INSTR:
@ -2398,10 +2465,10 @@ static int brcmnand_exec_instr(struct brcmnand_host *host, int i,
static int brcmnand_op_is_status(const struct nand_operation *op)
{
if ((op->ninstrs == 2) &&
(op->instrs[0].type == NAND_OP_CMD_INSTR) &&
(op->instrs[0].ctx.cmd.opcode == NAND_CMD_STATUS) &&
(op->instrs[1].type == NAND_OP_DATA_IN_INSTR))
if (op->ninstrs == 2 &&
op->instrs[0].type == NAND_OP_CMD_INSTR &&
op->instrs[0].ctx.cmd.opcode == NAND_CMD_STATUS &&
op->instrs[1].type == NAND_OP_DATA_IN_INSTR)
return 1;
return 0;
@ -2409,10 +2476,10 @@ static int brcmnand_op_is_status(const struct nand_operation *op)
static int brcmnand_op_is_reset(const struct nand_operation *op)
{
if ((op->ninstrs == 2) &&
(op->instrs[0].type == NAND_OP_CMD_INSTR) &&
(op->instrs[0].ctx.cmd.opcode == NAND_CMD_RESET) &&
(op->instrs[1].type == NAND_OP_WAITRDY_INSTR))
if (op->ninstrs == 2 &&
op->instrs[0].type == NAND_OP_CMD_INSTR &&
op->instrs[0].ctx.cmd.opcode == NAND_CMD_RESET &&
op->instrs[1].type == NAND_OP_WAITRDY_INSTR)
return 1;
return 0;
@ -2433,11 +2500,14 @@ static int brcmnand_exec_op(struct nand_chip *chip,
if (brcmnand_op_is_status(op)) {
status = op->instrs[1].ctx.data.buf.in;
*status = brcmnand_status(host);
ret = brcmnand_status(host);
if (ret < 0)
return ret;
*status = ret & 0xFF;
return 0;
}
else if (brcmnand_op_is_reset(op)) {
} else if (brcmnand_op_is_reset(op)) {
ret = brcmnand_reset(host);
if (ret < 0)
return ret;
@ -2608,19 +2678,37 @@ static int brcmnand_setup_dev(struct brcmnand_host *host)
nanddev_get_memorg(&chip->base);
struct brcmnand_controller *ctrl = host->ctrl;
struct brcmnand_cfg *cfg = &host->hwcfg;
char msg[128];
struct device_node *np = nand_get_flash_node(chip);
u32 offs, tmp, oob_sector;
bool use_strap = false;
char msg[128];
int ret;
memset(cfg, 0, sizeof(*cfg));
use_strap = of_property_read_bool(np, "brcm,nand-ecc-use-strap");
ret = of_property_read_u32(nand_get_flash_node(chip),
"brcm,nand-oob-sector-size",
/*
* Either nand-ecc-xxx or brcm,nand-ecc-use-strap can be set. Error out
* if both exist.
*/
if (chip->ecc.strength && use_strap) {
dev_err(ctrl->dev,
"ECC strap and DT ECC configuration properties are mutually exclusive\n");
return -EINVAL;
}
if (use_strap)
brcmnand_get_ecc_settings(host, chip);
ret = of_property_read_u32(np, "brcm,nand-oob-sector-size",
&oob_sector);
if (ret) {
/* Use detected size */
cfg->spare_area_size = mtd->oobsize /
(mtd->writesize >> FC_SHIFT);
if (use_strap)
cfg->spare_area_size = brcmnand_get_spare_size(host);
else
/* Use detected size */
cfg->spare_area_size = mtd->oobsize /
(mtd->writesize >> FC_SHIFT);
} else {
cfg->spare_area_size = oob_sector;
}
@ -3135,6 +3223,10 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
/* Disable XOR addressing */
brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0);
/* Check if the board connects the WP pin */
if (of_property_read_bool(dn, "brcm,wp-not-connected"))
wp_on = 0;
if (ctrl->features & BRCMNAND_HAS_WP) {
/* Permanently disable write protection */
if (wp_on == 2)

View File

@ -24,6 +24,8 @@ struct brcmnand_soc {
void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en);
void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare,
bool is_param);
void (*read_data_bus)(struct brcmnand_soc *soc, void __iomem *flash_cache,
u32 *buffer, int fc_words);
const struct brcmnand_io_ops *ops;
};

View File

@ -869,7 +869,8 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev)
struct mtd_info *mtd;
if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
return -ENODEV;
return dev_err_probe(&pdev->dev, -EPROBE_DEFER, "lbc_ctrl_dev missing\n");
lbc = fsl_lbc_ctrl_dev->regs;
dev = fsl_lbc_ctrl_dev->dev;

View File

@ -303,8 +303,9 @@ static int lpc32xx_nand_device_ready(struct nand_chip *nand_chip)
return 0;
}
static irqreturn_t lpc3xxx_nand_irq(int irq, struct lpc32xx_nand_host *host)
static irqreturn_t lpc3xxx_nand_irq(int irq, void *data)
{
struct lpc32xx_nand_host *host = data;
uint8_t sr;
/* Clear interrupt flag by reading status */
@ -780,7 +781,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
goto release_dma_chan;
}
if (request_irq(host->irq, (irq_handler_t)&lpc3xxx_nand_irq,
if (request_irq(host->irq, &lpc3xxx_nand_irq,
IRQF_TRIGGER_HIGH, DRV_NAME, host)) {
dev_err(&pdev->dev, "Error requesting NAND IRQ\n");
res = -ENXIO;

View File

@ -63,7 +63,7 @@
#define CMDRWGEN(cmd_dir, ran, bch, short_mode, page_size, pages) \
( \
(cmd_dir) | \
((ran) << 19) | \
(ran) | \
((bch) << 14) | \
((short_mode) << 13) | \
(((page_size) & 0x7f) << 6) | \

View File

@ -1356,7 +1356,7 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
return -EINVAL;
}
chip = devm_kzalloc(dev, sizeof(*chip) + nsels * sizeof(u8),
chip = devm_kzalloc(dev, struct_size(chip, sels, nsels),
GFP_KERNEL);
if (!chip)
return -ENOMEM;

View File

@ -1211,21 +1211,36 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
return nand_exec_op(chip, &op);
}
static unsigned int rawnand_last_page_of_lun(unsigned int pages_per_lun, unsigned int lun)
{
/* lun is expected to be very small */
return (lun * pages_per_lun) + pages_per_lun - 1;
}
static void rawnand_cap_cont_reads(struct nand_chip *chip)
{
struct nand_memory_organization *memorg;
unsigned int pages_per_lun, first_lun, last_lun;
unsigned int ppl, first_lun, last_lun;
memorg = nanddev_get_memorg(&chip->base);
pages_per_lun = memorg->pages_per_eraseblock * memorg->eraseblocks_per_lun;
first_lun = chip->cont_read.first_page / pages_per_lun;
last_lun = chip->cont_read.last_page / pages_per_lun;
ppl = memorg->pages_per_eraseblock * memorg->eraseblocks_per_lun;
first_lun = chip->cont_read.first_page / ppl;
last_lun = chip->cont_read.last_page / ppl;
/* Prevent sequential cache reads across LUN boundaries */
if (first_lun != last_lun)
chip->cont_read.pause_page = first_lun * pages_per_lun + pages_per_lun - 1;
chip->cont_read.pause_page = rawnand_last_page_of_lun(ppl, first_lun);
else
chip->cont_read.pause_page = chip->cont_read.last_page;
if (chip->cont_read.first_page == chip->cont_read.pause_page) {
chip->cont_read.first_page++;
chip->cont_read.pause_page = min(chip->cont_read.last_page,
rawnand_last_page_of_lun(ppl, first_lun + 1));
}
if (chip->cont_read.first_page >= chip->cont_read.last_page)
chip->cont_read.ongoing = false;
}
static int nand_lp_exec_cont_read_page_op(struct nand_chip *chip, unsigned int page,
@ -1292,12 +1307,11 @@ static int nand_lp_exec_cont_read_page_op(struct nand_chip *chip, unsigned int p
if (!chip->cont_read.ongoing)
return 0;
if (page == chip->cont_read.pause_page &&
page != chip->cont_read.last_page) {
chip->cont_read.first_page = chip->cont_read.pause_page + 1;
rawnand_cap_cont_reads(chip);
} else if (page == chip->cont_read.last_page) {
if (page == chip->cont_read.last_page) {
chip->cont_read.ongoing = false;
} else if (page == chip->cont_read.pause_page) {
chip->cont_read.first_page++;
rawnand_cap_cont_reads(chip);
}
return 0;
@ -3466,30 +3480,36 @@ static void rawnand_enable_cont_reads(struct nand_chip *chip, unsigned int page,
u32 readlen, int col)
{
struct mtd_info *mtd = nand_to_mtd(chip);
unsigned int end_page, end_col;
unsigned int first_page, last_page;
chip->cont_read.ongoing = false;
if (!chip->controller->supported_op.cont_read)
return;
end_page = DIV_ROUND_UP(col + readlen, mtd->writesize);
end_col = (col + readlen) % mtd->writesize;
if (col)
page++;
if (end_col && end_page)
end_page--;
if (page + 1 > end_page)
/*
* Don't bother making any calculations if the length is too small.
* Side effect: avoids possible integer underflows below.
*/
if (readlen < (2 * mtd->writesize))
return;
chip->cont_read.first_page = page;
chip->cont_read.last_page = end_page;
chip->cont_read.ongoing = true;
/* Derive the page where continuous read should start (the first full page read) */
first_page = page;
if (col)
first_page++;
rawnand_cap_cont_reads(chip);
/* Derive the page where continuous read should stop (the last full page read) */
last_page = page + ((col + readlen) / mtd->writesize) - 1;
/* Configure and enable continuous read when suitable */
if (first_page < last_page) {
chip->cont_read.first_page = first_page;
chip->cont_read.last_page = last_page;
chip->cont_read.ongoing = true;
/* May reset the ongoing flag */
rawnand_cap_cont_reads(chip);
}
}
static void rawnand_cont_read_skip_first_page(struct nand_chip *chip, unsigned int page)
@ -3498,10 +3518,7 @@ static void rawnand_cont_read_skip_first_page(struct nand_chip *chip, unsigned i
return;
chip->cont_read.first_page++;
if (chip->cont_read.first_page == chip->cont_read.pause_page)
chip->cont_read.first_page++;
if (chip->cont_read.first_page >= chip->cont_read.last_page)
chip->cont_read.ongoing = false;
rawnand_cap_cont_reads(chip);
}
/**
@ -3577,7 +3594,8 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
oob = ops->oobbuf;
oob_required = oob ? 1 : 0;
rawnand_enable_cont_reads(chip, page, readlen, col);
if (likely(ops->mode != MTD_OPS_RAW))
rawnand_enable_cont_reads(chip, page, readlen, col);
while (1) {
struct mtd_ecc_stats ecc_stats = mtd->ecc_stats;
@ -3710,6 +3728,9 @@ read_retry:
}
nand_deselect_target(chip);
if (WARN_ON_ONCE(chip->cont_read.ongoing))
chip->cont_read.ongoing = false;
ops->retlen = ops->len - (size_t) readlen;
if (oob)
ops->oobretlen = ops->ooblen - oobreadlen;
@ -5195,6 +5216,15 @@ static void rawnand_late_check_supported_ops(struct nand_chip *chip)
if (!nand_has_exec_op(chip))
return;
/*
* For now, continuous reads can only be used with the core page helpers.
* This can be extended later.
*/
if (!(chip->ecc.read_page == nand_read_page_hwecc ||
chip->ecc.read_page == nand_read_page_syndrome ||
chip->ecc.read_page == nand_read_page_swecc))
return;
rawnand_check_cont_read_support(chip);
}

View File

@ -576,7 +576,6 @@ static int search_bbt(struct nand_chip *this, uint8_t *buf,
startblock &= bbtblocks - 1;
} else {
chips = 1;
bbtblocks = mtd->size >> this->bbt_erase_shift;
}
for (i = 0; i < chips; i++) {

View File

@ -31,7 +31,6 @@ struct hynix_read_retry {
/**
* struct hynix_nand - private Hynix NAND struct
* @nand_technology: manufacturing process expressed in picometer
* @read_retry: read-retry information
*/
struct hynix_nand {

View File

@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/mtd/rawnand.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@ -37,7 +38,7 @@
#define FMC2_MAX_SG 16
/* Max chip enable */
#define FMC2_MAX_CE 2
#define FMC2_MAX_CE 4
/* Max ECC buffer length */
#define FMC2_MAX_ECC_BUF_LEN (FMC2_BCHDSRS_LEN * FMC2_MAX_SG)
@ -243,6 +244,13 @@ static inline struct stm32_fmc2_nand *to_fmc2_nand(struct nand_chip *chip)
return container_of(chip, struct stm32_fmc2_nand, chip);
}
struct stm32_fmc2_nfc;
struct stm32_fmc2_nfc_data {
int max_ncs;
int (*set_cdev)(struct stm32_fmc2_nfc *nfc);
};
struct stm32_fmc2_nfc {
struct nand_controller base;
struct stm32_fmc2_nand nand;
@ -256,6 +264,7 @@ struct stm32_fmc2_nfc {
phys_addr_t data_phys_addr[FMC2_MAX_CE];
struct clk *clk;
u8 irq_state;
const struct stm32_fmc2_nfc_data *data;
struct dma_chan *dma_tx_ch;
struct dma_chan *dma_rx_ch;
@ -264,6 +273,8 @@ struct stm32_fmc2_nfc {
struct sg_table dma_ecc_sg;
u8 *ecc_buf;
int dma_ecc_len;
u32 tx_dma_max_burst;
u32 rx_dma_max_burst;
struct completion complete;
struct completion dma_data_complete;
@ -347,20 +358,26 @@ static int stm32_fmc2_nfc_select_chip(struct nand_chip *chip, int chipnr)
stm32_fmc2_nfc_setup(chip);
stm32_fmc2_nfc_timings_init(chip);
if (nfc->dma_tx_ch && nfc->dma_rx_ch) {
if (nfc->dma_tx_ch) {
memset(&dma_cfg, 0, sizeof(dma_cfg));
dma_cfg.src_addr = nfc->data_phys_addr[nfc->cs_sel];
dma_cfg.dst_addr = nfc->data_phys_addr[nfc->cs_sel];
dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
dma_cfg.src_maxburst = 32;
dma_cfg.dst_maxburst = 32;
dma_cfg.dst_maxburst = nfc->tx_dma_max_burst /
dma_cfg.dst_addr_width;
ret = dmaengine_slave_config(nfc->dma_tx_ch, &dma_cfg);
if (ret) {
dev_err(nfc->dev, "tx DMA engine slave config failed\n");
return ret;
}
}
if (nfc->dma_rx_ch) {
memset(&dma_cfg, 0, sizeof(dma_cfg));
dma_cfg.src_addr = nfc->data_phys_addr[nfc->cs_sel];
dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
dma_cfg.src_maxburst = nfc->rx_dma_max_burst /
dma_cfg.src_addr_width;
ret = dmaengine_slave_config(nfc->dma_rx_ch, &dma_cfg);
if (ret) {
@ -1545,6 +1562,7 @@ static int stm32_fmc2_nfc_setup_interface(struct nand_chip *chip, int chipnr,
static int stm32_fmc2_nfc_dma_setup(struct stm32_fmc2_nfc *nfc)
{
struct dma_slave_caps caps;
int ret = 0;
nfc->dma_tx_ch = dma_request_chan(nfc->dev, "tx");
@ -1557,6 +1575,11 @@ static int stm32_fmc2_nfc_dma_setup(struct stm32_fmc2_nfc *nfc)
goto err_dma;
}
ret = dma_get_slave_caps(nfc->dma_tx_ch, &caps);
if (ret)
return ret;
nfc->tx_dma_max_burst = caps.max_burst;
nfc->dma_rx_ch = dma_request_chan(nfc->dev, "rx");
if (IS_ERR(nfc->dma_rx_ch)) {
ret = PTR_ERR(nfc->dma_rx_ch);
@ -1567,6 +1590,11 @@ static int stm32_fmc2_nfc_dma_setup(struct stm32_fmc2_nfc *nfc)
goto err_dma;
}
ret = dma_get_slave_caps(nfc->dma_rx_ch, &caps);
if (ret)
return ret;
nfc->rx_dma_max_burst = caps.max_burst;
nfc->dma_ecc_ch = dma_request_chan(nfc->dev, "ecc");
if (IS_ERR(nfc->dma_ecc_ch)) {
ret = PTR_ERR(nfc->dma_ecc_ch);
@ -1790,7 +1818,7 @@ static int stm32_fmc2_nfc_parse_child(struct stm32_fmc2_nfc *nfc,
return ret;
}
if (cs >= FMC2_MAX_CE) {
if (cs >= nfc->data->max_ncs) {
dev_err(nfc->dev, "invalid reg value: %d\n", cs);
return -EINVAL;
}
@ -1896,9 +1924,17 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev)
nand_controller_init(&nfc->base);
nfc->base.ops = &stm32_fmc2_nfc_controller_ops;
ret = stm32_fmc2_nfc_set_cdev(nfc);
if (ret)
return ret;
nfc->data = of_device_get_match_data(dev);
if (!nfc->data)
return -EINVAL;
if (nfc->data->set_cdev) {
ret = nfc->data->set_cdev(nfc);
if (ret)
return ret;
} else {
nfc->cdev = dev->parent;
}
ret = stm32_fmc2_nfc_parse_dt(nfc);
if (ret)
@ -1917,7 +1953,7 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev)
if (nfc->dev == nfc->cdev)
start_region = 1;
for (chip_cs = 0, mem_region = start_region; chip_cs < FMC2_MAX_CE;
for (chip_cs = 0, mem_region = start_region; chip_cs < nfc->data->max_ncs;
chip_cs++, mem_region += 3) {
if (!(nfc->cs_assigned & BIT(chip_cs)))
continue;
@ -2073,7 +2109,7 @@ static int __maybe_unused stm32_fmc2_nfc_resume(struct device *dev)
stm32_fmc2_nfc_wp_disable(nand);
for (chip_cs = 0; chip_cs < FMC2_MAX_CE; chip_cs++) {
for (chip_cs = 0; chip_cs < nfc->data->max_ncs; chip_cs++) {
if (!(nfc->cs_assigned & BIT(chip_cs)))
continue;
@ -2086,9 +2122,28 @@ static int __maybe_unused stm32_fmc2_nfc_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(stm32_fmc2_nfc_pm_ops, stm32_fmc2_nfc_suspend,
stm32_fmc2_nfc_resume);
static const struct stm32_fmc2_nfc_data stm32_fmc2_nfc_mp1_data = {
.max_ncs = 2,
.set_cdev = stm32_fmc2_nfc_set_cdev,
};
static const struct stm32_fmc2_nfc_data stm32_fmc2_nfc_mp25_data = {
.max_ncs = 4,
};
static const struct of_device_id stm32_fmc2_nfc_match[] = {
{.compatible = "st,stm32mp15-fmc2"},
{.compatible = "st,stm32mp1-fmc2-nfc"},
{
.compatible = "st,stm32mp15-fmc2",
.data = &stm32_fmc2_nfc_mp1_data,
},
{
.compatible = "st,stm32mp1-fmc2-nfc",
.data = &stm32_fmc2_nfc_mp1_data,
},
{
.compatible = "st,stm32mp25-fmc2-nfc",
.data = &stm32_fmc2_nfc_mp25_data,
},
{}
};
MODULE_DEVICE_TABLE(of, stm32_fmc2_nfc_match);

View File

@ -104,7 +104,8 @@ static const struct mtd_ooblayout_ops f50l1g41lb_ooblayout = {
static const struct spinand_info esmt_c8_spinand_table[] = {
SPINAND_INFO("F50L1G41LB",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01),
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01, 0x7f,
0x7f, 0x7f),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(1, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
@ -113,7 +114,8 @@ static const struct spinand_info esmt_c8_spinand_table[] = {
0,
SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)),
SPINAND_INFO("F50D1G41LB",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x11),
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x11, 0x7f,
0x7f, 0x7f),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(1, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
@ -122,7 +124,8 @@ static const struct spinand_info esmt_c8_spinand_table[] = {
0,
SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)),
SPINAND_INFO("F50D2G41KA",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x51),
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x51, 0x7f,
0x7f, 0x7f),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,

View File

@ -15,6 +15,8 @@
#define WINBOND_CFG_BUF_READ BIT(3)
#define W25N04KV_STATUS_ECC_5_8_BITFLIPS (3 << 4)
static SPINAND_OP_VARIANTS(read_cache_variants,
SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
@ -118,6 +120,7 @@ static int w25n02kv_ecc_get_status(struct spinand_device *spinand,
return -EBADMSG;
case STATUS_ECC_HAS_BITFLIPS:
case W25N04KV_STATUS_ECC_5_8_BITFLIPS:
/*
* Let's try to retrieve the real maximum number of bitflips
* in order to avoid forcing the wear-leveling layer to move
@ -214,6 +217,15 @@ static const struct spinand_info winbond_spinand_table[] = {
&update_cache_variants),
0,
SPINAND_ECCINFO(&w25m02gv_ooblayout, w25n02kv_ecc_get_status)),
SPINAND_INFO("W25N04KV",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x23),
NAND_MEMORG(1, 2048, 128, 64, 4096, 40, 2, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
0,
SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
};
static int winbond_spinand_init(struct spinand_device *spinand)

View File

@ -169,7 +169,7 @@
struct spinand_op;
struct spinand_device;
#define SPINAND_MAX_ID_LEN 4
#define SPINAND_MAX_ID_LEN 5
/*
* For erase, write and read operation, we got the following timings :
* tBERS (erase) 1ms to 4ms