1 /* 2 * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms and conditions of the GNU General Public License, 6 * version 2, as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope it will be useful, but WITHOUT 9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 * more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16 17 #include <linux/clk-provider.h> 18 #include <linux/export.h> 19 #include <linux/slab.h> 20 #include <linux/err.h> 21 22 #include "clk.h" 23 24 static u8 clk_periph_get_parent(struct clk_hw *hw) 25 { 26 struct tegra_clk_periph *periph = to_clk_periph(hw); 27 const struct clk_ops *mux_ops = periph->mux_ops; 28 struct clk_hw *mux_hw = &periph->mux.hw; 29 30 __clk_hw_set_clk(mux_hw, hw); 31 32 return mux_ops->get_parent(mux_hw); 33 } 34 35 static int clk_periph_set_parent(struct clk_hw *hw, u8 index) 36 { 37 struct tegra_clk_periph *periph = to_clk_periph(hw); 38 const struct clk_ops *mux_ops = periph->mux_ops; 39 struct clk_hw *mux_hw = &periph->mux.hw; 40 41 __clk_hw_set_clk(mux_hw, hw); 42 43 return mux_ops->set_parent(mux_hw, index); 44 } 45 46 static unsigned long clk_periph_recalc_rate(struct clk_hw *hw, 47 unsigned long parent_rate) 48 { 49 struct tegra_clk_periph *periph = to_clk_periph(hw); 50 const struct clk_ops *div_ops = periph->div_ops; 51 struct clk_hw *div_hw = &periph->divider.hw; 52 53 __clk_hw_set_clk(div_hw, hw); 54 55 return div_ops->recalc_rate(div_hw, parent_rate); 56 } 57 58 static long clk_periph_round_rate(struct clk_hw *hw, unsigned long rate, 59 unsigned long *prate) 60 { 61 struct tegra_clk_periph *periph = to_clk_periph(hw); 62 const struct clk_ops *div_ops = periph->div_ops; 63 struct clk_hw *div_hw = &periph->divider.hw; 64 65 __clk_hw_set_clk(div_hw, hw); 66 67 return div_ops->round_rate(div_hw, rate, prate); 68 } 69 70 static int clk_periph_set_rate(struct clk_hw *hw, unsigned long rate, 71 unsigned long parent_rate) 72 { 73 struct tegra_clk_periph *periph = to_clk_periph(hw); 74 const struct clk_ops *div_ops = periph->div_ops; 75 struct clk_hw *div_hw = &periph->divider.hw; 76 77 __clk_hw_set_clk(div_hw, hw); 78 79 return div_ops->set_rate(div_hw, rate, parent_rate); 80 } 81 82 static int clk_periph_is_enabled(struct clk_hw *hw) 83 { 84 struct tegra_clk_periph *periph = to_clk_periph(hw); 85 const struct clk_ops *gate_ops = periph->gate_ops; 86 struct clk_hw *gate_hw = &periph->gate.hw; 87 88 __clk_hw_set_clk(gate_hw, hw); 89 90 return gate_ops->is_enabled(gate_hw); 91 } 92 93 static int clk_periph_enable(struct clk_hw *hw) 94 { 95 struct tegra_clk_periph *periph = to_clk_periph(hw); 96 const struct clk_ops *gate_ops = periph->gate_ops; 97 struct clk_hw *gate_hw = &periph->gate.hw; 98 99 __clk_hw_set_clk(gate_hw, hw); 100 101 return gate_ops->enable(gate_hw); 102 } 103 104 static void clk_periph_disable(struct clk_hw *hw) 105 { 106 struct tegra_clk_periph *periph = to_clk_periph(hw); 107 const struct clk_ops *gate_ops = periph->gate_ops; 108 struct clk_hw *gate_hw = &periph->gate.hw; 109 110 gate_ops->disable(gate_hw); 111 } 112 113 const struct clk_ops tegra_clk_periph_ops = { 114 .get_parent = clk_periph_get_parent, 115 .set_parent = clk_periph_set_parent, 116 .recalc_rate = clk_periph_recalc_rate, 117 .round_rate = clk_periph_round_rate, 118 .set_rate = clk_periph_set_rate, 119 .is_enabled = clk_periph_is_enabled, 120 .enable = clk_periph_enable, 121 .disable = clk_periph_disable, 122 }; 123 124 static const struct clk_ops tegra_clk_periph_nodiv_ops = { 125 .get_parent = clk_periph_get_parent, 126 .set_parent = clk_periph_set_parent, 127 .is_enabled = clk_periph_is_enabled, 128 .enable = clk_periph_enable, 129 .disable = clk_periph_disable, 130 }; 131 132 static const struct clk_ops tegra_clk_periph_no_gate_ops = { 133 .get_parent = clk_periph_get_parent, 134 .set_parent = clk_periph_set_parent, 135 .recalc_rate = clk_periph_recalc_rate, 136 .round_rate = clk_periph_round_rate, 137 .set_rate = clk_periph_set_rate, 138 }; 139 140 static struct clk *_tegra_clk_register_periph(const char *name, 141 const char **parent_names, int num_parents, 142 struct tegra_clk_periph *periph, 143 void __iomem *clk_base, u32 offset, 144 unsigned long flags) 145 { 146 struct clk *clk; 147 struct clk_init_data init; 148 const struct tegra_clk_periph_regs *bank; 149 bool div = !(periph->gate.flags & TEGRA_PERIPH_NO_DIV); 150 151 if (periph->gate.flags & TEGRA_PERIPH_NO_DIV) { 152 flags |= CLK_SET_RATE_PARENT; 153 init.ops = &tegra_clk_periph_nodiv_ops; 154 } else if (periph->gate.flags & TEGRA_PERIPH_NO_GATE) 155 init.ops = &tegra_clk_periph_no_gate_ops; 156 else 157 init.ops = &tegra_clk_periph_ops; 158 159 init.name = name; 160 init.flags = flags; 161 init.parent_names = parent_names; 162 init.num_parents = num_parents; 163 164 bank = get_reg_bank(periph->gate.clk_num); 165 if (!bank) 166 return ERR_PTR(-EINVAL); 167 168 /* Data in .init is copied by clk_register(), so stack variable OK */ 169 periph->hw.init = &init; 170 periph->magic = TEGRA_CLK_PERIPH_MAGIC; 171 periph->mux.reg = clk_base + offset; 172 periph->divider.reg = div ? (clk_base + offset) : NULL; 173 periph->gate.clk_base = clk_base; 174 periph->gate.regs = bank; 175 periph->gate.enable_refcnt = periph_clk_enb_refcnt; 176 177 clk = clk_register(NULL, &periph->hw); 178 if (IS_ERR(clk)) 179 return clk; 180 181 periph->mux.hw.clk = clk; 182 periph->divider.hw.clk = div ? clk : NULL; 183 periph->gate.hw.clk = clk; 184 185 return clk; 186 } 187 188 struct clk *tegra_clk_register_periph(const char *name, 189 const char **parent_names, int num_parents, 190 struct tegra_clk_periph *periph, void __iomem *clk_base, 191 u32 offset, unsigned long flags) 192 { 193 return _tegra_clk_register_periph(name, parent_names, num_parents, 194 periph, clk_base, offset, flags); 195 } 196 197 struct clk *tegra_clk_register_periph_nodiv(const char *name, 198 const char **parent_names, int num_parents, 199 struct tegra_clk_periph *periph, void __iomem *clk_base, 200 u32 offset) 201 { 202 periph->gate.flags |= TEGRA_PERIPH_NO_DIV; 203 return _tegra_clk_register_periph(name, parent_names, num_parents, 204 periph, clk_base, offset, CLK_SET_RATE_PARENT); 205 } 206