diff mbox series

[v3,15/22] mtd: rawnand: fsmc: Stop implementing ->select_chip()

Message ID 20181111075524.13123-16-boris.brezillon@bootlin.com
State Accepted
Headers show
Series mtd: rawnand: 3rd batch of cleanup | expand

Commit Message

Boris Brezillon Nov. 11, 2018, 7:55 a.m. UTC
Now that the CS line to assert is directly passed through the
nand_operation struct we can replace the fsmc_select_chip()
implementation by an internal fsmc_ce_ctrl() function which is
directly called from fsmc_exec_op()

Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
---
Changes in v3:
- None

Changes in v2:
- None
---
 drivers/mtd/nand/raw/fsmc_nand.c | 23 ++++++++++++-----------
 1 file changed, 12 insertions(+), 11 deletions(-)

Comments

Linus Walleij Jan. 9, 2019, 6:18 p.m. UTC | #1
On Sun, Nov 11, 2018 at 8:59 AM Boris Brezillon
<boris.brezillon@bootlin.com> wrote:

> Now that the CS line to assert is directly passed through the
> nand_operation struct we can replace the fsmc_select_chip()
> implementation by an internal fsmc_ce_ctrl() function which is
> directly called from fsmc_exec_op()
>
> Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>

This commit regresses the Nomadik NHK15 in a curious way.
The code seems fine but after a few reads to the chip it crashes
(trace with debug prints enabled):

fsmc-nand 10100000.flash: FSMC device partno 090, manufacturer 80,
revision 00, config 00
Executing operation [2 instructions]:
  ->CMD      [0xff]
  ->WAITRDY  [max 250 ms]
Executing operation [1 instructions]:
  ->CMD      [0x70]
Executing operation [1 instructions]:
  ->DATA_IN  [1 B, force 8-bit]
Executing operation [1 instructions]:
  ->CMD      [0x00]
Executing operation [3 instructions]:
  ->CMD      [0x90]
  ->ADDR     [1 cyc]
  ->DATA_IN  [2 B, force 8-bit]
Executing operation [3 instructions]:
  ->CMD      [0x90]
  ->ADDR     [1 cyc]
  ->DATA_IN  [8 B, force 8-bit]
Executing operation [3 instructions]:
  ->CMD      [0x90]
  ->ADDR     [1 cyc]
  ->DATA_IN  [4 B, force 8-bit]
Executing operation [3 instructions]:
  ->CMD      [0x90]
  ->ADDR     [1 cyc]
  ->DATA_IN  [5 B, force 8-bit]
nand: device found, Manufacturer ID: 0x20, Chip ID: 0xa1
nand: ST Micro NAND 128MiB 1,8V 8-bit
nand: 128 MiB, SLC, erase size: 128 KiB, page size: 2048, OOB size: 64
fsmc-nand 10100000.flash: Using 1-bit HW ECC scheme
Scanning device for bad blocks
Executing operation [5 instructions]:
  ->CMD      [0x00]
  ->ADDR     [4 cyc]
  ->CMD      [0x30]
  ->WAITRDY  [max 200000 ms]
Executing operation [1 instructions]:
  ->CMD      [0x70]
Executing operation [1 instructions]:
  ->DATA_IN  [1 B, force 8-bit]
Executing operation [1 instructions]:
  ->CMD      [0x00]
  ->DATA_IN  [64 B]
Unhandled fault: external abort on non-linefetch (0x008) at 0xcc960000
pgd = (ptrval)
[cc960000] *pgd=0b808811, *pte=40000653, *ppte=40000552
Internal error: : 8 [#1] PREEMPT ARM
Modules linked in:
CPU: 0 PID: 1 Comm: swapper Not tainted 5.0.0-rc1+ #84
Hardware name: Nomadik STn8815
PC is at fsmc_exec_op+0x180/0x284
LR is at fsmc_exec_op+0x144/0x284
pc : [<c0361f18>]    lr : [<c0361edc>]    psr: 20000013
sp : cb82ba88  ip : c092e6f8  fp : c0644078
r10: c0615ae8  r9 : cb9d5020  r8 : 00000000
r7 : cb9d5038  r6 : cb82bac4  r5 : 00000004  r4 : cb82bb20
r3 : cb8807fc  r2 : cb88083c  r1 : cc960000  r0 : 00000013
Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
Control: 0005317f  Table: 0b334000  DAC: 00000053
Process swapper (pid: 1, stack limit = 0x(ptrval))
(...)
[<c0361f18>] (fsmc_exec_op) from [<c0355834>]
(nand_lp_exec_read_page_op+0x1cc/0x224)
[<c0355834>] (nand_lp_exec_read_page_op) from [<c0355968>]
(nand_read_page_op+0xdc/0x2f0)
[<c0355968>] (nand_read_page_op) from [<c0355c48>] (nand_read_oob_std+0x1c/0x24)
[<c0355c48>] (nand_read_oob_std) from [<c0359444>] (nand_read_oob+0x504/0x708)
[<c0359444>] (nand_read_oob) from [<c0347548>] (mtd_read_oob+0x54/0xcc)
[<c0347548>] (mtd_read_oob) from [<c035cea8>] (create_bbt+0x120/0x290)
[<c035cea8>] (create_bbt) from [<c035e87c>] (nand_create_bbt+0x4f8/0x69c)
[<c035e87c>] (nand_create_bbt) from [<c035b00c>] (nand_scan_tail+0x9c4/0xb04)
[<c035b00c>] (nand_scan_tail) from [<c035b8ac>] (nand_scan_with_ids+0x760/0xab4)
[<c035b8ac>] (nand_scan_with_ids) from [<c06ca508>]
(fsmc_nand_probe+0x454/0x578)
[<c06ca508>] (fsmc_nand_probe) from [<c03071d8>] (platform_drv_probe+0x48/0x98)
[<c03071d8>] (platform_drv_probe) from [<c0305620>] (really_probe+0x224/0x2d4)
[<c0305620>] (really_probe) from [<c0305830>] (driver_probe_device+0x5c/0x16c)
[<c0305830>] (driver_probe_device) from [<c0305a10>] (__driver_attach+0xd0/0xd4)
[<c0305a10>] (__driver_attach) from [<c0303798>] (bus_for_each_dev+0x70/0xb4)
[<c0303798>] (bus_for_each_dev) from [<c0304a74>] (bus_add_driver+0x170/0x204)
[<c0304a74>] (bus_add_driver) from [<c03063ac>] (driver_register+0x74/0x108)
[<c03063ac>] (driver_register) from [<c0307390>]
(__platform_driver_probe+0x58/0x124)
[<c0307390>] (__platform_driver_probe) from [<c000a604>]
(do_one_initcall+0x48/0x1a0)
[<c000a604>] (do_one_initcall) from [<c06b3dd4>]
(kernel_init_freeable+0x104/0x1c8)
[<c06b3dd4>] (kernel_init_freeable) from [<c04fcd24>] (kernel_init+0x8/0xf4)
[<c04fcd24>] (kernel_init) from [<c00090e0>] (ret_from_fork+0x14/0x34)
(...)
Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b

After looking at it I realized that if I uncomment this line:

// fsmc_ce_ctrl(host, false);

The driver works fine, so just holding CE enabled all the time makes
everything work.

I suspect it is some kind of timing issue maybe platform- or electronics
dependent where you need to hold CE enabled for a while before
reading pages from the NAND. This would explain why it is not seen
in the platform this was developed on.

I will experiment with some delay valued and try to read some data
sheets but if you already have hints on how to deal with this I'd
like to hear!

Yours,
Linus Walleij
Boris Brezillon Jan. 9, 2019, 7:44 p.m. UTC | #2
On Wed, 9 Jan 2019 19:18:58 +0100
Linus Walleij <linus.walleij@linaro.org> wrote:

> On Sun, Nov 11, 2018 at 8:59 AM Boris Brezillon
> <boris.brezillon@bootlin.com> wrote:
> 
> > Now that the CS line to assert is directly passed through the
> > nand_operation struct we can replace the fsmc_select_chip()
> > implementation by an internal fsmc_ce_ctrl() function which is
> > directly called from fsmc_exec_op()
> >
> > Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>  
> 
> This commit regresses the Nomadik NHK15 in a curious way.
> The code seems fine but after a few reads to the chip it crashes
> (trace with debug prints enabled):
> 
> fsmc-nand 10100000.flash: FSMC device partno 090, manufacturer 80,
> revision 00, config 00
> Executing operation [2 instructions]:
>   ->CMD      [0xff]
>   ->WAITRDY  [max 250 ms]  
> Executing operation [1 instructions]:
>   ->CMD      [0x70]  
> Executing operation [1 instructions]:
>   ->DATA_IN  [1 B, force 8-bit]  
> Executing operation [1 instructions]:
>   ->CMD      [0x00]  
> Executing operation [3 instructions]:
>   ->CMD      [0x90]
>   ->ADDR     [1 cyc]
>   ->DATA_IN  [2 B, force 8-bit]  
> Executing operation [3 instructions]:
>   ->CMD      [0x90]
>   ->ADDR     [1 cyc]
>   ->DATA_IN  [8 B, force 8-bit]  
> Executing operation [3 instructions]:
>   ->CMD      [0x90]
>   ->ADDR     [1 cyc]
>   ->DATA_IN  [4 B, force 8-bit]  
> Executing operation [3 instructions]:
>   ->CMD      [0x90]
>   ->ADDR     [1 cyc]
>   ->DATA_IN  [5 B, force 8-bit]  
> nand: device found, Manufacturer ID: 0x20, Chip ID: 0xa1
> nand: ST Micro NAND 128MiB 1,8V 8-bit
> nand: 128 MiB, SLC, erase size: 128 KiB, page size: 2048, OOB size: 64
> fsmc-nand 10100000.flash: Using 1-bit HW ECC scheme
> Scanning device for bad blocks
> Executing operation [5 instructions]:
>   ->CMD      [0x00]
>   ->ADDR     [4 cyc]
>   ->CMD      [0x30]
>   ->WAITRDY  [max 200000 ms]  
> Executing operation [1 instructions]:
>   ->CMD      [0x70]  
> Executing operation [1 instructions]:
>   ->DATA_IN  [1 B, force 8-bit]  
> Executing operation [1 instructions]:
>   ->CMD      [0x00]
>   ->DATA_IN  [64 B]  
> Unhandled fault: external abort on non-linefetch (0x008) at 0xcc960000
> pgd = (ptrval)
> [cc960000] *pgd=0b808811, *pte=40000653, *ppte=40000552
> Internal error: : 8 [#1] PREEMPT ARM
> Modules linked in:
> CPU: 0 PID: 1 Comm: swapper Not tainted 5.0.0-rc1+ #84
> Hardware name: Nomadik STn8815
> PC is at fsmc_exec_op+0x180/0x284
> LR is at fsmc_exec_op+0x144/0x284
> pc : [<c0361f18>]    lr : [<c0361edc>]    psr: 20000013
> sp : cb82ba88  ip : c092e6f8  fp : c0644078
> r10: c0615ae8  r9 : cb9d5020  r8 : 00000000
> r7 : cb9d5038  r6 : cb82bac4  r5 : 00000004  r4 : cb82bb20
> r3 : cb8807fc  r2 : cb88083c  r1 : cc960000  r0 : 00000013
> Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> Control: 0005317f  Table: 0b334000  DAC: 00000053
> Process swapper (pid: 1, stack limit = 0x(ptrval))
> (...)
> [<c0361f18>] (fsmc_exec_op) from [<c0355834>]
> (nand_lp_exec_read_page_op+0x1cc/0x224)
> [<c0355834>] (nand_lp_exec_read_page_op) from [<c0355968>]
> (nand_read_page_op+0xdc/0x2f0)
> [<c0355968>] (nand_read_page_op) from [<c0355c48>] (nand_read_oob_std+0x1c/0x24)
> [<c0355c48>] (nand_read_oob_std) from [<c0359444>] (nand_read_oob+0x504/0x708)
> [<c0359444>] (nand_read_oob) from [<c0347548>] (mtd_read_oob+0x54/0xcc)
> [<c0347548>] (mtd_read_oob) from [<c035cea8>] (create_bbt+0x120/0x290)
> [<c035cea8>] (create_bbt) from [<c035e87c>] (nand_create_bbt+0x4f8/0x69c)
> [<c035e87c>] (nand_create_bbt) from [<c035b00c>] (nand_scan_tail+0x9c4/0xb04)
> [<c035b00c>] (nand_scan_tail) from [<c035b8ac>] (nand_scan_with_ids+0x760/0xab4)
> [<c035b8ac>] (nand_scan_with_ids) from [<c06ca508>]
> (fsmc_nand_probe+0x454/0x578)
> [<c06ca508>] (fsmc_nand_probe) from [<c03071d8>] (platform_drv_probe+0x48/0x98)
> [<c03071d8>] (platform_drv_probe) from [<c0305620>] (really_probe+0x224/0x2d4)
> [<c0305620>] (really_probe) from [<c0305830>] (driver_probe_device+0x5c/0x16c)
> [<c0305830>] (driver_probe_device) from [<c0305a10>] (__driver_attach+0xd0/0xd4)
> [<c0305a10>] (__driver_attach) from [<c0303798>] (bus_for_each_dev+0x70/0xb4)
> [<c0303798>] (bus_for_each_dev) from [<c0304a74>] (bus_add_driver+0x170/0x204)
> [<c0304a74>] (bus_add_driver) from [<c03063ac>] (driver_register+0x74/0x108)
> [<c03063ac>] (driver_register) from [<c0307390>]
> (__platform_driver_probe+0x58/0x124)
> [<c0307390>] (__platform_driver_probe) from [<c000a604>]
> (do_one_initcall+0x48/0x1a0)
> [<c000a604>] (do_one_initcall) from [<c06b3dd4>]
> (kernel_init_freeable+0x104/0x1c8)
> [<c06b3dd4>] (kernel_init_freeable) from [<c04fcd24>] (kernel_init+0x8/0xf4)
> [<c04fcd24>] (kernel_init) from [<c00090e0>] (ret_from_fork+0x14/0x34)
> (...)
> Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
> 
> After looking at it I realized that if I uncomment this line:
> 
> // fsmc_ce_ctrl(host, false);
> 
> The driver works fine, so just holding CE enabled all the time makes
> everything work.
> 
> I suspect it is some kind of timing issue maybe platform- or electronics
> dependent where you need to hold CE enabled for a while before
> reading pages from the NAND. This would explain why it is not seen
> in the platform this was developed on.
> 
> I will experiment with some delay valued and try to read some data
> sheets but if you already have hints on how to deal with this I'd
> like to hear!

Might be caused by a missing barrier: when de-asserting the CE line, we
must make sure all accesses to the ->data_va range have been done.
Can you try with the following diff applied?

--->8---
diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c
index 325b4414dccc..264d809c2d37 100644
--- a/drivers/mtd/nand/raw/fsmc_nand.c
+++ b/drivers/mtd/nand/raw/fsmc_nand.c
@@ -598,16 +598,21 @@ static void fsmc_ce_ctrl(struct fsmc_nand_data *host, bool assert)
 {
        u32 pc = readl(host->regs_va + FSMC_PC);
 
-       if (!assert)
+       if (!assert) {
+               /*
+                * Make sure all previous read/write have been done before
+                * de-asserting the CE line.
+                */
+               mb();
                writel_relaxed(pc & ~FSMC_ENABLE, host->regs_va + FSMC_PC);
-       else
+       } else {
                writel_relaxed(pc | FSMC_ENABLE, host->regs_va + FSMC_PC);
-
-       /*
-        * nCE line changes must be applied before returning from this
-        * function.
-        */
-       mb();
+               /*
+                * nCE assertion must be applied before returning from this
+                * function.
+                */
+               mb();
+       }
 }
 
 /*
Linus Walleij Jan. 9, 2019, 8:41 p.m. UTC | #3
On Wed, Jan 9, 2019 at 8:45 PM Boris Brezillon <bbrezillon@kernel.org> wrote:
> [Me]
> > I will experiment with some delay valued and try to read some data
> > sheets but if you already have hints on how to deal with this I'd
> > like to hear!
>
> Might be caused by a missing barrier: when de-asserting the CE line, we
> must make sure all accesses to the ->data_va range have been done.
> Can you try with the following diff applied?

Tried it, but it sadly does not help :/

The machine crashes at the same place when doing ther first
64 bytes read.

I suppose the old code would hold CE enabled across all the
commands?

Since the old code contained this:

       /* Support only one CS */
       if (chipnr > 0)
               return;

I guess that is normal for FSMC: only one CS. It seems a bit
aggressive to toggle CS on/off between every command like this,
I suspect the FSMC isn't really built for that but shakes apart
or something.

I will send a version of your patch that keeps CS enabled
for your consideration. (worksforme).

Yours,
Linus Walleij
Boris Brezillon Jan. 9, 2019, 8:54 p.m. UTC | #4
On Wed, 9 Jan 2019 21:41:24 +0100
Linus Walleij <linus.walleij@linaro.org> wrote:

> On Wed, Jan 9, 2019 at 8:45 PM Boris Brezillon <bbrezillon@kernel.org> wrote:
> > [Me]  
> > > I will experiment with some delay valued and try to read some data
> > > sheets but if you already have hints on how to deal with this I'd
> > > like to hear!  
> >
> > Might be caused by a missing barrier: when de-asserting the CE line, we
> > must make sure all accesses to the ->data_va range have been done.
> > Can you try with the following diff applied?  
> 
> Tried it, but it sadly does not help :/
> 
> The machine crashes at the same place when doing ther first
> 64 bytes read.
> 
> I suppose the old code would hold CE enabled across all the
> commands?

For a read/write page accesses, yes.

> 
> Since the old code contained this:
> 
>        /* Support only one CS */
>        if (chipnr > 0)
>                return;
> 
> I guess that is normal for FSMC: only one CS. It seems a bit
> aggressive to toggle CS on/off between every command like this,
> I suspect the FSMC isn't really built for that but shakes apart
> or something.

Hm, that would be weird. There's indeed timing constraints on the NAND
chip side, but none infringing those constraints should not trigger an
external abort exception. Can you check which phys range is remapped at
0xcc960000?

> 
> I will send a version of your patch that keeps CS enabled
> for your consideration. (worksforme).

I guess that's fine to keep CE enabled if the bus is not shared with
other memories.
Linus Walleij Jan. 9, 2019, 9:30 p.m. UTC | #5
On Wed, Jan 9, 2019 at 9:54 PM Boris Brezillon <bbrezillon@kernel.org> wrote:
> [Me]
> > I guess that is normal for FSMC: only one CS. It seems a bit
> > aggressive to toggle CS on/off between every command like this,
> > I suspect the FSMC isn't really built for that but shakes apart
> > or something.
>
> Hm, that would be weird. There's indeed timing constraints on the NAND
> chip side, but none infringing those constraints should not trigger an
> external abort exception.

The manual contains this (the same bit, just another name than
in the driver):

PBKEN PC-card/NAND-Flash chip-select enable.
Enables the corresponding chip-select.
If a disabled chip-select is accessed, an HRESP = ERROR is generated
on the AHB bus.
0: disabled (default after reset)
1: enabled

> Can you check which phys range is remapped at
> 0xcc960000?

No idea how to do that but I'll see if I can figure it out...

Yours,
Linus Walleij
Boris Brezillon Jan. 9, 2019, 9:52 p.m. UTC | #6
On Wed, 9 Jan 2019 22:30:18 +0100
Linus Walleij <linus.walleij@linaro.org> wrote:

> On Wed, Jan 9, 2019 at 9:54 PM Boris Brezillon <bbrezillon@kernel.org> wrote:
> > [Me]  
> > > I guess that is normal for FSMC: only one CS. It seems a bit
> > > aggressive to toggle CS on/off between every command like this,
> > > I suspect the FSMC isn't really built for that but shakes apart
> > > or something.  
> >
> > Hm, that would be weird. There's indeed timing constraints on the NAND
> > chip side, but none infringing those constraints should not trigger an
> > external abort exception.  
> 
> The manual contains this (the same bit, just another name than
> in the driver):
> 
> PBKEN PC-card/NAND-Flash chip-select enable.
> Enables the corresponding chip-select.
> If a disabled chip-select is accessed, an HRESP = ERROR is generated
> on the AHB bus.
> 0: disabled (default after reset)
> 1: enabled

It's still not clear what this bit does. Do you have access to the CS
pin on the board?

> 
> > Can you check which phys range is remapped at
> > 0xcc960000?  
> 
> No idea how to do that but I'll see if I can figure it out...

print the __iomem values returned by devm_ioremap_resource() and find
the one that matches this address.
Linus Walleij Jan. 9, 2019, 10 p.m. UTC | #7
On Wed, Jan 9, 2019 at 10:52 PM Boris Brezillon <bbrezillon@kernel.org> wrote:
> [Me]

> > The manual contains this (the same bit, just another name than
> > in the driver):
> >
> > PBKEN PC-card/NAND-Flash chip-select enable.
> > Enables the corresponding chip-select.
> > If a disabled chip-select is accessed, an HRESP = ERROR is generated
> > on the AHB bus.
> > 0: disabled (default after reset)
> > 1: enabled
>
> It's still not clear what this bit does. Do you have access to the CS
> pin on the board?

Sadly no...

> > > Can you check which phys range is remapped at
> > > 0xcc960000?
> >
> > No idea how to do that but I'll see if I can figure it out...
>
> print the __iomem values returned by devm_ioremap_resource() and find
> the one that matches this address.

Ah of course. This is the first range, host->data_va
"nand_data" I.e. the NAND memory itself.

Yours,
Linus Walleij
Marc Gonzalez Jan. 9, 2019, 10:20 p.m. UTC | #8
On 09/01/2019 20:44, Boris Brezillon wrote:

> Might be caused by a missing barrier: when de-asserting the CE line, we
> must make sure all accesses to the ->data_va range have been done.
> Can you try with the following diff applied?
> 
> --->8---
> diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c
> index 325b4414dccc..264d809c2d37 100644
> --- a/drivers/mtd/nand/raw/fsmc_nand.c
> +++ b/drivers/mtd/nand/raw/fsmc_nand.c
> @@ -598,16 +598,21 @@ static void fsmc_ce_ctrl(struct fsmc_nand_data *host, bool assert)
>  {
>         u32 pc = readl(host->regs_va + FSMC_PC);
>  
> -       if (!assert)
> +       if (!assert) {
> +               /*
> +                * Make sure all previous read/write have been done before
> +                * de-asserting the CE line.
> +                */
> +               mb();
>                 writel_relaxed(pc & ~FSMC_ENABLE, host->regs_va + FSMC_PC);
> -       else
> +       } else {
>                 writel_relaxed(pc | FSMC_ENABLE, host->regs_va + FSMC_PC);
> -
> -       /*
> -        * nCE line changes must be applied before returning from this
> -        * function.
> -        */
> -       mb();
> +               /*
> +                * nCE assertion must be applied before returning from this
> +                * function.
> +                */
> +               mb();
> +       }

+ Will Deacon, an expert on arm mmio shenanigans.
e.g. https://elinux.org/images/a/a8/Uh-oh-Its-IO-Ordering-Will-Deacon-Arm.pdf

As far as I understand (which isn't very far), a memory barrier (however
strong) cannot guarantee that all (posted) writes have reached a device
(unless the underlying bus explicitly provides such guarantees?)

At best, a memory barrier guarantees that the mmio writes have "left" the CPU
and its "coherency fabric" (shared LLC). Thus subsequent accesses are
guaranteed to appear "later" on the bus. (Much hand-waving sorry)

I've been told that the one true way to ensure that a posted write has reached
a device is simply to read the value back, i.e. writel+readl.

If I'm wrong, I'd like to be schooled :-)

Regards.
Boris Brezillon Jan. 10, 2019, 8:32 a.m. UTC | #9
On Wed, 9 Jan 2019 23:20:40 +0100
Marc Gonzalez <marc.w.gonzalez@free.fr> wrote:

> On 09/01/2019 20:44, Boris Brezillon wrote:
> 
> > Might be caused by a missing barrier: when de-asserting the CE line, we
> > must make sure all accesses to the ->data_va range have been done.
> > Can you try with the following diff applied?
> >   
> > --->8---  
> > diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c
> > index 325b4414dccc..264d809c2d37 100644
> > --- a/drivers/mtd/nand/raw/fsmc_nand.c
> > +++ b/drivers/mtd/nand/raw/fsmc_nand.c
> > @@ -598,16 +598,21 @@ static void fsmc_ce_ctrl(struct fsmc_nand_data *host, bool assert)
> >  {
> >         u32 pc = readl(host->regs_va + FSMC_PC);
> >  
> > -       if (!assert)
> > +       if (!assert) {
> > +               /*
> > +                * Make sure all previous read/write have been done before
> > +                * de-asserting the CE line.
> > +                */
> > +               mb();
> >                 writel_relaxed(pc & ~FSMC_ENABLE, host->regs_va + FSMC_PC);
> > -       else
> > +       } else {
> >                 writel_relaxed(pc | FSMC_ENABLE, host->regs_va + FSMC_PC);
> > -
> > -       /*
> > -        * nCE line changes must be applied before returning from this
> > -        * function.
> > -        */
> > -       mb();
> > +               /*
> > +                * nCE assertion must be applied before returning from this
> > +                * function.
> > +                */
> > +               mb();
> > +       }  
> 
> + Will Deacon, an expert on arm mmio shenanigans.
> e.g. https://elinux.org/images/a/a8/Uh-oh-Its-IO-Ordering-Will-Deacon-Arm.pdf
> 
> As far as I understand (which isn't very far), a memory barrier (however
> strong) cannot guarantee that all (posted) writes have reached a device
> (unless the underlying bus explicitly provides such guarantees?)
> 
> At best, a memory barrier guarantees that the mmio writes have "left" the CPU
> and its "coherency fabric" (shared LLC). Thus subsequent accesses are
> guaranteed to appear "later" on the bus. (Much hand-waving sorry)

Yes, that's what I was trying to enforce.
diff mbox series

Patch

diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c
index 41ba76efd6c8..ea69ac6e6d7a 100644
--- a/drivers/mtd/nand/raw/fsmc_nand.c
+++ b/drivers/mtd/nand/raw/fsmc_nand.c
@@ -609,22 +609,19 @@  static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf,
 }
 
 /* fsmc_select_chip - assert or deassert nCE */
-static void fsmc_select_chip(struct nand_chip *chip, int chipnr)
+static void fsmc_ce_ctrl(struct fsmc_nand_data *host, bool assert)
 {
-	struct fsmc_nand_data *host = mtd_to_fsmc(nand_to_mtd(chip));
-	u32 pc;
-
-	/* Support only one CS */
-	if (chipnr > 0)
-		return;
+	u32 pc = readl(host->regs_va + FSMC_PC);
 
-	pc = readl(host->regs_va + FSMC_PC);
-	if (chipnr < 0)
+	if (!assert)
 		writel_relaxed(pc & ~FSMC_ENABLE, host->regs_va + FSMC_PC);
 	else
 		writel_relaxed(pc | FSMC_ENABLE, host->regs_va + FSMC_PC);
 
-	/* nCE line must be asserted before starting any operation */
+	/*
+	 * nCE line changes must be applied before returning from this
+	 * function.
+	 */
 	mb();
 }
 
@@ -645,6 +642,9 @@  static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op,
 	int i;
 
 	pr_debug("Executing operation [%d instructions]:\n", op->ninstrs);
+
+	fsmc_ce_ctrl(host, true);
+
 	for (op_id = 0; op_id < op->ninstrs; op_id++) {
 		instr = &op->instrs[op_id];
 
@@ -701,6 +701,8 @@  static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op,
 		}
 	}
 
+	fsmc_ce_ctrl(host, false);
+
 	return ret;
 }
 
@@ -1081,7 +1083,6 @@  static int __init fsmc_nand_probe(struct platform_device *pdev)
 
 	mtd->dev.parent = &pdev->dev;
 	nand->exec_op = fsmc_exec_op;
-	nand->select_chip = fsmc_select_chip;
 
 	/*
 	 * Setup default ECC mode. nand_dt_init() called from nand_scan_ident()