diff options
author | Andrey Konovalov <andrey.konovalov@linaro.org> | 2013-10-17 18:24:00 +0400 |
---|---|---|
committer | Andrey Konovalov <andrey.konovalov@linaro.org> | 2013-10-17 18:24:00 +0400 |
commit | 804a2fa7ddc7a2b4b2dac7fa2d569e0bff5aad12 (patch) | |
tree | aacbb4bc5e06153f27d1f2c6e9b17f9392b05fb3 | |
parent | 57c6042028e0b4c8911038124adc1e6f4abe8112 (diff) | |
parent | bd6757f02ea091ecf15bc188672d93b04ca16250 (diff) |
Merge branch 'tracking-integration-hilt-linux-linaro' into merge-linux-linaroll-20131017.0ll_20131017.0
44 files changed, 9686 insertions, 3 deletions
diff --git a/Documentation/devicetree/bindings/arm/hs/hisilicon.txt b/Documentation/devicetree/bindings/arm/hs/hisilicon.txt new file mode 100644 index 000000000000..41ac783542e1 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/hs/hisilicon.txt @@ -0,0 +1,42 @@ +Hisilicon Platforms Device Tree Bindings +---------------------------------------------------- + +Hi3716 Development Board +Required root node properties: + - compatible = "hisilicon,hi3716-dkb"; + +Hi4511 Board +Required root node properties: + - compatible = "hisilicon,hi3620-hi4511"; + + +Hisilicon sctrl resiter description + +Required properties: +- compatible : "hisilicon,sctrl" +- reg : Address and size of sysctrl. +- smp_reg : offset in sysctrl for notifying slave cpu booting + cpu 1, reg; + cpu 2, reg + 0x4; + cpu 3, reg + 0x8; + If reg value is not zero, cpun exit wfi and go +- resume_reg : offset in sysctrl for notifying cpu0 when resume +- reset_reg : offset in sysctrl for system reset + +Example: + hi3716: + sctrl@f8000000 { + compatible = "hisilicon,sctrl"; + reg = <0xf8000000 0x1000>; + smp_reg = <0xc0>; + reboot_reg = <0x4>; + }; + + hi3620: + sctrl@fc802000 { + compatible = "hisilicon,sctrl"; + reg = <0xfc802000 0x1000>; + smp_reg = <0x31c>; + resume_reg = <0x308>; + reboot_reg = <0x4>; + }; diff --git a/Documentation/devicetree/bindings/clock/hi3716.txt b/Documentation/devicetree/bindings/clock/hi3716.txt new file mode 100644 index 000000000000..60af61e1a413 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/hi3716.txt @@ -0,0 +1,121 @@ +Device Tree Clock bindings for hi3716 + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Clock control register +Required properties: +- compatible : "hisilicon,clkbase" +- reg : Address and size of clkbase. + +Device Clocks + +Device clocks are required to have one or both of the following sets of +properties: + + +Gated device clocks: + +Required properties: +- compatible : "hisilicon,hi3716-clk-gate" +- gate-reg : shall be the register offset from clkbase and enable bit, reset bit +- clock-output-names : shall be reference name + + +Mux device clocks: + +Required properties: +- compatible : "hisilicon,hi3716-clk-mux" +- mux-reg : shall be the register offset from clkbase and mux_shift mux_width +- mux-table : shall be reg value to be choose clocks accordingly +- clock-output-names : shall be reference name + + +Fixed divisor device clocks: + +Required properties: +- compatible : "hisilicon,hi3716-fixed-divider" +- div-table : shall be divisor sequence +- clock-output-names : shall be reference name according to divisor + + +Fixed pll clocks: + +Required properties: +- compatible : "hisilicon,hi3716-fixed-pll" +- clock-frequency : shall be output clock frequence sequence +- clock-output-names : shall be reference name according to clock-frequnce + +For example: + clkbase@f8a22000 { + compatible = "hisilicon,clkbase"; + reg = <0xf8a22000 0x1000>; + }; + + osc24m: osc { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24000000>; + clock-output-names = "osc24mhz"; + }; + + bpll: bpll { + compatible = "hisilicon,hi3716-fixed-pll"; + #clock-cells = <1>; + clocks = <&osc24m>; + clock-frequency = <1200000000>, + <600000000>, + <300000000>, + <200000000>, + <150000000>; + clock-output-names = "bpll_fout0", + "bpll_fout1", + "bpll_fout2", + "bpll_fout3", + "bpll_fout4"; + }; + + bpll_fout0_div: bpll_fout0_div {/* 1200Mhz */ + compatible = "hisilicon,hi3716-fixed-divider"; + #clock-cells = <1>; + clocks = <&bpll 0>; + div-table = <3 14 25 50>; + clock-output-names = "fout0_400m", "fout0_86m", + "fout0_48m", "fout0_24m"; + }; + + bpll_fout3_div: bpll_fout3_div { + compatible = "hisilicon,hi3716-fixed-divider"; + #clock-cells = <1>; + clocks = <&bpll 3>; + div-table = <2 4 5 8>; + clock-output-names = "fout3_100m", "fout3_50m", + "fout3_40m", "fout3_25m"; + }; + + clk_sfc_mux: clk_sfc_mux { + compatible = "hisilicon,hi3716-clk-mux"; + #clock-cells = <0>; + /* clks: 24M 75M 100M 150M 200M */ + clocks = <&osc24m>, <&bpll_fout2_div>, + <&bpll_fout3_div 0>, <&bpll 4>, <&bpll 3>; + + /* offset mux_shift mux_width */ + mux-reg = <0x5c 8 3>; + /* mux reg value to choose clks */ + mux-table = <0 7 6 4 5>; + + clock-output-names = "sfc_mux"; + }; + + clk_sfc: clk_sfc { + compatible = "hisilicon,hi3716-clk-gate"; + #clock-cells = <0>; + clocks = <&clk_sfc_mux>; + + /* offset, enable, reset */ + gate-reg = <0x5c 0 4>; + + clock-output-names = "sfc"; + }; diff --git a/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt new file mode 100644 index 000000000000..54a9d8bfdfaf --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt @@ -0,0 +1,66 @@ +* Hisilicon specific extensions to the Synopsis Designware Mobile + Storage Host Controller + +Read synopsis-dw-mshc.txt for more details +The Synopsis designware mobile storage host controller is used to interface +a SoC with storage medium such as eMMC or SD/MMC cards. This file documents +differences between the core Synopsis dw mshc controller properties described +by synopsis-dw-mshc.txt and the properties used by the Hisilicon specific +extensions to the Synopsis Designware Mobile Storage Host Controller. + +Required Properties: + +* compatible: should be + - "hisilicon,hi4511-dw-mshc": for controllers with hi4511 + specific extentions. +* vmmc-supply: should be vmmc used in dwmmc +* fifo-depth: should be provided if register can not provide correct value +* clken-reg: should be clock enable register and offset +* drv-sel-reg: should be driver delay select register and offset +* sam-sel-reg: should be sample delay select register and offset +* div-reg: should be divider register and offset + +Example: + + The MSHC controller node can be split into two portions, SoC specific and + board specific portions as listed below. + + dwmmc_0: dwmmc0@fcd03000 { + compatible = "hisilicon,hi4511-dw-mshc"; + reg = <0xfcd03000 0x1000>; + interrupts = <0 16 4>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clk_sd>, <&clk_ddrc_per>; + clock-names = "ciu", "biu"; + clken-reg = <0x1f8 0>; + drv-sel-reg = <0x1f8 4>; + sam-sel-reg = <0x1f8 8>; + div-reg = <0x1f8 1>; + }; + dwmmc0@fcd03000 { + num-slots = <1>; + vmmc-supply = <&ldo12>; + fifo-depth = <0x100>; + supports-highspeed; + pinctrl-names = "default"; + pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>; + cd-gpio = <&gpio10 3 0>; + slot@0 { + reg = <0>; + bus-width = <4>; + }; + }; + +PCTRL: + +Required Properties: +* compatible: should be + - "hisilicon,pctrl": Peripheral control + +Example: + + pctrl: pctrl@fca09000 { + compatible = "hisilicon,pctrl"; + reg = <0xfca09000 0x1000>; + }; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 46fcf5f7f7bd..b2d1acc62247 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -954,6 +954,8 @@ source "arch/arm/mach-gemini/Kconfig" source "arch/arm/mach-highbank/Kconfig" +source "arch/arm/mach-hs/Kconfig" + source "arch/arm/mach-integrator/Kconfig" source "arch/arm/mach-iop32x/Kconfig" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 1b1e3c9138d0..f4ba7b29ca2b 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -157,6 +157,7 @@ machine-$(CONFIG_ARCH_EP93XX) += ep93xx machine-$(CONFIG_ARCH_EXYNOS) += exynos machine-$(CONFIG_ARCH_GEMINI) += gemini machine-$(CONFIG_ARCH_HIGHBANK) += highbank +machine-$(CONFIG_ARCH_HI3xxx) += hs machine-$(CONFIG_ARCH_INTEGRATOR) += integrator machine-$(CONFIG_ARCH_IOP13XX) += iop13xx machine-$(CONFIG_ARCH_IOP32X) += iop32x diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index a3178d1d435d..09450c686bae 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -68,6 +68,8 @@ dtb-$(CONFIG_ARCH_EXYNOS) += exynos4210-origen.dtb \ exynos5440-ssdk5440.dtb dtb-$(CONFIG_ARCH_HIGHBANK) += highbank.dtb \ ecx-2000.dtb +dtb-$(CONFIG_ARCH_HI3xxx) += hi4511.dtb \ + hi3716-dkb.dtb dtb-$(CONFIG_ARCH_INTEGRATOR) += integratorap.dtb \ integratorcp.dtb dtb-$(CONFIG_ARCH_LPC32XX) += ea3250.dtb phy3250.dtb diff --git a/arch/arm/boot/dts/hi3620.dtsi b/arch/arm/boot/dts/hi3620.dtsi new file mode 100644 index 000000000000..9829c9cca12e --- /dev/null +++ b/arch/arm/boot/dts/hi3620.dtsi @@ -0,0 +1,1599 @@ +/* + * Hisilicon Ltd. Hi3620 SoC + * + * Copyright (C) 2012-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 version 2 as + * publishhed by the Free Software Foundation. + */ + +/include/ "skeleton.dtsi" + +/ { + aliases { + serial0 = &uart0; + serial1 = &uart1; + mshc0 = &dwmmc_0; + mshc1 = &dwmmc_1; + mshc2 = &dwmmc_2; + 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"; + }; + + + amba { + #address-cells = <1>; + #size-cells = <1>; + compatible = "arm,amba-bus"; + interrupt-parent = <&intc>; + ranges; + + pmctrl: pmctrl@fca08000 { + compatible = "hisilicon,pmctrl"; + reg = <0xfca08000 0x1000>; + }; + + pctrl: pctrl@fca09000 { + compatible = "hisilicon,pctrl"; + reg = <0xfca09000 0x1000>; + }; + + secram: secram@f8000000 { + compatible = "hisilicon,secram"; + reg = <0xf8000000 0x14000>; + }; + + ddrcfg: ddrcfg@fcd00000 { + compatible = "hisilicon,ddrcfg"; + reg = <0xfcd00000 0x2000>; + }; + + + sctrl: sctrl@fc802000 { + compatible = "hisilicon,sctrl"; + reg = <0xfc802000 0x1000>; + smp_reg = <0x31c>; + resume_reg = <0x308>; + reboot_reg = <0x4>; + + refclk_uart0: refclk@0 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&osc26m &pclk>; + hiword; + clock-output-names = "rclk_uart0"; + /* reg_offset, enable_bits */ + hisilicon,clkmux-reg = <0x100 0x80>; + hisilicon,clkmux-table = <0 0x80>; + }; + refclk_uart1: refclk@1 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&osc26m &pclk>; + hiword; + clock-output-names = "rclk_uart1"; + hisilicon,clkmux-reg = <0x100 0x100>; + hisilicon,clkmux-table = <0 0x100>; + }; + refclk_uart2: refclk@2 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&osc26m &pclk>; + hiword; + clock-output-names = "rclk_uart2"; + hisilicon,clkmux-reg = <0x100 0x200>; + hisilicon,clkmux-table = <0 0x200>; + }; + refclk_uart3: refclk@3 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&osc26m &pclk>; + hiword; + clock-output-names = "rclk_uart3"; + hisilicon,clkmux-reg = <0x100 0x400>; + hisilicon,clkmux-table = <0 0x400>; + }; + refclk_uart4: refclk@4 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&osc26m &pclk>; + hiword; + clock-output-names = "rclk_uart4"; + hisilicon,clkmux-reg = <0x100 0x800>; + hisilicon,clkmux-table = <0 0x800>; + }; + + refclk_hsic: hsic { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&pll_usb &pll_peri>; + hiword; + clock-output-names = "rclk_hsic"; + hisilicon,clkmux-reg = <0x130 0x4>; + hisilicon,clkmux-table = <0 0x4>; + }; + + clk_osc480m: clk_osc480m { + compatible = "hisilicon,hi3620-clk-div"; + #clock-cells = <0>; + clocks = <&refclk_hsic>; + clock-output-names = "clk_osc480m"; + hisilicon,clkdiv-table = <4 1>; + /* divider register offset, mask */ + hisilicon,clkdiv = <0x130 0xf>; + }; + 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>; + hiword; + clock-output-names = "rclk_spi0"; + hisilicon,clkmux-reg = <0x100 0x1000>; + hisilicon,clkmux-table = <0 0x1000>; + }; + refclk_spi1: refclk@7 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&osc26m &refclk_cfgaxi>; + hiword; + clock-output-names = "rclk_spi1"; + hisilicon,clkmux-reg = <0x100 0x2000>; + hisilicon,clkmux-table = <0 0x2000>; + }; + refclk_spi2: refclk@8 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&osc26m &refclk_cfgaxi>; + hiword; + clock-output-names = "rclk_spi2"; + hisilicon,clkmux-reg = <0x100 0x4000>; + hisilicon,clkmux-table = <0 0x4000>; + }; + refclk_tcxo: refclk@11 { + compatible = "hisilicon,clk-fixed-factor"; + #clock-cells = <0>; + clocks = <&osc26m>; + clock-output-names = "rclk_tcxo"; + hisilicon,fixed-factor = <1 4>; + }; + timer0_mux: timer0_mux { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&osc32k &timerclk01>; + clock-output-names = "timer0_mux"; + hisilicon,clkmux-reg = <0 0x18000>; + hisilicon,clkmux-table = <0 0x8000>; + }; + timer1_mux: timer1_mux { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&osc32k &timerclk01>; + clock-output-names = "timer1_mux"; + hisilicon,clkmux-reg = <0 0x60000>; + hisilicon,clkmux-table = <0 0x20000>; + }; + timer2_mux: timer2_mux { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&osc32k &timerclk23>; + clock-output-names = "timer2_mux"; + hisilicon,clkmux-reg = <0 0x180000>; + hisilicon,clkmux-table = <0 0x80000>; + }; + timer3_mux: timer3_mux { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&osc32k &timerclk23>; + clock-output-names = "timer3_mux"; + hisilicon,clkmux-reg = <0 0x600000>; + hisilicon,clkmux-table = <0 0x200000>; + }; + timer4_mux: timer4_mux { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&osc32k &timerclk45>; + clock-output-names = "timer4_mux"; + hisilicon,clkmux-reg = <0x18 0x3>; + hisilicon,clkmux-table = <0 0x1>; + }; + timer5_mux: timer5_mux { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&osc32k &timerclk45>; + clock-output-names = "timer5_mux"; + hisilicon,clkmux-reg = <0x18 0xc>; + hisilicon,clkmux-table = <0 0x4>; + }; + timer6_mux: timer6_mux { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&osc32k &timerclk67>; + clock-output-names = "timer6_mux"; + hisilicon,clkmux-reg = <0x18 0x30>; + hisilicon,clkmux-table = <0 0x10>; + }; + timer7_mux: timer7_mux { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&osc32k &timerclk67>; + clock-output-names = "timer7_mux"; + hisilicon,clkmux-reg = <0x18 0xc0>; + hisilicon,clkmux-table = <0 0x40>; + }; + refclk_shareAXI: refclk@22 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&pll_usb &pll_peri>; + hiword; + clock-output-names = "rclk_shareAXI"; + hisilicon,clkmux-reg = <0x24 0x8000>; + hisilicon,clkmux-table = <0 0x8000>; + }; + refclk_mmc1: refclk@23 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&pll_peri &pll_usb>; + hiword; + clock-output-names = "rclk_mmc1"; + hisilicon,clkmux-reg = <0x108 0x200>; + hisilicon,clkmux-table = <0 0x200>; + }; + refclk_mmc2: refclk@24 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&pll_peri &pll_usb>; + hiword; + clock-output-names = "rclk_mmc2"; + hisilicon,clkmux-reg = <0x140 0x10>; + hisilicon,clkmux-table = <0 0x10>; + }; + refclk_mmc3: refclk@25 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&pll_peri &pll_usb>; + hiword; + clock-output-names = "rclk_mmc3"; + hisilicon,clkmux-reg = <0x140 0x200>; + hisilicon,clkmux-table = <0 0x200>; + }; + refclk_sd: refclk@26 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&pll_peri &pll_usb>; + hiword; + clock-output-names = "rclk_sd"; + hisilicon,clkmux-reg = <0x108 0x10>; + hisilicon,clkmux-table = <0 0x10>; + }; + refclk_mmc1_parent: refclk@27 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + hiword; + clocks = <&osc26m &div_mmc1>; + clock-output-names = "rclk_mmc1_parent"; + hisilicon,clkmux-reg = <0x108 0x400>; + hisilicon,clkmux-table = <0 0x400>; + }; + refclk_venc: refclk@28 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + hiword; + clocks = <&pll_peri &pll_usb>; + clock-output-names = "rclk_venc"; + hisilicon,clkmux-reg = <0x10c 0x800>; + hisilicon,clkmux-table = <0 0x800>; + }; + refclk_g2d: refclk@29 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + hiword; + clocks = <&pll_peri &pll_usb>; + clock-output-names = "rclk_g2d"; + hisilicon,clkmux-reg = <0x10c 0x20>; + hisilicon,clkmux-table = <0 0x20>; + }; + refclk_vdec: refclk@30 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + hiword; + clocks = <&pll_peri &pll_usb>; + clock-output-names = "rclk_vdec"; + hisilicon,clkmux-reg = <0x110 0x20>; + hisilicon,clkmux-table = <0 0x20>; + }; + refclk_vpp: refclk@31 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + hiword; + clocks = <&pll_peri &pll_usb>; + clock-output-names = "rclk_vpp"; + hisilicon,clkmux-reg = <0x110 0x800>; + hisilicon,clkmux-table = <0 0x800>; + }; + refclk_ldi0: refclk@32 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + hiword; + clocks = <&pll_peri &pll_usb &pll_hdmi>; + clock-output-names = "rclk_ldi0"; + hisilicon,clkmux-reg = <0x114 0x6000>; + hisilicon,clkmux-table = <0 0x6000>; + }; + refclk_ldi1: refclk@33 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + hiword; + clocks = <&pll_peri &pll_usb &pll_hdmi>; + clock-output-names = "rclk_ldi1"; + hisilicon,clkmux-reg = <0x118 0xc000>; + hisilicon,clkmux-table = <0 0xc000>; + }; + clk_osc480mdiv40: osc480mdiv40 { + compatible = "hisilicon,clk-fixed-factor"; + #clock-cells = <0>; + clocks = <&clk_osc480m>; + /*mult, div*/ + hisilicon,fixed-factor = <1 40>; + clock-output-names = "clk_osc480mdiv40"; + }; + clk_usbpicophy: usbpicophy { + compatible = "hisilicon,hi3620-clk-gate"; + #clock-cells = <0>; + clocks = <&clk_osc480mdiv40>; + clock-output-names = "clk_usbpicophy"; + hisilicon,hi3620-clkreset = <0x8c 0x1000000>; + hisilicon,hi3620-clkgate = <0x30 0x1000000>; + }; + clk_usb2dvc: usb2dvc { + compatible = "hisilicon,hi3620-clk-gate"; + #clock-cells = <0>; + clocks = <&refclk_cfgaxi>; + clock-output-names = "clk_usb2dvc"; + hisilicon,hi3620-clkreset = <0xa4 0x20000>; + hisilicon,hi3620-clkgate = <0x50 0x20000>; + }; + 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>; + }; + pwm0_mux: pwm0_mux { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&osc32k &pwm_divider>; + hiword; + clock-output-names = "pwm0_mux"; + hisilicon,clkmux-reg = <0x104 0x400>; + hisilicon,clkmux-table = <0 0x400>; + }; + pwm1_mux: pwm1_mux { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + clocks = <&osc32k &pwm_divider>; + hiword; + clock-output-names = "pwm1_mux"; + hisilicon,clkmux-reg = <0x104 0x800>; + hisilicon,clkmux-table = <0 0x800>; + }; + pwmclk0: clkgate@30 { + compatible = "hisilicon,hi3620-clk-gate"; + #clock-cells = <0>; + clocks = <&pwm0_mux>; + 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 = <&pwm1_mux>; + 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>; + }; + 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"; + hisilicon,hi3620-clkgate = <0x40 0x1000000>; + }; + i2cclk1: clkgate@49 { + compatible = "hisilicon,hi3620-clk-gate"; + #clock-cells = <0>; + clocks = <&pclk>; + clock-output-names = "clk_i2c1"; + hisilicon,hi3620-clkgate = <0x40 0x2000000>; + }; + i2cclk2: clkgate@50 { + compatible = "hisilicon,hi3620-clk-gate"; + #clock-cells = <0>; + clocks = <&pclk>; + clock-output-names = "clk_i2c2"; + hisilicon,hi3620-clkgate = <0x40 0x10000000>; + }; + i2cclk3: clkgate@51 { + compatible = "hisilicon,hi3620-clk-gate"; + #clock-cells = <0>; + clocks = <&pclk>; + clock-output-names = "clk_i2c3"; + 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 = <&refclk_mmc1_parent>; + 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 = <&div_sd>; + 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>; + }; + sciclk: clkgate@61 { + compatible = "hisilicon,hi3620-clk-gate"; + #clock-cells = <0>; + clocks = <&osc26m>; + clock-output-names = "clk_sci"; + hisilicon,hi3620-clkgate = <0x40 0x4000000>; + }; + dphyclk0: clkgate@62 { + compatible = "hisilicon,hi3620-clk-gate"; + #clock-cells = <0>; + clocks = <&osc26m>; + clock-output-names = "clk_dphy0"; + hisilicon,hi3620-clkgate = <0x30 0x8000>; + }; + dphyclk1: clkgate@63 { + compatible = "hisilicon,hi3620-clk-gate"; + #clock-cells = <0>; + clocks = <&osc26m>; + clock-output-names = "clk_dphy1"; + hisilicon,hi3620-clkgate = <0x30 0x10000>; + }; + 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>; + }; + edcclk1: clkgate@68 { + compatible = "hisilicon,hi3620-clk-gate"; + #clock-cells = <0>; + clocks = <&pclk>; + clock-output-names = "clk_edc1"; + hisilicon,hi3620-clkgate = <0x30 0x400>; + }; + + dtable: clkdiv@0 { + #hisilicon,clkdiv-table-cells = <2>; + }; + + div_shareaxi: clkdiv@1 { + compatible = "hisilicon,hi3620-clk-div"; + #clock-cells = <0>; + clocks = <&refclk_shareAXI>; + clock-output-names = "shareAXI_div"; + hisilicon,clkdiv-table = <32 1>; + /* 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 = <2 2>; + hisilicon,clkdiv = <0x100 0x60>; + }; + div_mmc1: clkdiv@3 { + compatible = "hisilicon,hi3620-clk-div"; + #clock-cells = <0>; + clocks = <&refclk_mmc1>; + clock-output-names = "div_mmc1"; + hisilicon,clkdiv-table = <16 1>; + hisilicon,clkdiv = <0x108 0x1e0>; + }; + div_mmc2: clkdiv@4 { + compatible = "hisilicon,hi3620-clk-div"; + #clock-cells = <0>; + clocks = <&refclk_mmc2>; + clock-output-names = "div_mmc2"; + hisilicon,clkdiv-table = <16 1>; + hisilicon,clkdiv = <0x140 0xf>; + }; + div_mmc3: clkdiv@5 { + compatible = "hisilicon,hi3620-clk-div"; + #clock-cells = <0>; + clocks = <&refclk_mmc3>; + clock-output-names = "div_mmc3"; + hisilicon,clkdiv-table = <16 1>; + hisilicon,clkdiv = <0x140 0x1e0>; + }; + div_sd: clkdiv@6 { + compatible = "hisilicon,hi3620-clk-div"; + #clock-cells = <0>; + clocks = <&refclk_sd>; + clock-output-names = "div_sd"; + hisilicon,clkdiv-table = <16 1>; + hisilicon,clkdiv = <0x108 0xf>; + }; + pwm_divider: pwm_divider { + compatible = "hisilicon,hi3620-clk-div"; + #clock-cells = <0>; + clocks = <&osc26m>; + clock-output-names = "pwm_divider"; + hisilicon,clkdiv-table = <31 1>; + hisilicon,clkdiv = <0x104 0x3e0>; + }; + }; + + rtc0: rtc@fc804000 { + compatible = "arm,rtc-pl031", "arm,primecell"; + reg = <0xfc804000 0x1000>; + interrupts = <0 9 0x4>; + clocks = <&rtcclk>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + l2: l2-cache { + compatible = "arm,pl310-cache"; + reg = <0xfc100000 0x100000>; + interrupts = <0 15 4>; + cache-unified; + cache-level = <2>; + }; + + intc: interrupt-controller@fc001000 { + compatible = "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + /* gic dist base, gic cpu base */ + reg = <0xfc001000 0x1000>, <0xfc000100 0x100>; + }; + + dual_timer0: dual_timer@fc800000 { + compatible = "arm,sp804", "arm,primecell"; + reg = <0xfc800000 0x1000>; + /* timer00 & timer01 */ + interrupts = <0 0 4>, <0 1 4>; + clocks = <&timer0_mux &timer1_mux>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + dual_timer1: dual_timer@fc801000 { + /* + * Only used in NORMAL state, not available ins + * SLOW or DOZE state. + * The rate is fixed in 24MHz. + */ + compatible = "arm,sp804", "arm,primecell"; + reg = <0xfc801000 0x1000>; + /* timer10 & timer11 */ + interrupts = <0 2 4>, <0 3 4>; + clocks = <&timer2_mux &timer3_mux>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + dual_timer2: dual_timer@fca01000 { + compatible = "arm,sp804", "arm,primecell"; + reg = <0xfca01000 0x1000>; + /* timer20 & timer21 */ + interrupts = <0 4 4>, <0 5 4>; + clocks = <&timer4_mux &timer5_mux>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + dual_timer3: dual_timer@fca02000 { + compatible = "arm,sp804", "arm,primecell"; + reg = <0xfca02000 0x1000>; + /* timer30 & timer31 */ + interrupts = <0 6 4>, <0 7 4>; + clocks = <&timer6_mux &timer7_mux>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + + timer5: timer@fc000600 { + compatible = "arm,cortex-a9-twd-timer"; + reg = <0xfc000600 0x20>; + interrupts = <1 13 0xf01>; + }; + + uart0: uart@fcb00000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0xfcb00000 0x1000>; + interrupts = <0 20 4>; + clocks = <&uartclk0>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + uart1: uart@fcb01000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0xfcb01000 0x1000>; + interrupts = <0 21 4>; + clocks = <&uartclk1>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + uart2: uart@fcb02000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0xfcb02000 0x1000>; + interrupts = <0 22 4>; + clocks = <&uartclk2>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + uart3: uart@fcb03000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0xfcb03000 0x1000>; + interrupts = <0 23 4>; + clocks = <&uartclk3>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + uart4: uart@fcb04000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0xfcb04000 0x1000>; + interrupts = <0 24 4>; + clocks = <&uartclk4>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + gpio0: gpio@fc806000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc806000 0x1000>; + interrupts = <0 64 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 2 0 1 &pmx0 3 0 1 &pmx0 4 0 1 + &pmx0 5 0 1 &pmx0 6 1 1 &pmx0 7 2 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk0>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio1: gpio@fc807000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc807000 0x1000>; + interrupts = <0 65 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 0 3 1 &pmx0 1 3 1 &pmx0 2 3 1 + &pmx0 3 3 1 &pmx0 4 3 1 &pmx0 5 4 1 + &pmx0 6 5 1 &pmx0 7 6 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk1>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio2: gpio@fc808000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc808000 0x1000>; + interrupts = <0 66 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 0 7 1 &pmx0 1 8 1 &pmx0 2 9 1 + &pmx0 3 10 1 &pmx0 4 3 1 &pmx0 5 3 1 + &pmx0 6 3 1 &pmx0 7 3 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk2>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio3: gpio@fc809000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc809000 0x1000>; + interrupts = <0 67 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 0 3 1 &pmx0 1 3 1 &pmx0 2 3 1 + &pmx0 3 3 1 &pmx0 4 11 1 &pmx0 5 11 1 + &pmx0 6 11 1 &pmx0 7 11 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk3>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio4: gpio@fc80a000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc80a000 0x1000>; + interrupts = <0 68 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 0 11 1 &pmx0 1 11 1 &pmx0 2 11 1 + &pmx0 3 11 1 &pmx0 4 12 1 &pmx0 5 12 1 + &pmx0 6 13 1 &pmx0 7 13 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk4>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio5: gpio@fc80b000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc80b000 0x1000>; + interrupts = <0 69 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 0 14 1 &pmx0 1 15 1 &pmx0 2 16 1 + &pmx0 3 16 1 &pmx0 4 16 1 &pmx0 5 16 1 + &pmx0 6 16 1 &pmx0 7 16 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk5>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio6: gpio@fc80c000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc80c000 0x1000>; + interrupts = <0 70 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 0 16 1 &pmx0 1 16 1 &pmx0 2 17 1 + &pmx0 3 17 1 &pmx0 4 18 1 &pmx0 5 18 1 + &pmx0 6 18 1 &pmx0 7 19 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk6>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio7: gpio@fc80d000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc80d000 0x1000>; + interrupts = <0 71 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 0 19 1 &pmx0 1 20 1 &pmx0 2 21 1 + &pmx0 3 22 1 &pmx0 4 23 1 &pmx0 5 24 1 + &pmx0 6 25 1 &pmx0 7 26 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk7>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio8: gpio@fc80e000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc80e000 0x1000>; + interrupts = <0 72 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 0 27 1 &pmx0 1 28 1 &pmx0 2 29 1 + &pmx0 3 30 1 &pmx0 4 31 1 &pmx0 5 32 1 + &pmx0 6 33 1 &pmx0 7 34 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk8>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio9: gpio@fc80f000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc80f000 0x1000>; + interrupts = <0 73 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 0 35 1 &pmx0 1 36 1 &pmx0 2 37 1 + &pmx0 3 38 1 &pmx0 4 39 1 &pmx0 5 40 1 + &pmx0 6 41 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk9>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio10: gpio@fc810000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc810000 0x1000>; + interrupts = <0 74 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 2 43 1 &pmx0 3 44 1 &pmx0 4 45 1 + &pmx0 5 45 1 &pmx0 6 46 1 &pmx0 7 46 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk10>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio11: gpio@fc811000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc811000 0x1000>; + interrupts = <0 75 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 0 47 1 &pmx0 1 47 1 &pmx0 2 47 1 + &pmx0 3 47 1 &pmx0 4 47 1 &pmx0 5 48 1 + &pmx0 6 49 1 &pmx0 7 49 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk11>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio12: gpio@fc812000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc812000 0x1000>; + interrupts = <0 76 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 0 49 1 &pmx0 1 50 1 &pmx0 2 49 1 + &pmx0 3 49 1 &pmx0 4 51 1 &pmx0 5 51 1 + &pmx0 6 51 1 &pmx0 7 52 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk12>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio13: gpio@fc813000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc813000 0x1000>; + interrupts = <0 77 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 0 51 1 &pmx0 1 51 1 &pmx0 2 53 1 + &pmx0 3 53 1 &pmx0 4 53 1 &pmx0 5 54 1 + &pmx0 6 55 1 &pmx0 7 56 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk13>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio14: gpio@fc814000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc814000 0x1000>; + interrupts = <0 78 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 0 57 1 &pmx0 1 97 1 &pmx0 2 97 1 + &pmx0 3 58 1 &pmx0 4 59 1 &pmx0 5 60 1 + &pmx0 6 60 1 &pmx0 7 61 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk14>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio15: gpio@fc815000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc815000 0x1000>; + interrupts = <0 79 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 0 61 1 &pmx0 1 62 1 &pmx0 2 62 1 + &pmx0 3 63 1 &pmx0 4 63 1 &pmx0 5 64 1 + &pmx0 6 64 1 &pmx0 7 65 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk15>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio16: gpio@fc816000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc816000 0x1000>; + interrupts = <0 80 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 0 66 1 &pmx0 1 67 1 &pmx0 2 68 1 + &pmx0 3 69 1 &pmx0 4 70 1 &pmx0 5 71 1 + &pmx0 6 72 1 &pmx0 7 73 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk16>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio17: gpio@fc817000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc817000 0x1000>; + interrupts = <0 81 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 0 74 1 &pmx0 1 75 1 &pmx0 2 76 1 + &pmx0 3 77 1 &pmx0 4 78 1 &pmx0 5 79 1 + &pmx0 6 80 1 &pmx0 7 81 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk17>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio18: gpio@fc818000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc818000 0x1000>; + interrupts = <0 82 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 0 82 1 &pmx0 1 83 1 &pmx0 2 83 1 + &pmx0 3 84 1 &pmx0 4 84 1 &pmx0 5 85 1 + &pmx0 6 86 1 &pmx0 7 87 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk18>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio19: gpio@fc819000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc819000 0x1000>; + interrupts = <0 83 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 0 87 1 &pmx0 1 87 1 &pmx0 2 88 1 + &pmx0 3 88 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk19>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio20: gpio@fc81a000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc81a000 0x1000>; + interrupts = <0 84 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 0 89 1 &pmx0 1 89 1 &pmx0 2 90 1 + &pmx0 3 90 1 &pmx0 4 91 1 &pmx0 5 92 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk20>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + gpio21: gpio@fc81b000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc81b000 0x1000>; + interrupts = <0 85 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = < &pmx0 3 94 1 &pmx0 7 96 1>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&gpioclk21>; + clock-names = "apb_pclk"; + status = "disable"; + }; + + pmx0: pinmux@fc803000 { + compatible = "pinctrl-single"; + reg = <0xfc803000 0x188>; + #address-cells = <1>; + #size-cells = <1>; + #gpio-range-cells = <3>; + ranges; + + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <7>; + /* pin base, nr pins & gpio function */ + pinctrl-single,gpio-range = <&range 0 3 0 &range 3 9 1 + &range 12 1 0 &range 13 29 1 + &range 43 1 0 &range 44 49 1 + &range 94 1 1 &range 96 2 1>; + + range: gpio-range { + #pinctrl-single,gpio-range-cells = <3>; + }; + }; + + pmx1: pinmux@fc803800 { + compatible = "pinconf-single"; + reg = <0xfc803800 0x2dc>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + pinctrl-single,register-width = <32>; + }; + + i2c0: i2c@fcb08000 { + compatible = "snps,designware-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xfcb08000 0x1000>; + interrupts = <0 28 4>; + clocks = <&i2cclk0>; + status = "disabled"; + }; + + i2c1: i2c@fcb09000 { + compatible = "snps,designware-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xfcb09000 0x1000>; + interrupts = <0 29 4>; + clocks = <&i2cclk1>; + status = "disabled"; + }; + + i2c2: i2c@fcb0c000 { + compatible = "snps,designware-i2c"; + reg = <0xfcb0c000 0x1000>; + interrupts = <0 62 4>; + clocks = <&i2cclk2>; + status = "disabled"; + }; + + i2c3: i2c@fcb0d000 { + compatible = "snps,designware-i2c"; + reg = <0xfcb0d000 0x1000>; + interrupts = <0 63 4>; + clocks = <&i2cclk3>; + status = "disabled"; + }; + + /* unremovable emmc as mmcblk0 */ + dwmmc_1: dwmmc1@fcd04000 { + compatible = "hisilicon,hi4511-dw-mshc"; + reg = <0xfcd04000 0x1000>; + interrupts = <0 17 4>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&mmcclk1>, <&ddrcperclk>; + clock-names = "ciu", "biu"; + clken-reg = <0x1f8 12>; + drv-sel-reg = <0x1f8 16>; + sam-sel-reg = <0x1f8 20>; + div-reg = <0x1f8 13>; + }; + + /* sd as mmcblk1 */ + dwmmc_0: dwmmc0@fcd03000 { + compatible = "hisilicon,hi4511-dw-mshc"; + reg = <0xfcd03000 0x1000>; + interrupts = <0 16 4>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&sdclk>, <&ddrcperclk>; + clock-names = "ciu", "biu"; + clken-reg = <0x1f8 0>; + drv-sel-reg = <0x1f8 4>; + sam-sel-reg = <0x1f8 8>; + div-reg = <0x1f8 1>; + }; + + dwmmc_2: dwmmc2@fcd05000 { + compatible = "hisilicon,hi4511-dw-mshc"; + reg = <0xfcd05000 0x1000>; + interrupts = <0 18 4>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&mmcclk2>; + clken-reg = <0x1f8 24>; + drv-sel-reg = <0x1f8 28>; + sam-sel-reg = <0x1fc 0>; + div-reg = <0x1f8 25>; + }; + + dwmmc_3: dwmmc3@fcd06000 { + compatible = "hisilicon,hi4511-dw-mshc"; + reg = <0xfcd06000 0x1000>; + interrupts = <0 19 4>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&mmcclk3>; + clken-reg = <0x1fc 4>; + drv-sel-reg = <0x1fc 8>; + sam-sel-reg = <0x1fc 12>; + div-reg = <0x1f8 5>; + }; + + dma0: dma@fcd02000 { + compatible = "hisilicon,k3-dma-1.0"; + reg = <0xfcd02000 0x1000>; + #dma-cells = <1>; + dma-channels = <16>; + dma-requests = <27>; + interrupts = <0 12 4>; + clocks = <&dmaclk>; + status = "disable"; + }; + + 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"; + }; + }; +}; diff --git a/arch/arm/boot/dts/hi3716-dkb.dts b/arch/arm/boot/dts/hi3716-dkb.dts new file mode 100644 index 000000000000..91beec5f236f --- /dev/null +++ b/arch/arm/boot/dts/hi3716-dkb.dts @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013 Linaro Ltd. + * Copyright (c) 2013 Hisilicon Limited. + * + * 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 + * publishhed by the Free Software Foundation. + */ + +/dts-v1/; +/include/ "hi3716.dtsi" + +/ { + model = "Hisilicon Hi3716 Development Board"; + compatible = "hisilicon,hi3716"; + + chosen { + bootargs = "console=ttyAMA0,115200"; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,cortex-a9"; + device_type = "cpu"; + reg = <0>; + next-level-cache = <&l2>; + }; + + cpu@1 { + compatible = "arm,cortex-a9"; + device_type = "cpu"; + reg = <1>; + next-level-cache = <&l2>; + }; + }; + + memory { + device_type = "memory"; + reg = <0x00000000 0x80000000>; + }; + + soc { + amba { + timer0: timer@f8002000 { + status = "okay"; + }; + + uart0: uart@f8b00000 { + status = "okay"; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/hi3716.dtsi b/arch/arm/boot/dts/hi3716.dtsi new file mode 100644 index 000000000000..5eea43c055f5 --- /dev/null +++ b/arch/arm/boot/dts/hi3716.dtsi @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2013 Linaro Ltd. + * Copyright (c) 2013 Hisilicon Limited. + * + * 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 + * publishhed by the Free Software Foundation. + */ + +/include/ "skeleton.dtsi" + +/ { + aliases { + serial0 = &uart0; + }; + + gic: interrupt-controller@fc001000 { + compatible = "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + /* gic dist base, gic cpu base */ + reg = <0xf8a01000 0x1000>, <0xf8a00100 0x100>; + }; + + soc { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + device_type = "soc"; + interrupt-parent = <&gic>; + ranges; + + amba { + #address-cells = <1>; + #size-cells = <1>; + compatible = "arm,amba-bus"; + ranges; + + timer0: timer@f8002000 { + compatible = "arm,sp804", "arm,primecell"; + reg = <0xf8002000 0x1000>; + /* timer00 & timer01 */ + interrupts = <0 24 4>; + clocks = <&osc24m>; + status = "disabled"; + }; + + timer1: timer@f8a29000 { + /* + * Only used in NORMAL state, not available ins + * SLOW or DOZE state. + * The rate is fixed in 24MHz. + */ + compatible = "arm,sp804", "arm,primecell"; + reg = <0xf8a29000 0x1000>; + /* timer10 & timer11 */ + interrupts = <0 25 4>; + clocks = <&osc24m>; + status = "disabled"; + }; + + timer2: timer@f8a2a000 { + compatible = "arm,sp804", "arm,primecell"; + reg = <0xf8a2a000 0x1000>; + /* timer20 & timer21 */ + interrupts = <0 26 4>; + clocks = <&osc24m>; + status = "disabled"; + }; + + timer3: timer@f8a2b000 { + compatible = "arm,sp804", "arm,primecell"; + reg = <0xf8a2b000 0x1000>; + /* timer30 & timer31 */ + interrupts = <0 27 4>; + clocks = <&osc24m>; + status = "disabled"; + }; + + timer4: timer@f8a81000 { + compatible = "arm,sp804", "arm,primecell"; + reg = <0xf8a81000 0x1000>; + /* timer30 & timer31 */ + interrupts = <0 28 4>; + clocks = <&osc24m>; + status = "disabled"; + }; + + uart0: uart@f8b00000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0xf8b00000 0x1000>; + interrupts = <0 49 4>; + clocks = <&bpll_fout0_div 1>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + uart1: uart@f8006000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0xf8006000 0x1000>; + interrupts = <0 50 4>; + clocks = <&bpll_fout0_div 1>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + uart2: uart@f8b02000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0xf8b02000 0x1000>; + interrupts = <0 51 4>; + clocks = <&bpll_fout0_div 1>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + uart3: uart@f8b03000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0xf8b03000 0x1000>; + interrupts = <0 52 4>; + clocks = <&bpll_fout0_div 1>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + uart4: uart@f8b04000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0xf8b04000 0x1000>; + interrupts = <0 53 4>; + clocks = <&bpll_fout0_div 1>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + }; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + osc24m: osc { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24000000>; + clock-output-names = "osc24mhz"; + }; + + bpll: bpll { + compatible = "hisilicon,hi3716-fixed-pll"; + #clock-cells = <1>; + clocks = <&osc24m>; + clock-frequency = <1200000000>, + <600000000>, + <300000000>, + <200000000>, + <150000000>; + clock-output-names = "bpll_fout0", + "bpll_fout1", + "bpll_fout2", + "bpll_fout3", + "bpll_fout4"; + }; + + bpll_fout0_div: bpll_fout0_div {/* 1200Mhz */ + compatible = "hisilicon,hi3716-fixed-divider"; + #clock-cells = <1>; + clocks = <&bpll 0>; + div-table = <3 14 25 50>; + clock-output-names = "fout0_400m", "fout0_86m", + "fout0_48m", "fout0_24m"; + }; + + bpll_fout1_div: bpll_fout1_div { + compatible = "hisilicon,hi3716-fixed-divider"; + #clock-cells = <0>; + clocks = <&bpll 1>; + div-table = <10>; + clock-output-names = "fout1_60m"; + }; + + bpll_fout2_div: bpll_fout2_div { + compatible = "hisilicon,hi3716-fixed-divider"; + #clock-cells = <0>; + clocks = <&bpll 2>; + div-table = <4>; + clock-output-names = "fout2_75m"; + }; + + bpll_fout3_div: bpll_fout3_div { + compatible = "hisilicon,hi3716-fixed-divider"; + #clock-cells = <1>; + clocks = <&bpll 3>; + div-table = <2 4 5 8>; + clock-output-names = "fout3_100m", "fout3_50m", + "fout3_40m", "fout3_25m"; + }; + + clk_sfc_mux: clk_sfc_mux { + compatible = "hisilicon,hi3716-clk-mux"; + #clock-cells = <0>; + /* clks: 24M 75M 100M 150M 200M */ + clocks = <&osc24m>, <&bpll_fout2_div>, + <&bpll_fout3_div 0>, <&bpll 4>, <&bpll 3>; + + /* offset mux_shift mux_width */ + mux-reg = <0x5c 8 3>; + /* mux reg value to choose clks */ + mux-table = <0 7 6 4 5>; + + clock-output-names = "sfc_mux"; + }; + + clk_sfc: clk_sfc { + compatible = "hisilicon,hi3716-clk-gate"; + #clock-cells = <0>; + clocks = <&clk_sfc_mux>; + + /* offset, enable, reset */ + gate-reg = <0x5c 0 4>; + + clock-output-names = "sfc"; + }; + }; + + local_timer@f8a00600 { + compatible = "arm,cortex-a9-twd-timer"; + reg = <0xf8a00600 0x20>; + interrupts = <1 13 0xf01>; + }; + + l2: l2-cache { + compatible = "arm,pl310-cache"; + reg = <0xf8a10000 0x100000>; + interrupts = <0 15 4>; + cache-unified; + cache-level = <2>; + hisilicon,l2cache-aux = <0x00050000 0xfff0ffff>; + }; + + sctrl@f8000000 { + compatible = "hisilicon,sctrl"; + reg = <0xf8000000 0x1000>; + smp_reg = <0xc0>; + reboot_reg = <0x4>; + }; + + clkbase@f8a22000 { + compatible = "hisilicon,clkbase"; + reg = <0xf8a22000 0x1000>; + }; + + cpuctrl@f8a22000 { + compatible = "hisilicon,cpuctrl"; + reg = <0xf8a22000 0x2000>; + }; + }; +}; diff --git a/arch/arm/boot/dts/hi4511.dts b/arch/arm/boot/dts/hi4511.dts new file mode 100644 index 000000000000..c16690fd50c7 --- /dev/null +++ b/arch/arm/boot/dts/hi4511.dts @@ -0,0 +1,1477 @@ +/* + * Copyright (C) 2012-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 version 2 as + * publishhed by the Free Software Foundation. + */ + +/dts-v1/; +/include/ "hi3620.dtsi" + +/ { + model = "Hisilicon Hi4511 Development Board"; + compatible = "hisilicon,hi3620-hi4511"; + + chosen { + bootargs = "console=ttyAMA0,115200 root=/dev/ram0 mem=512m earlyprintk no_console_suspend"; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,cortex-a9"; + device_type = "cpu"; + reg = <0>; + next-level-cache = <&l2>; + }; + + cpu@1 { + compatible = "arm,cortex-a9"; + device_type = "cpu"; + reg = <1>; + next-level-cache = <&l2>; + }; + + cpu@2 { + compatible = "arm,cortex-a9"; + device_type = "cpu"; + reg = <2>; + next-level-cache = <&l2>; + }; + + cpu@3 { + compatible = "arm,cortex-a9"; + device_type = "cpu"; + reg = <3>; + next-level-cache = <&l2>; + }; + }; + + memory { + reg = <0x00000000 0x20000000>; + }; + + amba { + dual_timer0: dual_timer@fc800000 { + status = "ok"; + }; + + dma0: dma@fcd02000 { + status = "ok"; + }; + + uart0: uart@fcb00000 { /* console */ + /* + pinctrl-names = "default", "idle"; + pinctrl-0 = <&uart0_pmx_func &uart0_cfg_func>; + pinctrl-1 = <&uart0_pmx_idle &uart0_cfg_idle>; + */ + status = "ok"; + }; + + uart1: uart@fcb01000 { /* modem */ + pinctrl-names = "default", "idle"; + pinctrl-0 = <&uart1_pmx_func &uart1_cfg_func>; + pinctrl-1 = <&uart1_pmx_idle &uart1_cfg_idle>; + status = "ok"; + }; + + uart2: uart@fcb02000 { /* audience */ + pinctrl-names = "default", "idle"; + pinctrl-0 = <&uart2_pmx_func &uart2_cfg_func>; + pinctrl-1 = <&uart2_pmx_idle &uart2_cfg_idle>; + status = "ok"; + }; + + uart3: uart@fcb03000 { + pinctrl-names = "default", "idle"; + pinctrl-0 = <&uart3_pmx_func &uart3_cfg_func>; + pinctrl-1 = <&uart3_pmx_idle &uart3_cfg_idle>; + status = "ok"; + }; + + uart4: uart@fcb04000 { + pinctrl-names = "default", "idle"; + pinctrl-0 = <&uart4_pmx_func &uart4_cfg_func>; + pinctrl-1 = <&uart4_pmx_idle &uart4_cfg_func>; + status = "ok"; + }; + + rtc0: rtc@fc804000 { + status = "ok"; + }; + + gpio0: gpio@fc806000 { + status = "ok"; + }; + + gpio1: gpio@fc807000 { + status = "ok"; + }; + + gpio2: gpio@fc808000 { + status = "ok"; + }; + + gpio3: gpio@fc809000 { + status = "ok"; + }; + + gpio4: gpio@fc80a000 { + status = "ok"; + }; + + gpio5: gpio@fc80b000 { + status = "ok"; + }; + + gpio6: gpio@fc80c000 { + status = "ok"; + }; + + gpio7: gpio@fc80d000 { + status = "ok"; + }; + + gpio8: gpio@fc80e000 { + status = "ok"; + }; + + gpio9: gpio@fc80f000 { + status = "ok"; + }; + + gpio10: gpio@fc810000 { + status = "ok"; + }; + + gpio11: gpio@fc811000 { + status = "ok"; + }; + + gpio12: gpio@fc812000 { + status = "ok"; + }; + + gpio13: gpio@fc813000 { + status = "ok"; + }; + + gpio14: gpio@fc814000 { + status = "ok"; + }; + + gpio15: gpio@fc815000 { + status = "ok"; + }; + + gpio16: gpio@fc816000 { + status = "ok"; + }; + + gpio17: gpio@fc817000 { + status = "ok"; + }; + + gpio18: gpio@fc818000 { + status = "ok"; + }; + + gpio19: gpio@fc819000 { + status = "ok"; + }; + + gpio20: gpio@fc81a000 { + status = "ok"; + }; + + gpio21: gpio@fc81b000 { + status = "ok"; + }; + + gpio-keys { + compatible = "gpio-keys"; + + call { + label = "call"; + gpios = <&gpio17 2 0>; + linux,code = <169>; /* KEY_PHONE */ + }; + }; + + pmx0: pinmux@fc803000 { + pinctrl-names = "default"; + pinctrl-0 = <&board_pmx_pins>; + + board_pmx_pins: pinmux_board_pmx_pins { + pinctrl-single,pins = < + 0x008 0x0 /* GPIO -- eFUSE_DOUT */ + 0x100 0x0 /* USIM_CLK & USIM_DATA (IOMG63) */ + >; + }; + sd_pmx_pins: pinmux_sd_pins { + pinctrl-single,pins = < + 0x0bc 0x0 /* SD_CLK, SD_CMD, SD_DATA[0:2] */ + 0x0c0 0x0 /* SD_DATA[3] */ + >; + }; + uart0_pmx_func: pinmux_uart0_func { + pinctrl-single,pins = < + 0x0f0 0x0 + 0x0f4 0x0 /* UART0_RX & UART0_TX */ + >; + }; + uart0_pmx_idle: pinmux_uart0_idle { + pinctrl-single,pins = < + /*0x0f0 0x1*/ /* UART0_CTS & UART0_RTS */ + 0x0f4 0x1 /* UART0_RX & UART0_TX */ + >; + }; + uart1_pmx_func: pinmux_uart1_func { + pinctrl-single,pins = < + 0x0f8 0x0 /* UART1_CTS & UART1_RTS (IOMG61) */ + 0x0fc 0x0 /* UART1_RX & UART1_TX (IOMG62) */ + >; + }; + uart1_pmx_idle: pinmux_uart1_idle { + pinctrl-single,pins = < + 0x0f8 0x1 /* GPIO (IOMG61) */ + 0x0fc 0x1 /* GPIO (IOMG62) */ + >; + }; + uart2_pmx_func: pinmux_uart2_func { + pinctrl-single,pins = < + 0x104 0x2 /* UART2_RXD (IOMG96) */ + 0x108 0x2 /* UART2_TXD (IOMG64) */ + >; + }; + uart2_pmx_idle: pinmux_uart2_idle { + pinctrl-single,pins = < + 0x104 0x1 /* GPIO (IOMG96) */ + 0x108 0x1 /* GPIO (IOMG64) */ + >; + }; + uart3_pmx_func: pinmux_uart3_func { + pinctrl-single,pins = < + 0x160 0x2 /* UART3_CTS & UART3_RTS (IOMG85) */ + 0x164 0x2 /* UART3_RXD & UART3_TXD (IOMG86) */ + >; + }; + uart3_pmx_idle: pinmux_uart3_idle { + pinctrl-single,pins = < + 0x160 0x1 /* GPIO (IOMG85) */ + 0x164 0x1 /* GPIO (IOMG86) */ + >; + }; + uart4_pmx_func: pinmux_uart4_func { + pinctrl-single,pins = < + 0x168 0x0 /* UART4_CTS & UART4_RTS (IOMG87) */ + 0x16c 0x0 /* UART4_RXD (IOMG88) */ + 0x170 0x0 /* UART4_TXD (IOMG93) */ + >; + }; + uart4_pmx_idle: pinmux_uart4_idle { + pinctrl-single,pins = < + 0x168 0x1 /* GPIO (IOMG87) */ + 0x16c 0x1 /* GPIO (IOMG88) */ + 0x170 0x1 /* GPIO (IOMG93) */ + >; + }; + i2c0_pmx_func: pinmux_i2c0_func { + pinctrl-single,pins = < + 0x0b4 0x0 /* I2C0_SCL & I2C0_SDA (IOMG45) */ + >; + }; + i2c0_pmx_idle: pinmux_i2c0_idle { + pinctrl-single,pins = < + 0x0b4 0x1 /* GPIO (IOMG45) */ + >; + }; + i2c1_pmx_func: pinmux_i2c1_func { + pinctrl-single,pins = < + 0x0b8 0x0 /* I2C1_SCL & I2C1_SDA (IOMG46) */ + >; + }; + i2c1_pmx_idle: pinmux_i2c1_idle { + pinctrl-single,pins = < + 0x0b8 0x1 /* GPIO (IOMG46) */ + >; + }; + i2c2_pmx_func: pinmux_i2c2_func { + pinctrl-single,pins = < + 0x068 0x0 /* I2C2_SCL (IOMG26) */ + 0x06c 0x0 /* I2C2_SDA (IOMG27) */ + >; + }; + i2c2_pmx_idle: pinmux_i2c2_idle { + pinctrl-single,pins = < + 0x068 0x1 /* GPIO (IOMG26) */ + 0x06c 0x1 /* GPIO (IOMG27) */ + >; + }; + i2c3_pmx_func: pinmux_i2c3_func { + pinctrl-single,pins = < + 0x050 0x2 /* I2C3_SCL (IOMG20) */ + 0x054 0x2 /* I2C3_SDA (IOMG21) */ + >; + }; + i2c3_pmx_idle: pinmux_i2c3_idle { + pinctrl-single,pins = < + 0x050 0x1 /* GPIO (IOMG20) */ + 0x054 0x1 /* GPIO (IOMG21) */ + >; + }; + spi0_pmx_func: pinmux_spi0_func { + pinctrl-single,pins = < + 0x0d4 0x0 /* SPI0_CLK/SPI0_DI/SPI0_DO (IOMG53) */ + 0x0d8 0x0 /* SPI0_CS0 (IOMG54) */ + 0x0dc 0x0 /* SPI0_CS1 (IOMG55) */ + 0x0e0 0x0 /* SPI0_CS2 (IOMG56) */ + 0x0e4 0x0 /* SPI0_CS3 (IOMG57) */ + >; + }; + spi0_pmx_idle: pinmux_spi0_idle { + pinctrl-single,pins = < + 0x0d4 0x1 /* GPIO (IOMG53) */ + 0x0d8 0x1 /* GPIO (IOMG54) */ + 0x0dc 0x1 /* GPIO (IOMG55) */ + 0x0e0 0x1 /* GPIO (IOMG56) */ + 0x0e4 0x1 /* GPIO (IOMG57) */ + >; + }; + spi1_pmx_func: pinmux_spi1_func { + pinctrl-single,pins = < + 0x184 0x0 /* SPI1_CLK/SPI1_DI (IOMG98) */ + 0x0e8 0x0 /* SPI1_DO (IOMG58) */ + 0x0ec 0x0 /* SPI1_CS (IOMG95) */ + >; + }; + spi1_pmx_idle: pinmux_spi1_idle { + pinctrl-single,pins = < + 0x184 0x1 /* GPIO (IOMG98) */ + 0x0e8 0x1 /* GPIO (IOMG58) */ + 0x0ec 0x1 /* GPIO (IOMG95) */ + >; + }; + kpc_pmx_func: pinmux_kpc_func { + pinctrl-single,pins = < + 0x12c 0x0 /* KEY_IN0 (IOMG73) */ + 0x130 0x0 /* KEY_IN1 (IOMG74) */ + 0x134 0x0 /* KEY_IN2 (IOMG75) */ + 0x10c 0x0 /* KEY_OUT0 (IOMG65) */ + 0x110 0x0 /* KEY_OUT1 (IOMG66) */ + 0x114 0x0 /* KEY_OUT2 (IOMG67) */ + >; + }; + gpio_key_func: pinmux_gpiokey_func { + pinctrl-single,pins = < + 0x10c 0x1 /* KEY_OUT0/GPIO (IOMG65) */ + 0x130 0x1 /* KEY_IN1/GPIO (IOMG74) */ + >; + }; + emmc_pmx_func: pinmux_emmc_pins@0 { + pinctrl-single,pins = < + 0x030 0x2 /* eMMC_CMD/eMMC_CLK (IOMG12) */ + 0x018 0x0 /* NAND_CS3_N (IOMG6) */ + 0x024 0x0 /* NAND_BUSY2_N (IOMG8) */ + 0x028 0x0 /* NAND_BUSY3_N (IOMG9) */ + 0x02c 0x2 /* eMMC_DATA[0:7] (IOMG10) */ + >; + }; + emmc_pmx_idle: pinmux_emmc_pins@1 { + pinctrl-single,pins = < + 0x030 0x0 /* GPIO (IOMG12) */ + 0x018 0x1 /* GPIO (IOMG6) */ + 0x024 0x1 /* GPIO (IOMG8) */ + 0x028 0x1 /* GPIO (IOMG9) */ + 0x02c 0x1 /* GPIO (IOMG10) */ + >; + }; + sd_pmx_func: pinmux_sd_pins@0 { + pinctrl-single,pins = < + 0x0bc 0x0 /* SD_CLK/SD_CMD/SD_DATA0/SD_DATA1/SD_DATA2 (IOMG47) */ + 0x0c0 0x0 /* SD_DATA3 (IOMG48) */ + >; + }; + sd_pmx_idle: pinmux_sd_pins@1 { + pinctrl-single,pins = < + 0x0bc 0x1 /* GPIO (IOMG47) */ + 0x0c0 0x1 /* GPIO (IOMG48) */ + >; + }; + nand_pmx_func: pinmux_nand_func { + pinctrl-single,pins = < + 0x00c 0x0 /* NAND_ALE/NAND_CLE/.../NAND_DATA[0:7] (IOMG3) */ + 0x010 0x0 /* NAND_CS1_N (IOMG4) */ + 0x014 0x0 /* NAND_CS2_N (IOMG5) */ + 0x018 0x0 /* NAND_CS3_N (IOMG6) */ + 0x01c 0x0 /* NAND_BUSY0_N (IOMG94) */ + 0x020 0x0 /* NAND_BUSY1_N (IOMG7) */ + 0x024 0x0 /* NAND_BUSY2_N (IOMG8) */ + 0x028 0x0 /* NAND_BUSY3_N (IOMG9) */ + 0x02c 0x0 /* NAND_DATA[8:15] (IOMG10) */ + >; + }; + nand_pmx_idle: pinmux_nand_idle { + pinctrl-single,pins = < + 0x00c 0x1 /* GPIO (IOMG3) */ + 0x010 0x1 /* GPIO (IOMG4) */ + 0x014 0x1 /* GPIO (IOMG5) */ + 0x018 0x1 /* GPIO (IOMG6) */ + 0x01c 0x1 /* GPIO (IOMG94) */ + 0x020 0x1 /* GPIO (IOMG7) */ + 0x024 0x1 /* GPIO (IOMG8) */ + 0x028 0x1 /* GPIO (IOMG9) */ + 0x02c 0x1 /* GPIO (IOMG10) */ + >; + }; + sdio_pmx_func: pinmux_sdio_func { + pinctrl-single,pins = < + 0x0c4 0x0 /* SDIO_CLK/SDIO_CMD/SDIO_DATA[0:3] (IOMG49) */ + >; + }; + sdio_pmx_idle: pinmux_sdio_idle { + pinctrl-single,pins = < + 0x0c4 0x1 /* GPIO (IOMG49) */ + >; + }; + audio_out_pmx_func: pinmux_audio_func { + pinctrl-single,pins = < + 0x0f0 0x1 /* GPIO (IOMG59), audio spk & earphone */ + >; + }; + pwm0_pmx_func: pinmux_pwm0_func { + pinctrl-single,pins = < + 0x154 0x0 /* PWM0 (IOMG82) */ + >; + }; + pwm0_pmx_idle: pinmux_pwm0_idle { + pinctrl-single,pins = < + 0x154 0x1 /* GPIO149 (IOMG82) */ + >; + }; + pwm1_pmx_func: pinmux_pwm1_func { + pinctrl-single,pins = < + 0x158 0x0 /* PWM1 (IOMG83) */ + >; + }; + pwm1_pmx_idle: pinmux_pwm1_idle { + pinctrl-single,pins = < + 0x158 0x1 /* GPIO150 (IOMG83) */ + >; + }; + + }; + + pmx1: pinmux@fc803800 { + pinctrl-names = "default"; + pinctrl-0 = < &board_pu_pins &board_pd_pins &board_pd_ps_pins + &board_np_pins &board_ps_pins &kpc_cfg_func + &audio_out_cfg_func>; + board_pu_pins: pinmux_board_pu_pins { + pinctrl-single,pins = < + 0x014 0 /* GPIO_158 (IOCFG2) */ + 0x01c 0 /* BOOT_MODE0 (IOCFG4) */ + 0x020 0 /* BOOT_MODE1 (IOCFG5) */ + >; + pinctrl-single,bias-pulldown = <0 2 0 2>; + pinctrl-single,bias-pullup = <1 1 0 1>; + }; + board_pd_pins: pinmux_board_pd_pins { + pinctrl-single,pins = < + 0x038 0 /* eFUSE_DOUT (IOCFG11) */ + 0x150 0 /* ISP_GPIO8 (IOCFG93) */ + 0x154 0 /* ISP_GPIO9 (IOCFG94) */ + >; + pinctrl-single,bias-pulldown = <2 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + }; + board_pd_ps_pins: pinmux_board_pd_ps_pins { + pinctrl-single,pins = < + 0x2d8 0 /* CLK_OUT0 (IOCFG190) */ + 0x004 0 /* PMU_SPI_DATA (IOCFG192) */ + >; + pinctrl-single,bias-pulldown = <2 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + pinctrl-single,drive-strength = <0x30 0xf0>; + }; + board_np_pins: pinmux_board_np_pins { + pinctrl-single,pins = < + 0x24c 0 /* KEYPAD_OUT7 (IOCFG155) */ + >; + pinctrl-single,bias-pulldown = <0 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + }; + board_ps_pins: pinmux_board_ps_pins { + pinctrl-single,pins = < + 0x000 0 /* PMU_SPI_CLK (IOCFG191) */ + 0x008 0 /* PMU_SPI_CS_N (IOCFG193) */ + >; + pinctrl-single,drive-strength = <0x30 0xf0>; + }; + uart0_cfg_func: pincfg_uart0_func { + pinctrl-single,pins = < + 0x208 0 /* UART0_RXD (IOCFG138) */ + 0x20c 0 /* UART0_TXD (IOCFG139) */ + >; + pinctrl-single,bias-pulldown = <0 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + }; + uart0_cfg_idle: pincfg_uart0_idle { + pinctrl-single,pins = < + 0x208 0 /* UART0_RXD (IOCFG138) */ + 0x20c 0 /* UART0_TXD (IOCFG139) */ + >; + pinctrl-single,bias-pulldown = <2 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + }; + uart1_cfg_func: pincfg_uart1_func { + pinctrl-single,pins = < + 0x210 0 /* UART1_CTS (IOCFG140) */ + 0x214 0 /* UART1_RTS (IOCFG141) */ + 0x218 0 /* UART1_RXD (IOCFG142) */ + 0x21c 0 /* UART1_TXD (IOCFG143) */ + >; + pinctrl-single,bias-pulldown = <0 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + }; + uart1_cfg_idle: pincfg_uart1_idle { + pinctrl-single,pins = < + 0x210 0 /* UART1_CTS (IOCFG140) */ + 0x214 0 /* UART1_RTS (IOCFG141) */ + 0x218 0 /* UART1_RXD (IOCFG142) */ + 0x21c 0 /* UART1_TXD (IOCFG143) */ + >; + pinctrl-single,bias-pulldown = <2 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + }; + uart2_cfg_func: pincfg_uart2_func { + pinctrl-single,pins = < + 0x220 0 /* UART2_CTS (IOCFG144) */ + 0x224 0 /* UART2_RTS (IOCFG145) */ + 0x228 0 /* UART2_RXD (IOCFG146) */ + 0x22c 0 /* UART2_TXD (IOCFG147) */ + >; + pinctrl-single,bias-pulldown = <0 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + }; + uart2_cfg_idle: pincfg_uart2_idle { + pinctrl-single,pins = < + 0x220 0 /* GPIO (IOCFG144) */ + 0x224 0 /* GPIO (IOCFG145) */ + 0x228 0 /* GPIO (IOCFG146) */ + 0x22c 0 /* GPIO (IOCFG147) */ + >; + pinctrl-single,bias-pulldown = <2 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + }; + uart3_cfg_func: pincfg_uart3_func { + pinctrl-single,pins = < + 0x294 0 /* UART3_CTS (IOCFG173) */ + 0x298 0 /* UART3_RTS (IOCFG174) */ + 0x29c 0 /* UART3_RXD (IOCFG175) */ + 0x2a0 0 /* UART3_TXD (IOCFG176) */ + >; + pinctrl-single,bias-pulldown = <0 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + }; + uart3_cfg_idle: pincfg_uart3_idle { + pinctrl-single,pins = < + 0x294 0 /* UART3_CTS (IOCFG173) */ + 0x298 0 /* UART3_RTS (IOCFG174) */ + 0x29c 0 /* UART3_RXD (IOCFG175) */ + 0x2a0 0 /* UART3_TXD (IOCFG176) */ + >; + pinctrl-single,bias-pulldown = <2 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + }; + uart4_cfg_func: pincfg_uart4_func { + pinctrl-single,pins = < + 0x2a4 0 /* UART4_CTS (IOCFG177) */ + 0x2a8 0 /* UART4_RTS (IOCFG178) */ + 0x2ac 0 /* UART4_RXD (IOCFG179) */ + 0x2b0 0 /* UART4_TXD (IOCFG180) */ + >; + pinctrl-single,bias-pulldown = <0 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + }; + i2c0_cfg_func: pincfg_i2c0_func { + pinctrl-single,pins = < + 0x17c 0 /* I2C0_SCL (IOCFG103) */ + 0x180 0 /* I2C0_SDA (IOCFG104) */ + >; + pinctrl-single,bias-pulldown = <0 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + pinctrl-single,drive-strength = <0x30 0xf0>; + }; + i2c1_cfg_func: pincfg_i2c1_func { + pinctrl-single,pins = < + 0x184 0 /* I2C1_SCL (IOCFG105) */ + 0x188 0 /* I2C1_SDA (IOCFG106) */ + >; + pinctrl-single,bias-pulldown = <0 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + pinctrl-single,drive-strength = <0x30 0xf0>; + }; + i2c2_cfg_func: pincfg_i2c2_func { + pinctrl-single,pins = < + 0x118 0 /* I2C2_SCL (IOCFG79) */ + 0x11c 0 /* I2C2_SDA (IOCFG80) */ + >; + pinctrl-single,bias-pulldown = <0 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + pinctrl-single,drive-strength = <0x30 0xf0>; + }; + i2c3_cfg_func: pincfg_i2c3_func { + pinctrl-single,pins = < + 0x100 0 /* I2C3_SCL (IOCFG73) */ + 0x104 0 /* I2C3_SDA (IOCFG74) */ + >; + pinctrl-single,bias-pulldown = <0 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + pinctrl-single,drive-strength = <0x30 0xf0>; + }; + spi0_cfg_func1: pincfg_spi0_f1 { + pinctrl-single,pins = < + 0x1d4 0 /* SPI0_CLK (IOCFG125) */ + 0x1d8 0 /* SPI0_DI (IOCFG126) */ + 0x1dc 0 /* SPI0_DO (IOCFG127) */ + >; + pinctrl-single,bias-pulldown = <2 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + pinctrl-single,drive-strength = <0x30 0xf0>; + }; + spi0_cfg_func2: pincfg_spi0_f2 { + pinctrl-single,pins = < + 0x1e0 0 /* SPI0_CS0 (IOCFG128) */ + 0x1e4 0 /* SPI0_CS1 (IOCFG129) */ + 0x1e8 0 /* SPI0_CS2 (IOCFG130 */ + 0x1ec 0 /* SPI0_CS3 (IOCFG131) */ + >; + pinctrl-single,bias-pulldown = <0 2 0 2>; + pinctrl-single,bias-pullup = <1 1 0 1>; + pinctrl-single,drive-strength = <0x30 0xf0>; + }; + spi1_cfg_func1: pincfg_spi1_f1 { + pinctrl-single,pins = < + 0x1f0 0 /* SPI1_CLK (IOCFG132) */ + 0x1f4 0 /* SPI1_DI (IOCFG133) */ + 0x1f8 0 /* SPI1_DO (IOCFG134) */ + >; + pinctrl-single,bias-pulldown = <2 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + pinctrl-single,drive-strength = <0x30 0xf0>; + }; + spi1_cfg_func2: pincfg_spi1_f2 { + pinctrl-single,pins = < + 0x1fc 0 /* SPI1_CS (IOCFG135) */ + >; + pinctrl-single,bias-pulldown = <0 2 0 2>; + pinctrl-single,bias-pullup = <1 1 0 1>; + pinctrl-single,drive-strength = <0x30 0xf0>; + }; + kpc_cfg_func: pincfg_kpc_func { + pinctrl-single,pins = < + 0x250 0 /* KEY_IN0 (IOCFG156) */ + 0x254 0 /* KEY_IN1 (IOCFG157) */ + 0x258 0 /* KEY_IN2 (IOCFG158) */ + 0x230 0 /* KEY_OUT0 (IOCFG148) */ + 0x234 0 /* KEY_OUT1 (IOCFG149) */ + 0x238 0 /* KEY_OUT2 (IOCFG150) */ + >; + pinctrl-single,bias-pulldown = <2 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + }; + emmc_cfg_func: pincfg_emmc_func { + pinctrl-single,pins = < + 0x0ac 0 /* eMMC_CMD (IOCFG40) */ + 0x08c 0 /* NAND_DATA8 (IOCFG32) */ + 0x090 0 /* NAND_DATA9 (IOCFG33) */ + 0x094 0 /* NAND_DATA10 (IOCFG34) */ + 0x098 0 /* NAND_DATA11 (IOCFG35) */ + 0x09c 0 /* NAND_DATA12 (IOCFG36) */ + 0x0a0 0 /* NAND_DATA13 (IOCFG37) */ + 0x0a4 0 /* NAND_DATA14 (IOCFG38) */ + 0x0a8 0 /* NAND_DATA15 (IOCFG39) */ + >; + pinctrl-single,bias-pulldown = <0 2 0 2>; + pinctrl-single,bias-pullup = <1 1 0 1>; + pinctrl-single,drive-strength = <0x30 0xf0>; + }; + emmc_cfg_clk_func: pincfg_emmc_clk_func { + pinctrl-single,pins = < + 0x0b0 0 /* eMMC_CLK (IOCFG41) */ + >; + pinctrl-single,bias-pulldown = <0 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + pinctrl-single,drive-strength = <0x30 0xf0>; + }; + sd_cfg_func1: pincfg_sd_f1 { + pinctrl-single,pins = < + 0x18c 0 /* SD_CLK (IOCFG107) */ + >; + pinctrl-single,bias-pulldown = <0 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + pinctrl-single,drive-strength = <0x30 0xf0>; + }; + sd_cfg_func2: pincfg_sd_f2 { + pinctrl-single,pins = < + 0x190 0 /* SD_CMD (IOCFG108) */ + 0x194 0 /* SD_DATA0 (IOCFG109) */ + 0x198 0 /* SD_DATA1 (IOCFG110) */ + 0x19c 0 /* SD_DATA2 (IOCFG111) */ + 0x1a0 0 /* SD_DATA3 (IOCFG112) */ + >; + pinctrl-single,bias-pulldown = <0 2 0 2>; + pinctrl-single,bias-pullup = <1 1 0 1>; + pinctrl-single,drive-strength = <0x70 0xf0>; + }; + nand_cfg_func1: pincfg_nand_f1 { + pinctrl-single,pins = < + 0x03c 0 /* NAND_ALE (IOCFG12) */ + 0x040 0 /* NAND_CLE (IOCFG13) */ + 0x06c 0 /* NAND_DATA0 (IOCFG24) */ + 0x070 0 /* NAND_DATA1 (IOCFG25) */ + 0x074 0 /* NAND_DATA2 (IOCFG26) */ + 0x078 0 /* NAND_DATA3 (IOCFG27) */ + 0x07c 0 /* NAND_DATA4 (IOCFG28) */ + 0x080 0 /* NAND_DATA5 (IOCFG29) */ + 0x084 0 /* NAND_DATA6 (IOCFG30) */ + 0x088 0 /* NAND_DATA7 (IOCFG31) */ + 0x08c 0 /* NAND_DATA8 (IOCFG32) */ + 0x090 0 /* NAND_DATA9 (IOCFG33) */ + 0x094 0 /* NAND_DATA10 (IOCFG34) */ + 0x098 0 /* NAND_DATA11 (IOCFG35) */ + 0x09c 0 /* NAND_DATA12 (IOCFG36) */ + 0x0a0 0 /* NAND_DATA13 (IOCFG37) */ + 0x0a4 0 /* NAND_DATA14 (IOCFG38) */ + 0x0a8 0 /* NAND_DATA15 (IOCFG39) */ + >; + pinctrl-single,bias-pulldown = <2 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + pinctrl-single,drive-strength = <0x30 0xf0>; + }; + nand_cfg_func2: pincfg_nand_f2 { + pinctrl-single,pins = < + 0x044 0 /* NAND_RE_N (IOCFG14) */ + 0x048 0 /* NAND_WE_N (IOCFG15) */ + 0x04c 0 /* NAND_CS0_N (IOCFG16) */ + 0x050 0 /* NAND_CS1_N (IOCFG17) */ + 0x054 0 /* NAND_CS2_N (IOCFG18) */ + 0x058 0 /* NAND_CS3_N (IOCFG19) */ + 0x05c 0 /* NAND_BUSY0_N (IOCFG20) */ + 0x060 0 /* NAND_BUSY1_N (IOCFG21) */ + 0x064 0 /* NAND_BUSY2_N (IOCFG22) */ + 0x068 0 /* NAND_BUSY3_N (IOCFG23) */ + >; + pinctrl-single,bias-pulldown = <0 2 0 2>; + pinctrl-single,bias-pullup = <1 1 0 1>; + pinctrl-single,drive-strength = <0x30 0xf0>; + }; + sdio_cfg_func: pincfg_sdio_func { + pinctrl-single,pins = < + 0x1a4 0 /* SDIO0_CLK (IOCG113) */ + 0x1a8 0 /* SDIO0_CMD (IOCG114) */ + 0x1ac 0 /* SDIO0_DATA0 (IOCG115) */ + 0x1b0 0 /* SDIO0_DATA1 (IOCG116) */ + 0x1b4 0 /* SDIO0_DATA2 (IOCG117) */ + 0x1b8 0 /* SDIO0_DATA3 (IOCG118) */ + >; + pinctrl-single,bias-pulldown = <2 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + pinctrl-single,drive-strength = <0x30 0xf0>; + }; + audio_out_cfg_func: pincfg_audio_func { + pinctrl-single,pins = < + 0x200 0 /* GPIO (IOCFG136) */ + 0x204 0 /* GPIO (IOCFG137) */ + >; + pinctrl-single,bias-pulldown = <2 2 0 2>; + pinctrl-single,bias-pullup = <0 1 0 1>; + }; + pmic_int_cfg_func: pincfg_pmic_func { + pinctrl-single,pins = < + 0x018 0 /* GPIO159 (IOCFG003) */ + >; + }; + /* TP_IRQ need pullup */ + ts_pin_cfg: pincfg_ts_func { + pinctrl-single,pins = < + 0x010 0 /* GPIO157 (TP_IRQ) */ + >; + pinctrl-single,bias-pulldown = <0 2 0 2>; + pinctrl-single,bias-pullup = <1 1 0 1>; + }; + pwm0_cfg_func: pincfg_pwm0_func { + 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_func { + 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>; + }; + pmic_int_cfg_func: pincfg_pmic_func { + pinctrl-single,pins = < + 0x018 0 /* GPIO159 (IOCFG003) */ + >; + }; + }; + + i2c0: i2c@fcb08000 { + status = "ok"; + pinctrl-names = "default", "idle"; + pinctrl-0 = <&i2c0_pmx_func &i2c0_cfg_func>; + pinctrl-1 = <&i2c0_pmx_idle &i2c0_cfg_func>; + }; + + i2c1: i2c@fcb09000 { + status = "ok"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pmx_func &i2c1_cfg_func>; + ts_mxt224e: ts@4a { + compatible = "atmel,ts-mxt224e"; + reg = <0x4a>; + ldo-supply = <&ldo6>; + pinctrl-names = "default"; + pinctrl-0 = <&ts_pin_cfg>; + atmel-ts,gpio-irq = <&gpio19 5 0>; + atmel-ts,gpio-reset = <&gpio19 4 0>; + /* min max: x y pressure width */ + atmel-ts,abs = <0 719 0 1279 0 255 0 255>; + atmel-ts,cfg_t6 = /bits/ 8 <0 0 0 0 0 0>; + atmel-ts,cfg_t7 = /bits/ 8 <32 255 10>; + atmel-ts,cfg_t8 = /bits/ 8 <24 0 1 10 0 0 5 60 10 192>; + atmel-ts,cfg_t9 = /bits/ 8 <143 0 0 19 11 0 32 66 2 3 0 2 2 47 10 15 22 10 106 5 + 207 2 0 0 0 0 161 40 183 64 30 20 0 0 1>; + atmel-ts,cfg_t15 = /bits/ 8 <0 0 0 0 0 0 0 0 0 0 0>; + atmel-ts,cfg_t19 = /bits/ 8 <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>; + atmel-ts,cfg_t23 = /bits/ 8 <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>; + atmel-ts,cfg_t25 = /bits/ 8 <0 0 0 0 0 0 0 0 0 0 0 0 0 0>; + atmel-ts,cfg_t40 = /bits/ 8 <0 0 0 0 0>; + atmel-ts,cfg_t42 = /bits/ 8 <0 40 40 80 128 0 0 0>; + atmel-ts,cfg_t46 = /bits/ 8 <0 3 32 32 0 0 0 0 0>; + atmel-ts,cfg_t47 = /bits/ 8 <0 20 50 5 2 40 40 180 0 100>; + atmel-ts,cfg_t48 = /bits/ 8 <1 4 10 0 0 0 0 0 1 1 0 0 0 6 6 0 0 63 6 64 + 10 0 20 5 0 38 0 20 0 0 0 0 0 0 0 40 2 2 2 32 + 10 12 20 241 251 0 0 191 40 183 64 30 15 0>; + atmel-ts,object_crc = /bits/ 8 <0xFD 0x3B 0x8D>; + atmel-ts,cable_config = /bits/ 8 <70 30 32 32>; + atmel-ts,cable_config_t7 = /bits/ 8 <32 16 25>; + atmel-ts,cable_config_t8 = /bits/ 8 <24 0 5 5 0 0 5 60 10 192>; + atmel-ts,cable_config_T9 = /bits/ 8 <139 0 0 19 11 0 32 66 2 3 0 5 2 64 10 + 12 20 10 106 5 207 2 0 0 0 0 161 40 183 64 30 20 0 0 0>; + atmel-ts,cable_config_t46 = /bits/ 8 <0 3 40 40 0 0 0 0 0>; + atmel-ts,cable_config_t48 = /bits/ 8 <1 128 114 0 0 0 0 0 1 2 0 0 0 6 6 + 0 0 63 6 64 10 0 20 5 0 38 0 20 0 0 0 0 0 0 0 + 40 2 2 2 32 10 12 20 241 251 0 0 191 40 183 64 30 15 0>; + atmel-ts,noise_config = /bits/ 8 <70 3 35>; + atmel-ts,filter_level = /bits/ 16 <0 0 539 539>; + atmel-ts,gcaf_level = /bits/ 8 <8 16 24 32 40>; + atmel-ts,atch_nor = /bits/ 8 <0 0 5 60 10 192>; + atmel-ts,atch_nor_20s = /bits/ 8 <0 0 255 1 0 0>; + }; + }; + + dwmmc1@fcd04000 { + num-slots = <1>; + vmmc-supply = <&ldo0>; + vqmmc-supply = <&ldo5>; + /* emmc fifo register value is incorrect */ + fifo-depth = <0x100>; + broken-cd; + supports-highspeed; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_pmx_func &emmc_cfg_func &emmc_cfg_clk_func>; + slot@0 { + reg = <0>; + bus-width = <8>; + disable-wp; + }; + }; + + dwmmc0@fcd03000 { + num-slots = <1>; + vmmc-supply = <&ldo12>; + fifo-depth = <0x100>; + supports-highspeed; + pinctrl-names = "default"; + pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>; + cd-gpio = <&gpio10 3 0>; + slot@0 { + reg = <0>; + bus-width = <4>; + disable-wp; + }; + }; + + dwmmc2@fcd05000 { + status = "disabled"; + }; + + dwmmc3@fcd06000 { + status = "disabled"; + }; + + power_management { + compatible = "hisilicon,hs-power-management"; + reg = <0xfcc00000 0x0240>, /* pmu_spi_base_addr */ + <0xf8000000 0x14000>, /* secram_base_addr */ + <0xfca09000 0x1000>, /* pctrl_base_addr */ + <0xfc000000 0x2000>, /* a9_per_base_addr */ + <0xfcb00000 0x1000>, /* uart0_base_addr, console workaround */ + <0xfc803000 0x1000>; /* io_base, console workaround */ + pmu-power-hold-gpios = <&gpio19 6 0>; + }; + + edc0: edc@fa202000 { + hisilicon,pixel-format = "RGB565"; + hisilicon,color-mode = <5>; + hisilicon,dsi-clock-frequency = <270000000>; /* 241MHz, not 300MHz */ + hisilicon,mipi-mode = "video"; + hisilicon,mipi-lanes = <4>; + status = "ok"; + + display-timings { + native-mode = <&timing0>; + timing0: timing0 { + clock-frequency = <68000000>; /* 13.158MHz pixel clock */ + hactive = <720>; + vactive = <1280>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <1>; + pixelclk-active = <1>; + hfront-porch = <96>; + hback-porch = <16>; + hsync-len = <16>; + vfront-porch = <12>; + vback-porch = <12>; + vsync-len = <16>; + }; + }; + }; + + pmic: pmic@fcc00000 { + compatible = "hisilicon,hi6421-pmic"; + reg = <0xfcc00000 0x0180>; /* 0x60 << 2 */ + #interrupt-cells = <2>; + interrupt-controller; + gpios = <&gpio19 7 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_int_cfg_func>; + + ldo0: ldo@20 { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDO0"; + regulator-min-microvolt = <2850000>; + regulator-max-microvolt = <2850000>; + hisilicon,hi6421-ctrl = <0x20 0x10 0x20>; + hisilicon,hi6421-vset = <0x20 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1500000>, <1800000>, + <2400000>, <2500000>, + <2600000>, <2700000>, + <2850000>, <3000000>; + hisilicon,hi6421-off-on-delay-us = <10000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <8000>; + }; + + ldo1: ldo@21 { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDO1"; + regulator-min-microvolt = <1700000>; + regulator-max-microvolt = <2000000>; + regulator-boot-on; + regulator-always-on; + hisilicon,hi6421-ctrl = <0x21 0x10 0x20>; + hisilicon,hi6421-vset = <0x21 0x03>; + hisilicon,hi6421-n-voltages = <4>; + hisilicon,hi6421-vset-table = <1700000>, <1800000>, + <1900000>, <2000000>; + hisilicon,hi6421-off-on-delay-us = <10000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <5000>; + }; + + ldo2: ldo@22 { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDO2"; + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + regulator-always-on; + hisilicon,hi6421-ctrl = <0x22 0x10 0x20>; + hisilicon,hi6421-vset = <0x22 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1050000>, <1100000>, + <1150000>, <1200000>, + <1250000>, <1300000>, + <1350000>, <1400000>; + hisilicon,hi6421-off-on-delay-us = <20000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <8000>; + }; + + ldo3: ldo@23 { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDO3"; + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + regulator-always-on; + hisilicon,hi6421-ctrl = <0x23 0x10 0x20>; + hisilicon,hi6421-vset = <0x23 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1050000>, <1100000>, + <1150000>, <1200000>, + <1250000>, <1300000>, + <1350000>, <1400000>; + hisilicon,hi6421-off-on-delay-us = <20000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <8000>; + }; + + ldo4: ldo@24 { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDO4"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <3000000>; + regulator-boot-on; + regulator-always-on; + hisilicon,hi6421-ctrl = <0x24 0x10 0x20>; + hisilicon,hi6421-vset = <0x24 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1500000>, <1800000>, + <2400000>, <2500000>, + <2600000>, <2700000>, + <2850000>, <3000000>; + hisilicon,hi6421-off-on-delay-us = <20000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <8000>; + }; + + ldo5: ldo@25 { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDO5"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <3000000>; + regulator-boot-on; + regulator-always-on; + hisilicon,hi6421-ctrl = <0x25 0x10 0x20>; + hisilicon,hi6421-vset = <0x25 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1500000>, <1800000>, + <2400000>, <2500000>, + <2600000>, <2700000>, + <2850000>, <3000000>; + hisilicon,hi6421-off-on-delay-us = <20000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <8000>; + }; + + ldo6: ldo@26 { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDO6"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <3000000>; + regulator-boot-on; + regulator-always-on; + hisilicon,hi6421-ctrl = <0x26 0x10 0x20>; + hisilicon,hi6421-vset = <0x26 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1500000>, <1800000>, + <2400000>, <2500000>, + <2600000>, <2700000>, + <2850000>, <3000000>; + hisilicon,hi6421-off-on-delay-us = <20000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <8000>; + }; + + ldo7: ldo@27 { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDO7"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <3000000>; + regulator-boot-on; + regulator-always-on; + hisilicon,hi6421-ctrl = <0x27 0x10 0x20>; + hisilicon,hi6421-vset = <0x27 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1500000>, <1800000>, + <2400000>, <2500000>, + <2600000>, <2700000>, + <2850000>, <3000000>; + hisilicon,hi6421-off-on-delay-us = <20000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <5000>; + }; + + ldo8: ldo@28 { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDO8"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + hisilicon,hi6421-ctrl = <0x28 0x10 0x20>; + hisilicon,hi6421-vset = <0x28 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1500000>, <1800000>, + <2400000>, <2600000>, + <2700000>, <2850000>, + <3000000>, <3300000>; + hisilicon,hi6421-off-on-delay-us = <20000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <8000>; + }; + + ldo9: ldo@29 { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDO9"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <3000000>; + hisilicon,hi6421-ctrl = <0x29 0x10 0x20>; + hisilicon,hi6421-vset = <0x29 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1500000>, <1800000>, + <2400000>, <2500000>, + <2600000>, <2700000>, + <2850000>, <3000000>; + hisilicon,hi6421-off-on-delay-us = <40000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <8000>; + }; + + ldo10: ldo@2a { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDO10"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <3000000>; + hisilicon,hi6421-ctrl = <0x2a 0x10 0x20>; + hisilicon,hi6421-vset = <0x2a 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1500000>, <1800000>, + <2400000>, <2500000>, + <2600000>, <2700000>, + <2850000>, <3000000>; + hisilicon,hi6421-off-on-delay-us = <40000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <8000>; + }; + + ldo11: ldo@2b { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDO11"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <3000000>; + hisilicon,hi6421-ctrl = <0x2b 0x10 0x20>; + hisilicon,hi6421-vset = <0x2b 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1500000>, <1800000>, + <2400000>, <2500000>, + <2600000>, <2700000>, + <2850000>, <3000000>; + hisilicon,hi6421-off-on-delay-us = <40000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <8000>; + }; + + ldo12: ldo@2c { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDO12"; + regulator-min-microvolt = <2850000>; + regulator-max-microvolt = <2850000>; + hisilicon,hi6421-ctrl = <0x2c 0x10 0x20>; + hisilicon,hi6421-vset = <0x2c 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1500000>, <1800000>, + <2400000>, <2500000>, + <2600000>, <2700000>, + <2850000>, <3000000>; + hisilicon,hi6421-off-on-delay-us = <40000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <8000>; + }; + + ldo13: ldo@2d { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDO13"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <3000000>; + hisilicon,hi6421-ctrl = <0x2d 0x10 0x20>; + hisilicon,hi6421-vset = <0x2d 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1500000>, <1800000>, + <2400000>, <2500000>, + <2600000>, <2700000>, + <2850000>, <3000000>; + hisilicon,hi6421-off-on-delay-us = <40000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <8000>; + }; + + ldo14: ldo@2e { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDO14"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <3000000>; + hisilicon,hi6421-ctrl = <0x2e 0x10 0x20>; + hisilicon,hi6421-vset = <0x2e 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1500000>, <1800000>, + <2400000>, <2500000>, + <2600000>, <2700000>, + <2850000>, <3000000>; + hisilicon,hi6421-off-on-delay-us = <40000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <8000>; + }; + + ldo15: ldo@2f { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDO15"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <3300000>; + hisilicon,hi6421-ctrl = <0x2f 0x10 0x20>; + hisilicon,hi6421-vset = <0x2f 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1500000>, <1800000>, + <2400000>, <2600000>, + <2700000>, <2850000>, + <3000000>, <3300000>; + hisilicon,hi6421-off-on-delay-us = <40000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <8000>; + }; + + ldo16: ldo@30 { + compatible = "hisilicon,hi6421-ldo"; + 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>; + hisilicon,hi6421-vset-table = <1500000>, <1800000>, + <2400000>, <2500000>, + <2600000>, <2700000>, + <2850000>, <3000000>; + hisilicon,hi6421-off-on-delay-us = <40000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <8000>; + }; + + ldo17: ldo@31 { + compatible = "hisilicon,hi6421-ldo"; + 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>; + hisilicon,hi6421-vset-table = <1500000>, <1800000>, + <2400000>, <2500000>, + <2600000>, <2700000>, + <2850000>, <3000000>; + hisilicon,hi6421-off-on-delay-us = <40000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <8000>; + }; + + ldo18: ldo@32 { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDO18"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <3000000>; + hisilicon,hi6421-ctrl = <0x32 0x10 0x20>; + hisilicon,hi6421-vset = <0x32 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1500000>, <1800000>, + <2400000>, <2500000>, + <2600000>, <2700000>, + <2850000>, <3000000>; + hisilicon,hi6421-off-on-delay-us = <40000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <8000>; + }; + + ldo19: ldo@33 { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDO19"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <3000000>; + hisilicon,hi6421-ctrl = <0x2a 0x10 0x20>; + hisilicon,hi6421-vset = <0x2a 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1500000>, <1800000>, + <2400000>, <2500000>, + <2600000>, <2700000>, + <2850000>, <3000000>; + hisilicon,hi6421-off-on-delay-us = <40000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <8000>; + }; + + ldo20: ldo@34 { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDO20"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <3000000>; + hisilicon,hi6421-ctrl = <0x34 0x10 0x20>; + hisilicon,hi6421-vset = <0x34 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1500000>, <1800000>, + <2400000>, <2500000>, + <2600000>, <2700000>, + <2850000>, <3000000>; + hisilicon,hi6421-off-on-delay-us = <40000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <8000>; + }; + + ldoaudio: ldo@36 { + compatible = "hisilicon,hi6421-ldo"; + regulator-name = "LDOAUDIO"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <3300000>; + hisilicon,hi6421-ctrl = <0x36 0x01 0x02>; + hisilicon,hi6421-vset = <0x36 0x70>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <2800000>, <2850000>, + <2900000>, <2950000>, + <3000000>, <3100000>, + <3200000>, <3300000>; + hisilicon,hi6421-off-on-delay-us = <40000>; + hisilicon,hi6421-enable-time-us = <250>; + hisilicon,hi6421-eco-microamp = <5000>; + }; + + buck0: buck@0c { + compatible = "hisilicon,hi6421-buck012"; + regulator-name = "BUCK0"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1600000>; + regulator-boot-on; + regulator-always-on; + hisilicon,hi6421-ctrl = <0x0c 0x01 0x10>; + hisilicon,hi6421-vset = <0x0d 0x7f>; + hisilicon,hi6421-n-voltages = <128>; + hisilicon,hi6421-uv-step = <7086>; + hisilicon,hi6421-off-on-delay-us = <20000>; + hisilicon,hi6421-enable-time-us = <300>; + }; + + buck1: buck@0e { + compatible = "hisilicon,hi6421-buck012"; + regulator-name = "BUCK1"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1600000>; + regulator-boot-on; + regulator-always-on; + hisilicon,hi6421-ctrl = <0x0e 0x01 0x10>; + hisilicon,hi6421-vset = <0x0f 0x7f>; + hisilicon,hi6421-n-voltages = <128>; + hisilicon,hi6421-uv-step = <7086>; + hisilicon,hi6421-off-on-delay-us = <20000>; + hisilicon,hi6421-enable-time-us = <300>; + }; + + buck2: buck@10 { + compatible = "hisilicon,hi6421-buck012"; + regulator-name = "BUCK2"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1600000>; + regulator-boot-on; + hisilicon,hi6421-ctrl = <0x10 0x01 0x10>; + hisilicon,hi6421-vset = <0x11 0x7f>; + hisilicon,hi6421-n-voltages = <128>; + hisilicon,hi6421-uv-step = <7086>; + hisilicon,hi6421-off-on-delay-us = <100>; + hisilicon,hi6421-enable-time-us = <250>; + }; + + buck3: buck@12 { + compatible = "hisilicon,hi6421-buck345"; + regulator-name = "BUCK3"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + regulator-always-on; + hisilicon,hi6421-ctrl = <0x12 0x01 0x10>; + hisilicon,hi6421-vset = <0x13 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <950000>, <1050000>, + <1100000>, <1170000>, + <1134000>, <1150000>, + <1167000>, <1200000>; + hisilicon,hi6421-off-on-delay-us = <20000>; + hisilicon,hi6421-enable-time-us = <250>; + }; + + buck4: buck@14 { + compatible = "hisilicon,hi6421-buck345"; + regulator-name = "BUCK4"; + regulator-min-microvolt = <1150000>; + regulator-max-microvolt = <2000000>; + regulator-boot-on; + regulator-always-on; + hisilicon,hi6421-ctrl = <0x14 0x01 0x10>; + hisilicon,hi6421-vset = <0x15 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1150000>, <1200000>, + <1250000>, <1350000>, + <1700000>, <1800000>, + <1900000>, <2000000>; + hisilicon,hi6421-off-on-delay-us = <20000>; + hisilicon,hi6421-enable-time-us = <250>; + }; + + buck5: buck@16 { + compatible = "hisilicon,hi6421-buck345"; + regulator-name = "BUCK5"; + regulator-min-microvolt = <1150000>; + regulator-max-microvolt = <1900000>; + regulator-boot-on; + regulator-always-on; + hisilicon,hi6421-ctrl = <0x16 0x01 0x10>; + hisilicon,hi6421-vset = <0x17 0x07>; + hisilicon,hi6421-n-voltages = <8>; + hisilicon,hi6421-vset-table = <1150000>, <1200000>, + <1250000>, <1350000>, + <1600000>, <1700000>, + <1800000>, <1900000>; + hisilicon,hi6421-off-on-delay-us = <20000>; + hisilicon,hi6421-enable-time-us = <250>; + }; + + onkey { + compatible = "hisilicon,hi6421-onkey"; + interrupt-parent = <&pmic>; + interrupts = <7 0>, <6 0>, <5 0>, <4 0>; + interrupt-names = "down", "up", "hold 1s", "hold 10s"; + }; + + rtc { + compatible = "hisilicon,hi6421-rtc"; + interrupt-parent = <&pmic>; + interrupts = <0 0>; + }; + }; /* end of pmic */ + }; +}; diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 4a5903e04827..c0fadd76fff8 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -22,6 +22,7 @@ CONFIG_SOC_OMAP5=y CONFIG_SOC_AM33XX=y CONFIG_SOC_AM43XX=y CONFIG_ARCH_ROCKCHIP=y +CONFIG_ARCH_HI3xxx=y CONFIG_ARCH_SOCFPGA=y CONFIG_PLAT_SPEAR=y CONFIG_ARCH_SPEAR13XX=y diff --git a/arch/arm/mach-hs/Kconfig b/arch/arm/mach-hs/Kconfig new file mode 100644 index 000000000000..6b1e18c57118 --- /dev/null +++ b/arch/arm/mach-hs/Kconfig @@ -0,0 +1,21 @@ +config ARCH_HI3xxx + bool "Hisilicon Hi36xx/Hi37xx family" if ARCH_MULTI_V7 + select CACHE_L2X0 + select CACHE_PL310 + select PINCTRL + select PINCTRL_SINGLE + select SERIAL_AMBA_PL011 + select SERIAL_AMBA_PL011_CONSOLE + help + Support for Hislicon Hi36xx/Hi37xx processor family + +if ARCH_HI3xxx + +config MACH_HS_DT + bool "Hisilicon Development Board" + default y + help + Say 'Y' here if you want to support the Hisilicon Development + Board. + +endif diff --git a/arch/arm/mach-hs/Makefile b/arch/arm/mach-hs/Makefile new file mode 100644 index 000000000000..4d7b57a64214 --- /dev/null +++ b/arch/arm/mach-hs/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for Hisilicon Hi36xx/Hi37xx processors line +# + +obj-$(CONFIG_MACH_HS_DT) += hs-dt.o system.o +obj-$(CONFIG_SMP) += platsmp.o +obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o +obj-$(CONFIG_PM) += pm.o +obj-$(CONFIG_PM) += hilpm-cpugodp.o +obj-$(CONFIG_PM) += lowpmregs.o diff --git a/arch/arm/mach-hs/core.h b/arch/arm/mach-hs/core.h new file mode 100644 index 000000000000..ec2388b88612 --- /dev/null +++ b/arch/arm/mach-hs/core.h @@ -0,0 +1,28 @@ +#ifndef __HISILICON_CORE_H +#define __HISILICON_CORE_H + +#include <linux/reboot.h> + +extern void __iomem *hs_sctrl_base; +extern void __iomem *hs_secram_va_base; +extern void __iomem *hs_a9per_va_base; +extern void __iomem *hs_pctrl_va_base; +extern void __iomem *hs_pmuspi_va_base; + +extern void hs_set_cpu_jump(int cpu, void *jump_addr); +extern int hs_get_cpu_jump(int cpu); +extern void secondary_startup(void); +extern void hs_map_io(void); +extern struct smp_operations hs_smp_ops; +extern void hs_restart(enum reboot_mode, const char *cmd); + +extern void __init hs_hotplug_init(void); +extern void hs_cpu_die(unsigned int cpu); +extern int hs_cpu_kill(unsigned int cpu); +extern void hs_set_cpu(int cpu, bool enable); + +#ifdef CONFIG_PM +extern void pmulowpower(int isuspend); +#endif + +#endif diff --git a/arch/arm/mach-hs/hilpm-cpugodp.S b/arch/arm/mach-hs/hilpm-cpugodp.S new file mode 100644 index 000000000000..e87d647ed2d1 --- /dev/null +++ b/arch/arm/mach-hs/hilpm-cpugodp.S @@ -0,0 +1,1006 @@ +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <asm/cp15.h> +#include "hipm.h" + +.extern hs_a9per_va_base +.extern hs_secram_va_base +.extern hs_sctrl_base + +/**physical address to virtal or virtual to physical**/ +.macro addr_proc, rx, rxt, p, v + LDR \rxt, =\p + SUB \rx, \rxt + LDR \rxt, =\v + ADD \rx, \rxt +.endm + +/**physical to virtual**/ +.macro p2v, rx, rxt, p, v + addr_proc \rx, \rxt, \p, \v +.endm + +/**virtual to physical**/ +.macro v2p, rx, rxt, v, p + addr_proc \rx, \rxt, \v, \p +.endm + +/* + *------------------------------------------------------------------------------ + * Function: hilpm_cpu_godpsleep + * + * this function is the low level interface when deep sleep. + * + */ + +ENTRY (hilpm_cpu_godpsleep) + /* According to Procedure Call Standard for ARM, r0-r3 dont need to + * be preserved + */ + STMFD sp!, {r4-r11, lr} + + LDR r8, hi_cpu_godpsleep_phybase + @r8 store the PHY address of stored_ctx + LDR r0, =(hs_secram_va_base) + LDR r0, [r0] + LDR r9, =(A9_PRE_STORE_DATA_ADDR_OFFSET) + ADD r9, r9, r0 @r9 store the pre-store address which in securam + + /* Some A9 Contexts need be protected before MMU and cache disabled + * r9 store the address in securam, to store some critial cp15 + * register which has relationship with MMU operation + */ + + MOV r1, #0 + MCR p15, 0, r1, c7, c5, 0 @ Invalidate entire instruction cache + @ and flush branch predictor arrays + /* get the CPUID */ + MRC p15, 0, r0, c0, c0, 5 @ Read CPU MPIDR + AND r0, r0, #0x03 @ Mask off, leaving the CPU ID field + +save_ctx: + + /* save critial CP15 register before MMU Disabled + * CPU_ID save in r0 + * save CTRL_Register in r1 + * save Aux_Ctrl_register in r2 + * TTBR0 in r3 + * TTBR1 in r4 + * TTBCR in r5 + * DAC in r6 + */ + mrc p15, 0, r1, c1, c0, 0 @ sctlr + mrc p15, 0, r2, c1, c0, 1 @ actlr + mrc p15, 0, r3, c2, c0, 0 @ TTBR0 + mrc p15, 0, r4, c2, c0, 1 @ TTBR1 + mrc p15, 0, r5, c2, c0, 2 @ TTBCR + mrc p15, 0, r6, c3, c0, 0 @ domain access control reg + + /* Notes: MMU is enabled, using the pre-store addree which stored in R9 + * r0,[r9] @offset0 store the CPU_ID + * r1,[r9,#0x4] @CTRL_Register + * r2,[r9,#0x8] @Aux_Ctrl_register + * r3,[r9,#0xc] @TTBR0 + * r4,[r9,#0x10] @TTBR1 + * r5,[r9,#0x14] @TTBCR + * r6,[r9,#0x18] @DAC + */ + STMIA r9,{r0-r6} + + /* now Clean and Invalid D-Cache, and Disable Cache */ + mov r0, #0 + mcr p15, 0, r0, c7, c5, 4 @ Flush prefetch buffer + mcr p15, 0, r0, c7, c5, 6 @ Invalidate branch predictor array + mcr p15, 0, r0, c8, c5, 0 @ Invalidate instruction TLB + mcr p15, 0, r0, c8, c6, 0 @ Invalidate data TLB + + /* protect r9 to r6 while clean and invalid l1-cache + * now r9 can be released for free use + * r8 is reserved which store the PHY address + */ + mov r6,r9 + + /* Flush the entire cache system: Dcache and Icache + * Corrupted registers: R0~R3 + */ + bl v7_flush_kern_cache_all @ Flush the entire cache system + + mov r0, #0 + mcr p15, 0, r0, c7, c1, 6 @ BPIALLIS + mcr p15, 0, r0, c8, c3, 0 + + mov r0, #0 + mcr p15, 0, r0, c1, c0, 1 @ A9 exit coherency now + + /* After clean and invalid cache, we need disable + * D-Cache immediately + */ + + /* Data Cache Disable */ + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #(CR_C) @ Dcache disable + mcr p15, 0, r0, c1, c0, 0 + + /* save back Secruram stored data address + * r10 store the pre-store ctx address in securam + * r6 can be releaseed for free use + */ + mov r10, r6 + + /* Before MMU is disabled, to + * Convert r10 from virtual address to physical address + * Note: r10 is the address in SECRAM to save tmp data + */ + LDR r0, =(hs_secram_va_base) + LDR r0, [r0] + SUB r10, r10, r0 @ r10 is offset to SECRAM base + LDR r0, =(REG_BASE_SECRAM) + ADD r10, r10, r0 @ r10 is physical address + + /* write domain access to get the domain access right */ + LDR r0, =0xFFFFFFFF + MCR p15, 0, r0, c3, c0, 0 + + /*read TTBCR*/ + mrc p15, 0, r7, c2, c0, 2 + and r7, #0x7 + cmp r7, #0x0 + beq create_idmap +ttbr_error: + @TTBR1 not supports + b ttbr_error + +create_idmap: + /**read TTBR0 registers**/ + mrc p15, 0, r2, c2, c0, 0 @ r2: translation table base register 0 + ldr r5, =TTBRBIT_MASK @ 0xFFFFC000, high 18 bits + and r2, r5 @ r2 = TTBR0's high 18 bits + ldr r4, =(hisi_v2p(disable_mmu)) @ r4, pa(disable_mmu) + ldr r5, =TABLE_INDEX_MASK @ 0xFFF00000, top 12 bits. + @ why not 14? + and r4, r5 @ r4 = keeps the top 12 bits. + ldr r1, =TABLE_ENTRY + @ r1 = 0x00000C02 + @ why 0x0C02? these are ttb property bits. + @ any change in v3.8? + add r1, r1, r4 @ r1 = top 12 bits of pa(disable_mmu) | 0x0C02 + lsr r4, #18 @ r4 = r4 >> 18 + add r2, r4 @ r2 = r2 + r4, TTBR0's high 18 bits + + @ r4's top 14 bits (shifted to low 14 bits) + + /**r2 virtual addr for TLB**/ + p2v r2, r4, K3_PLAT_PHYS_OFFSET, PAGE_OFFSET + + @ now, r2 is the virtual address of disable_mmu() code part. + @ what if disable_mmu() code part cross two or + @ more translation table section? + @ /* TODO: need manual checking now. should be fixed in future */ + + /**read the TLB**/ + LDR r7, [r2] + + /**config the identy mapping**/ + STR r1, [r2] @ change the TLB its to it's physical address. + + /**r9 virtual addr for tlb**/ + mov r9, r2 + + /**r11 virtual addr of the enable_mmu**/ + ADR r11, mmu_enalbed + NOP + NOP + NOP + + LDR r6, =(hisi_v2p(ready_to_store)) + LDR pc, =(hisi_v2p(disable_mmu)) + +disable_mmu: + instr_sync + + /*disable MMU*/ + MRC p15, 0, r0, c1, c0, 0 + BIC r0, r0, #(CR_M | CR_C) @ MMU disable, Dcache disable + MCR p15, 0, r0, c1, c0, 0 + + /* invalidate I & D TLBs */ + LDR r0,=0x0 + MCR p15, 0, r0, c8, c7, 0 + instr_sync + + MOV pc, r6 + + /* From this scratch , MMU is Disabled */ +ready_to_store: + /* move critical data from securam to DDR (L1/l2 unaccessable) + * r0,[r10] offset0 store the Slave CPU Return Addres + * if offset0 is 0, that means this cpu has + * not booted up yet + * r1,[r10,#0x4] @CTRL_Register + * r2,[r10,#0x8] @Aux_Ctrl_register + * r3,[r10,#0xC] @TTBR0 + * r4,[r10,#0x10] @TTBR1 + * r5,[r10,#0x14] @TTBCR + * r6,[r10,#0x18] @DAC + * r7,[r10,#0x1C] direct mapping first level descriptor + * r9,[r10,#0x20] virtual addr for the first level descriptor + * r11,[r10, #0x24] enable_mmu virtual addr + */ + + /* r10 is physical address of SECURAM to save tmp data + * Note: Converted before MMU is disabled + */ + LDMIA r10, {r0-r6} + + mrc p15, 0, r3, c2, c0, 0 @ TTBR0 + mrc p15, 0, r4, c2, c0, 1 @ TTBR1 + mrc p15, 0, r5, c2, c0, 2 @ TTBCR + + /* r8 is addr to store data in ddr */ + STMIA r8, {r0-r7, r9, r11} + +all_to_store: + + /* R6/R10 can be release now + * R9/R8 is reserved + */ + add r9, r8, #SUSPEND_STORE_RESEVED_UNIT + + /* save CP15 register */ + mrc p15, 2, r0, c0, c0, 0 @ csselr + mrc p15, 0, r4, c15, c0, 0 @ pctlr + stmia r9!, {r0, r4} + + mrc p15, 0, r0, c15, c0, 1 @ diag + mrc p15, 0, r1, c1, c0, 2 @ cpacr + stmia r9!, {r0-r1} + mrc p15, 0, r4, c7, c4, 0 @ PAR + mrc p15, 0, r5, c10, c2, 0 @ PRRR + mrc p15, 0, r6, c10, c2, 1 @ NMRR + mrc p15, 0, r7, c12, c0, 0 @ VBAR + stmia r9!, {r4-r7} + + mrc p15, 0, r0, c13, c0, 1 @ CONTEXTIDR + mrc p15, 0, r1, c13, c0, 2 @ TPIDRURW + mrc p15, 0, r2, c13, c0, 3 @ TPIDRURO + mrc p15, 0, r3, c13, c0, 4 @ TPIDRPRW + stmia r9!, {r0-r3} + + /* to save normal register which including R9,so + * use R0 as stack pointer + */ + MOV r0,r9 + + /** + save 7 modes programmable registers + save svc mode registers + enter svc mode, no interrupts + **/ + + MOV r2, #MODE_SVC | I_BIT | F_BIT + MSR cpsr_c, r2 + MRS r1, spsr + STMIA r0!, {r1, r13, r14} + + /** + save fiq mode registers + enter fiq mode, no interrupts + **/ + MOV r2, #MODE_FIQ | I_BIT | F_BIT + MSR cpsr_c, r2 + MRS r1,spsr + STMIA r0!, {r1, r8-r14} + + /** + save irq mode registers + enter irq mode, no interrupts + **/ + MOV r2, #MODE_IRQ | I_BIT | F_BIT + MSR cpsr_c, r2 + MRS r1,spsr + STMIA r0!, {r1, r13, r14} + + /** + save undefine mode registers + enter undefine mode, no interrupts + **/ + MOV r2, #MODE_UND | I_BIT | F_BIT + MSR cpsr_c, r2 + MRS r1,spsr + STMIA r0!, {r1, r13, r14} + + /** + save abort mode registers + enter abort mode, no interrupts + **/ + MOV r2, #MODE_ABT | I_BIT | F_BIT + MSR cpsr_c, r2 + MRS r1,spsr + STMIA r0!, {r1, r13, r14} + + /** + save system mode registers + enter system mode, no interrupts + **/ + MOV r2, #MODE_SYS | I_BIT | F_BIT + MSR cpsr_c, r2 + STMIA r0!, {r13, r14} + + /** back to SVC mode, no interrupts **/ + MOV r2, #MODE_SVC | I_BIT | F_BIT + MSR cpsr_c, r2 + + /** save the private timer **/ +save_prv_timer: + LDR r4,= A9_PRV_TIMER_BASE + LDR r2, [r4, #TIMER_LD] @timer load + LDR r3, [r4, #TIMER_CTL] @timer control + STMIA r0!, {r2-r3} + + /** + Now Master CPU protect the Global timer. + save the 64bit timer + **/ + LDR r1,= A9_GLB_TIMER_BASE + LDR r2, [r1, #TIM64_CTL] @64-bit timer control + BIC r3, r2, #0xF + STR r3, [r1, #TIM64_CTL] @disable the features + + /** the registers are now frozen for the context save **/ + LDR r3, [r1, #TIM64_AUTOINC] @Autoincrement register + LDR r4, [r1, #TIM64_CMPLO] @comparator - lo word + LDR r5, [r1, #TIM64_CMPHI] @comparator - hi word + STMIA r0!, {r2-r5} + + LDR r2, [r1, #TIM64_CNTLO] @counter - lo word + LDR r3, [r1, #TIM64_CNTHI] @counter - hi word + STMIA r0!, {r2-r3} + + +#ifdef CONFIG_CACHE_L2X0 + /** + save L2CC Configuration + **/ + dsb + + ldr r6, =REG_BASE_L2CC + ldr r2, [r6, #L2X0_AUX_CTRL] + ldr r3, [r6, #L2X0_TAG_LATENCY_CTRL] + ldr r4, [r6, #L2X0_DATA_LATENCY_CTRL] + ldr r5, [r6, #L2X0_PREFETCH_OFFSET] + stmia r0!, {r2-r5} +#endif + /** save SCU Configruation **/ + LDR r1,=A9_SCU_BASE + LDR r2,[r1,#SCU_FILTER_START_OFFSET] + LDR r3,[r1,#SCU_FILTER_END_OFFSET] + LDR r4,[r1,#SCU_ACCESS_CONTROL_OFFSET] + LDR r5,[r1,#SCU_NONSEC_CONTROL_OFFSET] + LDR r6,[r1,#SCU_CONTROL_OFFSET] + LDR r7,[r1,#SCU_POWER_STATE_OFFSET] + STMIA r0!,{r2-r7} + + /* + Protect the NAND Configuration + Since it is a risk to read NAND register when + we do not konw its clock and reset status + so left this work to NAND driver. + */ + /* + LDR r4,=NAND_CTRLOR_BASE + LDR r1,[r4,#0x0] + LDR r2,[r4,#0x10] + LDR r3,[r4,#0x14] + STMIA r0!,{r1-r3} + */ + + /** + Need not protect the eMMC Configuration + eMMC will re enumation + **/ + +SKIP_MASTER_CPU_OPRATORATION: + + str r0,[r8,#SUSPEND_STACK_ADDR] @store the stack_top + + /* Jump to SecuRAM to execute + * r6: load return address in r6. But, in normal case, Securam code + * will stop in WFI, and never return. + * + * When system resume, fastboot will jump to (MASTER_SR_BACK_PHY_ADDR) + */ + LDR r7, =(MASTER_SR_BACK_PHY_ADDR) + LDR r6, [r7] + + LDR r0, =(REG_BASE_SECRAM) + LDR r1, =(DPSLEEP_CODE_ADDR_OFFSET) + ADD r0, r0, r1 + MOV PC, r0 @ Jump to the SECRAM address + @ Goto hs_finish_suspend to read the code + +/* + * Function: master_cpu_resume() + * + * Resume entry point of Master CPU. Physical address of this function is + * stored in MASTER_SR_BACK_PHY_ADDR. During resume, fastboot code fetches + * from MASTER_SR_BACK_PHY_ADDR, and jump. + * + * At the end of this function, it returns to the C world where + * hilpm_cpu_godpsleep() is called. + */ +ENTRY (master_cpu_resume) + /* write domain access to get the domain access right */ + LDR r0, =0xFFFFFFFF + MCR p15, 0, r0, c3, c0, 0 + + mov r0, #0 + mcr p15, 0, r0, c7, c5, 4 @ Flush prefetch buffer + mcr p15, 0, r0, c7, c5, 6 @ Invalidate branch predictor array + mcr p15, 0, r0, c8, c5, 0 @ Invalidate instruction TLB + mcr p15, 0, r0, c8, c6, 0 @ Invalidate data TLB + + mov r10, #0 @ swith back to cache level 0 + mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr + dsb + isb + mov r0, #0 + mcr p15, 0, r0, c7, c1, 0 @ ICIALLUIS + mcr p15, 0, r0, c7, c1, 6 @ BPIALLIS + mcr p15, 0, r0, c8, c3, 0 + + /** + restore_data + R10 is reserved to store the PHY address of A9 stored_ctx + **/ + LDR r10, hi_cpu_godpsleep_phybase + + mov r1, #0 + mcr p15, 0, r1, c7, c5, 0 @ Invalidate entire instruction cache + @ and flush branch predictor arrays + +master_start_load_ctx: + + /* R0 is reserved to stored_ctx stack pointer */ + LDR r0, [r10, #SUSPEND_STACK_ADDR] + + /** + Resume the NAND Configuration + Since it is a risk to read NAND register when + we do not konw its clock and reset status + so left this work to NAND driver. + **/ + /* + LDMDB r0!, {r1-r3} + LDR r4,=REG_BASE_NANDC_CFG + STR r3,[r4,#0x14] + STR r2,[r4,#0x10] + STR r1,[r4,#0x0] + */ + + /* Restore SCU Configruation **/ + LDMDB r0!, {r2-r7} + LDR r1,=A9_SCU_BASE + STR r2,[r1,#SCU_FILTER_START_OFFSET] + STR r3,[r1,#SCU_FILTER_END_OFFSET] + STR r4,[r1,#SCU_ACCESS_CONTROL_OFFSET] + STR r5,[r1,#SCU_NONSEC_CONTROL_OFFSET] + + LDR r8, =0xFFFF + /* invalidate the duplicate TAG store */ + STR r8, [r1, #SCU_SEC_INVALID_REG_OFFSET] + + STR r6,[r1,#SCU_CONTROL_OFFSET] + STR r7,[r1,#SCU_POWER_STATE_OFFSET] @restore CPU power statue + +#ifdef CONFIG_CACHE_L2X0 + /* restore l2-cache configuration */ + ldr r6, =REG_BASE_L2CC + LDMDB r0!, {r2-r5} + str r3, [r6, #L2X0_TAG_LATENCY_CTRL] + str r4, [r6, #L2X0_DATA_LATENCY_CTRL] + str r5, [r6, #L2X0_PREFETCH_OFFSET] + str r2, [r6, #L2X0_AUX_CTRL] +#endif + + /* restore 64bit global timer */ + LDR r1, =A9_GLB_TIMER_BASE + LDMDB r0!, {r2-r3} + STR r2, [r1, #TIM64_CNTLO] @counter - lo word + STR r3, [r1, #TIM64_CNTHI] @counter - hi word + + LDMDB r0!, {r2-r5} + STR r3, [r1, #TIM64_AUTOINC] @Autoincrement register + STR r4, [r1, #TIM64_CMPLO] @comparator - lo word + STR r5, [r1, #TIM64_CMPHI] @comparator - hi word + STR r2, [r1, #TIM64_CTL] @restore the control last + +slave_start_load_ctx: + /* restore private timer */ + LDR r1, =A9_PRV_TIMER_BASE + LDMDB r0!, {r2-r3} + STR r2, [r1, #TIMER_LD] @timer load + STR r3, [r1, #TIMER_CTL] @timer control + + /** + resume system mode registers + enter system mode, no interrupts + **/ + MOV r2, #MODE_SYS | I_BIT | F_BIT + MSR cpsr_c, r2 + LDMDB r0!, {r13, r14} + + /** + resume abort mode registers + enter abort mode, no interrupts + **/ + MOV r2, #MODE_ABT | I_BIT | F_BIT + MSR cpsr_c, r2 + LDMDB r0!, {r1, r13, r14} + MSR spsr_c, r1 + + /** + resume undefine mode registers + enter undefine mode, no interrupts + **/ + MOV r2, #MODE_UND | I_BIT | F_BIT + MSR cpsr_c, r2 + LDMDB r0!, {r1, r13, r14} + MSR spsr_c, r1 + + /** + resume irq mode registers + enter irq mode, no interrupts + **/ + MOV r2, #MODE_IRQ | I_BIT | F_BIT + MSR cpsr_c, r2 + LDMDB r0!, {r1, r13, r14} + MSR spsr_c, r1 + + /** + resume fiq mode registers + enter fiq mode, no interrupts + **/ + MOV r2, #MODE_FIQ | I_BIT | F_BIT + MSR cpsr_c, r2 + LDMDB r0!, {r1, r8-r14} + MSR spsr_c, r1 + + /** + resume svc mode registers + enter svc mode, no interrupts + **/ + MOV r2, #MODE_SVC | I_BIT | F_BIT + MSR cpsr_c, r2 + LDMDB r0!, {r1, r13, r14} + MSR spsr_c, r1 + + /* Restore CP15 register, need use r0 register + * use R8 replace R0 to save Stack_pointer + */ + MOV r8,r0 + + + /** restore CP15 register **/ + LDMDB r8!, {r0-r3} + mcr p15, 0, r0, c13, c0, 1 @ CONTEXTIDR + mcr p15, 0, r1, c13, c0, 2 @ TPIDRURW + mcr p15, 0, r2, c13, c0, 3 @ TPIDRURO + mcr p15, 0, r3, c13, c0, 4 @ TPIDRPRW + + LDMDB r8!, {r4-r7} + mcr p15, 0, r4, c7, c4, 0 @ PAR + mcr p15, 0, r5, c10, c2, 0 @ PRRR + mcr p15, 0, r6, c10, c2, 1 @ NMRR + mcr p15, 0, r7, c12, c0, 0 @ VBAR + + LDMDB r8!, {r0,r1} + mcr p15, 0, r0, c15, c0, 1 @ diag + mcr p15, 0, r1, c1, c0, 2 @ cpacr + + LDMDB r8!, {r0, r4} + mcr p15, 2, r0, c0, c0, 0 @ csselr + mcr p15, 0, r4, c15, c0, 0 @ pctlr + + /* Invalid L1-Cache */ + mov r3,#0x0 + mcr p15, 2, r3, c0, c0, 0 @ select L1 Data-cache + mrc p15, 1, r3, c0, c0, 0 @ Read Current Cache Size- + @ Identification Register + + ldr r1,=0x1ff + and r3, r1, r3, LSR #13 @r3 = number of sets in cache + mov r0,#0x0 +way_loop: + mov r1, #0x0 @r1 -> set counter +set_loop: + mov r2, r0, LSL #30 + orr r2, r1, LSL #5 @r2->set/way cache-op format + mcr p15, 0, r2, c7, c6, 2 @Invalid Line descript by r2 + add r1, r1, #1 @increment set counter + + cmp r1, r3 @check if last set is reached... + ble set_loop @if not continue set_loop + add r0,r0, #1 @else increment way counter + + cmp r0,#4 @check if last way is reached + blt way_loop @if not,continue way_loop + + /** + now restore the critial P15 register + restore critial P15 register before MMU Enabled + save CTRL_Register in r0 + save Aux_Ctrl_register in r1 + TTBR0 in r2 + TTBR1 in r3 + TTBCR in r4 + DAC in R5 + **/ + + ADD r10,r10,#0x4 + + LDMIA r10, {r0-r5, r7-r9} + + MCR p15, 0, r2, c2, c0, 0 @ TTBR0 + MCR p15, 0, r3, c2, c0, 1 @ TTBR1 + MCR p15, 0, r4, c2, c0, 2 @ TTBCR + + /** + r0 store the Stored Control register value + **/ + MCR p15, 0, r0, c1, c0, 0 @ turn on MMU, I-cache, etc + instr_sync + mov pc, r9 + +mmu_enalbed: + str r7, [r8] + + mcr p15, 0, r1, c1, c0, 1 @ actlr + mcr p15, 0, r5, c3, c0, 0 @ domain access control reg + +#ifdef CONFIG_CPU_IDLE +#error hilpm_cp_cpuidle_code() not defined +@ bl hilpm_cp_cpuidle_code +#endif + + LDMFD sp!, {r4-r11, pc} +ENDPROC (master_cpu_resume) + + +/* Function: hs_finish_suspend() + * + * This code is copied to Securam for execution, because DDR is set to + * self-refresh mode. Here, final steps before setting master CPU to + * deepsleep are included. + * + * After WFI, master CPU enters deepsleep. All codes after WFI is not + * executed. When master CPU is waken up, master_cpu_resume() will be + * called. Please read description there. + */ + .align 3 +ENTRY (hs_finish_suspend) + instr_sync + /** + STEP1: Protect the DDR Train Address and Traning Data into Syscontrol + We need not do it, it will be config in fastboot + **/ + + /** + config DDR enter self-refresh state + **/ + LDR r0, =DDR_CTRLOR_BASE + LDR r1, =0x01 + STR r1, [r0,#0x4] + + /* check DDR self-fresh status */ +CheckDDREnterSF: + LDR r1, [r0, #0x0] + TST r1, #0x04 + BEQ CheckDDREnterSF + + /** + config DDR PHY enter CKE-Retention status + fastboot code will do opposition relevent operations. + **/ + LDR r4,=REG_BASE_SCTRL + LDR r1,[r4,#0x20C] @SCTRL SCPERCTRL3 register + ORR r1,r1,#0x3 @set sc_ddr_ret_en bit[1:0] to 0x3 + STR r1,[r4,#0x20C] + + /** + Set MDDRC's clock to DDRPHY's input clock + fastboot code will do opposition relevent operations. + **/ + LDR r4,=REG_BASE_PMCTRL + LDR r1,=0x0 + STR r1,[r4,#0xA8] @DDRCLKSEL + +r_wait_mddrc_clk: + LDR r1,[r4,#0xA8] + TST r1,#0x2 + BNE r_wait_mddrc_clk + + /*set ddr clk div*/ @360M + LDR r1, = 0x03 + STR r1, [r4, #0x0AC] +r_waite_mddr_div: + LDR r1, [r4, #0x0AC] + TST r1, #0x20 + BEQ r_waite_mddr_div + + /*ddr clk change to peri PLL*/ + MOV r2, #0x0 + LDR r1, =0x00 + STR r1, [r4, #0x030] +r_wait_ddrcclk_sw: + ADD r2, r2, #0x1 + CMP r2, #0x1000 + BEQ r_wait_ddrcclk_ok + LDR r1, [r4, #0x30] + CMP r1, #0x00 + BNE r_wait_ddrcclk_sw + +r_wait_ddrcclk_ok: + + /*close GPU PLL*/ + LDR r1, =0x00 + STR r1, [r4, #0x028] + + /** + Close LD03 + **/ + + /*enable pmuspi*/ + /*pmuspi clk div 4*/ + LDR r1, =REG_BASE_PCTRL + LDR r4, =0xFF0003 + STR r4, [r1, #0x8] + + /*enable clk*/ + LDR r1, =REG_BASE_SCTRL + LDR r4, =0x2 + STR r4, [r1, #0x40] + + /*undo reset*/ + LDR r4, =0x2 + STR r4, [r1, #0x9C] + + /*close LDO3*/ + LDR r1, =REG_BASE_PMUSPI + LDR r4, [r1, #0x8C] + BIC r4, #0x10 + STR r4, [r1, #0x8C] + + /*disable pmuspi*/ + /*reset*/ + LDR r1, =REG_BASE_SCTRL + LDR r4, =0x2 + STR r4, [r1, #0x98] + + /*disable clk*/ + LDR r4, =0x2 + STR r4, [r1, #0x44] + + + /** + STEP2. Clear intr response mode status register + Try clear intr response mode status first + if the status is cleared, means there has no + intr pending, go dpsleep, else do not configuration + any dpsleep register and go back + **/ + LDR r4,=REG_BASE_SCTRL + LDR r1,=0x0 + STR r1,[r4,#0xc] @clear intr status register + NOP + NOP + /* check if we are still in intr response status */ + /* 2012-2-14 do not care about wakeup intr*/ + @LDR r1,[r4,#0xc] + @TST r1,#0x01 + @BNE Back_from_WFI @go directly to Resume + + /* exit intr response mode */ + LDR r2,[r4,#8] + BIC r2,#1 + STR r2,[r4,#8] + + /** + STEP3 Protect EMMC/NAND Component IO, config WP to LOW CLOSE eMMC/NAND Component LDO + NOW EMMC/NAND Driver do the protection operation, we do nothing here + **/ + + /** + STEP4 config dpsleep register + **/ + LDR r4,=REG_BASE_SCTRL + LDR r1,[r4] + LDR r2,=0x01000007 @BIT24: Enable DpSleep Mode, BIT2|BIT1|BIT0 = 7 + BIC r1,r1,r2 @clear first + LDR r2,=0x01000000 @BIT24: Enable DpSleep Mod=1, + @modctrl(BIT2|BIT1|BIT0) = 000 (Sleep) + ORR r1,r1,r2 @Enable DpSleep Mode,ModeCtrl=sleep + STR r1,[r4] + + /* STEP5 CHIP required, config L2CC for LOWPOWER */ + LDR r4,=REG_BASE_L2CC + LDR r2,[r4,#0xF80] + ORR r1,r2,#0x1 + STR r1,[r4,#0xF80] + + /** + STEP6. CHIP required, configure SCU power status register + cause if we back from wfi, scu power status must be + restore back, we protect the data into r11 + **/ + LDR r4,=REG_BASE_A9PER + LDR r11,[r4,#0x08] @protect the SCU power status to r11 + LDR r1,=0x03030303 @all CPU to Power-off Mode + STR r1,[r4,#0x08] + + /** + STEP7. configure SCU Standby EN + CAUSTION: this configuration suggest to be set before the whole + system start + **/ + LDR R4,=REG_BASE_A9PER + LDR r1,[r4] + ORR r1,r1,#0x20 + STR r1,[r4] + + /** + STEP9 to enable ACP''s clock as a must for SCUIDLE + **/ + LDR R4, =REG_BASE_SCTRL + MOV R1, #0x10000000 @BIT28 for ACP clock enable control + STR R1, [R4,#0x30] @Register SCPEREN1 + + + /** + STEP10 clear ScuRAM Ready FLAG,please refer to OnChipROM Design + FIXME: the FLAG also including the optional information + (Refer to OnChipROM design) + **/ + LDR r4,=SECURAM_CODE_READY_FLAG + LDR r1,=0x0 + STR r1,[r4,#0x0] + + /** + STEP11 Enter WFI + **/ + DSB + WFI + + NOP + NOP + NOP + NOP + +Back_from_WFI: + /** + We are draged back from an interrupt + Caustion: R6 is reserved to store the go-back PHY address + + STEP1: Restore the IO/LDO Configuration of EMMC/NAND Component + WE DO Nothing here + **/ + + /* STEP2: Restore the Securam_CODE_READY FLAG. + * Refer to OnChipROM Design + */ + LDR r4,=SECURAM_CODE_READY_FLAG + LDR r1,[r4] + LDR r2,=0xFFFF + BIC r1,r1,r2 + LDR r2,=0xBEEF @0xBEEF is the SECURAM_CODE_READY_FLAG + ORR r1,r1,r2 + STR r1,[r4,#0x0] + + /** + STEP3: make Sure system in Normal state + if system in SLOW mode, configuration to NORMAL mode + FIXME: Coding here if needed + **/ + LDR r2, =REG_BASE_SCTRL + LDR r1, [r2] + AND r4, r1, #0x4 + CMP r4, #0x4 + BEQ cpunormal + ORR r1, r1, #0x4 + STR r1, [r2] +normalwait: + LDR r1, [r2] + AND r1, r1, #0x78 + CMP r1, #0x20 + BNE normalwait + +cpunormal: + + /** + STEP4: Restore DDR Configuration to Normal + Restore DDR PHY CLK, exit CKE-Retention mode + **/ + + /** change DDR PHY CLK to output mode **/ + /** + FIXME: Remove the comments when fastboot add the + relevent operations. + **/ + LDR r4,=REG_BASE_PMCTRL + LDR r1,=0x1 + STR r1,[r4,#0xA8] @DDRCLKSEL + + /** exit CKE retention mode **/ + LDR r4,=REG_BASE_SCTRL + LDR r1,[r4,#0x20C] @SCTRL SCPERCTRL3 register + BIC r1,r1,#0x3 @set sc_ddr_ret_en bit[1:0] to 0x0 + STR r1,[r4,#0x20C] + + /** + Open LD03 + **/ + + /*enable pmuspi*/ + /*pmuspi clk div 4*/ + LDR r1, =REG_BASE_PCTRL + LDR r4, =0xFF0003 + STR r4, [r1, #0x8] + + /*enable clk*/ + LDR r1, =REG_BASE_SCTRL + LDR r4, =0x2 + STR r4, [r1, #0x40] + + /*undo reset*/ + LDR r4, =0x2 + STR r4, [r1, #0x9C] + + /*open LDO3*/ + LDR r1, =REG_BASE_PMUSPI + LDR r4, [r1, #0x8C] + ORR r4, #0x10 + STR r4, [r1, #0x8C] + + /*disable pmuspi*/ + /*reset*/ + /*LDR r1, =REG_BASE_SCTRL*/ + /*LDR r4, =0x2*/ + /*STR r4, [r1, #0x98]*/ + + /*disable clk*/ + /*LDR r4, =0x2*/ + /*STR r4, [r1, #0x44]*/ + + /*about 100ms*/ + LDR r4, =0x2625A00 +ldo3delay: + SUBS r4, r4, #0x1 + BNE ldo3delay + + /** Config DDR leave self-refresh mode **/ + LDR r0,=DDR_CTRLOR_BASE + LDR r1,=0x00 + STR r1,[r0,#0x4] + + /** check DDR self-refresh status **/ +CheckDDRLeaveSF: + LDR r1, [r0, #0x0] + TST r1, #0x04 + BNE CheckDDRLeaveSF + + /** STEP5 restore SCU CPU power states, which restore in r11 before **/ + LDR r4,=REG_BASE_A9PER + STR r11,[r4,#0x08] @restore the SCU power status from r11 + + /** STEP6 go Back to DDR Address Store in R6 **/ + MOV pc, r6 + +ENDPROC (hs_finish_suspend) + + .ltorg + +.global hi_cpu_godpsleep_ddrbase +hi_cpu_godpsleep_ddrbase: + .word hi_cpu_godpsleep_ddrbase + +.global hi_cpu_godpsleep_phybase +hi_cpu_godpsleep_phybase: + .word hi_cpu_godpsleep_phybase diff --git a/arch/arm/mach-hs/hipm.h b/arch/arm/mach-hs/hipm.h new file mode 100644 index 000000000000..db7059622da7 --- /dev/null +++ b/arch/arm/mach-hs/hipm.h @@ -0,0 +1,183 @@ +/* + * Header for power management related routines + * + * Copyright © 2011-2013 HiSilicon Technologies Co., Ltd. + * http://www.hisilicon.com + * Copyright © 2012-2013 Linaro Ltd. + * http://www.linaro.org + * + * Author: Guodong Xu <guodong.xu@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. + * + */ +#ifndef __MACH_HS_HIPM_H +#define __MACH_HS_HIPM_H + +#include <linux/linkage.h> +#include <linux/init.h> +#include <asm/pgtable.h> + +/* Physical DRAM offset */ +#define K3_PLAT_PHYS_OFFSET UL(0x40000000) + +#define SR_PROTECT_CTX_BUFF_SIZE (2048) +#define PMU_HRST_REG (hs_pmuspi_va_base + (0x87 << 2)) + +#define REG_BASE_SECRAM (0xF8000000) +#define REG_BASE_L2CC (0xFC100000) +#define REG_BASE_A9PER (0xFC000000) +#define REG_BASE_DDRC_CFG (0xFCD00000) +#define REG_BASE_SCTRL (0xFC802000) +#define REG_BASE_PMCTRL (0xFCA08000) +#define REG_BASE_PCTRL (0xFCA09000) +#define REG_BASE_PMUSPI (0xFCC00000) + +/* Define A9 Cluster Address */ +#define A9_SCU_BASE REG_BASE_A9PER +#define A9_PRV_TIMER_BASE (REG_BASE_A9PER + 0x600) +#define A9_GLB_TIMER_BASE (REG_BASE_A9PER + 0x200) + +#define DDR_CTRLOR_BASE (REG_BASE_DDRC_CFG) +#define NAND_CTRLOR_BASE (REG_BASE_NANDC_CFG) + +/* OFFSETs based off SYSCTRL */ +/* MASTER_SR_BACK_PHY_ADDR Store the restore address of CPU0 + */ +#define MASTER_SR_BACK_PHY_ADDR (REG_BASE_SCTRL + 0x308) + +/************************************************************ + SecuRAM Useage Map During Suspend/Resume + + ++---------------------------+ 0xF8000000 +| | Boot-Code(Support S/R operation) +| 2K Bytes | Including Function: +| | DISMMU_IN_SECURAM, +| | ENMMU_IN_SECURAM ++---------------------------+ 0xF8000800 +| 32 Bytes | Prestore area of CTRL_Register/ +| | Aux_Ctrl_register/ +| | TTBR0/TTBR1/TTBCR/DAC of CPU0 ++---------------------------+ 0xF8000820 +| 32 Bytes | Prestore area of CTRL_Register/ +| | Aux_Ctrl_register/ +| | TTBR0/TTBR1/TTBCR/DAC of CPU1 ++---------------------------+ 0xF8000840 +| 32 Bytes | Prestore area of CTRL_Register/ +| | Aux_Ctrl_register/ +| | TTBR0/TTBR1/TTBCR/DAC of CPU2 ++---------------------------+ 0xF8000860 +| 32 Bytes | Prestore area of CTRL_Register/ +| | Aux_Ctrl_register/ +| | TTBR0/TTBR1/TTBCR/DAC of CPU3 ++---------------------------+ 0xF8000880 +| | ++---------------------------+ 0xF8000900 +| 64 Bytes | Store disable_mmu assembly code ++---------------------------+ 0xF8000940 +| 64 Bytes | Store the enable_mmu assembly code ++---------------------------+ 0xF8000980 +| 128 Bytes | Slave CPU entry code +| | ++---------------------------+ 0xF8000A00 +| DeepSleep Code | +| 1K Bytes | Code to make DDR enter +| | self-refresh, and +| | configure the SYSCTRL to +| | make system enter deepsleep ++---------------------------+ 0xF8000E00 +| | +|~~ ~~ ~~ ~~ ~~ ~~ ~~ | + ~~ ~~ ~~ ~~ ~~ ~~ ~ + +A9_PRE_STORE_DATA_ADDR: the Address in Securam, +A9_PRE_STORE_DATA_LEN: the data length of each A9 +DPSLEEP_CODE_ADDR: the address of DeepSleep Code +DPSLEEP_CODE_LEN: the len of DeepSleep Code + +******************************************************************/ + +/* Disable/Enable MMU FUNCTION ADDRESS */ +#define DISMMU_CODE_IN_SECURAM (REG_BASE_SECRAM+0x900) +#define ENMMU_CODE_IN_SECURAM (REG_BASE_SECRAM+0x940) +#define SECURAM_CODE_LEN (0x40) + +/* OFFSETs based off SECRAM */ +#define SLAVE_CPU_ENTRY_CODE_ADDR (REG_BASE_SECRAM + 0x980) +#define SLAVE_CPU_ENTRY_CODE_LEN (0x80) +#define A9_PRE_STORE_DATA_ADDR_OFFSET (0x800) +#define A9_PRE_STORE_DATA_LEN (32) +#define DPSLEEP_CODE_ADDR_OFFSET (0xA00) +#define DPSLEEP_CODE_LEN (0x400) + +#define SECURAM_CODE_READY_FLAG (REG_BASE_SCTRL+0x330) + +/* SUSPEND_STORE_OFFSET_UNIT define + the Data-Length of DDR area to save + A9 context */ +#define SUSPEND_STORE_OFFSET_UNIT (0x200) +#define SUSPEND_STORE_RESEVED_UNIT (0x30) +#define SUSPEND_STACK_ADDR (SUSPEND_STORE_RESEVED_UNIT - 0x4) + + +#define SCU_CONTROL_OFFSET (0x00) +#define SCU_CONFIG_OFFSET (0x04) +#define SCU_POWER_STATE_OFFSET (0x08) +#define SCU_SEC_INVALID_REG_OFFSET (0x0c) +#define SCU_FILTER_START_OFFSET (0x40) +#define SCU_FILTER_END_OFFSET (0x44) +#define SCU_ACCESS_CONTROL_OFFSET (0x50) +#define SCU_NONSEC_CONTROL_OFFSET (0x54) + +#define L2X0_AUX_CTRL 0x104 +#define L2X0_TAG_LATENCY_CTRL 0x108 +#define L2X0_DATA_LATENCY_CTRL 0x10C +#define L2X0_PREFETCH_OFFSET 0xF60 + +#define TIMER_LD (0x0) +#define TIMER_CTL (0x8) + +#define TIM64_CNTLO (0x0) +#define TIM64_CNTHI (0x4) +#define TIM64_CTL (0x8) +#define TIM64_CMPLO (0x10) +#define TIM64_CMPHI (0x14) +#define TIM64_AUTOINC (0x18) + +/* General CPU constants */ +#define MODE_FIQ (0x11) +#define MODE_IRQ (0x12) +#define MODE_SVC (0x13) +#define MODE_ABT (0x17) +#define MODE_UND (0x1B) +#define MODE_SYS (0x1F) +#define I_BIT (0x80) +#define F_BIT (0x40) + +#define TTBRBIT_MASK 0xFFFFC000 +#define TABLE_INDEX_MASK 0xFFF00000 +#define TABLE_ENTRY 0x00000C02 + +#define hisi_v2p(x) ((x) - PAGE_OFFSET + K3_PLAT_PHYS_OFFSET) +#define hisi_p2v(x) ((x) - K3_PLAT_PHYS_OFFSET + PAGE_OFFSET) + +/* TCXO stable time configuation */ +#define SCXTALCTRL_CFG_VAL_005MS (0x1FFFF5D0) /*5ms*/ +#define SCXTALCTRL_CFG_VAL_200MS (0x1FFE6660) /*200ms*/ + +#ifndef __ASSEMBLY__ +extern asmlinkage void __iomem *hs_finish_suspend(void); +extern asmlinkage void __iomem *master_cpu_resume(void); +extern asmlinkage void __iomem *get_ttbr0(void); +extern asmlinkage void hilpm_cpu_godpsleep(void); +extern asmlinkage void hilpm_cp_securam_code(void __iomem *); +extern asmlinkage void hilpm_cp_cpuidle_code(void); +extern unsigned long hi_cpu_godpsleep_ddrbase; +extern unsigned long hi_cpu_godpsleep_phybase; +#endif + +#endif /* #ifndef __MACH_HS_HIPM_H */ diff --git a/arch/arm/mach-hs/hotplug.c b/arch/arm/mach-hs/hotplug.c new file mode 100644 index 000000000000..c67f8a22a890 --- /dev/null +++ b/arch/arm/mach-hs/hotplug.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2013 Linaro Ltd. + * Copyright (c) 2013 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ +#include <linux/cpu.h> +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <asm/cacheflush.h> +#include <asm/smp_plat.h> +#include "core.h" + +enum { + HI3620_CTRL, + HI3716_CTRL, +}; + +static void __iomem *ctrl_base; +static int id; + +void hs_set_cpu(int cpu, bool enable) +{ + u32 val = 0; + + if (!ctrl_base) + return; + + if (id == HI3620_CTRL) { + if (enable) { + /* MTCMOS */ + writel_relaxed((0x01 << (cpu + 3)), ctrl_base + 0xD0); + writel_relaxed((0x1011 << cpu), ctrl_base + 0x414); + writel_relaxed((0x401011 << cpu), ctrl_base + 0x410); + + /* ISO disable */ + writel((0x01 << (cpu + 3)), ctrl_base + 0x0C4); + + /* WFI Mask */ + val = readl(ctrl_base + 0x200); + val &= ~(0x1 << (cpu+28)); + writel(val, ctrl_base + 0x200); + + /* Enable core */ + writel_relaxed((0x01 << cpu), ctrl_base + 0xf4); + /* Unreset */ + writel_relaxed((0x401011 << cpu), ctrl_base + 0x414); + } else { + /* iso enable */ + writel_relaxed((0x01 << (cpu + 3)), ctrl_base + 0xC0); + + /* MTCMOS */ + writel_relaxed((0x01 << (cpu + 3)), ctrl_base + 0xD4); + + /* wfi mask */ + val = readl_relaxed(ctrl_base + 0x200); + val |= (0x1 << (cpu+28)); + writel_relaxed(val, ctrl_base + 0x200); + + /* disable core*/ + writel_relaxed((0x01 << cpu), ctrl_base + 0xf8); + /* Reset */ + writel_relaxed((0x401011 << cpu), ctrl_base + 0x410); + } + } else if (id == HI3716_CTRL) { + if (enable) { + /* power on cpu1 */ + val = readl_relaxed(ctrl_base + 0x1000); + val &= ~(0x1 << 8); + val |= (0x1 << 7); + val &= ~(0x1 << 3); + writel_relaxed(val, ctrl_base + 0x1000); + + /* unreset */ + val = readl_relaxed(ctrl_base + 0x50); + val &= ~(0x1 << 17); + writel_relaxed(val, ctrl_base + 0x50); + } else { + /* power down cpu1 */ + val = readl_relaxed(ctrl_base + 0x1000); + val &= ~(0x1 << 8); + val |= (0x1 << 7); + val |= (0x1 << 3); + writel_relaxed(val, ctrl_base + 0x1000); + + /* reset */ + val = readl_relaxed(ctrl_base + 0x50); + val |= (0x1 << 17); + writel_relaxed(val, ctrl_base + 0x50); + } + } +} + +void __init hs_hotplug_init(void) +{ + struct device_node *node; + + node = of_find_compatible_node(NULL, NULL, "hisilicon,cpuctrl"); + if (node) { + ctrl_base = of_iomap(node, 0); + id = HI3716_CTRL; + return; + } + node = of_find_compatible_node(NULL, NULL, "hisilicon,sctrl"); + if (node) { + ctrl_base = of_iomap(node, 0); + id = HI3620_CTRL; + return; + } +} + +static inline void cpu_enter_lowpower(void) +{ + unsigned int v; + + flush_cache_all(); + asm volatile( + /* + * Turn off coherency and L1 D-cache + */ + " mrc p15, 0, %0, c1, c0, 1\n" + " bic %0, %0, #0x40\n" + " mcr p15, 0, %0, c1, c0, 1\n" + " mrc p15, 0, %0, c1, c0, 0\n" + " bic %0, %0, #0x04\n" + " mcr p15, 0, %0, c1, c0, 0\n" + : "=&r" (v) + : "r" (0) + : "cc"); +} + +void hs_cpu_die(unsigned int cpu) +{ + cpu_enter_lowpower(); + hs_set_cpu_jump(cpu, phys_to_virt(0)); + cpu_do_idle(); + + /* We should have never returned from idle */ + panic("cpu %d unexpectedly exit from shutdown\n", cpu); +} + +int hs_cpu_kill(unsigned int cpu) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(50); + + while (hs_get_cpu_jump(cpu)) + if (time_after(jiffies, timeout)) + return 0; + hs_set_cpu(cpu, false); + return 1; +} diff --git a/arch/arm/mach-hs/hs-dt.c b/arch/arm/mach-hs/hs-dt.c new file mode 100644 index 000000000000..774ace66cc22 --- /dev/null +++ b/arch/arm/mach-hs/hs-dt.c @@ -0,0 +1,82 @@ +/* + * (Hisilicon's Hi36xx/Hi37xx SoC based) flattened device tree enabled machine + * + * Copyright (c) 2012-2013 Linaro Ltd. + * + * 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/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/irqchip.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> + +#include <asm/hardware/arm_timer.h> +#include <asm/hardware/cache-l2x0.h> +#include <asm/hardware/timer-sp.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/time.h> + +#include <linux/clocksource.h> +#include "core.h" + +static struct of_device_id hs_l2cache_match[] __initdata = { + { .compatible = "arm,pl310-cache", }, + {} +}; + +static void __init hi3xxx_timer_init(void) +{ + struct device_node *node = NULL; + int ret; + u32 data[2]; + + hs_map_io(); + hs_hotplug_init(); + of_clk_init(NULL); + clocksource_of_init(); + + node = of_find_matching_node(NULL, hs_l2cache_match); + WARN_ON(!node); + if (!node) { + pr_err("Failed to find l2cache\n"); + return; + } + ret = of_property_read_u32_array(node, "hisilicon,l2cache-aux", + &data[0], 2); + if (ret < 0) { + data[0] = 0; + data[1] = ~0UL; + } + l2x0_of_init(data[0], data[1]); +} + +static void __init hs_init(void) +{ + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); +} + +static const char *hs_compat[] __initdata = { + "hisilicon,hi3620-hi4511", + "hisilicon,hi3716", + NULL, +}; + +DT_MACHINE_START(HS_DT, "Hisilicon Hi36xx/Hi37xx (Flattened Device Tree)") + /* Maintainer: Haojian Zhuang <haojian.zhuang@linaro.org> */ + .map_io = debug_ll_io_init, + .init_irq = irqchip_init, + .init_time = hi3xxx_timer_init, + .init_machine = hs_init, + .dt_compat = hs_compat, + .smp = smp_ops(hs_smp_ops), + .restart = hs_restart, +MACHINE_END diff --git a/arch/arm/mach-hs/lowpmregs.c b/arch/arm/mach-hs/lowpmregs.c new file mode 100644 index 000000000000..d1c84edcc57e --- /dev/null +++ b/arch/arm/mach-hs/lowpmregs.c @@ -0,0 +1,150 @@ +/* + * Routines for PMU/Hi6421 suspend/resume. Hi4511 board specific. + * + * Copyright © 2011-2013 HiSilicon Technologies Co., Ltd. + * http://www.hisilicon.com + * Copyright © 2012-2013 Linaro Ltd. + * http://www.linaro.org + * + * Author: Guodong Xu <guodong.xu@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. + * + * Note: Routines defined in this file is for Hi4511 board specific, and will be + * called during system suspend to RAM. It should be finally moved to Hi6421 + * driver's pm routines. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/console.h> +#include <asm/hardware/arm_timer.h> +#include <linux/regulator/consumer.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include "core.h" + +/* function: pmuspi_enable + * description: + * enable pmu clk. + */ +static void pmuspi_enable(void) +{ + /*set clk div*/ + writel(0xFF0003, (hs_pctrl_va_base + 0x8)); + + /*enable clk*/ + writel(1<<1, (hs_sctrl_base + 0x40)); + + /*undo reset*/ + writel(1<<1, (hs_sctrl_base + 0x9C)); +} + +/* function: pmuspi_disable + * description: + * disable pmu clk. + */ +static void pmuspi_disable(void) +{ + /*reset*/ + writel(1<<1, (hs_sctrl_base + 0x98)); + + /*disable clk*/ + writel(1<<1, (hs_sctrl_base + 0x44)); +} + +#define PMUSPI_REG(x) (hs_pmuspi_va_base + ((x)<<2)) + +struct pmuregs { + unsigned char ucoffset; + char cval; + char old_val; + char cmask; +}; + +#define PMU_LOW(x, y, z) { .ucoffset = (x),\ + .cval = (y), \ + .cmask = (z), \ + .old_val = 0, \ + } + +static struct pmuregs pmuregs_lookups[] = { + /*close LDO0 */ + PMU_LOW(0x20, 0x00, 0x10), + + /*w 35 0, emmc rst2_n output low.*/ + PMU_LOW(0x35, 0x00, 0x01), + + /*close ldo13*/ + PMU_LOW(0x2D, 0x30, 0x30), + + /*w 25 31 LDO5 ECO mode*/ + /*PMU_LOW(0x25, 0x20, 0x20),*/ + + /*w 26 34 LDO6 ECO mode*/ + /*PMU_LOW(0x26, 0x20, 0x20),*/ + + /*w 4e 20 close over temperature protect*/ + PMU_LOW(0x4E, 0x00, 0x01), + + /*w 52 00 close backup battery charging*/ + PMU_LOW(0x52, 0x00, 0x01), + + /*w 14 11 BUCK4 Sleep*/ + PMU_LOW(0x14, 0x11, 0x11), + + /*w 16 11 BUCK5 Sleep*/ + PMU_LOW(0x16, 0x11, 0x11), + + /*w 8f 08 sleep*/ + /*PMU_LOW(0x8F, 0x08, 0x07),*/ +}; + + +/* function: pmulowpower + * description: + * configure pmu low power state. + */ +void pmulowpower(int isuspend) +{ + int i = 0; + int ilen = ARRAY_SIZE(pmuregs_lookups); + unsigned uregv = 0; + + pr_info("[%s] %d enter.\n", __func__, __LINE__); + + pmuspi_enable(); + + if (1 == isuspend) { + for (i = 0; i < ilen; i++) { + uregv = readl(PMUSPI_REG(pmuregs_lookups[i].ucoffset)); + pmuregs_lookups[i].old_val = uregv; + uregv &= ~pmuregs_lookups[i].cmask; + uregv |= pmuregs_lookups[i].cval; + writel(uregv, PMUSPI_REG(pmuregs_lookups[i].ucoffset)); + } + } else { + for (i = (ilen - 1); i >= 0; i--) { + uregv = readl(PMUSPI_REG(pmuregs_lookups[i].ucoffset)); + uregv &= ~pmuregs_lookups[i].cmask; + uregv |= pmuregs_lookups[i].old_val; + writel(uregv, PMUSPI_REG(pmuregs_lookups[i].ucoffset)); + } + } + + if (1 == isuspend) { + pmuspi_disable(); + /* here is an workround way to delay 40ms + * make sure LDO0 is poweroff very clean + */ + mdelay(40); + } +} diff --git a/arch/arm/mach-hs/platsmp.c b/arch/arm/mach-hs/platsmp.c new file mode 100644 index 000000000000..6a086307abbf --- /dev/null +++ b/arch/arm/mach-hs/platsmp.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2013 Linaro Ltd. + * Copyright (c) 2013 Hisilicon Limited. + * Based on platsmp.c, Copyright (C) 2002 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ +#include <linux/smp.h> +#include <linux/io.h> +#include <asm/smp_scu.h> + +#include "core.h" + +static void __init hs_smp_prepare_cpus(unsigned int max_cpus) +{ + unsigned long base; + void __iomem *scu_base; + + if (scu_a9_has_base()) { + base = scu_a9_get_base(); + scu_base = ioremap(base, SZ_4K); + if (!scu_base) { + pr_err("ioremap(scu_base) failed\n"); + return; + } + scu_enable(scu_base); + iounmap(scu_base); + } +} + +static int hs_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + hs_set_cpu(cpu, true); + hs_set_cpu_jump(cpu, secondary_startup); + arch_send_wakeup_ipi_mask(cpumask_of(cpu)); + return 0; +} + +struct smp_operations hs_smp_ops __initdata = { + .smp_prepare_cpus = hs_smp_prepare_cpus, + .smp_boot_secondary = hs_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_die = hs_cpu_die, + .cpu_kill = hs_cpu_kill, +#endif +}; diff --git a/arch/arm/mach-hs/pm.c b/arch/arm/mach-hs/pm.c new file mode 100644 index 000000000000..efbca0e555b8 --- /dev/null +++ b/arch/arm/mach-hs/pm.c @@ -0,0 +1,495 @@ +/* + * Power Management suspend/resume routines for HiSilicon platform + * + * Copyright © 2011-2013 HiSilicon Technologies Co., Ltd. + * http://www.hisilicon.com + * Copyright © 2012-2013 Linaro Ltd. + * http://www.linaro.org + * + * Author: Guodong Xu <guodong.xu@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. + * + */ + +#define CONFIG_HSK3_RTC_WAKEUP +#define CONFIG_HSK3_CONSOLE_WORKAROUND + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/pm.h> +#include <linux/suspend.h> +#include <linux/cpu.h> +#include <linux/notifier.h> +#include <linux/reboot.h> +#include <asm/memory.h> +#include <linux/delay.h> +#include <linux/suspend.h> +#include <asm/mach/time.h> +#include <linux/slab.h> +#include <asm/io.h> +#include <linux/clk.h> +#include <asm/cacheflush.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/of_gpio.h> +#include <linux/cpu_pm.h> +#include <asm/fncpy.h> +#include "core.h" +#include "hipm.h" +#ifdef CONFIG_HSK3_RTC_WAKEUP +#include <linux/irqchip/arm-gic.h> +#include <linux/irq.h> +#endif +#ifdef CONFIG_HSK3_CONSOLE_WORKAROUND +#include <linux/console.h> +#endif + +/* resources in power management */ +struct iomap_resource { + struct resource *res; + void __iomem *virt_addr; +}; + +struct pm_resources { + struct iomap_resource pmu_spi; + struct iomap_resource secram; + struct iomap_resource pctrl; + struct iomap_resource a9_per; +#ifdef CONFIG_HSK3_CONSOLE_WORKAROUND + struct iomap_resource uart0; + struct iomap_resource io; +#endif +}; + +struct pm_resources hs_pm_resources; +void __iomem *timer0_base_addr; +void __iomem *hs_secram_va_base; +void __iomem *hs_a9per_va_base; +void __iomem *hs_pctrl_va_base; +void __iomem *hs_pmuspi_va_base; + +#ifdef CONFIG_HSK3_CONSOLE_WORKAROUND +static void __iomem *hs_uart0_va_base; +static void __iomem *hs_io_va_base; + +/* re-init debug uart */ +static void debuguart_reinit(void) +{ + void __iomem *usctrl_base = hs_sctrl_base; + void __iomem *uuart_base = hs_uart0_va_base; + void __iomem *io_base = hs_io_va_base; + unsigned int uregv = 0; + + /* Config necessary IOMG configuration */ + writel(0, (io_base + 0xF4)); + + /* config necessary IOCG configuration */ + writel(0, (io_base + 0xA08)); + writel(0, (io_base + 0xA0C)); + + /* disable clk */ + uregv = 0x10000; + writel(uregv, (usctrl_base + 0x44)); + + /* select 26MHz clock */ + uregv = (1 << 23); + writel(uregv, (usctrl_base + 0x100)); + + /* enable clk */ + uregv = 0x10000; + writel(uregv, (usctrl_base + 0x40)); + + /* disable recieve and send */ + uregv = 0x0; + writel(uregv, (uuart_base + 0x30)); + + /* enable FIFO */ + uregv = 0x70; + writel(uregv, (uuart_base + 0x2c)); + + /* set baudrate */ + uregv = 0xE; + writel(uregv, (uuart_base + 0x24)); + + uregv = 0x7; + writel(uregv, (uuart_base + 0x28)); + + /* clear buffer */ + uregv = readl(uuart_base); + + /* enable FIFO */ + uregv = 0x70; + writel(uregv, (uuart_base + 0x2C)); + + /* set FIFO depth */ + uregv = 0x10A; + writel(uregv, (uuart_base + 0x34)); + + uregv = 0x50; + writel(uregv, (uuart_base + 0x38)); + + /* enable uart trans */ + uregv = 0xF01; + writel(uregv, (uuart_base + 0x30)); +} +#endif /* CONFIG_HSK3_CONSOLE_WORKAROUND */ + +/* power-off GPIO pin handle */ +static int pmu_power_off_handle; + +/* system power off func */ +static void hisik3_power_off(void) +{ + int ret; + + ret = gpio_request(pmu_power_off_handle, 0); + if (ret != 0) + pr_emerg("request PMU_POWER_OFF gpio error:%d\n", ret); + + /* clear HRST_REG */ + writel(0, PMU_HRST_REG); + + while (1) { + pr_emerg("system power off now\n"); + + gpio_direction_output(pmu_power_off_handle, 0); + gpio_set_value(pmu_power_off_handle, 0); + + msleep(1000); + } + + gpio_free(pmu_power_off_handle); +} + +static int wrapper_hs_godpsleep(void) +{ + + hilpm_cpu_godpsleep(); + +#ifdef CONFIG_HSK3_CONSOLE_WORKAROUND + /* debuguart needs to be reinit when "no_console_suspend" is set. + */ + if (!console_suspend_enabled) + debuguart_reinit(); +#endif + + return 0; +} + +static int hisik3_pm_enter(suspend_state_t state) +{ + void __iomem *addr; + unsigned long flage = 0; + + pr_notice("[PM] Enter sleep, in %s()\n", __func__); + + switch (state) { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + break; + default: + return -EINVAL; + } + + /* store master cpu resume addree into SCTRL */ + addr = (MASTER_SR_BACK_PHY_ADDR - REG_BASE_SCTRL) + hs_sctrl_base; + writel(hisi_v2p(master_cpu_resume), addr); + + /* copy securam code */ + fncpy(hs_secram_va_base + DPSLEEP_CODE_ADDR_OFFSET, + &hs_finish_suspend, DPSLEEP_CODE_LEN); + + local_irq_save(flage); + +#ifdef CONFIG_CACHE_L2X0 + outer_flush_all(); + outer_disable(); +#endif + /* config pmu low power */ + pmulowpower(1); + + /* Entering deep sleep */ + wrapper_hs_godpsleep(); + + /*PMU regs restore*/ + pmulowpower(0); + +#ifdef CONFIG_CACHE_L2X0 + outer_resume(); +#endif + + flush_cache_all(); + + local_irq_restore(flage); + + pr_notice("[PM] Restore OK\n"); + + return 0; +} + +static const struct platform_suspend_ops hisik3_pm_ops = { + .enter = hisik3_pm_enter, + .valid = suspend_valid_only_mem, +}; + +static int hs_pm_get_resources(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct resource *res; + int ret = 0; + + /* get the base address of pmu_spi */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENOMEM; + dev_err(dev, "platform_get_resource 0 err, ret=%d\n", ret); + goto error_res_1; + } + hs_pm_resources.pmu_spi.res = res; + hs_pm_resources.pmu_spi.virt_addr = ioremap(res->start, + resource_size(res)); + if (!hs_pm_resources.pmu_spi.virt_addr) { + ret = -ENOMEM; + dev_err(dev, "cannot ioremap pmu_spi register memory\n"); + goto error_res_1; + } + hs_pmuspi_va_base = hs_pm_resources.pmu_spi.virt_addr; + + /* get the base address of secram */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + ret = -ENOMEM; + dev_err(dev, "platform_get_resource 1 err, ret=%d\n", ret); + goto error_res_2; + } + hs_pm_resources.secram.res = res; + hs_pm_resources.secram.virt_addr = ioremap(res->start, + resource_size(res)); + if (!hs_pm_resources.secram.virt_addr) { + ret = -ENOMEM; + dev_err(dev, "cannot ioremap secram register memory\n"); + goto error_res_2; + } + hs_secram_va_base = hs_pm_resources.secram.virt_addr; + + /* get the base address of pctrl */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!res) { + ret = -ENOMEM; + dev_err(dev, "platform_get_resource 2 err, ret=%d\n", ret); + goto error_res_3; + } + hs_pm_resources.pctrl.res = res; + hs_pm_resources.pctrl.virt_addr = ioremap(res->start, + resource_size(res)); + if (!hs_pm_resources.pctrl.virt_addr) { + ret = -ENOMEM; + dev_err(dev, "cannot ioremap pctrl register memory\n"); + goto error_res_3; + } + hs_pctrl_va_base = hs_pm_resources.pctrl.virt_addr; + + /* get the base address of a9_per */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 3); + if (!res) { + ret = -ENOMEM; + dev_err(dev, "platform_get_resource 3 err, ret=%d\n", ret); + goto error_res_4; + } + hs_pm_resources.a9_per.res = res; + hs_pm_resources.a9_per.virt_addr = ioremap(res->start, + resource_size(res)); + if (!hs_pm_resources.a9_per.virt_addr) { + ret = -ENOMEM; + dev_err(dev, "cannot ioremap a9_per register memory\n"); + goto error_res_4; + } + hs_a9per_va_base = hs_pm_resources.a9_per.virt_addr; + +#ifdef CONFIG_HSK3_CONSOLE_WORKAROUND + /* get the base address of uart0 */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 4); + if (!res) { + ret = -ENOMEM; + dev_err(dev, "platform_get_resource 4 err, ret=%d\n", ret); + goto error_res_5; + } + hs_pm_resources.uart0.res = res; + hs_pm_resources.uart0.virt_addr = ioremap(res->start, + resource_size(res)); + if (!hs_pm_resources.uart0.virt_addr) { + ret = -ENOMEM; + dev_err(dev, "cannot ioremap uart0 register memory\n"); + goto error_res_5; + } + hs_uart0_va_base = hs_pm_resources.uart0.virt_addr; + + /* get the base address of io */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 5); + if (!res) { + ret = -ENOMEM; + dev_err(dev, "platform_get_resource 5 err, ret=%d\n", ret); + goto error_res_6; + } + hs_pm_resources.io.res = res; + hs_pm_resources.io.virt_addr = ioremap(res->start, + resource_size(res)); + if (!hs_pm_resources.io.virt_addr) { + ret = -ENOMEM; + dev_err(dev, "cannot ioremap io register memory\n"); + goto error_res_6; + } + hs_io_va_base = hs_pm_resources.io.virt_addr; +#endif /* CONFIG_HSK3_CONSOLE_WORKAROUND */ + + /* get pmu_power_off GPIO */ + pmu_power_off_handle = of_get_named_gpio(np, "pmu-power-hold-gpios", 0); + + if (!gpio_is_valid(pmu_power_off_handle)) { + dev_err(dev, "Fail to get pmu_power_off gpio, err=%d\n", + pmu_power_off_handle); + ret = pmu_power_off_handle; + goto error_pmu_gpio; + } + + goto get_res_end; + +error_pmu_gpio: +#ifdef CONFIG_HSK3_CONSOLE_WORKAROUND + iounmap(hs_pm_resources.io.virt_addr); +error_res_6: + iounmap(hs_pm_resources.uart0.virt_addr); +error_res_5: +#endif + iounmap(hs_pm_resources.a9_per.virt_addr); +error_res_4: + iounmap(hs_pm_resources.pctrl.virt_addr); +error_res_3: + iounmap(hs_pm_resources.secram.virt_addr); +error_res_2: + iounmap(hs_pm_resources.pmu_spi.virt_addr); +error_res_1: + +get_res_end: + return ret; +} + +static int hs_pm_free_resources(struct platform_device *pdev) +{ +#ifdef CONFIG_HSK3_CONSOLE_WORKAROUND + iounmap(hs_pm_resources.io.virt_addr); + iounmap(hs_pm_resources.uart0.virt_addr); +#endif /* CONFIG_HSK3_CONSOLE_WORKAROUND */ + iounmap(hs_pm_resources.a9_per.virt_addr); + iounmap(hs_pm_resources.pctrl.virt_addr); + iounmap(hs_pm_resources.secram.virt_addr); + iounmap(hs_pm_resources.pmu_spi.virt_addr); + return 0; +} + +#ifdef CONFIG_HSK3_RTC_WAKEUP +static int hs_set_wake(struct irq_data *data, unsigned int state) +{ + return 0; +} +#endif + +static int hs_pm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + void __iomem *addr; + int ret = 0; + + /* alloc memmory for suspend */ + hi_cpu_godpsleep_ddrbase = (unsigned long)kzalloc( + (SR_PROTECT_CTX_BUFF_SIZE), GFP_DMA|GFP_KERNEL); + if (!hi_cpu_godpsleep_ddrbase) { + dev_err(dev, "kzalloc err for hi_cpu_godpsleep_ddrbase!\n"); + return -ENOMEM; + } + + hi_cpu_godpsleep_phybase = hisi_v2p(hi_cpu_godpsleep_ddrbase); + + /* get resources, parse from device tree */ + ret = hs_pm_get_resources(pdev); + if (ret) { + dev_err(dev, "hs_pm_get_resources err, ret=%d\n", ret); + goto error_res; + } + +#ifdef CONFIG_CPU_IDLE + /* hilpm_cpu_godpsleep() use disable_mmu() and enable_mmu() + * which running in securam. + * we copy the two functions during init phase + */ + hilpm_cp_cpuidle_code(); +#endif + + addr = hs_sctrl_base + 0x10; + writel(SCXTALCTRL_CFG_VAL_200MS, addr); + + suspend_set_ops(&hisik3_pm_ops); + + /* power off function */ + pm_power_off = hisik3_power_off; + +#ifdef CONFIG_HSK3_RTC_WAKEUP + /* set wakeup funciton */ + gic_arch_extn.irq_set_wake = hs_set_wake; +#endif + + goto probe_end; + +error_res: + kfree((void *)hi_cpu_godpsleep_ddrbase); +probe_end: + return ret; +} + +static int hs_pm_remove(struct platform_device *pdev) +{ + hs_pm_free_resources(pdev); + kfree((void *)hi_cpu_godpsleep_ddrbase); + return 0; +} + +static struct of_device_id of_hs_pm_match_tbl[] = { + { + .compatible = "hisilicon,hs-power-management", + }, + { /* end */ } +}; + +static struct platform_driver hs_pm_driver = { + .driver = { + .name = "hs_pm", + .owner = THIS_MODULE, + .of_match_table = of_hs_pm_match_tbl, + }, + .probe = hs_pm_probe, + .remove = hs_pm_remove, +}; + +static int __init hs_pm_init(void) +{ + return platform_driver_register(&hs_pm_driver); +} +module_init(hs_pm_init); + +static void __exit hs_pm_exit(void) +{ + platform_driver_unregister(&hs_pm_driver); +} +module_exit(hs_pm_exit); + +MODULE_AUTHOR("Guodong Xu <guodong.xu@linaro.org>"); +MODULE_DESCRIPTION("HiSilicon power management driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-hs/system.c b/arch/arm/mach-hs/system.c new file mode 100644 index 000000000000..64cfe34cef89 --- /dev/null +++ b/arch/arm/mach-hs/system.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013 Linaro Ltd. + * Copyright (c) 2013 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> + +#include <asm/proc-fns.h> +#include <asm/smp_plat.h> + +#include "core.h" + +void __iomem *hs_sctrl_base; +static int hs_smp_reg; +static int hs_resume_reg; +static int hs_reboot_reg; + +void hs_map_io(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "hisilicon,sctrl"); + if (np) { + hs_sctrl_base = of_iomap(np, 0); + if (!hs_sctrl_base) + pr_err("of_iomap(sctrl_base) failed\n"); + of_property_read_u32(np, "smp_reg", &hs_smp_reg); + of_property_read_u32(np, "resume_reg", &hs_resume_reg); + of_property_read_u32(np, "reboot_reg", &hs_reboot_reg); + } +} + +void hs_set_cpu_jump(int cpu, void *jump_addr) +{ + int offset = hs_smp_reg; + + cpu = cpu_logical_map(cpu); + if (cpu > 0) + offset += 0x04 * (cpu - 1); + writel_relaxed(virt_to_phys(jump_addr), hs_sctrl_base + offset); +} + +int hs_get_cpu_jump(int cpu) +{ + int offset = hs_smp_reg; + + cpu = cpu_logical_map(cpu); + if (cpu > 0) + offset += 0x04 * (cpu - 1); + return readl_relaxed(hs_sctrl_base + offset); +} + +void hs_restart(enum reboot_mode mode, const char *cmd) +{ + writel_relaxed(0xdeadbeef, hs_sctrl_base + hs_reboot_reg); + + while (1) + cpu_do_idle(); +} diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 279407a36391..b16766c5baad 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -95,4 +95,5 @@ config CLK_PPC_CORENET endmenu +source "drivers/clk/hisilicon/Kconfig" source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 7b111062ccba..04994da8742a 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o +obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/ obj-$(CONFIG_ARCH_MXS) += mxs/ obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/ obj-$(CONFIG_PLAT_SPEAR) += spear/ diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig new file mode 100644 index 000000000000..344ddb764c1f --- /dev/null +++ b/drivers/clk/hisilicon/Kconfig @@ -0,0 +1,3 @@ +config HI3xxx_CLK_CORE + bool "Core clock driver of Hi3xxx Soc" + default y if COMMON_CLK diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile new file mode 100644 index 000000000000..682b8dc12b04 --- /dev/null +++ b/drivers/clk/hisilicon/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_HI3xxx_CLK_CORE) += clk-hi3xxx.o clk-hi3716.o diff --git a/drivers/clk/hisilicon/clk-hi3716.c b/drivers/clk/hisilicon/clk-hi3716.c new file mode 100644 index 000000000000..887ffe90fba5 --- /dev/null +++ b/drivers/clk/hisilicon/clk-hi3716.c @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2013 Linaro Ltd. + * Copyright (c) 2013 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> + +static DEFINE_SPINLOCK(_lock); + +static void __iomem *clk_base; + +struct hi3716_clk { + struct clk_gate gate; + void __iomem *reg; + u8 reset_bit; +}; + +#define MAX_NUMS 10 + +static struct hi3716_clk *to_clk_hi3716(struct clk_hw *hw) +{ + return container_of(hw, struct hi3716_clk, gate.hw); +} + +static void __init hi3716_map_io(void) +{ + struct device_node *node; + + if (clk_base) + return; + + node = of_find_compatible_node(NULL, NULL, "hisilicon,clkbase"); + if (node) + clk_base = of_iomap(node, 0); + WARN_ON(!clk_base); +} + +static int hi3716_clkgate_prepare(struct clk_hw *hw) +{ + struct hi3716_clk *clk = to_clk_hi3716(hw); + unsigned long flags = 0; + u32 reg; + + spin_lock_irqsave(&_lock, flags); + + reg = readl_relaxed(clk->reg); + reg &= ~BIT(clk->reset_bit); + writel_relaxed(reg, clk->reg); + + spin_unlock_irqrestore(&_lock, flags); + + return 0; +} + +static void hi3716_clkgate_unprepare(struct clk_hw *hw) +{ + struct hi3716_clk *clk = to_clk_hi3716(hw); + unsigned long flags = 0; + u32 reg; + + spin_lock_irqsave(&_lock, flags); + + reg = readl_relaxed(clk->reg); + reg |= BIT(clk->reset_bit); + writel_relaxed(reg, clk->reg); + + spin_unlock_irqrestore(&_lock, flags); +} + +static struct clk_ops hi3716_clkgate_ops = { + .prepare = hi3716_clkgate_prepare, + .unprepare = hi3716_clkgate_unprepare, +}; + +void __init hi3716_clkgate_setup(struct device_node *node) +{ + struct clk *clk; + struct hi3716_clk *p_clk; + struct clk_init_data init; + const char *parent_name; + u32 array[3]; /* reg, enable_bit, reset_bit */ + int err; + + hi3716_map_io(); + err = of_property_read_u32_array(node, "gate-reg", &array[0], 3); + if (WARN_ON(err)) + return; + + err = of_property_read_string(node, "clock-output-names", &init.name); + if (WARN_ON(err)) + return; + + p_clk = kzalloc(sizeof(*p_clk), GFP_KERNEL); + if (WARN_ON(!p_clk)) + return; + + hi3716_clkgate_ops.enable = clk_gate_ops.enable; + hi3716_clkgate_ops.disable = clk_gate_ops.disable; + hi3716_clkgate_ops.is_enabled = clk_gate_ops.is_enabled; + + init.ops = &hi3716_clkgate_ops; + init.flags = CLK_SET_RATE_PARENT; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = &parent_name; + init.num_parents = 1; + + p_clk->reg = p_clk->gate.reg = clk_base + array[0]; + p_clk->gate.bit_idx = array[1]; + p_clk->gate.flags = 0; + p_clk->gate.lock = &_lock; + p_clk->gate.hw.init = &init; + p_clk->reset_bit = array[2]; + + clk = clk_register(NULL, &p_clk->gate.hw); + if (WARN_ON(IS_ERR(clk))) { + kfree(p_clk); + return; + } + + of_clk_add_provider(node, of_clk_src_simple_get, clk); +} + +static void __init hi3716_clkmux_setup(struct device_node *node) +{ + int num = 0, err; + void __iomem *reg; + unsigned int shift, width; + u32 array[3]; /* reg, mux_shift, mux_width */ + u32 *table = NULL; + const char *clk_name = node->name; + const char *parents[MAX_NUMS]; + struct clk *clk; + + hi3716_map_io(); + err = of_property_read_string(node, "clock-output-names", &clk_name); + if (WARN_ON(err)) + return; + + err = of_property_read_u32_array(node, "mux-reg", &array[0], 3); + if (WARN_ON(err)) + return; + + reg = clk_base + array[0]; + shift = array[1]; + width = array[2]; + + while ((num < MAX_NUMS) && + ((parents[num] = of_clk_get_parent_name(node, num)) != NULL)) + num++; + if (!num) + return; + + table = kzalloc(sizeof(u32 *) * num, GFP_KERNEL); + if (WARN_ON(!table)) + return; + + err = of_property_read_u32_array(node, "mux-table", table, num); + if (WARN_ON(err)) + goto err; + + clk = clk_register_mux_table(NULL, clk_name, parents, num, + CLK_SET_RATE_PARENT, reg, shift, BIT(width) - 1, + 0, table, &_lock); + if (WARN_ON(IS_ERR(clk))) + goto err; + + clk_register_clkdev(clk, clk_name, NULL); + of_clk_add_provider(node, of_clk_src_simple_get, clk); + return; + +err: + kfree(table); + return; +} + +static void __init hi3716_fixed_pll_setup(struct device_node *node) +{ + const char *clk_name, *parent_name; + struct clk *clks[MAX_NUMS]; + u32 rate[MAX_NUMS]; + struct clk_onecell_data *clk_data; + int i, err, nums = 0; + + nums = of_property_count_strings(node, "clock-output-names"); + if (WARN_ON((nums < 0) || (nums > MAX_NUMS))) + return; + + err = of_property_read_u32_array(node, "clock-frequency", + &rate[0], nums); + WARN_ON(err); + + parent_name = of_clk_get_parent_name(node, 0); + + for (i = 0; i < nums; i++) { + err = of_property_read_string_index(node, "clock-output-names", + i, &clk_name); + WARN_ON(err); + + clks[i] = clk_register_fixed_rate(NULL, clk_name, + parent_name, 0, rate[i]); + WARN_ON(IS_ERR(clks[i])); + } + + clk_data = kzalloc(sizeof(*clk_data) + nums * sizeof(struct clk *), + GFP_KERNEL); + if (WARN_ON(!clk_data)) + return; + + memcpy(&clk_data[1], clks, nums * sizeof(struct clk *)); + clk_data->clks = (struct clk **)&clk_data[1]; + clk_data->clk_num = nums; + of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); +} + +void __init hi3716_fixed_divider_setup(struct device_node *node) +{ + const char *clk_parent; + const char *clk_name; + u32 div[MAX_NUMS]; + struct clk *clks[MAX_NUMS]; + struct clk_onecell_data *clk_data; + int err, i, nums = 0; + + clk_parent = of_clk_get_parent_name(node, 0); + + nums = of_property_count_strings(node, "clock-output-names"); + if (WARN_ON((nums < 0) || (nums > MAX_NUMS))) + return; + + err = of_property_read_u32_array(node, "div-table", &div[0], nums); + WARN_ON(err); + + for (i = 0; i < nums; i++) { + err = of_property_read_string_index(node, + "clock-output-names", i, &clk_name); + WARN_ON(err); + + clks[i] = clk_register_fixed_factor(NULL, clk_name, + clk_parent, CLK_SET_RATE_PARENT, 1, div[i]); + WARN_ON(IS_ERR(clks[i])); + } + + clk_data = kzalloc(sizeof(*clk_data) + nums * sizeof(struct clk *), + GFP_KERNEL); + if (WARN_ON(!clk_data)) + return; + + memcpy(&clk_data[1], clks, nums * sizeof(struct clk *)); + clk_data->clks = (struct clk **)&clk_data[1]; + clk_data->clk_num = nums; + of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); +} + +CLK_OF_DECLARE(hi3716_fixed_rate, "fixed-clock", of_fixed_clk_setup) +CLK_OF_DECLARE(hi3716_fixed_pll, "hisilicon,hi3716-fixed-pll", hi3716_fixed_pll_setup) +CLK_OF_DECLARE(hi3716_divider, "hisilicon,hi3716-fixed-divider", hi3716_fixed_divider_setup) +CLK_OF_DECLARE(hi3716_mux, "hisilicon,hi3716-clk-mux", hi3716_clkmux_setup) +CLK_OF_DECLARE(hi3716_gate, "hisilicon,hi3716-clk-gate", hi3716_clkgate_setup) diff --git a/drivers/clk/hisilicon/clk-hi3xxx.c b/drivers/clk/hisilicon/clk-hi3xxx.c new file mode 100644 index 000000000000..68691c12fdf8 --- /dev/null +++ b/drivers/clk/hisilicon/clk-hi3xxx.c @@ -0,0 +1,656 @@ +/* + * 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 = { + .lock = __SPIN_LOCK_UNLOCKED(hs_clk.lock), +}; + +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-output-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 int __init hi3xxx_parse_mux(struct device_node *np, + u8 *num_parents, + u32 *table) +{ + int i, cnt, ret; + + /* get the count of items in mux */ + for (i = 0, cnt = 0; ; i++, cnt++) { + /* parent's #clock-cells property is always 0 */ + if (!of_parse_phandle(np, "clocks", i)) + break; + } + + for (i = 0; i < cnt; i++) { + if (!of_clk_get_parent_name(np, i)) + return -ENOENT; + } + *num_parents = cnt; + table = kzalloc(sizeof(u32 *) * cnt, GFP_KERNEL); + if (!table) + return -ENOMEM; + ret = of_property_read_u32_array(np, "hisilicon,clkmux-table", + table, cnt); + if (ret) + goto err; + return 0; +err: + kfree(table); + return ret; +} + +static void __init hi3620_clkmux_setup(struct device_node *np) +{ + struct clk *clk; + const char *clk_name, **parent_names = NULL; + u32 rdata[2], mask, *table = NULL; + u8 num_parents, shift, flag = 0; + void __iomem *reg, *base; + int i, ret; + + base = hs_init_clocks(np); + if (!base) + return; + + if (of_property_read_string(np, "clock-output-names", &clk_name)) + return; + if (of_property_read_u32_array(np, "hisilicon,clkmux-reg", + &rdata[0], 2)) + return; + + if (of_property_read_bool(np, "hiword")) + flag = CLK_MUX_HIWORD_MASK; + + ret = hi3xxx_parse_mux(np, &num_parents, table); + if (ret) + return; + + parent_names = kzalloc(sizeof(char *) * num_parents, GFP_KERNEL); + if (!parent_names) + goto err; + for (i = 0; i < num_parents; i++) + parent_names[i] = of_clk_get_parent_name(np, i); + + reg = base + rdata[0]; + shift = ffs(rdata[1]) - 1; + mask = rdata[1] >> shift; + clk = clk_register_mux_table(NULL, clk_name, parent_names, num_parents, + CLK_SET_RATE_PARENT, reg, shift, mask, + flag, table, + &hs_clk.lock); + if (IS_ERR(clk)) + goto err_clk; + of_clk_add_provider(np, of_clk_src_simple_get, clk); + + return; +err_clk: + kfree(parent_names); +err: + kfree(table); +} +CLK_OF_DECLARE(hi3620_mux, "hisilicon,hi3620-clk-mux", hi3620_clkmux_setup) + +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_warn("%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]; + unsigned int max_div, min_div; + + reg_base = hs_init_clocks(np); + if (!reg_base) + return; + + if (of_property_read_string(np, "clock-output-names", &clk_name)) + return; + + /*process the div_table*/ + if (of_property_read_u32_array(np, "hisilicon,clkdiv-table", + &data[0], 2)) + return; + + max_div = (u8)data[0]; + min_div = (u8)data[1]; + + if (of_property_read_u32_array(np, "hisilicon,clkdiv", + &data[0], 2)) + return; + + /*table ends with <0, 0>, so plus one to table_num*/ + table_num = max_div - min_div + 1; + + table = kzalloc(sizeof(struct clk_div_table) * table_num, GFP_KERNEL); + if (!table) + return; + + for (i = 0; i < table_num; i++) { + table[i].div = min_div + i; + table[i].val = table[i].div - 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); + clk_register_clkdev(clk, clk_name, NULL); + return; +err_clk: + kfree(init); +err_init: + kfree(dclk); +err_dclk: + kfree(parent_names); +err_par: + kfree(table); +} + +CLK_OF_DECLARE(hi3620_fixed_rate, "fixed-clock", of_fixed_clk_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) +CLK_OF_DECLARE(hi3620_gate, "hisilicon,hi3620-clk-gate", hi3620_clkgate_setup) + +static const struct of_device_id hs_of_match[] = { + { .compatible = "hisilicon,pmctrl", .data = (void *)HS_PMCTRL, }, + { .compatible = "hisilicon,sctrl", .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/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index e09ec67957a3..6be3908a43a4 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -919,4 +919,16 @@ config TOUCHSCREEN_TPS6507X To compile this driver as a module, choose M here: the module will be called tps6507x_ts. +config TOUCHSCREEN_MXT224E + tristate "Atmel mXT224E based touchscreens" + depends on I2C + help + Say Y here if you have a TPS6507x based touchscreen + controller. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called atmel_mXT224E. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index f5216c1bf53e..967014672ae6 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -75,3 +75,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o +obj-$(CONFIG_TOUCHSCREEN_MXT224E) += atmel_mXT224E.o diff --git a/drivers/input/touchscreen/atmel_mXT224E.c b/drivers/input/touchscreen/atmel_mXT224E.c new file mode 100644 index 000000000000..0a7fab242d98 --- /dev/null +++ b/drivers/input/touchscreen/atmel_mXT224E.c @@ -0,0 +1,1473 @@ +/* drivers/input/touchscreen/atmel_mXT224E.c - ATMEL Touch driver + * + * Copyright (C) 2008 ATMEL + * Copyright (C) 2011 Huawei Corporation. + * + * Based on touchscreen code from Atmel Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/jiffies.h> +#include <linux/stat.h> +#include <linux/slab.h> +#include <linux/regulator/consumer.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/bitops.h> +#include <linux/of_gpio.h> +#include <linux/pinctrl/consumer.h> + +#include <linux/kthread.h> + +#ifdef TS_ATMEL_DEBUG +#define TS_DEBUG_ATMEL(fmt, args...) pr_info(fmt, ##args) +#else +#define TS_DEBUG_ATMEL(fmt, args...) +#endif + +#define ATMEL_MXT224E_NAME "atmel_mxt224e" + +#define INFO_BLK_FID 0 +#define INFO_BLK_VID 1 +#define INFO_BLK_VER 2 +#define INFO_BLK_BUILD 3 +#define INFO_BLK_XSIZE 4 +#define INFO_BLK_YSIZE 5 +#define INFO_BLK_OBJS 6 + +#define OBJ_TABLE_TYPE 0 +#define OBJ_TABLE_LSB 1 +#define OBJ_TABLE_MSB 2 +#define OBJ_TABLE_SIZE 3 +#define OBJ_TABLE_INSTANCES 4 +#define OBJ_TABLE_RIDS 5 + +#define RESERVED_T0 0u +#define RESERVED_T1 1u +#define DEBUG_DELTAS_T2 2u +#define DEBUG_REFERENCES_T3 3u +#define DEBUG_SIGNALS_T4 4u +#define GEN_MESSAGEPROCESSOR_T5 5u +#define GEN_COMMANDPROCESSOR_T6 6u +#define GEN_POWERCONFIG_T7 7u +#define GEN_ACQUISITIONCONFIG_T8 8u +#define TOUCH_MULTITOUCHSCREEN_T9 9u +#define TOUCH_SINGLETOUCHSCREEN_T10 10u +#define TOUCH_XSLIDER_T11 11u +#define TOUCH_YSLIDER_T12 12u +#define TOUCH_XWHEEL_T13 13u +#define TOUCH_YWHEEL_T14 14u +#define TOUCH_KEYARRAY_T15 15u +#define PROCG_SIGNALFILTER_T16 16u +#define PROCI_LINEARIZATIONTABLE_T17 17u +#define SPT_COMCONFIG_T18 18u +#define SPT_GPIOPWM_T19 19u +#define PROCI_GRIPFACESUPPRESSION_T20 20u +#define RESERVED_T21 21u +#define PROCG_NOISESUPPRESSION_T22 22u +#define TOUCH_PROXIMITY_T23 23u +#define PROCI_ONETOUCHGESTUREPROCESSOR_T24 24u +#define SPT_SELFTEST_T25 25u +#define DEBUG_CTERANGE_T26 26u +#define PROCI_TWOTOUCHGESTUREPROCESSOR_T27 27u +#define SPT_CTECONFIG_T28 28u +#define SPT_GPI_T29 29u +#define SPT_GATE_T30 30u +#define TOUCH_KEYSET_T31 31u +#define TOUCH_XSLIDERSET_T32 32u +#define DIAGNOSTIC_T37 37u +#define PROCI_GRIPSUPPRESSION_T40 40u +#define PROCI_TOUCHSUPPRESSION_T42 42u +#define SPT_CTECONFIG_T46 46u +#define PROCI_STYLUS_T47 47u +#define PROCG_NOISESUPPRESSION_T48 48u + +#define T37_PAGE_SIZE 128 + +#define T37_TCH_FLAG_SIZE 80 +#define T37_TCH_FLAG_IDX 0 +#define T37_ATCH_FLAG_IDX 40 + +#define T37_MODE 0 +#define T37_PAGE 1 +#define T37_DATA 2 /* n bytes */ + +#define T37_PAGE_NUM0 0 +#define T37_PAGE_NUM1 1 +#define T37_PAGE_NUM2 2 +#define T37_PAGE_NUM3 3 + +#define MSG_RID 0 + +#define T6_CFG_RESET 0 +#define T6_CFG_BACKUPNV 1 +#define T6_CFG_CALIBRATE 2 +#define T6_CFG_REPORTALL 3 +/* Reserved */ +#define T6_CFG_DIAG 5 + +#define T6_CFG_DIAG_CMD_PAGEUP 0x01 +#define T6_CFG_DIAG_CMD_PAGEDOWN 0x02 +#define T6_CFG_DIAG_CMD_DELTAS 0x10 +#define T6_CFG_DIAG_CMD_REF 0x11 +#define T6_CFG_DIAG_CMD_CTE 0x31 +#define T6_CFG_DIAG_CMD_TCH 0xF3 + +#define T6_MSG_STATUS 1 +#define T6_MSG_CHECKSUM 2 /* three bytes */ + +#define T6_MSG_STATUS_COMSERR BIT(2) +#define T6_MSG_STATUS_CFGERR BIT(3) +#define T6_MSG_STATUS_CAL BIT(4) +#define T6_MSG_STATUS_SIGERR BIT(5) +#define T6_MSG_STATUS_OFL BIT(6) +#define T6_MSG_STATUS_RESET BIT(7) + +#define T7_CFG_IDLEACQINT 0 +#define T7_CFG_ACTVACQINT 1 +#define T7_CFG_ACTV2IDLETO 2 + +#define T8_CFG_CHRGTIME 0 +/* Reserved */ +#define T8_CFG_TCHDRIFT 2 +#define T8_CFG_DRIFTST 3 +#define T8_CFG_TCHAUTOCAL 4 +#define T8_CFG_SYNC 5 +#define T8_CFG_ATCHCALST 6 +#define T8_CFG_ATCHCALSTHR 7 +#define T8_CFG_ATCHFRCCALTHR 8 /* FW v2.x */ +#define T8_CFG_ATCHFRCCALRATIO 9 /* FW v2.x */ + +#define T9_CFG_CTRL 0 +#define T9_CFG_XORIGIN 1 +#define T9_CFG_YORIGIN 2 +#define T9_CFG_XSIZE 3 +#define T9_CFG_YSIZE 4 +#define T9_CFG_AKSCFG 5 +#define T9_CFG_BLEN 6 +#define T9_CFG_TCHTHR 7 +#define T9_CFG_TCHDI 8 +#define T9_CFG_ORIENT 9 +#define T9_CFG_MRGTIMEOUT 10 +#define T9_CFG_MOVHYSTI 11 +#define T9_CFG_MOVHYSTN 12 +#define T9_CFG_MOVFILTER 13 +#define T9_CFG_NUMTOUCH 14 +#define T9_CFG_MRGHYST 15 +#define T9_CFG_MRGTHR 16 +#define T9_CFG_AMPHYST 17 +#define T9_CFG_XRANGE 18 /* two bytes */ +#define T9_CFG_YRANGE 20 /* two bytes */ +#define T9_CFG_XLOCLIP 22 +#define T9_CFG_XHICLIP 23 +#define T9_CFG_YLOCLIP 24 +#define T9_CFG_YHICLIP 25 +#define T9_CFG_XEDGECTRL 26 +#define T9_CFG_XEDGEDIST 27 +#define T9_CFG_YEDGECTRL 28 +#define T9_CFG_YEDGEDIST 29 +#define T9_CFG_JUMPLIMIT 30 +#define T9_CFG_TCHHYST 31 /* FW v2.x */ + +#define T9_MSG_STATUS 1 +#define T9_MSG_XPOSMSB 2 +#define T9_MSG_YPOSMSB 3 +#define T9_MSG_XYPOSLSB 4 +#define T9_MSG_TCHAREA 5 +#define T9_MSG_TCHAMPLITUDE 6 +#define T9_MSG_TCHVECTOR 7 + +#define T9_MSG_STATUS_UNGRIP BIT(0) /* FW v2.x */ +#define T9_MSG_STATUS_SUPPRESS BIT(1) +#define T9_MSG_STATUS_AMP BIT(2) +#define T9_MSG_STATUS_VECTOR BIT(3) +#define T9_MSG_STATUS_MOVE BIT(4) +#define T9_MSG_STATUS_RELEASE BIT(5) +#define T9_MSG_STATUS_PRESS BIT(6) +#define T9_MSG_STATUS_DETECT BIT(7) + +#define T20_CFG_CTRL 0 +#define T20_CFG_XLOGRIP 1 +#define T20_CFG_XHIGRIP 2 +#define T20_CFG_YLOGRIP 3 +#define T20_CFG_YHIGRIP 4 +#define T20_CFG_MAXTCHS 5 +/* Reserved */ +#define T20_CFG_SZTHR1 7 +#define T20_CFG_SZTHR2 8 +#define T20_CFG_SHPTHR1 9 +#define T20_CFG_SHPTHR2 10 +#define T20_CFG_SHPEXTTO 11 + +#define T20_MSG_STATUS 1 + +#define T20_MSG_STATUS_FACESUP BIT(0) + +#define T22_CFG_CTRL 0 +/* Reserved */ +#define T22_CFG_GCAFUL 3 /* two bytes */ +#define T22_CFG_GCAFLL 5 /* two bytes */ +#define T22_CFG_ACTVGCAFVALID 7 +#define T22_CFG_NOISETHR 8 +/* Reserved */ +#define T22_CFG_FREQHOPSCALE 10 +#define T22_CFG_FREQ 11 /* five bytes */ +#define T22_CFG_IDLEGCAFVAILD 16 + +#define T22_MSG_STATUS 1 +#define T22_MSG_GCAFDEPTH 2 +#define T22_MSG_FREQINDEX 3 + +#define T22_MSG_STATUS_FHCHG BIT(0) +#define T22_MSG_STATUS_GCAFERR BIT(2) +#define T22_MSG_STATUS_FHERR BIT(3) +#define T22_MSG_STATUS_GCAFCHG BIT(4) + +#define T19_CFG_CTRL 0 +#define T19_CFG_REPORTMASK 1 +#define T19_CFG_DIR 2 +#define T19_CFG_INTPULLUP 3 +#define T19_CFG_OUT 4 +#define T19_CFG_WAKE 5 +#define T19_CFG_PWM 6 +#define T19_CFG_PERIOD 7 +#define T19_CFG_DUTY0 8 +#define T19_CFG_DUTY1 9 +#define T19_CFG_DUTY2 10 +#define T19_CFG_DUTY3 11 +#define T19_CFG_TRIGGER0 12 +#define T19_CFG_TRIGGER1 13 +#define T19_CFG_TRIGGER2 14 +#define T19_CFG_TRIGGER3 15 + +#define T19_CFG_CTRL_ENABLE BIT(0) +#define T19_CFG_CTRL_RPTEN BIT(1) +#define T19_CFG_CTRL_FORCERPT BIT(2) + +#define T19_MSG_STATUS 1 + +#define T25_CFG_CTRL 0 +#define T25_CFG_CMD 1 + +#define T25_MSG_STATUS 1 +#define T25_MSG_INFO 2 /* five bytes */ + +#define T28_CFG_CTRL 0 +#define T28_CFG_CMD 1 +#define T28_CFG_MODE 2 +#define T28_CFG_IDLEGCAFDEPTH 3 +#define T28_CFG_ACTVGCAFDEPTH 4 +#define T28_CFG_VOLTAGE 5 + +#define T28_CFG_MODE0_X 16 +#define T28_CFG_MODE0_Y 14 + +#define T28_MSG_STATUS 1 + +#define T48_NOISESUPPRESSION_CFG 1 + +/* cable_config[] of atmel_i2c_platform_data */ +/* config[] of atmel_config_data */ +#define CB_TCHTHR 0 +#define CB_NOISETHR 1 +#define CB_IDLEGCAFDEPTH 2 +#define CB_ACTVGCAFDEPTH 3 + +#define NC_TCHTHR 0 +#define NC_TCHDI 1 +#define NC_NOISETHR 2 + +/* filter_level */ +#define FL_XLOGRIPMIN 0 +#define FL_XLOGRIPMAX 1 +#define FL_XHIGRIPMIN 2 +#define FL_XHIGRIPMAX 3 + +struct info_id_t { + uint8_t family_id; + uint8_t variant_id; + uint8_t version; + uint8_t build; + uint8_t matrix_x_size; + uint8_t matrix_y_size; + uint8_t num_declared_objects; +}; + +struct object_t { + uint8_t object_type; + uint16_t i2c_address; + uint8_t size; + uint8_t instances; + uint8_t num_report_ids; + uint8_t report_ids; +}; + +struct atmel_virtual_key { + int keycode; + int range_min; + int range_max; +}; + +struct atmel_finger_data { + int x; + int y; + int w; + int z; +}; + +struct atmel_i2c_platform_data { + uint16_t version; + uint16_t source; + uint16_t abs_x_min; + uint16_t abs_x_max; + uint16_t abs_y_min; + uint16_t abs_y_max; + uint8_t abs_pressure_min; + uint8_t abs_pressure_max; + uint8_t abs_width_min; + uint8_t abs_width_max; + uint8_t abs_area_min; + uint8_t abs_area_max; + int gpio_irq; + int gpio_reset; + int (*power)(int on); + u8 config_T6[6]; + u8 config_T7[3]; + u8 config_T8[10]; + u8 config_T9[35]; + u8 config_T15[11]; + u8 config_T19[16]; + u8 config_T20[12]; + u8 config_T22[17]; + u8 config_T23[15]; + u8 config_T24[19]; + u8 config_T25[14]; + u8 config_T27[7]; + u8 config_T28[6]; + u8 config_T40[5]; + u8 config_T42[8]; + u8 config_T46[9]; + u8 config_T47[10]; + u8 config_T48[54]; + u8 object_crc[3]; + u8 cable_config[4]; + u8 cable_config_T7[3]; + u8 cable_config_T8[10]; + u8 cable_config_T9[35]; + u8 cable_config_T22[17]; + u8 cable_config_T28[6]; + u8 cable_config_T46[9]; + u8 cable_config_T48[54]; + u8 noise_config[3]; + u16 filter_level[4]; + u8 GCAF_level[5]; + u8 ATCH_NOR[6]; + u8 ATCH_NOR_20S[6]; +}; + +struct atmel_config_data { + int8_t config[4]; + int8_t *config_T7; + int8_t *config_T8; + int8_t *config_T9; + int8_t *config_T22; + int8_t *config_T28; + int8_t *config_T46; + int8_t *config_T48; +}; + +#define ATMEL_I2C_RETRY_TIMES 10 + +/* config_setting */ +#define NONE 0 +#define CONNECTED 1 +struct atmel_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct atmel_i2c_platform_data *pdata; + struct workqueue_struct *atmel_wq; + struct work_struct work; + int (*power) (int on); + struct info_id_t *id; + struct object_t *object_table; + struct iomux_block *gpio_block; + struct block_config *gpio_block_config; + uint8_t finger_count; + uint16_t abs_x_min; + uint16_t abs_x_max; + uint16_t abs_y_min; + uint16_t abs_y_max; + uint8_t abs_area_min; + uint8_t abs_area_max; + uint8_t abs_width_min; + uint8_t abs_width_max; + uint8_t abs_pressure_min; + uint8_t abs_pressure_max; + uint8_t first_pressed; + struct atmel_finger_data finger_data[10]; + uint8_t finger_type; + uint8_t finger_support; + uint16_t finger_pressed; + uint8_t face_suppression; + uint8_t grip_suppression; + uint8_t noise_status[2]; + uint16_t *filter_level; + uint8_t calibration_confirm; + uint64_t timestamp; + struct atmel_config_data config_setting[2]; + int8_t noise_config[3]; + uint8_t status; + uint8_t GCAF_sample; + uint8_t *GCAF_level; + uint8_t noisethr; + uint8_t noisethr_config; + uint8_t diag_command; + uint8_t *ATCH_EXT; + int8_t *ATCH_NOR; + int8_t *ATCH_NOR_20S; + int pre_data[11]; + /*unlock flag used to indicate calibration after unlock system*/ + int unlock_flag; + + /*For usb detect*/ + struct work_struct usb_work; + struct notifier_block nb; + unsigned long usb_event; + struct mutex lock; +}; + +static struct atmel_ts_data *private_ts; + +#define LDO_POWR_VOLTAGE 2700000 /*2.7v*/ +static struct regulator *LDO; + +int i2c_atmel_read(struct i2c_client *client, uint16_t address, uint8_t *data, uint8_t length) +{ + int retry, ret; + uint8_t addr[2]; + + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = addr, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + } + }; + addr[0] = address & 0xFF; + addr[1] = (address >> 8) & 0xFF; + + for (retry = 0; retry < ATMEL_I2C_RETRY_TIMES; retry++) { + ret = i2c_transfer(client->adapter, msg, 2); + if ((ret == 2) || (ret == -ERESTARTSYS)) + break; + mdelay(10); + } + if (retry == ATMEL_I2C_RETRY_TIMES) { + dev_err(&client->dev, "k3ts, %s: i2c_read_block retry over %d\n", __func__, + ATMEL_I2C_RETRY_TIMES); + return -EIO; + } + return 0; +} + +int i2c_atmel_write(struct i2c_client *client, uint16_t address, uint8_t *data, uint8_t length) +{ + int retry, loop_i, ret; + uint8_t buf[length + 2]; + + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = length + 2, + .buf = buf, + } + }; + + buf[0] = address & 0xFF; + buf[1] = (address >> 8) & 0xFF; + + for (loop_i = 0; loop_i < length; loop_i++) + buf[loop_i + 2] = data[loop_i]; + + for (retry = 0; retry < ATMEL_I2C_RETRY_TIMES; retry++) { + ret = i2c_transfer(client->adapter, msg, 1); + if ((ret == 1) || (ret == -ERESTARTSYS)) + break; + mdelay(10); + } + + if (retry == ATMEL_I2C_RETRY_TIMES) { + dev_err(&client->dev, "k3ts, %s: i2c_write_block retry over %d\n", __func__, + ATMEL_I2C_RETRY_TIMES); + return -EIO; + } + return 0; + +} + +int i2c_atmel_write_byte_data(struct i2c_client *client, uint16_t address, uint8_t value) +{ + i2c_atmel_write(client, address, &value, 1); + return 0; +} + +uint16_t get_object_address(struct atmel_ts_data *ts, uint8_t object_type) +{ + uint8_t loop_i; + for (loop_i = 0; loop_i < ts->id->num_declared_objects; loop_i++) { + if (ts->object_table[loop_i].object_type == object_type) + return ts->object_table[loop_i].i2c_address; + } + return 0; +} +uint8_t get_object_size(struct atmel_ts_data *ts, uint8_t object_type) +{ + uint8_t loop_i; + for (loop_i = 0; loop_i < ts->id->num_declared_objects; loop_i++) { + if (ts->object_table[loop_i].object_type == object_type) + return ts->object_table[loop_i].size; + } + return 0; +} + +uint8_t get_object_size_from_address(struct atmel_ts_data *ts, int address) +{ + uint8_t loop_i; + for (loop_i = 0; loop_i < ts->id->num_declared_objects; loop_i++) { + if (ts->object_table[loop_i].i2c_address == address) + return ts->object_table[loop_i].size; + } + return 0; +} + +uint8_t get_rid(struct atmel_ts_data *ts, uint8_t object_type) +{ + uint8_t loop_i; + for (loop_i = 0; loop_i < ts->id->num_declared_objects; loop_i++) { + if (ts->object_table[loop_i].object_type == object_type) + return ts->object_table[loop_i].report_ids; + } + return 0; +} + +static void check_calibration(struct atmel_ts_data *ts) +{ + uint8_t data[T37_DATA + T37_TCH_FLAG_SIZE]; + uint8_t loop_i, loop_j, x_limit = 0, check_mask, tch_ch = 0, atch_ch = 0; + + memset(data, 0xFF, sizeof(data)); + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_DIAG, T6_CFG_DIAG_CMD_TCH); + + for (loop_i = 0; + !(data[T37_MODE] == T6_CFG_DIAG_CMD_TCH && data[T37_PAGE] == T37_PAGE_NUM0) && loop_i < 10; loop_i++) { + msleep(5); + i2c_atmel_read(ts->client, + get_object_address(ts, DIAGNOSTIC_T37), data, 2); + } + + if (loop_i == 10) + dev_err(&ts->client->dev, "k3ts, %s: Diag data not ready\n", __func__); + + i2c_atmel_read(ts->client, get_object_address(ts, DIAGNOSTIC_T37), data, + T37_DATA + T37_TCH_FLAG_SIZE); + if (data[T37_MODE] == T6_CFG_DIAG_CMD_TCH && + data[T37_PAGE] == T37_PAGE_NUM0) { + x_limit = T28_CFG_MODE0_X + ts->config_setting[NONE].config_T28[T28_CFG_MODE]; + x_limit = x_limit << 1; + if (x_limit <= 40) { + for (loop_i = 0; loop_i < x_limit; loop_i += 2) { + for (loop_j = 0; loop_j < BITS_PER_BYTE; loop_j++) { + check_mask = BIT_MASK(loop_j); + if (data[T37_DATA + T37_TCH_FLAG_IDX + loop_i] & + check_mask) + tch_ch++; + if (data[T37_DATA + T37_TCH_FLAG_IDX + loop_i + 1] & + check_mask) + tch_ch++; + if (data[T37_DATA + T37_ATCH_FLAG_IDX + loop_i] & + check_mask) + atch_ch++; + if (data[T37_DATA + T37_ATCH_FLAG_IDX + loop_i + 1] & + check_mask) + atch_ch++; + } + } + } + } + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_DIAG, T6_CFG_DIAG_CMD_PAGEUP); + + if (tch_ch && (atch_ch == 0)) { + if (jiffies > (ts->timestamp + HZ/2) && (ts->calibration_confirm == 1)) { + ts->calibration_confirm = 2; + } + if (ts->calibration_confirm < 2) + ts->calibration_confirm = 1; + ts->timestamp = jiffies; + } else if (atch_ch > 1 || tch_ch > 8) { + ts->calibration_confirm = 0; + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_CALIBRATE, 0x55); + } +} + +static void confirm_calibration(struct atmel_ts_data *ts) +{ + i2c_atmel_write(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + + T8_CFG_TCHAUTOCAL, ts->ATCH_NOR_20S, 6); + ts->pre_data[0] = 2; +} + +static void msg_process_finger_data_x10y10bit(struct atmel_finger_data *fdata, uint8_t *data) +{ + fdata->x = data[T9_MSG_XPOSMSB] << 2 | data[T9_MSG_XYPOSLSB] >> 6; + fdata->y = data[T9_MSG_YPOSMSB] << 2 | (data[T9_MSG_XYPOSLSB] & 0x0C) >>2; + fdata->w = data[T9_MSG_TCHAREA]; + fdata->z = data[T9_MSG_TCHAMPLITUDE]; +} +static void msg_process_finger_data_x10y12bit(struct atmel_finger_data *fdata, uint8_t *data) +{ + fdata->x = data[T9_MSG_XPOSMSB] << 2 | data[T9_MSG_XYPOSLSB] >> 6; + fdata->y = data[T9_MSG_YPOSMSB] << 4 | (data[T9_MSG_XYPOSLSB] & 0x0F) ; + fdata->w = data[T9_MSG_TCHAREA]; + fdata->z = data[T9_MSG_TCHAMPLITUDE]; +} + +static void msg_process_multitouch(struct atmel_ts_data *ts, uint8_t *data, uint8_t idx) +{ + if (ts->calibration_confirm < 2 && ts->id->version == 0x10) + check_calibration(ts); + if(ts->abs_y_max >= 1024) { + msg_process_finger_data_x10y12bit(&ts->finger_data[idx], data); + } else { + msg_process_finger_data_x10y10bit(&ts->finger_data[idx], data); + } + if (data[T9_MSG_STATUS] & T9_MSG_STATUS_RELEASE) { + if (ts->grip_suppression & BIT(idx)) + ts->grip_suppression &= ~BIT(idx); + if (ts->finger_pressed & BIT(idx)) { + ts->finger_count--; + ts->finger_pressed &= ~BIT(idx); + if (!ts->first_pressed) { + if (!ts->finger_count) + ts->first_pressed = 1; + } + if (ts->pre_data[0] < 2 && ts->unlock_flag != 1) { + + if (ts->finger_count) { + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_CALIBRATE, 0x55); + } else if (!ts->finger_count && ts->pre_data[0] == 1) + ts->pre_data[0] = 0; + } + } + } else if ((data[T9_MSG_STATUS] & (T9_MSG_STATUS_DETECT | T9_MSG_STATUS_PRESS)) + && !(ts->finger_pressed & BIT(idx))) { + if (ts->id->version >= 0x10 && ts->pre_data[0] < 2) { + if (jiffies > (ts->timestamp + 20 * HZ)) { + confirm_calibration(ts); + } + } + if (!(ts->grip_suppression & BIT(idx))) { + ts->finger_count++; + ts->finger_pressed |= BIT(idx); + if (ts->id->version >= 0x10 && ts->pre_data[0] < 2) { + ts->pre_data[idx + 1] = ts->finger_data[idx].x; + ts->pre_data[idx + 2] = ts->finger_data[idx].y; + if (ts->finger_count == ts->finger_support) { + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_CALIBRATE, 0x55); + } else if (!ts->pre_data[0] && ts->finger_count == 1) + ts->pre_data[0] = 1; + } + } + } else if ((data[T9_MSG_STATUS] & (T9_MSG_STATUS_DETECT|T9_MSG_STATUS_PRESS)) + && ts->pre_data[0] < 2 && ts->unlock_flag != 1) { + if (ts->finger_count == 1 && ts->pre_data[0] && + (idx == 0 && ((abs(ts->finger_data[idx].y - ts->pre_data[idx + 2]) > 50) + || (abs(ts->finger_data[idx].x - ts->pre_data[idx + 1]) > 50)))) + { + ts->unlock_flag = 1; + ts->calibration_confirm = 2; + } + } + +} + +static void compatible_input_report(struct input_dev *idev, + struct atmel_finger_data *fdata, uint8_t press, uint8_t last) +{ + if (!press) { + input_mt_sync(idev); + /*input_report_key(idev, BTN_TOUCH, 0);*/ + input_report_key(idev, BTN_TOUCH, 1); + + } else { + TS_DEBUG_ATMEL("k3ts, %s: Touch report_key x = %d, y = %d, z = %d, w = %d\n ", __func__, + fdata->x, fdata->y, fdata->z, fdata->w); + input_report_abs(idev, ABS_MT_TOUCH_MAJOR, fdata->z); + input_report_abs(idev, ABS_MT_WIDTH_MAJOR, fdata->w); + input_report_abs(idev, ABS_MT_POSITION_X, fdata->x); + input_report_abs(idev, ABS_MT_POSITION_Y, fdata->y); + input_mt_sync(idev); + } +} + + + +static void multi_input_report(struct atmel_ts_data *ts) +{ + uint8_t loop_i, finger_report = 0; + + for (loop_i = 0; loop_i < ts->finger_support; loop_i++) { + if (ts->finger_pressed & BIT(loop_i)) { + compatible_input_report(ts->input_dev, &ts->finger_data[loop_i], + 1, (ts->finger_count == ++finger_report)); + } + } +} +static irqreturn_t atmel_interrupt_fun(int irq, void *dev_id) +{ + int ret; + struct atmel_ts_data *ts = dev_id; + uint8_t data[7]; + int8_t report_type; + uint8_t msg_byte_num = 7; + + memset(data, 0x0, sizeof(data)); + + mutex_lock(&ts->lock); + ret = i2c_atmel_read(ts->client, get_object_address(ts, + GEN_MESSAGEPROCESSOR_T5), data, 7); + + report_type = data[MSG_RID] - ts->finger_type; + if (report_type >= 0 && report_type < ts->finger_support) { + msg_process_multitouch(ts, data, report_type); + } else { + if (data[MSG_RID] == get_rid(ts, GEN_COMMANDPROCESSOR_T6)) { + if (data[1] & 0x10) { + ts->timestamp = jiffies; + } + if (data[1] & 0x80) { + msleep(100); + + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_CALIBRATE, 0x55); + } + msg_byte_num = 5; + } + if (data[MSG_RID] == get_rid(ts, PROCI_TOUCHSUPPRESSION_T42)) { + if (ts->calibration_confirm < 2 && ts->id->version == 0x10) { + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_CALIBRATE, 0x55); + } + ts->face_suppression = data[T20_MSG_STATUS]; + printk(KERN_INFO "Touch Face suppression %s: ", + ts->face_suppression ? "Active" : "Inactive"); + msg_byte_num = 2; + } + } + if (!ts->finger_count || ts->face_suppression) { + ts->finger_pressed = 0; + ts->finger_count = 0; + compatible_input_report(ts->input_dev, NULL, 0, 1); + } else { + multi_input_report(ts); + } + input_sync(ts->input_dev); + mutex_unlock(&ts->lock); + + return IRQ_HANDLED; +} + +static int read_object_table(struct atmel_ts_data *ts) +{ + uint8_t i, type_count = 0; + uint8_t data[6]; + memset(data, 0x0, sizeof(data)); + + ts->object_table = kzalloc(sizeof(struct object_t)*ts->id->num_declared_objects, GFP_KERNEL); + if (ts->object_table == NULL) { + dev_err(&ts->client->dev, "k3ts, %s: allocate object_table failed\n", __func__); + return -ENOMEM; + } + + for (i = 0; i < ts->id->num_declared_objects; i++) { + i2c_atmel_read(ts->client, i * 6 + 0x07, data, 6); + ts->object_table[i].object_type = data[OBJ_TABLE_TYPE]; + ts->object_table[i].i2c_address = + data[OBJ_TABLE_LSB] | data[OBJ_TABLE_MSB] << 8; + ts->object_table[i].size = data[OBJ_TABLE_SIZE] + 1; + ts->object_table[i].instances = data[OBJ_TABLE_INSTANCES]; + ts->object_table[i].num_report_ids = data[OBJ_TABLE_RIDS]; + if (data[OBJ_TABLE_RIDS]) { + ts->object_table[i].report_ids = type_count + 1; + type_count += data[OBJ_TABLE_RIDS]; + } + if (data[OBJ_TABLE_TYPE] == TOUCH_MULTITOUCHSCREEN_T9) + ts->finger_type = ts->object_table[i].report_ids; + } + + return 0; +} + +struct atmel_i2c_platform_data *atmel_ts_get_pdata(struct i2c_client *client) +{ + struct device_node *node = client->dev.of_node; + struct atmel_i2c_platform_data *pdata = client->dev.platform_data; + u32 data[8]; + + if (pdata) + return pdata; + + if (!node) + return NULL; + + pdata = devm_kzalloc(&client->dev, sizeof(struct atmel_i2c_platform_data), + GFP_KERNEL); + pdata->gpio_irq = of_get_named_gpio(node, "atmel-ts,gpio-irq", 0); + pdata->gpio_reset = of_get_named_gpio(node, "atmel-ts,gpio-reset", 0); + + of_property_read_u32_array(node, "atmel-ts,abs", &data[0], 8); + pdata->abs_x_min = data[0]; + pdata->abs_x_max = data[1]; + pdata->abs_y_min = data[2]; + pdata->abs_y_max = data[3]; + pdata->abs_pressure_min = data[4]; + pdata->abs_pressure_max = data[5]; + pdata->abs_width_min = data[6]; + pdata->abs_width_max = data[7]; + + of_property_read_u8_array(node, "atmel-ts,cfg_t6", &pdata->config_T6[0], 6); + of_property_read_u8_array(node, "atmel-ts,cfg_t7", &pdata->config_T7[0], 3); + of_property_read_u8_array(node, "atmel-ts,cfg_t8", &pdata->config_T8[0], 10); + of_property_read_u8_array(node, "atmel-ts,cfg_t9", &pdata->config_T9[0], 35); + of_property_read_u8_array(node, "atmel-ts,cfg_t15", &pdata->config_T15[0], 11); + of_property_read_u8_array(node, "atmel-ts,cfg_t19", &pdata->config_T19[0], 16); + of_property_read_u8_array(node, "atmel-ts,cfg_t23", &pdata->config_T23[0], 15); + of_property_read_u8_array(node, "atmel-ts,cfg_t25", &pdata->config_T25[0], 14); + of_property_read_u8_array(node, "atmel-ts,cfg_t40", &pdata->config_T40[0], 5); + of_property_read_u8_array(node, "atmel-ts,cfg_t42", &pdata->config_T42[0], 8); + of_property_read_u8_array(node, "atmel-ts,cfg_t46", &pdata->config_T46[0], 9); + of_property_read_u8_array(node, "atmel-ts,cfg_t47", &pdata->config_T47[0], 10); + of_property_read_u8_array(node, "atmel-ts,cfg_t48", &pdata->config_T48[0], 54); + of_property_read_u8_array(node, "atmel-ts,object_crc", &pdata->object_crc[0], 3); + of_property_read_u8_array(node, "atmel-ts,cable_config", &pdata->cable_config[0], 4); + of_property_read_u8_array(node, "atmel-ts,cable_config_t7", &pdata->cable_config_T7[0], 3); + of_property_read_u8_array(node, "atmel-ts,cable_config_t8", &pdata->cable_config_T8[0], 10); + of_property_read_u8_array(node, "atmel-ts,cable_config_t46", &pdata->cable_config_T46[0], 9); + of_property_read_u8_array(node, "atmel-ts,cable_config_t48", &pdata->cable_config_T48[0], 54); + of_property_read_u8_array(node, "atmel-ts,noise_config", &pdata->noise_config[0], 3); + of_property_read_u16_array(node, "atmel-ts,filter_level", &pdata->filter_level[0], 4); + of_property_read_u8_array(node, "atmel-ts,gcaf_level", &pdata->GCAF_level[0], 5); + of_property_read_u8_array(node, "atmel-ts,atch_nor", &pdata->ATCH_NOR[0], 6); + of_property_read_u8_array(node, "atmel-ts,atch_nor_20s", &pdata->ATCH_NOR_20S[0], 6); + + return pdata; +} + +static int atmel_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct atmel_ts_data *ts; + struct atmel_i2c_platform_data *pdata; + int ret = 0, intr = 0; + uint8_t loop_i; + struct i2c_msg msg[2]; + uint8_t data[16]; + uint8_t CRC_check = 0; + + client->dev.init_name = "atmel-ts"; + LDO = regulator_get(&client->dev, "ldo"); + if (IS_ERR(LDO)) { + dev_err(&client->dev, "no regulator found\n"); + LDO = NULL; + } else { + ret = regulator_enable(LDO); + if (!ret) + ret = regulator_set_voltage(LDO, LDO_POWR_VOLTAGE, LDO_POWR_VOLTAGE); + if (ret) + dev_err(&client->dev, "k3ts, %s: failed to set LDO\n", __func__); + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "k3ts, %s: need I2C_FUNC_I2C\n", __func__); + ret = -ENODEV; + goto err_check_functionality_failed; + } + + ts = kzalloc(sizeof(struct atmel_ts_data), GFP_KERNEL); + if (ts == NULL) { + ret = -ENOMEM; + goto err_alloc_data_failed; + } + + ts->unlock_flag = 0; + mutex_init(&ts->lock); + + ts->atmel_wq = create_singlethread_workqueue("atmel_wq"); + if (!ts->atmel_wq) { + dev_err(&client->dev, "k3ts, %s: create workqueue failed\n", __func__); + ret = -ENOMEM; + goto err_cread_wq_failed; + } + + ts->client = client; + i2c_set_clientdata(client, ts); + + pdata = atmel_ts_get_pdata(client); + ts->pdata = pdata; + + if (pdata) { + ts->power = pdata->power; + intr = pdata->gpio_irq; + client->irq = gpio_to_irq(intr); + } + if (ts->power) + ret = ts->power(1); + ret = gpio_request(intr, "gpio_tp_intr"); + if (ret) { + dev_err(&client->dev, "gpio_request %d failed\n", intr); + goto err_request_gpio_failed; + } + ret = gpio_direction_input(intr); + if (ret) { + dev_err(&client->dev, "k3ts, %s: gpio_direction_input failed %d\n", __func__, intr); + goto err_gpio_direction_failed; + } + + ret = gpio_request(ts->pdata->gpio_reset, "gpio_tp_reset"); + if (ret) { + dev_err(&client->dev, "k3ts, %s: gpio_request failed %d, ret = %d\n", + __func__, ts->pdata->gpio_reset, ret); + goto err_request_gpio_reset_failed; + } + + gpio_direction_output(ts->pdata->gpio_reset, 1); + mdelay(5); + gpio_direction_output(ts->pdata->gpio_reset, 0); + mdelay(10); + gpio_direction_output(ts->pdata->gpio_reset, 1); + mdelay(50); + + for (loop_i = 0; loop_i < 10; loop_i++) { + if (!gpio_get_value(intr)) + break; + msleep(10); + } + + if (loop_i == 10) + dev_err(&client->dev, "k3ts, %s: No Messages\n", __func__); + + /* read message*/ + msg[0].addr = ts->client->addr; + msg[0].flags = I2C_M_RD; + msg[0].len = 7; + msg[0].buf = data; + ret = i2c_transfer(client->adapter, msg, 1); + + if (ret < 0) { + dev_err(&client->dev, "k3ts, %s: No Atmel chip inside\n", __func__); + goto err_detect_failed; + } + if (ts->power) + ret = ts->power(2); + + if (data[MSG_RID] == 0x01 && + (data[T6_MSG_STATUS] & (T6_MSG_STATUS_SIGERR|T6_MSG_STATUS_COMSERR))) { + dev_err(&client->dev, "k3ts, %s: init err: %x\n", __func__, data[1]); + goto err_detect_failed; + } else { + for (loop_i = 0; loop_i < 10; loop_i++) { + if (gpio_get_value(intr)) { + dev_err(&client->dev, "k3ts, %s: No more message\n", __func__); + break; + } + ret = i2c_transfer(client->adapter, msg, 1); + msleep(10); + } + } + + /* Read the info block data. */ + ts->id = kzalloc(sizeof(struct info_id_t), GFP_KERNEL); + if (ts->id == NULL) { + dev_err(&client->dev, "k3ts, %s: allocate info_id_t failed\n", __func__); + goto err_alloc_failed; + } + ret = i2c_atmel_read(client, 0x00, data, 7); + + ts->id->family_id = data[INFO_BLK_FID]; + ts->id->variant_id = data[INFO_BLK_VID]; + if (ts->id->family_id == 0x80 && ts->id->variant_id == 0x10) + ts->id->version = data[INFO_BLK_VER] + 6; + else + ts->id->version = data[INFO_BLK_VER]; + + ts->id->build = data[INFO_BLK_BUILD]; + ts->id->matrix_x_size = data[INFO_BLK_XSIZE]; + ts->id->matrix_y_size = data[INFO_BLK_YSIZE]; + ts->id->num_declared_objects = data[INFO_BLK_OBJS]; + + /* Read object table. */ + ret = read_object_table(ts); + if (ret < 0) + goto err_read_table_failed; + + + if (pdata) { + ts->finger_support = pdata->config_T9[T9_CFG_NUMTOUCH]; + + /* OBJECT CONFIG CRC check */ + if (pdata->object_crc[0]) { + ret = i2c_atmel_write_byte_data(client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_CALIBRATE, 0x55); + for (loop_i = 0; loop_i < 10; loop_i++) { + if (!gpio_get_value(intr)) { + ret = i2c_atmel_read(ts->client, get_object_address(ts, + GEN_MESSAGEPROCESSOR_T5), data, 5); + if (data[MSG_RID] == get_rid(ts, GEN_COMMANDPROCESSOR_T6)) + break; + } + msleep(10); + } + if (loop_i == 10) + dev_err(&client->dev, "k3ts, %s: No checksum read\n", __func__); + else { + dev_info(&client->dev, "k3ts, %s: CRC print : %x, %x, %x\n", __func__, + data[T6_MSG_CHECKSUM + 0], data[T6_MSG_CHECKSUM + 1], data[T6_MSG_CHECKSUM + 2]); + for (loop_i = 0; loop_i < 3; loop_i++) { + if (pdata->object_crc[loop_i] != data[T6_MSG_CHECKSUM + loop_i]) { + dev_err(&client->dev, + "k3ts, %s: CRC Error: %x, %x\n", __func__, + pdata->object_crc[loop_i], + data[T6_MSG_CHECKSUM + loop_i]); + break; + } + } + if (loop_i == 3) { + dev_info(&client->dev, "k3ts, %s: CRC passed: ", __func__); + for (loop_i = 0; loop_i < 3; loop_i++) + pr_info("0x%2.2X ", pdata->object_crc[loop_i]); + pr_info("\n"); + CRC_check = 1;/*means CRC check OK*/ + } + } + } + ts->abs_x_min = pdata->abs_x_min; + ts->abs_x_max = pdata->abs_x_max; + ts->abs_y_min = pdata->abs_y_min; + ts->abs_y_max = pdata->abs_y_max; + ts->abs_pressure_min = pdata->abs_pressure_min; + ts->abs_pressure_max = pdata->abs_pressure_max; + ts->abs_width_min = pdata->abs_width_min; + ts->abs_width_max = pdata->abs_width_max; + + ts->GCAF_level = pdata->GCAF_level; + if (ts->id->version >= 0x10) { + ts->ATCH_EXT = &pdata->config_T8[6]; + ts->timestamp = jiffies + 60 * HZ; + } + ts->ATCH_NOR = pdata->ATCH_NOR; + ts->ATCH_NOR_20S = pdata->ATCH_NOR_20S; + ts->filter_level = pdata->filter_level; + + ts->config_setting[NONE].config_T7 + = ts->config_setting[CONNECTED].config_T7 + = pdata->config_T7; + ts->config_setting[NONE].config_T8 = pdata->config_T8; + ts->config_setting[CONNECTED].config_T8 = pdata->cable_config_T8; + ts->config_setting[NONE].config_T9 = pdata->config_T9; + ts->config_setting[NONE].config_T22 = pdata->config_T22; + ts->config_setting[NONE].config_T28 = pdata->config_T28; + ts->config_setting[NONE].config_T46 = pdata->config_T46; + ts->config_setting[NONE].config_T48 = pdata->config_T48; + ts->config_setting[CONNECTED].config_T46 = pdata->cable_config_T46; + ts->config_setting[CONNECTED].config_T48 = pdata->cable_config_T48; + + if (pdata->noise_config[0]) + for (loop_i = 0; loop_i < 3; loop_i++) + ts->noise_config[loop_i] = pdata->noise_config[loop_i]; + + if (pdata->cable_config[0]) { + ts->config_setting[NONE].config[CB_TCHTHR] = + pdata->config_T9[T9_CFG_TCHTHR]; + ts->config_setting[NONE].config[CB_NOISETHR] = + pdata->config_T22[T22_CFG_NOISETHR]; + ts->config_setting[NONE].config[CB_IDLEGCAFDEPTH] = + pdata->config_T28[T28_CFG_IDLEGCAFDEPTH]; + ts->config_setting[NONE].config[CB_ACTVGCAFDEPTH] = + pdata->config_T28[T28_CFG_ACTVGCAFDEPTH]; + for (loop_i = 0; loop_i < 4; loop_i++) + ts->config_setting[CONNECTED].config[loop_i] = + pdata->cable_config[loop_i]; + ts->GCAF_sample = + ts->config_setting[CONNECTED].config[CB_ACTVGCAFDEPTH]; + if (ts->id->version >= 0x20) + ts->noisethr = pdata->cable_config[CB_TCHTHR] - + pdata->config_T9[T9_CFG_TCHHYST]; + else + ts->noisethr = pdata->cable_config[CB_TCHTHR]; + ts->noisethr_config = + ts->config_setting[CONNECTED].config[CB_NOISETHR]; + } else { + if (pdata->cable_config_T7[0]) + ts->config_setting[CONNECTED].config_T7 = + pdata->cable_config_T7; + if (pdata->cable_config_T8[0]) + ts->config_setting[CONNECTED].config_T8 = + pdata->cable_config_T8; + if (pdata->cable_config_T9[0]) { + ts->config_setting[CONNECTED].config_T9 = + pdata->cable_config_T9; + ts->config_setting[CONNECTED].config_T22 = + pdata->cable_config_T22; + ts->config_setting[CONNECTED].config_T28 = + pdata->cable_config_T28; + ts->GCAF_sample = + ts->config_setting[CONNECTED].config_T28[T28_CFG_ACTVGCAFDEPTH]; + } + if (ts->status == CONNECTED) + ts->noisethr = (ts->id->version >= 0x20) ? + pdata->cable_config_T9[T9_CFG_TCHTHR] - pdata->cable_config_T9[T9_CFG_TCHHYST] : + pdata->cable_config_T9[T9_CFG_TCHTHR]; + else + ts->noisethr = (ts->id->version >= 0x20) ? + pdata->config_T9[T9_CFG_TCHTHR] - pdata->config_T9[T9_CFG_TCHHYST] : + pdata->config_T9[T9_CFG_TCHTHR]; + ts->noisethr_config = pdata->cable_config_T22[T22_CFG_NOISETHR]; + + } + + i2c_atmel_write(ts->client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6), + pdata->config_T6, + get_object_size(ts, GEN_COMMANDPROCESSOR_T6)); + i2c_atmel_write(ts->client, + get_object_address(ts, GEN_POWERCONFIG_T7), + pdata->config_T7, + get_object_size(ts, GEN_POWERCONFIG_T7)); + i2c_atmel_write(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8), + pdata->config_T8, + get_object_size(ts, GEN_ACQUISITIONCONFIG_T8)); + i2c_atmel_write(ts->client, + get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9), + pdata->config_T9, + get_object_size(ts, TOUCH_MULTITOUCHSCREEN_T9)); + i2c_atmel_write(ts->client, + get_object_address(ts, TOUCH_KEYARRAY_T15), + pdata->config_T15, + get_object_size(ts, TOUCH_KEYARRAY_T15)); + i2c_atmel_write(ts->client, + get_object_address(ts, SPT_GPIOPWM_T19), + pdata->config_T19, + get_object_size(ts, SPT_GPIOPWM_T19)); + + i2c_atmel_write(ts->client, + get_object_address(ts, PROCI_GRIPSUPPRESSION_T40), + pdata->config_T40, + get_object_size(ts, PROCI_GRIPSUPPRESSION_T40)); + + i2c_atmel_write(ts->client, + get_object_address(ts, PROCI_TOUCHSUPPRESSION_T42), + pdata->config_T42, + get_object_size(ts, PROCI_TOUCHSUPPRESSION_T42)); + i2c_atmel_write(ts->client, + get_object_address(ts, PROCG_NOISESUPPRESSION_T48), + pdata->config_T48, + get_object_size(ts, PROCG_NOISESUPPRESSION_T48)); + i2c_atmel_write(ts->client, + get_object_address(ts, TOUCH_PROXIMITY_T23), + pdata->config_T23, + get_object_size(ts, TOUCH_PROXIMITY_T23)); + i2c_atmel_write(ts->client, + get_object_address(ts, SPT_SELFTEST_T25), + pdata->config_T25, + get_object_size(ts, SPT_SELFTEST_T25)); + i2c_atmel_write(ts->client, + get_object_address(ts, SPT_CTECONFIG_T46), + pdata->config_T46, + get_object_size(ts, SPT_CTECONFIG_T46)); + i2c_atmel_write(ts->client, + get_object_address(ts, PROCI_STYLUS_T47), + pdata->config_T47, + get_object_size(ts, PROCI_STYLUS_T47)); + + ret = i2c_atmel_write_byte_data(client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_BACKUPNV, 0x55); + + for (loop_i = 0; loop_i < 10; loop_i++) { + if (!gpio_get_value(intr)) + break; + dev_err(&client->dev, "k3ts, %s: wait for Message(%d)\n", __func__, loop_i + 1); + msleep(10); + } + + i2c_atmel_read(client, + get_object_address(ts, GEN_MESSAGEPROCESSOR_T5), data, 7); + + ret = i2c_atmel_write_byte_data(client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_RESET, 0x11);/*reset*/ + msleep(100); + + if (ts->status == CONNECTED) { + if (ts->config_setting[CONNECTED].config_T8 != NULL) + i2c_atmel_write(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8), + ts->config_setting[CONNECTED].config_T8, + get_object_size(ts, GEN_ACQUISITIONCONFIG_T8)); + if (ts->config_setting[CONNECTED].config_T46 != NULL) + i2c_atmel_write(ts->client, + get_object_address(ts, SPT_CTECONFIG_T46), + ts->config_setting[CONNECTED].config_T46, + get_object_size(ts, SPT_CTECONFIG_T46)); + if (ts->config_setting[CONNECTED].config_T48 != NULL) { + i2c_atmel_write(ts->client, + get_object_address(ts, PROCG_NOISESUPPRESSION_T48), + ts->config_setting[CONNECTED].config_T48, + get_object_size(ts, PROCG_NOISESUPPRESSION_T48)); + } + } + } + ts->calibration_confirm = 0; + ts->input_dev = input_allocate_device(); + if (ts->input_dev == NULL) { + ret = -ENOMEM; + dev_err(&client->dev, "k3ts, %s: Failed to allocate input device\n", __func__); + goto err_input_dev_alloc_failed; + } + /*Modified by z181527 for Debug Only*/ + ts->input_dev->name = "synaptics"/*"atmel-touchscreen"*/; + set_bit(EV_SYN, ts->input_dev->evbit); + set_bit(EV_KEY, ts->input_dev->evbit); + set_bit(BTN_TOUCH, ts->input_dev->keybit); + set_bit(BTN_2, ts->input_dev->keybit); + set_bit(EV_ABS, ts->input_dev->evbit); + set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, + ts->abs_x_min, ts->abs_x_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, + ts->abs_y_min, ts->abs_y_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, + ts->abs_pressure_min, ts->abs_pressure_max, + 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, + ts->abs_width_min, ts->abs_width_max, 0, 0); + + ret = input_register_device(ts->input_dev); + if (ret) { + dev_err(&client->dev, + "k3ts, %s: atmel_ts_probe: Unable to register %s input device\n", __func__, + ts->input_dev->name); + goto err_input_register_device_failed; + } + + ret = request_threaded_irq(client->irq, NULL, atmel_interrupt_fun, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->name, ts); + if (ret) + dev_err(&client->dev, "k3ts, %s: request_irq failed\n", __func__); + + private_ts = ts; + + dev_info(&client->dev, "k3ts, %s: probe %s successfully\n", __func__, + ts->input_dev->name); + + return 0; + +err_input_register_device_failed: + input_free_device(ts->input_dev); +err_input_dev_alloc_failed: +err_read_table_failed: + kfree(ts->id); +err_alloc_failed: +err_detect_failed: +err_gpio_direction_failed: +err_request_gpio_reset_failed: + gpio_free(ts->pdata->gpio_reset); + gpio_free(intr); +err_request_gpio_failed: + destroy_workqueue(ts->atmel_wq); +err_cread_wq_failed: + kfree(ts); +err_alloc_data_failed: +err_check_functionality_failed: + if (LDO != NULL) { + regulator_disable(LDO); + regulator_put(LDO); + } + return ret; +} + +static int atmel_ts_remove(struct i2c_client *client) +{ + struct atmel_ts_data *ts = i2c_get_clientdata(client); + + free_irq(client->irq, ts); + + destroy_workqueue(ts->atmel_wq); + input_unregister_device(ts->input_dev); + kfree(ts); + + regulator_disable(LDO); + regulator_put(LDO); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int atmel_ts_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct atmel_ts_data *ts = i2c_get_clientdata(client); + struct atmel_i2c_platform_data *pdata = ts->pdata; + uint8_t data[7]; + int ret = 0; + + mutex_lock(&ts->lock); + ts->finger_pressed = 0; + ts->finger_count = 0; + ts->first_pressed = 0; + + if (ts->id->version >= 0x10) { + ts->pre_data[0] = 0; + ret = i2c_atmel_write(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + T8_CFG_ATCHCALST, + ts->ATCH_EXT, 4); + if (ret < 0) + pr_err("k3ts, %s: failed to write config T8\n", __func__); + } + + ret = i2c_atmel_write_byte_data(client, + get_object_address(ts, GEN_POWERCONFIG_T7) + T7_CFG_IDLEACQINT, 0x0); + if (ret < 0) + pr_err("k3ts, %s: failed to write config T7\n", __func__); + + ret = i2c_atmel_write_byte_data(client, + get_object_address(ts, GEN_POWERCONFIG_T7) + T7_CFG_ACTVACQINT, 0x0); + if (ret < 0) + pr_err("k3ts, %s: failed to write config T7\n", __func__); + + /* Read T5 until gpio_irq is HIGH level */ + if (!gpio_get_value(pdata->gpio_irq)) { + ret = i2c_atmel_read(ts->client, get_object_address(ts, + GEN_MESSAGEPROCESSOR_T5), data, 7); + if (ret < 0) { + pr_err("k3ts, %s: failed to read T5\n", __func__); + } + } + + mutex_unlock(&ts->lock); + + pr_info("[%s]: -\n", __func__); + return 0; +} + +static int atmel_ts_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct atmel_ts_data *ts = i2c_get_clientdata(client); + int ret = 0; + + pr_info("[%s]: +\n", __func__); + + mutex_lock(&ts->lock); + if (ts->id->version >= 0x10) + ts->timestamp = jiffies; + + ts->unlock_flag = 0; + + ret = i2c_atmel_write(ts->client, + get_object_address(ts, GEN_POWERCONFIG_T7), + ts->config_setting[ts->status].config_T7, + get_object_size(ts, GEN_POWERCONFIG_T7)); + if (ret < 0) + pr_err("k3ts, %s: failed to write config T7\n", __func__); + + ts->calibration_confirm = 0; + msleep(1); + + ret = i2c_atmel_write(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + + T8_CFG_TCHAUTOCAL, ts->ATCH_NOR, 6); + if (ret < 0) + pr_err("k3ts, %s: failed to write config T8\n", __func__); + + ret = i2c_atmel_write_byte_data(client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_CALIBRATE, 0x55); + if (ret < 0) + pr_err("k3ts, %s: failed to write config T6\n", __func__); + + mutex_unlock(&ts->lock); + + pr_info("[%s]: -\n", __func__); + + return 0; +} +#endif +static SIMPLE_DEV_PM_OPS(atmel_ts_pm_ops, atmel_ts_suspend, atmel_ts_resume); + +static const struct i2c_device_id atml_ts_i2c_id[] = { + { ATMEL_MXT224E_NAME, 0 }, + { } +}; + +#ifdef CONFIG_OF +static const struct of_device_id atmel_ts_dt_ids[] = { + { .compatible = "atmel,ts-mxt224e", }, + { } +}; +MODULE_DEVICE_TABLE(of, atmel_ts_dt_ids); +#endif + +static struct i2c_driver atmel_ts_driver = { + .id_table = atml_ts_i2c_id, + .probe = atmel_ts_probe, + .remove = atmel_ts_remove, + .driver = { + .of_match_table = of_match_ptr(atmel_ts_dt_ids), + .name = ATMEL_MXT224E_NAME, + .pm = &atmel_ts_pm_ops, + }, +}; +module_i2c_driver(atmel_ts_driver); + +MODULE_DESCRIPTION("ATMEL Touch driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 89d9d44683ab..dec56df228e1 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1151,6 +1151,14 @@ config MFD_WM8994 core support for the WM8994, in order to use the actual functionaltiy of the device other drivers must be enabled. +config MFD_HI6421_PMIC + tristate "HiSilicon Hi6421 PMU/Codec IC" + depends on OF + help + This driver supports HiSilicon Hi6421 power management and codec IC, + including regulators, codec, ADCs, Coulomb counter, etc. Memory + mapped I/O ports are the way of communication with it. + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 409fb6260926..d60a7cab76c9 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -163,3 +163,4 @@ obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o vexpress-sysreg.o obj-$(CONFIG_VEXPRESS_SPC) += vexpress-spc.o obj-$(CONFIG_MFD_RETU) += retu-mfd.o obj-$(CONFIG_MFD_AS3711) += as3711.o +obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o diff --git a/drivers/mfd/hi6421-pmic-core.c b/drivers/mfd/hi6421-pmic-core.c new file mode 100644 index 000000000000..b2d7c4e82bd2 --- /dev/null +++ b/drivers/mfd/hi6421-pmic-core.c @@ -0,0 +1,302 @@ +/* + * Device driver for regulators in Hi6421 IC + * + * Copyright (c) 2013 Linaro Ltd. + * Copyright (c) 2011 Hisilicon. + * + * Guodong Xu <guodong.xu@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. + * + * 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/slab.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> +#include <linux/mfd/hi6421-pmic.h> + +#include <asm/mach/irq.h> + +/* 8-bit register offset in PMIC */ +#define HI6421_REG_IRQ1 1 +#define HI6421_REG_IRQ2 2 +#define HI6421_REG_IRQ3 3 +#define HI6421_REG_IRQM1 4 +#define HI6421_REG_IRQM2 5 +#define HI6421_REG_IRQM3 6 + +static struct of_device_id of_hi6421_pmic_child_match_tbl[] = { + /* regulators */ + { + .compatible = "hisilicon,hi6421-ldo", + }, + { + .compatible = "hisilicon,hi6421-buck012", + }, + { + .compatible = "hisilicon,hi6421-buck345", + }, + { /* end */ } +}; + +static struct of_device_id of_hi6421_pmic_match_tbl[] = { + { + .compatible = "hisilicon,hi6421-pmic", + }, + { /* end */ } +}; + +/* + * The PMIC register is only 8-bit. + * Hisilicon SoC use hardware to map PMIC register into SoC mapping. + * At here, we are accessing SoC register with 32-bit. + */ +u32 hi6421_pmic_read(struct hi6421_pmic *pmic, int reg) +{ + unsigned long flags; + u32 ret; + spin_lock_irqsave(&pmic->lock, flags); + ret = readl_relaxed(pmic->regs + (reg << 2)); + spin_unlock_irqrestore(&pmic->lock, flags); + return ret; +} +EXPORT_SYMBOL(hi6421_pmic_read); + +void hi6421_pmic_write(struct hi6421_pmic *pmic, int reg, u32 val) +{ + unsigned long flags; + spin_lock_irqsave(&pmic->lock, flags); + writel_relaxed(val, pmic->regs + (reg << 2)); + spin_unlock_irqrestore(&pmic->lock, flags); +} +EXPORT_SYMBOL(hi6421_pmic_write); + +void hi6421_pmic_rmw(struct hi6421_pmic *pmic, int reg, + u32 mask, u32 bits) +{ + u32 data; + + spin_lock(&pmic->lock); + data = readl_relaxed(pmic->regs + (reg << 2)) & ~mask; + data |= mask & bits; + writel_relaxed(data, pmic->regs + (reg << 2)); + spin_unlock(&pmic->lock); +} +EXPORT_SYMBOL(hi6421_pmic_rmw); + +static int hi6421_to_irq(struct hi6421_pmic *pmic, unsigned offset) +{ + return irq_create_mapping(pmic->domain, offset); +} + +static irqreturn_t hi6421_irq_handler(int irq, void *data) +{ + struct hi6421_pmic *pmic = (struct hi6421_pmic *)data; + unsigned long pending; + int i, offset, index; + + + for (i = HI6421_REG_IRQ1; i <= HI6421_REG_IRQ3; i++) { + spin_lock(&pmic->lock); + pending = readl_relaxed(pmic->regs + (i << 2)); + pending &= HI6421_MASK_FIELD; + writel_relaxed(pending, pmic->regs + ((i + 3) << 2)); + spin_unlock(&pmic->lock); + + if (pending) { + for_each_set_bit(offset, &pending, HI6421_BITS) { + index = offset + (i - HI6421_REG_IRQ1) * HI6421_BITS; + generic_handle_irq(hi6421_to_irq(pmic, index)); + } + } + + spin_lock(&pmic->lock); + writel_relaxed(0, pmic->regs + ((i + 3) << 2)); + writel_relaxed(pending, pmic->regs + (i << 2)); + spin_unlock(&pmic->lock); + } + + return IRQ_HANDLED; +} + +static void hi6421_irq_mask(struct irq_data *d) +{ + struct hi6421_pmic *pmic = irq_data_get_irq_chip_data(d); + u32 data, offset; + + offset = ((irqd_to_hwirq(d) >> 3) + HI6421_REG_IRQM1) << 2; + spin_lock(&pmic->lock); + data = readl_relaxed(pmic->regs + offset); + data |= irqd_to_hwirq(d) % 8; + writel_relaxed(data, pmic->regs + offset); + spin_unlock(&pmic->lock); +} + +static void hi6421_irq_unmask(struct irq_data *d) +{ + struct hi6421_pmic *pmic = irq_data_get_irq_chip_data(d); + u32 data, offset; + + offset = ((irqd_to_hwirq(d) >> 3) + HI6421_REG_IRQM1) << 2; + spin_lock(&pmic->lock); + data = readl_relaxed(pmic->regs + offset); + data &= ~(irqd_to_hwirq(d) % 8); + writel_relaxed(data, pmic->regs + offset); + spin_unlock(&pmic->lock); +} + +static struct irq_chip hi6421_irqchip = { + .name = "pmic", + .irq_mask = hi6421_irq_mask, + .irq_unmask = hi6421_irq_unmask, +}; + +static int hi6421_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) +{ + struct hi6421_pmic *pmic = d->host_data; + + irq_set_chip_and_handler_name(virq, &hi6421_irqchip, + handle_simple_irq, "hi6421"); + irq_set_chip_data(virq, pmic); + irq_set_irq_type(virq, IRQ_TYPE_NONE); + + return 0; +} + +static struct irq_domain_ops hi6421_domain_ops = { + .map = hi6421_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +static int hi6421_pmic_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct hi6421_pmic *pmic = NULL; + enum of_gpio_flags flags; + int ret; + + pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); + if (!pmic) { + dev_err(dev, "cannot allocate hi6421_pmic device info\n"); + return -ENOMEM; + } + + mutex_init(&pmic->enable_mutex); + /* get resources */ + pmic->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!pmic->res) { + dev_err(dev, "platform_get_resource err\n"); + return -ENOENT; + } + + if (!devm_request_mem_region(dev, pmic->res->start, + resource_size(pmic->res), + pdev->name)) { + dev_err(dev, "cannot claim register memory\n"); + return -ENOMEM; + } + + pmic->regs = devm_ioremap(dev, pmic->res->start, + resource_size(pmic->res)); + if (!pmic->regs) { + dev_err(dev, "cannot map register memory\n"); + return -ENOMEM; + } + + /* TODO: get and enable clk request */ + + spin_lock_init(&pmic->lock); + + pmic->gpio = of_get_gpio_flags(np, 0, &flags); + if (pmic->gpio < 0) + return pmic->gpio; + if (!gpio_is_valid(pmic->gpio)) + return -EINVAL; + ret = gpio_request_one(pmic->gpio, GPIOF_IN, "pmic"); + if (ret < 0) { + dev_err(dev, "failed to request gpio%d\n", pmic->gpio); + return ret; + } + pmic->irq = gpio_to_irq(pmic->gpio); + /* clear IRQ status */ + spin_lock(&pmic->lock); + writel_relaxed(0xff, pmic->regs + (HI6421_REG_IRQ1 << 2)); + writel_relaxed(0xff, pmic->regs + (HI6421_REG_IRQ2 << 2)); + writel_relaxed(0xff, pmic->regs + (HI6421_REG_IRQ3 << 2)); + spin_unlock(&pmic->lock); + + pmic->domain = irq_domain_add_simple(np, HI6421_NR_IRQ, 0, + &hi6421_domain_ops, pmic); + if (!pmic->domain) + return -ENODEV; + + ret = request_threaded_irq(pmic->irq, hi6421_irq_handler, NULL, + IRQF_TRIGGER_LOW | IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND, + "pmic", pmic); + + platform_set_drvdata(pdev, pmic); + + /* set over-current protection debounce 8ms*/ + hi6421_pmic_rmw(pmic, OCP_DEB_CTRL_REG, \ + OCP_DEB_SEL_MASK | OCP_EN_DEBOUNCE_MASK | OCP_AUTO_STOP_MASK, \ + OCP_DEB_SEL_8MS | OCP_EN_DEBOUNCE_ENABLE); + + /* populate sub nodes */ + of_platform_populate(np, of_hi6421_pmic_child_match_tbl, NULL, dev); + + return 0; +} + +static int hi6421_pmic_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct hi6421_pmic *pmic = platform_get_drvdata(pdev); + + free_irq(pmic->irq, pmic); + gpio_free(pmic->gpio); + devm_iounmap(dev, pmic->regs); + devm_release_mem_region(dev, pmic->res->start, + resource_size(pmic->res)); + devm_kfree(dev, pmic); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver hi6421_pmic_driver = { + .driver = { + .name = "hi6421_pmic", + .owner = THIS_MODULE, + .of_match_table = of_hi6421_pmic_match_tbl, + }, + .probe = hi6421_pmic_probe, + .remove = hi6421_pmic_remove, +}; +module_platform_driver(hi6421_pmic_driver); + +MODULE_AUTHOR("Guodong Xu <guodong.xu@linaro.org>"); +MODULE_DESCRIPTION("Hi6421 PMIC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 7e89650fb31e..f3723357a64f 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -575,6 +575,16 @@ config MMC_DW_SOCFPGA This selects support for Altera SoCFPGA specific extensions to the Synopsys DesignWare Memory Card Interface driver. +config MMC_DW_K3 + tristate "K3 specific extensions for Synopsys DW Memory Card Interface" + depends on MMC_DW + select MMC_DW_PLTFM + select MMC_DW_IDMAC + help + This selects support for Hisilicon K3 SoC specific extensions to the + Synopsys DesignWare Memory Card Interface driver. Select this option + for platforms based on Hisilicon K3 SoC's. + config MMC_DW_PCI tristate "Synopsys Designware MCI support on PCI bus" depends on MMC_DW && PCI diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index c41d0c364509..64f5f8d35839 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_MMC_DW) += dw_mmc.o obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o obj-$(CONFIG_MMC_DW_SOCFPGA) += dw_mmc-socfpga.o +obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c new file mode 100644 index 000000000000..f46453fe0c57 --- /dev/null +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2013 Linaro Ltd. + * Copyright (c) 2013 Hisilicon Limited. + * + * 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. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/mmc/host.h> +#include <linux/mmc/dw_mmc.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_address.h> + +#include "dw_mmc.h" +#include "dw_mmc-pltfm.h" + +#define DRIVER_NAME "dwmmc_k3" + +enum dw_mci_k3_type { + DW_MCI_TYPE_HI4511, +}; + +static struct dw_mci_k3_compatible { + char *compatible; + enum dw_mci_k3_type type; +} k3_compat[] = { + { + .compatible = "hisilicon,hi4511-dw-mshc", + .type = DW_MCI_TYPE_HI4511, + }, +}; + +struct dw_mci_k3_priv_data { + enum dw_mci_k3_type type; + int old_timing; + u32 id; + u32 gpio_cd; + u32 clken_reg; + u32 clken_bit; + u32 sam_sel_reg; + u32 sam_sel_bit; + u32 drv_sel_reg; + u32 drv_sel_bit; + u32 div_reg; + u32 div_bit; +}; + +static void __iomem *pctrl; +static DEFINE_SPINLOCK(mmc_tuning_lock); +static int k3_tuning_config[][8][6] = { + /* bus_clk, div, drv_sel, sam_sel_max, sam_sel_min, input_clk */ + { + {180000000, 6, 6, 13, 13, 25000000}, /* 0: LEGACY 400k */ + {0}, /* 1: MMC_HS */ + {360000000, 6, 4, 2, 0, 50000000 }, /* 2: SD_HS */ + {180000000, 6, 4, 13, 13, 25000000}, /* 3: SDR12 */ + {360000000, 6, 4, 2, 0, 50000000 }, /* 4: SDR25 */ + {720000000, 6, 1, 9, 4, 100000000 }, /* 5: SDR50 */ + {0}, /* 6: SDR104 */ + {360000000, 7, 1, 3, 0, 50000000 }, /* 7: DDR50 */ + }, { + {26000000, 1, 1, 3, 3, 13000000 }, /* 0: LEGACY 400k */ + {360000000, 6, 3, 3, 1, 50000000 }, /* 1: MMC_HS*/ + {0}, /* 2: SD_HS */ + {0}, /* 3: SDR12 */ + {26000000, 1, 1, 3, 3, 13000000 }, /* 4: SDR25 */ + {360000000, 6, 3, 3, 1, 50000000 }, /* 5: SDR50 */ + {0}, /* 6: SDR104 */ + {720000000, 6, 4, 8, 4, 100000000}, /* 7: DDR50 */ + }, +}; + +static void dw_mci_k3_set_timing(struct dw_mci_k3_priv_data *priv, + int idx, int sam, int drv, int div) +{ + unsigned int clken_reg = priv->clken_reg; + unsigned int clken_bit = priv->clken_bit; + unsigned int sam_sel_reg = priv->sam_sel_reg; + unsigned int sam_sel_bit = priv->sam_sel_bit; + unsigned int drv_sel_reg = priv->drv_sel_reg; + unsigned int drv_sel_bit = priv->drv_sel_bit; + unsigned int div_reg = priv->div_reg; + unsigned int div_bit = priv->div_bit; + int i = 0; + unsigned int temp_reg; + unsigned long flags; + + spin_lock_irqsave(&mmc_tuning_lock, flags); + + /* disable clock */ + temp_reg = readl(pctrl + clken_reg); + temp_reg &= ~(1<<clken_bit); + writel(temp_reg, pctrl + clken_reg); + + temp_reg = readl(pctrl + sam_sel_reg); + if (sam >= 0) { + /* set sam delay */ + for (i = 0; i < 4; i++) { + if (sam % 2) + temp_reg |= 1<<(sam_sel_bit + i); + else + temp_reg &= ~(1<<(sam_sel_bit + i)); + sam = sam >> 1; + } + } + writel(temp_reg, pctrl + sam_sel_reg); + + temp_reg = readl(pctrl + drv_sel_reg); + if (drv >= 0) { + /* set drv delay */ + for (i = 0; i < 4; i++) { + if (drv % 2) + temp_reg |= 1<<(drv_sel_bit + i); + else + temp_reg &= ~(1<<(drv_sel_bit + i)); + drv = drv >> 1; + } + } + writel(temp_reg, pctrl + drv_sel_reg); + + temp_reg = readl(pctrl + div_reg); + if (div >= 0) { + /* set drv delay */ + for (i = 0; i < 3; i++) { + if (div % 2) + temp_reg |= 1<<(div_bit + i); + else + temp_reg &= ~(1<<(div_bit + i)); + div = div >> 1; + } + } + writel(temp_reg, pctrl + div_reg); + + /* enable clock */ + temp_reg = readl(pctrl + clken_reg); + temp_reg |= 1<<clken_bit; + writel(temp_reg, pctrl + clken_reg); + + spin_unlock_irqrestore(&mmc_tuning_lock, flags); +} + +static void dw_mci_k3_tun(struct dw_mci *host, int id, int index) +{ + struct dw_mci_k3_priv_data *priv = host->priv; + int ret; + + if (!pctrl) + return; + + if (priv->old_timing == index) + return; + + ret = clk_set_rate(host->ciu_clk, k3_tuning_config[id][index][0]); + if (ret) + dev_err(host->dev, "clk_set_rate failed\n"); + + dw_mci_k3_set_timing(priv, id, + (k3_tuning_config[id][index][3] + + k3_tuning_config[id][index][4]) / 2, + k3_tuning_config[id][index][2], + k3_tuning_config[id][index][1]); + + host->bus_hz = k3_tuning_config[id][index][5]; + priv->old_timing = index; +} + +static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios) +{ + struct dw_mci_k3_priv_data *priv = host->priv; + int id = priv->id; + + if (priv->type == DW_MCI_TYPE_HI4511) + dw_mci_k3_tun(host, id, ios->timing); +} + +static int dw_mci_k3_priv_init(struct dw_mci *host) +{ + struct dw_mci_k3_priv_data *priv; + int i; + + priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(host->dev, "mem alloc failed for private data\n"); + return -ENOMEM; + } + priv->id = of_alias_get_id(host->dev->of_node, "mshc"); + priv->old_timing = -1; + host->priv = priv; + + for (i = 0; i < ARRAY_SIZE(k3_compat); i++) { + if (of_device_is_compatible(host->dev->of_node, + k3_compat[i].compatible)) + priv->type = k3_compat[i].type; + } + + if (priv->type == DW_MCI_TYPE_HI4511) { + if (!pctrl) { + struct device_node *node; + + node = of_find_compatible_node(NULL, NULL, + "hisilicon,pctrl"); + pctrl = of_iomap(node, 0); + } + } + + return 0; +} + +static int dw_mci_k3_setup_clock(struct dw_mci *host) +{ + struct dw_mci_k3_priv_data *priv = host->priv; + + if (priv->type == DW_MCI_TYPE_HI4511) + dw_mci_k3_tun(host, priv->id, MMC_TIMING_LEGACY); + + return 0; +} + + +static irqreturn_t dw_mci_k3_card_detect(int irq, void *data) +{ + struct dw_mci *host = (struct dw_mci *)data; + + queue_work(host->card_workqueue, &host->card_work); + return IRQ_HANDLED; +}; + +static int dw_mci_k3_get_cd(struct dw_mci *host, u32 slot_id) +{ + unsigned int status; + struct dw_mci_k3_priv_data *priv = host->priv; + + status = !gpio_get_value(priv->gpio_cd); + return status; +} + +static int dw_mci_k3_parse_dt(struct dw_mci *host) +{ + struct dw_mci_k3_priv_data *priv = host->priv; + struct device_node *np = host->dev->of_node; + u32 data[2]; + int ret; + + if (priv->type == DW_MCI_TYPE_HI4511) { + ret = of_property_read_u32_array(np, + "clken-reg", data, 2); + if (!ret) { + priv->clken_reg = data[0]; + priv->clken_bit = data[1]; + } + + ret = of_property_read_u32_array(np, + "drv-sel-reg", data, 2); + if (!ret) { + priv->drv_sel_reg = data[0]; + priv->drv_sel_bit = data[1]; + } + + ret = of_property_read_u32_array(np, + "sam-sel-reg", data, 2); + if (!ret) { + priv->sam_sel_reg = data[0]; + priv->sam_sel_bit = data[1]; + } + + ret = of_property_read_u32_array(np, + "div-reg", data, 2); + if (!ret) { + priv->div_reg = data[0]; + priv->div_bit = data[1]; + } + } + + return 0; +} + +static unsigned long k3_dwmmc_caps[4] = { + MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED, + MMC_CAP_8_BIT_DATA | MMC_CAP_MMC_HIGHSPEED, + 0, + 0, +}; + +static const struct dw_mci_drv_data k3_drv_data = { + .caps = k3_dwmmc_caps, + .init = dw_mci_k3_priv_init, + .set_ios = dw_mci_k3_set_ios, + .setup_clock = dw_mci_k3_setup_clock, + .parse_dt = dw_mci_k3_parse_dt, +}; + +static const struct of_device_id dw_mci_k3_match[] = { + { .compatible = "hisilicon,hi4511-dw-mshc", + .data = &k3_drv_data, }, + {}, +}; +MODULE_DEVICE_TABLE(of, dw_mci_k3_match); + +int dw_mci_k3_probe(struct platform_device *pdev) +{ + const struct dw_mci_drv_data *drv_data; + const struct of_device_id *match; + struct dw_mci *host; + int gpio, err; + + match = of_match_node(dw_mci_k3_match, pdev->dev.of_node); + drv_data = match->data; + + err = dw_mci_pltfm_register(pdev, drv_data); + if (err) + return err; + + host = platform_get_drvdata(pdev); + if (host->pdata->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION) + return 0; + + gpio = of_get_named_gpio(pdev->dev.of_node, "cd-gpio", 0); + if (gpio_is_valid(gpio)) { + if (devm_gpio_request(host->dev, gpio, "dw-mci-cd")) { + dev_err(host->dev, "gpio [%d] request failed\n", gpio); + } else { + struct dw_mci_k3_priv_data *priv = host->priv; + priv->gpio_cd = gpio; + host->pdata->get_cd = dw_mci_k3_get_cd; + err = devm_request_irq(host->dev, gpio_to_irq(gpio), + dw_mci_k3_card_detect, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + DRIVER_NAME, host); + if (err) + dev_warn(mmc_dev(host->dev), "request gpio irq error\n"); + } + + } else { + dev_info(host->dev, "cd gpio not available"); + } + return 0; +} + +static int dw_mci_k3_suspend(struct device *dev) +{ + int ret; + struct dw_mci *host = dev_get_drvdata(dev); + + ret = dw_mci_suspend(host); + if (ret) + return ret; + + return 0; +} + +static int dw_mci_k3_resume(struct device *dev) +{ + int ret; + struct dw_mci *host = dev_get_drvdata(dev); + struct dw_mci_k3_priv_data *priv = host->priv; + + if (priv->type == DW_MCI_TYPE_HI4511) { + int id = priv->id; + + priv->old_timing = -1; + dw_mci_k3_tun(host, id, MMC_TIMING_LEGACY); + } + + ret = dw_mci_resume(host); + if (ret) + return ret; + + return 0; +} + +SIMPLE_DEV_PM_OPS(dw_mci_k3_pmops, dw_mci_k3_suspend, dw_mci_k3_resume); + +static struct platform_driver dw_mci_k3_pltfm_driver = { + .probe = dw_mci_k3_probe, + .remove = dw_mci_pltfm_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = dw_mci_k3_match, + .pm = &dw_mci_k3_pmops, + }, +}; + +module_platform_driver(dw_mci_k3_pltfm_driver); + +MODULE_DESCRIPTION("K3 Specific DW-MSHC Driver Extension"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index d73b52daef71..e49f16e88e99 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -877,7 +877,7 @@ static int dw_mci_get_cd(struct mmc_host *mmc) if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION) present = 1; else if (brd->get_cd) - present = !brd->get_cd(slot->id); + present = !brd->get_cd(slot->host, slot->id); else present = (mci_readl(slot->host, CDETECT) & (1 << slot->id)) == 0 ? 1 : 0; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index dfe58096b374..af7086c9ba08 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -571,5 +571,12 @@ config REGULATOR_WM8994 This driver provides support for the voltage regulators on the WM8994 CODEC. +config REGULATOR_HI6421 + tristate "HiSilicon Hi6421 PMIC regulators" + depends on MFD_HI6421_PMIC + help + This driver provides support for the voltage regulators on the + HiSilicon Hi6421 PMU / Codec IC. + endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 185cce246022..556ab2946eb9 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -76,6 +76,6 @@ obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o - +obj-$(CONFIG_REGULATOR_HI6421) += hi6421-regulator.o ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/hi6421-regulator.c b/drivers/regulator/hi6421-regulator.c new file mode 100644 index 000000000000..9ffef468faaa --- /dev/null +++ b/drivers/regulator/hi6421-regulator.c @@ -0,0 +1,556 @@ +/* + * Device driver for regulators in Hi6421 IC + * + * Copyright (c) 2013 Linaro Ltd. + * Copyright (c) 2011 Hisilicon. + * + * Guodong Xu <guodong.xu@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. + * + * 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/slab.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/mfd/hi6421-pmic.h> +#include <linux/delay.h> +#include <linux/time.h> + +struct hi6421_regulator_register_info { + u32 ctrl_reg; + u32 enable_mask; + u32 eco_mode_mask; + u32 vset_reg; + u32 vset_mask; +}; + +struct hi6421_regulator { + const char *name; + struct hi6421_regulator_register_info register_info; + struct timeval last_off_time; + u32 off_on_delay; + u32 eco_uA; + struct regulator_desc rdesc; + int (*dt_parse)(struct hi6421_regulator *, struct platform_device *); +}; + +static inline struct hi6421_pmic *rdev_to_pmic(struct regulator_dev *dev) +{ + /* regulator_dev parent to-> + * hi6421 regulator platform device_dev parent to-> + * hi6421 pmic platform device_dev + */ + return dev_get_drvdata(rdev_get_dev(dev)->parent->parent); +} + +/* helper function to ensure when it returns it is at least 'delay_us' + * microseconds after 'since'. + */ +static void ensured_time_after(struct timeval since, u32 delay_us) +{ + struct timeval now; + u64 elapsed_ns64, delay_ns64; + u32 actual_us32; + + delay_ns64 = delay_us * NSEC_PER_USEC; + do_gettimeofday(&now); + elapsed_ns64 = timeval_to_ns(&now) - timeval_to_ns(&since); + if (delay_ns64 > elapsed_ns64) { + actual_us32 = ((u32)(delay_ns64 - elapsed_ns64) / + NSEC_PER_USEC); + if (actual_us32 >= 1000) { + mdelay(actual_us32 / 1000); + udelay(actual_us32 % 1000); + } else if (actual_us32 > 0) { + udelay(actual_us32); + } + } + return; +} + +static int hi6421_regulator_is_enabled(struct regulator_dev *dev) +{ + u32 reg_val; + struct hi6421_regulator *sreg = rdev_get_drvdata(dev); + struct hi6421_pmic *pmic = rdev_to_pmic(dev); + + reg_val = hi6421_pmic_read(pmic, sreg->register_info.ctrl_reg); + + return ((reg_val & sreg->register_info.enable_mask) != 0); +} + +static int hi6421_regulator_enable(struct regulator_dev *dev) +{ + struct hi6421_regulator *sreg = rdev_get_drvdata(dev); + struct hi6421_pmic *pmic = rdev_to_pmic(dev); + + /* keep a distance of off_on_delay from last time disabled */ + ensured_time_after(sreg->last_off_time, sreg->off_on_delay); + + /* cannot enable more than one regulator at one time */ + mutex_lock(&pmic->enable_mutex); + ensured_time_after(pmic->last_enabled, HI6421_REGS_ENA_PROTECT_TIME); + + /* set enable register */ + hi6421_pmic_rmw(pmic, sreg->register_info.ctrl_reg, + sreg->register_info.enable_mask, + sreg->register_info.enable_mask); + + do_gettimeofday(&pmic->last_enabled); + mutex_unlock(&pmic->enable_mutex); + + return 0; +} + +static int hi6421_regulator_disable(struct regulator_dev *dev) +{ + struct hi6421_regulator *sreg = rdev_get_drvdata(dev); + struct hi6421_pmic *pmic = rdev_to_pmic(dev); + + /* set enable register to 0 */ + hi6421_pmic_rmw(pmic, sreg->register_info.ctrl_reg, + sreg->register_info.enable_mask, 0); + + do_gettimeofday(&sreg->last_off_time); + + return 0; +} + +static int hi6421_regulator_get_voltage(struct regulator_dev *dev) +{ + struct hi6421_regulator *sreg = rdev_get_drvdata(dev); + struct hi6421_pmic *pmic = rdev_to_pmic(dev); + u32 reg_val, selector; + + /* get voltage selector */ + reg_val = hi6421_pmic_read(pmic, sreg->register_info.vset_reg); + selector = (reg_val & sreg->register_info.vset_mask) >> + (ffs(sreg->register_info.vset_mask) - 1); + + return sreg->rdesc.ops->list_voltage(dev, selector); +} + +static int hi6421_regulator_ldo_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV, unsigned *selector) +{ + struct hi6421_regulator *sreg = rdev_get_drvdata(dev); + struct hi6421_pmic *pmic = rdev_to_pmic(dev); + u32 vsel; + int ret = 0; + + for (vsel = 0; vsel < sreg->rdesc.n_voltages; vsel++) { + int uV = sreg->rdesc.volt_table[vsel]; + /* Break at the first in-range value */ + if (min_uV <= uV && uV <= max_uV) + break; + } + + /* unlikely to happen. sanity test done by regulator core */ + if (unlikely(vsel == sreg->rdesc.n_voltages)) + return -EINVAL; + + *selector = vsel; + /* set voltage selector */ + hi6421_pmic_rmw(pmic, sreg->register_info.vset_reg, + sreg->register_info.vset_mask, + vsel << (ffs(sreg->register_info.vset_mask) - 1)); + + return ret; +} + +static int hi6421_regulator_buck012_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV, unsigned *selector) +{ + struct hi6421_regulator *sreg = rdev_get_drvdata(dev); + struct hi6421_pmic *pmic = rdev_to_pmic(dev); + u32 vsel; + int ret = 0; + + vsel = DIV_ROUND_UP((max_uV - sreg->rdesc.min_uV), + sreg->rdesc.uV_step); + + *selector = vsel; + /* set voltage selector */ + hi6421_pmic_rmw(pmic, sreg->register_info.vset_reg, + sreg->register_info.vset_mask, + vsel << (ffs(sreg->register_info.vset_mask) - 1)); + + return ret; +} + +static unsigned int hi6421_regulator_get_mode(struct regulator_dev *dev) +{ + struct hi6421_regulator *sreg = rdev_get_drvdata(dev); + struct hi6421_pmic *pmic = rdev_to_pmic(dev); + u32 reg_val; + + reg_val = hi6421_pmic_read(pmic, sreg->register_info.ctrl_reg); + if (reg_val & sreg->register_info.eco_mode_mask) + return REGULATOR_MODE_IDLE; + else + return REGULATOR_MODE_NORMAL; +} + +static int hi6421_regulator_set_mode(struct regulator_dev *dev, + unsigned int mode) +{ + struct hi6421_regulator *sreg = rdev_get_drvdata(dev); + struct hi6421_pmic *pmic = rdev_to_pmic(dev); + u32 eco_mode; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + eco_mode = HI6421_ECO_MODE_DISABLE; + break; + case REGULATOR_MODE_IDLE: + eco_mode = HI6421_ECO_MODE_ENABLE; + break; + default: + return -EINVAL; + } + + /* set mode */ + hi6421_pmic_rmw(pmic, sreg->register_info.ctrl_reg, + sreg->register_info.eco_mode_mask, + eco_mode << (ffs(sreg->register_info.eco_mode_mask) - 1)); + + return 0; +} + + +unsigned int hi6421_regulator_get_optimum_mode(struct regulator_dev *dev, + int input_uV, int output_uV, int load_uA) +{ + struct hi6421_regulator *sreg = rdev_get_drvdata(dev); + + if ((load_uA == 0) || (load_uA > sreg->eco_uA)) + return REGULATOR_MODE_NORMAL; + else + return REGULATOR_MODE_IDLE; +} + +static int hi6421_dt_parse_common(struct hi6421_regulator *sreg, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct regulator_desc *rdesc = &sreg->rdesc; + unsigned int register_info[3]; + int ret = 0; + + /* parse .register_info.ctrl_reg */ + ret = of_property_read_u32_array(np, "hisilicon,hi6421-ctrl", + register_info, 3); + if (ret) { + dev_err(dev, "no hisilicon,hi6421-ctrl property set\n"); + goto dt_parse_common_end; + } + sreg->register_info.ctrl_reg = register_info[0]; + sreg->register_info.enable_mask = register_info[1]; + sreg->register_info.eco_mode_mask = register_info[2]; + + /* parse .register_info.vset_reg */ + ret = of_property_read_u32_array(np, "hisilicon,hi6421-vset", + register_info, 2); + if (ret) { + dev_err(dev, "no hisilicon,hi6421-vset property set\n"); + goto dt_parse_common_end; + } + sreg->register_info.vset_reg = register_info[0]; + sreg->register_info.vset_mask = register_info[1]; + + /* parse .off-on-delay */ + ret = of_property_read_u32(np, "hisilicon,hi6421-off-on-delay-us", + &sreg->off_on_delay); + if (ret) { + dev_err(dev, "no hisilicon,hi6421-off-on-delay-us property set\n"); + goto dt_parse_common_end; + } + + /* parse .enable_time */ + ret = of_property_read_u32(np, "hisilicon,hi6421-enable-time-us", + &rdesc->enable_time); + if (ret) { + dev_err(dev, "no hisilicon,hi6421-enable-time-us property set\n"); + goto dt_parse_common_end; + } + + /* parse .eco_uA */ + ret = of_property_read_u32(np, "hisilicon,hi6421-eco-microamp", + &sreg->eco_uA); + if (ret) { + sreg->eco_uA = 0; + ret = 0; + } + +dt_parse_common_end: + return ret; +} + +static int hi6421_dt_parse_ldo(struct hi6421_regulator *sreg, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct regulator_desc *rdesc = &sreg->rdesc; + unsigned int *v_table; + int ret = 0; + + /* parse .n_voltages, and .volt_table */ + ret = of_property_read_u32(np, "hisilicon,hi6421-n-voltages", + &rdesc->n_voltages); + if (ret) { + dev_err(dev, "no hisilicon,hi6421-n-voltages property set\n"); + goto dt_parse_ldo_end; + } + + /* alloc space for .volt_table */ + v_table = devm_kzalloc(dev, sizeof(unsigned int) * rdesc->n_voltages, + GFP_KERNEL); + if (unlikely(!v_table)) { + ret = -ENOMEM; + dev_err(dev, "no memory for .volt_table\n"); + goto dt_parse_ldo_end; + } + + ret = of_property_read_u32_array(np, "hisilicon,hi6421-vset-table", + v_table, rdesc->n_voltages); + if (ret) { + dev_err(dev, "no hisilicon,hi6421-vset-table property set\n"); + goto dt_parse_ldo_end; + } + rdesc->volt_table = v_table; + + /* parse hi6421 regulator's dt common part */ + ret = hi6421_dt_parse_common(sreg, pdev); + if (ret) { + dev_err(dev, "failure in hi6421_dt_parse_common\n"); + goto dt_parse_ldo_end; + } + +dt_parse_ldo_end: + return ret; +} + +static int hi6421_dt_parse_buck012(struct hi6421_regulator *sreg, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct regulator_desc *rdesc = &sreg->rdesc; + int ret = 0; + + /* parse .n_voltages, and .uV_step */ + ret = of_property_read_u32(np, "hisilicon,hi6421-n-voltages", + &rdesc->n_voltages); + if (ret) { + dev_err(dev, "no hisilicon,hi6421-n-voltages property set\n"); + goto dt_parse_buck012_end; + } + ret = of_property_read_u32(np, "hisilicon,hi6421-uv-step", + &rdesc->uV_step); + if (ret) { + dev_err(dev, "no hisilicon,hi6421-uv-step property set\n"); + goto dt_parse_buck012_end; + } + + /* parse hi6421 regulator's dt common part */ + ret = hi6421_dt_parse_common(sreg, pdev); + if (ret) { + dev_err(dev, "failure in hi6421_dt_parse_common\n"); + goto dt_parse_buck012_end; + } + +dt_parse_buck012_end: + return ret; +} + +static struct regulator_ops hi6421_ldo_rops = { + .is_enabled = hi6421_regulator_is_enabled, + .enable = hi6421_regulator_enable, + .disable = hi6421_regulator_disable, + .list_voltage = regulator_list_voltage_table, + .get_voltage = hi6421_regulator_get_voltage, + .set_voltage = hi6421_regulator_ldo_set_voltage, + .get_mode = hi6421_regulator_get_mode, + .set_mode = hi6421_regulator_set_mode, + .get_optimum_mode = hi6421_regulator_get_optimum_mode, +}; + +static struct regulator_ops hi6421_buck012_rops = { + .is_enabled = hi6421_regulator_is_enabled, + .enable = hi6421_regulator_enable, + .disable = hi6421_regulator_disable, + .list_voltage = regulator_list_voltage_linear, + .get_voltage = hi6421_regulator_get_voltage, + .set_voltage = hi6421_regulator_buck012_set_voltage, + .get_mode = hi6421_regulator_get_mode, + .set_mode = hi6421_regulator_set_mode, + .get_optimum_mode = hi6421_regulator_get_optimum_mode, +}; + +static struct regulator_ops hi6421_buck345_rops = { + .is_enabled = hi6421_regulator_is_enabled, + .enable = hi6421_regulator_enable, + .disable = hi6421_regulator_disable, + .list_voltage = regulator_list_voltage_table, + .get_voltage = hi6421_regulator_get_voltage, + .set_voltage = hi6421_regulator_ldo_set_voltage, + .get_mode = hi6421_regulator_get_mode, + .set_mode = hi6421_regulator_set_mode, + .get_optimum_mode = hi6421_regulator_get_optimum_mode, +}; + +static const struct hi6421_regulator hi6421_regulator_ldo = { + .rdesc = { + .ops = &hi6421_ldo_rops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .dt_parse = hi6421_dt_parse_ldo, +}; + +static const struct hi6421_regulator hi6421_regulator_buck012 = { + .rdesc = { + .ops = &hi6421_buck012_rops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .dt_parse = hi6421_dt_parse_buck012, +}; + +static const struct hi6421_regulator hi6421_regulator_buck345 = { + .rdesc = { + .ops = &hi6421_buck345_rops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .dt_parse = hi6421_dt_parse_ldo, +}; + +static struct of_device_id of_hi6421_regulator_match_tbl[] = { + { + .compatible = "hisilicon,hi6421-ldo", + .data = &hi6421_regulator_ldo, + }, + { + .compatible = "hisilicon,hi6421-buck012", + .data = &hi6421_regulator_buck012, + }, + { + .compatible = "hisilicon,hi6421-buck345", + .data = &hi6421_regulator_buck345, + }, + { /* end */ } +}; + +static int hi6421_regulator_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct regulator_desc *rdesc; + struct regulator_dev *rdev; + struct hi6421_regulator *sreg = NULL; + struct regulator_init_data *initdata; + struct regulation_constraints *c; + struct regulator_config config = { }; + const struct of_device_id *match; + const struct hi6421_regulator *template = NULL; + int ret = 0; + + /* to check which type of regulator this is */ + match = of_match_device(of_hi6421_regulator_match_tbl, &pdev->dev); + if (match) + template = match->data; + else + return -EINVAL; + + initdata = of_get_regulator_init_data(dev, np); + + /* hi6421 regulator supports two modes */ + c = &initdata->constraints; + c->valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE; + c->valid_ops_mask |= (REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_DRMS); + c->input_uV = c->min_uV; + + sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL); + if (sreg == NULL) + return -ENOMEM; + memcpy(sreg, template, sizeof(*sreg)); + + sreg->name = initdata->constraints.name; + rdesc = &sreg->rdesc; + rdesc->name = sreg->name; + rdesc->min_uV = initdata->constraints.min_uV; + + /* to parse device tree data for regulator specific */ + ret = sreg->dt_parse(sreg, pdev); + if (ret) { + dev_err(dev, "device tree parameter parse error!\n"); + goto hi6421_probe_end; + } + + config.dev = &pdev->dev; + config.init_data = initdata; + config.driver_data = sreg; + config.of_node = pdev->dev.of_node; + + /* register regulator */ + rdev = regulator_register(rdesc, &config); + if (IS_ERR(rdev)) { + dev_err(dev, "failed to register %s\n", + rdesc->name); + ret = PTR_ERR(rdev); + goto hi6421_probe_end; + } + + platform_set_drvdata(pdev, rdev); + +hi6421_probe_end: + return ret; +} + +static int hi6421_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + regulator_unregister(rdev); + + return 0; +} + +static struct platform_driver hi6421_regulator_driver = { + .driver = { + .name = "hi6421_regulator", + .owner = THIS_MODULE, + .of_match_table = of_hi6421_regulator_match_tbl, + }, + .probe = hi6421_regulator_probe, + .remove = hi6421_regulator_remove, +}; +module_platform_driver(hi6421_regulator_driver); + +MODULE_AUTHOR("Guodong Xu <guodong.xu@linaro.org>"); +MODULE_DESCRIPTION("Hi6421 regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index e3b25712b659..7fbb5c1d38fc 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -401,6 +401,32 @@ err_req: return ret; } +#ifdef CONFIG_PM +static int pl031_suspend(struct device *dev) +{ + struct amba_device *adev = to_amba_device(dev); + + if (adev->irq[0] >= 0 && device_may_wakeup(&adev->dev)) + enable_irq_wake(adev->irq[0]); + return 0; +} + +static int pl031_resume(struct device *dev) +{ + struct amba_device *adev = to_amba_device(dev); + + if (adev->irq[0] >= 0 && device_may_wakeup(&adev->dev)) + disable_irq_wake(adev->irq[0]); + return 0; +} + +static SIMPLE_DEV_PM_OPS(pl031_pm, pl031_suspend, pl031_resume); + +#define PL031_PM (&pl031_pm) +#else +#define PL031_PM NULL +#endif + /* Operations for the original ARM version */ static struct pl031_vendor_data arm_pl031 = { .ops = { @@ -470,6 +496,7 @@ MODULE_DEVICE_TABLE(amba, pl031_ids); static struct amba_driver pl031_driver = { .drv = { .name = "rtc-pl031", + .pm = PL031_PM, }, .id_table = pl031_ids, .probe = pl031_probe, diff --git a/include/linux/mfd/hi6421-pmic.h b/include/linux/mfd/hi6421-pmic.h new file mode 100644 index 000000000000..0b60a9b2a666 --- /dev/null +++ b/include/linux/mfd/hi6421-pmic.h @@ -0,0 +1,99 @@ +/* + * Header file for device driver Hi6421 PMIC + * + * Copyright (c) 2013 Linaro Ltd. + * Copyright (C) 2011 Hisilicon. + * + * Guodong Xu <guodong.xu@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. + * + * 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 __HI6421_PMIC_H +#define __HI6421_PMIC_H + +#include <linux/irqdomain.h> +#include <linux/mutex.h> +#include <linux/time.h> + +#define OCP_DEB_CTRL_REG (0x51) +#define OCP_DEB_SEL_MASK (0x0C) +#define OCP_DEB_SEL_8MS (0x00) +#define OCP_DEB_SEL_16MS (0x04) +#define OCP_DEB_SEL_32MS (0x08) +#define OCP_DEB_SEL_64MS (0x0C) +#define OCP_EN_DEBOUNCE_MASK (0x02) +#define OCP_EN_DEBOUNCE_ENABLE (0x02) +#define OCP_AUTO_STOP_MASK (0x01) +#define OCP_AUTO_STOP_ENABLE (0x01) +#define HI6421_REGS_ENA_PROTECT_TIME (100) /* in microseconds */ +#define HI6421_ECO_MODE_ENABLE (1) +#define HI6421_ECO_MODE_DISABLE (0) + +#define HI6421_NR_IRQ 24 +#define HI6421_MASK_FIELD 0xFF +#define HI6421_BITS 8 + +#define HI6421_IRQ_ALARM 0 /* RTC Alarm */ +#define HI6421_IRQ_OTMP 1 /* Temperature too high */ +#define HI6421_IRQ_OCP 2 /* BUCK/LDO overload */ +#define HI6421_IRQ_RESET_IN 3 /* RESETIN_N signal */ +#define HI6421_IRQ_ONKEY_10S 4 /* PWRON key hold for 10s */ +#define HI6421_IRQ_ONKEY_1S 5 /* PWRON key hold for 1s */ +#define HI6421_IRQ_ONKEY_UP 6 /* PWRON key released */ +#define HI6421_IRQ_ONKEY_DOWN 7 /* PWRON key pressed */ +#define HI6421_IRQ_HEADSET_OUT 8 /* Headset plugged out */ +#define HI6421_IRQ_HEADSET_IN 9 /* Headset plugged in */ +#define HI6421_IRQ_COULOMB 10 /* Coulomb Counter */ +#define HI6421_IRQ_VBUS_UP 11 /* VBUS rising */ +#define HI6421_IRQ_VBUS_DOWN 12 /* VBUS falling */ +#define HI6421_IRQ_VBAT_LOW 13 /* VBattery too low */ +#define HI6421_IRQ_VBAT_HIGH 14 /* VBattery overvoltage */ +#define HI6421_IRQ_CHARGE_IN1 15 /* Charger plugged in */ +#define HI6421_IRQ_CHARGE_IN3 16 /* Charger plugged in */ +#define HI6421_IRQ_CHARGE_IN2 17 /* Charger plugged in */ +#define HI6421_IRQ_HS_BTN_DOWN 18 /* Headset button pressed */ +#define HI6421_IRQ_HS_BTN_UP 19 /* Headset button released */ +#define HI6421_IRQ_BATTERY_ON 20 /* Battery inserted */ +#define HI6421_IRQ_RESET 21 /* Reset */ + +/** + * struct hi6421_pmic - Hi6421 PMIC chip-level data structure. + * + * This struct describes Hi6421 PMIC chip-level data. + * + * @enable_mutex: Used by regulators, to make sure that only one regulator is + * in the turning-on phase at any time. See also @last_enabled + * and hi6421_regulator_enable(). + * @last_enabled: Used by regulators, to make sure enough distance in time + * between enabling of any of two regulators. + */ +struct hi6421_pmic { + struct resource *res; + struct device *dev; + void __iomem *regs; + spinlock_t lock; + struct irq_domain *domain; + int irq; + int gpio; + struct mutex enable_mutex; + struct timeval last_enabled; +}; + +/* Register Access Helpers */ +u32 hi6421_pmic_read(struct hi6421_pmic *pmic, int reg); +void hi6421_pmic_write(struct hi6421_pmic *pmic, int reg, u32 val); +void hi6421_pmic_rmw(struct hi6421_pmic *pmic, int reg, u32 mask, u32 bits); + +#endif /* __HI6421_PMIC_H */ diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 198f0fa44e9f..5a359dc44705 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -246,7 +246,7 @@ struct dw_mci_board { int (*init)(u32 slot_id, irq_handler_t , void *); int (*get_ro)(u32 slot_id); - int (*get_cd)(u32 slot_id); + int (*get_cd)(struct dw_mci *host, u32 slot_id); int (*get_ocr)(u32 slot_id); int (*get_bus_wd)(u32 slot_id); /* |