diff --git a/config/boards/nanopifire3.wip b/config/boards/nanopifire3.wip new file mode 100644 index 000000000000..557c56f78734 --- /dev/null +++ b/config/boards/nanopifire3.wip @@ -0,0 +1,16 @@ +# S5P6818 octa core 1Gb SoC +BOARD_NAME="NanoPi Fire3" +BOARDFAMILY="s5p6818" +BOOTCONFIG="nanopifire3_defconfig" +# +MODULES="" +MODULES_NEXT="g_serial" +CPUMIN="400000" +CPUMAX="1400000" +# +KERNEL_TARGET="next" +CLI_TARGET="stretch:next" +DESKTOP_TARGET="xenial:next" +# +CLI_BETA_TARGET="bionic:next" +# diff --git a/config/kernel/linux-s5p6818-next.config b/config/kernel/linux-s5p6818-next.config index e49824b9d36a..2e7e53886e2b 100644 --- a/config/kernel/linux-s5p6818-next.config +++ b/config/kernel/linux-s5p6818-next.config @@ -2366,8 +2366,8 @@ CONFIG_THERMAL_GOV_STEP_WISE=y # CONFIG_THERMAL_GOV_BANG_BANG is not set # CONFIG_THERMAL_GOV_USER_SPACE is not set # CONFIG_THERMAL_GOV_POWER_ALLOCATOR is not set -# CONFIG_CPU_THERMAL is not set -# CONFIG_THERMAL_EMULATION is not set +CONFIG_CPU_THERMAL=y +CONFIG_THERMAL_EMULATION=y # CONFIG_QORIQ_THERMAL is not set # @@ -2553,6 +2553,7 @@ CONFIG_REGULATOR_AXP228=y # CONFIG_REGULATOR_PV88080 is not set # CONFIG_REGULATOR_PV88090 is not set # CONFIG_REGULATOR_PWM is not set +CONFIG_REGULATOR_SPU1705=y # CONFIG_REGULATOR_TPS51632 is not set # CONFIG_REGULATOR_TPS62360 is not set # CONFIG_REGULATOR_TPS65023 is not set diff --git a/patch/kernel/s5p6818-next/update-nanopi-fire3-support.patch b/patch/kernel/s5p6818-next/update-nanopi-fire3-support.patch new file mode 100644 index 000000000000..8a66c7f88e8e --- /dev/null +++ b/patch/kernel/s5p6818-next/update-nanopi-fire3-support.patch @@ -0,0 +1,1591 @@ +diff --git a/arch/arm64/boot/dts/nexell/Makefile b/arch/arm64/boot/dts/nexell/Makefile +index 46364d6..7f4dcad 100644 +--- a/arch/arm64/boot/dts/nexell/Makefile ++++ b/arch/arm64/boot/dts/nexell/Makefile +@@ -1,4 +1,5 @@ + dtb-$(CONFIG_ARCH_S5P6818) += s5p6818-nanopi-m3.dtb ++dtb-$(CONFIG_ARCH_S5P6818) += s5p6818-nanopi-fire3.dtb + + always := $(dtb-y) + subdir-y := $(dts-dirs) +diff --git a/arch/arm64/boot/dts/nexell/s5p6818-nanopi-fire3.dts b/arch/arm64/boot/dts/nexell/s5p6818-nanopi-fire3.dts +new file mode 100644 +index 0000000..dc9041e +--- /dev/null ++++ b/arch/arm64/boot/dts/nexell/s5p6818-nanopi-fire3.dts +@@ -0,0 +1,814 @@ ++/* ++ * Copyright (C) 2016 Nexell Co., Ltd. ++ * Author: Youngbok, Park ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++/dts-v1/; ++#include ++#include ++#include ++#include "s5p6818.dtsi" ++ ++#define PMIC_PDATA_INIT(_id, _rname, _minuv, \ ++ _maxuv, _always_on, _boot_on, \ ++ _init_uv, _init_enable, _slp_slots) \ ++ regulator-name = _rname; \ ++ regulator-min-microvolt = <_minuv>; \ ++ regulator-max-microvolt = <_maxuv>; \ ++ nx,id = <_id>; \ ++ nx,always_on = <_always_on>; \ ++ nx,boot_on = <_boot_on>; \ ++ nx,init_enable = <_init_enable>; \ ++ nx,init_uV = <_init_uv>; \ ++ nx,sleep_slots = <_slp_slots>; ++ ++/ { ++ memory { ++ /* Note: Samsung Artik u-boot fixates memory information to values ++ * specified by CONFIG_SYS_SDRAM_BASE and CONFIG_SYS_SDRAM_SIZE in ++ * the u-boot configuration. Values specified below are meaningless. ++ */ ++ device_type = "memory"; ++ reg = <0x40000000 0x40000000>; ++ }; ++ ++ aliases { ++ ethernet0 = &gmac0; ++ }; ++ ++ nx-v4l2 { ++ status = "okay"; ++ }; ++ ++ soc { ++ #include "s5p6818-pinctrl.dtsi" ++ ++ clocks { ++ uart0:uart@c00a9000 { clock-frequency = <147500000>; }; ++ uart1:uart@c00a8000 { clock-frequency = <147500000>; }; ++ uart2:uart@c00aa000 { clock-frequency = <147500000>; }; ++ uart3:uart@c00ab000 { clock-frequency = <147500000>; }; ++ uart4:uart@c006e000 { clock-frequency = <147500000>; }; ++ uart5:uart@c0084000 { clock-frequency = <147500000>; }; ++ pwm0:pwm0@c00ba000 { clock-frequency = <100000000>; }; ++ i2c0:i2c@c00ae000 { clock-frequency = <200000000>; }; ++ i2c1:i2c@c00af000 { clock-frequency = <200000000>; }; ++ i2c2:i2c@c00b0000 { clock-frequency = <200000000>; }; ++ vip1:vip@c00c2000 { src-force = <4>; }; ++ }; ++ ++ serial0:serial@c00a1000 { ++ status ="okay"; ++ }; ++ ++ serial1:serial@c00a0000 { ++ status ="okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&serial1_pin &serial1_flow>; ++ }; ++ ++ amba { ++ pl08xdma0:pl08xdma@c0000000 { ++ use_isr; ++ ++ ch12 { ++ slave_wait_flush_dma; ++ }; ++ ++ ch13 { ++ slave_wait_flush_dma; ++ }; ++ ++ ch14 { ++ slave_wait_flush_dma; ++ }; ++ ++ ch15 { ++ slave_wait_flush_dma; ++ }; ++ }; ++ ++ pl08xdma1:pl08xdma@c0001000 { ++ use_isr; ++ ++ ch0 { ++ slave_wait_flush_dma; ++ }; ++ ++ ch1 { ++ slave_wait_flush_dma; ++ }; ++ }; ++ }; ++ ++ dw_mmc_0:dw_mmc@c0062000 { // mappings from kernel 3.x: ++ bus-width = <4>; // MMC_CAP_4_BIT_DATA ++ cap-sd-highspeed; // DW_MCI_QUIRK_HIGHSPEED ++ cap-mmc-highspeed; // also DW_MCI_QUIRK_HIGHSPEED ++ clock-frequency = <100000000>; // bus_hz: 100 * 1000 * 1000 ++ card-detect-delay = <200>; // detect_delay_ms ++ disable-wp; // write protect: -> get_ro; feature not available for micro SD ++ cd-gpios = <&alive_0 1 GPIO_ACTIVE_LOW>; // card detect: CFG_SDMMC0_DETECT_IO == PAD_GPIO_ALV + 1 ++ nexell,drive_dly = <0x0>; // DW_MMC_DRIVE_DELAY(0) ++ nexell,drive_shift = <0x02>; // DW_MMC_DRIVE_PHASE(2) ++ nexell,sample_dly = <0x00>; // DW_MMC_SAMPLE_DELAY(0) ++ nexell,sample_shift = <0x01>; // DW_MMC_SAMPLE_PHASE(1) ++ status = "okay"; ++ }; ++ ++ dw_mmc_1:dw_mmc@c0068000 { ++ bus-width = <4>; ++ cap-sd-highspeed; ++ clock-frequency = <100000000>; ++ card-detect-delay = <200>; ++ non-removable; ++ keep-power-in-suspend; ++ nexell,drive_dly = <0x0>; ++ nexell,drive_shift = <0x02>; ++ nexell,sample_dly = <0x00>; ++ nexell,sample_shift = <0x01>; ++ mmc-pwrseq = <&wifi_powerseq>; ++ status = "okay"; ++ ++ /* wifi definition for brcmfmac.ko module */ ++ brcmf: bcrmf@1 { ++ compatible = "brcm,bcm4329-fmac"; ++ reg = <1>; ++ interrupt-parent = <&gpio_c>; ++ interrupts = <17 IRQ_TYPE_LEVEL_HIGH>; ++ brcm,powersave-default-off; ++ }; ++ }; ++ ++ dw_mmc_2:dw_mmc@c0069000 { ++ bus-width = <4>; // MMC_CAP_4_BIT_DATA ++ cap-sd-highspeed; // DW_MCI_QUIRK_HIGHSPEED ++ cap-mmc-highspeed; // also DW_MCI_QUIRK_HIGHSPEED ++ sd-uhs-ddr50; // MMC_CAP_UHS_DDR50 ++ cap-mmc-hw-reset; // MMC_CAP_HW_RESET ++ clock-frequency = <200000000>; // bus_hz: 200 * 1000 * 1000 ++ card-detect-delay = <200>; // detect_delay_ms ++ non-removable; // MMC_CAP_NONREMOVABLE ++ broken-cd; ++ cd-gpios = <&gpio_c 24 GPIO_ACTIVE_LOW>; // card detect: CFG_SDMMC2_DETECT_IO == PAD_GPIO_C + 24 ++ nexell,drive_dly = <0x0>; // DW_MMC_DRIVE_DELAY(0) ++ nexell,drive_shift = <0x03>; // DW_MMC_DRIVE_PHASE(3) ++ nexell,sample_dly = <0x00>; // DW_MMC_SAMPLE_DELAY(0) ++ nexell,sample_shift = <0x02>; // DW_MMC_SAMPLE_PHASE(2) ++ status = "okay"; ++ }; ++ ++ dvfs:dynamic-freq@bb000 { ++ supply_name = "vdd_arm_spu"; ++ supply_optional = "vdd_arm_axp"; ++ vdd_arm_spu-supply = <&VCC1P1_ARM_SPU>; ++ vdd_arm_axp-supply = <&VCC1P1_ARM_PMIC>; ++ status = "okay"; ++ dvfs-tables = < 1400000 1200000 ++ 1300000 1140000 ++ 1200000 1100000 ++ 1100000 1040000 ++ 1000000 1040000 ++ 900000 1000000 ++ 800000 1000000 ++ 700000 980000 ++ 600000 980000 ++ 500000 940000 ++ 400000 940000>; ++ }; ++ ++ tmuctrl_0: tmuctrl@c0096000 { ++ status = "okay"; ++ }; ++ ++ thermal-zones { ++ cpu0_thermal: cpu0-thermal { ++ thermal-sensors = <&tmuctrl_0>; ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ trips { ++ cpu_alert0: cpu-alert-0 { ++ temperature = <80000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ ++ cpu_alert1: cpu-alert-1 { ++ temperature = <85000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ ++ cpu_alert2: cpu-alert-2 { ++ temperature = <100000>; ++ hysteresis = <2000>; ++ type = "passive"; ++ }; ++ ++ cpu_crit0: cpu-crit-0 { ++ temperature = <115000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ }; ++ ++ cooling-maps { ++ map0 { ++ trip = <&cpu_alert0>; ++ cooling-device = <&cpu0 THERMAL_NO_LIMIT 1>; ++ }; ++ ++ map1 { ++ trip = <&cpu_alert1>; ++ cooling-device = <&cpu0 1 4>; ++ }; ++ ++ map2 { ++ trip = <&cpu_alert2>; ++ cooling-device = <&cpu0 4 10>; ++ }; ++ }; ++ }; ++ }; ++ ++ /* FIXME: bluetooth reset is piggybacked here although their data flow ++ * goes through serial1 */ ++ wifi_powerseq: wifi_powerseq { ++ compatible = "mmc-pwrseq-simple"; ++ reset-gpios = ++ <&gpio_b 24 GPIO_ACTIVE_LOW /* wifi */ ++ &gpio_b 8 GPIO_ACTIVE_LOW>; /* bluetooth */ ++ post-power-on-delay-ms = <50>; ++ }; ++ ++ i2c3_gpio:i2c@0 { ++ compatible = "i2c-gpio"; ++ gpios = <&gpio_e 31 0 /* sda */ ++ &gpio_e 30 0 /* scl */ ++ >; ++ i2c-gpio,delay-us = <10>;/* ~100 kHz */ ++ i2c-gpio,ch =<3>; ++ }; ++ ++ i2c3_gpio:i2c@0 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ spu1705@2d { ++ compatible = "spu1705,fe-pmu"; ++ reg = <0x2d>; ++ regulators { ++ VCC1P1_ARM_SPU: DCDC1 { ++ regulator-name = "vdd_arm_1.3V"; ++ regulator-min-microvolt = <905000>; ++ regulator-max-microvolt = <1265000>; ++ regulator-always-on; ++ }; ++ }; ++ }; ++ ++ axp228@34 { ++ compatible = "x-powers,axp228"; ++ reg = <0x34>; ++ interrupt-parent = <&alive_0>; // CFG_GPIO_PMIC_INTR ++ interrupts = <0x4 IRQ_TYPE_EDGE_FALLING>; ++ nx,id = <0>; ++ /* vdd_arm-supply = <&VCC1P1_ARM_PMIC>; */ ++ /* vdd_core-supply = <&VCC1P0_CORE_PMIC>; */ ++ regulators { ++ VCC_LDO1: ++ axp22_rtcldo{PMIC_PDATA_INIT( 0, ++ "axp228_rtcldo", ++ 3000000, 3000000, 0, 0, 3300000, ++ 0, 0xF) }; ++ VCC_LDO2: ++ axp22_aldo1{PMIC_PDATA_INIT( 1, ++ "axp228_3p3_alive", ++ 700000, 3300000, 1, 1, 3300000, ++ 1, 0xF) }; ++ VCC_LDO3: ++ axp22_aldo2{PMIC_PDATA_INIT( 2, ++ "axp228_1p8_alive", ++ 700000, 3300000, 1, 1, 1800000, ++ 1, 0xF) }; ++ VCC_LDO4: ++ axp22_aldo3{PMIC_PDATA_INIT( 3, ++ "axp228_1p0_alive", ++ 700000, 3300000, 1, 1, 1000000, ++ 1, 0xF) }; ++ VCC_LDO5: ++ axp22_dldo1{PMIC_PDATA_INIT( 4, ++ "axp228_wide", ++ 700000, 3300000, 1, 1, 3300000, ++ 1, 0xF) }; ++ VCC_LDO6: ++ axp22_dldo2{PMIC_PDATA_INIT( 5, ++ "axp228_1p8_cam", ++ 700000, 3300000, 0, 0, 1800000, ++ 0, 0xF) }; ++ VCC_LDO7: ++ axp22_dldo3{PMIC_PDATA_INIT( 6, ++ "axp228_dldo3", ++ 700000, 3300000, 0, 0, 700000, ++ 0, 0xF) }; ++ VCC_LDO8: ++ axp22_dldo4{PMIC_PDATA_INIT( 7, ++ "axp228_dldo4", ++ 700000, 3300000, 0, 0, 700000, ++ 0, 0xF) }; ++ VCC_LDO9: ++ axp22_eldo1{PMIC_PDATA_INIT( 8, ++ "axp228_1p8_sys", ++ 700000, 3300000, 1, 1, 1800000, ++ 1, 0xF) }; ++ VCC_LDO10: ++ axp22_eldo2{PMIC_PDATA_INIT( 9, ++ "axp228_3p3_wifi", ++ 700000, 3300000, 1, 1, 3300000, ++ 1, 0xF) }; ++ VCC_LDO11: ++ axp22_eldo3{PMIC_PDATA_INIT(10, ++ "axp228_eldo3", ++ 700000, 3300000, 0, 0, 700000, ++ 0, 0xF) }; ++ VCC_LDO12: ++ axp22_dc5ldo{PMIC_PDATA_INIT(11, ++ "axp228_1p2_cvbs", ++ 700000, 1400000, 0, 0, 1200000, ++ 0, 0xF) }; ++ VCC_DCDC1: ++ axp22_dcdc1{PMIC_PDATA_INIT(12, ++ "axp228_3p3_sys", ++ 1600000, 3400000, 1, 1, 3300000, ++ 1, 0xF) }; ++ VCC1P1_ARM_PMIC: ++ axp22_dcdc2{PMIC_PDATA_INIT(13, ++ "axp228_1p1_arm", ++ 600000, 1540000, 1, 1, 1200000, ++ 1, 0xF) }; ++ VCC1P0_CORE_PMIC: ++ axp22_dcdc3{PMIC_PDATA_INIT(14, ++ "axp228_1p0_core", ++ 600000, 1860000, 1, 1, 1200000, ++ 1, 0xF) }; ++ VCC_DCDC4: ++ axp22_dcdc4{PMIC_PDATA_INIT(15, ++ "axp228_1p5_sys", ++ 600000, 1540000, 1, 1, 1500000, ++ 1, 0xF) }; ++ VCC_DCDC5: ++ axp22_dcdc5{PMIC_PDATA_INIT(16, ++ "axp228_1p5_ddr", ++ 1000000, 2550000, 1, 1, 1500000, ++ 1, 0xF) }; ++ VCC_LDOIO0: ++ axp22_ldoio0{PMIC_PDATA_INIT(17, ++ "axp228_ldoio0", ++ 700000, 3300000, 0, 0, 1800000, ++ 0, 0xF) }; ++ VCC_LDOIO1: ++ axp22_ldoio1{PMIC_PDATA_INIT(18, ++ "axp228_ldoio1", ++ 700000, 3300000, 0, 0, 1000000, ++ 0, 0xF) }; ++ }; ++ }; ++ }; ++ ++ pinctrl@C0010000 { ++ touchpanel_irq: touchpanel-irq { ++ nexell,pins = "gpioc-16"; ++ nexell,pin-function = <1>; ++ nexell,pin-pull = <2>; ++ nexell,pin-strength = <0>; ++ }; ++ }; ++ ++ nexell_usbphy: nexell-usbphy@c0012000 { ++ status = "okay"; ++ }; ++ ++ ehci@c0030000 { ++ status = "okay"; ++ port@0 { ++ status = "okay"; ++ }; ++ }; ++ ++ ohci@c0020000 { ++ status = "okay"; ++ port@0 { ++ status = "okay"; ++ }; ++ }; ++ ++ dwc2otg@c0040000 { ++ gpios = <&gpio_d 21 0>; ++ status = "okay"; ++ }; ++ ++ gmac0:ethernet@c0060000 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&gmac_pins>; ++ ++ status = "okay"; ++ #address-cells = <0x1>; ++ #size-cells = <0x0>; ++ ++ snps,phy-addr = <7>; ++ snps,reset-gpio = <&gpio_e 22 0>; ++ snps,reset-active-low; ++ snps,reset-delays-us = <0 10000 30000>; ++ ++ mdio { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ ethernet_phy: ethernet-phy@3 { ++ reg = <3>; ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ }; ++ }; ++ ++ i2c_0:i2c@c00a4000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ es8316_codec: es8316@11 { ++ #sound-dai-cells = <0>; ++ compatible = "everest,es8316"; ++ reg = <0x11>; ++ }; ++ }; ++ ++ i2c_1:i2c@c00a5000 { ++ status = "okay"; ++ }; ++ ++ i2c_2:i2c@c00a6000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ /* Note: touch sensors are registered by onewire */ ++ /*touchscreen@38 { ++ compatible = "edt,edt-ft5506"; ++ reg = <0x38>; ++ interrupt-parent = <&gpio_c>; ++ interrupts = <16 IRQ_TYPE_EDGE_FALLING>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&touchpanel_irq>; ++ touchscreen-size-x = <1280>; ++ touchscreen-size-y = <800>; ++ touchscreen-max-pressure = <255>; ++ };*/ ++ ++ /*touchscreen@46 { ++ compatible = "ite,it7260"; ++ reg = <0x46>; ++ interrupt-parent = <&gpio_c>; ++ interrupts = <16 IRQ_TYPE_LEVEL_LOW>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&touchpanel_irq>; ++ };*/ ++ }; ++ ++ pwm:pwm@c0018000 { ++ // block pwm3_pin - conflicts with spi0_miso (on spi0_bus) drawn on 2.54mm header ++ pinctrl-0 = <&pwm0_pin &pwm1_pin &pwm2_pin>; ++ samsung,pwm-outputs = <0>, <1>, <2>; ++ status = "okay"; ++ }; ++ ++ vip_1:vip@c0064000 { ++ status = "okay"; ++ }; ++ ++ clipper_1:clipper1@c0064000 { ++ status = "okay"; ++ pwms = <&pwm 1 41 0>; /* 1000000000/41 */ ++ interface_type = ; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&vid1_data_clk &vid1_sync> ; ++ port = <0>; ++ external_sync = <0>; ++ data_order = ; ++ interlace = <0>; ++ regulator_names = "axp22_dldo2"; ++ regulator_voltages = <1800000>; ++ ++ gpios = <&gpio_c 4 0 ++ &gpio_c 5 0 ++ &gpio_c 6 0>; ++ ++ sensor { ++ type = ; ++ i2c_name = "SP2518"; ++ i2c_adapter = <0>; ++ addr = <0x30>; ++ }; ++ ++ power { ++ enable_seq = < ++ NX_ACTION_START NX_ACTION_TYPE_GPIO 2 1 0 NX_ACTION_END ++ NX_ACTION_START NX_ACTION_TYPE_PMIC 0 0 NX_ACTION_END ++ NX_ACTION_START NX_ACTION_TYPE_PMIC 1 0 NX_ACTION_END ++ NX_ACTION_START NX_ACTION_TYPE_GPIO 0 0 0 1 0 NX_ACTION_END ++ NX_ACTION_START NX_ACTION_TYPE_CLOCK 1 10 NX_ACTION_END ++ NX_ACTION_START NX_ACTION_TYPE_GPIO 0 0 0 NX_ACTION_END ++ NX_ACTION_START NX_ACTION_TYPE_GPIO 1 1 0 0 1 NX_ACTION_END ++ NX_ACTION_START NX_ACTION_TYPE_GPIO 1 1 100 NX_ACTION_END ++ >; ++ }; ++ }; ++ ++ dp_drm: display_drm { ++ status = "okay"; ++ ports { ++ port@0 { ++ reg = <0>; ++ back_color = < 0x0 >; ++ color_key = < 0x0 >; ++ /* Port 0 has two RGB planes and one video plane. These planes ++ * are arranged in z-order: RGB plane 0 is below plane 1, ++ * video plane may be set at any position in z-order. ++ * ++ * Possible names for RGB planes: "primary", "rgb", "cursor" ++ * Possible name for video plane: "video" ++ * Two RGB plane names and one video plane name may be specified in ++ * "plane-names" property. ++ * RGB plane "primary" will be used as root window. ++ * RGB plane "cursor" will be used for cursor. ++ * RGB plane "rgb" and video plane are overlay planes, normally ++ * not used by X-windows. ++ * ++ * Order of plane names specifies z-order of planes, top to bottom. ++ */ ++ plane-names = "cursor", "video", "primary"; ++ }; ++ port@1 { ++ reg = <1>; ++ back_color = < 0x0 >; ++ color_key = < 0x0 >; ++ /* Port 1 has one RGB plane and one video plane only. */ ++ plane-names = "video", "primary"; ++ }; ++ }; ++ }; ++ ++ dp_drm_hdmi: display_drm_hdmi { ++ ddc-i2c-bus = <&i2c_1>; ++ q_range = <1>; ++ status = "ok"; ++ }; ++ ++ dp_drm_rgb: display_drm_rgb { ++ remote-endpoint = <&rgb_panel>; ++ status = "okay"; ++ ++ dp_control { ++ clk_src_lv0 = <0>; ++ clk_div_lv0 = <16>; ++ clk_src_lv1 = <7>; ++ clk_div_lv1 = <1>; ++ out_format = <3>; ++ invert_field = <0>; ++ swap_rb = <0>; ++ yc_order = <0>; ++ delay_mask = < ((1<<0) | (1<<1) | (1<<2) | (1<<3)) >; ++ d_rgb_pvd = <0>; ++ d_hsync_cp1 = <0>; ++ d_vsync_fram = <0>; ++ d_de_cp2 = <7>; ++ vs_start_offset = <863>; ++ ev_start_offset = <863>; ++ vs_end_offset = <0>; ++ ev_end_offset = <0>; ++ }; ++ }; ++ ++ dp_drm_lvds: display_drm_lvds { ++ status = "ok"; ++ remote-endpoint = <&lvds_panel>; ++ dp_control { ++ clk_src_lv0 = <0>; ++ clk_div_lv0 = <16>; ++ clk_src_lv1 = <7>; ++ clk_div_lv1 = <1>; ++ out_format = <3>; ++ }; ++ }; ++ ++ rtc@c0010c00 { ++ status = "okay"; ++ }; ++ ++ i2s_0:i2s@c0055000 { ++ #sound-dai-cells = <1>; ++ sample-rate = <48000>; ++ frame-bit = <32>; ++ status = "okay"; ++ }; ++ ++ spdif_tx: spdiftx@c0059000 { ++ #sound-dai-cells = <1>; ++ status = "okay"; ++ }; ++ ++ adc:adc@c0053000 { ++ status = "okay"; ++ }; ++ ++ video-codec@c0080000 { ++ status = "okay"; ++ sram = <0 0>; ++ }; ++ ++ scaler@c0066000 { ++ status = "okay"; ++ }; ++ ++ nano-videodev { ++ compatible = "nexell,nano-videodev"; ++ reg = <0xc0102000 0x100>; ++ reg-names = "mlc.0"; ++ status = "okay"; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ ++ green { ++ label = "green"; ++ gpios = <&gpio_b 12 GPIO_ACTIVE_LOW>; ++ linux,default-trigger = "heartbeat"; ++ }; ++ }; ++ ++ }; /*** soc ***/ ++ ++ panel_lvds { ++ compatible = "nanopi,nano-panel"; ++ lvds; ++ status = "okay"; ++ ++ port { ++ lvds_panel: endpoint { ++ }; ++ }; ++ }; ++ ++ panel_rgb { ++ compatible = "nanopi,nano-panel"; ++ status = "okay"; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&dp_rgb_vclk &dp_rgb_vsync &dp_rgb_hsync ++ &dp_rgb_de &dp_rgb_R &dp_rgb_G &dp_rgb_B>; ++ ++ port { ++ rgb_panel: endpoint { ++ }; ++ }; ++ }; ++ ++ spdif_out: spdif-out { ++ #sound-dai-cells = <0>; ++ compatible = "linux,spdif-dit"; ++ status = "okay"; ++ }; ++ ++ /* Audio jack output configured to use with Nexell driver. Not used. ++ */ ++ es8316_sound: es8316@i2s0 { ++ compatible = "nexell,nexell-es8316"; ++ ch = <0>; ++ sample-rate = <48000>; ++ format = "S16"; ++ hpin-support = <0>; ++ hpin-gpio = <&gpio_b 27 0>; ++ hpin-level = <1>; ++ status = "disabled"; ++ }; ++ ++ /* HDMI output configured to use with Nexell driver. Not used also. ++ * ++ * Note that es8316_sound and spdif_sound cannot be enabled together ++ * because of nexell-pcm device used by both. ++ */ ++ spdif_sound { ++ compatible = "nexell,spdif-transceiver"; ++ sample_rate = <48000>; ++ format = "S16"; ++ status = "disabled"; ++ }; ++ ++ jack_sound { ++ compatible = "simple-audio-card"; ++ simple-audio-card,mclk-fs = <256>; ++ simple-audio-card,name = "Jack"; ++ simple-audio-card,widgets = ++ "Headphone", "Headphones", ++ "Microphone", "Microphone"; ++ simple-audio-card,routing = ++ "Headphones", "HPOL", ++ "Headphones", "HPOR", ++ "MIC1", "Microphone"; ++ status = "okay"; ++ ++ simple-audio-card,dai-link@0 { ++ format = "i2s"; ++ cpu { ++ sound-dai = <&i2s_0 0>; ++ }; ++ ++ codec { ++ sound-dai = <&es8316_codec>; ++ }; ++ }; ++ }; ++ ++ hdmi_sound { ++ compatible = "simple-audio-card"; ++ simple-audio-card,mclk-fs = <256>; ++ simple-audio-card,name = "HDMI"; ++ simple-audio-card,widgets = ++ "Headphone", "TV Out"; ++ simple-audio-card,routing = ++ "TV Out", "spdif-out"; ++ status = "okay"; ++ ++ simple-audio-card,dai-link@0 { ++ cpu { ++ sound-dai = <&spdif_tx 0>; ++ }; ++ ++ codec { ++ sound-dai = <&spdif_out>; ++ }; ++ }; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ ++ blue { ++ label = "blue"; ++ gpios = <&gpio_b 12 GPIO_ACTIVE_LOW>; ++ linux,default-trigger = "mmc1"; ++ }; ++ }; ++ ++ wifi_bcm4329 { /* wifi definition for bcmdhd.ko module */ ++ compatible = "nanopi,bcm4329"; ++ interrupt-parent = <&gpio_c>; ++ interrupts = <17 IRQ_TYPE_LEVEL_HIGH>; ++ }; ++ ++ nanopi-thermistor { ++ compatible = "friendlyarm,nanopi-thermistor"; ++ status = "okay"; ++ ++ io-channels = <&adc 2>; ++ io-channel-names = "nanopi-thermistor"; ++ }; ++ ++ nanopi-onewire { ++ interrupt-parent = <&gic>; ++ compatible = "friendlyarm,onewire"; ++ ++ channel-gpio = <&gpio_c 15 0>; ++ reg = ; ++ interrupts = <0 IRQ_TIMER3 0>; ++ irq-timer = <3>; ++ }; ++ ++ onewire-touch { ++ compatible = "friendlyarm,onewire-touch"; ++ interrupt-parent = <&gpio_c>; ++ interrupts = <16 IRQ_TYPE_NONE>; ++ i2c-bus = <&i2c_2>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&touchpanel_irq>; ++ }; ++}; ++ +diff --git a/arch/arm64/configs/nanopim3_defconfig b/arch/arm64/configs/nanopim3_defconfig +index 662657a..fba15ac 100644 +--- a/arch/arm64/configs/nanopim3_defconfig ++++ b/arch/arm64/configs/nanopim3_defconfig +@@ -2032,8 +2032,8 @@ CONFIG_THERMAL_GOV_STEP_WISE=y + # CONFIG_THERMAL_GOV_BANG_BANG is not set + # CONFIG_THERMAL_GOV_USER_SPACE is not set + # CONFIG_THERMAL_GOV_POWER_ALLOCATOR is not set +-# CONFIG_CPU_THERMAL is not set +-# CONFIG_THERMAL_EMULATION is not set ++CONFIG_CPU_THERMAL=y ++CONFIG_THERMAL_EMULATION=y + # CONFIG_QORIQ_THERMAL is not set + + # +@@ -2210,6 +2210,7 @@ CONFIG_REGULATOR_AXP228=y + # CONFIG_REGULATOR_PV88080 is not set + # CONFIG_REGULATOR_PV88090 is not set + # CONFIG_REGULATOR_PWM is not set ++CONFIG_REGULATOR_SPU1705=y + # CONFIG_REGULATOR_TPS51632 is not set + # CONFIG_REGULATOR_TPS62360 is not set + # CONFIG_REGULATOR_TPS65023 is not set +diff --git a/drivers/cpufreq/nexell-cpufreq.c b/drivers/cpufreq/nexell-cpufreq.c +index 94a0012..7acf28b 100644 +--- a/drivers/cpufreq/nexell-cpufreq.c ++++ b/drivers/cpufreq/nexell-cpufreq.c +@@ -713,6 +713,7 @@ static void *nxp_cpufreq_make_table(struct platform_device *pdev, + struct cpufreq_frequency_table *freq_table; + struct cpufreq_asv_ops *ops = &asv_ops; + unsigned long (*plat_tbs)[2] = NULL; ++ unsigned long plat_n_voltage = 0; + int tb_size, asv_size = 0; + int id = 0, n = 0; + +@@ -741,17 +742,22 @@ static void *nxp_cpufreq_make_table(struct platform_device *pdev, + /* make frequency table with platform data */ + if (asv_size > 0) { + for (n = 0, id = 0; tb_size > id && asv_size > n; n++) { +- if (plat_tbs) { +- for (n = 0; asv_size > n; n++) { +- if (plat_tbs[id][0] == +- dvfs_tables[n][0]) { +- dvfs_tables[id][0] = +- dvfs_tables[n][0]; +- dvfs_tables[id][1] = +- dvfs_tables[n][1]; +- break; +- } +- } ++ if (plat_tbs && plat_tbs[id][1] > 0) ++ plat_n_voltage = plat_tbs[id][1]; ++ ++ if (plat_n_voltage) { ++ dvfs_tables[id][0] = plat_tbs[id][0]; ++ dvfs_tables[id][1] = plat_n_voltage; ++ ++ } else if (plat_tbs) { ++ for (n = 0; asv_size > n; n++) { ++ if (plat_tbs[id][0] == dvfs_tables[n][0]) { ++ dvfs_tables[id][0] = dvfs_tables[n][0]; ++ dvfs_tables[id][1] = dvfs_tables[n][1]; ++ break; ++ } ++ } ++ + } else { + if (dvfs_tables[n][0] > FREQ_MAX_FREQ_KHZ) + continue; +@@ -775,6 +781,10 @@ static void *nxp_cpufreq_make_table(struct platform_device *pdev, + } + } + ++ /* disabling ASV table to active user defined one */ ++ if (plat_n_voltage) ++ ops->get_voltage = NULL; ++ + /* End table */ + freq_table[id].frequency = CPUFREQ_TABLE_END; + *table_size = id; +diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig +index 32763d8..db3f7cb 100644 +--- a/drivers/regulator/Kconfig ++++ b/drivers/regulator/Kconfig +@@ -776,6 +776,12 @@ config REGULATOR_STM32_VREFBUF + This driver can also be built as a module. If so, the module + will be called stm32-vrefbuf. + ++config REGULATOR_SPU1705 ++ tristate "FriendlyElec SPU1705 regulator driver" ++ depends on I2C ++ help ++ Say Y here to support the voltage regulators on SPU1705 ++ + config REGULATOR_TI_ABB + tristate "TI Adaptive Body Bias on-chip LDO" + depends on ARCH_OMAP +diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile +index 24b2ff3..4e6ddfe 100644 +--- a/drivers/regulator/Makefile ++++ b/drivers/regulator/Makefile +@@ -96,6 +96,7 @@ obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o + obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o + obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o + obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o ++obj-$(CONFIG_REGULATOR_SPU1705) += spu1705.o + obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o + obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o + obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o +diff --git a/drivers/regulator/spu1705.c b/drivers/regulator/spu1705.c +new file mode 100644 +index 0000000..b021e5d +--- /dev/null ++++ b/drivers/regulator/spu1705.c +@@ -0,0 +1,596 @@ ++/* ++ * Regulator driver for STM32 based PMIC chip ++ * ++ * Copyright (C) Guangzhou FriendlyElec Computer Tech. Co., Ltd. ++ * (http://www.friendlyarm.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, you can access it online at ++ * http://www.gnu.org/licenses/gpl-2.0.html. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct spu1705 { ++ struct device *dev; ++ struct mutex io_lock; ++ struct i2c_client *i2c; ++ int chip_id, chip_rev; ++ int pwm_en; ++ int num_regulators; ++ struct regulator_dev *rdev[SPU1705_NUM_REGULATORS]; ++ void (*pm_power_off)(void); ++#if defined(CONFIG_REGULATOR_SPU1705_REBOOT) ++ struct notifier_block reboot_handler; ++ int blocked_uV; ++#endif ++}; ++ ++static int spu1705_i2c_read(struct i2c_client *client, ++ unsigned char req, unsigned char *buf, int count) ++{ ++ int ret; ++ ++ struct i2c_msg msgs[] = { ++ { ++ .addr = client->addr, ++ .flags = 0, ++ .len = 1, ++ .buf = &req, ++ }, { ++ .addr = client->addr, ++ .flags = I2C_M_RD, ++ .len = count, ++ .buf = buf, ++ }, ++ }; ++ ++ ret = i2c_transfer(client->adapter, &msgs[0], 2); ++ if (ret < 0) { ++ pr_err("spu1705: REQ 0x%02x: i2c xfer error %d\n", req, ret); ++ return ret; ++ } ++ ++ pr_debug("spu1705: resp %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]); ++ return 0; ++} ++ ++static int spu1705_i2c_write(struct i2c_client *client, ++ unsigned char req, unsigned char *buf, int count) ++{ ++ int ret; ++ struct i2c_msg msgs[] = { ++ { ++ .addr = client->addr, ++ .flags = 0, ++ .len = count, ++ .buf = buf, ++ }, ++ }; ++ ++ ret = i2c_transfer(client->adapter, &msgs[0], 1); ++ if (ret < 0) { ++ pr_err("spu1705: REQ 0x%02x: i2c write error %d\n", req, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* Supported commands */ ++#define SPU1705_GET_INFO 0x21 ++#define SPU1705_GET_TIME 0x22 ++#define SPU1705_GET_PWM 0x23 ++#define SPU1705_SET_TIME 0x11 ++#define SPU1705_SET_PWR 0x12 ++#define SPU1705_SET_PWM 0x13 ++ ++/* Supported voltages */ ++#define SPU1705_MIN_uV 905000 ++#define SPU1705_MAX_uV 1265000 ++#define SPU1705_INIT_uV 1200000 ++#define SPU1705_N_VOLTAGES 96 ++ ++#define VOLT_STEP ((SPU1705_MAX_uV - SPU1705_MIN_uV) / SPU1705_N_VOLTAGES) ++ ++static int spu1705_dcdc_list_voltage(struct regulator_dev *dev, unsigned index) ++{ ++ return (SPU1705_MAX_uV - (VOLT_STEP * index)); ++} ++ ++/* DCDC is always enabled */ ++static int spu1705_dcdc_is_enabled(struct regulator_dev *dev) ++{ ++ return 1; ++} ++ ++static int spu1705_dcdc_enable(struct regulator_dev *dev) ++{ ++ return 0; ++} ++ ++static int spu1705_dcdc_disable(struct regulator_dev *dev) ++{ ++ return 0; ++} ++ ++static int spu1705_dcdc_get_voltage(struct regulator_dev *dev) ++{ ++ struct spu1705 *priv = rdev_get_drvdata(dev); ++ int buck = rdev_get_id(dev) - SPU1705_DCDC1; ++ unsigned char pwm[4] = { 0 }; ++ ++ if (!priv->pwm_en) { ++ dev_dbg(priv->dev, "get_voltage: %d (default)\n", SPU1705_INIT_uV); ++ return SPU1705_INIT_uV; ++ } ++ ++ mutex_lock(&priv->io_lock); ++ spu1705_i2c_read(priv->i2c, SPU1705_GET_PWM, pwm, 2); ++ mutex_unlock(&priv->io_lock); ++ ++ dev_dbg(priv->dev, "get_voltage: buck = %d, pwm = %d, vol = %d\n", ++ buck, pwm[buck], (SPU1705_MAX_uV - (VOLT_STEP * pwm[buck]))); ++ ++ return (SPU1705_MAX_uV - (VOLT_STEP * pwm[buck])); ++} ++ ++static int spu1705_dcdc_set_voltage(struct regulator_dev *dev, ++ int min_uV, int max_uV, ++ unsigned int *selector) ++{ ++ struct spu1705 *priv = rdev_get_drvdata(dev); ++ int buck = rdev_get_id(dev) - SPU1705_DCDC1; ++ int index; ++ u8 buf[4]; ++ int ret; ++ ++ index = (SPU1705_MAX_uV - min_uV) / VOLT_STEP; ++ *selector = index; ++ ++#if defined(CONFIG_REGULATOR_SPU1705_REBOOT) ++ if (unlikely(min_uV < priv->blocked_uV)) { ++ dev_dbg(priv->dev, "voltage blocked to %d mV\n", priv->blocked_uV/1000); ++ return 0; ++ } ++#endif ++ ++ mutex_lock(&priv->io_lock); ++ ++ buf[0] = SPU1705_SET_PWM; ++ buf[1] = (buck + 1) & 0xff; ++ buf[2] = index & 0xff; ++ spu1705_i2c_write(priv->i2c, SPU1705_SET_PWM, buf, 3); ++ priv->pwm_en = 1; ++ ++ /* verify write */ ++ buf[0] = 0; ++ buf[1] = 0; ++ ret = spu1705_i2c_read(priv->i2c, SPU1705_GET_PWM, buf, 2); ++ ++ mutex_unlock(&priv->io_lock); ++ ++ if (ret < 0 || buf[buck] != index) ++ return -EIO; ++ ++ dev_dbg(priv->dev, "set DCDC%d (%d, %d) mV --> sel %d\n", buck, ++ min_uV/1000, max_uV/1000, buf[buck]); ++ return 0; ++} ++ ++static struct regulator_ops spu1705_dcdc_ops = { ++ .list_voltage = spu1705_dcdc_list_voltage, ++ .is_enabled = spu1705_dcdc_is_enabled, ++ .enable = spu1705_dcdc_enable, ++ .disable = spu1705_dcdc_disable, ++ .get_voltage = spu1705_dcdc_get_voltage, ++ .set_voltage = spu1705_dcdc_set_voltage, ++}; ++ ++static struct regulator_desc regulators[] = { ++ { ++ .name = "DCDC1", ++ .id = SPU1705_DCDC1, ++ .ops = &spu1705_dcdc_ops, ++ .n_voltages = SPU1705_N_VOLTAGES, ++ .type = REGULATOR_VOLTAGE, ++ .owner = THIS_MODULE, ++ }, ++ { ++ .name = "DCDC2", ++ .id = SPU1705_DCDC2, ++ .ops = &spu1705_dcdc_ops, ++ .n_voltages = SPU1705_N_VOLTAGES, ++ .type = REGULATOR_VOLTAGE, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int spu1705_dt_parse_pdata(struct spu1705 *priv, ++ struct spu1705_platform_data *pdata) ++{ ++ struct device *dev = priv->dev; ++ struct device_node *regulators_np, *reg_np; ++ struct spu1705_regulator_subdev *rdata; ++ int i, ret; ++ ++ regulators_np = of_get_child_by_name(dev->of_node, "regulators"); ++ if (!regulators_np) { ++ dev_err(dev, "could not find regulators sub-node\n"); ++ return -EINVAL; ++ } ++ ++ /* count the number of regulators to be supported in pmic */ ++ ret = of_get_child_count(regulators_np); ++ if (ret <= 0) { ++ dev_err(dev, "Error parsing regulator init data, %d\n", ret); ++ return -EINVAL; ++ } ++ ++ rdata = devm_kzalloc(dev, sizeof(*rdata) * ret, GFP_KERNEL); ++ if (!rdata) { ++ of_node_put(regulators_np); ++ return -ENOMEM; ++ } ++ ++ pdata->num_regulators = ret; ++ pdata->regulators = rdata; ++ ++ for_each_child_of_node(regulators_np, reg_np) { ++ for (i = 0; i < ARRAY_SIZE(regulators); i++) ++ if (!of_node_cmp(reg_np->name, regulators[i].name)) ++ break; ++ ++ if (i == ARRAY_SIZE(regulators)) { ++ dev_warn(dev, "don't know how to configure regulator %s\n", ++ reg_np->name); ++ continue; ++ } ++ ++ rdata->id = i; ++ rdata->initdata = of_get_regulator_init_data(dev, reg_np, ®ulators[i]); ++ rdata->reg_node = reg_np; ++ rdata++; ++ } ++ ++ of_node_put(regulators_np); ++ ++ return 0; ++} ++ ++static int setup_regulators(struct spu1705 *priv, ++ struct spu1705_platform_data *pdata) ++{ ++ struct regulator_config config = { }; ++ int i, err; ++ ++ priv->num_regulators = pdata->num_regulators; ++ ++ for (i = 0; i < pdata->num_regulators; i++) { ++ int id = pdata->regulators[i].id; ++ ++ config.dev = priv->dev; ++ config.driver_data = priv; ++ config.init_data = pdata->regulators[i].initdata; ++ config.of_node = pdata->regulators[i].reg_node; ++ ++ priv->rdev[i] = devm_regulator_register(priv->dev, ®ulators[id], ++ &config); ++ if (IS_ERR(priv->rdev[i])) { ++ err = PTR_ERR(priv->rdev[i]); ++ dev_err(priv->dev, "failed to register regulator %d, err = %d\n", ++ i, err); ++ return err; ++ ++ } ++ } ++ ++ return 0; ++} ++ ++/* Power On/Off support */ ++static struct i2c_client *pm_i2c; ++ ++static void spu1705_power_off(void) ++{ ++ struct spu1705 *priv = i2c_get_clientdata(pm_i2c); ++ u8 buf[4]; ++ ++ buf[0] = SPU1705_SET_PWR; ++ buf[1] = 0x01; ++ spu1705_i2c_write(pm_i2c, SPU1705_SET_PWR, buf, 2); ++ ++ pr_info("spu1705: power off\n"); ++ ++ if (priv->pm_power_off) ++ priv->pm_power_off(); ++} ++ ++#if defined(CONFIG_REGULATOR_SPU1705_REBOOT) ++static int spu1705_restart_handle(struct notifier_block *this, ++ unsigned long mode, void *cmd) ++{ ++ struct spu1705 *priv = ++ container_of(this, struct spu1705, reboot_handler); ++ unsigned int sel; ++ int i; ++ ++ priv->blocked_uV = SPU1705_INIT_uV; ++ ++ for (i = 0; i < priv->num_regulators; i++) ++ spu1705_dcdc_set_voltage(priv->rdev[i], ++ SPU1705_INIT_uV, SPU1705_INIT_uV, &sel); ++ ++ return NOTIFY_DONE; ++} ++#endif ++ ++#define to_i2c_client(d) container_of(d, struct i2c_client, dev) ++ ++static inline void spu1705_tm_to_data(struct rtc_time *tm, u8 *data) ++{ ++ data[0] = tm->tm_hour; ++ data[1] = tm->tm_min; ++ data[2] = tm->tm_sec; ++} ++ ++static ssize_t spu1705_sysfs_show_wakealarm(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct spu1705 *priv = dev_get_drvdata(dev); ++ u8 tm[8]; ++ ssize_t n; ++ int ret; ++ ++ mutex_lock(&priv->io_lock); ++ ret = spu1705_i2c_read(priv->i2c, SPU1705_GET_TIME, tm, 7); ++ mutex_unlock(&priv->io_lock); ++ ++ if (ret < 0) ++ return -EIO; ++ ++ n = sprintf(buf, "%02d:%02d:%02d", tm[1], tm[2], tm[3]); ++ if (tm[0]) ++ n += sprintf(buf + n, " %02d:%02d:%02d\n", tm[4], tm[5], tm[6]); ++ else ++ n += sprintf(buf + n, " disabled\n"); ++ ++ return n; ++} ++ ++#define SPU1705_ALARM_MIN 60 ++ ++static ssize_t spu1705_sysfs_set_wakealarm(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t n) ++{ ++ struct spu1705 *priv = dev_get_drvdata(dev); ++ struct rtc_time tm; ++ struct timeval tv; ++ unsigned long alarm; ++ u8 data[8]; ++ int count = 8; ++ ++ do_gettimeofday(&tv); ++ rtc_time_to_tm(tv.tv_sec, &tm); ++ spu1705_tm_to_data(&tm, &data[2]); ++ ++ alarm = simple_strtoul(buf, NULL, 0); ++ if (alarm > SPU1705_ALARM_MIN) { ++ data[1] = 1; ++ tv.tv_sec += alarm; ++ rtc_time_to_tm(tv.tv_sec, &tm); ++ spu1705_tm_to_data(&tm, &data[5]); ++ dev_info(dev, "wake alarm: %02d:%02d:%02d\n", tm.tm_hour, tm.tm_min, tm.tm_sec); ++ ++ } else if (alarm == 0) { ++ data[1] = 0; ++ count = 2; ++ dev_info(dev, "wake alarm: disabled\n"); ++ ++ } else { ++ dev_err(dev, "invalid alarm %lu (0: disable, >%d: enable)\n", ++ alarm, SPU1705_ALARM_MIN); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&priv->io_lock); ++ ++ data[0] = SPU1705_SET_TIME; ++ spu1705_i2c_write(priv->i2c, SPU1705_SET_TIME, data, count); ++ ++ mutex_unlock(&priv->io_lock); ++ ++ return n; ++} ++ ++static DEVICE_ATTR(wakealarm, S_IRUGO | S_IWUSR, ++ spu1705_sysfs_show_wakealarm, spu1705_sysfs_set_wakealarm); ++ ++static void spu1705_sysfs_add_device(struct spu1705 *priv) ++{ ++ int err; ++ ++ err = device_create_file(priv->dev, &dev_attr_wakealarm); ++ if (err) ++ dev_err(priv->dev, "failed to create alarm attribute, %d\n", err); ++} ++ ++static int sp1705_identify_chip(struct spu1705 *priv) ++{ ++ unsigned char id[4] = { 0 }; ++ ++ if (spu1705_i2c_read(priv->i2c, SPU1705_GET_INFO, id, 4) < 0) ++ return -1; ++ ++ if (!id[0] || !id[1]) ++ return -1; ++ ++ priv->chip_id = id[0]; ++ priv->chip_rev = id[1] * 100 + id[2]; ++ priv->pwm_en = id[3]; ++ ++ return 0; ++} ++ ++static int spu1705_i2c_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct spu1705_platform_data *pdata = client->dev.platform_data; ++ struct spu1705 *priv; ++ int ret; ++ ++ priv = devm_kzalloc(&client->dev, sizeof(struct spu1705), ++ GFP_KERNEL); ++ if (priv == NULL) ++ return -ENOMEM; ++ ++ mutex_init(&priv->io_lock); ++ priv->i2c = client; ++ priv->dev = &client->dev; ++ ++ if (IS_ENABLED(CONFIG_OF) && priv->dev->of_node) { ++ pdata = devm_kzalloc(&client->dev, ++ sizeof(struct spu1705_platform_data), GFP_KERNEL); ++ if (!pdata) { ++ dev_err(&client->dev, "could not allocate memory for pdata\n"); ++ return -ENOMEM; ++ } ++ ++ ret = spu1705_dt_parse_pdata(priv, pdata); ++ if (ret < 0) ++ return ret; ++ } ++ ++ if (!pdata) { ++ dev_err(&client->dev, "No platform init data supplied\n"); ++ return -ENODEV; ++ } ++ ++ if (sp1705_identify_chip(priv) < 0) { ++ dev_err(&client->dev, "failed to detect chip\n"); ++ ret = -ENODEV; ++ goto err_detect; ++ } ++ ++ ret = setup_regulators(priv, pdata); ++ if (ret < 0) ++ goto err_detect; ++ ++ i2c_set_clientdata(client, priv); ++ ++ /* PM hookup */ ++ if (pm_power_off) ++ priv->pm_power_off = pm_power_off; ++ pm_i2c = client; ++ pm_power_off = spu1705_power_off; ++ ++#if defined(CONFIG_REGULATOR_SPU1705_REBOOT) ++ priv->reboot_handler.notifier_call = spu1705_restart_handle; ++ priv->reboot_handler.priority = 192; ++ ret = register_reboot_notifier(&priv->reboot_handler); ++ if (ret) { ++ dev_err(&client->dev, "can't register restart notifier, %d\n", ret); ++ return ret; ++ } ++#endif ++ ++ spu1705_sysfs_add_device(priv); ++ ++ dev_info(&client->dev, "found chip 0x%02x, rev %04d\n", ++ priv->chip_id, priv->chip_rev); ++ return 0; ++ ++err_detect: ++ return ret; ++} ++ ++static int spu1705_i2c_remove(struct i2c_client *i2c) ++{ ++ struct spu1705 *priv = i2c_get_clientdata(i2c); ++ unsigned int sel; ++ int i; ++ ++#if defined(CONFIG_REGULATOR_SPU1705_REBOOT) ++ if (unregister_reboot_notifier(&priv->reboot_handler)) ++ dev_err(priv->dev, "can't unregister restart handler\n"); ++ return -ENODEV; ++ } ++#endif ++ ++ pm_power_off = priv->pm_power_off; ++ ++ for (i = 0; i < priv->num_regulators; i++) ++ spu1705_dcdc_set_voltage(priv->rdev[i], ++ SPU1705_INIT_uV, SPU1705_INIT_uV, &sel); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id spu1705_i2c_id[] = { ++ { "fe-pmu", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, spu1705_i2c_id); ++ ++#if defined(CONFIG_OF) ++static const struct of_device_id spu1705_of_match[] = { ++ { .compatible = "spu1705", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, spu1705_of_match); ++#endif ++ ++static struct i2c_driver spu1705_i2c_driver = { ++ .driver = { ++ .name = "spu1705", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(spu1705_of_match), ++ }, ++ .probe = spu1705_i2c_probe, ++ .remove = spu1705_i2c_remove, ++ .id_table = spu1705_i2c_id, ++}; ++ ++static int __init spu1705_module_init(void) ++{ ++ int ret; ++ ++ ret = i2c_add_driver(&spu1705_i2c_driver); ++ if (ret != 0) ++ pr_err("Failed to register I2C driver: %d\n", ret); ++ ++ return ret; ++} ++module_init(spu1705_module_init); ++ ++static void __exit spu1705_module_exit(void) ++{ ++ i2c_del_driver(&spu1705_i2c_driver); ++} ++module_exit(spu1705_module_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Guangzhou FriendlyElec Computer Tech. Co., Ltd."); ++MODULE_DESCRIPTION("SPU1705 PMIC driver"); +diff --git a/include/linux/regulator/spu1705.h b/include/linux/regulator/spu1705.h +new file mode 100644 +index 0000000..03fce65 +--- /dev/null ++++ b/include/linux/regulator/spu1705.h +@@ -0,0 +1,44 @@ ++/* ++ * STM32 based PMIC chip client interface ++ * ++ * Copyright (C) Guangzhou FriendlyElec Computer Tech. Co., Ltd. ++ * (http://www.friendlyarm.com) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __LINUX_REGULATOR_SPU1705_H ++#define __LINUX_REGULATOR_SPU1705_H ++ ++#include ++ ++#define SPU1705_DCDC1 0 ++#define SPU1705_DCDC2 1 ++ ++#define SPU1705_NUM_REGULATORS 2 ++ ++ ++struct spu1705_regulator_subdev { ++ int id; ++ struct regulator_init_data *initdata; ++ struct device_node *reg_node; ++}; ++ ++struct spu1705_platform_data { ++ int num_regulators; ++ struct spu1705_regulator_subdev *regulators; ++}; ++ ++#endif /* __LINUX_REGULATOR_SPU1705_H */ diff --git a/patch/u-boot/u-boot-s5p6818/update-nanopi-fire3-support.patch b/patch/u-boot/u-boot-s5p6818/update-nanopi-fire3-support.patch new file mode 100644 index 000000000000..b1d205e037eb --- /dev/null +++ b/patch/u-boot/u-boot-s5p6818/update-nanopi-fire3-support.patch @@ -0,0 +1,332 @@ +diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile +index 83afbe8..cc9f1ee 100644 +--- a/arch/arm/dts/Makefile ++++ b/arch/arm/dts/Makefile +@@ -16,7 +16,8 @@ dtb-$(CONFIG_ARCH_S5P4418) += s5p4418-drone.dtb \ + + dtb-$(CONFIG_ARCH_S5P6818) += s5p6818-drone.dtb \ + s5p6818-artik710-raptor.dtb \ +- s5p6818-nanopim3.dtb ++ s5p6818-nanopim3.dtb \ ++ s5p6818-nanopi-fire3.dtb + + dtb-$(CONFIG_EXYNOS5) += exynos5250-arndale.dtb \ + exynos5250-snow.dtb \ +diff --git a/arch/arm/dts/s5p6818-nanopi-fire3.dts b/arch/arm/dts/s5p6818-nanopi-fire3.dts +new file mode 100644 +index 0000000..189d9b5 +--- /dev/null ++++ b/arch/arm/dts/s5p6818-nanopi-fire3.dts +@@ -0,0 +1,87 @@ ++/* ++ * (C) Copyright 2016 Nexell ++ * Youngbok, Park ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++/dts-v1/; ++#include "s5p6818.dtsi" ++ ++/ { ++ model = "NanoPi Fire3 board based on s5p6818"; ++ cpu-model = "S5p6818"; ++ ++ compatible = "nexell,s5p6818"; ++ ++ mmc0:mmc@c0062000 { ++ frequency = <50000000>; ++ nexell,drive_dly = <0x0>; ++ nexell,drive_shift = <0x02>; ++ nexell,sample_dly = <0x00>; ++ nexell,sample_shift = <0x01>; ++ status = "okay"; ++ }; ++ ++ mmc2:mmc@c0069000 { ++ frequency = <50000000>; ++ nexell,drive_dly = <0x0>; ++ nexell,drive_shift = <0x03>; ++ nexell,sample_dly = <0x00>; ++ nexell,sample_shift = <0x02>; ++ nexell,bus-width = <4>; ++ status = "okay"; ++ }; ++ ++ dp0:dp@c0102800 { ++ lcd-type = "hdmi"; ++ status = "okay"; ++ ++ dp-device { ++ preset = <0>; // 0 - 1280x720, 1 - 1920x1080 ++ }; ++ dp-planes { ++ layer_top { ++ screen_width = <280>; ++ screen_height = <120>; ++ back_color = <0>; ++ video_prior = <0>; ++ }; ++ layer_1 { ++ fb_base = <0x46000000>; ++ left = <30>; ++ top = <20>; ++ width = <1220>; ++ height = <680>; ++ pixel_byte = <2>; ++ /* possible format values for rgb layer: ++ * r5g6b5 0x44320000 ++ * b5g6r5 0xc4320000 ++ * x1r5g5b5 0x43420000 ++ * x1b5g5r5 0xc3420000 ++ * x4r4g4b4 0x42110000 ++ * x4b4g4r4 0xc2110000 ++ * x8r3g3b2 0x41200000 ++ * x8b3g3r2 0xc1200000 ++ * a1r5g5b5 0x33420000 ++ * a1b5g5r5 0xb3420000 ++ * a4r4g4b4 0x22110000 ++ * a4b4g4r4 0xa2110000 ++ * a8r3g3b2 0x11200000 ++ * a8b3g3r2 0x91200000 ++ * r8g8b8 0x46530000 ++ * b8g8r8 0xc6530000 ++ * x8r8g8b8 0x46530000 ++ * x8b8g8r8 0xc6530000 ++ * a8r8g8b8 0x06530000 ++ * a8b8g8r8 0x86530000 ++ */ ++ format = <0x44320000>; ++ }; ++ }; ++ }; ++ ++ dwc2otg@c0040000 { ++ status = "okay"; ++ }; ++}; +diff --git a/board/s5p6818/Kconfig b/board/s5p6818/Kconfig +index cee812b..46acb2e 100644 +--- a/board/s5p6818/Kconfig ++++ b/board/s5p6818/Kconfig +@@ -16,6 +16,11 @@ config TARGET_S5P6818_NANOPIM3 + help + Support for s5p6818 NanoPI M3 platform. + ++config TARGET_S5P6818_NANOPIFIRE3 ++ bool "S5P6818_NANOPIFIRE3" ++ help ++ Support for the s5p6818 NanoPi Fire3 platform. ++ + endchoice + + config SYS_VENDOR +@@ -30,4 +35,5 @@ config SYS_CONFIG_NAME + + source "board/s5p6818/drone/Kconfig" + source "board/s5p6818/nanopim3/Kconfig" ++source "board/s5p6818/nanopifire3/Kconfig" + source "board/s5p6818/artik710_raptor/Kconfig" +diff --git a/board/s5p6818/nanopifire3/Kconfig b/board/s5p6818/nanopifire3/Kconfig +new file mode 100644 +index 0000000..af8dffd +--- /dev/null ++++ b/board/s5p6818/nanopifire3/Kconfig +@@ -0,0 +1,7 @@ ++if TARGET_S5P6818_NANOPIFIRE3 ++ ++config SYS_BOARD ++ default "nanopifire3" ++ ++endif ++ +diff --git a/board/s5p6818/nanopifire3/Makefile b/board/s5p6818/nanopifire3/Makefile +new file mode 100644 +index 0000000..1550b5f +--- /dev/null ++++ b/board/s5p6818/nanopifire3/Makefile +@@ -0,0 +1 @@ ++obj-y := board.o +diff --git a/board/s5p6818/nanopifire3/board.c b/board/s5p6818/nanopifire3/board.c +new file mode 100644 +index 0000000..01e84d4 +--- /dev/null ++++ b/board/s5p6818/nanopifire3/board.c +@@ -0,0 +1,149 @@ ++/* ++ * (C) Copyright 2016 Nexell ++ * Hyunseok, Jung ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include ++#include ++#ifdef CONFIG_PWM_NX ++#include ++#endif ++#include ++ ++#include ++#include ++#include ++ ++ ++DECLARE_GLOBAL_DATA_PTR; ++ ++/*------------------------------------------------------------------------------ ++ * intialize nexell soc and board status. ++ */ ++ ++/* call from u-boot */ ++int board_early_init_f(void) ++{ ++ return 0; ++} ++ ++void board_gpio_init(void) ++{ ++ nx_gpio_initialize(); ++ nx_gpio_set_base_address(0, (void *)PHY_BASEADDR_GPIOA); ++ nx_gpio_set_base_address(1, (void *)PHY_BASEADDR_GPIOB); ++ nx_gpio_set_base_address(2, (void *)PHY_BASEADDR_GPIOC); ++ nx_gpio_set_base_address(3, (void *)PHY_BASEADDR_GPIOD); ++ nx_gpio_set_base_address(4, (void *)PHY_BASEADDR_GPIOE); ++} ++ ++#ifdef CONFIG_PWM_NX ++void board_backlight_init(void) ++{ ++ pwm_init(CONFIG_BACKLIGHT_CH, CONFIG_BACKLIGHT_DIV, ++ CONFIG_BACKLIGHT_INV); ++ pwm_config(CONFIG_BACKLIGHT_CH, TO_DUTY_NS(CONFIG_BACKLIGHT_DUTY, ++ CONFIG_BACKLIGHT_HZ), ++ TO_PERIOD_NS(CONFIG_BACKLIGHT_HZ)); ++} ++#endif ++ ++int mmc_get_env_dev(void) ++{ ++ static int envDev = -1; ++ int bl1LoadEmmc, ubootLoadPort; ++ ++ if( envDev == -1 ) { ++ bl1LoadEmmc = readl(PHY_BASEADDR_CLKPWR + SYSRSTCONFIG) >> 19 & 1; ++ printf("loaded from %s", bl1LoadEmmc ? "emmc" : "SD"); ++ ubootLoadPort = readl(SCR_ARM_SECOND_BOOT_REG1); ++ switch( ubootLoadPort ) { ++ case EMMC_PORT_NUM: ++ envDev = 0; ++ if( ! bl1LoadEmmc ) ++ printf("+emmc"); ++ break; ++ case SD_PORT_NUM: ++ if( bl1LoadEmmc ) ++ printf("+SD"); ++ envDev = 1; ++ break; ++ default: ++ printf("+unknown(%d)", ubootLoadPort); ++ envDev = 1; ++ break; ++ } ++ printf(", getting env from MMC %d\n", envDev); ++ } ++ return envDev; ++} ++ ++int board_init(void) ++{ ++ board_gpio_init(); ++#ifdef CONFIG_PWM_NX ++ board_backlight_init(); ++#endif ++ return 0; ++} ++ ++/* u-boot dram initialize */ ++int dram_init(void) ++{ ++ gd->ram_size = CONFIG_SYS_SDRAM_SIZE; ++ return 0; ++} ++ ++/* u-boot dram board specific */ ++void dram_init_banksize(void) ++{ ++ /* set global data memory */ ++ gd->bd->bi_arch_number = machine_arch_type; ++ 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; ++} ++ ++#define ETHER_MAC_TAG "ethmac" ++ ++#define DEFAULT_DTB "s5p6818-nanopi-fire3.dtb" ++ ++int board_late_init(void) ++{ ++ //#### move to configuration #### ++ setenv("fdtfile", DEFAULT_DTB); ++ ++ if( getenv("ethaddr") == NULL ) { ++ char tmp[18]; ++ unsigned char addr[6]; ++ u32 hash[20]; ++ u32 *ec2 = (u32*)0xc006705c; ++ u32 *ecid = (u32*)0xc0067000; ++ ++ memset(hash, 0, sizeof(hash)); ++ memcpy(hash + 12, ETHER_MAC_TAG, sizeof(ETHER_MAC_TAG)); ++ while( (readl(ec2) & 0x8000) == 0 ) ++ udelay(100); ++ hash[4] = readl(ecid); ++ hash[5] = readl(ecid + 1); ++ hash[6] = readl(ecid + 2); ++ hash[7] = readl(ecid + 3); ++ ++ MD5Transform(hash, hash + 4); ++ hash[0] ^= hash[2]; ++ hash[1] ^= hash[3]; ++ ++ memcpy(addr, (char *)hash, 6); ++ addr[0] &= 0xfe; /* clear multicast bit */ ++ addr[0] |= 0x02; ++ ++ sprintf(tmp, "%02x:%02x:%02x:%02x:%02x:%02x", ++ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); ++ setenv("ethaddr", tmp); ++ } ++ return 0; ++} ++ +diff --git a/configs/nanopifire3_defconfig b/configs/nanopifire3_defconfig +new file mode 100644 +index 0000000..13ddfd3 +--- /dev/null ++++ b/configs/nanopifire3_defconfig +@@ -0,0 +1,22 @@ ++CONFIG_ARM=y ++CONFIG_SYS_CONFIG_NAME="s5p6818_nanopim3" ++CONFIG_ARCH_NEXELL=y ++CONFIG_ARCH_S5P6818=y ++CONFIG_SYS_MALLOC_F=y ++CONFIG_DEFAULT_DEVICE_TREE="s5p6818-nanopi-fire3" ++CONFIG_TARGET_S5P6818_NANOPIFIRE3=y ++CONFIG_FIT=y ++# CONFIG_CMD_IMI is not set ++# CONFIG_CMD_IMLS is not set ++# CONFIG_CMD_FLASH is not set ++# CONFIG_CMD_FPGA is not set ++# CONFIG_CMD_SETEXPR is not set ++# CONFIG_CMD_NET is not set ++# CONFIG_CMD_NFS is not set ++CONFIG_CMD_FDISK=y ++CONFIG_CMD_EXT4_IMG_WRITE=y ++CONFIG_CMD_SD_RECOVERY=y ++CONFIG_OF_CONTROL=y ++CONFIG_OF_EMBED=y ++CONFIG_VIDEO_NX=y ++CONFIG_VIDEO_NX_HDMI=y