From: Stefan Bosch Date: Fri, 10 Jul 2020 17:07:37 +0000 (+0200) Subject: arm: add support for SoC s5p4418 (cpu) / nanopi2 board X-Git-Url: http://git.dujemihanovic.xyz/?a=commitdiff_plain;h=d1611086e0058c0b701cd487fc9735ffcc9c6c4c;p=u-boot.git 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 ' 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 --- diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile index 8c955d0d52..0e83e394d5 100644 --- a/arch/arm/cpu/armv7/Makefile +++ b/arch/arm/cpu/armv7/Makefile @@ -42,3 +42,5 @@ obj-$(CONFIG_RMOBILE) += rmobile/ 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 0000000000..321b257b6d --- /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 + +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 0000000000..8add9474ad --- /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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 f01eb7bf5f..a3a1e3fbe4 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -147,6 +147,9 @@ dtb-$(CONFIG_ROCKCHIP_RV1108) += \ 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 0000000000..4deaf10a1c --- /dev/null +++ b/arch/arm/dts/s5p4418-nanopi2.dts @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2020 Stefan Bosch + * + * (C) Copyright 2017 FriendlyElec Computer Tech. Co., Ltd. + * (http://www.friendlyarm.com) + * + * (C) Copyright 2016 Nexell + * Youngbok, Park + */ + +/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 0000000000..a7e1c2c381 --- /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 + * + * 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 0000000000..a4d1a1bd03 --- /dev/null +++ b/arch/arm/dts/s5p4418.dtsi @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2020 Stefan Bosch + * + * (C) Copyright 2016 Nexell + * Youngbok, Park + * + */ + +#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 0000000000..f8f9cfd879 --- /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 0000000000..0f684229ea --- /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 0000000000..c8e2ce7b7c --- /dev/null +++ b/board/friendlyarm/nanopi2/MAINTAINERS @@ -0,0 +1,7 @@ +NANOPI2 BOARD +NANOPC-T2 BOARD +M: Stefan Bosch +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 0000000000..5c8b3b77fc --- /dev/null +++ b/board/friendlyarm/nanopi2/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2016 Nexell +# Hyunseok, Jung + +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 0000000000..68980536ab --- /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 +#include +#include +#include +#include +#ifdef CONFIG_PWM_NX +#include +#endif +#include + +#include +#include +#include +#include + +#include + +#include + +#include "hwrev.h" +#include "onewire.h" +#include "nxp-fb.h" + +#include /* for env_save() */ +#include + +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 +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 0000000000..b1e23a48a8 --- /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 +#include +#include +#include + +#include +#include + +/* Board revision list: + * 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 0000000000..1b1a828afb --- /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 0000000000..7303e53af9 --- /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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 0000000000..d31a03d7ce --- /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 0000000000..994befb1ec --- /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 +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#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 0000000000..9f6d7cfe53 --- /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__ */