diff options
113 files changed, 17406 insertions, 593 deletions
diff --git a/Documentation/clk.txt b/Documentation/clk.txt index b9911c27f496..3110ba472e8a 100644 --- a/Documentation/clk.txt +++ b/Documentation/clk.txt @@ -70,6 +70,10 @@ the operations defined in clk.h: unsigned long parent_rate); long (*round_rate)(struct clk_hw *hw, unsigned long, unsigned long *); + long (*determine_rate)(struct clk_hw *hw, + unsigned long rate, + unsigned long *best_parent_rate, + struct clk **best_parent_clk); int (*set_parent)(struct clk_hw *hw, u8 index); u8 (*get_parent)(struct clk_hw *hw); int (*set_rate)(struct clk_hw *hw, unsigned long); 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/dma/k3dma.txt b/Documentation/devicetree/bindings/dma/k3dma.txt new file mode 100644 index 000000000000..23f8d712c3ce --- /dev/null +++ b/Documentation/devicetree/bindings/dma/k3dma.txt @@ -0,0 +1,46 @@ +* Hisilicon K3 DMA controller + +See dma.txt first + +Required properties: +- compatible: Should be "hisilicon,k3-dma-1.0" +- reg: Should contain DMA registers location and length. +- interrupts: Should contain one interrupt shared by all channel +- #dma-cells: see dma.txt, should be 1, para number +- dma-channels: physical channels supported +- dma-requests: virtual channels supported, each virtual channel + have specific request line +- clocks: clock required + +Example: + +Controller: + 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 = <&pclk>; + status = "disable"; + }; + +Client: +Use specific request line passing from dmax +For example, i2c0 read channel request line is 18, while write channel use 19 + + i2c0: i2c@fcb08000 { + compatible = "snps,designware-i2c"; + dmas = <&dma0 18 /* read channel */ + &dma0 19>; /* write channel */ + dma-names = "rx", "tx"; + }; + + i2c1: i2c@fcb09000 { + compatible = "snps,designware-i2c"; + dmas = <&dma0 20 /* read channel */ + &dma0 21>; /* write channel */ + dma-names = "rx", "tx"; + }; + diff --git a/Documentation/devicetree/bindings/gpio/gpio-pl061.txt b/Documentation/devicetree/bindings/gpio/gpio-pl061.txt new file mode 100644 index 000000000000..687206727bd3 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-pl061.txt @@ -0,0 +1,33 @@ +PL061 GPIO controller bindings + +Required properties: +- compatible: + - "arm,pl061", "arm,primecell". +- #gpio-cells : Should be two. + - first cell is the gpio pin number + - second cell is used to specify the gpio polarity: + 0 = active high + 1 = active low +- gpio-controller : Marks the device node as a GPIO controller. +- interrupt-controller : Marks the device node as an interrupt controller. +- #interrupt-cells : Should be two. + - first cell is the hw irq number + - second cell is used to specify the interrupt type: + 0 = default, unspecified type + 1 = rising edge triggered + 2 = falling edge triggered + 4 = high level triggered + 8 = low level triggered +- linux,gpio-base : Should be the global GPIO number. + +Example: + gpio0: gpio@fc806000 { + compatible = "arm,pl061", "arm,primecell"; + reg = <0xfc806000 0x1000>; + interrupts = <0 64 0x4>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + linux,gpio-base = <0>; + }; diff --git a/Documentation/devicetree/bindings/mmc/hisilicon-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/hisilicon-dw-mshc.txt new file mode 100644 index 000000000000..960eb32e5988 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/hisilicon-dw-mshc.txt @@ -0,0 +1,50 @@ +* 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. + - "hisilicon,hi3620-dw-mshc": for controllers with hi3620 + specific extentions. + +Required properties for a slot: + +* vmmc-supply is vmmc used in dwmmc +* fifo-depth should be provided if register can not provide correct value + +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"; + }; + 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>; + slot@0 { + reg = <0>; + bus-width = <4>; + hisilicon,cd-pinmux-gpio = <&gpio10 3 0>; + }; + }; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 3d2e60807861..ec94c57ef942 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -938,6 +938,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/Kconfig.debug b/arch/arm/Kconfig.debug index 21cc8a765988..1d61af02b604 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -272,6 +272,20 @@ choice Say Y here if you want kernel low-level debugging support on i.MX6Q/DL. + config DEBUG_HI3620_UART0 + bool "Hisilicon HI3620 Debug UART0" + depends on ARCH_HS + help + Say Y here if you want kernel low-level debugging support + on HI3620 UART0. + + config DEBUG_HI3716_UART0 + bool "Hisilicon HI3716 Debug UART0" + depends on ARCH_HS + help + Say Y here if you want kernel low-level debugging support + on HI3716 UART0. + config DEBUG_MMP_UART2 bool "Kernel low-level debugging message via MMP UART2" depends on ARCH_MMP @@ -644,6 +658,7 @@ config DEBUG_LL_INCLUDE default "debug/cns3xxx.S" if DEBUG_CNS3XXX default "debug/exynos.S" if DEBUG_EXYNOS_UART default "debug/highbank.S" if DEBUG_HIGHBANK_UART + default "debug/hisilicon.S" if DEBUG_HI3620_UART0 || DEBUG_HI3716_UART0 default "debug/icedcc.S" if DEBUG_ICEDCC default "debug/imx.S" if DEBUG_IMX1_UART || \ DEBUG_IMX25_UART || \ diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 883e4bec807f..ee69c43c61af 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -148,6 +148,7 @@ machine-$(CONFIG_ARCH_EBSA110) += ebsa110 machine-$(CONFIG_ARCH_EP93XX) += ep93xx machine-$(CONFIG_ARCH_GEMINI) += gemini machine-$(CONFIG_ARCH_HIGHBANK) += highbank +machine-$(CONFIG_ARCH_HS) += 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 00baf9f5766a..937ba39020cf 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -60,6 +60,8 @@ dtb-$(CONFIG_ARCH_EXYNOS) += exynos4210-origen.dtb \ exynos5440-ssdk5440.dtb dtb-$(CONFIG_ARCH_HIGHBANK) += highbank.dtb \ ecx-2000.dtb +dtb-$(CONFIG_ARCH_HS) += 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..7ba98c698ca0 --- /dev/null +++ b/arch/arm/boot/dts/hi3620.dtsi @@ -0,0 +1,1606 @@ +/* + * 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + + 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 1>; + }; + + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + 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 1>; + }; + refclk_ldi0: refclk@32 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + hiword; + clocks = <&pll_peri &pll_hdmi &pll_usb>; + clock-output-names = "rclk_ldi0"; + hisilicon,clkmux-reg = <0x114 0x6000>; + hisilicon,clkmux-table = <0 1 2>; + }; + refclk_ldi1: refclk@33 { + compatible = "hisilicon,hi3620-clk-mux"; + #clock-cells = <0>; + hiword; + clocks = <&pll_peri &pll_hdmi &pll_usb>; + clock-output-names = "rclk_ldi1"; + hisilicon,clkmux-reg = <0x118 0xc000>; + hisilicon,clkmux-table = <0 1 2>; + }; + 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 1>; + }; + 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 1>; + }; + 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"; + linux,gpio-base = <0>; + 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"; + linux,gpio-base = <8>; + 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"; + linux,gpio-base = <16>; + 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"; + linux,gpio-base = <24>; + 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"; + linux,gpio-base = <32>; + 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"; + linux,gpio-base = <40>; + 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"; + linux,gpio-base = <48>; + 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"; + linux,gpio-base = <56>; + 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"; + linux,gpio-base = <64>; + 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"; + linux,gpio-base = <72>; + 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"; + linux,gpio-base = <80>; + 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"; + linux,gpio-base = <88>; + 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"; + linux,gpio-base = <96>; + 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"; + linux,gpio-base = <104>; + 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"; + linux,gpio-base = <112>; + 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"; + linux,gpio-base = <120>; + 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"; + linux,gpio-base = <128>; + 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"; + linux,gpio-base = <136>; + 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"; + linux,gpio-base = <144>; + 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"; + linux,gpio-base = <152>; + 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"; + linux,gpio-base = <160>; + 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"; + linux,gpio-base = <168>; + 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"; + }; + + /* 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"; + }; + + dwmmc_2: dwmmc2@fcd05000 { + compatible = "hisilicon,hi4511-dw-mshc"; + reg = <0xfcd05000 0x1000>; + interrupts = <0 18 4>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&mmcclk2>; + }; + + dwmmc_3: dwmmc3@fcd06000 { + compatible = "hisilicon,hi4511-dw-mshc"; + reg = <0xfcd06000 0x1000>; + interrupts = <0 19 4>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&mmcclk3>; + }; + + 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..86fae4a783aa --- /dev/null +++ b/arch/arm/boot/dts/hi3716-dkb.dts @@ -0,0 +1,60 @@ +/* + * 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"; + }; + + dmac: dmac@f9870000 { + status = "okay"; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/hi3716.dtsi b/arch/arm/boot/dts/hi3716.dtsi new file mode 100644 index 000000000000..591d4316749c --- /dev/null +++ b/arch/arm/boot/dts/hi3716.dtsi @@ -0,0 +1,280 @@ +/* + * 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"; + }; + + dmac: dmac@f9870000 { + compatible = "arm,pl080", "arm,primecell"; + arm,primecell-periphid = <0x00041080>; + reg = <0xf9870000 0x1000>; + #dma-cells = <1>; + dma-channels = <8>; + dma-requests = <16>; + clocks = <&clk_dmac>; + clock-names = "apb_pclk"; + interrupts = <0 30 4>; + 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"; + }; + + clk_dmac: clk_dmac { + compatible = "hisilicon,hi3716-clk-gate"; + #clock-cells = <0>; + clocks = <&bpll 3>; + + /* offset, enable, reset */ + gate-reg = <0xa4 0 4>; + + clock-output-names = "dmac"; + }; + }; + + 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..2e85f395cd43 --- /dev/null +++ b/arch/arm/boot/dts/hi4511.dts @@ -0,0 +1,1478 @@ +/* + * 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 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 { + device_type = "memory"; + reg = <0x40000000 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/hs_defconfig b/arch/arm/configs/hs_defconfig new file mode 100644 index 000000000000..bb1e921fb9aa --- /dev/null +++ b/arch/arm/configs/hs_defconfig @@ -0,0 +1,1998 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.8.0-rc3 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_NO_IOPORT=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_ARM_PATCH_PHYS_VIRT=y +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +CONFIG_SWAP=y +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_FHANDLE is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_DEBUG=y +CONFIG_SPARSE_IRQ=y +CONFIG_KTIME_SCALAR=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y + +# +# Timers subsystem +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y + +# +# CPU/Task time and stats accounting +# +CONFIG_TICK_CPU_ACCOUNTING=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_PREEMPT_RCU is not set +CONFIG_RCU_FANOUT=32 +CONFIG_RCU_FANOUT_LEAF=16 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_RCU_FAST_NO_HZ is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_RCU_NOCB_CPU is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=17 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +CONFIG_NAMESPACES=y +CONFIG_UTS_NS=y +# CONFIG_USER_NS is not set +CONFIG_PID_NS=y +CONFIG_NET_NS=y +CONFIG_UIDGID_CONVERTED=y +# CONFIG_UIDGID_STRICT_TYPE_CHECKS is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="/opt/workspace/kernel/rootfs/" +CONFIG_INITRAMFS_ROOT_UID=0 +CONFIG_INITRAMFS_ROOT_GID=0 +CONFIG_RD_GZIP=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_RD_XZ=y +CONFIG_RD_LZO=y +# CONFIG_INITRAMFS_COMPRESSION_NONE is not set +# CONFIG_INITRAMFS_COMPRESSION_GZIP is not set +# CONFIG_INITRAMFS_COMPRESSION_BZIP2 is not set +CONFIG_INITRAMFS_COMPRESSION_LZMA=y +# CONFIG_INITRAMFS_COMPRESSION_XZ is not set +# CONFIG_INITRAMFS_COMPRESSION_LZO is not set +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=1 +# CONFIG_EXPERT is not set +CONFIG_HAVE_UID16=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y +# CONFIG_EMBEDDED is not set +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_JUMP_LABEL is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_USE_GENERIC_SMP_HELPERS=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_CLONE_BACKWARDS=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_STOP_MACHINE=y +CONFIG_BLOCK=y +CONFIG_LBDAF=y +CONFIG_BLK_DEV_BSG=y +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_EFI_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +CONFIG_INLINE_READ_UNLOCK=y +CONFIG_INLINE_READ_UNLOCK_IRQ=y +CONFIG_INLINE_WRITE_UNLOCK=y +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +CONFIG_ARCH_MULTIPLATFORM=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCM2835 is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_SIRF is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C24XX is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_EXYNOS is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_ARCH_VT8500_SINGLE is not set + +# +# Multiple platform selection +# + +# +# CPU Core family selection +# +# CONFIG_ARCH_MULTI_V6 is not set +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_MULTI_V6_V7=y +# CONFIG_ARCH_MULTI_CPU_AUTO is not set +# CONFIG_ARCH_MVEBU is not set +# CONFIG_ARCH_BCM is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +CONFIG_ARCH_HIGHBANK=y +CONFIG_ARCH_HS=y +CONFIG_MACH_HS_DT=y +# CONFIG_ARCH_MXC is not set +CONFIG_ARCH_SOCFPGA=y +# CONFIG_ARCH_SUNXI is not set +CONFIG_ARCH_VEXPRESS=y + +# +# Versatile Express platform type +# +# CONFIG_ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA is not set +# CONFIG_ARCH_VEXPRESS_CA9X4 is not set +CONFIG_PLAT_VERSATILE_CLCD=y +CONFIG_PLAT_VERSATILE_SCHED_CLOCK=y +# CONFIG_ARCH_VT8500 is not set +# CONFIG_ARCH_ZYNQ is not set +CONFIG_PLAT_VERSATILE=y +CONFIG_ARM_TIMER_SP804=y + +# +# Processor Type +# +CONFIG_CPU_V7=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +# CONFIG_ARM_LPAE is not set +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +CONFIG_ARM_THUMB=y +# CONFIG_ARM_THUMBEE is not set +# CONFIG_ARM_VIRT_EXT is not set +CONFIG_SWP_EMULATE=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_OUTER_CACHE=y +CONFIG_OUTER_CACHE_SYNC=y +CONFIG_MIGHT_HAVE_CACHE_L2X0=y +CONFIG_CACHE_L2X0=y +CONFIG_CACHE_PL310=y +CONFIG_ARM_L1_CACHE_SHIFT_6=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_ARM_NR_BANKS=8 +CONFIG_MULTI_IRQ_HANDLER=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_PL310_ERRATA_588369 is not set +# CONFIG_ARM_ERRATA_720789 is not set +# CONFIG_PL310_ERRATA_727915 is not set +# CONFIG_PL310_ERRATA_753970 is not set +CONFIG_ARM_ERRATA_754322=y +# CONFIG_ARM_ERRATA_754327 is not set +# CONFIG_ARM_ERRATA_764369 is not set +# CONFIG_PL310_ERRATA_769419 is not set +# CONFIG_ARM_ERRATA_775420 is not set +CONFIG_ARM_GIC=y +CONFIG_ICST=y + +# +# Bus support +# +CONFIG_ARM_AMBA=y +# CONFIG_PCI_SYSCALL is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_HAVE_SMP=y +CONFIG_SMP=y +CONFIG_SMP_ON_UP=y +CONFIG_ARM_CPU_TOPOLOGY=y +# CONFIG_SCHED_MC is not set +# CONFIG_SCHED_SMT is not set +CONFIG_HAVE_ARM_SCU=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_HAVE_ARM_TWD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_NR_CPUS=4 +CONFIG_HOTPLUG_CPU=y +CONFIG_LOCAL_TIMERS=y +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +CONFIG_HIGHMEM=y +CONFIG_HIGHPTE=y +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +CONFIG_COMPACTION=y +CONFIG_MIGRATION=y +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_CROSS_MEMORY_ATTACH=y +# CONFIG_CLEANCACHE is not set +# CONFIG_FRONTSWAP is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_XEN is not set + +# +# Boot options +# +CONFIG_USE_OF=y +CONFIG_ATAGS=y +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_ARM_APPENDED_DTB=y +# CONFIG_ARM_ATAG_DTB_COMPAT is not set +CONFIG_CMDLINE="" +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +CONFIG_AUTO_ZRELADDR=y + +# +# CPU Power Management +# +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_FPE_NWFPE is not set +# CONFIG_FPE_FASTFPE is not set +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set +CONFIG_COREDUMP=y + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_RUNTIME=y +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +# CONFIG_PACKET is not set +CONFIG_UNIX=y +# CONFIG_UNIX_DIAG is not set +# CONFIG_NET_KEY is not set +# CONFIG_INET is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +# CONFIG_NETFILTER is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +CONFIG_HAVE_NET_DSA=y +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_RPS=y +CONFIG_RFS_ACCEL=y +CONFIG_XPS=y +CONFIG_BQL=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_WIRELESS=y +# CONFIG_CFG80211 is not set +# CONFIG_LIB80211 is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# +# CONFIG_WIMAX is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_NFC is not set +CONFIG_HAVE_BPF_JIT=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +# CONFIG_DMA_SHARED_BUFFER is not set +# CONFIG_CMA is not set + +# +# Bus devices +# +# CONFIG_CONNECTOR is not set +# CONFIG_MTD is not set +CONFIG_DTC=y +CONFIG_OF=y + +# +# Device Tree and Open Firmware support +# +# CONFIG_PROC_DEVICETREE is not set +# CONFIG_OF_SELFTEST is not set +CONFIG_OF_FLATTREE=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_IRQ=y +CONFIG_OF_DEVICE=y +CONFIG_OF_I2C=y +CONFIG_OF_NET=y +CONFIG_OF_MDIO=y +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set + +# +# DRBD disabled because PROC_FS or INET not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_DS1682 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_ARM_CHARLCD is not set +# CONFIG_BMP085_I2C is not set +# CONFIG_BMP085_SPI is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_ISCSI_BOOT_SYSFS is not set +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +CONFIG_HAVE_PATA_PLATFORM=y +CONFIG_ATA=y +# CONFIG_ATA_NONSTANDARD is not set +CONFIG_ATA_VERBOSE_ERROR=y +CONFIG_SATA_PMP=y + +# +# Controllers with non-SFF native interface +# +# CONFIG_SATA_AHCI_PLATFORM is not set +CONFIG_ATA_SFF=y + +# +# SFF controllers with custom DMA interface +# +CONFIG_ATA_BMDMA=y + +# +# SATA SFF controllers with BMDMA +# +CONFIG_SATA_HIGHBANK=y +CONFIG_SATA_MV=y + +# +# PATA SFF controllers with BMDMA +# +# CONFIG_PATA_ARASAN_CF is not set + +# +# PIO-only SFF controllers +# +# CONFIG_PATA_PLATFORM is not set + +# +# Generic fallback / legacy drivers +# +# CONFIG_MD is not set +# CONFIG_TARGET_CORE is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_MII=y +# CONFIG_NET_TEAM is not set +# CONFIG_MACVLAN is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set + +# +# CAIF transport drivers +# + +# +# Distributed Switch Architecture drivers +# +# CONFIG_NET_DSA_MV88E6XXX is not set +# CONFIG_NET_DSA_MV88E6060 is not set +# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set +# CONFIG_NET_DSA_MV88E6131 is not set +# CONFIG_NET_DSA_MV88E6123_61_65 is not set +CONFIG_ETHERNET=y +CONFIG_NET_CADENCE=y +# CONFIG_ARM_AT91_ETHER is not set +# CONFIG_MACB is not set +CONFIG_NET_VENDOR_BROADCOM=y +# CONFIG_B44 is not set +CONFIG_NET_CALXEDA_XGMAC=y +CONFIG_NET_VENDOR_CIRRUS=y +# CONFIG_CS89x0 is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +CONFIG_NET_VENDOR_FARADAY=y +# CONFIG_FTMAC100 is not set +# CONFIG_FTGMAC100 is not set +CONFIG_NET_VENDOR_INTEL=y +CONFIG_NET_VENDOR_I825XX=y +CONFIG_NET_VENDOR_MICREL=y +# CONFIG_KS8842 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set +CONFIG_NET_VENDOR_MICROCHIP=y +# CONFIG_ENC28J60 is not set +CONFIG_NET_VENDOR_NATSEMI=y +CONFIG_NET_VENDOR_8390=y +# CONFIG_AX88796 is not set +# CONFIG_ETHOC is not set +CONFIG_NET_VENDOR_SEEQ=y +# CONFIG_SEEQ8005 is not set +CONFIG_NET_VENDOR_SMSC=y +# CONFIG_SMC91X is not set +# CONFIG_SMC911X is not set +CONFIG_SMSC911X=y +# CONFIG_SMSC911X_ARCH_HOOKS is not set +CONFIG_NET_VENDOR_STMICRO=y +CONFIG_STMMAC_ETH=y +CONFIG_STMMAC_PLATFORM=y +# CONFIG_STMMAC_DEBUG_FS is not set +# CONFIG_STMMAC_DA is not set +CONFIG_STMMAC_RING=y +# CONFIG_STMMAC_CHAINED is not set +CONFIG_NET_VENDOR_WIZNET=y +# CONFIG_WIZNET_W5100 is not set +# CONFIG_WIZNET_W5300 is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_AT803X_PHY is not set +# CONFIG_AMD_PHY is not set +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_BCM87XX_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_STE10XP is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_MICREL_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +# CONFIG_MDIO_BUS_MUX_GPIO is not set +# CONFIG_MDIO_BUS_MUX_MMIOREG is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +# CONFIG_USB_ZD1201 is not set +# CONFIG_HOSTAP is not set +# CONFIG_WL_TI is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set +# CONFIG_INPUT_MATRIXKMAP is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_QT2160 is not set +# CONFIG_KEYBOARD_LKKBD is not set +CONFIG_KEYBOARD_GPIO=y +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8333 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +CONFIG_MOUSE_PS2_ALPS=y +CONFIG_MOUSE_PS2_LOGIPS2PP=y +CONFIG_MOUSE_PS2_SYNAPTICS=y +CONFIG_MOUSE_PS2_TRACKPOINT=y +# CONFIG_MOUSE_PS2_ELANTECH is not set +# CONFIG_MOUSE_PS2_SENTELIC is not set +# CONFIG_MOUSE_PS2_TOUCHKIT is not set +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_APPLETOUCH is not set +# CONFIG_MOUSE_BCM5974 is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_MOUSE_GPIO is not set +# CONFIG_MOUSE_SYNAPTICS_I2C is not set +# CONFIG_MOUSE_SYNAPTICS_USB is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_MXT224E=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_HI6421_ONKEY=y + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +CONFIG_SERIO_AMBAKMI=y +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_SERIO_PS2MULT is not set +# CONFIG_SERIO_ARC_PS2 is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_VT_CONSOLE_SLEEP=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVKMEM=y + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_AMBA_PL010 is not set +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX310X is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_SCCNXP is not set +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_IFX6X60 is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +# CONFIG_SERIAL_ARC is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +# CONFIG_HW_RANDOM_ATMEL is not set +# CONFIG_HW_RANDOM_EXYNOS is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_CBUS_GPIO is not set +CONFIG_I2C_DESIGNWARE_CORE=y +CONFIG_I2C_DESIGNWARE_PLATFORM=y +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_NOMADIK is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_VERSATILE is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +CONFIG_SPI_PL022=y +# CONFIG_SPI_PXA2XX_PCI is not set +# CONFIG_SPI_SC18IS602 is not set +# CONFIG_SPI_XCOMM is not set +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_HSI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# + +# +# PTP clock support +# +# CONFIG_PTP_1588_CLOCK is not set + +# +# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. +# +# CONFIG_PTP_1588_CLOCK_PCH is not set +CONFIG_PINCTRL=y + +# +# Pin controllers +# +CONFIG_PINMUX=y +CONFIG_PINCONF=y +CONFIG_GENERIC_PINCONF=y +# CONFIG_DEBUG_PINCTRL is not set +CONFIG_PINCTRL_SINGLE=y +# CONFIG_PINCTRL_EXYNOS4 is not set +# CONFIG_PINCTRL_EXYNOS5440 is not set +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +CONFIG_OF_GPIO=y +# CONFIG_DEBUG_GPIO is not set +CONFIG_GPIO_SYSFS=y + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set +# CONFIG_GPIO_EM is not set +CONFIG_GPIO_PL061=y +# CONFIG_GPIO_TS5500 is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set +# CONFIG_GPIO_ADNP is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# + +# +# USB GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_POWER_AVS is not set +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# +# CONFIG_SENSORS_AD7314 is not set +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADCXX is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7410 is not set +# CONFIG_SENSORS_ADT7411 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ASC7621 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS620 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_GPIO_FAN is not set +# CONFIG_SENSORS_HIH6130 is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_JC42 is not set +# CONFIG_SENSORS_LINEAGE is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM73 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LTC4151 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LTC4261 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_LM95245 is not set +# CONFIG_SENSORS_MAX1111 is not set +# CONFIG_SENSORS_MAX16065 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX1668 is not set +# CONFIG_SENSORS_MAX197 is not set +# CONFIG_SENSORS_MAX6639 is not set +# CONFIG_SENSORS_MAX6642 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_MCP3021 is not set +# CONFIG_SENSORS_NTC_THERMISTOR is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_PMBUS is not set +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_SHT21 is not set +# CONFIG_SENSORS_SMM665 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_EMC1403 is not set +# CONFIG_SENSORS_EMC2103 is not set +# CONFIG_SENSORS_EMC6W201 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_SCH56XX_COMMON is not set +# CONFIG_SENSORS_ADS1015 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_ADS7871 is not set +# CONFIG_SENSORS_AMC6821 is not set +# CONFIG_SENSORS_INA2XX is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP102 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_VEXPRESS is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83795 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_88PM800 is not set +# CONFIG_MFD_88PM805 is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_MFD_TI_AM335X_TSCADC is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_MFD_LM3533 is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_MFD_TPS80031 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_SMSC is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_MFD_DA9055 is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_LP8788 is not set +# CONFIG_MFD_MAX77686 is not set +# CONFIG_MFD_MAX77693 is not set +# CONFIG_MFD_MAX8907 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_SEC_CORE is not set +# CONFIG_MFD_ARIZONA_I2C is not set +# CONFIG_MFD_ARIZONA_SPI is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +CONFIG_MFD_R63306=y +# CONFIG_MFD_MC13XXX_SPI is not set +# CONFIG_MFD_MC13XXX_I2C is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_MFD_SYSCON is not set +# CONFIG_MFD_PALMAS is not set +# CONFIG_MFD_VIPERBOARD is not set +# CONFIG_MFD_RETU is not set +# CONFIG_MFD_AS3711 is not set +CONFIG_MFD_HI6421_PMIC=y +CONFIG_VEXPRESS_CONFIG=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_HI6421=y +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +CONFIG_FB_ARMCLCD=y +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_SMSCUFX is not set +# CONFIG_FB_UDL is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_FB_AUO_K190X is not set +# CONFIG_EXYNOS_VIDEO is not set +CONFIG_FB_HI3620=y +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Console display driver support +# +CONFIG_DUMMY_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +# CONFIG_LOGO is not set +# CONFIG_FB_SSD1307 is not set +# CONFIG_SOUND is not set + +# +# HID support +# +CONFIG_HID=y +# CONFIG_HIDRAW is not set +# CONFIG_UHID is not set +CONFIG_HID_GENERIC=y + +# +# Special HID drivers +# +CONFIG_HID_A4TECH=y +# CONFIG_HID_ACRUX is not set +CONFIG_HID_APPLE=y +# CONFIG_HID_AUREAL is not set +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_CYPRESS=y +# CONFIG_HID_DRAGONRISE is not set +# CONFIG_HID_EMS_FF is not set +CONFIG_HID_EZKEY=y +# CONFIG_HID_HOLTEK is not set +# CONFIG_HID_KEYTOUCH is not set +# CONFIG_HID_KYE is not set +# CONFIG_HID_UCLOGIC is not set +# CONFIG_HID_WALTOP is not set +# CONFIG_HID_GYRATION is not set +# CONFIG_HID_TWINHAN is not set +CONFIG_HID_KENSINGTON=y +# CONFIG_HID_LCPOWER is not set +# CONFIG_HID_LENOVO_TPKBD is not set +CONFIG_HID_LOGITECH=y +# CONFIG_HID_LOGITECH_DJ is not set +# CONFIG_LOGITECH_FF is not set +# CONFIG_LOGIRUMBLEPAD2_FF is not set +# CONFIG_LOGIG940_FF is not set +# CONFIG_LOGIWHEELS_FF is not set +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +# CONFIG_HID_MULTITOUCH is not set +# CONFIG_HID_NTRIG is not set +# CONFIG_HID_ORTEK is not set +# CONFIG_HID_PANTHERLORD is not set +# CONFIG_HID_PETALYNX is not set +# CONFIG_HID_PICOLCD is not set +# CONFIG_HID_PRIMAX is not set +# CONFIG_HID_ROCCAT is not set +# CONFIG_HID_SAITEK is not set +# CONFIG_HID_SAMSUNG is not set +# CONFIG_HID_SONY is not set +# CONFIG_HID_SPEEDLINK is not set +# CONFIG_HID_SUNPLUS is not set +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_TIVO is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_THRUSTMASTER is not set +# CONFIG_HID_ZEROPLUS is not set +# CONFIG_HID_ZYDACRON is not set +# CONFIG_HID_SENSOR_HUB is not set + +# +# USB HID support +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +# CONFIG_USB_HIDDEV is not set + +# +# I2C HID support +# +# CONFIG_I2C_HID is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set + +# +# Miscellaneous USB options +# +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +CONFIG_USB_ISP1760_HCD=y +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_CHIPIDEA is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_REALTEK is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_STORAGE_ENE_UB6250 is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +# CONFIG_USB_EZUSB_FX2 is not set + +# +# USB Physical Layer drivers +# +# CONFIG_USB_ISP1301 is not set +# CONFIG_USB_RCAR_PHY is not set +# CONFIG_USB_GADGET is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set +# CONFIG_MMC_CLKGATE is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +CONFIG_MMC_ARMMMCI=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +CONFIG_MMC_DW=y +CONFIG_MMC_DW_IDMAC=y +CONFIG_MMC_DW_PLTFM=y +CONFIG_MMC_DW_HISILICON=y +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +# CONFIG_MEMSTICK is not set +# CONFIG_NEW_LEDS is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_EDAC=y +CONFIG_EDAC_LEGACY_SYSFS=y +# CONFIG_EDAC_DEBUG is not set +CONFIG_EDAC_MM_EDAC=y +CONFIG_EDAC_HIGHBANK_MC=y +CONFIG_EDAC_HIGHBANK_L2=y +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +CONFIG_RTC_DRV_HI6421=y +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8523 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set +# CONFIG_RTC_DRV_DS2404 is not set + +# +# on-CPU RTC drivers +# +# CONFIG_RTC_DRV_PL030 is not set +CONFIG_RTC_DRV_PL031=y +# CONFIG_RTC_DRV_SNVS is not set +CONFIG_DMADEVICES=y +# CONFIG_DMADEVICES_DEBUG is not set + +# +# DMA Devices +# +CONFIG_AMBA_PL08X=y +# CONFIG_DW_DMAC is not set +# CONFIG_TIMB_DMA is not set +CONFIG_PL330_DMA=y +CONFIG_K3_DMA=y +CONFIG_DMA_ENGINE=y + +# +# DMA Clients +# +# CONFIG_NET_DMA is not set +# CONFIG_ASYNC_TX_DMA is not set +# CONFIG_DMATEST is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set + +# +# Virtio drivers +# +# CONFIG_VIRTIO_MMIO is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_HAVE_CLK_PREPARE=y +CONFIG_COMMON_CLK=y + +# +# Common Clock Framework +# +# CONFIG_COMMON_CLK_DEBUG is not set +CONFIG_COMMON_CLK_VERSATILE=y + +# +# Hardware Spinlock drivers +# +CONFIG_CLKSRC_MMIO=y +CONFIG_DW_APB_TIMER=y +CONFIG_DW_APB_TIMER_OF=y +CONFIG_IOMMU_SUPPORT=y +CONFIG_OF_IOMMU=y + +# +# Remoteproc drivers (EXPERIMENTAL) +# +# CONFIG_STE_MODEM_RPROC is not set + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_EXTCON is not set +# CONFIG_MEMORY is not set +# CONFIG_IIO is not set +# CONFIG_PWM is not set +# CONFIG_IPACK_BUS is not set + +# +# File systems +# +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_EXT2_FS=y +CONFIG_EXT3_FS=y +CONFIG_EXT3_DEFAULTS_TO_ORDERED=y +CONFIG_EXT3_FS_XATTR=y +CONFIG_EXT4_FS=y +CONFIG_JBD=y +CONFIG_JBD2=y +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +# CONFIG_VFAT_FS is not set +CONFIG_FAT_DEFAULT_CODEPAGE=437 +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_TMPFS is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +# CONFIG_F2FS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_MAC_ROMAN is not set +# CONFIG_NLS_MAC_CELTIC is not set +# CONFIG_NLS_MAC_CENTEURO is not set +# CONFIG_NLS_MAC_CROATIAN is not set +# CONFIG_NLS_MAC_CYRILLIC is not set +# CONFIG_NLS_MAC_GAELIC is not set +# CONFIG_NLS_MAC_GREEK is not set +# CONFIG_NLS_MAC_ICELAND is not set +# CONFIG_NLS_MAC_INUIT is not set +# CONFIG_NLS_MAC_ROMANIAN is not set +# CONFIG_NLS_MAC_TURKISH is not set +# CONFIG_NLS_UTF8 is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_READABLE_ASM is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_LOCKUP_DETECTOR=y +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=1 +# CONFIG_PANIC_ON_OOPS is not set +CONFIG_PANIC_ON_OOPS_VALUE=0 +# CONFIG_DETECT_HUNG_TASK is not set +CONFIG_SCHED_DEBUG=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +CONFIG_HAVE_DEBUG_KMEMLEAK=y +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_HIGHMEM is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +CONFIG_DEBUG_MEMORY_INIT=y +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=21 +# CONFIG_RCU_CPU_STALL_INFO is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_DEBUG_PER_CPU_MAPS is not set +# CONFIG_LKDTM is not set +# CONFIG_NOTIFIER_ERROR_INJECTION is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +CONFIG_DYNAMIC_DEBUG=y +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_ARM_UNWIND=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_LL is not set +CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" +# CONFIG_OC_ETM is not set +# CONFIG_PID_IN_CONTEXTIDR is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +# CONFIG_CRYPTO is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +CONFIG_PERCPU_RWSEM=y +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_XZ_DEC=y +CONFIG_XZ_DEC_X86=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DECOMPRESS_XZ=y +CONFIG_DECOMPRESS_LZO=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_DMA=y +CONFIG_CPU_RMAP=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +# CONFIG_AVERAGE is not set +# CONFIG_CORDIC is not set +# CONFIG_DDR is not set diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 2e67a272df70..2f8816bc9c69 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -6,6 +6,8 @@ CONFIG_MACH_ARMADA_370=y CONFIG_ARCH_SIRF=y CONFIG_MACH_ARMADA_XP=y CONFIG_ARCH_HIGHBANK=y +CONFIG_MACH_HI4511=y +CONFIG_ARCH_HS=y CONFIG_ARCH_SOCFPGA=y CONFIG_ARCH_SUNXI=y CONFIG_ARCH_WM8850=y diff --git a/arch/arm/include/debug/hisilicon.S b/arch/arm/include/debug/hisilicon.S new file mode 100644 index 000000000000..2da0f6d55477 --- /dev/null +++ b/arch/arm/include/debug/hisilicon.S @@ -0,0 +1,35 @@ +/* + * Early serial output macro for Hisilicon SoC + * + * 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. +*/ + +#define HI3620_UART0_PHYS_BASE 0xfcb00000 +#define HI3620_UART0_VIRT_BASE 0xfeb00000 + +#define HI3716_UART0_PHYS_BASE 0xf8b00000 +#define HI3716_UART0_VIRT_BASE 0xfeb00000 + +#if defined(CONFIG_DEBUG_HI3620_UART0) + .macro addruart,rp,rv,tmp + ldr \rp, =HI3620_UART0_PHYS_BASE + ldr \rv, =HI3620_UART0_VIRT_BASE + .endm + +#include <asm/hardware/debug-pl01x.S> + +#elif defined(CONFIG_DEBUG_HI3716_UART0) + .macro addruart,rp,rv,tmp + ldr \rp, =HI3716_UART0_PHYS_BASE + ldr \rv, =HI3716_UART0_VIRT_BASE + .endm + +#include <asm/hardware/debug-pl01x.S> + +#endif diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 6002fe06b124..1f1eabd7b6be 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -470,6 +470,13 @@ void __init smp_setup_processor_id(void) for (i = 1; i < nr_cpu_ids; ++i) cpu_logical_map(i) = i == cpu ? 0 : i; + /* + * clear __my_cpu_offset on boot CPU to avoid hang caused by + * using percpu variable early, for example, lockdep will + * access percpu variable inside lock_release + */ + set_my_cpu_offset(0); + printk(KERN_INFO "Booting Linux on physical CPU 0x%x\n", mpidr); } diff --git a/arch/arm/mach-hs/Kconfig b/arch/arm/mach-hs/Kconfig new file mode 100644 index 000000000000..6fbc2e3ed560 --- /dev/null +++ b/arch/arm/mach-hs/Kconfig @@ -0,0 +1,21 @@ +config ARCH_HS + 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_HS + +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..efc9e5db80fb --- /dev/null +++ b/arch/arm/mach-hs/core.h @@ -0,0 +1,28 @@ +#ifndef __HISILICON_CORE_H +#define __HISILICON_CORE_H + +#include <linux/init.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(char 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..32cb26039a24 --- /dev/null +++ b/arch/arm/mach-hs/system.c @@ -0,0 +1,65 @@ +/* + * 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(char mode, const char *cmd) +{ + writel_relaxed(0xdeadbeef, hs_sctrl_base + hs_reboot_reg); + + while (1) + cpu_do_idle(); +} + diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h index d9d9d9c66dff..cdc67918cd37 100644 --- a/arch/arm/mach-imx/clk.h +++ b/arch/arm/mach-imx/clk.h @@ -77,7 +77,8 @@ static inline struct clk *imx_clk_gate(const char *name, const char *parent, static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg, u8 shift, u8 width, const char **parents, int num_parents) { - return clk_register_mux(NULL, name, parents, num_parents, 0, reg, shift, + return clk_register_mux(NULL, name, parents, num_parents, + CLK_SET_RATE_NO_REPARENT, reg, shift, width, 0, &imx_ccm_lock); } diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index d0d9b2124752..4efa404078e6 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -83,4 +83,5 @@ config COMMON_CLK_AXI_CLKGEN endmenu +source "drivers/clk/hisilicon/Kconfig" source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 137d3e730f86..9fa7b73533d5 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-composite.o obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o +obj-$(CONFIG_ARCH_HS) += hisilicon/ obj-$(CONFIG_ARCH_MXS) += mxs/ obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/ obj-$(CONFIG_PLAT_SPEAR) += spear/ diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 15114febfd92..790306e921c8 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -53,12 +53,18 @@ static void clk_gate_endisable(struct clk_hw *hw, int enable) if (gate->lock) spin_lock_irqsave(gate->lock, flags); - reg = readl(gate->reg); - - if (set) - reg |= BIT(gate->bit_idx); - else - reg &= ~BIT(gate->bit_idx); + if (gate->flags & CLK_GATE_HIWORD_MASK) { + reg = BIT(gate->bit_idx + 16); + if (set) + reg |= BIT(gate->bit_idx); + } else { + reg = readl(gate->reg); + + if (set) + reg |= BIT(gate->bit_idx); + else + reg &= ~BIT(gate->bit_idx); + } writel(reg, gate->reg); @@ -121,6 +127,13 @@ struct clk *clk_register_gate(struct device *dev, const char *name, struct clk *clk; struct clk_init_data init; + if (clk_gate_flags & CLK_GATE_HIWORD_MASK) { + if (bit_idx > 16) { + pr_err("gate bit exceeds LOWORD field\n"); + return ERR_PTR(-EINVAL); + } + } + /* allocate the gate */ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); if (!gate) { diff --git a/drivers/clk/clk-hi3xxx.c b/drivers/clk/clk-hi3xxx.c new file mode 100644 index 000000000000..ac1520b49729 --- /dev/null +++ b/drivers/clk/clk-hi3xxx.c @@ -0,0 +1,643 @@ +/* + * 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) + +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; + spinlock_t lock; +}; + +static void __init hs_init_clocks(void); + +static struct hs_clk hs_clk; + +static int hi3620_clkgate_prepare(struct clk_hw *hw) +{ + struct hi3620_periclk *pclk; + unsigned long flags = 0; + + pclk = container_of(hw, struct hi3620_periclk, hw); + + if (pclk->lock) + spin_lock_irqsave(pclk->lock, flags); + writel_relaxed(pclk->ebits, pclk->enable + HI3620_DISABLE_OFF); + 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; + u32 rdata[2], gdata[2]; + + if (!hs_clk.sctrl) + return; + + if (of_property_read_string(np, "clock-output-names", &clk_name)) + return; + if (of_property_read_u32_array(np, "hisilicon,hi3620-clkreset", + &rdata[0], 2)) + 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; + + pclk->reset = hs_clk.sctrl + rdata[0]; + pclk->rbits = rdata[1]; + pclk->enable = hs_clk.sctrl + gdata[0]; + pclk->ebits = gdata[1]; + pclk->lock = &hs_clk.lock; + pclk->hw.init = init; + + clk = clk_register(NULL, &pclk->hw); + if (IS_ERR(clk)) + goto err_clk; + if (!of_property_read_string(np, "clock-names", &name)) + clk_register_clkdev(clk, name, NULL); + of_clk_add_provider(np, of_clk_src_simple_get, clk); + return; +err_clk: + kfree(init); +err_init: + kfree(pclk); +err_pclk: + kfree(parent_names); +} + +static u8 hi3620_clk_get_parent(struct clk_hw *hw) +{ + struct hi3620_muxclk *mclk; + u32 data; + unsigned long flags = 0; + + mclk = container_of(hw, struct hi3620_muxclk, hw); + + if (mclk->lock) + spin_lock_irqsave(mclk->lock, flags); + + data = readl_relaxed(mclk->reg) >> mclk->shift; + data &= WIDTH_TO_MASK(mclk->width); + + if (mclk->lock) + spin_unlock_irqrestore(mclk->lock, flags); + + if (data >= __clk_get_num_parents(hw->clk)) + return -EINVAL; + + return (u8)data; +} + +static int hi3620_clk_set_parent(struct clk_hw *hw, u8 index) +{ + struct hi3620_muxclk *mclk; + u32 data; + unsigned long flags = 0; + + mclk = container_of(hw, struct hi3620_muxclk, hw); + + if (mclk->lock) + spin_lock_irqsave(mclk->lock, flags); + + data = readl_relaxed(mclk->reg); + data &= ~(WIDTH_TO_MASK(mclk->width) << mclk->shift); + data |= index << mclk->shift; + writel_relaxed(data, mclk->reg); + /* set mask enable bits */ + data |= mclk->mbits; + writel_relaxed(data, mclk->reg); + + if (mclk->lock) + spin_unlock_irqrestore(mclk->lock, flags); + + return 0; +} + +static struct clk_ops hi3620_clkmux_ops = { + .get_parent = hi3620_clk_get_parent, + .set_parent = hi3620_clk_set_parent, +}; + +static void __init hi3620_clkmux_setup(struct device_node *np) +{ + struct hi3620_muxclk *mclk; + struct clk_init_data *init; + struct clk *clk; + const char *clk_name, **parent_names; + u32 rdata[2]; + u8 num_parents; + int i; + + hs_init_clocks(); + if (!hs_clk.sctrl) + return; + + if (of_property_read_string(np, "clock-output-names", &clk_name)) + return; + if (of_property_read_u32_array(np, "hisilicon,hi3620-clkmux", + &rdata[0], 2)) + return; + /* get the count of items in mux */ + for (i = 0; ; i++) { + /* parent's #clock-cells property is always 0 */ + if (!of_parse_phandle(np, "clocks", i)) + break; + } + parent_names = kzalloc(sizeof(char *) * i, GFP_KERNEL); + if (!parent_names) + return; + + for (num_parents = i, i = 0; i < num_parents; i++) + parent_names[i] = of_clk_get_parent_name(np, i); + + mclk = kzalloc(sizeof(*mclk), GFP_KERNEL); + if (!mclk) + goto err_mclk; + init = kzalloc(sizeof(*init), GFP_KERNEL); + if (!init) + goto err_init; + init->name = kstrdup(clk_name, GFP_KERNEL); + init->ops = &hi3620_clkmux_ops; + init->flags = CLK_SET_RATE_PARENT; + init->parent_names = parent_names; + init->num_parents = num_parents; + + mclk->reg = hs_clk.sctrl + rdata[0]; + /* enable_mask bits are in higher 16bits */ + mclk->mbits = rdata[1] << 16; + mclk->shift = ffs(rdata[1]) - 1; + mclk->width = fls(rdata[1]) - ffs(rdata[1]) + 1; + mclk->lock = &hs_clk.lock; + mclk->hw.init = init; + + clk = clk_register(NULL, &mclk->hw); + if (IS_ERR(clk)) + goto err_clk; + of_clk_add_provider(np, of_clk_src_simple_get, clk); + + return; +err_clk: + kfree(init); +err_init: + kfree(mclk); +err_mclk: + kfree(parent_names); +} + +static void __init hs_clkgate_setup(struct device_node *np) +{ + struct clk *clk; + const char *clk_name, **parent_names, *name; + unsigned long flags = 0; + u32 data[2]; + + hs_init_clocks(); + if (!hs_clk.sctrl) + 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, + hs_clk.sctrl + 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], 0, + data[0], data[1]); + if (IS_ERR(clk)) + goto err; + of_clk_add_provider(np, of_clk_src_simple_get, clk); + return; +err: + kfree(parent_names); +} + +static unsigned int hi3620_get_table_maxdiv(const struct clk_div_table *table) +{ + unsigned int maxdiv = 0; + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div > maxdiv) + maxdiv = clkt->div; + return maxdiv; +} + +static unsigned int hi3620_get_table_div(const struct clk_div_table *table, + unsigned int val) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->val == val) + return clkt->div; + return 0; +} + +static unsigned int hi3620_get_table_val(const struct clk_div_table *table, + unsigned int div) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div == div) + return clkt->val; + return 0; +} + +static unsigned long hi3620_clkdiv_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct hi3620_divclk *dclk = container_of(hw, struct hi3620_divclk, hw); + unsigned int div, val; + + val = readl_relaxed(dclk->reg) >> dclk->shift; + val &= WIDTH_TO_MASK(dclk->width); + + div = hi3620_get_table_div(dclk->table, val); + if (!div) { + pr_warning("%s: Invalid divisor for clock %s\n", __func__, + __clk_get_name(hw->clk)); + return parent_rate; + } + + return parent_rate / div; +} + +static bool hi3620_is_valid_table_div(const struct clk_div_table *table, + unsigned int div) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div == div) + return true; + return false; +} + +static int hi3620_clkdiv_bestdiv(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate) +{ + struct hi3620_divclk *dclk = container_of(hw, struct hi3620_divclk, hw); + struct clk *clk_parent = __clk_get_parent(hw->clk); + int i, bestdiv = 0; + unsigned long parent_rate, best = 0, now, maxdiv; + + maxdiv = hi3620_get_table_maxdiv(dclk->table); + + if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { + parent_rate = *best_parent_rate; + bestdiv = DIV_ROUND_UP(parent_rate, rate); + bestdiv = bestdiv == 0 ? 1 : bestdiv; + bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; + return bestdiv; + } + + /* + * The maximum divider we can use without overflowing + * unsigned long in rate * i below + */ + maxdiv = min(ULONG_MAX / rate, maxdiv); + + for (i = 1; i <= maxdiv; i++) { + if (!hi3620_is_valid_table_div(dclk->table, i)) + continue; + parent_rate = __clk_round_rate(clk_parent, + MULT_ROUND_UP(rate, i)); + now = parent_rate / i; + if (now <= rate && now > best) { + bestdiv = i; + best = now; + *best_parent_rate = parent_rate; + } + } + + if (!bestdiv) { + bestdiv = hi3620_get_table_maxdiv(dclk->table); + *best_parent_rate = __clk_round_rate(clk_parent, 1); + } + + return bestdiv; +} + +static long hi3620_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + int div; + + if (!rate) + rate = 1; + div = hi3620_clkdiv_bestdiv(hw, rate, prate); + + return *prate / div; +} + +static int hi3620_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct hi3620_divclk *dclk = container_of(hw, struct hi3620_divclk, hw); + unsigned int div, value; + unsigned long flags = 0; + u32 data; + + div = parent_rate / rate; + value = hi3620_get_table_val(dclk->table, div); + + if (value > WIDTH_TO_MASK(dclk->width)) + value = WIDTH_TO_MASK(dclk->width); + + if (dclk->lock) + spin_lock_irqsave(dclk->lock, flags); + + data = readl_relaxed(dclk->reg); + data &= ~(WIDTH_TO_MASK(dclk->width) << dclk->shift); + data |= value << dclk->shift; + data |= dclk->mbits; + writel_relaxed(data, dclk->reg); + + if (dclk->lock) + spin_unlock_irqrestore(dclk->lock, flags); + + return 0; +} + +static struct clk_ops hi3620_clkdiv_ops = { + .recalc_rate = hi3620_clkdiv_recalc_rate, + .round_rate = hi3620_clkdiv_round_rate, + .set_rate = hi3620_clkdiv_set_rate, +}; + +void __init hi3620_clkdiv_setup(struct device_node *np) +{ + struct clk *clk; + const char *clk_name, **parent_names; + struct clk_init_data *init; + struct clk_div_table *table; + struct hi3620_divclk *dclk; + unsigned int table_num; + int i; + u32 data[2]; + const char *propname = "hisilicon,clkdiv-table"; + const char *cellname = "#hisilicon,clkdiv-table-cells"; + struct of_phandle_args div_table; + + hs_init_clocks(); + if (!hs_clk.sctrl) + return; + + if (of_property_read_string(np, "clock-output-names", &clk_name)) + return; + if (of_property_read_u32_array(np, "hisilicon,clkdiv", + &data[0], 2)) + return; + + /*process the div_table*/ + for (i = 0; ; i++) { + if (of_parse_phandle_with_args(np, propname, cellname, + i, &div_table)) + break; + } + + /*table ends with <0, 0>, so plus one to table_num*/ + table_num = i + 1; + + table = kzalloc(sizeof(struct clk_div_table) * table_num, GFP_KERNEL); + if (!table) + return ; + + for (i = 0; ; i++) { + if (of_parse_phandle_with_args(np, propname, cellname, + i, &div_table)) + break; + + table[i].val = div_table.args[0]; + table[i].div = div_table.args[1]; + } + + /* gate only has the fixed parent */ + parent_names = kzalloc(sizeof(char *), GFP_KERNEL); + if (!parent_names) + goto err_par; + parent_names[0] = of_clk_get_parent_name(np, 0); + + dclk = kzalloc(sizeof(*dclk), GFP_KERNEL); + if (!dclk) + goto err_dclk; + init = kzalloc(sizeof(*init), GFP_KERNEL); + if (!init) + goto err_init; + init->name = kstrdup(clk_name, GFP_KERNEL); + init->ops = &hi3620_clkdiv_ops; + init->parent_names = parent_names; + init->num_parents = 1; + + dclk->reg = hs_clk.sctrl + data[0]; + dclk->shift = ffs(data[1]) - 1; + dclk->width = fls(data[1]) - ffs(data[1]) + 1; + dclk->mbits = data[1] << 16; + dclk->lock = &hs_clk.lock; + dclk->hw.init = init; + dclk->table = table; + clk = clk_register(NULL, &dclk->hw); + if (IS_ERR(clk)) + goto err_clk; + of_clk_add_provider(np, of_clk_src_simple_get, clk); + return; +err_clk: + kfree(init); +err_init: + kfree(dclk); +err_dclk: + kfree(parent_names); +err_par: + kfree(table); +} +CLK_OF_DECLARE(hi3620_mux, "hisilicon,hi3620-clk-mux", hi3620_clkmux_setup) +CLK_OF_DECLARE(hi3620_gate, "hisilicon,hi3620-clk-gate", hi3620_clkgate_setup) +CLK_OF_DECLARE(hi3620_div, "hisilicon,hi3620-clk-div", hi3620_clkdiv_setup) +CLK_OF_DECLARE(hs_gate, "hisilicon,clk-gate", hs_clkgate_setup) +CLK_OF_DECLARE(hs_fixed, "hisilicon,clk-fixed-factor", hs_fixed_factor_setup) + +static void __init hs_init_clocks(void) +{ + struct device_node *node = NULL; + + if (!hs_clk.pmctrl) { + /* map pmctrl registers */ + node = of_find_compatible_node(NULL, NULL, "hisilicon,pmctrl"); + hs_clk.pmctrl = of_iomap(node, 0); + WARN_ON(!hs_clk.pmctrl); + } + + if (!hs_clk.sctrl) { + node = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl"); + hs_clk.sctrl = of_iomap(node, 0); + } +} diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 25b1734560d0..5af146ac500c 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -51,7 +51,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) for (i = 0; i < num_parents; i++) if (mux->table[i] == val) return i; - return -EINVAL; + return 0; } if (val && (mux->flags & CLK_MUX_INDEX_BIT)) @@ -61,7 +61,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) val--; if (val >= num_parents) - return -EINVAL; + return 0; return val; } @@ -70,6 +70,7 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index) { struct clk_mux *mux = to_clk_mux(hw); u32 val; + u8 width = 0; unsigned long flags = 0; if (mux->table) @@ -89,6 +90,13 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index) val = readl(mux->reg); val &= ~(mux->mask << mux->shift); val |= index << mux->shift; + if (mux->flags & CLK_MUX_HIWORD_MASK) { + width = fls(mux->mask) - ffs(mux->mask) + 1; + if (width + mux->shift > 16) + pr_warn("mux value exceeds LOWORD field\n"); + else + val |= mux->mask << (mux->shift + 16); + } writel(val, mux->reg); if (mux->lock) @@ -100,6 +108,7 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index) const struct clk_ops clk_mux_ops = { .get_parent = clk_mux_get_parent, .set_parent = clk_mux_set_parent, + .determine_rate = __clk_mux_determine_rate, }; EXPORT_SYMBOL_GPL(clk_mux_ops); diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 1144e8c7579d..6b582f730a05 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -226,6 +226,35 @@ static const struct file_operations clk_dump_fops = { .release = single_release, }; +#ifdef DEBUG +static int clk_rate_fops_get(void *data, u64 *rate) +{ + struct clk *clk = data; + + *rate = clk->rate; + + return 0; +}; + +static int clk_rate_fops_set(void *data, u64 rate) +{ + struct clk *clk = data; + int ret = 0; + + ret = clk_prepare_enable(clk); + if (ret) + goto out; + clk_set_rate(clk, rate); + clk_disable_unprepare(clk); + +out: + return ret; +}; + +DEFINE_SIMPLE_ATTRIBUTE(clk_rate_fops, clk_rate_fops_get, + clk_rate_fops_set, "%llu\n"); +#endif + /* caller must hold prepare_lock */ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry) { @@ -243,8 +272,13 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry) clk->dentry = d; +#ifdef DEBUG + d = debugfs_create_file("clk_rate", S_IWUSR | S_IRUGO, clk->dentry, + clk, &clk_rate_fops); +#else d = debugfs_create_u32("clk_rate", S_IRUGO, clk->dentry, (u32 *)&clk->rate); +#endif if (!d) goto err_out; @@ -559,6 +593,19 @@ struct clk *__clk_get_parent(struct clk *clk) return !clk ? NULL : clk->parent; } +struct clk *clk_get_parent_by_index(struct clk *clk, u8 index) +{ + if (!clk || index >= clk->num_parents) + return NULL; + else if (!clk->parents) + return __clk_lookup(clk->parent_names[index]); + else if (!clk->parents[index]) + return clk->parents[index] = + __clk_lookup(clk->parent_names[index]); + else + return clk->parents[index]; +} + unsigned int __clk_get_enable_count(struct clk *clk) { return !clk ? 0 : clk->enable_count; @@ -679,6 +726,55 @@ struct clk *__clk_lookup(const char *name) return NULL; } +/* + * Helper for finding best parent to provide a given frequency. This can be used + * directly as a determine_rate callback (e.g. for a mux), or from a more + * complex clock that may combine a mux with other operations. + */ +long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate, + struct clk **best_parent_p) +{ + struct clk *clk = hw->clk, *parent, *best_parent = NULL; + int i, num_parents; + unsigned long parent_rate, best = 0; + + /* if NO_REPARENT flag set, pass through to current parent */ + if (clk->flags & CLK_SET_RATE_NO_REPARENT) { + parent = clk->parent; + if (clk->flags & CLK_SET_RATE_PARENT) + best = __clk_round_rate(parent, rate); + else if (parent) + best = __clk_get_rate(parent); + else + best = __clk_get_rate(clk); + goto out; + } + + /* find the parent that can provide the fastest rate <= rate */ + num_parents = clk->num_parents; + for (i = 0; i < num_parents; i++) { + parent = clk_get_parent_by_index(clk, i); + if (!parent) + continue; + if (clk->flags & CLK_SET_RATE_PARENT) + parent_rate = __clk_round_rate(parent, rate); + else + parent_rate = __clk_get_rate(parent); + if (parent_rate <= rate && parent_rate > best) { + best_parent = parent; + best = parent_rate; + } + } + +out: + if (best_parent) + *best_parent_p = best_parent; + *best_parent_rate = best; + + return best; +} + /*** clk api ***/ void __clk_unprepare(struct clk *clk) @@ -875,21 +971,24 @@ EXPORT_SYMBOL_GPL(clk_enable); unsigned long __clk_round_rate(struct clk *clk, unsigned long rate) { unsigned long parent_rate = 0; + struct clk *parent; if (!clk) return 0; - if (!clk->ops->round_rate) { - if (clk->flags & CLK_SET_RATE_PARENT) - return __clk_round_rate(clk->parent, rate); - else - return clk->rate; - } - - if (clk->parent) - parent_rate = clk->parent->rate; - - return clk->ops->round_rate(clk->hw, rate, &parent_rate); + parent = clk->parent; + if (parent) + parent_rate = parent->rate; + + if (clk->ops->determine_rate) + return clk->ops->determine_rate(clk->hw, rate, &parent_rate, + &parent); + else if (clk->ops->round_rate) + return clk->ops->round_rate(clk->hw, rate, &parent_rate); + else if (clk->flags & CLK_SET_RATE_PARENT) + return __clk_round_rate(clk->parent, rate); + else + return clk->rate; } /** @@ -1014,6 +1113,112 @@ unsigned long clk_get_rate(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_get_rate); +static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent) +{ + u8 i; + + if (!clk->parents) + clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents), + GFP_KERNEL); + + /* + * find index of new parent clock using cached parent ptrs, + * or if not yet cached, use string name comparison and cache + * them now to avoid future calls to __clk_lookup. + */ + for (i = 0; i < clk->num_parents; i++) { + if (clk->parents && clk->parents[i] == parent) + break; + else if (!strcmp(clk->parent_names[i], parent->name)) { + if (clk->parents) + clk->parents[i] = __clk_lookup(parent->name); + break; + } + } + + return i; +} + +static void clk_reparent(struct clk *clk, struct clk *new_parent) +{ + /* avoid duplicate POST_RATE_CHANGE notifications */ + if (new_parent->new_child == clk) + new_parent->new_child = NULL; + + hlist_del(&clk->child_node); + + if (new_parent) + hlist_add_head(&clk->child_node, &new_parent->children); + else + hlist_add_head(&clk->child_node, &clk_orphan_list); + + clk->parent = new_parent; +} + +static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index) +{ + unsigned long flags; + int ret = 0; + struct clk *old_parent = clk->parent; + + /* + * Migrate prepare state between parents and prevent race with + * clk_enable(). + * + * If the clock is not prepared, then a race with + * clk_enable/disable() is impossible since we already have the + * prepare lock (future calls to clk_enable() need to be preceded by + * a clk_prepare()). + * + * If the clock is prepared, migrate the prepared state to the new + * parent and also protect against a race with clk_enable() by + * forcing the clock and the new parent on. This ensures that all + * future calls to clk_enable() are practically NOPs with respect to + * hardware and software states. + */ + if (clk->prepare_count) { + __clk_prepare(parent); + clk_enable(parent); + clk_enable(clk); + } + + /* update the clk tree topology */ + flags = clk_enable_lock(); + clk_reparent(clk, parent); + clk_enable_unlock(flags); + + /* change clock input source */ + if (parent && clk->ops->set_parent) + ret = clk->ops->set_parent(clk->hw, p_index); + + if (ret) { + flags = clk_enable_lock(); + clk_reparent(clk, old_parent); + clk_enable_unlock(flags); + + if (clk->prepare_count) { + clk_disable(clk); + clk_disable(parent); + __clk_unprepare(parent); + } + return ret; + } + + /* + * Finish the migration of prepare state and undo the changes done + * for preventing a race with clk_enable(). + */ + if (clk->prepare_count) { + clk_disable(clk); + clk_disable(old_parent); + __clk_unprepare(old_parent); + } + + /* update debugfs with new clk tree topology */ + clk_debug_reparent(clk, parent); + return 0; +} + /** * __clk_speculate_rates * @clk: first clk in the subtree @@ -1058,18 +1263,25 @@ out: return ret; } -static void clk_calc_subtree(struct clk *clk, unsigned long new_rate) +static void clk_calc_subtree(struct clk *clk, unsigned long new_rate, + struct clk *new_parent, u8 p_index) { struct clk *child; clk->new_rate = new_rate; + clk->new_parent = new_parent; + clk->new_parent_index = p_index; + /* include clk in new parent's PRE_RATE_CHANGE notifications */ + clk->new_child = NULL; + if (new_parent && new_parent != clk->parent) + new_parent->new_child = clk; hlist_for_each_entry(child, &clk->children, child_node) { if (child->ops->recalc_rate) child->new_rate = child->ops->recalc_rate(child->hw, new_rate); else child->new_rate = new_rate; - clk_calc_subtree(child, child->new_rate); + clk_calc_subtree(child, child->new_rate, NULL, 0); } } @@ -1080,50 +1292,63 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate) static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate) { struct clk *top = clk; + struct clk *old_parent, *parent; unsigned long best_parent_rate = 0; unsigned long new_rate; + u8 p_index = 0; /* sanity */ if (IS_ERR_OR_NULL(clk)) return NULL; /* save parent rate, if it exists */ - if (clk->parent) - best_parent_rate = clk->parent->rate; - - /* never propagate up to the parent */ - if (!(clk->flags & CLK_SET_RATE_PARENT)) { - if (!clk->ops->round_rate) { - clk->new_rate = clk->rate; - return NULL; - } - new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate); + parent = old_parent = clk->parent; + if (parent) + best_parent_rate = parent->rate; + + /* find the closest rate and parent clk/rate */ + if (clk->ops->determine_rate) { + new_rate = clk->ops->determine_rate(clk->hw, rate, + &best_parent_rate, + &parent); + } else if (clk->ops->round_rate) { + new_rate = clk->ops->round_rate(clk->hw, rate, + &best_parent_rate); + } else if (!parent || !(clk->flags & CLK_SET_RATE_PARENT)) { + /* pass-through clock without adjustable parent */ + clk->new_rate = clk->rate; + return NULL; + } else { + /* pass-through clock with adjustable parent */ + top = clk_calc_new_rates(parent, rate); + new_rate = parent->new_rate; goto out; } - /* need clk->parent from here on out */ - if (!clk->parent) { - pr_debug("%s: %s has NULL parent\n", __func__, clk->name); + /* some clocks must be gated to change parent */ + if (parent != old_parent && + (clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) { + pr_debug("%s: %s not gated but wants to reparent\n", + __func__, clk->name); return NULL; } - if (!clk->ops->round_rate) { - top = clk_calc_new_rates(clk->parent, rate); - new_rate = clk->parent->new_rate; - - goto out; + /* try finding the new parent index */ + if (parent) { + p_index = clk_fetch_parent_index(clk, parent); + if (p_index == clk->num_parents) { + pr_debug("%s: clk %s can not be parent of clk %s\n", + __func__, parent->name, clk->name); + return NULL; + } } - new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate); - - if (best_parent_rate != clk->parent->rate) { - top = clk_calc_new_rates(clk->parent, best_parent_rate); - - goto out; - } + if ((clk->flags & CLK_SET_RATE_PARENT) && parent && + best_parent_rate != parent->rate) + top = clk_calc_new_rates(parent, best_parent_rate); out: - clk_calc_subtree(clk, new_rate); + clk_calc_subtree(clk, new_rate, parent, p_index); return top; } @@ -1135,7 +1360,7 @@ out: */ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long event) { - struct clk *child, *fail_clk = NULL; + struct clk *child, *tmp_clk, *fail_clk = NULL; int ret = NOTIFY_DONE; if (clk->rate == clk->new_rate) @@ -1148,9 +1373,19 @@ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long even } hlist_for_each_entry(child, &clk->children, child_node) { - clk = clk_propagate_rate_change(child, event); - if (clk) - fail_clk = clk; + /* Skip children who will be reparented to another clock */ + if (child->new_parent && child->new_parent != clk) + continue; + tmp_clk = clk_propagate_rate_change(child, event); + if (tmp_clk) + fail_clk = tmp_clk; + } + + /* handle the new child who might not be in clk->children yet */ + if (clk->new_child) { + tmp_clk = clk_propagate_rate_change(clk->new_child, event); + if (tmp_clk) + fail_clk = tmp_clk; } return fail_clk; @@ -1168,6 +1403,10 @@ static void clk_change_rate(struct clk *clk) old_rate = clk->rate; + /* set parent */ + if (clk->new_parent && clk->new_parent != clk->parent) + __clk_set_parent(clk, clk->new_parent, clk->new_parent_index); + if (clk->parent) best_parent_rate = clk->parent->rate; @@ -1182,8 +1421,16 @@ static void clk_change_rate(struct clk *clk) if (clk->notifier_count && old_rate != clk->rate) __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); - hlist_for_each_entry(child, &clk->children, child_node) + hlist_for_each_entry(child, &clk->children, child_node) { + /* Skip children who will be reparented to another clock */ + if (child->new_parent && child->new_parent != clk) + continue; clk_change_rate(child); + } + + /* handle the new child who might not be in clk->children yet */ + if (clk->new_child) + clk_change_rate(clk->new_child); } /** @@ -1315,30 +1562,12 @@ static struct clk *__clk_init_parent(struct clk *clk) kzalloc((sizeof(struct clk*) * clk->num_parents), GFP_KERNEL); - if (!clk->parents) - ret = __clk_lookup(clk->parent_names[index]); - else if (!clk->parents[index]) - ret = clk->parents[index] = - __clk_lookup(clk->parent_names[index]); - else - ret = clk->parents[index]; + ret = clk_get_parent_by_index(clk, index); out: return ret; } -static void clk_reparent(struct clk *clk, struct clk *new_parent) -{ - hlist_del(&clk->child_node); - - if (new_parent) - hlist_add_head(&clk->child_node, &new_parent->children); - else - hlist_add_head(&clk->child_node, &clk_orphan_list); - - clk->parent = new_parent; -} - void __clk_reparent(struct clk *clk, struct clk *new_parent) { clk_reparent(clk, new_parent); @@ -1346,104 +1575,6 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent) __clk_recalc_rates(clk, POST_RATE_CHANGE); } -static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent) -{ - u8 i; - - if (!clk->parents) - clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents), - GFP_KERNEL); - - /* - * find index of new parent clock using cached parent ptrs, - * or if not yet cached, use string name comparison and cache - * them now to avoid future calls to __clk_lookup. - */ - for (i = 0; i < clk->num_parents; i++) { - if (clk->parents && clk->parents[i] == parent) - break; - else if (!strcmp(clk->parent_names[i], parent->name)) { - if (clk->parents) - clk->parents[i] = __clk_lookup(parent->name); - break; - } - } - - return i; -} - -static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index) -{ - unsigned long flags; - int ret = 0; - struct clk *old_parent = clk->parent; - bool migrated_enable = false; - - /* migrate prepare */ - if (clk->prepare_count) - __clk_prepare(parent); - - flags = clk_enable_lock(); - - /* migrate enable */ - if (clk->enable_count) { - __clk_enable(parent); - migrated_enable = true; - } - - /* update the clk tree topology */ - clk_reparent(clk, parent); - - clk_enable_unlock(flags); - - /* change clock input source */ - if (parent && clk->ops->set_parent) - ret = clk->ops->set_parent(clk->hw, p_index); - - if (ret) { - /* - * The error handling is tricky due to that we need to release - * the spinlock while issuing the .set_parent callback. This - * means the new parent might have been enabled/disabled in - * between, which must be considered when doing rollback. - */ - flags = clk_enable_lock(); - - clk_reparent(clk, old_parent); - - if (migrated_enable && clk->enable_count) { - __clk_disable(parent); - } else if (migrated_enable && (clk->enable_count == 0)) { - __clk_disable(old_parent); - } else if (!migrated_enable && clk->enable_count) { - __clk_disable(parent); - __clk_enable(old_parent); - } - - clk_enable_unlock(flags); - - if (clk->prepare_count) - __clk_unprepare(parent); - - return ret; - } - - /* clean up enable for old parent if migration was done */ - if (migrated_enable) { - flags = clk_enable_lock(); - __clk_disable(old_parent); - clk_enable_unlock(flags); - } - - /* clean up prepare for old parent if migration was done */ - if (clk->prepare_count) - __clk_unprepare(old_parent); - - /* update debugfs with new clk tree topology */ - clk_debug_reparent(clk, parent); - return 0; -} - /** * clk_set_parent - switch the parent of a mux clk * @clk: the mux clk whose input we are switching @@ -1546,8 +1677,9 @@ int __clk_init(struct device *dev, struct clk *clk) /* check that clk_ops are sane. See Documentation/clk.txt */ if (clk->ops->set_rate && - !(clk->ops->round_rate && clk->ops->recalc_rate)) { - pr_warning("%s: %s must implement .round_rate & .recalc_rate\n", + !((clk->ops->round_rate || clk->ops->determine_rate) && + clk->ops->recalc_rate)) { + pr_warning("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_rate\n", __func__, clk->name); ret = -EINVAL; goto out; diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig new file mode 100644 index 000000000000..1f8b3d3a2ed6 --- /dev/null +++ b/drivers/clk/hisilicon/Kconfig @@ -0,0 +1,7 @@ +config HI3xxx_CLK_CORE + bool "Core clock driver of Hi3xxx Soc" + default y if COMMON_CLK + +config HI3620_CLK_MIPI_DSI + bool "MIPI DSI clock driver of Hi3620 SoC" + default y if COMMON_CLK && FB_HI3620 diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile new file mode 100644 index 000000000000..e25405e712d9 --- /dev/null +++ b/drivers/clk/hisilicon/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_HI3xxx_CLK_CORE) += clk-hi3xxx.o clk-hi3716.o +obj-$(CONFIG_HI3620_CLK_MIPI_DSI) += clk-hi3620-dsi.o diff --git a/drivers/clk/hisilicon/clk-hi3620-dsi.c b/drivers/clk/hisilicon/clk-hi3620-dsi.c new file mode 100644 index 000000000000..c89e8ff1aa81 --- /dev/null +++ b/drivers/clk/hisilicon/clk-hi3620-dsi.c @@ -0,0 +1,364 @@ +/* + * Hisilicon Hi3620 MIPI DSI clock driver + * + * Copyright (c) 2012-2013 Hisilicon Limited. + * Copyright (c) 2012-2013 Linaro Limited. + * + * Author: Haojian Zhuang <haojian.zhuang@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/kernel.h> +#include <linux/clk-provider.h> +#include <linux/clk-private.h> +#include <linux/clkdev.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/slab.h> +#include <linux/clk.h> +#include <linux/platform_data/hi3620-dsi.h> + +#define DSI_CLKMGR_CFG 0x908 +#define DSI_PHY_RSTZ 0x954 + +enum { + HI3620_EDC, +}; + +struct clk_phy { + struct clk_hw hw; + void __iomem *reg_base; + unsigned int mult; + unsigned int div; +}; + +struct clk_esc { + struct clk_hw hw; + void __iomem *reg_base; + unsigned int div; +}; + +struct hs_clk { + void __iomem *edc; + spinlock_t lock; +}; + +static void __iomem __init *hi3620_init_clocks(struct device_node *np); + +static struct hs_clk hi3620_clk; + +struct phy_mult { + int mult; + int cp_current; + int lpf_ctrl; +}; + +static unsigned long clk_phy_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_phy *phy = container_of(hw, struct clk_phy, hw); + unsigned long long int rate; + + if (!phy->div) + return 0; + rate = (unsigned long long int)parent_rate * phy->mult; + do_div(rate, phy->div); + return (unsigned long)rate; +} + +static long clk_phy_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_phy *phy = container_of(hw, struct clk_phy, hw); + struct clk *clk_parent; + unsigned long long int mult, new_rate; + + clk_parent = __clk_get_parent(hw->clk); + *prate = __clk_get_rate(clk_parent); + mult = (unsigned long long int)rate * phy->div; + do_div(mult, *prate); + new_rate = *prate * mult; + do_div(new_rate, phy->div); + return (unsigned long)new_rate; +} + +static int clk_phy_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_phy *phy = container_of(hw, struct clk_phy, hw); + void __iomem *base = phy->reg_base; + unsigned long long int mult; + unsigned char cp_current, lpf_ctrl; + int i; + struct phy_mult m[] = { {32, 0x6, 0x10}, {64, 0x6, 0x10}, + {128, 0xc, 0x8}, {256, 0x4, 0x4}, + {512, 0x0, 0x1}, {768, 0x1, 0x1}, + {1000, 0x2, 0x1}, }; + mult = (unsigned long long int)rate * phy->div; + do_div(mult, parent_rate); + phy->mult = (unsigned int)mult; + + for (i = 0; i < ARRAY_SIZE(m); i++) { + if (phy->mult <= m[i].mult) { + cp_current = m[i].cp_current; + lpf_ctrl = m[i].lpf_ctrl; + break; + } + } + if (i >= ARRAY_SIZE(m)) + return -EINVAL; + lpf_ctrl |= 0xc0; /* bypass CP & LPF default values */ + /* write CP current */ + hi3620_dsi_phy_write(base, 0x11, cp_current); + /* write LPF control */ + hi3620_dsi_phy_write(base, 0x12, lpf_ctrl); + /* configure N and M factors effectively */ + hi3620_dsi_phy_write(base, 0x19, 0x33); + /* write N divider */ + hi3620_dsi_phy_write(base, 0x17, phy->div - 1); + /* write M multiplier 1 */ + hi3620_dsi_phy_write(base, 0x18, (phy->mult - 1) & 0x1f); + /* write M multiplier 2 */ + hi3620_dsi_phy_write(base, 0x18, ((phy->mult - 1) >> 5) | 0x80); + /* set PLL unlocking filter */ + hi3620_dsi_phy_write(base, 0x16, 0xff); + return 0; +} + +struct clk_ops clk_phy_ops = { + .round_rate = clk_phy_round_rate, + .set_rate = clk_phy_set_rate, + .recalc_rate = clk_phy_recalc_rate, +}; + +static struct clk *clk_phy_register(struct device *dev, const char *name, + const char *parent_name, void __iomem *reg, + unsigned long flags) +{ + struct clk_phy *phy; + struct clk_init_data init; + struct clk *clk; + + phy = kzalloc(sizeof(*phy), GFP_KERNEL); + if (!phy) { + pr_err("%s: could not allocate dsi phy clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + /* struct clk_phy assignments */ + phy->hw.init = &init; + phy->div = 13; /* since parent rate is always 26MHz */ + phy->reg_base = reg; + + init.name = name; + init.ops = &clk_phy_ops; + init.flags = flags | CLK_IS_BASIC; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(dev, &phy->hw); + + if (IS_ERR(clk)) + kfree(phy); + + return clk; +} + +void __init hi3620_phy_setup(struct device_node *np) +{ + struct clk *clk; + const char *clk_name, **parent_names; + void __iomem *reg_base; + + reg_base = hi3620_init_clocks(np); + if (!reg_base) + return; + if (of_property_read_string(np, "clock-output-names", &clk_name)) + return; + /* gate only has the fixed parent */ + parent_names = kzalloc(sizeof(char *), GFP_KERNEL); + if (!parent_names) + return; + parent_names[0] = of_clk_get_parent_name(np, 0); + + clk = clk_phy_register(NULL, clk_name, parent_names[0], reg_base, 0); + if (IS_ERR(clk)) + goto err; + of_clk_add_provider(np, of_clk_src_simple_get, clk); + return; +err: + kfree(parent_names); +} + +static int find_best_esc_divider(unsigned long parent_rate) +{ + unsigned int max_rate = 20000000; /* 20MHz */ + unsigned int target_rate = 10000000; /* 10MHz */ + unsigned int div, out_rate; + + div = parent_rate / target_rate; + for (; div > 0; div--) { + out_rate = parent_rate / div; + if (out_rate < max_rate) + break; + } + if (div <= 0) + return 0; + return div; +} + +static unsigned long clk_esc_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_esc *esc = container_of(hw, struct clk_esc, hw); + unsigned int div; + + if (!esc->div) + div = find_best_esc_divider(parent_rate); + else + div = esc->div; + if (!div) + return 0; + return parent_rate / div; +} + +static long clk_esc_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk *clk_parent; + unsigned int div; + + clk_parent = __clk_get_parent(hw->clk); + *prate = __clk_get_rate(clk_parent); + + div = find_best_esc_divider(*prate); + return *prate / div; +} + +static int clk_esc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_esc *esc = container_of(hw, struct clk_esc, hw); + unsigned int data; + + esc->div = parent_rate / rate; + data = readl_relaxed(esc->reg_base + DSI_CLKMGR_CFG); + data &= ~0xff; + writel_relaxed(data | esc->div, esc->reg_base + DSI_CLKMGR_CFG); + return 0; +} + +struct clk_ops clk_esc_ops = { + .round_rate = clk_esc_round_rate, + .set_rate = clk_esc_set_rate, + .recalc_rate = clk_esc_recalc_rate, +}; + +static struct clk *clk_esc_register(struct device *dev, const char *name, + const char *parent_name, void __iomem *reg, + unsigned long flags) +{ + struct clk_esc *esc; + struct clk_init_data init; + struct clk *clk; + + esc = kzalloc(sizeof(*esc), GFP_KERNEL); + if (!esc) { + pr_err("%s: could not allocate dsi esc clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + /* struct clk_esc assignments */ + esc->hw.init = &init; + esc->reg_base = reg; + + init.name = name; + init.ops = &clk_esc_ops; + init.flags = flags | CLK_IS_BASIC; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(dev, &esc->hw); + + if (IS_ERR(clk)) + kfree(esc); + + return clk; +} + +void __init hi3620_esc_setup(struct device_node *np) +{ + struct clk *clk; + const char *clk_name, **parent_names; + void __iomem *reg_base; + + reg_base = hi3620_init_clocks(np); + if (!reg_base) + return; + if (of_property_read_string(np, "clock-output-names", &clk_name)) + return; + /* gate only has the fixed parent */ + parent_names = kzalloc(sizeof(char *), GFP_KERNEL); + if (!parent_names) + return; + parent_names[0] = of_clk_get_parent_name(np, 0); + + clk = clk_esc_register(NULL, clk_name, parent_names[0], reg_base, 0); + if (IS_ERR(clk)) + goto err; + of_clk_add_provider(np, of_clk_src_simple_get, clk); + return; +err: + kfree(parent_names); +} + +CLK_OF_DECLARE(hi3620_dsi_pll, "hisilicon,hi3620-phy", hi3620_phy_setup) +CLK_OF_DECLARE(hi3620_dsi_esc, "hisilicon,hi3620-phy-esc", hi3620_esc_setup) + +static const struct of_device_id hi3620_of_match[] = { + { .compatible = "hisilicon,hi3620-fb", .data = (void *)HI3620_EDC, }, +}; + +static void __iomem __init *hi3620_init_clocks(struct device_node *np) +{ + struct device_node *parent; + const struct of_device_id *match; + void __iomem *ret = NULL; + + parent = of_get_parent(np); + if (!parent) + goto out; + match = of_match_node(hi3620_of_match, parent); + if (!match) + goto out; + switch ((unsigned int)match->data) { + case HI3620_EDC: + if (!hi3620_clk.edc) { + ret = of_iomap(parent, 0); + WARN_ON(!ret); + hi3620_clk.edc = ret; + } else { + ret = hi3620_clk.edc; + } + break; + } +out: + return ret; +} diff --git a/drivers/clk/hisilicon/clk-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..d3969a3ea5c8 --- /dev/null +++ b/drivers/clk/hisilicon/clk-hi3xxx.c @@ -0,0 +1,641 @@ +/* + * 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 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 shift, flag = 0; + void __iomem *reg, *base; + int i, cnt, 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; + + cnt = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + if (cnt < 0) { + pr_err("failed to find clock parent\n"); + return; + } + + table = kzalloc(sizeof(u32) * cnt, GFP_KERNEL); + if (!table) + return; + ret = of_property_read_u32_array(np, "hisilicon,clkmux-table", + table, cnt); + if (ret) { + pr_err("failed on parsing %s's clkmux-table \n", clk_name); + return; + } + + parent_names = kzalloc(sizeof(char *) * cnt, GFP_KERNEL); + if (!parent_names) + goto err; + for (i = 0; i < cnt; 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, (u8)cnt, + 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; + if (of_property_read_bool(np, "hiword")) + flags |= CLK_GATE_HIWORD_MASK; + /* 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], + CLK_SET_RATE_PARENT, reg_base + data[0], + (u8)data[1], flags, &hs_clk.lock); + if (IS_ERR(clk)) + goto err; + if (!of_property_read_string(np, "clock-names", &name)) + clk_register_clkdev(clk, name, NULL); + of_clk_add_provider(np, of_clk_src_simple_get, clk); + return; +err: + kfree(parent_names); +} + +void __init hs_fixed_factor_setup(struct device_node *np) +{ + struct clk *clk; + const char *clk_name, **parent_names; + u32 data[2]; + + if (of_property_read_string(np, "clock-output-names", &clk_name)) + return; + if (of_property_read_u32_array(np, "hisilicon,fixed-factor", + data, 2)) + return ; + /* gate only has the fixed parent */ + parent_names = kzalloc(sizeof(char *), GFP_KERNEL); + if (!parent_names) + return; + parent_names[0] = of_clk_get_parent_name(np, 0); + + clk = clk_register_fixed_factor(NULL, clk_name, parent_names[0], + CLK_SET_RATE_PARENT, + data[0], data[1]); + if (IS_ERR(clk)) + goto err; + of_clk_add_provider(np, of_clk_src_simple_get, clk); + return; +err: + kfree(parent_names); +} + +static unsigned int hi3620_get_table_maxdiv(const struct clk_div_table *table) +{ + unsigned int maxdiv = 0; + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div > maxdiv) + maxdiv = clkt->div; + return maxdiv; +} + +static unsigned int hi3620_get_table_div(const struct clk_div_table *table, + unsigned int val) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->val == val) + return clkt->div; + return 0; +} + +static unsigned int hi3620_get_table_val(const struct clk_div_table *table, + unsigned int div) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div == div) + return clkt->val; + return 0; +} + +static unsigned long hi3620_clkdiv_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct hi3620_divclk *dclk = container_of(hw, struct hi3620_divclk, hw); + unsigned int div, val; + + val = readl_relaxed(dclk->reg) >> dclk->shift; + val &= WIDTH_TO_MASK(dclk->width); + + div = hi3620_get_table_div(dclk->table, val); + if (!div) { + pr_warning("%s: Invalid divisor for clock %s\n", __func__, + __clk_get_name(hw->clk)); + return parent_rate; + } + + return parent_rate / div; +} + +static bool hi3620_is_valid_table_div(const struct clk_div_table *table, + unsigned int div) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div == div) + return true; + return false; +} + +static int hi3620_clkdiv_bestdiv(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate) +{ + struct hi3620_divclk *dclk = container_of(hw, struct hi3620_divclk, hw); + struct clk *clk_parent = __clk_get_parent(hw->clk); + int i, bestdiv = 0; + unsigned long parent_rate, best = 0, now, maxdiv; + + maxdiv = hi3620_get_table_maxdiv(dclk->table); + + if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { + parent_rate = *best_parent_rate; + bestdiv = DIV_ROUND_UP(parent_rate, rate); + bestdiv = bestdiv == 0 ? 1 : bestdiv; + bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; + return bestdiv; + } + + /* + * The maximum divider we can use without overflowing + * unsigned long in rate * i below + */ + maxdiv = min(ULONG_MAX / rate, maxdiv); + + for (i = 1; i <= maxdiv; i++) { + if (!hi3620_is_valid_table_div(dclk->table, i)) + continue; + parent_rate = __clk_round_rate(clk_parent, + MULT_ROUND_UP(rate, i)); + now = parent_rate / i; + if (now <= rate && now > best) { + bestdiv = i; + best = now; + *best_parent_rate = parent_rate; + } + } + + if (!bestdiv) { + bestdiv = hi3620_get_table_maxdiv(dclk->table); + *best_parent_rate = __clk_round_rate(clk_parent, 1); + } + + return bestdiv; +} + +static long hi3620_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + int div; + + if (!rate) + rate = 1; + div = hi3620_clkdiv_bestdiv(hw, rate, prate); + + return *prate / div; +} + +static int hi3620_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct hi3620_divclk *dclk = container_of(hw, struct hi3620_divclk, hw); + unsigned int div, value; + unsigned long flags = 0; + u32 data; + + div = parent_rate / rate; + value = hi3620_get_table_val(dclk->table, div); + + if (value > WIDTH_TO_MASK(dclk->width)) + value = WIDTH_TO_MASK(dclk->width); + + if (dclk->lock) + spin_lock_irqsave(dclk->lock, flags); + + data = readl_relaxed(dclk->reg); + data &= ~(WIDTH_TO_MASK(dclk->width) << dclk->shift); + data |= value << dclk->shift; + data |= dclk->mbits; + writel_relaxed(data, dclk->reg); + + if (dclk->lock) + spin_unlock_irqrestore(dclk->lock, flags); + + return 0; +} + +static struct clk_ops hi3620_clkdiv_ops = { + .recalc_rate = hi3620_clkdiv_recalc_rate, + .round_rate = hi3620_clkdiv_round_rate, + .set_rate = hi3620_clkdiv_set_rate, +}; + +void __init hi3620_clkdiv_setup(struct device_node *np) +{ + struct clk *clk; + const char *clk_name, **parent_names; + struct clk_init_data *init; + struct clk_div_table *table; + struct hi3620_divclk *dclk; + void __iomem *reg_base; + unsigned int table_num; + int i; + u32 data[2]; + unsigned int max_div, min_div; + struct of_phandle_args div_table; + + reg_base = hs_init_clocks(np); + if (!reg_base) + return; + + if (of_property_read_string(np, "clock-output-names", &clk_name)) + return; + + /*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 + 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/clk/mmp/clk-mmp2.c b/drivers/clk/mmp/clk-mmp2.c index d1f1a19d4351..b2721cae257a 100644 --- a/drivers/clk/mmp/clk-mmp2.c +++ b/drivers/clk/mmp/clk-mmp2.c @@ -248,7 +248,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "mmp2-pwm.3"); clk = clk_register_mux(NULL, "uart0_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART0, 4, 3, 0, &clk_lock); clk_set_parent(clk, vctcxo); clk_register_clkdev(clk, "uart_mux.0", NULL); @@ -258,7 +259,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.0"); clk = clk_register_mux(NULL, "uart1_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART1, 4, 3, 0, &clk_lock); clk_set_parent(clk, vctcxo); clk_register_clkdev(clk, "uart_mux.1", NULL); @@ -268,7 +270,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.1"); clk = clk_register_mux(NULL, "uart2_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART2, 4, 3, 0, &clk_lock); clk_set_parent(clk, vctcxo); clk_register_clkdev(clk, "uart_mux.2", NULL); @@ -278,7 +281,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.2"); clk = clk_register_mux(NULL, "uart3_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART3, 4, 3, 0, &clk_lock); clk_set_parent(clk, vctcxo); clk_register_clkdev(clk, "uart_mux.3", NULL); @@ -288,7 +292,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.3"); clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "uart_mux.0", NULL); @@ -297,7 +302,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.0"); clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.1", NULL); @@ -306,7 +312,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.1"); clk = clk_register_mux(NULL, "ssp2_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP2, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.2", NULL); @@ -315,7 +322,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.2"); clk = clk_register_mux(NULL, "ssp3_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP3, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.3", NULL); @@ -324,7 +332,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.3"); clk = clk_register_mux(NULL, "sdh_mux", sdh_parent, - ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(sdh_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_SDH0, 8, 2, 0, &clk_lock); clk_register_clkdev(clk, "sdh_mux", NULL); @@ -354,7 +363,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, "usb_clk", NULL); clk = clk_register_mux(NULL, "disp0_mux", disp_parent, - ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(disp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_DISP0, 6, 2, 0, &clk_lock); clk_register_clkdev(clk, "disp_mux.0", NULL); @@ -376,7 +386,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, "disp_sphy.0", NULL); clk = clk_register_mux(NULL, "disp1_mux", disp_parent, - ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(disp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_DISP1, 6, 2, 0, &clk_lock); clk_register_clkdev(clk, "disp_mux.1", NULL); @@ -394,7 +405,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, "ccic_arbiter", NULL); clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent, - ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ccic_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_CCIC0, 6, 2, 0, &clk_lock); clk_register_clkdev(clk, "ccic_mux.0", NULL); @@ -421,7 +433,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, "sphyclk", "mmp-ccic.0"); clk = clk_register_mux(NULL, "ccic1_mux", ccic_parent, - ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ccic_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_CCIC1, 6, 2, 0, &clk_lock); clk_register_clkdev(clk, "ccic_mux.1", NULL); diff --git a/drivers/clk/mmp/clk-pxa168.c b/drivers/clk/mmp/clk-pxa168.c index 28b3b51c794b..014396b028a2 100644 --- a/drivers/clk/mmp/clk-pxa168.c +++ b/drivers/clk/mmp/clk-pxa168.c @@ -199,7 +199,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "pxa168-pwm.3"); clk = clk_register_mux(NULL, "uart0_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART0, 4, 3, 0, &clk_lock); clk_set_parent(clk, uart_pll); clk_register_clkdev(clk, "uart_mux.0", NULL); @@ -209,7 +210,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.0"); clk = clk_register_mux(NULL, "uart1_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART1, 4, 3, 0, &clk_lock); clk_set_parent(clk, uart_pll); clk_register_clkdev(clk, "uart_mux.1", NULL); @@ -219,7 +221,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.1"); clk = clk_register_mux(NULL, "uart2_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART2, 4, 3, 0, &clk_lock); clk_set_parent(clk, uart_pll); clk_register_clkdev(clk, "uart_mux.2", NULL); @@ -229,7 +232,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.2"); clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "uart_mux.0", NULL); @@ -238,7 +242,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.0"); clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.1", NULL); @@ -247,7 +252,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.1"); clk = clk_register_mux(NULL, "ssp2_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP2, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.2", NULL); @@ -256,7 +262,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.2"); clk = clk_register_mux(NULL, "ssp3_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP3, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.3", NULL); @@ -265,7 +272,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.3"); clk = clk_register_mux(NULL, "ssp4_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP4, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.4", NULL); @@ -278,7 +286,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "pxa3xx-nand.0"); clk = clk_register_mux(NULL, "sdh0_mux", sdh_parent, - ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(sdh_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_SDH0, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "sdh0_mux", NULL); @@ -287,7 +296,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "sdhci-pxa.0"); clk = clk_register_mux(NULL, "sdh1_mux", sdh_parent, - ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(sdh_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_SDH1, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "sdh1_mux", NULL); @@ -304,7 +314,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, "sph_clk", NULL); clk = clk_register_mux(NULL, "disp0_mux", disp_parent, - ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(disp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_DISP0, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "disp_mux.0", NULL); @@ -317,7 +328,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, "hclk", "mmp-disp.0"); clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent, - ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ccic_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_CCIC0, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "ccic_mux.0", NULL); @@ -327,8 +339,8 @@ void __init pxa168_clk_init(void) clk = clk_register_mux(NULL, "ccic0_phy_mux", ccic_phy_parent, ARRAY_SIZE(ccic_phy_parent), - CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0, - 7, 1, 0, &clk_lock); + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + apmu_base + APMU_CCIC0, 7, 1, 0, &clk_lock); clk_register_clkdev(clk, "ccic_phy_mux.0", NULL); clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_phy_mux", diff --git a/drivers/clk/mmp/clk-pxa910.c b/drivers/clk/mmp/clk-pxa910.c index 6ec05698ed38..9efc6a47535d 100644 --- a/drivers/clk/mmp/clk-pxa910.c +++ b/drivers/clk/mmp/clk-pxa910.c @@ -204,7 +204,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "pxa910-pwm.3"); clk = clk_register_mux(NULL, "uart0_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART0, 4, 3, 0, &clk_lock); clk_set_parent(clk, uart_pll); clk_register_clkdev(clk, "uart_mux.0", NULL); @@ -214,7 +215,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.0"); clk = clk_register_mux(NULL, "uart1_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART1, 4, 3, 0, &clk_lock); clk_set_parent(clk, uart_pll); clk_register_clkdev(clk, "uart_mux.1", NULL); @@ -224,7 +226,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.1"); clk = clk_register_mux(NULL, "uart2_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbcp_base + APBCP_UART2, 4, 3, 0, &clk_lock); clk_set_parent(clk, uart_pll); clk_register_clkdev(clk, "uart_mux.2", NULL); @@ -234,7 +237,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.2"); clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "uart_mux.0", NULL); @@ -243,7 +247,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.0"); clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.1", NULL); @@ -256,7 +261,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "pxa3xx-nand.0"); clk = clk_register_mux(NULL, "sdh0_mux", sdh_parent, - ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(sdh_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_SDH0, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "sdh0_mux", NULL); @@ -265,7 +271,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "sdhci-pxa.0"); clk = clk_register_mux(NULL, "sdh1_mux", sdh_parent, - ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(sdh_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_SDH1, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "sdh1_mux", NULL); @@ -282,7 +289,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, "sph_clk", NULL); clk = clk_register_mux(NULL, "disp0_mux", disp_parent, - ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(disp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_DISP0, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "disp_mux.0", NULL); @@ -291,7 +299,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-disp.0"); clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent, - ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ccic_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_CCIC0, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "ccic_mux.0", NULL); @@ -301,8 +310,8 @@ void __init pxa910_clk_init(void) clk = clk_register_mux(NULL, "ccic0_phy_mux", ccic_phy_parent, ARRAY_SIZE(ccic_phy_parent), - CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0, - 7, 1, 0, &clk_lock); + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + apmu_base + APMU_CCIC0, 7, 1, 0, &clk_lock); clk_register_clkdev(clk, "ccic_phy_mux.0", NULL); clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_phy_mux", diff --git a/drivers/clk/mxs/clk.h b/drivers/clk/mxs/clk.h index 81421e28e69c..ef10ad9b5daa 100644 --- a/drivers/clk/mxs/clk.h +++ b/drivers/clk/mxs/clk.h @@ -52,8 +52,8 @@ static inline struct clk *mxs_clk_mux(const char *name, void __iomem *reg, u8 shift, u8 width, const char **parent_names, int num_parents) { return clk_register_mux(NULL, name, parent_names, num_parents, - CLK_SET_RATE_PARENT, reg, shift, width, - 0, &mxs_lock); + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + reg, shift, width, 0, &mxs_lock); } static inline struct clk *mxs_clk_fixed_factor(const char *name, diff --git a/drivers/clk/spear/spear1310_clock.c b/drivers/clk/spear/spear1310_clock.c index aedbbe12f321..65894f7687ed 100644 --- a/drivers/clk/spear/spear1310_clock.c +++ b/drivers/clk/spear/spear1310_clock.c @@ -416,9 +416,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) /* clock derived from 24 or 25 MHz osc clk */ /* vco-pll */ clk = clk_register_mux(NULL, "vco1_mclk", vco_parents, - ARRAY_SIZE(vco_parents), 0, SPEAR1310_PLL_CFG, - SPEAR1310_PLL1_CLK_SHIFT, SPEAR1310_PLL_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PLL_CFG, SPEAR1310_PLL1_CLK_SHIFT, + SPEAR1310_PLL_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "vco1_mclk", NULL); clk = clk_register_vco_pll("vco1_clk", "pll1_clk", NULL, "vco1_mclk", 0, SPEAR1310_PLL1_CTR, SPEAR1310_PLL1_FRQ, pll_rtbl, @@ -427,9 +427,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk1, "pll1_clk", NULL); clk = clk_register_mux(NULL, "vco2_mclk", vco_parents, - ARRAY_SIZE(vco_parents), 0, SPEAR1310_PLL_CFG, - SPEAR1310_PLL2_CLK_SHIFT, SPEAR1310_PLL_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PLL_CFG, SPEAR1310_PLL2_CLK_SHIFT, + SPEAR1310_PLL_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "vco2_mclk", NULL); clk = clk_register_vco_pll("vco2_clk", "pll2_clk", NULL, "vco2_mclk", 0, SPEAR1310_PLL2_CTR, SPEAR1310_PLL2_FRQ, pll_rtbl, @@ -438,9 +438,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk1, "pll2_clk", NULL); clk = clk_register_mux(NULL, "vco3_mclk", vco_parents, - ARRAY_SIZE(vco_parents), 0, SPEAR1310_PLL_CFG, - SPEAR1310_PLL3_CLK_SHIFT, SPEAR1310_PLL_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PLL_CFG, SPEAR1310_PLL3_CLK_SHIFT, + SPEAR1310_PLL_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "vco3_mclk", NULL); clk = clk_register_vco_pll("vco3_clk", "pll3_clk", NULL, "vco3_mclk", 0, SPEAR1310_PLL3_CTR, SPEAR1310_PLL3_FRQ, pll_rtbl, @@ -515,9 +515,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) /* gpt clocks */ clk = clk_register_mux(NULL, "gpt0_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG, - SPEAR1310_GPT0_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GPT0_CLK_SHIFT, + SPEAR1310_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt0_mclk", NULL); clk = clk_register_gate(NULL, "gpt0_clk", "gpt0_mclk", 0, SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_GPT0_CLK_ENB, 0, @@ -525,9 +525,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "gpt0"); clk = clk_register_mux(NULL, "gpt1_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG, - SPEAR1310_GPT1_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GPT1_CLK_SHIFT, + SPEAR1310_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt1_mclk", NULL); clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk", 0, SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_GPT1_CLK_ENB, 0, @@ -535,9 +535,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "gpt1"); clk = clk_register_mux(NULL, "gpt2_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG, - SPEAR1310_GPT2_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GPT2_CLK_SHIFT, + SPEAR1310_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt2_mclk", NULL); clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk", 0, SPEAR1310_PERIP2_CLK_ENB, SPEAR1310_GPT2_CLK_ENB, 0, @@ -545,9 +545,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "gpt2"); clk = clk_register_mux(NULL, "gpt3_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG, - SPEAR1310_GPT3_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GPT3_CLK_SHIFT, + SPEAR1310_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt3_mclk", NULL); clk = clk_register_gate(NULL, "gpt3_clk", "gpt3_mclk", 0, SPEAR1310_PERIP2_CLK_ENB, SPEAR1310_GPT3_CLK_ENB, 0, @@ -562,7 +562,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk1, "uart_syn_gclk", NULL); clk = clk_register_mux(NULL, "uart0_mclk", uart0_parents, - ARRAY_SIZE(uart0_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart0_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1310_PERIP_CLK_CFG, SPEAR1310_UART_CLK_SHIFT, SPEAR1310_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart0_mclk", NULL); @@ -602,7 +603,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk1, "c3_syn_gclk", NULL); clk = clk_register_mux(NULL, "c3_mclk", c3_parents, - ARRAY_SIZE(c3_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(c3_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1310_PERIP_CLK_CFG, SPEAR1310_C3_CLK_SHIFT, SPEAR1310_C3_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "c3_mclk", NULL); @@ -614,8 +616,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) /* gmac */ clk = clk_register_mux(NULL, "phy_input_mclk", gmac_phy_input_parents, - ARRAY_SIZE(gmac_phy_input_parents), 0, - SPEAR1310_GMAC_CLK_CFG, + ARRAY_SIZE(gmac_phy_input_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1310_GMAC_CLK_CFG, SPEAR1310_GMAC_PHY_INPUT_CLK_SHIFT, SPEAR1310_GMAC_PHY_INPUT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "phy_input_mclk", NULL); @@ -627,15 +629,16 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk1, "phy_syn_gclk", NULL); clk = clk_register_mux(NULL, "phy_mclk", gmac_phy_parents, - ARRAY_SIZE(gmac_phy_parents), 0, + ARRAY_SIZE(gmac_phy_parents), CLK_SET_RATE_NO_REPARENT, SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GMAC_PHY_CLK_SHIFT, SPEAR1310_GMAC_PHY_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "stmmacphy.0", NULL); /* clcd */ clk = clk_register_mux(NULL, "clcd_syn_mclk", clcd_synth_parents, - ARRAY_SIZE(clcd_synth_parents), 0, - SPEAR1310_CLCD_CLK_SYNT, SPEAR1310_CLCD_SYNT_CLK_SHIFT, + ARRAY_SIZE(clcd_synth_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1310_CLCD_CLK_SYNT, + SPEAR1310_CLCD_SYNT_CLK_SHIFT, SPEAR1310_CLCD_SYNT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "clcd_syn_mclk", NULL); @@ -645,7 +648,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, "clcd_syn_clk", NULL); clk = clk_register_mux(NULL, "clcd_pixel_mclk", clcd_pixel_parents, - ARRAY_SIZE(clcd_pixel_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(clcd_pixel_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1310_PERIP_CLK_CFG, SPEAR1310_CLCD_CLK_SHIFT, SPEAR1310_CLCD_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "clcd_pixel_mclk", NULL); @@ -657,9 +661,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) /* i2s */ clk = clk_register_mux(NULL, "i2s_src_mclk", i2s_src_parents, - ARRAY_SIZE(i2s_src_parents), 0, SPEAR1310_I2S_CLK_CFG, - SPEAR1310_I2S_SRC_CLK_SHIFT, SPEAR1310_I2S_SRC_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(i2s_src_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_I2S_CLK_CFG, SPEAR1310_I2S_SRC_CLK_SHIFT, + SPEAR1310_I2S_SRC_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2s_src_mclk", NULL); clk = clk_register_aux("i2s_prs1_clk", NULL, "i2s_src_mclk", 0, @@ -668,7 +672,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, "i2s_prs1_clk", NULL); clk = clk_register_mux(NULL, "i2s_ref_mclk", i2s_ref_parents, - ARRAY_SIZE(i2s_ref_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(i2s_ref_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1310_I2S_CLK_CFG, SPEAR1310_I2S_REF_SHIFT, SPEAR1310_I2S_REF_SEL_MASK, 0, &_lock); clk_register_clkdev(clk, "i2s_ref_mclk", NULL); @@ -806,13 +811,15 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) /* RAS clks */ clk = clk_register_mux(NULL, "gen_syn0_1_mclk", gen_synth0_1_parents, - ARRAY_SIZE(gen_synth0_1_parents), 0, SPEAR1310_PLL_CFG, + ARRAY_SIZE(gen_synth0_1_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1310_PLL_CFG, SPEAR1310_RAS_SYNT0_1_CLK_SHIFT, SPEAR1310_RAS_SYNT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gen_syn0_1_clk", NULL); clk = clk_register_mux(NULL, "gen_syn2_3_mclk", gen_synth2_3_parents, - ARRAY_SIZE(gen_synth2_3_parents), 0, SPEAR1310_PLL_CFG, + ARRAY_SIZE(gen_synth2_3_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1310_PLL_CFG, SPEAR1310_RAS_SYNT2_3_CLK_SHIFT, SPEAR1310_RAS_SYNT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gen_syn2_3_clk", NULL); @@ -929,8 +936,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk = clk_register_mux(NULL, "smii_rgmii_phy_mclk", smii_rgmii_phy_parents, - ARRAY_SIZE(smii_rgmii_phy_parents), 0, - SPEAR1310_RAS_CTRL_REG1, + ARRAY_SIZE(smii_rgmii_phy_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1310_RAS_CTRL_REG1, SPEAR1310_SMII_RGMII_PHY_CLK_SHIFT, SPEAR1310_PHY_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "stmmacphy.1", NULL); @@ -938,15 +945,15 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, "stmmacphy.4", NULL); clk = clk_register_mux(NULL, "rmii_phy_mclk", rmii_phy_parents, - ARRAY_SIZE(rmii_phy_parents), 0, + ARRAY_SIZE(rmii_phy_parents), CLK_SET_RATE_NO_REPARENT, SPEAR1310_RAS_CTRL_REG1, SPEAR1310_RMII_PHY_CLK_SHIFT, SPEAR1310_PHY_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "stmmacphy.3", NULL); clk = clk_register_mux(NULL, "uart1_mclk", uart_parents, - ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_UART1_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART1_CLK_SHIFT, + SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart1_mclk", NULL); clk = clk_register_gate(NULL, "uart1_clk", "uart1_mclk", 0, @@ -955,9 +962,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5c800000.serial"); clk = clk_register_mux(NULL, "uart2_mclk", uart_parents, - ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_UART2_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART2_CLK_SHIFT, + SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart2_mclk", NULL); clk = clk_register_gate(NULL, "uart2_clk", "uart2_mclk", 0, @@ -966,9 +973,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5c900000.serial"); clk = clk_register_mux(NULL, "uart3_mclk", uart_parents, - ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_UART3_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART3_CLK_SHIFT, + SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart3_mclk", NULL); clk = clk_register_gate(NULL, "uart3_clk", "uart3_mclk", 0, @@ -977,9 +984,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5ca00000.serial"); clk = clk_register_mux(NULL, "uart4_mclk", uart_parents, - ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_UART4_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART4_CLK_SHIFT, + SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart4_mclk", NULL); clk = clk_register_gate(NULL, "uart4_clk", "uart4_mclk", 0, @@ -988,9 +995,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5cb00000.serial"); clk = clk_register_mux(NULL, "uart5_mclk", uart_parents, - ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_UART5_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART5_CLK_SHIFT, + SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart5_mclk", NULL); clk = clk_register_gate(NULL, "uart5_clk", "uart5_mclk", 0, @@ -999,9 +1006,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5cc00000.serial"); clk = clk_register_mux(NULL, "i2c1_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C1_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C1_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c1_mclk", NULL); clk = clk_register_gate(NULL, "i2c1_clk", "i2c1_mclk", 0, @@ -1010,9 +1017,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5cd00000.i2c"); clk = clk_register_mux(NULL, "i2c2_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C2_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C2_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c2_mclk", NULL); clk = clk_register_gate(NULL, "i2c2_clk", "i2c2_mclk", 0, @@ -1021,9 +1028,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5ce00000.i2c"); clk = clk_register_mux(NULL, "i2c3_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C3_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C3_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c3_mclk", NULL); clk = clk_register_gate(NULL, "i2c3_clk", "i2c3_mclk", 0, @@ -1032,9 +1039,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5cf00000.i2c"); clk = clk_register_mux(NULL, "i2c4_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C4_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C4_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c4_mclk", NULL); clk = clk_register_gate(NULL, "i2c4_clk", "i2c4_mclk", 0, @@ -1043,9 +1050,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5d000000.i2c"); clk = clk_register_mux(NULL, "i2c5_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C5_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C5_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c5_mclk", NULL); clk = clk_register_gate(NULL, "i2c5_clk", "i2c5_mclk", 0, @@ -1054,9 +1061,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5d100000.i2c"); clk = clk_register_mux(NULL, "i2c6_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C6_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C6_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c6_mclk", NULL); clk = clk_register_gate(NULL, "i2c6_clk", "i2c6_mclk", 0, @@ -1065,9 +1072,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5d200000.i2c"); clk = clk_register_mux(NULL, "i2c7_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C7_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C7_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c7_mclk", NULL); clk = clk_register_gate(NULL, "i2c7_clk", "i2c7_mclk", 0, @@ -1076,9 +1083,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5d300000.i2c"); clk = clk_register_mux(NULL, "ssp1_mclk", ssp1_parents, - ARRAY_SIZE(ssp1_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_SSP1_CLK_SHIFT, SPEAR1310_SSP1_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(ssp1_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_SSP1_CLK_SHIFT, + SPEAR1310_SSP1_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "ssp1_mclk", NULL); clk = clk_register_gate(NULL, "ssp1_clk", "ssp1_mclk", 0, @@ -1087,9 +1094,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5d400000.spi"); clk = clk_register_mux(NULL, "pci_mclk", pci_parents, - ARRAY_SIZE(pci_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_PCI_CLK_SHIFT, SPEAR1310_PCI_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(pci_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_PCI_CLK_SHIFT, + SPEAR1310_PCI_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "pci_mclk", NULL); clk = clk_register_gate(NULL, "pci_clk", "pci_mclk", 0, @@ -1098,9 +1105,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "pci"); clk = clk_register_mux(NULL, "tdm1_mclk", tdm_parents, - ARRAY_SIZE(tdm_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_TDM1_CLK_SHIFT, SPEAR1310_TDM_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(tdm_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_TDM1_CLK_SHIFT, + SPEAR1310_TDM_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "tdm1_mclk", NULL); clk = clk_register_gate(NULL, "tdm1_clk", "tdm1_mclk", 0, @@ -1109,9 +1116,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "tdm_hdlc.0"); clk = clk_register_mux(NULL, "tdm2_mclk", tdm_parents, - ARRAY_SIZE(tdm_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_TDM2_CLK_SHIFT, SPEAR1310_TDM_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(tdm_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_TDM2_CLK_SHIFT, + SPEAR1310_TDM_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "tdm2_mclk", NULL); clk = clk_register_gate(NULL, "tdm2_clk", "tdm2_mclk", 0, diff --git a/drivers/clk/spear/spear1340_clock.c b/drivers/clk/spear/spear1340_clock.c index 9d0b3949db30..fe835c1845fe 100644 --- a/drivers/clk/spear/spear1340_clock.c +++ b/drivers/clk/spear/spear1340_clock.c @@ -473,9 +473,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) /* clock derived from 24 or 25 MHz osc clk */ /* vco-pll */ clk = clk_register_mux(NULL, "vco1_mclk", vco_parents, - ARRAY_SIZE(vco_parents), 0, SPEAR1340_PLL_CFG, - SPEAR1340_PLL1_CLK_SHIFT, SPEAR1340_PLL_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PLL_CFG, SPEAR1340_PLL1_CLK_SHIFT, + SPEAR1340_PLL_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "vco1_mclk", NULL); clk = clk_register_vco_pll("vco1_clk", "pll1_clk", NULL, "vco1_mclk", 0, SPEAR1340_PLL1_CTR, SPEAR1340_PLL1_FRQ, pll_rtbl, @@ -484,9 +484,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "pll1_clk", NULL); clk = clk_register_mux(NULL, "vco2_mclk", vco_parents, - ARRAY_SIZE(vco_parents), 0, SPEAR1340_PLL_CFG, - SPEAR1340_PLL2_CLK_SHIFT, SPEAR1340_PLL_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PLL_CFG, SPEAR1340_PLL2_CLK_SHIFT, + SPEAR1340_PLL_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "vco2_mclk", NULL); clk = clk_register_vco_pll("vco2_clk", "pll2_clk", NULL, "vco2_mclk", 0, SPEAR1340_PLL2_CTR, SPEAR1340_PLL2_FRQ, pll_rtbl, @@ -495,9 +495,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "pll2_clk", NULL); clk = clk_register_mux(NULL, "vco3_mclk", vco_parents, - ARRAY_SIZE(vco_parents), 0, SPEAR1340_PLL_CFG, - SPEAR1340_PLL3_CLK_SHIFT, SPEAR1340_PLL_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PLL_CFG, SPEAR1340_PLL3_CLK_SHIFT, + SPEAR1340_PLL_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "vco3_mclk", NULL); clk = clk_register_vco_pll("vco3_clk", "pll3_clk", NULL, "vco3_mclk", 0, SPEAR1340_PLL3_CTR, SPEAR1340_PLL3_FRQ, pll_rtbl, @@ -561,8 +561,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "amba_syn_clk", NULL); clk = clk_register_mux(NULL, "sys_mclk", sys_parents, - ARRAY_SIZE(sys_parents), 0, SPEAR1340_SYS_CLK_CTRL, - SPEAR1340_SCLK_SRC_SEL_SHIFT, + ARRAY_SIZE(sys_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_SYS_CLK_CTRL, SPEAR1340_SCLK_SRC_SEL_SHIFT, SPEAR1340_SCLK_SRC_SEL_MASK, 0, &_lock); clk_register_clkdev(clk, "sys_mclk", NULL); @@ -583,8 +583,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, NULL, "smp_twd"); clk = clk_register_mux(NULL, "ahb_clk", ahb_parents, - ARRAY_SIZE(ahb_parents), 0, SPEAR1340_SYS_CLK_CTRL, - SPEAR1340_HCLK_SRC_SEL_SHIFT, + ARRAY_SIZE(ahb_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_SYS_CLK_CTRL, SPEAR1340_HCLK_SRC_SEL_SHIFT, SPEAR1340_HCLK_SRC_SEL_MASK, 0, &_lock); clk_register_clkdev(clk, "ahb_clk", NULL); @@ -594,9 +594,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) /* gpt clocks */ clk = clk_register_mux(NULL, "gpt0_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG, - SPEAR1340_GPT0_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GPT0_CLK_SHIFT, + SPEAR1340_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt0_mclk", NULL); clk = clk_register_gate(NULL, "gpt0_clk", "gpt0_mclk", 0, SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_GPT0_CLK_ENB, 0, @@ -604,9 +604,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, NULL, "gpt0"); clk = clk_register_mux(NULL, "gpt1_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG, - SPEAR1340_GPT1_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GPT1_CLK_SHIFT, + SPEAR1340_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt1_mclk", NULL); clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk", 0, SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_GPT1_CLK_ENB, 0, @@ -614,9 +614,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, NULL, "gpt1"); clk = clk_register_mux(NULL, "gpt2_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG, - SPEAR1340_GPT2_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GPT2_CLK_SHIFT, + SPEAR1340_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt2_mclk", NULL); clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk", 0, SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_GPT2_CLK_ENB, 0, @@ -624,9 +624,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, NULL, "gpt2"); clk = clk_register_mux(NULL, "gpt3_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG, - SPEAR1340_GPT3_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GPT3_CLK_SHIFT, + SPEAR1340_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt3_mclk", NULL); clk = clk_register_gate(NULL, "gpt3_clk", "gpt3_mclk", 0, SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_GPT3_CLK_ENB, 0, @@ -641,7 +641,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "uart0_syn_gclk", NULL); clk = clk_register_mux(NULL, "uart0_mclk", uart0_parents, - ARRAY_SIZE(uart0_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart0_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1340_PERIP_CLK_CFG, SPEAR1340_UART0_CLK_SHIFT, SPEAR1340_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart0_mclk", NULL); @@ -658,9 +659,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "uart1_syn_gclk", NULL); clk = clk_register_mux(NULL, "uart1_mclk", uart1_parents, - ARRAY_SIZE(uart1_parents), 0, SPEAR1340_PERIP_CLK_CFG, - SPEAR1340_UART1_CLK_SHIFT, SPEAR1340_UART_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(uart1_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PERIP_CLK_CFG, SPEAR1340_UART1_CLK_SHIFT, + SPEAR1340_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart1_mclk", NULL); clk = clk_register_gate(NULL, "uart1_clk", "uart1_mclk", 0, @@ -698,7 +699,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "c3_syn_gclk", NULL); clk = clk_register_mux(NULL, "c3_mclk", c3_parents, - ARRAY_SIZE(c3_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(c3_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1340_PERIP_CLK_CFG, SPEAR1340_C3_CLK_SHIFT, SPEAR1340_C3_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "c3_mclk", NULL); @@ -710,8 +712,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) /* gmac */ clk = clk_register_mux(NULL, "phy_input_mclk", gmac_phy_input_parents, - ARRAY_SIZE(gmac_phy_input_parents), 0, - SPEAR1340_GMAC_CLK_CFG, + ARRAY_SIZE(gmac_phy_input_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1340_GMAC_CLK_CFG, SPEAR1340_GMAC_PHY_INPUT_CLK_SHIFT, SPEAR1340_GMAC_PHY_INPUT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "phy_input_mclk", NULL); @@ -723,15 +725,16 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "phy_syn_gclk", NULL); clk = clk_register_mux(NULL, "phy_mclk", gmac_phy_parents, - ARRAY_SIZE(gmac_phy_parents), 0, + ARRAY_SIZE(gmac_phy_parents), CLK_SET_RATE_NO_REPARENT, SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GMAC_PHY_CLK_SHIFT, SPEAR1340_GMAC_PHY_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "stmmacphy.0", NULL); /* clcd */ clk = clk_register_mux(NULL, "clcd_syn_mclk", clcd_synth_parents, - ARRAY_SIZE(clcd_synth_parents), 0, - SPEAR1340_CLCD_CLK_SYNT, SPEAR1340_CLCD_SYNT_CLK_SHIFT, + ARRAY_SIZE(clcd_synth_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1340_CLCD_CLK_SYNT, + SPEAR1340_CLCD_SYNT_CLK_SHIFT, SPEAR1340_CLCD_SYNT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "clcd_syn_mclk", NULL); @@ -741,7 +744,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "clcd_syn_clk", NULL); clk = clk_register_mux(NULL, "clcd_pixel_mclk", clcd_pixel_parents, - ARRAY_SIZE(clcd_pixel_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(clcd_pixel_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1340_PERIP_CLK_CFG, SPEAR1340_CLCD_CLK_SHIFT, SPEAR1340_CLCD_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "clcd_pixel_mclk", NULL); @@ -753,9 +757,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) /* i2s */ clk = clk_register_mux(NULL, "i2s_src_mclk", i2s_src_parents, - ARRAY_SIZE(i2s_src_parents), 0, SPEAR1340_I2S_CLK_CFG, - SPEAR1340_I2S_SRC_CLK_SHIFT, SPEAR1340_I2S_SRC_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(i2s_src_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_I2S_CLK_CFG, SPEAR1340_I2S_SRC_CLK_SHIFT, + SPEAR1340_I2S_SRC_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2s_src_mclk", NULL); clk = clk_register_aux("i2s_prs1_clk", NULL, "i2s_src_mclk", @@ -765,7 +769,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "i2s_prs1_clk", NULL); clk = clk_register_mux(NULL, "i2s_ref_mclk", i2s_ref_parents, - ARRAY_SIZE(i2s_ref_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(i2s_ref_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1340_I2S_CLK_CFG, SPEAR1340_I2S_REF_SHIFT, SPEAR1340_I2S_REF_SEL_MASK, 0, &_lock); clk_register_clkdev(clk, "i2s_ref_mclk", NULL); @@ -891,13 +896,15 @@ void __init spear1340_clk_init(void __iomem *misc_base) /* RAS clks */ clk = clk_register_mux(NULL, "gen_syn0_1_mclk", gen_synth0_1_parents, - ARRAY_SIZE(gen_synth0_1_parents), 0, SPEAR1340_PLL_CFG, + ARRAY_SIZE(gen_synth0_1_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1340_PLL_CFG, SPEAR1340_GEN_SYNT0_1_CLK_SHIFT, SPEAR1340_GEN_SYNT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gen_syn0_1_mclk", NULL); clk = clk_register_mux(NULL, "gen_syn2_3_mclk", gen_synth2_3_parents, - ARRAY_SIZE(gen_synth2_3_parents), 0, SPEAR1340_PLL_CFG, + ARRAY_SIZE(gen_synth2_3_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1340_PLL_CFG, SPEAR1340_GEN_SYNT2_3_CLK_SHIFT, SPEAR1340_GEN_SYNT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gen_syn2_3_mclk", NULL); @@ -938,7 +945,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, NULL, "spear_cec.1"); clk = clk_register_mux(NULL, "spdif_out_mclk", spdif_out_parents, - ARRAY_SIZE(spdif_out_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(spdif_out_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1340_PERIP_CLK_CFG, SPEAR1340_SPDIF_OUT_CLK_SHIFT, SPEAR1340_SPDIF_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "spdif_out_mclk", NULL); @@ -949,7 +957,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, NULL, "d0000000.spdif-out"); clk = clk_register_mux(NULL, "spdif_in_mclk", spdif_in_parents, - ARRAY_SIZE(spdif_in_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(spdif_in_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1340_PERIP_CLK_CFG, SPEAR1340_SPDIF_IN_CLK_SHIFT, SPEAR1340_SPDIF_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "spdif_in_mclk", NULL); diff --git a/drivers/clk/spear/spear3xx_clock.c b/drivers/clk/spear/spear3xx_clock.c index 080c3c5e33f6..c2d204315546 100644 --- a/drivers/clk/spear/spear3xx_clock.c +++ b/drivers/clk/spear/spear3xx_clock.c @@ -294,7 +294,8 @@ static void __init spear320_clk_init(void __iomem *soc_config_base) clk_register_clkdev(clk, NULL, "a9400000.i2s"); clk = clk_register_mux(NULL, "i2s_ref_clk", i2s_ref_parents, - ARRAY_SIZE(i2s_ref_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(i2s_ref_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_CONTROL_REG, I2S_REF_PCLK_SHIFT, I2S_REF_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2s_ref_clk", NULL); @@ -313,57 +314,66 @@ static void __init spear320_clk_init(void __iomem *soc_config_base) clk_register_clkdev(clk, "hclk", "ab000000.eth"); clk = clk_register_mux(NULL, "rs485_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_EXT_CTRL_REG, SPEAR320_RS485_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "a9300000.serial"); clk = clk_register_mux(NULL, "sdhci_clk", sdhci_parents, - ARRAY_SIZE(sdhci_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(sdhci_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_CONTROL_REG, SDHCI_PCLK_SHIFT, SDHCI_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "70000000.sdhci"); clk = clk_register_mux(NULL, "smii_pclk", smii0_parents, - ARRAY_SIZE(smii0_parents), 0, SPEAR320_CONTROL_REG, - SMII_PCLK_SHIFT, SMII_PCLK_MASK, 0, &_lock); + ARRAY_SIZE(smii0_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR320_CONTROL_REG, SMII_PCLK_SHIFT, SMII_PCLK_MASK, + 0, &_lock); clk_register_clkdev(clk, NULL, "smii_pclk"); clk = clk_register_fixed_factor(NULL, "smii_clk", "smii_pclk", 0, 1, 1); clk_register_clkdev(clk, NULL, "smii"); clk = clk_register_mux(NULL, "uart1_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_CONTROL_REG, UART1_PCLK_SHIFT, UART1_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "a3000000.serial"); clk = clk_register_mux(NULL, "uart2_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_EXT_CTRL_REG, SPEAR320_UART2_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "a4000000.serial"); clk = clk_register_mux(NULL, "uart3_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_EXT_CTRL_REG, SPEAR320_UART3_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "a9100000.serial"); clk = clk_register_mux(NULL, "uart4_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_EXT_CTRL_REG, SPEAR320_UART4_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "a9200000.serial"); clk = clk_register_mux(NULL, "uart5_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_EXT_CTRL_REG, SPEAR320_UART5_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "60000000.serial"); clk = clk_register_mux(NULL, "uart6_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_EXT_CTRL_REG, SPEAR320_UART6_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "60100000.serial"); @@ -427,7 +437,8 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_ clk_register_clkdev(clk1, "uart_syn_gclk", NULL); clk = clk_register_mux(NULL, "uart0_mclk", uart0_parents, - ARRAY_SIZE(uart0_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart0_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, PERIP_CLK_CFG, UART_CLK_SHIFT, UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart0_mclk", NULL); @@ -444,7 +455,8 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_ clk_register_clkdev(clk1, "firda_syn_gclk", NULL); clk = clk_register_mux(NULL, "firda_mclk", firda_parents, - ARRAY_SIZE(firda_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(firda_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, PERIP_CLK_CFG, FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "firda_mclk", NULL); @@ -458,14 +470,16 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_ clk_register_gpt("gpt0_syn_clk", "pll1_clk", 0, PRSC0_CLK_CFG, gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); clk = clk_register_mux(NULL, "gpt0_clk", gpt0_parents, - ARRAY_SIZE(gpt0_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(gpt0_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, PERIP_CLK_CFG, GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "gpt0"); clk_register_gpt("gpt1_syn_clk", "pll1_clk", 0, PRSC1_CLK_CFG, gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); clk = clk_register_mux(NULL, "gpt1_mclk", gpt1_parents, - ARRAY_SIZE(gpt1_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(gpt1_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, PERIP_CLK_CFG, GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt1_mclk", NULL); clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk", @@ -476,7 +490,8 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_ clk_register_gpt("gpt2_syn_clk", "pll1_clk", 0, PRSC2_CLK_CFG, gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); clk = clk_register_mux(NULL, "gpt2_mclk", gpt2_parents, - ARRAY_SIZE(gpt2_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(gpt2_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, PERIP_CLK_CFG, GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt2_mclk", NULL); clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk", @@ -498,9 +513,9 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_ clk_register_clkdev(clk1, "gen1_syn_gclk", NULL); clk = clk_register_mux(NULL, "gen2_3_par_clk", gen2_3_parents, - ARRAY_SIZE(gen2_3_parents), 0, CORE_CLK_CFG, - GEN_SYNTH2_3_CLK_SHIFT, GEN_SYNTH2_3_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gen2_3_parents), CLK_SET_RATE_NO_REPARENT, + CORE_CLK_CFG, GEN_SYNTH2_3_CLK_SHIFT, + GEN_SYNTH2_3_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gen2_3_par_clk", NULL); clk = clk_register_aux("gen2_syn_clk", "gen2_syn_gclk", @@ -540,8 +555,8 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_ clk_register_clkdev(clk, "ahbmult2_clk", NULL); clk = clk_register_mux(NULL, "ddr_clk", ddr_parents, - ARRAY_SIZE(ddr_parents), 0, PLL_CLK_CFG, MCTR_CLK_SHIFT, - MCTR_CLK_MASK, 0, &_lock); + ARRAY_SIZE(ddr_parents), CLK_SET_RATE_NO_REPARENT, + PLL_CLK_CFG, MCTR_CLK_SHIFT, MCTR_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "ddr_clk", NULL); clk = clk_register_divider(NULL, "apb_clk", "ahb_clk", diff --git a/drivers/clk/spear/spear6xx_clock.c b/drivers/clk/spear/spear6xx_clock.c index 9406f2426d64..4f649c9cb094 100644 --- a/drivers/clk/spear/spear6xx_clock.c +++ b/drivers/clk/spear/spear6xx_clock.c @@ -169,8 +169,9 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "uart_syn_gclk", NULL); clk = clk_register_mux(NULL, "uart_mclk", uart_parents, - ARRAY_SIZE(uart_parents), 0, PERIP_CLK_CFG, - UART_CLK_SHIFT, UART_CLK_MASK, 0, &_lock); + ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, UART_CLK_SHIFT, UART_CLK_MASK, 0, + &_lock); clk_register_clkdev(clk, "uart_mclk", NULL); clk = clk_register_gate(NULL, "uart0", "uart_mclk", 0, PERIP1_CLK_ENB, @@ -188,8 +189,9 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "firda_syn_gclk", NULL); clk = clk_register_mux(NULL, "firda_mclk", firda_parents, - ARRAY_SIZE(firda_parents), 0, PERIP_CLK_CFG, - FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0, &_lock); + ARRAY_SIZE(firda_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0, + &_lock); clk_register_clkdev(clk, "firda_mclk", NULL); clk = clk_register_gate(NULL, "firda_clk", "firda_mclk", 0, @@ -203,8 +205,9 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "clcd_syn_gclk", NULL); clk = clk_register_mux(NULL, "clcd_mclk", clcd_parents, - ARRAY_SIZE(clcd_parents), 0, PERIP_CLK_CFG, - CLCD_CLK_SHIFT, CLCD_CLK_MASK, 0, &_lock); + ARRAY_SIZE(clcd_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, CLCD_CLK_SHIFT, CLCD_CLK_MASK, 0, + &_lock); clk_register_clkdev(clk, "clcd_mclk", NULL); clk = clk_register_gate(NULL, "clcd_clk", "clcd_mclk", 0, @@ -217,13 +220,13 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "gpt0_1_syn_clk", NULL); clk = clk_register_mux(NULL, "gpt0_mclk", gpt0_1_parents, - ARRAY_SIZE(gpt0_1_parents), 0, PERIP_CLK_CFG, - GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + ARRAY_SIZE(gpt0_1_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "gpt0"); clk = clk_register_mux(NULL, "gpt1_mclk", gpt0_1_parents, - ARRAY_SIZE(gpt0_1_parents), 0, PERIP_CLK_CFG, - GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + ARRAY_SIZE(gpt0_1_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt1_mclk", NULL); clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk", 0, @@ -235,8 +238,8 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "gpt2_syn_clk", NULL); clk = clk_register_mux(NULL, "gpt2_mclk", gpt2_parents, - ARRAY_SIZE(gpt2_parents), 0, PERIP_CLK_CFG, - GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + ARRAY_SIZE(gpt2_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt2_mclk", NULL); clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk", 0, @@ -248,8 +251,8 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "gpt3_syn_clk", NULL); clk = clk_register_mux(NULL, "gpt3_mclk", gpt3_parents, - ARRAY_SIZE(gpt3_parents), 0, PERIP_CLK_CFG, - GPT3_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + ARRAY_SIZE(gpt3_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, GPT3_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt3_mclk", NULL); clk = clk_register_gate(NULL, "gpt3_clk", "gpt3_mclk", 0, @@ -277,8 +280,8 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "ahbmult2_clk", NULL); clk = clk_register_mux(NULL, "ddr_clk", ddr_parents, - ARRAY_SIZE(ddr_parents), 0, PLL_CLK_CFG, MCTR_CLK_SHIFT, - MCTR_CLK_MASK, 0, &_lock); + ARRAY_SIZE(ddr_parents), CLK_SET_RATE_NO_REPARENT, + PLL_CLK_CFG, MCTR_CLK_SHIFT, MCTR_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "ddr_clk", NULL); clk = clk_register_divider(NULL, "apb_clk", "ahb_clk", diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index 075db0c99edb..a0eaeb22b4f2 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -778,7 +778,8 @@ static void __init tegra20_audio_clk_init(void) /* audio */ clk = clk_register_mux(NULL, "audio_mux", audio_parents, - ARRAY_SIZE(audio_parents), 0, + ARRAY_SIZE(audio_parents), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "audio", "audio_mux", 0, clk_base + AUDIO_SYNC_CLK, 4, @@ -941,7 +942,8 @@ static void __init tegra20_periph_clk_init(void) /* emc */ clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, - ARRAY_SIZE(mux_pllmcp_clkm), 0, + ARRAY_SIZE(mux_pllmcp_clkm), + CLK_SET_RATE_NO_REPARENT, clk_base + CLK_SOURCE_EMC, 30, 2, 0, NULL); clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0, diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index ba99e3844106..082903e2fe26 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -1008,7 +1008,8 @@ static void __init tegra30_pll_init(void) /* PLLE */ clk = clk_register_mux(NULL, "pll_e_mux", pll_e_parents, - ARRAY_SIZE(pll_e_parents), 0, + ARRAY_SIZE(pll_e_parents), + CLK_SET_RATE_NO_REPARENT, clk_base + PLLE_AUX, 2, 1, 0, NULL); clk = tegra_clk_register_plle("pll_e", "pll_e_mux", clk_base, pmc_base, CLK_GET_RATE_NOCACHE, 100000000, &pll_e_params, @@ -1068,7 +1069,8 @@ static void __init tegra30_audio_clk_init(void) /* audio0 */ clk = clk_register_mux(NULL, "audio0_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S0, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "audio0", "audio0_mux", 0, clk_base + AUDIO_SYNC_CLK_I2S0, 4, @@ -1078,7 +1080,8 @@ static void __init tegra30_audio_clk_init(void) /* audio1 */ clk = clk_register_mux(NULL, "audio1_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S1, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "audio1", "audio1_mux", 0, clk_base + AUDIO_SYNC_CLK_I2S1, 4, @@ -1088,7 +1091,8 @@ static void __init tegra30_audio_clk_init(void) /* audio2 */ clk = clk_register_mux(NULL, "audio2_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S2, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "audio2", "audio2_mux", 0, clk_base + AUDIO_SYNC_CLK_I2S2, 4, @@ -1098,7 +1102,8 @@ static void __init tegra30_audio_clk_init(void) /* audio3 */ clk = clk_register_mux(NULL, "audio3_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S3, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "audio3", "audio3_mux", 0, clk_base + AUDIO_SYNC_CLK_I2S3, 4, @@ -1108,7 +1113,8 @@ static void __init tegra30_audio_clk_init(void) /* audio4 */ clk = clk_register_mux(NULL, "audio4_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S4, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "audio4", "audio4_mux", 0, clk_base + AUDIO_SYNC_CLK_I2S4, 4, @@ -1118,7 +1124,8 @@ static void __init tegra30_audio_clk_init(void) /* spdif */ clk = clk_register_mux(NULL, "spdif_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_SPDIF, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "spdif", "spdif_mux", 0, clk_base + AUDIO_SYNC_CLK_SPDIF, 4, @@ -1211,7 +1218,8 @@ static void __init tegra30_pmc_clk_init(void) /* clk_out_1 */ clk = clk_register_mux(NULL, "clk_out_1_mux", clk_out1_parents, - ARRAY_SIZE(clk_out1_parents), 0, + ARRAY_SIZE(clk_out1_parents), + CLK_SET_RATE_NO_REPARENT, pmc_base + PMC_CLK_OUT_CNTRL, 6, 3, 0, &clk_out_lock); clks[clk_out_1_mux] = clk; @@ -1223,7 +1231,8 @@ static void __init tegra30_pmc_clk_init(void) /* clk_out_2 */ clk = clk_register_mux(NULL, "clk_out_2_mux", clk_out2_parents, - ARRAY_SIZE(clk_out1_parents), 0, + ARRAY_SIZE(clk_out1_parents), + CLK_SET_RATE_NO_REPARENT, pmc_base + PMC_CLK_OUT_CNTRL, 14, 3, 0, &clk_out_lock); clk = clk_register_gate(NULL, "clk_out_2", "clk_out_2_mux", 0, @@ -1234,7 +1243,8 @@ static void __init tegra30_pmc_clk_init(void) /* clk_out_3 */ clk = clk_register_mux(NULL, "clk_out_3_mux", clk_out3_parents, - ARRAY_SIZE(clk_out1_parents), 0, + ARRAY_SIZE(clk_out1_parents), + CLK_SET_RATE_NO_REPARENT, pmc_base + PMC_CLK_OUT_CNTRL, 22, 3, 0, &clk_out_lock); clk = clk_register_gate(NULL, "clk_out_3", "clk_out_3_mux", 0, @@ -1661,7 +1671,8 @@ static void __init tegra30_periph_clk_init(void) /* emc */ clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, - ARRAY_SIZE(mux_pllmcp_clkm), 0, + ARRAY_SIZE(mux_pllmcp_clkm), + CLK_SET_RATE_NO_REPARENT, clk_base + CLK_SOURCE_EMC, 30, 2, 0, NULL); clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0, diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index e9924898043a..6f286897359c 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -312,6 +312,15 @@ config MMP_PDMA help Support the MMP PDMA engine for PXA and MMP platfrom. +config K3_DMA + tristate "Hisilicon K3 DMA support" + depends on ARCH_HS + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + help + Support the DMA engine for Hisilicon K3 platform + devices. + config DMA_ENGINE bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index a2b0df591f95..3b05b152a4c8 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -38,3 +38,4 @@ obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o obj-$(CONFIG_DMA_OMAP) += omap-dma.o obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o +obj-$(CONFIG_K3_DMA) += k3dma.o diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 8bad254a498d..089d6a7b4732 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -83,6 +83,7 @@ #include <linux/pm_runtime.h> #include <linux/seq_file.h> #include <linux/slab.h> +#include <linux/of.h> #include <linux/amba/pl080.h> #include "dmaengine.h" @@ -1718,8 +1719,10 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, chan->signal = -1; if (slave) { - chan->cd = &pl08x->pd->slave_channels[i]; - pl08x_dma_slave_init(chan); + if (pl08x->pd->slave_channels) { + chan->cd = &pl08x->pd->slave_channels[i]; + pl08x_dma_slave_init(chan); + } } else { chan->cd = &pl08x->pd->memcpy_channel; chan->name = kasprintf(GFP_KERNEL, "memcpy%d", i); @@ -1841,6 +1844,25 @@ static inline void init_pl08x_debugfs(struct pl08x_driver_data *pl08x) { } #endif +static struct pl08x_platform_data *pl08x_get_pdata(struct device *dev) +{ + struct pl08x_platform_data *pd; + struct device_node *np = dev->of_node; + + pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); + if (!pd) + return NULL; + of_property_read_u32(np, "dma-requests", &pd->num_slave_channels); + + pd->memcpy_channel.cctl_memcpy = + (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | + PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT | + PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT | + PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT | + PL080_CONTROL_PROT_BUFF | PL080_CONTROL_PROT_CACHE | + PL080_CONTROL_PROT_SYS); + return pd; +} static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) { @@ -1883,7 +1905,10 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) pl08x->slave.device_control = pl08x_control; /* Get the platform data */ - pl08x->pd = dev_get_platdata(&adev->dev); + if ((&adev->dev)->of_node) + pl08x->pd = pl08x_get_pdata(&adev->dev); + else + pl08x->pd = dev_get_platdata(&adev->dev); if (!pl08x->pd) { dev_err(&adev->dev, "no platform data supplied\n"); ret = -EINVAL; diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 93f7992bee5c..78dbbe09d7b1 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -504,6 +504,32 @@ static struct dma_chan *private_candidate(const dma_cap_mask_t *mask, } /** + * dma_request_channel - try to get specific channel exclusively + * @chan: target channel + */ +struct dma_chan *dma_get_slave_channel(struct dma_chan *chan) +{ + int err = -EBUSY; + + /* lock against __dma_request_channel */ + mutex_lock(&dma_list_mutex); + + if (chan->client_count == 0) + err = dma_chan_get(chan); + else + chan = NULL; + + mutex_unlock(&dma_list_mutex); + + if (err) + pr_debug("%s: failed to get %s: (%d)\n", + __func__, dma_chan_name(chan), err); + + return chan; +} +EXPORT_SYMBOL_GPL(dma_get_slave_channel); + +/** * dma_request_channel - try to allocate an exclusive channel * @mask: capabilities that the channel must satisfy * @fn: optional callback to disposition available channels diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c new file mode 100644 index 000000000000..1ba08bb093e7 --- /dev/null +++ b/drivers/dma/k3dma.c @@ -0,0 +1,849 @@ +/* + * 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 + * published by the Free Software Foundation. + */ +#include <linux/sched.h> +#include <linux/device.h> +#include <linux/dmaengine.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/clk.h> +#include <linux/of_dma.h> + +#include "virt-dma.h" + +#define DRIVER_NAME "k3-dma" +#define DMA_ALIGN 3 +#define DMA_MAX_SIZE 0x1ffc + +#define INT_STAT 0x00 +#define INT_TC1 0x04 +#define INT_ERR1 0x0c +#define INT_ERR2 0x10 +#define INT_TC1_MASK 0x18 +#define INT_ERR1_MASK 0x20 +#define INT_ERR2_MASK 0x24 +#define INT_TC1_RAW 0x600 +#define INT_ERR1_RAW 0x608 +#define INT_ERR2_RAW 0x610 +#define CH_PRI 0x688 +#define CH_STAT 0x690 +#define CX_CUR_CNT 0x704 +#define CX_LLI 0x800 +#define CX_CNT 0x810 +#define CX_SRC 0x814 +#define CX_DST 0x818 +#define CX_CONFIG 0x81c +#define AXI_CONFIG 0x820 +#define DEF_AXI_CONFIG 0x201201 + +#define CX_LLI_CHAIN_EN 0x2 +#define CCFG_EN 0x1 +#define CCFG_MEM2PER (0x1 << 2) +#define CCFG_PER2MEM (0x2 << 2) +#define CCFG_SRCINCR (0x1 << 31) +#define CCFG_DSTINCR (0x1 << 30) + +struct k3_desc_hw { + u32 lli; + u32 reserved[3]; + u32 count; + u32 saddr; + u32 daddr; + u32 config; +} __aligned(32); + +struct k3_dma_desc_sw { + struct virt_dma_desc vd; + dma_addr_t desc_hw_lli; + size_t desc_num; + size_t size; + struct k3_desc_hw desc_hw[0]; +}; + +struct k3_dma_phy; + +struct k3_dma_chan { + u32 ccfg; + struct virt_dma_chan vc; + struct k3_dma_phy *phy; + struct list_head node; + enum dma_transfer_direction dir; + dma_addr_t dev_addr; + enum dma_status status; +}; + +struct k3_dma_phy { + u32 idx; + void __iomem *base; + struct k3_dma_chan *vchan; + struct k3_dma_desc_sw *ds_run; + struct k3_dma_desc_sw *ds_done; +}; + +struct k3_dma_dev { + struct dma_device slave; + void __iomem *base; + struct tasklet_struct task; + spinlock_t lock; + struct list_head chan_pending; + struct k3_dma_phy *phy; + struct k3_dma_chan *chans; + struct clk *clk; + u32 dma_channels; + u32 dma_requests; +}; + +#define to_k3_dma(dmadev) container_of(dmadev, struct k3_dma_dev, slave) + +static struct k3_dma_chan *to_k3_chan(struct dma_chan *chan) +{ + return container_of(chan, struct k3_dma_chan, vc.chan); +} + +static void k3_dma_pause_dma(struct k3_dma_phy *phy, bool on) +{ + u32 val = 0; + + if (on) { + val = readl_relaxed(phy->base + CX_CONFIG); + val |= CCFG_EN; + writel_relaxed(val, phy->base + CX_CONFIG); + } else { + val = readl_relaxed(phy->base + CX_CONFIG); + val &= ~CCFG_EN; + writel_relaxed(val, phy->base + CX_CONFIG); + } +} + +static void k3_dma_terminate_chan(struct k3_dma_phy *phy, struct k3_dma_dev *d) +{ + u32 val = 0; + + k3_dma_pause_dma(phy, false); + + val = 0x1 << phy->idx; + writel_relaxed(val, d->base + INT_TC1_RAW); + writel_relaxed(val, d->base + INT_ERR1_RAW); + writel_relaxed(val, d->base + INT_ERR2_RAW); +} + +static void k3_dma_set_desc(struct k3_dma_phy *phy, struct k3_desc_hw *hw) +{ + writel_relaxed(hw->lli, phy->base + CX_LLI); + writel_relaxed(hw->count, phy->base + CX_CNT); + writel_relaxed(hw->saddr, phy->base + CX_SRC); + writel_relaxed(hw->daddr, phy->base + CX_DST); + writel_relaxed(DEF_AXI_CONFIG, phy->base + AXI_CONFIG); + writel_relaxed(hw->config, phy->base + CX_CONFIG); +} + +static u32 k3_dma_get_curr_cnt(struct k3_dma_dev *d, struct k3_dma_phy *phy) +{ + u32 cnt = 0; + + cnt = readl_relaxed(d->base + CX_CUR_CNT + phy->idx * 0x10); + cnt &= 0xffff; + return cnt; +} + +static u32 k3_dma_get_curr_lli(struct k3_dma_phy *phy) +{ + return readl_relaxed(phy->base + CX_LLI); +} + +static u32 k3_dma_get_chan_stat(struct k3_dma_dev *d) +{ + return readl_relaxed(d->base + CH_STAT); +} + +static void k3_dma_enable_dma(struct k3_dma_dev *d, bool on) +{ + if (on) { + /* set same priority */ + writel_relaxed(0x0, d->base + CH_PRI); + + /* unmask irq */ + writel_relaxed(0xffff, d->base + INT_TC1_MASK); + writel_relaxed(0xffff, d->base + INT_ERR1_MASK); + writel_relaxed(0xffff, d->base + INT_ERR2_MASK); + } else { + /* mask irq */ + writel_relaxed(0x0, d->base + INT_TC1_MASK); + writel_relaxed(0x0, d->base + INT_ERR1_MASK); + writel_relaxed(0x0, d->base + INT_ERR2_MASK); + } +} + +static irqreturn_t k3_dma_int_handler(int irq, void *dev_id) +{ + struct k3_dma_dev *d = (struct k3_dma_dev *)dev_id; + struct k3_dma_phy *p; + struct k3_dma_chan *c; + u32 stat = readl_relaxed(d->base + INT_STAT); + u32 tc1 = readl_relaxed(d->base + INT_TC1); + u32 err1 = readl_relaxed(d->base + INT_ERR1); + u32 err2 = readl_relaxed(d->base + INT_ERR2); + u32 i, irq_chan = 0; + + while (stat) { + i = __ffs(stat); + stat &= (stat - 1); + if (likely(tc1 & BIT(i))) { + p = &d->phy[i]; + c = p->vchan; + if (c) { + unsigned long flags; + + spin_lock_irqsave(&c->vc.lock, flags); + vchan_cookie_complete(&p->ds_run->vd); + p->ds_done = p->ds_run; + spin_unlock_irqrestore(&c->vc.lock, flags); + } + irq_chan |= BIT(i); + } + if (unlikely((err1 & BIT(i)) || (err2 & BIT(i)))) + dev_warn(d->slave.dev, "DMA ERR\n"); + } + + writel_relaxed(irq_chan, d->base + INT_TC1_RAW); + writel_relaxed(err1, d->base + INT_ERR1_RAW); + writel_relaxed(err2, d->base + INT_ERR2_RAW); + + if (irq_chan) { + tasklet_schedule(&d->task); + return IRQ_HANDLED; + } else + return IRQ_NONE; +} + +static int k3_dma_start_txd(struct k3_dma_chan *c) +{ + struct k3_dma_dev *d = to_k3_dma(c->vc.chan.device); + struct virt_dma_desc *vd = vchan_next_desc(&c->vc); + + if (!c->phy) + return -EAGAIN; + + if (BIT(c->phy->idx) & k3_dma_get_chan_stat(d)) + return -EAGAIN; + + if (vd) { + struct k3_dma_desc_sw *ds = + container_of(vd, struct k3_dma_desc_sw, vd); + /* + * fetch and remove request from vc->desc_issued + * so vc->desc_issued only contains desc pending + */ + list_del(&ds->vd.node); + c->phy->ds_run = ds; + c->phy->ds_done = NULL; + /* start dma */ + k3_dma_set_desc(c->phy, &ds->desc_hw[0]); + return 0; + } + c->phy->ds_done = NULL; + c->phy->ds_run = NULL; + return -EAGAIN; +} + +static void k3_dma_tasklet(unsigned long arg) +{ + struct k3_dma_dev *d = (struct k3_dma_dev *)arg; + struct k3_dma_phy *p; + struct k3_dma_chan *c, *cn; + unsigned pch, pch_alloc = 0; + + /* check new dma request of running channel in vc->desc_issued */ + list_for_each_entry_safe(c, cn, &d->slave.channels, vc.chan.device_node) { + spin_lock_irq(&c->vc.lock); + p = c->phy; + if (p && p->ds_done) { + if (k3_dma_start_txd(c)) { + /* No current txd associated with this channel */ + dev_dbg(d->slave.dev, "pchan %u: free\n", p->idx); + /* Mark this channel free */ + c->phy = NULL; + p->vchan = NULL; + } + } + spin_unlock_irq(&c->vc.lock); + } + + /* check new channel request in d->chan_pending */ + spin_lock_irq(&d->lock); + for (pch = 0; pch < d->dma_channels; pch++) { + p = &d->phy[pch]; + + if (p->vchan == NULL && !list_empty(&d->chan_pending)) { + c = list_first_entry(&d->chan_pending, + struct k3_dma_chan, node); + /* remove from d->chan_pending */ + list_del_init(&c->node); + pch_alloc |= 1 << pch; + /* Mark this channel allocated */ + p->vchan = c; + c->phy = p; + dev_dbg(d->slave.dev, "pchan %u: alloc vchan %p\n", pch, &c->vc); + } + } + spin_unlock_irq(&d->lock); + + for (pch = 0; pch < d->dma_channels; pch++) { + if (pch_alloc & (1 << pch)) { + p = &d->phy[pch]; + c = p->vchan; + if (c) { + spin_lock_irq(&c->vc.lock); + k3_dma_start_txd(c); + spin_unlock_irq(&c->vc.lock); + } + } + } +} + +static int k3_dma_alloc_chan_resources(struct dma_chan *chan) +{ + return 0; +} + +static void k3_dma_free_chan_resources(struct dma_chan *chan) +{ + struct k3_dma_chan *c = to_k3_chan(chan); + struct k3_dma_dev *d = to_k3_dma(chan->device); + unsigned long flags; + + spin_lock_irqsave(&d->lock, flags); + list_del_init(&c->node); + spin_unlock_irqrestore(&d->lock, flags); + + vchan_free_chan_resources(&c->vc); + c->ccfg = 0; +} + +static enum dma_status k3_dma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *state) +{ + struct k3_dma_chan *c = to_k3_chan(chan); + struct k3_dma_dev *d = to_k3_dma(chan->device); + struct k3_dma_phy *p; + struct virt_dma_desc *vd; + unsigned long flags; + enum dma_status ret; + size_t bytes = 0; + + ret = dma_cookie_status(&c->vc.chan, cookie, state); + if (ret == DMA_SUCCESS) + return ret; + + spin_lock_irqsave(&c->vc.lock, flags); + p = c->phy; + ret = c->status; + + /* + * If the cookie is on our issue queue, then the residue is + * its total size. + */ + vd = vchan_find_desc(&c->vc, cookie); + if (vd) { + bytes = container_of(vd, struct k3_dma_desc_sw, vd)->size; + } else if ((!p) || (!p->ds_run)) { + bytes = 0; + } else { + struct k3_dma_desc_sw *ds = p->ds_run; + u32 clli = 0, index = 0; + + bytes = k3_dma_get_curr_cnt(d, p); + clli = k3_dma_get_curr_lli(p); + index = (clli - ds->desc_hw_lli) / sizeof(struct k3_desc_hw); + for (; index < ds->desc_num; index++) { + bytes += ds->desc_hw[index].count; + /* end of lli */ + if (!ds->desc_hw[index].lli) + break; + } + } + spin_unlock_irqrestore(&c->vc.lock, flags); + dma_set_residue(state, bytes); + return ret; +} + +static void k3_dma_issue_pending(struct dma_chan *chan) +{ + struct k3_dma_chan *c = to_k3_chan(chan); + struct k3_dma_dev *d = to_k3_dma(chan->device); + unsigned long flags; + + spin_lock_irqsave(&c->vc.lock, flags); + /* add request to vc->desc_issued */ + if (vchan_issue_pending(&c->vc)) { + spin_lock(&d->lock); + if (!c->phy) { + if (list_empty(&c->node)) { + /* if new channel, add chan_pending */ + list_add_tail(&c->node, &d->chan_pending); + /* check in tasklet */ + tasklet_schedule(&d->task); + dev_dbg(d->slave.dev, "vchan %p: issued\n", &c->vc); + } + } + spin_unlock(&d->lock); + } else + dev_dbg(d->slave.dev, "vchan %p: nothing to issue\n", &c->vc); + spin_unlock_irqrestore(&c->vc.lock, flags); +} + +static void k3_dma_fill_desc(struct k3_dma_desc_sw *ds, dma_addr_t dst, + dma_addr_t src, size_t len, u32 num, u32 ccfg) +{ + if ((num + 1) < ds->desc_num) + ds->desc_hw[num].lli = ds->desc_hw_lli + (num + 1) * + sizeof(struct k3_desc_hw); + ds->desc_hw[num].lli |= CX_LLI_CHAIN_EN; + ds->desc_hw[num].count = len; + ds->desc_hw[num].saddr = src; + ds->desc_hw[num].daddr = dst; + ds->desc_hw[num].config = ccfg; +} + +static struct dma_async_tx_descriptor *k3_dma_prep_memcpy( + struct dma_chan *chan, dma_addr_t dst, dma_addr_t src, + size_t len, unsigned long flags) +{ + struct k3_dma_chan *c = to_k3_chan(chan); + struct k3_dma_desc_sw *ds; + size_t copy = 0; + int num = 0; + + if (!len) + return NULL; + + num = DIV_ROUND_UP(len, DMA_MAX_SIZE); + ds = kzalloc(sizeof(*ds) + num * sizeof(ds->desc_hw[0]), GFP_ATOMIC); + if (!ds) { + dev_dbg(chan->device->dev, "vchan %p: kzalloc fail\n", &c->vc); + return NULL; + } + ds->desc_hw_lli = __virt_to_phys((unsigned long)&ds->desc_hw[0]); + ds->size = len; + ds->desc_num = num; + num = 0; + + if (!c->ccfg) { + /* default is memtomem, without calling device_control */ + c->ccfg = CCFG_SRCINCR | CCFG_DSTINCR | CCFG_EN; + c->ccfg |= (0xf << 20) | (0xf << 24); /* burst = 16 */ + c->ccfg |= (0x3 << 12) | (0x3 << 16); /* width = 64 bit */ + } + + do { + copy = min_t(size_t, len, DMA_MAX_SIZE); + k3_dma_fill_desc(ds, dst, src, copy, num++, c->ccfg); + + if (c->dir == DMA_MEM_TO_DEV) { + src += copy; + } else if (c->dir == DMA_DEV_TO_MEM) { + dst += copy; + } else { + src += copy; + dst += copy; + } + len -= copy; + } while (len); + + ds->desc_hw[num-1].lli = 0; /* end of link */ + return vchan_tx_prep(&c->vc, &ds->vd, flags); +} + +static struct dma_async_tx_descriptor *k3_dma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sgl, unsigned int sglen, + enum dma_transfer_direction dir, unsigned long flags, void *context) +{ + struct k3_dma_chan *c = to_k3_chan(chan); + struct k3_dma_desc_sw *ds; + size_t len, avail, total = 0; + struct scatterlist *sg; + dma_addr_t addr, src = 0, dst = 0; + int num = sglen, i; + + if (sgl == 0) + return NULL; + + for_each_sg(sgl, sg, sglen, i) { + avail = sg_dma_len(sg); + if (avail > DMA_MAX_SIZE) + num += DIV_ROUND_UP(avail, DMA_MAX_SIZE) - 1; + } + + ds = kzalloc(sizeof(*ds) + num * sizeof(ds->desc_hw[0]), GFP_ATOMIC); + if (!ds) { + dev_dbg(chan->device->dev, "vchan %p: kzalloc fail\n", &c->vc); + return NULL; + } + ds->desc_hw_lli = __virt_to_phys((unsigned long)&ds->desc_hw[0]); + ds->desc_num = num; + num = 0; + + for_each_sg(sgl, sg, sglen, i) { + addr = sg_dma_address(sg); + avail = sg_dma_len(sg); + total += avail; + + do { + len = min_t(size_t, avail, DMA_MAX_SIZE); + + if (dir == DMA_MEM_TO_DEV) { + src = addr; + dst = c->dev_addr; + } else if (dir == DMA_DEV_TO_MEM) { + src = c->dev_addr; + dst = addr; + } + + k3_dma_fill_desc(ds, dst, src, len, num++, c->ccfg); + + addr += len; + avail -= len; + } while (avail); + } + + ds->desc_hw[num-1].lli = 0; /* end of link */ + ds->size = total; + return vchan_tx_prep(&c->vc, &ds->vd, flags); +} + +static int k3_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct k3_dma_chan *c = to_k3_chan(chan); + struct k3_dma_dev *d = to_k3_dma(chan->device); + struct dma_slave_config *cfg = (void *)arg; + struct k3_dma_phy *p = NULL; + unsigned long flags; + u32 maxburst = 0, val = 0; + enum dma_slave_buswidth width = DMA_SLAVE_BUSWIDTH_UNDEFINED; + LIST_HEAD(head); + + if (c) + p = c->phy; + + switch (cmd) { + case DMA_SLAVE_CONFIG: + if (cfg == NULL) + return -EINVAL; + c->dir = cfg->direction; + if (c->dir == DMA_DEV_TO_MEM) { + c->ccfg = CCFG_DSTINCR; + c->dev_addr = cfg->src_addr; + maxburst = cfg->src_maxburst; + width = cfg->src_addr_width; + } else if (c->dir == DMA_MEM_TO_DEV) { + c->ccfg = CCFG_SRCINCR; + c->dev_addr = cfg->dst_addr; + maxburst = cfg->dst_maxburst; + width = cfg->dst_addr_width; + } + switch (width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + val = 0; + break; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + val = 1; + break; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + val = 2; + break; + case DMA_SLAVE_BUSWIDTH_8_BYTES: + val = 3; + break; + default: + break; + } + c->ccfg |= (val << 12) | (val << 16); + + if ((maxburst == 0) || (maxburst > 16)) + val = 16; + else + val = maxburst - 1; + c->ccfg |= (val << 20) | (val << 24); + c->ccfg |= CCFG_MEM2PER | CCFG_EN; + + /* specific request line */ + c->ccfg |= c->vc.chan.chan_id << 4; + break; + + case DMA_TERMINATE_ALL: + dev_dbg(d->slave.dev, "vchan %p: terminate all\n", &c->vc); + + /* Prevent this channel being scheduled */ + spin_lock(&d->lock); + list_del_init(&c->node); + spin_unlock(&d->lock); + + /* Clear the tx descriptor lists */ + spin_lock_irqsave(&c->vc.lock, flags); + vchan_get_all_descriptors(&c->vc, &head); + if (p) { + /* vchan is assigned to a pchan - stop the channel */ + k3_dma_terminate_chan(p, d); + c->phy = NULL; + p->vchan = NULL; + p->ds_run = p->ds_done = NULL; + } + spin_unlock_irqrestore(&c->vc.lock, flags); + vchan_dma_desc_free_list(&c->vc, &head); + break; + + case DMA_PAUSE: + dev_dbg(d->slave.dev, "vchan %p: pause\n", &c->vc); + if (c->status == DMA_IN_PROGRESS) { + c->status = DMA_PAUSED; + if (p) { + k3_dma_pause_dma(p, false); + } else { + spin_lock(&d->lock); + list_del_init(&c->node); + spin_unlock(&d->lock); + } + } + break; + + case DMA_RESUME: + dev_dbg(d->slave.dev, "vchan %p: resume\n", &c->vc); + spin_lock_irqsave(&c->vc.lock, flags); + if (c->status == DMA_PAUSED) { + c->status = DMA_IN_PROGRESS; + if (p) { + k3_dma_pause_dma(p, true); + } else if (!list_empty(&c->vc.desc_issued)) { + spin_lock(&d->lock); + list_add_tail(&c->node, &d->chan_pending); + spin_unlock(&d->lock); + } + } + spin_unlock_irqrestore(&c->vc.lock, flags); + break; + default: + return -ENXIO; + } + return 0; +} + +static void k3_dma_free_desc(struct virt_dma_desc *vd) +{ + struct k3_dma_desc_sw *ds = + container_of(vd, struct k3_dma_desc_sw, vd); + + kfree(ds); +} + +static struct of_device_id k3_pdma_dt_ids[] = { + { .compatible = "hisilicon,k3-dma-1.0", }, + {} +}; +MODULE_DEVICE_TABLE(of, k3_pdma_dt_ids); + +static struct dma_chan *k3_of_dma_simple_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct k3_dma_dev *d = ofdma->of_dma_data; + unsigned int request = dma_spec->args[0]; + + if (request > d->dma_requests) + return NULL; + + return dma_get_slave_channel(&(d->chans[request].vc.chan)); +} + +static int k3_dma_probe(struct platform_device *op) +{ + struct k3_dma_dev *d; + const struct of_device_id *of_id; + struct resource *iores; + int i, ret, irq = 0; + + iores = platform_get_resource(op, IORESOURCE_MEM, 0); + if (!iores) + return -EINVAL; + + d = devm_kzalloc(&op->dev, sizeof(*d), GFP_KERNEL); + if (!d) + return -ENOMEM; + + d->base = devm_request_and_ioremap(&op->dev, iores); + if (!d->base) + return -EADDRNOTAVAIL; + + of_id = of_match_device(k3_pdma_dt_ids, &op->dev); + if (of_id) { + of_property_read_u32((&op->dev)->of_node, + "dma-channels", &d->dma_channels); + of_property_read_u32((&op->dev)->of_node, + "dma-requests", &d->dma_requests); + } + + d->clk = devm_clk_get(&op->dev, NULL); + if (IS_ERR(d->clk)) { + dev_err(&op->dev, "no dma clk\n"); + return PTR_ERR(d->clk); + } + + irq = platform_get_irq(op, 0); + ret = devm_request_irq(&op->dev, irq, + k3_dma_int_handler, IRQF_DISABLED, DRIVER_NAME, d); + if (ret) + return ret; + + /* init phy channel */ + d->phy = devm_kzalloc(&op->dev, + d->dma_channels * sizeof(struct k3_dma_phy), GFP_KERNEL); + if (d->phy == NULL) + return -ENOMEM; + + for (i = 0; i < d->dma_channels; i++) { + struct k3_dma_phy *p = &d->phy[i]; + + p->idx = i; + p->base = d->base + i * 0x40; + } + + INIT_LIST_HEAD(&d->slave.channels); + dma_cap_set(DMA_SLAVE, d->slave.cap_mask); + dma_cap_set(DMA_MEMCPY, d->slave.cap_mask); + d->slave.dev = &op->dev; + d->slave.device_alloc_chan_resources = k3_dma_alloc_chan_resources; + d->slave.device_free_chan_resources = k3_dma_free_chan_resources; + d->slave.device_tx_status = k3_dma_tx_status; + d->slave.device_prep_dma_memcpy = k3_dma_prep_memcpy; + d->slave.device_prep_slave_sg = k3_dma_prep_slave_sg; + d->slave.device_issue_pending = k3_dma_issue_pending; + d->slave.device_control = k3_dma_control; + d->slave.copy_align = DMA_ALIGN; + d->slave.chancnt = d->dma_requests; + + /* init virtual channel */ + d->chans = devm_kzalloc(&op->dev, + d->dma_requests * sizeof(struct k3_dma_chan), GFP_KERNEL); + if (d->chans == NULL) + return -ENOMEM; + + for (i = 0; i < d->dma_requests; i++) { + struct k3_dma_chan *c = &d->chans[i]; + + c->status = DMA_IN_PROGRESS; + INIT_LIST_HEAD(&c->node); + c->vc.desc_free = k3_dma_free_desc; + vchan_init(&c->vc, &d->slave); + } + + /* Enable clock before accessing registers */ + ret = clk_prepare_enable(d->clk); + if (ret < 0) { + dev_err(&op->dev, "clk_prepare_enable failed: %d\n", ret); + return -EINVAL; + } + + k3_dma_enable_dma(d, true); + + ret = dma_async_device_register(&d->slave); + if (ret) + return ret; + + ret = of_dma_controller_register((&op->dev)->of_node, + k3_of_dma_simple_xlate, d); + if (ret) + goto of_dma_register_fail; + + spin_lock_init(&d->lock); + INIT_LIST_HEAD(&d->chan_pending); + tasklet_init(&d->task, k3_dma_tasklet, (unsigned long)d); + platform_set_drvdata(op, d); + dev_info(&op->dev, "initialized\n"); + + return 0; + +of_dma_register_fail: + dma_async_device_unregister(&d->slave); + return ret; +} + +static int k3_dma_remove(struct platform_device *op) +{ + struct k3_dma_chan *c, *cn; + struct k3_dma_dev *d = platform_get_drvdata(op); + + dma_async_device_unregister(&d->slave); + of_dma_controller_free((&op->dev)->of_node); + + list_for_each_entry_safe(c, cn, &d->slave.channels, vc.chan.device_node) { + list_del(&c->vc.chan.device_node); + tasklet_kill(&c->vc.task); + } + tasklet_kill(&d->task); + clk_disable_unprepare(d->clk); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int k3_dma_pltfm_suspend(struct device *dev) +{ + struct k3_dma_dev *d = dev_get_drvdata(dev); + u32 stat = 0; + + stat = k3_dma_get_chan_stat(d); + if (stat) { + dev_warn(d->slave.dev, + "chan %d is running fail to suspend\n", stat); + return -1; + } + k3_dma_enable_dma(d, false); + clk_disable_unprepare(d->clk); + return 0; +} + +static int k3_dma_pltfm_resume(struct device *dev) +{ + struct k3_dma_dev *d = dev_get_drvdata(dev); + int ret = 0; + + ret = clk_prepare_enable(d->clk); + if (ret < 0) { + dev_err(d->slave.dev, "clk_prepare_enable failed: %d\n", ret); + return -EINVAL; + } + k3_dma_enable_dma(d, true); + return 0; +} +#else +#define k3_dma_pltfm_suspend NULL +#define k3_dma_pltfm_resume NULL +#endif /* CONFIG_PM_SLEEP */ + +SIMPLE_DEV_PM_OPS(k3_dma_pltfm_pmops, k3_dma_pltfm_suspend, k3_dma_pltfm_resume); + +static struct platform_driver k3_pdma_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .pm = &k3_dma_pltfm_pmops, + .of_match_table = k3_pdma_dt_ids, + }, + .probe = k3_dma_probe, + .remove = k3_dma_remove, +}; + +module_platform_driver(k3_pdma_driver); + +MODULE_DESCRIPTION("Hisilicon k3 DMA Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 4fbe12d283d5..207e41675241 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -256,6 +256,19 @@ static const struct irq_domain_ops pl061_domain_ops = { .xlate = irq_domain_xlate_twocell, }; +/* Parse gpio base from DT */ +static int pl061_parse_gpio_base(struct device *dev) +{ + struct device_node *np = dev->of_node; + int ret; + + if (of_property_read_u32(np, "linux,gpio-base", &ret)) + return -ENOENT; + if (ret >= 0) + return ret; + return -EINVAL; +} + static int pl061_probe(struct amba_device *adev, const struct amba_id *id) { struct device *dev = &adev->dev; @@ -273,7 +286,7 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id) if (irq_base <= 0) return -ENODEV; } else { - chip->gc.base = -1; + chip->gc.base = pl061_parse_gpio_base(dev); irq_base = 0; } @@ -288,8 +301,11 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id) spin_lock_init(&chip->lock); - chip->gc.request = pl061_gpio_request; - chip->gc.free = pl061_gpio_free; + /* Hook the request()/free() for pinctrl operation */ + if (of_get_property(dev->of_node, "gpio-ranges", NULL)) { + chip->gc.request = pl061_gpio_request; + chip->gc.free = pl061_gpio_free; + } chip->gc.direction_input = pl061_direction_input; chip->gc.direction_output = pl061_direction_output; chip->gc.get = pl061_get_value; diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c2534d62911c..ff0fd655729f 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1214,15 +1214,14 @@ int gpiochip_add(struct gpio_chip *chip) } } + spin_unlock_irqrestore(&gpio_lock, flags); + #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&chip->pin_ranges); #endif of_gpiochip_add(chip); -unlock: - spin_unlock_irqrestore(&gpio_lock, flags); - if (status) goto fail; @@ -1235,6 +1234,9 @@ unlock: chip->label ? : "generic"); return 0; + +unlock: + spin_unlock_irqrestore(&gpio_lock, flags); fail: /* failures here can mean systems won't boot... */ pr_err("gpiochip_add: gpios %d..%d (%s) failed to register\n", diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 35b70a1edf57..41659c0f25b7 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -231,6 +231,7 @@ static SIMPLE_DEV_PM_OPS(dw_i2c_dev_pm_ops, dw_i2c_suspend, dw_i2c_resume); MODULE_ALIAS("platform:i2c_designware"); static struct platform_driver dw_i2c_driver = { + .probe = dw_i2c_probe, .remove = dw_i2c_remove, .driver = { .name = "i2c_designware", @@ -240,18 +241,7 @@ static struct platform_driver dw_i2c_driver = { .pm = &dw_i2c_dev_pm_ops, }, }; - -static int __init dw_i2c_init_driver(void) -{ - return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe); -} -subsys_initcall(dw_i2c_init_driver); - -static void __exit dw_i2c_exit_driver(void) -{ - platform_driver_unregister(&dw_i2c_driver); -} -module_exit(dw_i2c_exit_driver); +module_platform_driver(dw_i2c_driver); MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter"); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 4abf046e30b1..c1116f14ad4f 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -93,6 +93,15 @@ config INPUT_BMA150 To compile this driver as a module, choose M here: the module will be called bma150. +config INPUT_HI6421_ONKEY + tristate "Hisilicon Hi6421 PMIC ONKEY support" + depends on MFD_HI6421_PMIC + help + Say Y to enable support for Hi6421 PMIC ONKEY. + + To compile this driver as a module, choose M here: the + module will be called hi6421_pwrkey. + config INPUT_PCSPKR tristate "PC Speaker support" depends on PCSPKR_PLATFORM diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 6b0e8a677725..5978a0ce64f8 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o obj-$(CONFIG_INPUT_GPIO) += gpio_event.o gpio_matrix.o gpio_input.o gpio_output.o gpio_axis.o +obj-$(CONFIG_INPUT_HI6421_ONKEY) += hi6421_pwrkey.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o diff --git a/drivers/input/misc/hi6421_pwrkey.c b/drivers/input/misc/hi6421_pwrkey.c new file mode 100644 index 000000000000..31d0145fc688 --- /dev/null +++ b/drivers/input/misc/hi6421_pwrkey.c @@ -0,0 +1,153 @@ +/* + * hi6421_pwrkey.c - Hisilicon Hi6421 PMIC ONKEY driver + * + * Copyright (C) 2013 Hisilicon Ltd. + * Copyright (C) 2013 Linaro Ltd. + * Author: Haojian Zhuang <haojian.zhuang@linaro.org> + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/mfd/hi6421-pmic.h> + +struct hi6421_onkey_info { + struct input_dev *idev; + int irq[4]; +}; + +static irqreturn_t hi6421_onkey_handler(int irq, void *data) +{ + struct hi6421_onkey_info *info = (struct hi6421_onkey_info *)data; + + /* only handle power down & power up event at here */ + if (irq == info->irq[0]) { + input_report_key(info->idev, KEY_POWER, 1); + input_sync(info->idev); + } else if (irq == info->irq[1]) { + input_report_key(info->idev, KEY_POWER, 0); + input_sync(info->idev); + } + return IRQ_HANDLED; +} + +static int hi6421_onkey_probe(struct platform_device *pdev) +{ + struct hi6421_onkey_info *info; + struct device *dev = &pdev->dev; + int ret; + + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->idev = input_allocate_device(); + if (!info->idev) { + dev_err(&pdev->dev, "Failed to allocate input device\n"); + return -ENOMEM; + } + info->idev->name = "hi6421_on"; + info->idev->phys = "hi6421_on/input0"; + info->idev->dev.parent = &pdev->dev; + info->idev->evbit[0] = BIT_MASK(EV_KEY); + __set_bit(KEY_POWER, info->idev->keybit); + + info->irq[0] = platform_get_irq_byname(pdev, "down"); + if (info->irq[0] < 0) { + ret = -ENOENT; + goto err_irq0; + } + ret = request_irq(info->irq[0], hi6421_onkey_handler, + IRQF_DISABLED, "down", info); + if (ret < 0) + goto err_irq0; + info->irq[1] = platform_get_irq_byname(pdev, "up"); + if (info->irq[1] < 0) { + ret = -ENOENT; + goto err_irq1; + } + ret = request_irq(info->irq[1], hi6421_onkey_handler, + IRQF_DISABLED, "up", info); + if (ret < 0) + goto err_irq1; + info->irq[2] = platform_get_irq_byname(pdev, "hold 1s"); + if (info->irq[2] < 0) { + ret = -ENOENT; + goto err_irq2; + } + ret = request_irq(info->irq[2], hi6421_onkey_handler, + IRQF_DISABLED, "hold 1s", info); + if (ret < 0) + goto err_irq2; + info->irq[3] = platform_get_irq_byname(pdev, "hold 10s"); + if (info->irq[3] < 0) { + ret = -ENOENT; + goto err_irq3; + } + ret = request_irq(info->irq[3], hi6421_onkey_handler, + IRQF_DISABLED, "hold 10s", info); + if (ret < 0) + goto err_irq3; + + ret = input_register_device(info->idev); + if (ret) { + dev_err(&pdev->dev, "Can't register input device: %d\n", ret); + goto err_reg; + } + + platform_set_drvdata(pdev, info); + return ret; +err_reg: + free_irq(info->irq[3], info); +err_irq3: + free_irq(info->irq[2], info); +err_irq2: + free_irq(info->irq[1], info); +err_irq1: + free_irq(info->irq[0], info); +err_irq0: + input_free_device(info->idev); + return ret; +} + +static int hi6421_onkey_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct of_device_id hi6421_onkey_of_match[] = { + { .compatible = "hisilicon,hi6421-onkey", }, + { }, +}; +MODULE_DEVICE_TABLE(of, hi6421_onkey_of_match); + +static struct platform_driver hi6421_onkey_driver = { + .probe = hi6421_onkey_probe, + .remove = hi6421_onkey_remove, + .driver = { + .owner = THIS_MODULE, + .name = "hi6421-onkey", + .of_match_table = hi6421_onkey_of_match, + }, +}; +module_platform_driver(hi6421_onkey_driver); + +MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@linaro.org"); +MODULE_DESCRIPTION("Hi6421 PMIC Power key driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index f9a5fd89bc02..dee8f0e0ec68 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -888,4 +888,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 6bfbeab67c9f..a00b61bcb740 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -72,3 +72,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 a5e54f0d6a73..5767b22a139c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -442,6 +442,12 @@ config MFD_PM8XXX_IRQ This is required to use certain other PM 8xxx features, such as GPIO and MPP. +config MFD_R63306 + tristate "Support R63306 Graphics Liquid Crystal Controller" + select MFD_CORE + help + This supports for Renesas R63306 Graphics Liquid Crystal Controller. + config MFD_RDC321X tristate "RDC R-321x southbridge" select MFD_CORE @@ -1116,6 +1122,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 3a0120315aa3..f3b3b48f97c4 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -142,6 +142,7 @@ obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o +obj-$(CONFIG_MFD_R63306) += r63306.o obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o obj-$(CONFIG_MFD_TPS65090) += tps65090.o obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o @@ -156,3 +157,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..1ef6d4e16172 --- /dev/null +++ b/drivers/mfd/hi6421-pmic-core.c @@ -0,0 +1,310 @@ +/* + * 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_find_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 i, 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; + + for (i = 0; i < HI6421_NR_IRQ; i++) { + ret = irq_create_mapping(pmic->domain, i); + if (ret == NO_IRQ) { + dev_err(dev, "failed mapping hwirq %d\n", i); + return -ENOMEM; + } + } + + 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/mfd/r63306.c b/drivers/mfd/r63306.c new file mode 100644 index 000000000000..2bd0f4ad97bc --- /dev/null +++ b/drivers/mfd/r63306.c @@ -0,0 +1,327 @@ +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/mfd/core.h> +#include <linux/mfd/r63306.h> + +#define RENESAS_ID_VENDOR 0x0122 + +#define DSI_CMD_LEN 32 + +enum DATA_TYPE { + GEN_SHORT_WR_PARAM0 = 0x03, + GEN_SHORT_WR_PARAM1 = 0x13, + GEN_SHORT_WR_PARAM2 = 0x23, + GEN_RD_PARAM0 = 0x04, + GEN_RD_PARAM1 = 0x14, + GEN_RD_PARAM2 = 0x24, + DCS_SHORT_WR_PARAM0 = 0x05, + DCS_SHORT_WR_PARAM1 = 0x15, + DCS_RD_PARAM0 = 0x06, + SET_MAX_PKT = 0x37, + NULL_PKT = 0x09, + BLANKING_PKT = 0x19, + GEN_LONG_WR = 0x29, + DCS_LONG_WR = 0x39, + PACKED_PIXEL_16B = 0x0e, + PACKED_PIXEL_18B = 0x1e, + LOOSELY_PACKED_PIXEL_18B = 0x2e, + PACKED_PIXEL_24B = 0x3e, +}; + +/* 1st byte is DT type, 2nd byte is command, others are parameters. */ +static u8 soft_reset[] = {DCS_SHORT_WR_PARAM0, 0x01}; +static u8 get_power_mode[] = {DCS_RD_PARAM0, 0x0a}; +static u8 get_address_mode[] = {DCS_RD_PARAM0, 0x0b}; +static u8 get_display_mode[] = {DCS_RD_PARAM0, 0x0d}; +static u8 get_signal_mode[] = {DCS_RD_PARAM0, 0x0e}; +static u8 enter_sleep_mode[] = {DCS_SHORT_WR_PARAM0, 0x10}; +static u8 exit_sleep_mode[] = {DCS_SHORT_WR_PARAM0, 0x11}; +static u8 exit_invert_mode[] = {DCS_SHORT_WR_PARAM0, 0x20}; +static u8 enter_invert_mode[] = {DCS_SHORT_WR_PARAM0, 0x21}; +static u8 set_display_off[] = {DCS_SHORT_WR_PARAM0, 0x28}; +static u8 set_display_on[] = {DCS_SHORT_WR_PARAM0, 0x29}; +static u8 set_address_mode[] = {DCS_SHORT_WR_PARAM1, 0x36, 0x0}; +static u8 read_dsi_ctrl[] = {GEN_RD_PARAM1, 0xb6}; +static u8 write_dsi_ctrl[] = {GEN_LONG_WR, 0xb6}; +static u8 read_pfm_pwm_ctrl[] = {GEN_RD_PARAM1, 0xb9}; +static u8 write_pfm_pwm_ctrl[] = {GEN_LONG_WR, 0xb9}; +static u8 read_backlight[] = {GEN_RD_PARAM1, 0xba}; +static u8 read_cabc_ctrl[] = {GEN_RD_PARAM1, 0xbb}; +static u8 write_cabc_ctrl[] = {GEN_LONG_WR, 0xbb}; +static u8 read_cabc_param[] = {GEN_RD_PARAM1, 0xbe}; +static u8 write_cabc_param[] = {GEN_LONG_WR, 0xbe}; +static u8 read_device_code[] = {GEN_RD_PARAM1, 0xbf}; + +static struct mfd_cell pwm_devs[] = { + { + .name = "r63306-pwm", + .id = -1, + .of_compatible = "renesas,r63306-pwm", + }, +}; + +extern int dsi_set_packet(u8 *cmd, int nr_payload); + +int dsi_reset(void) +{ + u8 cmd[DSI_CMD_LEN]; + + memset(cmd, 0, DSI_CMD_LEN); + memcpy(cmd, soft_reset, sizeof(soft_reset)); + return dsi_set_packet(cmd, 1); +} +EXPORT_SYMBOL(dsi_reset); + +int dsi_enter_sleep(void) +{ + u8 cmd[DSI_CMD_LEN]; + + memset(cmd, 0, DSI_CMD_LEN); + memcpy(cmd, enter_sleep_mode, sizeof(enter_sleep_mode)); + return dsi_set_packet(cmd, 1); +} +EXPORT_SYMBOL(dsi_enter_sleep); + +int dsi_exit_sleep(void) +{ + u8 cmd[DSI_CMD_LEN]; + + memset(cmd, 0, DSI_CMD_LEN); + memcpy(cmd, exit_sleep_mode, sizeof(exit_sleep_mode)); + return dsi_set_packet(cmd, 1); +} +EXPORT_SYMBOL(dsi_exit_sleep); + +int dsi_get_id(struct r63306_device_id *id) +{ + u8 cmd[DSI_CMD_LEN]; + int ret; + +pr_err("#%s, %d\n", __func__, __LINE__); + memset(cmd, 0, DSI_CMD_LEN); + memcpy(cmd, read_device_code, sizeof(read_device_code)); + ret = dsi_set_packet(cmd, 5); + if (ret) + return ret; + id->vendor = cmd[2] << 8 | cmd[3]; + id->product = cmd[4] << 8 | cmd[5]; + id->revision = cmd[6]; + return 0; +} +EXPORT_SYMBOL(dsi_get_id); + +int dsi_get_backlight(int *level) +{ + u8 cmd[DSI_CMD_LEN]; + int ret; + + memset(cmd, 0, DSI_CMD_LEN); + memcpy(cmd, read_backlight, sizeof(read_backlight)); + ret = dsi_set_packet(cmd, 2); + if (ret) + return ret; + pr_err("#%s, %d, backlight:0x%x 0x%x\n", __func__, __LINE__, cmd[2], cmd[3]); + return 0; +} + +int dsi_get_cabc(struct r63306_cabc *cabc) +{ + u8 cmd[DSI_CMD_LEN]; + int ret; + + memset(cmd, 0, DSI_CMD_LEN); + memcpy(cmd, read_cabc_ctrl, sizeof(read_cabc_ctrl)); + ret = dsi_set_packet(cmd, 1); + if (ret) + return ret; + cabc->cabc_on = (cmd[2] & (1 << 0)) ? 1 : 0; + cabc->pwm_on = (cmd[2] & (1 << 1)) ? 1 : 0; + cabc->pfm_on = (cmd[2] & (1 << 2)) ? 1 : 0; + cabc->ledpwm_pin = (cmd[2] & (1 << 3)) ? 1 : 0; + cabc->ledpwm_pol = (cmd[2] & (1 << 4)) ? 1 : 0; + pr_err("#%s, %d, cabc:%d, pwm_on:%d, pfm_on:%d, ledpwm_pin:%d, ledpwm_pol:%d\n", + __func__, __LINE__, cabc->cabc_on, cabc->pwm_on, cabc->pfm_on, cabc->ledpwm_pin, cabc->ledpwm_pol); + return 0; +} +EXPORT_SYMBOL(dsi_get_cabc); + +int dsi_set_cabc(struct r63306_cabc *cabc) +{ + u8 cmd[DSI_CMD_LEN]; + + memset(cmd, 0, DSI_CMD_LEN); + memcpy(cmd, write_cabc_ctrl, sizeof(write_cabc_ctrl)); + cmd[2] = (cabc->cabc_on << 0) | (cabc->pwm_on << 1) | + (cabc->pfm_on << 2) | (cabc->ledpwm_pin << 3) | + (cabc->ledpwm_pol << 4); + return dsi_set_packet(cmd, 1 + 1); +} +EXPORT_SYMBOL(dsi_set_cabc); + +int dsi_get_cabc_param(struct r63306_cabc_param *cabc) +{ + u8 cmd[DSI_CMD_LEN]; + int ret; + + memset(cmd, 0, DSI_CMD_LEN); + memcpy(cmd, read_cabc_param, sizeof(read_cabc_param)); + ret = dsi_set_packet(cmd, 8); + if (ret < 0) + return ret; +#if 0 + cabc->backlight = ((cmd[2] & 0xf) << 4) | (cmd[3] & 0xf); +#else + cabc->backlight = (cmd[2] << 8) | (cmd[3] & 0xf); +#endif + pr_err("#%s, %d, [2]:0x%x, [3]:0x%x, [4]:0x%x\n", + __func__, __LINE__, cmd[2], cmd[3], cmd[4]); + return 0; +} +EXPORT_SYMBOL(dsi_get_cabc_param); + +int dsi_set_cabc_param(struct r63306_cabc_param *cabc) +{ + u8 cmd[DSI_CMD_LEN]; + + memset(cmd, 0, DSI_CMD_LEN); + memcpy(cmd, write_cabc_param, sizeof(write_cabc_param)); +/* +#if 0 + cmd[2] = (cabc->backlight >> 4) & 0xf; + cmd[3] = cabc->backlight & 0xf; + cmd[4] = 0x02; + cmd[5] = 0x02; + cmd[6] = 0x04; + cmd[7] = 0x04; + cmd[8] = 0x00; + cmd[9] = 0x5d; +#else + cmd[2] = (cabc->backlight & 0xff0) >> 4; + cmd[3] = cabc->backlight & 0xf; +#endif +*/ + cmd[2] = cabc->backlight; + cmd[3] = cabc->backlight & 0xf; + return dsi_set_packet(cmd, 2 + 1); +} + +int dsi_get_pwm(struct r63306_pwm *pwm) +{ + u8 cmd[DSI_CMD_LEN]; + int ret; + int i; + + memset(cmd, 0, DSI_CMD_LEN); + memcpy(cmd, read_pfm_pwm_ctrl, sizeof(read_pfm_pwm_ctrl)); + ret = dsi_set_packet(cmd, 12); + if (ret) + return ret; + pwm->divider = cmd[2]; + pwm->duty_cycle = ((cmd[3] << 8) | cmd[4]) & 0x1ff; + pwm->pwm_wm = (cmd[3] & 0x10) ? 1 : 0; +#if 1 + pr_err("#%s, %d, divider:%d, duty_cycle:%d, pwm_wm:%d\n", + __func__, __LINE__, pwm->divider, pwm->duty_cycle, pwm->pwm_wm); + for (i = 0; i < 12; i++) { + pr_err("c[%d]:0x%x ", i, cmd[i]); + } + pr_err("\n"); +#endif + return 0; +} +EXPORT_SYMBOL(dsi_get_pwm); + +int dsi_set_pwm(struct r63306_pwm *pwm) +{ +#if 1 + u8 cmd[DSI_CMD_LEN]; + + memset(cmd, 0, DSI_CMD_LEN); + memcpy(cmd, write_pfm_pwm_ctrl, sizeof(write_pfm_pwm_ctrl)); + cmd[2] = pwm->divider; + cmd[3] = ((pwm->duty_cycle & 0x100) >> 8) | (pwm->pwm_wm << 4); + cmd[4] = pwm->duty_cycle & 0xff; + //cmd[4] = 0x0e; + return dsi_set_packet(cmd, 12 + 1); +#else + u8 cmd[DSI_CMD_LEN]; + int ret; + + memset(cmd, 0, DSI_CMD_LEN); + memcpy(cmd, read_pfm_pwm_ctrl, sizeof(read_pfm_pwm_ctrl)); + ret = dsi_set_packet(cmd, 12); + if (ret) + return ret; + cmd[0] = write_pfm_pwm_ctrl[0]; + cmd[2] = pwm->divider; + cmd[3] = ((pwm->duty_cycle & 0x100) >> 8) | (pwm->pwm_wm << 4); + cmd[3] |= (1 << 4); + cmd[4] = pwm->duty_cycle & 0xff; + //cmd[4] = 0x0e; + cmd[5] = 0x73; + return dsi_set_packet(cmd, 12 + 1); +#endif +} +EXPORT_SYMBOL(dsi_set_pwm); + +static int r63306_probe(struct platform_device *pdev) +{ + struct r63306_chip *chip; + struct r63306_device_id id; + struct r63306_cabc cabc; + int ret; + + if (dsi_get_id(&id) < 0 || id.vendor != RENESAS_ID_VENDOR) { + dev_err(&pdev->dev, "Can't detect Renesas chip\n"); + return -EPROBE_DEFER; + } + + /* set pwm on */ + ret = dsi_get_cabc(&cabc); + if (ret < 0) + return -EPROBE_DEFER; + //cabc.pwm_on = true; + ret = dsi_set_cabc(&cabc); + if (ret < 0) + return -EPROBE_DEFER; + chip = kzalloc(sizeof(struct r63306_chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + chip->dev = &pdev->dev; + + ret = mfd_add_devices(chip->dev, 0, pwm_devs, ARRAY_SIZE(pwm_devs), + NULL, 0, NULL); + if (ret) + goto err; + return 0; +err: + kfree(chip); + return ret; +} + +static int r63306_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id r63306_of_match[] = { + { .compatible = "renesas,r63306", }, + {}, +}; +MODULE_DEVICE_TABLE(of, r63306_of_match); + +static struct platform_driver r63306_driver = { + .probe = r63306_probe, + .remove = r63306_remove, + .driver = { + .name = "r63306", + .owner = THIS_MODULE, + .of_match_table = r63306_of_match, + }, +}; +module_platform_driver(r63306_driver); diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 4f98f8b38395..41e8f8770ea2 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -34,6 +34,7 @@ #include <linux/delay.h> #include <linux/capability.h> #include <linux/compat.h> +#include <linux/pm_runtime.h> #define CREATE_TRACE_POINTS #include <trace/events/mmc.h> @@ -221,7 +222,7 @@ static ssize_t power_ro_lock_store(struct device *dev, md = mmc_blk_get(dev_to_disk(dev)); card = md->queue.card; - mmc_claim_host(card->host); + mmc_get_card(card); ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_WP, card->ext_csd.boot_ro_lock | @@ -232,7 +233,7 @@ static ssize_t power_ro_lock_store(struct device *dev, else card->ext_csd.boot_ro_lock |= EXT_CSD_BOOT_WP_B_PWR_WP_EN; - mmc_release_host(card->host); + mmc_put_card(card); if (!ret) { pr_info("%s: Locking boot partition ro until next power on\n", @@ -490,7 +491,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, mrq.cmd = &cmd; - mmc_claim_host(card->host); + mmc_get_card(card); err = mmc_blk_part_switch(card, md); if (err) @@ -557,7 +558,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, } cmd_rel_host: - mmc_release_host(card->host); + mmc_put_card(card); cmd_done: mmc_blk_put(md); @@ -1945,7 +1946,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) if (req && !mq->mqrq_prev->req) /* claim host only for the first request */ - mmc_claim_host(card->host); + mmc_get_card(card); ret = mmc_blk_part_switch(card, md); if (ret) { @@ -1989,7 +1990,7 @@ out: * In case sepecial request, there is no reentry to * the 'mmc_blk_issue_rq' with 'mqrq_prev->req'. */ - mmc_release_host(card->host); + mmc_put_card(card); return ret; } @@ -2390,6 +2391,19 @@ static int mmc_blk_probe(struct mmc_card *card) if (mmc_add_disk(part_md)) goto out; } + + pm_runtime_set_autosuspend_delay(&card->dev, 3000); + pm_runtime_use_autosuspend(&card->dev); + + /* + * Don't enable runtime PM for SD-combo cards here. Leave that + * decision to be taken during the SDIO init sequence instead. + */ + if (card->type != MMC_TYPE_SD_COMBO) { + pm_runtime_set_active(&card->dev); + pm_runtime_enable(&card->dev); + } + return 0; out: @@ -2403,9 +2417,13 @@ static void mmc_blk_remove(struct mmc_card *card) struct mmc_blk_data *md = mmc_get_drvdata(card); mmc_blk_remove_parts(card, md); + pm_runtime_get_sync(&card->dev); mmc_claim_host(card->host); mmc_blk_part_switch(card, md); mmc_release_host(card->host); + if (card->type != MMC_TYPE_SD_COMBO) + pm_runtime_disable(&card->dev); + pm_runtime_put_noidle(&card->dev); mmc_blk_remove_req(md); mmc_set_drvdata(card, NULL); #ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME @@ -2420,6 +2438,7 @@ static int mmc_blk_suspend(struct mmc_card *card) struct mmc_blk_data *md = mmc_get_drvdata(card); if (md) { + pm_runtime_get_sync(&card->dev); mmc_queue_suspend(&md->queue); list_for_each_entry(part_md, &md->part, part) { mmc_queue_suspend(&part_md->queue); @@ -2443,6 +2462,7 @@ static int mmc_blk_resume(struct mmc_card *card) list_for_each_entry(part_md, &md->part, part) { mmc_queue_resume(&part_md->queue); } + pm_runtime_put(&card->dev); } return 0; } diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index e219c97a02a4..d9e8c2b7f4c5 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -151,15 +151,25 @@ static int mmc_bus_resume(struct device *dev) static int mmc_runtime_suspend(struct device *dev) { struct mmc_card *card = mmc_dev_to_card(dev); + struct mmc_host *host = card->host; + int ret = 0; + + if (host->bus_ops->runtime_suspend) + ret = host->bus_ops->runtime_suspend(host); - return mmc_power_save_host(card->host); + return ret; } static int mmc_runtime_resume(struct device *dev) { struct mmc_card *card = mmc_dev_to_card(dev); + struct mmc_host *host = card->host; + int ret = 0; + + if (host->bus_ops->runtime_resume) + ret = host->bus_ops->runtime_resume(host); - return mmc_power_restore_host(card->host); + return ret; } static int mmc_runtime_idle(struct device *dev) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 6a83f4ccc108..8fc40d004974 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -960,6 +960,29 @@ void mmc_release_host(struct mmc_host *host) EXPORT_SYMBOL(mmc_release_host); /* + * This is a helper function, which fetches a runtime pm reference for the + * card device and also claims the host. + */ +void mmc_get_card(struct mmc_card *card) +{ + pm_runtime_get_sync(&card->dev); + mmc_claim_host(card->host); +} +EXPORT_SYMBOL(mmc_get_card); + +/* + * This is a helper function, which releases the host and drops the runtime + * pm reference for the card device. + */ +void mmc_put_card(struct mmc_card *card) +{ + mmc_release_host(card->host); + pm_runtime_mark_last_busy(&card->dev); + pm_runtime_put_autosuspend(&card->dev); +} +EXPORT_SYMBOL(mmc_put_card); + +/* * Internal function that does the actual ios call to the host driver, * optionally printing some debug output. */ @@ -1467,7 +1490,7 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type) * If a host does all the power sequencing itself, ignore the * initial MMC_POWER_UP stage. */ -static void mmc_power_up(struct mmc_host *host) +void mmc_power_up(struct mmc_host *host) { int bit; @@ -2700,14 +2723,8 @@ int mmc_suspend_host(struct mmc_host *host) mmc_bus_get(host); if (host->bus_ops && !host->bus_dead) { - if (host->bus_ops->suspend) { - if (mmc_card_doing_bkops(host->card)) { - err = mmc_stop_bkops(host->card); - if (err) - goto out; - } + if (host->bus_ops->suspend) err = host->bus_ops->suspend(host); - } if (err == -ENOSYS || !host->bus_ops->resume) { /* @@ -2731,10 +2748,8 @@ int mmc_suspend_host(struct mmc_host *host) if (!err && !mmc_card_keep_power(host)) mmc_power_off(host); -out: return err; } - EXPORT_SYMBOL(mmc_suspend_host); /** @@ -2795,22 +2810,10 @@ int mmc_pm_notify(struct notifier_block *notify_block, struct mmc_host *host = container_of( notify_block, struct mmc_host, pm_notify); unsigned long flags; - int err = 0; switch (mode) { case PM_HIBERNATION_PREPARE: case PM_SUSPEND_PREPARE: - if (host->card && mmc_card_mmc(host->card) && - mmc_card_doing_bkops(host->card)) { - err = mmc_stop_bkops(host->card); - if (err) { - pr_err("%s: didn't stop bkops\n", - mmc_hostname(host)); - return err; - } - mmc_card_clr_doing_bkops(host->card); - } - spin_lock_irqsave(&host->lock, flags); if (mmc_bus_needs_resume(host)) { spin_unlock_irqrestore(&host->lock, flags); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index b9f18a2a8874..6242ffb789c4 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -22,6 +22,8 @@ struct mmc_bus_ops { void (*detect)(struct mmc_host *); int (*suspend)(struct mmc_host *); int (*resume)(struct mmc_host *); + int (*runtime_suspend)(struct mmc_host *); + int (*runtime_resume)(struct mmc_host *); int (*power_save)(struct mmc_host *); int (*power_restore)(struct mmc_host *); int (*alive)(struct mmc_host *); @@ -44,6 +46,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage); int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage); void mmc_set_timing(struct mmc_host *host, unsigned int timing); void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type); +void mmc_power_up(struct mmc_host *host); void mmc_power_off(struct mmc_host *host); void mmc_power_cycle(struct mmc_host *host); diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 35c2f85b1956..54829c0ed000 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -258,13 +258,13 @@ static int mmc_dbg_card_status_get(void *data, u64 *val) u32 status; int ret; - mmc_claim_host(card->host); + mmc_get_card(card); ret = mmc_send_status(data, &status); if (!ret) *val = status; - mmc_release_host(card->host); + mmc_put_card(card); return ret; } @@ -291,9 +291,9 @@ static int mmc_ext_csd_open(struct inode *inode, struct file *filp) goto out_free; } - mmc_claim_host(card->host); + mmc_get_card(card); err = mmc_send_ext_csd(card, ext_csd); - mmc_release_host(card->host); + mmc_put_card(card); if (err) goto out_free; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 0cbd1effe960..506f4ee84e12 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1380,14 +1380,14 @@ static void mmc_detect(struct mmc_host *host) BUG_ON(!host); BUG_ON(!host->card); - mmc_claim_host(host); + mmc_get_card(host->card); /* * Just check if our card has been removed. */ err = _mmc_detect_card_removed(host); - mmc_release_host(host); + mmc_put_card(host->card); if (err) { mmc_remove(host); @@ -1411,6 +1411,12 @@ static int mmc_suspend(struct mmc_host *host) mmc_claim_host(host); + if (mmc_card_doing_bkops(host->card)) { + err = mmc_stop_bkops(host->card); + if (err) + goto out; + } + err = mmc_cache_ctrl(host, 0); if (err) goto out; @@ -1448,6 +1454,54 @@ static int mmc_resume(struct mmc_host *host) return err; } + +/* + * Callback for runtime_suspend. + */ +static int mmc_runtime_suspend(struct mmc_host *host) +{ + int err; + + if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) + return 0; + + mmc_claim_host(host); + + err = mmc_suspend(host); + if (err) { + pr_err("%s: error %d doing aggessive suspend\n", + mmc_hostname(host), err); + goto out; + } + mmc_power_off(host); + +out: + mmc_release_host(host); + return err; +} + +/* + * Callback for runtime_resume. + */ +static int mmc_runtime_resume(struct mmc_host *host) +{ + int err; + + if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) + return 0; + + mmc_claim_host(host); + + mmc_power_up(host); + err = mmc_resume(host); + if (err) + pr_err("%s: error %d doing aggessive resume\n", + mmc_hostname(host), err); + + mmc_release_host(host); + return 0; +} + static int mmc_power_restore(struct mmc_host *host) { int ret; @@ -1508,6 +1562,8 @@ static const struct mmc_bus_ops mmc_ops_unsafe = { .detect = mmc_detect, .suspend = mmc_suspend, .resume = mmc_resume, + .runtime_suspend = mmc_runtime_suspend, + .runtime_resume = mmc_runtime_resume, .power_restore = mmc_power_restore, .alive = mmc_alive, }; diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index f008318c5c4d..09fc0f26236e 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1062,7 +1062,7 @@ static void mmc_sd_detect(struct mmc_host *host) BUG_ON(!host); BUG_ON(!host->card); - mmc_claim_host(host); + mmc_get_card(host->card); /* * Just check if our card has been removed. @@ -1085,7 +1085,7 @@ static void mmc_sd_detect(struct mmc_host *host) err = _mmc_detect_card_removed(host); #endif - mmc_release_host(host); + mmc_put_card(host->card); if (err) { mmc_sd_remove(host); @@ -1155,6 +1155,53 @@ static int mmc_sd_resume(struct mmc_host *host) return err; } +/* + * Callback for runtime_suspend. + */ +static int mmc_sd_runtime_suspend(struct mmc_host *host) +{ + int err; + + if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) + return 0; + + mmc_claim_host(host); + + err = mmc_sd_suspend(host); + if (err) { + pr_err("%s: error %d doing aggessive suspend\n", + mmc_hostname(host), err); + goto out; + } + mmc_power_off(host); + +out: + mmc_release_host(host); + return err; +} + +/* + * Callback for runtime_resume. + */ +static int mmc_sd_runtime_resume(struct mmc_host *host) +{ + int err; + + if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) + return 0; + + mmc_claim_host(host); + + mmc_power_up(host); + err = mmc_sd_resume(host); + if (err) + pr_err("%s: error %d doing aggessive resume\n", + mmc_hostname(host), err); + + mmc_release_host(host); + return 0; +} + static int mmc_sd_power_restore(struct mmc_host *host) { int ret; @@ -1179,6 +1226,8 @@ static const struct mmc_bus_ops mmc_sd_ops = { static const struct mmc_bus_ops mmc_sd_ops_unsafe = { .remove = mmc_sd_remove, .detect = mmc_sd_detect, + .runtime_suspend = mmc_sd_runtime_suspend, + .runtime_resume = mmc_sd_runtime_resume, .suspend = mmc_sd_suspend, .resume = mmc_sd_resume, .power_restore = mmc_sd_power_restore, diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 46e68f125ff2..33831c4b2f2b 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1072,11 +1072,27 @@ out: return ret; } +static int mmc_sdio_runtime_suspend(struct mmc_host *host) +{ + /* No references to the card, cut the power to it. */ + mmc_power_off(host); + return 0; +} + +static int mmc_sdio_runtime_resume(struct mmc_host *host) +{ + /* Restore power and re-initialize. */ + mmc_power_up(host); + return mmc_sdio_power_restore(host); +} + static const struct mmc_bus_ops mmc_sdio_ops = { .remove = mmc_sdio_remove, .detect = mmc_sdio_detect, .suspend = mmc_sdio_suspend, .resume = mmc_sdio_resume, + .runtime_suspend = mmc_sdio_runtime_suspend, + .runtime_resume = mmc_sdio_runtime_resume, .power_restore = mmc_sdio_power_restore, .alive = mmc_sdio_alive, }; diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 9ab8f8dee942..9652fd646b37 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -556,6 +556,16 @@ config MMC_DW_EXYNOS Synopsys DesignWare Memory Card Interface driver. Select this option for platforms based on Exynos4 and Exynos5 SoC's. +config MMC_DW_HISILICON + tristate "Hisilicon 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 Samsung Exynos SoC specific extensions to the + Synopsys DesignWare Memory Card Interface driver. Select this option + for platforms based on Exynos4 and Exynos5 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 cd3228075553..38c50c00d236 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o 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_HISILICON) += dw_mmc-hisilicon.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-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index f013e7e3746b..939302ee5182 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -191,7 +191,7 @@ static int dw_mci_exynos_probe(struct platform_device *pdev) static struct platform_driver dw_mci_exynos_pltfm_driver = { .probe = dw_mci_exynos_probe, - .remove = __exit_p(dw_mci_pltfm_remove), + .remove = dw_mci_pltfm_remove, .driver = { .name = "dwmmc_exynos", .of_match_table = dw_mci_exynos_match, diff --git a/drivers/mmc/host/dw_mmc-hisilicon.c b/drivers/mmc/host/dw_mmc-hisilicon.c new file mode 100644 index 000000000000..50ba8eb81f2b --- /dev/null +++ b/drivers/mmc/host/dw_mmc-hisilicon.c @@ -0,0 +1,395 @@ +/* + * 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/pinctrl/consumer.h> +#include <linux/of_address.h> + +#include "dw_mmc.h" +#include "dw_mmc-pltfm.h" + +#define DRIVER_NAME "dwmmc_hs" + +enum dw_mci_hisilicon_type { + DW_MCI_TYPE_HI4511, + DW_MCI_TYPE_HI3620, +}; + +static struct dw_mci_exynos_compatible { + char *compatible; + enum dw_mci_hisilicon_type type; +} hs_compat[] = { + { + .compatible = "hisilicon,hi4511-dw-mshc", + .type = DW_MCI_TYPE_HI4511, + }, { + .compatible = "hisilicon,hi3620-dw-mshc", + .type = DW_MCI_TYPE_HI3620, + }, +}; + +struct dw_mci_hs_priv_data { + int id; + int old_timing; + int gpio_cd; + enum dw_mci_hisilicon_type type; +}; + +static void __iomem *pctrl; +static DEFINE_SPINLOCK(mmc_tuning_lock); +static int hs_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_hs_set_timing(int idx, int sam, int drv, int div) +{ + unsigned int clken_reg; + unsigned int clken_bit; + unsigned int sam_sel_reg; + unsigned int sam_sel_bit; + unsigned int drv_sel_reg; + unsigned int drv_sel_bit; + unsigned int div_reg; + unsigned int div_bit; + int i = 0; + unsigned int temp_reg; + unsigned long flags; + + switch (idx) { + case 0: + clken_reg = 0x1F8; + clken_bit = 0; + drv_sel_reg = 0x1F8; + drv_sel_bit = 4; + sam_sel_reg = 0x1F8; + sam_sel_bit = 8; + div_reg = 0x1F8; + div_bit = 1; + break; + case 1: + clken_reg = 0x1F8; + clken_bit = 12; + drv_sel_reg = 0x1F8; + drv_sel_bit = 16; + sam_sel_reg = 0x1F8; + sam_sel_bit = 20; + div_reg = 0x1F8; + div_bit = 13; + break; + case 2: + clken_reg = 0x1F8; + clken_bit = 24; + drv_sel_reg = 0x1F8; + drv_sel_bit = 28; + sam_sel_reg = 0x1FC; + sam_sel_bit = 0; + div_reg = 0x1F8; + div_bit = 25; + break; + case 3: + clken_reg = 0x1FC; + clken_bit = 4; + drv_sel_reg = 0x1FC; + drv_sel_bit = 8; + sam_sel_reg = 0x1FC; + sam_sel_bit = 12; + div_reg = 0x1FC; + div_bit = 5; + break; + default: + return; + } + + 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 + drv_sel_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 + drv_sel_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_hs_tun(struct dw_mci *host, int id, int index) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + int ret; + + if (!pctrl) + return; + + if (priv->old_timing == index) + return; + + ret = clk_set_rate(host->ciu_clk, hs_tuning_config[id][index][0]); + if (ret) + dev_err(host->dev, "clk_set_rate failed\n"); + + dw_mci_hs_set_timing(id, + (hs_tuning_config[id][index][3] + + hs_tuning_config[id][index][4]) / 2, + hs_tuning_config[id][index][2], + hs_tuning_config[id][index][1]); + + host->bus_hz = hs_tuning_config[id][index][5]; + priv->old_timing = index; +} + +static void dw_mci_hs_set_ios(struct dw_mci *host, struct mmc_ios *ios) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + int id = priv->id; + + if (priv->type == DW_MCI_TYPE_HI4511) + dw_mci_hs_tun(host, id, ios->timing); +} + +static int dw_mci_hs_priv_init(struct dw_mci *host) +{ + struct dw_mci_hs_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(hs_compat); i++) { + if (of_device_is_compatible(host->dev->of_node, + hs_compat[i].compatible)) + priv->type = hs_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_hs_setup_clock(struct dw_mci *host) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + + if (priv->type == DW_MCI_TYPE_HI4511) + dw_mci_hs_tun(host, priv->id, 0); + + return 0; +} + + +static irqreturn_t dw_mci_hs_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_hs_get_cd(struct dw_mci *host, u32 slot_id) +{ + unsigned int status; + struct dw_mci_hs_priv_data *priv = host->priv; + + status = !gpio_get_value(priv->gpio_cd); + return status; +} + +static unsigned long hs_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 hs_drv_data = { + .caps = hs_dwmmc_caps, + .init = dw_mci_hs_priv_init, + .set_ios = dw_mci_hs_set_ios, + .setup_clock = dw_mci_hs_setup_clock, +}; + +static const struct of_device_id dw_mci_hs_match[] = { + { .compatible = "hisilicon,hi4511-dw-mshc", + .data = &hs_drv_data, }, + { .compatible = "hisilicon,hi3620-dw-mshc", + .data = &hs_drv_data, }, + {}, +}; +MODULE_DEVICE_TABLE(of, dw_mci_hs_match); + +int dw_mci_hs_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_hs_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_hs_priv_data *priv = host->priv; + priv->gpio_cd = gpio; + host->pdata->get_cd = dw_mci_hs_get_cd; + err = devm_request_irq(host->dev, gpio_to_irq(gpio), + dw_mci_hs_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_hs_suspend(struct device *dev) +{ + int ret; + struct dw_mci *host = dev_get_drvdata(dev); + struct dw_mci_hs_priv_data *priv = host->priv; + + ret = dw_mci_suspend(host); + if (ret) + return ret; + priv->old_timing = -1; + + return 0; +} + +static int dw_mci_hs_resume(struct device *dev) +{ + int ret; + struct dw_mci *host = dev_get_drvdata(dev); + struct dw_mci_hs_priv_data *priv = host->priv; + + if (priv->type == DW_MCI_TYPE_HI4511) { + int id = priv->id; + + dw_mci_hs_tun(host, id, MMC_TIMING_LEGACY); + } + + ret = dw_mci_resume(host); + if (ret) + return ret; + + return 0; +} + +SIMPLE_DEV_PM_OPS(dw_mci_hs_pmops, dw_mci_hs_suspend, dw_mci_hs_resume); + +static struct platform_driver dw_mci_hs_pltfm_driver = { + .probe = dw_mci_hs_probe, + .remove = dw_mci_pltfm_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(dw_mci_hs_match), + .pm = &dw_mci_hs_pmops, + }, +}; + +module_platform_driver(dw_mci_hs_pltfm_driver); + +MODULE_DESCRIPTION("Hisilicon Specific DW-MSHC Driver Extension"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 41c27b74b003..c58e513185d4 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -68,7 +68,7 @@ static int dw_mci_pltfm_probe(struct platform_device *pdev) return dw_mci_pltfm_register(pdev, NULL); } -static int dw_mci_pltfm_remove(struct platform_device *pdev) +int dw_mci_pltfm_remove(struct platform_device *pdev) { struct dw_mci *host = platform_get_drvdata(pdev); diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index bc3a1bc4940f..78166a0f5fc8 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -871,7 +871,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; @@ -933,6 +933,45 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) } } +static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = slot->host; + int min_uV = 0, max_uV = 0; + int ret; + + if (!host->vqmmc) + return 0; + + switch (ios->signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: + min_uV = 2700000; + max_uV = 3600000; + break; + case MMC_SIGNAL_VOLTAGE_180: + min_uV = 1700000; + max_uV = 1950000; + break; + case MMC_SIGNAL_VOLTAGE_120: + min_uV = 1100000; + max_uV = 1300000; + break; + default: + return 0; + } + + ret = regulator_set_voltage(host->vqmmc, min_uV, max_uV); + if (ret) { + pr_warning("%s: Switching signalling voltage " + " failed\n", mmc_hostname(mmc)); + return -EIO; + } + + /* Wait for 5ms */ + usleep_range(5000, 5500); + return 0; +} + static const struct mmc_host_ops dw_mci_ops = { .request = dw_mci_request, .pre_req = dw_mci_pre_req, @@ -941,6 +980,7 @@ static const struct mmc_host_ops dw_mci_ops = { .get_ro = dw_mci_get_ro, .get_cd = dw_mci_get_cd, .enable_sdio_irq = dw_mci_enable_sdio_irq, + .start_signal_voltage_switch = dw_mci_switch_voltage, }; static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) @@ -1998,6 +2038,19 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) } } + host->vqmmc = devm_regulator_get(mmc_dev(mmc), "vqmmc"); + if (IS_ERR(host->vqmmc)) { + pr_warning("%s: no vqmmc regulator found\n", mmc_hostname(mmc)); + host->vqmmc = NULL; + } else { + ret = regulator_enable(host->vqmmc); + if (ret) { + dev_err(host->dev, + "failed to enable regulator vqmmc: %d\n", ret); + goto err_setup_bus; + } + } + if (dw_mci_get_cd(mmc)) set_bit(DW_MMC_CARD_PRESENT, &slot->flags); else @@ -2418,6 +2471,9 @@ void dw_mci_remove(struct dw_mci *host) if (host->vmmc) regulator_disable(host->vmmc); + if (host->vqmmc) + regulator_disable(host->vqmmc); + if (!IS_ERR(host->ciu_clk)) clk_disable_unprepare(host->ciu_clk); @@ -2454,6 +2510,9 @@ int dw_mci_suspend(struct dw_mci *host) if (host->vmmc) regulator_disable(host->vmmc); + if (host->vqmmc) + regulator_disable(host->vqmmc); + return 0; } EXPORT_SYMBOL(dw_mci_suspend); @@ -2471,6 +2530,15 @@ int dw_mci_resume(struct dw_mci *host) } } + if (host->vqmmc) { + ret = regulator_enable(host->vqmmc); + if (ret) { + dev_err(host->dev, + "failed to enable regulator vqmmc: %d\n", ret); + return ret; + } + } + if (!mci_wait_reset(host->dev, host)) { ret = -ENODEV; return ret; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 8bb26446037e..08774c02a840 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -514,5 +514,12 @@ config REGULATOR_AS3711 This driver provides support for the voltage regulators on the AS3711 PMIC +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 47a34ff88f98..5a67225c8973 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -70,6 +70,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..083e2c3d83ca --- /dev/null +++ b/drivers/regulator/hi6421-regulator.c @@ -0,0 +1,560 @@ +/* + * 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; + if (of_get_property(np, "ldo-supply", NULL)) { + rdesc->supply_name = "ldo"; + } + + /* 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); + struct hi6421_regulator *sreg = rdev_get_drvdata(rdev); + + 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/Kconfig b/drivers/rtc/Kconfig index b9838130a7b0..d2a8577bdfc5 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -208,6 +208,17 @@ config RTC_DRV_LP8788 help Say Y to enable support for the LP8788 RTC/ALARM driver. +config RTC_DRV_HI6421 + tristate "Hisilicon Hi6421 RTC" + depends on MFD_HI6421_PMIC + help + If you say yes here you get support for Hisilicon Hi6421 PMIC + RTC functions. If an interrupt is associated with the device, + the alarm functionality is supported. + + This driver can also be built as a module. If so, the module + will be called rtc-hi6421. + config RTC_DRV_MAX6900 tristate "Maxim MAX6900" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index c33f86f1a69b..b71f81793e04 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o +obj-$(CONFIG_RTC_DRV_HI6421) += rtc-hi6421.o obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o diff --git a/drivers/rtc/rtc-hi6421.c b/drivers/rtc/rtc-hi6421.c new file mode 100644 index 000000000000..50ec3911861b --- /dev/null +++ b/drivers/rtc/rtc-hi6421.c @@ -0,0 +1,259 @@ +/* + * Hisilicon Hi6421 RTC driver + * + * Copyright (C) 2013 Hisilicon Ltd. + * Copyright (C) 2013 Linaro Ltd. + * + * Author: Haojian Zhuang <haojian.zhuang@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of_device.h> +#include <linux/rtc.h> +#include <linux/mfd/hi6421-pmic.h> + +#define REG_IRQ1 0x01 +#define REG_IRQM1 0x04 +#define REG_RTCDR0 0x58 +#define REG_RTCDR1 0x59 +#define REG_RTCDR2 0x5a +#define REG_RTCDR3 0x5b +#define REG_RTCMR0 0x5c +#define REG_RTCMR1 0x5d +#define REG_RTCMR2 0x5e +#define REG_RTCMR3 0x5f +#define REG_RTCLR0 0x60 +#define REG_RTCLR1 0x61 +#define REG_RTCLR2 0x62 +#define REG_RTCLR3 0x63 +#define REG_RTCCTRL 0x64 +#define REG_SOFT_RST 0x86 + +#define ALARM_ON (1 << HI6421_IRQ_ALARM) + +struct hi6421_rtc_info { + struct rtc_device *rtc; + struct hi6421_pmic *pmic; + int irq; +}; + +static irqreturn_t hi6421_rtc_handler(int irq, void *data) +{ + struct hi6421_rtc_info *info = (struct hi6421_rtc_info *)data; + + /* clear alarm status */ + hi6421_pmic_rmw(info->pmic, REG_IRQ1, ALARM_ON, ALARM_ON); + rtc_update_irq(info->rtc, 1, RTC_AF); + return IRQ_HANDLED; +} + +/* read 4 8-bit registers & covert it into a 32-bit data */ +static unsigned int hi6421_read_bulk(struct hi6421_pmic *pmic, + unsigned int addr) +{ + unsigned int data, sum = 0; + int i; + + for (i = 0; i < 4; i++) { + data = hi6421_pmic_read(pmic, addr + i); + sum |= (data & 0xff) << (i * 8); + } + return sum; +} + +/* write a 32-bit data into 4 8-bit registers */ +static void hi6421_write_bulk(struct hi6421_pmic *pmic, unsigned int addr, + unsigned int data) +{ + unsigned int value; + int i; + + for (i = 0; i < 4; i++) { + value = (data >> (i * 8)) & 0xff; + hi6421_pmic_write(pmic, addr + i, value); + } +} + +static int hi6421_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct hi6421_rtc_info *info = dev_get_drvdata(dev); + unsigned long ticks; + + ticks = hi6421_read_bulk(info->pmic, REG_RTCDR0); + rtc_time_to_tm(ticks, tm); + return 0; +} + +static int hi6421_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct hi6421_rtc_info *info = dev_get_drvdata(dev); + unsigned long ticks; + + if ((tm->tm_year < 70) || (tm->tm_year > 138)) { + dev_dbg(dev, "Set time %d out of range. " + "Please set time between 1970 to 2038.\n", + 1900 + tm->tm_year); + return -EINVAL; + } + rtc_tm_to_time(tm, &ticks); + hi6421_write_bulk(info->pmic, REG_RTCLR0, ticks); + return 0; +} + +static int hi6421_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct hi6421_rtc_info *info = dev_get_drvdata(dev); + unsigned long ticks, data; + + ticks = hi6421_read_bulk(info->pmic, REG_RTCMR0); + rtc_time_to_tm(ticks, &alrm->time); + + data = hi6421_pmic_read(info->pmic, REG_IRQ1); + alrm->pending = (data & ALARM_ON) ? 1 : 0; + + data = hi6421_pmic_read(info->pmic, REG_IRQM1); + alrm->enabled = (data & ALARM_ON) ? 0 : 1; + return 0; +} + +/* + * Calculate the next alarm time given the requested alarm time mask + * and the current time. + */ +static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, + struct rtc_time *alrm) +{ + unsigned long next_time; + unsigned long now_time; + + next->tm_year = now->tm_year; + next->tm_mon = now->tm_mon; + next->tm_mday = now->tm_mday; + next->tm_hour = alrm->tm_hour; + next->tm_min = alrm->tm_min; + next->tm_sec = alrm->tm_sec; + + rtc_tm_to_time(now, &now_time); + rtc_tm_to_time(next, &next_time); + + if (next_time < now_time) { + /* Advance one day */ + next_time += 60 * 60 * 24; + rtc_time_to_tm(next_time, next); + } +} + +static int hi6421_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct hi6421_rtc_info *info = dev_get_drvdata(dev); + struct rtc_time now_tm, alarm_tm; + unsigned long ticks; + + /* load 32-bit read-only counter */ + ticks = hi6421_read_bulk(info->pmic, REG_RTCDR0); + rtc_time_to_tm(ticks, &now_tm); + dev_dbg(dev, "%s, now time : %lu\n", __func__, ticks); + rtc_next_alarm_time(&alarm_tm, &now_tm, &alrm->time); + + /* get new ticks for alarm in 24 hours */ + rtc_tm_to_time(&alarm_tm, &ticks); + dev_dbg(dev, "%s, alarm time: %lu\n", __func__, ticks); + if (alrm->enabled) + hi6421_write_bulk(info->pmic, REG_RTCMR0, ticks); + return 0; +} + +static int hi6421_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct hi6421_rtc_info *info = dev_get_drvdata(dev); + unsigned int data = 0; + + if (!enabled) + data = ALARM_ON; + hi6421_pmic_rmw(info->pmic, REG_IRQM1, ALARM_ON, data); + return 0; +} + +static const struct rtc_class_ops hi6421_rtc_ops = { + .read_time = hi6421_rtc_read_time, + .set_time = hi6421_rtc_set_time, + .read_alarm = hi6421_rtc_read_alarm, + .set_alarm = hi6421_rtc_set_alarm, + .alarm_irq_enable = hi6421_rtc_alarm_irq_enable, +}; + +static int hi6421_rtc_probe(struct platform_device *pdev) +{ + struct hi6421_rtc_info *info; + int ret; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + info->irq = platform_get_irq(pdev, 0); + if (info->irq < 0) + return -ENOENT; + + info->pmic = dev_get_drvdata(pdev->dev.parent); + platform_set_drvdata(pdev, info); + + /* enable RTC device */ + hi6421_pmic_write(info->pmic, REG_RTCCTRL, 1); + + info->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &hi6421_rtc_ops, THIS_MODULE); + if (IS_ERR(info->rtc)) + return PTR_ERR(info->rtc); + + ret = devm_request_irq(&pdev->dev, info->irq, hi6421_rtc_handler, + IRQF_DISABLED, "alarm", info); + if (ret < 0) + return ret; + + return 0; +} + +static int hi6421_rtc_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct of_device_id hi6421_rtc_of_match[] = { + { .compatible = "hisilicon,hi6421-rtc" }, + { } +}; +MODULE_DEVICE_TABLE(of, hi6421_rtc_of_match); + +static struct platform_driver hi6421_rtc_driver = { + .driver = { + .name = "hi6421-rtc", + .of_match_table = of_match_ptr(hi6421_rtc_of_match), + }, + .probe = hi6421_rtc_probe, + .remove = hi6421_rtc_remove, +}; +module_platform_driver(hi6421_rtc_driver); + +MODULE_LICENSE("GPLv2"); +MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@linaro.org"); +MODULE_ALIAS("platform:hi6421-rtc"); diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index 0f0609b1aa2c..8851e6b807c6 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -371,6 +371,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) } } + device_init_wakeup(&adev->dev, 1); ldata->rtc = rtc_device_register("pl031", &adev->dev, ops, THIS_MODULE); if (IS_ERR(ldata->rtc)) { @@ -402,6 +403,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 = { @@ -471,6 +498,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/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index 3511b0840362..2685008e17b1 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -231,8 +231,20 @@ static ssize_t ashmem_read(struct file *file, char __user *buf, goto out_unlock; } + if (!asma->file->f_op) { + pr_info("asma->file->f_op is NULL\n"); + ret = -EBADF; + goto out_unlock; + } + + if (!asma->file->f_op->read) { + pr_info("asma->file->f_op->read is NULL\n"); + ret = -EBADF; + goto out_unlock; + } mutex_unlock(&ashmem_mutex); + /* * asma and asma->file are used outside the lock here. We assume * once asma->file is set it will never be changed, and will not @@ -268,6 +280,18 @@ static loff_t ashmem_llseek(struct file *file, loff_t offset, int origin) goto out; } + if (!asma->file->f_op) { + pr_info("asma->file->f_op is NULL\n"); + ret = -EBADF; + goto out; + } + + if (!asma->file->f_op->llseek) { + pr_info("asma->file->f_op->llseek is NULL\n"); + ret = -EBADF; + goto out; + } + ret = asma->file->f_op->llseek(asma->file, offset, origin); if (ret < 0) goto out; diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index a3279c7def71..6eba25e5e350 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2494,6 +2494,7 @@ config FB_SIMPLE source "drivers/video/omap/Kconfig" source "drivers/video/omap2/Kconfig" source "drivers/video/exynos/Kconfig" +source "drivers/video/hisilicon/Kconfig" source "drivers/video/mmp/Kconfig" source "drivers/video/backlight/Kconfig" source "drivers/video/adf/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 3adbd32eb091..8b6269857b99 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_FB_RIVA) += riva/ obj-$(CONFIG_FB_NVIDIA) += nvidia/ obj-$(CONFIG_FB_ATY) += aty/ macmodes.o obj-$(CONFIG_FB_ATY128) += aty/ macmodes.o +obj-$(CONFIG_FB_HI3620) += hisilicon/ obj-$(CONFIG_FB_RADEON) += aty/ obj-$(CONFIG_FB_SIS) += sis/ obj-$(CONFIG_FB_VIA) += via/ diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c index 6103fa6fb54f..f5c5f6644d17 100644 --- a/drivers/video/fbmon.c +++ b/drivers/video/fbmon.c @@ -1398,6 +1398,7 @@ int fb_videomode_from_videomode(const struct videomode *vm, fbmode->sync = 0; fbmode->vmode = 0; + fbmode->flag = 0; if (vm->flags & DISPLAY_FLAGS_HSYNC_HIGH) fbmode->sync |= FB_SYNC_HOR_HIGH_ACT; if (vm->flags & DISPLAY_FLAGS_VSYNC_HIGH) @@ -1406,7 +1407,14 @@ int fb_videomode_from_videomode(const struct videomode *vm, fbmode->vmode |= FB_VMODE_INTERLACED; if (vm->flags & DISPLAY_FLAGS_DOUBLESCAN) fbmode->vmode |= FB_VMODE_DOUBLE; - fbmode->flag = 0; + if (vm->flags & DISPLAY_FLAGS_DE_HIGH) + fbmode->flag |= FB_FLAG_DE_HIGH; + if (vm->flags & DISPLAY_FLAGS_DE_LOW) + fbmode->flag |= FB_FLAG_DE_LOW; + if (vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) + fbmode->flag |= FB_FLAG_PIXDATA_POSEDGE; + if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) + fbmode->flag |= FB_FLAG_PIXDATA_NEGEDGE; htotal = vm->hactive + vm->hfront_porch + vm->hback_porch + vm->hsync_len; diff --git a/drivers/video/hisilicon/Kconfig b/drivers/video/hisilicon/Kconfig new file mode 100644 index 000000000000..3f59b0be78e0 --- /dev/null +++ b/drivers/video/hisilicon/Kconfig @@ -0,0 +1,11 @@ +config FB_HI3620 + tristate "Hisilicon Hi3620 Frame Buffer support" + depends on FB && ARCH_HS + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + select FB_MODE_HELPERS + select VIDEOMODE_HELPERS + help + This is the frame buffer device driver for the Hisilicon Hi3620 LCD + controller. diff --git a/drivers/video/hisilicon/Makefile b/drivers/video/hisilicon/Makefile new file mode 100644 index 000000000000..1f4b79befe51 --- /dev/null +++ b/drivers/video/hisilicon/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_FB_HI3620) += hi3620_fb.o +obj-y += hi3620_dsi.o diff --git a/drivers/video/hisilicon/hi3620_dsi.c b/drivers/video/hisilicon/hi3620_dsi.c new file mode 100644 index 000000000000..e7140d2accb2 --- /dev/null +++ b/drivers/video/hisilicon/hi3620_dsi.c @@ -0,0 +1,626 @@ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include <linux/platform_data/hi3620-dsi.h> + +#include "hi3620_fb.h" + +/* DSI PHY internal register */ +#define DSI_PHY_CP_CURRENT 0x11 +#define DSI_PHY_LPF_CTRL 0x12 +#define DSI_PHY_PLL_UNLOCK_FILTER 0x16 +#define DSI_PHY_N_PLL 0x17 +#define DSI_PHY_M_PLL 0x18 +#define DSI_PHY_FACTOR 0x19 +#define DSI_PHY_HS_FREQ_RANGE 0x44 + +#define PHY_ADDR (1 << 16) + +#define DTYPE_GEN_WRITE_HEAD 0x03 +#define DTYPE_GEN_WRITE_PAYLOAD 0x29 + +struct phy_timing { + int dsi2x; /* MHz */ + int lp2hs; + int hs2lp; + int hsfreq; +}; + +static struct device *dsi_dev = NULL; + +static void reset(void __iomem *reg_base) +{ + writel_relaxed(0, reg_base + DSI_PWR_UP); +} + +static void unreset(void __iomem *reg_base) +{ + writel_relaxed(1, reg_base + DSI_PWR_UP); +} + +static void set_highspeed(void __iomem *reg_base) +{ + unsigned int data; + + /* enable high speed clock for PHY */ + data = readl_relaxed(reg_base + DSI_PHY_IF_CTRL); + data |= (1 << 0); + writel_relaxed(data, reg_base + DSI_PHY_IF_CTRL); +} + +static void unset_highspeed(void __iomem *reg_base) +{ + unsigned int data; + + /* disable high speed clock for PHY */ + data = readl_relaxed(reg_base + DSI_PHY_IF_CTRL); + data &= ~(1 << 0); + writel_relaxed(data, reg_base + DSI_PHY_IF_CTRL); +} + +/* bit definition of register DSI_VID_MODE_CFG */ +#define VIDEO_MODE_EN (1 << 0) + +/* bit definition of register DSI_CMD_MODE_CFG */ +#define CMD_MODE_EN (1 << 0) +#define CMD_MODE_LP (0x1ffe) + +static int is_video_mode(void __iomem *base) +{ + unsigned int data; + + data = readl_relaxed(base + DSI_VID_MODE_CFG); + if (data & VIDEO_MODE_EN) + return 1; + return 0; +} + +static int is_cmd_mode(void __iomem *base) +{ + unsigned int data; + + data = readl_relaxed(base + DSI_CMD_MODE_CFG); + if (data & CMD_MODE_EN) + return 1; + return 0; +} + +/* Switch to CMD mode with LP transmission */ +static void set_cmd_mode(void __iomem *base) +{ + unsigned int data; + + /* disable VIDEO mode */ + data = readl_relaxed(base + DSI_VID_MODE_CFG); + data &= ~VIDEO_MODE_EN; + writel_relaxed(data, base + DSI_VID_MODE_CFG); + /* enable COMMAND mode with LP transmission */ + data = readl_relaxed(base + DSI_CMD_MODE_CFG); + data |= CMD_MODE_LP | CMD_MODE_EN; + writel_relaxed(data, base + DSI_CMD_MODE_CFG); +} + +static void init_video_mode(void __iomem *base, struct hi3620fb_info *info) +{ + struct fb_var_screeninfo *var = &info->fb->var; + unsigned int data; + + data = readl_relaxed(base + DSI_VID_MODE_CFG); + data |= 0x1f8; /* enable Low Power control */ + data &= ~(3 << 1); /* mask video transfer mode */ + data |= (2 << 1); /* Burst with Sync Pulses */ + data &= ~0x800; /* Disable frame end ACK */ + writel_relaxed(data, base + DSI_VID_MODE_CFG); + data = readl_relaxed(base + DSI_VID_PKT_CFG); + data &= ~0x7ff; /* mask single video packet size */ + data |= var->xres; + writel_relaxed(data, base + DSI_VID_PKT_CFG); + data = readl_relaxed(base + DSI_PCKHDL_CFG); + data |= (1 << 2); /* enable Bus Turn-Around */ + writel_relaxed(data, base + DSI_PCKHDL_CFG); +} + +/* Switch to VIDEO mode */ +static void set_video_mode(void __iomem *base) +{ + unsigned int data; + + /* disable CMD mode */ + data = readl_relaxed(base + DSI_CMD_MODE_CFG); + data |= CMD_MODE_LP; + data &= ~CMD_MODE_EN; + writel_relaxed(data, base + DSI_CMD_MODE_CFG); + /* enable VIDEO mode */ + data = readl_relaxed(base + DSI_VID_MODE_CFG); + data |= VIDEO_MODE_EN; + writel_relaxed(data, base + DSI_VID_MODE_CFG); +} + +static void set_dpi_timing(struct hi3620fb_info *info) +{ + void __iomem *base = info->reg_base; + struct fb_videomode *fb_vm = info->fb->mode; + unsigned int dsi_rate, lane_rate, pixclock, data, timing; + unsigned long long int tmp; + + dsi_rate = clk_get_rate(info->clk_dsi); /* dsi1x, not dsi2x */ + lane_rate = dsi_rate / 4; /* dsi2x / 8 */ + + pixclock = PICOS2KHZ(fb_vm->pixclock) * 1000; + tmp = (unsigned long long int)(fb_vm->hsync_len) * lane_rate; + do_div(tmp, pixclock); + data = (unsigned int)tmp; + timing = data & 0x1ff; + tmp = (unsigned long long int)(fb_vm->left_margin) * lane_rate; + do_div(tmp, pixclock); + data = (unsigned int)tmp; + timing |= (data & 0x1ff) << 9; + tmp = (unsigned long long int)(fb_vm->left_margin + fb_vm->xres + + fb_vm->right_margin + fb_vm->hsync_len); + tmp *= lane_rate; + do_div(tmp, pixclock); + data = (unsigned int)tmp; + timing |= data << 18; + writel_relaxed(timing, info->reg_base + DSI_TMR_LINE_CFG); + + /* setup frame timing */ + timing = (fb_vm->vsync_len) & 0xf; + timing |= ((fb_vm->upper_margin) & 0x3f) << 4; + timing |= ((fb_vm->lower_margin) & 0x3f) << 10; + timing |= ((fb_vm->yres) & 0x7ff) << 16; + writel_relaxed(timing, info->reg_base + DSI_VTIMING_CFG); +} + +#define PHY_TX_OUT_LP_DATA (1 << 4) +#define PHY_TX_IN_LP_DATA (1 << 3) +#define PHY_TX_OUT_LP_CLK (1 << 2) +#define PHY_TX_IN_LP_CLK (1 << 1) +#define PHY_TX_REQ_HS_CLK (1 << 0) +#define PHY_TX_MASK 0x1f + +static int is_phy_hs(void __iomem *base) +{ + unsigned int data, expected; + + data = readl_relaxed(base + DSI_PHY_IF_CTRL); + expected = PHY_TX_OUT_LP_DATA | PHY_TX_OUT_LP_CLK | PHY_TX_REQ_HS_CLK; + if ((data & PHY_TX_MASK) == expected) + return 1; + return 0; +} + +static int is_phy_lp(void __iomem *base) +{ + unsigned int data, expected; + + data = readl_relaxed(base + DSI_PHY_IF_CTRL); + expected = PHY_TX_IN_LP_DATA | PHY_TX_IN_LP_CLK; + if ((data & PHY_TX_MASK) == expected) + return 1; + return 0; +} + +static int set_phy_hs(void __iomem *base) +{ + unsigned int data; + + data = readl_relaxed(base + DSI_PHY_IF_CTRL); + data &= ~PHY_TX_MASK; + data |= PHY_TX_OUT_LP_DATA | PHY_TX_OUT_LP_CLK; + writel_relaxed(data, base + DSI_PHY_IF_CTRL); + data = readl_relaxed(base + DSI_PHY_IF_CTRL); + return 0; +} + +static int set_phy_lp(void __iomem *base) +{ + unsigned int data; + + data = readl_relaxed(base + DSI_PHY_IF_CTRL); + data &= ~PHY_TX_MASK; + data |= PHY_TX_OUT_LP_DATA | PHY_TX_OUT_LP_CLK; + writel_relaxed(data, base + DSI_PHY_IF_CTRL); + data = readl_relaxed(base + DSI_PHY_IF_CTRL); + return 0; +} + +static void enable_phy(void __iomem *base) +{ + unsigned int status, mask; + + writel_relaxed(0x7, base + DSI_PHY_RSTZ); + mask = DSI_PHY_STOP_STATE0_LANE | DSI_PHY_LOCK; + do { + cpu_relax(); + status = readl_relaxed(base + DSI_PHY_STATUS); + } while ((status & mask) != mask); +} + +static void disable_phy(void __iomem *reg_base) +{ + writel_relaxed(0, reg_base + DSI_PHY_RSTZ); +} + +void hi3620_mipi_dsi_set_video_packet_size(void __iomem *reg_base, + int null_pkt_size, int num_vid_pkt, + int vid_pkt_size) +{ + unsigned int data; + data = (null_pkt_size & 0x3ff) << 21; /* byte size */ + /* number of video packets for Each Multiple Packets */ + data |= (num_vid_pkt & 0x3ff) << 11; + /* pixels of each video packet */ + data |= vid_pkt_size & 0x7ff; + writel_relaxed(data, reg_base + DSI_VID_PKT_CFG); +} + +static void hi3620_mipi_setup_dpi(struct hi3620fb_info *info) +{ + unsigned int data; + + /* set lane numbers */ + /* + data = readl_relaxed(info->reg_base + DSI_PHY_IF_CFG) & ~0x3; + data |= (info->lane_cnt - 1) & 0x3; + writel_relaxed(data, info->reg_base + DSI_PHY_IF_CFG); + */ + data = 0; + //data = readl_relaxed(info->reg_base + DSI_DPI_CFG); + /* set virtual channel ID as 0 */ + data &= ~(3 << 0); + /* set color mode */ + data &= ~(7 << 2); + data |= info->color_mode << 2; + /* always set color mode & shutdown high active */ + writel_relaxed(data, info->reg_base + DSI_DPI_CFG); +} + +#define MAX_TX_ESC_CLK (10 * 1000 * 1000) + +static void setup_phy(struct hi3620fb_info *info) +{ + struct clk *parent; + unsigned char hs2lp = 0, lp2hs = 0, hsfreq = 0; + unsigned int data; + void __iomem *base = info->reg_base; + int rate, rate_mhz, dsi_rate, lane_rate, i; + struct phy_timing timing[] = { + {90, 24, 14, 0}, {100, 25, 14, 0x20}, + {110, 25, 14, 0x40}, {125, 25, 14, 0x02}, + {140, 25, 14, 0x22}, {150, 25, 14, 0x42}, + {160, 25, 14, 0x04}, {180, 28, 16, 0x24}, + {200, 32, 16, 0x44}, {210, 31, 16, 0x06}, + {240, 35, 17, 0x26}, {250, 37, 18, 0x46}, + {270, 37, 18, 0x08}, {300, 39, 19, 0x28}, + {330, 44, 20, 0x08}, {360, 47, 21, 0x2a}, + {400, 48, 21, 0x4a}, {450, 54, 23, 0x0c}, + {500, 58, 25, 0x2c}, {550, 62, 26, 0x0e}, + {600, 67, 28, 0x2e}, {650, 72, 30, 0x10}, + {700, 76, 31, 0x30}, {750, 81, 32, 0x12}, + {800, 86, 34, 0x32}, {850, 89, 35, 0x14}, + {900, 95, 37, 0x34}, {950, 99, 38, 0x54}, + {1000, 104, 40, 0x74}, }; + parent = clk_get_parent(info->clk_dsi); + rate = clk_get_rate(parent); + rate_mhz = rate / 1000000; + for (i = 0; i < ARRAY_SIZE(timing); i++) { + if (rate_mhz <= timing[i].dsi2x) { + hs2lp = timing[i].hs2lp; + lp2hs = timing[i].lp2hs; + hsfreq = timing[i].hsfreq; + break; + } + } + + /* setup PHY timing */ + data = 4095; /* bta time */ + data |= (lp2hs & 0xff) << 16; + data |= (hs2lp & 0xff) << 24; + writel_relaxed(data, base + DSI_PHY_TMR_CFG); + /* set hsfreqrange */ + hi3620_dsi_phy_write(base, 0x44, hsfreq); + writel_relaxed(0x7, base + DSI_PHY_RSTZ); + +#if 0 + /* set tx esc clk division */ + dsi_rate = clk_get_rate(info->clk_dsi); + lane_rate = dsi_rate / 4; + data = (lane_rate + (MAX_TX_ESC_CLK / 2) - 1) / MAX_TX_ESC_CLK; + writel_relaxed(data, base + DSI_CLKMGR_CFG); +#endif +} + +void hi3620_mipi_dsi_set_lane(void __iomem *reg_base, int id, int count) +{ + unsigned int data, cnt; + + cnt = count - 1; + if (cnt < 0 || id > cnt) + return; + data = readl_relaxed(reg_base + DSI_PHY_IF_CFG) & ~0x3; + data |= cnt & 0x3; + writel_relaxed(data, reg_base + DSI_PHY_IF_CFG); + data = readl_relaxed(reg_base + DSI_DPI_CFG) & ~0x3; + data |= id & 0x3; + writel_relaxed(data, reg_base + DSI_DPI_CFG); +} + +/* + * PHY_TST_CTRL0 & PHY_TST_CTRL1 registers are the interfaces of accessing + * PHY internal registers. + * PHY_TST_CTRL0 is used to produce clock, as I2C SCLK. + * PHY_TST_CTRL1 is used to store address or data, as I2C SDA. + */ +static void set_phy_testclk(void __iomem *reg_base, int level) +{ + unsigned int data; + + if (level) + data = 0x2; + else + data = 0; + writel_relaxed(data, reg_base + DSI_PHY_TST_CTRL0); +} + +/* write 8-bit data into 8-bit phy register */ +int hi3620_dsi_phy_write(void __iomem *reg_base, unsigned char addr, + unsigned char data) +{ + unsigned int value; + + set_phy_testclk(reg_base, 0); + value = (unsigned int)addr | PHY_ADDR; + writel_relaxed(value, reg_base + DSI_PHY_TST_CTRL1); + set_phy_testclk(reg_base, 1); + + set_phy_testclk(reg_base, 0); + value = (unsigned int)data; + writel_relaxed(value, reg_base + DSI_PHY_TST_CTRL1); + set_phy_testclk(reg_base, 1); + set_phy_testclk(reg_base, 0); + return 0; +} +EXPORT_SYMBOL(hi3620_dsi_phy_write); + +/* read 8-bit data from 8-bit phy register */ +unsigned char hi3620_dsi_phy_read(void __iomem *reg_base, unsigned char addr) +{ + unsigned int value; + + set_phy_testclk(reg_base, 0); + value = (unsigned int)addr | PHY_ADDR; + writel_relaxed(value, reg_base + DSI_PHY_TST_CTRL1); + set_phy_testclk(reg_base, 1); + set_phy_testclk(reg_base, 0); + value = readl_relaxed(reg_base + DSI_PHY_TST_CTRL1); + return (unsigned char)(value >> 8); +} + +static bool is_wfifo_full(void __iomem *base) +{ + unsigned int status; + + status = readl_relaxed(base + DSI_CMD_PKT_STATUS); + if (status & GEN_PLD_W_FULL) + return true; + return false; +} + +static int recv_generic_pkt(void __iomem *base, u8 *val, int len) +{ + int i, cnt; + unsigned int status, data; + + cnt = (len + 3) / 4; + for (i = 0; i < cnt; i++) { + do { + cpu_relax(); + status = readl_relaxed(base + DSI_CMD_PKT_STATUS); + if (status & GEN_PLD_R_EMPTY) { + pr_err("#%s, %d, found empty read fifo\n", + __func__, __LINE__); + return -EINVAL; + } + } while (status & GEN_RD_CMD_BUSY); + + data = readl_relaxed(base + DSI_GEN_PLD_DATA); + val[i * 4] = data & 0xff; + val[i * 4 + 1] = (data >> 8) & 0xff; + val[i * 4 + 2] = (data >> 16) & 0xff; + val[i * 4 + 3] = (data >> 24) & 0xff; + } + do { + cpu_relax(); + status = readl_relaxed(base + DSI_CMD_PKT_STATUS); + /* read data until FIFO empty */ + readl_relaxed(base + DSI_GEN_PLD_DATA); + } while (!(status & GEN_PLD_R_EMPTY)); + return 0; +} + +static void wait_phy_ready(void __iomem *base) +{ +#if 0 + unsigned int status; + + do { + cpu_relax(); + status = readl_relaxed(base + DSI_PHY_STATUS); + } while (!(status & DSI_PHY_STOP_STATE0_LANE)); +#else + /* + * In some board, it always loop as dead. + * So use delay as workaround. + */ + msleep(5); +#endif +} + +int set_packet(struct device *dev, u8 *cmd, int nr_payload) +{ + struct hi3620fb_info *info = dev_get_drvdata(dev); + void __iomem *base = info->reg_base; + unsigned int header, type, data = 0; + int ret = 0, video_mode = 0, i = 0, j = 0; + + mutex_lock(&info->dsi_mutex); + wait_phy_ready(base); + if (is_video_mode(base)) { + video_mode = 1; + set_cmd_mode(base); + } + + /* set DT type */ + type = cmd[0] & 0xff; + header = type; + + switch (type) { + case DCS_SHORT_WR_PARAM0: + /* set DSI command */ + header |= (cmd[1] & 0xff) << 8; + writel_relaxed(header, base + DSI_GEN_HDR); + break; + case DCS_SHORT_WR_PARAM1: + header |= (cmd[1] & 0xff) << 8; + header |= (cmd[2] & 0xff) << 16; + writel_relaxed(header, base + DSI_GEN_HDR); + break; + case GEN_LONG_WR: + for (i = 0; i < nr_payload;) { + data = 0; + for (j = 0; i < nr_payload && j < 4; i++, j++) + data |= cmd[i + 1] << (j * 8); + writel_relaxed(data, base + DSI_GEN_PLD_DATA); + if (is_wfifo_full(base)) + pr_err("wfifo is full\n"); + } + header |= nr_payload << 8; + writel_relaxed(header, base + DSI_GEN_HDR); + break; + case GEN_RD_PARAM1: + header |= (cmd[1] & 0xff) << 8; + writel_relaxed(header, base + DSI_GEN_HDR); + udelay(20); /* FIXME */ + ret = recv_generic_pkt(base, &cmd[2], nr_payload); + break; + case DCS_RD_PARAM0: + header |= (cmd[1] & 0xff) << 8; + writel_relaxed(header, base + DSI_GEN_HDR); + udelay(20); /* FIXME */ + ret = recv_generic_pkt(base, &cmd[2], 1); + break; + default: + pr_warn("###%s: not set packet type\n", __func__); + break; + } + wait_phy_ready(base); + /* restore video mode if necessary */ + if (video_mode) + set_video_mode(base); + mutex_unlock(&info->dsi_mutex); + return ret; +} + +static int write_packet(struct device *dev, enum DATA_TYPE type, u8 *cmd, + int len) +{ + struct hi3620fb_info *info = dev_get_drvdata(dev); + void __iomem *base = info->reg_base; + unsigned int header; + int ret, video_mode = 0; + + if (is_video_mode(base)) { + video_mode = 1; + set_cmd_mode(base); + } + + header = type & 0xff; + header |= (cmd[0] & 0xff) << 8; + + switch (type) { + case DCS_SHORT_WR_PARAM0: + writel_relaxed(header, base + DSI_GEN_HDR); + break; + case DCS_SHORT_WR_PARAM1: + header |= (cmd[1] & 0xff) << 16; + writel_relaxed(header, base + DSI_GEN_HDR); + break; + default: + pr_err("#%s, %d, wrong type:%d\n", __func__, __LINE__, type); + break; + } + /* restore video mode if necessary */ + if (video_mode) + set_video_mode(base); + return ret; +} + +int dsi_set_packet(u8 *cmd, int nr_payload) +{ + if (dsi_dev) + return set_packet(dsi_dev, cmd, nr_payload); + else + return -EINVAL; +} + +int hi3620_mipi_init(struct device *dev) +{ + struct hi3620fb_info *info = dev_get_drvdata(dev); + + dsi_dev = dev; + mutex_init(&info->dsi_mutex); + return 0; +} +EXPORT_SYMBOL(hi3620_mipi_init); + +int hi3620_mipi_enable(struct device *dev) +{ + struct hi3620fb_info *info = dev_get_drvdata(dev); + + unreset(info->reg_base); + init_video_mode(info->reg_base, info); + + clk_set_rate(info->clk_dsi, info->dsi_rate); /* huawei logo is shifted to right & color may be changed??? */ + set_dpi_timing(info); + clk_prepare_enable(info->clk_dsi); + + setup_phy(info); + enable_phy(info->reg_base); + + set_cmd_mode(info->reg_base); + unset_highspeed(info->reg_base); + + /* set panel on */ + + if (!strncmp(info->mipi_mode_name, "video", 5)) + set_video_mode(info->reg_base); + else if (!strncmp(info->mipi_mode_name, "command", 7)) + set_cmd_mode(info->reg_base); + else + return -EINVAL; + set_highspeed(info->reg_base); + return 0; +} +EXPORT_SYMBOL(hi3620_mipi_enable); + +int hi3620_mipi_disable(struct device *dev) +{ + struct hi3620fb_info *info = dev_get_drvdata(dev); + + /* set panel off */ + + set_cmd_mode(info->reg_base); + unset_highspeed(info->reg_base); + unreset(info->reg_base); + disable_phy(info->reg_base); + reset(info->reg_base); + clk_disable_unprepare(info->clk_dsi); + return 0; +} +EXPORT_SYMBOL(hi3620_mipi_disable); diff --git a/drivers/video/hisilicon/hi3620_fb.c b/drivers/video/hisilicon/hi3620_fb.c new file mode 100644 index 000000000000..634ad1375b1f --- /dev/null +++ b/drivers/video/hisilicon/hi3620_fb.c @@ -0,0 +1,926 @@ +/* + * Framebuffer driver of Hisilicon Hi3620 SoC + * + * Copyright (c) 2013 Linaro Ltd. + * Copyright (c) 2013 Hisilicon Ltd. + * + * Author: Haojian Zhuang <haojian.zhuang@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/fb.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/regulator/consumer.h> +#include <linux/mfd/r63306.h> +#include <video/of_display_timing.h> +#include <video/display_timing.h> +#include "hi3620_fb.h" + +static unsigned int hi3620fb_pseudo_palette[16] = { + 0, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, + ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, +}; + +static int match_fmt_555(struct fb_var_screeninfo *var) +{ + if (var->blue.offset == 0 && var->green.offset <= 5 && + var->red.offset <= 10 && var->transp.offset <= 15 && + var->blue.offset <= 5 && var->green.length <= 5 && + var->red.length <= 5) + return 1; + return 0; +} + +static int match_fmt_565(struct fb_var_screeninfo *var) +{ + if (var->blue.offset == 0 && var->green.offset <= 5 && + var->red.offset <= 11 && var->transp.offset == 0 && + var->blue.length <= 5 && var->green.length <= 6 && + var->red.length <= 5 && var->transp.length == 0) + return 1; + return 0; +} + +static int match_fmt_888(struct fb_var_screeninfo *var) +{ + if (var->blue.offset == 0 && var->green.offset <= 8 && + var->red.offset <= 16 && var->transp.offset <= 24 && + var->blue.length <= 8 && var->green.length <= 8 && + var->red.length <= 8) + return 1; + return 0; +} + +static int find_best_pix_fmt(struct fb_var_screeninfo *var) +{ + if (var->bits_per_pixel == 16) { + /* RGB565/RGBA5551/RGBX5551 */ + if (match_fmt_555(var)) { + if (var->transp.length == 1) + return IMG_PIXEL_FORMAT_ARGB1555; + else if (var->transp.length == 0) + return IMG_PIXEL_FORMAT_RGB555; + } else if (match_fmt_565(var)) + return IMG_PIXEL_FORMAT_RGB565; + } else if (var->bits_per_pixel == 32) { + if (match_fmt_888(var)) { + if (var->transp.length == 8) + return IMG_PIXEL_FORMAT_ARGB8888; + else if (var->transp.length == 0) + return IMG_PIXEL_FORMAT_RGB888; + } + } + return -EINVAL; +} + +static void set_pix_fmt(struct hi3620fb_info *info, int pix_fmt) +{ + struct fb_info *fb = info->fb; + struct fb_var_screeninfo *var = &fb->var; + + switch (pix_fmt) { + case IMG_PIXEL_FORMAT_RGB565: + var->blue.offset = 0; + var->blue.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->red.offset = 11; + var->red.length = 5; + var->transp.offset = 0; + var->transp.length = 0; + var->bits_per_pixel = 16; + break; + case IMG_PIXEL_FORMAT_ARGB1555: + var->blue.offset = 0; + var->blue.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->red.offset = 10; + var->red.length = 5; + var->transp.offset = 15; + var->transp.length = 1; + var->bits_per_pixel = 16; + break; + case IMG_PIXEL_FORMAT_RGB555: + var->blue.offset = 0; + var->blue.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->red.offset = 10; + var->red.length = 5; + var->transp.offset = 15; + var->transp.length = 0; + var->bits_per_pixel = 16; + break; + case IMG_PIXEL_FORMAT_ARGB8888: + var->blue.offset = 0; + var->blue.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->red.offset = 16; + var->red.length = 8; + var->transp.offset = 24; + var->transp.length = 8; + var->bits_per_pixel = 32; + break; + case IMG_PIXEL_FORMAT_RGB888: + var->blue.offset = 0; + var->blue.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->red.offset = 16; + var->red.length = 8; + var->transp.offset = 24; + var->transp.length = 0; + var->bits_per_pixel = 32; + break; + default: + return; + } + info->pix_fmt = pix_fmt; +} + +static int hi3620fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *fb) +{ + struct hi3620fb_info *info = fb->par; + int pix_fmt; + + /* + * Determine which pixel format we're going to use. + */ + pix_fmt = find_best_pix_fmt(var); + if (pix_fmt < 0) + return pix_fmt; + set_pix_fmt(info, pix_fmt); + + /* + * Basic geometry sanity checks. + */ + if (var->xoffset + var->xres > var->xres_virtual) + return -EINVAL; + if (var->yoffset + var->yres > var->yres_virtual) + return -EINVAL; + if (var->xres + var->right_margin + + var->hsync_len + var->left_margin > 2048) + return -EINVAL; + if (var->yres + var->lower_margin + + var->vsync_len + var->upper_margin > 2048) + return -EINVAL; + + /* + * Check size of framebuffer. + */ + if (var->xres_virtual * var->yres_virtual * + (var->bits_per_pixel >> 3) > fb->fix.smem_len) + return -EINVAL; + return 0; +} + +/* It's used to make EDC configuration work. */ +static void update_edc(void __iomem *base) +{ + unsigned int data; + + data = readl_relaxed(base + EDC_DISP_CTL); + writel_relaxed(data | EDC_CTRL_CFG_OK, base + EDC_DISP_CTL); + data &= ~EDC_CTRL_CFG_OK; + writel_relaxed(data, base + EDC_DISP_CTL); +} + +static void disable_edc(void __iomem *base) +{ + unsigned int data; + + data = readl_relaxed(base + LDI_CTRL); + data &= ~(LDI_CTRL_EN | LDI_CTRL_SHUTDOWN); + writel_relaxed(data, base + LDI_CTRL); + data = readl_relaxed(base + EDC_DISP_CTL); + data &= ~EDC_CTRL_EN; + data |= EDC_CTRL_CLK_EN; + writel_relaxed(data, base + EDC_DISP_CTL); + update_edc(base); + data = readl_relaxed(base + EDC_VIDEO_CHAN_CTRL); + data &= ~EDC_VIDEO_CHAN_CTRL_EN; + writel_relaxed(data, base + EDC_VIDEO_CHAN_CTRL); +} + +static void enable_edc(void __iomem *base) +{ + unsigned int data; + + data = readl_relaxed(base + EDC_VIDEO_CHAN_CTRL); + data |= EDC_VIDEO_CHAN_CTRL_EN; + writel_relaxed(data, base + EDC_VIDEO_CHAN_CTRL); + + data = readl_relaxed(base + LDI_WORK_MODE); + data |= LDI_WORK_MODE_EN; + writel_relaxed(data, base + LDI_WORK_MODE); + + data = readl_relaxed(base + LDI_CTRL); + data &= ~(LDI_CTRL_SHUTDOWN | LDI_CTRL_COLOR_MODE | LDI_CTRL_BGR); + data &= ~(LDI_CTRL_DISP_MODE); + data |= LDI_CTRL_EN | LDI_CTRL_DATA_GATE_EN; + writel_relaxed(data, base + LDI_CTRL); + + data = readl_relaxed(base + EDC_DISP_CTL); + data |= EDC_CTRL_EN | EDC_CTRL_CLK_EN; + data &= ~(EDC_CTRL_CFG_OK_SEL | EDC_CTRL_FRAME_END_START); + /* unflow level */ + data &= ~(0xfff << 11); + data |= (0xc00 << 11); + writel_relaxed(data, base + EDC_DISP_CTL); + update_edc(base); +} + +static void set_panel_control(struct fb_info *fb) +{ + struct fb_videomode *fb_vm = fb->mode; + struct hi3620fb_info *info = fb->par; + void __iomem *base = info->reg_base; + u32 ldi, dpi; + + ldi = readl_relaxed(base + LDI_PLR_CTRL) & ~LDI_POLARITY_MASK; + dpi = readl_relaxed(base + DSI_DPI_CFG) & ~DSI_DPI_POLARITY_MASK; + if (fb_vm->sync & FB_SYNC_HOR_HIGH_ACT) { + ldi &= ~LDI_HSYNC_POLARITY; + dpi &= ~DSI_DPI_HSYNC_POLARITY; + } else { + ldi |= LDI_HSYNC_POLARITY; + dpi |= DSI_DPI_HSYNC_POLARITY; + } + if (fb_vm->sync & FB_SYNC_VERT_HIGH_ACT) { + ldi &= ~LDI_VSYNC_POLARITY; + dpi &= ~DSI_DPI_VSYNC_POLARITY; + } else { + ldi |= LDI_VSYNC_POLARITY; + dpi |= DSI_DPI_VSYNC_POLARITY; + } + if (fb_vm->flag & FB_FLAG_DE_HIGH) { + ldi &= ~LDI_DATAEN_POLARITY; + dpi &= ~DSI_DPI_DATAEN_POLARITY; + } + if (fb_vm->flag & FB_FLAG_DE_LOW) { + ldi |= LDI_DATAEN_POLARITY; + dpi |= DSI_DPI_DATAEN_POLARITY; + } + if (fb_vm->flag & FB_FLAG_PIXDATA_POSEDGE) + ldi |= LDI_PIXELCLK_POLARITY; + if (fb_vm->flag & FB_FLAG_PIXDATA_NEGEDGE) + ldi &= ~LDI_PIXELCLK_POLARITY; + writel_relaxed(ldi, base + LDI_PLR_CTRL); + /* always set color mode & shutdown high active */ + writel_relaxed(dpi, info->reg_base + DSI_DPI_CFG); +} + +static void set_screen_dimensions(struct fb_info *fb) +{ + struct fb_var_screeninfo *var = &fb->var; + struct hi3620fb_info *info = fb->par; + void __iomem *base = info->reg_base; + u32 data; + + data = (var->left_margin & 0xfff) << 20; + data |= var->right_margin & 0xfff; + writel_relaxed(data, base + LDI_HRZ_CTRL0); + data = (var->hsync_len - 1) & 0xfff; + writel_relaxed(data, base + LDI_HRZ_CTRL1); + data = (var->upper_margin & 0xfff) << 20; + data |= var->lower_margin & 0xfff; + writel_relaxed(data, base + LDI_VRT_CTRL0); + data = (var->vsync_len - 1) & 0xfff; + writel_relaxed(data, base + LDI_VRT_CTRL1); + + data = (var->xres - 1) & 0xfff; + data |= ((var->yres - 1) & 0xfff) << 20; + writel_relaxed(data, base + LDI_DSP_SIZE); + + data = (var->yres - 1) & 0xfff; + data |= ((var->xres - 1) & 0xfff) << 16; + writel_relaxed(data, base + EDC_DISP_SIZE); + + data = (fb->var.xres_virtual - 1) << 16 | (fb->var.yres_virtual - 1); + writel_relaxed(data, base + EDC_VIDEO_CHAN_SIZE); +} + +static void set_graphics_start(struct fb_info *fb, int xoffs, int yoffs) +{ + struct hi3620fb_info *info = fb->par; + struct fb_var_screeninfo *var = &fb->var; + void __iomem *base = info->reg_base; + u32 addr, data; + + if (yoffs >= var->yres) + data = (yoffs - var->yres) & 0xfff; + else + data = yoffs & 0xfff; + data |= (xoffs & 0xfff) << 16; + writel_relaxed(data, base + EDC_VIDEO_CHAN_XY); + /* setup dma address */ + addr = (yoffs * var->xres_virtual + xoffs) * var->bits_per_pixel / 8; + addr = ALIGN(addr + fb->fix.smem_start, 64); + writel_relaxed(addr, base + EDC_VIDEO_CHAN_ADDR); +} + +static void set_dma_control(struct fb_info *fb) +{ + struct hi3620fb_info *info = fb->par; + void __iomem *base = info->reg_base; + unsigned int data; + + /* setup dma stride */ + writel_relaxed(fb->fix.line_length, base + EDC_VIDEO_CHAN_STRIDE); + + /* setup outstanding */ + data = readl_relaxed(base + EDC_DISP_CTL); + data &= ~(0xf << 26); + data |= (8 << 26); + writel_relaxed(data, base + EDC_DISP_CTL); +} + +static void set_color(struct fb_info *fb) +{ + struct fb_var_screeninfo *var = &fb->var; + struct hi3620fb_info *info = fb->par; + void __iomem *base = info->reg_base; + unsigned int ctrl = 0; + + ctrl = readl_relaxed(base + EDC_VIDEO_CHAN_CTRL); + if (var->blue.offset) + ctrl |= EDC_CHAN_CTRL_BGR; /* BGR format */ + ctrl &= ~(7 << 16); + switch (info->pix_fmt) { + case IMG_PIXEL_FORMAT_ARGB1555: + case IMG_PIXEL_FORMAT_RGB555: + break; + case IMG_PIXEL_FORMAT_RGB565: + ctrl |= 1 << 16; + break; + case IMG_PIXEL_FORMAT_RGB888: + ctrl |= 2 << 16; + break; + case IMG_PIXEL_FORMAT_ARGB8888: + ctrl |= 3 << 16; + break; + } + writel_relaxed(ctrl, base + EDC_VIDEO_CHAN_CTRL); + ctrl = readl_relaxed(base + EDC_DISP_CTL); + ctrl &= ~(3 << 6); + switch (info->pix_fmt) { + case IMG_PIXEL_FORMAT_ARGB1555: + case IMG_PIXEL_FORMAT_RGB555: + case IMG_PIXEL_FORMAT_RGB565: + break; + case IMG_PIXEL_FORMAT_RGB888: + case IMG_PIXEL_FORMAT_ARGB8888: + ctrl |= 2 << 6; + break; + } + writel_relaxed(ctrl, base + EDC_DISP_CTL); + ctrl = readl_relaxed(base + LDI_CTRL); + ctrl &= ~(3 << 3); + switch (info->pix_fmt) { + case IMG_PIXEL_FORMAT_ARGB1555: + case IMG_PIXEL_FORMAT_RGB555: + case IMG_PIXEL_FORMAT_RGB565: + break; + case IMG_PIXEL_FORMAT_RGB888: + case IMG_PIXEL_FORMAT_ARGB8888: + ctrl |= 2 << 3; + break; + } + writel_relaxed(ctrl, base + LDI_CTRL); + ctrl = readl_relaxed(base + DSI_DPI_CFG); + ctrl &= ~(7 << 2); + switch (info->pix_fmt) { + case IMG_PIXEL_FORMAT_ARGB1555: + case IMG_PIXEL_FORMAT_RGB555: + case IMG_PIXEL_FORMAT_RGB565: + break; + case IMG_PIXEL_FORMAT_RGB888: + case IMG_PIXEL_FORMAT_ARGB8888: + ctrl |= 5 << 2; + break; + } + writel_relaxed(ctrl, base + DSI_DPI_CFG); +} + +static int hi3620fb_set_par(struct fb_info *fb) +{ + struct fb_var_screeninfo *var = &fb->var; + struct hi3620fb_info *info = fb->par; + void __iomem *base = info->reg_base; + unsigned int ctrl = 0; + + fb->fix.ypanstep = var->yres; + + ctrl = readl_relaxed(base + EDC_VIDEO_CHAN_CTRL); + ctrl |= EDC_VIDEO_CHAN_CTRL_EN; + writel_relaxed(ctrl, base + EDC_VIDEO_CHAN_CTRL); + + set_color(fb); + set_panel_control(fb); + set_screen_dimensions(fb); + set_dma_control(fb); + update_edc(base); + return 0; +} + +static int hi3620fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *fb) +{ + struct hi3620fb_info *info = fb->par; + void __iomem *base = info->reg_base; + + set_graphics_start(fb, var->xoffset, var->yoffset); + update_edc(base); + return 0; +} + +static int hi3620fb_blank(int blank_mode, struct fb_info *info) +{ + return 0; +} + +static int hi3620fb_ioctl(struct fb_info *fb, unsigned int cmd, + unsigned long arg) +{ + struct hi3620fb_info *info = fb->par; + u32 count = 0, status = 0, mask = 0; + int ret = 0; + + switch (cmd) { + case FBIO_WAITFORVSYNC: + count = info->vsync_cnt; + + /* clear interrupt status of End of Frame */ + status = readl_relaxed(info->reg_base + EDC_INTS); + status &= ~EDC_INT_BAS_END; + writel_relaxed(status, info->reg_base + EDC_INTS); + /* unmask interrupt of End of Frame */ + mask = readl_relaxed(info->reg_base + EDC_INTE); + mask &= ~EDC_INT_BAS_END; + writel_relaxed(mask, info->reg_base + EDC_INTE); + + ret = wait_event_interruptible_timeout(info->wait_vsync, + count != info->vsync_cnt, + HZ / 10); + if (ret < 0) + return ret; + /* mask interrupt of End of Frame */ + mask |= EDC_INT_BAS_END; + writel_relaxed(mask, info->reg_base + EDC_INTE); + break; + } + return 0; +} + +static struct fb_ops hi3620fb_ops = { + .owner = THIS_MODULE, + .fb_check_var = hi3620fb_check_var, + .fb_set_par = hi3620fb_set_par, + .fb_pan_display = hi3620fb_pan_display, + .fb_blank = hi3620fb_blank, + .fb_ioctl = hi3620fb_ioctl, + .fb_compat_ioctl = hi3620fb_ioctl, +}; + +static int hi3620_parse_dt(struct device_node *np, struct hi3620fb_info *info) +{ + const char *name; + int ret; + + ret = of_property_read_u32(np, "hisilicon,dsi-clock-frequency", + &info->dsi_rate); + if (ret) + return ret; + ret = of_property_read_string(np, "hisilicon,mipi-mode", &name); + if (ret < 0) + return ret; + info->mipi_mode_name = kstrdup(name, GFP_KERNEL); + ret = of_property_read_u32(np, "hisilicon,mipi-lanes", &info->lane_cnt); + if (ret < 0) + return ret; + ret = of_property_read_u32(np, "hisilicon,color-mode", &info->color_mode); + if (ret < 0) + return ret; + return 0; +} + +static int hi3620_init_mode(struct device_node *np, struct fb_info *fb) +{ + struct fb_fix_screeninfo *fix = &fb->fix; + struct fb_var_screeninfo *var = &fb->var; + struct display_timings *disp; + struct fb_videomode *fb_vm; + struct hi3620fb_info *info = fb->par; + const char *pix_name; + int ret = 0, pix_fmt, i, length; + + fb_vm = kzalloc(sizeof(*fb_vm), GFP_KERNEL); + if (!fb_vm) + return -ENOMEM; + fb->mode = fb_vm; + disp = of_get_display_timings(np); + if (!disp) + return -ENOENT; + /* How to handle multiple display timings ???, + * add_videomode is implemented by register_framebuffer() */ + for (i = 0; i < disp->num_timings; i++) { + ret = of_get_fb_videomode(np, fb_vm, i); + if (ret) + goto out; + ret = of_property_read_string(np, "hisilicon,pixel-format", + &pix_name); + if (ret) + goto out; + if (!strncmp(pix_name, "RGBA8888", 8)) + pix_fmt = IMG_PIXEL_FORMAT_ARGB8888; + else if (!strncmp(pix_name, "RGBX8888", 8)) + pix_fmt = IMG_PIXEL_FORMAT_RGB888; + else if (!strncmp(pix_name, "RGBA5551", 8)) + pix_fmt = IMG_PIXEL_FORMAT_ARGB1555; + else if (!strncmp(pix_name, "RGBX5551", 8)) + pix_fmt = IMG_PIXEL_FORMAT_RGB555; + else if (!strncmp(pix_name, "RGB565", 6)) + pix_fmt = IMG_PIXEL_FORMAT_RGB565; + else { + ret = -EINVAL; + goto out; + } + + set_pix_fmt(info, pix_fmt); + fb_videomode_to_var(var, fb_vm); + var->xres_virtual = fb_vm->xres; + var->yres_virtual = fb_vm->yres * 2; /* double buffering */ + var->grayscale = 0; + var->accel_flags = FB_ACCEL_NONE; + /* Now assume that video mode is only 1 in DTS. */ + //fb_add_videomode(&vm, &fb->modelist); + } + of_display_timings_exist(np); + + fix->type_aux = 0; + fix->type = FB_TYPE_PACKED_PIXELS; + fix->visual = FB_VISUAL_TRUECOLOR; + fix->xpanstep = 1; + fix->ypanstep = 1; + fix->ywrapstep = 0; + fix->mmio_start = 0; /* No MMIO address */ + fix->mmio_len = 0; /* No MMIO address */ + fix->accel = FB_ACCEL_NONE; /* No hardware accelerator */ + + length = var->xres_virtual * var->bits_per_pixel / 8; + fb->fix.line_length = length; + fb->fix.smem_len = ALIGN(fb->fix.line_length * fb->var.yres_virtual, + PAGE_SIZE); + hi3620_parse_dt(np, info); +out: + return ret; +} + +static irqreturn_t edc_irq_handler(int irq, void *data) +{ + struct hi3620fb_info *info = data; + u32 status; + + /* clear masked interrupts */ + status = readl_relaxed(info->reg_base + EDC_INTS); + status &= ~readl_relaxed(info->reg_base + EDC_INTE) & 0x3ffff; + + if (status & EDC_INT_BAS_END) { + status &= ~EDC_INT_BAS_END; + info->vsync_cnt++; + wake_up_interruptible(&info->wait_vsync); + } + writel_relaxed(status, info->reg_base + EDC_INTS); + return IRQ_HANDLED; +} + +static irqreturn_t ldi_irq_handler(int irq, void *data) +{ + struct hi3620fb_info *info = data; + u32 value; + + value = readl_relaxed(info->reg_base + LDI_ORG_INT); + writel_relaxed(value, info->reg_base + LDI_INT_CLR); + return IRQ_HANDLED; +} + +static irqreturn_t dsi_irq_handler(int irq, void *data) +{ + return IRQ_HANDLED; +} + +static int hi3620_fb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct hi3620fb_info *info; + struct resource *res; + struct fb_info *fb; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no memory resource defined\n"); + return -ENODEV; + } + + fb = framebuffer_alloc(sizeof(*info), dev); + if (!fb) { + dev_err(dev, "failed to allocate framebuffer\n"); + return -ENOMEM; + } + fb->dev = &pdev->dev; + info = fb->par; + info->fb = fb; + info->dev = &pdev->dev; + info->irq_edc = platform_get_irq_byname(pdev, "edc"); + if (info->irq_edc < 0) { + ret = -ENOENT; + goto err_fb; + } + info->irq_ldi = platform_get_irq_byname(pdev, "ldi"); + if (info->irq_ldi < 0) { + ret = -ENOENT; + goto err_fb; + } + info->irq_dsi = platform_get_irq_byname(pdev, "dsi"); + if (info->irq_dsi < 0) { + ret = -ENOENT; + goto err_fb; + } + + info->reg_base = devm_request_and_ioremap(&pdev->dev, res); + if (!info->reg_base) { + ret = -EADDRNOTAVAIL; + goto err_fb; + } + info->vedc = devm_regulator_get(dev, "vedc"); + if (IS_ERR_OR_NULL(info->vedc)) { + if (IS_ERR(info->vedc)) { + dev_err(dev, "failed to get vedc regulator\n"); + info->vedc = NULL; + } + } + info->clk_ldi = of_clk_get_by_name(np, "ldi"); + if (IS_ERR(info->clk_ldi)) { + dev_err(dev, "failed to get ldi clock\n"); + ret = PTR_ERR(info->clk_ldi); + goto err_fb; + } + info->clk_edc = of_clk_get_by_name(np, "edc"); + if (IS_ERR(info->clk_edc)) { + dev_err(dev, "failed to get edc clock\n"); + ret = PTR_ERR(info->clk_edc); + goto err_fb; + } + info->clk_dsi = of_clk_get_by_name(np, "dsi"); + if (IS_ERR(info->clk_dsi)) { + dev_err(dev, "failed to get dsi clock\n"); + ret = PTR_ERR(info->clk_dsi); + goto err_clk; + } + if (info->vedc) + regulator_enable(info->vedc); + clk_prepare_enable(info->clk_ldi); + //clk_prepare_enable(info->clk_edc); /* debug for keep display on after boot */ + + fb->fbops = &hi3620fb_ops; + fb->pseudo_palette = &hi3620fb_pseudo_palette; + + ret = hi3620_init_mode(np, fb); + if (ret) + goto err_clk; + +#if 1 + fb->screen_base = dma_alloc_coherent(fb->dev, fb->fix.smem_len, + &info->fb_start_dma, + GFP_KERNEL); + if (!fb->screen_base) { + dev_err(dev, "failed to allocate memory\n"); + return -ENOMEM; + } + fb->screen_size = fb->fix.smem_len; + fb->fix.smem_start = info->fb_start_dma; +#else + /* debug for remapping the display region in bootloader */ + info->fb_start_dma = PAGE_ALIGN(0x35b00130 + 0x40000000); + fb->fix.smem_start = info->fb_start_dma; + fb->screen_size = fb->fix.smem_len; + fb->screen_base = __va(info->fb_start_dma); +#endif + platform_set_drvdata(pdev, info); + + disable_edc(info->reg_base); + hi3620_mipi_init(dev); + hi3620_mipi_enable(dev); + set_graphics_start(fb, 0, 0); + hi3620fb_set_par(fb); + + + /* clear IRQ status & enable IRQ */ + writel_relaxed(0, info->reg_base + EDC_INTS); + /* enable interrupts of bus error */ + writel_relaxed(0x2ff, info->reg_base + EDC_INTE); + writel_relaxed(0x4, info->reg_base + LDI_INT_EN); /* disable front porch int for debugging */ + writel_relaxed(0x3fff, info->reg_base + LDI_INT_CLR); + + enable_edc(info->reg_base); + + ret = devm_request_irq(dev, info->irq_edc, edc_irq_handler, + IRQF_DISABLED, "edc", info); + if (ret < 0) { + dev_err(dev, "failed to request edc irq\n"); + goto err_clk; + } + ret = devm_request_irq(dev, info->irq_ldi, ldi_irq_handler, + IRQF_DISABLED, "ldi", info); + if (ret < 0) { + dev_err(dev, "failed to request ldi irq\n"); + goto err_clk; + } +#if 0 + ret = devm_request_irq(dev, info->irq_dsi, dsi_irq_handler, + IRQF_DISABLED, "dsi", info); + if (ret < 0) { + dev_err(dev, "failed to request dsi irq\n"); + goto err_clk; + } +#endif + ret = register_framebuffer(fb); + if (ret < 0) { + dev_err(dev, "failed to register hi3620 framebuffer\n"); + goto err_clk; + } + init_waitqueue_head(&info->wait_vsync); + /* clock rate of ldi */ + + return 0; +err_clk: + clk_disable_unprepare(info->clk_edc); + clk_disable_unprepare(info->clk_ldi); +err_fb: + framebuffer_release(fb); + return ret; +} + +static int hi3620_fb_remove(struct platform_device *pdev) +{ + hi3620_mipi_disable(&pdev->dev); + return 0; +} + +static void dump_reg(void __iomem *base, int offset, int size) +{ + int i; + for (i = 0; i < size; i += 4) { + pr_err("#[0x%x]:0x%x\n", (int)base + offset + i, readl_relaxed(base + offset + i)); + } +} + +static int dsi_init(void __iomem *base) +{ + writel_relaxed(1, base + DSI_PWR_UP); + writel_relaxed(7, base + DSI_PHY_RSTZ); + return 0; +} + +static int hi3620_fb_suspend(struct device *dev) +{ + unsigned int data; + struct hi3620fb_info *info = dev_get_drvdata(dev); + void __iomem *base = info->reg_base; + + dsi_enter_sleep(); + + info->graph_chan_addr = readl_relaxed(base + EDC_GRAPH_CHAN_ADDR); + info->video_chan_addr = readl_relaxed(base + EDC_VIDEO_CHAN_ADDR); + info->graph_chan_stride = readl_relaxed(base + EDC_GRAPH_CHAN_STRIDE); + info->video_chan_stride = readl_relaxed(base + EDC_VIDEO_CHAN_STRIDE); + info->graph_chan_xy = readl_relaxed(base + EDC_GRAPH_CHAN_XY); + info->video_chan_xy = readl_relaxed(base + EDC_VIDEO_CHAN_XY); + info->graph_chan_size = readl_relaxed(base + EDC_GRAPH_CHAN_SIZE); + info->video_chan_size = readl_relaxed(base + EDC_VIDEO_CHAN_SIZE); + + info->edc_disp_size = readl_relaxed(base + EDC_DISP_SIZE); + info->edc_disp_dpd = readl_relaxed(base + EDC_DISP_DPD); + info->ldi_hrz_ctrl0 = readl_relaxed(base + LDI_HRZ_CTRL0); + info->ldi_hrz_ctrl1 = readl_relaxed(base + LDI_HRZ_CTRL1); + info->ldi_vrt_ctrl0 = readl_relaxed(base + LDI_VRT_CTRL0); + info->ldi_vrt_ctrl1 = readl_relaxed(base + LDI_VRT_CTRL1); + info->ldi_plr_ctrl = readl_relaxed(base + LDI_PLR_CTRL); + info->ldi_dsp_size = readl_relaxed(base + LDI_DSP_SIZE); + info->ldi_ctrl = readl_relaxed(base + LDI_CTRL); + info->ldi_inte = readl_relaxed(base + LDI_INT_EN); + info->dsi_cmd_mod_ctrl = readl_relaxed(base + DSI_CMD_MOD_CTRL); + info->dsi_te_ctrl = readl_relaxed(base + DSI_TE_CTRL); + info->dsi_te_hs_num = readl_relaxed(base + DSI_TE_HS_NUM); + info->dsi_te_hs_wd = readl_relaxed(base + DSI_TE_HS_WD); + info->dsi_te_vs_wd = readl_relaxed(base + DSI_TE_VS_WD); + info->clkmgr_cfg = readl_relaxed(base + DSI_CLKMGR_CFG); + info->dpi_cfg = readl_relaxed(base + DSI_DPI_CFG); + info->pckhdl_cfg = readl_relaxed(base + DSI_PCKHDL_CFG); + info->vid_mode_cfg = readl_relaxed(base + DSI_VID_MODE_CFG); + info->vid_pkt_cfg = readl_relaxed(base + DSI_VID_PKT_CFG); + info->cmd_mode_cfg = readl_relaxed(base + DSI_CMD_MODE_CFG); + info->tmr_line_cfg = readl_relaxed(base + DSI_TMR_LINE_CFG); + info->vtiming_cfg = readl_relaxed(base + DSI_VTIMING_CFG); + info->to_cnt_cfg = readl_relaxed(base + DSI_TO_CNT_CFG); + info->phy_if_cfg = readl_relaxed(base + DSI_PHY_IF_CFG); + info->phy_if_ctrl = readl_relaxed(base + DSI_PHY_IF_CTRL); + info->edpi_cfg = readl_relaxed(base + DSI_EDPI_CFG); + info->lpcmd_time = readl_relaxed(base + DSI_LPCMD_TIME); + + /* disable channel */ + data = readl_relaxed(base + EDC_GRAPH_CHAN_CTRL); + info->graph_chan_ctrl = data; + data &= ~EDC_GRAPH_CHAN_CTRL_EN; + writel_relaxed(data, base + EDC_GRAPH_CHAN_CTRL); + data = readl_relaxed(base + EDC_VIDEO_CHAN_CTRL); + info->video_chan_ctrl = data; + data &= ~EDC_VIDEO_CHAN_CTRL_EN; + writel_relaxed(data, base + EDC_VIDEO_CHAN_CTRL); + + /* disable all EDC interrupts */ + info->edc_inte = readl_relaxed(base + EDC_INTE); + writel_relaxed(0xffffffff, base + EDC_INTE); + /* clear EDC interrupt status */ + writel_relaxed(0, base + EDC_INTS); + + /* disable EDC */ + data = readl_relaxed(base + EDC_DISP_CTL); + info->edc_disp_ctrl = data; + data &= ~EDC_CTRL_EN; + writel_relaxed(data, base + EDC_DISP_CTL); + update_edc(base); + return 0; +} + +static int hi3620_fb_resume(struct device *dev) +{ + struct hi3620fb_info *info = dev_get_drvdata(dev); + void __iomem *base = info->reg_base; + + dsi_init(base); + hi3620_mipi_enable(info->dev); + writel_relaxed(info->clkmgr_cfg, base + DSI_CLKMGR_CFG); + set_graphics_start(info->fb, 0, 0); + hi3620fb_set_par(info->fb); + writel_relaxed(info->ldi_inte, base + LDI_INT_EN); + writel_relaxed(0x3fff, info->reg_base + LDI_INT_CLR); + + /* clear EDC interrupt status */ + writel_relaxed(0, base + EDC_INTS); + /* enable all EDC interrupts */ + writel_relaxed(info->edc_inte, base + EDC_INTE); + + writel_relaxed(info->ldi_ctrl, base + LDI_CTRL); + + enable_edc(base); + + /* enable channel */ + writel_relaxed(info->graph_chan_ctrl, + base + EDC_GRAPH_CHAN_CTRL); + writel_relaxed(info->video_chan_ctrl, + base + EDC_VIDEO_CHAN_CTRL); + + dsi_exit_sleep(); + + return 0; +} + +static const struct dev_pm_ops hi3620_fb_pm_ops = { + .suspend = hi3620_fb_suspend, + .resume = hi3620_fb_resume, +}; + +static const struct of_device_id hi3620_fb_of_match[] = { + { .compatible = "hisilicon,hi3620-fb", }, + {}, +}; +MODULE_DEVICE_TABLE(of, hi3620_fb_of_match); + +static struct platform_driver hi3620_fb_driver = { + .probe = hi3620_fb_probe, + .remove = hi3620_fb_remove, + .driver = { + .name = "hi3620-fb", + .owner = THIS_MODULE, + .pm = &hi3620_fb_pm_ops, + .of_match_table = hi3620_fb_of_match, + }, +}; +module_platform_driver(hi3620_fb_driver); diff --git a/drivers/video/hisilicon/hi3620_fb.h b/drivers/video/hisilicon/hi3620_fb.h new file mode 100644 index 000000000000..213d0695a8ab --- /dev/null +++ b/drivers/video/hisilicon/hi3620_fb.h @@ -0,0 +1,208 @@ +#ifndef __HI3620FB_H +#define __HI3620FB_H + +#define EDC_ID 0x000 + +/* Channel 1 */ +#define EDC_GRAPH_CHAN_ADDR 0x004 +#define EDC_GRAPH_CHAN_STRIDE 0x00c +#define EDC_GRAPH_CHAN_XY 0x010 +#define EDC_GRAPH_CHAN_SIZE 0x014 +#define EDC_GRAPH_CHAN_CTRL 0x018 +#define EDC_GRAPH_CHAN_CKEY_MIN 0x01c +#define EDC_GRAPH_CHAN_CKEY_MAX 0x020 + +/* Channel 2 */ +#define EDC_VIDEO_CHAN_ADDR 0x024 +#define EDC_VIDEO_CHAN_STRIDE 0x02c +#define EDC_VIDEO_CHAN_XY 0x030 +#define EDC_VIDEO_CHAN_SIZE 0x034 +#define EDC_VIDEO_CHAN_CTRL 0x038 +#define EDC_VIDEO_CHAN_CKEY_MIN 0x03c +#define EDC_VIDEO_CHAN_CKEY_MAX 0x040 + +#define EDC_GRAPH_CHAN_CTRL_EN (1 << 24) +#define EDC_VIDEO_CHAN_CTRL_EN (1 << 22) +#define EDC_CHAN_CTRL_BGR (1 << 19) + +#define EDC_DISP_SIZE 0x090 +#define EDC_DISP_CTL 0x094 +#define EDC_DISP_DPD 0x098 +#define EDC_STS 0x09c +#define EDC_INTS 0x0a0 +#define EDC_INTE 0x0a4 + +#define LDI_HRZ_CTRL0 0x800 +#define LDI_HRZ_CTRL1 0x804 +#define LDI_VRT_CTRL0 0x808 +#define LDI_VRT_CTRL1 0x80c +#define LDI_PLR_CTRL 0x810 +#define LDI_DSP_SIZE 0x814 +#define LDI_INT_EN 0x81c +#define LDI_CTRL 0x820 +#define LDI_ORG_INT 0x824 +#define LDI_MSK_INT 0x828 +#define LDI_INT_CLR 0x82c +#define LDI_WORK_MODE 0x830 +#define LDI_HDMI_DSI_GT 0x834 + +#define DSI_CMD_MOD_CTRL 0x83c +#define DSI_TE_CTRL 0x840 +#define DSI_TE_HS_NUM 0x844 +#define DSI_TE_HS_WD 0x848 +#define DSI_TE_VS_WD 0x84c + +#define DSI_PWR_UP 0x904 +#define DSI_CLKMGR_CFG 0x908 +#define DSI_DPI_CFG 0x90c +#define DSI_PCKHDL_CFG 0x918 +#define DSI_VID_MODE_CFG 0x91c +#define DSI_VID_PKT_CFG 0x920 +#define DSI_CMD_MODE_CFG 0x924 +#define DSI_TMR_LINE_CFG 0x928 +#define DSI_VTIMING_CFG 0x92c +#define DSI_PHY_TMR_CFG 0x930 +#define DSI_GEN_HDR 0x934 +#define DSI_GEN_PLD_DATA 0x938 +#define DSI_CMD_PKT_STATUS 0x93c +#define DSI_TO_CNT_CFG 0x940 +#define DSI_ERROR_ST0 0x944 +#define DSI_ERROR_ST1 0x948 +#define DSI_ERROR_MSK0 0x94c +#define DSI_ERROR_MSK1 0x950 +#define DSI_PHY_RSTZ 0x954 +#define DSI_PHY_IF_CFG 0x958 +#define DSI_PHY_IF_CTRL 0x95c +#define DSI_PHY_STATUS 0x960 +#define DSI_PHY_TST_CTRL0 0x964 +#define DSI_PHY_TST_CTRL1 0x968 +#define DSI_EDPI_CFG 0x96c +#define DSI_LPCMD_TIME 0x970 + +/* bits field of DSI_CMD_PKT_STATUS register */ +#define GEN_RD_CMD_BUSY (1 << 6) +#define GEN_PLD_R_FULL (1 << 5) +#define GEN_PLD_R_EMPTY (1 << 4) +#define GEN_PLD_W_FULL (1 << 3) + +/* bits field of EDC_DISP_CTL register */ +#define EDC_CTRL_CLK_EN (1 << 31) +#define EDC_CTRL_FRAME_END_START (1 << 30) +#define EDC_CTRL_EN (1 << 10) +#define EDC_CTRL_CFG_OK (1 << 1) +#define EDC_CTRL_CFG_OK_SEL (1 << 0) + +/* bits field of EDC_INTS/EDC_INTE register */ +#define EDC_INT_BAS_END (1 << 6) + +/* bits field of LDI_CTRL register */ +#define LDI_CTRL_SHUTDOWN (1 << 15) +#define LDI_CTRL_COLOR_MODE (1 << 14) +#define LDI_CTRL_BGR (1 << 13) +#define LDI_CTRL_DATA_GATE_EN (1 << 2) +#define LDI_CTRL_DISP_MODE (1 << 1) +#define LDI_CTRL_EN (1 << 0) + +/* bits field of LDI_WORK_MODE register */ +#define LDI_WORK_MODE_EN (1 << 0) + +/* bits field of LDI_PLT_CTRL register */ +#define LDI_POLARITY_MASK 0xf +#define LDI_DATAEN_POLARITY (1 << 3) +#define LDI_PIXELCLK_POLARITY (1 << 2) +#define LDI_HSYNC_POLARITY (1 << 1) +#define LDI_VSYNC_POLARITY (1 << 0) + +/* bits field of DSI_DPI_CFG register */ +#define DSI_DPI_POLARITY_MASK (0x1f << 5) +#define DSI_DPI_COLORM_POLARITY (1 << 9) +#define DSI_DPI_SHUTD_POLARITY (1 << 8) +#define DSI_DPI_HSYNC_POLARITY (1 << 7) +#define DSI_DPI_VSYNC_POLARITY (1 << 6) +#define DSI_DPI_DATAEN_POLARITY (1 << 5) + +/* bits field of DSI_PHY_STATUS register */ +#define DSI_PHY_STOP_STATE3_LANE (1 << 12) +#define DSI_PHY_STOP_STATE2_LANE (1 << 9) +#define DSI_PHY_STOP_STATE1_LANE (1 << 7) +#define DSI_PHY_STOP_STATE0_LANE (1 << 4) +#define DSI_PHY_LOCK (1 << 0) + +enum { + IMG_PIXEL_FORMAT_ARGB1555 = 0, + IMG_PIXEL_FORMAT_RGB555, + IMG_PIXEL_FORMAT_RGB565, + IMG_PIXEL_FORMAT_RGB888, + IMG_PIXEL_FORMAT_ARGB8888, +}; + +struct hi3620fb_info { + struct fb_info *fb; + struct device *dev; + void __iomem *reg_base; + struct clk *clk_ldi; + struct clk *clk_edc; + struct clk *clk_dsi; + int irq_edc; + int irq_ldi; + int irq_dsi; + struct regulator *vedc; + dma_addr_t fb_start_dma; + int pix_fmt; + int dsi_rate; /* dsi bit clock rate */ + const char *mipi_mode_name; + int lane_cnt; + int color_mode; + wait_queue_head_t wait_vsync; + int vsync_cnt; + struct mutex dsi_mutex; + unsigned int graph_chan_addr; + unsigned int graph_chan_stride; + unsigned int graph_chan_xy; + unsigned int graph_chan_size; + unsigned int graph_chan_ctrl; + unsigned int video_chan_addr; + unsigned int video_chan_stride; + unsigned int video_chan_xy; + unsigned int video_chan_size; + unsigned int video_chan_ctrl; + unsigned int edc_inte; + unsigned int edc_disp_size; + unsigned int edc_disp_dpd; + unsigned int edc_disp_ctrl; + unsigned int ldi_hrz_ctrl0; + unsigned int ldi_hrz_ctrl1; + unsigned int ldi_vrt_ctrl0; + unsigned int ldi_vrt_ctrl1; + unsigned int ldi_plr_ctrl; + unsigned int ldi_dsp_size; + unsigned int ldi_inte; + unsigned int ldi_ctrl; + unsigned int ldi_work_mode; + unsigned int ldi_hdmi_dsi_gt; + unsigned int dsi_cmd_mod_ctrl; + unsigned int dsi_te_ctrl; + unsigned int dsi_te_hs_num; + unsigned int dsi_te_hs_wd; + unsigned int dsi_te_vs_wd; + unsigned int clkmgr_cfg; + unsigned int dpi_cfg; + unsigned int pckhdl_cfg; + unsigned int vid_mode_cfg; + unsigned int vid_pkt_cfg; + unsigned int cmd_mode_cfg; + unsigned int tmr_line_cfg; + unsigned int vtiming_cfg; + unsigned int to_cnt_cfg; + unsigned int phy_rstz; + unsigned int phy_if_cfg; + unsigned int phy_if_ctrl; + unsigned int edpi_cfg; + unsigned int lpcmd_time; +}; + +extern int hi3620_mipi_init(struct device *dev); +extern int hi3620_mipi_enable(struct device *dev); +extern int hi3620_mipi_disable(struct device *dev); +extern int send_generic_packet(u8 *cmd, int len); +#endif /* __HI3620FB_H */ diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h index dd7adff76e81..8138c94409f3 100644 --- a/include/linux/clk-private.h +++ b/include/linux/clk-private.h @@ -33,8 +33,11 @@ struct clk { const char **parent_names; struct clk **parents; u8 num_parents; + u8 new_parent_index; unsigned long rate; unsigned long new_rate; + struct clk *new_parent; + struct clk *new_child; unsigned long flags; unsigned int enable_count; unsigned int prepare_count; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 11860985fecb..227a20d5b9b2 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -27,6 +27,7 @@ #define CLK_IS_ROOT BIT(4) /* root clk, has no parent */ #define CLK_IS_BASIC BIT(5) /* Basic clk, can't do a to_clk_foo() */ #define CLK_GET_RATE_NOCACHE BIT(6) /* do not use the cached clk rate */ +#define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */ struct clk_hw; @@ -79,6 +80,10 @@ struct clk_hw; * @round_rate: Given a target rate as input, returns the closest rate actually * supported by the clock. * + * @determine_rate: Given a target rate as input, returns the closest rate + * actually supported by the clock, and optionally the parent clock + * that should be used to provide the clock rate. + * * @get_parent: Queries the hardware to determine the parent of a clock. The * return value is a u8 which specifies the index corresponding to * the parent clock. This index can be applied to either the @@ -126,6 +131,9 @@ struct clk_ops { unsigned long parent_rate); long (*round_rate)(struct clk_hw *hw, unsigned long, unsigned long *); + long (*determine_rate)(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate, + struct clk **best_parent_clk); int (*set_parent)(struct clk_hw *hw, u8 index); u8 (*get_parent)(struct clk_hw *hw); int (*set_rate)(struct clk_hw *hw, unsigned long, @@ -210,6 +218,10 @@ void of_fixed_clk_setup(struct device_node *np); * CLK_GATE_SET_TO_DISABLE - by default this clock sets the bit at bit_idx to * enable the clock. Setting this flag does the opposite: setting the bit * disable the clock and clearing it enables the clock + * CLK_GATE_HIWORD_MASK - The gate settings are only in lower 16-bit + * of this register, and mask of gate bits are in higher 16-bit of this + * register. While setting the gate bits, higher 16-bit should also be + * updated to indicate changing gate bits. */ struct clk_gate { struct clk_hw hw; @@ -220,6 +232,7 @@ struct clk_gate { }; #define CLK_GATE_SET_TO_DISABLE BIT(0) +#define CLK_GATE_HIWORD_MASK BIT(1) extern const struct clk_ops clk_gate_ops; struct clk *clk_register_gate(struct device *dev, const char *name, @@ -299,6 +312,7 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name, * Flags: * CLK_MUX_INDEX_ONE - register index starts at 1, not 0 * CLK_MUX_INDEX_BIT - register index is a single bit (power of two) + * CLK_MUX_HIWORD_MASK - register contains high 16-bit as mask field */ struct clk_mux { struct clk_hw hw; @@ -312,6 +326,7 @@ struct clk_mux { #define CLK_MUX_INDEX_ONE BIT(0) #define CLK_MUX_INDEX_BIT BIT(1) +#define CLK_MUX_HIWORD_MASK BIT(2) extern const struct clk_ops clk_mux_ops; @@ -403,6 +418,7 @@ const char *__clk_get_name(struct clk *clk); struct clk_hw *__clk_get_hw(struct clk *clk); u8 __clk_get_num_parents(struct clk *clk); struct clk *__clk_get_parent(struct clk *clk); +struct clk *clk_get_parent_by_index(struct clk *clk, u8 index); unsigned int __clk_get_enable_count(struct clk *clk); unsigned int __clk_get_prepare_count(struct clk *clk); unsigned long __clk_get_rate(struct clk *clk); @@ -410,6 +426,9 @@ unsigned long __clk_get_flags(struct clk *clk); bool __clk_is_prepared(struct clk *clk); bool __clk_is_enabled(struct clk *clk); struct clk *__clk_lookup(const char *name); +long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate, + struct clk **best_parent_p); /* * FIXME clock api without lock protection diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 96d3e4ab11a9..4e1c843895e7 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -1000,6 +1000,7 @@ int dma_async_device_register(struct dma_device *device); void dma_async_device_unregister(struct dma_device *device); void dma_run_dependencies(struct dma_async_tx_descriptor *tx); struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type); +struct dma_chan *dma_get_slave_channel(struct dma_chan *chan); struct dma_chan *net_dma_find_channel(void); #define dma_request_channel(mask, x, y) __dma_request_channel(&(mask), x, y) #define dma_request_slave_channel_compat(mask, x, y, dev, name) \ 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/mfd/r63306.h b/include/linux/mfd/r63306.h new file mode 100644 index 000000000000..c266c99966ad --- /dev/null +++ b/include/linux/mfd/r63306.h @@ -0,0 +1,45 @@ + +#ifndef __R63306_H +#define __R63306_H + +struct r63306_chip { + struct device *dev; + struct mutex *lock; +}; + +struct r63306_pwm { + int divider; + int duty_cycle; + int pwm_wm; +}; + +struct r63306_device_id { + int vendor; + int product; + int revision; +}; + +struct r63306_cabc { + bool cabc_on; + bool pwm_on; + bool pfm_on; + bool ledpwm_pin; + bool ledpwm_pol; +}; + +struct r63306_cabc_param { + int backlight; +}; + +extern int dsi_reset(void); +extern int dsi_enter_sleep(void); +extern int dsi_exit_sleep(void); +extern int dsi_get_pwm(struct r63306_pwm *pwm); +extern int dsi_set_pwm(struct r63306_pwm *pwm); +extern int dsi_get_cabc(struct r63306_cabc *cabc); +extern int dsi_set_cabc(struct r63306_cabc *cabc); +extern int dsi_get_cabc_param(struct r63306_cabc_param *cabc); +extern int dsi_set_cabc_param(struct r63306_cabc_param *cabc); +extern int dsi_get_backlight(int *level); + +#endif /* __R63306_H */ diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 39613b9a6fc5..49fb132ab2f7 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -188,6 +188,9 @@ extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort); extern void mmc_release_host(struct mmc_host *host); extern int mmc_try_claim_host(struct mmc_host *host); +extern void mmc_get_card(struct mmc_card *card); +extern void mmc_put_card(struct mmc_card *card); + extern int mmc_flush_cache(struct mmc_card *); extern int mmc_detect_card_removed(struct mmc_host *host); diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 198f0fa44e9f..3e6b68620473 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -185,6 +185,7 @@ struct dw_mci { u32 quirks; struct regulator *vmmc; /* Power regulator */ + struct regulator *vqmmc; /* Signaling regulator (vccq) */ unsigned long irq_flags; /* IRQ flags */ int irq; }; @@ -246,7 +247,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); /* diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 66b465927602..5621c9c581d3 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -240,7 +240,7 @@ struct mmc_host { #define MMC_CAP_SPI (1 << 4) /* Talks only SPI protocols */ #define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */ #define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */ - +#define MMC_CAP_AGGRESSIVE_PM (1 << 7) /* Suspend (e)MMC/SD at idle */ #define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */ #define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */ #define MMC_CAP_ERASE (1 << 10) /* Allow erase/trim commands */ diff --git a/include/linux/nmi.h b/include/linux/nmi.h index c8f8aa0383e5..db50840e6355 100644 --- a/include/linux/nmi.h +++ b/include/linux/nmi.h @@ -14,11 +14,8 @@ * may be used to reset the timeout - for code which intentionally * disables interrupts for a long time. This call is stateless. */ -#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR_NMI) -#include <asm/nmi.h> -#endif - #if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR) +#include <asm/nmi.h> extern void touch_nmi_watchdog(void); #else static inline void touch_nmi_watchdog(void) diff --git a/include/linux/platform_data/hi3620-dsi.h b/include/linux/platform_data/hi3620-dsi.h new file mode 100644 index 000000000000..ebc141cf482a --- /dev/null +++ b/include/linux/platform_data/hi3620-dsi.h @@ -0,0 +1,53 @@ +/* + * Hisilicon Hi3620 MIPI DSI head file + * + * Copyright (c) 2013 Hisilicon Limited. + * Copyright (c) 2013 Linaro Limited. + * + * Author: Haojian Zhuang <haojian.zhuang@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef __HI3620_DSI_H +#define __HI3620_DSI_H + +enum DATA_TYPE { + GEN_SHORT_WR_PARAM0 = 0x03, + GEN_SHORT_WR_PARAM1 = 0x13, + GEN_SHORT_WR_PARAM2 = 0x23, + GEN_RD_PARAM0 = 0x04, + GEN_RD_PARAM1 = 0x14, + GEN_RD_PARAM2 = 0x24, + DCS_SHORT_WR_PARAM0 = 0x05, + DCS_SHORT_WR_PARAM1 = 0x15, + DCS_RD_PARAM0 = 0x06, + SET_MAX_PKT = 0x37, + NULL_PKT = 0x09, + BLANKING_PKT = 0x19, + GEN_LONG_WR = 0x29, + DCS_LONG_WR = 0x39, + PACKED_PIXEL_16B = 0x0e, + PACKED_PIXEL_18B = 0x1e, + LOOSELY_PACKED_PIXEL_18B = 0x2e, + PACKED_PIXEL_24B = 0x3e, +}; + +int hi3620_dsi_phy_write(void __iomem *reg_base, unsigned char addr, + unsigned char data); +unsigned char hi3620_dsi_phy_read(void __iomem *reg_base, unsigned char addr); + +#endif diff --git a/include/uapi/linux/fb.h b/include/uapi/linux/fb.h index fb795c3b3c17..864e01e9613c 100644 --- a/include/uapi/linux/fb.h +++ b/include/uapi/linux/fb.h @@ -226,6 +226,11 @@ struct fb_bitfield { #define FB_VMODE_SMOOTH_XPAN 512 /* smooth xpan possible (internally used) */ #define FB_VMODE_CONUPDATE 512 /* don't update x/yoffset */ +#define FB_FLAG_DE_HIGH 1 /* data enable high active */ +#define FB_FLAG_DE_LOW 2 /* data enable low active */ +#define FB_FLAG_PIXDATA_POSEDGE 4 /* pixdata postive edge */ +#define FB_FLAG_PIXDATA_NEGEDGE 8 /* pixdata negative edge */ + /* * Display rotation support */ diff --git a/kernel/watchdog.c b/kernel/watchdog.c index e092e5a6cdd7..05039e348f07 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -45,11 +45,6 @@ static DEFINE_PER_CPU(unsigned long, soft_lockup_hrtimer_cnt); static DEFINE_PER_CPU(bool, hard_watchdog_warn); static DEFINE_PER_CPU(bool, watchdog_nmi_touch); static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts_saved); -#endif -#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU -static cpumask_t __read_mostly watchdog_cpus; -#endif -#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI static DEFINE_PER_CPU(struct perf_event *, watchdog_ev); #endif @@ -183,7 +178,7 @@ void touch_softlockup_watchdog_sync(void) __raw_get_cpu_var(watchdog_touch_ts) = 0; } -#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI +#ifdef CONFIG_HARDLOCKUP_DETECTOR /* watchdog detector functions */ static int is_hardlockup(void) { @@ -197,76 +192,6 @@ static int is_hardlockup(void) } #endif -#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU -static unsigned int watchdog_next_cpu(unsigned int cpu) -{ - cpumask_t cpus = watchdog_cpus; - unsigned int next_cpu; - - next_cpu = cpumask_next(cpu, &cpus); - if (next_cpu >= nr_cpu_ids) - next_cpu = cpumask_first(&cpus); - - if (next_cpu == cpu) - return nr_cpu_ids; - - return next_cpu; -} - -static int is_hardlockup_other_cpu(unsigned int cpu) -{ - unsigned long hrint = per_cpu(hrtimer_interrupts, cpu); - - if (per_cpu(hrtimer_interrupts_saved, cpu) == hrint) - return 1; - - per_cpu(hrtimer_interrupts_saved, cpu) = hrint; - return 0; -} - -static void watchdog_check_hardlockup_other_cpu(void) -{ - unsigned int next_cpu; - - /* - * Test for hardlockups every 3 samples. The sample period is - * watchdog_thresh * 2 / 5, so 3 samples gets us back to slightly over - * watchdog_thresh (over by 20%). - */ - if (__this_cpu_read(hrtimer_interrupts) % 3 != 0) - return; - - /* check for a hardlockup on the next cpu */ - next_cpu = watchdog_next_cpu(smp_processor_id()); - if (next_cpu >= nr_cpu_ids) - return; - - smp_rmb(); - - if (per_cpu(watchdog_nmi_touch, next_cpu) == true) { - per_cpu(watchdog_nmi_touch, next_cpu) = false; - return; - } - - if (is_hardlockup_other_cpu(next_cpu)) { - /* only warn once */ - if (per_cpu(hard_watchdog_warn, next_cpu) == true) - return; - - if (hardlockup_panic) - panic("Watchdog detected hard LOCKUP on cpu %u", next_cpu); - else - WARN(1, "Watchdog detected hard LOCKUP on cpu %u", next_cpu); - - per_cpu(hard_watchdog_warn, next_cpu) = true; - } else { - per_cpu(hard_watchdog_warn, next_cpu) = false; - } -} -#else -static inline void watchdog_check_hardlockup_other_cpu(void) { return; } -#endif - static int is_softlockup(unsigned long touch_ts) { unsigned long now = get_timestamp(); @@ -278,7 +203,7 @@ static int is_softlockup(unsigned long touch_ts) return 0; } -#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI +#ifdef CONFIG_HARDLOCKUP_DETECTOR static struct perf_event_attr wd_hw_attr = { .type = PERF_TYPE_HARDWARE, @@ -326,7 +251,7 @@ static void watchdog_overflow_callback(struct perf_event *event, __this_cpu_write(hard_watchdog_warn, false); return; } -#endif /* CONFIG_HARDLOCKUP_DETECTOR_NMI */ +#endif /* CONFIG_HARDLOCKUP_DETECTOR */ static void watchdog_interrupt_count(void) { @@ -346,9 +271,6 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) /* kick the hardlockup detector */ watchdog_interrupt_count(); - /* test for hardlockups on the next cpu */ - watchdog_check_hardlockup_other_cpu(); - /* kick the softlockup detector */ wake_up_process(__this_cpu_read(softlockup_watchdog)); @@ -473,7 +395,7 @@ static void watchdog(unsigned int cpu) __touch_watchdog(); } -#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI +#ifdef CONFIG_HARDLOCKUP_DETECTOR /* * People like the simple clean cpu node info on boot. * Reduce the watchdog noise by only printing messages @@ -549,44 +471,9 @@ static void watchdog_nmi_disable(unsigned int cpu) return; } #else -#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU -static int watchdog_nmi_enable(unsigned int cpu) -{ - /* - * The new cpu will be marked online before the first hrtimer interrupt - * runs on it. If another cpu tests for a hardlockup on the new cpu - * before it has run its first hrtimer, it will get a false positive. - * Touch the watchdog on the new cpu to delay the first check for at - * least 3 sampling periods to guarantee one hrtimer has run on the new - * cpu. - */ - per_cpu(watchdog_nmi_touch, cpu) = true; - smp_wmb(); - cpumask_set_cpu(cpu, &watchdog_cpus); - return 0; -} - -static void watchdog_nmi_disable(unsigned int cpu) -{ - unsigned int next_cpu = watchdog_next_cpu(cpu); - - /* - * Offlining this cpu will cause the cpu before this one to start - * checking the one after this one. If this cpu just finished checking - * the next cpu and updating hrtimer_interrupts_saved, and then the - * previous cpu checks it within one sample period, it will trigger a - * false positive. Touch the watchdog on the next cpu to prevent it. - */ - if (next_cpu < nr_cpu_ids) - per_cpu(watchdog_nmi_touch, next_cpu) = true; - smp_wmb(); - cpumask_clear_cpu(cpu, &watchdog_cpus); -} -#else static int watchdog_nmi_enable(unsigned int cpu) { return 0; } static void watchdog_nmi_disable(unsigned int cpu) { return; } -#endif /* CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU */ -#endif /* CONFIG_HARDLOCKUP_DETECTOR_NMI */ +#endif /* CONFIG_HARDLOCKUP_DETECTOR */ /* prepare/enable/disable routines */ /* sysctl functions */ diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index d317c1ad62ab..66bde7034df8 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -191,27 +191,15 @@ config LOCKUP_DETECTOR The overhead should be minimal. A periodic hrtimer runs to generate interrupts and kick the watchdog task every 4 seconds. An NMI is generated every 10 seconds or so to check for hardlockups. - If NMIs are not available on the platform, every 12 seconds the - hrtimer interrupt on one cpu will be used to check for hardlockups - on the next cpu. The frequency of hrtimer and NMI events and the soft and hard lockup thresholds can be controlled through the sysctl watchdog_thresh. -config HARDLOCKUP_DETECTOR_NMI +config HARDLOCKUP_DETECTOR def_bool y depends on LOCKUP_DETECTOR && !HAVE_NMI_WATCHDOG depends on PERF_EVENTS && HAVE_PERF_EVENTS_NMI -config HARDLOCKUP_DETECTOR_OTHER_CPU - def_bool y - depends on LOCKUP_DETECTOR && SMP - depends on !HARDLOCKUP_DETECTOR_NMI && !HAVE_NMI_WATCHDOG - -config HARDLOCKUP_DETECTOR - def_bool y - depends on HARDLOCKUP_DETECTOR_NMI || HARDLOCKUP_DETECTOR_OTHER_CPU - config BOOTPARAM_HARDLOCKUP_PANIC bool "Panic (Reboot) On Hard Lockups" depends on HARDLOCKUP_DETECTOR diff --git a/linaro/configs/android.conf b/linaro/configs/android.conf index 538adefff80f..86ebfe2a380b 100644 --- a/linaro/configs/android.conf +++ b/linaro/configs/android.conf @@ -1,5 +1,6 @@ CONFIG_IPV6=y # CONFIG_IPV6_SIT is not set +# CONFIG_INITRAMFS_SOURCE is not set CONFIG_PANIC_TIMEOUT=0 CONFIG_HAS_WAKELOCK=y CONFIG_WAKELOCK=y |