1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * mmp gate clock operation source file |
4 | * |
5 | * Copyright (C) 2014 Marvell |
6 | * Chao Xie <chao.xie@marvell.com> |
7 | */ |
8 | |
9 | #include <linux/clk-provider.h> |
10 | #include <linux/slab.h> |
11 | #include <linux/io.h> |
12 | #include <linux/err.h> |
13 | #include <linux/delay.h> |
14 | |
15 | #include "clk.h" |
16 | |
17 | /* |
18 | * Some clocks will have mutiple bits to enable the clocks, and |
19 | * the bits to disable the clock is not same as enabling bits. |
20 | */ |
21 | |
22 | #define to_clk_mmp_gate(hw) container_of(hw, struct mmp_clk_gate, hw) |
23 | |
24 | static int mmp_clk_gate_enable(struct clk_hw *hw) |
25 | { |
26 | struct mmp_clk_gate *gate = to_clk_mmp_gate(hw); |
27 | unsigned long flags = 0; |
28 | unsigned long rate; |
29 | u32 tmp; |
30 | |
31 | if (gate->lock) |
32 | spin_lock_irqsave(gate->lock, flags); |
33 | |
34 | tmp = readl(addr: gate->reg); |
35 | tmp &= ~gate->mask; |
36 | tmp |= gate->val_enable; |
37 | writel(val: tmp, addr: gate->reg); |
38 | |
39 | if (gate->lock) |
40 | spin_unlock_irqrestore(lock: gate->lock, flags); |
41 | |
42 | if (gate->flags & MMP_CLK_GATE_NEED_DELAY) { |
43 | rate = clk_hw_get_rate(hw); |
44 | /* Need delay 2 cycles. */ |
45 | udelay(2000000/rate); |
46 | } |
47 | |
48 | return 0; |
49 | } |
50 | |
51 | static void mmp_clk_gate_disable(struct clk_hw *hw) |
52 | { |
53 | struct mmp_clk_gate *gate = to_clk_mmp_gate(hw); |
54 | unsigned long flags = 0; |
55 | u32 tmp; |
56 | |
57 | if (gate->lock) |
58 | spin_lock_irqsave(gate->lock, flags); |
59 | |
60 | tmp = readl(addr: gate->reg); |
61 | tmp &= ~gate->mask; |
62 | tmp |= gate->val_disable; |
63 | writel(val: tmp, addr: gate->reg); |
64 | |
65 | if (gate->lock) |
66 | spin_unlock_irqrestore(lock: gate->lock, flags); |
67 | } |
68 | |
69 | static int mmp_clk_gate_is_enabled(struct clk_hw *hw) |
70 | { |
71 | struct mmp_clk_gate *gate = to_clk_mmp_gate(hw); |
72 | unsigned long flags = 0; |
73 | u32 tmp; |
74 | |
75 | if (gate->lock) |
76 | spin_lock_irqsave(gate->lock, flags); |
77 | |
78 | tmp = readl(addr: gate->reg); |
79 | |
80 | if (gate->lock) |
81 | spin_unlock_irqrestore(lock: gate->lock, flags); |
82 | |
83 | return (tmp & gate->mask) == gate->val_enable; |
84 | } |
85 | |
86 | const struct clk_ops mmp_clk_gate_ops = { |
87 | .enable = mmp_clk_gate_enable, |
88 | .disable = mmp_clk_gate_disable, |
89 | .is_enabled = mmp_clk_gate_is_enabled, |
90 | }; |
91 | |
92 | struct clk *mmp_clk_register_gate(struct device *dev, const char *name, |
93 | const char *parent_name, unsigned long flags, |
94 | void __iomem *reg, u32 mask, u32 val_enable, u32 val_disable, |
95 | unsigned int gate_flags, spinlock_t *lock) |
96 | { |
97 | struct mmp_clk_gate *gate; |
98 | struct clk *clk; |
99 | struct clk_init_data init; |
100 | |
101 | /* allocate the gate */ |
102 | gate = kzalloc(size: sizeof(*gate), GFP_KERNEL); |
103 | if (!gate) |
104 | return ERR_PTR(error: -ENOMEM); |
105 | |
106 | init.name = name; |
107 | init.ops = &mmp_clk_gate_ops; |
108 | init.flags = flags; |
109 | init.parent_names = (parent_name ? &parent_name : NULL); |
110 | init.num_parents = (parent_name ? 1 : 0); |
111 | |
112 | /* struct clk_gate assignments */ |
113 | gate->reg = reg; |
114 | gate->mask = mask; |
115 | gate->val_enable = val_enable; |
116 | gate->val_disable = val_disable; |
117 | gate->flags = gate_flags; |
118 | gate->lock = lock; |
119 | gate->hw.init = &init; |
120 | |
121 | clk = clk_register(dev, hw: &gate->hw); |
122 | |
123 | if (IS_ERR(ptr: clk)) |
124 | kfree(objp: gate); |
125 | |
126 | return clk; |
127 | } |
128 | |