Commit | Line | Data |
---|---|---|
26d34431 NA |
1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) |
2 | /* | |
3 | * Copyright (c) 2019 BayLibre, SAS. | |
4 | * Author: Neil Armstrong <narmstrong@baylibre.com> | |
5 | */ | |
6 | ||
7 | #include <linux/clk-provider.h> | |
8 | #include <linux/module.h> | |
9 | ||
10 | #include "clk-regmap.h" | |
11 | #include "clk-cpu-dyndiv.h" | |
12 | ||
13 | static inline struct meson_clk_cpu_dyndiv_data * | |
14 | meson_clk_cpu_dyndiv_data(struct clk_regmap *clk) | |
15 | { | |
16 | return (struct meson_clk_cpu_dyndiv_data *)clk->data; | |
17 | } | |
18 | ||
19 | static unsigned long meson_clk_cpu_dyndiv_recalc_rate(struct clk_hw *hw, | |
20 | unsigned long prate) | |
21 | { | |
22 | struct clk_regmap *clk = to_clk_regmap(hw); | |
23 | struct meson_clk_cpu_dyndiv_data *data = meson_clk_cpu_dyndiv_data(clk); | |
24 | ||
25 | return divider_recalc_rate(hw, prate, | |
26 | meson_parm_read(clk->map, &data->div), | |
27 | NULL, 0, data->div.width); | |
28 | } | |
29 | ||
30 | static long meson_clk_cpu_dyndiv_round_rate(struct clk_hw *hw, | |
31 | unsigned long rate, | |
32 | unsigned long *prate) | |
33 | { | |
34 | struct clk_regmap *clk = to_clk_regmap(hw); | |
35 | struct meson_clk_cpu_dyndiv_data *data = meson_clk_cpu_dyndiv_data(clk); | |
36 | ||
37 | return divider_round_rate(hw, rate, prate, NULL, data->div.width, 0); | |
38 | } | |
39 | ||
40 | static int meson_clk_cpu_dyndiv_set_rate(struct clk_hw *hw, unsigned long rate, | |
41 | unsigned long parent_rate) | |
42 | { | |
43 | struct clk_regmap *clk = to_clk_regmap(hw); | |
44 | struct meson_clk_cpu_dyndiv_data *data = meson_clk_cpu_dyndiv_data(clk); | |
45 | unsigned int val; | |
46 | int ret; | |
47 | ||
48 | ret = divider_get_val(rate, parent_rate, NULL, data->div.width, 0); | |
49 | if (ret < 0) | |
50 | return ret; | |
51 | ||
52 | val = (unsigned int)ret << data->div.shift; | |
53 | ||
54 | /* Write the SYS_CPU_DYN_ENABLE bit before changing the divider */ | |
55 | meson_parm_write(clk->map, &data->dyn, 1); | |
56 | ||
57 | /* Update the divider while removing the SYS_CPU_DYN_ENABLE bit */ | |
58 | return regmap_update_bits(clk->map, data->div.reg_off, | |
59 | SETPMASK(data->div.width, data->div.shift) | | |
60 | SETPMASK(data->dyn.width, data->dyn.shift), | |
61 | val); | |
62 | }; | |
63 | ||
64 | const struct clk_ops meson_clk_cpu_dyndiv_ops = { | |
65 | .recalc_rate = meson_clk_cpu_dyndiv_recalc_rate, | |
66 | .round_rate = meson_clk_cpu_dyndiv_round_rate, | |
67 | .set_rate = meson_clk_cpu_dyndiv_set_rate, | |
68 | }; | |
69 | EXPORT_SYMBOL_GPL(meson_clk_cpu_dyndiv_ops); | |
70 | ||
71 | MODULE_DESCRIPTION("Amlogic CPU Dynamic Clock divider"); | |
72 | MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); | |
73 | MODULE_LICENSE("GPL v2"); |