From 763c5bd045b1098be444142403398f1414fd42fb Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Mon, 15 May 2017 00:30:34 +0800 Subject: clk: sunxi-ng: add support for DE2 CCU The "Display Engine 2.0" in Allwinner newer SoCs contains a clock management unit for its subunits, like the DE CCU in A80. Add a sunxi-ng style driver for it. Signed-off-by: Icenowy Zheng Signed-off-by: Maxime Ripard --- drivers/clk/sunxi-ng/Kconfig | 5 + drivers/clk/sunxi-ng/Makefile | 1 + drivers/clk/sunxi-ng/ccu-sun8i-de2.c | 260 +++++++++++++++++++++++++++++++++++ drivers/clk/sunxi-ng/ccu-sun8i-de2.h | 28 ++++ 4 files changed, 294 insertions(+) create mode 100644 drivers/clk/sunxi-ng/ccu-sun8i-de2.c create mode 100644 drivers/clk/sunxi-ng/ccu-sun8i-de2.h (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig index eb89c7801f00..b5706fc73f3e 100644 --- a/drivers/clk/sunxi-ng/Kconfig +++ b/drivers/clk/sunxi-ng/Kconfig @@ -140,6 +140,11 @@ config SUN8I_V3S_CCU default MACH_SUN8I depends on MACH_SUN8I || COMPILE_TEST +config SUN8I_DE2_CCU + bool "Support for the Allwinner SoCs DE2 CCU" + select SUNXI_CCU_DIV + select SUNXI_CCU_GATE + config SUN9I_A80_CCU bool "Support for the Allwinner A80 CCU" select SUNXI_CCU_DIV diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile index 0ec02fe14c50..be616279450e 100644 --- a/drivers/clk/sunxi-ng/Makefile +++ b/drivers/clk/sunxi-ng/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_SUN8I_A23_CCU) += ccu-sun8i-a23.o obj-$(CONFIG_SUN8I_A33_CCU) += ccu-sun8i-a33.o obj-$(CONFIG_SUN8I_H3_CCU) += ccu-sun8i-h3.o obj-$(CONFIG_SUN8I_V3S_CCU) += ccu-sun8i-v3s.o +obj-$(CONFIG_SUN8I_DE2_CCU) += ccu-sun8i-de2.o obj-$(CONFIG_SUN8I_R_CCU) += ccu-sun8i-r.o obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80.o obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80-de.o diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c new file mode 100644 index 000000000000..15aaa9c4a3af --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2017 Icenowy Zheng + * + * 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 +#include +#include + +#include "ccu_common.h" +#include "ccu_div.h" +#include "ccu_gate.h" +#include "ccu_reset.h" + +#include "ccu-sun8i-de2.h" + +static SUNXI_CCU_GATE(bus_mixer0_clk, "bus-mixer0", "bus-de", + 0x04, BIT(0), 0); +static SUNXI_CCU_GATE(bus_mixer1_clk, "bus-mixer1", "bus-de", + 0x04, BIT(1), 0); +static SUNXI_CCU_GATE(bus_wb_clk, "bus-wb", "bus-de", + 0x04, BIT(2), 0); + +static SUNXI_CCU_GATE(mixer0_clk, "mixer0", "mixer0-div", + 0x00, BIT(0), CLK_SET_RATE_PARENT); +static SUNXI_CCU_GATE(mixer1_clk, "mixer1", "mixer1-div", + 0x00, BIT(1), CLK_SET_RATE_PARENT); +static SUNXI_CCU_GATE(wb_clk, "wb", "wb-div", + 0x00, BIT(2), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M(mixer0_div_clk, "mixer0-div", "de", 0x0c, 0, 4, + CLK_SET_RATE_PARENT); +static SUNXI_CCU_M(mixer1_div_clk, "mixer1-div", "de", 0x0c, 4, 4, + CLK_SET_RATE_PARENT); +static SUNXI_CCU_M(wb_div_clk, "wb-div", "de", 0x0c, 8, 4, + CLK_SET_RATE_PARENT); + +static struct ccu_common *sun8i_a83t_de2_clks[] = { + &mixer0_clk.common, + &mixer1_clk.common, + &wb_clk.common, + + &bus_mixer0_clk.common, + &bus_mixer1_clk.common, + &bus_wb_clk.common, + + &mixer0_div_clk.common, + &mixer1_div_clk.common, + &wb_div_clk.common, +}; + +static struct ccu_common *sun8i_v3s_de2_clks[] = { + &mixer0_clk.common, + &wb_clk.common, + + &bus_mixer0_clk.common, + &bus_wb_clk.common, + + &mixer0_div_clk.common, + &wb_div_clk.common, +}; + +static struct clk_hw_onecell_data sun8i_a83t_de2_hw_clks = { + .hws = { + [CLK_MIXER0] = &mixer0_clk.common.hw, + [CLK_MIXER1] = &mixer1_clk.common.hw, + [CLK_WB] = &wb_clk.common.hw, + + [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw, + [CLK_BUS_MIXER1] = &bus_mixer1_clk.common.hw, + [CLK_BUS_WB] = &bus_wb_clk.common.hw, + + [CLK_MIXER0_DIV] = &mixer0_div_clk.common.hw, + [CLK_MIXER1_DIV] = &mixer1_div_clk.common.hw, + [CLK_WB_DIV] = &wb_div_clk.common.hw, + }, + .num = CLK_NUMBER, +}; + +static struct clk_hw_onecell_data sun8i_v3s_de2_hw_clks = { + .hws = { + [CLK_MIXER0] = &mixer0_clk.common.hw, + [CLK_WB] = &wb_clk.common.hw, + + [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw, + [CLK_BUS_WB] = &bus_wb_clk.common.hw, + + [CLK_MIXER0_DIV] = &mixer0_div_clk.common.hw, + [CLK_WB_DIV] = &wb_div_clk.common.hw, + }, + .num = CLK_NUMBER, +}; + +static struct ccu_reset_map sun8i_a83t_de2_resets[] = { + [RST_MIXER0] = { 0x08, BIT(0) }, + /* + * For A83T, H3 and R40, mixer1 reset line is shared with wb, so + * only RST_WB is exported here. + * For V3s there's just no mixer1, so it also shares this struct. + */ + [RST_WB] = { 0x08, BIT(2) }, +}; + +static struct ccu_reset_map sun50i_a64_de2_resets[] = { + [RST_MIXER0] = { 0x08, BIT(0) }, + [RST_MIXER1] = { 0x08, BIT(1) }, + [RST_WB] = { 0x08, BIT(2) }, +}; + +static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc = { + .ccu_clks = sun8i_a83t_de2_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_a83t_de2_clks), + + .hw_clks = &sun8i_a83t_de2_hw_clks, + + .resets = sun8i_a83t_de2_resets, + .num_resets = ARRAY_SIZE(sun8i_a83t_de2_resets), +}; + +static const struct sunxi_ccu_desc sun50i_a64_de2_clk_desc = { + .ccu_clks = sun8i_a83t_de2_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_a83t_de2_clks), + + .hw_clks = &sun8i_a83t_de2_hw_clks, + + .resets = sun50i_a64_de2_resets, + .num_resets = ARRAY_SIZE(sun50i_a64_de2_resets), +}; + +static const struct sunxi_ccu_desc sun8i_v3s_de2_clk_desc = { + .ccu_clks = sun8i_v3s_de2_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_v3s_de2_clks), + + .hw_clks = &sun8i_v3s_de2_hw_clks, + + .resets = sun8i_a83t_de2_resets, + .num_resets = ARRAY_SIZE(sun8i_a83t_de2_resets), +}; + +static int sunxi_de2_clk_probe(struct platform_device *pdev) +{ + struct resource *res; + struct clk *bus_clk, *mod_clk; + struct reset_control *rstc; + void __iomem *reg; + const struct sunxi_ccu_desc *ccu_desc; + int ret; + + ccu_desc = of_device_get_match_data(&pdev->dev); + if (!ccu_desc) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + bus_clk = devm_clk_get(&pdev->dev, "bus"); + if (IS_ERR(bus_clk)) { + ret = PTR_ERR(bus_clk); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Couldn't get bus clk: %d\n", ret); + return ret; + } + + mod_clk = devm_clk_get(&pdev->dev, "mod"); + if (IS_ERR(mod_clk)) { + ret = PTR_ERR(mod_clk); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Couldn't get mod clk: %d\n", ret); + return ret; + } + + rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(rstc)) { + ret = PTR_ERR(bus_clk); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "Couldn't get reset control: %d\n", ret); + return ret; + } + + /* The clocks need to be enabled for us to access the registers */ + ret = clk_prepare_enable(bus_clk); + if (ret) { + dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(mod_clk); + if (ret) { + dev_err(&pdev->dev, "Couldn't enable mod clk: %d\n", ret); + goto err_disable_bus_clk; + } + + /* The reset control needs to be asserted for the controls to work */ + ret = reset_control_deassert(rstc); + if (ret) { + dev_err(&pdev->dev, + "Couldn't deassert reset control: %d\n", ret); + goto err_disable_mod_clk; + } + + ret = sunxi_ccu_probe(pdev->dev.of_node, reg, ccu_desc); + if (ret) + goto err_assert_reset; + + return 0; + +err_assert_reset: + reset_control_assert(rstc); +err_disable_mod_clk: + clk_disable_unprepare(mod_clk); +err_disable_bus_clk: + clk_disable_unprepare(bus_clk); + return ret; +} + +static const struct of_device_id sunxi_de2_clk_ids[] = { + { + .compatible = "allwinner,sun8i-a83t-de2-clk", + .data = &sun8i_a83t_de2_clk_desc, + }, + { + .compatible = "allwinner,sun8i-v3s-de2-clk", + .data = &sun8i_v3s_de2_clk_desc, + }, + { + .compatible = "allwinner,sun50i-h5-de2-clk", + .data = &sun50i_a64_de2_clk_desc, + }, + /* + * The Allwinner A64 SoC needs some bit to be poke in syscon to make + * DE2 really working. + * So there's currently no A64 compatible here. + * H5 shares the same reset line with A64, so here H5 is using the + * clock description of A64. + */ + { } +}; + +static struct platform_driver sunxi_de2_clk_driver = { + .probe = sunxi_de2_clk_probe, + .driver = { + .name = "sunxi-de2-clks", + .of_match_table = sunxi_de2_clk_ids, + }, +}; +builtin_platform_driver(sunxi_de2_clk_driver); diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-de2.h b/drivers/clk/sunxi-ng/ccu-sun8i-de2.h new file mode 100644 index 000000000000..530c006e0ae9 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun8i-de2.h @@ -0,0 +1,28 @@ +/* + * Copyright 2016 Icenowy Zheng + * + * 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_SUN8I_DE2_H_ +#define _CCU_SUN8I_DE2_H_ + +#include +#include + +/* Intermediary clock dividers are not exported */ +#define CLK_MIXER0_DIV 3 +#define CLK_MIXER1_DIV 4 +#define CLK_WB_DIV 5 + +#define CLK_NUMBER (CLK_WB + 1) + +#endif /* _CCU_SUN8I_DE2_H_ */ -- cgit v1.2.1 From b042e42feec495dd199525d3f88ffb323e5ec199 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Mon, 15 May 2017 12:23:07 +0200 Subject: clk: sunxi-ng: explicitly include linux/spinlock.h ccu_reset.h and ccu_reset.c use spinlock_t and associated functions but rely on implict inclusion of linux/spinlock.h which means that changes in other headers could break the build. Thus, add an explicit include. Signed-off-by: Tobias Klauser Acked-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi-ng/ccu_reset.h | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi-ng/ccu_reset.h b/drivers/clk/sunxi-ng/ccu_reset.h index 36a4679210bd..ff8f5ebca435 100644 --- a/drivers/clk/sunxi-ng/ccu_reset.h +++ b/drivers/clk/sunxi-ng/ccu_reset.h @@ -15,6 +15,7 @@ #define _CCU_RESET_H_ #include +#include struct ccu_reset_map { u16 reg; -- cgit v1.2.1 From 22833a9165a1c72a54ddc696a3765bd6f87fbb92 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 17 May 2017 09:40:30 +0200 Subject: clk: divider: Make divider_round_rate take the parent clock So far, divider_round_rate only considers the parent clock returned by clk_hw_get_parent. This works fine on clocks that have a single parents, this doesn't work on muxes, since we will only consider the first parent, while other parents may totally be able to provide a better combination. Clocks in that case cannot use divider_round_rate, so would have to come up with a very similar logic to work around it. Instead of having to do something like this, and duplicate that logic everywhere, create a divider_round_rate parent to allow caller to give an additional parameter for the parent clock to consider. Reviewed-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard Acked-by: Stephen Boyd Signed-off-by: Chen-Yu Tsai --- drivers/clk/clk-divider.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 96386ffc8483..9bb472cccca6 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -275,7 +275,8 @@ static int _next_div(const struct clk_div_table *table, int div, return div; } -static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, +static int clk_divider_bestdiv(struct clk_hw *hw, struct clk_hw *parent, + unsigned long rate, unsigned long *best_parent_rate, const struct clk_div_table *table, u8 width, unsigned long flags) @@ -314,8 +315,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, *best_parent_rate = parent_rate_saved; return i; } - parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), - rate * i); + parent_rate = clk_hw_round_rate(parent, rate * i); now = DIV_ROUND_UP_ULL((u64)parent_rate, i); if (_is_best_div(rate, now, best, flags)) { bestdiv = i; @@ -326,23 +326,24 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, if (!bestdiv) { bestdiv = _get_maxdiv(table, width, flags); - *best_parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), 1); + *best_parent_rate = clk_hw_round_rate(parent, 1); } return bestdiv; } -long divider_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate, const struct clk_div_table *table, - u8 width, unsigned long flags) +long divider_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent, + unsigned long rate, unsigned long *prate, + const struct clk_div_table *table, + u8 width, unsigned long flags) { int div; - div = clk_divider_bestdiv(hw, rate, prate, table, width, flags); + div = clk_divider_bestdiv(hw, parent, rate, prate, table, width, flags); return DIV_ROUND_UP_ULL((u64)*prate, div); } -EXPORT_SYMBOL_GPL(divider_round_rate); +EXPORT_SYMBOL_GPL(divider_round_rate_parent); static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) -- cgit v1.2.1 From 10a8d9b90642da9b6cef477725c4c6bdd4c36cb3 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 17 May 2017 09:40:31 +0200 Subject: clk: sunxi-ng: Pass the parent and a pointer to the clocks round rate The clocks might need to modify their parent clocks. In order to make that possible, give them access to the parent clock being evaluated, and to a pointer to the parent rate so that they can modify it if needed. Signed-off-by: Maxime Ripard Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu_div.c | 7 ++++--- drivers/clk/sunxi-ng/ccu_mp.c | 7 ++++--- drivers/clk/sunxi-ng/ccu_mult.c | 11 ++++++----- drivers/clk/sunxi-ng/ccu_mux.c | 8 +++++--- drivers/clk/sunxi-ng/ccu_mux.h | 3 ++- drivers/clk/sunxi-ng/ccu_nkm.c | 7 ++++--- 6 files changed, 25 insertions(+), 18 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c index 4057e6021aa9..a489f18a3c01 100644 --- a/drivers/clk/sunxi-ng/ccu_div.c +++ b/drivers/clk/sunxi-ng/ccu_div.c @@ -14,7 +14,8 @@ #include "ccu_div.h" static unsigned long ccu_div_round_rate(struct ccu_mux_internal *mux, - unsigned long parent_rate, + struct clk_hw *parent, + unsigned long *parent_rate, unsigned long rate, void *data) { @@ -26,10 +27,10 @@ static unsigned long ccu_div_round_rate(struct ccu_mux_internal *mux, * several parents, while we might be called to evaluate * several different parents. */ - val = divider_get_val(rate, parent_rate, cd->div.table, cd->div.width, + val = divider_get_val(rate, *parent_rate, cd->div.table, cd->div.width, cd->div.flags); - return divider_recalc_rate(&cd->common.hw, parent_rate, val, + return divider_recalc_rate(&cd->common.hw, *parent_rate, val, cd->div.table, cd->div.flags); } diff --git a/drivers/clk/sunxi-ng/ccu_mp.c b/drivers/clk/sunxi-ng/ccu_mp.c index b583f186a804..de02e6c386d8 100644 --- a/drivers/clk/sunxi-ng/ccu_mp.c +++ b/drivers/clk/sunxi-ng/ccu_mp.c @@ -41,7 +41,8 @@ static void ccu_mp_find_best(unsigned long parent, unsigned long rate, } static unsigned long ccu_mp_round_rate(struct ccu_mux_internal *mux, - unsigned long parent_rate, + struct clk_hw *hw, + unsigned long *parent_rate, unsigned long rate, void *data) { @@ -52,9 +53,9 @@ static unsigned long ccu_mp_round_rate(struct ccu_mux_internal *mux, max_m = cmp->m.max ?: 1 << cmp->m.width; max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1); - ccu_mp_find_best(parent_rate, rate, max_m, max_p, &m, &p); + ccu_mp_find_best(*parent_rate, rate, max_m, max_p, &m, &p); - return parent_rate / p / m; + return *parent_rate / p / m; } static void ccu_mp_disable(struct clk_hw *hw) diff --git a/drivers/clk/sunxi-ng/ccu_mult.c b/drivers/clk/sunxi-ng/ccu_mult.c index 671141359895..6ee7ba0738fb 100644 --- a/drivers/clk/sunxi-ng/ccu_mult.c +++ b/drivers/clk/sunxi-ng/ccu_mult.c @@ -33,9 +33,10 @@ static void ccu_mult_find_best(unsigned long parent, unsigned long rate, } static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux, - unsigned long parent_rate, - unsigned long rate, - void *data) + struct clk_hw *parent, + unsigned long *parent_rate, + unsigned long rate, + void *data) { struct ccu_mult *cm = data; struct _ccu_mult _cm; @@ -47,9 +48,9 @@ static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux, else _cm.max = (1 << cm->mult.width) + cm->mult.offset - 1; - ccu_mult_find_best(parent_rate, rate, &_cm); + ccu_mult_find_best(*parent_rate, rate, &_cm); - return parent_rate * _cm.mult; + return *parent_rate * _cm.mult; } static void ccu_mult_disable(struct clk_hw *hw) diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c index c6bb1f523232..bae735e252b6 100644 --- a/drivers/clk/sunxi-ng/ccu_mux.c +++ b/drivers/clk/sunxi-ng/ccu_mux.c @@ -61,7 +61,8 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common, struct ccu_mux_internal *cm, struct clk_rate_request *req, unsigned long (*round)(struct ccu_mux_internal *, - unsigned long, + struct clk_hw *, + unsigned long *, unsigned long, void *), void *data) @@ -80,7 +81,8 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common, ccu_mux_helper_adjust_parent_for_prediv(common, cm, -1, &adj_parent_rate); - best_rate = round(cm, adj_parent_rate, req->rate, data); + best_rate = round(cm, best_parent, &adj_parent_rate, + req->rate, data); goto out; } @@ -109,7 +111,7 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common, ccu_mux_helper_adjust_parent_for_prediv(common, cm, i, &adj_parent_rate); - tmp_rate = round(cm, adj_parent_rate, req->rate, data); + tmp_rate = round(cm, parent, &adj_parent_rate, req->rate, data); if (tmp_rate == req->rate) { best_parent = parent; best_parent_rate = parent_rate; diff --git a/drivers/clk/sunxi-ng/ccu_mux.h b/drivers/clk/sunxi-ng/ccu_mux.h index 47aba3a48245..4be56eee2bfd 100644 --- a/drivers/clk/sunxi-ng/ccu_mux.h +++ b/drivers/clk/sunxi-ng/ccu_mux.h @@ -86,7 +86,8 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common, struct ccu_mux_internal *cm, struct clk_rate_request *req, unsigned long (*round)(struct ccu_mux_internal *, - unsigned long, + struct clk_hw *, + unsigned long *, unsigned long, void *), void *data); diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c index cba84afe1cf1..44b16dc8fea6 100644 --- a/drivers/clk/sunxi-ng/ccu_nkm.c +++ b/drivers/clk/sunxi-ng/ccu_nkm.c @@ -102,7 +102,8 @@ static unsigned long ccu_nkm_recalc_rate(struct clk_hw *hw, } static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux, - unsigned long parent_rate, + struct clk_hw *hw, + unsigned long *parent_rate, unsigned long rate, void *data) { @@ -116,9 +117,9 @@ static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux, _nkm.min_m = 1; _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width; - ccu_nkm_find_best(parent_rate, rate, &_nkm); + ccu_nkm_find_best(*parent_rate, rate, &_nkm); - return parent_rate * _nkm.n * _nkm.k / _nkm.m; + return *parent_rate * _nkm.n * _nkm.k / _nkm.m; } static int ccu_nkm_determine_rate(struct clk_hw *hw, -- cgit v1.2.1 From e69b2afa876d246feff724dfc7b0b58aec360633 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 17 May 2017 09:40:32 +0200 Subject: clk: sunxi-ng: div: Switch to divider_round_rate divider_round_rate_parent already evaluates changing the parent rate if CLK_SET_RATE_PARENT is set. Now that we can do that on muxes too, let's just use it. Signed-off-by: Maxime Ripard Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu_div.c | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c index a489f18a3c01..419463375bc1 100644 --- a/drivers/clk/sunxi-ng/ccu_div.c +++ b/drivers/clk/sunxi-ng/ccu_div.c @@ -20,18 +20,11 @@ static unsigned long ccu_div_round_rate(struct ccu_mux_internal *mux, void *data) { struct ccu_div *cd = data; - unsigned long val; - - /* - * We can't use divider_round_rate that assumes that there's - * several parents, while we might be called to evaluate - * several different parents. - */ - val = divider_get_val(rate, *parent_rate, cd->div.table, cd->div.width, - cd->div.flags); - return divider_recalc_rate(&cd->common.hw, *parent_rate, val, - cd->div.table, cd->div.flags); + return divider_round_rate_parent(&cd->common.hw, parent, + rate, parent_rate, + cd->div.table, cd->div.width, + cd->div.flags); } static void ccu_div_disable(struct clk_hw *hw) @@ -78,18 +71,6 @@ static int ccu_div_determine_rate(struct clk_hw *hw, { struct ccu_div *cd = hw_to_ccu_div(hw); - if (clk_hw_get_num_parents(hw) == 1) { - req->rate = divider_round_rate(hw, req->rate, - &req->best_parent_rate, - cd->div.table, - cd->div.width, - cd->div.flags); - - req->best_parent_hw = clk_hw_get_parent(hw); - - return 0; - } - return ccu_mux_helper_determine_rate(&cd->common, &cd->mux, req, ccu_div_round_rate, cd); } -- cgit v1.2.1 From 73e3e04fc009973d55a523ece1a7261ef7a089be Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 17 May 2017 09:40:33 +0200 Subject: clk: sunxi-ng: mux: Don't just rely on the parent for CLK_SET_RATE_PARENT The current code only rely on the parent to change its rate in the case where CLK_SET_RATE_PARENT is set. However, some clock rates might be obtained only through a modification of the parent and the clock divider. Just rely on the round rate of the clocks to give us the best computation that might be achieved for a given rate. round_rate functions now need to honor CLK_SET_RATE_PARENT, but either the functions already do that if they modify the parent, or don't modify the praents at all. Signed-off-by: Maxime Ripard Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu_mux.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c index bae735e252b6..58b6e349a0ed 100644 --- a/drivers/clk/sunxi-ng/ccu_mux.c +++ b/drivers/clk/sunxi-ng/ccu_mux.c @@ -95,19 +95,7 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common, if (!parent) continue; - if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) { - struct clk_rate_request parent_req = *req; - int ret = __clk_determine_rate(parent, &parent_req); - - if (ret) - continue; - - parent_rate = parent_req.rate; - } else { - parent_rate = clk_hw_get_rate(parent); - } - - adj_parent_rate = parent_rate; + adj_parent_rate = parent_rate = clk_hw_get_rate(parent); ccu_mux_helper_adjust_parent_for_prediv(common, cm, i, &adj_parent_rate); -- cgit v1.2.1 From ea8edcded581ed9bd935ef7c575b0bf58eb56c82 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 17 May 2017 09:40:34 +0200 Subject: clk: sunxi-ng: mux: split out the pre-divider computation code The pre-divider retrieval code was merged into the function to apply the current pre-divider onto the parent clock rate so that we can use that adjusted value to do our factors computation. However, since we'll need to do the reverse operation, we need to split out that code into a function that will be shared. Signed-off-by: Maxime Ripard Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu_mux.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c index 58b6e349a0ed..3eb23d4e6534 100644 --- a/drivers/clk/sunxi-ng/ccu_mux.c +++ b/drivers/clk/sunxi-ng/ccu_mux.c @@ -15,24 +15,20 @@ #include "ccu_gate.h" #include "ccu_mux.h" -void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common, - struct ccu_mux_internal *cm, - int parent_index, - unsigned long *parent_rate) +static u16 ccu_mux_get_prediv(struct ccu_common *common, + struct ccu_mux_internal *cm, + int parent_index) { u16 prediv = 1; u32 reg; - int i; if (!((common->features & CCU_FEATURE_FIXED_PREDIV) || (common->features & CCU_FEATURE_VARIABLE_PREDIV) || (common->features & CCU_FEATURE_ALL_PREDIV))) - return; + return 1; - if (common->features & CCU_FEATURE_ALL_PREDIV) { - *parent_rate = *parent_rate / common->prediv; - return; - } + if (common->features & CCU_FEATURE_ALL_PREDIV) + return common->prediv; reg = readl(common->base + common->reg); if (parent_index < 0) { @@ -40,10 +36,13 @@ void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common, parent_index &= (1 << cm->width) - 1; } - if (common->features & CCU_FEATURE_FIXED_PREDIV) + if (common->features & CCU_FEATURE_FIXED_PREDIV) { + int i; + for (i = 0; i < cm->n_predivs; i++) if (parent_index == cm->fixed_predivs[i].index) prediv = cm->fixed_predivs[i].div; + } if (common->features & CCU_FEATURE_VARIABLE_PREDIV) if (parent_index == cm->variable_prediv.index) { @@ -54,7 +53,16 @@ void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common, prediv = div + 1; } - *parent_rate = *parent_rate / prediv; + return prediv; +} + +void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common, + struct ccu_mux_internal *cm, + int parent_index, + unsigned long *parent_rate) +{ + *parent_rate = *parent_rate / ccu_mux_get_prediv(common, cm, + parent_index); } int ccu_mux_helper_determine_rate(struct ccu_common *common, -- cgit v1.2.1 From d754b15951ffe3c85e2581eaa244ed4a82080970 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 17 May 2017 09:40:35 +0200 Subject: clk: sunxi-ng: mux: Change pre-divider application function prototype The current function name is a bit confusing, and doesn't really allow to create an explicit function to reverse the operation. We also for now change the parent rate through a pointer, while we don't return anything. In order to be less confusing, and easier to use for downstream users, change the function name to something hopefully clearer, and return the adjusted rate instead of changing the pointer. Signed-off-by: Maxime Ripard Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu_div.c | 8 ++++---- drivers/clk/sunxi-ng/ccu_mp.c | 8 ++++---- drivers/clk/sunxi-ng/ccu_mult.c | 8 ++++---- drivers/clk/sunxi-ng/ccu_mux.c | 29 ++++++++++++----------------- drivers/clk/sunxi-ng/ccu_mux.h | 8 ++++---- 5 files changed, 28 insertions(+), 33 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c index 419463375bc1..c0e5c10d0091 100644 --- a/drivers/clk/sunxi-ng/ccu_div.c +++ b/drivers/clk/sunxi-ng/ccu_div.c @@ -59,8 +59,8 @@ static unsigned long ccu_div_recalc_rate(struct clk_hw *hw, val = reg >> cd->div.shift; val &= (1 << cd->div.width) - 1; - ccu_mux_helper_adjust_parent_for_prediv(&cd->common, &cd->mux, -1, - &parent_rate); + parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1, + parent_rate); return divider_recalc_rate(hw, parent_rate, val, cd->div.table, cd->div.flags); @@ -83,8 +83,8 @@ static int ccu_div_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long val; u32 reg; - ccu_mux_helper_adjust_parent_for_prediv(&cd->common, &cd->mux, -1, - &parent_rate); + parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1, + parent_rate); val = divider_get_val(rate, parent_rate, cd->div.table, cd->div.width, cd->div.flags); diff --git a/drivers/clk/sunxi-ng/ccu_mp.c b/drivers/clk/sunxi-ng/ccu_mp.c index de02e6c386d8..b917ad7a386c 100644 --- a/drivers/clk/sunxi-ng/ccu_mp.c +++ b/drivers/clk/sunxi-ng/ccu_mp.c @@ -87,8 +87,8 @@ static unsigned long ccu_mp_recalc_rate(struct clk_hw *hw, u32 reg; /* Adjust parent_rate according to pre-dividers */ - ccu_mux_helper_adjust_parent_for_prediv(&cmp->common, &cmp->mux, - -1, &parent_rate); + parent_rate = ccu_mux_helper_apply_prediv(&cmp->common, &cmp->mux, -1, + parent_rate); reg = readl(cmp->common.base + cmp->common.reg); @@ -123,8 +123,8 @@ static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate, u32 reg; /* Adjust parent_rate according to pre-dividers */ - ccu_mux_helper_adjust_parent_for_prediv(&cmp->common, &cmp->mux, - -1, &parent_rate); + parent_rate = ccu_mux_helper_apply_prediv(&cmp->common, &cmp->mux, -1, + parent_rate); max_m = cmp->m.max ?: 1 << cmp->m.width; max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1); diff --git a/drivers/clk/sunxi-ng/ccu_mult.c b/drivers/clk/sunxi-ng/ccu_mult.c index 6ee7ba0738fb..20d0300867f2 100644 --- a/drivers/clk/sunxi-ng/ccu_mult.c +++ b/drivers/clk/sunxi-ng/ccu_mult.c @@ -88,8 +88,8 @@ static unsigned long ccu_mult_recalc_rate(struct clk_hw *hw, val = reg >> cm->mult.shift; val &= (1 << cm->mult.width) - 1; - ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1, - &parent_rate); + parent_rate = ccu_mux_helper_apply_prediv(&cm->common, &cm->mux, -1, + parent_rate); return parent_rate * (val + cm->mult.offset); } @@ -116,8 +116,8 @@ static int ccu_mult_set_rate(struct clk_hw *hw, unsigned long rate, else ccu_frac_helper_disable(&cm->common, &cm->frac); - ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1, - &parent_rate); + parent_rate = ccu_mux_helper_apply_prediv(&cm->common, &cm->mux, -1, + parent_rate); _cm.min = cm->mult.min; diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c index 3eb23d4e6534..c33210972581 100644 --- a/drivers/clk/sunxi-ng/ccu_mux.c +++ b/drivers/clk/sunxi-ng/ccu_mux.c @@ -56,13 +56,12 @@ static u16 ccu_mux_get_prediv(struct ccu_common *common, return prediv; } -void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common, - struct ccu_mux_internal *cm, - int parent_index, - unsigned long *parent_rate) +unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common, + struct ccu_mux_internal *cm, + int parent_index, + unsigned long parent_rate) { - *parent_rate = *parent_rate / ccu_mux_get_prediv(common, cm, - parent_index); + return parent_rate / ccu_mux_get_prediv(common, cm, parent_index); } int ccu_mux_helper_determine_rate(struct ccu_common *common, @@ -84,10 +83,8 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common, best_parent = clk_hw_get_parent(hw); best_parent_rate = clk_hw_get_rate(best_parent); - - adj_parent_rate = best_parent_rate; - ccu_mux_helper_adjust_parent_for_prediv(common, cm, -1, - &adj_parent_rate); + adj_parent_rate = ccu_mux_helper_apply_prediv(common, cm, -1, + best_parent_rate); best_rate = round(cm, best_parent, &adj_parent_rate, req->rate, data); @@ -103,9 +100,9 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common, if (!parent) continue; - adj_parent_rate = parent_rate = clk_hw_get_rate(parent); - ccu_mux_helper_adjust_parent_for_prediv(common, cm, i, - &adj_parent_rate); + parent_rate = clk_hw_get_rate(parent); + adj_parent_rate = ccu_mux_helper_apply_prediv(common, cm, i, + parent_rate); tmp_rate = round(cm, parent, &adj_parent_rate, req->rate, data); if (tmp_rate == req->rate) { @@ -215,10 +212,8 @@ static unsigned long ccu_mux_recalc_rate(struct clk_hw *hw, { struct ccu_mux *cm = hw_to_ccu_mux(hw); - ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1, - &parent_rate); - - return parent_rate; + return ccu_mux_helper_apply_prediv(&cm->common, &cm->mux, -1, + parent_rate); } const struct clk_ops ccu_mux_ops = { diff --git a/drivers/clk/sunxi-ng/ccu_mux.h b/drivers/clk/sunxi-ng/ccu_mux.h index 4be56eee2bfd..dba12c76cf54 100644 --- a/drivers/clk/sunxi-ng/ccu_mux.h +++ b/drivers/clk/sunxi-ng/ccu_mux.h @@ -78,10 +78,10 @@ static inline struct ccu_mux *hw_to_ccu_mux(struct clk_hw *hw) extern const struct clk_ops ccu_mux_ops; -void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common, - struct ccu_mux_internal *cm, - int parent_index, - unsigned long *parent_rate); +unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common, + struct ccu_mux_internal *cm, + int parent_index, + unsigned long parent_rate); int ccu_mux_helper_determine_rate(struct ccu_common *common, struct ccu_mux_internal *cm, struct clk_rate_request *req, -- cgit v1.2.1 From abea24218aa9e40e504d89ac1b7c6b4588a6cd9b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 17 May 2017 09:40:36 +0200 Subject: clk: sunxi-ng: mux: Re-adjust parent rate Currently, the parent rate given back to the clock framework in our request is the original parent rate we calculated before trying to round the rate of our clock. This works fine unless our clock also changes its parent rate, in which case we will simply ignore that change and still use the previous parent rate. Create a new function to re-adjust the parent rate to take the pre-dividers into account, and give that back to the clock framework. Signed-off-by: Maxime Ripard Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu_mux.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c index c33210972581..748b172f9193 100644 --- a/drivers/clk/sunxi-ng/ccu_mux.c +++ b/drivers/clk/sunxi-ng/ccu_mux.c @@ -64,6 +64,14 @@ unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common, return parent_rate / ccu_mux_get_prediv(common, cm, parent_index); } +unsigned long ccu_mux_helper_unapply_prediv(struct ccu_common *common, + struct ccu_mux_internal *cm, + int parent_index, + unsigned long parent_rate) +{ + return parent_rate * ccu_mux_get_prediv(common, cm, parent_index); +} + int ccu_mux_helper_determine_rate(struct ccu_common *common, struct ccu_mux_internal *cm, struct clk_rate_request *req, @@ -89,22 +97,37 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common, best_rate = round(cm, best_parent, &adj_parent_rate, req->rate, data); + /* + * adj_parent_rate might have been modified by our clock. + * Unapply the pre-divider if there's one, and give + * the actual frequency the parent needs to run at. + */ + best_parent_rate = ccu_mux_helper_unapply_prediv(common, cm, -1, + adj_parent_rate); + goto out; } for (i = 0; i < clk_hw_get_num_parents(hw); i++) { - unsigned long tmp_rate, parent_rate, adj_parent_rate; + unsigned long tmp_rate, parent_rate; struct clk_hw *parent; parent = clk_hw_get_parent_by_index(hw, i); if (!parent) continue; - parent_rate = clk_hw_get_rate(parent); - adj_parent_rate = ccu_mux_helper_apply_prediv(common, cm, i, - parent_rate); + parent_rate = ccu_mux_helper_apply_prediv(common, cm, i, + clk_hw_get_rate(parent)); + + tmp_rate = round(cm, parent, &parent_rate, req->rate, data); - tmp_rate = round(cm, parent, &adj_parent_rate, req->rate, data); + /* + * parent_rate might have been modified by our clock. + * Unapply the pre-divider if there's one, and give + * the actual frequency the parent needs to run at. + */ + parent_rate = ccu_mux_helper_unapply_prediv(common, cm, i, + parent_rate); if (tmp_rate == req->rate) { best_parent = parent; best_parent_rate = parent_rate; -- cgit v1.2.1 From 0adad031ef5d0d89ee92d92964d3799685ea2387 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 17 May 2017 09:40:37 +0200 Subject: clk: sunxi-ng: sun5i: Export video PLLs The video PLLs are used directly by the HDMI controller. Export them so that we can use them in our DT node. Signed-off-by: Maxime Ripard Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu-sun5i.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi-ng/ccu-sun5i.h b/drivers/clk/sunxi-ng/ccu-sun5i.h index 8144487eb7ca..93a275fbd9a9 100644 --- a/drivers/clk/sunxi-ng/ccu-sun5i.h +++ b/drivers/clk/sunxi-ng/ccu-sun5i.h @@ -28,15 +28,17 @@ #define CLK_PLL_AUDIO_4X 6 #define CLK_PLL_AUDIO_8X 7 #define CLK_PLL_VIDEO0 8 -#define CLK_PLL_VIDEO0_2X 9 + +/* The PLL_VIDEO0_2X is exported for HDMI */ + #define CLK_PLL_VE 10 #define CLK_PLL_DDR_BASE 11 #define CLK_PLL_DDR 12 #define CLK_PLL_DDR_OTHER 13 #define CLK_PLL_PERIPH 14 #define CLK_PLL_VIDEO1 15 -#define CLK_PLL_VIDEO1_2X 16 +/* The PLL_VIDEO1_2X is exported for HDMI */ /* The CPU clock is exported */ #define CLK_AXI 18 -- cgit v1.2.1 From 1f6d640cad6e5885e175ed359b87f615a3448e8f Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 18 May 2017 15:55:13 +0000 Subject: clk: sunxi-ng: de2: fix wrong pointer passed to PTR_ERR() PTR_ERR should access the value just tested by IS_ERR, otherwise the wrong error code will be returned. Fixes: b0d9a4bd52bd ("clk: sunxi-ng: add support for DE2 CCU") Signed-off-by: Wei Yongjun Signed-off-by: Maxime Ripard --- drivers/clk/sunxi-ng/ccu-sun8i-de2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c index 15aaa9c4a3af..5cdaf52669e4 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c @@ -184,7 +184,7 @@ static int sunxi_de2_clk_probe(struct platform_device *pdev) rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(rstc)) { - ret = PTR_ERR(bus_clk); + ret = PTR_ERR(rstc); if (ret != -EPROBE_DEFER) dev_err(&pdev->dev, "Couldn't get reset control: %d\n", ret); -- cgit v1.2.1 From 13e0dde8b2ed043aa3e65437342d501715d975c1 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 19 May 2017 15:06:08 +0800 Subject: clk: sunxi-ng: Support multiple variable pre-dividers On the A83T, the AHB1 clock has a shared pre-divider on the two PLL-PERIPH clock parents. To support such instances of shared pre-dividers, this patch extends the mux clock type to support multiple variable pre-dividers. As the pre-dividers are only used to calculate the rate, but do not participate in the factorization process, this is fairly straightforward. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi-ng/ccu-sun50i-a64.c | 10 +++++----- drivers/clk/sunxi-ng/ccu-sun6i-a31.c | 10 +++++----- drivers/clk/sunxi-ng/ccu-sun8i-a23.c | 10 +++++----- drivers/clk/sunxi-ng/ccu-sun8i-a33.c | 10 +++++----- drivers/clk/sunxi-ng/ccu-sun8i-h3.c | 10 +++++----- drivers/clk/sunxi-ng/ccu-sun8i-r.c | 10 +++++----- drivers/clk/sunxi-ng/ccu-sun8i-v3s.c | 10 +++++----- drivers/clk/sunxi-ng/ccu_mux.c | 18 +++++++++++------- drivers/clk/sunxi-ng/ccu_mux.h | 13 ++++++++----- 9 files changed, 54 insertions(+), 47 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c index f54114c607df..2bb4cabf802f 100644 --- a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c +++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c @@ -211,6 +211,9 @@ static SUNXI_CCU_M(axi_clk, "axi", "cpux", 0x050, 0, 2, 0); static const char * const ahb1_parents[] = { "osc32k", "osc24M", "axi", "pll-periph0" }; +static const struct ccu_mux_var_prediv ahb1_predivs[] = { + { .index = 3, .shift = 6, .width = 2 }, +}; static struct ccu_div ahb1_clk = { .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), @@ -218,11 +221,8 @@ static struct ccu_div ahb1_clk = { .shift = 12, .width = 2, - .variable_prediv = { - .index = 3, - .shift = 6, - .width = 2, - }, + .var_predivs = ahb1_predivs, + .n_var_predivs = ARRAY_SIZE(ahb1_predivs), }, .common = { diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c index df97e25aec76..4d6078fca9ac 100644 --- a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c +++ b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c @@ -195,6 +195,9 @@ static SUNXI_CCU_DIV_TABLE(axi_clk, "axi", "cpu", static const char * const ahb1_parents[] = { "osc32k", "osc24M", "axi", "pll-periph" }; +static const struct ccu_mux_var_prediv ahb1_predivs[] = { + { .index = 3, .shift = 6, .width = 2 }, +}; static struct ccu_div ahb1_clk = { .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), @@ -203,11 +206,8 @@ static struct ccu_div ahb1_clk = { .shift = 12, .width = 2, - .variable_prediv = { - .index = 3, - .shift = 6, - .width = 2, - }, + .var_predivs = ahb1_predivs, + .n_var_predivs = ARRAY_SIZE(ahb1_predivs), }, .common = { diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c index 5c6d37bdf247..8a753ed0426d 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c @@ -169,6 +169,9 @@ static SUNXI_CCU_M(axi_clk, "axi", "cpux", 0x050, 0, 2, 0); static const char * const ahb1_parents[] = { "osc32k", "osc24M", "axi" , "pll-periph" }; +static const struct ccu_mux_var_prediv ahb1_predivs[] = { + { .index = 3, .shift = 6, .width = 2 }, +}; static struct ccu_div ahb1_clk = { .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), @@ -176,11 +179,8 @@ static struct ccu_div ahb1_clk = { .shift = 12, .width = 2, - .variable_prediv = { - .index = 3, - .shift = 6, - .width = 2, - }, + .var_predivs = ahb1_predivs, + .n_var_predivs = ARRAY_SIZE(ahb1_predivs), }, .common = { diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c index 8d38e6510e29..10b38dc46f75 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c @@ -180,6 +180,9 @@ static SUNXI_CCU_M(axi_clk, "axi", "cpux", 0x050, 0, 2, 0); static const char * const ahb1_parents[] = { "osc32k", "osc24M", "axi" , "pll-periph" }; +static const struct ccu_mux_var_prediv ahb1_predivs[] = { + { .index = 3, .shift = 6, .width = 2 }, +}; static struct ccu_div ahb1_clk = { .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), @@ -187,11 +190,8 @@ static struct ccu_div ahb1_clk = { .shift = 12, .width = 2, - .variable_prediv = { - .index = 3, - .shift = 6, - .width = 2, - }, + .var_predivs = ahb1_predivs, + .n_var_predivs = ARRAY_SIZE(ahb1_predivs), }, .common = { diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c index 4cbc1b701b7c..62e4f0d2b2fc 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c @@ -141,6 +141,9 @@ static SUNXI_CCU_M(axi_clk, "axi", "cpux", 0x050, 0, 2, 0); static const char * const ahb1_parents[] = { "osc32k", "osc24M", "axi" , "pll-periph0" }; +static const struct ccu_mux_var_prediv ahb1_predivs[] = { + { .index = 3, .shift = 6, .width = 2 }, +}; static struct ccu_div ahb1_clk = { .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), @@ -148,11 +151,8 @@ static struct ccu_div ahb1_clk = { .shift = 12, .width = 2, - .variable_prediv = { - .index = 3, - .shift = 6, - .width = 2, - }, + .var_predivs = ahb1_predivs, + .n_var_predivs = ARRAY_SIZE(ahb1_predivs), }, .common = { diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r.c b/drivers/clk/sunxi-ng/ccu-sun8i-r.c index 119f47b568ea..de02be75785c 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-r.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-r.c @@ -27,6 +27,9 @@ static const char * const ar100_parents[] = { "osc32k", "osc24M", "pll-periph0", "iosc" }; +static const struct ccu_mux_var_prediv ar100_predivs[] = { + { .index = 2, .shift = 8, .width = 5 }, +}; static struct ccu_div ar100_clk = { .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), @@ -35,11 +38,8 @@ static struct ccu_div ar100_clk = { .shift = 16, .width = 2, - .variable_prediv = { - .index = 2, - .shift = 8, - .width = 5, - }, + .var_predivs = ar100_predivs, + .n_var_predivs = ARRAY_SIZE(ar100_predivs), }, .common = { diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c index 6297add857b5..a34a78d7fb28 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c @@ -132,6 +132,9 @@ static SUNXI_CCU_M(axi_clk, "axi", "cpu", 0x050, 0, 2, 0); static const char * const ahb1_parents[] = { "osc32k", "osc24M", "axi", "pll-periph0" }; +static const struct ccu_mux_var_prediv ahb1_predivs[] = { + { .index = 3, .shift = 6, .width = 2 }, +}; static struct ccu_div ahb1_clk = { .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), @@ -139,11 +142,8 @@ static struct ccu_div ahb1_clk = { .shift = 12, .width = 2, - .variable_prediv = { - .index = 3, - .shift = 6, - .width = 2, - }, + .var_predivs = ahb1_predivs, + .n_var_predivs = ARRAY_SIZE(ahb1_predivs), }, .common = { diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c index 748b172f9193..cfe4538304fb 100644 --- a/drivers/clk/sunxi-ng/ccu_mux.c +++ b/drivers/clk/sunxi-ng/ccu_mux.c @@ -44,14 +44,18 @@ static u16 ccu_mux_get_prediv(struct ccu_common *common, prediv = cm->fixed_predivs[i].div; } - if (common->features & CCU_FEATURE_VARIABLE_PREDIV) - if (parent_index == cm->variable_prediv.index) { - u8 div; + if (common->features & CCU_FEATURE_VARIABLE_PREDIV) { + int i; - div = reg >> cm->variable_prediv.shift; - div &= (1 << cm->variable_prediv.width) - 1; - prediv = div + 1; - } + for (i = 0; i < cm->n_var_predivs; i++) + if (parent_index == cm->var_predivs[i].index) { + u8 div; + + div = reg >> cm->var_predivs[i].shift; + div &= (1 << cm->var_predivs[i].width) - 1; + prediv = div + 1; + } + } return prediv; } diff --git a/drivers/clk/sunxi-ng/ccu_mux.h b/drivers/clk/sunxi-ng/ccu_mux.h index dba12c76cf54..f20c0bd62a47 100644 --- a/drivers/clk/sunxi-ng/ccu_mux.h +++ b/drivers/clk/sunxi-ng/ccu_mux.h @@ -10,6 +10,12 @@ struct ccu_mux_fixed_prediv { u16 div; }; +struct ccu_mux_var_prediv { + u8 index; + u8 shift; + u8 width; +}; + struct ccu_mux_internal { u8 shift; u8 width; @@ -18,11 +24,8 @@ struct ccu_mux_internal { const struct ccu_mux_fixed_prediv *fixed_predivs; u8 n_predivs; - struct { - u8 index; - u8 shift; - u8 width; - } variable_prediv; + const struct ccu_mux_var_prediv *var_predivs; + u8 n_var_predivs; }; #define _SUNXI_CCU_MUX_TABLE(_shift, _width, _table) \ -- cgit v1.2.1 From 05359be1176bd097af9e7e833ff0317c55c5a86c Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 19 May 2017 15:06:09 +0800 Subject: clk: sunxi-ng: Add driver for A83T CCU The A83T clock control unit is a hybrid of some new style clock designs from the A80, and old style layout from the other Allwinner SoCs. Like the A80, the SoC does not have a low speed 32.768 kHz oscillator. Unlike the A80, there is no clock input either. The only low speed clock available is the internal oscillator which runs at around 16 MHz, divided by 512, yielding a low speed clock around 31.250 kHz. Also, the MMC2 module clock supports switching to a "new timing" mode. This mode divides the clock output by half, and disables the CCU based clock delays. The MMC controller must be configure to the same mode, and then use its internal clock delays. This driver does not support runtime switching of the timing modes. Instead, the new timing mode is enforced at probe time. Consumers can check which mode is active by trying to get the current phase delay of the MMC2 phase clocks, which will return -ENOTSUPP if the new timing mode is active. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi-ng/Kconfig | 11 + drivers/clk/sunxi-ng/Makefile | 1 + drivers/clk/sunxi-ng/ccu-sun8i-a83t.c | 922 ++++++++++++++++++++++++++++++++++ drivers/clk/sunxi-ng/ccu-sun8i-a83t.h | 64 +++ 4 files changed, 998 insertions(+) create mode 100644 drivers/clk/sunxi-ng/ccu-sun8i-a83t.c create mode 100644 drivers/clk/sunxi-ng/ccu-sun8i-a83t.h (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig index b5706fc73f3e..a384c695b388 100644 --- a/drivers/clk/sunxi-ng/Kconfig +++ b/drivers/clk/sunxi-ng/Kconfig @@ -116,6 +116,17 @@ config SUN8I_A33_CCU default MACH_SUN8I depends on MACH_SUN8I || COMPILE_TEST +config SUN8I_A83T_CCU + bool "Support for the Allwinner A83T CCU" + select SUNXI_CCU_DIV + select SUNXI_CCU_GATE + select SUNXI_CCU_MP + select SUNXI_CCU_MUX + select SUNXI_CCU_NKMP + select SUNXI_CCU_NM + select SUNXI_CCU_PHASE + default MACH_SUN8I + config SUN8I_H3_CCU bool "Support for the Allwinner H3 CCU" select SUNXI_CCU_DIV diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile index be616279450e..0185c6ffadcb 100644 --- a/drivers/clk/sunxi-ng/Makefile +++ b/drivers/clk/sunxi-ng/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_SUN5I_CCU) += ccu-sun5i.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 +obj-$(CONFIG_SUN8I_A83T_CCU) += ccu-sun8i-a83t.o obj-$(CONFIG_SUN8I_H3_CCU) += ccu-sun8i-h3.o obj-$(CONFIG_SUN8I_V3S_CCU) += ccu-sun8i-v3s.o obj-$(CONFIG_SUN8I_DE2_CCU) += ccu-sun8i-de2.o diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c new file mode 100644 index 000000000000..4a201a7e03b8 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c @@ -0,0 +1,922 @@ +/* + * Copyright (c) 2017 Chen-Yu Tsai. 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_mux.h" +#include "ccu_nkmp.h" +#include "ccu_nm.h" +#include "ccu_phase.h" + +#include "ccu-sun8i-a83t.h" + +#define CCU_SUN8I_A83T_LOCK_REG 0x208 + +/* + * The CPU PLLs are actually NP clocks, with P being /1 or /4. However + * P should only be used for output frequencies lower than 228 MHz. + * Neither mainline Linux, U-boot, nor the vendor BSPs use these. + * + * For now we can just model it as a multiplier clock, and force P to /1. + */ +#define SUN8I_A83T_PLL_C0CPUX_REG 0x000 +#define SUN8I_A83T_PLL_C1CPUX_REG 0x004 + +static struct ccu_mult pll_c0cpux_clk = { + .enable = BIT(31), + .lock = BIT(0), + .mult = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .common = { + .reg = SUN8I_A83T_PLL_C0CPUX_REG, + .lock_reg = CCU_SUN8I_A83T_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-c0cpux", "osc24M", + &ccu_mult_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_mult pll_c1cpux_clk = { + .enable = BIT(31), + .lock = BIT(1), + .mult = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .common = { + .reg = SUN8I_A83T_PLL_C1CPUX_REG, + .lock_reg = CCU_SUN8I_A83T_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-c1cpux", "osc24M", + &ccu_mult_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +/* + * The Audio PLL has d1, d2 dividers in addition to the usual N, M + * factors. Since we only need 2 frequencies from this PLL: 22.5792 MHz + * and 24.576 MHz, ignore them for now. Enforce the default for them, + * which is d1 = 0, d2 = 1. + */ +#define SUN8I_A83T_PLL_AUDIO_REG 0x008 + +static struct ccu_nm pll_audio_clk = { + .enable = BIT(31), + .lock = BIT(2), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV_OFFSET(0, 6, 0), + .common = { + .reg = SUN8I_A83T_PLL_AUDIO_REG, + .lock_reg = CCU_SUN8I_A83T_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-audio", "osc24M", + &ccu_nm_ops, CLK_SET_RATE_UNGATE), + }, +}; + +/* Some PLLs are input * N / div1 / P. Model them as NKMP with no K */ +static struct ccu_nkmp pll_video0_clk = { + .enable = BIT(31), + .lock = BIT(3), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV(16, 1), /* input divider */ + .p = _SUNXI_CCU_DIV(0, 2), /* output divider */ + .common = { + .reg = 0x010, + .lock_reg = CCU_SUN8I_A83T_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-video0", "osc24M", + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_nkmp pll_ve_clk = { + .enable = BIT(31), + .lock = BIT(4), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV(16, 1), /* input divider */ + .p = _SUNXI_CCU_DIV(18, 1), /* output divider */ + .common = { + .reg = 0x018, + .lock_reg = CCU_SUN8I_A83T_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-ve", "osc24M", + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_nkmp pll_ddr_clk = { + .enable = BIT(31), + .lock = BIT(5), + .n = _SUNXI_CCU_MULT_MIN(8, 8, 12), + .m = _SUNXI_CCU_DIV(16, 1), /* input divider */ + .p = _SUNXI_CCU_DIV(18, 1), /* output divider */ + .common = { + .reg = 0x020, + .lock_reg = CCU_SUN8I_A83T_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-ddr", "osc24M", + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_nkmp pll_periph_clk = { + .enable = BIT(31), + .lock = BIT(6), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV(16, 1), /* input divider */ + .p = _SUNXI_CCU_DIV(18, 1), /* output divider */ + .common = { + .reg = 0x028, + .lock_reg = CCU_SUN8I_A83T_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-periph", "osc24M", + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_nkmp pll_gpu_clk = { + .enable = BIT(31), + .lock = BIT(7), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV(16, 1), /* input divider */ + .p = _SUNXI_CCU_DIV(18, 1), /* output divider */ + .common = { + .reg = 0x038, + .lock_reg = CCU_SUN8I_A83T_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-gpu", "osc24M", + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_nkmp pll_hsic_clk = { + .enable = BIT(31), + .lock = BIT(8), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV(16, 1), /* input divider */ + .p = _SUNXI_CCU_DIV(18, 1), /* output divider */ + .common = { + .reg = 0x044, + .lock_reg = CCU_SUN8I_A83T_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-hsic", "osc24M", + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_nkmp pll_de_clk = { + .enable = BIT(31), + .lock = BIT(9), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV(16, 1), /* input divider */ + .p = _SUNXI_CCU_DIV(18, 1), /* output divider */ + .common = { + .reg = 0x048, + .lock_reg = CCU_SUN8I_A83T_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-de", "osc24M", + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_nkmp pll_video1_clk = { + .enable = BIT(31), + .lock = BIT(10), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV(16, 1), /* input divider */ + .p = _SUNXI_CCU_DIV(0, 2), /* external divider p */ + .common = { + .reg = 0x04c, + .lock_reg = CCU_SUN8I_A83T_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-video1", "osc24M", + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static const char * const c0cpux_parents[] = { "osc24M", "pll-c0cpux" }; +static SUNXI_CCU_MUX(c0cpux_clk, "c0cpux", c0cpux_parents, + 0x50, 12, 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + +static const char * const c1cpux_parents[] = { "osc24M", "pll-c1cpux" }; +static SUNXI_CCU_MUX(c1cpux_clk, "c1cpux", c1cpux_parents, + 0x50, 28, 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + +static SUNXI_CCU_M(axi0_clk, "axi0", "c0cpux", 0x050, 0, 2, 0); +static SUNXI_CCU_M(axi1_clk, "axi1", "c1cpux", 0x050, 16, 2, 0); + +static const char * const ahb1_parents[] = { "osc16M-d512", "osc24M", + "pll-periph", + "pll-periph" }; +static const struct ccu_mux_var_prediv ahb1_predivs[] = { + { .index = 2, .shift = 6, .width = 2 }, + { .index = 3, .shift = 6, .width = 2 }, +}; +static struct ccu_div ahb1_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), + .mux = { + .shift = 12, + .width = 2, + + .var_predivs = ahb1_predivs, + .n_var_predivs = ARRAY_SIZE(ahb1_predivs), + }, + .common = { + .reg = 0x054, + .hw.init = CLK_HW_INIT_PARENTS("ahb1", + ahb1_parents, + &ccu_div_ops, + 0), + }, +}; + +static SUNXI_CCU_M(apb1_clk, "apb1", "ahb1", 0x054, 8, 2, 0); + +static const char * const apb2_parents[] = { "osc16M-d512", "osc24M", + "pll-periph", "pll-periph" }; + +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-periph" }; +static const struct ccu_mux_fixed_prediv ahb2_prediv = { + .index = 1, .div = 2 +}; +static struct ccu_mux ahb2_clk = { + .mux = { + .shift = 0, + .width = 2, + .fixed_predivs = &ahb2_prediv, + .n_predivs = 1, + }, + .common = { + .reg = 0x05c, + .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_ss_clk, "bus-ss", "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_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(24), 0); +static SUNXI_CCU_GATE(bus_ehci0_clk, "bus-ehci0", "ahb2", + 0x060, BIT(26), 0); +static SUNXI_CCU_GATE(bus_ehci1_clk, "bus-ehci1", "ahb2", + 0x060, BIT(27), 0); +static SUNXI_CCU_GATE(bus_ohci0_clk, "bus-ohci0", "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(4), 0); +static SUNXI_CCU_GATE(bus_tcon1_clk, "bus-tcon1", "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_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_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_tdm_clk, "bus-tdm", "apb1", + 0x068, BIT(15), 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(0), 0); +static SUNXI_CCU_GATE(bus_i2c2_clk, "bus-i2c2", "apb2", + 0x06c, BIT(0), 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 const char * const cci400_parents[] = { "osc24M", "pll-periph", + "pll-hsic" }; +static struct ccu_div cci400_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(0, 2, 0), + .mux = _SUNXI_CCU_MUX(24, 2), + .common = { + .reg = 0x078, + .hw.init = CLK_HW_INIT_PARENTS("cci400", + cci400_parents, + &ccu_div_ops, + CLK_IS_CRITICAL), + }, +}; + +static const char * const mod0_default_parents[] = { "osc24M", "pll-periph" }; + +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 SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mod0_default_parents, + 0x088, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc0_sample_clk, "mmc0-sample", "mmc0", + 0x088, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc0_output_clk, "mmc0-output", "mmc0", + 0x088, 8, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mod0_default_parents, + 0x08c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc1_sample_clk, "mmc1-sample", "mmc1", + 0x08c, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc1_output_clk, "mmc1-output", "mmc1", + 0x08c, 8, 3, 0); + +/* TODO Support MMC2 clock's new timing mode. */ +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents, + 0x090, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc2_sample_clk, "mmc2-sample", "mmc2", + 0x090, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc2_output_clk, "mmc2-output", "mmc2", + 0x090, 8, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(ss_clk, "ss", mod0_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, 4, /* 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, 4, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_M_WITH_GATE(i2s0_clk, "i2s0", "pll-audio", + 0x0b0, 0, 4, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_M_WITH_GATE(i2s1_clk, "i2s1", "pll-audio", + 0x0b4, 0, 4, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_M_WITH_GATE(i2s2_clk, "i2s2", "pll-audio", + 0x0b8, 0, 4, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_M_WITH_GATE(tdm_clk, "tdm", "pll-audio", + 0x0bc, 0, 4, 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 struct ccu_gate usb_hsic_12m_clk = { + .enable = BIT(11), + .common = { + .reg = 0x0cc, + .prediv = 2, + .features = CCU_FEATURE_ALL_PREDIV, + .hw.init = CLK_HW_INIT("usb-hsic-12m", "osc24M", + &ccu_gate_ops, 0), + } +}; +static SUNXI_CCU_GATE(usb_ohci0_clk, "usb-ohci0", "osc24M", + 0x0cc, BIT(16), 0); + +/* TODO divider has minimum of 2 */ +static SUNXI_CCU_M(dram_clk, "dram", "pll-ddr", 0x0f4, 0, 4, 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 const char * const tcon0_parents[] = { "pll-video0" }; +static SUNXI_CCU_MUX_WITH_GATE(tcon0_clk, "tcon0", tcon0_parents, + 0x118, 24, 3, BIT(31), CLK_SET_RATE_PARENT); + +static const char * const tcon1_parents[] = { "pll-video1" }; +static SUNXI_CCU_MUX_WITH_GATE(tcon1_clk, "tcon1", tcon1_parents, + 0x11c, 24, 3, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(csi_misc_clk, "csi-misc", "osc24M", 0x130, BIT(16), 0); + +static SUNXI_CCU_GATE(mipi_csi_clk, "mipi-csi", "osc24M", 0x130, BIT(31), 0); + +static const char * const csi_mclk_parents[] = { "pll-de", "osc24M" }; +static const u8 csi_mclk_table[] = { 3, 5 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi_mclk_clk, "csi-mclk", + csi_mclk_parents, csi_mclk_table, + 0x134, + 0, 5, /* M */ + 10, 3, /* mux */ + BIT(15), /* gate */ + 0); + +static const char * const csi_sclk_parents[] = { "pll-periph", "pll-ve" }; +static const u8 csi_sclk_table[] = { 0, 5 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi_sclk_clk, "csi-sclk", + csi_sclk_parents, csi_sclk_table, + 0x134, + 16, 4, /* M */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve", 0x13c, + 16, 3, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M", 0x144, BIT(31), 0); + +static const char * const hdmi_parents[] = { "pll-video1" }; +static SUNXI_CCU_M_WITH_MUX_GATE(hdmi_clk, "hdmi", hdmi_parents, + 0x150, + 0, 4, /* M */ + 24, 2, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(hdmi_slow_clk, "hdmi-slow", "osc24M", 0x154, BIT(31), 0); + +static const char * const mbus_parents[] = { "osc24M", "pll-periph", + "pll-ddr" }; +static SUNXI_CCU_M_WITH_MUX_GATE(mbus_clk, "mbus", mbus_parents, + 0x15c, + 0, 3, /* M */ + 24, 2, /* mux */ + BIT(31), /* gate */ + CLK_IS_CRITICAL); + +static const char * const mipi_dsi0_parents[] = { "pll-video0" }; +static const u8 mipi_dsi0_table[] = { 8 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(mipi_dsi0_clk, "mipi-dsi0", + mipi_dsi0_parents, mipi_dsi0_table, + 0x168, + 0, 4, /* M */ + 24, 4, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static const char * const mipi_dsi1_parents[] = { "osc24M", "pll-video0" }; +static const u8 mipi_dsi1_table[] = { 0, 9 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(mipi_dsi1_clk, "mipi-dsi1", + mipi_dsi1_parents, mipi_dsi1_table, + 0x16c, + 0, 4, /* M */ + 24, 4, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_GATE(gpu_core_clk, "gpu-core", "pll-gpu", 0x1a0, + 0, 3, BIT(31), CLK_SET_RATE_PARENT); + +static const char * const gpu_memory_parents[] = { "pll-gpu", "pll-ddr" }; +static SUNXI_CCU_M_WITH_MUX_GATE(gpu_memory_clk, "gpu-memory", + gpu_memory_parents, + 0x1a4, + 0, 3, /* M */ + 24, 1, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_GATE(gpu_hyd_clk, "gpu-hyd", "pll-gpu", 0x1a8, + 0, 3, BIT(31), CLK_SET_RATE_PARENT); + +static struct ccu_common *sun8i_a83t_ccu_clks[] = { + &pll_c0cpux_clk.common, + &pll_c1cpux_clk.common, + &pll_audio_clk.common, + &pll_video0_clk.common, + &pll_ve_clk.common, + &pll_ddr_clk.common, + &pll_periph_clk.common, + &pll_gpu_clk.common, + &pll_hsic_clk.common, + &pll_de_clk.common, + &pll_video1_clk.common, + &c0cpux_clk.common, + &c1cpux_clk.common, + &axi0_clk.common, + &axi1_clk.common, + &ahb1_clk.common, + &ahb2_clk.common, + &apb1_clk.common, + &apb2_clk.common, + &bus_mipi_dsi_clk.common, + &bus_ss_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_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_ve_clk.common, + &bus_tcon0_clk.common, + &bus_tcon1_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_spdif_clk.common, + &bus_pio_clk.common, + &bus_i2s0_clk.common, + &bus_i2s1_clk.common, + &bus_i2s2_clk.common, + &bus_tdm_clk.common, + &bus_i2c0_clk.common, + &bus_i2c1_clk.common, + &bus_i2c2_clk.common, + &bus_uart0_clk.common, + &bus_uart1_clk.common, + &bus_uart2_clk.common, + &bus_uart3_clk.common, + &bus_uart4_clk.common, + &cci400_clk.common, + &nand_clk.common, + &mmc0_clk.common, + &mmc0_sample_clk.common, + &mmc0_output_clk.common, + &mmc1_clk.common, + &mmc1_sample_clk.common, + &mmc1_output_clk.common, + &mmc2_clk.common, + &mmc2_sample_clk.common, + &mmc2_output_clk.common, + &ss_clk.common, + &spi0_clk.common, + &spi1_clk.common, + &i2s0_clk.common, + &i2s1_clk.common, + &i2s2_clk.common, + &tdm_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, + &dram_clk.common, + &dram_ve_clk.common, + &dram_csi_clk.common, + &tcon0_clk.common, + &tcon1_clk.common, + &csi_misc_clk.common, + &mipi_csi_clk.common, + &csi_mclk_clk.common, + &csi_sclk_clk.common, + &ve_clk.common, + &avs_clk.common, + &hdmi_clk.common, + &hdmi_slow_clk.common, + &mbus_clk.common, + &mipi_dsi0_clk.common, + &mipi_dsi1_clk.common, + &gpu_core_clk.common, + &gpu_memory_clk.common, + &gpu_hyd_clk.common, +}; + +static struct clk_hw_onecell_data sun8i_a83t_hw_clks = { + .hws = { + [CLK_PLL_C0CPUX] = &pll_c0cpux_clk.common.hw, + [CLK_PLL_C1CPUX] = &pll_c1cpux_clk.common.hw, + [CLK_PLL_AUDIO] = &pll_audio_clk.common.hw, + [CLK_PLL_VIDEO0] = &pll_video0_clk.common.hw, + [CLK_PLL_VE] = &pll_ve_clk.common.hw, + [CLK_PLL_DDR] = &pll_ddr_clk.common.hw, + [CLK_PLL_PERIPH] = &pll_periph_clk.common.hw, + [CLK_PLL_GPU] = &pll_gpu_clk.common.hw, + [CLK_PLL_HSIC] = &pll_hsic_clk.common.hw, + [CLK_PLL_DE] = &pll_de_clk.common.hw, + [CLK_PLL_VIDEO1] = &pll_video1_clk.common.hw, + [CLK_C0CPUX] = &c0cpux_clk.common.hw, + [CLK_C1CPUX] = &c1cpux_clk.common.hw, + [CLK_AXI0] = &axi0_clk.common.hw, + [CLK_AXI1] = &axi1_clk.common.hw, + [CLK_AHB1] = &ahb1_clk.common.hw, + [CLK_AHB2] = &ahb2_clk.common.hw, + [CLK_APB1] = &apb1_clk.common.hw, + [CLK_APB2] = &apb2_clk.common.hw, + [CLK_BUS_MIPI_DSI] = &bus_mipi_dsi_clk.common.hw, + [CLK_BUS_SS] = &bus_ss_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_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_VE] = &bus_ve_clk.common.hw, + [CLK_BUS_TCON0] = &bus_tcon0_clk.common.hw, + [CLK_BUS_TCON1] = &bus_tcon1_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_SPDIF] = &bus_spdif_clk.common.hw, + [CLK_BUS_PIO] = &bus_pio_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_TDM] = &bus_tdm_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_CCI400] = &cci400_clk.common.hw, + [CLK_NAND] = &nand_clk.common.hw, + [CLK_MMC0] = &mmc0_clk.common.hw, + [CLK_MMC0_SAMPLE] = &mmc0_sample_clk.common.hw, + [CLK_MMC0_OUTPUT] = &mmc0_output_clk.common.hw, + [CLK_MMC1] = &mmc1_clk.common.hw, + [CLK_MMC1_SAMPLE] = &mmc1_sample_clk.common.hw, + [CLK_MMC1_OUTPUT] = &mmc1_output_clk.common.hw, + [CLK_MMC2] = &mmc2_clk.common.hw, + [CLK_MMC2_SAMPLE] = &mmc2_sample_clk.common.hw, + [CLK_MMC2_OUTPUT] = &mmc2_output_clk.common.hw, + [CLK_SS] = &ss_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_TDM] = &tdm_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_DRAM] = &dram_clk.common.hw, + [CLK_DRAM_VE] = &dram_ve_clk.common.hw, + [CLK_DRAM_CSI] = &dram_csi_clk.common.hw, + [CLK_TCON0] = &tcon0_clk.common.hw, + [CLK_TCON1] = &tcon1_clk.common.hw, + [CLK_CSI_MISC] = &csi_misc_clk.common.hw, + [CLK_MIPI_CSI] = &mipi_csi_clk.common.hw, + [CLK_CSI_MCLK] = &csi_mclk_clk.common.hw, + [CLK_CSI_SCLK] = &csi_sclk_clk.common.hw, + [CLK_VE] = &ve_clk.common.hw, + [CLK_AVS] = &avs_clk.common.hw, + [CLK_HDMI] = &hdmi_clk.common.hw, + [CLK_HDMI_SLOW] = &hdmi_slow_clk.common.hw, + [CLK_MBUS] = &mbus_clk.common.hw, + [CLK_MIPI_DSI0] = &mipi_dsi0_clk.common.hw, + [CLK_MIPI_DSI1] = &mipi_dsi1_clk.common.hw, + [CLK_GPU_CORE] = &gpu_core_clk.common.hw, + [CLK_GPU_MEMORY] = &gpu_memory_clk.common.hw, + [CLK_GPU_HYD] = &gpu_hyd_clk.common.hw, + }, + .num = CLK_NUMBER, +}; + +static struct ccu_reset_map sun8i_a83t_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_SS] = { 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_HSTIMER] = { 0x2c0, BIT(19) }, + [RST_BUS_SPI0] = { 0x2c0, BIT(20) }, + [RST_BUS_SPI1] = { 0x2c0, BIT(21) }, + [RST_BUS_OTG] = { 0x2c0, BIT(24) }, + [RST_BUS_EHCI0] = { 0x2c0, BIT(26) }, + [RST_BUS_EHCI1] = { 0x2c0, BIT(27) }, + [RST_BUS_OHCI0] = { 0x2c0, BIT(29) }, + [RST_BUS_VE] = { 0x2c4, BIT(0) }, + [RST_BUS_TCON0] = { 0x2c4, BIT(4) }, + [RST_BUS_TCON1] = { 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_LVDS] = { 0x2c8, BIT(0) }, + [RST_BUS_SPDIF] = { 0x2d0, BIT(1) }, + [RST_BUS_I2S0] = { 0x2d0, BIT(12) }, + [RST_BUS_I2S1] = { 0x2d0, BIT(13) }, + [RST_BUS_I2S2] = { 0x2d0, BIT(14) }, + [RST_BUS_TDM] = { 0x2d0, BIT(15) }, + [RST_BUS_I2C0] = { 0x2d8, BIT(0) }, + [RST_BUS_I2C1] = { 0x2d8, BIT(1) }, + [RST_BUS_I2C2] = { 0x2d8, BIT(2) }, + [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 sun8i_a83t_ccu_desc = { + .ccu_clks = sun8i_a83t_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_a83t_ccu_clks), + + .hw_clks = &sun8i_a83t_hw_clks, + + .resets = sun8i_a83t_ccu_resets, + .num_resets = ARRAY_SIZE(sun8i_a83t_ccu_resets), +}; + +#define SUN8I_A83T_PLL_P_SHIFT 16 +#define SUN8I_A83T_PLL_N_SHIFT 8 +#define SUN8I_A83T_PLL_N_WIDTH 8 + +static void sun8i_a83t_cpu_pll_fixup(void __iomem *reg) +{ + u32 val = readl(reg); + + /* bail out if P divider is not used */ + if (!(val & BIT(SUN8I_A83T_PLL_P_SHIFT))) + return; + + /* + * If P is used, output should be less than 288 MHz. When we + * set P to 1, we should also decrease the multiplier so the + * output doesn't go out of range, but not too much such that + * the multiplier stays above 12, the minimal operation value. + * + * To keep it simple, set the multiplier to 17, the reset value. + */ + val &= ~GENMASK(SUN8I_A83T_PLL_N_SHIFT + SUN8I_A83T_PLL_N_WIDTH - 1, + SUN8I_A83T_PLL_N_SHIFT); + val |= 17 << SUN8I_A83T_PLL_N_SHIFT; + + /* And clear P */ + val &= ~BIT(SUN8I_A83T_PLL_P_SHIFT); + + writel(val, reg); +} + +static int sun8i_a83t_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); + + /* Enforce d1 = 0, d2 = 0 for Audio PLL */ + val = readl(reg + SUN8I_A83T_PLL_AUDIO_REG); + val &= ~(BIT(16) | BIT(18)); + writel(val, reg + SUN8I_A83T_PLL_AUDIO_REG); + + /* Enforce P = 1 for both CPU cluster PLLs */ + sun8i_a83t_cpu_pll_fixup(reg + SUN8I_A83T_PLL_C0CPUX_REG); + sun8i_a83t_cpu_pll_fixup(reg + SUN8I_A83T_PLL_C1CPUX_REG); + + return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun8i_a83t_ccu_desc); +} + +static const struct of_device_id sun8i_a83t_ccu_ids[] = { + { .compatible = "allwinner,sun8i-a83t-ccu" }, + { } +}; + +static struct platform_driver sun8i_a83t_ccu_driver = { + .probe = sun8i_a83t_ccu_probe, + .driver = { + .name = "sun8i-a83t-ccu", + .of_match_table = sun8i_a83t_ccu_ids, + }, +}; +builtin_platform_driver(sun8i_a83t_ccu_driver); diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.h b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.h new file mode 100644 index 000000000000..d67edaf76748 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.h @@ -0,0 +1,64 @@ +/* + * Copyright 2016 Chen-Yu Tsai + * + * Chen-Yu Tsai + * + * 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_SUN8I_A83T_H_ +#define _CCU_SUN8I_A83T_H_ + +#include +#include + +#define CLK_PLL_C0CPUX 0 +#define CLK_PLL_C1CPUX 1 +#define CLK_PLL_AUDIO 2 +#define CLK_PLL_VIDEO0 3 +#define CLK_PLL_VE 4 +#define CLK_PLL_DDR 5 + +/* pll-periph is exported to the PRCM block */ + +#define CLK_PLL_GPU 7 +#define CLK_PLL_HSIC 8 + +/* pll-de is exported for the display engine */ + +#define CLK_PLL_VIDEO1 10 + +/* The CPUX clocks are exported */ + +#define CLK_AXI0 13 +#define CLK_AXI1 14 +#define CLK_AHB1 15 +#define CLK_AHB2 16 +#define CLK_APB1 17 +#define CLK_APB2 18 + +/* bus gates exported */ + +#define CLK_CCI400 58 + +/* module and usb clocks exported */ + +#define CLK_DRAM 82 + +/* dram gates and more module clocks exported */ + +#define CLK_MBUS 95 + +/* more module clocks exported */ + +#define CLK_NUMBER (CLK_GPU_HYD + 1) + +#endif /* _CCU_SUN8I_A83T_H_ */ -- cgit v1.2.1 From faea8b0e33c2e6a276d34a755258bb2176553616 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 22 May 2017 14:25:47 +0800 Subject: clk: sunxi-ng: a83t: Fix PLL lock status register offset The offset for the PLL lock status register was incorrectly set to 0x208, which actually points to an unused register. The correct register offset is 0x20c. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi-ng/ccu-sun8i-a83t.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c index 4a201a7e03b8..a9c5cc87d9d0 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c @@ -28,7 +28,7 @@ #include "ccu-sun8i-a83t.h" -#define CCU_SUN8I_A83T_LOCK_REG 0x208 +#define CCU_SUN8I_A83T_LOCK_REG 0x20c /* * The CPU PLLs are actually NP clocks, with P being /1 or /4. However -- cgit v1.2.1 From f2fe1b640f8d5c3567ea1088544bf55e4d9654d8 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 22 May 2017 14:25:48 +0800 Subject: clk: sunxi-ng: a83t: Fix audio PLL divider offset The divider of the audio PLL has an offset of 1. Fix this in the driver. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi-ng/ccu-sun8i-a83t.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c index a9c5cc87d9d0..947f9f6e05d2 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c @@ -80,7 +80,7 @@ static struct ccu_nm pll_audio_clk = { .enable = BIT(31), .lock = BIT(2), .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), - .m = _SUNXI_CCU_DIV_OFFSET(0, 6, 0), + .m = _SUNXI_CCU_DIV(0, 6), .common = { .reg = SUN8I_A83T_PLL_AUDIO_REG, .lock_reg = CCU_SUN8I_A83T_LOCK_REG, -- cgit v1.2.1 From b5e53469f35f8f295ae6bbb66604580fc02cfcf4 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 22 May 2017 22:29:40 +0200 Subject: clk: sunxi-ng: select SUNXI_CCU_MULT for sun8i-a83t We get a link error when CCU_MULT is not set with the newly added driver: drivers/clk/sunxi-ng/ccu-sun8i-a83t.o:(.data.__compound_literal.1+0x4): undefined reference to `ccu_mult_ops' drivers/clk/sunxi-ng/ccu-sun8i-a83t.o:(.data.__compound_literal.3+0x4): undefined reference to `ccu_mult_ops' Fixes: 46b492116666 ("clk: sunxi-ng: Add driver for A83T CCU") Signed-off-by: Arnd Bergmann Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig index a384c695b388..67acef3d2494 100644 --- a/drivers/clk/sunxi-ng/Kconfig +++ b/drivers/clk/sunxi-ng/Kconfig @@ -121,6 +121,7 @@ config SUN8I_A83T_CCU select SUNXI_CCU_DIV select SUNXI_CCU_GATE select SUNXI_CCU_MP + select SUNXI_CCU_MULT select SUNXI_CCU_MUX select SUNXI_CCU_NKMP select SUNXI_CCU_NM -- cgit v1.2.1 From 5a90c14c0b11342f1121c9aa3fd8b679595015c7 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 26 May 2017 16:00:24 +0800 Subject: clk: sunxi-ng: a83t: Add support for A83T's PRCM The A83T's PRCM has the same set of clocks and resets as the A64. However, a few dividers are different. And due to the lack of a low speed 32.768 kHz oscillator, a few of the clock parents are different. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi-ng/ccu-sun8i-r.c | 107 +++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r.c b/drivers/clk/sunxi-ng/ccu-sun8i-r.c index de02be75785c..e54816ec1dbe 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-r.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-r.c @@ -27,6 +27,8 @@ static const char * const ar100_parents[] = { "osc32k", "osc24M", "pll-periph0", "iosc" }; +static const char * const a83t_ar100_parents[] = { "osc16M-d512", "osc24M", + "pll-periph0", "iosc" }; static const struct ccu_mux_var_prediv ar100_predivs[] = { { .index = 2, .shift = 8, .width = 5 }, }; @@ -52,6 +54,27 @@ static struct ccu_div ar100_clk = { }, }; +static struct ccu_div a83t_ar100_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), + + .mux = { + .shift = 16, + .width = 2, + + .var_predivs = ar100_predivs, + .n_var_predivs = ARRAY_SIZE(ar100_predivs), + }, + + .common = { + .reg = 0x00, + .features = CCU_FEATURE_VARIABLE_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("ar100", + a83t_ar100_parents, + &ccu_div_ops, + 0), + }, +}; + static CLK_FIXED_FACTOR(ahb0_clk, "ahb0", "ar100", 1, 1, 0); static struct ccu_div apb0_clk = { @@ -66,6 +89,8 @@ static struct ccu_div apb0_clk = { }, }; +static SUNXI_CCU_M(a83t_apb0_clk, "apb0", "ahb0", 0x0c, 0, 2, 0); + static SUNXI_CCU_GATE(apb0_pio_clk, "apb0-pio", "apb0", 0x28, BIT(0), 0); static SUNXI_CCU_GATE(apb0_ir_clk, "apb0-ir", "apb0", @@ -90,6 +115,46 @@ static SUNXI_CCU_MP_WITH_MUX_GATE(ir_clk, "ir", BIT(31), /* gate */ 0); +static const char *const a83t_r_mod0_parents[] = { "osc16M", "osc24M" }; +static const struct ccu_mux_fixed_prediv a83t_ir_predivs[] = { + { .index = 0, .div = 16 }, +}; +static struct ccu_mp a83t_ir_clk = { + .enable = BIT(31), + + .m = _SUNXI_CCU_DIV(0, 4), + .p = _SUNXI_CCU_DIV(16, 2), + + .mux = { + .shift = 24, + .width = 2, + .fixed_predivs = a83t_ir_predivs, + .n_predivs = ARRAY_SIZE(a83t_ir_predivs), + }, + + .common = { + .reg = 0x54, + .features = CCU_FEATURE_VARIABLE_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("ir", + a83t_r_mod0_parents, + &ccu_mp_ops, + 0), + }, +}; + +static struct ccu_common *sun8i_a83t_r_ccu_clks[] = { + &a83t_ar100_clk.common, + &a83t_apb0_clk.common, + &apb0_pio_clk.common, + &apb0_ir_clk.common, + &apb0_timer_clk.common, + &apb0_rsb_clk.common, + &apb0_uart_clk.common, + &apb0_i2c_clk.common, + &apb0_twd_clk.common, + &a83t_ir_clk.common, +}; + static struct ccu_common *sun8i_h3_r_ccu_clks[] = { &ar100_clk.common, &apb0_clk.common, @@ -115,6 +180,23 @@ static struct ccu_common *sun50i_a64_r_ccu_clks[] = { &ir_clk.common, }; +static struct clk_hw_onecell_data sun8i_a83t_r_hw_clks = { + .hws = { + [CLK_AR100] = &a83t_ar100_clk.common.hw, + [CLK_AHB0] = &ahb0_clk.hw, + [CLK_APB0] = &a83t_apb0_clk.common.hw, + [CLK_APB0_PIO] = &apb0_pio_clk.common.hw, + [CLK_APB0_IR] = &apb0_ir_clk.common.hw, + [CLK_APB0_TIMER] = &apb0_timer_clk.common.hw, + [CLK_APB0_RSB] = &apb0_rsb_clk.common.hw, + [CLK_APB0_UART] = &apb0_uart_clk.common.hw, + [CLK_APB0_I2C] = &apb0_i2c_clk.common.hw, + [CLK_APB0_TWD] = &apb0_twd_clk.common.hw, + [CLK_IR] = &a83t_ir_clk.common.hw, + }, + .num = CLK_NUMBER, +}; + static struct clk_hw_onecell_data sun8i_h3_r_hw_clks = { .hws = { [CLK_AR100] = &ar100_clk.common.hw, @@ -148,6 +230,14 @@ static struct clk_hw_onecell_data sun50i_a64_r_hw_clks = { .num = CLK_NUMBER, }; +static struct ccu_reset_map sun8i_a83t_r_ccu_resets[] = { + [RST_APB0_IR] = { 0xb0, BIT(1) }, + [RST_APB0_TIMER] = { 0xb0, BIT(2) }, + [RST_APB0_RSB] = { 0xb0, BIT(3) }, + [RST_APB0_UART] = { 0xb0, BIT(4) }, + [RST_APB0_I2C] = { 0xb0, BIT(6) }, +}; + static struct ccu_reset_map sun8i_h3_r_ccu_resets[] = { [RST_APB0_IR] = { 0xb0, BIT(1) }, [RST_APB0_TIMER] = { 0xb0, BIT(2) }, @@ -163,6 +253,16 @@ static struct ccu_reset_map sun50i_a64_r_ccu_resets[] = { [RST_APB0_I2C] = { 0xb0, BIT(6) }, }; +static const struct sunxi_ccu_desc sun8i_a83t_r_ccu_desc = { + .ccu_clks = sun8i_a83t_r_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_a83t_r_ccu_clks), + + .hw_clks = &sun8i_a83t_r_hw_clks, + + .resets = sun8i_a83t_r_ccu_resets, + .num_resets = ARRAY_SIZE(sun8i_a83t_r_ccu_resets), +}; + static const struct sunxi_ccu_desc sun8i_h3_r_ccu_desc = { .ccu_clks = sun8i_h3_r_ccu_clks, .num_ccu_clks = ARRAY_SIZE(sun8i_h3_r_ccu_clks), @@ -198,6 +298,13 @@ static void __init sunxi_r_ccu_init(struct device_node *node, sunxi_ccu_probe(node, reg, desc); } +static void __init sun8i_a83t_r_ccu_setup(struct device_node *node) +{ + sunxi_r_ccu_init(node, &sun8i_a83t_r_ccu_desc); +} +CLK_OF_DECLARE(sun8i_a83t_r_ccu, "allwinner,sun8i-a83t-r-ccu", + sun8i_a83t_r_ccu_setup); + static void __init sun8i_h3_r_ccu_setup(struct device_node *node) { sunxi_r_ccu_init(node, &sun8i_h3_r_ccu_desc); -- cgit v1.2.1 From 06e226c7fb233f676b01b144d0b321ebe510fdcd Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 2 Jun 2017 15:30:06 -0700 Subject: clk: sunxi-ng: Move all clock types to a library We've run into kconfig missing dependency errors in the sunxi-ng code a couple times now. Each time the fix is to find the missing select statement and add it to the Kconfig entry for a particular SoC driver. Given that all this code is builtin (non-modular) we don't need to do this complicated dependency tracking in Kconfig. Instead we can move all the "library"ish code to be compiled as lib-y instead of obj-y, let the linker throw away unused code in the resulting vmlinux, and drop all the Kconfig stuff we use to track clock types. Suggested-by: Arnd Bergmann Signed-off-by: Stephen Boyd [Maxime: added lib.a to obj-y, added the comment] Signed-off-by: Maxime Ripard --- drivers/clk/sunxi-ng/Kconfig | 119 ------------------------------------------ drivers/clk/sunxi-ng/Makefile | 35 ++++++++----- 2 files changed, 22 insertions(+), 132 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig index 67acef3d2494..7342928c35cd 100644 --- a/drivers/clk/sunxi-ng/Kconfig +++ b/drivers/clk/sunxi-ng/Kconfig @@ -6,174 +6,55 @@ config SUNXI_CCU if SUNXI_CCU -# Base clock types - -config SUNXI_CCU_DIV - bool - select SUNXI_CCU_MUX - -config SUNXI_CCU_FRAC - bool - -config SUNXI_CCU_GATE - def_bool y - -config SUNXI_CCU_MUX - bool - -config SUNXI_CCU_MULT - bool - select SUNXI_CCU_MUX - -config SUNXI_CCU_PHASE - bool - -# Multi-factor clocks - -config SUNXI_CCU_NK - bool - select SUNXI_CCU_GATE - -config SUNXI_CCU_NKM - bool - select SUNXI_CCU_GATE - -config SUNXI_CCU_NKMP - bool - select SUNXI_CCU_GATE - -config SUNXI_CCU_NM - bool - select SUNXI_CCU_FRAC - select SUNXI_CCU_GATE - -config SUNXI_CCU_MP - bool - select SUNXI_CCU_GATE - select SUNXI_CCU_MUX - -# 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 depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST config SUN5I_CCU bool "Support for the Allwinner sun5i family CCM" - select SUNXI_CCU_DIV - select SUNXI_CCU_MULT - select SUNXI_CCU_NK - select SUNXI_CCU_NKM - select SUNXI_CCU_NM - select SUNXI_CCU_MP - select SUNXI_CCU_PHASE default MACH_SUN5I depends on MACH_SUN5I || COMPILE_TEST config SUN6I_A31_CCU bool "Support for the Allwinner A31/A31s 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 MACH_SUN6I depends on MACH_SUN6I || COMPILE_TEST config SUN8I_A23_CCU bool "Support for the Allwinner A23 CCU" - select SUNXI_CCU_DIV - select SUNXI_CCU_MULT - 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 MACH_SUN8I depends on MACH_SUN8I || COMPILE_TEST config SUN8I_A33_CCU bool "Support for the Allwinner A33 CCU" - select SUNXI_CCU_DIV - select SUNXI_CCU_MULT - 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 MACH_SUN8I depends on MACH_SUN8I || COMPILE_TEST config SUN8I_A83T_CCU bool "Support for the Allwinner A83T CCU" - select SUNXI_CCU_DIV - select SUNXI_CCU_GATE - select SUNXI_CCU_MP - select SUNXI_CCU_MULT - select SUNXI_CCU_MUX - select SUNXI_CCU_NKMP - select SUNXI_CCU_NM - select SUNXI_CCU_PHASE default MACH_SUN8I config SUN8I_H3_CCU bool "Support for the Allwinner H3 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 MACH_SUN8I || (ARM64 && ARCH_SUNXI) depends on MACH_SUN8I || (ARM64 && ARCH_SUNXI) || COMPILE_TEST config SUN8I_V3S_CCU bool "Support for the Allwinner V3s 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 MACH_SUN8I depends on MACH_SUN8I || COMPILE_TEST config SUN8I_DE2_CCU bool "Support for the Allwinner SoCs DE2 CCU" - select SUNXI_CCU_DIV - select SUNXI_CCU_GATE config SUN9I_A80_CCU bool "Support for the Allwinner A80 CCU" - select SUNXI_CCU_DIV - select SUNXI_CCU_MULT - select SUNXI_CCU_GATE - select SUNXI_CCU_NKMP - select SUNXI_CCU_NM - select SUNXI_CCU_MP - select SUNXI_CCU_PHASE default MACH_SUN9I depends on MACH_SUN9I || COMPILE_TEST config SUN8I_R_CCU bool "Support for Allwinner SoCs' PRCM CCUs" - select SUNXI_CCU_DIV - select SUNXI_CCU_GATE - select SUNXI_CCU_MP default MACH_SUN8I || (ARCH_SUNXI && ARM64) endif diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile index 0185c6ffadcb..0c45fa50283d 100644 --- a/drivers/clk/sunxi-ng/Makefile +++ b/drivers/clk/sunxi-ng/Makefile @@ -1,21 +1,21 @@ # Common objects -obj-$(CONFIG_SUNXI_CCU) += ccu_common.o -obj-$(CONFIG_SUNXI_CCU) += ccu_reset.o +lib-$(CONFIG_SUNXI_CCU) += ccu_common.o +lib-$(CONFIG_SUNXI_CCU) += ccu_reset.o # Base clock types -obj-$(CONFIG_SUNXI_CCU_DIV) += ccu_div.o -obj-$(CONFIG_SUNXI_CCU_FRAC) += ccu_frac.o -obj-$(CONFIG_SUNXI_CCU_GATE) += ccu_gate.o -obj-$(CONFIG_SUNXI_CCU_MUX) += ccu_mux.o -obj-$(CONFIG_SUNXI_CCU_MULT) += ccu_mult.o -obj-$(CONFIG_SUNXI_CCU_PHASE) += ccu_phase.o +lib-$(CONFIG_SUNXI_CCU) += ccu_div.o +lib-$(CONFIG_SUNXI_CCU) += ccu_frac.o +lib-$(CONFIG_SUNXI_CCU) += ccu_gate.o +lib-$(CONFIG_SUNXI_CCU) += ccu_mux.o +lib-$(CONFIG_SUNXI_CCU) += ccu_mult.o +lib-$(CONFIG_SUNXI_CCU) += ccu_phase.o # Multi-factor clocks -obj-$(CONFIG_SUNXI_CCU_NK) += ccu_nk.o -obj-$(CONFIG_SUNXI_CCU_NKM) += ccu_nkm.o -obj-$(CONFIG_SUNXI_CCU_NKMP) += ccu_nkmp.o -obj-$(CONFIG_SUNXI_CCU_NM) += ccu_nm.o -obj-$(CONFIG_SUNXI_CCU_MP) += ccu_mp.o +lib-$(CONFIG_SUNXI_CCU) += ccu_nk.o +lib-$(CONFIG_SUNXI_CCU) += ccu_nkm.o +lib-$(CONFIG_SUNXI_CCU) += ccu_nkmp.o +lib-$(CONFIG_SUNXI_CCU) += ccu_nm.o +lib-$(CONFIG_SUNXI_CCU) += ccu_mp.o # SoC support obj-$(CONFIG_SUN50I_A64_CCU) += ccu-sun50i-a64.o @@ -31,3 +31,12 @@ obj-$(CONFIG_SUN8I_R_CCU) += ccu-sun8i-r.o obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80.o obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80-de.o obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80-usb.o + +# The lib-y file goals is supposed to work only in arch/*/lib or lib/. In our +# case, we want to use that goal, but even though lib.a will be properly +# generated, it will not be linked in, eventually resulting in a linker error +# for missing symbols. +# +# We can work around that by explicitly adding lib.a to the obj-y goal. This is +# an undocumented behaviour, but works well for now. +obj-$(CONFIG_SUNXI_CCU) += lib.a -- cgit v1.2.1