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