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.h> 18 #include <linux/clk-provider.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 mux_hw->clk = hw->clk; 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 mux_hw->clk = hw->clk; 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 div_hw->clk = hw->clk; 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 div_hw->clk = hw->clk; 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 div_hw->clk = hw->clk; 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 gate_hw->clk = hw->clk; 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 gate_hw->clk = hw->clk; 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 void tegra_periph_reset_deassert(struct clk *c) 114 { 115 struct clk_hw *hw = __clk_get_hw(c); 116 struct tegra_clk_periph *periph = to_clk_periph(hw); 117 struct tegra_clk_periph_gate *gate; 118 119 if (periph->magic != TEGRA_CLK_PERIPH_MAGIC) { 120 gate = to_clk_periph_gate(hw); 121 if (gate->magic != TEGRA_CLK_PERIPH_GATE_MAGIC) { 122 WARN_ON(1); 123 return; 124 } 125 } else { 126 gate = &periph->gate; 127 } 128 129 tegra_periph_reset(gate, 0); 130 } 131 132 void tegra_periph_reset_assert(struct clk *c) 133 { 134 struct clk_hw *hw = __clk_get_hw(c); 135 struct tegra_clk_periph *periph = to_clk_periph(hw); 136 struct tegra_clk_periph_gate *gate; 137 138 if (periph->magic != TEGRA_CLK_PERIPH_MAGIC) { 139 gate = to_clk_periph_gate(hw); 140 if (gate->magic != TEGRA_CLK_PERIPH_GATE_MAGIC) { 141 WARN_ON(1); 142 return; 143 } 144 } else { 145 gate = &periph->gate; 146 } 147 148 tegra_periph_reset(gate, 1); 149 } 150 151 const struct clk_ops tegra_clk_periph_ops = { 152 .get_parent = clk_periph_get_parent, 153 .set_parent = clk_periph_set_parent, 154 .recalc_rate = clk_periph_recalc_rate, 155 .round_rate = clk_periph_round_rate, 156 .set_rate = clk_periph_set_rate, 157 .is_enabled = clk_periph_is_enabled, 158 .enable = clk_periph_enable, 159 .disable = clk_periph_disable, 160 }; 161 162 const struct clk_ops tegra_clk_periph_nodiv_ops = { 163 .get_parent = clk_periph_get_parent, 164 .set_parent = clk_periph_set_parent, 165 .is_enabled = clk_periph_is_enabled, 166 .enable = clk_periph_enable, 167 .disable = clk_periph_disable, 168 }; 169 170 static struct clk *_tegra_clk_register_periph(const char *name, 171 const char **parent_names, int num_parents, 172 struct tegra_clk_periph *periph, 173 void __iomem *clk_base, u32 offset, bool div) 174 { 175 struct clk *clk; 176 struct clk_init_data init; 177 178 init.name = name; 179 init.ops = div ? &tegra_clk_periph_ops : &tegra_clk_periph_nodiv_ops; 180 init.flags = div ? 0 : CLK_SET_RATE_PARENT; 181 init.parent_names = parent_names; 182 init.num_parents = num_parents; 183 184 /* Data in .init is copied by clk_register(), so stack variable OK */ 185 periph->hw.init = &init; 186 periph->magic = TEGRA_CLK_PERIPH_MAGIC; 187 periph->mux.reg = clk_base + offset; 188 periph->divider.reg = div ? (clk_base + offset) : NULL; 189 periph->gate.clk_base = clk_base; 190 191 clk = clk_register(NULL, &periph->hw); 192 if (IS_ERR(clk)) 193 return clk; 194 195 periph->mux.hw.clk = clk; 196 periph->divider.hw.clk = div ? clk : NULL; 197 periph->gate.hw.clk = clk; 198 199 return clk; 200 } 201 202 struct clk *tegra_clk_register_periph(const char *name, 203 const char **parent_names, int num_parents, 204 struct tegra_clk_periph *periph, void __iomem *clk_base, 205 u32 offset) 206 { 207 return _tegra_clk_register_periph(name, parent_names, num_parents, 208 periph, clk_base, offset, true); 209 } 210 211 struct clk *tegra_clk_register_periph_nodiv(const char *name, 212 const char **parent_names, int num_parents, 213 struct tegra_clk_periph *periph, void __iomem *clk_base, 214 u32 offset) 215 { 216 return _tegra_clk_register_periph(name, parent_names, num_parents, 217 periph, clk_base, offset, false); 218 } 219