diff options
author | Tom Rini <trini@konsulko.com> | 2019-04-24 09:04:23 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2019-04-24 09:04:23 -0400 |
commit | 3fbd2dce351ab5d40d3244f26bd713caa4f826e2 (patch) | |
tree | 4a8e5c2fd2dee4abed674da4ce0312af06f97bc1 | |
parent | 8a94262435ca559a5e2dad79f2dc3c130e9047a8 (diff) | |
parent | 74a7e0018a97a0e7318c4c7a3b473fd9ebbb5ad1 (diff) | |
download | u-boot-3fbd2dce351ab5d40d3244f26bd713caa4f826e2.zip u-boot-3fbd2dce351ab5d40d3244f26bd713caa4f826e2.tar.gz u-boot-3fbd2dce351ab5d40d3244f26bd713caa4f826e2.tar.bz2 |
Merge branch '2019-04-22-master-imports'
- Add and enable brcmnand driver on a number of relevant platforms.
Also add and enable LED drivers on more bcm platforms.
- Various ARMv8 fixes/improvements, including extending PSCI
functionality.
- fs_loader improvments
- Various FIT/SPL improvements
- PCI bugfixes
- Poplar platform ethernet support
- MediaTek MMC improvements
- Android boot improvements
92 files changed, 7993 insertions, 264 deletions
diff --git a/.travis.yml b/.travis.yml index eb531f1..951b6a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,7 @@ addons: - liblz4-tool - libisl15 - clang-7 + - srecord install: # Clone uboot-test-hooks diff --git a/MAINTAINERS b/MAINTAINERS index aa4b3bc..b9cb686 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -86,6 +86,7 @@ M: Albert Aribaud <albert.u.boot@aribaud.net> S: Maintained T: git git://git.denx.de/u-boot-arm.git F: arch/arm/ +F: cmd/arm/ ARM ALTERA SOCFPGA M: Marek Vasut <marex@denx.de> @@ -677,6 +678,7 @@ M: Rick Chen <rick@andestech.com> S: Maintained T: git git://git.denx.de/u-boot-riscv.git F: arch/riscv/ +F: cmd/riscv/ F: tools/prelink-riscv.c ROCKUSB @@ -788,6 +790,7 @@ M: Bin Meng <bmeng.cn@gmail.com> S: Maintained T: git git://git.denx.de/u-boot-x86.git F: arch/x86/ +F: cmd/x86/ XTENSA M: Max Filippov <jcmvbkbc@gmail.com> @@ -1977,6 +1977,13 @@ endif $(build)=$(build-dir) $(@:.ko=.o) $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost +quiet_cmd_genenv = GENENV $@ +cmd_genenv = $(OBJCOPY) --dump-section .rodata.default_environment=$@ env/common.o; \ + sed --in-place -e 's/\x00/\x0A/g' $@ + +u-boot-initial-env: u-boot.bin + $(call if_changed,genenv) + # Consistency checks # --------------------------------------------------------------------------- diff --git a/arch/arm/cpu/armv8/Makefile b/arch/arm/cpu/armv8/Makefile index a5f5433..b349b13 100644 --- a/arch/arm/cpu/armv8/Makefile +++ b/arch/arm/cpu/armv8/Makefile @@ -19,7 +19,9 @@ endif obj-y += cache.o obj-y += tlb.o obj-y += transition.o +ifndef CONFIG_ARMV8_PSCI obj-y += fwcall.o +endif obj-y += cpu-dt.o obj-$(CONFIG_ARM_SMCCC) += smccc-call.o diff --git a/arch/arm/cpu/armv8/fwcall.c b/arch/arm/cpu/armv8/fwcall.c index 9957c29..b0aca1b 100644 --- a/arch/arm/cpu/armv8/fwcall.c +++ b/arch/arm/cpu/armv8/fwcall.c @@ -28,7 +28,6 @@ static void hvc_call(struct pt_regs *args) "ldr x4, %4\n" "ldr x5, %5\n" "ldr x6, %6\n" - "ldr x7, %7\n" "hvc #0\n" "str x0, %0\n" "str x1, %1\n" @@ -37,7 +36,7 @@ static void hvc_call(struct pt_regs *args) : "+m" (args->regs[0]), "+m" (args->regs[1]), "+m" (args->regs[2]), "+m" (args->regs[3]) : "m" (args->regs[4]), "m" (args->regs[5]), - "m" (args->regs[6]), "m" (args->regs[7]) + "m" (args->regs[6]) : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17"); diff --git a/arch/arm/cpu/armv8/psci.S b/arch/arm/cpu/armv8/psci.S index 358df8f..7ffc8db 100644 --- a/arch/arm/cpu/armv8/psci.S +++ b/arch/arm/cpu/armv8/psci.S @@ -8,6 +8,7 @@ #include <config.h> #include <linux/linkage.h> #include <asm/psci.h> +#include <asm/secure.h> /* Default PSCI function, return -1, Not Implemented */ #define PSCI_DEFAULT(__fn) \ @@ -19,8 +20,8 @@ /* PSCI function and ID table definition*/ #define PSCI_TABLE(__id, __fn) \ - .word __id; \ - .word __fn + .quad __id; \ + .quad __fn .pushsection ._secure.text, "ax" @@ -132,33 +133,52 @@ PSCI_TABLE(0, 0) /* Caller must put PSCI function-ID table base in x9 */ handle_psci: psci_enter -1: ldr x10, [x9] /* Load PSCI function table */ - ubfx x11, x10, #32, #32 - ubfx x10, x10, #0, #32 +1: ldr x10, [x9] /* Load PSCI function table */ cbz x10, 3f /* If reach the end, bail out */ cmp x10, x0 b.eq 2f /* PSCI function found */ - add x9, x9, #8 /* If not match, try next entry */ + add x9, x9, #16 /* If not match, try next entry */ b 1b -2: blr x11 /* Call PSCI function */ +2: ldr x11, [x9, #8] /* Load PSCI function */ + blr x11 /* Call PSCI function */ psci_return 3: mov x0, #ARM_PSCI_RET_NI psci_return -unknown_smc_id: - ldr x0, =0xFFFFFFFF +/* + * Handle SiP service functions defined in SiP service function table. + * Use DECLARE_SECURE_SVC(_name, _id, _fn) to add platform specific SiP + * service function into the SiP service function table. + * SiP service function table is located in '._secure_svc_tbl_entries' section, + * which is next to '._secure.text' section. + */ +handle_svc: + adr x9, __secure_svc_tbl_start + adr x10, __secure_svc_tbl_end + subs x12, x10, x9 /* Get number of entries in table */ + b.eq 2f /* Make sure SiP function table is not empty */ + psci_enter +1: ldr x10, [x9] /* Load SiP function table */ + ldr x11, [x9, #8] + cmp w10, w0 + b.eq 2b /* SiP service function found */ + add x9, x9, #SECURE_SVC_TBL_OFFSET /* Move to next entry */ + subs x12, x12, #SECURE_SVC_TBL_OFFSET + b.eq 3b /* If reach the end, bail out */ + b 1b +2: ldr x0, =0xFFFFFFFF eret handle_smc32: /* SMC function ID 0x84000000-0x8400001F: 32 bits PSCI */ ldr w9, =0x8400001F cmp w0, w9 - b.gt unknown_smc_id + b.gt handle_svc ldr w9, =0x84000000 cmp w0, w9 - b.lt unknown_smc_id + b.lt handle_svc adr x9, _psci_32_table b handle_psci @@ -171,10 +191,10 @@ handle_smc64: /* SMC function ID 0xC4000000-0xC400001F: 64 bits PSCI */ ldr x9, =0xC400001F cmp x0, x9 - b.gt unknown_smc_id + b.gt handle_svc ldr x9, =0xC4000000 cmp x0, x9 - b.lt unknown_smc_id + b.lt handle_svc adr x9, _psci_64_table b handle_psci diff --git a/arch/arm/cpu/armv8/u-boot.lds b/arch/arm/cpu/armv8/u-boot.lds index 53de80f..2554980 100644 --- a/arch/arm/cpu/armv8/u-boot.lds +++ b/arch/arm/cpu/armv8/u-boot.lds @@ -58,6 +58,10 @@ SECTIONS AT(ADDR(.__secure_start) + SIZEOF(.__secure_start)) { *(._secure.text) + . = ALIGN(8); + __secure_svc_tbl_start = .; + KEEP(*(._secure_svc_tbl_entries)) + __secure_svc_tbl_end = .; } .secure_data : AT(LOADADDR(.secure_text) + SIZEOF(.secure_text)) diff --git a/arch/arm/dts/bcm63158.dtsi b/arch/arm/dts/bcm63158.dtsi index 4f41f62..4b2eaee 100644 --- a/arch/arm/dts/bcm63158.dtsi +++ b/arch/arm/dts/bcm63158.dtsi @@ -82,6 +82,13 @@ status = "disabled"; }; + leds: led-controller@ff800800 { + compatible = "brcm,bcm6858-leds"; + reg = <0x0 0xff800800 0x0 0xe4>; + + status = "disabled"; + }; + wdt1: watchdog@ff800480 { compatible = "brcm,bcm6345-wdt"; reg = <0x0 0xff800480 0x0 0x14>; @@ -178,5 +185,18 @@ status = "disabled"; }; + + nand: nand-controller@ff801800 { + compatible = "brcm,nand-bcm63158", + "brcm,brcmnand-v5.0", + "brcm,brcmnand"; + reg-names = "nand", "nand-int-base", "nand-cache"; + reg = <0x0 0xff801800 0x0 0x180>, + <0x0 0xff802000 0x0 0x10>, + <0x0 0xff801c00 0x0 0x200>; + parameter-page-big-endian = <0>; + + status = "disabled"; + }; }; }; diff --git a/arch/arm/dts/bcm6858.dtsi b/arch/arm/dts/bcm6858.dtsi index 5d5e64d..76ba0ea 100644 --- a/arch/arm/dts/bcm6858.dtsi +++ b/arch/arm/dts/bcm6858.dtsi @@ -82,6 +82,13 @@ status = "disabled"; }; + leds: led-controller@ff800800 { + compatible = "brcm,bcm6858-leds"; + reg = <0x0 0xff800800 0x0 0xe4>; + + status = "disabled"; + }; + wdt1: watchdog@ff802780 { compatible = "brcm,bcm6345-wdt"; reg = <0x0 0xff802780 0x0 0x14>; @@ -178,5 +185,18 @@ status = "disabled"; }; + + nand: nand-controller@ff801800 { + compatible = "brcm,nand-bcm6858", + "brcm,brcmnand-v5.0", + "brcm,brcmnand"; + reg-names = "nand", "nand-int-base", "nand-cache"; + reg = <0x0 0xff801800 0x0 0x180>, + <0x0 0xff802000 0x0 0x10>, + <0x0 0xff801c00 0x0 0x200>; + parameter-page-big-endian = <0>; + + status = "disabled"; + }; }; }; diff --git a/arch/arm/dts/bcm963158.dts b/arch/arm/dts/bcm963158.dts index b5c825b..8565944 100644 --- a/arch/arm/dts/bcm963158.dts +++ b/arch/arm/dts/bcm963158.dts @@ -61,3 +61,67 @@ &gpio7 { status = "okay"; }; + +&nand { + status = "okay"; + write-protect = <0>; + #address-cells = <1>; + #size-cells = <0>; + + nandcs@0 { + compatible = "brcm,nandcs"; + reg = <0>; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + brcm,nand-oob-sector-size = <16>; + }; +}; + +&leds { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + brcm,serial-led-en-pol; + brcm,serial-led-data-ppol; + + led@16 { + reg = <16>; + label = "red:dsl2"; + }; + + led@17 { + reg = <17>; + label = "green:dsl1"; + }; + + led@18 { + reg = <18>; + label = "green:fxs2"; + }; + + led@19 { + reg = <19>; + label = "green:fxs1"; + }; + + led@26 { + reg = <26>; + label = "green:wan1_act"; + }; + + led@27 { + reg = <27>; + label = "green:wps"; + }; + + led@28 { + reg = <28>; + active-low; + label = "green:aggregate_act"; + }; + + led@29 { + reg = <29>; + label = "green:aggregate_link"; + }; +}; diff --git a/arch/arm/dts/bcm968580xref.dts b/arch/arm/dts/bcm968580xref.dts index 15febb0..861e989 100644 --- a/arch/arm/dts/bcm968580xref.dts +++ b/arch/arm/dts/bcm968580xref.dts @@ -61,3 +61,66 @@ &gpio7 { status = "okay"; }; + +&nand { + status = "okay"; + write-protect = <0>; + #address-cells = <1>; + #size-cells = <0>; + + nandcs@0 { + compatible = "brcm,nandcs"; + reg = <0>; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + brcm,nand-oob-sector-size = <16>; + }; +}; + +&leds { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + brcm,serial-led-en-pol; + brcm,serial-led-data-ppol; + + led@2 { + reg = <2>; + label = "green:inet"; + }; + + led@5 { + reg = <5>; + label = "red:alarm"; + }; + + led@8 { + reg = <8>; + label = "green:wlan_link"; + }; + + led@11 { + reg = <11>; + label = "green:fxs1"; + }; + + led@14 { + reg = <14>; + label = "green:fxs2"; + }; + + led@15 { + reg = <15>; + label = "green:usb0"; + }; + + led@16 { + reg = <16>; + label = "green:usb1"; + }; + + led@17 { + reg = <17>; + label = "green:wps"; + }; +}; diff --git a/arch/arm/dts/hi3798cv200-u-boot.dtsi b/arch/arm/dts/hi3798cv200-u-boot.dtsi index 7844c52..2de06d9 100644 --- a/arch/arm/dts/hi3798cv200-u-boot.dtsi +++ b/arch/arm/dts/hi3798cv200-u-boot.dtsi @@ -8,7 +8,15 @@ * (C) Copyright 2017 Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org> */ +#include <dt-bindings/reset/ti-syscon.h> + &soc { + rst: reset-controller@8a22000 { + compatible = "hisilicon,hi3798cv200-reset"; + reg = <0x8a22000 0x1000>; + #reset-cells = <3>; + }; + usb2: ehci@9890000 { compatible = "generic-ehci"; reg = <0x9890000 0x100>; @@ -16,6 +24,12 @@ }; }; +&gmac1 { + resets = <&rst 0xcc 9 ASSERT_SET>, + <&rst 0xcc 11 ASSERT_SET>, + <&rst 0xcc 13 DEASSERT_SET>; +}; + &uart0 { clock = <75000000>; status = "okay"; diff --git a/arch/arm/dts/mt8516-u-boot.dtsi b/arch/arm/dts/mt8516-u-boot.dtsi new file mode 100644 index 0000000..3c0d843 --- /dev/null +++ b/arch/arm/dts/mt8516-u-boot.dtsi @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2019 BayLibre, SAS + * Author: Fabien Parent <fparent@baylibre.com> + */ + +&infracfg { + u-boot,dm-pre-reloc; +}; + +&topckgen_ { + u-boot,dm-pre-reloc; +}; + +&topckgen_cg { + u-boot,dm-pre-reloc; +}; + +&apmixedsys { + u-boot,dm-pre-reloc; +}; + +&uart0 { + u-boot,dm-pre-reloc; +}; diff --git a/arch/arm/dts/mt8516.dtsi b/arch/arm/dts/mt8516.dtsi new file mode 100644 index 0000000..1c33582 --- /dev/null +++ b/arch/arm/dts/mt8516.dtsi @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2019 BayLibre, SAS + * Author: Fabien Parent <fparent@baylibre.com> + */ + +#include <dt-bindings/clock/mt8516-clk.h> +#include <dt-bindings/gpio/gpio.h> +#include <dt-bindings/interrupt-controller/irq.h> +#include <dt-bindings/interrupt-controller/arm-gic.h> + +/ { + compatible = "mediatek,mt8516"; + interrupt-parent = <&sysirq>; + #address-cells = <1>; + #size-cells = <1>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + enable-method = "mediatek,mt8516-smp"; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a35"; + reg = <0x0>; + clock-frequency = <1300000000>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a35"; + reg = <0x1>; + clock-frequency = <1300000000>; + }; + + cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a35"; + reg = <0x2>; + clock-frequency = <1300000000>; + }; + + cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a35"; + reg = <0x3>; + clock-frequency = <1300000000>; + }; + }; + + topckgen: clock-controller@10000000 { + compatible = "mediatek,mt8516-topckgen"; + reg = <0x10000000 0x1000>; + #clock-cells = <1>; + }; + + topckgen_cg: clock-controller-cg@10000000 { + compatible = "mediatek,mt8516-topckgen-cg"; + reg = <0x10000000 0x1000>; + #clock-cells = <1>; + }; + + infracfg: clock-controller@10001000 { + compatible = "mediatek,mt8516-infracfg"; + reg = <0x10001000 0x1000>; + #clock-cells = <1>; + }; + + apmixedsys: clock-controller@10018000 { + compatible = "mediatek,mt8516-apmixedsys"; + reg = <0x10018000 0x710>; + #clock-cells = <1>; + }; + + gic: interrupt-controller@10310000 { + compatible = "arm,gic-400"; + interrupt-controller; + #interrupt-cells = <3>; + interrupt-parent = <&gic>; + reg = <0x10310000 0x1000>, + <0x10320000 0x1000>, + <0x10340000 0x2000>, + <0x10360000 0x2000>; + interrupts = <GIC_PPI 9 + (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>; + }; + + sysirq: interrupt-controller@10200620 { + compatible = "mediatek,sysirq"; + interrupt-controller; + #interrupt-cells = <3>; + interrupt-parent = <&gic>; + reg = <0x10200620 0x20>; + }; + + watchdog: watchdog@10007000 { + compatible = "mediatek,wdt"; + reg = <0x10007000 0x1000>; + interrupts = <GIC_SPI 198 IRQ_TYPE_EDGE_FALLING>; + #reset-cells = <1>; + status = "disabled"; + }; + + pinctrl: pinctrl@10005000 { + compatible = "mediatek,mt8516-pinctrl"; + reg = <0x10005000 0x1000>; + + gpio: gpio-controller { + gpio-controller; + #gpio-cells = <2>; + }; + }; + + mmc0: mmc@11120000 { + compatible = "mediatek,mt8516-mmc"; + reg = <0x11120000 0x1000>; + interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_LOW>; + clocks = <&topckgen_cg CLK_TOP_MSDC0>, + <&topckgen CLK_TOP_AHB_INFRA_SEL>, + <&topckgen_cg CLK_TOP_MSDC0_INFRA>; + clock-names = "source", "hclk", "source_cg"; + status = "disabled"; + }; + + uart0: serial@11005000 { + compatible = "mediatek,hsuart"; + reg = <0x11005000 0x1000>; + reg-shift = <2>; + interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_LOW>; + clocks = <&topckgen CLK_TOP_UART0_SEL>, + <&topckgen_cg CLK_TOP_UART0>; + clock-names = "baud","bus"; + status = "disabled"; + }; +}; diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h index 12bc7fb..e6d27b6 100644 --- a/arch/arm/include/asm/io.h +++ b/arch/arm/include/asm/io.h @@ -123,6 +123,27 @@ static inline void __raw_readsl(unsigned long addr, void *data, int longlen) #define readq(c) ({ u64 __v = __arch_getq(c); __iormb(); __v; }) /* + * Relaxed I/O memory access primitives. These follow the Device memory + * ordering rules but do not guarantee any ordering relative to Normal memory + * accesses. + */ +#define readb_relaxed(c) ({ u8 __r = __raw_readb(c); __r; }) +#define readw_relaxed(c) ({ u16 __r = le16_to_cpu((__force __le16) \ + __raw_readw(c)); __r; }) +#define readl_relaxed(c) ({ u32 __r = le32_to_cpu((__force __le32) \ + __raw_readl(c)); __r; }) +#define readq_relaxed(c) ({ u64 __r = le64_to_cpu((__force __le64) \ + __raw_readq(c)); __r; }) + +#define writeb_relaxed(v, c) ((void)__raw_writeb((v), (c))) +#define writew_relaxed(v, c) ((void)__raw_writew((__force u16) \ + cpu_to_le16(v), (c))) +#define writel_relaxed(v, c) ((void)__raw_writel((__force u32) \ + cpu_to_le32(v), (c))) +#define writeq_relaxed(v, c) ((void)__raw_writeq((__force u64) \ + cpu_to_le64(v), (c))) + +/* * The compiler seems to be incapable of optimising constants * properly. Spell it out to the compiler in some cases. * These are only valid for small values of "off" (< 1<<12) diff --git a/arch/arm/include/asm/secure.h b/arch/arm/include/asm/secure.h index d23044a..50582c9 100644 --- a/arch/arm/include/asm/secure.h +++ b/arch/arm/include/asm/secure.h @@ -6,6 +6,37 @@ #define __secure __attribute__ ((section ("._secure.text"))) #define __secure_data __attribute__ ((section ("._secure.data"))) +#ifndef __ASSEMBLY__ + +typedef struct secure_svc_tbl { + u32 id; +#ifdef CONFIG_ARMV8_PSCI + u8 pad[4]; +#endif + void *func; +} secure_svc_tbl_t; + +/* + * Macro to declare a SiP function service in '_secure_svc_tbl_entries' section + */ +#define DECLARE_SECURE_SVC(_name, _id, _fn) \ + static const secure_svc_tbl_t __secure_svc_ ## _name \ + __attribute__((used, section("._secure_svc_tbl_entries"))) \ + = { \ + .id = _id, \ + .func = _fn } + +#else + +#ifdef CONFIG_ARMV8_PSCI +#define SECURE_SVC_TBL_OFFSET 16 +#else +#define SECURE_SVC_TBL_OFFSET 8 + +#endif + +#endif /* __ASSEMBLY__ */ + #if defined(CONFIG_ARMV7_SECURE_BASE) || defined(CONFIG_ARMV8_SECURE_BASE) /* * Warning, horror ahead. diff --git a/arch/arm/lib/relocate_64.S b/arch/arm/lib/relocate_64.S index 7603f52..26d29c5 100644 --- a/arch/arm/lib/relocate_64.S +++ b/arch/arm/lib/relocate_64.S @@ -26,9 +26,10 @@ ENTRY(relocate_code) /* * Copy u-boot from flash to RAM */ - adr x1, __image_copy_start /* x1 <- Run &__image_copy_start */ - subs x9, x0, x1 /* x8 <- Run to copy offset */ - b.eq relocate_done /* skip relocation */ + adrp x1, __image_copy_start /* x1 <- address bits [31:12] */ + add x1, x1, :lo12:__image_copy_start/* x1 <- address bits [11:00] */ + subs x9, x0, x1 /* x9 <- Run to copy offset */ + b.eq relocate_done /* skip relocation */ /* * Don't ldr x1, __image_copy_start here, since if the code is already * running at an address other than it was linked to, that instruction @@ -42,8 +43,10 @@ ENTRY(relocate_code) ldr x1, _TEXT_BASE /* x1 <- Linked &__image_copy_start */ subs x9, x0, x1 /* x9 <- Link to copy offset */ - adr x1, __image_copy_start /* x1 <- Run &__image_copy_start */ - adr x2, __image_copy_end /* x2 <- Run &__image_copy_end */ + adrp x1, __image_copy_start /* x1 <- address bits [31:12] */ + add x1, x1, :lo12:__image_copy_start/* x1 <- address bits [11:00] */ + adrp x2, __image_copy_end /* x2 <- address bits [31:12] */ + add x2, x2, :lo12:__image_copy_end /* x2 <- address bits [11:00] */ copy_loop: ldp x10, x11, [x1], #16 /* copy from source address [x1] */ stp x10, x11, [x0], #16 /* copy to target address [x0] */ @@ -54,8 +57,10 @@ copy_loop: /* * Fix .rela.dyn relocations */ - adr x2, __rel_dyn_start /* x2 <- Run &__rel_dyn_start */ - adr x3, __rel_dyn_end /* x3 <- Run &__rel_dyn_end */ + adrp x2, __rel_dyn_start /* x2 <- address bits [31:12] */ + add x2, x2, :lo12:__rel_dyn_start /* x2 <- address bits [11:00] */ + adrp x3, __rel_dyn_end /* x3 <- address bits [31:12] */ + add x3, x3, :lo12:__rel_dyn_end /* x3 <- address bits [11:00] */ fixloop: ldp x0, x1, [x2], #16 /* (x0,x1) <- (SRC location, fixup) */ ldr x4, [x2], #8 /* x4 <- addend */ diff --git a/arch/arm/mach-mediatek/Kconfig b/arch/arm/mach-mediatek/Kconfig index 7a733e9..b5e91d4 100644 --- a/arch/arm/mach-mediatek/Kconfig +++ b/arch/arm/mach-mediatek/Kconfig @@ -31,6 +31,16 @@ config TARGET_MT7629 including DDR3, crypto engine, 3x3 11n/ac Wi-Fi, Gigabit Ethernet, switch, USB3.0, PCIe, UART, SPI, I2C and PWM. +config TARGET_MT8516 + bool "MediaTek MT8516 SoC" + select ARM64 + select ARCH_MISC_INIT + help + The MediaTek MT8516 is a ARM64-based SoC with a quad-core Cortex-A35. + including UART, SPI, USB2.0 and OTG, SD and MMC cards, NAND, PWM, + Ethernet, IR TX/RX, I2C, I2S, S/PDIF, and built-in Wi-Fi / Bluetooth combo + chip and several DDR3 and DDR4 options. + endchoice source "board/mediatek/mt7623/Kconfig" diff --git a/arch/arm/mach-mediatek/Makefile b/arch/arm/mach-mediatek/Makefile index b5d3a37..ea414dc 100644 --- a/arch/arm/mach-mediatek/Makefile +++ b/arch/arm/mach-mediatek/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_SPL_BUILD) += spl.o obj-$(CONFIG_TARGET_MT7623) += mt7623/ obj-$(CONFIG_TARGET_MT7629) += mt7629/ +obj-$(CONFIG_TARGET_MT8516) += mt8516/ diff --git a/arch/arm/mach-mediatek/mt8516/Makefile b/arch/arm/mach-mediatek/mt8516/Makefile new file mode 100644 index 0000000..886ab7e --- /dev/null +++ b/arch/arm/mach-mediatek/mt8516/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-y += init.o diff --git a/arch/arm/mach-mediatek/mt8516/init.c b/arch/arm/mach-mediatek/mt8516/init.c new file mode 100644 index 0000000..26a215a --- /dev/null +++ b/arch/arm/mach-mediatek/mt8516/init.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 MediaTek Inc. + * Copyright (C) 2019 BayLibre, SAS + * Author: Fabien Parent <fparent@baylibre.com> + */ + +#include <clk.h> +#include <common.h> +#include <dm.h> +#include <fdtdec.h> +#include <ram.h> +#include <asm/arch/misc.h> +#include <asm/armv8/mmu.h> +#include <asm/sections.h> +#include <dm/uclass.h> +#include <linux/io.h> +#include <dt-bindings/clock/mt8516-clk.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define WDOG_SWRST 0x10007014 +#define WDOG_SWRST_KEY 0x1209 + +int dram_init(void) +{ + int ret; + + ret = fdtdec_setup_memory_banksize(); + if (ret) + return ret; + + return fdtdec_setup_mem_size_base(); +} + +int dram_init_banksize(void) +{ + gd->bd->bi_dram[0].start = gd->ram_base; + gd->bd->bi_dram[0].size = gd->ram_size; + + return 0; +} + +int mtk_pll_early_init(void) +{ + unsigned long pll_rates[] = { + [CLK_APMIXED_ARMPLL] = 1300000000, + [CLK_APMIXED_MAINPLL] = 1501000000, + [CLK_APMIXED_UNIVPLL] = 1248000000, + [CLK_APMIXED_MMPLL] = 380000000, + }; + struct udevice *dev; + int ret, i; + + ret = uclass_get_device_by_driver(UCLASS_CLK, + DM_GET_DRIVER(mtk_clk_apmixedsys), &dev); + if (ret) + return ret; + + /* configure default rate then enable apmixedsys */ + for (i = 0; i < ARRAY_SIZE(pll_rates); i++) { + struct clk clk = { .id = i, .dev = dev }; + + ret = clk_set_rate(&clk, pll_rates[i]); + if (ret) + return ret; + + ret = clk_enable(&clk); + if (ret) + return ret; + } + + return 0; +} + +int mtk_soc_early_init(void) +{ + int ret; + + /* initialize early clocks */ + ret = mtk_pll_early_init(); + if (ret) + return ret; + + return 0; +} + +void reset_cpu(ulong addr) +{ + while (1) { + writel(WDOG_SWRST_KEY, WDOG_SWRST); + mdelay(5); + } +} + +int print_cpuinfo(void) +{ + printf("CPU: MediaTek MT8516\n"); + return 0; +} + +static struct mm_region mt8516_mem_map[] = { + { + /* DDR */ + .virt = 0x40000000UL, + .phys = 0x40000000UL, + .size = 0x20000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_OUTER_SHARE, + }, { + .virt = 0x00000000UL, + .phys = 0x00000000UL, + .size = 0x20000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + 0, + } +}; +struct mm_region *mem_map = mt8516_mem_map; diff --git a/arch/mips/dts/brcm,bcm6838.dtsi b/arch/mips/dts/brcm,bcm6838.dtsi index c060802..6676f83 100644 --- a/arch/mips/dts/brcm,bcm6838.dtsi +++ b/arch/mips/dts/brcm,bcm6838.dtsi @@ -125,5 +125,18 @@ status = "disabled"; }; + + nand: nand-controller@14e02200 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "brcm,nand-bcm6838", + "brcm,brcmnand-v5.0", + "brcm,brcmnand"; + reg-names = "nand", "nand-int-base", "nand-cache"; + reg = <0x14e02200 0x180>, + <0x14e000f0 0x10>, + <0x14e02600 0x180>; + status = "disabled"; + }; }; }; diff --git a/arch/mips/dts/brcm,bcm968380gerg.dts b/arch/mips/dts/brcm,bcm968380gerg.dts index 98471e3..5a5ac0e 100644 --- a/arch/mips/dts/brcm,bcm968380gerg.dts +++ b/arch/mips/dts/brcm,bcm968380gerg.dts @@ -50,3 +50,15 @@ &gpio_mid1 { status = "okay"; }; + +&nand { + status = "okay"; + + nandcs@0 { + compatible = "brcm,nandcs"; + reg = <0>; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + brcm,nand-oob-sector-size = <16>; + }; +}; diff --git a/board/davinci/da8xxevm/da850evm.c b/board/davinci/da8xxevm/da850evm.c index e8ec553..1bc2682 100644 --- a/board/davinci/da8xxevm/da850evm.c +++ b/board/davinci/da8xxevm/da850evm.c @@ -204,25 +204,6 @@ int misc_init_r(void) return 0; } -#ifndef CONFIG_DM_MMC -#ifdef CONFIG_MMC_DAVINCI -static struct davinci_mmc mmc_sd0 = { - .reg_base = (struct davinci_mmc_regs *)DAVINCI_MMC_SD0_BASE, - .host_caps = MMC_MODE_4BIT, /* DA850 supports only 4-bit SD/MMC */ - .voltages = MMC_VDD_32_33 | MMC_VDD_33_34, - .version = MMC_CTLR_VERSION_2, -}; - -int board_mmc_init(bd_t *bis) -{ - mmc_sd0.input_clk = clk_get(DAVINCI_MMCSD_CLKID); - - /* Add slot-0 to mmc subsystem */ - return davinci_mmc_init(bis, &mmc_sd0); -} -#endif -#endif - static const struct pinmux_config gpio_pins[] = { #ifdef CONFIG_USE_NOR /* GP0[11] is required for NOR to work on Rev 3 EVMs */ diff --git a/cmd/Kconfig b/cmd/Kconfig index 2bdbfcb..5d1999e 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1433,6 +1433,12 @@ config CMD_EFIDEBUG particularly for managing boot parameters as well as examining various EFI status for debugging. +config CMD_EXCEPTION + bool "exception - raise exception" + depends on ARM || RISCV || X86 + help + Enable the 'exception' command which allows to raise an exception. + config CMD_LED bool "led" depends on LED diff --git a/cmd/Makefile b/cmd/Makefile index 6b1c6b0..7864fcf 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -173,6 +173,8 @@ obj-$(CONFIG_CMD_BLOB) += blob.o # Android Verified Boot 2.0 obj-$(CONFIG_CMD_AVB) += avb.o +obj-$(CONFIG_ARM) += arm/ +obj-$(CONFIG_RISCV) += riscv/ obj-$(CONFIG_X86) += x86/ obj-$(CONFIG_ARCH_MVEBU) += mvebu/ diff --git a/cmd/arm/Makefile b/cmd/arm/Makefile new file mode 100644 index 0000000..94367dc --- /dev/null +++ b/cmd/arm/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ + +ifdef CONFIG_ARM64 +obj-$(CONFIG_CMD_EXCEPTION) += exception64.o +else +obj-$(CONFIG_CMD_EXCEPTION) += exception.o +endif diff --git a/cmd/arm/exception.c b/cmd/arm/exception.c new file mode 100644 index 0000000..33bc759 --- /dev/null +++ b/cmd/arm/exception.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * The 'exception' command can be used for testing exception handling. + * + * Copyright (c) 2018, Heinrich Schuchardt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <command.h> + +static int do_unaligned(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + /* + * The LDRD instruction requires the data source to be four byte aligned + * even if strict alignment fault checking is disabled in the system + * control register. + */ + asm volatile ( + "MOV r5, sp\n" + "ADD r5, #1\n" + "LDRD r6, r7, [r5]\n"); + return CMD_RET_FAILURE; +} + +static int do_breakpoint(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + asm volatile ("BKPT #123\n"); + return CMD_RET_FAILURE; +} + +static int do_undefined(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + /* + * 0xe7f...f. is undefined in ARM mode + * 0xde.. is undefined in Thumb mode + */ + asm volatile (".word 0xe7f7defb\n"); + return CMD_RET_FAILURE; +} + +static cmd_tbl_t cmd_sub[] = { + U_BOOT_CMD_MKENT(breakpoint, CONFIG_SYS_MAXARGS, 1, do_breakpoint, + "", ""), + U_BOOT_CMD_MKENT(unaligned, CONFIG_SYS_MAXARGS, 1, do_unaligned, + "", ""), + U_BOOT_CMD_MKENT(undefined, CONFIG_SYS_MAXARGS, 1, do_undefined, + "", ""), +}; + +static char exception_help_text[] = + "<ex>\n" + " The following exceptions are available:\n" + " breakpoint - prefetch abort\n" + " unaligned - data abort\n" + " undefined - undefined instruction\n" + ; + +#include <exception.h> diff --git a/cmd/arm/exception64.c b/cmd/arm/exception64.c new file mode 100644 index 0000000..a363818 --- /dev/null +++ b/cmd/arm/exception64.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * The 'exception' command can be used for testing exception handling. + * + * Copyright (c) 2018, Heinrich Schuchardt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <command.h> + +static int do_undefined(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + /* + * 0xe7f...f. is undefined in ARM mode + * 0xde.. is undefined in Thumb mode + */ + asm volatile (".word 0xe7f7defb\n"); + return CMD_RET_FAILURE; +} + +static cmd_tbl_t cmd_sub[] = { + U_BOOT_CMD_MKENT(undefined, CONFIG_SYS_MAXARGS, 1, do_undefined, + "", ""), +}; + +static char exception_help_text[] = + "<ex>\n" + " The following exceptions are available:\n" + " undefined - undefined instruction\n" + ; + +#include <exception.h> diff --git a/cmd/riscv/Makefile b/cmd/riscv/Makefile new file mode 100644 index 0000000..24df023 --- /dev/null +++ b/cmd/riscv/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_CMD_EXCEPTION) += exception.o diff --git a/cmd/riscv/exception.c b/cmd/riscv/exception.c new file mode 100644 index 0000000..547fb7d --- /dev/null +++ b/cmd/riscv/exception.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * The 'exception' command can be used for testing exception handling. + * + * Copyright (c) 2018, Heinrich Schuchardt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <command.h> + +static int do_undefined(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + asm volatile (".word 0xffffffff\n"); + return CMD_RET_FAILURE; +} + +static cmd_tbl_t cmd_sub[] = { + U_BOOT_CMD_MKENT(undefined, CONFIG_SYS_MAXARGS, 1, do_undefined, + "", ""), +}; + +static char exception_help_text[] = + "<ex>\n" + " The following exceptions are available:\n" + " undefined - undefined instruction\n" + ; + +#include <exception.h> diff --git a/cmd/x86/Makefile b/cmd/x86/Makefile index bcc6d06..7071614 100644 --- a/cmd/x86/Makefile +++ b/cmd/x86/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0+ obj-y += mtrr.o +obj-$(CONFIG_CMD_EXCEPTION) += exception.o obj-$(CONFIG_HAVE_FSP) += fsp.o diff --git a/cmd/x86/exception.c b/cmd/x86/exception.c new file mode 100644 index 0000000..ade1e2e --- /dev/null +++ b/cmd/x86/exception.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * The 'exception' command can be used for testing exception handling. + * + * Copyright (c) 2018, Heinrich Schuchardt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <command.h> + +static int do_undefined(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + asm volatile (".word 0xffff\n"); + return CMD_RET_FAILURE; +} + +static cmd_tbl_t cmd_sub[] = { + U_BOOT_CMD_MKENT(undefined, CONFIG_SYS_MAXARGS, 1, do_undefined, + "", ""), +}; + +static char exception_help_text[] = + "<ex>\n" + " The following exceptions are available:\n" + " undefined - undefined instruction\n" + ; + +#include <exception.h> @@ -159,9 +159,9 @@ do_imgextract(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) } } - /* get subimage data address and length */ - if (fit_image_get_data(fit_hdr, noffset, - &fit_data, &fit_len)) { + /* get subimage/external data address and length */ + if (fit_image_get_data_and_size(fit_hdr, noffset, + &fit_data, &fit_len)) { puts("Could not find script subimage data\n"); return 1; } diff --git a/common/bootm.c b/common/bootm.c index 3adbcea..b5d37d3 100644 --- a/common/bootm.c +++ b/common/bootm.c @@ -154,7 +154,7 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, #ifdef CONFIG_ANDROID_BOOT_IMAGE case IMAGE_FORMAT_ANDROID: images.os.type = IH_TYPE_KERNEL; - images.os.comp = IH_COMP_NONE; + images.os.comp = android_image_get_kcomp(os_hdr); images.os.os = IH_OS_LINUX; images.os.end = android_image_get_end(os_hdr); @@ -450,7 +450,6 @@ static int bootm_load_os(bootm_headers_t *images, int boot_progress) ulong image_start = os.image_start; ulong image_len = os.image_len; ulong flush_start = ALIGN_DOWN(load, ARCH_DMA_MINALIGN); - ulong flush_len; bool no_overlap; void *load_buf, *image_buf; int err; @@ -465,11 +464,7 @@ static int bootm_load_os(bootm_headers_t *images, int boot_progress) return err; } - flush_len = load_end - load; - if (flush_start < load) - flush_len += load - flush_start; - - flush_cache(flush_start, ALIGN(flush_len, ARCH_DMA_MINALIGN)); + flush_cache(flush_start, ALIGN(load_end, ARCH_DMA_MINALIGN) - flush_start); debug(" kernel loaded at 0x%08lx, end = 0x%08lx\n", load, load_end); bootstage_mark(BOOTSTAGE_ID_KERNEL_LOADED); diff --git a/common/dlmalloc.c b/common/dlmalloc.c index edaad29..6f12a18 100644 --- a/common/dlmalloc.c +++ b/common/dlmalloc.c @@ -1893,8 +1893,7 @@ Void_t* mEMALIGn(alignment, bytes) size_t alignment; size_t bytes; #if CONFIG_VAL(SYS_MALLOC_F_LEN) if (!(gd->flags & GD_FLG_FULL_MALLOC_INIT)) { - nb = roundup(bytes, alignment); - return malloc_simple(nb); + return memalign_simple(alignment, bytes); } #endif diff --git a/common/image-android.c b/common/image-android.c index 2f38c19..8b0f6b3 100644 --- a/common/image-android.c +++ b/common/image-android.c @@ -8,6 +8,7 @@ #include <android_image.h> #include <malloc.h> #include <errno.h> +#include <asm/unaligned.h> #define ANDROID_IMAGE_DEFAULT_KERNEL_ADDR 0x10008000 @@ -126,6 +127,16 @@ ulong android_image_get_kload(const struct andr_img_hdr *hdr) return android_image_get_kernel_addr(hdr); } +ulong android_image_get_kcomp(const struct andr_img_hdr *hdr) +{ + const void *p = (void *)((uintptr_t)hdr + hdr->page_size); + + if (get_unaligned_le32(p) == LZ4F_MAGIC) + return IH_COMP_LZ4; + else + return IH_COMP_NONE; +} + int android_image_get_ramdisk(const struct andr_img_hdr *hdr, ulong *rd_data, ulong *rd_len) { @@ -186,7 +197,7 @@ void android_print_contents(const struct andr_img_hdr *hdr) printf("%skernel size: %x\n", p, hdr->kernel_size); printf("%skernel address: %x\n", p, hdr->kernel_addr); printf("%sramdisk size: %x\n", p, hdr->ramdisk_size); - printf("%sramdisk addrress: %x\n", p, hdr->ramdisk_addr); + printf("%sramdisk address: %x\n", p, hdr->ramdisk_addr); printf("%ssecond size: %x\n", p, hdr->second_size); printf("%ssecond address: %x\n", p, hdr->second_addr); printf("%stags address: %x\n", p, hdr->tags_addr); diff --git a/common/image-fdt.c b/common/image-fdt.c index 01186ae..9ed00b7 100644 --- a/common/image-fdt.c +++ b/common/image-fdt.c @@ -284,7 +284,7 @@ int boot_get_fdt(int flag, int argc, char * const argv[], uint8_t arch, *of_flat_tree = NULL; *of_size = 0; - img_addr = simple_strtoul(argv[0], NULL, 16); + img_addr = (argc == 0) ? load_addr : simple_strtoul(argv[0], NULL, 16); buf = map_sysmem(img_addr, 0); if (argc > 2) diff --git a/common/image-fit.c b/common/image-fit.c index ac901e1..a74b44f 100644 --- a/common/image-fit.c +++ b/common/image-fit.c @@ -2118,6 +2118,18 @@ int boot_get_fdt_fit(bootm_headers_t *images, ulong addr, if (next_config) *next_config++ = '\0'; uname = NULL; + + /* + * fit_image_load() would load the first FDT from the + * extra config only when uconfig is specified. + * Check if the extra config contains multiple FDTs and + * if so, load them. + */ + cfg_noffset = fit_conf_get_node(fit, uconfig); + + i = 0; + count = fit_conf_get_prop_node_count(fit, cfg_noffset, + FIT_FDT_PROP); } debug("%d: using uname=%s uconfig=%s\n", i, uname, uconfig); diff --git a/common/image.c b/common/image.c index 4d4248f..75b84d5 100644 --- a/common/image.c +++ b/common/image.c @@ -957,7 +957,7 @@ int boot_get_ramdisk(int argc, char * const argv[], bootm_headers_t *images, */ buf = map_sysmem(images->os.start, 0); if (buf && genimg_get_format(buf) == IMAGE_FORMAT_ANDROID) - select = argv[0]; + select = (argc == 0) ? env_get("loadaddr") : argv[0]; #endif if (argc >= 2) diff --git a/common/spl/spl_nand.c b/common/spl/spl_nand.c index 6eb190f..e2bcefb 100644 --- a/common/spl/spl_nand.c +++ b/common/spl/spl_nand.c @@ -17,6 +17,10 @@ static int spl_nand_load_image(struct spl_image_info *spl_image, { nand_init(); + printf("Loading U-Boot from 0x%08x (size 0x%08x) to 0x%08x\n", + CONFIG_SYS_NAND_U_BOOT_OFFS, CONFIG_SYS_NAND_U_BOOT_SIZE, + CONFIG_SYS_NAND_U_BOOT_DST); + nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS, CONFIG_SYS_NAND_U_BOOT_SIZE, (void *)CONFIG_SYS_NAND_U_BOOT_DST); diff --git a/common/spl/spl_ymodem.c b/common/spl/spl_ymodem.c index 25226e9..fa539ec 100644 --- a/common/spl/spl_ymodem.c +++ b/common/spl/spl_ymodem.c @@ -89,7 +89,25 @@ static int spl_ymodem_load_image(struct spl_image_info *spl_image, if (res <= 0) goto end_stream; - if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && + if (IS_ENABLED(CONFIG_SPL_LOAD_FIT_FULL) && + image_get_magic((struct image_header *)buf) == FDT_MAGIC) { + addr = CONFIG_SYS_LOAD_ADDR; + ih = (struct image_header *)addr; + + memcpy((void *)addr, buf, res); + size += res; + addr += res; + + while ((res = xyzModem_stream_read(buf, BUF_SIZE, &err)) > 0) { + memcpy((void *)addr, buf, res); + size += res; + addr += res; + } + + ret = spl_parse_image_header(spl_image, ih); + if (ret) + return ret; + } else if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && image_get_magic((struct image_header *)buf) == FDT_MAGIC) { struct spl_load_info load; struct ymodem_fit_info info; diff --git a/configs/bcm963158_ram_defconfig b/configs/bcm963158_ram_defconfig index 5659249..528b714 100644 --- a/configs/bcm963158_ram_defconfig +++ b/configs/bcm963158_ram_defconfig @@ -21,6 +21,8 @@ CONFIG_CMD_BOOTEFI_SELFTEST=y # CONFIG_CMD_UNZIP is not set # CONFIG_CMD_FLASH is not set CONFIG_CMD_GPIO=y +CONFIG_CMD_MTD=y +CONFIG_CMD_NAND=y CONFIG_CMD_CACHE=y CONFIG_DOS_PARTITION=y CONFIG_ISO_PARTITION=y @@ -31,7 +33,14 @@ CONFIG_BLK=y CONFIG_CLK=y CONFIG_DM_GPIO=y CONFIG_BCM6345_GPIO=y +CONFIG_LED=y +CONFIG_LED_BCM6858=y +CONFIG_LED_BLINK=y # CONFIG_MMC is not set +CONFIG_MTD=y +CONFIG_NAND=y +CONFIG_NAND_BRCMNAND=y +CONFIG_NAND_BRCMNAND_63158=y CONFIG_SPECIFY_CONSOLE_INDEX=y # CONFIG_SPL_SERIAL_PRESENT is not set CONFIG_CONS_INDEX=0 diff --git a/configs/bcm968380gerg_ram_defconfig b/configs/bcm968380gerg_ram_defconfig index fa9dc85..53423e5 100644 --- a/configs/bcm968380gerg_ram_defconfig +++ b/configs/bcm968380gerg_ram_defconfig @@ -26,6 +26,7 @@ CONFIG_CMD_MEMINFO=y # CONFIG_CMD_FLASH is not set CONFIG_CMD_GPIO=y # CONFIG_CMD_LOADS is not set +CONFIG_CMD_NAND=y # CONFIG_CMD_MISC is not set CONFIG_DEFAULT_DEVICE_TREE="brcm,bcm968380gerg" # CONFIG_NET is not set @@ -36,6 +37,12 @@ CONFIG_BCM6345_GPIO=y CONFIG_LED=y CONFIG_LED_BCM6328=y CONFIG_LED_BLINK=y +CONFIG_MTD=y +CONFIG_NAND=y +CONFIG_NAND_BRCMNAND=y +CONFIG_NAND_BRCMNAND_6838=y +CONFIG_SPI_FLASH=y +# CONFIG_SPI_FLASH_USE_4K_SECTORS is not set CONFIG_PHY=y CONFIG_BCM6368_USBH_PHY=y CONFIG_PINCTRL=y diff --git a/configs/bcm968580xref_ram_defconfig b/configs/bcm968580xref_ram_defconfig index 456ece7..62c33d1 100644 --- a/configs/bcm968580xref_ram_defconfig +++ b/configs/bcm968580xref_ram_defconfig @@ -16,6 +16,9 @@ CONFIG_DISPLAY_BOARDINFO_LATE=y CONFIG_HUSH_PARSER=y CONFIG_CMD_BOOTEFI_SELFTEST=y CONFIG_CMD_GPIO=y +CONFIG_CMD_MTD=y +CONFIG_CMD_NAND=y +CONFIG_CMD_PART=y CONFIG_DOS_PARTITION=y CONFIG_ISO_PARTITION=y CONFIG_EFI_PARTITION=y @@ -25,7 +28,14 @@ CONFIG_BLK=y CONFIG_CLK=y CONFIG_DM_GPIO=y CONFIG_BCM6345_GPIO=y +CONFIG_LED=y +CONFIG_LED_BCM6858=y +CONFIG_LED_BLINK=y # CONFIG_MMC is not set +CONFIG_MTD=y +CONFIG_NAND=y +CONFIG_NAND_BRCMNAND=y +CONFIG_NAND_BRCMNAND_6858=y CONFIG_SPECIFY_CONSOLE_INDEX=y # CONFIG_SPL_SERIAL_PRESENT is not set CONFIG_CONS_INDEX=0 diff --git a/configs/poplar_defconfig b/configs/poplar_defconfig index 81bd370..76ab5eb 100644 --- a/configs/poplar_defconfig +++ b/configs/poplar_defconfig @@ -19,6 +19,9 @@ CONFIG_FASTBOOT_FLASH_MMC_DEV=0 CONFIG_DM_MMC=y CONFIG_MMC_DW=y CONFIG_MMC_DW_K3=y +CONFIG_DM_ETH=y +CONFIG_HIGMACV300_ETH=y +CONFIG_RESET_HISILICON=y CONFIG_USB=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_EHCI_GENERIC=y diff --git a/doc/device-tree-bindings/leds/leds-bcm6858.txt b/doc/device-tree-bindings/leds/leds-bcm6858.txt new file mode 100644 index 0000000..ea2fe23 --- /dev/null +++ b/doc/device-tree-bindings/leds/leds-bcm6858.txt @@ -0,0 +1,51 @@ +LEDs connected to Broadcom BCM6858 controller + +This controller is present on BCM6858, BCM6328, BCM6362 and BCM63268. +In these SoCs it's possible to control LEDs both as GPIOs or by hardware. + +Required properties: + - compatible : should be "brcm,bcm6858-leds". + - #address-cells : must be 1. + - #size-cells : must be 0. + - reg : BCM6858 LED controller address and size. + +Optional properties: + - brcm,serial-led-msb-first : Boolean, msb data come out first on serial data pin + Default : false + - brcm,serial-led-en-pol : Boolean, serial led polarity (true => active high) + Default : false + - brcm,serial-led-clk-pol : Boolean, serial clock polarity (true => active high) + Default : false + - brcm,serial-led-data-ppol : Boolean, serial data polarity (true => active high) + Default : false + - brcm,serial-shift-inv : Boolean, led test mode + Default : false + +Each LED is represented as a sub-node of the brcm,bcm6858-leds device. + +LED sub-node required properties: + - reg : LED pin number (only LEDs 0 to 32 are valid). + +LED sub-node optional properties: + - label : see Documentation/devicetree/bindings/leds/common.txt + - active-low : Boolean, makes LED active low. + Default : false + +Examples: +BCM6328 with 2 GPIO LEDs + leds0: led-controller@ff800800 { + compatible = "brcm,bcm6858-leds"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0xff800800 0x0 0xe4>; + + led@2 { + reg = <2>; + label = "green:inet"; + }; + + led@5 { + reg = <5>; + label = "red:alarm"; + }; + }; diff --git a/doc/driver-model/fs_firmware_loader.txt b/doc/driver-model/fs_firmware_loader.txt index b9aee84..8be6185 100644 --- a/doc/driver-model/fs_firmware_loader.txt +++ b/doc/driver-model/fs_firmware_loader.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2018 Intel Corporation <www.intel.com> +# Copyright (C) 2018-2019 Intel Corporation <www.intel.com> # # SPDX-License-Identifier: GPL-2.0 @@ -27,7 +27,7 @@ Firmware storage device described in device tree source defined in fs-loader node as shown in below: Example for block device: - fs_loader0: fs-loader@0 { + fs_loader0: fs-loader { u-boot,dm-pre-reloc; compatible = "u-boot,fs-loader"; phandlepart = <&mmc 1>; @@ -39,22 +39,55 @@ Firmware storage device described in device tree source device, it can be described in FDT as shown in below: Example for ubi: - fs_loader1: fs-loader@1 { + fs_loader1: fs-loader { u-boot,dm-pre-reloc; compatible = "u-boot,fs-loader"; mtdpart = "UBI", ubivol = "ubi0"; }; - Then, firmware_loader property would be set with the path of fs_loader - node under /chosen node such as: + Then, firmware-loader property can be added with any device node, which + driver would use the firmware loader for loading. + + The value of the firmware-loader property should be set with phandle + of the fs-loader node. + For example: + firmware-loader = <&fs_loader0>; + + If there are majority of devices using the same fs-loader node, then + firmware-loader property can be added under /chosen node instead of + adding to each of device node. + + For example: /{ chosen { - firmware_loader = &fs_loader0; + firmware-loader = <&fs_loader0>; }; }; - However, this driver is also designed to support U-boot environment + In each respective driver of devices using firmware loader, the firmware + loaded instance should be created by DT phandle. + + For example of getting DT phandle from /chosen and creating instance: + chosen_node = ofnode_path("/chosen"); + if (!ofnode_valid(chosen_node)) { + debug("/chosen node was not found.\n"); + return -ENOENT; + } + + phandle_p = ofnode_get_property(chosen_node, "firmware-loader", &size); + if (!phandle_p) { + debug("firmware-loader property was not found.\n"); + return -ENOENT; + } + + phandle = fdt32_to_cpu(*phandle_p); + ret = uclass_get_device_by_phandle_id(UCLASS_FS_FIRMWARE_LOADER, + phandle, &dev); + if (ret) + return ret; + + Firmware loader driver is also designed to support U-boot environment variables, so all these data from FDT can be overwritten through the U-boot environment variable during run time. For examples: @@ -104,9 +137,12 @@ return: Description: The firmware is loaded directly into the buffer pointed to by buf -Example of creating firmware loader instance and calling -request_firmware_into_buf API: - if (uclass_get_device(UCLASS_FS_FIRMWARE_LOADER, 0, &dev)) { - request_firmware_into_buf(dev, filename, buffer_location, - buffer_size, offset_ofreading); - } +Example of calling request_firmware_into_buf API after creating firmware loader +instance: + ret = uclass_get_device_by_phandle_id(UCLASS_FS_FIRMWARE_LOADER, + phandle, &dev); + if (ret) + return ret; + + request_firmware_into_buf(dev, filename, buffer_location, buffer_size, + offset_ofreading); diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile index 0632dc8..a47a5bd 100644 --- a/drivers/clk/mediatek/Makefile +++ b/drivers/clk/mediatek/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_ARCH_MEDIATEK) += clk-mtk.o # SoC Drivers obj-$(CONFIG_TARGET_MT7623) += clk-mt7623.o obj-$(CONFIG_TARGET_MT7629) += clk-mt7629.o +obj-$(CONFIG_TARGET_MT8516) += clk-mt8516.o diff --git a/drivers/clk/mediatek/clk-mt8516.c b/drivers/clk/mediatek/clk-mt8516.c new file mode 100644 index 0000000..071bf69 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt8516.c @@ -0,0 +1,802 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek clock driver for MT8516 SoC + * + * Copyright (C) 2018 BayLibre, SAS + * Author: Fabien Parent <fparent@baylibre.com> + */ + +#include <common.h> +#include <dm.h> +#include <asm/io.h> +#include <dt-bindings/clock/mt8516-clk.h> + +#include "clk-mtk.h" + +#define MT8516_PLL_FMAX (1502UL * MHZ) +#define MT8516_CON0_RST_BAR BIT(27) + +/* apmixedsys */ +#define PLL(_id, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, _pd_reg, \ + _pd_shift, _pcw_reg, _pcw_shift) { \ + .id = _id, \ + .reg = _reg, \ + .pwr_reg = _pwr_reg, \ + .en_mask = _en_mask, \ + .rst_bar_mask = MT8516_CON0_RST_BAR, \ + .fmax = MT8516_PLL_FMAX, \ + .flags = _flags, \ + .pcwbits = _pcwbits, \ + .pd_reg = _pd_reg, \ + .pd_shift = _pd_shift, \ + .pcw_reg = _pcw_reg, \ + .pcw_shift = _pcw_shift, \ + } + +static const struct mtk_pll_data apmixed_plls[] = { + PLL(CLK_APMIXED_ARMPLL, 0x0100, 0x0110, 0x00000001, 0, + 21, 0x0104, 24, 0x0104, 0), + PLL(CLK_APMIXED_MAINPLL, 0x0120, 0x0130, 0x00000001, + HAVE_RST_BAR, 21, 0x0124, 24, 0x0124, 0), + PLL(CLK_APMIXED_UNIVPLL, 0x0140, 0x0150, 0x30000001, + HAVE_RST_BAR, 7, 0x0144, 24, 0x0144, 0), + PLL(CLK_APMIXED_MMPLL, 0x0160, 0x0170, 0x00000001, 0, + 21, 0x0164, 24, 0x0164, 0), + PLL(CLK_APMIXED_APLL1, 0x0180, 0x0190, 0x00000001, 0, + 31, 0x0180, 1, 0x0184, 0), + PLL(CLK_APMIXED_APLL2, 0x01A0, 0x01B0, 0x00000001, 0, + 31, 0x01A0, 1, 0x01A4, 0), +}; + +/* topckgen */ +#define FACTOR0(_id, _parent, _mult, _div) \ + FACTOR(_id, _parent, _mult, _div, CLK_PARENT_APMIXED) + +#define FACTOR1(_id, _parent, _mult, _div) \ + FACTOR(_id, _parent, _mult, _div, CLK_PARENT_TOPCKGEN) + +#define FACTOR2(_id, _parent, _mult, _div) \ + FACTOR(_id, _parent, _mult, _div, 0) + +static const struct mtk_fixed_clk top_fixed_clks[] = { + FIXED_CLK(CLK_TOP_CLK_NULL, CLK_XTAL, 26000000), + FIXED_CLK(CLK_TOP_I2S_INFRA_BCK, CLK_TOP_CLK_NULL, 26000000), + FIXED_CLK(CLK_TOP_MEMPLL, CLK_TOP_CLK26M, 800000000), +}; + +static const struct mtk_fixed_factor top_fixed_divs[] = { + FACTOR1(CLK_TOP_DMPLL, CLK_TOP_MEMPLL, 1, 1), + FACTOR0(CLK_TOP_MAINPLL_D2, CLK_APMIXED_MAINPLL, 1, 2), + FACTOR0(CLK_TOP_MAINPLL_D4, CLK_APMIXED_MAINPLL, 1, 4), + FACTOR0(CLK_TOP_MAINPLL_D8, CLK_APMIXED_MAINPLL, 1, 8), + FACTOR0(CLK_TOP_MAINPLL_D16, CLK_APMIXED_MAINPLL, 1, 16), + FACTOR0(CLK_TOP_MAINPLL_D11, CLK_APMIXED_MAINPLL, 1, 11), + FACTOR0(CLK_TOP_MAINPLL_D22, CLK_APMIXED_MAINPLL, 1, 22), + FACTOR0(CLK_TOP_MAINPLL_D3, CLK_APMIXED_MAINPLL, 1, 3), + FACTOR0(CLK_TOP_MAINPLL_D6, CLK_APMIXED_MAINPLL, 1, 6), + FACTOR0(CLK_TOP_MAINPLL_D12, CLK_APMIXED_MAINPLL, 1, 12), + FACTOR0(CLK_TOP_MAINPLL_D5, CLK_APMIXED_MAINPLL, 1, 5), + FACTOR0(CLK_TOP_MAINPLL_D10, CLK_APMIXED_MAINPLL, 1, 10), + FACTOR0(CLK_TOP_MAINPLL_D20, CLK_APMIXED_MAINPLL, 1, 20), + FACTOR0(CLK_TOP_MAINPLL_D40, CLK_APMIXED_MAINPLL, 1, 40), + FACTOR0(CLK_TOP_MAINPLL_D7, CLK_APMIXED_MAINPLL, 1, 7), + FACTOR0(CLK_TOP_MAINPLL_D14, CLK_APMIXED_MAINPLL, 1, 14), + FACTOR0(CLK_TOP_UNIVPLL_D2, CLK_APMIXED_UNIVPLL, 1, 2), + FACTOR0(CLK_TOP_UNIVPLL_D4, CLK_APMIXED_UNIVPLL, 1, 4), + FACTOR0(CLK_TOP_UNIVPLL_D8, CLK_APMIXED_UNIVPLL, 1, 8), + FACTOR0(CLK_TOP_UNIVPLL_D16, CLK_APMIXED_UNIVPLL, 1, 16), + FACTOR0(CLK_TOP_UNIVPLL_D3, CLK_APMIXED_UNIVPLL, 1, 3), + FACTOR0(CLK_TOP_UNIVPLL_D6, CLK_APMIXED_UNIVPLL, 1, 6), + FACTOR0(CLK_TOP_UNIVPLL_D12, CLK_APMIXED_UNIVPLL, 1, 12), + FACTOR0(CLK_TOP_UNIVPLL_D24, CLK_APMIXED_UNIVPLL, 1, 24), + FACTOR0(CLK_TOP_UNIVPLL_D5, CLK_APMIXED_UNIVPLL, 1, 5), + FACTOR0(CLK_TOP_UNIVPLL_D20, CLK_APMIXED_UNIVPLL, 1, 20), + FACTOR0(CLK_TOP_MMPLL380M, CLK_APMIXED_MMPLL, 1, 1), + FACTOR0(CLK_TOP_MMPLL_D2, CLK_APMIXED_MMPLL, 1, 2), + FACTOR0(CLK_TOP_MMPLL_200M, CLK_APMIXED_MMPLL, 1, 3), + FACTOR0(CLK_TOP_USB_PHY48M, CLK_APMIXED_UNIVPLL, 1, 26), + FACTOR0(CLK_TOP_APLL1, CLK_APMIXED_APLL1, 1, 1), + FACTOR1(CLK_TOP_APLL1_D2, CLK_TOP_APLL1, 1, 2), + FACTOR1(CLK_TOP_APLL1_D4, CLK_TOP_RG_APLL1_D2_EN, 1, 2), + FACTOR1(CLK_TOP_APLL1_D8, CLK_TOP_RG_APLL1_D4_EN, 1, 2), + FACTOR0(CLK_TOP_APLL2, CLK_APMIXED_APLL2, 1, 1), + FACTOR1(CLK_TOP_APLL2_D2, CLK_TOP_APLL2, 1, 2), + FACTOR1(CLK_TOP_APLL2_D4, CLK_TOP_RG_APLL2_D2_EN, 1, 2), + FACTOR1(CLK_TOP_APLL2_D8, CLK_TOP_RG_APLL2_D4_EN, 1, 2), + FACTOR2(CLK_TOP_CLK26M, CLK_XTAL, 1, 1), + FACTOR2(CLK_TOP_CLK26M_D2, CLK_XTAL, 1, 2), + FACTOR1(CLK_TOP_AHB_INFRA_D2, CLK_TOP_AHB_INFRA_SEL, 1, 2), + FACTOR1(CLK_TOP_NFI1X, CLK_TOP_NFI2X_PAD_SEL, 1, 2), + FACTOR1(CLK_TOP_ETH_D2, CLK_TOP_ETH_SEL, 1, 2), +}; + +static const int uart0_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_UNIVPLL_D24, +}; + +static const int gfmux_emi1x_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_DMPLL, +}; + +static const int emi_ddrphy_parents[] = { + CLK_TOP_GFMUX_EMI1X_SEL, + CLK_TOP_GFMUX_EMI1X_SEL, +}; + +static const int ahb_infra_parents[] = { + CLK_TOP_CLK_NULL, + CLK_TOP_CLK26M, + CLK_TOP_MAINPLL_D11, + CLK_TOP_CLK_NULL, + CLK_TOP_MAINPLL_D12, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_MAINPLL_D10, +}; + +static const int csw_mux_mfg_parents[] = { + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_UNIVPLL_D3, + CLK_TOP_UNIVPLL_D2, + CLK_TOP_CLK26M, + CLK_TOP_MAINPLL_D4, + CLK_TOP_UNIVPLL_D24, + CLK_TOP_MMPLL380M, +}; + +static const int msdc0_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_UNIVPLL_D6, + CLK_TOP_MAINPLL_D8, + CLK_TOP_UNIVPLL_D8, + CLK_TOP_MAINPLL_D16, + CLK_TOP_MMPLL_200M, + CLK_TOP_MAINPLL_D12, + CLK_TOP_MMPLL_D2, +}; + +static const int pwm_mm_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_UNIVPLL_D12, +}; + +static const int uart1_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_UNIVPLL_D24, +}; + +static const int msdc1_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_UNIVPLL_D6, + CLK_TOP_MAINPLL_D8, + CLK_TOP_UNIVPLL_D8, + CLK_TOP_MAINPLL_D16, + CLK_TOP_MMPLL_200M, + CLK_TOP_MAINPLL_D12, + CLK_TOP_MMPLL_D2, +}; + +static const int spm_52m_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_UNIVPLL_D24, +}; + +static const int pmicspi_parents[] = { + CLK_TOP_UNIVPLL_D20, + CLK_TOP_USB_PHY48M, + CLK_TOP_UNIVPLL_D16, + CLK_TOP_CLK26M, +}; + +static const int qaxi_aud26m_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_AHB_INFRA_SEL, +}; + +static const int aud_intbus_parents[] = { + CLK_TOP_CLK_NULL, + CLK_TOP_CLK26M, + CLK_TOP_MAINPLL_D22, + CLK_TOP_CLK_NULL, + CLK_TOP_MAINPLL_D11, +}; + +static const int nfi2x_pad_parents[] = { + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK26M, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_MAINPLL_D12, + CLK_TOP_MAINPLL_D8, + CLK_TOP_CLK_NULL, + CLK_TOP_MAINPLL_D6, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_MAINPLL_D4, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_MAINPLL_D10, + CLK_TOP_MAINPLL_D7, + CLK_TOP_CLK_NULL, + CLK_TOP_MAINPLL_D5 +}; + +static const int nfi1x_pad_parents[] = { + CLK_TOP_AHB_INFRA_SEL, + CLK_TOP_NFI1X, +}; + +static const int mfg_mm_parents[] = { + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CSW_MUX_MFG_SEL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_MAINPLL_D3, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_CLK_NULL, + CLK_TOP_MAINPLL_D5, + CLK_TOP_MAINPLL_D7, + CLK_TOP_CLK_NULL, + CLK_TOP_MAINPLL_D14 +}; + +static const int ddrphycfg_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_MAINPLL_D16 +}; + +static const int usb_78m_parents[] = { + CLK_TOP_CLK_NULL, + CLK_TOP_CLK26M, + CLK_TOP_UNIVPLL_D16, + CLK_TOP_CLK_NULL, + CLK_TOP_MAINPLL_D20, +}; + +static const int spinor_parents[] = { + CLK_TOP_CLK26M_D2, + CLK_TOP_CLK26M, + CLK_TOP_MAINPLL_D40, + CLK_TOP_UNIVPLL_D24, + CLK_TOP_UNIVPLL_D20, + CLK_TOP_MAINPLL_D20, + CLK_TOP_MAINPLL_D16, + CLK_TOP_UNIVPLL_D12 +}; + +static const int msdc2_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_UNIVPLL_D6, + CLK_TOP_MAINPLL_D8, + CLK_TOP_UNIVPLL_D8, + CLK_TOP_MAINPLL_D16, + CLK_TOP_MMPLL_200M, + CLK_TOP_MAINPLL_D12, + CLK_TOP_MMPLL_D2 +}; + +static const int eth_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_MAINPLL_D40, + CLK_TOP_UNIVPLL_D24, + CLK_TOP_UNIVPLL_D20, + CLK_TOP_MAINPLL_D20 +}; + +static const int axi_mfg_in_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_MAINPLL_D11, + CLK_TOP_UNIVPLL_D24, + CLK_TOP_MMPLL380M, +}; + +static const int slow_mfg_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_UNIVPLL_D12, + CLK_TOP_UNIVPLL_D24 +}; + +static const int aud1_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_APLL1 +}; + +static const int aud2_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_APLL2 +}; + +static const int aud_engen1_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_RG_APLL1_D2_EN, + CLK_TOP_RG_APLL1_D4_EN, + CLK_TOP_RG_APLL1_D8_EN +}; + +static const int aud_engen2_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_RG_APLL2_D2_EN, + CLK_TOP_RG_APLL2_D4_EN, + CLK_TOP_RG_APLL2_D8_EN +}; + +static const int i2c_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_UNIVPLL_D20, + CLK_TOP_UNIVPLL_D16, + CLK_TOP_UNIVPLL_D12 +}; + +static const int aud_i2s0_m_parents[] = { + CLK_TOP_RG_AUD1, + CLK_TOP_RG_AUD2 +}; + +static const int pwm_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_UNIVPLL_D12 +}; + +static const int spi_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_UNIVPLL_D12, + CLK_TOP_UNIVPLL_D8, + CLK_TOP_UNIVPLL_D6 +}; + +static const int aud_spdifin_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_UNIVPLL_D2 +}; + +static const int uart2_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_UNIVPLL_D24 +}; + +static const int bsi_parents[] = { + CLK_TOP_CLK26M, + CLK_TOP_MAINPLL_D10, + CLK_TOP_MAINPLL_D12, + CLK_TOP_MAINPLL_D20 +}; + +static const int dbg_atclk_parents[] = { + CLK_TOP_CLK_NULL, + CLK_TOP_CLK26M, + CLK_TOP_MAINPLL_D5, + CLK_TOP_CLK_NULL, + CLK_TOP_UNIVPLL_D5 +}; + +static const int csw_nfiecc_parents[] = { + CLK_TOP_CLK_NULL, + CLK_TOP_MAINPLL_D7, + CLK_TOP_MAINPLL_D6, + CLK_TOP_CLK_NULL, + CLK_TOP_MAINPLL_D5 +}; + +static const int nfiecc_parents[] = { + CLK_TOP_CLK_NULL, + CLK_TOP_NFI2X_PAD_SEL, + CLK_TOP_MAINPLL_D4, + CLK_TOP_CLK_NULL, + CLK_TOP_CSW_NFIECC_SEL, +}; + +static const struct mtk_composite top_muxes[] = { + /* CLK_MUX_SEL0 */ + MUX(CLK_TOP_UART0_SEL, uart0_parents, 0x000, 0, 1), + MUX(CLK_TOP_GFMUX_EMI1X_SEL, gfmux_emi1x_parents, 0x000, 1, 1), + MUX(CLK_TOP_EMI_DDRPHY_SEL, emi_ddrphy_parents, 0x000, 2, 1), + MUX(CLK_TOP_AHB_INFRA_SEL, ahb_infra_parents, 0x000, 4, 4), + MUX(CLK_TOP_CSW_MUX_MFG_SEL, csw_mux_mfg_parents, 0x000, 8, 3), + MUX(CLK_TOP_MSDC0_SEL, msdc0_parents, 0x000, 11, 3), + MUX(CLK_TOP_PWM_MM_SEL, pwm_mm_parents, 0x000, 18, 1), + MUX(CLK_TOP_UART1_SEL, uart1_parents, 0x000, 19, 1), + MUX(CLK_TOP_MSDC1_SEL, msdc1_parents, 0x000, 20, 3), + MUX(CLK_TOP_SPM_52M_SEL, spm_52m_parents, 0x000, 23, 1), + MUX(CLK_TOP_PMICSPI_SEL, pmicspi_parents, 0x000, 24, 2), + MUX(CLK_TOP_QAXI_AUD26M_SEL, qaxi_aud26m_parents, 0x000, 26, 1), + MUX(CLK_TOP_AUD_INTBUS_SEL, aud_intbus_parents, 0x000, 27, 3), + /* CLK_MUX_SEL1 */ + MUX(CLK_TOP_NFI2X_PAD_SEL, nfi2x_pad_parents, 0x004, 0, 7), + MUX(CLK_TOP_NFI1X_PAD_SEL, nfi1x_pad_parents, 0x004, 7, 1), + MUX(CLK_TOP_MFG_MM_SEL, mfg_mm_parents, 0x004, 8, 6), + MUX(CLK_TOP_DDRPHYCFG_SEL, ddrphycfg_parents, 0x004, 15, 1), + MUX(CLK_TOP_USB_78M_SEL, usb_78m_parents, 0x004, 20, 3), + /* CLK_MUX_SEL8 */ + MUX(CLK_TOP_SPINOR_SEL, spinor_parents, 0x040, 0, 3), + MUX(CLK_TOP_MSDC2_SEL, msdc2_parents, 0x040, 3, 3), + MUX(CLK_TOP_ETH_SEL, eth_parents, 0x040, 6, 3), + MUX(CLK_TOP_AXI_MFG_IN_SEL, axi_mfg_in_parents, 0x040, 18, 2), + MUX(CLK_TOP_SLOW_MFG_SEL, slow_mfg_parents, 0x040, 20, 2), + MUX(CLK_TOP_AUD1_SEL, aud1_parents, 0x040, 22, 1), + MUX(CLK_TOP_AUD2_SEL, aud2_parents, 0x040, 23, 1), + MUX(CLK_TOP_AUD_ENGEN1_SEL, aud_engen1_parents, 0x040, 24, 2), + MUX(CLK_TOP_AUD_ENGEN2_SEL, aud_engen2_parents, 0x040, 26, 2), + MUX(CLK_TOP_I2C_SEL, i2c_parents, 0x040, 28, 2), + /* CLK_MUX_SEL9 */ + MUX(CLK_TOP_AUD_I2S0_M_SEL, aud_i2s0_m_parents, 0x044, 12, 1), + MUX(CLK_TOP_AUD_I2S1_M_SEL, aud_i2s0_m_parents, 0x044, 13, 1), + MUX(CLK_TOP_AUD_I2S2_M_SEL, aud_i2s0_m_parents, 0x044, 14, 1), + MUX(CLK_TOP_AUD_I2S3_M_SEL, aud_i2s0_m_parents, 0x044, 15, 1), + MUX(CLK_TOP_AUD_I2S4_M_SEL, aud_i2s0_m_parents, 0x044, 16, 1), + MUX(CLK_TOP_AUD_I2S5_M_SEL, aud_i2s0_m_parents, 0x044, 17, 1), + MUX(CLK_TOP_AUD_SPDIF_B_SEL, aud_i2s0_m_parents, 0x044, 18, 1), + /* CLK_MUX_SEL13 */ + MUX(CLK_TOP_PWM_SEL, pwm_parents, 0x07c, 0, 1), + MUX(CLK_TOP_SPI_SEL, spi_parents, 0x07c, 1, 2), + MUX(CLK_TOP_AUD_SPDIFIN_SEL, aud_spdifin_parents, 0x07c, 3, 1), + MUX(CLK_TOP_UART2_SEL, uart2_parents, 0x07c, 4, 1), + MUX(CLK_TOP_BSI_SEL, bsi_parents, 0x07c, 5, 2), + MUX(CLK_TOP_DBG_ATCLK_SEL, dbg_atclk_parents, 0x07c, 7, 3), + MUX(CLK_TOP_CSW_NFIECC_SEL, csw_nfiecc_parents, 0x07c, 10, 3), + MUX(CLK_TOP_NFIECC_SEL, nfiecc_parents, 0x07c, 13, 3), +}; + +static const struct mtk_gate_regs top0_cg_regs = { + .set_ofs = 0x50, + .clr_ofs = 0x80, + .sta_ofs = 0x20, +}; + +static const struct mtk_gate_regs top1_cg_regs = { + .set_ofs = 0x54, + .clr_ofs = 0x84, + .sta_ofs = 0x24, +}; + +static const struct mtk_gate_regs top2_cg_regs = { + .set_ofs = 0x6c, + .clr_ofs = 0x9c, + .sta_ofs = 0x3c, +}; + +static const struct mtk_gate_regs top3_cg_regs = { + .set_ofs = 0xa0, + .clr_ofs = 0xb0, + .sta_ofs = 0x70, +}; + +static const struct mtk_gate_regs top4_cg_regs = { + .set_ofs = 0xa4, + .clr_ofs = 0xb4, + .sta_ofs = 0x74, +}; + +static const struct mtk_gate_regs top5_cg_regs = { + .set_ofs = 0x44, + .clr_ofs = 0x44, + .sta_ofs = 0x44, +}; + +#define GATE_TOP0(_id, _parent, _shift) { \ + .id = _id, \ + .parent = _parent, \ + .regs = &top0_cg_regs, \ + .shift = _shift, \ + .flags = CLK_GATE_SETCLR | CLK_PARENT_TOPCKGEN, \ + } + +#define GATE_TOP1(_id, _parent, _shift) { \ + .id = _id, \ + .parent = _parent, \ + .regs = &top1_cg_regs, \ + .shift = _shift, \ + .flags = CLK_GATE_SETCLR | CLK_PARENT_TOPCKGEN, \ + } + +#define GATE_TOP2(_id, _parent, _shift) { \ + .id = _id, \ + .parent = _parent, \ + .regs = &top2_cg_regs, \ + .shift = _shift, \ + .flags = CLK_GATE_SETCLR | CLK_PARENT_TOPCKGEN, \ + } + +#define GATE_TOP2_I(_id, _parent, _shift) { \ + .id = _id, \ + .parent = _parent, \ + .regs = &top2_cg_regs, \ + .shift = _shift, \ + .flags = CLK_GATE_SETCLR_INV | CLK_PARENT_TOPCKGEN, \ + } + +#define GATE_TOP3(_id, _parent, _shift) { \ + .id = _id, \ + .parent = _parent, \ + .regs = &top3_cg_regs, \ + .shift = _shift, \ + .flags = CLK_GATE_SETCLR | CLK_PARENT_TOPCKGEN, \ + } + +#define GATE_TOP4_I(_id, _parent, _shift) { \ + .id = _id, \ + .parent = _parent, \ + .regs = &top4_cg_regs, \ + .shift = _shift, \ + .flags = CLK_GATE_SETCLR_INV | CLK_PARENT_TOPCKGEN, \ + } + +#define GATE_TOP5(_id, _parent, _shift) { \ + .id = _id, \ + .parent = _parent, \ + .regs = &top5_cg_regs, \ + .shift = _shift, \ + .flags = CLK_GATE_NO_SETCLR | CLK_PARENT_TOPCKGEN, \ + } + +static const struct mtk_gate top_clks[] = { + /* TOP0 */ + GATE_TOP0(CLK_TOP_PWM_MM, CLK_TOP_PWM_MM_SEL, 0), + GATE_TOP0(CLK_TOP_MFG_MM, CLK_TOP_MFG_MM_SEL, 2), + GATE_TOP0(CLK_TOP_SPM_52M, CLK_TOP_SPM_52M_SEL, 3), + /* TOP1 */ + GATE_TOP1(CLK_TOP_THEM, CLK_TOP_AHB_INFRA_SEL, 1), + GATE_TOP1(CLK_TOP_APDMA, CLK_TOP_AHB_INFRA_SEL, 2), + GATE_TOP1(CLK_TOP_I2C0, CLK_IFR_I2C0_SEL, 3), + GATE_TOP1(CLK_TOP_I2C1, CLK_IFR_I2C1_SEL, 4), + GATE_TOP1(CLK_TOP_AUXADC1, CLK_TOP_AHB_INFRA_SEL, 5), + GATE_TOP1(CLK_TOP_NFI, CLK_TOP_NFI1X_PAD_SEL, 6), + GATE_TOP1(CLK_TOP_NFIECC, CLK_TOP_RG_NFIECC, 7), + GATE_TOP1(CLK_TOP_DEBUGSYS, CLK_TOP_RG_DBG_ATCLK, 8), + GATE_TOP1(CLK_TOP_PWM, CLK_TOP_AHB_INFRA_SEL, 9), + GATE_TOP1(CLK_TOP_UART0, CLK_TOP_UART0_SEL, 10), + GATE_TOP1(CLK_TOP_UART1, CLK_TOP_UART1_SEL, 11), + GATE_TOP1(CLK_TOP_BTIF, CLK_TOP_AHB_INFRA_SEL, 12), + GATE_TOP1(CLK_TOP_USB, CLK_TOP_USB_78M, 13), + GATE_TOP1(CLK_TOP_FLASHIF_26M, CLK_TOP_CLK26M, 14), + GATE_TOP1(CLK_TOP_AUXADC2, CLK_TOP_AHB_INFRA_SEL, 15), + GATE_TOP1(CLK_TOP_I2C2, CLK_IFR_I2C2_SEL, 16), + GATE_TOP1(CLK_TOP_MSDC0, CLK_TOP_MSDC0_SEL, 17), + GATE_TOP1(CLK_TOP_MSDC1, CLK_TOP_MSDC1_SEL, 18), + GATE_TOP1(CLK_TOP_NFI2X, CLK_TOP_NFI2X_PAD_SEL, 19), + GATE_TOP1(CLK_TOP_PMICWRAP_AP, CLK_TOP_CLK26M, 20), + GATE_TOP1(CLK_TOP_SEJ, CLK_TOP_AHB_INFRA_SEL, 21), + GATE_TOP1(CLK_TOP_MEMSLP_DLYER, CLK_TOP_CLK26M, 22), + GATE_TOP1(CLK_TOP_SPI, CLK_TOP_SPI_SEL, 23), + GATE_TOP1(CLK_TOP_APXGPT, CLK_TOP_CLK26M, 24), + GATE_TOP1(CLK_TOP_AUDIO, CLK_TOP_CLK26M, 25), + GATE_TOP1(CLK_TOP_PMICWRAP_MD, CLK_TOP_CLK26M, 27), + GATE_TOP1(CLK_TOP_PMICWRAP_CONN, CLK_TOP_CLK26M, 28), + GATE_TOP1(CLK_TOP_PMICWRAP_26M, CLK_TOP_CLK26M, 29), + GATE_TOP1(CLK_TOP_AUX_ADC, CLK_TOP_CLK26M, 30), + GATE_TOP1(CLK_TOP_AUX_TP, CLK_TOP_CLK26M, 31), + /* TOP2 */ + GATE_TOP2(CLK_TOP_MSDC2, CLK_TOP_AHB_INFRA_SEL, 0), + GATE_TOP2(CLK_TOP_RBIST, CLK_TOP_UNIVPLL_D12, 1), + GATE_TOP2(CLK_TOP_NFI_BUS, CLK_TOP_AHB_INFRA_SEL, 2), + GATE_TOP2(CLK_TOP_GCE, CLK_TOP_AHB_INFRA_SEL, 4), + GATE_TOP2(CLK_TOP_TRNG, CLK_TOP_AHB_INFRA_SEL, 5), + GATE_TOP2(CLK_TOP_SEJ_13M, CLK_TOP_CLK26M, 6), + GATE_TOP2(CLK_TOP_AES, CLK_TOP_AHB_INFRA_SEL, 7), + GATE_TOP2(CLK_TOP_PWM_B, CLK_TOP_RG_PWM_INFRA, 8), + GATE_TOP2(CLK_TOP_PWM1_FB, CLK_TOP_RG_PWM_INFRA, 9), + GATE_TOP2(CLK_TOP_PWM2_FB, CLK_TOP_RG_PWM_INFRA, 10), + GATE_TOP2(CLK_TOP_PWM3_FB, CLK_TOP_RG_PWM_INFRA, 11), + GATE_TOP2(CLK_TOP_PWM4_FB, CLK_TOP_RG_PWM_INFRA, 12), + GATE_TOP2(CLK_TOP_PWM5_FB, CLK_TOP_RG_PWM_INFRA, 13), + GATE_TOP2(CLK_TOP_USB_1P, CLK_TOP_USB_78M, 14), + GATE_TOP2(CLK_TOP_FLASHIF_FREERUN, CLK_TOP_AHB_INFRA_SEL, 15), + GATE_TOP2(CLK_TOP_66M_ETH, CLK_TOP_AHB_INFRA_D2, 19), + GATE_TOP2(CLK_TOP_133M_ETH, CLK_TOP_AHB_INFRA_SEL, 20), + GATE_TOP2(CLK_TOP_FETH_25M, CLK_IFR_ETH_25M_SEL, 21), + GATE_TOP2(CLK_TOP_FETH_50M, CLK_TOP_RG_ETH, 22), + GATE_TOP2(CLK_TOP_FLASHIF_AXI, CLK_TOP_AHB_INFRA_SEL, 23), + GATE_TOP2(CLK_TOP_USBIF, CLK_TOP_AHB_INFRA_SEL, 24), + GATE_TOP2(CLK_TOP_UART2, CLK_TOP_RG_UART2, 25), + GATE_TOP2(CLK_TOP_BSI, CLK_TOP_AHB_INFRA_SEL, 26), + GATE_TOP2_I(CLK_TOP_MSDC0_INFRA, CLK_TOP_MSDC0, 28), + GATE_TOP2_I(CLK_TOP_MSDC1_INFRA, CLK_TOP_MSDC1, 29), + GATE_TOP2_I(CLK_TOP_MSDC2_INFRA, CLK_TOP_RG_MSDC2, 30), + GATE_TOP2(CLK_TOP_USB_78M, CLK_TOP_USB_78M_SEL, 31), + /* TOP3 */ + GATE_TOP3(CLK_TOP_RG_SPINOR, CLK_TOP_SPINOR_SEL, 0), + GATE_TOP3(CLK_TOP_RG_MSDC2, CLK_TOP_MSDC2_SEL, 1), + GATE_TOP3(CLK_TOP_RG_ETH, CLK_TOP_ETH_SEL, 2), + GATE_TOP3(CLK_TOP_RG_AXI_MFG, CLK_TOP_AXI_MFG_IN_SEL, 6), + GATE_TOP3(CLK_TOP_RG_SLOW_MFG, CLK_TOP_SLOW_MFG_SEL, 7), + GATE_TOP3(CLK_TOP_RG_AUD1, CLK_TOP_AUD1_SEL, 8), + GATE_TOP3(CLK_TOP_RG_AUD2, CLK_TOP_AUD2_SEL, 9), + GATE_TOP3(CLK_TOP_RG_AUD_ENGEN1, CLK_TOP_AUD_ENGEN1_SEL, 10), + GATE_TOP3(CLK_TOP_RG_AUD_ENGEN2, CLK_TOP_AUD_ENGEN2_SEL, 11), + GATE_TOP3(CLK_TOP_RG_I2C, CLK_TOP_I2C_SEL, 12), + GATE_TOP3(CLK_TOP_RG_PWM_INFRA, CLK_TOP_PWM_SEL, 13), + GATE_TOP3(CLK_TOP_RG_AUD_SPDIF_IN, CLK_TOP_AUD_SPDIFIN_SEL, 14), + GATE_TOP3(CLK_TOP_RG_UART2, CLK_TOP_UART2_SEL, 15), + GATE_TOP3(CLK_TOP_RG_BSI, CLK_TOP_BSI_SEL, 16), + GATE_TOP3(CLK_TOP_RG_DBG_ATCLK, CLK_TOP_DBG_ATCLK_SEL, 17), + GATE_TOP3(CLK_TOP_RG_NFIECC, CLK_TOP_NFIECC_SEL, 18), + /* TOP4 */ + GATE_TOP4_I(CLK_TOP_RG_APLL1_D2_EN, CLK_TOP_APLL1_D2, 8), + GATE_TOP4_I(CLK_TOP_RG_APLL1_D4_EN, CLK_TOP_APLL1_D4, 9), + GATE_TOP4_I(CLK_TOP_RG_APLL1_D8_EN, CLK_TOP_APLL1_D8, 10), + GATE_TOP4_I(CLK_TOP_RG_APLL2_D2_EN, CLK_TOP_APLL2_D2, 11), + GATE_TOP4_I(CLK_TOP_RG_APLL2_D4_EN, CLK_TOP_APLL2_D4, 12), + GATE_TOP4_I(CLK_TOP_RG_APLL2_D8_EN, CLK_TOP_APLL2_D8, 13), + /* TOP5 */ + GATE_TOP5(CLK_TOP_APLL12_DIV0, CLK_TOP_APLL12_CK_DIV0, 0), + GATE_TOP5(CLK_TOP_APLL12_DIV1, CLK_TOP_APLL12_CK_DIV1, 1), + GATE_TOP5(CLK_TOP_APLL12_DIV2, CLK_TOP_APLL12_CK_DIV2, 2), + GATE_TOP5(CLK_TOP_APLL12_DIV3, CLK_TOP_APLL12_CK_DIV3, 3), + GATE_TOP5(CLK_TOP_APLL12_DIV4, CLK_TOP_APLL12_CK_DIV4, 4), + GATE_TOP5(CLK_TOP_APLL12_DIV4B, CLK_TOP_APLL12_CK_DIV4B, 5), + GATE_TOP5(CLK_TOP_APLL12_DIV5, CLK_TOP_APLL12_CK_DIV5, 6), + GATE_TOP5(CLK_TOP_APLL12_DIV5B, CLK_TOP_APLL12_CK_DIV5B, 7), + GATE_TOP5(CLK_TOP_APLL12_DIV6, CLK_TOP_APLL12_CK_DIV6, 8), +}; + +static const struct mtk_clk_tree mt8516_clk_tree = { + .xtal_rate = 26 * MHZ, + .xtal2_rate = 26 * MHZ, + .fdivs_offs = CLK_TOP_DMPLL, + .muxes_offs = CLK_TOP_UART0_SEL, + .plls = apmixed_plls, + .fclks = top_fixed_clks, + .fdivs = top_fixed_divs, + .muxes = top_muxes, +}; + +static int mt8516_apmixedsys_probe(struct udevice *dev) +{ + return mtk_common_clk_init(dev, &mt8516_clk_tree); +} + +static int mt8516_topckgen_probe(struct udevice *dev) +{ + return mtk_common_clk_init(dev, &mt8516_clk_tree); +} + +static int mt8516_topckgen_cg_probe(struct udevice *dev) +{ + return mtk_common_clk_gate_init(dev, &mt8516_clk_tree, top_clks); +} + +static const struct udevice_id mt8516_apmixed_compat[] = { + { .compatible = "mediatek,mt8516-apmixedsys", }, + { } +}; + +static const struct udevice_id mt8516_topckgen_compat[] = { + { .compatible = "mediatek,mt8516-topckgen", }, + { } +}; + +static const struct udevice_id mt8516_topckgen_cg_compat[] = { + { .compatible = "mediatek,mt8516-topckgen-cg", }, + { } +}; + +U_BOOT_DRIVER(mtk_clk_apmixedsys) = { + .name = "mt8516-apmixedsys", + .id = UCLASS_CLK, + .of_match = mt8516_apmixed_compat, + .probe = mt8516_apmixedsys_probe, + .priv_auto_alloc_size = sizeof(struct mtk_clk_priv), + .ops = &mtk_clk_apmixedsys_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +U_BOOT_DRIVER(mtk_clk_topckgen) = { + .name = "mt8516-topckgen", + .id = UCLASS_CLK, + .of_match = mt8516_topckgen_compat, + .probe = mt8516_topckgen_probe, + .priv_auto_alloc_size = sizeof(struct mtk_clk_priv), + .ops = &mtk_clk_topckgen_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +U_BOOT_DRIVER(mtk_clk_topckgen_cg) = { + .name = "mt8516-topckgen-cg", + .id = UCLASS_CLK, + .of_match = mt8516_topckgen_cg_compat, + .probe = mt8516_topckgen_cg_probe, + .priv_auto_alloc_size = sizeof(struct mtk_cg_priv), + .ops = &mtk_clk_gate_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c index 870b14e..6c6b500 100644 --- a/drivers/clk/mediatek/clk-mtk.c +++ b/drivers/clk/mediatek/clk-mtk.c @@ -390,6 +390,12 @@ static int mtk_clk_gate_enable(struct clk *clk) case CLK_GATE_SETCLR: writel(bit, priv->base + gate->regs->clr_ofs); break; + case CLK_GATE_SETCLR_INV: + writel(bit, priv->base + gate->regs->set_ofs); + break; + case CLK_GATE_NO_SETCLR: + clrsetbits_le32(priv->base + gate->regs->sta_ofs, bit, 0); + break; case CLK_GATE_NO_SETCLR_INV: clrsetbits_le32(priv->base + gate->regs->sta_ofs, bit, bit); break; @@ -411,6 +417,12 @@ static int mtk_clk_gate_disable(struct clk *clk) case CLK_GATE_SETCLR: writel(bit, priv->base + gate->regs->set_ofs); break; + case CLK_GATE_SETCLR_INV: + writel(bit, priv->base + gate->regs->clr_ofs); + break; + case CLK_GATE_NO_SETCLR: + clrsetbits_le32(priv->base + gate->regs->sta_ofs, bit, bit); + break; case CLK_GATE_NO_SETCLR_INV: clrsetbits_le32(priv->base + gate->regs->sta_ofs, bit, 0); break; diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig index 5da5c4a..5643939 100644 --- a/drivers/led/Kconfig +++ b/drivers/led/Kconfig @@ -28,6 +28,13 @@ config LED_BCM6358 LED HW controller accessed via MMIO registers. HW has no blinking capabilities and up to 32 LEDs can be controlled. +config LED_BCM6858 + bool "LED Support for BCM6858" + depends on LED && (ARCH_BCM6858 || ARCH_BCM63158) + help + This option enables support for LEDs connected to the BCM6858 + HW has blinking capabilities and up to 32 LEDs can be controlled. + config LED_BLINK bool "Support LED blinking" depends on LED diff --git a/drivers/led/Makefile b/drivers/led/Makefile index 160a8f3..3654dd3 100644 --- a/drivers/led/Makefile +++ b/drivers/led/Makefile @@ -6,4 +6,5 @@ obj-y += led-uclass.o obj-$(CONFIG_LED_BCM6328) += led_bcm6328.o obj-$(CONFIG_LED_BCM6358) += led_bcm6358.o +obj-$(CONFIG_LED_BCM6858) += led_bcm6858.o obj-$(CONFIG_$(SPL_)LED_GPIO) += led_gpio.o diff --git a/drivers/led/led_bcm6858.c b/drivers/led/led_bcm6858.c new file mode 100644 index 0000000..27a76fc --- /dev/null +++ b/drivers/led/led_bcm6858.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Philippe Reynes <philippe.reynes@softathome.com> + * + * based on: + * drivers/led/led_bcm6328.c + * drivers/led/led_bcm6358.c + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <led.h> +#include <asm/io.h> +#include <dm/lists.h> + +#define LEDS_MAX 32 +#define LEDS_WAIT 100 + +/* LED Mode register */ +#define LED_MODE_REG 0x0 +#define LED_MODE_OFF 0 +#define LED_MODE_ON 1 +#define LED_MODE_MASK 1 + +/* LED Controller Global settings register */ +#define LED_CTRL_REG 0x00 +#define LED_CTRL_MASK 0x1f +#define LED_CTRL_LED_TEST_MODE BIT(0) +#define LED_CTRL_SERIAL_LED_DATA_PPOL BIT(1) +#define LED_CTRL_SERIAL_LED_CLK_POL BIT(2) +#define LED_CTRL_SERIAL_LED_EN_POL BIT(3) +#define LED_CTRL_SERIAL_LED_MSB_FIRST BIT(4) + +/* LED Controller IP LED source select register */ +#define LED_HW_LED_EN_REG 0x08 +/* LED Flash control register0 */ +#define LED_FLASH_RATE_CONTROL_REG0 0x10 +/* Soft LED input register */ +#define LED_SW_LED_IP_REG 0xb8 +/* Soft LED input polarity register */ +#define LED_SW_LED_IP_PPOL_REG 0xbc + +struct bcm6858_led_priv { + void __iomem *regs; + u8 pin; +}; + +#ifdef CONFIG_LED_BLINK +/* + * The value for flash rate are: + * 0 : no blinking + * 1 : rate is 25 Hz => 40 ms (period) + * 2 : rate is 12.5 Hz => 80 ms (period) + * 3 : rate is 6.25 Hz => 160 ms (period) + * 4 : rate is 3.125 Hz => 320 ms (period) + * 5 : rate is 1.5625 Hz => 640 ms (period) + * 6 : rate is 0.7815 Hz => 1280 ms (period) + * 7 : rate is 0.390625 Hz => 2560 ms (period) + */ +static const int bcm6858_flash_rate[8] = { + 0, 40, 80, 160, 320, 640, 1280, 2560 +}; + +static u32 bcm6858_flash_rate_value(int period_ms) +{ + unsigned long value = 7; + int i; + + for (i = 0; i < ARRAY_SIZE(bcm6858_flash_rate); i++) { + if (period_ms <= bcm6858_flash_rate[i]) { + value = i; + break; + } + } + + return value; +} + +static int bcm6858_led_set_period(struct udevice *dev, int period_ms) +{ + struct bcm6858_led_priv *priv = dev_get_priv(dev); + u32 offset, shift, mask, value; + + offset = (priv->pin / 8) * 4; + shift = (priv->pin % 8) * 4; + mask = 0x7 << shift; + value = bcm6858_flash_rate_value(period_ms) << shift; + + clrbits_32(priv->regs + LED_FLASH_RATE_CONTROL_REG0 + offset, mask); + setbits_32(priv->regs + LED_FLASH_RATE_CONTROL_REG0 + offset, value); + + return 0; +} +#endif + +static enum led_state_t bcm6858_led_get_state(struct udevice *dev) +{ + struct bcm6858_led_priv *priv = dev_get_priv(dev); + enum led_state_t state = LEDST_OFF; + u32 sw_led_ip; + + sw_led_ip = readl(priv->regs + LED_SW_LED_IP_REG); + if (sw_led_ip & (1 << priv->pin)) + state = LEDST_ON; + + return state; +} + +static int bcm6858_led_set_state(struct udevice *dev, enum led_state_t state) +{ + struct bcm6858_led_priv *priv = dev_get_priv(dev); + + switch (state) { + case LEDST_OFF: + clrbits_32(priv->regs + LED_SW_LED_IP_REG, (1 << priv->pin)); +#ifdef CONFIG_LED_BLINK + bcm6858_led_set_period(dev, 0); +#endif + break; + case LEDST_ON: + setbits_32(priv->regs + LED_SW_LED_IP_REG, (1 << priv->pin)); +#ifdef CONFIG_LED_BLINK + bcm6858_led_set_period(dev, 0); +#endif + break; + case LEDST_TOGGLE: + if (bcm6858_led_get_state(dev) == LEDST_OFF) + return bcm6858_led_set_state(dev, LEDST_ON); + else + return bcm6858_led_set_state(dev, LEDST_OFF); + break; +#ifdef CONFIG_LED_BLINK + case LEDST_BLINK: + setbits_32(priv->regs + LED_SW_LED_IP_REG, (1 << priv->pin)); + break; +#endif + default: + return -EINVAL; + } + + return 0; +} + +static const struct led_ops bcm6858_led_ops = { + .get_state = bcm6858_led_get_state, + .set_state = bcm6858_led_set_state, +#ifdef CONFIG_LED_BLINK + .set_period = bcm6858_led_set_period, +#endif +}; + +static int bcm6858_led_probe(struct udevice *dev) +{ + struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev); + + /* Top-level LED node */ + if (!uc_plat->label) { + void __iomem *regs; + u32 set_bits = 0; + + regs = dev_remap_addr(dev); + if (!regs) + return -EINVAL; + + if (dev_read_bool(dev, "brcm,serial-led-msb-first")) + set_bits |= LED_CTRL_SERIAL_LED_MSB_FIRST; + if (dev_read_bool(dev, "brcm,serial-led-en-pol")) + set_bits |= LED_CTRL_SERIAL_LED_EN_POL; + if (dev_read_bool(dev, "brcm,serial-led-clk-pol")) + set_bits |= LED_CTRL_SERIAL_LED_CLK_POL; + if (dev_read_bool(dev, "brcm,serial-led-data-ppol")) + set_bits |= LED_CTRL_SERIAL_LED_DATA_PPOL; + if (dev_read_bool(dev, "brcm,led-test-mode")) + set_bits |= LED_CTRL_LED_TEST_MODE; + + clrsetbits_32(regs + LED_CTRL_REG, ~0, set_bits); + } else { + struct bcm6858_led_priv *priv = dev_get_priv(dev); + void __iomem *regs; + unsigned int pin; + + regs = dev_remap_addr(dev_get_parent(dev)); + if (!regs) + return -EINVAL; + + pin = dev_read_u32_default(dev, "reg", LEDS_MAX); + if (pin >= LEDS_MAX) + return -EINVAL; + + priv->regs = regs; + priv->pin = pin; + + /* this led is managed by software */ + clrbits_32(regs + LED_HW_LED_EN_REG, 1 << pin); + + /* configure the polarity */ + if (dev_read_bool(dev, "active-low")) + clrbits_32(regs + LED_SW_LED_IP_PPOL_REG, 1 << pin); + else + setbits_32(regs + LED_SW_LED_IP_PPOL_REG, 1 << pin); + } + + return 0; +} + +static int bcm6858_led_bind(struct udevice *parent) +{ + ofnode node; + + dev_for_each_subnode(node, parent) { + struct led_uc_plat *uc_plat; + struct udevice *dev; + const char *label; + int ret; + + label = ofnode_read_string(node, "label"); + if (!label) { + debug("%s: node %s has no label\n", __func__, + ofnode_get_name(node)); + return -EINVAL; + } + + ret = device_bind_driver_to_node(parent, "bcm6858-led", + ofnode_get_name(node), + node, &dev); + if (ret) + return ret; + + uc_plat = dev_get_uclass_platdata(dev); + uc_plat->label = label; + } + + return 0; +} + +static const struct udevice_id bcm6858_led_ids[] = { + { .compatible = "brcm,bcm6858-leds" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(bcm6858_led) = { + .name = "bcm6858-led", + .id = UCLASS_LED, + .of_match = bcm6858_led_ids, + .bind = bcm6858_led_bind, + .probe = bcm6858_led_probe, + .priv_auto_alloc_size = sizeof(struct bcm6858_led_priv), + .ops = &bcm6858_led_ops, +}; diff --git a/drivers/misc/fs_loader.c b/drivers/misc/fs_loader.c index 57a14a3..f42eeff 100644 --- a/drivers/misc/fs_loader.c +++ b/drivers/misc/fs_loader.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2018 Intel Corporation <www.intel.com> + * Copyright (C) 2018-2019 Intel Corporation <www.intel.com> * */ #include <common.h> @@ -219,32 +219,26 @@ int request_firmware_into_buf(struct udevice *dev, static int fs_loader_ofdata_to_platdata(struct udevice *dev) { - const char *fs_loader_path; u32 phandlepart[2]; - fs_loader_path = ofnode_get_chosen_prop("firmware-loader"); + ofnode fs_loader_node = dev_ofnode(dev); - if (fs_loader_path) { - ofnode fs_loader_node; + if (ofnode_valid(fs_loader_node)) { + struct device_platdata *plat; - fs_loader_node = ofnode_path(fs_loader_path); - if (ofnode_valid(fs_loader_node)) { - struct device_platdata *plat; - plat = dev->platdata; - - if (!ofnode_read_u32_array(fs_loader_node, - "phandlepart", - phandlepart, 2)) { - plat->phandlepart.phandle = phandlepart[0]; - plat->phandlepart.partition = phandlepart[1]; - } + plat = dev->platdata; + if (!ofnode_read_u32_array(fs_loader_node, + "phandlepart", + phandlepart, 2)) { + plat->phandlepart.phandle = phandlepart[0]; + plat->phandlepart.partition = phandlepart[1]; + } - plat->mtdpart = (char *)ofnode_read_string( - fs_loader_node, "mtdpart"); + plat->mtdpart = (char *)ofnode_read_string( + fs_loader_node, "mtdpart"); - plat->ubivol = (char *)ofnode_read_string( - fs_loader_node, "ubivol"); - } + plat->ubivol = (char *)ofnode_read_string( + fs_loader_node, "ubivol"); } return 0; @@ -252,6 +246,29 @@ static int fs_loader_ofdata_to_platdata(struct udevice *dev) static int fs_loader_probe(struct udevice *dev) { +#if CONFIG_IS_ENABLED(DM) && CONFIG_IS_ENABLED(BLK) + int ret; + struct device_platdata *plat = dev->platdata; + + if (plat->phandlepart.phandle) { + ofnode node = ofnode_get_by_phandle(plat->phandlepart.phandle); + struct udevice *parent_dev = NULL; + + ret = device_get_global_by_ofnode(node, &parent_dev); + if (!ret) { + struct udevice *dev; + + ret = blk_get_from_parent(parent_dev, &dev); + if (ret) { + debug("fs_loader: No block device: %d\n", + ret); + + return ret; + } + } + } +#endif + return 0; }; diff --git a/drivers/mmc/mtk-sd.c b/drivers/mmc/mtk-sd.c index d3f0778..e0ac3e9 100644 --- a/drivers/mmc/mtk-sd.c +++ b/drivers/mmc/mtk-sd.c @@ -247,6 +247,7 @@ struct msdc_host { struct msdc_compatible *dev_comp; struct clk src_clk; /* for SD/MMC bus clock */ + struct clk src_clk_cg; /* optional, MSDC source clock control gate */ struct clk h_clk; /* MSDC core clock */ u32 src_clk_freq; /* source clock */ @@ -269,7 +270,7 @@ struct msdc_host { bool builtin_cd; /* card detection / write protection GPIOs */ -#if IS_ENABLED(DM_GPIO) +#if CONFIG_IS_ENABLED(DM_GPIO) struct gpio_desc gpio_wp; struct gpio_desc gpio_cd; #endif @@ -849,7 +850,7 @@ static int msdc_ops_get_cd(struct udevice *dev) return !(val & MSDC_PS_CDSTS); } -#if IS_ENABLED(DM_GPIO) +#if CONFIG_IS_ENABLED(DM_GPIO) if (!host->gpio_cd.dev) return 1; @@ -861,7 +862,7 @@ static int msdc_ops_get_cd(struct udevice *dev) static int msdc_ops_get_wp(struct udevice *dev) { -#if IS_ENABLED(DM_GPIO) +#if CONFIG_IS_ENABLED(DM_GPIO) struct msdc_host *host = dev_get_priv(dev); if (!host->gpio_wp.dev) @@ -1269,6 +1270,8 @@ static void msdc_ungate_clock(struct msdc_host *host) { clk_enable(&host->src_clk); clk_enable(&host->h_clk); + if (host->src_clk_cg.dev) + clk_enable(&host->src_clk_cg); } static int msdc_drv_probe(struct udevice *dev) @@ -1332,7 +1335,9 @@ static int msdc_ofdata_to_platdata(struct udevice *dev) if (ret < 0) return ret; -#if IS_ENABLED(DM_GPIO) + clk_get_by_name(dev, "source_cg", &host->src_clk_cg); /* optional */ + +#if CONFIG_IS_ENABLED(DM_GPIO) gpio_request_by_name(dev, "wp-gpios", 0, &host->gpio_wp, GPIOD_IS_IN); gpio_request_by_name(dev, "cd-gpios", 0, &host->gpio_cd, GPIOD_IS_IN); #endif @@ -1376,8 +1381,18 @@ static const struct msdc_compatible mt7623_compat = { .enhance_rx = false }; +static const struct msdc_compatible mt8516_compat = { + .clk_div_bits = 12, + .pad_tune0 = true, + .async_fifo = true, + .data_tune = true, + .busy_check = true, + .stop_clk_fix = true, +}; + static const struct udevice_id msdc_ids[] = { { .compatible = "mediatek,mt7623-mmc", .data = (ulong)&mt7623_compat }, + { .compatible = "mediatek,mt8516-mmc", .data = (ulong)&mt8516_compat }, {} }; diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index dc087ab..f86035b 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -60,6 +60,31 @@ config SPL_GENERATE_ATMEL_PMECC_HEADER endif +config NAND_BRCMNAND + bool "Support Broadcom NAND controller" + depends on OF_CONTROL && DM && MTD + help + Enable the driver for NAND flash on platforms using a Broadcom NAND + controller. + +config NAND_BRCMNAND_6838 + bool "Support Broadcom NAND controller on bcm6838" + depends on NAND_BRCMNAND && ARCH_BMIPS && SOC_BMIPS_BCM6838 + help + Enable support for broadcom nand driver on bcm6838. + +config NAND_BRCMNAND_6858 + bool "Support Broadcom NAND controller on bcm6858" + depends on NAND_BRCMNAND && ARCH_BCM6858 + help + Enable support for broadcom nand driver on bcm6858. + +config NAND_BRCMNAND_63158 + bool "Support Broadcom NAND controller on bcm63158" + depends on NAND_BRCMNAND && ARCH_BCM63158 + help + Enable support for broadcom nand driver on bcm63158. + config NAND_DAVINCI bool "Support TI Davinci NAND controller" help diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index b10e718..9337f64 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_NAND_ECC_BCH) += nand_bch.o obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o obj-$(CONFIG_NAND_ARASAN) += arasan_nfc.o +obj-$(CONFIG_NAND_BRCMNAND) += brcmnand/ obj-$(CONFIG_NAND_DAVINCI) += davinci_nand.o obj-$(CONFIG_NAND_DENALI) += denali.o obj-$(CONFIG_NAND_DENALI_DT) += denali_dt.o diff --git a/drivers/mtd/nand/raw/brcmnand/Makefile b/drivers/mtd/nand/raw/brcmnand/Makefile new file mode 100644 index 0000000..a2363cc --- /dev/null +++ b/drivers/mtd/nand/raw/brcmnand/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_NAND_BRCMNAND_63158) += bcm63158_nand.o +obj-$(CONFIG_NAND_BRCMNAND_6838) += bcm6838_nand.o +obj-$(CONFIG_NAND_BRCMNAND_6858) += bcm6858_nand.o +obj-$(CONFIG_NAND_BRCMNAND) += brcmnand.o +obj-$(CONFIG_NAND_BRCMNAND) += brcmnand_compat.o diff --git a/drivers/mtd/nand/raw/brcmnand/bcm63158_nand.c b/drivers/mtd/nand/raw/brcmnand/bcm63158_nand.c new file mode 100644 index 0000000..16b0d44 --- /dev/null +++ b/drivers/mtd/nand/raw/brcmnand/bcm63158_nand.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <common.h> +#include <asm/io.h> +#include <memalign.h> +#include <nand.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <dm.h> + +#include "brcmnand.h" + +struct bcm63158_nand_soc { + struct brcmnand_soc soc; + void __iomem *base; +}; + +#define BCM63158_NAND_INT 0x00 +#define BCM63158_NAND_STATUS_SHIFT 0 +#define BCM63158_NAND_STATUS_MASK (0xfff << BCM63158_NAND_STATUS_SHIFT) + +#define BCM63158_NAND_INT_EN 0x04 +#define BCM63158_NAND_ENABLE_SHIFT 0 +#define BCM63158_NAND_ENABLE_MASK (0xffff << BCM63158_NAND_ENABLE_SHIFT) + +enum { + BCM63158_NP_READ = BIT(0), + BCM63158_BLOCK_ERASE = BIT(1), + BCM63158_COPY_BACK = BIT(2), + BCM63158_PAGE_PGM = BIT(3), + BCM63158_CTRL_READY = BIT(4), + BCM63158_DEV_RBPIN = BIT(5), + BCM63158_ECC_ERR_UNC = BIT(6), + BCM63158_ECC_ERR_CORR = BIT(7), +}; + +static bool bcm63158_nand_intc_ack(struct brcmnand_soc *soc) +{ + struct bcm63158_nand_soc *priv = + container_of(soc, struct bcm63158_nand_soc, soc); + void __iomem *mmio = priv->base + BCM63158_NAND_INT; + u32 val = brcmnand_readl(mmio); + + if (val & (BCM63158_CTRL_READY << BCM63158_NAND_STATUS_SHIFT)) { + /* Ack interrupt */ + val &= ~BCM63158_NAND_STATUS_MASK; + val |= BCM63158_CTRL_READY << BCM63158_NAND_STATUS_SHIFT; + brcmnand_writel(val, mmio); + return true; + } + + return false; +} + +static void bcm63158_nand_intc_set(struct brcmnand_soc *soc, bool en) +{ + struct bcm63158_nand_soc *priv = + container_of(soc, struct bcm63158_nand_soc, soc); + void __iomem *mmio = priv->base + BCM63158_NAND_INT_EN; + u32 val = brcmnand_readl(mmio); + + /* Don't ack any interrupts */ + val &= ~BCM63158_NAND_STATUS_MASK; + + if (en) + val |= BCM63158_CTRL_READY << BCM63158_NAND_ENABLE_SHIFT; + else + val &= ~(BCM63158_CTRL_READY << BCM63158_NAND_ENABLE_SHIFT); + + brcmnand_writel(val, mmio); +} + +static int bcm63158_nand_probe(struct udevice *dev) +{ + struct udevice *pdev = dev; + struct bcm63158_nand_soc *priv = dev_get_priv(dev); + struct brcmnand_soc *soc; + struct resource res; + + soc = &priv->soc; + + dev_read_resource_byname(pdev, "nand-int-base", &res); + priv->base = devm_ioremap(dev, res.start, resource_size(&res)); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + soc->ctlrdy_ack = bcm63158_nand_intc_ack; + soc->ctlrdy_set_enabled = bcm63158_nand_intc_set; + + /* Disable and ack all interrupts */ + brcmnand_writel(0, priv->base + BCM63158_NAND_INT_EN); + brcmnand_writel(0, priv->base + BCM63158_NAND_INT); + + return brcmnand_probe(pdev, soc); +} + +static const struct udevice_id bcm63158_nand_dt_ids[] = { + { + .compatible = "brcm,nand-bcm63158", + }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(bcm63158_nand) = { + .name = "bcm63158-nand", + .id = UCLASS_MTD, + .of_match = bcm63158_nand_dt_ids, + .probe = bcm63158_nand_probe, + .priv_auto_alloc_size = sizeof(struct bcm63158_nand_soc), +}; + +void board_nand_init(void) +{ + struct udevice *dev; + int ret; + + ret = uclass_get_device_by_driver(UCLASS_MTD, + DM_GET_DRIVER(bcm63158_nand), &dev); + if (ret && ret != -ENODEV) + pr_err("Failed to initialize %s. (error %d)\n", dev->name, + ret); +} diff --git a/drivers/mtd/nand/raw/brcmnand/bcm6838_nand.c b/drivers/mtd/nand/raw/brcmnand/bcm6838_nand.c new file mode 100644 index 0000000..ece9444 --- /dev/null +++ b/drivers/mtd/nand/raw/brcmnand/bcm6838_nand.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <common.h> +#include <asm/io.h> +#include <memalign.h> +#include <nand.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <dm.h> + +#include "brcmnand.h" + +struct bcm6838_nand_soc { + struct brcmnand_soc soc; + void __iomem *base; +}; + +#define BCM6838_NAND_INT 0x00 +#define BCM6838_NAND_STATUS_SHIFT 0 +#define BCM6838_NAND_STATUS_MASK (0xfff << BCM6838_NAND_STATUS_SHIFT) +#define BCM6838_NAND_ENABLE_SHIFT 16 +#define BCM6838_NAND_ENABLE_MASK (0xffff << BCM6838_NAND_ENABLE_SHIFT) + +enum { + BCM6838_NP_READ = BIT(0), + BCM6838_BLOCK_ERASE = BIT(1), + BCM6838_COPY_BACK = BIT(2), + BCM6838_PAGE_PGM = BIT(3), + BCM6838_CTRL_READY = BIT(4), + BCM6838_DEV_RBPIN = BIT(5), + BCM6838_ECC_ERR_UNC = BIT(6), + BCM6838_ECC_ERR_CORR = BIT(7), +}; + +static bool bcm6838_nand_intc_ack(struct brcmnand_soc *soc) +{ + struct bcm6838_nand_soc *priv = + container_of(soc, struct bcm6838_nand_soc, soc); + void __iomem *mmio = priv->base + BCM6838_NAND_INT; + u32 val = brcmnand_readl(mmio); + + if (val & (BCM6838_CTRL_READY << BCM6838_NAND_STATUS_SHIFT)) { + /* Ack interrupt */ + val &= ~BCM6838_NAND_STATUS_MASK; + val |= BCM6838_CTRL_READY << BCM6838_NAND_STATUS_SHIFT; + brcmnand_writel(val, mmio); + return true; + } + + return false; +} + +static void bcm6838_nand_intc_set(struct brcmnand_soc *soc, bool en) +{ + struct bcm6838_nand_soc *priv = + container_of(soc, struct bcm6838_nand_soc, soc); + void __iomem *mmio = priv->base + BCM6838_NAND_INT; + u32 val = brcmnand_readl(mmio); + + /* Don't ack any interrupts */ + val &= ~BCM6838_NAND_STATUS_MASK; + + if (en) + val |= BCM6838_CTRL_READY << BCM6838_NAND_ENABLE_SHIFT; + else + val &= ~(BCM6838_CTRL_READY << BCM6838_NAND_ENABLE_SHIFT); + + brcmnand_writel(val, mmio); +} + +static int bcm6838_nand_probe(struct udevice *dev) +{ + struct udevice *pdev = dev; + struct bcm6838_nand_soc *priv = dev_get_priv(dev); + struct brcmnand_soc *soc; + struct resource res; + + soc = &priv->soc; + + dev_read_resource_byname(pdev, "nand-int-base", &res); + priv->base = ioremap(res.start, resource_size(&res)); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + soc->ctlrdy_ack = bcm6838_nand_intc_ack; + soc->ctlrdy_set_enabled = bcm6838_nand_intc_set; + + /* Disable and ack all interrupts */ + brcmnand_writel(0, priv->base + BCM6838_NAND_INT); + brcmnand_writel(BCM6838_NAND_STATUS_MASK, + priv->base + BCM6838_NAND_INT); + + return brcmnand_probe(pdev, soc); +} + +static const struct udevice_id bcm6838_nand_dt_ids[] = { + { + .compatible = "brcm,nand-bcm6838", + }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(bcm6838_nand) = { + .name = "bcm6838-nand", + .id = UCLASS_MTD, + .of_match = bcm6838_nand_dt_ids, + .probe = bcm6838_nand_probe, + .priv_auto_alloc_size = sizeof(struct bcm6838_nand_soc), +}; + +void board_nand_init(void) +{ + struct udevice *dev; + int ret; + + ret = uclass_get_device_by_driver(UCLASS_MTD, + DM_GET_DRIVER(bcm6838_nand), &dev); + if (ret && ret != -ENODEV) + pr_err("Failed to initialize %s. (error %d)\n", dev->name, + ret); +} diff --git a/drivers/mtd/nand/raw/brcmnand/bcm6858_nand.c b/drivers/mtd/nand/raw/brcmnand/bcm6858_nand.c new file mode 100644 index 0000000..3586baa --- /dev/null +++ b/drivers/mtd/nand/raw/brcmnand/bcm6858_nand.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <common.h> +#include <asm/io.h> +#include <memalign.h> +#include <nand.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <dm.h> + +#include "brcmnand.h" + +struct bcm6858_nand_soc { + struct brcmnand_soc soc; + void __iomem *base; +}; + +#define BCM6858_NAND_INT 0x00 +#define BCM6858_NAND_STATUS_SHIFT 0 +#define BCM6858_NAND_STATUS_MASK (0xfff << BCM6858_NAND_STATUS_SHIFT) + +#define BCM6858_NAND_INT_EN 0x04 +#define BCM6858_NAND_ENABLE_SHIFT 0 +#define BCM6858_NAND_ENABLE_MASK (0xffff << BCM6858_NAND_ENABLE_SHIFT) + +enum { + BCM6858_NP_READ = BIT(0), + BCM6858_BLOCK_ERASE = BIT(1), + BCM6858_COPY_BACK = BIT(2), + BCM6858_PAGE_PGM = BIT(3), + BCM6858_CTRL_READY = BIT(4), + BCM6858_DEV_RBPIN = BIT(5), + BCM6858_ECC_ERR_UNC = BIT(6), + BCM6858_ECC_ERR_CORR = BIT(7), +}; + +static bool bcm6858_nand_intc_ack(struct brcmnand_soc *soc) +{ + struct bcm6858_nand_soc *priv = + container_of(soc, struct bcm6858_nand_soc, soc); + void __iomem *mmio = priv->base + BCM6858_NAND_INT; + u32 val = brcmnand_readl(mmio); + + if (val & (BCM6858_CTRL_READY << BCM6858_NAND_STATUS_SHIFT)) { + /* Ack interrupt */ + val &= ~BCM6858_NAND_STATUS_MASK; + val |= BCM6858_CTRL_READY << BCM6858_NAND_STATUS_SHIFT; + brcmnand_writel(val, mmio); + return true; + } + + return false; +} + +static void bcm6858_nand_intc_set(struct brcmnand_soc *soc, bool en) +{ + struct bcm6858_nand_soc *priv = + container_of(soc, struct bcm6858_nand_soc, soc); + void __iomem *mmio = priv->base + BCM6858_NAND_INT_EN; + u32 val = brcmnand_readl(mmio); + + /* Don't ack any interrupts */ + val &= ~BCM6858_NAND_STATUS_MASK; + + if (en) + val |= BCM6858_CTRL_READY << BCM6858_NAND_ENABLE_SHIFT; + else + val &= ~(BCM6858_CTRL_READY << BCM6858_NAND_ENABLE_SHIFT); + + brcmnand_writel(val, mmio); +} + +static int bcm6858_nand_probe(struct udevice *dev) +{ + struct udevice *pdev = dev; + struct bcm6858_nand_soc *priv = dev_get_priv(dev); + struct brcmnand_soc *soc; + struct resource res; + + soc = &priv->soc; + + dev_read_resource_byname(pdev, "nand-int-base", &res); + priv->base = devm_ioremap(dev, res.start, resource_size(&res)); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + soc->ctlrdy_ack = bcm6858_nand_intc_ack; + soc->ctlrdy_set_enabled = bcm6858_nand_intc_set; + + /* Disable and ack all interrupts */ + brcmnand_writel(0, priv->base + BCM6858_NAND_INT_EN); + brcmnand_writel(0, priv->base + BCM6858_NAND_INT); + + return brcmnand_probe(pdev, soc); +} + +static const struct udevice_id bcm6858_nand_dt_ids[] = { + { + .compatible = "brcm,nand-bcm6858", + }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(bcm6858_nand) = { + .name = "bcm6858-nand", + .id = UCLASS_MTD, + .of_match = bcm6858_nand_dt_ids, + .probe = bcm6858_nand_probe, + .priv_auto_alloc_size = sizeof(struct bcm6858_nand_soc), +}; + +void board_nand_init(void) +{ + struct udevice *dev; + int ret; + + ret = uclass_get_device_by_driver(UCLASS_MTD, + DM_GET_DRIVER(bcm6858_nand), &dev); + if (ret && ret != -ENODEV) + pr_err("Failed to initialize %s. (error %d)\n", dev->name, + ret); +} diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c new file mode 100644 index 0000000..faa6da4 --- /dev/null +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -0,0 +1,2805 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright © 2010-2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <common.h> +#include <asm/io.h> +#include <memalign.h> +#include <nand.h> +#include <clk.h> +#include <linux/ioport.h> +#include <linux/completion.h> +#include <linux/errno.h> +#include <linux/log2.h> +#include <asm/processor.h> +#include <dm.h> + +#include "brcmnand.h" +#include "brcmnand_compat.h" + +/* + * This flag controls if WP stays on between erase/write commands to mitigate + * flash corruption due to power glitches. Values: + * 0: NAND_WP is not used or not available + * 1: NAND_WP is set by default, cleared for erase/write operations + * 2: NAND_WP is always cleared + */ +static int wp_on = 1; +module_param(wp_on, int, 0444); + +/*********************************************************************** + * Definitions + ***********************************************************************/ + +#define DRV_NAME "brcmnand" + +#define CMD_NULL 0x00 +#define CMD_PAGE_READ 0x01 +#define CMD_SPARE_AREA_READ 0x02 +#define CMD_STATUS_READ 0x03 +#define CMD_PROGRAM_PAGE 0x04 +#define CMD_PROGRAM_SPARE_AREA 0x05 +#define CMD_COPY_BACK 0x06 +#define CMD_DEVICE_ID_READ 0x07 +#define CMD_BLOCK_ERASE 0x08 +#define CMD_FLASH_RESET 0x09 +#define CMD_BLOCKS_LOCK 0x0a +#define CMD_BLOCKS_LOCK_DOWN 0x0b +#define CMD_BLOCKS_UNLOCK 0x0c +#define CMD_READ_BLOCKS_LOCK_STATUS 0x0d +#define CMD_PARAMETER_READ 0x0e +#define CMD_PARAMETER_CHANGE_COL 0x0f +#define CMD_LOW_LEVEL_OP 0x10 + +struct brcm_nand_dma_desc { + u32 next_desc; + u32 next_desc_ext; + u32 cmd_irq; + u32 dram_addr; + u32 dram_addr_ext; + u32 tfr_len; + u32 total_len; + u32 flash_addr; + u32 flash_addr_ext; + u32 cs; + u32 pad2[5]; + u32 status_valid; +} __packed; + +/* Bitfields for brcm_nand_dma_desc::status_valid */ +#define FLASH_DMA_ECC_ERROR (1 << 8) +#define FLASH_DMA_CORR_ERROR (1 << 9) + +/* 512B flash cache in the NAND controller HW */ +#define FC_SHIFT 9U +#define FC_BYTES 512U +#define FC_WORDS (FC_BYTES >> 2) + +#define BRCMNAND_MIN_PAGESIZE 512 +#define BRCMNAND_MIN_BLOCKSIZE (8 * 1024) +#define BRCMNAND_MIN_DEVSIZE (4ULL * 1024 * 1024) + +#define NAND_CTRL_RDY (INTFC_CTLR_READY | INTFC_FLASH_READY) +#define NAND_POLL_STATUS_TIMEOUT_MS 100 + +/* Controller feature flags */ +enum { + BRCMNAND_HAS_1K_SECTORS = BIT(0), + BRCMNAND_HAS_PREFETCH = BIT(1), + BRCMNAND_HAS_CACHE_MODE = BIT(2), + BRCMNAND_HAS_WP = BIT(3), +}; + +struct brcmnand_controller { +#ifndef __UBOOT__ + struct device *dev; +#else + struct udevice *dev; +#endif /* __UBOOT__ */ + struct nand_hw_control controller; + void __iomem *nand_base; + void __iomem *nand_fc; /* flash cache */ + void __iomem *flash_dma_base; + unsigned int irq; + unsigned int dma_irq; + int nand_version; + int parameter_page_big_endian; + + /* Some SoCs provide custom interrupt status register(s) */ + struct brcmnand_soc *soc; + + /* Some SoCs have a gateable clock for the controller */ + struct clk *clk; + + int cmd_pending; + bool dma_pending; + struct completion done; + struct completion dma_done; + + /* List of NAND hosts (one for each chip-select) */ + struct list_head host_list; + + struct brcm_nand_dma_desc *dma_desc; + dma_addr_t dma_pa; + + /* in-memory cache of the FLASH_CACHE, used only for some commands */ + u8 flash_cache[FC_BYTES]; + + /* Controller revision details */ + const u16 *reg_offsets; + unsigned int reg_spacing; /* between CS1, CS2, ... regs */ + const u8 *cs_offsets; /* within each chip-select */ + const u8 *cs0_offsets; /* within CS0, if different */ + unsigned int max_block_size; + const unsigned int *block_sizes; + unsigned int max_page_size; + const unsigned int *page_sizes; + unsigned int max_oob; + u32 features; + + /* for low-power standby/resume only */ + u32 nand_cs_nand_select; + u32 nand_cs_nand_xor; + u32 corr_stat_threshold; + u32 flash_dma_mode; +}; + +struct brcmnand_cfg { + u64 device_size; + unsigned int block_size; + unsigned int page_size; + unsigned int spare_area_size; + unsigned int device_width; + unsigned int col_adr_bytes; + unsigned int blk_adr_bytes; + unsigned int ful_adr_bytes; + unsigned int sector_size_1k; + unsigned int ecc_level; + /* use for low-power standby/resume only */ + u32 acc_control; + u32 config; + u32 config_ext; + u32 timing_1; + u32 timing_2; +}; + +struct brcmnand_host { + struct list_head node; + + struct nand_chip chip; +#ifndef __UBOOT__ + struct platform_device *pdev; +#else + struct udevice *pdev; +#endif /* __UBOOT__ */ + int cs; + + unsigned int last_cmd; + unsigned int last_byte; + u64 last_addr; + struct brcmnand_cfg hwcfg; + struct brcmnand_controller *ctrl; +}; + +enum brcmnand_reg { + BRCMNAND_CMD_START = 0, + BRCMNAND_CMD_EXT_ADDRESS, + BRCMNAND_CMD_ADDRESS, + BRCMNAND_INTFC_STATUS, + BRCMNAND_CS_SELECT, + BRCMNAND_CS_XOR, + BRCMNAND_LL_OP, + BRCMNAND_CS0_BASE, + BRCMNAND_CS1_BASE, /* CS1 regs, if non-contiguous */ + BRCMNAND_CORR_THRESHOLD, + BRCMNAND_CORR_THRESHOLD_EXT, + BRCMNAND_UNCORR_COUNT, + BRCMNAND_CORR_COUNT, + BRCMNAND_CORR_EXT_ADDR, + BRCMNAND_CORR_ADDR, + BRCMNAND_UNCORR_EXT_ADDR, + BRCMNAND_UNCORR_ADDR, + BRCMNAND_SEMAPHORE, + BRCMNAND_ID, + BRCMNAND_ID_EXT, + BRCMNAND_LL_RDATA, + BRCMNAND_OOB_READ_BASE, + BRCMNAND_OOB_READ_10_BASE, /* offset 0x10, if non-contiguous */ + BRCMNAND_OOB_WRITE_BASE, + BRCMNAND_OOB_WRITE_10_BASE, /* offset 0x10, if non-contiguous */ + BRCMNAND_FC_BASE, +}; + +/* BRCMNAND v4.0 */ +static const u16 brcmnand_regs_v40[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x6c, + [BRCMNAND_CS_SELECT] = 0x14, + [BRCMNAND_CS_XOR] = 0x18, + [BRCMNAND_LL_OP] = 0x178, + [BRCMNAND_CS0_BASE] = 0x40, + [BRCMNAND_CS1_BASE] = 0xd0, + [BRCMNAND_CORR_THRESHOLD] = 0x84, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0, + [BRCMNAND_UNCORR_COUNT] = 0, + [BRCMNAND_CORR_COUNT] = 0, + [BRCMNAND_CORR_EXT_ADDR] = 0x70, + [BRCMNAND_CORR_ADDR] = 0x74, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x78, + [BRCMNAND_UNCORR_ADDR] = 0x7c, + [BRCMNAND_SEMAPHORE] = 0x58, + [BRCMNAND_ID] = 0x60, + [BRCMNAND_ID_EXT] = 0x64, + [BRCMNAND_LL_RDATA] = 0x17c, + [BRCMNAND_OOB_READ_BASE] = 0x20, + [BRCMNAND_OOB_READ_10_BASE] = 0x130, + [BRCMNAND_OOB_WRITE_BASE] = 0x30, + [BRCMNAND_OOB_WRITE_10_BASE] = 0, + [BRCMNAND_FC_BASE] = 0x200, +}; + +/* BRCMNAND v5.0 */ +static const u16 brcmnand_regs_v50[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x6c, + [BRCMNAND_CS_SELECT] = 0x14, + [BRCMNAND_CS_XOR] = 0x18, + [BRCMNAND_LL_OP] = 0x178, + [BRCMNAND_CS0_BASE] = 0x40, + [BRCMNAND_CS1_BASE] = 0xd0, + [BRCMNAND_CORR_THRESHOLD] = 0x84, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0, + [BRCMNAND_UNCORR_COUNT] = 0, + [BRCMNAND_CORR_COUNT] = 0, + [BRCMNAND_CORR_EXT_ADDR] = 0x70, + [BRCMNAND_CORR_ADDR] = 0x74, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x78, + [BRCMNAND_UNCORR_ADDR] = 0x7c, + [BRCMNAND_SEMAPHORE] = 0x58, + [BRCMNAND_ID] = 0x60, + [BRCMNAND_ID_EXT] = 0x64, + [BRCMNAND_LL_RDATA] = 0x17c, + [BRCMNAND_OOB_READ_BASE] = 0x20, + [BRCMNAND_OOB_READ_10_BASE] = 0x130, + [BRCMNAND_OOB_WRITE_BASE] = 0x30, + [BRCMNAND_OOB_WRITE_10_BASE] = 0x140, + [BRCMNAND_FC_BASE] = 0x200, +}; + +/* BRCMNAND v6.0 - v7.1 */ +static const u16 brcmnand_regs_v60[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x14, + [BRCMNAND_CS_SELECT] = 0x18, + [BRCMNAND_CS_XOR] = 0x1c, + [BRCMNAND_LL_OP] = 0x20, + [BRCMNAND_CS0_BASE] = 0x50, + [BRCMNAND_CS1_BASE] = 0, + [BRCMNAND_CORR_THRESHOLD] = 0xc0, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0xc4, + [BRCMNAND_UNCORR_COUNT] = 0xfc, + [BRCMNAND_CORR_COUNT] = 0x100, + [BRCMNAND_CORR_EXT_ADDR] = 0x10c, + [BRCMNAND_CORR_ADDR] = 0x110, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, + [BRCMNAND_UNCORR_ADDR] = 0x118, + [BRCMNAND_SEMAPHORE] = 0x150, + [BRCMNAND_ID] = 0x194, + [BRCMNAND_ID_EXT] = 0x198, + [BRCMNAND_LL_RDATA] = 0x19c, + [BRCMNAND_OOB_READ_BASE] = 0x200, + [BRCMNAND_OOB_READ_10_BASE] = 0, + [BRCMNAND_OOB_WRITE_BASE] = 0x280, + [BRCMNAND_OOB_WRITE_10_BASE] = 0, + [BRCMNAND_FC_BASE] = 0x400, +}; + +/* BRCMNAND v7.1 */ +static const u16 brcmnand_regs_v71[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x14, + [BRCMNAND_CS_SELECT] = 0x18, + [BRCMNAND_CS_XOR] = 0x1c, + [BRCMNAND_LL_OP] = 0x20, + [BRCMNAND_CS0_BASE] = 0x50, + [BRCMNAND_CS1_BASE] = 0, + [BRCMNAND_CORR_THRESHOLD] = 0xdc, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0, + [BRCMNAND_UNCORR_COUNT] = 0xfc, + [BRCMNAND_CORR_COUNT] = 0x100, + [BRCMNAND_CORR_EXT_ADDR] = 0x10c, + [BRCMNAND_CORR_ADDR] = 0x110, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, + [BRCMNAND_UNCORR_ADDR] = 0x118, + [BRCMNAND_SEMAPHORE] = 0x150, + [BRCMNAND_ID] = 0x194, + [BRCMNAND_ID_EXT] = 0x198, + [BRCMNAND_LL_RDATA] = 0x19c, + [BRCMNAND_OOB_READ_BASE] = 0x200, + [BRCMNAND_OOB_READ_10_BASE] = 0, + [BRCMNAND_OOB_WRITE_BASE] = 0x280, + [BRCMNAND_OOB_WRITE_10_BASE] = 0, + [BRCMNAND_FC_BASE] = 0x400, +}; + +/* BRCMNAND v7.2 */ +static const u16 brcmnand_regs_v72[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x14, + [BRCMNAND_CS_SELECT] = 0x18, + [BRCMNAND_CS_XOR] = 0x1c, + [BRCMNAND_LL_OP] = 0x20, + [BRCMNAND_CS0_BASE] = 0x50, + [BRCMNAND_CS1_BASE] = 0, + [BRCMNAND_CORR_THRESHOLD] = 0xdc, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0, + [BRCMNAND_UNCORR_COUNT] = 0xfc, + [BRCMNAND_CORR_COUNT] = 0x100, + [BRCMNAND_CORR_EXT_ADDR] = 0x10c, + [BRCMNAND_CORR_ADDR] = 0x110, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, + [BRCMNAND_UNCORR_ADDR] = 0x118, + [BRCMNAND_SEMAPHORE] = 0x150, + [BRCMNAND_ID] = 0x194, + [BRCMNAND_ID_EXT] = 0x198, + [BRCMNAND_LL_RDATA] = 0x19c, + [BRCMNAND_OOB_READ_BASE] = 0x200, + [BRCMNAND_OOB_READ_10_BASE] = 0, + [BRCMNAND_OOB_WRITE_BASE] = 0x400, + [BRCMNAND_OOB_WRITE_10_BASE] = 0, + [BRCMNAND_FC_BASE] = 0x600, +}; + +enum brcmnand_cs_reg { + BRCMNAND_CS_CFG_EXT = 0, + BRCMNAND_CS_CFG, + BRCMNAND_CS_ACC_CONTROL, + BRCMNAND_CS_TIMING1, + BRCMNAND_CS_TIMING2, +}; + +/* Per chip-select offsets for v7.1 */ +static const u8 brcmnand_cs_offsets_v71[] = { + [BRCMNAND_CS_ACC_CONTROL] = 0x00, + [BRCMNAND_CS_CFG_EXT] = 0x04, + [BRCMNAND_CS_CFG] = 0x08, + [BRCMNAND_CS_TIMING1] = 0x0c, + [BRCMNAND_CS_TIMING2] = 0x10, +}; + +/* Per chip-select offsets for pre v7.1, except CS0 on <= v5.0 */ +static const u8 brcmnand_cs_offsets[] = { + [BRCMNAND_CS_ACC_CONTROL] = 0x00, + [BRCMNAND_CS_CFG_EXT] = 0x04, + [BRCMNAND_CS_CFG] = 0x04, + [BRCMNAND_CS_TIMING1] = 0x08, + [BRCMNAND_CS_TIMING2] = 0x0c, +}; + +/* Per chip-select offset for <= v5.0 on CS0 only */ +static const u8 brcmnand_cs_offsets_cs0[] = { + [BRCMNAND_CS_ACC_CONTROL] = 0x00, + [BRCMNAND_CS_CFG_EXT] = 0x08, + [BRCMNAND_CS_CFG] = 0x08, + [BRCMNAND_CS_TIMING1] = 0x10, + [BRCMNAND_CS_TIMING2] = 0x14, +}; + +/* + * Bitfields for the CFG and CFG_EXT registers. Pre-v7.1 controllers only had + * one config register, but once the bitfields overflowed, newer controllers + * (v7.1 and newer) added a CFG_EXT register and shuffled a few fields around. + */ +enum { + CFG_BLK_ADR_BYTES_SHIFT = 8, + CFG_COL_ADR_BYTES_SHIFT = 12, + CFG_FUL_ADR_BYTES_SHIFT = 16, + CFG_BUS_WIDTH_SHIFT = 23, + CFG_BUS_WIDTH = BIT(CFG_BUS_WIDTH_SHIFT), + CFG_DEVICE_SIZE_SHIFT = 24, + + /* Only for pre-v7.1 (with no CFG_EXT register) */ + CFG_PAGE_SIZE_SHIFT = 20, + CFG_BLK_SIZE_SHIFT = 28, + + /* Only for v7.1+ (with CFG_EXT register) */ + CFG_EXT_PAGE_SIZE_SHIFT = 0, + CFG_EXT_BLK_SIZE_SHIFT = 4, +}; + +/* BRCMNAND_INTFC_STATUS */ +enum { + INTFC_FLASH_STATUS = GENMASK(7, 0), + + INTFC_ERASED = BIT(27), + INTFC_OOB_VALID = BIT(28), + INTFC_CACHE_VALID = BIT(29), + INTFC_FLASH_READY = BIT(30), + INTFC_CTLR_READY = BIT(31), +}; + +static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs) +{ + return brcmnand_readl(ctrl->nand_base + offs); +} + +static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs, + u32 val) +{ + brcmnand_writel(val, ctrl->nand_base + offs); +} + +static int brcmnand_revision_init(struct brcmnand_controller *ctrl) +{ + static const unsigned int block_sizes_v6[] = { 8, 16, 128, 256, 512, 1024, 2048, 0 }; + static const unsigned int block_sizes_v4[] = { 16, 128, 8, 512, 256, 1024, 2048, 0 }; + static const unsigned int page_sizes[] = { 512, 2048, 4096, 8192, 0 }; + + ctrl->nand_version = nand_readreg(ctrl, 0) & 0xffff; + + /* Only support v4.0+? */ + if (ctrl->nand_version < 0x0400) { + dev_err(ctrl->dev, "version %#x not supported\n", + ctrl->nand_version); + return -ENODEV; + } + + /* Register offsets */ + if (ctrl->nand_version >= 0x0702) + ctrl->reg_offsets = brcmnand_regs_v72; + else if (ctrl->nand_version >= 0x0701) + ctrl->reg_offsets = brcmnand_regs_v71; + else if (ctrl->nand_version >= 0x0600) + ctrl->reg_offsets = brcmnand_regs_v60; + else if (ctrl->nand_version >= 0x0500) + ctrl->reg_offsets = brcmnand_regs_v50; + else if (ctrl->nand_version >= 0x0400) + ctrl->reg_offsets = brcmnand_regs_v40; + + /* Chip-select stride */ + if (ctrl->nand_version >= 0x0701) + ctrl->reg_spacing = 0x14; + else + ctrl->reg_spacing = 0x10; + + /* Per chip-select registers */ + if (ctrl->nand_version >= 0x0701) { + ctrl->cs_offsets = brcmnand_cs_offsets_v71; + } else { + ctrl->cs_offsets = brcmnand_cs_offsets; + + /* v5.0 and earlier has a different CS0 offset layout */ + if (ctrl->nand_version <= 0x0500) + ctrl->cs0_offsets = brcmnand_cs_offsets_cs0; + } + + /* Page / block sizes */ + if (ctrl->nand_version >= 0x0701) { + /* >= v7.1 use nice power-of-2 values! */ + ctrl->max_page_size = 16 * 1024; + ctrl->max_block_size = 2 * 1024 * 1024; + } else { + ctrl->page_sizes = page_sizes; + if (ctrl->nand_version >= 0x0600) + ctrl->block_sizes = block_sizes_v6; + else + ctrl->block_sizes = block_sizes_v4; + + if (ctrl->nand_version < 0x0400) { + ctrl->max_page_size = 4096; + ctrl->max_block_size = 512 * 1024; + } + } + + /* Maximum spare area sector size (per 512B) */ + if (ctrl->nand_version >= 0x0702) + ctrl->max_oob = 128; + else if (ctrl->nand_version >= 0x0600) + ctrl->max_oob = 64; + else if (ctrl->nand_version >= 0x0500) + ctrl->max_oob = 32; + else + ctrl->max_oob = 16; + + /* v6.0 and newer (except v6.1) have prefetch support */ + if (ctrl->nand_version >= 0x0600 && ctrl->nand_version != 0x0601) + ctrl->features |= BRCMNAND_HAS_PREFETCH; + + /* + * v6.x has cache mode, but it's implemented differently. Ignore it for + * now. + */ + if (ctrl->nand_version >= 0x0700) + ctrl->features |= BRCMNAND_HAS_CACHE_MODE; + + if (ctrl->nand_version >= 0x0500) + ctrl->features |= BRCMNAND_HAS_1K_SECTORS; + + if (ctrl->nand_version >= 0x0700) + ctrl->features |= BRCMNAND_HAS_WP; +#ifndef __UBOOT__ + else if (of_property_read_bool(ctrl->dev->of_node, "brcm,nand-has-wp")) +#else + else if (dev_read_bool(ctrl->dev, "brcm,nand-has-wp")) +#endif /* __UBOOT__ */ + ctrl->features |= BRCMNAND_HAS_WP; + + return 0; +} + +static inline u32 brcmnand_read_reg(struct brcmnand_controller *ctrl, + enum brcmnand_reg reg) +{ + u16 offs = ctrl->reg_offsets[reg]; + + if (offs) + return nand_readreg(ctrl, offs); + else + return 0; +} + +static inline void brcmnand_write_reg(struct brcmnand_controller *ctrl, + enum brcmnand_reg reg, u32 val) +{ + u16 offs = ctrl->reg_offsets[reg]; + + if (offs) + nand_writereg(ctrl, offs, val); +} + +static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl, + enum brcmnand_reg reg, u32 mask, unsigned + int shift, u32 val) +{ + u32 tmp = brcmnand_read_reg(ctrl, reg); + + tmp &= ~mask; + tmp |= val << shift; + brcmnand_write_reg(ctrl, reg, tmp); +} + +static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word) +{ + return __raw_readl(ctrl->nand_fc + word * 4); +} + +static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl, + int word, u32 val) +{ + __raw_writel(val, ctrl->nand_fc + word * 4); +} + +static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs, + enum brcmnand_cs_reg reg) +{ + u16 offs_cs0 = ctrl->reg_offsets[BRCMNAND_CS0_BASE]; + u16 offs_cs1 = ctrl->reg_offsets[BRCMNAND_CS1_BASE]; + u8 cs_offs; + + if (cs == 0 && ctrl->cs0_offsets) + cs_offs = ctrl->cs0_offsets[reg]; + else + cs_offs = ctrl->cs_offsets[reg]; + + if (cs && offs_cs1) + return offs_cs1 + (cs - 1) * ctrl->reg_spacing + cs_offs; + + return offs_cs0 + cs * ctrl->reg_spacing + cs_offs; +} + +static inline u32 brcmnand_count_corrected(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version < 0x0600) + return 1; + return brcmnand_read_reg(ctrl, BRCMNAND_CORR_COUNT); +} + +static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val) +{ + struct brcmnand_controller *ctrl = host->ctrl; + unsigned int shift = 0, bits; + enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD; + int cs = host->cs; + + if (ctrl->nand_version >= 0x0702) + bits = 7; + else if (ctrl->nand_version >= 0x0600) + bits = 6; + else if (ctrl->nand_version >= 0x0500) + bits = 5; + else + bits = 4; + + if (ctrl->nand_version >= 0x0702) { + if (cs >= 4) + reg = BRCMNAND_CORR_THRESHOLD_EXT; + shift = (cs % 4) * bits; + } else if (ctrl->nand_version >= 0x0600) { + if (cs >= 5) + reg = BRCMNAND_CORR_THRESHOLD_EXT; + shift = (cs % 5) * bits; + } + brcmnand_rmw_reg(ctrl, reg, (bits - 1) << shift, shift, val); +} + +static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version < 0x0602) + return 24; + return 0; +} + +/*********************************************************************** + * NAND ACC CONTROL bitfield + * + * Some bits have remained constant throughout hardware revision, while + * others have shifted around. + ***********************************************************************/ + +/* Constant for all versions (where supported) */ +enum { + /* See BRCMNAND_HAS_CACHE_MODE */ + ACC_CONTROL_CACHE_MODE = BIT(22), + + /* See BRCMNAND_HAS_PREFETCH */ + ACC_CONTROL_PREFETCH = BIT(23), + + ACC_CONTROL_PAGE_HIT = BIT(24), + ACC_CONTROL_WR_PREEMPT = BIT(25), + ACC_CONTROL_PARTIAL_PAGE = BIT(26), + ACC_CONTROL_RD_ERASED = BIT(27), + ACC_CONTROL_FAST_PGM_RDIN = BIT(28), + ACC_CONTROL_WR_ECC = BIT(30), + ACC_CONTROL_RD_ECC = BIT(31), +}; + +static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version >= 0x0702) + return GENMASK(7, 0); + else if (ctrl->nand_version >= 0x0600) + return GENMASK(6, 0); + else + return GENMASK(5, 0); +} + +#define NAND_ACC_CONTROL_ECC_SHIFT 16 +#define NAND_ACC_CONTROL_ECC_EXT_SHIFT 13 + +static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl) +{ + u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f; + + mask <<= NAND_ACC_CONTROL_ECC_SHIFT; + + /* v7.2 includes additional ECC levels */ + if (ctrl->nand_version >= 0x0702) + mask |= 0x7 << NAND_ACC_CONTROL_ECC_EXT_SHIFT; + + return mask; +} + +static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u16 offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL); + u32 acc_control = nand_readreg(ctrl, offs); + u32 ecc_flags = ACC_CONTROL_WR_ECC | ACC_CONTROL_RD_ECC; + + if (en) { + acc_control |= ecc_flags; /* enable RD/WR ECC */ + acc_control |= host->hwcfg.ecc_level + << NAND_ACC_CONTROL_ECC_SHIFT; + } else { + acc_control &= ~ecc_flags; /* disable RD/WR ECC */ + acc_control &= ~brcmnand_ecc_level_mask(ctrl); + } + + nand_writereg(ctrl, offs, acc_control); +} + +static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version >= 0x0702) + return 9; + else if (ctrl->nand_version >= 0x0600) + return 7; + else if (ctrl->nand_version >= 0x0500) + return 6; + else + return -1; +} + +static int brcmnand_get_sector_size_1k(struct brcmnand_host *host) +{ + struct brcmnand_controller *ctrl = host->ctrl; + int shift = brcmnand_sector_1k_shift(ctrl); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + + if (shift < 0) + return 0; + + return (nand_readreg(ctrl, acc_control_offs) >> shift) & 0x1; +} + +static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val) +{ + struct brcmnand_controller *ctrl = host->ctrl; + int shift = brcmnand_sector_1k_shift(ctrl); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + u32 tmp; + + if (shift < 0) + return; + + tmp = nand_readreg(ctrl, acc_control_offs); + tmp &= ~(1 << shift); + tmp |= (!!val) << shift; + nand_writereg(ctrl, acc_control_offs, tmp); +} + +/*********************************************************************** + * CS_NAND_SELECT + ***********************************************************************/ + +enum { + CS_SELECT_NAND_WP = BIT(29), + CS_SELECT_AUTO_DEVICE_ID_CFG = BIT(30), +}; + +static int bcmnand_ctrl_poll_status(struct brcmnand_controller *ctrl, + u32 mask, u32 expected_val, + unsigned long timeout_ms) +{ +#ifndef __UBOOT__ + unsigned long limit; + u32 val; + + if (!timeout_ms) + timeout_ms = NAND_POLL_STATUS_TIMEOUT_MS; + + limit = jiffies + msecs_to_jiffies(timeout_ms); + do { + val = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS); + if ((val & mask) == expected_val) + return 0; + + cpu_relax(); + } while (time_after(limit, jiffies)); +#else + unsigned long base, limit; + u32 val; + + if (!timeout_ms) + timeout_ms = NAND_POLL_STATUS_TIMEOUT_MS; + + base = get_timer(0); + limit = CONFIG_SYS_HZ * timeout_ms / 1000; + do { + val = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS); + if ((val & mask) == expected_val) + return 0; + + cpu_relax(); + } while (get_timer(base) < limit); +#endif /* __UBOOT__ */ + + dev_warn(ctrl->dev, "timeout on status poll (expected %x got %x)\n", + expected_val, val & mask); + + return -ETIMEDOUT; +} + +static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en) +{ + u32 val = en ? CS_SELECT_NAND_WP : 0; + + brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, CS_SELECT_NAND_WP, 0, val); +} + +/*********************************************************************** + * Flash DMA + ***********************************************************************/ + +enum flash_dma_reg { + FLASH_DMA_REVISION = 0x00, + FLASH_DMA_FIRST_DESC = 0x04, + FLASH_DMA_FIRST_DESC_EXT = 0x08, + FLASH_DMA_CTRL = 0x0c, + FLASH_DMA_MODE = 0x10, + FLASH_DMA_STATUS = 0x14, + FLASH_DMA_INTERRUPT_DESC = 0x18, + FLASH_DMA_INTERRUPT_DESC_EXT = 0x1c, + FLASH_DMA_ERROR_STATUS = 0x20, + FLASH_DMA_CURRENT_DESC = 0x24, + FLASH_DMA_CURRENT_DESC_EXT = 0x28, +}; + +static inline bool has_flash_dma(struct brcmnand_controller *ctrl) +{ + return ctrl->flash_dma_base; +} + +static inline bool flash_dma_buf_ok(const void *buf) +{ +#ifndef __UBOOT__ + return buf && !is_vmalloc_addr(buf) && + likely(IS_ALIGNED((uintptr_t)buf, 4)); +#else + return buf && likely(IS_ALIGNED((uintptr_t)buf, 4)); +#endif /* __UBOOT__ */ +} + +static inline void flash_dma_writel(struct brcmnand_controller *ctrl, u8 offs, + u32 val) +{ + brcmnand_writel(val, ctrl->flash_dma_base + offs); +} + +static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl, u8 offs) +{ + return brcmnand_readl(ctrl->flash_dma_base + offs); +} + +/* Low-level operation types: command, address, write, or read */ +enum brcmnand_llop_type { + LL_OP_CMD, + LL_OP_ADDR, + LL_OP_WR, + LL_OP_RD, +}; + +/*********************************************************************** + * Internal support functions + ***********************************************************************/ + +static inline bool is_hamming_ecc(struct brcmnand_controller *ctrl, + struct brcmnand_cfg *cfg) +{ + if (ctrl->nand_version <= 0x0701) + return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 && + cfg->ecc_level == 15; + else + return cfg->sector_size_1k == 0 && ((cfg->spare_area_size == 16 && + cfg->ecc_level == 15) || + (cfg->spare_area_size == 28 && cfg->ecc_level == 16)); +} + +/* + * Set mtd->ooblayout to the appropriate mtd_ooblayout_ops given + * the layout/configuration. + * Returns -ERRCODE on failure. + */ +static int brcmnand_hamming_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k); + + if (section >= sectors) + return -ERANGE; + + oobregion->offset = (section * sas) + 6; + oobregion->length = 3; + + return 0; +} + +static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k); + + if (section >= sectors * 2) + return -ERANGE; + + oobregion->offset = (section / 2) * sas; + + if (section & 1) { + oobregion->offset += 9; + oobregion->length = 7; + } else { + oobregion->length = 6; + + /* First sector of each page may have BBI */ + if (!section) { + /* + * Small-page NAND use byte 6 for BBI while large-page + * NAND use byte 0. + */ + if (cfg->page_size > 512) + oobregion->offset++; + oobregion->length--; + } + } + + return 0; +} + +static const struct mtd_ooblayout_ops brcmnand_hamming_ooblayout_ops = { + .ecc = brcmnand_hamming_ooblayout_ecc, + .free = brcmnand_hamming_ooblayout_free, +}; + +static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k); + + if (section >= sectors) + return -ERANGE; + + oobregion->offset = (section * (sas + 1)) - chip->ecc.bytes; + oobregion->length = chip->ecc.bytes; + + return 0; +} + +static int brcmnand_bch_ooblayout_free_lp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k); + + if (section >= sectors) + return -ERANGE; + + if (sas <= chip->ecc.bytes) + return 0; + + oobregion->offset = section * sas; + oobregion->length = sas - chip->ecc.bytes; + + if (!section) { + oobregion->offset++; + oobregion->length--; + } + + return 0; +} + +static int brcmnand_bch_ooblayout_free_sp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + + if (section > 1 || sas - chip->ecc.bytes < 6 || + (section && sas - chip->ecc.bytes == 6)) + return -ERANGE; + + if (!section) { + oobregion->offset = 0; + oobregion->length = 5; + } else { + oobregion->offset = 6; + oobregion->length = sas - chip->ecc.bytes - 6; + } + + return 0; +} + +static const struct mtd_ooblayout_ops brcmnand_bch_lp_ooblayout_ops = { + .ecc = brcmnand_bch_ooblayout_ecc, + .free = brcmnand_bch_ooblayout_free_lp, +}; + +static const struct mtd_ooblayout_ops brcmnand_bch_sp_ooblayout_ops = { + .ecc = brcmnand_bch_ooblayout_ecc, + .free = brcmnand_bch_ooblayout_free_sp, +}; + +static int brcmstb_choose_ecc_layout(struct brcmnand_host *host) +{ + struct brcmnand_cfg *p = &host->hwcfg; + struct mtd_info *mtd = nand_to_mtd(&host->chip); + struct nand_ecc_ctrl *ecc = &host->chip.ecc; + unsigned int ecc_level = p->ecc_level; + int sas = p->spare_area_size << p->sector_size_1k; + int sectors = p->page_size / (512 << p->sector_size_1k); + + if (p->sector_size_1k) + ecc_level <<= 1; + + if (is_hamming_ecc(host->ctrl, p)) { + ecc->bytes = 3 * sectors; + mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops); + return 0; + } + + /* + * CONTROLLER_VERSION: + * < v5.0: ECC_REQ = ceil(BCH_T * 13/8) + * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8) + * But we will just be conservative. + */ + ecc->bytes = DIV_ROUND_UP(ecc_level * 14, 8); + if (p->page_size == 512) + mtd_set_ooblayout(mtd, &brcmnand_bch_sp_ooblayout_ops); + else + mtd_set_ooblayout(mtd, &brcmnand_bch_lp_ooblayout_ops); + + if (ecc->bytes >= sas) { + dev_err(&host->pdev->dev, + "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n", + ecc->bytes, sas); + return -EINVAL; + } + + return 0; +} + +static void brcmnand_wp(struct mtd_info *mtd, int wp) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_controller *ctrl = host->ctrl; + + if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) { + static int old_wp = -1; + int ret; + + if (old_wp != wp) { + dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off"); + old_wp = wp; + } + + /* + * make sure ctrl/flash ready before and after + * changing state of #WP pin + */ + ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY | + NAND_STATUS_READY, + NAND_CTRL_RDY | + NAND_STATUS_READY, 0); + if (ret) + return; + + brcmnand_set_wp(ctrl, wp); + nand_status_op(chip, NULL); + /* NAND_STATUS_WP 0x00 = protected, 0x80 = not protected */ + ret = bcmnand_ctrl_poll_status(ctrl, + NAND_CTRL_RDY | + NAND_STATUS_READY | + NAND_STATUS_WP, + NAND_CTRL_RDY | + NAND_STATUS_READY | + (wp ? 0 : NAND_STATUS_WP), 0); +#ifndef __UBOOT__ + if (ret) + dev_err_ratelimited(&host->pdev->dev, + "nand #WP expected %s\n", + wp ? "on" : "off"); +#else + if (ret) + dev_err(&host->pdev->dev, + "nand #WP expected %s\n", + wp ? "on" : "off"); +#endif /* __UBOOT__ */ + } +} + +/* Helper functions for reading and writing OOB registers */ +static inline u8 oob_reg_read(struct brcmnand_controller *ctrl, u32 offs) +{ + u16 offset0, offset10, reg_offs; + + offset0 = ctrl->reg_offsets[BRCMNAND_OOB_READ_BASE]; + offset10 = ctrl->reg_offsets[BRCMNAND_OOB_READ_10_BASE]; + + if (offs >= ctrl->max_oob) + return 0x77; + + if (offs >= 16 && offset10) + reg_offs = offset10 + ((offs - 0x10) & ~0x03); + else + reg_offs = offset0 + (offs & ~0x03); + + return nand_readreg(ctrl, reg_offs) >> (24 - ((offs & 0x03) << 3)); +} + +static inline void oob_reg_write(struct brcmnand_controller *ctrl, u32 offs, + u32 data) +{ + u16 offset0, offset10, reg_offs; + + offset0 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_BASE]; + offset10 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_10_BASE]; + + if (offs >= ctrl->max_oob) + return; + + if (offs >= 16 && offset10) + reg_offs = offset10 + ((offs - 0x10) & ~0x03); + else + reg_offs = offset0 + (offs & ~0x03); + + nand_writereg(ctrl, reg_offs, data); +} + +/* + * read_oob_from_regs - read data from OOB registers + * @ctrl: NAND controller + * @i: sub-page sector index + * @oob: buffer to read to + * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE) + * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal + */ +static int read_oob_from_regs(struct brcmnand_controller *ctrl, int i, u8 *oob, + int sas, int sector_1k) +{ + int tbytes = sas << sector_1k; + int j; + + /* Adjust OOB values for 1K sector size */ + if (sector_1k && (i & 0x01)) + tbytes = max(0, tbytes - (int)ctrl->max_oob); + tbytes = min_t(int, tbytes, ctrl->max_oob); + + for (j = 0; j < tbytes; j++) + oob[j] = oob_reg_read(ctrl, j); + return tbytes; +} + +/* + * write_oob_to_regs - write data to OOB registers + * @i: sub-page sector index + * @oob: buffer to write from + * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE) + * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal + */ +static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i, + const u8 *oob, int sas, int sector_1k) +{ + int tbytes = sas << sector_1k; + int j; + + /* Adjust OOB values for 1K sector size */ + if (sector_1k && (i & 0x01)) + tbytes = max(0, tbytes - (int)ctrl->max_oob); + tbytes = min_t(int, tbytes, ctrl->max_oob); + + for (j = 0; j < tbytes; j += 4) + oob_reg_write(ctrl, j, + (oob[j + 0] << 24) | + (oob[j + 1] << 16) | + (oob[j + 2] << 8) | + (oob[j + 3] << 0)); + return tbytes; +} + +#ifndef __UBOOT__ +static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data) +{ + struct brcmnand_controller *ctrl = data; + + /* Discard all NAND_CTLRDY interrupts during DMA */ + if (ctrl->dma_pending) + return IRQ_HANDLED; + + complete(&ctrl->done); + return IRQ_HANDLED; +} + +/* Handle SoC-specific interrupt hardware */ +static irqreturn_t brcmnand_irq(int irq, void *data) +{ + struct brcmnand_controller *ctrl = data; + + if (ctrl->soc->ctlrdy_ack(ctrl->soc)) + return brcmnand_ctlrdy_irq(irq, data); + + return IRQ_NONE; +} + +static irqreturn_t brcmnand_dma_irq(int irq, void *data) +{ + struct brcmnand_controller *ctrl = data; + + complete(&ctrl->dma_done); + + return IRQ_HANDLED; +} +#endif /* __UBOOT__ */ + +static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd) +{ + struct brcmnand_controller *ctrl = host->ctrl; + int ret; + + dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd, + brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS)); + BUG_ON(ctrl->cmd_pending != 0); + ctrl->cmd_pending = cmd; + + ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, NAND_CTRL_RDY, 0); + WARN_ON(ret); + + mb(); /* flush previous writes */ + brcmnand_write_reg(ctrl, BRCMNAND_CMD_START, + cmd << brcmnand_cmd_shift(ctrl)); +} + +/*********************************************************************** + * NAND MTD API: read/program/erase + ***********************************************************************/ + +static void brcmnand_cmd_ctrl(struct mtd_info *mtd, int dat, + unsigned int ctrl) +{ + /* intentionally left blank */ +} + +static int brcmnand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_controller *ctrl = host->ctrl; + +#ifndef __UBOOT__ + unsigned long timeo = msecs_to_jiffies(100); + + dev_dbg(ctrl->dev, "wait on native cmd %d\n", ctrl->cmd_pending); + if (ctrl->cmd_pending && + wait_for_completion_timeout(&ctrl->done, timeo) <= 0) { + u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START) + >> brcmnand_cmd_shift(ctrl); + + dev_err_ratelimited(ctrl->dev, + "timeout waiting for command %#02x\n", cmd); + dev_err_ratelimited(ctrl->dev, "intfc status %08x\n", + brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS)); + } +#else + unsigned long timeo = 100; /* 100 msec */ + int ret; + + dev_dbg(ctrl->dev, "wait on native cmd %d\n", ctrl->cmd_pending); + + ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, NAND_CTRL_RDY, timeo); + WARN_ON(ret); +#endif /* __UBOOT__ */ + + ctrl->cmd_pending = 0; + return brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) & + INTFC_FLASH_STATUS; +} + +enum { + LLOP_RE = BIT(16), + LLOP_WE = BIT(17), + LLOP_ALE = BIT(18), + LLOP_CLE = BIT(19), + LLOP_RETURN_IDLE = BIT(31), + + LLOP_DATA_MASK = GENMASK(15, 0), +}; + +static int brcmnand_low_level_op(struct brcmnand_host *host, + enum brcmnand_llop_type type, u32 data, + bool last_op) +{ + struct mtd_info *mtd = nand_to_mtd(&host->chip); + struct nand_chip *chip = &host->chip; + struct brcmnand_controller *ctrl = host->ctrl; + u32 tmp; + + tmp = data & LLOP_DATA_MASK; + switch (type) { + case LL_OP_CMD: + tmp |= LLOP_WE | LLOP_CLE; + break; + case LL_OP_ADDR: + /* WE | ALE */ + tmp |= LLOP_WE | LLOP_ALE; + break; + case LL_OP_WR: + /* WE */ + tmp |= LLOP_WE; + break; + case LL_OP_RD: + /* RE */ + tmp |= LLOP_RE; + break; + } + if (last_op) + /* RETURN_IDLE */ + tmp |= LLOP_RETURN_IDLE; + + dev_dbg(ctrl->dev, "ll_op cmd %#x\n", tmp); + + brcmnand_write_reg(ctrl, BRCMNAND_LL_OP, tmp); + (void)brcmnand_read_reg(ctrl, BRCMNAND_LL_OP); + + brcmnand_send_cmd(host, CMD_LOW_LEVEL_OP); + return brcmnand_waitfunc(mtd, chip); +} + +static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_controller *ctrl = host->ctrl; + u64 addr = (u64)page_addr << chip->page_shift; + int native_cmd = 0; + + if (command == NAND_CMD_READID || command == NAND_CMD_PARAM || + command == NAND_CMD_RNDOUT) + addr = (u64)column; + /* Avoid propagating a negative, don't-care address */ + else if (page_addr < 0) + addr = 0; + + dev_dbg(ctrl->dev, "cmd 0x%x addr 0x%llx\n", command, + (unsigned long long)addr); + + host->last_cmd = command; + host->last_byte = 0; + host->last_addr = addr; + + switch (command) { + case NAND_CMD_RESET: + native_cmd = CMD_FLASH_RESET; + break; + case NAND_CMD_STATUS: + native_cmd = CMD_STATUS_READ; + break; + case NAND_CMD_READID: + native_cmd = CMD_DEVICE_ID_READ; + break; + case NAND_CMD_READOOB: + native_cmd = CMD_SPARE_AREA_READ; + break; + case NAND_CMD_ERASE1: + native_cmd = CMD_BLOCK_ERASE; + brcmnand_wp(mtd, 0); + break; + case NAND_CMD_PARAM: + native_cmd = CMD_PARAMETER_READ; + break; + case NAND_CMD_SET_FEATURES: + case NAND_CMD_GET_FEATURES: + brcmnand_low_level_op(host, LL_OP_CMD, command, false); + brcmnand_low_level_op(host, LL_OP_ADDR, column, false); + break; + case NAND_CMD_RNDOUT: + native_cmd = CMD_PARAMETER_CHANGE_COL; + addr &= ~((u64)(FC_BYTES - 1)); + /* + * HW quirk: PARAMETER_CHANGE_COL requires SECTOR_SIZE_1K=0 + * NB: hwcfg.sector_size_1k may not be initialized yet + */ + if (brcmnand_get_sector_size_1k(host)) { + host->hwcfg.sector_size_1k = + brcmnand_get_sector_size_1k(host); + brcmnand_set_sector_size_1k(host, 0); + } + break; + } + + if (!native_cmd) + return; + + brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, + (host->cs << 16) | ((addr >> 32) & 0xffff)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); + brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, lower_32_bits(addr)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + + brcmnand_send_cmd(host, native_cmd); + brcmnand_waitfunc(mtd, chip); + + if (native_cmd == CMD_PARAMETER_READ || + native_cmd == CMD_PARAMETER_CHANGE_COL) { + /* Copy flash cache word-wise */ + u32 *flash_cache = (u32 *)ctrl->flash_cache; + int i; + + brcmnand_soc_data_bus_prepare(ctrl->soc, true); + + /* + * Must cache the FLASH_CACHE now, since changes in + * SECTOR_SIZE_1K may invalidate it + */ + for (i = 0; i < FC_WORDS; i++) { + u32 fc; + + fc = brcmnand_read_fc(ctrl, i); + + /* + * Flash cache is big endian for parameter pages, at + * least on STB SoCs + */ + if (ctrl->parameter_page_big_endian) + flash_cache[i] = be32_to_cpu(fc); + else + flash_cache[i] = le32_to_cpu(fc); + } + + brcmnand_soc_data_bus_unprepare(ctrl->soc, true); + + /* Cleanup from HW quirk: restore SECTOR_SIZE_1K */ + if (host->hwcfg.sector_size_1k) + brcmnand_set_sector_size_1k(host, + host->hwcfg.sector_size_1k); + } + + /* Re-enable protection is necessary only after erase */ + if (command == NAND_CMD_ERASE1) + brcmnand_wp(mtd, 1); +} + +static uint8_t brcmnand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_controller *ctrl = host->ctrl; + uint8_t ret = 0; + int addr, offs; + + switch (host->last_cmd) { + case NAND_CMD_READID: + if (host->last_byte < 4) + ret = brcmnand_read_reg(ctrl, BRCMNAND_ID) >> + (24 - (host->last_byte << 3)); + else if (host->last_byte < 8) + ret = brcmnand_read_reg(ctrl, BRCMNAND_ID_EXT) >> + (56 - (host->last_byte << 3)); + break; + + case NAND_CMD_READOOB: + ret = oob_reg_read(ctrl, host->last_byte); + break; + + case NAND_CMD_STATUS: + ret = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) & + INTFC_FLASH_STATUS; + if (wp_on) /* hide WP status */ + ret |= NAND_STATUS_WP; + break; + + case NAND_CMD_PARAM: + case NAND_CMD_RNDOUT: + addr = host->last_addr + host->last_byte; + offs = addr & (FC_BYTES - 1); + + /* At FC_BYTES boundary, switch to next column */ + if (host->last_byte > 0 && offs == 0) + nand_change_read_column_op(chip, addr, NULL, 0, false); + + ret = ctrl->flash_cache[offs]; + break; + case NAND_CMD_GET_FEATURES: + if (host->last_byte >= ONFI_SUBFEATURE_PARAM_LEN) { + ret = 0; + } else { + bool last = host->last_byte == + ONFI_SUBFEATURE_PARAM_LEN - 1; + brcmnand_low_level_op(host, LL_OP_RD, 0, last); + ret = brcmnand_read_reg(ctrl, BRCMNAND_LL_RDATA) & 0xff; + } + } + + dev_dbg(ctrl->dev, "read byte = 0x%02x\n", ret); + host->last_byte++; + + return ret; +} + +static void brcmnand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + + for (i = 0; i < len; i++, buf++) + *buf = brcmnand_read_byte(mtd); +} + +static void brcmnand_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + int i; + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + + switch (host->last_cmd) { + case NAND_CMD_SET_FEATURES: + for (i = 0; i < len; i++) + brcmnand_low_level_op(host, LL_OP_WR, buf[i], + (i + 1) == len); + break; + default: + BUG(); + break; + } +} + +/** + * Construct a FLASH_DMA descriptor as part of a linked list. You must know the + * following ahead of time: + * - Is this descriptor the beginning or end of a linked list? + * - What is the (DMA) address of the next descriptor in the linked list? + */ +#ifndef __UBOOT__ +static int brcmnand_fill_dma_desc(struct brcmnand_host *host, + struct brcm_nand_dma_desc *desc, u64 addr, + dma_addr_t buf, u32 len, u8 dma_cmd, + bool begin, bool end, + dma_addr_t next_desc) +{ + memset(desc, 0, sizeof(*desc)); + /* Descriptors are written in native byte order (wordwise) */ + desc->next_desc = lower_32_bits(next_desc); + desc->next_desc_ext = upper_32_bits(next_desc); + desc->cmd_irq = (dma_cmd << 24) | + (end ? (0x03 << 8) : 0) | /* IRQ | STOP */ + (!!begin) | ((!!end) << 1); /* head, tail */ +#ifdef CONFIG_CPU_BIG_ENDIAN + desc->cmd_irq |= 0x01 << 12; +#endif + desc->dram_addr = lower_32_bits(buf); + desc->dram_addr_ext = upper_32_bits(buf); + desc->tfr_len = len; + desc->total_len = len; + desc->flash_addr = lower_32_bits(addr); + desc->flash_addr_ext = upper_32_bits(addr); + desc->cs = host->cs; + desc->status_valid = 0x01; + return 0; +} + +/** + * Kick the FLASH_DMA engine, with a given DMA descriptor + */ +static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc) +{ + struct brcmnand_controller *ctrl = host->ctrl; + unsigned long timeo = msecs_to_jiffies(100); + + flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC, lower_32_bits(desc)); + (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC); + flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT, upper_32_bits(desc)); + (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT); + + /* Start FLASH_DMA engine */ + ctrl->dma_pending = true; + mb(); /* flush previous writes */ + flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0x03); /* wake | run */ + + if (wait_for_completion_timeout(&ctrl->dma_done, timeo) <= 0) { + dev_err(ctrl->dev, + "timeout waiting for DMA; status %#x, error status %#x\n", + flash_dma_readl(ctrl, FLASH_DMA_STATUS), + flash_dma_readl(ctrl, FLASH_DMA_ERROR_STATUS)); + } + ctrl->dma_pending = false; + flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0); /* force stop */ +} + +static int brcmnand_dma_trans(struct brcmnand_host *host, u64 addr, u32 *buf, + u32 len, u8 dma_cmd) +{ + struct brcmnand_controller *ctrl = host->ctrl; + dma_addr_t buf_pa; + int dir = dma_cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + buf_pa = dma_map_single(ctrl->dev, buf, len, dir); + if (dma_mapping_error(ctrl->dev, buf_pa)) { + dev_err(ctrl->dev, "unable to map buffer for DMA\n"); + return -ENOMEM; + } + + brcmnand_fill_dma_desc(host, ctrl->dma_desc, addr, buf_pa, len, + dma_cmd, true, true, 0); + + brcmnand_dma_run(host, ctrl->dma_pa); + + dma_unmap_single(ctrl->dev, buf_pa, len, dir); + + if (ctrl->dma_desc->status_valid & FLASH_DMA_ECC_ERROR) + return -EBADMSG; + else if (ctrl->dma_desc->status_valid & FLASH_DMA_CORR_ERROR) + return -EUCLEAN; + + return 0; +} +#endif /* __UBOOT__ */ + +/* + * Assumes proper CS is already set + */ +static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, + u64 addr, unsigned int trans, u32 *buf, + u8 *oob, u64 *err_addr) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_controller *ctrl = host->ctrl; + int i, j, ret = 0; + + /* Clear error addresses */ + brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_ADDR, 0); + brcmnand_write_reg(ctrl, BRCMNAND_CORR_ADDR, 0); + brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_EXT_ADDR, 0); + brcmnand_write_reg(ctrl, BRCMNAND_CORR_EXT_ADDR, 0); + + brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, + (host->cs << 16) | ((addr >> 32) & 0xffff)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); + + for (i = 0; i < trans; i++, addr += FC_BYTES) { + brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, + lower_32_bits(addr)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + /* SPARE_AREA_READ does not use ECC, so just use PAGE_READ */ + brcmnand_send_cmd(host, CMD_PAGE_READ); + brcmnand_waitfunc(mtd, chip); + + if (likely(buf)) { + brcmnand_soc_data_bus_prepare(ctrl->soc, false); + + for (j = 0; j < FC_WORDS; j++, buf++) + *buf = brcmnand_read_fc(ctrl, j); + + brcmnand_soc_data_bus_unprepare(ctrl->soc, false); + } + + if (oob) + oob += read_oob_from_regs(ctrl, i, oob, + mtd->oobsize / trans, + host->hwcfg.sector_size_1k); + + if (!ret) { + *err_addr = brcmnand_read_reg(ctrl, + BRCMNAND_UNCORR_ADDR) | + ((u64)(brcmnand_read_reg(ctrl, + BRCMNAND_UNCORR_EXT_ADDR) + & 0xffff) << 32); + if (*err_addr) + ret = -EBADMSG; + } + + if (!ret) { + *err_addr = brcmnand_read_reg(ctrl, + BRCMNAND_CORR_ADDR) | + ((u64)(brcmnand_read_reg(ctrl, + BRCMNAND_CORR_EXT_ADDR) + & 0xffff) << 32); + if (*err_addr) + ret = -EUCLEAN; + } + } + + return ret; +} + +/* + * Check a page to see if it is erased (w/ bitflips) after an uncorrectable ECC + * error + * + * Because the HW ECC signals an ECC error if an erase paged has even a single + * bitflip, we must check each ECC error to see if it is actually an erased + * page with bitflips, not a truly corrupted page. + * + * On a real error, return a negative error code (-EBADMSG for ECC error), and + * buf will contain raw data. + * Otherwise, buf gets filled with 0xffs and return the maximum number of + * bitflips-per-ECC-sector to the caller. + * + */ +static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd, + struct nand_chip *chip, void *buf, u64 addr) +{ + int i, sas; + void *oob = chip->oob_poi; + int bitflips = 0; + int page = addr >> chip->page_shift; + int ret; + + if (!buf) { +#ifndef __UBOOT__ + buf = chip->data_buf; +#else + buf = chip->buffers->databuf; +#endif + /* Invalidate page cache */ + chip->pagebuf = -1; + } + + sas = mtd->oobsize / chip->ecc.steps; + + /* read without ecc for verification */ + ret = chip->ecc.read_page_raw(mtd, chip, buf, true, page); + if (ret) + return ret; + + for (i = 0; i < chip->ecc.steps; i++, oob += sas) { + ret = nand_check_erased_ecc_chunk(buf, chip->ecc.size, + oob, sas, NULL, 0, + chip->ecc.strength); + if (ret < 0) + return ret; + + bitflips = max(bitflips, ret); + } + + return bitflips; +} + +static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip, + u64 addr, unsigned int trans, u32 *buf, u8 *oob) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_controller *ctrl = host->ctrl; + u64 err_addr = 0; + int err; + bool retry = true; + + dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf); + +try_dmaread: + brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_COUNT, 0); + +#ifndef __UBOOT__ + if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { + err = brcmnand_dma_trans(host, addr, buf, trans * FC_BYTES, + CMD_PAGE_READ); + if (err) { + if (mtd_is_bitflip_or_eccerr(err)) + err_addr = addr; + else + return -EIO; + } + } else { + if (oob) + memset(oob, 0x99, mtd->oobsize); + + err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf, + oob, &err_addr); + } +#else + if (oob) + memset(oob, 0x99, mtd->oobsize); + + err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf, + oob, &err_addr); +#endif /* __UBOOT__ */ + + if (mtd_is_eccerr(err)) { + /* + * On controller version and 7.0, 7.1 , DMA read after a + * prior PIO read that reported uncorrectable error, + * the DMA engine captures this error following DMA read + * cleared only on subsequent DMA read, so just retry once + * to clear a possible false error reported for current DMA + * read + */ + if ((ctrl->nand_version == 0x0700) || + (ctrl->nand_version == 0x0701)) { + if (retry) { + retry = false; + goto try_dmaread; + } + } + + /* + * Controller version 7.2 has hw encoder to detect erased page + * bitflips, apply sw verification for older controllers only + */ + if (ctrl->nand_version < 0x0702) { + err = brcmstb_nand_verify_erased_page(mtd, chip, buf, + addr); + /* erased page bitflips corrected */ + if (err >= 0) + return err; + } + + dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n", + (unsigned long long)err_addr); + mtd->ecc_stats.failed++; + /* NAND layer expects zero on ECC errors */ + return 0; + } + + if (mtd_is_bitflip(err)) { + unsigned int corrected = brcmnand_count_corrected(ctrl); + + dev_dbg(ctrl->dev, "corrected error at 0x%llx\n", + (unsigned long long)err_addr); + mtd->ecc_stats.corrected += corrected; + /* Always exceed the software-imposed threshold */ + return max(mtd->bitflip_threshold, corrected); + } + + return 0; +} + +static int brcmnand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL; + + nand_read_page_op(chip, page, 0, NULL, 0); + + return brcmnand_read(mtd, chip, host->last_addr, + mtd->writesize >> FC_SHIFT, (u32 *)buf, oob); +} + +static int brcmnand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL; + int ret; + + nand_read_page_op(chip, page, 0, NULL, 0); + + brcmnand_set_ecc_enabled(host, 0); + ret = brcmnand_read(mtd, chip, host->last_addr, + mtd->writesize >> FC_SHIFT, (u32 *)buf, oob); + brcmnand_set_ecc_enabled(host, 1); + return ret; +} + +static int brcmnand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return brcmnand_read(mtd, chip, (u64)page << chip->page_shift, + mtd->writesize >> FC_SHIFT, + NULL, (u8 *)chip->oob_poi); +} + +static int brcmnand_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + + brcmnand_set_ecc_enabled(host, 0); + brcmnand_read(mtd, chip, (u64)page << chip->page_shift, + mtd->writesize >> FC_SHIFT, + NULL, (u8 *)chip->oob_poi); + brcmnand_set_ecc_enabled(host, 1); + return 0; +} + +static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip, + u64 addr, const u32 *buf, u8 *oob) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_controller *ctrl = host->ctrl; + unsigned int i, j, trans = mtd->writesize >> FC_SHIFT; + int status, ret = 0; + + dev_dbg(ctrl->dev, "write %llx <- %p\n", (unsigned long long)addr, buf); + + if (unlikely((unsigned long)buf & 0x03)) { + dev_warn(ctrl->dev, "unaligned buffer: %p\n", buf); + buf = (u32 *)((unsigned long)buf & ~0x03); + } + + brcmnand_wp(mtd, 0); + + for (i = 0; i < ctrl->max_oob; i += 4) + oob_reg_write(ctrl, i, 0xffffffff); + +#ifndef __UBOOT__ + if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { + if (brcmnand_dma_trans(host, addr, (u32 *)buf, + mtd->writesize, CMD_PROGRAM_PAGE)) + ret = -EIO; + goto out; + } +#endif /* __UBOOT__ */ + + brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, + (host->cs << 16) | ((addr >> 32) & 0xffff)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); + + for (i = 0; i < trans; i++, addr += FC_BYTES) { + /* full address MUST be set before populating FC */ + brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, + lower_32_bits(addr)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + + if (buf) { + brcmnand_soc_data_bus_prepare(ctrl->soc, false); + + for (j = 0; j < FC_WORDS; j++, buf++) + brcmnand_write_fc(ctrl, j, *buf); + + brcmnand_soc_data_bus_unprepare(ctrl->soc, false); + } else if (oob) { + for (j = 0; j < FC_WORDS; j++) + brcmnand_write_fc(ctrl, j, 0xffffffff); + } + + if (oob) { + oob += write_oob_to_regs(ctrl, i, oob, + mtd->oobsize / trans, + host->hwcfg.sector_size_1k); + } + + /* we cannot use SPARE_AREA_PROGRAM when PARTIAL_PAGE_EN=0 */ + brcmnand_send_cmd(host, CMD_PROGRAM_PAGE); + status = brcmnand_waitfunc(mtd, chip); + + if (status & NAND_STATUS_FAIL) { + dev_info(ctrl->dev, "program failed at %llx\n", + (unsigned long long)addr); + ret = -EIO; + goto out; + } + } +out: + brcmnand_wp(mtd, 1); + return ret; +} + +static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + void *oob = oob_required ? chip->oob_poi : NULL; + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); + + return nand_prog_page_end_op(chip); +} + +static int brcmnand_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, + int oob_required, int page) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + void *oob = oob_required ? chip->oob_poi : NULL; + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + brcmnand_set_ecc_enabled(host, 0); + brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); + brcmnand_set_ecc_enabled(host, 1); + + return nand_prog_page_end_op(chip); +} + +static int brcmnand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return brcmnand_write(mtd, chip, (u64)page << chip->page_shift, + NULL, chip->oob_poi); +} + +static int brcmnand_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + int ret; + + brcmnand_set_ecc_enabled(host, 0); + ret = brcmnand_write(mtd, chip, (u64)page << chip->page_shift, NULL, + (u8 *)chip->oob_poi); + brcmnand_set_ecc_enabled(host, 1); + + return ret; +} + +/*********************************************************************** + * Per-CS setup (1 NAND device) + ***********************************************************************/ + +static int brcmnand_set_cfg(struct brcmnand_host *host, + struct brcmnand_cfg *cfg) +{ + struct brcmnand_controller *ctrl = host->ctrl; + struct nand_chip *chip = &host->chip; + u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG); + u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_CFG_EXT); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + u8 block_size = 0, page_size = 0, device_size = 0; + u32 tmp; + + if (ctrl->block_sizes) { + int i, found; + + for (i = 0, found = 0; ctrl->block_sizes[i]; i++) + if (ctrl->block_sizes[i] * 1024 == cfg->block_size) { + block_size = i; + found = 1; + } + if (!found) { + dev_warn(ctrl->dev, "invalid block size %u\n", + cfg->block_size); + return -EINVAL; + } + } else { + block_size = ffs(cfg->block_size) - ffs(BRCMNAND_MIN_BLOCKSIZE); + } + + if (cfg->block_size < BRCMNAND_MIN_BLOCKSIZE || (ctrl->max_block_size && + cfg->block_size > ctrl->max_block_size)) { + dev_warn(ctrl->dev, "invalid block size %u\n", + cfg->block_size); + block_size = 0; + } + + if (ctrl->page_sizes) { + int i, found; + + for (i = 0, found = 0; ctrl->page_sizes[i]; i++) + if (ctrl->page_sizes[i] == cfg->page_size) { + page_size = i; + found = 1; + } + if (!found) { + dev_warn(ctrl->dev, "invalid page size %u\n", + cfg->page_size); + return -EINVAL; + } + } else { + page_size = ffs(cfg->page_size) - ffs(BRCMNAND_MIN_PAGESIZE); + } + + if (cfg->page_size < BRCMNAND_MIN_PAGESIZE || (ctrl->max_page_size && + cfg->page_size > ctrl->max_page_size)) { + dev_warn(ctrl->dev, "invalid page size %u\n", cfg->page_size); + return -EINVAL; + } + + if (fls64(cfg->device_size) < fls64(BRCMNAND_MIN_DEVSIZE)) { + dev_warn(ctrl->dev, "invalid device size 0x%llx\n", + (unsigned long long)cfg->device_size); + return -EINVAL; + } + device_size = fls64(cfg->device_size) - fls64(BRCMNAND_MIN_DEVSIZE); + + tmp = (cfg->blk_adr_bytes << CFG_BLK_ADR_BYTES_SHIFT) | + (cfg->col_adr_bytes << CFG_COL_ADR_BYTES_SHIFT) | + (cfg->ful_adr_bytes << CFG_FUL_ADR_BYTES_SHIFT) | + (!!(cfg->device_width == 16) << CFG_BUS_WIDTH_SHIFT) | + (device_size << CFG_DEVICE_SIZE_SHIFT); + if (cfg_offs == cfg_ext_offs) { + tmp |= (page_size << CFG_PAGE_SIZE_SHIFT) | + (block_size << CFG_BLK_SIZE_SHIFT); + nand_writereg(ctrl, cfg_offs, tmp); + } else { + nand_writereg(ctrl, cfg_offs, tmp); + tmp = (page_size << CFG_EXT_PAGE_SIZE_SHIFT) | + (block_size << CFG_EXT_BLK_SIZE_SHIFT); + nand_writereg(ctrl, cfg_ext_offs, tmp); + } + + tmp = nand_readreg(ctrl, acc_control_offs); + tmp &= ~brcmnand_ecc_level_mask(ctrl); + tmp |= cfg->ecc_level << NAND_ACC_CONTROL_ECC_SHIFT; + tmp &= ~brcmnand_spare_area_mask(ctrl); + tmp |= cfg->spare_area_size; + nand_writereg(ctrl, acc_control_offs, tmp); + + brcmnand_set_sector_size_1k(host, cfg->sector_size_1k); + + /* threshold = ceil(BCH-level * 0.75) */ + brcmnand_wr_corr_thresh(host, DIV_ROUND_UP(chip->ecc.strength * 3, 4)); + + return 0; +} + +static void brcmnand_print_cfg(struct brcmnand_host *host, + char *buf, struct brcmnand_cfg *cfg) +{ + buf += sprintf(buf, + "%lluMiB total, %uKiB blocks, %u%s pages, %uB OOB, %u-bit", + (unsigned long long)cfg->device_size >> 20, + cfg->block_size >> 10, + cfg->page_size >= 1024 ? cfg->page_size >> 10 : cfg->page_size, + cfg->page_size >= 1024 ? "KiB" : "B", + cfg->spare_area_size, cfg->device_width); + + /* Account for Hamming ECC and for BCH 512B vs 1KiB sectors */ + if (is_hamming_ecc(host->ctrl, cfg)) + sprintf(buf, ", Hamming ECC"); + else if (cfg->sector_size_1k) + sprintf(buf, ", BCH-%u (1KiB sector)", cfg->ecc_level << 1); + else + sprintf(buf, ", BCH-%u", cfg->ecc_level); +} + +/* + * Minimum number of bytes to address a page. Calculated as: + * roundup(log2(size / page-size) / 8) + * + * NB: the following does not "round up" for non-power-of-2 'size'; but this is + * OK because many other things will break if 'size' is irregular... + */ +static inline int get_blk_adr_bytes(u64 size, u32 writesize) +{ + return ALIGN(ilog2(size) - ilog2(writesize), 8) >> 3; +} + +static int brcmnand_setup_dev(struct brcmnand_host *host) +{ + struct mtd_info *mtd = nand_to_mtd(&host->chip); + struct nand_chip *chip = &host->chip; + struct brcmnand_controller *ctrl = host->ctrl; + struct brcmnand_cfg *cfg = &host->hwcfg; + char msg[128]; + u32 offs, tmp, oob_sector; + int ret; + + memset(cfg, 0, sizeof(*cfg)); + +#ifndef __UBOOT__ + ret = of_property_read_u32(nand_get_flash_node(chip), + "brcm,nand-oob-sector-size", + &oob_sector); +#else + ret = ofnode_read_u32(nand_get_flash_node(chip), + "brcm,nand-oob-sector-size", + &oob_sector); +#endif /* __UBOOT__ */ + if (ret) { + /* Use detected size */ + cfg->spare_area_size = mtd->oobsize / + (mtd->writesize >> FC_SHIFT); + } else { + cfg->spare_area_size = oob_sector; + } + if (cfg->spare_area_size > ctrl->max_oob) + cfg->spare_area_size = ctrl->max_oob; + /* + * Set oobsize to be consistent with controller's spare_area_size, as + * the rest is inaccessible. + */ + mtd->oobsize = cfg->spare_area_size * (mtd->writesize >> FC_SHIFT); + + cfg->device_size = mtd->size; + cfg->block_size = mtd->erasesize; + cfg->page_size = mtd->writesize; + cfg->device_width = (chip->options & NAND_BUSWIDTH_16) ? 16 : 8; + cfg->col_adr_bytes = 2; + cfg->blk_adr_bytes = get_blk_adr_bytes(mtd->size, mtd->writesize); + + if (chip->ecc.mode != NAND_ECC_HW) { + dev_err(ctrl->dev, "only HW ECC supported; selected: %d\n", + chip->ecc.mode); + return -EINVAL; + } + + if (chip->ecc.algo == NAND_ECC_UNKNOWN) { + if (chip->ecc.strength == 1 && chip->ecc.size == 512) + /* Default to Hamming for 1-bit ECC, if unspecified */ + chip->ecc.algo = NAND_ECC_HAMMING; + else + /* Otherwise, BCH */ + chip->ecc.algo = NAND_ECC_BCH; + } + + if (chip->ecc.algo == NAND_ECC_HAMMING && (chip->ecc.strength != 1 || + chip->ecc.size != 512)) { + dev_err(ctrl->dev, "invalid Hamming params: %d bits per %d bytes\n", + chip->ecc.strength, chip->ecc.size); + return -EINVAL; + } + + switch (chip->ecc.size) { + case 512: + if (chip->ecc.algo == NAND_ECC_HAMMING) + cfg->ecc_level = 15; + else + cfg->ecc_level = chip->ecc.strength; + cfg->sector_size_1k = 0; + break; + case 1024: + if (!(ctrl->features & BRCMNAND_HAS_1K_SECTORS)) { + dev_err(ctrl->dev, "1KB sectors not supported\n"); + return -EINVAL; + } + if (chip->ecc.strength & 0x1) { + dev_err(ctrl->dev, + "odd ECC not supported with 1KB sectors\n"); + return -EINVAL; + } + + cfg->ecc_level = chip->ecc.strength >> 1; + cfg->sector_size_1k = 1; + break; + default: + dev_err(ctrl->dev, "unsupported ECC size: %d\n", + chip->ecc.size); + return -EINVAL; + } + + cfg->ful_adr_bytes = cfg->blk_adr_bytes; + if (mtd->writesize > 512) + cfg->ful_adr_bytes += cfg->col_adr_bytes; + else + cfg->ful_adr_bytes += 1; + + ret = brcmnand_set_cfg(host, cfg); + if (ret) + return ret; + + brcmnand_set_ecc_enabled(host, 1); + + brcmnand_print_cfg(host, msg, cfg); + dev_info(ctrl->dev, "detected %s\n", msg); + + /* Configure ACC_CONTROL */ + offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL); + tmp = nand_readreg(ctrl, offs); + tmp &= ~ACC_CONTROL_PARTIAL_PAGE; + tmp &= ~ACC_CONTROL_RD_ERASED; + + /* We need to turn on Read from erased paged protected by ECC */ + if (ctrl->nand_version >= 0x0702) + tmp |= ACC_CONTROL_RD_ERASED; + tmp &= ~ACC_CONTROL_FAST_PGM_RDIN; + if (ctrl->features & BRCMNAND_HAS_PREFETCH) + tmp &= ~ACC_CONTROL_PREFETCH; + + nand_writereg(ctrl, offs, tmp); + + return 0; +} + +#ifndef __UBOOT__ +static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn) +#else +static int brcmnand_init_cs(struct brcmnand_host *host, ofnode dn) +#endif +{ + struct brcmnand_controller *ctrl = host->ctrl; +#ifndef __UBOOT__ + struct platform_device *pdev = host->pdev; +#else + struct udevice *pdev = host->pdev; +#endif /* __UBOOT__ */ + struct mtd_info *mtd; + struct nand_chip *chip; + int ret; + u16 cfg_offs; + +#ifndef __UBOOT__ + ret = of_property_read_u32(dn, "reg", &host->cs); +#else + ret = ofnode_read_s32(dn, "reg", &host->cs); +#endif + if (ret) { + dev_err(&pdev->dev, "can't get chip-select\n"); + return -ENXIO; + } + + mtd = nand_to_mtd(&host->chip); + chip = &host->chip; + + nand_set_flash_node(chip, dn); + nand_set_controller_data(chip, host); +#ifndef __UBOOT__ + mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d", + host->cs); +#else + mtd->name = devm_kasprintf(pdev, GFP_KERNEL, "brcmnand.%d", + host->cs); +#endif /* __UBOOT__ */ + if (!mtd->name) + return -ENOMEM; + + mtd->owner = THIS_MODULE; +#ifndef __UBOOT__ + mtd->dev.parent = &pdev->dev; +#else + mtd->dev->parent = pdev; +#endif /* __UBOOT__ */ + + chip->IO_ADDR_R = (void __iomem *)0xdeadbeef; + chip->IO_ADDR_W = (void __iomem *)0xdeadbeef; + + chip->cmd_ctrl = brcmnand_cmd_ctrl; + chip->cmdfunc = brcmnand_cmdfunc; + chip->waitfunc = brcmnand_waitfunc; + chip->read_byte = brcmnand_read_byte; + chip->read_buf = brcmnand_read_buf; + chip->write_buf = brcmnand_write_buf; + + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.read_page = brcmnand_read_page; + chip->ecc.write_page = brcmnand_write_page; + chip->ecc.read_page_raw = brcmnand_read_page_raw; + chip->ecc.write_page_raw = brcmnand_write_page_raw; + chip->ecc.write_oob_raw = brcmnand_write_oob_raw; + chip->ecc.read_oob_raw = brcmnand_read_oob_raw; + chip->ecc.read_oob = brcmnand_read_oob; + chip->ecc.write_oob = brcmnand_write_oob; + + chip->controller = &ctrl->controller; + + /* + * The bootloader might have configured 16bit mode but + * NAND READID command only works in 8bit mode. We force + * 8bit mode here to ensure that NAND READID commands works. + */ + cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG); + nand_writereg(ctrl, cfg_offs, + nand_readreg(ctrl, cfg_offs) & ~CFG_BUS_WIDTH); + + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + return ret; + + chip->options |= NAND_NO_SUBPAGE_WRITE; + /* + * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA + * to/from, and have nand_base pass us a bounce buffer instead, as + * needed. + */ + chip->options |= NAND_USE_BOUNCE_BUFFER; + + if (chip->bbt_options & NAND_BBT_USE_FLASH) + chip->bbt_options |= NAND_BBT_NO_OOB; + + if (brcmnand_setup_dev(host)) + return -ENXIO; + + chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512; + /* only use our internal HW threshold */ + mtd->bitflip_threshold = 1; + + ret = brcmstb_choose_ecc_layout(host); + if (ret) + return ret; + + ret = nand_scan_tail(mtd); + if (ret) + return ret; + +#ifndef __UBOOT__ + ret = mtd_device_register(mtd, NULL, 0); + if (ret) + nand_cleanup(chip); +#else + ret = nand_register(0, mtd); +#endif /* __UBOOT__ */ + + return ret; +} + +#ifndef __UBOOT__ +static void brcmnand_save_restore_cs_config(struct brcmnand_host *host, + int restore) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG); + u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_CFG_EXT); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + u16 t1_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING1); + u16 t2_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING2); + + if (restore) { + nand_writereg(ctrl, cfg_offs, host->hwcfg.config); + if (cfg_offs != cfg_ext_offs) + nand_writereg(ctrl, cfg_ext_offs, + host->hwcfg.config_ext); + nand_writereg(ctrl, acc_control_offs, host->hwcfg.acc_control); + nand_writereg(ctrl, t1_offs, host->hwcfg.timing_1); + nand_writereg(ctrl, t2_offs, host->hwcfg.timing_2); + } else { + host->hwcfg.config = nand_readreg(ctrl, cfg_offs); + if (cfg_offs != cfg_ext_offs) + host->hwcfg.config_ext = + nand_readreg(ctrl, cfg_ext_offs); + host->hwcfg.acc_control = nand_readreg(ctrl, acc_control_offs); + host->hwcfg.timing_1 = nand_readreg(ctrl, t1_offs); + host->hwcfg.timing_2 = nand_readreg(ctrl, t2_offs); + } +} + +static int brcmnand_suspend(struct device *dev) +{ + struct brcmnand_controller *ctrl = dev_get_drvdata(dev); + struct brcmnand_host *host; + + list_for_each_entry(host, &ctrl->host_list, node) + brcmnand_save_restore_cs_config(host, 0); + + ctrl->nand_cs_nand_select = brcmnand_read_reg(ctrl, BRCMNAND_CS_SELECT); + ctrl->nand_cs_nand_xor = brcmnand_read_reg(ctrl, BRCMNAND_CS_XOR); + ctrl->corr_stat_threshold = + brcmnand_read_reg(ctrl, BRCMNAND_CORR_THRESHOLD); + + if (has_flash_dma(ctrl)) + ctrl->flash_dma_mode = flash_dma_readl(ctrl, FLASH_DMA_MODE); + + return 0; +} + +static int brcmnand_resume(struct device *dev) +{ + struct brcmnand_controller *ctrl = dev_get_drvdata(dev); + struct brcmnand_host *host; + + if (has_flash_dma(ctrl)) { + flash_dma_writel(ctrl, FLASH_DMA_MODE, ctrl->flash_dma_mode); + flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0); + } + + brcmnand_write_reg(ctrl, BRCMNAND_CS_SELECT, ctrl->nand_cs_nand_select); + brcmnand_write_reg(ctrl, BRCMNAND_CS_XOR, ctrl->nand_cs_nand_xor); + brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD, + ctrl->corr_stat_threshold); + if (ctrl->soc) { + /* Clear/re-enable interrupt */ + ctrl->soc->ctlrdy_ack(ctrl->soc); + ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true); + } + + list_for_each_entry(host, &ctrl->host_list, node) { + struct nand_chip *chip = &host->chip; + + brcmnand_save_restore_cs_config(host, 1); + + /* Reset the chip, required by some chips after power-up */ + nand_reset_op(chip); + } + + return 0; +} + +const struct dev_pm_ops brcmnand_pm_ops = { + .suspend = brcmnand_suspend, + .resume = brcmnand_resume, +}; +EXPORT_SYMBOL_GPL(brcmnand_pm_ops); + +static const struct of_device_id brcmnand_of_match[] = { + { .compatible = "brcm,brcmnand-v4.0" }, + { .compatible = "brcm,brcmnand-v5.0" }, + { .compatible = "brcm,brcmnand-v6.0" }, + { .compatible = "brcm,brcmnand-v6.1" }, + { .compatible = "brcm,brcmnand-v6.2" }, + { .compatible = "brcm,brcmnand-v7.0" }, + { .compatible = "brcm,brcmnand-v7.1" }, + { .compatible = "brcm,brcmnand-v7.2" }, + {}, +}; +MODULE_DEVICE_TABLE(of, brcmnand_of_match); +#endif /* __UBOOT__ */ + +/*********************************************************************** + * Platform driver setup (per controller) + ***********************************************************************/ + +#ifndef __UBOOT__ +int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) +#else +int brcmnand_probe(struct udevice *dev, struct brcmnand_soc *soc) +#endif /* __UBOOT__ */ +{ +#ifndef __UBOOT__ + struct device *dev = &pdev->dev; + struct device_node *dn = dev->of_node, *child; +#else + ofnode child; + struct udevice *pdev = dev; +#endif /* __UBOOT__ */ + struct brcmnand_controller *ctrl; +#ifndef __UBOOT__ + struct resource *res; +#else + struct resource res; +#endif /* __UBOOT__ */ + int ret; + +#ifndef __UBOOT__ + /* We only support device-tree instantiation */ + if (!dn) + return -ENODEV; + + if (!of_match_node(brcmnand_of_match, dn)) + return -ENODEV; +#endif /* __UBOOT__ */ + + ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + +#ifndef __UBOOT__ + dev_set_drvdata(dev, ctrl); +#else + /* + * in u-boot, the data for the driver is allocated before probing + * so to keep the reference to ctrl, we store it in the variable soc + */ + soc->ctrl = ctrl; +#endif /* __UBOOT__ */ + ctrl->dev = dev; + + init_completion(&ctrl->done); + init_completion(&ctrl->dma_done); + nand_hw_control_init(&ctrl->controller); + INIT_LIST_HEAD(&ctrl->host_list); + + /* Is parameter page in big endian ? */ + ctrl->parameter_page_big_endian = + dev_read_u32_default(dev, "parameter-page-big-endian", 1); + + /* NAND register range */ +#ifndef __UBOOT__ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ctrl->nand_base = devm_ioremap_resource(dev, res); +#else + dev_read_resource(pdev, 0, &res); + ctrl->nand_base = devm_ioremap(pdev, res.start, resource_size(&res)); +#endif + if (IS_ERR(ctrl->nand_base)) + return PTR_ERR(ctrl->nand_base); + + /* Enable clock before using NAND registers */ + ctrl->clk = devm_clk_get(dev, "nand"); + if (!IS_ERR(ctrl->clk)) { + ret = clk_prepare_enable(ctrl->clk); + if (ret) + return ret; + } else { + ret = PTR_ERR(ctrl->clk); + if (ret == -EPROBE_DEFER) + return ret; + + ctrl->clk = NULL; + } + + /* Initialize NAND revision */ + ret = brcmnand_revision_init(ctrl); + if (ret) + goto err; + + /* + * Most chips have this cache at a fixed offset within 'nand' block. + * Some must specify this region separately. + */ +#ifndef __UBOOT__ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-cache"); + if (res) { + ctrl->nand_fc = devm_ioremap_resource(dev, res); + if (IS_ERR(ctrl->nand_fc)) { + ret = PTR_ERR(ctrl->nand_fc); + goto err; + } + } else { + ctrl->nand_fc = ctrl->nand_base + + ctrl->reg_offsets[BRCMNAND_FC_BASE]; + } +#else + if (!dev_read_resource_byname(pdev, "nand-cache", &res)) { + ctrl->nand_fc = devm_ioremap(dev, res.start, + resource_size(&res)); + if (IS_ERR(ctrl->nand_fc)) { + ret = PTR_ERR(ctrl->nand_fc); + goto err; + } + } else { + ctrl->nand_fc = ctrl->nand_base + + ctrl->reg_offsets[BRCMNAND_FC_BASE]; + } +#endif + +#ifndef __UBOOT__ + /* FLASH_DMA */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-dma"); + if (res) { + ctrl->flash_dma_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctrl->flash_dma_base)) { + ret = PTR_ERR(ctrl->flash_dma_base); + goto err; + } + + flash_dma_writel(ctrl, FLASH_DMA_MODE, 1); /* linked-list */ + flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0); + + /* Allocate descriptor(s) */ + ctrl->dma_desc = dmam_alloc_coherent(dev, + sizeof(*ctrl->dma_desc), + &ctrl->dma_pa, GFP_KERNEL); + if (!ctrl->dma_desc) { + ret = -ENOMEM; + goto err; + } + + ctrl->dma_irq = platform_get_irq(pdev, 1); + if ((int)ctrl->dma_irq < 0) { + dev_err(dev, "missing FLASH_DMA IRQ\n"); + ret = -ENODEV; + goto err; + } + + ret = devm_request_irq(dev, ctrl->dma_irq, + brcmnand_dma_irq, 0, DRV_NAME, + ctrl); + if (ret < 0) { + dev_err(dev, "can't allocate IRQ %d: error %d\n", + ctrl->dma_irq, ret); + goto err; + } + + dev_info(dev, "enabling FLASH_DMA\n"); + } +#endif /* __UBOOT__ */ + + /* Disable automatic device ID config, direct addressing */ + brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, + CS_SELECT_AUTO_DEVICE_ID_CFG | 0xff, 0, 0); + /* Disable XOR addressing */ + brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0); + + /* Read the write-protect configuration in the device tree */ + wp_on = dev_read_u32_default(dev, "write-protect", wp_on); + + if (ctrl->features & BRCMNAND_HAS_WP) { + /* Permanently disable write protection */ + if (wp_on == 2) + brcmnand_set_wp(ctrl, false); + } else { + wp_on = 0; + } + +#ifndef __UBOOT__ + /* IRQ */ + ctrl->irq = platform_get_irq(pdev, 0); + if ((int)ctrl->irq < 0) { + dev_err(dev, "no IRQ defined\n"); + ret = -ENODEV; + goto err; + } + + /* + * Some SoCs integrate this controller (e.g., its interrupt bits) in + * interesting ways + */ + if (soc) { + ctrl->soc = soc; + + ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0, + DRV_NAME, ctrl); + + /* Enable interrupt */ + ctrl->soc->ctlrdy_ack(ctrl->soc); + ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true); + } else { + /* Use standard interrupt infrastructure */ + ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0, + DRV_NAME, ctrl); + } + if (ret < 0) { + dev_err(dev, "can't allocate IRQ %d: error %d\n", + ctrl->irq, ret); + goto err; + } +#endif /* __UBOOT__ */ + +#ifndef __UBOOT__ + for_each_available_child_of_node(dn, child) { + if (of_device_is_compatible(child, "brcm,nandcs")) { + struct brcmnand_host *host; + + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) { + of_node_put(child); + ret = -ENOMEM; + goto err; + } + host->pdev = pdev; + host->ctrl = ctrl; + + ret = brcmnand_init_cs(host, child); + if (ret) { + devm_kfree(dev, host); + continue; /* Try all chip-selects */ + } + + list_add_tail(&host->node, &ctrl->host_list); + } + } +#else + ofnode_for_each_subnode(child, dev_ofnode(dev)) { + if (ofnode_device_is_compatible(child, "brcm,nandcs")) { + struct brcmnand_host *host; + + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) { + ret = -ENOMEM; + goto err; + } + host->pdev = pdev; + host->ctrl = ctrl; + + ret = brcmnand_init_cs(host, child); + if (ret) { + devm_kfree(dev, host); + continue; /* Try all chip-selects */ + } + + list_add_tail(&host->node, &ctrl->host_list); + } + } +#endif /* __UBOOT__ */ + +err: +#ifndef __UBOOT__ + clk_disable_unprepare(ctrl->clk); +#else + if (ctrl->clk) + clk_disable(ctrl->clk); +#endif /* __UBOOT__ */ + return ret; + +} +EXPORT_SYMBOL_GPL(brcmnand_probe); + +#ifndef __UBOOT__ +int brcmnand_remove(struct platform_device *pdev) +{ + struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev); + struct brcmnand_host *host; + + list_for_each_entry(host, &ctrl->host_list, node) + nand_release(nand_to_mtd(&host->chip)); + + clk_disable_unprepare(ctrl->clk); + + dev_set_drvdata(&pdev->dev, NULL); + + return 0; +} +#else +int brcmnand_remove(struct udevice *pdev) +{ + return 0; +} +#endif /* __UBOOT__ */ +EXPORT_SYMBOL_GPL(brcmnand_remove); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Kevin Cernekee"); +MODULE_AUTHOR("Brian Norris"); +MODULE_DESCRIPTION("NAND driver for Broadcom chips"); +MODULE_ALIAS("platform:brcmnand"); diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.h b/drivers/mtd/nand/raw/brcmnand/brcmnand.h new file mode 100644 index 0000000..6946a62 --- /dev/null +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __BRCMNAND_H__ +#define __BRCMNAND_H__ + +#include <linux/types.h> +#include <linux/io.h> + +struct brcmnand_soc { + bool (*ctlrdy_ack)(struct brcmnand_soc *soc); + void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en); + void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare, + bool is_param); + void *ctrl; +}; + +static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc, + bool is_param) +{ + if (soc && soc->prepare_data_bus) + soc->prepare_data_bus(soc, true, is_param); +} + +static inline void brcmnand_soc_data_bus_unprepare(struct brcmnand_soc *soc, + bool is_param) +{ + if (soc && soc->prepare_data_bus) + soc->prepare_data_bus(soc, false, is_param); +} + +static inline u32 brcmnand_readl(void __iomem *addr) +{ + /* + * MIPS endianness is configured by boot strap, which also reverses all + * bus endianness (i.e., big-endian CPU + big endian bus ==> native + * endian I/O). + * + * Other architectures (e.g., ARM) either do not support big endian, or + * else leave I/O in little endian mode. + */ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_SYS_BIG_ENDIAN)) + return __raw_readl(addr); + else + return readl_relaxed(addr); +} + +static inline void brcmnand_writel(u32 val, void __iomem *addr) +{ + /* See brcmnand_readl() comments */ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_SYS_BIG_ENDIAN)) + __raw_writel(val, addr); + else + writel_relaxed(val, addr); +} + +int brcmnand_probe(struct udevice *dev, struct brcmnand_soc *soc); +int brcmnand_remove(struct udevice *dev); + +#ifndef __UBOOT__ +extern const struct dev_pm_ops brcmnand_pm_ops; +#endif /* __UBOOT__ */ + +#endif /* __BRCMNAND_H__ */ diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand_compat.c b/drivers/mtd/nand/raw/brcmnand/brcmnand_compat.c new file mode 100644 index 0000000..96b27e6 --- /dev/null +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand_compat.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <common.h> +#include "brcmnand_compat.h" + +struct clk *devm_clk_get(struct udevice *dev, const char *id) +{ + struct clk *clk; + int ret; + + clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL); + if (!clk) { + debug("%s: can't allocate clock\n", __func__); + return ERR_PTR(-ENOMEM); + } + + ret = clk_get_by_name(dev, id, clk); + if (ret < 0) { + debug("%s: can't get clock (ret = %d)!\n", __func__, ret); + return ERR_PTR(ret); + } + + return clk; +} + +int clk_prepare_enable(struct clk *clk) +{ + return clk_enable(clk); +} + +void clk_disable_unprepare(struct clk *clk) +{ + clk_disable(clk); +} + +static char *devm_kvasprintf(struct udevice *dev, gfp_t gfp, const char *fmt, + va_list ap) +{ + unsigned int len; + char *p; + va_list aq; + + va_copy(aq, ap); + len = vsnprintf(NULL, 0, fmt, aq); + va_end(aq); + + p = devm_kmalloc(dev, len + 1, gfp); + if (!p) + return NULL; + + vsnprintf(p, len + 1, fmt, ap); + + return p; +} + +char *devm_kasprintf(struct udevice *dev, gfp_t gfp, const char *fmt, ...) +{ + va_list ap; + char *p; + + va_start(ap, fmt); + p = devm_kvasprintf(dev, gfp, fmt, ap); + va_end(ap); + + return p; +} diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand_compat.h b/drivers/mtd/nand/raw/brcmnand/brcmnand_compat.h new file mode 100644 index 0000000..02cab0f --- /dev/null +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand_compat.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __BRCMNAND_COMPAT_H +#define __BRCMNAND_COMPAT_H + +#include <clk.h> +#include <dm.h> + +struct clk *devm_clk_get(struct udevice *dev, const char *id); +int clk_prepare_enable(struct clk *clk); +void clk_disable_unprepare(struct clk *clk); + +char *devm_kasprintf(struct udevice *dev, gfp_t gfp, const char *fmt, ...); + +#endif /* __BRCMNAND_COMPAT_H */ diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 6d2ff58..e07bd6b 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -486,14 +486,19 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) static int nand_check_wp(struct mtd_info *mtd) { struct nand_chip *chip = mtd_to_nand(mtd); + u8 status; + int ret; /* Broken xD cards report WP despite being writable */ if (chip->options & NAND_BROKEN_XD) return 0; /* Check the WP bit */ - chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); - return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; + ret = nand_status_op(chip, &status); + if (ret) + return ret; + + return status & NAND_STATUS_WP ? 0 : 1; } /** @@ -575,11 +580,18 @@ static void nand_wait_status_ready(struct mtd_info *mtd, unsigned long timeo) { register struct nand_chip *chip = mtd_to_nand(mtd); u32 time_start; + int ret; timeo = (CONFIG_SYS_HZ * timeo) / 1000; time_start = get_timer(0); while (get_timer(time_start) < timeo) { - if ((chip->read_byte(mtd) & NAND_STATUS_READY)) + u8 status; + + ret = nand_read_data_op(chip, &status, sizeof(status), true); + if (ret) + return; + + if (status & NAND_STATUS_READY) break; WATCHDOG_RESET(); } @@ -851,7 +863,15 @@ static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip, if (chip->dev_ready(mtd)) break; } else { - if (chip->read_byte(mtd) & NAND_STATUS_READY) + int ret; + u8 status; + + ret = nand_read_data_op(chip, &status, sizeof(status), + true); + if (ret) + return; + + if (status & NAND_STATUS_READY) break; } mdelay(1); @@ -867,8 +887,9 @@ static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip, */ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) { - int status; unsigned long timeo = 400; + u8 status; + int ret; led_trigger_event(nand_led_trigger, LED_FULL); @@ -878,7 +899,9 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) */ ndelay(100); - chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + ret = nand_status_op(chip, NULL); + if (ret) + return ret; u32 timer = (CONFIG_SYS_HZ * timeo) / 1000; u32 time_start; @@ -889,13 +912,21 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) if (chip->dev_ready(mtd)) break; } else { - if (chip->read_byte(mtd) & NAND_STATUS_READY) + ret = nand_read_data_op(chip, &status, + sizeof(status), true); + if (ret) + return ret; + + if (status & NAND_STATUS_READY) break; } } led_trigger_event(nand_led_trigger, LED_OFF); - status = (int)chip->read_byte(mtd); + ret = nand_read_data_op(chip, &status, sizeof(status), true); + if (ret) + return ret; + /* This can happen if in case of timeout or buggy dev_ready */ WARN_ON(!(status & NAND_STATUS_READY)); return status; @@ -1048,6 +1079,516 @@ static void __maybe_unused nand_release_data_interface(struct nand_chip *chip) } /** + * nand_read_page_op - Do a READ PAGE operation + * @chip: The NAND chip + * @page: page to read + * @offset_in_page: offset within the page + * @buf: buffer used to store the data + * @len: length of the buffer + * + * This function issues a READ PAGE operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_read_page_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, void *buf, unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (len && !buf) + return -EINVAL; + + if (offset_in_page + len > mtd->writesize + mtd->oobsize) + return -EINVAL; + + chip->cmdfunc(mtd, NAND_CMD_READ0, offset_in_page, page); + if (len) + chip->read_buf(mtd, buf, len); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_read_page_op); + +/** + * nand_read_param_page_op - Do a READ PARAMETER PAGE operation + * @chip: The NAND chip + * @page: parameter page to read + * @buf: buffer used to store the data + * @len: length of the buffer + * + * This function issues a READ PARAMETER PAGE operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +static int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf, + unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + unsigned int i; + u8 *p = buf; + + if (len && !buf) + return -EINVAL; + + chip->cmdfunc(mtd, NAND_CMD_PARAM, page, -1); + for (i = 0; i < len; i++) + p[i] = chip->read_byte(mtd); + + return 0; +} + +/** + * nand_change_read_column_op - Do a CHANGE READ COLUMN operation + * @chip: The NAND chip + * @offset_in_page: offset within the page + * @buf: buffer used to store the data + * @len: length of the buffer + * @force_8bit: force 8-bit bus access + * + * This function issues a CHANGE READ COLUMN operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_change_read_column_op(struct nand_chip *chip, + unsigned int offset_in_page, void *buf, + unsigned int len, bool force_8bit) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (len && !buf) + return -EINVAL; + + if (offset_in_page + len > mtd->writesize + mtd->oobsize) + return -EINVAL; + + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset_in_page, -1); + if (len) + chip->read_buf(mtd, buf, len); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_change_read_column_op); + +/** + * nand_read_oob_op - Do a READ OOB operation + * @chip: The NAND chip + * @page: page to read + * @offset_in_oob: offset within the OOB area + * @buf: buffer used to store the data + * @len: length of the buffer + * + * This function issues a READ OOB operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_read_oob_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_oob, void *buf, unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (len && !buf) + return -EINVAL; + + if (offset_in_oob + len > mtd->oobsize) + return -EINVAL; + + chip->cmdfunc(mtd, NAND_CMD_READOOB, offset_in_oob, page); + if (len) + chip->read_buf(mtd, buf, len); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_read_oob_op); + +/** + * nand_prog_page_begin_op - starts a PROG PAGE operation + * @chip: The NAND chip + * @page: page to write + * @offset_in_page: offset within the page + * @buf: buffer containing the data to write to the page + * @len: length of the buffer + * + * This function issues the first half of a PROG PAGE operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, const void *buf, + unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (len && !buf) + return -EINVAL; + + if (offset_in_page + len > mtd->writesize + mtd->oobsize) + return -EINVAL; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, offset_in_page, page); + + if (buf) + chip->write_buf(mtd, buf, len); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_prog_page_begin_op); + +/** + * nand_prog_page_end_op - ends a PROG PAGE operation + * @chip: The NAND chip + * + * This function issues the second half of a PROG PAGE operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_prog_page_end_op(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int status; + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = chip->waitfunc(mtd, chip); + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} +EXPORT_SYMBOL_GPL(nand_prog_page_end_op); + +/** + * nand_prog_page_op - Do a full PROG PAGE operation + * @chip: The NAND chip + * @page: page to write + * @offset_in_page: offset within the page + * @buf: buffer containing the data to write to the page + * @len: length of the buffer + * + * This function issues a full PROG PAGE operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_prog_page_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, const void *buf, + unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int status; + + if (!len || !buf) + return -EINVAL; + + if (offset_in_page + len > mtd->writesize + mtd->oobsize) + return -EINVAL; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, offset_in_page, page); + chip->write_buf(mtd, buf, len); + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = chip->waitfunc(mtd, chip); + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} +EXPORT_SYMBOL_GPL(nand_prog_page_op); + +/** + * nand_change_write_column_op - Do a CHANGE WRITE COLUMN operation + * @chip: The NAND chip + * @offset_in_page: offset within the page + * @buf: buffer containing the data to send to the NAND + * @len: length of the buffer + * @force_8bit: force 8-bit bus access + * + * This function issues a CHANGE WRITE COLUMN operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_change_write_column_op(struct nand_chip *chip, + unsigned int offset_in_page, + const void *buf, unsigned int len, + bool force_8bit) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (len && !buf) + return -EINVAL; + + if (offset_in_page + len > mtd->writesize + mtd->oobsize) + return -EINVAL; + + chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset_in_page, -1); + if (len) + chip->write_buf(mtd, buf, len); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_change_write_column_op); + +/** + * nand_readid_op - Do a READID operation + * @chip: The NAND chip + * @addr: address cycle to pass after the READID command + * @buf: buffer used to store the ID + * @len: length of the buffer + * + * This function sends a READID command and reads back the ID returned by the + * NAND. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf, + unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + unsigned int i; + u8 *id = buf; + + if (len && !buf) + return -EINVAL; + + chip->cmdfunc(mtd, NAND_CMD_READID, addr, -1); + + for (i = 0; i < len; i++) + id[i] = chip->read_byte(mtd); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_readid_op); + +/** + * nand_status_op - Do a STATUS operation + * @chip: The NAND chip + * @status: out variable to store the NAND status + * + * This function sends a STATUS command and reads back the status returned by + * the NAND. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_status_op(struct nand_chip *chip, u8 *status) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + if (status) + *status = chip->read_byte(mtd); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_status_op); + +/** + * nand_exit_status_op - Exit a STATUS operation + * @chip: The NAND chip + * + * This function sends a READ0 command to cancel the effect of the STATUS + * command to avoid reading only the status until a new read command is sent. + * + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_exit_status_op(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + chip->cmdfunc(mtd, NAND_CMD_READ0, -1, -1); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_exit_status_op); + +/** + * nand_erase_op - Do an erase operation + * @chip: The NAND chip + * @eraseblock: block to erase + * + * This function sends an ERASE command and waits for the NAND to be ready + * before returning. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + unsigned int page = eraseblock << + (chip->phys_erase_shift - chip->page_shift); + int status; + + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); + chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); + + status = chip->waitfunc(mtd, chip); + if (status < 0) + return status; + + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} +EXPORT_SYMBOL_GPL(nand_erase_op); + +/** + * nand_set_features_op - Do a SET FEATURES operation + * @chip: The NAND chip + * @feature: feature id + * @data: 4 bytes of data + * + * This function sends a SET FEATURES command and waits for the NAND to be + * ready before returning. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +static int nand_set_features_op(struct nand_chip *chip, u8 feature, + const void *data) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + const u8 *params = data; + int i, status; + + chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, feature, -1); + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) + chip->write_byte(mtd, params[i]); + + status = chip->waitfunc(mtd, chip); + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} + +/** + * nand_get_features_op - Do a GET FEATURES operation + * @chip: The NAND chip + * @feature: feature id + * @data: 4 bytes of data + * + * This function sends a GET FEATURES command and waits for the NAND to be + * ready before returning. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +static int nand_get_features_op(struct nand_chip *chip, u8 feature, + void *data) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + u8 *params = data; + int i; + + chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, feature, -1); + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) + params[i] = chip->read_byte(mtd); + + return 0; +} + +/** + * nand_reset_op - Do a reset operation + * @chip: The NAND chip + * + * This function sends a RESET command and waits for the NAND to be ready + * before returning. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_reset_op(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_reset_op); + +/** + * nand_read_data_op - Read data from the NAND + * @chip: The NAND chip + * @buf: buffer used to store the data + * @len: length of the buffer + * @force_8bit: force 8-bit bus access + * + * This function does a raw data read on the bus. Usually used after launching + * another NAND operation like nand_read_page_op(). + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len, + bool force_8bit) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (!len || !buf) + return -EINVAL; + + if (force_8bit) { + u8 *p = buf; + unsigned int i; + + for (i = 0; i < len; i++) + p[i] = chip->read_byte(mtd); + } else { + chip->read_buf(mtd, buf, len); + } + + return 0; +} +EXPORT_SYMBOL_GPL(nand_read_data_op); + +/** + * nand_write_data_op - Write data from the NAND + * @chip: The NAND chip + * @buf: buffer containing the data to send on the bus + * @len: length of the buffer + * @force_8bit: force 8-bit bus access + * + * This function does a raw data write on the bus. Usually used after launching + * another NAND operation like nand_write_page_begin_op(). + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_write_data_op(struct nand_chip *chip, const void *buf, + unsigned int len, bool force_8bit) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (!len || !buf) + return -EINVAL; + + if (force_8bit) { + const u8 *p = buf; + unsigned int i; + + for (i = 0; i < len; i++) + chip->write_byte(mtd, p[i]); + } else { + chip->write_buf(mtd, buf, len); + } + + return 0; +} +EXPORT_SYMBOL_GPL(nand_write_data_op); + +/** * nand_reset - Reset and initialize a NAND device * @chip: The NAND chip * @chipnr: Internal die id @@ -1068,8 +1609,10 @@ int nand_reset(struct nand_chip *chip, int chipnr) * interface settings, hence this weird ->select_chip() dance. */ chip->select_chip(mtd, chipnr); - chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + ret = nand_reset_op(chip); chip->select_chip(mtd, -1); + if (ret) + return ret; chip->select_chip(mtd, chipnr); ret = nand_setup_data_interface(chip, chipnr); @@ -1220,9 +1763,19 @@ EXPORT_SYMBOL(nand_check_erased_ecc_chunk); 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); + int ret; + + ret = nand_read_data_op(chip, buf, mtd->writesize, false); + if (ret) + return ret; + + if (oob_required) { + ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, + false); + if (ret) + return ret; + } + return 0; } @@ -1243,29 +1796,46 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; uint8_t *oob = chip->oob_poi; - int steps, size; + int steps, size, ret; for (steps = chip->ecc.steps; steps > 0; steps--) { - chip->read_buf(mtd, buf, eccsize); + ret = nand_read_data_op(chip, buf, eccsize, false); + if (ret) + return ret; + buf += eccsize; if (chip->ecc.prepad) { - chip->read_buf(mtd, oob, chip->ecc.prepad); + ret = nand_read_data_op(chip, oob, chip->ecc.prepad, + false); + if (ret) + return ret; + oob += chip->ecc.prepad; } - chip->read_buf(mtd, oob, eccbytes); + ret = nand_read_data_op(chip, oob, eccbytes, false); + if (ret) + return ret; + oob += eccbytes; if (chip->ecc.postpad) { - chip->read_buf(mtd, oob, chip->ecc.postpad); + ret = nand_read_data_op(chip, oob, chip->ecc.postpad, + false); + if (ret) + return ret; + oob += chip->ecc.postpad; } } size = mtd->oobsize - (oob - chip->oob_poi); - if (size) - chip->read_buf(mtd, oob, size); + if (size) { + ret = nand_read_data_op(chip, oob, size, false); + if (ret) + return ret; + } return 0; } @@ -1336,6 +1906,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; int index; unsigned int max_bitflips = 0; + int ret; /* Column address within the page aligned to ECC size (256bytes) */ start_step = data_offs / chip->ecc.size; @@ -1353,7 +1924,9 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1); p = bufpoi + data_col_addr; - chip->read_buf(mtd, p, datafrag_len); + ret = nand_read_data_op(chip, p, datafrag_len, false); + if (ret) + return ret; /* Calculate ECC */ for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) @@ -1370,8 +1943,11 @@ 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); + ret = nand_change_read_column_op(chip, mtd->writesize, + chip->oob_poi, mtd->oobsize, + false); + if (ret) + return ret; } else { /* * Send the command to read the particular ECC bytes take care @@ -1384,9 +1960,12 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, if (eccpos[index + (num_steps * chip->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); + ret = nand_change_read_column_op(chip, + mtd->writesize + aligned_pos, + &chip->oob_poi[aligned_pos], + aligned_len, false); + if (ret) + return ret; } for (i = 0; i < eccfrag_len; i++) @@ -1439,13 +2018,21 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; unsigned int max_bitflips = 0; + int ret; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); + + ret = nand_read_data_op(chip, p, eccsize, false); + if (ret) + return ret; + chip->ecc.calculate(mtd, p, &ecc_calc[i]); } - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, false); + if (ret) + return ret; for (i = 0; i < chip->ecc.total; i++) ecc_code[i] = chip->oob_poi[eccpos[i]]; @@ -1501,11 +2088,16 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, uint32_t *eccpos = chip->ecc.layout->eccpos; uint8_t *ecc_calc = chip->buffers->ecccalc; unsigned int max_bitflips = 0; + int ret; /* Read the OOB area first */ - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + ret = nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); + if (ret) + return ret; + + ret = nand_read_page_op(chip, page, 0, NULL, 0); + if (ret) + return ret; for (i = 0; i < chip->ecc.total; i++) ecc_code[i] = chip->oob_poi[eccpos[i]]; @@ -1514,7 +2106,11 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, int stat; chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); + + ret = nand_read_data_op(chip, p, eccsize, false); + if (ret) + return ret; + chip->ecc.calculate(mtd, p, &ecc_calc[i]); stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); @@ -1551,7 +2147,7 @@ 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 ret, i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; int eccpadbytes = eccbytes + chip->ecc.prepad + chip->ecc.postpad; @@ -1563,21 +2159,36 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int stat; chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); + + ret = nand_read_data_op(chip, p, eccsize, false); + if (ret) + return ret; if (chip->ecc.prepad) { - chip->read_buf(mtd, oob, chip->ecc.prepad); + ret = nand_read_data_op(chip, oob, chip->ecc.prepad, + false); + if (ret) + return ret; + oob += chip->ecc.prepad; } chip->ecc.hwctl(mtd, NAND_ECC_READSYN); - chip->read_buf(mtd, oob, eccbytes); + + ret = nand_read_data_op(chip, oob, eccbytes, false); + if (ret) + return ret; + stat = chip->ecc.correct(mtd, p, oob, NULL); oob += eccbytes; if (chip->ecc.postpad) { - chip->read_buf(mtd, oob, chip->ecc.postpad); + ret = nand_read_data_op(chip, oob, chip->ecc.postpad, + false); + if (ret) + return ret; + oob += chip->ecc.postpad; } @@ -1601,8 +2212,11 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, /* Calculate remaining oob bytes */ i = mtd->oobsize - (oob - chip->oob_poi); - if (i) - chip->read_buf(mtd, oob, i); + if (i) { + ret = nand_read_data_op(chip, oob, i, false); + if (ret) + return ret; + } return max_bitflips; } @@ -1739,8 +2353,11 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, __func__, buf); read_retry: - if (nand_standard_page_accessors(&chip->ecc)) - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + if (nand_standard_page_accessors(&chip->ecc)) { + ret = nand_read_page_op(chip, page, 0, NULL, 0); + if (ret) + break; + } /* * Now read the page into the buffer. Absent an error, @@ -1874,9 +2491,7 @@ read_retry: 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); - return 0; + return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); } /** @@ -1893,25 +2508,43 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; int eccsize = chip->ecc.size; uint8_t *bufpoi = chip->oob_poi; - int i, toread, sndrnd = 0, pos; + int i, toread, sndrnd = 0, pos, ret; + + ret = nand_read_page_op(chip, page, chip->ecc.size, NULL, 0); + if (ret) + return ret; - chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page); for (i = 0; i < chip->ecc.steps; i++) { if (sndrnd) { + int ret; + pos = eccsize + i * (eccsize + chunk); if (mtd->writesize > 512) - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1); + ret = nand_change_read_column_op(chip, pos, + NULL, 0, + false); else - chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page); + ret = nand_read_page_op(chip, page, pos, NULL, + 0); + + if (ret) + return ret; } else sndrnd = 1; toread = min_t(int, length, chunk); - chip->read_buf(mtd, bufpoi, toread); + + ret = nand_read_data_op(chip, bufpoi, toread, false); + if (ret) + return ret; + bufpoi += toread; length -= toread; } - if (length > 0) - chip->read_buf(mtd, bufpoi, length); + if (length > 0) { + ret = nand_read_data_op(chip, bufpoi, length, false); + if (ret) + return ret; + } return 0; } @@ -1925,18 +2558,8 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) { - int status = 0; - const uint8_t *buf = chip->oob_poi; - int length = mtd->oobsize; - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); - chip->write_buf(mtd, buf, length); - /* Send command to program the OOB data */ - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - - status = chip->waitfunc(mtd, chip); - - return status & NAND_STATUS_FAIL ? -EIO : 0; + return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi, + mtd->oobsize); } /** @@ -1951,7 +2574,7 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd, { 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 ret, i, len, pos, sndcmd = 0, steps = chip->ecc.steps; const uint8_t *bufpoi = chip->oob_poi; /* @@ -1965,7 +2588,10 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd, } else pos = eccsize; - chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page); + ret = nand_prog_page_begin_op(chip, page, pos, NULL, 0); + if (ret) + return ret; + for (i = 0; i < steps; i++) { if (sndcmd) { if (mtd->writesize <= 512) { @@ -1974,28 +2600,40 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd, len = eccsize; while (len > 0) { int num = min_t(int, len, 4); - chip->write_buf(mtd, (uint8_t *)&fill, - num); + + ret = nand_write_data_op(chip, &fill, + num, false); + if (ret) + return ret; + len -= num; } } else { pos = eccsize + i * (eccsize + chunk); - chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1); + ret = nand_change_write_column_op(chip, pos, + NULL, 0, + false); + if (ret) + return ret; } } else sndcmd = 1; len = min_t(int, length, chunk); - chip->write_buf(mtd, bufpoi, len); + + ret = nand_write_data_op(chip, bufpoi, len, false); + if (ret) + return ret; + bufpoi += len; length -= len; } - if (length > 0) - chip->write_buf(mtd, bufpoi, length); - - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); + if (length > 0) { + ret = nand_write_data_op(chip, bufpoi, length, false); + if (ret) + return ret; + } - return status & NAND_STATUS_FAIL ? -EIO : 0; + return nand_prog_page_end_op(chip); } /** @@ -2154,9 +2792,18 @@ out: static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) { - chip->write_buf(mtd, buf, mtd->writesize); - if (oob_required) - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + int ret; + + ret = nand_write_data_op(chip, buf, mtd->writesize, false); + if (ret) + return ret; + + if (oob_required) { + ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, + false); + if (ret) + return ret; + } return 0; } @@ -2179,29 +2826,46 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd, int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; uint8_t *oob = chip->oob_poi; - int steps, size; + int steps, size, ret; for (steps = chip->ecc.steps; steps > 0; steps--) { - chip->write_buf(mtd, buf, eccsize); + ret = nand_write_data_op(chip, buf, eccsize, false); + if (ret) + return ret; + buf += eccsize; if (chip->ecc.prepad) { - chip->write_buf(mtd, oob, chip->ecc.prepad); + ret = nand_write_data_op(chip, oob, chip->ecc.prepad, + false); + if (ret) + return ret; + oob += chip->ecc.prepad; } - chip->write_buf(mtd, oob, eccbytes); + ret = nand_write_data_op(chip, oob, eccbytes, false); + if (ret) + return ret; + oob += eccbytes; if (chip->ecc.postpad) { - chip->write_buf(mtd, oob, chip->ecc.postpad); + ret = nand_write_data_op(chip, oob, chip->ecc.postpad, + false); + if (ret) + return ret; + oob += chip->ecc.postpad; } } size = mtd->oobsize - (oob - chip->oob_poi); - if (size) - chip->write_buf(mtd, oob, size); + if (size) { + ret = nand_write_data_op(chip, oob, size, false); + if (ret) + return ret; + } return 0; } @@ -2252,17 +2916,24 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_calc = chip->buffers->ecccalc; const uint8_t *p = buf; uint32_t *eccpos = chip->ecc.layout->eccpos; + int ret; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - chip->write_buf(mtd, p, eccsize); + + ret = nand_write_data_op(chip, p, eccsize, false); + if (ret) + return ret; + chip->ecc.calculate(mtd, p, &ecc_calc[i]); } for (i = 0; i < chip->ecc.total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false); + if (ret) + return ret; return 0; } @@ -2293,13 +2964,16 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, uint32_t end_step = (offset + data_len - 1) / ecc_size; int oob_bytes = mtd->oobsize / ecc_steps; int step, i; + int ret; for (step = 0; step < ecc_steps; step++) { /* configure controller for WRITE access */ chip->ecc.hwctl(mtd, NAND_ECC_WRITE); /* write data (untouched subpages already masked by 0xFF) */ - chip->write_buf(mtd, buf, ecc_size); + ret = nand_write_data_op(chip, buf, ecc_size, false); + if (ret) + return ret; /* mask ECC of un-touched subpages by padding 0xFF */ if ((step < start_step) || (step > end_step)) @@ -2324,7 +2998,9 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, chip->oob_poi[eccpos[i]] = ecc_calc[i]; /* write OOB buffer to NAND device */ - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false); + if (ret) + return ret; return 0; } @@ -2351,31 +3027,49 @@ static int nand_write_page_syndrome(struct mtd_info *mtd, int eccsteps = chip->ecc.steps; const uint8_t *p = buf; uint8_t *oob = chip->oob_poi; + int ret; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - chip->write_buf(mtd, p, eccsize); + + ret = nand_write_data_op(chip, p, eccsize, false); + if (ret) + return ret; if (chip->ecc.prepad) { - chip->write_buf(mtd, oob, chip->ecc.prepad); + ret = nand_write_data_op(chip, oob, chip->ecc.prepad, + false); + if (ret) + return ret; + oob += chip->ecc.prepad; } chip->ecc.calculate(mtd, p, oob); - chip->write_buf(mtd, oob, eccbytes); + + ret = nand_write_data_op(chip, oob, eccbytes, false); + if (ret) + return ret; + oob += eccbytes; if (chip->ecc.postpad) { - chip->write_buf(mtd, oob, chip->ecc.postpad); + ret = nand_write_data_op(chip, oob, chip->ecc.postpad, + false); + if (ret) + return ret; + oob += chip->ecc.postpad; } } /* Calculate remaining oob bytes */ i = mtd->oobsize - (oob - chip->oob_poi); - if (i) - chip->write_buf(mtd, oob, i); + if (i) { + ret = nand_write_data_op(chip, oob, i, false); + if (ret) + return ret; + } return 0; } @@ -2403,8 +3097,11 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, else subpage = 0; - if (nand_standard_page_accessors(&chip->ecc)) - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + if (nand_standard_page_accessors(&chip->ecc)) { + status = nand_prog_page_begin_op(chip, page, 0, NULL, 0); + if (status) + return status; + } if (unlikely(raw)) status = chip->ecc.write_page_raw(mtd, chip, buf, @@ -2419,13 +3116,8 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, if (status < 0) return status; - if (nand_standard_page_accessors(&chip->ecc)) { - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - - status = chip->waitfunc(mtd, chip); - if (status & NAND_STATUS_FAIL) - return -EIO; - } + if (nand_standard_page_accessors(&chip->ecc)) + return nand_prog_page_end_op(chip); return 0; } @@ -2785,11 +3477,12 @@ out: static int single_erase(struct mtd_info *mtd, int page) { struct nand_chip *chip = mtd_to_nand(mtd); + unsigned int eraseblock; + /* Send commands to erase a block */ - chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); - chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); + eraseblock = page >> (chip->phys_erase_shift - chip->page_shift); - return chip->waitfunc(mtd, chip); + return nand_erase_op(chip, eraseblock); } /** @@ -2982,9 +3675,6 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, int addr, uint8_t *subfeature_param) { - int status; - int i; - #ifdef CONFIG_SYS_NAND_ONFI_DETECTION if (!chip->onfi_version || !(le16_to_cpu(chip->onfi_params.opt_cmd) @@ -2992,14 +3682,7 @@ static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, return -ENOTSUPP; #endif - chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1); - for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) - chip->write_byte(mtd, subfeature_param[i]); - - status = chip->waitfunc(mtd, chip); - if (status & NAND_STATUS_FAIL) - return -EIO; - return 0; + return nand_set_features_op(chip, addr, subfeature_param); } /** @@ -3012,8 +3695,6 @@ static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip, int addr, uint8_t *subfeature_param) { - int i; - #ifdef CONFIG_SYS_NAND_ONFI_DETECTION if (!chip->onfi_version || !(le16_to_cpu(chip->onfi_params.opt_cmd) @@ -3021,10 +3702,7 @@ static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip, return -ENOTSUPP; #endif - chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1); - for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) - *subfeature_param++ = chip->read_byte(mtd); - return 0; + return nand_get_features_op(chip, addr, subfeature_param); } /* Set default functions */ @@ -3118,7 +3796,7 @@ static int nand_flash_detect_ext_param_page(struct mtd_info *mtd, struct onfi_ext_section *s; struct onfi_ext_ecc_info *ecc; uint8_t *cursor; - int ret = -EINVAL; + int ret; int len; int i; @@ -3128,14 +3806,18 @@ static int nand_flash_detect_ext_param_page(struct mtd_info *mtd, return -ENOMEM; /* Send our own NAND_CMD_PARAM. */ - chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); + ret = nand_read_param_page_op(chip, 0, NULL, 0); + if (ret) + goto ext_out; /* Use the Change Read Column command to skip the ONFI param pages. */ - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, - sizeof(*p) * p->num_of_param_pages , -1); + ret = nand_change_read_column_op(chip, + sizeof(*p) * p->num_of_param_pages, + ep, len, true); + if (ret) + goto ext_out; - /* Read out the Extended Parameter Page. */ - chip->read_buf(mtd, (uint8_t *)ep, len); + ret = -EINVAL; if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2) != le16_to_cpu(ep->crc))) { pr_debug("fail in the CRC.\n"); @@ -3212,19 +3894,23 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, int *busw) { struct nand_onfi_params *p = &chip->onfi_params; - int i, j; - int val; + char id[4]; + int i, ret, val; /* Try ONFI for unknown chip or LP */ - chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1); - if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' || - chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I') + ret = nand_readid_op(chip, 0x20, id, sizeof(id)); + if (ret || strncmp(id, "ONFI", 4)) + return 0; + + ret = nand_read_param_page_op(chip, 0, NULL, 0); + if (ret) return 0; - chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); for (i = 0; i < 3; i++) { - for (j = 0; j < sizeof(*p); j++) - ((uint8_t *)p)[j] = chip->read_byte(mtd); + ret = nand_read_data_op(chip, p, sizeof(*p), true); + if (ret) + return 0; + if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == le16_to_cpu(p->crc)) { break; @@ -3324,20 +4010,22 @@ static int nand_flash_detect_jedec(struct mtd_info *mtd, struct nand_chip *chip, { struct nand_jedec_params *p = &chip->jedec_params; struct jedec_ecc_info *ecc; - int val; - int i, j; + char id[5]; + int i, val, ret; /* Try JEDEC for unknown chip or LP */ - chip->cmdfunc(mtd, NAND_CMD_READID, 0x40, -1); - if (chip->read_byte(mtd) != 'J' || chip->read_byte(mtd) != 'E' || - chip->read_byte(mtd) != 'D' || chip->read_byte(mtd) != 'E' || - chip->read_byte(mtd) != 'C') + ret = nand_readid_op(chip, 0x40, id, sizeof(id)); + if (ret || strncmp(id, "JEDEC", sizeof(id))) + return 0; + + ret = nand_read_param_page_op(chip, 0x40, NULL, 0); + if (ret) return 0; - chip->cmdfunc(mtd, NAND_CMD_PARAM, 0x40, -1); for (i = 0; i < 3; i++) { - for (j = 0; j < sizeof(*p); j++) - ((uint8_t *)p)[j] = chip->read_byte(mtd); + ret = nand_read_data_op(chip, p, sizeof(*p), true); + if (ret) + return 0; if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) == le16_to_cpu(p->crc)) @@ -3708,25 +4396,29 @@ struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, int *maf_id, int *dev_id, struct nand_flash_dev *type) { - int busw; - int i, maf_idx; + int busw, ret; + int maf_idx; u8 id_data[8]; /* * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx) * after power-up. */ - nand_reset(chip, 0); + ret = nand_reset(chip, 0); + if (ret) + return ERR_PTR(ret); /* Select the device */ chip->select_chip(mtd, 0); /* Send the command for reading device ID */ - chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + ret = nand_readid_op(chip, 0, id_data, 2); + if (ret) + return ERR_PTR(ret); /* Read manufacturer and device IDs */ - *maf_id = chip->read_byte(mtd); - *dev_id = chip->read_byte(mtd); + *maf_id = id_data[0]; + *dev_id = id_data[1]; /* * Try again to make sure, as some systems the bus-hold or other @@ -3735,11 +4427,10 @@ struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, * not match, ignore the device completely. */ - chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - /* Read entire ID string */ - for (i = 0; i < 8; i++) - id_data[i] = chip->read_byte(mtd); + ret = nand_readid_op(chip, 0, id_data, 8); + if (ret) + return ERR_PTR(ret); if (id_data[0] != *maf_id || id_data[1] != *dev_id) { pr_info("second ID read did not match %02x,%02x against %02x,%02x\n", @@ -3999,15 +4690,17 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, /* Check for a chip array */ for (i = 1; i < maxchips; i++) { + u8 id[2]; + /* See comment in nand_get_flash_type for reset */ nand_reset(chip, i); chip->select_chip(mtd, i); /* Send the command for reading device ID */ - chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + nand_readid_op(chip, 0, id, sizeof(id)); + /* Read manufacturer and device IDs */ - if (nand_maf_id != chip->read_byte(mtd) || - nand_dev_id != chip->read_byte(mtd)) { + if (nand_maf_id != id[0] || nand_dev_id != id[1]) { chip->select_chip(mtd, -1); break; } diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index d0e5426..6e436b5 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -528,4 +528,13 @@ config MEDIATEK_ETH This Driver support MediaTek Ethernet GMAC Say Y to enable support for the MediaTek Ethernet GMAC. +config HIGMACV300_ETH + bool "HiSilicon Gigabit Ethernet Controller" + depends on DM_ETH + select DM_RESET + select PHYLIB + help + This driver supports HIGMACV300 Ethernet controller found on + HiSilicon SoCs. + endif # NETDEVICES diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 51be72b..8d02a37 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -76,3 +76,4 @@ obj-$(CONFIG_SNI_AVE) += sni_ave.o obj-y += ti/ obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth.o obj-y += mscc_eswitch/ +obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o diff --git a/drivers/net/higmacv300.c b/drivers/net/higmacv300.c new file mode 100644 index 0000000..1be8359 --- /dev/null +++ b/drivers/net/higmacv300.c @@ -0,0 +1,597 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019, Linaro Limited + */ + +#include <asm/io.h> +#include <common.h> +#include <console.h> +#include <linux/bug.h> +#include <linux/mii.h> +#include <miiphy.h> +#include <net.h> +#include <reset.h> +#include <wait_bit.h> + +#define STATION_ADDR_LOW 0x0000 +#define STATION_ADDR_HIGH 0x0004 +#define MAC_DUPLEX_HALF_CTRL 0x0008 +#define PORT_MODE 0x0040 +#define PORT_EN 0x0044 +#define BIT_TX_EN BIT(2) +#define BIT_RX_EN BIT(1) +#define MODE_CHANGE_EN 0x01b4 +#define BIT_MODE_CHANGE_EN BIT(0) +#define MDIO_SINGLE_CMD 0x03c0 +#define BIT_MDIO_BUSY BIT(20) +#define MDIO_READ (BIT(17) | BIT_MDIO_BUSY) +#define MDIO_WRITE (BIT(16) | BIT_MDIO_BUSY) +#define MDIO_SINGLE_DATA 0x03c4 +#define MDIO_RDATA_STATUS 0x03d0 +#define BIT_MDIO_RDATA_INVALID BIT(0) +#define RX_FQ_START_ADDR 0x0500 +#define RX_FQ_DEPTH 0x0504 +#define RX_FQ_WR_ADDR 0x0508 +#define RX_FQ_RD_ADDR 0x050c +#define RX_FQ_REG_EN 0x0518 +#define RX_BQ_START_ADDR 0x0520 +#define RX_BQ_DEPTH 0x0524 +#define RX_BQ_WR_ADDR 0x0528 +#define RX_BQ_RD_ADDR 0x052c +#define RX_BQ_REG_EN 0x0538 +#define TX_BQ_START_ADDR 0x0580 +#define TX_BQ_DEPTH 0x0584 +#define TX_BQ_WR_ADDR 0x0588 +#define TX_BQ_RD_ADDR 0x058c +#define TX_BQ_REG_EN 0x0598 +#define TX_RQ_START_ADDR 0x05a0 +#define TX_RQ_DEPTH 0x05a4 +#define TX_RQ_WR_ADDR 0x05a8 +#define TX_RQ_RD_ADDR 0x05ac +#define TX_RQ_REG_EN 0x05b8 +#define BIT_START_ADDR_EN BIT(2) +#define BIT_DEPTH_EN BIT(1) +#define DESC_WR_RD_ENA 0x05cc +#define BIT_RX_OUTCFF_WR BIT(3) +#define BIT_RX_CFF_RD BIT(2) +#define BIT_TX_OUTCFF_WR BIT(1) +#define BIT_TX_CFF_RD BIT(0) +#define BITS_DESC_ENA (BIT_RX_OUTCFF_WR | BIT_RX_CFF_RD | \ + BIT_TX_OUTCFF_WR | BIT_TX_CFF_RD) + +/* MACIF_CTRL */ +#define RGMII_SPEED_1000 0x2c +#define RGMII_SPEED_100 0x2f +#define RGMII_SPEED_10 0x2d +#define MII_SPEED_100 0x0f +#define MII_SPEED_10 0x0d +#define GMAC_SPEED_1000 0x05 +#define GMAC_SPEED_100 0x01 +#define GMAC_SPEED_10 0x00 +#define GMAC_FULL_DUPLEX BIT(4) + +#define RX_DESC_NUM 64 +#define TX_DESC_NUM 2 +#define DESC_SIZE 32 +#define DESC_WORD_SHIFT 3 +#define DESC_BYTE_SHIFT 5 +#define DESC_CNT(n) ((n) >> DESC_BYTE_SHIFT) +#define DESC_BYTE(n) ((n) << DESC_BYTE_SHIFT) +#define DESC_VLD_FREE 0 +#define DESC_VLD_BUSY 1 + +#define MAC_MAX_FRAME_SIZE 1600 + +enum higmac_queue { + RX_FQ, + RX_BQ, + TX_BQ, + TX_RQ, +}; + +struct higmac_desc { + unsigned int buf_addr; + unsigned int buf_len:11; + unsigned int reserve0:5; + unsigned int data_len:11; + unsigned int reserve1:2; + unsigned int fl:2; + unsigned int descvid:1; + unsigned int reserve2[6]; +}; + +struct higmac_priv { + void __iomem *base; + void __iomem *macif_ctrl; + struct reset_ctl rst_phy; + struct higmac_desc *rxfq; + struct higmac_desc *rxbq; + struct higmac_desc *txbq; + struct higmac_desc *txrq; + int rxdesc_in_use; + struct mii_dev *bus; + struct phy_device *phydev; + int phyintf; + int phyaddr; +}; + +#define flush_desc(d) flush_cache((unsigned long)(d), sizeof(*(d))) +#define invalidate_desc(d) \ + invalidate_dcache_range((unsigned long)(d), \ + (unsigned long)(d) + sizeof(*(d))) + +static int higmac_write_hwaddr(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_platdata(dev); + struct higmac_priv *priv = dev_get_priv(dev); + unsigned char *mac = pdata->enetaddr; + u32 val; + + val = mac[1] | (mac[0] << 8); + writel(val, priv->base + STATION_ADDR_HIGH); + + val = mac[5] | (mac[4] << 8) | (mac[3] << 16) | (mac[2] << 24); + writel(val, priv->base + STATION_ADDR_LOW); + + return 0; +} + +static int higmac_free_pkt(struct udevice *dev, uchar *packet, int length) +{ + struct higmac_priv *priv = dev_get_priv(dev); + + /* Inform GMAC that the RX descriptor is no longer in use */ + writel(DESC_BYTE(priv->rxdesc_in_use), priv->base + RX_BQ_RD_ADDR); + + return 0; +} + +static int higmac_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct higmac_priv *priv = dev_get_priv(dev); + struct higmac_desc *fqd = priv->rxfq; + struct higmac_desc *bqd = priv->rxbq; + int fqw_pos, fqr_pos, bqw_pos, bqr_pos; + int timeout = 100000; + int len = 0; + int space; + int i; + + fqw_pos = DESC_CNT(readl(priv->base + RX_FQ_WR_ADDR)); + fqr_pos = DESC_CNT(readl(priv->base + RX_FQ_RD_ADDR)); + + if (fqw_pos >= fqr_pos) + space = RX_DESC_NUM - (fqw_pos - fqr_pos); + else + space = fqr_pos - fqw_pos; + + /* Leave one free to distinguish full filled from empty buffer */ + for (i = 0; i < space - 1; i++) { + fqd = priv->rxfq + fqw_pos; + invalidate_dcache_range(fqd->buf_addr, + fqd->buf_addr + MAC_MAX_FRAME_SIZE); + + if (++fqw_pos >= RX_DESC_NUM) + fqw_pos = 0; + + writel(DESC_BYTE(fqw_pos), priv->base + RX_FQ_WR_ADDR); + } + + bqr_pos = DESC_CNT(readl(priv->base + RX_BQ_RD_ADDR)); + bqd += bqr_pos; + /* BQ is only ever written by GMAC */ + invalidate_desc(bqd); + + do { + bqw_pos = DESC_CNT(readl(priv->base + RX_BQ_WR_ADDR)); + udelay(1); + } while (--timeout && bqw_pos == bqr_pos); + + if (!timeout) + return -ETIMEDOUT; + + if (++bqr_pos >= RX_DESC_NUM) + bqr_pos = 0; + + len = bqd->data_len; + + /* CPU should not have touched this buffer since we added it to FQ */ + invalidate_dcache_range(bqd->buf_addr, bqd->buf_addr + len); + *packetp = (void *)(unsigned long)bqd->buf_addr; + + /* Record the RX_BQ descriptor that is holding RX data */ + priv->rxdesc_in_use = bqr_pos; + + return len; +} + +static int higmac_send(struct udevice *dev, void *packet, int length) +{ + struct higmac_priv *priv = dev_get_priv(dev); + struct higmac_desc *bqd = priv->txbq; + int bqw_pos, rqw_pos, rqr_pos; + int timeout = 1000; + + flush_cache((unsigned long)packet, length); + + bqw_pos = DESC_CNT(readl(priv->base + TX_BQ_WR_ADDR)); + bqd += bqw_pos; + bqd->buf_addr = (unsigned long)packet; + bqd->descvid = DESC_VLD_BUSY; + bqd->data_len = length; + flush_desc(bqd); + + if (++bqw_pos >= TX_DESC_NUM) + bqw_pos = 0; + + writel(DESC_BYTE(bqw_pos), priv->base + TX_BQ_WR_ADDR); + + rqr_pos = DESC_CNT(readl(priv->base + TX_RQ_RD_ADDR)); + if (++rqr_pos >= TX_DESC_NUM) + rqr_pos = 0; + + do { + rqw_pos = DESC_CNT(readl(priv->base + TX_RQ_WR_ADDR)); + udelay(1); + } while (--timeout && rqr_pos != rqw_pos); + + if (!timeout) + return -ETIMEDOUT; + + writel(DESC_BYTE(rqr_pos), priv->base + TX_RQ_RD_ADDR); + + return 0; +} + +static int higmac_adjust_link(struct higmac_priv *priv) +{ + struct phy_device *phydev = priv->phydev; + int interface = priv->phyintf; + u32 val; + + switch (interface) { + case PHY_INTERFACE_MODE_RGMII: + if (phydev->speed == SPEED_1000) + val = RGMII_SPEED_1000; + else if (phydev->speed == SPEED_100) + val = RGMII_SPEED_100; + else + val = RGMII_SPEED_10; + break; + case PHY_INTERFACE_MODE_MII: + if (phydev->speed == SPEED_100) + val = MII_SPEED_100; + else + val = MII_SPEED_10; + break; + default: + debug("unsupported mode: %d\n", interface); + return -EINVAL; + } + + if (phydev->duplex) + val |= GMAC_FULL_DUPLEX; + + writel(val, priv->macif_ctrl); + + if (phydev->speed == SPEED_1000) + val = GMAC_SPEED_1000; + else if (phydev->speed == SPEED_100) + val = GMAC_SPEED_100; + else + val = GMAC_SPEED_10; + + writel(BIT_MODE_CHANGE_EN, priv->base + MODE_CHANGE_EN); + writel(val, priv->base + PORT_MODE); + writel(0, priv->base + MODE_CHANGE_EN); + writel(phydev->duplex, priv->base + MAC_DUPLEX_HALF_CTRL); + + return 0; +} + +static int higmac_start(struct udevice *dev) +{ + struct higmac_priv *priv = dev_get_priv(dev); + struct phy_device *phydev = priv->phydev; + int ret; + + ret = phy_startup(phydev); + if (ret) + return ret; + + if (!phydev->link) { + debug("%s: link down\n", phydev->dev->name); + return -ENODEV; + } + + ret = higmac_adjust_link(priv); + if (ret) + return ret; + + /* Enable port */ + writel(BITS_DESC_ENA, priv->base + DESC_WR_RD_ENA); + writel(BIT_TX_EN | BIT_RX_EN, priv->base + PORT_EN); + + return 0; +} + +static void higmac_stop(struct udevice *dev) +{ + struct higmac_priv *priv = dev_get_priv(dev); + + /* Disable port */ + writel(0, priv->base + PORT_EN); + writel(0, priv->base + DESC_WR_RD_ENA); +} + +static const struct eth_ops higmac_ops = { + .start = higmac_start, + .send = higmac_send, + .recv = higmac_recv, + .free_pkt = higmac_free_pkt, + .stop = higmac_stop, + .write_hwaddr = higmac_write_hwaddr, +}; + +static int higmac_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + struct higmac_priv *priv = bus->priv; + int ret; + + ret = wait_for_bit_le32(priv->base + MDIO_SINGLE_CMD, BIT_MDIO_BUSY, + false, 1000, false); + if (ret) + return ret; + + writel(MDIO_READ | addr << 8 | reg, priv->base + MDIO_SINGLE_CMD); + + ret = wait_for_bit_le32(priv->base + MDIO_SINGLE_CMD, BIT_MDIO_BUSY, + false, 1000, false); + if (ret) + return ret; + + if (readl(priv->base + MDIO_RDATA_STATUS) & BIT_MDIO_RDATA_INVALID) + return -EINVAL; + + return readl(priv->base + MDIO_SINGLE_DATA) >> 16; +} + +static int higmac_mdio_write(struct mii_dev *bus, int addr, int devad, + int reg, u16 value) +{ + struct higmac_priv *priv = bus->priv; + int ret; + + ret = wait_for_bit_le32(priv->base + MDIO_SINGLE_CMD, BIT_MDIO_BUSY, + false, 1000, false); + if (ret) + return ret; + + writel(value, priv->base + MDIO_SINGLE_DATA); + writel(MDIO_WRITE | addr << 8 | reg, priv->base + MDIO_SINGLE_CMD); + + return 0; +} + +static int higmac_init_rx_descs(struct higmac_desc *descs, int num) +{ + int i; + + for (i = 0; i < num; i++) { + struct higmac_desc *desc = &descs[i]; + + desc->buf_addr = (unsigned long)memalign(ARCH_DMA_MINALIGN, + MAC_MAX_FRAME_SIZE); + if (!desc->buf_addr) + goto free_bufs; + + desc->descvid = DESC_VLD_FREE; + desc->buf_len = MAC_MAX_FRAME_SIZE - 1; + flush_desc(desc); + } + + return 0; + +free_bufs: + while (--i > 0) + free((void *)(unsigned long)descs[i].buf_addr); + return -ENOMEM; +} + +static int higmac_init_hw_queue(struct higmac_priv *priv, + enum higmac_queue queue) +{ + struct higmac_desc *desc, **pdesc; + u32 regaddr, regen, regdep; + int depth; + int len; + + switch (queue) { + case RX_FQ: + regaddr = RX_FQ_START_ADDR; + regen = RX_FQ_REG_EN; + regdep = RX_FQ_DEPTH; + depth = RX_DESC_NUM; + pdesc = &priv->rxfq; + break; + case RX_BQ: + regaddr = RX_BQ_START_ADDR; + regen = RX_BQ_REG_EN; + regdep = RX_BQ_DEPTH; + depth = RX_DESC_NUM; + pdesc = &priv->rxbq; + break; + case TX_BQ: + regaddr = TX_BQ_START_ADDR; + regen = TX_BQ_REG_EN; + regdep = TX_BQ_DEPTH; + depth = TX_DESC_NUM; + pdesc = &priv->txbq; + break; + case TX_RQ: + regaddr = TX_RQ_START_ADDR; + regen = TX_RQ_REG_EN; + regdep = TX_RQ_DEPTH; + depth = TX_DESC_NUM; + pdesc = &priv->txrq; + break; + } + + /* Enable depth */ + writel(BIT_DEPTH_EN, priv->base + regen); + writel(depth << DESC_WORD_SHIFT, priv->base + regdep); + writel(0, priv->base + regen); + + len = depth * sizeof(*desc); + desc = memalign(ARCH_DMA_MINALIGN, len); + if (!desc) + return -ENOMEM; + memset(desc, 0, len); + flush_cache((unsigned long)desc, len); + *pdesc = desc; + + /* Set up RX_FQ descriptors */ + if (queue == RX_FQ) + higmac_init_rx_descs(desc, depth); + + /* Enable start address */ + writel(BIT_START_ADDR_EN, priv->base + regen); + writel((unsigned long)desc, priv->base + regaddr); + writel(0, priv->base + regen); + + return 0; +} + +static int higmac_hw_init(struct higmac_priv *priv) +{ + int ret; + + /* Initialize hardware queues */ + ret = higmac_init_hw_queue(priv, RX_FQ); + if (ret) + return ret; + + ret = higmac_init_hw_queue(priv, RX_BQ); + if (ret) + goto free_rx_fq; + + ret = higmac_init_hw_queue(priv, TX_BQ); + if (ret) + goto free_rx_bq; + + ret = higmac_init_hw_queue(priv, TX_RQ); + if (ret) + goto free_tx_bq; + + /* Reset phy */ + reset_deassert(&priv->rst_phy); + mdelay(10); + reset_assert(&priv->rst_phy); + mdelay(30); + reset_deassert(&priv->rst_phy); + mdelay(30); + + return 0; + +free_tx_bq: + free(priv->txbq); +free_rx_bq: + free(priv->rxbq); +free_rx_fq: + free(priv->rxfq); + return ret; +} + +static int higmac_probe(struct udevice *dev) +{ + struct higmac_priv *priv = dev_get_priv(dev); + struct phy_device *phydev; + struct mii_dev *bus; + int ret; + + ret = higmac_hw_init(priv); + if (ret) + return ret; + + bus = mdio_alloc(); + if (!bus) + return -ENOMEM; + + bus->read = higmac_mdio_read; + bus->write = higmac_mdio_write; + bus->priv = priv; + priv->bus = bus; + + ret = mdio_register_seq(bus, dev->seq); + if (ret) + return ret; + + phydev = phy_connect(bus, priv->phyaddr, dev, priv->phyintf); + if (!phydev) + return -ENODEV; + + phydev->supported &= PHY_GBIT_FEATURES; + phydev->advertising = phydev->supported; + priv->phydev = phydev; + + return phy_config(phydev); +} + +static int higmac_remove(struct udevice *dev) +{ + struct higmac_priv *priv = dev_get_priv(dev); + int i; + + mdio_unregister(priv->bus); + mdio_free(priv->bus); + + /* Free RX packet buffers */ + for (i = 0; i < RX_DESC_NUM; i++) + free((void *)(unsigned long)priv->rxfq[i].buf_addr); + + return 0; +} + +static int higmac_ofdata_to_platdata(struct udevice *dev) +{ + struct higmac_priv *priv = dev_get_priv(dev); + int phyintf = PHY_INTERFACE_MODE_NONE; + const char *phy_mode; + ofnode phy_node; + + priv->base = dev_remap_addr_index(dev, 0); + priv->macif_ctrl = dev_remap_addr_index(dev, 1); + + phy_mode = dev_read_string(dev, "phy-mode"); + if (phy_mode) + phyintf = phy_get_interface_by_name(phy_mode); + if (phyintf == PHY_INTERFACE_MODE_NONE) + return -ENODEV; + priv->phyintf = phyintf; + + phy_node = dev_read_subnode(dev, "phy"); + if (!ofnode_valid(phy_node)) { + debug("failed to find phy node\n"); + return -ENODEV; + } + priv->phyaddr = ofnode_read_u32_default(phy_node, "reg", 0); + + return reset_get_by_name(dev, "phy", &priv->rst_phy); +} + +static const struct udevice_id higmac_ids[] = { + { .compatible = "hisilicon,hi3798cv200-gmac" }, + { } +}; + +U_BOOT_DRIVER(eth_higmac) = { + .name = "eth_higmac", + .id = UCLASS_ETH, + .of_match = higmac_ids, + .ofdata_to_platdata = higmac_ofdata_to_platdata, + .probe = higmac_probe, + .remove = higmac_remove, + .ops = &higmac_ops, + .priv_auto_alloc_size = sizeof(struct higmac_priv), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), +}; diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 824fa11..cf1e761 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -918,6 +918,11 @@ static void decode_regions(struct pci_controller *hose, ofnode parent_node, return; for (i = 0; i < CONFIG_NR_DRAM_BANKS; ++i) { + if (hose->region_count == MAX_PCI_REGIONS) { + pr_err("maximum number of regions parsed, aborting\n"); + break; + } + if (bd->bi_dram[i].size) { pci_set_region(hose->regions + hose->region_count++, bd->bi_dram[i].start, diff --git a/drivers/pinctrl/mediatek/Kconfig b/drivers/pinctrl/mediatek/Kconfig index 1bd9a92..9930ca1 100644 --- a/drivers/pinctrl/mediatek/Kconfig +++ b/drivers/pinctrl/mediatek/Kconfig @@ -12,4 +12,8 @@ config PINCTRL_MT7629 bool "MT7629 SoC pinctrl driver" select PINCTRL_MTK +config PINCTRL_MT8516 + bool "MT8516 SoC pinctrl driver" + select PINCTRL_MTK + endif diff --git a/drivers/pinctrl/mediatek/Makefile b/drivers/pinctrl/mediatek/Makefile index f6ef362..c4f2908 100644 --- a/drivers/pinctrl/mediatek/Makefile +++ b/drivers/pinctrl/mediatek/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_PINCTRL_MTK) += pinctrl-mtk-common.o # SoC Drivers obj-$(CONFIG_PINCTRL_MT7623) += pinctrl-mt7623.o obj-$(CONFIG_PINCTRL_MT7629) += pinctrl-mt7629.o +obj-$(CONFIG_PINCTRL_MT8516) += pinctrl-mt8516.o diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8516.c b/drivers/pinctrl/mediatek/pinctrl-mt8516.c new file mode 100644 index 0000000..829b30e --- /dev/null +++ b/drivers/pinctrl/mediatek/pinctrl-mt8516.c @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Fabien Parent <fparent@baylibre.com> + */ + +#include <dm.h> + +#include "pinctrl-mtk-common.h" + +#define PIN_FIELD(_s_pin, _e_pin, _s_addr, _x_addrs, _s_bit, _x_bits) \ + PIN_FIELD_CALC(_s_pin, _e_pin, _s_addr, _x_addrs, _s_bit, \ + _x_bits, 16, false) + +static const struct mtk_pin_field_calc mt8516_pin_mode_range[] = { + PIN_FIELD_CALC(0, 124, 0x300, 0x10, 0, 3, 15, false), +}; + +static const struct mtk_pin_field_calc mt8516_pin_dir_range[] = { + PIN_FIELD(0, 124, 0x0, 0x10, 0, 1), +}; + +static const struct mtk_pin_field_calc mt8516_pin_di_range[] = { + PIN_FIELD(0, 124, 0x200, 0x10, 0, 1), +}; + +static const struct mtk_pin_field_calc mt8516_pin_do_range[] = { + PIN_FIELD(0, 124, 0x100, 0x10, 0, 1), +}; + +static const struct mtk_pin_field_calc mt8516_pin_ies_range[] = { + PIN_FIELD(0, 6, 0x900, 0x10, 2, 1), + PIN_FIELD(7, 10, 0x900, 0x10, 3, 1), + PIN_FIELD(11, 13, 0x900, 0x10, 12, 1), + PIN_FIELD(14, 17, 0x900, 0x10, 13, 1), + PIN_FIELD(18, 20, 0x910, 0x10, 10, 1), + PIN_FIELD(21, 23, 0x900, 0x10, 13, 1), + PIN_FIELD(24, 25, 0x900, 0x10, 12, 1), + PIN_FIELD(26, 30, 0x900, 0x10, 0, 1), + PIN_FIELD(31, 33, 0x900, 0x10, 1, 1), + PIN_FIELD(34, 39, 0x900, 0x10, 2, 1), + PIN_FIELD(40, 40, 0x910, 0x10, 11, 1), + PIN_FIELD(41, 43, 0x900, 0x10, 10, 1), + PIN_FIELD(44, 47, 0x900, 0x10, 11, 1), + PIN_FIELD(48, 51, 0x900, 0x10, 14, 1), + PIN_FIELD(52, 53, 0x910, 0x10, 0, 1), + PIN_FIELD(54, 54, 0x910, 0x10, 2, 1), + PIN_FIELD(55, 57, 0x910, 0x10, 4, 1), + PIN_FIELD(58, 59, 0x900, 0x10, 15, 1), + PIN_FIELD(60, 61, 0x910, 0x10, 1, 1), + PIN_FIELD(62, 65, 0x910, 0x10, 5, 1), + PIN_FIELD(66, 67, 0x910, 0x10, 6, 1), + PIN_FIELD(68, 68, 0x930, 0x10, 2, 1), + PIN_FIELD(69, 69, 0x930, 0x10, 1, 1), + PIN_FIELD(70, 70, 0x930, 0x10, 6, 1), + PIN_FIELD(71, 71, 0x930, 0x10, 5, 1), + PIN_FIELD(72, 72, 0x930, 0x10, 4, 1), + PIN_FIELD(73, 73, 0x930, 0x10, 3, 1), + + PIN_FIELD(100, 103, 0x910, 0x10, 7, 1), + PIN_FIELD(104, 104, 0x920, 0x10, 12, 1), + PIN_FIELD(105, 105, 0x920, 0x10, 11, 1), + PIN_FIELD(106, 106, 0x930, 0x10, 0, 1), + PIN_FIELD(107, 107, 0x920, 0x10, 15, 1), + PIN_FIELD(108, 108, 0x920, 0x10, 14, 1), + PIN_FIELD(109, 109, 0x920, 0x10, 13, 1), + PIN_FIELD(110, 110, 0x920, 0x10, 9, 1), + PIN_FIELD(111, 111, 0x920, 0x10, 8, 1), + PIN_FIELD(112, 112, 0x920, 0x10, 7, 1), + PIN_FIELD(113, 113, 0x920, 0x10, 6, 1), + PIN_FIELD(114, 114, 0x920, 0x10, 10, 1), + PIN_FIELD(115, 115, 0x920, 0x10, 1, 1), + PIN_FIELD(116, 116, 0x920, 0x10, 0, 1), + PIN_FIELD(117, 117, 0x920, 0x10, 5, 1), + PIN_FIELD(118, 118, 0x920, 0x10, 4, 1), + PIN_FIELD(119, 119, 0x920, 0x10, 3, 1), + PIN_FIELD(120, 120, 0x920, 0x10, 2, 1), + PIN_FIELD(121, 124, 0x910, 0x10, 9, 1), +}; + +static const struct mtk_pin_field_calc mt8516_pin_smt_range[] = { + PIN_FIELD(0, 6, 0xA00, 0x10, 2, 1), + PIN_FIELD(7, 10, 0xA00, 0x10, 3, 1), + PIN_FIELD(11, 13, 0xA00, 0x10, 12, 1), + PIN_FIELD(14, 17, 0xA00, 0x10, 13, 1), + PIN_FIELD(18, 20, 0xA10, 0x10, 10, 1), + PIN_FIELD(21, 23, 0xA00, 0x10, 13, 1), + PIN_FIELD(24, 25, 0xA00, 0x10, 12, 1), + PIN_FIELD(26, 30, 0xA00, 0x10, 0, 1), + PIN_FIELD(31, 33, 0xA00, 0x10, 1, 1), + PIN_FIELD(40, 40, 0xA10, 0x10, 11, 1), + PIN_FIELD(41, 43, 0xA00, 0x10, 10, 1), + PIN_FIELD(44, 47, 0xA00, 0x10, 11, 1), + PIN_FIELD(48, 51, 0xA00, 0x10, 14, 1), + PIN_FIELD(52, 53, 0xA10, 0x10, 0, 1), + PIN_FIELD(54, 54, 0xA10, 0x10, 2, 1), + PIN_FIELD(55, 57, 0xA10, 0x10, 4, 1), + PIN_FIELD(58, 59, 0xA00, 0x10, 15, 1), + PIN_FIELD(60, 61, 0xA10, 0x10, 1, 1), + PIN_FIELD(62, 65, 0xA10, 0x10, 5, 1), + PIN_FIELD(66, 67, 0xA10, 0x10, 6, 1), + PIN_FIELD(68, 68, 0xA30, 0x10, 2, 1), + PIN_FIELD(69, 69, 0xA30, 0x10, 1, 1), + PIN_FIELD(70, 70, 0xA30, 0x10, 3, 1), + PIN_FIELD(71, 71, 0xA30, 0x10, 4, 1), + PIN_FIELD(72, 72, 0xA30, 0x10, 5, 1), + PIN_FIELD(73, 73, 0xA30, 0x10, 6, 1), + + PIN_FIELD(100, 103, 0xA10, 0x10, 7, 1), + PIN_FIELD(104, 104, 0xA20, 0x10, 12, 1), + PIN_FIELD(105, 105, 0xA20, 0x10, 11, 1), + PIN_FIELD(106, 106, 0xA30, 0x10, 13, 1), + PIN_FIELD(107, 107, 0xA20, 0x10, 14, 1), + PIN_FIELD(108, 108, 0xA20, 0x10, 15, 1), + PIN_FIELD(109, 109, 0xA30, 0x10, 0, 1), + PIN_FIELD(110, 110, 0xA20, 0x10, 9, 1), + PIN_FIELD(111, 111, 0xA20, 0x10, 8, 1), + PIN_FIELD(112, 112, 0xA20, 0x10, 7, 1), + PIN_FIELD(113, 113, 0xA20, 0x10, 6, 1), + PIN_FIELD(114, 114, 0xA20, 0x10, 10, 1), + PIN_FIELD(115, 115, 0xA20, 0x10, 1, 1), + PIN_FIELD(116, 116, 0xA20, 0x10, 0, 1), + PIN_FIELD(117, 117, 0xA20, 0x10, 5, 1), + PIN_FIELD(118, 118, 0xA20, 0x10, 4, 1), + PIN_FIELD(119, 119, 0xA20, 0x10, 3, 1), + PIN_FIELD(120, 120, 0xA20, 0x10, 2, 1), + PIN_FIELD(121, 124, 0xA10, 0x10, 9, 1), +}; + +static const struct mtk_pin_field_calc mt8516_pin_pullen_range[] = { + PIN_FIELD(0, 13, 0x500, 0x10, 0, 1), + PIN_FIELD(18, 20, 0x510, 0x10, 2, 1), + PIN_FIELD(24, 31, 0x510, 0x10, 8, 1), + PIN_FIELD(32, 39, 0x520, 0x10, 0, 1), + PIN_FIELD(44, 47, 0x520, 0x10, 12, 1), + PIN_FIELD(48, 63, 0x530, 0x10, 0, 1), + PIN_FIELD(64, 67, 0x540, 0x10, 0, 1), + PIN_FIELD(100, 103, 0x560, 0x10, 4, 1), + PIN_FIELD(121, 124, 0x570, 0x10, 9, 1), +}; + +static const struct mtk_pin_field_calc mt8516_pin_pullsel_range[] = { + PIN_FIELD(0, 13, 0x600, 0x10, 0, 1), + PIN_FIELD(18, 20, 0x610, 0x10, 2, 1), + PIN_FIELD(24, 31, 0x610, 0x10, 8, 1), + PIN_FIELD(32, 39, 0x620, 0x10, 0, 1), + PIN_FIELD(44, 47, 0x620, 0x10, 12, 1), + PIN_FIELD(48, 63, 0x630, 0x10, 0, 1), + PIN_FIELD(64, 67, 0x640, 0x10, 0, 1), + PIN_FIELD(100, 103, 0x660, 0x10, 4, 1), + PIN_FIELD(121, 124, 0x670, 0x10, 9, 1), +}; + +static const struct mtk_pin_field_calc mt8516_pin_drv_range[] = { + PIN_FIELD(0, 4, 0xd00, 0x10, 0, 4), + PIN_FIELD(5, 10, 0xd00, 0x10, 4, 4), + PIN_FIELD(11, 13, 0xd00, 0x10, 8, 4), + PIN_FIELD(14, 17, 0xd00, 0x10, 12, 4), + PIN_FIELD(18, 20, 0xd10, 0x10, 0, 4), + PIN_FIELD(21, 23, 0xd00, 0x10, 12, 4), + PIN_FIELD(24, 25, 0xd00, 0x10, 8, 4), + PIN_FIELD(26, 30, 0xd10, 0x10, 4, 4), + PIN_FIELD(31, 33, 0xd10, 0x10, 8, 4), + PIN_FIELD(34, 35, 0xd10, 0x10, 12, 4), + PIN_FIELD(36, 39, 0xd20, 0x10, 0, 4), + PIN_FIELD(40, 40, 0xd20, 0x10, 4, 4), + PIN_FIELD(41, 43, 0xd20, 0x10, 8, 4), + PIN_FIELD(44, 47, 0xd20, 0x10, 12, 4), + PIN_FIELD(48, 51, 0xd30, 0x10, 0, 4), + PIN_FIELD(54, 54, 0xd30, 0x10, 8, 4), + PIN_FIELD(55, 57, 0xd30, 0x10, 12, 4), + PIN_FIELD(62, 67, 0xd40, 0x10, 8, 4), + PIN_FIELD(68, 68, 0xd40, 0x10, 12, 4), + PIN_FIELD(69, 69, 0xd50, 0x10, 0, 4), + PIN_FIELD(70, 73, 0xd50, 0x10, 4, 4), + PIN_FIELD(100, 103, 0xd50, 0x10, 8, 4), + PIN_FIELD(104, 104, 0xd50, 0x10, 12, 4), + PIN_FIELD(105, 105, 0xd60, 0x10, 0, 4), + PIN_FIELD(106, 109, 0xd60, 0x10, 4, 4), + PIN_FIELD(110, 113, 0xd70, 0x10, 0, 4), + PIN_FIELD(114, 114, 0xd70, 0x10, 4, 4), + PIN_FIELD(115, 115, 0xd60, 0x10, 12, 4), + PIN_FIELD(116, 116, 0xd60, 0x10, 8, 4), + PIN_FIELD(117, 120, 0xd70, 0x10, 0, 4), +}; + +static const struct mtk_pin_reg_calc mt8516_reg_cals[] = { + [PINCTRL_PIN_REG_MODE] = MTK_RANGE(mt8516_pin_mode_range), + [PINCTRL_PIN_REG_DIR] = MTK_RANGE(mt8516_pin_dir_range), + [PINCTRL_PIN_REG_DI] = MTK_RANGE(mt8516_pin_di_range), + [PINCTRL_PIN_REG_DO] = MTK_RANGE(mt8516_pin_do_range), + [PINCTRL_PIN_REG_IES] = MTK_RANGE(mt8516_pin_ies_range), + [PINCTRL_PIN_REG_SMT] = MTK_RANGE(mt8516_pin_smt_range), + [PINCTRL_PIN_REG_PULLSEL] = MTK_RANGE(mt8516_pin_pullsel_range), + [PINCTRL_PIN_REG_PULLEN] = MTK_RANGE(mt8516_pin_pullen_range), + [PINCTRL_PIN_REG_DRV] = MTK_RANGE(mt8516_pin_drv_range), +}; + +static const struct mtk_pin_desc mt8516_pins[] = { + MTK_PIN(0, "EINT0", DRV_GRP0), + MTK_PIN(1, "EINT1", DRV_GRP0), + MTK_PIN(2, "EINT2", DRV_GRP0), + MTK_PIN(3, "EINT3", DRV_GRP0), + MTK_PIN(4, "EINT4", DRV_GRP0), + MTK_PIN(5, "EINT5", DRV_GRP0), + MTK_PIN(6, "EINT6", DRV_GRP0), + MTK_PIN(7, "EINT7", DRV_GRP0), + MTK_PIN(8, "EINT8", DRV_GRP0), + MTK_PIN(9, "EINT9", DRV_GRP0), + MTK_PIN(10, "EINT10", DRV_GRP0), + MTK_PIN(11, "EINT11", DRV_GRP0), + MTK_PIN(12, "EINT12", DRV_GRP0), + MTK_PIN(13, "EINT13", DRV_GRP0), + MTK_PIN(14, "EINT14", DRV_GRP2), + MTK_PIN(15, "EINT15", DRV_GRP2), + MTK_PIN(16, "EINT16", DRV_GRP2), + MTK_PIN(17, "EINT17", DRV_GRP2), + MTK_PIN(18, "EINT18", DRV_GRP0), + MTK_PIN(19, "EINT19", DRV_GRP0), + MTK_PIN(20, "EINT20", DRV_GRP0), + MTK_PIN(21, "EINT21", DRV_GRP2), + MTK_PIN(22, "EINT22", DRV_GRP2), + MTK_PIN(23, "EINT23", DRV_GRP2), + MTK_PIN(24, "EINT24", DRV_GRP0), + MTK_PIN(25, "EINT25", DRV_GRP0), + MTK_PIN(26, "PWRAP_SPI0_MI", DRV_GRP4), + MTK_PIN(27, "PWRAP_SPI0_MO", DRV_GRP4), + MTK_PIN(28, "PWRAP_INT", DRV_GRP4), + MTK_PIN(29, "PWRAP_SPIO0_CK", DRV_GRP4), + MTK_PIN(30, "PWARP_SPI0_CSN", DRV_GRP4), + MTK_PIN(31, "RTC32K_CK", DRV_GRP4), + MTK_PIN(32, "WATCHDOG", DRV_GRP4), + MTK_PIN(33, "SRCLKENA0", DRV_GRP4), + MTK_PIN(34, "URXD2", DRV_GRP0), + MTK_PIN(35, "UTXD2", DRV_GRP0), + MTK_PIN(36, "MRG_CLK", DRV_GRP0), + MTK_PIN(37, "MRG_SYNC", DRV_GRP0), + MTK_PIN(38, "MRG_DI", DRV_GRP0), + MTK_PIN(39, "MRG_DO", DRV_GRP0), + MTK_PIN(40, "KPROW0", DRV_GRP2), + MTK_PIN(41, "KPROW1", DRV_GRP2), + MTK_PIN(42, "KPCOL0", DRV_GRP2), + MTK_PIN(43, "KPCOL1", DRV_GRP2), + MTK_PIN(44, "JMTS", DRV_GRP2), + MTK_PIN(45, "JTCK", DRV_GRP2), + MTK_PIN(46, "JTDI", DRV_GRP2), + MTK_PIN(47, "JTDO", DRV_GRP2), + MTK_PIN(48, "SPI_CS", DRV_GRP2), + MTK_PIN(49, "SPI_CK", DRV_GRP2), + MTK_PIN(50, "SPI_MI", DRV_GRP2), + MTK_PIN(51, "SPI_MO", DRV_GRP2), + MTK_PIN(52, "SDA1", DRV_GRP2), + MTK_PIN(53, "SCL1", DRV_GRP2), + MTK_PIN(54, "DISP_PWM", DRV_GRP2), + MTK_PIN(55, "I2S_DATA_IN", DRV_GRP2), + MTK_PIN(56, "I2S_LRCK", DRV_GRP2), + MTK_PIN(57, "I2S_BCK", DRV_GRP2), + MTK_PIN(58, "SDA0", DRV_GRP2), + MTK_PIN(59, "SCL0", DRV_GRP2), + MTK_PIN(60, "SDA2", DRV_GRP2), + MTK_PIN(61, "SCL2", DRV_GRP2), + MTK_PIN(62, "URXD0", DRV_GRP2), + MTK_PIN(63, "UTXD0", DRV_GRP2), + MTK_PIN(64, "URXD1", DRV_GRP2), + MTK_PIN(65, "UTXD1", DRV_GRP2), + MTK_PIN(66, "LCM_RST", DRV_GRP2), + MTK_PIN(67, "DSI_TE", DRV_GRP2), + MTK_PIN(68, "MSDC2_CMD", DRV_GRP4), + MTK_PIN(69, "MSDC2_CLK", DRV_GRP4), + MTK_PIN(70, "MSDC2_DAT0", DRV_GRP4), + MTK_PIN(71, "MSDC2_DAT1", DRV_GRP4), + MTK_PIN(72, "MSDC2_DAT2", DRV_GRP4), + MTK_PIN(73, "MSDC2_DAT3", DRV_GRP4), + MTK_PIN(74, "TDN3", DRV_GRP0), + MTK_PIN(75, "TDP3", DRV_GRP0), + MTK_PIN(76, "TDN2", DRV_GRP0), + MTK_PIN(77, "TDP2", DRV_GRP0), + MTK_PIN(78, "TCN", DRV_GRP0), + MTK_PIN(79, "TCP", DRV_GRP0), + MTK_PIN(80, "TDN1", DRV_GRP0), + MTK_PIN(81, "TDP1", DRV_GRP0), + MTK_PIN(82, "TDN0", DRV_GRP0), + MTK_PIN(83, "TDP0", DRV_GRP0), + MTK_PIN(84, "RDN0", DRV_GRP0), + MTK_PIN(85, "RDP0", DRV_GRP0), + MTK_PIN(86, "RDN1", DRV_GRP0), + MTK_PIN(87, "RDP1", DRV_GRP0), + MTK_PIN(88, "RCN", DRV_GRP0), + MTK_PIN(89, "RCP", DRV_GRP0), + MTK_PIN(90, "RDN2", DRV_GRP0), + MTK_PIN(91, "RDP2", DRV_GRP0), + MTK_PIN(92, "RDN3", DRV_GRP0), + MTK_PIN(93, "RDP3", DRV_GRP0), + MTK_PIN(94, "RCN_A", DRV_GRP0), + MTK_PIN(95, "RCP_A", DRV_GRP0), + MTK_PIN(96, "RDN1_A", DRV_GRP0), + MTK_PIN(97, "RDP1_A", DRV_GRP0), + MTK_PIN(98, "RDN0_A", DRV_GRP0), + MTK_PIN(99, "RDP0_A", DRV_GRP0), + MTK_PIN(100, "CMDDAT0", DRV_GRP2), + MTK_PIN(101, "CMDDAT1", DRV_GRP2), + MTK_PIN(102, "CMMCLK", DRV_GRP2), + MTK_PIN(103, "CMPCLK", DRV_GRP2), + MTK_PIN(104, "MSDC1_CMD", DRV_GRP4), + MTK_PIN(105, "MSDC1_CLK", DRV_GRP4), + MTK_PIN(106, "MSDC1_DAT0", DRV_GRP4), + MTK_PIN(107, "MSDC1_DAT1", DRV_GRP4), + MTK_PIN(108, "MSDC1_DAT2", DRV_GRP4), + MTK_PIN(109, "MSDC1_DAT3", DRV_GRP4), + MTK_PIN(110, "MSDC0_DAT7", DRV_GRP4), + MTK_PIN(111, "MSDC0_DAT6", DRV_GRP4), + MTK_PIN(112, "MSDC0_DAT5", DRV_GRP4), + MTK_PIN(113, "MSDC0_DAT4", DRV_GRP4), + MTK_PIN(114, "MSDC0_RSTB", DRV_GRP4), + MTK_PIN(115, "MSDC0_CMD", DRV_GRP4), + MTK_PIN(116, "MSDC0_CLK", DRV_GRP4), + MTK_PIN(117, "MSDC0_DAT3", DRV_GRP4), + MTK_PIN(118, "MSDC0_DAT2", DRV_GRP4), + MTK_PIN(119, "MSDC0_DAT1", DRV_GRP4), + MTK_PIN(120, "MSDC0_DAT0", DRV_GRP4), +}; + +/* List all groups consisting of these pins dedicated to the enablement of + * certain hardware block and the corresponding mode for all of the pins. + * The hardware probably has multiple combinations of these pinouts. + */ + +/* UART */ +static int mt8516_uart0_0_rxd_txd_pins[] = { 62, 63, }; +static int mt8516_uart0_0_rxd_txd_funcs[] = { 1, 1, }; +static int mt8516_uart1_0_rxd_txd_pins[] = { 64, 65, }; +static int mt8516_uart1_0_rxd_txd_funcs[] = { 1, 1, }; +static int mt8516_uart2_0_rxd_txd_pins[] = { 34, 35, }; +static int mt8516_uart2_0_rxd_txd_funcs[] = { 1, 1, }; + +/* Joint those groups owning the same capability in user point of view which + * allows that people tend to use through the device tree. + */ +static const char *const mt8516_uart_groups[] = { "uart0_0_rxd_txd", + "uart1_0_rxd_txd", + "uart2_0_rxd_txd", }; + +/* MMC0 */ +static int mt8516_msdc0_pins[] = { 110, 111, 112, 113, 114, 115, 116, 117, 118, + 119, 120, }; +static int mt8516_msdc0_funcs[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }; + +static const struct mtk_group_desc mt8516_groups[] = { + PINCTRL_PIN_GROUP("uart0_0_rxd_txd", mt8516_uart0_0_rxd_txd), + PINCTRL_PIN_GROUP("uart1_0_rxd_txd", mt8516_uart1_0_rxd_txd), + PINCTRL_PIN_GROUP("uart2_0_rxd_txd", mt8516_uart2_0_rxd_txd), + + PINCTRL_PIN_GROUP("msdc0", mt8516_msdc0), +}; + +static const char *const mt8516_msdc_groups[] = { "msdc0" }; + +static const struct mtk_function_desc mt8516_functions[] = { + {"uart", mt8516_uart_groups, ARRAY_SIZE(mt8516_uart_groups)}, + {"msdc", mt8516_msdc_groups, ARRAY_SIZE(mt8516_msdc_groups)}, +}; + +static struct mtk_pinctrl_soc mt8516_data = { + .name = "mt8516_pinctrl", + .reg_cal = mt8516_reg_cals, + .pins = mt8516_pins, + .npins = ARRAY_SIZE(mt8516_pins), + .grps = mt8516_groups, + .ngrps = ARRAY_SIZE(mt8516_groups), + .funcs = mt8516_functions, + .nfuncs = ARRAY_SIZE(mt8516_functions), +}; + +static int mtk_pinctrl_mt8516_probe(struct udevice *dev) +{ + return mtk_pinctrl_common_probe(dev, &mt8516_data); +} + +static const struct udevice_id mt8516_pctrl_match[] = { + { .compatible = "mediatek,mt8516-pinctrl" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(mt8516_pinctrl) = { + .name = "mt8516_pinctrl", + .id = UCLASS_PINCTRL, + .of_match = mt8516_pctrl_match, + .ops = &mtk_pinctrl_ops, + .probe = mtk_pinctrl_mt8516_probe, + .priv_auto_alloc_size = sizeof(struct mtk_pinctrl_priv), +}; diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index a81e767..6ec6f39 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -121,4 +121,10 @@ config RESET_SUNXI This enables support for common reset driver for Allwinner SoCs. +config RESET_HISILICON + bool "Reset controller driver for HiSilicon SoCs" + depends on DM_RESET + help + Support for reset controller on HiSilicon SoCs. + endmenu diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 4fad7d4..7fec75b 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -19,3 +19,4 @@ obj-$(CONFIG_RESET_MESON) += reset-meson.o obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_RESET_MEDIATEK) += reset-mediatek.o obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o +obj-$(CONFIG_RESET_HISILICON) += reset-hisilicon.o diff --git a/drivers/reset/reset-hisilicon.c b/drivers/reset/reset-hisilicon.c new file mode 100644 index 0000000..a9f052a --- /dev/null +++ b/drivers/reset/reset-hisilicon.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019, Linaro Limited + */ + +#include <asm/io.h> +#include <common.h> +#include <dm.h> +#include <dt-bindings/reset/ti-syscon.h> +#include <reset-uclass.h> + +struct hisi_reset_priv { + void __iomem *base; +}; + +static int hisi_reset_deassert(struct reset_ctl *rst) +{ + struct hisi_reset_priv *priv = dev_get_priv(rst->dev); + u32 val; + + val = readl(priv->base + rst->data); + if (rst->polarity & DEASSERT_SET) + val |= BIT(rst->id); + else + val &= ~BIT(rst->id); + writel(val, priv->base + rst->data); + + return 0; +} + +static int hisi_reset_assert(struct reset_ctl *rst) +{ + struct hisi_reset_priv *priv = dev_get_priv(rst->dev); + u32 val; + + val = readl(priv->base + rst->data); + if (rst->polarity & ASSERT_SET) + val |= BIT(rst->id); + else + val &= ~BIT(rst->id); + writel(val, priv->base + rst->data); + + return 0; +} + +static int hisi_reset_free(struct reset_ctl *rst) +{ + return 0; +} + +static int hisi_reset_request(struct reset_ctl *rst) +{ + return 0; +} + +static int hisi_reset_of_xlate(struct reset_ctl *rst, + struct ofnode_phandle_args *args) +{ + if (args->args_count != 3) { + debug("Invalid args_count: %d\n", args->args_count); + return -EINVAL; + } + + /* Use .data field as register offset and .id field as bit shift */ + rst->data = args->args[0]; + rst->id = args->args[1]; + rst->polarity = args->args[2]; + + return 0; +} + +static const struct reset_ops hisi_reset_reset_ops = { + .of_xlate = hisi_reset_of_xlate, + .request = hisi_reset_request, + .free = hisi_reset_free, + .rst_assert = hisi_reset_assert, + .rst_deassert = hisi_reset_deassert, +}; + +static const struct udevice_id hisi_reset_ids[] = { + { .compatible = "hisilicon,hi3798cv200-reset" }, + { } +}; + +static int hisi_reset_probe(struct udevice *dev) +{ + struct hisi_reset_priv *priv = dev_get_priv(dev); + + priv->base = dev_remap_addr(dev); + if (!priv->base) + return -ENOMEM; + + return 0; +} + +U_BOOT_DRIVER(hisi_reset) = { + .name = "hisilicon_reset", + .id = UCLASS_RESET, + .of_match = hisi_reset_ids, + .ops = &hisi_reset_reset_ops, + .probe = hisi_reset_probe, + .priv_auto_alloc_size = sizeof(struct hisi_reset_priv), +}; diff --git a/include/configs/broadcom_bcm963158.h b/include/configs/broadcom_bcm963158.h index 5834e1e..2de6f21 100644 --- a/include/configs/broadcom_bcm963158.h +++ b/include/configs/broadcom_bcm963158.h @@ -30,6 +30,13 @@ #define CONFIG_SKIP_LOWLEVEL_INIT +#ifdef CONFIG_NAND +#define CONFIG_SYS_MAX_NAND_DEVICE 1 +#define CONFIG_SYS_NAND_SELF_INIT +#define CONFIG_SYS_NAND_ONFI_DETECTION +#define CONFIG_SYS_NAND_DRIVER_ECC_LAYOUT +#endif /* CONFIG_NAND */ + /* * bcm963158 */ diff --git a/include/configs/broadcom_bcm968380gerg.h b/include/configs/broadcom_bcm968380gerg.h index 6126a88..355f3ef 100644 --- a/include/configs/broadcom_bcm968380gerg.h +++ b/include/configs/broadcom_bcm968380gerg.h @@ -7,3 +7,10 @@ #include <configs/bmips_bcm6838.h> #define CONFIG_ENV_SIZE (8 * 1024) + +#ifdef CONFIG_NAND +#define CONFIG_SYS_MAX_NAND_DEVICE 1 +#define CONFIG_SYS_NAND_SELF_INIT +#define CONFIG_SYS_NAND_ONFI_DETECTION +#define CONFIG_SYS_NAND_DRIVER_ECC_LAYOUT +#endif /* CONFIG_NAND */ diff --git a/include/configs/broadcom_bcm968580xref.h b/include/configs/broadcom_bcm968580xref.h index 1c0945e..52b4f55 100644 --- a/include/configs/broadcom_bcm968580xref.h +++ b/include/configs/broadcom_bcm968580xref.h @@ -29,6 +29,13 @@ #define CONFIG_SKIP_LOWLEVEL_INIT +#ifdef CONFIG_NAND +#define CONFIG_SYS_MAX_NAND_DEVICE 1 +#define CONFIG_SYS_NAND_SELF_INIT +#define CONFIG_SYS_NAND_ONFI_DETECTION +#define CONFIG_SYS_NAND_DRIVER_ECC_LAYOUT +#endif /* CONFIG_NAND */ + /* * 968580xref */ diff --git a/include/dt-bindings/clock/mt8516-clk.h b/include/dt-bindings/clock/mt8516-clk.h new file mode 100644 index 0000000..745b87f --- /dev/null +++ b/include/dt-bindings/clock/mt8516-clk.h @@ -0,0 +1,251 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 BayLibre, SAS + * Copyright (c) 2018 MediaTek Inc. + * Author: Fabien Parent <fparent@baylibre.com> + */ + +#ifndef _DT_BINDINGS_CLK_MT8516_H +#define _DT_BINDINGS_CLK_MT8516_H + + +/* APMIXEDSYS */ + +#define CLK_APMIXED_ARMPLL 0 +#define CLK_APMIXED_MAINPLL 1 +#define CLK_APMIXED_UNIVPLL 2 +#define CLK_APMIXED_MMPLL 3 +#define CLK_APMIXED_APLL1 4 +#define CLK_APMIXED_APLL2 5 +#define CLK_APMIXED_NR_CLK 6 + +/* TOPCKGEN */ + +#define CLK_TOP_CLK_NULL 0 +#define CLK_TOP_I2S_INFRA_BCK 1 +#define CLK_TOP_MEMPLL 2 +#define CLK_TOP_DMPLL 3 +#define CLK_TOP_MAINPLL_D2 4 +#define CLK_TOP_MAINPLL_D4 5 +#define CLK_TOP_MAINPLL_D8 6 +#define CLK_TOP_MAINPLL_D16 7 +#define CLK_TOP_MAINPLL_D11 8 +#define CLK_TOP_MAINPLL_D22 9 +#define CLK_TOP_MAINPLL_D3 10 +#define CLK_TOP_MAINPLL_D6 11 +#define CLK_TOP_MAINPLL_D12 12 +#define CLK_TOP_MAINPLL_D5 13 +#define CLK_TOP_MAINPLL_D10 14 +#define CLK_TOP_MAINPLL_D20 15 +#define CLK_TOP_MAINPLL_D40 16 +#define CLK_TOP_MAINPLL_D7 17 +#define CLK_TOP_MAINPLL_D14 18 +#define CLK_TOP_UNIVPLL_D2 19 +#define CLK_TOP_UNIVPLL_D4 20 +#define CLK_TOP_UNIVPLL_D8 21 +#define CLK_TOP_UNIVPLL_D16 22 +#define CLK_TOP_UNIVPLL_D3 23 +#define CLK_TOP_UNIVPLL_D6 24 +#define CLK_TOP_UNIVPLL_D12 25 +#define CLK_TOP_UNIVPLL_D24 26 +#define CLK_TOP_UNIVPLL_D5 27 +#define CLK_TOP_UNIVPLL_D20 28 +#define CLK_TOP_MMPLL380M 29 +#define CLK_TOP_MMPLL_D2 30 +#define CLK_TOP_MMPLL_200M 31 +#define CLK_TOP_USB_PHY48M 32 +#define CLK_TOP_APLL1 33 +#define CLK_TOP_APLL1_D2 34 +#define CLK_TOP_APLL1_D4 35 +#define CLK_TOP_APLL1_D8 36 +#define CLK_TOP_APLL2 37 +#define CLK_TOP_APLL2_D2 38 +#define CLK_TOP_APLL2_D4 39 +#define CLK_TOP_APLL2_D8 40 +#define CLK_TOP_CLK26M 41 +#define CLK_TOP_CLK26M_D2 42 +#define CLK_TOP_AHB_INFRA_D2 43 +#define CLK_TOP_NFI1X 44 +#define CLK_TOP_ETH_D2 45 +#define CLK_TOP_UART0_SEL 46 +#define CLK_TOP_GFMUX_EMI1X_SEL 47 +#define CLK_TOP_EMI_DDRPHY_SEL 48 +#define CLK_TOP_AHB_INFRA_SEL 49 +#define CLK_TOP_CSW_MUX_MFG_SEL 50 +#define CLK_TOP_MSDC0_SEL 51 +#define CLK_TOP_PWM_MM_SEL 52 +#define CLK_TOP_UART1_SEL 53 +#define CLK_TOP_MSDC1_SEL 54 +#define CLK_TOP_SPM_52M_SEL 55 +#define CLK_TOP_PMICSPI_SEL 56 +#define CLK_TOP_QAXI_AUD26M_SEL 57 +#define CLK_TOP_AUD_INTBUS_SEL 58 +#define CLK_TOP_NFI2X_PAD_SEL 59 +#define CLK_TOP_NFI1X_PAD_SEL 60 +#define CLK_TOP_MFG_MM_SEL 61 +#define CLK_TOP_DDRPHYCFG_SEL 62 +#define CLK_TOP_USB_78M_SEL 63 +#define CLK_TOP_SPINOR_SEL 64 +#define CLK_TOP_MSDC2_SEL 65 +#define CLK_TOP_ETH_SEL 66 +#define CLK_TOP_AXI_MFG_IN_SEL 67 +#define CLK_TOP_SLOW_MFG_SEL 68 +#define CLK_TOP_AUD1_SEL 69 +#define CLK_TOP_AUD2_SEL 70 +#define CLK_TOP_AUD_ENGEN1_SEL 71 +#define CLK_TOP_AUD_ENGEN2_SEL 72 +#define CLK_TOP_I2C_SEL 73 +#define CLK_TOP_AUD_I2S0_M_SEL 74 +#define CLK_TOP_AUD_I2S1_M_SEL 75 +#define CLK_TOP_AUD_I2S2_M_SEL 76 +#define CLK_TOP_AUD_I2S3_M_SEL 77 +#define CLK_TOP_AUD_I2S4_M_SEL 78 +#define CLK_TOP_AUD_I2S5_M_SEL 79 +#define CLK_TOP_AUD_SPDIF_B_SEL 80 +#define CLK_TOP_PWM_SEL 81 +#define CLK_TOP_SPI_SEL 82 +#define CLK_TOP_AUD_SPDIFIN_SEL 83 +#define CLK_TOP_UART2_SEL 84 +#define CLK_TOP_BSI_SEL 85 +#define CLK_TOP_DBG_ATCLK_SEL 86 +#define CLK_TOP_CSW_NFIECC_SEL 87 +#define CLK_TOP_NFIECC_SEL 88 +#define CLK_TOP_APLL12_CK_DIV0 89 +#define CLK_TOP_APLL12_CK_DIV1 90 +#define CLK_TOP_APLL12_CK_DIV2 91 +#define CLK_TOP_APLL12_CK_DIV3 92 +#define CLK_TOP_APLL12_CK_DIV4 93 +#define CLK_TOP_APLL12_CK_DIV4B 94 +#define CLK_TOP_APLL12_CK_DIV5 95 +#define CLK_TOP_APLL12_CK_DIV5B 96 +#define CLK_TOP_APLL12_CK_DIV6 97 +#define CLK_TOP_NR_CLK 98 + +/* TOPCKGEN Gates */ +#define CLK_TOP_PWM_MM 0 +#define CLK_TOP_MFG_MM 1 +#define CLK_TOP_SPM_52M 2 +#define CLK_TOP_THEM 3 +#define CLK_TOP_APDMA 4 +#define CLK_TOP_I2C0 5 +#define CLK_TOP_I2C1 6 +#define CLK_TOP_AUXADC1 7 +#define CLK_TOP_NFI 8 +#define CLK_TOP_NFIECC 9 +#define CLK_TOP_DEBUGSYS 10 +#define CLK_TOP_PWM 11 +#define CLK_TOP_UART0 12 +#define CLK_TOP_UART1 13 +#define CLK_TOP_BTIF 14 +#define CLK_TOP_USB 15 +#define CLK_TOP_FLASHIF_26M 16 +#define CLK_TOP_AUXADC2 17 +#define CLK_TOP_I2C2 18 +#define CLK_TOP_MSDC0 19 +#define CLK_TOP_MSDC1 20 +#define CLK_TOP_NFI2X 21 +#define CLK_TOP_PMICWRAP_AP 22 +#define CLK_TOP_SEJ 23 +#define CLK_TOP_MEMSLP_DLYER 24 +#define CLK_TOP_SPI 25 +#define CLK_TOP_APXGPT 26 +#define CLK_TOP_AUDIO 27 +#define CLK_TOP_PMICWRAP_MD 28 +#define CLK_TOP_PMICWRAP_CONN 29 +#define CLK_TOP_PMICWRAP_26M 30 +#define CLK_TOP_AUX_ADC 31 +#define CLK_TOP_AUX_TP 32 +#define CLK_TOP_MSDC2 33 +#define CLK_TOP_RBIST 34 +#define CLK_TOP_NFI_BUS 35 +#define CLK_TOP_GCE 36 +#define CLK_TOP_TRNG 37 +#define CLK_TOP_SEJ_13M 38 +#define CLK_TOP_AES 39 +#define CLK_TOP_PWM_B 40 +#define CLK_TOP_PWM1_FB 41 +#define CLK_TOP_PWM2_FB 42 +#define CLK_TOP_PWM3_FB 43 +#define CLK_TOP_PWM4_FB 44 +#define CLK_TOP_PWM5_FB 45 +#define CLK_TOP_USB_1P 46 +#define CLK_TOP_FLASHIF_FREERUN 47 +#define CLK_TOP_66M_ETH 48 +#define CLK_TOP_133M_ETH 49 +#define CLK_TOP_FETH_25M 50 +#define CLK_TOP_FETH_50M 51 +#define CLK_TOP_FLASHIF_AXI 52 +#define CLK_TOP_USBIF 53 +#define CLK_TOP_UART2 54 +#define CLK_TOP_BSI 55 +#define CLK_TOP_MSDC0_INFRA 56 +#define CLK_TOP_MSDC1_INFRA 57 +#define CLK_TOP_MSDC2_INFRA 58 +#define CLK_TOP_USB_78M 59 +#define CLK_TOP_RG_SPINOR 60 +#define CLK_TOP_RG_MSDC2 61 +#define CLK_TOP_RG_ETH 62 +#define CLK_TOP_RG_AXI_MFG 63 +#define CLK_TOP_RG_SLOW_MFG 64 +#define CLK_TOP_RG_AUD1 65 +#define CLK_TOP_RG_AUD2 66 +#define CLK_TOP_RG_AUD_ENGEN1 67 +#define CLK_TOP_RG_AUD_ENGEN2 68 +#define CLK_TOP_RG_I2C 69 +#define CLK_TOP_RG_PWM_INFRA 70 +#define CLK_TOP_RG_AUD_SPDIF_IN 71 +#define CLK_TOP_RG_UART2 72 +#define CLK_TOP_RG_BSI 73 +#define CLK_TOP_RG_DBG_ATCLK 74 +#define CLK_TOP_RG_NFIECC 75 +#define CLK_TOP_RG_APLL1_D2_EN 76 +#define CLK_TOP_RG_APLL1_D4_EN 77 +#define CLK_TOP_RG_APLL1_D8_EN 78 +#define CLK_TOP_RG_APLL2_D2_EN 79 +#define CLK_TOP_RG_APLL2_D4_EN 80 +#define CLK_TOP_RG_APLL2_D8_EN 81 +#define CLK_TOP_APLL12_DIV0 82 +#define CLK_TOP_APLL12_DIV1 83 +#define CLK_TOP_APLL12_DIV2 84 +#define CLK_TOP_APLL12_DIV3 85 +#define CLK_TOP_APLL12_DIV4 86 +#define CLK_TOP_APLL12_DIV4B 87 +#define CLK_TOP_APLL12_DIV5 88 +#define CLK_TOP_APLL12_DIV5B 89 +#define CLK_TOP_APLL12_DIV6 90 + +/* INFRACFG */ + +#define CLK_IFR_MUX1_SEL 0 +#define CLK_IFR_ETH_25M_SEL 1 +#define CLK_IFR_I2C0_SEL 2 +#define CLK_IFR_I2C1_SEL 3 +#define CLK_IFR_I2C2_SEL 4 +#define CLK_IFR_NR_CLK 5 + +/* AUDIOTOP */ + +#define CLK_AUD_AFE 0 +#define CLK_AUD_I2S 1 +#define CLK_AUD_22M 2 +#define CLK_AUD_24M 3 +#define CLK_AUD_INTDIR 4 +#define CLK_AUD_APLL2_TUNER 5 +#define CLK_AUD_APLL_TUNER 6 +#define CLK_AUD_HDMI 7 +#define CLK_AUD_SPDF 8 +#define CLK_AUD_ADC 9 +#define CLK_AUD_DAC 10 +#define CLK_AUD_DAC_PREDIS 11 +#define CLK_AUD_TML 12 +#define CLK_AUD_NR_CLK 13 + +/* MFGCFG */ + +#define CLK_MFG_BAXI 0 +#define CLK_MFG_BMEM 1 +#define CLK_MFG_BG3D 2 +#define CLK_MFG_B26M 3 +#define CLK_MFG_NR_CLK 4 + +#endif /* _DT_BINDINGS_CLK_MT8516_H */ diff --git a/include/exception.h b/include/exception.h new file mode 100644 index 0000000..fc02490 --- /dev/null +++ b/include/exception.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * The 'exception' command can be used for testing exception handling. + * + * Copyright (c) 2018, Heinrich Schuchardt <xypron.glpk@gmx.de> + */ + +static int do_exception(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + cmd_tbl_t *cp; + + if (argc != 2) + return CMD_RET_USAGE; + + /* drop sub-command parameter */ + argc--; + argv++; + + cp = find_cmd_tbl(argv[0], cmd_sub, ARRAY_SIZE(cmd_sub)); + + if (cp) + return cp->cmd(cmdtp, flag, argc, argv); + + return CMD_RET_USAGE; +} + +static int exception_complete(int argc, char * const argv[], char last_char, + int maxv, char *cmdv[]) +{ + int len = 0; + int i = 0; + cmd_tbl_t *cmdtp; + + switch (argc) { + case 1: + break; + case 2: + len = strlen(argv[1]); + break; + default: + return 0; + } + for (cmdtp = cmd_sub; cmdtp != cmd_sub + ARRAY_SIZE(cmd_sub); cmdtp++) { + if (i >= maxv - 1) + return i; + if (!strncmp(argv[1], cmdtp->name, len)) + cmdv[i++] = cmdtp->name; + } + cmdv[i] = NULL; + return i; +} + +U_BOOT_CMD_COMPLETE( + exception, 2, 0, do_exception, + "Forces an exception to occur", + exception_help_text, exception_complete +); diff --git a/include/image.h b/include/image.h index 765ffec..889305c 100644 --- a/include/image.h +++ b/include/image.h @@ -306,6 +306,7 @@ enum { IH_COMP_COUNT, }; +#define LZ4F_MAGIC 0x184D2204 /* LZ4 Magic Number */ #define IH_MAGIC 0x27051956 /* Image Magic Number */ #define IH_NMLEN 32 /* Image Name Length */ @@ -1312,6 +1313,7 @@ int android_image_get_second(const struct andr_img_hdr *hdr, ulong *second_data, ulong *second_len); ulong android_image_get_end(const struct andr_img_hdr *hdr); ulong android_image_get_kload(const struct andr_img_hdr *hdr); +ulong android_image_get_kcomp(const struct andr_img_hdr *hdr); void android_print_contents(const struct andr_img_hdr *hdr); #endif /* CONFIG_ANDROID_BOOT_IMAGE */ diff --git a/include/linux/completion.h b/include/linux/completion.h new file mode 100644 index 0000000..9835826 --- /dev/null +++ b/include/linux/completion.h @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LINUX_COMPLETION_H +#define __LINUX_COMPLETION_H + +/* + * (C) Copyright 2001 Linus Torvalds + * + * Atomic wait-for-completion handler data structures. + * See kernel/sched/completion.c for details. + */ +#ifndef __UBOOT__ +#include <linux/wait.h> +#endif /* __UBOOT__ */ + +/* + * struct completion - structure used to maintain state for a "completion" + * + * This is the opaque structure used to maintain the state for a "completion". + * Completions currently use a FIFO to queue threads that have to wait for + * the "completion" event. + * + * See also: complete(), wait_for_completion() (and friends _timeout, + * _interruptible, _interruptible_timeout, and _killable), init_completion(), + * reinit_completion(), and macros DECLARE_COMPLETION(), + * DECLARE_COMPLETION_ONSTACK(). + */ +struct completion { + unsigned int done; +#ifndef __UBOOT__ + wait_queue_head_t wait; +#endif /* __UBOOT__ */ +}; + +#define init_completion_map(x, m) __init_completion(x) +#define init_completion(x) __init_completion(x) +static inline void complete_acquire(struct completion *x) {} +static inline void complete_release(struct completion *x) {} + +#define COMPLETION_INITIALIZER(work) \ + { 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait) } + +#define COMPLETION_INITIALIZER_ONSTACK_MAP(work, map) \ + (*({ init_completion_map(&(work), &(map)); &(work); })) + +#define COMPLETION_INITIALIZER_ONSTACK(work) \ + (*({ init_completion(&work); &work; })) + +/** + * DECLARE_COMPLETION - declare and initialize a completion structure + * @work: identifier for the completion structure + * + * This macro declares and initializes a completion structure. Generally used + * for static declarations. You should use the _ONSTACK variant for automatic + * variables. + */ +#define DECLARE_COMPLETION(work) \ + struct completion work = COMPLETION_INITIALIZER(work) + +/* + * Lockdep needs to run a non-constant initializer for on-stack + * completions - so we use the _ONSTACK() variant for those that + * are on the kernel stack: + */ +/** + * DECLARE_COMPLETION_ONSTACK - declare and initialize a completion structure + * @work: identifier for the completion structure + * + * This macro declares and initializes a completion structure on the kernel + * stack. + */ +#ifdef CONFIG_LOCKDEP +# define DECLARE_COMPLETION_ONSTACK(work) \ + struct completion work = COMPLETION_INITIALIZER_ONSTACK(work) +# define DECLARE_COMPLETION_ONSTACK_MAP(work, map) \ + struct completion work = COMPLETION_INITIALIZER_ONSTACK_MAP(work, map) +#else +# define DECLARE_COMPLETION_ONSTACK(work) DECLARE_COMPLETION(work) +# define DECLARE_COMPLETION_ONSTACK_MAP(work, map) DECLARE_COMPLETION(work) +#endif + +/** + * init_completion - Initialize a dynamically allocated completion + * @x: pointer to completion structure that is to be initialized + * + * This inline function will initialize a dynamically created completion + * structure. + */ +static inline void __init_completion(struct completion *x) +{ + x->done = 0; +#ifndef __UBOOT__ + init_waitqueue_head(&x->wait); +#endif /* __UBOOT__ */ +} + +/** + * reinit_completion - reinitialize a completion structure + * @x: pointer to completion structure that is to be reinitialized + * + * This inline function should be used to reinitialize a completion structure so it can + * be reused. This is especially important after complete_all() is used. + */ +static inline void reinit_completion(struct completion *x) +{ + x->done = 0; +} + +#ifndef __UBOOT__ +extern void wait_for_completion(struct completion *); +extern void wait_for_completion_io(struct completion *); +extern int wait_for_completion_interruptible(struct completion *x); +extern int wait_for_completion_killable(struct completion *x); +extern unsigned long wait_for_completion_timeout(struct completion *x, + unsigned long timeout); +extern unsigned long wait_for_completion_io_timeout(struct completion *x, + unsigned long timeout); +extern long wait_for_completion_interruptible_timeout( + struct completion *x, unsigned long timeout); +extern long wait_for_completion_killable_timeout( + struct completion *x, unsigned long timeout); +extern bool try_wait_for_completion(struct completion *x); +extern bool completion_done(struct completion *x); + +extern void complete(struct completion *); +extern void complete_all(struct completion *); + +#else /* __UBOOT __ */ + +#define wait_for_completion(x) do {} while (0) +#define wait_for_completion_io(x) do {} while (0) + +inline int wait_for_completion_interruptible(struct completion *x) +{ + return 1; +} +inline int wait_for_completion_killable(struct completion *x) +{ + return 1; +} +inline unsigned long wait_for_completion_timeout(struct completion *x, + unsigned long timeout) +{ + return 1; +} +inline unsigned long wait_for_completion_io_timeout(struct completion *x, + unsigned long timeout) +{ + return 1; +} +inline long wait_for_completion_interruptible_timeout(struct completion *x, + unsigned long timeout) +{ + return 1; +} +inline long wait_for_completion_killable_timeout(struct completion *x, + unsigned long timeout) +{ + return 1; +} +inline bool try_wait_for_completion(struct completion *x) +{ + return 1; +} +inline bool completion_done(struct completion *x) +{ + return 1; +} + +#define complete(x) do {} while (0) +#define complete_all(x) do {} while (0) +#endif /* __UBOOT__ */ + +#endif diff --git a/include/linux/io.h b/include/linux/io.h index 9badab4..7984788 100644 --- a/include/linux/io.h +++ b/include/linux/io.h @@ -65,8 +65,8 @@ static inline void __iomem *ioremap(resource_size_t offset, static inline void iounmap(void __iomem *addr) { } +#endif #define devm_ioremap(dev, offset, size) ioremap(offset, size) -#endif #endif /* _LINUX_IO_H */ diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 9f5dc81..bd373b9 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -15,6 +15,7 @@ #include <config.h> +#include <dm/device.h> #include <linux/compat.h> #include <linux/mtd/mtd.h> #include <linux/mtd/flashchip.h> @@ -498,6 +499,13 @@ struct nand_hw_control { struct nand_chip *active; }; +static inline void nand_hw_control_init(struct nand_hw_control *nfc) +{ + nfc->active = NULL; + spin_lock_init(&nfc->lock); + init_waitqueue_head(&nfc->wq); +} + /** * struct nand_ecc_step_info - ECC step information of ECC engine * @stepsize: data bytes per ECC step @@ -961,6 +969,17 @@ struct nand_chip { void *priv; }; +static inline void nand_set_flash_node(struct nand_chip *chip, + ofnode node) +{ + chip->flash_node = ofnode_to_offset(node); +} + +static inline ofnode nand_get_flash_node(struct nand_chip *chip) +{ + return offset_to_ofnode(chip->flash_node); +} + static inline struct nand_chip *mtd_to_nand(struct mtd_info *mtd) { return container_of(mtd, struct nand_chip, mtd); @@ -1280,4 +1299,34 @@ int nand_maximize_ecc(struct nand_chip *chip, /* Reset and initialize a NAND device */ int nand_reset(struct nand_chip *chip, int chipnr); + +/* NAND operation helpers */ +int nand_reset_op(struct nand_chip *chip); +int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf, + unsigned int len); +int nand_status_op(struct nand_chip *chip, u8 *status); +int nand_exit_status_op(struct nand_chip *chip); +int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock); +int nand_read_page_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, void *buf, unsigned int len); +int nand_change_read_column_op(struct nand_chip *chip, + unsigned int offset_in_page, void *buf, + unsigned int len, bool force_8bit); +int nand_read_oob_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, void *buf, unsigned int len); +int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, const void *buf, + unsigned int len); +int nand_prog_page_end_op(struct nand_chip *chip); +int nand_prog_page_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, const void *buf, + unsigned int len); +int nand_change_write_column_op(struct nand_chip *chip, + unsigned int offset_in_page, const void *buf, + unsigned int len, bool force_8bit); +int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len, + bool force_8bit); +int nand_write_data_op(struct nand_chip *chip, const void *buf, + unsigned int len, bool force_8bit); + #endif /* __LINUX_MTD_RAWNAND_H */ diff --git a/include/malloc.h b/include/malloc.h index b714fed..5efa692 100644 --- a/include/malloc.h +++ b/include/malloc.h @@ -878,7 +878,6 @@ extern Void_t* sbrk(); #define memalign memalign_simple static inline void free(void *ptr) {} void *calloc(size_t nmemb, size_t size); -void *memalign_simple(size_t alignment, size_t bytes); void *realloc_simple(void *ptr, size_t size); void malloc_simple_info(void); #else @@ -914,6 +913,7 @@ int initf_malloc(void); /* Simple versions which can be used when space is tight */ void *malloc_simple(size_t size); +void *memalign_simple(size_t alignment, size_t bytes); #pragma GCC visibility push(hidden) # if __STD_C diff --git a/include/pci.h b/include/pci.h index 5fb212c..9668503 100644 --- a/include/pci.h +++ b/include/pci.h @@ -545,7 +545,11 @@ extern void pci_cfgfunc_do_nothing(struct pci_controller* hose, pci_dev_t dev, extern void pci_cfgfunc_config_device(struct pci_controller* hose, pci_dev_t dev, struct pci_config_table *); -#define MAX_PCI_REGIONS 7 +#ifdef CONFIG_NR_DRAM_BANKS +#define MAX_PCI_REGIONS (CONFIG_NR_DRAM_BANKS + 7) +#else +#define MAX_PCI_REGIONS 7 +#endif #define INDIRECT_TYPE_NO_PCIE_LINK 1 diff --git a/include/reset.h b/include/reset.h index 65aa7a4..a1a9ad5 100644 --- a/include/reset.h +++ b/include/reset.h @@ -43,6 +43,8 @@ struct udevice; * @data: An optional data field for scenarios where a single integer ID is not * sufficient. If used, it can be populated through an .of_xlate op and * processed during the various reset ops. + * @polarity: An optional polarity field for drivers that support + * different reset polarities. * * Should additional information to identify and configure any reset signal * for any provider be required in the future, the struct could be expanded to @@ -59,6 +61,7 @@ struct reset_ctl { */ unsigned long id; unsigned long data; + unsigned long polarity; }; /** diff --git a/lib/lz4_wrapper.c b/lib/lz4_wrapper.c index 487d39e..1c68e67 100644 --- a/lib/lz4_wrapper.c +++ b/lib/lz4_wrapper.c @@ -5,6 +5,7 @@ #include <common.h> #include <compiler.h> +#include <image.h> #include <linux/kernel.h> #include <linux/types.h> @@ -23,8 +24,6 @@ typedef uint64_t U64; /* Unaltered (except removing unrelated code) from github.com/Cyan4973/lz4. */ #include "lz4.c" /* #include for inlining, do not link! */ -#define LZ4F_MAGIC 0x184D2204 - struct lz4_frame_header { u32 magic; union { diff --git a/test/env/Kconfig b/test/env/Kconfig index ff16413..6cb8233 100644 --- a/test/env/Kconfig +++ b/test/env/Kconfig @@ -1,6 +1,7 @@ config UT_ENV bool "Enable env unit tests" depends on UNIT_TEST + default y help This enables the 'ut env' command which runs a series of unit tests on the env code. diff --git a/test/lib/hexdump.c b/test/lib/hexdump.c index 567b576..5dccf43 100644 --- a/test/lib/hexdump.c +++ b/test/lib/hexdump.c @@ -6,7 +6,8 @@ #include <common.h> #include <hexdump.h> -#include <dm/test.h> +#include <test/lib.h> +#include <test/test.h> #include <test/ut.h> static int lib_test_hex_to_bin(struct unit_test_state *uts) @@ -32,7 +33,7 @@ static int lib_test_hex_to_bin(struct unit_test_state *uts) return 0; } -DM_TEST(lib_test_hex_to_bin, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); +LIB_TEST(lib_test_hex_to_bin, 0); static int lib_test_hex2bin(struct unit_test_state *uts) { @@ -62,7 +63,7 @@ static int lib_test_hex2bin(struct unit_test_state *uts) return 0; } -DM_TEST(lib_test_hex2bin, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); +LIB_TEST(lib_test_hex2bin, 0); static int lib_test_bin2hex(struct unit_test_state *uts) { @@ -92,4 +93,4 @@ static int lib_test_bin2hex(struct unit_test_state *uts) return 0; } -DM_TEST(lib_test_bin2hex, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); +LIB_TEST(lib_test_bin2hex, 0); |