diff options
Diffstat (limited to 'drivers/clk/mmp/clk-gate.c')
-rw-r--r-- | drivers/clk/mmp/clk-gate.c | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/drivers/clk/mmp/clk-gate.c b/drivers/clk/mmp/clk-gate.c new file mode 100644 index 000000000000..adbd9d64ded2 --- /dev/null +++ b/drivers/clk/mmp/clk-gate.c | |||
@@ -0,0 +1,133 @@ | |||
1 | /* | ||
2 | * mmp gate clock operation source file | ||
3 | * | ||
4 | * Copyright (C) 2014 Marvell | ||
5 | * Chao Xie <chao.xie@marvell.com> | ||
6 | * | ||
7 | * This file is licensed under the terms of the GNU General Public | ||
8 | * License version 2. This program is licensed "as is" without any | ||
9 | * warranty of any kind, whether express or implied. | ||
10 | */ | ||
11 | |||
12 | #include <linux/clk-provider.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/io.h> | ||
15 | #include <linux/err.h> | ||
16 | #include <linux/delay.h> | ||
17 | |||
18 | #include "clk.h" | ||
19 | |||
20 | /* | ||
21 | * Some clocks will have mutiple bits to enable the clocks, and | ||
22 | * the bits to disable the clock is not same as enabling bits. | ||
23 | */ | ||
24 | |||
25 | #define to_clk_mmp_gate(hw) container_of(hw, struct mmp_clk_gate, hw) | ||
26 | |||
27 | static int mmp_clk_gate_enable(struct clk_hw *hw) | ||
28 | { | ||
29 | struct mmp_clk_gate *gate = to_clk_mmp_gate(hw); | ||
30 | struct clk *clk = hw->clk; | ||
31 | unsigned long flags = 0; | ||
32 | unsigned long rate; | ||
33 | u32 tmp; | ||
34 | |||
35 | if (gate->lock) | ||
36 | spin_lock_irqsave(gate->lock, flags); | ||
37 | |||
38 | tmp = readl(gate->reg); | ||
39 | tmp &= ~gate->mask; | ||
40 | tmp |= gate->val_enable; | ||
41 | writel(tmp, gate->reg); | ||
42 | |||
43 | if (gate->lock) | ||
44 | spin_unlock_irqrestore(gate->lock, flags); | ||
45 | |||
46 | if (gate->flags & MMP_CLK_GATE_NEED_DELAY) { | ||
47 | rate = __clk_get_rate(clk); | ||
48 | /* Need delay 2 cycles. */ | ||
49 | udelay(2000000/rate); | ||
50 | } | ||
51 | |||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | static void mmp_clk_gate_disable(struct clk_hw *hw) | ||
56 | { | ||
57 | struct mmp_clk_gate *gate = to_clk_mmp_gate(hw); | ||
58 | unsigned long flags = 0; | ||
59 | u32 tmp; | ||
60 | |||
61 | if (gate->lock) | ||
62 | spin_lock_irqsave(gate->lock, flags); | ||
63 | |||
64 | tmp = readl(gate->reg); | ||
65 | tmp &= ~gate->mask; | ||
66 | tmp |= gate->val_disable; | ||
67 | writel(tmp, gate->reg); | ||
68 | |||
69 | if (gate->lock) | ||
70 | spin_unlock_irqrestore(gate->lock, flags); | ||
71 | } | ||
72 | |||
73 | static int mmp_clk_gate_is_enabled(struct clk_hw *hw) | ||
74 | { | ||
75 | struct mmp_clk_gate *gate = to_clk_mmp_gate(hw); | ||
76 | unsigned long flags = 0; | ||
77 | u32 tmp; | ||
78 | |||
79 | if (gate->lock) | ||
80 | spin_lock_irqsave(gate->lock, flags); | ||
81 | |||
82 | tmp = readl(gate->reg); | ||
83 | |||
84 | if (gate->lock) | ||
85 | spin_unlock_irqrestore(gate->lock, flags); | ||
86 | |||
87 | return (tmp & gate->mask) == gate->val_enable; | ||
88 | } | ||
89 | |||
90 | const struct clk_ops mmp_clk_gate_ops = { | ||
91 | .enable = mmp_clk_gate_enable, | ||
92 | .disable = mmp_clk_gate_disable, | ||
93 | .is_enabled = mmp_clk_gate_is_enabled, | ||
94 | }; | ||
95 | |||
96 | struct clk *mmp_clk_register_gate(struct device *dev, const char *name, | ||
97 | const char *parent_name, unsigned long flags, | ||
98 | void __iomem *reg, u32 mask, u32 val_enable, u32 val_disable, | ||
99 | unsigned int gate_flags, spinlock_t *lock) | ||
100 | { | ||
101 | struct mmp_clk_gate *gate; | ||
102 | struct clk *clk; | ||
103 | struct clk_init_data init; | ||
104 | |||
105 | /* allocate the gate */ | ||
106 | gate = kzalloc(sizeof(*gate), GFP_KERNEL); | ||
107 | if (!gate) { | ||
108 | pr_err("%s:%s could not allocate gate clk\n", __func__, name); | ||
109 | return ERR_PTR(-ENOMEM); | ||
110 | } | ||
111 | |||
112 | init.name = name; | ||
113 | init.ops = &mmp_clk_gate_ops; | ||
114 | init.flags = flags | CLK_IS_BASIC; | ||
115 | init.parent_names = (parent_name ? &parent_name : NULL); | ||
116 | init.num_parents = (parent_name ? 1 : 0); | ||
117 | |||
118 | /* struct clk_gate assignments */ | ||
119 | gate->reg = reg; | ||
120 | gate->mask = mask; | ||
121 | gate->val_enable = val_enable; | ||
122 | gate->val_disable = val_disable; | ||
123 | gate->flags = gate_flags; | ||
124 | gate->lock = lock; | ||
125 | gate->hw.init = &init; | ||
126 | |||
127 | clk = clk_register(dev, &gate->hw); | ||
128 | |||
129 | if (IS_ERR(clk)) | ||
130 | kfree(gate); | ||
131 | |||
132 | return clk; | ||
133 | } | ||