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 int clk_periph_determine_rate(struct clk_hw *hw, 49 struct clk_rate_request *req) 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 unsigned long rate; 55 56 __clk_hw_set_clk(div_hw, hw); 57 58 rate = div_ops->round_rate(div_hw, req->rate, &req->best_parent_rate); 59 if (rate < 0) 60 return rate; 61 62 req->rate = rate; 63 return 0; 64 } 65 66 static int clk_periph_set_rate(struct clk_hw *hw, unsigned long rate, 67 unsigned long parent_rate) 68 { 69 struct tegra_clk_periph *periph = to_clk_periph(hw); 70 const struct clk_ops *div_ops = periph->div_ops; 71 struct clk_hw *div_hw = &periph->divider.hw; 72 73 __clk_hw_set_clk(div_hw, hw); 74 75 return div_ops->set_rate(div_hw, rate, parent_rate); 76 } 77 78 static int clk_periph_is_enabled(struct clk_hw *hw) 79 { 80 struct tegra_clk_periph *periph = to_clk_periph(hw); 81 const struct clk_ops *gate_ops = periph->gate_ops; 82 struct clk_hw *gate_hw = &periph->gate.hw; 83 84 __clk_hw_set_clk(gate_hw, hw); 85 86 return gate_ops->is_enabled(gate_hw); 87 } 88 89 static int clk_periph_enable(struct clk_hw *hw) 90 { 91 struct tegra_clk_periph *periph = to_clk_periph(hw); 92 const struct clk_ops *gate_ops = periph->gate_ops; 93 struct clk_hw *gate_hw = &periph->gate.hw; 94 95 __clk_hw_set_clk(gate_hw, hw); 96 97 return gate_ops->enable(gate_hw); 98 } 99 100 static void clk_periph_disable(struct clk_hw *hw) 101 { 102 struct tegra_clk_periph *periph = to_clk_periph(hw); 103 const struct clk_ops *gate_ops = periph->gate_ops; 104 struct clk_hw *gate_hw = &periph->gate.hw; 105 106 gate_ops->disable(gate_hw); 107 } 108 109 static void clk_periph_disable_unused(struct clk_hw *hw) 110 { 111 struct tegra_clk_periph *periph = to_clk_periph(hw); 112 const struct clk_ops *gate_ops = periph->gate_ops; 113 struct clk_hw *gate_hw = &periph->gate.hw; 114 115 gate_ops->disable_unused(gate_hw); 116 } 117 118 static void clk_periph_restore_context(struct clk_hw *hw) 119 { 120 struct tegra_clk_periph *periph = to_clk_periph(hw); 121 const struct clk_ops *div_ops = periph->div_ops; 122 struct clk_hw *div_hw = &periph->divider.hw; 123 int parent_id; 124 125 parent_id = clk_hw_get_parent_index(hw); 126 if (WARN_ON(parent_id < 0)) 127 return; 128 129 if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV)) 130 div_ops->restore_context(div_hw); 131 132 clk_periph_set_parent(hw, parent_id); 133 } 134 135 const struct clk_ops tegra_clk_periph_ops = { 136 .get_parent = clk_periph_get_parent, 137 .set_parent = clk_periph_set_parent, 138 .recalc_rate = clk_periph_recalc_rate, 139 .determine_rate = clk_periph_determine_rate, 140 .set_rate = clk_periph_set_rate, 141 .is_enabled = clk_periph_is_enabled, 142 .enable = clk_periph_enable, 143 .disable = clk_periph_disable, 144 .disable_unused = clk_periph_disable_unused, 145 .restore_context = clk_periph_restore_context, 146 }; 147 148 static const struct clk_ops tegra_clk_periph_nodiv_ops = { 149 .determine_rate = clk_hw_determine_rate_no_reparent, 150 .get_parent = clk_periph_get_parent, 151 .set_parent = clk_periph_set_parent, 152 .is_enabled = clk_periph_is_enabled, 153 .enable = clk_periph_enable, 154 .disable = clk_periph_disable, 155 .disable_unused = clk_periph_disable_unused, 156 .restore_context = clk_periph_restore_context, 157 }; 158 159 static const struct clk_ops tegra_clk_periph_no_gate_ops = { 160 .get_parent = clk_periph_get_parent, 161 .set_parent = clk_periph_set_parent, 162 .recalc_rate = clk_periph_recalc_rate, 163 .determine_rate = clk_periph_determine_rate, 164 .set_rate = clk_periph_set_rate, 165 .restore_context = clk_periph_restore_context, 166 }; 167 168 static struct clk *_tegra_clk_register_periph(const char *name, 169 const char * const *parent_names, int num_parents, 170 struct tegra_clk_periph *periph, 171 void __iomem *clk_base, u32 offset, 172 unsigned long flags) 173 { 174 struct clk *clk; 175 struct clk_init_data init; 176 const struct tegra_clk_periph_regs *bank; 177 bool div = !(periph->gate.flags & TEGRA_PERIPH_NO_DIV); 178 179 if (periph->gate.flags & TEGRA_PERIPH_NO_DIV) { 180 flags |= CLK_SET_RATE_PARENT; 181 init.ops = &tegra_clk_periph_nodiv_ops; 182 } else if (periph->gate.flags & TEGRA_PERIPH_NO_GATE) 183 init.ops = &tegra_clk_periph_no_gate_ops; 184 else 185 init.ops = &tegra_clk_periph_ops; 186 187 init.name = name; 188 init.flags = flags; 189 init.parent_names = parent_names; 190 init.num_parents = num_parents; 191 192 bank = get_reg_bank(periph->gate.clk_num); 193 if (!bank) 194 return ERR_PTR(-EINVAL); 195 196 /* Data in .init is copied by clk_register(), so stack variable OK */ 197 periph->hw.init = &init; 198 periph->magic = TEGRA_CLK_PERIPH_MAGIC; 199 periph->mux.reg = clk_base + offset; 200 periph->divider.reg = div ? (clk_base + offset) : NULL; 201 periph->gate.clk_base = clk_base; 202 periph->gate.regs = bank; 203 periph->gate.enable_refcnt = periph_clk_enb_refcnt; 204 205 clk = clk_register(NULL, &periph->hw); 206 if (IS_ERR(clk)) 207 return clk; 208 209 periph->mux.hw.clk = clk; 210 periph->divider.hw.clk = div ? clk : NULL; 211 periph->gate.hw.clk = clk; 212 213 return clk; 214 } 215 216 struct clk *tegra_clk_register_periph(const char *name, 217 const char * const *parent_names, int num_parents, 218 struct tegra_clk_periph *periph, void __iomem *clk_base, 219 u32 offset, unsigned long flags) 220 { 221 return _tegra_clk_register_periph(name, parent_names, num_parents, 222 periph, clk_base, offset, flags); 223 } 224 225 struct clk *tegra_clk_register_periph_nodiv(const char *name, 226 const char * const *parent_names, int num_parents, 227 struct tegra_clk_periph *periph, void __iomem *clk_base, 228 u32 offset) 229 { 230 periph->gate.flags |= TEGRA_PERIPH_NO_DIV; 231 return _tegra_clk_register_periph(name, parent_names, num_parents, 232 periph, clk_base, offset, CLK_SET_RATE_PARENT); 233 } 234 235 struct clk *tegra_clk_register_periph_data(void __iomem *clk_base, 236 struct tegra_periph_init_data *init) 237 { 238 return _tegra_clk_register_periph(init->name, init->p.parent_names, 239 init->num_parents, &init->periph, 240 clk_base, init->offset, init->flags); 241 } 242