1615 lines
41 KiB
Diff
1615 lines
41 KiB
Diff
diff --git a/arch/arm64/boot/dts/nexell/Makefile b/arch/arm64/boot/dts/nexell/Makefile
|
|
index 46364d6a..7f4dcadb 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 00000000..25ac71dd
|
|
--- /dev/null
|
|
+++ b/arch/arm64/boot/dts/nexell/s5p6818-nanopi-fire3.dts
|
|
@@ -0,0 +1,837 @@
|
|
+/*
|
|
+ * Copyright (C) 2016 Nexell Co., Ltd.
|
|
+ * Author: Youngbok, Park <ybpark@nexell.co.kr>
|
|
+ *
|
|
+ * 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 <http://www.gnu.org/licenses/>.
|
|
+ */
|
|
+
|
|
+/dts-v1/;
|
|
+#include <dt-bindings/interrupt-controller/irq.h>
|
|
+#include <dt-bindings/gpio/gpio.h>
|
|
+#include <dt-bindings/thermal/thermal.h>
|
|
+#include <dt-bindings/input/input.h>
|
|
+#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 {
|
|
+ key_power: key_power {
|
|
+ nexell,pins = "alive-0";
|
|
+ nexell,pin-function = <0>;
|
|
+ nexell,pin-pull = <1>;
|
|
+ nexell,pin-strength = <0>;
|
|
+ };
|
|
+
|
|
+ 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 = <NX_CAPTURE_INTERFACE_PARALLEL>;
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&vid1_data_clk &vid1_sync> ;
|
|
+ port = <0>;
|
|
+ external_sync = <0>;
|
|
+ data_order = <NX_VIN_Y0CBY1CR>;
|
|
+ interlace = <0>;
|
|
+ regulator_names = "axp22_dldo2";
|
|
+ regulator_voltages = <1800000>;
|
|
+
|
|
+ gpios = <&gpio_c 4 0
|
|
+ &gpio_c 5 0
|
|
+ &gpio_c 6 0>;
|
|
+
|
|
+ sensor {
|
|
+ type = <NX_CAPTURE_SENSOR_I2C>;
|
|
+ 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 {
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+
|
|
+ gpio_key: gpio_keys {
|
|
+ compatible = "gpio-keys";
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&key_power>;
|
|
+
|
|
+ power {
|
|
+ label = "Power";
|
|
+ gpios = <&alive_0 0 0>;
|
|
+ linux,code = <KEY_POWER>;
|
|
+ gpio-key,wakeup;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ 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 = <PHYS_BASE_TIMER 0x1000>;
|
|
+ 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 662657a2..fba15ac2 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 94a00121..f698a270 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 32763d82..db3f7cb8 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 24b2ff3f..4e6ddfec 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 00000000..b021e5d4
|
|
--- /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 <linux/i2c.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/regulator/driver.h>
|
|
+#include <linux/regulator/of_regulator.h>
|
|
+#include <linux/regulator/spu1705.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/cpufreq.h>
|
|
+#include <linux/rtc.h>
|
|
+#include <linux/reboot.h>
|
|
+
|
|
+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 00000000..03fce65e
|
|
--- /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 <linux/regulator/machine.h>
|
|
+
|
|
+#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 */
|