aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuodong Xu <guodong.xu@linaro.org>2013-04-07 10:47:43 +0800
committerGuodong Xu <guodong.xu@linaro.org>2013-04-07 10:47:43 +0800
commitdd0720b62da7de8ce61a6bf493490fb436b7cf7f (patch)
treeee3642b80e5df8e4ce2f50d3017eb5c429395e47
parentbb09358585cd95234e627cb7e0d528934ce8f686 (diff)
parent9084bd35830ad6e03c96b6f6f7102eec57d57507 (diff)
Merge remote-tracking branch 'origin/lcd-m2' into m2-candidate-0407
-rw-r--r--Documentation/devicetree/bindings/video/display-timing.txt109
-rw-r--r--arch/arm/boot/dts/hi3620.dtsi1670
-rw-r--r--arch/arm/boot/dts/hi4511.dts78
-rw-r--r--arch/arm/configs/hs_defconfig10
-rw-r--r--arch/arm/mach-hs/hs-dt.c3
-rw-r--r--drivers/clk/Kconfig1
-rw-r--r--drivers/clk/Makefile11
-rw-r--r--drivers/clk/clk-divider.c6
-rw-r--r--drivers/clk/clk-fixed-rate.c3
-rw-r--r--drivers/clk/clk.c169
-rw-r--r--drivers/clk/hisilicon/Kconfig7
-rw-r--r--drivers/clk/hisilicon/Makefile2
-rw-r--r--drivers/clk/hisilicon/clk-hi3620-dsi.c364
-rw-r--r--drivers/clk/hisilicon/clk-hi3xxx.c695
-rw-r--r--drivers/pwm/Kconfig9
-rw-r--r--drivers/pwm/Makefile1
-rw-r--r--drivers/pwm/pwm-hi3620.c186
-rw-r--r--drivers/video/Kconfig22
-rw-r--r--drivers/video/Makefile5
-rw-r--r--drivers/video/display_timing.c24
-rw-r--r--drivers/video/fbmon.c102
-rw-r--r--drivers/video/hisilicon/Kconfig9
-rw-r--r--drivers/video/hisilicon/Makefile2
-rw-r--r--drivers/video/hisilicon/hi3620_dsi.c353
-rw-r--r--drivers/video/hisilicon/hi3620_fb.c650
-rw-r--r--drivers/video/hisilicon/hi3620_fb.h125
-rw-r--r--drivers/video/of_display_timing.c239
-rw-r--r--drivers/video/of_videomode.c54
-rw-r--r--drivers/video/videomode.c39
-rw-r--r--include/asm-generic/vmlinux.lds.h10
-rw-r--r--include/linux/clk-provider.h6
-rw-r--r--include/linux/fb.h8
-rw-r--r--include/linux/platform_data/hi3620-dsi.h32
-rw-r--r--include/uapi/linux/fb.h5
-rw-r--r--include/video/display_timing.h124
-rw-r--r--include/video/of_display_timing.h20
-rw-r--r--include/video/of_videomode.h18
-rw-r--r--include/video/videomode.h48
38 files changed, 4503 insertions, 716 deletions
diff --git a/Documentation/devicetree/bindings/video/display-timing.txt b/Documentation/devicetree/bindings/video/display-timing.txt
new file mode 100644
index 00000000000..150038552bc
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/display-timing.txt
@@ -0,0 +1,109 @@
+display-timing bindings
+=======================
+
+display-timings node
+--------------------
+
+required properties:
+ - none
+
+optional properties:
+ - native-mode: The native mode for the display, in case multiple modes are
+ provided. When omitted, assume the first node is the native.
+
+timing subnode
+--------------
+
+required properties:
+ - hactive, vactive: display resolution
+ - hfront-porch, hback-porch, hsync-len: horizontal display timing parameters
+ in pixels
+ vfront-porch, vback-porch, vsync-len: vertical display timing parameters in
+ lines
+ - clock-frequency: display clock in Hz
+
+optional properties:
+ - hsync-active: hsync pulse is active low/high/ignored
+ - vsync-active: vsync pulse is active low/high/ignored
+ - de-active: data-enable pulse is active low/high/ignored
+ - pixelclk-active: with
+ - active high = drive pixel data on rising edge/
+ sample data on falling edge
+ - active low = drive pixel data on falling edge/
+ sample data on rising edge
+ - ignored = ignored
+ - interlaced (bool): boolean to enable interlaced mode
+ - doublescan (bool): boolean to enable doublescan mode
+
+All the optional properties that are not bool follow the following logic:
+ <1>: high active
+ <0>: low active
+ omitted: not used on hardware
+
+There are different ways of describing the capabilities of a display. The
+devicetree representation corresponds to the one commonly found in datasheets
+for displays. If a display supports multiple signal timings, the native-mode
+can be specified.
+
+The parameters are defined as:
+
+ +----------+-------------------------------------+----------+-------+
+ | | ↑ | | |
+ | | |vback_porch | | |
+ | | ↓ | | |
+ +----------#######################################----------+-------+
+ | # ↑ # | |
+ | # | # | |
+ | hback # | # hfront | hsync |
+ | porch # | hactive # porch | len |
+ |<-------->#<-------+--------------------------->#<-------->|<----->|
+ | # | # | |
+ | # |vactive # | |
+ | # | # | |
+ | # ↓ # | |
+ +----------#######################################----------+-------+
+ | | ↑ | | |
+ | | |vfront_porch | | |
+ | | ↓ | | |
+ +----------+-------------------------------------+----------+-------+
+ | | ↑ | | |
+ | | |vsync_len | | |
+ | | ↓ | | |
+ +----------+-------------------------------------+----------+-------+
+
+Example:
+
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: 1080p24 {
+ /* 1920x1080p24 */
+ clock-frequency = <52000000>;
+ hactive = <1920>;
+ vactive = <1080>;
+ hfront-porch = <25>;
+ hback-porch = <25>;
+ hsync-len = <25>;
+ vback-porch = <2>;
+ vfront-porch = <2>;
+ vsync-len = <2>;
+ hsync-active = <1>;
+ };
+ };
+
+Every required property also supports the use of ranges, so the commonly used
+datasheet description with minimum, typical and maximum values can be used.
+
+Example:
+
+ timing1: timing {
+ /* 1920x1080p24 */
+ clock-frequency = <148500000>;
+ hactive = <1920>;
+ vactive = <1080>;
+ hsync-len = <0 44 60>;
+ hfront-porch = <80 88 95>;
+ hback-porch = <100 148 160>;
+ vfront-porch = <0 4 6>;
+ vback-porch = <0 36 50>;
+ vsync-len = <0 5 6>;
+ };
diff --git a/arch/arm/boot/dts/hi3620.dtsi b/arch/arm/boot/dts/hi3620.dtsi
index c8bbdc42224..c2e7ea59ef4 100644
--- a/arch/arm/boot/dts/hi3620.dtsi
+++ b/arch/arm/boot/dts/hi3620.dtsi
@@ -21,6 +21,67 @@
mshc3 = &dwmmc_3;
};
+ osc32k: osc@0 {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <32768>;
+ clock-output-names = "osc32khz";
+ };
+ osc26m: osc@1 {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <26000000>;
+ clock-output-names = "osc26mhz";
+ };
+ pclk: clk@0 {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <26000000>;
+ clock-output-names = "apb_pclk";
+ };
+ pll_arm0: clk@1 {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <1600000000>;
+ clock-output-names = "armpll0";
+ };
+ pll_arm1: clk@2 {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <1600000000>;
+ clock-output-names = "armpll1";
+ };
+ pll_peri: clk@3 {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <1440000000>;
+ clock-output-names = "armpll2";
+ };
+ pll_usb: clk@4 {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <1440000000>;
+ clock-output-names = "armpll3";
+ };
+ pll_hdmi: clk@5 {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <1188000000>;
+ clock-output-names = "armpll4";
+ };
+ pll_gpu: clk@6 {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <1300000000>;
+ clock-output-names = "armpll5";
+ };
+ test_sd_clk: clk@7 {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <100000000>;
+ clock-output-names = "testsdclk";
+ };
+
amba {
#address-cells = <1>;
#size-cells = <1>;
@@ -32,7 +93,7 @@
compatible = "arm,cortex-a9-twd-timer";
reg = <0xfc000600 0x20>;
interrupts = <1 13 0xf01>;
- clocks = <&armpll0>;
+ clocks = <&pll_arm0>;
};
pmctrl: pmctrl@fca08000 {
@@ -55,678 +116,845 @@
reg = <0xfcd00000 0x2000>;
};
- /*clocks begins*/
- clocks {
- #address-cells = <1>;
- #size-cells = <0>;
-
- osc32k: osc@0 {
- compatible = "fixed-clock";
- #clock-cells = <0>;
- clock-frequency = <32768>;
- clock-output-names = "osc32khz";
- };
-
- osc26m: osc@1 {
- compatible = "fixed-clock";
- #clock-cells = <0>;
- clock-frequency = <26000000>;
- clock-output-names = "osc26mhz";
- };
-
- pclk: clk@0 {
- compatible = "fixed-clock";
- #clock-cells = <0>;
- clock-frequency = <26000000>;
- clock-output-names = "apb_pclk";
- };
-
- timclk0: clk@1 {
- compatible = "fixed-clock";
- #clock-cells = <0>;
- clock-frequency = <60000000>;
- clock-output-names = "timer0";
- };
-
- timclk1: clk@2 {
- compatible = "fixed-clock";
- #clock-cells = <0>;
- clock-frequency = <60000000>;
- clock-output-names = "timer1";
- };
-
- /*------pll clk------*/
- armpll0: pll0 {
- compatible = "hisilicon,pll";
- #clock-cells = <0>;
- clocks = <&osc26m>;
- clock-frequency = <1600000000>;
- clock-output-names = "clk_armpll0";
- };
- armpll1: pll1 {
- compatible = "hisilicon,pll";
- #clock-cells = <0>;
- clocks = <&osc26m>;
- clock-frequency = <1600000000>;
- clock-output-names = "clk_armpll1";
- };
- peripll: pll2 {
- compatible = "hisilicon,pll";
- #clock-cells = <0>;
- clocks = <&osc26m>;
- clock-frequency = <1440000000>;
- clock-output-names = "clk_armpll2";
- };
- testsdclk: testsd {
- compatible = "hisilicon,pll";
- #clock-cells = <0>;
- clocks = <&peripll>;
- clock-frequency = <100000000>;
- };
- usbpll: pll3 {
- compatible = "hisilicon,pll";
- #clock-cells = <0>;
- clocks = <&osc26m>;
- clock-frequency = <1440000000>;
- clock-output-names = "clk_armpll3";
- };
-
- hdmipll: pll4 {
- compatible = "hisilicon,pll";
- #clock-cells = <0>;
- clocks = <&osc26m>;
- clock-frequency = <1188000000>;
- clock-output-names = "clk_armpll4";
- };
-
- gpupll: pll5 {
- compatible = "hisilicon,pll";
- #clock-cells = <0>;
- clocks = <&osc26m>;
- clock-frequency = <1300000000>;
- clock-output-names = "clk_armpll5";
- };
+ sysctrl@fc802000 {
+ compatible = "hisilicon,sysctrl";
+ reg = <0xfc802000 0x1000>;
+ smp_reg = <0x31c>;
+ reboot_reg = <0x4>;
- /*--------------cfgaxi clock--------------------*/
- clk_cfgaxi: cfgaxi {
- compatible = "hisilicon,cfgaxi";
- #clock-cells = <0>;
- clocks = <&peripll>;
+ refclk_uart0: refclk@0 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc26m &pclk>;
+ clock-output-names = "rclk_uart0";
+ /* reg_offset, enable_bits */
+ hisilicon,hi3620-clkmux = <0x100 0x80>;
+ };
+ refclk_uart1: refclk@1 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc26m &pclk>;
+ clock-output-names = "rclk_uart1";
+ hisilicon,hi3620-clkmux = <0x100 0x100>;
+ };
+ refclk_uart2: refclk@2 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc26m &pclk>;
+ clock-output-names = "rclk_uart2";
+ hisilicon,hi3620-clkmux = <0x100 0x200>;
+ };
+ refclk_uart3: refclk@3 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc26m &pclk>;
+ clock-output-names = "rclk_uart3";
+ hisilicon,hi3620-clkmux = <0x100 0x400>;
+ };
+ refclk_uart4: refclk@4 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc26m &pclk>;
+ clock-output-names = "rclk_uart4";
+ hisilicon,hi3620-clkmux = <0x100 0x800>;
+ };
+ refclk_cfgaxi: refclk@5 {
+ compatible = "hisilicon,clk-fixed-factor";
+ #clock-cells = <0>;
+ clocks = <&pll_peri>;
+ clock-output-names = "rclk_cfgaxi";
/*mult, div*/
hisilicon,fixed-factor = <1 30>;
+ };
+ refclk_spi0: refclk@6 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc26m &refclk_cfgaxi>;
+ clock-output-names = "rclk_spi0";
+ hisilicon,hi3620-clkmux = <0x100 0x1000>;
+ };
+ refclk_spi1: refclk@7 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc26m &refclk_cfgaxi>;
+ clock-output-names = "rclk_spi1";
+ hisilicon,hi3620-clkmux = <0x100 0x2000>;
+ };
+ refclk_spi2: refclk@8 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc26m &refclk_cfgaxi>;
+ clock-output-names = "rclk_spi2";
+ hisilicon,hi3620-clkmux = <0x100 0x4000>;
+ };
+ refclk_pwm0: refclk@9 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc32k &osc26m>;
+ clock-output-names = "rclk_pwm0";
+ hisilicon,hi3620-clkmux = <0x104 0x400>;
+ };
+ refclk_pwm1: refclk@10 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc32k &osc26m>;
+ clock-output-names = "rclk_pwm1";
+ hisilicon,hi3620-clkmux = <0x104 0x800>;
+ };
+ refclk_tcxo: refclk@11 {
+ compatible = "hisilicon,clk-fixed-factor";
+ #clock-cells = <0>;
+ clocks = <&osc26m>;
+ clock-output-names = "rclk_tcxo";
+ hisilicon,fixed-factor = <1 4>;
+ };
+ refclk_timer0: refclk@12 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc32k &timerclk01>;
+ clock-output-names = "rclk_tim0";
+ hisilicon,hi3620-clkmux = <0 0x8000>;
+ };
+ refclk_timer1: refclk@13 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc32k &timerclk01>;
+ clock-output-names = "rclk_tim1";
+ hisilicon,hi3620-clkmux = <0 0x20000>;
+ };
+ refclk_timer2: refclk@14 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc32k &timerclk23>;
+ clock-output-names = "rclk_tim2";
+ hisilicon,hi3620-clkmux = <0 0x80000>;
+ };
+ refclk_timer3: refclk@15 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc32k &timerclk23>;
+ clock-output-names = "rclk_tim3";
+ hisilicon,hi3620-clkmux = <0 0x200000>;
+ };
+ refclk_timer4: refclk@16 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc32k &timerclk45>;
+ clock-output-names = "rclk_tim4";
+ hisilicon,hi3620-clkmux = <0x18 0x1>;
+ };
+ refclk_timer5: refclk@17 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc32k &timerclk45>;
+ clock-output-names = "rclk_tim5";
+ hisilicon,hi3620-clkmux = <0x18 0x4>;
+ };
+ refclk_timer6: refclk@18 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc32k &timerclk67>;
+ clock-output-names = "rclk_tim6";
+ hisilicon,hi3620-clkmux = <0x18 0x10>;
+ };
+ refclk_timer7: refclk@19 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc32k &timerclk67>;
+ clock-output-names = "rclk_tim7";
+ hisilicon,hi3620-clkmux = <0x18 0x40>;
+ };
+ refclk_timer8: refclk@20 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc32k &timerclk89>;
+ clock-output-names = "rclk_tim8";
+ hisilicon,hi3620-clkmux = <0x18 0x100>;
+ };
+ refclk_timer9: refclk@21 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc32k &timerclk89>;
+ clock-output-names = "rclk_tim9";
+ hisilicon,hi3620-clkmux = <0x18 0x400>;
+ };
+ refclk_shareAXI: refclk@22 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&pll_usb &pll_peri>;
+ clock-output-names = "rclk_shareAXI";
+ hisilicon,hi3620-clkmux = <0x24 0x8000>;
+ };
+ refclk_mmc1: refclk@23 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&pll_peri &pll_usb>;
+ hisilicon,hi3620-clkmux = <0x108 0x200>;
};
-
- /*-------------------mux clock-------------------*/
- clk_uart0_mux: uart0_mux {
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&osc26m &clk_cfgaxi>;
- /*select register offset, mask*/
- hisilicon,hi3620-clkmux = <0x100 0x80>;
- };
-
- clk_uart1_mux: uart1_mux {
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&osc26m &clk_cfgaxi>;
- hisilicon,hi3620-clkmux = <0x100 0x100>;
- };
-
- clk_uart2_mux: uart2_mux {
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&osc26m &clk_cfgaxi>;
- hisilicon,hi3620-clkmux = <0x100 0x200>;
- };
-
- clk_uart3_mux: uart3_mux {
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&osc26m &clk_cfgaxi>;
- hisilicon,hi3620-clkmux = <0x100 0x400>;
- };
-
- clk_uart4_mux: uart4_mux {
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&osc26m &clk_cfgaxi>;
- hisilicon,hi3620-clkmux = <0x100 0x800>;
- };
-
- /*---------------------gate clock-------------------*/
- clk_uart0: uart0 {
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&clk_uart0_mux>;
- /*enable register, enable bit*/
- hisilicon,hi3620-clkgate = <0x40 16>;
- clock-output-names = "clk_uart0";
- };
-
- clk_uart1: uart1 {
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&clk_uart1_mux>;
- hisilicon,hi3620-clkgate = <0x40 17>;
- clock-output-names = "clk_uart1";
- };
-
- clk_uart2: uart2 {
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&clk_uart2_mux>;
- hisilicon,hi3620-clkgate = <0x40 18>;
- clock-output-names = "clk_uart2";
- };
-
- clk_uart3: uart3 {
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&clk_uart3_mux>;
- hisilicon,hi3620-clkgate = <0x40 19>;
- clock-output-names = "clk_uart3";
- };
-
- clk_uart4: uart4 {
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&clk_uart4_mux>;
- hisilicon,hi3620-clkgate = <0x40 20>;
- clock-output-names = "clk_uart4";
- };
-
- /*-------------------spi clock----------------*/
-
- clk_spi0_mux: spi0_mux {
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&osc26m &clk_cfgaxi>;
- hisilicon,hi3620-clkmux = <0x100 0x1000>;
- };
-
- clk_spi1_mux: spi1_mux {
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&osc26m &clk_cfgaxi>;
- hisilicon,hi3620-clkmux = <0x100 0x2000>;
- };
-
- clk_spi2_mux: spi2_mux {
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&osc26m &clk_cfgaxi>;
- hisilicon,hi3620-clkmux = <0x100 0x4000>;
- };
-
- clk_spi0: spi0 {
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&clk_spi0_mux>;
- hisilicon,hi3620-clkgate = <0x40 21>;
- clock-output-names = "clk_spi0";
+ refclk_mmc2: refclk@24 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&pll_peri &pll_usb>;
+ hisilicon,hi3620-clkmux = <0x140 0x10>;
};
-
- clk_spi1: spi1 {
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&clk_spi1_mux>;
- hisilicon,hi3620-clkgate = <0x40 22>;
- clock-output-names = "clk_spi1";
+ refclk_mmc3: refclk@25 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&pll_peri &pll_usb>;
+ hisilicon,hi3620-clkmux = <0x140 0x200>;
};
-
- clk_spi2: spi2 {
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&clk_spi2_mux>;
- hisilicon,hi3620-clkgate = <0x40 23>;
- clock-output-names = "clk_spi2";
+ refclk_sd: refclk@26 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&pll_peri &pll_usb>;
+ hisilicon,hi3620-clkmux = <0x108 0x10>;
};
-
- /*-------------------mux clock----------------*/
- clk_pwm0_mux: pwm0_mux{
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&osc32k &osc26m>;
- hisilicon,hi3620-clkmux = <0x104 0x400>;
+ refclk_mmc1_parent: refclk@27 {
+ compatible = "hisilicon,hi3620-clk-mux";
+ #clock-cells = <0>;
+ clocks = <&osc26m &div_mmc1>;
+ hisilicon,hi3620-clkmux = <0x108 0x400>;
};
-
- clk_venc_mux: venc_mux{
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&peripll &usbpll>;
+ refclk_venc: refclk@28 {
+ #clock-cells = <0>;
+ clocks = <&pll_peri &pll_usb>;
hisilicon,hi3620-clkmux = <0x10c 0x800>;
};
-
- clk_g2d_mux: g2d_mux{
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&peripll &usbpll>;
+ refclk_g2d: refclk@29 {
+ #clock-cells = <0>;
+ clocks = <&pll_peri &pll_usb>;
hisilicon,hi3620-clkmux = <0x10c 0x20>;
};
-
- clk_vdec_mux: vdec_mux{
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&peripll &usbpll>;
+ refclk_vdec: refclk@30 {
+ #clock-cells = <0>;
+ clocks = <&pll_peri &pll_usb>;
hisilicon,hi3620-clkmux = <0x110 0x20>;
};
-
- clk_vpp_mux: vpp_mux{
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&peripll &usbpll>;
+ refclk_vpp: refclk@31 {
+ #clock-cells = <0>;
+ clocks = <&pll_peri &pll_usb>;
hisilicon,hi3620-clkmux = <0x110 0x800>;
};
-
- clk_ldi0_mux: ldi0_mux{
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&peripll &usbpll &hdmipll>;
+ refclk_ldi0: refclk@32 {
+ #clock-cells = <0>;
+ clocks = <&pll_peri &pll_usb &pll_hdmi>;
hisilicon,hi3620-clkmux = <0x114 0x6000>;
};
-
- clk_ldi1_mux: ldi1_mux{
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&peripll &usbpll &hdmipll>;
+ refclk_ldi1: refclk@33 {
+ #clock-cells = <0>;
+ clocks = <&pll_peri &pll_usb &pll_hdmi>;
hisilicon,hi3620-clkmux = <0x118 0xc000>;
};
- /*----periclock: gate clk, reg offset to sctrl-----*/
-
- clk_sci: sci {
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&osc26m>;
- /*enable register, enable bit*/
- hisilicon,hi3620-clkgate = <0x40 26>;
- clock-output-names = "clk_sci";
- };
-
- clk_dphy0: dphy0 {
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&osc26m>;
- hisilicon,hi3620-clkgate = <0x30 15>;
- clock-output-names = "clk_dphy0";
- };
-
- clk_dphy1: dphy1 {
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&osc26m>;
- hisilicon,hi3620-clkgate = <0x30 16>;
- };
-
- clk_dphy2: dphy2 {
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&osc26m>;
- hisilicon,hi3620-clkgate = <0x30 17>;
- };
-
- clk_kpc: kpc {
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&osc32k>;
- hisilicon,hi3620-clkgate = <0x20 6>;
- };
-
- clk_pwm0_gate: pwm0_gate {
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&osc32k &osc26m>;
- hisilicon,hi3620-clkgate = <0x40 7>;
- };
-
- /*gpio0-----gpio21 gate clock*/
- clk_gpio0: gpio0{
- compatible = "hisilicon,periclock";
- hisilicon,hi3620-clkgate = <0x20 8>;
- clock-output-names = "clk_gpio0";
- };
- clk_gpio1: gpio1{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 9>;
- clock-output-names = "clk_gpio1";
- };
- clk_gpio2: gpio2{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 10>;
- clock-output-names = "clk_gpio2";
- };
- clk_gpio3: gpio3{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 11>;
- clock-output-names = "clk_gpio3";
- };
- clk_gpio4: gpio4{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 12>;
- clock-output-names = "clk_gpio4";
- };
- clk_gpio5: gpio5{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 13>;
- clock-output-names = "clk_gpio5";
- };
- clk_gpio6: gpio6{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 14>;
- clock-output-names = "clk_gpio6";
- };
-
- clk_gpio7: gpio7{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 15>;
- clock-output-names = "clk_gpio7";
- };
- clk_gpio8: gpio8{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 16>;
- clock-output-names = "clk_gpio8";
- };
- clk_gpio9: gpio9{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 17>;
- clock-output-names = "clk_gpio9";
- };
- clk_gpio10: gpio10{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 18>;
- clock-output-names = "clk_gpio10";
- };
- clk_gpio11: gpio11{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 19>;
- clock-output-names = "clk_gpio11";
- };
- clk_gpio12: gpio12{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 20>;
- clock-output-names = "clk_gpio12";
- };
- clk_gpio13: gpio13{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 21>;
- clock-output-names = "clk_gpio13";
- };
- clk_gpio14: gpio14{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 22>;
- clock-output-names = "clk_gpio14";
- };
- clk_gpio15: gpio15{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 23>;
- clock-output-names = "clk_gpio15";
- };
- clk_gpio16: gpio16{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 24>;
- clock-output-names = "clk_gpio16";
- };
- clk_gpio17: gpio17{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 25>;
- clock-output-names = "clk_gpio17";
- };
- clk_gpio18: gpio18{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 26>;
- clock-output-names = "clk_gpio18";
- };
- clk_gpio19: gpio19{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 27>;
- clock-output-names = "clk_gpio19";
- };
- clk_gpio20: gpio20{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 28>;
- clock-output-names = "clk_gpio20";
- };
- clk_gpio21: gpio21{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- hisilicon,hi3620-clkgate = <0x20 29>;
- clock-output-names = "clk_gpio21";
- };
-
- /*i2c clk*/
- clk_i2c0: i2c0{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&clk_cfgaxi>;
- hisilicon,hi3620-clkgate = <0x40 24>;
+ uartclk0: clkgate@0 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_uart0>;
+ clock-output-names = "uartclk0";
+ hisilicon,hi3620-clkreset = <0x98 0x10000>;
+ hisilicon,hi3620-clkgate = <0x40 0x10000>;
+ };
+ uartclk1: clkgate@1 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_uart1>;
+ clock-output-names = "uartclk1";
+ hisilicon,hi3620-clkreset = <0x98 0x20000>;
+ hisilicon,hi3620-clkgate = <0x40 0x20000>;
+ };
+ uartclk2: clkgate@2 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_uart2>;
+ clock-output-names = "uartclk2";
+ hisilicon,hi3620-clkreset = <0x98 0x40000>;
+ hisilicon,hi3620-clkgate = <0x40 0x40000>;
+ };
+ uartclk3: clkgate@3 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_uart3>;
+ clock-output-names = "uartclk3";
+ hisilicon,hi3620-clkreset = <0x98 0x80000>;
+ hisilicon,hi3620-clkgate = <0x40 0x80000>;
+ };
+ uartclk4: clkgate@4 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_uart4>;
+ clock-output-names = "uartclk4";
+ hisilicon,hi3620-clkreset = <0x98 0x100000>;
+ hisilicon,hi3620-clkgate = <0x40 0x100000>;
+ };
+ gpioclk0: clkgate@5 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk0";
+ hisilicon,hi3620-clkreset = <0x80 0x100>;
+ hisilicon,hi3620-clkgate = <0x20 0x100>;
+ };
+ gpioclk1: clkgate@6 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk1";
+ hisilicon,hi3620-clkreset = <0x80 0x200>;
+ hisilicon,hi3620-clkgate = <0x20 0x200>;
+ };
+ gpioclk2: clkgate@7 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk2";
+ hisilicon,hi3620-clkreset = <0x80 0x400>;
+ hisilicon,hi3620-clkgate = <0x20 0x400>;
+ };
+ gpioclk3: clkgate@8 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk3";
+ hisilicon,hi3620-clkreset = <0x80 0x800>;
+ hisilicon,hi3620-clkgate = <0x20 0x800>;
+ };
+ gpioclk4: clkgate@9 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk4";
+ hisilicon,hi3620-clkreset = <0x80 0x1000>;
+ hisilicon,hi3620-clkgate = <0x20 0x1000>;
+ };
+ gpioclk5: clkgate@10 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk5";
+ hisilicon,hi3620-clkreset = <0x80 0x2000>;
+ hisilicon,hi3620-clkgate = <0x20 0x2000>;
+ };
+ gpioclk6: clkgate@11 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk6";
+ hisilicon,hi3620-clkreset = <0x80 0x4000>;
+ hisilicon,hi3620-clkgate = <0x20 0x4000>;
+ };
+ gpioclk7: clkgate@12 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk7";
+ hisilicon,hi3620-clkreset = <0x80 0x8000>;
+ hisilicon,hi3620-clkgate = <0x20 0x8000>;
+ };
+ gpioclk8: clkgate@13 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk8";
+ hisilicon,hi3620-clkreset = <0x80 0x10000>;
+ hisilicon,hi3620-clkgate = <0x20 0x10000>;
+ };
+ gpioclk9: clkgate@14 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk9";
+ hisilicon,hi3620-clkreset = <0x80 0x20000>;
+ hisilicon,hi3620-clkgate = <0x20 0x20000>;
+ };
+ gpioclk10: clkgate@15 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk10";
+ hisilicon,hi3620-clkreset = <0x80 0x40000>;
+ hisilicon,hi3620-clkgate = <0x20 0x40000>;
+ };
+ gpioclk11: clkgate@16 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk11";
+ hisilicon,hi3620-clkreset = <0x80 0x80000>;
+ hisilicon,hi3620-clkgate = <0x20 0x80000>;
+ };
+ gpioclk12: clkgate@17 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk12";
+ hisilicon,hi3620-clkreset = <0x80 0x100000>;
+ hisilicon,hi3620-clkgate = <0x20 0x100000>;
+ };
+ gpioclk13: clkgate@18 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk13";
+ hisilicon,hi3620-clkreset = <0x80 0x200000>;
+ hisilicon,hi3620-clkgate = <0x20 0x200000>;
+ };
+ gpioclk14: clkgate@19 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk14";
+ hisilicon,hi3620-clkreset = <0x80 0x400000>;
+ hisilicon,hi3620-clkgate = <0x20 0x400000>;
+ };
+ gpioclk15: clkgate@20 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk15";
+ hisilicon,hi3620-clkreset = <0x80 0x800000>;
+ hisilicon,hi3620-clkgate = <0x20 0x800000>;
+ };
+ gpioclk16: clkgate@21 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk16";
+ hisilicon,hi3620-clkreset = <0x80 0x1000000>;
+ hisilicon,hi3620-clkgate = <0x20 0x1000000>;
+ };
+ gpioclk17: clkgate@22 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk17";
+ hisilicon,hi3620-clkreset = <0x80 0x2000000>;
+ hisilicon,hi3620-clkgate = <0x20 0x2000000>;
+ };
+ gpioclk18: clkgate@23 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk18";
+ hisilicon,hi3620-clkreset = <0x80 0x4000000>;
+ hisilicon,hi3620-clkgate = <0x20 0x4000000>;
+ };
+ gpioclk19: clkgate@24 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk19";
+ hisilicon,hi3620-clkreset = <0x80 0x8000000>;
+ hisilicon,hi3620-clkgate = <0x20 0x8000000>;
+ };
+ gpioclk20: clkgate@25 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk20";
+ hisilicon,hi3620-clkreset = <0x80 0x10000000>;
+ hisilicon,hi3620-clkgate = <0x20 0x10000000>;
+ };
+ gpioclk21: clkgate@26 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "gpioclk21";
+ hisilicon,hi3620-clkreset = <0x80 0x20000000>;
+ hisilicon,hi3620-clkgate = <0x20 0x20000000>;
+ };
+ spiclk0: clkgate@27 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_spi0>;
+ clock-output-names = "spiclk0";
+ hisilicon,hi3620-clkreset = <0x98 0x200000>;
+ hisilicon,hi3620-clkgate = <0x40 0x200000>;
+ };
+ spiclk1: clkgate@28 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_spi1>;
+ clock-output-names = "spiclk1";
+ hisilicon,hi3620-clkreset = <0x98 0x400000>;
+ hisilicon,hi3620-clkgate = <0x40 0x400000>;
+ };
+ spiclk2: clkgate@29 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_spi2>;
+ clock-output-names = "spiclk2";
+ hisilicon,hi3620-clkreset = <0x98 0x800000>;
+ hisilicon,hi3620-clkgate = <0x40 0x800000>;
+ };
+ pwmclk0: clkgate@30 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_pwm0>;
+ clock-output-names = "pwmclk0";
+ hisilicon,hi3620-clkreset = <0x98 0x80>;
+ hisilicon,hi3620-clkgate = <0x40 0x80>;
+ };
+ pwmclk1: clkgate@31 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_pwm1>;
+ clock-output-names = "pwmclk1";
+ hisilicon,hi3620-clkreset = <0x98 0x100>;
+ hisilicon,hi3620-clkgate = <0x40 0x100>;
+ };
+ timerclk01: clkgate@32 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_tcxo>;
+ clock-output-names = "timerclk01";
+ hisilicon,hi3620-clkreset = <0x80 0x1>;
+ hisilicon,hi3620-clkgate = <0x20 0x3>;
+ };
+ timerclk23: clkgate@33 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_tcxo>;
+ clock-output-names = "timerclk23";
+ hisilicon,hi3620-clkreset = <0x80 0x2>;
+ hisilicon,hi3620-clkgate = <0x20 0xc>;
+ };
+ timerclk45: clkgate@34 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_tcxo>;
+ clock-output-names = "timerclk45";
+ hisilicon,hi3620-clkreset = <0x98 0x8>;
+ hisilicon,hi3620-clkgate = <0x40 0x8>;
+ };
+ timerclk67: clkgate@35 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_tcxo>;
+ clock-output-names = "timerclk67";
+ hisilicon,hi3620-clkreset = <0x98 0x10>;
+ hisilicon,hi3620-clkgate = <0x40 0x10>;
+ };
+ timerclk89: clkgate@36 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_tcxo>;
+ clock-output-names = "timerclk89";
+ hisilicon,hi3620-clkreset = <0x98 0x20>;
+ hisilicon,hi3620-clkgate = <0x40 0x20>;
+ };
+ timclk0: clkgate@37 {
+ compatible = "hisilicon,clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_timer0>;
+ clock-output-names = "timclk0";
+ hisilicon,clkgate-inverted;
+ hisilicon,clkgate = <0 16>;
+ };
+ timclk1: clkgate@38 {
+ compatible = "hisilicon,clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_timer1>;
+ clock-output-names = "timclk1";
+ hisilicon,clkgate-inverted;
+ hisilicon,clkgate = <0 18>;
+ };
+ timclk2: clkgate@39 {
+ compatible = "hisilicon,clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_timer2>;
+ clock-output-names = "timclk2";
+ hisilicon,clkgate-inverted;
+ hisilicon,clkgate = <0 20>;
+ };
+ timclk3: clkgate@40 {
+ compatible = "hisilicon,clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_timer3>;
+ clock-output-names = "timclk3";
+ hisilicon,clkgate-inverted;
+ hisilicon,clkgate = <0 22>;
+ };
+ timclk4: clkgate@41 {
+ compatible = "hisilicon,clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_timer4>;
+ clock-output-names = "timclk4";
+ hisilicon,clkgate-inverted;
+ hisilicon,clkgate = <0x18 0>;
+ };
+ timclk5: clkgate@42 {
+ compatible = "hisilicon,clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_timer5>;
+ clock-output-names = "timclk5";
+ hisilicon,clkgate-inverted;
+ hisilicon,clkgate = <0x18 2>;
+ };
+ timclk6: clkgate@43 {
+ compatible = "hisilicon,clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_timer6>;
+ clock-output-names = "timclk6";
+ hisilicon,clkgate-inverted;
+ hisilicon,clkgate = <0x18 4>;
+ };
+ timclk7: clkgate@44 {
+ compatible = "hisilicon,clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_timer7>;
+ clock-output-names = "timclk7";
+ hisilicon,clkgate-inverted;
+ hisilicon,clkgate = <0x18 6>;
+ };
+ timclk8: clkgate@45 {
+ compatible = "hisilicon,clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_timer8>;
+ clock-output-names = "timclk8";
+ hisilicon,clkgate-inverted;
+ hisilicon,clkgate = <0x18 8>;
+ };
+ timclk9: clkgate@46 {
+ compatible = "hisilicon,clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_timer9>;
+ clock-output-names = "timclk9";
+ hisilicon,clkgate-inverted;
+ hisilicon,clkgate = <0x18 10>;
+ };
+ rtcclk: clkgate@47 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "clk_rtc";
+ hisilicon,hi3620-clkgate = <0x20 0x20>;
+ };
+ i2cclk0: clkgate@48 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
clock-output-names = "clk_i2c0";
- };
- clk_i2c1: i2c1{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&clk_cfgaxi>;
- hisilicon,hi3620-clkgate = <0x40 25>;
+ hisilicon,hi3620-clkgate = <0x40 0x1000000>;
+ };
+ i2cclk1: clkgate@49 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
clock-output-names = "clk_i2c1";
- };
- clk_i2c2: i2c2{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&clk_cfgaxi>;
- hisilicon,hi3620-clkgate = <0x40 28>;
+ hisilicon,hi3620-clkgate = <0x40 0x2000000>;
+ };
+ i2cclk2: clkgate@50 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
clock-output-names = "clk_i2c2";
- };
- clk_i2c3: i2c3{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&clk_cfgaxi>;
- hisilicon,hi3620-clkgate = <0x40 29>;
+ hisilicon,hi3620-clkgate = <0x40 0x10000000>;
+ };
+ i2cclk3: clkgate@51 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
clock-output-names = "clk_i2c3";
- };
-
- /* clk_acp */
- clk_acp: acp{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&clk_cfgaxi>;
- hisilicon,hi3620-clkgate = <0x30 28>;
- };
-
- /*dmac clk*/
- clk_dmac: dmac{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&clk_acp>;
- hisilicon,hi3620-clkgate = <0x50 10>;
+ hisilicon,hi3620-clkgate = <0x40 0x20000000>;
+ };
+ dmaclk: clkgate@52 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&acpclk>;
clock-output-names = "clk_dmac";
+ hisilicon,hi3620-clkgate = <0x50 0x400>;
+ };
+ mcuclk: clkgate@53 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_cfgaxi>;
+ clock-output-names = "clk_mcu";
+ hisilicon,hi3620-clkgate = <0x50 0x1000000>;
+ };
+ ddrcperclk: clkgate@54 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_cfgaxi>;
+ clock-output-names = "clk_ddrc_per";
+ hisilicon,hi3620-clkgate = <0x50 0x200>;
+ };
+ acpclk: clkgate@55 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_cfgaxi>;
+ clock-output-names = "clk_apc";
+ hisilicon,hi3620-clkgate = <0x30 0x10000000>;
+ };
+ mmcclk1: clkgate@56 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&test_sd_clk>;
+ clock-output-names = "clk_mmc1";
+ hisilicon,hi3620-clkgate = <0x50 0x200000>;
+ };
+ mmcclk2: clkgate@57 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&div_mmc2>;
+ clock-output-names = "clk_mmc2";
+ hisilicon,hi3620-clkgate = <0x50 0x400000>;
+ };
+ mmcclk3: clkgate@58 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&div_mmc3>;
+ clock-output-names = "clk_mmc3";
+ hisilicon,hi3620-clkgate = <0x50 0x800000>;
+ };
+ sdclk: clkgate@59 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&test_sd_clk>;
+ clock-output-names = "clk_sd";
+ hisilicon,hi3620-clkgate = <0x50 0x100000>;
+ };
+ kpcclk: clkgate@60 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&osc32k>;
+ clock-output-names = "clk_kpc";
+ hisilicon,hi3620-clkgate = <0x20 0x40>;
};
-
- /*mmc clk*/
- clk_mmc1_mux: mmc1_mux{
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&peripll &usbpll>;
- hisilicon,hi3620-clkmux = <0x108 0x200>;
- };
-
- clk_mmc2_mux: mmc2_mux{
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&peripll &usbpll>;
- hisilicon,hi3620-clkmux = <0x140 0x010>;
- };
-
- clk_mmc3_mux: mmc3_mux{
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&peripll &usbpll>;
- hisilicon,hi3620-clkmux = <0x140 0x200>;
- };
-
- clk_sd_mux: sd_mux{
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&peripll &usbpll>;
- hisilicon,hi3620-clkmux = <0x108 0x010>;
- };
-
-
- /*-----------divider table clk---------------------*/
- clk_div_mmc1: div_mmc1{
- compatible = "hisilicon,divclock";
- #clock-cells = <0>;
- clocks = <&clk_mmc1_mux>;
- hisilicon,clkdiv-table = <&divtable 0x0f 16 &divtable 0x0e 15 &divtable 0x0d 14
- &divtable 0x0c 13 &divtable 0x0b 12 &divtable 0x0a 11
- &divtable 0x09 10 &divtable 0x08 9 &divtable 0x07 8
- &divtable 0x06 7 &divtable 0x05 6 &divtable 0x04 5
- &divtable 0x03 4 &divtable 0x02 3 &divtable 0x01 2
- &divtable 0x00 1>;
- /*divider register offset, shift, width*/
- hisilicon,hi3620-clkdiv = <0x108 5 4>;
- };
-
- clk_div_mmc2: div_mmc2{
- compatible = "hisilicon,divclock";
- #clock-cells = <0>;
- clocks = <&clk_mmc2_mux>;
- hisilicon,clkdiv-table = <&divtable 0x0f 16 &divtable 0x0e 15 &divtable 0x0d 14
- &divtable 0x0c 13 &divtable 0x0b 12 &divtable 0x0a 11
- &divtable 0x09 10 &divtable 0x08 9 &divtable 0x07 8
- &divtable 0x06 7 &divtable 0x05 6 &divtable 0x04 5
- &divtable 0x03 4 &divtable 0x02 3 &divtable 0x01 2
- &divtable 0x00 1>;
- /*divider register offset, shift, width*/
- hisilicon,hi3620-clkdiv = <0x140 0 4>;
- };
-
- clk_div_mmc3: div_mmc3{
- compatible = "hisilicon,divclock";
- #clock-cells = <0>;
- clocks = <&clk_mmc3_mux>;
- hisilicon,clkdiv-table = <&divtable 0x0f 16 &divtable 0x0e 15 &divtable 0x0d 14
- &divtable 0x0c 13 &divtable 0x0b 12 &divtable 0x0a 11
- &divtable 0x09 10 &divtable 0x08 9 &divtable 0x07 8
- &divtable 0x06 7 &divtable 0x05 6 &divtable 0x04 5
- &divtable 0x03 4 &divtable 0x02 3 &divtable 0x01 2
- &divtable 0x00 1>;
- /*divider register offset, shift, width*/
- hisilicon,hi3620-clkdiv = <0x140 5 4>;
- };
-
- clk_div_sd: div_sd{
- compatible = "hisilicon,divclock";
- #clock-cells = <0>;
- clocks = <&clk_sd_mux>;
- hisilicon,clkdiv-table = <&divtable 0x0f 16 &divtable 0x0e 15 &divtable 0x0d 14&divtable 0x0c 13
- &divtable 0x0b 12 &divtable 0x0a 11 &divtable 0x09 10 &divtable 0x08 9
- &divtable 0x07 8 &divtable 0x06 7 &divtable 0x05 6 &divtable 0x04 5
- &divtable 0x03 4 &divtable 0x02 3 &divtable 0x01 2 &divtable 0x00 1>;
- /*divider register offset, shift, width*/
- hisilicon,hi3620-clkdiv = <0x108 0 4>;
- };
-
- /*---------------gate--------------------*/
- clk_mmc1_parent: mmc1_parent{
- compatible = "hisilicon,muxclock";
- #clock-cells = <0>;
- clocks = <&osc26m &clk_div_mmc1>;
- hisilicon,hi3620-clkmux = <0x108 0x400>;
- };
-
- clk_mmc1: mmc1 {
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&testsdclk>;
- /* clocks = <&clk_mmc1_parent>; */
- hisilicon,hi3620-clkgate = <0x50 21>;
- };
-
- clk_mmc2: mmc2 {
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&clk_div_mmc2>;
- hisilicon,hi3620-clkgate = <0x50 22>;
- };
-
- clk_mmc3: mmc3 {
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&clk_div_mmc3>;
- hisilicon,hi3620-clkgate = <0x50 23>;
+ sciclk: clkgate@61 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&osc26m>;
+ clock-output-names = "clk_sci";
+ hisilicon,hi3620-clkgate = <0x40 0x4000000>;
};
-
- clk_sd: sd {
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- /* clocks = <&clk_div_sd>; */
- clocks = <&testsdclk>;
- hisilicon,hi3620-clkgate = <0x50 20>;
+ dphyclk0: clkgate@62 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&osc26m>;
+ clock-output-names = "clk_dphy0";
+ hisilicon,hi3620-clkgate = <0x30 0x8000>;
};
-
- clk_ddrc_per: ddrc_per {
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&clk_cfgaxi>;
- hisilicon,hi3620-clkgate = <0x50 9>;
+ dphyclk1: clkgate@63 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&osc26m>;
+ clock-output-names = "clk_dphy1";
+ hisilicon,hi3620-clkgate = <0x30 0x10000>;
};
-
- clk_mcu: mcu{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
- clocks = <&clk_cfgaxi>;
- hisilicon,hi3620-clkgate = <0x50 24>;
- clock-output-names = "clk_mcu";
+ dphyclk2: clkgate@64 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&osc26m>;
+ clock-output-names = "clk_dphy2";
+ hisilicon,hi3620-clkgate = <0x30 0x20000>;
+ };
+ ldiclk0: clkgate@65 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_ldi0>;
+ clock-output-names = "clk_ldi0";
+ hisilicon,hi3620-clkgate = <0x30 0x200>;
+ };
+ ldiclk1: clkgate@66 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&refclk_ldi1>;
+ clock-output-names = "clk_ldi1";
+ hisilicon,hi3620-clkgate = <0x30 0x800>;
+ };
+ edcclk0: clkgate@67 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&pclk>;
+ clock-output-names = "clk_edc0";
+ hisilicon,hi3620-clkgate = <0x30 0x100>;
};
-
- /*rtc clk*/
- clk_rtc: rtc{
- compatible = "hisilicon,periclock";
- #clock-cells = <0>;
+ edcclk1: clkgate@68 {
+ compatible = "hisilicon,hi3620-clk-gate";
+ #clock-cells = <0>;
clocks = <&pclk>;
- hisilicon,hi3620-clkgate = <0x20 5>;
- clock-output-names = "clk_rtc";
+ clock-output-names = "clk_edc1";
+ hisilicon,hi3620-clkgate = <0x30 0x400>;
};
-
- /*--------------------divider clock -------------------*/
- divtable: clkdiv {
+ dtable: clkdiv@0 {
#hisilicon,clkdiv-table-cells = <2>;
- };
-
- clk_div_shaxi: div_shaxi{
- compatible = "hisilicon,divclock";
- #clock-cells = <0>;
- clocks = <&peripll>;
- hisilicon,clkdiv-table = <&divtable 0x04 5>;
- /*divider register offset, shift, width*/
- hisilicon,hi3620-clkdiv = <0x100 0 5>;
- };
-
- clk_div_cfgaxi: div_cfgaxi{
- compatible = "hisilicon,divclock";
- #clock-cells = <0>;
- clocks = <&clk_div_shaxi>;
- hisilicon,clkdiv-table = <&divtable 0x01 2>;
- hisilicon,hi3620-clkdiv = <0x100 5 2>;
+ };
+ div_shareaxi: clkdiv@1 {
+ compatible = "hisilicon,hi3620-clk-div";
+ #clock-cells = <0>;
+ clocks = <&refclk_shareAXI>;
+ clock-output-names = "shareAXI_div";
+ hisilicon,clkdiv-table = <
+ &dtable 0 1 &dtable 1 2 &dtable 2 3 &dtable 3 4
+ &dtable 4 5 &dtable 5 6 &dtable 6 7 &dtable 7 8
+ &dtable 8 9 &dtable 9 10 &dtable 10 11 &dtable 11 12
+ &dtable 12 13 &dtable 13 14 &dtable 14 15 &dtable 15 16
+ &dtable 16 17 &dtable 17 18 &dtable 18 19 &dtable 19 20
+ &dtable 20 21 &dtable 21 22 &dtable 22 23 &dtable 23 24
+ &dtable 24 25 &dtable 25 26 &dtable 26 27 &dtable 27 28
+ &dtable 28 29 &dtable 29 30 &dtable 30 31 &dtable 31 32>;
+ /* divider register offset, mask */
+ hisilicon,clkdiv = <0x100 0x1f>;
+ };
+ div_cfgaxi: clkdiv@2 {
+ compatible = "hisilicon,hi3620-clk-div";
+ #clock-cells = <0>;
+ clocks = <&div_shareaxi>;
+ clock-output-names = "cfgAXI_div";
+ hisilicon,clkdiv-table = <&dtable 0x01 2>;
+ hisilicon,clkdiv = <0x100 0x60>;
+ };
+ div_mmc1: clkdiv@3 {
+ compatible = "hisilicon,hi3620-clk-div";
+ #clock-cells = <0>;
+ clocks = <&refclk_mmc1>;
+ hisilicon,clkdiv-table = <
+ &dtable 0xf 16 &dtable 0xe 15 &dtable 0xd 14
+ &dtable 0xc 13 &dtable 0xb 12 &dtable 0xa 11
+ &dtable 9 10 &dtable 8 9 &dtable 7 8
+ &dtable 6 7 &dtable 5 6 &dtable 4 5
+ &dtable 3 4 &dtable 2 3 &dtable 1 2
+ &dtable 0 1>;
+ hisilicon,clkdiv = <0x108 0x1e0>;
+ };
+ div_mmc2: clkdiv@4 {
+ compatible = "hisilicon,hi3620-clk-div";
+ #clock-cells = <0>;
+ clocks = <&refclk_mmc2>;
+ hisilicon,clkdiv-table = <
+ &dtable 0xf 16 &dtable 0xe 15 &dtable 0xd 14
+ &dtable 0xc 13 &dtable 0xb 12 &dtable 0xa 11
+ &dtable 9 10 &dtable 8 9 &dtable 7 8
+ &dtable 6 7 &dtable 5 6 &dtable 4 5
+ &dtable 3 4 &dtable 2 3 &dtable 1 2
+ &dtable 0 1>;
+ hisilicon,clkdiv = <0x140 0xf>;
+ };
+ div_mmc3: clkdiv@5 {
+ compatible = "hisilicon,hi3620-clk-div";
+ #clock-cells = <0>;
+ clocks = <&refclk_mmc3>;
+ hisilicon,clkdiv-table = <
+ &dtable 0xf 16 &dtable 0xe 15 &dtable 0xd 14
+ &dtable 0xc 13 &dtable 0xb 12 &dtable 0xa 11
+ &dtable 9 10 &dtable 8 9 &dtable 7 8
+ &dtable 6 7 &dtable 5 6 &dtable 4 5
+ &dtable 3 4 &dtable 2 3 &dtable 1 2
+ &dtable 0 1>;
+ hisilicon,clkdiv = <0x140 0x1e0>;
+ };
+ div_sd: clkdiv@6 {
+ compatible = "hisilicon,hi3620-clk-div";
+ #clock-cells = <0>;
+ clocks = <&refclk_sd>;
+ hisilicon,clkdiv-table = <
+ &dtable 0xf 16 &dtable 0xe 15 &dtable 0xd 14
+ &dtable 0xc 13 &dtable 0xb 12 &dtable 0xa 11
+ &dtable 9 10 &dtable 8 9 &dtable 7 8
+ &dtable 6 7 &dtable 5 6 &dtable 4 5
+ &dtable 3 4 &dtable 2 3 &dtable 1 2
+ &dtable 0 1>;
+ hisilicon,clkdiv = <0x108 0xf>;
};
};
@@ -734,7 +962,7 @@
compatible = "arm,rtc-pl031", "arm,primecell";
reg = <0xfc804000 0x1000>;
interrupts = <0 9 0x4>;
- clocks = <&clk_rtc>;
+ clocks = <&rtcclk>;
clock-names = "apb_pclk";
status = "disabled";
};
@@ -762,8 +990,8 @@
reg = <0xfc800000 0x1000>;
/* timer00 & timer01 */
interrupts = <0 0 4>, <0 1 4>;
- clocks = <&timclk0 &timclk1 &pclk>;
- clock-names = "timer0", "timer1", "apb_pclk";
+ clocks = <&timclk0 &timclk1>;
+ clock-names = "apb_pclk";
status = "disabled";
};
@@ -777,8 +1005,8 @@
reg = <0xfc801000 0x1000>;
/* timer10 & timer11 */
interrupts = <0 2 4>, <0 3 4>;
- clocks = <&timclk0 &timclk1 &pclk>;
- clock-names = "timer0", "timer1", "apb_pclk";
+ clocks = <&timclk2 &timclk3>;
+ clock-names = "apb_pclk";
status = "disabled";
};
@@ -787,8 +1015,8 @@
reg = <0xfca01000 0x1000>;
/* timer20 & timer21 */
interrupts = <0 4 4>, <0 5 4>;
- clocks = <&timclk0 &timclk1 &pclk>;
- clock-names = "timer0", "timer1", "apb_pclk";
+ clocks = <&timclk4 &timclk5>;
+ clock-names = "apb_pclk";
status = "disabled";
};
@@ -797,8 +1025,8 @@
reg = <0xfca02000 0x1000>;
/* timer30 & timer31 */
interrupts = <0 6 4>, <0 7 4>;
- clocks = <&timclk0 &timclk1 &pclk>;
- clock-names = "timer0", "timer1", "apb_pclk";
+ clocks = <&timclk6 &timclk7>;
+ clock-names = "apb_pclk";
status = "disabled";
};
@@ -807,8 +1035,8 @@
reg = <0xfca03000 0x1000>;
/* timer40 & timer41 */
interrupts = <0 96 4>, <0 97 4>;
- clocks = <&timclk0 &timclk1 &pclk>;
- clock-names = "timer0", "timer1", "apb_pclk";
+ clocks = <&timclk8 &timclk9>;
+ clock-names = "apb_pclk";
status = "disabled";
};
@@ -816,7 +1044,7 @@
compatible = "arm,pl011", "arm,primecell";
reg = <0xfcb00000 0x1000>;
interrupts = <0 20 4>;
- clocks = <&clk_uart0>;
+ clocks = <&uartclk0>;
clock-names = "apb_pclk";
status = "disabled";
};
@@ -825,7 +1053,7 @@
compatible = "arm,pl011", "arm,primecell";
reg = <0xfcb01000 0x1000>;
interrupts = <0 21 4>;
- clocks = <&clk_uart1>;
+ clocks = <&uartclk1>;
clock-names = "apb_pclk";
status = "disabled";
};
@@ -834,7 +1062,7 @@
compatible = "arm,pl011", "arm,primecell";
reg = <0xfcb02000 0x1000>;
interrupts = <0 22 4>;
- clocks = <&clk_uart2>;
+ clocks = <&uartclk2>;
clock-names = "apb_pclk";
status = "disabled";
};
@@ -843,7 +1071,7 @@
compatible = "arm,pl011", "arm,primecell";
reg = <0xfcb03000 0x1000>;
interrupts = <0 23 4>;
- clocks = <&clk_uart3>;
+ clocks = <&uartclk3>;
clock-names = "apb_pclk";
status = "disabled";
};
@@ -852,7 +1080,7 @@
compatible = "arm,pl011", "arm,primecell";
reg = <0xfcb04000 0x1000>;
interrupts = <0 24 4>;
- clocks = <&clk_uart4>;
+ clocks = <&uartclk4>;
clock-names = "apb_pclk";
status = "disabled";
};
@@ -867,7 +1095,7 @@
&pmx0 5 0 1 &pmx0 6 1 1 &pmx0 7 2 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk0>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -883,7 +1111,7 @@
&pmx0 6 5 1 &pmx0 7 6 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk1>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -899,7 +1127,7 @@
&pmx0 6 3 1 &pmx0 7 3 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk2>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -915,7 +1143,7 @@
&pmx0 6 11 1 &pmx0 7 11 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk3>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -931,7 +1159,7 @@
&pmx0 6 13 1 &pmx0 7 13 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk4>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -947,7 +1175,7 @@
&pmx0 6 16 1 &pmx0 7 16 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk5>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -963,7 +1191,7 @@
&pmx0 6 18 1 &pmx0 7 19 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk6>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -979,7 +1207,7 @@
&pmx0 6 25 1 &pmx0 7 26 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk7>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -995,7 +1223,7 @@
&pmx0 6 33 1 &pmx0 7 34 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk8>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -1011,7 +1239,7 @@
&pmx0 6 41 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk9>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -1026,7 +1254,7 @@
&pmx0 5 45 1 &pmx0 6 46 1 &pmx0 7 46 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk10>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -1042,7 +1270,7 @@
&pmx0 6 49 1 &pmx0 7 49 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk11>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -1058,7 +1286,7 @@
&pmx0 6 51 1 &pmx0 7 52 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk12>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -1074,7 +1302,7 @@
&pmx0 6 55 1 &pmx0 7 56 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk13>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -1090,7 +1318,7 @@
&pmx0 6 60 1 &pmx0 7 61 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk14>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -1106,7 +1334,7 @@
&pmx0 6 64 1 &pmx0 7 65 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk15>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -1122,7 +1350,7 @@
&pmx0 6 72 1 &pmx0 7 73 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk16>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -1138,7 +1366,7 @@
&pmx0 6 80 1 &pmx0 7 81 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk17>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -1154,7 +1382,7 @@
&pmx0 6 86 1 &pmx0 7 87 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk18>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -1169,7 +1397,7 @@
&pmx0 3 88 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk19>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -1184,7 +1412,7 @@
&pmx0 3 90 1 &pmx0 4 91 1 &pmx0 5 92 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk20>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -1198,7 +1426,7 @@
gpio-ranges = < &pmx0 3 94 1 &pmx0 7 96 1>;
interrupt-controller;
#interrupt-cells = <2>;
- clocks = <&pclk>;
+ clocks = <&gpioclk21>;
clock-names = "apb_pclk";
status = "disable";
};
@@ -1234,20 +1462,13 @@
pinctrl-single,register-width = <32>;
};
- sysctrl@fc802000 {
- compatible = "hisilicon,sysctrl";
- reg = <0xfc802000 0x1000>;
- smp_reg = <0x31c>;
- reboot_reg = <0x4>;
- };
-
dma0: dma@fcd02000 {
compatible = "hisilicon,k3-dma-1.0";
reg = <0xfcd02000 0x1000>;
#dma-cells = <1>;
dma-channels = <27>;
interrupts = <0 12 4>;
- clocks = <&clk_dmac>;
+ clocks = <&dmaclk>;
status = "disable";
};
@@ -1257,7 +1478,7 @@
#size-cells = <0>;
reg = <0xfcb08000 0x1000>;
interrupts = <0 28 4>;
- clocks = <&clk_i2c0>;
+ clocks = <&i2cclk0>;
dmas = <&dma0 18 /* read channel */
&dma0 19>; /* write channel */
dma-names = "rx", "tx";
@@ -1271,7 +1492,7 @@
#size-cells = <0>;
reg = <0xfcb09000 0x1000>;
interrupts = <0 29 4>;
- clocks = <&clk_i2c1>;
+ clocks = <&i2cclk1>;
dmas = <&dma0 20 /* read channel */
&dma0 21>; /* write channel */
dma-names = "rx", "tx";
@@ -1283,7 +1504,7 @@
compatible = "hisilicon,designware-i2c";
reg = <0xfcb0c000 0x1000>;
interrupts = <0 62 4>;
- clocks = <&clk_i2c2>;
+ clocks = <&i2cclk2>;
delay-reg = <0xc 4>;
status = "disabled";
};
@@ -1292,7 +1513,7 @@
compatible = "hisilicon,designware-i2c";
reg = <0xfcb0d000 0x1000>;
interrupts = <0 63 4>;
- clocks = <&clk_i2c3>;
+ clocks = <&i2cclk3>;
delay-reg = <0xc 5>;
status = "disabled";
};
@@ -1301,7 +1522,7 @@
compatible = "hisilicon,hi3620-mcu";
reg = <0xfd000000 0x00010000>;
interrupts = <0 89 4>;
- clocks = <&clk_mcu>;
+ clocks = <&mcuclk>;
status = "disabled";
};
@@ -1312,7 +1533,7 @@
interrupts = <0 17 4>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clk_mmc1>, <&clk_ddrc_per>;
+ clocks = <&mmcclk1>, <&ddrcperclk>;
clock-names = "ciu", "biu";
};
@@ -1323,7 +1544,7 @@
interrupts = <0 16 4>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clk_sd>, <&clk_ddrc_per>;
+ clocks = <&sdclk>, <&ddrcperclk>;
clock-names = "ciu", "biu";
};
@@ -1333,7 +1554,7 @@
interrupts = <0 18 4>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clk_mmc2>;
+ clocks = <&mmcclk2>;
};
dwmmc_3: dwmmc3@fcd06000 {
@@ -1342,14 +1563,73 @@
interrupts = <0 19 4>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clk_mmc3>;
+ clocks = <&mmcclk3>;
};
kpc: kpc@fc805000 {
compatible = "hisilicon,k3_keypad";
reg = <0xfc805000 0x1000>;
interrupts = <0 10 4>;
- clocks = <&clk_kpc>;
+ clocks = <&kpcclk>;
status = "disabled";
};
+ edc0: edc@fa202000 {
+ compatible = "hisilicon,hi3620-fb";
+ reg = <0xfa202000 0x1000>;
+ clocks = <&ldiclk0 &edcclk0 &dsiclk0 &lanebyteclk0>;
+ clock-names = "ldi", "edc", "dsi", "lane";
+ interrupts = <0 38 0x4>, <0 39 0x4>, <0 40 0x4>;
+ interrupt-names = "edc", "ldi", "dsi";
+ status = "disabled";
+
+ dsi2xclk0: clkdsi@0 {
+ compatible = "hisilicon,hi3620-phy";
+ #clock-cells = <0>;
+ clocks = <&osc26m>;
+ clock-output-names = "clk_dsi2x0";
+ };
+ dsiclk0: clkdsi@1 {
+ compatible = "hisilicon,clk-fixed-factor";
+ #clock-cells = <0>;
+ clocks = <&dsi2xclk0>;
+ clock-output-names = "clk_dsi0";
+ /*mult, div*/
+ hisilicon,fixed-factor = <1 2>;
+ };
+ lanebyteclk0: clkdsi@2 {
+ compatible = "hisilicon,clk-fixed-factor";
+ #clock-cells = <0>;
+ clocks = <&dsi2xclk0>;
+ clock-output-names = "clk_lanebyte0";
+ /*mult, div*/
+ hisilicon,fixed-factor = <1 8>;
+ };
+ escclk0: clkdsi@3 {
+ compatible = "hisilicon,hi3620-phy-esc";
+ #clock-cells = <0>;
+ clocks = <&lanebyteclk0>;
+ clock-output-names = "clk_dsi_phy_esc0";
+ };
+ };
+ edc1: edc@fa206900 {
+ compatible = "hisilicon,hi3620-fb";
+ clocks = <&ldiclk1 &edcclk1>;
+ clock-names = "ldi", "edc";
+ status = "disabled";
+ };
+ pwm0: pwm@fca05000 {
+ compatible = "hisilicon,hi3620-pwm";
+ reg = <0xfca05000 0x1000>;
+ clocks = <&pwmclk0>;
+ #pwm-cells = <2>;
+ status = "disabled";
+ };
+ pwm1: pwm@fca06000 {
+ compatible = "hisilicon,hi3620-pwm";
+ reg = <0xfca06000 0x1000>;
+ clocks = <&pwmclk1>;
+ #pwm-cells = <2>;
+ status = "disabled";
+ };
+
};
};
diff --git a/arch/arm/boot/dts/hi4511.dts b/arch/arm/boot/dts/hi4511.dts
index a124213bde0..52914890529 100644
--- a/arch/arm/boot/dts/hi4511.dts
+++ b/arch/arm/boot/dts/hi4511.dts
@@ -443,6 +443,27 @@
0x0f0 0x1 /* GPIO (IOMG59), audio spk & earphone */
>;
};
+ pwm0_pmx_func: pinmux_pwm0_pins@0 {
+ pinctrl-single,pins = <
+ 0x154 0x0 /* PWM0 (IOMG82) */
+ >;
+ };
+ pwm0_pmx_idle: pinmux_pwm0_pins@1 {
+ pinctrl-single,pins = <
+ 0x154 0x1 /* GPIO149 (IOMG82) */
+ >;
+ };
+ pwm1_pmx_func: pinmux_pwm1_pins@0 {
+ pinctrl-single,pins = <
+ 0x158 0x0 /* PWM1 (IOMG83) */
+ >;
+ };
+ pwm1_pmx_idle: pinmux_pwm1_pins@1 {
+ pinctrl-single,pins = <
+ 0x158 0x1 /* GPIO150 (IOMG83) */
+ >;
+ };
+
};
pmx1: pinmux@fc803800 {
@@ -784,6 +805,22 @@
pinctrl-single,bias-pulldown = <0 2 0 2>;
pinctrl-single,bias-pullup = <1 1 0 1>;
};
+ pwm0_cfg_func: pincfg_pwm0_pins@0 {
+ pinctrl-single,pins = <
+ 0x280 0 /* PWM0 (IOCFG168) */
+ >;
+ pinctrl-single,bias-pulldown = <2 2 0 2>;
+ pinctrl-single,bias-pullup = <0 1 0 1>;
+ pinctrl-single,drive-strength = <0x30 0xf0>;
+ };
+ pwm1_cfg_func: pincfg_pwm1_pins@0 {
+ pinctrl-single,pins = <
+ 0x284 0 /* PWM1 (IOCFG169) */
+ >;
+ pinctrl-single,bias-pulldown = <2 2 0 2>;
+ pinctrl-single,bias-pullup = <0 1 0 1>;
+ pinctrl-single,drive-strength = <0x30 0xf0>;
+ };
};
i2c0: i2c@fcb08000 {
@@ -1180,6 +1217,7 @@
regulator-name = "LDO16";
regulator-min-microvolt = <1500000>;
regulator-max-microvolt = <3000000>;
+ regulator-always-on;
hisilicon,hi6421-ctrl = <0x30 0x10 0x20>;
hisilicon,hi6421-vset = <0x30 0x07>;
hisilicon,hi6421-n-voltages = <8>;
@@ -1197,6 +1235,7 @@
regulator-name = "LDO17";
regulator-min-microvolt = <1500000>;
regulator-max-microvolt = <3000000>;
+ regulator-always-on;
hisilicon,hi6421-ctrl = <0x31 0x10 0x20>;
hisilicon,hi6421-vset = <0x31 0x07>;
hisilicon,hi6421-n-voltages = <8>;
@@ -1392,5 +1431,44 @@
mcu: mcu@fd000000 {
status = "ok";
};
+ edc0: edc@fa202000 {
+ hisilicon,pixel-format = "RGBA8888";
+ hisilicon,color-mode = <5>;
+ hisilicon,dsi-clock-frequency = <241000000>; /* 241MHz, not 300MHz */
+ hisilicon,mipi-mode = "video";
+ hisilicon,mipi-lanes = <4>;
+ status = "ok";
+
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: timing0 {
+ clock-frequency = <13158>; /* 13.158MHz pixel clock */
+ hactive = <720>;
+ vactive = <1280>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <1>;
+ hfront-porch = <134>;
+ hback-porch = <53>;
+ hsync-len = <67>;
+ vfront-porch = <7>;
+ vback-porch = <12>;
+ vsync-len = <1>;
+ };
+ };
+ };
+ pwm0: pwm@fca05000 {
+ pinctrl-names = "default", "idle";
+ pinctrl-0 = <&pwm0_pmx_func &pwm0_cfg_func>;
+ pinctrl-1 = <&pwm0_pmx_idle &pwm0_cfg_func>;
+ status = "ok";
+ };
+ pwm1: pwm@fca06000 {
+ pinctrl-names = "default", "idle";
+ pinctrl-0 = <&pwm1_pmx_func &pwm1_cfg_func>;
+ pinctrl-1 = <&pwm1_pmx_idle &pwm1_cfg_func>;
+ status = "ok";
+ };
}; /* end of amba */
};
diff --git a/arch/arm/configs/hs_defconfig b/arch/arm/configs/hs_defconfig
index 94cdfd4807e..8ef04b8164f 100644
--- a/arch/arm/configs/hs_defconfig
+++ b/arch/arm/configs/hs_defconfig
@@ -1329,6 +1329,8 @@ CONFIG_REGULATOR_HI6421=y
# CONFIG_DRM is not set
# CONFIG_VGASTATE is not set
# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+CONFIG_OF_DISPLAY_TIMING=y
+CONFIG_OF_VIDEOMODE=y
CONFIG_FB=y
# CONFIG_FIRMWARE_EDID is not set
# CONFIG_FB_DDC is not set
@@ -1345,8 +1347,9 @@ CONFIG_FB_CFB_IMAGEBLIT=y
# CONFIG_FB_WMT_GE_ROPS is not set
# CONFIG_FB_SVGALIB is not set
# CONFIG_FB_MACMODES is not set
+CONFIG_FB_HI3620=y
# CONFIG_FB_BACKLIGHT is not set
-# CONFIG_FB_MODE_HELPERS is not set
+CONFIG_FB_MODE_HELPERS=y
# CONFIG_FB_TILEBLITTING is not set
#
@@ -1367,7 +1370,7 @@ CONFIG_FB_ARMCLCD=y
# Console display driver support
#
CONFIG_DUMMY_CONSOLE=y
-CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE is not set
# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
# CONFIG_FONTS is not set
@@ -1737,7 +1740,8 @@ CONFIG_OF_IOMMU=y
# CONFIG_EXTCON is not set
# CONFIG_MEMORY is not set
# CONFIG_IIO is not set
-# CONFIG_PWM is not set
+CONFIG_PWM=y
+CONFIG_PWM_HI3620=y
# CONFIG_IPACK_BUS is not set
#
diff --git a/arch/arm/mach-hs/hs-dt.c b/arch/arm/mach-hs/hs-dt.c
index e66a482e7f1..0ad473d82c6 100644
--- a/arch/arm/mach-hs/hs-dt.c
+++ b/arch/arm/mach-hs/hs-dt.c
@@ -12,6 +12,7 @@
#include <linux/clk.h>
#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
@@ -69,7 +70,7 @@ static void __init hs_timer_init(void)
void __iomem *base;
int irq;
- hs_init_clocks();
+ of_clk_init(NULL);
node = of_find_matching_node(NULL, hs_timer_match);
WARN_ON(!node);
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index a47e6ee98b8..a18280ad62f 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -65,4 +65,5 @@ config CLK_TWL6040
endmenu
+source "drivers/clk/hisilicon/Kconfig"
source "drivers/clk/mvebu/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 554ec53bc6e..4190b55fa45 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -1,13 +1,18 @@
# common clock types
obj-$(CONFIG_HAVE_CLK) += clk-devres.o
obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
-obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \
- clk-mux.o clk-divider.o clk-fixed-factor.o
+obj-$(CONFIG_COMMON_CLK) += clk.o
+obj-$(CONFIG_COMMON_CLK) += clk-divider.o
+obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o
+obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o
+obj-$(CONFIG_COMMON_CLK) += clk-gate.o
+obj-$(CONFIG_COMMON_CLK) += clk-mux.o
+
# SoCs specific
obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
-obj-$(CONFIG_ARCH_HS) += clk-hs.o
+obj-$(CONFIG_ARCH_HS) += hisilicon/
obj-$(CONFIG_ARCH_MXS) += mxs/
obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
obj-$(CONFIG_PLAT_SPEAR) += spear/
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index a9204c69148..68b40210117 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -16,6 +16,7 @@
#include <linux/io.h>
#include <linux/err.h>
#include <linux/string.h>
+#include <linux/log2.h>
/*
* DOC: basic adjustable divider clock that cannot gate
@@ -29,8 +30,7 @@
#define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
-#define div_mask(d) ((1 << (d->width)) - 1)
-#define is_power_of_two(i) !(i & ~i)
+#define div_mask(d) ((1 << ((d)->width)) - 1)
static unsigned int _get_table_maxdiv(const struct clk_div_table *table)
{
@@ -137,7 +137,7 @@ static bool _is_valid_table_div(const struct clk_div_table *table,
static bool _is_valid_div(struct clk_divider *divider, unsigned int div)
{
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
- return is_power_of_two(div);
+ return is_power_of_2(div);
if (divider->table)
return _is_valid_table_div(divider->table, div);
return true;
diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c
index af78ed6b67e..dc58fbd8516 100644
--- a/drivers/clk/clk-fixed-rate.c
+++ b/drivers/clk/clk-fixed-rate.c
@@ -85,7 +85,7 @@ struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
/**
* of_fixed_clk_setup() - Setup function for simple fixed rate clock
*/
-void __init of_fixed_clk_setup(struct device_node *node)
+void of_fixed_clk_setup(struct device_node *node)
{
struct clk *clk;
const char *clk_name = node->name;
@@ -101,4 +101,5 @@ void __init of_fixed_clk_setup(struct device_node *node)
of_clk_add_provider(node, of_clk_src_simple_get, clk);
}
EXPORT_SYMBOL_GPL(of_fixed_clk_setup);
+CLK_OF_DECLARE(fixed_clk, "fixed-clock", of_fixed_clk_setup);
#endif
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 251e45d6024..fabbfe1a925 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -18,6 +18,7 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/device.h>
+#include <linux/init.h>
static DEFINE_SPINLOCK(enable_lock);
static DEFINE_MUTEX(prepare_lock);
@@ -35,6 +36,137 @@ static struct dentry *rootdir;
static struct dentry *orphandir;
static int inited = 0;
+static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level)
+{
+ if (!c)
+ return;
+
+ seq_printf(s, "%*s%-*s %-11d %-12d %-10lu",
+ level * 3 + 1, "",
+ 30 - level * 3, c->name,
+ c->enable_count, c->prepare_count, c->rate);
+ seq_printf(s, "\n");
+}
+
+static void clk_summary_show_subtree(struct seq_file *s, struct clk *c,
+ int level)
+{
+ struct clk *child;
+ struct hlist_node *tmp;
+
+ if (!c)
+ return;
+
+ clk_summary_show_one(s, c, level);
+
+ hlist_for_each_entry(child, tmp, &c->children, child_node)
+ clk_summary_show_subtree(s, child, level + 1);
+}
+
+static int clk_summary_show(struct seq_file *s, void *data)
+{
+ struct clk *c;
+ struct hlist_node *tmp;
+
+ seq_printf(s, " clock enable_cnt prepare_cnt rate\n");
+ seq_printf(s, "---------------------------------------------------------------------\n");
+
+ mutex_lock(&prepare_lock);
+
+ hlist_for_each_entry(c, tmp, &clk_root_list, child_node)
+ clk_summary_show_subtree(s, c, 0);
+
+ hlist_for_each_entry(c, tmp, &clk_orphan_list, child_node)
+ clk_summary_show_subtree(s, c, 0);
+
+ mutex_unlock(&prepare_lock);
+
+ return 0;
+}
+
+
+static int clk_summary_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, clk_summary_show, inode->i_private);
+}
+
+static const struct file_operations clk_summary_fops = {
+ .open = clk_summary_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void clk_dump_one(struct seq_file *s, struct clk *c, int level)
+{
+ if (!c)
+ return;
+
+ seq_printf(s, "\"%s\": { ", c->name);
+ seq_printf(s, "\"enable_count\": %d,", c->enable_count);
+ seq_printf(s, "\"prepare_count\": %d,", c->prepare_count);
+ seq_printf(s, "\"rate\": %lu", c->rate);
+}
+
+static void clk_dump_subtree(struct seq_file *s, struct clk *c, int level)
+{
+ struct clk *child;
+ struct hlist_node *tmp;
+
+ if (!c)
+ return;
+
+ clk_dump_one(s, c, level);
+
+ hlist_for_each_entry(child, tmp, &c->children, child_node) {
+ seq_printf(s, ",");
+ clk_dump_subtree(s, child, level + 1);
+ }
+
+ seq_printf(s, "}");
+}
+
+static int clk_dump(struct seq_file *s, void *data)
+{
+ struct clk *c;
+ struct hlist_node *tmp;
+ bool first_node = true;
+
+ seq_printf(s, "{");
+
+ mutex_lock(&prepare_lock);
+
+ hlist_for_each_entry(c, tmp, &clk_root_list, child_node) {
+ if (!first_node)
+ seq_printf(s, ",");
+ first_node = false;
+ clk_dump_subtree(s, c, 0);
+ }
+
+ hlist_for_each_entry(c, tmp, &clk_orphan_list, child_node) {
+ seq_printf(s, ",");
+ clk_dump_subtree(s, c, 0);
+ }
+
+ mutex_unlock(&prepare_lock);
+
+ seq_printf(s, "}");
+ return 0;
+}
+
+
+static int clk_dump_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, clk_dump, inode->i_private);
+}
+
+static const struct file_operations clk_dump_fops = {
+ .open = clk_dump_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
/* caller must hold prepare_lock */
static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry)
{
@@ -168,12 +300,23 @@ static int __init clk_debug_init(void)
{
struct clk *clk;
struct hlist_node *tmp;
+ struct dentry *d;
rootdir = debugfs_create_dir("clk", NULL);
if (!rootdir)
return -ENOMEM;
+ d = debugfs_create_file("clk_summary", S_IRUGO, rootdir, NULL,
+ &clk_summary_fops);
+ if (!d)
+ return -ENOMEM;
+
+ d = debugfs_create_file("clk_dump", S_IRUGO, rootdir, NULL,
+ &clk_dump_fops);
+ if (!d)
+ return -ENOMEM;
+
orphandir = debugfs_create_dir("orphans", rootdir);
if (!orphandir)
@@ -259,32 +402,33 @@ late_initcall(clk_disable_unused);
/*** helper functions ***/
-inline const char *__clk_get_name(struct clk *clk)
+const char *__clk_get_name(struct clk *clk)
{
return !clk ? NULL : clk->name;
}
+EXPORT_SYMBOL_GPL(__clk_get_name);
-inline struct clk_hw *__clk_get_hw(struct clk *clk)
+struct clk_hw *__clk_get_hw(struct clk *clk)
{
return !clk ? NULL : clk->hw;
}
-inline u8 __clk_get_num_parents(struct clk *clk)
+u8 __clk_get_num_parents(struct clk *clk)
{
return !clk ? 0 : clk->num_parents;
}
-inline struct clk *__clk_get_parent(struct clk *clk)
+struct clk *__clk_get_parent(struct clk *clk)
{
return !clk ? NULL : clk->parent;
}
-inline unsigned int __clk_get_enable_count(struct clk *clk)
+unsigned int __clk_get_enable_count(struct clk *clk)
{
return !clk ? 0 : clk->enable_count;
}
-inline unsigned int __clk_get_prepare_count(struct clk *clk)
+unsigned int __clk_get_prepare_count(struct clk *clk)
{
return !clk ? 0 : clk->prepare_count;
}
@@ -310,7 +454,7 @@ out:
return ret;
}
-inline unsigned long __clk_get_flags(struct clk *clk)
+unsigned long __clk_get_flags(struct clk *clk)
{
return !clk ? 0 : clk->flags;
}
@@ -950,9 +1094,6 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
/* change the rates */
clk_change_rate(top);
- mutex_unlock(&prepare_lock);
-
- return 0;
out:
mutex_unlock(&prepare_lock);
@@ -1663,6 +1804,11 @@ struct of_clk_provider {
void *data;
};
+extern struct of_device_id __clk_of_table[];
+
+static const struct of_device_id __clk_of_table_sentinel
+ __used __section(__clk_of_table_end);
+
static LIST_HEAD(of_clk_providers);
static DEFINE_MUTEX(of_clk_lock);
@@ -1791,6 +1937,9 @@ void __init of_clk_init(const struct of_device_id *matches)
{
struct device_node *np;
+ if (!matches)
+ matches = __clk_of_table;
+
for_each_matching_node(np, matches) {
const struct of_device_id *match = of_match_node(matches, np);
of_clk_init_cb_t clk_init_cb = match->data;
diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig
new file mode 100644
index 00000000000..1f8b3d3a2ed
--- /dev/null
+++ b/drivers/clk/hisilicon/Kconfig
@@ -0,0 +1,7 @@
+config HI3xxx_CLK_CORE
+ bool "Core clock driver of Hi3xxx Soc"
+ default y if COMMON_CLK
+
+config HI3620_CLK_MIPI_DSI
+ bool "MIPI DSI clock driver of Hi3620 SoC"
+ default y if COMMON_CLK && FB_HI3620
diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile
new file mode 100644
index 00000000000..cd97fda6489
--- /dev/null
+++ b/drivers/clk/hisilicon/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_HI3xxx_CLK_CORE) += clk-hi3xxx.o
+obj-$(CONFIG_HI3620_CLK_MIPI_DSI) += clk-hi3620-dsi.o
diff --git a/drivers/clk/hisilicon/clk-hi3620-dsi.c b/drivers/clk/hisilicon/clk-hi3620-dsi.c
new file mode 100644
index 00000000000..c89e8ff1aa8
--- /dev/null
+++ b/drivers/clk/hisilicon/clk-hi3620-dsi.c
@@ -0,0 +1,364 @@
+/*
+ * Hisilicon Hi3620 MIPI DSI clock driver
+ *
+ * Copyright (c) 2012-2013 Hisilicon Limited.
+ * Copyright (c) 2012-2013 Linaro Limited.
+ *
+ * Author: Haojian Zhuang <haojian.zhuang@linaro.org>
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk-provider.h>
+#include <linux/clk-private.h>
+#include <linux/clkdev.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/platform_data/hi3620-dsi.h>
+
+#define DSI_CLKMGR_CFG 0x908
+#define DSI_PHY_RSTZ 0x954
+
+enum {
+ HI3620_EDC,
+};
+
+struct clk_phy {
+ struct clk_hw hw;
+ void __iomem *reg_base;
+ unsigned int mult;
+ unsigned int div;
+};
+
+struct clk_esc {
+ struct clk_hw hw;
+ void __iomem *reg_base;
+ unsigned int div;
+};
+
+struct hs_clk {
+ void __iomem *edc;
+ spinlock_t lock;
+};
+
+static void __iomem __init *hi3620_init_clocks(struct device_node *np);
+
+static struct hs_clk hi3620_clk;
+
+struct phy_mult {
+ int mult;
+ int cp_current;
+ int lpf_ctrl;
+};
+
+static unsigned long clk_phy_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_phy *phy = container_of(hw, struct clk_phy, hw);
+ unsigned long long int rate;
+
+ if (!phy->div)
+ return 0;
+ rate = (unsigned long long int)parent_rate * phy->mult;
+ do_div(rate, phy->div);
+ return (unsigned long)rate;
+}
+
+static long clk_phy_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_phy *phy = container_of(hw, struct clk_phy, hw);
+ struct clk *clk_parent;
+ unsigned long long int mult, new_rate;
+
+ clk_parent = __clk_get_parent(hw->clk);
+ *prate = __clk_get_rate(clk_parent);
+ mult = (unsigned long long int)rate * phy->div;
+ do_div(mult, *prate);
+ new_rate = *prate * mult;
+ do_div(new_rate, phy->div);
+ return (unsigned long)new_rate;
+}
+
+static int clk_phy_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_phy *phy = container_of(hw, struct clk_phy, hw);
+ void __iomem *base = phy->reg_base;
+ unsigned long long int mult;
+ unsigned char cp_current, lpf_ctrl;
+ int i;
+ struct phy_mult m[] = { {32, 0x6, 0x10}, {64, 0x6, 0x10},
+ {128, 0xc, 0x8}, {256, 0x4, 0x4},
+ {512, 0x0, 0x1}, {768, 0x1, 0x1},
+ {1000, 0x2, 0x1}, };
+ mult = (unsigned long long int)rate * phy->div;
+ do_div(mult, parent_rate);
+ phy->mult = (unsigned int)mult;
+
+ for (i = 0; i < ARRAY_SIZE(m); i++) {
+ if (phy->mult <= m[i].mult) {
+ cp_current = m[i].cp_current;
+ lpf_ctrl = m[i].lpf_ctrl;
+ break;
+ }
+ }
+ if (i >= ARRAY_SIZE(m))
+ return -EINVAL;
+ lpf_ctrl |= 0xc0; /* bypass CP & LPF default values */
+ /* write CP current */
+ hi3620_dsi_phy_write(base, 0x11, cp_current);
+ /* write LPF control */
+ hi3620_dsi_phy_write(base, 0x12, lpf_ctrl);
+ /* configure N and M factors effectively */
+ hi3620_dsi_phy_write(base, 0x19, 0x33);
+ /* write N divider */
+ hi3620_dsi_phy_write(base, 0x17, phy->div - 1);
+ /* write M multiplier 1 */
+ hi3620_dsi_phy_write(base, 0x18, (phy->mult - 1) & 0x1f);
+ /* write M multiplier 2 */
+ hi3620_dsi_phy_write(base, 0x18, ((phy->mult - 1) >> 5) | 0x80);
+ /* set PLL unlocking filter */
+ hi3620_dsi_phy_write(base, 0x16, 0xff);
+ return 0;
+}
+
+struct clk_ops clk_phy_ops = {
+ .round_rate = clk_phy_round_rate,
+ .set_rate = clk_phy_set_rate,
+ .recalc_rate = clk_phy_recalc_rate,
+};
+
+static struct clk *clk_phy_register(struct device *dev, const char *name,
+ const char *parent_name, void __iomem *reg,
+ unsigned long flags)
+{
+ struct clk_phy *phy;
+ struct clk_init_data init;
+ struct clk *clk;
+
+ phy = kzalloc(sizeof(*phy), GFP_KERNEL);
+ if (!phy) {
+ pr_err("%s: could not allocate dsi phy clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* struct clk_phy assignments */
+ phy->hw.init = &init;
+ phy->div = 13; /* since parent rate is always 26MHz */
+ phy->reg_base = reg;
+
+ init.name = name;
+ init.ops = &clk_phy_ops;
+ init.flags = flags | CLK_IS_BASIC;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ clk = clk_register(dev, &phy->hw);
+
+ if (IS_ERR(clk))
+ kfree(phy);
+
+ return clk;
+}
+
+void __init hi3620_phy_setup(struct device_node *np)
+{
+ struct clk *clk;
+ const char *clk_name, **parent_names;
+ void __iomem *reg_base;
+
+ reg_base = hi3620_init_clocks(np);
+ if (!reg_base)
+ return;
+ if (of_property_read_string(np, "clock-output-names", &clk_name))
+ return;
+ /* gate only has the fixed parent */
+ parent_names = kzalloc(sizeof(char *), GFP_KERNEL);
+ if (!parent_names)
+ return;
+ parent_names[0] = of_clk_get_parent_name(np, 0);
+
+ clk = clk_phy_register(NULL, clk_name, parent_names[0], reg_base, 0);
+ if (IS_ERR(clk))
+ goto err;
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ return;
+err:
+ kfree(parent_names);
+}
+
+static int find_best_esc_divider(unsigned long parent_rate)
+{
+ unsigned int max_rate = 20000000; /* 20MHz */
+ unsigned int target_rate = 10000000; /* 10MHz */
+ unsigned int div, out_rate;
+
+ div = parent_rate / target_rate;
+ for (; div > 0; div--) {
+ out_rate = parent_rate / div;
+ if (out_rate < max_rate)
+ break;
+ }
+ if (div <= 0)
+ return 0;
+ return div;
+}
+
+static unsigned long clk_esc_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_esc *esc = container_of(hw, struct clk_esc, hw);
+ unsigned int div;
+
+ if (!esc->div)
+ div = find_best_esc_divider(parent_rate);
+ else
+ div = esc->div;
+ if (!div)
+ return 0;
+ return parent_rate / div;
+}
+
+static long clk_esc_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk *clk_parent;
+ unsigned int div;
+
+ clk_parent = __clk_get_parent(hw->clk);
+ *prate = __clk_get_rate(clk_parent);
+
+ div = find_best_esc_divider(*prate);
+ return *prate / div;
+}
+
+static int clk_esc_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_esc *esc = container_of(hw, struct clk_esc, hw);
+ unsigned int data;
+
+ esc->div = parent_rate / rate;
+ data = readl_relaxed(esc->reg_base + DSI_CLKMGR_CFG);
+ data &= ~0xff;
+ writel_relaxed(data | esc->div, esc->reg_base + DSI_CLKMGR_CFG);
+ return 0;
+}
+
+struct clk_ops clk_esc_ops = {
+ .round_rate = clk_esc_round_rate,
+ .set_rate = clk_esc_set_rate,
+ .recalc_rate = clk_esc_recalc_rate,
+};
+
+static struct clk *clk_esc_register(struct device *dev, const char *name,
+ const char *parent_name, void __iomem *reg,
+ unsigned long flags)
+{
+ struct clk_esc *esc;
+ struct clk_init_data init;
+ struct clk *clk;
+
+ esc = kzalloc(sizeof(*esc), GFP_KERNEL);
+ if (!esc) {
+ pr_err("%s: could not allocate dsi esc clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* struct clk_esc assignments */
+ esc->hw.init = &init;
+ esc->reg_base = reg;
+
+ init.name = name;
+ init.ops = &clk_esc_ops;
+ init.flags = flags | CLK_IS_BASIC;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ clk = clk_register(dev, &esc->hw);
+
+ if (IS_ERR(clk))
+ kfree(esc);
+
+ return clk;
+}
+
+void __init hi3620_esc_setup(struct device_node *np)
+{
+ struct clk *clk;
+ const char *clk_name, **parent_names;
+ void __iomem *reg_base;
+
+ reg_base = hi3620_init_clocks(np);
+ if (!reg_base)
+ return;
+ if (of_property_read_string(np, "clock-output-names", &clk_name))
+ return;
+ /* gate only has the fixed parent */
+ parent_names = kzalloc(sizeof(char *), GFP_KERNEL);
+ if (!parent_names)
+ return;
+ parent_names[0] = of_clk_get_parent_name(np, 0);
+
+ clk = clk_esc_register(NULL, clk_name, parent_names[0], reg_base, 0);
+ if (IS_ERR(clk))
+ goto err;
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ return;
+err:
+ kfree(parent_names);
+}
+
+CLK_OF_DECLARE(hi3620_dsi_pll, "hisilicon,hi3620-phy", hi3620_phy_setup)
+CLK_OF_DECLARE(hi3620_dsi_esc, "hisilicon,hi3620-phy-esc", hi3620_esc_setup)
+
+static const struct of_device_id hi3620_of_match[] = {
+ { .compatible = "hisilicon,hi3620-fb", .data = (void *)HI3620_EDC, },
+};
+
+static void __iomem __init *hi3620_init_clocks(struct device_node *np)
+{
+ struct device_node *parent;
+ const struct of_device_id *match;
+ void __iomem *ret = NULL;
+
+ parent = of_get_parent(np);
+ if (!parent)
+ goto out;
+ match = of_match_node(hi3620_of_match, parent);
+ if (!match)
+ goto out;
+ switch ((unsigned int)match->data) {
+ case HI3620_EDC:
+ if (!hi3620_clk.edc) {
+ ret = of_iomap(parent, 0);
+ WARN_ON(!ret);
+ hi3620_clk.edc = ret;
+ } else {
+ ret = hi3620_clk.edc;
+ }
+ break;
+ }
+out:
+ return ret;
+}
diff --git a/drivers/clk/hisilicon/clk-hi3xxx.c b/drivers/clk/hisilicon/clk-hi3xxx.c
new file mode 100644
index 00000000000..9682517edfd
--- /dev/null
+++ b/drivers/clk/hisilicon/clk-hi3xxx.c
@@ -0,0 +1,695 @@
+/*
+ * Hisilicon clock driver
+ *
+ * Copyright (c) 2012-2013 Hisilicon Limited.
+ * Copyright (c) 2012-2013 Linaro Limited.
+ *
+ * Author: Haojian Zhuang <haojian.zhuang@linaro.org>
+ * Xin Li <li.xin@linaro.org>
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk-provider.h>
+#include <linux/clk-private.h>
+#include <linux/clkdev.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+
+#define HI3620_DISABLE_OFF 0x4
+#define HI3620_STATUS_OFF 0x8
+
+#define WIDTH_TO_MASK(width) ((1 << (width)) - 1)
+
+/*
+ * The reverse of DIV_ROUND_UP: The maximum number which
+ * divided by m is r
+ */
+#define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1)
+
+enum {
+ HS_PMCTRL,
+ HS_SYSCTRL,
+ HS_EDC,
+};
+
+struct hi3620_periclk {
+ struct clk_hw hw;
+ void __iomem *enable; /* enable register */
+ void __iomem *reset; /* reset register */
+ u32 ebits; /* bits in enable/disable register */
+ u32 rbits; /* bits in reset/unreset register */
+ spinlock_t *lock;
+};
+
+struct hi3620_muxclk {
+ struct clk_hw hw;
+ void __iomem *reg; /* mux register */
+ u8 shift;
+ u8 width;
+ u32 mbits; /* mask bits in mux register */
+ spinlock_t *lock;
+};
+
+struct hi3620_divclk {
+ struct clk_hw hw;
+ void __iomem *reg; /* divider register */
+ u8 shift;
+ u8 width;
+ u32 mbits; /* mask bits in divider register */
+ const struct clk_div_table *table;
+ spinlock_t *lock;
+};
+
+struct hs_clk {
+ void __iomem *pmctrl;
+ void __iomem *sctrl;
+ void __iomem *edc;
+ spinlock_t lock;
+};
+
+static void __iomem __init *hs_init_clocks(struct device_node *np);
+
+static struct hs_clk hs_clk;
+
+static int hi3620_clkgate_prepare(struct clk_hw *hw)
+{
+ struct hi3620_periclk *pclk;
+ unsigned long flags = 0;
+
+ pclk = container_of(hw, struct hi3620_periclk, hw);
+
+ if (pclk->lock)
+ spin_lock_irqsave(pclk->lock, flags);
+ if (pclk->reset) {
+ writel_relaxed(pclk->rbits, pclk->reset + HI3620_DISABLE_OFF);
+ readl_relaxed(pclk->reset + HI3620_STATUS_OFF);
+ }
+ if (pclk->lock)
+ spin_unlock_irqrestore(pclk->lock, flags);
+ return 0;
+}
+
+static int hi3620_clkgate_enable(struct clk_hw *hw)
+{
+ struct hi3620_periclk *pclk;
+ unsigned long flags = 0;
+
+ pclk = container_of(hw, struct hi3620_periclk, hw);
+ if (pclk->lock)
+ spin_lock_irqsave(pclk->lock, flags);
+ writel_relaxed(pclk->ebits, pclk->enable);
+ readl_relaxed(pclk->enable + HI3620_STATUS_OFF);
+ if (pclk->lock)
+ spin_unlock_irqrestore(pclk->lock, flags);
+ return 0;
+}
+
+static void hi3620_clkgate_disable(struct clk_hw *hw)
+{
+ struct hi3620_periclk *pclk;
+ unsigned long flags = 0;
+
+ pclk = container_of(hw, struct hi3620_periclk, hw);
+ if (pclk->lock)
+ spin_lock_irqsave(pclk->lock, flags);
+ writel_relaxed(pclk->ebits, pclk->enable + HI3620_DISABLE_OFF);
+ readl_relaxed(pclk->enable + HI3620_STATUS_OFF);
+ if (pclk->lock)
+ spin_unlock_irqrestore(pclk->lock, flags);
+}
+
+static struct clk_ops hi3620_clkgate_ops = {
+ .prepare = hi3620_clkgate_prepare,
+ .enable = hi3620_clkgate_enable,
+ .disable = hi3620_clkgate_disable,
+};
+
+static void __init hi3620_clkgate_setup(struct device_node *np)
+{
+ struct hi3620_periclk *pclk;
+ struct clk_init_data *init;
+ struct clk *clk;
+ const char *clk_name, *name, **parent_names;
+ void __iomem *reg_base;
+ u32 rdata[2], gdata[2];
+
+ reg_base = hs_init_clocks(np);
+ if (!reg_base)
+ return;
+
+ if (of_property_read_string(np, "clock-output-names", &clk_name))
+ return;
+ if (of_property_read_u32_array(np, "hisilicon,hi3620-clkgate",
+ &gdata[0], 2))
+ return;
+
+ /* gate only has the fixed parent */
+ parent_names = kzalloc(sizeof(char *), GFP_KERNEL);
+ if (!parent_names)
+ return;
+ parent_names[0] = of_clk_get_parent_name(np, 0);
+
+ pclk = kzalloc(sizeof(*pclk), GFP_KERNEL);
+ if (!pclk)
+ goto err_pclk;
+
+ init = kzalloc(sizeof(*init), GFP_KERNEL);
+ if (!init)
+ goto err_init;
+ init->name = kstrdup(clk_name, GFP_KERNEL);
+ init->ops = &hi3620_clkgate_ops;
+ init->flags = CLK_SET_RATE_PARENT;
+ init->parent_names = parent_names;
+ init->num_parents = 1;
+
+ if (of_property_read_u32_array(np, "hisilicon,hi3620-clkreset",
+ &rdata[0], 2)) {
+ pclk->reset = 0;
+ pclk->rbits = 0;
+ } else {
+ pclk->reset = reg_base + rdata[0];
+ pclk->rbits = rdata[1];
+ }
+ pclk->enable = reg_base + gdata[0];
+ pclk->ebits = gdata[1];
+ pclk->lock = &hs_clk.lock;
+ pclk->hw.init = init;
+
+ clk = clk_register(NULL, &pclk->hw);
+ if (IS_ERR(clk))
+ goto err_clk;
+ if (!of_property_read_string(np, "clock-names", &name))
+ clk_register_clkdev(clk, name, NULL);
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ return;
+err_clk:
+ kfree(init);
+err_init:
+ kfree(pclk);
+err_pclk:
+ kfree(parent_names);
+}
+
+static u8 hi3620_clk_get_parent(struct clk_hw *hw)
+{
+ struct hi3620_muxclk *mclk;
+ u32 data;
+ unsigned long flags = 0;
+
+ mclk = container_of(hw, struct hi3620_muxclk, hw);
+
+ if (mclk->lock)
+ spin_lock_irqsave(mclk->lock, flags);
+
+ data = readl_relaxed(mclk->reg) >> mclk->shift;
+ data &= WIDTH_TO_MASK(mclk->width);
+
+ if (mclk->lock)
+ spin_unlock_irqrestore(mclk->lock, flags);
+
+ if (data >= __clk_get_num_parents(hw->clk))
+ return -EINVAL;
+
+ return (u8)data;
+}
+
+static int hi3620_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct hi3620_muxclk *mclk;
+ u32 data;
+ unsigned long flags = 0;
+
+ mclk = container_of(hw, struct hi3620_muxclk, hw);
+
+ if (mclk->lock)
+ spin_lock_irqsave(mclk->lock, flags);
+
+ data = readl_relaxed(mclk->reg);
+ data &= ~(WIDTH_TO_MASK(mclk->width) << mclk->shift);
+ data |= index << mclk->shift;
+ writel_relaxed(data, mclk->reg);
+ /* set mask enable bits */
+ data |= mclk->mbits;
+ writel_relaxed(data, mclk->reg);
+
+ if (mclk->lock)
+ spin_unlock_irqrestore(mclk->lock, flags);
+
+ return 0;
+}
+
+static struct clk_ops hi3620_clkmux_ops = {
+ .get_parent = hi3620_clk_get_parent,
+ .set_parent = hi3620_clk_set_parent,
+};
+
+static void __init hi3620_clkmux_setup(struct device_node *np)
+{
+ struct hi3620_muxclk *mclk;
+ struct clk_init_data *init;
+ struct clk *clk;
+ const char *clk_name, **parent_names;
+ void __iomem *reg_base;
+ u32 rdata[2];
+ u8 num_parents;
+ int i;
+
+ reg_base = hs_init_clocks(np);
+ if (!reg_base)
+ return;
+
+ if (of_property_read_string(np, "clock-output-names", &clk_name))
+ return;
+ if (of_property_read_u32_array(np, "hisilicon,hi3620-clkmux",
+ &rdata[0], 2))
+ return;
+ /* get the count of items in mux */
+ for (i = 0; ; i++) {
+ /* parent's #clock-cells property is always 0 */
+ if (!of_parse_phandle(np, "clocks", i))
+ break;
+ }
+ parent_names = kzalloc(sizeof(char *) * i, GFP_KERNEL);
+ if (!parent_names)
+ return;
+
+ for (num_parents = i, i = 0; i < num_parents; i++)
+ parent_names[i] = of_clk_get_parent_name(np, i);
+
+ mclk = kzalloc(sizeof(*mclk), GFP_KERNEL);
+ if (!mclk)
+ goto err_mclk;
+ init = kzalloc(sizeof(*init), GFP_KERNEL);
+ if (!init)
+ goto err_init;
+ init->name = kstrdup(clk_name, GFP_KERNEL);
+ init->ops = &hi3620_clkmux_ops;
+ init->flags = CLK_SET_RATE_PARENT;
+ init->parent_names = parent_names;
+ init->num_parents = num_parents;
+
+ mclk->reg = reg_base + rdata[0];
+ /* enable_mask bits are in higher 16bits */
+ mclk->mbits = rdata[1] << 16;
+ mclk->shift = ffs(rdata[1]) - 1;
+ mclk->width = fls(rdata[1]) - ffs(rdata[1]) + 1;
+ mclk->lock = &hs_clk.lock;
+ mclk->hw.init = init;
+
+ clk = clk_register(NULL, &mclk->hw);
+ if (IS_ERR(clk))
+ goto err_clk;
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+
+ return;
+err_clk:
+ kfree(init);
+err_init:
+ kfree(mclk);
+err_mclk:
+ kfree(parent_names);
+}
+
+static void __init hs_clkgate_setup(struct device_node *np)
+{
+ struct clk *clk;
+ const char *clk_name, **parent_names, *name;
+ unsigned long flags = 0;
+ void __iomem *reg_base;
+ u32 data[2];
+
+ reg_base = hs_init_clocks(np);
+ if (!reg_base)
+ return;
+ if (of_property_read_string(np, "clock-output-names", &clk_name))
+ return;
+ if (of_property_read_u32_array(np, "hisilicon,clkgate",
+ &data[0], 2))
+ return;
+ if (of_property_read_bool(np, "hisilicon,clkgate-inverted"))
+ flags = CLK_GATE_SET_TO_DISABLE;
+ /* gate only has the fixed parent */
+ parent_names = kzalloc(sizeof(char *), GFP_KERNEL);
+ if (!parent_names)
+ return;
+ parent_names[0] = of_clk_get_parent_name(np, 0);
+
+ clk = clk_register_gate(NULL, clk_name, parent_names[0], 0,
+ reg_base + data[0], (u8)data[1], flags,
+ &hs_clk.lock);
+ if (IS_ERR(clk))
+ goto err;
+ if (!of_property_read_string(np, "clock-names", &name))
+ clk_register_clkdev(clk, name, NULL);
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ return;
+err:
+ kfree(parent_names);
+}
+
+void __init hs_fixed_factor_setup(struct device_node *np)
+{
+ struct clk *clk;
+ const char *clk_name, **parent_names;
+ u32 data[2];
+
+ if (of_property_read_string(np, "clock-output-names", &clk_name))
+ return;
+ if (of_property_read_u32_array(np, "hisilicon,fixed-factor",
+ data, 2))
+ return ;
+ /* gate only has the fixed parent */
+ parent_names = kzalloc(sizeof(char *), GFP_KERNEL);
+ if (!parent_names)
+ return;
+ parent_names[0] = of_clk_get_parent_name(np, 0);
+
+ clk = clk_register_fixed_factor(NULL, clk_name, parent_names[0],
+ CLK_SET_RATE_PARENT,
+ data[0], data[1]);
+ if (IS_ERR(clk))
+ goto err;
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ return;
+err:
+ kfree(parent_names);
+}
+
+static unsigned int hi3620_get_table_maxdiv(const struct clk_div_table *table)
+{
+ unsigned int maxdiv = 0;
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->div > maxdiv)
+ maxdiv = clkt->div;
+ return maxdiv;
+}
+
+static unsigned int hi3620_get_table_div(const struct clk_div_table *table,
+ unsigned int val)
+{
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->val == val)
+ return clkt->div;
+ return 0;
+}
+
+static unsigned int hi3620_get_table_val(const struct clk_div_table *table,
+ unsigned int div)
+{
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->div == div)
+ return clkt->val;
+ return 0;
+}
+
+static unsigned long hi3620_clkdiv_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct hi3620_divclk *dclk = container_of(hw, struct hi3620_divclk, hw);
+ unsigned int div, val;
+
+ val = readl_relaxed(dclk->reg) >> dclk->shift;
+ val &= WIDTH_TO_MASK(dclk->width);
+
+ div = hi3620_get_table_div(dclk->table, val);
+ if (!div) {
+ pr_warning("%s: Invalid divisor for clock %s\n", __func__,
+ __clk_get_name(hw->clk));
+ return parent_rate;
+ }
+
+ return parent_rate / div;
+}
+
+static bool hi3620_is_valid_table_div(const struct clk_div_table *table,
+ unsigned int div)
+{
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->div == div)
+ return true;
+ return false;
+}
+
+static int hi3620_clkdiv_bestdiv(struct clk_hw *hw, unsigned long rate,
+ unsigned long *best_parent_rate)
+{
+ struct hi3620_divclk *dclk = container_of(hw, struct hi3620_divclk, hw);
+ struct clk *clk_parent = __clk_get_parent(hw->clk);
+ int i, bestdiv = 0;
+ unsigned long parent_rate, best = 0, now, maxdiv;
+
+ maxdiv = hi3620_get_table_maxdiv(dclk->table);
+
+ if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
+ parent_rate = *best_parent_rate;
+ bestdiv = DIV_ROUND_UP(parent_rate, rate);
+ bestdiv = bestdiv == 0 ? 1 : bestdiv;
+ bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
+ return bestdiv;
+ }
+
+ /*
+ * The maximum divider we can use without overflowing
+ * unsigned long in rate * i below
+ */
+ maxdiv = min(ULONG_MAX / rate, maxdiv);
+
+ for (i = 1; i <= maxdiv; i++) {
+ if (!hi3620_is_valid_table_div(dclk->table, i))
+ continue;
+ parent_rate = __clk_round_rate(clk_parent,
+ MULT_ROUND_UP(rate, i));
+ now = parent_rate / i;
+ if (now <= rate && now > best) {
+ bestdiv = i;
+ best = now;
+ *best_parent_rate = parent_rate;
+ }
+ }
+
+ if (!bestdiv) {
+ bestdiv = hi3620_get_table_maxdiv(dclk->table);
+ *best_parent_rate = __clk_round_rate(clk_parent, 1);
+ }
+
+ return bestdiv;
+}
+
+static long hi3620_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ int div;
+
+ if (!rate)
+ rate = 1;
+ div = hi3620_clkdiv_bestdiv(hw, rate, prate);
+
+ return *prate / div;
+}
+
+static int hi3620_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct hi3620_divclk *dclk = container_of(hw, struct hi3620_divclk, hw);
+ unsigned int div, value;
+ unsigned long flags = 0;
+ u32 data;
+
+ div = parent_rate / rate;
+ value = hi3620_get_table_val(dclk->table, div);
+
+ if (value > WIDTH_TO_MASK(dclk->width))
+ value = WIDTH_TO_MASK(dclk->width);
+
+ if (dclk->lock)
+ spin_lock_irqsave(dclk->lock, flags);
+
+ data = readl_relaxed(dclk->reg);
+ data &= ~(WIDTH_TO_MASK(dclk->width) << dclk->shift);
+ data |= value << dclk->shift;
+ data |= dclk->mbits;
+ writel_relaxed(data, dclk->reg);
+
+ if (dclk->lock)
+ spin_unlock_irqrestore(dclk->lock, flags);
+
+ return 0;
+}
+
+static struct clk_ops hi3620_clkdiv_ops = {
+ .recalc_rate = hi3620_clkdiv_recalc_rate,
+ .round_rate = hi3620_clkdiv_round_rate,
+ .set_rate = hi3620_clkdiv_set_rate,
+};
+
+void __init hi3620_clkdiv_setup(struct device_node *np)
+{
+ struct clk *clk;
+ const char *clk_name, **parent_names;
+ struct clk_init_data *init;
+ struct clk_div_table *table;
+ struct hi3620_divclk *dclk;
+ void __iomem *reg_base;
+ unsigned int table_num;
+ int i;
+ u32 data[2];
+ const char *propname = "hisilicon,clkdiv-table";
+ const char *cellname = "#hisilicon,clkdiv-table-cells";
+ struct of_phandle_args div_table;
+
+ reg_base = hs_init_clocks(np);
+ if (!reg_base)
+ return;
+
+ if (of_property_read_string(np, "clock-output-names", &clk_name))
+ return;
+ if (of_property_read_u32_array(np, "hisilicon,clkdiv",
+ &data[0], 2))
+ return;
+
+ /*process the div_table*/
+ for (i = 0; ; i++) {
+ if (of_parse_phandle_with_args(np, propname, cellname,
+ i, &div_table))
+ break;
+ }
+
+ /*table ends with <0, 0>, so plus one to table_num*/
+ table_num = i + 1;
+
+ table = kzalloc(sizeof(struct clk_div_table) * table_num, GFP_KERNEL);
+ if (!table)
+ return ;
+
+ for (i = 0; ; i++) {
+ if (of_parse_phandle_with_args(np, propname, cellname,
+ i, &div_table))
+ break;
+
+ table[i].val = div_table.args[0];
+ table[i].div = div_table.args[1];
+ }
+
+ /* gate only has the fixed parent */
+ parent_names = kzalloc(sizeof(char *), GFP_KERNEL);
+ if (!parent_names)
+ goto err_par;
+ parent_names[0] = of_clk_get_parent_name(np, 0);
+
+ dclk = kzalloc(sizeof(*dclk), GFP_KERNEL);
+ if (!dclk)
+ goto err_dclk;
+ init = kzalloc(sizeof(*init), GFP_KERNEL);
+ if (!init)
+ goto err_init;
+ init->name = kstrdup(clk_name, GFP_KERNEL);
+ init->ops = &hi3620_clkdiv_ops;
+ init->parent_names = parent_names;
+ init->num_parents = 1;
+
+ dclk->reg = reg_base + data[0];
+ dclk->shift = ffs(data[1]) - 1;
+ dclk->width = fls(data[1]) - ffs(data[1]) + 1;
+ dclk->mbits = data[1] << 16;
+ dclk->lock = &hs_clk.lock;
+ dclk->hw.init = init;
+ dclk->table = table;
+ clk = clk_register(NULL, &dclk->hw);
+ if (IS_ERR(clk))
+ goto err_clk;
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ return;
+err_clk:
+ kfree(init);
+err_init:
+ kfree(dclk);
+err_dclk:
+ kfree(parent_names);
+err_par:
+ kfree(table);
+}
+
+CLK_OF_DECLARE(hi3620_mux, "hisilicon,hi3620-clk-mux", hi3620_clkmux_setup)
+CLK_OF_DECLARE(hi3620_gate, "hisilicon,hi3620-clk-gate", hi3620_clkgate_setup)
+CLK_OF_DECLARE(hi3620_div, "hisilicon,hi3620-clk-div", hi3620_clkdiv_setup)
+CLK_OF_DECLARE(hs_gate, "hisilicon,clk-gate", hs_clkgate_setup)
+CLK_OF_DECLARE(hs_fixed, "hisilicon,clk-fixed-factor", hs_fixed_factor_setup)
+
+static const struct of_device_id hs_of_match[] = {
+ { .compatible = "hisilicon,pmctrl", .data = (void *)HS_PMCTRL, },
+ { .compatible = "hisilicon,sysctrl", .data = (void *)HS_SYSCTRL, },
+ { .compatible = "hisilicon,hi3620-fb", .data = (void *)HS_EDC, },
+};
+
+static void __iomem __init *hs_init_clocks(struct device_node *np)
+{
+ struct device_node *parent;
+ const struct of_device_id *match;
+ void __iomem *ret = NULL;
+
+ parent = of_get_parent(np);
+ if (!parent)
+ goto out;
+ match = of_match_node(hs_of_match, parent);
+ if (!match)
+ goto out;
+ switch ((unsigned int)match->data) {
+ case HS_PMCTRL:
+ if (!hs_clk.pmctrl) {
+ ret = of_iomap(parent, 0);
+ WARN_ON(!ret);
+ hs_clk.pmctrl = ret;
+ } else {
+ ret = hs_clk.pmctrl;
+ }
+ break;
+ case HS_SYSCTRL:
+ if (!hs_clk.sctrl) {
+ ret = of_iomap(parent, 0);
+ WARN_ON(!ret);
+ hs_clk.sctrl = ret;
+ } else {
+ ret = hs_clk.sctrl;
+ }
+ break;
+ case HS_EDC:
+ if (!hs_clk.edc) {
+ ret = of_iomap(parent, 0);
+ WARN_ON(!ret);
+ hs_clk.edc = ret;
+ } else {
+ ret = hs_clk.edc;
+ }
+ break;
+ }
+out:
+ return ret;
+}
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index e513cd99817..8ffb7c56e18 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -46,6 +46,15 @@ config PWM_BFIN
To compile this driver as a module, choose M here: the module
will be called pwm-bfin.
+config PWM_HI3620
+ tristate "Hi3620 PWM support"
+ depends on ARCH_HS
+ help
+ Generic PWM framework driver for Hisilicon Hi3620 SoC.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-hi3620.
+
config PWM_IMX
tristate "i.MX pwm support"
depends on ARCH_MXC
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 62a2963cfe5..076bdeb3965 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_PWM) += core.o
obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o
obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o
+obj-$(CONFIG_PWM_HI3620) += pwm-hi3620.o
obj-$(CONFIG_PWM_IMX) += pwm-imx.o
obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o
obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o
diff --git a/drivers/pwm/pwm-hi3620.c b/drivers/pwm/pwm-hi3620.c
new file mode 100644
index 00000000000..1fbb6855cc0
--- /dev/null
+++ b/drivers/pwm/pwm-hi3620.c
@@ -0,0 +1,186 @@
+/*
+ * Hisilicon Hi3620 PWM driver
+ *
+ * Copyright (C) 2013 Hisilicon Ltd.
+ * Copyright (C) 2013 Linaro Ltd.
+ *
+ * Author: Haojian Zhuang <haojian.zhuang@linaro.org>
+ *
+ * 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.
+ */
+
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/pwm.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+
+#define REG_OUT_EN 0x00
+#define REG_OUT_DIV 0x08
+#define REG_OUT_WIDE 0x10
+#define REG_OUT_WARN 0x18
+
+#define DELTA_NS 5
+
+struct hi3620_pwm_info {
+ struct pwm_chip chip;
+ struct clk *clk;
+ void __iomem *mmio_base;
+ unsigned int clk_rate;
+ int ratio;
+};
+
+static inline struct hi3620_pwm_info *to_hi3620_pwm_info(struct pwm_chip *chip)
+{
+ return container_of(chip, struct hi3620_pwm_info, chip);
+}
+
+static int hi3620_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct hi3620_pwm_info *info = to_hi3620_pwm_info(chip);
+ int ratio, data;
+ unsigned long long int c;
+
+ if (!info->ratio) {
+ c = (unsigned long long int)period_ns * info->clk_rate;
+ do_div(c, NSEC_PER_SEC);
+ ratio = (int)c;
+ info->ratio = 1 << __fls(ratio);
+ writel_relaxed(info->ratio, info->mmio_base + REG_OUT_DIV);
+ readl_relaxed(info->mmio_base + REG_OUT_DIV);
+ }
+ c = (unsigned long long int)(duty_ns + DELTA_NS)* info->clk_rate;
+ do_div(c, NSEC_PER_SEC);
+ data = (int)c;
+ writel_relaxed(data, info->mmio_base + REG_OUT_WIDE);
+ readl_relaxed(info->mmio_base + REG_OUT_WIDE);
+ return 0;
+}
+
+static int hi3620_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct hi3620_pwm_info *info = to_hi3620_pwm_info(chip);
+
+ writel_relaxed(1, info->mmio_base + REG_OUT_EN);
+ readl_relaxed(info->mmio_base + REG_OUT_EN);
+ return 0;
+}
+
+static void hi3620_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct hi3620_pwm_info *info = to_hi3620_pwm_info(chip);
+
+ writel_relaxed(0, info->mmio_base + REG_OUT_EN);
+ readl_relaxed(info->mmio_base + REG_OUT_EN);
+}
+
+static const struct pwm_ops hi3620_pwm_ops = {
+ .config = hi3620_pwm_config,
+ .enable = hi3620_pwm_enable,
+ .disable = hi3620_pwm_disable,
+ .owner = THIS_MODULE,
+};
+
+static int hi3620_pwm_probe(struct platform_device *pdev)
+{
+ struct pinctrl *pinctrl;
+ struct resource *res;
+ struct hi3620_pwm_info *info;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no memory resource defined\n");
+ return -ENODEV;
+ }
+
+ pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+ if (IS_ERR(pinctrl)) {
+ dev_err(&pdev->dev, "unable to select pin group\n");
+ return PTR_ERR(pinctrl);
+ }
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+ info->mmio_base = devm_request_and_ioremap(&pdev->dev, res);
+ if (!info->mmio_base)
+ return -EADDRNOTAVAIL;
+
+ info->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(info->clk)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ return PTR_ERR(info->clk);
+ }
+ ret = clk_prepare_enable(info->clk);
+ if (ret)
+ return ret;
+ info->clk_rate = clk_get_rate(info->clk);
+ if (!info->clk_rate) {
+ dev_err(&pdev->dev, "failed to get clock rate\n");
+ return -EINVAL;
+ }
+ info->chip.dev = &pdev->dev;
+ info->chip.ops = &hi3620_pwm_ops;
+ info->chip.base = -1;
+ info->chip.npwm = 1;
+
+ ret = pwmchip_add(&info->chip);
+ if (ret)
+ goto err;
+
+ platform_set_drvdata(pdev, info);
+ return 0;
+err:
+ clk_disable_unprepare(info->clk);
+ return ret;
+}
+
+static int hi3620_pwm_remove(struct platform_device *pdev)
+{
+ struct hi3620_pwm_info *info;
+
+ info = platform_get_drvdata(pdev);
+ pwmchip_remove(&info->chip);
+ clk_disable_unprepare(info->clk);
+ return 0;
+}
+
+static struct of_device_id hi3620_pwm_of_match[] = {
+ { .compatible = "hisilicon,hi3620-pwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hi3620_pwm_of_match);
+
+static struct platform_driver hi3620_pwm_driver = {
+ .driver = {
+ .name = "hi3620-pwm",
+ .of_match_table = of_match_ptr(hi3620_pwm_of_match),
+ },
+ .probe = hi3620_pwm_probe,
+ .remove = hi3620_pwm_remove,
+};
+module_platform_driver(hi3620_pwm_driver);
+
+MODULE_LICENSE("GPLv2");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@linaro.org");
+MODULE_ALIAS("platform:hi3620-pwm");
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index e7068c50880..2343912499d 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -33,6 +33,27 @@ config VIDEO_OUTPUT_CONTROL
This framework adds support for low-level control of the video
output switch.
+config DISPLAY_TIMING
+ bool
+
+config VIDEOMODE
+ bool
+
+config OF_DISPLAY_TIMING
+ bool "Enable device tree display timing support"
+ depends on OF
+ select DISPLAY_TIMING
+ help
+ helper to parse display timings from the devicetree
+
+config OF_VIDEOMODE
+ bool "Enable device tree videomode support"
+ depends on OF
+ select VIDEOMODE
+ select OF_DISPLAY_TIMING
+ help
+ helper to get videomodes from the devicetree
+
menuconfig FB
tristate "Support for frame buffer devices"
---help---
@@ -2422,6 +2443,7 @@ config FB_PUV3_UNIGFX
source "drivers/video/omap/Kconfig"
source "drivers/video/omap2/Kconfig"
source "drivers/video/exynos/Kconfig"
+source "drivers/video/hisilicon/Kconfig"
source "drivers/video/backlight/Kconfig"
if VT
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 768a137a1ba..b322ebb7bc7 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -161,6 +161,7 @@ obj-$(CONFIG_FB_BFIN_7393) += bfin_adv7393fb.o
obj-$(CONFIG_FB_MX3) += mx3fb.o
obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o
obj-$(CONFIG_FB_MXS) += mxsfb.o
+obj-$(CONFIG_FB_HI3620) += hisilicon/
obj-$(CONFIG_FB_SSD1307) += ssd1307fb.o
# the test framebuffer is last
@@ -168,3 +169,7 @@ obj-$(CONFIG_FB_VIRTUAL) += vfb.o
#video output switch sysfs driver
obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o
+obj-$(CONFIG_DISPLAY_TIMING) += display_timing.o
+obj-$(CONFIG_OF_DISPLAY_TIMING) += of_display_timing.o
+obj-$(CONFIG_VIDEOMODE) += videomode.o
+obj-$(CONFIG_OF_VIDEOMODE) += of_videomode.o
diff --git a/drivers/video/display_timing.c b/drivers/video/display_timing.c
new file mode 100644
index 00000000000..5e1822cef57
--- /dev/null
+++ b/drivers/video/display_timing.c
@@ -0,0 +1,24 @@
+/*
+ * generic display timing functions
+ *
+ * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
+ *
+ * This file is released under the GPLv2
+ */
+
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <video/display_timing.h>
+
+void display_timings_release(struct display_timings *disp)
+{
+ if (disp->timings) {
+ unsigned int i;
+
+ for (i = 0; i < disp->num_timings; i++)
+ kfree(disp->timings[i]);
+ kfree(disp->timings);
+ }
+ kfree(disp);
+}
+EXPORT_SYMBOL_GPL(display_timings_release);
diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c
index cef65574db6..7e1fb813b06 100644
--- a/drivers/video/fbmon.c
+++ b/drivers/video/fbmon.c
@@ -31,6 +31,8 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <video/edid.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
#ifdef CONFIG_PPC_OF
#include <asm/prom.h>
#include <asm/pci-bridge.h>
@@ -1373,6 +1375,106 @@ int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_inf
kfree(timings);
return err;
}
+
+#if IS_ENABLED(CONFIG_VIDEOMODE)
+int fb_videomode_from_videomode(const struct videomode *vm,
+ struct fb_videomode *fbmode)
+{
+ unsigned int htotal, vtotal;
+
+ fbmode->xres = vm->hactive;
+ fbmode->left_margin = vm->hback_porch;
+ fbmode->right_margin = vm->hfront_porch;
+ fbmode->hsync_len = vm->hsync_len;
+
+ fbmode->yres = vm->vactive;
+ fbmode->upper_margin = vm->vback_porch;
+ fbmode->lower_margin = vm->vfront_porch;
+ fbmode->vsync_len = vm->vsync_len;
+
+ /* prevent division by zero in KHZ2PICOS macro */
+ fbmode->pixclock = vm->pixelclock ?
+ KHZ2PICOS(vm->pixelclock / 1000) : 0;
+
+ fbmode->sync = 0;
+ fbmode->vmode = 0;
+ fbmode->flag = 0;
+ if (vm->dmt_flags & VESA_DMT_HSYNC_HIGH)
+ fbmode->sync |= FB_SYNC_HOR_HIGH_ACT;
+ if (vm->dmt_flags & VESA_DMT_HSYNC_HIGH)
+ fbmode->sync |= FB_SYNC_VERT_HIGH_ACT;
+ if (vm->data_flags & DISPLAY_FLAGS_INTERLACED)
+ fbmode->vmode |= FB_VMODE_INTERLACED;
+ if (vm->data_flags & DISPLAY_FLAGS_DOUBLESCAN)
+ fbmode->vmode |= FB_VMODE_DOUBLE;
+ if (vm->data_flags & DISPLAY_FLAGS_DE_HIGH)
+ fbmode->flag |= FB_FLAG_DE_HIGH;
+ if (vm->data_flags & DISPLAY_FLAGS_DE_LOW)
+ fbmode->flag |= FB_FLAG_DE_LOW;
+ if (vm->data_flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
+ fbmode->flag |= FB_FLAG_PIXDATA_POSEDGE;
+ if (vm->data_flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
+ fbmode->flag |= FB_FLAG_PIXDATA_NEGEDGE;
+
+ htotal = vm->hactive + vm->hfront_porch + vm->hback_porch +
+ vm->hsync_len;
+ vtotal = vm->vactive + vm->vfront_porch + vm->vback_porch +
+ vm->vsync_len;
+ /* prevent division by zero */
+ if (htotal && vtotal) {
+ fbmode->refresh = vm->pixelclock / (htotal * vtotal);
+ /* a mode must have htotal and vtotal != 0 or it is invalid */
+ } else {
+ fbmode->refresh = 0;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fb_videomode_from_videomode);
+#endif
+
+#if IS_ENABLED(CONFIG_OF_VIDEOMODE)
+static inline void dump_fb_videomode(const struct fb_videomode *m)
+{
+ pr_debug("fb_videomode = %ux%u@%uHz (%ukHz) %u %u %u %u %u %u %u %u %u\n",
+ m->xres, m->yres, m->refresh, m->pixclock, m->left_margin,
+ m->right_margin, m->upper_margin, m->lower_margin,
+ m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag);
+}
+
+/**
+ * of_get_fb_videomode - get a fb_videomode from devicetree
+ * @np: device_node with the timing specification
+ * @fb: will be set to the return value
+ * @index: index into the list of display timings in devicetree
+ *
+ * DESCRIPTION:
+ * This function is expensive and should only be used, if only one mode is to be
+ * read from DT. To get multiple modes start with of_get_display_timings ond
+ * work with that instead.
+ */
+int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fb,
+ int index)
+{
+ struct videomode vm;
+ int ret;
+
+ ret = of_get_videomode(np, &vm, index);
+ if (ret)
+ return ret;
+
+ fb_videomode_from_videomode(&vm, fb);
+
+ pr_debug("%s: got %dx%d display mode from %s\n",
+ of_node_full_name(np), vm.hactive, vm.vactive, np->name);
+ dump_fb_videomode(fb);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_get_fb_videomode);
+#endif
+
#else
int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
{
diff --git a/drivers/video/hisilicon/Kconfig b/drivers/video/hisilicon/Kconfig
new file mode 100644
index 00000000000..a683760d218
--- /dev/null
+++ b/drivers/video/hisilicon/Kconfig
@@ -0,0 +1,9 @@
+config FB_HI3620
+ tristate "Hisilicon Hi3620 Frame Buffer support"
+ depends on FB && ARCH_HS
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ help
+ This is the frame buffer device driver for the Hisilicon Hi3620 LCD
+ controller.
diff --git a/drivers/video/hisilicon/Makefile b/drivers/video/hisilicon/Makefile
new file mode 100644
index 00000000000..1f4b79befe5
--- /dev/null
+++ b/drivers/video/hisilicon/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_FB_HI3620) += hi3620_fb.o
+obj-y += hi3620_dsi.o
diff --git a/drivers/video/hisilicon/hi3620_dsi.c b/drivers/video/hisilicon/hi3620_dsi.c
new file mode 100644
index 00000000000..af3dd68abd8
--- /dev/null
+++ b/drivers/video/hisilicon/hi3620_dsi.c
@@ -0,0 +1,353 @@
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/fb.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/platform_data/hi3620-dsi.h>
+#include "hi3620_fb.h"
+
+/* DSI PHY internal register */
+#define DSI_PHY_CP_CURRENT 0x11
+#define DSI_PHY_LPF_CTRL 0x12
+#define DSI_PHY_PLL_UNLOCK_FILTER 0x16
+#define DSI_PHY_N_PLL 0x17
+#define DSI_PHY_M_PLL 0x18
+#define DSI_PHY_FACTOR 0x19
+#define DSI_PHY_HS_FREQ_RANGE 0x44
+
+#define PHY_ADDR (1 << 16)
+
+#define DTYPE_GEN_WRITE_HEAD 0x03
+#define DTYPE_GEN_WRITE_PAYLOAD 0x29
+
+struct phy_timing {
+ int dsi2x; /* MHz */
+ int lp2hs;
+ int hs2lp;
+ int hsfreq;
+};
+
+static void hi3620_mipi_reset(void __iomem *reg_base)
+{
+ writel_relaxed(0, reg_base + DSI_PWR_UP);
+}
+
+static void hi3620_mipi_unreset(void __iomem *reg_base)
+{
+ writel_relaxed(1, reg_base + DSI_PWR_UP);
+}
+
+static void hi3620_mipi_set_highspeed(void __iomem *reg_base)
+{
+ unsigned int data;
+
+ /* enable high speed clock for PHY */
+ data = readl_relaxed(reg_base + DSI_PHY_IF_CTRL);
+ data |= (1 << 0);
+ writel_relaxed(data, reg_base + DSI_PHY_IF_CTRL);
+}
+
+static void hi3620_mipi_unset_highspeed(void __iomem *reg_base)
+{
+ unsigned int data;
+
+ /* disable high speed clock for PHY */
+ data = readl_relaxed(reg_base + DSI_PHY_IF_CTRL);
+ data &= ~(1 << 0);
+ writel_relaxed(data, reg_base + DSI_PHY_IF_CTRL);
+}
+
+/* Switch to CMD mode with Low Power mode */
+static void hi3620_mipi_switch_cmd_mode(void __iomem *reg_base)
+{
+ unsigned int data;
+
+ /* disable VIDEO mode */
+ data = readl_relaxed(reg_base + DSI_VID_MODE_CFG);
+ data &= ~(1 << 0);
+ writel_relaxed(data, reg_base + DSI_VID_MODE_CFG);
+ data = readl_relaxed(reg_base + DSI_CMD_MODE_CFG);
+ data |= ((1 << 13) - 1);
+ writel_relaxed(data, reg_base + DSI_CMD_MODE_CFG);
+}
+
+/* Switch to VIDEO mode */
+static void hi3620_mipi_switch_video_mode(void __iomem *reg_base)
+{
+ unsigned int data;
+
+ /* disable CMD mode */
+ data = readl_relaxed(reg_base + DSI_CMD_MODE_CFG);
+ data &= ~(1 << 0);
+ writel_relaxed(data, reg_base + DSI_CMD_MODE_CFG);
+ /* enable VIDEO mode */
+ data = readl_relaxed(reg_base + DSI_VID_MODE_CFG);
+ data |= (1 << 0);
+ writel_relaxed(data, reg_base + DSI_VID_MODE_CFG);
+}
+
+static void hi3620_mipi_enable_phy(void __iomem *reg_base)
+{
+ writel_relaxed(0x7, reg_base + DSI_PHY_RSTZ);
+}
+
+static void hi3620_mipi_disable_phy(void __iomem *reg_base)
+{
+ writel_relaxed(0, reg_base + DSI_PHY_RSTZ);
+}
+
+#if 0
+static void hi3620_mipi_setup_cmd_mode(void __iomem *reg_base)
+{
+ /* enable CMD halt & disable VIDEO halt & discret WMS */
+ writel_relaxed(1, reg_base + DSI_CMD_MOD_CTRL);
+ /* enable TE for CMD mode */
+ writel_relaxed(0x4001, reg_base + DSI_TE_CTRL);
+ /* send data only when VSync is detected */
+ writel_relaxed(0, reg_base + DSI_TE_HS_NUM);
+ /* HSync width */
+ writel_relaxed(0x1001, reg_base + DSI_TE_HS_WD);
+ /* VSync width > Hsync width */
+ writel_relaxed(0x8002, reg_base + DSI_TE_VS_WD);
+}
+
+static void hi3620_mipi_setup_video_mode(struct hi3620fb_info *info)
+{
+ struct fb_info *fb = info->fb;
+ struct fb_var_screeninfo *var = &fb->var;
+ unsigned int data;
+
+ /* enable low power mode & disable ACK */
+ data = 0x1f8;
+ /* burst with Sync pulses */
+ data |= 2 << 1;
+ writel_relaxed(data, info->reg_base + DSI_VID_MODE_CFG);
+ data = var->xres & 0x7ff;
+ writel_relaxed(data, info->reg_base + DSI_VID_PKT_CFG);
+
+ /* only enable BTA, Bus Turn-Around */
+ data = readl_relaxed(info->reg_base + DSI_PCKHDL_CFG);
+ data &= ~0x1f;
+ data |= 1 << 2;
+ writel_relaxed(data, info->reg_base + DSI_PCKHDL_CFG);
+}
+#endif
+
+void hi3620_mipi_dsi_set_video_packet_size(void __iomem *reg_base,
+ int null_pkt_size, int num_vid_pkt,
+ int vid_pkt_size)
+{
+ unsigned int data;
+ data = (null_pkt_size & 0x3ff) << 21; /* byte size */
+ /* number of video packets for Each Multiple Packets */
+ data |= (num_vid_pkt & 0x3ff) << 11;
+ /* pixels of each video packet */
+ data |= vid_pkt_size & 0x7ff;
+ writel_relaxed(data, reg_base + DSI_VID_PKT_CFG);
+}
+
+static void hi3620_mipi_setup_dpi(struct hi3620fb_info *info)
+{
+ unsigned int data;
+
+ /* set lane numbers */
+ /*
+ data = readl_relaxed(info->reg_base + DSI_PHY_IF_CFG) & ~0x3;
+ data |= (info->lane_cnt - 1) & 0x3;
+ writel_relaxed(data, info->reg_base + DSI_PHY_IF_CFG);
+ */
+ data = 0;
+ //data = readl_relaxed(info->reg_base + DSI_DPI_CFG);
+ /* set virtual channel ID as 0 */
+ data &= ~(3 << 0);
+ /* set color mode */
+ data &= ~(7 << 2);
+ data |= info->color_mode << 2;
+ /* always set color mode & shutdown high active */
+ writel_relaxed(data, info->reg_base + DSI_DPI_CFG);
+}
+
+static void hi3620_mipi_setup_phy(struct hi3620fb_info *info)
+{
+ struct clk *parent;
+ unsigned char hs2lp = 0, lp2hs = 0, hsfreq = 0;
+ unsigned int data;
+ int rate, rate_mhz, i;
+ struct phy_timing timing[] = {
+ {90, 24, 14, 0}, {100, 25, 14, 0x20},
+ {110, 25, 14, 0x40}, {125, 25, 14, 0x02},
+ {140, 25, 14, 0x22}, {150, 25, 14, 0x42},
+ {160, 25, 14, 0x04}, {180, 28, 16, 0x24},
+ {200, 32, 16, 0x44}, {210, 31, 16, 0x06},
+ {240, 35, 17, 0x26}, {250, 37, 18, 0x46},
+ {270, 37, 18, 0x08}, {300, 39, 19, 0x28},
+ {330, 44, 20, 0x08}, {360, 47, 21, 0x2a},
+ {400, 48, 21, 0x4a}, {450, 54, 23, 0x0c},
+ {500, 58, 25, 0x2c}, {550, 62, 26, 0x0e},
+ {600, 67, 28, 0x2e}, {650, 72, 30, 0x10},
+ {700, 76, 31, 0x30}, {750, 81, 32, 0x12},
+ {800, 86, 34, 0x32}, {850, 89, 35, 0x14},
+ {900, 95, 37, 0x34}, {950, 99, 38, 0x54},
+ {1000, 104, 40, 0x74}, };
+ parent = clk_get_parent(info->clk_dsi);
+ rate = clk_get_rate(parent);
+ rate_mhz = rate / 1000000;
+ for (i = 0; i < ARRAY_SIZE(timing); i++) {
+ if (rate_mhz <= timing[i].dsi2x) {
+ hs2lp = timing[i].hs2lp;
+ lp2hs = timing[i].lp2hs;
+ hsfreq = timing[i].hsfreq;
+ break;
+ }
+ }
+ /* setup PHY timing */
+ data = 4095; /* bta time */
+ data |= (lp2hs & 0xff) << 16;
+ data |= (hs2lp & 0xff) << 24;
+ writel_relaxed(data, info->reg_base + DSI_PHY_TMR_CFG);
+ /* set hsfreqrange */
+ hi3620_dsi_phy_write(info->reg_base, 0x44, hsfreq);
+ writel_relaxed(0x7, info->reg_base + DSI_PHY_RSTZ);
+}
+
+void hi3620_mipi_dsi_set_lane(void __iomem *reg_base, int id, int count)
+{
+ unsigned int data, cnt;
+
+ cnt = count - 1;
+ if (cnt < 0 || id > cnt)
+ return;
+ data = readl_relaxed(reg_base + DSI_PHY_IF_CFG) & ~0x3;
+ data |= cnt & 0x3;
+ writel_relaxed(data, reg_base + DSI_PHY_IF_CFG);
+ data = readl_relaxed(reg_base + DSI_DPI_CFG) & ~0x3;
+ data |= id & 0x3;
+ writel_relaxed(data, reg_base + DSI_DPI_CFG);
+}
+
+/*
+ * PHY_TST_CTRL0 & PHY_TST_CTRL1 registers are the interfaces of accessing
+ * PHY internal registers.
+ * PHY_TST_CTRL0 is used to produce clock, as I2C SCLK.
+ * PHY_TST_CTRL1 is used to store address or data, as I2C SDA.
+ */
+static void set_phy_testclk(void __iomem *reg_base, int level)
+{
+ unsigned int data;
+
+ if (level)
+ data = 0x2;
+ else
+ data = 0;
+ writel_relaxed(data, reg_base + DSI_PHY_TST_CTRL0);
+}
+
+/* write 8-bit data into 8-bit phy register */
+int hi3620_dsi_phy_write(void __iomem *reg_base, unsigned char addr,
+ unsigned char data)
+{
+ unsigned int value;
+
+ set_phy_testclk(reg_base, 0);
+ value = (unsigned int)addr | PHY_ADDR;
+ writel_relaxed(value, reg_base + DSI_PHY_TST_CTRL1);
+ set_phy_testclk(reg_base, 1);
+
+ set_phy_testclk(reg_base, 0);
+ value = (unsigned int)data;
+ writel_relaxed(value, reg_base + DSI_PHY_TST_CTRL1);
+ set_phy_testclk(reg_base, 1);
+ set_phy_testclk(reg_base, 0);
+ return 0;
+}
+EXPORT_SYMBOL(hi3620_dsi_phy_write);
+
+/* read 8-bit data from 8-bit phy register */
+unsigned char hi3620_dsi_phy_read(void __iomem *reg_base, unsigned char addr)
+{
+ unsigned int value;
+
+ set_phy_testclk(reg_base, 0);
+ value = (unsigned int)addr | PHY_ADDR;
+ writel_relaxed(value, reg_base + DSI_PHY_TST_CTRL1);
+ set_phy_testclk(reg_base, 1);
+ set_phy_testclk(reg_base, 0);
+ value = readl_relaxed(reg_base + DSI_PHY_TST_CTRL1);
+ return (unsigned char)(value >> 8);
+}
+
+static struct hi3620fb_info *hinfo = NULL;
+
+extern int sharp_display_on(void);
+
+int hi3620_mipi_enable(struct hi3620fb_info *info)
+{
+ if (!hinfo)
+ hinfo = info;
+ hi3620_mipi_enable_phy(info->reg_base);
+ hi3620_mipi_reset(info->reg_base);
+ //hi3620_mipi_switch_cmd_mode(info->reg_base); /* debug for keeping display on after boot */
+ //hi3620_mipi_unset_highspeed(info->reg_base); /* debug for keeping display on after boot */
+ hi3620_mipi_unreset(info->reg_base);
+
+ clk_prepare_enable(info->clk_dsi);
+ clk_prepare_enable(info->clk_lane);
+ clk_set_rate(info->clk_dsi, info->dsi_rate); /* huawei logo is shifted to right & color may be changed??? */
+
+ hi3620_mipi_setup_phy(info);
+
+ if (!strncmp(info->mipi_mode_name, "video", 5))
+ hi3620_mipi_switch_video_mode(info->reg_base);
+ else if (!strncmp(info->mipi_mode_name, "command", 7))
+ hi3620_mipi_switch_cmd_mode(info->reg_base);
+ else
+ return -EINVAL;
+ hi3620_mipi_set_highspeed(info->reg_base);
+ hi3620_mipi_setup_dpi(info); /* debug for keeping display on after boot */
+ return 0;
+}
+EXPORT_SYMBOL(hi3620_mipi_enable);
+
+int hi3620_mipi_disable(struct hi3620fb_info *info)
+{
+ hi3620_mipi_reset(info->reg_base);
+ hi3620_mipi_switch_cmd_mode(info->reg_base);
+ hi3620_mipi_unset_highspeed(info->reg_base);
+ hi3620_mipi_unreset(info->reg_base);
+ hi3620_mipi_disable_phy(info->reg_base);
+ return 0;
+}
+EXPORT_SYMBOL(hi3620_mipi_disable);
+
+int send_generic_packet(u8 *cmd, int len)
+{
+ unsigned int head = 0, data = 0;
+ int i;
+
+ if (len <= 0 || !hinfo || !cmd)
+ return -EINVAL;
+ switch (len) {
+ case 2:
+ head |= cmd[1] << 16;
+ /* fallthrough */
+ case 1:
+ head |= cmd[0] << 8;
+ head |= (len & 0x3) << 4;
+ head |= DTYPE_GEN_WRITE_HEAD;
+ break;
+ default:
+ head |= DTYPE_GEN_WRITE_PAYLOAD;
+ head |= (len & 0xffff) << 8;
+ for (i = 0; i < len; i += 4) {
+ data = 0;
+ data |= *(u32 *)(cmd + i);
+ writel_relaxed(data, hinfo->reg_base + DSI_GEN_PLD_DATA);
+ }
+ break;
+ }
+ /* write head to trigger a packet transfer */
+ writel_relaxed(data, hinfo->reg_base + DSI_GEN_HDR);
+ return len;
+}
+EXPORT_SYMBOL(send_generic_packet);
diff --git a/drivers/video/hisilicon/hi3620_fb.c b/drivers/video/hisilicon/hi3620_fb.c
new file mode 100644
index 00000000000..83cce202274
--- /dev/null
+++ b/drivers/video/hisilicon/hi3620_fb.c
@@ -0,0 +1,650 @@
+/*
+ * Framebuffer driver of Hisilicon Hi3620 SoC
+ *
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (c) 2013 Hisilicon Ltd.
+ *
+ * Author: Haojian Zhuang <haojian.zhuang@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/fb.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <video/of_display_timing.h>
+#include <video/display_timing.h>
+#include "hi3620_fb.h"
+
+static unsigned int hi3620fb_pseudo_palette[16] = {
+ 0, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL,
+ ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL,
+};
+
+static int match_fmt_555(struct fb_var_screeninfo *var)
+{
+ if (var->blue.offset == 0 && var->green.offset <= 5 &&
+ var->red.offset <= 10 && var->transp.offset <= 15 &&
+ var->blue.offset <= 5 && var->green.length <= 5 &&
+ var->red.length <= 5)
+ return 1;
+ return 0;
+}
+
+static int match_fmt_565(struct fb_var_screeninfo *var)
+{
+ if (var->blue.offset == 0 && var->green.offset <= 5 &&
+ var->red.offset <= 11 && var->transp.offset == 0 &&
+ var->blue.length <= 5 && var->green.length <= 6 &&
+ var->red.length <= 5 && var->transp.length == 0)
+ return 1;
+ return 0;
+}
+
+static int match_fmt_888(struct fb_var_screeninfo *var)
+{
+ if (var->blue.offset == 0 && var->green.offset <= 8 &&
+ var->red.offset <= 16 && var->transp.offset <= 24 &&
+ var->blue.length <= 8 && var->green.length <= 8 &&
+ var->red.length <= 8)
+ return 1;
+ return 0;
+}
+
+static int find_best_pix_fmt(struct fb_var_screeninfo *var)
+{
+ if (var->bits_per_pixel == 16) {
+ /* RGB565/RGBA5551/RGBX5551 */
+ if (match_fmt_555(var)) {
+ if (var->transp.length == 1)
+ return IMG_PIXEL_FORMAT_ARGB1555;
+ else if (var->transp.length == 0)
+ return IMG_PIXEL_FORMAT_RGB555;
+ } else if (match_fmt_565(var))
+ return IMG_PIXEL_FORMAT_RGB565;
+ } else if (var->bits_per_pixel == 32) {
+ if (match_fmt_888(var)) {
+ if (var->transp.length == 8)
+ return IMG_PIXEL_FORMAT_ARGB8888;
+ else if (var->transp.length == 0)
+ return IMG_PIXEL_FORMAT_RGB888;
+ }
+ }
+ return -EINVAL;
+}
+
+static void set_pix_fmt(struct hi3620fb_info *info, int pix_fmt)
+{
+ struct fb_info *fb = info->fb;
+ struct fb_var_screeninfo *var = &fb->var;
+
+ switch (pix_fmt) {
+ case IMG_PIXEL_FORMAT_RGB565:
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ var->bits_per_pixel = 16;
+ break;
+ case IMG_PIXEL_FORMAT_ARGB1555:
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->green.offset = 5;
+ var->green.length = 5;
+ var->red.offset = 10;
+ var->red.length = 5;
+ var->transp.offset = 15;
+ var->transp.length = 1;
+ var->bits_per_pixel = 16;
+ break;
+ case IMG_PIXEL_FORMAT_RGB555:
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->green.offset = 5;
+ var->green.length = 5;
+ var->red.offset = 10;
+ var->red.length = 5;
+ var->transp.offset = 15;
+ var->transp.length = 0;
+ var->bits_per_pixel = 16;
+ break;
+ case IMG_PIXEL_FORMAT_ARGB8888:
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->transp.offset = 24;
+ var->transp.length = 8;
+ var->bits_per_pixel = 32;
+ break;
+ case IMG_PIXEL_FORMAT_RGB888:
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->transp.offset = 24;
+ var->transp.length = 0;
+ var->bits_per_pixel = 32;
+ break;
+ default:
+ return;
+ }
+ info->pix_fmt = pix_fmt;
+}
+
+static int hi3620fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *fb)
+{
+ struct hi3620fb_info *info = fb->par;
+ int pix_fmt;
+
+ /*
+ * Determine which pixel format we're going to use.
+ */
+ pix_fmt = find_best_pix_fmt(var);
+ if (pix_fmt < 0)
+ return pix_fmt;
+ set_pix_fmt(info, pix_fmt);
+
+ /*
+ * Basic geometry sanity checks.
+ */
+ if (var->xoffset + var->xres > var->xres_virtual)
+ return -EINVAL;
+ if (var->yoffset + var->yres > var->yres_virtual)
+ return -EINVAL;
+ if (var->xres + var->right_margin +
+ var->hsync_len + var->left_margin > 2048)
+ return -EINVAL;
+ if (var->yres + var->lower_margin +
+ var->vsync_len + var->upper_margin > 2048)
+ return -EINVAL;
+
+ /*
+ * Check size of framebuffer.
+ */
+ if (var->xres_virtual * var->yres_virtual *
+ (var->bits_per_pixel >> 3) > fb->fix.smem_len)
+ return -EINVAL;
+ return 0;
+}
+
+/* It's used to make EDC configuration work. */
+static void update_edc(void __iomem *base)
+{
+ unsigned int data;
+
+ data = readl_relaxed(base + EDC_DISP_CTL);
+ writel_relaxed(data | EDC_CFG_OK, base + EDC_DISP_CTL);
+ data &= ~EDC_CFG_OK;
+ writel_relaxed(data, base + EDC_DISP_CTL);
+}
+
+static void set_panel_control(struct fb_info *fb)
+{
+ struct fb_videomode *fb_vm = fb->mode;
+ struct hi3620fb_info *info = fb->par;
+ void __iomem *base = info->reg_base;
+ u32 ldi, dpi;
+
+ ldi = readl_relaxed(base + LDI_PLR_CTRL) & ~LDI_POLARITY_MASK;
+ dpi = readl_relaxed(base + DSI_DPI_CFG) & ~DSI_DPI_POLARITY_MASK;
+ if (fb_vm->sync & FB_SYNC_HOR_HIGH_ACT) {
+ ldi &= ~LDI_HSYNC_POLARITY;
+ dpi &= ~DSI_DPI_HSYNC_POLARITY;
+ } else {
+ ldi |= LDI_HSYNC_POLARITY;
+ dpi |= DSI_DPI_HSYNC_POLARITY;
+ }
+ if (fb_vm->sync & FB_SYNC_VERT_HIGH_ACT) {
+ ldi &= ~LDI_VSYNC_POLARITY;
+ dpi &= ~DSI_DPI_VSYNC_POLARITY;
+ } else {
+ ldi |= LDI_VSYNC_POLARITY;
+ dpi |= DSI_DPI_VSYNC_POLARITY;
+ }
+ if (fb_vm->flag & FB_FLAG_DE_HIGH) {
+ ldi &= ~LDI_DATAEN_POLARITY;
+ dpi &= ~DSI_DPI_DATAEN_POLARITY;
+ }
+ if (fb_vm->flag & FB_FLAG_DE_LOW) {
+ ldi |= LDI_DATAEN_POLARITY;
+ dpi |= DSI_DPI_DATAEN_POLARITY;
+ }
+ if (fb_vm->flag & FB_FLAG_PIXDATA_POSEDGE)
+ ldi |= LDI_PIXELCLK_POLARITY;
+ if (fb_vm->flag & FB_FLAG_PIXDATA_NEGEDGE)
+ ldi &= ~LDI_PIXELCLK_POLARITY;
+ writel_relaxed(ldi, base + LDI_PLR_CTRL);
+ /* always set color mode & shutdown high active */
+ writel_relaxed(dpi, info->reg_base + DSI_DPI_CFG);
+}
+
+static void set_screen_dimensions(struct fb_info *fb)
+{
+ struct fb_var_screeninfo *var = &fb->var;
+ struct fb_videomode *fb_vm = fb->mode;
+ struct hi3620fb_info *info = fb->par;
+ void __iomem *base = info->reg_base;
+ unsigned long long int tmp;
+ u32 data, lane_rate, timing;
+
+ data = (var->left_margin & 0xfff) << 20;
+ data |= var->right_margin & 0xfff;
+ writel_relaxed(data, base + LDI_HRZ_CTRL0);
+ data = (var->hsync_len - 1) & 0xfff;
+ writel_relaxed(data, base + LDI_HRZ_CTRL1);
+ data = (var->upper_margin & 0xfff) << 20;
+ data |= var->lower_margin & 0xfff;
+ writel_relaxed(data, base + LDI_VRT_CTRL0);
+ data = (var->vsync_len - 1) & 0xfff;
+ writel_relaxed(data, base + LDI_VRT_CTRL1);
+
+ data = (var->xres - 1) & 0xfff;
+ data |= ((var->yres - 1) & 0xfff) << 20;
+ writel_relaxed(data, base + LDI_DSP_SIZE);
+
+ data = (fb->var.xres_virtual - 1) << 16 | (fb->var.yres_virtual - 1);
+ writel_relaxed(data, base + EDC_VIDEO_CHAN_SIZE);
+
+ /* setup line timing */
+ lane_rate = clk_get_rate(info->clk_lane);
+ data = fb_vm->hsync_len * lane_rate / fb_vm->pixclock;
+ timing = data & 0x1ff;
+ data = fb_vm->left_margin * lane_rate / fb_vm->pixclock;
+ timing |= (data & 0x1ff) << 9;
+ tmp = (unsigned long long int)(fb_vm->left_margin + fb_vm->xres +
+ fb_vm->right_margin + fb_vm->hsync_len);
+ tmp *= lane_rate;
+ do_div(tmp, fb_vm->pixclock);
+ data = (unsigned int)tmp;
+ timing |= data << 18;
+ writel_relaxed(timing, info->reg_base + DSI_TMR_LINE_CFG);
+
+ /* setup frame timing */
+ timing = fb_vm->vsync_len & 0xf;
+ timing |= (fb_vm->upper_margin & 0x3f) << 4;
+ timing |= (fb_vm->lower_margin & 0x3f) << 10;
+ timing |= (fb_vm->yres & 0x7ff) << 16;
+ writel_relaxed(timing, info->reg_base + DSI_VTIMING_CFG);
+}
+
+static void set_graphics_start(struct fb_info *fb, int xoffset, int yoffset)
+{
+ struct hi3620fb_info *info = fb->par;
+ void __iomem *base = info->reg_base;
+ u32 data;
+
+ data = yoffset & 0xfff;
+ data |= (xoffset & 0xfff) << 16;
+ writel_relaxed(data, base + EDC_VIDEO_CHAN_XY);
+ /* setup dma address */
+ writel_relaxed(fb->fix.smem_start, base + EDC_VIDEO_CHAN_ADDR);
+}
+
+static void set_dma_control(struct fb_info *fb)
+{
+ struct hi3620fb_info *info = fb->par;
+ void __iomem *base = info->reg_base;
+
+ /* setup dma stride */
+ writel_relaxed(fb->fix.line_length, base + EDC_VIDEO_CHAN_STRIDE);
+}
+
+static int hi3620fb_set_par(struct fb_info *fb)
+{
+ struct fb_var_screeninfo *var = &fb->var;
+ struct hi3620fb_info *info = fb->par;
+ void __iomem *base = info->reg_base;
+ unsigned int ctrl = 0;
+
+ fb->fix.ypanstep = var->yres;
+
+ ctrl = readl_relaxed(base + EDC_VIDEO_CHAN_CTRL);
+ if (var->blue.offset)
+ ctrl |= EDC_CHAN_CTRL_BGR; /* BGR format */
+ ctrl &= ~(7 << 16);
+ switch (info->pix_fmt) {
+ case IMG_PIXEL_FORMAT_ARGB1555:
+ case IMG_PIXEL_FORMAT_RGB555:
+ break;
+ case IMG_PIXEL_FORMAT_RGB565:
+ ctrl |= 1 << 16;
+ break;
+ case IMG_PIXEL_FORMAT_RGB888:
+ ctrl |= 2 << 16;
+ break;
+ case IMG_PIXEL_FORMAT_ARGB8888:
+ ctrl |= 3 << 16;
+ break;
+ }
+ ctrl |= EDC_VIDEO_CHAN_CTRL_EN;
+ /* color key & rotate is always disabled, linear format */
+// ctrl |= 1 << 24; /* enable channel */
+// ctrl &= ~0xfff;
+// ctrl |= 0xa;
+// ctrl |= var->yres - 1; /* debug. add for interrupt */
+ writel_relaxed(ctrl, base + EDC_VIDEO_CHAN_CTRL);
+
+ set_panel_control(fb);
+ set_screen_dimensions(fb);
+ set_dma_control(fb);
+ update_edc(base);
+ return 0;
+}
+
+static int hi3620fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *fb)
+{
+ return 0;
+}
+
+static struct fb_ops hi3620fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = hi3620fb_check_var,
+ .fb_set_par = hi3620fb_set_par,
+ .fb_pan_display = hi3620fb_pan_display,
+};
+
+static int hi3620_parse_dt(struct device_node *np, struct hi3620fb_info *info)
+{
+ const char *name;
+ int ret;
+
+ ret = of_property_read_u32(np, "hisilicon,dsi-clock-frequency",
+ &info->dsi_rate);
+ if (ret)
+ return ret;
+ ret = of_property_read_string(np, "hisilicon,mipi-mode", &name);
+ if (ret < 0)
+ return ret;
+ info->mipi_mode_name = kstrdup(name, GFP_KERNEL);
+ ret = of_property_read_u32(np, "hisilicon,mipi-lanes", &info->lane_cnt);
+ if (ret < 0)
+ return ret;
+ ret = of_property_read_u32(np, "hisilicon,color-mode", &info->color_mode);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int hi3620_init_mode(struct device_node *np, struct fb_info *fb)
+{
+ struct fb_fix_screeninfo *fix = &fb->fix;
+ struct fb_var_screeninfo *var = &fb->var;
+ struct display_timings *disp;
+ struct fb_videomode *fb_vm;
+ struct hi3620fb_info *info = fb->par;
+ const char *pix_name;
+ int ret = 0, pix_fmt, i, length;
+
+ fb_vm = kzalloc(sizeof(*fb_vm), GFP_KERNEL);
+ if (!fb_vm)
+ return -ENOMEM;
+ fb->mode = fb_vm;
+ disp = of_get_display_timings(np);
+ if (!disp)
+ return -ENOENT;
+ /* How to handle multiple display timings ???,
+ * add_videomode is implemented by register_framebuffer() */
+ for (i = 0; i < disp->num_timings; i++) {
+ ret = of_get_fb_videomode(np, fb_vm, i);
+ if (ret)
+ goto out;
+ ret = of_property_read_string(np, "hisilicon,pixel-format",
+ &pix_name);
+ if (ret)
+ goto out;
+ if (!strncmp(pix_name, "RGBA8888", 8))
+ pix_fmt = IMG_PIXEL_FORMAT_ARGB8888;
+ else if (!strncmp(pix_name, "RGBX8888", 8))
+ pix_fmt = IMG_PIXEL_FORMAT_RGB888;
+ else if (!strncmp(pix_name, "RGBA5551", 8))
+ pix_fmt = IMG_PIXEL_FORMAT_ARGB1555;
+ else if (!strncmp(pix_name, "RGBX5551", 8))
+ pix_fmt = IMG_PIXEL_FORMAT_RGB555;
+ else if (!strncmp(pix_name, "RGB565", 6))
+ pix_fmt = IMG_PIXEL_FORMAT_RGB565;
+ else {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ set_pix_fmt(info, pix_fmt);
+ fb_videomode_to_var(var, fb_vm);
+ var->xres_virtual = fb_vm->xres;
+ var->yres_virtual = fb_vm->yres;
+ var->grayscale = 0;
+ var->accel_flags = FB_ACCEL_NONE;
+ /* Now assume that video mode is only 1 in DTS. */
+ //fb_add_videomode(&vm, &fb->modelist);
+ }
+ of_display_timings_exist(np);
+
+ fix->type_aux = 0;
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+ fix->ywrapstep = 0;
+ fix->mmio_start = 0; /* No MMIO address */
+ fix->mmio_len = 0; /* No MMIO address */
+ fix->accel = FB_ACCEL_NONE; /* No hardware accelerator */
+
+ length = var->xres_virtual * var->bits_per_pixel / 8;
+ fb->fix.line_length = ALIGN(length, 64);
+ fb->fix.smem_len = ALIGN(fb->fix.line_length * fb->var.yres_virtual,
+ PAGE_SIZE);
+ hi3620_parse_dt(np, info);
+out:
+ return ret;
+}
+
+static irqreturn_t edc_irq_handler(int irq, void *data)
+{
+ struct hi3620fb_info *info = data;
+
+ pr_err("#%s, %d, ints:0x%x, inte:0x%x\n",
+ __func__, __LINE__, readl_relaxed(info->reg_base + EDC_INTS),
+ readl_relaxed(info->reg_base + EDC_INTE));
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ldi_irq_handler(int irq, void *data)
+{
+ struct hi3620fb_info *info = data;
+ u32 value;
+
+ value = readl_relaxed(info->reg_base + LDI_ORG_INT);
+ writel_relaxed(value, info->reg_base + LDI_INT_CLR);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t dsi_irq_handler(int irq, void *data)
+{
+ return IRQ_HANDLED;
+}
+
+static int hi3620_fb_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct hi3620fb_info *info;
+ struct resource *res;
+ struct fb_info *fb;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no memory resource defined\n");
+ return -ENODEV;
+ }
+
+ fb = framebuffer_alloc(sizeof(*info), dev);
+ if (!fb) {
+ dev_err(dev, "failed to allocate framebuffer\n");
+ return -ENOMEM;
+ }
+ fb->dev = &pdev->dev;
+ info = fb->par;
+ info->fb = fb;
+ info->dev = &pdev->dev;
+ info->irq_edc = platform_get_irq_byname(pdev, "edc");
+ if (info->irq_edc < 0) {
+ ret = -ENOENT;
+ goto err_fb;
+ }
+ info->irq_ldi = platform_get_irq_byname(pdev, "ldi");
+ if (info->irq_ldi < 0) {
+ ret = -ENOENT;
+ goto err_fb;
+ }
+ info->irq_dsi = platform_get_irq_byname(pdev, "dsi");
+ if (info->irq_dsi < 0) {
+ ret = -ENOENT;
+ goto err_fb;
+ }
+
+ info->reg_base = devm_request_and_ioremap(&pdev->dev, res);
+ if (!info->reg_base) {
+ ret = -EADDRNOTAVAIL;
+ goto err_fb;
+ }
+ info->vedc = devm_regulator_get(dev, "vedc");
+ if (IS_ERR_OR_NULL(info->vedc)) {
+ if (IS_ERR(info->vedc)) {
+ dev_err(dev, "failed to get vedc regulator\n");
+ info->vedc = NULL;
+ }
+ }
+ info->clk_ldi = of_clk_get_by_name(np, "ldi");
+ if (IS_ERR(info->clk_ldi)) {
+ dev_err(dev, "failed to get ldi clock\n");
+ ret = PTR_ERR(info->clk_ldi);
+ goto err_fb;
+ }
+ info->clk_edc = of_clk_get_by_name(np, "edc");
+ if (IS_ERR(info->clk_edc)) {
+ dev_err(dev, "failed to get edc clock\n");
+ ret = PTR_ERR(info->clk_edc);
+ goto err_fb;
+ }
+ info->clk_dsi = of_clk_get_by_name(np, "dsi");
+ if (IS_ERR(info->clk_dsi)) {
+ dev_err(dev, "failed to get dsi clock\n");
+ ret = PTR_ERR(info->clk_dsi);
+ goto err_clk;
+ }
+ info->clk_lane = of_clk_get_by_name(np, "lane");
+ if (IS_ERR(info->clk_lane)) {
+ dev_err(dev, "failed to get lane clock\n");
+ ret = PTR_ERR(info->clk_lane);
+ goto err_clk;
+ }
+ if (info->vedc)
+ regulator_enable(info->vedc);
+ clk_prepare_enable(info->clk_ldi);
+ //clk_prepare_enable(info->clk_edc); /* debug for keep display on after boot */
+
+ fb->fbops = &hi3620fb_ops;
+ fb->pseudo_palette = &hi3620fb_pseudo_palette;
+
+ ret = hi3620_init_mode(np, fb);
+ if (ret)
+ goto err_clk;
+
+#if 1
+ fb->screen_base = dma_alloc_coherent(fb->dev, fb->fix.smem_len,
+ &info->fb_start_dma,
+ GFP_KERNEL);
+ fb->screen_size = fb->fix.smem_len;
+ fb->fix.smem_start = info->fb_start_dma;
+#else
+ /* debug for remapping the display region in bootloader */
+ info->fb_start_dma = PAGE_ALIGN(0x35b00130 + 0x40000000);
+ fb->fix.smem_start = info->fb_start_dma;
+ fb->screen_size = fb->fix.smem_len;
+ fb->screen_base = __va(info->fb_start_dma);
+#endif
+ hi3620_mipi_enable(info);
+ set_graphics_start(fb, 0, 0);
+ hi3620fb_set_par(fb);
+
+
+ /* clear IRQ status & enable IRQ */
+ writel_relaxed(0, info->reg_base + EDC_INTS);
+ writel_relaxed(0x2c8, info->reg_base + EDC_INTE);
+ writel_relaxed(0x4, info->reg_base + LDI_INT_EN); /* disable front porch int for debugging */
+ writel_relaxed(0x3fff, info->reg_base + LDI_INT_CLR);
+
+ ret = devm_request_irq(dev, info->irq_edc, edc_irq_handler,
+ IRQF_DISABLED, "edc", info);
+ if (ret < 0) {
+ dev_err(dev, "failed to request edc irq\n");
+ goto err_clk;
+ }
+ ret = devm_request_irq(dev, info->irq_ldi, ldi_irq_handler,
+ IRQF_DISABLED, "ldi", info);
+ if (ret < 0) {
+ dev_err(dev, "failed to request ldi irq\n");
+ goto err_clk;
+ }
+ ret = devm_request_irq(dev, info->irq_dsi, dsi_irq_handler,
+ IRQF_DISABLED, "dsi", info);
+ if (ret < 0) {
+ dev_err(dev, "failed to request dsi irq\n");
+ goto err_clk;
+ }
+ ret = register_framebuffer(fb);
+ if (ret < 0) {
+ dev_err(dev, "failed to register hi3620 framebuffer\n");
+ goto err_clk;
+ }
+ platform_set_drvdata(pdev, info);
+ /* clock rate of ldi */
+ return 0;
+err_clk:
+ clk_disable_unprepare(info->clk_edc);
+ clk_disable_unprepare(info->clk_ldi);
+err_fb:
+ framebuffer_release(fb);
+ return ret;
+}
+
+static int hi3620_fb_remove(struct platform_device *pdev)
+{
+ struct hi3620fb_info *info = platform_get_drvdata(pdev);
+ hi3620_mipi_disable(info);
+ return 0;
+}
+
+static const struct of_device_id hi3620_fb_of_match[] = {
+ { .compatible = "hisilicon,hi3620-fb", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hi3620_fb_of_match);
+
+static struct platform_driver hi3620_fb_driver = {
+ .probe = hi3620_fb_probe,
+ .remove = hi3620_fb_remove,
+ .driver = {
+ .name = "hi3620-fb",
+ .owner = THIS_MODULE,
+ .of_match_table = hi3620_fb_of_match,
+ },
+};
+module_platform_driver(hi3620_fb_driver);
diff --git a/drivers/video/hisilicon/hi3620_fb.h b/drivers/video/hisilicon/hi3620_fb.h
new file mode 100644
index 00000000000..676ff8e2e8f
--- /dev/null
+++ b/drivers/video/hisilicon/hi3620_fb.h
@@ -0,0 +1,125 @@
+#ifndef __HI3620FB_H
+#define __HI3620FB_H
+
+#define EDC_ID 0x000
+
+/* Channel 1 */
+#define EDC_GRAPH_CHAN_ADDR 0x004
+#define EDC_GRAPH_CHAN_STRIDE 0x00c
+#define EDC_GRAPH_CHAN_XY 0x010
+#define EDC_GRAPH_CHAN_SIZE 0x014
+#define EDC_GRAPH_CHAN_CTRL 0x018
+#define EDC_GRAPH_CHAN_CKEY_MIN 0x01c
+#define EDC_GRAPH_CHAN_CKEY_MAX 0x020
+
+/* Channel 2 */
+#define EDC_VIDEO_CHAN_ADDR 0x024
+#define EDC_VIDEO_CHAN_STRIDE 0x02c
+#define EDC_VIDEO_CHAN_XY 0x030
+#define EDC_VIDEO_CHAN_SIZE 0x034
+#define EDC_VIDEO_CHAN_CTRL 0x038
+#define EDC_VIDEO_CHAN_CKEY_MIN 0x03c
+#define EDC_VIDEO_CHAN_CKEY_MAX 0x040
+
+#define EDC_GRAPH_CHAN_CTRL_EN (1 << 24)
+#define EDC_VIDEO_CHAN_CTRL_EN (1 << 22)
+#define EDC_CHAN_CTRL_BGR (1 << 19)
+
+#define EDC_DISP_CTL 0x094
+#define EDC_DISP_DPD 0x098
+#define EDC_STS 0x09c
+#define EDC_INTS 0x0a0
+#define EDC_INTE 0x0a4
+
+#define LDI_HRZ_CTRL0 0x800
+#define LDI_HRZ_CTRL1 0x804
+#define LDI_VRT_CTRL0 0x808
+#define LDI_VRT_CTRL1 0x80c
+#define LDI_PLR_CTRL 0x810
+#define LDI_DSP_SIZE 0x814
+#define LDI_INT_EN 0x81c
+#define LDI_CTRL 0x820
+#define LDI_ORG_INT 0x824
+#define LDI_MSK_INT 0x828
+#define LDI_INT_CLR 0x82c
+#define LDI_WORK_MODE 0x830
+#define LDI_HDMI_DSI_GT 0x834
+
+#define DSI_CMD_MOD_CTRL 0x83c
+#define DSI_TE_CTRL 0x840
+#define DSI_TE_HS_NUM 0x844
+#define DSI_TE_HS_WD 0x848
+#define DSI_TE_VS_WD 0x84c
+
+#define DSI_PWR_UP 0x904
+#define DSI_CLKMGR_CFG 0x908
+#define DSI_DPI_CFG 0x90c
+#define DSI_PCKHDL_CFG 0x918
+#define DSI_VID_MODE_CFG 0x91c
+#define DSI_VID_PKT_CFG 0x920
+#define DSI_CMD_MODE_CFG 0x924
+#define DSI_TMR_LINE_CFG 0x928
+#define DSI_VTIMING_CFG 0x92c
+#define DSI_PHY_TMR_CFG 0x930
+#define DSI_GEN_HDR 0x934
+#define DSI_GEN_PLD_DATA 0x938
+#define DSI_ERROR_ST0 0x944
+#define DSI_ERROR_ST1 0x948
+#define DSI_ERROR_MSK0 0x94c
+#define DSI_ERROR_MSK1 0x950
+#define DSI_PHY_RSTZ 0x954
+#define DSI_PHY_IF_CFG 0x958
+#define DSI_PHY_IF_CTRL 0x95c
+#define DSI_PHY_TST_CTRL0 0x964
+#define DSI_PHY_TST_CTRL1 0x968
+
+/* bits field of EDC_DISP_CTL register */
+#define EDC_CFG_OK (1 << 1)
+
+/* bits field of LDI_PLT_CTRL register */
+#define LDI_POLARITY_MASK 0xf
+#define LDI_DATAEN_POLARITY (1 << 3)
+#define LDI_PIXELCLK_POLARITY (1 << 2)
+#define LDI_HSYNC_POLARITY (1 << 1)
+#define LDI_VSYNC_POLARITY (1 << 0)
+
+/* bits field of DSI_DPI_CFG register */
+#define DSI_DPI_POLARITY_MASK (0x1f << 5)
+#define DSI_DPI_COLORM_POLARITY (1 << 9)
+#define DSI_DPI_SHUTD_POLARITY (1 << 8)
+#define DSI_DPI_HSYNC_POLARITY (1 << 7)
+#define DSI_DPI_VSYNC_POLARITY (1 << 6)
+#define DSI_DPI_DATAEN_POLARITY (1 << 5)
+
+enum {
+ IMG_PIXEL_FORMAT_ARGB1555 = 0,
+ IMG_PIXEL_FORMAT_RGB555,
+ IMG_PIXEL_FORMAT_RGB565,
+ IMG_PIXEL_FORMAT_RGB888,
+ IMG_PIXEL_FORMAT_ARGB8888,
+};
+
+struct hi3620fb_info {
+ struct fb_info *fb;
+ struct device *dev;
+ void __iomem *reg_base;
+ struct clk *clk_ldi;
+ struct clk *clk_edc;
+ struct clk *clk_dsi;
+ struct clk *clk_lane;
+ int irq_edc;
+ int irq_ldi;
+ int irq_dsi;
+ struct regulator *vedc;
+ dma_addr_t fb_start_dma;
+ int pix_fmt;
+ int dsi_rate; /* dsi bit clock rate */
+ const char *mipi_mode_name;
+ int lane_cnt;
+ int color_mode;
+};
+
+extern int hi3620_mipi_enable(struct hi3620fb_info *info);
+extern int hi3620_mipi_disable(struct hi3620fb_info *info);
+extern int send_generic_packet(u8 *cmd, int len);
+#endif /* __HI3620FB_H */
diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c
new file mode 100644
index 00000000000..13ecd989701
--- /dev/null
+++ b/drivers/video/of_display_timing.c
@@ -0,0 +1,239 @@
+/*
+ * OF helpers for parsing display timings
+ *
+ * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
+ *
+ * based on of_videomode.c by Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This file is released under the GPLv2
+ */
+#include <linux/export.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <video/display_timing.h>
+#include <video/of_display_timing.h>
+
+/**
+ * parse_timing_property - parse timing_entry from device_node
+ * @np: device_node with the property
+ * @name: name of the property
+ * @result: will be set to the return value
+ *
+ * DESCRIPTION:
+ * Every display_timing can be specified with either just the typical value or
+ * a range consisting of min/typ/max. This function helps handling this
+ **/
+static int parse_timing_property(struct device_node *np, const char *name,
+ struct timing_entry *result)
+{
+ struct property *prop;
+ int length, cells, ret;
+
+ prop = of_find_property(np, name, &length);
+ if (!prop) {
+ pr_err("%s: could not find property %s\n",
+ of_node_full_name(np), name);
+ return -EINVAL;
+ }
+
+ cells = length / sizeof(u32);
+ if (cells == 1) {
+ ret = of_property_read_u32(np, name, &result->typ);
+ result->min = result->typ;
+ result->max = result->typ;
+ } else if (cells == 3) {
+ ret = of_property_read_u32_array(np, name, &result->min, cells);
+ } else {
+ pr_err("%s: illegal timing specification in %s\n",
+ of_node_full_name(np), name);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+/**
+ * of_get_display_timing - parse display_timing entry from device_node
+ * @np: device_node with the properties
+ **/
+static struct display_timing *of_get_display_timing(struct device_node *np)
+{
+ struct display_timing *dt;
+ u32 val = 0;
+ int ret = 0;
+
+ dt = kzalloc(sizeof(*dt), GFP_KERNEL);
+ if (!dt) {
+ pr_err("%s: could not allocate display_timing struct\n",
+ of_node_full_name(np));
+ return NULL;
+ }
+
+ ret |= parse_timing_property(np, "hback-porch", &dt->hback_porch);
+ ret |= parse_timing_property(np, "hfront-porch", &dt->hfront_porch);
+ ret |= parse_timing_property(np, "hactive", &dt->hactive);
+ ret |= parse_timing_property(np, "hsync-len", &dt->hsync_len);
+ ret |= parse_timing_property(np, "vback-porch", &dt->vback_porch);
+ ret |= parse_timing_property(np, "vfront-porch", &dt->vfront_porch);
+ ret |= parse_timing_property(np, "vactive", &dt->vactive);
+ ret |= parse_timing_property(np, "vsync-len", &dt->vsync_len);
+ ret |= parse_timing_property(np, "clock-frequency", &dt->pixelclock);
+
+ dt->dmt_flags = 0;
+ dt->data_flags = 0;
+ if (!of_property_read_u32(np, "vsync-active", &val))
+ dt->dmt_flags |= val ? VESA_DMT_VSYNC_HIGH :
+ VESA_DMT_VSYNC_LOW;
+ if (!of_property_read_u32(np, "hsync-active", &val))
+ dt->dmt_flags |= val ? VESA_DMT_HSYNC_HIGH :
+ VESA_DMT_HSYNC_LOW;
+ if (!of_property_read_u32(np, "de-active", &val))
+ dt->data_flags |= val ? DISPLAY_FLAGS_DE_HIGH :
+ DISPLAY_FLAGS_DE_LOW;
+ if (!of_property_read_u32(np, "pixelclk-active", &val))
+ dt->data_flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE :
+ DISPLAY_FLAGS_PIXDATA_NEGEDGE;
+
+ if (of_property_read_bool(np, "interlaced"))
+ dt->data_flags |= DISPLAY_FLAGS_INTERLACED;
+ if (of_property_read_bool(np, "doublescan"))
+ dt->data_flags |= DISPLAY_FLAGS_DOUBLESCAN;
+
+ if (ret) {
+ pr_err("%s: error reading timing properties\n",
+ of_node_full_name(np));
+ kfree(dt);
+ return NULL;
+ }
+
+ return dt;
+}
+
+/**
+ * of_get_display_timings - parse all display_timing entries from a device_node
+ * @np: device_node with the subnodes
+ **/
+struct display_timings *of_get_display_timings(struct device_node *np)
+{
+ struct device_node *timings_np;
+ struct device_node *entry;
+ struct device_node *native_mode;
+ struct display_timings *disp;
+
+ if (!np) {
+ pr_err("%s: no devicenode given\n", of_node_full_name(np));
+ return NULL;
+ }
+
+ timings_np = of_find_node_by_name(np, "display-timings");
+ if (!timings_np) {
+ pr_err("%s: could not find display-timings node\n",
+ of_node_full_name(np));
+ return NULL;
+ }
+
+ disp = kzalloc(sizeof(*disp), GFP_KERNEL);
+ if (!disp) {
+ pr_err("%s: could not allocate struct disp'\n",
+ of_node_full_name(np));
+ goto dispfail;
+ }
+
+ entry = of_parse_phandle(timings_np, "native-mode", 0);
+ /* assume first child as native mode if none provided */
+ if (!entry)
+ entry = of_get_next_child(np, NULL);
+ /* if there is no child, it is useless to go on */
+ if (!entry) {
+ pr_err("%s: no timing specifications given\n",
+ of_node_full_name(np));
+ goto entryfail;
+ }
+
+ pr_debug("%s: using %s as default timing\n",
+ of_node_full_name(np), entry->name);
+
+ native_mode = entry;
+
+ disp->num_timings = of_get_child_count(timings_np);
+ if (disp->num_timings == 0) {
+ /* should never happen, as entry was already found above */
+ pr_err("%s: no timings specified\n", of_node_full_name(np));
+ goto entryfail;
+ }
+
+ disp->timings = kzalloc(sizeof(struct display_timing *) *
+ disp->num_timings, GFP_KERNEL);
+ if (!disp->timings) {
+ pr_err("%s: could not allocate timings array\n",
+ of_node_full_name(np));
+ goto entryfail;
+ }
+
+ disp->num_timings = 0;
+ disp->native_mode = 0;
+
+ for_each_child_of_node(timings_np, entry) {
+ struct display_timing *dt;
+
+ dt = of_get_display_timing(entry);
+ if (!dt) {
+ /*
+ * to not encourage wrong devicetrees, fail in case of
+ * an error
+ */
+ pr_err("%s: error in timing %d\n",
+ of_node_full_name(np), disp->num_timings + 1);
+ goto timingfail;
+ }
+
+ if (native_mode == entry)
+ disp->native_mode = disp->num_timings;
+
+ disp->timings[disp->num_timings] = dt;
+ disp->num_timings++;
+ }
+ of_node_put(timings_np);
+ /*
+ * native_mode points to the device_node returned by of_parse_phandle
+ * therefore call of_node_put on it
+ */
+ of_node_put(native_mode);
+
+ pr_debug("%s: got %d timings. Using timing #%d as default\n",
+ of_node_full_name(np), disp->num_timings,
+ disp->native_mode + 1);
+
+ return disp;
+
+timingfail:
+ if (native_mode)
+ of_node_put(native_mode);
+ display_timings_release(disp);
+entryfail:
+ kfree(disp);
+dispfail:
+ of_node_put(timings_np);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(of_get_display_timings);
+
+/**
+ * of_display_timings_exist - check if a display-timings node is provided
+ * @np: device_node with the timing
+ **/
+int of_display_timings_exist(struct device_node *np)
+{
+ struct device_node *timings_np;
+
+ if (!np)
+ return -EINVAL;
+
+ timings_np = of_parse_phandle(np, "display-timings", 0);
+ if (!timings_np)
+ return -EINVAL;
+
+ of_node_put(timings_np);
+ return 1;
+}
+EXPORT_SYMBOL_GPL(of_display_timings_exist);
diff --git a/drivers/video/of_videomode.c b/drivers/video/of_videomode.c
new file mode 100644
index 00000000000..5b8066cd397
--- /dev/null
+++ b/drivers/video/of_videomode.c
@@ -0,0 +1,54 @@
+/*
+ * generic videomode helper
+ *
+ * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
+ *
+ * This file is released under the GPLv2
+ */
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/of.h>
+#include <video/display_timing.h>
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+/**
+ * of_get_videomode - get the videomode #<index> from devicetree
+ * @np - devicenode with the display_timings
+ * @vm - set to return value
+ * @index - index into list of display_timings
+ * (Set this to OF_USE_NATIVE_MODE to use whatever mode is
+ * specified as native mode in the DT.)
+ *
+ * DESCRIPTION:
+ * Get a list of all display timings and put the one
+ * specified by index into *vm. This function should only be used, if
+ * only one videomode is to be retrieved. A driver that needs to work
+ * with multiple/all videomodes should work with
+ * of_get_display_timings instead.
+ **/
+int of_get_videomode(struct device_node *np, struct videomode *vm,
+ int index)
+{
+ struct display_timings *disp;
+ int ret;
+
+ disp = of_get_display_timings(np);
+ if (!disp) {
+ pr_err("%s: no timings specified\n", of_node_full_name(np));
+ return -EINVAL;
+ }
+
+ if (index == OF_USE_NATIVE_MODE)
+ index = disp->native_mode;
+
+ ret = videomode_from_timing(disp, vm, index);
+ if (ret)
+ return ret;
+
+ display_timings_release(disp);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_get_videomode);
diff --git a/drivers/video/videomode.c b/drivers/video/videomode.c
new file mode 100644
index 00000000000..21c47a202af
--- /dev/null
+++ b/drivers/video/videomode.c
@@ -0,0 +1,39 @@
+/*
+ * generic display timing functions
+ *
+ * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
+ *
+ * This file is released under the GPLv2
+ */
+
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <video/display_timing.h>
+#include <video/videomode.h>
+
+int videomode_from_timing(const struct display_timings *disp,
+ struct videomode *vm, unsigned int index)
+{
+ struct display_timing *dt;
+
+ dt = display_timings_get(disp, index);
+ if (!dt)
+ return -EINVAL;
+
+ vm->pixelclock = display_timing_get_value(&dt->pixelclock, TE_TYP);
+ vm->hactive = display_timing_get_value(&dt->hactive, TE_TYP);
+ vm->hfront_porch = display_timing_get_value(&dt->hfront_porch, TE_TYP);
+ vm->hback_porch = display_timing_get_value(&dt->hback_porch, TE_TYP);
+ vm->hsync_len = display_timing_get_value(&dt->hsync_len, TE_TYP);
+
+ vm->vactive = display_timing_get_value(&dt->vactive, TE_TYP);
+ vm->vfront_porch = display_timing_get_value(&dt->vfront_porch, TE_TYP);
+ vm->vback_porch = display_timing_get_value(&dt->vback_porch, TE_TYP);
+ vm->vsync_len = display_timing_get_value(&dt->vsync_len, TE_TYP);
+
+ vm->dmt_flags = dt->dmt_flags;
+ vm->data_flags = dt->data_flags;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(videomode_from_timing);
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index d1ea7ce0b4c..c1fe60ad154 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -150,6 +150,15 @@
#endif
+#ifdef CONFIG_COMMON_CLK
+#define CLK_OF_TABLES() . = ALIGN(8); \
+ VMLINUX_SYMBOL(__clk_of_table) = .; \
+ *(__clk_of_table) \
+ *(__clk_of_table_end)
+#else
+#define CLK_OF_TABLES()
+#endif
+
#define KERNEL_DTB() \
STRUCT_ALIGN(); \
VMLINUX_SYMBOL(__dtb_start) = .; \
@@ -493,6 +502,7 @@
DEV_DISCARD(init.rodata) \
CPU_DISCARD(init.rodata) \
MEM_DISCARD(init.rodata) \
+ CLK_OF_TABLES() \
KERNEL_DTB()
#define INIT_TEXT \
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 4989b8a7bed..7f197d7addb 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -379,7 +379,13 @@ struct clk_onecell_data {
};
struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data);
const char *of_clk_get_parent_name(struct device_node *np, int index);
+
void of_clk_init(const struct of_device_id *matches);
+#define CLK_OF_DECLARE(name, compat, fn) \
+ static const struct of_device_id __clk_of_table_##name \
+ __used __section(__clk_of_table) \
+ = { .compatible = compat, .data = fn };
+
#endif /* CONFIG_COMMON_CLK */
#endif /* CLK_PROVIDER_H */
diff --git a/include/linux/fb.h b/include/linux/fb.h
index c7a95714b1f..58b98606ac2 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -19,6 +19,8 @@ struct vm_area_struct;
struct fb_info;
struct device;
struct file;
+struct videomode;
+struct device_node;
/* Definitions below are used in the parsed monitor specs */
#define FB_DPMS_ACTIVE_OFF 1
@@ -714,6 +716,12 @@ extern void fb_destroy_modedb(struct fb_videomode *modedb);
extern int fb_find_mode_cvt(struct fb_videomode *mode, int margins, int rb);
extern unsigned char *fb_ddc_read(struct i2c_adapter *adapter);
+extern int of_get_fb_videomode(struct device_node *np,
+ struct fb_videomode *fb,
+ int index);
+extern int fb_videomode_from_videomode(const struct videomode *vm,
+ struct fb_videomode *fbmode);
+
/* drivers/video/modedb.c */
#define VESA_MODEDB_SIZE 34
extern void fb_var_to_videomode(struct fb_videomode *mode,
diff --git a/include/linux/platform_data/hi3620-dsi.h b/include/linux/platform_data/hi3620-dsi.h
new file mode 100644
index 00000000000..447366f1e5c
--- /dev/null
+++ b/include/linux/platform_data/hi3620-dsi.h
@@ -0,0 +1,32 @@
+/*
+ * Hisilicon Hi3620 MIPI DSI head file
+ *
+ * Copyright (c) 2013 Hisilicon Limited.
+ * Copyright (c) 2013 Linaro Limited.
+ *
+ * Author: Haojian Zhuang <haojian.zhuang@linaro.org>
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef __HI3620_DSI_H
+#define __HI3620_DSI_H
+
+int hi3620_dsi_phy_write(void __iomem *reg_base, unsigned char addr,
+ unsigned char data);
+unsigned char hi3620_dsi_phy_read(void __iomem *reg_base, unsigned char addr);
+
+#endif
diff --git a/include/uapi/linux/fb.h b/include/uapi/linux/fb.h
index fb795c3b3c1..864e01e9613 100644
--- a/include/uapi/linux/fb.h
+++ b/include/uapi/linux/fb.h
@@ -226,6 +226,11 @@ struct fb_bitfield {
#define FB_VMODE_SMOOTH_XPAN 512 /* smooth xpan possible (internally used) */
#define FB_VMODE_CONUPDATE 512 /* don't update x/yoffset */
+#define FB_FLAG_DE_HIGH 1 /* data enable high active */
+#define FB_FLAG_DE_LOW 2 /* data enable low active */
+#define FB_FLAG_PIXDATA_POSEDGE 4 /* pixdata postive edge */
+#define FB_FLAG_PIXDATA_NEGEDGE 8 /* pixdata negative edge */
+
/*
* Display rotation support
*/
diff --git a/include/video/display_timing.h b/include/video/display_timing.h
new file mode 100644
index 00000000000..71e9a383a98
--- /dev/null
+++ b/include/video/display_timing.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ *
+ * description of display timings
+ *
+ * This file is released under the GPLv2
+ */
+
+#ifndef __LINUX_DISPLAY_TIMING_H
+#define __LINUX_DISPLAY_TIMING_H
+
+#include <linux/bitops.h>
+#include <linux/types.h>
+
+/* VESA display monitor timing parameters */
+#define VESA_DMT_HSYNC_LOW BIT(0)
+#define VESA_DMT_HSYNC_HIGH BIT(1)
+#define VESA_DMT_VSYNC_LOW BIT(2)
+#define VESA_DMT_VSYNC_HIGH BIT(3)
+
+/* display specific flags */
+#define DISPLAY_FLAGS_DE_LOW BIT(0) /* data enable flag */
+#define DISPLAY_FLAGS_DE_HIGH BIT(1)
+#define DISPLAY_FLAGS_PIXDATA_POSEDGE BIT(2) /* drive data on pos. edge */
+#define DISPLAY_FLAGS_PIXDATA_NEGEDGE BIT(3) /* drive data on neg. edge */
+#define DISPLAY_FLAGS_INTERLACED BIT(4)
+#define DISPLAY_FLAGS_DOUBLESCAN BIT(5)
+
+/*
+ * A single signal can be specified via a range of minimal and maximal values
+ * with a typical value, that lies somewhere inbetween.
+ */
+struct timing_entry {
+ u32 min;
+ u32 typ;
+ u32 max;
+};
+
+enum timing_entry_index {
+ TE_MIN = 0,
+ TE_TYP = 1,
+ TE_MAX = 2,
+};
+
+/*
+ * Single "mode" entry. This describes one set of signal timings a display can
+ * have in one setting. This struct can later be converted to struct videomode
+ * (see include/video/videomode.h). As each timing_entry can be defined as a
+ * range, one struct display_timing may become multiple struct videomodes.
+ *
+ * Example: hsync active high, vsync active low
+ *
+ * Active Video
+ * Video ______________________XXXXXXXXXXXXXXXXXXXXXX_____________________
+ * |<- sync ->|<- back ->|<----- active ----->|<- front ->|<- sync..
+ * | | porch | | porch |
+ *
+ * HSync _|¯¯¯¯¯¯¯¯¯¯|___________________________________________|¯¯¯¯¯¯¯¯¯
+ *
+ * VSync ¯|__________|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_________
+ */
+struct display_timing {
+ struct timing_entry pixelclock;
+
+ struct timing_entry hactive; /* hor. active video */
+ struct timing_entry hfront_porch; /* hor. front porch */
+ struct timing_entry hback_porch; /* hor. back porch */
+ struct timing_entry hsync_len; /* hor. sync len */
+
+ struct timing_entry vactive; /* ver. active video */
+ struct timing_entry vfront_porch; /* ver. front porch */
+ struct timing_entry vback_porch; /* ver. back porch */
+ struct timing_entry vsync_len; /* ver. sync len */
+
+ unsigned int dmt_flags; /* VESA DMT flags */
+ unsigned int data_flags; /* video data flags */
+};
+
+/*
+ * This describes all timing settings a display provides.
+ * The native_mode is the default setting for this display.
+ * Drivers that can handle multiple videomodes should work with this struct and
+ * convert each entry to the desired end result.
+ */
+struct display_timings {
+ unsigned int num_timings;
+ unsigned int native_mode;
+
+ struct display_timing **timings;
+};
+
+/* get value specified by index from struct timing_entry */
+static inline u32 display_timing_get_value(const struct timing_entry *te,
+ enum timing_entry_index index)
+{
+ switch (index) {
+ case TE_MIN:
+ return te->min;
+ break;
+ case TE_TYP:
+ return te->typ;
+ break;
+ case TE_MAX:
+ return te->max;
+ break;
+ default:
+ return te->typ;
+ }
+}
+
+/* get one entry from struct display_timings */
+static inline struct display_timing *display_timings_get(const struct
+ display_timings *disp,
+ unsigned int index)
+{
+ if (disp->num_timings > index)
+ return disp->timings[index];
+ else
+ return NULL;
+}
+
+void display_timings_release(struct display_timings *disp);
+
+#endif
diff --git a/include/video/of_display_timing.h b/include/video/of_display_timing.h
new file mode 100644
index 00000000000..8016eb727cf
--- /dev/null
+++ b/include/video/of_display_timing.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ *
+ * display timings of helpers
+ *
+ * This file is released under the GPLv2
+ */
+
+#ifndef __LINUX_OF_DISPLAY_TIMING_H
+#define __LINUX_OF_DISPLAY_TIMING_H
+
+struct device_node;
+struct display_timings;
+
+#define OF_USE_NATIVE_MODE -1
+
+struct display_timings *of_get_display_timings(struct device_node *np);
+int of_display_timings_exist(struct device_node *np);
+
+#endif
diff --git a/include/video/of_videomode.h b/include/video/of_videomode.h
new file mode 100644
index 00000000000..a07efcc5142
--- /dev/null
+++ b/include/video/of_videomode.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ *
+ * videomode of-helpers
+ *
+ * This file is released under the GPLv2
+ */
+
+#ifndef __LINUX_OF_VIDEOMODE_H
+#define __LINUX_OF_VIDEOMODE_H
+
+struct device_node;
+struct videomode;
+
+int of_get_videomode(struct device_node *np, struct videomode *vm,
+ int index);
+
+#endif /* __LINUX_OF_VIDEOMODE_H */
diff --git a/include/video/videomode.h b/include/video/videomode.h
new file mode 100644
index 00000000000..a42156234dd
--- /dev/null
+++ b/include/video/videomode.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ *
+ * generic videomode description
+ *
+ * This file is released under the GPLv2
+ */
+
+#ifndef __LINUX_VIDEOMODE_H
+#define __LINUX_VIDEOMODE_H
+
+#include <linux/types.h>
+#include <video/display_timing.h>
+
+/*
+ * Subsystem independent description of a videomode.
+ * Can be generated from struct display_timing.
+ */
+struct videomode {
+ unsigned long pixelclock; /* pixelclock in Hz */
+
+ u32 hactive;
+ u32 hfront_porch;
+ u32 hback_porch;
+ u32 hsync_len;
+
+ u32 vactive;
+ u32 vfront_porch;
+ u32 vback_porch;
+ u32 vsync_len;
+
+ unsigned int dmt_flags; /* VESA DMT flags */
+ unsigned int data_flags; /* video data flags */
+};
+
+/**
+ * videomode_from_timing - convert display timing to videomode
+ * @disp: structure with all possible timing entries
+ * @vm: return value
+ * @index: index into the list of display timings in devicetree
+ *
+ * DESCRIPTION:
+ * This function converts a struct display_timing to a struct videomode.
+ */
+int videomode_from_timing(const struct display_timings *disp,
+ struct videomode *vm, unsigned int index);
+
+#endif