2013-06-02 23:39:40

by Heiko Stübner

[permalink] [raw]
Subject: arm: add basic support for Rockchip Cortex-A9 SoCs

Prepare your eggs and tomatoes please, I got myself a second playground ;-) .

As the title implies this series adds basic support for Rockchip's Cortex-A9
SoCs, like the RK3066 used in many cheaper tablets.

On the periphal side it's very lightweight, as it seems someone from Rockchip
was shopping in the DesignWare store.

This makes the pinctrl driver the biggest hunk in the series, but to get some
bonus points I used the generic_irq implementation for the interrupt handling,
based on Thomas' irqdomain support series.

Supported periphals are, the timers, uarts and mmc controllers, so interested
parties will need a serial console for now.

Hopefully I didn't mess up to much :-)


Dependencies:
- the irqdomain support for the generic_irq from Thomas Gleixner is required
by the pinctrl driver
- "mmc: dw_mmc: Add support DW SD/MMC driver on SOCFPGA" moves the
SDMMC_CMD_USE_HOLD_REG constant into the common header, which is required
on these Rockchip SoCs

Heiko Stuebner (10):
clocksource: dw_apb_timer_of: use the clocksource as sched clock if
necessary
clocksource: dw_apb_timer_of: add clock-handling
clk: flag to use upper half of the register as change indicator
clk: divider: add flag to limit possible dividers to even numbers
mmc: dw_mmc-pltfm: remove static from dw_mci_pltfm_remove
mmc: dw_mmc-pltfm: add Rockchip variant
pinctrl: add pinctrl driver for Rockchip SoCs
clk: add basic Rockchip rk3066a clock support
arm: add debug uarts for rockchip rk29xx and rk3xxx series
arm: add basic support for Rockchip RK3066a boards

.../bindings/pinctrl/rockchip-pinctrl.txt | 100 ++
Documentation/devicetree/bindings/rtc/dw-apb.txt | 19 +
arch/arm/Kconfig | 2 +
arch/arm/Kconfig.debug | 34 +
arch/arm/Makefile | 1 +
arch/arm/boot/dts/rk3066a-clocks.dtsi | 459 +++++++
arch/arm/boot/dts/rk3066a.dtsi | 359 ++++++
arch/arm/include/debug/rockchip.S | 42 +
arch/arm/mach-rockchip/Kconfig | 17 +
arch/arm/mach-rockchip/Makefile | 1 +
arch/arm/mach-rockchip/Makefile.boot | 3 +
arch/arm/mach-rockchip/rockchip.c | 54 +
drivers/clk/Makefile | 1 +
drivers/clk/clk-divider.c | 29 +-
drivers/clk/clk-gate.c | 24 +-
drivers/clk/clk-mux.c | 15 +-
drivers/clk/rockchip/Makefile | 6 +
drivers/clk/rockchip/clk-rockchip-pll.c | 131 ++
drivers/clk/rockchip/clk-rockchip-pll.h | 19 +
drivers/clk/rockchip/clk-rockchip.c | 315 +++++
drivers/clocksource/dw_apb_timer_of.c | 52 +-
drivers/mmc/host/dw_mmc-pltfm.c | 48 +-
drivers/pinctrl/Kconfig | 6 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-rockchip.c | 1319 ++++++++++++++++++++
include/dt-bindings/pinctrl/rockchip.h | 37 +
include/linux/clk-provider.h | 18 +
include/linux/clk/rockchip.h | 21 +
28 files changed, 3097 insertions(+), 36 deletions(-)
create mode 100644 Documentation/devicetree/bindings/pinctrl/rockchip-pinctrl.txt
create mode 100644 arch/arm/boot/dts/rk3066a-clocks.dtsi
create mode 100644 arch/arm/boot/dts/rk3066a.dtsi
create mode 100644 arch/arm/include/debug/rockchip.S
create mode 100644 arch/arm/mach-rockchip/Kconfig
create mode 100644 arch/arm/mach-rockchip/Makefile
create mode 100644 arch/arm/mach-rockchip/Makefile.boot
create mode 100644 arch/arm/mach-rockchip/rockchip.c
create mode 100644 drivers/clk/rockchip/Makefile
create mode 100644 drivers/clk/rockchip/clk-rockchip-pll.c
create mode 100644 drivers/clk/rockchip/clk-rockchip-pll.h
create mode 100644 drivers/clk/rockchip/clk-rockchip.c
create mode 100644 drivers/pinctrl/pinctrl-rockchip.c
create mode 100644 include/dt-bindings/pinctrl/rockchip.h
create mode 100644 include/linux/clk/rockchip.h

--
1.7.2.3


2013-06-02 23:00:54

by Heiko Stübner

[permalink] [raw]
Subject: [PATCH 08/10] clk: add basic Rockchip rk3066a clock support

This adds basic support for clocks on Rockchip rk3066 SoCs.
The clock handling thru small dt nodes is heavily inspired by the
sunxi clk code.

The clock specifiers are named as "x-cortex-a9-x", because the clock
semantics are the same thru the whole cortex-a9 series (rk30xx,
rk3188, rk2928; but not the other rk29 SoCs, which are Cortex-A8 based).

The plls are currently read-only, as their setting needs more
investigation. This also results in slow cpu speeds.

Signed-off-by: Heiko Stuebner <[email protected]>
---
arch/arm/boot/dts/rk3066a-clocks.dtsi | 459 +++++++++++++++++++++++++++++++
drivers/clk/Makefile | 1 +
drivers/clk/rockchip/Makefile | 6 +
drivers/clk/rockchip/clk-rockchip-pll.c | 131 +++++++++
drivers/clk/rockchip/clk-rockchip-pll.h | 19 ++
drivers/clk/rockchip/clk-rockchip.c | 315 +++++++++++++++++++++
include/linux/clk/rockchip.h | 21 ++
7 files changed, 952 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/boot/dts/rk3066a-clocks.dtsi
create mode 100644 drivers/clk/rockchip/Makefile
create mode 100644 drivers/clk/rockchip/clk-rockchip-pll.c
create mode 100644 drivers/clk/rockchip/clk-rockchip-pll.h
create mode 100644 drivers/clk/rockchip/clk-rockchip.c
create mode 100644 include/linux/clk/rockchip.h

diff --git a/arch/arm/boot/dts/rk3066a-clocks.dtsi b/arch/arm/boot/dts/rk3066a-clocks.dtsi
new file mode 100644
index 0000000..5e0614a
--- /dev/null
+++ b/arch/arm/boot/dts/rk3066a-clocks.dtsi
@@ -0,0 +1,459 @@
+/*
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/ {
+ clocks {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ /*
+ * This is a dummy clock, to be used as placeholder on
+ * other mux clocks when a specific parent clock is not
+ * yet implemented. It should be dropped when the driver
+ * is complete.
+ */
+ dummy: dummy {
+ compatible = "fixed-clock";
+ clock-frequency = <0>;
+ #clock-cells = <0>;
+ };
+
+ xin24m: xin24m {
+ compatible = "fixed-clock";
+ clock-frequency = <24000000>;
+ #clock-cells = <0>;
+ };
+
+ apll: apll@20000000 {
+ compatible = "rockchip,rk3066a-apll";
+ reg = <0x20000000 0x10>,
+ <0x20000040 0x04>;
+ clocks = <&xin24m>;
+ #clock-cells = <0>;
+ };
+
+ dpll: dpll@20000000 {
+ compatible = "rockchip,rk3066a-dpll";
+ reg = <0x20000010 0x10>,
+ <0x20000040 0x04>;
+ clocks = <&xin24m>;
+ #clock-cells = <0>;
+ };
+
+ cpll: cpll@20000000 {
+ compatible = "rockchip,rk3066a-cpll";
+ reg = <0x20000020 0x10>,
+ <0x20000040 0x04>;
+ clocks = <&xin24m>;
+ #clock-cells = <0>;
+ };
+
+ gpll: gpll@20000000 {
+ compatible = "rockchip,rk3066a-gpll";
+ reg = <0x20000030 0x10>,
+ <0x20000040 0x04>;
+ clocks = <&xin24m>;
+ #clock-cells = <0>;
+ };
+
+ mux_aclk_periph: mux-aclk-periph@2000006c {
+ compatible = "rockchip,cortex-a9-gpll-cpll-bit15-mux";
+ reg = <0x2000006c 0x04>;
+ clocks = <&gpll>, <&cpll>;
+ #clock-cells = <0>;
+ };
+
+ mux_uart_pll: mux-uart_pll@20000074 {
+ compatible = "rockchip,cortex-a9-gpll-cpll-bit15-mux";
+ reg = <0x20000074 0x04>;
+ clocks = <&gpll>, <&cpll>;
+ #clock-cells = <0>;
+ };
+
+ div_uart0: div-uart0@20000078 {
+ compatible = "rockchip,cortex-a9-uart-divider";
+ reg = <0x20000078 0x04>;
+ clocks = <&mux_uart_pll>;
+ #clock-cells = <0>;
+ };
+
+ mux_uart0: mux-uart0@20000078 {
+ compatible = "rockchip,cortex-a9-uart-mux";
+ reg = <0x20000078 0x04>;
+ clocks = <&clk_gates1 8>, <&dummy>, <&xin24m>; /* dummy is uart0_frac_div */
+ #clock-cells = <0>;
+ };
+
+ div_uart1: div-uart1@2000007c {
+ compatible = "rockchip,cortex-a9-uart-divider";
+ reg = <0x2000007c 0x04>;
+ clocks = <&mux_uart_pll>;
+ #clock-cells = <0>;
+ };
+
+ mux_uart1: mux-uart1@2000007c {
+ compatible = "rockchip,cortex-a9-uart-mux";
+ reg = <0x2000007c 0x04>;
+ clocks = <&clk_gates1 10>, <&dummy>, <&xin24m>;
+ #clock-cells = <0>;
+ };
+
+ div_uart2: div-uart2@20000080 {
+ compatible = "rockchip,cortex-a9-uart-divider";
+ reg = <0x20000080 0x04>;
+ clocks = <&mux_uart_pll>;
+ #clock-cells = <0>;
+ };
+
+ mux_uart2: mux-uart2@20000080 {
+ compatible = "rockchip,cortex-a9-uart-mux";
+ reg = <0x20000080 0x04>;
+ clocks = <&clk_gates1 12>, <&dummy>, <&xin24m>;
+ #clock-cells = <0>;
+ };
+
+ div_uart3: div-uart3@20000084 {
+ compatible = "rockchip,cortex-a9-uart-divider";
+ reg = <0x20000084 0x04>;
+ clocks = <&mux_uart_pll>;
+ #clock-cells = <0>;
+ };
+
+ mux_uart3: mux-uart3@20000084 {
+ compatible = "rockchip,cortex-a9-uart-mux";
+ reg = <0x20000084 0x04>;
+ clocks = <&clk_gates1 14>, <&dummy>, <&xin24m>;
+ #clock-cells = <0>;
+ };
+
+ mux_cpu: mux-cpu@20000044 {
+ compatible = "rockchip,cortex-a9-cpu-mux";
+ reg = <0x20000044 0x4>;
+ clocks = <&apll>, <&dummy> /* cpu_gpll_path */;
+ #clock-cells = <0>;
+ };
+
+ div_cpu: div-cpu@20000044 {
+ compatible = "rockchip,cortex-a9-cpu-divider";
+ reg = <0x20000044 0x4>;
+ clocks = <&mux_cpu>;
+ #clock-cells = <0>;
+ };
+
+ div_aclk_cpu: div-aclk-cpu@20000048 {
+ compatible = "rockchip,cortex-a9-aclk-cpu-divider";
+ reg = <0x20000048 0x4>;
+ clocks = <&div_cpu>;
+ #clock-cells = <0>;
+ };
+
+ div_aclk_periph: div-aclk-periph@2000006c {
+ compatible = "rockchip,cortex-a9-hclk-divider";
+ reg = <0x2000006c 0x4>;
+ clocks = <&mux_aclk_periph>;
+ #clock-cells = <0>;
+ };
+
+ div_hclk_periph: div-hclk-periph@2000006c {
+ compatible = "rockchip,cortex-a9-hclk-divider";
+ reg = <0x2000006c 0x4>;
+ clocks = <&clk_gates2 1>;
+ #clock-cells = <0>;
+ };
+
+ div_pclk_periph: div-pclk-periph@2000006c {
+ compatible = "rockchip,cortex-a9-pclk-divider";
+ reg = <0x2000006c 0x4>;
+ clocks = <&clk_gates2 1>;
+ #clock-cells = <0>;
+ };
+
+ div_hclk_cpu: div-hclk-cpu@20000048 {
+ compatible = "rockchip,cortex-a9-hclk-divider";
+ reg = <0x20000048 0x4>;
+ clocks = <&clk_gates0 3>;
+ #clock-cells = <0>;
+ };
+
+ div_pclk_cpu: div-pclk-cpu@20000048 {
+ compatible = "rockchip,cortex-a9-pclk-divider";
+ reg = <0x20000048 0x4>;
+ clocks = <&clk_gates0 3>;
+ #clock-cells = <0>;
+ };
+
+ div_mmc0: div-mmc0@20000070 {
+ compatible = "rockchip,cortex-a9-mmc-divider";
+ reg = <0x20000070 0x4>;
+ clocks = <&clk_gates2 2>;
+ #clock-cells = <0>;
+ };
+
+ div_mmc1: div-mmc1@20000074 {
+ compatible = "rockchip,cortex-a9-mmc-divider";
+ reg = <0x20000074 0x4>;
+ clocks = <&clk_gates2 2>;
+ #clock-cells = <0>;
+ };
+
+ clk_gates0: gate-clk@200000d0 {
+ compatible = "rockchip,cortex-a9-gates-clk";
+ reg = <0x200000d0 0x4>;
+ clocks = <&dummy>, <&dummy>,
+ <&dummy>, <&div_aclk_cpu>,
+ <&div_hclk_cpu>, <&div_pclk_cpu>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>;
+
+ clock-output-names =
+ "gate_core_periph", "gate_cpu_gpll",
+ "gate_ddrphy", "gate_aclk_cpu",
+ "gate_hclk_cpu", "gate_pclk_cpu",
+ "gate_atclk_cpu", "gate_i2s0",
+ "gate_i2s0_frac", "gate_i2s1",
+ "gate_i2s1_frac", "gate_i2s2",
+ "gate_i2s2_frac", "gate_spdif",
+ "gate_spdif_frac", "gate_testclk";
+
+ #clock-cells = <1>;
+ };
+
+ clk_gates1: gate-clk@200000d4 {
+ compatible = "rockchip,cortex-a9-gates-clk";
+ reg = <0x200000d4 0x4>;
+ clocks = <&xin24m>, <&xin24m>,
+ <&xin24m>, <&dummy>,
+ <&dummy>, <&xin24m>,
+ <&xin24m>, <&dummy>,
+ <&div_uart0>, <&dummy>,
+ <&div_uart1>, <&dummy>,
+ <&div_uart2>, <&dummy>,
+ <&div_uart3>, <&dummy>;
+
+ clock-output-names =
+ "gate_timer0", "gate_timer1",
+ "gate_timer2", "gate_jtag",
+ "gate_aclk_lcdc1_src", "gate_otgphy0",
+ "gate_otgphy1", "gate_ddr_gpll",
+ "gate_uart0", "gate_frac_uart0",
+ "gate_uart1", "gate_frac_uart1",
+ "gate_uart2", "gate_frac_uart2",
+ "gate_uart3", "gate_frac_uart3";
+
+ #clock-cells = <1>;
+ };
+
+ clk_gates2: gate-clk@200000d8 {
+ compatible = "rockchip,cortex-a9-gates-clk";
+ reg = <0x200000d8 0x4>;
+ clocks = <&clk_gates2 1>, <&div_aclk_periph>,
+ <&div_hclk_periph>, <&div_pclk_periph>,
+ <&dummy>, <&dummy>,
+ <&clk_gates2 3>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&div_mmc0>,
+ <&dummy>, <&div_mmc1>,
+ <&dummy>, <&dummy>;
+
+ clock-output-names =
+ "gate_periph_src", "gate_aclk_periph",
+ "gate_hclk_periph", "gate_pclk_periph",
+ "gate_smc", "gate_mac",
+ "gate_hsadc", "gate_hsadc_frac",
+ "gate_saradc", "gate_spi0",
+ "gate_spi1", "gate_mmc0",
+ "gate_mac_lbtest", "gate_mmc1",
+ "gate_emmc", "gate_tsadc";
+
+ #clock-cells = <1>;
+ };
+
+ clk_gates3: gate-clk@200000dc {
+ compatible = "rockchip,cortex-a9-gates-clk";
+ reg = <0x200000dc 0x4>;
+ clocks = <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>;
+
+ clock-output-names =
+ "gate_aclk_lcdc0_src", "gate_dclk_lcdc0",
+ "gate_dclk_lcdc1", "gate_pclkin_cif0",
+ "gate_pclkin_cif1", "reserved",
+ "reserved", "gate_cif0_out",
+ "gate_cif1_out", "gate_aclk_vepu",
+ "gate_hclk_vepu", "gate_aclk_vdpu",
+ "gate_hclk_vdpu", "gate_gpu_src",
+ "reserved", "gate_xin27m";
+
+ #clock-cells = <1>;
+ };
+
+ clk_gates4: gate-clk@200000e0 {
+ compatible = "rockchip,cortex-a9-gates-clk";
+ reg = <0x200000e0 0x4>;
+ clocks = <&clk_gates2 2>, <&clk_gates2 3>,
+ <&clk_gates2 1>, <&clk_gates2 1>,
+ <&clk_gates2 1>, <&clk_gates2 2>,
+ <&clk_gates2 2>, <&clk_gates2 2>,
+ <&clk_gates0 4>, <&clk_gates0 4>,
+ <&clk_gates0 3>, <&clk_gates0 3>,
+ <&clk_gates0 3>, <&clk_gates2 3>,
+ <&clk_gates0 4>;
+
+ clock-output-names =
+ "gate_hclk_peri_axi_matrix", "gate_pclk_peri_axi_matrix",
+ "gate_aclk_cpu_peri", "gate_aclk_peri_axi_matrix",
+ "gate_aclk_pei_niu", "gate_hclk_usb_peri",
+ "gate_hclk_peri_ahb_arbi", "gate_hclk_emem_peri",
+ "gate_hclk_cpubus", "gate_hclk_ahb2apb",
+ "gate_aclk_strc_sys", "gate_aclk_l2mem_con",
+ "gate_aclk_intmem", "gate_pclk_tsadc",
+ "gate_hclk_hdmi";
+
+ #clock-cells = <1>;
+ };
+
+ clk_gates5: gate-clk@200000e4 {
+ compatible = "rockchip,cortex-a9-gates-clk";
+ reg = <0x200000e4 0x4>;
+ clocks = <&clk_gates0 3>, <&clk_gates2 1>,
+ <&clk_gates0 5>, <&clk_gates0 5>,
+ <&clk_gates0 5>, <&clk_gates0 5>,
+ <&clk_gates0 4>, <&clk_gates0 5>,
+ <&clk_gates2 1>, <&clk_gates2 2>,
+ <&clk_gates2 2>, <&clk_gates2 2>,
+ <&clk_gates2 2>, <&clk_gates4 5>,
+ <&clk_gates4 5>, <&dummy>;
+
+ clock-output-names =
+ "gate_aclk_dmac1", "gate_aclk_dmac2",
+ "gate_pclk_efuse", "gate_pclk_tzpc",
+ "gate_pclk_grf", "gate_pclk_pmu",
+ "gate_hclk_rom", "gate_pclk_ddrupctl",
+ "gate_aclk_smc", "gate_hclk_nandc",
+ "gate_hclk_mmc0", "gate_hclk_mmc1",
+ "gate_hclk_emmc", "gate_hclk_otg0",
+ "gate_hclk_otg1", "gate_aclk_gpu";
+
+ #clock-cells = <1>;
+ };
+
+ clk_gates6: gate-clk@200000e8 {
+ compatible = "rockchip,cortex-a9-gates-clk";
+ reg = <0x200000e8 0x4>;
+ clocks = <&clk_gates3 0>, <&clk_gates0 4>,
+ <&clk_gates0 4>, <&clk_gates1 4>,
+ <&clk_gates0 4>, <&clk_gates3 0>,
+ <&clk_gates0 4>, <&clk_gates1 4>,
+ <&clk_gates3 0>, <&clk_gates0 4>,
+ <&clk_gates0 4>, <&clk_gates1 4>,
+ <&clk_gates0 4>, <&clk_gates3 0>,
+ <&dummy>, <&dummy>;
+
+ clock-output-names =
+ "gate_aclk_lcdc0", "gate_hclk_lcdc0",
+ "gate_hclk_lcdc1", "gate_aclk_lcdc1",
+ "gate_hclk_cif0", "gate_aclk_cif0",
+ "gate_hclk_cif1", "gate_aclk_cif1",
+ "gate_aclk_ipp", "gate_hclk_ipp",
+ "gate_hclk_rga", "gate_aclk_rga",
+ "gate_hclk_vio_bus", "gate_aclk_vio0",
+ "gate_aclk_vcodec", "gate_shclk_vio_h2h";
+
+ #clock-cells = <1>;
+ };
+
+ clk_gates7: gate-clk@200000ec {
+ compatible = "rockchip,cortex-a9-gates-clk";
+ reg = <0x200000ec 0x4>;
+ clocks = <&clk_gates2 2>, <&clk_gates0 4>,
+ <&clk_gates0 4>, <&clk_gates0 4>,
+ <&clk_gates0 4>, <&clk_gates2 2>,
+ <&clk_gates2 2>, <&clk_gates0 5>,
+ <&clk_gates0 5>, <&clk_gates0 5>,
+ <&clk_gates0 5>, <&clk_gates2 3>,
+ <&clk_gates2 3>, <&clk_gates2 3>,
+ <&clk_gates2 3>, <&clk_gates2 3>;
+
+ clock-output-names =
+ "gate_hclk_emac", "gate_hclk_spdif",
+ "gate_hclk_i2s0_2ch", "gate_hclk_i2s1_2ch",
+ "gate_hclk_i2s_8ch", "gate_hclk_hsadc",
+ "gate_hclk_pidf", "gate_pclk_timer0",
+ "gate_pclk_timer1", "gate_pclk_timer2",
+ "gate_pclk_pwm01", "gate_pclk_pwm23",
+ "gate_pclk_spi0", "gate_pclk_spi1",
+ "gate_pclk_saradc", "gate_pclk_wdt";
+
+ #clock-cells = <1>;
+ };
+
+ clk_gates8: gate-clk@200000f0 {
+ compatible = "rockchip,cortex-a9-gates-clk";
+ reg = <0x200000f0 0x4>;
+ clocks = <&clk_gates0 5>, <&clk_gates0 5>,
+ <&clk_gates2 3>, <&clk_gates2 3>,
+ <&clk_gates0 5>, <&clk_gates0 5>,
+ <&clk_gates2 3>, <&clk_gates2 3>,
+ <&clk_gates2 3>, <&clk_gates0 5>,
+ <&clk_gates0 5>, <&clk_gates0 5>,
+ <&clk_gates2 3>, <&clk_gates2 3>,
+ <&dummy>, <&clk_gates0 5>;
+
+ clock-output-names =
+ "gate_pclk_uart0", "gate_pclk_uart1",
+ "gate_pclk_uart2", "gate_pclk_uart3",
+ "gate_pclk_i2c0", "gate_pclk_i2c1",
+ "gate_pclk_i2c2", "gate_pclk_i2c3",
+ "gate_pclk_i2c4", "gate_pclk_gpio0",
+ "gate_pclk_gpio1", "gate_pclk_gpio2",
+ "gate_pclk_gpio3", "gate_pclk_gpio4",
+ "reserved", "gate_pclk_gpio6";
+
+ #clock-cells = <1>;
+ };
+
+ clk_gates9: gate-clk@200000f4 {
+ compatible = "rockchip,cortex-a9-gates-clk";
+ reg = <0x200000f4 0x4>;
+ clocks = <&dummy>, <&clk_gates0 5>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&clk_gates1 4>,
+ <&clk_gates0 5>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>;
+
+ clock-output-names =
+ "gate_clk_core_dbg", "gate_pclk_dbg",
+ "gate_clk_trace", "gate_atclk",
+ "gate_clk_l2c", "gate_aclk_vio1",
+ "gate_pclk_publ", "gate_aclk_intmem0",
+ "gate_aclk_intmem1", "gate_aclk_intmem2",
+ "gate_aclk_intmem3";
+
+ #clock-cells = <1>;
+ };
+ };
+
+};
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 3a26115..4063afc 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -24,6 +24,7 @@ ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_MMP) += mmp/
endif
obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
+obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_ARCH_U8500) += ux500/
obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
new file mode 100644
index 0000000..e0fbafb
--- /dev/null
+++ b/drivers/clk/rockchip/Makefile
@@ -0,0 +1,6 @@
+#
+# Rockchip Clock specific Makefile
+#
+
+obj-y += clk-rockchip.o
+obj-y += clk-rockchip-pll.o
diff --git a/drivers/clk/rockchip/clk-rockchip-pll.c b/drivers/clk/rockchip/clk-rockchip-pll.c
new file mode 100644
index 0000000..4456445
--- /dev/null
+++ b/drivers/clk/rockchip/clk-rockchip-pll.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/div64.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clk-private.h>
+
+#define RK3X_PLL_MODE_MASK 0x3
+#define RK3X_PLL_MODE_SLOW 0x0
+#define RK3X_PLL_MODE_NORM 0x1
+#define RK3X_PLL_MODE_DEEP 0x2
+
+#define RK3X_PLLCON0_OD_MASK 0xf
+#define RK3X_PLLCON0_OD_SHIFT 0
+#define RK3X_PLLCON0_NR_MASK 0x3f
+#define RK3X_PLLCON0_NR_SHIFT 8
+
+#define RK3X_PLLCON1_NF_MASK 0x1fff
+#define RK3X_PLLCON1_NF_SHIFT 0
+
+#define RK3X_PLLCON3_REST (1 << 5)
+#define RK3X_PLLCON3_BYPASS (1 << 0)
+
+struct rockchip_clk_pll {
+ struct clk_hw hw;
+ void __iomem *reg_base;
+ void __iomem *reg_mode;
+ unsigned int shift_mode;
+ spinlock_t *lock;
+};
+
+#define to_clk_pll(_hw) container_of(_hw, struct rockchip_clk_pll, hw)
+
+static unsigned long rk3x_generic_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rockchip_clk_pll *pll = to_clk_pll(hw);
+ u32 pll_con0 = readl_relaxed(pll->reg_base);
+ u32 pll_con1 = readl_relaxed(pll->reg_base + 0x4);
+ u32 pll_con3 = readl_relaxed(pll->reg_base + 0xc);
+ u32 mode_con = readl_relaxed(pll->reg_mode) >> pll->shift_mode;
+ u64 pll_nf;
+ u64 pll_nr;
+ u64 pll_no;
+ u64 rate64;
+
+ if (pll_con3 & RK3X_PLLCON3_BYPASS) {
+ pr_debug("%s: pll %s is bypassed\n", __func__,
+ __clk_get_name(hw->clk));
+ return parent_rate;
+ }
+
+ mode_con &= RK3X_PLL_MODE_MASK;
+ if (mode_con != RK3X_PLL_MODE_NORM) {
+ pr_debug("%s: pll %s not in normal mode: %d\n", __func__,
+ __clk_get_name(hw->clk), mode_con);
+ return parent_rate;
+ }
+
+ pll_nf = (pll_con1 >> RK3X_PLLCON1_NF_SHIFT);
+ pll_nf &= RK3X_PLLCON1_NF_MASK;
+ pll_nf++;
+ rate64 = (u64)parent_rate * pll_nf;
+
+ pll_nr = (pll_con0 >> RK3X_PLLCON0_NR_SHIFT);
+ pll_nr &= RK3X_PLLCON0_NR_MASK;
+ pll_nr++;
+ do_div(rate64, pll_nr);
+
+ pll_no = (pll_con0 >> RK3X_PLLCON0_OD_SHIFT);
+ pll_no &= RK3X_PLLCON0_OD_MASK;
+ pll_no++;
+ do_div(rate64, pll_no);
+
+ return (unsigned long)rate64;
+}
+
+static const struct clk_ops rk3x_generic_pll_clk_ops = {
+ .recalc_rate = rk3x_generic_pll_recalc_rate,
+};
+
+struct clk * __init rockchip_clk_register_rk3x_pll(const char *name,
+ const char *pname, void __iomem *reg_base,
+ void __iomem *reg_mode, unsigned int shift_mode,
+ spinlock_t *lock)
+{
+ struct rockchip_clk_pll *pll;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll) {
+ pr_err("%s: could not allocate pll clk %s\n", __func__, name);
+ return NULL;
+ }
+
+ init.name = name;
+ init.ops = &rk3x_generic_pll_clk_ops;
+ init.flags = CLK_GET_RATE_NOCACHE;
+ init.parent_names = &pname;
+ init.num_parents = 1;
+
+ pll->hw.init = &init;
+ pll->reg_base = reg_base;
+ pll->reg_mode = reg_mode;
+ pll->shift_mode = shift_mode;
+ pll->lock = lock;
+
+ clk = clk_register(NULL, &pll->hw);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register pll clock %s\n", __func__,
+ name);
+ kfree(pll);
+ }
+
+ return clk;
+}
diff --git a/drivers/clk/rockchip/clk-rockchip-pll.h b/drivers/clk/rockchip/clk-rockchip-pll.h
new file mode 100644
index 0000000..a63288a
--- /dev/null
+++ b/drivers/clk/rockchip/clk-rockchip-pll.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+extern struct clk * __init rockchip_clk_register_rk3x_pll(const char *name,
+ const char *pname, const void __iomem *reg_base,
+ const void __iomem *reg_mode, unsigned int shift_mode,
+ spinlock_t *lock);
diff --git a/drivers/clk/rockchip/clk-rockchip.c b/drivers/clk/rockchip/clk-rockchip.c
new file mode 100644
index 0000000..3d7e0b2
--- /dev/null
+++ b/drivers/clk/rockchip/clk-rockchip.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/rockchip.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include "clk-rockchip-pll.h"
+
+static DEFINE_SPINLOCK(clk_lock);
+
+struct rockchip_pll_data {
+ int mode_shift;
+};
+
+struct rockchip_pll_data rk3066a_apll_data = {
+ .mode_shift = 0,
+};
+
+struct rockchip_pll_data rk3066a_dpll_data = {
+ .mode_shift = 4,
+};
+
+struct rockchip_pll_data rk3066a_cpll_data = {
+ .mode_shift = 8,
+};
+
+struct rockchip_pll_data rk3066a_gpll_data = {
+ .mode_shift = 12,
+};
+
+/* Matches for plls */
+static const __initconst struct of_device_id clk_pll_match[] = {
+ { .compatible = "rockchip,rk3066a-apll", .data = &rk3066a_apll_data },
+ { .compatible = "rockchip,rk3066a-dpll", .data = &rk3066a_dpll_data },
+ { .compatible = "rockchip,rk3066a-cpll", .data = &rk3066a_cpll_data },
+ { .compatible = "rockchip,rk3066a-gpll", .data = &rk3066a_gpll_data },
+ {}
+};
+
+static void __init rockchip_pll_setup(struct device_node *node,
+ struct rockchip_pll_data *data)
+{
+ struct clk *clk;
+ const char *clk_name = node->name;
+ const char *clk_parent;
+ void __iomem *reg_base;
+ void __iomem *reg_mode;
+ u32 rate;
+
+ reg_base = of_iomap(node, 0);
+ reg_mode = of_iomap(node, 1);
+
+ clk_parent = of_clk_get_parent_name(node, 0);
+
+ pr_debug("%s: adding %s as child of %s\n",
+ __func__, clk_name, clk_parent);
+
+ clk = rockchip_clk_register_rk3x_pll(clk_name, clk_parent, reg_base,
+ reg_mode, data->mode_shift,
+ &clk_lock);
+ if (clk) {
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+
+ /* optionally set a target frequency for the pll */
+ if (!of_property_read_u32(node, "clock-frequency", &rate))
+ clk_set_rate(clk, rate);
+ }
+}
+
+/*
+ * Mux clocks
+ */
+
+struct rockchip_mux_data {
+ int shift;
+ int width;
+};
+
+#define RK_MUX(n, s, w) \
+static const __initconst struct rockchip_mux_data n = { \
+ .shift = s, \
+ .width = w, \
+}
+
+RK_MUX(gpll_cpll_15_mux_data, 15, 1);
+RK_MUX(uart_mux_data, 8, 2);
+RK_MUX(cpu_mux_data, 8, 1);
+
+static const __initconst struct of_device_id clk_mux_match[] = {
+ { .compatible = "rockchip,cortex-a9-gpll-cpll-bit15-mux",
+ .data = &gpll_cpll_15_mux_data },
+ { .compatible = "rockchip,cortex-a9-uart-mux",
+ .data = &uart_mux_data },
+ { .compatible = "rockchip,cortex-a9-cpu-mux",
+ .data = &cpu_mux_data },
+ {}
+};
+
+static void __init rockchip_mux_clk_setup(struct device_node *node,
+ struct rockchip_mux_data *data)
+{
+ struct clk *clk;
+ const char *clk_name = node->name;
+ void __iomem *reg;
+ int max_parents = (1 << data->width);
+ const char *parents[max_parents];
+ int flags;
+ int i = 0;
+
+ reg = of_iomap(node, 0);
+
+ while (i < max_parents &&
+ (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
+ i++;
+
+ flags = CLK_MUX_MASK_UPPER_HALF;
+
+ clk = clk_register_mux(NULL, clk_name, parents, i, 0,
+ reg, data->shift, data->width,
+ flags, &clk_lock);
+ if (clk)
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+
+/*
+ * Divider clocks
+ */
+
+struct rockchip_div_data {
+ int shift;
+ int width;
+ int max;
+ int flags;
+};
+
+#define RK_DIV(n, s, w, f) \
+static const __initconst struct rockchip_div_data n = { \
+ .shift = s, \
+ .width = w, \
+ .flags = f, \
+}
+
+RK_DIV(cpu_div_data, 0, 5, 0);
+RK_DIV(aclk_periph_div_data, 0, 5, 0);
+RK_DIV(aclk_cpu_div_data, 0, 3, 0);
+RK_DIV(hclk_div_data, 8, 2, CLK_DIVIDER_POWER_OF_TWO);
+RK_DIV(pclk_div_data, 12, 2, CLK_DIVIDER_POWER_OF_TWO);
+RK_DIV(mmc_div_data, 0, 6, CLK_DIVIDER_EVEN);
+RK_DIV(uart_div_data, 0, 7, 0);
+
+static const __initconst struct of_device_id clk_divider_match[] = {
+ { .compatible = "rockchip,cortex-a9-cpu-divider",
+ .data = &cpu_div_data },
+ { .compatible = "rockchip,cortex-a9-aclk-periph-divider",
+ .data = &aclk_periph_div_data },
+ { .compatible = "rockchip,cortex-a9-aclk-cpu-divider",
+ .data = &aclk_cpu_div_data },
+ { .compatible = "rockchip,cortex-a9-hclk-divider",
+ .data = &hclk_div_data },
+ { .compatible = "rockchip,cortex-a9-pclk-divider",
+ .data = &pclk_div_data },
+ { .compatible = "rockchip,cortex-a9-mmc-divider",
+ .data = &mmc_div_data },
+ { .compatible = "rockchip,cortex-a9-uart-divider",
+ .data = &uart_div_data },
+ {}
+};
+
+static void __init rockchip_divider_clk_setup(struct device_node *node,
+ struct rockchip_div_data *data)
+{
+ struct clk *clk;
+ const char *clk_name = node->name;
+ const char *clk_parent;
+ void __iomem *reg;
+ int flags;
+
+ reg = of_iomap(node, 0);
+
+ clk_parent = of_clk_get_parent_name(node, 0);
+
+ flags = data->flags;
+ flags |= CLK_DIVIDER_MASK_UPPER_HALF;
+
+ clk = clk_register_divider(NULL, clk_name, clk_parent, 0,
+ reg, data->shift, data->width,
+ flags, &clk_lock);
+ if (clk)
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+
+/*
+ * Gate clocks
+ */
+
+static void __init rockchip_gate_clk_setup(struct device_node *node,
+ void *data)
+{
+ struct clk_onecell_data *clk_data;
+ const char *clk_parent;
+ const char *clk_name;
+ void __iomem *reg;
+ void __iomem *reg_idx;
+ int flags;
+ int qty;
+ int reg_bit;
+ int clkflags = CLK_SET_RATE_PARENT;
+ int i;
+
+ qty = of_property_count_strings(node, "clock-output-names");
+ if (qty < 0) {
+ pr_err("%s: error in clock-output-names %d\n", __func__, qty);
+ return;
+ }
+
+ if (qty == 0) {
+ pr_info("%s: nothing to do\n", __func__);
+ return;
+ }
+
+ reg = of_iomap(node, 0);
+
+ clk_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
+ if (!clk_data)
+ return;
+
+ clk_data->clks = kzalloc(qty * sizeof(struct clk *), GFP_KERNEL);
+ if (!clk_data->clks) {
+ kfree(clk_data);
+ return;
+ }
+
+ flags = CLK_GATE_MASK_UPPER_HALF | CLK_GATE_SET_TO_DISABLE;
+
+ for (i = 0; i < qty; i++) {
+ of_property_read_string_index(node, "clock-output-names",
+ i, &clk_name);
+
+ /* ignore empty slots */
+ if (!strcmp("reserved", clk_name))
+ continue;
+
+ clk_parent = of_clk_get_parent_name(node, i);
+
+ /* keep all gates untouched for now */
+ clkflags |= CLK_IGNORE_UNUSED;
+
+ reg_idx = reg + (4 * (i / 16));
+ reg_bit = (i % 16);
+
+ clk_data->clks[i] = clk_register_gate(NULL, clk_name,
+ clk_parent, clkflags,
+ reg_idx, reg_bit,
+ flags,
+ &clk_lock);
+ WARN_ON(IS_ERR(clk_data->clks[i]));
+ }
+
+ clk_data->clk_num = qty;
+
+ of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+}
+
+static const __initconst struct of_device_id clk_gate_match[] = {
+ { .compatible = "rockchip,cortex-a9-gates-clk" },
+ {}
+};
+
+void __init of_rockchip_clk_table_clock_setup(
+ const struct of_device_id *clk_match,
+ void *function)
+{
+ struct device_node *np;
+ const struct div_data *data;
+ const struct of_device_id *match;
+ void (*setup_function)(struct device_node *, const void *) = function;
+
+ for_each_matching_node(np, clk_match) {
+ match = of_match_node(clk_match, np);
+ data = match->data;
+ setup_function(np, data);
+ }
+}
+
+void __init rockchip_init_clocks(void)
+{
+ of_clk_init(NULL);
+
+ of_rockchip_clk_table_clock_setup(clk_pll_match,
+ rockchip_pll_setup);
+
+ of_rockchip_clk_table_clock_setup(clk_mux_match,
+ rockchip_mux_clk_setup);
+
+ of_rockchip_clk_table_clock_setup(clk_gate_match,
+ rockchip_gate_clk_setup);
+
+ of_rockchip_clk_table_clock_setup(clk_divider_match,
+ rockchip_divider_clk_setup);
+}
diff --git a/include/linux/clk/rockchip.h b/include/linux/clk/rockchip.h
new file mode 100644
index 0000000..571721f
--- /dev/null
+++ b/include/linux/clk/rockchip.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LINUX_CLK_ROCKCHIP_H_
+#define __LINUX_CLK_ROCKCHIP_H_
+
+void __init rockchip_init_clocks(void);
+
+#endif
--
1.7.2.3

2013-06-02 23:01:19

by Heiko Stübner

[permalink] [raw]
Subject: [PATCH 09/10] arm: add debug uarts for rockchip rk29xx and rk3xxx series

Uarts on all recent Rockchip SoCs are Synopsis DesignWare 8250 types.
Only their addresses vary very much.

This patch adds the necessary definitions to use any of the uart ports
for early debug purposes.

Signed-off-by: Heiko Stuebner <[email protected]>
---
arch/arm/Kconfig.debug | 34 +++++++++++++++++++++++++++++
arch/arm/include/debug/rockchip.S | 42 +++++++++++++++++++++++++++++++++++++
2 files changed, 76 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/include/debug/rockchip.S

diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 29f7623..2060cd5 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -360,6 +360,13 @@ choice
their output to the standard serial port on the RealView
PB1176 platform.

+ config DEBUG_ROCKCHIP_UART
+ bool "Kernel low-level debugging messages via Rockchip UART"
+ depends on ARCH_ROCKCHIP
+ help
+ Say Y here if you want kernel low-level debugging support
+ on Rockchip based platforms.
+
config DEBUG_S3C_UART0
depends on PLAT_SAMSUNG
select DEBUG_EXYNOS_UART if ARCH_EXYNOS
@@ -597,6 +604,32 @@ endchoice

choice
prompt "Low-level debug console UART"
+ depends on DEBUG_ROCKCHIP_UART
+
+ config DEBUG_RK29_UART0
+ bool "RK29 UART0"
+
+ config DEBUG_RK29_UART1
+ bool "RK29 UART1"
+
+ config DEBUG_RK29_UART2
+ bool "RK29 UART2"
+
+ config DEBUG_RK3X_UART0
+ bool "RK3X UART0"
+
+ config DEBUG_RK3X_UART1
+ bool "RK3X UART1"
+
+ config DEBUG_RK3X_UART2
+ bool "RK3X UART2"
+
+ config DEBUG_RK3X_UART3
+ bool "RK3X UART3"
+endchoice
+
+choice
+ prompt "Low-level debug console UART"
depends on DEBUG_LL && DEBUG_TEGRA_UART

config TEGRA_DEBUG_UART_AUTO_ODMDATA
@@ -648,6 +681,7 @@ config DEBUG_LL_INCLUDE
default "debug/picoxcell.S" if DEBUG_PICOXCELL_UART
default "debug/pxa.S" if DEBUG_PXA_UART1 || DEBUG_MMP_UART2 || \
DEBUG_MMP_UART3
+ default "debug/rockchip.S" if DEBUG_ROCKCHIP_UART
default "debug/sirf.S" if DEBUG_SIRFPRIMA2_UART1 || DEBUG_SIRFMARCO_UART1
default "debug/socfpga.S" if DEBUG_SOCFPGA_UART
default "debug/sunxi.S" if DEBUG_SUNXI_UART0 || DEBUG_SUNXI_UART1
diff --git a/arch/arm/include/debug/rockchip.S b/arch/arm/include/debug/rockchip.S
new file mode 100644
index 0000000..2b914dc
--- /dev/null
+++ b/arch/arm/include/debug/rockchip.S
@@ -0,0 +1,42 @@
+/*
+ * Early serial output macro for Rockchip SoCs
+ *
+ * Copyright (C) 2012 Maxime Ripard
+ *
+ * Maxime Ripard <[email protected]>
+ *
+ * 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.
+*/
+
+#if defined(CONFIG_DEBUG_RK29_UART0)
+#define ROCKCHIP_UART_DEBUG_PHYS_BASE 0x20060000
+#define ROCKCHIP_UART_DEBUG_VIRT_BASE 0xfe060000
+#elif defined(CONFIG_DEBUG_RK29_UART1)
+#define ROCKCHIP_UART_DEBUG_PHYS_BASE 0x20064000
+#define ROCKCHIP_UART_DEBUG_VIRT_BASE 0xfed64000
+#elif defined(CONFIG_DEBUG_RK29_UART2)
+#define ROCKCHIP_UART_DEBUG_PHYS_BASE 0x20068000
+#define ROCKCHIP_UART_DEBUG_VIRT_BASE 0xfed68000
+#elif defined(CONFIG_DEBUG_RK3X_UART0)
+#define ROCKCHIP_UART_DEBUG_PHYS_BASE 0x10124000
+#define ROCKCHIP_UART_DEBUG_VIRT_BASE 0xfeb24000
+#elif defined(CONFIG_DEBUG_RK3X_UART1)
+#define ROCKCHIP_UART_DEBUG_PHYS_BASE 0x10126000
+#define ROCKCHIP_UART_DEBUG_VIRT_BASE 0xfeb26000
+#elif defined(CONFIG_DEBUG_RK3X_UART2)
+#define ROCKCHIP_UART_DEBUG_PHYS_BASE 0x20064000
+#define ROCKCHIP_UART_DEBUG_VIRT_BASE 0xfed64000
+#elif defined(CONFIG_DEBUG_RK3X_UART3)
+#define ROCKCHIP_UART_DEBUG_PHYS_BASE 0x20068000
+#define ROCKCHIP_UART_DEBUG_VIRT_BASE 0xfed68000
+#endif
+
+ .macro addruart, rp, rv, tmp
+ ldr \rp, =ROCKCHIP_UART_DEBUG_PHYS_BASE
+ ldr \rv, =ROCKCHIP_UART_DEBUG_VIRT_BASE
+ .endm
+
+#define UART_SHIFT 2
+#include <asm/hardware/debug-8250.S>
--
1.7.2.3

2013-06-02 23:02:39

by Heiko Stübner

[permalink] [raw]
Subject: [PATCH 10/10] arm: add basic support for Rockchip RK3066a boards

This adds a generic devicetree board file and a dtsi for boards
based on the RK3066a SoCs from Rockchip.

Apart from the generic parts (gic, clocks, pinctrl) the only components
currently supported are the timers, uarts and mmc ports (all DesignWare-
based).

Signed-off-by: Heiko Stuebner <[email protected]>
---
arch/arm/Kconfig | 2 +
arch/arm/Makefile | 1 +
arch/arm/boot/dts/rk3066a.dtsi | 359 ++++++++++++++++++++++++++++++++++
arch/arm/mach-rockchip/Kconfig | 17 ++
arch/arm/mach-rockchip/Makefile | 1 +
arch/arm/mach-rockchip/Makefile.boot | 3 +
arch/arm/mach-rockchip/rockchip.c | 54 +++++
7 files changed, 437 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/boot/dts/rk3066a.dtsi
create mode 100644 arch/arm/mach-rockchip/Kconfig
create mode 100644 arch/arm/mach-rockchip/Makefile
create mode 100644 arch/arm/mach-rockchip/Makefile.boot
create mode 100644 arch/arm/mach-rockchip/rockchip.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index ff580d3..ff892b1 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -983,6 +983,8 @@ source "arch/arm/mach-mmp/Kconfig"

source "arch/arm/mach-realview/Kconfig"

+source "arch/arm/mach-rockchip/Kconfig"
+
source "arch/arm/mach-sa1100/Kconfig"

source "arch/arm/plat-samsung/Kconfig"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 3380c4f..0153f60 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -172,6 +172,7 @@ machine-$(CONFIG_ARCH_PICOXCELL) += picoxcell
machine-$(CONFIG_ARCH_PRIMA2) += prima2
machine-$(CONFIG_ARCH_PXA) += pxa
machine-$(CONFIG_ARCH_REALVIEW) += realview
+machine-$(CONFIG_ARCH_ROCKCHIP) += rockchip
machine-$(CONFIG_ARCH_RPC) += rpc
machine-$(CONFIG_ARCH_S3C24XX) += s3c24xx
machine-$(CONFIG_ARCH_S3C64XX) += s3c64xx
diff --git a/arch/arm/boot/dts/rk3066a.dtsi b/arch/arm/boot/dts/rk3066a.dtsi
new file mode 100644
index 0000000..2b42d26
--- /dev/null
+++ b/arch/arm/boot/dts/rk3066a.dtsi
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/pinctrl/rockchip.h>
+#include "skeleton.dtsi"
+#include "rk3066a-clocks.dtsi"
+
+/ {
+ compatible = "rockchip,rk30xx";
+ interrupt-parent = <&gic>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ next-level-cache = <&L2>;
+ reg = <0x0>;
+ };
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ next-level-cache = <&L2>;
+ reg = <0x1>;
+ };
+ };
+
+ soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ ranges;
+
+ gic: interrupt-controller@1013d000 {
+ compatible = "arm,cortex-a9-gic";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ reg = <0x1013d000 0x1000>,
+ <0x1013c100 0x0100>;
+ };
+
+ L2: l2-cache-controller@10138000 {
+ compatible = "arm,pl310-cache";
+ reg = <0x10138000 0x1000>;
+ cache-unified;
+ cache-level = <2>;
+ };
+
+ local-timer@1013c600 {
+ compatible = "arm,cortex-a9-twd-timer";
+ reg = <0x1013c600 0x20>;
+ interrupts = <GIC_PPI 13 0x304>;
+ };
+
+ timer@20038000 {
+ compatible = "snps,dw-apb-timer-osc";
+ reg = <0x20038000 0x100>;
+ interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates1 0>, <&clk_gates7 7>;
+ clock-names = "timer", "pclk";
+ };
+
+ timer@2003a000 {
+ compatible = "snps,dw-apb-timer-osc";
+ reg = <0x2003a000 0x100>;
+ interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates1 1>, <&clk_gates7 8>;
+ clock-names = "timer", "pclk";
+ };
+
+ timer@2000e000 {
+ compatible = "snps,dw-apb-timer-osc";
+ reg = <0x2000e000 0x100>;
+ interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates1 2>, <&clk_gates7 9>;
+ clock-names = "timer", "pclk";
+ };
+
+ pinctrl@20008000 {
+ compatible = "rockchip,rk3066a-pinctrl";
+ reg = <0x20008000 0x150>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ gpio0: gpio0@20034000 {
+ compatible = "rockchip,gpio-bank";
+ reg = <0x20034000 0x100>;
+ interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates8 9>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio1: gpio1@2003c000 {
+ compatible = "rockchip,gpio-bank";
+ reg = <0x2003c000 0x100>;
+ interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates8 10>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio2: gpio2@2003e000 {
+ compatible = "rockchip,gpio-bank";
+ reg = <0x2003e000 0x100>;
+ interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates8 11>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio3: gpio3@20080000 {
+ compatible = "rockchip,gpio-bank";
+ reg = <0x20080000 0x100>;
+ interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates8 12>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio4: gpio4@20084000 {
+ compatible = "rockchip,gpio-bank";
+ reg = <0x20084000 0x100>;
+ interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates8 13>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio6: gpio6@2000a000 {
+ compatible = "rockchip,gpio-bank";
+ reg = <0x2000a000 0x100>;
+ interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates8 15>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ uart0 {
+ uart0_xfer: uart0-xfer {
+ rockchip,pins = <RK_GPIO1 0 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>,
+ <RK_GPIO1 1 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+
+ uart0_cts: uart0-cts {
+ rockchip,pins = <RK_GPIO1 2 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+
+ uart0_rts: uart0-rts {
+ rockchip,pins = <RK_GPIO1 3 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+ };
+
+ uart1 {
+ uart1_xfer: uart1-xfer {
+ rockchip,pins = <RK_GPIO1 4 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>,
+ <RK_GPIO1 5 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+
+ uart1_cts: uart1-cts {
+ rockchip,pins = <RK_GPIO1 6 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+
+ uart1_rts: uart1-rts {
+ rockchip,pins = <RK_GPIO1 7 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+ };
+
+ uart2 {
+ uart2_xfer: uart2-xfer {
+ rockchip,pins = <RK_GPIO1 8 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>,
+ <RK_GPIO1 9 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+ /* no rts / cts for uart2 */
+ };
+
+ uart3 {
+ uart3_xfer: uart3-xfer {
+ rockchip,pins = <RK_GPIO3 27 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>,
+ <RK_GPIO3 28 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+
+ uart3_cts: uart3-cts {
+ rockchip,pins = <RK_GPIO3 29 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+
+ uart3_rts: uart3-rts {
+ rockchip,pins = <RK_GPIO3 30 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+ };
+
+ sd0 {
+ sd0_clk: sd0-clk {
+ rockchip,pins = <RK_GPIO3 8 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+
+ sd0_cmd: sd0-cmd {
+ rockchip,pins = <RK_GPIO3 9 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+
+ sd0_cd: sd0-cd {
+ rockchip,pins = <RK_GPIO3 14 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+
+ sd0_wp: sd0-wp {
+ rockchip,pins = <RK_GPIO3 15 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+
+ sd0_bus1: sd0-bus-width1 {
+ rockchip,pins = <RK_GPIO3 10 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+
+ sd0_bus4: sd0-bus-width4 {
+ rockchip,pins = <RK_GPIO3 10 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>,
+ <RK_GPIO3 11 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>,
+ <RK_GPIO3 12 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>,
+ <RK_GPIO3 13 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+ };
+
+ sd1 {
+ sd1_clk: sd1-clk {
+ rockchip,pins = <RK_GPIO3 21 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+
+ sd1_cmd: sd1-cmd {
+ rockchip,pins = <RK_GPIO3 16 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+
+ sd1_cd: sd1-cd {
+ rockchip,pins = <RK_GPIO3 22 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+
+ sd1_wp: sd1-wp {
+ rockchip,pins = <RK_GPIO3 23 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+
+ sd1_bus1: sd1-bus-width1 {
+ rockchip,pins = <RK_GPIO3 17 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+
+ sd1_bus4: sd1-bus-width4 {
+ rockchip,pins = <RK_GPIO3 17 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>,
+ <RK_GPIO3 18 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>,
+ <RK_GPIO3 19 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>,
+ <RK_GPIO3 20 RK_FUNC_1 RK_PINCTRL_PULL_AUTO>;
+ };
+ };
+ };
+
+ uart0: serial@10124000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x10124000 0x400>;
+ interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <1>;
+ clocks = <&mux_uart0>;
+ status = "disabled";
+ };
+
+ uart1: serial@10126000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x10126000 0x400>;
+ interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <1>;
+ clocks = <&mux_uart1>;
+ status = "disabled";
+ };
+
+ uart2: serial@20064000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x20064000 0x400>;
+ interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <1>;
+ clocks = <&mux_uart2>;
+ status = "disabled";
+ };
+
+ uart3: serial@20068000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x20068000 0x400>;
+ interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <1>;
+ clocks = <&mux_uart3>;
+ status = "disabled";
+ };
+
+ dwmmc@10214000 {
+ compatible = "rockchip,cortex-a9-dw-mshc";
+ reg = <0x10214000 0x1000>;
+ interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ clocks = <&clk_gates5 10>, <&clk_gates2 11>;
+ clock-names = "biu", "ciu";
+
+ status = "disabled";
+ };
+
+ dwmmc@10218000 {
+ compatible = "rockchip,cortex-a9-dw-mshc";
+ reg = <0x10218000 0x1000>;
+ interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ clocks = <&clk_gates5 11>, <&clk_gates2 13>;
+ clock-names = "biu", "ciu";
+
+ status = "disabled";
+ };
+ };
+};
diff --git a/arch/arm/mach-rockchip/Kconfig b/arch/arm/mach-rockchip/Kconfig
new file mode 100644
index 0000000..7e332ea
--- /dev/null
+++ b/arch/arm/mach-rockchip/Kconfig
@@ -0,0 +1,17 @@
+config ARCH_ROCKCHIP
+ bool "Rockchip RK2928 and RK3xxx SOCs" if ARCH_MULTI_V7
+ select PINCTRL
+ select PINCTRL_ROCKCHIP
+ select ARCH_REQUIRE_GPIOLIB
+ select ARM_GIC
+ select CACHE_L2X0
+ select HAVE_ARM_TWD if LOCAL_TIMERS
+ select HAVE_SMP
+ select LOCAL_TIMERS if SMP
+ select COMMON_CLK
+ select GENERIC_CLOCKEVENTS
+ select DW_APB_TIMER
+ select DW_APB_TIMER_OF
+ help
+ Support for Rockchip's Cortex-A9 Single-to-Quad-Core-SoCs
+ containing the RK2928, RK30xx and RK31xx series.
diff --git a/arch/arm/mach-rockchip/Makefile b/arch/arm/mach-rockchip/Makefile
new file mode 100644
index 0000000..880b287
--- /dev/null
+++ b/arch/arm/mach-rockchip/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip.o
\ No newline at end of file
diff --git a/arch/arm/mach-rockchip/Makefile.boot b/arch/arm/mach-rockchip/Makefile.boot
new file mode 100644
index 0000000..a2d8c70
--- /dev/null
+++ b/arch/arm/mach-rockchip/Makefile.boot
@@ -0,0 +1,3 @@
+zreladdr-$(CONFIG_ARCH_ROCKCHIP) := 0x60408000
+params_phys-$(CONFIG_ARCH_ROCKCHIP) := 0x60088000
+initrd_phys-$(CONFIG_ARCH_ROCKCHIP) := 0x60800000
diff --git a/arch/arm/mach-rockchip/rockchip.c b/arch/arm/mach-rockchip/rockchip.c
new file mode 100644
index 0000000..094b37d
--- /dev/null
+++ b/arch/arm/mach-rockchip/rockchip.c
@@ -0,0 +1,54 @@
+/*
+ * Device Tree support for Rockchip SoCs
+ *
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/of_platform.h>
+#include <linux/irqchip.h>
+#include <linux/dw_apb_timer.h>
+#include <linux/clk/rockchip.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/hardware/cache-l2x0.h>
+
+static void __init rockchip_timer_init(void)
+{
+ rockchip_init_clocks();
+ dw_apb_timer_init();
+}
+
+static void __init rockchip_dt_init(void)
+{
+#ifdef CONFIG_CACHE_L2X0
+ l2x0_of_init(0, ~0UL);
+#endif
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+}
+
+static const char * const rockchip_board_dt_compat[] = {
+ "rockchip,rk2928", /* single core */
+ "rockchip,rk30xx", /* dual cores */
+ "rockchip,rk31xx", /* dual and quad cores */
+ NULL,
+};
+
+DT_MACHINE_START(ROCKCHIP_DT, "Rockchip Cortex-A9 (Device Tree)")
+ .map_io = debug_ll_io_init,
+ .init_machine = rockchip_dt_init,
+ .init_time = rockchip_timer_init,
+ .dt_compat = rockchip_board_dt_compat,
+MACHINE_END
--
1.7.2.3

2013-06-02 23:39:44

by Heiko Stübner

[permalink] [raw]
Subject: [PATCH 07/10] pinctrl: add pinctrl driver for Rockchip SoCs

This driver adds support the Cortex-A9 based SoCs from Rockchip,
so at least the RK2928, RK3066 (a and b) and RK3188.
Earlier Rockchip SoCs seem to use similar mechanics for gpio
handling so should be supportable with relative small changes.
Pull handling on the rk3188 is currently a stub, due to it being
a bit different to the earlier SoCs.

Pinmuxing as well as gpio (and interrupt-) handling tested on
a rk3066a based machine.

Signed-off-by: Heiko Stuebner <[email protected]>
---
.../bindings/pinctrl/rockchip-pinctrl.txt | 100 ++
drivers/pinctrl/Kconfig | 6 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-rockchip.c | 1319 ++++++++++++++++++++
include/dt-bindings/pinctrl/rockchip.h | 37 +
5 files changed, 1463 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/pinctrl/rockchip-pinctrl.txt
create mode 100644 drivers/pinctrl/pinctrl-rockchip.c
create mode 100644 include/dt-bindings/pinctrl/rockchip.h

diff --git a/Documentation/devicetree/bindings/pinctrl/rockchip-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/rockchip-pinctrl.txt
new file mode 100644
index 0000000..b939192
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/rockchip-pinctrl.txt
@@ -0,0 +1,100 @@
+* Rockchip Pinmux Controller
+
+The Rockchip Pinmux Controller, enables the IC
+to share one PAD to several functional blocks. The sharing is done by
+multiplexing the PAD input/output signals. For each PAD there are up to
+4 muxing options with option 0 being the use as a GPIO.
+
+Please refer to pinctrl-bindings.txt in this directory for details of the
+common pinctrl bindings used by client devices, including the meaning of the
+phrase "pin configuration node".
+
+The Rockchip pin configuration node is a node of a group of pins which can be
+used for a specific device or function. This node represents both mux and
+config of the pins in that group. The 'pins' selects the function mode(also
+named pin mode) this pin can work on and the 'config' configures various pad
+settings such as pull-up, etc.
+
+The pins are grouped into up to 5 individual pin banks which need to be
+defined as gpio sub-nodes of the pinmux controller.
+
+Required properties for iomux controller:
+ - compatible: one of "rockchip,rk2928-pinctrl", "rockchip,rk3066a-pinctrl"
+ "rockchip,rk3066b-pinctrl", "rockchip,rk3188-pinctrl"
+
+Required properties for gpio sub nodes:
+ - compatible: "rockchip,gpio-bank"
+ - reg: register of the gpio bank (different than the iomux registerset)
+ - interrupts: base interrupt of the gpio bank in the interrupt controller
+ - clocks: clock that drives this bank
+ - gpio-controller: identifies the node as a gpio controller and pin bank.
+ - #gpio-cells: number of cells in GPIO specifier. Since the generic GPIO
+ binding is used, the amount of cells must be specified as 2. See generic
+ GPIO binding documentation for description of particular cells.
+ - interrupt-controller: identifies the controller node as interrupt-parent.
+ - #interrupt-cells: the value of this property should be 2.
+ - First Cell: represents the external gpio interrupt number local to the
+ external gpio interrupt space of the controller.
+ - Second Cell: flags to identify the type of the interrupt
+ - 1 = rising edge triggered
+ - 2 = falling edge triggered
+ - 3 = rising and falling edge triggered
+ - 4 = high level triggered
+ - 8 = low level triggered
+
+
+Required properties for pin configuration node:
+ - rockchip,pins: 4 integers array, represents a group of pins mux and config
+ setting. The format is rockchip,pins = <PIN_BANK PIN_BANK_NUM MUX CONFIG>.
+ The MUX 0 means gpio and MUX 1 to 3 mean the specific device function
+
+Bits used for CONFIG:
+PULL_AUTO (1 << 0): indicate this pin needs a pull setting for SoCs
+ that determine the pull up or down themselfs
+PULL_UP (1 << 1): indicate this pin needs a pull up
+PULL_DOWN (1 << 2): indicate this pin needs a pull down
+
+Examples:
+
+pinctrl@20008000 {
+ compatible = "rockchip,rk3066a-pinctrl";
+ reg = <0x20008000 0x150>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ gpio0: gpio0@20034000 {
+ compatible = "rockchip,gpio-bank";
+ reg = <0x20034000 0x100>;
+ interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates8 9>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ ...
+
+ uart2 {
+ uart2_xfer: uart2-xfer {
+ rockchip,pins = <RK_GPIO1 8 1 RK_PINCTRL_PULL_AUTO>,
+ <RK_GPIO1 9 1 RK_PINCTRL_PULL_AUTO>;
+ };
+ };
+};
+
+uart2: serial@20064000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x20064000 0x400>;
+ interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <1>;
+ clocks = <&mux_uart2>;
+ status = "okay";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart2_xfer>;
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 5259d40..3e8cf8d 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -158,6 +158,12 @@ config PINCTRL_DB8540
bool "DB8540 pin controller driver"
depends on PINCTRL_NOMADIK && ARCH_U8500

+config PINCTRL_ROCKCHIP
+ bool
+ select PINMUX
+ select PINCONF
+ select GENERIC_IRQ_CHIP
+
config PINCTRL_SINGLE
tristate "One-register-per-pin type device tree based pinctrl driver"
depends on OF
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 3b26e3c..16c3f8bc 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_PINCTRL_NOMADIK) += pinctrl-nomadik.o
obj-$(CONFIG_PINCTRL_STN8815) += pinctrl-nomadik-stn8815.o
obj-$(CONFIG_PINCTRL_DB8500) += pinctrl-nomadik-db8500.o
obj-$(CONFIG_PINCTRL_DB8540) += pinctrl-nomadik-db8540.o
+obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
obj-$(CONFIG_PINCTRL_SIRF) += sirf/
obj-$(CONFIG_PINCTRL_SUNXI) += pinctrl-sunxi.o
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
new file mode 100644
index 0000000..9dba424
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -0,0 +1,1319 @@
+/*
+ * Pinctrl driver for Rockchip SoCs
+ *
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/clk-provider.h>
+#include <dt-bindings/pinctrl/rockchip.h>
+
+#include "core.h"
+
+/* 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
+
+/**
+ * @reg_base: register base of the gpio bank
+ * @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
+ * @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 clk *clk;
+ int irq;
+ u32 pin_base;
+ u8 nr_pins;
+ char *name;
+ u8 bank_num;
+ 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;
+
+};
+
+#define PIN_BANK(id, pins, label) \
+ { \
+ .bank_num = id, \
+ .nr_pins = pins, \
+ .name = label, \
+ }
+
+/**
+ * @pull_auto: some SoCs don't allow pulls to be specified as up or down, but
+ * instead decide this automatically based on the pad-type.
+ */
+struct rockchip_pin_ctrl {
+ struct rockchip_pin_bank *pin_banks;
+ u32 nr_banks;
+ u32 nr_pins;
+ char *label;
+ int mux_offset;
+ int pull_offset;
+ bool pull_auto;
+};
+
+/**
+ * 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.
+ * @num_pins: number of pins included in this group.
+ * @mux: the mux function number to be programmed when selected.
+ */
+struct rockchip_pin_group {
+ const char *name;
+ unsigned int *pins;
+ unsigned int npins;
+ unsigned int *func;
+ unsigned long *configs;
+};
+
+/**
+ * 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 {
+ void __iomem *reg_base;
+ 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 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)
+{
+ const struct rockchip_pin_group *grp = NULL;
+ int i;
+
+ for (i = 0; i < info->ngroups; i++) {
+ if (strcmp(info->groups[i].name, name))
+ continue;
+
+ grp = &info->groups[i];
+ break;
+ }
+
+ return grp;
+}
+
+/*
+ * 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->pin_base + b->nr_pins - 1) < pin))
+ 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++) {
+ if (b->bank_num == num)
+ break;
+
+ b++;
+ }
+
+ if (b->bank_num != num)
+ return ERR_PTR(-EINVAL);
+
+ return b;
+}
+
+/*
+ * 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 void rockchip_pin_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned offset)
+{
+ seq_printf(s, "%s", dev_name(pctldev->dev));
+}
+
+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->configs[i];
+ new_map[i].data.configs.num_configs = 1;
+ }
+
+ dev_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,
+ .pin_dbg_show = rockchip_pin_dbg_show,
+ .dt_node_to_map = rockchip_dt_node_to_map,
+ .dt_free_map = rockchip_dt_free_map,
+};
+
+/*
+ * Hardware access
+ */
+
+/*
+ * 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 void rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+ void __iomem *reg = info->reg_base + info->ctrl->mux_offset;
+ unsigned long flags;
+ int bit;
+ u32 data;
+
+ dev_dbg(info->dev, "setting mux of GPIO%d-%d to %d\n",
+ bank->bank_num, pin, mux);
+
+ /* get basic quadrupel of mux registers and the correct reg inside */
+ reg += bank->bank_num * 0x10;
+ reg += (pin / 8) * 4;
+ bit = (pin % 8) * 2;
+
+ spin_lock_irqsave(&bank->slock, flags);
+
+ data = (3 << (bit + 16));
+ data |= (mux & 3) << bit;
+ writel(data, reg);
+
+ spin_unlock_irqrestore(&bank->slock, flags);
+}
+
+static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+ void __iomem *reg;
+ int bit;
+ u32 data;
+
+ /* rk3066b does support any pulls */
+ if (!info->ctrl->pull_offset)
+ return 0;
+
+ reg = info->reg_base + info->ctrl->pull_offset;
+
+ if (info->ctrl->pull_auto) {
+ reg += bank->bank_num * 8;
+ reg += (pin_num / 16) * 4;
+ bit = pin_num % 16;
+
+ data = readl_relaxed(reg);
+ data >>= bit;
+ data &= 1;
+
+ return !data;
+ } else {
+ dev_err(info->dev, "pull support for rk31xx not implemented\n");
+ return -EIO;
+ }
+}
+
+static int rockchip_set_pull(struct rockchip_pin_bank *bank,
+ int pin_num, int pull)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+ void __iomem *reg;
+ unsigned long flags;
+ int bit;
+ u32 data;
+
+ dev_dbg(info->dev, "setting pull of GPIO%d-%d to %d\n",
+ bank->bank_num, pin_num, pull);
+
+ /* rk3066b does support any pulls */
+ if (!info->ctrl->pull_offset)
+ return pull ? -EINVAL : 0;
+
+ reg = info->reg_base + info->ctrl->pull_offset;
+
+ if (info->ctrl->pull_auto) {
+ if (pull > RK_PINCTRL_PULL_AUTO) {
+ dev_err(info->dev, "only PULL_AUTO allowed\n");
+ return -EINVAL;
+ }
+
+ reg += bank->bank_num * 8;
+ reg += (pin_num / 16) * 4;
+ bit = pin_num % 16;
+
+ spin_lock_irqsave(&bank->slock, flags);
+
+ data = (1 << (bit + 16));
+ if (!pull)
+ data |= (1 << bit);
+ writel(data, reg);
+
+ spin_unlock_irqrestore(&bank->slock, flags);
+ } else {
+ if (pull == RK_PINCTRL_PULL_AUTO) {
+ dev_err(info->dev, "pull direction (up/down) needs to be specified\n");
+ return -EINVAL;
+ }
+
+ dev_err(info->dev, "pull support for rk31xx not implemented\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * 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;
+ struct rockchip_pin_bank *bank;
+ int cnt;
+
+ dev_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]);
+ rockchip_set_mux(bank, pins[cnt] - bank->pin_base,
+ info->groups[group].func[cnt]);
+ }
+
+ 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;
+
+ dev_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;
+ 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");
+
+ rockchip_set_mux(bank, pin, RK_FUNC_GPIO);
+
+ data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
+ /* set bit to 1 for output, 0 for input */
+ if (!input)
+ data |= (1 << pin);
+ else
+ data &= ~(1 << 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
+ */
+
+/* set the pin config settings for a specified pin */
+static int rockchip_pinconf_set(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);
+ int val;
+
+ val = config & (RK_PINCTRL_PULL_AUTO | RK_PINCTRL_PULL_UP |
+ RK_PINCTRL_PULL_DOWN);
+ return rockchip_set_pull(bank, pin - bank->pin_base, val);
+
+ 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);
+
+ *config |= rockchip_get_pull(bank, pin - bank->pin_base);
+
+ return 0;
+}
+
+static const struct pinconf_ops rockchip_pinconf_ops = {
+ .pin_config_get = rockchip_pinconf_get,
+ .pin_config_set = rockchip_pinconf_set,
+};
+
+static const char *gpio_compat = "rockchip,gpio-bank";
+
+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_device_is_compatible(child, gpio_compat))
+ 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 pin;
+
+ dev_dbg(info->dev, "group(%d): %s\n", index, np->name);
+
+ /* Initialise group */
+ grp->name = np->name;
+
+ /*
+ * the binding format is rockchip,pins = <bank pin mux CONFIG>,
+ * 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->configs = devm_kzalloc(info->dev, grp->npins *
+ sizeof(unsigned long),
+ GFP_KERNEL);
+ grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int),
+ GFP_KERNEL);
+ grp->func = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int),
+ GFP_KERNEL);
+ if (!grp->configs || !grp->pins)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < size; i += 4, j++) {
+ num = be32_to_cpu(*list++);
+ bank = bank_num_to_bank(info, num);
+ if (IS_ERR(bank))
+ return PTR_ERR(bank);
+
+ pin = be32_to_cpu(*list++);
+ grp->pins[j] = bank->pin_base + pin;
+ grp->func[j] = be32_to_cpu(*list++);
+ grp->configs[j] = be32_to_cpu(*list++);
+ }
+
+ 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) {
+ dev_err(info->dev, "no groups defined for %s\n", np->name);
+ return -EINVAL;
+ }
+ 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_device_is_compatible(child, gpio_compat))
+ 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 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 &= ~(1 << offset);
+ if (value)
+ data |= 1 << 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 = {
+ .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 pend;
+
+ dev_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);
+
+ while (pend) {
+ unsigned int virq;
+
+ irq = __ffs(pend);
+ pend &= ~(1 << irq);
+ virq = irq_linear_revmap(bank->domain, irq);
+
+ if (!virq) {
+ dev_err(bank->drvdata->dev, "unmapped irq %d\n", irq);
+ continue;
+ }
+
+ dev_dbg(bank->drvdata->dev, "handling irq %d\n", irq);
+
+ generic_handle_irq(virq);
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct rockchip_pin_bank *bank = gc->private;
+ u32 bit = (1 << d->hwirq);
+ u32 polarity;
+ u32 level;
+ u32 data;
+
+ 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);
+
+ irq_gc_lock(gc);
+
+ level = readl_relaxed(gc->reg_base + GPIO_INTTYPE_LEVEL);
+ polarity = readl_relaxed(gc->reg_base + GPIO_INT_POLARITY);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ level |= bit;
+ polarity |= bit;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ level |= bit;
+ polarity &= ~bit;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ level &= ~bit;
+ polarity |= bit;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ level &= ~bit;
+ polarity &= ~bit;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ writel_relaxed(level, gc->reg_base + GPIO_INTTYPE_LEVEL);
+ writel_relaxed(polarity, gc->reg_base + GPIO_INT_POLARITY);
+
+ irq_gc_unlock(gc);
+
+ /* make sure the pin is configured as gpio input */
+ rockchip_set_mux(bank, d->hwirq, RK_FUNC_GPIO);
+ data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
+ data &= ~bit;
+ writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR);
+
+ return 0;
+}
+
+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;
+ struct irq_chip_generic *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->domain = irq_domain_add_linear(bank->of_node, 32,
+ &irq_generic_chip_ops, NULL);
+ if (!bank->domain) {
+ dev_warn(&pdev->dev, "could not initialize irq domain for bank %s\n",
+ bank->name);
+ continue;
+ }
+
+ ret = irq_alloc_domain_generic_chips(bank->domain, 32, 1,
+ "rockchip_gpio_irq", handle_level_irq,
+ clr, 0, IRQ_GC_INIT_MASK_CACHE);
+ if (ret) {
+ dev_err(&pdev->dev, "could not alloc generic chips for bank %s\n",
+ bank->name);
+ irq_domain_remove(bank->domain);
+ continue;
+ }
+
+ gc = irq_get_domain_generic_chip(bank->domain, 0);
+ gc->reg_base = bank->reg_base;
+ gc->private = bank;
+ gc->chip_types[0].regs.mask = GPIO_INTEN;
+ gc->chip_types[0].regs.ack = GPIO_PORTS_EOI;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
+ gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake;
+ gc->chip_types[0].chip.irq_set_type = rockchip_irq_set_type;
+
+ 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 device *dev)
+{
+ struct resource res;
+
+ if (of_address_to_resource(bank->of_node, 0, &res)) {
+ dev_err(dev, "cannot find IO resource for bank\n");
+ return -ENOENT;
+ }
+
+ bank->reg_base = devm_ioremap_resource(dev, &res);
+ if (IS_ERR(bank->reg_base))
+ return PTR_ERR(bank->reg_base);
+
+ 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);
+}
+
+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 i;
+
+ 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, &pdev->dev))
+ bank->valid = true;
+
+ break;
+ }
+ }
+ }
+
+ bank = ctrl->pin_banks;
+ for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
+ spin_lock_init(&bank->slock);
+ bank->drvdata = d;
+ bank->pin_base = ctrl->nr_pins;
+ ctrl->nr_pins += bank->nr_pins;
+ }
+
+ 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 resource *res;
+ 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;
+
+ ctrl = rockchip_pinctrl_get_soc_data(info, pdev);
+ if (!ctrl) {
+ dev_err(dev, "driver data not available\n");
+ return -EINVAL;
+ }
+ info->ctrl = ctrl;
+ info->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "cannot find IO resource\n");
+ return -ENOENT;
+ }
+
+ info->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(info->reg_base))
+ return PTR_ERR(info->reg_base);
+
+ 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);
+
+ return 0;
+}
+
+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",
+ .mux_offset = 0xa8,
+ .pull_offset = 0x118,
+ .pull_auto = 1,
+};
+
+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",
+ .mux_offset = 0xa8,
+ .pull_offset = 0x118,
+ .pull_auto = 1,
+};
+
+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",
+ .mux_offset = 0x60,
+ .pull_offset = -EINVAL,
+};
+
+static struct rockchip_pin_bank rk3188_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 rk3188_pin_ctrl = {
+ .pin_banks = rk3188_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3188_pin_banks),
+ .label = "RK3188-GPIO",
+ .mux_offset = 0x68,
+ .pull_offset = 0x164,
+};
+
+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 },
+ {},
+};
+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 = of_match_ptr(rockchip_pinctrl_dt_match),
+ },
+};
+
+static int __init rockchip_pinctrl_drv_register(void)
+{
+ return platform_driver_register(&rockchip_pinctrl_driver);
+}
+postcore_initcall(rockchip_pinctrl_drv_register);
+
+MODULE_AUTHOR("Heiko Stuebner <[email protected]>");
+MODULE_DESCRIPTION("Rockchip pinctrl driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/dt-bindings/pinctrl/rockchip.h b/include/dt-bindings/pinctrl/rockchip.h
new file mode 100644
index 0000000..8721db2
--- /dev/null
+++ b/include/dt-bindings/pinctrl/rockchip.h
@@ -0,0 +1,37 @@
+/*
+ * Header providing constants for Rockchip pinctrl bindings.
+ *
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __DT_BINDINGS_ROCKCHIP_PINCTRL_H__
+#define __DT_BINDINGS_ROCKCHIP_PINCTRL_H__
+
+#define RK_GPIO0 0
+#define RK_GPIO1 1
+#define RK_GPIO2 2
+#define RK_GPIO3 3
+#define RK_GPIO4 4
+#define RK_GPIO6 6
+
+#define RK_FUNC_GPIO 0
+#define RK_FUNC_1 1
+#define RK_FUNC_2 2
+
+#define RK_PINCTRL_NONE 0
+#define RK_PINCTRL_PULL_AUTO (1 << 0)
+#define RK_PINCTRL_PULL_UP (1 << 1)
+#define RK_PINCTRL_PULL_DOWN (1 << 2)
+
+#endif
--
1.7.2.3

2013-06-02 23:39:57

by Heiko Stübner

[permalink] [raw]
Subject: [PATCH 02/10] clocksource: dw_apb_timer_of: add clock-handling

Add the possibility to get the clock-frequency from a timer clock instead
of specifying it as dt property. Additionally also add the possibility
to also define a controlling periphal clock for the timer block.

The clock-frequency property is kept to act as fallback if no clocks
are specified.

Signed-off-by: Heiko Stuebner <[email protected]>
---
Documentation/devicetree/bindings/rtc/dw-apb.txt | 19 ++++++++++++++++
drivers/clocksource/dw_apb_timer_of.c | 26 +++++++++++++++++++++-
2 files changed, 44 insertions(+), 1 deletions(-)

diff --git a/Documentation/devicetree/bindings/rtc/dw-apb.txt b/Documentation/devicetree/bindings/rtc/dw-apb.txt
index 93e2b0f..80ab1c1 100644
--- a/Documentation/devicetree/bindings/rtc/dw-apb.txt
+++ b/Documentation/devicetree/bindings/rtc/dw-apb.txt
@@ -5,9 +5,20 @@ Required properties:
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: IRQ line for the timer.
+- either clocks+clock-names or clock-frequency properties
+
+Optional properties:
+- clocks : list of clock specifiers, corresponding to entries in
+ the clock-names property;
+- clock-names : should contain "timer and "pclk" entries, matching entries
+ in the clocks property.
- clock-frequency: The frequency in HZ of the timer.
- clock-freq: For backwards compatibility with picoxcell

+If using the clock specifiers, the pclk clock is optional, as not all
+systems may use one.
+
+
Example:

timer1: timer@ffc09000 {
@@ -23,3 +34,11 @@ Example:
clock-frequency = <200000000>;
reg = <0xffd00000 0x1000>;
};
+
+ timer3: timer@ffe00000 {
+ compatible = "snps,dw-apb-timer-osc";
+ interrupts = <0 170 4>;
+ reg = <0xffe00000 0x1000>;
+ clocks = <&timer_clk>, <&timer_pclk>;
+ clock-names = "timer", "pclk";
+ };
diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c
index d6c0fda..1964f87 100644
--- a/drivers/clocksource/dw_apb_timer_of.c
+++ b/drivers/clocksource/dw_apb_timer_of.c
@@ -20,6 +20,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
+#include <linux/clk.h>

#include <asm/mach/time.h>
#include <asm/sched_clock.h>
@@ -27,14 +28,37 @@
static void timer_get_base_and_rate(struct device_node *np,
void __iomem **base, u32 *rate)
{
+ struct clk *timer_clk;
+ struct clk *pclk;
+
*base = of_iomap(np, 0);

if (!*base)
panic("Unable to map regs for %s", np->name);

+ /*
+ * Not all implementations use a periphal clock, so don't panic
+ * if it's not present
+ */
+ pclk = of_clk_get_by_name(np, "pclk");
+ if (!IS_ERR(pclk))
+ if (clk_prepare_enable(pclk))
+ pr_warn("pclk for %s is present, but could not be activated\n",
+ np->name);
+
+ timer_clk = of_clk_get_by_name(np, "timer");
+ if (IS_ERR(timer_clk))
+ goto try_clock_freq;
+
+ if (!clk_prepare_enable(timer_clk)) {
+ *rate = clk_get_rate(timer_clk);
+ return;
+ }
+
+try_clock_freq:
if (of_property_read_u32(np, "clock-freq", rate) &&
of_property_read_u32(np, "clock-frequency", rate))
- panic("No clock-frequency property for %s", np->name);
+ panic("No clock nor clock-frequency property for %s", np->name);
}

static void add_clockevent(struct device_node *event_timer)
--
1.7.2.3

2013-06-02 23:40:04

by Heiko Stübner

[permalink] [raw]
Subject: [PATCH 01/10] clocksource: dw_apb_timer_of: use the clocksource as sched clock if necessary

Currently the dw_apb_timer always expects a separate special timer to be
availbable for the sched_clock. Some devices using dw_apb_timers do not
have the sptimer but can use the clocksource as sched_clock.
Therefore this patch adds using the clocksource timer as
a fallback if no usable sched timer is found.

Signed-off-by: Heiko Stuebner <[email protected]>
---
drivers/clocksource/dw_apb_timer_of.c | 26 +++++++++++++++++---------
1 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c
index ab09ed3..d6c0fda 100644
--- a/drivers/clocksource/dw_apb_timer_of.c
+++ b/drivers/clocksource/dw_apb_timer_of.c
@@ -57,6 +57,9 @@ static void add_clockevent(struct device_node *event_timer)
dw_apb_clockevent_register(ced);
}

+static void __iomem *sched_io_base;
+static u32 sched_rate;
+
static void add_clocksource(struct device_node *source_timer)
{
void __iomem *iobase;
@@ -71,9 +74,15 @@ static void add_clocksource(struct device_node *source_timer)

dw_apb_clocksource_start(cs);
dw_apb_clocksource_register(cs);
-}

-static void __iomem *sched_io_base;
+ /*
+ * Fallback to use the clocksource as sched_clock if no separate
+ * timer is found. sched_io_base then points to the current_value
+ * register of the clocksource timer.
+ */
+ sched_io_base = iobase + 0x04;
+ sched_rate = rate;
+}

static u32 read_sched_clock(void)
{
@@ -89,16 +98,15 @@ static const struct of_device_id sptimer_ids[] __initconst = {
static void init_sched_clock(void)
{
struct device_node *sched_timer;
- u32 rate;

sched_timer = of_find_matching_node(NULL, sptimer_ids);
- if (!sched_timer)
- panic("No RTC for sched clock to use");
-
- timer_get_base_and_rate(sched_timer, &sched_io_base, &rate);
- of_node_put(sched_timer);
+ if (sched_timer) {
+ timer_get_base_and_rate(sched_timer, &sched_io_base,
+ &sched_rate);
+ of_node_put(sched_timer);
+ }

- setup_sched_clock(read_sched_clock, 32, rate);
+ setup_sched_clock(read_sched_clock, 32, sched_rate);
}

static const struct of_device_id osctimer_ids[] __initconst = {
--
1.7.2.3

2013-06-02 23:40:14

by Heiko Stübner

[permalink] [raw]
Subject: [PATCH 06/10] mmc: dw_mmc-pltfm: add Rockchip variant

Cortex-A9 SoCs from Rockchip use a slightly modified variant of dw_mmc
controllers that seems to require the SDMMC_CMD_USE_HOLD_REG bit to
always be set.

There also seem to be no other modifications (additional register etc)
present, so to keep the footprint low, add this small variant to the
pltfm driver.

Signed-off-by: Heiko Stuebner <[email protected]>
---
drivers/mmc/host/dw_mmc-pltfm.c | 48 +++++++++++++++++++++++++++-----------
1 files changed, 34 insertions(+), 14 deletions(-)

diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index 0048da8..7d041b5 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -24,6 +24,16 @@

#include "dw_mmc.h"

+
+static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr)
+{
+ *cmdr |= SDMMC_CMD_USE_HOLD_REG;
+}
+
+static const struct dw_mci_drv_data rockchip_drv_data = {
+ .prepare_command = dw_mci_rockchip_prepare_command,
+};
+
int dw_mci_pltfm_register(struct platform_device *pdev,
const struct dw_mci_drv_data *drv_data)
{
@@ -63,20 +73,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
}
EXPORT_SYMBOL_GPL(dw_mci_pltfm_register);

-static int dw_mci_pltfm_probe(struct platform_device *pdev)
-{
- return dw_mci_pltfm_register(pdev, NULL);
-}
-
-int dw_mci_pltfm_remove(struct platform_device *pdev)
-{
- struct dw_mci *host = platform_get_drvdata(pdev);
-
- dw_mci_remove(host);
- return 0;
-}
-EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove);
-
#ifdef CONFIG_PM_SLEEP
/*
* TODO: we should probably disable the clock to the card in the suspend path.
@@ -114,10 +110,34 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops);

static const struct of_device_id dw_mci_pltfm_match[] = {
{ .compatible = "snps,dw-mshc", },
+ { .compatible = "rockchip,cortex-a9-dw-mshc",
+ .data = &rockchip_drv_data },
{},
};
MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);

+static int dw_mci_pltfm_probe(struct platform_device *pdev)
+{
+ const struct dw_mci_drv_data *drv_data = NULL;
+ const struct of_device_id *match;
+
+ if (pdev->dev.of_node) {
+ match = of_match_node(dw_mci_pltfm_match, pdev->dev.of_node);
+ drv_data = match->data;
+ }
+
+ return dw_mci_pltfm_register(pdev, drv_data);
+}
+
+int dw_mci_pltfm_remove(struct platform_device *pdev)
+{
+ struct dw_mci *host = platform_get_drvdata(pdev);
+
+ dw_mci_remove(host);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove);
+
static struct platform_driver dw_mci_pltfm_driver = {
.probe = dw_mci_pltfm_probe,
.remove = dw_mci_pltfm_remove,
--
1.7.2.3

2013-06-02 23:40:20

by Heiko Stübner

[permalink] [raw]
Subject: [PATCH 04/10] clk: divider: add flag to limit possible dividers to even numbers

SoCs like the Rockchip Cortex-A9 ones contain divider some clocks
that use the regular mechanisms for storage but allow only even
dividers and 1 to be used.

Therefore add a flag that lets _is_valid_div limit the valid dividers
to these values. _get_maxdiv is also adapted to return even values
for the CLK_DIVIDER_ONE_BASED case.

Signed-off-by: Heiko Stuebner <[email protected]>
---
drivers/clk/clk-divider.c | 14 ++++++++++++--
include/linux/clk-provider.h | 2 ++
2 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index e37c48a..adfbd0d 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -45,8 +45,16 @@ static unsigned int _get_table_maxdiv(const struct clk_div_table *table)

static unsigned int _get_maxdiv(struct clk_divider *divider)
{
- if (divider->flags & CLK_DIVIDER_ONE_BASED)
- return div_mask(divider);
+ if (divider->flags & CLK_DIVIDER_ONE_BASED) {
+ unsigned int div = div_mask(divider);
+
+ /* decrease to even number */
+ if (divider->flags & CLK_DIVIDER_EVEN)
+ div--;
+
+ return div;
+ }
+
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
return 1 << div_mask(divider);
if (divider->table)
@@ -141,6 +149,8 @@ static bool _is_valid_div(struct clk_divider *divider, unsigned int div)
return is_power_of_2(div);
if (divider->table)
return _is_valid_table_div(divider->table, div);
+ if (divider->flags & CLK_DIVIDER_EVEN && div != 1 && (div % 2) != 0)
+ return false;
return true;
}

diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 420a187..9fdd60d 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -268,6 +268,7 @@ struct clk_div_table {
* indicate the bits that get changed during a write. So for a clock with
* shift 0 and width 2, setting the divider to 2 would result in a write
* of (3 << 16) | (2 << 0).
+ * CLK_DIVIDER_EVEN - only allow even divider values
*/
struct clk_divider {
struct clk_hw hw;
@@ -283,6 +284,7 @@ struct clk_divider {
#define CLK_DIVIDER_POWER_OF_TWO BIT(1)
#define CLK_DIVIDER_ALLOW_ZERO BIT(2)
#define CLK_DIVIDER_MASK_UPPER_HALF BIT(3)
+#define CLK_DIVIDER_EVEN BIT(4)

extern const struct clk_ops clk_divider_ops;
struct clk *clk_register_divider(struct device *dev, const char *name,
--
1.7.2.3

2013-06-02 23:40:26

by Heiko Stübner

[permalink] [raw]
Subject: [PATCH 03/10] clk: flag to use upper half of the register as change indicator

There exist platforms, namely at least all Rockchip Cortex-A9 based ones,
that don't use the paradigm of reading-changing-writing the register contents,
but instead only write the changes to the register with a mask that indicates
the changed bits.

This patch adds flags and code to support the case where the lower 16 bit of
hold the information and the upper 16 bit are used as mask to indicate the
written changes.

As hardware-specific flags should not be added to the common clk flags, the
flags are added to gate, mux and divider clocks individually.

Signed-off-by: Heiko Stuebner <[email protected]>
---
drivers/clk/clk-divider.c | 15 +++++++++++++--
drivers/clk/clk-gate.c | 24 ++++++++++++++++++------
drivers/clk/clk-mux.c | 15 +++++++++++++--
include/linux/clk-provider.h | 16 ++++++++++++++++
4 files changed, 60 insertions(+), 10 deletions(-)

diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index 6d96741..e37c48a 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -217,8 +217,12 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
if (divider->lock)
spin_lock_irqsave(divider->lock, flags);

- val = readl(divider->reg);
- val &= ~(div_mask(divider) << divider->shift);
+ if (divider->flags & CLK_DIVIDER_MASK_UPPER_HALF) {
+ val = div_mask(divider) << (divider->shift + 16);
+ } else {
+ val = readl(divider->reg);
+ val &= ~(div_mask(divider) << divider->shift);
+ }
val |= value << divider->shift;
writel(val, divider->reg);

@@ -245,6 +249,13 @@ static struct clk *_register_divider(struct device *dev, const char *name,
struct clk *clk;
struct clk_init_data init;

+ if ((clk_divider_flags & CLK_DIVIDER_MASK_UPPER_HALF) &&
+ (shift + width > 15)) {
+ pr_err("%s: shift %d + width %d invalid\n", __func__,
+ shift, width);
+ return ERR_PTR(-EINVAL);
+ }
+
/* allocate the divider */
div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
if (!div) {
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
index 15114fe..e553fa7 100644
--- a/drivers/clk/clk-gate.c
+++ b/drivers/clk/clk-gate.c
@@ -53,12 +53,19 @@ static void clk_gate_endisable(struct clk_hw *hw, int enable)
if (gate->lock)
spin_lock_irqsave(gate->lock, flags);

- reg = readl(gate->reg);
-
- if (set)
- reg |= BIT(gate->bit_idx);
- else
- reg &= ~BIT(gate->bit_idx);
+ if (gate->flags & CLK_GATE_MASK_UPPER_HALF) {
+ reg = BIT(gate->bit_idx + 16);
+
+ if (set)
+ reg |= BIT(gate->bit_idx);
+ } else {
+ reg = readl(gate->reg);
+
+ if (set)
+ reg |= BIT(gate->bit_idx);
+ else
+ reg &= ~BIT(gate->bit_idx);
+ }

writel(reg, gate->reg);

@@ -121,6 +128,11 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
struct clk *clk;
struct clk_init_data init;

+ if ((clk_gate_flags & CLK_GATE_MASK_UPPER_HALF) && bit_idx > 15) {
+ pr_err("%s: bit_idx %d invalid\n", __func__, bit_idx);
+ return ERR_PTR(-EINVAL);
+ }
+
/* allocate the gate */
gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
if (!gate) {
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
index 25b1734..fce26f5 100644
--- a/drivers/clk/clk-mux.c
+++ b/drivers/clk/clk-mux.c
@@ -86,8 +86,12 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
if (mux->lock)
spin_lock_irqsave(mux->lock, flags);

- val = readl(mux->reg);
- val &= ~(mux->mask << mux->shift);
+ if (mux->flags & CLK_MUX_MASK_UPPER_HALF) {
+ val = mux->mask << (mux->shift + 16);
+ } else {
+ val = readl(mux->reg);
+ val &= ~(mux->mask << mux->shift);
+ }
val |= index << mux->shift;
writel(val, mux->reg);

@@ -112,6 +116,13 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name,
struct clk *clk;
struct clk_init_data init;

+ if (!mask || ((clk_mux_flags & CLK_MUX_MASK_UPPER_HALF) &&
+ (__fls(mask << shift) > 15))) {
+ pr_err("%s: shift %d + mask %d invalid\n", __func__,
+ shift, mask);
+ return ERR_PTR(-EINVAL);
+ }
+
/* allocate the mux */
mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
if (!mux) {
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 265f384..420a187 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -210,6 +210,11 @@ void of_fixed_clk_setup(struct device_node *np);
* CLK_GATE_SET_TO_DISABLE - by default this clock sets the bit at bit_idx to
* enable the clock. Setting this flag does the opposite: setting the bit
* disable the clock and clearing it enables the clock
+ * CLK_GATE_MASK_UPPER_HALF - this clock only uses the lower 16 bit of the
+ * register to hold the gate bits, while using the upper 16 bit to
+ * indicate the bits that get changed during a write. So gating the
+ * clock in bit 2 would write BIT(18) | BIT(2) to the register, while
+ * ungating this clock would write only BIT(18) to the register.
*/
struct clk_gate {
struct clk_hw hw;
@@ -220,6 +225,7 @@ struct clk_gate {
};

#define CLK_GATE_SET_TO_DISABLE BIT(0)
+#define CLK_GATE_MASK_UPPER_HALF BIT(1)

extern const struct clk_ops clk_gate_ops;
struct clk *clk_register_gate(struct device *dev, const char *name,
@@ -257,6 +263,11 @@ struct clk_div_table {
* Some hardware implementations gracefully handle this case and allow a
* zero divisor by not modifying their input clock
* (divide by one / bypass).
+ * CLK_DIVIDER_MASK_UPPER_HALF - this clock only uses the lower 16 bit of the
+ * register to hold the divider bits, while using the upper 16 bit to
+ * indicate the bits that get changed during a write. So for a clock with
+ * shift 0 and width 2, setting the divider to 2 would result in a write
+ * of (3 << 16) | (2 << 0).
*/
struct clk_divider {
struct clk_hw hw;
@@ -271,6 +282,7 @@ struct clk_divider {
#define CLK_DIVIDER_ONE_BASED BIT(0)
#define CLK_DIVIDER_POWER_OF_TWO BIT(1)
#define CLK_DIVIDER_ALLOW_ZERO BIT(2)
+#define CLK_DIVIDER_MASK_UPPER_HALF BIT(3)

extern const struct clk_ops clk_divider_ops;
struct clk *clk_register_divider(struct device *dev, const char *name,
@@ -299,6 +311,9 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name,
* Flags:
* CLK_MUX_INDEX_ONE - register index starts at 1, not 0
* CLK_MUX_INDEX_BIT - register index is a single bit (power of two)
+ * CLK_MUX_MASK_UPPER_HALF - this clock only uses the lower 16 bit of the
+ * register to hold the mux bits, while using the upper 16 bit to
+ * indicate the bits that get changed during a write.
*/
struct clk_mux {
struct clk_hw hw;
@@ -312,6 +327,7 @@ struct clk_mux {

#define CLK_MUX_INDEX_ONE BIT(0)
#define CLK_MUX_INDEX_BIT BIT(1)
+#define CLK_MUX_MASK_UPPER_HALF BIT(3)

extern const struct clk_ops clk_mux_ops;

--
1.7.2.3

2013-06-02 23:41:13

by Heiko Stübner

[permalink] [raw]
Subject: [PATCH 05/10] mmc: dw_mmc-pltfm: remove static from dw_mci_pltfm_remove

dw_mci_pltfm_remove gets exported and used by dw_mmc-exynos, so should
not be static.

Signed-off-by: Heiko Stuebner <[email protected]>
---
drivers/mmc/host/dw_mmc-pltfm.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index 37873f1..0048da8 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -68,7 +68,7 @@ static int dw_mci_pltfm_probe(struct platform_device *pdev)
return dw_mci_pltfm_register(pdev, NULL);
}

-static int dw_mci_pltfm_remove(struct platform_device *pdev)
+int dw_mci_pltfm_remove(struct platform_device *pdev)
{
struct dw_mci *host = platform_get_drvdata(pdev);

--
1.7.2.3

2013-06-03 02:08:32

by Arnd Bergmann

[permalink] [raw]
Subject: Re: arm: add basic support for Rockchip Cortex-A9 SoCs

On Monday 03 June 2013 00:55:15 Heiko St?bner wrote:
> Prepare your eggs and tomatoes please, I got myself a second playground .
>
> As the title implies this series adds basic support for Rockchip's Cortex-A9
> SoCs, like the RK3066 used in many cheaper tablets.
>
> On the periphal side it's very lightweight, as it seems someone from Rockchip
> was shopping in the DesignWare store.
>
> This makes the pinctrl driver the biggest hunk in the series, but to get some
> bonus points I used the generic_irq implementation for the interrupt handling,
> based on Thomas' irqdomain support series.
>
> Supported periphals are, the timers, uarts and mmc controllers, so interested
> parties will need a serial console for now.
>
> Hopefully I didn't mess up to much

Great stuff! I have a few small comments for later, but I'm basically fine
with it going in as-is.

Needs review from the clk and pinctrl maintainers of course.

Arnd

2013-06-03 02:08:52

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 09/10] arm: add debug uarts for rockchip rk29xx and rk3xxx series

On Monday 03 June 2013 01:01:03 Heiko St?bner wrote:
> +#if defined(CONFIG_DEBUG_RK29_UART0)
> +#define ROCKCHIP_UART_DEBUG_PHYS_BASE 0x20060000
> +#define ROCKCHIP_UART_DEBUG_VIRT_BASE 0xfe060000

0xfed60000 maybe? looks like a typo.

Arnd

2013-06-03 02:15:52

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 10/10] arm: add basic support for Rockchip RK3066a boards

On Monday 03 June 2013 01:02:20 Heiko St?bner wrote:
> index 0000000..094b37d
> --- /dev/null
> +++ b/arch/arm/mach-rockchip/rockchip.c

If all goes well, this file can be removed again in 3.11 since it's
all generic, but let's add it for now.

> +
> +static void __init rockchip_timer_init(void)
> +{
> + rockchip_init_clocks();
> + dw_apb_timer_init();
> +}

Can't you use

of_clk_init(NULL);
clocksource_of_init();

here and change the two drivers to provide the respective macros?

> +
> +static void __init rockchip_dt_init(void)
> +{
> +#ifdef CONFIG_CACHE_L2X0
> + l2x0_of_init(0, ~0UL);
> +#endif
> + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
> +}

We still need to find a common location to call l2x0_of_init.

> +
> +static const char * const rockchip_board_dt_compat[] = {
> + "rockchip,rk2928", /* single core */
> + "rockchip,rk30xx", /* dual cores */
> + "rockchip,rk31xx", /* dual and quad cores */
> + NULL,
> +};

Please use real numbers instead of wildcards: rockchip,rk3066
not rockchip,rk30xx.

> +DT_MACHINE_START(ROCKCHIP_DT, "Rockchip Cortex-A9 (Device Tree)")
> + .map_io = debug_ll_io_init,
> + .init_machine = rockchip_dt_init,
> + .init_time = rockchip_timer_init,
> + .dt_compat = rockchip_board_dt_compat,
> +MACHINE_END

The map_io line can already get removed.

What about SMP support? Still working on it?

Arnd

2013-06-03 03:22:34

by Baruch Siach

[permalink] [raw]
Subject: Re: [PATCH 02/10] clocksource: dw_apb_timer_of: add clock-handling

Hi Heiko,

On Mon, Jun 03, 2013 at 12:56:37AM +0200, Heiko St?bner wrote:
> Add the possibility to get the clock-frequency from a timer clock instead
> of specifying it as dt property. Additionally also add the possibility
> to also define a controlling periphal clock for the timer block.
>
> The clock-frequency property is kept to act as fallback if no clocks
> are specified.
>
> Signed-off-by: Heiko Stuebner <[email protected]>
> ---
> Documentation/devicetree/bindings/rtc/dw-apb.txt | 19 ++++++++++++++++
> drivers/clocksource/dw_apb_timer_of.c | 26 +++++++++++++++++++++-
> 2 files changed, 44 insertions(+), 1 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/rtc/dw-apb.txt b/Documentation/devicetree/bindings/rtc/dw-apb.txt
> index 93e2b0f..80ab1c1 100644
> --- a/Documentation/devicetree/bindings/rtc/dw-apb.txt
> +++ b/Documentation/devicetree/bindings/rtc/dw-apb.txt
> @@ -5,9 +5,20 @@ Required properties:
> - reg: physical base address of the controller and length of memory mapped
> region.
> - interrupts: IRQ line for the timer.
> +- either clocks+clock-names or clock-frequency properties
> +
> +Optional properties:
> +- clocks : list of clock specifiers, corresponding to entries in
> + the clock-names property;
> +- clock-names : should contain "timer and "pclk" entries, matching entries

Missing closing quotes around "timer".

baruch

--
http://baruch.siach.name/blog/ ~. .~ Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
- [email protected] - tel: +972.2.679.5364, http://www.tkos.co.il -

2013-06-03 03:27:20

by Olof Johansson

[permalink] [raw]
Subject: Re: [PATCH 08/10] clk: add basic Rockchip rk3066a clock support

On Mon, Jun 03, 2013 at 01:00:31AM +0200, Heiko St?bner wrote:
> This adds basic support for clocks on Rockchip rk3066 SoCs.
> The clock handling thru small dt nodes is heavily inspired by the
> sunxi clk code.
>
> The clock specifiers are named as "x-cortex-a9-x", because the clock
> semantics are the same thru the whole cortex-a9 series (rk30xx,
> rk3188, rk2928; but not the other rk29 SoCs, which are Cortex-A8 based).

Hi,

I think you're better off naming them after the first SoC of that series.
Otherwise, if there's an A9-based platform in the future that is not
compatible, things will get complicated.

It's pretty common that we do this for other kinds of devices, so it seems
appropriate for clock drivers too.


-Olof

2013-06-03 07:54:55

by Heiko Stübner

[permalink] [raw]
Subject: Re: [PATCH 09/10] arm: add debug uarts for rockchip rk29xx and rk3xxx series

Am Montag, 3. Juni 2013, 04:08:37 schrieb Arnd Bergmann:
> On Monday 03 June 2013 01:01:03 Heiko St?bner wrote:
> > +#if defined(CONFIG_DEBUG_RK29_UART0)
> > +#define ROCKCHIP_UART_DEBUG_PHYS_BASE 0x20060000
> > +#define ROCKCHIP_UART_DEBUG_VIRT_BASE 0xfe060000
>
> 0xfed60000 maybe? looks like a typo.

ha, correct ... thanks

2013-06-03 08:24:04

by Heiko Stübner

[permalink] [raw]
Subject: Re: [PATCH 10/10] arm: add basic support for Rockchip RK3066a boards

Am Montag, 3. Juni 2013, 04:15:46 schrieb Arnd Bergmann:
> On Monday 03 June 2013 01:02:20 Heiko St?bner wrote:
> > index 0000000..094b37d
> > --- /dev/null
> > +++ b/arch/arm/mach-rockchip/rockchip.c
>
> If all goes well, this file can be removed again in 3.11 since it's
> all generic, but let's add it for now.
>
> > +
> > +static void __init rockchip_timer_init(void)
> > +{
> > + rockchip_init_clocks();
> > + dw_apb_timer_init();
> > +}
>
> Can't you use
>
> of_clk_init(NULL);
> clocksource_of_init();
>
> here and change the two drivers to provide the respective macros?

hmm, while this would make a lot of things easier I don't see right now how
this would work.

The dw_apb_timer clocksource does not have its own device node, but instead
uses two timer devices as clocksource and clockevent.

Hmm ... one idea would be to wrap them in the dt, like

clocksource {
compatible = "snps,dw-apb-clocksource"

timer@2003a000 {
compatible = "snps,dw-apb-timer-osc";
reg = <0x2003a000 0x100>;
interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk_gates1 1>, <&clk_gates7 8>;
clock-names = "timer", "pclk";
};

timer@2000e000 {
compatible = "snps,dw-apb-timer-osc";
reg = <0x2000e000 0x100>;
interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk_gates1 2>, <&clk_gates7 9>;
clock-names = "timer", "pclk";
};
};

> > +
> > +static void __init rockchip_dt_init(void)
> > +{
> > +#ifdef CONFIG_CACHE_L2X0
> > + l2x0_of_init(0, ~0UL);
> > +#endif
> > + of_platform_populate(NULL, of_default_bus_match_table, NULL,
> > NULL); +}
>
> We still need to find a common location to call l2x0_of_init.
>
> > +
> > +static const char * const rockchip_board_dt_compat[] = {
> > + "rockchip,rk2928", /* single core */
> > + "rockchip,rk30xx", /* dual cores */
> > + "rockchip,rk31xx", /* dual and quad cores */
> > + NULL,
> > +};
>
> Please use real numbers instead of wildcards: rockchip,rk3066
> not rockchip,rk30xx.

ok

> > +DT_MACHINE_START(ROCKCHIP_DT, "Rockchip Cortex-A9 (Device Tree)")
> > + .map_io = debug_ll_io_init,
> > + .init_machine = rockchip_dt_init,
> > + .init_time = rockchip_timer_init,
> > + .dt_compat = rockchip_board_dt_compat,
> > +MACHINE_END
>
> The map_io line can already get removed.

Yesterday I did grep thru the linux-next I was using as base looking for the
debug_ll_io_init default I read about but was not able to find it ... most
likely my linux-next is a tad to old.


> What about SMP support? Still working on it?

I haven't even looked into it yet ;-) . But this is one of the next items on
my wishlist ... which also contains making the SoC run at more than 600MHz
(due to the currently read-only pll which starts at this value and needs to be
set)

Reading the "upstream" kernel code to get the necessary informations does not
make this easier ;-)


Heiko

2013-06-03 08:39:42

by Heiko Stübner

[permalink] [raw]
Subject: Re: [PATCH 02/10] clocksource: dw_apb_timer_of: add clock-handling

Am Montag, 3. Juni 2013, 05:22:19 schrieb Baruch Siach:
> Hi Heiko,
>
> On Mon, Jun 03, 2013 at 12:56:37AM +0200, Heiko St?bner wrote:
> > Add the possibility to get the clock-frequency from a timer clock instead
> > of specifying it as dt property. Additionally also add the possibility
> > to also define a controlling periphal clock for the timer block.
> >
> > The clock-frequency property is kept to act as fallback if no clocks
> > are specified.
> >
> > Signed-off-by: Heiko Stuebner <[email protected]>
> > ---
> >
> > Documentation/devicetree/bindings/rtc/dw-apb.txt | 19 ++++++++++++++++
> > drivers/clocksource/dw_apb_timer_of.c | 26
> > +++++++++++++++++++++- 2 files changed, 44 insertions(+), 1
> > deletions(-)
> >
> > diff --git a/Documentation/devicetree/bindings/rtc/dw-apb.txt
> > b/Documentation/devicetree/bindings/rtc/dw-apb.txt index
> > 93e2b0f..80ab1c1 100644
> > --- a/Documentation/devicetree/bindings/rtc/dw-apb.txt
> > +++ b/Documentation/devicetree/bindings/rtc/dw-apb.txt
> >
> > @@ -5,9 +5,20 @@ Required properties:
> > - reg: physical base address of the controller and length of memory
> > mapped
> >
> > region.
> >
> > - interrupts: IRQ line for the timer.
> >
> > +- either clocks+clock-names or clock-frequency properties
> > +
> > +Optional properties:
> > +- clocks : list of clock specifiers, corresponding to entries in
> > + the clock-names property;
> > +- clock-names : should contain "timer and "pclk" entries, matching
> > entries
>
> Missing closing quotes around "timer".

thanks for finding ... will fix in the next run

2013-06-03 08:40:04

by Heiko Stübner

[permalink] [raw]
Subject: Re: [PATCH 08/10] clk: add basic Rockchip rk3066a clock support

Am Montag, 3. Juni 2013, 05:27:11 schrieb Olof Johansson:
> On Mon, Jun 03, 2013 at 01:00:31AM +0200, Heiko St?bner wrote:
> > This adds basic support for clocks on Rockchip rk3066 SoCs.
> > The clock handling thru small dt nodes is heavily inspired by the
> > sunxi clk code.
> >
> > The clock specifiers are named as "x-cortex-a9-x", because the clock
> > semantics are the same thru the whole cortex-a9 series (rk30xx,
> > rk3188, rk2928; but not the other rk29 SoCs, which are Cortex-A8 based).
>
> Hi,
>
> I think you're better off naming them after the first SoC of that series.
> Otherwise, if there's an A9-based platform in the future that is not
> compatible, things will get complicated.
>
> It's pretty common that we do this for other kinds of devices, so it seems
> appropriate for clock drivers too.

ok, I'll rename them

2013-06-03 09:23:44

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 10/10] arm: add basic support for Rockchip RK3066a boards

On Monday 03 June 2013 10:23:49 Heiko St?bner wrote:
> Am Montag, 3. Juni 2013, 04:15:46 schrieb Arnd Bergmann:
> >
> > Can't you use
> >
> > of_clk_init(NULL);
> > clocksource_of_init();
> >
> > here and change the two drivers to provide the respective macros?
>
> hmm, while this would make a lot of things easier I don't see right now how
> this would work.
>
> The dw_apb_timer clocksource does not have its own device node, but instead
> uses two timer devices as clocksource and clockevent.
>
> Hmm ... one idea would be to wrap them in the dt, like
>
> clocksource {
> compatible = "snps,dw-apb-clocksource"
>
> timer@2003a000 {
> compatible = "snps,dw-apb-timer-osc";
> reg = <0x2003a000 0x100>;
> interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
> clocks = <&clk_gates1 1>, <&clk_gates7 8>;
> clock-names = "timer", "pclk";
> };
>
> timer@2000e000 {
> compatible = "snps,dw-apb-timer-osc";
> reg = <0x2000e000 0x100>;
> interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
> clocks = <&clk_gates1 2>, <&clk_gates7 9>;
> clock-names = "timer", "pclk";
> };
> };

Can't you just have multiple CLOCKSOURCE_OF_DECLARE() lines, one
for each of the nodes?

> > > +DT_MACHINE_START(ROCKCHIP_DT, "Rockchip Cortex-A9 (Device Tree)")
> > > + .map_io = debug_ll_io_init,
> > > + .init_machine = rockchip_dt_init,
> > > + .init_time = rockchip_timer_init,
> > > + .dt_compat = rockchip_board_dt_compat,
> > > +MACHINE_END
> >
> > The map_io line can already get removed.
>
> Yesterday I did grep thru the linux-next I was using as base looking for the
> debug_ll_io_init default I read about but was not able to find it ... most
> likely my linux-next is a tad to old.

Yes, it only showed up in today's linux-next. I thought it was older.

Arnd

2013-06-03 09:47:15

by Heiko Stübner

[permalink] [raw]
Subject: Re: [PATCH 10/10] arm: add basic support for Rockchip RK3066a boards

Am Montag, 3. Juni 2013, 11:22:35 schrieb Arnd Bergmann:
> On Monday 03 June 2013 10:23:49 Heiko St?bner wrote:
> > Am Montag, 3. Juni 2013, 04:15:46 schrieb Arnd Bergmann:
> > > Can't you use
> > >
> > > of_clk_init(NULL);
> > > clocksource_of_init();
> > >
> > > here and change the two drivers to provide the respective macros?
> >
> > hmm, while this would make a lot of things easier I don't see right now
> > how this would work.
> >
> > The dw_apb_timer clocksource does not have its own device node, but
> > instead uses two timer devices as clocksource and clockevent.
> >
> > Hmm ... one idea would be to wrap them in the dt, like
> >
> > clocksource {
> >
> > compatible = "snps,dw-apb-clocksource"
> >
> > timer@2003a000 {
> >
> > compatible = "snps,dw-apb-timer-osc";
> > reg = <0x2003a000 0x100>;
> > interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
> > clocks = <&clk_gates1 1>, <&clk_gates7 8>;
> > clock-names = "timer", "pclk";
> >
> > };
> >
> > timer@2000e000 {
> >
> > compatible = "snps,dw-apb-timer-osc";
> > reg = <0x2000e000 0x100>;
> > interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
> > clocks = <&clk_gates1 2>, <&clk_gates7 9>;
> > clock-names = "timer", "pclk";
> >
> > };
> >
> > };
>
> Can't you just have multiple CLOCKSOURCE_OF_DECLARE() lines, one
> for each of the nodes?

The timers are of the same type and the clocksource driver just grabs the
first of them as clockevent and the second as clocksource, so I think two
CLOCKSOURCE_OF_DECLARE lines won't do.

But I just looked at clocksource_of_init a bit more closely, which does a
for_each_matching_node_and_match over the nodes. So the init_func could grab
the device for the clockevent on the first call and the clocksource when it
gets called for the second matching node.


> > > > +DT_MACHINE_START(ROCKCHIP_DT, "Rockchip Cortex-A9 (Device Tree)")
> > > > + .map_io = debug_ll_io_init,
> > > > + .init_machine = rockchip_dt_init,
> > > > + .init_time = rockchip_timer_init,
> > > > + .dt_compat = rockchip_board_dt_compat,
> > > > +MACHINE_END
> > >
> > > The map_io line can already get removed.
> >
> > Yesterday I did grep thru the linux-next I was using as base looking for
> > the debug_ll_io_init default I read about but was not able to find it
> > ... most likely my linux-next is a tad to old.
>
> Yes, it only showed up in today's linux-next. I thought it was older.

2013-06-03 10:26:39

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 10/10] arm: add basic support for Rockchip RK3066a boards

On Monday 03 June 2013 11:46:57 Heiko St?bner wrote:
>
> The timers are of the same type and the clocksource driver just grabs the
> first of them as clockevent and the second as clocksource, so I think two
> CLOCKSOURCE_OF_DECLARE lines won't do.

Ok, got it. I was confused by the fact that the existing two sets of
"compatible" strings have separate sets of strings:

static const struct of_device_id sptimer_ids[] __initconst = {
{ .compatible = "picochip,pc3x2-rtc" },
{ .compatible = "snps,dw-apb-timer-sp" },
{ /* Sentinel */ },
};

static const struct of_device_id osctimer_ids[] __initconst = {
{ .compatible = "picochip,pc3x2-timer" },
{ .compatible = "snps,dw-apb-timer-osc" },
{},
};

and thought they were for clocksource and clockevent respectively,
which is wrong.

> But I just looked at clocksource_of_init a bit more closely, which does a
> for_each_matching_node_and_match over the nodes. So the init_func could grab
> the device for the clockevent on the first call and the clocksource when it
> gets called for the second matching node.

Yes, I think that should work. You just have to be careful about calling
init_sched_clock() only once.

Arnd

2013-06-03 12:15:47

by Heiko Stübner

[permalink] [raw]
Subject: [RFC] dw_apb_timer_of: use clocksource_of_init

dw_apb_timer_init used to search the devicetree for matching timer
devices, making calls to it from board files necessary.

Change the dw_apb_timer_init to work with CLOCKSOURCE_OF_DECLARE.
With this change the function gets called once for each timer node
and tracks these number of calls to attach clockevent and clocksource
devices to the nodes.

Also convert all previous users of dw_apb_timer_init to use
clocksource_of_init.

Tested on the upcoming rk3066 code.

Signed-off-by: Heiko Stuebner <[email protected]>
---
so something like this?

arch/arm/mach-picoxcell/Kconfig | 1 +
arch/arm/mach-picoxcell/common.c | 4 +-
arch/arm/mach-socfpga/Kconfig | 1 +
arch/arm/mach-socfpga/socfpga.c | 4 +-
drivers/clocksource/dw_apb_timer_of.c | 41 ++++++++++++++++-----------------
include/linux/dw_apb_timer.h | 1 -
6 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/arch/arm/mach-picoxcell/Kconfig b/arch/arm/mach-picoxcell/Kconfig
index 13bae78..3ef6bbe 100644
--- a/arch/arm/mach-picoxcell/Kconfig
+++ b/arch/arm/mach-picoxcell/Kconfig
@@ -6,6 +6,7 @@ config ARCH_PICOXCELL
select CPU_V6K
select DW_APB_TIMER
select DW_APB_TIMER_OF
+ select CLKSRC_OF
select GENERIC_CLOCKEVENTS
select HAVE_TCM
select NO_IOPORT
diff --git a/arch/arm/mach-picoxcell/common.c b/arch/arm/mach-picoxcell/common.c
index 70b441a..170f085 100644
--- a/arch/arm/mach-picoxcell/common.c
+++ b/arch/arm/mach-picoxcell/common.c
@@ -15,7 +15,7 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
-#include <linux/dw_apb_timer.h>
+#include <linux/clocksource.h>

#include <asm/mach/arch.h>
#include <asm/mach/map.h>
@@ -88,7 +88,7 @@ DT_MACHINE_START(PICOXCELL, "Picochip picoXcell")
.map_io = picoxcell_map_io,
.nr_irqs = NR_IRQS_LEGACY,
.init_irq = irqchip_init,
- .init_time = dw_apb_timer_init,
+ .init_time = clocksource_of_init,
.init_machine = picoxcell_init_machine,
.dt_compat = picoxcell_dt_match,
.restart = picoxcell_wdt_restart,
diff --git a/arch/arm/mach-socfpga/Kconfig b/arch/arm/mach-socfpga/Kconfig
index 566e804..1c95288 100644
--- a/arch/arm/mach-socfpga/Kconfig
+++ b/arch/arm/mach-socfpga/Kconfig
@@ -9,6 +9,7 @@ config ARCH_SOCFPGA
select CPU_V7
select DW_APB_TIMER
select DW_APB_TIMER_OF
+ select CLKSRC_OF
select GENERIC_CLOCKEVENTS
select GPIO_PL061 if GPIOLIB
select HAVE_ARM_SCU
diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c
index 46a0513..921ff67 100644
--- a/arch/arm/mach-socfpga/socfpga.c
+++ b/arch/arm/mach-socfpga/socfpga.c
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <linux/dw_apb_timer.h>
+#include <linux/clocksource.h>
#include <linux/clk-provider.h>
#include <linux/irqchip.h>
#include <linux/of_address.h>
@@ -120,7 +120,7 @@ DT_MACHINE_START(SOCFPGA, "Altera SOCFPGA")
.smp = smp_ops(socfpga_smp_ops),
.map_io = socfpga_map_io,
.init_irq = socfpga_init_irq,
- .init_time = dw_apb_timer_init,
+ .init_time = clocksource_of_init,
.init_machine = socfpga_cyclone5_init,
.restart = socfpga_cyclone5_restart,
.dt_compat = altera_dt_match,
diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c
index 1964f87..cef5544 100644
--- a/drivers/clocksource/dw_apb_timer_of.c
+++ b/drivers/clocksource/dw_apb_timer_of.c
@@ -133,27 +133,26 @@ static void init_sched_clock(void)
setup_sched_clock(read_sched_clock, 32, sched_rate);
}

-static const struct of_device_id osctimer_ids[] __initconst = {
- { .compatible = "picochip,pc3x2-timer" },
- { .compatible = "snps,dw-apb-timer-osc" },
- {},
-};
-
-void __init dw_apb_timer_init(void)
+static int num_called;
+static void __init dw_apb_timer_init(struct device_node *timer)
{
- struct device_node *event_timer, *source_timer;
-
- event_timer = of_find_matching_node(NULL, osctimer_ids);
- if (!event_timer)
- panic("No timer for clockevent");
- add_clockevent(event_timer);
-
- source_timer = of_find_matching_node(event_timer, osctimer_ids);
- if (!source_timer)
- panic("No timer for clocksource");
- add_clocksource(source_timer);
-
- of_node_put(source_timer);
+ switch (num_called) {
+ case 0:
+ pr_debug("%s: found clockevent timer\n", __func__);
+ add_clockevent(timer);
+ of_node_put(timer);
+ break;
+ case 1:
+ pr_debug("%s: found clocksource timer\n", __func__);
+ add_clocksource(timer);
+ of_node_put(timer);
+ init_sched_clock();
+ break;
+ default:
+ break;
+ }

- init_sched_clock();
+ num_called++;
}
+CLOCKSOURCE_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init);
+CLOCKSOURCE_OF_DECLARE(apb_timer, "snps,dw-apb-timer-osc", dw_apb_timer_init);
diff --git a/include/linux/dw_apb_timer.h b/include/linux/dw_apb_timer.h
index dd755ce..07261d5 100644
--- a/include/linux/dw_apb_timer.h
+++ b/include/linux/dw_apb_timer.h
@@ -53,5 +53,4 @@ void dw_apb_clocksource_start(struct dw_apb_clocksource *dw_cs);
cycle_t dw_apb_clocksource_read(struct dw_apb_clocksource *dw_cs);
void dw_apb_clocksource_unregister(struct dw_apb_clocksource *dw_cs);

-extern void dw_apb_timer_init(void);
#endif /* __DW_APB_TIMER_H__ */
--
1.7.2.3

2013-06-03 12:27:34

by Rob Herring

[permalink] [raw]
Subject: Re: [RFC] dw_apb_timer_of: use clocksource_of_init

On 06/03/2013 07:15 AM, Heiko St?bner wrote:
> dw_apb_timer_init used to search the devicetree for matching timer
> devices, making calls to it from board files necessary.
>
> Change the dw_apb_timer_init to work with CLOCKSOURCE_OF_DECLARE.
> With this change the function gets called once for each timer node
> and tracks these number of calls to attach clockevent and clocksource
> devices to the nodes.
>
> Also convert all previous users of dw_apb_timer_init to use
> clocksource_of_init.
>
> Tested on the upcoming rk3066 code.
>
> Signed-off-by: Heiko Stuebner <[email protected]>

Acked-by: Rob Herring <[email protected]>

But 1 comment:

> @@ -88,7 +88,7 @@ DT_MACHINE_START(PICOXCELL, "Picochip picoXcell")
> .map_io = picoxcell_map_io,
> .nr_irqs = NR_IRQS_LEGACY,
> .init_irq = irqchip_init,
> - .init_time = dw_apb_timer_init,
> + .init_time = clocksource_of_init,

clocksource_of_init is the default now, so you can just remove this and
the one in socfpga.

Same for irqchip_init BTW, but that's another patch.

Rob

2013-06-03 13:20:36

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC] dw_apb_timer_of: use clocksource_of_init

On Monday 03 June 2013 14:15:28 Heiko St?bner wrote:
> dw_apb_timer_init used to search the devicetree for matching timer
> devices, making calls to it from board files necessary.
>
> Change the dw_apb_timer_init to work with CLOCKSOURCE_OF_DECLARE.
> With this change the function gets called once for each timer node
> and tracks these number of calls to attach clockevent and clocksource
> devices to the nodes.
>
> Also convert all previous users of dw_apb_timer_init to use
> clocksource_of_init.
>
> Tested on the upcoming rk3066 code.
>
> Signed-off-by: Heiko Stuebner <[email protected]>

Acked-by: Arnd Bergmann <[email protected]>

2013-06-04 03:58:59

by Jaehoon Chung

[permalink] [raw]
Subject: Re: [PATCH 05/10] mmc: dw_mmc-pltfm: remove static from dw_mci_pltfm_remove

Acked-by: Jaehoon Chung <[email protected]>

On 06/03/2013 07:58 AM, Heiko St?bner wrote:
> dw_mci_pltfm_remove gets exported and used by dw_mmc-exynos, so should
> not be static.
>
> Signed-off-by: Heiko Stuebner <[email protected]>
> ---
> drivers/mmc/host/dw_mmc-pltfm.c | 2 +-
> 1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
> index 37873f1..0048da8 100644
> --- a/drivers/mmc/host/dw_mmc-pltfm.c
> +++ b/drivers/mmc/host/dw_mmc-pltfm.c
> @@ -68,7 +68,7 @@ static int dw_mci_pltfm_probe(struct platform_device *pdev)
> return dw_mci_pltfm_register(pdev, NULL);
> }
>
> -static int dw_mci_pltfm_remove(struct platform_device *pdev)
> +int dw_mci_pltfm_remove(struct platform_device *pdev)
> {
> struct dw_mci *host = platform_get_drvdata(pdev);
>
>

2013-06-04 04:06:28

by Jaehoon Chung

[permalink] [raw]
Subject: Re: [PATCH 06/10] mmc: dw_mmc-pltfm: add Rockchip variant

On 06/03/2013 07:59 AM, Heiko St?bner wrote:
> Cortex-A9 SoCs from Rockchip use a slightly modified variant of dw_mmc
> controllers that seems to require the SDMMC_CMD_USE_HOLD_REG bit to
> always be set.
>
> There also seem to be no other modifications (additional register etc)
> present, so to keep the footprint low, add this small variant to the
> pltfm driver.
>
> Signed-off-by: Heiko Stuebner <[email protected]>
> ---
> drivers/mmc/host/dw_mmc-pltfm.c | 48 +++++++++++++++++++++++++++-----------
> 1 files changed, 34 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
> index 0048da8..7d041b5 100644
> --- a/drivers/mmc/host/dw_mmc-pltfm.c
> +++ b/drivers/mmc/host/dw_mmc-pltfm.c
> @@ -24,6 +24,16 @@
>
> #include "dw_mmc.h"
>
> +
> +static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr)
How about using "dw_mci_pltfm_prepare_command()"?
Maybe HOLD_REG could be used at other SoC.
> +{
> + *cmdr |= SDMMC_CMD_USE_HOLD_REG;
> +}
> +
> +static const struct dw_mci_drv_data rockchip_drv_data = {
> + .prepare_command = dw_mci_rockchip_prepare_command,
> +};
> +
> int dw_mci_pltfm_register(struct platform_device *pdev,
> const struct dw_mci_drv_data *drv_data)
> {
> @@ -63,20 +73,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
> }
> EXPORT_SYMBOL_GPL(dw_mci_pltfm_register);
>
> -static int dw_mci_pltfm_probe(struct platform_device *pdev)
> -{
> - return dw_mci_pltfm_register(pdev, NULL);
> -}
> -
> -int dw_mci_pltfm_remove(struct platform_device *pdev)
> -{
> - struct dw_mci *host = platform_get_drvdata(pdev);
> -
> - dw_mci_remove(host);
> - return 0;
> -}
> -EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove);
> -
> #ifdef CONFIG_PM_SLEEP
> /*
> * TODO: we should probably disable the clock to the card in the suspend path.
> @@ -114,10 +110,34 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops);
>
> static const struct of_device_id dw_mci_pltfm_match[] = {
> { .compatible = "snps,dw-mshc", },
> + { .compatible = "rockchip,cortex-a9-dw-mshc",
> + .data = &rockchip_drv_data },
> {},
> };
> MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
>
> +static int dw_mci_pltfm_probe(struct platform_device *pdev)
> +{
> + const struct dw_mci_drv_data *drv_data = NULL;
> + const struct of_device_id *match;
> +
> + if (pdev->dev.of_node) {
> + match = of_match_node(dw_mci_pltfm_match, pdev->dev.of_node);
> + drv_data = match->data;
> + }
> +
> + return dw_mci_pltfm_register(pdev, drv_data);
> +}
> +
> +int dw_mci_pltfm_remove(struct platform_device *pdev)
> +{
> + struct dw_mci *host = platform_get_drvdata(pdev);
> +
> + dw_mci_remove(host);
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove);
> +
> static struct platform_driver dw_mci_pltfm_driver = {
> .probe = dw_mci_pltfm_probe,
> .remove = dw_mci_pltfm_remove,
>

2013-06-04 06:34:47

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 01/10] clocksource: dw_apb_timer_of: use the clocksource as sched clock if necessary

On Mon, Jun 3, 2013 at 12:56 AM, Heiko St?bner <[email protected]> wrote:

> Currently the dw_apb_timer always expects a separate special timer to be
> availbable for the sched_clock. Some devices using dw_apb_timers do not
> have the sptimer but can use the clocksource as sched_clock.
> Therefore this patch adds using the clocksource timer as
> a fallback if no usable sched timer is found.
>
> Signed-off-by: Heiko Stuebner <[email protected]>

Is this really what the patch does? I mean "ass the clocksourse as
fallback", it seems more like that is controlled from the device tree,
this looks more like some more careful handling of the device tree
input making the sched_timer optional, it doesn't really "add"
anything, does it?

Yours,
Linus Walleij

2013-06-04 07:08:12

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 07/10] pinctrl: add pinctrl driver for Rockchip SoCs

On Mon, Jun 3, 2013 at 12:59 AM, Heiko St?bner <[email protected]> wrote:

> This driver adds support the Cortex-A9 based SoCs from Rockchip,
> so at least the RK2928, RK3066 (a and b) and RK3188.
> Earlier Rockchip SoCs seem to use similar mechanics for gpio
> handling so should be supportable with relative small changes.
> Pull handling on the rk3188 is currently a stub, due to it being
> a bit different to the earlier SoCs.
>
> Pinmuxing as well as gpio (and interrupt-) handling tested on
> a rk3066a based machine.
>
> Signed-off-by: Heiko Stuebner <[email protected]>

Overall this is looking very good, mainly minor comments.

> +++ b/Documentation/devicetree/bindings/pinctrl/rockchip-pinctrl.txt

Please name this beginning with the vendor and similar to
the compatible string: rockchip,pinctrl or something.
(Check the neighbors.)

(...)
> +Required properties for gpio sub nodes:
> + - compatible: "rockchip,gpio-bank"
> + - reg: register of the gpio bank (different than the iomux registerset)
> + - interrupts: base interrupt of the gpio bank in the interrupt controller
> + - clocks: clock that drives this bank
> + - gpio-controller: identifies the node as a gpio controller and pin bank.
> + - #gpio-cells: number of cells in GPIO specifier. Since the generic GPIO
> + binding is used, the amount of cells must be specified as 2. See generic
> + GPIO binding documentation for description of particular cells.
> + - interrupt-controller: identifies the controller node as interrupt-parent.
> + - #interrupt-cells: the value of this property should be 2.
> + - First Cell: represents the external gpio interrupt number local to the
> + external gpio interrupt space of the controller.
> + - Second Cell: flags to identify the type of the interrupt
> + - 1 = rising edge triggered
> + - 2 = falling edge triggered
> + - 3 = rising and falling edge triggered
> + - 4 = high level triggered
> + - 8 = low level triggered

Can't you just reference
Documentation/devicetree/bindings/interrupt-controller/interrupts.txt?

> +Required properties for pin configuration node:
> + - rockchip,pins: 4 integers array, represents a group of pins mux and config
> + setting. The format is rockchip,pins = <PIN_BANK PIN_BANK_NUM MUX CONFIG>.
> + The MUX 0 means gpio and MUX 1 to 3 mean the specific device function
> +
> +Bits used for CONFIG:
> +PULL_AUTO (1 << 0): indicate this pin needs a pull setting for SoCs
> + that determine the pull up or down themselfs

Hm, never saw that before...

(...)
> + uart2 {
> + uart2_xfer: uart2-xfer {
> + rockchip,pins = <RK_GPIO1 8 1 RK_PINCTRL_PULL_AUTO>,
> + <RK_GPIO1 9 1 RK_PINCTRL_PULL_AUTO>;
> + };

This looks like you're using #include to define these constants,
probably you should include that in the example or mention it?

> +uart2: serial@20064000 {
> + compatible = "snps,dw-apb-uart";
> + reg = <0x20064000 0x400>;
> + interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;

Using #defines, nice!

(...)
+++ b/drivers/pinctrl/Kconfig
@@ -158,6 +158,12 @@ config PINCTRL_DB8540
bool "DB8540 pin controller driver"
depends on PINCTRL_NOMADIK && ARCH_U8500

+config PINCTRL_ROCKCHIP
+ bool
+ select PINMUX
+ select PINCONF
+ select GENERIC_IRQ_CHIP

Why is this super-simple pin config thing not using
GENERIC_PINCONF?

I *know* it is simpler to implement your own thing, but think of the
poor maintainers that have to wade through 50 identical implementations.
Do this, it pays off.

BTW: it leads to wanting to use generic pinconf DT bindings as experienced
by Laurent and others. We need to fix that too...

(...)
> +++ b/drivers/pinctrl/pinctrl-rockchip.c
> +static void rockchip_pin_dbg_show(struct pinctrl_dev *pctldev,
> + struct seq_file *s, unsigned offset)
> +{
> + seq_printf(s, "%s", dev_name(pctldev->dev));
> +}

Nothing else you want to say about the pins here?
(No big deal for sure, but....)

> +static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num)
> +{
> + struct rockchip_pinctrl *info = bank->drvdata;
> + void __iomem *reg;
> + int bit;

Is that really an int? I'd guess more like u8...

> + u32 data;
> +
> + /* rk3066b does support any pulls */
> + if (!info->ctrl->pull_offset)
> + return 0;
> +
> + reg = info->reg_base + info->ctrl->pull_offset;
> +
> + if (info->ctrl->pull_auto) {
> + reg += bank->bank_num * 8;

I'd define some constant like

#define RK_BANK_STRIDE 8
reg += bank->bank_num * RK_BANK_STRIDE;
(Since 8 bytes of stride is no natural law.)

And then use that here and elsewhere.

> + reg += (pin_num / 16) * 4;
> + bit = pin_num % 16;

This is clear however.

> +
> + data = readl_relaxed(reg);
> + data >>= bit;
> + data &= 1;
> +
> + return !data;


That's a bit hard to read, I'd just:

#include <linux/bitops.h>
return !(readl_relaxed(reg) & BIT(bit));

And skip the "data" variable. The ! operator will
clamp this to a bool (0/1).

But we all have our habits.

> +static int rockchip_set_pull(struct rockchip_pin_bank *bank,
> + int pin_num, int pull)

Similar comments for this function.

> +static int rockchip_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
> + struct pinctrl_gpio_range *range,
> + unsigned offset, bool input)
(...)
> + /* set bit to 1 for output, 0 for input */
> + if (!input)
> + data |= (1 << pin);
> + else
> + data &= ~(1 << pin);

Here again I would use <linux/bitops.h> and:

data |= BIT(pin);

etc, but it's a matter of taste so if you prefer this, do keep it.

(...)
> +static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
> +{
> + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
> + struct rockchip_pin_bank *bank = gc->private;
> + u32 bit = (1 << d->hwirq);

Name that something like "mask" to match other naming in the
kernel. "bit" sort of implies a number between 0 and 31 and that
is not the case.

All I could think of right now...

Yours,
Linus Walleij

2013-06-04 08:29:53

by Heiko Stübner

[permalink] [raw]
Subject: Re: [PATCH 01/10] clocksource: dw_apb_timer_of: use the clocksource as sched clock if necessary

Am Dienstag, 4. Juni 2013, 08:34:44 schrieb Linus Walleij:
> On Mon, Jun 3, 2013 at 12:56 AM, Heiko St?bner <[email protected]> wrote:
> > Currently the dw_apb_timer always expects a separate special timer to be
> > availbable for the sched_clock. Some devices using dw_apb_timers do not
> > have the sptimer but can use the clocksource as sched_clock.
> > Therefore this patch adds using the clocksource timer as
> > a fallback if no usable sched timer is found.
> >
> > Signed-off-by: Heiko Stuebner <[email protected]>
>
> Is this really what the patch does? I mean "ass the clocksourse as
> fallback", it seems more like that is controlled from the device tree,
> this looks more like some more careful handling of the device tree
> input making the sched_timer optional, it doesn't really "add"
> anything, does it?

Right, the commit message is probably based a bit to much on the before-state
of the driver, always expecting the "sptimer" and panicing if not found.

How about:

Currently the dw_apb_timer always expects a separate special timer to be
availbable for the sched_clock. Some devices using dw_apb_timers do not
have this sptimer but can use the clocksource as sched_clock instead.

Therefore enable the driver to distiguish between devices with and without
sptimer based on the devicetree data and select the correct timer as
sched_clock.

2013-06-04 08:44:14

by Heiko Stübner

[permalink] [raw]
Subject: Re: [PATCH 06/10] mmc: dw_mmc-pltfm: add Rockchip variant

Am Dienstag, 4. Juni 2013, 06:06:39 schrieb Jaehoon Chung:
> On 06/03/2013 07:59 AM, Heiko St?bner wrote:
> > Cortex-A9 SoCs from Rockchip use a slightly modified variant of dw_mmc
> > controllers that seems to require the SDMMC_CMD_USE_HOLD_REG bit to
> > always be set.
> >
> > There also seem to be no other modifications (additional register etc)
> > present, so to keep the footprint low, add this small variant to the
> > pltfm driver.
> >
> > Signed-off-by: Heiko Stuebner <[email protected]>
> > ---
> >
> > drivers/mmc/host/dw_mmc-pltfm.c | 48
> > +++++++++++++++++++++++++++----------- 1 files changed, 34
> > insertions(+), 14 deletions(-)
> >
> > diff --git a/drivers/mmc/host/dw_mmc-pltfm.c
> > b/drivers/mmc/host/dw_mmc-pltfm.c index 0048da8..7d041b5 100644
> > --- a/drivers/mmc/host/dw_mmc-pltfm.c
> > +++ b/drivers/mmc/host/dw_mmc-pltfm.c
> > @@ -24,6 +24,16 @@
> >
> > #include "dw_mmc.h"
> >
> > +
> > +static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32
> > *cmdr)
>
> How about using "dw_mci_pltfm_prepare_command()"?
> Maybe HOLD_REG could be used at other SoC.

The problem I had when thinking about it is that every implementation using
the HOLD_REG stuff does it differently ... on the Exynos variant it depends on
the CLKSEL register value and on the SOCFPGA variant on other clock values.

It's only on the Rockchip variant that it seems to needed all the time.

So, doing it with a "dw_mci_pltfm_prepare_command()" would need a flag to
signal that the implementation needs the HOLD_REG all the time, but we won't
know yet if other implementations will have other constraints on its use -
like only i special cases or such.

So personally I would keep it as it is for now, until more platforms using the
HOLD_REG come along to see some sort of pattern of its use?

2013-06-04 09:44:05

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 01/10] clocksource: dw_apb_timer_of: use the clocksource as sched clock if necessary

On Tue, Jun 4, 2013 at 10:29 AM, Heiko St?bner <[email protected]> wrote:

> How about:
>
> Currently the dw_apb_timer always expects a separate special timer to be
> availbable for the sched_clock. Some devices using dw_apb_timers do not
> have this sptimer but can use the clocksource as sched_clock instead.
>
> Therefore enable the driver to distiguish between devices with and without
> sptimer based on the devicetree data and select the correct timer as
> sched_clock.

Allright, Acked-by: Linus Walleij <[email protected]>

Yours,
Linus Walleij

2013-06-04 12:05:33

by Heiko Stübner

[permalink] [raw]
Subject: Re: [PATCH 07/10] pinctrl: add pinctrl driver for Rockchip SoCs

Hi,

I'll just skip over the "right, will fix that" issues and just address the
unclear ones.

Am Dienstag, 4. Juni 2013, 09:08:09 schrieb Linus Walleij:
> On Mon, Jun 3, 2013 at 12:59 AM, Heiko St?bner <[email protected]> wrote:
> > This driver adds support the Cortex-A9 based SoCs from Rockchip,
> > so at least the RK2928, RK3066 (a and b) and RK3188.
> > Earlier Rockchip SoCs seem to use similar mechanics for gpio
> > handling so should be supportable with relative small changes.
> > Pull handling on the rk3188 is currently a stub, due to it being
> > a bit different to the earlier SoCs.
> >
> > Pinmuxing as well as gpio (and interrupt-) handling tested on
> > a rk3066a based machine.
> >
> > Signed-off-by: Heiko Stuebner <[email protected]>
>
> Overall this is looking very good, mainly minor comments.

> > +Required properties for pin configuration node:
> > + - rockchip,pins: 4 integers array, represents a group of pins mux and
> > config + setting. The format is rockchip,pins = <PIN_BANK
> > PIN_BANK_NUM MUX CONFIG>. + The MUX 0 means gpio and MUX 1 to 3 mean
> > the specific device function +
> > +Bits used for CONFIG:
> > +PULL_AUTO (1 << 0): indicate this pin needs a pull setting for SoCs
> > + that determine the pull up or down themselfs
>
> Hm, never saw that before...

Citing the original gpio driver:

/*
* Values written to this register independently
* control Pullup/Pulldown or not for the
* corresponding data bit in GPIO.
* 0: pull up/down enable, PAD type will decide
* to be up or down, not related with this value
* 1: pull up/down disable
*/

So if it's a pull up or down is decided based on the mux of the pin. Calling
everything a "pull down" (or up) when it isn't seemed somehow wrong to me.

The rk3188 on the other hand supports both pull up and down separately.

Or should this be selected as PULL_UP | PULL_DOWN in the config?


> > +uart2: serial@20064000 {
> > + compatible = "snps,dw-apb-uart";
> > + reg = <0x20064000 0x400>;
> > + interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
>
> Using #defines, nice!

everything for bonus-points ;-)

And actually it's definitly easier on me as well, as I don't have to remember
what each integer value means.

> +++ b/drivers/pinctrl/Kconfig
> @@ -158,6 +158,12 @@ config PINCTRL_DB8540
> bool "DB8540 pin controller driver"
> depends on PINCTRL_NOMADIK && ARCH_U8500
>
> +config PINCTRL_ROCKCHIP
> + bool
> + select PINMUX
> + select PINCONF
> + select GENERIC_IRQ_CHIP
>
> Why is this super-simple pin config thing not using
> GENERIC_PINCONF?
>
> I *know* it is simpler to implement your own thing, but think of the
> poor maintainers that have to wade through 50 identical implementations.
> Do this, it pays off.

generic pinconf sounds interesting ... will give it a try.

The only problem is the pull stuff mentioned above that is either pull up or
down without the driver having knowledge about it. And generic_pinconf only
knows about them separately right now.


> BTW: it leads to wanting to use generic pinconf DT bindings as experienced
> by Laurent and others. We need to fix that too...
>
> (...)
>
> > +++ b/drivers/pinctrl/pinctrl-rockchip.c
> > +static void rockchip_pin_dbg_show(struct pinctrl_dev *pctldev,
> > + struct seq_file *s, unsigned
> > offset) +{
> > + seq_printf(s, "%s", dev_name(pctldev->dev));
> > +}
>
> Nothing else you want to say about the pins here?
> (No big deal for sure, but....)

when using pinconfig_generic, its dump_pin function should be more talkative
right?


> > +
> > + data = readl_relaxed(reg);
> > + data >>= bit;
> > + data &= 1;
> > +
> > + return !data;
>
> That's a bit hard to read, I'd just:
>
> #include <linux/bitops.h>
> return !(readl_relaxed(reg) & BIT(bit));
>
> And skip the "data" variable. The ! operator will
> clamp this to a bool (0/1).
>
> But we all have our habits.

yeah, but sometimes it's also good to try to break them ... your solution is
much nicer to read (and shorter).


> All I could think of right now...

Thanks for the review
Heiko

2013-06-05 07:01:47

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 07/10] pinctrl: add pinctrl driver for Rockchip SoCs

On Tue, Jun 4, 2013 at 2:05 PM, Heiko St?bner <[email protected]> wrote:

>> > +PULL_AUTO (1 << 0): indicate this pin needs a pull setting for SoCs
>> > + that determine the pull up or down themselfs
>>
>> Hm, never saw that before...
>
> Citing the original gpio driver:
>
> /*
> * Values written to this register independently
> * control Pullup/Pulldown or not for the
> * corresponding data bit in GPIO.
> * 0: pull up/down enable, PAD type will decide
> * to be up or down, not related with this value
> * 1: pull up/down disable
> */
>
> So if it's a pull up or down is decided based on the mux of the pin. Calling
> everything a "pull down" (or up) when it isn't seemed somehow wrong to me.
>
> The rk3188 on the other hand supports both pull up and down separately.
>
> Or should this be selected as PULL_UP | PULL_DOWN in the config?

The generic config is pretty much either/or so it'd be a new
config for that approach.

Basically it seems they have embedded knowledge into the
silicon: there is no specific rule as to whether a pad should be
pulled up or down depending on "pad type" as is stated, rather
it's so that if you know a pad will be used for I2C SCL then
you know it needs pull-up. Probably something like a "1" on
some constant switch in VHDL/Verilog is hard-coded into
the silicon turning on pull-up if I2C is selected and the
autopull is set for example.

>> +config PINCTRL_ROCKCHIP
>> + bool
>> + select PINMUX
>> + select PINCONF
>> + select GENERIC_IRQ_CHIP
>>
>> Why is this super-simple pin config thing not using
>> GENERIC_PINCONF?
>>
>> I *know* it is simpler to implement your own thing, but think of the
>> poor maintainers that have to wade through 50 identical implementations.
>> Do this, it pays off.
>
> generic pinconf sounds interesting ... will give it a try.
>
> The only problem is the pull stuff mentioned above that is either pull up or
> down without the driver having knowledge about it. And generic_pinconf only
> knows about them separately right now.

Create a separate patch adding PIN_CONFIG_BIAS_PULL_AUTO
to include/linux/pinctrl/pinconf-generic.h, don't forget the
kerneldoc, and patching drivers/pinctrl/pinconf-generic.c.

I'll apply it right away.

>> Nothing else you want to say about the pins here?
>> (No big deal for sure, but....)
>
> when using pinconfig_generic, its dump_pin function should be more talkative
> right?

Yes, one of the things you get for free... soon also the pin config
DT parser will be for free I hope.

Yours,
Linus Walleij

2013-06-05 07:11:26

by Thomas Petazzoni

[permalink] [raw]
Subject: Re: [PATCH 10/10] arm: add basic support for Rockchip RK3066a boards

Dear Heiko Stübner,

On Mon, 3 Jun 2013 01:02:20 +0200, Heiko Stübner wrote:
> index 0000000..a2d8c70
> --- /dev/null
> +++ b/arch/arm/mach-rockchip/Makefile.boot
> @@ -0,0 +1,3 @@
> +zreladdr-$(CONFIG_ARCH_ROCKCHIP) := 0x60408000
> +params_phys-$(CONFIG_ARCH_ROCKCHIP) := 0x60088000
> +initrd_phys-$(CONFIG_ARCH_ROCKCHIP) := 0x60800000

Thanks to the AUTO_ZRELADDR thing that you're using as part of the
MULTIPLATFORM support, this Makefile.boot file is no longer useful. See
mach-socfpga, mach-mvebu, mach-bcm2835, mach-bcm, mach-highbank, etc.

Cc'ing Maxime Ripard, since I see that mach-sunxi does have a
Makefile.boot, even though I believe it is not needed.

Best regards,

Thomas
--
Thomas Petazzoni, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

2013-06-05 14:00:33

by Seungwon Jeon

[permalink] [raw]
Subject: RE: [PATCH 05/10] mmc: dw_mmc-pltfm: remove static from dw_mci_pltfm_remove

On 06/03/13 2013 7:58 AM, Heiko Stübner wrote:
> dw_mci_pltfm_remove gets exported and used by dw_mmc-exynos, so should
> not be static.
>
> Signed-off-by: Heiko Stuebner <[email protected]>

Acked-by: Seungwon Jeon <[email protected]>

Thanks,
Seungwon Jeon

2013-06-05 14:00:48

by Seungwon Jeon

[permalink] [raw]
Subject: RE: [PATCH 06/10] mmc: dw_mmc-pltfm: add Rockchip variant

On 06/03/13 7:59 AM, Heiko Stübner wrote:
> Cortex-A9 SoCs from Rockchip use a slightly modified variant of dw_mmc
> controllers that seems to require the SDMMC_CMD_USE_HOLD_REG bit to
> always be set.
>
> There also seem to be no other modifications (additional register etc)
> present, so to keep the footprint low, add this small variant to the
> pltfm driver.
>
> Signed-off-by: Heiko Stuebner <[email protected]>
> ---
> drivers/mmc/host/dw_mmc-pltfm.c | 48 +++++++++++++++++++++++++++-----------
> 1 files changed, 34 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
> index 0048da8..7d041b5 100644
> --- a/drivers/mmc/host/dw_mmc-pltfm.c
> +++ b/drivers/mmc/host/dw_mmc-pltfm.c
> @@ -24,6 +24,16 @@
>
> #include "dw_mmc.h"
>
> +
> +static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr)
> +{
> + *cmdr |= SDMMC_CMD_USE_HOLD_REG;
Currently, SDMMC_CMD_USE_HOLD_REG is defined in dw_mmc-exynos.c
It should be moved to dw_mmc.h

Thanks,
Seungwon Jeon
> +}
> +
> +static const struct dw_mci_drv_data rockchip_drv_data = {
> + .prepare_command = dw_mci_rockchip_prepare_command,
> +};
> +
> int dw_mci_pltfm_register(struct platform_device *pdev,
> const struct dw_mci_drv_data *drv_data)
> {
> @@ -63,20 +73,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
> }
> EXPORT_SYMBOL_GPL(dw_mci_pltfm_register);
>
> -static int dw_mci_pltfm_probe(struct platform_device *pdev)
> -{
> - return dw_mci_pltfm_register(pdev, NULL);
> -}
> -
> -int dw_mci_pltfm_remove(struct platform_device *pdev)
> -{
> - struct dw_mci *host = platform_get_drvdata(pdev);
> -
> - dw_mci_remove(host);
> - return 0;
> -}
> -EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove);
> -
> #ifdef CONFIG_PM_SLEEP
> /*
> * TODO: we should probably disable the clock to the card in the suspend path.
> @@ -114,10 +110,34 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops);
>
> static const struct of_device_id dw_mci_pltfm_match[] = {
> { .compatible = "snps,dw-mshc", },
> + { .compatible = "rockchip,cortex-a9-dw-mshc",
> + .data = &rockchip_drv_data },
> {},
> };
> MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
>
> +static int dw_mci_pltfm_probe(struct platform_device *pdev)
> +{
> + const struct dw_mci_drv_data *drv_data = NULL;
> + const struct of_device_id *match;
> +
> + if (pdev->dev.of_node) {
> + match = of_match_node(dw_mci_pltfm_match, pdev->dev.of_node);
> + drv_data = match->data;
> + }
> +
> + return dw_mci_pltfm_register(pdev, drv_data);
> +}
> +
> +int dw_mci_pltfm_remove(struct platform_device *pdev)
> +{
> + struct dw_mci *host = platform_get_drvdata(pdev);
> +
> + dw_mci_remove(host);
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove);
> +
> static struct platform_driver dw_mci_pltfm_driver = {
> .probe = dw_mci_pltfm_probe,
> .remove = dw_mci_pltfm_remove,
> --
> 1.7.2.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-06-05 14:11:35

by Heiko Stübner

[permalink] [raw]
Subject: Re: [PATCH 06/10] mmc: dw_mmc-pltfm: add Rockchip variant

Am Mittwoch, 5. Juni 2013, 16:00:43 schrieb Seungwon Jeon:
> On 06/03/13 7:59 AM, Heiko Stübner wrote:
> > Cortex-A9 SoCs from Rockchip use a slightly modified variant of dw_mmc
> > controllers that seems to require the SDMMC_CMD_USE_HOLD_REG bit to
> > always be set.
> >
> > There also seem to be no other modifications (additional register etc)
> > present, so to keep the footprint low, add this small variant to the
> > pltfm driver.
> >
> > Signed-off-by: Heiko Stuebner <[email protected]>
> > ---
> >
> > drivers/mmc/host/dw_mmc-pltfm.c | 48
> > +++++++++++++++++++++++++++----------- 1 files changed, 34
> > insertions(+), 14 deletions(-)
> >
> > diff --git a/drivers/mmc/host/dw_mmc-pltfm.c
> > b/drivers/mmc/host/dw_mmc-pltfm.c index 0048da8..7d041b5 100644
> > --- a/drivers/mmc/host/dw_mmc-pltfm.c
> > +++ b/drivers/mmc/host/dw_mmc-pltfm.c
> > @@ -24,6 +24,16 @@
> >
> > #include "dw_mmc.h"
> >
> > +
> > +static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32
> > *cmdr) +{
> > + *cmdr |= SDMMC_CMD_USE_HOLD_REG;
>
> Currently, SDMMC_CMD_USE_HOLD_REG is defined in dw_mmc-exynos.c
> It should be moved to dw_mmc.h

It's already done in another patch "mmc: dw_mmc: Add support DW SD/MMC driver
on SOCFPGA" from Dinh Nguyen <[email protected]>, which I therefore named as
dependency in the cover letter.

Don't know if it's better to also include the move here again and let the
merging person sort out the conflict?

2013-06-05 17:18:56

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 07/10] pinctrl: add pinctrl driver for Rockchip SoCs

On 06/05/2013 01:01 AM, Linus Walleij wrote:
> On Tue, Jun 4, 2013 at 2:05 PM, Heiko St?bner <[email protected]> wrote:
...
>> The only problem is the pull stuff mentioned above that is either pull up or
>> down without the driver having knowledge about it. And generic_pinconf only
>> knows about them separately right now.
>
> Create a separate patch adding PIN_CONFIG_BIAS_PULL_AUTO
> to include/linux/pinctrl/pinconf-generic.h, don't forget the
> kerneldoc, and patching drivers/pinctrl/pinconf-generic.c.

"AUTO" sounds really rather generic. Based on just the word "AUTO", I
have no idea if it's a HW- or SW-supplied default, or what the algorithm
is for determining the automatically selected value. Perhaps
s/AUTO/PIN_DEFAULT/ or something like that?

While the concept is simple enough, it's unusual enough that such a
patch would hopefully have a comment containing a full explanation of
exactly what this option means.

2013-06-05 19:39:34

by Heiko Stübner

[permalink] [raw]
Subject: Re: [PATCH 07/10] pinctrl: add pinctrl driver for Rockchip SoCs

Am Mittwoch, 5. Juni 2013, 19:18:49 schrieb Stephen Warren:
> On 06/05/2013 01:01 AM, Linus Walleij wrote:
> > On Tue, Jun 4, 2013 at 2:05 PM, Heiko St?bner <[email protected]> wrote:
> ...
>
> >> The only problem is the pull stuff mentioned above that is either pull
> >> up or down without the driver having knowledge about it. And
> >> generic_pinconf only knows about them separately right now.
> >
> > Create a separate patch adding PIN_CONFIG_BIAS_PULL_AUTO
> > to include/linux/pinctrl/pinconf-generic.h, don't forget the
> > kerneldoc, and patching drivers/pinctrl/pinconf-generic.c.
>
> "AUTO" sounds really rather generic. Based on just the word "AUTO", I
> have no idea if it's a HW- or SW-supplied default, or what the algorithm
> is for determining the automatically selected value. Perhaps
> s/AUTO/PIN_DEFAULT/ or something like that?

I would also go with PIN_DEFAULT, which somehow captures what the function
does better than "AUTO". So, if no-one objects I'll go with Stephen's
suggestion.


> While the concept is simple enough, it's unusual enough that such a
> patch would hopefully have a comment containing a full explanation of
> exactly what this option means.

of course :-)

2013-06-05 21:45:26

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 10/10] arm: add basic support for Rockchip RK3066a boards

Hi Thomas,

On Wed, Jun 05, 2013 at 09:11:19AM +0200, Thomas Petazzoni wrote:
> Dear Heiko St?bner,
>
> On Mon, 3 Jun 2013 01:02:20 +0200, Heiko St?bner wrote:
> > index 0000000..a2d8c70
> > --- /dev/null
> > +++ b/arch/arm/mach-rockchip/Makefile.boot
> > @@ -0,0 +1,3 @@
> > +zreladdr-$(CONFIG_ARCH_ROCKCHIP) := 0x60408000
> > +params_phys-$(CONFIG_ARCH_ROCKCHIP) := 0x60088000
> > +initrd_phys-$(CONFIG_ARCH_ROCKCHIP) := 0x60800000
>
> Thanks to the AUTO_ZRELADDR thing that you're using as part of the
> MULTIPLATFORM support, this Makefile.boot file is no longer useful. See
> mach-socfpga, mach-mvebu, mach-bcm2835, mach-bcm, mach-highbank, etc.
>
> Cc'ing Maxime Ripard, since I see that mach-sunxi does have a
> Makefile.boot, even though I believe it is not needed.

Yes, it is a left-over and should be removed obviously.

Thanks for reminding it to me :)

Maxime

2013-06-06 20:01:24

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH 06/10] mmc: dw_mmc-pltfm: add Rockchip variant

On Mon, Jun 3, 2013 at 1:59 AM, Heiko Stübner <[email protected]> wrote:
> Cortex-A9 SoCs from Rockchip use a slightly modified variant of dw_mmc
> controllers that seems to require the SDMMC_CMD_USE_HOLD_REG bit to
> always be set.
>
> There also seem to be no other modifications (additional register etc)
> present, so to keep the footprint low, add this small variant to the
> pltfm driver.

Few comments below.

> --- a/drivers/mmc/host/dw_mmc-pltfm.c
> +++ b/drivers/mmc/host/dw_mmc-pltfm.c
> @@ -24,6 +24,16 @@
>
> #include "dw_mmc.h"
>
> +

No need to add extra empty line here.

> +static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr)

> @@ -63,20 +73,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
> }
> EXPORT_SYMBOL_GPL(dw_mci_pltfm_register);
>
> -static int dw_mci_pltfm_probe(struct platform_device *pdev)
> -{
> - return dw_mci_pltfm_register(pdev, NULL);
> -}
> -
> -int dw_mci_pltfm_remove(struct platform_device *pdev)
> -{
> - struct dw_mci *host = platform_get_drvdata(pdev);
> -
> - dw_mci_remove(host);
> - return 0;
> -}
> -EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove);
> -

If you want to move those through code, please do separate patch.

--
With Best Regards,
Andy Shevchenko