diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 01:02:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 01:02:30 +0000 |
commit | 76cb841cb886eef6b3bee341a2266c76578724ad (patch) | |
tree | f5892e5ba6cc11949952a6ce4ecbe6d516d6ce58 /drivers/clk/sunxi-ng/ccu_div.c | |
parent | Initial commit. (diff) | |
download | linux-76cb841cb886eef6b3bee341a2266c76578724ad.tar.xz linux-76cb841cb886eef6b3bee341a2266c76578724ad.zip |
Adding upstream version 4.19.249.upstream/4.19.249
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/clk/sunxi-ng/ccu_div.c')
-rw-r--r-- | drivers/clk/sunxi-ng/ccu_div.c | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c new file mode 100644 index 000000000..302a18efd --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_div.c @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2016 Maxime Ripard + * Maxime Ripard <maxime.ripard@free-electrons.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/clk-provider.h> + +#include "ccu_gate.h" +#include "ccu_div.h" + +static unsigned long ccu_div_round_rate(struct ccu_mux_internal *mux, + struct clk_hw *parent, + unsigned long *parent_rate, + unsigned long rate, + void *data) +{ + struct ccu_div *cd = data; + + if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate *= cd->fixed_post_div; + + rate = divider_round_rate_parent(&cd->common.hw, parent, + rate, parent_rate, + cd->div.table, cd->div.width, + cd->div.flags); + + if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate /= cd->fixed_post_div; + + return rate; +} + +static void ccu_div_disable(struct clk_hw *hw) +{ + struct ccu_div *cd = hw_to_ccu_div(hw); + + return ccu_gate_helper_disable(&cd->common, cd->enable); +} + +static int ccu_div_enable(struct clk_hw *hw) +{ + struct ccu_div *cd = hw_to_ccu_div(hw); + + return ccu_gate_helper_enable(&cd->common, cd->enable); +} + +static int ccu_div_is_enabled(struct clk_hw *hw) +{ + struct ccu_div *cd = hw_to_ccu_div(hw); + + return ccu_gate_helper_is_enabled(&cd->common, cd->enable); +} + +static unsigned long ccu_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct ccu_div *cd = hw_to_ccu_div(hw); + unsigned long val; + u32 reg; + + reg = readl(cd->common.base + cd->common.reg); + val = reg >> cd->div.shift; + val &= (1 << cd->div.width) - 1; + + parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1, + parent_rate); + + val = divider_recalc_rate(hw, parent_rate, val, cd->div.table, + cd->div.flags, cd->div.width); + + if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV) + val /= cd->fixed_post_div; + + return val; +} + +static int ccu_div_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct ccu_div *cd = hw_to_ccu_div(hw); + + return ccu_mux_helper_determine_rate(&cd->common, &cd->mux, + req, ccu_div_round_rate, cd); +} + +static int ccu_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct ccu_div *cd = hw_to_ccu_div(hw); + unsigned long flags; + unsigned long val; + u32 reg; + + parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1, + parent_rate); + + if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate *= cd->fixed_post_div; + + val = divider_get_val(rate, parent_rate, cd->div.table, cd->div.width, + cd->div.flags); + + spin_lock_irqsave(cd->common.lock, flags); + + reg = readl(cd->common.base + cd->common.reg); + reg &= ~GENMASK(cd->div.width + cd->div.shift - 1, cd->div.shift); + + writel(reg | (val << cd->div.shift), + cd->common.base + cd->common.reg); + + spin_unlock_irqrestore(cd->common.lock, flags); + + return 0; +} + +static u8 ccu_div_get_parent(struct clk_hw *hw) +{ + struct ccu_div *cd = hw_to_ccu_div(hw); + + return ccu_mux_helper_get_parent(&cd->common, &cd->mux); +} + +static int ccu_div_set_parent(struct clk_hw *hw, u8 index) +{ + struct ccu_div *cd = hw_to_ccu_div(hw); + + return ccu_mux_helper_set_parent(&cd->common, &cd->mux, index); +} + +const struct clk_ops ccu_div_ops = { + .disable = ccu_div_disable, + .enable = ccu_div_enable, + .is_enabled = ccu_div_is_enabled, + + .get_parent = ccu_div_get_parent, + .set_parent = ccu_div_set_parent, + + .determine_rate = ccu_div_determine_rate, + .recalc_rate = ccu_div_recalc_rate, + .set_rate = ccu_div_set_rate, +}; |