diff options
author | Thierry Reding <treding@nvidia.com> | 2015-04-20 08:34:57 -0400 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2016-04-28 06:41:47 -0400 |
commit | 1ec7032ad517714108cc53a6ee7067276ca21e80 (patch) | |
tree | e852e5e9f0788c4dc448f0c9fe41c43e533a82f8 | |
parent | 07314fc108e195044c074f4b443974b5aaa2d5d7 (diff) |
clk: tegra: Add fixed factor peripheral clock type
Some of the peripheral clocks on Tegra are derived from one of the top-
level PLLs with a fixed factor. Support these clocks by implementing the
->enable() and ->disable() callbacks using the peripheral clock register
banks and the ->recalc_rate() by dividing the parent rate by the fixed
factor.
Signed-off-by: Thierry Reding <treding@nvidia.com>
-rw-r--r-- | drivers/clk/tegra/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-periph-fixed.c | 120 | ||||
-rw-r--r-- | drivers/clk/tegra/clk.h | 17 |
3 files changed, 138 insertions, 0 deletions
diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index 97984c503bbb..33fd0938d79e 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile | |||
@@ -3,6 +3,7 @@ obj-y += clk-audio-sync.o | |||
3 | obj-y += clk-dfll.o | 3 | obj-y += clk-dfll.o |
4 | obj-y += clk-divider.o | 4 | obj-y += clk-divider.o |
5 | obj-y += clk-periph.o | 5 | obj-y += clk-periph.o |
6 | obj-y += clk-periph-fixed.o | ||
6 | obj-y += clk-periph-gate.o | 7 | obj-y += clk-periph-gate.o |
7 | obj-y += clk-pll.o | 8 | obj-y += clk-pll.o |
8 | obj-y += clk-pll-out.o | 9 | obj-y += clk-pll-out.o |
diff --git a/drivers/clk/tegra/clk-periph-fixed.c b/drivers/clk/tegra/clk-periph-fixed.c new file mode 100644 index 000000000000..c57dfb037b10 --- /dev/null +++ b/drivers/clk/tegra/clk-periph-fixed.c | |||
@@ -0,0 +1,120 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2015, 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-provider.h> | ||
18 | |||
19 | #include "clk.h" | ||
20 | |||
21 | static inline struct tegra_clk_periph_fixed * | ||
22 | to_tegra_clk_periph_fixed(struct clk_hw *hw) | ||
23 | { | ||
24 | return container_of(hw, struct tegra_clk_periph_fixed, hw); | ||
25 | } | ||
26 | |||
27 | static int tegra_clk_periph_fixed_is_enabled(struct clk_hw *hw) | ||
28 | { | ||
29 | struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw); | ||
30 | u32 mask = 1 << (fixed->num % 32), value; | ||
31 | |||
32 | value = readl(fixed->base + fixed->regs->enb_reg); | ||
33 | if (value & mask) { | ||
34 | value = readl(fixed->base + fixed->regs->rst_reg); | ||
35 | if ((value & mask) == 0) | ||
36 | return 1; | ||
37 | } | ||
38 | |||
39 | return 0; | ||
40 | } | ||
41 | |||
42 | static int tegra_clk_periph_fixed_enable(struct clk_hw *hw) | ||
43 | { | ||
44 | struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw); | ||
45 | u32 mask = 1 << (fixed->num % 32); | ||
46 | |||
47 | writel(mask, fixed->base + fixed->regs->enb_set_reg); | ||
48 | |||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | static void tegra_clk_periph_fixed_disable(struct clk_hw *hw) | ||
53 | { | ||
54 | struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw); | ||
55 | u32 mask = 1 << (fixed->num % 32); | ||
56 | |||
57 | writel(mask, fixed->base + fixed->regs->enb_clr_reg); | ||
58 | } | ||
59 | |||
60 | static unsigned long | ||
61 | tegra_clk_periph_fixed_recalc_rate(struct clk_hw *hw, | ||
62 | unsigned long parent_rate) | ||
63 | { | ||
64 | struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw); | ||
65 | unsigned long long rate; | ||
66 | |||
67 | rate = (unsigned long long)parent_rate * fixed->mul; | ||
68 | do_div(rate, fixed->div); | ||
69 | |||
70 | return (unsigned long)rate; | ||
71 | } | ||
72 | |||
73 | static const struct clk_ops tegra_clk_periph_fixed_ops = { | ||
74 | .is_enabled = tegra_clk_periph_fixed_is_enabled, | ||
75 | .enable = tegra_clk_periph_fixed_enable, | ||
76 | .disable = tegra_clk_periph_fixed_disable, | ||
77 | .recalc_rate = tegra_clk_periph_fixed_recalc_rate, | ||
78 | }; | ||
79 | |||
80 | struct clk *tegra_clk_register_periph_fixed(const char *name, | ||
81 | const char *parent, | ||
82 | unsigned long flags, | ||
83 | void __iomem *base, | ||
84 | unsigned int mul, | ||
85 | unsigned int div, | ||
86 | unsigned int num) | ||
87 | { | ||
88 | const struct tegra_clk_periph_regs *regs; | ||
89 | struct tegra_clk_periph_fixed *fixed; | ||
90 | struct clk_init_data init; | ||
91 | struct clk *clk; | ||
92 | |||
93 | regs = get_reg_bank(num); | ||
94 | if (!regs) | ||
95 | return ERR_PTR(-EINVAL); | ||
96 | |||
97 | fixed = kzalloc(sizeof(*fixed), GFP_KERNEL); | ||
98 | if (!fixed) | ||
99 | return ERR_PTR(-ENOMEM); | ||
100 | |||
101 | init.name = name; | ||
102 | init.flags = flags; | ||
103 | init.parent_names = parent ? &parent : NULL; | ||
104 | init.num_parents = parent ? 1 : 0; | ||
105 | init.ops = &tegra_clk_periph_fixed_ops; | ||
106 | |||
107 | fixed->base = base; | ||
108 | fixed->regs = regs; | ||
109 | fixed->mul = mul; | ||
110 | fixed->div = div; | ||
111 | fixed->num = num; | ||
112 | |||
113 | fixed->hw.init = &init; | ||
114 | |||
115 | clk = clk_register(NULL, &fixed->hw); | ||
116 | if (IS_ERR(clk)) | ||
117 | kfree(fixed); | ||
118 | |||
119 | return clk; | ||
120 | } | ||
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 3ef530a8989d..7264f17e269e 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h | |||
@@ -516,6 +516,23 @@ struct clk *tegra_clk_register_periph_gate(const char *name, | |||
516 | const char *parent_name, u8 gate_flags, void __iomem *clk_base, | 516 | const char *parent_name, u8 gate_flags, void __iomem *clk_base, |
517 | unsigned long flags, int clk_num, int *enable_refcnt); | 517 | unsigned long flags, int clk_num, int *enable_refcnt); |
518 | 518 | ||
519 | struct tegra_clk_periph_fixed { | ||
520 | struct clk_hw hw; | ||
521 | void __iomem *base; | ||
522 | const struct tegra_clk_periph_regs *regs; | ||
523 | unsigned int mul; | ||
524 | unsigned int div; | ||
525 | unsigned int num; | ||
526 | }; | ||
527 | |||
528 | struct clk *tegra_clk_register_periph_fixed(const char *name, | ||
529 | const char *parent, | ||
530 | unsigned long flags, | ||
531 | void __iomem *base, | ||
532 | unsigned int mul, | ||
533 | unsigned int div, | ||
534 | unsigned int num); | ||
535 | |||
519 | /** | 536 | /** |
520 | * struct clk-periph - peripheral clock | 537 | * struct clk-periph - peripheral clock |
521 | * | 538 | * |