diff options
author | Guodong Xu <guodong.xu@linaro.org> | 2013-04-07 10:47:43 +0800 |
---|---|---|
committer | Guodong Xu <guodong.xu@linaro.org> | 2013-04-07 10:47:43 +0800 |
commit | dd0720b62da7de8ce61a6bf493490fb436b7cf7f (patch) | |
tree | ee3642b80e5df8e4ce2f50d3017eb5c429395e47 | |
parent | bb09358585cd95234e627cb7e0d528934ce8f686 (diff) | |
parent | 9084bd35830ad6e03c96b6f6f7102eec57d57507 (diff) |
Merge remote-tracking branch 'origin/lcd-m2' into m2-candidate-0407m2-release.v3.8m2-candidate-0407
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 000000000000..150038552bc3 --- /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 c8bbdc42224c..c2e7ea59ef48 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 a124213bde0a..52914890529f 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 94cdfd4807e6..8ef04b8164f0 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 e66a482e7f1a..0ad473d82c67 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 a47e6ee98b8c..a18280ad62fb 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 554ec53bc6e0..4190b55fa456 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 a9204c69148d..68b402101170 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 af78ed6b67ef..dc58fbd8516f 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 251e45d6024d..fabbfe1a9253 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 000000000000..1f8b3d3a2ed6 --- /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 000000000000..cd97fda64891 --- /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 000000000000..c89e8ff1aa81 --- /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 000000000000..9682517edfd4 --- /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 e513cd998170..8ffb7c56e18e 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 62a2963cfe58..076bdeb3965b 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 000000000000..1fbb6855cc02 --- /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 e7068c508800..2343912499d0 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 768a137a1bac..b322ebb7bc76 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 000000000000..5e1822cef571 --- /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 cef65574db6c..7e1fb813b062 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 000000000000..a683760d2185 --- /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 000000000000..1f4b79befe51 --- /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 000000000000..af3dd68abd81 --- /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 000000000000..83cce2022743 --- /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 000000000000..676ff8e2e8f7 --- /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 000000000000..13ecd9897010 --- /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 000000000000..5b8066cd397f --- /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 000000000000..21c47a202afa --- /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 d1ea7ce0b4cb..c1fe60ad1540 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 4989b8a7bed1..7f197d7addb0 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 c7a95714b1fe..58b98606ac26 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 000000000000..447366f1e5c1 --- /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 fb795c3b3c17..864e01e9613c 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 000000000000..71e9a383a981 --- /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 000000000000..8016eb727cf3 --- /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 000000000000..a07efcc51424 --- /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 000000000000..a42156234dd4 --- /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 |