0001
0002
0003
0004
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);