From 5a18dc261d197de5e41718a751dfada39c47624b Mon Sep 17 00:00:00 2001 From: wdc Date: Fri, 24 Oct 2014 11:11:18 +0800 Subject: [PATCH] rk3368: new pinctrl, io-domain driver supported --- arch/arm64/boot/dts/rk3368.dtsi | 935 ++++++++++ drivers/pinctrl/pinctrl-rk3368.c | 2329 ++++++++++++++++++++++++ drivers/power/avs/Kconfig | 8 + drivers/power/avs/Makefile | 1 + drivers/power/avs/rockchip-io-domain.c | 407 +++++ 5 files changed, 3680 insertions(+) create mode 100644 arch/arm64/boot/dts/rk3368.dtsi create mode 100755 drivers/pinctrl/pinctrl-rk3368.c create mode 100755 drivers/power/avs/rockchip-io-domain.c diff --git a/arch/arm64/boot/dts/rk3368.dtsi b/arch/arm64/boot/dts/rk3368.dtsi new file mode 100644 index 000000000000..522f7996da31 --- /dev/null +++ b/arch/arm64/boot/dts/rk3368.dtsi @@ -0,0 +1,935 @@ +/dts-v1/; + +#include +#include +#include +#include + +#include "skeleton.dtsi" + +/ { + compatible = "rockchip,rk3368"; + + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + xin24m: xin24m { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24000000>; + clock-output-names = "xin24m"; + }; + + aliases { + serial0 = &uart_bt; + serial1 = &uart_bb; + serial2 = &uart_dbg; + serial3 = &uart_gps; + serial4 = &uart_exp; + i2c0 = &i2c0; + i2c1 = &i2c1; + i2c2 = &i2c2; + i2c3 = &i2c3; + i2c4 = &i2c4; + i2c5 = &i2c5; + spi0 = &spi0; + spi1 = &spi1; + spi2 = &spi2; + }; + + cpus { + #address-cells = <2>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53","arm,armv8"; + reg = <0x0 0x0>; + }; + }; + + chosen { + bootargs = "console=ttyS2 earlyprintk=uart8250-32bit,0xff690000"; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + ; + clock-frequency = <24000000>; + }; + + memory@00000000 { + device_type = "memory"; + reg = <0x0 0x00000000 0x0 0x20000000>; + }; + + gic: interrupt-controller@ffb70000 { + compatible = "arm,cortex-a15-gic"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0x0 0xffb71000 0 0x1000>, + <0x0 0xffb72000 0 0x1000>; + }; + + pmu_grf: syscon@ff738000 { + compatible = "rockchip,rk3388-pmu-grf", "syscon"; + reg = <0x0 0xff738000 0x0 0x100>; + }; + + sgrf: syscon@ff740000 { + compatible = "rockchip,rk3388-sgrf", "syscon"; + reg = <0x0 0xff740000 0x0 0x1000>; + + }; + + grf: syscon@ff770000 { + compatible = "rockchip,rk3388-grf", "syscon"; + reg = <0x0 0xff770000 0x0 0x1000>; + }; + + i2c0: i2c@ff650000 { + compatible = "rockchip,rk30-i2c"; + reg = <0x0 0xff650000 0x0 0x1000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&i2c0_xfer>; + pinctrl-1 = <&i2c0_gpio>; + gpios = <&gpio0 GPIO_A6 GPIO_ACTIVE_LOW>, <&gpio0 GPIO_A7 GPIO_ACTIVE_LOW>; + //clocks = <&clk_gates10 2>; + rockchip,check-idle = <1>; + status = "disabled"; + }; + + i2c1: i2c@ff140000 { + compatible = "rockchip,rk30-i2c"; + reg = <0x0 0xff140000 0x0 0x1000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&i2c1_xfer>; + pinctrl-1 = <&i2c1_gpio>; + gpios = <&gpio2 GPIO_C5 GPIO_ACTIVE_LOW>, <&gpio2 GPIO_C6 GPIO_ACTIVE_LOW>; + //clocks = <&clk_gates10 3>; + rockchip,check-idle = <1>; + status = "disabled"; + }; + + i2c2: i2c@ff660000 { + compatible = "rockchip,rk30-i2c"; + reg = <0x0 0xff660000 0x0 0x1000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&i2c2_xfer>; + pinctrl-1 = <&i2c2_gpio>; + gpios = <&gpio3 GPIO_D7 GPIO_ACTIVE_LOW>, <&gpio0 GPIO_B1 GPIO_ACTIVE_LOW>; + //clocks = <&clk_gates6 13>; + rockchip,check-idle = <1>; + status = "disabled"; + }; + + i2c3: i2c@ff150000 { + compatible = "rockchip,rk30-i2c"; + reg = <0x0 0xff150000 0x0 0x1000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&i2c3_xfer>; + pinctrl-1 = <&i2c3_gpio>; + gpios = <&gpio1 GPIO_C1 GPIO_ACTIVE_LOW>, <&gpio1 GPIO_C0 GPIO_ACTIVE_LOW>; + //clocks = <&clk_gates6 14>; + rockchip,check-idle = <1>; + status = "disabled"; + }; + + i2c4: i2c@ff160000 { + compatible = "rockchip,rk30-i2c"; + reg = <0x0 0xff160000 0x0 0x1000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&i2c4_xfer>; + pinctrl-1 = <&i2c4_gpio>; + gpios = <&gpio3 GPIO_D0 GPIO_ACTIVE_LOW>, <&gpio3 GPIO_D1 GPIO_ACTIVE_LOW>; + //clocks = <&clk_gates6 15>; + rockchip,check-idle = <1>; + status = "disabled"; + }; + + i2c5: i2c@ff170000 { + compatible = "rockchip,rk30-i2c"; + reg = <0x0 0xff170000 0x0 0x1000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&i2c5_xfer>; + pinctrl-1 = <&i2c5_gpio>; + gpios = <&gpio3 GPIO_D2 GPIO_ACTIVE_LOW>, <&gpio3 GPIO_D3 GPIO_ACTIVE_LOW>; + //clocks = <&clk_gates7 0>; + rockchip,check-idle = <1>; + status = "disabled"; + }; + + uart_bt: serial@ff180000 { + compatible = "rockchip,serial"; + reg = <0x0 0xff180000 0x0 0x100>; + interrupts = ; + clock-frequency = <24000000>; + clocks = <&xin24m>, <&xin24m>; + clock-names = "sclk_uart", "pclk_uart"; + reg-shift = <2>; + reg-io-width = <4>; + //dmas = <&pdma1 1>, <&pdma1 2>; + //#dma-cells = <2>; + pinctrl-names = "default"; + pinctrl-0 = <&uart0_xfer &uart0_cts &uart0_rts>; + status = "disabled"; + }; + + uart_bb: serial@ff190000 { + compatible = "rockchip,serial"; + reg = <0x0 0xff190000 0x0 0x100>; + interrupts = ; + clock-frequency = <24000000>; + clocks = <&xin24m>, <&xin24m>; + clock-names = "sclk_uart", "pclk_uart"; + reg-shift = <2>; + reg-io-width = <4>; + //dmas = <&pdma1 3>, <&pdma1 4>; + //#dma-cells = <2>; + pinctrl-names = "default"; + pinctrl-0 = <&uart1_xfer &uart1_cts &uart1_rts>; + status = "disabled"; + }; + + uart_dbg: serial@ff690000 { + compatible = "rockchip,serial"; + reg = <0x0 0xff690000 0x0 0x100>; + interrupts = ; + clock-frequency = <24000000>; + clocks = <&xin24m>, <&xin24m>; + clock-names = "sclk_uart", "pclk_uart"; + reg-shift = <2>; + reg-io-width = <4>; + //dmas = <&pdma0 4>, <&pdma0 5>; + //#dma-cells = <2>; + pinctrl-names = "default"; + pinctrl-0 = <&uart2_xfer>; + //status = "disabled"; + }; + + uart_gps: serial@ff1b0000 { + compatible = "rockchip,serial"; + reg = <0x0 0xff1b0000 0x0 0x100>; + interrupts = ; + clock-frequency = <24000000>; + clocks = <&xin24m>, <&xin24m>; + clock-names = "sclk_uart", "pclk_uart"; + current-speed = <115200>; + reg-shift = <2>; + reg-io-width = <4>; + //dmas = <&pdma1 7>, <&pdma1 8>; + //#dma-cells = <2>; + pinctrl-names = "default"; + pinctrl-0 = <&uart3_xfer &uart3_cts &uart3_rts>; + status = "disabled"; + }; + + uart_exp: serial@ff1c0000 { + compatible = "rockchip,serial"; + reg = <0x0 0xff1c0000 0x0 0x100>; + interrupts = ; + clock-frequency = <24000000>; + clocks = <&xin24m>, <&xin24m>; + clock-names = "sclk_uart", "pclk_uart"; + reg-shift = <2>; + reg-io-width = <4>; + //dmas = <&pdma1 9>, <&pdma1 10>; + //#dma-cells = <2>; + pinctrl-names = "default"; + pinctrl-0 = <&uart4_xfer &uart4_cts &uart4_rts>; + status = "disabled"; + }; + + spi0: spi@ff110000 { + compatible = "rockchip,rockchip-spi"; + reg = <0x0 0xff110000 0x0 0x1000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&spi0_clk &spi0_tx &spi0_rx &spi0_cs0 &spi0_cs1>; + rockchip,spi-src-clk = <0>; + num-cs = <2>; + //clocks =<&clk_spi0>, <&clk_gates6 4>; + //clock-names = "spi","pclk_spi0"; + //dmas = <&pdma1 11>, <&pdma1 12>; + //#dma-cells = <2>; + //dma-names = "tx", "rx"; + status = "disabled"; + }; + + spi1: spi@ff120000 { + compatible = "rockchip,rockchip-spi"; + reg = <0x0 0xff120000 0x0 0x1000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&spi1_clk &spi1_tx &spi1_rx &spi1_cs0>; + rockchip,spi-src-clk = <1>; + num-cs = <1>; + //clocks = <&clk_spi1>, <&clk_gates6 5>; + //clock-names = "spi","pclk_spi1"; + //dmas = <&pdma1 13>, <&pdma1 14>; + //#dma-cells = <2>; + //dma-names = "tx", "rx"; + status = "disabled"; + }; + + spi2: spi@ff130000 { + compatible = "rockchip,rockchip-spi"; + reg = <0x0 0xff130000 0x0 0x1000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&spi2_clk &spi2_tx &spi2_rx &spi2_cs0>; + rockchip,spi-src-clk = <2>; + num-cs = <2>; + //clocks = <&clk_spi2>, <&clk_gates6 6>; + //clock-names = "spi","pclk_spi2"; + //dmas = <&pdma1 15>, <&pdma1 16>; + //#dma-cells = <2>; + //dma-names = "tx", "rx"; + status = "disabled"; + }; + + + pinctrl: pinctrl { + compatible = "rockchip,rk3368-pinctrl"; + rockchip,grf = <&grf>; + rockchip,pmu = <&pmu_grf>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + gpio0: gpio0@ff750000 { + compatible = "rockchip,gpio-bank"; + reg = <0x0 0xff750000 0x0 0x100>; + interrupts = ; + //clocks = <&clk_gates17 4>; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpio1: gpio1@ff780000 { + compatible = "rockchip,gpio-bank"; + reg = <0x0 0xff780000 0x0 0x100>; + interrupts = ; + //clocks = <&clk_gates14 1>; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpio2: gpio2@ff790000 { + compatible = "rockchip,gpio-bank"; + reg = <0x0 0xff790000 0x0 0x100>; + interrupts = ; + //clocks = <&clk_gates14 2>; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpio3: gpio3@ff7a0000 { + compatible = "rockchip,gpio-bank"; + reg = <0x0 0xff7a0000 0x0 0x100>; + interrupts = ; + //clocks = <&clk_gates14 3>; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + pcfg_pull_up: pcfg-pull-up { + bias-pull-up; + }; + + pcfg_pull_down: pcfg-pull-down { + bias-pull-down; + }; + + pcfg_pull_none: pcfg-pull-none { + bias-disable; + }; + + pcfg_pull_none_drv_8ma: pcfg-pull-none-drv-8ma { + drive-strength = <8>; + }; + + pcfg_pull_up_drv_8ma: pcfg-pull-up-drv-8ma { + bias-pull-up; + drive-strength = <8>; + }; + + pcfg_pull_none_drv_4ma: pcfg-pull-none-drv-4ma { + drive-strength = <4>; + }; + + pcfg_pull_up_drv_4ma: pcfg-pull-up-drv-4ma { + bias-pull-up; + drive-strength = <4>; + }; + + pcfg_output_high: pcfg-output-high { + output-high; + }; + + pcfg_output_low: pcfg-output-low { + output-low; + }; + + i2c0 { + i2c0_xfer: i2c0-xfer { + rockchip,pins = <0 GPIO_A6 RK_FUNC_1 &pcfg_pull_none>, + <0 GPIO_A7 RK_FUNC_1 &pcfg_pull_none>; + }; + i2c0_gpio: i2c0-gpio { + rockchip,pins = <0 GPIO_A6 RK_FUNC_GPIO &pcfg_pull_none>, + <0 GPIO_A7 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + i2c1 { + i2c1_xfer: i2c1-xfer { + rockchip,pins = <2 GPIO_C5 RK_FUNC_1 &pcfg_pull_none>, + <2 GPIO_C6 RK_FUNC_1 &pcfg_pull_none>; + }; + i2c1_gpio: i2c1-gpio { + rockchip,pins = <2 GPIO_C5 RK_FUNC_GPIO &pcfg_pull_none>, + <2 GPIO_C6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + i2c2 { + i2c2_xfer: i2c2-xfer { + rockchip,pins = <3 GPIO_D7 RK_FUNC_2 &pcfg_pull_none>, + <0 GPIO_B1 RK_FUNC_2 &pcfg_pull_none>; + }; + i2c2_gpio: i2c2-gpio { + rockchip,pins = <3 GPIO_D7 RK_FUNC_GPIO &pcfg_pull_none>, + <0 GPIO_B1 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + i2c3 { + i2c3_xfer: i2c3-xfer { + rockchip,pins = <1 GPIO_C0 RK_FUNC_1 &pcfg_pull_none>, + <1 GPIO_C1 RK_FUNC_1 &pcfg_pull_none>; + }; + i2c3_gpio: i2c3-gpio { + rockchip,pins = <1 GPIO_C0 RK_FUNC_GPIO &pcfg_pull_none>, + <1 GPIO_C1 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + i2c4 { + i2c4_xfer: i2c4-xfer { + rockchip,pins = <3 GPIO_D0 RK_FUNC_2 &pcfg_pull_none>, + <3 GPIO_D1 RK_FUNC_2 &pcfg_pull_none>; + }; + i2c4_gpio: i2c4-gpio { + rockchip,pins = <3 GPIO_D0 RK_FUNC_GPIO &pcfg_pull_none>, + <3 GPIO_D1 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + i2c5 { + i2c5_xfer: i2c5-xfer { + rockchip,pins = <3 GPIO_D2 RK_FUNC_2 &pcfg_pull_none>, + <3 GPIO_D3 RK_FUNC_2 &pcfg_pull_none>; + }; + i2c5_gpio: i2c5-gpio { + rockchip,pins = <3 GPIO_D2 RK_FUNC_GPIO &pcfg_pull_none>, + <3 GPIO_D3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + uart0 { + uart0_xfer: uart0-xfer { + rockchip,pins = <2 GPIO_D0 RK_FUNC_1 &pcfg_pull_up>, + <2 GPIO_D1 RK_FUNC_1 &pcfg_pull_none>; + }; + + uart0_cts: uart0-cts { + rockchip,pins = <2 GPIO_D2 RK_FUNC_1 &pcfg_pull_none>; + }; + + uart0_rts: uart0-rts { + rockchip,pins = <2 GPIO_D3 RK_FUNC_1 &pcfg_pull_none>; + }; + + uart0_rts_gpio: uart0-rts-gpio { + rockchip,pins = <2 GPIO_D3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + uart1 { + uart1_xfer: uart1-xfer { + rockchip,pins = <0 GPIO_C4 RK_FUNC_3 &pcfg_pull_up>, + <0 GPIO_C5 RK_FUNC_3 &pcfg_pull_none>; + }; + + uart1_cts: uart1-cts { + rockchip,pins = <0 GPIO_C6 RK_FUNC_3 &pcfg_pull_none>; + }; + + uart1_rts: uart1-rts { + rockchip,pins = <0 GPIO_C7 RK_FUNC_3 &pcfg_pull_none>; + }; + }; + + uart2 { + uart2_xfer: uart2-xfer { + rockchip,pins = <2 GPIO_A6 RK_FUNC_2 &pcfg_pull_up>, + <2 GPIO_A5 RK_FUNC_2 &pcfg_pull_none>; + }; + }; + + uart3 { + uart3_xfer: uart3-xfer { + rockchip,pins = <3 GPIO_D5 RK_FUNC_2 &pcfg_pull_up>, + <3 GPIO_D6 RK_FUNC_2 &pcfg_pull_none>; + }; + + uart3_cts: uart3-cts { + rockchip,pins = <3 GPIO_C0 RK_FUNC_2 &pcfg_pull_none>; + }; + + uart3_rts: uart3-rts { + rockchip,pins = <3 GPIO_C1 RK_FUNC_2 &pcfg_pull_none>; + }; + }; + + uart4 { + uart4_xfer: uart4-xfer { + rockchip,pins = <0 GPIO_D3 RK_FUNC_3 &pcfg_pull_up>, + <0 GPIO_D2 RK_FUNC_3 &pcfg_pull_none>; + }; + + uart4_cts: uart4-cts { + rockchip,pins = <0 GPIO_D0 RK_FUNC_3 &pcfg_pull_none>; + }; + + uart4_rts: uart4-rts { + rockchip,pins = <0 GPIO_D1 RK_FUNC_3 &pcfg_pull_none>; + }; + }; + + spi0 { + spi0_clk: spi0-clk { + rockchip,pins = <1 GPIO_D5 RK_FUNC_2 &pcfg_pull_up>; + }; + spi0_cs0: spi0-cs0 { + rockchip,pins = <1 GPIO_D0 RK_FUNC_3 &pcfg_pull_up>; + }; + spi0_tx: spi0-tx { + rockchip,pins = <1 GPIO_C7 RK_FUNC_3 &pcfg_pull_up>; + }; + spi0_rx: spi0-rx { + rockchip,pins = <1 GPIO_C6 RK_FUNC_3 &pcfg_pull_up>; + }; + spi0_cs1: spi0-cs1 { + rockchip,pins = <1 GPIO_D1 RK_FUNC_3 &pcfg_pull_up>; + }; + }; + + spi1 { + spi1_clk: spi1-clk { + rockchip,pins = <1 GPIO_B6 RK_FUNC_2 &pcfg_pull_up>; + }; + spi1_cs0: spi1-cs0 { + rockchip,pins = <1 GPIO_B7 RK_FUNC_2 &pcfg_pull_up>; + }; + spi1_rx: spi1-rx { + rockchip,pins = <1 GPIO_C0 RK_FUNC_2 &pcfg_pull_up>; + }; + spi1_tx: spi1-tx { + rockchip,pins = <1 GPIO_C1 RK_FUNC_2 &pcfg_pull_up>; + }; + }; + + spi2 { + spi2_clk: spi2-clk { + rockchip,pins = <0 GPIO_B4 RK_FUNC_2 &pcfg_pull_up>; + }; + spi2_cs0: spi2-cs0 { + rockchip,pins = <0 GPIO_B5 RK_FUNC_2 &pcfg_pull_up>; + }; + spi2_rx: spi2-rx { + rockchip,pins = <0 GPIO_B2 RK_FUNC_2 &pcfg_pull_up>; + }; + spi2_tx: spi2-tx { + rockchip,pins = <0 GPIO_B3 RK_FUNC_2 &pcfg_pull_up>; + }; + }; + + i2s { + i2s_mclk: i2s-mclk { + rockchip,pins = <2 GPIO_C4 RK_FUNC_1 &pcfg_pull_none>; + }; + + i2s_sclk:i2s-sclk { + rockchip,pins = <2 GPIO_B4 RK_FUNC_1 &pcfg_pull_none>; + }; + + i2s_lrckrx:i2s-lrckrx { + rockchip,pins = <2 GPIO_B5 RK_FUNC_1 &pcfg_pull_none>; + }; + + i2s_lrcktx:i2s-lrcktx { + rockchip,pins = <2 GPIO_B6 RK_FUNC_1 &pcfg_pull_none>; + }; + + i2s_sdi:i2s-sdi { + rockchip,pins = <2 GPIO_B7 RK_FUNC_1 &pcfg_pull_none>; + }; + + i2s_sdo0:i2s-sdo0 { + rockchip,pins = <2 GPIO_C0 RK_FUNC_1 &pcfg_pull_none>; + }; + + i2s_sdo1:i2s-sdo1 { + rockchip,pins = <2 GPIO_C1 RK_FUNC_1 &pcfg_pull_none>; + }; + + i2s_sdo2:i2s-sdo2 { + rockchip,pins = <2 GPIO_C2 RK_FUNC_1 &pcfg_pull_none>; + }; + + i2s_sdo3:i2s-sdo3 { + rockchip,pins = <2 GPIO_C3 RK_FUNC_1 &pcfg_pull_none>; + }; + + i2s_gpio: i2s-gpio { + rockchip,pins = <2 GPIO_C4 RK_FUNC_GPIO &pcfg_pull_none>, + <2 GPIO_B4 RK_FUNC_GPIO &pcfg_pull_none>, + <2 GPIO_B5 RK_FUNC_GPIO &pcfg_pull_none>, + <2 GPIO_B6 RK_FUNC_GPIO &pcfg_pull_none>, + <2 GPIO_B7 RK_FUNC_GPIO &pcfg_pull_none>, + <2 GPIO_C0 RK_FUNC_GPIO &pcfg_pull_none>, + <2 GPIO_C1 RK_FUNC_GPIO &pcfg_pull_none>, + <2 GPIO_C2 RK_FUNC_GPIO &pcfg_pull_none>, + <2 GPIO_C3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + spdif { + spdif_tx: spdif-tx { + rockchip,pins = <2 GPIO_C7 RK_FUNC_1 &pcfg_pull_none>; + }; + }; + + sdmmc { + sdmmc_clk: sdmmc-clk { + rockchip,pins = <2 GPIO_B1 RK_FUNC_1 &pcfg_pull_none_drv_4ma>; + }; + + sdmmc_cmd: sdmmc-cmd { + rockchip,pins = <2 GPIO_B2 RK_FUNC_1 &pcfg_pull_up_drv_4ma>; + }; + + sdmmc_dectn: sdmmc-dectn { + rockchip,pins = <2 GPIO_B3 RK_FUNC_1 &pcfg_pull_up_drv_4ma>; + }; + + sdmmc_bus1: sdmmc-bus1 { + rockchip,pins = <2 GPIO_A5 RK_FUNC_1 &pcfg_pull_up_drv_4ma>; + }; + + sdmmc_bus4: sdmmc-bus4 { + rockchip,pins = <2 GPIO_A5 RK_FUNC_1 &pcfg_pull_up_drv_4ma>, + <2 GPIO_A6 RK_FUNC_1 &pcfg_pull_up_drv_4ma>, + <2 GPIO_A7 RK_FUNC_1 &pcfg_pull_up_drv_4ma>, + <2 GPIO_B0 RK_FUNC_1 &pcfg_pull_up_drv_4ma>; + }; + + sdmmc_gpio: sdmmc-gpio { + rockchip,pins = <2 GPIO_B1 RK_FUNC_GPIO &pcfg_pull_up_drv_4ma>,//CLK + <2 GPIO_B2 RK_FUNC_GPIO &pcfg_pull_up_drv_4ma>,//CMD + <2 GPIO_B3 RK_FUNC_GPIO &pcfg_pull_up_drv_4ma>,//DET + <2 GPIO_A5 RK_FUNC_GPIO &pcfg_pull_up_drv_4ma>,//DO + <2 GPIO_A6 RK_FUNC_GPIO &pcfg_pull_up_drv_4ma>,//D1 + <2 GPIO_A7 RK_FUNC_GPIO &pcfg_pull_up_drv_4ma>,//D2 + <2 GPIO_B0 RK_FUNC_GPIO &pcfg_pull_up_drv_4ma>;//D3 + }; + }; + + sdio0 { + sdio0_bus1: sdio0-bus1 { + rockchip,pins = <2 GPIO_D4 RK_FUNC_1 &pcfg_pull_up_drv_4ma>; + }; + + sdio0_bus4: sdio0-bus4 { + rockchip,pins = <2 GPIO_D4 RK_FUNC_1 &pcfg_pull_up_drv_4ma>, + <2 GPIO_D5 RK_FUNC_1 &pcfg_pull_up_drv_4ma>, + <2 GPIO_D6 RK_FUNC_1 &pcfg_pull_up_drv_4ma>, + <2 GPIO_D7 RK_FUNC_1 &pcfg_pull_up_drv_4ma>; + }; + + sdio0_cmd: sdio0-cmd { + rockchip,pins = <3 GPIO_A0 RK_FUNC_1 &pcfg_pull_up_drv_4ma>; + }; + + sdio0_clk: sdio0-clk { + rockchip,pins = <3 GPIO_A1 RK_FUNC_1 &pcfg_pull_none_drv_4ma>; + }; + + sdio0_dectn: sdio0-dectn { + rockchip,pins = <3 GPIO_A2 RK_FUNC_1 &pcfg_pull_up>; + }; + + sdio0_wrprt: sdio0-wrprt { + rockchip,pins = <3 GPIO_A3 RK_FUNC_1 &pcfg_pull_up>; + }; + + sdio0_pwren: sdio0-pwren { + rockchip,pins = <3 GPIO_A4 RK_FUNC_1 &pcfg_pull_up>; + }; + + sdio0_bkpwr: sdio0-bkpwr { + rockchip,pins = <3 GPIO_A5 RK_FUNC_1 &pcfg_pull_up>; + }; + + sdio0_int: sdio0-int { + rockchip,pins = <3 GPIO_A6 RK_FUNC_1 &pcfg_pull_up>; + }; + + sdio0_gpio: sdio0-gpio { + rockchip,pins = <3 GPIO_A0 RK_FUNC_GPIO &pcfg_pull_up_drv_4ma>,//CMD + <3 GPIO_A1 RK_FUNC_GPIO &pcfg_pull_up_drv_4ma>,//CLK + <3 GPIO_A2 RK_FUNC_GPIO &pcfg_pull_up_drv_4ma>,//DET + <3 GPIO_A3 RK_FUNC_GPIO &pcfg_pull_up_drv_4ma>,//wrprt + <3 GPIO_A4 RK_FUNC_GPIO &pcfg_pull_up_drv_4ma>,//PWREN + <3 GPIO_A5 RK_FUNC_GPIO &pcfg_pull_up_drv_4ma>,//BKPWR + <3 GPIO_A6 RK_FUNC_GPIO &pcfg_pull_up_drv_4ma>,//INTN + <2 GPIO_D4 RK_FUNC_GPIO &pcfg_pull_up_drv_4ma>,//DO + <2 GPIO_D5 RK_FUNC_GPIO &pcfg_pull_up_drv_4ma>,//D1 + <2 GPIO_D6 RK_FUNC_GPIO &pcfg_pull_up_drv_4ma>,//D2 + <2 GPIO_D7 RK_FUNC_GPIO &pcfg_pull_up_drv_4ma>;//D3 + }; + }; + + emmc { + emmc_clk: emmc-clk { + rockchip,pins = <2 GPIO_A4 RK_FUNC_2 &pcfg_pull_none_drv_8ma>; + }; + + emmc_cmd: emmc-cmd { + rockchip,pins = <1 GPIO_D2 RK_FUNC_2 &pcfg_pull_up_drv_8ma>; + }; + + emmc_pwren: emmc-pwren { + rockchip,pins = <1 GPIO_D3 RK_FUNC_2 &pcfg_pull_none>; + }; + + emmc_rstnout: emmc_rstnout { + rockchip,pins = <2 GPIO_A3 RK_FUNC_2 &pcfg_pull_none>; + }; + + emmc_bus1: emmc-bus1 { + rockchip,pins = <1 GPIO_C2 RK_FUNC_2 &pcfg_pull_up_drv_8ma>;//DO + }; + + emmc_bus4: emmc-bus4 { + rockchip,pins = <1 GPIO_C2 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,//DO + <1 GPIO_C3 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,//D1 + <1 GPIO_C4 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,//D2 + <1 GPIO_C5 RK_FUNC_2 &pcfg_pull_up_drv_8ma>;//D3 + }; + }; + + pwm0 { + pwm0_pin: pwm0-pin { + rockchip,pins = <3 GPIO_B0 RK_FUNC_2 &pcfg_pull_none>; + }; + + vop_pwm_pin:vop-pwm { + rockchip,pins = <3 GPIO_B0 RK_FUNC_3 &pcfg_pull_none>; + }; + }; + + pwm1 { + pwm1_pin: pwm1-pin { + rockchip,pins = <0 GPIO_B0 RK_FUNC_2 &pcfg_pull_none>; + }; + }; + + pwm3 { + pwm3_pin: pwm3-pin { + rockchip,pins = <3 GPIO_D6 RK_FUNC_3 &pcfg_pull_none>; + }; + }; + + lcdc { + lcdc_lcdc: lcdc-lcdc { + rockchip,pins = <0 GPIO_D7 RK_FUNC_1 &pcfg_pull_none>,//DCLK + <0 GPIO_D6 RK_FUNC_1 &pcfg_pull_none>,//DEN + <0 GPIO_D4 RK_FUNC_1 &pcfg_pull_none>,//HSYNC + <0 GPIO_D5 RK_FUNC_1 &pcfg_pull_none>;//VSYN + }; + + lcdc_gpio: lcdc-gpio { + rockchip,pins = <0 GPIO_D7 RK_FUNC_GPIO &pcfg_pull_none>,//DCLK + <0 GPIO_D6 RK_FUNC_GPIO &pcfg_pull_none>,//DEN + <0 GPIO_D4 RK_FUNC_GPIO &pcfg_pull_none>,//HSYNC + <0 GPIO_D5 RK_FUNC_GPIO &pcfg_pull_none>;//VSYN + }; + }; + + isp { + cif_clkout: cif-clkout { + rockchip,pins = <1 GPIO_B3 RK_FUNC_1 &pcfg_pull_none>;//cif_clkout + }; + + isp_dvp_d2d9: isp-dvp-d2d9 { + rockchip,pins = <1 GPIO_A0 RK_FUNC_1 &pcfg_pull_none>,//cif_data2 + <1 GPIO_A1 RK_FUNC_1 &pcfg_pull_none>,//cif_data3 + <1 GPIO_A2 RK_FUNC_1 &pcfg_pull_none>,//cif_data4 + <1 GPIO_A3 RK_FUNC_1 &pcfg_pull_none>,//cif_data5 + <1 GPIO_A4 RK_FUNC_1 &pcfg_pull_none>,//cif_data6 + <1 GPIO_A5 RK_FUNC_1 &pcfg_pull_none>,//cif_data7 + <1 GPIO_A6 RK_FUNC_1 &pcfg_pull_none>,//cif_data8 + <1 GPIO_A7 RK_FUNC_1 &pcfg_pull_none>,//cif_data9 + <1 GPIO_B0 RK_FUNC_1 &pcfg_pull_none>,//cif_sync + <1 GPIO_B1 RK_FUNC_1 &pcfg_pull_none>,//cif_href + <1 GPIO_B2 RK_FUNC_1 &pcfg_pull_none>,//cif_clkin + <1 GPIO_B3 RK_FUNC_1 &pcfg_pull_none>;//cif_clkout + }; + + isp_dvp_d0d1: isp-dvp-d0d1 { + rockchip,pins = <1 GPIO_B4 RK_FUNC_1 &pcfg_pull_none>,//cif_data0 + <1 GPIO_B5 RK_FUNC_1 &pcfg_pull_none>;//cif_data1 + }; + + isp_dvp_d10d11:isp_d10d11 { + rockchip,pins = <1 GPIO_B6 RK_FUNC_1 &pcfg_pull_none>,//cif_data10 + <1 GPIO_B7 RK_FUNC_1 &pcfg_pull_none>;//cif_data11 + }; + + isp_dvp_d0d7: isp-dvp-d0d7 { + rockchip,pins = <1 GPIO_B4 RK_FUNC_1 &pcfg_pull_none>,//cif_data0 + <1 GPIO_B5 RK_FUNC_1 &pcfg_pull_none>,//cif_data1 + <1 GPIO_A0 RK_FUNC_1 &pcfg_pull_none>,//cif_data2 + <1 GPIO_A1 RK_FUNC_1 &pcfg_pull_none>,//cif_data3 + <1 GPIO_A2 RK_FUNC_1 &pcfg_pull_none>,//cif_data4 + <1 GPIO_A3 RK_FUNC_1 &pcfg_pull_none>,//cif_data5 + <1 GPIO_A4 RK_FUNC_1 &pcfg_pull_none>,//cif_data6 + <1 GPIO_A5 RK_FUNC_1 &pcfg_pull_none>;//cif_data7 + }; + + isp_shutter: isp-shutter { + rockchip,pins = <3 GPIO_C3 RK_FUNC_2 &pcfg_pull_none>, //SHUTTEREN + <3 GPIO_C6 RK_FUNC_2 &pcfg_pull_none>;//SHUTTERTRIG + }; + + isp_flash_trigger: isp-flash-trigger { + rockchip,pins = <3 GPIO_C4 RK_FUNC_2 &pcfg_pull_none>; //ISP_FLASHTRIGOU + }; + + isp_prelight: isp-prelight { + rockchip,pins = <3 GPIO_C5 RK_FUNC_2 &pcfg_pull_none>;//ISP_PRELIGHTTRIG + }; + + isp_flash_trigger_as_gpio: isp_flash_trigger_as_gpio { + rockchip,pins = <3 GPIO_C4 RK_FUNC_GPIO &pcfg_pull_none>;//ISP_FLASHTRIGOU + }; + }; + + gps { + gps_mag: gps-mag { + rockchip,pins = <3 GPIO_B6 RK_FUNC_2 &pcfg_pull_none>; + }; + + gps_sig: gps-sig { + rockchip,pins = <3 GPIO_B7 RK_FUNC_2 &pcfg_pull_none>; + + }; + + gps_rfclk: gps-rfclk { + rockchip,pins = <3 GPIO_C0 RK_FUNC_3 &pcfg_pull_none>; + }; + }; + + gmac { + mac_clk: mac-clk { + rockchip,pins = <3 GPIO_C6 RK_FUNC_1 &pcfg_pull_none>; + }; + + mac_txpins: mac-txpins { + rockchip,pins = <3 GPIO_B0 RK_FUNC_1 &pcfg_pull_none>,//TXD0 + <3 GPIO_B1 RK_FUNC_1 &pcfg_pull_none>,//TXD1 + <3 GPIO_B2 RK_FUNC_1 &pcfg_pull_none>,//TXD2 + <3 GPIO_B6 RK_FUNC_1 &pcfg_pull_none>,//TXD3 + <3 GPIO_B5 RK_FUNC_1 &pcfg_pull_none>,//TXEN + <3 GPIO_D4 RK_FUNC_1 &pcfg_pull_none>;//TXCLK + }; + + mac_rxpins: mac-rxpins { + rockchip,pins = <3 GPIO_B7 RK_FUNC_1 &pcfg_pull_none>,//RXD0 + <3 GPIO_C0 RK_FUNC_1 &pcfg_pull_none>,//RXD1 + <3 GPIO_C1 RK_FUNC_1 &pcfg_pull_none>,//RXD2 + <3 GPIO_C2 RK_FUNC_1 &pcfg_pull_none>,//RXD3 + <3 GPIO_C4 RK_FUNC_1 &pcfg_pull_none>,//RXDV + <3 GPIO_C5 RK_FUNC_1 &pcfg_pull_none>,//RXER + <3 GPIO_D1 RK_FUNC_1 &pcfg_pull_none>,//RXCLK + <3 GPIO_B4 RK_FUNC_1 &pcfg_pull_none>;//COL + }; + + mac_crs: mac-crs { + rockchip,pins = <3 GPIO_B3 RK_FUNC_1 &pcfg_pull_none>; //CRS + }; + + mac_mdpins: mac-mdpins { + rockchip,pins = <3 GPIO_D0 RK_FUNC_1 &pcfg_pull_none>,//MDIO + <3 GPIO_C3 RK_FUNC_1 &pcfg_pull_none>;//MDC + }; + }; + + tsadc_pin { + tsadc_int: tsadc-int { + rockchip,pins = <0 GPIO_A3 RK_FUNC_1 &pcfg_pull_none>; + }; + tsadc_gpio: tsadc-gpio { + rockchip,pins = <0 GPIO_A3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + }; +}; diff --git a/drivers/pinctrl/pinctrl-rk3368.c b/drivers/pinctrl/pinctrl-rk3368.c new file mode 100755 index 000000000000..fe68f6e0a08b --- /dev/null +++ b/drivers/pinctrl/pinctrl-rk3368.c @@ -0,0 +1,2329 @@ +/* + * Pinctrl driver for Rockchip SoCs + * + * Copyright (c) 2013 MundoReader S.L. + * Author: Heiko Stuebner + * + * With some ideas taken from pinctrl-samsung: + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * Copyright (c) 2012 Linaro Ltd + * http://www.linaro.org + * + * and pinctrl-at91: + * Copyright (C) 2011-2012 Jean-Christophe PLAGNIOL-VILLARD + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "pinconf.h" + + +#if 0 +#define pinctrl_dbg(dev, format, arg...) \ + dev_printk(KERN_INFO , dev , format , ## arg) +#else +#define pinctrl_dbg(dev, format, arg...) +#endif + + +/* GPIO control registers */ +#define GPIO_SWPORT_DR 0x00 +#define GPIO_SWPORT_DDR 0x04 +#define GPIO_INTEN 0x30 +#define GPIO_INTMASK 0x34 +#define GPIO_INTTYPE_LEVEL 0x38 +#define GPIO_INT_POLARITY 0x3c +#define GPIO_INT_STATUS 0x40 +#define GPIO_INT_RAWSTATUS 0x44 +#define GPIO_DEBOUNCE 0x48 +#define GPIO_PORTS_EOI 0x4c +#define GPIO_EXT_PORT 0x50 +#define GPIO_LS_SYNC 0x60 + +enum rockchip_pinctrl_type { + RK2928, + RK3066B, + RK3188, + RK3288, + RK3368, +}; + +/** + * Encode variants of iomux registers into a type variable + */ +#define IOMUX_GPIO_ONLY BIT(0) +#define IOMUX_WIDTH_4BIT BIT(1) +#define IOMUX_SOURCE_PMU BIT(2) +#define IOMUX_UNROUTED BIT(3) + +/** + * @type: iomux variant using IOMUX_* constants + * @offset: if initialized to -1 it will be autocalculated, by specifying + * an initial offset value the relevant source offset can be reset + * to a new value for autocalculating the following iomux registers. + */ +struct rockchip_iomux { + int type; + int offset; +}; + +/** + * @reg_base: register base of the gpio bank + * @reg_pull: optional separate register for additional pull settings + * @clk: clock of the gpio bank + * @irq: interrupt of the gpio bank + * @pin_base: first pin number + * @nr_pins: number of pins in this bank + * @name: name of the bank + * @bank_num: number of the bank, to account for holes + * @iomux: array describing the 4 iomux sources of the bank + * @valid: are all necessary informations present + * @of_node: dt node of this bank + * @drvdata: common pinctrl basedata + * @domain: irqdomain of the gpio bank + * @gpio_chip: gpiolib chip + * @grange: gpio range + * @slock: spinlock for the gpio bank + */ +struct rockchip_pin_bank { + void __iomem *reg_base; + struct regmap *regmap_pull; + struct clk *clk; + int irq; + u32 pin_base; + u8 nr_pins; + char *name; + u8 bank_num; + struct rockchip_iomux iomux[4]; + bool valid; + struct device_node *of_node; + struct rockchip_pinctrl *drvdata; + struct irq_domain *domain; + struct gpio_chip gpio_chip; + struct pinctrl_gpio_range grange; + spinlock_t slock; + u32 toggle_edge_mode; + u32 suspend_wakeup; + u32 saved_wakeup; +}; + +#define PIN_BANK(id, pins, label) \ + { \ + .bank_num = id, \ + .nr_pins = pins, \ + .name = label, \ + .iomux = { \ + { .offset = -1 }, \ + { .offset = -1 }, \ + { .offset = -1 }, \ + { .offset = -1 }, \ + }, \ + } + +#define PIN_BANK_IOMUX_FLAGS(id, pins, label, iom0, iom1, iom2, iom3) \ + { \ + .bank_num = id, \ + .nr_pins = pins, \ + .name = label, \ + .iomux = { \ + { .type = iom0, .offset = -1 }, \ + { .type = iom1, .offset = -1 }, \ + { .type = iom2, .offset = -1 }, \ + { .type = iom3, .offset = -1 }, \ + }, \ + } + +/** + */ +struct rockchip_pin_ctrl { + struct rockchip_pin_bank *pin_banks; + u32 nr_banks; + u32 nr_pins; + char *label; + enum rockchip_pinctrl_type type; + int grf_mux_offset; + int pmu_mux_offset; + void (*pull_calc_reg)(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit); +}; + +struct rockchip_pin_config { + unsigned int func; + unsigned long *configs; + unsigned int nconfigs; +}; + +/** + * struct rockchip_pin_group: represent group of pins of a pinmux function. + * @name: name of the pin group, used to lookup the group. + * @pins: the pins included in this group. + * @npins: number of pins included in this group. + * @func: the mux function number to be programmed when selected. + * @configs: the config values to be set for each pin + * @nconfigs: number of configs for each pin + */ +struct rockchip_pin_group { + const char *name; + unsigned int npins; + unsigned int *pins; + struct rockchip_pin_config *data; +}; + +/** + * struct rockchip_pmx_func: represent a pin function. + * @name: name of the pin function, used to lookup the function. + * @groups: one or more names of pin groups that provide this function. + * @num_groups: number of groups included in @groups. + */ +struct rockchip_pmx_func { + const char *name; + const char **groups; + u8 ngroups; +}; + +struct rockchip_pinctrl { + struct regmap *regmap_base; + int reg_size; + struct regmap *regmap_pull; + struct regmap *regmap_pmu; + struct device *dev; + struct rockchip_pin_ctrl *ctrl; + struct pinctrl_desc pctl; + struct pinctrl_dev *pctl_dev; + struct rockchip_pin_group *groups; + unsigned int ngroups; + struct rockchip_pmx_func *functions; + unsigned int nfunctions; +}; + +static struct regmap_config rockchip_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; +static struct rockchip_pinctrl *g_info; + +static inline struct rockchip_pin_bank *gc_to_pin_bank(struct gpio_chip *gc) +{ + return container_of(gc, struct rockchip_pin_bank, gpio_chip); +} + +static const inline struct rockchip_pin_group *pinctrl_name_to_group( + const struct rockchip_pinctrl *info, + const char *name) +{ + int i; + + for (i = 0; i < info->ngroups; i++) { + if (!strcmp(info->groups[i].name, name)) + return &info->groups[i]; + } + + return NULL; +} + +/* + * given a pin number that is local to a pin controller, find out the pin bank + * and the register base of the pin bank. + */ +static struct rockchip_pin_bank *pin_to_bank(struct rockchip_pinctrl *info, + unsigned pin) +{ + struct rockchip_pin_bank *b = info->ctrl->pin_banks; + + while (pin >= (b->pin_base + b->nr_pins)) + b++; + + return b; +} + +static struct rockchip_pin_bank *bank_num_to_bank( + struct rockchip_pinctrl *info, + unsigned num) +{ + struct rockchip_pin_bank *b = info->ctrl->pin_banks; + int i; + + for (i = 0; i < info->ctrl->nr_banks; i++, b++) { + if (b->bank_num == num) + return b; + } + + return ERR_PTR(-EINVAL); +} + +/* + * Pinctrl_ops handling + */ + +static int rockchip_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->ngroups; +} + +static const char *rockchip_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->groups[selector].name; +} + +static int rockchip_get_group_pins(struct pinctrl_dev *pctldev, + unsigned selector, const unsigned **pins, + unsigned *npins) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + if (selector >= info->ngroups) + return -EINVAL; + + *pins = info->groups[selector].pins; + *npins = info->groups[selector].npins; + + return 0; +} + +static int rockchip_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, unsigned *num_maps) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const struct rockchip_pin_group *grp; + struct pinctrl_map *new_map; + struct device_node *parent; + int map_num = 1; + int i; + + /* + * first find the group of this node and check if we need to create + * config maps for pins + */ + grp = pinctrl_name_to_group(info, np->name); + if (!grp) { + dev_err(info->dev, "unable to find group for node %s\n", + np->name); + return -EINVAL; + } + + map_num += grp->npins; + new_map = devm_kzalloc(pctldev->dev, sizeof(*new_map) * map_num, + GFP_KERNEL); + if (!new_map) + return -ENOMEM; + + *map = new_map; + *num_maps = map_num; + + /* create mux map */ + parent = of_get_parent(np); + if (!parent) { + devm_kfree(pctldev->dev, new_map); + return -EINVAL; + } + new_map[0].type = PIN_MAP_TYPE_MUX_GROUP; + new_map[0].data.mux.function = parent->name; + new_map[0].data.mux.group = np->name; + of_node_put(parent); + + /* create config map */ + new_map++; + for (i = 0; i < grp->npins; i++) { + new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN; + new_map[i].data.configs.group_or_pin = + pin_get_name(pctldev, grp->pins[i]); + new_map[i].data.configs.configs = grp->data[i].configs; + new_map[i].data.configs.num_configs = grp->data[i].nconfigs; + } + + pinctrl_dbg(pctldev->dev, "maps: function %s group %s num %d\n", + (*map)->data.mux.function, (*map)->data.mux.group, map_num); + + return 0; +} + +static void rockchip_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ +} + +static const struct pinctrl_ops rockchip_pctrl_ops = { + .get_groups_count = rockchip_get_groups_count, + .get_group_name = rockchip_get_group_name, + .get_group_pins = rockchip_get_group_pins, + .dt_node_to_map = rockchip_dt_node_to_map, + .dt_free_map = rockchip_dt_free_map, +}; + +/* + * Hardware access + */ + +static int rockchip_get_mux(struct rockchip_pin_bank *bank, int pin) +{ + struct rockchip_pinctrl *info = bank->drvdata; + int iomux_num = (pin / 8); + struct regmap *regmap; + unsigned int val; + int reg, ret, mask; + u8 bit; + + if (iomux_num > 3) + return -EINVAL; + + if (bank->iomux[iomux_num].type & IOMUX_UNROUTED) { + dev_err(info->dev, "pin %d is unrouted\n", pin); + return -EINVAL; + } + + if (bank->iomux[iomux_num].type & IOMUX_GPIO_ONLY) + return RK_FUNC_GPIO; + + regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) + ? info->regmap_pmu : info->regmap_base; + + /* get basic quadrupel of mux registers and the correct reg inside */ + mask = (bank->iomux[iomux_num].type & IOMUX_WIDTH_4BIT) ? 0xf : 0x3; + reg = bank->iomux[iomux_num].offset; + if (bank->iomux[iomux_num].type & IOMUX_WIDTH_4BIT) { + if ((pin % 8) >= 4) + reg += 0x4; + bit = (pin % 4) * 4; + } else { + bit = (pin % 8) * 2; + } + + ret = regmap_read(regmap, reg, &val); + if (ret) + return ret; + + return ((val >> bit) & mask); +} + +/* + * Set a new mux function for a pin. + * + * The register is divided into the upper and lower 16 bit. When changing + * a value, the previous register value is not read and changed. Instead + * it seems the changed bits are marked in the upper 16 bit, while the + * changed value gets set in the same offset in the lower 16 bit. + * All pin settings seem to be 2 bit wide in both the upper and lower + * parts. + * @bank: pin bank to change + * @pin: pin to change + * @mux: new mux function to set + */ +static int rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) +{ + struct rockchip_pinctrl *info = bank->drvdata; + int iomux_num = (pin / 8); + struct regmap *regmap; + int reg, ret, mask; + unsigned long flags; + u8 bit; + u32 data, rmask; + + if (iomux_num > 3) + return -EINVAL; + + if (bank->iomux[iomux_num].type & IOMUX_UNROUTED) { + dev_err(info->dev, "pin %d is unrouted\n", pin); + return -EINVAL; + } + + if (bank->iomux[iomux_num].type & IOMUX_GPIO_ONLY) { + if (mux != RK_FUNC_GPIO) { + dev_err(info->dev, + "pin %d only supports a gpio mux\n", pin); + return -ENOTSUPP; + } else { + return 0; + } + } + + pinctrl_dbg(info->dev, "setting mux of GPIO%d-%d to %d\n", + bank->bank_num, pin, mux); + + regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) + ? info->regmap_pmu : info->regmap_base; + + /* get basic quadrupel of mux registers and the correct reg inside */ + mask = (bank->iomux[iomux_num].type & IOMUX_WIDTH_4BIT) ? 0xf : 0x3; + reg = bank->iomux[iomux_num].offset; + if (bank->iomux[iomux_num].type & IOMUX_WIDTH_4BIT) { + if ((pin % 8) >= 4) + reg += 0x4; + bit = (pin % 4) * 4; + } else { + bit = (pin % 8) * 2; + } + + spin_lock_irqsave(&bank->slock, flags); + + data = (mask << (bit + 16)); + rmask = data | (data >> 16); + data |= (mux & mask) << bit; + ret = regmap_update_bits(regmap, reg, rmask, data); + + spin_unlock_irqrestore(&bank->slock, flags); + + return ret; +} + +#define RK2928_PULL_OFFSET 0x118 +#define RK2928_PULL_PINS_PER_REG 16 +#define RK2928_PULL_BANK_STRIDE 8 + +static void rk2928_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl *info = bank->drvdata; + + *regmap = info->regmap_base; + *reg = RK2928_PULL_OFFSET; + *reg += bank->bank_num * RK2928_PULL_BANK_STRIDE; + *reg += (pin_num / RK2928_PULL_PINS_PER_REG) * 4; + + *bit = pin_num % RK2928_PULL_PINS_PER_REG; +}; + +#define RK3188_PULL_OFFSET 0x164 +#define RK3188_PULL_BITS_PER_PIN 2 +#define RK3188_PULL_PINS_PER_REG 8 +#define RK3188_PULL_BANK_STRIDE 16 +#define RK3188_PULL_PMU_OFFSET 0x64 + +static void rk3188_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl *info = bank->drvdata; + + /* The first 12 pins of the first bank are located elsewhere */ + if (bank->bank_num == 0 && pin_num < 12) { + *regmap = info->regmap_pmu ? info->regmap_pmu + : bank->regmap_pull; + *reg = info->regmap_pmu ? RK3188_PULL_PMU_OFFSET : 0; + *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4); + *bit = pin_num % RK3188_PULL_PINS_PER_REG; + *bit *= RK3188_PULL_BITS_PER_PIN; + } else { + *regmap = info->regmap_pull ? info->regmap_pull + : info->regmap_base; + *reg = info->regmap_pull ? 0 : RK3188_PULL_OFFSET; + + /* correct the offset, as it is the 2nd pull register */ + *reg -= 4; + *reg += bank->bank_num * RK3188_PULL_BANK_STRIDE; + *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4); + + /* + * The bits in these registers have an inverse ordering + * with the lowest pin being in bits 15:14 and the highest + * pin in bits 1:0 + */ + *bit = 7 - (pin_num % RK3188_PULL_PINS_PER_REG); + *bit *= RK3188_PULL_BITS_PER_PIN; + } +} + +#define RK3288_PULL_OFFSET 0x140 +static void rk3288_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl *info = bank->drvdata; + + /* The first 24 pins of the first bank are located in PMU */ + if (bank->bank_num == 0) { + *regmap = info->regmap_pmu; + *reg = RK3188_PULL_PMU_OFFSET; + + *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4); + *bit = pin_num % RK3188_PULL_PINS_PER_REG; + *bit *= RK3188_PULL_BITS_PER_PIN; + } else { + *regmap = info->regmap_base; + *reg = RK3288_PULL_OFFSET; + + /* correct the offset, as we're starting with the 2nd bank */ + *reg -= 0x10; + *reg += bank->bank_num * RK3188_PULL_BANK_STRIDE; + *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4); + + *bit = (pin_num % RK3188_PULL_PINS_PER_REG); + *bit *= RK3188_PULL_BITS_PER_PIN; + } +} + +#define RK3368_PULL_PMU_OFFSET 0x10 +#define RK3368_PULL_OFFSET 0x100 +static void rk3368_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl *info = bank->drvdata; + + /* The first 24 pins of the first bank are located in PMU */ + if (bank->bank_num == 0) { + *regmap = info->regmap_pmu; + *reg = RK3368_PULL_PMU_OFFSET; + + *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4); + *bit = pin_num % RK3188_PULL_PINS_PER_REG; + *bit *= RK3188_PULL_BITS_PER_PIN; + } else { + *regmap = info->regmap_base; + *reg = RK3368_PULL_OFFSET; + + /* correct the offset, as we're starting with the 2nd bank */ + *reg -= 0x10; + *reg += bank->bank_num * RK3188_PULL_BANK_STRIDE; + *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4); + + *bit = (pin_num % RK3188_PULL_PINS_PER_REG); + *bit *= RK3188_PULL_BITS_PER_PIN; + } +} + + +#define RK3288_DRV_PMU_OFFSET 0x70 +#define RK3288_DRV_GRF_OFFSET 0x1c0 +#define RK3288_DRV_BITS_PER_PIN 2 +#define RK3288_DRV_PINS_PER_REG 8 +#define RK3288_DRV_BANK_STRIDE 16 +static int rk3288_drv_list[] = { 2, 4, 8, 12 }; + +#define RK3368_DRV_PMU_OFFSET 0x20 +#define RK3368_DRV_GRF_OFFSET 0x200 + + +static void rk3288_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl *info = bank->drvdata; + struct rockchip_pin_ctrl *ctrl = info->ctrl; + + /* The first 24 pins of the first bank are located in PMU */ + if (bank->bank_num == 0) { + *regmap = info->regmap_pmu; + if(ctrl->type == RK3288) + *reg = RK3288_DRV_PMU_OFFSET; + else if (ctrl->type == RK3368) + *reg = RK3368_DRV_PMU_OFFSET; + + *reg += ((pin_num / RK3288_DRV_PINS_PER_REG) * 4); + *bit = pin_num % RK3288_DRV_PINS_PER_REG; + *bit *= RK3288_DRV_BITS_PER_PIN; + } else { + *regmap = info->regmap_base; + if(ctrl->type == RK3288) + *reg = RK3288_DRV_GRF_OFFSET; + else if (ctrl->type == RK3368) + *reg = RK3368_DRV_GRF_OFFSET; + + + /* correct the offset, as we're starting with the 2nd bank */ + *reg -= 0x10; + *reg += bank->bank_num * RK3288_DRV_BANK_STRIDE; + *reg += ((pin_num / RK3288_DRV_PINS_PER_REG) * 4); + + *bit = (pin_num % RK3288_DRV_PINS_PER_REG); + *bit *= RK3288_DRV_BITS_PER_PIN; + } +} + +static int rk3288_get_drive(struct rockchip_pin_bank *bank, int pin_num) +{ + struct regmap *regmap; + int reg, ret; + u32 data; + u8 bit; + + rk3288_calc_drv_reg_and_bit(bank, pin_num, ®map, ®, &bit); + + ret = regmap_read(regmap, reg, &data); + if (ret) + return ret; + + data >>= bit; + data &= (1 << RK3288_DRV_BITS_PER_PIN) - 1; + + return rk3288_drv_list[data]; +} + +static int rk3288_set_drive(struct rockchip_pin_bank *bank, int pin_num, + int strength) +{ + struct rockchip_pinctrl *info = bank->drvdata; + struct regmap *regmap; + unsigned long flags; + int reg, ret, i; + u32 data, rmask; + u8 bit; + + rk3288_calc_drv_reg_and_bit(bank, pin_num, ®map, ®, &bit); + + ret = -EINVAL; + for (i = 0; i < ARRAY_SIZE(rk3288_drv_list); i++) { + if (rk3288_drv_list[i] == strength) { + ret = i; + break; + } + } + + if (ret < 0) { + dev_err(info->dev, "unsupported driver strength %d\n", + strength); + return ret; + } + + spin_lock_irqsave(&bank->slock, flags); + + /* enable the write to the equivalent lower bits */ + data = ((1 << RK3288_DRV_BITS_PER_PIN) - 1) << (bit + 16); + rmask = data | (data >> 16); + data |= (ret << bit); + + ret = regmap_update_bits(regmap, reg, rmask, data); + spin_unlock_irqrestore(&bank->slock, flags); + + return ret; +} + +static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num) +{ + struct rockchip_pinctrl *info = bank->drvdata; + struct rockchip_pin_ctrl *ctrl = info->ctrl; + struct regmap *regmap; + int reg, ret; + u8 bit; + u32 data; + + /* rk3066b does support any pulls */ + if (ctrl->type == RK3066B) + return PIN_CONFIG_BIAS_DISABLE; + + ctrl->pull_calc_reg(bank, pin_num, ®map, ®, &bit); + + ret = regmap_read(regmap, reg, &data); + if (ret) + return ret; + + switch (ctrl->type) { + case RK2928: + return !(data & BIT(bit)) + ? PIN_CONFIG_BIAS_PULL_PIN_DEFAULT + : PIN_CONFIG_BIAS_DISABLE; + case RK3188: + case RK3288: + case RK3368: + data >>= bit; + data &= (1 << RK3188_PULL_BITS_PER_PIN) - 1; + + switch (data) { + case 0: + return PIN_CONFIG_BIAS_DISABLE; + case 1: + return PIN_CONFIG_BIAS_PULL_UP; + case 2: + return PIN_CONFIG_BIAS_PULL_DOWN; + case 3: + return PIN_CONFIG_BIAS_BUS_HOLD; + } + + dev_err(info->dev, "unknown pull setting\n"); + return -EIO; + default: + dev_err(info->dev, "unsupported pinctrl type\n"); + return -EINVAL; + }; +} + +static int rockchip_set_pull(struct rockchip_pin_bank *bank, + int pin_num, int pull) +{ + struct rockchip_pinctrl *info = bank->drvdata; + struct rockchip_pin_ctrl *ctrl = info->ctrl; + struct regmap *regmap; + int reg, ret; + unsigned long flags; + u8 bit; + u32 data, rmask; + + pinctrl_dbg(info->dev, "setting pull of GPIO%d-%d to %d\n", + bank->bank_num, pin_num, pull); + + /* rk3066b does support any pulls */ + if (ctrl->type == RK3066B) + return pull ? -EINVAL : 0; + + ctrl->pull_calc_reg(bank, pin_num, ®map, ®, &bit); + + switch (ctrl->type) { + case RK2928: + spin_lock_irqsave(&bank->slock, flags); + + data = BIT(bit + 16); + if (pull == PIN_CONFIG_BIAS_DISABLE) + data |= BIT(bit); + ret = regmap_write(regmap, reg, data); + + spin_unlock_irqrestore(&bank->slock, flags); + break; + case RK3188: + case RK3288: + case RK3368: + spin_lock_irqsave(&bank->slock, flags); + + /* enable the write to the equivalent lower bits */ + data = ((1 << RK3188_PULL_BITS_PER_PIN) - 1) << (bit + 16); + rmask = data | (data >> 16); + + switch (pull) { + case PIN_CONFIG_BIAS_DISABLE: + break; + case PIN_CONFIG_BIAS_PULL_UP: + data |= (1 << bit); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + data |= (2 << bit); + break; + case PIN_CONFIG_BIAS_BUS_HOLD: + data |= (3 << bit); + break; + default: + spin_unlock_irqrestore(&bank->slock, flags); + dev_err(info->dev, "unsupported pull setting %d\n", + pull); + return -EINVAL; + } + + ret = regmap_update_bits(regmap, reg, rmask, data); + + spin_unlock_irqrestore(&bank->slock, flags); + break; + default: + dev_err(info->dev, "unsupported pinctrl type\n"); + return -EINVAL; + } + + return ret; +} + +/* + * Pinmux_ops handling + */ + +static int rockchip_pmx_get_funcs_count(struct pinctrl_dev *pctldev) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->nfunctions; +} + +static const char *rockchip_pmx_get_func_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->functions[selector].name; +} + +static int rockchip_pmx_get_groups(struct pinctrl_dev *pctldev, + unsigned selector, const char * const **groups, + unsigned * const num_groups) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + *groups = info->functions[selector].groups; + *num_groups = info->functions[selector].ngroups; + + return 0; +} + +static int rockchip_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector, + unsigned group) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const unsigned int *pins = info->groups[group].pins; + const struct rockchip_pin_config *data = info->groups[group].data; + struct rockchip_pin_bank *bank; + int cnt, ret = 0; + + pinctrl_dbg(info->dev, "enable function %s group %s\n", + info->functions[selector].name, info->groups[group].name); + + /* + * for each pin in the pin group selected, program the correspoding pin + * pin function number in the config register. + */ + for (cnt = 0; cnt < info->groups[group].npins; cnt++) { + bank = pin_to_bank(info, pins[cnt]); + ret = rockchip_set_mux(bank, pins[cnt] - bank->pin_base, + data[cnt].func); + if (ret) + break; + } + + if (ret) { + /* revert the already done pin settings */ + for (cnt--; cnt >= 0; cnt--) + rockchip_set_mux(bank, pins[cnt] - bank->pin_base, 0); + + return ret; + } + + return 0; +} + +static void rockchip_pmx_disable(struct pinctrl_dev *pctldev, + unsigned selector, unsigned group) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const unsigned int *pins = info->groups[group].pins; + struct rockchip_pin_bank *bank; + int cnt; + + pinctrl_dbg(info->dev, "disable function %s group %s\n", + info->functions[selector].name, info->groups[group].name); + + for (cnt = 0; cnt < info->groups[group].npins; cnt++) { + bank = pin_to_bank(info, pins[cnt]); + rockchip_set_mux(bank, pins[cnt] - bank->pin_base, 0); + } +} + +/* + * The calls to gpio_direction_output() and gpio_direction_input() + * leads to this function call (via the pinctrl_gpio_direction_{input|output}() + * function called from the gpiolib interface). + */ +static int rockchip_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset, bool input) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + struct rockchip_pin_bank *bank; + struct gpio_chip *chip; + int pin, ret; + u32 data; + + chip = range->gc; + bank = gc_to_pin_bank(chip); + pin = offset - chip->base; + + dev_dbg(info->dev, "gpio_direction for pin %u as %s-%d to %s\n", + offset, range->name, pin, input ? "input" : "output"); + + ret = rockchip_set_mux(bank, pin, RK_FUNC_GPIO); + if (ret < 0) + return ret; + + data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR); + /* set bit to 1 for output, 0 for input */ + if (!input) + data |= BIT(pin); + else + data &= ~BIT(pin); + writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR); + + return 0; +} + +static const struct pinmux_ops rockchip_pmx_ops = { + .get_functions_count = rockchip_pmx_get_funcs_count, + .get_function_name = rockchip_pmx_get_func_name, + .get_function_groups = rockchip_pmx_get_groups, + .enable = rockchip_pmx_enable, + .disable = rockchip_pmx_disable, + .gpio_set_direction = rockchip_pmx_gpio_set_direction, +}; + +/* + * Pinconf_ops handling + */ + +static bool rockchip_pinconf_pull_valid(struct rockchip_pin_ctrl *ctrl, + enum pin_config_param pull) +{ + switch (ctrl->type) { + case RK2928: + return (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT || + pull == PIN_CONFIG_BIAS_DISABLE); + case RK3066B: + return pull ? false : true; + case RK3188: + case RK3288: + case RK3368: + return (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT); + } + + return false; +} + +static int rockchip_gpio_direction_output(struct gpio_chip *gc, + unsigned offset, int value); +static int rockchip_gpio_get(struct gpio_chip *gc, unsigned offset); + +/* set the pin config settings for a specified pin */ +static int rockchip_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long configs) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + struct rockchip_pin_bank *bank = pin_to_bank(info, pin); + enum pin_config_param param; + u16 arg; + //int i; + int rc; + + //for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs); + arg = pinconf_to_config_argument(configs); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + rc = rockchip_set_pull(bank, pin - bank->pin_base, + param); + if (rc) + return rc; + break; + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + case PIN_CONFIG_BIAS_BUS_HOLD: + if (!rockchip_pinconf_pull_valid(info->ctrl, param)) + return -ENOTSUPP; + + if (!arg) + return -EINVAL; + + rc = rockchip_set_pull(bank, pin - bank->pin_base, + param); + if (rc) + return rc; + break; + case PIN_CONFIG_OUTPUT: + rc = rockchip_gpio_direction_output(&bank->gpio_chip, + pin - bank->pin_base, + arg); + if (rc) + return rc; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + /* rk3288 RK3368 is the first with per-pin drive-strength */ + if ((info->ctrl->type != RK3288) && ((info->ctrl->type != RK3368))) + return -ENOTSUPP; + + rc = rk3288_set_drive(bank, pin - bank->pin_base, arg); + if (rc < 0) + return rc; + break; + default: + return -ENOTSUPP; + break; + } + //} /* for each config */ + + return 0; +} + +/* get the pin config settings for a specified pin */ +static int rockchip_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *config) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + struct rockchip_pin_bank *bank = pin_to_bank(info, pin); + enum pin_config_param param = pinconf_to_config_param(*config); + u16 arg; + int rc; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + if (rockchip_get_pull(bank, pin - bank->pin_base) != param) + return -EINVAL; + + arg = 0; + break; + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + case PIN_CONFIG_BIAS_BUS_HOLD: + if (!rockchip_pinconf_pull_valid(info->ctrl, param)) + return -ENOTSUPP; + + if (rockchip_get_pull(bank, pin - bank->pin_base) != param) + return -EINVAL; + + arg = 1; + break; + case PIN_CONFIG_OUTPUT: + rc = rockchip_get_mux(bank, pin - bank->pin_base); + if (rc != RK_FUNC_GPIO) + return -EINVAL; + + rc = rockchip_gpio_get(&bank->gpio_chip, pin - bank->pin_base); + if (rc < 0) + return rc; + + arg = rc ? 1 : 0; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + /* rk3288 RK3368 is the first with per-pin drive-strength */ + if ((info->ctrl->type != RK3288) && ((info->ctrl->type != RK3368))) + return -ENOTSUPP; + + rc = rk3288_get_drive(bank, pin - bank->pin_base); + if (rc < 0) + return rc; + + arg = rc; + break; + default: + return -ENOTSUPP; + break; + } + + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static const struct pinconf_ops rockchip_pinconf_ops = { + .pin_config_get = rockchip_pinconf_get, + .pin_config_set = rockchip_pinconf_set, + .is_generic = true, +}; + +static const struct of_device_id rockchip_bank_match[] = { + { .compatible = "rockchip,gpio-bank" }, + { .compatible = "rockchip,rk3188-gpio-bank0" }, + {}, +}; + +static void rockchip_pinctrl_child_count(struct rockchip_pinctrl *info, + struct device_node *np) +{ + struct device_node *child; + + for_each_child_of_node(np, child) { + if (of_match_node(rockchip_bank_match, child)) + continue; + + info->nfunctions++; + info->ngroups += of_get_child_count(child); + } +} + +static int rockchip_pinctrl_parse_groups(struct device_node *np, + struct rockchip_pin_group *grp, + struct rockchip_pinctrl *info, + u32 index) +{ + struct rockchip_pin_bank *bank; + int size; + const __be32 *list; + int num; + int i, j; + int ret; + + dev_dbg(info->dev, "group(%d): %s\n", index, np->name); + + /* Initialise group */ + grp->name = np->name; + + /* + * the binding format is rockchip,pins = , + * do sanity check and calculate pins number + */ + list = of_get_property(np, "rockchip,pins", &size); + /* we do not check return since it's safe node passed down */ + size /= sizeof(*list); + if (!size || size % 4) { + dev_err(info->dev, "wrong pins number or pins and configs should be by 4\n"); + return -EINVAL; + } + + grp->npins = size / 4; + + grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int), + GFP_KERNEL); + grp->data = devm_kzalloc(info->dev, grp->npins * + sizeof(struct rockchip_pin_config), + GFP_KERNEL); + if (!grp->pins || !grp->data) + return -ENOMEM; + + for (i = 0, j = 0; i < size; i += 4, j++) { + const __be32 *phandle; + struct device_node *np_config; + + num = be32_to_cpu(*list++); + bank = bank_num_to_bank(info, num); + if (IS_ERR(bank)) + return PTR_ERR(bank); + + grp->pins[j] = bank->pin_base + be32_to_cpu(*list++); + grp->data[j].func = be32_to_cpu(*list++); + + phandle = list++; + if (!phandle) + return -EINVAL; + + np_config = of_find_node_by_phandle(be32_to_cpup(phandle)); + ret = pinconf_generic_parse_dt_config(np_config, + &grp->data[j].configs, &grp->data[j].nconfigs); + if (ret) + return ret; + } + + return 0; +} + +static int rockchip_pinctrl_parse_functions(struct device_node *np, + struct rockchip_pinctrl *info, + u32 index) +{ + struct device_node *child; + struct rockchip_pmx_func *func; + struct rockchip_pin_group *grp; + int ret; + static u32 grp_index; + u32 i = 0; + + dev_dbg(info->dev, "parse function(%d): %s\n", index, np->name); + + func = &info->functions[index]; + + /* Initialise function */ + func->name = np->name; + func->ngroups = of_get_child_count(np); + if (func->ngroups <= 0) + return 0; + + func->groups = devm_kzalloc(info->dev, + func->ngroups * sizeof(char *), GFP_KERNEL); + if (!func->groups) + return -ENOMEM; + + for_each_child_of_node(np, child) { + func->groups[i] = child->name; + grp = &info->groups[grp_index++]; + ret = rockchip_pinctrl_parse_groups(child, grp, info, i++); + if (ret) + return ret; + } + + return 0; +} + +static int rockchip_pinctrl_parse_dt(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *child; + int ret; + int i; + + rockchip_pinctrl_child_count(info, np); + + dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions); + dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups); + + info->functions = devm_kzalloc(dev, info->nfunctions * + sizeof(struct rockchip_pmx_func), + GFP_KERNEL); + if (!info->functions) { + dev_err(dev, "failed to allocate memory for function list\n"); + return -EINVAL; + } + + info->groups = devm_kzalloc(dev, info->ngroups * + sizeof(struct rockchip_pin_group), + GFP_KERNEL); + if (!info->groups) { + dev_err(dev, "failed allocate memory for ping group list\n"); + return -EINVAL; + } + + i = 0; + + for_each_child_of_node(np, child) { + if (of_match_node(rockchip_bank_match, child)) + continue; + + ret = rockchip_pinctrl_parse_functions(child, info, i++); + if (ret) { + dev_err(&pdev->dev, "failed to parse function\n"); + return ret; + } + } + + return 0; +} + +static int rockchip_pinctrl_register(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct pinctrl_desc *ctrldesc = &info->pctl; + struct pinctrl_pin_desc *pindesc, *pdesc; + struct rockchip_pin_bank *pin_bank; + int pin, bank, ret; + int k; + + ctrldesc->name = "rockchip-pinctrl"; + ctrldesc->owner = THIS_MODULE; + ctrldesc->pctlops = &rockchip_pctrl_ops; + ctrldesc->pmxops = &rockchip_pmx_ops; + ctrldesc->confops = &rockchip_pinconf_ops; + + pindesc = devm_kzalloc(&pdev->dev, sizeof(*pindesc) * + info->ctrl->nr_pins, GFP_KERNEL); + if (!pindesc) { + dev_err(&pdev->dev, "mem alloc for pin descriptors failed\n"); + return -ENOMEM; + } + ctrldesc->pins = pindesc; + ctrldesc->npins = info->ctrl->nr_pins; + + pdesc = pindesc; + for (bank = 0 , k = 0; bank < info->ctrl->nr_banks; bank++) { + pin_bank = &info->ctrl->pin_banks[bank]; + for (pin = 0; pin < pin_bank->nr_pins; pin++, k++) { + pdesc->number = k; + pdesc->name = kasprintf(GFP_KERNEL, "%s-%d", + pin_bank->name, pin); + pdesc++; + } + } + + info->pctl_dev = pinctrl_register(ctrldesc, &pdev->dev, info); + if (!info->pctl_dev) { + dev_err(&pdev->dev, "could not register pinctrl driver\n"); + return -EINVAL; + } + + for (bank = 0; bank < info->ctrl->nr_banks; ++bank) { + pin_bank = &info->ctrl->pin_banks[bank]; + pin_bank->grange.name = pin_bank->name; + pin_bank->grange.id = bank; + pin_bank->grange.pin_base = pin_bank->pin_base; + pin_bank->grange.base = pin_bank->gpio_chip.base; + pin_bank->grange.npins = pin_bank->gpio_chip.ngpio; + pin_bank->grange.gc = &pin_bank->gpio_chip; + pinctrl_add_gpio_range(info->pctl_dev, &pin_bank->grange); + } + + ret = rockchip_pinctrl_parse_dt(pdev, info); + if (ret) { + pinctrl_unregister(info->pctl_dev); + return ret; + } + + return 0; +} + +/* + * GPIO handling + */ + +static int rockchip_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + return pinctrl_request_gpio(chip->base + offset); +} + +static void rockchip_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + pinctrl_free_gpio(chip->base + offset); +} + +static void rockchip_gpio_set(struct gpio_chip *gc, unsigned offset, int value) +{ + struct rockchip_pin_bank *bank = gc_to_pin_bank(gc); + void __iomem *reg = bank->reg_base + GPIO_SWPORT_DR; + unsigned long flags; + u32 data; + + spin_lock_irqsave(&bank->slock, flags); + + data = readl(reg); + data &= ~BIT(offset); + if (value) + data |= BIT(offset); + writel(data, reg); + + spin_unlock_irqrestore(&bank->slock, flags); +} + +/* + * Returns the level of the pin for input direction and setting of the DR + * register for output gpios. + */ +static int rockchip_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct rockchip_pin_bank *bank = gc_to_pin_bank(gc); + u32 data; + + data = readl(bank->reg_base + GPIO_EXT_PORT); + data >>= offset; + data &= 1; + return data; +} + +/* + * gpiolib gpio_direction_input callback function. The setting of the pin + * mux function as 'gpio input' will be handled by the pinctrl susbsystem + * interface. + */ +static int rockchip_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + return pinctrl_gpio_direction_input(gc->base + offset); +} + +/* + * gpiolib gpio_direction_output callback function. The setting of the pin + * mux function as 'gpio output' will be handled by the pinctrl susbsystem + * interface. + */ +static int rockchip_gpio_direction_output(struct gpio_chip *gc, + unsigned offset, int value) +{ + rockchip_gpio_set(gc, offset, value); + return pinctrl_gpio_direction_output(gc->base + offset); +} + +/* + * gpiolib gpio_to_irq callback function. Creates a mapping between a GPIO pin + * and a virtual IRQ, if not already present. + */ +static int rockchip_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct rockchip_pin_bank *bank = gc_to_pin_bank(gc); + unsigned int virq; + + if (!bank->domain) + return -ENXIO; + + virq = irq_create_mapping(bank->domain, offset); + + return (virq) ? : -ENXIO; +} + +static const struct gpio_chip rockchip_gpiolib_chip = { + .request = rockchip_gpio_request, + .free = rockchip_gpio_free, + .set = rockchip_gpio_set, + .get = rockchip_gpio_get, + .direction_input = rockchip_gpio_direction_input, + .direction_output = rockchip_gpio_direction_output, + .to_irq = rockchip_gpio_to_irq, + .owner = THIS_MODULE, +}; + +/* + * Interrupt handling + */ + +static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc) +{ + struct irq_chip *chip = irq_get_chip(irq); + struct rockchip_pin_bank *bank = irq_get_handler_data(irq); + u32 polarity = 0, data = 0; + u32 pend; + bool edge_changed = false; + + pinctrl_dbg(bank->drvdata->dev, "got irq for bank %s\n", bank->name); + + chained_irq_enter(chip, desc); + + pend = readl_relaxed(bank->reg_base + GPIO_INT_STATUS); + + if (bank->toggle_edge_mode) { + polarity = readl_relaxed(bank->reg_base + + GPIO_INT_POLARITY); + data = readl_relaxed(bank->reg_base + GPIO_EXT_PORT); + } + + while (pend) { + unsigned int virq; + + irq = __ffs(pend); + pend &= ~BIT(irq); + virq = irq_linear_revmap(bank->domain, irq); + + if (!virq) { + dev_err(bank->drvdata->dev, "unmapped irq %d\n", irq); + continue; + } + + pinctrl_dbg(bank->drvdata->dev, "handling irq %d\n", irq); + + /* + * Triggering IRQ on both rising and falling edge + * needs manual intervention. + */ + if (bank->toggle_edge_mode & BIT(irq)) { + if (data & BIT(irq)) + polarity &= ~BIT(irq); + else + polarity |= BIT(irq); + + edge_changed = true; + } + + generic_handle_irq(virq); + } + + if (bank->toggle_edge_mode && edge_changed) { + /* Interrupt params should only be set with ints disabled */ + data = readl_relaxed(bank->reg_base + GPIO_INTEN); + writel_relaxed(0, bank->reg_base + GPIO_INTEN); + writel(polarity, bank->reg_base + GPIO_INT_POLARITY); + writel(data, bank->reg_base + GPIO_INTEN); + } + + chained_irq_exit(chip, desc); +} + +static int rockchip_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct rockchip_pin_bank *bank = irq_data_get_irq_chip_data(d); + //struct rockchip_pinctrl *info = bank->drvdata; + u32 mask = BIT(d->hwirq); + u32 polarity; + u32 level; + u32 data; + int ret; + unsigned long flags; + + /* make sure the pin is configured as gpio input */ + ret = rockchip_set_mux(bank, d->hwirq, RK_FUNC_GPIO); + if (ret < 0) + return ret; + + data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR); + data &= ~mask; + writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR); + + if (type & IRQ_TYPE_EDGE_BOTH) + __irq_set_handler_locked(d->irq, handle_edge_irq); + else + __irq_set_handler_locked(d->irq, handle_level_irq); + + spin_lock_irqsave(&bank->slock, flags); + + level = readl_relaxed(bank->reg_base + GPIO_INTTYPE_LEVEL); + polarity = readl_relaxed(bank->reg_base + GPIO_INT_POLARITY); + + switch (type) { + case IRQ_TYPE_EDGE_BOTH: + bank->toggle_edge_mode |= mask; + level |= mask; + + /* + * Determine gpio state. If 1 next interrupt should be falling + * otherwise rising. + */ + data = readl(bank->reg_base + GPIO_EXT_PORT); + if (data & mask) + polarity &= ~mask; + else + polarity |= mask; + break; + case IRQ_TYPE_EDGE_RISING: + bank->toggle_edge_mode &= ~mask; + level |= mask; + polarity |= mask; + break; + case IRQ_TYPE_EDGE_FALLING: + bank->toggle_edge_mode &= ~mask; + level |= mask; + polarity &= ~mask; + break; + case IRQ_TYPE_LEVEL_HIGH: + bank->toggle_edge_mode &= ~mask; + level &= ~mask; + polarity |= mask; + break; + case IRQ_TYPE_LEVEL_LOW: + bank->toggle_edge_mode &= ~mask; + level &= ~mask; + polarity &= ~mask; + break; + default: + //irq_gc_unlock(gc); + return -EINVAL; + } + + writel_relaxed(level, bank->reg_base + GPIO_INTTYPE_LEVEL); + writel_relaxed(polarity, bank->reg_base + GPIO_INT_POLARITY); + + spin_unlock_irqrestore(&bank->slock, flags); + + //DBG_PINCTRL("%s:type=%d,irq=%d,hwirq=%d,ok\n",__func__,type, d->irq, (int)d->hwirq); + return 0; +} + +static inline void rockchip_gpio_bit_op(void __iomem *reg_base + , unsigned int offset, u32 bit, unsigned char flag) +{ + u32 val = __raw_readl(reg_base + offset); + if (flag) + val |= BIT(bit); + else + val &= ~BIT(bit); + + + __raw_writel(val, reg_base + offset); +} + +static inline unsigned gpio_to_bit(struct rockchip_pin_bank *bank, unsigned gpio) +{ + while (gpio >= (bank->pin_base + bank->nr_pins)) + bank++; + + return gpio - bank->pin_base; +} + +static inline unsigned offset_to_bit(unsigned offset) +{ + return 1u << offset; +} + +static void GPIOEnableIntr(void __iomem *reg_base, unsigned int bit) +{ + rockchip_gpio_bit_op(reg_base, GPIO_INTEN, bit, 1); +} + +static void GPIODisableIntr(void __iomem *reg_base, unsigned int bit) +{ + rockchip_gpio_bit_op(reg_base, GPIO_INTEN, bit, 0); +} + +static void GPIOAckIntr(void __iomem *reg_base, unsigned int bit) +{ + rockchip_gpio_bit_op(reg_base, GPIO_PORTS_EOI, bit, 1); +} + +static int rockchip_gpio_irq_set_wake(struct irq_data *d, unsigned int on) +{ + struct rockchip_pin_bank *bank = irq_data_get_irq_chip_data(d); + //struct rockchip_pinctrl *info = bank->drvdata; + u32 bit = d->hwirq; + unsigned long flags; + //int pin = d->hwirq; + + spin_lock_irqsave(&bank->slock, flags); + + if (on) + { + bank->suspend_wakeup |= BIT(bit); + } + else + { + bank->suspend_wakeup &= ~BIT(bit); + } + spin_unlock_irqrestore(&bank->slock, flags); + + //DBG_PINCTRL("%s:irq=%d,hwirq=%d,bank->reg_base=0x%x,bit=%d\n" + //, __func__,d->irq, (int)d->hwirq, (int)bank->reg_base,bit); + return 0; +} + +static void rockchip_gpio_irq_unmask(struct irq_data *d) +{ + struct rockchip_pin_bank *bank = irq_data_get_irq_chip_data(d); + //struct rockchip_pinctrl *info = bank->drvdata; + u32 bit = d->hwirq; + unsigned long flags; + //int pin = d->hwirq; + + spin_lock_irqsave(&bank->slock, flags); + GPIOEnableIntr(bank->reg_base, bit); + spin_unlock_irqrestore(&bank->slock, flags); + + //DBG_PINCTRL("%s:irq=%d,hwirq=%d,bank->reg_base=0x%x,bit=%d\n" + //, __func__,d->irq, (int)d->hwirq, (int)bank->reg_base,bit); +} + +static void rockchip_gpio_irq_mask(struct irq_data *d) +{ + struct rockchip_pin_bank *bank = irq_data_get_irq_chip_data(d); + //struct rockchip_pinctrl *info = bank->drvdata; + //u32 bit = gpio_to_bit(bank, d->irq); + u32 bit = d->hwirq; + unsigned long flags; + //int pin = d->hwirq; + + spin_lock_irqsave(&bank->slock, flags); + GPIODisableIntr(bank->reg_base, bit); + spin_unlock_irqrestore(&bank->slock, flags); + + //DBG_PINCTRL("%s:irq=%d,hwirq=%d,bank->reg_base=0x%x,bit=%d\n" + //, __func__,d->irq, (int)d->hwirq, (int)bank->reg_base,bit); +} + +static void rockchip_gpio_irq_ack(struct irq_data *d) +{ + struct rockchip_pin_bank *bank = irq_data_get_irq_chip_data(d); + //struct rockchip_pinctrl *info = bank->drvdata; + //u32 bit = gpio_to_bit(bank, d->irq); + u32 bit = d->hwirq; + //int pin = d->hwirq; + + GPIOAckIntr(bank->reg_base, bit); + + //DBG_PINCTRL("%s:irq=%d,hwirq=%d,bank->reg_base=0x%x,bit=%d\n" + //, __func__,d->irq, (int)d->hwirq, (int)bank->reg_base,bit); +} + +static struct irq_chip rockchip_gpio_irq_chip = { + .name = "ROCKCHIP_GPIO_CHIP", + .irq_ack = rockchip_gpio_irq_ack, + .irq_disable = rockchip_gpio_irq_mask, + .irq_mask = rockchip_gpio_irq_mask, + .irq_unmask = rockchip_gpio_irq_unmask, + .irq_set_type = rockchip_gpio_irq_set_type, + .irq_set_wake = rockchip_gpio_irq_set_wake, +}; + +static int rockchip_gpio_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct rockchip_pin_bank *bank = d->host_data; + //struct rockchip_pinctrl *info = bank->drvdata; + struct irq_data *irq_data = irq_get_irq_data(irq); + //int pin = hwirq; + + if (!bank) + { + printk("%s:bank=0x%p,irq=%d\n",__func__,bank, irq); + return -EINVAL; + } + + irq_set_chip_and_handler(irq, &rockchip_gpio_irq_chip, handle_level_irq); + irq_set_chip_data(irq, bank); + set_irq_flags(irq, IRQF_VALID); + + irq_data->hwirq = hwirq; + irq_data->irq = irq; + + pinctrl_dbg(bank->drvdata->dev, "%s:irq = %d, hwirq =%ld\n",__func__,irq, hwirq); + return 0; +} + +const struct irq_domain_ops rockchip_gpio_irq_ops = { + .map = rockchip_gpio_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +static int rockchip_interrupts_register(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct rockchip_pin_ctrl *ctrl = info->ctrl; + struct rockchip_pin_bank *bank = ctrl->pin_banks; + //unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; + int i; + + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + if (!bank->valid) { + dev_warn(&pdev->dev, "bank %s is not valid\n", + bank->name); + continue; + } + + __raw_writel(0, bank->reg_base + GPIO_INTEN); + + bank->drvdata = info; + bank->domain = irq_domain_add_linear(bank->of_node, 32, + &rockchip_gpio_irq_ops, bank); + if (!bank->domain) { + dev_warn(&pdev->dev, "could not initialize irq domain for bank %s\n", + bank->name); + continue; + } + + //if(atomic_read(&info->bank_debug_flag) == (bank->bank_num + 1)) + //printk("%s:bank_num=%d\n",__func__,bank->bank_num); + + irq_set_handler_data(bank->irq, bank); + irq_set_chained_handler(bank->irq, rockchip_irq_demux); + } + + return 0; +} + +static int rockchip_gpiolib_register(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct rockchip_pin_ctrl *ctrl = info->ctrl; + struct rockchip_pin_bank *bank = ctrl->pin_banks; + struct gpio_chip *gc; + int ret; + int i; + + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + if (!bank->valid) { + dev_warn(&pdev->dev, "bank %s is not valid\n", + bank->name); + continue; + } + + bank->gpio_chip = rockchip_gpiolib_chip; + + gc = &bank->gpio_chip; + gc->base = bank->pin_base; + gc->ngpio = bank->nr_pins; + gc->dev = &pdev->dev; + gc->of_node = bank->of_node; + gc->label = bank->name; + + ret = gpiochip_add(gc); + if (ret) { + dev_err(&pdev->dev, "failed to register gpio_chip %s, error code: %d\n", + gc->label, ret); + goto fail; + } + } + + rockchip_interrupts_register(pdev, info); + + return 0; + +fail: + for (--i, --bank; i >= 0; --i, --bank) { + if (!bank->valid) + continue; + + if (gpiochip_remove(&bank->gpio_chip)) + dev_err(&pdev->dev, "gpio chip %s remove failed\n", + bank->gpio_chip.label); + } + return ret; +} + +static int rockchip_gpiolib_unregister(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct rockchip_pin_ctrl *ctrl = info->ctrl; + struct rockchip_pin_bank *bank = ctrl->pin_banks; + int ret = 0; + int i; + + for (i = 0; !ret && i < ctrl->nr_banks; ++i, ++bank) { + if (!bank->valid) + continue; + + ret = gpiochip_remove(&bank->gpio_chip); + } + + if (ret) + dev_err(&pdev->dev, "gpio chip remove failed\n"); + + return ret; +} + +static int rockchip_get_bank_data(struct rockchip_pin_bank *bank, + struct rockchip_pinctrl *info) +{ + struct resource res; + void __iomem *base; + + if (of_address_to_resource(bank->of_node, 0, &res)) { + dev_err(info->dev, "cannot find IO resource for bank\n"); + return -ENOENT; + } + + bank->reg_base = devm_ioremap_resource(info->dev, &res); + if (IS_ERR(bank->reg_base)) + return PTR_ERR(bank->reg_base); + + /* + * special case, where parts of the pull setting-registers are + * part of the PMU register space + */ + if (of_device_is_compatible(bank->of_node, + "rockchip,rk3188-gpio-bank0")) { + struct device_node *node; + + node = of_parse_phandle(bank->of_node->parent, + "rockchip,pmu", 0); + if (!node) { + if (of_address_to_resource(bank->of_node, 1, &res)) { + dev_err(info->dev, "cannot find IO resource for bank\n"); + return -ENOENT; + } + + base = devm_ioremap_resource(info->dev, &res); + if (IS_ERR(base)) + return PTR_ERR(base); + rockchip_regmap_config.max_register = + resource_size(&res) - 4; + rockchip_regmap_config.name = + "rockchip,rk3188-gpio-bank0-pull"; + bank->regmap_pull = devm_regmap_init_mmio(info->dev, + base, + &rockchip_regmap_config); + } + } + + bank->irq = irq_of_parse_and_map(bank->of_node, 0); + + //bank->clk = of_clk_get(bank->of_node, 0); + //if (IS_ERR(bank->clk)) + //return PTR_ERR(bank->clk); + + //return clk_prepare_enable(bank->clk); + return 0; +} + +static const struct of_device_id rockchip_pinctrl_dt_match[]; + +/* retrieve the soc specific data */ +static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data( + struct rockchip_pinctrl *d, + struct platform_device *pdev) +{ + const struct of_device_id *match; + struct device_node *node = pdev->dev.of_node; + struct device_node *np; + struct rockchip_pin_ctrl *ctrl; + struct rockchip_pin_bank *bank; + int grf_offs, pmu_offs, i, j; + + match = of_match_node(rockchip_pinctrl_dt_match, node); + ctrl = (struct rockchip_pin_ctrl *)match->data; + + for_each_child_of_node(node, np) { + if (!of_find_property(np, "gpio-controller", NULL)) + continue; + + bank = ctrl->pin_banks; + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + if (!strcmp(bank->name, np->name)) { + bank->of_node = np; + + if (!rockchip_get_bank_data(bank, d)) + bank->valid = true; + + break; + } + } + } + + grf_offs = ctrl->grf_mux_offset; + pmu_offs = ctrl->pmu_mux_offset; + bank = ctrl->pin_banks; + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + int bank_pins = 0; + + spin_lock_init(&bank->slock); + bank->drvdata = d; + bank->pin_base = ctrl->nr_pins; + ctrl->nr_pins += bank->nr_pins; + + /* calculate iomux offsets */ + for (j = 0; j < 4; j++) { + struct rockchip_iomux *iom = &bank->iomux[j]; + int inc; + + if (bank_pins >= bank->nr_pins) + break; + + /* preset offset value, set new start value */ + if (iom->offset >= 0) { + if (iom->type & IOMUX_SOURCE_PMU) + pmu_offs = iom->offset; + else + grf_offs = iom->offset; + } else { /* set current offset */ + iom->offset = (iom->type & IOMUX_SOURCE_PMU) ? + pmu_offs : grf_offs; + } + + pinctrl_dbg(d->dev, "bank %d, iomux %d has offset 0x%x\n", + i, j, iom->offset); + + /* + * Increase offset according to iomux width. + * 4bit iomux'es are spread over two registers. + */ + inc = (iom->type & IOMUX_WIDTH_4BIT) ? 8 : 4; + if (iom->type & IOMUX_SOURCE_PMU) + pmu_offs += inc; + else + grf_offs += inc; + + bank_pins += 8; + } + } + + return ctrl; +} + +static int rockchip_pinctrl_probe(struct platform_device *pdev) +{ + struct rockchip_pinctrl *info; + struct device *dev = &pdev->dev; + struct rockchip_pin_ctrl *ctrl; + struct device_node *np = pdev->dev.of_node, *node; + struct resource *res; + void __iomem *base; + int ret; + + if (!dev->of_node) { + dev_err(dev, "device tree node not found\n"); + return -ENODEV; + } + + info = devm_kzalloc(dev, sizeof(struct rockchip_pinctrl), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->dev = dev; + + ctrl = rockchip_pinctrl_get_soc_data(info, pdev); + if (!ctrl) { + dev_err(dev, "driver data not available\n"); + return -EINVAL; + } + info->ctrl = ctrl; + g_info = info; + + node = of_parse_phandle(np, "rockchip,grf", 0); + if (node) { + info->regmap_base = syscon_node_to_regmap(node); + if (IS_ERR(info->regmap_base)) + return PTR_ERR(info->regmap_base); + } else { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + rockchip_regmap_config.max_register = resource_size(res) - 4; + rockchip_regmap_config.name = "rockchip,pinctrl"; + info->regmap_base = devm_regmap_init_mmio(&pdev->dev, base, + &rockchip_regmap_config); + + /* to check for the old dt-bindings */ + info->reg_size = resource_size(res); + + /* Honor the old binding, with pull registers as 2nd resource */ + if (ctrl->type == RK3188 && info->reg_size < 0x200) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + rockchip_regmap_config.max_register = + resource_size(res) - 4; + rockchip_regmap_config.name = "rockchip,pinctrl-pull"; + info->regmap_pull = devm_regmap_init_mmio(&pdev->dev, + base, + &rockchip_regmap_config); + } + } + + /* try to find the optional reference to the pmu syscon */ + node = of_parse_phandle(np, "rockchip,pmu", 0); + if (node) { + info->regmap_pmu = syscon_node_to_regmap(node); + if (IS_ERR(info->regmap_pmu)) + return PTR_ERR(info->regmap_pmu); + } + + ret = rockchip_gpiolib_register(pdev, info); + if (ret) + return ret; + + ret = rockchip_pinctrl_register(pdev, info); + if (ret) { + rockchip_gpiolib_unregister(pdev, info); + return ret; + } + + platform_set_drvdata(pdev, info); + printk("%s:init ok\n",__func__); + + return 0; +} + +#ifdef CONFIG_PM +static int rockchip_pinctrl_suspend(void) +{ + struct rockchip_pinctrl *info = g_info; + struct rockchip_pin_ctrl *ctrl = info->ctrl; + struct rockchip_pin_bank *bank = ctrl->pin_banks; + int n; + //int value = 0; + + for(n=0; nnr_banks-1; n++) + { +#if 0 + int i; + for(i=0; i<0x60; i=i+4) + { + value = readl_relaxed(bank->reg_base + i); + printk("%s:bank_num=%d,reg[0x%x+0x%x]=0x%x,bank_name=%s\n",__func__,bank->bank_num, bank->reg_base, i, value, bank->name); + } +#endif + bank->saved_wakeup = __raw_readl(bank->reg_base + GPIO_INTEN); + __raw_writel(bank->suspend_wakeup, bank->reg_base + GPIO_INTEN); + + //if (!bank->suspend_wakeup) + //clk_disable_unprepare(bank->clk); + + //if(atomic_read(&info->bank_debug_flag) == (bank->bank_num + 1)) + //printk("%s:bank_num=%d, suspend_wakeup=0x%x\n" + //,__func__, bank->bank_num, bank->suspend_wakeup); + bank++; + } + + + return 0; +} + +static void rockchip_pinctrl_resume(void) +{ + struct rockchip_pinctrl *info = g_info; + struct rockchip_pin_ctrl *ctrl = info->ctrl; + struct rockchip_pin_bank *bank = ctrl->pin_banks; + int n; + u32 isr; + + for(n=0; nnr_banks-1; n++) + { +#if 0 + int i; + for(i=0; i<0x60; i=i+4) + { + u32 value = readl_relaxed(bank->reg_base + i); + printk("%s:bank_num=%d,reg[0x%x+0x%x]=0x%x,bank_name=%s\n",__func__,bank->bank_num, bank->reg_base, i, value, bank->name); + } +#endif + //if (!bank->suspend_wakeup) + //clk_prepare_enable(bank->clk); + + /* keep enable for resume irq */ + isr = __raw_readl(bank->reg_base + GPIO_INT_STATUS); + __raw_writel(bank->saved_wakeup | (bank->suspend_wakeup & isr) + , bank->reg_base + GPIO_INTEN); + + //if(atomic_read(&info->bank_debug_flag) == (bank->bank_num + 1)) + //printk("%s:bank_num=%d, suspend_wakeup=0x%x\n",__func__ + //bank->bank_num, bank->saved_wakeup | (bank->suspend_wakeup & isr)); + + bank++; + } + +} +#endif + +static struct rockchip_pin_bank rk2928_pin_banks[] = { + PIN_BANK(0, 32, "gpio0"), + PIN_BANK(1, 32, "gpio1"), + PIN_BANK(2, 32, "gpio2"), + PIN_BANK(3, 32, "gpio3"), +}; + +static struct rockchip_pin_ctrl rk2928_pin_ctrl = { + .pin_banks = rk2928_pin_banks, + .nr_banks = ARRAY_SIZE(rk2928_pin_banks), + .label = "RK2928-GPIO", + .type = RK2928, + .grf_mux_offset = 0xa8, + .pull_calc_reg = rk2928_calc_pull_reg_and_bit, +}; + +static struct rockchip_pin_bank rk3066a_pin_banks[] = { + PIN_BANK(0, 32, "gpio0"), + PIN_BANK(1, 32, "gpio1"), + PIN_BANK(2, 32, "gpio2"), + PIN_BANK(3, 32, "gpio3"), + PIN_BANK(4, 32, "gpio4"), + PIN_BANK(6, 16, "gpio6"), +}; + +static struct rockchip_pin_ctrl rk3066a_pin_ctrl = { + .pin_banks = rk3066a_pin_banks, + .nr_banks = ARRAY_SIZE(rk3066a_pin_banks), + .label = "RK3066a-GPIO", + .type = RK2928, + .grf_mux_offset = 0xa8, + .pull_calc_reg = rk2928_calc_pull_reg_and_bit, +}; + +static struct rockchip_pin_bank rk3066b_pin_banks[] = { + PIN_BANK(0, 32, "gpio0"), + PIN_BANK(1, 32, "gpio1"), + PIN_BANK(2, 32, "gpio2"), + PIN_BANK(3, 32, "gpio3"), +}; + +static struct rockchip_pin_ctrl rk3066b_pin_ctrl = { + .pin_banks = rk3066b_pin_banks, + .nr_banks = ARRAY_SIZE(rk3066b_pin_banks), + .label = "RK3066b-GPIO", + .type = RK3066B, + .grf_mux_offset = 0x60, +}; + +static struct rockchip_pin_bank rk3188_pin_banks[] = { + PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_GPIO_ONLY, 0, 0, 0), + PIN_BANK(1, 32, "gpio1"), + PIN_BANK(2, 32, "gpio2"), + PIN_BANK(3, 32, "gpio3"), +}; + +static struct rockchip_pin_ctrl rk3188_pin_ctrl = { + .pin_banks = rk3188_pin_banks, + .nr_banks = ARRAY_SIZE(rk3188_pin_banks), + .label = "RK3188-GPIO", + .type = RK3188, + .grf_mux_offset = 0x60, + .pull_calc_reg = rk3188_calc_pull_reg_and_bit, +}; + +static struct rockchip_pin_bank rk3288_pin_banks[] = { + PIN_BANK_IOMUX_FLAGS(0, 24, "gpio0", IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_UNROUTED + ), + PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", IOMUX_UNROUTED, + IOMUX_UNROUTED, + IOMUX_UNROUTED, + 0 + ), + PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2", 0, 0, 0, IOMUX_UNROUTED), + PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3", 0, 0, 0, IOMUX_WIDTH_4BIT), + PIN_BANK_IOMUX_FLAGS(4, 32, "gpio4", IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + 0, + 0 + ), + PIN_BANK_IOMUX_FLAGS(5, 32, "gpio5", IOMUX_UNROUTED, + 0, + 0, + IOMUX_UNROUTED + ), + PIN_BANK_IOMUX_FLAGS(6, 32, "gpio6", 0, 0, 0, IOMUX_UNROUTED), + PIN_BANK_IOMUX_FLAGS(7, 32, "gpio7", 0, + 0, + IOMUX_WIDTH_4BIT, + IOMUX_UNROUTED + ), + PIN_BANK(8, 16, "gpio8"), +}; + +static struct rockchip_pin_ctrl rk3288_pin_ctrl = { + .pin_banks = rk3288_pin_banks, + .nr_banks = ARRAY_SIZE(rk3288_pin_banks), + .label = "RK3288-GPIO", + .type = RK3288, + .grf_mux_offset = 0x0, + .pmu_mux_offset = 0x84, + .pull_calc_reg = rk3288_calc_pull_reg_and_bit, +}; + +static struct rockchip_pin_bank rk3368_pin_banks[] = { + PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU), + PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", 0, 0, 0, 0), + PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2", 0, 0, 0, 0), + PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3", 0, 0, 0, 0), +}; + +static struct rockchip_pin_ctrl rk3368_pin_ctrl = { + .pin_banks = rk3368_pin_banks, + .nr_banks = ARRAY_SIZE(rk3368_pin_banks), + .label = "RK3368-GPIO", + .type = RK3368, + .grf_mux_offset = 0x0, + .pmu_mux_offset = 0x0, + .pull_calc_reg = rk3368_calc_pull_reg_and_bit, +}; + +static const struct of_device_id rockchip_pinctrl_dt_match[] = { + { .compatible = "rockchip,rk2928-pinctrl", + .data = (void *)&rk2928_pin_ctrl }, + { .compatible = "rockchip,rk3066a-pinctrl", + .data = (void *)&rk3066a_pin_ctrl }, + { .compatible = "rockchip,rk3066b-pinctrl", + .data = (void *)&rk3066b_pin_ctrl }, + { .compatible = "rockchip,rk3188-pinctrl", + .data = (void *)&rk3188_pin_ctrl }, + { .compatible = "rockchip,rk3288-pinctrl", + .data = (void *)&rk3288_pin_ctrl }, + { .compatible = "rockchip,rk3368-pinctrl", + .data = (void *)&rk3368_pin_ctrl }, + {}, +}; +MODULE_DEVICE_TABLE(of, rockchip_pinctrl_dt_match); + +static struct platform_driver rockchip_pinctrl_driver = { + .probe = rockchip_pinctrl_probe, + .driver = { + .name = "rockchip-pinctrl", + .owner = THIS_MODULE, + .of_match_table = rockchip_pinctrl_dt_match, + }, +}; + +#ifdef CONFIG_PM +static struct syscore_ops rockchip_gpio_syscore_ops = { + .suspend = rockchip_pinctrl_suspend, + .resume = rockchip_pinctrl_resume, +}; +#endif + +static int __init rockchip_pinctrl_drv_register(void) +{ +#ifdef CONFIG_PM + register_syscore_ops(&rockchip_gpio_syscore_ops); +#endif + return platform_driver_register(&rockchip_pinctrl_driver); +} +postcore_initcall(rockchip_pinctrl_drv_register); + +MODULE_AUTHOR("Heiko Stuebner "); +MODULE_DESCRIPTION("Rockchip pinctrl driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/avs/Kconfig b/drivers/power/avs/Kconfig index 2a1008b61121..7f3d389bd601 100644 --- a/drivers/power/avs/Kconfig +++ b/drivers/power/avs/Kconfig @@ -10,3 +10,11 @@ menuconfig POWER_AVS AVS is also called SmartReflex on OMAP devices. Say Y here to enable Adaptive Voltage Scaling class support. + +config ROCKCHIP_IODOMAIN + tristate "Rockchip IO domain support" + depends on ARCH_ROCKCHIP && OF + help + Say y here to enable support io domains on Rockchip SoCs. It is + necessary for the io domain setting of the SoC to match the + voltage supplied by the regulators. diff --git a/drivers/power/avs/Makefile b/drivers/power/avs/Makefile index 0843386a6c19..ba4c7bc69225 100644 --- a/drivers/power/avs/Makefile +++ b/drivers/power/avs/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_POWER_AVS_OMAP) += smartreflex.o +obj-$(CONFIG_ROCKCHIP_IODOMAIN) += rockchip-io-domain.o diff --git a/drivers/power/avs/rockchip-io-domain.c b/drivers/power/avs/rockchip-io-domain.c new file mode 100755 index 000000000000..ef7967e21869 --- /dev/null +++ b/drivers/power/avs/rockchip-io-domain.c @@ -0,0 +1,407 @@ +/* + * Rockchip IO Voltage Domain driver + * + * Copyright 2014 MundoReader S.L. + * Copyright 2014 Google, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#define MAX_SUPPLIES 16 + +/* + * The max voltage for 1.8V and 3.3V come from the Rockchip datasheet under + * "Recommended Operating Conditions" for "Digital GPIO". When the typical + * is 3.3V the max is 3.6V. When the typical is 1.8V the max is 1.98V. + * + * They are used like this: + * - If the voltage on a rail is above the "1.8" voltage (1.98V) we'll tell the + * SoC we're at 3.3. + * - If the voltage on a rail is above the "3.3" voltage (3.6V) we'll consider + * that to be an error. + */ +#define MAX_VOLTAGE_1_8 1980000 +#define MAX_VOLTAGE_3_3 3600000 + +#define RK3288_SOC_CON2 0x24c +#define RK3288_SOC_CON2_FLASH0 BIT(7) +#define RK3288_SOC_FLASH_SUPPLY_NUM 2 + +#define RK3368_GRF_SOC_CON15 0x43c +#define RK3368_GRF_SOC_CON15_FLASH0 BIT(14) +#define RK3368_SOC_FLASH_SUPPLY_NUM 2 + + +struct rockchip_iodomain; + +/** + * @supplies: voltage settings matching the register bits. + */ +struct rockchip_iodomain_soc_data { + int grf_offset; + const char *supply_names[MAX_SUPPLIES]; + void (*init)(struct rockchip_iodomain *iod); +}; + +struct rockchip_iodomain_supply { + struct rockchip_iodomain *iod; + struct regulator *reg; + struct notifier_block nb; + int idx; +}; + +struct rockchip_iodomain { + struct device *dev; + struct regmap *grf; + struct rockchip_iodomain_soc_data *soc_data; + struct rockchip_iodomain_supply supplies[MAX_SUPPLIES]; +}; + +static int rockchip_iodomain_write(struct rockchip_iodomain_supply *supply, + int uV) +{ + struct rockchip_iodomain *iod = supply->iod; + u32 val; + int ret; + + /* set value bit */ + val = (uV > MAX_VOLTAGE_1_8) ? 0 : 1; + val <<= supply->idx; + + /* apply hiword-mask */ + val |= (BIT(supply->idx) << 16); + + ret = regmap_write(iod->grf, iod->soc_data->grf_offset, val); + if (ret) + dev_err(iod->dev, "Couldn't write to GRF\n"); + + return ret; +} + +static int rockchip_iodomain_notify(struct notifier_block *nb, + unsigned long event, + void *data) +{ + struct rockchip_iodomain_supply *supply = + container_of(nb, struct rockchip_iodomain_supply, nb); + int uV; + int ret; + + /* + * According to Rockchip it's important to keep the SoC IO domain + * higher than (or equal to) the external voltage. That means we need + * to change it before external voltage changes happen in the case + * of an increase. + * + * Note that in the "pre" change we pick the max possible voltage that + * the regulator might end up at (the client requests a range and we + * don't know for certain the exact voltage). Right now we rely on the + * slop in MAX_VOLTAGE_1_8 and MAX_VOLTAGE_3_3 to save us if clients + * request something like a max of 3.6V when they really want 3.3V. + * We could attempt to come up with better rules if this fails. + */ +/* + if (event & REGULATOR_EVENT_PRE_VOLTAGE_CHANGE) { + struct pre_voltage_change_data *pvc_data = data; + + uV = max_t(unsigned long, pvc_data->old_uV, pvc_data->max_uV); + } else +*/ + if (event & (REGULATOR_EVENT_VOLTAGE_CHANGE)) {// | + //REGULATOR_EVENT_ABORT_VOLTAGE_CHANGE)) { + uV = (unsigned long)data; + } else { + return NOTIFY_OK; + } + + dev_dbg(supply->iod->dev, "Setting to %d\n", uV); + + if (uV > MAX_VOLTAGE_3_3) { + dev_err(supply->iod->dev, "Voltage too high: %d\n", uV); + + //if (event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE) + return NOTIFY_BAD; + } + + ret = rockchip_iodomain_write(supply, uV); +/* + if (ret && event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE) + return NOTIFY_BAD; +*/ + + dev_info(supply->iod->dev, "Setting to %d done\n", uV); + return NOTIFY_OK; +} + +static void rk3288_iodomain_init(struct rockchip_iodomain *iod) +{ + int ret; + u32 val; + + /* if no flash supply we should leave things alone */ + if (!iod->supplies[RK3288_SOC_FLASH_SUPPLY_NUM].reg) + return; + + /* + * set flash0 iodomain to also use this framework + * instead of a special gpio. + */ + val = RK3288_SOC_CON2_FLASH0 | (RK3288_SOC_CON2_FLASH0 << 16); + ret = regmap_write(iod->grf, RK3288_SOC_CON2, val); + if (ret < 0) + dev_warn(iod->dev, "couldn't update flash0 ctrl\n"); +} + +static void rk3368_iodomain_init(struct rockchip_iodomain *iod) +{ + int ret; + u32 val; + + /* if no flash supply we should leave things alone */ + if (!iod->supplies[RK3368_SOC_FLASH_SUPPLY_NUM].reg) + return; + + /* + * set flash0 iodomain to also use this framework + * instead of a special gpio. + */ + val = RK3368_GRF_SOC_CON15_FLASH0 | (RK3368_GRF_SOC_CON15_FLASH0 << 16); + ret = regmap_write(iod->grf, RK3368_GRF_SOC_CON15, val); + if (ret < 0) + dev_warn(iod->dev, "couldn't update flash0 ctrl\n"); +} + + +/* + * On the rk3188 the io-domains are handled by a shared register with the + * lower 8 bits being still being continuing drive-strength settings. + */ +static const struct rockchip_iodomain_soc_data soc_data_rk3188 = { + .grf_offset = 0x104, + .supply_names = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "ap0", + "ap1", + "cif", + "flash", + "vccio0", + "vccio1", + "lcdc0", + "lcdc1", + }, +}; + +static const struct rockchip_iodomain_soc_data soc_data_rk3288 = { + .grf_offset = 0x380, + .supply_names = { + "lcdc", /* LCDC_VDD */ + "dvp", /* DVPIO_VDD */ + "flash0", /* FLASH0_VDD (emmc) */ + "flash1", /* FLASH1_VDD (sdio1) */ + "wifi", /* APIO3_VDD (sdio0) */ + "bb", /* APIO5_VDD */ + "audio", /* APIO4_VDD */ + "sdcard", /* SDMMC0_VDD (sdmmc) */ + "gpio30", /* APIO1_VDD */ + "gpio1830", /* APIO2_VDD */ + }, + .init = rk3288_iodomain_init, +}; + +static const struct rockchip_iodomain_soc_data soc_data_rk3368 = { + .grf_offset = 0x900, + .supply_names = { + NULL, + "dvp_v18sel", /*DVP IO domain*/ + "flash0_v18sel", /*FLASH0 IO domain*/ + "wifi_v18sel", /*WIFI IO domain*/ + NULL, + "audio_v18sel", /*AUDIO IO domain*/ + "sdcard_v18sel", /*SDCARD IO domain*/ + "gpio30_v18sel", /*GPIO30 IO domain*/ + "gpio1830_v18sel", /*GPIO1830 IO domain*/ + }, + .init = rk3368_iodomain_init, +}; + + +static const struct of_device_id rockchip_iodomain_match[] = { + { + .compatible = "rockchip,rk3188-io-voltage-domain", + .data = (void *)&soc_data_rk3188 + }, + { + .compatible = "rockchip,rk3288-io-voltage-domain", + .data = (void *)&soc_data_rk3288 + }, + { + .compatible = "rockchip,rk3368-io-voltage-domain", + .data = (void *)&soc_data_rk3368 + }, + { /* sentinel */ }, +}; + +static int rockchip_iodomain_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; + struct rockchip_iodomain *iod; + int i, ret = 0; + + if (!np) + return -ENODEV; + + iod = devm_kzalloc(&pdev->dev, sizeof(*iod), GFP_KERNEL); + if (!iod) + return -ENOMEM; + + iod->dev = &pdev->dev; + platform_set_drvdata(pdev, iod); + + match = of_match_node(rockchip_iodomain_match, np); + iod->soc_data = (struct rockchip_iodomain_soc_data *)match->data; + + iod->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(iod->grf)) { + dev_err(&pdev->dev, "couldn't find grf regmap\n"); + return PTR_ERR(iod->grf); + } + + for (i = 0; i < MAX_SUPPLIES; i++) { + const char *supply_name = iod->soc_data->supply_names[i]; + struct rockchip_iodomain_supply *supply = &iod->supplies[i]; + struct regulator *reg; + int uV; + const char *regulator_name = NULL; + + if (!supply_name) + continue; + + of_property_read_string(np, supply_name, ®ulator_name); + if (!regulator_name) + continue; + + reg = regulator_get(NULL, regulator_name); + if (IS_ERR(reg)) { + ret = PTR_ERR(reg); + + /* If a supply wasn't specified, that's OK */ + if (ret == -ENODEV) + continue; + else if (ret != -EPROBE_DEFER) + dev_err(iod->dev, "couldn't get regulator %s\n", + supply_name); + goto unreg_notify; + } + + /* set initial correct value */ + uV = regulator_get_voltage(reg); + + /* must be a regulator we can get the voltage of */ + if (uV < 0) { + dev_err(iod->dev, "Can't determine voltage: %s\n", + supply_name); + goto unreg_notify; + } + + if (uV > MAX_VOLTAGE_3_3) { + dev_crit(iod->dev, + "%d uV is too high. May damage SoC!\n", + uV); + ret = -EINVAL; + goto unreg_notify; + } + + /* setup our supply */ + supply->idx = i; + supply->iod = iod; + supply->reg = reg; + supply->nb.notifier_call = rockchip_iodomain_notify; + + ret = rockchip_iodomain_write(supply, uV); + if (ret) { + supply->reg = NULL; + goto unreg_notify; + } + + /* register regulator notifier */ + ret = regulator_register_notifier(reg, &supply->nb); + if (ret) { + dev_err(&pdev->dev, + "regulator notifier request failed\n"); + supply->reg = NULL; + goto unreg_notify; + } + } + + if (iod->soc_data->init) + iod->soc_data->init(iod); + + return 0; + +unreg_notify: + for (i = MAX_SUPPLIES - 1; i >= 0; i--) { + struct rockchip_iodomain_supply *io_supply = &iod->supplies[i]; + + if (io_supply->reg) + regulator_unregister_notifier(io_supply->reg, + &io_supply->nb); + } + + return ret; +} + +static int rockchip_iodomain_remove(struct platform_device *pdev) +{ + struct rockchip_iodomain *iod = platform_get_drvdata(pdev); + int i; + + for (i = MAX_SUPPLIES - 1; i >= 0; i--) { + struct rockchip_iodomain_supply *io_supply = &iod->supplies[i]; + + if (io_supply->reg) + regulator_unregister_notifier(io_supply->reg, + &io_supply->nb); + } + + return 0; +} + +static struct platform_driver rockchip_iodomain_driver = { + .probe = rockchip_iodomain_probe, + .remove = rockchip_iodomain_remove, + .driver = { + .name = "rockchip-iodomain", + .of_match_table = rockchip_iodomain_match, + }, +}; + +module_platform_driver(rockchip_iodomain_driver); + +MODULE_DESCRIPTION("Rockchip IO-domain driver"); +MODULE_AUTHOR("Heiko Stuebner "); +MODULE_AUTHOR("Doug Anderson "); +MODULE_LICENSE("GPL v2"); -- 2.34.1