1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2016 Maxime Ripard 4 * Maxime Ripard <maxime.ripard@free-electrons.com> 5 */ 6 7 #include <linux/clk-provider.h> 8 #include <linux/io.h> 9 10 #include "ccu_gate.h" 11 #include "ccu_div.h" 12 13 static int ccu_div_determine_rate_helper(struct ccu_mux_internal *mux, 14 struct clk_rate_request *req, 15 void *data) 16 { 17 struct ccu_div *cd = data; 18 int ret; 19 20 if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV) 21 req->rate *= cd->fixed_post_div; 22 23 ret = divider_determine_rate(&cd->common.hw, req, cd->div.table, 24 cd->div.width, cd->div.flags); 25 if (ret) 26 return ret; 27 28 if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV) 29 req->rate /= cd->fixed_post_div; 30 31 return 0; 32 } 33 34 static void ccu_div_disable(struct clk_hw *hw) 35 { 36 struct ccu_div *cd = hw_to_ccu_div(hw); 37 38 return ccu_gate_helper_disable(&cd->common, cd->enable); 39 } 40 41 static int ccu_div_enable(struct clk_hw *hw) 42 { 43 struct ccu_div *cd = hw_to_ccu_div(hw); 44 45 return ccu_gate_helper_enable(&cd->common, cd->enable); 46 } 47 48 static int ccu_div_is_enabled(struct clk_hw *hw) 49 { 50 struct ccu_div *cd = hw_to_ccu_div(hw); 51 52 return ccu_gate_helper_is_enabled(&cd->common, cd->enable); 53 } 54 55 static unsigned long ccu_div_recalc_rate(struct clk_hw *hw, 56 unsigned long parent_rate) 57 { 58 struct ccu_div *cd = hw_to_ccu_div(hw); 59 unsigned long val; 60 u32 reg; 61 62 reg = readl(cd->common.base + cd->common.reg); 63 val = reg >> cd->div.shift; 64 val &= (1 << cd->div.width) - 1; 65 66 parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1, 67 parent_rate); 68 69 val = divider_recalc_rate(hw, parent_rate, val, cd->div.table, 70 cd->div.flags, cd->div.width); 71 72 if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV) 73 val /= cd->fixed_post_div; 74 75 return val; 76 } 77 78 static int ccu_div_determine_rate(struct clk_hw *hw, 79 struct clk_rate_request *req) 80 { 81 struct ccu_div *cd = hw_to_ccu_div(hw); 82 83 return ccu_mux_helper_determine_rate(&cd->common, &cd->mux, 84 req, ccu_div_determine_rate_helper, cd); 85 } 86 87 static int ccu_div_set_rate(struct clk_hw *hw, unsigned long rate, 88 unsigned long parent_rate) 89 { 90 struct ccu_div *cd = hw_to_ccu_div(hw); 91 unsigned long flags; 92 unsigned long val; 93 u32 reg; 94 95 parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1, 96 parent_rate); 97 98 if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV) 99 rate *= cd->fixed_post_div; 100 101 val = divider_get_val(rate, parent_rate, cd->div.table, cd->div.width, 102 cd->div.flags); 103 104 spin_lock_irqsave(cd->common.lock, flags); 105 106 reg = readl(cd->common.base + cd->common.reg); 107 reg &= ~GENMASK(cd->div.width + cd->div.shift - 1, cd->div.shift); 108 if (cd->common.features & CCU_FEATURE_UPDATE_BIT) 109 reg |= CCU_SUNXI_UPDATE_BIT; 110 111 writel(reg | (val << cd->div.shift), 112 cd->common.base + cd->common.reg); 113 114 spin_unlock_irqrestore(cd->common.lock, flags); 115 116 return 0; 117 } 118 119 static u8 ccu_div_get_parent(struct clk_hw *hw) 120 { 121 struct ccu_div *cd = hw_to_ccu_div(hw); 122 123 return ccu_mux_helper_get_parent(&cd->common, &cd->mux); 124 } 125 126 static int ccu_div_set_parent(struct clk_hw *hw, u8 index) 127 { 128 struct ccu_div *cd = hw_to_ccu_div(hw); 129 130 return ccu_mux_helper_set_parent(&cd->common, &cd->mux, index); 131 } 132 133 const struct clk_ops ccu_div_ops = { 134 .disable = ccu_div_disable, 135 .enable = ccu_div_enable, 136 .is_enabled = ccu_div_is_enabled, 137 138 .get_parent = ccu_div_get_parent, 139 .set_parent = ccu_div_set_parent, 140 141 .determine_rate = ccu_div_determine_rate, 142 .recalc_rate = ccu_div_recalc_rate, 143 .set_rate = ccu_div_set_rate, 144 }; 145 EXPORT_SYMBOL_NS_GPL(ccu_div_ops, "SUNXI_CCU"); 146