Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) 2016 Maxime Ripard
0004  * Maxime Ripard <maxime.ripard@free-electrons.com>
0005  */
0006 
0007 #include <linux/clk-provider.h>
0008 #include <linux/io.h>
0009 
0010 #include "ccu_gate.h"
0011 #include "ccu_div.h"
0012 
0013 static unsigned long ccu_div_round_rate(struct ccu_mux_internal *mux,
0014                     struct clk_hw *parent,
0015                     unsigned long *parent_rate,
0016                     unsigned long rate,
0017                     void *data)
0018 {
0019     struct ccu_div *cd = data;
0020 
0021     if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
0022         rate *= cd->fixed_post_div;
0023 
0024     rate = divider_round_rate_parent(&cd->common.hw, parent,
0025                      rate, parent_rate,
0026                      cd->div.table, cd->div.width,
0027                      cd->div.flags);
0028 
0029     if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
0030         rate /= cd->fixed_post_div;
0031 
0032     return rate;
0033 }
0034 
0035 static void ccu_div_disable(struct clk_hw *hw)
0036 {
0037     struct ccu_div *cd = hw_to_ccu_div(hw);
0038 
0039     return ccu_gate_helper_disable(&cd->common, cd->enable);
0040 }
0041 
0042 static int ccu_div_enable(struct clk_hw *hw)
0043 {
0044     struct ccu_div *cd = hw_to_ccu_div(hw);
0045 
0046     return ccu_gate_helper_enable(&cd->common, cd->enable);
0047 }
0048 
0049 static int ccu_div_is_enabled(struct clk_hw *hw)
0050 {
0051     struct ccu_div *cd = hw_to_ccu_div(hw);
0052 
0053     return ccu_gate_helper_is_enabled(&cd->common, cd->enable);
0054 }
0055 
0056 static unsigned long ccu_div_recalc_rate(struct clk_hw *hw,
0057                     unsigned long parent_rate)
0058 {
0059     struct ccu_div *cd = hw_to_ccu_div(hw);
0060     unsigned long val;
0061     u32 reg;
0062 
0063     reg = readl(cd->common.base + cd->common.reg);
0064     val = reg >> cd->div.shift;
0065     val &= (1 << cd->div.width) - 1;
0066 
0067     parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1,
0068                           parent_rate);
0069 
0070     val = divider_recalc_rate(hw, parent_rate, val, cd->div.table,
0071                   cd->div.flags, cd->div.width);
0072 
0073     if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
0074         val /= cd->fixed_post_div;
0075 
0076     return val;
0077 }
0078 
0079 static int ccu_div_determine_rate(struct clk_hw *hw,
0080                 struct clk_rate_request *req)
0081 {
0082     struct ccu_div *cd = hw_to_ccu_div(hw);
0083 
0084     return ccu_mux_helper_determine_rate(&cd->common, &cd->mux,
0085                          req, ccu_div_round_rate, cd);
0086 }
0087 
0088 static int ccu_div_set_rate(struct clk_hw *hw, unsigned long rate,
0089                unsigned long parent_rate)
0090 {
0091     struct ccu_div *cd = hw_to_ccu_div(hw);
0092     unsigned long flags;
0093     unsigned long val;
0094     u32 reg;
0095 
0096     parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1,
0097                           parent_rate);
0098 
0099     if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
0100         rate *= cd->fixed_post_div;
0101 
0102     val = divider_get_val(rate, parent_rate, cd->div.table, cd->div.width,
0103                   cd->div.flags);
0104 
0105     spin_lock_irqsave(cd->common.lock, flags);
0106 
0107     reg = readl(cd->common.base + cd->common.reg);
0108     reg &= ~GENMASK(cd->div.width + cd->div.shift - 1, cd->div.shift);
0109 
0110     writel(reg | (val << cd->div.shift),
0111            cd->common.base + cd->common.reg);
0112 
0113     spin_unlock_irqrestore(cd->common.lock, flags);
0114 
0115     return 0;
0116 }
0117 
0118 static u8 ccu_div_get_parent(struct clk_hw *hw)
0119 {
0120     struct ccu_div *cd = hw_to_ccu_div(hw);
0121 
0122     return ccu_mux_helper_get_parent(&cd->common, &cd->mux);
0123 }
0124 
0125 static int ccu_div_set_parent(struct clk_hw *hw, u8 index)
0126 {
0127     struct ccu_div *cd = hw_to_ccu_div(hw);
0128 
0129     return ccu_mux_helper_set_parent(&cd->common, &cd->mux, index);
0130 }
0131 
0132 const struct clk_ops ccu_div_ops = {
0133     .disable    = ccu_div_disable,
0134     .enable     = ccu_div_enable,
0135     .is_enabled = ccu_div_is_enabled,
0136 
0137     .get_parent = ccu_div_get_parent,
0138     .set_parent = ccu_div_set_parent,
0139 
0140     .determine_rate = ccu_div_determine_rate,
0141     .recalc_rate    = ccu_div_recalc_rate,
0142     .set_rate   = ccu_div_set_rate,
0143 };
0144 EXPORT_SYMBOL_NS_GPL(ccu_div_ops, SUNXI_CCU);