diff --git a/target/linux/sunxi/config-4.9 b/target/linux/sunxi/config-4.9
index f338c6ec76..45677f9a83 100644
--- a/target/linux/sunxi/config-4.9
+++ b/target/linux/sunxi/config-4.9
@@ -486,6 +486,7 @@ CONFIG_STMMAC_PLATFORM=y
 CONFIG_STRICT_DEVMEM=y
 # CONFIG_SUN4I_EMAC is not set
 CONFIG_SUN4I_TIMER=y
+# CONFIG_SUN50I_A64_CCU is not set
 CONFIG_SUN5I_HSTIMER=y
 CONFIG_SUN6I_A31_CCU=y
 CONFIG_SUN8I_A23_CCU=y
diff --git a/target/linux/sunxi/patches-4.9/0001-arm64-sunxi-always-enable-reset-controller.patch b/target/linux/sunxi/patches-4.9/0001-arm64-sunxi-always-enable-reset-controller.patch
new file mode 100644
index 0000000000..e23475218b
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0001-arm64-sunxi-always-enable-reset-controller.patch
@@ -0,0 +1,39 @@
+From 900a9020af7a023f9b64c919fddf8a7486108962 Mon Sep 17 00:00:00 2001
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Tue, 18 Apr 2017 15:55:51 +0200
+Subject: arm64: sunxi: always enable reset controller
+
+The sunxi clk driver causes a link error when the reset controller
+subsystem is disabled:
+
+drivers/clk/built-in.o: In function `sun4i_ve_clk_setup':
+:(.init.text+0xd040): undefined reference to `reset_controller_register'
+drivers/clk/built-in.o: In function `sun4i_a10_display_init':
+:(.init.text+0xe5e0): undefined reference to `reset_controller_register'
+drivers/clk/built-in.o: In function `sunxi_usb_clk_setup':
+:(.init.text+0x10074): undefined reference to `reset_controller_register'
+
+We already force it to be enabled on arm32 and some other arm64 platforms,
+but not on arm64/sunxi. This adds the respective Kconfig statements to
+also select it here.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+---
+ arch/arm64/Kconfig.platforms | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/arch/arm64/Kconfig.platforms
++++ b/arch/arm64/Kconfig.platforms
+@@ -2,9 +2,11 @@ menu "Platform selection"
+ 
+ config ARCH_SUNXI
+ 	bool "Allwinner sunxi 64-bit SoC Family"
++	select ARCH_HAS_RESET_CONTROLLER
+ 	select GENERIC_IRQ_CHIP
+ 	select PINCTRL
+ 	select PINCTRL_SUN50I_A64
++	select RESET_CONTROLLER
+ 	help
+ 	  This enables support for Allwinner sunxi based SoCs like the A64.
+ 
diff --git a/target/linux/sunxi/patches-4.9/0002-clk-sunxi-ng-Rename-the-internal-structures.patch b/target/linux/sunxi/patches-4.9/0002-clk-sunxi-ng-Rename-the-internal-structures.patch
new file mode 100644
index 0000000000..f3e485d88a
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0002-clk-sunxi-ng-Rename-the-internal-structures.patch
@@ -0,0 +1,239 @@
+From a501a14e38cc4d8e9c91bb508cdca7032d53f717 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@free-electrons.com>
+Date: Fri, 30 Sep 2016 10:05:32 +0200
+Subject: clk: sunxi-ng: Rename the internal structures
+
+Rename the structures meant to be embedded in other structures to make it
+consistent with the mux structure name
+
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Acked-by: Chen-Yu Tsai <wens@csie.org>
+---
+ drivers/clk/sunxi-ng/ccu_div.h  |  6 +++---
+ drivers/clk/sunxi-ng/ccu_frac.c | 12 ++++++------
+ drivers/clk/sunxi-ng/ccu_frac.h | 14 +++++++-------
+ drivers/clk/sunxi-ng/ccu_mp.h   |  4 ++--
+ drivers/clk/sunxi-ng/ccu_mult.h |  4 ++--
+ drivers/clk/sunxi-ng/ccu_nk.h   |  4 ++--
+ drivers/clk/sunxi-ng/ccu_nkm.h  |  6 +++---
+ drivers/clk/sunxi-ng/ccu_nkmp.h |  8 ++++----
+ drivers/clk/sunxi-ng/ccu_nm.h   |  6 +++---
+ 9 files changed, 32 insertions(+), 32 deletions(-)
+
+--- a/drivers/clk/sunxi-ng/ccu_div.h
++++ b/drivers/clk/sunxi-ng/ccu_div.h
+@@ -20,7 +20,7 @@
+ #include "ccu_mux.h"
+ 
+ /**
+- * struct _ccu_div - Internal divider description
++ * struct ccu_div_internal - Internal divider description
+  * @shift: Bit offset of the divider in its register
+  * @width: Width of the divider field in its register
+  * @max: Maximum value allowed for that divider. This is the
+@@ -36,7 +36,7 @@
+  * It is basically a wrapper around the clk_divider functions
+  * arguments.
+  */
+-struct _ccu_div {
++struct ccu_div_internal {
+ 	u8			shift;
+ 	u8			width;
+ 
+@@ -78,7 +78,7 @@ struct _ccu_div {
+ struct ccu_div {
+ 	u32			enable;
+ 
+-	struct _ccu_div		div;
++	struct ccu_div_internal		div;
+ 	struct ccu_mux_internal	mux;
+ 	struct ccu_common	common;
+ };
+--- a/drivers/clk/sunxi-ng/ccu_frac.c
++++ b/drivers/clk/sunxi-ng/ccu_frac.c
+@@ -14,7 +14,7 @@
+ #include "ccu_frac.h"
+ 
+ bool ccu_frac_helper_is_enabled(struct ccu_common *common,
+-				struct _ccu_frac *cf)
++				struct ccu_frac_internal *cf)
+ {
+ 	if (!(common->features & CCU_FEATURE_FRACTIONAL))
+ 		return false;
+@@ -23,7 +23,7 @@ bool ccu_frac_helper_is_enabled(struct c
+ }
+ 
+ void ccu_frac_helper_enable(struct ccu_common *common,
+-			    struct _ccu_frac *cf)
++			    struct ccu_frac_internal *cf)
+ {
+ 	unsigned long flags;
+ 	u32 reg;
+@@ -38,7 +38,7 @@ void ccu_frac_helper_enable(struct ccu_c
+ }
+ 
+ void ccu_frac_helper_disable(struct ccu_common *common,
+-			     struct _ccu_frac *cf)
++			     struct ccu_frac_internal *cf)
+ {
+ 	unsigned long flags;
+ 	u32 reg;
+@@ -53,7 +53,7 @@ void ccu_frac_helper_disable(struct ccu_
+ }
+ 
+ bool ccu_frac_helper_has_rate(struct ccu_common *common,
+-			      struct _ccu_frac *cf,
++			      struct ccu_frac_internal *cf,
+ 			      unsigned long rate)
+ {
+ 	if (!(common->features & CCU_FEATURE_FRACTIONAL))
+@@ -63,7 +63,7 @@ bool ccu_frac_helper_has_rate(struct ccu
+ }
+ 
+ unsigned long ccu_frac_helper_read_rate(struct ccu_common *common,
+-					struct _ccu_frac *cf)
++					struct ccu_frac_internal *cf)
+ {
+ 	u32 reg;
+ 
+@@ -84,7 +84,7 @@ unsigned long ccu_frac_helper_read_rate(
+ }
+ 
+ int ccu_frac_helper_set_rate(struct ccu_common *common,
+-			     struct _ccu_frac *cf,
++			     struct ccu_frac_internal *cf,
+ 			     unsigned long rate)
+ {
+ 	unsigned long flags;
+--- a/drivers/clk/sunxi-ng/ccu_frac.h
++++ b/drivers/clk/sunxi-ng/ccu_frac.h
+@@ -18,7 +18,7 @@
+ 
+ #include "ccu_common.h"
+ 
+-struct _ccu_frac {
++struct ccu_frac_internal {
+ 	u32		enable;
+ 	u32		select;
+ 
+@@ -33,21 +33,21 @@ struct _ccu_frac {
+ 	}
+ 
+ bool ccu_frac_helper_is_enabled(struct ccu_common *common,
+-				struct _ccu_frac *cf);
++				struct ccu_frac_internal *cf);
+ void ccu_frac_helper_enable(struct ccu_common *common,
+-			    struct _ccu_frac *cf);
++			    struct ccu_frac_internal *cf);
+ void ccu_frac_helper_disable(struct ccu_common *common,
+-			     struct _ccu_frac *cf);
++			     struct ccu_frac_internal *cf);
+ 
+ bool ccu_frac_helper_has_rate(struct ccu_common *common,
+-			      struct _ccu_frac *cf,
++			      struct ccu_frac_internal *cf,
+ 			      unsigned long rate);
+ 
+ unsigned long ccu_frac_helper_read_rate(struct ccu_common *common,
+-					struct _ccu_frac *cf);
++					struct ccu_frac_internal *cf);
+ 
+ int ccu_frac_helper_set_rate(struct ccu_common *common,
+-			     struct _ccu_frac *cf,
++			     struct ccu_frac_internal *cf,
+ 			     unsigned long rate);
+ 
+ #endif /* _CCU_FRAC_H_ */
+--- a/drivers/clk/sunxi-ng/ccu_mp.h
++++ b/drivers/clk/sunxi-ng/ccu_mp.h
+@@ -29,8 +29,8 @@
+ struct ccu_mp {
+ 	u32			enable;
+ 
+-	struct _ccu_div		m;
+-	struct _ccu_div		p;
++	struct ccu_div_internal		m;
++	struct ccu_div_internal		p;
+ 	struct ccu_mux_internal	mux;
+ 	struct ccu_common	common;
+ };
+--- a/drivers/clk/sunxi-ng/ccu_mult.h
++++ b/drivers/clk/sunxi-ng/ccu_mult.h
+@@ -4,7 +4,7 @@
+ #include "ccu_common.h"
+ #include "ccu_mux.h"
+ 
+-struct _ccu_mult {
++struct ccu_mult_internal {
+ 	u8	shift;
+ 	u8	width;
+ };
+@@ -18,7 +18,7 @@ struct _ccu_mult {
+ struct ccu_mult {
+ 	u32			enable;
+ 
+-	struct _ccu_mult	mult;
++	struct ccu_mult_internal	mult;
+ 	struct ccu_mux_internal	mux;
+ 	struct ccu_common	common;
+ };
+--- a/drivers/clk/sunxi-ng/ccu_nk.h
++++ b/drivers/clk/sunxi-ng/ccu_nk.h
+@@ -30,8 +30,8 @@ struct ccu_nk {
+ 	u32			enable;
+ 	u32			lock;
+ 
+-	struct _ccu_mult	n;
+-	struct _ccu_mult	k;
++	struct ccu_mult_internal	n;
++	struct ccu_mult_internal	k;
+ 
+ 	unsigned int		fixed_post_div;
+ 
+--- a/drivers/clk/sunxi-ng/ccu_nkm.h
++++ b/drivers/clk/sunxi-ng/ccu_nkm.h
+@@ -29,9 +29,9 @@ struct ccu_nkm {
+ 	u32			enable;
+ 	u32			lock;
+ 
+-	struct _ccu_mult	n;
+-	struct _ccu_mult	k;
+-	struct _ccu_div		m;
++	struct ccu_mult_internal	n;
++	struct ccu_mult_internal	k;
++	struct ccu_div_internal		m;
+ 	struct ccu_mux_internal	mux;
+ 
+ 	struct ccu_common	common;
+--- a/drivers/clk/sunxi-ng/ccu_nkmp.h
++++ b/drivers/clk/sunxi-ng/ccu_nkmp.h
+@@ -29,10 +29,10 @@ struct ccu_nkmp {
+ 	u32			enable;
+ 	u32			lock;
+ 
+-	struct _ccu_mult	n;
+-	struct _ccu_mult	k;
+-	struct _ccu_div		m;
+-	struct _ccu_div		p;
++	struct ccu_mult_internal	n;
++	struct ccu_mult_internal	k;
++	struct ccu_div_internal		m;
++	struct ccu_div_internal		p;
+ 
+ 	struct ccu_common	common;
+ };
+--- a/drivers/clk/sunxi-ng/ccu_nm.h
++++ b/drivers/clk/sunxi-ng/ccu_nm.h
+@@ -30,9 +30,9 @@ struct ccu_nm {
+ 	u32			enable;
+ 	u32			lock;
+ 
+-	struct _ccu_mult	n;
+-	struct _ccu_div		m;
+-	struct _ccu_frac	frac;
++	struct ccu_mult_internal	n;
++	struct ccu_div_internal		m;
++	struct ccu_frac_internal	frac;
+ 
+ 	struct ccu_common	common;
+ };
diff --git a/target/linux/sunxi/patches-4.9/0003-clk-sunxi-ng-Remove-the-use-of-rational-computations.patch b/target/linux/sunxi/patches-4.9/0003-clk-sunxi-ng-Remove-the-use-of-rational-computations.patch
new file mode 100644
index 0000000000..6b8f46eae0
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0003-clk-sunxi-ng-Remove-the-use-of-rational-computations.patch
@@ -0,0 +1,239 @@
+From ee28648cb2b4d4ab5c2eb8199ea86675fe19016b Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@free-electrons.com>
+Date: Thu, 29 Sep 2016 22:53:12 +0200
+Subject: clk: sunxi-ng: Remove the use of rational computations
+
+While the rational library works great, it doesn't really allow us to add
+more constraints, like the minimum.
+
+Remove that in order to be able to deal with the constraints we'll need.
+
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Acked-by: Chen-Yu Tsai <wens@csie.org>
+---
+ drivers/clk/sunxi-ng/Kconfig    |  3 ---
+ drivers/clk/sunxi-ng/ccu_nkm.c  | 31 ++++++++++++-----------
+ drivers/clk/sunxi-ng/ccu_nkmp.c | 37 ++++++++++++++--------------
+ drivers/clk/sunxi-ng/ccu_nm.c   | 54 +++++++++++++++++++++++++++++++----------
+ 4 files changed, 74 insertions(+), 51 deletions(-)
+
+--- a/drivers/clk/sunxi-ng/Kconfig
++++ b/drivers/clk/sunxi-ng/Kconfig
+@@ -35,17 +35,14 @@ config SUNXI_CCU_NK
+ 
+ config SUNXI_CCU_NKM
+ 	bool
+-	select RATIONAL
+ 	select SUNXI_CCU_GATE
+ 
+ config SUNXI_CCU_NKMP
+ 	bool
+-	select RATIONAL
+ 	select SUNXI_CCU_GATE
+ 
+ config SUNXI_CCU_NM
+ 	bool
+-	select RATIONAL
+ 	select SUNXI_CCU_FRAC
+ 	select SUNXI_CCU_GATE
+ 
+--- a/drivers/clk/sunxi-ng/ccu_nkm.c
++++ b/drivers/clk/sunxi-ng/ccu_nkm.c
+@@ -9,7 +9,6 @@
+  */
+ 
+ #include <linux/clk-provider.h>
+-#include <linux/rational.h>
+ 
+ #include "ccu_gate.h"
+ #include "ccu_nkm.h"
+@@ -28,21 +27,21 @@ static void ccu_nkm_find_best(unsigned l
+ 	unsigned long _n, _k, _m;
+ 
+ 	for (_k = 1; _k <= nkm->max_k; _k++) {
+-		unsigned long tmp_rate;
+-
+-		rational_best_approximation(rate / _k, parent,
+-					    nkm->max_n, nkm->max_m, &_n, &_m);
+-
+-		tmp_rate = parent * _n * _k / _m;
+-
+-		if (tmp_rate > rate)
+-			continue;
+-
+-		if ((rate - tmp_rate) < (rate - best_rate)) {
+-			best_rate = tmp_rate;
+-			best_n = _n;
+-			best_k = _k;
+-			best_m = _m;
++		for (_n = 1; _n <= nkm->max_n; _n++) {
++			for (_m = 1; _n <= nkm->max_m; _m++) {
++				unsigned long tmp_rate;
++
++				tmp_rate = parent * _n * _k / _m;
++
++				if (tmp_rate > rate)
++					continue;
++				if ((rate - tmp_rate) < (rate - best_rate)) {
++					best_rate = tmp_rate;
++					best_n = _n;
++					best_k = _k;
++					best_m = _m;
++				}
++			}
+ 		}
+ 	}
+ 
+--- a/drivers/clk/sunxi-ng/ccu_nkmp.c
++++ b/drivers/clk/sunxi-ng/ccu_nkmp.c
+@@ -9,7 +9,6 @@
+  */
+ 
+ #include <linux/clk-provider.h>
+-#include <linux/rational.h>
+ 
+ #include "ccu_gate.h"
+ #include "ccu_nkmp.h"
+@@ -29,24 +28,24 @@ static void ccu_nkmp_find_best(unsigned
+ 	unsigned long _n, _k, _m, _p;
+ 
+ 	for (_k = 1; _k <= nkmp->max_k; _k++) {
+-		for (_p = 1; _p <= nkmp->max_p; _p <<= 1) {
+-			unsigned long tmp_rate;
+-
+-			rational_best_approximation(rate / _k, parent / _p,
+-						    nkmp->max_n, nkmp->max_m,
+-						    &_n, &_m);
+-
+-			tmp_rate = parent * _n * _k / (_m * _p);
+-
+-			if (tmp_rate > rate)
+-				continue;
+-
+-			if ((rate - tmp_rate) < (rate - best_rate)) {
+-				best_rate = tmp_rate;
+-				best_n = _n;
+-				best_k = _k;
+-				best_m = _m;
+-				best_p = _p;
++		for (_n = 1; _n <= nkmp->max_n; _n++) {
++			for (_m = 1; _n <= nkmp->max_m; _m++) {
++				for (_p = 1; _p <= nkmp->max_p; _p <<= 1) {
++					unsigned long tmp_rate;
++
++					tmp_rate = parent * _n * _k / (_m * _p);
++
++					if (tmp_rate > rate)
++						continue;
++
++					if ((rate - tmp_rate) < (rate - best_rate)) {
++						best_rate = tmp_rate;
++						best_n = _n;
++						best_k = _k;
++						best_m = _m;
++						best_p = _p;
++					}
++				}
+ 			}
+ 		}
+ 	}
+--- a/drivers/clk/sunxi-ng/ccu_nm.c
++++ b/drivers/clk/sunxi-ng/ccu_nm.c
+@@ -9,12 +9,42 @@
+  */
+ 
+ #include <linux/clk-provider.h>
+-#include <linux/rational.h>
+ 
+ #include "ccu_frac.h"
+ #include "ccu_gate.h"
+ #include "ccu_nm.h"
+ 
++struct _ccu_nm {
++	unsigned long	n, max_n;
++	unsigned long	m, max_m;
++};
++
++static void ccu_nm_find_best(unsigned long parent, unsigned long rate,
++			     struct _ccu_nm *nm)
++{
++	unsigned long best_rate = 0;
++	unsigned long best_n = 0, best_m = 0;
++	unsigned long _n, _m;
++
++	for (_n = 1; _n <= nm->max_n; _n++) {
++		for (_m = 1; _n <= nm->max_m; _m++) {
++			unsigned long tmp_rate = parent * _n  / _m;
++
++			if (tmp_rate > rate)
++				continue;
++
++			if ((rate - tmp_rate) < (rate - best_rate)) {
++				best_rate = tmp_rate;
++				best_n = _n;
++				best_m = _m;
++			}
++		}
++	}
++
++	nm->n = best_n;
++	nm->m = best_m;
++}
++
+ static void ccu_nm_disable(struct clk_hw *hw)
+ {
+ 	struct ccu_nm *nm = hw_to_ccu_nm(hw);
+@@ -61,24 +91,22 @@ static long ccu_nm_round_rate(struct clk
+ 			      unsigned long *parent_rate)
+ {
+ 	struct ccu_nm *nm = hw_to_ccu_nm(hw);
+-	unsigned long max_n, max_m;
+-	unsigned long n, m;
++	struct _ccu_nm _nm;
+ 
+-	max_n = 1 << nm->n.width;
+-	max_m = nm->m.max ?: 1 << nm->m.width;
++	_nm.max_n = 1 << nm->n.width;
++	_nm.max_m = nm->m.max ?: 1 << nm->m.width;
+ 
+-	rational_best_approximation(rate, *parent_rate, max_n, max_m, &n, &m);
++	ccu_nm_find_best(*parent_rate, rate, &_nm);
+ 
+-	return *parent_rate * n / m;
++	return *parent_rate * _nm.n / _nm.m;
+ }
+ 
+ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate,
+ 			   unsigned long parent_rate)
+ {
+ 	struct ccu_nm *nm = hw_to_ccu_nm(hw);
++	struct _ccu_nm _nm;
+ 	unsigned long flags;
+-	unsigned long max_n, max_m;
+-	unsigned long n, m;
+ 	u32 reg;
+ 
+ 	if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate))
+@@ -86,10 +114,10 @@ static int ccu_nm_set_rate(struct clk_hw
+ 	else
+ 		ccu_frac_helper_disable(&nm->common, &nm->frac);
+ 
+-	max_n = 1 << nm->n.width;
+-	max_m = nm->m.max ?: 1 << nm->m.width;
++	_nm.max_n = 1 << nm->n.width;
++	_nm.max_m = nm->m.max ?: 1 << nm->m.width;
+ 
+-	rational_best_approximation(rate, parent_rate, max_n, max_m, &n, &m);
++	ccu_nm_find_best(parent_rate, rate, &_nm);
+ 
+ 	spin_lock_irqsave(nm->common.lock, flags);
+ 
+@@ -97,7 +125,7 @@ static int ccu_nm_set_rate(struct clk_hw
+ 	reg &= ~GENMASK(nm->n.width + nm->n.shift - 1, nm->n.shift);
+ 	reg &= ~GENMASK(nm->m.width + nm->m.shift - 1, nm->m.shift);
+ 
+-	writel(reg | ((m - 1) << nm->m.shift) | ((n - 1) << nm->n.shift),
++	writel(reg | ((_nm.m - 1) << nm->m.shift) | ((_nm.n - 1) << nm->n.shift),
+ 	       nm->common.base + nm->common.reg);
+ 
+ 	spin_unlock_irqrestore(nm->common.lock, flags);
diff --git a/target/linux/sunxi/patches-4.9/0004-clk-sunxi-ng-Finish-to-convert-to-structures-for-arg.patch b/target/linux/sunxi/patches-4.9/0004-clk-sunxi-ng-Finish-to-convert-to-structures-for-arg.patch
new file mode 100644
index 0000000000..4b91892b81
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0004-clk-sunxi-ng-Finish-to-convert-to-structures-for-arg.patch
@@ -0,0 +1,182 @@
+From b8302c7267dedaeeb1bf38143f099defbf16dce8 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@free-electrons.com>
+Date: Thu, 29 Sep 2016 23:50:21 +0200
+Subject: clk: sunxi-ng: Finish to convert to structures for arguments
+
+Some clocks still use an explicit list of arguments, which make it a bit
+more tedious to add new parameters.
+
+Convert those over to a structure pointer argument to add as many
+arguments as possible without having to many noise in our patches, or a
+very long list of arguments.
+
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Acked-by: Chen-Yu Tsai <wens@csie.org>
+---
+ drivers/clk/sunxi-ng/ccu_mult.c | 28 ++++++++++++++++++++--------
+ drivers/clk/sunxi-ng/ccu_nk.c   | 39 ++++++++++++++++++++++-----------------
+ 2 files changed, 42 insertions(+), 25 deletions(-)
+
+--- a/drivers/clk/sunxi-ng/ccu_mult.c
++++ b/drivers/clk/sunxi-ng/ccu_mult.c
+@@ -13,10 +13,20 @@
+ #include "ccu_gate.h"
+ #include "ccu_mult.h"
+ 
++struct _ccu_mult {
++	unsigned long	mult, max;
++};
++
+ static void ccu_mult_find_best(unsigned long parent, unsigned long rate,
+-			       unsigned int max_n, unsigned int *n)
++			       struct _ccu_mult *mult)
+ {
+-	*n = rate / parent;
++	int _mult;
++
++	_mult = rate / parent;
++	if (_mult > mult->max)
++		_mult = mult->max;
++
++	mult->mult = _mult;
+ }
+ 
+ static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux,
+@@ -25,11 +35,12 @@ static unsigned long ccu_mult_round_rate
+ 					void *data)
+ {
+ 	struct ccu_mult *cm = data;
+-	unsigned int n;
++	struct _ccu_mult _cm;
+ 
+-	ccu_mult_find_best(parent_rate, rate, 1 << cm->mult.width, &n);
++	_cm.max = 1 << cm->mult.width;
++	ccu_mult_find_best(parent_rate, rate, &_cm);
+ 
+-	return parent_rate * n;
++	return parent_rate * _cm.mult;
+ }
+ 
+ static void ccu_mult_disable(struct clk_hw *hw)
+@@ -83,21 +94,22 @@ static int ccu_mult_set_rate(struct clk_
+ 			   unsigned long parent_rate)
+ {
+ 	struct ccu_mult *cm = hw_to_ccu_mult(hw);
++	struct _ccu_mult _cm;
+ 	unsigned long flags;
+-	unsigned int n;
+ 	u32 reg;
+ 
+ 	ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1,
+ 						&parent_rate);
+ 
+-	ccu_mult_find_best(parent_rate, rate, 1 << cm->mult.width, &n);
++	_cm.max = 1 << cm->mult.width;
++	ccu_mult_find_best(parent_rate, rate, &_cm);
+ 
+ 	spin_lock_irqsave(cm->common.lock, flags);
+ 
+ 	reg = readl(cm->common.base + cm->common.reg);
+ 	reg &= ~GENMASK(cm->mult.width + cm->mult.shift - 1, cm->mult.shift);
+ 
+-	writel(reg | ((n - 1) << cm->mult.shift),
++	writel(reg | ((_cm.mult - 1) << cm->mult.shift),
+ 	       cm->common.base + cm->common.reg);
+ 
+ 	spin_unlock_irqrestore(cm->common.lock, flags);
+--- a/drivers/clk/sunxi-ng/ccu_nk.c
++++ b/drivers/clk/sunxi-ng/ccu_nk.c
+@@ -9,21 +9,24 @@
+  */
+ 
+ #include <linux/clk-provider.h>
+-#include <linux/rational.h>
+ 
+ #include "ccu_gate.h"
+ #include "ccu_nk.h"
+ 
++struct _ccu_nk {
++	unsigned long	n, max_n;
++	unsigned long	k, max_k;
++};
++
+ static void ccu_nk_find_best(unsigned long parent, unsigned long rate,
+-			     unsigned int max_n, unsigned int max_k,
+-			     unsigned int *n, unsigned int *k)
++			     struct _ccu_nk *nk)
+ {
+ 	unsigned long best_rate = 0;
+ 	unsigned int best_k = 0, best_n = 0;
+ 	unsigned int _k, _n;
+ 
+-	for (_k = 1; _k <= max_k; _k++) {
+-		for (_n = 1; _n <= max_n; _n++) {
++	for (_k = 1; _k <= nk->max_k; _k++) {
++		for (_n = 1; _n <= nk->max_n; _n++) {
+ 			unsigned long tmp_rate = parent * _n * _k;
+ 
+ 			if (tmp_rate > rate)
+@@ -37,8 +40,8 @@ static void ccu_nk_find_best(unsigned lo
+ 		}
+ 	}
+ 
+-	*k = best_k;
+-	*n = best_n;
++	nk->k = best_k;
++	nk->n = best_n;
+ }
+ 
+ static void ccu_nk_disable(struct clk_hw *hw)
+@@ -89,16 +92,17 @@ static long ccu_nk_round_rate(struct clk
+ 			      unsigned long *parent_rate)
+ {
+ 	struct ccu_nk *nk = hw_to_ccu_nk(hw);
+-	unsigned int n, k;
++	struct _ccu_nk _nk;
+ 
+ 	if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
+ 		rate *= nk->fixed_post_div;
+ 
+-	ccu_nk_find_best(*parent_rate, rate,
+-			 1 << nk->n.width, 1 << nk->k.width,
+-			 &n, &k);
++	_nk.max_n = 1 << nk->n.width;
++	_nk.max_k = 1 << nk->k.width;
++
++	ccu_nk_find_best(*parent_rate, rate, &_nk);
++	rate = *parent_rate * _nk.n * _nk.k;
+ 
+-	rate = *parent_rate * n * k;
+ 	if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
+ 		rate = rate / nk->fixed_post_div;
+ 
+@@ -110,15 +114,16 @@ static int ccu_nk_set_rate(struct clk_hw
+ {
+ 	struct ccu_nk *nk = hw_to_ccu_nk(hw);
+ 	unsigned long flags;
+-	unsigned int n, k;
++	struct _ccu_nk _nk;
+ 	u32 reg;
+ 
+ 	if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
+ 		rate = rate * nk->fixed_post_div;
+ 
+-	ccu_nk_find_best(parent_rate, rate,
+-			 1 << nk->n.width, 1 << nk->k.width,
+-			 &n, &k);
++	_nk.max_n = 1 << nk->n.width;
++	_nk.max_k = 1 << nk->k.width;
++
++	ccu_nk_find_best(parent_rate, rate, &_nk);
+ 
+ 	spin_lock_irqsave(nk->common.lock, flags);
+ 
+@@ -126,7 +131,7 @@ static int ccu_nk_set_rate(struct clk_hw
+ 	reg &= ~GENMASK(nk->n.width + nk->n.shift - 1, nk->n.shift);
+ 	reg &= ~GENMASK(nk->k.width + nk->k.shift - 1, nk->k.shift);
+ 
+-	writel(reg | ((k - 1) << nk->k.shift) | ((n - 1) << nk->n.shift),
++	writel(reg | ((_nk.k - 1) << nk->k.shift) | ((_nk.n - 1) << nk->n.shift),
+ 	       nk->common.base + nk->common.reg);
+ 
+ 	spin_unlock_irqrestore(nk->common.lock, flags);
diff --git a/target/linux/sunxi/patches-4.9/0005-clk-sunxi-ng-Add-minimums-for-all-the-relevant-struc.patch b/target/linux/sunxi/patches-4.9/0005-clk-sunxi-ng-Add-minimums-for-all-the-relevant-struc.patch
new file mode 100644
index 0000000000..0165ade138
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0005-clk-sunxi-ng-Add-minimums-for-all-the-relevant-struc.patch
@@ -0,0 +1,256 @@
+From 6e0d50daa97f4bf9706e343b4f71171e88921209 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@free-electrons.com>
+Date: Thu, 29 Sep 2016 22:57:26 +0200
+Subject: clk: sunxi-ng: Add minimums for all the relevant structures and
+ clocks
+
+Modify the current clocks we have to be able to specify the minimum for
+each clocks we support, just like we support the max.
+
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Acked-by: Chen-Yu Tsai <wens@csie.org>
+---
+ drivers/clk/sunxi-ng/ccu_mult.c |  7 ++++++-
+ drivers/clk/sunxi-ng/ccu_nk.c   | 12 ++++++++----
+ drivers/clk/sunxi-ng/ccu_nkm.c  | 18 ++++++++++++------
+ drivers/clk/sunxi-ng/ccu_nkmp.c | 24 ++++++++++++++++--------
+ drivers/clk/sunxi-ng/ccu_nm.c   | 12 ++++++++----
+ 5 files changed, 50 insertions(+), 23 deletions(-)
+
+--- a/drivers/clk/sunxi-ng/ccu_mult.c
++++ b/drivers/clk/sunxi-ng/ccu_mult.c
+@@ -14,7 +14,7 @@
+ #include "ccu_mult.h"
+ 
+ struct _ccu_mult {
+-	unsigned long	mult, max;
++	unsigned long	mult, min, max;
+ };
+ 
+ static void ccu_mult_find_best(unsigned long parent, unsigned long rate,
+@@ -23,6 +23,9 @@ static void ccu_mult_find_best(unsigned
+ 	int _mult;
+ 
+ 	_mult = rate / parent;
++	if (_mult < mult->min)
++		_mult = mult->min;
++
+ 	if (_mult > mult->max)
+ 		_mult = mult->max;
+ 
+@@ -37,6 +40,7 @@ static unsigned long ccu_mult_round_rate
+ 	struct ccu_mult *cm = data;
+ 	struct _ccu_mult _cm;
+ 
++	_cm.min = 1;
+ 	_cm.max = 1 << cm->mult.width;
+ 	ccu_mult_find_best(parent_rate, rate, &_cm);
+ 
+@@ -101,6 +105,7 @@ static int ccu_mult_set_rate(struct clk_
+ 	ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1,
+ 						&parent_rate);
+ 
++	_cm.min = 1;
+ 	_cm.max = 1 << cm->mult.width;
+ 	ccu_mult_find_best(parent_rate, rate, &_cm);
+ 
+--- a/drivers/clk/sunxi-ng/ccu_nk.c
++++ b/drivers/clk/sunxi-ng/ccu_nk.c
+@@ -14,8 +14,8 @@
+ #include "ccu_nk.h"
+ 
+ struct _ccu_nk {
+-	unsigned long	n, max_n;
+-	unsigned long	k, max_k;
++	unsigned long	n, min_n, max_n;
++	unsigned long	k, min_k, max_k;
+ };
+ 
+ static void ccu_nk_find_best(unsigned long parent, unsigned long rate,
+@@ -25,8 +25,8 @@ static void ccu_nk_find_best(unsigned lo
+ 	unsigned int best_k = 0, best_n = 0;
+ 	unsigned int _k, _n;
+ 
+-	for (_k = 1; _k <= nk->max_k; _k++) {
+-		for (_n = 1; _n <= nk->max_n; _n++) {
++	for (_k = nk->min_k; _k <= nk->max_k; _k++) {
++		for (_n = nk->min_n; _n <= nk->max_n; _n++) {
+ 			unsigned long tmp_rate = parent * _n * _k;
+ 
+ 			if (tmp_rate > rate)
+@@ -97,7 +97,9 @@ static long ccu_nk_round_rate(struct clk
+ 	if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
+ 		rate *= nk->fixed_post_div;
+ 
++	_nk.min_n = 1;
+ 	_nk.max_n = 1 << nk->n.width;
++	_nk.min_k = 1;
+ 	_nk.max_k = 1 << nk->k.width;
+ 
+ 	ccu_nk_find_best(*parent_rate, rate, &_nk);
+@@ -120,7 +122,9 @@ static int ccu_nk_set_rate(struct clk_hw
+ 	if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
+ 		rate = rate * nk->fixed_post_div;
+ 
++	_nk.min_n = 1;
+ 	_nk.max_n = 1 << nk->n.width;
++	_nk.min_k = 1;
+ 	_nk.max_k = 1 << nk->k.width;
+ 
+ 	ccu_nk_find_best(parent_rate, rate, &_nk);
+--- a/drivers/clk/sunxi-ng/ccu_nkm.c
++++ b/drivers/clk/sunxi-ng/ccu_nkm.c
+@@ -14,9 +14,9 @@
+ #include "ccu_nkm.h"
+ 
+ struct _ccu_nkm {
+-	unsigned long	n, max_n;
+-	unsigned long	k, max_k;
+-	unsigned long	m, max_m;
++	unsigned long	n, min_n, max_n;
++	unsigned long	k, min_k, max_k;
++	unsigned long	m, min_m, max_m;
+ };
+ 
+ static void ccu_nkm_find_best(unsigned long parent, unsigned long rate,
+@@ -26,9 +26,9 @@ static void ccu_nkm_find_best(unsigned l
+ 	unsigned long best_n = 0, best_k = 0, best_m = 0;
+ 	unsigned long _n, _k, _m;
+ 
+-	for (_k = 1; _k <= nkm->max_k; _k++) {
+-		for (_n = 1; _n <= nkm->max_n; _n++) {
+-			for (_m = 1; _n <= nkm->max_m; _m++) {
++	for (_k = nkm->min_k; _k <= nkm->max_k; _k++) {
++		for (_n = nkm->min_n; _n <= nkm->max_n; _n++) {
++			for (_m = nkm->min_m; _m <= nkm->max_m; _m++) {
+ 				unsigned long tmp_rate;
+ 
+ 				tmp_rate = parent * _n * _k / _m;
+@@ -100,8 +100,11 @@ static unsigned long ccu_nkm_round_rate(
+ 	struct ccu_nkm *nkm = data;
+ 	struct _ccu_nkm _nkm;
+ 
++	_nkm.min_n = 1;
+ 	_nkm.max_n = 1 << nkm->n.width;
++	_nkm.min_k = 1;
+ 	_nkm.max_k = 1 << nkm->k.width;
++	_nkm.min_m = 1;
+ 	_nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
+ 
+ 	ccu_nkm_find_best(parent_rate, rate, &_nkm);
+@@ -126,8 +129,11 @@ static int ccu_nkm_set_rate(struct clk_h
+ 	unsigned long flags;
+ 	u32 reg;
+ 
++	_nkm.min_n = 1;
+ 	_nkm.max_n = 1 << nkm->n.width;
++	_nkm.min_k = 1;
+ 	_nkm.max_k = 1 << nkm->k.width;
++	_nkm.min_m = 1;
+ 	_nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
+ 
+ 	ccu_nkm_find_best(parent_rate, rate, &_nkm);
+--- a/drivers/clk/sunxi-ng/ccu_nkmp.c
++++ b/drivers/clk/sunxi-ng/ccu_nkmp.c
+@@ -14,10 +14,10 @@
+ #include "ccu_nkmp.h"
+ 
+ struct _ccu_nkmp {
+-	unsigned long	n, max_n;
+-	unsigned long	k, max_k;
+-	unsigned long	m, max_m;
+-	unsigned long	p, max_p;
++	unsigned long	n, min_n, max_n;
++	unsigned long	k, min_k, max_k;
++	unsigned long	m, min_m, max_m;
++	unsigned long	p, min_p, max_p;
+ };
+ 
+ static void ccu_nkmp_find_best(unsigned long parent, unsigned long rate,
+@@ -27,10 +27,10 @@ static void ccu_nkmp_find_best(unsigned
+ 	unsigned long best_n = 0, best_k = 0, best_m = 0, best_p = 0;
+ 	unsigned long _n, _k, _m, _p;
+ 
+-	for (_k = 1; _k <= nkmp->max_k; _k++) {
+-		for (_n = 1; _n <= nkmp->max_n; _n++) {
+-			for (_m = 1; _n <= nkmp->max_m; _m++) {
+-				for (_p = 1; _p <= nkmp->max_p; _p <<= 1) {
++	for (_k = nkmp->min_k; _k <= nkmp->max_k; _k++) {
++		for (_n = nkmp->min_n; _n <= nkmp->max_n; _n++) {
++			for (_m = nkmp->min_m; _m <= nkmp->max_m; _m++) {
++				for (_p = nkmp->min_p; _p <= nkmp->max_p; _p <<= 1) {
+ 					unsigned long tmp_rate;
+ 
+ 					tmp_rate = parent * _n * _k / (_m * _p);
+@@ -107,9 +107,13 @@ static long ccu_nkmp_round_rate(struct c
+ 	struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw);
+ 	struct _ccu_nkmp _nkmp;
+ 
++	_nkmp.min_n = 1;
+ 	_nkmp.max_n = 1 << nkmp->n.width;
++	_nkmp.min_k = 1;
+ 	_nkmp.max_k = 1 << nkmp->k.width;
++	_nkmp.min_m = 1;
+ 	_nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width;
++	_nkmp.min_p = 1;
+ 	_nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1);
+ 
+ 	ccu_nkmp_find_best(*parent_rate, rate, &_nkmp);
+@@ -125,9 +129,13 @@ static int ccu_nkmp_set_rate(struct clk_
+ 	unsigned long flags;
+ 	u32 reg;
+ 
++	_nkmp.min_n = 1;
+ 	_nkmp.max_n = 1 << nkmp->n.width;
++	_nkmp.min_k = 1;
+ 	_nkmp.max_k = 1 << nkmp->k.width;
++	_nkmp.min_m = 1;
+ 	_nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width;
++	_nkmp.min_p = 1;
+ 	_nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1);
+ 
+ 	ccu_nkmp_find_best(parent_rate, rate, &_nkmp);
+--- a/drivers/clk/sunxi-ng/ccu_nm.c
++++ b/drivers/clk/sunxi-ng/ccu_nm.c
+@@ -15,8 +15,8 @@
+ #include "ccu_nm.h"
+ 
+ struct _ccu_nm {
+-	unsigned long	n, max_n;
+-	unsigned long	m, max_m;
++	unsigned long	n, min_n, max_n;
++	unsigned long	m, min_m, max_m;
+ };
+ 
+ static void ccu_nm_find_best(unsigned long parent, unsigned long rate,
+@@ -26,8 +26,8 @@ static void ccu_nm_find_best(unsigned lo
+ 	unsigned long best_n = 0, best_m = 0;
+ 	unsigned long _n, _m;
+ 
+-	for (_n = 1; _n <= nm->max_n; _n++) {
+-		for (_m = 1; _n <= nm->max_m; _m++) {
++	for (_n = nm->min_n; _n <= nm->max_n; _n++) {
++		for (_m = nm->min_m; _m <= nm->max_m; _m++) {
+ 			unsigned long tmp_rate = parent * _n  / _m;
+ 
+ 			if (tmp_rate > rate)
+@@ -93,7 +93,9 @@ static long ccu_nm_round_rate(struct clk
+ 	struct ccu_nm *nm = hw_to_ccu_nm(hw);
+ 	struct _ccu_nm _nm;
+ 
++	_nm.min_n = 1;
+ 	_nm.max_n = 1 << nm->n.width;
++	_nm.min_m = 1;
+ 	_nm.max_m = nm->m.max ?: 1 << nm->m.width;
+ 
+ 	ccu_nm_find_best(*parent_rate, rate, &_nm);
+@@ -114,7 +116,9 @@ static int ccu_nm_set_rate(struct clk_hw
+ 	else
+ 		ccu_frac_helper_disable(&nm->common, &nm->frac);
+ 
++	_nm.min_n = 1;
+ 	_nm.max_n = 1 << nm->n.width;
++	_nm.min_m = 1;
+ 	_nm.max_m = nm->m.max ?: 1 << nm->m.width;
+ 
+ 	ccu_nm_find_best(parent_rate, rate, &_nm);
diff --git a/target/linux/sunxi/patches-4.9/0006-clk-sunxi-ng-Implement-minimum-for-multipliers.patch b/target/linux/sunxi/patches-4.9/0006-clk-sunxi-ng-Implement-minimum-for-multipliers.patch
new file mode 100644
index 0000000000..668d596493
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0006-clk-sunxi-ng-Implement-minimum-for-multipliers.patch
@@ -0,0 +1,132 @@
+From 2beaa601c849e72683a2dd0fe6fd77763f19f051 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@free-electrons.com>
+Date: Fri, 30 Sep 2016 22:16:51 +0200
+Subject: clk: sunxi-ng: Implement minimum for multipliers
+
+Allow the CCU drivers to specify a multiplier for their clocks.
+
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Acked-by: Chen-Yu Tsai <wens@csie.org>
+---
+ drivers/clk/sunxi-ng/ccu_mult.c |  2 +-
+ drivers/clk/sunxi-ng/ccu_mult.h | 13 +++++++++----
+ drivers/clk/sunxi-ng/ccu_nk.c   |  8 ++++----
+ drivers/clk/sunxi-ng/ccu_nkm.c  |  8 ++++----
+ drivers/clk/sunxi-ng/ccu_nkmp.c |  4 ++--
+ drivers/clk/sunxi-ng/ccu_nm.c   |  2 +-
+ 6 files changed, 21 insertions(+), 16 deletions(-)
+
+--- a/drivers/clk/sunxi-ng/ccu_mult.c
++++ b/drivers/clk/sunxi-ng/ccu_mult.c
+@@ -105,7 +105,7 @@ static int ccu_mult_set_rate(struct clk_
+ 	ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1,
+ 						&parent_rate);
+ 
+-	_cm.min = 1;
++	_cm.min = cm->mult.min;
+ 	_cm.max = 1 << cm->mult.width;
+ 	ccu_mult_find_best(parent_rate, rate, &_cm);
+ 
+--- a/drivers/clk/sunxi-ng/ccu_mult.h
++++ b/drivers/clk/sunxi-ng/ccu_mult.h
+@@ -7,14 +7,19 @@
+ struct ccu_mult_internal {
+ 	u8	shift;
+ 	u8	width;
++	u8	min;
+ };
+ 
+-#define _SUNXI_CCU_MULT(_shift, _width)		\
+-	{					\
+-		.shift	= _shift,		\
+-		.width	= _width,		\
++#define _SUNXI_CCU_MULT_MIN(_shift, _width, _min)	\
++	{						\
++		.shift	= _shift,			\
++		.width	= _width,			\
++		.min	= _min,				\
+ 	}
+ 
++#define _SUNXI_CCU_MULT(_shift, _width)		\
++	_SUNXI_CCU_MULT_MIN(_shift, _width, 1)
++
+ struct ccu_mult {
+ 	u32			enable;
+ 
+--- a/drivers/clk/sunxi-ng/ccu_nk.c
++++ b/drivers/clk/sunxi-ng/ccu_nk.c
+@@ -97,9 +97,9 @@ static long ccu_nk_round_rate(struct clk
+ 	if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
+ 		rate *= nk->fixed_post_div;
+ 
+-	_nk.min_n = 1;
++	_nk.min_n = nk->n.min;
+ 	_nk.max_n = 1 << nk->n.width;
+-	_nk.min_k = 1;
++	_nk.min_k = nk->k.min;
+ 	_nk.max_k = 1 << nk->k.width;
+ 
+ 	ccu_nk_find_best(*parent_rate, rate, &_nk);
+@@ -122,9 +122,9 @@ static int ccu_nk_set_rate(struct clk_hw
+ 	if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
+ 		rate = rate * nk->fixed_post_div;
+ 
+-	_nk.min_n = 1;
++	_nk.min_n = nk->n.min;
+ 	_nk.max_n = 1 << nk->n.width;
+-	_nk.min_k = 1;
++	_nk.min_k = nk->k.min;
+ 	_nk.max_k = 1 << nk->k.width;
+ 
+ 	ccu_nk_find_best(parent_rate, rate, &_nk);
+--- a/drivers/clk/sunxi-ng/ccu_nkm.c
++++ b/drivers/clk/sunxi-ng/ccu_nkm.c
+@@ -100,9 +100,9 @@ static unsigned long ccu_nkm_round_rate(
+ 	struct ccu_nkm *nkm = data;
+ 	struct _ccu_nkm _nkm;
+ 
+-	_nkm.min_n = 1;
++	_nkm.min_n = nkm->n.min;
+ 	_nkm.max_n = 1 << nkm->n.width;
+-	_nkm.min_k = 1;
++	_nkm.min_k = nkm->k.min;
+ 	_nkm.max_k = 1 << nkm->k.width;
+ 	_nkm.min_m = 1;
+ 	_nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
+@@ -129,9 +129,9 @@ static int ccu_nkm_set_rate(struct clk_h
+ 	unsigned long flags;
+ 	u32 reg;
+ 
+-	_nkm.min_n = 1;
++	_nkm.min_n = nkm->n.min;
+ 	_nkm.max_n = 1 << nkm->n.width;
+-	_nkm.min_k = 1;
++	_nkm.min_k = nkm->k.min;
+ 	_nkm.max_k = 1 << nkm->k.width;
+ 	_nkm.min_m = 1;
+ 	_nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
+--- a/drivers/clk/sunxi-ng/ccu_nkmp.c
++++ b/drivers/clk/sunxi-ng/ccu_nkmp.c
+@@ -107,9 +107,9 @@ static long ccu_nkmp_round_rate(struct c
+ 	struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw);
+ 	struct _ccu_nkmp _nkmp;
+ 
+-	_nkmp.min_n = 1;
++	_nkmp.min_n = nkmp->n.min;
+ 	_nkmp.max_n = 1 << nkmp->n.width;
+-	_nkmp.min_k = 1;
++	_nkmp.min_k = nkmp->k.min;
+ 	_nkmp.max_k = 1 << nkmp->k.width;
+ 	_nkmp.min_m = 1;
+ 	_nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width;
+--- a/drivers/clk/sunxi-ng/ccu_nm.c
++++ b/drivers/clk/sunxi-ng/ccu_nm.c
+@@ -93,7 +93,7 @@ static long ccu_nm_round_rate(struct clk
+ 	struct ccu_nm *nm = hw_to_ccu_nm(hw);
+ 	struct _ccu_nm _nm;
+ 
+-	_nm.min_n = 1;
++	_nm.min_n = nm->n.min;
+ 	_nm.max_n = 1 << nm->n.width;
+ 	_nm.min_m = 1;
+ 	_nm.max_m = nm->m.max ?: 1 << nm->m.width;
diff --git a/target/linux/sunxi/patches-4.9/0007-clk-sunxi-ng-Add-A64-clocks.patch b/target/linux/sunxi/patches-4.9/0007-clk-sunxi-ng-Add-A64-clocks.patch
new file mode 100644
index 0000000000..fa0bae92b4
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0007-clk-sunxi-ng-Add-A64-clocks.patch
@@ -0,0 +1,1295 @@
+From c6a0637460c29799f1e63a6a4a65bda22caf4a54 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@free-electrons.com>
+Date: Wed, 6 Jul 2016 08:31:34 +0200
+Subject: clk: sunxi-ng: Add A64 clocks
+
+Add the A64 CCU clocks set.
+
+Acked-by: Rob Herring <robh@kernel.org>
+Acked-by: Chen-Yu Tsai <wens@csie.org>
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+---
+ .../devicetree/bindings/clock/sunxi-ccu.txt        |   1 +
+ drivers/clk/sunxi-ng/Kconfig                       |  11 +
+ drivers/clk/sunxi-ng/Makefile                      |   1 +
+ drivers/clk/sunxi-ng/ccu-sun50i-a64.c              | 915 +++++++++++++++++++++
+ drivers/clk/sunxi-ng/ccu-sun50i-a64.h              |  72 ++
+ include/dt-bindings/clock/sun50i-a64-ccu.h         | 134 +++
+ include/dt-bindings/reset/sun50i-a64-ccu.h         |  98 +++
+ 7 files changed, 1232 insertions(+)
+ create mode 100644 drivers/clk/sunxi-ng/ccu-sun50i-a64.c
+ create mode 100644 drivers/clk/sunxi-ng/ccu-sun50i-a64.h
+ create mode 100644 include/dt-bindings/clock/sun50i-a64-ccu.h
+ create mode 100644 include/dt-bindings/reset/sun50i-a64-ccu.h
+
+--- a/Documentation/devicetree/bindings/clock/sunxi-ccu.txt
++++ b/Documentation/devicetree/bindings/clock/sunxi-ccu.txt
+@@ -7,6 +7,7 @@ Required properties :
+ 		- "allwinner,sun8i-a23-ccu"
+ 		- "allwinner,sun8i-a33-ccu"
+ 		- "allwinner,sun8i-h3-ccu"
++		- "allwinner,sun50i-a64-ccu"
+ 
+ - reg: Must contain the registers base address and length
+ - clocks: phandle to the oscillators feeding the CCU. Two are needed:
+--- a/drivers/clk/sunxi-ng/Kconfig
++++ b/drivers/clk/sunxi-ng/Kconfig
+@@ -53,6 +53,17 @@ config SUNXI_CCU_MP
+ 
+ # SoC Drivers
+ 
++config SUN50I_A64_CCU
++	bool "Support for the Allwinner A64 CCU"
++	select SUNXI_CCU_DIV
++	select SUNXI_CCU_NK
++	select SUNXI_CCU_NKM
++	select SUNXI_CCU_NKMP
++	select SUNXI_CCU_NM
++	select SUNXI_CCU_MP
++	select SUNXI_CCU_PHASE
++	default ARM64 && ARCH_SUNXI
++
+ config SUN6I_A31_CCU
+ 	bool "Support for the Allwinner A31/A31s CCU"
+ 	select SUNXI_CCU_DIV
+--- a/drivers/clk/sunxi-ng/Makefile
++++ b/drivers/clk/sunxi-ng/Makefile
+@@ -18,6 +18,7 @@ obj-$(CONFIG_SUNXI_CCU_NM)	+= ccu_nm.o
+ obj-$(CONFIG_SUNXI_CCU_MP)	+= ccu_mp.o
+ 
+ # SoC support
++obj-$(CONFIG_SUN50I_A64_CCU)	+= ccu-sun50i-a64.o
+ obj-$(CONFIG_SUN6I_A31_CCU)	+= ccu-sun6i-a31.o
+ obj-$(CONFIG_SUN8I_A23_CCU)	+= ccu-sun8i-a23.o
+ obj-$(CONFIG_SUN8I_A33_CCU)	+= ccu-sun8i-a33.o
+--- /dev/null
++++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
+@@ -0,0 +1,915 @@
++/*
++ * Copyright (c) 2016 Maxime Ripard. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/clk-provider.h>
++#include <linux/of_address.h>
++#include <linux/platform_device.h>
++
++#include "ccu_common.h"
++#include "ccu_reset.h"
++
++#include "ccu_div.h"
++#include "ccu_gate.h"
++#include "ccu_mp.h"
++#include "ccu_mult.h"
++#include "ccu_nk.h"
++#include "ccu_nkm.h"
++#include "ccu_nkmp.h"
++#include "ccu_nm.h"
++#include "ccu_phase.h"
++
++#include "ccu-sun50i-a64.h"
++
++static struct ccu_nkmp pll_cpux_clk = {
++	.enable		= BIT(31),
++	.lock		= BIT(28),
++	.n		= _SUNXI_CCU_MULT(8, 5),
++	.k		= _SUNXI_CCU_MULT(4, 2),
++	.m		= _SUNXI_CCU_DIV(0, 2),
++	.p		= _SUNXI_CCU_DIV_MAX(16, 2, 4),
++	.common		= {
++		.reg		= 0x000,
++		.hw.init	= CLK_HW_INIT("pll-cpux",
++					      "osc24M",
++					      &ccu_nkmp_ops,
++					      CLK_SET_RATE_UNGATE),
++	},
++};
++
++/*
++ * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from
++ * the base (2x, 4x and 8x), and one variable divider (the one true
++ * pll audio).
++ *
++ * We don't have any need for the variable divider for now, so we just
++ * hardcode it to match with the clock names
++ */
++#define SUN50I_A64_PLL_AUDIO_REG	0x008
++
++static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_audio_base_clk, "pll-audio-base",
++				   "osc24M", 0x008,
++				   8, 7,	/* N */
++				   0, 5,	/* M */
++				   BIT(31),	/* gate */
++				   BIT(28),	/* lock */
++				   CLK_SET_RATE_UNGATE);
++
++static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video0_clk, "pll-video0",
++					"osc24M", 0x010,
++					8, 7,		/* N */
++					0, 4,		/* M */
++					BIT(24),	/* frac enable */
++					BIT(25),	/* frac select */
++					270000000,	/* frac rate 0 */
++					297000000,	/* frac rate 1 */
++					BIT(31),	/* gate */
++					BIT(28),	/* lock */
++					CLK_SET_RATE_UNGATE);
++
++static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve",
++					"osc24M", 0x018,
++					8, 7,		/* N */
++					0, 4,		/* M */
++					BIT(24),	/* frac enable */
++					BIT(25),	/* frac select */
++					270000000,	/* frac rate 0 */
++					297000000,	/* frac rate 1 */
++					BIT(31),	/* gate */
++					BIT(28),	/* lock */
++					CLK_SET_RATE_UNGATE);
++
++static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr0_clk, "pll-ddr0",
++				    "osc24M", 0x020,
++				    8, 5,	/* N */
++				    4, 2,	/* K */
++				    0, 2,	/* M */
++				    BIT(31),	/* gate */
++				    BIT(28),	/* lock */
++				    CLK_SET_RATE_UNGATE);
++
++static struct ccu_nk pll_periph0_clk = {
++	.enable		= BIT(31),
++	.lock		= BIT(28),
++	.n		= _SUNXI_CCU_MULT(8, 5),
++	.k		= _SUNXI_CCU_MULT_MIN(4, 2, 2),
++	.fixed_post_div	= 2,
++	.common		= {
++		.reg		= 0x028,
++		.features	= CCU_FEATURE_FIXED_POSTDIV,
++		.hw.init	= CLK_HW_INIT("pll-periph0", "osc24M",
++					      &ccu_nk_ops, CLK_SET_RATE_UNGATE),
++	},
++};
++
++static struct ccu_nk pll_periph1_clk = {
++	.enable		= BIT(31),
++	.lock		= BIT(28),
++	.n		= _SUNXI_CCU_MULT(8, 5),
++	.k		= _SUNXI_CCU_MULT_MIN(4, 2, 2),
++	.fixed_post_div	= 2,
++	.common		= {
++		.reg		= 0x02c,
++		.features	= CCU_FEATURE_FIXED_POSTDIV,
++		.hw.init	= CLK_HW_INIT("pll-periph1", "osc24M",
++					      &ccu_nk_ops, CLK_SET_RATE_UNGATE),
++	},
++};
++
++static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video1_clk, "pll-video1",
++					"osc24M", 0x030,
++					8, 7,		/* N */
++					0, 4,		/* M */
++					BIT(24),	/* frac enable */
++					BIT(25),	/* frac select */
++					270000000,	/* frac rate 0 */
++					297000000,	/* frac rate 1 */
++					BIT(31),	/* gate */
++					BIT(28),	/* lock */
++					CLK_SET_RATE_UNGATE);
++
++static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_gpu_clk, "pll-gpu",
++					"osc24M", 0x038,
++					8, 7,		/* N */
++					0, 4,		/* M */
++					BIT(24),	/* frac enable */
++					BIT(25),	/* frac select */
++					270000000,	/* frac rate 0 */
++					297000000,	/* frac rate 1 */
++					BIT(31),	/* gate */
++					BIT(28),	/* lock */
++					CLK_SET_RATE_UNGATE);
++
++/*
++ * The output function can be changed to something more complex that
++ * we do not handle yet.
++ *
++ * Hardcode the mode so that we don't fall in that case.
++ */
++#define SUN50I_A64_PLL_MIPI_REG		0x040
++
++struct ccu_nkm pll_mipi_clk = {
++	.enable		= BIT(31),
++	.lock		= BIT(28),
++	.n		= _SUNXI_CCU_MULT(8, 4),
++	.k		= _SUNXI_CCU_MULT_MIN(4, 2, 2),
++	.m		= _SUNXI_CCU_DIV(0, 4),
++	.common		= {
++		.reg		= 0x040,
++		.hw.init	= CLK_HW_INIT("pll-mipi", "pll-video0",
++					      &ccu_nkm_ops, CLK_SET_RATE_UNGATE),
++	},
++};
++
++static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_hsic_clk, "pll-hsic",
++					"osc24M", 0x044,
++					8, 7,		/* N */
++					0, 4,		/* M */
++					BIT(24),	/* frac enable */
++					BIT(25),	/* frac select */
++					270000000,	/* frac rate 0 */
++					297000000,	/* frac rate 1 */
++					BIT(31),	/* gate */
++					BIT(28),	/* lock */
++					CLK_SET_RATE_UNGATE);
++
++static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_de_clk, "pll-de",
++					"osc24M", 0x048,
++					8, 7,		/* N */
++					0, 4,		/* M */
++					BIT(24),	/* frac enable */
++					BIT(25),	/* frac select */
++					270000000,	/* frac rate 0 */
++					297000000,	/* frac rate 1 */
++					BIT(31),	/* gate */
++					BIT(28),	/* lock */
++					CLK_SET_RATE_UNGATE);
++
++static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_ddr1_clk, "pll-ddr1",
++				   "osc24M", 0x04c,
++				   8, 7,	/* N */
++				   0, 2,	/* M */
++				   BIT(31),	/* gate */
++				   BIT(28),	/* lock */
++				   CLK_SET_RATE_UNGATE);
++
++static const char * const cpux_parents[] = { "osc32k", "osc24M",
++					     "pll-cpux" , "pll-cpux" };
++static SUNXI_CCU_MUX(cpux_clk, "cpux", cpux_parents,
++		     0x050, 16, 2, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
++
++static SUNXI_CCU_M(axi_clk, "axi", "cpux", 0x050, 0, 2, 0);
++
++static const char * const ahb1_parents[] = { "osc32k", "osc24M",
++					     "axi" , "pll-periph0" };
++static struct ccu_div ahb1_clk = {
++	.div		= _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO),
++
++	.mux		= {
++		.shift	= 12,
++		.width	= 2,
++
++		.variable_prediv	= {
++			.index	= 3,
++			.shift	= 6,
++			.width	= 2,
++		},
++	},
++
++	.common		= {
++		.reg		= 0x054,
++		.features	= CCU_FEATURE_VARIABLE_PREDIV,
++		.hw.init	= CLK_HW_INIT_PARENTS("ahb1",
++						      ahb1_parents,
++						      &ccu_div_ops,
++						      0),
++	},
++};
++
++static struct clk_div_table apb1_div_table[] = {
++	{ .val = 0, .div = 2 },
++	{ .val = 1, .div = 2 },
++	{ .val = 2, .div = 4 },
++	{ .val = 3, .div = 8 },
++	{ /* Sentinel */ },
++};
++static SUNXI_CCU_DIV_TABLE(apb1_clk, "apb1", "ahb1",
++			   0x054, 8, 2, apb1_div_table, 0);
++
++static const char * const apb2_parents[] = { "osc32k", "osc24M",
++					     "pll-periph0-2x" ,
++					     "pll-periph0-2x" };
++static SUNXI_CCU_MP_WITH_MUX(apb2_clk, "apb2", apb2_parents, 0x058,
++			     0, 5,	/* M */
++			     16, 2,	/* P */
++			     24, 2,	/* mux */
++			     0);
++
++static const char * const ahb2_parents[] = { "ahb1" , "pll-periph0" };
++static const struct ccu_mux_fixed_prediv ahb2_fixed_predivs[] = {
++	{ .index = 1, .div = 2 },
++};
++static struct ccu_mux ahb2_clk = {
++	.mux		= {
++		.shift	= 0,
++		.width	= 1,
++		.fixed_predivs	= ahb2_fixed_predivs,
++		.n_predivs	= ARRAY_SIZE(ahb2_fixed_predivs),
++	},
++
++	.common		= {
++		.reg		= 0x05c,
++		.features	= CCU_FEATURE_FIXED_PREDIV,
++		.hw.init	= CLK_HW_INIT_PARENTS("ahb2",
++						      ahb2_parents,
++						      &ccu_mux_ops,
++						      0),
++	},
++};
++
++static SUNXI_CCU_GATE(bus_mipi_dsi_clk,	"bus-mipi-dsi",	"ahb1",
++		      0x060, BIT(1), 0);
++static SUNXI_CCU_GATE(bus_ce_clk,	"bus-ce",	"ahb1",
++		      0x060, BIT(5), 0);
++static SUNXI_CCU_GATE(bus_dma_clk,	"bus-dma",	"ahb1",
++		      0x060, BIT(6), 0);
++static SUNXI_CCU_GATE(bus_mmc0_clk,	"bus-mmc0",	"ahb1",
++		      0x060, BIT(8), 0);
++static SUNXI_CCU_GATE(bus_mmc1_clk,	"bus-mmc1",	"ahb1",
++		      0x060, BIT(9), 0);
++static SUNXI_CCU_GATE(bus_mmc2_clk,	"bus-mmc2",	"ahb1",
++		      0x060, BIT(10), 0);
++static SUNXI_CCU_GATE(bus_nand_clk,	"bus-nand",	"ahb1",
++		      0x060, BIT(13), 0);
++static SUNXI_CCU_GATE(bus_dram_clk,	"bus-dram",	"ahb1",
++		      0x060, BIT(14), 0);
++static SUNXI_CCU_GATE(bus_emac_clk,	"bus-emac",	"ahb2",
++		      0x060, BIT(17), 0);
++static SUNXI_CCU_GATE(bus_ts_clk,	"bus-ts",	"ahb1",
++		      0x060, BIT(18), 0);
++static SUNXI_CCU_GATE(bus_hstimer_clk,	"bus-hstimer",	"ahb1",
++		      0x060, BIT(19), 0);
++static SUNXI_CCU_GATE(bus_spi0_clk,	"bus-spi0",	"ahb1",
++		      0x060, BIT(20), 0);
++static SUNXI_CCU_GATE(bus_spi1_clk,	"bus-spi1",	"ahb1",
++		      0x060, BIT(21), 0);
++static SUNXI_CCU_GATE(bus_otg_clk,	"bus-otg",	"ahb1",
++		      0x060, BIT(23), 0);
++static SUNXI_CCU_GATE(bus_ehci0_clk,	"bus-ehci0",	"ahb1",
++		      0x060, BIT(24), 0);
++static SUNXI_CCU_GATE(bus_ehci1_clk,	"bus-ehci1",	"ahb2",
++		      0x060, BIT(25), 0);
++static SUNXI_CCU_GATE(bus_ohci0_clk,	"bus-ohci0",	"ahb1",
++		      0x060, BIT(28), 0);
++static SUNXI_CCU_GATE(bus_ohci1_clk,	"bus-ohci1",	"ahb2",
++		      0x060, BIT(29), 0);
++
++static SUNXI_CCU_GATE(bus_ve_clk,	"bus-ve",	"ahb1",
++		      0x064, BIT(0), 0);
++static SUNXI_CCU_GATE(bus_tcon0_clk,	"bus-tcon0",	"ahb1",
++		      0x064, BIT(3), 0);
++static SUNXI_CCU_GATE(bus_tcon1_clk,	"bus-tcon1",	"ahb1",
++		      0x064, BIT(4), 0);
++static SUNXI_CCU_GATE(bus_deinterlace_clk,	"bus-deinterlace",	"ahb1",
++		      0x064, BIT(5), 0);
++static SUNXI_CCU_GATE(bus_csi_clk,	"bus-csi",	"ahb1",
++		      0x064, BIT(8), 0);
++static SUNXI_CCU_GATE(bus_hdmi_clk,	"bus-hdmi",	"ahb1",
++		      0x064, BIT(11), 0);
++static SUNXI_CCU_GATE(bus_de_clk,	"bus-de",	"ahb1",
++		      0x064, BIT(12), 0);
++static SUNXI_CCU_GATE(bus_gpu_clk,	"bus-gpu",	"ahb1",
++		      0x064, BIT(20), 0);
++static SUNXI_CCU_GATE(bus_msgbox_clk,	"bus-msgbox",	"ahb1",
++		      0x064, BIT(21), 0);
++static SUNXI_CCU_GATE(bus_spinlock_clk,	"bus-spinlock",	"ahb1",
++		      0x064, BIT(22), 0);
++
++static SUNXI_CCU_GATE(bus_codec_clk,	"bus-codec",	"apb1",
++		      0x068, BIT(0), 0);
++static SUNXI_CCU_GATE(bus_spdif_clk,	"bus-spdif",	"apb1",
++		      0x068, BIT(1), 0);
++static SUNXI_CCU_GATE(bus_pio_clk,	"bus-pio",	"apb1",
++		      0x068, BIT(5), 0);
++static SUNXI_CCU_GATE(bus_ths_clk,	"bus-ths",	"apb1",
++		      0x068, BIT(8), 0);
++static SUNXI_CCU_GATE(bus_i2s0_clk,	"bus-i2s0",	"apb1",
++		      0x068, BIT(12), 0);
++static SUNXI_CCU_GATE(bus_i2s1_clk,	"bus-i2s1",	"apb1",
++		      0x068, BIT(13), 0);
++static SUNXI_CCU_GATE(bus_i2s2_clk,	"bus-i2s2",	"apb1",
++		      0x068, BIT(14), 0);
++
++static SUNXI_CCU_GATE(bus_i2c0_clk,	"bus-i2c0",	"apb2",
++		      0x06c, BIT(0), 0);
++static SUNXI_CCU_GATE(bus_i2c1_clk,	"bus-i2c1",	"apb2",
++		      0x06c, BIT(1), 0);
++static SUNXI_CCU_GATE(bus_i2c2_clk,	"bus-i2c2",	"apb2",
++		      0x06c, BIT(2), 0);
++static SUNXI_CCU_GATE(bus_scr_clk,	"bus-scr",	"apb2",
++		      0x06c, BIT(5), 0);
++static SUNXI_CCU_GATE(bus_uart0_clk,	"bus-uart0",	"apb2",
++		      0x06c, BIT(16), 0);
++static SUNXI_CCU_GATE(bus_uart1_clk,	"bus-uart1",	"apb2",
++		      0x06c, BIT(17), 0);
++static SUNXI_CCU_GATE(bus_uart2_clk,	"bus-uart2",	"apb2",
++		      0x06c, BIT(18), 0);
++static SUNXI_CCU_GATE(bus_uart3_clk,	"bus-uart3",	"apb2",
++		      0x06c, BIT(19), 0);
++static SUNXI_CCU_GATE(bus_uart4_clk,	"bus-uart4",	"apb2",
++		      0x06c, BIT(20), 0);
++
++static SUNXI_CCU_GATE(bus_dbg_clk,	"bus-dbg",	"ahb1",
++		      0x070, BIT(7), 0);
++
++static struct clk_div_table ths_div_table[] = {
++	{ .val = 0, .div = 1 },
++	{ .val = 1, .div = 2 },
++	{ .val = 2, .div = 4 },
++	{ .val = 3, .div = 6 },
++};
++static const char * const ths_parents[] = { "osc24M" };
++static struct ccu_div ths_clk = {
++	.enable	= BIT(31),
++	.div	= _SUNXI_CCU_DIV_TABLE(0, 2, ths_div_table),
++	.mux	= _SUNXI_CCU_MUX(24, 2),
++	.common	= {
++		.reg		= 0x074,
++		.hw.init	= CLK_HW_INIT_PARENTS("ths",
++						      ths_parents,
++						      &ccu_div_ops,
++						      0),
++	},
++};
++
++static const char * const mod0_default_parents[] = { "osc24M", "pll-periph0",
++						     "pll-periph1" };
++static SUNXI_CCU_MP_WITH_MUX_GATE(nand_clk, "nand", mod0_default_parents, 0x080,
++				  0, 4,		/* M */
++				  16, 2,	/* P */
++				  24, 2,	/* mux */
++				  BIT(31),	/* gate */
++				  0);
++
++static const char * const mmc_default_parents[] = { "osc24M", "pll-periph0-2x",
++						    "pll-periph1-2x" };
++static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mmc_default_parents, 0x088,
++				  0, 4,		/* M */
++				  16, 2,	/* P */
++				  24, 2,	/* mux */
++				  BIT(31),	/* gate */
++				  0);
++
++static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mmc_default_parents, 0x08c,
++				  0, 4,		/* M */
++				  16, 2,	/* P */
++				  24, 2,	/* mux */
++				  BIT(31),	/* gate */
++				  0);
++
++static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mmc_default_parents, 0x090,
++				  0, 4,		/* M */
++				  16, 2,	/* P */
++				  24, 2,	/* mux */
++				  BIT(31),	/* gate */
++				  0);
++
++static const char * const ts_parents[] = { "osc24M", "pll-periph0", };
++static SUNXI_CCU_MP_WITH_MUX_GATE(ts_clk, "ts", ts_parents, 0x098,
++				  0, 4,		/* M */
++				  16, 2,	/* P */
++				  24, 4,	/* mux */
++				  BIT(31),	/* gate */
++				  0);
++
++static SUNXI_CCU_MP_WITH_MUX_GATE(ce_clk, "ce", mmc_default_parents, 0x09c,
++				  0, 4,		/* M */
++				  16, 2,	/* P */
++				  24, 2,	/* mux */
++				  BIT(31),	/* gate */
++				  0);
++
++static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, 0x0a0,
++				  0, 4,		/* M */
++				  16, 2,	/* P */
++				  24, 2,	/* mux */
++				  BIT(31),	/* gate */
++				  0);
++
++static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents, 0x0a4,
++				  0, 4,		/* M */
++				  16, 2,	/* P */
++				  24, 2,	/* mux */
++				  BIT(31),	/* gate */
++				  0);
++
++static const char * const i2s_parents[] = { "pll-audio-8x", "pll-audio-4x",
++					    "pll-audio-2x", "pll-audio" };
++static SUNXI_CCU_MUX_WITH_GATE(i2s0_clk, "i2s0", i2s_parents,
++			       0x0b0, 16, 2, BIT(31), CLK_SET_RATE_PARENT);
++
++static SUNXI_CCU_MUX_WITH_GATE(i2s1_clk, "i2s1", i2s_parents,
++			       0x0b4, 16, 2, BIT(31), CLK_SET_RATE_PARENT);
++
++static SUNXI_CCU_MUX_WITH_GATE(i2s2_clk, "i2s2", i2s_parents,
++			       0x0b8, 16, 2, BIT(31), CLK_SET_RATE_PARENT);
++
++static SUNXI_CCU_M_WITH_GATE(spdif_clk, "spdif", "pll-audio",
++			     0x0c0, 0, 4, BIT(31), CLK_SET_RATE_PARENT);
++
++static SUNXI_CCU_GATE(usb_phy0_clk,	"usb-phy0",	"osc24M",
++		      0x0cc, BIT(8), 0);
++static SUNXI_CCU_GATE(usb_phy1_clk,	"usb-phy1",	"osc24M",
++		      0x0cc, BIT(9), 0);
++static SUNXI_CCU_GATE(usb_hsic_clk,	"usb-hsic",	"pll-hsic",
++		      0x0cc, BIT(10), 0);
++static SUNXI_CCU_GATE(usb_hsic_12m_clk,	"usb-hsic-12M",	"osc12M",
++		      0x0cc, BIT(11), 0);
++static SUNXI_CCU_GATE(usb_ohci0_clk,	"usb-ohci0",	"osc12M",
++		      0x0cc, BIT(16), 0);
++static SUNXI_CCU_GATE(usb_ohci1_clk,	"usb-ohci1",	"usb-ohci0",
++		      0x0cc, BIT(17), 0);
++
++static const char * const dram_parents[] = { "pll-ddr0", "pll-ddr1" };
++static SUNXI_CCU_M_WITH_MUX(dram_clk, "dram", dram_parents,
++			    0x0f4, 0, 4, 20, 2, CLK_IS_CRITICAL);
++
++static SUNXI_CCU_GATE(dram_ve_clk,	"dram-ve",	"dram",
++		      0x100, BIT(0), 0);
++static SUNXI_CCU_GATE(dram_csi_clk,	"dram-csi",	"dram",
++		      0x100, BIT(1), 0);
++static SUNXI_CCU_GATE(dram_deinterlace_clk,	"dram-deinterlace",	"dram",
++		      0x100, BIT(2), 0);
++static SUNXI_CCU_GATE(dram_ts_clk,	"dram-ts",	"dram",
++		      0x100, BIT(3), 0);
++
++static const char * const de_parents[] = { "pll-periph0-2x", "pll-de" };
++static SUNXI_CCU_M_WITH_MUX_GATE(de_clk, "de", de_parents,
++				 0x104, 0, 4, 24, 3, BIT(31), 0);
++
++static const char * const tcon0_parents[] = { "pll-mipi", "pll-video0-2x" };
++static const u8 tcon0_table[] = { 0, 2, };
++static SUNXI_CCU_MUX_TABLE_WITH_GATE(tcon0_clk, "tcon0", tcon0_parents,
++				     tcon0_table, 0x118, 24, 3, BIT(31),
++				     CLK_SET_RATE_PARENT);
++
++static const char * const tcon1_parents[] = { "pll-video0", "pll-video1" };
++static const u8 tcon1_table[] = { 0, 2, };
++struct ccu_div tcon1_clk = {
++	.enable		= BIT(31),
++	.div		= _SUNXI_CCU_DIV(0, 4),
++	.mux		= _SUNXI_CCU_MUX_TABLE(24, 2, tcon1_table),
++	.common		= {
++		.reg		= 0x11c,
++		.hw.init	= CLK_HW_INIT_PARENTS("tcon1",
++						      tcon1_parents,
++						      &ccu_div_ops,
++						      CLK_SET_RATE_PARENT),
++	},
++};
++
++static const char * const deinterlace_parents[] = { "pll-periph0", "pll-periph1" };
++static SUNXI_CCU_M_WITH_MUX_GATE(deinterlace_clk, "deinterlace", deinterlace_parents,
++				 0x124, 0, 4, 24, 3, BIT(31), 0);
++
++static SUNXI_CCU_GATE(csi_misc_clk,	"csi-misc",	"osc24M",
++		      0x130, BIT(31), 0);
++
++static const char * const csi_sclk_parents[] = { "pll-periph0", "pll-periph1" };
++static SUNXI_CCU_M_WITH_MUX_GATE(csi_sclk_clk, "csi-sclk", csi_sclk_parents,
++				 0x134, 16, 4, 24, 3, BIT(31), 0);
++
++static const char * const csi_mclk_parents[] = { "osc24M", "pll-video1", "pll-periph1" };
++static SUNXI_CCU_M_WITH_MUX_GATE(csi_mclk_clk, "csi-mclk", csi_mclk_parents,
++				 0x134, 0, 5, 8, 3, BIT(15), 0);
++
++static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve",
++			     0x13c, 16, 3, BIT(31), 0);
++
++static SUNXI_CCU_GATE(ac_dig_clk,	"ac-dig",	"pll-audio",
++		      0x140, BIT(31), CLK_SET_RATE_PARENT);
++
++static SUNXI_CCU_GATE(ac_dig_4x_clk,	"ac-dig-4x",	"pll-audio-4x",
++		      0x140, BIT(30), CLK_SET_RATE_PARENT);
++
++static SUNXI_CCU_GATE(avs_clk,		"avs",		"osc24M",
++		      0x144, BIT(31), 0);
++
++static const char * const hdmi_parents[] = { "pll-video0", "pll-video1" };
++static SUNXI_CCU_M_WITH_MUX_GATE(hdmi_clk, "hdmi", hdmi_parents,
++				 0x150, 0, 4, 24, 2, BIT(31), CLK_SET_RATE_PARENT);
++
++static SUNXI_CCU_GATE(hdmi_ddc_clk,	"hdmi-ddc",	"osc24M",
++		      0x154, BIT(31), 0);
++
++static const char * const mbus_parents[] = { "osc24M", "pll-periph0-2x",
++						 "pll-ddr0", "pll-ddr1" };
++static SUNXI_CCU_M_WITH_MUX_GATE(mbus_clk, "mbus", mbus_parents,
++				 0x15c, 0, 3, 24, 2, BIT(31), CLK_IS_CRITICAL);
++
++static const char * const dsi_dphy_parents[] = { "pll-video0", "pll-periph0" };
++static const u8 dsi_dphy_table[] = { 0, 2, };
++static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(dsi_dphy_clk, "dsi-dphy",
++				       dsi_dphy_parents, dsi_dphy_table,
++				       0x168, 0, 4, 8, 2, BIT(31), CLK_SET_RATE_PARENT);
++
++static SUNXI_CCU_M_WITH_GATE(gpu_clk, "gpu", "pll-gpu",
++			     0x1a0, 0, 3, BIT(31), CLK_SET_RATE_PARENT);
++
++/* Fixed Factor clocks */
++static CLK_FIXED_FACTOR(osc12M_clk, "osc12M", "osc24M", 1, 2, 0);
++
++/* We hardcode the divider to 4 for now */
++static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio",
++			"pll-audio-base", 4, 1, CLK_SET_RATE_PARENT);
++static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x",
++			"pll-audio-base", 2, 1, CLK_SET_RATE_PARENT);
++static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x",
++			"pll-audio-base", 1, 1, CLK_SET_RATE_PARENT);
++static CLK_FIXED_FACTOR(pll_audio_8x_clk, "pll-audio-8x",
++			"pll-audio-base", 1, 2, CLK_SET_RATE_PARENT);
++static CLK_FIXED_FACTOR(pll_periph0_2x_clk, "pll-periph0-2x",
++			"pll-periph0", 1, 2, 0);
++static CLK_FIXED_FACTOR(pll_periph1_2x_clk, "pll-periph1-2x",
++			"pll-periph1", 1, 2, 0);
++static CLK_FIXED_FACTOR(pll_video0_2x_clk, "pll-video0-2x",
++			"pll-video0", 1, 2, CLK_SET_RATE_PARENT);
++
++static struct ccu_common *sun50i_a64_ccu_clks[] = {
++	&pll_cpux_clk.common,
++	&pll_audio_base_clk.common,
++	&pll_video0_clk.common,
++	&pll_ve_clk.common,
++	&pll_ddr0_clk.common,
++	&pll_periph0_clk.common,
++	&pll_periph1_clk.common,
++	&pll_video1_clk.common,
++	&pll_gpu_clk.common,
++	&pll_mipi_clk.common,
++	&pll_hsic_clk.common,
++	&pll_de_clk.common,
++	&pll_ddr1_clk.common,
++	&cpux_clk.common,
++	&axi_clk.common,
++	&ahb1_clk.common,
++	&apb1_clk.common,
++	&apb2_clk.common,
++	&ahb2_clk.common,
++	&bus_mipi_dsi_clk.common,
++	&bus_ce_clk.common,
++	&bus_dma_clk.common,
++	&bus_mmc0_clk.common,
++	&bus_mmc1_clk.common,
++	&bus_mmc2_clk.common,
++	&bus_nand_clk.common,
++	&bus_dram_clk.common,
++	&bus_emac_clk.common,
++	&bus_ts_clk.common,
++	&bus_hstimer_clk.common,
++	&bus_spi0_clk.common,
++	&bus_spi1_clk.common,
++	&bus_otg_clk.common,
++	&bus_ehci0_clk.common,
++	&bus_ehci1_clk.common,
++	&bus_ohci0_clk.common,
++	&bus_ohci1_clk.common,
++	&bus_ve_clk.common,
++	&bus_tcon0_clk.common,
++	&bus_tcon1_clk.common,
++	&bus_deinterlace_clk.common,
++	&bus_csi_clk.common,
++	&bus_hdmi_clk.common,
++	&bus_de_clk.common,
++	&bus_gpu_clk.common,
++	&bus_msgbox_clk.common,
++	&bus_spinlock_clk.common,
++	&bus_codec_clk.common,
++	&bus_spdif_clk.common,
++	&bus_pio_clk.common,
++	&bus_ths_clk.common,
++	&bus_i2s0_clk.common,
++	&bus_i2s1_clk.common,
++	&bus_i2s2_clk.common,
++	&bus_i2c0_clk.common,
++	&bus_i2c1_clk.common,
++	&bus_i2c2_clk.common,
++	&bus_scr_clk.common,
++	&bus_uart0_clk.common,
++	&bus_uart1_clk.common,
++	&bus_uart2_clk.common,
++	&bus_uart3_clk.common,
++	&bus_uart4_clk.common,
++	&bus_dbg_clk.common,
++	&ths_clk.common,
++	&nand_clk.common,
++	&mmc0_clk.common,
++	&mmc1_clk.common,
++	&mmc2_clk.common,
++	&ts_clk.common,
++	&ce_clk.common,
++	&spi0_clk.common,
++	&spi1_clk.common,
++	&i2s0_clk.common,
++	&i2s1_clk.common,
++	&i2s2_clk.common,
++	&spdif_clk.common,
++	&usb_phy0_clk.common,
++	&usb_phy1_clk.common,
++	&usb_hsic_clk.common,
++	&usb_hsic_12m_clk.common,
++	&usb_ohci0_clk.common,
++	&usb_ohci1_clk.common,
++	&dram_clk.common,
++	&dram_ve_clk.common,
++	&dram_csi_clk.common,
++	&dram_deinterlace_clk.common,
++	&dram_ts_clk.common,
++	&de_clk.common,
++	&tcon0_clk.common,
++	&tcon1_clk.common,
++	&deinterlace_clk.common,
++	&csi_misc_clk.common,
++	&csi_sclk_clk.common,
++	&csi_mclk_clk.common,
++	&ve_clk.common,
++	&ac_dig_clk.common,
++	&ac_dig_4x_clk.common,
++	&avs_clk.common,
++	&hdmi_clk.common,
++	&hdmi_ddc_clk.common,
++	&mbus_clk.common,
++	&dsi_dphy_clk.common,
++	&gpu_clk.common,
++};
++
++static struct clk_hw_onecell_data sun50i_a64_hw_clks = {
++	.hws	= {
++		[CLK_OSC_12M]		= &osc12M_clk.hw,
++		[CLK_PLL_CPUX]		= &pll_cpux_clk.common.hw,
++		[CLK_PLL_AUDIO_BASE]	= &pll_audio_base_clk.common.hw,
++		[CLK_PLL_AUDIO]		= &pll_audio_clk.hw,
++		[CLK_PLL_AUDIO_2X]	= &pll_audio_2x_clk.hw,
++		[CLK_PLL_AUDIO_4X]	= &pll_audio_4x_clk.hw,
++		[CLK_PLL_AUDIO_8X]	= &pll_audio_8x_clk.hw,
++		[CLK_PLL_VIDEO0]	= &pll_video0_clk.common.hw,
++		[CLK_PLL_VIDEO0_2X]	= &pll_video0_2x_clk.hw,
++		[CLK_PLL_VE]		= &pll_ve_clk.common.hw,
++		[CLK_PLL_DDR0]		= &pll_ddr0_clk.common.hw,
++		[CLK_PLL_PERIPH0]	= &pll_periph0_clk.common.hw,
++		[CLK_PLL_PERIPH0_2X]	= &pll_periph0_2x_clk.hw,
++		[CLK_PLL_PERIPH1]	= &pll_periph1_clk.common.hw,
++		[CLK_PLL_PERIPH1_2X]	= &pll_periph1_2x_clk.hw,
++		[CLK_PLL_VIDEO1]	= &pll_video1_clk.common.hw,
++		[CLK_PLL_GPU]		= &pll_gpu_clk.common.hw,
++		[CLK_PLL_MIPI]  	= &pll_mipi_clk.common.hw,
++		[CLK_PLL_HSIC]		= &pll_hsic_clk.common.hw,
++		[CLK_PLL_DE]		= &pll_de_clk.common.hw,
++		[CLK_PLL_DDR1]		= &pll_ddr1_clk.common.hw,
++		[CLK_CPUX]		= &cpux_clk.common.hw,
++		[CLK_AXI]		= &axi_clk.common.hw,
++		[CLK_AHB1]		= &ahb1_clk.common.hw,
++		[CLK_APB1]		= &apb1_clk.common.hw,
++		[CLK_APB2]		= &apb2_clk.common.hw,
++		[CLK_AHB2]		= &ahb2_clk.common.hw,
++		[CLK_BUS_MIPI_DSI]	= &bus_mipi_dsi_clk.common.hw,
++		[CLK_BUS_CE]		= &bus_ce_clk.common.hw,
++		[CLK_BUS_DMA]		= &bus_dma_clk.common.hw,
++		[CLK_BUS_MMC0]		= &bus_mmc0_clk.common.hw,
++		[CLK_BUS_MMC1]		= &bus_mmc1_clk.common.hw,
++		[CLK_BUS_MMC2]		= &bus_mmc2_clk.common.hw,
++		[CLK_BUS_NAND]		= &bus_nand_clk.common.hw,
++		[CLK_BUS_DRAM]		= &bus_dram_clk.common.hw,
++		[CLK_BUS_EMAC]		= &bus_emac_clk.common.hw,
++		[CLK_BUS_TS]		= &bus_ts_clk.common.hw,
++		[CLK_BUS_HSTIMER]	= &bus_hstimer_clk.common.hw,
++		[CLK_BUS_SPI0]		= &bus_spi0_clk.common.hw,
++		[CLK_BUS_SPI1]		= &bus_spi1_clk.common.hw,
++		[CLK_BUS_OTG]		= &bus_otg_clk.common.hw,
++		[CLK_BUS_EHCI0]		= &bus_ehci0_clk.common.hw,
++		[CLK_BUS_EHCI1]		= &bus_ehci1_clk.common.hw,
++		[CLK_BUS_OHCI0]		= &bus_ohci0_clk.common.hw,
++		[CLK_BUS_OHCI1]		= &bus_ohci1_clk.common.hw,
++		[CLK_BUS_VE]		= &bus_ve_clk.common.hw,
++		[CLK_BUS_TCON0]		= &bus_tcon0_clk.common.hw,
++		[CLK_BUS_TCON1]		= &bus_tcon1_clk.common.hw,
++		[CLK_BUS_DEINTERLACE]	= &bus_deinterlace_clk.common.hw,
++		[CLK_BUS_CSI]		= &bus_csi_clk.common.hw,
++		[CLK_BUS_HDMI]		= &bus_hdmi_clk.common.hw,
++		[CLK_BUS_DE]		= &bus_de_clk.common.hw,
++		[CLK_BUS_GPU]		= &bus_gpu_clk.common.hw,
++		[CLK_BUS_MSGBOX]	= &bus_msgbox_clk.common.hw,
++		[CLK_BUS_SPINLOCK]	= &bus_spinlock_clk.common.hw,
++		[CLK_BUS_CODEC]		= &bus_codec_clk.common.hw,
++		[CLK_BUS_SPDIF]		= &bus_spdif_clk.common.hw,
++		[CLK_BUS_PIO]		= &bus_pio_clk.common.hw,
++		[CLK_BUS_THS]		= &bus_ths_clk.common.hw,
++		[CLK_BUS_I2S0]		= &bus_i2s0_clk.common.hw,
++		[CLK_BUS_I2S1]		= &bus_i2s1_clk.common.hw,
++		[CLK_BUS_I2S2]		= &bus_i2s2_clk.common.hw,
++		[CLK_BUS_I2C0]		= &bus_i2c0_clk.common.hw,
++		[CLK_BUS_I2C1]		= &bus_i2c1_clk.common.hw,
++		[CLK_BUS_I2C2]		= &bus_i2c2_clk.common.hw,
++		[CLK_BUS_UART0]		= &bus_uart0_clk.common.hw,
++		[CLK_BUS_UART1]		= &bus_uart1_clk.common.hw,
++		[CLK_BUS_UART2]		= &bus_uart2_clk.common.hw,
++		[CLK_BUS_UART3]		= &bus_uart3_clk.common.hw,
++		[CLK_BUS_UART4]		= &bus_uart4_clk.common.hw,
++		[CLK_BUS_SCR]		= &bus_scr_clk.common.hw,
++		[CLK_BUS_DBG]		= &bus_dbg_clk.common.hw,
++		[CLK_THS]		= &ths_clk.common.hw,
++		[CLK_NAND]		= &nand_clk.common.hw,
++		[CLK_MMC0]		= &mmc0_clk.common.hw,
++		[CLK_MMC1]		= &mmc1_clk.common.hw,
++		[CLK_MMC2]		= &mmc2_clk.common.hw,
++		[CLK_TS]		= &ts_clk.common.hw,
++		[CLK_CE]		= &ce_clk.common.hw,
++		[CLK_SPI0]		= &spi0_clk.common.hw,
++		[CLK_SPI1]		= &spi1_clk.common.hw,
++		[CLK_I2S0]		= &i2s0_clk.common.hw,
++		[CLK_I2S1]		= &i2s1_clk.common.hw,
++		[CLK_I2S2]		= &i2s2_clk.common.hw,
++		[CLK_SPDIF]		= &spdif_clk.common.hw,
++		[CLK_USB_PHY0]		= &usb_phy0_clk.common.hw,
++		[CLK_USB_PHY1]		= &usb_phy1_clk.common.hw,
++		[CLK_USB_HSIC]		= &usb_hsic_clk.common.hw,
++		[CLK_USB_HSIC_12M]	= &usb_hsic_12m_clk.common.hw,
++		[CLK_USB_OHCI0]		= &usb_ohci0_clk.common.hw,
++		[CLK_USB_OHCI1]		= &usb_ohci1_clk.common.hw,
++		[CLK_DRAM]		= &dram_clk.common.hw,
++		[CLK_DRAM_VE]		= &dram_ve_clk.common.hw,
++		[CLK_DRAM_CSI]		= &dram_csi_clk.common.hw,
++		[CLK_DRAM_DEINTERLACE]	= &dram_deinterlace_clk.common.hw,
++		[CLK_DRAM_TS]		= &dram_ts_clk.common.hw,
++		[CLK_DE]		= &de_clk.common.hw,
++		[CLK_TCON0]		= &tcon0_clk.common.hw,
++		[CLK_TCON1]		= &tcon1_clk.common.hw,
++		[CLK_DEINTERLACE]	= &deinterlace_clk.common.hw,
++		[CLK_CSI_MISC]		= &csi_misc_clk.common.hw,
++		[CLK_CSI_SCLK]		= &csi_sclk_clk.common.hw,
++		[CLK_CSI_MCLK]		= &csi_mclk_clk.common.hw,
++		[CLK_VE]		= &ve_clk.common.hw,
++		[CLK_AC_DIG]		= &ac_dig_clk.common.hw,
++		[CLK_AC_DIG_4X]		= &ac_dig_4x_clk.common.hw,
++		[CLK_AVS]		= &avs_clk.common.hw,
++		[CLK_HDMI]		= &hdmi_clk.common.hw,
++		[CLK_HDMI_DDC]		= &hdmi_ddc_clk.common.hw,
++		[CLK_MBUS]		= &mbus_clk.common.hw,
++		[CLK_DSI_DPHY]		= &dsi_dphy_clk.common.hw,
++		[CLK_GPU]		= &gpu_clk.common.hw,
++	},
++	.num	= CLK_NUMBER,
++};
++
++static struct ccu_reset_map sun50i_a64_ccu_resets[] = {
++	[RST_USB_PHY0]		=  { 0x0cc, BIT(0) },
++	[RST_USB_PHY1]		=  { 0x0cc, BIT(1) },
++	[RST_USB_HSIC]		=  { 0x0cc, BIT(2) },
++
++	[RST_DRAM]		=  { 0x0f4, BIT(31) },
++	[RST_MBUS]		=  { 0x0fc, BIT(31) },
++
++	[RST_BUS_MIPI_DSI]	=  { 0x2c0, BIT(1) },
++	[RST_BUS_CE]		=  { 0x2c0, BIT(5) },
++	[RST_BUS_DMA]		=  { 0x2c0, BIT(6) },
++	[RST_BUS_MMC0]		=  { 0x2c0, BIT(8) },
++	[RST_BUS_MMC1]		=  { 0x2c0, BIT(9) },
++	[RST_BUS_MMC2]		=  { 0x2c0, BIT(10) },
++	[RST_BUS_NAND]		=  { 0x2c0, BIT(13) },
++	[RST_BUS_DRAM]		=  { 0x2c0, BIT(14) },
++	[RST_BUS_EMAC]		=  { 0x2c0, BIT(17) },
++	[RST_BUS_TS]		=  { 0x2c0, BIT(18) },
++	[RST_BUS_HSTIMER]	=  { 0x2c0, BIT(19) },
++	[RST_BUS_SPI0]		=  { 0x2c0, BIT(20) },
++	[RST_BUS_SPI1]		=  { 0x2c0, BIT(21) },
++	[RST_BUS_OTG]		=  { 0x2c0, BIT(23) },
++	[RST_BUS_EHCI0]		=  { 0x2c0, BIT(24) },
++	[RST_BUS_EHCI1]		=  { 0x2c0, BIT(25) },
++	[RST_BUS_OHCI0]		=  { 0x2c0, BIT(28) },
++	[RST_BUS_OHCI1]		=  { 0x2c0, BIT(29) },
++
++	[RST_BUS_VE]		=  { 0x2c4, BIT(0) },
++	[RST_BUS_TCON0]		=  { 0x2c4, BIT(3) },
++	[RST_BUS_TCON1]		=  { 0x2c4, BIT(4) },
++	[RST_BUS_DEINTERLACE]	=  { 0x2c4, BIT(5) },
++	[RST_BUS_CSI]		=  { 0x2c4, BIT(8) },
++	[RST_BUS_HDMI0]		=  { 0x2c4, BIT(10) },
++	[RST_BUS_HDMI1]		=  { 0x2c4, BIT(11) },
++	[RST_BUS_DE]		=  { 0x2c4, BIT(12) },
++	[RST_BUS_GPU]		=  { 0x2c4, BIT(20) },
++	[RST_BUS_MSGBOX]	=  { 0x2c4, BIT(21) },
++	[RST_BUS_SPINLOCK]	=  { 0x2c4, BIT(22) },
++	[RST_BUS_DBG]		=  { 0x2c4, BIT(31) },
++
++	[RST_BUS_LVDS]		=  { 0x2c8, BIT(0) },
++
++	[RST_BUS_CODEC]		=  { 0x2d0, BIT(0) },
++	[RST_BUS_SPDIF]		=  { 0x2d0, BIT(1) },
++	[RST_BUS_THS]		=  { 0x2d0, BIT(8) },
++	[RST_BUS_I2S0]		=  { 0x2d0, BIT(12) },
++	[RST_BUS_I2S1]		=  { 0x2d0, BIT(13) },
++	[RST_BUS_I2S2]		=  { 0x2d0, BIT(14) },
++
++	[RST_BUS_I2C0]		=  { 0x2d8, BIT(0) },
++	[RST_BUS_I2C1]		=  { 0x2d8, BIT(1) },
++	[RST_BUS_I2C2]		=  { 0x2d8, BIT(2) },
++	[RST_BUS_SCR]		=  { 0x2d8, BIT(5) },
++	[RST_BUS_UART0]		=  { 0x2d8, BIT(16) },
++	[RST_BUS_UART1]		=  { 0x2d8, BIT(17) },
++	[RST_BUS_UART2]		=  { 0x2d8, BIT(18) },
++	[RST_BUS_UART3]		=  { 0x2d8, BIT(19) },
++	[RST_BUS_UART4]		=  { 0x2d8, BIT(20) },
++};
++
++static const struct sunxi_ccu_desc sun50i_a64_ccu_desc = {
++	.ccu_clks	= sun50i_a64_ccu_clks,
++	.num_ccu_clks	= ARRAY_SIZE(sun50i_a64_ccu_clks),
++
++	.hw_clks	= &sun50i_a64_hw_clks,
++
++	.resets		= sun50i_a64_ccu_resets,
++	.num_resets	= ARRAY_SIZE(sun50i_a64_ccu_resets),
++};
++
++static int sun50i_a64_ccu_probe(struct platform_device *pdev)
++{
++	struct resource *res;
++	void __iomem *reg;
++	u32 val;
++
++	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	reg = devm_ioremap_resource(&pdev->dev, res);
++	if (IS_ERR(reg))
++		return PTR_ERR(reg);
++
++	/* Force the PLL-Audio-1x divider to 4 */
++	val = readl(reg + SUN50I_A64_PLL_AUDIO_REG);
++	val &= ~GENMASK(19, 16);
++	writel(val | (3 << 16), reg + SUN50I_A64_PLL_AUDIO_REG);
++
++	writel(0x515, reg + SUN50I_A64_PLL_MIPI_REG);
++
++	return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_a64_ccu_desc);
++}
++
++static const struct of_device_id sun50i_a64_ccu_ids[] = {
++	{ .compatible = "allwinner,sun50i-a64-ccu" },
++	{ }
++};
++
++static struct platform_driver sun50i_a64_ccu_driver = {
++	.probe	= sun50i_a64_ccu_probe,
++	.driver	= {
++		.name	= "sun50i-a64-ccu",
++		.of_match_table	= sun50i_a64_ccu_ids,
++	},
++};
++builtin_platform_driver(sun50i_a64_ccu_driver);
+--- /dev/null
++++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.h
+@@ -0,0 +1,72 @@
++/*
++ * Copyright 2016 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.
++ *
++ * 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 _CCU_SUN50I_A64_H_
++#define _CCU_SUN50I_A64_H_
++
++#include <dt-bindings/clock/sun50i-a64-ccu.h>
++#include <dt-bindings/reset/sun50i-a64-ccu.h>
++
++#define CLK_OSC_12M			0
++#define CLK_PLL_CPUX			1
++#define CLK_PLL_AUDIO_BASE		2
++#define CLK_PLL_AUDIO			3
++#define CLK_PLL_AUDIO_2X		4
++#define CLK_PLL_AUDIO_4X		5
++#define CLK_PLL_AUDIO_8X		6
++#define CLK_PLL_VIDEO0			7
++#define CLK_PLL_VIDEO0_2X		8
++#define CLK_PLL_VE			9
++#define CLK_PLL_DDR0			10
++#define CLK_PLL_PERIPH0			11
++#define CLK_PLL_PERIPH0_2X		12
++#define CLK_PLL_PERIPH1			13
++#define CLK_PLL_PERIPH1_2X		14
++#define CLK_PLL_VIDEO1			15
++#define CLK_PLL_GPU			16
++#define CLK_PLL_MIPI			17
++#define CLK_PLL_HSIC			18
++#define CLK_PLL_DE			19
++#define CLK_PLL_DDR1			20
++#define CLK_CPUX			21
++#define CLK_AXI				22
++#define CLK_APB				23
++#define CLK_AHB1			24
++#define CLK_APB1			25
++#define CLK_APB2			26
++#define CLK_AHB2			27
++
++/* All the bus gates are exported */
++
++/* The first bunch of module clocks are exported */
++
++#define CLK_USB_OHCI0_12M		90
++
++#define CLK_USB_OHCI1_12M		92
++
++#define CLK_DRAM			94
++
++/* All the DRAM gates are exported */
++
++/* Some more module clocks are exported */
++
++#define CLK_MBUS			112
++
++/* And the DSI and GPU module clock is exported */
++
++#define CLK_NUMBER			(CLK_GPU + 1)
++
++#endif /* _CCU_SUN50I_A64_H_ */
+--- /dev/null
++++ b/include/dt-bindings/clock/sun50i-a64-ccu.h
+@@ -0,0 +1,134 @@
++/*
++ * Copyright (C) 2016 Maxime Ripard <maxime.ripard@free-electrons.com>
++ *
++ * This file is dual-licensed: you can use it either under the terms
++ * of the GPL or the X11 license, at your option. Note that this dual
++ * licensing only applies to this file, and not this project as a
++ * whole.
++ *
++ *  a) This file 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 file 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.
++ *
++ * Or, alternatively,
++ *
++ *  b) Permission is hereby granted, free of charge, to any person
++ *     obtaining a copy of this software and associated documentation
++ *     files (the "Software"), to deal in the Software without
++ *     restriction, including without limitation the rights to use,
++ *     copy, modify, merge, publish, distribute, sublicense, and/or
++ *     sell copies of the Software, and to permit persons to whom the
++ *     Software is furnished to do so, subject to the following
++ *     conditions:
++ *
++ *     The above copyright notice and this permission notice shall be
++ *     included in all copies or substantial portions of the Software.
++ *
++ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
++ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
++ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
++ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ *     OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef _DT_BINDINGS_CLK_SUN50I_A64_H_
++#define _DT_BINDINGS_CLK_SUN50I_A64_H_
++
++#define CLK_BUS_MIPI_DSI	28
++#define CLK_BUS_CE		29
++#define CLK_BUS_DMA		30
++#define CLK_BUS_MMC0		31
++#define CLK_BUS_MMC1		32
++#define CLK_BUS_MMC2		33
++#define CLK_BUS_NAND		34
++#define CLK_BUS_DRAM		35
++#define CLK_BUS_EMAC		36
++#define CLK_BUS_TS		37
++#define CLK_BUS_HSTIMER		38
++#define CLK_BUS_SPI0		39
++#define CLK_BUS_SPI1		40
++#define CLK_BUS_OTG		41
++#define CLK_BUS_EHCI0		42
++#define CLK_BUS_EHCI1		43
++#define CLK_BUS_OHCI0		44
++#define CLK_BUS_OHCI1		45
++#define CLK_BUS_VE		46
++#define CLK_BUS_TCON0		47
++#define CLK_BUS_TCON1		48
++#define CLK_BUS_DEINTERLACE	49
++#define CLK_BUS_CSI		50
++#define CLK_BUS_HDMI		51
++#define CLK_BUS_DE		52
++#define CLK_BUS_GPU		53
++#define CLK_BUS_MSGBOX		54
++#define CLK_BUS_SPINLOCK	55
++#define CLK_BUS_CODEC		56
++#define CLK_BUS_SPDIF		57
++#define CLK_BUS_PIO		58
++#define CLK_BUS_THS		59
++#define CLK_BUS_I2S0		60
++#define CLK_BUS_I2S1		61
++#define CLK_BUS_I2S2		62
++#define CLK_BUS_I2C0		63
++#define CLK_BUS_I2C1		64
++#define CLK_BUS_I2C2		65
++#define CLK_BUS_SCR		66
++#define CLK_BUS_UART0		67
++#define CLK_BUS_UART1		68
++#define CLK_BUS_UART2		69
++#define CLK_BUS_UART3		70
++#define CLK_BUS_UART4		71
++#define CLK_BUS_DBG		72
++#define CLK_THS			73
++#define CLK_NAND		74
++#define CLK_MMC0		75
++#define CLK_MMC1		76
++#define CLK_MMC2		77
++#define CLK_TS			78
++#define CLK_CE			79
++#define CLK_SPI0		80
++#define CLK_SPI1		81
++#define CLK_I2S0		82
++#define CLK_I2S1		83
++#define CLK_I2S2		84
++#define CLK_SPDIF		85
++#define CLK_USB_PHY0		86
++#define CLK_USB_PHY1		87
++#define CLK_USB_HSIC		88
++#define CLK_USB_HSIC_12M	89
++
++#define CLK_USB_OHCI0		91
++
++#define CLK_USB_OHCI1		93
++
++#define CLK_DRAM_VE		95
++#define CLK_DRAM_CSI		96
++#define CLK_DRAM_DEINTERLACE	97
++#define CLK_DRAM_TS		98
++#define CLK_DE			99
++#define CLK_TCON0		100
++#define CLK_TCON1		101
++#define CLK_DEINTERLACE		102
++#define CLK_CSI_MISC		103
++#define CLK_CSI_SCLK		104
++#define CLK_CSI_MCLK		105
++#define CLK_VE			106
++#define CLK_AC_DIG		107
++#define CLK_AC_DIG_4X		108
++#define CLK_AVS			109
++#define CLK_HDMI		110
++#define CLK_HDMI_DDC		111
++
++#define CLK_DSI_DPHY		113
++#define CLK_GPU			114
++
++#endif /* _DT_BINDINGS_CLK_SUN50I_H_ */
+--- /dev/null
++++ b/include/dt-bindings/reset/sun50i-a64-ccu.h
+@@ -0,0 +1,98 @@
++/*
++ * Copyright (C) 2016 Maxime Ripard <maxime.ripard@free-electrons.com>
++ *
++ * This file is dual-licensed: you can use it either under the terms
++ * of the GPL or the X11 license, at your option. Note that this dual
++ * licensing only applies to this file, and not this project as a
++ * whole.
++ *
++ *  a) This file 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 file 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.
++ *
++ * Or, alternatively,
++ *
++ *  b) Permission is hereby granted, free of charge, to any person
++ *     obtaining a copy of this software and associated documentation
++ *     files (the "Software"), to deal in the Software without
++ *     restriction, including without limitation the rights to use,
++ *     copy, modify, merge, publish, distribute, sublicense, and/or
++ *     sell copies of the Software, and to permit persons to whom the
++ *     Software is furnished to do so, subject to the following
++ *     conditions:
++ *
++ *     The above copyright notice and this permission notice shall be
++ *     included in all copies or substantial portions of the Software.
++ *
++ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
++ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
++ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
++ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ *     OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef _DT_BINDINGS_RST_SUN50I_A64_H_
++#define _DT_BINDINGS_RST_SUN50I_A64_H_
++
++#define RST_USB_PHY0		0
++#define RST_USB_PHY1		1
++#define RST_USB_HSIC		2
++#define RST_DRAM		3
++#define RST_MBUS		4
++#define RST_BUS_MIPI_DSI	5
++#define RST_BUS_CE		6
++#define RST_BUS_DMA		7
++#define RST_BUS_MMC0		8
++#define RST_BUS_MMC1		9
++#define RST_BUS_MMC2		10
++#define RST_BUS_NAND		11
++#define RST_BUS_DRAM		12
++#define RST_BUS_EMAC		13
++#define RST_BUS_TS		14
++#define RST_BUS_HSTIMER		15
++#define RST_BUS_SPI0		16
++#define RST_BUS_SPI1		17
++#define RST_BUS_OTG		18
++#define RST_BUS_EHCI0		19
++#define RST_BUS_EHCI1		20
++#define RST_BUS_OHCI0		21
++#define RST_BUS_OHCI1		22
++#define RST_BUS_VE		23
++#define RST_BUS_TCON0		24
++#define RST_BUS_TCON1		25
++#define RST_BUS_DEINTERLACE	26
++#define RST_BUS_CSI		27
++#define RST_BUS_HDMI0		28
++#define RST_BUS_HDMI1		29
++#define RST_BUS_DE		30
++#define RST_BUS_GPU		31
++#define RST_BUS_MSGBOX		32
++#define RST_BUS_SPINLOCK	33
++#define RST_BUS_DBG		34
++#define RST_BUS_LVDS		35
++#define RST_BUS_CODEC		36
++#define RST_BUS_SPDIF		37
++#define RST_BUS_THS		38
++#define RST_BUS_I2S0		39
++#define RST_BUS_I2S1		40
++#define RST_BUS_I2S2		41
++#define RST_BUS_I2C0		42
++#define RST_BUS_I2C1		43
++#define RST_BUS_I2C2		44
++#define RST_BUS_SCR		45
++#define RST_BUS_UART0		46
++#define RST_BUS_UART1		47
++#define RST_BUS_UART2		48
++#define RST_BUS_UART3		49
++#define RST_BUS_UART4		50
++
++#endif /* _DT_BINDINGS_RST_SUN50I_A64_H_ */
diff --git a/target/linux/sunxi/patches-4.9/0010-arm64-dts-add-Allwinner-A64-SoC-.dtsi.patch b/target/linux/sunxi/patches-4.9/0010-arm64-dts-add-Allwinner-A64-SoC-.dtsi.patch
new file mode 100644
index 0000000000..eaaba96fc1
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0010-arm64-dts-add-Allwinner-A64-SoC-.dtsi.patch
@@ -0,0 +1,311 @@
+From 6bc37fac30cf01c39feb17834090089304bd1d31 Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Mon, 18 Jan 2016 10:24:31 +0000
+Subject: arm64: dts: add Allwinner A64 SoC .dtsi
+
+The Allwinner A64 SoC is a low-cost chip with 4 ARM Cortex-A53 cores
+and the typical tablet / TV box peripherals.
+The SoC is based on the (32-bit) Allwinner H3 chip, sharing most of
+the peripherals and the memory map.
+Although the cores are proper 64-bit ones, the whole SoC is actually
+limited to 4GB (including all the supported DRAM), so we use 32-bit
+address and size cells. This has the nice feature of us being able to
+reuse the DT for 32-bit kernels as well.
+This .dtsi lists the hardware that we support so far.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+Acked-by: Rob Herring <robh@kernel.org>
+Acked-by: Chen-Yu Tsai <wens@csie.org>
+[Maxime: Convert to CCU binding, drop the MMC support for now]
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+---
+ Documentation/devicetree/bindings/arm/sunxi.txt |   1 +
+ MAINTAINERS                                     |   1 +
+ arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi   | 263 ++++++++++++++++++++++++
+ 3 files changed, 265 insertions(+)
+ create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+
+--- a/Documentation/devicetree/bindings/arm/sunxi.txt
++++ b/Documentation/devicetree/bindings/arm/sunxi.txt
+@@ -14,4 +14,5 @@ using one of the following compatible st
+   allwinner,sun8i-a83t
+   allwinner,sun8i-h3
+   allwinner,sun9i-a80
++  allwinner,sun50i-a64
+   nextthing,gr8
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -1026,6 +1026,7 @@ L:	linux-arm-kernel@lists.infradead.org
+ S:	Maintained
+ N:	sun[x456789]i
+ F:	arch/arm/boot/dts/ntc-gr8*
++F:	arch/arm64/boot/dts/allwinner/
+ 
+ ARM/Allwinner SoC Clock Support
+ M:	Emilio López <emilio@elopez.com.ar>
+--- /dev/null
++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+@@ -0,0 +1,263 @@
++/*
++ * Copyright (C) 2016 ARM Ltd.
++ * based on the Allwinner H3 dtsi:
++ *    Copyright (C) 2015 Jens Kuske <jenskuske@gmail.com>
++ *
++ * This file is dual-licensed: you can use it either under the terms
++ * of the GPL or the X11 license, at your option. Note that this dual
++ * licensing only applies to this file, and not this project as a
++ * whole.
++ *
++ *  a) This file 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 file 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.
++ *
++ * Or, alternatively,
++ *
++ *  b) Permission is hereby granted, free of charge, to any person
++ *     obtaining a copy of this software and associated documentation
++ *     files (the "Software"), to deal in the Software without
++ *     restriction, including without limitation the rights to use,
++ *     copy, modify, merge, publish, distribute, sublicense, and/or
++ *     sell copies of the Software, and to permit persons to whom the
++ *     Software is furnished to do so, subject to the following
++ *     conditions:
++ *
++ *     The above copyright notice and this permission notice shall be
++ *     included in all copies or substantial portions of the Software.
++ *
++ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
++ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
++ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
++ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ *     OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++#include <dt-bindings/clock/sun50i-a64-ccu.h>
++#include <dt-bindings/interrupt-controller/arm-gic.h>
++#include <dt-bindings/pinctrl/sun4i-a10.h>
++#include <dt-bindings/reset/sun50i-a64-ccu.h>
++
++/ {
++	interrupt-parent = <&gic>;
++	#address-cells = <1>;
++	#size-cells = <1>;
++
++	cpus {
++		#address-cells = <1>;
++		#size-cells = <0>;
++
++		cpu0: cpu@0 {
++			compatible = "arm,cortex-a53", "arm,armv8";
++			device_type = "cpu";
++			reg = <0>;
++			enable-method = "psci";
++		};
++
++		cpu1: cpu@1 {
++			compatible = "arm,cortex-a53", "arm,armv8";
++			device_type = "cpu";
++			reg = <1>;
++			enable-method = "psci";
++		};
++
++		cpu2: cpu@2 {
++			compatible = "arm,cortex-a53", "arm,armv8";
++			device_type = "cpu";
++			reg = <2>;
++			enable-method = "psci";
++		};
++
++		cpu3: cpu@3 {
++			compatible = "arm,cortex-a53", "arm,armv8";
++			device_type = "cpu";
++			reg = <3>;
++			enable-method = "psci";
++		};
++	};
++
++	osc24M: osc24M_clk {
++		#clock-cells = <0>;
++		compatible = "fixed-clock";
++		clock-frequency = <24000000>;
++		clock-output-names = "osc24M";
++	};
++
++	osc32k: osc32k_clk {
++		#clock-cells = <0>;
++		compatible = "fixed-clock";
++		clock-frequency = <32768>;
++		clock-output-names = "osc32k";
++	};
++
++	psci {
++		compatible = "arm,psci-0.2";
++		method = "smc";
++	};
++
++	timer {
++		compatible = "arm,armv8-timer";
++		interrupts = <GIC_PPI 13
++			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
++			     <GIC_PPI 14
++			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
++			     <GIC_PPI 11
++			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
++			     <GIC_PPI 10
++			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
++	};
++
++	soc {
++		compatible = "simple-bus";
++		#address-cells = <1>;
++		#size-cells = <1>;
++		ranges;
++
++		ccu: clock@01c20000 {
++			compatible = "allwinner,sun50i-a64-ccu";
++			reg = <0x01c20000 0x400>;
++			clocks = <&osc24M>, <&osc32k>;
++			clock-names = "hosc", "losc";
++			#clock-cells = <1>;
++			#reset-cells = <1>;
++		};
++
++		pio: pinctrl@1c20800 {
++			compatible = "allwinner,sun50i-a64-pinctrl";
++			reg = <0x01c20800 0x400>;
++			interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&ccu CLK_BUS_PIO>;
++			gpio-controller;
++			#gpio-cells = <3>;
++			interrupt-controller;
++			#interrupt-cells = <3>;
++
++			i2c1_pins: i2c1_pins {
++				pins = "PH2", "PH3";
++				function = "i2c1";
++			};
++
++			uart0_pins_a: uart0@0 {
++				pins = "PB8", "PB9";
++				function = "uart0";
++			};
++		};
++
++		uart0: serial@1c28000 {
++			compatible = "snps,dw-apb-uart";
++			reg = <0x01c28000 0x400>;
++			interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
++			reg-shift = <2>;
++			reg-io-width = <4>;
++			clocks = <&ccu CLK_BUS_UART0>;
++			resets = <&ccu RST_BUS_UART0>;
++			status = "disabled";
++		};
++
++		uart1: serial@1c28400 {
++			compatible = "snps,dw-apb-uart";
++			reg = <0x01c28400 0x400>;
++			interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
++			reg-shift = <2>;
++			reg-io-width = <4>;
++			clocks = <&ccu CLK_BUS_UART1>;
++			resets = <&ccu RST_BUS_UART1>;
++			status = "disabled";
++		};
++
++		uart2: serial@1c28800 {
++			compatible = "snps,dw-apb-uart";
++			reg = <0x01c28800 0x400>;
++			interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
++			reg-shift = <2>;
++			reg-io-width = <4>;
++			clocks = <&ccu CLK_BUS_UART2>;
++			resets = <&ccu RST_BUS_UART2>;
++			status = "disabled";
++		};
++
++		uart3: serial@1c28c00 {
++			compatible = "snps,dw-apb-uart";
++			reg = <0x01c28c00 0x400>;
++			interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
++			reg-shift = <2>;
++			reg-io-width = <4>;
++			clocks = <&ccu CLK_BUS_UART3>;
++			resets = <&ccu RST_BUS_UART3>;
++			status = "disabled";
++		};
++
++		uart4: serial@1c29000 {
++			compatible = "snps,dw-apb-uart";
++			reg = <0x01c29000 0x400>;
++			interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
++			reg-shift = <2>;
++			reg-io-width = <4>;
++			clocks = <&ccu CLK_BUS_UART4>;
++			resets = <&ccu RST_BUS_UART4>;
++			status = "disabled";
++		};
++
++		i2c0: i2c@1c2ac00 {
++			compatible = "allwinner,sun6i-a31-i2c";
++			reg = <0x01c2ac00 0x400>;
++			interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&ccu CLK_BUS_I2C0>;
++			resets = <&ccu RST_BUS_I2C0>;
++			status = "disabled";
++			#address-cells = <1>;
++			#size-cells = <0>;
++		};
++
++		i2c1: i2c@1c2b000 {
++			compatible = "allwinner,sun6i-a31-i2c";
++			reg = <0x01c2b000 0x400>;
++			interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&ccu CLK_BUS_I2C1>;
++			resets = <&ccu RST_BUS_I2C1>;
++			status = "disabled";
++			#address-cells = <1>;
++			#size-cells = <0>;
++		};
++
++		i2c2: i2c@1c2b400 {
++			compatible = "allwinner,sun6i-a31-i2c";
++			reg = <0x01c2b400 0x400>;
++			interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&ccu CLK_BUS_I2C2>;
++			resets = <&ccu RST_BUS_I2C2>;
++			status = "disabled";
++			#address-cells = <1>;
++			#size-cells = <0>;
++		};
++
++		gic: interrupt-controller@1c81000 {
++			compatible = "arm,gic-400";
++			reg = <0x01c81000 0x1000>,
++			      <0x01c82000 0x2000>,
++			      <0x01c84000 0x2000>,
++			      <0x01c86000 0x2000>;
++			interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
++			interrupt-controller;
++			#interrupt-cells = <3>;
++		};
++
++		rtc: rtc@1f00000 {
++			compatible = "allwinner,sun6i-a31-rtc";
++			reg = <0x01f00000 0x54>;
++			interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
++		};
++	};
++};
diff --git a/target/linux/sunxi/patches-4.9/0011-arm64-dts-add-Pine64-support.patch b/target/linux/sunxi/patches-4.9/0011-arm64-dts-add-Pine64-support.patch
new file mode 100644
index 0000000000..9960588ab8
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0011-arm64-dts-add-Pine64-support.patch
@@ -0,0 +1,176 @@
+From 4e3886081848b7ea16452a92c4324acaab644d49 Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Tue, 19 Jan 2016 10:36:39 +0000
+Subject: arm64: dts: add Pine64 support
+
+The Pine64 is a cost-efficient development board based on the
+Allwinner A64 SoC.
+There are three models: the basic version with Fast Ethernet and
+512 MB of DRAM (Pine64) and two Pine64+ versions, which both
+feature Gigabit Ethernet and additional connectors for touchscreens
+and a camera. Or as my son put it: "Those are smaller and these are
+missing." ;-)
+The two Pine64+ models just differ in the amount of DRAM
+(1GB vs. 2GB). Since U-Boot will figure out the right size for us and
+patches the DT accordingly we just need to provide one DT for the
+Pine64+.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+[Maxime: Removed the common DTSI and include directly the pine64 DTS]
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+---
+ arch/arm64/boot/dts/Makefile                       |  1 +
+ arch/arm64/boot/dts/allwinner/Makefile             |  5 ++
+ .../boot/dts/allwinner/sun50i-a64-pine64-plus.dts  | 50 +++++++++++++++
+ .../arm64/boot/dts/allwinner/sun50i-a64-pine64.dts | 74 ++++++++++++++++++++++
+ 4 files changed, 130 insertions(+)
+ create mode 100644 arch/arm64/boot/dts/allwinner/Makefile
+ create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts
+ create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts
+
+--- a/arch/arm64/boot/dts/Makefile
++++ b/arch/arm64/boot/dts/Makefile
+@@ -1,4 +1,5 @@
+ dts-dirs += al
++dts-dirs += allwinner
+ dts-dirs += altera
+ dts-dirs += amd
+ dts-dirs += amlogic
+--- /dev/null
++++ b/arch/arm64/boot/dts/allwinner/Makefile
+@@ -0,0 +1,5 @@
++dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a64-pine64-plus.dtb sun50i-a64-pine64.dtb
++
++always		:= $(dtb-y)
++subdir-y	:= $(dts-dirs)
++clean-files	:= *.dtb
+--- /dev/null
++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts
+@@ -0,0 +1,50 @@
++/*
++ * Copyright (c) 2016 ARM Ltd.
++ *
++ * This file is dual-licensed: you can use it either under the terms
++ * of the GPL or the X11 license, at your option. Note that this dual
++ * licensing only applies to this file, and not this project as a
++ * whole.
++ *
++ *  a) This library 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 library 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.
++ *
++ * Or, alternatively,
++ *
++ *  b) Permission is hereby granted, free of charge, to any person
++ *     obtaining a copy of this software and associated documentation
++ *     files (the "Software"), to deal in the Software without
++ *     restriction, including without limitation the rights to use,
++ *     copy, modify, merge, publish, distribute, sublicense, and/or
++ *     sell copies of the Software, and to permit persons to whom the
++ *     Software is furnished to do so, subject to the following
++ *     conditions:
++ *
++ *     The above copyright notice and this permission notice shall be
++ *     included in all copies or substantial portions of the Software.
++ *
++ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
++ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
++ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
++ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ *     OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++#include "sun50i-a64-pine64.dts"
++
++/ {
++	model = "Pine64+";
++	compatible = "pine64,pine64-plus", "allwinner,sun50i-a64";
++
++	/* TODO: Camera, Ethernet PHY, touchscreen, etc. */
++};
+--- /dev/null
++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts
+@@ -0,0 +1,74 @@
++/*
++ * Copyright (c) 2016 ARM Ltd.
++ *
++ * This file is dual-licensed: you can use it either under the terms
++ * of the GPL or the X11 license, at your option. Note that this dual
++ * licensing only applies to this file, and not this project as a
++ * whole.
++ *
++ *  a) This library 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 library 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.
++ *
++ * Or, alternatively,
++ *
++ *  b) Permission is hereby granted, free of charge, to any person
++ *     obtaining a copy of this software and associated documentation
++ *     files (the "Software"), to deal in the Software without
++ *     restriction, including without limitation the rights to use,
++ *     copy, modify, merge, publish, distribute, sublicense, and/or
++ *     sell copies of the Software, and to permit persons to whom the
++ *     Software is furnished to do so, subject to the following
++ *     conditions:
++ *
++ *     The above copyright notice and this permission notice shall be
++ *     included in all copies or substantial portions of the Software.
++ *
++ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
++ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
++ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
++ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ *     OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++/dts-v1/;
++
++#include "sun50i-a64.dtsi"
++
++/ {
++	model = "Pine64";
++	compatible = "pine64,pine64", "allwinner,sun50i-a64";
++
++	aliases {
++		serial0 = &uart0;
++	};
++
++	chosen {
++		stdout-path = "serial0:115200n8";
++	};
++};
++
++&uart0 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&uart0_pins_a>;
++	status = "okay";
++};
++
++&i2c1 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&i2c1_pins>;
++	status = "okay";
++};
++
++&i2c1_pins {
++	bias-pull-up;
++};
diff --git a/target/linux/sunxi/patches-4.9/0012-arm64-dts-fix-build-errors-from-missing-dependencies.patch b/target/linux/sunxi/patches-4.9/0012-arm64-dts-fix-build-errors-from-missing-dependencies.patch
new file mode 100644
index 0000000000..1719b682b3
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0012-arm64-dts-fix-build-errors-from-missing-dependencies.patch
@@ -0,0 +1,134 @@
+From f98121f3ef3d36f4d040b11ab38f15387f6eefa2 Mon Sep 17 00:00:00 2001
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Wed, 30 Nov 2016 15:08:55 +0100
+Subject: arm64: dts: fix build errors from missing dependencies
+
+Two branches were incorrectly sent without having the necessary
+header file changes. Rather than back those out now, I'm replacing
+the symbolic names for the clks and resets with the numeric
+values to get 'make allmodconfig dtbs' back to work.
+
+After the header file changes are merged, we can revert this
+patch.
+
+Fixes: 6bc37fa ("arm64: dts: add Allwinner A64 SoC .dtsi")
+Fixes: 50784e6 ("dts: arm64: db820c: add pmic pins specific dts file")
+Acked-by: Andre Przywara <andre.przywara@arm.com>
+Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Acked-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+---
+ arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi      | 36 ++++++++++------------
+ .../boot/dts/qcom/apq8096-db820c-pmic-pins.dtsi    |  2 +-
+ 2 files changed, 18 insertions(+), 20 deletions(-)
+
+--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+@@ -42,10 +42,8 @@
+  *     OTHER DEALINGS IN THE SOFTWARE.
+  */
+ 
+-#include <dt-bindings/clock/sun50i-a64-ccu.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/pinctrl/sun4i-a10.h>
+-#include <dt-bindings/reset/sun50i-a64-ccu.h>
+ 
+ / {
+ 	interrupt-parent = <&gic>;
+@@ -137,7 +135,7 @@
+ 			interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
+ 				     <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>,
+ 				     <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
+-			clocks = <&ccu CLK_BUS_PIO>;
++			clocks = <&ccu 58>;
+ 			gpio-controller;
+ 			#gpio-cells = <3>;
+ 			interrupt-controller;
+@@ -160,8 +158,8 @@
+ 			interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
+ 			reg-shift = <2>;
+ 			reg-io-width = <4>;
+-			clocks = <&ccu CLK_BUS_UART0>;
+-			resets = <&ccu RST_BUS_UART0>;
++			clocks = <&ccu 67>;
++			resets = <&ccu 46>;
+ 			status = "disabled";
+ 		};
+ 
+@@ -171,8 +169,8 @@
+ 			interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
+ 			reg-shift = <2>;
+ 			reg-io-width = <4>;
+-			clocks = <&ccu CLK_BUS_UART1>;
+-			resets = <&ccu RST_BUS_UART1>;
++			clocks = <&ccu 68>;
++			resets = <&ccu 47>;
+ 			status = "disabled";
+ 		};
+ 
+@@ -182,8 +180,8 @@
+ 			interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
+ 			reg-shift = <2>;
+ 			reg-io-width = <4>;
+-			clocks = <&ccu CLK_BUS_UART2>;
+-			resets = <&ccu RST_BUS_UART2>;
++			clocks = <&ccu 69>;
++			resets = <&ccu 48>;
+ 			status = "disabled";
+ 		};
+ 
+@@ -193,8 +191,8 @@
+ 			interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
+ 			reg-shift = <2>;
+ 			reg-io-width = <4>;
+-			clocks = <&ccu CLK_BUS_UART3>;
+-			resets = <&ccu RST_BUS_UART3>;
++			clocks = <&ccu 70>;
++			resets = <&ccu 49>;
+ 			status = "disabled";
+ 		};
+ 
+@@ -204,8 +202,8 @@
+ 			interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
+ 			reg-shift = <2>;
+ 			reg-io-width = <4>;
+-			clocks = <&ccu CLK_BUS_UART4>;
+-			resets = <&ccu RST_BUS_UART4>;
++			clocks = <&ccu 71>;
++			resets = <&ccu 50>;
+ 			status = "disabled";
+ 		};
+ 
+@@ -213,8 +211,8 @@
+ 			compatible = "allwinner,sun6i-a31-i2c";
+ 			reg = <0x01c2ac00 0x400>;
+ 			interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+-			clocks = <&ccu CLK_BUS_I2C0>;
+-			resets = <&ccu RST_BUS_I2C0>;
++			clocks = <&ccu 63>;
++			resets = <&ccu 42>;
+ 			status = "disabled";
+ 			#address-cells = <1>;
+ 			#size-cells = <0>;
+@@ -224,8 +222,8 @@
+ 			compatible = "allwinner,sun6i-a31-i2c";
+ 			reg = <0x01c2b000 0x400>;
+ 			interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
+-			clocks = <&ccu CLK_BUS_I2C1>;
+-			resets = <&ccu RST_BUS_I2C1>;
++			clocks = <&ccu 64>;
++			resets = <&ccu 43>;
+ 			status = "disabled";
+ 			#address-cells = <1>;
+ 			#size-cells = <0>;
+@@ -235,8 +233,8 @@
+ 			compatible = "allwinner,sun6i-a31-i2c";
+ 			reg = <0x01c2b400 0x400>;
+ 			interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+-			clocks = <&ccu CLK_BUS_I2C2>;
+-			resets = <&ccu RST_BUS_I2C2>;
++			clocks = <&ccu 65>;
++			resets = <&ccu 44>;
+ 			status = "disabled";
+ 			#address-cells = <1>;
+ 			#size-cells = <0>;
diff --git a/target/linux/sunxi/patches-4.9/0030-pinctrl-sunxi-Rework-the-pin-config-building-code.patch b/target/linux/sunxi/patches-4.9/0030-pinctrl-sunxi-Rework-the-pin-config-building-code.patch
new file mode 100644
index 0000000000..498581712d
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0030-pinctrl-sunxi-Rework-the-pin-config-building-code.patch
@@ -0,0 +1,251 @@
+From f233dbca6227703eaae2f67d6d9c79819773f16b Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@free-electrons.com>
+Date: Tue, 11 Oct 2016 17:45:59 +0200
+Subject: pinctrl: sunxi: Rework the pin config building code
+
+In order to support more easily the generic pinctrl properties, rework the
+pinctrl maps configuration and split it into several sub-functions.
+
+One of the side-effects from that rework is that we only parse the pin
+configuration once, since it's going to be common to every pin, instead of
+having to parsing once for each pin.
+
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 178 +++++++++++++++++++++++++---------
+ 1 file changed, 130 insertions(+), 48 deletions(-)
+
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -145,6 +145,110 @@ static int sunxi_pctrl_get_group_pins(st
+ 	return 0;
+ }
+ 
++static bool sunxi_pctrl_has_bias_prop(struct device_node *node)
++{
++	return of_find_property(node, "allwinner,pull", NULL);
++}
++
++static bool sunxi_pctrl_has_drive_prop(struct device_node *node)
++{
++	return of_find_property(node, "allwinner,drive", NULL);
++}
++
++static int sunxi_pctrl_parse_bias_prop(struct device_node *node)
++{
++	u32 val;
++
++	if (of_property_read_u32(node, "allwinner,pull", &val))
++		return -EINVAL;
++
++	switch (val) {
++	case 1:
++		return PIN_CONFIG_BIAS_PULL_UP;
++	case 2:
++		return PIN_CONFIG_BIAS_PULL_DOWN;
++	}
++
++	return -EINVAL;
++}
++
++static int sunxi_pctrl_parse_drive_prop(struct device_node *node)
++{
++	u32 val;
++
++	if (of_property_read_u32(node, "allwinner,drive", &val))
++		return -EINVAL;
++
++	return (val + 1) * 10;
++}
++
++static const char *sunxi_pctrl_parse_function_prop(struct device_node *node)
++{
++	const char *function;
++	int ret;
++
++	ret = of_property_read_string(node, "allwinner,function", &function);
++	if (!ret)
++		return function;
++
++	return NULL;
++}
++
++static const char *sunxi_pctrl_find_pins_prop(struct device_node *node,
++					      int *npins)
++{
++	int count;
++
++	count = of_property_count_strings(node, "allwinner,pins");
++	if (count > 0) {
++		*npins = count;
++		return "allwinner,pins";
++	}
++
++	return NULL;
++}
++
++static unsigned long *sunxi_pctrl_build_pin_config(struct device_node *node,
++						   unsigned int *len)
++{
++	unsigned long *pinconfig;
++	unsigned int configlen = 0, idx = 0;
++
++	if (sunxi_pctrl_has_drive_prop(node))
++		configlen++;
++	if (sunxi_pctrl_has_bias_prop(node))
++		configlen++;
++
++	pinconfig = kzalloc(configlen * sizeof(*pinconfig), GFP_KERNEL);
++	if (!pinconfig)
++		return NULL;
++
++	if (sunxi_pctrl_has_drive_prop(node)) {
++		int drive = sunxi_pctrl_parse_drive_prop(node);
++		if (drive < 0)
++			goto err_free;
++
++		pinconfig[idx++] = pinconf_to_config_packed(PIN_CONFIG_DRIVE_STRENGTH,
++							  drive);
++	}
++
++	if (sunxi_pctrl_has_bias_prop(node)) {
++		int pull = sunxi_pctrl_parse_bias_prop(node);
++		if (pull < 0)
++			goto err_free;
++
++		pinconfig[idx++] = pinconf_to_config_packed(pull, 0);
++	}
++
++
++	*len = configlen;
++	return pinconfig;
++
++err_free:
++	kfree(pinconfig);
++	return NULL;
++}
++
+ static int sunxi_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
+ 				      struct device_node *node,
+ 				      struct pinctrl_map **map,
+@@ -153,38 +257,45 @@ static int sunxi_pctrl_dt_node_to_map(st
+ 	struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+ 	unsigned long *pinconfig;
+ 	struct property *prop;
+-	const char *function;
++	const char *function, *pin_prop;
+ 	const char *group;
+-	int ret, nmaps, i = 0;
+-	u32 val;
++	int ret, npins, nmaps, configlen = 0, i = 0;
+ 
+ 	*map = NULL;
+ 	*num_maps = 0;
+ 
+-	ret = of_property_read_string(node, "allwinner,function", &function);
+-	if (ret) {
+-		dev_err(pctl->dev,
+-			"missing allwinner,function property in node %s\n",
++	function = sunxi_pctrl_parse_function_prop(node);
++	if (!function) {
++		dev_err(pctl->dev, "missing function property in node %s\n",
+ 			node->name);
+ 		return -EINVAL;
+ 	}
+ 
+-	nmaps = of_property_count_strings(node, "allwinner,pins") * 2;
+-	if (nmaps < 0) {
+-		dev_err(pctl->dev,
+-			"missing allwinner,pins property in node %s\n",
++	pin_prop = sunxi_pctrl_find_pins_prop(node, &npins);
++	if (!pin_prop) {
++		dev_err(pctl->dev, "missing pins property in node %s\n",
+ 			node->name);
+ 		return -EINVAL;
+ 	}
+ 
++	/*
++	 * We have two maps for each pin: one for the function, one
++	 * for the configuration (bias, strength, etc)
++	 */
++	nmaps = npins * 2;
+ 	*map = kmalloc(nmaps * sizeof(struct pinctrl_map), GFP_KERNEL);
+ 	if (!*map)
+ 		return -ENOMEM;
+ 
+-	of_property_for_each_string(node, "allwinner,pins", prop, group) {
++	pinconfig = sunxi_pctrl_build_pin_config(node, &configlen);
++	if (!pinconfig) {
++		ret = -EINVAL;
++		goto err_free_map;
++	}
++
++	of_property_for_each_string(node, pin_prop, prop, group) {
+ 		struct sunxi_pinctrl_group *grp =
+ 			sunxi_pinctrl_find_group_by_name(pctl, group);
+-		int j = 0, configlen = 0;
+ 
+ 		if (!grp) {
+ 			dev_err(pctl->dev, "unknown pin %s", group);
+@@ -207,34 +318,6 @@ static int sunxi_pctrl_dt_node_to_map(st
+ 
+ 		(*map)[i].type = PIN_MAP_TYPE_CONFIGS_GROUP;
+ 		(*map)[i].data.configs.group_or_pin = group;
+-
+-		if (of_find_property(node, "allwinner,drive", NULL))
+-			configlen++;
+-		if (of_find_property(node, "allwinner,pull", NULL))
+-			configlen++;
+-
+-		pinconfig = kzalloc(configlen * sizeof(*pinconfig), GFP_KERNEL);
+-		if (!pinconfig) {
+-			kfree(*map);
+-			return -ENOMEM;
+-		}
+-
+-		if (!of_property_read_u32(node, "allwinner,drive", &val)) {
+-			u16 strength = (val + 1) * 10;
+-			pinconfig[j++] =
+-				pinconf_to_config_packed(PIN_CONFIG_DRIVE_STRENGTH,
+-							 strength);
+-		}
+-
+-		if (!of_property_read_u32(node, "allwinner,pull", &val)) {
+-			enum pin_config_param pull = PIN_CONFIG_END;
+-			if (val == 1)
+-				pull = PIN_CONFIG_BIAS_PULL_UP;
+-			else if (val == 2)
+-				pull = PIN_CONFIG_BIAS_PULL_DOWN;
+-			pinconfig[j++] = pinconf_to_config_packed(pull, 0);
+-		}
+-
+ 		(*map)[i].data.configs.configs = pinconfig;
+ 		(*map)[i].data.configs.num_configs = configlen;
+ 
+@@ -244,19 +327,18 @@ static int sunxi_pctrl_dt_node_to_map(st
+ 	*num_maps = nmaps;
+ 
+ 	return 0;
++
++err_free_map:
++	kfree(map);
++	return ret;
+ }
+ 
+ static void sunxi_pctrl_dt_free_map(struct pinctrl_dev *pctldev,
+ 				    struct pinctrl_map *map,
+ 				    unsigned num_maps)
+ {
+-	int i;
+-
+-	for (i = 0; i < num_maps; i++) {
+-		if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP)
+-			kfree(map[i].data.configs.configs);
+-	}
+-
++	/* All the maps have the same pin config, free only the first one */
++	kfree(map[0].data.configs.configs);
+ 	kfree(map);
+ }
+ 
diff --git a/target/linux/sunxi/patches-4.9/0031-pinctrl-sunxi-Use-macros-from-bindings-header-file-f.patch b/target/linux/sunxi/patches-4.9/0031-pinctrl-sunxi-Use-macros-from-bindings-header-file-f.patch
new file mode 100644
index 0000000000..39be965422
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0031-pinctrl-sunxi-Use-macros-from-bindings-header-file-f.patch
@@ -0,0 +1,38 @@
+From 42676fa4aa87eda4fc762df495d4bde2ddc4bfce Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@free-electrons.com>
+Date: Tue, 11 Oct 2016 17:46:00 +0200
+Subject: pinctrl: sunxi: Use macros from bindings header file for DT parsing
+
+Since we have some bindings header for our hardcoded flags, let's use them
+when we can.
+
+Acked-by: Chen-Yu Tsai <wens@csie.org>
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -28,6 +28,8 @@
+ #include <linux/platform_device.h>
+ #include <linux/slab.h>
+ 
++#include <dt-bindings/pinctrl/sun4i-a10.h>
++
+ #include "../core.h"
+ #include "pinctrl-sunxi.h"
+ 
+@@ -163,9 +165,9 @@ static int sunxi_pctrl_parse_bias_prop(s
+ 		return -EINVAL;
+ 
+ 	switch (val) {
+-	case 1:
++	case SUN4I_PINCTRL_PULL_UP:
+ 		return PIN_CONFIG_BIAS_PULL_UP;
+-	case 2:
++	case SUN4I_PINCTRL_PULL_DOWN:
+ 		return PIN_CONFIG_BIAS_PULL_DOWN;
+ 	}
+ 
diff --git a/target/linux/sunxi/patches-4.9/0032-pinctrl-sunxi-Handle-bias-disable.patch b/target/linux/sunxi/patches-4.9/0032-pinctrl-sunxi-Handle-bias-disable.patch
new file mode 100644
index 0000000000..61d6102c92
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0032-pinctrl-sunxi-Handle-bias-disable.patch
@@ -0,0 +1,42 @@
+From 07fe64ba213f36ca8f6ffd8c4d5893f022744fdb Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@free-electrons.com>
+Date: Tue, 11 Oct 2016 17:46:01 +0200
+Subject: pinctrl: sunxi: Handle bias disable
+
+So far, putting NO_PULL in allwinner,pull was ignored, behaving like if
+that property was not there at all.
+
+Obviously, this is not the right thing to do, and in that case, we really
+need to just disable the bias.
+
+Acked-by: Chen-Yu Tsai <wens@csie.org>
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -165,6 +165,8 @@ static int sunxi_pctrl_parse_bias_prop(s
+ 		return -EINVAL;
+ 
+ 	switch (val) {
++	case SUN4I_PINCTRL_NO_PULL:
++		return PIN_CONFIG_BIAS_DISABLE;
+ 	case SUN4I_PINCTRL_PULL_UP:
+ 		return PIN_CONFIG_BIAS_PULL_UP;
+ 	case SUN4I_PINCTRL_PULL_DOWN:
+@@ -401,6 +403,12 @@ static int sunxi_pconf_group_set(struct
+ 				| dlevel << sunxi_dlevel_offset(pin),
+ 				pctl->membase + sunxi_dlevel_reg(pin));
+ 			break;
++		case PIN_CONFIG_BIAS_DISABLE:
++			val = readl(pctl->membase + sunxi_pull_reg(pin));
++			mask = PULL_PINS_MASK << sunxi_pull_offset(pin);
++			writel((val & ~mask),
++			       pctl->membase + sunxi_pull_reg(pin));
++			break;
+ 		case PIN_CONFIG_BIAS_PULL_UP:
+ 			val = readl(pctl->membase + sunxi_pull_reg(pin));
+ 			mask = PULL_PINS_MASK << sunxi_pull_offset(pin);
diff --git a/target/linux/sunxi/patches-4.9/0033-pinctrl-sunxi-Support-generic-binding.patch b/target/linux/sunxi/patches-4.9/0033-pinctrl-sunxi-Support-generic-binding.patch
new file mode 100644
index 0000000000..35c6876812
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0033-pinctrl-sunxi-Support-generic-binding.patch
@@ -0,0 +1,106 @@
+From cefbf1a1b29531a970bc2908a50a75d6474fcc38 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@free-electrons.com>
+Date: Thu, 20 Oct 2016 15:49:03 +0200
+Subject: pinctrl: sunxi: Support generic binding
+
+Our bindings are mostly irrelevant now that we have generic pinctrl
+bindings that cover exactly the same uses cases.
+
+Add support for the new ones, and obviously keep our old binding support in
+order to keep the ABI stable.
+
+Acked-by: Chen-Yu Tsai <wens@csie.org>
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 48 +++++++++++++++++++++++++++++++++--
+ 1 file changed, 46 insertions(+), 2 deletions(-)
+
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -149,18 +149,33 @@ static int sunxi_pctrl_get_group_pins(st
+ 
+ static bool sunxi_pctrl_has_bias_prop(struct device_node *node)
+ {
+-	return of_find_property(node, "allwinner,pull", NULL);
++	return of_find_property(node, "bias-pull-up", NULL) ||
++		of_find_property(node, "bias-pull-down", NULL) ||
++		of_find_property(node, "bias-disable", NULL) ||
++		of_find_property(node, "allwinner,pull", NULL);
+ }
+ 
+ static bool sunxi_pctrl_has_drive_prop(struct device_node *node)
+ {
+-	return of_find_property(node, "allwinner,drive", NULL);
++	return of_find_property(node, "drive-strength", NULL) ||
++		of_find_property(node, "allwinner,drive", NULL);
+ }
+ 
+ static int sunxi_pctrl_parse_bias_prop(struct device_node *node)
+ {
+ 	u32 val;
+ 
++	/* Try the new style binding */
++	if (of_find_property(node, "bias-pull-up", NULL))
++		return PIN_CONFIG_BIAS_PULL_UP;
++
++	if (of_find_property(node, "bias-pull-down", NULL))
++		return PIN_CONFIG_BIAS_PULL_DOWN;
++
++	if (of_find_property(node, "bias-disable", NULL))
++		return PIN_CONFIG_BIAS_DISABLE;
++
++	/* And fall back to the old binding */
+ 	if (of_property_read_u32(node, "allwinner,pull", &val))
+ 		return -EINVAL;
+ 
+@@ -180,6 +195,21 @@ static int sunxi_pctrl_parse_drive_prop(
+ {
+ 	u32 val;
+ 
++	/* Try the new style binding */
++	if (!of_property_read_u32(node, "drive-strength", &val)) {
++		/* We can't go below 10mA ... */
++		if (val < 10)
++			return -EINVAL;
++
++		/* ... and only up to 40 mA ... */
++		if (val > 40)
++			val = 40;
++
++		/* by steps of 10 mA */
++		return rounddown(val, 10);
++	}
++
++	/* And then fall back to the old binding */
+ 	if (of_property_read_u32(node, "allwinner,drive", &val))
+ 		return -EINVAL;
+ 
+@@ -191,6 +221,12 @@ static const char *sunxi_pctrl_parse_fun
+ 	const char *function;
+ 	int ret;
+ 
++	/* Try the generic binding */
++	ret = of_property_read_string(node, "function", &function);
++	if (!ret)
++		return function;
++
++	/* And fall back to our legacy one */
+ 	ret = of_property_read_string(node, "allwinner,function", &function);
+ 	if (!ret)
+ 		return function;
+@@ -203,6 +239,14 @@ static const char *sunxi_pctrl_find_pins
+ {
+ 	int count;
+ 
++	/* Try the generic binding */
++	count = of_property_count_strings(node, "pins");
++	if (count > 0) {
++		*npins = count;
++		return "pins";
++	}
++
++	/* And fall back to our legacy one */
+ 	count = of_property_count_strings(node, "allwinner,pins");
+ 	if (count > 0) {
+ 		*npins = count;
diff --git a/target/linux/sunxi/patches-4.9/0034-pinctrl-sunxi-Deal-with-configless-pins.patch b/target/linux/sunxi/patches-4.9/0034-pinctrl-sunxi-Deal-with-configless-pins.patch
new file mode 100644
index 0000000000..119ab2b8f9
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0034-pinctrl-sunxi-Deal-with-configless-pins.patch
@@ -0,0 +1,128 @@
+From e11dee2e98f8abc99ad5336796576a827853ccfa Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@free-electrons.com>
+Date: Thu, 20 Oct 2016 15:49:02 +0200
+Subject: pinctrl: sunxi: Deal with configless pins
+
+Even though the our binding had the assumption that the allwinner,pull and
+allwinner,drive properties were optional, the code never took that into
+account.
+
+Fix that.
+
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Acked-by: Chen-Yu Tsai <wens@csie.org>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 51 +++++++++++++++++++++++++----------
+ 1 file changed, 37 insertions(+), 14 deletions(-)
+
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -261,20 +261,29 @@ static unsigned long *sunxi_pctrl_build_
+ {
+ 	unsigned long *pinconfig;
+ 	unsigned int configlen = 0, idx = 0;
++	int ret;
+ 
+ 	if (sunxi_pctrl_has_drive_prop(node))
+ 		configlen++;
+ 	if (sunxi_pctrl_has_bias_prop(node))
+ 		configlen++;
+ 
++	/*
++	 * If we don't have any configuration, bail out
++	 */
++	if (!configlen)
++		return NULL;
++
+ 	pinconfig = kzalloc(configlen * sizeof(*pinconfig), GFP_KERNEL);
+ 	if (!pinconfig)
+-		return NULL;
++		return ERR_PTR(-ENOMEM);
+ 
+ 	if (sunxi_pctrl_has_drive_prop(node)) {
+ 		int drive = sunxi_pctrl_parse_drive_prop(node);
+-		if (drive < 0)
++		if (drive < 0) {
++			ret = drive;
+ 			goto err_free;
++		}
+ 
+ 		pinconfig[idx++] = pinconf_to_config_packed(PIN_CONFIG_DRIVE_STRENGTH,
+ 							  drive);
+@@ -282,8 +291,10 @@ static unsigned long *sunxi_pctrl_build_
+ 
+ 	if (sunxi_pctrl_has_bias_prop(node)) {
+ 		int pull = sunxi_pctrl_parse_bias_prop(node);
+-		if (pull < 0)
++		if (pull < 0) {
++			ret = pull;
+ 			goto err_free;
++		}
+ 
+ 		pinconfig[idx++] = pinconf_to_config_packed(pull, 0);
+ 	}
+@@ -294,7 +305,7 @@ static unsigned long *sunxi_pctrl_build_
+ 
+ err_free:
+ 	kfree(pinconfig);
+-	return NULL;
++	return ERR_PTR(ret);
+ }
+ 
+ static int sunxi_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
+@@ -328,7 +339,10 @@ static int sunxi_pctrl_dt_node_to_map(st
+ 
+ 	/*
+ 	 * We have two maps for each pin: one for the function, one
+-	 * for the configuration (bias, strength, etc)
++	 * for the configuration (bias, strength, etc).
++	 *
++	 * We might be slightly overshooting, since we might not have
++	 * any configuration.
+ 	 */
+ 	nmaps = npins * 2;
+ 	*map = kmalloc(nmaps * sizeof(struct pinctrl_map), GFP_KERNEL);
+@@ -336,8 +350,8 @@ static int sunxi_pctrl_dt_node_to_map(st
+ 		return -ENOMEM;
+ 
+ 	pinconfig = sunxi_pctrl_build_pin_config(node, &configlen);
+-	if (!pinconfig) {
+-		ret = -EINVAL;
++	if (IS_ERR(pinconfig)) {
++		ret = PTR_ERR(pinconfig);
+ 		goto err_free_map;
+ 	}
+ 
+@@ -364,15 +378,24 @@ static int sunxi_pctrl_dt_node_to_map(st
+ 
+ 		i++;
+ 
+-		(*map)[i].type = PIN_MAP_TYPE_CONFIGS_GROUP;
+-		(*map)[i].data.configs.group_or_pin = group;
+-		(*map)[i].data.configs.configs = pinconfig;
+-		(*map)[i].data.configs.num_configs = configlen;
+-
+-		i++;
++		if (pinconfig) {
++			(*map)[i].type = PIN_MAP_TYPE_CONFIGS_GROUP;
++			(*map)[i].data.configs.group_or_pin = group;
++			(*map)[i].data.configs.configs = pinconfig;
++			(*map)[i].data.configs.num_configs = configlen;
++			i++;
++		}
+ 	}
+ 
+-	*num_maps = nmaps;
++	*num_maps = i;
++
++	/*
++	 * We know have the number of maps we need, we can resize our
++	 * map array
++	 */
++	*map = krealloc(*map, i * sizeof(struct pinctrl_map), GFP_KERNEL);
++	if (!map)
++		return -ENOMEM;
+ 
+ 	return 0;
+ 
diff --git a/target/linux/sunxi/patches-4.9/0035-pinctrl-sunxi-make-bool-drivers-explicitly-non-modul.patch b/target/linux/sunxi/patches-4.9/0035-pinctrl-sunxi-make-bool-drivers-explicitly-non-modul.patch
new file mode 100644
index 0000000000..06680e5ffb
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0035-pinctrl-sunxi-make-bool-drivers-explicitly-non-modul.patch
@@ -0,0 +1,437 @@
+From 0c8c6ba00cbf2c0a6164aa41d43d017d65caf321 Mon Sep 17 00:00:00 2001
+From: Paul Gortmaker <paul.gortmaker@windriver.com>
+Date: Sat, 29 Oct 2016 20:00:30 -0400
+Subject: pinctrl: sunxi: make bool drivers explicitly non-modular
+
+None of the Kconfigs for any of these drivers are tristate,
+meaning that they currently are not being built as a module by anyone.
+
+Lets remove the modular code that is essentially orphaned, so that
+when reading the drivers there is no doubt they are builtin-only.  All
+drivers get essentially the same change, so they are handled in batch.
+
+Changes are (1) use builtin_platform_driver, (2) use init.h header
+(3) delete module_exit related code, (4) delete MODULE_DEVICE_TABLE,
+and (5) delete MODULE_LICENCE/MODULE_AUTHOR and associated tags.
+
+Since module_platform_driver() uses the same init level priority as
+builtin_platform_driver() the init ordering remains unchanged with
+this commit.
+
+Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code.
+
+We do delete the MODULE_LICENSE etc. tags since all that information
+is already contained at the top of each file in the comments.
+
+Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
+Cc: Chen-Yu Tsai <wens@csie.org>
+Cc: Hans de Goede <hdegoede@redhat.com>
+Cc: Linus Walleij <linus.walleij@linaro.org>
+Cc: Patrice Chotard <patrice.chotard@st.com>
+Cc: Hongzhou Yang <hongzhou.yang@mediatek.com>
+Cc: Fabian Frederick <fabf@skynet.be>
+Cc: Maxime Coquelin <maxime.coquelin@st.com>
+Cc: Vishnu Patekar <vishnupatekar0510@gmail.com>
+Cc: Mylene Josserand <mylene.josserand@free-electrons.com>
+Cc: linux-gpio@vger.kernel.org
+Cc: linux-arm-kernel@lists.infradead.org
+Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com>
+Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/sunxi/pinctrl-gr8.c         |  9 ++-------
+ drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c   |  9 ++-------
+ drivers/pinctrl/sunxi/pinctrl-sun5i-a10s.c  |  9 ++-------
+ drivers/pinctrl/sunxi/pinctrl-sun5i-a13.c   |  9 ++-------
+ drivers/pinctrl/sunxi/pinctrl-sun6i-a31-r.c | 10 ++--------
+ drivers/pinctrl/sunxi/pinctrl-sun6i-a31.c   |  9 ++-------
+ drivers/pinctrl/sunxi/pinctrl-sun6i-a31s.c  |  9 ++-------
+ drivers/pinctrl/sunxi/pinctrl-sun7i-a20.c   |  9 ++-------
+ drivers/pinctrl/sunxi/pinctrl-sun8i-a23-r.c | 11 ++---------
+ drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c   | 10 ++--------
+ drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c   |  9 ++-------
+ drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c  |  9 ++-------
+ drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c   |  9 ++-------
+ 13 files changed, 26 insertions(+), 95 deletions(-)
+
+--- a/drivers/pinctrl/sunxi/pinctrl-gr8.c
++++ b/drivers/pinctrl/sunxi/pinctrl-gr8.c
+@@ -12,7 +12,7 @@
+  * warranty of any kind, whether express or implied.
+  */
+ 
+-#include <linux/module.h>
++#include <linux/init.h>
+ #include <linux/platform_device.h>
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+@@ -525,7 +525,6 @@ static const struct of_device_id sun5i_g
+ 	{ .compatible = "nextthing,gr8-pinctrl", },
+ 	{}
+ };
+-MODULE_DEVICE_TABLE(of, sun5i_gr8_pinctrl_match);
+ 
+ static struct platform_driver sun5i_gr8_pinctrl_driver = {
+ 	.probe	= sun5i_gr8_pinctrl_probe,
+@@ -534,8 +533,4 @@ static struct platform_driver sun5i_gr8_
+ 		.of_match_table	= sun5i_gr8_pinctrl_match,
+ 	},
+ };
+-module_platform_driver(sun5i_gr8_pinctrl_driver);
+-
+-MODULE_AUTHOR("Mylene Josserand <mylene.josserand@free-electrons.com");
+-MODULE_DESCRIPTION("NextThing GR8 pinctrl driver");
+-MODULE_LICENSE("GPL");
++builtin_platform_driver(sun5i_gr8_pinctrl_driver);
+--- a/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c
+@@ -10,7 +10,7 @@
+  * warranty of any kind, whether express or implied.
+  */
+ 
+-#include <linux/module.h>
++#include <linux/init.h>
+ #include <linux/platform_device.h>
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+@@ -1035,7 +1035,6 @@ static const struct of_device_id sun4i_a
+ 	{ .compatible = "allwinner,sun4i-a10-pinctrl", },
+ 	{}
+ };
+-MODULE_DEVICE_TABLE(of, sun4i_a10_pinctrl_match);
+ 
+ static struct platform_driver sun4i_a10_pinctrl_driver = {
+ 	.probe	= sun4i_a10_pinctrl_probe,
+@@ -1044,8 +1043,4 @@ static struct platform_driver sun4i_a10_
+ 		.of_match_table	= sun4i_a10_pinctrl_match,
+ 	},
+ };
+-module_platform_driver(sun4i_a10_pinctrl_driver);
+-
+-MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com");
+-MODULE_DESCRIPTION("Allwinner A10 pinctrl driver");
+-MODULE_LICENSE("GPL");
++builtin_platform_driver(sun4i_a10_pinctrl_driver);
+--- a/drivers/pinctrl/sunxi/pinctrl-sun5i-a10s.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sun5i-a10s.c
+@@ -10,7 +10,7 @@
+  * warranty of any kind, whether express or implied.
+  */
+ 
+-#include <linux/module.h>
++#include <linux/init.h>
+ #include <linux/platform_device.h>
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+@@ -674,7 +674,6 @@ static const struct of_device_id sun5i_a
+ 	{ .compatible = "allwinner,sun5i-a10s-pinctrl", },
+ 	{}
+ };
+-MODULE_DEVICE_TABLE(of, sun5i_a10s_pinctrl_match);
+ 
+ static struct platform_driver sun5i_a10s_pinctrl_driver = {
+ 	.probe	= sun5i_a10s_pinctrl_probe,
+@@ -683,8 +682,4 @@ static struct platform_driver sun5i_a10s
+ 		.of_match_table	= sun5i_a10s_pinctrl_match,
+ 	},
+ };
+-module_platform_driver(sun5i_a10s_pinctrl_driver);
+-
+-MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com");
+-MODULE_DESCRIPTION("Allwinner A10s pinctrl driver");
+-MODULE_LICENSE("GPL");
++builtin_platform_driver(sun5i_a10s_pinctrl_driver);
+--- a/drivers/pinctrl/sunxi/pinctrl-sun5i-a13.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sun5i-a13.c
+@@ -10,7 +10,7 @@
+  * warranty of any kind, whether express or implied.
+  */
+ 
+-#include <linux/module.h>
++#include <linux/init.h>
+ #include <linux/platform_device.h>
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+@@ -392,7 +392,6 @@ static const struct of_device_id sun5i_a
+ 	{ .compatible = "allwinner,sun5i-a13-pinctrl", },
+ 	{}
+ };
+-MODULE_DEVICE_TABLE(of, sun5i_a13_pinctrl_match);
+ 
+ static struct platform_driver sun5i_a13_pinctrl_driver = {
+ 	.probe	= sun5i_a13_pinctrl_probe,
+@@ -401,8 +400,4 @@ static struct platform_driver sun5i_a13_
+ 		.of_match_table	= sun5i_a13_pinctrl_match,
+ 	},
+ };
+-module_platform_driver(sun5i_a13_pinctrl_driver);
+-
+-MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com");
+-MODULE_DESCRIPTION("Allwinner A13 pinctrl driver");
+-MODULE_LICENSE("GPL");
++builtin_platform_driver(sun5i_a13_pinctrl_driver);
+--- a/drivers/pinctrl/sunxi/pinctrl-sun6i-a31-r.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sun6i-a31-r.c
+@@ -12,7 +12,7 @@
+  * warranty of any kind, whether express or implied.
+  */
+ 
+-#include <linux/module.h>
++#include <linux/init.h>
+ #include <linux/platform_device.h>
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+@@ -136,7 +136,6 @@ static const struct of_device_id sun6i_a
+ 	{ .compatible = "allwinner,sun6i-a31-r-pinctrl", },
+ 	{}
+ };
+-MODULE_DEVICE_TABLE(of, sun6i_a31_r_pinctrl_match);
+ 
+ static struct platform_driver sun6i_a31_r_pinctrl_driver = {
+ 	.probe	= sun6i_a31_r_pinctrl_probe,
+@@ -145,9 +144,4 @@ static struct platform_driver sun6i_a31_
+ 		.of_match_table	= sun6i_a31_r_pinctrl_match,
+ 	},
+ };
+-module_platform_driver(sun6i_a31_r_pinctrl_driver);
+-
+-MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com");
+-MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com");
+-MODULE_DESCRIPTION("Allwinner A31 R_PIO pinctrl driver");
+-MODULE_LICENSE("GPL");
++builtin_platform_driver(sun6i_a31_r_pinctrl_driver);
+--- a/drivers/pinctrl/sunxi/pinctrl-sun6i-a31.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sun6i-a31.c
+@@ -10,7 +10,7 @@
+  * warranty of any kind, whether express or implied.
+  */
+ 
+-#include <linux/module.h>
++#include <linux/init.h>
+ #include <linux/platform_device.h>
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+@@ -934,7 +934,6 @@ static const struct of_device_id sun6i_a
+ 	{ .compatible = "allwinner,sun6i-a31-pinctrl", },
+ 	{}
+ };
+-MODULE_DEVICE_TABLE(of, sun6i_a31_pinctrl_match);
+ 
+ static struct platform_driver sun6i_a31_pinctrl_driver = {
+ 	.probe	= sun6i_a31_pinctrl_probe,
+@@ -943,8 +942,4 @@ static struct platform_driver sun6i_a31_
+ 		.of_match_table	= sun6i_a31_pinctrl_match,
+ 	},
+ };
+-module_platform_driver(sun6i_a31_pinctrl_driver);
+-
+-MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com");
+-MODULE_DESCRIPTION("Allwinner A31 pinctrl driver");
+-MODULE_LICENSE("GPL");
++builtin_platform_driver(sun6i_a31_pinctrl_driver);
+--- a/drivers/pinctrl/sunxi/pinctrl-sun6i-a31s.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sun6i-a31s.c
+@@ -11,7 +11,7 @@
+  * warranty of any kind, whether express or implied.
+  */
+ 
+-#include <linux/module.h>
++#include <linux/init.h>
+ #include <linux/platform_device.h>
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+@@ -798,7 +798,6 @@ static const struct of_device_id sun6i_a
+ 	{ .compatible = "allwinner,sun6i-a31s-pinctrl", },
+ 	{}
+ };
+-MODULE_DEVICE_TABLE(of, sun6i_a31s_pinctrl_match);
+ 
+ static struct platform_driver sun6i_a31s_pinctrl_driver = {
+ 	.probe	= sun6i_a31s_pinctrl_probe,
+@@ -807,8 +806,4 @@ static struct platform_driver sun6i_a31s
+ 		.of_match_table	= sun6i_a31s_pinctrl_match,
+ 	},
+ };
+-module_platform_driver(sun6i_a31s_pinctrl_driver);
+-
+-MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+-MODULE_DESCRIPTION("Allwinner A31s pinctrl driver");
+-MODULE_LICENSE("GPL");
++builtin_platform_driver(sun6i_a31s_pinctrl_driver);
+--- a/drivers/pinctrl/sunxi/pinctrl-sun7i-a20.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sun7i-a20.c
+@@ -10,7 +10,7 @@
+  * warranty of any kind, whether express or implied.
+  */
+ 
+-#include <linux/module.h>
++#include <linux/init.h>
+ #include <linux/platform_device.h>
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+@@ -1045,7 +1045,6 @@ static const struct of_device_id sun7i_a
+ 	{ .compatible = "allwinner,sun7i-a20-pinctrl", },
+ 	{}
+ };
+-MODULE_DEVICE_TABLE(of, sun7i_a20_pinctrl_match);
+ 
+ static struct platform_driver sun7i_a20_pinctrl_driver = {
+ 	.probe	= sun7i_a20_pinctrl_probe,
+@@ -1054,8 +1053,4 @@ static struct platform_driver sun7i_a20_
+ 		.of_match_table	= sun7i_a20_pinctrl_match,
+ 	},
+ };
+-module_platform_driver(sun7i_a20_pinctrl_driver);
+-
+-MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com");
+-MODULE_DESCRIPTION("Allwinner A20 pinctrl driver");
+-MODULE_LICENSE("GPL");
++builtin_platform_driver(sun7i_a20_pinctrl_driver);
+--- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a23-r.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a23-r.c
+@@ -15,7 +15,7 @@
+  * warranty of any kind, whether express or implied.
+  */
+ 
+-#include <linux/module.h>
++#include <linux/init.h>
+ #include <linux/platform_device.h>
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+@@ -123,7 +123,6 @@ static const struct of_device_id sun8i_a
+ 	{ .compatible = "allwinner,sun8i-a23-r-pinctrl", },
+ 	{}
+ };
+-MODULE_DEVICE_TABLE(of, sun8i_a23_r_pinctrl_match);
+ 
+ static struct platform_driver sun8i_a23_r_pinctrl_driver = {
+ 	.probe	= sun8i_a23_r_pinctrl_probe,
+@@ -132,10 +131,4 @@ static struct platform_driver sun8i_a23_
+ 		.of_match_table	= sun8i_a23_r_pinctrl_match,
+ 	},
+ };
+-module_platform_driver(sun8i_a23_r_pinctrl_driver);
+-
+-MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
+-MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com");
+-MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com");
+-MODULE_DESCRIPTION("Allwinner A23 R_PIO pinctrl driver");
+-MODULE_LICENSE("GPL");
++builtin_platform_driver(sun8i_a23_r_pinctrl_driver);
+--- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c
+@@ -14,7 +14,7 @@
+  * warranty of any kind, whether express or implied.
+  */
+ 
+-#include <linux/module.h>
++#include <linux/init.h>
+ #include <linux/platform_device.h>
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+@@ -575,7 +575,6 @@ static const struct of_device_id sun8i_a
+ 	{ .compatible = "allwinner,sun8i-a23-pinctrl", },
+ 	{}
+ };
+-MODULE_DEVICE_TABLE(of, sun8i_a23_pinctrl_match);
+ 
+ static struct platform_driver sun8i_a23_pinctrl_driver = {
+ 	.probe	= sun8i_a23_pinctrl_probe,
+@@ -584,9 +583,4 @@ static struct platform_driver sun8i_a23_
+ 		.of_match_table	= sun8i_a23_pinctrl_match,
+ 	},
+ };
+-module_platform_driver(sun8i_a23_pinctrl_driver);
+-
+-MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
+-MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com");
+-MODULE_DESCRIPTION("Allwinner A23 pinctrl driver");
+-MODULE_LICENSE("GPL");
++builtin_platform_driver(sun8i_a23_pinctrl_driver);
+--- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c
+@@ -12,7 +12,7 @@
+  * warranty of any kind, whether express or implied.
+  */
+ 
+-#include <linux/module.h>
++#include <linux/init.h>
+ #include <linux/platform_device.h>
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+@@ -498,7 +498,6 @@ static const struct of_device_id sun8i_a
+ 	{ .compatible = "allwinner,sun8i-a33-pinctrl", },
+ 	{}
+ };
+-MODULE_DEVICE_TABLE(of, sun8i_a33_pinctrl_match);
+ 
+ static struct platform_driver sun8i_a33_pinctrl_driver = {
+ 	.probe	= sun8i_a33_pinctrl_probe,
+@@ -507,8 +506,4 @@ static struct platform_driver sun8i_a33_
+ 		.of_match_table	= sun8i_a33_pinctrl_match,
+ 	},
+ };
+-module_platform_driver(sun8i_a33_pinctrl_driver);
+-
+-MODULE_AUTHOR("Vishnu Patekar <vishnupatekar0510@gmail.com>");
+-MODULE_DESCRIPTION("Allwinner a33 pinctrl driver");
+-MODULE_LICENSE("GPL");
++builtin_platform_driver(sun8i_a33_pinctrl_driver);
+--- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c
+@@ -12,7 +12,7 @@
+  * warranty of any kind, whether express or implied.
+  */
+ 
+-#include <linux/module.h>
++#include <linux/init.h>
+ #include <linux/platform_device.h>
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+@@ -587,7 +587,6 @@ static const struct of_device_id sun8i_a
+ 	{ .compatible = "allwinner,sun8i-a83t-pinctrl", },
+ 	{}
+ };
+-MODULE_DEVICE_TABLE(of, sun8i_a83t_pinctrl_match);
+ 
+ static struct platform_driver sun8i_a83t_pinctrl_driver = {
+ 	.probe	= sun8i_a83t_pinctrl_probe,
+@@ -596,8 +595,4 @@ static struct platform_driver sun8i_a83t
+ 		.of_match_table	= sun8i_a83t_pinctrl_match,
+ 	},
+ };
+-module_platform_driver(sun8i_a83t_pinctrl_driver);
+-
+-MODULE_AUTHOR("Vishnu Patekar <vishnupatekar0510@gmail.com>");
+-MODULE_DESCRIPTION("Allwinner a83t pinctrl driver");
+-MODULE_LICENSE("GPL");
++builtin_platform_driver(sun8i_a83t_pinctrl_driver);
+--- a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c
+@@ -10,7 +10,7 @@
+  * warranty of any kind, whether express or implied.
+  */
+ 
+-#include <linux/module.h>
++#include <linux/init.h>
+ #include <linux/platform_device.h>
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+@@ -733,7 +733,6 @@ static const struct of_device_id sun9i_a
+ 	{ .compatible = "allwinner,sun9i-a80-pinctrl", },
+ 	{}
+ };
+-MODULE_DEVICE_TABLE(of, sun9i_a80_pinctrl_match);
+ 
+ static struct platform_driver sun9i_a80_pinctrl_driver = {
+ 	.probe	= sun9i_a80_pinctrl_probe,
+@@ -742,8 +741,4 @@ static struct platform_driver sun9i_a80_
+ 		.of_match_table	= sun9i_a80_pinctrl_match,
+ 	},
+ };
+-module_platform_driver(sun9i_a80_pinctrl_driver);
+-
+-MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+-MODULE_DESCRIPTION("Allwinner A80 pinctrl driver");
+-MODULE_LICENSE("GPL");
++builtin_platform_driver(sun9i_a80_pinctrl_driver);
diff --git a/target/linux/sunxi/patches-4.9/0036-pinctrl-sunxi-Free-configs-in-pinctrl_map-only-if-it.patch b/target/linux/sunxi/patches-4.9/0036-pinctrl-sunxi-Free-configs-in-pinctrl_map-only-if-it.patch
new file mode 100644
index 0000000000..02c5f568c8
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0036-pinctrl-sunxi-Free-configs-in-pinctrl_map-only-if-it.patch
@@ -0,0 +1,51 @@
+From 88f01a1bd0e0dbd01b65907023dbe53cf524ea2a Mon Sep 17 00:00:00 2001
+From: Chen-Yu Tsai <wens@csie.org>
+Date: Fri, 11 Nov 2016 10:35:10 +0800
+Subject: pinctrl: sunxi: Free configs in pinctrl_map only if it is a config
+ map
+
+In the recently refactored sunxi pinctrl library, we are only allocating
+one set of pin configs for each pinmux setting node. When the pinctrl_map
+structure is freed, the pin configs should also be freed. However the
+code assumed the first map would contain the configs, which actually
+never happens, as the mux function map gets added first.
+
+The proper way to do this is to look through all the maps and free the
+first one whose type is actually PIN_MAP_TYPE_CONFIGS_GROUP.
+
+Also slightly expand the comment explaining this.
+
+Fixes: f233dbca6227 ("pinctrl: sunxi: Rework the pin config building code")
+Signed-off-by: Chen-Yu Tsai <wens@csie.org>
+Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 17 +++++++++++++++--
+ 1 file changed, 15 insertions(+), 2 deletions(-)
+
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -408,8 +408,21 @@ static void sunxi_pctrl_dt_free_map(stru
+ 				    struct pinctrl_map *map,
+ 				    unsigned num_maps)
+ {
+-	/* All the maps have the same pin config, free only the first one */
+-	kfree(map[0].data.configs.configs);
++	int i;
++
++	/* pin config is never in the first map */
++	for (i = 1; i < num_maps; i++) {
++		if (map[i].type != PIN_MAP_TYPE_CONFIGS_GROUP)
++			continue;
++
++		/*
++		 * All the maps share the same pin config,
++		 * free only the first one we find.
++		 */
++		kfree(map[i].data.configs.configs);
++		break;
++	}
++
+ 	kfree(map);
+ }
+ 
diff --git a/target/linux/sunxi/patches-4.9/0037-pinctrl-sunxi-Fix-PIN_CONFIG_BIAS_PULL_-DOWN-UP-argu.patch b/target/linux/sunxi/patches-4.9/0037-pinctrl-sunxi-Fix-PIN_CONFIG_BIAS_PULL_-DOWN-UP-argu.patch
new file mode 100644
index 0000000000..4921240f79
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0037-pinctrl-sunxi-Fix-PIN_CONFIG_BIAS_PULL_-DOWN-UP-argu.patch
@@ -0,0 +1,40 @@
+From 223dba00b4072efc590c7d648f230db1b44186b9 Mon Sep 17 00:00:00 2001
+From: Chen-Yu Tsai <wens@csie.org>
+Date: Fri, 11 Nov 2016 17:50:34 +0800
+Subject: pinctrl: sunxi: Fix PIN_CONFIG_BIAS_PULL_{DOWN,UP} argument
+
+According to pinconf-generic.h, the argument for
+PIN_CONFIG_BIAS_PULL_{DOWN,UP} is non-zero if the bias is enabled
+with a pull up/down resistor, zero if it is directly connected
+to VDD or ground.
+
+Since Allwinner hardware uses a weak pull resistor internally,
+the argument should be 1.
+
+Signed-off-by: Chen-Yu Tsai <wens@csie.org>
+Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -291,12 +291,16 @@ static unsigned long *sunxi_pctrl_build_
+ 
+ 	if (sunxi_pctrl_has_bias_prop(node)) {
+ 		int pull = sunxi_pctrl_parse_bias_prop(node);
++		int arg = 0;
+ 		if (pull < 0) {
+ 			ret = pull;
+ 			goto err_free;
+ 		}
+ 
+-		pinconfig[idx++] = pinconf_to_config_packed(pull, 0);
++		if (pull != PIN_CONFIG_BIAS_DISABLE)
++			arg = 1; /* hardware uses weak pull resistors */
++
++		pinconfig[idx++] = pinconf_to_config_packed(pull, arg);
+ 	}
+ 
+ 
diff --git a/target/linux/sunxi/patches-4.9/0038-pinctrl-sunxi-Add-support-for-fetching-pinconf-setti.patch b/target/linux/sunxi/patches-4.9/0038-pinctrl-sunxi-Add-support-for-fetching-pinconf-setti.patch
new file mode 100644
index 0000000000..d7972197ff
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0038-pinctrl-sunxi-Add-support-for-fetching-pinconf-setti.patch
@@ -0,0 +1,158 @@
+From c5fda170e87a4bdaeb278f7e50f7a1f654e94eb5 Mon Sep 17 00:00:00 2001
+From: Chen-Yu Tsai <wens@csie.org>
+Date: Fri, 11 Nov 2016 17:50:35 +0800
+Subject: pinctrl: sunxi: Add support for fetching pinconf settings from
+ hardware
+
+The sunxi pinctrl driver only caches whatever pinconf setting was last
+set on a given pingroup. This is not particularly helpful, nor is it
+correct.
+
+Fix this by actually reading the hardware registers and returning
+the correct results or error codes. Also filter out unsupported
+pinconf settings. Since this driver has a peculiar setup of 1 pin
+per group, we can support both pin and pingroup pinconf setting
+read back with the same code. The sunxi_pconf_reg helper and code
+structure is inspired by pinctrl-msm.
+
+With this done we can also claim to support generic pinconf, by
+setting .is_generic = true in pinconf_ops.
+
+Also remove the cached config value. The behavior of this was never
+correct, as it only cached 1 setting instead of all of them. Since
+we can now read back settings directly from the hardware, it is no
+longer required.
+
+Signed-off-by: Chen-Yu Tsai <wens@csie.org>
+Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 86 +++++++++++++++++++++++++++++++++--
+ drivers/pinctrl/sunxi/pinctrl-sunxi.h |  1 -
+ 2 files changed, 81 insertions(+), 6 deletions(-)
+
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -438,15 +438,91 @@ static const struct pinctrl_ops sunxi_pc
+ 	.get_group_pins		= sunxi_pctrl_get_group_pins,
+ };
+ 
++static int sunxi_pconf_reg(unsigned pin, enum pin_config_param param,
++			   u32 *offset, u32 *shift, u32 *mask)
++{
++	switch (param) {
++	case PIN_CONFIG_DRIVE_STRENGTH:
++		*offset = sunxi_dlevel_reg(pin);
++		*shift = sunxi_dlevel_offset(pin);
++		*mask = DLEVEL_PINS_MASK;
++		break;
++
++	case PIN_CONFIG_BIAS_PULL_UP:
++	case PIN_CONFIG_BIAS_PULL_DOWN:
++	case PIN_CONFIG_BIAS_DISABLE:
++		*offset = sunxi_pull_reg(pin);
++		*shift = sunxi_pull_offset(pin);
++		*mask = PULL_PINS_MASK;
++		break;
++
++	default:
++		return -ENOTSUPP;
++	}
++
++	return 0;
++}
++
++static int sunxi_pconf_get(struct pinctrl_dev *pctldev, unsigned pin,
++			   unsigned long *config)
++{
++	struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
++	enum pin_config_param param = pinconf_to_config_param(*config);
++	u32 offset, shift, mask, val;
++	u16 arg;
++	int ret;
++
++	pin -= pctl->desc->pin_base;
++
++	ret = sunxi_pconf_reg(pin, param, &offset, &shift, &mask);
++	if (ret < 0)
++		return ret;
++
++	val = (readl(pctl->membase + offset) >> shift) & mask;
++
++	switch (pinconf_to_config_param(*config)) {
++	case PIN_CONFIG_DRIVE_STRENGTH:
++		arg = (val + 1) * 10;
++		break;
++
++	case PIN_CONFIG_BIAS_PULL_UP:
++		if (val != SUN4I_PINCTRL_PULL_UP)
++			return -EINVAL;
++		arg = 1; /* hardware is weak pull-up */
++		break;
++
++	case PIN_CONFIG_BIAS_PULL_DOWN:
++		if (val != SUN4I_PINCTRL_PULL_DOWN)
++			return -EINVAL;
++		arg = 1; /* hardware is weak pull-down */
++		break;
++
++	case PIN_CONFIG_BIAS_DISABLE:
++		if (val != SUN4I_PINCTRL_NO_PULL)
++			return -EINVAL;
++		arg = 0;
++		break;
++
++	default:
++		/* sunxi_pconf_reg should catch anything unsupported */
++		WARN_ON(1);
++		return -ENOTSUPP;
++	}
++
++	*config = pinconf_to_config_packed(param, arg);
++
++	return 0;
++}
++
+ static int sunxi_pconf_group_get(struct pinctrl_dev *pctldev,
+ 				 unsigned group,
+ 				 unsigned long *config)
+ {
+ 	struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
++	struct sunxi_pinctrl_group *g = &pctl->groups[group];
+ 
+-	*config = pctl->groups[group].config;
+-
+-	return 0;
++	/* We only support 1 pin per group. Chain it to the pin callback */
++	return sunxi_pconf_get(pctldev, g->pin, config);
+ }
+ 
+ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev,
+@@ -508,8 +584,6 @@ static int sunxi_pconf_group_set(struct
+ 		default:
+ 			break;
+ 		}
+-		/* cache the config value */
+-		g->config = configs[i];
+ 	} /* for each config */
+ 
+ 	spin_unlock_irqrestore(&pctl->lock, flags);
+@@ -518,6 +592,8 @@ static int sunxi_pconf_group_set(struct
+ }
+ 
+ static const struct pinconf_ops sunxi_pconf_ops = {
++	.is_generic		= true,
++	.pin_config_get		= sunxi_pconf_get,
+ 	.pin_config_group_get	= sunxi_pconf_group_get,
+ 	.pin_config_group_set	= sunxi_pconf_group_set,
+ };
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
+@@ -109,7 +109,6 @@ struct sunxi_pinctrl_function {
+ 
+ struct sunxi_pinctrl_group {
+ 	const char	*name;
+-	unsigned long	config;
+ 	unsigned	pin;
+ };
+ 
diff --git a/target/linux/sunxi/patches-4.9/0039-pinctrl-sunxi-Make-sunxi_pconf_group_set-use-sunxi_p.patch b/target/linux/sunxi/patches-4.9/0039-pinctrl-sunxi-Make-sunxi_pconf_group_set-use-sunxi_p.patch
new file mode 100644
index 0000000000..7555933f63
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0039-pinctrl-sunxi-Make-sunxi_pconf_group_set-use-sunxi_p.patch
@@ -0,0 +1,122 @@
+From 51814827190214986c452a166718bf12d32211c7 Mon Sep 17 00:00:00 2001
+From: Chen-Yu Tsai <wens@csie.org>
+Date: Fri, 11 Nov 2016 17:50:36 +0800
+Subject: pinctrl: sunxi: Make sunxi_pconf_group_set use sunxi_pconf_reg helper
+
+The sunxi_pconf_reg helper introduced in the last patch gives us the
+chance to rework sunxi_pconf_group_set to have it match the structure
+of sunxi_pconf_(group_)get and make it easier to understand.
+
+For each config to set, it:
+
+    1. checks if the parameter is supported.
+    2. checks if the argument is within limits.
+    3. converts argument to the register value.
+    4. writes to the register with spinlock held.
+
+As a result the function now blocks unsupported config parameters,
+instead of silently ignoring them.
+
+Signed-off-by: Chen-Yu Tsai <wens@csie.org>
+Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 64 +++++++++++++++++------------------
+ 1 file changed, 32 insertions(+), 32 deletions(-)
+
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -532,23 +532,27 @@ static int sunxi_pconf_group_set(struct
+ {
+ 	struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+ 	struct sunxi_pinctrl_group *g = &pctl->groups[group];
+-	unsigned long flags;
+ 	unsigned pin = g->pin - pctl->desc->pin_base;
+-	u32 val, mask;
+-	u16 strength;
+-	u8 dlevel;
+ 	int i;
+ 
+-	spin_lock_irqsave(&pctl->lock, flags);
+-
+ 	for (i = 0; i < num_configs; i++) {
+-		switch (pinconf_to_config_param(configs[i])) {
++		enum pin_config_param param;
++		unsigned long flags;
++		u32 offset, shift, mask, reg;
++		u16 arg, val;
++		int ret;
++
++		param = pinconf_to_config_param(configs[i]);
++		arg = pinconf_to_config_argument(configs[i]);
++
++		ret = sunxi_pconf_reg(pin, param, &offset, &shift, &mask);
++		if (ret < 0)
++			return ret;
++
++		switch (param) {
+ 		case PIN_CONFIG_DRIVE_STRENGTH:
+-			strength = pinconf_to_config_argument(configs[i]);
+-			if (strength > 40) {
+-				spin_unlock_irqrestore(&pctl->lock, flags);
++			if (arg < 10 || arg > 40)
+ 				return -EINVAL;
+-			}
+ 			/*
+ 			 * We convert from mA to what the register expects:
+ 			 *   0: 10mA
+@@ -556,37 +560,33 @@ static int sunxi_pconf_group_set(struct
+ 			 *   2: 30mA
+ 			 *   3: 40mA
+ 			 */
+-			dlevel = strength / 10 - 1;
+-			val = readl(pctl->membase + sunxi_dlevel_reg(pin));
+-			mask = DLEVEL_PINS_MASK << sunxi_dlevel_offset(pin);
+-			writel((val & ~mask)
+-				| dlevel << sunxi_dlevel_offset(pin),
+-				pctl->membase + sunxi_dlevel_reg(pin));
++			val = arg / 10 - 1;
+ 			break;
+ 		case PIN_CONFIG_BIAS_DISABLE:
+-			val = readl(pctl->membase + sunxi_pull_reg(pin));
+-			mask = PULL_PINS_MASK << sunxi_pull_offset(pin);
+-			writel((val & ~mask),
+-			       pctl->membase + sunxi_pull_reg(pin));
++			val = 0;
+ 			break;
+ 		case PIN_CONFIG_BIAS_PULL_UP:
+-			val = readl(pctl->membase + sunxi_pull_reg(pin));
+-			mask = PULL_PINS_MASK << sunxi_pull_offset(pin);
+-			writel((val & ~mask) | 1 << sunxi_pull_offset(pin),
+-				pctl->membase + sunxi_pull_reg(pin));
++			if (arg == 0)
++				return -EINVAL;
++			val = 1;
+ 			break;
+ 		case PIN_CONFIG_BIAS_PULL_DOWN:
+-			val = readl(pctl->membase + sunxi_pull_reg(pin));
+-			mask = PULL_PINS_MASK << sunxi_pull_offset(pin);
+-			writel((val & ~mask) | 2 << sunxi_pull_offset(pin),
+-				pctl->membase + sunxi_pull_reg(pin));
++			if (arg == 0)
++				return -EINVAL;
++			val = 2;
+ 			break;
+ 		default:
+-			break;
++			/* sunxi_pconf_reg should catch anything unsupported */
++			WARN_ON(1);
++			return -ENOTSUPP;
+ 		}
+-	} /* for each config */
+ 
+-	spin_unlock_irqrestore(&pctl->lock, flags);
++		spin_lock_irqsave(&pctl->lock, flags);
++		reg = readl(pctl->membase + offset);
++		reg &= ~(mask << shift);
++		writel(reg | val << shift, pctl->membase + offset);
++		spin_unlock_irqrestore(&pctl->lock, flags);
++	} /* for each config */
+ 
+ 	return 0;
+ }
diff --git a/target/linux/sunxi/patches-4.9/0040-pinctrl-sunxi-Add-support-for-interrupt-debouncing.patch b/target/linux/sunxi/patches-4.9/0040-pinctrl-sunxi-Add-support-for-interrupt-debouncing.patch
new file mode 100644
index 0000000000..01cbe31bef
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0040-pinctrl-sunxi-Add-support-for-interrupt-debouncing.patch
@@ -0,0 +1,171 @@
+From 7c926492d38a3feef4b4b29c91b7c03eb1b8b546 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@free-electrons.com>
+Date: Mon, 14 Nov 2016 21:53:03 +0100
+Subject: pinctrl: sunxi: Add support for interrupt debouncing
+
+The pin controller found in the Allwinner SoCs has support for interrupts
+debouncing.
+
+However, this is not done per-pin, preventing us from using the generic
+pinconf binding for that, but per irq bank, which, depending on the SoC,
+ranges from one to five.
+
+Introduce a device-wide property to deal with this using a microsecond
+resolution. We can re-use the per-pin input-debounce property for that, so
+let's do it!
+
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Acked-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ .../bindings/pinctrl/allwinner,sunxi-pinctrl.txt   | 14 ++++
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c              | 84 ++++++++++++++++++++++
+ drivers/pinctrl/sunxi/pinctrl-sunxi.h              |  7 ++
+ 3 files changed, 105 insertions(+)
+
+--- a/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt
++++ b/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt
+@@ -28,6 +28,20 @@ Required properties:
+ - reg: Should contain the register physical address and length for the
+   pin controller.
+ 
++- clocks: phandle to the clocks feeding the pin controller:
++  - "apb": the gated APB parent clock
++  - "hosc": the high frequency oscillator in the system
++  - "losc": the low frequency oscillator in the system
++
++Note: For backward compatibility reasons, the hosc and losc clocks are only
++required if you need to use the optional input-debounce property. Any new
++device tree should set them.
++
++Optional properties:
++  - input-debounce: Array of debouncing periods in microseconds. One period per
++    irq bank found in the controller. 0 if no setup required.
++
++
+ Please refer to pinctrl-bindings.txt in this directory for details of the
+ common pinctrl bindings used by client devices.
+ 
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -1122,6 +1122,88 @@ static int sunxi_pinctrl_build_state(str
+ 	return 0;
+ }
+ 
++static int sunxi_pinctrl_get_debounce_div(struct clk *clk, int freq, int *diff)
++{
++	unsigned long clock = clk_get_rate(clk);
++	unsigned int best_diff = ~0, best_div;
++	int i;
++
++	for (i = 0; i < 8; i++) {
++		int cur_diff = abs(freq - (clock >> i));
++
++		if (cur_diff < best_diff) {
++			best_diff = cur_diff;
++			best_div = i;
++		}
++	}
++
++	*diff = best_diff;
++	return best_div;
++}
++
++static int sunxi_pinctrl_setup_debounce(struct sunxi_pinctrl *pctl,
++					struct device_node *node)
++{
++	unsigned int hosc_diff, losc_diff;
++	unsigned int hosc_div, losc_div;
++	struct clk *hosc, *losc;
++	u8 div, src;
++	int i, ret;
++
++	/* Deal with old DTs that didn't have the oscillators */
++	if (of_count_phandle_with_args(node, "clocks", "#clock-cells") != 3)
++		return 0;
++
++	/* If we don't have any setup, bail out */
++	if (!of_find_property(node, "input-debounce", NULL))
++		return 0;
++
++	losc = devm_clk_get(pctl->dev, "losc");
++	if (IS_ERR(losc))
++		return PTR_ERR(losc);
++
++	hosc = devm_clk_get(pctl->dev, "hosc");
++	if (IS_ERR(hosc))
++		return PTR_ERR(hosc);
++
++	for (i = 0; i < pctl->desc->irq_banks; i++) {
++		unsigned long debounce_freq;
++		u32 debounce;
++
++		ret = of_property_read_u32_index(node, "input-debounce",
++						 i, &debounce);
++		if (ret)
++			return ret;
++
++		if (!debounce)
++			continue;
++
++		debounce_freq = DIV_ROUND_CLOSEST(USEC_PER_SEC, debounce);
++		losc_div = sunxi_pinctrl_get_debounce_div(losc,
++							  debounce_freq,
++							  &losc_diff);
++
++		hosc_div = sunxi_pinctrl_get_debounce_div(hosc,
++							  debounce_freq,
++							  &hosc_diff);
++
++		if (hosc_diff < losc_diff) {
++			div = hosc_div;
++			src = 1;
++		} else {
++			div = losc_div;
++			src = 0;
++		}
++
++		writel(src | div << 4,
++		       pctl->membase +
++		       sunxi_irq_debounce_reg_from_bank(i,
++							pctl->desc->irq_bank_base));
++	}
++
++	return 0;
++}
++
+ int sunxi_pinctrl_init(struct platform_device *pdev,
+ 		       const struct sunxi_pinctrl_desc *desc)
+ {
+@@ -1284,6 +1366,8 @@ int sunxi_pinctrl_init(struct platform_d
+ 						 pctl);
+ 	}
+ 
++	sunxi_pinctrl_setup_debounce(pctl, node);
++
+ 	dev_info(&pdev->dev, "initialized sunXi PIO driver\n");
+ 
+ 	return 0;
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
+@@ -69,6 +69,8 @@
+ #define IRQ_STATUS_IRQ_BITS		1
+ #define IRQ_STATUS_IRQ_MASK		((1 << IRQ_STATUS_IRQ_BITS) - 1)
+ 
++#define IRQ_DEBOUNCE_REG	0x218
++
+ #define IRQ_MEM_SIZE		0x20
+ 
+ #define IRQ_EDGE_RISING		0x00
+@@ -265,6 +267,11 @@ static inline u32 sunxi_irq_ctrl_offset(
+ 	return irq_num * IRQ_CTRL_IRQ_BITS;
+ }
+ 
++static inline u32 sunxi_irq_debounce_reg_from_bank(u8 bank, unsigned bank_base)
++{
++	return IRQ_DEBOUNCE_REG + (bank_base + bank) * IRQ_MEM_SIZE;
++}
++
+ static inline u32 sunxi_irq_status_reg_from_bank(u8 bank, unsigned bank_base)
+ {
+ 	return IRQ_STATUS_REG + (bank_base + bank) * IRQ_MEM_SIZE;
diff --git a/target/linux/sunxi/patches-4.9/0041-pinctrl-sunxi-fix-theoretical-uninitialized-variable.patch b/target/linux/sunxi/patches-4.9/0041-pinctrl-sunxi-fix-theoretical-uninitialized-variable.patch
new file mode 100644
index 0000000000..69de015b67
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0041-pinctrl-sunxi-fix-theoretical-uninitialized-variable.patch
@@ -0,0 +1,40 @@
+From d8a22212737314cc02692cc90eda7d844fa20257 Mon Sep 17 00:00:00 2001
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Wed, 16 Nov 2016 15:18:18 +0100
+Subject: pinctrl: sunxi: fix theoretical uninitialized variable access
+
+gcc warns about a  way that it could use an uninitialized variable:
+
+drivers/pinctrl/sunxi/pinctrl-sunxi.c: In function 'sunxi_pinctrl_init':
+drivers/pinctrl/sunxi/pinctrl-sunxi.c:1191:8: error: 'best_div' may be used uninitialized in this function [-Werror=maybe-uninitialized]
+
+This cannot really happen except if 'freq' is UINT_MAX and 'clock' is
+zero, and both of these are forbidden. To shut up the warning anyway,
+this changes the logic to initialize the return code to the first
+divider value before looking at the others.
+
+Fixes: 7c926492d38a ("pinctrl: sunxi: Add support for interrupt debouncing")
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -1125,10 +1125,13 @@ static int sunxi_pinctrl_build_state(str
+ static int sunxi_pinctrl_get_debounce_div(struct clk *clk, int freq, int *diff)
+ {
+ 	unsigned long clock = clk_get_rate(clk);
+-	unsigned int best_diff = ~0, best_div;
++	unsigned int best_diff, best_div;
+ 	int i;
+ 
+-	for (i = 0; i < 8; i++) {
++	best_diff = abs(freq - clock);
++	best_div = 0;
++
++	for (i = 1; i < 8; i++) {
+ 		int cur_diff = abs(freq - (clock >> i));
+ 
+ 		if (cur_diff < best_diff) {
diff --git a/target/linux/sunxi/patches-4.9/0042-pinctrl-sunxi-Testing-the-wrong-variable.patch b/target/linux/sunxi/patches-4.9/0042-pinctrl-sunxi-Testing-the-wrong-variable.patch
new file mode 100644
index 0000000000..8ed4f27b4c
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0042-pinctrl-sunxi-Testing-the-wrong-variable.patch
@@ -0,0 +1,35 @@
+From b3cde198b17f504643cc1eeffc4623f03326f436 Mon Sep 17 00:00:00 2001
+From: Dan Carpenter <dan.carpenter@oracle.com>
+Date: Fri, 18 Nov 2016 14:35:57 +0300
+Subject: pinctrl: sunxi: Testing the wrong variable
+
+Smatch complains that we dereference "map" before testing it for NULL
+which is true.  We should be testing "*map" instead.  Also on the error
+path, we should free *map and set it to NULL.
+
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -398,13 +398,14 @@ static int sunxi_pctrl_dt_node_to_map(st
+ 	 * map array
+ 	 */
+ 	*map = krealloc(*map, i * sizeof(struct pinctrl_map), GFP_KERNEL);
+-	if (!map)
++	if (!*map)
+ 		return -ENOMEM;
+ 
+ 	return 0;
+ 
+ err_free_map:
+-	kfree(map);
++	kfree(*map);
++	*map = NULL;
+ 	return ret;
+ }
+ 
diff --git a/target/linux/sunxi/patches-4.9/0043-pinctrl-sunxi-Don-t-enforce-bias-disable-for-now.patch b/target/linux/sunxi/patches-4.9/0043-pinctrl-sunxi-Don-t-enforce-bias-disable-for-now.patch
new file mode 100644
index 0000000000..d6e639af52
--- /dev/null
+++ b/target/linux/sunxi/patches-4.9/0043-pinctrl-sunxi-Don-t-enforce-bias-disable-for-now.patch
@@ -0,0 +1,42 @@
+From 2154d94b40ea2a5de05245521371d0461bb0d669 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@free-electrons.com>
+Date: Mon, 23 Jan 2017 09:21:30 +0100
+Subject: pinctrl: sunxi: Don't enforce bias disable (for now)
+
+Commit 07fe64ba213f ("pinctrl: sunxi: Handle bias disable") actually
+enforced enforced the disabling of the pull up/down resistors instead of
+ignoring it like it was done before.
+
+This was part of a wider rework to switch to the generic pinconf bindings,
+and was meant to be merged together with DT patches that were switching to
+it, and removing what was considered default values by both the binding and
+the boards. This included no bias on a pin.
+
+However, those DT patches were delayed to 4.11, which would be fine only
+for a significant number boards having the bias setup wrong, which in turns
+break the MMC on those boards (and possibly other devices too).
+
+In order to avoid conflicts as much as possible, bring back the old
+behaviour for 4.10, and we'll revert that commit once all the DT bits will
+have landed.
+
+Tested-by: Priit Laes <plaes@plaes.org>
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+Acked-by: Chen-Yu Tsai <wens@csie.org>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -564,8 +564,7 @@ static int sunxi_pconf_group_set(struct
+ 			val = arg / 10 - 1;
+ 			break;
+ 		case PIN_CONFIG_BIAS_DISABLE:
+-			val = 0;
+-			break;
++			continue;
+ 		case PIN_CONFIG_BIAS_PULL_UP:
+ 			if (arg == 0)
+ 				return -EINVAL;
