Lines Matching +full:set +full:- +full:rate +full:- +full:parent

1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2015-2020, NVIDIA CORPORATION. All rights reserved.
8 #include <linux/clk-provider.h>
57 value = readl_relaxed(emc->regs + CLK_SOURCE_EMC); in tegra210_clk_emc_get_parent()
70 * CCF assumes that neither the parent nor its rate will change during in tegra210_clk_emc_recalc_rate()
71 * ->set_rate(), so the parent rate passed in here was cached from the in tegra210_clk_emc_recalc_rate()
72 * parent before the ->set_rate() call. in tegra210_clk_emc_recalc_rate()
75 * the parent and/or parent rate have changed as part of the EMC rate in tegra210_clk_emc_recalc_rate()
76 * change sequence. Fix this by overriding the parent clock with what in tegra210_clk_emc_recalc_rate()
77 * we know to be the correct value after the rate change. in tegra210_clk_emc_recalc_rate()
81 value = readl_relaxed(emc->regs + CLK_SOURCE_EMC); in tegra210_clk_emc_recalc_rate()
89 static long tegra210_clk_emc_round_rate(struct clk_hw *hw, unsigned long rate, in tegra210_clk_emc_round_rate() argument
93 struct tegra210_clk_emc_provider *provider = emc->provider; in tegra210_clk_emc_round_rate()
96 if (!provider || !provider->configs || provider->num_configs == 0) in tegra210_clk_emc_round_rate()
99 for (i = 0; i < provider->num_configs; i++) { in tegra210_clk_emc_round_rate()
100 if (provider->configs[i].rate >= rate) in tegra210_clk_emc_round_rate()
101 return provider->configs[i].rate; in tegra210_clk_emc_round_rate()
104 return provider->configs[i - 1].rate; in tegra210_clk_emc_round_rate()
110 struct clk_hw *parent = clk_hw_get_parent_by_index(&emc->hw, index); in tegra210_clk_emc_find_parent() local
111 const char *name = clk_hw_get_name(parent); in tegra210_clk_emc_find_parent()
118 static int tegra210_clk_emc_set_rate(struct clk_hw *hw, unsigned long rate, in tegra210_clk_emc_set_rate() argument
122 struct tegra210_clk_emc_provider *provider = emc->provider; in tegra210_clk_emc_set_rate()
124 struct device *dev = provider->dev; in tegra210_clk_emc_set_rate()
125 struct clk_hw *old, *new, *parent; in tegra210_clk_emc_set_rate() local
131 if (!provider->configs || provider->num_configs == 0) in tegra210_clk_emc_set_rate()
132 return -EINVAL; in tegra210_clk_emc_set_rate()
134 for (i = 0; i < provider->num_configs; i++) { in tegra210_clk_emc_set_rate()
135 if (provider->configs[i].rate >= rate) { in tegra210_clk_emc_set_rate()
136 config = &provider->configs[i]; in tegra210_clk_emc_set_rate()
141 if (i == provider->num_configs) in tegra210_clk_emc_set_rate()
142 config = &provider->configs[i - 1]; in tegra210_clk_emc_set_rate()
145 new_idx = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, config->value); in tegra210_clk_emc_set_rate()
150 /* if the rate has changed... */ in tegra210_clk_emc_set_rate()
151 if (config->parent_rate != clk_hw_get_rate(old)) { in tegra210_clk_emc_set_rate()
178 return -EINVAL; in tegra210_clk_emc_set_rate()
184 parent = new; in tegra210_clk_emc_set_rate()
187 parent = old; in tegra210_clk_emc_set_rate()
193 dev_err(dev, "failed to get parent clock for index %u: %d\n", in tegra210_clk_emc_set_rate()
198 /* set the new parent clock to the required rate */ in tegra210_clk_emc_set_rate()
199 if (clk_get_rate(clk) != config->parent_rate) { in tegra210_clk_emc_set_rate()
200 err = clk_set_rate(clk, config->parent_rate); in tegra210_clk_emc_set_rate()
202 dev_err(dev, "failed to set rate %lu Hz for %pC: %d\n", in tegra210_clk_emc_set_rate()
203 config->parent_rate, clk, err); in tegra210_clk_emc_set_rate()
208 /* enable the new parent clock */ in tegra210_clk_emc_set_rate()
209 if (parent != old) { in tegra210_clk_emc_set_rate()
212 dev_err(dev, "failed to enable parent clock %pC: %d\n", in tegra210_clk_emc_set_rate()
218 /* update the EMC source configuration to reflect the new parent */ in tegra210_clk_emc_set_rate()
219 config->value &= ~CLK_SOURCE_EMC_2X_CLK_SRC; in tegra210_clk_emc_set_rate()
220 config->value |= FIELD_PREP(CLK_SOURCE_EMC_2X_CLK_SRC, index); in tegra210_clk_emc_set_rate()
223 * Finally, switch the EMC programming with both old and new parent in tegra210_clk_emc_set_rate()
226 err = provider->set_rate(dev, config); in tegra210_clk_emc_set_rate()
228 dev_err(dev, "failed to set EMC rate to %lu Hz: %d\n", rate, in tegra210_clk_emc_set_rate()
233 * longer need the new parent to be enabled. in tegra210_clk_emc_set_rate()
235 if (parent != old) in tegra210_clk_emc_set_rate()
241 /* reparent to new parent clock and disable the old parent clock */ in tegra210_clk_emc_set_rate()
242 if (parent != old) { in tegra210_clk_emc_set_rate()
247 "failed to get parent clock for index %u: %d\n", in tegra210_clk_emc_set_rate()
252 clk_hw_reparent(hw, parent); in tegra210_clk_emc_set_rate()
275 return ERR_PTR(-ENOMEM); in tegra210_clk_register_emc()
277 emc->regs = regs; in tegra210_clk_register_emc()
284 emc->hw.init = &init; in tegra210_clk_register_emc()
286 clk = clk_register(NULL, &emc->hw); in tegra210_clk_register_emc()
300 struct device *dev = provider->dev; in tegra210_clk_emc_attach()
304 if (!try_module_get(provider->owner)) in tegra210_clk_emc_attach()
305 return -ENODEV; in tegra210_clk_emc_attach()
307 for (i = 0; i < provider->num_configs; i++) { in tegra210_clk_emc_attach()
308 struct tegra210_clk_emc_config *config = &provider->configs[i]; in tegra210_clk_emc_attach()
309 struct clk_hw *parent; in tegra210_clk_emc_attach() local
313 div = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_DIVISOR, config->value); in tegra210_clk_emc_attach()
314 src = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, config->value); in tegra210_clk_emc_attach()
318 dev_err(dev, "invalid odd divider %u for rate %lu Hz\n", in tegra210_clk_emc_attach()
319 div, config->rate); in tegra210_clk_emc_attach()
320 err = -EINVAL; in tegra210_clk_emc_attach()
324 same_freq = config->value & CLK_SOURCE_EMC_MC_EMC_SAME_FREQ; in tegra210_clk_emc_attach()
326 if (same_freq != config->same_freq) { in tegra210_clk_emc_attach()
328 "ambiguous EMC to MC ratio for rate %lu Hz\n", in tegra210_clk_emc_attach()
329 config->rate); in tegra210_clk_emc_attach()
330 err = -EINVAL; in tegra210_clk_emc_attach()
334 parent = clk_hw_get_parent_by_index(hw, src); in tegra210_clk_emc_attach()
335 config->parent = src; in tegra210_clk_emc_attach()
338 config->parent_rate = config->rate * (1 + div / 2); in tegra210_clk_emc_attach()
340 unsigned long rate = config->rate * (1 + div / 2); in tegra210_clk_emc_attach() local
342 config->parent_rate = clk_hw_get_rate(parent); in tegra210_clk_emc_attach()
344 if (config->parent_rate != rate) { in tegra210_clk_emc_attach()
346 "rate %lu Hz does not match input\n", in tegra210_clk_emc_attach()
347 config->rate); in tegra210_clk_emc_attach()
348 err = -EINVAL; in tegra210_clk_emc_attach()
354 emc->provider = provider; in tegra210_clk_emc_attach()
359 module_put(provider->owner); in tegra210_clk_emc_attach()
368 module_put(emc->provider->owner); in tegra210_clk_emc_detach()
369 emc->provider = NULL; in tegra210_clk_emc_detach()