diff --git a/target/linux/sunxi/config-4.9 b/target/linux/sunxi/config-4.9 index f338c6ec76..45677f9a83 100644 --- a/target/linux/sunxi/config-4.9 +++ b/target/linux/sunxi/config-4.9 @@ -486,6 +486,7 @@ CONFIG_STMMAC_PLATFORM=y CONFIG_STRICT_DEVMEM=y # CONFIG_SUN4I_EMAC is not set CONFIG_SUN4I_TIMER=y +# CONFIG_SUN50I_A64_CCU is not set CONFIG_SUN5I_HSTIMER=y CONFIG_SUN6I_A31_CCU=y CONFIG_SUN8I_A23_CCU=y diff --git a/target/linux/sunxi/patches-4.9/0001-arm64-sunxi-always-enable-reset-controller.patch b/target/linux/sunxi/patches-4.9/0001-arm64-sunxi-always-enable-reset-controller.patch new file mode 100644 index 0000000000..e23475218b --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0001-arm64-sunxi-always-enable-reset-controller.patch @@ -0,0 +1,39 @@ +From 900a9020af7a023f9b64c919fddf8a7486108962 Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +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 +Acked-by: Maxime Ripard +--- + arch/arm64/Kconfig.platforms | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/arch/arm64/Kconfig.platforms ++++ b/arch/arm64/Kconfig.platforms +@@ -2,9 +2,11 @@ menu "Platform selection" + + config ARCH_SUNXI + bool "Allwinner sunxi 64-bit SoC Family" ++ select ARCH_HAS_RESET_CONTROLLER + select GENERIC_IRQ_CHIP + select PINCTRL + select PINCTRL_SUN50I_A64 ++ select RESET_CONTROLLER + help + This enables support for Allwinner sunxi based SoCs like the A64. + diff --git a/target/linux/sunxi/patches-4.9/0002-clk-sunxi-ng-Rename-the-internal-structures.patch b/target/linux/sunxi/patches-4.9/0002-clk-sunxi-ng-Rename-the-internal-structures.patch new file mode 100644 index 0000000000..f3e485d88a --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0002-clk-sunxi-ng-Rename-the-internal-structures.patch @@ -0,0 +1,239 @@ +From a501a14e38cc4d8e9c91bb508cdca7032d53f717 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +Acked-by: Chen-Yu Tsai +--- + drivers/clk/sunxi-ng/ccu_div.h | 6 +++--- + drivers/clk/sunxi-ng/ccu_frac.c | 12 ++++++------ + drivers/clk/sunxi-ng/ccu_frac.h | 14 +++++++------- + drivers/clk/sunxi-ng/ccu_mp.h | 4 ++-- + drivers/clk/sunxi-ng/ccu_mult.h | 4 ++-- + drivers/clk/sunxi-ng/ccu_nk.h | 4 ++-- + drivers/clk/sunxi-ng/ccu_nkm.h | 6 +++--- + drivers/clk/sunxi-ng/ccu_nkmp.h | 8 ++++---- + drivers/clk/sunxi-ng/ccu_nm.h | 6 +++--- + 9 files changed, 32 insertions(+), 32 deletions(-) + +--- a/drivers/clk/sunxi-ng/ccu_div.h ++++ b/drivers/clk/sunxi-ng/ccu_div.h +@@ -20,7 +20,7 @@ + #include "ccu_mux.h" + + /** +- * struct _ccu_div - Internal divider description ++ * struct ccu_div_internal - Internal divider description + * @shift: Bit offset of the divider in its register + * @width: Width of the divider field in its register + * @max: Maximum value allowed for that divider. This is the +@@ -36,7 +36,7 @@ + * It is basically a wrapper around the clk_divider functions + * arguments. + */ +-struct _ccu_div { ++struct ccu_div_internal { + u8 shift; + u8 width; + +@@ -78,7 +78,7 @@ struct _ccu_div { + struct ccu_div { + u32 enable; + +- struct _ccu_div div; ++ struct ccu_div_internal div; + struct ccu_mux_internal mux; + struct ccu_common common; + }; +--- a/drivers/clk/sunxi-ng/ccu_frac.c ++++ b/drivers/clk/sunxi-ng/ccu_frac.c +@@ -14,7 +14,7 @@ + #include "ccu_frac.h" + + bool ccu_frac_helper_is_enabled(struct ccu_common *common, +- struct _ccu_frac *cf) ++ struct ccu_frac_internal *cf) + { + if (!(common->features & CCU_FEATURE_FRACTIONAL)) + return false; +@@ -23,7 +23,7 @@ bool ccu_frac_helper_is_enabled(struct c + } + + void ccu_frac_helper_enable(struct ccu_common *common, +- struct _ccu_frac *cf) ++ struct ccu_frac_internal *cf) + { + unsigned long flags; + u32 reg; +@@ -38,7 +38,7 @@ void ccu_frac_helper_enable(struct ccu_c + } + + void ccu_frac_helper_disable(struct ccu_common *common, +- struct _ccu_frac *cf) ++ struct ccu_frac_internal *cf) + { + unsigned long flags; + u32 reg; +@@ -53,7 +53,7 @@ void ccu_frac_helper_disable(struct ccu_ + } + + bool ccu_frac_helper_has_rate(struct ccu_common *common, +- struct _ccu_frac *cf, ++ struct ccu_frac_internal *cf, + unsigned long rate) + { + if (!(common->features & CCU_FEATURE_FRACTIONAL)) +@@ -63,7 +63,7 @@ bool ccu_frac_helper_has_rate(struct ccu + } + + unsigned long ccu_frac_helper_read_rate(struct ccu_common *common, +- struct _ccu_frac *cf) ++ struct ccu_frac_internal *cf) + { + u32 reg; + +@@ -84,7 +84,7 @@ unsigned long ccu_frac_helper_read_rate( + } + + int ccu_frac_helper_set_rate(struct ccu_common *common, +- struct _ccu_frac *cf, ++ struct ccu_frac_internal *cf, + unsigned long rate) + { + unsigned long flags; +--- a/drivers/clk/sunxi-ng/ccu_frac.h ++++ b/drivers/clk/sunxi-ng/ccu_frac.h +@@ -18,7 +18,7 @@ + + #include "ccu_common.h" + +-struct _ccu_frac { ++struct ccu_frac_internal { + u32 enable; + u32 select; + +@@ -33,21 +33,21 @@ struct _ccu_frac { + } + + bool ccu_frac_helper_is_enabled(struct ccu_common *common, +- struct _ccu_frac *cf); ++ struct ccu_frac_internal *cf); + void ccu_frac_helper_enable(struct ccu_common *common, +- struct _ccu_frac *cf); ++ struct ccu_frac_internal *cf); + void ccu_frac_helper_disable(struct ccu_common *common, +- struct _ccu_frac *cf); ++ struct ccu_frac_internal *cf); + + bool ccu_frac_helper_has_rate(struct ccu_common *common, +- struct _ccu_frac *cf, ++ struct ccu_frac_internal *cf, + unsigned long rate); + + unsigned long ccu_frac_helper_read_rate(struct ccu_common *common, +- struct _ccu_frac *cf); ++ struct ccu_frac_internal *cf); + + int ccu_frac_helper_set_rate(struct ccu_common *common, +- struct _ccu_frac *cf, ++ struct ccu_frac_internal *cf, + unsigned long rate); + + #endif /* _CCU_FRAC_H_ */ +--- a/drivers/clk/sunxi-ng/ccu_mp.h ++++ b/drivers/clk/sunxi-ng/ccu_mp.h +@@ -29,8 +29,8 @@ + struct ccu_mp { + u32 enable; + +- struct _ccu_div m; +- struct _ccu_div p; ++ struct ccu_div_internal m; ++ struct ccu_div_internal p; + struct ccu_mux_internal mux; + struct ccu_common common; + }; +--- a/drivers/clk/sunxi-ng/ccu_mult.h ++++ b/drivers/clk/sunxi-ng/ccu_mult.h +@@ -4,7 +4,7 @@ + #include "ccu_common.h" + #include "ccu_mux.h" + +-struct _ccu_mult { ++struct ccu_mult_internal { + u8 shift; + u8 width; + }; +@@ -18,7 +18,7 @@ struct _ccu_mult { + struct ccu_mult { + u32 enable; + +- struct _ccu_mult mult; ++ struct ccu_mult_internal mult; + struct ccu_mux_internal mux; + struct ccu_common common; + }; +--- a/drivers/clk/sunxi-ng/ccu_nk.h ++++ b/drivers/clk/sunxi-ng/ccu_nk.h +@@ -30,8 +30,8 @@ struct ccu_nk { + u32 enable; + u32 lock; + +- struct _ccu_mult n; +- struct _ccu_mult k; ++ struct ccu_mult_internal n; ++ struct ccu_mult_internal k; + + unsigned int fixed_post_div; + +--- a/drivers/clk/sunxi-ng/ccu_nkm.h ++++ b/drivers/clk/sunxi-ng/ccu_nkm.h +@@ -29,9 +29,9 @@ struct ccu_nkm { + u32 enable; + u32 lock; + +- struct _ccu_mult n; +- struct _ccu_mult k; +- struct _ccu_div m; ++ struct ccu_mult_internal n; ++ struct ccu_mult_internal k; ++ struct ccu_div_internal m; + struct ccu_mux_internal mux; + + struct ccu_common common; +--- a/drivers/clk/sunxi-ng/ccu_nkmp.h ++++ b/drivers/clk/sunxi-ng/ccu_nkmp.h +@@ -29,10 +29,10 @@ struct ccu_nkmp { + u32 enable; + u32 lock; + +- struct _ccu_mult n; +- struct _ccu_mult k; +- struct _ccu_div m; +- struct _ccu_div p; ++ struct ccu_mult_internal n; ++ struct ccu_mult_internal k; ++ struct ccu_div_internal m; ++ struct ccu_div_internal p; + + struct ccu_common common; + }; +--- a/drivers/clk/sunxi-ng/ccu_nm.h ++++ b/drivers/clk/sunxi-ng/ccu_nm.h +@@ -30,9 +30,9 @@ struct ccu_nm { + u32 enable; + u32 lock; + +- struct _ccu_mult n; +- struct _ccu_div m; +- struct _ccu_frac frac; ++ struct ccu_mult_internal n; ++ struct ccu_div_internal m; ++ struct ccu_frac_internal frac; + + struct ccu_common common; + }; diff --git a/target/linux/sunxi/patches-4.9/0003-clk-sunxi-ng-Remove-the-use-of-rational-computations.patch b/target/linux/sunxi/patches-4.9/0003-clk-sunxi-ng-Remove-the-use-of-rational-computations.patch new file mode 100644 index 0000000000..6b8f46eae0 --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0003-clk-sunxi-ng-Remove-the-use-of-rational-computations.patch @@ -0,0 +1,239 @@ +From ee28648cb2b4d4ab5c2eb8199ea86675fe19016b Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +Acked-by: Chen-Yu Tsai +--- + 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 +-#include + + #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 +-#include + + #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 +-#include + + #include "ccu_frac.h" + #include "ccu_gate.h" + #include "ccu_nm.h" + ++struct _ccu_nm { ++ unsigned long n, max_n; ++ unsigned long m, max_m; ++}; ++ ++static void ccu_nm_find_best(unsigned long parent, unsigned long rate, ++ struct _ccu_nm *nm) ++{ ++ unsigned long best_rate = 0; ++ unsigned long best_n = 0, best_m = 0; ++ unsigned long _n, _m; ++ ++ for (_n = 1; _n <= nm->max_n; _n++) { ++ for (_m = 1; _n <= nm->max_m; _m++) { ++ unsigned long tmp_rate = parent * _n / _m; ++ ++ if (tmp_rate > rate) ++ continue; ++ ++ if ((rate - tmp_rate) < (rate - best_rate)) { ++ best_rate = tmp_rate; ++ best_n = _n; ++ best_m = _m; ++ } ++ } ++ } ++ ++ nm->n = best_n; ++ nm->m = best_m; ++} ++ + static void ccu_nm_disable(struct clk_hw *hw) + { + struct ccu_nm *nm = hw_to_ccu_nm(hw); +@@ -61,24 +91,22 @@ static long ccu_nm_round_rate(struct clk + unsigned long *parent_rate) + { + struct ccu_nm *nm = hw_to_ccu_nm(hw); +- unsigned long max_n, max_m; +- unsigned long n, m; ++ struct _ccu_nm _nm; + +- max_n = 1 << nm->n.width; +- max_m = nm->m.max ?: 1 << nm->m.width; ++ _nm.max_n = 1 << nm->n.width; ++ _nm.max_m = nm->m.max ?: 1 << nm->m.width; + +- rational_best_approximation(rate, *parent_rate, max_n, max_m, &n, &m); ++ ccu_nm_find_best(*parent_rate, rate, &_nm); + +- return *parent_rate * n / m; ++ return *parent_rate * _nm.n / _nm.m; + } + + static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) + { + struct ccu_nm *nm = hw_to_ccu_nm(hw); ++ struct _ccu_nm _nm; + unsigned long flags; +- unsigned long max_n, max_m; +- unsigned long n, m; + u32 reg; + + if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) +@@ -86,10 +114,10 @@ static int ccu_nm_set_rate(struct clk_hw + else + ccu_frac_helper_disable(&nm->common, &nm->frac); + +- max_n = 1 << nm->n.width; +- max_m = nm->m.max ?: 1 << nm->m.width; ++ _nm.max_n = 1 << nm->n.width; ++ _nm.max_m = nm->m.max ?: 1 << nm->m.width; + +- rational_best_approximation(rate, parent_rate, max_n, max_m, &n, &m); ++ ccu_nm_find_best(parent_rate, rate, &_nm); + + spin_lock_irqsave(nm->common.lock, flags); + +@@ -97,7 +125,7 @@ static int ccu_nm_set_rate(struct clk_hw + reg &= ~GENMASK(nm->n.width + nm->n.shift - 1, nm->n.shift); + reg &= ~GENMASK(nm->m.width + nm->m.shift - 1, nm->m.shift); + +- writel(reg | ((m - 1) << nm->m.shift) | ((n - 1) << nm->n.shift), ++ writel(reg | ((_nm.m - 1) << nm->m.shift) | ((_nm.n - 1) << nm->n.shift), + nm->common.base + nm->common.reg); + + spin_unlock_irqrestore(nm->common.lock, flags); diff --git a/target/linux/sunxi/patches-4.9/0004-clk-sunxi-ng-Finish-to-convert-to-structures-for-arg.patch b/target/linux/sunxi/patches-4.9/0004-clk-sunxi-ng-Finish-to-convert-to-structures-for-arg.patch new file mode 100644 index 0000000000..4b91892b81 --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0004-clk-sunxi-ng-Finish-to-convert-to-structures-for-arg.patch @@ -0,0 +1,182 @@ +From b8302c7267dedaeeb1bf38143f099defbf16dce8 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +Acked-by: Chen-Yu Tsai +--- + 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 +-#include + + #include "ccu_gate.h" + #include "ccu_nk.h" + ++struct _ccu_nk { ++ unsigned long n, max_n; ++ unsigned long k, max_k; ++}; ++ + static void ccu_nk_find_best(unsigned long parent, unsigned long rate, +- unsigned int max_n, unsigned int max_k, +- unsigned int *n, unsigned int *k) ++ struct _ccu_nk *nk) + { + unsigned long best_rate = 0; + unsigned int best_k = 0, best_n = 0; + unsigned int _k, _n; + +- for (_k = 1; _k <= max_k; _k++) { +- for (_n = 1; _n <= max_n; _n++) { ++ for (_k = 1; _k <= nk->max_k; _k++) { ++ for (_n = 1; _n <= nk->max_n; _n++) { + unsigned long tmp_rate = parent * _n * _k; + + if (tmp_rate > rate) +@@ -37,8 +40,8 @@ static void ccu_nk_find_best(unsigned lo + } + } + +- *k = best_k; +- *n = best_n; ++ nk->k = best_k; ++ nk->n = best_n; + } + + static void ccu_nk_disable(struct clk_hw *hw) +@@ -89,16 +92,17 @@ static long ccu_nk_round_rate(struct clk + unsigned long *parent_rate) + { + struct ccu_nk *nk = hw_to_ccu_nk(hw); +- unsigned int n, k; ++ struct _ccu_nk _nk; + + if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate *= nk->fixed_post_div; + +- ccu_nk_find_best(*parent_rate, rate, +- 1 << nk->n.width, 1 << nk->k.width, +- &n, &k); ++ _nk.max_n = 1 << nk->n.width; ++ _nk.max_k = 1 << nk->k.width; ++ ++ ccu_nk_find_best(*parent_rate, rate, &_nk); ++ rate = *parent_rate * _nk.n * _nk.k; + +- rate = *parent_rate * n * k; + if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate = rate / nk->fixed_post_div; + +@@ -110,15 +114,16 @@ static int ccu_nk_set_rate(struct clk_hw + { + struct ccu_nk *nk = hw_to_ccu_nk(hw); + unsigned long flags; +- unsigned int n, k; ++ struct _ccu_nk _nk; + u32 reg; + + if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate = rate * nk->fixed_post_div; + +- ccu_nk_find_best(parent_rate, rate, +- 1 << nk->n.width, 1 << nk->k.width, +- &n, &k); ++ _nk.max_n = 1 << nk->n.width; ++ _nk.max_k = 1 << nk->k.width; ++ ++ ccu_nk_find_best(parent_rate, rate, &_nk); + + spin_lock_irqsave(nk->common.lock, flags); + +@@ -126,7 +131,7 @@ static int ccu_nk_set_rate(struct clk_hw + reg &= ~GENMASK(nk->n.width + nk->n.shift - 1, nk->n.shift); + reg &= ~GENMASK(nk->k.width + nk->k.shift - 1, nk->k.shift); + +- writel(reg | ((k - 1) << nk->k.shift) | ((n - 1) << nk->n.shift), ++ writel(reg | ((_nk.k - 1) << nk->k.shift) | ((_nk.n - 1) << nk->n.shift), + nk->common.base + nk->common.reg); + + spin_unlock_irqrestore(nk->common.lock, flags); diff --git a/target/linux/sunxi/patches-4.9/0005-clk-sunxi-ng-Add-minimums-for-all-the-relevant-struc.patch b/target/linux/sunxi/patches-4.9/0005-clk-sunxi-ng-Add-minimums-for-all-the-relevant-struc.patch new file mode 100644 index 0000000000..0165ade138 --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0005-clk-sunxi-ng-Add-minimums-for-all-the-relevant-struc.patch @@ -0,0 +1,256 @@ +From 6e0d50daa97f4bf9706e343b4f71171e88921209 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +Acked-by: Chen-Yu Tsai +--- + drivers/clk/sunxi-ng/ccu_mult.c | 7 ++++++- + drivers/clk/sunxi-ng/ccu_nk.c | 12 ++++++++---- + drivers/clk/sunxi-ng/ccu_nkm.c | 18 ++++++++++++------ + drivers/clk/sunxi-ng/ccu_nkmp.c | 24 ++++++++++++++++-------- + drivers/clk/sunxi-ng/ccu_nm.c | 12 ++++++++---- + 5 files changed, 50 insertions(+), 23 deletions(-) + +--- a/drivers/clk/sunxi-ng/ccu_mult.c ++++ b/drivers/clk/sunxi-ng/ccu_mult.c +@@ -14,7 +14,7 @@ + #include "ccu_mult.h" + + struct _ccu_mult { +- unsigned long mult, max; ++ unsigned long mult, min, max; + }; + + static void ccu_mult_find_best(unsigned long parent, unsigned long rate, +@@ -23,6 +23,9 @@ static void ccu_mult_find_best(unsigned + int _mult; + + _mult = rate / parent; ++ if (_mult < mult->min) ++ _mult = mult->min; ++ + if (_mult > mult->max) + _mult = mult->max; + +@@ -37,6 +40,7 @@ static unsigned long ccu_mult_round_rate + struct ccu_mult *cm = data; + struct _ccu_mult _cm; + ++ _cm.min = 1; + _cm.max = 1 << cm->mult.width; + ccu_mult_find_best(parent_rate, rate, &_cm); + +@@ -101,6 +105,7 @@ static int ccu_mult_set_rate(struct clk_ + ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1, + &parent_rate); + ++ _cm.min = 1; + _cm.max = 1 << cm->mult.width; + ccu_mult_find_best(parent_rate, rate, &_cm); + +--- a/drivers/clk/sunxi-ng/ccu_nk.c ++++ b/drivers/clk/sunxi-ng/ccu_nk.c +@@ -14,8 +14,8 @@ + #include "ccu_nk.h" + + struct _ccu_nk { +- unsigned long n, max_n; +- unsigned long k, max_k; ++ unsigned long n, min_n, max_n; ++ unsigned long k, min_k, max_k; + }; + + static void ccu_nk_find_best(unsigned long parent, unsigned long rate, +@@ -25,8 +25,8 @@ static void ccu_nk_find_best(unsigned lo + unsigned int best_k = 0, best_n = 0; + unsigned int _k, _n; + +- for (_k = 1; _k <= nk->max_k; _k++) { +- for (_n = 1; _n <= nk->max_n; _n++) { ++ for (_k = nk->min_k; _k <= nk->max_k; _k++) { ++ for (_n = nk->min_n; _n <= nk->max_n; _n++) { + unsigned long tmp_rate = parent * _n * _k; + + if (tmp_rate > rate) +@@ -97,7 +97,9 @@ static long ccu_nk_round_rate(struct clk + if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate *= nk->fixed_post_div; + ++ _nk.min_n = 1; + _nk.max_n = 1 << nk->n.width; ++ _nk.min_k = 1; + _nk.max_k = 1 << nk->k.width; + + ccu_nk_find_best(*parent_rate, rate, &_nk); +@@ -120,7 +122,9 @@ static int ccu_nk_set_rate(struct clk_hw + if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate = rate * nk->fixed_post_div; + ++ _nk.min_n = 1; + _nk.max_n = 1 << nk->n.width; ++ _nk.min_k = 1; + _nk.max_k = 1 << nk->k.width; + + ccu_nk_find_best(parent_rate, rate, &_nk); +--- a/drivers/clk/sunxi-ng/ccu_nkm.c ++++ b/drivers/clk/sunxi-ng/ccu_nkm.c +@@ -14,9 +14,9 @@ + #include "ccu_nkm.h" + + struct _ccu_nkm { +- unsigned long n, max_n; +- unsigned long k, max_k; +- unsigned long m, max_m; ++ unsigned long n, min_n, max_n; ++ unsigned long k, min_k, max_k; ++ unsigned long m, min_m, max_m; + }; + + static void ccu_nkm_find_best(unsigned long parent, unsigned long rate, +@@ -26,9 +26,9 @@ static void ccu_nkm_find_best(unsigned l + unsigned long best_n = 0, best_k = 0, best_m = 0; + unsigned long _n, _k, _m; + +- for (_k = 1; _k <= nkm->max_k; _k++) { +- for (_n = 1; _n <= nkm->max_n; _n++) { +- for (_m = 1; _n <= nkm->max_m; _m++) { ++ for (_k = nkm->min_k; _k <= nkm->max_k; _k++) { ++ for (_n = nkm->min_n; _n <= nkm->max_n; _n++) { ++ for (_m = nkm->min_m; _m <= nkm->max_m; _m++) { + unsigned long tmp_rate; + + tmp_rate = parent * _n * _k / _m; +@@ -100,8 +100,11 @@ static unsigned long ccu_nkm_round_rate( + struct ccu_nkm *nkm = data; + struct _ccu_nkm _nkm; + ++ _nkm.min_n = 1; + _nkm.max_n = 1 << nkm->n.width; ++ _nkm.min_k = 1; + _nkm.max_k = 1 << nkm->k.width; ++ _nkm.min_m = 1; + _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width; + + ccu_nkm_find_best(parent_rate, rate, &_nkm); +@@ -126,8 +129,11 @@ static int ccu_nkm_set_rate(struct clk_h + unsigned long flags; + u32 reg; + ++ _nkm.min_n = 1; + _nkm.max_n = 1 << nkm->n.width; ++ _nkm.min_k = 1; + _nkm.max_k = 1 << nkm->k.width; ++ _nkm.min_m = 1; + _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width; + + ccu_nkm_find_best(parent_rate, rate, &_nkm); +--- a/drivers/clk/sunxi-ng/ccu_nkmp.c ++++ b/drivers/clk/sunxi-ng/ccu_nkmp.c +@@ -14,10 +14,10 @@ + #include "ccu_nkmp.h" + + struct _ccu_nkmp { +- unsigned long n, max_n; +- unsigned long k, max_k; +- unsigned long m, max_m; +- unsigned long p, max_p; ++ unsigned long n, min_n, max_n; ++ unsigned long k, min_k, max_k; ++ unsigned long m, min_m, max_m; ++ unsigned long p, min_p, max_p; + }; + + static void ccu_nkmp_find_best(unsigned long parent, unsigned long rate, +@@ -27,10 +27,10 @@ static void ccu_nkmp_find_best(unsigned + unsigned long best_n = 0, best_k = 0, best_m = 0, best_p = 0; + unsigned long _n, _k, _m, _p; + +- for (_k = 1; _k <= nkmp->max_k; _k++) { +- for (_n = 1; _n <= nkmp->max_n; _n++) { +- for (_m = 1; _n <= nkmp->max_m; _m++) { +- for (_p = 1; _p <= nkmp->max_p; _p <<= 1) { ++ for (_k = nkmp->min_k; _k <= nkmp->max_k; _k++) { ++ for (_n = nkmp->min_n; _n <= nkmp->max_n; _n++) { ++ for (_m = nkmp->min_m; _m <= nkmp->max_m; _m++) { ++ for (_p = nkmp->min_p; _p <= nkmp->max_p; _p <<= 1) { + unsigned long tmp_rate; + + tmp_rate = parent * _n * _k / (_m * _p); +@@ -107,9 +107,13 @@ static long ccu_nkmp_round_rate(struct c + struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw); + struct _ccu_nkmp _nkmp; + ++ _nkmp.min_n = 1; + _nkmp.max_n = 1 << nkmp->n.width; ++ _nkmp.min_k = 1; + _nkmp.max_k = 1 << nkmp->k.width; ++ _nkmp.min_m = 1; + _nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width; ++ _nkmp.min_p = 1; + _nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1); + + ccu_nkmp_find_best(*parent_rate, rate, &_nkmp); +@@ -125,9 +129,13 @@ static int ccu_nkmp_set_rate(struct clk_ + unsigned long flags; + u32 reg; + ++ _nkmp.min_n = 1; + _nkmp.max_n = 1 << nkmp->n.width; ++ _nkmp.min_k = 1; + _nkmp.max_k = 1 << nkmp->k.width; ++ _nkmp.min_m = 1; + _nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width; ++ _nkmp.min_p = 1; + _nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1); + + ccu_nkmp_find_best(parent_rate, rate, &_nkmp); +--- a/drivers/clk/sunxi-ng/ccu_nm.c ++++ b/drivers/clk/sunxi-ng/ccu_nm.c +@@ -15,8 +15,8 @@ + #include "ccu_nm.h" + + struct _ccu_nm { +- unsigned long n, max_n; +- unsigned long m, max_m; ++ unsigned long n, min_n, max_n; ++ unsigned long m, min_m, max_m; + }; + + static void ccu_nm_find_best(unsigned long parent, unsigned long rate, +@@ -26,8 +26,8 @@ static void ccu_nm_find_best(unsigned lo + unsigned long best_n = 0, best_m = 0; + unsigned long _n, _m; + +- for (_n = 1; _n <= nm->max_n; _n++) { +- for (_m = 1; _n <= nm->max_m; _m++) { ++ for (_n = nm->min_n; _n <= nm->max_n; _n++) { ++ for (_m = nm->min_m; _m <= nm->max_m; _m++) { + unsigned long tmp_rate = parent * _n / _m; + + if (tmp_rate > rate) +@@ -93,7 +93,9 @@ static long ccu_nm_round_rate(struct clk + struct ccu_nm *nm = hw_to_ccu_nm(hw); + struct _ccu_nm _nm; + ++ _nm.min_n = 1; + _nm.max_n = 1 << nm->n.width; ++ _nm.min_m = 1; + _nm.max_m = nm->m.max ?: 1 << nm->m.width; + + ccu_nm_find_best(*parent_rate, rate, &_nm); +@@ -114,7 +116,9 @@ static int ccu_nm_set_rate(struct clk_hw + else + ccu_frac_helper_disable(&nm->common, &nm->frac); + ++ _nm.min_n = 1; + _nm.max_n = 1 << nm->n.width; ++ _nm.min_m = 1; + _nm.max_m = nm->m.max ?: 1 << nm->m.width; + + ccu_nm_find_best(parent_rate, rate, &_nm); diff --git a/target/linux/sunxi/patches-4.9/0006-clk-sunxi-ng-Implement-minimum-for-multipliers.patch b/target/linux/sunxi/patches-4.9/0006-clk-sunxi-ng-Implement-minimum-for-multipliers.patch new file mode 100644 index 0000000000..668d596493 --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0006-clk-sunxi-ng-Implement-minimum-for-multipliers.patch @@ -0,0 +1,132 @@ +From 2beaa601c849e72683a2dd0fe6fd77763f19f051 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +Acked-by: Chen-Yu Tsai +--- + drivers/clk/sunxi-ng/ccu_mult.c | 2 +- + drivers/clk/sunxi-ng/ccu_mult.h | 13 +++++++++---- + drivers/clk/sunxi-ng/ccu_nk.c | 8 ++++---- + drivers/clk/sunxi-ng/ccu_nkm.c | 8 ++++---- + drivers/clk/sunxi-ng/ccu_nkmp.c | 4 ++-- + drivers/clk/sunxi-ng/ccu_nm.c | 2 +- + 6 files changed, 21 insertions(+), 16 deletions(-) + +--- a/drivers/clk/sunxi-ng/ccu_mult.c ++++ b/drivers/clk/sunxi-ng/ccu_mult.c +@@ -105,7 +105,7 @@ static int ccu_mult_set_rate(struct clk_ + ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1, + &parent_rate); + +- _cm.min = 1; ++ _cm.min = cm->mult.min; + _cm.max = 1 << cm->mult.width; + ccu_mult_find_best(parent_rate, rate, &_cm); + +--- a/drivers/clk/sunxi-ng/ccu_mult.h ++++ b/drivers/clk/sunxi-ng/ccu_mult.h +@@ -7,14 +7,19 @@ + struct ccu_mult_internal { + u8 shift; + u8 width; ++ u8 min; + }; + +-#define _SUNXI_CCU_MULT(_shift, _width) \ +- { \ +- .shift = _shift, \ +- .width = _width, \ ++#define _SUNXI_CCU_MULT_MIN(_shift, _width, _min) \ ++ { \ ++ .shift = _shift, \ ++ .width = _width, \ ++ .min = _min, \ + } + ++#define _SUNXI_CCU_MULT(_shift, _width) \ ++ _SUNXI_CCU_MULT_MIN(_shift, _width, 1) ++ + struct ccu_mult { + u32 enable; + +--- a/drivers/clk/sunxi-ng/ccu_nk.c ++++ b/drivers/clk/sunxi-ng/ccu_nk.c +@@ -97,9 +97,9 @@ static long ccu_nk_round_rate(struct clk + if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate *= nk->fixed_post_div; + +- _nk.min_n = 1; ++ _nk.min_n = nk->n.min; + _nk.max_n = 1 << nk->n.width; +- _nk.min_k = 1; ++ _nk.min_k = nk->k.min; + _nk.max_k = 1 << nk->k.width; + + ccu_nk_find_best(*parent_rate, rate, &_nk); +@@ -122,9 +122,9 @@ static int ccu_nk_set_rate(struct clk_hw + if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate = rate * nk->fixed_post_div; + +- _nk.min_n = 1; ++ _nk.min_n = nk->n.min; + _nk.max_n = 1 << nk->n.width; +- _nk.min_k = 1; ++ _nk.min_k = nk->k.min; + _nk.max_k = 1 << nk->k.width; + + ccu_nk_find_best(parent_rate, rate, &_nk); +--- a/drivers/clk/sunxi-ng/ccu_nkm.c ++++ b/drivers/clk/sunxi-ng/ccu_nkm.c +@@ -100,9 +100,9 @@ static unsigned long ccu_nkm_round_rate( + struct ccu_nkm *nkm = data; + struct _ccu_nkm _nkm; + +- _nkm.min_n = 1; ++ _nkm.min_n = nkm->n.min; + _nkm.max_n = 1 << nkm->n.width; +- _nkm.min_k = 1; ++ _nkm.min_k = nkm->k.min; + _nkm.max_k = 1 << nkm->k.width; + _nkm.min_m = 1; + _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width; +@@ -129,9 +129,9 @@ static int ccu_nkm_set_rate(struct clk_h + unsigned long flags; + u32 reg; + +- _nkm.min_n = 1; ++ _nkm.min_n = nkm->n.min; + _nkm.max_n = 1 << nkm->n.width; +- _nkm.min_k = 1; ++ _nkm.min_k = nkm->k.min; + _nkm.max_k = 1 << nkm->k.width; + _nkm.min_m = 1; + _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width; +--- a/drivers/clk/sunxi-ng/ccu_nkmp.c ++++ b/drivers/clk/sunxi-ng/ccu_nkmp.c +@@ -107,9 +107,9 @@ static long ccu_nkmp_round_rate(struct c + struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw); + struct _ccu_nkmp _nkmp; + +- _nkmp.min_n = 1; ++ _nkmp.min_n = nkmp->n.min; + _nkmp.max_n = 1 << nkmp->n.width; +- _nkmp.min_k = 1; ++ _nkmp.min_k = nkmp->k.min; + _nkmp.max_k = 1 << nkmp->k.width; + _nkmp.min_m = 1; + _nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width; +--- a/drivers/clk/sunxi-ng/ccu_nm.c ++++ b/drivers/clk/sunxi-ng/ccu_nm.c +@@ -93,7 +93,7 @@ static long ccu_nm_round_rate(struct clk + struct ccu_nm *nm = hw_to_ccu_nm(hw); + struct _ccu_nm _nm; + +- _nm.min_n = 1; ++ _nm.min_n = nm->n.min; + _nm.max_n = 1 << nm->n.width; + _nm.min_m = 1; + _nm.max_m = nm->m.max ?: 1 << nm->m.width; diff --git a/target/linux/sunxi/patches-4.9/0007-clk-sunxi-ng-Add-A64-clocks.patch b/target/linux/sunxi/patches-4.9/0007-clk-sunxi-ng-Add-A64-clocks.patch new file mode 100644 index 0000000000..fa0bae92b4 --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0007-clk-sunxi-ng-Add-A64-clocks.patch @@ -0,0 +1,1295 @@ +From c6a0637460c29799f1e63a6a4a65bda22caf4a54 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +Acked-by: Chen-Yu Tsai +Signed-off-by: Maxime Ripard +--- + .../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 ++#include ++#include ++ ++#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 ++ * ++ * 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 ++#include ++ ++#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 ++ * ++ * 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 ++ * ++ * This file is dual-licensed: you can use it either under the terms ++ * of the GPL or the X11 license, at your option. Note that this dual ++ * licensing only applies to this file, and not this project as a ++ * whole. ++ * ++ * a) This file is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This file is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * Or, alternatively, ++ * ++ * b) Permission is hereby granted, free of charge, to any person ++ * obtaining a copy of this software and associated documentation ++ * files (the "Software"), to deal in the Software without ++ * restriction, including without limitation the rights to use, ++ * copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following ++ * conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES ++ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ++ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, ++ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ */ ++ ++#ifndef _DT_BINDINGS_RST_SUN50I_A64_H_ ++#define _DT_BINDINGS_RST_SUN50I_A64_H_ ++ ++#define RST_USB_PHY0 0 ++#define RST_USB_PHY1 1 ++#define RST_USB_HSIC 2 ++#define RST_DRAM 3 ++#define RST_MBUS 4 ++#define RST_BUS_MIPI_DSI 5 ++#define RST_BUS_CE 6 ++#define RST_BUS_DMA 7 ++#define RST_BUS_MMC0 8 ++#define RST_BUS_MMC1 9 ++#define RST_BUS_MMC2 10 ++#define RST_BUS_NAND 11 ++#define RST_BUS_DRAM 12 ++#define RST_BUS_EMAC 13 ++#define RST_BUS_TS 14 ++#define RST_BUS_HSTIMER 15 ++#define RST_BUS_SPI0 16 ++#define RST_BUS_SPI1 17 ++#define RST_BUS_OTG 18 ++#define RST_BUS_EHCI0 19 ++#define RST_BUS_EHCI1 20 ++#define RST_BUS_OHCI0 21 ++#define RST_BUS_OHCI1 22 ++#define RST_BUS_VE 23 ++#define RST_BUS_TCON0 24 ++#define RST_BUS_TCON1 25 ++#define RST_BUS_DEINTERLACE 26 ++#define RST_BUS_CSI 27 ++#define RST_BUS_HDMI0 28 ++#define RST_BUS_HDMI1 29 ++#define RST_BUS_DE 30 ++#define RST_BUS_GPU 31 ++#define RST_BUS_MSGBOX 32 ++#define RST_BUS_SPINLOCK 33 ++#define RST_BUS_DBG 34 ++#define RST_BUS_LVDS 35 ++#define RST_BUS_CODEC 36 ++#define RST_BUS_SPDIF 37 ++#define RST_BUS_THS 38 ++#define RST_BUS_I2S0 39 ++#define RST_BUS_I2S1 40 ++#define RST_BUS_I2S2 41 ++#define RST_BUS_I2C0 42 ++#define RST_BUS_I2C1 43 ++#define RST_BUS_I2C2 44 ++#define RST_BUS_SCR 45 ++#define RST_BUS_UART0 46 ++#define RST_BUS_UART1 47 ++#define RST_BUS_UART2 48 ++#define RST_BUS_UART3 49 ++#define RST_BUS_UART4 50 ++ ++#endif /* _DT_BINDINGS_RST_SUN50I_A64_H_ */ diff --git a/target/linux/sunxi/patches-4.9/0010-arm64-dts-add-Allwinner-A64-SoC-.dtsi.patch b/target/linux/sunxi/patches-4.9/0010-arm64-dts-add-Allwinner-A64-SoC-.dtsi.patch new file mode 100644 index 0000000000..eaaba96fc1 --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0010-arm64-dts-add-Allwinner-A64-SoC-.dtsi.patch @@ -0,0 +1,311 @@ +From 6bc37fac30cf01c39feb17834090089304bd1d31 Mon Sep 17 00:00:00 2001 +From: Andre Przywara +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 +Acked-by: Rob Herring +Acked-by: Chen-Yu Tsai +[Maxime: Convert to CCU binding, drop the MMC support for now] +Signed-off-by: Maxime Ripard +--- + 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 +--- /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 ++ * ++ * 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 ++#include ++#include ++#include ++ ++/ { ++ 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 = , ++ , ++ , ++ ; ++ }; ++ ++ 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 = , ++ , ++ ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ interrupt-controller; ++ #interrupt-cells = <3>; ++ }; ++ ++ rtc: rtc@1f00000 { ++ compatible = "allwinner,sun6i-a31-rtc"; ++ reg = <0x01f00000 0x54>; ++ interrupts = , ++ ; ++ }; ++ }; ++}; diff --git a/target/linux/sunxi/patches-4.9/0011-arm64-dts-add-Pine64-support.patch b/target/linux/sunxi/patches-4.9/0011-arm64-dts-add-Pine64-support.patch new file mode 100644 index 0000000000..9960588ab8 --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0011-arm64-dts-add-Pine64-support.patch @@ -0,0 +1,176 @@ +From 4e3886081848b7ea16452a92c4324acaab644d49 Mon Sep 17 00:00:00 2001 +From: Andre Przywara +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 +[Maxime: Removed the common DTSI and include directly the pine64 DTS] +Signed-off-by: Maxime Ripard +--- + arch/arm64/boot/dts/Makefile | 1 + + arch/arm64/boot/dts/allwinner/Makefile | 5 ++ + .../boot/dts/allwinner/sun50i-a64-pine64-plus.dts | 50 +++++++++++++++ + .../arm64/boot/dts/allwinner/sun50i-a64-pine64.dts | 74 ++++++++++++++++++++++ + 4 files changed, 130 insertions(+) + create mode 100644 arch/arm64/boot/dts/allwinner/Makefile + create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts + create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts + +--- a/arch/arm64/boot/dts/Makefile ++++ b/arch/arm64/boot/dts/Makefile +@@ -1,4 +1,5 @@ + dts-dirs += al ++dts-dirs += allwinner + dts-dirs += altera + dts-dirs += amd + dts-dirs += amlogic +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/Makefile +@@ -0,0 +1,5 @@ ++dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a64-pine64-plus.dtb sun50i-a64-pine64.dtb ++ ++always := $(dtb-y) ++subdir-y := $(dts-dirs) ++clean-files := *.dtb +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts +@@ -0,0 +1,50 @@ ++/* ++ * Copyright (c) 2016 ARM Ltd. ++ * ++ * This file is dual-licensed: you can use it either under the terms ++ * of the GPL or the X11 license, at your option. Note that this dual ++ * licensing only applies to this file, and not this project as a ++ * whole. ++ * ++ * a) This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * Or, alternatively, ++ * ++ * b) Permission is hereby granted, free of charge, to any person ++ * obtaining a copy of this software and associated documentation ++ * files (the "Software"), to deal in the Software without ++ * restriction, including without limitation the rights to use, ++ * copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following ++ * conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES ++ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ++ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, ++ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ */ ++ ++#include "sun50i-a64-pine64.dts" ++ ++/ { ++ model = "Pine64+"; ++ compatible = "pine64,pine64-plus", "allwinner,sun50i-a64"; ++ ++ /* TODO: Camera, Ethernet PHY, touchscreen, etc. */ ++}; +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts +@@ -0,0 +1,74 @@ ++/* ++ * Copyright (c) 2016 ARM Ltd. ++ * ++ * This file is dual-licensed: you can use it either under the terms ++ * of the GPL or the X11 license, at your option. Note that this dual ++ * licensing only applies to this file, and not this project as a ++ * whole. ++ * ++ * a) This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * Or, alternatively, ++ * ++ * b) Permission is hereby granted, free of charge, to any person ++ * obtaining a copy of this software and associated documentation ++ * files (the "Software"), to deal in the Software without ++ * restriction, including without limitation the rights to use, ++ * copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following ++ * conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES ++ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ++ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, ++ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ */ ++ ++/dts-v1/; ++ ++#include "sun50i-a64.dtsi" ++ ++/ { ++ model = "Pine64"; ++ compatible = "pine64,pine64", "allwinner,sun50i-a64"; ++ ++ aliases { ++ serial0 = &uart0; ++ }; ++ ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++}; ++ ++&uart0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart0_pins_a>; ++ status = "okay"; ++}; ++ ++&i2c1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c1_pins>; ++ status = "okay"; ++}; ++ ++&i2c1_pins { ++ bias-pull-up; ++}; diff --git a/target/linux/sunxi/patches-4.9/0012-arm64-dts-fix-build-errors-from-missing-dependencies.patch b/target/linux/sunxi/patches-4.9/0012-arm64-dts-fix-build-errors-from-missing-dependencies.patch new file mode 100644 index 0000000000..1719b682b3 --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0012-arm64-dts-fix-build-errors-from-missing-dependencies.patch @@ -0,0 +1,134 @@ +From f98121f3ef3d36f4d040b11ab38f15387f6eefa2 Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +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 +Acked-by: Maxime Ripard +Acked-by: Srinivas Kandagatla +Signed-off-by: Arnd Bergmann +--- + 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 + #include + #include +-#include + + / { + interrupt-parent = <&gic>; +@@ -137,7 +135,7 @@ + interrupts = , + , + ; +- clocks = <&ccu CLK_BUS_PIO>; ++ clocks = <&ccu 58>; + gpio-controller; + #gpio-cells = <3>; + interrupt-controller; +@@ -160,8 +158,8 @@ + interrupts = ; + 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 = ; + 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 = ; + 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 = ; + 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 = ; + 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 = ; +- 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 = ; +- 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 = ; +- clocks = <&ccu CLK_BUS_I2C2>; +- resets = <&ccu RST_BUS_I2C2>; ++ clocks = <&ccu 65>; ++ resets = <&ccu 44>; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; diff --git a/target/linux/sunxi/patches-4.9/0030-pinctrl-sunxi-Rework-the-pin-config-building-code.patch b/target/linux/sunxi/patches-4.9/0030-pinctrl-sunxi-Rework-the-pin-config-building-code.patch new file mode 100644 index 0000000000..498581712d --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0030-pinctrl-sunxi-Rework-the-pin-config-building-code.patch @@ -0,0 +1,251 @@ +From f233dbca6227703eaae2f67d6d9c79819773f16b Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/sunxi/pinctrl-sunxi.c | 178 +++++++++++++++++++++++++--------- + 1 file changed, 130 insertions(+), 48 deletions(-) + +--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c ++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c +@@ -145,6 +145,110 @@ static int sunxi_pctrl_get_group_pins(st + return 0; + } + ++static bool sunxi_pctrl_has_bias_prop(struct device_node *node) ++{ ++ return of_find_property(node, "allwinner,pull", NULL); ++} ++ ++static bool sunxi_pctrl_has_drive_prop(struct device_node *node) ++{ ++ return of_find_property(node, "allwinner,drive", NULL); ++} ++ ++static int sunxi_pctrl_parse_bias_prop(struct device_node *node) ++{ ++ u32 val; ++ ++ if (of_property_read_u32(node, "allwinner,pull", &val)) ++ return -EINVAL; ++ ++ switch (val) { ++ case 1: ++ return PIN_CONFIG_BIAS_PULL_UP; ++ case 2: ++ return PIN_CONFIG_BIAS_PULL_DOWN; ++ } ++ ++ return -EINVAL; ++} ++ ++static int sunxi_pctrl_parse_drive_prop(struct device_node *node) ++{ ++ u32 val; ++ ++ if (of_property_read_u32(node, "allwinner,drive", &val)) ++ return -EINVAL; ++ ++ return (val + 1) * 10; ++} ++ ++static const char *sunxi_pctrl_parse_function_prop(struct device_node *node) ++{ ++ const char *function; ++ int ret; ++ ++ ret = of_property_read_string(node, "allwinner,function", &function); ++ if (!ret) ++ return function; ++ ++ return NULL; ++} ++ ++static const char *sunxi_pctrl_find_pins_prop(struct device_node *node, ++ int *npins) ++{ ++ int count; ++ ++ count = of_property_count_strings(node, "allwinner,pins"); ++ if (count > 0) { ++ *npins = count; ++ return "allwinner,pins"; ++ } ++ ++ return NULL; ++} ++ ++static unsigned long *sunxi_pctrl_build_pin_config(struct device_node *node, ++ unsigned int *len) ++{ ++ unsigned long *pinconfig; ++ unsigned int configlen = 0, idx = 0; ++ ++ if (sunxi_pctrl_has_drive_prop(node)) ++ configlen++; ++ if (sunxi_pctrl_has_bias_prop(node)) ++ configlen++; ++ ++ pinconfig = kzalloc(configlen * sizeof(*pinconfig), GFP_KERNEL); ++ if (!pinconfig) ++ return NULL; ++ ++ if (sunxi_pctrl_has_drive_prop(node)) { ++ int drive = sunxi_pctrl_parse_drive_prop(node); ++ if (drive < 0) ++ goto err_free; ++ ++ pinconfig[idx++] = pinconf_to_config_packed(PIN_CONFIG_DRIVE_STRENGTH, ++ drive); ++ } ++ ++ if (sunxi_pctrl_has_bias_prop(node)) { ++ int pull = sunxi_pctrl_parse_bias_prop(node); ++ if (pull < 0) ++ goto err_free; ++ ++ pinconfig[idx++] = pinconf_to_config_packed(pull, 0); ++ } ++ ++ ++ *len = configlen; ++ return pinconfig; ++ ++err_free: ++ kfree(pinconfig); ++ return NULL; ++} ++ + static int sunxi_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *node, + struct pinctrl_map **map, +@@ -153,38 +257,45 @@ static int sunxi_pctrl_dt_node_to_map(st + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + unsigned long *pinconfig; + struct property *prop; +- const char *function; ++ const char *function, *pin_prop; + const char *group; +- int ret, nmaps, i = 0; +- u32 val; ++ int ret, npins, nmaps, configlen = 0, i = 0; + + *map = NULL; + *num_maps = 0; + +- ret = of_property_read_string(node, "allwinner,function", &function); +- if (ret) { +- dev_err(pctl->dev, +- "missing allwinner,function property in node %s\n", ++ function = sunxi_pctrl_parse_function_prop(node); ++ if (!function) { ++ dev_err(pctl->dev, "missing function property in node %s\n", + node->name); + return -EINVAL; + } + +- nmaps = of_property_count_strings(node, "allwinner,pins") * 2; +- if (nmaps < 0) { +- dev_err(pctl->dev, +- "missing allwinner,pins property in node %s\n", ++ pin_prop = sunxi_pctrl_find_pins_prop(node, &npins); ++ if (!pin_prop) { ++ dev_err(pctl->dev, "missing pins property in node %s\n", + node->name); + return -EINVAL; + } + ++ /* ++ * We have two maps for each pin: one for the function, one ++ * for the configuration (bias, strength, etc) ++ */ ++ nmaps = npins * 2; + *map = kmalloc(nmaps * sizeof(struct pinctrl_map), GFP_KERNEL); + if (!*map) + return -ENOMEM; + +- of_property_for_each_string(node, "allwinner,pins", prop, group) { ++ pinconfig = sunxi_pctrl_build_pin_config(node, &configlen); ++ if (!pinconfig) { ++ ret = -EINVAL; ++ goto err_free_map; ++ } ++ ++ of_property_for_each_string(node, pin_prop, prop, group) { + struct sunxi_pinctrl_group *grp = + sunxi_pinctrl_find_group_by_name(pctl, group); +- int j = 0, configlen = 0; + + if (!grp) { + dev_err(pctl->dev, "unknown pin %s", group); +@@ -207,34 +318,6 @@ static int sunxi_pctrl_dt_node_to_map(st + + (*map)[i].type = PIN_MAP_TYPE_CONFIGS_GROUP; + (*map)[i].data.configs.group_or_pin = group; +- +- if (of_find_property(node, "allwinner,drive", NULL)) +- configlen++; +- if (of_find_property(node, "allwinner,pull", NULL)) +- configlen++; +- +- pinconfig = kzalloc(configlen * sizeof(*pinconfig), GFP_KERNEL); +- if (!pinconfig) { +- kfree(*map); +- return -ENOMEM; +- } +- +- if (!of_property_read_u32(node, "allwinner,drive", &val)) { +- u16 strength = (val + 1) * 10; +- pinconfig[j++] = +- pinconf_to_config_packed(PIN_CONFIG_DRIVE_STRENGTH, +- strength); +- } +- +- if (!of_property_read_u32(node, "allwinner,pull", &val)) { +- enum pin_config_param pull = PIN_CONFIG_END; +- if (val == 1) +- pull = PIN_CONFIG_BIAS_PULL_UP; +- else if (val == 2) +- pull = PIN_CONFIG_BIAS_PULL_DOWN; +- pinconfig[j++] = pinconf_to_config_packed(pull, 0); +- } +- + (*map)[i].data.configs.configs = pinconfig; + (*map)[i].data.configs.num_configs = configlen; + +@@ -244,19 +327,18 @@ static int sunxi_pctrl_dt_node_to_map(st + *num_maps = nmaps; + + return 0; ++ ++err_free_map: ++ kfree(map); ++ return ret; + } + + static void sunxi_pctrl_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, + unsigned num_maps) + { +- int i; +- +- for (i = 0; i < num_maps; i++) { +- if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP) +- kfree(map[i].data.configs.configs); +- } +- ++ /* All the maps have the same pin config, free only the first one */ ++ kfree(map[0].data.configs.configs); + kfree(map); + } + diff --git a/target/linux/sunxi/patches-4.9/0031-pinctrl-sunxi-Use-macros-from-bindings-header-file-f.patch b/target/linux/sunxi/patches-4.9/0031-pinctrl-sunxi-Use-macros-from-bindings-header-file-f.patch new file mode 100644 index 0000000000..39be965422 --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0031-pinctrl-sunxi-Use-macros-from-bindings-header-file-f.patch @@ -0,0 +1,38 @@ +From 42676fa4aa87eda4fc762df495d4bde2ddc4bfce Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +Signed-off-by: Maxime Ripard +Signed-off-by: Linus Walleij +--- + 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 + #include + ++#include ++ + #include "../core.h" + #include "pinctrl-sunxi.h" + +@@ -163,9 +165,9 @@ static int sunxi_pctrl_parse_bias_prop(s + return -EINVAL; + + switch (val) { +- case 1: ++ case SUN4I_PINCTRL_PULL_UP: + return PIN_CONFIG_BIAS_PULL_UP; +- case 2: ++ case SUN4I_PINCTRL_PULL_DOWN: + return PIN_CONFIG_BIAS_PULL_DOWN; + } + diff --git a/target/linux/sunxi/patches-4.9/0032-pinctrl-sunxi-Handle-bias-disable.patch b/target/linux/sunxi/patches-4.9/0032-pinctrl-sunxi-Handle-bias-disable.patch new file mode 100644 index 0000000000..61d6102c92 --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0032-pinctrl-sunxi-Handle-bias-disable.patch @@ -0,0 +1,42 @@ +From 07fe64ba213f36ca8f6ffd8c4d5893f022744fdb Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +Signed-off-by: Maxime Ripard +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/sunxi/pinctrl-sunxi.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c ++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c +@@ -165,6 +165,8 @@ static int sunxi_pctrl_parse_bias_prop(s + return -EINVAL; + + switch (val) { ++ case SUN4I_PINCTRL_NO_PULL: ++ return PIN_CONFIG_BIAS_DISABLE; + case SUN4I_PINCTRL_PULL_UP: + return PIN_CONFIG_BIAS_PULL_UP; + case SUN4I_PINCTRL_PULL_DOWN: +@@ -401,6 +403,12 @@ static int sunxi_pconf_group_set(struct + | dlevel << sunxi_dlevel_offset(pin), + pctl->membase + sunxi_dlevel_reg(pin)); + break; ++ case PIN_CONFIG_BIAS_DISABLE: ++ val = readl(pctl->membase + sunxi_pull_reg(pin)); ++ mask = PULL_PINS_MASK << sunxi_pull_offset(pin); ++ writel((val & ~mask), ++ pctl->membase + sunxi_pull_reg(pin)); ++ break; + case PIN_CONFIG_BIAS_PULL_UP: + val = readl(pctl->membase + sunxi_pull_reg(pin)); + mask = PULL_PINS_MASK << sunxi_pull_offset(pin); diff --git a/target/linux/sunxi/patches-4.9/0033-pinctrl-sunxi-Support-generic-binding.patch b/target/linux/sunxi/patches-4.9/0033-pinctrl-sunxi-Support-generic-binding.patch new file mode 100644 index 0000000000..35c6876812 --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0033-pinctrl-sunxi-Support-generic-binding.patch @@ -0,0 +1,106 @@ +From cefbf1a1b29531a970bc2908a50a75d6474fcc38 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +Signed-off-by: Maxime Ripard +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/sunxi/pinctrl-sunxi.c | 48 +++++++++++++++++++++++++++++++++-- + 1 file changed, 46 insertions(+), 2 deletions(-) + +--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c ++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c +@@ -149,18 +149,33 @@ static int sunxi_pctrl_get_group_pins(st + + static bool sunxi_pctrl_has_bias_prop(struct device_node *node) + { +- return of_find_property(node, "allwinner,pull", NULL); ++ return of_find_property(node, "bias-pull-up", NULL) || ++ of_find_property(node, "bias-pull-down", NULL) || ++ of_find_property(node, "bias-disable", NULL) || ++ of_find_property(node, "allwinner,pull", NULL); + } + + static bool sunxi_pctrl_has_drive_prop(struct device_node *node) + { +- return of_find_property(node, "allwinner,drive", NULL); ++ return of_find_property(node, "drive-strength", NULL) || ++ of_find_property(node, "allwinner,drive", NULL); + } + + static int sunxi_pctrl_parse_bias_prop(struct device_node *node) + { + u32 val; + ++ /* Try the new style binding */ ++ if (of_find_property(node, "bias-pull-up", NULL)) ++ return PIN_CONFIG_BIAS_PULL_UP; ++ ++ if (of_find_property(node, "bias-pull-down", NULL)) ++ return PIN_CONFIG_BIAS_PULL_DOWN; ++ ++ if (of_find_property(node, "bias-disable", NULL)) ++ return PIN_CONFIG_BIAS_DISABLE; ++ ++ /* And fall back to the old binding */ + if (of_property_read_u32(node, "allwinner,pull", &val)) + return -EINVAL; + +@@ -180,6 +195,21 @@ static int sunxi_pctrl_parse_drive_prop( + { + u32 val; + ++ /* Try the new style binding */ ++ if (!of_property_read_u32(node, "drive-strength", &val)) { ++ /* We can't go below 10mA ... */ ++ if (val < 10) ++ return -EINVAL; ++ ++ /* ... and only up to 40 mA ... */ ++ if (val > 40) ++ val = 40; ++ ++ /* by steps of 10 mA */ ++ return rounddown(val, 10); ++ } ++ ++ /* And then fall back to the old binding */ + if (of_property_read_u32(node, "allwinner,drive", &val)) + return -EINVAL; + +@@ -191,6 +221,12 @@ static const char *sunxi_pctrl_parse_fun + const char *function; + int ret; + ++ /* Try the generic binding */ ++ ret = of_property_read_string(node, "function", &function); ++ if (!ret) ++ return function; ++ ++ /* And fall back to our legacy one */ + ret = of_property_read_string(node, "allwinner,function", &function); + if (!ret) + return function; +@@ -203,6 +239,14 @@ static const char *sunxi_pctrl_find_pins + { + int count; + ++ /* Try the generic binding */ ++ count = of_property_count_strings(node, "pins"); ++ if (count > 0) { ++ *npins = count; ++ return "pins"; ++ } ++ ++ /* And fall back to our legacy one */ + count = of_property_count_strings(node, "allwinner,pins"); + if (count > 0) { + *npins = count; diff --git a/target/linux/sunxi/patches-4.9/0034-pinctrl-sunxi-Deal-with-configless-pins.patch b/target/linux/sunxi/patches-4.9/0034-pinctrl-sunxi-Deal-with-configless-pins.patch new file mode 100644 index 0000000000..119ab2b8f9 --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0034-pinctrl-sunxi-Deal-with-configless-pins.patch @@ -0,0 +1,128 @@ +From e11dee2e98f8abc99ad5336796576a827853ccfa Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +Acked-by: Chen-Yu Tsai +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/sunxi/pinctrl-sunxi.c | 51 +++++++++++++++++++++++++---------- + 1 file changed, 37 insertions(+), 14 deletions(-) + +--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c ++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c +@@ -261,20 +261,29 @@ static unsigned long *sunxi_pctrl_build_ + { + unsigned long *pinconfig; + unsigned int configlen = 0, idx = 0; ++ int ret; + + if (sunxi_pctrl_has_drive_prop(node)) + configlen++; + if (sunxi_pctrl_has_bias_prop(node)) + configlen++; + ++ /* ++ * If we don't have any configuration, bail out ++ */ ++ if (!configlen) ++ return NULL; ++ + pinconfig = kzalloc(configlen * sizeof(*pinconfig), GFP_KERNEL); + if (!pinconfig) +- return NULL; ++ return ERR_PTR(-ENOMEM); + + if (sunxi_pctrl_has_drive_prop(node)) { + int drive = sunxi_pctrl_parse_drive_prop(node); +- if (drive < 0) ++ if (drive < 0) { ++ ret = drive; + goto err_free; ++ } + + pinconfig[idx++] = pinconf_to_config_packed(PIN_CONFIG_DRIVE_STRENGTH, + drive); +@@ -282,8 +291,10 @@ static unsigned long *sunxi_pctrl_build_ + + if (sunxi_pctrl_has_bias_prop(node)) { + int pull = sunxi_pctrl_parse_bias_prop(node); +- if (pull < 0) ++ if (pull < 0) { ++ ret = pull; + goto err_free; ++ } + + pinconfig[idx++] = pinconf_to_config_packed(pull, 0); + } +@@ -294,7 +305,7 @@ static unsigned long *sunxi_pctrl_build_ + + err_free: + kfree(pinconfig); +- return NULL; ++ return ERR_PTR(ret); + } + + static int sunxi_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, +@@ -328,7 +339,10 @@ static int sunxi_pctrl_dt_node_to_map(st + + /* + * We have two maps for each pin: one for the function, one +- * for the configuration (bias, strength, etc) ++ * for the configuration (bias, strength, etc). ++ * ++ * We might be slightly overshooting, since we might not have ++ * any configuration. + */ + nmaps = npins * 2; + *map = kmalloc(nmaps * sizeof(struct pinctrl_map), GFP_KERNEL); +@@ -336,8 +350,8 @@ static int sunxi_pctrl_dt_node_to_map(st + return -ENOMEM; + + pinconfig = sunxi_pctrl_build_pin_config(node, &configlen); +- if (!pinconfig) { +- ret = -EINVAL; ++ if (IS_ERR(pinconfig)) { ++ ret = PTR_ERR(pinconfig); + goto err_free_map; + } + +@@ -364,15 +378,24 @@ static int sunxi_pctrl_dt_node_to_map(st + + i++; + +- (*map)[i].type = PIN_MAP_TYPE_CONFIGS_GROUP; +- (*map)[i].data.configs.group_or_pin = group; +- (*map)[i].data.configs.configs = pinconfig; +- (*map)[i].data.configs.num_configs = configlen; +- +- i++; ++ if (pinconfig) { ++ (*map)[i].type = PIN_MAP_TYPE_CONFIGS_GROUP; ++ (*map)[i].data.configs.group_or_pin = group; ++ (*map)[i].data.configs.configs = pinconfig; ++ (*map)[i].data.configs.num_configs = configlen; ++ i++; ++ } + } + +- *num_maps = nmaps; ++ *num_maps = i; ++ ++ /* ++ * We know have the number of maps we need, we can resize our ++ * map array ++ */ ++ *map = krealloc(*map, i * sizeof(struct pinctrl_map), GFP_KERNEL); ++ if (!map) ++ return -ENOMEM; + + return 0; + diff --git a/target/linux/sunxi/patches-4.9/0035-pinctrl-sunxi-make-bool-drivers-explicitly-non-modul.patch b/target/linux/sunxi/patches-4.9/0035-pinctrl-sunxi-make-bool-drivers-explicitly-non-modul.patch new file mode 100644 index 0000000000..8ab535c30f --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0035-pinctrl-sunxi-make-bool-drivers-explicitly-non-modul.patch @@ -0,0 +1,437 @@ +From 0c8c6ba00cbf2c0a6164aa41d43d017d65caf321 Mon Sep 17 00:00:00 2001 +From: Paul Gortmaker +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 +Cc: Chen-Yu Tsai +Cc: Hans de Goede +Cc: Linus Walleij +Cc: Patrice Chotard +Cc: Hongzhou Yang +Cc: Fabian Frederick +Cc: Maxime Coquelin +Cc: Vishnu Patekar +Cc: Mylene Josserand +Cc: linux-gpio@vger.kernel.org +Cc: linux-arm-kernel@lists.infradead.org +Signed-off-by: Paul Gortmaker +Acked-by: Maxime Ripard +Signed-off-by: Linus Walleij +--- + 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 ++#include + #include + #include + #include +@@ -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 ++#include + #include + #include + #include +@@ -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 ++#include + #include + #include + #include +@@ -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 ++#include + #include + #include + #include +@@ -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 ++#include + #include + #include + #include +@@ -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 ++#include + #include + #include + #include +@@ -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 ++#include + #include + #include + #include +@@ -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 "); +-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 ++#include + #include + #include + #include +@@ -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 ++#include + #include + #include + #include +@@ -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 "); +-MODULE_AUTHOR("Boris Brezillon ++#include + #include + #include + #include +@@ -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 "); +-MODULE_AUTHOR("Maxime Ripard ++#include + #include + #include + #include +@@ -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 "); +-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 ++#include + #include + #include + #include +@@ -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 "); +-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 ++#include + #include + #include + #include +@@ -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 "); +-MODULE_DESCRIPTION("Allwinner A80 pinctrl driver"); +-MODULE_LICENSE("GPL"); ++builtin_platform_driver(sun9i_a80_pinctrl_driver); diff --git a/target/linux/sunxi/patches-4.9/0036-pinctrl-sunxi-Free-configs-in-pinctrl_map-only-if-it.patch b/target/linux/sunxi/patches-4.9/0036-pinctrl-sunxi-Free-configs-in-pinctrl_map-only-if-it.patch new file mode 100644 index 0000000000..02c5f568c8 --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0036-pinctrl-sunxi-Free-configs-in-pinctrl_map-only-if-it.patch @@ -0,0 +1,51 @@ +From 88f01a1bd0e0dbd01b65907023dbe53cf524ea2a Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai +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 +Acked-by: Maxime Ripard +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/sunxi/pinctrl-sunxi.c | 17 +++++++++++++++-- + 1 file changed, 15 insertions(+), 2 deletions(-) + +--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c ++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c +@@ -408,8 +408,21 @@ static void sunxi_pctrl_dt_free_map(stru + struct pinctrl_map *map, + unsigned num_maps) + { +- /* All the maps have the same pin config, free only the first one */ +- kfree(map[0].data.configs.configs); ++ int i; ++ ++ /* pin config is never in the first map */ ++ for (i = 1; i < num_maps; i++) { ++ if (map[i].type != PIN_MAP_TYPE_CONFIGS_GROUP) ++ continue; ++ ++ /* ++ * All the maps share the same pin config, ++ * free only the first one we find. ++ */ ++ kfree(map[i].data.configs.configs); ++ break; ++ } ++ + kfree(map); + } + diff --git a/target/linux/sunxi/patches-4.9/0037-pinctrl-sunxi-Fix-PIN_CONFIG_BIAS_PULL_-DOWN-UP-argu.patch b/target/linux/sunxi/patches-4.9/0037-pinctrl-sunxi-Fix-PIN_CONFIG_BIAS_PULL_-DOWN-UP-argu.patch new file mode 100644 index 0000000000..4921240f79 --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0037-pinctrl-sunxi-Fix-PIN_CONFIG_BIAS_PULL_-DOWN-UP-argu.patch @@ -0,0 +1,40 @@ +From 223dba00b4072efc590c7d648f230db1b44186b9 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai +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 +Acked-by: Maxime Ripard +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/sunxi/pinctrl-sunxi.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c ++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c +@@ -291,12 +291,16 @@ static unsigned long *sunxi_pctrl_build_ + + if (sunxi_pctrl_has_bias_prop(node)) { + int pull = sunxi_pctrl_parse_bias_prop(node); ++ int arg = 0; + if (pull < 0) { + ret = pull; + goto err_free; + } + +- pinconfig[idx++] = pinconf_to_config_packed(pull, 0); ++ if (pull != PIN_CONFIG_BIAS_DISABLE) ++ arg = 1; /* hardware uses weak pull resistors */ ++ ++ pinconfig[idx++] = pinconf_to_config_packed(pull, arg); + } + + diff --git a/target/linux/sunxi/patches-4.9/0038-pinctrl-sunxi-Add-support-for-fetching-pinconf-setti.patch b/target/linux/sunxi/patches-4.9/0038-pinctrl-sunxi-Add-support-for-fetching-pinconf-setti.patch new file mode 100644 index 0000000000..d7972197ff --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0038-pinctrl-sunxi-Add-support-for-fetching-pinconf-setti.patch @@ -0,0 +1,158 @@ +From c5fda170e87a4bdaeb278f7e50f7a1f654e94eb5 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai +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 +Acked-by: Maxime Ripard +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/sunxi/pinctrl-sunxi.c | 86 +++++++++++++++++++++++++++++++++-- + drivers/pinctrl/sunxi/pinctrl-sunxi.h | 1 - + 2 files changed, 81 insertions(+), 6 deletions(-) + +--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c ++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c +@@ -438,15 +438,91 @@ static const struct pinctrl_ops sunxi_pc + .get_group_pins = sunxi_pctrl_get_group_pins, + }; + ++static int sunxi_pconf_reg(unsigned pin, enum pin_config_param param, ++ u32 *offset, u32 *shift, u32 *mask) ++{ ++ switch (param) { ++ case PIN_CONFIG_DRIVE_STRENGTH: ++ *offset = sunxi_dlevel_reg(pin); ++ *shift = sunxi_dlevel_offset(pin); ++ *mask = DLEVEL_PINS_MASK; ++ break; ++ ++ case PIN_CONFIG_BIAS_PULL_UP: ++ case PIN_CONFIG_BIAS_PULL_DOWN: ++ case PIN_CONFIG_BIAS_DISABLE: ++ *offset = sunxi_pull_reg(pin); ++ *shift = sunxi_pull_offset(pin); ++ *mask = PULL_PINS_MASK; ++ break; ++ ++ default: ++ return -ENOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static int sunxi_pconf_get(struct pinctrl_dev *pctldev, unsigned pin, ++ unsigned long *config) ++{ ++ struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); ++ enum pin_config_param param = pinconf_to_config_param(*config); ++ u32 offset, shift, mask, val; ++ u16 arg; ++ int ret; ++ ++ pin -= pctl->desc->pin_base; ++ ++ ret = sunxi_pconf_reg(pin, param, &offset, &shift, &mask); ++ if (ret < 0) ++ return ret; ++ ++ val = (readl(pctl->membase + offset) >> shift) & mask; ++ ++ switch (pinconf_to_config_param(*config)) { ++ case PIN_CONFIG_DRIVE_STRENGTH: ++ arg = (val + 1) * 10; ++ break; ++ ++ case PIN_CONFIG_BIAS_PULL_UP: ++ if (val != SUN4I_PINCTRL_PULL_UP) ++ return -EINVAL; ++ arg = 1; /* hardware is weak pull-up */ ++ break; ++ ++ case PIN_CONFIG_BIAS_PULL_DOWN: ++ if (val != SUN4I_PINCTRL_PULL_DOWN) ++ return -EINVAL; ++ arg = 1; /* hardware is weak pull-down */ ++ break; ++ ++ case PIN_CONFIG_BIAS_DISABLE: ++ if (val != SUN4I_PINCTRL_NO_PULL) ++ return -EINVAL; ++ arg = 0; ++ break; ++ ++ default: ++ /* sunxi_pconf_reg should catch anything unsupported */ ++ WARN_ON(1); ++ return -ENOTSUPP; ++ } ++ ++ *config = pinconf_to_config_packed(param, arg); ++ ++ return 0; ++} ++ + static int sunxi_pconf_group_get(struct pinctrl_dev *pctldev, + unsigned group, + unsigned long *config) + { + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); ++ struct sunxi_pinctrl_group *g = &pctl->groups[group]; + +- *config = pctl->groups[group].config; +- +- return 0; ++ /* We only support 1 pin per group. Chain it to the pin callback */ ++ return sunxi_pconf_get(pctldev, g->pin, config); + } + + static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, +@@ -508,8 +584,6 @@ static int sunxi_pconf_group_set(struct + default: + break; + } +- /* cache the config value */ +- g->config = configs[i]; + } /* for each config */ + + spin_unlock_irqrestore(&pctl->lock, flags); +@@ -518,6 +592,8 @@ static int sunxi_pconf_group_set(struct + } + + static const struct pinconf_ops sunxi_pconf_ops = { ++ .is_generic = true, ++ .pin_config_get = sunxi_pconf_get, + .pin_config_group_get = sunxi_pconf_group_get, + .pin_config_group_set = sunxi_pconf_group_set, + }; +--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h ++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h +@@ -109,7 +109,6 @@ struct sunxi_pinctrl_function { + + struct sunxi_pinctrl_group { + const char *name; +- unsigned long config; + unsigned pin; + }; + diff --git a/target/linux/sunxi/patches-4.9/0039-pinctrl-sunxi-Make-sunxi_pconf_group_set-use-sunxi_p.patch b/target/linux/sunxi/patches-4.9/0039-pinctrl-sunxi-Make-sunxi_pconf_group_set-use-sunxi_p.patch new file mode 100644 index 0000000000..7555933f63 --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0039-pinctrl-sunxi-Make-sunxi_pconf_group_set-use-sunxi_p.patch @@ -0,0 +1,122 @@ +From 51814827190214986c452a166718bf12d32211c7 Mon Sep 17 00:00:00 2001 +From: Chen-Yu Tsai +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 +Acked-by: Maxime Ripard +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/sunxi/pinctrl-sunxi.c | 64 +++++++++++++++++------------------ + 1 file changed, 32 insertions(+), 32 deletions(-) + +--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c ++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c +@@ -532,23 +532,27 @@ static int sunxi_pconf_group_set(struct + { + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + struct sunxi_pinctrl_group *g = &pctl->groups[group]; +- unsigned long flags; + unsigned pin = g->pin - pctl->desc->pin_base; +- u32 val, mask; +- u16 strength; +- u8 dlevel; + int i; + +- spin_lock_irqsave(&pctl->lock, flags); +- + for (i = 0; i < num_configs; i++) { +- switch (pinconf_to_config_param(configs[i])) { ++ enum pin_config_param param; ++ unsigned long flags; ++ u32 offset, shift, mask, reg; ++ u16 arg, val; ++ int ret; ++ ++ param = pinconf_to_config_param(configs[i]); ++ arg = pinconf_to_config_argument(configs[i]); ++ ++ ret = sunxi_pconf_reg(pin, param, &offset, &shift, &mask); ++ if (ret < 0) ++ return ret; ++ ++ switch (param) { + case PIN_CONFIG_DRIVE_STRENGTH: +- strength = pinconf_to_config_argument(configs[i]); +- if (strength > 40) { +- spin_unlock_irqrestore(&pctl->lock, flags); ++ if (arg < 10 || arg > 40) + return -EINVAL; +- } + /* + * We convert from mA to what the register expects: + * 0: 10mA +@@ -556,37 +560,33 @@ static int sunxi_pconf_group_set(struct + * 2: 30mA + * 3: 40mA + */ +- dlevel = strength / 10 - 1; +- val = readl(pctl->membase + sunxi_dlevel_reg(pin)); +- mask = DLEVEL_PINS_MASK << sunxi_dlevel_offset(pin); +- writel((val & ~mask) +- | dlevel << sunxi_dlevel_offset(pin), +- pctl->membase + sunxi_dlevel_reg(pin)); ++ val = arg / 10 - 1; + break; + case PIN_CONFIG_BIAS_DISABLE: +- val = readl(pctl->membase + sunxi_pull_reg(pin)); +- mask = PULL_PINS_MASK << sunxi_pull_offset(pin); +- writel((val & ~mask), +- pctl->membase + sunxi_pull_reg(pin)); ++ val = 0; + break; + case PIN_CONFIG_BIAS_PULL_UP: +- val = readl(pctl->membase + sunxi_pull_reg(pin)); +- mask = PULL_PINS_MASK << sunxi_pull_offset(pin); +- writel((val & ~mask) | 1 << sunxi_pull_offset(pin), +- pctl->membase + sunxi_pull_reg(pin)); ++ if (arg == 0) ++ return -EINVAL; ++ val = 1; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: +- val = readl(pctl->membase + sunxi_pull_reg(pin)); +- mask = PULL_PINS_MASK << sunxi_pull_offset(pin); +- writel((val & ~mask) | 2 << sunxi_pull_offset(pin), +- pctl->membase + sunxi_pull_reg(pin)); ++ if (arg == 0) ++ return -EINVAL; ++ val = 2; + break; + default: +- break; ++ /* sunxi_pconf_reg should catch anything unsupported */ ++ WARN_ON(1); ++ return -ENOTSUPP; + } +- } /* for each config */ + +- spin_unlock_irqrestore(&pctl->lock, flags); ++ spin_lock_irqsave(&pctl->lock, flags); ++ reg = readl(pctl->membase + offset); ++ reg &= ~(mask << shift); ++ writel(reg | val << shift, pctl->membase + offset); ++ spin_unlock_irqrestore(&pctl->lock, flags); ++ } /* for each config */ + + return 0; + } diff --git a/target/linux/sunxi/patches-4.9/0040-pinctrl-sunxi-Add-support-for-interrupt-debouncing.patch b/target/linux/sunxi/patches-4.9/0040-pinctrl-sunxi-Add-support-for-interrupt-debouncing.patch new file mode 100644 index 0000000000..01cbe31bef --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0040-pinctrl-sunxi-Add-support-for-interrupt-debouncing.patch @@ -0,0 +1,171 @@ +From 7c926492d38a3feef4b4b29c91b7c03eb1b8b546 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +Acked-by: Rob Herring +Signed-off-by: Linus Walleij +--- + .../bindings/pinctrl/allwinner,sunxi-pinctrl.txt | 14 ++++ + drivers/pinctrl/sunxi/pinctrl-sunxi.c | 84 ++++++++++++++++++++++ + drivers/pinctrl/sunxi/pinctrl-sunxi.h | 7 ++ + 3 files changed, 105 insertions(+) + +--- a/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt ++++ b/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt +@@ -28,6 +28,20 @@ Required properties: + - reg: Should contain the register physical address and length for the + pin controller. + ++- clocks: phandle to the clocks feeding the pin controller: ++ - "apb": the gated APB parent clock ++ - "hosc": the high frequency oscillator in the system ++ - "losc": the low frequency oscillator in the system ++ ++Note: For backward compatibility reasons, the hosc and losc clocks are only ++required if you need to use the optional input-debounce property. Any new ++device tree should set them. ++ ++Optional properties: ++ - input-debounce: Array of debouncing periods in microseconds. One period per ++ irq bank found in the controller. 0 if no setup required. ++ ++ + Please refer to pinctrl-bindings.txt in this directory for details of the + common pinctrl bindings used by client devices. + +--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c ++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c +@@ -1122,6 +1122,88 @@ static int sunxi_pinctrl_build_state(str + return 0; + } + ++static int sunxi_pinctrl_get_debounce_div(struct clk *clk, int freq, int *diff) ++{ ++ unsigned long clock = clk_get_rate(clk); ++ unsigned int best_diff = ~0, best_div; ++ int i; ++ ++ for (i = 0; i < 8; i++) { ++ int cur_diff = abs(freq - (clock >> i)); ++ ++ if (cur_diff < best_diff) { ++ best_diff = cur_diff; ++ best_div = i; ++ } ++ } ++ ++ *diff = best_diff; ++ return best_div; ++} ++ ++static int sunxi_pinctrl_setup_debounce(struct sunxi_pinctrl *pctl, ++ struct device_node *node) ++{ ++ unsigned int hosc_diff, losc_diff; ++ unsigned int hosc_div, losc_div; ++ struct clk *hosc, *losc; ++ u8 div, src; ++ int i, ret; ++ ++ /* Deal with old DTs that didn't have the oscillators */ ++ if (of_count_phandle_with_args(node, "clocks", "#clock-cells") != 3) ++ return 0; ++ ++ /* If we don't have any setup, bail out */ ++ if (!of_find_property(node, "input-debounce", NULL)) ++ return 0; ++ ++ losc = devm_clk_get(pctl->dev, "losc"); ++ if (IS_ERR(losc)) ++ return PTR_ERR(losc); ++ ++ hosc = devm_clk_get(pctl->dev, "hosc"); ++ if (IS_ERR(hosc)) ++ return PTR_ERR(hosc); ++ ++ for (i = 0; i < pctl->desc->irq_banks; i++) { ++ unsigned long debounce_freq; ++ u32 debounce; ++ ++ ret = of_property_read_u32_index(node, "input-debounce", ++ i, &debounce); ++ if (ret) ++ return ret; ++ ++ if (!debounce) ++ continue; ++ ++ debounce_freq = DIV_ROUND_CLOSEST(USEC_PER_SEC, debounce); ++ losc_div = sunxi_pinctrl_get_debounce_div(losc, ++ debounce_freq, ++ &losc_diff); ++ ++ hosc_div = sunxi_pinctrl_get_debounce_div(hosc, ++ debounce_freq, ++ &hosc_diff); ++ ++ if (hosc_diff < losc_diff) { ++ div = hosc_div; ++ src = 1; ++ } else { ++ div = losc_div; ++ src = 0; ++ } ++ ++ writel(src | div << 4, ++ pctl->membase + ++ sunxi_irq_debounce_reg_from_bank(i, ++ pctl->desc->irq_bank_base)); ++ } ++ ++ return 0; ++} ++ + int sunxi_pinctrl_init(struct platform_device *pdev, + const struct sunxi_pinctrl_desc *desc) + { +@@ -1284,6 +1366,8 @@ int sunxi_pinctrl_init(struct platform_d + pctl); + } + ++ sunxi_pinctrl_setup_debounce(pctl, node); ++ + dev_info(&pdev->dev, "initialized sunXi PIO driver\n"); + + return 0; +--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h ++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h +@@ -69,6 +69,8 @@ + #define IRQ_STATUS_IRQ_BITS 1 + #define IRQ_STATUS_IRQ_MASK ((1 << IRQ_STATUS_IRQ_BITS) - 1) + ++#define IRQ_DEBOUNCE_REG 0x218 ++ + #define IRQ_MEM_SIZE 0x20 + + #define IRQ_EDGE_RISING 0x00 +@@ -265,6 +267,11 @@ static inline u32 sunxi_irq_ctrl_offset( + return irq_num * IRQ_CTRL_IRQ_BITS; + } + ++static inline u32 sunxi_irq_debounce_reg_from_bank(u8 bank, unsigned bank_base) ++{ ++ return IRQ_DEBOUNCE_REG + (bank_base + bank) * IRQ_MEM_SIZE; ++} ++ + static inline u32 sunxi_irq_status_reg_from_bank(u8 bank, unsigned bank_base) + { + return IRQ_STATUS_REG + (bank_base + bank) * IRQ_MEM_SIZE; diff --git a/target/linux/sunxi/patches-4.9/0041-pinctrl-sunxi-fix-theoretical-uninitialized-variable.patch b/target/linux/sunxi/patches-4.9/0041-pinctrl-sunxi-fix-theoretical-uninitialized-variable.patch new file mode 100644 index 0000000000..69de015b67 --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0041-pinctrl-sunxi-fix-theoretical-uninitialized-variable.patch @@ -0,0 +1,40 @@ +From d8a22212737314cc02692cc90eda7d844fa20257 Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +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 +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/sunxi/pinctrl-sunxi.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c ++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c +@@ -1125,10 +1125,13 @@ static int sunxi_pinctrl_build_state(str + static int sunxi_pinctrl_get_debounce_div(struct clk *clk, int freq, int *diff) + { + unsigned long clock = clk_get_rate(clk); +- unsigned int best_diff = ~0, best_div; ++ unsigned int best_diff, best_div; + int i; + +- for (i = 0; i < 8; i++) { ++ best_diff = abs(freq - clock); ++ best_div = 0; ++ ++ for (i = 1; i < 8; i++) { + int cur_diff = abs(freq - (clock >> i)); + + if (cur_diff < best_diff) { diff --git a/target/linux/sunxi/patches-4.9/0042-pinctrl-sunxi-Testing-the-wrong-variable.patch b/target/linux/sunxi/patches-4.9/0042-pinctrl-sunxi-Testing-the-wrong-variable.patch new file mode 100644 index 0000000000..8ed4f27b4c --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0042-pinctrl-sunxi-Testing-the-wrong-variable.patch @@ -0,0 +1,35 @@ +From b3cde198b17f504643cc1eeffc4623f03326f436 Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +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 +Acked-by: Maxime Ripard +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/sunxi/pinctrl-sunxi.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c ++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c +@@ -398,13 +398,14 @@ static int sunxi_pctrl_dt_node_to_map(st + * map array + */ + *map = krealloc(*map, i * sizeof(struct pinctrl_map), GFP_KERNEL); +- if (!map) ++ if (!*map) + return -ENOMEM; + + return 0; + + err_free_map: +- kfree(map); ++ kfree(*map); ++ *map = NULL; + return ret; + } + diff --git a/target/linux/sunxi/patches-4.9/0043-pinctrl-sunxi-Don-t-enforce-bias-disable-for-now.patch b/target/linux/sunxi/patches-4.9/0043-pinctrl-sunxi-Don-t-enforce-bias-disable-for-now.patch new file mode 100644 index 0000000000..d6e639af52 --- /dev/null +++ b/target/linux/sunxi/patches-4.9/0043-pinctrl-sunxi-Don-t-enforce-bias-disable-for-now.patch @@ -0,0 +1,42 @@ +From 2154d94b40ea2a5de05245521371d0461bb0d669 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +Signed-off-by: Maxime Ripard +Acked-by: Chen-Yu Tsai +Signed-off-by: Linus Walleij +--- + 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;