]>
Commit | Line | Data |
---|---|---|
6b63f023 CX |
1 | /* |
2 | * mmp APB clock operation source file | |
3 | * | |
4 | * Copyright (C) 2012 Marvell | |
5 | * Chao Xie <xiechao.mail@gmail.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/kernel.h> | |
6b63f023 CX |
13 | #include <linux/io.h> |
14 | #include <linux/err.h> | |
15 | #include <linux/delay.h> | |
16 | #include <linux/slab.h> | |
17 | ||
18 | #include "clk.h" | |
19 | ||
20 | /* Common APB clock register bit definitions */ | |
21 | #define APBC_APBCLK (1 << 0) /* APB Bus Clock Enable */ | |
22 | #define APBC_FNCLK (1 << 1) /* Functional Clock Enable */ | |
23 | #define APBC_RST (1 << 2) /* Reset Generation */ | |
24 | #define APBC_POWER (1 << 7) /* Reset Generation */ | |
25 | ||
26 | #define to_clk_apbc(hw) container_of(hw, struct clk_apbc, hw) | |
27 | struct clk_apbc { | |
28 | struct clk_hw hw; | |
29 | void __iomem *base; | |
30 | unsigned int delay; | |
31 | unsigned int flags; | |
32 | spinlock_t *lock; | |
33 | }; | |
34 | ||
35 | static int clk_apbc_prepare(struct clk_hw *hw) | |
36 | { | |
37 | struct clk_apbc *apbc = to_clk_apbc(hw); | |
38 | unsigned int data; | |
39 | unsigned long flags = 0; | |
40 | ||
41 | /* | |
42 | * It may share same register as MUX clock, | |
43 | * and it will impact FNCLK enable. Spinlock is needed | |
44 | */ | |
45 | if (apbc->lock) | |
46 | spin_lock_irqsave(apbc->lock, flags); | |
47 | ||
48 | data = readl_relaxed(apbc->base); | |
49 | if (apbc->flags & APBC_POWER_CTRL) | |
50 | data |= APBC_POWER; | |
51 | data |= APBC_FNCLK; | |
52 | writel_relaxed(data, apbc->base); | |
53 | ||
54 | if (apbc->lock) | |
55 | spin_unlock_irqrestore(apbc->lock, flags); | |
56 | ||
57 | udelay(apbc->delay); | |
58 | ||
59 | if (apbc->lock) | |
60 | spin_lock_irqsave(apbc->lock, flags); | |
61 | ||
62 | data = readl_relaxed(apbc->base); | |
63 | data |= APBC_APBCLK; | |
64 | writel_relaxed(data, apbc->base); | |
65 | ||
66 | if (apbc->lock) | |
67 | spin_unlock_irqrestore(apbc->lock, flags); | |
68 | ||
69 | udelay(apbc->delay); | |
70 | ||
71 | if (!(apbc->flags & APBC_NO_BUS_CTRL)) { | |
72 | if (apbc->lock) | |
73 | spin_lock_irqsave(apbc->lock, flags); | |
74 | ||
75 | data = readl_relaxed(apbc->base); | |
76 | data &= ~APBC_RST; | |
77 | writel_relaxed(data, apbc->base); | |
78 | ||
79 | if (apbc->lock) | |
80 | spin_unlock_irqrestore(apbc->lock, flags); | |
81 | } | |
82 | ||
83 | return 0; | |
84 | } | |
85 | ||
86 | static void clk_apbc_unprepare(struct clk_hw *hw) | |
87 | { | |
88 | struct clk_apbc *apbc = to_clk_apbc(hw); | |
89 | unsigned long data; | |
90 | unsigned long flags = 0; | |
91 | ||
92 | if (apbc->lock) | |
93 | spin_lock_irqsave(apbc->lock, flags); | |
94 | ||
95 | data = readl_relaxed(apbc->base); | |
96 | if (apbc->flags & APBC_POWER_CTRL) | |
97 | data &= ~APBC_POWER; | |
98 | data &= ~APBC_FNCLK; | |
99 | writel_relaxed(data, apbc->base); | |
100 | ||
101 | if (apbc->lock) | |
102 | spin_unlock_irqrestore(apbc->lock, flags); | |
103 | ||
104 | udelay(10); | |
105 | ||
106 | if (apbc->lock) | |
107 | spin_lock_irqsave(apbc->lock, flags); | |
108 | ||
109 | data = readl_relaxed(apbc->base); | |
110 | data &= ~APBC_APBCLK; | |
111 | writel_relaxed(data, apbc->base); | |
112 | ||
113 | if (apbc->lock) | |
114 | spin_unlock_irqrestore(apbc->lock, flags); | |
115 | } | |
116 | ||
52127755 | 117 | static struct clk_ops clk_apbc_ops = { |
6b63f023 CX |
118 | .prepare = clk_apbc_prepare, |
119 | .unprepare = clk_apbc_unprepare, | |
120 | }; | |
121 | ||
122 | struct clk *mmp_clk_register_apbc(const char *name, const char *parent_name, | |
123 | void __iomem *base, unsigned int delay, | |
124 | unsigned int apbc_flags, spinlock_t *lock) | |
125 | { | |
126 | struct clk_apbc *apbc; | |
127 | struct clk *clk; | |
128 | struct clk_init_data init; | |
129 | ||
130 | apbc = kzalloc(sizeof(*apbc), GFP_KERNEL); | |
131 | if (!apbc) | |
132 | return NULL; | |
133 | ||
134 | init.name = name; | |
135 | init.ops = &clk_apbc_ops; | |
136 | init.flags = CLK_SET_RATE_PARENT; | |
137 | init.parent_names = (parent_name ? &parent_name : NULL); | |
138 | init.num_parents = (parent_name ? 1 : 0); | |
139 | ||
140 | apbc->base = base; | |
141 | apbc->delay = delay; | |
142 | apbc->flags = apbc_flags; | |
143 | apbc->lock = lock; | |
144 | apbc->hw.init = &init; | |
145 | ||
146 | clk = clk_register(NULL, &apbc->hw); | |
147 | if (IS_ERR(clk)) | |
148 | kfree(apbc); | |
149 | ||
150 | return clk; | |
151 | } |