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
--- /dev/null
+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.
+
--- /dev/null
+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;
+ };
--- /dev/null
+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);
--- /dev/null
+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);
--- /dev/null
+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);
--- /dev/null
+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;
--- /dev/null
+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_ */
--- /dev/null
+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>;
++ };
++ };
++};
--- /dev/null
+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;
++};
--- /dev/null
+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>;
--- /dev/null
+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);
+ }
+
--- /dev/null
+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;
+ }
+
--- /dev/null
+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);
--- /dev/null
+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;
--- /dev/null
+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;
+
--- /dev/null
+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>
+@@ -1036,7 +1036,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,
+@@ -1045,8 +1044,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);
--- /dev/null
+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);
+ }
+
--- /dev/null
+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);
+ }
+
+
--- /dev/null
+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;
+ };
+
--- /dev/null
+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;
+ }
--- /dev/null
+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;
--- /dev/null
+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) {
--- /dev/null
+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;
+ }
+
--- /dev/null
+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;