diff --git a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts index 9afb4e018593..97c863eb5e62 100644 --- a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts +++ b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts @@ -147,6 +147,63 @@ status = "okay"; }; +&nfc { + pinctrl-names = "default"; + pinctrl-0 = <&nand_pins_a &nand_cs0_pins_a &nand_rb0_pins_a>; + status = "okay"; + + nand@0 { + #address-cells = <2>; + #size-cells = <2>; + reg = <0>; + allwinner,rb = <0>; + + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-on-flash-bbt; + + boot0@0 { + label = "boot0"; + reg = /bits/ 64 <0x0 0x200000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + boot0-rescue@200000 { + label = "boot0-rescue"; + reg = /bits/ 64 <0x200000 0x200000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + main@200000 { + label = "main"; + reg = /bits/ 64 <0x400000 0xffc00000>; + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 < + 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72 + 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436 + 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d + 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130 + 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56 + 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55 + 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb + 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17 + 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62 + 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064 + 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126 + 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e + 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3 + 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b + 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d + 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>; + }; + }; +}; + &ohci0 { status = "okay"; }; diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi index c48f32028b77..2782ac62aba0 100644 --- a/arch/arm/boot/dts/sun4i-a10.dtsi +++ b/arch/arm/boot/dts/sun4i-a10.dtsi @@ -516,6 +516,17 @@ #dma-cells = <2>; }; + nfc: nand@01c03000 { + compatible = "allwinner,sun4i-a10-nand"; + reg = <0x01c03000 0x1000>; + interrupts = <37>; + clocks = <&ahb_gates 13>, <&nand_clk>; + clock-names = "ahb", "mod"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + spi0: spi@01c05000 { compatible = "allwinner,sun4i-a10-spi"; reg = <0x01c05000 0x1000>; @@ -913,6 +924,86 @@ allwinner,drive = ; allwinner,pull = ; }; + + nand_pins_a: nand_base0@0 { + allwinner,pins = "PC0", "PC1", "PC2", + "PC5", "PC8", "PC9", "PC10", + "PC11", "PC12", "PC13", "PC14", + "PC15", "PC16"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs0_pins_a: nand_cs@0 { + allwinner,pins = "PC4"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs1_pins_a: nand_cs@1 { + allwinner,pins = "PC3"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs2_pins_a: nand_cs@2 { + allwinner,pins = "PC17"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs3_pins_a: nand_cs@3 { + allwinner,pins = "PC18"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs4_pins_a: nand_cs@4 { + allwinner,pins = "PC19"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs5_pins_a: nand_cs@5 { + allwinner,pins = "PC20"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs6_pins_a: nand_cs@6 { + allwinner,pins = "PC21"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs7_pins_a: nand_cs@7 { + allwinner,pins = "PC22"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_rb0_pins_a: nand_rb@0 { + allwinner,pins = "PC6"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_rb1_pins_a: nand_rb@1 { + allwinner,pins = "PC7"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; }; timer@01c20c00 { diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi index 7ce79f566eff..74f4da129c9d 100644 --- a/arch/arm/boot/dts/sun5i-a10s.dtsi +++ b/arch/arm/boot/dts/sun5i-a10s.dtsi @@ -194,6 +194,20 @@ allwinner,drive = ; allwinner,pull = ; }; + + nand_cs2_pins_a: nand_cs@2 { + allwinner,pins = "PC17"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs3_pins_a: nand_cs@3 { + allwinner,pins = "PC18"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; }; &sram_a { diff --git a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts index 42324005eb7c..40c245575e47 100644 --- a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts +++ b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts @@ -155,6 +155,65 @@ status = "okay"; }; +&nfc { + pinctrl-names = "default"; + pinctrl-0 = <&nand_pins_a &nand_cs0_pins_a &nand_rb0_pins_a>; + status = "okay"; + + nand@0 { + #address-cells = <2>; + #size-cells = <2>; + reg = <0>; + allwinner,rb = <0>; + + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-ecc-strength = <40>; + nand-ecc-step-size = <1024>; + nand-on-flash-bbt; + + boot0@0 { + label = "boot0"; + reg = /bits/ 64 <0x0 0x200000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + boot0-rescue@200000 { + label = "boot0-rescue"; + reg = /bits/ 64 <0x200000 0x200000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + main@200000 { + label = "main"; + reg = /bits/ 64 <0x400000 0xffc00000>; + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 < + 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72 + 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436 + 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d + 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130 + 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56 + 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55 + 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb + 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17 + 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62 + 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064 + 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126 + 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e + 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3 + 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b + 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d + 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>; + }; + }; +}; + &ohci0 { status = "okay"; }; diff --git a/arch/arm/boot/dts/sun5i-r8-chip.dts b/arch/arm/boot/dts/sun5i-r8-chip.dts index 1cf6084dca68..8f8edbbd3d84 100644 --- a/arch/arm/boot/dts/sun5i-r8-chip.dts +++ b/arch/arm/boot/dts/sun5i-r8-chip.dts @@ -269,6 +269,140 @@ status = "okay"; }; +&nfc { + pinctrl-names = "default"; + pinctrl-0 = <&nand_pins_a &nand_cs0_pins_a &nand_rb0_pins_a>; + status = "okay"; + + nand@0 { + #address-cells = <2>; + #size-cells = <2>; + reg = <0>; + allwinner,rb = <0>; + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-ecc-step-size = <1024>; + nand-ecc-strength = <56>; + nand-on-flash-bbt; + nand-randomizer-seeds = /bits/ 16 < + 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72 + 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436 + 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d + 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130 + 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56 + 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55 + 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb + 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17 + 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62 + 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064 + 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126 + 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e + 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3 + 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b + 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d + 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>; + + spl@0 { + label = "SPL"; + reg = /bits/ 64 <0x0 0x400000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-ecc-step-size = <1024>; + nand-ecc-strength = <40>; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + spl-backup@400000 { + label = "SPL.backup"; + reg = /bits/ 64 <0x400000 0x400000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-ecc-step-size = <1024>; + nand-ecc-strength = <40>; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + u-boot@800000 { + label = "U-Boot"; + reg = /bits/ 64 <0x800000 0x400000>; + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-ecc-step-size = <1024>; + nand-ecc-strength = <56>; + nand-randomizer-seeds = /bits/ 16 < + 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72 + 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436 + 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d + 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130 + 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56 + 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55 + 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb + 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17 + 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62 + 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064 + 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126 + 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e + 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3 + 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b + 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d + 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>; + }; + + env@c00000 { + label = "env"; + reg = /bits/ 64 <0xc00000 0x400000>; + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-ecc-step-size = <1024>; + nand-ecc-strength = <56>; + nand-randomizer-seeds = /bits/ 16 < + 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72 + 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436 + 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d + 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130 + 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56 + 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55 + 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb + 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17 + 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62 + 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064 + 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126 + 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e + 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3 + 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b + 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d + 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>; + }; + + rootfs@1000000 { + label = "rootfs"; + reg = /bits/ 64 <0x1000000 0x1ff000000>; + slc-mode; + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-ecc-step-size = <1024>; + nand-ecc-strength = <56>; + nand-randomizer-seeds = /bits/ 16 < + 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72 + 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436 + 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d + 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130 + 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56 + 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55 + 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb + 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17 + 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62 + 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064 + 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126 + 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e + 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3 + 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b + 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d + 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>; + }; + }; +}; + &ohci0 { status = "okay"; }; diff --git a/arch/arm/boot/dts/sun5i.dtsi b/arch/arm/boot/dts/sun5i.dtsi index dc4247c181a1..40195a33b6e9 100644 --- a/arch/arm/boot/dts/sun5i.dtsi +++ b/arch/arm/boot/dts/sun5i.dtsi @@ -354,6 +354,17 @@ #dma-cells = <2>; }; + nfc: nand@01c03000 { + compatible = "allwinner,sun4i-a10-nand"; + reg = <0x01c03000 0x1000>; + interrupts = <37>; + clocks = <&ahb_gates 13>, <&nand_clk>; + clock-names = "ahb", "mod"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + spi0: spi@01c05000 { compatible = "allwinner,sun4i-a10-spi"; reg = <0x01c05000 0x1000>; @@ -546,6 +557,44 @@ allwinner,pull = ; }; + nand_pins_a: nand_base0@0 { + allwinner,pins = "PC0", "PC1", "PC2", + "PC5", "PC8", "PC9", "PC10", + "PC11", "PC12", "PC13", "PC14", + "PC15"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs0_pins_a: nand_cs@0 { + allwinner,pins = "PC4"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs1_pins_a: nand_cs@1 { + allwinner,pins = "PC3"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_rb0_pins_a: nand_rb@0 { + allwinner,pins = "PC6"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_rb1_pins_a: nand_rb@1 { + allwinner,pins = "PC7"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + uart3_pins_a: uart3@0 { allwinner,pins = "PG9", "PG10"; allwinner,function = "uart3"; diff --git a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts index 39a51d5143f7..f35957d1b8c4 100644 --- a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts +++ b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts @@ -142,6 +142,63 @@ status = "okay"; }; +&nfc { + pinctrl-names = "default"; + pinctrl-0 = <&nand_pins_a>, <&nand_cs0_pins_a>, <&nand_rb0_pins_a>; + status = "okay"; + + nand@0 { + #address-cells = <2>; + #size-cells = <2>; + reg = <0>; + allwinner,rb = <0>; + + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-on-flash-bbt; + + boot0@0 { + label = "boot0"; + reg = /bits/ 64 <0x0 0x200000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + boot0-rescue@200000 { + label = "boot0-rescue"; + reg = /bits/ 64 <0x200000 0x200000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + main@200000 { + label = "main"; + reg = /bits/ 64 <0x400000 0xffc00000>; + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 < + 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72 + 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436 + 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d + 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130 + 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56 + 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55 + 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb + 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17 + 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62 + 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064 + 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126 + 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e + 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3 + 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b + 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d + 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>; + }; + }; +}; + &ohci0 { status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts index 70d0f85a636d..78078fdb34d6 100644 --- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts @@ -191,6 +191,61 @@ allwinner,pull = ; }; +&nfc { + pinctrl-names = "default"; + pinctrl-0 = <&nand_pins_a>, <&nand_cs0_pins_a>, <&nand_rb0_pins_a>; + status = "okay"; + + nand@0 { + #address-cells = <2>; + #size-cells = <2>; + reg = <0>; + allwinner,rb = <0>; + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + + boot0@0 { + label = "boot0"; + reg = /bits/ 64 <0x0 0x200000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + boot0-rescue@200000 { + label = "boot0-rescue"; + reg = /bits/ 64 <0x200000 0x200000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + main@200000 { + label = "main"; + reg = /bits/ 64 <0x400000 0x1ffc00000>; + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 < + 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72 + 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436 + 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d + 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130 + 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56 + 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55 + 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb + 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17 + 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62 + 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064 + 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126 + 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e + 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3 + 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b + 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d + 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>; + }; + }; +}; + &ohci0 { status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi index 479304f93c56..09c46a95039e 100644 --- a/arch/arm/boot/dts/sun7i-a20.dtsi +++ b/arch/arm/boot/dts/sun7i-a20.dtsi @@ -604,6 +604,17 @@ #dma-cells = <2>; }; + nfc: nand@01c03000 { + compatible = "allwinner,sun4i-a10-nand"; + reg = <0x01c03000 0x1000>; + interrupts = <0 37 4>; + clocks = <&ahb_gates 13>, <&nand_clk>; + clock-names = "ahb", "mod"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + spi0: spi@01c05000 { compatible = "allwinner,sun4i-a10-spi"; reg = <0x01c05000 0x1000>; @@ -1106,6 +1117,86 @@ allwinner,drive = ; allwinner,pull = ; }; + + nand_pins_a: nand_base0@0 { + allwinner,pins = "PC0", "PC1", "PC2", + "PC5", "PC8", "PC9", "PC10", + "PC11", "PC12", "PC13", "PC14", + "PC15", "PC16"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs0_pins_a: nand_cs@0 { + allwinner,pins = "PC4"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs1_pins_a: nand_cs@1 { + allwinner,pins = "PC3"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs2_pins_a: nand_cs@2 { + allwinner,pins = "PC17"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs3_pins_a: nand_cs@3 { + allwinner,pins = "PC18"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs4_pins_a: nand_cs@4 { + allwinner,pins = "PC19"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs5_pins_a: nand_cs@5 { + allwinner,pins = "PC20"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs6_pins_a: nand_cs@6 { + allwinner,pins = "PC21"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs7_pins_a: nand_cs@7 { + allwinner,pins = "PC22"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_rb0_pins_a: nand_rb@0 { + allwinner,pins = "PC6"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_rb1_pins_a: nand_rb@1 { + allwinner,pins = "PC7"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; }; timer@01c20c00 { diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 5b2806a7e5f7..08f1ec28bfad 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -22,6 +22,10 @@ menuconfig MTD_NAND if MTD_NAND +config MTD_OF_NAND_PARTS + tristate + default n + config MTD_NAND_BCH tristate select BCH @@ -529,6 +533,7 @@ config MTD_NAND_XWAY config MTD_NAND_SUNXI tristate "Support for NAND on Allwinner SoCs" depends on ARCH_SUNXI + select MTD_OF_NAND_PARTS help Enables support for NAND Flash chips on Allwinner SoCs. diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 1f897ec3c242..4f2139e6e3e4 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_MTD_NAND) += nand.o obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o obj-$(CONFIG_MTD_NAND_BCH) += nand_bch.o -obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o +obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o nand_hynix.o obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o @@ -54,4 +54,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_OF_NAND_PARTS) += ofnandpart.o + nand-objs := nand_base.o nand_bbt.o nand_timings.o diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index ceb68ca8277a..c1b86cf30773 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -97,6 +97,22 @@ static int nand_get_device(struct mtd_info *mtd, int new_state); static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops); +static void nand_set_slc_mode(struct mtd_info *mtd, int chipnr, bool enable) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->slc_mode == enable) + return; + + chip->slc_mode = enable; + chip->pagebuf = -1; + if (chip->set_slc_mode) { + chip->select_chip(mtd, chipnr); + chip->set_slc_mode(mtd); + chip->select_chip(mtd, -1); + } +} + /* * For devices which display every fart in the system on a separate LED. Is * compiled away when LED support is disabled. @@ -107,16 +123,20 @@ static int check_offs_len(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct nand_chip *chip = mtd->priv; + int shift = chip->phys_erase_shift; int ret = 0; + if (chip->slc_mode) + shift--; + /* Start address must align on block boundary */ - if (ofs & ((1ULL << chip->phys_erase_shift) - 1)) { + if (ofs & ((1ULL << shift) - 1)) { pr_debug("%s: unaligned address\n", __func__); ret = -EINVAL; } /* Length must align on block boundary */ - if (len & ((1ULL << chip->phys_erase_shift) - 1)) { + if (len & ((1ULL << shift) - 1)) { pr_debug("%s: length not block aligned\n", __func__); ret = -EINVAL; } @@ -517,6 +537,9 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, if (!chip->bbt) return chip->block_bad(mtd, ofs, getchip); + if (chip->slc_mode) + ofs *= 2; + /* Return info from the table */ return nand_isbad_bbt(mtd, ofs, allowbbt); } @@ -724,6 +747,12 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, chip->cmd_ctrl(mtd, column >> 8, ctrl); } if (page_addr != -1) { + if (chip->slc_mode && chip->fix_page && + (command == NAND_CMD_ERASE1 || + command == NAND_CMD_READ0 || + command == NAND_CMD_SEQIN)) + chip->fix_page(mtd, &page_addr); + chip->cmd_ctrl(mtd, page_addr, ctrl); chip->cmd_ctrl(mtd, page_addr >> 8, NAND_NCE | NAND_ALE); @@ -1100,6 +1129,202 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) } EXPORT_SYMBOL(nand_lock); +/** + * nand_rnd_is_activ - check wether a region of a NAND page requires NAND + * randomizer to be disabled + * @mtd: mtd info + * @page: NAND page + * @column: offset within the page + * @len: len of the region + * + * Returns 1 if the randomizer should be enabled, 0 if not, or -ERR in case of + * error. + * + * In case of success len will contain the size of the region: + * - if the requested region fits in a NAND random region len will not change + * - else len will be replaced by the available length within the NAND random + * region + */ +int nand_rnd_is_activ(struct mtd_info *mtd, int page, int column, int *len) +{ + struct nand_chip *chip = mtd->priv; + struct nand_rnd_layout *layout = chip->cur_rnd->layout; + struct nand_rndfree *range; + int ret = 1; + int tmp; + int i; + + if (!len || *len < 0 || column < 0 || + column + *len > mtd->writesize + mtd->oobsize) + return -EINVAL; + + if (layout) { + for (i = 0; i < layout->nranges; i++) { + range = &layout->ranges[i]; + if (column + *len <= range->offset) { + break; + } else if (column >= range->offset + range->length) { + continue; + } else if (column < range->offset) { + tmp = range->offset - column; + if (*len > tmp) + *len = tmp; + break; + } else { + tmp = range->offset + range->length - column; + if (*len > tmp) + *len = tmp; + ret = 0; + break; + } + + } + } + + return ret; +} +EXPORT_SYMBOL(nand_rnd_is_activ); + +/** + * nand_page_is_empty - check wether a NAND page contains only FFs + * @mtd: mtd info + * @data: data buffer + * @oob: oob buffer + * + * Reads the data stored in the databuf buffer and check if it contains only + * FFs. + * + * Return true if it does else return false. + */ +bool nand_page_is_empty(struct mtd_info *mtd, void *data, void *oob) +{ + u8 *buf; + int length; + u32 pattern = 0xffffffff; + int bitflips = 0; + int cnt; + + buf = data; + length = mtd->writesize; + while (length) { + cnt = length < sizeof(pattern) ? length : sizeof(pattern); + if (memcmp(&pattern, buf, cnt)) { + int i; + for (i = 0; i < cnt * BITS_PER_BYTE; i++) { + if (!(buf[i / BITS_PER_BYTE] & + (1 << (i % BITS_PER_BYTE)))) { + bitflips++; + if (bitflips > mtd->ecc_strength) + return false; + } + } + } + + buf += sizeof(pattern); + length -= sizeof(pattern); + } + + buf = oob; + length = mtd->oobsize; + while (length) { + cnt = length < sizeof(pattern) ? length : sizeof(pattern); + if (memcmp(&pattern, buf, cnt)) { + int i; + for (i = 0; i < cnt * BITS_PER_BYTE; i++) { + if (!(buf[i / BITS_PER_BYTE] & + (1 << (i % BITS_PER_BYTE)))) { + bitflips++; + if (bitflips > mtd->ecc_strength) + return false; + } + } + } + + buf += sizeof(pattern); + length -= sizeof(pattern); + } + + return true; +} +EXPORT_SYMBOL(nand_page_is_empty); + +/** + * nand_page_get_status - retrieve page status from the page status table (pst) + * @mtd: mtd info + * @page: page you want to get status on + * + * Return the page status. + */ +int nand_page_get_status(struct mtd_info *mtd, int page) +{ + struct nand_chip *chip = mtd->priv; + u8 shift; + uint64_t offset; + + if (!chip->pst) + return NAND_PAGE_STATUS_UNKNOWN; + + if (chip->slc_mode) + page *= 2; + + shift = (page % 4) * 2; + offset = page / 4; + + return (chip->pst[offset] >> shift) & 0x3; +} +EXPORT_SYMBOL(nand_page_get_status); + +/** + * nand_page_set_status - assign page status from in the page status table + * @mtd: mtd info + * @page: page you want to get status on + * @status: new status to assign + */ +void nand_page_set_status(struct mtd_info *mtd, int page, + enum nand_page_status status) +{ + struct nand_chip *chip = mtd->priv; + u8 shift; + uint64_t offset; + + if (!chip->pst) + return; + + if (chip->slc_mode) + page *= 2; + + shift = (page % 4) * 2; + offset = page / 4; + chip->pst[offset] &= ~(0x3 << shift); + chip->pst[offset] |= (status & 0x3) << shift; +} +EXPORT_SYMBOL(nand_page_set_status); + +/** + * nand_pst_create - create a page status table + * @mtd: mtd info + * + * Allocate a page status table and assign it to the mtd device. + * + * Returns 0 in case of success or -ERRNO in case of error. + */ +int nand_pst_create(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->pst) + return 0; + + chip->pst = kzalloc(mtd->size >> + (chip->page_shift - mtd->subpage_sft + 2), + GFP_KERNEL); + if (!chip->pst) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL(nand_pst_create); + /** * nand_read_page_raw - [INTERN] read raw page data without ecc * @mtd: mtd info structure @@ -1113,9 +1338,14 @@ EXPORT_SYMBOL(nand_lock); static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - chip->read_buf(mtd, buf, mtd->writesize); - if (oob_required) - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, page, 0, NAND_RND_READ); + nand_rnd_read_buf(mtd, buf, mtd->writesize); + if (oob_required) { + nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_READ); + nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize); + } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); + return 0; } @@ -1133,32 +1363,44 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; + int eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; uint8_t *oob = chip->oob_poi; int steps, size; + int column = 0; - for (steps = chip->ecc.steps; steps > 0; steps--) { - chip->read_buf(mtd, buf, eccsize); + for (steps = chip->cur_ecc->steps; steps > 0; steps--) { + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, buf, eccsize); buf += eccsize; + column += eccsize; - if (chip->ecc.prepad) { - chip->read_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; + if (chip->cur_ecc->prepad) { + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, chip->cur_ecc->prepad); + oob += chip->cur_ecc->prepad; + column += chip->cur_ecc->prepad; } - chip->read_buf(mtd, oob, eccbytes); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, eccbytes); oob += eccbytes; + column += eccbytes; - if (chip->ecc.postpad) { - chip->read_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; + if (chip->cur_ecc->postpad) { + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, chip->cur_ecc->postpad); + oob += chip->cur_ecc->postpad; + column += chip->cur_ecc->postpad; } } size = mtd->oobsize - (oob - chip->oob_poi); - if (size) - chip->read_buf(mtd, oob, size); + if (size) { + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, size); + } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); return 0; } @@ -1174,30 +1416,31 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; uint8_t *p = buf; uint8_t *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; - uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; unsigned int max_bitflips = 0; - chip->ecc.read_page_raw(mtd, chip, buf, 1, page); + chip->cur_ecc->read_page_raw(mtd, chip, buf, 1, page); for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) - chip->ecc.calculate(mtd, p, &ecc_calc[i]); + chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]); - for (i = 0; i < chip->ecc.total; i++) + for (i = 0; i < chip->cur_ecc->total; i++) ecc_code[i] = chip->oob_poi[eccpos[i]]; - eccsteps = chip->ecc.steps; + eccsteps = chip->cur_ecc->steps; p = buf; for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; - stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + stat = chip->cur_ecc->correct(mtd, p, &ecc_code[i], + &ecc_calc[i]); if (stat < 0) { mtd->ecc_stats.failed++; } else { @@ -1222,7 +1465,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, int page) { int start_step, end_step, num_steps; - uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; uint8_t *p; int data_col_addr, i, gaps = 0; int datafrag_len, eccfrag_len, aligned_len, aligned_pos; @@ -1231,26 +1474,28 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, unsigned int max_bitflips = 0; /* Column address within the page aligned to ECC size (256bytes) */ - start_step = data_offs / chip->ecc.size; - end_step = (data_offs + readlen - 1) / chip->ecc.size; + start_step = data_offs / chip->cur_ecc->size; + end_step = (data_offs + readlen - 1) / chip->cur_ecc->size; num_steps = end_step - start_step + 1; - index = start_step * chip->ecc.bytes; + index = start_step * chip->cur_ecc->bytes; /* Data size aligned to ECC ecc.size */ - datafrag_len = num_steps * chip->ecc.size; - eccfrag_len = num_steps * chip->ecc.bytes; + datafrag_len = num_steps * chip->cur_ecc->size; + eccfrag_len = num_steps * chip->cur_ecc->bytes; - data_col_addr = start_step * chip->ecc.size; + data_col_addr = start_step * chip->cur_ecc->size; /* If we read not a page aligned data */ if (data_col_addr != 0) chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1); p = bufpoi + data_col_addr; - chip->read_buf(mtd, p, datafrag_len); + nand_rnd_config(mtd, -1, data_col_addr, NAND_RND_READ); + nand_rnd_read_buf(mtd, p, datafrag_len); /* Calculate ECC */ - for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) - chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]); + for (i = 0; i < eccfrag_len; + i += chip->cur_ecc->bytes, p += chip->cur_ecc->size) + chip->cur_ecc->calculate(mtd, p, &chip->buffers->ecccalc[i]); /* * The performance is faster if we position offsets according to @@ -1264,7 +1509,8 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, } if (gaps) { chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, -1, mtd->writesize, NAND_RND_READ); + nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize); } else { /* * Send the command to read the particular ECC bytes take care @@ -1274,23 +1520,27 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, aligned_len = eccfrag_len; if (eccpos[index] & (busw - 1)) aligned_len++; - if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1)) + if (eccpos[index + (num_steps * chip->cur_ecc->bytes)] & + (busw - 1)) aligned_len++; chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1); - chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); + nand_rnd_config(mtd, -1, mtd->writesize + aligned_pos, NAND_RND_READ); + nand_rnd_read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); } for (i = 0; i < eccfrag_len; i++) chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + index]]; p = bufpoi + data_col_addr; - for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) { + for (i = 0; i < eccfrag_len; + i += chip->cur_ecc->bytes, p += chip->cur_ecc->size) { int stat; - stat = chip->ecc.correct(mtd, p, - &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); + stat = chip->cur_ecc->correct(mtd, p, + &chip->buffers->ecccode[i], + &chip->buffers->ecccalc[i]); if (stat < 0) { mtd->ecc_stats.failed++; } else { @@ -1298,6 +1548,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, max_bitflips = max_t(unsigned int, max_bitflips, stat); } } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); return max_bitflips; } @@ -1314,32 +1565,37 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; uint8_t *p = buf; uint8_t *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; - uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; unsigned int max_bitflips = 0; + int column = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); - chip->ecc.calculate(mtd, p, &ecc_calc[i]); + chip->cur_ecc->hwctl(mtd, NAND_ECC_READ); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, p, eccsize); + chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]); + column += eccsize; } - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize); - for (i = 0; i < chip->ecc.total; i++) + for (i = 0; i < chip->cur_ecc->total; i++) ecc_code[i] = chip->oob_poi[eccpos[i]]; - eccsteps = chip->ecc.steps; + eccsteps = chip->cur_ecc->steps; p = buf; for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; - stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + stat = chip->cur_ecc->correct(mtd, p, &ecc_code[i], + &ecc_calc[i]); if (stat < 0) { mtd->ecc_stats.failed++; } else { @@ -1347,6 +1603,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, max_bitflips = max_t(unsigned int, max_bitflips, stat); } } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); return max_bitflips; } @@ -1367,31 +1624,35 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; uint8_t *p = buf; uint8_t *ecc_code = chip->buffers->ecccode; - uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; uint8_t *ecc_calc = chip->buffers->ecccalc; unsigned int max_bitflips = 0; + int column = 0; /* Read the OOB area first */ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_READ); + nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize); chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + column = 0; - for (i = 0; i < chip->ecc.total; i++) + for (i = 0; i < chip->cur_ecc->total; i++) ecc_code[i] = chip->oob_poi[eccpos[i]]; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; - chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); - chip->ecc.calculate(mtd, p, &ecc_calc[i]); + chip->cur_ecc->hwctl(mtd, NAND_ECC_READ); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, p, eccsize); + chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]); - stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); + stat = chip->cur_ecc->correct(mtd, p, &ecc_code[i], NULL); if (stat < 0) { mtd->ecc_stats.failed++; } else { @@ -1399,6 +1660,7 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, max_bitflips = max_t(unsigned int, max_bitflips, stat); } } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); return max_bitflips; } @@ -1416,27 +1678,34 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; uint8_t *p = buf; uint8_t *oob = chip->oob_poi; unsigned int max_bitflips = 0; + int column = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; - chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); + chip->cur_ecc->hwctl(mtd, NAND_ECC_READ); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, p, eccsize); + column += eccsize; - if (chip->ecc.prepad) { - chip->read_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; + if (chip->cur_ecc->prepad) { + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, chip->cur_ecc->prepad); + oob += chip->cur_ecc->prepad; } - chip->ecc.hwctl(mtd, NAND_ECC_READSYN); - chip->read_buf(mtd, oob, eccbytes); - stat = chip->ecc.correct(mtd, p, oob, NULL); + chip->cur_ecc->hwctl(mtd, NAND_ECC_READSYN); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, eccbytes); + column += eccbytes; + + stat = chip->cur_ecc->correct(mtd, p, oob, NULL); if (stat < 0) { mtd->ecc_stats.failed++; @@ -1447,30 +1716,37 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, oob += eccbytes; - if (chip->ecc.postpad) { - chip->read_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; + if (chip->cur_ecc->postpad) { + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, chip->cur_ecc->postpad); + column += chip->cur_ecc->postpad; + oob += chip->cur_ecc->postpad; } } /* Calculate remaining oob bytes */ i = mtd->oobsize - (oob - chip->oob_poi); - if (i) - chip->read_buf(mtd, oob, i); + if (i) { + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, i); + } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); return max_bitflips; } /** * nand_transfer_oob - [INTERN] Transfer oob to client buffer - * @chip: nand chip structure + * @mtd: mtd structure * @oob: oob destination address * @ops: oob ops structure * @len: size of oob to transfer */ -static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, +static uint8_t *nand_transfer_oob(struct mtd_info *mtd, uint8_t *oob, struct mtd_oob_ops *ops, size_t len) { + struct nand_chip *chip = mtd->priv; + switch (ops->mode) { case MTD_OPS_PLACE_OOB: @@ -1479,7 +1755,7 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, return oob + len; case MTD_OPS_AUTO_OOB: { - struct nand_oobfree *free = chip->ecc.layout->oobfree; + struct nand_oobfree *free = chip->cur_ecc->layout->oobfree; uint32_t boffs = 0, roffs = ops->ooboffs; size_t bytes = 0; @@ -1598,18 +1874,25 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, * Now read the page into the buffer. Absent an error, * the read methods return max bitflips per ecc step. */ + nand_rnd_config(mtd, page, -1, NAND_RND_READ); if (unlikely(ops->mode == MTD_OPS_RAW)) - ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, - oob_required, - page); + ret = chip->cur_ecc->read_page_raw(mtd, chip, + bufpoi, + oob_required, + page); else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) && !oob) - ret = chip->ecc.read_subpage(mtd, chip, - col, bytes, bufpoi, - page); + ret = chip->cur_ecc->read_subpage(mtd, chip, + col, bytes, + bufpoi, + page); else - ret = chip->ecc.read_page(mtd, chip, bufpoi, - oob_required, page); + ret = chip->cur_ecc->read_page(mtd, chip, + bufpoi, + oob_required, + page); + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); + if (ret < 0) { if (use_bufpoi) /* Invalidate page cache */ @@ -1637,8 +1920,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, int toread = min(oobreadlen, max_oobsize); if (toread) { - oob = nand_transfer_oob(chip, - oob, ops, toread); + oob = nand_transfer_oob(mtd, oob, ops, + toread); oobreadlen -= toread; } } @@ -1744,6 +2027,45 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, return ret; } +/** + * nand_part_read - [MTD Interface] MTD compatibility function for nand_do_read_ecc + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * + * Get hold of the chip and call nand_do_read. + */ +static int nand_part_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, uint8_t *buf) +{ + struct nand_chip *chip = mtd->priv; + struct nand_part *part = to_nand_part(mtd); + struct mtd_oob_ops ops; + int chipnr; + int ret; + + from += part->slc_mode ? part->offset / 2 : part->offset; + nand_get_device(part->master, FL_READING); + if (part->ecc) + chip->cur_ecc = part->ecc; + if (part->rnd) + chip->cur_rnd = part->rnd; + chipnr = from >> (chip->chip_shift - (part->slc_mode ? 1 : 0)); + nand_set_slc_mode(mtd, chipnr, part->slc_mode); + ops.len = len; + ops.datbuf = buf; + ops.oobbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + ret = nand_do_read_ops(part->master, from, &ops); + *retlen = ops.retlen; + chip->cur_rnd = &chip->rnd; + chip->cur_ecc = &chip->ecc; + nand_release_device(part->master); + return ret; +} + /** * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function * @mtd: mtd info structure @@ -1754,7 +2076,9 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) { chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_READ); + nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); return 0; } @@ -1769,13 +2093,14 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int page) { int length = mtd->oobsize; - int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; - int eccsize = chip->ecc.size; + int chunk = chip->cur_ecc->bytes + chip->cur_ecc->prepad + + chip->cur_ecc->postpad; + int eccsize = chip->cur_ecc->size; uint8_t *bufpoi = chip->oob_poi; - int i, toread, sndrnd = 0, pos; + int i, toread, sndrnd = 0, pos = eccsize; - chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page); - for (i = 0; i < chip->ecc.steps; i++) { + chip->cmdfunc(mtd, NAND_CMD_READ0, chip->cur_ecc->size, page); + for (i = 0; i < chip->cur_ecc->steps; i++) { if (sndrnd) { pos = eccsize + i * (eccsize + chunk); if (mtd->writesize > 512) @@ -1785,12 +2110,17 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, } else sndrnd = 1; toread = min_t(int, length, chunk); - chip->read_buf(mtd, bufpoi, toread); + nand_rnd_config(mtd, page, pos, NAND_RND_READ); + nand_rnd_read_buf(mtd, bufpoi, toread); bufpoi += toread; length -= toread; } - if (length > 0) - chip->read_buf(mtd, bufpoi, length); + if (length > 0) { + pos = mtd->writesize + mtd->oobsize - length; + nand_rnd_config(mtd, page, pos, NAND_RND_READ); + nand_rnd_read_buf(mtd, bufpoi, length); + } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); return 0; } @@ -1809,7 +2139,9 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int length = mtd->oobsize; chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); - chip->write_buf(mtd, buf, length); + nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, buf, length); + nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE); /* Send command to program the OOB data */ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); @@ -1828,9 +2160,10 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, static int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int page) { - int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; - int eccsize = chip->ecc.size, length = mtd->oobsize; - int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps; + int chunk = chip->cur_ecc->bytes + chip->cur_ecc->prepad + + chip->cur_ecc->postpad; + int eccsize = chip->cur_ecc->size, length = mtd->oobsize; + int i, len, pos, status = 0, sndcmd = 0, steps = chip->cur_ecc->steps; const uint8_t *bufpoi = chip->oob_poi; /* @@ -1838,7 +2171,7 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd, * or * data-pad-ecc-pad-data-pad .... ecc-pad-oob */ - if (!chip->ecc.prepad && !chip->ecc.postpad) { + if (!chip->cur_ecc->prepad && !chip->cur_ecc->postpad) { pos = steps * (eccsize + chunk); steps = 0; } else @@ -1864,12 +2197,18 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd, } else sndcmd = 1; len = min_t(int, length, chunk); - chip->write_buf(mtd, bufpoi, len); + nand_rnd_config(mtd, page, pos, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, bufpoi, len); bufpoi += len; length -= len; } - if (length > 0) - chip->write_buf(mtd, bufpoi, length); + if (length > 0) { + pos = mtd->writesize + mtd->oobsize - length; + nand_rnd_config(mtd, page, pos, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, bufpoi, length); + } + + nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE); chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); status = chip->waitfunc(mtd, chip); @@ -1902,7 +2241,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, stats = mtd->ecc_stats; if (ops->mode == MTD_OPS_AUTO_OOB) - len = chip->ecc.layout->oobavail; + len = chip->cur_ecc->layout->oobavail; else len = mtd->oobsize; @@ -1930,15 +2269,15 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, while (1) { if (ops->mode == MTD_OPS_RAW) - ret = chip->ecc.read_oob_raw(mtd, chip, page); + ret = chip->cur_ecc->read_oob_raw(mtd, chip, page); else - ret = chip->ecc.read_oob(mtd, chip, page); + ret = chip->cur_ecc->read_oob(mtd, chip, page); if (ret < 0) break; len = min(len, readlen); - buf = nand_transfer_oob(chip, buf, ops, len); + buf = nand_transfer_oob(mtd, buf, ops, len); if (chip->options & NAND_NEED_READRDY) { /* Apply delay or wait for ready/busy pin */ @@ -2020,6 +2359,62 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, return ret; } +/** + * nand_part_read_oob - [MTD Interface] NAND read data and/or out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operation description structure + * + * NAND read data and/or out-of-band data. + */ +static int nand_part_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct nand_chip *chip = mtd->priv; + struct nand_part *part = to_nand_part(mtd); + int ret = -ENOTSUPP; + int chipnr; + + ops->retlen = 0; + + /* Do not allow reads past end of device */ + if (ops->datbuf && (from + ops->len) > mtd->size) { + pr_debug("%s: attempt to read beyond end of device\n", + __func__); + return -EINVAL; + } + + from += part->slc_mode ? part->offset / 2 : part->offset; + nand_get_device(part->master, FL_READING); + if (part->ecc) + chip->cur_ecc = part->ecc; + if (part->rnd) + chip->cur_rnd = part->rnd; + chipnr = from >> (chip->chip_shift - (part->slc_mode ? 1 : 0)); + nand_set_slc_mode(mtd, chipnr, part->slc_mode); + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + case MTD_OPS_RAW: + break; + + default: + goto out; + } + + if (!ops->datbuf) + ret = nand_do_read_oob(part->master, from, ops); + else + ret = nand_do_read_ops(part->master, from, ops); + +out: + chip->cur_rnd = &chip->rnd; + chip->cur_ecc = &chip->ecc; + nand_release_device(part->master); + return ret; +} + /** * nand_write_page_raw - [INTERN] raw page write function @@ -2033,9 +2428,11 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required) { - chip->write_buf(mtd, buf, mtd->writesize); - if (oob_required) - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_write_buf(mtd, buf, mtd->writesize); + if (oob_required) { + nand_rnd_config(mtd, -1, mtd->writesize, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, chip->oob_poi, mtd->oobsize); + } return 0; } @@ -2053,32 +2450,43 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required) { - int eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; + int eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; uint8_t *oob = chip->oob_poi; int steps, size; + int column = 0; - for (steps = chip->ecc.steps; steps > 0; steps--) { - chip->write_buf(mtd, buf, eccsize); + for (steps = chip->cur_ecc->steps; steps > 0; steps--) { + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, buf, eccsize); buf += eccsize; + column += eccsize; - if (chip->ecc.prepad) { - chip->write_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; + if (chip->cur_ecc->prepad) { + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, chip->cur_ecc->prepad); + oob += chip->cur_ecc->prepad; + column += chip->cur_ecc->prepad; } - chip->write_buf(mtd, oob, eccbytes); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, eccbytes); oob += eccbytes; + column += eccbytes; - if (chip->ecc.postpad) { - chip->write_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; + if (chip->cur_ecc->postpad) { + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, chip->cur_ecc->postpad); + oob += chip->cur_ecc->postpad; + column += chip->cur_ecc->postpad; } } size = mtd->oobsize - (oob - chip->oob_poi); - if (size) - chip->write_buf(mtd, oob, size); + if (size) { + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, size); + } return 0; } @@ -2092,21 +2500,21 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd, static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; uint8_t *ecc_calc = chip->buffers->ecccalc; const uint8_t *p = buf; - uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; /* Software ECC calculation */ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) - chip->ecc.calculate(mtd, p, &ecc_calc[i]); + chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]); - for (i = 0; i < chip->ecc.total; i++) + for (i = 0; i < chip->cur_ecc->total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; - return chip->ecc.write_page_raw(mtd, chip, buf, 1); + return chip->cur_ecc->write_page_raw(mtd, chip, buf, 1); } /** @@ -2119,23 +2527,27 @@ static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; uint8_t *ecc_calc = chip->buffers->ecccalc; const uint8_t *p = buf; - uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; + int column = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - chip->write_buf(mtd, p, eccsize); - chip->ecc.calculate(mtd, p, &ecc_calc[i]); + chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, p, eccsize); + chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]); + column += eccsize; } - for (i = 0; i < chip->ecc.total; i++) + for (i = 0; i < chip->cur_ecc->total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, chip->oob_poi, mtd->oobsize); return 0; } @@ -2157,10 +2569,10 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, { uint8_t *oob_buf = chip->oob_poi; uint8_t *ecc_calc = chip->buffers->ecccalc; - int ecc_size = chip->ecc.size; - int ecc_bytes = chip->ecc.bytes; - int ecc_steps = chip->ecc.steps; - uint32_t *eccpos = chip->ecc.layout->eccpos; + int ecc_size = chip->cur_ecc->size; + int ecc_bytes = chip->cur_ecc->bytes; + int ecc_steps = chip->cur_ecc->steps; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; uint32_t start_step = offset / ecc_size; uint32_t end_step = (offset + data_len - 1) / ecc_size; int oob_bytes = mtd->oobsize / ecc_steps; @@ -2168,16 +2580,18 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, for (step = 0; step < ecc_steps; step++) { /* configure controller for WRITE access */ - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE); /* write data (untouched subpages already masked by 0xFF) */ - chip->write_buf(mtd, buf, ecc_size); + nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, buf, ecc_size); + offset += ecc_size; /* mask ECC of un-touched subpages by padding 0xFF */ if ((step < start_step) || (step > end_step)) memset(ecc_calc, 0xff, ecc_bytes); else - chip->ecc.calculate(mtd, buf, ecc_calc); + chip->cur_ecc->calculate(mtd, buf, ecc_calc); /* mask OOB of un-touched subpages by padding 0xFF */ /* if oob_required, preserve OOB metadata of written subpage */ @@ -2192,11 +2606,12 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, /* copy calculated ECC for whole page to chip->buffer->oob */ /* this include masked-value(0xFF) for unwritten subpages */ ecc_calc = chip->buffers->ecccalc; - for (i = 0; i < chip->ecc.total; i++) + for (i = 0; i < chip->cur_ecc->total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; /* write OOB buffer to NAND device */ - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, chip->oob_poi, mtd->oobsize); return 0; } @@ -2216,36 +2631,47 @@ static int nand_write_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; const uint8_t *p = buf; uint8_t *oob = chip->oob_poi; + int column = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - chip->write_buf(mtd, p, eccsize); + chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, p, eccsize); + column += eccsize; - if (chip->ecc.prepad) { - chip->write_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; + if (chip->cur_ecc->prepad) { + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, chip->cur_ecc->prepad); + oob += chip->cur_ecc->prepad; + column += chip->cur_ecc->prepad; } - chip->ecc.calculate(mtd, p, oob); - chip->write_buf(mtd, oob, eccbytes); + chip->cur_ecc->calculate(mtd, p, oob); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, eccbytes); oob += eccbytes; + column += eccbytes; - if (chip->ecc.postpad) { - chip->write_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; + if (chip->cur_ecc->postpad) { + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, chip->cur_ecc->postpad); + oob += chip->cur_ecc->postpad; + column += chip->cur_ecc->postpad; } } /* Calculate remaining oob bytes */ i = mtd->oobsize - (oob - chip->oob_poi); - if (i) - chip->write_buf(mtd, oob, i); + if (i) { + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, i); + } return 0; } @@ -2269,21 +2695,25 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, int status, subpage; if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && - chip->ecc.write_subpage) + chip->cur_ecc->write_subpage) subpage = offset || (data_len < mtd->writesize); else subpage = 0; chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + nand_rnd_config(mtd, page, 0, NAND_RND_WRITE); if (unlikely(raw)) - status = chip->ecc.write_page_raw(mtd, chip, buf, - oob_required); + status = chip->cur_ecc->write_page_raw(mtd, chip, buf, + oob_required); else if (subpage) - status = chip->ecc.write_subpage(mtd, chip, offset, data_len, - buf, oob_required); + status = chip->cur_ecc->write_subpage(mtd, chip, offset, + data_len, buf, + oob_required); else - status = chip->ecc.write_page(mtd, chip, buf, oob_required); + status = chip->cur_ecc->write_page(mtd, chip, buf, + oob_required); + nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE); if (status < 0) return status; @@ -2342,7 +2772,7 @@ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len, return oob + len; case MTD_OPS_AUTO_OOB: { - struct nand_oobfree *free = chip->ecc.layout->oobfree; + struct nand_oobfree *free = chip->cur_ecc->layout->oobfree; uint32_t boffs = 0, woffs = ops->ooboffs; size_t bytes = 0; @@ -2441,6 +2871,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, uint8_t *wbuf = buf; int use_bufpoi; int part_pagewr = (column || writelen < (mtd->writesize - 1)); + int subpage; if (part_pagewr) use_bufpoi = 1; @@ -2476,6 +2907,14 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, if (ret) break; + for (subpage = column / chip->subpagesize; + subpage < (column + writelen) / chip->subpagesize; + subpage++) + nand_page_set_status(mtd, + (realpage << mtd->subpage_sft) + + subpage, + NAND_PAGE_FILLED); + writelen -= bytes; if (!writelen) break; @@ -2537,6 +2976,51 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, return ret; } +/** + * panic_nand_part_write - [MTD Interface] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * NAND write with ECC. Used when performing writes in interrupt context, this + * may for example be called by mtdoops when writing an oops while in panic. + */ +static int panic_nand_part_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf) +{ + struct nand_chip *chip = mtd->priv; + struct nand_part *part = to_nand_part(mtd); + struct mtd_oob_ops ops; + int ret, chipnr; + + to += part->slc_mode ? part->offset / 2 : part->offset; + /* Wait for the device to get ready */ + panic_nand_wait(part->master, chip, 400); + + /* Grab the device */ + panic_nand_get_device(chip, part->master, FL_WRITING); + if (part->ecc) + chip->cur_ecc = part->ecc; + if (part->rnd) + chip->cur_rnd = part->rnd; + chipnr = to >> (chip->chip_shift - (part->slc_mode ? 1 : 0)); + nand_set_slc_mode(mtd, chipnr, part->slc_mode); + + ops.len = len; + ops.datbuf = (uint8_t *)buf; + ops.oobbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + + ret = nand_do_write_ops(part->master, to, &ops); + + chip->cur_rnd = &chip->rnd; + chip->cur_ecc = &chip->ecc; + *retlen = ops.retlen; + return ret; +} + /** * nand_write - [MTD Interface] NAND write with ECC * @mtd: MTD device structure @@ -2564,6 +3048,44 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, return ret; } +/** + * nand_part_write - [MTD Interface] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * NAND write with ECC. + */ +static int nand_part_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf) +{ + struct nand_chip *chip = mtd->priv; + struct nand_part *part = to_nand_part(mtd); + struct mtd_oob_ops ops; + int ret, chipnr; + + to += part->slc_mode ? part->offset / 2 : part->offset; + nand_get_device(part->master, FL_WRITING); + if (part->ecc) + chip->cur_ecc = part->ecc; + if (part->rnd) + chip->cur_rnd = part->rnd; + chipnr = to >> (chip->chip_shift - (part->slc_mode ? 1 : 0)); + nand_set_slc_mode(mtd, chipnr, part->slc_mode); + ops.len = len; + ops.datbuf = (uint8_t *)buf; + ops.oobbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + ret = nand_do_write_ops(part->master, to, &ops); + *retlen = ops.retlen; + chip->cur_rnd = &chip->rnd; + chip->cur_ecc = &chip->ecc; + nand_release_device(part->master); + return ret; +} + /** * nand_do_write_oob - [MTD Interface] NAND write out-of-band * @mtd: MTD device structure @@ -2582,7 +3104,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, __func__, (unsigned int)to, (int)ops->ooblen); if (ops->mode == MTD_OPS_AUTO_OOB) - len = chip->ecc.layout->oobavail; + len = chip->cur_ecc->layout->oobavail; else len = mtd->oobsize; @@ -2636,9 +3158,11 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops); if (ops->mode == MTD_OPS_RAW) - status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask); + status = chip->cur_ecc->write_oob_raw(mtd, chip, + page & chip->pagemask); else - status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); + status = chip->cur_ecc->write_oob(mtd, chip, + page & chip->pagemask); chip->select_chip(mtd, -1); @@ -2692,6 +3216,59 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, return ret; } +/** + * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure + */ +static int nand_part_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct nand_chip *chip = mtd->priv; + struct nand_part *part = to_nand_part(mtd); + int ret = -ENOTSUPP, chipnr; + + ops->retlen = 0; + + /* Do not allow writes past end of device */ + if (ops->datbuf && (to + ops->len) > mtd->size) { + pr_debug("%s: attempt to write beyond end of device\n", + __func__); + return -EINVAL; + } + + to += part->slc_mode ? part->offset / 2 : part->offset; + nand_get_device(part->master, FL_WRITING); + if (part->ecc) + chip->cur_ecc = part->ecc; + if (part->rnd) + chip->cur_rnd = part->rnd; + chipnr = to >> (chip->chip_shift - (part->slc_mode ? 1 : 0)); + nand_set_slc_mode(mtd, chipnr, part->slc_mode); + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + case MTD_OPS_RAW: + break; + + default: + goto out; + } + + if (!ops->datbuf) + ret = nand_do_write_oob(part->master, to, ops); + else + ret = nand_do_write_ops(part->master, to, ops); + +out: + chip->cur_rnd = &chip->rnd; + chip->cur_ecc = &chip->ecc; + nand_release_device(part->master); + return ret; +} + /** * single_erase - [GENERIC] NAND standard block erase command function * @mtd: MTD device structure @@ -2721,6 +3298,32 @@ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) return nand_erase_nand(mtd, instr, 0); } +/** + * nand_part_erase - [MTD Interface] erase partition block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * + * Erase one ore more blocks. + */ +static int nand_part_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct nand_chip *chip = mtd->priv; + struct nand_part *part = to_nand_part(mtd); + int ret, chipnr; + + instr->addr += part->slc_mode ? part->offset / 2 : part->offset; + chipnr = instr->addr >> (chip->chip_shift - (part->slc_mode ? 1 : 0)); + nand_set_slc_mode(mtd, chipnr, part->slc_mode); + ret = nand_erase_nand(part->master, instr, 0); + if (ret) { + if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) + instr->fail_addr -= part->slc_mode ? part->offset / 2 : part->offset; + instr->addr -= part->slc_mode ? part->offset / 2 : part->offset; + } + + return ret; +} + /** * nand_erase_nand - [INTERN] erase block(s) * @mtd: MTD device structure @@ -2734,7 +3337,9 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, { int page, status, pages_per_block, ret, chipnr; struct nand_chip *chip = mtd->priv; + uint32_t erasesize = mtd->erasesize; loff_t len; + int i; pr_debug("%s: start = 0x%012llx, len = %llu\n", __func__, (unsigned long long)instr->addr, @@ -2753,6 +3358,11 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, /* Calculate pages in each block */ pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift); + if (chip->slc_mode) { + pages_per_block /= 2; + erasesize /= 2; + } + /* Select the NAND device */ chip->select_chip(mtd, chipnr); @@ -2807,8 +3417,20 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, goto erase_exit; } + for (i = 0; i < pages_per_block; i++) { + int subpage; + for (subpage = 0; + subpage < 1 << mtd->subpage_sft; + subpage++) { + nand_page_set_status(mtd, + ((page + i) << mtd->subpage_sft) + + subpage, + NAND_PAGE_EMPTY); + } + } + /* Increment page address and decrement length */ - len -= (1ULL << chip->phys_erase_shift); + len -= erasesize; page += pages_per_block; /* Check, if we cross a chip boundary */ @@ -2862,6 +3484,22 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) return nand_block_checkbad(mtd, offs, 1, 0); } +/** + * nand_part_block_isbad - [MTD Interface] Check if block at offset is bad + * @mtd: MTD device structure + * @offs: offset relative to mtd start + */ +static int nand_part_block_isbad(struct mtd_info *mtd, loff_t offs) +{ + struct nand_part *part = to_nand_part(mtd); + loff_t part_ofs = part->offset; + + if (part->slc_mode) + part_ofs /= 2; + + return nand_block_checkbad(part->master, part_ofs + offs, 1, 0); +} + /** * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad * @mtd: MTD device structure @@ -2882,6 +3520,37 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) return nand_block_markbad_lowlevel(mtd, ofs); } +/** + * nand_part_block_markbad - [MTD Interface] Mark block at the given offset as + * bad + * @mtd: MTD device structure + * @ofs: offset relative to mtd start + */ +static int nand_part_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_part *part = to_nand_part(mtd); + loff_t part_ofs = part->offset; + int ret; + + if (part->slc_mode) + part_ofs /= 2; + + ofs += part_ofs; + ret = nand_block_isbad(part->master, ofs); + if (ret) { + /* If it was bad already, return success and do nothing */ + if (ret > 0) + return 0; + return ret; + } + + ret = nand_block_markbad_lowlevel(part->master, ofs); + if (!ret) + mtd->ecc_stats.badblocks++; + + return ret; +} + /** * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand * @mtd: MTD device structure @@ -3605,7 +4274,7 @@ static inline bool is_full_id_nand(struct nand_flash_dev *type) } static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip, - struct nand_flash_dev *type, u8 *id_data, int *busw) + struct nand_flash_dev *type, const u8 *id_data, int *busw) { if (!strncmp(type->id, id_data, type->id_len)) { mtd->writesize = type->pagesize; @@ -3630,6 +4299,21 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip, return false; } +/* + * Print full detail of chip ID read from chip. + */ +static void print_nand_chip_info(int maf_id, int dev_id, u8 id_data[8]) +{ + u8 delim[8] = { [0 ... 7] = ',' }; + pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", maf_id, dev_id); + delim[7] = ' '; + delim[nand_id_len(id_data, 8) - 1] = ';'; + /* This sucks. Kernel seems to insert newline after every other printk so format in one go. */ + pr_info("chip id data: 0x%02x%c 0x%02x%c 0x%02x%c 0x%02x%c 0x%02x%c 0x%02x%c 0x%02x%c 0x%02x%c\n", + id_data[0], delim[0], id_data[1], delim[1], id_data[2], delim[2], id_data[3], delim[3], + id_data[4], delim[4], id_data[5], delim[5], id_data[6], delim[6], id_data[7], delim[7]); +} + /* * Get the flash and manufacturer id and lookup if the type is supported. */ @@ -3743,8 +4427,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, * Check, if buswidth is correct. Hardware drivers should set * chip correct! */ - pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", - *maf_id, *dev_id); + print_nand_chip_info(*maf_id, *dev_id, id_data); pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, mtd->name); pr_warn("bus width %d instead %d bit\n", (chip->options & NAND_BUSWIDTH_16) ? 16 : 8, @@ -3775,8 +4458,14 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, if (mtd->writesize > 512 && chip->cmdfunc == nand_command) chip->cmdfunc = nand_command_lp; - pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", - *maf_id, *dev_id); + if (nand_manuf_ids[maf_idx].init) { + int err; + err = nand_manuf_ids[maf_idx].init(mtd, id_data); + if (err) + return ERR_PTR(err); + } + + print_nand_chip_info(*maf_id, *dev_id, id_data); if (chip->onfi_version) pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, @@ -3788,9 +4477,11 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, type->name); - pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n", + pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, " + "OOB size: %d, ECC strength %d size %d\n", (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC", - mtd->erasesize >> 10, mtd->writesize, mtd->oobsize); + mtd->erasesize >> 10, mtd->writesize, mtd->oobsize, + chip->ecc_strength_ds, chip->ecc_step_ds); return type; } @@ -3928,42 +4619,15 @@ static bool nand_ecc_strength_good(struct mtd_info *mtd) return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds; } -/** - * nand_scan_tail - [NAND Interface] Scan for the NAND device - * @mtd: MTD device structure - * - * This is the second phase of the normal nand_scan() function. It fills out - * all the uninitialized function pointers with the defaults and scans for a - * bad block table if appropriate. +/* + * Initialize ECC struct: + * - fill ECC struct with default function/values when these ones are undefined + * - fill ECC infos based on MTD device */ -int nand_scan_tail(struct mtd_info *mtd) +static int nand_ecc_ctrl_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc) { int i; - struct nand_chip *chip = mtd->priv; - struct nand_ecc_ctrl *ecc = &chip->ecc; - struct nand_buffers *nbuf; - /* New bad blocks should be marked in OOB, flash-based BBT, or both */ - BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && - !(chip->bbt_options & NAND_BBT_USE_FLASH)); - - if (!(chip->options & NAND_OWN_BUFFERS)) { - nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize - + mtd->oobsize * 3, GFP_KERNEL); - if (!nbuf) - return -ENOMEM; - nbuf->ecccalc = (uint8_t *)(nbuf + 1); - nbuf->ecccode = nbuf->ecccalc + mtd->oobsize; - nbuf->databuf = nbuf->ecccode + mtd->oobsize; - - chip->buffers = nbuf; - } else { - if (!chip->buffers) - return -ENOMEM; - } - - /* Set the internal oob buffer location, just after the page data */ - chip->oob_poi = chip->buffers->databuf + mtd->writesize; /* * If no default placement scheme is given, select an appropriate one. @@ -3989,9 +4653,6 @@ int nand_scan_tail(struct mtd_info *mtd) } } - if (!chip->write_page) - chip->write_page = nand_write_page; - /* * Check ECC mode, default to software if 3byte/512byte hardware ECC is * selected and we have 256 byte pagesize fallback to software ECC @@ -4161,6 +4822,235 @@ int nand_scan_tail(struct mtd_info *mtd) } ecc->total = ecc->steps * ecc->bytes; + return 0; +} + +/** + * nand_add_partition - [NAND Interface] Add a NAND partition to a NAND device + * @master: MTD device structure representing the NAND device + * @part: NAND partition to add to the NAND device + * + * Adds a NAND partition to a NAND device. + * The NAND partition cannot overlap with another existing partition. + * + * Returns zero in case of success and a negative error code in case of failure. + */ +int nand_add_partition(struct mtd_info *master, struct nand_part *part) +{ + struct nand_chip *chip = master->priv; + struct mtd_info *mtd = &part->mtd; + struct nand_ecc_ctrl *ecc = part->ecc; + struct nand_part *pos; + bool inserted = false; + int ret; + + if (!chip->set_slc_mode) + part->slc_mode = false; + + /* set up the MTD object for this partition */ + mtd->type = master->type; + mtd->flags = master->flags & ~mtd->flags; + mtd->writesize = master->writesize; + mtd->writebufsize = master->writebufsize; + mtd->oobsize = master->oobsize; + mtd->oobavail = master->oobavail; + mtd->subpage_sft = master->subpage_sft; + mtd->erasesize = master->erasesize; + mtd->size = part->size; + + if (part->slc_mode) { + mtd->size /= 2; + mtd->erasesize /= 2; + } + + mtd->priv = chip; + mtd->owner = master->owner; + mtd->backing_dev_info = master->backing_dev_info; + + mtd->dev.parent = master->dev.parent; + + if (ecc) { + ret = nand_ecc_ctrl_init(mtd, ecc); + if (ret) + return ret; + } else { + ecc = &chip->ecc; + } + + mtd->_erase = nand_part_erase; + mtd->_point = NULL; + mtd->_unpoint = NULL; + mtd->_read = nand_part_read; + mtd->_write = nand_part_write; + mtd->_panic_write = panic_nand_part_write; + mtd->_read_oob = nand_part_read_oob; + mtd->_write_oob = nand_part_write_oob; + mtd->_sync = nand_sync; + mtd->_lock = NULL; + mtd->_unlock = NULL; + mtd->_suspend = nand_suspend; + mtd->_resume = nand_resume; + mtd->_block_isbad = nand_part_block_isbad; + mtd->_block_markbad = nand_part_block_markbad; + + /* propagate ecc info to mtd_info */ + mtd->ecclayout = ecc->layout; + mtd->ecc_strength = ecc->strength; + mtd->ecc_step_size = ecc->size; + /* + * Initialize bitflip_threshold to its default prior scan_bbt() call. + * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be + * properly set. + */ + if (!mtd->bitflip_threshold) + mtd->bitflip_threshold = mtd->ecc_strength; + + part->master = master; + + mutex_lock(&chip->part_lock); + list_for_each_entry(pos, &chip->partitions, node) { + if (part->offset >= pos->offset + pos->size) { + continue; + } else if (part->offset + part->size > pos->offset) { + ret = -EINVAL; + goto out; + } + + list_add(&part->node, pos->node.prev); + inserted = true; + break; + } + + if (!inserted) + list_add_tail(&part->node, &chip->partitions); + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + list_del(&part->node); + goto out; + } + + if (master->_block_isbad) { + uint64_t offs = 0; + + while (offs < mtd->size) { + if (mtd_block_isreserved(master, offs + part->offset)) + mtd->ecc_stats.bbtblocks++; + else if (mtd_block_isbad(master, offs + part->offset)) + mtd->ecc_stats.badblocks++; + offs += mtd->erasesize; + } + } + +out: + mutex_unlock(&chip->part_lock); + return ret; +} +EXPORT_SYMBOL(nand_add_partition); + +/** + * nand_del_partition - [NAND Interface] Delete a NAND part from a NAND dev + * @part: NAND partition to delete + * + * Deletes a NAND partition from a NAND device. + */ +void nand_del_partition(struct nand_part *part) +{ + struct nand_chip *chip = part->mtd.priv; + + mutex_lock(&chip->part_lock); + mtd_device_unregister(&part->mtd); + list_del(&part->node); + mutex_unlock(&chip->part_lock); + + if (part->ecc && part->ecc->mode == NAND_ECC_SOFT_BCH) + nand_bch_free((struct nand_bch_control *)part->ecc->priv); + + if (part->release) + part->release(part); +} +EXPORT_SYMBOL(nand_del_partition); + +/* + * NAND part release function. Used by nandpart_alloc as its release function. + */ +static void nandpart_release(struct nand_part *part) +{ + kfree(part); +} + +/** + * nandpart_alloc - [NAND Interface] Allocate a NAND part struct + * + * Allocate a NAND partition and assign the nandpart release function. + * This nand_part struct must be filled before passing it to the + * nand_add_partition function. + */ +struct nand_part *nandpart_alloc(void) +{ + struct nand_part *part = kzalloc(sizeof(*part), GFP_KERNEL); + if (!part) + return ERR_PTR(-ENOMEM); + part->release = nandpart_release; + + return part; +} +EXPORT_SYMBOL(nandpart_alloc); + +/** + * nand_scan_tail - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * + * This is the second phase of the normal nand_scan() function. It fills out + * all the uninitialized function pointers with the defaults and scans for a + * bad block table if appropriate. + */ +int nand_scan_tail(struct mtd_info *mtd) +{ + int ret; + struct nand_chip *chip = mtd->priv; + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct nand_buffers *nbuf; + + /* New bad blocks should be marked in OOB, flash-based BBT, or both */ + BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && + !(chip->bbt_options & NAND_BBT_USE_FLASH)); + + if (!(chip->options & NAND_OWN_BUFFERS)) { + nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize + + mtd->oobsize * 3, GFP_KERNEL); + if (!nbuf) + return -ENOMEM; + nbuf->ecccalc = (uint8_t *)(nbuf + 1); + nbuf->ecccode = nbuf->ecccalc + mtd->oobsize; + nbuf->databuf = nbuf->ecccode + mtd->oobsize; + chip->buffers = nbuf; + } else { + if (!chip->buffers) + return -ENOMEM; + } + + /* Set the internal oob buffer location, just after the page data */ + chip->oob_poi = chip->buffers->databuf + mtd->writesize; + + if (!chip->write_page) + chip->write_page = nand_write_page; + + /* Initialize ECC struct */ + ret = nand_ecc_ctrl_init(mtd, ecc); + if (ret) { + if (!(chip->options & NAND_OWN_BUFFERS)) + kfree(chip->buffers); + + return ret; + } + + INIT_LIST_HEAD(&chip->partitions); + mutex_init(&chip->part_lock); + + chip->cur_ecc = &chip->ecc; + chip->cur_rnd = &chip->rnd; + /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) { switch (ecc->steps) { diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c index 3803e0bba23b..b82b976894b3 100644 --- a/drivers/mtd/nand/nand_bch.c +++ b/drivers/mtd/nand/nand_bch.c @@ -53,14 +53,14 @@ int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, unsigned char *code) { const struct nand_chip *chip = mtd->priv; - struct nand_bch_control *nbc = chip->ecc.priv; + struct nand_bch_control *nbc = chip->cur_ecc->priv; unsigned int i; - memset(code, 0, chip->ecc.bytes); - encode_bch(nbc->bch, buf, chip->ecc.size, code); + memset(code, 0, chip->cur_ecc->bytes); + encode_bch(nbc->bch, buf, chip->cur_ecc->size, code); /* apply mask so that an erased page is a valid codeword */ - for (i = 0; i < chip->ecc.bytes; i++) + for (i = 0; i < chip->cur_ecc->bytes; i++) code[i] ^= nbc->eccmask[i]; return 0; @@ -80,15 +80,15 @@ int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc) { const struct nand_chip *chip = mtd->priv; - struct nand_bch_control *nbc = chip->ecc.priv; + struct nand_bch_control *nbc = chip->cur_ecc->priv; unsigned int *errloc = nbc->errloc; int i, count; - count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, - NULL, errloc); + count = decode_bch(nbc->bch, NULL, chip->cur_ecc->size, read_ecc, + calc_ecc, NULL, errloc); if (count > 0) { for (i = 0; i < count; i++) { - if (errloc[i] < (chip->ecc.size*8)) + if (errloc[i] < (chip->cur_ecc->size*8)) /* error is located in data, correct it */ buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); /* else error in ecc, no action needed */ diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index 97c4c0216c90..f35c418bf0e2 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -424,7 +424,7 @@ int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, unsigned char *code) { __nand_calculate_ecc(buf, - ((struct nand_chip *)mtd->priv)->ecc.size, code); + ((struct nand_chip *)mtd->priv)->cur_ecc->size, code); return 0; } @@ -524,7 +524,7 @@ int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc) { return __nand_correct_data(buf, read_ecc, calc_ecc, - ((struct nand_chip *)mtd->priv)->ecc.size); + ((struct nand_chip *)mtd->priv)->cur_ecc->size); } EXPORT_SYMBOL(nand_correct_data); diff --git a/drivers/mtd/nand/nand_hynix.c b/drivers/mtd/nand/nand_hynix.c new file mode 100644 index 000000000000..1c8a4b4b7714 --- /dev/null +++ b/drivers/mtd/nand/nand_hynix.c @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2014 Boris BREZILLON + * + * 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. + */ + +#include +#include +#include + +static u8 h27ucg8t2a_read_retry_regs[] = { + 0xcc, 0xbf, 0xaa, 0xab, 0xcd, 0xad, 0xae, 0xaf +}; + +static u8 h27q_read_retry_regs[] = { + 0x38, 0x39, 0x3a, 0x3b, +}; + +struct hynix_read_retry { + int nregs; + u8 *regs; + u8 values[0]; +}; + +struct hynix_nand { + struct hynix_read_retry read_retry; +}; + +int nand_setup_read_retry_hynix(struct mtd_info *mtd, int retry_mode) +{ + struct nand_chip *chip = mtd->priv; + struct hynix_nand *hynix = chip->manuf_priv; + int offset = retry_mode * hynix->read_retry.nregs; + int status; + int i; + + chip->cmdfunc(mtd, 0x36, -1, -1); + for (i = 0; i < hynix->read_retry.nregs; i++) { + int column = hynix->read_retry.regs[i]; + column |= column << 8; + chip->cmdfunc(mtd, NAND_CMD_NONE, column, -1); + chip->write_byte(mtd, hynix->read_retry.values[offset + i]); + } + chip->cmdfunc(mtd, 0x16, -1, -1); + + status = chip->waitfunc(mtd, chip); + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} + +static void h27_cleanup(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + kfree(chip->manuf_priv); +} + +static int h27ucg8t2a_init(struct mtd_info *mtd, const uint8_t *id) +{ + struct nand_chip *chip = mtd->priv; + struct hynix_nand *hynix; + u8 * buf = NULL; + int i, j; + int ret; + + buf = kzalloc(1024, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + chip->select_chip(mtd, 0); + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + chip->cmdfunc(mtd, 0x36, 0xff, -1); + chip->write_byte(mtd, 0x40); + chip->cmdfunc(mtd, NAND_CMD_NONE, 0xcc, -1); + chip->write_byte(mtd, 0x4d); + chip->cmdfunc(mtd, 0x16, -1, -1); + chip->cmdfunc(mtd, 0x17, -1, -1); + chip->cmdfunc(mtd, 0x04, -1, -1); + chip->cmdfunc(mtd, 0x19, -1, -1); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, 0x200); + + chip->read_buf(mtd, buf, 2); + if (buf[0] != 0x8 || buf[1] != 0x8) { + ret = -EINVAL; + goto leave; + } + chip->read_buf(mtd, buf, 1024); + + ret = 0; + for (j = 0; j < 8; j++) { + for (i = 0; i < 64; i++) { + u8 *tmp = buf + (128 * j); + if ((tmp[i] | tmp[i + 64]) != 0xff) { + ret = -EINVAL; + goto leave; + } + } + } + + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + chip->cmdfunc(mtd, 0x38, -1, -1); + chip->select_chip(mtd, -1); + + if (!ret) { + hynix = kzalloc(sizeof(*hynix) + (8 * 8), + GFP_KERNEL); + if (!hynix) { + ret = -ENOMEM; + goto leave; + } + + hynix->read_retry.nregs = 8; + hynix->read_retry.regs = h27ucg8t2a_read_retry_regs; + memcpy(hynix->read_retry.values, buf, 64); + chip->manuf_priv = hynix; + chip->setup_read_retry = nand_setup_read_retry_hynix; + chip->read_retries = 8; + chip->manuf_cleanup = h27_cleanup; + } + +leave: + kfree(buf); + + return ret; +} + +static int h27q_get_best_val(const u8 *buf, int size, int min_cnt) +{ + int *val, *cnt, best = -1, max_cnt = 0; + int i, j; + + val = kzalloc(size * 2 * sizeof(int), GFP_KERNEL); + cnt = val + size; + + for (i = 0; i < size; i++) { + if (val[i] < 0) + continue; + + val[i] = buf[i]; + cnt[i] = 1; + + for (j = i + 1; j < size; j++) { + if (buf[j] == val[i]) { + val[j] = -1; + cnt[i]++; + } + } + + if (max_cnt < cnt[i]) { + max_cnt = cnt[i]; + best = i; + } + } + + if (best >= 0) + best = val[best]; + + kfree(val); + + if (best < 0 || max_cnt < min_cnt) + return -EINVAL; + + return best; +} + +#define H27Q_RR_TABLE_SIZE 784 +#define H27Q_RR_TABLE_NSETS 8 + +static void h27q_set_slc_mode(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + u8 cmd = chip->slc_mode ? 0xbf : 0xbe; + + chip->cmdfunc(mtd, cmd, -1, -1); +} + +static void h27q_fix_page(struct mtd_info *mtd, int *page) +{ + struct nand_chip *chip = mtd->priv; + int blkmsk; + int tmp; + + if (!chip->slc_mode) + return; + + blkmsk = ((1 << (chip->phys_erase_shift - chip->page_shift)) - 1); + tmp = *page & (blkmsk >> 1); + tmp |= (*page << 1) & ~blkmsk; + *page = tmp; +} + +static int h27q_init(struct mtd_info *mtd, const uint8_t *id) +{ + struct nand_chip *chip = mtd->priv; + struct hynix_nand *hynix = NULL; + u8 * buf = NULL, tmp_buf[H27Q_RR_TABLE_NSETS]; + u8 total_rr_count, rr_reg_count; + int i, j, k; + int ret; + + buf = kzalloc(H27Q_RR_TABLE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + chip->select_chip(mtd, 0); + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + chip->cmdfunc(mtd, 0x36, 0x38, -1); + chip->write_byte(mtd, 0x52); + chip->cmdfunc(mtd, 0x16, -1, -1); + chip->cmdfunc(mtd, 0x17, -1, -1); + chip->cmdfunc(mtd, 0x04, -1, -1); + chip->cmdfunc(mtd, 0x19, -1, -1); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, 0x21f); + + chip->read_buf(mtd, buf, H27Q_RR_TABLE_SIZE); + + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + chip->cmdfunc(mtd, 0x36, 0x38, -1); + chip->write_byte(mtd, 0x0); + chip->cmdfunc(mtd, 0x16, -1, -1); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, -1); + chip->select_chip(mtd, -1); + + /* TODO: support multi-die chips */ + + ret = h27q_get_best_val(buf, 8, 5); + if (ret < 0) + goto err; + total_rr_count = ret; + + ret = h27q_get_best_val(buf + 8, 8, 5); + if (ret < 0) + goto err; + rr_reg_count = ret; + + if (rr_reg_count != sizeof(h27q_read_retry_regs)) { + ret = -EINVAL; + goto err; + } + + hynix = kzalloc(sizeof(*hynix) + + (total_rr_count * rr_reg_count), + GFP_KERNEL); + if (!hynix) { + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < total_rr_count; i++) { + for (j = 0; j < rr_reg_count; j++) { + int offset = 16 + (i * rr_reg_count) + j; + + for (k = 0; k < H27Q_RR_TABLE_NSETS; k++) { + int set_offset = k * rr_reg_count * + total_rr_count * 2; + + tmp_buf[k] = buf[offset + set_offset]; + } + + ret = h27q_get_best_val(tmp_buf, H27Q_RR_TABLE_NSETS, + 5); + if (ret >= 0) { + hynix->read_retry.values[(i * rr_reg_count) + j] = ret; + continue; + } + + offset += rr_reg_count * total_rr_count; + for (k = 0; k < H27Q_RR_TABLE_NSETS; k++) { + int set_offset = k * rr_reg_count * + total_rr_count; + + tmp_buf[k] = buf[offset + set_offset]; + } + ret = h27q_get_best_val(tmp_buf, H27Q_RR_TABLE_NSETS, + 5); + if (ret < 0) + goto err; + hynix->read_retry.values[(i * rr_reg_count) + j] = ~ret; + } + } + + kfree(buf); + + hynix->read_retry.nregs = rr_reg_count; + hynix->read_retry.regs = h27q_read_retry_regs; + chip->manuf_priv = hynix; + chip->setup_read_retry = nand_setup_read_retry_hynix; + chip->read_retries = total_rr_count; + chip->manuf_cleanup = h27_cleanup; + chip->set_slc_mode = h27q_set_slc_mode; + chip->fix_page = h27q_fix_page; + + return 0; + +err: + kfree(buf); + kfree(hynix); + + return ret; +} + +struct hynix_nand_initializer { + u8 id[6]; + int (*init)(struct mtd_info *mtd, const uint8_t *id); +}; + +struct hynix_nand_initializer initializers[] = { + { + .id = {NAND_MFR_HYNIX, 0xde, 0x94, 0xda, 0x74, 0xc4}, + .init = h27ucg8t2a_init, + }, + { + .id = {NAND_MFR_HYNIX, 0xde, 0x14, 0xa7, 0x42, 0x4a}, + .init = h27q_init, + }, +}; + +int hynix_nand_init(struct mtd_info *mtd, const uint8_t *id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(initializers); i++) { + struct hynix_nand_initializer *initializer = &initializers[i]; + if (memcmp(id, initializer->id, sizeof(initializer->id))) + continue; + + return initializer->init(mtd, id); + } + + return 0; +} +EXPORT_SYMBOL(hynix_nand_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Boris BREZILLON "); +MODULE_DESCRIPTION("Hynix NAND specific code"); diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 7124400d903b..5583ff9c3405 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -48,6 +48,10 @@ struct nand_flash_dev nand_flash_ids[] = { { .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} }, SZ_8K, SZ_8K, SZ_2M, 0, 6, 640, NAND_ECC_INFO(40, SZ_1K), 4 }, + {"H27QCG8T2E5R‐BCF 64G 3.3V 8-bit", + { .id = {0xad, 0xde, 0x14, 0xa7, 0x42, 0x4a} }, + SZ_16K, SZ_8K, SZ_4M, 0, 6, 1664, NAND_ECC_INFO(40, SZ_1K), + 2 }, LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS), LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS), @@ -161,6 +165,7 @@ struct nand_flash_dev nand_flash_ids[] = { {NULL} }; + /* Manufacturer IDs */ struct nand_manufacturers nand_manuf_ids[] = { {NAND_MFR_TOSHIBA, "Toshiba"}, @@ -169,7 +174,7 @@ struct nand_manufacturers nand_manuf_ids[] = { {NAND_MFR_NATIONAL, "National"}, {NAND_MFR_RENESAS, "Renesas"}, {NAND_MFR_STMICRO, "ST Micro"}, - {NAND_MFR_HYNIX, "Hynix"}, + {NAND_MFR_HYNIX, "Hynix", hynix_nand_init}, {NAND_MFR_MICRON, "Micron"}, {NAND_MFR_AMD, "AMD/Spansion"}, {NAND_MFR_MACRONIX, "Macronix"}, diff --git a/drivers/mtd/nand/ofnandpart.c b/drivers/mtd/nand/ofnandpart.c new file mode 100644 index 000000000000..c7bc34d07558 --- /dev/null +++ b/drivers/mtd/nand/ofnandpart.c @@ -0,0 +1,107 @@ +/* + * NAND Flash partitions described by the OF (or flattened) device tree + * + * Copyright © 2014 Boris BREZILLON + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +static inline bool node_has_compatible(struct device_node *pp) +{ + return of_get_property(pp, "compatible", NULL); +} + +int ofnandpart_parse(struct mtd_info *master, + const struct ofnandpart_data *data) +{ + struct device_node *node; + const char *partname; + struct device_node *pp; + int i; + + if (!data) + return 0; + + node = data->node; + if (!node) + return 0; + + i = 0; + for_each_child_of_node(node, pp) { + const __be32 *reg; + int len; + int a_cells, s_cells; + uint64_t offset, size; + uint32_t mask_flags = 0; + struct nand_part *part; + + if (node_has_compatible(pp)) + continue; + + reg = of_get_property(pp, "reg", &len); + if (!reg) + continue; + + a_cells = of_n_addr_cells(pp); + s_cells = of_n_size_cells(pp); + offset = of_read_number(reg, a_cells); + size = of_read_number(reg + a_cells, s_cells); + + partname = of_get_property(pp, "label", &len); + if (!partname) + partname = of_get_property(pp, "name", &len); + + if (of_get_property(pp, "read-only", &len)) + mask_flags |= MTD_WRITEABLE; + + if (of_get_property(pp, "lock", &len)) + mask_flags |= MTD_POWERUP_LOCK; + + if (data->parse) + part = data->parse(data->priv, master, pp); + else + part = nandpart_alloc(); + + if (IS_ERR(part)) + continue; + + part->offset = offset; + part->size = size; + part->master = master; + part->mtd.name = partname; + part->mtd.flags = mask_flags; + + if (of_property_read_bool(pp, "slc-mode")) + part->slc_mode = true; + + if (nand_add_partition(master, part)) { + if (part->release) + part->release(part); + continue; + } + + i++; + } + + if (!i) { + of_node_put(pp); + pr_err("No valid partition found on %s\n", node->full_name); + } + + return i; +} +EXPORT_SYMBOL(ofnandpart_parse); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Parser for NAND flash partitioning information in device tree"); +MODULE_AUTHOR("Boris BREZILLON"); diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 6f93b2990d25..15d3e902e408 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -201,6 +201,48 @@ struct sunxi_nand_hw_ecc { struct nand_ecclayout layout; }; +/* + * sunxi NAND partition structure: stores NAND partitions information + * + * @part: base paritition structure + * @ecc: per-partition ECC info + * @rnd: per-partition randomizer info + */ +struct sunxi_nand_part { + struct nand_part part; + struct nand_ecc_ctrl ecc; + struct nand_rnd_ctrl rnd; +}; + +static inline struct sunxi_nand_part * +to_sunxi_nand_part(struct nand_part *part) +{ + return container_of(part, struct sunxi_nand_part, part); +} + +/* + * sunxi NAND randomizer structure: stores NAND randomizer information + * + * @page: current page + * @column: current column + * @nseeds: seed table size + * @seeds: seed table + * @subseeds: pre computed sub seeds + * @step: step function + * @left: number of remaining bytes in the page + * @state: current randomizer state + */ +struct sunxi_nand_hw_rnd { + int page; + int column; + int nseeds; + u16 *seeds; + u16 *subseeds; + u16 (*step)(struct mtd_info *mtd, u16 state, int column, int *left); + int left; + u16 state; +}; + /* * NAND chip structure: stores NAND chip device related information * @@ -216,6 +258,7 @@ struct sunxi_nand_chip { struct list_head node; struct nand_chip nand; struct mtd_info mtd; + void *buffer; unsigned long clk_rate; int selected; int nsels; @@ -472,6 +515,185 @@ static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, } } +static u16 sunxi_nfc_hwrnd_step(struct sunxi_nand_hw_rnd *rnd, u16 state, int count) +{ + state &= 0x7fff; + count *= 8; + while (count--) + state = ((state >> 1) | + ((((state >> 0) ^ (state >> 1)) & 1) << 14)) & 0x7fff; + + return state; +} + +static u16 sunxi_nfc_hwrnd_single_step(u16 state, int count) +{ + state &= 0x7fff; + while (count--) + state = ((state >> 1) | + ((((state >> 0) ^ (state >> 1)) & 1) << 14)) & 0x7fff; + + return state; +} + +static int sunxi_nfc_hwrnd_config(struct mtd_info *mtd, int page, int column, + enum nand_rnd_action action) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nand_hw_rnd *rnd = nand->cur_rnd->priv; + u16 state; + + if (page < 0 && column < 0) { + rnd->page = -1; + rnd->column = -1; + return 0; + } + + if (column < 0) + column = 0; + if (page < 0) + page = rnd->page; + + if (page < 0) + return -EINVAL; + + if (page != rnd->page && action == NAND_RND_READ) { + int status; + + status = nand_page_get_status(mtd, page); + if (status == NAND_PAGE_STATUS_UNKNOWN) { + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); + sunxi_nfc_read_buf(mtd, sunxi_nand->buffer, + mtd->writesize + mtd->oobsize); + + if (nand_page_is_empty(mtd, sunxi_nand->buffer, + sunxi_nand->buffer + + mtd->writesize)) + status = NAND_PAGE_EMPTY; + else + status = NAND_PAGE_FILLED; + + nand_page_set_status(mtd, page, status); + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, column, -1); + } + } + + state = rnd->seeds[page % rnd->nseeds]; + rnd->page = page; + rnd->column = column; + + if (rnd->step) { + rnd->state = rnd->step(mtd, state, column, &rnd->left); + } else { + rnd->state = sunxi_nfc_hwrnd_step(rnd, state, column % 4096); + rnd->left = mtd->oobsize + mtd->writesize - column; + } + + return 0; +} + +static void sunxi_nfc_hwrnd_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct sunxi_nand_hw_rnd *rnd = nand->cur_rnd->priv; + u32 tmp = readl(nfc->regs + NFC_REG_ECC_CTL); + int cnt; + int offs = 0; + int rndactiv; + + tmp &= ~(NFC_RANDOM_DIRECTION | NFC_RANDOM_SEED | NFC_RANDOM_EN); + writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + + if (rnd->page < 0) { + sunxi_nfc_write_buf(mtd, buf, len); + return; + } + + while (len > offs) { + cnt = len - offs; + if (cnt > 1024) + cnt = 1024; + + rndactiv = nand_rnd_is_activ(mtd, rnd->page, rnd->column, + &cnt); + if (rndactiv > 0) { + writel(tmp | NFC_RANDOM_EN | (rnd->state << 16), + nfc->regs + NFC_REG_ECC_CTL); + if (rnd->left < cnt) + cnt = rnd->left; + } + + sunxi_nfc_write_buf(mtd, buf + offs, cnt); + + if (rndactiv > 0) + writel(tmp & ~NFC_RANDOM_EN, + nfc->regs + NFC_REG_ECC_CTL); + + offs += cnt; + if (len <= offs) + break; + + sunxi_nfc_hwrnd_config(mtd, -1, rnd->column + cnt, NAND_RND_WRITE); + } +} + +static void sunxi_nfc_hwrnd_read_buf(struct mtd_info *mtd, uint8_t *buf, + int len) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct sunxi_nand_hw_rnd *rnd = nand->cur_rnd->priv; + u32 tmp = readl(nfc->regs + NFC_REG_ECC_CTL); + int cnt; + int offs = 0; + int rndactiv; + + tmp &= ~(NFC_RANDOM_DIRECTION | NFC_RANDOM_SEED | NFC_RANDOM_EN); + writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + + if (rnd->page < 0) { + sunxi_nfc_read_buf(mtd, buf, len); + return; + } + + while (len > offs) { + cnt = len - offs; + if (cnt > 1024) + cnt = 1024; + + if (nand_page_get_status(mtd, rnd->page) != NAND_PAGE_EMPTY && + nand_rnd_is_activ(mtd, rnd->page, rnd->column, &cnt) > 0) + rndactiv = 1; + else + rndactiv = 0; + + if (rndactiv > 0) { + writel(tmp | NFC_RANDOM_EN | (rnd->state << 16), + nfc->regs + NFC_REG_ECC_CTL); + if (rnd->left < cnt) + cnt = rnd->left; + } + + if (buf) + sunxi_nfc_read_buf(mtd, buf + offs, cnt); + else + sunxi_nfc_read_buf(mtd, NULL, cnt); + + if (rndactiv > 0) + writel(tmp & ~NFC_RANDOM_EN, + nfc->regs + NFC_REG_ECC_CTL); + + offs += cnt; + if (len <= offs) + break; + + sunxi_nfc_hwrnd_config(mtd, -1, rnd->column + cnt, NAND_RND_READ); + } +} + static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd) { uint8_t ret; @@ -521,16 +743,43 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, int oob_required, int page) { struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); - struct nand_ecc_ctrl *ecc = &chip->ecc; + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); + struct nand_ecc_ctrl *ecc = chip->cur_ecc; struct nand_ecclayout *layout = ecc->layout; struct sunxi_nand_hw_ecc *data = ecc->priv; unsigned int max_bitflips = 0; + int status; int offset; int ret; u32 tmp; int i; int cnt; + status = nand_page_get_status(mtd, page); + if (status == NAND_PAGE_STATUS_UNKNOWN) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); + sunxi_nfc_read_buf(mtd, sunxi_nand->buffer, + mtd->writesize + mtd->oobsize); + + if (nand_page_is_empty(mtd, sunxi_nand->buffer, + sunxi_nand->buffer + + mtd->writesize)) { + status = NAND_PAGE_EMPTY; + } else { + status = NAND_PAGE_FILLED; + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); + } + + nand_page_set_status(mtd, page, status); + } + + if (status == NAND_PAGE_EMPTY) { + memset(buf, 0xff, mtd->writesize); + if (oob_required) + memset(chip->oob_poi, 0xff, mtd->oobsize); + return 0; + } + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE); tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) | @@ -539,12 +788,15 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, writel(tmp, nfc->regs + NFC_REG_ECC_CTL); for (i = 0; i < ecc->steps; i++) { + bool rndactiv = false; + if (i) chip->cmdfunc(mtd, NAND_CMD_RNDOUT, i * ecc->size, -1); offset = mtd->writesize + layout->eccpos[i * ecc->bytes] - 4; - chip->read_buf(mtd, NULL, ecc->size); + nand_rnd_config(mtd, page, i * ecc->size, NAND_RND_READ); + nand_rnd_read_buf(mtd, NULL, ecc->size); chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); @@ -552,6 +804,25 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, if (ret) return ret; + if (i) { + cnt = ecc->bytes + 4; + if (nand_rnd_is_activ(mtd, page, offset, &cnt) > 0 && + cnt == ecc->bytes + 4) + rndactiv = true; + } else { + cnt = ecc->bytes + 2; + if (nand_rnd_is_activ(mtd, page, offset + 2, &cnt) > 0 && + cnt == ecc->bytes + 2) + rndactiv = true; + } + + if (rndactiv) { + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); + tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION); + tmp |= NFC_RANDOM_EN; + writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + } + tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30); writel(tmp, nfc->regs + NFC_REG_CMD); @@ -562,6 +833,9 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, memcpy_fromio(buf + (i * ecc->size), nfc->regs + NFC_RAM0_BASE, ecc->size); + writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, + nfc->regs + NFC_REG_ECC_CTL); + if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) { mtd->ecc_stats.failed++; } else { @@ -577,9 +851,10 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, if (ret) return ret; + nand_rnd_config(mtd, -1, offset, NAND_RND_READ); offset -= mtd->writesize; - chip->read_buf(mtd, chip->oob_poi + offset, - ecc->bytes + 4); + nand_rnd_read_buf(mtd, chip->oob_poi + offset, + ecc->bytes + 4); } } @@ -589,11 +864,14 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, offset = mtd->writesize + ecc->layout->oobfree[ecc->steps].offset; chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); + nand_rnd_config(mtd, -1, offset, NAND_RND_READ); offset -= mtd->writesize; - chip->read_buf(mtd, chip->oob_poi + offset, cnt); + nand_rnd_read_buf(mtd, chip->oob_poi + offset, cnt); } } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); tmp &= ~NFC_ECC_EN; @@ -607,9 +885,10 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, const uint8_t *buf, int oob_required) { struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); - struct nand_ecc_ctrl *ecc = &chip->ecc; + struct nand_ecc_ctrl *ecc = chip->cur_ecc; struct nand_ecclayout *layout = ecc->layout; struct sunxi_nand_hw_ecc *data = ecc->priv; + struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv; int offset; int ret; u32 tmp; @@ -624,24 +903,62 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, writel(tmp, nfc->regs + NFC_REG_ECC_CTL); for (i = 0; i < ecc->steps; i++) { + bool rndactiv = false; + u32 user_data; + if (i) chip->cmdfunc(mtd, NAND_CMD_RNDIN, i * ecc->size, -1); - chip->write_buf(mtd, buf + (i * ecc->size), ecc->size); + nand_rnd_config(mtd, -1, i * ecc->size, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, buf + (i * ecc->size), ecc->size); offset = layout->eccpos[i * ecc->bytes] - 4 + mtd->writesize; /* Fill OOB data in */ - if (oob_required) { - tmp = 0xffffffff; - memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp, - 4); + if (!oob_required) { + user_data = 0xffffffff; + } else { + memcpy(&user_data, + chip->oob_poi + layout->oobfree[i].offset, 4); + user_data = le32_to_cpu(user_data); + } + + if (i) { + cnt = ecc->bytes + 4; + if (rnd && + nand_rnd_is_activ(mtd, -1, offset, &cnt) > 0 && + cnt == ecc->bytes + 4) + rndactiv = true; } else { - memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, - chip->oob_poi + offset - mtd->writesize, - 4); + cnt = ecc->bytes + 2; + if (rnd && + nand_rnd_is_activ(mtd, -1, offset + 2, &cnt) > 0 && + cnt == ecc->bytes + 2) + rndactiv = true; } + if (rndactiv) { + /* pre randomize to generate FF patterns on the NAND */ + if (!i) { + u8 oob_tmp[2]; + u16 state = rnd->subseeds[rnd->page % rnd->nseeds]; + oob_tmp[0] = user_data; + oob_tmp[1] = user_data >> 8; + state = sunxi_nfc_hwrnd_single_step(state, 15); + oob_tmp[0] ^= state; + state = sunxi_nfc_hwrnd_step(rnd, state, 1); + oob_tmp[1] ^= state; + user_data &= ~0xffff; + user_data |= oob_tmp[0] | (oob_tmp[1] << 8); + } + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); + tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION); + tmp |= NFC_RANDOM_EN; + writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + } + + writel(user_data, nfc->regs + NFC_REG_USER_DATA_BASE); + chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); @@ -654,6 +971,9 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); if (ret) return ret; + + writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, + nfc->regs + NFC_REG_ECC_CTL); } if (oob_required) { @@ -662,11 +982,14 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, offset = mtd->writesize + ecc->layout->oobfree[i].offset; chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); + nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE); offset -= mtd->writesize; - chip->write_buf(mtd, chip->oob_poi + offset, cnt); + nand_rnd_write_buf(mtd, chip->oob_poi + offset, cnt); } } + nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE); + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); tmp &= ~NFC_ECC_EN; @@ -675,22 +998,76 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, return 0; } +static u16 sunxi_nfc_hw_ecc_rnd_steps(struct mtd_info *mtd, u16 state, + int column, int *left) +{ + struct nand_chip *chip = mtd->priv; + struct nand_ecc_ctrl *ecc = chip->cur_ecc; + struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv; + int nblks = mtd->writesize / ecc->size; + int modsize = ecc->size; + int steps; + + if (column < mtd->writesize) { + steps = column % modsize; + *left = modsize - steps; + } else if (column < mtd->writesize + + (nblks * (ecc->bytes + 4))) { + column -= mtd->writesize; + steps = column % (ecc->bytes + 4); + *left = ecc->bytes + 4 - steps; + state = rnd->subseeds[rnd->page % rnd->nseeds]; + } else { + steps = column % 4096; + *left = mtd->writesize + mtd->oobsize - column; + } + + return sunxi_nfc_hwrnd_step(rnd, state, steps); +} + static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); - struct nand_ecc_ctrl *ecc = &chip->ecc; + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); + struct nand_ecc_ctrl *ecc = chip->cur_ecc; struct sunxi_nand_hw_ecc *data = ecc->priv; unsigned int max_bitflips = 0; uint8_t *oob = chip->oob_poi; int offset = 0; int ret; + int status; int cnt; u32 tmp; int i; + status = nand_page_get_status(mtd, page); + if (status == NAND_PAGE_STATUS_UNKNOWN) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); + sunxi_nfc_read_buf(mtd, sunxi_nand->buffer, + mtd->writesize + mtd->oobsize); + + if (nand_page_is_empty(mtd, sunxi_nand->buffer, + sunxi_nand->buffer + + mtd->writesize)) { + status = NAND_PAGE_EMPTY; + } else { + status = NAND_PAGE_FILLED; + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); + } + + nand_page_set_status(mtd, page, status); + } + + if (status == NAND_PAGE_EMPTY) { + memset(buf, 0xff, mtd->writesize); + if (oob_required) + memset(chip->oob_poi, 0xff, mtd->oobsize); + return 0; + } + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE); tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) | @@ -699,7 +1076,17 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, writel(tmp, nfc->regs + NFC_REG_ECC_CTL); for (i = 0; i < ecc->steps; i++) { - chip->read_buf(mtd, NULL, ecc->size); + nand_rnd_config(mtd, page, offset, NAND_RND_READ); + nand_rnd_read_buf(mtd, NULL, ecc->size); + + cnt = ecc->bytes + 4; + if (nand_rnd_is_activ(mtd, page, offset, &cnt) > 0 && + cnt == ecc->bytes + 4) { + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); + tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION); + tmp |= NFC_RANDOM_EN; + writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + } tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30); writel(tmp, nfc->regs + NFC_REG_CMD); @@ -712,6 +1099,9 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, buf += ecc->size; offset += ecc->size; + writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, + nfc->regs + NFC_REG_ECC_CTL); + if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) { mtd->ecc_stats.failed++; } else { @@ -722,7 +1112,8 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, if (oob_required) { chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); - chip->read_buf(mtd, oob, ecc->bytes + ecc->prepad); + nand_rnd_config(mtd, -1, offset, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, ecc->bytes + ecc->prepad); oob += ecc->bytes + ecc->prepad; } @@ -733,10 +1124,13 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, cnt = mtd->oobsize - (oob - chip->oob_poi); if (cnt > 0) { chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); - chip->read_buf(mtd, oob, cnt); + nand_rnd_config(mtd, page, offset, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, cnt); } } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); + writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN, nfc->regs + NFC_REG_ECC_CTL); @@ -749,8 +1143,9 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, int oob_required) { struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); - struct nand_ecc_ctrl *ecc = &chip->ecc; + struct nand_ecc_ctrl *ecc = chip->cur_ecc; struct sunxi_nand_hw_ecc *data = ecc->priv; + struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv; uint8_t *oob = chip->oob_poi; int offset = 0; int ret; @@ -766,17 +1161,28 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, writel(tmp, nfc->regs + NFC_REG_ECC_CTL); for (i = 0; i < ecc->steps; i++) { - chip->write_buf(mtd, buf + (i * ecc->size), ecc->size); + nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, buf + (i * ecc->size), ecc->size); offset += ecc->size; /* Fill OOB data in */ if (oob_required) { tmp = 0xffffffff; - memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp, - 4); } else { - memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob, - 4); + memcpy(&tmp, oob, sizeof(tmp)); + tmp = le32_to_cpu(tmp); + } + + writel(tmp, nfc->regs + NFC_REG_USER_DATA_BASE); + + cnt = ecc->bytes + 4; + if (rnd && + nand_rnd_is_activ(mtd, rnd->page, offset, &cnt) > 0 && + cnt == ecc->bytes + 4) { + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); + tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION); + tmp |= NFC_RANDOM_EN; + writel(tmp, nfc->regs + NFC_REG_ECC_CTL); } tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR | @@ -787,6 +1193,9 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, if (ret) return ret; + writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, + nfc->regs + NFC_REG_ECC_CTL); + offset += ecc->bytes + ecc->prepad; oob += ecc->bytes + ecc->prepad; } @@ -795,9 +1204,11 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, cnt = mtd->oobsize - (oob - chip->oob_poi); if (cnt > 0) { chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); - chip->write_buf(mtd, oob, cnt); + nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, cnt); } } + nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE); tmp = readl(nfc->regs + NFC_REG_ECC_CTL); tmp &= ~NFC_ECC_EN; @@ -807,6 +1218,128 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, return 0; } +static u16 sunxi_nfc_hw_syndrome_ecc_rnd_steps(struct mtd_info *mtd, u16 state, + int column, int *left) +{ + struct nand_chip *chip = mtd->priv; + struct nand_ecc_ctrl *ecc = chip->cur_ecc; + struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv; + int eccsteps = mtd->writesize / ecc->size; + int modsize = ecc->size + ecc->prepad + ecc->bytes; + int steps; + + if (column < (eccsteps * modsize)) { + steps = column % modsize; + *left = modsize - steps; + if (steps >= ecc->size) { + steps -= ecc->size; + state = rnd->subseeds[rnd->page % rnd->nseeds]; + } + } else { + steps = column % 4096; + *left = mtd->writesize + mtd->oobsize - column; + } + + return sunxi_nfc_hwrnd_step(rnd, state, steps); +} + +static u16 default_seeds[] = {0x4a80}; + +static void sunxi_nand_rnd_ctrl_cleanup(struct nand_rnd_ctrl *rnd) +{ + struct sunxi_nand_hw_rnd *hwrnd = rnd->priv; + + if (hwrnd->seeds != default_seeds) + kfree(hwrnd->seeds); + kfree(hwrnd->subseeds); + kfree(rnd->layout); + kfree(hwrnd); +} + +static int sunxi_nand_rnd_ctrl_init(struct mtd_info *mtd, + struct nand_rnd_ctrl *rnd, + struct nand_ecc_ctrl *ecc, + struct device_node *np) +{ + struct sunxi_nand_hw_rnd *hwrnd; + struct nand_rnd_layout *layout = NULL; + int ret; + + hwrnd = kzalloc(sizeof(*hwrnd), GFP_KERNEL); + if (!hwrnd) + return -ENOMEM; + + hwrnd->seeds = default_seeds; + hwrnd->nseeds = ARRAY_SIZE(default_seeds); + + if (of_get_property(np, "nand-randomizer-seeds", &ret)) { + hwrnd->nseeds = ret / sizeof(*hwrnd->seeds); + hwrnd->seeds = kzalloc(hwrnd->nseeds * sizeof(*hwrnd->seeds), + GFP_KERNEL); + if (!hwrnd->seeds) { + ret = -ENOMEM; + goto err; + } + + ret = of_property_read_u16_array(np, "nand-randomizer-seeds", + hwrnd->seeds, hwrnd->nseeds); + if (ret) + goto err; + } + + switch (ecc->mode) { + case NAND_ECC_HW_SYNDROME: + hwrnd->step = sunxi_nfc_hw_syndrome_ecc_rnd_steps; + break; + + case NAND_ECC_HW: + hwrnd->step = sunxi_nfc_hw_ecc_rnd_steps; + + default: + layout = kzalloc(sizeof(*layout) + sizeof(struct nand_rndfree), + GFP_KERNEL); + if (!layout) { + ret = -ENOMEM; + goto err; + } + layout->nranges = 1; + layout->ranges[0].offset = mtd->writesize; + layout->ranges[0].length = 2; + rnd->layout = layout; + break; + } + + if (ecc->mode == NAND_ECC_HW_SYNDROME || ecc->mode == NAND_ECC_HW) { + int i; + + hwrnd->subseeds = kzalloc(hwrnd->nseeds * + sizeof(*hwrnd->subseeds), + GFP_KERNEL); + if (!hwrnd->subseeds) { + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < hwrnd->nseeds; i++) + hwrnd->subseeds[i] = sunxi_nfc_hwrnd_step(hwrnd, + hwrnd->seeds[i], + ecc->size); + } + + rnd->config = sunxi_nfc_hwrnd_config; + rnd->read_buf = sunxi_nfc_hwrnd_read_buf; + rnd->write_buf = sunxi_nfc_hwrnd_write_buf; + rnd->priv = hwrnd; + + return 0; + +err: + kfree(hwrnd); + kfree(layout); + + return ret; +} + static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, const struct nand_sdr_timings *timings) { @@ -1067,6 +1600,40 @@ static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd, return 0; } +static void sunxi_nand_rnd_cleanup(struct nand_rnd_ctrl *rnd) +{ + switch (rnd->mode) { + case NAND_RND_HW: + sunxi_nand_rnd_ctrl_cleanup(rnd); + break; + default: + break; + } +} + +static int sunxi_nand_rnd_init(struct mtd_info *mtd, + struct nand_rnd_ctrl *rnd, + struct nand_ecc_ctrl *ecc, + struct device_node *np) +{ + int ret; + + rnd->mode = NAND_RND_NONE; + + ret = of_get_nand_rnd_mode(np); + if (ret >= 0) + rnd->mode = ret; + + switch (rnd->mode) { + case NAND_RND_HW: + return sunxi_nand_rnd_ctrl_init(mtd, rnd, ecc, np); + default: + break; + } + + return 0; +} + static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc) { switch (ecc->mode) { @@ -1099,8 +1666,13 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, ecc->strength = nand->ecc_strength_ds; } - if (!ecc->size || !ecc->strength) - return -EINVAL; + if (!ecc->size || !ecc->strength) { + if (ecc == &nand->ecc) + return -EINVAL; + + ecc->size = nand->ecc.size; + ecc->strength = nand->ecc.strength; + } ecc->mode = NAND_ECC_HW; @@ -1135,12 +1707,55 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, return 0; } +static void sunxi_nand_part_release(struct nand_part *part) +{ + kfree(to_sunxi_nand_part(part)); +} + +struct nand_part *sunxi_ofnandpart_parse(void *priv, struct mtd_info *master, + struct device_node *pp) +{ + struct nand_chip *chip = master->priv; + struct sunxi_nand_part *part; + int ret; + + part = kzalloc(sizeof(*part), GFP_KERNEL); + part->part.release = sunxi_nand_part_release; + + if (of_find_property(pp, "nand-ecc-mode", NULL)) { + ret = sunxi_nand_ecc_init(master, &part->ecc, pp); + if (ret) + goto err; + + part->part.ecc = &part->ecc; + } + + if (of_find_property(pp, "nand-rnd-mode", NULL)) { + ret = sunxi_nand_rnd_init(master, &part->rnd, + part->part.ecc ? part->part.ecc : &chip->ecc, + pp); + if (ret) + goto err; + + part->part.rnd = &part->rnd; + } + + return &part->part; + +err: + if (part->part.ecc) + sunxi_nand_ecc_cleanup(part->part.ecc); + + kfree(part); + return ERR_PTR(ret); +} + static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, struct device_node *np) { const struct nand_sdr_timings *timings; struct sunxi_nand_chip *chip; - struct mtd_part_parser_data ppdata; + struct ofnandpart_data ppdata; struct mtd_info *mtd; struct nand_chip *nand; int nsels; @@ -1251,26 +1866,44 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, if (ret) return ret; + chip->buffer = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); + if (!chip->buffer) + return -ENOMEM; + ret = sunxi_nand_chip_init_timings(chip, np); if (ret) { dev_err(dev, "could not configure chip timings: %d\n", ret); return ret; } + ret = nand_pst_create(mtd); + if (ret) + return ret; + ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np); if (ret) { dev_err(dev, "ECC init failed: %d\n", ret); return ret; } + ret = sunxi_nand_rnd_init(mtd, &nand->rnd, &nand->ecc, np); + if (ret) + return ret; + ret = nand_scan_tail(mtd); if (ret) { dev_err(dev, "nand_scan_tail failed: %d\n", ret); return ret; } - ppdata.of_node = np; - ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); + ppdata.node = np; + ppdata.parse = sunxi_ofnandpart_parse; + ret = ofnandpart_parse(mtd, &ppdata); + if (!ret) + ret = mtd_device_register(mtd, NULL, 0); + else if (ret > 0) + ret = 0; + if (ret) { dev_err(dev, "failed to register mtd device: %d\n", ret); nand_release(mtd); @@ -1312,6 +1945,8 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) node); nand_release(&chip->mtd); sunxi_nand_ecc_cleanup(&chip->nand.ecc); + sunxi_nand_rnd_cleanup(&chip->nand.rnd); + kfree(chip->buffer); } } diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c index b7361ed70537..4e42c26d91c3 100644 --- a/drivers/of/of_mtd.c +++ b/drivers/of/of_mtd.c @@ -83,6 +83,41 @@ int of_get_nand_ecc_strength(struct device_node *np) } EXPORT_SYMBOL_GPL(of_get_nand_ecc_strength); +/** + * It maps 'enum nand_rnd_modes_t' found in include/linux/mtd/nand.h + * into the device tree binding of 'nand-rnd', so that MTD + * device driver can get nand rnd from device tree. + */ +static const char *nand_rnd_modes[] = { + [NAND_RND_NONE] = "none", + [NAND_RND_SOFT] = "soft", + [NAND_RND_HW] = "hw", +}; + +/** + * of_get_nand_rnd_mode - Get nand randomizer mode for given device_node + * @np: Pointer to the given device_node + * + * The function gets randomizer mode string from property 'nand-rnd-mode', + * and return its index in nand_rnd_modes table, or errno in error case. + */ +int of_get_nand_rnd_mode(struct device_node *np) +{ + const char *pm; + int err, i; + + err = of_property_read_string(np, "nand-rnd-mode", &pm); + if (err < 0) + return err; + + for (i = 0; i < ARRAY_SIZE(nand_rnd_modes); i++) + if (!strcasecmp(pm, nand_rnd_modes[i])) + return i; + + return -ENODEV; +} +EXPORT_SYMBOL_GPL(of_get_nand_rnd_mode); + /** * of_get_nand_bus_width - Get nand bus witdh for given device_node * @np: Pointer to the given device_node diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c index 96f3448b6eb4..fd65b3f1923c 100644 --- a/fs/ubifs/xattr.c +++ b/fs/ubifs/xattr.c @@ -652,11 +652,8 @@ int ubifs_init_security(struct inode *dentry, struct inode *inode, { int err; - mutex_lock(&inode->i_mutex); err = security_inode_init_security(inode, dentry, qstr, &init_xattrs, 0); - mutex_unlock(&inode->i_mutex); - if (err) { struct ubifs_info *c = dentry->i_sb->s_fs_info; ubifs_err(c, "cannot initialize security for inode %lu, error %d", diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index f17fa75809aa..a83e394bfbdb 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -95,7 +95,7 @@ struct mtd_oob_ops { }; #define MTD_MAX_OOBFREE_ENTRIES_LARGE 32 -#define MTD_MAX_ECCPOS_ENTRIES_LARGE 640 +#define MTD_MAX_ECCPOS_ENTRIES_LARGE 1664 /* * Internal ECC layout control structure. For historical reasons, there is a * similar, smaller struct nand_ecclayout_user (in mtd-abi.h) that is retained diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index f25e2bdd188c..f185da5d6ca3 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -177,18 +177,18 @@ typedef enum { #define NAND_OWN_BUFFERS 0x00020000 /* Chip may not exist, so silence any errors in scan */ #define NAND_SCAN_SILENT_NODEV 0x00040000 -/* - * This option could be defined by controller drivers to protect against - * kmap'ed, vmalloc'ed highmem buffers being passed from upper layers - */ -#define NAND_USE_BOUNCE_BUFFER 0x00080000 /* * Autodetect nand buswidth with readid/onfi. * This suppose the driver will configure the hardware in 8 bits mode * when calling nand_scan_ident, and update its configuration * before calling nand_scan_tail. */ -#define NAND_BUSWIDTH_AUTO 0x00080000 +#define NAND_BUSWIDTH_AUTO 0x00080000 +/* + * This option could be defined by controller drivers to protect against + * kmap'ed, vmalloc'ed highmem buffers being passed from upper layers + */ +#define NAND_USE_BOUNCE_BUFFER 0x00100000 /* Options set by nand scan */ /* Nand scan has allocated controller struct */ @@ -523,6 +523,82 @@ struct nand_ecc_ctrl { int page); }; +/* + * Constants for page status + */ +enum nand_page_status { + NAND_PAGE_STATUS_UNKNOWN, + NAND_PAGE_EMPTY, + NAND_PAGE_FILLED, +}; + +bool nand_page_is_empty(struct mtd_info *mtd, void *data, void *oob); + +int nand_page_get_status(struct mtd_info *mtd, int page); + +void nand_page_set_status(struct mtd_info *mtd, int page, + enum nand_page_status status); + +int nand_pst_create(struct mtd_info *mtd); + +/* + * Constants for randomizer modes + */ +typedef enum { + NAND_RND_NONE, + NAND_RND_SOFT, + NAND_RND_HW, +} nand_rnd_modes_t; + +/* + * Constants for randomizer actions + */ +enum nand_rnd_action { + NAND_RND_NO_ACTION, + NAND_RND_READ, + NAND_RND_WRITE, +}; + +/** + * struct nand_rndfree - Structure defining a NAND page region where the + * randomizer should be disabled + * @offset: range offset + * @length: range length + */ +struct nand_rndfree { + u32 offset; + u32 length; +}; + +/** + * struct nand_rnd_layout - Structure defining rndfree regions + * @nranges: number of ranges + * @ranges: array defining the rndfree regions + */ +struct nand_rnd_layout { + int nranges; + struct nand_rndfree ranges[0]; +}; + +/** + * struct nand_rnd_ctrl - Randomizer Control structure + * @mode: Randomizer mode + * @config: function to prepare the randomizer (i.e.: set the appropriate + * seed/init value). + * @read_buf: function that read from the NAND and descramble the retrieved + * data. + * @write_buf: function that scramble data before writing it to the NAND. + */ +struct nand_rnd_ctrl { + nand_rnd_modes_t mode; + struct nand_rnd_layout *layout; + void *priv; + int (*config)(struct mtd_info *mtd, int page, int column, + enum nand_rnd_action action); + void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); + void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); +}; + /** * struct nand_buffers - buffer structure for read/write * @ecccalc: buffer pointer for calculated ECC, size is oobsize. @@ -633,6 +709,7 @@ struct nand_buffers { * @bbt_md: [REPLACEABLE] bad block table mirror descriptor * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial * bad block scan. + * @pst: [INTERN] page status table * @controller: [REPLACEABLE] a pointer to a hardware controller * structure which is shared among multiple independent * devices. @@ -676,7 +753,13 @@ struct nand_chip { int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip, int feature_addr, uint8_t *subfeature_para); int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode); + void (*manuf_cleanup)(struct mtd_info *mtd); + void (*set_slc_mode)(struct mtd_info *mtd); + void (*fix_page)(struct mtd_info *mtd, int *page); + + void *manuf_priv; + bool slc_mode; int chip_delay; unsigned int options; unsigned int bbt_options; @@ -713,18 +796,65 @@ struct nand_chip { struct nand_hw_control *controller; struct nand_ecc_ctrl ecc; + struct nand_ecc_ctrl *cur_ecc; struct nand_buffers *buffers; struct nand_hw_control hwcontrol; + struct nand_rnd_ctrl rnd; + struct nand_rnd_ctrl *cur_rnd; + uint8_t *bbt; struct nand_bbt_descr *bbt_td; struct nand_bbt_descr *bbt_md; struct nand_bbt_descr *badblock_pattern; + uint8_t *pst; + + struct list_head partitions; + struct mutex part_lock; + void *priv; }; +/** + * struct nand_part - NAND partition structure + * @node: list node used to attach the partition to its NAND dev + * @mtd: MTD partiton info + * @master: MTD device representing the NAND chip + * @offset: partition offset + * @ecc: partition specific ECC struct + * @rnd: partition specific randomizer struct + * @release: function used to release this nand_part struct + * + * NAND partitions work as standard MTD partitions except it can override + * NAND chip ECC handling. + * This is particularly useful for SoCs that need specific ECC configs to boot + * from NAND while these ECC configs do not fit the NAND chip ECC requirements. + */ +struct nand_part { + struct list_head node; + struct mtd_info mtd; + struct mtd_info *master; + uint64_t offset; + uint64_t size; + struct nand_ecc_ctrl *ecc; + struct nand_rnd_ctrl *rnd; + void (*release)(struct nand_part *part); + bool slc_mode; +}; + +static inline struct nand_part *to_nand_part(struct mtd_info *mtd) +{ + return container_of(mtd, struct nand_part, mtd); +} + +int nand_add_partition(struct mtd_info *master, struct nand_part *part); + +void nand_del_partition(struct nand_part *part); + +struct nand_part *nandpart_alloc(void); + /* * NAND Flash Manufacturer ID Codes */ @@ -833,11 +963,14 @@ struct nand_flash_dev { struct nand_manufacturers { int id; char *name; + int (*init)(struct mtd_info *mtd, const uint8_t *id); }; extern struct nand_flash_dev nand_flash_ids[]; extern struct nand_manufacturers nand_manuf_ids[]; +int hynix_nand_init(struct mtd_info *mtd, const uint8_t *id); + extern int nand_default_bbt(struct mtd_info *mtd); extern int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs); extern int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs); @@ -847,6 +980,41 @@ extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf); +static inline int nand_rnd_config(struct mtd_info *mtd, int page, int column, + enum nand_rnd_action action) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->cur_rnd && chip->cur_rnd->config) + return chip->cur_rnd->config(mtd, page, column, action); + + return 0; +} + +static inline void nand_rnd_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->cur_rnd && chip->cur_rnd->read_buf) + chip->cur_rnd->write_buf(mtd, buf, len); + else + chip->write_buf(mtd, buf, len); +} + +static inline void nand_rnd_read_buf(struct mtd_info *mtd, uint8_t *buf, + int len) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->cur_rnd && chip->cur_rnd->read_buf) + chip->cur_rnd->read_buf(mtd, buf, len); + else + chip->read_buf(mtd, buf, len); +} + +int nand_rnd_is_activ(struct mtd_info *mtd, int page, int column, int *len); + /** * struct platform_nand_chip - chip level device structure * @nr_chips: max. number of chips to scan for @@ -979,6 +1147,23 @@ static inline int jedec_feature(struct nand_chip *chip) : 0; } +/** + * struct ofnandpart_data - struct used to retrieve NAND partitions from a DT + * node + * @parse: driver specific parser function + * @priv: driver private data + * @node: OF node containing NAND partitions + */ +struct ofnandpart_data { + struct nand_part *(*parse)(void *priv, struct mtd_info *master, + struct device_node *pp); + void *priv; + struct device_node *node; +}; + +int ofnandpart_parse(struct mtd_info *master, + const struct ofnandpart_data *data); + /* * struct nand_sdr_timings - SDR NAND chip timings * diff --git a/include/linux/of_mtd.h b/include/linux/of_mtd.h index e266caa36402..1059472915d2 100644 --- a/include/linux/of_mtd.h +++ b/include/linux/of_mtd.h @@ -15,6 +15,7 @@ int of_get_nand_ecc_mode(struct device_node *np); int of_get_nand_ecc_step_size(struct device_node *np); int of_get_nand_ecc_strength(struct device_node *np); +int of_get_nand_rnd_mode(struct device_node *np); int of_get_nand_bus_width(struct device_node *np); bool of_get_nand_on_flash_bbt(struct device_node *np); @@ -35,6 +36,11 @@ static inline int of_get_nand_ecc_strength(struct device_node *np) return -ENOSYS; } +static inline int of_get_nand_rnd_mode(struct device_node *np) +{ + return -ENOSYS; +} + static inline int of_get_nand_bus_width(struct device_node *np) { return -ENOSYS;