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 110 writel(reg | (val << cd->div.shift), 111 cd->common.base + cd->common.reg); 112 113 spin_unlock_irqrestore(cd->common.lock, flags); 114 115 return 0; 116 } 117 118 static u8 ccu_div_get_parent(struct clk_hw *hw) 119 { 120 struct ccu_div *cd = hw_to_ccu_div(hw); 121 122 return ccu_mux_helper_get_parent(&cd->common, &cd->mux); 123 } 124 125 static int ccu_div_set_parent(struct clk_hw *hw, u8 index) 126 { 127 struct ccu_div *cd = hw_to_ccu_div(hw); 128 129 return ccu_mux_helper_set_parent(&cd->common, &cd->mux, index); 130 } 131 132 const struct clk_ops ccu_div_ops = { 133 .disable = ccu_div_disable, 134 .enable = ccu_div_enable, 135 .is_enabled = ccu_div_is_enabled, 136 137 .get_parent = ccu_div_get_parent, 138 .set_parent = ccu_div_set_parent, 139 140 .determine_rate = ccu_div_determine_rate, 141 .recalc_rate = ccu_div_recalc_rate, 142 .set_rate = ccu_div_set_rate, 143 }; 144 EXPORT_SYMBOL_NS_GPL(ccu_div_ops, SUNXI_CCU); 145