build/patch/kernel/archive/s5p6818-4.14/update-nanopi-fire3-support.patch

1615 lines
41 KiB
Diff
Raw Permalink Normal View History

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, &regulators[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, &regulators[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 */