From d0b2f91bede3bd5e3d24dd6803e56eee959c1797 Mon Sep 17 00:00:00 2001 From: André Fabian Silva Delgado Date: Thu, 20 Oct 2016 00:10:27 -0300 Subject: Linux-libre 4.8.2-gnu --- drivers/clk/sunxi-ng/ccu_div.c | 136 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 drivers/clk/sunxi-ng/ccu_div.c (limited to 'drivers/clk/sunxi-ng/ccu_div.c') diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c new file mode 100644 index 000000000..8659b4cb6 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_div.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2016 Maxime Ripard + * Maxime Ripard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include + +#include "ccu_gate.h" +#include "ccu_div.h" + +static unsigned long ccu_div_round_rate(struct ccu_mux_internal *mux, + unsigned long parent_rate, + unsigned long rate, + 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); +} + +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; + + ccu_mux_helper_adjust_parent_for_prediv(&cd->common, &cd->mux, -1, + &parent_rate); + + return divider_recalc_rate(hw, parent_rate, val, cd->div.table, + cd->div.flags); +} + +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; + + ccu_mux_helper_adjust_parent_for_prediv(&cd->common, &cd->mux, -1, + &parent_rate); + + 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, +}; -- cgit v1.2.3-54-g00ecf