arm: add support for SoC s5p4418 (cpu) / nanopi2 board
Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01:
- SPL not supported yet --> no spl-dir in arch/arm/cpu/armv7/s5p4418/.
Appropriate line in Makefile removed.
- cpu.c: '#include <cpu_func.h>' added.
- arch/arm/cpu/armv7/s5p4418/u-boot.lds removed, is not required
anylonger.
- "obj-$(CONFIG_ARCH_NEXELL) += s5p-common/" added to
arch/arm/cpu/armv7/Makefile since s5p-common/pwm.c is used instead
of drivers/pwm/pwm-nexell.c.
- s5p4418.dtsi: '#include "../../../include/generated/autoconf.h"'
removed, is not necessary, error at out-of-tree building.
'#ifdef CONFIG_CPU_NXP4330'-blocks (2x) removed. Some minor changes
regarding mmc. 'u-boot,dm-pre-reloc' added to dp0 because of added
DM_VIDEO support.
- board/s5p4418/ renamed to board/friendlyarm/
- All s5p4418-boards except nanopi2 removed because there is no
possibility to test the other boards.
- Kconfig: Changes to have a structure like mach-bcm283x (RaspberryPi),
e.g. "config ..." entries moved from/to other Kconfig.
- "CONFIG_" removed from several s5p4418/nanopi2 specific defines
because the appropriate values do not need to be configurable.
- nanopi2/board.c: All getenv(), getenv_ulong(), setenv() and saveenv()
renamed to env_get(), env_get_ulong(), env_set() and env_save(),
respectively. MACH_TYPE_S5P4418 is not defined anymore, therefore
appropriate code removed (not necessary for DT-kernels).
- nanopi2/onewire.c: All crc8() renamed to crc8_ow() because crc8() is
already defined in lib/crc8.c (with different parameters).
- dts: "nexell,s5pxx18-i2c" used instead of "i2c-gpio", i2c0 and
i2c1 added. gmac-, ehci- and dwc2otg-entries removed because the
appropriate functionality is not supported yet. New mmc-property
"mmcboost" added.
s5p4418-pinctrl.dtsi: gmac-entries removed, mmc- and i2c-entries
added.
- '#ifdef CONFIG...' changed to 'if (IS_ENABLED(CONFIG...))' where
possible (and similar).
Signed-off-by: Stefan Bosch <stefan_b@posteo.net>
diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile
index 8c955d0..0e83e39 100644
--- a/arch/arm/cpu/armv7/Makefile
+++ b/arch/arm/cpu/armv7/Makefile
@@ -42,3 +42,5 @@
obj-$(if $(filter stv0991,$(SOC)),y) += stv0991/
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_VF610) += vf610/
+obj-$(CONFIG_ARCH_S5P4418) += s5p4418/
+obj-$(CONFIG_ARCH_NEXELL) += s5p-common/
diff --git a/arch/arm/cpu/armv7/s5p4418/Makefile b/arch/arm/cpu/armv7/s5p4418/Makefile
new file mode 100644
index 0000000..321b257
--- /dev/null
+++ b/arch/arm/cpu/armv7/s5p4418/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2016 Nexell
+# Hyunseok, Jung <hsjung@nexell.co.kr>
+
+obj-y += cpu.o
diff --git a/arch/arm/cpu/armv7/s5p4418/cpu.c b/arch/arm/cpu/armv7/s5p4418/cpu.c
new file mode 100644
index 0000000..8add947
--- /dev/null
+++ b/arch/arm/cpu/armv7/s5p4418/cpu.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2016 Nexell
+ * Hyunseok, Jung <hsjung@nexell.co.kr>
+ */
+
+#include <common.h>
+#include <command.h>
+#include <asm/system.h>
+#include <asm/cache.h>
+#include <asm/sections.h>
+#include <asm/io.h>
+#include <asm/arch/nexell.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/reset.h>
+#include <asm/arch/tieoff.h>
+#include <cpu_func.h>
+#include <linux/delay.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#ifndef CONFIG_ARCH_CPU_INIT
+#error must be define the macro "CONFIG_ARCH_CPU_INIT"
+#endif
+
+void s_init(void)
+{
+}
+
+static void cpu_soc_init(void)
+{
+ /*
+ * NOTE> ALIVE Power Gate must enable for Alive register access.
+ * must be clear wfi jump address
+ */
+ writel(1, ALIVEPWRGATEREG);
+ writel(0xFFFFFFFF, SCR_ARM_SECOND_BOOT);
+
+ /* write 0xf0 on alive scratchpad reg for boot success check */
+ writel(readl(SCR_SIGNAGURE_READ) | 0xF0, (SCR_SIGNAGURE_SET));
+
+ /* set l2 cache tieoff */
+ nx_tieoff_set(NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_L2RET1N_0, 1);
+ nx_tieoff_set(NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_L2RET1N_1, 1);
+}
+
+#ifdef CONFIG_PL011_SERIAL
+static void serial_device_init(void)
+{
+ char dev[10];
+ int id;
+
+ sprintf(dev, "nx-uart.%d", CONFIG_CONS_INDEX);
+ id = RESET_ID_UART0 + CONFIG_CONS_INDEX;
+
+ struct clk *clk = clk_get((const char *)dev);
+
+ /* reset control: Low active ___|--- */
+ nx_rstcon_setrst(id, RSTCON_ASSERT);
+ udelay(10);
+ nx_rstcon_setrst(id, RSTCON_NEGATE);
+ udelay(10);
+
+ /* set clock */
+ clk_disable(clk);
+ clk_set_rate(clk, CONFIG_PL011_CLOCK);
+ clk_enable(clk);
+}
+#endif
+
+int arch_cpu_init(void)
+{
+ flush_dcache_all();
+ cpu_soc_init();
+ clk_init();
+
+ if (IS_ENABLED(CONFIG_PL011_SERIAL))
+ serial_device_init();
+
+ return 0;
+}
+
+#if defined(CONFIG_DISPLAY_CPUINFO)
+int print_cpuinfo(void)
+{
+ return 0;
+}
+#endif
+
+void reset_cpu(ulong ignored)
+{
+ void *clkpwr_reg = (void *)PHY_BASEADDR_CLKPWR;
+ const u32 sw_rst_enb_bitpos = 3;
+ const u32 sw_rst_enb_mask = 1 << sw_rst_enb_bitpos;
+ const u32 sw_rst_bitpos = 12;
+ const u32 sw_rst_mask = 1 << sw_rst_bitpos;
+ int pwrcont = 0x224;
+ int pwrmode = 0x228;
+ u32 read_value;
+
+ read_value = readl((void *)(clkpwr_reg + pwrcont));
+
+ read_value &= ~sw_rst_enb_mask;
+ read_value |= 1 << sw_rst_enb_bitpos;
+
+ writel(read_value, (void *)(clkpwr_reg + pwrcont));
+ writel(sw_rst_mask, (void *)(clkpwr_reg + pwrmode));
+}
+
+void enable_caches(void)
+{
+ /* Enable D-cache. I-cache is already enabled in start.S */
+ dcache_enable();
+}
+
+#if defined(CONFIG_ARCH_MISC_INIT)
+int arch_misc_init(void)
+{
+ return 0;
+}
+#endif /* CONFIG_ARCH_MISC_INIT */
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index f01eb7b..a3a1e3f 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -147,6 +147,9 @@
rv1108-elgin-r1.dtb \
rv1108-evb.dtb
+dtb-$(CONFIG_ARCH_S5P4418) += \
+ s5p4418-nanopi2.dtb
+
dtb-$(CONFIG_ARCH_MESON) += \
meson-gxbb-nanopi-k2.dtb \
meson-gxbb-odroidc2.dtb \
diff --git a/arch/arm/dts/s5p4418-nanopi2.dts b/arch/arm/dts/s5p4418-nanopi2.dts
new file mode 100644
index 0000000..4deaf10
--- /dev/null
+++ b/arch/arm/dts/s5p4418-nanopi2.dts
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2020 Stefan Bosch <stefan_b@posteo.net>
+ *
+ * (C) Copyright 2017 FriendlyElec Computer Tech. Co., Ltd.
+ * (http://www.friendlyarm.com)
+ *
+ * (C) Copyright 2016 Nexell
+ * Youngbok, Park <park@nexell.co.kr>
+ */
+
+/dts-v1/;
+#include "s5p4418.dtsi"
+
+/ {
+ model = "FriendlyElec boards based on Nexell s5p4418";
+ cpu-model = "S5p4418";
+
+ compatible = "friendlyelec,nanopi2",
+ "nexell,s5p4418";
+
+ aliases {
+ mmc0 = "/mmc@c0069000";
+ mmc1 = "/mmc@c0062000";
+ i2c0 = "/i2c@c00a4000";
+ i2c1 = "/i2c@c00a5000";
+ i2c2 = "/i2c@c00a6000";
+ };
+
+ mmc0:mmc@c0062000 {
+ frequency = <50000000>;
+ drive_dly = <0x0>;
+ drive_shift = <0x03>;
+ sample_dly = <0x00>;
+ sample_shift = <0x02>;
+ mmcboost = <0>;
+ status = "okay";
+ };
+
+ mmc2:mmc@c0069000 {
+ frequency = <50000000>;
+ drive_dly = <0x0>;
+ drive_shift = <0x03>;
+ sample_dly = <0x00>;
+ sample_shift = <0x02>;
+ mmcboost = <0>;
+ status = "okay";
+ };
+
+ /* NanoPi2: Header "CON2", NanoPC-T2: EEPROM (MAC-Addr.) and Audio */
+ i2c0:i2c@c00a4000 {
+ status ="okay";
+ };
+
+ /* NanoPi2: Header "CON2" and HDMI, NanoPC-T2: HDMI */
+ i2c1:i2c@c00a5000 {
+ status ="okay";
+ };
+
+ /* NanoPi2: LCD interface, NanoPC-T2: LCD, LVDS and MIPI interfaces */
+ i2c2:i2c@c00a6000 {
+ status ="okay";
+ };
+
+ dp0:dp@c0102800 {
+ status = "okay";
+ module = <0>;
+ lcd-type = "lvds";
+
+ dp-device {
+ format = <0>; /* 0:VESA, 1:JEIDA */
+ };
+
+ dp-sync {
+ h_active_len = <1024>;
+ h_front_porch = <84>;
+ h_back_porch = <84>;
+ h_sync_width = <88>;
+ h_sync_invert = <0>;
+ v_active_len = <600>;
+ v_front_porch = <10>;
+ v_back_porch = <10>;
+ v_sync_width = <20>;
+ v_sync_invert = <0>;
+ };
+
+ dp-ctrl {
+ clk_src_lv0 = <3>;
+ clk_div_lv0 = <16>;
+ clk_src_lv1 = <7>;
+ clk_div_lv1 = <1>;
+ out_format = <2>;
+ };
+
+ dp-planes {
+ layer_top {
+ screen_width = <1024>;
+ screen_height = <600>;
+ back_color = <0x0>;
+ };
+
+ layer_1 { /* RGB 1 */
+ width = <1024>;
+ height = <600>;
+ format = <0x06530000>;
+ pixel_byte = <4>;
+ };
+ };
+ };
+};
diff --git a/arch/arm/dts/s5p4418-pinctrl.dtsi b/arch/arm/dts/s5p4418-pinctrl.dtsi
new file mode 100644
index 0000000..a7e1c2c
--- /dev/null
+++ b/arch/arm/dts/s5p4418-pinctrl.dtsi
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Nexell's s5p6818 SoC pin-mux and pin-config device tree source
+ *
+ * (C) Copyright 2020 Stefan Bosch <stefan_b@posteo.net>
+ *
+ * Copyright (C) 2016 Nexell Co., Ltd.
+ * http://www.nexell.co.kr
+ *
+ * Nexell's s5p6818 SoC pin-mux and pin-config options are listed as
+ * device tree nodes in this file.
+ */
+
+pinctrl@C0010000 {
+ /*
+ * values for "pin-pull":
+ * pulldown resistor = 0
+ * pullup = 1
+ * no pullup/down = 2
+ */
+
+ /* MMC */
+ mmc0_clk: mmc0-clk {
+ pins = "gpioa-29";
+ pin-function = <1>;
+ pin-pull = <2>;
+ pin-strength = <2>;
+ };
+
+ mmc0_cmd: mmc0-cmd {
+ pins = "gpioa-31";
+ pin-function = <1>;
+ pin-pull = <2>;
+ pin-strength = <1>;
+ };
+
+ mmc0_bus4: mmc0-bus-width4 {
+ pins = "gpiob-1, gpiob-3, gpiob-5, gpiob-7";
+ pin-function = <1>;
+ pin-pull = <2>;
+ pin-strength = <1>;
+ };
+
+ mmc1_clk: mmc1-clk {
+ pins = "gpiod-22";
+ pin-function = <1>;
+ pin-pull = <2>;
+ pin-strength = <2>;
+ };
+
+ mmc1_cmd: mmc1-cmd {
+ pins = "gpiod-23";
+ pin-function = <1>;
+ pin-pull = <2>;
+ pin-strength = <1>;
+ };
+
+ mmc1_bus4: mmc1-bus-width4 {
+ pins = "gpiod-24, gpiod-25, gpiod-26, gpiod-27";
+ pin-function = <1>;
+ pin-pull = <2>;
+ pin-strength = <1>;
+ };
+
+ mmc2_clk: mmc2-clk {
+ pins = "gpioc-18";
+ pin-function = <2>;
+ pin-pull = <2>;
+ pin-strength = <2>;
+ };
+
+ mmc2_cmd: mmc2-cmd {
+ pins = "gpioc-19";
+ pin-function = <2>;
+ pin-pull = <2>;
+ pin-strength = <1>;
+ };
+
+ mmc2_bus4: mmc2-bus-width4 {
+ pins = "gpioc-20, gpioc-21, gpioc-22, gpioc-23";
+ pin-function = <2>;
+ pin-pull = <2>;
+ pin-strength = <1>;
+ };
+
+ mmc2_bus8: mmc2-bus-width8 {
+ nexell,pins = "gpioe-21", "gpioe-22", "gpioe-23", "gpioe-24";
+ pin-function = <2>;
+ pin-pull = <2>;
+ pin-strength = <1>;
+ };
+
+ /* I2C */
+ i2c0_sda:i2c0-sda {
+ pins = "gpiod-3";
+ pin-function = <1>;
+ pin-pull = <2>;
+ pin-strength = <0>;
+ };
+
+ i2c0_scl:i2c0-scl {
+ pins = "gpiod-2";
+ pin-function = <1>;
+ pin-pull = <2>;
+ pin-strength = <0>;
+ };
+
+ i2c1_sda:i2c1-sda {
+ pins = "gpiod-5";
+ pin-function = <1>;
+ pin-pull = <2>;
+ pin-strength = <0>;
+ };
+
+ i2c1_scl:i2c1-scl {
+ pins = "gpiod-4";
+ pin-function = <1>;
+ pin-pull = <2>;
+ pin-strength = <0>;
+ };
+
+ i2c2_sda:i2c2-sda {
+ pins = "gpiod-7";
+ pin-function = <1>;
+ pin-pull = <2>;
+ pin-strength = <0>;
+ };
+
+ i2c2_scl:i2c2-scl {
+ pins = "gpiod-6";
+ pin-function = <1>;
+ pin-pull = <2>;
+ pin-strength = <0>;
+ };
+};
diff --git a/arch/arm/dts/s5p4418.dtsi b/arch/arm/dts/s5p4418.dtsi
new file mode 100644
index 0000000..a4d1a1b
--- /dev/null
+++ b/arch/arm/dts/s5p4418.dtsi
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2020 Stefan Bosch <stefan_b@posteo.net>
+ *
+ * (C) Copyright 2016 Nexell
+ * Youngbok, Park <park@nexell.co.kr>
+ *
+ */
+
+#include "skeleton.dtsi"
+
+/ {
+ #include "s5p4418-pinctrl.dtsi"
+
+ aliases {
+ mmc0 = &mmc0;
+ mmc1 = &mmc1;
+ mmc2 = &mmc2;
+ gmac = "/ethernet@c0060000";
+ };
+
+ mmc2:mmc@c0069000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "nexell,nexell-dwmmc";
+ reg = <0xc0069000 0x1000>;
+ bus-width = <4>;
+ index = <2>;
+ max-frequency = <50000000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc2_clk>, <&mmc2_cmd>, <&mmc2_bus4>;
+ status = "disabled";
+ };
+
+ mmc1:mmc@c0068000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "nexell,nexell-dwmmc";
+ reg = <0xc0068000 0x1000>;
+ bus-width = <4>;
+ index = <1>;
+ max-frequency = <50000000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc1_clk>, <&mmc1_cmd>, <&mmc1_bus4>;
+ status = "disabled";
+ };
+
+ mmc0:mmc@c0062000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "nexell,nexell-dwmmc";
+ reg = <0xc0062000 0x1000>;
+ bus-width = <4>;
+ index = <0>;
+ max-frequency = <50000000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc0_clk>, <&mmc0_cmd>, <&mmc0_bus4>;
+ status = "disabled";
+ };
+
+ i2c0:i2c@c00a4000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "nexell,s5pxx18-i2c";
+ reg = <0xc00a4000 0x100>;
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c0_sda>, <&i2c0_scl>;
+ status ="disabled";
+ };
+
+ i2c1:i2c@c00a5000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "nexell,s5pxx18-i2c";
+ reg = <0xc00a5000 0x100>;
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_sda>, <&i2c1_scl>;
+ status ="disabled";
+ };
+
+ i2c2:i2c@c00a6000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "nexell,s5pxx18-i2c";
+ reg = <0xc00a6000 0x100>;
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c2_sda>, <&i2c2_scl>;
+ status ="disabled";
+ };
+
+ dp0:dp@c0102800 {
+ compatible = "nexell,nexell-display";
+ reg = <0xc0102800 0x100>;
+ index = <0>;
+ u-boot,dm-pre-reloc;
+ status = "disabled";
+ };
+
+ dp1:dp@c0102c00 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "nexell,nexell-display";
+ reg = <0xc0102c00 0x100>;
+ index = <1>;
+ status = "disabled";
+ };
+
+ gpio_a:gpio@c001a000 {
+ compatible = "nexell,nexell-gpio";
+ reg = <0xc001a000 0x00000010>;
+ altr,gpio-bank-width = <32>;
+ gpio-bank-name = "gpio_a";
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpio_b:gpio@c001b000 {
+ compatible = "nexell,nexell-gpio";
+ reg = <0xc001b000 0x00000010>;
+ altr,gpio-bank-width = <32>;
+ gpio-bank-name = "gpio_b";
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpio_c:gpio@c001c000 {
+ compatible = "nexell,nexell-gpio";
+ reg = <0xc001c000 0x00000010>;
+ nexell,gpio-bank-width = <32>;
+ gpio-bank-name = "gpio_c";
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpio_d:gpio@c001d000 {
+ compatible = "nexell,nexell-gpio";
+ reg = <0xc001d000 0x00000010>;
+ nexell,gpio-bank-width = <32>;
+ gpio-bank-name = "gpio_d";
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpio_e:gpio@c001e000 {
+ compatible = "nexell,nexell-gpio";
+ reg = <0xc001e000 0x00000010>;
+ nexell,gpio-bank-width = <32>;
+ gpio-bank-name = "gpio_e";
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpio_alv:gpio@c0010800 {
+ compatible = "nexell,nexell-gpio";
+ reg = <0xc0010800 0x00000010>;
+ nexell,gpio-bank-width = <32>;
+ gpio-bank-name = "gpio_alv";
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ pinctrl@C0010000 {
+ compatible = "nexell,s5pxx18-pinctrl";
+ reg = <0xc0010000 0xf000>;
+ u-boot,dm-pre-reloc;
+ };
+};
diff --git a/board/friendlyarm/Kconfig b/board/friendlyarm/Kconfig
new file mode 100644
index 0000000..f8f9cfd
--- /dev/null
+++ b/board/friendlyarm/Kconfig
@@ -0,0 +1,37 @@
+choice
+ prompt "LCD backlight control"
+ optional
+ default S5P4418_ONEWIRE
+
+config S5P4418_ONEWIRE
+ bool "I2C / 1-Wire"
+ help
+ This enables LCD-Backlight control for FriendlyARM LCD-panels.
+ I2C is used if available, otherwise 1-Wire is used.
+
+config PWM_NX
+ bool "PWM"
+ help
+ This enables LCD-Backlight control via PWM.
+endchoice
+
+config ROOT_DEV
+ int "ROOT_DEV"
+ help
+ Environment variable rootdev is set to this value if env. var. firstboot
+ does not exist. Otherwise rootdev is set to the MMC boot device. rootdev
+ determines (together with env. var. bootpart) where the OS (linux) is
+ booted from.
+
+config BOOT_PART
+ int "BOOT_PART"
+ help
+ Environment variable bootpart is set to this value. bootpart determines
+ (together with env. var. rootdev) where the OS (linux) is booted from.
+
+config ROOT_PART
+ int "ROOT_PART"
+ help
+ Environment variable rootpart is set to this value.
+
+source "board/friendlyarm/nanopi2/Kconfig"
diff --git a/board/friendlyarm/nanopi2/Kconfig b/board/friendlyarm/nanopi2/Kconfig
new file mode 100644
index 0000000..0f68422
--- /dev/null
+++ b/board/friendlyarm/nanopi2/Kconfig
@@ -0,0 +1,12 @@
+if TARGET_NANOPI2
+
+config SYS_BOARD
+ default "nanopi2"
+
+config SYS_VENDOR
+ default "friendlyarm"
+
+config SYS_CONFIG_NAME
+ default "s5p4418_nanopi2"
+
+endif
diff --git a/board/friendlyarm/nanopi2/MAINTAINERS b/board/friendlyarm/nanopi2/MAINTAINERS
new file mode 100644
index 0000000..c8e2ce7
--- /dev/null
+++ b/board/friendlyarm/nanopi2/MAINTAINERS
@@ -0,0 +1,7 @@
+NANOPI2 BOARD
+NANOPC-T2 BOARD
+M: Stefan Bosch <stefan_b@posteo.net>
+S: Maintained
+F: board/s5p4418/nanopi2/
+F: include/configs/s5p4418_nanopi2.h
+F: configs/s5p4418_nanopi2_defconfig
diff --git a/board/friendlyarm/nanopi2/Makefile b/board/friendlyarm/nanopi2/Makefile
new file mode 100644
index 0000000..5c8b3b7
--- /dev/null
+++ b/board/friendlyarm/nanopi2/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2016 Nexell
+# Hyunseok, Jung <hsjung@nexell.co.kr>
+
+obj-y := board.o hwrev.o lcds.o
+obj-$(CONFIG_S5P4418_ONEWIRE) += onewire.o
diff --git a/board/friendlyarm/nanopi2/board.c b/board/friendlyarm/nanopi2/board.c
new file mode 100644
index 0000000..6898053
--- /dev/null
+++ b/board/friendlyarm/nanopi2/board.c
@@ -0,0 +1,575 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) Guangzhou FriendlyARM Computer Tech. Co., Ltd.
+ * (http://www.friendlyarm.com)
+ */
+
+#include <config.h>
+#include <common.h>
+#include <command.h>
+#include <fdt_support.h>
+#include <log.h>
+#ifdef CONFIG_PWM_NX
+#include <pwm.h>
+#endif
+#include <asm/io.h>
+
+#include <asm/arch/nexell.h>
+#include <asm/arch/nx_gpio.h>
+#include <asm/arch/display.h>
+#include <asm/arch/display_dev.h>
+
+#include <u-boot/md5.h>
+
+#include <linux/stringify.h>
+
+#include "hwrev.h"
+#include "onewire.h"
+#include "nxp-fb.h"
+
+#include <env_internal.h> /* for env_save() */
+#include <asm/mach-types.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+enum gpio_group {
+ gpio_a, gpio_b, gpio_c, gpio_d, gpio_e,
+};
+
+#ifdef CONFIG_PWM_NX
+struct pwm_device {
+ int grp;
+ int bit;
+ int io_fn;
+};
+
+static inline void bd_pwm_config_gpio(int ch)
+{
+ struct pwm_device pwm_dev[] = {
+ [0] = { .grp = gpio_d, .bit = 1, .io_fn = 0 },
+ [1] = { .grp = gpio_c, .bit = 13, .io_fn = 1 },
+ [2] = { .grp = gpio_c, .bit = 14, .io_fn = 1 },
+ [3] = { .grp = gpio_d, .bit = 0, .io_fn = 0 },
+ };
+
+ int gp = pwm_dev[ch].grp;
+ int io = pwm_dev[ch].bit;
+
+ /* pwm backlight OFF: HIGH, ON: LOW */
+ nx_gpio_set_pad_function(gp, io, pwm_dev[ch].io_fn);
+ nx_gpio_set_output_value(gp, io, 1);
+ nx_gpio_set_output_enable(gp, io, 1);
+}
+#endif
+
+static void bd_backlight_off(void)
+{
+#ifdef CONFIG_S5P4418_ONEWIRE
+ onewire_set_backlight(0);
+
+#elif defined(BACKLIGHT_CH)
+ bd_pwm_config_gpio(BACKLIGHT_CH);
+#endif
+}
+
+static void bd_backlight_on(void)
+{
+#ifdef CONFIG_S5P4418_ONEWIRE
+ onewire_set_backlight(127);
+
+#elif defined(BACKLIGHT_CH)
+ /* pwm backlight ON: HIGH, ON: LOW */
+ pwm_init(BACKLIGHT_CH,
+ BACKLIGHT_DIV, BACKLIGHT_INV);
+ pwm_config(BACKLIGHT_CH,
+ TO_DUTY_NS(BACKLIGHT_DUTY, BACKLIGHT_HZ),
+ TO_PERIOD_NS(BACKLIGHT_HZ));
+#endif
+}
+
+static void bd_lcd_config_gpio(void)
+{
+ int i;
+
+ for (i = 0; i < 28; i++) {
+ nx_gpio_set_pad_function(gpio_a, i, 1);
+ nx_gpio_set_drive_strength(gpio_a, i, 0);
+ nx_gpio_set_pull_mode(gpio_a, i, 2);
+ }
+
+ nx_gpio_set_drive_strength(gpio_a, 0, 1);
+}
+
+/* DEFAULT mmc dev for eMMC boot (dwmmc.2) */
+static int mmc_boot_dev;
+
+int board_mmc_bootdev(void)
+{
+ return mmc_boot_dev;
+}
+
+/* call from common/env_mmc.c */
+int mmc_get_env_dev(void)
+{
+ return mmc_boot_dev;
+}
+
+#ifdef CONFIG_DISPLAY_BOARDINFO
+int checkboard(void)
+{
+ printf("Board: %s\n", get_board_name());
+
+ return 0;
+}
+#endif
+
+int nx_display_fixup_dp(struct nx_display_dev *dp)
+{
+ struct nxp_lcd *lcd = bd_get_lcd();
+ enum lcd_format fmt = bd_get_lcd_format();
+ struct nxp_lcd_timing *timing = &lcd->timing;
+ struct dp_sync_info *sync = &dp->sync;
+ struct dp_plane_info *plane = &dp->planes[0];
+ int i;
+ u32 clk = 800000000;
+ u32 div;
+
+ sync->h_active_len = lcd->width;
+ sync->h_sync_width = timing->h_sw;
+ sync->h_back_porch = timing->h_bp;
+ sync->h_front_porch = timing->h_fp;
+ sync->h_sync_invert = !lcd->polarity.inv_hsync;
+
+ sync->v_active_len = lcd->height;
+ sync->v_sync_width = timing->v_sw;
+ sync->v_back_porch = timing->v_bp;
+ sync->v_front_porch = timing->v_fp;
+ sync->v_sync_invert = !lcd->polarity.inv_vsync;
+
+ /* calculates pixel clock */
+ div = timing->h_sw + timing->h_bp + timing->h_fp + lcd->width;
+ div *= timing->v_sw + timing->v_bp + timing->v_fp + lcd->height;
+ div *= lcd->freq ? : 60;
+ clk /= div;
+
+ dp->ctrl.clk_div_lv0 = clk;
+ dp->ctrl.clk_inv_lv0 = lcd->polarity.rise_vclk;
+
+ dp->top.screen_width = lcd->width;
+ dp->top.screen_height = lcd->height;
+
+ for (i = 0; i < dp->top.plane_num; i++, plane++) {
+ if (plane->enable) {
+ plane->width = lcd->width;
+ plane->height = lcd->height;
+ }
+ }
+
+ /* initialize display device type */
+ if (fmt == LCD_RGB) {
+ dp->dev_type = DP_DEVICE_RGBLCD;
+
+ } else if (fmt == LCD_HDMI) {
+ struct dp_hdmi_dev *dev = (struct dp_hdmi_dev *)dp->device;
+
+ dp->dev_type = DP_DEVICE_HDMI;
+ if (lcd->width == 1920 && lcd->height == 1080)
+ dev->preset = 1;
+ else
+ dev->preset = 0;
+
+ } else {
+ struct dp_lvds_dev *dev = (struct dp_lvds_dev *)dp->device;
+
+ dp->dev_type = DP_DEVICE_LVDS;
+ dev->lvds_format = (fmt & 0x3);
+ }
+
+ return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * initialize board status.
+ */
+
+#define MMC_BOOT_CH0 (0)
+#define MMC_BOOT_CH1 (1 << 3)
+#define MMC_BOOT_CH2 (1 << 19)
+
+static void bd_bootdev_init(void)
+{
+ unsigned int rst = readl(PHY_BASEADDR_CLKPWR + SYSRSTCONFIG);
+
+ rst &= (1 << 19) | (1 << 3);
+ if (rst == MMC_BOOT_CH0) {
+ /* mmc dev 1 for SD boot */
+ mmc_boot_dev = 1;
+ }
+}
+
+#ifdef CONFIG_S5P4418_ONEWIRE
+static void bd_onewire_init(void)
+{
+ unsigned char lcd;
+ unsigned short fw_ver;
+
+ onewire_init();
+ onewire_get_info(&lcd, &fw_ver);
+}
+#endif
+
+static void bd_lcd_init(void)
+{
+ struct nxp_lcd *cfg;
+ int id = -1;
+ int ret;
+
+#ifdef CONFIG_S5P4418_ONEWIRE
+ id = onewire_get_lcd_id();
+ /* -1: onwire probe failed
+ * 0: bad
+ * >0: identified
+ */
+#endif
+ ret = bd_setup_lcd_by_id(id);
+ if (id <= 0 || ret != id) {
+ printf("Panel: N/A (%d)\n", id);
+ bd_setup_lcd_by_name("HDMI720P60");
+
+ } else {
+ printf("Panel: %s\n", bd_get_lcd_name());
+
+ cfg = bd_get_lcd();
+ if (cfg->gpio_init)
+ cfg->gpio_init();
+ }
+}
+
+static int mac_read_from_generic_eeprom(u8 *addr)
+{
+ return -1;
+}
+
+static void make_ether_addr(u8 *addr)
+{
+ u32 hash[20];
+
+#define ETHER_MAC_TAG "ethmac"
+ memset(hash, 0, sizeof(hash));
+ memcpy(hash + 12, ETHER_MAC_TAG, sizeof(ETHER_MAC_TAG));
+
+ hash[4] = readl(PHY_BASEADDR_ECID + 0x00);
+ hash[5] = readl(PHY_BASEADDR_ECID + 0x04);
+ hash[6] = readl(PHY_BASEADDR_ECID + 0x08);
+ hash[7] = readl(PHY_BASEADDR_ECID + 0x0c);
+
+ md5((unsigned char *)&hash[4], 64, (unsigned char *)hash);
+
+ hash[0] ^= hash[2];
+ hash[1] ^= hash[3];
+
+ memcpy(addr, (char *)hash, 6);
+ addr[0] &= 0xfe; /* clear multicast bit */
+ addr[0] |= 0x02;
+}
+
+static void set_ether_addr(void)
+{
+ unsigned char mac[6];
+ char ethaddr[20];
+ int ret;
+
+ if (env_get("ethaddr"))
+ return;
+
+ ret = mac_read_from_generic_eeprom(mac);
+ if (ret < 0)
+ make_ether_addr(mac);
+
+ sprintf(ethaddr, "%02x:%02x:%02x:%02x:%02x:%02x",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ if (!ret)
+ printf("MAC: [%s]\n", ethaddr);
+
+ env_set("ethaddr", ethaddr);
+}
+
+#ifdef CONFIG_REVISION_TAG
+static void set_board_rev(void)
+{
+ char info[64] = {0, };
+
+ snprintf(info, ARRAY_SIZE(info), "%02x", get_board_rev());
+ env_set("board_rev", info);
+}
+#endif
+
+static void set_dtb_name(void)
+{
+ char info[64] = {0, };
+
+ snprintf(info, ARRAY_SIZE(info),
+ "s5p4418-nanopi2-rev%02x.dtb", get_board_rev());
+ env_set("dtb_name", info);
+}
+
+static void bd_update_env(void)
+{
+ char *lcdtype = env_get("lcdtype");
+ char *lcddpi = env_get("lcddpi");
+ char *bootargs = env_get("bootargs");
+ const char *name;
+ char *p = NULL;
+ int rootdev = board_mmc_bootdev();
+ int need_save = 0;
+
+#define CMDLINE_LCD " lcd="
+ char cmdline[CONFIG_SYS_CBSIZE];
+ int n = 1;
+
+ if (rootdev != CONFIG_ROOT_DEV && !env_get("firstboot")) {
+ env_set_ulong("rootdev", rootdev);
+ env_set("firstboot", "0");
+ need_save = 1;
+ }
+
+ if (lcdtype) {
+ /* Setup again as user specified LCD in env */
+ bd_setup_lcd_by_name(lcdtype);
+ }
+
+ name = bd_get_lcd_name();
+
+ if (bootargs)
+ n = strlen(bootargs); /* isn't 0 for NULL */
+ else
+ cmdline[0] = '\0';
+
+ if ((n + strlen(name) + sizeof(CMDLINE_LCD)) > sizeof(cmdline)) {
+ printf("Error: `bootargs' is too large (%d)\n", n);
+ goto __exit;
+ }
+
+ if (bootargs) {
+ p = strstr(bootargs, CMDLINE_LCD);
+ if (p) {
+ n = (p - bootargs);
+ p += strlen(CMDLINE_LCD);
+ }
+ strncpy(cmdline, bootargs, n);
+ }
+
+ /* add `lcd=NAME,NUMdpi' */
+ strncpy(cmdline + n, CMDLINE_LCD, strlen(CMDLINE_LCD));
+ n += strlen(CMDLINE_LCD);
+
+ strcpy(cmdline + n, name);
+ n += strlen(name);
+
+ if (lcddpi) {
+ n += sprintf(cmdline + n, ",%sdpi", lcddpi);
+ } else {
+ int dpi = bd_get_lcd_density();
+
+ if (dpi > 0 && dpi < 600)
+ n += sprintf(cmdline + n, ",%ddpi", dpi);
+ }
+
+ /* copy remaining of bootargs */
+ if (p) {
+ p = strstr(p, " ");
+ if (p) {
+ strcpy(cmdline + n, p);
+ n += strlen(p);
+ }
+ }
+
+ /* append `bootdev=2' */
+#define CMDLINE_BDEV " bootdev="
+ if (rootdev > 0 && !strstr(cmdline, CMDLINE_BDEV))
+ n += sprintf(cmdline + n, "%s2", CMDLINE_BDEV);
+
+ /* finally, let's update uboot env & save it */
+ if (bootargs && strncmp(cmdline, bootargs, sizeof(cmdline))) {
+ env_set("bootargs", cmdline);
+ need_save = 1;
+ }
+
+__exit:
+ if (need_save)
+ env_save();
+}
+
+/* --------------------------------------------------------------------------
+ * call from u-boot
+ */
+
+int board_early_init_f(void)
+{
+ return 0;
+}
+
+int board_init(void)
+{
+ bd_hwrev_init();
+ bd_base_rev_init();
+
+ bd_bootdev_init();
+#ifdef CONFIG_S5P4418_ONEWIRE
+ bd_onewire_init();
+#endif
+
+ bd_backlight_off();
+
+ bd_lcd_config_gpio();
+ bd_lcd_init();
+
+ if (IS_ENABLED(CONFIG_SILENT_CONSOLE))
+ gd->flags |= GD_FLG_SILENT;
+
+ return 0;
+}
+
+#ifdef CONFIG_BOARD_LATE_INIT
+int board_late_init(void)
+{
+ bd_update_env();
+
+#ifdef CONFIG_REVISION_TAG
+ set_board_rev();
+#endif
+ set_dtb_name();
+
+ set_ether_addr();
+
+ if (IS_ENABLED(CONFIG_SILENT_CONSOLE))
+ gd->flags &= ~GD_FLG_SILENT;
+
+ bd_backlight_on();
+ printf("\n");
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_SPLASH_SOURCE
+#include <splash.h>
+static struct splash_location splash_locations[] = {
+ {
+ .name = "mmc_fs",
+ .storage = SPLASH_STORAGE_MMC,
+ .flags = SPLASH_STORAGE_FS,
+ .devpart = __stringify(CONFIG_ROOT_DEV) ":"
+ __stringify(CONFIG_BOOT_PART),
+ },
+};
+
+int splash_screen_prepare(void)
+{
+ int err;
+ char *env_cmd = env_get("load_splash");
+
+ debug("%s()\n", __func__);
+
+ if (env_cmd) {
+ err = run_command(env_cmd, 0);
+
+ } else {
+ char devpart[64] = { 0, };
+ int bootpart = env_get_ulong("bootpart", 0, CONFIG_BOOT_PART);
+ int rootdev;
+
+ if (env_get("firstboot"))
+ rootdev = env_get_ulong("rootdev", 0, CONFIG_ROOT_DEV);
+ else
+ rootdev = board_mmc_bootdev();
+
+ snprintf(devpart, ARRAY_SIZE(devpart), "%d:%d", rootdev,
+ bootpart);
+ splash_locations[0].devpart = devpart;
+
+ err = splash_source_load(splash_locations,
+ ARRAY_SIZE(splash_locations));
+ }
+
+ if (!err) {
+ char addr[64];
+
+ sprintf(addr, "0x%lx", gd->fb_base);
+ env_set("fb_addr", addr);
+ }
+
+ return err;
+}
+#endif
+
+/* u-boot dram initialize */
+int dram_init(void)
+{
+ gd->ram_size = CONFIG_SYS_SDRAM_SIZE;
+ return 0;
+}
+
+/* u-boot dram board specific */
+int dram_init_banksize(void)
+{
+#define SCR_USER_SIG6_READ (SCR_ALIVE_BASE + 0x0F0)
+ unsigned int reg_val = readl(SCR_USER_SIG6_READ);
+
+ /* set global data memory */
+ gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x00000100;
+
+ gd->bd->bi_dram[0].start = CONFIG_SYS_SDRAM_BASE;
+ gd->bd->bi_dram[0].size = CONFIG_SYS_SDRAM_SIZE;
+
+ /* Number of Row: 14 bits */
+ if ((reg_val >> 28) == 14)
+ gd->bd->bi_dram[0].size -= 0x20000000;
+
+ /* Number of Memory Chips */
+ if ((reg_val & 0x3) > 1) {
+ gd->bd->bi_dram[1].start = 0x80000000;
+ gd->bd->bi_dram[1].size = 0x40000000;
+ }
+ return 0;
+}
+
+#if defined(CONFIG_OF_BOARD_SETUP)
+int ft_board_setup(void *blob, struct bd_info *bd)
+{
+ int nodeoff;
+ unsigned int rootdev;
+ unsigned int fb_addr;
+
+ if (board_mmc_bootdev() > 0) {
+ rootdev = fdt_getprop_u32_default(blob, "/board", "sdidx", 2);
+ if (rootdev) {
+ /* find or create "/chosen" node. */
+ nodeoff = fdt_find_or_add_subnode(blob, 0, "chosen");
+ if (nodeoff >= 0)
+ fdt_setprop_u32(blob, nodeoff, "linux,rootdev",
+ rootdev);
+ }
+ }
+
+ fb_addr = env_get_ulong("fb_addr", 0, 0);
+ if (fb_addr) {
+ nodeoff = fdt_path_offset(blob, "/reserved-memory");
+ if (nodeoff < 0)
+ return nodeoff;
+
+ nodeoff = fdt_add_subnode(blob, nodeoff, "display_reserved");
+ if (nodeoff >= 0) {
+ fdt32_t cells[2];
+
+ cells[0] = cpu_to_fdt32(fb_addr);
+ cells[1] = cpu_to_fdt32(0x800000);
+
+ fdt_setprop(blob, nodeoff, "reg", cells,
+ sizeof(cells[0]) * 2);
+ }
+ }
+
+ return 0;
+}
+#endif
diff --git a/board/friendlyarm/nanopi2/hwrev.c b/board/friendlyarm/nanopi2/hwrev.c
new file mode 100644
index 0000000..b1e23a4
--- /dev/null
+++ b/board/friendlyarm/nanopi2/hwrev.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) Guangzhou FriendlyARM Computer Tech. Co., Ltd.
+ * (http://www.friendlyarm.com)
+ */
+
+#include <config.h>
+#include <common.h>
+#include <i2c.h>
+#include <asm/io.h>
+
+#include <asm/arch/nexell.h>
+#include <asm/arch/nx_gpio.h>
+
+/* Board revision list: <PCB3 | PCB2 | PCB1>
+ * 0b000 - NanoPi 2
+ * 0b001 - NanoPC-T2
+ * 0b010 - NanoPi S2
+ * 0b011 - Smart4418
+ * 0b100 - NanoPi Fire 2A
+ * 0b111 - NanoPi M2A
+ *
+ * Extented revision:
+ * 0b001 - Smart4418-SDK
+ */
+#define __IO_GRP 2 /* GPIO_C */
+#define __IO_PCB1 26
+#define __IO_PCB2 27
+#define __IO_PCB3 25
+
+static int pcb_rev = -1;
+static int base_rev;
+
+static void bd_hwrev_config_gpio(void)
+{
+ int gpios[3][2] = {
+ { __IO_PCB1, 1 },
+ { __IO_PCB2, 1 },
+ { __IO_PCB3, 1 },
+ };
+ int i;
+
+ /* gpio input mode, pull-down */
+ for (i = 0; i < 3; i++) {
+ nx_gpio_set_pad_function(__IO_GRP, gpios[i][0], gpios[i][1]);
+ nx_gpio_set_output_enable(__IO_GRP, gpios[i][0], 0);
+ nx_gpio_set_pull_mode(__IO_GRP, gpios[i][0], 0);
+ }
+}
+
+void bd_hwrev_init(void)
+{
+ if (pcb_rev >= 0)
+ return;
+
+ bd_hwrev_config_gpio();
+
+ pcb_rev = nx_gpio_get_input_value(__IO_GRP, __IO_PCB1);
+ pcb_rev |= nx_gpio_get_input_value(__IO_GRP, __IO_PCB2) << 1;
+ pcb_rev |= nx_gpio_get_input_value(__IO_GRP, __IO_PCB3) << 2;
+}
+
+/* Get extended revision for SmartXX18 */
+void bd_base_rev_init(void)
+{
+ struct udevice *dev;
+ u8 val = 0;
+
+ if (pcb_rev != 0x3)
+ return;
+
+#define PCA9536_I2C_BUS 2
+#define PCA9636_I2C_ADDR 0x41
+ if (i2c_get_chip_for_busnum
+ (PCA9536_I2C_BUS, PCA9636_I2C_ADDR, 1, &dev))
+ return;
+
+ if (!dm_i2c_read(dev, 0, &val, 1))
+ base_rev = (val & 0xf);
+}
+
+/* To override __weak symbols */
+u32 get_board_rev(void)
+{
+ return (base_rev << 8) | pcb_rev;
+}
+
+const char *get_board_name(void)
+{
+ bd_hwrev_init();
+
+ switch (pcb_rev) {
+ case 0:
+ return "NanoPi 2";
+ case 1:
+ return "NanoPC-T2";
+ case 2:
+ return "NanoPi S2";
+ case 3:
+ return "Smart4418";
+ case 4:
+ return "NanoPi Fire 2A";
+ case 7:
+ return "NanoPi M2A";
+ default:
+ return "s5p4418-X";
+ }
+}
diff --git a/board/friendlyarm/nanopi2/hwrev.h b/board/friendlyarm/nanopi2/hwrev.h
new file mode 100644
index 0000000..1b1a828
--- /dev/null
+++ b/board/friendlyarm/nanopi2/hwrev.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0+
+ *
+ * Copyright (C) Guangzhou FriendlyARM Computer Tech. Co., Ltd.
+ * (http://www.friendlyarm.com)
+ */
+
+#ifndef __BD_HW_REV_H__
+#define __BD_HW_REV_H__
+
+extern void bd_hwrev_init(void);
+extern void bd_base_rev_init(void);
+extern u32 get_board_rev(void);
+extern const char *get_board_name(void);
+
+#endif /* __BD_HW_REV_H__ */
diff --git a/board/friendlyarm/nanopi2/lcds.c b/board/friendlyarm/nanopi2/lcds.c
new file mode 100644
index 0000000..7303e53
--- /dev/null
+++ b/board/friendlyarm/nanopi2/lcds.c
@@ -0,0 +1,697 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2017 FriendlyARM (www.arm9.net)
+ */
+
+#include <config.h>
+#include <common.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <fdt_support.h>
+#include <asm/io.h>
+
+#include <asm/arch/nexell.h>
+#include <asm/arch/display.h>
+#include <asm/arch/nx_gpio.h>
+
+#include "nxp-fb.h"
+
+/*
+ * param @module_index for nx_gpio APIs and will be removed
+ * after support pinctrl
+ */
+#ifndef PAD_GPIO_A
+#define PAD_GPIO_A 0
+#endif
+
+static inline void common_gpio_init(void)
+{
+ /* PVCLK */
+ nx_gpio_set_fast_slew(PAD_GPIO_A, 0, 1);
+}
+
+static void s70_gpio_init(void)
+{
+ int i;
+
+ /* PVCLK */
+ nx_gpio_set_drive_strength(PAD_GPIO_A, 0, 1);
+
+ /* RGB24 */
+ for (i = 1; i < 25; i++)
+ nx_gpio_set_drive_strength(PAD_GPIO_A, i, 2);
+
+ /* HS/VS/DE */
+ for (; i < 28; i++)
+ nx_gpio_set_drive_strength(PAD_GPIO_A, i, 1);
+}
+
+static void s702_gpio_init(void)
+{
+ int i;
+
+ common_gpio_init();
+
+ nx_gpio_set_drive_strength(PAD_GPIO_A, 0, 2);
+
+ for (i = 1; i < 25; i++)
+ nx_gpio_set_drive_strength(PAD_GPIO_A, i, 0);
+
+ for (; i < 28; i++)
+ nx_gpio_set_drive_strength(PAD_GPIO_A, i, 1);
+}
+
+static void s430_gpio_init(void)
+{
+ int i;
+
+ for (i = 0; i < 28; i++)
+ nx_gpio_set_drive_strength(PAD_GPIO_A, i, 1);
+}
+
+static void hd101_gpio_init(void)
+{
+ int i;
+
+ common_gpio_init();
+
+ nx_gpio_set_drive_strength(PAD_GPIO_A, 0, 2);
+
+ for (i = 1; i < 25; i++)
+ nx_gpio_set_drive_strength(PAD_GPIO_A, i, 1);
+
+ nx_gpio_set_drive_strength(PAD_GPIO_A, 27, 1);
+}
+
+static void hd700_gpio_init(void)
+{
+ hd101_gpio_init();
+}
+
+/* NXP display configs for supported LCD */
+
+static struct nxp_lcd wxga_hd700 = {
+ .width = 800,
+ .height = 1280,
+ .p_width = 94,
+ .p_height = 151,
+ .bpp = 24,
+ .freq = 60,
+
+ .timing = {
+ .h_fp = 20,
+ .h_bp = 20,
+ .h_sw = 24,
+ .v_fp = 4,
+ .v_fpe = 1,
+ .v_bp = 4,
+ .v_bpe = 1,
+ .v_sw = 8,
+ },
+ .polarity = {
+ .rise_vclk = 0,
+ .inv_hsync = 0,
+ .inv_vsync = 0,
+ .inv_vden = 0,
+ },
+ .gpio_init = hd700_gpio_init,
+};
+
+static struct nxp_lcd wvga_s70 = {
+ .width = 800,
+ .height = 480,
+ .p_width = 155,
+ .p_height = 93,
+ .bpp = 24,
+ .freq = 61,
+
+ .timing = {
+ .h_fp = 48,
+ .h_bp = 36,
+ .h_sw = 10,
+ .v_fp = 22,
+ .v_fpe = 1,
+ .v_bp = 15,
+ .v_bpe = 1,
+ .v_sw = 8,
+ },
+ .polarity = {
+ .rise_vclk = 0,
+ .inv_hsync = 1,
+ .inv_vsync = 1,
+ .inv_vden = 0,
+ },
+ .gpio_init = s70_gpio_init,
+};
+
+static struct nxp_lcd wvga_s702 = {
+ .width = 800,
+ .height = 480,
+ .p_width = 155,
+ .p_height = 93,
+ .bpp = 24,
+ .freq = 61,
+
+ .timing = {
+ .h_fp = 44,
+ .h_bp = 26,
+ .h_sw = 20,
+ .v_fp = 22,
+ .v_fpe = 1,
+ .v_bp = 15,
+ .v_bpe = 1,
+ .v_sw = 8,
+ },
+ .polarity = {
+ .rise_vclk = 1,
+ .inv_hsync = 1,
+ .inv_vsync = 1,
+ .inv_vden = 0,
+ },
+ .gpio_init = s702_gpio_init,
+};
+
+static struct nxp_lcd wvga_s70d = {
+ .width = 800,
+ .height = 480,
+ .p_width = 155,
+ .p_height = 93,
+ .bpp = 24,
+ .freq = 61,
+
+ .timing = {
+ .h_fp = 80,
+ .h_bp = 78,
+ .h_sw = 10,
+ .v_fp = 22,
+ .v_fpe = 1,
+ .v_bp = 24,
+ .v_bpe = 1,
+ .v_sw = 8,
+ },
+ .polarity = {
+ .rise_vclk = 0,
+ .inv_hsync = 1,
+ .inv_vsync = 1,
+ .inv_vden = 0,
+ },
+ .gpio_init = s702_gpio_init,
+};
+
+static struct nxp_lcd wvga_w50 = {
+ .width = 800,
+ .height = 480,
+ .p_width = 108,
+ .p_height = 64,
+ .bpp = 24,
+ .freq = 61,
+
+ .timing = {
+ .h_fp = 40,
+ .h_bp = 40,
+ .h_sw = 48,
+ .v_fp = 20,
+ .v_fpe = 1,
+ .v_bp = 20,
+ .v_bpe = 1,
+ .v_sw = 12,
+ },
+ .polarity = {
+ .rise_vclk = 0,
+ .inv_hsync = 1,
+ .inv_vsync = 1,
+ .inv_vden = 0,
+ },
+ .gpio_init = s70_gpio_init,
+};
+
+static struct nxp_lcd wvga_s430 = {
+ .width = 480,
+ .height = 800,
+ .p_width = 108,
+ .p_height = 64,
+ .bpp = 24,
+ .freq = 60,
+
+ .timing = {
+ .h_fp = 64,
+ .h_bp = 0,
+ .h_sw = 16,
+ .v_fp = 32,
+ .v_fpe = 1,
+ .v_bp = 0,
+ .v_bpe = 1,
+ .v_sw = 16,
+ },
+ .polarity = {
+ .rise_vclk = 1,
+ .inv_hsync = 1,
+ .inv_vsync = 1,
+ .inv_vden = 0,
+ },
+ .gpio_init = s430_gpio_init,
+};
+
+static struct nxp_lcd wsvga_w101 = {
+ .width = 1024,
+ .height = 600,
+ .p_width = 204,
+ .p_height = 120,
+ .bpp = 24,
+ .freq = 60,
+
+ .timing = {
+ .h_fp = 40,
+ .h_bp = 40,
+ .h_sw = 200,
+ .v_fp = 8,
+ .v_fpe = 1,
+ .v_bp = 8,
+ .v_bpe = 1,
+ .v_sw = 16,
+ },
+ .polarity = {
+ .rise_vclk = 1,
+ .inv_hsync = 1,
+ .inv_vsync = 1,
+ .inv_vden = 0,
+ },
+};
+
+static struct nxp_lcd wsvga_x710 = {
+ .width = 1024,
+ .height = 600,
+ .p_width = 154,
+ .p_height = 90,
+ .bpp = 24,
+ .freq = 61,
+
+ .timing = {
+ .h_fp = 84,
+ .h_bp = 84,
+ .h_sw = 88,
+ .v_fp = 10,
+ .v_fpe = 1,
+ .v_bp = 10,
+ .v_bpe = 1,
+ .v_sw = 20,
+ },
+ .polarity = {
+ .rise_vclk = 0,
+ .inv_hsync = 1,
+ .inv_vsync = 1,
+ .inv_vden = 0,
+ },
+ .gpio_init = hd101_gpio_init,
+};
+
+static struct nxp_lcd xga_a97 = {
+ .width = 1024,
+ .height = 768,
+ .p_width = 200,
+ .p_height = 150,
+ .bpp = 24,
+ .freq = 61,
+
+ .timing = {
+ .h_fp = 12,
+ .h_bp = 12,
+ .h_sw = 4,
+ .v_fp = 8,
+ .v_fpe = 1,
+ .v_bp = 8,
+ .v_bpe = 1,
+ .v_sw = 4,
+ },
+ .polarity = {
+ .rise_vclk = 0,
+ .inv_hsync = 1,
+ .inv_vsync = 1,
+ .inv_vden = 0,
+ },
+};
+
+static struct nxp_lcd xga_lq150 = {
+ .width = 1024,
+ .height = 768,
+ .p_width = 304,
+ .p_height = 228,
+ .bpp = 24,
+ .freq = 60,
+
+ .timing = {
+ .h_fp = 12,
+ .h_bp = 12,
+ .h_sw = 40,
+ .v_fp = 8,
+ .v_fpe = 1,
+ .v_bp = 8,
+ .v_bpe = 1,
+ .v_sw = 40,
+ },
+ .polarity = {
+ .rise_vclk = 0,
+ .inv_hsync = 1,
+ .inv_vsync = 1,
+ .inv_vden = 0,
+ },
+};
+
+static struct nxp_lcd vga_l80 = {
+ .width = 640,
+ .height = 480,
+ .p_width = 160,
+ .p_height = 120,
+ .bpp = 32,
+ .freq = 60,
+
+ .timing = {
+ .h_fp = 35,
+ .h_bp = 53,
+ .h_sw = 73,
+ .v_fp = 3,
+ .v_fpe = 1,
+ .v_bp = 29,
+ .v_bpe = 1,
+ .v_sw = 6,
+ },
+ .polarity = {
+ .rise_vclk = 0,
+ .inv_hsync = 1,
+ .inv_vsync = 1,
+ .inv_vden = 0,
+ },
+};
+
+static struct nxp_lcd wxga_bp101 = {
+ .width = 1280,
+ .height = 800,
+ .p_width = 218,
+ .p_height = 136,
+ .bpp = 24,
+ .freq = 60,
+
+ .timing = {
+ .h_fp = 20,
+ .h_bp = 20,
+ .h_sw = 24,
+ .v_fp = 4,
+ .v_fpe = 1,
+ .v_bp = 4,
+ .v_bpe = 1,
+ .v_sw = 8,
+ },
+ .polarity = {
+ .rise_vclk = 1,
+ .inv_hsync = 1,
+ .inv_vsync = 1,
+ .inv_vden = 0,
+ },
+};
+
+static struct nxp_lcd wxga_hd101 = {
+ .width = 1280,
+ .height = 800,
+ .p_width = 218,
+ .p_height = 136,
+ .bpp = 24,
+ .freq = 60,
+
+ .timing = {
+ .h_fp = 16,
+ .h_bp = 16,
+ .h_sw = 30,
+ .v_fp = 8,
+ .v_fpe = 1,
+ .v_bp = 8,
+ .v_bpe = 1,
+ .v_sw = 12,
+ },
+ .polarity = {
+ .rise_vclk = 1,
+ .inv_hsync = 0,
+ .inv_vsync = 0,
+ .inv_vden = 0,
+ },
+ .gpio_init = hd101_gpio_init,
+};
+
+static struct nxp_lcd hvga_h43 = {
+ .width = 480,
+ .height = 272,
+ .p_width = 96,
+ .p_height = 54,
+ .bpp = 32,
+ .freq = 65,
+
+ .timing = {
+ .h_fp = 5,
+ .h_bp = 40,
+ .h_sw = 2,
+ .v_fp = 8,
+ .v_fpe = 1,
+ .v_bp = 8,
+ .v_bpe = 1,
+ .v_sw = 2,
+ },
+ .polarity = {
+ .rise_vclk = 0,
+ .inv_hsync = 1,
+ .inv_vsync = 1,
+ .inv_vden = 0,
+ },
+};
+
+static struct nxp_lcd hvga_p43 = {
+ .width = 480,
+ .height = 272,
+ .p_width = 96,
+ .p_height = 54,
+ .bpp = 32,
+ .freq = 65,
+
+ .timing = {
+ .h_fp = 5,
+ .h_bp = 40,
+ .h_sw = 2,
+ .v_fp = 8,
+ .v_fpe = 1,
+ .v_bp = 9,
+ .v_bpe = 1,
+ .v_sw = 2,
+ },
+ .polarity = {
+ .rise_vclk = 1,
+ .inv_hsync = 1,
+ .inv_vsync = 1,
+ .inv_vden = 0,
+ },
+};
+
+static struct nxp_lcd qvga_w35 = {
+ .width = 320,
+ .height = 240,
+ .p_width = 70,
+ .p_height = 52,
+ .bpp = 16,
+ .freq = 65,
+
+ .timing = {
+ .h_fp = 4,
+ .h_bp = 70,
+ .h_sw = 4,
+ .v_fp = 4,
+ .v_fpe = 1,
+ .v_bp = 12,
+ .v_bpe = 1,
+ .v_sw = 4,
+ },
+ .polarity = {
+ .rise_vclk = 1,
+ .inv_hsync = 0,
+ .inv_vsync = 0,
+ .inv_vden = 0,
+ },
+};
+
+/* HDMI */
+static struct nxp_lcd hdmi_def = {
+ .width = 1920,
+ .height = 1080,
+ .p_width = 480,
+ .p_height = 320,
+ .bpp = 24,
+ .freq = 60,
+
+ .timing = {
+ .h_fp = 12,
+ .h_bp = 12,
+ .h_sw = 4,
+ .v_fp = 8,
+ .v_fpe = 1,
+ .v_bp = 8,
+ .v_bpe = 1,
+ .v_sw = 4,
+ },
+ .polarity = {
+ .rise_vclk = 0,
+ .inv_hsync = 1,
+ .inv_vsync = 1,
+ .inv_vden = 0,
+ },
+};
+
+static struct hdmi_config {
+ char *name;
+ int width;
+ int height;
+} bd_hdmi_config[] = {
+ { "HDMI1080P60", 1920, 1080 },
+ { "HDMI1080I60", 1920, 1080 },
+ { "HDMI1080P30", 1920, 1080 },
+ { "HDMI1080P50", 1920, 1080 },
+ { "HDMI1080I50", 1920, 1080 },
+
+ { "HDMI1080P60D", 960, 536 },
+ { "HDMI1080I60D", 960, 536 },
+ { "HDMI1080P30D", 960, 536 },
+ { "HDMI1080P50D", 960, 536 },
+ { "HDMI1080I50D", 960, 536 },
+
+ { "HDMI720P60", 1280, 720 },
+ { "HDMI720P60D", 640, 360 },
+ { "HDMI720P50", 1280, 720 },
+ { "HDMI720P50D", 640, 360 },
+
+ { "HDMI576P16X9", 720, 576 },
+ { "HDMI576P16X9D", 720, 576 },
+ { "HDMI576P4X3", 720, 576 },
+ { "HDMI576P4X3D", 720, 576 },
+
+ { "HDMI480P16X9", 720, 480 },
+ { "HDMI480P16X9D", 720, 480 },
+ { "HDMI480P4X3", 720, 480 },
+ { "HDMI480P4X3D", 720, 480 },
+};
+
+/* Try to guess LCD panel by kernel command line, or
+ * using *HD101* as default
+ */
+static struct {
+ int id;
+ char *name;
+ struct nxp_lcd *lcd;
+ int dpi;
+ int ctp;
+ enum lcd_format fmt;
+} bd_lcd_config[] = {
+ { 25, "HD101", &wxga_hd101, 0, 1, LCD_RGB },
+ { 32, "HD101B", &wxga_hd101, 0, 1, LCD_RGB },
+ { 18, "HD700", &wxga_hd700, 213, 1, LCD_RGB },
+ { 30, "HD702", &wxga_hd700, 213, 1, LCD_RGB },
+ { 33, "H70", &wxga_hd700, 213, 0, LCD_VESA },
+ { 3, "S70", &wvga_s70, 128, 1, LCD_RGB },
+ { 36, "S701", &wvga_s70, 128, 1, LCD_RGB },
+ { 24, "S702", &wvga_s702, 128, 3, LCD_RGB },
+ { 26, "S70D", &wvga_s70d, 128, 0, LCD_RGB },
+ { 14, "H43", &hvga_h43, 0, 0, LCD_RGB },
+ { 19, "P43", &hvga_p43, 0, 0, LCD_RGB },
+ { 8, "W35", &qvga_w35, 0, 0, LCD_RGB },
+ { 28, "X710", &wsvga_x710, 0, 1, LCD_RGB },
+ { 31, "S430", &wvga_s430, 180, 1, LCD_RGB },
+ { 4, "W50", &wvga_w50, 0, 0, LCD_RGB },
+
+ /* TODO: Testing */
+ { 15, "W101", &wsvga_w101, 0, 1, LCD_RGB },
+ { 5, "L80", &vga_l80, 0, 1, LCD_RGB },
+ { -1, "A97", &xga_a97, 0, 0, LCD_RGB },
+ { -1, "LQ150", &xga_lq150, 0, 1, LCD_RGB },
+ { -1, "BP101", &wxga_bp101, 0, 1, LCD_RGB },
+ /* Pls keep it at last */
+ { 128, "HDMI", &hdmi_def, 0, 0, LCD_HDMI },
+};
+
+static int lcd_idx;
+
+int bd_setup_lcd_by_id(int id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bd_lcd_config); i++) {
+ if (bd_lcd_config[i].id == id) {
+ lcd_idx = i;
+ break;
+ }
+ }
+
+ if (i >= ARRAY_SIZE(bd_lcd_config)) {
+ /* NOT found */
+ return -19;
+ }
+
+ return bd_lcd_config[i].id;
+}
+
+int bd_setup_lcd_by_name(char *str)
+{
+ char *delim;
+ int i;
+
+ delim = strchr(str, ',');
+ if (delim)
+ *delim++ = '\0';
+
+ if (!strncasecmp("HDMI", str, 4)) {
+ struct hdmi_config *cfg = &bd_hdmi_config[0];
+ struct nxp_lcd *lcd;
+
+ lcd_idx = ARRAY_SIZE(bd_lcd_config) - 1;
+ lcd = bd_lcd_config[lcd_idx].lcd;
+
+ for (i = 0; i < ARRAY_SIZE(bd_hdmi_config); i++, cfg++) {
+ if (!strcasecmp(cfg->name, str)) {
+ lcd->width = cfg->width;
+ lcd->height = cfg->height;
+ bd_lcd_config[lcd_idx].name = cfg->name;
+ goto __ret;
+ }
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(bd_lcd_config); i++) {
+ if (!strcasecmp(bd_lcd_config[i].name, str)) {
+ lcd_idx = i;
+ break;
+ }
+ }
+
+__ret:
+ return 0;
+}
+
+struct nxp_lcd *bd_get_lcd(void)
+{
+ return bd_lcd_config[lcd_idx].lcd;
+}
+
+const char *bd_get_lcd_name(void)
+{
+ return bd_lcd_config[lcd_idx].name;
+}
+
+enum lcd_format bd_get_lcd_format(void)
+{
+ return bd_lcd_config[lcd_idx].fmt;
+}
+
+int bd_get_lcd_density(void)
+{
+ return bd_lcd_config[lcd_idx].dpi;
+}
+
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+int bd_fixup_lcd_fdt(void *blob, struct nxp_lcd *lcd)
+{
+ return 0;
+}
+#endif
diff --git a/board/friendlyarm/nanopi2/nxp-fb.h b/board/friendlyarm/nanopi2/nxp-fb.h
new file mode 100644
index 0000000..d31a03d
--- /dev/null
+++ b/board/friendlyarm/nanopi2/nxp-fb.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0+
+ *
+ * Copyright (c) 2017 FriendlyARM (www.arm9.net)
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Header file for NXP Display Driver
+ */
+
+#ifndef __MACH_NXP_FB_H__
+#define __MACH_NXP_FB_H__
+
+/*
+ * struct nxp_lcd_polarity
+ * @rise_vclk: if 1, video data is fetched at rising edge
+ * @inv_hsync: if HSYNC polarity is inversed
+ * @inv_vsync: if VSYNC polarity is inversed
+ * @inv_vden: if VDEN polarity is inversed
+ */
+struct nxp_lcd_polarity {
+ int rise_vclk;
+ int inv_hsync;
+ int inv_vsync;
+ int inv_vden;
+};
+
+/*
+ * struct nxp_lcd_timing
+ * @h_fp: horizontal front porch
+ * @h_bp: horizontal back porch
+ * @h_sw: horizontal sync width
+ * @v_fp: vertical front porch
+ * @v_fpe: vertical front porch for even field
+ * @v_bp: vertical back porch
+ * @v_bpe: vertical back porch for even field
+ */
+struct nxp_lcd_timing {
+ int h_fp;
+ int h_bp;
+ int h_sw;
+ int v_fp;
+ int v_fpe;
+ int v_bp;
+ int v_bpe;
+ int v_sw;
+};
+
+/*
+ * struct nxp_lcd
+ * @width: horizontal resolution
+ * @height: vertical resolution
+ * @p_width: width of lcd in mm
+ * @p_height: height of lcd in mm
+ * @bpp: bits per pixel
+ * @freq: vframe frequency
+ * @timing: timing values
+ * @polarity: polarity settings
+ * @gpio_init: pointer to GPIO init function
+ *
+ */
+struct nxp_lcd {
+ int width;
+ int height;
+ int p_width;
+ int p_height;
+ int bpp;
+ int freq;
+ struct nxp_lcd_timing timing;
+ struct nxp_lcd_polarity polarity;
+ void (*gpio_init)(void);
+};
+
+/**
+ * Public interfaces
+ */
+enum lcd_format {
+ LCD_VESA = 0,
+ LCD_JEIDA = 1,
+ LCD_LOC = 2,
+
+ LCD_RGB = 4,
+ LCD_HDMI = 5,
+};
+
+extern int bd_setup_lcd_by_id(int id);
+extern int bd_setup_lcd_by_name(char *name);
+extern struct nxp_lcd *bd_get_lcd(void);
+extern const char *bd_get_lcd_name(void);
+extern int bd_get_lcd_density(void);
+extern enum lcd_format bd_get_lcd_format(void);
+extern int bd_fixup_lcd_fdt(void *blob, struct nxp_lcd *cfg);
+
+#endif /* __MACH_NXP_FB_H__ */
diff --git a/board/friendlyarm/nanopi2/onewire.c b/board/friendlyarm/nanopi2/onewire.c
new file mode 100644
index 0000000..994befb
--- /dev/null
+++ b/board/friendlyarm/nanopi2/onewire.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) Guangzhou FriendlyARM Computer Tech. Co., Ltd.
+ * (http://www.friendlyarm.com)
+ */
+
+#include <config.h>
+#include <common.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <asm/arch/clk.h>
+#include <i2c.h>
+#include <pwm.h>
+
+#include <irq_func.h>
+
+#include <asm/arch/nexell.h>
+#include <asm/arch/nx_gpio.h>
+
+#ifndef NSEC_PER_SEC
+#define NSEC_PER_SEC 1000000000L
+#endif
+
+#define SAMPLE_BPS 9600
+#define SAMPLE_IN_US 101 /* (1000000 / BPS) */
+
+#define REQ_INFO 0x60U
+#define REQ_BL 0x80U
+
+#define BUS_I2C 0x18
+#define ONEWIRE_I2C_BUS 2
+#define ONEWIRE_I2C_ADDR 0x2f
+
+static int bus_type = -1;
+static int lcd_id = -1;
+static unsigned short lcd_fwrev;
+static int current_brightness = -1;
+#ifdef CONFIG_DM_I2C
+static struct udevice *i2c_dev;
+#endif
+
+/* debug */
+#if (0)
+#define DBGOUT(msg...) do { printf("onewire: " msg); } while (0)
+#else
+#define DBGOUT(msg...) do {} while (0)
+#endif
+
+/* based on web page from http://lfh1986.blogspot.com */
+static unsigned char crc8_ow(unsigned int v, unsigned int len)
+{
+ unsigned char crc = 0xACU;
+
+ while (len--) {
+ if ((crc & 0x80U) != 0) {
+ crc <<= 1;
+ crc ^= 0x7U;
+ } else {
+ crc <<= 1;
+ }
+ if ((v & (1U << 31)) != 0)
+ crc ^= 0x7U;
+ v <<= 1;
+ }
+ return crc;
+}
+
+/* GPIO helpers */
+#define __IO_GRP 2 /* GPIOC15 */
+#define __IO_IDX 15
+
+static inline void set_pin_as_input(void)
+{
+ nx_gpio_set_output_enable(__IO_GRP, __IO_IDX, 0);
+}
+
+static inline void set_pin_as_output(void)
+{
+ nx_gpio_set_output_enable(__IO_GRP, __IO_IDX, 1);
+}
+
+static inline void set_pin_value(int v)
+{
+ nx_gpio_set_output_value(__IO_GRP, __IO_IDX, !!v);
+}
+
+static inline int get_pin_value(void)
+{
+ return nx_gpio_get_input_value(__IO_GRP, __IO_IDX);
+}
+
+/* Timer helpers */
+#define PWM_CH 3
+#define PWM_TCON (PHY_BASEADDR_PWM + 0x08)
+#define PWM_TCON_START (1 << 16)
+#define PWM_TINT_CSTAT (PHY_BASEADDR_PWM + 0x44)
+
+static int onewire_init_timer(void)
+{
+ int period_ns = NSEC_PER_SEC / SAMPLE_BPS;
+
+ /* range: 1080~1970 */
+ period_ns -= 1525;
+
+ return pwm_config(PWM_CH, period_ns >> 1, period_ns);
+}
+
+static void wait_one_tick(void)
+{
+ unsigned int tcon;
+
+ tcon = readl(PWM_TCON);
+ tcon |= PWM_TCON_START;
+ writel(tcon, PWM_TCON);
+
+ while (1) {
+ if (readl(PWM_TINT_CSTAT) & (1 << (5 + PWM_CH)))
+ break;
+ }
+
+ writel((1 << (5 + PWM_CH)), PWM_TINT_CSTAT);
+
+ tcon &= ~PWM_TCON_START;
+ writel(tcon, PWM_TCON);
+}
+
+/* Session handler */
+static int onewire_session(unsigned char req, unsigned char res[])
+{
+ unsigned int Req;
+ unsigned int *Res;
+ int ints = disable_interrupts();
+ int i;
+ int ret;
+
+ Req = (req << 24) | (crc8_ow(req << 24, 8) << 16);
+ Res = (unsigned int *)res;
+
+ set_pin_value(1);
+ set_pin_as_output();
+ for (i = 0; i < 60; i++)
+ wait_one_tick();
+
+ set_pin_value(0);
+ for (i = 0; i < 2; i++)
+ wait_one_tick();
+
+ for (i = 0; i < 16; i++) {
+ int v = !!(Req & (1U << 31));
+
+ Req <<= 1;
+ set_pin_value(v);
+ wait_one_tick();
+ }
+
+ wait_one_tick();
+ set_pin_as_input();
+ wait_one_tick();
+ for (i = 0; i < 32; i++) {
+ (*Res) <<= 1;
+ (*Res) |= get_pin_value();
+ wait_one_tick();
+ }
+ set_pin_value(1);
+ set_pin_as_output();
+
+ if (ints)
+ enable_interrupts();
+
+ ret = crc8_ow(*Res, 24) == res[0];
+ DBGOUT("req = %02X, res = %02X%02X%02X%02X, ret = %d\n",
+ req, res[3], res[2], res[1], res[0], ret);
+
+ return ret;
+}
+
+static int onewire_i2c_do_request(unsigned char req, unsigned char *buf)
+{
+ unsigned char tx[4];
+ int ret;
+
+ tx[0] = req;
+ tx[1] = crc8_ow(req << 24, 8);
+
+#ifdef CONFIG_DM_I2C
+ if (dm_i2c_write(i2c_dev, 0, tx, 2))
+ return -EIO;
+
+ if (!buf)
+ return 0;
+
+ if (dm_i2c_read(i2c_dev, 0, buf, 4))
+ return -EIO;
+#else
+ if (i2c_write(ONEWIRE_I2C_ADDR, 0, 0, tx, 2))
+ return -EIO;
+
+ if (!buf) /* NO READ */
+ return 0;
+
+ if (i2c_read(ONEWIRE_I2C_ADDR, 0, 0, buf, 4))
+ return -EIO;
+#endif
+
+ ret = crc8_ow((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8), 24);
+ DBGOUT("req = %02X, res = %02X%02X%02X%02X, ret = %02x\n",
+ req, buf[0], buf[1], buf[2], buf[3], ret);
+
+ return (ret == buf[3]) ? 0 : -EIO;
+}
+
+static void onewire_i2c_init(void)
+{
+ unsigned char buf[4];
+ int ret;
+
+#ifdef CONFIG_DM_I2C
+ ret = i2c_get_chip_for_busnum(ONEWIRE_I2C_BUS,
+ ONEWIRE_I2C_ADDR, 0, &i2c_dev);
+#else
+ i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
+ i2c_set_bus_num(ONEWIRE_I2C_BUS);
+
+ ret = i2c_probe(ONEWIRE_I2C_ADDR);
+#endif
+ if (ret)
+ return;
+
+ ret = onewire_i2c_do_request(REQ_INFO, buf);
+ if (!ret) {
+ lcd_id = buf[0];
+ lcd_fwrev = buf[1] * 0x100 + buf[2];
+ bus_type = BUS_I2C;
+ }
+}
+
+void onewire_init(void)
+{
+ /* GPIO, Pull-off */
+ nx_gpio_set_pad_function(__IO_GRP, __IO_IDX, 1);
+ nx_gpio_set_pull_mode(__IO_GRP, __IO_IDX, 2);
+
+ onewire_init_timer();
+ onewire_i2c_init();
+}
+
+int onewire_get_info(unsigned char *lcd, unsigned short *fw_ver)
+{
+ unsigned char res[4];
+ int i;
+
+ if (bus_type == BUS_I2C && lcd_id > 0) {
+ *lcd = lcd_id;
+ *fw_ver = lcd_fwrev;
+ return 0;
+ }
+
+ for (i = 0; i < 3; i++) {
+ if (onewire_session(REQ_INFO, res)) {
+ *lcd = res[3];
+ *fw_ver = res[2] * 0x100 + res[1];
+ lcd_id = *lcd;
+ DBGOUT("lcd = %d, fw_ver = %x\n", *lcd, *fw_ver);
+ return 0;
+ }
+ }
+
+ /* LCD unknown or not connected */
+ *lcd = 0;
+ *fw_ver = -1;
+
+ return -1;
+}
+
+int onewire_get_lcd_id(void)
+{
+ return lcd_id;
+}
+
+int onewire_set_backlight(int brightness)
+{
+ unsigned char res[4];
+ int i;
+
+ if (brightness == current_brightness)
+ return 0;
+
+ if (brightness > 127)
+ brightness = 127;
+ else if (brightness < 0)
+ brightness = 0;
+
+ if (bus_type == BUS_I2C) {
+ onewire_i2c_do_request((REQ_BL | brightness), NULL);
+ current_brightness = brightness;
+ return 0;
+ }
+
+ for (i = 0; i < 3; i++) {
+ if (onewire_session((REQ_BL | brightness), res)) {
+ current_brightness = brightness;
+ return 0;
+ }
+ }
+
+ return -1;
+}
diff --git a/board/friendlyarm/nanopi2/onewire.h b/board/friendlyarm/nanopi2/onewire.h
new file mode 100644
index 0000000..9f6d7cf
--- /dev/null
+++ b/board/friendlyarm/nanopi2/onewire.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0+
+ *
+ * Copyright (C) Guangzhou FriendlyARM Computer Tech. Co., Ltd.
+ * (http://www.friendlyarm.com)
+ */
+
+#ifndef __ONE_WIRE_H__
+#define __ONE_WIRE_H__
+
+extern void onewire_init(void);
+extern int onewire_get_info(unsigned char *lcd, unsigned short *fw_ver);
+extern int onewire_get_lcd_id(void);
+extern int onewire_set_backlight(int brightness);
+
+#endif /* __ONE_WIRE_H__ */