1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. 4 */ 5 6 #include <linux/clk.h> 7 #include <linux/clk-provider.h> 8 #include <linux/export.h> 9 #include <linux/slab.h> 10 #include <linux/err.h> 11 12 #include "clk.h" 13 14 static u8 clk_periph_get_parent(struct clk_hw *hw) 15 { 16 struct tegra_clk_periph *periph = to_clk_periph(hw); 17 const struct clk_ops *mux_ops = periph->mux_ops; 18 struct clk_hw *mux_hw = &periph->mux.hw; 19 20 __clk_hw_set_clk(mux_hw, hw); 21 22 return mux_ops->get_parent(mux_hw); 23 } 24 25 static int clk_periph_set_parent(struct clk_hw *hw, u8 index) 26 { 27 struct tegra_clk_periph *periph = to_clk_periph(hw); 28 const struct clk_ops *mux_ops = periph->mux_ops; 29 struct clk_hw *mux_hw = &periph->mux.hw; 30 31 __clk_hw_set_clk(mux_hw, hw); 32 33 return mux_ops->set_parent(mux_hw, index); 34 } 35 36 static unsigned long clk_periph_recalc_rate(struct clk_hw *hw, 37 unsigned long parent_rate) 38 { 39 struct tegra_clk_periph *periph = to_clk_periph(hw); 40 const struct clk_ops *div_ops = periph->div_ops; 41 struct clk_hw *div_hw = &periph->divider.hw; 42 43 __clk_hw_set_clk(div_hw, hw); 44 45 return div_ops->recalc_rate(div_hw, parent_rate); 46 } 47 48 static long clk_periph_round_rate(struct clk_hw *hw, unsigned long rate, 49 unsigned long *prate) 50 { 51 struct tegra_clk_periph *periph = to_clk_periph(hw); 52 const struct clk_ops *div_ops = periph->div_ops; 53 struct clk_hw *div_hw = &periph->divider.hw; 54 55 __clk_hw_set_clk(div_hw, hw); 56 57 return div_ops->round_rate(div_hw, rate, prate); 58 } 59 60 static int clk_periph_set_rate(struct clk_hw *hw, unsigned long rate, 61 unsigned long parent_rate) 62 { 63 struct tegra_clk_periph *periph = to_clk_periph(hw); 64 const struct clk_ops *div_ops = periph->div_ops; 65 struct clk_hw *div_hw = &periph->divider.hw; 66 67 __clk_hw_set_clk(div_hw, hw); 68 69 return div_ops->set_rate(div_hw, rate, parent_rate); 70 } 71 72 static int clk_periph_is_enabled(struct clk_hw *hw) 73 { 74 struct tegra_clk_periph *periph = to_clk_periph(hw); 75 const struct clk_ops *gate_ops = periph->gate_ops; 76 struct clk_hw *gate_hw = &periph->gate.hw; 77 78 __clk_hw_set_clk(gate_hw, hw); 79 80 return gate_ops->is_enabled(gate_hw); 81 } 82 83 static int clk_periph_enable(struct clk_hw *hw) 84 { 85 struct tegra_clk_periph *periph = to_clk_periph(hw); 86 const struct clk_ops *gate_ops = periph->gate_ops; 87 struct clk_hw *gate_hw = &periph->gate.hw; 88 89 __clk_hw_set_clk(gate_hw, hw); 90 91 return gate_ops->enable(gate_hw); 92 } 93 94 static void clk_periph_disable(struct clk_hw *hw) 95 { 96 struct tegra_clk_periph *periph = to_clk_periph(hw); 97 const struct clk_ops *gate_ops = periph->gate_ops; 98 struct clk_hw *gate_hw = &periph->gate.hw; 99 100 gate_ops->disable(gate_hw); 101 } 102 103 static void clk_periph_restore_context(struct clk_hw *hw) 104 { 105 struct tegra_clk_periph *periph = to_clk_periph(hw); 106 const struct clk_ops *div_ops = periph->div_ops; 107 struct clk_hw *div_hw = &periph->divider.hw; 108 int parent_id; 109 110 parent_id = clk_hw_get_parent_index(hw); 111 if (WARN_ON(parent_id < 0)) 112 return; 113 114 if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV)) 115 div_ops->restore_context(div_hw); 116 117 clk_periph_set_parent(hw, parent_id); 118 } 119 120 const struct clk_ops tegra_clk_periph_ops = { 121 .get_parent = clk_periph_get_parent, 122 .set_parent = clk_periph_set_parent, 123 .recalc_rate = clk_periph_recalc_rate, 124 .round_rate = clk_periph_round_rate, 125 .set_rate = clk_periph_set_rate, 126 .is_enabled = clk_periph_is_enabled, 127 .enable = clk_periph_enable, 128 .disable = clk_periph_disable, 129 .restore_context = clk_periph_restore_context, 130 }; 131 132 static const struct clk_ops tegra_clk_periph_nodiv_ops = { 133 .get_parent = clk_periph_get_parent, 134 .set_parent = clk_periph_set_parent, 135 .is_enabled = clk_periph_is_enabled, 136 .enable = clk_periph_enable, 137 .disable = clk_periph_disable, 138 .restore_context = clk_periph_restore_context, 139 }; 140 141 static const struct clk_ops tegra_clk_periph_no_gate_ops = { 142 .get_parent = clk_periph_get_parent, 143 .set_parent = clk_periph_set_parent, 144 .recalc_rate = clk_periph_recalc_rate, 145 .round_rate = clk_periph_round_rate, 146 .set_rate = clk_periph_set_rate, 147 .restore_context = clk_periph_restore_context, 148 }; 149 150 static struct clk *_tegra_clk_register_periph(const char *name, 151 const char * const *parent_names, int num_parents, 152 struct tegra_clk_periph *periph, 153 void __iomem *clk_base, u32 offset, 154 unsigned long flags) 155 { 156 struct clk *clk; 157 struct clk_init_data init; 158 const struct tegra_clk_periph_regs *bank; 159 bool div = !(periph->gate.flags & TEGRA_PERIPH_NO_DIV); 160 161 if (periph->gate.flags & TEGRA_PERIPH_NO_DIV) { 162 flags |= CLK_SET_RATE_PARENT; 163 init.ops = &tegra_clk_periph_nodiv_ops; 164 } else if (periph->gate.flags & TEGRA_PERIPH_NO_GATE) 165 init.ops = &tegra_clk_periph_no_gate_ops; 166 else 167 init.ops = &tegra_clk_periph_ops; 168 169 init.name = name; 170 init.flags = flags; 171 init.parent_names = parent_names; 172 init.num_parents = num_parents; 173 174 bank = get_reg_bank(periph->gate.clk_num); 175 if (!bank) 176 return ERR_PTR(-EINVAL); 177 178 /* Data in .init is copied by clk_register(), so stack variable OK */ 179 periph->hw.init = &init; 180 periph->magic = TEGRA_CLK_PERIPH_MAGIC; 181 periph->mux.reg = clk_base + offset; 182 periph->divider.reg = div ? (clk_base + offset) : NULL; 183 periph->gate.clk_base = clk_base; 184 periph->gate.regs = bank; 185 periph->gate.enable_refcnt = periph_clk_enb_refcnt; 186 187 clk = clk_register(NULL, &periph->hw); 188 if (IS_ERR(clk)) 189 return clk; 190 191 periph->mux.hw.clk = clk; 192 periph->divider.hw.clk = div ? clk : NULL; 193 periph->gate.hw.clk = clk; 194 195 return clk; 196 } 197 198 struct clk *tegra_clk_register_periph(const char *name, 199 const char * const *parent_names, int num_parents, 200 struct tegra_clk_periph *periph, void __iomem *clk_base, 201 u32 offset, unsigned long flags) 202 { 203 return _tegra_clk_register_periph(name, parent_names, num_parents, 204 periph, clk_base, offset, flags); 205 } 206 207 struct clk *tegra_clk_register_periph_nodiv(const char *name, 208 const char * const *parent_names, int num_parents, 209 struct tegra_clk_periph *periph, void __iomem *clk_base, 210 u32 offset) 211 { 212 periph->gate.flags |= TEGRA_PERIPH_NO_DIV; 213 return _tegra_clk_register_periph(name, parent_names, num_parents, 214 periph, clk_base, offset, CLK_SET_RATE_PARENT); 215 } 216 217 struct clk *tegra_clk_register_periph_data(void __iomem *clk_base, 218 struct tegra_periph_init_data *init) 219 { 220 return _tegra_clk_register_periph(init->name, init->p.parent_names, 221 init->num_parents, &init->periph, 222 clk_base, init->offset, init->flags); 223 } 224