diff options
author | Zoltan Herpai <wigyori@uid0.hu> | 2014-08-27 12:09:46 +0000 |
---|---|---|
committer | Zoltan Herpai <wigyori@uid0.hu> | 2014-08-27 12:09:46 +0000 |
commit | f10f009609aac0941d750ae3a1106675e6d6656a (patch) | |
tree | 70f62106f833dc3b4882118e914ff183440e1e9d /target/linux/sunxi/patches-3.14 | |
parent | b3d10005c1ce3b9676580d09001e14458e410f89 (diff) | |
download | upstream-f10f009609aac0941d750ae3a1106675e6d6656a.tar.gz upstream-f10f009609aac0941d750ae3a1106675e6d6656a.tar.bz2 upstream-f10f009609aac0941d750ae3a1106675e6d6656a.zip |
sunxi: initial 3.14 patchset
Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
SVN-Revision: 42313
Diffstat (limited to 'target/linux/sunxi/patches-3.14')
110 files changed, 18419 insertions, 0 deletions
diff --git a/target/linux/sunxi/patches-3.14/100-dt-sun4i-add-missing-serial-aliases.patch b/target/linux/sunxi/patches-3.14/100-dt-sun4i-add-missing-serial-aliases.patch new file mode 100644 index 0000000000..5048f8500a --- /dev/null +++ b/target/linux/sunxi/patches-3.14/100-dt-sun4i-add-missing-serial-aliases.patch @@ -0,0 +1,33 @@ +From b25983b215e889447ae670a158b1af5e7f253091 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Thu, 2 Jan 2014 22:05:04 +0100 +Subject: [PATCH] ARM: sun4i: a10: Add missing serial aliases + +Some UART aliases have been defined, but not all of them. Add the remaining +ones to be consistent and to ease the parsing of the DT by the bootloaders. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun4i-a10.dtsi | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi +index d4d2763..64fc716 100644 +--- a/arch/arm/boot/dts/sun4i-a10.dtsi ++++ b/arch/arm/boot/dts/sun4i-a10.dtsi +@@ -19,6 +19,12 @@ + ethernet0 = &emac; + serial0 = &uart0; + serial1 = &uart1; ++ serial2 = &uart2; ++ serial3 = &uart3; ++ serial4 = &uart4; ++ serial5 = &uart5; ++ serial6 = &uart6; ++ serial7 = &uart7; + }; + + cpus { +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/101-dt-sun6i-add-missing-serial-aliases.patch b/target/linux/sunxi/patches-3.14/101-dt-sun6i-add-missing-serial-aliases.patch new file mode 100644 index 0000000000..e2ba3c19ea --- /dev/null +++ b/target/linux/sunxi/patches-3.14/101-dt-sun6i-add-missing-serial-aliases.patch @@ -0,0 +1,37 @@ +From b6e3460ee25b8af673d00df0435a304f5dbf7c40 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Thu, 2 Jan 2014 22:05:04 +0100 +Subject: [PATCH] ARM: sun6i: Add missing serial aliases + +Some UART aliases have been defined, but not all of them. Add the remaining +ones to be consistent and to ease the parsing of the DT by the bootloaders. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun6i-a31.dtsi | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi +index 5256ad9..092bf97 100644 +--- a/arch/arm/boot/dts/sun6i-a31.dtsi ++++ b/arch/arm/boot/dts/sun6i-a31.dtsi +@@ -16,6 +16,16 @@ + / { + interrupt-parent = <&gic>; + ++ aliases { ++ serial0 = &uart0; ++ serial1 = &uart1; ++ serial2 = &uart2; ++ serial3 = &uart3; ++ serial4 = &uart4; ++ serial5 = &uart5; ++ }; ++ ++ + cpus { + #address-cells = <1>; + #size-cells = <0>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/102-dt-sun7i-add-missing-serial-aliases.patch b/target/linux/sunxi/patches-3.14/102-dt-sun7i-add-missing-serial-aliases.patch new file mode 100644 index 0000000000..a979036411 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/102-dt-sun7i-add-missing-serial-aliases.patch @@ -0,0 +1,35 @@ +From 1070f51b55d11e8831317d718acf2af46a181311 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Thu, 2 Jan 2014 22:05:04 +0100 +Subject: [PATCH] ARM: sun7i: Add missing serial aliases + +Some UART aliases have been defined, but not all of them. Add the remaining +ones to be consistent and to ease the parsing of the DT by the bootloaders. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun7i-a20.dtsi | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 6f25cf5..0b6e610 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -18,6 +18,14 @@ + + aliases { + ethernet0 = &emac; ++ serial0 = &uart0; ++ serial1 = &uart1; ++ serial2 = &uart2; ++ serial3 = &uart3; ++ serial4 = &uart4; ++ serial5 = &uart5; ++ serial6 = &uart6; ++ serial7 = &uart7; + }; + + cpus { +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/103-dt-sun5i-add-missing-serial-aliases.patch b/target/linux/sunxi/patches-3.14/103-dt-sun5i-add-missing-serial-aliases.patch new file mode 100644 index 0000000000..c262dfe99c --- /dev/null +++ b/target/linux/sunxi/patches-3.14/103-dt-sun5i-add-missing-serial-aliases.patch @@ -0,0 +1,32 @@ +From 0ffac463caf4b0022cf0d917552d51b3ec82849a Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Mon, 13 Jan 2014 11:08:47 +0100 +Subject: [PATCH] ARM: sun5i: a13: Add missing serial aliases + +Some UART aliases have been defined, but not all of them. Add the remaining +ones to be consistent and to ease the parsing of the DT by the bootloaders. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun5i-a13.dtsi | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi +index c463fd7..d8207b0 100644 +--- a/arch/arm/boot/dts/sun5i-a13.dtsi ++++ b/arch/arm/boot/dts/sun5i-a13.dtsi +@@ -16,6 +16,11 @@ + / { + interrupt-parent = <&intc>; + ++ aliases { ++ serial0 = &uart1; ++ serial1 = &uart3; ++ }; ++ + cpus { + #address-cells = <1>; + #size-cells = <0>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/104-dt-sun7i-add-pinmuxing-for-uart2.patch b/target/linux/sunxi/patches-3.14/104-dt-sun7i-add-pinmuxing-for-uart2.patch new file mode 100644 index 0000000000..cd343285e3 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/104-dt-sun7i-add-pinmuxing-for-uart2.patch @@ -0,0 +1,35 @@ +From 875f2d579db0c3ea113f6367af9ccb32b3dbebcc Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Tue, 14 Jan 2014 22:49:50 +0800 +Subject: [PATCH] ARM: dts: sun7i: add pin muxing options for UART2 + +UART2 is used on CubieTruck to connect to the Bluetooth module. +Add the pin set used in this case. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun7i-a20.dtsi | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 0b6e610..2139e0f 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -381,6 +381,13 @@ + allwinner,pull = <0>; + }; + ++ uart2_pins_a: uart2@0 { ++ allwinner,pins = "PI16", "PI17", "PI18", "PI19"; ++ allwinner,function = "uart2"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ + uart6_pins_a: uart6@0 { + allwinner,pins = "PI12", "PI13"; + allwinner,function = "uart6"; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/105-dt-sun4i-add-linksprite-pcduino.patch b/target/linux/sunxi/patches-3.14/105-dt-sun4i-add-linksprite-pcduino.patch new file mode 100644 index 0000000000..713c922501 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/105-dt-sun4i-add-linksprite-pcduino.patch @@ -0,0 +1,86 @@ +From 2fc6dcd5e7c76c203d2e174e70ef21393a231163 Mon Sep 17 00:00:00 2001 +From: Zoltan HERPAI <wigyori@uid0.hu> +Date: Mon, 13 Jan 2014 14:15:01 +0100 +Subject: [PATCH] ARM: sun4i: dt: Add basic board support for LinkSprite + pcDuino + +This patch will add a basic board support DT for the +LinkSprite pcDuino board. + +Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu> +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/Makefile | 1 + + arch/arm/boot/dts/sun4i-a10-pcduino.dts | 48 +++++++++++++++++++++++++++++++++ + 2 files changed, 49 insertions(+) + create mode 100644 arch/arm/boot/dts/sun4i-a10-pcduino.dts + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index 0320303..8e1d636 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -284,6 +284,7 @@ dtb-$(CONFIG_ARCH_SUNXI) += \ + sun4i-a10-cubieboard.dtb \ + sun4i-a10-mini-xplus.dtb \ + sun4i-a10-hackberry.dtb \ ++ sun4i-a10-pcduino.dtb \ + sun5i-a10s-olinuxino-micro.dtb \ + sun5i-a13-olinuxino.dtb \ + sun5i-a13-olinuxino-micro.dtb \ +diff --git a/arch/arm/boot/dts/sun4i-a10-pcduino.dts b/arch/arm/boot/dts/sun4i-a10-pcduino.dts +new file mode 100644 +index 0000000..f5692a3 +--- /dev/null ++++ b/arch/arm/boot/dts/sun4i-a10-pcduino.dts +@@ -0,0 +1,48 @@ ++/* ++ * Copyright 2014 Zoltan HERPAI ++ * Zoltan HERPAI <wigyori@uid0.hu> ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/dts-v1/; ++/include/ "sun4i-a10.dtsi" ++ ++/ { ++ model = "LinkSprite pcDuino"; ++ compatible = "linksprite,a10-pcduino", "allwinner,sun4i-a10"; ++ ++ soc@01c00000 { ++ emac: ethernet@01c0b000 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&emac_pins_a>; ++ phy = <&phy1>; ++ status = "okay"; ++ }; ++ ++ mdio@01c0b080 { ++ status = "okay"; ++ ++ phy1: ethernet-phy@1 { ++ reg = <1>; ++ }; ++ }; ++ ++ uart0: serial@01c28000 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart0_pins_a>; ++ status = "okay"; ++ }; ++ ++ i2c0: i2c@01c2ac00 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c0_pins_a>; ++ status = "okay"; ++ }; ++ }; ++}; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/106-dt-sun7i-add-arch-timer-node.patch b/target/linux/sunxi/patches-3.14/106-dt-sun7i-add-arch-timer-node.patch new file mode 100644 index 0000000000..4ded898bbf --- /dev/null +++ b/target/linux/sunxi/patches-3.14/106-dt-sun7i-add-arch-timer-node.patch @@ -0,0 +1,36 @@ +From 0f9bb2cf6171f47d932df46e34cc5cfce384ff3d Mon Sep 17 00:00:00 2001 +From: Marc Zyngier <marc.zyngier@arm.com> +Date: Tue, 18 Feb 2014 14:04:44 +0000 +Subject: [PATCH] ARM: sun7i: add arch timer node + +The Allwinner A20 SoC is built around a pair of Cortex-A7 cores, +which have the usual generic timers. Report this in the DT. + +Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun7i-a20.dtsi | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 822a816..911d4e4 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -49,6 +49,14 @@ + reg = <0x40000000 0x80000000>; + }; + ++ timer { ++ compatible = "arm,armv7-timer"; ++ interrupts = <1 13 0xf08>, ++ <1 14 0xf08>, ++ <1 11 0xf08>, ++ <1 10 0xf08>; ++ }; ++ + clocks { + #address-cells = <1>; + #size-cells = <1>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/107-dt-sun4i-add-a10-lime.patch b/target/linux/sunxi/patches-3.14/107-dt-sun4i-add-a10-lime.patch new file mode 100644 index 0000000000..c6f9edbf18 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/107-dt-sun4i-add-a10-lime.patch @@ -0,0 +1,149 @@ +From a1c70ed831e4d5356618834202b0da3aa34e218e Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Fri, 10 Jan 2014 23:23:06 +0100 +Subject: [PATCH] ARM: sun4i: dt: Add support for the A10-OLinuXino-LIME board + +This add support for the A10-OLinuXino-LIME: +https://www.olimex.com/Products/OLinuXino/A10/A10-OLinuXino-LIME + +A low cost Allwinner A10 based dev-board, with sata, ethernet, hdmi and 2x USB. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/Makefile | 1 + + arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts | 111 +++++++++++++++++++++++++ + 2 files changed, 112 insertions(+) + create mode 100644 arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index 8e1d636..66d6df3 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -284,6 +284,7 @@ dtb-$(CONFIG_ARCH_SUNXI) += \ + sun4i-a10-cubieboard.dtb \ + sun4i-a10-mini-xplus.dtb \ + sun4i-a10-hackberry.dtb \ ++ sun4i-a10-olinuxino-lime.dtb \ + sun4i-a10-pcduino.dtb \ + sun5i-a10s-olinuxino-micro.dtb \ + sun5i-a13-olinuxino.dtb \ +diff --git a/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts b/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts +new file mode 100644 +index 0000000..66cf0c7 +--- /dev/null ++++ b/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts +@@ -0,0 +1,111 @@ ++/* ++ * Copyright 2014 - Hans de Goede <hdegoede@redhat.com> ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/dts-v1/; ++/include/ "sun4i-a10.dtsi" ++/include/ "sunxi-common-regulators.dtsi" ++ ++/ { ++ model = "Olimex A10-OLinuXino-LIME"; ++ compatible = "olimex,a10-olinuxino-lime", "allwinner,sun4i-a10"; ++ ++ soc@01c00000 { ++ emac: ethernet@01c0b000 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&emac_pins_a>; ++ phy = <&phy1>; ++ status = "okay"; ++ }; ++ ++ mdio@01c0b080 { ++ status = "okay"; ++ ++ phy1: ethernet-phy@1 { ++ reg = <1>; ++ }; ++ }; ++ ++ usbphy: phy@01c13400 { ++ usb1_vbus-supply = <®_usb1_vbus>; ++ usb2_vbus-supply = <®_usb2_vbus>; ++ status = "okay"; ++ }; ++ ++ ehci0: usb@01c14000 { ++ status = "okay"; ++ }; ++ ++ ohci0: usb@01c14400 { ++ status = "okay"; ++ }; ++ ++ ahci: sata@01c18000 { ++ target-supply = <®_ahci_5v>; ++ status = "okay"; ++ }; ++ ++ ehci1: usb@01c1c000 { ++ status = "okay"; ++ }; ++ ++ ohci1: usb@01c1c400 { ++ status = "okay"; ++ }; ++ ++ pinctrl@01c20800 { ++ ahci_pwr_pin_olinuxinolime: ahci_pwr_pin@1 { ++ allwinner,pins = "PC3"; ++ allwinner,function = "gpio_out"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ ++ led_pins_olinuxinolime: led_pins@0 { ++ allwinner,pins = "PH2"; ++ allwinner,function = "gpio_out"; ++ allwinner,drive = <1>; ++ allwinner,pull = <0>; ++ }; ++ }; ++ ++ uart0: serial@01c28000 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart0_pins_a>; ++ status = "okay"; ++ }; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&led_pins_olinuxinolime>; ++ ++ green { ++ label = "a10-olinuxino-lime:green:usr"; ++ gpios = <&pio 7 2 0>; ++ default-state = "on"; ++ }; ++ }; ++ ++ reg_ahci_5v: ahci-5v { ++ pinctrl-0 = <&ahci_pwr_pin_olinuxinolime>; ++ gpio = <&pio 2 3 0>; ++ status = "okay"; ++ }; ++ ++ reg_usb1_vbus: usb1-vbus { ++ status = "okay"; ++ }; ++ ++ reg_usb2_vbus: usb2-vbus { ++ status = "okay"; ++ }; ++}; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/110-dt-sun6i-add-pll-and-spi-modclocks.patch b/target/linux/sunxi/patches-3.14/110-dt-sun6i-add-pll-and-spi-modclocks.patch new file mode 100644 index 0000000000..75c0bd84ce --- /dev/null +++ b/target/linux/sunxi/patches-3.14/110-dt-sun6i-add-pll-and-spi-modclocks.patch @@ -0,0 +1,82 @@ +From 7f94ebf35b017f1664e957857a7f36752e2577cd Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Wed, 5 Feb 2014 14:05:04 +0100 +Subject: [PATCH] ARM: sun6i: dt: Add PLL6 and SPI module clocks + +The module clocks in the A31 are still compatible with the A10 one. Add the SPI +module clocks and the PLL6 in the device tree to allow their use by the SPI +controllers. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun6i-a31.dtsi | 46 ++++++++++++++++++++++++++++++++-------- + 1 file changed, 37 insertions(+), 9 deletions(-) + +diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi +index 092bf97..93d7bb6 100644 +--- a/arch/arm/boot/dts/sun6i-a31.dtsi ++++ b/arch/arm/boot/dts/sun6i-a31.dtsi +@@ -83,16 +83,12 @@ + clocks = <&osc24M>; + }; + +- /* +- * 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. +- */ +- pll6: pll6 { ++ pll6: clk@01c20028 { + #clock-cells = <0>; +- compatible = "fixed-clock"; +- clock-frequency = <0>; ++ compatible = "allwinner,sun6i-a31-pll6-clk"; ++ reg = <0x01c20028 0x4>; ++ clocks = <&osc24M>; ++ clock-output-names = "pll6"; + }; + + cpu: cpu@01c20050 { +@@ -192,6 +188,38 @@ + "apb2_uart1", "apb2_uart2", "apb2_uart3", + "apb2_uart4", "apb2_uart5"; + }; ++ ++ spi0_clk: clk@01c200a0 { ++ #clock-cells = <0>; ++ compatible = "allwinner,sun4i-mod0-clk"; ++ reg = <0x01c200a0 0x4>; ++ clocks = <&osc24M>, <&pll6>; ++ clock-output-names = "spi0"; ++ }; ++ ++ spi1_clk: clk@01c200a4 { ++ #clock-cells = <0>; ++ compatible = "allwinner,sun4i-mod0-clk"; ++ reg = <0x01c200a4 0x4>; ++ clocks = <&osc24M>, <&pll6>; ++ clock-output-names = "spi1"; ++ }; ++ ++ spi2_clk: clk@01c200a8 { ++ #clock-cells = <0>; ++ compatible = "allwinner,sun4i-mod0-clk"; ++ reg = <0x01c200a8 0x4>; ++ clocks = <&osc24M>, <&pll6>; ++ clock-output-names = "spi2"; ++ }; ++ ++ spi3_clk: clk@01c200ac { ++ #clock-cells = <0>; ++ compatible = "allwinner,sun4i-mod0-clk"; ++ reg = <0x01c200ac 0x4>; ++ clocks = <&osc24M>, <&pll6>; ++ clock-output-names = "spi3"; ++ }; + }; + + soc@01c00000 { +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/111-dt-sun4i-rename-clocknodes.patch b/target/linux/sunxi/patches-3.14/111-dt-sun4i-rename-clocknodes.patch new file mode 100644 index 0000000000..51280b55d2 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/111-dt-sun4i-rename-clocknodes.patch @@ -0,0 +1,139 @@ +From 35b7dfc295f4d6079572a22a225c7444134e1f72 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Mon, 3 Feb 2014 09:51:41 +0800 +Subject: [PATCH] ARM: dts: sun4i: rename clock node names to clk@N + +Device tree naming conventions state that node names should match +node function. Change fully functioning clock nodes to match and +add clock-output-names to all sunxi clock nodes. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun4i-a10.dtsi | 30 ++++++++++++++++++++---------- + 1 file changed, 20 insertions(+), 10 deletions(-) + +diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi +index 64fc716..132b261 100644 +--- a/arch/arm/boot/dts/sun4i-a10.dtsi ++++ b/arch/arm/boot/dts/sun4i-a10.dtsi +@@ -58,34 +58,38 @@ + clock-frequency = <0>; + }; + +- osc24M: osc24M@01c20050 { ++ osc24M: clk@01c20050 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-osc-clk"; + reg = <0x01c20050 0x4>; + clock-frequency = <24000000>; ++ clock-output-names = "osc24M"; + }; + +- osc32k: osc32k { ++ osc32k: clk@0 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <32768>; ++ clock-output-names = "osc32k"; + }; + +- pll1: pll1@01c20000 { ++ pll1: clk@01c20000 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-pll1-clk"; + reg = <0x01c20000 0x4>; + clocks = <&osc24M>; ++ clock-output-names = "pll1"; + }; + +- pll4: pll4@01c20018 { ++ pll4: clk@01c20018 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-pll1-clk"; + reg = <0x01c20018 0x4>; + clocks = <&osc24M>; ++ clock-output-names = "pll4"; + }; + +- pll5: pll5@01c20020 { ++ pll5: clk@01c20020 { + #clock-cells = <1>; + compatible = "allwinner,sun4i-pll5-clk"; + reg = <0x01c20020 0x4>; +@@ -93,7 +97,7 @@ + clock-output-names = "pll5_ddr", "pll5_other"; + }; + +- pll6: pll6@01c20028 { ++ pll6: clk@01c20028 { + #clock-cells = <1>; + compatible = "allwinner,sun4i-pll6-clk"; + reg = <0x01c20028 0x4>; +@@ -107,6 +111,7 @@ + compatible = "allwinner,sun4i-cpu-clk"; + reg = <0x01c20054 0x4>; + clocks = <&osc32k>, <&osc24M>, <&pll1>, <&dummy>; ++ clock-output-names = "cpu"; + }; + + axi: axi@01c20054 { +@@ -114,9 +119,10 @@ + compatible = "allwinner,sun4i-axi-clk"; + reg = <0x01c20054 0x4>; + clocks = <&cpu>; ++ clock-output-names = "axi"; + }; + +- axi_gates: axi_gates@01c2005c { ++ axi_gates: clk@01c2005c { + #clock-cells = <1>; + compatible = "allwinner,sun4i-axi-gates-clk"; + reg = <0x01c2005c 0x4>; +@@ -129,9 +135,10 @@ + compatible = "allwinner,sun4i-ahb-clk"; + reg = <0x01c20054 0x4>; + clocks = <&axi>; ++ clock-output-names = "ahb"; + }; + +- ahb_gates: ahb_gates@01c20060 { ++ ahb_gates: clk@01c20060 { + #clock-cells = <1>; + compatible = "allwinner,sun4i-ahb-gates-clk"; + reg = <0x01c20060 0x8>; +@@ -154,9 +161,10 @@ + compatible = "allwinner,sun4i-apb0-clk"; + reg = <0x01c20054 0x4>; + clocks = <&ahb>; ++ clock-output-names = "apb0"; + }; + +- apb0_gates: apb0_gates@01c20068 { ++ apb0_gates: clk@01c20068 { + #clock-cells = <1>; + compatible = "allwinner,sun4i-apb0-gates-clk"; + reg = <0x01c20068 0x4>; +@@ -171,6 +179,7 @@ + compatible = "allwinner,sun4i-apb1-mux-clk"; + reg = <0x01c20058 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&osc32k>; ++ clock-output-names = "apb1_mux"; + }; + + apb1: apb1@01c20058 { +@@ -178,9 +187,10 @@ + compatible = "allwinner,sun4i-apb1-clk"; + reg = <0x01c20058 0x4>; + clocks = <&apb1_mux>; ++ clock-output-names = "apb1"; + }; + +- apb1_gates: apb1_gates@01c2006c { ++ apb1_gates: clk@01c2006c { + #clock-cells = <1>; + compatible = "allwinner,sun4i-apb1-gates-clk"; + reg = <0x01c2006c 0x4>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/112-dt-sun5i-rename-clocknodes.patch b/target/linux/sunxi/patches-3.14/112-dt-sun5i-rename-clocknodes.patch new file mode 100644 index 0000000000..4ed1f2d59e --- /dev/null +++ b/target/linux/sunxi/patches-3.14/112-dt-sun5i-rename-clocknodes.patch @@ -0,0 +1,261 @@ +From 266f79cef78cdf3545065a4786506eee0ae012b3 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Mon, 3 Feb 2014 09:51:42 +0800 +Subject: [PATCH] ARM: dts: sun5i: rename clock node names to clk@N + +Device tree naming conventions state that node names should match +node function. Change fully functioning clock nodes to match and +add clock-output-names to all sunxi clock nodes. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun5i-a10s.dtsi | 30 ++++++++++++++++++++---------- + arch/arm/boot/dts/sun5i-a13.dtsi | 30 ++++++++++++++++++++---------- + 2 files changed, 40 insertions(+), 20 deletions(-) + +diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi +index 848baaa..99a5120 100644 +--- a/arch/arm/boot/dts/sun5i-a10s.dtsi ++++ b/arch/arm/boot/dts/sun5i-a10s.dtsi +@@ -51,34 +51,38 @@ + clock-frequency = <0>; + }; + +- osc24M: osc24M@01c20050 { ++ osc24M: clk@01c20050 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-osc-clk"; + reg = <0x01c20050 0x4>; + clock-frequency = <24000000>; ++ clock-output-names = "osc24M"; + }; + +- osc32k: osc32k { ++ osc32k: clk@0 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <32768>; ++ clock-output-names = "osc32k"; + }; + +- pll1: pll1@01c20000 { ++ pll1: clk@01c20000 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-pll1-clk"; + reg = <0x01c20000 0x4>; + clocks = <&osc24M>; ++ clock-output-names = "pll1"; + }; + +- pll4: pll4@01c20018 { ++ pll4: clk@01c20018 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-pll1-clk"; + reg = <0x01c20018 0x4>; + clocks = <&osc24M>; ++ clock-output-names = "pll4"; + }; + +- pll5: pll5@01c20020 { ++ pll5: clk@01c20020 { + #clock-cells = <1>; + compatible = "allwinner,sun4i-pll5-clk"; + reg = <0x01c20020 0x4>; +@@ -86,7 +90,7 @@ + clock-output-names = "pll5_ddr", "pll5_other"; + }; + +- pll6: pll6@01c20028 { ++ pll6: clk@01c20028 { + #clock-cells = <1>; + compatible = "allwinner,sun4i-pll6-clk"; + reg = <0x01c20028 0x4>; +@@ -100,6 +104,7 @@ + compatible = "allwinner,sun4i-cpu-clk"; + reg = <0x01c20054 0x4>; + clocks = <&osc32k>, <&osc24M>, <&pll1>, <&dummy>; ++ clock-output-names = "cpu"; + }; + + axi: axi@01c20054 { +@@ -107,9 +112,10 @@ + compatible = "allwinner,sun4i-axi-clk"; + reg = <0x01c20054 0x4>; + clocks = <&cpu>; ++ clock-output-names = "axi"; + }; + +- axi_gates: axi_gates@01c2005c { ++ axi_gates: clk@01c2005c { + #clock-cells = <1>; + compatible = "allwinner,sun4i-axi-gates-clk"; + reg = <0x01c2005c 0x4>; +@@ -122,9 +128,10 @@ + compatible = "allwinner,sun4i-ahb-clk"; + reg = <0x01c20054 0x4>; + clocks = <&axi>; ++ clock-output-names = "ahb"; + }; + +- ahb_gates: ahb_gates@01c20060 { ++ ahb_gates: clk@01c20060 { + #clock-cells = <1>; + compatible = "allwinner,sun5i-a10s-ahb-gates-clk"; + reg = <0x01c20060 0x8>; +@@ -143,9 +150,10 @@ + compatible = "allwinner,sun4i-apb0-clk"; + reg = <0x01c20054 0x4>; + clocks = <&ahb>; ++ clock-output-names = "apb0"; + }; + +- apb0_gates: apb0_gates@01c20068 { ++ apb0_gates: clk@01c20068 { + #clock-cells = <1>; + compatible = "allwinner,sun5i-a10s-apb0-gates-clk"; + reg = <0x01c20068 0x4>; +@@ -159,6 +167,7 @@ + compatible = "allwinner,sun4i-apb1-mux-clk"; + reg = <0x01c20058 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&osc32k>; ++ clock-output-names = "apb1_mux"; + }; + + apb1: apb1@01c20058 { +@@ -166,9 +175,10 @@ + compatible = "allwinner,sun4i-apb1-clk"; + reg = <0x01c20058 0x4>; + clocks = <&apb1_mux>; ++ clock-output-names = "apb1"; + }; + +- apb1_gates: apb1_gates@01c2006c { ++ apb1_gates: clk@01c2006c { + #clock-cells = <1>; + compatible = "allwinner,sun5i-a10s-apb1-gates-clk"; + reg = <0x01c2006c 0x4>; +diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi +index d8207b0..b776baa 100644 +--- a/arch/arm/boot/dts/sun5i-a13.dtsi ++++ b/arch/arm/boot/dts/sun5i-a13.dtsi +@@ -52,34 +52,38 @@ + clock-frequency = <0>; + }; + +- osc24M: osc24M@01c20050 { ++ osc24M: clk@01c20050 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-osc-clk"; + reg = <0x01c20050 0x4>; + clock-frequency = <24000000>; ++ clock-output-names = "osc24M"; + }; + +- osc32k: osc32k { ++ osc32k: clk@0 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <32768>; ++ clock-output-names = "osc32k"; + }; + +- pll1: pll1@01c20000 { ++ pll1: clk@01c20000 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-pll1-clk"; + reg = <0x01c20000 0x4>; + clocks = <&osc24M>; ++ clock-output-names = "pll1"; + }; + +- pll4: pll4@01c20018 { ++ pll4: clk@01c20018 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-pll1-clk"; + reg = <0x01c20018 0x4>; + clocks = <&osc24M>; ++ clock-output-names = "pll4"; + }; + +- pll5: pll5@01c20020 { ++ pll5: clk@01c20020 { + #clock-cells = <1>; + compatible = "allwinner,sun4i-pll5-clk"; + reg = <0x01c20020 0x4>; +@@ -87,7 +91,7 @@ + clock-output-names = "pll5_ddr", "pll5_other"; + }; + +- pll6: pll6@01c20028 { ++ pll6: clk@01c20028 { + #clock-cells = <1>; + compatible = "allwinner,sun4i-pll6-clk"; + reg = <0x01c20028 0x4>; +@@ -101,6 +105,7 @@ + compatible = "allwinner,sun4i-cpu-clk"; + reg = <0x01c20054 0x4>; + clocks = <&osc32k>, <&osc24M>, <&pll1>, <&dummy>; ++ clock-output-names = "cpu"; + }; + + axi: axi@01c20054 { +@@ -108,9 +113,10 @@ + compatible = "allwinner,sun4i-axi-clk"; + reg = <0x01c20054 0x4>; + clocks = <&cpu>; ++ clock-output-names = "axi"; + }; + +- axi_gates: axi_gates@01c2005c { ++ axi_gates: clk@01c2005c { + #clock-cells = <1>; + compatible = "allwinner,sun4i-axi-gates-clk"; + reg = <0x01c2005c 0x4>; +@@ -123,9 +129,10 @@ + compatible = "allwinner,sun4i-ahb-clk"; + reg = <0x01c20054 0x4>; + clocks = <&axi>; ++ clock-output-names = "ahb"; + }; + +- ahb_gates: ahb_gates@01c20060 { ++ ahb_gates: clk@01c20060 { + #clock-cells = <1>; + compatible = "allwinner,sun5i-a13-ahb-gates-clk"; + reg = <0x01c20060 0x8>; +@@ -143,9 +150,10 @@ + compatible = "allwinner,sun4i-apb0-clk"; + reg = <0x01c20054 0x4>; + clocks = <&ahb>; ++ clock-output-names = "apb0"; + }; + +- apb0_gates: apb0_gates@01c20068 { ++ apb0_gates: clk@01c20068 { + #clock-cells = <1>; + compatible = "allwinner,sun5i-a13-apb0-gates-clk"; + reg = <0x01c20068 0x4>; +@@ -158,6 +166,7 @@ + compatible = "allwinner,sun4i-apb1-mux-clk"; + reg = <0x01c20058 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&osc32k>; ++ clock-output-names = "apb1_mux"; + }; + + apb1: apb1@01c20058 { +@@ -165,9 +174,10 @@ + compatible = "allwinner,sun4i-apb1-clk"; + reg = <0x01c20058 0x4>; + clocks = <&apb1_mux>; ++ clock-output-names = "apb1"; + }; + +- apb1_gates: apb1_gates@01c2006c { ++ apb1_gates: clk@01c2006c { + #clock-cells = <1>; + compatible = "allwinner,sun5i-a13-apb1-gates-clk"; + reg = <0x01c2006c 0x4>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/113-dt-sun6i-rename-clocknodes.patch b/target/linux/sunxi/patches-3.14/113-dt-sun6i-rename-clocknodes.patch new file mode 100644 index 0000000000..56c0208b0a --- /dev/null +++ b/target/linux/sunxi/patches-3.14/113-dt-sun6i-rename-clocknodes.patch @@ -0,0 +1,112 @@ +From 8bd1bb3a670aae791c4b2e9ab13c92768233368a Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Mon, 3 Feb 2014 09:51:43 +0800 +Subject: [PATCH] ARM: dts: sun6i: rename clock node names to clk@N + +Device tree naming conventions state that node names should match +node function. Change fully functioning clock nodes to match and +add clock-output-names to all sunxi clock nodes. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun6i-a31.dtsi | 19 ++++++++++++++----- + 1 file changed, 14 insertions(+), 5 deletions(-) + +diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi +index fc07f70..d3f1995 100644 +--- a/arch/arm/boot/dts/sun6i-a31.dtsi ++++ b/arch/arm/boot/dts/sun6i-a31.dtsi +@@ -70,17 +70,19 @@ + clock-frequency = <24000000>; + }; + +- osc32k: osc32k { ++ osc32k: clk@0 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <32768>; ++ clock-output-names = "osc32k"; + }; + +- pll1: pll1@01c20000 { ++ pll1: clk@01c20000 { + #clock-cells = <0>; + compatible = "allwinner,sun6i-a31-pll1-clk"; + reg = <0x01c20000 0x4>; + clocks = <&osc24M>; ++ clock-output-names = "pll1"; + }; + + pll6: clk@01c20028 { +@@ -103,6 +105,7 @@ + * Allwinner. + */ + clocks = <&osc32k>, <&osc24M>, <&pll1>, <&pll1>; ++ clock-output-names = "cpu"; + }; + + axi: axi@01c20050 { +@@ -110,6 +113,7 @@ + compatible = "allwinner,sun4i-axi-clk"; + reg = <0x01c20050 0x4>; + clocks = <&cpu>; ++ clock-output-names = "axi"; + }; + + ahb1_mux: ahb1_mux@01c20054 { +@@ -117,6 +121,7 @@ + compatible = "allwinner,sun6i-a31-ahb1-mux-clk"; + reg = <0x01c20054 0x4>; + clocks = <&osc32k>, <&osc24M>, <&axi>, <&pll6>; ++ clock-output-names = "ahb1_mux"; + }; + + ahb1: ahb1@01c20054 { +@@ -124,9 +129,10 @@ + compatible = "allwinner,sun4i-ahb-clk"; + reg = <0x01c20054 0x4>; + clocks = <&ahb1_mux>; ++ clock-output-names = "ahb1"; + }; + +- ahb1_gates: ahb1_gates@01c20060 { ++ ahb1_gates: clk@01c20060 { + #clock-cells = <1>; + compatible = "allwinner,sun6i-a31-ahb1-gates-clk"; + reg = <0x01c20060 0x8>; +@@ -152,9 +158,10 @@ + compatible = "allwinner,sun4i-apb0-clk"; + reg = <0x01c20054 0x4>; + clocks = <&ahb1>; ++ clock-output-names = "apb1"; + }; + +- apb1_gates: apb1_gates@01c20060 { ++ apb1_gates: clk@01c20068 { + #clock-cells = <1>; + compatible = "allwinner,sun6i-a31-apb1-gates-clk"; + reg = <0x01c20068 0x4>; +@@ -169,6 +176,7 @@ + compatible = "allwinner,sun4i-apb1-mux-clk"; + reg = <0x01c20058 0x4>; + clocks = <&osc32k>, <&osc24M>, <&pll6>, <&pll6>; ++ clock-output-names = "apb2_mux"; + }; + + apb2: apb2@01c20058 { +@@ -176,9 +184,10 @@ + compatible = "allwinner,sun6i-a31-apb2-div-clk"; + reg = <0x01c20058 0x4>; + clocks = <&apb2_mux>; ++ clock-output-names = "apb2"; + }; + +- apb2_gates: apb2_gates@01c2006c { ++ apb2_gates: clk@01c2006c { + #clock-cells = <1>; + compatible = "allwinner,sun6i-a31-apb2-gates-clk"; + reg = <0x01c2006c 0x4>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/114-dt-sun7i-rename-clocknodes.patch b/target/linux/sunxi/patches-3.14/114-dt-sun7i-rename-clocknodes.patch new file mode 100644 index 0000000000..a1786fc89e --- /dev/null +++ b/target/linux/sunxi/patches-3.14/114-dt-sun7i-rename-clocknodes.patch @@ -0,0 +1,132 @@ +From c57b781689bba48dad635caf005962cc9c8e5e3d Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Mon, 3 Feb 2014 09:51:44 +0800 +Subject: [PATCH] ARM: dts: sun7i: rename clock node names to clk@N + +Device tree naming conventions state that node names should match +node function. Change fully functioning clock nodes to match and +add clock-output-names to all sunxi clock nodes. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun7i-a20.dtsi | 25 +++++++++++++++++-------- + 1 file changed, 17 insertions(+), 8 deletions(-) + +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 2139e0f..78f562a 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -54,11 +54,12 @@ + #size-cells = <1>; + ranges; + +- osc24M: osc24M@01c20050 { ++ osc24M: clk@01c20050 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-osc-clk"; + reg = <0x01c20050 0x4>; + clock-frequency = <24000000>; ++ clock-output-names = "osc24M"; + }; + + osc32k: clk@0 { +@@ -68,21 +69,23 @@ + clock-output-names = "osc32k"; + }; + +- pll1: pll1@01c20000 { ++ pll1: clk@01c20000 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-pll1-clk"; + reg = <0x01c20000 0x4>; + clocks = <&osc24M>; ++ clock-output-names = "pll1"; + }; + +- pll4: pll4@01c20018 { ++ pll4: clk@01c20018 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-pll1-clk"; + reg = <0x01c20018 0x4>; + clocks = <&osc24M>; ++ clock-output-names = "pll4"; + }; + +- pll5: pll5@01c20020 { ++ pll5: clk@01c20020 { + #clock-cells = <1>; + compatible = "allwinner,sun4i-pll5-clk"; + reg = <0x01c20020 0x4>; +@@ -90,7 +93,7 @@ + clock-output-names = "pll5_ddr", "pll5_other"; + }; + +- pll6: pll6@01c20028 { ++ pll6: clk@01c20028 { + #clock-cells = <1>; + compatible = "allwinner,sun4i-pll6-clk"; + reg = <0x01c20028 0x4>; +@@ -103,6 +106,7 @@ + compatible = "allwinner,sun4i-cpu-clk"; + reg = <0x01c20054 0x4>; + clocks = <&osc32k>, <&osc24M>, <&pll1>, <&pll6 1>; ++ clock-output-names = "cpu"; + }; + + axi: axi@01c20054 { +@@ -110,6 +114,7 @@ + compatible = "allwinner,sun4i-axi-clk"; + reg = <0x01c20054 0x4>; + clocks = <&cpu>; ++ clock-output-names = "axi"; + }; + + ahb: ahb@01c20054 { +@@ -117,9 +122,10 @@ + compatible = "allwinner,sun4i-ahb-clk"; + reg = <0x01c20054 0x4>; + clocks = <&axi>; ++ clock-output-names = "ahb"; + }; + +- ahb_gates: ahb_gates@01c20060 { ++ ahb_gates: clk@01c20060 { + #clock-cells = <1>; + compatible = "allwinner,sun7i-a20-ahb-gates-clk"; + reg = <0x01c20060 0x8>; +@@ -144,9 +150,10 @@ + compatible = "allwinner,sun4i-apb0-clk"; + reg = <0x01c20054 0x4>; + clocks = <&ahb>; ++ clock-output-names = "apb0"; + }; + +- apb0_gates: apb0_gates@01c20068 { ++ apb0_gates: clk@01c20068 { + #clock-cells = <1>; + compatible = "allwinner,sun7i-a20-apb0-gates-clk"; + reg = <0x01c20068 0x4>; +@@ -162,6 +169,7 @@ + compatible = "allwinner,sun4i-apb1-mux-clk"; + reg = <0x01c20058 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&osc32k>; ++ clock-output-names = "apb1_mux"; + }; + + apb1: apb1@01c20058 { +@@ -169,9 +177,10 @@ + compatible = "allwinner,sun4i-apb1-clk"; + reg = <0x01c20058 0x4>; + clocks = <&apb1_mux>; ++ clock-output-names = "apb1"; + }; + +- apb1_gates: apb1_gates@01c2006c { ++ apb1_gates: clk@01c2006c { + #clock-cells = <1>; + compatible = "allwinner,sun7i-a20-apb1-gates-clk"; + reg = <0x01c2006c 0x4>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/115-dt-sun6i-fix-mod0-compat.patch b/target/linux/sunxi/patches-3.14/115-dt-sun6i-fix-mod0-compat.patch new file mode 100644 index 0000000000..6b14459100 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/115-dt-sun6i-fix-mod0-compat.patch @@ -0,0 +1,56 @@ +From 95c1fe603fbea0fd01d98262bd5ff7d5442a86db Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Mon, 24 Feb 2014 17:29:06 +0100 +Subject: [PATCH] ARM: sun6i: dt: Fix mod0 compatible + +The module 0 clock compatibles were changed between the time the patch was sent +and it was merged. Update the compatibles. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun6i-a31.dtsi | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi +index af6f87c..42f310a 100644 +--- a/arch/arm/boot/dts/sun6i-a31.dtsi ++++ b/arch/arm/boot/dts/sun6i-a31.dtsi +@@ -200,7 +200,7 @@ + + spi0_clk: clk@01c200a0 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200a0 0x4>; + clocks = <&osc24M>, <&pll6>; + clock-output-names = "spi0"; +@@ -208,7 +208,7 @@ + + spi1_clk: clk@01c200a4 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200a4 0x4>; + clocks = <&osc24M>, <&pll6>; + clock-output-names = "spi1"; +@@ -216,7 +216,7 @@ + + spi2_clk: clk@01c200a8 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200a8 0x4>; + clocks = <&osc24M>, <&pll6>; + clock-output-names = "spi2"; +@@ -224,7 +224,7 @@ + + spi3_clk: clk@01c200ac { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200ac 0x4>; + clocks = <&osc24M>, <&pll6>; + clock-output-names = "spi3"; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/120-dt-sun7i-add-gmac-clocknode.patch b/target/linux/sunxi/patches-3.14/120-dt-sun7i-add-gmac-clocknode.patch new file mode 100644 index 0000000000..47dab70c69 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/120-dt-sun7i-add-gmac-clocknode.patch @@ -0,0 +1,56 @@ +From 3e1660161aacc5aeeebb09a0c8d91ad01399e5cd Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Mon, 10 Feb 2014 18:35:48 +0800 +Subject: [PATCH] ARM: dts: sun7i: Add GMAC clock node to sun7i DTSI + +The GMAC uses 1 of 2 sources for its transmit clock, depending on the +PHY interface mode. Add both sources as dummy clocks, and as parents +to the GMAC clock node. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +--- + arch/arm/boot/dts/sun7i-a20.dtsi | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 78f562a..f6ae357 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -322,6 +322,34 @@ + }; + + /* ++ * The following two are dummy clocks, placeholders used in the gmac_tx ++ * clock. The gmac driver will choose one parent depending on the PHY ++ * interface mode, using clk_set_rate auto-reparenting. ++ * The actual TX clock rate is not controlled by the gmac_tx clock. ++ */ ++ mii_phy_tx_clk: clk@2 { ++ #clock-cells = <0>; ++ compatible = "fixed-clock"; ++ clock-frequency = <25000000>; ++ clock-output-names = "mii_phy_tx"; ++ }; ++ ++ gmac_int_tx_clk: clk@3 { ++ #clock-cells = <0>; ++ compatible = "fixed-clock"; ++ clock-frequency = <125000000>; ++ clock-output-names = "gmac_int_tx"; ++ }; ++ ++ gmac_tx_clk: clk@01c20164 { ++ #clock-cells = <0>; ++ compatible = "allwinner,sun7i-a20-gmac-clk"; ++ reg = <0x01c20164 0x4>; ++ clocks = <&mii_phy_tx_clk>, <&gmac_int_tx_clk>; ++ clock-output-names = "gmac_tx"; ++ }; ++ ++ /* + * Dummy clock used by output clocks + */ + osc24M_32k: clk@1 { +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/121-dt-sun7i-add-gmac-ctrlnode.patch b/target/linux/sunxi/patches-3.14/121-dt-sun7i-add-gmac-ctrlnode.patch new file mode 100644 index 0000000000..bf2d384325 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/121-dt-sun7i-add-gmac-ctrlnode.patch @@ -0,0 +1,39 @@ +From 22b9ac16c600694b12479a75461bce031295e4b9 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Mon, 10 Feb 2014 18:35:49 +0800 +Subject: [PATCH] ARM: dts: sun7i: Add GMAC controller node to sun7i DTSI + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +--- + arch/arm/boot/dts/sun7i-a20.dtsi | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index f6ae357..fa489fe 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -645,6 +645,21 @@ + status = "disabled"; + }; + ++ gmac: ethernet@01c50000 { ++ compatible = "allwinner,sun7i-a20-gmac"; ++ reg = <0x01c50000 0x10000>; ++ interrupts = <0 85 4>; ++ interrupt-names = "macirq"; ++ clocks = <&ahb_gates 49>, <&gmac_tx_clk>; ++ clock-names = "stmmaceth", "allwinner_gmac_tx"; ++ snps,pbl = <2>; ++ snps,fixed-burst; ++ snps,force_sf_dma_mode; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ + hstimer@01c60000 { + compatible = "allwinner,sun7i-a20-hstimer"; + reg = <0x01c60000 0x1000>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/122-dt-sun7i-add-pinmuxing-for-gmac.patch b/target/linux/sunxi/patches-3.14/122-dt-sun7i-add-pinmuxing-for-gmac.patch new file mode 100644 index 0000000000..7fa7b9c898 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/122-dt-sun7i-add-pinmuxing-for-gmac.patch @@ -0,0 +1,53 @@ +From 9f6deb688f4cb733cd3f36e0cc88f14d2f81982d Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Mon, 10 Feb 2014 18:35:50 +0800 +Subject: [PATCH] ARM: dts: sun7i: Add pin muxing options for the GMAC + +The A20 has EMAC and GMAC muxed on the same pins. +Add pin sets with gmac function for MII and RGMII mode to the DTSI. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +--- + arch/arm/boot/dts/sun7i-a20.dtsi | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index fa489fe..679dc50 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -484,6 +484,32 @@ + allwinner,drive = <0>; + allwinner,pull = <0>; + }; ++ ++ gmac_pins_mii_a: gmac_mii@0 { ++ allwinner,pins = "PA0", "PA1", "PA2", ++ "PA3", "PA4", "PA5", "PA6", ++ "PA7", "PA8", "PA9", "PA10", ++ "PA11", "PA12", "PA13", "PA14", ++ "PA15", "PA16"; ++ allwinner,function = "gmac"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ ++ gmac_pins_rgmii_a: gmac_rgmii@0 { ++ allwinner,pins = "PA0", "PA1", "PA2", ++ "PA3", "PA4", "PA5", "PA6", ++ "PA7", "PA8", "PA10", ++ "PA11", "PA12", "PA13", ++ "PA15", "PA16"; ++ allwinner,function = "gmac"; ++ /* ++ * data lines in RGMII mode use DDR mode ++ * and need a higher signal drive strength ++ */ ++ allwinner,drive = <3>; ++ allwinner,pull = <0>; ++ }; + }; + + timer@01c20c00 { +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/123-dt-sun7i-cubietruck-enable-gmac.patch b/target/linux/sunxi/patches-3.14/123-dt-sun7i-cubietruck-enable-gmac.patch new file mode 100644 index 0000000000..4bc4a76ab5 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/123-dt-sun7i-cubietruck-enable-gmac.patch @@ -0,0 +1,38 @@ +From d09483e1cfcc00be70450c469c3c23496a063d98 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Mon, 10 Feb 2014 18:35:51 +0800 +Subject: [PATCH] ARM: dts: sun7i: cubietruck: Enable the GMAC + +The CubieTruck uses the GMAC with an RGMII phy. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +--- + arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +index f9dcb61..025ce52 100644 +--- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts ++++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +@@ -51,6 +51,18 @@ + pinctrl-0 = <&i2c2_pins_a>; + status = "okay"; + }; ++ ++ gmac: ethernet@01c50000 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&gmac_pins_rgmii_a>; ++ phy = <&phy1>; ++ phy-mode = "rgmii"; ++ status = "okay"; ++ ++ phy1: ethernet-phy@1 { ++ reg = <1>; ++ }; ++ }; + }; + + leds { +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/124-dt-sun7i-cubieboard2-enable-gmac.patch b/target/linux/sunxi/patches-3.14/124-dt-sun7i-cubieboard2-enable-gmac.patch new file mode 100644 index 0000000000..511606466d --- /dev/null +++ b/target/linux/sunxi/patches-3.14/124-dt-sun7i-cubieboard2-enable-gmac.patch @@ -0,0 +1,61 @@ +From 856cae07bff4d7e7a111affd4f15803142d29880 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Mon, 10 Feb 2014 18:35:52 +0800 +Subject: [PATCH] ARM: dts: sun7i: cubieboard2: Enable GMAC instead of EMAC + +GMAC has better performance and fewer hardware issues. +Use the GMAC in MII mode for ethernet instead of the EMAC. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +--- + arch/arm/boot/dts/sun7i-a20-cubieboard2.dts | 27 ++++++++++++--------------- + 1 file changed, 12 insertions(+), 15 deletions(-) + +diff --git a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts +index 5c51cb8..7bf4935 100644 +--- a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts ++++ b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts +@@ -19,21 +19,6 @@ + compatible = "cubietech,cubieboard2", "allwinner,sun7i-a20"; + + soc@01c00000 { +- emac: ethernet@01c0b000 { +- pinctrl-names = "default"; +- pinctrl-0 = <&emac_pins_a>; +- phy = <&phy1>; +- status = "okay"; +- }; +- +- mdio@01c0b080 { +- status = "okay"; +- +- phy1: ethernet-phy@1 { +- reg = <1>; +- }; +- }; +- + pinctrl@01c20800 { + led_pins_cubieboard2: led_pins@0 { + allwinner,pins = "PH20", "PH21"; +@@ -60,6 +45,18 @@ + pinctrl-0 = <&i2c1_pins_a>; + status = "okay"; + }; ++ ++ gmac: ethernet@01c50000 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&gmac_pins_mii_a>; ++ phy = <&phy1>; ++ phy-mode = "mii"; ++ status = "okay"; ++ ++ phy1: ethernet-phy@1 { ++ reg = <1>; ++ }; ++ }; + }; + + leds { +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/125-dt-sun7i-olinuxinom-enable-gmac.patch b/target/linux/sunxi/patches-3.14/125-dt-sun7i-olinuxinom-enable-gmac.patch new file mode 100644 index 0000000000..756400a557 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/125-dt-sun7i-olinuxinom-enable-gmac.patch @@ -0,0 +1,62 @@ +From f5b426a2eb246e91eb2de0cd215565d38d1d5ce7 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Mon, 10 Feb 2014 18:35:53 +0800 +Subject: [PATCH] ARM: dts: sun7i: a20-olinuxino-micro: Enable GMAC instead of + EMAC + +GMAC has better performance and fewer hardware issues. +Use the GMAC in MII mode for ethernet instead of the EMAC. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +--- + arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts | 27 +++++++++++-------------- + 1 file changed, 12 insertions(+), 15 deletions(-) + +diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts +index ead3013..b02a796 100644 +--- a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts ++++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts +@@ -19,21 +19,6 @@ + compatible = "olimex,a20-olinuxino-micro", "allwinner,sun7i-a20"; + + soc@01c00000 { +- emac: ethernet@01c0b000 { +- pinctrl-names = "default"; +- pinctrl-0 = <&emac_pins_a>; +- phy = <&phy1>; +- status = "okay"; +- }; +- +- mdio@01c0b080 { +- status = "okay"; +- +- phy1: ethernet-phy@1 { +- reg = <1>; +- }; +- }; +- + pinctrl@01c20800 { + led_pins_olinuxino: led_pins@0 { + allwinner,pins = "PH2"; +@@ -78,6 +63,18 @@ + pinctrl-0 = <&i2c2_pins_a>; + status = "okay"; + }; ++ ++ gmac: ethernet@01c50000 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&gmac_pins_mii_a>; ++ phy = <&phy1>; ++ phy-mode = "mii"; ++ status = "okay"; ++ ++ phy1: ethernet-phy@1 { ++ reg = <1>; ++ }; ++ }; + }; + + leds { +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/126-dt-sun7i-add-eth-alias-for-gmac.patch b/target/linux/sunxi/patches-3.14/126-dt-sun7i-add-eth-alias-for-gmac.patch new file mode 100644 index 0000000000..0fe2087ebc --- /dev/null +++ b/target/linux/sunxi/patches-3.14/126-dt-sun7i-add-eth-alias-for-gmac.patch @@ -0,0 +1,31 @@ +From 614b4b996be81daca9d8333e1ac163d23e1701c4 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Mon, 10 Feb 2014 18:35:54 +0800 +Subject: [PATCH] ARM: dts: sun7i: Add ethernet alias for GMAC + +All Allwinner A20 boards we support can only use either EMAC or GMAC, +as they share the same pins. As we have switched all supported to +GMAC, we should alias GMAC (the active controller) as ethernet0, +so u-boot will insert the MAC address for the correct controller. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +--- + arch/arm/boot/dts/sun7i-a20.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 679dc50..bfc3a95 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -17,7 +17,7 @@ + interrupt-parent = <&gic>; + + aliases { +- ethernet0 = &emac; ++ ethernet0 = &gmac; + serial0 = &uart0; + serial1 = &uart1; + serial2 = &uart2; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/130-dt-sun4i-add-usbclock-bindings.patch b/target/linux/sunxi/patches-3.14/130-dt-sun4i-add-usbclock-bindings.patch new file mode 100644 index 0000000000..71d97c67ac --- /dev/null +++ b/target/linux/sunxi/patches-3.14/130-dt-sun4i-add-usbclock-bindings.patch @@ -0,0 +1,35 @@ +From b5325f9813a6a7cf80ede22fbee66376abbe2384 Mon Sep 17 00:00:00 2001 +From: Roman Byshko <rbyshko@gmail.com> +Date: Fri, 7 Feb 2014 16:21:51 +0100 +Subject: [PATCH] ARM: sun4i: dt: Add bindings for USB clocks + +Signed-off-by: Roman Byshko <rbyshko@gmail.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun4i-a10.dtsi | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi +index 132b261..2d623d0 100644 +--- a/arch/arm/boot/dts/sun4i-a10.dtsi ++++ b/arch/arm/boot/dts/sun4i-a10.dtsi +@@ -315,6 +315,15 @@ + clock-output-names = "ir1"; + }; + ++ usb_clk: clk@01c200cc { ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ compatible = "allwinner,sun4i-a10-usb-clk"; ++ reg = <0x01c200cc 0x4>; ++ clocks = <&pll6 1>; ++ clock-output-names = "usb_ohci0", "usb_ohci1", "usb_phy"; ++ }; ++ + spi3_clk: clk@01c200d4 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-mod0-clk"; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/131-dt-sun5i-add-usbclock-bindings.patch b/target/linux/sunxi/patches-3.14/131-dt-sun5i-add-usbclock-bindings.patch new file mode 100644 index 0000000000..337710cbf5 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/131-dt-sun5i-add-usbclock-bindings.patch @@ -0,0 +1,56 @@ +From 5b08dd0d672b59fff16d02239ca6a36aaecdb1ca Mon Sep 17 00:00:00 2001 +From: Roman Byshko <rbyshko@gmail.com> +Date: Fri, 7 Feb 2014 16:21:52 +0100 +Subject: [PATCH] ARM: sun5i: dt: Add bindings for USB clocks + +Signed-off-by: Roman Byshko <rbyshko@gmail.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun5i-a10s.dtsi | 9 +++++++++ + arch/arm/boot/dts/sun5i-a13.dtsi | 9 +++++++++ + 2 files changed, 18 insertions(+) + +diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi +index 99a5120..905317e 100644 +--- a/arch/arm/boot/dts/sun5i-a10s.dtsi ++++ b/arch/arm/boot/dts/sun5i-a10s.dtsi +@@ -276,6 +276,15 @@ + clock-output-names = "ir0"; + }; + ++ usb_clk: clk@01c200cc { ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ compatible = "allwinner,sun5i-a13-usb-clk"; ++ reg = <0x01c200cc 0x4>; ++ clocks = <&pll6 1>; ++ clock-output-names = "usb_ohci0", "usb_phy"; ++ }; ++ + mbus_clk: clk@01c2015c { + #clock-cells = <0>; + compatible = "allwinner,sun4i-mod0-clk"; +diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi +index b776baa..d196ebc6 100644 +--- a/arch/arm/boot/dts/sun5i-a13.dtsi ++++ b/arch/arm/boot/dts/sun5i-a13.dtsi +@@ -274,6 +274,15 @@ + clock-output-names = "ir0"; + }; + ++ usb_clk: clk@01c200cc { ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ compatible = "allwinner,sun5i-a13-usb-clk"; ++ reg = <0x01c200cc 0x4>; ++ clocks = <&pll6 1>; ++ clock-output-names = "usb_ohci0", "usb_phy"; ++ }; ++ + mbus_clk: clk@01c2015c { + #clock-cells = <0>; + compatible = "allwinner,sun4i-mod0-clk"; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/132-dt-sun7i-add-usbclock-bindings.patch b/target/linux/sunxi/patches-3.14/132-dt-sun7i-add-usbclock-bindings.patch new file mode 100644 index 0000000000..151aa46162 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/132-dt-sun7i-add-usbclock-bindings.patch @@ -0,0 +1,35 @@ +From 25a1ac92997df2b1e2c76a374d444605cbc96a9f Mon Sep 17 00:00:00 2001 +From: Roman Byshko <rbyshko@gmail.com> +Date: Fri, 7 Feb 2014 16:21:53 +0100 +Subject: [PATCH] ARM: sun7i: dt: Add bindings for USB clocks + +Signed-off-by: Roman Byshko <rbyshko@gmail.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun7i-a20.dtsi | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index bfc3a95..822a816 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -305,6 +305,15 @@ + clock-output-names = "ir1"; + }; + ++ usb_clk: clk@01c200cc { ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ compatible = "allwinner,sun4i-a10-usb-clk"; ++ reg = <0x01c200cc 0x4>; ++ clocks = <&pll6 1>; ++ clock-output-names = "usb_ohci0", "usb_ohci1", "usb_phy"; ++ }; ++ + spi3_clk: clk@01c200d4 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-mod0-clk"; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/135-pinctrl-fixes.patch b/target/linux/sunxi/patches-3.14/135-pinctrl-fixes.patch new file mode 100644 index 0000000000..e653fe900f --- /dev/null +++ b/target/linux/sunxi/patches-3.14/135-pinctrl-fixes.patch @@ -0,0 +1,101 @@ +From 68a7d9940935cb71440a9ff384e5859592b0dbfd Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 14 Dec 2013 17:20:13 +0100 +Subject: [PATCH] pinctrl-sunxi: Fix sun5i-a13 port F multiplexing + +The correct value for selecting the mmc0 function on port F pins is 2 not 4, +as per the data-sheet: +http://dl.linux-sunxi.org/A13/A13%20Datasheet%20-%20v1.12%20%282012-03-29%29.pdf + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + drivers/pinctrl/pinctrl-sunxi-pins.h | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-sunxi-pins.h b/drivers/pinctrl/pinctrl-sunxi-pins.h +index 6fd8d4d..3d60669 100644 +--- a/drivers/pinctrl/pinctrl-sunxi-pins.h ++++ b/drivers/pinctrl/pinctrl-sunxi-pins.h +@@ -1932,27 +1932,27 @@ static const struct sunxi_desc_pin sun5i_a13_pins[] = { + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), +- SUNXI_FUNCTION(0x4, "mmc0")), /* D1 */ ++ SUNXI_FUNCTION(0x2, "mmc0")), /* D1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), +- SUNXI_FUNCTION(0x4, "mmc0")), /* D0 */ ++ SUNXI_FUNCTION(0x2, "mmc0")), /* D0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), +- SUNXI_FUNCTION(0x4, "mmc0")), /* CLK */ ++ SUNXI_FUNCTION(0x2, "mmc0")), /* CLK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), +- SUNXI_FUNCTION(0x4, "mmc0")), /* CMD */ ++ SUNXI_FUNCTION(0x2, "mmc0")), /* CMD */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), +- SUNXI_FUNCTION(0x4, "mmc0")), /* D3 */ ++ SUNXI_FUNCTION(0x2, "mmc0")), /* D3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), +- SUNXI_FUNCTION(0x4, "mmc0")), /* D2 */ ++ SUNXI_FUNCTION(0x2, "mmc0")), /* D2 */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG0, + SUNXI_FUNCTION(0x0, "gpio_in"), +-- +2.0.3 + +From 8d2c11e63a3302bbbdac41fc765c881da8a8eb37 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 15 Feb 2014 12:58:17 +0100 +Subject: [PATCH] pinctrl-sunxi: Fix hang on gpio irq + +Our irq handler was missing chained_irq_enter / exit calls, causing a +hard hang as soon as a gpio irq happened. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + drivers/pinctrl/pinctrl-sunxi.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/pinctrl/pinctrl-sunxi.c b/drivers/pinctrl/pinctrl-sunxi.c +index f9fabe9..d16da53 100644 +--- a/drivers/pinctrl/pinctrl-sunxi.c ++++ b/drivers/pinctrl/pinctrl-sunxi.c +@@ -13,6 +13,7 @@ + #include <linux/io.h> + #include <linux/clk.h> + #include <linux/gpio.h> ++#include <linux/irqchip/chained_irq.h> + #include <linux/irqdomain.h> + #include <linux/irqchip/chained_irq.h> + #include <linux/module.h> +@@ -670,6 +671,8 @@ static void sunxi_pinctrl_irq_handler(unsigned irq, struct irq_desc *desc) + struct sunxi_pinctrl *pctl = irq_get_handler_data(irq); + const unsigned long reg = readl(pctl->membase + IRQ_STATUS_REG); + ++ chained_irq_enter(chip, desc); ++ + /* Clear all interrupts */ + writel(reg, pctl->membase + IRQ_STATUS_REG); + +@@ -683,6 +686,7 @@ static void sunxi_pinctrl_irq_handler(unsigned irq, struct irq_desc *desc) + } + chained_irq_exit(chip, desc); + } ++ chained_irq_exit(chip, desc); + } + + static struct of_device_id sunxi_pinctrl_match[] = { +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/136-1-irqchip-sun4i-fixes.patch b/target/linux/sunxi/patches-3.14/136-1-irqchip-sun4i-fixes.patch new file mode 100644 index 0000000000..5a8c83c5e1 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/136-1-irqchip-sun4i-fixes.patch @@ -0,0 +1,415 @@ +From 843da234cfc0e7014f9e2da82786a485e0820665 Mon Sep 17 00:00:00 2001 +From: Thomas Gleixner <tglx@linutronix.de> +Date: Thu, 13 Mar 2014 15:32:05 +0100 +Subject: [PATCH] irq: Add a new IRQCHIP_EOI_THREADED flag + +This flag must be used in combination with handle_fasteoi_irq, when set +handle_fasteoi_irq will delay the calling of chip->irq_eoi until the threaded +handler has run. + +Reviewed-by: Hans de Goede <hdegoede@redhat.com> +Tested-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + include/linux/irq.h | 3 +++ + kernel/irq/chip.c | 48 ++++++++++++++++++++++++++++++++++++++++-------- + kernel/irq/internals.h | 1 + + kernel/irq/manage.c | 2 +- + 4 files changed, 45 insertions(+), 9 deletions(-) + +diff --git a/include/linux/irq.h b/include/linux/irq.h +index 7dc1003..0f036fb 100644 +--- a/include/linux/irq.h ++++ b/include/linux/irq.h +@@ -349,6 +349,8 @@ struct irq_chip { + * IRQCHIP_ONOFFLINE_ENABLED: Only call irq_on/off_line callbacks + * when irq enabled + * IRQCHIP_SKIP_SET_WAKE: Skip chip.irq_set_wake(), for this irq chip ++ * IRQCHIP_ONESHOT_SAFE: One shot does not require mask/unmask ++ * IRQCHIP_EOI_THREADED: Chip requires eoi() on unmask in threaded mode + */ + enum { + IRQCHIP_SET_TYPE_MASKED = (1 << 0), +@@ -357,6 +359,7 @@ enum { + IRQCHIP_ONOFFLINE_ENABLED = (1 << 3), + IRQCHIP_SKIP_SET_WAKE = (1 << 4), + IRQCHIP_ONESHOT_SAFE = (1 << 5), ++ IRQCHIP_EOI_THREADED = (1 << 6), + }; + + /* This include will go away once we isolated irq_desc usage to core code */ +diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c +index dc04c16..6397df2 100644 +--- a/kernel/irq/chip.c ++++ b/kernel/irq/chip.c +@@ -281,6 +281,19 @@ void unmask_irq(struct irq_desc *desc) + } + } + ++void unmask_threaded_irq(struct irq_desc *desc) ++{ ++ struct irq_chip *chip = desc->irq_data.chip; ++ ++ if (chip->flags & IRQCHIP_EOI_THREADED) ++ chip->irq_eoi(&desc->irq_data); ++ ++ if (chip->irq_unmask) { ++ chip->irq_unmask(&desc->irq_data); ++ irq_state_clr_masked(desc); ++ } ++} ++ + /* + * handle_nested_irq - Handle a nested irq from a irq thread + * @irq: the interrupt number +@@ -435,6 +448,27 @@ static inline void preflow_handler(struct irq_desc *desc) + static inline void preflow_handler(struct irq_desc *desc) { } + #endif + ++static void cond_unmask_eoi_irq(struct irq_desc *desc, struct irq_chip *chip) ++{ ++ if (!(desc->istate & IRQS_ONESHOT)) { ++ chip->irq_eoi(&desc->irq_data); ++ return; ++ } ++ /* ++ * We need to unmask in the following cases: ++ * - Oneshot irq which did not wake the thread (caused by a ++ * spurious interrupt or a primary handler handling it ++ * completely). ++ */ ++ if (!irqd_irq_disabled(&desc->irq_data) && ++ irqd_irq_masked(&desc->irq_data) && !desc->threads_oneshot) { ++ chip->irq_eoi(&desc->irq_data); ++ unmask_irq(desc); ++ } else if (!(chip->flags & IRQCHIP_EOI_THREADED)) { ++ chip->irq_eoi(&desc->irq_data); ++ } ++} ++ + /** + * handle_fasteoi_irq - irq handler for transparent controllers + * @irq: the interrupt number +@@ -448,6 +482,8 @@ static inline void preflow_handler(struct irq_desc *desc) { } + void + handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc) + { ++ struct irq_chip *chip = desc->irq_data.chip; ++ + raw_spin_lock(&desc->lock); + + if (unlikely(irqd_irq_inprogress(&desc->irq_data))) +@@ -473,18 +509,14 @@ handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc) + preflow_handler(desc); + handle_irq_event(desc); + +- if (desc->istate & IRQS_ONESHOT) +- cond_unmask_irq(desc); ++ cond_unmask_eoi_irq(desc, chip); + +-out_eoi: +- desc->irq_data.chip->irq_eoi(&desc->irq_data); +-out_unlock: + raw_spin_unlock(&desc->lock); + return; + out: +- if (!(desc->irq_data.chip->flags & IRQCHIP_EOI_IF_HANDLED)) +- goto out_eoi; +- goto out_unlock; ++ if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED)) ++ chip->irq_eoi(&desc->irq_data); ++ raw_spin_unlock(&desc->lock); + } + + /** +diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h +index 001fa5b..e98bb56 100644 +--- a/kernel/irq/internals.h ++++ b/kernel/irq/internals.h +@@ -73,6 +73,7 @@ extern void irq_percpu_enable(struct irq_desc *desc, unsigned int cpu); + extern void irq_percpu_disable(struct irq_desc *desc, unsigned int cpu); + extern void mask_irq(struct irq_desc *desc); + extern void unmask_irq(struct irq_desc *desc); ++extern void unmask_threaded_irq(struct irq_desc *desc); + + extern void init_kstat_irqs(struct irq_desc *desc, int node, int nr); + +diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c +index d3bf660..7593958 100644 +--- a/kernel/irq/manage.c ++++ b/kernel/irq/manage.c +@@ -718,7 +718,7 @@ static void irq_finalize_oneshot(struct irq_desc *desc, + + if (!desc->threads_oneshot && !irqd_irq_disabled(&desc->irq_data) && + irqd_irq_masked(&desc->irq_data)) +- unmask_irq(desc); ++ unmask_threaded_irq(desc); + + out_unlock: + raw_spin_unlock_irq(&desc->lock); +-- +2.0.3 + +From d000f9a5348e6d6c8b620a9c2d0b97c69d6d6153 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Tue, 11 Mar 2014 16:47:46 +0100 +Subject: [PATCH] irqchip: sun4i: Fix irq 0 not working + +SUN4I_IRQ_VECTOR_REG containing 0 can mean one of 3 things: +1) no more irqs pending +2) irq 0 pending +3) spurious irq + +So if we immediately get a reading of 0, check the irq-pending reg +to differentiate between 2 and 3. We only do this once to avoid +the extra check in the common case of 1) hapening after having +read the vector-reg once. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + drivers/irqchip/irq-sun4i.c | 18 ++++++++++++++++-- + 1 file changed, 16 insertions(+), 2 deletions(-) + +diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c +index a5438d8..5c25048 100644 +--- a/drivers/irqchip/irq-sun4i.c ++++ b/drivers/irqchip/irq-sun4i.c +@@ -140,10 +140,24 @@ static asmlinkage void __exception_irq_entry sun4i_handle_irq(struct pt_regs *re + { + u32 irq, hwirq; + ++ /* ++ * hwirq == 0 can mean one of 3 things: ++ * 1) no more irqs pending ++ * 2) irq 0 pending ++ * 3) spurious irq ++ * So if we immediately get a reading of 0, check the irq-pending reg ++ * to differentiate between 2 and 3. We only do this once to avoid ++ * the extra check in the common case of 1 hapening after having ++ * read the vector-reg once. ++ */ + hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; +- while (hwirq != 0) { ++ if (hwirq == 0 && ++ !(readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0)) & BIT(0))) ++ return; ++ ++ do { + irq = irq_find_mapping(sun4i_irq_domain, hwirq); + handle_IRQ(irq, regs); + hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; +- } ++ } while (hwirq != 0); + } +-- +2.0.3 + +From b37587009473582d9fc080e8b8b99b67b0077a90 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Tue, 11 Mar 2014 16:53:23 +0100 +Subject: [PATCH] irqchip: sun4i: Fix a comment about mask register + initialization + +The comment was claiming that we were masking all irqs, while the code actually +*un*masks all of them. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + drivers/irqchip/irq-sun4i.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c +index 5c25048..8a2fbee 100644 +--- a/drivers/irqchip/irq-sun4i.c ++++ b/drivers/irqchip/irq-sun4i.c +@@ -109,7 +109,7 @@ static int __init sun4i_of_init(struct device_node *node, + writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(1)); + writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(2)); + +- /* Mask all the interrupts */ ++ /* Unmask all the interrupts, ENABLE_REG(x) is used for masking */ + writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(0)); + writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(1)); + writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(2)); +-- +2.0.3 + +From c8865ee82b74b2d95339370972a0d9bfdbac09cf Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Wed, 12 Mar 2014 17:43:45 +0100 +Subject: [PATCH] irqchip: sun4i: Don't ack IRQs != 0, fix acking of IRQ 0 + +All IRQs except for IRQ 0 seem to not need acking, so drop acking for them. + +The ENMI needs to have the ack done *after* clearing the interrupt source, +otherwise we will get a spurious interrupt for each real interrupt. + +So use the new IRQCHIP_EOI_THREADED flag for this in combination with +handle_fasteoi_irq. This uses a separate irq_chip struct for IRQ 0, +since we only want this behavior for IRQ 0. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + drivers/irqchip/irq-sun4i.c | 19 ++++++++++++++++--- + 1 file changed, 16 insertions(+), 3 deletions(-) + +diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c +index 8a2fbee..a0ed1ea 100644 +--- a/drivers/irqchip/irq-sun4i.c ++++ b/drivers/irqchip/irq-sun4i.c +@@ -76,16 +76,29 @@ static void sun4i_irq_unmask(struct irq_data *irqd) + + static struct irq_chip sun4i_irq_chip = { + .name = "sun4i_irq", +- .irq_ack = sun4i_irq_ack, + .irq_mask = sun4i_irq_mask, + .irq_unmask = sun4i_irq_unmask, + }; + ++/* IRQ 0 / the ENMI needs a late eoi call */ ++static struct irq_chip sun4i_irq_chip_enmi = { ++ .name = "sun4i_irq", ++ .irq_eoi = sun4i_irq_ack, ++ .irq_mask = sun4i_irq_mask, ++ .irq_unmask = sun4i_irq_unmask, ++ .flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED, ++}; ++ + static int sun4i_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) + { +- irq_set_chip_and_handler(virq, &sun4i_irq_chip, +- handle_level_irq); ++ if (hw == 0) ++ irq_set_chip_and_handler(virq, &sun4i_irq_chip_enmi, ++ handle_fasteoi_irq); ++ else ++ irq_set_chip_and_handler(virq, &sun4i_irq_chip, ++ handle_level_irq); ++ + set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); + + return 0; +-- +2.0.3 + +From f8b4347aa12d7a30aa1d3e5bfcdccece52d17af3 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Thu, 13 Mar 2014 19:38:26 +0100 +Subject: [PATCH] irqchip: sun4i: Use handle_fasteoi_irq for all interrupts + +Since the sun4i irq chip does not require any action and clears the interrupt +when the level goes back to inactive, we don't need to mask / unmask for +non oneshot IRQs, to achieve this we make sun4i_irq_ack a nop for all irqs +except irq 0 and use handle_fasteoi_irq for all interrupts. + +Now there might be a case when the device reactivates the interrupt +before the RETI. But that does not matter as we run the primary +interrupt handlers with interrupts disabled. + +This also allows us to get rid of needing to use 2 irq_chip structs, this +means that the IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED will now influence +all interrupts rather then just irq 0, but that does not matter as the eoi +is now a nop anyways for all interrupts but irq 0. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + drivers/irqchip/irq-sun4i.c | 18 ++++-------------- + 1 file changed, 4 insertions(+), 14 deletions(-) + +diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c +index a0ed1ea..6a8c88d 100644 +--- a/drivers/irqchip/irq-sun4i.c ++++ b/drivers/irqchip/irq-sun4i.c +@@ -45,6 +45,9 @@ static void sun4i_irq_ack(struct irq_data *irqd) + int reg = irq / 32; + u32 val; + ++ if (irq != 0) ++ return; /* Only IRQ 0 / the ENMI needs to be acked */ ++ + val = readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); + writel(val | (1 << irq_off), + sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); +@@ -76,13 +79,6 @@ static void sun4i_irq_unmask(struct irq_data *irqd) + + static struct irq_chip sun4i_irq_chip = { + .name = "sun4i_irq", +- .irq_mask = sun4i_irq_mask, +- .irq_unmask = sun4i_irq_unmask, +-}; +- +-/* IRQ 0 / the ENMI needs a late eoi call */ +-static struct irq_chip sun4i_irq_chip_enmi = { +- .name = "sun4i_irq", + .irq_eoi = sun4i_irq_ack, + .irq_mask = sun4i_irq_mask, + .irq_unmask = sun4i_irq_unmask, +@@ -92,13 +88,7 @@ static struct irq_chip sun4i_irq_chip_enmi = { + static int sun4i_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) + { +- if (hw == 0) +- irq_set_chip_and_handler(virq, &sun4i_irq_chip_enmi, +- handle_fasteoi_irq); +- else +- irq_set_chip_and_handler(virq, &sun4i_irq_chip, +- handle_level_irq); +- ++ irq_set_chip_and_handler(virq, &sun4i_irq_chip, handle_fasteoi_irq); + set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); + + return 0; +-- +2.0.3 + +From de39bc31eaa554bd044e6adefacd3da6da5bf6e3 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Thu, 13 Mar 2014 20:41:20 +0100 +Subject: [PATCH] irqchip: sun4i: simplify sun4i_irq_ack + +Now that we only ack irq 0 the code can be simplified a lot. + +Also switch from read / modify / write to a simple write clear: +1) This is what the android code does (it has a hack for acking irq 0 + in its unmask code doing this) +2) read / modify / write simply does not make sense for an irq status + register like this, if the other bits are writeable (and the data sheet says + they are not) they should be write 1 to clear, since otherwise a read / + modify / write can race with a device raising an interrupt and then clear + the pending bit unintentionally + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + drivers/irqchip/irq-sun4i.c | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c +index 6a8c88d..75615b5 100644 +--- a/drivers/irqchip/irq-sun4i.c ++++ b/drivers/irqchip/irq-sun4i.c +@@ -41,16 +41,11 @@ static asmlinkage void __exception_irq_entry sun4i_handle_irq(struct pt_regs *re + static void sun4i_irq_ack(struct irq_data *irqd) + { + unsigned int irq = irqd_to_hwirq(irqd); +- unsigned int irq_off = irq % 32; +- int reg = irq / 32; +- u32 val; + + if (irq != 0) + return; /* Only IRQ 0 / the ENMI needs to be acked */ + +- val = readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); +- writel(val | (1 << irq_off), +- sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); ++ writel(BIT(0), sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0)); + } + + static void sun4i_irq_mask(struct irq_data *irqd) +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/137-1-pinctrl-create-irq-pin-mapping.patch b/target/linux/sunxi/patches-3.14/137-1-pinctrl-create-irq-pin-mapping.patch new file mode 100644 index 0000000000..cd209cdb59 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/137-1-pinctrl-create-irq-pin-mapping.patch @@ -0,0 +1,41 @@ +From 4c228d02d1339a286e259893062ea445be82b573 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Tue, 7 Jan 2014 18:56:29 +0800 +Subject: [PATCH] pinctrl: sunxi: create irq/pin mapping during init + +The irq/pin mapping is used to lookup the pin to mux to the irq +function when the irq is enabled. It is created when gpio_to_irq +is called. Creating the mapping during init allows us to map the +interrupts directly from the device tree. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +--- + drivers/pinctrl/pinctrl-sunxi.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-sunxi.c b/drivers/pinctrl/pinctrl-sunxi.c +index d16da53..067e7e10 100644 +--- a/drivers/pinctrl/pinctrl-sunxi.c ++++ b/drivers/pinctrl/pinctrl-sunxi.c +@@ -531,8 +531,6 @@ static int sunxi_pinctrl_gpio_to_irq(struct gpio_chip *chip, unsigned offset) + if (!desc) + return -EINVAL; + +- pctl->irq_array[desc->irqnum] = offset; +- + dev_dbg(chip->dev, "%s: request IRQ for GPIO %d, return %d\n", + chip->label, offset + chip->base, desc->irqnum); + +@@ -759,6 +757,9 @@ static int sunxi_pinctrl_build_state(struct platform_device *pdev) + struct sunxi_desc_function *func = pin->functions; + + while (func->name) { ++ /* Create interrupt mapping while we're at it */ ++ if (!strcmp(func->name, "irq")) ++ pctl->irq_array[func->irqnum] = pin->pin.number; + sunxi_pinctrl_add_function(pctl, func->name); + func++; + } +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/137-2-pinctrl-add-IRQCHIP_SKIP_SET_WAKE.patch b/target/linux/sunxi/patches-3.14/137-2-pinctrl-add-IRQCHIP_SKIP_SET_WAKE.patch new file mode 100644 index 0000000000..8db59a454d --- /dev/null +++ b/target/linux/sunxi/patches-3.14/137-2-pinctrl-add-IRQCHIP_SKIP_SET_WAKE.patch @@ -0,0 +1,31 @@ +From 77f1265dd02dfd5dcaa0ebd6d3ea1d131bc095e2 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Tue, 7 Jan 2014 19:01:29 +0800 +Subject: [PATCH] pinctrl: sunxi: add IRQCHIP_SKIP_SET_WAKE flag for pinctrl + irq chip + +The sunxi pinctrl irq chip driver does not support wakeup at the +moment. Adding IRQCHIP_SKIP_SET_WAKE lets the irqs work with drivers +using wakeup. + +Also add a name to the irq chip. +--- + drivers/pinctrl/pinctrl-sunxi.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/pinctrl/pinctrl-sunxi.c b/drivers/pinctrl/pinctrl-sunxi.c +index 067e7e10..d7a41a8 100644 +--- a/drivers/pinctrl/pinctrl-sunxi.c ++++ b/drivers/pinctrl/pinctrl-sunxi.c +@@ -661,6 +661,8 @@ static struct irq_chip sunxi_pinctrl_irq_chip = { + .irq_mask_ack = sunxi_pinctrl_irq_mask_ack, + .irq_unmask = sunxi_pinctrl_irq_unmask, + .irq_set_type = sunxi_pinctrl_irq_set_type, ++ .name = "sunxi-pio", ++ .flags = IRQCHIP_SKIP_SET_WAKE, + }; + + static void sunxi_pinctrl_irq_handler(unsigned irq, struct irq_desc *desc) +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/137-3-dt-sun7i-add-interrupt-cells.patch b/target/linux/sunxi/patches-3.14/137-3-dt-sun7i-add-interrupt-cells.patch new file mode 100644 index 0000000000..31cf5e4852 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/137-3-dt-sun7i-add-interrupt-cells.patch @@ -0,0 +1,31 @@ +From eda34c0692e479dd9cd2b7cb70986ae57f15187f Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Tue, 7 Jan 2014 18:50:01 +0800 +Subject: [PATCH] ARM: dts: sun7i: Add #interrupt-cells to pinctrl node + +The pinctrl device is also an interrupt controller for external +interrupts. Add the missing #interrupt-cells property. + +Also remove the unused #address-cells property. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +--- + arch/arm/boot/dts/sun7i-a20.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 0c57ac5..30b9aba 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -572,7 +572,7 @@ + clocks = <&apb0_gates 5>; + gpio-controller; + interrupt-controller; +- #address-cells = <1>; ++ #interrupt-cells = <2>; + #size-cells = <0>; + #gpio-cells = <3>; + +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/140-dt-sunxi-convert-to-new-clock-compats.patch b/target/linux/sunxi/patches-3.14/140-dt-sunxi-convert-to-new-clock-compats.patch new file mode 100644 index 0000000000..443a65218e --- /dev/null +++ b/target/linux/sunxi/patches-3.14/140-dt-sunxi-convert-to-new-clock-compats.patch @@ -0,0 +1,1030 @@ +From 46b2ee17d7321149b4d48dd86ee2e346624aa141 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Thu, 6 Feb 2014 09:55:58 +0100 +Subject: [PATCH] ARM: sunxi: dt: Convert to the new clock compatibles + +Switch the device tree to the new compatibles introduced in the clock drivers +to have a common pattern accross all Allwinner SoCs. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun4i-a10.dtsi | 60 +++++++++++++++++++-------------------- + arch/arm/boot/dts/sun5i-a10s.dtsi | 48 +++++++++++++++---------------- + arch/arm/boot/dts/sun5i-a13.dtsi | 48 +++++++++++++++---------------- + arch/arm/boot/dts/sun6i-a31.dtsi | 10 +++---- + arch/arm/boot/dts/sun7i-a20.dtsi | 54 +++++++++++++++++------------------ + 5 files changed, 110 insertions(+), 110 deletions(-) + +diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi +index 2d623d0..f6f41d6 100644 +--- a/arch/arm/boot/dts/sun4i-a10.dtsi ++++ b/arch/arm/boot/dts/sun4i-a10.dtsi +@@ -60,7 +60,7 @@ + + osc24M: clk@01c20050 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-osc-clk"; ++ compatible = "allwinner,sun4i-a10-osc-clk"; + reg = <0x01c20050 0x4>; + clock-frequency = <24000000>; + clock-output-names = "osc24M"; +@@ -75,7 +75,7 @@ + + pll1: clk@01c20000 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-pll1-clk"; ++ compatible = "allwinner,sun4i-a10-pll1-clk"; + reg = <0x01c20000 0x4>; + clocks = <&osc24M>; + clock-output-names = "pll1"; +@@ -83,7 +83,7 @@ + + pll4: clk@01c20018 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-pll1-clk"; ++ compatible = "allwinner,sun4i-a10-pll1-clk"; + reg = <0x01c20018 0x4>; + clocks = <&osc24M>; + clock-output-names = "pll4"; +@@ -91,7 +91,7 @@ + + pll5: clk@01c20020 { + #clock-cells = <1>; +- compatible = "allwinner,sun4i-pll5-clk"; ++ compatible = "allwinner,sun4i-a10-pll5-clk"; + reg = <0x01c20020 0x4>; + clocks = <&osc24M>; + clock-output-names = "pll5_ddr", "pll5_other"; +@@ -99,7 +99,7 @@ + + pll6: clk@01c20028 { + #clock-cells = <1>; +- compatible = "allwinner,sun4i-pll6-clk"; ++ compatible = "allwinner,sun4i-a10-pll6-clk"; + reg = <0x01c20028 0x4>; + clocks = <&osc24M>; + clock-output-names = "pll6_sata", "pll6_other", "pll6"; +@@ -108,7 +108,7 @@ + /* dummy is 200M */ + cpu: cpu@01c20054 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-cpu-clk"; ++ compatible = "allwinner,sun4i-a10-cpu-clk"; + reg = <0x01c20054 0x4>; + clocks = <&osc32k>, <&osc24M>, <&pll1>, <&dummy>; + clock-output-names = "cpu"; +@@ -116,7 +116,7 @@ + + axi: axi@01c20054 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-axi-clk"; ++ compatible = "allwinner,sun4i-a10-axi-clk"; + reg = <0x01c20054 0x4>; + clocks = <&cpu>; + clock-output-names = "axi"; +@@ -124,7 +124,7 @@ + + axi_gates: clk@01c2005c { + #clock-cells = <1>; +- compatible = "allwinner,sun4i-axi-gates-clk"; ++ compatible = "allwinner,sun4i-a10-axi-gates-clk"; + reg = <0x01c2005c 0x4>; + clocks = <&axi>; + clock-output-names = "axi_dram"; +@@ -132,7 +132,7 @@ + + ahb: ahb@01c20054 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-ahb-clk"; ++ compatible = "allwinner,sun4i-a10-ahb-clk"; + reg = <0x01c20054 0x4>; + clocks = <&axi>; + clock-output-names = "ahb"; +@@ -140,7 +140,7 @@ + + ahb_gates: clk@01c20060 { + #clock-cells = <1>; +- compatible = "allwinner,sun4i-ahb-gates-clk"; ++ compatible = "allwinner,sun4i-a10-ahb-gates-clk"; + reg = <0x01c20060 0x8>; + clocks = <&ahb>; + clock-output-names = "ahb_usb0", "ahb_ehci0", +@@ -158,7 +158,7 @@ + + apb0: apb0@01c20054 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-apb0-clk"; ++ compatible = "allwinner,sun4i-a10-apb0-clk"; + reg = <0x01c20054 0x4>; + clocks = <&ahb>; + clock-output-names = "apb0"; +@@ -166,7 +166,7 @@ + + apb0_gates: clk@01c20068 { + #clock-cells = <1>; +- compatible = "allwinner,sun4i-apb0-gates-clk"; ++ compatible = "allwinner,sun4i-a10-apb0-gates-clk"; + reg = <0x01c20068 0x4>; + clocks = <&apb0>; + clock-output-names = "apb0_codec", "apb0_spdif", +@@ -176,7 +176,7 @@ + + apb1_mux: apb1_mux@01c20058 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-apb1-mux-clk"; ++ compatible = "allwinner,sun4i-a10-apb1-mux-clk"; + reg = <0x01c20058 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&osc32k>; + clock-output-names = "apb1_mux"; +@@ -184,7 +184,7 @@ + + apb1: apb1@01c20058 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-apb1-clk"; ++ compatible = "allwinner,sun4i-a10-apb1-clk"; + reg = <0x01c20058 0x4>; + clocks = <&apb1_mux>; + clock-output-names = "apb1"; +@@ -192,7 +192,7 @@ + + apb1_gates: clk@01c2006c { + #clock-cells = <1>; +- compatible = "allwinner,sun4i-apb1-gates-clk"; ++ compatible = "allwinner,sun4i-a10-apb1-gates-clk"; + reg = <0x01c2006c 0x4>; + clocks = <&apb1>; + clock-output-names = "apb1_i2c0", "apb1_i2c1", +@@ -205,7 +205,7 @@ + + nand_clk: clk@01c20080 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20080 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "nand"; +@@ -213,7 +213,7 @@ + + ms_clk: clk@01c20084 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20084 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "ms"; +@@ -221,7 +221,7 @@ + + mmc0_clk: clk@01c20088 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20088 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "mmc0"; +@@ -229,7 +229,7 @@ + + mmc1_clk: clk@01c2008c { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c2008c 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "mmc1"; +@@ -237,7 +237,7 @@ + + mmc2_clk: clk@01c20090 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20090 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "mmc2"; +@@ -245,7 +245,7 @@ + + mmc3_clk: clk@01c20094 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20094 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "mmc3"; +@@ -253,7 +253,7 @@ + + ts_clk: clk@01c20098 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20098 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "ts"; +@@ -261,7 +261,7 @@ + + ss_clk: clk@01c2009c { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c2009c 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "ss"; +@@ -269,7 +269,7 @@ + + spi0_clk: clk@01c200a0 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200a0 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "spi0"; +@@ -277,7 +277,7 @@ + + spi1_clk: clk@01c200a4 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200a4 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "spi1"; +@@ -285,7 +285,7 @@ + + spi2_clk: clk@01c200a8 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200a8 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "spi2"; +@@ -293,7 +293,7 @@ + + pata_clk: clk@01c200ac { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200ac 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "pata"; +@@ -301,7 +301,7 @@ + + ir0_clk: clk@01c200b0 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200b0 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "ir0"; +@@ -309,7 +309,7 @@ + + ir1_clk: clk@01c200b4 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200b4 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "ir1"; +@@ -326,7 +326,7 @@ + + spi3_clk: clk@01c200d4 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200d4 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "spi3"; +diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi +index 905317e..df90a29 100644 +--- a/arch/arm/boot/dts/sun5i-a10s.dtsi ++++ b/arch/arm/boot/dts/sun5i-a10s.dtsi +@@ -53,7 +53,7 @@ + + osc24M: clk@01c20050 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-osc-clk"; ++ compatible = "allwinner,sun4i-a10-osc-clk"; + reg = <0x01c20050 0x4>; + clock-frequency = <24000000>; + clock-output-names = "osc24M"; +@@ -68,7 +68,7 @@ + + pll1: clk@01c20000 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-pll1-clk"; ++ compatible = "allwinner,sun4i-a10-pll1-clk"; + reg = <0x01c20000 0x4>; + clocks = <&osc24M>; + clock-output-names = "pll1"; +@@ -76,7 +76,7 @@ + + pll4: clk@01c20018 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-pll1-clk"; ++ compatible = "allwinner,sun4i-a10-pll1-clk"; + reg = <0x01c20018 0x4>; + clocks = <&osc24M>; + clock-output-names = "pll4"; +@@ -84,7 +84,7 @@ + + pll5: clk@01c20020 { + #clock-cells = <1>; +- compatible = "allwinner,sun4i-pll5-clk"; ++ compatible = "allwinner,sun4i-a10-pll5-clk"; + reg = <0x01c20020 0x4>; + clocks = <&osc24M>; + clock-output-names = "pll5_ddr", "pll5_other"; +@@ -92,7 +92,7 @@ + + pll6: clk@01c20028 { + #clock-cells = <1>; +- compatible = "allwinner,sun4i-pll6-clk"; ++ compatible = "allwinner,sun4i-a10-pll6-clk"; + reg = <0x01c20028 0x4>; + clocks = <&osc24M>; + clock-output-names = "pll6_sata", "pll6_other", "pll6"; +@@ -101,7 +101,7 @@ + /* dummy is 200M */ + cpu: cpu@01c20054 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-cpu-clk"; ++ compatible = "allwinner,sun4i-a10-cpu-clk"; + reg = <0x01c20054 0x4>; + clocks = <&osc32k>, <&osc24M>, <&pll1>, <&dummy>; + clock-output-names = "cpu"; +@@ -109,7 +109,7 @@ + + axi: axi@01c20054 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-axi-clk"; ++ compatible = "allwinner,sun4i-a10-axi-clk"; + reg = <0x01c20054 0x4>; + clocks = <&cpu>; + clock-output-names = "axi"; +@@ -117,7 +117,7 @@ + + axi_gates: clk@01c2005c { + #clock-cells = <1>; +- compatible = "allwinner,sun4i-axi-gates-clk"; ++ compatible = "allwinner,sun4i-a10-axi-gates-clk"; + reg = <0x01c2005c 0x4>; + clocks = <&axi>; + clock-output-names = "axi_dram"; +@@ -125,7 +125,7 @@ + + ahb: ahb@01c20054 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-ahb-clk"; ++ compatible = "allwinner,sun4i-a10-ahb-clk"; + reg = <0x01c20054 0x4>; + clocks = <&axi>; + clock-output-names = "ahb"; +@@ -147,7 +147,7 @@ + + apb0: apb0@01c20054 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-apb0-clk"; ++ compatible = "allwinner,sun4i-a10-apb0-clk"; + reg = <0x01c20054 0x4>; + clocks = <&ahb>; + clock-output-names = "apb0"; +@@ -164,7 +164,7 @@ + + apb1_mux: apb1_mux@01c20058 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-apb1-mux-clk"; ++ compatible = "allwinner,sun4i-a10-apb1-mux-clk"; + reg = <0x01c20058 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&osc32k>; + clock-output-names = "apb1_mux"; +@@ -172,7 +172,7 @@ + + apb1: apb1@01c20058 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-apb1-clk"; ++ compatible = "allwinner,sun4i-a10-apb1-clk"; + reg = <0x01c20058 0x4>; + clocks = <&apb1_mux>; + clock-output-names = "apb1"; +@@ -190,7 +190,7 @@ + + nand_clk: clk@01c20080 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20080 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "nand"; +@@ -198,7 +198,7 @@ + + ms_clk: clk@01c20084 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20084 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "ms"; +@@ -206,7 +206,7 @@ + + mmc0_clk: clk@01c20088 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20088 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "mmc0"; +@@ -214,7 +214,7 @@ + + mmc1_clk: clk@01c2008c { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c2008c 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "mmc1"; +@@ -222,7 +222,7 @@ + + mmc2_clk: clk@01c20090 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20090 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "mmc2"; +@@ -230,7 +230,7 @@ + + ts_clk: clk@01c20098 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20098 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "ts"; +@@ -238,7 +238,7 @@ + + ss_clk: clk@01c2009c { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c2009c 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "ss"; +@@ -246,7 +246,7 @@ + + spi0_clk: clk@01c200a0 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200a0 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "spi0"; +@@ -254,7 +254,7 @@ + + spi1_clk: clk@01c200a4 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200a4 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "spi1"; +@@ -262,7 +262,7 @@ + + spi2_clk: clk@01c200a8 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200a8 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "spi2"; +@@ -270,7 +270,7 @@ + + ir0_clk: clk@01c200b0 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200b0 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "ir0"; +@@ -287,7 +287,7 @@ + + mbus_clk: clk@01c2015c { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c2015c 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "mbus"; +diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi +index d196ebc6..24cd86cb 100644 +--- a/arch/arm/boot/dts/sun5i-a13.dtsi ++++ b/arch/arm/boot/dts/sun5i-a13.dtsi +@@ -54,7 +54,7 @@ + + osc24M: clk@01c20050 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-osc-clk"; ++ compatible = "allwinner,sun4i-a10-osc-clk"; + reg = <0x01c20050 0x4>; + clock-frequency = <24000000>; + clock-output-names = "osc24M"; +@@ -69,7 +69,7 @@ + + pll1: clk@01c20000 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-pll1-clk"; ++ compatible = "allwinner,sun4i-a10-pll1-clk"; + reg = <0x01c20000 0x4>; + clocks = <&osc24M>; + clock-output-names = "pll1"; +@@ -77,7 +77,7 @@ + + pll4: clk@01c20018 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-pll1-clk"; ++ compatible = "allwinner,sun4i-a10-pll1-clk"; + reg = <0x01c20018 0x4>; + clocks = <&osc24M>; + clock-output-names = "pll4"; +@@ -85,7 +85,7 @@ + + pll5: clk@01c20020 { + #clock-cells = <1>; +- compatible = "allwinner,sun4i-pll5-clk"; ++ compatible = "allwinner,sun4i-a10-pll5-clk"; + reg = <0x01c20020 0x4>; + clocks = <&osc24M>; + clock-output-names = "pll5_ddr", "pll5_other"; +@@ -93,7 +93,7 @@ + + pll6: clk@01c20028 { + #clock-cells = <1>; +- compatible = "allwinner,sun4i-pll6-clk"; ++ compatible = "allwinner,sun4i-a10-pll6-clk"; + reg = <0x01c20028 0x4>; + clocks = <&osc24M>; + clock-output-names = "pll6_sata", "pll6_other", "pll6"; +@@ -102,7 +102,7 @@ + /* dummy is 200M */ + cpu: cpu@01c20054 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-cpu-clk"; ++ compatible = "allwinner,sun4i-a10-cpu-clk"; + reg = <0x01c20054 0x4>; + clocks = <&osc32k>, <&osc24M>, <&pll1>, <&dummy>; + clock-output-names = "cpu"; +@@ -110,7 +110,7 @@ + + axi: axi@01c20054 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-axi-clk"; ++ compatible = "allwinner,sun4i-a10-axi-clk"; + reg = <0x01c20054 0x4>; + clocks = <&cpu>; + clock-output-names = "axi"; +@@ -118,7 +118,7 @@ + + axi_gates: clk@01c2005c { + #clock-cells = <1>; +- compatible = "allwinner,sun4i-axi-gates-clk"; ++ compatible = "allwinner,sun4i-a10-axi-gates-clk"; + reg = <0x01c2005c 0x4>; + clocks = <&axi>; + clock-output-names = "axi_dram"; +@@ -126,7 +126,7 @@ + + ahb: ahb@01c20054 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-ahb-clk"; ++ compatible = "allwinner,sun4i-a10-ahb-clk"; + reg = <0x01c20054 0x4>; + clocks = <&axi>; + clock-output-names = "ahb"; +@@ -147,7 +147,7 @@ + + apb0: apb0@01c20054 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-apb0-clk"; ++ compatible = "allwinner,sun4i-a10-apb0-clk"; + reg = <0x01c20054 0x4>; + clocks = <&ahb>; + clock-output-names = "apb0"; +@@ -163,7 +163,7 @@ + + apb1_mux: apb1_mux@01c20058 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-apb1-mux-clk"; ++ compatible = "allwinner,sun4i-a10-apb1-mux-clk"; + reg = <0x01c20058 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&osc32k>; + clock-output-names = "apb1_mux"; +@@ -171,7 +171,7 @@ + + apb1: apb1@01c20058 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-apb1-clk"; ++ compatible = "allwinner,sun4i-a10-apb1-clk"; + reg = <0x01c20058 0x4>; + clocks = <&apb1_mux>; + clock-output-names = "apb1"; +@@ -188,7 +188,7 @@ + + nand_clk: clk@01c20080 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20080 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "nand"; +@@ -196,7 +196,7 @@ + + ms_clk: clk@01c20084 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20084 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "ms"; +@@ -204,7 +204,7 @@ + + mmc0_clk: clk@01c20088 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20088 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "mmc0"; +@@ -212,7 +212,7 @@ + + mmc1_clk: clk@01c2008c { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c2008c 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "mmc1"; +@@ -220,7 +220,7 @@ + + mmc2_clk: clk@01c20090 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20090 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "mmc2"; +@@ -228,7 +228,7 @@ + + ts_clk: clk@01c20098 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20098 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "ts"; +@@ -236,7 +236,7 @@ + + ss_clk: clk@01c2009c { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c2009c 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "ss"; +@@ -244,7 +244,7 @@ + + spi0_clk: clk@01c200a0 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200a0 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "spi0"; +@@ -252,7 +252,7 @@ + + spi1_clk: clk@01c200a4 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200a4 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "spi1"; +@@ -260,7 +260,7 @@ + + spi2_clk: clk@01c200a8 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200a8 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "spi2"; +@@ -268,7 +268,7 @@ + + ir0_clk: clk@01c200b0 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200b0 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "ir0"; +@@ -285,7 +285,7 @@ + + mbus_clk: clk@01c2015c { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c2015c 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "mbus"; +diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi +index d3f1995..af6f87c 100644 +--- a/arch/arm/boot/dts/sun6i-a31.dtsi ++++ b/arch/arm/boot/dts/sun6i-a31.dtsi +@@ -95,7 +95,7 @@ + + cpu: cpu@01c20050 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-cpu-clk"; ++ compatible = "allwinner,sun4i-a10-cpu-clk"; + reg = <0x01c20050 0x4>; + + /* +@@ -110,7 +110,7 @@ + + axi: axi@01c20050 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-axi-clk"; ++ compatible = "allwinner,sun4i-a10-axi-clk"; + reg = <0x01c20050 0x4>; + clocks = <&cpu>; + clock-output-names = "axi"; +@@ -126,7 +126,7 @@ + + ahb1: ahb1@01c20054 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-ahb-clk"; ++ compatible = "allwinner,sun4i-a10-ahb-clk"; + reg = <0x01c20054 0x4>; + clocks = <&ahb1_mux>; + clock-output-names = "ahb1"; +@@ -155,7 +155,7 @@ + + apb1: apb1@01c20054 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-apb0-clk"; ++ compatible = "allwinner,sun4i-a10-apb0-clk"; + reg = <0x01c20054 0x4>; + clocks = <&ahb1>; + clock-output-names = "apb1"; +@@ -173,7 +173,7 @@ + + apb2_mux: apb2_mux@01c20058 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-apb1-mux-clk"; ++ compatible = "allwinner,sun4i-a10-apb1-mux-clk"; + reg = <0x01c20058 0x4>; + clocks = <&osc32k>, <&osc24M>, <&pll6>, <&pll6>; + clock-output-names = "apb2_mux"; +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 911d4e4..d00fbf8 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -64,7 +64,7 @@ + + osc24M: clk@01c20050 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-osc-clk"; ++ compatible = "allwinner,sun4i-a10-osc-clk"; + reg = <0x01c20050 0x4>; + clock-frequency = <24000000>; + clock-output-names = "osc24M"; +@@ -79,7 +79,7 @@ + + pll1: clk@01c20000 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-pll1-clk"; ++ compatible = "allwinner,sun4i-a10-pll1-clk"; + reg = <0x01c20000 0x4>; + clocks = <&osc24M>; + clock-output-names = "pll1"; +@@ -87,7 +87,7 @@ + + pll4: clk@01c20018 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-pll1-clk"; ++ compatible = "allwinner,sun4i-a10-pll1-clk"; + reg = <0x01c20018 0x4>; + clocks = <&osc24M>; + clock-output-names = "pll4"; +@@ -95,7 +95,7 @@ + + pll5: clk@01c20020 { + #clock-cells = <1>; +- compatible = "allwinner,sun4i-pll5-clk"; ++ compatible = "allwinner,sun4i-a10-pll5-clk"; + reg = <0x01c20020 0x4>; + clocks = <&osc24M>; + clock-output-names = "pll5_ddr", "pll5_other"; +@@ -103,7 +103,7 @@ + + pll6: clk@01c20028 { + #clock-cells = <1>; +- compatible = "allwinner,sun4i-pll6-clk"; ++ compatible = "allwinner,sun4i-a10-pll6-clk"; + reg = <0x01c20028 0x4>; + clocks = <&osc24M>; + clock-output-names = "pll6_sata", "pll6_other", "pll6"; +@@ -111,7 +111,7 @@ + + cpu: cpu@01c20054 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-cpu-clk"; ++ compatible = "allwinner,sun4i-a10-cpu-clk"; + reg = <0x01c20054 0x4>; + clocks = <&osc32k>, <&osc24M>, <&pll1>, <&pll6 1>; + clock-output-names = "cpu"; +@@ -119,7 +119,7 @@ + + axi: axi@01c20054 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-axi-clk"; ++ compatible = "allwinner,sun4i-a10-axi-clk"; + reg = <0x01c20054 0x4>; + clocks = <&cpu>; + clock-output-names = "axi"; +@@ -127,7 +127,7 @@ + + ahb: ahb@01c20054 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-ahb-clk"; ++ compatible = "allwinner,sun4i-a10-ahb-clk"; + reg = <0x01c20054 0x4>; + clocks = <&axi>; + clock-output-names = "ahb"; +@@ -155,7 +155,7 @@ + + apb0: apb0@01c20054 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-apb0-clk"; ++ compatible = "allwinner,sun4i-a10-apb0-clk"; + reg = <0x01c20054 0x4>; + clocks = <&ahb>; + clock-output-names = "apb0"; +@@ -174,7 +174,7 @@ + + apb1_mux: apb1_mux@01c20058 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-apb1-mux-clk"; ++ compatible = "allwinner,sun4i-a10-apb1-mux-clk"; + reg = <0x01c20058 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&osc32k>; + clock-output-names = "apb1_mux"; +@@ -182,7 +182,7 @@ + + apb1: apb1@01c20058 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-apb1-clk"; ++ compatible = "allwinner,sun4i-a10-apb1-clk"; + reg = <0x01c20058 0x4>; + clocks = <&apb1_mux>; + clock-output-names = "apb1"; +@@ -203,7 +203,7 @@ + + nand_clk: clk@01c20080 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20080 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "nand"; +@@ -211,7 +211,7 @@ + + ms_clk: clk@01c20084 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20084 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "ms"; +@@ -219,7 +219,7 @@ + + mmc0_clk: clk@01c20088 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20088 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "mmc0"; +@@ -227,7 +227,7 @@ + + mmc1_clk: clk@01c2008c { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c2008c 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "mmc1"; +@@ -235,7 +235,7 @@ + + mmc2_clk: clk@01c20090 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20090 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "mmc2"; +@@ -243,7 +243,7 @@ + + mmc3_clk: clk@01c20094 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20094 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "mmc3"; +@@ -251,7 +251,7 @@ + + ts_clk: clk@01c20098 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c20098 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "ts"; +@@ -259,7 +259,7 @@ + + ss_clk: clk@01c2009c { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c2009c 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "ss"; +@@ -267,7 +267,7 @@ + + spi0_clk: clk@01c200a0 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200a0 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "spi0"; +@@ -275,7 +275,7 @@ + + spi1_clk: clk@01c200a4 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200a4 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "spi1"; +@@ -283,7 +283,7 @@ + + spi2_clk: clk@01c200a8 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200a8 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "spi2"; +@@ -291,7 +291,7 @@ + + pata_clk: clk@01c200ac { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200ac 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "pata"; +@@ -299,7 +299,7 @@ + + ir0_clk: clk@01c200b0 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200b0 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "ir0"; +@@ -307,7 +307,7 @@ + + ir1_clk: clk@01c200b4 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200b4 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "ir1"; +@@ -324,7 +324,7 @@ + + spi3_clk: clk@01c200d4 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200d4 0x4>; + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "spi3"; +@@ -332,7 +332,7 @@ + + mbus_clk: clk@01c2015c { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-mod0-clk"; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c2015c 0x4>; + clocks = <&osc24M>, <&pll6 2>, <&pll5 1>; + clock-output-names = "mbus"; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/141-dt-sunxi-add-common-regulator-include.patch b/target/linux/sunxi/patches-3.14/141-dt-sunxi-add-common-regulator-include.patch new file mode 100644 index 0000000000..07dda97efa --- /dev/null +++ b/target/linux/sunxi/patches-3.14/141-dt-sunxi-add-common-regulator-include.patch @@ -0,0 +1,103 @@ +From 6e763a8ebe7a16ae5635ade146fd2930749ed775 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 1 Mar 2014 14:57:56 +0100 +Subject: [PATCH] ARM: sunxi: dt: Add sunxi-common-regulators include file + +Most sunxi boards with a sata connector also have a gpio controlled connector +for sata target power and almost all sunxi boards have a gpio controlled vbus +for usb1 and usb2. + +This commit adds an include file for the regulators representing these +supplies, avoiding the need to copy and paste the regulator code to allmost +all sunxi board dts files. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sunxi-common-regulators.dtsi | 75 ++++++++++++++++++++++++++ + 1 file changed, 75 insertions(+) + create mode 100644 arch/arm/boot/dts/sunxi-common-regulators.dtsi + +diff --git a/arch/arm/boot/dts/sunxi-common-regulators.dtsi b/arch/arm/boot/dts/sunxi-common-regulators.dtsi +new file mode 100644 +index 0000000..18eeac0 +--- /dev/null ++++ b/arch/arm/boot/dts/sunxi-common-regulators.dtsi +@@ -0,0 +1,75 @@ ++/* ++ * sunxi boards common regulator (ahci target power supply, usb-vbus) code ++ * ++ * Copyright 2014 - Hans de Goede <hdegoede@redhat.com> ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/ { ++ soc@01c00000 { ++ pio: pinctrl@01c20800 { ++ ahci_pwr_pin_a: ahci_pwr_pin@0 { ++ allwinner,pins = "PB8"; ++ allwinner,function = "gpio_out"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ ++ usb1_vbus_pin_a: usb1_vbus_pin@0 { ++ allwinner,pins = "PH6"; ++ allwinner,function = "gpio_out"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ ++ usb2_vbus_pin_a: usb2_vbus_pin@0 { ++ allwinner,pins = "PH3"; ++ allwinner,function = "gpio_out"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ }; ++ }; ++ ++ reg_ahci_5v: ahci-5v { ++ compatible = "regulator-fixed"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&ahci_pwr_pin_a>; ++ regulator-name = "ahci-5v"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ enable-active-high; ++ gpio = <&pio 1 8 0>; ++ status = "disabled"; ++ }; ++ ++ reg_usb1_vbus: usb1-vbus { ++ compatible = "regulator-fixed"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&usb1_vbus_pin_a>; ++ regulator-name = "usb1-vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ enable-active-high; ++ gpio = <&pio 7 6 0>; ++ status = "disabled"; ++ }; ++ ++ reg_usb2_vbus: usb2-vbus { ++ compatible = "regulator-fixed"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&usb2_vbus_pin_a>; ++ regulator-name = "usb2-vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ enable-active-high; ++ gpio = <&pio 7 3 0>; ++ status = "disabled"; ++ }; ++}; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/145-1-dt-sun7i-add-a20-spi.patch b/target/linux/sunxi/patches-3.14/145-1-dt-sun7i-add-a20-spi.patch new file mode 100644 index 0000000000..47e342c66a --- /dev/null +++ b/target/linux/sunxi/patches-3.14/145-1-dt-sun7i-add-a20-spi.patch @@ -0,0 +1,78 @@ +From c9bfaadf8973cb4d9074e80c4bf8708deca62712 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Sat, 22 Feb 2014 22:35:54 +0100 +Subject: [PATCH] ARM: dt: sun7i: Add A20 SPI controller nodes + +The A20 has 4 SPI controllers compatible with the one found in the A10. Add +them in the DT. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun7i-a20.dtsi | 44 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 44 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index d00fbf8..0f0ee58 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -401,6 +401,28 @@ + #size-cells = <1>; + ranges; + ++ spi0: spi@01c05000 { ++ compatible = "allwinner,sun4i-a10-spi"; ++ reg = <0x01c05000 0x1000>; ++ interrupts = <0 10 4>; ++ clocks = <&ahb_gates 20>, <&spi0_clk>; ++ clock-names = "ahb", "mod"; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ spi1: spi@01c06000 { ++ compatible = "allwinner,sun4i-a10-spi"; ++ reg = <0x01c06000 0x1000>; ++ interrupts = <0 11 4>; ++ clocks = <&ahb_gates 21>, <&spi1_clk>; ++ clock-names = "ahb", "mod"; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ + emac: ethernet@01c0b000 { + compatible = "allwinner,sun4i-a10-emac"; + reg = <0x01c0b000 0x1000>; +@@ -417,6 +439,28 @@ + #size-cells = <0>; + }; + ++ spi2: spi@01c17000 { ++ compatible = "allwinner,sun4i-a10-spi"; ++ reg = <0x01c17000 0x1000>; ++ interrupts = <0 12 4>; ++ clocks = <&ahb_gates 22>, <&spi2_clk>; ++ clock-names = "ahb", "mod"; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ spi3: spi@01c1f000 { ++ compatible = "allwinner,sun4i-a10-spi"; ++ reg = <0x01c1f000 0x1000>; ++ interrupts = <0 50 4>; ++ clocks = <&ahb_gates 23>, <&spi3_clk>; ++ clock-names = "ahb", "mod"; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ + pio: pinctrl@01c20800 { + compatible = "allwinner,sun7i-a20-pinctrl"; + reg = <0x01c20800 0x400>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/145-2-dt-sun4i-add-a10-spi.patch b/target/linux/sunxi/patches-3.14/145-2-dt-sun4i-add-a10-spi.patch new file mode 100644 index 0000000000..d39b6eebec --- /dev/null +++ b/target/linux/sunxi/patches-3.14/145-2-dt-sun4i-add-a10-spi.patch @@ -0,0 +1,77 @@ +From cac3c4d67c80c4895d0d44e609beb535d66af6a3 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Sat, 22 Feb 2014 22:35:55 +0100 +Subject: [PATCH] ARM: dt: sun4i: Add A10 SPI controller nodes + +The A10 has 4 SPI controllers that are now supported. Add them in the DT. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun4i-a10.dtsi | 44 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 44 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi +index f6f41d6..157bc09 100644 +--- a/arch/arm/boot/dts/sun4i-a10.dtsi ++++ b/arch/arm/boot/dts/sun4i-a10.dtsi +@@ -339,6 +339,28 @@ + #size-cells = <1>; + ranges; + ++ spi0: spi@01c05000 { ++ compatible = "allwinner,sun4i-a10-spi"; ++ reg = <0x01c05000 0x1000>; ++ interrupts = <10>; ++ clocks = <&ahb_gates 20>, <&spi0_clk>; ++ clock-names = "ahb", "mod"; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ spi1: spi@01c06000 { ++ compatible = "allwinner,sun4i-a10-spi"; ++ reg = <0x01c06000 0x1000>; ++ interrupts = <11>; ++ clocks = <&ahb_gates 21>, <&spi1_clk>; ++ clock-names = "ahb", "mod"; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ + emac: ethernet@01c0b000 { + compatible = "allwinner,sun4i-a10-emac"; + reg = <0x01c0b000 0x1000>; +@@ -355,6 +377,28 @@ + #size-cells = <0>; + }; + ++ spi2: spi@01c17000 { ++ compatible = "allwinner,sun4i-a10-spi"; ++ reg = <0x01c17000 0x1000>; ++ interrupts = <12>; ++ clocks = <&ahb_gates 22>, <&spi2_clk>; ++ clock-names = "ahb", "mod"; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ spi3: spi@01c1f000 { ++ compatible = "allwinner,sun4i-a10-spi"; ++ reg = <0x01c1f000 0x1000>; ++ interrupts = <50>; ++ clocks = <&ahb_gates 23>, <&spi3_clk>; ++ clock-names = "ahb", "mod"; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ + intc: interrupt-controller@01c20400 { + compatible = "allwinner,sun4i-ic"; + reg = <0x01c20400 0x400>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/145-3-dt-sun5i-add-a13-spi.patch b/target/linux/sunxi/patches-3.14/145-3-dt-sun5i-add-a13-spi.patch new file mode 100644 index 0000000000..75cbcee7cb --- /dev/null +++ b/target/linux/sunxi/patches-3.14/145-3-dt-sun5i-add-a13-spi.patch @@ -0,0 +1,60 @@ +From aeb3b73fc416e14afd25f25e69f8713488edcc1b Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Sat, 22 Feb 2014 22:35:57 +0100 +Subject: [PATCH] ARM: dt: sun5i: Add A13 SPI controller nodes + +The A13 has 3 SPI controllers compatible with the one found in the A10. Add +them in the DT. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun5i-a13.dtsi | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi +index 24cd86cb..7102d12 100644 +--- a/arch/arm/boot/dts/sun5i-a13.dtsi ++++ b/arch/arm/boot/dts/sun5i-a13.dtsi +@@ -298,6 +298,39 @@ + #size-cells = <1>; + ranges; + ++ spi0: spi@01c05000 { ++ compatible = "allwinner,sun4i-a10-spi"; ++ reg = <0x01c05000 0x1000>; ++ interrupts = <10>; ++ clocks = <&ahb_gates 20>, <&spi0_clk>; ++ clock-names = "ahb", "mod"; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ spi1: spi@01c06000 { ++ compatible = "allwinner,sun4i-a10-spi"; ++ reg = <0x01c06000 0x1000>; ++ interrupts = <11>; ++ clocks = <&ahb_gates 21>, <&spi1_clk>; ++ clock-names = "ahb", "mod"; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ spi2: spi@01c17000 { ++ compatible = "allwinner,sun4i-a10-spi"; ++ reg = <0x01c17000 0x1000>; ++ interrupts = <12>; ++ clocks = <&ahb_gates 22>, <&spi2_clk>; ++ clock-names = "ahb", "mod"; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ + intc: interrupt-controller@01c20400 { + compatible = "allwinner,sun4i-ic"; + reg = <0x01c20400 0x400>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/146-1-spi-add-a31-spi.patch b/target/linux/sunxi/patches-3.14/146-1-spi-add-a31-spi.patch new file mode 100644 index 0000000000..3d52020e5e --- /dev/null +++ b/target/linux/sunxi/patches-3.14/146-1-spi-add-a31-spi.patch @@ -0,0 +1,572 @@ +From 86cb7c7ab176112f8b0031dc7c8d19103ba52277 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Wed, 5 Feb 2014 14:05:05 +0100 +Subject: [PATCH] spi: sunxi: Add Allwinner A31 SPI controller driver + +The Allwinner A31 has a new SPI controller IP compared to the older Allwinner +SoCs. + +It supports DMA, but the driver only does PIO for now, and DMA will be +supported eventually. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + .../devicetree/bindings/spi/spi-sun6i.txt | 24 + + drivers/spi/Kconfig | 6 + + drivers/spi/Makefile | 1 + + drivers/spi/spi-sun6i.c | 483 +++++++++++++++++++++ + 4 files changed, 514 insertions(+) + create mode 100644 Documentation/devicetree/bindings/spi/spi-sun6i.txt + create mode 100644 drivers/spi/spi-sun6i.c + +diff --git a/Documentation/devicetree/bindings/spi/spi-sun6i.txt b/Documentation/devicetree/bindings/spi/spi-sun6i.txt +new file mode 100644 +index 0000000..21de73d +--- /dev/null ++++ b/Documentation/devicetree/bindings/spi/spi-sun6i.txt +@@ -0,0 +1,24 @@ ++Allwinner A31 SPI controller ++ ++Required properties: ++- compatible: Should be "allwinner,sun6i-a31-spi". ++- reg: Should contain register location and length. ++- interrupts: Should contain interrupt. ++- clocks: phandle to the clocks feeding the SPI controller. Two are ++ needed: ++ - "ahb": the gated AHB parent clock ++ - "mod": the parent module clock ++- clock-names: Must contain the clock names described just above ++- resets: phandle to the reset controller asserting this device in ++ reset ++ ++Example: ++ ++spi1: spi@01c69000 { ++ compatible = "allwinner,sun6i-a31-spi"; ++ reg = <0x01c69000 0x1000>; ++ interrupts = <0 66 4>; ++ clocks = <&ahb1_gates 21>, <&spi1_clk>; ++ clock-names = "ahb", "mod"; ++ resets = <&ahb1_rst 21>; ++}; +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 581ee2a..58530d3 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -446,6 +446,12 @@ config SPI_SIRF + help + SPI driver for CSR SiRFprimaII SoCs + ++config SPI_SUN6I ++ tristate "Allwinner A31 SPI controller" ++ depends on ARCH_SUNXI || COMPILE_TEST ++ help ++ This enables using the SPI controller on the Allwinner A31 SoCs. ++ + config SPI_MXS + tristate "Freescale MXS SPI controller" + depends on ARCH_MXS +diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile +index 95af48d..13b6ccf9 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -70,6 +70,7 @@ obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o + obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o + obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o + obj-$(CONFIG_SPI_SIRF) += spi-sirf.o ++obj-$(CONFIG_SPI_SUN6I) += spi-sun6i.o + obj-$(CONFIG_SPI_TEGRA114) += spi-tegra114.o + obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o + obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o +diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c +new file mode 100644 +index 0000000..94d38d0 +--- /dev/null ++++ b/drivers/spi/spi-sun6i.c +@@ -0,0 +1,483 @@ ++/* ++ * Copyright (C) 2012 - 2014 Allwinner Tech ++ * Pan Nan <pannan@allwinnertech.com> ++ * ++ * Copyright (C) 2014 Maxime Ripard ++ * Maxime Ripard <maxime.ripard@free-electrons.com> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <linux/device.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/pm_runtime.h> ++#include <linux/reset.h> ++#include <linux/workqueue.h> ++ ++#include <linux/spi/spi.h> ++ ++#define SUN6I_FIFO_DEPTH 128 ++ ++#define SUN6I_GBL_CTL_REG 0x04 ++#define SUN6I_GBL_CTL_BUS_ENABLE BIT(0) ++#define SUN6I_GBL_CTL_MASTER BIT(1) ++#define SUN6I_GBL_CTL_TP BIT(7) ++#define SUN6I_GBL_CTL_RST BIT(31) ++ ++#define SUN6I_TFR_CTL_REG 0x08 ++#define SUN6I_TFR_CTL_CPHA BIT(0) ++#define SUN6I_TFR_CTL_CPOL BIT(1) ++#define SUN6I_TFR_CTL_SPOL BIT(2) ++#define SUN6I_TFR_CTL_CS_MASK 0x3 ++#define SUN6I_TFR_CTL_CS(cs) (((cs) & SUN6I_TFR_CTL_CS_MASK) << 4) ++#define SUN6I_TFR_CTL_CS_MANUAL BIT(6) ++#define SUN6I_TFR_CTL_CS_LEVEL BIT(7) ++#define SUN6I_TFR_CTL_DHB BIT(8) ++#define SUN6I_TFR_CTL_FBS BIT(12) ++#define SUN6I_TFR_CTL_XCH BIT(31) ++ ++#define SUN6I_INT_CTL_REG 0x10 ++#define SUN6I_INT_CTL_RF_OVF BIT(8) ++#define SUN6I_INT_CTL_TC BIT(12) ++ ++#define SUN6I_INT_STA_REG 0x14 ++ ++#define SUN6I_FIFO_CTL_REG 0x18 ++#define SUN6I_FIFO_CTL_RF_RST BIT(15) ++#define SUN6I_FIFO_CTL_TF_RST BIT(31) ++ ++#define SUN6I_FIFO_STA_REG 0x1c ++#define SUN6I_FIFO_STA_RF_CNT_MASK 0x7f ++#define SUN6I_FIFO_STA_RF_CNT_BITS 0 ++#define SUN6I_FIFO_STA_TF_CNT_MASK 0x7f ++#define SUN6I_FIFO_STA_TF_CNT_BITS 16 ++ ++#define SUN6I_CLK_CTL_REG 0x24 ++#define SUN6I_CLK_CTL_CDR2_MASK 0xff ++#define SUN6I_CLK_CTL_CDR2(div) (((div) & SUN6I_CLK_CTL_CDR2_MASK) << 0) ++#define SUN6I_CLK_CTL_CDR1_MASK 0xf ++#define SUN6I_CLK_CTL_CDR1(div) (((div) & SUN6I_CLK_CTL_CDR1_MASK) << 8) ++#define SUN6I_CLK_CTL_DRS BIT(12) ++ ++#define SUN6I_BURST_CNT_REG 0x30 ++#define SUN6I_BURST_CNT(cnt) ((cnt) & 0xffffff) ++ ++#define SUN6I_XMIT_CNT_REG 0x34 ++#define SUN6I_XMIT_CNT(cnt) ((cnt) & 0xffffff) ++ ++#define SUN6I_BURST_CTL_CNT_REG 0x38 ++#define SUN6I_BURST_CTL_CNT_STC(cnt) ((cnt) & 0xffffff) ++ ++#define SUN6I_TXDATA_REG 0x200 ++#define SUN6I_RXDATA_REG 0x300 ++ ++struct sun6i_spi { ++ struct spi_master *master; ++ void __iomem *base_addr; ++ struct clk *hclk; ++ struct clk *mclk; ++ struct reset_control *rstc; ++ ++ struct completion done; ++ ++ const u8 *tx_buf; ++ u8 *rx_buf; ++ int len; ++}; ++ ++static inline u32 sun6i_spi_read(struct sun6i_spi *sspi, u32 reg) ++{ ++ return readl(sspi->base_addr + reg); ++} ++ ++static inline void sun6i_spi_write(struct sun6i_spi *sspi, u32 reg, u32 value) ++{ ++ writel(value, sspi->base_addr + reg); ++} ++ ++static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi, int len) ++{ ++ u32 reg, cnt; ++ u8 byte; ++ ++ /* See how much data is available */ ++ reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG); ++ reg &= SUN6I_FIFO_STA_RF_CNT_MASK; ++ cnt = reg >> SUN6I_FIFO_STA_RF_CNT_BITS; ++ ++ if (len > cnt) ++ len = cnt; ++ ++ while (len--) { ++ byte = readb(sspi->base_addr + SUN6I_RXDATA_REG); ++ if (sspi->rx_buf) ++ *sspi->rx_buf++ = byte; ++ } ++} ++ ++static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi, int len) ++{ ++ u8 byte; ++ ++ if (len > sspi->len) ++ len = sspi->len; ++ ++ while (len--) { ++ byte = sspi->tx_buf ? *sspi->tx_buf++ : 0; ++ writeb(byte, sspi->base_addr + SUN6I_TXDATA_REG); ++ sspi->len--; ++ } ++} ++ ++static void sun6i_spi_set_cs(struct spi_device *spi, bool enable) ++{ ++ struct sun6i_spi *sspi = spi_master_get_devdata(spi->master); ++ u32 reg; ++ ++ reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); ++ reg &= ~SUN6I_TFR_CTL_CS_MASK; ++ reg |= SUN6I_TFR_CTL_CS(spi->chip_select); ++ ++ if (enable) ++ reg |= SUN6I_TFR_CTL_CS_LEVEL; ++ else ++ reg &= ~SUN6I_TFR_CTL_CS_LEVEL; ++ ++ sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg); ++} ++ ++ ++static int sun6i_spi_transfer_one(struct spi_master *master, ++ struct spi_device *spi, ++ struct spi_transfer *tfr) ++{ ++ struct sun6i_spi *sspi = spi_master_get_devdata(master); ++ unsigned int mclk_rate, div, timeout; ++ unsigned int tx_len = 0; ++ int ret = 0; ++ u32 reg; ++ ++ /* We don't support transfer larger than the FIFO */ ++ if (tfr->len > SUN6I_FIFO_DEPTH) ++ return -EINVAL; ++ ++ reinit_completion(&sspi->done); ++ sspi->tx_buf = tfr->tx_buf; ++ sspi->rx_buf = tfr->rx_buf; ++ sspi->len = tfr->len; ++ ++ /* Clear pending interrupts */ ++ sun6i_spi_write(sspi, SUN6I_INT_STA_REG, ~0); ++ ++ /* Reset FIFO */ ++ sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG, ++ SUN6I_FIFO_CTL_RF_RST | SUN6I_FIFO_CTL_TF_RST); ++ ++ /* ++ * Setup the transfer control register: Chip Select, ++ * polarities, etc. ++ */ ++ reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); ++ ++ if (spi->mode & SPI_CPOL) ++ reg |= SUN6I_TFR_CTL_CPOL; ++ else ++ reg &= ~SUN6I_TFR_CTL_CPOL; ++ ++ if (spi->mode & SPI_CPHA) ++ reg |= SUN6I_TFR_CTL_CPHA; ++ else ++ reg &= ~SUN6I_TFR_CTL_CPHA; ++ ++ if (spi->mode & SPI_LSB_FIRST) ++ reg |= SUN6I_TFR_CTL_FBS; ++ else ++ reg &= ~SUN6I_TFR_CTL_FBS; ++ ++ /* ++ * If it's a TX only transfer, we don't want to fill the RX ++ * FIFO with bogus data ++ */ ++ if (sspi->rx_buf) ++ reg &= ~SUN6I_TFR_CTL_DHB; ++ else ++ reg |= SUN6I_TFR_CTL_DHB; ++ ++ /* We want to control the chip select manually */ ++ reg |= SUN6I_TFR_CTL_CS_MANUAL; ++ ++ sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg); ++ ++ /* Ensure that we have a parent clock fast enough */ ++ mclk_rate = clk_get_rate(sspi->mclk); ++ if (mclk_rate < (2 * spi->max_speed_hz)) { ++ clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz); ++ mclk_rate = clk_get_rate(sspi->mclk); ++ } ++ ++ /* ++ * Setup clock divider. ++ * ++ * We have two choices there. Either we can use the clock ++ * divide rate 1, which is calculated thanks to this formula: ++ * SPI_CLK = MOD_CLK / (2 ^ cdr) ++ * Or we can use CDR2, which is calculated with the formula: ++ * SPI_CLK = MOD_CLK / (2 * (cdr + 1)) ++ * Wether we use the former or the latter is set through the ++ * DRS bit. ++ * ++ * First try CDR2, and if we can't reach the expected ++ * frequency, fall back to CDR1. ++ */ ++ div = mclk_rate / (2 * spi->max_speed_hz); ++ if (div <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) { ++ if (div > 0) ++ div--; ++ ++ reg = SUN6I_CLK_CTL_CDR2(div) | SUN6I_CLK_CTL_DRS; ++ } else { ++ div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz); ++ reg = SUN6I_CLK_CTL_CDR1(div); ++ } ++ ++ sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg); ++ ++ /* Setup the transfer now... */ ++ if (sspi->tx_buf) ++ tx_len = tfr->len; ++ ++ /* Setup the counters */ ++ sun6i_spi_write(sspi, SUN6I_BURST_CNT_REG, SUN6I_BURST_CNT(tfr->len)); ++ sun6i_spi_write(sspi, SUN6I_XMIT_CNT_REG, SUN6I_XMIT_CNT(tx_len)); ++ sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG, ++ SUN6I_BURST_CTL_CNT_STC(tx_len)); ++ ++ /* Fill the TX FIFO */ ++ sun6i_spi_fill_fifo(sspi, SUN6I_FIFO_DEPTH); ++ ++ /* Enable the interrupts */ ++ sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, SUN6I_INT_CTL_TC); ++ ++ /* Start the transfer */ ++ reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); ++ sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH); ++ ++ timeout = wait_for_completion_timeout(&sspi->done, ++ msecs_to_jiffies(1000)); ++ if (!timeout) { ++ ret = -ETIMEDOUT; ++ goto out; ++ } ++ ++ sun6i_spi_drain_fifo(sspi, SUN6I_FIFO_DEPTH); ++ ++out: ++ sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, 0); ++ ++ return ret; ++} ++ ++static irqreturn_t sun6i_spi_handler(int irq, void *dev_id) ++{ ++ struct sun6i_spi *sspi = dev_id; ++ u32 status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG); ++ ++ /* Transfer complete */ ++ if (status & SUN6I_INT_CTL_TC) { ++ sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TC); ++ complete(&sspi->done); ++ return IRQ_HANDLED; ++ } ++ ++ return IRQ_NONE; ++} ++ ++static int sun6i_spi_runtime_resume(struct device *dev) ++{ ++ struct spi_master *master = dev_get_drvdata(dev); ++ struct sun6i_spi *sspi = spi_master_get_devdata(master); ++ int ret; ++ ++ ret = clk_prepare_enable(sspi->hclk); ++ if (ret) { ++ dev_err(dev, "Couldn't enable AHB clock\n"); ++ goto out; ++ } ++ ++ ret = clk_prepare_enable(sspi->mclk); ++ if (ret) { ++ dev_err(dev, "Couldn't enable module clock\n"); ++ goto err; ++ } ++ ++ ret = reset_control_deassert(sspi->rstc); ++ if (ret) { ++ dev_err(dev, "Couldn't deassert the device from reset\n"); ++ goto err2; ++ } ++ ++ sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG, ++ SUN6I_GBL_CTL_BUS_ENABLE | SUN6I_GBL_CTL_MASTER | SUN6I_GBL_CTL_TP); ++ ++ return 0; ++ ++err2: ++ clk_disable_unprepare(sspi->mclk); ++err: ++ clk_disable_unprepare(sspi->hclk); ++out: ++ return ret; ++} ++ ++static int sun6i_spi_runtime_suspend(struct device *dev) ++{ ++ struct spi_master *master = dev_get_drvdata(dev); ++ struct sun6i_spi *sspi = spi_master_get_devdata(master); ++ ++ reset_control_assert(sspi->rstc); ++ clk_disable_unprepare(sspi->mclk); ++ clk_disable_unprepare(sspi->hclk); ++ ++ return 0; ++} ++ ++static int sun6i_spi_probe(struct platform_device *pdev) ++{ ++ struct spi_master *master; ++ struct sun6i_spi *sspi; ++ struct resource *res; ++ int ret = 0, irq; ++ ++ master = spi_alloc_master(&pdev->dev, sizeof(struct sun6i_spi)); ++ if (!master) { ++ dev_err(&pdev->dev, "Unable to allocate SPI Master\n"); ++ return -ENOMEM; ++ } ++ ++ platform_set_drvdata(pdev, master); ++ sspi = spi_master_get_devdata(master); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ sspi->base_addr = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(sspi->base_addr)) { ++ ret = PTR_ERR(sspi->base_addr); ++ goto err_free_master; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "No spi IRQ specified\n"); ++ ret = -ENXIO; ++ goto err_free_master; ++ } ++ ++ ret = devm_request_irq(&pdev->dev, irq, sun6i_spi_handler, ++ 0, "sun6i-spi", sspi); ++ if (ret) { ++ dev_err(&pdev->dev, "Cannot request IRQ\n"); ++ goto err_free_master; ++ } ++ ++ sspi->master = master; ++ master->set_cs = sun6i_spi_set_cs; ++ master->transfer_one = sun6i_spi_transfer_one; ++ master->num_chipselect = 4; ++ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; ++ master->dev.of_node = pdev->dev.of_node; ++ master->auto_runtime_pm = true; ++ ++ sspi->hclk = devm_clk_get(&pdev->dev, "ahb"); ++ if (IS_ERR(sspi->hclk)) { ++ dev_err(&pdev->dev, "Unable to acquire AHB clock\n"); ++ ret = PTR_ERR(sspi->hclk); ++ goto err_free_master; ++ } ++ ++ sspi->mclk = devm_clk_get(&pdev->dev, "mod"); ++ if (IS_ERR(sspi->mclk)) { ++ dev_err(&pdev->dev, "Unable to acquire module clock\n"); ++ ret = PTR_ERR(sspi->mclk); ++ goto err_free_master; ++ } ++ ++ init_completion(&sspi->done); ++ ++ sspi->rstc = devm_reset_control_get(&pdev->dev, NULL); ++ if (IS_ERR(sspi->rstc)) { ++ dev_err(&pdev->dev, "Couldn't get reset controller\n"); ++ ret = PTR_ERR(sspi->rstc); ++ goto err_free_master; ++ } ++ ++ /* ++ * This wake-up/shutdown pattern is to be able to have the ++ * device woken up, even if runtime_pm is disabled ++ */ ++ ret = sun6i_spi_runtime_resume(&pdev->dev); ++ if (ret) { ++ dev_err(&pdev->dev, "Couldn't resume the device\n"); ++ goto err_free_master; ++ } ++ ++ pm_runtime_set_active(&pdev->dev); ++ pm_runtime_enable(&pdev->dev); ++ pm_runtime_idle(&pdev->dev); ++ ++ ret = devm_spi_register_master(&pdev->dev, master); ++ if (ret) { ++ dev_err(&pdev->dev, "cannot register SPI master\n"); ++ goto err_pm_disable; ++ } ++ ++ return 0; ++ ++err_pm_disable: ++ pm_runtime_disable(&pdev->dev); ++ sun6i_spi_runtime_suspend(&pdev->dev); ++err_free_master: ++ spi_master_put(master); ++ return ret; ++} ++ ++static int sun6i_spi_remove(struct platform_device *pdev) ++{ ++ pm_runtime_disable(&pdev->dev); ++ ++ return 0; ++} ++ ++static const struct of_device_id sun6i_spi_match[] = { ++ { .compatible = "allwinner,sun6i-a31-spi", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, sun6i_spi_match); ++ ++static const struct dev_pm_ops sun6i_spi_pm_ops = { ++ .runtime_resume = sun6i_spi_runtime_resume, ++ .runtime_suspend = sun6i_spi_runtime_suspend, ++}; ++ ++static struct platform_driver sun6i_spi_driver = { ++ .probe = sun6i_spi_probe, ++ .remove = sun6i_spi_remove, ++ .driver = { ++ .name = "sun6i-spi", ++ .owner = THIS_MODULE, ++ .of_match_table = sun6i_spi_match, ++ .pm = &sun6i_spi_pm_ops, ++ }, ++}; ++module_platform_driver(sun6i_spi_driver); ++ ++MODULE_AUTHOR("Pan Nan <pannan@allwinnertech.com>"); ++MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); ++MODULE_DESCRIPTION("Allwinner A31 SPI controller driver"); ++MODULE_LICENSE("GPL"); +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/146-2-spi-add-a10-spi.patch b/target/linux/sunxi/patches-3.14/146-2-spi-add-a10-spi.patch new file mode 100644 index 0000000000..38461d4dc0 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/146-2-spi-add-a10-spi.patch @@ -0,0 +1,571 @@ +From 1ae7667375308c27023d793372d6be1f3b89f5b5 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Sat, 22 Feb 2014 22:35:53 +0100 +Subject: [PATCH] spi: sunxi: Add Allwinner A10 SPI controller driver + +The older Allwinner SoCs (A10, A13, A10s and A20) all have the same SPI +controller. + +Unfortunately, this SPI controller, even though quite similar, is significantly +different from the recently supported A31 SPI controller (different registers +offset, split/merged registers, etc.). Supporting both controllers in a single +driver would be unreasonable, hence the addition of a new driver. + +Like its more recent counterpart, it supports DMA, but the driver only does PIO +until we have a dmaengine driver for this platform. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + .../devicetree/bindings/spi/spi-sun4i.txt | 24 ++ + drivers/spi/Kconfig | 6 + + drivers/spi/Makefile | 1 + + drivers/spi/spi-sun4i.c | 477 +++++++++++++++++++++ + 4 files changed, 508 insertions(+) + create mode 100644 Documentation/devicetree/bindings/spi/spi-sun4i.txt + create mode 100644 drivers/spi/spi-sun4i.c + +diff --git a/Documentation/devicetree/bindings/spi/spi-sun4i.txt b/Documentation/devicetree/bindings/spi/spi-sun4i.txt +new file mode 100644 +index 0000000..de827f5 +--- /dev/null ++++ b/Documentation/devicetree/bindings/spi/spi-sun4i.txt +@@ -0,0 +1,24 @@ ++Allwinner A10 SPI controller ++ ++Required properties: ++- compatible: Should be "allwinner,sun4-a10-spi". ++- reg: Should contain register location and length. ++- interrupts: Should contain interrupt. ++- clocks: phandle to the clocks feeding the SPI controller. Two are ++ needed: ++ - "ahb": the gated AHB parent clock ++ - "mod": the parent module clock ++- clock-names: Must contain the clock names described just above ++ ++Example: ++ ++spi1: spi@01c06000 { ++ compatible = "allwinner,sun4i-a10-spi"; ++ reg = <0x01c06000 0x1000>; ++ interrupts = <11>; ++ clocks = <&ahb_gates 21>, <&spi1_clk>; ++ clock-names = "ahb", "mod"; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++}; +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 58530d3..78adfae 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -446,6 +446,12 @@ config SPI_SIRF + help + SPI driver for CSR SiRFprimaII SoCs + ++config SPI_SUN4I ++ tristate "Allwinner A10 SoCs SPI controller" ++ depends on ARCH_SUNXI || COMPILE_TEST ++ help ++ SPI driver for Allwinner sun4i, sun5i and sun7i SoCs ++ + config SPI_SUN6I + tristate "Allwinner A31 SPI controller" + depends on ARCH_SUNXI || COMPILE_TEST +diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile +index 13b6ccf9..65f4993 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -70,6 +70,7 @@ obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o + obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o + obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o + obj-$(CONFIG_SPI_SIRF) += spi-sirf.o ++obj-$(CONFIG_SPI_SUN4I) += spi-sun4i.o + obj-$(CONFIG_SPI_SUN6I) += spi-sun6i.o + obj-$(CONFIG_SPI_TEGRA114) += spi-tegra114.o + obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o +diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c +new file mode 100644 +index 0000000..3f82705 +--- /dev/null ++++ b/drivers/spi/spi-sun4i.c +@@ -0,0 +1,477 @@ ++/* ++ * Copyright (C) 2012 - 2014 Allwinner Tech ++ * Pan Nan <pannan@allwinnertech.com> ++ * ++ * Copyright (C) 2014 Maxime Ripard ++ * Maxime Ripard <maxime.ripard@free-electrons.com> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <linux/device.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/pm_runtime.h> ++#include <linux/workqueue.h> ++ ++#include <linux/spi/spi.h> ++ ++#define SUN4I_FIFO_DEPTH 64 ++ ++#define SUN4I_RXDATA_REG 0x00 ++ ++#define SUN4I_TXDATA_REG 0x04 ++ ++#define SUN4I_CTL_REG 0x08 ++#define SUN4I_CTL_ENABLE BIT(0) ++#define SUN4I_CTL_MASTER BIT(1) ++#define SUN4I_CTL_CPHA BIT(2) ++#define SUN4I_CTL_CPOL BIT(3) ++#define SUN4I_CTL_CS_ACTIVE_LOW BIT(4) ++#define SUN4I_CTL_LMTF BIT(6) ++#define SUN4I_CTL_TF_RST BIT(8) ++#define SUN4I_CTL_RF_RST BIT(9) ++#define SUN4I_CTL_XCH BIT(10) ++#define SUN4I_CTL_CS_MASK 0x3000 ++#define SUN4I_CTL_CS(cs) (((cs) << 12) & SUN4I_CTL_CS_MASK) ++#define SUN4I_CTL_DHB BIT(15) ++#define SUN4I_CTL_CS_MANUAL BIT(16) ++#define SUN4I_CTL_CS_LEVEL BIT(17) ++#define SUN4I_CTL_TP BIT(18) ++ ++#define SUN4I_INT_CTL_REG 0x0c ++#define SUN4I_INT_CTL_TC BIT(16) ++ ++#define SUN4I_INT_STA_REG 0x10 ++ ++#define SUN4I_DMA_CTL_REG 0x14 ++ ++#define SUN4I_WAIT_REG 0x18 ++ ++#define SUN4I_CLK_CTL_REG 0x1c ++#define SUN4I_CLK_CTL_CDR2_MASK 0xff ++#define SUN4I_CLK_CTL_CDR2(div) ((div) & SUN4I_CLK_CTL_CDR2_MASK) ++#define SUN4I_CLK_CTL_CDR1_MASK 0xf ++#define SUN4I_CLK_CTL_CDR1(div) (((div) & SUN4I_CLK_CTL_CDR1_MASK) << 8) ++#define SUN4I_CLK_CTL_DRS BIT(12) ++ ++#define SUN4I_BURST_CNT_REG 0x20 ++#define SUN4I_BURST_CNT(cnt) ((cnt) & 0xffffff) ++ ++#define SUN4I_XMIT_CNT_REG 0x24 ++#define SUN4I_XMIT_CNT(cnt) ((cnt) & 0xffffff) ++ ++#define SUN4I_FIFO_STA_REG 0x28 ++#define SUN4I_FIFO_STA_RF_CNT_MASK 0x7f ++#define SUN4I_FIFO_STA_RF_CNT_BITS 0 ++#define SUN4I_FIFO_STA_TF_CNT_MASK 0x7f ++#define SUN4I_FIFO_STA_TF_CNT_BITS 16 ++ ++struct sun4i_spi { ++ struct spi_master *master; ++ void __iomem *base_addr; ++ struct clk *hclk; ++ struct clk *mclk; ++ ++ struct completion done; ++ ++ const u8 *tx_buf; ++ u8 *rx_buf; ++ int len; ++}; ++ ++static inline u32 sun4i_spi_read(struct sun4i_spi *sspi, u32 reg) ++{ ++ return readl(sspi->base_addr + reg); ++} ++ ++static inline void sun4i_spi_write(struct sun4i_spi *sspi, u32 reg, u32 value) ++{ ++ writel(value, sspi->base_addr + reg); ++} ++ ++static inline void sun4i_spi_drain_fifo(struct sun4i_spi *sspi, int len) ++{ ++ u32 reg, cnt; ++ u8 byte; ++ ++ /* See how much data is available */ ++ reg = sun4i_spi_read(sspi, SUN4I_FIFO_STA_REG); ++ reg &= SUN4I_FIFO_STA_RF_CNT_MASK; ++ cnt = reg >> SUN4I_FIFO_STA_RF_CNT_BITS; ++ ++ if (len > cnt) ++ len = cnt; ++ ++ while (len--) { ++ byte = readb(sspi->base_addr + SUN4I_RXDATA_REG); ++ if (sspi->rx_buf) ++ *sspi->rx_buf++ = byte; ++ } ++} ++ ++static inline void sun4i_spi_fill_fifo(struct sun4i_spi *sspi, int len) ++{ ++ u8 byte; ++ ++ if (len > sspi->len) ++ len = sspi->len; ++ ++ while (len--) { ++ byte = sspi->tx_buf ? *sspi->tx_buf++ : 0; ++ writeb(byte, sspi->base_addr + SUN4I_TXDATA_REG); ++ sspi->len--; ++ } ++} ++ ++static void sun4i_spi_set_cs(struct spi_device *spi, bool enable) ++{ ++ struct sun4i_spi *sspi = spi_master_get_devdata(spi->master); ++ u32 reg; ++ ++ reg = sun4i_spi_read(sspi, SUN4I_CTL_REG); ++ ++ reg &= ~SUN4I_CTL_CS_MASK; ++ reg |= SUN4I_CTL_CS(spi->chip_select); ++ ++ if (enable) ++ reg |= SUN4I_CTL_CS_LEVEL; ++ else ++ reg &= ~SUN4I_CTL_CS_LEVEL; ++ ++ /* ++ * Even though this looks irrelevant since we are supposed to ++ * be controlling the chip select manually, this bit also ++ * controls the levels of the chip select for inactive ++ * devices. ++ * ++ * If we don't set it, the chip select level will go low by ++ * default when the device is idle, which is not really ++ * expected in the common case where the chip select is active ++ * low. ++ */ ++ if (spi->mode & SPI_CS_HIGH) ++ reg &= ~SUN4I_CTL_CS_ACTIVE_LOW; ++ else ++ reg |= SUN4I_CTL_CS_ACTIVE_LOW; ++ ++ sun4i_spi_write(sspi, SUN4I_CTL_REG, reg); ++} ++ ++static int sun4i_spi_transfer_one(struct spi_master *master, ++ struct spi_device *spi, ++ struct spi_transfer *tfr) ++{ ++ struct sun4i_spi *sspi = spi_master_get_devdata(master); ++ unsigned int mclk_rate, div, timeout; ++ unsigned int tx_len = 0; ++ int ret = 0; ++ u32 reg; ++ ++ /* We don't support transfer larger than the FIFO */ ++ if (tfr->len > SUN4I_FIFO_DEPTH) ++ return -EINVAL; ++ ++ reinit_completion(&sspi->done); ++ sspi->tx_buf = tfr->tx_buf; ++ sspi->rx_buf = tfr->rx_buf; ++ sspi->len = tfr->len; ++ ++ /* Clear pending interrupts */ ++ sun4i_spi_write(sspi, SUN4I_INT_STA_REG, ~0); ++ ++ ++ reg = sun4i_spi_read(sspi, SUN4I_CTL_REG); ++ ++ /* Reset FIFOs */ ++ sun4i_spi_write(sspi, SUN4I_CTL_REG, ++ reg | SUN4I_CTL_RF_RST | SUN4I_CTL_TF_RST); ++ ++ /* ++ * Setup the transfer control register: Chip Select, ++ * polarities, etc. ++ */ ++ if (spi->mode & SPI_CPOL) ++ reg |= SUN4I_CTL_CPOL; ++ else ++ reg &= ~SUN4I_CTL_CPOL; ++ ++ if (spi->mode & SPI_CPHA) ++ reg |= SUN4I_CTL_CPHA; ++ else ++ reg &= ~SUN4I_CTL_CPHA; ++ ++ if (spi->mode & SPI_LSB_FIRST) ++ reg |= SUN4I_CTL_LMTF; ++ else ++ reg &= ~SUN4I_CTL_LMTF; ++ ++ ++ /* ++ * If it's a TX only transfer, we don't want to fill the RX ++ * FIFO with bogus data ++ */ ++ if (sspi->rx_buf) ++ reg &= ~SUN4I_CTL_DHB; ++ else ++ reg |= SUN4I_CTL_DHB; ++ ++ /* We want to control the chip select manually */ ++ reg |= SUN4I_CTL_CS_MANUAL; ++ ++ sun4i_spi_write(sspi, SUN4I_CTL_REG, reg); ++ ++ /* Ensure that we have a parent clock fast enough */ ++ mclk_rate = clk_get_rate(sspi->mclk); ++ if (mclk_rate < (2 * spi->max_speed_hz)) { ++ clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz); ++ mclk_rate = clk_get_rate(sspi->mclk); ++ } ++ ++ /* ++ * Setup clock divider. ++ * ++ * We have two choices there. Either we can use the clock ++ * divide rate 1, which is calculated thanks to this formula: ++ * SPI_CLK = MOD_CLK / (2 ^ (cdr + 1)) ++ * Or we can use CDR2, which is calculated with the formula: ++ * SPI_CLK = MOD_CLK / (2 * (cdr + 1)) ++ * Wether we use the former or the latter is set through the ++ * DRS bit. ++ * ++ * First try CDR2, and if we can't reach the expected ++ * frequency, fall back to CDR1. ++ */ ++ div = mclk_rate / (2 * spi->max_speed_hz); ++ if (div <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) { ++ if (div > 0) ++ div--; ++ ++ reg = SUN4I_CLK_CTL_CDR2(div) | SUN4I_CLK_CTL_DRS; ++ } else { ++ div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz); ++ reg = SUN4I_CLK_CTL_CDR1(div); ++ } ++ ++ sun4i_spi_write(sspi, SUN4I_CLK_CTL_REG, reg); ++ ++ /* Setup the transfer now... */ ++ if (sspi->tx_buf) ++ tx_len = tfr->len; ++ ++ /* Setup the counters */ ++ sun4i_spi_write(sspi, SUN4I_BURST_CNT_REG, SUN4I_BURST_CNT(tfr->len)); ++ sun4i_spi_write(sspi, SUN4I_XMIT_CNT_REG, SUN4I_XMIT_CNT(tx_len)); ++ ++ /* Fill the TX FIFO */ ++ sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH); ++ ++ /* Enable the interrupts */ ++ sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, SUN4I_INT_CTL_TC); ++ ++ /* Start the transfer */ ++ reg = sun4i_spi_read(sspi, SUN4I_CTL_REG); ++ sun4i_spi_write(sspi, SUN4I_CTL_REG, reg | SUN4I_CTL_XCH); ++ ++ timeout = wait_for_completion_timeout(&sspi->done, ++ msecs_to_jiffies(1000)); ++ if (!timeout) { ++ ret = -ETIMEDOUT; ++ goto out; ++ } ++ ++ sun4i_spi_drain_fifo(sspi, SUN4I_FIFO_DEPTH); ++ ++out: ++ sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, 0); ++ ++ return ret; ++} ++ ++static irqreturn_t sun4i_spi_handler(int irq, void *dev_id) ++{ ++ struct sun4i_spi *sspi = dev_id; ++ u32 status = sun4i_spi_read(sspi, SUN4I_INT_STA_REG); ++ ++ /* Transfer complete */ ++ if (status & SUN4I_INT_CTL_TC) { ++ sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_TC); ++ complete(&sspi->done); ++ return IRQ_HANDLED; ++ } ++ ++ return IRQ_NONE; ++} ++ ++static int sun4i_spi_runtime_resume(struct device *dev) ++{ ++ struct spi_master *master = dev_get_drvdata(dev); ++ struct sun4i_spi *sspi = spi_master_get_devdata(master); ++ int ret; ++ ++ ret = clk_prepare_enable(sspi->hclk); ++ if (ret) { ++ dev_err(dev, "Couldn't enable AHB clock\n"); ++ goto out; ++ } ++ ++ ret = clk_prepare_enable(sspi->mclk); ++ if (ret) { ++ dev_err(dev, "Couldn't enable module clock\n"); ++ goto err; ++ } ++ ++ sun4i_spi_write(sspi, SUN4I_CTL_REG, ++ SUN4I_CTL_ENABLE | SUN4I_CTL_MASTER | SUN4I_CTL_TP); ++ ++ return 0; ++ ++err: ++ clk_disable_unprepare(sspi->hclk); ++out: ++ return ret; ++} ++ ++static int sun4i_spi_runtime_suspend(struct device *dev) ++{ ++ struct spi_master *master = dev_get_drvdata(dev); ++ struct sun4i_spi *sspi = spi_master_get_devdata(master); ++ ++ clk_disable_unprepare(sspi->mclk); ++ clk_disable_unprepare(sspi->hclk); ++ ++ return 0; ++} ++ ++static int sun4i_spi_probe(struct platform_device *pdev) ++{ ++ struct spi_master *master; ++ struct sun4i_spi *sspi; ++ struct resource *res; ++ int ret = 0, irq; ++ ++ master = spi_alloc_master(&pdev->dev, sizeof(struct sun4i_spi)); ++ if (!master) { ++ dev_err(&pdev->dev, "Unable to allocate SPI Master\n"); ++ return -ENOMEM; ++ } ++ ++ platform_set_drvdata(pdev, master); ++ sspi = spi_master_get_devdata(master); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ sspi->base_addr = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(sspi->base_addr)) { ++ ret = PTR_ERR(sspi->base_addr); ++ goto err_free_master; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "No spi IRQ specified\n"); ++ ret = -ENXIO; ++ goto err_free_master; ++ } ++ ++ ret = devm_request_irq(&pdev->dev, irq, sun4i_spi_handler, ++ 0, "sun4i-spi", sspi); ++ if (ret) { ++ dev_err(&pdev->dev, "Cannot request IRQ\n"); ++ goto err_free_master; ++ } ++ ++ sspi->master = master; ++ master->set_cs = sun4i_spi_set_cs; ++ master->transfer_one = sun4i_spi_transfer_one; ++ master->num_chipselect = 4; ++ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; ++ master->dev.of_node = pdev->dev.of_node; ++ master->auto_runtime_pm = true; ++ ++ sspi->hclk = devm_clk_get(&pdev->dev, "ahb"); ++ if (IS_ERR(sspi->hclk)) { ++ dev_err(&pdev->dev, "Unable to acquire AHB clock\n"); ++ ret = PTR_ERR(sspi->hclk); ++ goto err_free_master; ++ } ++ ++ sspi->mclk = devm_clk_get(&pdev->dev, "mod"); ++ if (IS_ERR(sspi->mclk)) { ++ dev_err(&pdev->dev, "Unable to acquire module clock\n"); ++ ret = PTR_ERR(sspi->mclk); ++ goto err_free_master; ++ } ++ ++ init_completion(&sspi->done); ++ ++ /* ++ * This wake-up/shutdown pattern is to be able to have the ++ * device woken up, even if runtime_pm is disabled ++ */ ++ ret = sun4i_spi_runtime_resume(&pdev->dev); ++ if (ret) { ++ dev_err(&pdev->dev, "Couldn't resume the device\n"); ++ goto err_free_master; ++ } ++ ++ pm_runtime_set_active(&pdev->dev); ++ pm_runtime_enable(&pdev->dev); ++ pm_runtime_idle(&pdev->dev); ++ ++ ret = devm_spi_register_master(&pdev->dev, master); ++ if (ret) { ++ dev_err(&pdev->dev, "cannot register SPI master\n"); ++ goto err_pm_disable; ++ } ++ ++ return 0; ++ ++err_pm_disable: ++ pm_runtime_disable(&pdev->dev); ++ sun4i_spi_runtime_suspend(&pdev->dev); ++err_free_master: ++ spi_master_put(master); ++ return ret; ++} ++ ++static int sun4i_spi_remove(struct platform_device *pdev) ++{ ++ pm_runtime_disable(&pdev->dev); ++ ++ return 0; ++} ++ ++static const struct of_device_id sun4i_spi_match[] = { ++ { .compatible = "allwinner,sun4i-a10-spi", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, sun4i_spi_match); ++ ++static const struct dev_pm_ops sun4i_spi_pm_ops = { ++ .runtime_resume = sun4i_spi_runtime_resume, ++ .runtime_suspend = sun4i_spi_runtime_suspend, ++}; ++ ++static struct platform_driver sun4i_spi_driver = { ++ .probe = sun4i_spi_probe, ++ .remove = sun4i_spi_remove, ++ .driver = { ++ .name = "sun4i-spi", ++ .owner = THIS_MODULE, ++ .of_match_table = sun4i_spi_match, ++ .pm = &sun4i_spi_pm_ops, ++ }, ++}; ++module_platform_driver(sun4i_spi_driver); ++ ++MODULE_AUTHOR("Pan Nan <pannan@allwinnertech.com>"); ++MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); ++MODULE_DESCRIPTION("Allwinner A1X/A20 SPI controller driver"); ++MODULE_LICENSE("GPL"); +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/147-sun6i-enable-spi-in-defconfig.patch b/target/linux/sunxi/patches-3.14/147-sun6i-enable-spi-in-defconfig.patch new file mode 100644 index 0000000000..2eac6a45cd --- /dev/null +++ b/target/linux/sunxi/patches-3.14/147-sun6i-enable-spi-in-defconfig.patch @@ -0,0 +1,34 @@ +From 3aa7ff0de5bddc825406ffff49dc4a38b13ebac3 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Wed, 5 Feb 2014 14:05:07 +0100 +Subject: [PATCH] ARM: sunxi: Enable A31 SPI and SID in the defconfig + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/configs/sunxi_defconfig | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig +index 3e2259b..b5df4a5 100644 +--- a/arch/arm/configs/sunxi_defconfig ++++ b/arch/arm/configs/sunxi_defconfig +@@ -24,6 +24,7 @@ CONFIG_IP_PNP_BOOTP=y + # CONFIG_WIRELESS is not set + CONFIG_DEVTMPFS=y + CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_EEPROM_SUNXI_SID=y + CONFIG_NETDEVICES=y + CONFIG_SUN4I_EMAC=y + # CONFIG_NET_CADENCE is not set +@@ -48,6 +49,8 @@ CONFIG_I2C=y + # CONFIG_I2C_COMPAT is not set + CONFIG_I2C_CHARDEV=y + CONFIG_I2C_MV64XXX=y ++CONFIG_SPI=y ++CONFIG_SPI_SUN6I=y + CONFIG_GPIO_SYSFS=y + # CONFIG_HWMON is not set + CONFIG_WATCHDOG=y +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/148-dt-sun7i-add-spi-muxing.patch b/target/linux/sunxi/patches-3.14/148-dt-sun7i-add-spi-muxing.patch new file mode 100644 index 0000000000..f554267a5e --- /dev/null +++ b/target/linux/sunxi/patches-3.14/148-dt-sun7i-add-spi-muxing.patch @@ -0,0 +1,38 @@ +From c38fe0f410a59e194ef5e58429658506d853f1b4 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Sat, 22 Feb 2014 22:35:58 +0100 +Subject: [PATCH] ARM: dt: sun7i: Add SPI muxing options + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun7i-a20.dtsi | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 0f0ee58..6161fd8 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -571,6 +571,20 @@ + allwinner,drive = <3>; + allwinner,pull = <0>; + }; ++ ++ spi1_pins_a: spi1@0 { ++ allwinner,pins = "PI16", "PI17", "PI18", "PI19"; ++ allwinner,function = "spi1"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ ++ spi2_pins_a: spi2@0 { ++ allwinner,pins = "PC19", "PC20", "PC21", "PC22"; ++ allwinner,function = "spi2"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; + }; + + timer@01c20c00 { +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/149-dt-sun7i-add-spi-on-olinuxinom.patch b/target/linux/sunxi/patches-3.14/149-dt-sun7i-add-spi-on-olinuxinom.patch new file mode 100644 index 0000000000..e814af5aef --- /dev/null +++ b/target/linux/sunxi/patches-3.14/149-dt-sun7i-add-spi-on-olinuxinom.patch @@ -0,0 +1,46 @@ +From 3f134732aaf4d785532c716f4ef7703d631a510b Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Sat, 22 Feb 2014 22:35:59 +0100 +Subject: [PATCH] ARM: dts: sun7i: Enable the SPI controllers of the + A20-olinuxino-micro + +The A20-Olinuxino-micro has two SPI bus exposed on its UEXT connectors, enable +them. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts +index b02a796..9d98316 100644 +--- a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts ++++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts +@@ -18,7 +18,24 @@ + model = "Olimex A20-Olinuxino Micro"; + compatible = "olimex,a20-olinuxino-micro", "allwinner,sun7i-a20"; + ++ aliases { ++ spi0 = &spi1; ++ spi1 = &spi2; ++ }; ++ + soc@01c00000 { ++ spi1: spi@01c06000 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi1_pins_a>; ++ status = "okay"; ++ }; ++ ++ spi2: spi@01c17000 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi2_pins_a>; ++ status = "okay"; ++ }; ++ + pinctrl@01c20800 { + led_pins_olinuxino: led_pins@0 { + allwinner,pins = "PH2"; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/150-dt-sun4i-add-ahci.patch b/target/linux/sunxi/patches-3.14/150-dt-sun4i-add-ahci.patch new file mode 100644 index 0000000000..f82261d52d --- /dev/null +++ b/target/linux/sunxi/patches-3.14/150-dt-sun4i-add-ahci.patch @@ -0,0 +1,87 @@ +From a9868f7ef1d3828e55de36cfeac2f84a77653a1e Mon Sep 17 00:00:00 2001 +From: Oliver Schinagl <oliver@schinagl.nl> +Date: Tue, 3 Dec 2013 12:10:11 +0100 +Subject: [PATCH] ARM: sun4i: dt: Add ahci / sata support + +This patch adds sunxi sata support to A10 boards that have such a connector. +Some boards also feature a regulator via a GPIO and support for this is also +added. + +Signed-off-by: Olliver Schinagl <oliver@schinagl.nl> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun4i-a10-a1000.dts | 4 ++++ + arch/arm/boot/dts/sun4i-a10-cubieboard.dts | 10 ++++++++++ + arch/arm/boot/dts/sun4i-a10.dtsi | 8 ++++++++ + 3 files changed, 22 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10-a1000.dts b/arch/arm/boot/dts/sun4i-a10-a1000.dts +index cbd2e13..d6ec839 100644 +--- a/arch/arm/boot/dts/sun4i-a10-a1000.dts ++++ b/arch/arm/boot/dts/sun4i-a10-a1000.dts +@@ -35,6 +35,10 @@ + }; + }; + ++ ahci: sata@01c18000 { ++ status = "okay"; ++ }; ++ + pinctrl@01c20800 { + emac_power_pin_a1000: emac_power_pin@0 { + allwinner,pins = "PH15"; +diff --git a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts +index b139ee6..20407ac 100644 +--- a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts ++++ b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts +@@ -12,6 +12,7 @@ + + /dts-v1/; + /include/ "sun4i-a10.dtsi" ++/include/ "sunxi-common-regulators.dtsi" + + / { + model = "Cubietech Cubieboard"; +@@ -33,6 +34,11 @@ + }; + }; + ++ ahci: sata@01c18000 { ++ target-supply = <®_ahci_5v>; ++ status = "okay"; ++ }; ++ + pinctrl@01c20800 { + led_pins_cubieboard: led_pins@0 { + allwinner,pins = "PH20", "PH21"; +@@ -77,4 +83,8 @@ + linux,default-trigger = "heartbeat"; + }; + }; ++ ++ reg_ahci_5v: ahci-5v { ++ status = "okay"; ++ }; + }; +diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi +index 157bc09..18e09b41 100644 +--- a/arch/arm/boot/dts/sun4i-a10.dtsi ++++ b/arch/arm/boot/dts/sun4i-a10.dtsi +@@ -388,6 +388,14 @@ + #size-cells = <0>; + }; + ++ ahci: sata@01c18000 { ++ compatible = "allwinner,sun4i-a10-ahci"; ++ reg = <0x01c18000 0x1000>; ++ interrupts = <56>; ++ clocks = <&pll6 0>, <&ahb_gates 25>; ++ status = "disabled"; ++ }; ++ + spi3: spi@01c1f000 { + compatible = "allwinner,sun4i-a10-spi"; + reg = <0x01c1f000 0x1000>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/151-dt-sun7i-add-ahci.patch b/target/linux/sunxi/patches-3.14/151-dt-sun7i-add-ahci.patch new file mode 100644 index 0000000000..d4685d93d5 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/151-dt-sun7i-add-ahci.patch @@ -0,0 +1,147 @@ +From cf454a47b64ef78ff85b097c8cb404120c14e6a5 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Fri, 3 Jan 2014 10:27:51 +0100 +Subject: [PATCH] ARM: sun7i: dt: Add ahci / sata support + +This patch adds sunxi sata support to A20 boards that have such a connector. +Some boards also feature a regulator via a GPIO and support for this is also +added. + +Signed-off-by: Olliver Schinagl <oliver@schinagl.nl> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun7i-a20-cubieboard2.dts | 10 ++++++++++ + arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 19 +++++++++++++++++++ + arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts | 10 ++++++++++ + arch/arm/boot/dts/sun7i-a20.dtsi | 8 ++++++++ + 4 files changed, 47 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts +index 7bf4935..4bed115 100644 +--- a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts ++++ b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts +@@ -13,12 +13,18 @@ + + /dts-v1/; + /include/ "sun7i-a20.dtsi" ++/include/ "sunxi-common-regulators.dtsi" + + / { + model = "Cubietech Cubieboard2"; + compatible = "cubietech,cubieboard2", "allwinner,sun7i-a20"; + + soc@01c00000 { ++ ahci: sata@01c18000 { ++ target-supply = <®_ahci_5v>; ++ status = "okay"; ++ }; ++ + pinctrl@01c20800 { + led_pins_cubieboard2: led_pins@0 { + allwinner,pins = "PH20", "PH21"; +@@ -74,4 +80,8 @@ + gpios = <&pio 7 20 0>; + }; + }; ++ ++ reg_ahci_5v: ahci-5v { ++ status = "okay"; ++ }; + }; +diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +index 025ce52..ef5fed8 100644 +--- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts ++++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +@@ -13,13 +13,26 @@ + + /dts-v1/; + /include/ "sun7i-a20.dtsi" ++/include/ "sunxi-common-regulators.dtsi" + + / { + model = "Cubietech Cubietruck"; + compatible = "cubietech,cubietruck", "allwinner,sun7i-a20"; + + soc@01c00000 { ++ ahci: sata@01c18000 { ++ target-supply = <®_ahci_5v>; ++ status = "okay"; ++ }; ++ + pinctrl@01c20800 { ++ ahci_pwr_pin_cubietruck: ahci_pwr_pin@1 { ++ allwinner,pins = "PH12"; ++ allwinner,function = "gpio_out"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ + led_pins_cubietruck: led_pins@0 { + allwinner,pins = "PH7", "PH11", "PH20", "PH21"; + allwinner,function = "gpio_out"; +@@ -90,4 +103,10 @@ + gpios = <&pio 7 7 0>; + }; + }; ++ ++ reg_ahci_5v: ahci-5v { ++ pinctrl-0 = <&ahci_pwr_pin_cubietruck>; ++ gpio = <&pio 7 12 0>; ++ status = "okay"; ++ }; + }; +diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts +index 9d98316..c9b0f37 100644 +--- a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts ++++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts +@@ -13,6 +13,7 @@ + + /dts-v1/; + /include/ "sun7i-a20.dtsi" ++/include/ "sunxi-common-regulators.dtsi" + + / { + model = "Olimex A20-Olinuxino Micro"; +@@ -36,6 +37,11 @@ + status = "okay"; + }; + ++ ahci: sata@01c18000 { ++ target-supply = <®_ahci_5v>; ++ status = "okay"; ++ }; ++ + pinctrl@01c20800 { + led_pins_olinuxino: led_pins@0 { + allwinner,pins = "PH2"; +@@ -105,4 +111,8 @@ + default-state = "on"; + }; + }; ++ ++ reg_ahci_5v: ahci-5v { ++ status = "okay"; ++ }; + }; +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 6161fd8..864b7c6 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -450,6 +450,14 @@ + #size-cells = <0>; + }; + ++ ahci: sata@01c18000 { ++ compatible = "allwinner,sun4i-a10-ahci"; ++ reg = <0x01c18000 0x1000>; ++ interrupts = <0 56 4>; ++ clocks = <&pll6 0>, <&ahb_gates 25>; ++ status = "disabled"; ++ }; ++ + spi3: spi@01c1f000 { + compatible = "allwinner,sun4i-a10-spi"; + reg = <0x01c1f000 0x1000>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/155-wdt-add-new-compats.patch b/target/linux/sunxi/patches-3.14/155-wdt-add-new-compats.patch new file mode 100644 index 0000000000..1635025522 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/155-wdt-add-new-compats.patch @@ -0,0 +1,53 @@ +From ac45fe6b0056d1f92b7c7e5f13514b591a6a9caf Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Sun, 2 Feb 2014 14:55:23 +0100 +Subject: [PATCH] wdt: sunxi: Introduce a new compatible for the A10 and A31 + +For historical reasons, the Allwinner A10 compatibles are not following the +patterns used for this other Allwinner SoCs. + +Introduce a new compatible following the usual pattern, and deprecate the olders. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + Documentation/devicetree/bindings/watchdog/sunxi-wdt.txt | 7 ++++--- + drivers/watchdog/sunxi_wdt.c | 1 + + 2 files changed, 5 insertions(+), 3 deletions(-) + +diff --git a/Documentation/devicetree/bindings/watchdog/sunxi-wdt.txt b/Documentation/devicetree/bindings/watchdog/sunxi-wdt.txt +index e39cb26..6e8c937 100644 +--- a/Documentation/devicetree/bindings/watchdog/sunxi-wdt.txt ++++ b/Documentation/devicetree/bindings/watchdog/sunxi-wdt.txt +@@ -2,13 +2,14 @@ Allwinner SoCs Watchdog timer + + Required properties: + +-- compatible : should be "allwinner,<soc-family>-wdt", the currently supported +- SoC families being sun4i and sun6i ++- compatible : should be either "allwinner,sun4i-a10-wdt" or ++ "allwinner,sun6i-a31-wdt" (deprecated: ++ "allwinner,sun4i-wdt", "allwinner,sun6i-wdt") + - reg : Specifies base physical address and size of the registers. + + Example: + + wdt: watchdog@01c20c90 { +- compatible = "allwinner,sun4i-wdt"; ++ compatible = "allwinner,sun4i-a10-wdt"; + reg = <0x01c20c90 0x10>; + }; +diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c +index 76332d89..7c8923d 100644 +--- a/drivers/watchdog/sunxi_wdt.c ++++ b/drivers/watchdog/sunxi_wdt.c +@@ -206,6 +206,7 @@ static void sunxi_wdt_shutdown(struct platform_device *pdev) + + static const struct of_device_id sunxi_wdt_dt_ids[] = { + { .compatible = "allwinner,sun4i-wdt" }, ++ { .compatible = "allwinner,sun4i-a10-wdt" }, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids); +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/156-dt-sunxi-update-wdt-compats.patch b/target/linux/sunxi/patches-3.14/156-dt-sunxi-update-wdt-compats.patch new file mode 100644 index 0000000000..6ac1a7e8f9 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/156-dt-sunxi-update-wdt-compats.patch @@ -0,0 +1,86 @@ +From f722ea226581a75107ef16b46db1b7b5b999c93a Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Sun, 2 Feb 2014 14:55:25 +0100 +Subject: [PATCH] ARM: sunxi: dt: Update the watchdog compatibles + +The watchdog compatibles were following a different pattern than the one found +in the other devices. Now that the driver supports the right pattern, switch to +it in the DT. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun4i-a10.dtsi | 2 +- + arch/arm/boot/dts/sun5i-a10s.dtsi | 2 +- + arch/arm/boot/dts/sun5i-a13.dtsi | 2 +- + arch/arm/boot/dts/sun6i-a31.dtsi | 2 +- + arch/arm/boot/dts/sun7i-a20.dtsi | 2 +- + 5 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi +index 3ce650b..4b90a18 100644 +--- a/arch/arm/boot/dts/sun4i-a10.dtsi ++++ b/arch/arm/boot/dts/sun4i-a10.dtsi +@@ -597,7 +597,7 @@ + }; + + wdt: watchdog@01c20c90 { +- compatible = "allwinner,sun4i-wdt"; ++ compatible = "allwinner,sun4i-a10-wdt"; + reg = <0x01c20c90 0x10>; + }; + +diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi +index 1795c26..45300a6 100644 +--- a/arch/arm/boot/dts/sun5i-a10s.dtsi ++++ b/arch/arm/boot/dts/sun5i-a10s.dtsi +@@ -508,7 +508,7 @@ + }; + + wdt: watchdog@01c20c90 { +- compatible = "allwinner,sun4i-wdt"; ++ compatible = "allwinner,sun4i-a10-wdt"; + reg = <0x01c20c90 0x10>; + }; + +diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi +index 8bc8e14..280ffee 100644 +--- a/arch/arm/boot/dts/sun5i-a13.dtsi ++++ b/arch/arm/boot/dts/sun5i-a13.dtsi +@@ -454,7 +454,7 @@ + }; + + wdt: watchdog@01c20c90 { +- compatible = "allwinner,sun4i-wdt"; ++ compatible = "allwinner,sun4i-a10-wdt"; + reg = <0x01c20c90 0x10>; + }; + +diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi +index e4267bd..8441733 100644 +--- a/arch/arm/boot/dts/sun6i-a31.dtsi ++++ b/arch/arm/boot/dts/sun6i-a31.dtsi +@@ -289,7 +289,7 @@ + }; + + wdt1: watchdog@01c20ca0 { +- compatible = "allwinner,sun6i-wdt"; ++ compatible = "allwinner,sun6i-a31-wdt"; + reg = <0x01c20ca0 0x20>; + }; + +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 30b9aba..4981f5e 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -725,7 +725,7 @@ + }; + + wdt: watchdog@01c20c90 { +- compatible = "allwinner,sun4i-wdt"; ++ compatible = "allwinner,sun4i-a10-wdt"; + reg = <0x01c20c90 0x10>; + }; + +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/160-dt-sun4i-add-usb-host-bindings.patch b/target/linux/sunxi/patches-3.14/160-dt-sun4i-add-usb-host-bindings.patch new file mode 100644 index 0000000000..9ca7345e20 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/160-dt-sun4i-add-usb-host-bindings.patch @@ -0,0 +1,86 @@ +From 991b5f6d2af837b56adfcb3b3a1fe167647b9fdb Mon Sep 17 00:00:00 2001 +From: Roman Byshko <rbyshko@gmail.com> +Date: Wed, 18 Sep 2013 00:30:04 +0200 +Subject: [PATCH] ARM: sun4i: dt: Add USB host bindings + +Add nodes for the usb-phy and ehci- and ohci-usb-host controllers. + +Signed-off-by: Roman Byshko <rbyshko@gmail.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun4i-a10.dtsi | 52 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 52 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi +index 18e09b41..27dc6ee 100644 +--- a/arch/arm/boot/dts/sun4i-a10.dtsi ++++ b/arch/arm/boot/dts/sun4i-a10.dtsi +@@ -377,6 +377,38 @@ + #size-cells = <0>; + }; + ++ usbphy: phy@01c13400 { ++ #phy-cells = <1>; ++ compatible = "allwinner,sun4i-a10-usb-phy"; ++ reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>; ++ reg-names = "phy_ctrl", "pmu1", "pmu2"; ++ clocks = <&usb_clk 8>; ++ clock-names = "usb_phy"; ++ resets = <&usb_clk 1>, <&usb_clk 2>; ++ reset-names = "usb1_reset", "usb2_reset"; ++ status = "disabled"; ++ }; ++ ++ ehci0: usb@01c14000 { ++ compatible = "allwinner,sun4i-a10-ehci", "generic-ehci"; ++ reg = <0x01c14000 0x100>; ++ interrupts = <39>; ++ clocks = <&ahb_gates 1>; ++ phys = <&usbphy 1>; ++ phy-names = "usb"; ++ status = "disabled"; ++ }; ++ ++ ohci0: usb@01c14400 { ++ compatible = "allwinner,sun4i-a10-ohci", "generic-ohci"; ++ reg = <0x01c14400 0x100>; ++ interrupts = <64>; ++ clocks = <&usb_clk 6>, <&ahb_gates 2>; ++ phys = <&usbphy 1>; ++ phy-names = "usb"; ++ status = "disabled"; ++ }; ++ + spi2: spi@01c17000 { + compatible = "allwinner,sun4i-a10-spi"; + reg = <0x01c17000 0x1000>; +@@ -396,6 +428,26 @@ + status = "disabled"; + }; + ++ ehci1: usb@01c1c000 { ++ compatible = "allwinner,sun4i-a10-ehci", "generic-ehci"; ++ reg = <0x01c1c000 0x100>; ++ interrupts = <40>; ++ clocks = <&ahb_gates 3>; ++ phys = <&usbphy 2>; ++ phy-names = "usb"; ++ status = "disabled"; ++ }; ++ ++ ohci1: usb@01c1c400 { ++ compatible = "allwinner,sun4i-a10-ohci", "generic-ohci"; ++ reg = <0x01c1c400 0x100>; ++ interrupts = <65>; ++ clocks = <&usb_clk 7>, <&ahb_gates 4>; ++ phys = <&usbphy 2>; ++ phy-names = "usb"; ++ status = "disabled"; ++ }; ++ + spi3: spi@01c1f000 { + compatible = "allwinner,sun4i-a10-spi"; + reg = <0x01c1f000 0x1000>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/161-dt-sun5i-add-usb-host-bindings.patch b/target/linux/sunxi/patches-3.14/161-dt-sun5i-add-usb-host-bindings.patch new file mode 100644 index 0000000000..1beb5e14d8 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/161-dt-sun5i-add-usb-host-bindings.patch @@ -0,0 +1,62 @@ +From 1b5a1b92147936c5aa2acec1683663b4d22e9ae6 Mon Sep 17 00:00:00 2001 +From: Roman Byshko <rbyshko@gmail.com> +Date: Tue, 24 Sep 2013 20:03:40 +0200 +Subject: [PATCH] ARM: sun5i: dt: Add USB host bindings + +Add nodes for the usb-phy and ehci- and ohci-usb-host controllers. + +Signed-off-by: Roman Byshko <rbyshko@gmail.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun5i-a10s.dtsi | 32 ++++++++++++++++++++++++++++++++ + arch/arm/boot/dts/sun5i-a13.dtsi | 32 ++++++++++++++++++++++++++++++++ + 2 files changed, 64 insertions(+) + +diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi +index b2cb5dc..f34e0d8 100644 +diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi +index 7102d12..0e9c239 100644 +--- a/arch/arm/boot/dts/sun5i-a13.dtsi ++++ b/arch/arm/boot/dts/sun5i-a13.dtsi +@@ -320,6 +320,38 @@ + #size-cells = <0>; + }; + ++ usbphy: phy@01c13400 { ++ #phy-cells = <1>; ++ compatible = "allwinner,sun5i-a13-usb-phy"; ++ reg = <0x01c13400 0x10 0x01c14800 0x4>; ++ reg-names = "phy_ctrl", "pmu1"; ++ clocks = <&usb_clk 8>; ++ clock-names = "usb_phy"; ++ resets = <&usb_clk 1>; ++ reset-names = "usb1_reset"; ++ status = "disabled"; ++ }; ++ ++ ehci0: usb@01c14000 { ++ compatible = "allwinner,sun5i-a13-ehci", "generic-ehci"; ++ reg = <0x01c14000 0x100>; ++ interrupts = <39>; ++ clocks = <&ahb_gates 1>; ++ phys = <&usbphy 1>; ++ phy-names = "usb"; ++ status = "disabled"; ++ }; ++ ++ ohci0: usb@01c14400 { ++ compatible = "allwinner,sun5i-a13-ohci", "generic-ohci"; ++ reg = <0x01c14400 0x100>; ++ interrupts = <40>; ++ clocks = <&usb_clk 6>, <&ahb_gates 2>; ++ phys = <&usbphy 1>; ++ phy-names = "usb"; ++ status = "disabled"; ++ }; ++ + spi2: spi@01c17000 { + compatible = "allwinner,sun4i-a10-spi"; + reg = <0x01c17000 0x1000>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/162-dt-sun7i-add-usb-host-bindings.patch b/target/linux/sunxi/patches-3.14/162-dt-sun7i-add-usb-host-bindings.patch new file mode 100644 index 0000000000..b2b9d33caa --- /dev/null +++ b/target/linux/sunxi/patches-3.14/162-dt-sun7i-add-usb-host-bindings.patch @@ -0,0 +1,86 @@ +From 008dffff2fa751c988671c4fc0c9a404ea808280 Mon Sep 17 00:00:00 2001 +From: Roman Byshko <rbyshko@gmail.com> +Date: Thu, 19 Sep 2013 21:36:10 +0200 +Subject: [PATCH] ARM: sun7i: dt: Add USB host bindings + +Add nodes for the usb-phy and ehci- and ohci-usb-host controllers. + +Signed-off-by: Roman Byshko <rbyshko@gmail.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun7i-a20.dtsi | 52 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 52 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 864b7c6..4cc2f5f 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -439,6 +439,38 @@ + #size-cells = <0>; + }; + ++ usbphy: phy@01c13400 { ++ #phy-cells = <1>; ++ compatible = "allwinner,sun7i-a20-usb-phy"; ++ reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>; ++ reg-names = "phy_ctrl", "pmu1", "pmu2"; ++ clocks = <&usb_clk 8>; ++ clock-names = "usb_phy"; ++ resets = <&usb_clk 1>, <&usb_clk 2>; ++ reset-names = "usb1_reset", "usb2_reset"; ++ status = "disabled"; ++ }; ++ ++ ehci0: usb@01c14000 { ++ compatible = "allwinner,sun7i-a20-ehci", "generic-ehci"; ++ reg = <0x01c14000 0x100>; ++ interrupts = <0 39 4>; ++ clocks = <&ahb_gates 1>; ++ phys = <&usbphy 1>; ++ phy-names = "usb"; ++ status = "disabled"; ++ }; ++ ++ ohci0: usb@01c14400 { ++ compatible = "allwinner,sun7i-a20-ohci", "generic-ohci"; ++ reg = <0x01c14400 0x100>; ++ interrupts = <0 64 4>; ++ clocks = <&usb_clk 6>, <&ahb_gates 2>; ++ phys = <&usbphy 1>; ++ phy-names = "usb"; ++ status = "disabled"; ++ }; ++ + spi2: spi@01c17000 { + compatible = "allwinner,sun4i-a10-spi"; + reg = <0x01c17000 0x1000>; +@@ -458,6 +490,26 @@ + status = "disabled"; + }; + ++ ehci1: usb@01c1c000 { ++ compatible = "allwinner,sun7i-a20-ehci", "generic-ehci"; ++ reg = <0x01c1c000 0x100>; ++ interrupts = <0 40 4>; ++ clocks = <&ahb_gates 3>; ++ phys = <&usbphy 2>; ++ phy-names = "usb"; ++ status = "disabled"; ++ }; ++ ++ ohci1: usb@01c1c400 { ++ compatible = "allwinner,sun7i-a20-ohci", "generic-ohci"; ++ reg = <0x01c1c400 0x100>; ++ interrupts = <0 65 4>; ++ clocks = <&usb_clk 7>, <&ahb_gates 4>; ++ phys = <&usbphy 2>; ++ phy-names = "usb"; ++ status = "disabled"; ++ }; ++ + spi3: spi@01c1f000 { + compatible = "allwinner,sun4i-a10-spi"; + reg = <0x01c1f000 0x1000>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/163-dt-sun4i-add-usb-host-to-boards.patch b/target/linux/sunxi/patches-3.14/163-dt-sun4i-add-usb-host-to-boards.patch new file mode 100644 index 0000000000..220bed603e --- /dev/null +++ b/target/linux/sunxi/patches-3.14/163-dt-sun4i-add-usb-host-to-boards.patch @@ -0,0 +1,295 @@ +From f2509ec45a09013e300460a967f694561d169b98 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 11 Jan 2014 04:47:38 +0100 +Subject: [PATCH] ARM: sun4i: dt: Add USB host nodes to hackberry dts + +Add nodes for the usb-phy and ehci- and ohci-usb-host controllers. + +Based on fex file settings, the fex file also contains a mysterious line: +usb_hub_vcc_en_gpio = port:PB09<1><0><default><0> + +Which also clashes with usbc0, which has: +usb_drv_vbus_gpio = port:PB09<1><0><default><0> + +So if usb does not work properly we need someone with a hackberry to look +closer into this. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun4i-a10-hackberry.dts | 40 +++++++++++++++++++++++++++++++ + 1 file changed, 40 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10-hackberry.dts b/arch/arm/boot/dts/sun4i-a10-hackberry.dts +index 6692d336..d7c17e4 100644 +--- a/arch/arm/boot/dts/sun4i-a10-hackberry.dts ++++ b/arch/arm/boot/dts/sun4i-a10-hackberry.dts +@@ -13,6 +13,7 @@ + + /dts-v1/; + /include/ "sun4i-a10.dtsi" ++/include/ "sunxi-common-regulators.dtsi" + + / { + model = "Miniand Hackberry"; +@@ -35,6 +36,28 @@ + }; + }; + ++ usbphy: phy@01c13400 { ++ usb1_vbus-supply = <®_usb1_vbus>; ++ usb2_vbus-supply = <®_usb2_vbus>; ++ status = "okay"; ++ }; ++ ++ ehci0: usb@01c14000 { ++ status = "okay"; ++ }; ++ ++ ohci0: usb@01c14400 { ++ status = "okay"; ++ }; ++ ++ ehci1: usb@01c1c000 { ++ status = "okay"; ++ }; ++ ++ ohci1: usb@01c1c400 { ++ status = "okay"; ++ }; ++ + pio: pinctrl@01c20800 { + pinctrl-names = "default"; + pinctrl-0 = <&hackberry_hogs>; +@@ -45,6 +68,13 @@ + allwinner,drive = <0>; + allwinner,pull = <0>; + }; ++ ++ usb2_vbus_pin_hackberry: usb2_vbus_pin@0 { ++ allwinner,pins = "PH12"; ++ allwinner,function = "gpio_out"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; + }; + + uart0: serial@01c28000 { +@@ -62,4 +92,14 @@ + enable-active-high; + gpio = <&pio 7 19 0>; + }; ++ ++ reg_usb1_vbus: usb1-vbus { ++ status = "okay"; ++ }; ++ ++ reg_usb2_vbus: usb2-vbus { ++ pinctrl-0 = <&usb2_vbus_pin_hackberry>; ++ gpio = <&pio 7 12 0>; ++ status = "okay"; ++ }; + }; +-- +2.0.3 + +From dbf6ffa0b3832d91c2509e6753f485cedc791051 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 11 Jan 2014 05:15:06 +0100 +Subject: [PATCH] ARM: sun4i: dt: Add USB host nodes to mini-xplus dts + +Add nodes for the usb-phy and ehci- and ohci-usb-host controllers. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun4i-a10-mini-xplus.dts | 31 ++++++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts b/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts +index 70b3323..dd84a9e3 100644 +--- a/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts ++++ b/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts +@@ -13,16 +13,47 @@ + + /dts-v1/; + /include/ "sun4i-a10.dtsi" ++/include/ "sunxi-common-regulators.dtsi" + + / { + model = "PineRiver Mini X-Plus"; + compatible = "pineriver,mini-xplus", "allwinner,sun4i-a10"; + + soc@01c00000 { ++ usbphy: phy@01c13400 { ++ usb1_vbus-supply = <®_usb1_vbus>; ++ usb2_vbus-supply = <®_usb2_vbus>; ++ status = "okay"; ++ }; ++ ++ ehci0: usb@01c14000 { ++ status = "okay"; ++ }; ++ ++ ohci0: usb@01c14400 { ++ status = "okay"; ++ }; ++ ++ ehci1: usb@01c1c000 { ++ status = "okay"; ++ }; ++ ++ ohci1: usb@01c1c400 { ++ status = "okay"; ++ }; ++ + uart0: serial@01c28000 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins_a>; + status = "okay"; + }; + }; ++ ++ reg_usb1_vbus: usb1-vbus { ++ status = "okay"; ++ }; ++ ++ reg_usb2_vbus: usb2-vbus { ++ status = "okay"; ++ }; + }; +-- +2.0.3 + +From 528808ae38fee761be9f3451f51b457cb56d33ee Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Mon, 17 Feb 2014 20:41:04 +0100 +Subject: [PATCH] ARM: sun4i: dt: Add USB host nodes to pcduino.dts + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun4i-a10-pcduino.dts | 31 +++++++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10-pcduino.dts b/arch/arm/boot/dts/sun4i-a10-pcduino.dts +index f5692a3..255b47e 100644 +--- a/arch/arm/boot/dts/sun4i-a10-pcduino.dts ++++ b/arch/arm/boot/dts/sun4i-a10-pcduino.dts +@@ -12,6 +12,7 @@ + + /dts-v1/; + /include/ "sun4i-a10.dtsi" ++/include/ "sunxi-common-regulators.dtsi" + + / { + model = "LinkSprite pcDuino"; +@@ -33,6 +34,28 @@ + }; + }; + ++ usbphy: phy@01c13400 { ++ usb1_vbus-supply = <®_usb1_vbus>; ++ usb2_vbus-supply = <®_usb2_vbus>; ++ status = "okay"; ++ }; ++ ++ ehci0: usb@01c14000 { ++ status = "okay"; ++ }; ++ ++ ohci0: usb@01c14400 { ++ status = "okay"; ++ }; ++ ++ ehci1: usb@01c1c000 { ++ status = "okay"; ++ }; ++ ++ ohci1: usb@01c1c400 { ++ status = "okay"; ++ }; ++ + uart0: serial@01c28000 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins_a>; +@@ -45,4 +68,12 @@ + status = "okay"; + }; + }; ++ ++ reg_usb1_vbus: usb1-vbus { ++ status = "okay"; ++ }; ++ ++ reg_usb2_vbus: usb2-vbus { ++ status = "okay"; ++ }; + }; +-- +2.0.3 + +From 58b778ce8cbc6fdb1fda5a6998fdd114a2b77cc9 Mon Sep 17 00:00:00 2001 +From: Roman Byshko <rbyshko@gmail.com> +Date: Wed, 18 Sep 2013 22:45:06 +0200 +Subject: [PATCH] ARM: sun4i: dt: Add USB host nodes to cubieboard dts + +Add nodes for the usb-phy and ehci- and ohci-usb-host controllers. + +Signed-off-by: Roman Byshko <rbyshko@gmail.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun4i-a10-cubieboard.dts | 30 ++++++++++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts +index 20407ac..4684cbe 100644 +--- a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts ++++ b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts +@@ -34,11 +34,33 @@ + }; + }; + ++ usbphy: phy@01c13400 { ++ usb1_vbus-supply = <®_usb1_vbus>; ++ usb2_vbus-supply = <®_usb2_vbus>; ++ status = "okay"; ++ }; ++ ++ ehci0: usb@01c14000 { ++ status = "okay"; ++ }; ++ ++ ohci0: usb@01c14400 { ++ status = "okay"; ++ }; ++ + ahci: sata@01c18000 { + target-supply = <®_ahci_5v>; + status = "okay"; + }; + ++ ehci1: usb@01c1c000 { ++ status = "okay"; ++ }; ++ ++ ohci1: usb@01c1c400 { ++ status = "okay"; ++ }; ++ + pinctrl@01c20800 { + led_pins_cubieboard: led_pins@0 { + allwinner,pins = "PH20", "PH21"; +@@ -87,4 +109,12 @@ + reg_ahci_5v: ahci-5v { + status = "okay"; + }; ++ ++ reg_usb1_vbus: usb1-vbus { ++ status = "okay"; ++ }; ++ ++ reg_usb2_vbus: usb2-vbus { ++ status = "okay"; ++ }; + }; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/164-dt-sun5i-add-usb-host-to-boards.patch b/target/linux/sunxi/patches-3.14/164-dt-sun5i-add-usb-host-to-boards.patch new file mode 100644 index 0000000000..203b185d3c --- /dev/null +++ b/target/linux/sunxi/patches-3.14/164-dt-sun5i-add-usb-host-to-boards.patch @@ -0,0 +1,143 @@ +From 9f193366e6094f6e2087c4767e7e413a395bf0d2 Mon Sep 17 00:00:00 2001 +From: Roman Byshko <rbyshko@gmail.com> +Date: Tue, 24 Sep 2013 20:07:53 +0200 +Subject: [PATCH] ARM: sun5i: dt: Add USB host nodes to A13-Olinuxino + +Add nodes for the usb-phy and ehci- and ohci-usb-host controllers. + +Signed-off-by: Roman Byshko <rbyshko@gmail.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun5i-a13-olinuxino.dts | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +diff --git a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts +index a4ba5ff..7a9187b 100644 +--- a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts ++++ b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts +@@ -13,12 +13,26 @@ + + /dts-v1/; + /include/ "sun5i-a13.dtsi" ++/include/ "sunxi-common-regulators.dtsi" + + / { + model = "Olimex A13-Olinuxino"; + compatible = "olimex,a13-olinuxino", "allwinner,sun5i-a13"; + + soc@01c00000 { ++ usbphy: phy@01c13400 { ++ usb1_vbus-supply = <®_usb1_vbus>; ++ status = "okay"; ++ }; ++ ++ ehci0: usb@01c14000 { ++ status = "okay"; ++ }; ++ ++ ohci0: usb@01c14400 { ++ status = "okay"; ++ }; ++ + pinctrl@01c20800 { + led_pins_olinuxino: led_pins@0 { + allwinner,pins = "PG9"; +@@ -26,6 +40,13 @@ + allwinner,drive = <1>; + allwinner,pull = <0>; + }; ++ ++ usb1_vbus_pin_olinuxino: usb1_vbus_pin@0 { ++ allwinner,pins = "PG11"; ++ allwinner,function = "gpio_out"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; + }; + + uart1: serial@01c28400 { +@@ -63,4 +84,10 @@ + default-state = "on"; + }; + }; ++ ++ reg_usb1_vbus: usb1-vbus { ++ pinctrl-0 = <&usb1_vbus_pin_olinuxino>; ++ gpio = <&pio 6 11 0>; ++ status = "okay"; ++ }; + }; +-- +2.0.3 + +From a3396ca280066e9a1ee74b36f5e376c945382bff Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 11 Jan 2014 05:21:43 +0100 +Subject: [PATCH] ARM: sun5i: dt: Add USB host nodes to a13-olinuxino-micro + +Add nodes for the usb-phy and ehci- and ohci-usb-host controllers. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts | 27 +++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +diff --git a/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts b/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts +index fe2ce0a..11169d5 100644 +--- a/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts ++++ b/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts +@@ -14,12 +14,26 @@ + + /dts-v1/; + /include/ "sun5i-a13.dtsi" ++/include/ "sunxi-common-regulators.dtsi" + + / { + model = "Olimex A13-Olinuxino Micro"; + compatible = "olimex,a13-olinuxino-micro", "allwinner,sun5i-a13"; + + soc@01c00000 { ++ usbphy: phy@01c13400 { ++ usb1_vbus-supply = <®_usb1_vbus>; ++ status = "okay"; ++ }; ++ ++ ehci0: usb@01c14000 { ++ status = "okay"; ++ }; ++ ++ ohci0: usb@01c14400 { ++ status = "okay"; ++ }; ++ + pinctrl@01c20800 { + led_pins_olinuxinom: led_pins@0 { + allwinner,pins = "PG9"; +@@ -27,6 +41,13 @@ + allwinner,drive = <1>; + allwinner,pull = <0>; + }; ++ ++ usb1_vbus_pin_olinuxinom: usb1_vbus_pin@0 { ++ allwinner,pins = "PG11"; ++ allwinner,function = "gpio_out"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; + }; + + uart1: serial@01c28400 { +@@ -65,4 +86,10 @@ + default-state = "on"; + }; + }; ++ ++ reg_usb1_vbus: usb1-vbus { ++ pinctrl-0 = <&usb1_vbus_pin_olinuxinom>; ++ gpio = <&pio 6 11 0>; ++ status = "okay"; ++ }; + }; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/165-dt-sun7i-add-usb-host-to-boards.patch b/target/linux/sunxi/patches-3.14/165-dt-sun7i-add-usb-host-to-boards.patch new file mode 100644 index 0000000000..d720f645d6 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/165-dt-sun7i-add-usb-host-to-boards.patch @@ -0,0 +1,201 @@ +From 56de1b69bf6782338193e373cee06fff252b31da Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Tue, 17 Dec 2013 23:04:57 +0100 +Subject: [PATCH] ARM: sun7i: dt: Add USB host nodes to cubietruck dts + +Add nodes for the usb-phy and ehci- and ohci-usb-host controllers. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 30 ++++++++++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +index ef5fed8..cb25d3c 100644 +--- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts ++++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +@@ -20,11 +20,33 @@ + compatible = "cubietech,cubietruck", "allwinner,sun7i-a20"; + + soc@01c00000 { ++ usbphy: phy@01c13400 { ++ usb1_vbus-supply = <®_usb1_vbus>; ++ usb2_vbus-supply = <®_usb2_vbus>; ++ status = "okay"; ++ }; ++ ++ ehci0: usb@01c14000 { ++ status = "okay"; ++ }; ++ ++ ohci0: usb@01c14400 { ++ status = "okay"; ++ }; ++ + ahci: sata@01c18000 { + target-supply = <®_ahci_5v>; + status = "okay"; + }; + ++ ehci1: usb@01c1c000 { ++ status = "okay"; ++ }; ++ ++ ohci1: usb@01c1c400 { ++ status = "okay"; ++ }; ++ + pinctrl@01c20800 { + ahci_pwr_pin_cubietruck: ahci_pwr_pin@1 { + allwinner,pins = "PH12"; +@@ -109,4 +131,12 @@ + gpio = <&pio 7 12 0>; + status = "okay"; + }; ++ ++ reg_usb1_vbus: usb1-vbus { ++ status = "okay"; ++ }; ++ ++ reg_usb2_vbus: usb2-vbus { ++ status = "okay"; ++ }; + }; +-- +2.0.3 + +From 8ce7da7026a24fe522abb3d7798cea12358946c0 Mon Sep 17 00:00:00 2001 +From: Roman Byshko <rbyshko@gmail.com> +Date: Thu, 19 Sep 2013 21:29:45 +0200 +Subject: [PATCH] ARM: sun7i: dt: Add USB host nodes to cubieboard2 dts + +Add nodes for the usb-phy and ehci- and ohci-usb-host controllers. + +Signed-off-by: Roman Byshko <rbyshko@gmail.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun7i-a20-cubieboard2.dts | 30 +++++++++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts +index 4bed115..68de89f 100644 +--- a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts ++++ b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts +@@ -20,11 +20,33 @@ + compatible = "cubietech,cubieboard2", "allwinner,sun7i-a20"; + + soc@01c00000 { ++ usbphy: phy@01c13400 { ++ usb1_vbus-supply = <®_usb1_vbus>; ++ usb2_vbus-supply = <®_usb2_vbus>; ++ status = "okay"; ++ }; ++ ++ ehci0: usb@01c14000 { ++ status = "okay"; ++ }; ++ ++ ohci0: usb@01c14400 { ++ status = "okay"; ++ }; ++ + ahci: sata@01c18000 { + target-supply = <®_ahci_5v>; + status = "okay"; + }; + ++ ehci1: usb@01c1c000 { ++ status = "okay"; ++ }; ++ ++ ohci1: usb@01c1c400 { ++ status = "okay"; ++ }; ++ + pinctrl@01c20800 { + led_pins_cubieboard2: led_pins@0 { + allwinner,pins = "PH20", "PH21"; +@@ -84,4 +106,12 @@ + reg_ahci_5v: ahci-5v { + status = "okay"; + }; ++ ++ reg_usb1_vbus: usb1-vbus { ++ status = "okay"; ++ }; ++ ++ reg_usb2_vbus: usb2-vbus { ++ status = "okay"; ++ }; + }; +-- +2.0.3 + +From e0e1a55781f7ba89a776456c8764782165e5efcc Mon Sep 17 00:00:00 2001 +From: Zalan Blenessy <zalan.blenessy@gmail.com> +Date: Sun, 22 Dec 2013 17:08:10 +0100 +Subject: [PATCH] ARM: sun7i: dt: Add USB host nodes to a20-olinuxino-micro dts + +Add nodes for the usb-phy and ehci- and ohci-usb-host controllers. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts | 30 +++++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts +index c9b0f37..eeadf76 100644 +--- a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts ++++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts +@@ -31,6 +31,20 @@ + status = "okay"; + }; + ++ usbphy: phy@01c13400 { ++ usb1_vbus-supply = <®_usb1_vbus>; ++ usb2_vbus-supply = <®_usb2_vbus>; ++ status = "okay"; ++ }; ++ ++ ehci0: usb@01c14000 { ++ status = "okay"; ++ }; ++ ++ ohci0: usb@01c14400 { ++ status = "okay"; ++ }; ++ + spi2: spi@01c17000 { + pinctrl-names = "default"; + pinctrl-0 = <&spi2_pins_a>; +@@ -42,6 +56,14 @@ + status = "okay"; + }; + ++ ehci1: usb@01c1c000 { ++ status = "okay"; ++ }; ++ ++ ohci1: usb@01c1c400 { ++ status = "okay"; ++ }; ++ + pinctrl@01c20800 { + led_pins_olinuxino: led_pins@0 { + allwinner,pins = "PH2"; +@@ -115,4 +137,12 @@ + reg_ahci_5v: ahci-5v { + status = "okay"; + }; ++ ++ reg_usb1_vbus: usb1-vbus { ++ status = "okay"; ++ }; ++ ++ reg_usb2_vbus: usb2-vbus { ++ status = "okay"; ++ }; + }; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/170-input-add-sun4i-ts-driver.patch b/target/linux/sunxi/patches-3.14/170-input-add-sun4i-ts-driver.patch new file mode 100644 index 0000000000..d97b02a306 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/170-input-add-sun4i-ts-driver.patch @@ -0,0 +1,354 @@ +From afbd58e1b1219cbcbe2cc07273fc51f658891e9b Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Mon, 23 Dec 2013 16:21:02 +0100 +Subject: [PATCH] input: Add new sun4i-ts driver for Allwinner sunxi SoC's rtp + controller + +Note the sun4i-ts controller is capable of detecting a second touch, but when +a second touch is present then the accuracy becomes so bad the reported touch +location is not useable. + +The original android driver contains some complicated heuristics using the +aprox. distance between the 2 touches to see if the user is making a pinch +open / close movement, and then reports emulated multi-touch events around +the last touch coordinate (as the dual-touch coordinates are worthless). + +These kinds of heuristics are just asking for trouble (and don't belong +in the kernel). So this driver offers straight forward, reliable single +touch functionality only. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + .../bindings/input/touchscreen/sun4i.txt | 15 ++ + drivers/input/touchscreen/Kconfig | 10 + + drivers/input/touchscreen/Makefile | 1 + + drivers/input/touchscreen/sun4i-ts.c | 262 +++++++++++++++++++++ + 4 files changed, 288 insertions(+) + create mode 100644 Documentation/devicetree/bindings/input/touchscreen/sun4i.txt + create mode 100644 drivers/input/touchscreen/sun4i-ts.c + +diff --git a/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt b/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt +new file mode 100644 +index 0000000..e45927e +--- /dev/null ++++ b/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt +@@ -0,0 +1,15 @@ ++sun4i resistive touchscreen controller ++-------------------------------------- ++ ++Required properties: ++ - compatible: "allwinner,sun4i-ts" ++ - reg: mmio address range of the chip ++ - interrupts: interrupt to which the chip is connected ++ ++Example: ++ ++ rtp: rtp@01c25000 { ++ compatible = "allwinner,sun4i-ts"; ++ reg = <0x01c25000 0x100>; ++ interrupts = <29>; ++ }; +diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig +index 07e9e82..f4c5ca3 100644 +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -906,6 +906,16 @@ config TOUCHSCREEN_STMPE + To compile this driver as a module, choose M here: the + module will be called stmpe-ts. + ++config TOUCHSCREEN_SUN4I ++ tristate "Allwinner sun4i resistive touchscreen controller support" ++ depends on ARCH_SUNXI ++ help ++ This selects support for the resistive touchscreen controller ++ found on Allwinner sunxi SoCs. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called sun4i-ts. ++ + config TOUCHSCREEN_SUR40 + tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen" + depends on USB +diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile +index 62801f2..c8f7375 100644 +--- a/drivers/input/touchscreen/Makefile ++++ b/drivers/input/touchscreen/Makefile +@@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_PIXCIR) += pixcir_i2c_ts.o + obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o + obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o + obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o ++obj-$(CONFIG_TOUCHSCREEN_SUN4I) += sun4i-ts.o + obj-$(CONFIG_TOUCHSCREEN_SUR40) += sur40.o + obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o + obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o +diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c +new file mode 100644 +index 0000000..5945219 +--- /dev/null ++++ b/drivers/input/touchscreen/sun4i-ts.c +@@ -0,0 +1,262 @@ ++/* ++ * Allwinner sunxi resistive touchscreen controller driver ++ * ++ * Copyright (C) 2013 - 2014 Hans de Goede <hdegoede@redhat.com> ++ * ++ * 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. ++ */ ++ ++/* ++ * The sun4i-ts controller is capable of detecting a second touch, but when a ++ * second touch is present then the accuracy becomes so bad the reported touch ++ * location is not useable. ++ * ++ * The original android driver contains some complicated heuristics using the ++ * aprox. distance between the 2 touches to see if the user is making a pinch ++ * open / close movement, and then reports emulated multi-touch events around ++ * the last touch coordinate (as the dual-touch coordinates are worthless). ++ * ++ * These kinds of heuristics are just asking for trouble (and don't belong ++ * in the kernel). So this driver offers straight forward, reliable single ++ * touch functionality only. ++ */ ++ ++#include <linux/err.h> ++#include <linux/init.h> ++#include <linux/input.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of_platform.h> ++#include <linux/platform_device.h> ++#include <linux/slab.h> ++ ++#define TP_CTRL0 0x00 ++#define TP_CTRL1 0x04 ++#define TP_CTRL2 0x08 ++#define TP_CTRL3 0x0c ++#define TP_INT_FIFOC 0x10 ++#define TP_INT_FIFOS 0x14 ++#define TP_TPR 0x18 ++#define TP_CDAT 0x1c ++#define TEMP_DATA 0x20 ++#define TP_DATA 0x24 ++ ++/* TP_CTRL0 bits */ ++#define ADC_FIRST_DLY(x) ((x) << 24) /* 8 bits */ ++#define ADC_FIRST_DLY_MODE(x) ((x) << 23) ++#define ADC_CLK_SEL(x) ((x) << 22) ++#define ADC_CLK_DIV(x) ((x) << 20) /* 3 bits */ ++#define FS_DIV(x) ((x) << 16) /* 4 bits */ ++#define T_ACQ(x) ((x) << 0) /* 16 bits */ ++ ++/* TP_CTRL1 bits */ ++#define STYLUS_UP_DEBOUN(x) ((x) << 12) /* 8 bits */ ++#define STYLUS_UP_DEBOUN_EN(x) ((x) << 9) ++#define TOUCH_PAN_CALI_EN(x) ((x) << 6) ++#define TP_DUAL_EN(x) ((x) << 5) ++#define TP_MODE_EN(x) ((x) << 4) ++#define TP_ADC_SELECT(x) ((x) << 3) ++#define ADC_CHAN_SELECT(x) ((x) << 0) /* 3 bits */ ++ ++/* TP_CTRL2 bits */ ++#define TP_SENSITIVE_ADJUST(x) ((x) << 28) /* 4 bits */ ++#define TP_MODE_SELECT(x) ((x) << 26) /* 2 bits */ ++#define PRE_MEA_EN(x) ((x) << 24) ++#define PRE_MEA_THRE_CNT(x) ((x) << 0) /* 24 bits */ ++ ++/* TP_CTRL3 bits */ ++#define FILTER_EN(x) ((x) << 2) ++#define FILTER_TYPE(x) ((x) << 0) /* 2 bits */ ++ ++/* TP_INT_FIFOC irq and fifo mask / control bits */ ++#define TEMP_IRQ_EN(x) ((x) << 18) ++#define OVERRUN_IRQ_EN(x) ((x) << 17) ++#define DATA_IRQ_EN(x) ((x) << 16) ++#define TP_DATA_XY_CHANGE(x) ((x) << 13) ++#define FIFO_TRIG(x) ((x) << 8) /* 5 bits */ ++#define DATA_DRQ_EN(x) ((x) << 7) ++#define FIFO_FLUSH(x) ((x) << 4) ++#define TP_UP_IRQ_EN(x) ((x) << 1) ++#define TP_DOWN_IRQ_EN(x) ((x) << 0) ++ ++/* TP_INT_FIFOS irq and fifo status bits */ ++#define TEMP_DATA_PENDING BIT(18) ++#define FIFO_OVERRUN_PENDING BIT(17) ++#define FIFO_DATA_PENDING BIT(16) ++#define TP_IDLE_FLG BIT(2) ++#define TP_UP_PENDING BIT(1) ++#define TP_DOWN_PENDING BIT(0) ++ ++/* TP_TPR bits */ ++#define TEMP_ENABLE(x) ((x) << 16) ++#define TEMP_PERIOD(x) ((x) << 0) /* t = x * 256 * 16 / clkin */ ++ ++struct sun4i_ts_data { ++ struct device *dev; ++ struct input_dev *input; ++ void __iomem *base; ++ unsigned int irq; ++ bool ignore_fifo_data; ++}; ++ ++static irqreturn_t sun4i_ts_irq(int irq, void *dev_id) ++{ ++ struct sun4i_ts_data *ts = dev_id; ++ u32 reg_val, x, y; ++ ++ reg_val = readl(ts->base + TP_INT_FIFOS); ++ ++ if (reg_val & FIFO_DATA_PENDING) { ++ x = readl(ts->base + TP_DATA); ++ y = readl(ts->base + TP_DATA); ++ /* The 1st location reported after an up event is unreliable */ ++ if (!ts->ignore_fifo_data) { ++ input_report_abs(ts->input, ABS_X, x); ++ input_report_abs(ts->input, ABS_Y, y); ++ /* ++ * The hardware has a separate down status bit, but ++ * that gets set before we get the first location, ++ * resulting in reporting a click on the old location. ++ */ ++ input_report_key(ts->input, BTN_TOUCH, 1); ++ input_sync(ts->input); ++ } else { ++ ts->ignore_fifo_data = false; ++ } ++ } ++ ++ if (reg_val & TP_UP_PENDING) { ++ ts->ignore_fifo_data = true; ++ input_report_key(ts->input, BTN_TOUCH, 0); ++ input_sync(ts->input); ++ } ++ ++ writel(reg_val, ts->base + TP_INT_FIFOS); ++ ++ return IRQ_HANDLED; ++} ++ ++static int sun4i_ts_open(struct input_dev *dev) ++{ ++ struct sun4i_ts_data *ts = input_get_drvdata(dev); ++ ++ /* Flush, set trig level to 1, enable data and up irqs */ ++ writel(DATA_IRQ_EN(1) | FIFO_TRIG(1) | FIFO_FLUSH(1) | TP_UP_IRQ_EN(1), ++ ts->base + TP_INT_FIFOC); ++ ++ return 0; ++} ++ ++static void sun4i_ts_close(struct input_dev *dev) ++{ ++ struct sun4i_ts_data *ts = input_get_drvdata(dev); ++ ++ /* Deactivate all IRQs */ ++ writel(0, ts->base + TP_INT_FIFOC); ++} ++ ++static int sun4i_ts_probe(struct platform_device *pdev) ++{ ++ struct sun4i_ts_data *ts; ++ struct device *dev = &pdev->dev; ++ int ret; ++ ++ ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL); ++ if (!ts) ++ return -ENOMEM; ++ ++ ts->dev = dev; ++ ts->ignore_fifo_data = true; ++ ++ ts->input = devm_input_allocate_device(dev); ++ if (!ts->input) ++ return -ENOMEM; ++ ++ ts->input->name = pdev->name; ++ ts->input->phys = "sun4i_ts/input0"; ++ ts->input->open = sun4i_ts_open; ++ ts->input->close = sun4i_ts_close; ++ ts->input->id.bustype = BUS_HOST; ++ ts->input->id.vendor = 0x0001; ++ ts->input->id.product = 0x0001; ++ ts->input->id.version = 0x0100; ++ ts->input->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS); ++ set_bit(BTN_TOUCH, ts->input->keybit); ++ input_set_abs_params(ts->input, ABS_X, 0, 4095, 0, 0); ++ input_set_abs_params(ts->input, ABS_Y, 0, 4095, 0, 0); ++ input_set_drvdata(ts->input, ts); ++ ++ ts->base = devm_ioremap_resource(dev, ++ platform_get_resource(pdev, IORESOURCE_MEM, 0)); ++ if (IS_ERR(ts->base)) ++ return PTR_ERR(ts->base); ++ ++ ts->irq = platform_get_irq(pdev, 0); ++ ret = devm_request_irq(dev, ts->irq, sun4i_ts_irq, 0, "sun4i-ts", ts); ++ if (ret) ++ return ret; ++ ++ /* ++ * Select HOSC clk, clkin = clk / 6, adc samplefreq = clkin / 8192, ++ * t_acq = clkin / (16 * 64) ++ */ ++ writel(ADC_CLK_SEL(0) | ADC_CLK_DIV(2) | FS_DIV(7) | T_ACQ(63), ++ ts->base + TP_CTRL0); ++ ++ /* ++ * sensitive_adjust = 15 : max, which is not all that sensitive, ++ * tp_mode = 0 : only x and y coordinates, as we don't use dual touch ++ */ ++ writel(TP_SENSITIVE_ADJUST(15) | TP_MODE_SELECT(0), ++ ts->base + TP_CTRL2); ++ ++ /* Enable median filter, type 1 : 5/3 */ ++ writel(FILTER_EN(1) | FILTER_TYPE(1), ts->base + TP_CTRL3); ++ ++ /* Enable temperature measurement, period 1953 (2 seconds) */ ++ writel(TEMP_ENABLE(1) | TEMP_PERIOD(1953), ts->base + TP_TPR); ++ ++ /* ++ * Set stylus up debounce to aprox 10 ms, enable debounce, and ++ * finally enable tp mode. ++ */ ++ writel(STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1) | TP_MODE_EN(1), ++ ts->base + TP_CTRL1); ++ ++ ret = input_register_device(ts->input); ++ if (ret) ++ return ret; ++ ++ platform_set_drvdata(pdev, ts); ++ return 0; ++} ++ ++static const struct of_device_id sun4i_ts_of_match[] = { ++ { .compatible = "allwinner,sun4i-ts", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, sun4i_ts_of_match); ++ ++static struct platform_driver sun4i_ts_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "sun4i-ts", ++ .of_match_table = of_match_ptr(sun4i_ts_of_match), ++ }, ++ .probe = sun4i_ts_probe, ++}; ++ ++module_platform_driver(sun4i_ts_driver); ++ ++MODULE_DESCRIPTION("Allwinner sun4i resistive touchscreen controller driver"); ++MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); ++MODULE_LICENSE("GPL"); +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/171-input-add-temp-sensor-support.patch b/target/linux/sunxi/patches-3.14/171-input-add-temp-sensor-support.patch new file mode 100644 index 0000000000..3d0bdad31f --- /dev/null +++ b/target/linux/sunxi/patches-3.14/171-input-add-temp-sensor-support.patch @@ -0,0 +1,264 @@ +From d8b5553dbf60e519d565dbd83327b08865e960e2 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Fri, 27 Dec 2013 15:25:28 +0100 +Subject: [PATCH] input: sun4i-ts: Add support for temperature sensor + +The sun4i resisitive touchscreen controller also comes with a built-in +temperature sensor. This commit adds support for it. + +This commit also introduces a new "ts-attached" device-tree property, +when this is not set, the input part of the driver won't register. This way +the internal temperature sensor can be used to measure the SoC temperature +independent of there actually being a touchscreen attached to the controller. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + .../bindings/input/touchscreen/sun4i.txt | 5 + + drivers/input/touchscreen/sun4i-ts.c | 140 ++++++++++++++++----- + 2 files changed, 114 insertions(+), 31 deletions(-) + +diff --git a/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt b/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt +index e45927e..6bac67b 100644 +--- a/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt ++++ b/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt +@@ -6,10 +6,15 @@ Required properties: + - reg: mmio address range of the chip + - interrupts: interrupt to which the chip is connected + ++Optional properties: ++ - allwinner,ts-attached: boolean indicating that an actual touchscreen is ++ attached to the controller ++ + Example: + + rtp: rtp@01c25000 { + compatible = "allwinner,sun4i-ts"; + reg = <0x01c25000 0x100>; + interrupts = <29>; ++ allwinner,ts-attached; + }; +diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c +index 5945219..16cbb01 100644 +--- a/drivers/input/touchscreen/sun4i-ts.c ++++ b/drivers/input/touchscreen/sun4i-ts.c +@@ -3,6 +3,9 @@ + * + * Copyright (C) 2013 - 2014 Hans de Goede <hdegoede@redhat.com> + * ++ * The hwmon parts are based on work by Corentin LABBE which is: ++ * Copyright (C) 2013 Corentin LABBE <clabbe.montjoie@gmail.com> ++ * + * 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 +@@ -30,6 +33,7 @@ + */ + + #include <linux/err.h> ++#include <linux/hwmon.h> + #include <linux/init.h> + #include <linux/input.h> + #include <linux/interrupt.h> +@@ -106,14 +110,12 @@ struct sun4i_ts_data { + void __iomem *base; + unsigned int irq; + bool ignore_fifo_data; ++ int temp_data; + }; + +-static irqreturn_t sun4i_ts_irq(int irq, void *dev_id) ++static void sun4i_ts_irq_handle_input(struct sun4i_ts_data *ts, u32 reg_val) + { +- struct sun4i_ts_data *ts = dev_id; +- u32 reg_val, x, y; +- +- reg_val = readl(ts->base + TP_INT_FIFOS); ++ u32 x, y; + + if (reg_val & FIFO_DATA_PENDING) { + x = readl(ts->base + TP_DATA); +@@ -139,6 +141,20 @@ static irqreturn_t sun4i_ts_irq(int irq, void *dev_id) + input_report_key(ts->input, BTN_TOUCH, 0); + input_sync(ts->input); + } ++} ++ ++static irqreturn_t sun4i_ts_irq(int irq, void *dev_id) ++{ ++ struct sun4i_ts_data *ts = dev_id; ++ u32 reg_val; ++ ++ reg_val = readl(ts->base + TP_INT_FIFOS); ++ ++ if (reg_val & TEMP_DATA_PENDING) ++ ts->temp_data = readl(ts->base + TEMP_DATA); ++ ++ if (ts->input) ++ sun4i_ts_irq_handle_input(ts, reg_val); + + writel(reg_val, ts->base + TP_INT_FIFOS); + +@@ -149,9 +165,9 @@ static int sun4i_ts_open(struct input_dev *dev) + { + struct sun4i_ts_data *ts = input_get_drvdata(dev); + +- /* Flush, set trig level to 1, enable data and up irqs */ +- writel(DATA_IRQ_EN(1) | FIFO_TRIG(1) | FIFO_FLUSH(1) | TP_UP_IRQ_EN(1), +- ts->base + TP_INT_FIFOC); ++ /* Flush, set trig level to 1, enable temp, data and up irqs */ ++ writel(TEMP_IRQ_EN(1) | DATA_IRQ_EN(1) | FIFO_TRIG(1) | FIFO_FLUSH(1) | ++ TP_UP_IRQ_EN(1), ts->base + TP_INT_FIFOC); + + return 0; + } +@@ -160,15 +176,48 @@ static void sun4i_ts_close(struct input_dev *dev) + { + struct sun4i_ts_data *ts = input_get_drvdata(dev); + +- /* Deactivate all IRQs */ +- writel(0, ts->base + TP_INT_FIFOC); ++ /* Deactivate all input IRQs */ ++ writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC); ++} ++ ++static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, ++ char *buf) ++{ ++ struct sun4i_ts_data *ts = dev_get_drvdata(dev); ++ ++ /* No temp_data until the first irq */ ++ if (ts->temp_data == -1) ++ return -EAGAIN; ++ ++ return sprintf(buf, "%d\n", (ts->temp_data - 1447) * 100); ++} ++ ++static ssize_t show_temp_label(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ return sprintf(buf, "SoC temperature\n"); + } + ++static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL); ++static DEVICE_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL); ++ ++static struct attribute *sun4i_ts_attrs[] = { ++ &dev_attr_temp1_input.attr, ++ &dev_attr_temp1_label.attr, ++ NULL ++}; ++ATTRIBUTE_GROUPS(sun4i_ts); ++ + static int sun4i_ts_probe(struct platform_device *pdev) + { + struct sun4i_ts_data *ts; + struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct device *hwmon; + int ret; ++ bool ts_attached; ++ ++ ts_attached = of_property_read_bool(np, "allwinner,ts-attached"); + + ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL); + if (!ts) +@@ -176,24 +225,27 @@ static int sun4i_ts_probe(struct platform_device *pdev) + + ts->dev = dev; + ts->ignore_fifo_data = true; +- +- ts->input = devm_input_allocate_device(dev); +- if (!ts->input) +- return -ENOMEM; +- +- ts->input->name = pdev->name; +- ts->input->phys = "sun4i_ts/input0"; +- ts->input->open = sun4i_ts_open; +- ts->input->close = sun4i_ts_close; +- ts->input->id.bustype = BUS_HOST; +- ts->input->id.vendor = 0x0001; +- ts->input->id.product = 0x0001; +- ts->input->id.version = 0x0100; +- ts->input->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS); +- set_bit(BTN_TOUCH, ts->input->keybit); +- input_set_abs_params(ts->input, ABS_X, 0, 4095, 0, 0); +- input_set_abs_params(ts->input, ABS_Y, 0, 4095, 0, 0); +- input_set_drvdata(ts->input, ts); ++ ts->temp_data = -1; ++ ++ if (ts_attached) { ++ ts->input = devm_input_allocate_device(dev); ++ if (!ts->input) ++ return -ENOMEM; ++ ++ ts->input->name = pdev->name; ++ ts->input->phys = "sun4i_ts/input0"; ++ ts->input->open = sun4i_ts_open; ++ ts->input->close = sun4i_ts_close; ++ ts->input->id.bustype = BUS_HOST; ++ ts->input->id.vendor = 0x0001; ++ ts->input->id.product = 0x0001; ++ ts->input->id.version = 0x0100; ++ ts->input->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS); ++ set_bit(BTN_TOUCH, ts->input->keybit); ++ input_set_abs_params(ts->input, ABS_X, 0, 4095, 0, 0); ++ input_set_abs_params(ts->input, ABS_Y, 0, 4095, 0, 0); ++ input_set_drvdata(ts->input, ts); ++ } + + ts->base = devm_ioremap_resource(dev, + platform_get_resource(pdev, IORESOURCE_MEM, 0)); +@@ -232,14 +284,39 @@ static int sun4i_ts_probe(struct platform_device *pdev) + writel(STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1) | TP_MODE_EN(1), + ts->base + TP_CTRL1); + +- ret = input_register_device(ts->input); +- if (ret) +- return ret; ++ hwmon = devm_hwmon_device_register_with_groups(ts->dev, "sun4i_ts", ++ ts, sun4i_ts_groups); ++ if (IS_ERR(hwmon)) ++ return PTR_ERR(hwmon); ++ ++ writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC); ++ ++ if (ts_attached) { ++ ret = input_register_device(ts->input); ++ if (ret) { ++ writel(0, ts->base + TP_INT_FIFOC); ++ return ret; ++ } ++ } + + platform_set_drvdata(pdev, ts); + return 0; + } + ++static int sun4i_ts_remove(struct platform_device *pdev) ++{ ++ struct sun4i_ts_data *ts = platform_get_drvdata(pdev); ++ ++ /* Explicit unregister to avoid open/close changing the imask later */ ++ if (ts->input) ++ input_unregister_device(ts->input); ++ ++ /* Deactivate all IRQs */ ++ writel(0, ts->base + TP_INT_FIFOC); ++ ++ return 0; ++} ++ + static const struct of_device_id sun4i_ts_of_match[] = { + { .compatible = "allwinner,sun4i-ts", }, + { /* sentinel */ } +@@ -253,6 +330,7 @@ static struct platform_driver sun4i_ts_driver = { + .of_match_table = of_match_ptr(sun4i_ts_of_match), + }, + .probe = sun4i_ts_probe, ++ .remove = sun4i_ts_remove, + }; + + module_platform_driver(sun4i_ts_driver); +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/172-input-add-lradc-keys-driver.patch b/target/linux/sunxi/patches-3.14/172-input-add-lradc-keys-driver.patch new file mode 100644 index 0000000000..ee8f80668f --- /dev/null +++ b/target/linux/sunxi/patches-3.14/172-input-add-lradc-keys-driver.patch @@ -0,0 +1,339 @@ +From 3f8fd9b9e2daefb7be4c46369f86af1c7bb2f1ca Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Wed, 1 Jan 2014 19:44:49 +0100 +Subject: [PATCH] input: Add new sun4i-lradc-keys driver + +Allwinnner sunxi SoCs have a low resolution adc (called lradc) which is +specifically designed to have various (tablet) keys (ie home, back, search, +etc). attached to it using a resistor network. This adds a driver for this. + +There are 2 channels, currently this driver only supports chan0 since there +are no boards known to use chan1. The devicetree properties are already +prefixed with chan0 as preparation for chan1 support in the future. + +This has been tested on an olimex a10s-olinuxino-micro, a13-olinuxino, and +a20-olinuxino-micro. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + .../devicetree/bindings/input/sun4i-lradc-keys.txt | 22 ++ + drivers/input/keyboard/Kconfig | 10 + + drivers/input/keyboard/Makefile | 1 + + drivers/input/keyboard/sun4i-lradc-keys.c | 243 +++++++++++++++++++++ + 4 files changed, 276 insertions(+) + create mode 100644 Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt + create mode 100644 drivers/input/keyboard/sun4i-lradc-keys.c + +diff --git a/Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt b/Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt +new file mode 100644 +index 0000000..7801264 +--- /dev/null ++++ b/Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt +@@ -0,0 +1,22 @@ ++Allwinner sun4i low res adc attached tablet keys ++------------------------------------------------ ++ ++Required properties: ++ - compatible: "allwinner,sun4i-lradc-keys" ++ - reg: mmio address range of the chip ++ - interrupts: interrupt to which the chip is connected ++ - allwinner,chan0-step: step in mV between keys must be 150 or 200 ++ - linux,chan0-keycodes: array of dt-bindings/input/input.h KEY_ codes ++ ++Example: ++ ++#include <dt-bindings/input/input.h> ++ ++ lradc: lradc@01c22800 { ++ compatible = "allwinner,sun4i-lradc-keys"; ++ reg = <0x01c22800 0x100>; ++ interrupts = <31>; ++ allwinner,chan0-step = <200>; ++ linux,chan0-keycodes = <KEY_VOLUMEUP KEY_VOLUMEDOWN ++ KEY_MENU KEY_ENTER KEY_HOME>; ++ }; +diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig +index a673c9f..d8a51cd 100644 +--- a/drivers/input/keyboard/Kconfig ++++ b/drivers/input/keyboard/Kconfig +@@ -544,6 +544,16 @@ config KEYBOARD_STMPE + To compile this driver as a module, choose M here: the module will be + called stmpe-keypad. + ++config KEYBOARD_SUN4I_LRADC ++ tristate "Allwinner sun4i low res adc attached tablet keys support" ++ depends on ARCH_SUNXI ++ help ++ This selects support for the Allwinner low res adc attached tablet ++ keys found on Allwinner sunxi SoCs. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called sun4i-lradc-keys. ++ + config KEYBOARD_DAVINCI + tristate "TI DaVinci Key Scan" + depends on ARCH_DAVINCI_DM365 +diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile +index a699b61..f3265bd 100644 +--- a/drivers/input/keyboard/Makefile ++++ b/drivers/input/keyboard/Makefile +@@ -50,6 +50,7 @@ obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o + obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o + obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o + obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o ++obj-$(CONFIG_KEYBOARD_SUN4I_LRADC) += sun4i-lradc-keys.o + obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o + obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o + obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o +diff --git a/drivers/input/keyboard/sun4i-lradc-keys.c b/drivers/input/keyboard/sun4i-lradc-keys.c +new file mode 100644 +index 0000000..5c55e17 +--- /dev/null ++++ b/drivers/input/keyboard/sun4i-lradc-keys.c +@@ -0,0 +1,243 @@ ++/* ++ * Allwinner sun4i low res adc attached tablet keys driver ++ * ++ * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com> ++ * ++ * 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. ++ */ ++ ++/* ++ * Allwinnner sunxi SoCs have a lradc which is specifically designed to have ++ * various (tablet) keys (ie home, back, search, etc). attached to it using ++ * a resistor network. This driver is for the keys on such boards. ++ * ++ * There are 2 channels, currently this driver only supports chan0 since there ++ * are no boards known to use chan1. The devicetree properties are already ++ * prefixed with chan0 as preparation for chan1 support in the future. ++ */ ++ ++#include <linux/err.h> ++#include <linux/init.h> ++#include <linux/input.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of_platform.h> ++#include <linux/platform_device.h> ++#include <linux/slab.h> ++ ++#define LRADC_CTRL 0x00 ++#define LRADC_INTC 0x04 ++#define LRADC_INTS 0x08 ++#define LRADC_DATA0 0x0c ++#define LRADC_DATA1 0x10 ++ ++/* LRADC_CTRL bits */ ++#define FIRST_CONVERT_DLY(x) ((x) << 24) /* 8 bits */ ++#define CHAN_SELECT(x) ((x) << 22) /* 2 bits */ ++#define CONTINUE_TIME_SEL(x) ((x) << 16) /* 4 bits */ ++#define KEY_MODE_SEL(x) ((x) << 12) /* 2 bits */ ++#define LEVELA_B_CNT(x) ((x) << 8) /* 4 bits */ ++#define HOLD_EN(x) ((x) << 6) ++#define LEVELB_VOL(x) ((x) << 4) /* 2 bits */ ++#define SAMPLE_RATE(x) ((x) << 2) /* 2 bits */ ++#define ENABLE(x) ((x) << 0) ++ ++/* LRADC_INTC and LRADC_INTS bits */ ++#define CHAN1_KEYUP_IRQ BIT(12) ++#define CHAN1_ALRDY_HOLD_IRQ BIT(11) ++#define CHAN1_HOLD_IRQ BIT(10) ++#define CHAN1_KEYDOWN_IRQ BIT(9) ++#define CHAN1_DATA_IRQ BIT(8) ++#define CHAN0_KEYUP_IRQ BIT(4) ++#define CHAN0_ALRDY_HOLD_IRQ BIT(3) ++#define CHAN0_HOLD_IRQ BIT(2) ++#define CHAN0_KEYDOWN_IRQ BIT(1) ++#define CHAN0_DATA_IRQ BIT(0) ++ ++#define MAX_KEYS 13 ++ ++/* Lookup table to map the adc val to a keycode index for 150 mv step size */ ++static const u8 adc_val_to_key_index_step150[64] = { ++ 0, 0, 0, ++ 1, 1, 1, 1, 1, ++ 2, 2, 2, 2, 2, ++ 3, 3, 3, 3, ++ 4, 4, 4, 4, 4, ++ 5, 5, 5, 5, 5, ++ 6, 6, 6, 6, 6, ++ 7, 7, 7, 7, ++ 8, 8, 8, 8, 8, ++ 9, 9, 9, 9, 9, ++ 10, 10, 10, 10, ++ 11, 11, 11, 11, ++ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12 ++}; ++ ++/* Lookup table to map the adc val to a keycode index for 200 mv step size */ ++static const u8 adc_val_to_key_index_step200[64] = { ++ 0, 0, 0, 0, 0, 0, 0, 0, ++ 1, 1, 1, 1, 1, 1, 1, ++ 2, 2, 2, 2, 2, 2, 2, ++ 3, 3, 3, 3, 3, 3, ++ 4, 4, 4, 4, 4, 4, ++ 5, 5, 5, 5, 5, 5, ++ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, ++ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 ++}; ++ ++struct sun4i_lradc_data { ++ struct device *dev; ++ struct input_dev *input; ++ void __iomem *base; ++ u32 chan0_step; ++ u32 chan0_keycode; ++ u32 chan0_keycodes[MAX_KEYS]; ++}; ++ ++static irqreturn_t sun4i_lradc_irq(int irq, void *dev_id) ++{ ++ struct sun4i_lradc_data *lradc = dev_id; ++ u32 ints, val; ++ ++ ints = readl(lradc->base + LRADC_INTS); ++ ++ /* ++ * lradc supports only one keypress at a time, release does not give ++ * any info as to which key was released, so we cache the keycode. ++ */ ++ if ((ints & CHAN0_KEYDOWN_IRQ) && lradc->chan0_keycode == 0) { ++ val = readl(lradc->base + LRADC_DATA0); ++ if (lradc->chan0_step == 150) ++ val = adc_val_to_key_index_step150[val]; ++ else ++ val = adc_val_to_key_index_step200[val]; ++ ++ lradc->chan0_keycode = lradc->chan0_keycodes[val]; ++ input_report_key(lradc->input, lradc->chan0_keycode, 1); ++ } ++ ++ if (ints & CHAN0_KEYUP_IRQ) { ++ input_report_key(lradc->input, lradc->chan0_keycode, 0); ++ lradc->chan0_keycode = 0; ++ } ++ ++ input_sync(lradc->input); ++ ++ writel(ints, lradc->base + LRADC_INTS); ++ ++ return IRQ_HANDLED; ++} ++ ++static int sun4i_lradc_open(struct input_dev *dev) ++{ ++ struct sun4i_lradc_data *lradc = input_get_drvdata(dev); ++ ++ /* ++ * Set sample time to 16 ms / 62.5 Hz. Wait 2 * 16 ms for key to ++ * stabilize on press, wait (1 + 1) * 16 ms for key release ++ */ ++ writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) | ++ SAMPLE_RATE(2) | ENABLE(1), lradc->base + LRADC_CTRL); ++ ++ writel(CHAN0_KEYUP_IRQ | CHAN0_KEYDOWN_IRQ, lradc->base + LRADC_INTC); ++ ++ return 0; ++} ++ ++static void sun4i_lradc_close(struct input_dev *dev) ++{ ++ struct sun4i_lradc_data *lradc = input_get_drvdata(dev); ++ ++ /* Disable lradc, leave other settings unchanged */ ++ writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) | ++ SAMPLE_RATE(2), lradc->base + LRADC_CTRL); ++ writel(0, lradc->base + LRADC_INTC); ++} ++ ++static int sun4i_lradc_probe(struct platform_device *pdev) ++{ ++ struct sun4i_lradc_data *lradc; ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ int i, ret; ++ ++ lradc = devm_kzalloc(dev, sizeof(struct sun4i_lradc_data), GFP_KERNEL); ++ if (!lradc) ++ return -ENOMEM; ++ ++ ret = of_property_read_u32(np, "allwinner,chan0-step", ++ &lradc->chan0_step); ++ if (ret || (lradc->chan0_step != 150 && lradc->chan0_step != 200)) { ++ dev_err(dev, "Invalid allwinner,chan0-step dt-property\n"); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < MAX_KEYS; i++) ++ of_property_read_u32_index(np, "linux,chan0-keycodes", ++ i, &lradc->chan0_keycodes[i]); ++ ++ lradc->dev = dev; ++ lradc->input = devm_input_allocate_device(dev); ++ if (!lradc->input) ++ return -ENOMEM; ++ ++ lradc->input->name = pdev->name; ++ lradc->input->phys = "sun4i_lradc/input0"; ++ lradc->input->open = sun4i_lradc_open; ++ lradc->input->close = sun4i_lradc_close; ++ lradc->input->id.bustype = BUS_HOST; ++ lradc->input->id.vendor = 0x0001; ++ lradc->input->id.product = 0x0001; ++ lradc->input->id.version = 0x0100; ++ lradc->input->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY); ++ for (i = 0; i < MAX_KEYS; i++) ++ set_bit(lradc->chan0_keycodes[i], lradc->input->keybit); ++ input_set_drvdata(lradc->input, lradc); ++ ++ lradc->base = devm_ioremap_resource(dev, ++ platform_get_resource(pdev, IORESOURCE_MEM, 0)); ++ if (IS_ERR(lradc->base)) ++ return PTR_ERR(lradc->base); ++ ++ ret = devm_request_irq(dev, platform_get_irq(pdev, 0), sun4i_lradc_irq, ++ 0, "sun4i-lradc-keys", lradc); ++ if (ret) ++ return ret; ++ ++ ret = input_register_device(lradc->input); ++ if (ret) ++ return ret; ++ ++ platform_set_drvdata(pdev, lradc); ++ return 0; ++} ++ ++static const struct of_device_id sun4i_lradc_of_match[] = { ++ { .compatible = "allwinner,sun4i-lradc-keys", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, sun4i_lradc_of_match); ++ ++static struct platform_driver sun4i_lradc_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "sun4i-lradc-keys", ++ .of_match_table = of_match_ptr(sun4i_lradc_of_match), ++ }, ++ .probe = sun4i_lradc_probe, ++}; ++ ++module_platform_driver(sun4i_lradc_driver); ++ ++MODULE_DESCRIPTION("Allwinner sun4i low res adc attached tablet keys driver"); ++MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); ++MODULE_LICENSE("GPL"); +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/173-1-dt-sun4i-add-lradc-node.patch b/target/linux/sunxi/patches-3.14/173-1-dt-sun4i-add-lradc-node.patch new file mode 100644 index 0000000000..1c7bc810f7 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/173-1-dt-sun4i-add-lradc-node.patch @@ -0,0 +1,31 @@ +From 87d0c26803eec56971c8a7e6299aefc4d72dfb3c Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Wed, 1 Jan 2014 19:51:36 +0100 +Subject: [PATCH] ARM: dts: sun4i: Add lradc node + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun4i-a10.dtsi | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi +index 7014518..3ce650b 100644 +--- a/arch/arm/boot/dts/sun4i-a10.dtsi ++++ b/arch/arm/boot/dts/sun4i-a10.dtsi +@@ -607,6 +607,13 @@ + interrupts = <24>; + }; + ++ lradc: lradc@01c22800 { ++ compatible = "allwinner,sun4i-lradc-keys"; ++ reg = <0x01c22800 0x100>; ++ interrupts = <31>; ++ status = "disabled"; ++ }; ++ + sid: eeprom@01c23800 { + compatible = "allwinner,sun4i-sid"; + reg = <0x01c23800 0x10>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/173-2-dt-sun5i-add-lradc-node.patch b/target/linux/sunxi/patches-3.14/173-2-dt-sun5i-add-lradc-node.patch new file mode 100644 index 0000000000..fbb0a32ca4 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/173-2-dt-sun5i-add-lradc-node.patch @@ -0,0 +1,64 @@ +From 9041b1f6118b32929e6357affb53db28439a11e7 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Wed, 1 Jan 2014 19:50:33 +0100 +Subject: [PATCH] ARM: dts: sun5i: Add lradc node + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts | 8 ++++++++ + arch/arm/boot/dts/sun5i-a10s.dtsi | 7 +++++++ + arch/arm/boot/dts/sun5i-a13-olinuxino.dts | 8 ++++++++ + arch/arm/boot/dts/sun5i-a13.dtsi | 7 +++++++ + 4 files changed, 30 insertions(+) + +diff --git a/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts b/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts +index 5bc25c7..93c6778 100644 +diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi +index 8ba1ed7..1795c26 100644 +diff --git a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts +index 177196c..c2c455d 100644 +--- a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts ++++ b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts +@@ -14,6 +14,7 @@ + /dts-v1/; + /include/ "sun5i-a13.dtsi" + /include/ "sunxi-common-regulators.dtsi" ++#include <dt-bindings/input/input.h> + + / { + model = "Olimex A13-Olinuxino"; +@@ -64,6 +65,13 @@ + }; + }; + ++ lradc: lradc@01c22800 { ++ allwinner,chan0-step = <200>; ++ linux,chan0-keycodes = <KEY_VOLUMEUP KEY_VOLUMEDOWN ++ KEY_MENU KEY_ENTER KEY_HOME>; ++ status = "okay"; ++ }; ++ + uart1: serial@01c28400 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins_b>; +diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi +index 6fc84a4..8bc8e14 100644 +--- a/arch/arm/boot/dts/sun5i-a13.dtsi ++++ b/arch/arm/boot/dts/sun5i-a13.dtsi +@@ -458,6 +458,13 @@ + reg = <0x01c20c90 0x10>; + }; + ++ lradc: lradc@01c22800 { ++ compatible = "allwinner,sun4i-lradc-keys"; ++ reg = <0x01c22800 0x100>; ++ interrupts = <31>; ++ status = "disabled"; ++ }; ++ + sid: eeprom@01c23800 { + compatible = "allwinner,sun4i-sid"; + reg = <0x01c23800 0x10>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/173-3-dt-sun7i-add-lradc-node.patch b/target/linux/sunxi/patches-3.14/173-3-dt-sun7i-add-lradc-node.patch new file mode 100644 index 0000000000..417dddc2c0 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/173-3-dt-sun7i-add-lradc-node.patch @@ -0,0 +1,59 @@ +From 98e21d18a26032912bf6c979f084c200a94e976b Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Wed, 1 Jan 2014 20:26:21 +0100 +Subject: [PATCH] ARM: dts: sun7i: Add lradc node + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts | 9 +++++++++ + arch/arm/boot/dts/sun7i-a20.dtsi | 7 +++++++ + 2 files changed, 16 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts +index 822cbe2..15f1f3f 100644 +--- a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts ++++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts +@@ -14,6 +14,7 @@ + /dts-v1/; + /include/ "sun7i-a20.dtsi" + /include/ "sunxi-common-regulators.dtsi" ++#include <dt-bindings/input/input.h> + + / { + model = "Olimex A20-Olinuxino Micro"; +@@ -96,6 +97,14 @@ + }; + }; + ++ lradc: lradc@01c22800 { ++ allwinner,chan0-step = <200>; ++ linux,chan0-keycodes = <KEY_VOLUMEUP KEY_VOLUMEDOWN ++ KEY_MENU KEY_SEARCH KEY_HOME ++ KEY_ESC KEY_ENTER>; ++ status = "okay"; ++ }; ++ + uart0: serial@01c28000 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins_a>; +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 1d9b314..0c57ac5 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -735,6 +735,13 @@ + interrupts = <0 24 4>; + }; + ++ lradc: lradc@01c22800 { ++ compatible = "allwinner,sun4i-lradc-keys"; ++ reg = <0x01c22800 0x100>; ++ interrupts = <0 31 4>; ++ status = "disabled"; ++ }; ++ + sid: eeprom@01c23800 { + compatible = "allwinner,sun7i-a20-sid"; + reg = <0x01c23800 0x200>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/175-reset-add-of_reset_control_get.patch b/target/linux/sunxi/patches-3.14/175-reset-add-of_reset_control_get.patch new file mode 100644 index 0000000000..7bf9511fe9 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/175-reset-add-of_reset_control_get.patch @@ -0,0 +1,119 @@ +From bfea2b9be28b20e076d5df8863c25e966f413fa3 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Fri, 20 Dec 2013 22:41:07 +0100 +Subject: [PATCH] reset: Add of_reset_control_get + +In some cases, you might need to deassert from reset an hardware block that +doesn't associated to a struct device (CPUs, timers, etc.). + +Add a small helper to retrieve the reset controller from the device tree +without the need to pass a struct device. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + drivers/reset/core.c | 39 ++++++++++++++++++++++++++++++--------- + include/linux/reset.h | 4 ++++ + 2 files changed, 34 insertions(+), 9 deletions(-) + +diff --git a/drivers/reset/core.c b/drivers/reset/core.c +index d1b6089..4f3dda7 100644 +--- a/drivers/reset/core.c ++++ b/drivers/reset/core.c +@@ -127,15 +127,16 @@ int reset_control_deassert(struct reset_control *rstc) + EXPORT_SYMBOL_GPL(reset_control_deassert); + + /** +- * reset_control_get - Lookup and obtain a reference to a reset controller. +- * @dev: device to be reset by the controller ++ * of_reset_control_get - Lookup and obtain a reference to a reset controller. ++ * @node: device to be reset by the controller + * @id: reset line name + * + * Returns a struct reset_control or IS_ERR() condition containing errno. + * + * Use of id names is optional. + */ +-struct reset_control *reset_control_get(struct device *dev, const char *id) ++struct reset_control *of_reset_control_get(struct device_node *node, ++ const char *id) + { + struct reset_control *rstc = ERR_PTR(-EPROBE_DEFER); + struct reset_controller_dev *r, *rcdev; +@@ -144,13 +145,10 @@ struct reset_control *reset_control_get(struct device *dev, const char *id) + int rstc_id; + int ret; + +- if (!dev) +- return ERR_PTR(-EINVAL); +- + if (id) +- index = of_property_match_string(dev->of_node, ++ index = of_property_match_string(node, + "reset-names", id); +- ret = of_parse_phandle_with_args(dev->of_node, "resets", "#reset-cells", ++ ret = of_parse_phandle_with_args(node, "resets", "#reset-cells", + index, &args); + if (ret) + return ERR_PTR(ret); +@@ -185,12 +183,35 @@ struct reset_control *reset_control_get(struct device *dev, const char *id) + return ERR_PTR(-ENOMEM); + } + +- rstc->dev = dev; + rstc->rcdev = rcdev; + rstc->id = rstc_id; + + return rstc; + } ++EXPORT_SYMBOL_GPL(of_reset_control_get); ++ ++/** ++ * reset_control_get - Lookup and obtain a reference to a reset controller. ++ * @dev: device to be reset by the controller ++ * @id: reset line name ++ * ++ * Returns a struct reset_control or IS_ERR() condition containing errno. ++ * ++ * Use of id names is optional. ++ */ ++struct reset_control *reset_control_get(struct device *dev, const char *id) ++{ ++ struct reset_control *rstc; ++ ++ if (!dev) ++ return ERR_PTR(-EINVAL); ++ ++ rstc = of_reset_control_get(dev->of_node, id); ++ if (!IS_ERR(rstc)) ++ rstc->dev = dev; ++ ++ return rstc; ++} + EXPORT_SYMBOL_GPL(reset_control_get); + + /** +diff --git a/include/linux/reset.h b/include/linux/reset.h +index 6082247..a398025 100644 +--- a/include/linux/reset.h ++++ b/include/linux/reset.h +@@ -1,6 +1,8 @@ + #ifndef _LINUX_RESET_H_ + #define _LINUX_RESET_H_ + ++#include <linux/of.h> ++ + struct device; + struct reset_control; + +@@ -8,6 +10,8 @@ int reset_control_reset(struct reset_control *rstc); + int reset_control_assert(struct reset_control *rstc); + int reset_control_deassert(struct reset_control *rstc); + ++struct reset_control *of_reset_control_get(struct device_node *node, ++ const char *id); + struct reset_control *reset_control_get(struct device *dev, const char *id); + void reset_control_put(struct reset_control *rstc); + struct reset_control *devm_reset_control_get(struct device *dev, const char *id); +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/176-clk-sun5i-add-support-for-reset-ctrler.patch b/target/linux/sunxi/patches-3.14/176-clk-sun5i-add-support-for-reset-ctrler.patch new file mode 100644 index 0000000000..6d322c7bc6 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/176-clk-sun5i-add-support-for-reset-ctrler.patch @@ -0,0 +1,69 @@ +From 3ec31fa2ce161d35f787354037f94d9d22d825d1 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Fri, 20 Dec 2013 22:41:08 +0100 +Subject: [PATCH] clocksource: sun5i: Add support for reset controller + +The Allwinner A31 that uses this timer has the timer IP asserted in reset. +Add an optional reset property to the DT, and deassert the timer from reset if +it's there. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + .../devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.txt | 4 ++++ + drivers/clocksource/timer-sun5i.c | 6 ++++++ + 2 files changed, 10 insertions(+) + +diff --git a/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.txt b/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.txt +index 7c26154..27cfc7d 100644 +--- a/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.txt ++++ b/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.txt +@@ -9,6 +9,9 @@ Required properties: + one) + - clocks: phandle to the source clock (usually the AHB clock) + ++Optionnal properties: ++- resets: phandle to a reset controller asserting the timer ++ + Example: + + timer@01c60000 { +@@ -19,4 +22,5 @@ timer@01c60000 { + <0 53 1>, + <0 54 1>; + clocks = <&ahb1_gates 19>; ++ resets = <&ahb1rst 19>; + }; +diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c +index deebcd6..0226844 100644 +--- a/drivers/clocksource/timer-sun5i.c ++++ b/drivers/clocksource/timer-sun5i.c +@@ -16,6 +16,7 @@ + #include <linux/interrupt.h> + #include <linux/irq.h> + #include <linux/irqreturn.h> ++#include <linux/reset.h> + #include <linux/sched_clock.h> + #include <linux/of.h> + #include <linux/of_address.h> +@@ -143,6 +144,7 @@ static u64 sun5i_timer_sched_read(void) + + static void __init sun5i_timer_init(struct device_node *node) + { ++ struct reset_control *rstc; + unsigned long rate; + struct clk *clk; + int ret, irq; +@@ -162,6 +164,10 @@ static void __init sun5i_timer_init(struct device_node *node) + clk_prepare_enable(clk); + rate = clk_get_rate(clk); + ++ rstc = of_reset_control_get(node, NULL); ++ if (!IS_ERR(rstc)) ++ reset_control_deassert(rstc); ++ + writel(~0, timer_base + TIMER_INTVAL_LO_REG(1)); + writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, + timer_base + TIMER_CTL_REG(1)); +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/180-clk-sunxi-add-clock-output-names-dt-prop-support.patch b/target/linux/sunxi/patches-3.14/180-clk-sunxi-add-clock-output-names-dt-prop-support.patch new file mode 100644 index 0000000000..4f7d275522 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/180-clk-sunxi-add-clock-output-names-dt-prop-support.patch @@ -0,0 +1,55 @@ +From 0bf618fda3ad24649add0bf943d16a9b4f5c3463 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Mon, 3 Feb 2014 09:51:37 +0800 +Subject: [PATCH] clk: sunxi: add clock-output-names dt property support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +sunxi clock drivers use dt node name as clock name, but clock +nodes should be named clk@X, so the names would be the same. +Let the drivers read clock names from dt clock-output-names +property. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> +Acked-by: Mike Turquette <mturquette@linaro.org> +Signed-off-by: Emilio López <emilio@elopez.com.ar> +--- + drivers/clk/sunxi/clk-sunxi.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c +index abb6c5a..0ed9794 100644 +--- a/drivers/clk/sunxi/clk-sunxi.c ++++ b/drivers/clk/sunxi/clk-sunxi.c +@@ -51,6 +51,8 @@ static void __init sun4i_osc_clk_setup(struct device_node *node) + if (!gate) + goto err_free_fixed; + ++ of_property_read_string(node, "clock-output-names", &clk_name); ++ + /* set up gate and fixed rate properties */ + gate->reg = of_iomap(node, 0); + gate->bit_idx = SUNXI_OSC24M_GATE; +@@ -601,6 +603,8 @@ static void __init sunxi_mux_clk_setup(struct device_node *node, + (parents[i] = of_clk_get_parent_name(node, i)) != NULL) + i++; + ++ of_property_read_string(node, "clock-output-names", &clk_name); ++ + clk = clk_register_mux(NULL, clk_name, parents, i, + CLK_SET_RATE_NO_REPARENT, reg, + data->shift, SUNXI_MUX_GATE_WIDTH, +@@ -660,6 +664,8 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, + + clk_parent = of_clk_get_parent_name(node, 0); + ++ of_property_read_string(node, "clock-output-names", &clk_name); ++ + clk = clk_register_divider(NULL, clk_name, clk_parent, 0, + reg, data->shift, data->width, + data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0, +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/181-clk-sunxi-add-names-for-pll56.patch b/target/linux/sunxi/patches-3.14/181-clk-sunxi-add-names-for-pll56.patch new file mode 100644 index 0000000000..879462b766 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/181-clk-sunxi-add-names-for-pll56.patch @@ -0,0 +1,83 @@ +From 8f3156f3f2b70128b2761526c208c8e3bfda694e Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Mon, 3 Feb 2014 09:51:39 +0800 +Subject: [PATCH] clk: sunxi: add names for pll5, pll6 parent clocks to + factors_data +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Some factor clocks, such as the parent clock of pll5 and pll6, have +multiple output names. Add the corresponding names to factors_data +tied to compatible string. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> +Acked-by: Mike Turquette <mturquette@linaro.org> +Signed-off-by: Emilio López <emilio@elopez.com.ar> +--- + drivers/clk/sunxi/clk-sunxi.c | 27 ++++++++++++++++++--------- + 1 file changed, 18 insertions(+), 9 deletions(-) + +diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c +index 0ed9794..7a2ed98 100644 +--- a/drivers/clk/sunxi/clk-sunxi.c ++++ b/drivers/clk/sunxi/clk-sunxi.c +@@ -389,6 +389,7 @@ struct factors_data { + int mux; + struct clk_factors_config *table; + void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); ++ const char *name; + }; + + static struct clk_factors_config sun4i_pll1_config = { +@@ -457,6 +458,14 @@ static const struct factors_data sun4i_pll5_data __initconst = { + .enable = 31, + .table = &sun4i_pll5_config, + .getter = sun4i_get_pll5_factors, ++ .name = "pll5", ++}; ++ ++static const struct factors_data sun4i_pll6_data __initconst = { ++ .enable = 31, ++ .table = &sun4i_pll5_config, ++ .getter = sun4i_get_pll5_factors, ++ .name = "pll6", + }; + + static const struct factors_data sun4i_apb1_data __initconst = { +@@ -499,14 +508,14 @@ static struct clk * __init sunxi_factors_clk_setup(struct device_node *node, + (parents[i] = of_clk_get_parent_name(node, i)) != NULL) + i++; + +- /* Nodes should be providing the name via clock-output-names +- * but originally our dts didn't, and so we used node->name. +- * The new, better nodes look like clk@deadbeef, so we pull the +- * name just in this case */ +- if (!strcmp("clk", clk_name)) { +- of_property_read_string_index(node, "clock-output-names", +- 0, &clk_name); +- } ++ /* ++ * some factor clocks, such as pll5 and pll6, may have multiple ++ * outputs, and have their name designated in factors_data ++ */ ++ if (data->name) ++ clk_name = data->name; ++ else ++ of_property_read_string(node, "clock-output-names", &clk_name); + + factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL); + if (!factors) +@@ -838,7 +847,7 @@ static const struct divs_data pll5_divs_data __initconst = { + }; + + static const struct divs_data pll6_divs_data __initconst = { +- .factors = &sun4i_pll5_data, ++ .factors = &sun4i_pll6_data, + .div = { + { .shift = 0, .table = pll6_sata_tbl, .gate = 14 }, /* M, SATA */ + { .fixed = 2 }, /* P, other */ +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/182-clk-sunxi-add-support-for-usb-clockreg-reset.patch b/target/linux/sunxi/patches-3.14/182-clk-sunxi-add-support-for-usb-clockreg-reset.patch new file mode 100644 index 0000000000..533b6013f1 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/182-clk-sunxi-add-support-for-usb-clockreg-reset.patch @@ -0,0 +1,133 @@ +From 0643a93746775da2189ab0afd8f748afcaa791c5 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Fri, 7 Feb 2014 16:21:49 +0100 +Subject: [PATCH] clk: sunxi: Add support for USB clock-register reset bits +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The usb-clk register is special in that it not only contains clk gate bits, +but also has a few reset bits. This commit adds support for this by allowing +gates type sunxi clks to also register a reset controller. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> +Signed-off-by: Emilio López <emilio@elopez.com.ar> +--- + drivers/clk/sunxi/clk-sunxi.c | 71 +++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 71 insertions(+) + +diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c +index 736fb60..23beb6e 100644 +--- a/drivers/clk/sunxi/clk-sunxi.c ++++ b/drivers/clk/sunxi/clk-sunxi.c +@@ -18,6 +18,7 @@ + #include <linux/clkdev.h> + #include <linux/of.h> + #include <linux/of_address.h> ++#include <linux/reset-controller.h> + + #include "clk-factors.h" + +@@ -688,6 +689,59 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, + + + /** ++ * sunxi_gates_reset... - reset bits in leaf gate clk registers handling ++ */ ++ ++struct gates_reset_data { ++ void __iomem *reg; ++ spinlock_t *lock; ++ struct reset_controller_dev rcdev; ++}; ++ ++static int sunxi_gates_reset_assert(struct reset_controller_dev *rcdev, ++ unsigned long id) ++{ ++ struct gates_reset_data *data = container_of(rcdev, ++ struct gates_reset_data, ++ rcdev); ++ unsigned long flags; ++ u32 reg; ++ ++ spin_lock_irqsave(data->lock, flags); ++ ++ reg = readl(data->reg); ++ writel(reg & ~BIT(id), data->reg); ++ ++ spin_unlock_irqrestore(data->lock, flags); ++ ++ return 0; ++} ++ ++static int sunxi_gates_reset_deassert(struct reset_controller_dev *rcdev, ++ unsigned long id) ++{ ++ struct gates_reset_data *data = container_of(rcdev, ++ struct gates_reset_data, ++ rcdev); ++ unsigned long flags; ++ u32 reg; ++ ++ spin_lock_irqsave(data->lock, flags); ++ ++ reg = readl(data->reg); ++ writel(reg | BIT(id), data->reg); ++ ++ spin_unlock_irqrestore(data->lock, flags); ++ ++ return 0; ++} ++ ++static struct reset_control_ops sunxi_gates_reset_ops = { ++ .assert = sunxi_gates_reset_assert, ++ .deassert = sunxi_gates_reset_deassert, ++}; ++ ++/** + * sunxi_gates_clk_setup() - Setup function for leaf gates on clocks + */ + +@@ -695,6 +749,7 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, + + struct gates_data { + DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE); ++ u32 reset_mask; + }; + + static const struct gates_data sun4i_axi_gates_data __initconst = { +@@ -765,6 +820,7 @@ static void __init sunxi_gates_clk_setup(struct device_node *node, + struct gates_data *data) + { + struct clk_onecell_data *clk_data; ++ struct gates_reset_data *reset_data; + const char *clk_parent; + const char *clk_name; + void *reg; +@@ -808,6 +864,21 @@ static void __init sunxi_gates_clk_setup(struct device_node *node, + clk_data->clk_num = i; + + of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); ++ ++ /* Register a reset controler for gates with reset bits */ ++ if (data->reset_mask == 0) ++ return; ++ ++ reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); ++ if (!reset_data) ++ return; ++ ++ reset_data->reg = reg; ++ reset_data->lock = &clk_lock; ++ reset_data->rcdev.nr_resets = __fls(data->reset_mask) + 1; ++ reset_data->rcdev.ops = &sunxi_gates_reset_ops; ++ reset_data->rcdev.of_node = node; ++ reset_controller_register(&reset_data->rcdev); + } + + +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/182-clk-sunxi-get-divs-parent-clockname.patch b/target/linux/sunxi/patches-3.14/182-clk-sunxi-get-divs-parent-clockname.patch new file mode 100644 index 0000000000..53f2217247 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/182-clk-sunxi-get-divs-parent-clockname.patch @@ -0,0 +1,45 @@ +From 0566b74f93eaf6b2281d2605a63e06e6ba809334 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Mon, 3 Feb 2014 09:51:40 +0800 +Subject: [PATCH] clk: sunxi: get divs parent clock name from parent factor + clock +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Divs clocks consist of a parent factor clock with multiple outputs, +and seperate clocks for each output. Get the name of the parent +clock from the parent factor clock, instead of the DT node name. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> +Acked-by: Mike Turquette <mturquette@linaro.org> +Signed-off-by: Emilio López <emilio@elopez.com.ar> +--- + drivers/clk/sunxi/clk-sunxi.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c +index 7a2ed98..736fb60 100644 +--- a/drivers/clk/sunxi/clk-sunxi.c ++++ b/drivers/clk/sunxi/clk-sunxi.c +@@ -869,7 +869,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node, + struct divs_data *data) + { + struct clk_onecell_data *clk_data; +- const char *parent = node->name; ++ const char *parent; + const char *clk_name; + struct clk **clks, *pclk; + struct clk_hw *gate_hw, *rate_hw; +@@ -883,6 +883,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node, + + /* Set up factor clock that we will be dividing */ + pclk = sunxi_factors_clk_setup(node, data->factors); ++ parent = __clk_get_name(pclk); + + reg = of_iomap(node, 0); + +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/183-clk-sunxi-add-usb-clockreg-defs.patch b/target/linux/sunxi/patches-3.14/183-clk-sunxi-add-usb-clockreg-defs.patch new file mode 100644 index 0000000000..07e8879896 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/183-clk-sunxi-add-usb-clockreg-defs.patch @@ -0,0 +1,76 @@ +From c61dfeb17581d32360a817ba40636aaed85caade Mon Sep 17 00:00:00 2001 +From: Roman Byshko <rbyshko@gmail.com> +Date: Fri, 7 Feb 2014 16:21:50 +0100 +Subject: [PATCH] clk: sunxi: Add USB clock register defintions +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add register definitions for the usb-clk register found on sun4i, sun5i and +sun7i SoCs. + +Signed-off-by: Roman Byshko <rbyshko@gmail.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> +Signed-off-by: Emilio López <emilio@elopez.com.ar> +--- + Documentation/devicetree/bindings/clock/sunxi.txt | 5 +++++ + drivers/clk/sunxi/clk-sunxi.c | 12 ++++++++++++ + 2 files changed, 17 insertions(+) + +diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt +index 0cf679b..ca2b692 100644 +--- a/Documentation/devicetree/bindings/clock/sunxi.txt ++++ b/Documentation/devicetree/bindings/clock/sunxi.txt +@@ -37,6 +37,8 @@ Required properties: + "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 + "allwinner,sun4i-mod0-clk" - for the module 0 family of clocks + "allwinner,sun7i-a20-out-clk" - for the external output clocks ++ "allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20 ++ "allwinner,sun5i-a13-usb-clk" - for usb gates + resets on A13 + + Required properties for all clocks: + - reg : shall be the control register address for the clock. +@@ -50,6 +52,9 @@ Required properties for all clocks: + If the clock module only has one output, the name shall be the + module name. + ++And "allwinner,*-usb-clk" clocks also require: ++- reset-cells : shall be set to 1 ++ + Clock consumers should specify the desired clocks they use with a + "clocks" phandle cell. Consumers that are using a gated clock should + provide an additional ID in their clock property. This ID is the +diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c +index 23beb6e..a779c31 100644 +--- a/drivers/clk/sunxi/clk-sunxi.c ++++ b/drivers/clk/sunxi/clk-sunxi.c +@@ -816,6 +816,16 @@ static const struct gates_data sun7i_a20_apb1_gates_data __initconst = { + .mask = { 0xff80ff }, + }; + ++static const struct gates_data sun4i_a10_usb_gates_data __initconst = { ++ .mask = {0x1C0}, ++ .reset_mask = 0x07, ++}; ++ ++static const struct gates_data sun5i_a13_usb_gates_data __initconst = { ++ .mask = {0x140}, ++ .reset_mask = 0x03, ++}; ++ + static void __init sunxi_gates_clk_setup(struct device_node *node, + struct gates_data *data) + { +@@ -1107,6 +1117,8 @@ static const struct of_device_id clk_gates_match[] __initconst = { + {.compatible = "allwinner,sun6i-a31-apb1-gates-clk", .data = &sun6i_a31_apb1_gates_data,}, + {.compatible = "allwinner,sun7i-a20-apb1-gates-clk", .data = &sun7i_a20_apb1_gates_data,}, + {.compatible = "allwinner,sun6i-a31-apb2-gates-clk", .data = &sun6i_a31_apb2_gates_data,}, ++ {.compatible = "allwinner,sun4i-a10-usb-clk", .data = &sun4i_a10_usb_gates_data,}, ++ {.compatible = "allwinner,sun5i-a13-usb-clk", .data = &sun5i_a13_usb_gates_data,}, + {} + }; + +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/184-clk-sunxi-add-pll6-on-a31.patch b/target/linux/sunxi/patches-3.14/184-clk-sunxi-add-pll6-on-a31.patch new file mode 100644 index 0000000000..4d71742803 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/184-clk-sunxi-add-pll6-on-a31.patch @@ -0,0 +1,111 @@ +From c225f78660cd61914f25dd00499c7ae71d1d6919 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Wed, 5 Feb 2014 14:05:03 +0100 +Subject: [PATCH] clk: sunxi: Add support for PLL6 on the A31 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The A31 has a slightly different PLL6 clock. Add support for this new clock in +our driver. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +Signed-off-by: Emilio López <emilio@elopez.com.ar> +--- + Documentation/devicetree/bindings/clock/sunxi.txt | 1 + + drivers/clk/sunxi/clk-sunxi.c | 45 +++++++++++++++++++++++ + 2 files changed, 46 insertions(+) + +diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt +index ca2b692..c37c764 100644 +--- a/Documentation/devicetree/bindings/clock/sunxi.txt ++++ b/Documentation/devicetree/bindings/clock/sunxi.txt +@@ -11,6 +11,7 @@ Required properties: + "allwinner,sun6i-a31-pll1-clk" - for the main PLL clock on A31 + "allwinner,sun4i-pll5-clk" - for the PLL5 clock + "allwinner,sun4i-pll6-clk" - for the PLL6 clock ++ "allwinner,sun6i-a31-pll6-clk" - for the PLL6 clock on A31 + "allwinner,sun4i-cpu-clk" - for the CPU multiplexer clock + "allwinner,sun4i-axi-clk" - for the AXI clock + "allwinner,sun4i-axi-gates-clk" - for the AXI gates +diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c +index a779c31..d4cf297 100644 +--- a/drivers/clk/sunxi/clk-sunxi.c ++++ b/drivers/clk/sunxi/clk-sunxi.c +@@ -252,7 +252,38 @@ static void sun4i_get_pll5_factors(u32 *freq, u32 parent_rate, + *n = DIV_ROUND_UP(div, (*k+1)); + } + ++/** ++ * sun6i_a31_get_pll6_factors() - calculates n, k factors for A31 PLL6 ++ * PLL6 rate is calculated as follows ++ * rate = parent_rate * n * (k + 1) / 2 ++ * parent_rate is always 24Mhz ++ */ ++ ++static void sun6i_a31_get_pll6_factors(u32 *freq, u32 parent_rate, ++ u8 *n, u8 *k, u8 *m, u8 *p) ++{ ++ u8 div; ++ ++ /* ++ * We always have 24MHz / 2, so we can just say that our ++ * parent clock is 12MHz. ++ */ ++ parent_rate = parent_rate / 2; ++ ++ /* Normalize value to a parent_rate multiple (24M / 2) */ ++ div = *freq / parent_rate; ++ *freq = parent_rate * div; ++ ++ /* we were called to round the frequency, we can now return */ ++ if (n == NULL) ++ return; ++ ++ *k = div / 32; ++ if (*k > 3) ++ *k = 3; + ++ *n = DIV_ROUND_UP(div, (*k+1)); ++} + + /** + * sun4i_get_apb1_factors() - calculates m, p factors for APB1 +@@ -420,6 +451,13 @@ static struct clk_factors_config sun4i_pll5_config = { + .kwidth = 2, + }; + ++static struct clk_factors_config sun6i_a31_pll6_config = { ++ .nshift = 8, ++ .nwidth = 5, ++ .kshift = 4, ++ .kwidth = 2, ++}; ++ + static struct clk_factors_config sun4i_apb1_config = { + .mshift = 0, + .mwidth = 5, +@@ -469,6 +507,12 @@ static const struct factors_data sun4i_pll6_data __initconst = { + .name = "pll6", + }; + ++static const struct factors_data sun6i_a31_pll6_data __initconst = { ++ .enable = 31, ++ .table = &sun6i_a31_pll6_config, ++ .getter = sun6i_a31_get_pll6_factors, ++}; ++ + static const struct factors_data sun4i_apb1_data __initconst = { + .table = &sun4i_apb1_config, + .getter = sun4i_get_apb1_factors, +@@ -1069,6 +1113,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node, + static const struct of_device_id clk_factors_match[] __initconst = { + {.compatible = "allwinner,sun4i-pll1-clk", .data = &sun4i_pll1_data,}, + {.compatible = "allwinner,sun6i-a31-pll1-clk", .data = &sun6i_a31_pll1_data,}, ++ {.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,}, + {.compatible = "allwinner,sun4i-apb1-clk", .data = &sun4i_apb1_data,}, + {.compatible = "allwinner,sun4i-mod0-clk", .data = &sun4i_mod0_data,}, + {.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,}, +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/185-clk-sunxi-add-a20-a31-gmac-clock.patch b/target/linux/sunxi/patches-3.14/185-clk-sunxi-add-a20-a31-gmac-clock.patch new file mode 100644 index 0000000000..e105c25702 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/185-clk-sunxi-add-a20-a31-gmac-clock.patch @@ -0,0 +1,182 @@ +From dd91dc4b9c55c8fa24738249214274442e2fcbd3 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Mon, 10 Feb 2014 18:35:47 +0800 +Subject: [PATCH] clk: sunxi: Add Allwinner A20/A31 GMAC clock unit +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The Allwinner A20/A31 clock module controls the transmit clock source +and interface type of the GMAC ethernet controller. Model this as +a single clock for GMAC drivers to use. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> +Signed-off-by: Emilio López <emilio@elopez.com.ar> +--- + Documentation/devicetree/bindings/clock/sunxi.txt | 30 +++++++ + drivers/clk/sunxi/clk-sunxi.c | 96 +++++++++++++++++++++++ + 2 files changed, 126 insertions(+) + +diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt +index c37c764..256a908 100644 +--- a/Documentation/devicetree/bindings/clock/sunxi.txt ++++ b/Documentation/devicetree/bindings/clock/sunxi.txt +@@ -38,6 +38,7 @@ Required properties: + "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 + "allwinner,sun4i-mod0-clk" - for the module 0 family of clocks + "allwinner,sun7i-a20-out-clk" - for the external output clocks ++ "allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31 + "allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20 + "allwinner,sun5i-a13-usb-clk" - for usb gates + resets on A13 + +@@ -56,6 +57,9 @@ Required properties for all clocks: + And "allwinner,*-usb-clk" clocks also require: + - reset-cells : shall be set to 1 + ++For "allwinner,sun7i-a20-gmac-clk", the parent clocks shall be fixed rate ++dummy clocks at 25 MHz and 125 MHz, respectively. See example. ++ + Clock consumers should specify the desired clocks they use with a + "clocks" phandle cell. Consumers that are using a gated clock should + provide an additional ID in their clock property. This ID is the +@@ -102,3 +106,29 @@ mmc0_clk: clk@01c20088 { + clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; + clock-output-names = "mmc0"; + }; ++ ++mii_phy_tx_clk: clk@2 { ++ #clock-cells = <0>; ++ compatible = "fixed-clock"; ++ clock-frequency = <25000000>; ++ clock-output-names = "mii_phy_tx"; ++}; ++ ++gmac_int_tx_clk: clk@3 { ++ #clock-cells = <0>; ++ compatible = "fixed-clock"; ++ clock-frequency = <125000000>; ++ clock-output-names = "gmac_int_tx"; ++}; ++ ++gmac_clk: clk@01c20164 { ++ #clock-cells = <0>; ++ compatible = "allwinner,sun7i-a20-gmac-clk"; ++ reg = <0x01c20164 0x4>; ++ /* ++ * The first clock must be fixed at 25MHz; ++ * the second clock must be fixed at 125MHz ++ */ ++ clocks = <&mii_phy_tx_clk>, <&gmac_int_tx_clk>; ++ clock-output-names = "gmac"; ++}; +diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c +index d4cf297..335c987 100644 +--- a/drivers/clk/sunxi/clk-sunxi.c ++++ b/drivers/clk/sunxi/clk-sunxi.c +@@ -411,6 +411,102 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate, + + + /** ++ * sun7i_a20_gmac_clk_setup - Setup function for A20/A31 GMAC clock module ++ * ++ * This clock looks something like this ++ * ________________________ ++ * MII TX clock from PHY >-----|___________ _________|----> to GMAC core ++ * GMAC Int. RGMII TX clk >----|___________\__/__gate---|----> to PHY ++ * Ext. 125MHz RGMII TX clk >--|__divider__/ | ++ * |________________________| ++ * ++ * The external 125 MHz reference is optional, i.e. GMAC can use its ++ * internal TX clock just fine. The A31 GMAC clock module does not have ++ * the divider controls for the external reference. ++ * ++ * To keep it simple, let the GMAC use either the MII TX clock for MII mode, ++ * and its internal TX clock for GMII and RGMII modes. The GMAC driver should ++ * select the appropriate source and gate/ungate the output to the PHY. ++ * ++ * Only the GMAC should use this clock. Altering the clock so that it doesn't ++ * match the GMAC's operation parameters will result in the GMAC not being ++ * able to send traffic out. The GMAC driver should set the clock rate and ++ * enable/disable this clock to configure the required state. The clock ++ * driver then responds by auto-reparenting the clock. ++ */ ++ ++#define SUN7I_A20_GMAC_GPIT 2 ++#define SUN7I_A20_GMAC_MASK 0x3 ++#define SUN7I_A20_GMAC_PARENTS 2 ++ ++static void __init sun7i_a20_gmac_clk_setup(struct device_node *node) ++{ ++ struct clk *clk; ++ struct clk_mux *mux; ++ struct clk_gate *gate; ++ const char *clk_name = node->name; ++ const char *parents[SUN7I_A20_GMAC_PARENTS]; ++ void *reg; ++ ++ if (of_property_read_string(node, "clock-output-names", &clk_name)) ++ return; ++ ++ /* allocate mux and gate clock structs */ ++ mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); ++ if (!mux) ++ return; ++ ++ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); ++ if (!gate) ++ goto free_mux; ++ ++ /* gmac clock requires exactly 2 parents */ ++ parents[0] = of_clk_get_parent_name(node, 0); ++ parents[1] = of_clk_get_parent_name(node, 1); ++ if (!parents[0] || !parents[1]) ++ goto free_gate; ++ ++ reg = of_iomap(node, 0); ++ if (!reg) ++ goto free_gate; ++ ++ /* set up gate and fixed rate properties */ ++ gate->reg = reg; ++ gate->bit_idx = SUN7I_A20_GMAC_GPIT; ++ gate->lock = &clk_lock; ++ mux->reg = reg; ++ mux->mask = SUN7I_A20_GMAC_MASK; ++ mux->flags = CLK_MUX_INDEX_BIT; ++ mux->lock = &clk_lock; ++ ++ clk = clk_register_composite(NULL, clk_name, ++ parents, SUN7I_A20_GMAC_PARENTS, ++ &mux->hw, &clk_mux_ops, ++ NULL, NULL, ++ &gate->hw, &clk_gate_ops, ++ 0); ++ ++ if (IS_ERR(clk)) ++ goto iounmap_reg; ++ ++ of_clk_add_provider(node, of_clk_src_simple_get, clk); ++ clk_register_clkdev(clk, clk_name, NULL); ++ ++ return; ++ ++iounmap_reg: ++ iounmap(reg); ++free_gate: ++ kfree(gate); ++free_mux: ++ kfree(mux); ++} ++CLK_OF_DECLARE(sun7i_a20_gmac, "allwinner,sun7i-a20-gmac-clk", ++ sun7i_a20_gmac_clk_setup); ++ ++ ++ ++/** + * sunxi_factors_clk_setup() - Setup function for factor clocks + */ + +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/186-clk-sunxi-add-new-clock-compats.patch b/target/linux/sunxi/patches-3.14/186-clk-sunxi-add-new-clock-compats.patch new file mode 100644 index 0000000000..728fca8454 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/186-clk-sunxi-add-new-clock-compats.patch @@ -0,0 +1,187 @@ +From 45ff9697ed1668e82ca3902b32309e157464e745 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Thu, 6 Feb 2014 09:55:57 +0100 +Subject: [PATCH] clk: sunxi: Add new clock compatibles +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The Allwinner A10 compatibles were following a slightly different compatible +patterns than the rest of the SoCs for historical reasons. Add compatibles +matching the other pattern to the clock driver for consistency, and keep the +older one for backward compatibility. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +Signed-off-by: Emilio López <emilio@elopez.com.ar> +--- + Documentation/devicetree/bindings/clock/sunxi.txt | 36 +++++++++++------------ + drivers/clk/sunxi/clk-sunxi.c | 30 +++++++++---------- + 2 files changed, 33 insertions(+), 33 deletions(-) + +diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt +index 256a908..a5160d8 100644 +--- a/Documentation/devicetree/bindings/clock/sunxi.txt ++++ b/Documentation/devicetree/bindings/clock/sunxi.txt +@@ -6,37 +6,37 @@ This binding uses the common clock binding[1]. + + Required properties: + - compatible : shall be one of the following: +- "allwinner,sun4i-osc-clk" - for a gatable oscillator +- "allwinner,sun4i-pll1-clk" - for the main PLL clock and PLL4 ++ "allwinner,sun4i-a10-osc-clk" - for a gatable oscillator ++ "allwinner,sun4i-a10-pll1-clk" - for the main PLL clock and PLL4 + "allwinner,sun6i-a31-pll1-clk" - for the main PLL clock on A31 +- "allwinner,sun4i-pll5-clk" - for the PLL5 clock +- "allwinner,sun4i-pll6-clk" - for the PLL6 clock ++ "allwinner,sun4i-a10-pll5-clk" - for the PLL5 clock ++ "allwinner,sun4i-a10-pll6-clk" - for the PLL6 clock + "allwinner,sun6i-a31-pll6-clk" - for the PLL6 clock on A31 +- "allwinner,sun4i-cpu-clk" - for the CPU multiplexer clock +- "allwinner,sun4i-axi-clk" - for the AXI clock +- "allwinner,sun4i-axi-gates-clk" - for the AXI gates +- "allwinner,sun4i-ahb-clk" - for the AHB clock +- "allwinner,sun4i-ahb-gates-clk" - for the AHB gates on A10 ++ "allwinner,sun4i-a10-cpu-clk" - for the CPU multiplexer clock ++ "allwinner,sun4i-a10-axi-clk" - for the AXI clock ++ "allwinner,sun4i-a10-axi-gates-clk" - for the AXI gates ++ "allwinner,sun4i-a10-ahb-clk" - for the AHB clock ++ "allwinner,sun4i-a10-ahb-gates-clk" - for the AHB gates on A10 + "allwinner,sun5i-a13-ahb-gates-clk" - for the AHB gates on A13 + "allwinner,sun5i-a10s-ahb-gates-clk" - for the AHB gates on A10s + "allwinner,sun7i-a20-ahb-gates-clk" - for the AHB gates on A20 + "allwinner,sun6i-a31-ahb1-mux-clk" - for the AHB1 multiplexer on A31 + "allwinner,sun6i-a31-ahb1-gates-clk" - for the AHB1 gates on A31 +- "allwinner,sun4i-apb0-clk" - for the APB0 clock +- "allwinner,sun4i-apb0-gates-clk" - for the APB0 gates on A10 ++ "allwinner,sun4i-a10-apb0-clk" - for the APB0 clock ++ "allwinner,sun4i-a10-apb0-gates-clk" - for the APB0 gates on A10 + "allwinner,sun5i-a13-apb0-gates-clk" - for the APB0 gates on A13 + "allwinner,sun5i-a10s-apb0-gates-clk" - for the APB0 gates on A10s + "allwinner,sun7i-a20-apb0-gates-clk" - for the APB0 gates on A20 +- "allwinner,sun4i-apb1-clk" - for the APB1 clock +- "allwinner,sun4i-apb1-mux-clk" - for the APB1 clock muxing +- "allwinner,sun4i-apb1-gates-clk" - for the APB1 gates on A10 ++ "allwinner,sun4i-a10-apb1-clk" - for the APB1 clock ++ "allwinner,sun4i-a10-apb1-mux-clk" - for the APB1 clock muxing ++ "allwinner,sun4i-a10-apb1-gates-clk" - for the APB1 gates on A10 + "allwinner,sun5i-a13-apb1-gates-clk" - for the APB1 gates on A13 + "allwinner,sun5i-a10s-apb1-gates-clk" - for the APB1 gates on A10s + "allwinner,sun6i-a31-apb1-gates-clk" - for the APB1 gates on A31 + "allwinner,sun7i-a20-apb1-gates-clk" - for the APB1 gates on A20 + "allwinner,sun6i-a31-apb2-div-clk" - for the APB2 gates on A31 + "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 +- "allwinner,sun4i-mod0-clk" - for the module 0 family of clocks ++ "allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks + "allwinner,sun7i-a20-out-clk" - for the external output clocks + "allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31 + "allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20 +@@ -69,7 +69,7 @@ For example: + + osc24M: clk@01c20050 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-osc-clk"; ++ compatible = "allwinner,sun4i-a10-osc-clk"; + reg = <0x01c20050 0x4>; + clocks = <&osc24M_fixed>; + clock-output-names = "osc24M"; +@@ -77,7 +77,7 @@ osc24M: clk@01c20050 { + + pll1: clk@01c20000 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-pll1-clk"; ++ compatible = "allwinner,sun4i-a10-pll1-clk"; + reg = <0x01c20000 0x4>; + clocks = <&osc24M>; + clock-output-names = "pll1"; +@@ -93,7 +93,7 @@ pll5: clk@01c20020 { + + cpu: cpu@01c20054 { + #clock-cells = <0>; +- compatible = "allwinner,sun4i-cpu-clk"; ++ compatible = "allwinner,sun4i-a10-cpu-clk"; + reg = <0x01c20054 0x4>; + clocks = <&osc32k>, <&osc24M>, <&pll1>; + clock-output-names = "cpu"; +diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c +index 335c987..23baad9 100644 +--- a/drivers/clk/sunxi/clk-sunxi.c ++++ b/drivers/clk/sunxi/clk-sunxi.c +@@ -80,7 +80,7 @@ static void __init sun4i_osc_clk_setup(struct device_node *node) + err_free_fixed: + kfree(fixed); + } +-CLK_OF_DECLARE(sun4i_osc, "allwinner,sun4i-osc-clk", sun4i_osc_clk_setup); ++CLK_OF_DECLARE(sun4i_osc, "allwinner,sun4i-a10-osc-clk", sun4i_osc_clk_setup); + + + +@@ -1207,52 +1207,52 @@ static void __init sunxi_divs_clk_setup(struct device_node *node, + + /* Matches for factors clocks */ + static const struct of_device_id clk_factors_match[] __initconst = { +- {.compatible = "allwinner,sun4i-pll1-clk", .data = &sun4i_pll1_data,}, ++ {.compatible = "allwinner,sun4i-a10-pll1-clk", .data = &sun4i_pll1_data,}, + {.compatible = "allwinner,sun6i-a31-pll1-clk", .data = &sun6i_a31_pll1_data,}, + {.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,}, +- {.compatible = "allwinner,sun4i-apb1-clk", .data = &sun4i_apb1_data,}, +- {.compatible = "allwinner,sun4i-mod0-clk", .data = &sun4i_mod0_data,}, ++ {.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,}, ++ {.compatible = "allwinner,sun4i-a10-mod0-clk", .data = &sun4i_mod0_data,}, + {.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,}, + {} + }; + + /* Matches for divider clocks */ + static const struct of_device_id clk_div_match[] __initconst = { +- {.compatible = "allwinner,sun4i-axi-clk", .data = &sun4i_axi_data,}, +- {.compatible = "allwinner,sun4i-ahb-clk", .data = &sun4i_ahb_data,}, +- {.compatible = "allwinner,sun4i-apb0-clk", .data = &sun4i_apb0_data,}, ++ {.compatible = "allwinner,sun4i-a10-axi-clk", .data = &sun4i_axi_data,}, ++ {.compatible = "allwinner,sun4i-a10-ahb-clk", .data = &sun4i_ahb_data,}, ++ {.compatible = "allwinner,sun4i-a10-apb0-clk", .data = &sun4i_apb0_data,}, + {.compatible = "allwinner,sun6i-a31-apb2-div-clk", .data = &sun6i_a31_apb2_div_data,}, + {} + }; + + /* Matches for divided outputs */ + static const struct of_device_id clk_divs_match[] __initconst = { +- {.compatible = "allwinner,sun4i-pll5-clk", .data = &pll5_divs_data,}, +- {.compatible = "allwinner,sun4i-pll6-clk", .data = &pll6_divs_data,}, ++ {.compatible = "allwinner,sun4i-a10-pll5-clk", .data = &pll5_divs_data,}, ++ {.compatible = "allwinner,sun4i-a10-pll6-clk", .data = &pll6_divs_data,}, + {} + }; + + /* Matches for mux clocks */ + static const struct of_device_id clk_mux_match[] __initconst = { +- {.compatible = "allwinner,sun4i-cpu-clk", .data = &sun4i_cpu_mux_data,}, +- {.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &sun4i_apb1_mux_data,}, ++ {.compatible = "allwinner,sun4i-a10-cpu-clk", .data = &sun4i_cpu_mux_data,}, ++ {.compatible = "allwinner,sun4i-a10-apb1-mux-clk", .data = &sun4i_apb1_mux_data,}, + {.compatible = "allwinner,sun6i-a31-ahb1-mux-clk", .data = &sun6i_a31_ahb1_mux_data,}, + {} + }; + + /* Matches for gate clocks */ + static const struct of_device_id clk_gates_match[] __initconst = { +- {.compatible = "allwinner,sun4i-axi-gates-clk", .data = &sun4i_axi_gates_data,}, +- {.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &sun4i_ahb_gates_data,}, ++ {.compatible = "allwinner,sun4i-a10-axi-gates-clk", .data = &sun4i_axi_gates_data,}, ++ {.compatible = "allwinner,sun4i-a10-ahb-gates-clk", .data = &sun4i_ahb_gates_data,}, + {.compatible = "allwinner,sun5i-a10s-ahb-gates-clk", .data = &sun5i_a10s_ahb_gates_data,}, + {.compatible = "allwinner,sun5i-a13-ahb-gates-clk", .data = &sun5i_a13_ahb_gates_data,}, + {.compatible = "allwinner,sun6i-a31-ahb1-gates-clk", .data = &sun6i_a31_ahb1_gates_data,}, + {.compatible = "allwinner,sun7i-a20-ahb-gates-clk", .data = &sun7i_a20_ahb_gates_data,}, +- {.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &sun4i_apb0_gates_data,}, ++ {.compatible = "allwinner,sun4i-a10-apb0-gates-clk", .data = &sun4i_apb0_gates_data,}, + {.compatible = "allwinner,sun5i-a10s-apb0-gates-clk", .data = &sun5i_a10s_apb0_gates_data,}, + {.compatible = "allwinner,sun5i-a13-apb0-gates-clk", .data = &sun5i_a13_apb0_gates_data,}, + {.compatible = "allwinner,sun7i-a20-apb0-gates-clk", .data = &sun7i_a20_apb0_gates_data,}, +- {.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &sun4i_apb1_gates_data,}, ++ {.compatible = "allwinner,sun4i-a10-apb1-gates-clk", .data = &sun4i_apb1_gates_data,}, + {.compatible = "allwinner,sun5i-a10s-apb1-gates-clk", .data = &sun5i_a10s_apb1_gates_data,}, + {.compatible = "allwinner,sun5i-a13-apb1-gates-clk", .data = &sun5i_a13_apb1_gates_data,}, + {.compatible = "allwinner,sun6i-a31-apb1-gates-clk", .data = &sun6i_a31_apb1_gates_data,}, +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/187-clk-sunxi-automatic-reparenting.patch b/target/linux/sunxi/patches-3.14/187-clk-sunxi-automatic-reparenting.patch new file mode 100644 index 0000000000..b78b661ed8 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/187-clk-sunxi-automatic-reparenting.patch @@ -0,0 +1,73 @@ +From b416520f239aeaa4207ebe84c22247cff2da444f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Emilio=20L=C3=B3pez?= <emilio@elopez.com.ar> +Date: Thu, 5 Sep 2013 19:52:41 -0300 +Subject: [PATCH] clk: sunxi: factors: automatic reparenting support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This commit implements .determine_rate, so that our factor clocks can be +reparented when needed. + +Signed-off-by: Emilio López <emilio@elopez.com.ar> +--- + drivers/clk/sunxi/clk-factors.c | 36 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c +index 9e23264..3806d97 100644 +--- a/drivers/clk/sunxi/clk-factors.c ++++ b/drivers/clk/sunxi/clk-factors.c +@@ -77,6 +77,41 @@ static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate, + return rate; + } + ++static long clk_factors_determine_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *best_parent_rate, ++ struct clk **best_parent_p) ++{ ++ struct clk *clk = hw->clk, *parent, *best_parent = NULL; ++ int i, num_parents; ++ unsigned long parent_rate, best = 0, child_rate, best_child_rate = 0; ++ ++ /* find the parent that can help provide the fastest rate <= rate */ ++ num_parents = __clk_get_num_parents(clk); ++ for (i = 0; i < num_parents; i++) { ++ parent = clk_get_parent_by_index(clk, i); ++ if (!parent) ++ continue; ++ if (__clk_get_flags(clk) & CLK_SET_RATE_PARENT) ++ parent_rate = __clk_round_rate(parent, rate); ++ else ++ parent_rate = __clk_get_rate(parent); ++ ++ child_rate = clk_factors_round_rate(hw, rate, &parent_rate); ++ ++ if (child_rate <= rate && child_rate > best_child_rate) { ++ best_parent = parent; ++ best = parent_rate; ++ best_child_rate = child_rate; ++ } ++ } ++ ++ if (best_parent) ++ *best_parent_p = best_parent; ++ *best_parent_rate = best; ++ ++ return best_child_rate; ++} ++ + static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) + { +@@ -113,6 +148,7 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, + } + + const struct clk_ops clk_factors_ops = { ++ .determine_rate = clk_factors_determine_rate, + .recalc_rate = clk_factors_recalc_rate, + .round_rate = clk_factors_round_rate, + .set_rate = clk_factors_set_rate, +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/188-clk-sunxi-implement-mmc-phasectrl.patch b/target/linux/sunxi/patches-3.14/188-clk-sunxi-implement-mmc-phasectrl.patch new file mode 100644 index 0000000000..1288f8fed9 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/188-clk-sunxi-implement-mmc-phasectrl.patch @@ -0,0 +1,95 @@ +From 36268d704307282109ec246f65cac2a42c825629 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Emilio=20L=C3=B3pez?= <emilio@elopez.com.ar> +Date: Fri, 20 Sep 2013 20:29:17 -0300 +Subject: [PATCH] clk: sunxi: Implement MMC phase control +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +HdG: add header exporting clk_sunxi_mmc_phase_control + +Signed-off-by: Emilio López <emilio@elopez.com.ar> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + drivers/clk/sunxi/clk-sunxi.c | 35 +++++++++++++++++++++++++++++++++++ + include/linux/clk/sunxi.h | 22 ++++++++++++++++++++++ + 2 files changed, 57 insertions(+) + create mode 100644 include/linux/clk/sunxi.h + +diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c +index 23baad9..9afd8dd 100644 +--- a/drivers/clk/sunxi/clk-sunxi.c ++++ b/drivers/clk/sunxi/clk-sunxi.c +@@ -507,6 +507,41 @@ CLK_OF_DECLARE(sun7i_a20_gmac, "allwinner,sun7i-a20-gmac-clk", + + + /** ++ * clk_sunxi_mmc_phase_control() - configures MMC clock phase control ++ */ ++ ++void clk_sunxi_mmc_phase_control(struct clk_hw *hw, u8 sample, u8 output) ++{ ++ #define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw) ++ #define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw) ++ ++ struct clk_composite *composite = to_clk_composite(hw); ++ struct clk_hw *rate_hw = composite->rate_hw; ++ struct clk_factors *factors = to_clk_factors(rate_hw); ++ unsigned long flags = 0; ++ u32 reg; ++ ++ if (factors->lock) ++ spin_lock_irqsave(factors->lock, flags); ++ ++ reg = readl(factors->reg); ++ ++ /* set sample clock phase control */ ++ reg &= ~(0x7 << 20); ++ reg |= ((sample & 0x7) << 20); ++ ++ /* set output clock phase control */ ++ reg &= ~(0x7 << 8); ++ reg |= ((output & 0x7) << 8); ++ ++ writel(reg, factors->reg); ++ ++ if (factors->lock) ++ spin_unlock_irqrestore(factors->lock, flags); ++} ++ ++ ++/** + * sunxi_factors_clk_setup() - Setup function for factor clocks + */ + +diff --git a/include/linux/clk/sunxi.h b/include/linux/clk/sunxi.h +new file mode 100644 +index 0000000..1ef5c89 +--- /dev/null ++++ b/include/linux/clk/sunxi.h +@@ -0,0 +1,22 @@ ++/* ++ * Copyright 2013 - Hans de Goede <hdegoede@redhat.com> ++ * ++ * 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_SUNXI_H_ ++#define __LINUX_CLK_SUNXI_H_ ++ ++#include <linux/clk.h> ++ ++void clk_sunxi_mmc_phase_control(struct clk_hw *hw, u8 sample, u8 output); ++ ++#endif +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/190-ahci-libahci-changes.patch b/target/linux/sunxi/patches-3.14/190-ahci-libahci-changes.patch new file mode 100644 index 0000000000..21a119f96b --- /dev/null +++ b/target/linux/sunxi/patches-3.14/190-ahci-libahci-changes.patch @@ -0,0 +1,1361 @@ +From 3b5db9d024f173c30ef4060c31bb8e9fbd194cc1 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Mon, 2 Dec 2013 16:13:32 +0100 +Subject: [PATCH] libahci: Allow drivers to override start_engine + +Allwinner A10 and A20 ARM SoCs have an AHCI sata controller which needs a +special register to be poked before starting the DMA engine. + +This register gets reset on an ahci_stop_engine call, so there is no other +place then ahci_start_engine where this poking can be done. + +This commit allows drivers to override ahci_start_engine behavior for use by +the Allwinner AHCI driver (and potentially other drivers in the future). + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + drivers/ata/ahci.c | 6 ++++-- + drivers/ata/ahci.h | 6 ++++++ + drivers/ata/libahci.c | 26 +++++++++++++++++++------- + drivers/ata/sata_highbank.c | 3 ++- + 4 files changed, 31 insertions(+), 10 deletions(-) + +diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c +index c81d809..8bfc477 100644 +--- a/drivers/ata/ahci.c ++++ b/drivers/ata/ahci.c +@@ -578,6 +578,7 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) + { + struct ata_port *ap = link->ap; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + bool online; + int rc; + +@@ -588,7 +589,7 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, + rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), + deadline, &online, NULL); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); + +@@ -603,6 +604,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, + { + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + struct ata_taskfile tf; + bool online; +@@ -618,7 +620,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, + rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), + deadline, &online, NULL); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + /* The pseudo configuration device on SIMG4726 attached to + * ASUS P5W-DH Deluxe doesn't send signature FIS after +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index 2289efd..64d1a99d 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -323,6 +323,12 @@ struct ahci_host_priv { + u32 em_msg_type; /* EM message type */ + struct clk *clk; /* Only for platforms supporting clk */ + void *plat_data; /* Other platform data */ ++ /* ++ * Optional ahci_start_engine override, if not set this gets set to the ++ * default ahci_start_engine during ahci_save_initial_config, this can ++ * be overridden anytime before the host is activated. ++ */ ++ void (*start_engine)(struct ata_port *ap); + }; + + extern int ahci_ignore_sss; +diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c +index 36605ab..f839bb3 100644 +--- a/drivers/ata/libahci.c ++++ b/drivers/ata/libahci.c +@@ -394,6 +394,9 @@ static ssize_t ahci_show_em_supported(struct device *dev, + * + * If inconsistent, config values are fixed up by this function. + * ++ * If it is not set already this function sets hpriv->start_engine to ++ * ahci_start_engine. ++ * + * LOCKING: + * None. + */ +@@ -500,6 +503,9 @@ void ahci_save_initial_config(struct device *dev, + hpriv->cap = cap; + hpriv->cap2 = cap2; + hpriv->port_map = port_map; ++ ++ if (!hpriv->start_engine) ++ hpriv->start_engine = ahci_start_engine; + } + EXPORT_SYMBOL_GPL(ahci_save_initial_config); + +@@ -766,7 +772,7 @@ static void ahci_start_port(struct ata_port *ap) + + /* enable DMA */ + if (!(hpriv->flags & AHCI_HFLAG_DELAY_ENGINE)) +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + /* turn on LEDs */ + if (ap->flags & ATA_FLAG_EM) { +@@ -1234,7 +1240,7 @@ int ahci_kick_engine(struct ata_port *ap) + + /* restart engine */ + out_restart: +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + return rc; + } + EXPORT_SYMBOL_GPL(ahci_kick_engine); +@@ -1426,6 +1432,7 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class, + const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + struct ata_taskfile tf; + bool online; +@@ -1443,7 +1450,7 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class, + rc = sata_link_hardreset(link, timing, deadline, &online, + ahci_check_ready); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + if (online) + *class = ahci_dev_classify(ap); +@@ -2007,10 +2014,12 @@ static void ahci_thaw(struct ata_port *ap) + + void ahci_error_handler(struct ata_port *ap) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; ++ + if (!(ap->pflags & ATA_PFLAG_FROZEN)) { + /* restart engine */ + ahci_stop_engine(ap); +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + } + + sata_pmp_error_handler(ap); +@@ -2031,6 +2040,7 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) + + static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + struct ata_device *dev = ap->link.device; + u32 devslp, dm, dito, mdat, deto; +@@ -2094,7 +2104,7 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) + PORT_DEVSLP_ADSE); + writel(devslp, port_mmio + PORT_DEVSLP); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + /* enable device sleep feature for the drive */ + err_mask = ata_dev_set_feature(dev, +@@ -2106,6 +2116,7 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) + + static void ahci_enable_fbs(struct ata_port *ap) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 fbs; +@@ -2134,11 +2145,12 @@ static void ahci_enable_fbs(struct ata_port *ap) + } else + dev_err(ap->host->dev, "Failed to enable FBS\n"); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + } + + static void ahci_disable_fbs(struct ata_port *ap) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 fbs; +@@ -2166,7 +2178,7 @@ static void ahci_disable_fbs(struct ata_port *ap) + pp->fbs_enabled = false; + } + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + } + + static void ahci_pmp_attach(struct ata_port *ap) +diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c +index 870b11e..b3b18d1 100644 +--- a/drivers/ata/sata_highbank.c ++++ b/drivers/ata/sata_highbank.c +@@ -403,6 +403,7 @@ static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class, + static const unsigned long timing[] = { 5, 100, 500}; + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + struct ata_taskfile tf; + bool online; +@@ -431,7 +432,7 @@ static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class, + break; + } while (!online && retry--); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + if (online) + *class = ahci_dev_classify(ap); +-- +2.0.3 + +From fcc3a79f048480e6723b0cfd294f9eecbf73dfd9 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Thu, 16 Jan 2014 14:32:35 +0100 +Subject: [PATCH] ahci-platform: Add support for devices with more then 1 clock + +The allwinner-sun4i AHCI controller needs 2 clocks to be enabled and the +imx AHCI controller needs 3 clocks to be enabled. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + .../devicetree/bindings/ata/ahci-platform.txt | 1 + + drivers/ata/ahci.h | 3 +- + drivers/ata/ahci_platform.c | 119 ++++++++++++++++----- + include/linux/ahci_platform.h | 4 + + 4 files changed, 99 insertions(+), 28 deletions(-) + +diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt +index 89de156..3ced07d 100644 +--- a/Documentation/devicetree/bindings/ata/ahci-platform.txt ++++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt +@@ -10,6 +10,7 @@ Required properties: + + Optional properties: + - dma-coherent : Present if dma operations are coherent ++- clocks : a list of phandle + clock specifier pairs + + Example: + sata@ffe08000 { +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index 64d1a99d..c12862b 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -51,6 +51,7 @@ + + enum { + AHCI_MAX_PORTS = 32, ++ AHCI_MAX_CLKS = 3, + AHCI_MAX_SG = 168, /* hardware max is 64K */ + AHCI_DMA_BOUNDARY = 0xffffffff, + AHCI_MAX_CMDS = 32, +@@ -321,7 +322,7 @@ struct ahci_host_priv { + u32 em_loc; /* enclosure management location */ + u32 em_buf_sz; /* EM buffer size in byte */ + u32 em_msg_type; /* EM message type */ +- struct clk *clk; /* Only for platforms supporting clk */ ++ struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ + void *plat_data; /* Other platform data */ + /* + * Optional ahci_start_engine override, if not set this gets set to the +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 4b231ba..609975d 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -87,6 +87,66 @@ static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT("ahci_platform"), + }; + ++/** ++ * ahci_platform_enable_clks - Enable platform clocks ++ * @hpriv: host private area to store config values ++ * ++ * This function enables all the clks found in hpriv->clks, starting ++ * at index 0. If any clk fails to enable it disables all the clks ++ * already enabled in reverse order, and then returns an error. ++ * ++ * LOCKING: ++ * None. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_enable_clks(struct ahci_host_priv *hpriv) ++{ ++ int c, rc; ++ ++ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) { ++ rc = clk_prepare_enable(hpriv->clks[c]); ++ if (rc) ++ goto disable_unprepare_clk; ++ } ++ return 0; ++ ++disable_unprepare_clk: ++ while (--c >= 0) ++ clk_disable_unprepare(hpriv->clks[c]); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_enable_clks); ++ ++/** ++ * ahci_platform_disable_clks - Disable platform clocks ++ * @hpriv: host private area to store config values ++ * ++ * This function disables all the clks found in hpriv->clks, in reverse ++ * order of ahci_platform_enable_clks (starting at the end of the array). ++ * ++ * LOCKING: ++ * None. ++ */ ++void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) ++{ ++ int c; ++ ++ for (c = AHCI_MAX_CLKS - 1; c >= 0; c--) ++ if (hpriv->clks[c]) ++ clk_disable_unprepare(hpriv->clks[c]); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); ++ ++static void ahci_put_clks(struct ahci_host_priv *hpriv) ++{ ++ int c; ++ ++ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) ++ clk_put(hpriv->clks[c]); ++} ++ + static int ahci_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -97,6 +157,7 @@ static int ahci_probe(struct platform_device *pdev) + struct ahci_host_priv *hpriv; + struct ata_host *host; + struct resource *mem; ++ struct clk *clk; + int irq; + int n_ports; + int i; +@@ -131,17 +192,31 @@ static int ahci_probe(struct platform_device *pdev) + return -ENOMEM; + } + +- hpriv->clk = clk_get(dev, NULL); +- if (IS_ERR(hpriv->clk)) { +- dev_err(dev, "can't get clock\n"); +- } else { +- rc = clk_prepare_enable(hpriv->clk); +- if (rc) { +- dev_err(dev, "clock prepare enable failed"); +- goto free_clk; ++ for (i = 0; i < AHCI_MAX_CLKS; i++) { ++ /* ++ * For now we must use clk_get(dev, NULL) for the first clock, ++ * because some platforms (da850, spear13xx) are not yet ++ * converted to use devicetree for clocks. For new platforms ++ * this is equivalent to of_clk_get(dev->of_node, 0). ++ */ ++ if (i == 0) ++ clk = clk_get(dev, NULL); ++ else ++ clk = of_clk_get(dev->of_node, i); ++ ++ if (IS_ERR(clk)) { ++ rc = PTR_ERR(clk); ++ if (rc == -EPROBE_DEFER) ++ goto free_clk; ++ break; + } ++ hpriv->clks[i] = clk; + } + ++ rc = ahci_enable_clks(dev, hpriv); ++ if (rc) ++ goto free_clk; ++ + /* + * Some platforms might need to prepare for mmio region access, + * which could be done in the following init call. So, the mmio +@@ -222,11 +297,9 @@ static int ahci_probe(struct platform_device *pdev) + if (pdata && pdata->exit) + pdata->exit(dev); + disable_unprepare_clk: +- if (!IS_ERR(hpriv->clk)) +- clk_disable_unprepare(hpriv->clk); ++ ahci_disable_clks(hpriv); + free_clk: +- if (!IS_ERR(hpriv->clk)) +- clk_put(hpriv->clk); ++ ahci_put_clks(hpriv); + return rc; + } + +@@ -239,10 +312,8 @@ static void ahci_host_stop(struct ata_host *host) + if (pdata && pdata->exit) + pdata->exit(dev); + +- if (!IS_ERR(hpriv->clk)) { +- clk_disable_unprepare(hpriv->clk); +- clk_put(hpriv->clk); +- } ++ ahci_disable_clks(hpriv); ++ ahci_put_clks(hpriv); + } + + #ifdef CONFIG_PM_SLEEP +@@ -277,8 +348,7 @@ static int ahci_suspend(struct device *dev) + if (pdata && pdata->suspend) + return pdata->suspend(dev); + +- if (!IS_ERR(hpriv->clk)) +- clk_disable_unprepare(hpriv->clk); ++ ahci_disable_clks(hpriv); + + return 0; + } +@@ -290,13 +360,9 @@ static int ahci_resume(struct device *dev) + struct ahci_host_priv *hpriv = host->private_data; + int rc; + +- if (!IS_ERR(hpriv->clk)) { +- rc = clk_prepare_enable(hpriv->clk); +- if (rc) { +- dev_err(dev, "clock prepare enable failed"); +- return rc; +- } +- } ++ rc = ahci_enable_clks(dev, hpriv); ++ if (rc) ++ return rc; + + if (pdata && pdata->resume) { + rc = pdata->resume(dev); +@@ -317,8 +383,7 @@ static int ahci_resume(struct device *dev) + return 0; + + disable_unprepare_clk: +- if (!IS_ERR(hpriv->clk)) +- clk_disable_unprepare(hpriv->clk); ++ ahci_disable_clks(hpriv); + + return rc; + } +diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h +index 73a2500..769d065 100644 +--- a/include/linux/ahci_platform.h ++++ b/include/linux/ahci_platform.h +@@ -19,6 +19,7 @@ + + struct device; + struct ata_port_info; ++struct ahci_host_priv; + + struct ahci_platform_data { + int (*init)(struct device *dev, void __iomem *addr); +@@ -30,4 +31,7 @@ struct ahci_platform_data { + unsigned int mask_port_map; + }; + ++int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); ++void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); ++ + #endif /* _AHCI_PLATFORM_H */ +-- +2.0.3 + +From 3dc53b267843b6dfeb2eb67e52e17915dc2347ab Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Fri, 17 Jan 2014 13:23:21 +0100 +Subject: [PATCH] ahci-platform: Add support for an optional regulator for + sata-target power + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + .../devicetree/bindings/ata/ahci-platform.txt | 1 + + drivers/ata/ahci.h | 2 ++ + drivers/ata/ahci_platform.c | 36 ++++++++++++++++++++-- + 3 files changed, 37 insertions(+), 2 deletions(-) + +diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt +index 3ced07d..1ac807f 100644 +--- a/Documentation/devicetree/bindings/ata/ahci-platform.txt ++++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt +@@ -11,6 +11,7 @@ Required properties: + Optional properties: + - dma-coherent : Present if dma operations are coherent + - clocks : a list of phandle + clock specifier pairs ++- target-supply : regulator for SATA target power + + Example: + sata@ffe08000 { +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index c12862b..bf8100c 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -37,6 +37,7 @@ + + #include <linux/clk.h> + #include <linux/libata.h> ++#include <linux/regulator/consumer.h> + + /* Enclosure Management Control */ + #define EM_CTRL_MSG_TYPE 0x000f0000 +@@ -323,6 +324,7 @@ struct ahci_host_priv { + u32 em_buf_sz; /* EM buffer size in byte */ + u32 em_msg_type; /* EM message type */ + struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ ++ struct regulator *target_pwr; /* Optional */ + void *plat_data; /* Other platform data */ + /* + * Optional ahci_start_engine override, if not set this gets set to the +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 609975d..907c076 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -192,6 +192,14 @@ static int ahci_probe(struct platform_device *pdev) + return -ENOMEM; + } + ++ hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); ++ if (IS_ERR(hpriv->target_pwr)) { ++ rc = PTR_ERR(hpriv->target_pwr); ++ if (rc == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ hpriv->target_pwr = NULL; ++ } ++ + for (i = 0; i < AHCI_MAX_CLKS; i++) { + /* + * For now we must use clk_get(dev, NULL) for the first clock, +@@ -213,9 +221,15 @@ static int ahci_probe(struct platform_device *pdev) + hpriv->clks[i] = clk; + } + ++ if (hpriv->target_pwr) { ++ rc = regulator_enable(hpriv->target_pwr); ++ if (rc) ++ goto free_clk; ++ } ++ + rc = ahci_enable_clks(dev, hpriv); + if (rc) +- goto free_clk; ++ goto disable_regulator; + + /* + * Some platforms might need to prepare for mmio region access, +@@ -298,6 +312,9 @@ static int ahci_probe(struct platform_device *pdev) + pdata->exit(dev); + disable_unprepare_clk: + ahci_disable_clks(hpriv); ++disable_regulator: ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); + free_clk: + ahci_put_clks(hpriv); + return rc; +@@ -314,6 +331,9 @@ static void ahci_host_stop(struct ata_host *host) + + ahci_disable_clks(hpriv); + ahci_put_clks(hpriv); ++ ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); + } + + #ifdef CONFIG_PM_SLEEP +@@ -350,6 +370,9 @@ static int ahci_suspend(struct device *dev) + + ahci_disable_clks(hpriv); + ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++ + return 0; + } + +@@ -360,9 +383,15 @@ static int ahci_resume(struct device *dev) + struct ahci_host_priv *hpriv = host->private_data; + int rc; + ++ if (hpriv->target_pwr) { ++ rc = regulator_enable(hpriv->target_pwr); ++ if (rc) ++ return rc; ++ } ++ + rc = ahci_enable_clks(dev, hpriv); + if (rc) +- return rc; ++ goto disable_regulator; + + if (pdata && pdata->resume) { + rc = pdata->resume(dev); +@@ -384,6 +413,9 @@ static int ahci_resume(struct device *dev) + + disable_unprepare_clk: + ahci_disable_clks(hpriv); ++disable_regulator: ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); + + return rc; + } +-- +2.0.3 + +From 74f54552b061865ff46d43aa68d0c6e8852dc592 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Mon, 20 Jan 2014 14:54:40 +0100 +Subject: [PATCH] ahci-platform: Add enable_ / disable_resources helper + functions + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + drivers/ata/ahci_platform.c | 112 ++++++++++++++++++++++++++++-------------- + include/linux/ahci_platform.h | 2 + + 2 files changed, 77 insertions(+), 37 deletions(-) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 907c076..6ebbc17 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -139,6 +139,68 @@ void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) + } + EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); + ++/** ++ * ahci_platform_enable_resources - Enable platform resources ++ * @hpriv: host private area to store config values ++ * ++ * This function enables all ahci_platform managed resources in ++ * the following order: ++ * 1) Regulator ++ * 2) Clocks (through ahci_platform_enable_clks) ++ * ++ * If resource enabling fails at any point the previous enabled ++ * resources are disabled in reverse order. ++ * ++ * LOCKING: ++ * None. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) ++{ ++ int rc; ++ ++ if (hpriv->target_pwr) { ++ rc = regulator_enable(hpriv->target_pwr); ++ if (rc) ++ return rc; ++ } ++ ++ rc = ahci_platform_enable_clks(hpriv); ++ if (rc) ++ goto disable_regulator; ++ ++ return 0; ++ ++disable_regulator: ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); ++ ++/** ++ * ahci_platform_disable_resources - Disable platform resources ++ * @hpriv: host private area to store config values ++ * ++ * This function disables all ahci_platform managed resources in ++ * the following order: ++ * 1) Clocks (through ahci_platform_disable_clks) ++ * 2) Regulator ++ * ++ * LOCKING: ++ * None. ++ */ ++void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) ++{ ++ ahci_platform_disable_clks(hpriv); ++ ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); ++ + static void ahci_put_clks(struct ahci_host_priv *hpriv) + { + int c; +@@ -221,15 +283,9 @@ static int ahci_probe(struct platform_device *pdev) + hpriv->clks[i] = clk; + } + +- if (hpriv->target_pwr) { +- rc = regulator_enable(hpriv->target_pwr); +- if (rc) +- goto free_clk; +- } +- +- rc = ahci_enable_clks(dev, hpriv); ++ rc = ahci_platform_enable_resources(hpriv); + if (rc) +- goto disable_regulator; ++ goto free_clk; + + /* + * Some platforms might need to prepare for mmio region access, +@@ -240,7 +296,7 @@ static int ahci_probe(struct platform_device *pdev) + if (pdata && pdata->init) { + rc = pdata->init(dev, hpriv->mmio); + if (rc) +- goto disable_unprepare_clk; ++ goto disable_resources; + } + + ahci_save_initial_config(dev, hpriv, +@@ -310,11 +366,8 @@ static int ahci_probe(struct platform_device *pdev) + pdata_exit: + if (pdata && pdata->exit) + pdata->exit(dev); +-disable_unprepare_clk: +- ahci_disable_clks(hpriv); +-disable_regulator: +- if (hpriv->target_pwr) +- regulator_disable(hpriv->target_pwr); ++disable_resources: ++ ahci_platform_disable_resources(hpriv); + free_clk: + ahci_put_clks(hpriv); + return rc; +@@ -329,11 +382,8 @@ static void ahci_host_stop(struct ata_host *host) + if (pdata && pdata->exit) + pdata->exit(dev); + +- ahci_disable_clks(hpriv); ++ ahci_platform_disable_resources(hpriv); + ahci_put_clks(hpriv); +- +- if (hpriv->target_pwr) +- regulator_disable(hpriv->target_pwr); + } + + #ifdef CONFIG_PM_SLEEP +@@ -368,10 +418,7 @@ static int ahci_suspend(struct device *dev) + if (pdata && pdata->suspend) + return pdata->suspend(dev); + +- ahci_disable_clks(hpriv); +- +- if (hpriv->target_pwr) +- regulator_disable(hpriv->target_pwr); ++ ahci_platform_disable_resources(hpriv); + + return 0; + } +@@ -383,26 +430,20 @@ static int ahci_resume(struct device *dev) + struct ahci_host_priv *hpriv = host->private_data; + int rc; + +- if (hpriv->target_pwr) { +- rc = regulator_enable(hpriv->target_pwr); +- if (rc) +- return rc; +- } +- +- rc = ahci_enable_clks(dev, hpriv); ++ rc = ahci_platform_enable_resources(hpriv); + if (rc) +- goto disable_regulator; ++ return rc; + + if (pdata && pdata->resume) { + rc = pdata->resume(dev); + if (rc) +- goto disable_unprepare_clk; ++ goto disable_resources; + } + + if (dev->power.power_state.event == PM_EVENT_SUSPEND) { + rc = ahci_reset_controller(host); + if (rc) +- goto disable_unprepare_clk; ++ goto disable_resources; + + ahci_init_controller(host); + } +@@ -411,11 +452,8 @@ static int ahci_resume(struct device *dev) + + return 0; + +-disable_unprepare_clk: +- ahci_disable_clks(hpriv); +-disable_regulator: +- if (hpriv->target_pwr) +- regulator_disable(hpriv->target_pwr); ++disable_resources: ++ ahci_platform_disable_resources(hpriv); + + return rc; + } +diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h +index 769d065..b674b01 100644 +--- a/include/linux/ahci_platform.h ++++ b/include/linux/ahci_platform.h +@@ -33,5 +33,7 @@ struct ahci_platform_data { + + int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); + void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); ++int ahci_platform_enable_resources(struct ahci_host_priv *hpriv); ++void ahci_platform_disable_resources(struct ahci_host_priv *hpriv); + + #endif /* _AHCI_PLATFORM_H */ +-- +2.0.3 + +From 88972833cba7a6dad8e9a1c9b43ab02fc274e4fd Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Mon, 20 Jan 2014 14:58:04 +0100 +Subject: [PATCH] ahci-platform: "Library-ise" ahci_probe functionality + +ahci_probe consists of 3 steps: +1) Get resources (get mmio, clks, regulator) +2) Enable resources, handled by ahci_platform_enable_resouces +3) The more or less standard ahci-host controller init sequence + +This commit refactors step 1 and 3 into separate functions, so the platform +drivers for AHCI implementations which need a specific order in step 2, +and / or need to do some custom register poking at some time, can re-use +ahci-platform.c code without needing to copy and paste it. + +Note that ahci_platform_init_host's prototype takes the 3 non function +members of ahci_platform_data as arguments, the idea is that drivers using +the new exported utility functions will not use ahci_platform_data at all, +and hopefully in the future ahci_platform_data can go away entirely. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + drivers/ata/ahci_platform.c | 195 ++++++++++++++++++++++++++++-------------- + include/linux/ahci_platform.h | 14 +++ + 2 files changed, 144 insertions(+), 65 deletions(-) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 6ebbc17..7f3f2ac 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -201,64 +201,64 @@ void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) + } + EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); + +-static void ahci_put_clks(struct ahci_host_priv *hpriv) ++static void ahci_platform_put_resources(struct device *dev, void *res) + { ++ struct ahci_host_priv *hpriv = res; + int c; + + for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) + clk_put(hpriv->clks[c]); + } + +-static int ahci_probe(struct platform_device *pdev) ++/** ++ * ahci_platform_get_resources - Get platform resources ++ * @pdev: platform device to get resources for ++ * ++ * This function allocates an ahci_host_priv struct, and gets the ++ * following resources, storing a reference to them inside the returned ++ * struct: ++ * ++ * 1) mmio registers (IORESOURCE_MEM 0, mandatory) ++ * 2) regulator for controlling the targets power (optional) ++ * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, ++ * or for non devicetree enabled platforms a single clock ++ * ++ * LOCKING: ++ * None. ++ * ++ * RETURNS: ++ * The allocated ahci_host_priv on success, otherwise an ERR_PTR value ++ */ ++struct ahci_host_priv *ahci_platform_get_resources( ++ struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +- struct ahci_platform_data *pdata = dev_get_platdata(dev); +- const struct platform_device_id *id = platform_get_device_id(pdev); +- struct ata_port_info pi = ahci_port_info[id ? id->driver_data : 0]; +- const struct ata_port_info *ppi[] = { &pi, NULL }; + struct ahci_host_priv *hpriv; +- struct ata_host *host; +- struct resource *mem; + struct clk *clk; +- int irq; +- int n_ports; +- int i; +- int rc; ++ int i, rc = -ENOMEM; + +- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- if (!mem) { +- dev_err(dev, "no mmio space\n"); +- return -EINVAL; +- } ++ if (!devres_open_group(dev, NULL, GFP_KERNEL)) ++ return ERR_PTR(-ENOMEM); + +- irq = platform_get_irq(pdev, 0); +- if (irq <= 0) { +- dev_err(dev, "no irq\n"); +- return -EINVAL; +- } ++ hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv), ++ GFP_KERNEL); ++ if (!hpriv) ++ goto err_out; + +- if (pdata && pdata->ata_port_info) +- pi = *pdata->ata_port_info; ++ devres_add(dev, hpriv); + +- hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); +- if (!hpriv) { +- dev_err(dev, "can't alloc ahci_host_priv\n"); +- return -ENOMEM; +- } +- +- hpriv->flags |= (unsigned long)pi.private_data; +- +- hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem)); ++ hpriv->mmio = devm_ioremap_resource(dev, ++ platform_get_resource(pdev, IORESOURCE_MEM, 0)); + if (!hpriv->mmio) { +- dev_err(dev, "can't map %pR\n", mem); +- return -ENOMEM; ++ dev_err(dev, "no mmio space\n"); ++ goto err_out; + } + + hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); + if (IS_ERR(hpriv->target_pwr)) { + rc = PTR_ERR(hpriv->target_pwr); + if (rc == -EPROBE_DEFER) +- return -EPROBE_DEFER; ++ goto err_out; + hpriv->target_pwr = NULL; + } + +@@ -277,33 +277,62 @@ static int ahci_probe(struct platform_device *pdev) + if (IS_ERR(clk)) { + rc = PTR_ERR(clk); + if (rc == -EPROBE_DEFER) +- goto free_clk; ++ goto err_out; + break; + } + hpriv->clks[i] = clk; + } + +- rc = ahci_platform_enable_resources(hpriv); +- if (rc) +- goto free_clk; ++ devres_remove_group(dev, NULL); ++ return hpriv; + +- /* +- * Some platforms might need to prepare for mmio region access, +- * which could be done in the following init call. So, the mmio +- * region shouldn't be accessed before init (if provided) has +- * returned successfully. +- */ +- if (pdata && pdata->init) { +- rc = pdata->init(dev, hpriv->mmio); +- if (rc) +- goto disable_resources; +- } ++err_out: ++ devres_release_group(dev, NULL); ++ return ERR_PTR(rc); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_get_resources); ++ ++/** ++ * ahci_platform_init_host - Bring up an ahci-platform host ++ * @pdev: platform device pointer for the host ++ * @hpriv: ahci-host private data for the host ++ * @pi_template: template for the ata_port_info to use ++ * @force_port_map: param passed to ahci_save_initial_config ++ * @mask_port_map: param passed to ahci_save_initial_config ++ * ++ * This function does all the usual steps needed to bring up an ++ * ahci-platform host, note any necessary resources (ie clks, phy, etc.) ++ * must be initialized / enabled before calling this. ++ * ++ * LOCKING: ++ * None. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_init_host(struct platform_device *pdev, ++ struct ahci_host_priv *hpriv, ++ const struct ata_port_info *pi_template, ++ unsigned int force_port_map, ++ unsigned int mask_port_map) ++{ ++ struct device *dev = &pdev->dev; ++ struct ata_port_info pi = *pi_template; ++ const struct ata_port_info *ppi[] = { &pi, NULL }; ++ struct ata_host *host; ++ int i, irq, n_ports, rc; + +- ahci_save_initial_config(dev, hpriv, +- pdata ? pdata->force_port_map : 0, +- pdata ? pdata->mask_port_map : 0); ++ irq = platform_get_irq(pdev, 0); ++ if (irq <= 0) { ++ dev_err(dev, "no irq\n"); ++ return -EINVAL; ++ } + + /* prepare host */ ++ hpriv->flags |= (unsigned long)pi.private_data; ++ ++ ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map); ++ + if (hpriv->cap & HOST_CAP_NCQ) + pi.flags |= ATA_FLAG_NCQ; + +@@ -320,10 +349,8 @@ static int ahci_probe(struct platform_device *pdev) + n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); + + host = ata_host_alloc_pinfo(dev, ppi, n_ports); +- if (!host) { +- rc = -ENOMEM; +- goto pdata_exit; +- } ++ if (!host) ++ return -ENOMEM; + + host->private_data = hpriv; + +@@ -338,7 +365,8 @@ static int ahci_probe(struct platform_device *pdev) + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + +- ata_port_desc(ap, "mmio %pR", mem); ++ ata_port_desc(ap, "mmio %pR", ++ platform_get_resource(pdev, IORESOURCE_MEM, 0)); + ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); + + /* set enclosure management message type */ +@@ -352,13 +380,53 @@ static int ahci_probe(struct platform_device *pdev) + + rc = ahci_reset_controller(host); + if (rc) +- goto pdata_exit; ++ return rc; + + ahci_init_controller(host); + ahci_print_info(host, "platform"); + +- rc = ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, +- &ahci_platform_sht); ++ return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, ++ &ahci_platform_sht); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_init_host); ++ ++static int ahci_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct ahci_platform_data *pdata = dev_get_platdata(dev); ++ const struct platform_device_id *id = platform_get_device_id(pdev); ++ const struct ata_port_info *pi_template; ++ struct ahci_host_priv *hpriv; ++ int rc; ++ ++ hpriv = ahci_platform_get_resources(pdev); ++ if (IS_ERR(hpriv)) ++ return PTR_ERR(hpriv); ++ ++ rc = ahci_platform_enable_resources(hpriv); ++ if (rc) ++ return rc; ++ ++ /* ++ * Some platforms might need to prepare for mmio region access, ++ * which could be done in the following init call. So, the mmio ++ * region shouldn't be accessed before init (if provided) has ++ * returned successfully. ++ */ ++ if (pdata && pdata->init) { ++ rc = pdata->init(dev, hpriv->mmio); ++ if (rc) ++ goto disable_resources; ++ } ++ ++ if (pdata && pdata->ata_port_info) ++ pi_template = pdata->ata_port_info; ++ else ++ pi_template = &ahci_port_info[id ? id->driver_data : 0]; ++ ++ rc = ahci_platform_init_host(pdev, hpriv, pi_template, ++ pdata ? pdata->force_port_map : 0, ++ pdata ? pdata->mask_port_map : 0); + if (rc) + goto pdata_exit; + +@@ -368,8 +436,6 @@ static int ahci_probe(struct platform_device *pdev) + pdata->exit(dev); + disable_resources: + ahci_platform_disable_resources(hpriv); +-free_clk: +- ahci_put_clks(hpriv); + return rc; + } + +@@ -383,7 +449,6 @@ static void ahci_host_stop(struct ata_host *host) + pdata->exit(dev); + + ahci_platform_disable_resources(hpriv); +- ahci_put_clks(hpriv); + } + + #ifdef CONFIG_PM_SLEEP +diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h +index b674b01..b80c51c 100644 +--- a/include/linux/ahci_platform.h ++++ b/include/linux/ahci_platform.h +@@ -20,7 +20,14 @@ + struct device; + struct ata_port_info; + struct ahci_host_priv; ++struct platform_device; + ++/* ++ * Note ahci_platform_data is deprecated, it is only kept around for use ++ * by the old da850 and spear13xx ahci code. ++ * New drivers should instead declare their own platform_driver struct, and ++ * use ahci_platform* functions in their own probe, suspend and resume methods. ++ */ + struct ahci_platform_data { + int (*init)(struct device *dev, void __iomem *addr); + void (*exit)(struct device *dev); +@@ -35,5 +42,12 @@ int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); + void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); + int ahci_platform_enable_resources(struct ahci_host_priv *hpriv); + void ahci_platform_disable_resources(struct ahci_host_priv *hpriv); ++struct ahci_host_priv *ahci_platform_get_resources( ++ struct platform_device *pdev); ++int ahci_platform_init_host(struct platform_device *pdev, ++ struct ahci_host_priv *hpriv, ++ const struct ata_port_info *pi_template, ++ unsigned int force_port_map, ++ unsigned int mask_port_map); + + #endif /* _AHCI_PLATFORM_H */ +-- +2.0.3 + +From dffde1e107b4360a4ff6b3aec3693e8b3b4a1bd0 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Mon, 20 Jan 2014 15:52:07 +0100 +Subject: [PATCH] ahci-platform: "Library-ise" suspend / resume functionality + +Split suspend / resume code into host suspend / resume functionality and +resource enable / disabling phases, and export the new suspend_ / resume_host +functions. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + drivers/ata/ahci_platform.c | 109 ++++++++++++++++++++++++++++++++++++------ + include/linux/ahci_platform.h | 5 ++ + 2 files changed, 99 insertions(+), 15 deletions(-) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 7f3f2ac..bdadec1 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -452,14 +452,26 @@ static void ahci_host_stop(struct ata_host *host) + } + + #ifdef CONFIG_PM_SLEEP +-static int ahci_suspend(struct device *dev) ++/** ++ * ahci_platform_suspend_host - Suspend an ahci-platform host ++ * @dev: device pointer for the host ++ * ++ * This function does all the usual steps needed to suspend an ++ * ahci-platform host, note any necessary resources (ie clks, phy, etc.) ++ * must be disabled after calling this. ++ * ++ * LOCKING: ++ * None. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_suspend_host(struct device *dev) + { +- struct ahci_platform_data *pdata = dev_get_platdata(dev); + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + u32 ctl; +- int rc; + + if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { + dev_err(dev, "firmware update required for suspend/resume\n"); +@@ -476,7 +488,64 @@ static int ahci_suspend(struct device *dev) + writel(ctl, mmio + HOST_CTL); + readl(mmio + HOST_CTL); /* flush */ + +- rc = ata_host_suspend(host, PMSG_SUSPEND); ++ return ata_host_suspend(host, PMSG_SUSPEND); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_suspend_host); ++ ++/** ++ * ahci_platform_resume_host - Resume an ahci-platform host ++ * @dev: device pointer for the host ++ * ++ * This function does all the usual steps needed to resume an ++ * ahci-platform host, note any necessary resources (ie clks, phy, etc.) ++ * must be initialized / enabled before calling this. ++ * ++ * LOCKING: ++ * None. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_resume_host(struct device *dev) ++{ ++ struct ata_host *host = dev_get_drvdata(dev); ++ int rc; ++ ++ if (dev->power.power_state.event == PM_EVENT_SUSPEND) { ++ rc = ahci_reset_controller(host); ++ if (rc) ++ return rc; ++ ++ ahci_init_controller(host); ++ } ++ ++ ata_host_resume(host); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_resume_host); ++ ++/** ++ * ahci_platform_suspend - Suspend an ahci-platform device ++ * @dev: the platform device to suspend ++ * ++ * This function suspends the host associated with the device, followed ++ * by disabling all the resources of the device. ++ * ++ * LOCKING: ++ * None. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_suspend(struct device *dev) ++{ ++ struct ahci_platform_data *pdata = dev_get_platdata(dev); ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ int rc; ++ ++ rc = ahci_platform_suspend_host(dev); + if (rc) + return rc; + +@@ -487,8 +556,22 @@ static int ahci_suspend(struct device *dev) + + return 0; + } ++EXPORT_SYMBOL_GPL(ahci_platform_suspend); + +-static int ahci_resume(struct device *dev) ++/** ++ * ahci_platform_resume - Resume an ahci-platform device ++ * @dev: the platform device to resume ++ * ++ * This function enables all the resources of the device followed by ++ * resuming the host associated with the device. ++ * ++ * LOCKING: ++ * None. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_resume(struct device *dev) + { + struct ahci_platform_data *pdata = dev_get_platdata(dev); + struct ata_host *host = dev_get_drvdata(dev); +@@ -505,15 +588,9 @@ static int ahci_resume(struct device *dev) + goto disable_resources; + } + +- if (dev->power.power_state.event == PM_EVENT_SUSPEND) { +- rc = ahci_reset_controller(host); +- if (rc) +- goto disable_resources; +- +- ahci_init_controller(host); +- } +- +- ata_host_resume(host); ++ rc = ahci_platform_resume_host(dev); ++ if (rc) ++ goto disable_resources; + + return 0; + +@@ -522,9 +599,11 @@ static int ahci_resume(struct device *dev) + + return rc; + } ++EXPORT_SYMBOL_GPL(ahci_platform_resume); + #endif + +-static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_suspend, ahci_resume); ++static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend, ++ ahci_platform_resume); + + static const struct of_device_id ahci_of_match[] = { + { .compatible = "snps,spear-ahci", }, +diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h +index b80c51c..542f268 100644 +--- a/include/linux/ahci_platform.h ++++ b/include/linux/ahci_platform.h +@@ -50,4 +50,9 @@ int ahci_platform_init_host(struct platform_device *pdev, + unsigned int force_port_map, + unsigned int mask_port_map); + ++int ahci_platform_suspend_host(struct device *dev); ++int ahci_platform_resume_host(struct device *dev); ++int ahci_platform_suspend(struct device *dev); ++int ahci_platform_resume(struct device *dev); ++ + #endif /* _AHCI_PLATFORM_H */ +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/191-ahci-add-sunxi-driver.patch b/target/linux/sunxi/patches-3.14/191-ahci-add-sunxi-driver.patch new file mode 100644 index 0000000000..51f5ec4686 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/191-ahci-add-sunxi-driver.patch @@ -0,0 +1,352 @@ +From 49270be12ed66b6aff84292f63c16ed77a62e8a6 Mon Sep 17 00:00:00 2001 +From: Olliver Schinagl <oliver@schinagl.nl> +Date: Sat, 18 Jan 2014 15:00:45 +0100 +Subject: [PATCH] ARM: sunxi: Add support for Allwinner SUNXi SoCs sata to + ahci_platform + +This patch adds support for the ahci sata controler found on Allwinner A10 +and A20 SoCs to the ahci_platform driver. + +Orignally written by Olliver Schinagl using the approach of having a platform +device which probe method creates a new child platform device which gets +driven by ahci_platform.c, as done by ahci_imx.c . + +Refactored by Hans de Goede to add most of the non sunxi specific functionality +to ahci_platform.c and use a platform_data pointer from of_device_id for the +sunxi specific bits. + +Signed-off-by: Olliver Schinagl <oliver@schinagl.nl> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + .../devicetree/bindings/ata/ahci-platform.txt | 15 +- + drivers/ata/Kconfig | 9 + + drivers/ata/Makefile | 1 + + drivers/ata/ahci_sunxi.c | 249 +++++++++++++++++++++ + 4 files changed, 271 insertions(+), 3 deletions(-) + create mode 100644 drivers/ata/ahci_sunxi.c + +diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt +index 1ac807f..499bfed 100644 +--- a/Documentation/devicetree/bindings/ata/ahci-platform.txt ++++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt +@@ -4,7 +4,9 @@ SATA nodes are defined to describe on-chip Serial ATA controllers. + Each SATA controller should have its own node. + + Required properties: +-- compatible : compatible list, contains "snps,spear-ahci" ++- compatible : compatible list, one of "snps,spear-ahci", ++ "snps,exynos5440-ahci", "ibm,476gtr-ahci", or ++ "allwinner,sun4i-a10-ahci" + - interrupts : <interrupt mapping for SATA IRQ> + - reg : <registers mapping> + +@@ -13,10 +15,17 @@ Optional properties: + - clocks : a list of phandle + clock specifier pairs + - target-supply : regulator for SATA target power + +-Example: ++Examples: + sata@ffe08000 { + compatible = "snps,spear-ahci"; + reg = <0xffe08000 0x1000>; + interrupts = <115>; +- + }; ++ ++ ahci: sata@01c18000 { ++ compatible = "allwinner,sun4i-a10-ahci"; ++ reg = <0x01c18000 0x1000>; ++ interrupts = <56>; ++ clocks = <&pll6 0>, <&ahb_gates 25>; ++ target-supply = <®_ahci_5v>; ++ }; +diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig +index 868429a4..10a9c25 100644 +--- a/drivers/ata/Kconfig ++++ b/drivers/ata/Kconfig +@@ -106,6 +106,15 @@ config AHCI_IMX + + If unsure, say N. + ++config AHCI_SUNXI ++ tristate "Allwinner sunxi AHCI SATA support" ++ depends on ARCH_SUNXI && SATA_AHCI_PLATFORM ++ help ++ This option enables support for the Allwinner sunxi SoC's ++ onboard AHCI SATA. ++ ++ If unsure, say N. ++ + config SATA_FSL + tristate "Freescale 3.0Gbps SATA support" + depends on FSL_SOC +diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile +index 46518c6..246050b 100644 +--- a/drivers/ata/Makefile ++++ b/drivers/ata/Makefile +@@ -11,6 +11,7 @@ obj-$(CONFIG_SATA_SIL24) += sata_sil24.o + obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o + obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o + obj-$(CONFIG_AHCI_IMX) += ahci_imx.o ++obj-$(CONFIG_AHCI_SUNXI) += ahci_sunxi.o + + # SFF w/ custom DMA + obj-$(CONFIG_PDC_ADMA) += pdc_adma.o +diff --git a/drivers/ata/ahci_sunxi.c b/drivers/ata/ahci_sunxi.c +new file mode 100644 +index 0000000..001f7dfc +--- /dev/null ++++ b/drivers/ata/ahci_sunxi.c +@@ -0,0 +1,249 @@ ++/* ++ * Allwinner sunxi AHCI SATA platform driver ++ * Copyright 2013 Olliver Schinagl <oliver@schinagl.nl> ++ * Copyright 2014 Hans de Goede <hdegoede@redhat.com> ++ * ++ * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov ++ * Based on code from Allwinner Technology Co., Ltd. <www.allwinnertech.com>, ++ * Daniel Wang <danielwang@allwinnertech.com> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope 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/ahci_platform.h> ++#include <linux/clk.h> ++#include <linux/errno.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of_device.h> ++#include <linux/platform_device.h> ++#include <linux/regulator/consumer.h> ++#include "ahci.h" ++ ++#define AHCI_BISTAFR 0x00a0 ++#define AHCI_BISTCR 0x00a4 ++#define AHCI_BISTFCTR 0x00a8 ++#define AHCI_BISTSR 0x00ac ++#define AHCI_BISTDECR 0x00b0 ++#define AHCI_DIAGNR0 0x00b4 ++#define AHCI_DIAGNR1 0x00b8 ++#define AHCI_OOBR 0x00bc ++#define AHCI_PHYCS0R 0x00c0 ++#define AHCI_PHYCS1R 0x00c4 ++#define AHCI_PHYCS2R 0x00c8 ++#define AHCI_TIMER1MS 0x00e0 ++#define AHCI_GPARAM1R 0x00e8 ++#define AHCI_GPARAM2R 0x00ec ++#define AHCI_PPARAMR 0x00f0 ++#define AHCI_TESTR 0x00f4 ++#define AHCI_VERSIONR 0x00f8 ++#define AHCI_IDR 0x00fc ++#define AHCI_RWCR 0x00fc ++#define AHCI_P0DMACR 0x0170 ++#define AHCI_P0PHYCR 0x0178 ++#define AHCI_P0PHYSR 0x017c ++ ++static void sunxi_clrbits(void __iomem *reg, u32 clr_val) ++{ ++ u32 reg_val; ++ ++ reg_val = readl(reg); ++ reg_val &= ~(clr_val); ++ writel(reg_val, reg); ++} ++ ++static void sunxi_setbits(void __iomem *reg, u32 set_val) ++{ ++ u32 reg_val; ++ ++ reg_val = readl(reg); ++ reg_val |= set_val; ++ writel(reg_val, reg); ++} ++ ++static void sunxi_clrsetbits(void __iomem *reg, u32 clr_val, u32 set_val) ++{ ++ u32 reg_val; ++ ++ reg_val = readl(reg); ++ reg_val &= ~(clr_val); ++ reg_val |= set_val; ++ writel(reg_val, reg); ++} ++ ++static u32 sunxi_getbits(void __iomem *reg, u8 mask, u8 shift) ++{ ++ return (readl(reg) >> shift) & mask; ++} ++ ++static int ahci_sunxi_phy_init(struct device *dev, void __iomem *reg_base) ++{ ++ u32 reg_val; ++ int timeout; ++ ++ /* This magic is from the original code */ ++ writel(0, reg_base + AHCI_RWCR); ++ mdelay(5); ++ ++ sunxi_setbits(reg_base + AHCI_PHYCS1R, BIT(19)); ++ sunxi_clrsetbits(reg_base + AHCI_PHYCS0R, ++ (0x7 << 24), ++ (0x5 << 24) | BIT(23) | BIT(18)); ++ sunxi_clrsetbits(reg_base + AHCI_PHYCS1R, ++ (0x3 << 16) | (0x1f << 8) | (0x3 << 6), ++ (0x2 << 16) | (0x6 << 8) | (0x2 << 6)); ++ sunxi_setbits(reg_base + AHCI_PHYCS1R, BIT(28) | BIT(15)); ++ sunxi_clrbits(reg_base + AHCI_PHYCS1R, BIT(19)); ++ sunxi_clrsetbits(reg_base + AHCI_PHYCS0R, ++ (0x7 << 20), (0x3 << 20)); ++ sunxi_clrsetbits(reg_base + AHCI_PHYCS2R, ++ (0x1f << 5), (0x19 << 5)); ++ mdelay(5); ++ ++ sunxi_setbits(reg_base + AHCI_PHYCS0R, (0x1 << 19)); ++ ++ timeout = 250; /* Power up takes aprox 50 us */ ++ do { ++ reg_val = sunxi_getbits(reg_base + AHCI_PHYCS0R, 0x7, 28); ++ if (reg_val == 0x02) ++ break; ++ ++ if (--timeout == 0) { ++ dev_err(dev, "PHY power up failed.\n"); ++ return -EIO; ++ } ++ udelay(1); ++ } while (1); ++ ++ sunxi_setbits(reg_base + AHCI_PHYCS2R, (0x1 << 24)); ++ ++ timeout = 100; /* Calibration takes aprox 10 us */ ++ do { ++ reg_val = sunxi_getbits(reg_base + AHCI_PHYCS2R, 0x1, 24); ++ if (reg_val == 0x00) ++ break; ++ ++ if (--timeout == 0) { ++ dev_err(dev, "PHY calibration failed.\n"); ++ return -EIO; ++ } ++ udelay(1); ++ } while (1); ++ ++ mdelay(15); ++ ++ writel(0x7, reg_base + AHCI_RWCR); ++ ++ return 0; ++} ++ ++static void ahci_sunxi_start_engine(struct ata_port *ap) ++{ ++ void __iomem *port_mmio = ahci_port_base(ap); ++ struct ahci_host_priv *hpriv = ap->host->private_data; ++ ++ /* Setup DMA before DMA start */ ++ sunxi_clrsetbits(hpriv->mmio + AHCI_P0DMACR, 0x0000ff00, 0x00004400); ++ ++ /* Start DMA */ ++ sunxi_setbits(port_mmio + PORT_CMD, PORT_CMD_START); ++} ++ ++static const struct ata_port_info ahci_sunxi_port_info = { ++ AHCI_HFLAGS(AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI | ++ AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ), ++ .flags = AHCI_FLAG_COMMON | ATA_FLAG_NCQ, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_platform_ops, ++}; ++ ++static int ahci_sunxi_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct ahci_host_priv *hpriv; ++ int rc; ++ ++ hpriv = ahci_platform_get_resources(pdev); ++ if (IS_ERR(hpriv)) ++ return PTR_ERR(hpriv); ++ ++ hpriv->start_engine = ahci_sunxi_start_engine; ++ ++ rc = ahci_platform_enable_resources(hpriv); ++ if (rc) ++ return rc; ++ ++ rc = ahci_sunxi_phy_init(dev, hpriv->mmio); ++ if (rc) ++ goto disable_resources; ++ ++ rc = ahci_platform_init_host(pdev, hpriv, &ahci_sunxi_port_info, 0, 0); ++ if (rc) ++ goto disable_resources; ++ ++ return 0; ++ ++disable_resources: ++ ahci_platform_disable_resources(hpriv); ++ return rc; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++int ahci_sunxi_resume(struct device *dev) ++{ ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ int rc; ++ ++ rc = ahci_platform_enable_resources(hpriv); ++ if (rc) ++ return rc; ++ ++ rc = ahci_sunxi_phy_init(dev, hpriv->mmio); ++ if (rc) ++ goto disable_resources; ++ ++ rc = ahci_platform_resume_host(dev); ++ if (rc) ++ goto disable_resources; ++ ++ return 0; ++ ++disable_resources: ++ ahci_platform_disable_resources(hpriv); ++ return rc; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(ahci_sunxi_pm_ops, ahci_platform_suspend, ++ ahci_sunxi_resume); ++ ++static const struct of_device_id ahci_sunxi_of_match[] = { ++ { .compatible = "allwinner,sun4i-a10-ahci", }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, ahci_sunxi_of_match); ++ ++static struct platform_driver ahci_sunxi_driver = { ++ .probe = ahci_sunxi_probe, ++ .remove = ata_platform_remove_one, ++ .driver = { ++ .name = "ahci-sunxi", ++ .owner = THIS_MODULE, ++ .of_match_table = ahci_sunxi_of_match, ++ .pm = &ahci_sunxi_pm_ops, ++ }, ++}; ++module_platform_driver(ahci_sunxi_driver); ++ ++MODULE_DESCRIPTION("Allwinner sunxi AHCI SATA driver"); ++MODULE_AUTHOR("Olliver Schinagl <oliver@schinagl.nl>"); ++MODULE_LICENSE("GPL"); +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/192-ahci-platform-changes.patch b/target/linux/sunxi/patches-3.14/192-ahci-platform-changes.patch new file mode 100644 index 0000000000..3c567f82a4 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/192-ahci-platform-changes.patch @@ -0,0 +1,305 @@ +From a52ae09871d171d6771b4bef2d4c56dd435e740f Mon Sep 17 00:00:00 2001 +From: Roger Quadros <rogerq@ti.com> +Date: Mon, 20 Jan 2014 16:32:33 +0200 +Subject: [PATCH] ata: ahci_platform: Add DT compatible for Synopsis DWC AHCI + controller + +Add compatible string "snps,dwc-ahci", which should be used +for Synopsis Designware SATA cores. e.g. on TI OMAP5 and DRA7 platforms. + +Signed-off-by: Roger Quadros <rogerq@ti.com> +Reviewed-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + drivers/ata/ahci_platform.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index bdadec1..d7e55ba 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -609,6 +609,7 @@ static const struct of_device_id ahci_of_match[] = { + { .compatible = "snps,spear-ahci", }, + { .compatible = "snps,exynos5440-ahci", }, + { .compatible = "ibm,476gtr-ahci", }, ++ { .compatible = "snps,dwc-ahci", }, + {}, + }; + MODULE_DEVICE_TABLE(of, ahci_of_match); +-- +2.0.3 + +From 2be39fa6b6531bd083c766999345adc348b14c77 Mon Sep 17 00:00:00 2001 +From: Roger Quadros <rogerq@ti.com> +Date: Mon, 27 Jan 2014 16:41:18 +0200 +Subject: [PATCH] ata: ahci_platform: Manage SATA PHY + +Some platforms have a PHY hooked up to the +SATA controller. The PHY needs to be initialized +and powered up for SATA to work. We do that +using the PHY framework. + +CC: Balaji T K <balajitk@ti.com> +Signed-off-by: Roger Quadros <rogerq@ti.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + drivers/ata/ahci.h | 2 ++ + drivers/ata/ahci_platform.c | 47 +++++++++++++++++++++++++++++++++++++++++++-- + 2 files changed, 47 insertions(+), 2 deletions(-) + +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index bf8100c..3ab7ac9 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -37,6 +37,7 @@ + + #include <linux/clk.h> + #include <linux/libata.h> ++#include <linux/phy/phy.h> + #include <linux/regulator/consumer.h> + + /* Enclosure Management Control */ +@@ -325,6 +326,7 @@ struct ahci_host_priv { + u32 em_msg_type; /* EM message type */ + struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ + struct regulator *target_pwr; /* Optional */ ++ struct phy *phy; /* If platform uses phy */ + void *plat_data; /* Other platform data */ + /* + * Optional ahci_start_engine override, if not set this gets set to the +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index d7e55ba..99d38c1 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -23,6 +23,7 @@ + #include <linux/platform_device.h> + #include <linux/libata.h> + #include <linux/ahci_platform.h> ++#include <linux/phy/phy.h> + #include "ahci.h" + + static void ahci_host_stop(struct ata_host *host); +@@ -147,6 +148,7 @@ EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); + * the following order: + * 1) Regulator + * 2) Clocks (through ahci_platform_enable_clks) ++ * 3) Phy + * + * If resource enabling fails at any point the previous enabled + * resources are disabled in reverse order. +@@ -171,8 +173,23 @@ int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + if (rc) + goto disable_regulator; + ++ if (hpriv->phy) { ++ rc = phy_init(hpriv->phy); ++ if (rc) ++ goto disable_clks; ++ ++ rc = phy_power_on(hpriv->phy); ++ if (rc) { ++ phy_exit(hpriv->phy); ++ goto disable_clks; ++ } ++ } ++ + return 0; + ++disable_clks: ++ ahci_platform_disable_clks(hpriv); ++ + disable_regulator: + if (hpriv->target_pwr) + regulator_disable(hpriv->target_pwr); +@@ -186,14 +203,20 @@ EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); + * + * This function disables all ahci_platform managed resources in + * the following order: +- * 1) Clocks (through ahci_platform_disable_clks) +- * 2) Regulator ++ * 1) Phy ++ * 2) Clocks (through ahci_platform_disable_clks) ++ * 3) Regulator + * + * LOCKING: + * None. + */ + void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) + { ++ if (hpriv->phy) { ++ phy_power_off(hpriv->phy); ++ phy_exit(hpriv->phy); ++ } ++ + ahci_platform_disable_clks(hpriv); + + if (hpriv->target_pwr) +@@ -222,6 +245,7 @@ static void ahci_platform_put_resources(struct device *dev, void *res) + * 2) regulator for controlling the targets power (optional) + * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, + * or for non devicetree enabled platforms a single clock ++ * 4) phy (optional) + * + * LOCKING: + * None. +@@ -283,6 +307,25 @@ struct ahci_host_priv *ahci_platform_get_resources( + hpriv->clks[i] = clk; + } + ++ hpriv->phy = devm_phy_get(dev, "sata-phy"); ++ if (IS_ERR(hpriv->phy)) { ++ rc = PTR_ERR(hpriv->phy); ++ switch (rc) { ++ case -ENODEV: ++ case -ENOSYS: ++ /* continue normally */ ++ hpriv->phy = NULL; ++ break; ++ ++ case -EPROBE_DEFER: ++ goto err_out; ++ ++ default: ++ dev_err(dev, "couldn't get sata-phy\n"); ++ goto err_out; ++ } ++ } ++ + devres_remove_group(dev, NULL); + return hpriv; + +-- +2.0.3 + +From 22d93de35a39507bcdf0c62c5e1fab0625f7c6f6 Mon Sep 17 00:00:00 2001 +From: Roger Quadros <rogerq@ti.com> +Date: Wed, 9 Oct 2013 15:08:59 +0300 +Subject: [PATCH] ata: ahci_platform: runtime resume the device before use + +On OMAP platforms the device needs to be runtime resumed before +it can be accessed. The OMAP HWMOD framework takes care of +enabling the module and its resources based on the +device's runtime PM state. + +In this patch we runtime resume during .probe() and runtime suspend +after .remove(). + +We also update the runtime PM state during .resume(). + +CC: Balaji T K <balajitk@ti.com> +Signed-off-by: Roger Quadros <rogerq@ti.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + drivers/ata/ahci.h | 1 + + drivers/ata/ahci_platform.c | 15 +++++++++++++++ + 2 files changed, 16 insertions(+) + +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index 3ab7ac9..51af275b 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -324,6 +324,7 @@ struct ahci_host_priv { + u32 em_loc; /* enclosure management location */ + u32 em_buf_sz; /* EM buffer size in byte */ + u32 em_msg_type; /* EM message type */ ++ bool got_runtime_pm; /* Did we do pm_runtime_get? */ + struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ + struct regulator *target_pwr; /* Optional */ + struct phy *phy; /* If platform uses phy */ +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 99d38c1..75698a4 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -24,6 +24,7 @@ + #include <linux/libata.h> + #include <linux/ahci_platform.h> + #include <linux/phy/phy.h> ++#include <linux/pm_runtime.h> + #include "ahci.h" + + static void ahci_host_stop(struct ata_host *host); +@@ -229,6 +230,11 @@ static void ahci_platform_put_resources(struct device *dev, void *res) + struct ahci_host_priv *hpriv = res; + int c; + ++ if (hpriv->got_runtime_pm) { ++ pm_runtime_put_sync(dev); ++ pm_runtime_disable(dev); ++ } ++ + for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) + clk_put(hpriv->clks[c]); + } +@@ -326,6 +332,10 @@ struct ahci_host_priv *ahci_platform_get_resources( + } + } + ++ pm_runtime_enable(dev); ++ pm_runtime_get_sync(dev); ++ hpriv->got_runtime_pm = true; ++ + devres_remove_group(dev, NULL); + return hpriv; + +@@ -635,6 +645,11 @@ int ahci_platform_resume(struct device *dev) + if (rc) + goto disable_resources; + ++ /* We resumed so update PM runtime state */ ++ pm_runtime_disable(dev); ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ + return 0; + + disable_resources: +-- +2.0.3 + +From 22cccaf7a29b2a9d8a1440d089f399a6d2432b81 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sun, 23 Feb 2014 11:37:17 +0100 +Subject: [PATCH] ahci_sunxi: Use msleep instead of mdelay + +ahci_sunxi_phy_init is called from the probe and resume code paths, and +sleeping is safe in both, so use msleep instead of mdelay. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + drivers/ata/ahci_sunxi.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/ata/ahci_sunxi.c b/drivers/ata/ahci_sunxi.c +index 001f7dfc..d1bf3f7 100644 +--- a/drivers/ata/ahci_sunxi.c ++++ b/drivers/ata/ahci_sunxi.c +@@ -90,7 +90,7 @@ static int ahci_sunxi_phy_init(struct device *dev, void __iomem *reg_base) + + /* This magic is from the original code */ + writel(0, reg_base + AHCI_RWCR); +- mdelay(5); ++ msleep(5); + + sunxi_setbits(reg_base + AHCI_PHYCS1R, BIT(19)); + sunxi_clrsetbits(reg_base + AHCI_PHYCS0R, +@@ -105,7 +105,7 @@ static int ahci_sunxi_phy_init(struct device *dev, void __iomem *reg_base) + (0x7 << 20), (0x3 << 20)); + sunxi_clrsetbits(reg_base + AHCI_PHYCS2R, + (0x1f << 5), (0x19 << 5)); +- mdelay(5); ++ msleep(5); + + sunxi_setbits(reg_base + AHCI_PHYCS0R, (0x1 << 19)); + +@@ -137,7 +137,7 @@ static int ahci_sunxi_phy_init(struct device *dev, void __iomem *reg_base) + udelay(1); + } while (1); + +- mdelay(15); ++ msleep(15); + + writel(0x7, reg_base + AHCI_RWCR); + +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/195-1-ohci-plat-changes.patch b/target/linux/sunxi/patches-3.14/195-1-ohci-plat-changes.patch new file mode 100644 index 0000000000..2fb1928888 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/195-1-ohci-plat-changes.patch @@ -0,0 +1,471 @@ +From 49ff47db168655cac5213cf5dd1844b08fb9823c Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sun, 5 Jan 2014 14:42:39 +0100 +Subject: [PATCH] ohci-platform: Add support for devicetree instantiation + +Add support for ohci-platform instantiation from devicetree, including +optionally getting clks and a phy from devicetree, and enabling / disabling +those on power_on / off. + +This should allow using ohci-platform from devicetree in various cases. +Specifically after this commit it can be used for the ohci controller found +on Allwinner sunxi SoCs. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Acked-by: Alan Stern <stern@rowland.harvard.edu> +--- + Documentation/devicetree/bindings/usb/usb-ohci.txt | 22 +++ + drivers/usb/host/ohci-platform.c | 162 ++++++++++++++++++--- + 2 files changed, 162 insertions(+), 22 deletions(-) + create mode 100644 Documentation/devicetree/bindings/usb/usb-ohci.txt + +diff --git a/Documentation/devicetree/bindings/usb/usb-ohci.txt b/Documentation/devicetree/bindings/usb/usb-ohci.txt +new file mode 100644 +index 0000000..6ba38d9 +--- /dev/null ++++ b/Documentation/devicetree/bindings/usb/usb-ohci.txt +@@ -0,0 +1,22 @@ ++USB OHCI controllers ++ ++Required properties: ++- compatible : "usb-ohci" ++- reg : ohci controller register range (address and length) ++- interrupts : ohci controller interrupt ++ ++Optional properties: ++- clocks : a list of phandle + clock specifier pairs ++- phys : phandle + phy specifier pair ++- phy-names : "usb" ++ ++Example: ++ ++ ohci0: usb@01c14400 { ++ compatible = "allwinner,sun4i-a10-ohci", "usb-ohci"; ++ reg = <0x01c14400 0x100>; ++ interrupts = <64>; ++ clocks = <&usb_clk 6>, <&ahb_gates 2>; ++ phys = <&usbphy 1>; ++ phy-names = "usb"; ++ }; +diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c +index 68f674c..49304dd 100644 +--- a/drivers/usb/host/ohci-platform.c ++++ b/drivers/usb/host/ohci-platform.c +@@ -3,6 +3,7 @@ + * + * Copyright 2007 Michael Buesch <m@bues.ch> + * Copyright 2011-2012 Hauke Mehrtens <hauke@hauke-m.de> ++ * Copyright 2014 Hans de Goede <hdegoede@redhat.com> + * + * Derived from the OCHI-SSB driver + * Derived from the OHCI-PCI driver +@@ -14,11 +15,14 @@ + * Licensed under the GNU/GPL. See COPYING for details. + */ + ++#include <linux/clk.h> ++#include <linux/dma-mapping.h> + #include <linux/hrtimer.h> + #include <linux/io.h> + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/err.h> ++#include <linux/phy/phy.h> + #include <linux/platform_device.h> + #include <linux/usb/ohci_pdriver.h> + #include <linux/usb.h> +@@ -27,6 +31,13 @@ + #include "ohci.h" + + #define DRIVER_DESC "OHCI generic platform driver" ++#define OHCI_MAX_CLKS 3 ++#define hcd_to_ohci_priv(h) ((struct ohci_platform_priv *)hcd_to_ohci(h)->priv) ++ ++struct ohci_platform_priv { ++ struct clk *clks[OHCI_MAX_CLKS]; ++ struct phy *phy; ++}; + + static const char hcd_name[] = "ohci-platform"; + +@@ -48,11 +59,67 @@ static int ohci_platform_reset(struct usb_hcd *hcd) + return ohci_setup(hcd); + } + ++static int ohci_platform_power_on(struct platform_device *dev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(dev); ++ struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); ++ int clk, ret; ++ ++ for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) { ++ ret = clk_prepare_enable(priv->clks[clk]); ++ if (ret) ++ goto err_disable_clks; ++ } ++ ++ if (priv->phy) { ++ ret = phy_init(priv->phy); ++ if (ret) ++ goto err_disable_clks; ++ ++ ret = phy_power_on(priv->phy); ++ if (ret) ++ goto err_exit_phy; ++ } ++ ++ return 0; ++ ++err_exit_phy: ++ phy_exit(priv->phy); ++err_disable_clks: ++ while (--clk >= 0) ++ clk_disable_unprepare(priv->clks[clk]); ++ ++ return ret; ++} ++ ++static void ohci_platform_power_off(struct platform_device *dev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(dev); ++ struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); ++ int clk; ++ ++ if (priv->phy) { ++ phy_power_off(priv->phy); ++ phy_exit(priv->phy); ++ } ++ ++ for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--) ++ if (priv->clks[clk]) ++ clk_disable_unprepare(priv->clks[clk]); ++} ++ + static struct hc_driver __read_mostly ohci_platform_hc_driver; + + static const struct ohci_driver_overrides platform_overrides __initconst = { +- .product_desc = "Generic Platform OHCI controller", +- .reset = ohci_platform_reset, ++ .product_desc = "Generic Platform OHCI controller", ++ .reset = ohci_platform_reset, ++ .extra_priv_size = sizeof(struct ohci_platform_priv), ++}; ++ ++static struct usb_ohci_pdata ohci_platform_defaults = { ++ .power_on = ohci_platform_power_on, ++ .power_suspend = ohci_platform_power_off, ++ .power_off = ohci_platform_power_off, + }; + + static int ohci_platform_probe(struct platform_device *dev) +@@ -60,17 +127,23 @@ static int ohci_platform_probe(struct platform_device *dev) + struct usb_hcd *hcd; + struct resource *res_mem; + struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); +- int irq; +- int err = -ENOMEM; +- +- if (!pdata) { +- WARN_ON(1); +- return -ENODEV; +- } ++ struct ohci_platform_priv *priv; ++ int err, irq, clk = 0; + + if (usb_disabled()) + return -ENODEV; + ++ /* ++ * Use reasonable defaults so platforms don't have to provide these ++ * with DT probing on ARM. ++ */ ++ if (!pdata) ++ pdata = &ohci_platform_defaults; ++ ++ err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)); ++ if (err) ++ return err; ++ + irq = platform_get_irq(dev, 0); + if (irq < 0) { + dev_err(&dev->dev, "no irq provided"); +@@ -83,17 +156,40 @@ static int ohci_platform_probe(struct platform_device *dev) + return -ENXIO; + } + ++ hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev, ++ dev_name(&dev->dev)); ++ if (!hcd) ++ return -ENOMEM; ++ ++ platform_set_drvdata(dev, hcd); ++ dev->dev.platform_data = pdata; ++ priv = hcd_to_ohci_priv(hcd); ++ ++ if (pdata == &ohci_platform_defaults && dev->dev.of_node) { ++ priv->phy = devm_phy_get(&dev->dev, "usb"); ++ if (IS_ERR(priv->phy)) { ++ err = PTR_ERR(priv->phy); ++ if (err == -EPROBE_DEFER) ++ goto err_put_hcd; ++ priv->phy = NULL; ++ } ++ ++ for (clk = 0; clk < OHCI_MAX_CLKS; clk++) { ++ priv->clks[clk] = of_clk_get(dev->dev.of_node, clk); ++ if (IS_ERR(priv->clks[clk])) { ++ err = PTR_ERR(priv->clks[clk]); ++ if (err == -EPROBE_DEFER) ++ goto err_put_clks; ++ priv->clks[clk] = NULL; ++ break; ++ } ++ } ++ } ++ + if (pdata->power_on) { + err = pdata->power_on(dev); + if (err < 0) +- return err; +- } +- +- hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev, +- dev_name(&dev->dev)); +- if (!hcd) { +- err = -ENOMEM; +- goto err_power; ++ goto err_put_clks; + } + + hcd->rsrc_start = res_mem->start; +@@ -102,11 +198,11 @@ static int ohci_platform_probe(struct platform_device *dev) + hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); + if (IS_ERR(hcd->regs)) { + err = PTR_ERR(hcd->regs); +- goto err_put_hcd; ++ goto err_power; + } + err = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (err) +- goto err_put_hcd; ++ goto err_power; + + device_wakeup_enable(hcd->self.controller); + +@@ -114,11 +210,17 @@ static int ohci_platform_probe(struct platform_device *dev) + + return err; + +-err_put_hcd: +- usb_put_hcd(hcd); + err_power: + if (pdata->power_off) + pdata->power_off(dev); ++err_put_clks: ++ while (--clk >= 0) ++ clk_put(priv->clks[clk]); ++err_put_hcd: ++ if (pdata == &ohci_platform_defaults) ++ dev->dev.platform_data = NULL; ++ ++ usb_put_hcd(hcd); + + return err; + } +@@ -127,13 +229,22 @@ static int ohci_platform_remove(struct platform_device *dev) + { + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); ++ struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); ++ int clk; + + usb_remove_hcd(hcd); +- usb_put_hcd(hcd); + + if (pdata->power_off) + pdata->power_off(dev); + ++ for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) ++ clk_put(priv->clks[clk]); ++ ++ usb_put_hcd(hcd); ++ ++ if (pdata == &ohci_platform_defaults) ++ dev->dev.platform_data = NULL; ++ + return 0; + } + +@@ -180,6 +291,12 @@ static int ohci_platform_resume(struct device *dev) + #define ohci_platform_resume NULL + #endif /* CONFIG_PM */ + ++static const struct of_device_id ohci_platform_ids[] = { ++ { .compatible = "usb-ohci", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, ohci_platform_ids); ++ + static const struct platform_device_id ohci_platform_table[] = { + { "ohci-platform", 0 }, + { } +@@ -200,6 +317,7 @@ static struct platform_driver ohci_platform_driver = { + .owner = THIS_MODULE, + .name = "ohci-platform", + .pm = &ohci_platform_pm_ops, ++ .of_match_table = ohci_platform_ids, + } + }; + +-- +2.0.3 + +From 8c4b97ea8b66e0fc8bdf648675e6889ce48c5ea7 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Tue, 21 Jan 2014 16:05:47 +0100 +Subject: [PATCH] ohci-platform: Add support for controllers with big-endian + regs / descriptors + +Note this commit uses the same devicetree booleans for this as the ones +already existing in the usb-ehci bindings, see: +Documentation/devicetree/bindings/usb/usb-ehci.txt + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + Documentation/devicetree/bindings/usb/usb-ohci.txt | 3 +++ + drivers/usb/host/ohci-platform.c | 27 ++++++++++++++++++++++ + 2 files changed, 30 insertions(+) + +diff --git a/Documentation/devicetree/bindings/usb/usb-ohci.txt b/Documentation/devicetree/bindings/usb/usb-ohci.txt +index 6ba38d9..6933b0c 100644 +--- a/Documentation/devicetree/bindings/usb/usb-ohci.txt ++++ b/Documentation/devicetree/bindings/usb/usb-ohci.txt +@@ -6,6 +6,9 @@ Required properties: + - interrupts : ohci controller interrupt + + Optional properties: ++- big-endian-regs : boolean, set this for hcds with big-endian registers ++- big-endian-desc : boolean, set this for hcds with big-endian descriptors ++- big-endian : boolean, for hcds with big-endian-regs + big-endian-desc + - clocks : a list of phandle + clock specifier pairs + - phys : phandle + phy specifier pair + - phy-names : "usb" +diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c +index 49304dd..e2c28fd 100644 +--- a/drivers/usb/host/ohci-platform.c ++++ b/drivers/usb/host/ohci-platform.c +@@ -128,6 +128,7 @@ static int ohci_platform_probe(struct platform_device *dev) + struct resource *res_mem; + struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); + struct ohci_platform_priv *priv; ++ struct ohci_hcd *ohci; + int err, irq, clk = 0; + + if (usb_disabled()) +@@ -164,8 +165,34 @@ static int ohci_platform_probe(struct platform_device *dev) + platform_set_drvdata(dev, hcd); + dev->dev.platform_data = pdata; + priv = hcd_to_ohci_priv(hcd); ++ ohci = hcd_to_ohci(hcd); + + if (pdata == &ohci_platform_defaults && dev->dev.of_node) { ++ if (of_property_read_bool(dev->dev.of_node, "big-endian-regs")) ++ ohci->flags |= OHCI_QUIRK_BE_MMIO; ++ ++ if (of_property_read_bool(dev->dev.of_node, "big-endian-desc")) ++ ohci->flags |= OHCI_QUIRK_BE_DESC; ++ ++ if (of_property_read_bool(dev->dev.of_node, "big-endian")) ++ ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC; ++ ++#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO ++ if (ohci->flags & OHCI_QUIRK_BE_MMIO) { ++ dev_err(&dev->dev, ++ "Error big-endian-regs not compiled in\n"); ++ err = -EINVAL; ++ goto err_put_hcd; ++ } ++#endif ++#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_DESC ++ if (ohci->flags & OHCI_QUIRK_BE_DESC) { ++ dev_err(&dev->dev, ++ "Error big-endian-desc not compiled in\n"); ++ err = -EINVAL; ++ goto err_put_hcd; ++ } ++#endif + priv->phy = devm_phy_get(&dev->dev, "usb"); + if (IS_ERR(priv->phy)) { + err = PTR_ERR(priv->phy); +-- +2.0.3 + +From ddf77eb2ec72a3676dabe17baf6e3b32ce0542e5 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Tue, 11 Feb 2014 11:27:29 +0100 +Subject: [PATCH] ohci-platform: Change compatible string from usb-ohci to + generic-ohci + +The initial versions of the devicetree enablement patches for ohci-platform +used "ohci-platform" as compatible string. However this was disliked by various +reviewers because the platform bus is a Linux invention and devicetree is +supposed to be OS agnostic. After much discussion I gave up and went with +the generic usb-ohci as requested. + +In retro-spect I should have chosen something different, the dts files for many +existing boards already claim to be compatible with "usb-ohci", ie they have: + + compatible = "ti,ohci-omap3", "usb-ohci"; + +In theory this should not be a problem since the "ti,ohci-omap3" entry takes +presedence, but in practice using a conflicting compatible string is an issue, +because it makes which driver gets used depend on driver registration order. + +This patch changes the compatible string claimed by ohci-platform to +"generic-ohci", avoiding the driver registration / module loading ordering +problems. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + Documentation/devicetree/bindings/usb/usb-ohci.txt | 4 ++-- + drivers/usb/host/ohci-platform.c | 2 +- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/Documentation/devicetree/bindings/usb/usb-ohci.txt b/Documentation/devicetree/bindings/usb/usb-ohci.txt +index 6933b0c..45f67d9 100644 +--- a/Documentation/devicetree/bindings/usb/usb-ohci.txt ++++ b/Documentation/devicetree/bindings/usb/usb-ohci.txt +@@ -1,7 +1,7 @@ + USB OHCI controllers + + Required properties: +-- compatible : "usb-ohci" ++- compatible : "generic-ohci" + - reg : ohci controller register range (address and length) + - interrupts : ohci controller interrupt + +@@ -16,7 +16,7 @@ Optional properties: + Example: + + ohci0: usb@01c14400 { +- compatible = "allwinner,sun4i-a10-ohci", "usb-ohci"; ++ compatible = "allwinner,sun4i-a10-ohci", "generic-ohci"; + reg = <0x01c14400 0x100>; + interrupts = <64>; + clocks = <&usb_clk 6>, <&ahb_gates 2>; +diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c +index e2c28fd..b6ca0b2 100644 +--- a/drivers/usb/host/ohci-platform.c ++++ b/drivers/usb/host/ohci-platform.c +@@ -319,7 +319,7 @@ static int ohci_platform_resume(struct device *dev) + #endif /* CONFIG_PM */ + + static const struct of_device_id ohci_platform_ids[] = { +- { .compatible = "usb-ohci", }, ++ { .compatible = "generic-ohci", }, + { } + }; + MODULE_DEVICE_TABLE(of, ohci_platform_ids); +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/195-2-ehci-plat-changes.patch b/target/linux/sunxi/patches-3.14/195-2-ehci-plat-changes.patch new file mode 100644 index 0000000000..b8ee130c4a --- /dev/null +++ b/target/linux/sunxi/patches-3.14/195-2-ehci-plat-changes.patch @@ -0,0 +1,552 @@ +From 738b350437abfca820dae226549ecf4fb100fa30 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sun, 5 Jan 2014 00:04:02 +0100 +Subject: [PATCH] ehci-platform: Add support for clks and phy passed through + devicetree + +Currently ehci-platform is only used in combination with devicetree when used +with some Via socs. By extending it to (optionally) get clks and a phy from +devicetree, and enabling / disabling those on power_on / off, it can be used +more generically. Specifically after this commit it can be used for the +ehci controller on Allwinner sunxi SoCs. + +Since ehci-platform is intended to handle any generic enough non pci ehci +device, add a "usb-ehci" compatibility string. + +There already is a usb-ehci device-tree bindings document, update this +with clks and phy bindings info. + +Although actually quite generic so far the via,vt8500 compatibilty string +had its own bindings document. Somehow we even ended up with 2 of them. Since +these provide no extra information over the generic usb-ehci documentation, +this patch removes them. + +The ehci-ppc-of.c driver also claims the usb-ehci compatibility string, +even though it mostly is ibm,usb-ehci-440epx specific. ehci-platform.c is +not needed on ppc platforms, so add a !PPC_OF dependency to it to avoid +2 drivers claiming the same compatibility string getting build on ppc. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Acked-by: Alan Stern <stern@rowland.harvard.edu> +--- + Documentation/devicetree/bindings/usb/usb-ehci.txt | 25 +++- + .../devicetree/bindings/usb/via,vt8500-ehci.txt | 15 --- + .../devicetree/bindings/usb/vt8500-ehci.txt | 12 -- + drivers/usb/host/Kconfig | 1 + + drivers/usb/host/ehci-platform.c | 147 +++++++++++++++++---- + 5 files changed, 142 insertions(+), 58 deletions(-) + delete mode 100644 Documentation/devicetree/bindings/usb/via,vt8500-ehci.txt + delete mode 100644 Documentation/devicetree/bindings/usb/vt8500-ehci.txt + +diff --git a/Documentation/devicetree/bindings/usb/usb-ehci.txt b/Documentation/devicetree/bindings/usb/usb-ehci.txt +index fa18612..2c1aeeb 100644 +--- a/Documentation/devicetree/bindings/usb/usb-ehci.txt ++++ b/Documentation/devicetree/bindings/usb/usb-ehci.txt +@@ -7,13 +7,14 @@ Required properties: + (debug-port or other) can be also specified here, but only after + definition of standard EHCI registers. + - interrupts : one EHCI interrupt should be described here. +-If device registers are implemented in big endian mode, the device +-node should have "big-endian-regs" property. +-If controller implementation operates with big endian descriptors, +-"big-endian-desc" property should be specified. +-If both big endian registers and descriptors are used by the controller +-implementation, "big-endian" property can be specified instead of having +-both "big-endian-regs" and "big-endian-desc". ++ ++Optional properties: ++ - big-endian-regs : boolean, set this for hcds with big-endian registers ++ - big-endian-desc : boolean, set this for hcds with big-endian descriptors ++ - big-endian : boolean, for hcds with big-endian-regs + big-endian-desc ++ - clocks : a list of phandle + clock specifier pairs ++ - phys : phandle + phy specifier pair ++ - phy-names : "usb" + + Example (Sequoia 440EPx): + ehci@e0000300 { +@@ -23,3 +24,13 @@ Example (Sequoia 440EPx): + reg = <0 e0000300 90 0 e0000390 70>; + big-endian; + }; ++ ++Example (Allwinner sun4i A10 SoC): ++ ehci0: usb@01c14000 { ++ compatible = "allwinner,sun4i-a10-ehci", "usb-ehci"; ++ reg = <0x01c14000 0x100>; ++ interrupts = <39>; ++ clocks = <&ahb_gates 1>; ++ phys = <&usbphy 1>; ++ phy-names = "usb"; ++ }; +diff --git a/Documentation/devicetree/bindings/usb/via,vt8500-ehci.txt b/Documentation/devicetree/bindings/usb/via,vt8500-ehci.txt +deleted file mode 100644 +index 17b3ad1..0000000 +--- a/Documentation/devicetree/bindings/usb/via,vt8500-ehci.txt ++++ /dev/null +@@ -1,15 +0,0 @@ +-VIA/Wondermedia VT8500 EHCI Controller +------------------------------------------------------ +- +-Required properties: +-- compatible : "via,vt8500-ehci" +-- reg : Should contain 1 register ranges(address and length) +-- interrupts : ehci controller interrupt +- +-Example: +- +- ehci@d8007900 { +- compatible = "via,vt8500-ehci"; +- reg = <0xd8007900 0x200>; +- interrupts = <43>; +- }; +diff --git a/Documentation/devicetree/bindings/usb/vt8500-ehci.txt b/Documentation/devicetree/bindings/usb/vt8500-ehci.txt +deleted file mode 100644 +index 5fb8fd6..0000000 +--- a/Documentation/devicetree/bindings/usb/vt8500-ehci.txt ++++ /dev/null +@@ -1,12 +0,0 @@ +-VIA VT8500 and Wondermedia WM8xxx SoC USB controllers. +- +-Required properties: +- - compatible: Should be "via,vt8500-ehci" or "wm,prizm-ehci". +- - reg: Address range of the ehci registers. size should be 0x200 +- - interrupts: Should contain the ehci interrupt. +- +-usb: ehci@D8007100 { +- compatible = "wm,prizm-ehci", "usb-ehci"; +- reg = <0xD8007100 0x200>; +- interrupts = <1>; +-}; +diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig +index a9707da..e28cbe0 100644 +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -255,6 +255,7 @@ config USB_EHCI_ATH79 + + config USB_EHCI_HCD_PLATFORM + tristate "Generic EHCI driver for a platform device" ++ depends on !PPC_OF + default n + ---help--- + Adds an EHCI host driver for a generic platform device, which +diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c +index 01536cf..5ebd0b7 100644 +--- a/drivers/usb/host/ehci-platform.c ++++ b/drivers/usb/host/ehci-platform.c +@@ -3,6 +3,7 @@ + * + * Copyright 2007 Steven Brown <sbrown@cortland.com> + * Copyright 2010-2012 Hauke Mehrtens <hauke@hauke-m.de> ++ * Copyright 2014 Hans de Goede <hdegoede@redhat.com> + * + * Derived from the ohci-ssb driver + * Copyright 2007 Michael Buesch <m@bues.ch> +@@ -18,6 +19,7 @@ + * + * Licensed under the GNU/GPL. See COPYING for details. + */ ++#include <linux/clk.h> + #include <linux/dma-mapping.h> + #include <linux/err.h> + #include <linux/kernel.h> +@@ -25,6 +27,7 @@ + #include <linux/io.h> + #include <linux/module.h> + #include <linux/of.h> ++#include <linux/phy/phy.h> + #include <linux/platform_device.h> + #include <linux/usb.h> + #include <linux/usb/hcd.h> +@@ -33,6 +36,13 @@ + #include "ehci.h" + + #define DRIVER_DESC "EHCI generic platform driver" ++#define EHCI_MAX_CLKS 3 ++#define hcd_to_ehci_priv(h) ((struct ehci_platform_priv *)hcd_to_ehci(h)->priv) ++ ++struct ehci_platform_priv { ++ struct clk *clks[EHCI_MAX_CLKS]; ++ struct phy *phy; ++}; + + static const char hcd_name[] = "ehci-platform"; + +@@ -64,38 +74,90 @@ static int ehci_platform_reset(struct usb_hcd *hcd) + return 0; + } + ++static int ehci_platform_power_on(struct platform_device *dev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(dev); ++ struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); ++ int clk, ret; ++ ++ for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) { ++ ret = clk_prepare_enable(priv->clks[clk]); ++ if (ret) ++ goto err_disable_clks; ++ } ++ ++ if (priv->phy) { ++ ret = phy_init(priv->phy); ++ if (ret) ++ goto err_disable_clks; ++ ++ ret = phy_power_on(priv->phy); ++ if (ret) ++ goto err_exit_phy; ++ } ++ ++ return 0; ++ ++err_exit_phy: ++ phy_exit(priv->phy); ++err_disable_clks: ++ while (--clk >= 0) ++ clk_disable_unprepare(priv->clks[clk]); ++ ++ return ret; ++} ++ ++static void ehci_platform_power_off(struct platform_device *dev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(dev); ++ struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); ++ int clk; ++ ++ if (priv->phy) { ++ phy_power_off(priv->phy); ++ phy_exit(priv->phy); ++ } ++ ++ for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--) ++ if (priv->clks[clk]) ++ clk_disable_unprepare(priv->clks[clk]); ++} ++ + static struct hc_driver __read_mostly ehci_platform_hc_driver; + + static const struct ehci_driver_overrides platform_overrides __initconst = { +- .reset = ehci_platform_reset, ++ .reset = ehci_platform_reset, ++ .extra_priv_size = sizeof(struct ehci_platform_priv), + }; + +-static struct usb_ehci_pdata ehci_platform_defaults; ++static struct usb_ehci_pdata ehci_platform_defaults = { ++ .power_on = ehci_platform_power_on, ++ .power_suspend = ehci_platform_power_off, ++ .power_off = ehci_platform_power_off, ++}; + + static int ehci_platform_probe(struct platform_device *dev) + { + struct usb_hcd *hcd; + struct resource *res_mem; +- struct usb_ehci_pdata *pdata; +- int irq; +- int err; ++ struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); ++ struct ehci_platform_priv *priv; ++ int err, irq, clk = 0; + + if (usb_disabled()) + return -ENODEV; + + /* +- * use reasonable defaults so platforms don't have to provide these. +- * with DT probing on ARM, none of these are set. ++ * Use reasonable defaults so platforms don't have to provide these ++ * with DT probing on ARM. + */ +- if (!dev_get_platdata(&dev->dev)) +- dev->dev.platform_data = &ehci_platform_defaults; ++ if (!pdata) ++ pdata = &ehci_platform_defaults; + + err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)); + if (err) + return err; + +- pdata = dev_get_platdata(&dev->dev); +- + irq = platform_get_irq(dev, 0); + if (irq < 0) { + dev_err(&dev->dev, "no irq provided"); +@@ -107,17 +169,40 @@ static int ehci_platform_probe(struct platform_device *dev) + return -ENXIO; + } + ++ hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, ++ dev_name(&dev->dev)); ++ if (!hcd) ++ return -ENOMEM; ++ ++ platform_set_drvdata(dev, hcd); ++ dev->dev.platform_data = pdata; ++ priv = hcd_to_ehci_priv(hcd); ++ ++ if (pdata == &ehci_platform_defaults && dev->dev.of_node) { ++ priv->phy = devm_phy_get(&dev->dev, "usb"); ++ if (IS_ERR(priv->phy)) { ++ err = PTR_ERR(priv->phy); ++ if (err == -EPROBE_DEFER) ++ goto err_put_hcd; ++ priv->phy = NULL; ++ } ++ ++ for (clk = 0; clk < EHCI_MAX_CLKS; clk++) { ++ priv->clks[clk] = of_clk_get(dev->dev.of_node, clk); ++ if (IS_ERR(priv->clks[clk])) { ++ err = PTR_ERR(priv->clks[clk]); ++ if (err == -EPROBE_DEFER) ++ goto err_put_clks; ++ priv->clks[clk] = NULL; ++ break; ++ } ++ } ++ } ++ + if (pdata->power_on) { + err = pdata->power_on(dev); + if (err < 0) +- return err; +- } +- +- hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, +- dev_name(&dev->dev)); +- if (!hcd) { +- err = -ENOMEM; +- goto err_power; ++ goto err_put_clks; + } + + hcd->rsrc_start = res_mem->start; +@@ -126,22 +211,28 @@ static int ehci_platform_probe(struct platform_device *dev) + hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); + if (IS_ERR(hcd->regs)) { + err = PTR_ERR(hcd->regs); +- goto err_put_hcd; ++ goto err_power; + } + err = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (err) +- goto err_put_hcd; ++ goto err_power; + + device_wakeup_enable(hcd->self.controller); + platform_set_drvdata(dev, hcd); + + return err; + +-err_put_hcd: +- usb_put_hcd(hcd); + err_power: + if (pdata->power_off) + pdata->power_off(dev); ++err_put_clks: ++ while (--clk >= 0) ++ clk_put(priv->clks[clk]); ++err_put_hcd: ++ if (pdata == &ehci_platform_defaults) ++ dev->dev.platform_data = NULL; ++ ++ usb_put_hcd(hcd); + + return err; + } +@@ -150,13 +241,19 @@ static int ehci_platform_remove(struct platform_device *dev) + { + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); ++ struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); ++ int clk; + + usb_remove_hcd(hcd); +- usb_put_hcd(hcd); + + if (pdata->power_off) + pdata->power_off(dev); + ++ for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) ++ clk_put(priv->clks[clk]); ++ ++ usb_put_hcd(hcd); ++ + if (pdata == &ehci_platform_defaults) + dev->dev.platform_data = NULL; + +@@ -207,8 +304,10 @@ static int ehci_platform_resume(struct device *dev) + static const struct of_device_id vt8500_ehci_ids[] = { + { .compatible = "via,vt8500-ehci", }, + { .compatible = "wm,prizm-ehci", }, ++ { .compatible = "usb-ehci", }, + {} + }; ++MODULE_DEVICE_TABLE(of, vt8500_ehci_ids); + + static const struct platform_device_id ehci_platform_table[] = { + { "ehci-platform", 0 }, +-- +2.0.3 + +From 91fc5f6e5e90d412a6778efbe05e5306a1df5032 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Tue, 21 Jan 2014 16:20:53 +0100 +Subject: [PATCH] ehci-platform: Add support for controllers with big-endian + regs / descriptors + +This uses the already documented devicetree booleans for this, see: +Documentation/devicetree/bindings/usb/usb-ehci.txt + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + drivers/usb/host/ehci-platform.c | 33 +++++++++++++++++++++++++++++++-- + 1 file changed, 31 insertions(+), 2 deletions(-) + +diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c +index 5ebd0b7..8fde649 100644 +--- a/drivers/usb/host/ehci-platform.c ++++ b/drivers/usb/host/ehci-platform.c +@@ -55,8 +55,10 @@ static int ehci_platform_reset(struct usb_hcd *hcd) + + hcd->has_tt = pdata->has_tt; + ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug; +- ehci->big_endian_desc = pdata->big_endian_desc; +- ehci->big_endian_mmio = pdata->big_endian_mmio; ++ if (pdata->big_endian_desc) ++ ehci->big_endian_desc = 1; ++ if (pdata->big_endian_mmio) ++ ehci->big_endian_mmio = 1; + ehci->ignore_oc = pdata->ignore_oc; + + if (pdata->pre_setup) { +@@ -142,6 +144,7 @@ static int ehci_platform_probe(struct platform_device *dev) + struct resource *res_mem; + struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); + struct ehci_platform_priv *priv; ++ struct ehci_hcd *ehci; + int err, irq, clk = 0; + + if (usb_disabled()) +@@ -177,8 +180,34 @@ static int ehci_platform_probe(struct platform_device *dev) + platform_set_drvdata(dev, hcd); + dev->dev.platform_data = pdata; + priv = hcd_to_ehci_priv(hcd); ++ ehci = hcd_to_ehci(hcd); + + if (pdata == &ehci_platform_defaults && dev->dev.of_node) { ++ if (of_property_read_bool(dev->dev.of_node, "big-endian-regs")) ++ ehci->big_endian_mmio = 1; ++ ++ if (of_property_read_bool(dev->dev.of_node, "big-endian-desc")) ++ ehci->big_endian_desc = 1; ++ ++ if (of_property_read_bool(dev->dev.of_node, "big-endian")) ++ ehci->big_endian_mmio = ehci->big_endian_desc = 1; ++ ++#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO ++ if (ehci->big_endian_mmio) { ++ dev_err(&dev->dev, ++ "Error big-endian-regs not compiled in\n"); ++ err = -EINVAL; ++ goto err_put_hcd; ++ } ++#endif ++#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_DESC ++ if (ehci->big_endian_desc) { ++ dev_err(&dev->dev, ++ "Error big-endian-desc not compiled in\n"); ++ err = -EINVAL; ++ goto err_put_hcd; ++ } ++#endif + priv->phy = devm_phy_get(&dev->dev, "usb"); + if (IS_ERR(priv->phy)) { + err = PTR_ERR(priv->phy); +-- +2.0.3 + +From 4a1ce69fa8c4595483493cd7df21c66dbcca1307 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Tue, 11 Feb 2014 11:46:13 +0100 +Subject: [PATCH] ehci-platform: Change compatible string from usb-ehci to + generic-ehci + +The initial versions of the devicetree enablement patches for ehci-platform +used "ehci-platform" as compatible string. However this was disliked by various +reviewers because the platform bus is a Linux invention and devicetree is +supposed to be OS agnostic. After much discussion I gave up, added a: +"depends on !PPC_OF" to Kconfig to avoid a known conflict with PPC-OF platforms +and went with the generic usb-ehci as requested. + +In retro-spect I should have chosen something different, the dts files for many +existing boards already claim to be compatible with "usb-ehci", ie they have: + + compatible = "ti,ehci-omap", "usb-ehci"; + +In theory this should not be a problem since the "ti,ehci-omap" entry takes +presedence, but in practice using a conflicting compatible string is an issue, +because it makes which driver gets used depend on driver registration order. + +This patch changes the compatible string claimed by ehci-platform to +"generic-ehci", avoiding the driver registration / module loading ordering +problems, and removes the "depends on !PPC_OF" workaround. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + Documentation/devicetree/bindings/usb/usb-ehci.txt | 4 ++-- + drivers/usb/host/Kconfig | 1 - + drivers/usb/host/ehci-platform.c | 2 +- + 3 files changed, 3 insertions(+), 4 deletions(-) + +diff --git a/Documentation/devicetree/bindings/usb/usb-ehci.txt b/Documentation/devicetree/bindings/usb/usb-ehci.txt +index 2c1aeeb..ff151ec 100644 +--- a/Documentation/devicetree/bindings/usb/usb-ehci.txt ++++ b/Documentation/devicetree/bindings/usb/usb-ehci.txt +@@ -1,7 +1,7 @@ + USB EHCI controllers + + Required properties: +- - compatible : should be "usb-ehci". ++ - compatible : should be "generic-ehci". + - reg : should contain at least address and length of the standard EHCI + register set for the device. Optional platform-dependent registers + (debug-port or other) can be also specified here, but only after +@@ -27,7 +27,7 @@ Example (Sequoia 440EPx): + + Example (Allwinner sun4i A10 SoC): + ehci0: usb@01c14000 { +- compatible = "allwinner,sun4i-a10-ehci", "usb-ehci"; ++ compatible = "allwinner,sun4i-a10-ehci", "generic-ehci"; + reg = <0x01c14000 0x100>; + interrupts = <39>; + clocks = <&ahb_gates 1>; +diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig +index e28cbe0..a9707da 100644 +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -255,7 +255,6 @@ config USB_EHCI_ATH79 + + config USB_EHCI_HCD_PLATFORM + tristate "Generic EHCI driver for a platform device" +- depends on !PPC_OF + default n + ---help--- + Adds an EHCI host driver for a generic platform device, which +diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c +index 8fde649..1178730 100644 +--- a/drivers/usb/host/ehci-platform.c ++++ b/drivers/usb/host/ehci-platform.c +@@ -333,7 +333,7 @@ static int ehci_platform_resume(struct device *dev) + static const struct of_device_id vt8500_ehci_ids[] = { + { .compatible = "via,vt8500-ehci", }, + { .compatible = "wm,prizm-ehci", }, +- { .compatible = "usb-ehci", }, ++ { .compatible = "generic-ehci", }, + {} + }; + MODULE_DEVICE_TABLE(of, vt8500_ehci_ids); +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/195-3-uhci-plat-changes.patch b/target/linux/sunxi/patches-3.14/195-3-uhci-plat-changes.patch new file mode 100644 index 0000000000..d9ce1fc119 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/195-3-uhci-plat-changes.patch @@ -0,0 +1,83 @@ +From 2adf0917bc9d6db8d957d56c8289a623be4027b6 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Tue, 11 Feb 2014 17:41:48 +0100 +Subject: [PATCH] uhci-platform: Change compatible string from platform-uhci to + generic-uhci + +This brings the uhci-platform bindings in sync with what we've done for +the ohci- and ehci-platform drivers. As discussed there using platform as a +prefix is a bit weird as the platform bus is a Linux specific thing and +the bindings are supposed to be OS agnostic. + +Note that the old platform-uhci compatible string is kept around for, well, +compatibility reasons. + +While at it rename the bindings txt file to match the name of all the +other ?hci-platform bindings docs. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + Documentation/devicetree/bindings/usb/platform-uhci.txt | 15 --------------- + Documentation/devicetree/bindings/usb/usb-uhci.txt | 15 +++++++++++++++ + drivers/usb/host/uhci-platform.c | 1 + + 3 files changed, 16 insertions(+), 15 deletions(-) + delete mode 100644 Documentation/devicetree/bindings/usb/platform-uhci.txt + create mode 100644 Documentation/devicetree/bindings/usb/usb-uhci.txt + +diff --git a/Documentation/devicetree/bindings/usb/platform-uhci.txt b/Documentation/devicetree/bindings/usb/platform-uhci.txt +deleted file mode 100644 +index a4fb071..0000000 +--- a/Documentation/devicetree/bindings/usb/platform-uhci.txt ++++ /dev/null +@@ -1,15 +0,0 @@ +-Generic Platform UHCI Controller +------------------------------------------------------ +- +-Required properties: +-- compatible : "platform-uhci" +-- reg : Should contain 1 register ranges(address and length) +-- interrupts : UHCI controller interrupt +- +-Example: +- +- uhci@d8007b00 { +- compatible = "platform-uhci"; +- reg = <0xd8007b00 0x200>; +- interrupts = <43>; +- }; +diff --git a/Documentation/devicetree/bindings/usb/usb-uhci.txt b/Documentation/devicetree/bindings/usb/usb-uhci.txt +new file mode 100644 +index 0000000..2981334 +--- /dev/null ++++ b/Documentation/devicetree/bindings/usb/usb-uhci.txt +@@ -0,0 +1,15 @@ ++Generic Platform UHCI Controller ++----------------------------------------------------- ++ ++Required properties: ++- compatible : "generic-uhci" (deprecated: "platform-uhci") ++- reg : Should contain 1 register ranges(address and length) ++- interrupts : UHCI controller interrupt ++ ++Example: ++ ++ uhci@d8007b00 { ++ compatible = "generic-uhci"; ++ reg = <0xd8007b00 0x200>; ++ interrupts = <43>; ++ }; +diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c +index 44e6c9d..01833ab 100644 +--- a/drivers/usb/host/uhci-platform.c ++++ b/drivers/usb/host/uhci-platform.c +@@ -148,6 +148,7 @@ static void uhci_hcd_platform_shutdown(struct platform_device *op) + } + + static const struct of_device_id platform_uhci_ids[] = { ++ { .compatible = "generic-uhci", }, + { .compatible = "platform-uhci", }, + {} + }; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/195-4-xhci-plat-changes.patch b/target/linux/sunxi/patches-3.14/195-4-xhci-plat-changes.patch new file mode 100644 index 0000000000..81dc4d4642 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/195-4-xhci-plat-changes.patch @@ -0,0 +1,56 @@ +From 8fd033e1b6cdd30c32762ef1c5e2216226dd61e1 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Tue, 11 Feb 2014 17:50:51 +0100 +Subject: [PATCH] xhci-platform: Change compatible string from xhci-platform to + generic-xhci + +This brings the xhci-platform bindings in sync with what we've done for +the ohci- and ehci-platform drivers. As discussed there using platform as a +postfix is a bit weird as the platform bus is a Linux specific thing and +the bindings are supposed to be OS agnostic. + +Note that the old xhci-platform compatible string is kept around for, well, +compatibility reasons. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + Documentation/devicetree/bindings/usb/usb-xhci.txt | 4 ++-- + drivers/usb/host/xhci-plat.c | 1 + + 2 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt +index 5752df0..90f8f60 100644 +--- a/Documentation/devicetree/bindings/usb/usb-xhci.txt ++++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt +@@ -1,14 +1,14 @@ + USB xHCI controllers + + Required properties: +- - compatible: should be "xhci-platform". ++ - compatible: should be "generic-xhci" (deprecated: "xhci-platform"). + - reg: should contain address and length of the standard XHCI + register set for the device. + - interrupts: one XHCI interrupt should be described here. + + Example: + usb@f0931000 { +- compatible = "xhci-platform"; ++ compatible = "generic-xhci"; + reg = <0xf0931000 0x8c8>; + interrupts = <0x0 0x4e 0x0>; + }; +diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c +index 8abda5c..8affef9 100644 +--- a/drivers/usb/host/xhci-plat.c ++++ b/drivers/usb/host/xhci-plat.c +@@ -226,6 +226,7 @@ static const struct dev_pm_ops xhci_plat_pm_ops = { + + #ifdef CONFIG_OF + static const struct of_device_id usb_xhci_of_match[] = { ++ { .compatible = "generic-xhci" }, + { .compatible = "xhci-platform" }, + { }, + }; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/196-usb-add-sunxi-phy-driver.patch b/target/linux/sunxi/patches-3.14/196-usb-add-sunxi-phy-driver.patch new file mode 100644 index 0000000000..eee697709a --- /dev/null +++ b/target/linux/sunxi/patches-3.14/196-usb-add-sunxi-phy-driver.patch @@ -0,0 +1,423 @@ +From 56feaa546c5ce4152fe14f725e9fc6b85f8a565b Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 4 Jan 2014 23:56:17 +0100 +Subject: [PATCH] PHY: sunxi: Add driver for sunxi usb phy + +The Allwinner A1x / A2x SoCs have 2 or 3 usb phys which are all accessed +through a single set of registers. Besides this there are also some other +phy related bits which need poking, which are per phy, but shared between the +ohci and ehci controllers, so these are also controlled from this new phy +driver. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + .../devicetree/bindings/phy/sun4i-usb-phy.txt | 26 ++ + drivers/phy/Kconfig | 11 + + drivers/phy/Makefile | 1 + + drivers/phy/phy-sun4i-usb.c | 331 +++++++++++++++++++++ + 4 files changed, 369 insertions(+) + create mode 100644 Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt + create mode 100644 drivers/phy/phy-sun4i-usb.c + +diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt +new file mode 100644 +index 0000000..a82361b +--- /dev/null ++++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt +@@ -0,0 +1,26 @@ ++Allwinner sun4i USB PHY ++----------------------- ++ ++Required properties: ++- compatible : should be one of "allwinner,sun4i-a10-usb-phy", ++ "allwinner,sun5i-a13-usb-phy" or "allwinner,sun7i-a20-usb-phy" ++- reg : a list of offset + length pairs ++- reg-names : "phy_ctrl", "pmu1" and for sun4i or sun7i "pmu2" ++- #phy-cells : from the generic phy bindings, must be 1 ++- clocks : phandle + clock specifier for the phy clock ++- clock-names : "usb_phy" ++- resets : a list of phandle + reset specifier pairs ++- reset-names : "usb0_reset", "usb1_reset" and for sun4i or sun7i "usb2_reset" ++ ++Example: ++ usbphy: phy@0x01c13400 { ++ #phy-cells = <1>; ++ compatible = "allwinner,sun4i-a10-usb-phy"; ++ /* phy base regs, phy1 pmu reg, phy2 pmu reg */ ++ reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>; ++ reg-names = "phy_ctrl", "pmu1", "pmu2"; ++ clocks = <&usb_clk 8>; ++ clock-names = "usb_phy"; ++ resets = <&usb_clk 1>, <&usb_clk 2>; ++ reset-names = "usb1_reset", "usb2_reset"; ++ }; +diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig +index c7a551c..66f7c4e 100644 +--- a/drivers/phy/Kconfig ++++ b/drivers/phy/Kconfig +@@ -65,4 +65,15 @@ config BCM_KONA_USB2_PHY + help + Enable this to support the Broadcom Kona USB 2.0 PHY. + ++config PHY_SUN4I_USB ++ tristate "Allwinner sunxi SoC USB PHY driver" ++ depends on ARCH_SUNXI && HAS_IOMEM && OF ++ select GENERIC_PHY ++ help ++ Enable this to support the transceiver that is part of Allwinner ++ sunxi SoCs. ++ ++ This driver controls the entire USB PHY block, both the USB OTG ++ parts, as well as the 2 regular USB 2 host PHYs. ++ + endmenu +diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile +index b57c253..9d4f8bb 100644 +--- a/drivers/phy/Makefile ++++ b/drivers/phy/Makefile +@@ -9,3 +9,4 @@ obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o + obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o + obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o + obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o ++obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o +diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c +new file mode 100644 +index 0000000..e6e6c4b +--- /dev/null ++++ b/drivers/phy/phy-sun4i-usb.c +@@ -0,0 +1,331 @@ ++/* ++ * Allwinner sun4i USB phy driver ++ * ++ * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com> ++ * ++ * Based on code from ++ * Allwinner Technology Co., Ltd. <www.allwinnertech.com> ++ * ++ * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver ++ * Copyright (C) 2013 Samsung Electronics Co., Ltd. ++ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> ++ * ++ * 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.h> ++#include <linux/io.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/mutex.h> ++#include <linux/of.h> ++#include <linux/of_address.h> ++#include <linux/phy/phy.h> ++#include <linux/platform_device.h> ++#include <linux/regulator/consumer.h> ++#include <linux/reset.h> ++ ++#define REG_ISCR 0x00 ++#define REG_PHYCTL 0x04 ++#define REG_PHYBIST 0x08 ++#define REG_PHYTUNE 0x0c ++ ++#define PHYCTL_DATA BIT(7) ++ ++#define SUNXI_AHB_ICHR8_EN BIT(10) ++#define SUNXI_AHB_INCR4_BURST_EN BIT(9) ++#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8) ++#define SUNXI_ULPI_BYPASS_EN BIT(0) ++ ++/* Common Control Bits for Both PHYs */ ++#define PHY_PLL_BW 0x03 ++#define PHY_RES45_CAL_EN 0x0c ++ ++/* Private Control Bits for Each PHY */ ++#define PHY_TX_AMPLITUDE_TUNE 0x20 ++#define PHY_TX_SLEWRATE_TUNE 0x22 ++#define PHY_VBUSVALID_TH_SEL 0x25 ++#define PHY_PULLUP_RES_SEL 0x27 ++#define PHY_OTG_FUNC_EN 0x28 ++#define PHY_VBUS_DET_EN 0x29 ++#define PHY_DISCON_TH_SEL 0x2a ++ ++#define MAX_PHYS 3 ++ ++struct sun4i_usb_phy_data { ++ struct clk *clk; ++ void __iomem *base; ++ struct mutex mutex; ++ int num_phys; ++ u32 disc_thresh; ++ struct sun4i_usb_phy { ++ struct phy *phy; ++ void __iomem *pmu; ++ struct regulator *vbus; ++ struct reset_control *reset; ++ int index; ++ } phys[MAX_PHYS]; ++}; ++ ++#define to_sun4i_usb_phy_data(phy) \ ++ container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index]) ++ ++static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data, ++ int len) ++{ ++ struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy); ++ u32 temp, usbc_bit = BIT(phy->index * 2); ++ int i; ++ ++ mutex_lock(&phy_data->mutex); ++ ++ for (i = 0; i < len; i++) { ++ temp = readl(phy_data->base + REG_PHYCTL); ++ ++ /* clear the address portion */ ++ temp &= ~(0xff << 8); ++ ++ /* set the address */ ++ temp |= ((addr + i) << 8); ++ writel(temp, phy_data->base + REG_PHYCTL); ++ ++ /* set the data bit and clear usbc bit*/ ++ temp = readb(phy_data->base + REG_PHYCTL); ++ if (data & 0x1) ++ temp |= PHYCTL_DATA; ++ else ++ temp &= ~PHYCTL_DATA; ++ temp &= ~usbc_bit; ++ writeb(temp, phy_data->base + REG_PHYCTL); ++ ++ /* pulse usbc_bit */ ++ temp = readb(phy_data->base + REG_PHYCTL); ++ temp |= usbc_bit; ++ writeb(temp, phy_data->base + REG_PHYCTL); ++ ++ temp = readb(phy_data->base + REG_PHYCTL); ++ temp &= ~usbc_bit; ++ writeb(temp, phy_data->base + REG_PHYCTL); ++ ++ data >>= 1; ++ } ++ mutex_unlock(&phy_data->mutex); ++} ++ ++static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable) ++{ ++ u32 bits, reg_value; ++ ++ if (!phy->pmu) ++ return; ++ ++ bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN | ++ SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN; ++ ++ reg_value = readl(phy->pmu); ++ ++ if (enable) ++ reg_value |= bits; ++ else ++ reg_value &= ~bits; ++ ++ writel(reg_value, phy->pmu); ++} ++ ++static int sun4i_usb_phy_init(struct phy *_phy) ++{ ++ struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); ++ struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); ++ int ret; ++ ++ ret = clk_prepare_enable(data->clk); ++ if (ret) ++ return ret; ++ ++ ret = reset_control_deassert(phy->reset); ++ if (ret) { ++ clk_disable_unprepare(data->clk); ++ return ret; ++ } ++ ++ /* Adjust PHY's magnitude and rate */ ++ sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5); ++ ++ /* Disconnect threshold adjustment */ ++ sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, data->disc_thresh, 2); ++ ++ sun4i_usb_phy_passby(phy, 1); ++ ++ return 0; ++} ++ ++static int sun4i_usb_phy_exit(struct phy *_phy) ++{ ++ struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); ++ struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); ++ ++ sun4i_usb_phy_passby(phy, 0); ++ reset_control_assert(phy->reset); ++ clk_disable_unprepare(data->clk); ++ ++ return 0; ++} ++ ++static int sun4i_usb_phy_power_on(struct phy *_phy) ++{ ++ struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); ++ int ret = 0; ++ ++ if (phy->vbus) ++ ret = regulator_enable(phy->vbus); ++ ++ return ret; ++} ++ ++static int sun4i_usb_phy_power_off(struct phy *_phy) ++{ ++ struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); ++ ++ if (phy->vbus) ++ regulator_disable(phy->vbus); ++ ++ return 0; ++} ++ ++static struct phy_ops sun4i_usb_phy_ops = { ++ .init = sun4i_usb_phy_init, ++ .exit = sun4i_usb_phy_exit, ++ .power_on = sun4i_usb_phy_power_on, ++ .power_off = sun4i_usb_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static struct phy *sun4i_usb_phy_xlate(struct device *dev, ++ struct of_phandle_args *args) ++{ ++ struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); ++ ++ if (WARN_ON(args->args[0] == 0 || args->args[0] >= data->num_phys)) ++ return ERR_PTR(-ENODEV); ++ ++ return data->phys[args->args[0]].phy; ++} ++ ++static int sun4i_usb_phy_probe(struct platform_device *pdev) ++{ ++ struct sun4i_usb_phy_data *data; ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ void __iomem *pmu = NULL; ++ struct phy_provider *phy_provider; ++ struct reset_control *reset; ++ struct regulator *vbus; ++ struct resource *res; ++ struct phy *phy; ++ char name[16]; ++ int i; ++ ++ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ mutex_init(&data->mutex); ++ ++ if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy")) ++ data->num_phys = 2; ++ else ++ data->num_phys = 3; ++ ++ if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy")) ++ data->disc_thresh = 3; ++ else ++ data->disc_thresh = 2; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl"); ++ data->base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(data->base)) ++ return PTR_ERR(data->base); ++ ++ data->clk = devm_clk_get(dev, "usb_phy"); ++ if (IS_ERR(data->clk)) { ++ dev_err(dev, "could not get usb_phy clock\n"); ++ return PTR_ERR(data->clk); ++ } ++ ++ /* Skip 0, 0 is the phy for otg which is not yet supported. */ ++ for (i = 1; i < data->num_phys; i++) { ++ snprintf(name, sizeof(name), "usb%d_vbus", i); ++ vbus = devm_regulator_get_optional(dev, name); ++ if (IS_ERR(vbus)) { ++ if (PTR_ERR(vbus) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ vbus = NULL; ++ } ++ ++ snprintf(name, sizeof(name), "usb%d_reset", i); ++ reset = devm_reset_control_get(dev, name); ++ if (IS_ERR(reset)) { ++ dev_err(dev, "failed to get reset %s\n", name); ++ return PTR_ERR(reset); ++ } ++ ++ if (i) { /* No pmu for usbc0 */ ++ snprintf(name, sizeof(name), "pmu%d", i); ++ res = platform_get_resource_byname(pdev, ++ IORESOURCE_MEM, name); ++ pmu = devm_ioremap_resource(dev, res); ++ if (IS_ERR(pmu)) ++ return PTR_ERR(pmu); ++ } ++ ++ phy = devm_phy_create(dev, &sun4i_usb_phy_ops, NULL); ++ if (IS_ERR(phy)) { ++ dev_err(dev, "failed to create PHY %d\n", i); ++ return PTR_ERR(phy); ++ } ++ ++ data->phys[i].phy = phy; ++ data->phys[i].pmu = pmu; ++ data->phys[i].vbus = vbus; ++ data->phys[i].reset = reset; ++ data->phys[i].index = i; ++ phy_set_drvdata(phy, &data->phys[i]); ++ } ++ ++ dev_set_drvdata(dev, data); ++ phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate); ++ if (IS_ERR(phy_provider)) ++ return PTR_ERR(phy_provider); ++ ++ return 0; ++} ++ ++static const struct of_device_id sun4i_usb_phy_of_match[] = { ++ { .compatible = "allwinner,sun4i-a10-usb-phy" }, ++ { .compatible = "allwinner,sun5i-a13-usb-phy" }, ++ { .compatible = "allwinner,sun7i-a20-usb-phy" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match); ++ ++static struct platform_driver sun4i_usb_phy_driver = { ++ .probe = sun4i_usb_phy_probe, ++ .driver = { ++ .of_match_table = sun4i_usb_phy_of_match, ++ .name = "sun4i-usb-phy", ++ .owner = THIS_MODULE, ++ } ++}; ++module_platform_driver(sun4i_usb_phy_driver); ++ ++MODULE_DESCRIPTION("Allwinner sun4i USB phy driver"); ++MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); ++MODULE_LICENSE("GPL v2"); +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/200-mmc-add-driver.patch b/target/linux/sunxi/patches-3.14/200-mmc-add-driver.patch new file mode 100644 index 0000000000..985b8b7947 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/200-mmc-add-driver.patch @@ -0,0 +1,1163 @@ +From a2fb45195f1b90058e96e09892c071ef4207b593 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?David=20Lanzend=C3=B6rfer?= <david.lanzendoerfer@o2s.ch> +Date: Sat, 22 Feb 2014 09:13:55 +0100 +Subject: [PATCH] ARM: sunxi: Add driver for SD/MMC hosts found on Allwinner + sunxi SoCs +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This is based on the driver Allwinner ships in their Android kernel sources. + +Initial porting to upstream kernels done by David Lanzendörfer, additional +fixes and cleanups by Hans de Goede. + +It uses dma in bus-master mode using a built-in designware idmac controller, +which is identical to the one found in the mmc-dw hosts. +The rest of the host is not identical to mmc-dw. + +Signed-off-by: David Lanzendörfer <david.lanzendoerfer@o2s.ch> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + drivers/mmc/host/Kconfig | 7 + + drivers/mmc/host/Makefile | 2 + + drivers/mmc/host/sunxi-mmc.c | 853 +++++++++++++++++++++++++++++++++++++++++++ + drivers/mmc/host/sunxi-mmc.h | 238 ++++++++++++ + 4 files changed, 1100 insertions(+) + create mode 100644 drivers/mmc/host/sunxi-mmc.c + create mode 100644 drivers/mmc/host/sunxi-mmc.h + +diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig +index 1384f67..7caf266 100644 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -689,3 +689,10 @@ config MMC_REALTEK_PCI + help + Say Y here to include driver code to support SD/MMC card interface + of Realtek PCI-E card reader ++ ++config MMC_SUNXI ++ tristate "Allwinner sunxi SD/MMC Host Controller support" ++ depends on ARCH_SUNXI ++ help ++ This selects support for the SD/MMC Host Controller on ++ Allwinner sunxi SoCs. +diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile +index 3483b6b..f3c7c243 100644 +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -54,6 +54,8 @@ obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o + + obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o + ++obj-$(CONFIG_MMC_SUNXI) += sunxi-mmc.o ++ + obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o + obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o + obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o +diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c +new file mode 100644 +index 0000000..a16abd2 +--- /dev/null ++++ b/drivers/mmc/host/sunxi-mmc.c +@@ -0,0 +1,853 @@ ++/* ++ * Driver for sunxi SD/MMC host controllers ++ * (C) Copyright 2007-2011 Reuuimlla Technology Co., Ltd. ++ * (C) Copyright 2007-2011 Aaron Maoye <leafy.myeh@reuuimllatech.com> ++ * (C) Copyright 2013-2014 O2S GmbH <www.o2s.ch> ++ * (C) Copyright 2013-2014 David Lanzendörfer <david.lanzendoerfer@o2s.ch> ++ * (C) Copyright 2013-2014 Hans de Goede <hdegoede@redhat.com> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/io.h> ++#include <linux/device.h> ++#include <linux/interrupt.h> ++#include <linux/delay.h> ++#include <linux/err.h> ++ ++#include <linux/clk.h> ++#include <linux/clk-private.h> ++#include <linux/clk/sunxi.h> ++ ++#include <linux/gpio.h> ++#include <linux/platform_device.h> ++#include <linux/spinlock.h> ++#include <linux/scatterlist.h> ++#include <linux/dma-mapping.h> ++#include <linux/slab.h> ++#include <linux/regulator/consumer.h> ++ ++#include <linux/of_address.h> ++#include <linux/of_gpio.h> ++#include <linux/of_platform.h> ++ ++#include <linux/mmc/host.h> ++#include <linux/mmc/sd.h> ++#include <linux/mmc/sdio.h> ++#include <linux/mmc/mmc.h> ++#include <linux/mmc/core.h> ++#include <linux/mmc/card.h> ++#include <linux/mmc/slot-gpio.h> ++ ++#include "sunxi-mmc.h" ++ ++static int sunxi_mmc_init_host(struct mmc_host *mmc) ++{ ++ u32 rval; ++ struct sunxi_mmc_host *smc_host = mmc_priv(mmc); ++ int ret; ++ ++ ret = clk_prepare_enable(smc_host->clk_ahb); ++ if (ret) { ++ dev_err(mmc_dev(smc_host->mmc), "AHB clk err %d\n", ret); ++ return ret; ++ } ++ ret = clk_prepare_enable(smc_host->clk_mod); ++ if (ret) { ++ dev_err(mmc_dev(smc_host->mmc), "MOD clk err %d\n", ret); ++ clk_disable_unprepare(smc_host->clk_ahb); ++ return ret; ++ } ++ ++ /* reset controller */ ++ rval = mci_readl(smc_host, REG_GCTRL) | SDXC_HARDWARE_RESET; ++ mci_writel(smc_host, REG_GCTRL, rval); ++ ++ mci_writel(smc_host, REG_FTRGL, 0x20070008); ++ mci_writel(smc_host, REG_TMOUT, 0xffffffff); ++ mci_writel(smc_host, REG_IMASK, smc_host->sdio_imask); ++ mci_writel(smc_host, REG_RINTR, 0xffffffff); ++ mci_writel(smc_host, REG_DBGC, 0xdeb); ++ mci_writel(smc_host, REG_FUNS, 0xceaa0000); ++ mci_writel(smc_host, REG_DLBA, smc_host->sg_dma); ++ rval = mci_readl(smc_host, REG_GCTRL)|SDXC_INTERRUPT_ENABLE_BIT; ++ rval &= ~SDXC_ACCESS_DONE_DIRECT; ++ mci_writel(smc_host, REG_GCTRL, rval); ++ ++ return 0; ++} ++ ++static void sunxi_mmc_exit_host(struct sunxi_mmc_host *smc_host) ++{ ++ mci_writel(smc_host, REG_GCTRL, SDXC_HARDWARE_RESET); ++ clk_disable_unprepare(smc_host->clk_ahb); ++ clk_disable_unprepare(smc_host->clk_mod); ++} ++ ++/* /\* UHS-I Operation Modes */ ++/* * DS 25MHz 12.5MB/s 3.3V */ ++/* * HS 50MHz 25MB/s 3.3V */ ++/* * SDR12 25MHz 12.5MB/s 1.8V */ ++/* * SDR25 50MHz 25MB/s 1.8V */ ++/* * SDR50 100MHz 50MB/s 1.8V */ ++/* * SDR104 208MHz 104MB/s 1.8V */ ++/* * DDR50 50MHz 50MB/s 1.8V */ ++/* * MMC Operation Modes */ ++/* * DS 26MHz 26MB/s 3/1.8/1.2V */ ++/* * HS 52MHz 52MB/s 3/1.8/1.2V */ ++/* * HSDDR 52MHz 104MB/s 3/1.8/1.2V */ ++/* * HS200 200MHz 200MB/s 1.8/1.2V */ ++/* * */ ++/* * Spec. Timing */ ++/* * SD3.0 */ ++/* * Fcclk Tcclk Fsclk Tsclk Tis Tih odly RTis RTih */ ++/* * 400K 2.5us 24M 41ns 5ns 5ns 1 2209ns 41ns */ ++/* * 25M 40ns 600M 1.67ns 5ns 5ns 3 14.99ns 5.01ns */ ++/* * 50M 20ns 600M 1.67ns 6ns 2ns 3 14.99ns 5.01ns */ ++/* * 50MDDR 20ns 600M 1.67ns 6ns 0.8ns 2 6.67ns 3.33ns */ ++/* * 104M 9.6ns 600M 1.67ns 3ns 0.8ns 1 7.93ns 1.67ns */ ++/* * 208M 4.8ns 600M 1.67ns 1.4ns 0.8ns 1 3.33ns 1.67ns */ ++ ++/* * 25M 40ns 300M 3.33ns 5ns 5ns 2 13.34ns 6.66ns */ ++/* * 50M 20ns 300M 3.33ns 6ns 2ns 2 13.34ns 6.66ns */ ++/* * 50MDDR 20ns 300M 3.33ns 6ns 0.8ns 1 6.67ns 3.33ns */ ++/* * 104M 9.6ns 300M 3.33ns 3ns 0.8ns 0 7.93ns 1.67ns */ ++/* * 208M 4.8ns 300M 3.33ns 1.4ns 0.8ns 0 3.13ns 1.67ns */ ++ ++/* * eMMC4.5 */ ++/* * 400K 2.5us 24M 41ns 3ns 3ns 1 2209ns 41ns */ ++/* * 25M 40ns 600M 1.67ns 3ns 3ns 3 14.99ns 5.01ns */ ++/* * 50M 20ns 600M 1.67ns 3ns 3ns 3 14.99ns 5.01ns */ ++/* * 50MDDR 20ns 600M 1.67ns 2.5ns 2.5ns 2 6.67ns 3.33ns */ ++/* * 200M 5ns 600M 1.67ns 1.4ns 0.8ns 1 3.33ns 1.67ns */ ++/* *\/ */ ++ ++static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host, ++ struct mmc_data *data) ++{ ++ struct sunxi_idma_des *pdes = (struct sunxi_idma_des *)host->sg_cpu; ++ struct sunxi_idma_des *pdes_pa = (struct sunxi_idma_des *)host->sg_dma; ++ int i, max_len = (1 << host->idma_des_size_bits); ++ ++ for (i = 0; i < data->sg_len; i++) { ++ pdes[i].config = SDXC_IDMAC_DES0_CH | SDXC_IDMAC_DES0_OWN | ++ SDXC_IDMAC_DES0_DIC; ++ ++ if (data->sg[i].length == max_len) ++ pdes[i].buf_size = 0; /* 0 == max_len */ ++ else ++ pdes[i].buf_size = data->sg[i].length; ++ ++ pdes[i].buf_addr_ptr1 = sg_dma_address(&data->sg[i]); ++ pdes[i].buf_addr_ptr2 = (u32)&pdes_pa[i + 1]; ++ } ++ ++ pdes[0].config |= SDXC_IDMAC_DES0_FD; ++ pdes[i - 1].config = SDXC_IDMAC_DES0_OWN | SDXC_IDMAC_DES0_LD; ++ ++ wmb(); /* Ensure idma_des hit main mem before we start the idmac */ ++} ++ ++static enum dma_data_direction sunxi_mmc_get_dma_dir(struct mmc_data *data) ++{ ++ if (data->flags & MMC_DATA_WRITE) ++ return DMA_TO_DEVICE; ++ else ++ return DMA_FROM_DEVICE; ++} ++ ++static int sunxi_mmc_prepare_dma(struct sunxi_mmc_host *smc_host, ++ struct mmc_data *data) ++{ ++ u32 dma_len; ++ u32 i; ++ u32 temp; ++ struct scatterlist *sg; ++ ++ dma_len = dma_map_sg(mmc_dev(smc_host->mmc), data->sg, data->sg_len, ++ sunxi_mmc_get_dma_dir(data)); ++ if (dma_len == 0) { ++ dev_err(mmc_dev(smc_host->mmc), "dma_map_sg failed\n"); ++ return -ENOMEM; ++ } ++ ++ for_each_sg(data->sg, sg, data->sg_len, i) { ++ if (sg->offset & 3 || sg->length & 3) { ++ dev_err(mmc_dev(smc_host->mmc), ++ "unaligned scatterlist: os %x length %d\n", ++ sg->offset, sg->length); ++ return -EINVAL; ++ } ++ } ++ ++ sunxi_mmc_init_idma_des(smc_host, data); ++ ++ temp = mci_readl(smc_host, REG_GCTRL); ++ temp |= SDXC_DMA_ENABLE_BIT; ++ mci_writel(smc_host, REG_GCTRL, temp); ++ temp |= SDXC_DMA_RESET; ++ mci_writel(smc_host, REG_GCTRL, temp); ++ mci_writel(smc_host, REG_DMAC, SDXC_IDMAC_SOFT_RESET); ++ ++ if (!(data->flags & MMC_DATA_WRITE)) ++ mci_writel(smc_host, REG_IDIE, SDXC_IDMAC_RECEIVE_INTERRUPT); ++ ++ mci_writel(smc_host, REG_DMAC, SDXC_IDMAC_FIX_BURST | SDXC_IDMAC_IDMA_ON); ++ ++ return 0; ++} ++ ++static void sunxi_mmc_send_manual_stop(struct sunxi_mmc_host *host, ++ struct mmc_request *req) ++{ ++ u32 cmd_val = SDXC_START | SDXC_RESP_EXPIRE | SDXC_STOP_ABORT_CMD ++ | SDXC_CHECK_RESPONSE_CRC | MMC_STOP_TRANSMISSION; ++ u32 ri = 0; ++ unsigned long expire = jiffies + msecs_to_jiffies(1000); ++ ++ mci_writel(host, REG_CARG, 0); ++ mci_writel(host, REG_CMDR, cmd_val); ++ ++ do { ++ ri = mci_readl(host, REG_RINTR); ++ } while (!(ri & (SDXC_COMMAND_DONE | SDXC_INTERRUPT_ERROR_BIT)) && ++ time_before(jiffies, expire)); ++ ++ if (ri & SDXC_INTERRUPT_ERROR_BIT) { ++ dev_err(mmc_dev(host->mmc), "send stop command failed\n"); ++ if (req->stop) ++ req->stop->resp[0] = -ETIMEDOUT; ++ } else { ++ if (req->stop) ++ req->stop->resp[0] = mci_readl(host, REG_RESP0); ++ } ++ ++ mci_writel(host, REG_RINTR, 0xffff); ++} ++ ++static void sunxi_mmc_dump_errinfo(struct sunxi_mmc_host *smc_host) ++{ ++ struct mmc_command *cmd = smc_host->mrq->cmd; ++ struct mmc_data *data = smc_host->mrq->data; ++ ++ /* For some cmds timeout is normal with sd/mmc cards */ ++ if ((smc_host->int_sum & SDXC_INTERRUPT_ERROR_BIT) == SDXC_RESP_TIMEOUT && ++ (cmd->opcode == SD_IO_SEND_OP_COND || cmd->opcode == SD_IO_RW_DIRECT)) ++ return; ++ ++ dev_err(mmc_dev(smc_host->mmc), ++ "smc %d err, cmd %d,%s%s%s%s%s%s%s%s%s%s !!\n", ++ smc_host->mmc->index, cmd->opcode, ++ data ? (data->flags & MMC_DATA_WRITE ? " WR" : " RD") : "", ++ smc_host->int_sum & SDXC_RESP_ERROR ? " RE" : "", ++ smc_host->int_sum & SDXC_RESP_CRC_ERROR ? " RCE" : "", ++ smc_host->int_sum & SDXC_DATA_CRC_ERROR ? " DCE" : "", ++ smc_host->int_sum & SDXC_RESP_TIMEOUT ? " RTO" : "", ++ smc_host->int_sum & SDXC_DATA_TIMEOUT ? " DTO" : "", ++ smc_host->int_sum & SDXC_FIFO_RUN_ERROR ? " FE" : "", ++ smc_host->int_sum & SDXC_HARD_WARE_LOCKED ? " HL" : "", ++ smc_host->int_sum & SDXC_START_BIT_ERROR ? " SBE" : "", ++ smc_host->int_sum & SDXC_END_BIT_ERROR ? " EBE" : "" ++ ); ++} ++ ++static void sunxi_mmc_finalize_request(struct sunxi_mmc_host *host) ++{ ++ struct mmc_request *mrq; ++ unsigned long iflags; ++ ++ spin_lock_irqsave(&host->lock, iflags); ++ ++ mrq = host->mrq; ++ if (!mrq) { ++ spin_unlock_irqrestore(&host->lock, iflags); ++ dev_err(mmc_dev(host->mmc), "no request to finalize\n"); ++ return; ++ } ++ ++ if (host->int_sum & SDXC_INTERRUPT_ERROR_BIT) { ++ sunxi_mmc_dump_errinfo(host); ++ mrq->cmd->error = -ETIMEDOUT; ++ if (mrq->data) ++ mrq->data->error = -ETIMEDOUT; ++ if (mrq->stop) ++ mrq->stop->error = -ETIMEDOUT; ++ } else { ++ if (mrq->cmd->flags & MMC_RSP_136) { ++ mrq->cmd->resp[0] = mci_readl(host, REG_RESP3); ++ mrq->cmd->resp[1] = mci_readl(host, REG_RESP2); ++ mrq->cmd->resp[2] = mci_readl(host, REG_RESP1); ++ mrq->cmd->resp[3] = mci_readl(host, REG_RESP0); ++ } else { ++ mrq->cmd->resp[0] = mci_readl(host, REG_RESP0); ++ } ++ if (mrq->data) ++ mrq->data->bytes_xfered = ++ mrq->data->blocks * mrq->data->blksz; ++ } ++ ++ if (mrq->data) { ++ struct mmc_data *data = mrq->data; ++ u32 temp; ++ ++ mci_writel(host, REG_IDST, 0x337); ++ mci_writel(host, REG_DMAC, 0); ++ temp = mci_readl(host, REG_GCTRL); ++ mci_writel(host, REG_GCTRL, temp|SDXC_DMA_RESET); ++ temp &= ~SDXC_DMA_ENABLE_BIT; ++ mci_writel(host, REG_GCTRL, temp); ++ temp |= SDXC_FIFO_RESET; ++ mci_writel(host, REG_GCTRL, temp); ++ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, ++ sunxi_mmc_get_dma_dir(data)); ++ } ++ ++ mci_writel(host, REG_RINTR, 0xffff); ++ ++ dev_dbg(mmc_dev(host->mmc), "req done, resp %08x %08x %08x %08x\n", ++ mrq->cmd->resp[0], mrq->cmd->resp[1], ++ mrq->cmd->resp[2], mrq->cmd->resp[3]); ++ ++ host->mrq = NULL; ++ host->int_sum = 0; ++ host->wait_dma = 0; ++ ++ spin_unlock_irqrestore(&host->lock, iflags); ++ ++ if (mrq->data && mrq->data->error) { ++ dev_err(mmc_dev(host->mmc), ++ "data error, sending stop command\n"); ++ sunxi_mmc_send_manual_stop(host, mrq); ++ } ++ ++ mmc_request_done(host->mmc, mrq); ++} ++ ++static irqreturn_t sunxi_mmc_irq(int irq, void *dev_id) ++{ ++ struct sunxi_mmc_host *host = dev_id; ++ u32 finalize = 0; ++ u32 sdio_int = 0; ++ u32 msk_int; ++ u32 idma_int; ++ ++ spin_lock(&host->lock); ++ ++ idma_int = mci_readl(host, REG_IDST); ++ msk_int = mci_readl(host, REG_MISTA); ++ ++ dev_dbg(mmc_dev(host->mmc), "irq: rq %p mi %08x idi %08x\n", ++ host->mrq, msk_int, idma_int); ++ ++ if (host->mrq) { ++ if (idma_int & SDXC_IDMAC_RECEIVE_INTERRUPT) ++ host->wait_dma = 0; ++ ++ host->int_sum |= msk_int; ++ ++ /* Wait for COMMAND_DONE on RESPONSE_TIMEOUT before finishing the req */ ++ if ((host->int_sum & SDXC_RESP_TIMEOUT) && ++ !(host->int_sum & SDXC_COMMAND_DONE)) ++ mci_writel(host, REG_IMASK, ++ host->sdio_imask | SDXC_COMMAND_DONE); ++ else if (host->int_sum & SDXC_INTERRUPT_ERROR_BIT) ++ finalize = 1; /* Don't wait for dma on error */ ++ else if (host->int_sum & SDXC_INTERRUPT_DONE_BIT && !host->wait_dma) ++ finalize = 1; /* Done */ ++ ++ if (finalize) { ++ mci_writel(host, REG_IMASK, host->sdio_imask); ++ mci_writel(host, REG_IDIE, 0); ++ } ++ } ++ ++ if (msk_int & SDXC_SDIO_INTERRUPT) ++ sdio_int = 1; ++ ++ mci_writel(host, REG_RINTR, msk_int); ++ mci_writel(host, REG_IDST, idma_int); ++ ++ spin_unlock(&host->lock); ++ ++ if (finalize) ++ tasklet_schedule(&host->tasklet); ++ ++ if (sdio_int) ++ mmc_signal_sdio_irq(host->mmc); ++ ++ return IRQ_HANDLED; ++} ++ ++static void sunxi_mmc_tasklet(unsigned long data) ++{ ++ struct sunxi_mmc_host *smc_host = (struct sunxi_mmc_host *) data; ++ sunxi_mmc_finalize_request(smc_host); ++} ++ ++static void sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en) ++{ ++ unsigned long expire = jiffies + msecs_to_jiffies(2000); ++ u32 rval; ++ ++ rval = mci_readl(host, REG_CLKCR); ++ rval &= ~(SDXC_CARD_CLOCK_ON | SDXC_LOW_POWER_ON); ++ ++ if (oclk_en) ++ rval |= SDXC_CARD_CLOCK_ON; ++ ++ mci_writel(host, REG_CLKCR, rval); ++ ++ rval = SDXC_START | SDXC_UPCLK_ONLY | SDXC_WAIT_PRE_OVER; ++ if (host->voltage_switching) ++ rval |= SDXC_VOLTAGE_SWITCH; ++ mci_writel(host, REG_CMDR, rval); ++ ++ do { ++ rval = mci_readl(host, REG_CMDR); ++ } while (time_before(jiffies, expire) && (rval & SDXC_START)); ++ ++ if (rval & SDXC_START) { ++ dev_err(mmc_dev(host->mmc), "fatal err update clk timeout\n"); ++ host->ferror = 1; ++ } ++} ++ ++struct sunxi_mmc_clk_dly mmc_clk_dly[MMC_CLK_MOD_NUM] = { ++ { MMC_CLK_400K, 0, 7 }, ++ { MMC_CLK_25M, 0, 5 }, ++ { MMC_CLK_50M, 3, 5 }, ++ { MMC_CLK_50MDDR, 2, 4 }, ++ { MMC_CLK_50MDDR_8BIT, 2, 4 }, ++ { MMC_CLK_100M, 1, 4 }, ++ { MMC_CLK_200M, 1, 4 }, ++}; ++ ++static void sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *smc_host, ++ unsigned int rate) ++{ ++ u32 newrate; ++ u32 src_clk; ++ u32 oclk_dly; ++ u32 sclk_dly; ++ u32 temp; ++ struct sunxi_mmc_clk_dly *dly = NULL; ++ struct clk_hw *hw = __clk_get_hw(smc_host->clk_mod); ++ ++ newrate = clk_round_rate(smc_host->clk_mod, rate); ++ if (smc_host->clk_mod_rate == newrate) { ++ dev_dbg(mmc_dev(smc_host->mmc), "clk already %d, rounded %d\n", ++ rate, newrate); ++ return; ++ } ++ ++ dev_dbg(mmc_dev(smc_host->mmc), "setting clk to %d, rounded %d\n", ++ rate, newrate); ++ ++ /* setting clock rate */ ++ clk_disable(smc_host->clk_mod); ++ clk_set_rate(smc_host->clk_mod, newrate); ++ clk_enable(smc_host->clk_mod); ++ smc_host->clk_mod_rate = newrate = clk_get_rate(smc_host->clk_mod); ++ dev_dbg(mmc_dev(smc_host->mmc), "clk is now %d\n", newrate); ++ ++ sunxi_mmc_oclk_onoff(smc_host, 0); ++ /* clear internal divider */ ++ temp = mci_readl(smc_host, REG_CLKCR); ++ temp &= ~0xff; ++ mci_writel(smc_host, REG_CLKCR, temp); ++ ++ /* determine delays */ ++ if (rate <= 400000) { ++ dly = &mmc_clk_dly[MMC_CLK_400K]; ++ } else if (rate <= 25000000) { ++ dly = &mmc_clk_dly[MMC_CLK_25M]; ++ } else if (rate <= 50000000) { ++ if (smc_host->ddr) { ++ if (smc_host->bus_width == 8) ++ dly = &mmc_clk_dly[MMC_CLK_50MDDR_8BIT]; ++ else ++ dly = &mmc_clk_dly[MMC_CLK_50MDDR]; ++ } else { ++ dly = &mmc_clk_dly[MMC_CLK_50M]; ++ } ++ } else if (rate <= 104000000) { ++ dly = &mmc_clk_dly[MMC_CLK_100M]; ++ } else if (rate <= 208000000) { ++ dly = &mmc_clk_dly[MMC_CLK_200M]; ++ } else { ++ dly = &mmc_clk_dly[MMC_CLK_50M]; ++ } ++ ++ oclk_dly = dly->oclk_dly; ++ sclk_dly = dly->sclk_dly; ++ ++ src_clk = clk_get_rate(clk_get_parent(smc_host->clk_mod)); ++ ++ if (src_clk >= 300000000 && src_clk <= 400000000) { ++ if (oclk_dly) ++ oclk_dly--; ++ if (sclk_dly) ++ sclk_dly--; ++ } ++ ++ clk_sunxi_mmc_phase_control(hw, sclk_dly, oclk_dly); ++ sunxi_mmc_oclk_onoff(smc_host, 1); ++ ++ /* oclk_onoff sets various irq status bits, clear these */ ++ mci_writel(smc_host, REG_RINTR, ++ mci_readl(smc_host, REG_RINTR) & ~SDXC_SDIO_INTERRUPT); ++} ++ ++static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ struct sunxi_mmc_host *host = mmc_priv(mmc); ++ u32 temp; ++ s32 err; ++ ++ /* Set the power state */ ++ switch (ios->power_mode) { ++ case MMC_POWER_ON: ++ break; ++ ++ case MMC_POWER_UP: ++ if (!IS_ERR(host->vmmc)) { ++ mmc_regulator_set_ocr(host->mmc, host->vmmc, ios->vdd); ++ udelay(200); ++ } ++ ++ err = sunxi_mmc_init_host(mmc); ++ if (err) { ++ host->ferror = 1; ++ return; ++ } ++ enable_irq(host->irq); ++ ++ dev_dbg(mmc_dev(host->mmc), "power on!\n"); ++ host->ferror = 0; ++ break; ++ ++ case MMC_POWER_OFF: ++ dev_dbg(mmc_dev(host->mmc), "power off!\n"); ++ disable_irq(host->irq); ++ sunxi_mmc_exit_host(host); ++ if (!IS_ERR(host->vmmc)) ++ mmc_regulator_set_ocr(host->mmc, host->vmmc, 0); ++ host->ferror = 0; ++ break; ++ } ++ ++ /* set bus width */ ++ switch (ios->bus_width) { ++ case MMC_BUS_WIDTH_1: ++ mci_writel(host, REG_WIDTH, SDXC_WIDTH1); ++ host->bus_width = 1; ++ break; ++ case MMC_BUS_WIDTH_4: ++ mci_writel(host, REG_WIDTH, SDXC_WIDTH4); ++ host->bus_width = 4; ++ break; ++ case MMC_BUS_WIDTH_8: ++ mci_writel(host, REG_WIDTH, SDXC_WIDTH8); ++ host->bus_width = 8; ++ break; ++ } ++ ++ /* set ddr mode */ ++ temp = mci_readl(host, REG_GCTRL); ++ if (ios->timing == MMC_TIMING_UHS_DDR50) { ++ temp |= SDXC_DDR_MODE; ++ host->ddr = 1; ++ } else { ++ temp &= ~SDXC_DDR_MODE; ++ host->ddr = 0; ++ } ++ mci_writel(host, REG_GCTRL, temp); ++ ++ /* set up clock */ ++ if (ios->clock && ios->power_mode) { ++ dev_dbg(mmc_dev(host->mmc), "ios->clock: %d\n", ios->clock); ++ sunxi_mmc_clk_set_rate(host, ios->clock); ++ usleep_range(50000, 55000); ++ } ++} ++ ++static void sunxi_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) ++{ ++ struct sunxi_mmc_host *smc_host = mmc_priv(mmc); ++ unsigned long flags; ++ u32 imask; ++ ++ spin_lock_irqsave(&smc_host->lock, flags); ++ ++ imask = mci_readl(smc_host, REG_IMASK); ++ if (enable) { ++ smc_host->sdio_imask = SDXC_SDIO_INTERRUPT; ++ imask |= SDXC_SDIO_INTERRUPT; ++ } else { ++ smc_host->sdio_imask = 0; ++ imask &= ~SDXC_SDIO_INTERRUPT; ++ } ++ mci_writel(smc_host, REG_IMASK, imask); ++ spin_unlock_irqrestore(&smc_host->lock, flags); ++} ++ ++static void sunxi_mmc_hw_reset(struct mmc_host *mmc) ++{ ++ struct sunxi_mmc_host *smc_host = mmc_priv(mmc); ++ mci_writel(smc_host, REG_HWRST, 0); ++ udelay(10); ++ mci_writel(smc_host, REG_HWRST, 1); ++ udelay(300); ++} ++ ++static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct sunxi_mmc_host *host = mmc_priv(mmc); ++ struct mmc_command *cmd = mrq->cmd; ++ struct mmc_data *data = mrq->data; ++ unsigned long iflags; ++ u32 imask = SDXC_INTERRUPT_ERROR_BIT; ++ u32 cmd_val = SDXC_START | (cmd->opcode & 0x3f); ++ u32 byte_cnt = 0; ++ int ret; ++ ++ if (!mmc_gpio_get_cd(mmc) || host->ferror) { ++ dev_dbg(mmc_dev(host->mmc), "no medium present\n"); ++ mrq->cmd->error = -ENOMEDIUM; ++ mmc_request_done(mmc, mrq); ++ return; ++ } ++ ++ if (data) { ++ byte_cnt = data->blksz * data->blocks; ++ mci_writel(host, REG_BLKSZ, data->blksz); ++ mci_writel(host, REG_BCNTR, byte_cnt); ++ ret = sunxi_mmc_prepare_dma(host, data); ++ if (ret < 0) { ++ dev_err(mmc_dev(host->mmc), "prepare DMA failed\n"); ++ cmd->error = ret; ++ cmd->data->error = ret; ++ mmc_request_done(host->mmc, mrq); ++ return; ++ } ++ } ++ ++ if (cmd->opcode == MMC_GO_IDLE_STATE) { ++ cmd_val |= SDXC_SEND_INIT_SEQUENCE; ++ imask |= SDXC_COMMAND_DONE; ++ } ++ ++ if (cmd->opcode == SD_SWITCH_VOLTAGE) { ++ cmd_val |= SDXC_VOLTAGE_SWITCH; ++ imask |= SDXC_VOLTAGE_CHANGE_DONE; ++ host->voltage_switching = 1; ++ sunxi_mmc_oclk_onoff(host, 1); ++ } ++ ++ if (cmd->flags & MMC_RSP_PRESENT) { ++ cmd_val |= SDXC_RESP_EXPIRE; ++ if (cmd->flags & MMC_RSP_136) ++ cmd_val |= SDXC_LONG_RESPONSE; ++ if (cmd->flags & MMC_RSP_CRC) ++ cmd_val |= SDXC_CHECK_RESPONSE_CRC; ++ ++ if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC) { ++ cmd_val |= SDXC_DATA_EXPIRE | SDXC_WAIT_PRE_OVER; ++ if (cmd->data->flags & MMC_DATA_STREAM) { ++ imask |= SDXC_AUTO_COMMAND_DONE; ++ cmd_val |= SDXC_SEQUENCE_MODE | SDXC_SEND_AUTO_STOP; ++ } ++ if (cmd->data->stop) { ++ imask |= SDXC_AUTO_COMMAND_DONE; ++ cmd_val |= SDXC_SEND_AUTO_STOP; ++ } else ++ imask |= SDXC_DATA_OVER; ++ ++ if (cmd->data->flags & MMC_DATA_WRITE) ++ cmd_val |= SDXC_WRITE; ++ else ++ host->wait_dma = 1; ++ } else ++ imask |= SDXC_COMMAND_DONE; ++ } else ++ imask |= SDXC_COMMAND_DONE; ++ ++ dev_dbg(mmc_dev(host->mmc), "cmd %d(%08x) arg %x ie 0x%08x len %d\n", ++ cmd_val & 0x3f, cmd_val, cmd->arg, imask, ++ mrq->data ? mrq->data->blksz * mrq->data->blocks : 0); ++ ++ spin_lock_irqsave(&host->lock, iflags); ++ host->mrq = mrq; ++ mci_writel(host, REG_IMASK, host->sdio_imask | imask); ++ spin_unlock_irqrestore(&host->lock, iflags); ++ ++ mci_writel(host, REG_CARG, cmd->arg); ++ mci_writel(host, REG_CMDR, cmd_val); ++} ++ ++static const struct of_device_id sunxi_mmc_of_match[] = { ++ { .compatible = "allwinner,sun4i-a10-mmc", }, ++ { .compatible = "allwinner,sun5i-a13-mmc", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match); ++ ++static struct mmc_host_ops sunxi_mmc_ops = { ++ .request = sunxi_mmc_request, ++ .set_ios = sunxi_mmc_set_ios, ++ .get_ro = mmc_gpio_get_ro, ++ .get_cd = mmc_gpio_get_cd, ++ .enable_sdio_irq = sunxi_mmc_enable_sdio_irq, ++ .hw_reset = sunxi_mmc_hw_reset, ++}; ++ ++static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host, ++ struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ int ret; ++ ++ if (of_device_is_compatible(np, "allwinner,sun4i-a10-mmc")) ++ host->idma_des_size_bits = 13; ++ else ++ host->idma_des_size_bits = 16; ++ ++ host->vmmc = devm_regulator_get_optional(&pdev->dev, "vmmc"); ++ if (IS_ERR(host->vmmc) && PTR_ERR(host->vmmc) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ ++ host->reg_base = devm_ioremap_resource(&pdev->dev, ++ platform_get_resource(pdev, IORESOURCE_MEM, 0)); ++ if (IS_ERR(host->reg_base)) ++ return PTR_ERR(host->reg_base); ++ ++ host->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); ++ if (IS_ERR(host->clk_ahb)) { ++ dev_err(&pdev->dev, "Could not get ahb clock\n"); ++ return PTR_ERR(host->clk_ahb); ++ } ++ ++ host->clk_mod = devm_clk_get(&pdev->dev, "mod"); ++ if (IS_ERR(host->clk_mod)) { ++ dev_err(&pdev->dev, "Could not get mod clock\n"); ++ return PTR_ERR(host->clk_mod); ++ } ++ ++ /* Make sure the controller is in a sane state before enabling irqs */ ++ ret = sunxi_mmc_init_host(host->mmc); ++ if (ret) ++ return ret; ++ ++ host->irq = platform_get_irq(pdev, 0); ++ ret = devm_request_irq(&pdev->dev, host->irq, sunxi_mmc_irq, 0, ++ "sunxi-mmc", host); ++ if (ret == 0) ++ disable_irq(host->irq); ++ ++ /* And put it back in reset */ ++ sunxi_mmc_exit_host(host); ++ ++ return ret; ++} ++ ++static int sunxi_mmc_probe(struct platform_device *pdev) ++{ ++ struct sunxi_mmc_host *host; ++ struct mmc_host *mmc; ++ int ret; ++ ++ mmc = mmc_alloc_host(sizeof(struct sunxi_mmc_host), &pdev->dev); ++ if (!mmc) { ++ dev_err(&pdev->dev, "mmc alloc host failed\n"); ++ return -ENOMEM; ++ } ++ ++ host = mmc_priv(mmc); ++ host->mmc = mmc; ++ spin_lock_init(&host->lock); ++ tasklet_init(&host->tasklet, sunxi_mmc_tasklet, (unsigned long)host); ++ ++ ret = sunxi_mmc_resource_request(host, pdev); ++ if (ret) ++ goto error_free_host; ++ ++ host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, ++ &host->sg_dma, GFP_KERNEL); ++ if (!host->sg_cpu) { ++ dev_err(&pdev->dev, "Failed to allocate DMA descriptor mem\n"); ++ ret = -ENOMEM; ++ goto error_free_host; ++ } ++ ++ mmc->ops = &sunxi_mmc_ops; ++ mmc->max_blk_count = 8192; ++ mmc->max_blk_size = 4096; ++ mmc->max_segs = PAGE_SIZE / sizeof(struct sunxi_idma_des); ++ mmc->max_seg_size = (1 << host->idma_des_size_bits); ++ mmc->max_req_size = mmc->max_seg_size * mmc->max_segs; ++ /* 400kHz ~ 50MHz */ ++ mmc->f_min = 400000; ++ mmc->f_max = 50000000; ++ /* available voltages */ ++ if (!IS_ERR(host->vmmc)) ++ mmc->ocr_avail = mmc_regulator_get_ocrmask(host->vmmc); ++ else ++ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; ++ ++ mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | ++ MMC_CAP_SDIO_IRQ; ++ mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP; ++ ++ ret = mmc_of_parse(mmc); ++ if (ret) ++ goto error_free_dma; ++ ++ ret = mmc_add_host(mmc); ++ if (ret) ++ goto error_free_dma; ++ ++ dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq); ++ platform_set_drvdata(pdev, mmc); ++ return 0; ++ ++error_free_dma: ++ dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); ++error_free_host: ++ mmc_free_host(mmc); ++ return ret; ++} ++ ++static int sunxi_mmc_remove(struct platform_device *pdev) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(pdev); ++ struct sunxi_mmc_host *host = mmc_priv(mmc); ++ ++ mmc_remove_host(mmc); ++ sunxi_mmc_exit_host(host); ++ tasklet_disable(&host->tasklet); ++ dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); ++ mmc_free_host(mmc); ++ ++ return 0; ++} ++ ++static struct platform_driver sunxi_mmc_driver = { ++ .driver = { ++ .name = "sunxi-mmc", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(sunxi_mmc_of_match), ++ }, ++ .probe = sunxi_mmc_probe, ++ .remove = sunxi_mmc_remove, ++}; ++module_platform_driver(sunxi_mmc_driver); ++ ++MODULE_DESCRIPTION("Allwinner's SD/MMC Card Controller Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("David Lanzendörfer <david.lanzendoerfer@o2s.ch>"); ++MODULE_ALIAS("platform:sunxi-mmc"); +diff --git a/drivers/mmc/host/sunxi-mmc.h b/drivers/mmc/host/sunxi-mmc.h +new file mode 100644 +index 0000000..84bab99 +--- /dev/null ++++ b/drivers/mmc/host/sunxi-mmc.h +@@ -0,0 +1,238 @@ ++/* ++ * Driver for sunxi SD/MMC host controllers ++ * (C) Copyright 2007-2011 Reuuimlla Technology Co., Ltd. ++ * (C) Copyright 2007-2011 Aaron Maoye <leafy.myeh@reuuimllatech.com> ++ * (C) Copyright 2013-2014 O2S GmbH <www.o2s.ch> ++ * (C) Copyright 2013-2014 David Lanzendörfer <david.lanzendoerfer@o2s.ch> ++ * (C) Copyright 2013-2014 Hans de Goede <hdegoede@redhat.com> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ */ ++ ++#ifndef __SUNXI_MMC_H__ ++#define __SUNXI_MMC_H__ ++ ++/* register offset definitions */ ++#define SDXC_REG_GCTRL (0x00) /* SMC Global Control Register */ ++#define SDXC_REG_CLKCR (0x04) /* SMC Clock Control Register */ ++#define SDXC_REG_TMOUT (0x08) /* SMC Time Out Register */ ++#define SDXC_REG_WIDTH (0x0C) /* SMC Bus Width Register */ ++#define SDXC_REG_BLKSZ (0x10) /* SMC Block Size Register */ ++#define SDXC_REG_BCNTR (0x14) /* SMC Byte Count Register */ ++#define SDXC_REG_CMDR (0x18) /* SMC Command Register */ ++#define SDXC_REG_CARG (0x1C) /* SMC Argument Register */ ++#define SDXC_REG_RESP0 (0x20) /* SMC Response Register 0 */ ++#define SDXC_REG_RESP1 (0x24) /* SMC Response Register 1 */ ++#define SDXC_REG_RESP2 (0x28) /* SMC Response Register 2 */ ++#define SDXC_REG_RESP3 (0x2C) /* SMC Response Register 3 */ ++#define SDXC_REG_IMASK (0x30) /* SMC Interrupt Mask Register */ ++#define SDXC_REG_MISTA (0x34) /* SMC Masked Interrupt Status Register */ ++#define SDXC_REG_RINTR (0x38) /* SMC Raw Interrupt Status Register */ ++#define SDXC_REG_STAS (0x3C) /* SMC Status Register */ ++#define SDXC_REG_FTRGL (0x40) /* SMC FIFO Threshold Watermark Registe */ ++#define SDXC_REG_FUNS (0x44) /* SMC Function Select Register */ ++#define SDXC_REG_CBCR (0x48) /* SMC CIU Byte Count Register */ ++#define SDXC_REG_BBCR (0x4C) /* SMC BIU Byte Count Register */ ++#define SDXC_REG_DBGC (0x50) /* SMC Debug Enable Register */ ++#define SDXC_REG_HWRST (0x78) /* SMC Card Hardware Reset for Register */ ++#define SDXC_REG_DMAC (0x80) /* SMC IDMAC Control Register */ ++#define SDXC_REG_DLBA (0x84) /* SMC IDMAC Descriptor List Base Addre */ ++#define SDXC_REG_IDST (0x88) /* SMC IDMAC Status Register */ ++#define SDXC_REG_IDIE (0x8C) /* SMC IDMAC Interrupt Enable Register */ ++#define SDXC_REG_CHDA (0x90) ++#define SDXC_REG_CBDA (0x94) ++ ++#define mci_readl(host, reg) \ ++ readl((host)->reg_base + SDXC_##reg) ++#define mci_writel(host, reg, value) \ ++ writel((value), (host)->reg_base + SDXC_##reg) ++ ++/* global control register bits */ ++#define SDXC_SOFT_RESET BIT(0) ++#define SDXC_FIFO_RESET BIT(1) ++#define SDXC_DMA_RESET BIT(2) ++#define SDXC_HARDWARE_RESET (SDXC_SOFT_RESET|SDXC_FIFO_RESET|SDXC_DMA_RESET) ++#define SDXC_INTERRUPT_ENABLE_BIT BIT(4) ++#define SDXC_DMA_ENABLE_BIT BIT(5) ++#define SDXC_DEBOUNCE_ENABLE_BIT BIT(8) ++#define SDXC_POSEDGE_LATCH_DATA BIT(9) ++#define SDXC_DDR_MODE BIT(10) ++#define SDXC_MEMORY_ACCESS_DONE BIT(29) ++#define SDXC_ACCESS_DONE_DIRECT BIT(30) ++#define SDXC_ACCESS_BY_AHB BIT(31) ++#define SDXC_ACCESS_BY_DMA (0U << 31) ++/* clock control bits */ ++#define SDXC_CARD_CLOCK_ON BIT(16) ++#define SDXC_LOW_POWER_ON BIT(17) ++/* bus width */ ++#define SDXC_WIDTH1 (0) ++#define SDXC_WIDTH4 (1) ++#define SDXC_WIDTH8 (2) ++/* smc command bits */ ++#define SDXC_RESP_EXPIRE BIT(6) ++#define SDXC_LONG_RESPONSE BIT(7) ++#define SDXC_CHECK_RESPONSE_CRC BIT(8) ++#define SDXC_DATA_EXPIRE BIT(9) ++#define SDXC_WRITE BIT(10) ++#define SDXC_SEQUENCE_MODE BIT(11) ++#define SDXC_SEND_AUTO_STOP BIT(12) ++#define SDXC_WAIT_PRE_OVER BIT(13) ++#define SDXC_STOP_ABORT_CMD BIT(14) ++#define SDXC_SEND_INIT_SEQUENCE BIT(15) ++#define SDXC_UPCLK_ONLY BIT(21) ++#define SDXC_READ_CEATA_DEV BIT(22) ++#define SDXC_CCS_EXPIRE BIT(23) ++#define SDXC_ENABLE_BIT_BOOT BIT(24) ++#define SDXC_ALT_BOOT_OPTIONS BIT(25) ++#define SDXC_BOOT_ACK_EXPIRE BIT(26) ++#define SDXC_BOOT_ABORT BIT(27) ++#define SDXC_VOLTAGE_SWITCH BIT(28) ++#define SDXC_USE_HOLD_REGISTER BIT(29) ++#define SDXC_START BIT(31) ++/* interrupt bits */ ++#define SDXC_RESP_ERROR BIT(1) ++#define SDXC_COMMAND_DONE BIT(2) ++#define SDXC_DATA_OVER BIT(3) ++#define SDXC_TX_DATA_REQUEST BIT(4) ++#define SDXC_RX_DATA_REQUEST BIT(5) ++#define SDXC_RESP_CRC_ERROR BIT(6) ++#define SDXC_DATA_CRC_ERROR BIT(7) ++#define SDXC_RESP_TIMEOUT BIT(8) ++#define SDXC_DATA_TIMEOUT BIT(9) ++#define SDXC_VOLTAGE_CHANGE_DONE BIT(10) ++#define SDXC_FIFO_RUN_ERROR BIT(11) ++#define SDXC_HARD_WARE_LOCKED BIT(12) ++#define SDXC_START_BIT_ERROR BIT(13) ++#define SDXC_AUTO_COMMAND_DONE BIT(14) ++#define SDXC_END_BIT_ERROR BIT(15) ++#define SDXC_SDIO_INTERRUPT BIT(16) ++#define SDXC_CARD_INSERT BIT(30) ++#define SDXC_CARD_REMOVE BIT(31) ++#define SDXC_INTERRUPT_ERROR_BIT (SDXC_RESP_ERROR | SDXC_RESP_CRC_ERROR | \ ++ SDXC_DATA_CRC_ERROR | SDXC_RESP_TIMEOUT | \ ++ SDXC_DATA_TIMEOUT | SDXC_FIFO_RUN_ERROR | \ ++ SDXC_HARD_WARE_LOCKED | SDXC_START_BIT_ERROR | \ ++ SDXC_END_BIT_ERROR) /* 0xbbc2 */ ++#define SDXC_INTERRUPT_DONE_BIT (SDXC_AUTO_COMMAND_DONE | SDXC_DATA_OVER | \ ++ SDXC_COMMAND_DONE | SDXC_VOLTAGE_CHANGE_DONE) ++/* status */ ++#define SDXC_RXWL_FLAG BIT(0) ++#define SDXC_TXWL_FLAG BIT(1) ++#define SDXC_FIFO_EMPTY BIT(2) ++#define SDXC_FIFO_FULL BIT(3) ++#define SDXC_CARD_PRESENT BIT(8) ++#define SDXC_CARD_DATA_BUSY BIT(9) ++#define SDXC_DATA_FSM_BUSY BIT(10) ++#define SDXC_DMA_REQUEST BIT(31) ++#define SDXC_FIFO_SIZE (16) ++/* Function select */ ++#define SDXC_CEATA_ON (0xceaaU << 16) ++#define SDXC_SEND_IRQ_RESPONSE BIT(0) ++#define SDXC_SDIO_READ_WAIT BIT(1) ++#define SDXC_ABORT_READ_DATA BIT(2) ++#define SDXC_SEND_CCSD BIT(8) ++#define SDXC_SEND_AUTO_STOPCCSD BIT(9) ++#define SDXC_CEATA_DEV_INTERRUPT_ENABLE_BIT BIT(10) ++/* IDMA controller bus mod bit field */ ++#define SDXC_IDMAC_SOFT_RESET BIT(0) ++#define SDXC_IDMAC_FIX_BURST BIT(1) ++#define SDXC_IDMAC_IDMA_ON BIT(7) ++#define SDXC_IDMAC_REFETCH_DES BIT(31) ++/* IDMA status bit field */ ++#define SDXC_IDMAC_TRANSMIT_INTERRUPT BIT(0) ++#define SDXC_IDMAC_RECEIVE_INTERRUPT BIT(1) ++#define SDXC_IDMAC_FATAL_BUS_ERROR BIT(2) ++#define SDXC_IDMAC_DESTINATION_INVALID BIT(4) ++#define SDXC_IDMAC_CARD_ERROR_SUM BIT(5) ++#define SDXC_IDMAC_NORMAL_INTERRUPT_SUM BIT(8) ++#define SDXC_IDMAC_ABNORMAL_INTERRUPT_SUM BIT(9) ++#define SDXC_IDMAC_HOST_ABORT_INTERRUPT_TX BIT(10) ++#define SDXC_IDMAC_HOST_ABORT_INTERRUPT_RX BIT(10) ++#define SDXC_IDMAC_IDLE (0U << 13) ++#define SDXC_IDMAC_SUSPEND (1U << 13) ++#define SDXC_IDMAC_DESC_READ (2U << 13) ++#define SDXC_IDMAC_DESC_CHECK (3U << 13) ++#define SDXC_IDMAC_READ_REQUEST_WAIT (4U << 13) ++#define SDXC_IDMAC_WRITE_REQUEST_WAIT (5U << 13) ++#define SDXC_IDMAC_READ (6U << 13) ++#define SDXC_IDMAC_WRITE (7U << 13) ++#define SDXC_IDMAC_DESC_CLOSE (8U << 13) ++ ++/* ++* If the idma-des-size-bits of property is ie 13, bufsize bits are: ++* Bits 0-12: buf1 size ++* Bits 13-25: buf2 size ++* Bits 26-31: not used ++* Since we only ever set buf1 size, we can simply store it directly. ++*/ ++#define SDXC_IDMAC_DES0_DIC BIT(1) /* disable interrupt on completion */ ++#define SDXC_IDMAC_DES0_LD BIT(2) /* last descriptor */ ++#define SDXC_IDMAC_DES0_FD BIT(3) /* first descriptor */ ++#define SDXC_IDMAC_DES0_CH BIT(4) /* chain mode */ ++#define SDXC_IDMAC_DES0_ER BIT(5) /* end of ring */ ++#define SDXC_IDMAC_DES0_CES BIT(30) /* card error summary */ ++#define SDXC_IDMAC_DES0_OWN BIT(31) /* 1-idma owns it, 0-host owns it */ ++ ++struct sunxi_idma_des { ++ u32 config; ++ u32 buf_size; ++ u32 buf_addr_ptr1; ++ u32 buf_addr_ptr2; ++}; ++ ++struct sunxi_mmc_host { ++ struct mmc_host *mmc; ++ struct regulator *vmmc; ++ ++ /* IO mapping base */ ++ void __iomem *reg_base; ++ ++ spinlock_t lock; ++ struct tasklet_struct tasklet; ++ ++ /* clock management */ ++ struct clk *clk_ahb; ++ struct clk *clk_mod; ++ ++ /* ios information */ ++ u32 clk_mod_rate; ++ u32 bus_width; ++ u32 idma_des_size_bits; ++ u32 ddr; ++ u32 voltage_switching; ++ ++ /* irq */ ++ int irq; ++ u32 int_sum; ++ u32 sdio_imask; ++ ++ /* flags */ ++ u32 power_on:1; ++ u32 wait_dma:1; ++ ++ dma_addr_t sg_dma; ++ void *sg_cpu; ++ ++ struct mmc_request *mrq; ++ u32 ferror; ++}; ++ ++#define MMC_CLK_400K 0 ++#define MMC_CLK_25M 1 ++#define MMC_CLK_50M 2 ++#define MMC_CLK_50MDDR 3 ++#define MMC_CLK_50MDDR_8BIT 4 ++#define MMC_CLK_100M 5 ++#define MMC_CLK_200M 6 ++#define MMC_CLK_MOD_NUM 7 ++ ++struct sunxi_mmc_clk_dly { ++ u32 mode; ++ u32 oclk_dly; ++ u32 sclk_dly; ++}; ++ ++#endif +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/201-dt-sun4i-add-mmc-nodes_NEED_REFRESH.patch b/target/linux/sunxi/patches-3.14/201-dt-sun4i-add-mmc-nodes_NEED_REFRESH.patch new file mode 100644 index 0000000000..a6261cda8e --- /dev/null +++ b/target/linux/sunxi/patches-3.14/201-dt-sun4i-add-mmc-nodes_NEED_REFRESH.patch @@ -0,0 +1,195 @@ +From b66989fe7a41e1093b1f825967ab29963e06cccd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?David=20Lanzend=C3=B6rfer?= <david.lanzendoerfer@o2s.ch> +Date: Sat, 15 Feb 2014 14:02:51 +0100 +Subject: [PATCH] ARM: dts: sun4i: Add support for mmc +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: David Lanzendörfer <david.lanzendoerfer@o2s.ch> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun4i-a10-a1000.dts | 8 +++++ + arch/arm/boot/dts/sun4i-a10-cubieboard.dts | 8 +++++ + arch/arm/boot/dts/sun4i-a10.dtsi | 58 ++++++++++++++++++++++++++++++ + 3 files changed, 74 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10-a1000.dts b/arch/arm/boot/dts/sun4i-a10-a1000.dts +index fa746aea..68b687e 100644 +diff --git a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts +index 4684cbe..13088f0 100644 +--- a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts ++++ b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts +@@ -34,6 +34,14 @@ + }; + }; + ++ mmc0: mmc@01c0f000 { ++ pinctrl-names = "default", "default"; ++ pinctrl-0 = <&mmc0_pins_a>; ++ pinctrl-1 = <&mmc0_cd_pin_reference_design>; ++ cd-gpios = <&pio 7 1 0>; /* PH1 */ ++ status = "okay"; ++ }; ++ + usbphy: phy@01c13400 { + usb1_vbus-supply = <®_usb1_vbus>; + usb2_vbus-supply = <®_usb2_vbus>; +diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi +index 27dc6ee..7014518 100644 +--- a/arch/arm/boot/dts/sun4i-a10.dtsi ++++ b/arch/arm/boot/dts/sun4i-a10.dtsi +@@ -377,6 +377,50 @@ + #size-cells = <0>; + }; + ++ mmc0: mmc@01c0f000 { ++ compatible = "allwinner,sun4i-a10-mmc"; ++ reg = <0x01c0f000 0x1000>; ++ clocks = <&ahb_gates 8>, <&mmc0_clk>; ++ clock-names = "ahb", "mod"; ++ interrupts = <32>; ++ bus-width = <4>; ++ cd-inverted; ++ status = "disabled"; ++ }; ++ ++ mmc1: mmc@01c10000 { ++ compatible = "allwinner,sun4i-a10-mmc"; ++ reg = <0x01c10000 0x1000>; ++ clocks = <&ahb_gates 9>, <&mmc1_clk>; ++ clock-names = "ahb", "mod"; ++ interrupts = <33>; ++ bus-width = <4>; ++ cd-inverted; ++ status = "disabled"; ++ }; ++ ++ mmc2: mmc@01c11000 { ++ compatible = "allwinner,sun4i-a10-mmc"; ++ reg = <0x01c11000 0x1000>; ++ clocks = <&ahb_gates 10>, <&mmc2_clk>; ++ clock-names = "ahb", "mod"; ++ interrupts = <34>; ++ bus-width = <4>; ++ cd-inverted; ++ status = "disabled"; ++ }; ++ ++ mmc3: mmc@01c12000 { ++ compatible = "allwinner,sun4i-a10-mmc"; ++ reg = <0x01c12000 0x1000>; ++ clocks = <&ahb_gates 11>, <&mmc3_clk>; ++ clock-names = "ahb", "mod"; ++ interrupts = <35>; ++ bus-width = <4>; ++ cd-inverted; ++ status = "disabled"; ++ }; ++ + usbphy: phy@01c13400 { + #phy-cells = <1>; + compatible = "allwinner,sun4i-a10-usb-phy"; +@@ -529,6 +573,20 @@ + allwinner,drive = <0>; + allwinner,pull = <0>; + }; ++ ++ mmc0_pins_a: mmc0@0 { ++ allwinner,pins = "PF0","PF1","PF2","PF3","PF4","PF5"; ++ allwinner,function = "mmc0"; ++ allwinner,drive = <2>; ++ allwinner,pull = <0>; ++ }; ++ ++ mmc0_cd_pin_reference_design: mmc0_cd_pin@0 { ++ allwinner,pins = "PH1"; ++ allwinner,function = "gpio_in"; ++ allwinner,drive = <0>; ++ allwinner,pull = <1>; ++ }; + }; + + timer@01c20c00 { +-- +2.0.3 + +From c0b8d688678e4a652895ce5f0cd48917a9a4f6ba Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Sat, 11 Jan 2014 04:33:23 +0100 +Subject: [PATCH] ARM: sun4i: dt: Add mmc node to a few more boards + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun4i-a10-hackberry.dts | 9 +++++++++ + arch/arm/boot/dts/sun4i-a10-inet97fv2.dts | 9 +++++++++ + arch/arm/boot/dts/sun4i-a10-mini-xplus.dts | 8 ++++++++ + arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts | 9 +++++++++ + arch/arm/boot/dts/sun4i-a10-pcduino.dts | 8 ++++++++ + 5 files changed, 43 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10-hackberry.dts b/arch/arm/boot/dts/sun4i-a10-hackberry.dts +index d7c17e4..a4b05d6 100644 +diff --git a/arch/arm/boot/dts/sun4i-a10-inet97fv2.dts b/arch/arm/boot/dts/sun4i-a10-inet97fv2.dts +index fe9272e..b73a070 100644 +diff --git a/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts b/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts +index dd84a9e3..c906171 100644 +--- a/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts ++++ b/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts +@@ -20,6 +20,14 @@ + compatible = "pineriver,mini-xplus", "allwinner,sun4i-a10"; + + soc@01c00000 { ++ mmc0: mmc@01c0f000 { ++ pinctrl-names = "default", "default"; ++ pinctrl-0 = <&mmc0_pins_a>; ++ pinctrl-1 = <&mmc0_cd_pin_reference_design>; ++ cd-gpios = <&pio 7 1 0>; /* PH1 */ ++ status = "okay"; ++ }; ++ + usbphy: phy@01c13400 { + usb1_vbus-supply = <®_usb1_vbus>; + usb2_vbus-supply = <®_usb2_vbus>; +diff --git a/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts b/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts +index 66cf0c7..e5a2765 100644 +--- a/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts ++++ b/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts +@@ -33,6 +33,15 @@ + }; + }; + ++ mmc0: mmc@01c0f000 { ++ pinctrl-names = "default", "default"; ++ pinctrl-0 = <&mmc0_pins_a>; ++ pinctrl-1 = <&mmc0_cd_pin_reference_design>; ++ cd-gpios = <&pio 7 1 0>; /* PH1 */ ++ cd-mode = <1>; ++ status = "okay"; ++ }; ++ + usbphy: phy@01c13400 { + usb1_vbus-supply = <®_usb1_vbus>; + usb2_vbus-supply = <®_usb2_vbus>; +diff --git a/arch/arm/boot/dts/sun4i-a10-pcduino.dts b/arch/arm/boot/dts/sun4i-a10-pcduino.dts +index 255b47e..2820229 100644 +--- a/arch/arm/boot/dts/sun4i-a10-pcduino.dts ++++ b/arch/arm/boot/dts/sun4i-a10-pcduino.dts +@@ -34,6 +34,14 @@ + }; + }; + ++ mmc0: mmc@01c0f000 { ++ pinctrl-names = "default", "default"; ++ pinctrl-0 = <&mmc0_pins_a>; ++ pinctrl-1 = <&mmc0_cd_pin_reference_design>; ++ cd-gpios = <&pio 7 1 0>; /* PH1 */ ++ status = "okay"; ++ }; ++ + usbphy: phy@01c13400 { + usb1_vbus-supply = <®_usb1_vbus>; + usb2_vbus-supply = <®_usb2_vbus>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/202-dt-sun5i-add-mmc-nodes.patch b/target/linux/sunxi/patches-3.14/202-dt-sun5i-add-mmc-nodes.patch new file mode 100644 index 0000000000..1f4b23c24c --- /dev/null +++ b/target/linux/sunxi/patches-3.14/202-dt-sun5i-add-mmc-nodes.patch @@ -0,0 +1,138 @@ +From 7afa5fb704679e84c59e5ad25bbdf7605844c5ca Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?David=20Lanzend=C3=B6rfer?= <david.lanzendoerfer@o2s.ch> +Date: Sat, 15 Feb 2014 14:02:29 +0100 +Subject: [PATCH] ARM: dts: sun5i: Add support for mmc +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: David Lanzendörfer <david.lanzendoerfer@o2s.ch> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts | 30 +++++++++++++++ + arch/arm/boot/dts/sun5i-a10s.dtsi | 47 ++++++++++++++++++++++++ + arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts | 15 ++++++++ + arch/arm/boot/dts/sun5i-a13-olinuxino.dts | 15 ++++++++ + arch/arm/boot/dts/sun5i-a13.dtsi | 29 +++++++++++++++ + 5 files changed, 136 insertions(+) + +diff --git a/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts b/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts +index 23611b7..5bc25c7 100644 +diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi +index f34e0d8..8ba1ed7 100644 +diff --git a/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts b/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts +index 11169d5..700f688 100644 +--- a/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts ++++ b/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts +@@ -21,6 +21,14 @@ + compatible = "olimex,a13-olinuxino-micro", "allwinner,sun5i-a13"; + + soc@01c00000 { ++ mmc0: mmc@01c0f000 { ++ pinctrl-names = "default", "default"; ++ pinctrl-0 = <&mmc0_pins_a>; ++ pinctrl-1 = <&mmc0_cd_pin_olinuxinom>; ++ cd-gpios = <&pio 6 0 0>; /* PG0 */ ++ status = "okay"; ++ }; ++ + usbphy: phy@01c13400 { + usb1_vbus-supply = <®_usb1_vbus>; + status = "okay"; +@@ -35,6 +43,13 @@ + }; + + pinctrl@01c20800 { ++ mmc0_cd_pin_olinuxinom: mmc0_cd_pin@0 { ++ allwinner,pins = "PG0"; ++ allwinner,function = "gpio_in"; ++ allwinner,drive = <0>; ++ allwinner,pull = <1>; ++ }; ++ + led_pins_olinuxinom: led_pins@0 { + allwinner,pins = "PG9"; + allwinner,function = "gpio_out"; +diff --git a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts +index 7a9187b..177196c 100644 +--- a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts ++++ b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts +@@ -20,6 +20,14 @@ + compatible = "olimex,a13-olinuxino", "allwinner,sun5i-a13"; + + soc@01c00000 { ++ mmc0: mmc@01c0f000 { ++ pinctrl-names = "default", "default"; ++ pinctrl-0 = <&mmc0_pins_a>; ++ pinctrl-1 = <&mmc0_cd_pin_olinuxino>; ++ cd-gpios = <&pio 6 0 0>; /* PG0 */ ++ status = "okay"; ++ }; ++ + usbphy: phy@01c13400 { + usb1_vbus-supply = <®_usb1_vbus>; + status = "okay"; +@@ -34,6 +42,13 @@ + }; + + pinctrl@01c20800 { ++ mmc0_cd_pin_olinuxino: mmc0_cd_pin@0 { ++ allwinner,pins = "PG0"; ++ allwinner,function = "gpio_in"; ++ allwinner,drive = <0>; ++ allwinner,pull = <1>; ++ }; ++ + led_pins_olinuxino: led_pins@0 { + allwinner,pins = "PG9"; + allwinner,function = "gpio_out"; +diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi +index 0e9c239..6fc84a4 100644 +--- a/arch/arm/boot/dts/sun5i-a13.dtsi ++++ b/arch/arm/boot/dts/sun5i-a13.dtsi +@@ -320,6 +320,28 @@ + #size-cells = <0>; + }; + ++ mmc0: mmc@01c0f000 { ++ compatible = "allwinner,sun5i-a13-mmc"; ++ reg = <0x01c0f000 0x1000>; ++ clocks = <&ahb_gates 8>, <&mmc0_clk>; ++ clock-names = "ahb", "mod"; ++ interrupts = <32>; ++ bus-width = <4>; ++ cd-inverted; ++ status = "disabled"; ++ }; ++ ++ mmc2: mmc@01c11000 { ++ compatible = "allwinner,sun5i-a13-mmc"; ++ reg = <0x01c11000 0x1000>; ++ clocks = <&ahb_gates 10>, <&mmc2_clk>; ++ clock-names = "ahb", "mod"; ++ interrupts = <34>; ++ bus-width = <4>; ++ cd-inverted; ++ status = "disabled"; ++ }; ++ + usbphy: phy@01c13400 { + #phy-cells = <1>; + compatible = "allwinner,sun5i-a13-usb-phy"; +@@ -415,6 +437,13 @@ + allwinner,drive = <0>; + allwinner,pull = <0>; + }; ++ ++ mmc0_pins_a: mmc0@0 { ++ allwinner,pins = "PF0","PF1","PF2","PF3","PF4","PF5"; ++ allwinner,function = "mmc0"; ++ allwinner,drive = <2>; ++ allwinner,pull = <0>; ++ }; + }; + + timer@01c20c00 { +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/203-dt-sun7i-add-mmc-nodes.patch b/target/linux/sunxi/patches-3.14/203-dt-sun7i-add-mmc-nodes.patch new file mode 100644 index 0000000000..daa6a91121 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/203-dt-sun7i-add-mmc-nodes.patch @@ -0,0 +1,182 @@ +From 33654facee61ebbd88684c9cf482ec2ea41f575e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?David=20Lanzend=C3=B6rfer?= <david.lanzendoerfer@o2s.ch> +Date: Sat, 15 Feb 2014 14:02:01 +0100 +Subject: [PATCH] ARM: dts: sun7i: Add support for mmc +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: David Lanzendörfer <david.lanzendoerfer@o2s.ch> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun7i-a20-cubieboard2.dts | 8 +++ + arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 8 +++ + arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts | 23 +++++++++ + arch/arm/boot/dts/sun7i-a20.dtsi | 65 +++++++++++++++++++++++++ + 4 files changed, 104 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts +index 68de89f..cd9d3c2 100644 +--- a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts ++++ b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts +@@ -20,6 +20,14 @@ + compatible = "cubietech,cubieboard2", "allwinner,sun7i-a20"; + + soc@01c00000 { ++ mmc0: mmc@01c0f000 { ++ pinctrl-names = "default", "default"; ++ pinctrl-0 = <&mmc0_pins_a>; ++ pinctrl-1 = <&mmc0_cd_pin_reference_design>; ++ cd-gpios = <&pio 7 1 0>; /* PH1 */ ++ status = "okay"; ++ }; ++ + usbphy: phy@01c13400 { + usb1_vbus-supply = <®_usb1_vbus>; + usb2_vbus-supply = <®_usb2_vbus>; +diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +index cb25d3c..66bb3ef 100644 +--- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts ++++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +@@ -20,6 +20,14 @@ + compatible = "cubietech,cubietruck", "allwinner,sun7i-a20"; + + soc@01c00000 { ++ mmc0: mmc@01c0f000 { ++ pinctrl-names = "default", "default"; ++ pinctrl-0 = <&mmc0_pins_a>; ++ pinctrl-1 = <&mmc0_cd_pin_reference_design>; ++ cd-gpios = <&pio 7 1 0>; /* PH1 */ ++ status = "okay"; ++ }; ++ + usbphy: phy@01c13400 { + usb1_vbus-supply = <®_usb1_vbus>; + usb2_vbus-supply = <®_usb2_vbus>; +diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts +index eeadf76..822cbe2 100644 +--- a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts ++++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts +@@ -31,6 +31,22 @@ + status = "okay"; + }; + ++ mmc0: mmc@01c0f000 { ++ pinctrl-names = "default", "default"; ++ pinctrl-0 = <&mmc0_pins_a>; ++ pinctrl-1 = <&mmc0_cd_pin_reference_design>; ++ cd-gpios = <&pio 7 1 0>; /* PH1 */ ++ status = "okay"; ++ }; ++ ++ mmc3: mmc@01c12000 { ++ pinctrl-names = "default", "default"; ++ pinctrl-0 = <&mmc3_pins_a>; ++ pinctrl-1 = <&mmc3_cd_pin_olinuxinom>; ++ cd-gpios = <&pio 7 11 0>; /* PH11 */ ++ status = "okay"; ++ }; ++ + usbphy: phy@01c13400 { + usb1_vbus-supply = <®_usb1_vbus>; + usb2_vbus-supply = <®_usb2_vbus>; +@@ -65,6 +81,13 @@ + }; + + pinctrl@01c20800 { ++ mmc3_cd_pin_olinuxinom: mmc3_cd_pin@0 { ++ allwinner,pins = "PH11"; ++ allwinner,function = "gpio_in"; ++ allwinner,drive = <0>; ++ allwinner,pull = <1>; ++ }; ++ + led_pins_olinuxino: led_pins@0 { + allwinner,pins = "PH2"; + allwinner,function = "gpio_out"; +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 4cc2f5f..1d9b314 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -439,6 +439,50 @@ + #size-cells = <0>; + }; + ++ mmc0: mmc@01c0f000 { ++ compatible = "allwinner,sun5i-a13-mmc"; ++ reg = <0x01c0f000 0x1000>; ++ clocks = <&ahb_gates 8>, <&mmc0_clk>; ++ clock-names = "ahb", "mod"; ++ interrupts = <0 32 4>; ++ bus-width = <4>; ++ cd-inverted; ++ status = "disabled"; ++ }; ++ ++ mmc1: mmc@01c10000 { ++ compatible = "allwinner,sun5i-a13-mmc"; ++ reg = <0x01c10000 0x1000>; ++ clocks = <&ahb_gates 9>, <&mmc1_clk>; ++ clock-names = "ahb", "mod"; ++ interrupts = <0 33 4>; ++ bus-width = <4>; ++ cd-inverted; ++ status = "disabled"; ++ }; ++ ++ mmc2: mmc@01c11000 { ++ compatible = "allwinner,sun5i-a13-mmc"; ++ reg = <0x01c11000 0x1000>; ++ clocks = <&ahb_gates 10>, <&mmc2_clk>; ++ clock-names = "ahb", "mod"; ++ interrupts = <0 34 4>; ++ bus-width = <4>; ++ cd-inverted; ++ status = "disabled"; ++ }; ++ ++ mmc3: mmc@01c12000 { ++ compatible = "allwinner,sun5i-a13-mmc"; ++ reg = <0x01c12000 0x1000>; ++ clocks = <&ahb_gates 11>, <&mmc3_clk>; ++ clock-names = "ahb", "mod"; ++ interrupts = <0 35 4>; ++ bus-width = <4>; ++ cd-inverted; ++ status = "disabled"; ++ }; ++ + usbphy: phy@01c13400 { + #phy-cells = <1>; + compatible = "allwinner,sun7i-a20-usb-phy"; +@@ -645,6 +689,27 @@ + allwinner,drive = <0>; + allwinner,pull = <0>; + }; ++ ++ mmc0_pins_a: mmc0@0 { ++ allwinner,pins = "PF0","PF1","PF2","PF3","PF4","PF5"; ++ allwinner,function = "mmc0"; ++ allwinner,drive = <2>; ++ allwinner,pull = <0>; ++ }; ++ ++ mmc0_cd_pin_reference_design: mmc0_cd_pin@0 { ++ allwinner,pins = "PH1"; ++ allwinner,function = "gpio_in"; ++ allwinner,drive = <0>; ++ allwinner,pull = <1>; ++ }; ++ ++ mmc3_pins_a: mmc3@0 { ++ allwinner,pins = "PI4","PI5","PI6","PI7","PI8","PI9"; ++ allwinner,function = "mmc3"; ++ allwinner,drive = <2>; ++ allwinner,pull = <0>; ++ }; + }; + + timer@01c20c00 { +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/205-nmi-add-driver.patch b/target/linux/sunxi/patches-3.14/205-nmi-add-driver.patch new file mode 100644 index 0000000000..2e23f94998 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/205-nmi-add-driver.patch @@ -0,0 +1,247 @@ +From b9ad0253e6c68ac3d37fd2ed8ed9bf8a334e4b65 Mon Sep 17 00:00:00 2001 +From: Carlo Caione <carlo@caione.org> +Date: Sat, 15 Mar 2014 14:40:59 +0100 +Subject: [PATCH] ARM: sun7i/sun6i: irqchip: Add irqchip driver for NMI + controller + +Allwinner A20/A31 SoCs have special registers to control / (un)mask / +acknowledge NMI. This NMI controller is separated and independent from GIC. +This patch adds a new irqchip to manage NMI. + +Signed-off-by: Carlo Caione <carlo@caione.org> +Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + drivers/irqchip/Makefile | 1 + + drivers/irqchip/irq-sunxi-nmi.c | 208 ++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 209 insertions(+) + create mode 100644 drivers/irqchip/irq-sunxi-nmi.c + +diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile +index 5194afb..1c0c151 100644 +--- a/drivers/irqchip/Makefile ++++ b/drivers/irqchip/Makefile +@@ -12,6 +12,7 @@ obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o + obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o + obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o + obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o ++obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o + obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o + obj-$(CONFIG_ARM_GIC) += irq-gic.o + obj-$(CONFIG_ARM_NVIC) += irq-nvic.o +diff --git a/drivers/irqchip/irq-sunxi-nmi.c b/drivers/irqchip/irq-sunxi-nmi.c +new file mode 100644 +index 0000000..12f547a +--- /dev/null ++++ b/drivers/irqchip/irq-sunxi-nmi.c +@@ -0,0 +1,208 @@ ++/* ++ * Allwinner A20/A31 SoCs NMI IRQ chip driver. ++ * ++ * Carlo Caione <carlo.caione@gmail.com> ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++ ++#include <linux/bitops.h> ++#include <linux/device.h> ++#include <linux/io.h> ++#include <linux/irq.h> ++#include <linux/interrupt.h> ++#include <linux/irqdomain.h> ++#include <linux/of_irq.h> ++#include <linux/of_address.h> ++#include <linux/of_platform.h> ++#include <linux/irqchip/chained_irq.h> ++#include "irqchip.h" ++ ++#define SUNXI_NMI_SRC_TYPE_MASK 0x00000003 ++ ++enum { ++ SUNXI_SRC_TYPE_LEVEL_LOW = 0, ++ SUNXI_SRC_TYPE_EDGE_FALLING, ++ SUNXI_SRC_TYPE_LEVEL_HIGH, ++ SUNXI_SRC_TYPE_EDGE_RISING, ++}; ++ ++struct sunxi_sc_nmi_reg_offs { ++ u32 ctrl; ++ u32 pend; ++ u32 enable; ++}; ++ ++static struct sunxi_sc_nmi_reg_offs sun7i_reg_offs = { ++ .ctrl = 0x00, ++ .pend = 0x04, ++ .enable = 0x08, ++}; ++ ++static struct sunxi_sc_nmi_reg_offs sun6i_reg_offs = { ++ .ctrl = 0x00, ++ .pend = 0x04, ++ .enable = 0x34, ++}; ++ ++static inline void sunxi_sc_nmi_write(struct irq_chip_generic *gc, u32 off, ++ u32 val) ++{ ++ irq_reg_writel(val, gc->reg_base + off); ++} ++ ++static inline u32 sunxi_sc_nmi_read(struct irq_chip_generic *gc, u32 off) ++{ ++ return irq_reg_readl(gc->reg_base + off); ++} ++ ++static void sunxi_sc_nmi_handle_irq(unsigned int irq, struct irq_desc *desc) ++{ ++ struct irq_domain *domain = irq_desc_get_handler_data(desc); ++ struct irq_chip *chip = irq_get_chip(irq); ++ unsigned int virq = irq_find_mapping(domain, 0); ++ ++ chained_irq_enter(chip, desc); ++ generic_handle_irq(virq); ++ chained_irq_exit(chip, desc); ++} ++ ++static int sunxi_sc_nmi_set_type(struct irq_data *data, unsigned int flow_type) ++{ ++ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); ++ struct irq_chip_type *ct = gc->chip_types; ++ u32 src_type_reg; ++ u32 ctrl_off = ct->regs.type; ++ unsigned int src_type; ++ unsigned int i; ++ ++ irq_gc_lock(gc); ++ ++ switch (flow_type & IRQF_TRIGGER_MASK) { ++ case IRQ_TYPE_EDGE_FALLING: ++ src_type = SUNXI_SRC_TYPE_EDGE_FALLING; ++ break; ++ case IRQ_TYPE_EDGE_RISING: ++ src_type = SUNXI_SRC_TYPE_EDGE_RISING; ++ break; ++ case IRQ_TYPE_LEVEL_HIGH: ++ src_type = SUNXI_SRC_TYPE_LEVEL_HIGH; ++ break; ++ case IRQ_TYPE_NONE: ++ case IRQ_TYPE_LEVEL_LOW: ++ src_type = SUNXI_SRC_TYPE_LEVEL_LOW; ++ break; ++ default: ++ irq_gc_unlock(gc); ++ pr_err("%s: Cannot assign multiple trigger modes to IRQ %d.\n", ++ __func__, data->irq); ++ return -EBADR; ++ } ++ ++ irqd_set_trigger_type(data, flow_type); ++ irq_setup_alt_chip(data, flow_type); ++ ++ for (i = 0; i <= gc->num_ct; i++, ct++) ++ if (ct->type & flow_type) ++ ctrl_off = ct->regs.type; ++ ++ src_type_reg = sunxi_sc_nmi_read(gc, ctrl_off); ++ src_type_reg &= ~SUNXI_NMI_SRC_TYPE_MASK; ++ src_type_reg |= src_type; ++ sunxi_sc_nmi_write(gc, ctrl_off, src_type_reg); ++ ++ irq_gc_unlock(gc); ++ ++ return IRQ_SET_MASK_OK; ++} ++ ++static int __init sunxi_sc_nmi_irq_init(struct device_node *node, ++ struct sunxi_sc_nmi_reg_offs *reg_offs) ++{ ++ struct irq_domain *domain; ++ struct irq_chip_generic *gc; ++ unsigned int irq; ++ unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; ++ int ret; ++ ++ ++ domain = irq_domain_add_linear(node, 1, &irq_generic_chip_ops, NULL); ++ if (!domain) { ++ pr_err("%s: Could not register interrupt domain.\n", node->name); ++ return -ENOMEM; ++ } ++ ++ ret = irq_alloc_domain_generic_chips(domain, 1, 2, node->name, ++ handle_fasteoi_irq, clr, 0, ++ IRQ_GC_INIT_MASK_CACHE); ++ if (ret) { ++ pr_err("%s: Could not allocate generic interrupt chip.\n", ++ node->name); ++ goto fail_irqd_remove; ++ } ++ ++ irq = irq_of_parse_and_map(node, 0); ++ if (irq <= 0) { ++ pr_err("%s: unable to parse irq\n", node->name); ++ ret = -EINVAL; ++ goto fail_irqd_remove; ++ } ++ ++ gc = irq_get_domain_generic_chip(domain, 0); ++ gc->reg_base = of_iomap(node, 0); ++ if (!gc->reg_base) { ++ pr_err("%s: unable to map resource\n", node->name); ++ ret = -ENOMEM; ++ goto fail_irqd_remove; ++ } ++ ++ gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK; ++ 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_eoi = irq_gc_ack_set_bit; ++ gc->chip_types[0].chip.irq_set_type = sunxi_sc_nmi_set_type; ++ gc->chip_types[0].chip.flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED; ++ gc->chip_types[0].regs.ack = reg_offs->pend; ++ gc->chip_types[0].regs.mask = reg_offs->enable; ++ gc->chip_types[0].regs.type = reg_offs->ctrl; ++ ++ gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH; ++ gc->chip_types[1].chip.name = gc->chip_types[0].chip.name; ++ gc->chip_types[1].chip.irq_ack = irq_gc_ack_set_bit; ++ gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit; ++ gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit; ++ gc->chip_types[1].chip.irq_set_type = sunxi_sc_nmi_set_type; ++ gc->chip_types[1].regs.ack = reg_offs->pend; ++ gc->chip_types[1].regs.mask = reg_offs->enable; ++ gc->chip_types[1].regs.type = reg_offs->ctrl; ++ gc->chip_types[1].handler = handle_edge_irq; ++ ++ sunxi_sc_nmi_write(gc, reg_offs->enable, 0); ++ sunxi_sc_nmi_write(gc, reg_offs->pend, 0x1); ++ ++ irq_set_handler_data(irq, domain); ++ irq_set_chained_handler(irq, sunxi_sc_nmi_handle_irq); ++ ++ return 0; ++ ++fail_irqd_remove: ++ irq_domain_remove(domain); ++ ++ return ret; ++} ++ ++static int __init sun6i_sc_nmi_irq_init(struct device_node *node, ++ struct device_node *parent) ++{ ++ return sunxi_sc_nmi_irq_init(node, &sun6i_reg_offs); ++} ++IRQCHIP_DECLARE(sun6i_sc_nmi, "allwinner,sun6i-a31-sc-nmi", sun6i_sc_nmi_irq_init); ++ ++static int __init sun7i_sc_nmi_irq_init(struct device_node *node, ++ struct device_node *parent) ++{ ++ return sunxi_sc_nmi_irq_init(node, &sun7i_reg_offs); ++} ++IRQCHIP_DECLARE(sun7i_sc_nmi, "allwinner,sun7i-a20-sc-nmi", sun7i_sc_nmi_irq_init); +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/206-dt-sun67i-add-nmi-irqchip.patch b/target/linux/sunxi/patches-3.14/206-dt-sun67i-add-nmi-irqchip.patch new file mode 100644 index 0000000000..57979ef0ce --- /dev/null +++ b/target/linux/sunxi/patches-3.14/206-dt-sun67i-add-nmi-irqchip.patch @@ -0,0 +1,54 @@ +From eebb592523672ee7288b9327bd222165db638d1a Mon Sep 17 00:00:00 2001 +From: Carlo Caione <carlo@caione.org> +Date: Thu, 27 Feb 2014 20:34:21 +0100 +Subject: [PATCH] ARM: sun7i/sun6i: dts: Add NMI irqchip support + +This patch adds DTS entries for NMI controller as child of GIC. + +Signed-off-by: Carlo Caione <carlo@caione.org> +--- + arch/arm/boot/dts/sun6i-a31.dtsi | 8 ++++++++ + arch/arm/boot/dts/sun7i-a20.dtsi | 8 ++++++++ + 2 files changed, 16 insertions(+) + +diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi +index 8441733..74d2920 100644 +--- a/arch/arm/boot/dts/sun6i-a31.dtsi ++++ b/arch/arm/boot/dts/sun6i-a31.dtsi +@@ -421,6 +421,14 @@ + interrupts = <1 9 0xf04>; + }; + ++ nmi_intc: interrupt-controller@01f00c0c { ++ compatible = "allwinner,sun6i-a31-sc-nmi"; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ reg = <0x01f00c0c 0x38>; ++ interrupts = <0 32 4>; ++ }; ++ + cpucfg@01f01c00 { + compatible = "allwinner,sun6i-a31-cpuconfig"; + reg = <0x01f01c00 0x300>; +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 4981f5e..2e66c85 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -401,6 +401,14 @@ + #size-cells = <1>; + ranges; + ++ nmi_intc: interrupt-controller@01c00030 { ++ compatible = "allwinner,sun7i-a20-sc-nmi"; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ reg = <0x01c00030 0x0c>; ++ interrupts = <0 0 4>; ++ }; ++ + spi0: spi@01c05000 { + compatible = "allwinner,sun4i-a10-spi"; + reg = <0x01c05000 0x1000>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/210-mfd-add-axp20x-pmic-driver.patch b/target/linux/sunxi/patches-3.14/210-mfd-add-axp20x-pmic-driver.patch new file mode 100644 index 0000000000..71a3952bfa --- /dev/null +++ b/target/linux/sunxi/patches-3.14/210-mfd-add-axp20x-pmic-driver.patch @@ -0,0 +1,517 @@ +From 509326e0138b762067904c0c60f818e9bdba4cd4 Mon Sep 17 00:00:00 2001 +From: Carlo Caione <carlo@caione.org> +Date: Sat, 1 Mar 2014 17:45:46 +0100 +Subject: [PATCH] mfd: AXP20x: Add mfd driver for AXP20x PMIC + +This patch introduces the preliminary support for PMICs X-Powers AXP202 +and AXP209. The AXP209 and AXP202 are the PMUs (Power Management Unit) +used by A10, A13 and A20 SoCs and developed by X-Powers, a sister company +of Allwinner. + +The core enables support for two subsystems: +- PEK (Power Enable Key) +- Regulators + +Signed-off-by: Carlo Caione <carlo@caione.org> +--- + arch/arm/configs/sunxi_defconfig | 1 + + drivers/mfd/Kconfig | 12 ++ + drivers/mfd/Makefile | 1 + + drivers/mfd/axp20x.c | 250 +++++++++++++++++++++++++++++++++++++++ + include/linux/mfd/axp20x.h | 180 ++++++++++++++++++++++++++++ + 5 files changed, 444 insertions(+) + create mode 100644 drivers/mfd/axp20x.c + create mode 100644 include/linux/mfd/axp20x.h + +diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig +index b5df4a5..d25660d 100644 +--- a/arch/arm/configs/sunxi_defconfig ++++ b/arch/arm/configs/sunxi_defconfig +@@ -55,6 +55,7 @@ CONFIG_GPIO_SYSFS=y + # CONFIG_HWMON is not set + CONFIG_WATCHDOG=y + CONFIG_SUNXI_WATCHDOG=y ++CONFIG_MFD_AXP20X=y + # CONFIG_USB_SUPPORT is not set + CONFIG_NEW_LEDS=y + CONFIG_LEDS_CLASS=y +diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig +index 49bb445..24ba61a 100644 +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -59,6 +59,18 @@ config MFD_AAT2870_CORE + additional drivers must be enabled in order to use the + functionality of the device. + ++config MFD_AXP20X ++ bool "X-Powers AXP20X" ++ select MFD_CORE ++ select REGMAP_I2C ++ select REGMAP_IRQ ++ depends on I2C=y ++ help ++ If you say Y here you get support for the AXP20X. ++ This driver provides common support for accessing the device, ++ additional drivers must be enabled in order to use the ++ functionality of the device. ++ + config MFD_CROS_EC + tristate "ChromeOS Embedded Controller" + select MFD_CORE +diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile +index 5aea5ef..fb773b5 100644 +--- a/drivers/mfd/Makefile ++++ b/drivers/mfd/Makefile +@@ -101,6 +101,7 @@ obj-$(CONFIG_PMIC_DA9052) += da9052-irq.o + obj-$(CONFIG_PMIC_DA9052) += da9052-core.o + obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o + obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o ++obj-$(CONFIG_MFD_AXP20X) += axp20x.o + + obj-$(CONFIG_MFD_LP3943) += lp3943.o + obj-$(CONFIG_MFD_LP8788) += lp8788.o lp8788-irq.o +diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c +new file mode 100644 +index 0000000..92e5b0f +--- /dev/null ++++ b/drivers/mfd/axp20x.c +@@ -0,0 +1,250 @@ ++/* ++ * axp20x.c - mfd core driver for the X-Powers AXP202 and AXP209 ++ * ++ * Author: Carlo Caione <carlo@caione.org> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/err.h> ++#include <linux/i2c.h> ++#include <linux/interrupt.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/pm_runtime.h> ++#include <linux/regmap.h> ++#include <linux/slab.h> ++#include <linux/regulator/consumer.h> ++#include <linux/mfd/axp20x.h> ++#include <linux/mfd/core.h> ++#include <linux/of_device.h> ++#include <linux/of_irq.h> ++ ++#define AXP20X_OFF 0x80 ++ ++static const struct regmap_range axp20x_writeable_ranges[] = { ++ regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE), ++ regmap_reg_range(AXP20X_DCDC_MODE, AXP20X_FG_RES), ++}; ++ ++static const struct regmap_range axp20x_volatile_ranges[] = { ++ regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ5_STATE), ++}; ++ ++static const struct regmap_access_table axp20x_writeable_table = { ++ .yes_ranges = axp20x_writeable_ranges, ++ .n_yes_ranges = ARRAY_SIZE(axp20x_writeable_ranges), ++}; ++ ++static const struct regmap_access_table axp20x_volatile_table = { ++ .yes_ranges = axp20x_volatile_ranges, ++ .n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges), ++}; ++ ++static struct resource axp20x_pek_resources[] = { ++ { ++ .name = "PEK_DBR", ++ .start = AXP20X_IRQ_PEK_RIS_EDGE, ++ .end = AXP20X_IRQ_PEK_RIS_EDGE, ++ .flags = IORESOURCE_IRQ, ++ }, ++ { ++ .name = "PEK_DBF", ++ .start = AXP20X_IRQ_PEK_FAL_EDGE, ++ .end = AXP20X_IRQ_PEK_FAL_EDGE, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static const struct regmap_config axp20x_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .wr_table = &axp20x_writeable_table, ++ .volatile_table = &axp20x_volatile_table, ++ .max_register = AXP20X_FG_RES, ++ .cache_type = REGCACHE_RBTREE, ++}; ++ ++#define AXP20X_IRQ(_irq, _off, _mask) \ ++ [AXP20X_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) } ++ ++static const struct regmap_irq axp20x_regmap_irqs[] = { ++ AXP20X_IRQ(ACIN_OVER_V, 0, 7), ++ AXP20X_IRQ(ACIN_PLUGIN, 0, 6), ++ AXP20X_IRQ(ACIN_REMOVAL, 0, 5), ++ AXP20X_IRQ(VBUS_OVER_V, 0, 4), ++ AXP20X_IRQ(VBUS_PLUGIN, 0, 3), ++ AXP20X_IRQ(VBUS_REMOVAL, 0, 2), ++ AXP20X_IRQ(VBUS_V_LOW, 0, 1), ++ AXP20X_IRQ(BATT_PLUGIN, 1, 7), ++ AXP20X_IRQ(BATT_REMOVAL, 1, 6), ++ AXP20X_IRQ(BATT_ENT_ACT_MODE, 1, 5), ++ AXP20X_IRQ(BATT_EXIT_ACT_MODE, 1, 4), ++ AXP20X_IRQ(CHARG, 1, 3), ++ AXP20X_IRQ(CHARG_DONE, 1, 2), ++ AXP20X_IRQ(BATT_TEMP_HIGH, 1, 1), ++ AXP20X_IRQ(BATT_TEMP_LOW, 1, 0), ++ AXP20X_IRQ(DIE_TEMP_HIGH, 2, 7), ++ AXP20X_IRQ(CHARG_I_LOW, 2, 6), ++ AXP20X_IRQ(DCDC1_V_LONG, 2, 5), ++ AXP20X_IRQ(DCDC2_V_LONG, 2, 4), ++ AXP20X_IRQ(DCDC3_V_LONG, 2, 3), ++ AXP20X_IRQ(PEK_SHORT, 2, 1), ++ AXP20X_IRQ(PEK_LONG, 2, 0), ++ AXP20X_IRQ(N_OE_PWR_ON, 3, 7), ++ AXP20X_IRQ(N_OE_PWR_OFF, 3, 6), ++ AXP20X_IRQ(VBUS_VALID, 3, 5), ++ AXP20X_IRQ(VBUS_NOT_VALID, 3, 4), ++ AXP20X_IRQ(VBUS_SESS_VALID, 3, 3), ++ AXP20X_IRQ(VBUS_SESS_END, 3, 2), ++ AXP20X_IRQ(LOW_PWR_LVL1, 3, 1), ++ AXP20X_IRQ(LOW_PWR_LVL2, 3, 0), ++ AXP20X_IRQ(TIMER, 4, 7), ++ AXP20X_IRQ(PEK_RIS_EDGE, 4, 6), ++ AXP20X_IRQ(PEK_FAL_EDGE, 4, 5), ++ AXP20X_IRQ(GPIO3_INPUT, 4, 3), ++ AXP20X_IRQ(GPIO2_INPUT, 4, 2), ++ AXP20X_IRQ(GPIO1_INPUT, 4, 1), ++ AXP20X_IRQ(GPIO0_INPUT, 4, 0), ++}; ++ ++static const struct regmap_irq_chip axp20x_regmap_irq_chip = { ++ .name = "axp20x_irq_chip", ++ .status_base = AXP20X_IRQ1_STATE, ++ .ack_base = AXP20X_IRQ1_STATE, ++ .mask_base = AXP20X_IRQ1_EN, ++ .num_regs = 5, ++ .irqs = axp20x_regmap_irqs, ++ .num_irqs = ARRAY_SIZE(axp20x_regmap_irqs), ++ .mask_invert = true, ++ .init_ack_masked = true, ++}; ++ ++static struct mfd_cell axp20x_cells[] = { ++ { ++ .name = "axp20x-pek", ++ .of_compatible = "x-powers,axp20x-pek", ++ .num_resources = ARRAY_SIZE(axp20x_pek_resources), ++ .resources = axp20x_pek_resources, ++ }, { ++ .name = "axp20x-regulator", ++ }, ++}; ++ ++const struct of_device_id axp20x_of_match[] = { ++ { .compatible = "x-powers,axp202", .data = (void *) AXP202_ID }, ++ { .compatible = "x-powers,axp209", .data = (void *) AXP209_ID }, ++ { }, ++}; ++ ++static struct axp20x_dev *axp20x_pm_power_off; ++static void axp20x_power_off(void) ++{ ++ regmap_write(axp20x_pm_power_off->regmap, AXP20X_OFF_CTRL, ++ AXP20X_OFF); ++} ++ ++static int axp20x_i2c_probe(struct i2c_client *i2c, ++ const struct i2c_device_id *id) ++{ ++ struct axp20x_dev *axp20x; ++ const struct of_device_id *of_id; ++ struct device_node *node = i2c->dev.of_node; ++ int ret; ++ ++ axp20x = devm_kzalloc(&i2c->dev, sizeof(*axp20x), GFP_KERNEL); ++ if (!axp20x) ++ return -ENOMEM; ++ ++ of_id = of_match_device(axp20x_of_match, &i2c->dev); ++ if (!of_id) { ++ dev_err(&i2c->dev, "Unable to setup AXP20X data\n"); ++ return -ENODEV; ++ } ++ axp20x->variant = (int) of_id->data; ++ ++ axp20x->i2c_client = i2c; ++ axp20x->dev = &i2c->dev; ++ dev_set_drvdata(axp20x->dev, axp20x); ++ ++ axp20x->regmap = devm_regmap_init_i2c(i2c, &axp20x_regmap_config); ++ if (IS_ERR(axp20x->regmap)) { ++ ret = PTR_ERR(axp20x->regmap); ++ dev_err(&i2c->dev, "regmap init failed: %d\n", ret); ++ return ret; ++ } ++ ++ axp20x->irq = i2c->irq; ++ ret = regmap_add_irq_chip(axp20x->regmap, axp20x->irq, ++ IRQF_ONESHOT | IRQF_SHARED, -1, ++ &axp20x_regmap_irq_chip, ++ &axp20x->regmap_irqc); ++ if (ret) { ++ dev_err(&i2c->dev, "failed to add irq chip: %d\n", ret); ++ return ret; ++ } ++ ++ ret = mfd_add_devices(axp20x->dev, -1, axp20x_cells, ++ ARRAY_SIZE(axp20x_cells), NULL, 0, NULL); ++ if (ret) { ++ dev_err(&i2c->dev, "failed to add MFD devices: %d\n", ret); ++ goto mfd_err; ++ } ++ ++ axp20x->pm_off = of_property_read_bool(node, "axp,system-power-controller"); ++ ++ if (axp20x->pm_off && !pm_power_off) { ++ axp20x_pm_power_off = axp20x; ++ pm_power_off = axp20x_power_off; ++ } ++ ++ dev_info(&i2c->dev, "AXP20X driver loaded\n"); ++ ++ return 0; ++ ++mfd_err: ++ regmap_del_irq_chip(axp20x->irq, axp20x->regmap_irqc); ++ ++ return ret; ++} ++ ++static int axp20x_i2c_remove(struct i2c_client *i2c) ++{ ++ struct axp20x_dev *axp20x = i2c_get_clientdata(i2c); ++ ++ if (axp20x == axp20x_pm_power_off) { ++ axp20x_pm_power_off = NULL; ++ pm_power_off = NULL; ++ } ++ ++ mfd_remove_devices(axp20x->dev); ++ regmap_del_irq_chip(axp20x->i2c_client->irq, axp20x->regmap_irqc); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id axp20x_i2c_id[] = { ++ { "axp202", AXP202_ID }, ++ { "axp209", AXP209_ID }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id); ++ ++static struct i2c_driver axp20x_i2c_driver = { ++ .driver = { ++ .name = "axp20x", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(axp20x_of_match), ++ }, ++ .probe = axp20x_i2c_probe, ++ .remove = axp20x_i2c_remove, ++ .id_table = axp20x_i2c_id, ++}; ++ ++module_i2c_driver(axp20x_i2c_driver); ++ ++MODULE_DESCRIPTION("PMIC MFD core driver for AXP20X"); ++MODULE_AUTHOR("Carlo Caione <carlo@caione.org>"); ++MODULE_LICENSE("GPL"); +diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h +new file mode 100644 +index 0000000..fcef32c +--- /dev/null ++++ b/include/linux/mfd/axp20x.h +@@ -0,0 +1,180 @@ ++/* ++ * Functions to access AXP20X power management chip. ++ * ++ * Copyright (C) 2013, Carlo Caione <carlo@caione.org> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef __LINUX_MFD_AXP20X_H ++#define __LINUX_MFD_AXP20X_H ++ ++#define AXP202_ID 0 ++#define AXP209_ID 1 ++ ++#define AXP20X_DATACACHE(m) (0x04 + (m)) ++ ++/* Power supply */ ++#define AXP20X_PWR_INPUT_STATUS 0x00 ++#define AXP20X_PWR_OP_MODE 0x01 ++#define AXP20X_USB_OTG_STATUS 0x02 ++#define AXP20X_PWR_OUT_CTRL 0x12 ++#define AXP20X_DCDC2_V_OUT 0x23 ++#define AXP20X_DCDC2_LDO3_V_SCAL 0x25 ++#define AXP20X_DCDC3_V_OUT 0x27 ++#define AXP20X_LDO24_V_OUT 0x28 ++#define AXP20X_LDO3_V_OUT 0x29 ++#define AXP20X_VBUS_IPSOUT_MGMT 0x30 ++#define AXP20X_V_OFF 0x31 ++#define AXP20X_OFF_CTRL 0x32 ++#define AXP20X_CHRG_CTRL1 0x33 ++#define AXP20X_CHRG_CTRL2 0x34 ++#define AXP20X_CHRG_BAK_CTRL 0x35 ++#define AXP20X_PEK_KEY 0x36 ++#define AXP20X_DCDC_FREQ 0x37 ++#define AXP20X_V_LTF_CHRG 0x38 ++#define AXP20X_V_HTF_CHRG 0x39 ++#define AXP20X_APS_WARN_L1 0x3a ++#define AXP20X_APS_WARN_L2 0x3b ++#define AXP20X_V_LTF_DISCHRG 0x3c ++#define AXP20X_V_HTF_DISCHRG 0x3d ++ ++/* Interrupt */ ++#define AXP20X_IRQ1_EN 0x40 ++#define AXP20X_IRQ2_EN 0x41 ++#define AXP20X_IRQ3_EN 0x42 ++#define AXP20X_IRQ4_EN 0x43 ++#define AXP20X_IRQ5_EN 0x44 ++#define AXP20X_IRQ1_STATE 0x48 ++#define AXP20X_IRQ2_STATE 0x49 ++#define AXP20X_IRQ3_STATE 0x4a ++#define AXP20X_IRQ4_STATE 0x4b ++#define AXP20X_IRQ5_STATE 0x4c ++ ++/* ADC */ ++#define AXP20X_ACIN_V_ADC_H 0x56 ++#define AXP20X_ACIN_V_ADC_L 0x57 ++#define AXP20X_ACIN_I_ADC_H 0x58 ++#define AXP20X_ACIN_I_ADC_L 0x59 ++#define AXP20X_VBUS_V_ADC_H 0x5a ++#define AXP20X_VBUS_V_ADC_L 0x5b ++#define AXP20X_VBUS_I_ADC_H 0x5c ++#define AXP20X_VBUS_I_ADC_L 0x5d ++#define AXP20X_TEMP_ADC_H 0x5e ++#define AXP20X_TEMP_ADC_L 0x5f ++#define AXP20X_TS_IN_H 0x62 ++#define AXP20X_TS_IN_L 0x63 ++#define AXP20X_GPIO0_V_ADC_H 0x64 ++#define AXP20X_GPIO0_V_ADC_L 0x65 ++#define AXP20X_GPIO1_V_ADC_H 0x66 ++#define AXP20X_GPIO1_V_ADC_L 0x67 ++#define AXP20X_PWR_BATT_H 0x70 ++#define AXP20X_PWR_BATT_M 0x71 ++#define AXP20X_PWR_BATT_L 0x72 ++#define AXP20X_BATT_V_H 0x78 ++#define AXP20X_BATT_V_L 0x79 ++#define AXP20X_BATT_CHRG_I_H 0x7a ++#define AXP20X_BATT_CHRG_I_L 0x7b ++#define AXP20X_BATT_DISCHRG_I_H 0x7c ++#define AXP20X_BATT_DISCHRG_I_L 0x7d ++#define AXP20X_IPSOUT_V_HIGH_H 0x7e ++#define AXP20X_IPSOUT_V_HIGH_L 0x7f ++ ++/* Power supply */ ++#define AXP20X_DCDC_MODE 0x80 ++#define AXP20X_ADC_EN1 0x82 ++#define AXP20X_ADC_EN2 0x83 ++#define AXP20X_ADC_RATE 0x84 ++#define AXP20X_GPIO10_IN_RANGE 0x85 ++#define AXP20X_GPIO1_ADC_IRQ_RIS 0x86 ++#define AXP20X_GPIO1_ADC_IRQ_FAL 0x87 ++#define AXP20X_TIMER_CTRL 0x8a ++#define AXP20X_VBUS_MON 0x8b ++#define AXP20X_OVER_TMP 0x8f ++ ++/* GPIO */ ++#define AXP20X_GPIO0_CTRL 0x90 ++#define AXP20X_LDO5_V_OUT 0x91 ++#define AXP20X_GPIO1_CTRL 0x92 ++#define AXP20X_GPIO2_CTRL 0x93 ++#define AXP20X_GPIO20_SS 0x94 ++#define AXP20X_GPIO3_CTRL 0x95 ++ ++/* Battery */ ++#define AXP20X_CHRG_CC_31_24 0xb0 ++#define AXP20X_CHRG_CC_23_16 0xb1 ++#define AXP20X_CHRG_CC_15_8 0xb2 ++#define AXP20X_CHRG_CC_7_0 0xb3 ++#define AXP20X_DISCHRG_CC_31_24 0xb4 ++#define AXP20X_DISCHRG_CC_23_16 0xb5 ++#define AXP20X_DISCHRG_CC_15_8 0xb6 ++#define AXP20X_DISCHRG_CC_7_0 0xb7 ++#define AXP20X_CC_CTRL 0xb8 ++#define AXP20X_FG_RES 0xb9 ++ ++/* Regulators IDs */ ++enum { ++ AXP20X_LDO1 = 0, ++ AXP20X_LDO2, ++ AXP20X_LDO3, ++ AXP20X_LDO4, ++ AXP20X_LDO5, ++ AXP20X_DCDC2, ++ AXP20X_DCDC3, ++ AXP20X_REG_ID_MAX, ++}; ++ ++/* IRQs */ ++enum { ++ AXP20X_IRQ_ACIN_OVER_V = 1, ++ AXP20X_IRQ_ACIN_PLUGIN, ++ AXP20X_IRQ_ACIN_REMOVAL, ++ AXP20X_IRQ_VBUS_OVER_V, ++ AXP20X_IRQ_VBUS_PLUGIN, ++ AXP20X_IRQ_VBUS_REMOVAL, ++ AXP20X_IRQ_VBUS_V_LOW, ++ AXP20X_IRQ_BATT_PLUGIN, ++ AXP20X_IRQ_BATT_REMOVAL, ++ AXP20X_IRQ_BATT_ENT_ACT_MODE, ++ AXP20X_IRQ_BATT_EXIT_ACT_MODE, ++ AXP20X_IRQ_CHARG, ++ AXP20X_IRQ_CHARG_DONE, ++ AXP20X_IRQ_BATT_TEMP_HIGH, ++ AXP20X_IRQ_BATT_TEMP_LOW, ++ AXP20X_IRQ_DIE_TEMP_HIGH, ++ AXP20X_IRQ_CHARG_I_LOW, ++ AXP20X_IRQ_DCDC1_V_LONG, ++ AXP20X_IRQ_DCDC2_V_LONG, ++ AXP20X_IRQ_DCDC3_V_LONG, ++ AXP20X_IRQ_PEK_SHORT = 22, ++ AXP20X_IRQ_PEK_LONG, ++ AXP20X_IRQ_N_OE_PWR_ON, ++ AXP20X_IRQ_N_OE_PWR_OFF, ++ AXP20X_IRQ_VBUS_VALID, ++ AXP20X_IRQ_VBUS_NOT_VALID, ++ AXP20X_IRQ_VBUS_SESS_VALID, ++ AXP20X_IRQ_VBUS_SESS_END, ++ AXP20X_IRQ_LOW_PWR_LVL1, ++ AXP20X_IRQ_LOW_PWR_LVL2, ++ AXP20X_IRQ_TIMER, ++ AXP20X_IRQ_PEK_RIS_EDGE, ++ AXP20X_IRQ_PEK_FAL_EDGE, ++ AXP20X_IRQ_GPIO3_INPUT, ++ AXP20X_IRQ_GPIO2_INPUT, ++ AXP20X_IRQ_GPIO1_INPUT, ++ AXP20X_IRQ_GPIO0_INPUT, ++}; ++ ++struct axp20x_dev { ++ struct device *dev; ++ struct i2c_client *i2c_client; ++ struct regmap *regmap; ++ struct regmap_irq_chip_data *regmap_irqc; ++ int variant; ++ int irq; ++ bool pm_off; ++}; ++ ++#endif /* __LINUX_MFD_AXP20X_H */ +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/211-input-add-axp20x-power-enable-key-support.patch b/target/linux/sunxi/patches-3.14/211-input-add-axp20x-power-enable-key-support.patch new file mode 100644 index 0000000000..9291dc5552 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/211-input-add-axp20x-power-enable-key-support.patch @@ -0,0 +1,339 @@ +From 656b9ff9781aa3ffeb4231f32dfc545c92d04a12 Mon Sep 17 00:00:00 2001 +From: Carlo Caione <carlo@caione.org> +Date: Sat, 1 Mar 2014 17:45:49 +0100 +Subject: [PATCH] input: misc: Add driver for AXP20x Power Enable Key + +This patch add support for the Power Enable Key found on MFD AXP202 and +AXP209. Besides the basic support for the button, the driver adds two +entries in sysfs to configure the time delay for power on/off. + +Signed-off-by: Carlo Caione <carlo@caione.org> +--- + arch/arm/configs/sunxi_defconfig | 2 + + drivers/input/misc/Kconfig | 11 ++ + drivers/input/misc/Makefile | 1 + + drivers/input/misc/axp20x-pek.c | 265 +++++++++++++++++++++++++++++++++++++++ + 4 files changed, 279 insertions(+) + create mode 100644 drivers/input/misc/axp20x-pek.c + +diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig +index d25660d..3ed7023 100644 +--- a/arch/arm/configs/sunxi_defconfig ++++ b/arch/arm/configs/sunxi_defconfig +@@ -40,6 +40,8 @@ CONFIG_SUN4I_EMAC=y + # CONFIG_NET_VENDOR_STMICRO is not set + # CONFIG_NET_VENDOR_WIZNET is not set + # CONFIG_WLAN is not set ++CONFIG_INPUT_MISC=y ++CONFIG_INPUT_AXP20X_PEK=y + CONFIG_SERIAL_8250=y + CONFIG_SERIAL_8250_CONSOLE=y + CONFIG_SERIAL_8250_NR_UARTS=8 +diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig +index 7904ab0..87244fb 100644 +--- a/drivers/input/misc/Kconfig ++++ b/drivers/input/misc/Kconfig +@@ -393,6 +393,17 @@ config INPUT_RETU_PWRBUTTON + To compile this driver as a module, choose M here. The module will + be called retu-pwrbutton. + ++config INPUT_AXP20X_PEK ++ tristate "X-Powers AXP20X power button driver" ++ depends on MFD_AXP20X ++ help ++ Say Y here if you want to enable power key reporting via the ++ AXP20X PMIC. ++ ++ To compile this driver as a module, choose M here. The module will ++ be called axp20x-pek. ++ ++ + config INPUT_TWL4030_PWRBUTTON + tristate "TWL4030 Power button Driver" + depends on TWL4030_CORE +diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile +index cda71fc..624abf5 100644 +--- a/drivers/input/misc/Makefile ++++ b/drivers/input/misc/Makefile +@@ -50,6 +50,7 @@ obj-$(CONFIG_INPUT_POWERMATE) += powermate.o + obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o + obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o + obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o ++obj-$(CONFIG_INPUT_AXP20X_PEK) += axp20x-pek.o + obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o + obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o + obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o +diff --git a/drivers/input/misc/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c +new file mode 100644 +index 0000000..799275d +--- /dev/null ++++ b/drivers/input/misc/axp20x-pek.c +@@ -0,0 +1,265 @@ ++/* ++ * axp20x power button driver. ++ * ++ * Copyright (C) 2013 Carlo Caione <carlo@caione.org> ++ * ++ * This file is subject to the terms and conditions of the GNU General ++ * Public License. See the file "COPYING" in the main directory of this ++ * archive for more details. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/errno.h> ++#include <linux/irq.h> ++#include <linux/init.h> ++#include <linux/input.h> ++#include <linux/interrupt.h> ++#include <linux/kernel.h> ++#include <linux/mfd/axp20x.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/regmap.h> ++#include <linux/slab.h> ++ ++#define AXP20X_PEK_STARTUP_MASK (0xc0) ++#define AXP20X_PEK_SHUTDOWN_MASK (0x03) ++ ++static const char const *startup_time[] = { "128mS", "3S" , "1S", "2S" }; ++static const char const *shutdown_time[] = { "4S", "6S" , "8S", "10S" }; ++ ++struct axp20x_pek { ++ struct axp20x_dev *axp20x; ++ struct input_dev *input; ++ int irq_dbr; ++ int irq_dbf; ++}; ++ ++struct axp20x_pek_ext_attr { ++ const char const **str; ++ unsigned int mask; ++}; ++ ++static struct axp20x_pek_ext_attr axp20x_pek_startup_ext_attr = { ++ .str = startup_time, ++ .mask = AXP20X_PEK_STARTUP_MASK, ++}; ++ ++static struct axp20x_pek_ext_attr axp20x_pek_shutdown_ext_attr = { ++ .str = shutdown_time, ++ .mask = AXP20X_PEK_SHUTDOWN_MASK, ++}; ++ ++static struct axp20x_pek_ext_attr *get_axp_ext_attr(struct device_attribute *attr) ++{ ++ return container_of(attr, struct dev_ext_attribute, attr)->var; ++} ++ ++ssize_t axp20x_show_ext_attr(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); ++ struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr); ++ unsigned int val; ++ int ret, i; ++ int cnt = 0; ++ ++ ret = regmap_read(axp20x_pek->axp20x->regmap, AXP20X_PEK_KEY, &val); ++ if (ret != 0) ++ return ret; ++ ++ val &= axp20x_ea->mask; ++ val >>= ffs(axp20x_ea->mask) - 1; ++ ++ for (i = 0; i < 4; i++) { ++ if (val == i) ++ cnt += sprintf(buf + cnt, "[%s] ", axp20x_ea->str[i]); ++ else ++ cnt += sprintf(buf + cnt, "%s ", axp20x_ea->str[i]); ++ } ++ ++ cnt += sprintf(buf + cnt, "\n"); ++ ++ return cnt; ++} ++ ++ssize_t axp20x_store_ext_attr(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); ++ struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr); ++ char val_str[20]; ++ int ret, i; ++ size_t len; ++ ++ val_str[sizeof(val_str) - 1] = '\0'; ++ strncpy(val_str, buf, sizeof(val_str) - 1); ++ len = strlen(val_str); ++ ++ if (len && val_str[len - 1] == '\n') ++ val_str[len - 1] = '\0'; ++ ++ for (i = 0; i < 4; i++) { ++ if (!strcmp(val_str, axp20x_ea->str[i])) { ++ ret = regmap_update_bits(axp20x_pek->axp20x->regmap, ++ AXP20X_PEK_KEY, ++ axp20x_ea->mask, i); ++ if (ret != 0) ++ return -EINVAL; ++ return count; ++ } ++ } ++ ++ return -EINVAL; ++} ++ ++static struct dev_ext_attribute axp20x_dev_attr_startup = { ++ .attr = __ATTR(startup, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr), ++ .var = &axp20x_pek_startup_ext_attr ++}; ++ ++static struct dev_ext_attribute axp20x_dev_attr_shutdown = { ++ __ATTR(shutdown, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr), ++ &axp20x_pek_shutdown_ext_attr ++}; ++ ++static struct attribute *dev_attrs[] = { ++ &axp20x_dev_attr_startup.attr.attr, ++ &axp20x_dev_attr_shutdown.attr.attr, ++ NULL, ++}; ++ ++static struct attribute_group dev_attr_group = { ++ .attrs = dev_attrs, ++}; ++ ++static const struct attribute_group *dev_attr_groups[] = { ++ &dev_attr_group, ++ NULL, ++}; ++ ++static irqreturn_t axp20x_pek_irq(int irq, void *pwr) ++{ ++ struct input_dev *idev = pwr; ++ struct axp20x_pek *axp20x_pek = input_get_drvdata(idev); ++ ++ if (irq == axp20x_pek->irq_dbr) ++ input_report_key(idev, KEY_POWER, true); ++ else if (irq == axp20x_pek->irq_dbf) ++ input_report_key(idev, KEY_POWER, false); ++ ++ input_sync(idev); ++ ++ return IRQ_HANDLED; ++} ++ ++static int axp20x_pek_probe(struct platform_device *pdev) ++{ ++ struct axp20x_pek *axp20x_pek; ++ struct axp20x_dev *axp20x; ++ struct input_dev *idev; ++ int error; ++ ++ axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek), ++ GFP_KERNEL); ++ if (!axp20x_pek) ++ return -ENOMEM; ++ ++ axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent); ++ axp20x = axp20x_pek->axp20x; ++ ++ axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR"); ++ if (axp20x_pek->irq_dbr < 0) { ++ dev_err(&pdev->dev, "No IRQ for PEK_DBR, error=%d\n", ++ axp20x_pek->irq_dbr); ++ return axp20x_pek->irq_dbr; ++ } ++ axp20x_pek->irq_dbr = regmap_irq_get_virq(axp20x->regmap_irqc, ++ axp20x_pek->irq_dbr); ++ ++ axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF"); ++ if (axp20x_pek->irq_dbf < 0) { ++ dev_err(&pdev->dev, "No IRQ for PEK_DBF, error=%d\n", ++ axp20x_pek->irq_dbf); ++ return axp20x_pek->irq_dbf; ++ } ++ axp20x_pek->irq_dbf = regmap_irq_get_virq(axp20x->regmap_irqc, ++ axp20x_pek->irq_dbf); ++ ++ axp20x_pek->input = devm_input_allocate_device(&pdev->dev); ++ if (!axp20x_pek->input) ++ return -ENOMEM; ++ ++ idev = axp20x_pek->input; ++ ++ idev->name = "axp20x-pek"; ++ idev->phys = "m1kbd/input2"; ++ idev->dev.parent = &pdev->dev; ++ ++ input_set_capability(idev, EV_KEY, KEY_POWER); ++ ++ input_set_drvdata(idev, axp20x_pek); ++ ++ error = devm_request_threaded_irq(&pdev->dev, axp20x_pek->irq_dbr, ++ NULL, axp20x_pek_irq, 0, ++ "axp20x-pek-dbr", idev); ++ if (error) { ++ dev_err(axp20x->dev, "Failed to request dbr IRQ#%d: %d\n", ++ axp20x_pek->irq_dbr, error); ++ ++ return error; ++ } ++ ++ error = devm_request_threaded_irq(&pdev->dev, axp20x_pek->irq_dbf, ++ NULL, axp20x_pek_irq, 0, ++ "axp20x-pek-dbf", idev); ++ if (error) { ++ dev_err(axp20x->dev, "Failed to request dbf IRQ#%d: %d\n", ++ axp20x_pek->irq_dbf, error); ++ return error; ++ } ++ ++ idev->dev.groups = dev_attr_groups; ++ error = input_register_device(idev); ++ if (error) { ++ dev_err(axp20x->dev, "Can't register input device: %d\n", error); ++ return error; ++ } ++ ++ platform_set_drvdata(pdev, axp20x_pek); ++ ++ return 0; ++} ++ ++static int axp20x_pek_remove(struct platform_device *pdev) ++{ ++ struct axp20x_pek *axp20x_pek = platform_get_drvdata(pdev); ++ ++ input_unregister_device(axp20x_pek->input); ++ ++ return 0; ++} ++ ++static const struct of_device_id axp20x_pek_match[] = { ++ { .compatible = "x-powers,axp20x-pek", }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, axp20x_pek_match); ++ ++static struct platform_driver axp20x_pek_driver = { ++ .probe = axp20x_pek_probe, ++ .remove = axp20x_pek_remove, ++ .driver = { ++ .name = "axp20x-pek", ++ .owner = THIS_MODULE, ++ .of_match_table = axp20x_pek_match, ++ }, ++}; ++module_platform_driver(axp20x_pek_driver); ++ ++MODULE_DESCRIPTION("axp20x Power Button"); ++MODULE_AUTHOR("Carlo Caione <carlo@caione.org>"); ++MODULE_LICENSE("GPL"); +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/212-regulator-add-axp20x-regulator-support.patch b/target/linux/sunxi/patches-3.14/212-regulator-add-axp20x-regulator-support.patch new file mode 100644 index 0000000000..d7f4dcfe60 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/212-regulator-add-axp20x-regulator-support.patch @@ -0,0 +1,414 @@ +From c3af279a9031b3375d3e5a684619c1adbbe30da9 Mon Sep 17 00:00:00 2001 +From: Carlo Caione <carlo@caione.org> +Date: Sat, 1 Mar 2014 17:45:51 +0100 +Subject: [PATCH] regulator: AXP20x: Add support for regulators subsystem + +AXP202 and AXP209 come with two synchronous step-down DC-DCs and five +LDOs. This patch introduces basic support for those regulators. + +Signed-off-by: Carlo Caione <carlo@caione.org> +--- + arch/arm/configs/sunxi_defconfig | 1 + + drivers/regulator/Kconfig | 7 + + drivers/regulator/Makefile | 1 + + drivers/regulator/axp20x-regulator.c | 349 +++++++++++++++++++++++++++++++++++ + 4 files changed, 358 insertions(+) + create mode 100644 drivers/regulator/axp20x-regulator.c + +diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig +index 3ed7023..6e305da 100644 +--- a/arch/arm/configs/sunxi_defconfig ++++ b/arch/arm/configs/sunxi_defconfig +@@ -72,3 +72,4 @@ CONFIG_NFS_FS=y + CONFIG_ROOT_NFS=y + CONFIG_NLS=y + CONFIG_PRINTK_TIME=y ++CONFIG_REGULATOR_AXP20X=y +diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig +index 6a79328..9f3bc48 100644 +--- a/drivers/regulator/Kconfig ++++ b/drivers/regulator/Kconfig +@@ -139,6 +139,13 @@ config REGULATOR_AS3722 + AS3722 PMIC. This will enable support for all the software + controllable DCDC/LDO regulators. + ++config REGULATOR_AXP20X ++ tristate "X-POWERS AXP20X PMIC Regulators" ++ depends on MFD_AXP20X ++ help ++ This driver provides support for the voltage regulators on the ++ AXP20X PMIC. ++ + config REGULATOR_DA903X + tristate "Dialog Semiconductor DA9030/DA9034 regulators" + depends on PMIC_DA903X +diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile +index 979f9dd..1dd084a 100644 +--- a/drivers/regulator/Makefile ++++ b/drivers/regulator/Makefile +@@ -20,6 +20,7 @@ obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o + obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o + obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o + obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o ++obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o + obj-$(CONFIG_REGULATOR_DA903X) += da903x.o + obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o + obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o +diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c +new file mode 100644 +index 0000000..9072f2f +--- /dev/null ++++ b/drivers/regulator/axp20x-regulator.c +@@ -0,0 +1,349 @@ ++/* ++ * axp20x regulators driver. ++ * ++ * Copyright (C) 2013 Carlo Caione <carlo@caione.org> ++ * ++ * This file is subject to the terms and conditions of the GNU General ++ * Public License. See the file "COPYING" in the main directory of this ++ * archive for more details. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/err.h> ++#include <linux/platform_device.h> ++#include <linux/of.h> ++#include <linux/of_device.h> ++#include <linux/regulator/driver.h> ++#include <linux/regulator/of_regulator.h> ++#include <linux/mfd/axp20x.h> ++#include <linux/regmap.h> ++ ++#define AXP20X_IO_ENABLED (0x03) ++ ++#define AXP20X_WORKMODE_DCDC2_MASK BIT(2) ++#define AXP20X_WORKMODE_DCDC3_MASK BIT(1) ++ ++#define AXP20X_FREQ_DCDC_MASK (0x0f) ++ ++struct axp20x_regulators { ++ struct regulator_desc rdesc[AXP20X_REG_ID_MAX]; ++ struct regulator_dev *rdev[AXP20X_REG_ID_MAX]; ++ struct axp20x_dev *axp20x; ++}; ++ ++#define AXP20X_DESC(_id, _min, _max, _step, _vreg, _vmask, _ereg, _emask) \ ++ [AXP20X_##_id] = { \ ++ .name = #_id, \ ++ .type = REGULATOR_VOLTAGE, \ ++ .id = AXP20X_##_id, \ ++ .n_voltages = (((_max) - (_min)) / (_step) + 1), \ ++ .owner = THIS_MODULE, \ ++ .min_uV = (_min) * 1000, \ ++ .uV_step = (_step) * 1000, \ ++ .vsel_reg = (_vreg), \ ++ .vsel_mask = (_vmask), \ ++ .enable_reg = (_ereg), \ ++ .enable_mask = (_emask), \ ++ .ops = &axp20x_ops, \ ++ } ++ ++#define AXP20X_DESC_IO(_id, _min, _max, _step, _vreg, _vmask, _ereg, _emask) \ ++ [AXP20X_##_id] = { \ ++ .name = #_id, \ ++ .type = REGULATOR_VOLTAGE, \ ++ .id = AXP20X_##_id, \ ++ .n_voltages = (((_max) - (_min)) / (_step) + 1), \ ++ .owner = THIS_MODULE, \ ++ .min_uV = (_min) * 1000, \ ++ .uV_step = (_step) * 1000, \ ++ .vsel_reg = (_vreg), \ ++ .vsel_mask = (_vmask), \ ++ .enable_reg = (_ereg), \ ++ .enable_mask = (_emask), \ ++ .ops = &axp20x_ops_io, \ ++ } ++ ++#define AXP20X_DESC_FIXED(_id, _volt) \ ++ [AXP20X_##_id] = { \ ++ .name = #_id, \ ++ .type = REGULATOR_VOLTAGE, \ ++ .id = AXP20X_##_id, \ ++ .n_voltages = 1, \ ++ .owner = THIS_MODULE, \ ++ .min_uV = (_volt) * 1000, \ ++ .ops = &axp20x_ops, \ ++ } ++ ++#define AXP20X_DESC_TABLE(_id, _table, _vreg, _vmask, _ereg, _emask) \ ++ [AXP20X_##_id] = { \ ++ .name = #_id, \ ++ .type = REGULATOR_VOLTAGE, \ ++ .id = AXP20X_##_id, \ ++ .n_voltages = ARRAY_SIZE(_table), \ ++ .owner = THIS_MODULE, \ ++ .vsel_reg = (_vreg), \ ++ .vsel_mask = (_vmask), \ ++ .enable_reg = (_ereg), \ ++ .enable_mask = (_emask), \ ++ .volt_table = (_table), \ ++ .ops = &axp20x_ops_table, \ ++ } ++ ++static int axp20x_ldo4_data[] = { 1250000, 1300000, 1400000, 1500000, 1600000, 1700000, ++ 1800000, 1900000, 2000000, 2500000, 2700000, 2800000, ++ 3000000, 3100000, 3200000, 3300000 }; ++ ++static int axp20x_set_suspend_voltage(struct regulator_dev *rdev, int uV) ++{ ++ return regulator_set_voltage_sel_regmap(rdev, 0); ++} ++ ++static int axp20x_io_enable_regmap(struct regulator_dev *rdev) ++{ ++ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, ++ rdev->desc->enable_mask, AXP20X_IO_ENABLED); ++} ++ ++static int axp109_io_is_enabled_regmap(struct regulator_dev *rdev) ++{ ++ unsigned int val; ++ int ret; ++ ++ ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val); ++ if (ret != 0) ++ return ret; ++ ++ val &= rdev->desc->enable_mask; ++ return (val == AXP20X_IO_ENABLED); ++} ++ ++static struct regulator_ops axp20x_ops_table = { ++ .set_voltage_sel = regulator_set_voltage_sel_regmap, ++ .get_voltage_sel = regulator_get_voltage_sel_regmap, ++ .list_voltage = regulator_list_voltage_table, ++ .enable = regulator_enable_regmap, ++ .disable = regulator_disable_regmap, ++ .is_enabled = regulator_is_enabled_regmap, ++ .set_suspend_enable = regulator_enable_regmap, ++ .set_suspend_disable = regulator_disable_regmap, ++ .set_suspend_voltage = axp20x_set_suspend_voltage, ++}; ++ ++ ++static struct regulator_ops axp20x_ops = { ++ .set_voltage_sel = regulator_set_voltage_sel_regmap, ++ .get_voltage_sel = regulator_get_voltage_sel_regmap, ++ .list_voltage = regulator_list_voltage_linear, ++ .enable = regulator_enable_regmap, ++ .disable = regulator_disable_regmap, ++ .is_enabled = regulator_is_enabled_regmap, ++ .set_suspend_enable = regulator_enable_regmap, ++ .set_suspend_disable = regulator_disable_regmap, ++ .set_suspend_voltage = axp20x_set_suspend_voltage, ++}; ++ ++static struct regulator_ops axp20x_ops_io = { ++ .set_voltage_sel = regulator_set_voltage_sel_regmap, ++ .get_voltage_sel = regulator_get_voltage_sel_regmap, ++ .list_voltage = regulator_list_voltage_linear, ++ .enable = axp20x_io_enable_regmap, ++ .disable = regulator_disable_regmap, ++ .is_enabled = axp109_io_is_enabled_regmap, ++ .set_suspend_enable = regulator_enable_regmap, ++ .set_suspend_disable = regulator_disable_regmap, ++ .set_suspend_voltage = axp20x_set_suspend_voltage, ++}; ++ ++static struct regulator_desc axp20x_regulators[] = { ++ AXP20X_DESC(DCDC2, 700, 2275, 25, AXP20X_DCDC2_V_OUT, 0x3f, ++ AXP20X_PWR_OUT_CTRL, 0x10), ++ AXP20X_DESC(DCDC3, 700, 3500, 25, AXP20X_DCDC3_V_OUT, 0x7f, ++ AXP20X_PWR_OUT_CTRL, 0x02), ++ AXP20X_DESC_FIXED(LDO1, 1300), ++ AXP20X_DESC(LDO2, 1800, 3300, 100, AXP20X_LDO24_V_OUT, 0xf0, ++ AXP20X_PWR_OUT_CTRL, 0x04), ++ AXP20X_DESC(LDO3, 700, 3500, 25, AXP20X_LDO3_V_OUT, 0x7f, ++ AXP20X_PWR_OUT_CTRL, 0x40), ++ AXP20X_DESC_TABLE(LDO4, axp20x_ldo4_data, AXP20X_LDO24_V_OUT, 0x0f, ++ AXP20X_PWR_OUT_CTRL, 0x08), ++ AXP20X_DESC_IO(LDO5, 1800, 3300, 100, AXP20X_LDO5_V_OUT, 0xf0, ++ AXP20X_GPIO0_CTRL, 0x07), ++}; ++ ++#define AXP_MATCH(_name, _id) \ ++ [AXP20X_##_id] = { \ ++ .name = #_name, \ ++ .driver_data = (void *) &axp20x_regulators[AXP20X_##_id], \ ++ } ++ ++static struct of_regulator_match axp20x_matches[] = { ++ AXP_MATCH(dcdc2, DCDC2), ++ AXP_MATCH(dcdc3, DCDC3), ++ AXP_MATCH(ldo1, LDO1), ++ AXP_MATCH(ldo2, LDO2), ++ AXP_MATCH(ldo3, LDO3), ++ AXP_MATCH(ldo4, LDO4), ++ AXP_MATCH(ldo5, LDO5), ++}; ++ ++static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq) ++{ ++ struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); ++ ++ if (dcdcfreq < 750) ++ dcdcfreq = 750; ++ ++ if (dcdcfreq > 1875) ++ dcdcfreq = 1875; ++ ++ dcdcfreq = (dcdcfreq - 750) / 75; ++ ++ return regmap_update_bits(axp20x->regmap, AXP20X_DCDC_FREQ, ++ AXP20X_FREQ_DCDC_MASK, dcdcfreq); ++} ++ ++static int axp20x_regulator_parse_dt(struct platform_device *pdev) ++{ ++ struct device_node *np, *regulators; ++ int ret; ++ u32 dcdcfreq; ++ ++ np = of_node_get(pdev->dev.parent->of_node); ++ if (!np) ++ return 0; ++ ++ regulators = of_find_node_by_name(np, "regulators"); ++ if (!regulators) { ++ dev_err(&pdev->dev, "regulators node not found\n"); ++ return -EINVAL; ++ } ++ ++ ret = of_regulator_match(&pdev->dev, regulators, axp20x_matches, ++ ARRAY_SIZE(axp20x_matches)); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ++ ret); ++ return ret; ++ } ++ ++ dcdcfreq = 0x08; ++ of_property_read_u32(regulators, "dcdc-freq", &dcdcfreq); ++ ret = axp20x_set_dcdc_freq(pdev, dcdcfreq); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Error setting dcdc frequency: %d\n", ret); ++ return ret; ++ } ++ ++ of_node_put(regulators); ++ ++ return 0; ++} ++ ++static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 workmode) ++{ ++ unsigned int mask = AXP20X_WORKMODE_DCDC2_MASK; ++ ++ if ((id != AXP20X_DCDC2) && (id != AXP20X_DCDC3)) ++ return -EINVAL; ++ ++ if (id == AXP20X_DCDC3) ++ mask = AXP20X_WORKMODE_DCDC3_MASK; ++ ++ return regmap_update_bits(rdev->regmap, AXP20X_DCDC_MODE, mask, workmode); ++} ++ ++static int axp20x_regulator_probe(struct platform_device *pdev) ++{ ++ struct axp20x_regulators *pmic; ++ struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); ++ struct regulator_config config = { }; ++ struct regulator_init_data *init_data; ++ int ret, i; ++ u32 workmode; ++ ++ ret = axp20x_regulator_parse_dt(pdev); ++ if (ret) ++ return ret; ++ ++ pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL); ++ if (!pmic) { ++ dev_err(&pdev->dev, "Failed to alloc pmic\n"); ++ return -ENOMEM; ++ } ++ ++ pmic->axp20x = axp20x; ++ memcpy(pmic->rdesc, axp20x_regulators, sizeof(pmic->rdesc)); ++ platform_set_drvdata(pdev, pmic); ++ ++ for (i = 0; i < AXP20X_REG_ID_MAX; i++) { ++ init_data = axp20x_matches[i].init_data; ++ if (!init_data) ++ continue; ++ ++ config.dev = &pdev->dev; ++ config.init_data = init_data; ++ config.driver_data = pmic; ++ config.regmap = axp20x->regmap; ++ config.of_node = axp20x_matches[i].of_node; ++ ++ pmic->rdev[i] = regulator_register(&pmic->rdesc[i], &config); ++ if (IS_ERR(pmic->rdev[i])) { ++ ret = PTR_ERR(pmic->rdev[i]); ++ dev_err(&pdev->dev, "Failed to register %s\n", ++ pmic->rdesc[i].name); ++ ++ while (--i >= 0) ++ regulator_unregister(pmic->rdev[i]); ++ ++ return ret; ++ } ++ ++ ret = of_property_read_u32(axp20x_matches[i].of_node, "dcdc-workmode", ++ &workmode); ++ if (!ret) { ++ ret = axp20x_set_dcdc_workmode(pmic->rdev[i], i, workmode); ++ if (ret) ++ dev_err(&pdev->dev, "Failed to set workmode on %s\n", ++ pmic->rdesc[i].name); ++ } ++ } ++ ++ return 0; ++} ++ ++static int axp20x_regulator_remove(struct platform_device *pdev) ++{ ++ struct axp20x_regulators *pmic = platform_get_drvdata(pdev); ++ int i; ++ ++ for (i = 0; i < AXP20X_REG_ID_MAX; i++) ++ regulator_unregister(pmic->rdev[i]); ++ ++ return 0; ++} ++ ++static struct platform_driver axp20x_regulator_driver = { ++ .probe = axp20x_regulator_probe, ++ .remove = axp20x_regulator_remove, ++ .driver = { ++ .name = "axp20x-regulator", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init axp20x_regulator_init(void) ++{ ++ return platform_driver_register(&axp20x_regulator_driver); ++} ++ ++subsys_initcall(axp20x_regulator_init); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Carlo Caione <carlo@caione.org>"); ++MODULE_DESCRIPTION("Regulator Driver for AXP20X PMIC"); +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/213-dt-sunxi-add-x-powers.patch b/target/linux/sunxi/patches-3.14/213-dt-sunxi-add-x-powers.patch new file mode 100644 index 0000000000..3b37c47282 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/213-dt-sunxi-add-x-powers.patch @@ -0,0 +1,83 @@ +From 7b42dc4ff2cca887e0c6e1ad291d65b30e64dd92 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Wed, 5 Mar 2014 20:30:41 +0100 +Subject: [PATCH] ARM: sunxi: dt: Add x-powers-axp209.dtsi file + +This dtsi describes the axp209 PMIC, and is to be included from inside +the i2c controller node to which the axp209 is connected. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/x-powers-axp209.dtsi | 60 ++++++++++++++++++++++++++++++++++ + 1 file changed, 60 insertions(+) + create mode 100644 arch/arm/boot/dts/x-powers-axp209.dtsi + +diff --git a/arch/arm/boot/dts/x-powers-axp209.dtsi b/arch/arm/boot/dts/x-powers-axp209.dtsi +new file mode 100644 +index 0000000..118ce3a1 +--- /dev/null ++++ b/arch/arm/boot/dts/x-powers-axp209.dtsi +@@ -0,0 +1,60 @@ ++/* ++ * x-powers,axp209 common code to be include from inside the axp209 node ++ * ++ * Copyright 2014 - Carlo Caione <carlo@caione.org> ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++ compatible = "x-powers,axp209"; ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ ++ regulators { ++ dcdc-freq = "8"; ++ ++ axp_dcdc2: dcdc2 { ++ regulator-min-microvolt = <700000>; ++ regulator-max-microvolt = <2275000>; ++ dcdc-workmode = <0>; ++ regulator-always-on; ++ }; ++ ++ axp_dcdc3: dcdc3 { ++ regulator-min-microvolt = <700000>; ++ regulator-max-microvolt = <3500000>; ++ dcdc-workmode = <0>; ++ regulator-always-on; ++ }; ++ ++ axp_ldo1: ldo1 { ++ regulator-min-microvolt = <1300000>; ++ regulator-max-microvolt = <1300000>; ++ }; ++ ++ axp_ldo2: ldo2 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ axp_ldo3: ldo3 { ++ regulator-min-microvolt = <700000>; ++ regulator-max-microvolt = <3500000>; ++ }; ++ ++ axp_ldo4: ldo4 { ++ regulator-min-microvolt = <1250000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ ++ axp_ldo5: ldo5 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ }; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/214-1-dt-sun7i-add-axp209-to-cubieboard2.patch b/target/linux/sunxi/patches-3.14/214-1-dt-sun7i-add-axp209-to-cubieboard2.patch new file mode 100644 index 0000000000..a7d9c17cab --- /dev/null +++ b/target/linux/sunxi/patches-3.14/214-1-dt-sun7i-add-axp209-to-cubieboard2.patch @@ -0,0 +1,40 @@ +From c792a05efcfebcf94ba925135a778961700965f5 Mon Sep 17 00:00:00 2001 +From: Carlo Caione <carlo@caione.org> +Date: Sat, 1 Mar 2014 17:45:48 +0100 +Subject: [PATCH] ARM: sun7i: dt: Add AXP209 support to the cubieboard2 + +AXP209 is the PMU used by Cubieboard2. This patch enables the AXP209 +support in the dts file. + +This patch requires: "ARM: sun7i/sun6i: irqchip: Add irqchip driver for +NMI controller" + +Signed-off-by: Carlo Caione <carlo@caione.org> +--- + arch/arm/boot/dts/sun7i-a20-cubieboard2.dts | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts +index cd9d3c2..40e9c3a 100644 +--- a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts ++++ b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts +@@ -74,6 +74,16 @@ + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins_a>; + status = "okay"; ++ ++ axp: axp20x@34 { ++ reg = <0x34>; ++ interrupt-parent = <&nmi_intc>; ++ interrupts = <0 8>; ++ ++ axp,system-power-controller; ++ ++ /include/ "x-powers-axp209.dtsi" ++ }; + }; + + i2c1: i2c@01c2b000 { +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/214-2-dt-sun4i-add-axp209-to-boards.patch b/target/linux/sunxi/patches-3.14/214-2-dt-sun4i-add-axp209-to-boards.patch new file mode 100644 index 0000000000..43fba1abf9 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/214-2-dt-sun4i-add-axp209-to-boards.patch @@ -0,0 +1,159 @@ +From d14c5523653ca6ce9f1487922c8ab4e571d17b62 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Tue, 11 Mar 2014 16:51:44 +0100 +Subject: [PATCH] ARM: sun4i: dt: Add AXP209 support to various boards + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun4i-a10-a1000.dts | 9 +++++++++ + arch/arm/boot/dts/sun4i-a10-cubieboard.dts | 9 +++++++++ + arch/arm/boot/dts/sun4i-a10-hackberry.dts | 15 +++++++++++++++ + arch/arm/boot/dts/sun4i-a10-inet97fv2.dts | 9 +++++++++ + arch/arm/boot/dts/sun4i-a10-mini-xplus.dts | 15 +++++++++++++++ + arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts | 15 +++++++++++++++ + arch/arm/boot/dts/sun4i-a10-pcduino.dts | 9 +++++++++ + 7 files changed, 81 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10-a1000.dts b/arch/arm/boot/dts/sun4i-a10-a1000.dts +index 68b687e..99f53c0 100644 +--- a/arch/arm/boot/dts/sun4i-a10-a1000.dts ++++ b/arch/arm/boot/dts/sun4i-a10-a1000.dts +@@ -96,6 +96,15 @@ + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins_a>; + status = "okay"; ++ ++ axp: axp20x@34 { ++ reg = <0x34>; ++ interrupts = <0>; ++ ++ axp,system-power-controller; ++ ++ /include/ "x-powers-axp209.dtsi" ++ }; + }; + }; + +diff --git a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts +index 13088f0..5e4986c 100644 +--- a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts ++++ b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts +@@ -88,6 +88,15 @@ + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins_a>; + status = "okay"; ++ ++ axp: axp20x@34 { ++ reg = <0x34>; ++ interrupts = <0>; ++ ++ axp,system-power-controller; ++ ++ /include/ "x-powers-axp209.dtsi" ++ }; + }; + + i2c1: i2c@01c2b000 { +diff --git a/arch/arm/boot/dts/sun4i-a10-hackberry.dts b/arch/arm/boot/dts/sun4i-a10-hackberry.dts +index a4b05d6..18f0030 100644 +--- a/arch/arm/boot/dts/sun4i-a10-hackberry.dts ++++ b/arch/arm/boot/dts/sun4i-a10-hackberry.dts +@@ -91,6 +91,21 @@ + pinctrl-0 = <&uart0_pins_a>; + status = "okay"; + }; ++ ++ i2c0: i2c@01c2ac00 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c0_pins_a>; ++ status = "okay"; ++ ++ axp: axp20x@34 { ++ reg = <0x34>; ++ interrupts = <0>; ++ ++ axp,system-power-controller; ++ ++ /include/ "x-powers-axp209.dtsi" ++ }; ++ }; + }; + + reg_emac_3v3: emac-3v3 { +diff --git a/arch/arm/boot/dts/sun4i-a10-inet97fv2.dts b/arch/arm/boot/dts/sun4i-a10-inet97fv2.dts +index b73a070..7d3599e 100644 +diff --git a/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts b/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts +index c906171..4acddf7 100644 +--- a/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts ++++ b/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts +@@ -55,6 +55,21 @@ + pinctrl-0 = <&uart0_pins_a>; + status = "okay"; + }; ++ ++ i2c0: i2c@01c2ac00 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c0_pins_a>; ++ status = "okay"; ++ ++ axp: axp20x@34 { ++ reg = <0x34>; ++ interrupts = <0>; ++ ++ axp,system-power-controller; ++ ++ /include/ "x-powers-axp209.dtsi" ++ }; ++ }; + }; + + reg_usb1_vbus: usb1-vbus { +diff --git a/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts b/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts +index e5a2765..2ce7267 100644 +--- a/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts ++++ b/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts +@@ -90,6 +90,21 @@ + pinctrl-0 = <&uart0_pins_a>; + status = "okay"; + }; ++ ++ i2c0: i2c@01c2ac00 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c0_pins_a>; ++ status = "okay"; ++ ++ axp: axp20x@34 { ++ reg = <0x34>; ++ interrupts = <0>; ++ ++ axp,system-power-controller; ++ ++ /include/ "x-powers-axp209.dtsi" ++ }; ++ }; + }; + + leds { +diff --git a/arch/arm/boot/dts/sun4i-a10-pcduino.dts b/arch/arm/boot/dts/sun4i-a10-pcduino.dts +index 2820229..817cdca 100644 +--- a/arch/arm/boot/dts/sun4i-a10-pcduino.dts ++++ b/arch/arm/boot/dts/sun4i-a10-pcduino.dts +@@ -74,6 +74,15 @@ + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins_a>; + status = "okay"; ++ ++ axp: axp20x@34 { ++ reg = <0x34>; ++ interrupts = <0>; ++ ++ axp,system-power-controller; ++ ++ /include/ "x-powers-axp209.dtsi" ++ }; + }; + }; + +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/214-3-dt-sun7i-add-axp209-to-cubietruck.patch b/target/linux/sunxi/patches-3.14/214-3-dt-sun7i-add-axp209-to-cubietruck.patch new file mode 100644 index 0000000000..af8ca023ea --- /dev/null +++ b/target/linux/sunxi/patches-3.14/214-3-dt-sun7i-add-axp209-to-cubietruck.patch @@ -0,0 +1,56 @@ +From 886f41537ad5e873caee522704e96e844a485961 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Wed, 5 Mar 2014 20:41:17 +0100 +Subject: [PATCH] ARM: sun7i: dt: Add AXP209 support to cubietruck + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 10 ++++++++++ + arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts | 10 ++++++++++ + 2 files changed, 20 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +index 9a127d1..ebf6a2f 100644 +--- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts ++++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +@@ -137,6 +137,16 @@ + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins_a>; + status = "okay"; ++ ++ axp: axp20x@34 { ++ reg = <0x34>; ++ interrupt-parent = <&nmi_intc>; ++ interrupts = <0 8>; ++ ++ axp,system-power-controller; ++ ++ /include/ "x-powers-axp209.dtsi" ++ }; + }; + + i2c1: i2c@01c2b000 { +diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts +index 15f1f3f..926f111 100644 +--- a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts ++++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts +@@ -127,6 +127,16 @@ + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins_a>; + status = "okay"; ++ ++ axp: axp20x@34 { ++ reg = <0x34>; ++ interrupt-parent = <&nmi_intc>; ++ interrupts = <0 8>; ++ ++ axp,system-power-controller; ++ ++ /include/ "x-powers-axp209.dtsi" ++ }; + }; + + i2c1: i2c@01c2b000 { +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/215-2-dt-sun5i-add-address-and-sizecells-to-i2c.patch b/target/linux/sunxi/patches-3.14/215-2-dt-sun5i-add-address-and-sizecells-to-i2c.patch new file mode 100644 index 0000000000..5f79073e92 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/215-2-dt-sun5i-add-address-and-sizecells-to-i2c.patch @@ -0,0 +1,45 @@ +From 505ad20db47442ae2650a41eca0a7a869aace789 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Wed, 5 Mar 2014 20:40:44 +0100 +Subject: [PATCH] ARM: sun5i: dt: Add address- and size-cells info to i2c + controller nodes + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun5i-a13.dtsi | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi +index 280ffee..68ab353 100644 +--- a/arch/arm/boot/dts/sun5i-a13.dtsi ++++ b/arch/arm/boot/dts/sun5i-a13.dtsi +@@ -503,6 +503,8 @@ + clocks = <&apb1_gates 0>; + clock-frequency = <100000>; + status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; + }; + + i2c1: i2c@01c2b000 { +@@ -512,6 +514,8 @@ + clocks = <&apb1_gates 1>; + clock-frequency = <100000>; + status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; + }; + + i2c2: i2c@01c2b400 { +@@ -521,6 +525,8 @@ + clocks = <&apb1_gates 2>; + clock-frequency = <100000>; + status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; + }; + + timer@01c60000 { +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/215-3-dt-sun7i-add-address-and-sizecells-to-i2c.patch b/target/linux/sunxi/patches-3.14/215-3-dt-sun7i-add-address-and-sizecells-to-i2c.patch new file mode 100644 index 0000000000..a699644466 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/215-3-dt-sun7i-add-address-and-sizecells-to-i2c.patch @@ -0,0 +1,63 @@ +From 338560ad44f2e8b6f4eb095567830b6c78b35ba2 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Wed, 5 Mar 2014 20:40:57 +0100 +Subject: [PATCH] ARM: sun7i: dt: Add address- and size-cells info to i2c + controller nodes + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun7i-a20.dtsi | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 2e66c85..15ea85e 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -848,6 +848,8 @@ + clocks = <&apb1_gates 0>; + clock-frequency = <100000>; + status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; + }; + + i2c1: i2c@01c2b000 { +@@ -857,6 +859,8 @@ + clocks = <&apb1_gates 1>; + clock-frequency = <100000>; + status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; + }; + + i2c2: i2c@01c2b400 { +@@ -866,6 +870,8 @@ + clocks = <&apb1_gates 2>; + clock-frequency = <100000>; + status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; + }; + + i2c3: i2c@01c2b800 { +@@ -875,6 +881,8 @@ + clocks = <&apb1_gates 3>; + clock-frequency = <100000>; + status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; + }; + + i2c4: i2c@01c2bc00 { +@@ -884,6 +892,8 @@ + clocks = <&apb1_gates 15>; + clock-frequency = <100000>; + status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; + }; + + gmac: ethernet@01c50000 { +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/215-dt-sun4i-add-address-and-sizecells-to-i2c.patch b/target/linux/sunxi/patches-3.14/215-dt-sun4i-add-address-and-sizecells-to-i2c.patch new file mode 100644 index 0000000000..c8c8ae1be6 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/215-dt-sun4i-add-address-and-sizecells-to-i2c.patch @@ -0,0 +1,45 @@ +From 46d3d005e5f91d8221676cbb6307e790d4b63345 Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Wed, 5 Mar 2014 20:40:26 +0100 +Subject: [PATCH] ARM: sun4i: dt: Add address- and size-cells info to i2c + controller nodes + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun4i-a10.dtsi | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi +index 4b90a18..2a705e7 100644 +--- a/arch/arm/boot/dts/sun4i-a10.dtsi ++++ b/arch/arm/boot/dts/sun4i-a10.dtsi +@@ -712,6 +712,8 @@ + clocks = <&apb1_gates 0>; + clock-frequency = <100000>; + status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; + }; + + i2c1: i2c@01c2b000 { +@@ -721,6 +723,8 @@ + clocks = <&apb1_gates 1>; + clock-frequency = <100000>; + status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; + }; + + i2c2: i2c@01c2b400 { +@@ -730,6 +734,8 @@ + clocks = <&apb1_gates 2>; + clock-frequency = <100000>; + status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; + }; + }; + }; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/216-dt-sun7i-add-i2c-to-cubietruck.patch b/target/linux/sunxi/patches-3.14/216-dt-sun7i-add-i2c-to-cubietruck.patch new file mode 100644 index 0000000000..1e972d83ca --- /dev/null +++ b/target/linux/sunxi/patches-3.14/216-dt-sun7i-add-i2c-to-cubietruck.patch @@ -0,0 +1,48 @@ +From 6267355f0e513bed9a5009924abc7a1e7de22ab3 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Mon, 6 Jan 2014 13:58:12 +0800 +Subject: [PATCH] arm: sun7i: cubietruck: Enable the i2c controllers + +The Cubietruck makes use of the first three i2c controllers found on the +Allwinner A20; i2c-0 is used internally for the PMIC, i2c-1 is exposed on +the board headers, and i2c-2 is used for DDC on the VGA connector. This +patch enables them in the device tree. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +index 8a1009d..f9dcb61 100644 +--- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts ++++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +@@ -33,6 +33,24 @@ + pinctrl-0 = <&uart0_pins_a>; + status = "okay"; + }; ++ ++ i2c0: i2c@01c2ac00 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c0_pins_a>; ++ status = "okay"; ++ }; ++ ++ i2c1: i2c@01c2b000 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c1_pins_a>; ++ status = "okay"; ++ }; ++ ++ i2c2: i2c@01c2b400 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c2_pins_a>; ++ status = "okay"; ++ }; + }; + + leds { +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/220-clk-sunxi-remove-calls-to-clk_put.patch b/target/linux/sunxi/patches-3.14/220-clk-sunxi-remove-calls-to-clk_put.patch new file mode 100644 index 0000000000..9d87e88a38 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/220-clk-sunxi-remove-calls-to-clk_put.patch @@ -0,0 +1,41 @@ +From 81b745ee30dc7cd230f924f6263879f1b7ffbc0c Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Thu, 13 Mar 2014 16:14:13 +0100 +Subject: [PATCH] clk: sunxi: Remove calls to clk_put + +Callers of clk_put must disable the clock first. This also means that as long +as the clock is enabled the driver should hold a reference to that clock. +Hence, the call to clk_put here are bogus and should be removed. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + drivers/clk/sunxi/clk-sunxi.c | 8 ++------ + 1 file changed, 2 insertions(+), 6 deletions(-) + +diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c +index 9afd8dd..a10a645 100644 +--- a/drivers/clk/sunxi/clk-sunxi.c ++++ b/drivers/clk/sunxi/clk-sunxi.c +@@ -1325,17 +1325,13 @@ static void __init sunxi_clock_protect(void) + + /* memory bus clock - sun5i+ */ + clk = clk_get(NULL, "mbus"); +- if (!IS_ERR(clk)) { ++ if (!IS_ERR(clk)) + clk_prepare_enable(clk); +- clk_put(clk); +- } + + /* DDR clock - sun4i+ */ + clk = clk_get(NULL, "pll5_ddr"); +- if (!IS_ERR(clk)) { ++ if (!IS_ERR(clk)) + clk_prepare_enable(clk); +- clk_put(clk); +- } + } + + static void __init sunxi_init_clocks(void) +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/230-net-rfkill-changes.patch b/target/linux/sunxi/patches-3.14/230-net-rfkill-changes.patch new file mode 100644 index 0000000000..ca71a915a8 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/230-net-rfkill-changes.patch @@ -0,0 +1,271 @@ +From 5f6f6af41e39677c9b722376a4088d10732cdd44 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Fri, 17 Jan 2014 14:47:26 +0800 +Subject: [PATCH] net: rfkill: gpio: fix gpio name buffer size off by 1 + +snprintf should be passed the complete size of the buffer, including +the space for '\0'. The previous code resulted in the *_reset and +*_shutdown strings being truncated. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +--- + net/rfkill/rfkill-gpio.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c +index bd2a5b9..97ec12a 100644 +--- a/net/rfkill/rfkill-gpio.c ++++ b/net/rfkill/rfkill-gpio.c +@@ -117,8 +117,8 @@ static int rfkill_gpio_probe(struct platform_device *pdev) + if (!rfkill->shutdown_name) + return -ENOMEM; + +- snprintf(rfkill->reset_name, len + 6 , "%s_reset", rfkill->name); +- snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", rfkill->name); ++ snprintf(rfkill->reset_name, len + 7 , "%s_reset", rfkill->name); ++ snprintf(rfkill->shutdown_name, len + 10, "%s_shutdown", rfkill->name); + + rfkill->clk = devm_clk_get(&pdev->dev, clk_name); + +-- +2.0.3 + +From d91c313c08167978c3fb20b327b6a7abb7b00ffd Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Fri, 17 Jan 2014 14:47:27 +0800 +Subject: [PATCH] net: rfkill: gpio: use + clk_prepare_enable/clk_disable_unprepare + +rfkill-gpio calls clk_enable() without first calling clk_prepare(), +resulting in a warning and no effect. Switch to clk_prepare_enable() +and clk_disable_unprepare. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +--- + net/rfkill/rfkill-gpio.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c +index 97ec12a..c7081b7 100644 +--- a/net/rfkill/rfkill-gpio.c ++++ b/net/rfkill/rfkill-gpio.c +@@ -51,10 +51,10 @@ static int rfkill_gpio_set_power(void *data, bool blocked) + gpiod_set_value(rfkill->shutdown_gpio, 0); + gpiod_set_value(rfkill->reset_gpio, 0); + if (!IS_ERR(rfkill->clk) && rfkill->clk_enabled) +- clk_disable(rfkill->clk); ++ clk_disable_unprepare(rfkill->clk); + } else { + if (!IS_ERR(rfkill->clk) && !rfkill->clk_enabled) +- clk_enable(rfkill->clk); ++ clk_prepare_enable(rfkill->clk); + gpiod_set_value(rfkill->reset_gpio, 1); + gpiod_set_value(rfkill->shutdown_gpio, 1); + } +-- +2.0.3 + +From f6dc85e22d3215a26f509fb5b34ca34c56a0d8b4 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Fri, 17 Jan 2014 14:47:28 +0800 +Subject: [PATCH] net: rfkill: gpio: fix reversed clock enable state + +rfkill-gpio has clk_enabled = blocked, which is true when rfkill +blocks the device. This results in calling clock enable/disable at +the wrong time. Reversing the value fixes this. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +--- + net/rfkill/rfkill-gpio.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c +index c7081b7..3084fa3 100644 +--- a/net/rfkill/rfkill-gpio.c ++++ b/net/rfkill/rfkill-gpio.c +@@ -59,7 +59,7 @@ static int rfkill_gpio_set_power(void *data, bool blocked) + gpiod_set_value(rfkill->shutdown_gpio, 1); + } + +- rfkill->clk_enabled = blocked; ++ rfkill->clk_enabled = !blocked; + + return 0; + } +-- +2.0.3 + +From 57301a41d4a82902e967f6bd9f09ba6ca31fcbed Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Fri, 17 Jan 2014 14:47:29 +0800 +Subject: [PATCH] net: rfkill: gpio: add device tree support + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +--- + .../devicetree/bindings/rfkill/rfkill-gpio.txt | 26 ++++++++++++++++++++++ + net/rfkill/rfkill-gpio.c | 23 +++++++++++++++++++ + 2 files changed, 49 insertions(+) + create mode 100644 Documentation/devicetree/bindings/rfkill/rfkill-gpio.txt + +diff --git a/Documentation/devicetree/bindings/rfkill/rfkill-gpio.txt b/Documentation/devicetree/bindings/rfkill/rfkill-gpio.txt +new file mode 100644 +index 0000000..8a07ea4 +--- /dev/null ++++ b/Documentation/devicetree/bindings/rfkill/rfkill-gpio.txt +@@ -0,0 +1,26 @@ ++GPIO controlled RFKILL devices ++ ++Required properties: ++- compatible : Must be "rfkill-gpio". ++- rfkill-name : Name of RFKILL device ++- rfkill-type : Type of RFKILL device: 1 for WiFi, 2 for BlueTooth ++- NAME_shutdown-gpios : GPIO phandle to shutdown control ++ (phandle must be the second) ++- NAME_reset-gpios : GPIO phandle to reset control ++ ++NAME must match the rfkill-name property. NAME_shutdown-gpios or ++NAME_reset-gpios, or both, must be defined. ++ ++Optional properties: ++- clocks : phandle to clock to enable/disable ++ ++Example: ++ ++ rfkill_bt: rfkill@0 { ++ compatible = "rfkill-gpio"; ++ rfkill-name = "bluetooth"; ++ rfkill-type = <2>; ++ bluetooth_shutdown-gpios = <0>, <&pio 7 18 0>; ++ bluetooth_reset-gpios = <&pio 7 24 0>; ++ clocks = <&clk_out_a>; ++ }; +diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c +index 3084fa3..48381a8 100644 +--- a/net/rfkill/rfkill-gpio.c ++++ b/net/rfkill/rfkill-gpio.c +@@ -26,6 +26,7 @@ + #include <linux/slab.h> + #include <linux/acpi.h> + #include <linux/gpio/consumer.h> ++#include <linux/of_gpio.h> + + #include <linux/rfkill-gpio.h> + +@@ -83,6 +84,18 @@ static int rfkill_gpio_acpi_probe(struct device *dev, + return 0; + } + ++static int rfkill_gpio_dt_probe(struct device *dev, ++ struct rfkill_gpio_data *rfkill) ++{ ++ struct device_node * np = dev->of_node; ++ ++ rfkill->name = np->name; ++ of_property_read_string(np, "rfkill-name", &rfkill->name); ++ of_property_read_u32(np, "rfkill-type", &rfkill->type); ++ ++ return 0; ++} ++ + static int rfkill_gpio_probe(struct platform_device *pdev) + { + struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data; +@@ -100,6 +113,10 @@ static int rfkill_gpio_probe(struct platform_device *pdev) + ret = rfkill_gpio_acpi_probe(&pdev->dev, rfkill); + if (ret) + return ret; ++ } else if (&pdev->dev.of_node) { ++ ret = rfkill_gpio_dt_probe(&pdev->dev, rfkill); ++ if (ret) ++ return ret; + } else if (pdata) { + clk_name = pdata->power_clk_name; + rfkill->name = pdata->name; +@@ -189,6 +206,11 @@ static const struct acpi_device_id rfkill_acpi_match[] = { + { }, + }; + ++static const struct of_device_id rfkill_of_match[] = { ++ { .compatible = "rfkill-gpio", }, ++ {}, ++}; ++ + static struct platform_driver rfkill_gpio_driver = { + .probe = rfkill_gpio_probe, + .remove = rfkill_gpio_remove, +@@ -196,6 +218,7 @@ static struct platform_driver rfkill_gpio_driver = { + .name = "rfkill_gpio", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(rfkill_acpi_match), ++ .of_match_table = of_match_ptr(rfkill_of_match), + }, + }; + +-- +2.0.3 + +From 83c43937ee8c5fcb38241a8e89c2b93e5b0f9526 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Fri, 17 Jan 2014 14:47:30 +0800 +Subject: [PATCH] net: rfkill: gpio: add clock-frequency device tree property + +Some devices, such as Broadcom Bluetooth devices, require a specific +clock rate for the clock tied to the rfkill device. Add clock-frequency +property so we can specify this from the device tree. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +--- + Documentation/devicetree/bindings/rfkill/rfkill-gpio.txt | 2 ++ + net/rfkill/rfkill-gpio.c | 4 ++++ + 2 files changed, 6 insertions(+) + +diff --git a/Documentation/devicetree/bindings/rfkill/rfkill-gpio.txt b/Documentation/devicetree/bindings/rfkill/rfkill-gpio.txt +index 8a07ea4..8b8db0a 100644 +--- a/Documentation/devicetree/bindings/rfkill/rfkill-gpio.txt ++++ b/Documentation/devicetree/bindings/rfkill/rfkill-gpio.txt +@@ -13,6 +13,7 @@ NAME_reset-gpios, or both, must be defined. + + Optional properties: + - clocks : phandle to clock to enable/disable ++- clock-frequency : clock rate to set for the given clock + + Example: + +@@ -23,4 +24,5 @@ Example: + bluetooth_shutdown-gpios = <0>, <&pio 7 18 0>; + bluetooth_reset-gpios = <&pio 7 24 0>; + clocks = <&clk_out_a>; ++ clock-frequency = <32678>; + }; +diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c +index 48381a8..3092681 100644 +--- a/net/rfkill/rfkill-gpio.c ++++ b/net/rfkill/rfkill-gpio.c +@@ -40,6 +40,7 @@ struct rfkill_gpio_data { + char *reset_name; + char *shutdown_name; + struct clk *clk; ++ int clk_frequency; + + bool clk_enabled; + }; +@@ -92,6 +93,7 @@ static int rfkill_gpio_dt_probe(struct device *dev, + rfkill->name = np->name; + of_property_read_string(np, "rfkill-name", &rfkill->name); + of_property_read_u32(np, "rfkill-type", &rfkill->type); ++ of_property_read_u32(np, "clock-frequency", &rfkill->clk_frequency); + + return 0; + } +@@ -138,6 +140,8 @@ static int rfkill_gpio_probe(struct platform_device *pdev) + snprintf(rfkill->shutdown_name, len + 10, "%s_shutdown", rfkill->name); + + rfkill->clk = devm_clk_get(&pdev->dev, clk_name); ++ if (!IS_ERR(rfkill->clk) && rfkill->clk_frequency > 0) ++ clk_set_rate(rfkill->clk, rfkill->clk_frequency); + + gpio = devm_gpiod_get_index(&pdev->dev, rfkill->reset_name, 0); + if (!IS_ERR(gpio)) { +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/231-1-brcmfmac-fix-sdio-sending.patch b/target/linux/sunxi/patches-3.14/231-1-brcmfmac-fix-sdio-sending.patch new file mode 100644 index 0000000000..8f9914fb10 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/231-1-brcmfmac-fix-sdio-sending.patch @@ -0,0 +1,37 @@ +From df25859babd9c164a61e86d953d5c88400009a14 Mon Sep 17 00:00:00 2001 +From: Hante Meuleman <meuleman@broadcom.com> +Date: Wed, 29 Jan 2014 15:32:16 +0100 +Subject: [PATCH] brcmfmac: fix sdio sending of large buffers. + +the function brcmf_sdiod_ramrw is supposed to be able to send +large blobs of data. However inside the loop the skb->len field +did not correctly get reset each round. As a result only small +blobs could be sent. This patch fixes this problem. + +Reviewed-by: Arend Van Spriel <arend@broadcom.com> +Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com> +Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> +Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com> +Signed-off-by: Hante Meuleman <meuleman@broadcom.com> +Signed-off-by: Arend van Spriel <arend@broadcom.com> +Signed-off-by: John W. Linville <linville@tuxdriver.com> +--- + drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +index fa35b23..bd19323 100644 +--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c ++++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +@@ -827,7 +827,7 @@ brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, + } + if (!write) + memcpy(data, pkt->data, dsize); +- skb_trim(pkt, dsize); ++ skb_trim(pkt, 0); + + /* Adjust for next transfer (if any) */ + size -= dsize; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/231-2-brcmfmac-fix-use-of-skb-ctrlbuf-in-SDIO.patch b/target/linux/sunxi/patches-3.14/231-2-brcmfmac-fix-use-of-skb-ctrlbuf-in-SDIO.patch new file mode 100644 index 0000000000..5743e66f6c --- /dev/null +++ b/target/linux/sunxi/patches-3.14/231-2-brcmfmac-fix-use-of-skb-ctrlbuf-in-SDIO.patch @@ -0,0 +1,69 @@ +From 3eee5fd6d045dc744f98fd684258e3fdfa667fd6 Mon Sep 17 00:00:00 2001 +From: Arend van Spriel <arend@broadcom.com> +Date: Tue, 25 Feb 2014 20:30:27 +0100 +Subject: [PATCH] brcmfmac: fix use of skb control buffer in SDIO driver part + +The SDIO driver has a 16-bit field defined in the skbuff control buffer. +However, it is accessed as a u32 overwriting other control info. Another +issue is that the field is not initialized for networking packets, but +the control buffer content is unspecified as other networking layers can +use it. + +Reviewed-by: Hante Meuleman <meuleman@broadcom.com> +Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com> +Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com> +Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> +Signed-off-by: Arend van Spriel <arend@broadcom.com> +Signed-off-by: John W. Linville <linville@tuxdriver.com> +--- + drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +index ddaa9ef..e006e7c 100644 +--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c ++++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +@@ -1955,7 +1955,7 @@ static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio *bus, + memcpy(pkt_pad->data, + pkt->data + pkt->len - tail_chop, + tail_chop); +- *(u32 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop; ++ *(u16 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop; + skb_trim(pkt, pkt->len - tail_chop); + skb_trim(pkt_pad, tail_pad + tail_chop); + __skb_queue_after(pktq, pkt, pkt_pad); +@@ -2003,7 +2003,7 @@ brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, + * already properly aligned and does not + * need an sdpcm header. + */ +- if (*(u32 *)(pkt_next->cb) & ALIGN_SKB_FLAG) ++ if (*(u16 *)(pkt_next->cb) & ALIGN_SKB_FLAG) + continue; + + /* align packet data pointer */ +@@ -2067,11 +2067,11 @@ brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq) + u8 *hdr; + u32 dat_offset; + u16 tail_pad; +- u32 dummy_flags, chop_len; ++ u16 dummy_flags, chop_len; + struct sk_buff *pkt_next, *tmp, *pkt_prev; + + skb_queue_walk_safe(pktq, pkt_next, tmp) { +- dummy_flags = *(u32 *)(pkt_next->cb); ++ dummy_flags = *(u16 *)(pkt_next->cb); + if (dummy_flags & ALIGN_SKB_FLAG) { + chop_len = dummy_flags & ALIGN_SKB_CHOP_LEN_MASK; + if (chop_len) { +@@ -2554,6 +2554,8 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) + + /* Priority based enq */ + spin_lock_irqsave(&bus->txqlock, flags); ++ /* reset bus_flags in packet cb */ ++ *(u16 *)(pkt->cb) = 0; + if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) { + skb_pull(pkt, bus->tx_hdrlen); + brcmf_err("out of bus->txq !!!\n"); +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/232-1-dt-sun7i-add-wifi-to-cubietruck.patch b/target/linux/sunxi/patches-3.14/232-1-dt-sun7i-add-wifi-to-cubietruck.patch new file mode 100644 index 0000000000..2e80804891 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/232-1-dt-sun7i-add-wifi-to-cubietruck.patch @@ -0,0 +1,96 @@ +From 3e7fc7d394db0783996519f2d5affde5152a628e Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Thu, 26 Dec 2013 17:14:33 +0800 +Subject: [PATCH] ARM: dts: sun7i: add WiFi module to Cubietruck DTS + +The CubieTruck has an AMPAK AP6210 WiFi+Bluetooth module. The WiFi +part is a BCM43362 IC connected to MMC2 in the A20 SoC via SDIO. +The IC also takes a 32.768 KHz low power clock input, and a power +enable signal via GPIO. + +The WiFi module supports out-of-band interrupt signaling via GPIO, +but this is not supported in this patch. +--- + arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 47 ++++++++++++++++++++++++++++++ + 1 file changed, 47 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +index 66bb3ef..2444806 100644 +--- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts ++++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +@@ -28,6 +28,23 @@ + status = "okay"; + }; + ++ mmc3: mmc@01c12000 { ++ pinctrl-names = "default", "default"; ++ pinctrl-0 = <&mmc3_pins_a>; ++ pinctrl-1 = <&wifi_host_wake_pin>; ++ vmmc-supply = <®_vmmc3>; ++ non-removable; ++ status = "okay"; ++ ++ brcmf: bcrmf@0 { ++ /* out of band interrupt not working */ ++ /* compatible = "broadcom,bcm43362"; */ ++ interrupt-parent = <&pio>; ++ interrupts = <10 2>; /* EINT10 */ ++ status = "okay"; ++ }; ++ }; ++ + usbphy: phy@01c13400 { + usb1_vbus-supply = <®_usb1_vbus>; + usb2_vbus-supply = <®_usb2_vbus>; +@@ -56,6 +73,18 @@ + }; + + pinctrl@01c20800 { ++ mmc3_pins_a: mmc3@0 { ++ /* AP6210 requires pull-up */ ++ allwinner,pull = <1>; ++ }; ++ ++ vmmc3_pin_cubietruck: vmmc3_pin@0 { ++ allwinner,pins = "PH9"; ++ allwinner,function = "gpio_out"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ + ahci_pwr_pin_cubietruck: ahci_pwr_pin@1 { + allwinner,pins = "PH12"; + allwinner,function = "gpio_out"; +@@ -69,6 +98,13 @@ + allwinner,drive = <0>; + allwinner,pull = <0>; + }; ++ ++ wifi_host_wake_pin: wifi_host_wake_pin@0 { ++ allwinner,pins = "PH10"; ++ allwinner,function = "gpio_in"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; + }; + + uart0: serial@01c28000 { +@@ -147,4 +183,15 @@ + reg_usb2_vbus: usb2-vbus { + status = "okay"; + }; ++ ++ reg_vmmc3: vmmc3 { ++ compatible = "regulator-fixed"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&vmmc3_pin_cubietruck>; ++ regulator-name = "vmmc3"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ enable-active-high; ++ gpio = <&pio 7 9 0>; ++ }; + }; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/232-2-dt-sun7i-add-bluetooth-to-cubietruck.patch b/target/linux/sunxi/patches-3.14/232-2-dt-sun7i-add-bluetooth-to-cubietruck.patch new file mode 100644 index 0000000000..e41eb82185 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/232-2-dt-sun7i-add-bluetooth-to-cubietruck.patch @@ -0,0 +1,78 @@ +From 7ed66f828ad1ae46226ad7c1b56466d5ae6d67b9 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai <wens@csie.org> +Date: Thu, 26 Dec 2013 17:15:47 +0800 +Subject: [PATCH] ARM: dts: sun7i: add bluetooth module to CubieTruck DTS + +The CubieTruck has an AMPAK AP6210 WiFi+Bluetooth module. The +Bluetooth part is a BCM20710 IC connected to UART2 in the A20 +SoC. The IC also takes a 32.768 KHz low power clock input, a power +enable signal and a wake signal via GPIO. + +The Bluetooth module supports out-of-band interrupt signaling via +GPIO, but this is not supported in this patch. +--- + arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 36 ++++++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +index 2444806..9a127d1 100644 +--- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts ++++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +@@ -105,6 +105,20 @@ + allwinner,drive = <0>; + allwinner,pull = <0>; + }; ++ ++ bt_pwr_pin: bt_pwr_pin@0 { ++ allwinner,pins = "PH18"; ++ allwinner,function = "gpio_out"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ ++ bt_wake_pin: bt_wake_pin@0 { ++ allwinner,pins = "PH24"; ++ allwinner,function = "gpio_out"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; + }; + + uart0: serial@01c28000 { +@@ -113,6 +127,12 @@ + status = "okay"; + }; + ++ uart2: serial@01c28800 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart2_pins_a>; ++ status = "okay"; ++ }; ++ + i2c0: i2c@01c2ac00 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins_a>; +@@ -194,4 +214,20 @@ + enable-active-high; + gpio = <&pio 7 9 0>; + }; ++ ++ rfkill-switches { ++ compatible = "simple-bus"; ++ pinctrl-names = "default"; ++ ++ rfkill_bt { ++ compatible = "rfkill-gpio"; ++ pinctrl-0 = <&bt_pwr_pin>, <&clk_out_a_pins_a>; ++ rfkill-name = "bt"; ++ rfkill-type = <2>; ++ bt_shutdown-gpios = <0>, <&pio 7 18 0>; /* PH18 */ ++ bt_reset-gpios = <&pio 7 24 0>; /* PH24 */ ++ clocks = <&clk_out_a>; ++ clock-frequency = <32768>; ++ }; ++ }; + }; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/250-pwm-add-driver.patch b/target/linux/sunxi/patches-3.14/250-pwm-add-driver.patch new file mode 100644 index 0000000000..8d77a3408e --- /dev/null +++ b/target/linux/sunxi/patches-3.14/250-pwm-add-driver.patch @@ -0,0 +1,376 @@ +diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig +index 5b34ff29ea38..178b017be827 100644 +--- a/drivers/pwm/Kconfig ++++ b/drivers/pwm/Kconfig +@@ -217,6 +217,15 @@ config PWM_SPEAR + To compile this driver as a module, choose M here: the module + will be called pwm-spear. + ++config PWM_SUNXI ++ tristate "Allwinner PWM support" ++ depends on ARCH_SUNXI || COMPILE_TEST ++ help ++ Generic PWM framework driver for Allwinner SoCs. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called pwm-sunxi. ++ + config PWM_TEGRA + tristate "NVIDIA Tegra PWM support" + depends on ARCH_TEGRA +diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile +index e57d2c38a794..39997ea2e276 100644 +--- a/drivers/pwm/Makefile ++++ b/drivers/pwm/Makefile +@@ -19,6 +19,7 @@ obj-$(CONFIG_PWM_PXA) += pwm-pxa.o + obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o + obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o + obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o ++obj-$(CONFIG_PWM_SUNXI) += pwm-sunxi.o + obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o + obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o + obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o +diff --git a/drivers/pwm/pwm-sunxi.c b/drivers/pwm/pwm-sunxi.c +new file mode 100644 +index 000000000000..e7c3ca1d3c42 +--- /dev/null ++++ b/drivers/pwm/pwm-sunxi.c +@@ -0,0 +1,338 @@ ++/* ++ * Driver for Allwinner Pulse Width Modulation Controller ++ * ++ * Copyright (C) 2014 Alexandre Belloni <alexandre.belloni@free-electrons.com> ++ * ++ * Licensed under GPLv2. ++ */ ++ ++#include <linux/bitops.h> ++#include <linux/clk.h> ++#include <linux/err.h> ++#include <linux/io.h> ++#include <linux/of.h> ++#include <linux/of_device.h> ++#include <linux/platform_device.h> ++#include <linux/pwm.h> ++#include <linux/module.h> ++#include <linux/mutex.h> ++#include <linux/slab.h> ++ ++#define PWM_CTRL_REG 0x0 ++ ++#define PWM_CH_PRD_BASE 0x4 ++#define PWM_CH_PRD_OFF 0x4 ++#define PWM_CH_PRD(x) (PWM_CH_PRD_BASE + PWM_CH_PRD_OFF * (x)) ++ ++#define PWMCH_OFFSET 15 ++#define PWM_PRESCAL_MASK GENMASK(3, 0) ++#define PWM_PRESCAL_OFF 0 ++#define PWM_EN BIT(4) ++#define PWM_ACT_STATE BIT(5) ++#define PWM_CLK_GATING BIT(6) ++#define PWM_MODE BIT(7) ++#define PWM_PULSE BIT(8) ++#define PWM_BYPASS BIT(9) ++ ++#define PWM_RDY_BASE 28 ++#define PWM_RDY_OFF 1 ++#define PWM_RDY(x) BIT(PWM_RDY_BASE + PWM_RDY_OFF * (x)) ++ ++#define PWM_PRD_ACT_MASK GENMASK(7, 0) ++#define PWM_PRD(x) ((x - 1) << 16) ++#define PWM_PRD_MASK GENMASK(7, 0) ++ ++#define BIT_CH(bit, chan) (bit << (chan * PWMCH_OFFSET)) ++ ++u32 prescal_table[] = { 120, 180, 240, 360, 480, 0, 0, 0, ++ 12000, 24000, 36000, 48000, 72000, ++ 0, 0, 1 }; ++ ++struct sunxi_pwm_data { ++ bool has_rdy; ++}; ++ ++struct sunxi_pwm_chip { ++ struct pwm_chip chip; ++ struct clk *clk; ++ void __iomem *base; ++ struct mutex ctrl_lock; ++ const struct sunxi_pwm_data *data; ++}; ++ ++#define to_sunxi_pwm_chip(chip) container_of(chip, struct sunxi_pwm_chip, chip) ++ ++static inline u32 sunxi_pwm_readl(struct sunxi_pwm_chip *chip, ++ unsigned long offset) ++{ ++ return readl(chip->base + offset); ++} ++ ++static inline void sunxi_pwm_writel(struct sunxi_pwm_chip *chip, ++ unsigned long offset, unsigned long val) ++{ ++ writel(val, chip->base + offset); ++} ++ ++static int sunxi_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ++ int duty_ns, int period_ns) ++{ ++ struct sunxi_pwm_chip *sunxi_pwm = to_sunxi_pwm_chip(chip); ++ u32 clk_rate, prd, dty; ++ u64 div; ++ u32 val, clk_gate; ++ int i, ret; ++ ++ clk_rate = clk_get_rate(sunxi_pwm->clk); ++ ++ /* First, test without any divider */ ++ i = PWM_PRESCAL_MASK; ++ div = clk_rate * period_ns; ++ do_div(div, 1000000000); ++ if (div > PWM_PRD_MASK) { ++ /* Then go up from the first divider */ ++ for (i = 0; i < PWM_PRESCAL_MASK; i++) { ++ if (!prescal_table[i]) ++ continue; ++ div = clk_rate / prescal_table[i]; ++ div = div * period_ns; ++ do_div(div, 1000000000); ++ if (div <= PWM_PRD_MASK) ++ break; ++ } ++ } ++ ++ if (div > PWM_PRD_MASK) { ++ dev_err(chip->dev, "prescaler exceeds the maximum value\n"); ++ return -EINVAL; ++ } ++ ++ prd = div; ++ div *= duty_ns; ++ do_div(div, period_ns); ++ dty = div; ++ ++ ret = clk_prepare_enable(sunxi_pwm->clk); ++ if (ret) { ++ dev_err(chip->dev, "failed to enable PWM clock\n"); ++ return ret; ++ } ++ ++ mutex_lock(&sunxi_pwm->ctrl_lock); ++ val = sunxi_pwm_readl(sunxi_pwm, PWM_CTRL_REG); ++ ++ if (sunxi_pwm->data->has_rdy && (val & PWM_RDY(pwm->hwpwm))) { ++ mutex_unlock(&sunxi_pwm->ctrl_lock); ++ clk_disable_unprepare(sunxi_pwm->clk); ++ return -EBUSY; ++ } ++ ++ clk_gate = val & BIT_CH(PWM_CLK_GATING, pwm->hwpwm); ++ if (clk_gate) { ++ val &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); ++ sunxi_pwm_writel(sunxi_pwm, PWM_CTRL_REG, val); ++ } ++ ++ val = sunxi_pwm_readl(sunxi_pwm, PWM_CTRL_REG); ++ val &= ~BIT_CH(PWM_PRESCAL_MASK, pwm->hwpwm); ++ val |= i; ++ sunxi_pwm_writel(sunxi_pwm, PWM_CTRL_REG, val); ++ ++ sunxi_pwm_writel(sunxi_pwm, PWM_CH_PRD(pwm->hwpwm), dty | PWM_PRD(prd)); ++ ++ if (clk_gate) { ++ val = sunxi_pwm_readl(sunxi_pwm, PWM_CTRL_REG); ++ val |= clk_gate; ++ sunxi_pwm_writel(sunxi_pwm, PWM_CTRL_REG, val); ++ } ++ ++ mutex_unlock(&sunxi_pwm->ctrl_lock); ++ clk_disable_unprepare(sunxi_pwm->clk); ++ ++ return 0; ++} ++ ++static int sunxi_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, ++ enum pwm_polarity polarity) ++{ ++ struct sunxi_pwm_chip *sunxi_pwm = to_sunxi_pwm_chip(chip); ++ u32 val; ++ int ret; ++ ++ ret = clk_prepare_enable(sunxi_pwm->clk); ++ if (ret) { ++ dev_err(chip->dev, "failed to enable PWM clock\n"); ++ return ret; ++ } ++ ++ mutex_lock(&sunxi_pwm->ctrl_lock); ++ val = sunxi_pwm_readl(sunxi_pwm, PWM_CTRL_REG); ++ ++ if (polarity != PWM_POLARITY_NORMAL) ++ val &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm); ++ else ++ val |= BIT_CH(PWM_ACT_STATE, pwm->hwpwm); ++ ++ ++ sunxi_pwm_writel(sunxi_pwm, PWM_CTRL_REG, val); ++ ++ mutex_unlock(&sunxi_pwm->ctrl_lock); ++ clk_disable_unprepare(sunxi_pwm->clk); ++ ++ return 0; ++} ++ ++static int sunxi_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct sunxi_pwm_chip *sunxi_pwm = to_sunxi_pwm_chip(chip); ++ u32 val; ++ int ret; ++ ++ ret = clk_prepare_enable(sunxi_pwm->clk); ++ if (ret) { ++ dev_err(chip->dev, "failed to enable PWM clock\n"); ++ return ret; ++ } ++ ++ mutex_lock(&sunxi_pwm->ctrl_lock); ++ val = sunxi_pwm_readl(sunxi_pwm, PWM_CTRL_REG); ++ val |= BIT_CH(PWM_EN, pwm->hwpwm); ++ val |= BIT_CH(PWM_CLK_GATING, pwm->hwpwm); ++ sunxi_pwm_writel(sunxi_pwm, PWM_CTRL_REG, val); ++ mutex_unlock(&sunxi_pwm->ctrl_lock); ++ ++ return 0; ++} ++ ++static void sunxi_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct sunxi_pwm_chip *sunxi_pwm = to_sunxi_pwm_chip(chip); ++ u32 val; ++ ++ mutex_lock(&sunxi_pwm->ctrl_lock); ++ val = sunxi_pwm_readl(sunxi_pwm, PWM_CTRL_REG); ++ val &= ~BIT_CH(PWM_EN, pwm->hwpwm); ++ val &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); ++ sunxi_pwm_writel(sunxi_pwm, PWM_CTRL_REG, val); ++ mutex_unlock(&sunxi_pwm->ctrl_lock); ++ ++ clk_disable_unprepare(sunxi_pwm->clk); ++} ++ ++static const struct pwm_ops sunxi_pwm_ops = { ++ .config = sunxi_pwm_config, ++ .set_polarity = sunxi_pwm_set_polarity, ++ .enable = sunxi_pwm_enable, ++ .disable = sunxi_pwm_disable, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct sunxi_pwm_data sunxi_pwm_data_a10 = { ++ .has_rdy = false, ++}; ++ ++static const struct sunxi_pwm_data sunxi_pwm_data_a20 = { ++ .has_rdy = true, ++}; ++ ++static const struct of_device_id sunxi_pwm_dt_ids[] = { ++ { ++ .compatible = "allwinner,sun4i-a10-pwm", ++ .data = &sunxi_pwm_data_a10, ++ }, { ++ .compatible = "allwinner,sun7i-a20-pwm", ++ .data = &sunxi_pwm_data_a20, ++ }, { ++ /* sentinel */ ++ }, ++}; ++MODULE_DEVICE_TABLE(of, sunxi_pwm_dt_ids); ++ ++static int sunxi_pwm_probe(struct platform_device *pdev) ++{ ++ struct sunxi_pwm_chip *sunxi_pwm; ++ struct resource *res; ++ int ret; ++ ++ const struct of_device_id *match; ++ ++ match = of_match_device(sunxi_pwm_dt_ids, &pdev->dev); ++ if (!match || !match->data) ++ return -ENODEV; ++ ++ sunxi_pwm = devm_kzalloc(&pdev->dev, sizeof(*sunxi_pwm), GFP_KERNEL); ++ if (!sunxi_pwm) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ sunxi_pwm->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(sunxi_pwm->base)) ++ return PTR_ERR(sunxi_pwm->base); ++ ++ sunxi_pwm->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(sunxi_pwm->clk)) ++ return PTR_ERR(sunxi_pwm->clk); ++ ++ sunxi_pwm->chip.dev = &pdev->dev; ++ sunxi_pwm->chip.ops = &sunxi_pwm_ops; ++ ++ sunxi_pwm->chip.base = -1; ++ sunxi_pwm->chip.npwm = 2; ++ sunxi_pwm->chip.can_sleep = true; ++ sunxi_pwm->chip.of_xlate = of_pwm_xlate_with_flags; ++ sunxi_pwm->chip.of_pwm_n_cells = 3; ++ sunxi_pwm->data = match->data; ++ ++ mutex_init(&sunxi_pwm->ctrl_lock); ++ ++ ret = clk_prepare_enable(sunxi_pwm->clk); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to enable PWM clock\n"); ++ goto error; ++ } ++ ++ /* By default, the polarity is inversed, set it to normal */ ++ sunxi_pwm_writel(sunxi_pwm, PWM_CTRL_REG, ++ BIT_CH(PWM_ACT_STATE, 0) | ++ BIT_CH(PWM_ACT_STATE, 1)); ++ clk_disable_unprepare(sunxi_pwm->clk); ++ ++ ret = pwmchip_add(&sunxi_pwm->chip); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "failed to add PWM chip %d\n", ret); ++ goto error; ++ } ++ ++ platform_set_drvdata(pdev, sunxi_pwm); ++ ++ return ret; ++ ++error: ++ mutex_destroy(&sunxi_pwm->ctrl_lock); ++ clk_disable_unprepare(sunxi_pwm->clk); ++ return ret; ++} ++ ++static int sunxi_pwm_remove(struct platform_device *pdev) ++{ ++ struct sunxi_pwm_chip *sunxi_pwm = platform_get_drvdata(pdev); ++ ++ mutex_destroy(&sunxi_pwm->ctrl_lock); ++ ++ return pwmchip_remove(&sunxi_pwm->chip); ++} ++ ++static struct platform_driver sunxi_pwm_driver = { ++ .driver = { ++ .name = "sunxi-pwm", ++ .of_match_table = sunxi_pwm_dt_ids, ++ }, ++ .probe = sunxi_pwm_probe, ++ .remove = sunxi_pwm_remove, ++}; ++module_platform_driver(sunxi_pwm_driver); ++ ++MODULE_ALIAS("platform:sunxi-pwm"); ++MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>"); ++MODULE_DESCRIPTION("Allwinner PWM driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/sunxi/patches-3.14/251-1-dt-sun4i-add-pinmux-for-pwm.patch b/target/linux/sunxi/patches-3.14/251-1-dt-sun4i-add-pinmux-for-pwm.patch new file mode 100644 index 0000000000..00881f02c3 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/251-1-dt-sun4i-add-pinmux-for-pwm.patch @@ -0,0 +1,41 @@ +From 1d8e9db920352680e9091ec5d2873e90a4a53214 Mon Sep 17 00:00:00 2001 +From: Alexandre Belloni <alexandre.belloni@free-electrons.com> +Date: Mon, 28 Apr 2014 18:17:10 +0200 +Subject: [PATCH] ARM: sun4i: dt: add pinmux configuration for the PWM + +Add the pinctrl descriptions for both PWM channels of the Allwinner A10. + +Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com> +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun4i-a10.dtsi | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi +index fe845eb..8810ce4 100644 +--- a/arch/arm/boot/dts/sun4i-a10.dtsi ++++ b/arch/arm/boot/dts/sun4i-a10.dtsi +@@ -477,6 +477,20 @@ + #size-cells = <0>; + #gpio-cells = <3>; + ++ pwm0_pins_a: pwm0@0 { ++ allwinner,pins = "PB2"; ++ allwinner,function = "pwm"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ ++ pwm1_pins_a: pwm1@0 { ++ allwinner,pins = "PI3"; ++ allwinner,function = "pwm"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ + uart0_pins_a: uart0@0 { + allwinner,pins = "PB22", "PB23"; + allwinner,function = "uart0"; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/251-2-dt-sun7i-add-pinmux-for-pwm.patch b/target/linux/sunxi/patches-3.14/251-2-dt-sun7i-add-pinmux-for-pwm.patch new file mode 100644 index 0000000000..128e064046 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/251-2-dt-sun7i-add-pinmux-for-pwm.patch @@ -0,0 +1,41 @@ +From cf34231072d53e643d6ef1baa95a8e9df644542e Mon Sep 17 00:00:00 2001 +From: Alexandre Belloni <alexandre.belloni@free-electrons.com> +Date: Mon, 28 Apr 2014 18:17:12 +0200 +Subject: [PATCH] ARM: sun7i: dt: add pinmux configuration for the PWM + +Add the pinctrl descriptions for both PWM channels of the Allwinner A20. + +Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com> +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun7i-a20.dtsi | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index f9f5e0c..2eaf7c0f 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -554,6 +554,20 @@ + #size-cells = <0>; + #gpio-cells = <3>; + ++ pwm0_pins_a: pwm0@0 { ++ allwinner,pins = "PB2"; ++ allwinner,function = "pwm"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ ++ pwm1_pins_a: pwm1@0 { ++ allwinner,pins = "PI3"; ++ allwinner,function = "pwm"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ + uart0_pins_a: uart0@0 { + allwinner,pins = "PB22", "PB23"; + allwinner,function = "uart0"; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/252-1-dt-sun4i-add-pwm-support.patch b/target/linux/sunxi/patches-3.14/252-1-dt-sun4i-add-pwm-support.patch new file mode 100644 index 0000000000..fd06b0bf89 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/252-1-dt-sun4i-add-pwm-support.patch @@ -0,0 +1,35 @@ +From 25a4e0f1ac49c9ecafeba0d034806e25c491f012 Mon Sep 17 00:00:00 2001 +From: Alexandre Belloni <alexandre.belloni@free-electrons.com> +Date: Mon, 28 Apr 2014 18:17:11 +0200 +Subject: [PATCH] ARM: sun4i: dt: add PWM support + +Add the PWM bindings for the Allwinner A10. + +Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com> +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun4i-a10.dtsi | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi +index 8810ce4..4dc3761 100644 +--- a/arch/arm/boot/dts/sun4i-a10.dtsi ++++ b/arch/arm/boot/dts/sun4i-a10.dtsi +@@ -563,6 +563,14 @@ + interrupts = <24>; + }; + ++ pwm: pwm@01c20e00 { ++ compatible = "allwinner,sun4i-a10-pwm"; ++ reg = <0x01c20e00 0xc>; ++ clocks = <&osc24M>; ++ #pwm-cells = <3>; ++ status = "disabled"; ++ }; ++ + sid: eeprom@01c23800 { + compatible = "allwinner,sun4i-a10-sid"; + reg = <0x01c23800 0x10>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/252-2-dt-sun7i-add-pwm-support.patch b/target/linux/sunxi/patches-3.14/252-2-dt-sun7i-add-pwm-support.patch new file mode 100644 index 0000000000..3bb1186523 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/252-2-dt-sun7i-add-pwm-support.patch @@ -0,0 +1,35 @@ +From 216a4cc15c24d46a5b50b3f9a224576a69c1a83e Mon Sep 17 00:00:00 2001 +From: Alexandre Belloni <alexandre.belloni@free-electrons.com> +Date: Mon, 28 Apr 2014 18:17:13 +0200 +Subject: [PATCH] ARM: sun7i: dt: add PWM support + +Add the PWM bindings for the Allwinner A20. + +Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com> +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/sun7i-a20.dtsi | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 2eaf7c0f..61a4b5e 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -706,6 +706,14 @@ + interrupts = <0 24 4>; + }; + ++ pwm: pwm@01c20e00 { ++ compatible = "allwinner,sun7i-a20-pwm"; ++ reg = <0x01c20e00 0xc>; ++ clocks = <&osc24M>; ++ #pwm-cells = <3>; ++ status = "disabled"; ++ }; ++ + sid: eeprom@01c23800 { + compatible = "allwinner,sun7i-a20-sid"; + reg = <0x01c23800 0x200>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/260-dt-sun7i-enable-arm-pmu.patch b/target/linux/sunxi/patches-3.14/260-dt-sun7i-enable-arm-pmu.patch new file mode 100644 index 0000000000..41ff076c85 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/260-dt-sun7i-enable-arm-pmu.patch @@ -0,0 +1,33 @@ +From 531f223d1426a826c7da4908e1a5e8eb3d40d6ed Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Thu, 17 Apr 2014 21:54:41 +0200 +Subject: [PATCH] ARM: sun7i: Add ARM PMU in A20 DTSI + +Enable the performance monitoring unit found in the A20 SoCs. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +Reviewed-by: Hans de Goede <hdegoede@redhat.com> +--- + arch/arm/boot/dts/sun7i-a20.dtsi | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 9dd904d..f9f5e0c 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -57,6 +57,12 @@ + <1 10 0xf08>; + }; + ++ pmu { ++ compatible = "arm,cortex-a7-pmu", "arm,cortex-a15-pmu"; ++ interrupts = <0 120 4>, ++ <0 121 4>; ++ }; ++ + clocks { + #address-cells = <1>; + #size-cells = <1>; +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/270-dt-sun7i-add-ss-to-a20.patch b/target/linux/sunxi/patches-3.14/270-dt-sun7i-add-ss-to-a20.patch new file mode 100644 index 0000000000..4a5ceba82e --- /dev/null +++ b/target/linux/sunxi/patches-3.14/270-dt-sun7i-add-ss-to-a20.patch @@ -0,0 +1,23 @@ +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 6acdbdf..19b1ced 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -529,6 +529,14 @@ + status = "disabled"; + }; + ++ crypto: crypto-engine@01c15000 { ++ compatible = "allwinner,sun7i-a20-crypto"; ++ reg = <0x01c15000 0x1000>; ++ interrupts = <0 86 4>; ++ clocks = <&ahb_gates 5>, <&ss_clk>; ++ clock-names = "ahb", "mod"; ++ }; ++ + spi2: spi@01c17000 { + compatible = "allwinner,sun4i-a10-spi"; + reg = <0x01c17000 0x1000>; +-- +1.8.5.5 + +
\ No newline at end of file diff --git a/target/linux/sunxi/patches-3.14/271-crypto-add-ss.patch b/target/linux/sunxi/patches-3.14/271-crypto-add-ss.patch new file mode 100644 index 0000000000..782a5373db --- /dev/null +++ b/target/linux/sunxi/patches-3.14/271-crypto-add-ss.patch @@ -0,0 +1,1264 @@ +diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig +index 03ccdb0..a2acda4 100644 +--- a/drivers/crypto/Kconfig ++++ b/drivers/crypto/Kconfig +@@ -418,4 +418,21 @@ config CRYPTO_DEV_MXS_DCP + To compile this driver as a module, choose M here: the module + will be called mxs-dcp. + ++config CRYPTO_DEV_SUNXI_SS ++ tristate "Support for Allwinner Security System cryptographic accelerator" ++ depends on ARCH_SUNXI ++ select CRYPTO_MD5 ++ select CRYPTO_SHA1 ++ select CRYPTO_AES ++ select CRYPTO_DES ++ select CRYPTO_BLKCIPHER ++ help ++ Some Allwinner SoC have a crypto accelerator named ++ Security System. Select this if you want to use it. ++ The Security System handle AES/DES/3DES ciphers in CBC mode ++ and SHA1 and MD5 hash algorithms. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called sunxi-ss. ++ + endif # CRYPTO_HW +diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile +index 482f090..855292a 100644 +--- a/drivers/crypto/Makefile ++++ b/drivers/crypto/Makefile +@@ -23,3 +23,4 @@ obj-$(CONFIG_CRYPTO_DEV_S5P) += s5p-sss.o + obj-$(CONFIG_CRYPTO_DEV_SAHARA) += sahara.o + obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o + obj-$(CONFIG_CRYPTO_DEV_UX500) += ux500/ ++obj-$(CONFIG_CRYPTO_DEV_SUNXI_SS) += sunxi-ss/ +diff --git a/drivers/crypto/sunxi-ss/Makefile b/drivers/crypto/sunxi-ss/Makefile +new file mode 100644 +index 0000000..8bb287d +--- /dev/null ++++ b/drivers/crypto/sunxi-ss/Makefile +@@ -0,0 +1,2 @@ ++obj-$(CONFIG_CRYPTO_DEV_SUNXI_SS) += sunxi-ss.o ++sunxi-ss-y += sunxi-ss-core.o sunxi-ss-hash.o sunxi-ss-cipher.o +diff --git a/drivers/crypto/sunxi-ss/sunxi-ss-cipher.c b/drivers/crypto/sunxi-ss/sunxi-ss-cipher.c +new file mode 100644 +index 0000000..c2422f7 +--- /dev/null ++++ b/drivers/crypto/sunxi-ss/sunxi-ss-cipher.c +@@ -0,0 +1,461 @@ ++/* ++ * sunxi-ss-cipher.c - hardware cryptographic accelerator for Allwinner A20 SoC ++ * ++ * Copyright (C) 2013-2014 Corentin LABBE <clabbe.montjoie@gmail.com> ++ * ++ * This file add support for AES cipher with 128,192,256 bits ++ * keysize in CBC mode. ++ * ++ * You could find the datasheet at ++ * http://dl.linux-sunxi.org/A20/A20%20User%20Manual%202013-03-22.pdf ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++#include "sunxi-ss.h" ++ ++extern struct sunxi_ss_ctx *ss; ++ ++static int sunxi_ss_cipher(struct ablkcipher_request *areq, u32 mode) ++{ ++ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(areq); ++ struct sunxi_req_ctx *op = crypto_ablkcipher_ctx(tfm); ++ const char *cipher_type; ++ ++ cipher_type = crypto_tfm_alg_name(crypto_ablkcipher_tfm(tfm)); ++ ++ if (areq->nbytes == 0) { ++ mutex_unlock(&ss->lock); ++ return 0; ++ } ++ ++ if (areq->info == NULL) { ++ dev_err(ss->dev, "ERROR: Empty IV\n"); ++ mutex_unlock(&ss->lock); ++ return -EINVAL; ++ } ++ ++ if (areq->src == NULL || areq->dst == NULL) { ++ dev_err(ss->dev, "ERROR: Some SGs are NULL\n"); ++ mutex_unlock(&ss->lock); ++ return -EINVAL; ++ } ++ ++ if (strcmp("cbc(aes)", cipher_type) == 0) { ++ op->mode |= SS_OP_AES | SS_CBC | SS_ENABLED | mode; ++ return sunxi_ss_aes_poll(areq); ++ } ++ if (strcmp("cbc(des)", cipher_type) == 0) { ++ op->mode = SS_OP_DES | SS_CBC | SS_ENABLED | mode; ++ return sunxi_ss_des_poll(areq); ++ } ++ if (strcmp("cbc(des3_ede)", cipher_type) == 0) { ++ op->mode = SS_OP_3DES | SS_CBC | SS_ENABLED | mode; ++ return sunxi_ss_des_poll(areq); ++ } ++ dev_err(ss->dev, "ERROR: Cipher %s not handled\n", cipher_type); ++ mutex_unlock(&ss->lock); ++ return -EINVAL; ++} ++ ++int sunxi_ss_cipher_encrypt(struct ablkcipher_request *areq) ++{ ++ return sunxi_ss_cipher(areq, SS_ENCRYPTION); ++} ++ ++int sunxi_ss_cipher_decrypt(struct ablkcipher_request *areq) ++{ ++ return sunxi_ss_cipher(areq, SS_DECRYPTION); ++} ++ ++int sunxi_ss_cipher_init(struct crypto_tfm *tfm) ++{ ++ struct sunxi_req_ctx *op = crypto_tfm_ctx(tfm); ++ ++ mutex_lock(&ss->lock); ++ ++ memset(op, 0, sizeof(struct sunxi_req_ctx)); ++ return 0; ++} ++ ++int sunxi_ss_aes_poll(struct ablkcipher_request *areq) ++{ ++ u32 spaces; ++ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(areq); ++ struct sunxi_req_ctx *op = crypto_ablkcipher_ctx(tfm); ++ unsigned int ivsize = crypto_ablkcipher_ivsize(tfm); ++ /* when activating SS, the default FIFO space is 32 */ ++ u32 rx_cnt = 32; ++ u32 tx_cnt = 0; ++ u32 v; ++ int i; ++ struct scatterlist *in_sg; ++ struct scatterlist *out_sg; ++ void *src_addr; ++ void *dst_addr; ++ unsigned int ileft = areq->nbytes; ++ unsigned int oleft = areq->nbytes; ++ unsigned int sgileft = areq->src->length; ++ unsigned int sgoleft = areq->dst->length; ++ unsigned int todo; ++ u32 *src32; ++ u32 *dst32; ++ ++ in_sg = areq->src; ++ out_sg = areq->dst; ++ for (i = 0; i < op->keylen; i += 4) ++ writel(*(op->key + i/4), ss->base + SS_KEY0 + i); ++ if (areq->info != NULL) { ++ for (i = 0; i < 4 && i < ivsize / 4; i++) { ++ v = *(u32 *)(areq->info + i * 4); ++ writel(v, ss->base + SS_IV0 + i * 4); ++ } ++ } ++ writel(op->mode, ss->base + SS_CTL); ++ ++ /* If we have only one SG, we can use kmap_atomic */ ++ if (sg_next(in_sg) == NULL && sg_next(out_sg) == NULL) { ++ src_addr = kmap_atomic(sg_page(in_sg)) + in_sg->offset; ++ if (src_addr == NULL) { ++ dev_err(ss->dev, "kmap_atomic error for src SG\n"); ++ writel(0, ss->base + SS_CTL); ++ mutex_unlock(&ss->lock); ++ return -EINVAL; ++ } ++ dst_addr = kmap_atomic(sg_page(out_sg)) + out_sg->offset; ++ if (dst_addr == NULL) { ++ dev_err(ss->dev, "kmap_atomic error for dst SG\n"); ++ writel(0, ss->base + SS_CTL); ++ kunmap_atomic(src_addr); ++ mutex_unlock(&ss->lock); ++ return -EINVAL; ++ } ++ src32 = (u32 *)src_addr; ++ dst32 = (u32 *)dst_addr; ++ ileft = areq->nbytes / 4; ++ oleft = areq->nbytes / 4; ++ i = 0; ++ do { ++ if (ileft > 0 && rx_cnt > 0) { ++ todo = min(rx_cnt, ileft); ++ ileft -= todo; ++ do { ++ writel_relaxed(*src32++, ++ ss->base + ++ SS_RXFIFO); ++ todo--; ++ } while (todo > 0); ++ } ++ if (tx_cnt > 0) { ++ todo = min(tx_cnt, oleft); ++ oleft -= todo; ++ do { ++ *dst32++ = readl_relaxed(ss->base + ++ SS_TXFIFO); ++ todo--; ++ } while (todo > 0); ++ } ++ spaces = readl_relaxed(ss->base + SS_FCSR); ++ rx_cnt = SS_RXFIFO_SPACES(spaces); ++ tx_cnt = SS_TXFIFO_SPACES(spaces); ++ } while (oleft > 0); ++ writel(0, ss->base + SS_CTL); ++ kunmap_atomic(src_addr); ++ kunmap_atomic(dst_addr); ++ mutex_unlock(&ss->lock); ++ return 0; ++ } ++ ++ /* If we have more than one SG, we cannot use kmap_atomic since ++ * we hold the mapping too long ++ */ ++ src_addr = kmap(sg_page(in_sg)) + in_sg->offset; ++ if (src_addr == NULL) { ++ dev_err(ss->dev, "KMAP error for src SG\n"); ++ mutex_unlock(&ss->lock); ++ return -EINVAL; ++ } ++ dst_addr = kmap(sg_page(out_sg)) + out_sg->offset; ++ if (dst_addr == NULL) { ++ kunmap(sg_page(in_sg)); ++ dev_err(ss->dev, "KMAP error for dst SG\n"); ++ mutex_unlock(&ss->lock); ++ return -EINVAL; ++ } ++ src32 = (u32 *)src_addr; ++ dst32 = (u32 *)dst_addr; ++ ileft = areq->nbytes / 4; ++ oleft = areq->nbytes / 4; ++ sgileft = in_sg->length / 4; ++ sgoleft = out_sg->length / 4; ++ do { ++ spaces = readl_relaxed(ss->base + SS_FCSR); ++ rx_cnt = SS_RXFIFO_SPACES(spaces); ++ tx_cnt = SS_TXFIFO_SPACES(spaces); ++ todo = min3(rx_cnt, ileft, sgileft); ++ if (todo > 0) { ++ ileft -= todo; ++ sgileft -= todo; ++ } ++ while (todo > 0) { ++ writel_relaxed(*src32++, ss->base + SS_RXFIFO); ++ todo--; ++ } ++ if (in_sg != NULL && sgileft == 0 && ileft > 0) { ++ kunmap(sg_page(in_sg)); ++ in_sg = sg_next(in_sg); ++ while (in_sg != NULL && in_sg->length == 0) ++ in_sg = sg_next(in_sg); ++ if (in_sg != NULL && ileft > 0) { ++ src_addr = kmap(sg_page(in_sg)) + in_sg->offset; ++ if (src_addr == NULL) { ++ dev_err(ss->dev, "ERROR: KMAP for src SG\n"); ++ mutex_unlock(&ss->lock); ++ return -EINVAL; ++ } ++ src32 = src_addr; ++ sgileft = in_sg->length / 4; ++ } ++ } ++ /* do not test oleft since when oleft == 0 we have finished */ ++ todo = min3(tx_cnt, oleft, sgoleft); ++ if (todo > 0) { ++ oleft -= todo; ++ sgoleft -= todo; ++ } ++ while (todo > 0) { ++ *dst32++ = readl_relaxed(ss->base + SS_TXFIFO); ++ todo--; ++ } ++ if (out_sg != NULL && sgoleft == 0 && oleft >= 0) { ++ kunmap(sg_page(out_sg)); ++ out_sg = sg_next(out_sg); ++ while (out_sg != NULL && out_sg->length == 0) ++ out_sg = sg_next(out_sg); ++ if (out_sg != NULL && oleft > 0) { ++ dst_addr = kmap(sg_page(out_sg)) + ++ out_sg->offset; ++ if (dst_addr == NULL) { ++ dev_err(ss->dev, "KMAP error\n"); ++ mutex_unlock(&ss->lock); ++ return -EINVAL; ++ } ++ dst32 = dst_addr; ++ sgoleft = out_sg->length / 4; ++ } ++ } ++ } while (oleft > 0); ++ ++ writel(0, ss->base + SS_CTL); ++ mutex_unlock(&ss->lock); ++ return 0; ++} ++ ++/* Pure CPU way of doing DES/3DES with SS ++ * Since DES and 3DES SGs could be smaller than 4 bytes, I use sg_copy_to_buffer ++ * for "linearize" them. ++ * The problem with that is that I alloc (2 x areq->nbytes) for buf_in/buf_out ++ * TODO: change this system ++ * SGsrc -> buf_in -> SS -> buf_out -> SGdst */ ++int sunxi_ss_des_poll(struct ablkcipher_request *areq) ++{ ++ u32 value, spaces; ++ size_t nb_in_sg_tx, nb_in_sg_rx; ++ size_t ir, it; ++ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(areq); ++ struct sunxi_req_ctx *op = crypto_ablkcipher_ctx(tfm); ++ unsigned int ivsize = crypto_ablkcipher_ivsize(tfm); ++ u32 tx_cnt = 0; ++ u32 rx_cnt = 0; ++ u32 v; ++ int i; ++ int no_chunk = 1; ++ ++ /* if we have only SGs with size multiple of 4, ++ * we can use the SS AES function */ ++ struct scatterlist *in_sg; ++ struct scatterlist *out_sg; ++ ++ in_sg = areq->src; ++ out_sg = areq->dst; ++ ++ while (in_sg != NULL && no_chunk == 1) { ++ if ((in_sg->length % 4) != 0) ++ no_chunk = 0; ++ in_sg = sg_next(in_sg); ++ } ++ while (out_sg != NULL && no_chunk == 1) { ++ if ((out_sg->length % 4) != 0) ++ no_chunk = 0; ++ out_sg = sg_next(out_sg); ++ } ++ ++ if (no_chunk == 1) ++ return sunxi_ss_aes_poll(areq); ++ in_sg = areq->src; ++ out_sg = areq->dst; ++ ++ nb_in_sg_rx = sg_nents(in_sg); ++ nb_in_sg_tx = sg_nents(out_sg); ++ ++ mutex_lock(&ss->bufin_lock); ++ if (ss->buf_in == NULL) { ++ ss->buf_in = kmalloc(areq->nbytes, GFP_KERNEL); ++ ss->buf_in_size = areq->nbytes; ++ } else { ++ if (areq->nbytes > ss->buf_in_size) { ++ kfree(ss->buf_in); ++ ss->buf_in = kmalloc(areq->nbytes, GFP_KERNEL); ++ ss->buf_in_size = areq->nbytes; ++ } ++ } ++ if (ss->buf_in == NULL) { ++ ss->buf_in_size = 0; ++ mutex_unlock(&ss->bufin_lock); ++ dev_err(ss->dev, "Unable to allocate pages.\n"); ++ return -ENOMEM; ++ } ++ if (ss->buf_out == NULL) { ++ mutex_lock(&ss->bufout_lock); ++ ss->buf_out = kmalloc(areq->nbytes, GFP_KERNEL); ++ if (ss->buf_out == NULL) { ++ ss->buf_out_size = 0; ++ mutex_unlock(&ss->bufout_lock); ++ dev_err(ss->dev, "Unable to allocate pages.\n"); ++ return -ENOMEM; ++ } ++ ss->buf_out_size = areq->nbytes; ++ mutex_unlock(&ss->bufout_lock); ++ } else { ++ if (areq->nbytes > ss->buf_out_size) { ++ mutex_lock(&ss->bufout_lock); ++ kfree(ss->buf_out); ++ ss->buf_out = kmalloc(areq->nbytes, GFP_KERNEL); ++ if (ss->buf_out == NULL) { ++ ss->buf_out_size = 0; ++ mutex_unlock(&ss->bufout_lock); ++ dev_err(ss->dev, "Unable to allocate pages.\n"); ++ return -ENOMEM; ++ } ++ ss->buf_out_size = areq->nbytes; ++ mutex_unlock(&ss->bufout_lock); ++ } ++ } ++ ++ sg_copy_to_buffer(areq->src, nb_in_sg_rx, ss->buf_in, areq->nbytes); ++ ++ ir = 0; ++ it = 0; ++ ++ for (i = 0; i < op->keylen; i += 4) ++ writel(*(op->key + i/4), ss->base + SS_KEY0 + i); ++ if (areq->info != NULL) { ++ for (i = 0; i < 4 && i < ivsize / 4; i++) { ++ v = *(u32 *)(areq->info + i * 4); ++ writel(v, ss->base + SS_IV0 + i * 4); ++ } ++ } ++ writel(op->mode, ss->base + SS_CTL); ++ ++ do { ++ if (rx_cnt == 0 || tx_cnt == 0) { ++ spaces = readl(ss->base + SS_FCSR); ++ rx_cnt = SS_RXFIFO_SPACES(spaces); ++ tx_cnt = SS_TXFIFO_SPACES(spaces); ++ } ++ if (rx_cnt > 0 && ir < areq->nbytes) { ++ do { ++ value = *(u32 *)(ss->buf_in + ir); ++ writel(value, ss->base + SS_RXFIFO); ++ ir += 4; ++ rx_cnt--; ++ } while (rx_cnt > 0 && ir < areq->nbytes); ++ } ++ if (tx_cnt > 0 && it < areq->nbytes) { ++ do { ++ value = readl(ss->base + SS_TXFIFO); ++ *(u32 *)(ss->buf_out + it) = value; ++ it += 4; ++ tx_cnt--; ++ } while (tx_cnt > 0 && it < areq->nbytes); ++ } ++ if (ir == areq->nbytes) { ++ mutex_unlock(&ss->bufin_lock); ++ ir++; ++ } ++ } while (it < areq->nbytes); ++ ++ writel(0, ss->base + SS_CTL); ++ mutex_unlock(&ss->lock); ++ ++ /* a simple optimization, since we dont need the hardware for this copy ++ * we release the lock and do the copy. With that we gain 5/10% perf */ ++ mutex_lock(&ss->bufout_lock); ++ sg_copy_from_buffer(areq->dst, nb_in_sg_tx, ss->buf_out, areq->nbytes); ++ ++ mutex_unlock(&ss->bufout_lock); ++ return 0; ++} ++ ++/* check and set the AES key, prepare the mode to be used */ ++int sunxi_ss_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key, ++ unsigned int keylen) ++{ ++ struct sunxi_req_ctx *op = crypto_ablkcipher_ctx(tfm); ++ ++ switch (keylen) { ++ case 128 / 8: ++ op->mode = SS_AES_128BITS; ++ break; ++ case 192 / 8: ++ op->mode = SS_AES_192BITS; ++ break; ++ case 256 / 8: ++ op->mode = SS_AES_256BITS; ++ break; ++ default: ++ dev_err(ss->dev, "ERROR: Invalid keylen %u\n", keylen); ++ crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); ++ mutex_unlock(&ss->lock); ++ return -EINVAL; ++ } ++ op->keylen = keylen; ++ memcpy(op->key, key, keylen); ++ return 0; ++} ++ ++/* check and set the DES key, prepare the mode to be used */ ++int sunxi_ss_des_setkey(struct crypto_ablkcipher *tfm, const u8 *key, ++ unsigned int keylen) ++{ ++ struct sunxi_req_ctx *op = crypto_ablkcipher_ctx(tfm); ++ ++ if (keylen != DES_KEY_SIZE) { ++ dev_err(ss->dev, "Invalid keylen %u\n", keylen); ++ crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); ++ mutex_unlock(&ss->lock); ++ return -EINVAL; ++ } ++ op->keylen = keylen; ++ memcpy(op->key, key, keylen); ++ return 0; ++} ++ ++/* check and set the 3DES key, prepare the mode to be used */ ++int sunxi_ss_des3_setkey(struct crypto_ablkcipher *tfm, const u8 *key, ++ unsigned int keylen) ++{ ++ struct sunxi_req_ctx *op = crypto_ablkcipher_ctx(tfm); ++ ++ if (keylen != 3 * DES_KEY_SIZE) { ++ dev_err(ss->dev, "Invalid keylen %u\n", keylen); ++ crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); ++ mutex_unlock(&ss->lock); ++ return -EINVAL; ++ } ++ op->keylen = keylen; ++ memcpy(op->key, key, keylen); ++ return 0; ++} +diff --git a/drivers/crypto/sunxi-ss/sunxi-ss-core.c b/drivers/crypto/sunxi-ss/sunxi-ss-core.c +new file mode 100644 +index 0000000..c76016e +--- /dev/null ++++ b/drivers/crypto/sunxi-ss/sunxi-ss-core.c +@@ -0,0 +1,308 @@ ++/* ++ * sunxi-ss.c - hardware cryptographic accelerator for Allwinner A20 SoC ++ * ++ * Copyright (C) 2013-2014 Corentin LABBE <clabbe.montjoie@gmail.com> ++ * ++ * Core file which registers crypto algorithms supported by the SS. ++ * ++ * You could find the datasheet at ++ * http://dl.linux-sunxi.org/A20/A20%20User%20Manual%202013-03-22.pdf ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++#include <linux/clk.h> ++#include <linux/crypto.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <crypto/scatterwalk.h> ++#include <linux/scatterlist.h> ++#include <linux/interrupt.h> ++#include <linux/delay.h> ++ ++#include "sunxi-ss.h" ++ ++struct sunxi_ss_ctx *ss; ++ ++/* General notes: ++ * I cannot use a key/IV cache because each time one of these change ALL stuff ++ * need to be re-writed (rewrite SS_KEYX ans SS_IVX). ++ * And for example, with dm-crypt IV changes on each request. ++ * ++ * After each request the device must be disabled with a write of 0 in SS_CTL ++ * ++ * For performance reason, we use writel_relaxed/read_relaxed for all ++ * operations on RX and TX FIFO and also SS_FCSR. ++ * For all other registers, we use writel/readl. ++ * See http://permalink.gmane.org/gmane.linux.ports.arm.kernel/117644 ++ * and http://permalink.gmane.org/gmane.linux.ports.arm.kernel/117640 ++ * */ ++ ++static struct ahash_alg sunxi_md5_alg = { ++ .init = sunxi_hash_init, ++ .update = sunxi_hash_update, ++ .final = sunxi_hash_final, ++ .finup = sunxi_hash_finup, ++ .digest = sunxi_hash_digest, ++ .halg = { ++ .digestsize = MD5_DIGEST_SIZE, ++ .base = { ++ .cra_name = "md5", ++ .cra_driver_name = "md5-sunxi-ss", ++ .cra_priority = 300, ++ .cra_alignmask = 3, ++ .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC, ++ .cra_blocksize = MD5_HMAC_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct sunxi_req_ctx), ++ .cra_module = THIS_MODULE, ++ .cra_type = &crypto_ahash_type ++ } ++ } ++}; ++static struct ahash_alg sunxi_sha1_alg = { ++ .init = sunxi_hash_init, ++ .update = sunxi_hash_update, ++ .final = sunxi_hash_final, ++ .finup = sunxi_hash_finup, ++ .digest = sunxi_hash_digest, ++ .halg = { ++ .digestsize = SHA1_DIGEST_SIZE, ++ .base = { ++ .cra_name = "sha1", ++ .cra_driver_name = "sha1-sunxi-ss", ++ .cra_priority = 300, ++ .cra_alignmask = 3, ++ .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC, ++ .cra_blocksize = SHA1_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct sunxi_req_ctx), ++ .cra_module = THIS_MODULE, ++ .cra_type = &crypto_ahash_type ++ } ++ } ++}; ++ ++static struct crypto_alg sunxi_cipher_algs[] = { ++{ ++ .cra_name = "cbc(aes)", ++ .cra_driver_name = "cbc-aes-sunxi-ss", ++ .cra_priority = 300, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER, ++ .cra_ctxsize = sizeof(struct sunxi_req_ctx), ++ .cra_module = THIS_MODULE, ++ .cra_alignmask = 3, ++ .cra_type = &crypto_ablkcipher_type, ++ .cra_init = sunxi_ss_cipher_init, ++ .cra_u = { ++ .ablkcipher = { ++ .min_keysize = AES_MIN_KEY_SIZE, ++ .max_keysize = AES_MAX_KEY_SIZE, ++ .ivsize = AES_BLOCK_SIZE, ++ .setkey = sunxi_ss_aes_setkey, ++ .encrypt = sunxi_ss_cipher_encrypt, ++ .decrypt = sunxi_ss_cipher_decrypt, ++ } ++ } ++}, { ++ .cra_name = "cbc(des)", ++ .cra_driver_name = "cbc-des-sunxi-ss", ++ .cra_priority = 300, ++ .cra_blocksize = DES_BLOCK_SIZE, ++ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER, ++ .cra_ctxsize = sizeof(struct sunxi_req_ctx), ++ .cra_module = THIS_MODULE, ++ .cra_alignmask = 3, ++ .cra_type = &crypto_ablkcipher_type, ++ .cra_init = sunxi_ss_cipher_init, ++ .cra_u.ablkcipher = { ++ .min_keysize = DES_KEY_SIZE, ++ .max_keysize = DES_KEY_SIZE, ++ .ivsize = DES_BLOCK_SIZE, ++ .setkey = sunxi_ss_des_setkey, ++ .encrypt = sunxi_ss_cipher_encrypt, ++ .decrypt = sunxi_ss_cipher_decrypt, ++ } ++}, { ++ .cra_name = "cbc(des3_ede)", ++ .cra_driver_name = "cbc-des3-sunxi-ss", ++ .cra_priority = 300, ++ .cra_blocksize = DES3_EDE_BLOCK_SIZE, ++ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER, ++ .cra_ctxsize = sizeof(struct sunxi_req_ctx), ++ .cra_module = THIS_MODULE, ++ .cra_alignmask = 3, ++ .cra_type = &crypto_ablkcipher_type, ++ .cra_init = sunxi_ss_cipher_init, ++ .cra_u.ablkcipher = { ++ .min_keysize = DES3_EDE_KEY_SIZE, ++ .max_keysize = DES3_EDE_KEY_SIZE, ++ .ivsize = DES3_EDE_BLOCK_SIZE, ++ .setkey = sunxi_ss_des3_setkey, ++ .encrypt = sunxi_ss_cipher_encrypt, ++ .decrypt = sunxi_ss_cipher_decrypt, ++ } ++} ++}; ++ ++static int sunxi_ss_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ u32 v; ++ int err; ++ unsigned long cr; ++ const unsigned long cr_ahb = 24 * 1000 * 1000; ++ const unsigned long cr_mod = 150 * 1000 * 1000; ++ ++ if (!pdev->dev.of_node) ++ return -ENODEV; ++ ++ ss = devm_kzalloc(&pdev->dev, sizeof(*ss), GFP_KERNEL); ++ if (ss == NULL) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ss->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(ss->base)) { ++ dev_err(&pdev->dev, "Cannot request MMIO\n"); ++ return PTR_ERR(ss->base); ++ } ++ ++ ss->ssclk = devm_clk_get(&pdev->dev, "mod"); ++ if (IS_ERR(ss->ssclk)) { ++ err = PTR_ERR(ss->ssclk); ++ dev_err(&pdev->dev, "Cannot get SS clock err=%d\n", err); ++ return err; ++ } ++ dev_dbg(&pdev->dev, "clock ss acquired\n"); ++ ++ ss->busclk = devm_clk_get(&pdev->dev, "ahb"); ++ if (IS_ERR(ss->busclk)) { ++ err = PTR_ERR(ss->busclk); ++ dev_err(&pdev->dev, "Cannot get AHB SS clock err=%d\n", err); ++ return err; ++ } ++ dev_dbg(&pdev->dev, "clock ahb_ss acquired\n"); ++ ++ /* Enable the clocks */ ++ err = clk_prepare_enable(ss->busclk); ++ if (err != 0) { ++ dev_err(&pdev->dev, "Cannot prepare_enable busclk\n"); ++ return err; ++ } ++ err = clk_prepare_enable(ss->ssclk); ++ if (err != 0) { ++ dev_err(&pdev->dev, "Cannot prepare_enable ssclk\n"); ++ clk_disable_unprepare(ss->busclk); ++ return err; ++ } ++ ++ /* Check that clock have the correct rates gived in the datasheet */ ++ /* Try to set the clock to the maximum allowed */ ++ err = clk_set_rate(ss->ssclk, cr_mod); ++ if (err != 0) { ++ dev_err(&pdev->dev, "Cannot set clock rate to ssclk\n"); ++ clk_disable_unprepare(ss->ssclk); ++ clk_disable_unprepare(ss->busclk); ++ return err; ++ } ++ cr = clk_get_rate(ss->busclk); ++ if (cr >= cr_ahb) ++ dev_dbg(&pdev->dev, "Clock bus %lu (%lu MHz) (must be >= %lu)\n", ++ cr, cr / 1000000, cr_ahb); ++ else ++ dev_warn(&pdev->dev, "Clock bus %lu (%lu MHz) (must be >= %lu)\n", ++ cr, cr / 1000000, cr_ahb); ++ cr = clk_get_rate(ss->ssclk); ++ if (cr == cr_mod) ++ dev_dbg(&pdev->dev, "Clock ss %lu (%lu MHz) (must be <= %lu)\n", ++ cr, cr / 1000000, cr_mod); ++ else { ++ dev_warn(&pdev->dev, "Clock ss is at %lu (%lu MHz) (must be <= %lu)\n", ++ cr, cr / 1000000, cr_mod); ++ } ++ ++ /* TODO Does this information could be usefull ? */ ++ writel(SS_ENABLED, ss->base + SS_CTL); ++ v = readl(ss->base + SS_CTL); ++ v >>= 16; ++ v &= 0x07; ++ dev_info(&pdev->dev, "Die ID %d\n", v); ++ writel(0, ss->base + SS_CTL); ++ ++ ss->dev = &pdev->dev; ++ ++ mutex_init(&ss->lock); ++ mutex_init(&ss->bufin_lock); ++ mutex_init(&ss->bufout_lock); ++ ++ err = crypto_register_ahash(&sunxi_md5_alg); ++ if (err) ++ goto error_md5; ++ err = crypto_register_ahash(&sunxi_sha1_alg); ++ if (err) ++ goto error_sha1; ++ err = crypto_register_algs(sunxi_cipher_algs, ++ ARRAY_SIZE(sunxi_cipher_algs)); ++ if (err) ++ goto error_ciphers; ++ ++ return 0; ++error_ciphers: ++ crypto_unregister_ahash(&sunxi_sha1_alg); ++error_sha1: ++ crypto_unregister_ahash(&sunxi_md5_alg); ++error_md5: ++ clk_disable_unprepare(ss->ssclk); ++ clk_disable_unprepare(ss->busclk); ++ return err; ++} ++ ++static int __exit sunxi_ss_remove(struct platform_device *pdev) ++{ ++ if (!pdev->dev.of_node) ++ return 0; ++ ++ crypto_unregister_ahash(&sunxi_md5_alg); ++ crypto_unregister_ahash(&sunxi_sha1_alg); ++ crypto_unregister_algs(sunxi_cipher_algs, ++ ARRAY_SIZE(sunxi_cipher_algs)); ++ ++ if (ss->buf_in != NULL) ++ kfree(ss->buf_in); ++ if (ss->buf_out != NULL) ++ kfree(ss->buf_out); ++ ++ writel(0, ss->base + SS_CTL); ++ clk_disable_unprepare(ss->busclk); ++ clk_disable_unprepare(ss->ssclk); ++ return 0; ++} ++ ++/*============================================================================*/ ++/*============================================================================*/ ++static const struct of_device_id a20ss_crypto_of_match_table[] = { ++ { .compatible = "allwinner,sun7i-a20-crypto" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, a20ss_crypto_of_match_table); ++ ++static struct platform_driver sunxi_ss_driver = { ++ .probe = sunxi_ss_probe, ++ .remove = __exit_p(sunxi_ss_remove), ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "sunxi-ss", ++ .of_match_table = a20ss_crypto_of_match_table, ++ }, ++}; ++ ++module_platform_driver(sunxi_ss_driver); ++ ++MODULE_DESCRIPTION("Allwinner Security System cryptographic accelerator"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Corentin LABBE <clabbe.montjoie@gmail.com>"); +diff --git a/drivers/crypto/sunxi-ss/sunxi-ss-hash.c b/drivers/crypto/sunxi-ss/sunxi-ss-hash.c +new file mode 100644 +index 0000000..6412bfb +--- /dev/null ++++ b/drivers/crypto/sunxi-ss/sunxi-ss-hash.c +@@ -0,0 +1,241 @@ ++/* ++ * sunxi-ss-hash.c - hardware cryptographic accelerator for Allwinner A20 SoC ++ * ++ * Copyright (C) 2013-2014 Corentin LABBE <clabbe.montjoie@gmail.com> ++ * ++ * This file add support for MD5 and SHA1. ++ * ++ * You could find the datasheet at ++ * http://dl.linux-sunxi.org/A20/A20%20User%20Manual%202013-03-22.pdf ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++#include "sunxi-ss.h" ++ ++extern struct sunxi_ss_ctx *ss; ++ ++/* sunxi_hash_init: initialize request context ++ * Activate the SS, and configure it for MD5 or SHA1 ++ */ ++int sunxi_hash_init(struct ahash_request *areq) ++{ ++ const char *hash_type; ++ struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); ++ struct sunxi_req_ctx *op = crypto_ahash_ctx(tfm); ++ ++ mutex_lock(&ss->lock); ++ ++ hash_type = crypto_tfm_alg_name(areq->base.tfm); ++ ++ op->byte_count = 0; ++ op->nbwait = 0; ++ op->waitbuf = 0; ++ ++ /* Enable and configure SS for MD5 or SHA1 */ ++ if (strcmp(hash_type, "sha1") == 0) ++ op->mode = SS_OP_SHA1; ++ else ++ op->mode = SS_OP_MD5; ++ ++ writel(op->mode | SS_ENABLED, ss->base + SS_CTL); ++ return 0; ++} ++ ++/* ++ * sunxi_hash_update: update hash engine ++ * ++ * Could be used for both SHA1 and MD5 ++ * Write data by step of 32bits and put then in the SS. ++ * The remaining data is stored (nbwait bytes) in op->waitbuf ++ * As an optimisation, we do not check RXFIFO_SPACES, since SS handle ++ * the FIFO faster than our writes ++ */ ++int sunxi_hash_update(struct ahash_request *areq) ++{ ++ u32 v; ++ unsigned int i = 0;/* bytes read, to be compared to areq->nbytes */ ++ struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); ++ struct sunxi_req_ctx *op = crypto_ahash_ctx(tfm); ++ struct scatterlist *in_sg; ++ unsigned int in_i = 0;/* advancement in the current SG */ ++ void *src_addr; ++ ++ u8 *waitbuf = (u8 *)(&op->waitbuf); ++ ++ if (areq->nbytes == 0) ++ return 0; ++ ++ in_sg = areq->src; ++ do { ++ src_addr = kmap(sg_page(in_sg)) + in_sg->offset; ++ /* step 1, if some bytes remains from last SG, ++ * try to complete them to 4 and sent its */ ++ if (op->nbwait > 0) { ++ while (op->nbwait < 4 && i < areq->nbytes && ++ in_i < in_sg->length) { ++ waitbuf[op->nbwait] = *(u8 *)(src_addr + in_i); ++ i++; ++ in_i++; ++ op->nbwait++; ++ } ++ if (op->nbwait == 4) { ++ writel(op->waitbuf, ss->base + SS_RXFIFO); ++ op->byte_count += 4; ++ op->nbwait = 0; ++ op->waitbuf = 0; ++ } ++ } ++ /* step 2, main loop, read data 4bytes at a time */ ++ while (i < areq->nbytes && areq->nbytes - i >= 4 && ++ in_i < in_sg->length && ++ in_sg->length - in_i >= 4) { ++ v = *(u32 *)(src_addr + in_i); ++ writel_relaxed(v, ss->base + SS_RXFIFO); ++ i += 4; ++ op->byte_count += 4; ++ in_i += 4; ++ } ++ /* step 3, if we have less than 4 bytes, copy them in waitbuf ++ * no need to check for op->nbwait < 4 since we cannot have ++ * more than 4 bytes remaining */ ++ if (in_i < in_sg->length && in_sg->length - in_i < 4 && ++ i < areq->nbytes) { ++ do { ++ waitbuf[op->nbwait] = *(u8 *)(src_addr + in_i); ++ op->nbwait++; ++ in_i++; ++ i++; ++ } while (in_i < in_sg->length && i < areq->nbytes); ++ } ++ /* we have finished the current SG, try next one */ ++ kunmap(sg_page(in_sg)); ++ in_sg = sg_next(in_sg); ++ in_i = 0; ++ } while (in_sg != NULL && i < areq->nbytes); ++ return 0; ++} ++ ++/* ++ * sunxi_hash_final: finalize hashing operation ++ * ++ * If we have some remaining bytes, send it. ++ * Then ask the SS for finalizing the hash ++ */ ++int sunxi_hash_final(struct ahash_request *areq) ++{ ++ u32 v; ++ unsigned int i; ++ int zeros; ++ unsigned int index, padlen; ++ __be64 bits; ++ struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); ++ struct sunxi_req_ctx *op = crypto_ahash_ctx(tfm); ++ ++ if (op->nbwait > 0) { ++ op->waitbuf |= ((1 << 7) << (op->nbwait * 8)); ++ writel(op->waitbuf, ss->base + SS_RXFIFO); ++ } else { ++ writel((1 << 7), ss->base + SS_RXFIFO); ++ } ++ ++ /* number of space to pad to obtain 64o minus 8(size) minus 4 (final 1) ++ * example len=0 ++ * example len=56 ++ * */ ++ ++ /* we have already send 4 more byte of which nbwait data */ ++ if (op->mode == SS_OP_MD5) { ++ index = (op->byte_count + 4) & 0x3f; ++ op->byte_count += op->nbwait; ++ if (index > 56) ++ zeros = (120 - index) / 4; ++ else ++ zeros = (56 - index) / 4; ++ } else { ++ op->byte_count += op->nbwait; ++ index = op->byte_count & 0x3f; ++ padlen = (index < 56) ? (56 - index) : ((64+56) - index); ++ zeros = (padlen - 1) / 4; ++ } ++ for (i = 0; i < zeros; i++) ++ writel(0, ss->base + SS_RXFIFO); ++ ++ /* write the lenght */ ++ if (op->mode == SS_OP_SHA1) { ++ bits = cpu_to_be64(op->byte_count << 3); ++ writel(bits & 0xffffffff, ss->base + SS_RXFIFO); ++ writel((bits >> 32) & 0xffffffff, ss->base + SS_RXFIFO); ++ } else { ++ writel((op->byte_count << 3) & 0xffffffff, ++ ss->base + SS_RXFIFO); ++ writel((op->byte_count >> 29) & 0xffffffff, ++ ss->base + SS_RXFIFO); ++ } ++ ++ /* stop the hashing */ ++ v = readl(ss->base + SS_CTL); ++ v |= SS_DATA_END; ++ writel(v, ss->base + SS_CTL); ++ ++ /* check the end */ ++ /* The timeout could happend only in case of bad overcloking */ ++#define SS_TIMEOUT 100 ++ i = 0; ++ do { ++ v = readl(ss->base + SS_CTL); ++ i++; ++ } while (i < SS_TIMEOUT && (v & SS_DATA_END) > 0); ++ if (i >= SS_TIMEOUT) { ++ dev_err(ss->dev, "ERROR: hash end timeout %d>%d\n", ++ i, SS_TIMEOUT); ++ writel(0, ss->base + SS_CTL); ++ mutex_unlock(&ss->lock); ++ return -1; ++ } ++ ++ if (op->mode == SS_OP_SHA1) { ++ for (i = 0; i < 5; i++) { ++ v = cpu_to_be32(readl(ss->base + SS_MD0 + i * 4)); ++ memcpy(areq->result + i * 4, &v, 4); ++ } ++ } else { ++ for (i = 0; i < 4; i++) { ++ v = readl(ss->base + SS_MD0 + i * 4); ++ memcpy(areq->result + i * 4, &v, 4); ++ } ++ } ++ writel(0, ss->base + SS_CTL); ++ mutex_unlock(&ss->lock); ++ return 0; ++} ++ ++/* sunxi_hash_finup: finalize hashing operation after an update */ ++int sunxi_hash_finup(struct ahash_request *areq) ++{ ++ int err; ++ ++ err = sunxi_hash_update(areq); ++ if (err != 0) ++ return err; ++ ++ return sunxi_hash_final(areq); ++} ++ ++/* combo of init/update/final functions */ ++int sunxi_hash_digest(struct ahash_request *areq) ++{ ++ int err; ++ ++ err = sunxi_hash_init(areq); ++ if (err != 0) ++ return err; ++ ++ err = sunxi_hash_update(areq); ++ if (err != 0) ++ return err; ++ ++ return sunxi_hash_final(areq); ++} +diff --git a/drivers/crypto/sunxi-ss/sunxi-ss.h b/drivers/crypto/sunxi-ss/sunxi-ss.h +new file mode 100644 +index 0000000..94aca20 +--- /dev/null ++++ b/drivers/crypto/sunxi-ss/sunxi-ss.h +@@ -0,0 +1,183 @@ ++/* ++ * sunxi-ss.c - hardware cryptographic accelerator for Allwinner A20 SoC ++ * ++ * Copyright (C) 2013-2014 Corentin LABBE <clabbe.montjoie@gmail.com> ++ * ++ * Support AES cipher with 128,192,256 bits keysize. ++ * Support MD5 and SHA1 hash algorithms. ++ * Support DES and 3DES ++ * Support PRNG ++ * ++ * You could find the datasheet at ++ * http://dl.linux-sunxi.org/A20/A20%20User%20Manual%202013-03-22.pdf ++ * ++ * ++ * Licensed under the GPL-2. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/crypto.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <crypto/scatterwalk.h> ++#include <linux/scatterlist.h> ++#include <linux/interrupt.h> ++#include <linux/delay.h> ++#include <crypto/md5.h> ++#include <crypto/sha.h> ++#include <crypto/hash.h> ++#include <crypto/internal/hash.h> ++#include <crypto/aes.h> ++#include <crypto/des.h> ++#include <crypto/internal/rng.h> ++ ++#define SS_CTL 0x00 ++#define SS_KEY0 0x04 ++#define SS_KEY1 0x08 ++#define SS_KEY2 0x0C ++#define SS_KEY3 0x10 ++#define SS_KEY4 0x14 ++#define SS_KEY5 0x18 ++#define SS_KEY6 0x1C ++#define SS_KEY7 0x20 ++ ++#define SS_IV0 0x24 ++#define SS_IV1 0x28 ++#define SS_IV2 0x2C ++#define SS_IV3 0x30 ++ ++#define SS_CNT0 0x34 ++#define SS_CNT1 0x38 ++#define SS_CNT2 0x3C ++#define SS_CNT3 0x40 ++ ++#define SS_FCSR 0x44 ++#define SS_ICSR 0x48 ++ ++#define SS_MD0 0x4C ++#define SS_MD1 0x50 ++#define SS_MD2 0x54 ++#define SS_MD3 0x58 ++#define SS_MD4 0x5C ++ ++#define SS_RXFIFO 0x200 ++#define SS_TXFIFO 0x204 ++ ++/* SS_CTL configuration values */ ++ ++/* PRNG generator mode - bit 15 */ ++#define SS_PRNG_ONESHOT (0 << 15) ++#define SS_PRNG_CONTINUE (1 << 15) ++ ++/* SS operation mode - bits 12-13 */ ++#define SS_ECB (0 << 12) ++#define SS_CBC (1 << 12) ++#define SS_CNT (2 << 12) ++ ++/* Counter width for CNT mode - bits 10-11 */ ++#define SS_CNT_16BITS (0 << 10) ++#define SS_CNT_32BITS (1 << 10) ++#define SS_CNT_64BITS (2 << 10) ++ ++/* Key size for AES - bits 8-9 */ ++#define SS_AES_128BITS (0 << 8) ++#define SS_AES_192BITS (1 << 8) ++#define SS_AES_256BITS (2 << 8) ++ ++/* Operation direction - bit 7 */ ++#define SS_ENCRYPTION (0 << 7) ++#define SS_DECRYPTION (1 << 7) ++ ++/* SS Method - bits 4-6 */ ++#define SS_OP_AES (0 << 4) ++#define SS_OP_DES (1 << 4) ++#define SS_OP_3DES (2 << 4) ++#define SS_OP_SHA1 (3 << 4) ++#define SS_OP_MD5 (4 << 4) ++#define SS_OP_PRNG (5 << 4) ++ ++/* Data end bit - bit 2 */ ++#define SS_DATA_END (1 << 2) ++ ++/* PRNG start bit - bit 1 */ ++#define SS_PRNG_START (1 << 1) ++ ++/* SS Enable bit - bit 0 */ ++#define SS_DISABLED (0 << 0) ++#define SS_ENABLED (1 << 0) ++ ++/* SS_FCSR configuration values */ ++/* RX FIFO status - bit 30 */ ++#define SS_RXFIFO_FREE (1 << 30) ++ ++/* RX FIFO empty spaces - bits 24-29 */ ++#define SS_RXFIFO_SPACES(val) (((val) >> 24) & 0x3f) ++ ++/* TX FIFO status - bit 22 */ ++#define SS_TXFIFO_AVAILABLE (1 << 22) ++ ++/* TX FIFO available spaces - bits 16-21 */ ++#define SS_TXFIFO_SPACES(val) (((val) >> 16) & 0x3f) ++ ++#define SS_RXFIFO_EMP_INT_PENDING (1 << 10) ++#define SS_TXFIFO_AVA_INT_PENDING (1 << 8) ++#define SS_RXFIFO_EMP_INT_ENABLE (1 << 2) ++#define SS_TXFIFO_AVA_INT_ENABLE (1 << 0) ++ ++/* SS_ICSR configuration values */ ++#define SS_ICS_DRQ_ENABLE (1 << 4) ++ ++struct sunxi_ss_ctx { ++ void __iomem *base; ++ int irq; ++ struct clk *busclk; ++ struct clk *ssclk; ++ struct device *dev; ++ struct resource *res; ++ void *buf_in; /* pointer to data to be uploaded to the device */ ++ size_t buf_in_size; /* size of buf_in */ ++ void *buf_out; ++ size_t buf_out_size; ++ struct mutex lock; /* control the use of the device */ ++ struct mutex bufout_lock; /* control the use of buf_out*/ ++ struct mutex bufin_lock; /* control the sue of buf_in*/ ++}; ++ ++struct sunxi_req_ctx { ++ u32 key[AES_MAX_KEY_SIZE / 4];/* divided by sizeof(u32) */ ++ u32 keylen; ++ u32 mode; ++ u64 byte_count; /* number of bytes "uploaded" to the device */ ++ u32 waitbuf; /* a partial word waiting to be completed and ++ uploaded to the device */ ++ /* number of bytes to be uploaded in the waitbuf word */ ++ unsigned int nbwait; ++}; ++ ++#define SS_SEED_LEN (192/8) ++#define SS_DATA_LEN (160/8) ++ ++struct prng_context { ++ u32 seed[SS_SEED_LEN/4]; ++ unsigned int slen; ++}; ++ ++int sunxi_hash_init(struct ahash_request *areq); ++int sunxi_hash_update(struct ahash_request *areq); ++int sunxi_hash_final(struct ahash_request *areq); ++int sunxi_hash_finup(struct ahash_request *areq); ++int sunxi_hash_digest(struct ahash_request *areq); ++ ++int sunxi_ss_aes_poll(struct ablkcipher_request *areq); ++int sunxi_ss_des_poll(struct ablkcipher_request *areq); ++int sunxi_ss_cipher_init(struct crypto_tfm *tfm); ++int sunxi_ss_cipher_encrypt(struct ablkcipher_request *areq); ++int sunxi_ss_cipher_decrypt(struct ablkcipher_request *areq); ++int sunxi_ss_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key, ++ unsigned int keylen); ++int sunxi_ss_des_setkey(struct crypto_ablkcipher *tfm, const u8 *key, ++ unsigned int keylen); ++int sunxi_ss_des3_setkey(struct crypto_ablkcipher *tfm, const u8 *key, ++ unsigned int keylen); +-- +1.8.5.5 + +
\ No newline at end of file diff --git a/target/linux/sunxi/patches-3.14/300-dt-sun7i-add-pcduino3.patch b/target/linux/sunxi/patches-3.14/300-dt-sun7i-add-pcduino3.patch new file mode 100644 index 0000000000..55b86d3da9 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/300-dt-sun7i-add-pcduino3.patch @@ -0,0 +1,47 @@ +From c6c022c42e6b9115cbc36dce3f9100b90c2d2b06 Mon Sep 17 00:00:00 2001 +From: Zoltan HERPAI <wigyori@uid0.hu> +Date: Tue, 20 May 2014 22:28:49 +0200 +Subject: [PATCH] ARM: sun7i: dt: Add board support for LinkSprite pcDuino V3 + +The LinkSprite pcDuino V3 is an A20-based revision of the +earlier pcDuinos. This series will add support for the board, +along with some of its devices where the driver is accepted or +soon-to-be-accepted into mainline. + +Changes since v2: + - update MMC entry to comply with upstream + - unify the 4 patches into one + +Changes since v1: + - fix cosmetic issues + - fix i2c entry + - remove unnecessary input bindings include + - add MMC support + +Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu> +--- + arch/arm/boot/dts/Makefile | 3 +- + arch/arm/boot/dts/sun7i-a20-pcduino3.dts | 119 +++++++++++++++++++++++++++++++ + 2 files changed, 121 insertions(+), 1 deletion(-) + create mode 100644 arch/arm/boot/dts/sun7i-a20-pcduino3.dts + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index 6967393..b1b59ea 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -353,7 +353,8 @@ dtb-$(CONFIG_MACH_SUN7I) += \ + sun6i-a31-colombus.dtb \ + sun7i-a20-cubieboard2.dtb \ + sun7i-a20-cubietruck.dtb \ +- sun7i-a20-olinuxino-micro.dtb ++ sun7i-a20-olinuxino-micro.dtb \ ++ sun7i-a20-pcduino3.dtb + dtb-$(CONFIG_ARCH_TEGRA) += tegra20-harmony.dtb \ + tegra20-iris-512.dtb \ + tegra20-medcom-wide.dtb \ +diff --git a/arch/arm/boot/dts/sun7i-a20-pcduino3.dts b/arch/arm/boot/dts/sun7i-a20-pcduino3.dts +new file mode 100644 +index 0000000..fc6a542 +-- +2.0.3 + diff --git a/target/linux/sunxi/patches-3.14/301-dt-sun7i-update-pcduino3.patch b/target/linux/sunxi/patches-3.14/301-dt-sun7i-update-pcduino3.patch new file mode 100644 index 0000000000..7a097d7dc3 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/301-dt-sun7i-update-pcduino3.patch @@ -0,0 +1,13 @@ +diff -ruN old/arch/arm/boot/dts/sun7i-a20-pcduino3.dts new/arch/arm/boot/dts/sun7i-a20-pcduino3.dts +--- old/arch/arm/boot/dts/sun7i-a20-pcduino3.dts 2014-08-01 21:28:06.000000000 +0200 ++++ new/arch/arm/boot/dts/sun7i-a20-pcduino3.dts 2014-08-03 16:52:51.621238166 +0200 +@@ -12,8 +12,7 @@ + + /dts-v1/; + /include/ "sun7i-a20.dtsi" +-/include/ "sunxi-ahci-reg.dtsi" +-/include/ "sun4i-a10-usb-vbus-reg.dtsi" ++/include/ "sunxi-common-regulators.dtsi" + #include <dt-bindings/input/input.h> + + / { diff --git a/target/linux/sunxi/patches-3.14/302-dt-sun7i-add-bananapi-Makefile.patch b/target/linux/sunxi/patches-3.14/302-dt-sun7i-add-bananapi-Makefile.patch new file mode 100644 index 0000000000..272b501f44 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/302-dt-sun7i-add-bananapi-Makefile.patch @@ -0,0 +1,11 @@ +diff -ruN old/arch/arm/boot/dts/Makefile new/arch/arm/boot/dts/Makefile +--- old/arch/arm/boot/dts/Makefile 2014-08-03 22:59:45.000000000 +0200 ++++ new/arch/arm/boot/dts/Makefile 2014-08-03 23:12:30.208474257 +0200 +@@ -290,6 +290,7 @@ + sun5i-a13-olinuxino.dtb \ + sun5i-a13-olinuxino-micro.dtb \ + sun6i-a31-colombus.dtb \ ++ sun7i-a20-bananapi.dtb \ + sun7i-a20-cubieboard2.dtb \ + sun7i-a20-cubietruck.dtb \ + sun7i-a20-olinuxino-micro.dtb \ diff --git a/target/linux/sunxi/patches-3.14/303-dt-sun7i-update-bananapi.patch b/target/linux/sunxi/patches-3.14/303-dt-sun7i-update-bananapi.patch new file mode 100644 index 0000000000..1c8f839c0f --- /dev/null +++ b/target/linux/sunxi/patches-3.14/303-dt-sun7i-update-bananapi.patch @@ -0,0 +1,13 @@ +diff -ruN old/arch/arm/boot/dts/sun7i-a20-bananapi.dts new/arch/arm/boot/dts/sun7i-a20-bananapi.dts +--- old/arch/arm/boot/dts/sun7i-a20-bananapi.dts 2014-08-01 21:28:06.000000000 +0200 ++++ new/arch/arm/boot/dts/sun7i-a20-bananapi.dts 2014-08-04 00:49:24.552906049 +0200 +@@ -12,8 +12,7 @@ + + /dts-v1/; + /include/ "sun7i-a20.dtsi" +-/include/ "sunxi-ahci-reg.dtsi" +-/include/ "sun4i-a10-usb-vbus-reg.dtsi" ++/include/ "sunxi-common-regulators.dtsi" + #include <dt-bindings/input/input.h> + + / { |