1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2016 Maxime Ripard |
4 | * Maxime Ripard <maxime.ripard@free-electrons.com> |
5 | */ |
6 | |
7 | #include <linux/clk-provider.h> |
8 | #include <linux/io.h> |
9 | |
10 | #include "ccu_gate.h" |
11 | |
12 | void ccu_gate_helper_disable(struct ccu_common *common, u32 gate) |
13 | { |
14 | unsigned long flags; |
15 | u32 reg; |
16 | |
17 | if (!gate) |
18 | return; |
19 | |
20 | spin_lock_irqsave(common->lock, flags); |
21 | |
22 | reg = readl(addr: common->base + common->reg); |
23 | writel(val: reg & ~gate, addr: common->base + common->reg); |
24 | |
25 | spin_unlock_irqrestore(lock: common->lock, flags); |
26 | } |
27 | EXPORT_SYMBOL_NS_GPL(ccu_gate_helper_disable, SUNXI_CCU); |
28 | |
29 | static void ccu_gate_disable(struct clk_hw *hw) |
30 | { |
31 | struct ccu_gate *cg = hw_to_ccu_gate(hw); |
32 | |
33 | return ccu_gate_helper_disable(&cg->common, cg->enable); |
34 | } |
35 | |
36 | int ccu_gate_helper_enable(struct ccu_common *common, u32 gate) |
37 | { |
38 | unsigned long flags; |
39 | u32 reg; |
40 | |
41 | if (!gate) |
42 | return 0; |
43 | |
44 | spin_lock_irqsave(common->lock, flags); |
45 | |
46 | reg = readl(addr: common->base + common->reg); |
47 | writel(val: reg | gate, addr: common->base + common->reg); |
48 | |
49 | spin_unlock_irqrestore(lock: common->lock, flags); |
50 | |
51 | return 0; |
52 | } |
53 | EXPORT_SYMBOL_NS_GPL(ccu_gate_helper_enable, SUNXI_CCU); |
54 | |
55 | static int ccu_gate_enable(struct clk_hw *hw) |
56 | { |
57 | struct ccu_gate *cg = hw_to_ccu_gate(hw); |
58 | |
59 | return ccu_gate_helper_enable(&cg->common, cg->enable); |
60 | } |
61 | |
62 | int ccu_gate_helper_is_enabled(struct ccu_common *common, u32 gate) |
63 | { |
64 | if (!gate) |
65 | return 1; |
66 | |
67 | return readl(addr: common->base + common->reg) & gate; |
68 | } |
69 | EXPORT_SYMBOL_NS_GPL(ccu_gate_helper_is_enabled, SUNXI_CCU); |
70 | |
71 | static int ccu_gate_is_enabled(struct clk_hw *hw) |
72 | { |
73 | struct ccu_gate *cg = hw_to_ccu_gate(hw); |
74 | |
75 | return ccu_gate_helper_is_enabled(&cg->common, cg->enable); |
76 | } |
77 | |
78 | static unsigned long ccu_gate_recalc_rate(struct clk_hw *hw, |
79 | unsigned long parent_rate) |
80 | { |
81 | struct ccu_gate *cg = hw_to_ccu_gate(hw); |
82 | unsigned long rate = parent_rate; |
83 | |
84 | if (cg->common.features & CCU_FEATURE_ALL_PREDIV) |
85 | rate /= cg->common.prediv; |
86 | |
87 | return rate; |
88 | } |
89 | |
90 | static long ccu_gate_round_rate(struct clk_hw *hw, unsigned long rate, |
91 | unsigned long *prate) |
92 | { |
93 | struct ccu_gate *cg = hw_to_ccu_gate(hw); |
94 | int div = 1; |
95 | |
96 | if (cg->common.features & CCU_FEATURE_ALL_PREDIV) |
97 | div = cg->common.prediv; |
98 | |
99 | if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) { |
100 | unsigned long best_parent = rate; |
101 | |
102 | if (cg->common.features & CCU_FEATURE_ALL_PREDIV) |
103 | best_parent *= div; |
104 | *prate = clk_hw_round_rate(hw: clk_hw_get_parent(hw), rate: best_parent); |
105 | } |
106 | |
107 | return *prate / div; |
108 | } |
109 | |
110 | static int ccu_gate_set_rate(struct clk_hw *hw, unsigned long rate, |
111 | unsigned long parent_rate) |
112 | { |
113 | /* |
114 | * We must report success but we can do so unconditionally because |
115 | * clk_factor_round_rate returns values that ensure this call is a |
116 | * nop. |
117 | */ |
118 | |
119 | return 0; |
120 | } |
121 | |
122 | const struct clk_ops ccu_gate_ops = { |
123 | .disable = ccu_gate_disable, |
124 | .enable = ccu_gate_enable, |
125 | .is_enabled = ccu_gate_is_enabled, |
126 | .round_rate = ccu_gate_round_rate, |
127 | .set_rate = ccu_gate_set_rate, |
128 | .recalc_rate = ccu_gate_recalc_rate, |
129 | }; |
130 | EXPORT_SYMBOL_NS_GPL(ccu_gate_ops, SUNXI_CCU); |
131 | |