diff options
author | Chao Xie <chao.xie@marvell.com> | 2012-08-19 22:55:11 -0400 |
---|---|---|
committer | Mike Turquette <mturquette@linaro.org> | 2012-08-28 17:14:14 -0400 |
commit | 6b63f023184e34b404b96bb9a8c4ac6692ff3fbd (patch) | |
tree | 1ad367ccdd9dcf6a87d2e30a32b910865ac14f52 /drivers/clk | |
parent | f9a6aa4303bd15bbdb24d9fe374e4e6850298460 (diff) |
clk: mmp: add mmp specific clocks
add mmp specific clocks including apbc cloks, apmu clocks,
and pll2, fraction clocks
Signed-off-by: Chao Xie <xiechao.mail@gmail.com>
Reviewed-by: Arnd Bergmann <arnd@arndb.de>
Acked-by: Haojian Zhuang <haojian.zhuang@gmail.com>
Signed-off-by: Mike Turquette <mturquette@linaro.org>
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/Makefile | 3 | ||||
-rw-r--r-- | drivers/clk/mmp/Makefile | 5 | ||||
-rw-r--r-- | drivers/clk/mmp/clk-apbc.c | 152 | ||||
-rw-r--r-- | drivers/clk/mmp/clk-apmu.c | 97 | ||||
-rw-r--r-- | drivers/clk/mmp/clk-frac.c | 153 | ||||
-rw-r--r-- | drivers/clk/mmp/clk.h | 35 |
6 files changed, 445 insertions, 0 deletions
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index e30376c4ff5d..fa5e1d210b84 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile | |||
@@ -11,6 +11,9 @@ obj-$(CONFIG_PLAT_SPEAR) += spear/ | |||
11 | obj-$(CONFIG_ARCH_U300) += clk-u300.o | 11 | obj-$(CONFIG_ARCH_U300) += clk-u300.o |
12 | obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/ | 12 | obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/ |
13 | obj-$(CONFIG_ARCH_PRIMA2) += clk-prima2.o | 13 | obj-$(CONFIG_ARCH_PRIMA2) += clk-prima2.o |
14 | ifeq ($(CONFIG_COMMON_CLK), y) | ||
15 | obj-$(CONFIG_ARCH_MMP) += mmp/ | ||
16 | endif | ||
14 | 17 | ||
15 | # Chip specific | 18 | # Chip specific |
16 | obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o | 19 | obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o |
diff --git a/drivers/clk/mmp/Makefile b/drivers/clk/mmp/Makefile new file mode 100644 index 000000000000..b5bc88cc8967 --- /dev/null +++ b/drivers/clk/mmp/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | # | ||
2 | # Makefile for mmp specific clk | ||
3 | # | ||
4 | |||
5 | obj-y += clk-apbc.o clk-apmu.o clk-frac.o | ||
diff --git a/drivers/clk/mmp/clk-apbc.c b/drivers/clk/mmp/clk-apbc.c new file mode 100644 index 000000000000..d14120eaa71f --- /dev/null +++ b/drivers/clk/mmp/clk-apbc.c | |||
@@ -0,0 +1,152 @@ | |||
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> | ||
13 | #include <linux/clk.h> | ||
14 | #include <linux/io.h> | ||
15 | #include <linux/err.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/slab.h> | ||
18 | |||
19 | #include "clk.h" | ||
20 | |||
21 | /* Common APB clock register bit definitions */ | ||
22 | #define APBC_APBCLK (1 << 0) /* APB Bus Clock Enable */ | ||
23 | #define APBC_FNCLK (1 << 1) /* Functional Clock Enable */ | ||
24 | #define APBC_RST (1 << 2) /* Reset Generation */ | ||
25 | #define APBC_POWER (1 << 7) /* Reset Generation */ | ||
26 | |||
27 | #define to_clk_apbc(hw) container_of(hw, struct clk_apbc, hw) | ||
28 | struct clk_apbc { | ||
29 | struct clk_hw hw; | ||
30 | void __iomem *base; | ||
31 | unsigned int delay; | ||
32 | unsigned int flags; | ||
33 | spinlock_t *lock; | ||
34 | }; | ||
35 | |||
36 | static int clk_apbc_prepare(struct clk_hw *hw) | ||
37 | { | ||
38 | struct clk_apbc *apbc = to_clk_apbc(hw); | ||
39 | unsigned int data; | ||
40 | unsigned long flags = 0; | ||
41 | |||
42 | /* | ||
43 | * It may share same register as MUX clock, | ||
44 | * and it will impact FNCLK enable. Spinlock is needed | ||
45 | */ | ||
46 | if (apbc->lock) | ||
47 | spin_lock_irqsave(apbc->lock, flags); | ||
48 | |||
49 | data = readl_relaxed(apbc->base); | ||
50 | if (apbc->flags & APBC_POWER_CTRL) | ||
51 | data |= APBC_POWER; | ||
52 | data |= APBC_FNCLK; | ||
53 | writel_relaxed(data, apbc->base); | ||
54 | |||
55 | if (apbc->lock) | ||
56 | spin_unlock_irqrestore(apbc->lock, flags); | ||
57 | |||
58 | udelay(apbc->delay); | ||
59 | |||
60 | if (apbc->lock) | ||
61 | spin_lock_irqsave(apbc->lock, flags); | ||
62 | |||
63 | data = readl_relaxed(apbc->base); | ||
64 | data |= APBC_APBCLK; | ||
65 | writel_relaxed(data, apbc->base); | ||
66 | |||
67 | if (apbc->lock) | ||
68 | spin_unlock_irqrestore(apbc->lock, flags); | ||
69 | |||
70 | udelay(apbc->delay); | ||
71 | |||
72 | if (!(apbc->flags & APBC_NO_BUS_CTRL)) { | ||
73 | if (apbc->lock) | ||
74 | spin_lock_irqsave(apbc->lock, flags); | ||
75 | |||
76 | data = readl_relaxed(apbc->base); | ||
77 | data &= ~APBC_RST; | ||
78 | writel_relaxed(data, apbc->base); | ||
79 | |||
80 | if (apbc->lock) | ||
81 | spin_unlock_irqrestore(apbc->lock, flags); | ||
82 | } | ||
83 | |||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static void clk_apbc_unprepare(struct clk_hw *hw) | ||
88 | { | ||
89 | struct clk_apbc *apbc = to_clk_apbc(hw); | ||
90 | unsigned long data; | ||
91 | unsigned long flags = 0; | ||
92 | |||
93 | if (apbc->lock) | ||
94 | spin_lock_irqsave(apbc->lock, flags); | ||
95 | |||
96 | data = readl_relaxed(apbc->base); | ||
97 | if (apbc->flags & APBC_POWER_CTRL) | ||
98 | data &= ~APBC_POWER; | ||
99 | data &= ~APBC_FNCLK; | ||
100 | writel_relaxed(data, apbc->base); | ||
101 | |||
102 | if (apbc->lock) | ||
103 | spin_unlock_irqrestore(apbc->lock, flags); | ||
104 | |||
105 | udelay(10); | ||
106 | |||
107 | if (apbc->lock) | ||
108 | spin_lock_irqsave(apbc->lock, flags); | ||
109 | |||
110 | data = readl_relaxed(apbc->base); | ||
111 | data &= ~APBC_APBCLK; | ||
112 | writel_relaxed(data, apbc->base); | ||
113 | |||
114 | if (apbc->lock) | ||
115 | spin_unlock_irqrestore(apbc->lock, flags); | ||
116 | } | ||
117 | |||
118 | struct clk_ops clk_apbc_ops = { | ||
119 | .prepare = clk_apbc_prepare, | ||
120 | .unprepare = clk_apbc_unprepare, | ||
121 | }; | ||
122 | |||
123 | struct clk *mmp_clk_register_apbc(const char *name, const char *parent_name, | ||
124 | void __iomem *base, unsigned int delay, | ||
125 | unsigned int apbc_flags, spinlock_t *lock) | ||
126 | { | ||
127 | struct clk_apbc *apbc; | ||
128 | struct clk *clk; | ||
129 | struct clk_init_data init; | ||
130 | |||
131 | apbc = kzalloc(sizeof(*apbc), GFP_KERNEL); | ||
132 | if (!apbc) | ||
133 | return NULL; | ||
134 | |||
135 | init.name = name; | ||
136 | init.ops = &clk_apbc_ops; | ||
137 | init.flags = CLK_SET_RATE_PARENT; | ||
138 | init.parent_names = (parent_name ? &parent_name : NULL); | ||
139 | init.num_parents = (parent_name ? 1 : 0); | ||
140 | |||
141 | apbc->base = base; | ||
142 | apbc->delay = delay; | ||
143 | apbc->flags = apbc_flags; | ||
144 | apbc->lock = lock; | ||
145 | apbc->hw.init = &init; | ||
146 | |||
147 | clk = clk_register(NULL, &apbc->hw); | ||
148 | if (IS_ERR(clk)) | ||
149 | kfree(apbc); | ||
150 | |||
151 | return clk; | ||
152 | } | ||
diff --git a/drivers/clk/mmp/clk-apmu.c b/drivers/clk/mmp/clk-apmu.c new file mode 100644 index 000000000000..abe182b2377f --- /dev/null +++ b/drivers/clk/mmp/clk-apmu.c | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
2 | * mmp AXI peripharal 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> | ||
13 | #include <linux/clk.h> | ||
14 | #include <linux/io.h> | ||
15 | #include <linux/err.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/slab.h> | ||
18 | |||
19 | #include "clk.h" | ||
20 | |||
21 | #define to_clk_apmu(clk) (container_of(clk, struct clk_apmu, clk)) | ||
22 | struct clk_apmu { | ||
23 | struct clk_hw hw; | ||
24 | void __iomem *base; | ||
25 | u32 rst_mask; | ||
26 | u32 enable_mask; | ||
27 | spinlock_t *lock; | ||
28 | }; | ||
29 | |||
30 | static int clk_apmu_enable(struct clk_hw *hw) | ||
31 | { | ||
32 | struct clk_apmu *apmu = to_clk_apmu(hw); | ||
33 | unsigned long data; | ||
34 | unsigned long flags = 0; | ||
35 | |||
36 | if (apmu->lock) | ||
37 | spin_lock_irqsave(apmu->lock, flags); | ||
38 | |||
39 | data = readl_relaxed(apmu->base) | apmu->enable_mask; | ||
40 | writel_relaxed(data, apmu->base); | ||
41 | |||
42 | if (apmu->lock) | ||
43 | spin_unlock_irqrestore(apmu->lock, flags); | ||
44 | |||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | static void clk_apmu_disable(struct clk_hw *hw) | ||
49 | { | ||
50 | struct clk_apmu *apmu = to_clk_apmu(hw); | ||
51 | unsigned long data; | ||
52 | unsigned long flags = 0; | ||
53 | |||
54 | if (apmu->lock) | ||
55 | spin_lock_irqsave(apmu->lock, flags); | ||
56 | |||
57 | data = readl_relaxed(apmu->base) & ~apmu->enable_mask; | ||
58 | writel_relaxed(data, apmu->base); | ||
59 | |||
60 | if (apmu->lock) | ||
61 | spin_unlock_irqrestore(apmu->lock, flags); | ||
62 | } | ||
63 | |||
64 | struct clk_ops clk_apmu_ops = { | ||
65 | .enable = clk_apmu_enable, | ||
66 | .disable = clk_apmu_disable, | ||
67 | }; | ||
68 | |||
69 | struct clk *mmp_clk_register_apmu(const char *name, const char *parent_name, | ||
70 | void __iomem *base, u32 enable_mask, spinlock_t *lock) | ||
71 | { | ||
72 | struct clk_apmu *apmu; | ||
73 | struct clk *clk; | ||
74 | struct clk_init_data init; | ||
75 | |||
76 | apmu = kzalloc(sizeof(*apmu), GFP_KERNEL); | ||
77 | if (!apmu) | ||
78 | return NULL; | ||
79 | |||
80 | init.name = name; | ||
81 | init.ops = &clk_apmu_ops; | ||
82 | init.flags = CLK_SET_RATE_PARENT; | ||
83 | init.parent_names = (parent_name ? &parent_name : NULL); | ||
84 | init.num_parents = (parent_name ? 1 : 0); | ||
85 | |||
86 | apmu->base = base; | ||
87 | apmu->enable_mask = enable_mask; | ||
88 | apmu->lock = lock; | ||
89 | apmu->hw.init = &init; | ||
90 | |||
91 | clk = clk_register(NULL, &apmu->hw); | ||
92 | |||
93 | if (IS_ERR(clk)) | ||
94 | kfree(apmu); | ||
95 | |||
96 | return clk; | ||
97 | } | ||
diff --git a/drivers/clk/mmp/clk-frac.c b/drivers/clk/mmp/clk-frac.c new file mode 100644 index 000000000000..80c1dd15d15c --- /dev/null +++ b/drivers/clk/mmp/clk-frac.c | |||
@@ -0,0 +1,153 @@ | |||
1 | /* | ||
2 | * mmp factor 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/clk-provider.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/io.h> | ||
15 | #include <linux/err.h> | ||
16 | |||
17 | #include "clk.h" | ||
18 | /* | ||
19 | * It is M/N clock | ||
20 | * | ||
21 | * Fout from synthesizer can be given from two equations: | ||
22 | * numerator/denominator = Fin / (Fout * factor) | ||
23 | */ | ||
24 | |||
25 | #define to_clk_factor(hw) container_of(hw, struct clk_factor, hw) | ||
26 | struct clk_factor { | ||
27 | struct clk_hw hw; | ||
28 | void __iomem *base; | ||
29 | struct clk_factor_masks *masks; | ||
30 | struct clk_factor_tbl *ftbl; | ||
31 | unsigned int ftbl_cnt; | ||
32 | }; | ||
33 | |||
34 | static long clk_factor_round_rate(struct clk_hw *hw, unsigned long drate, | ||
35 | unsigned long *prate) | ||
36 | { | ||
37 | struct clk_factor *factor = to_clk_factor(hw); | ||
38 | unsigned long rate = 0, prev_rate; | ||
39 | int i; | ||
40 | |||
41 | for (i = 0; i < factor->ftbl_cnt; i++) { | ||
42 | prev_rate = rate; | ||
43 | rate = (((*prate / 10000) * factor->ftbl[i].num) / | ||
44 | (factor->ftbl[i].den * factor->masks->factor)) * 10000; | ||
45 | if (rate > drate) | ||
46 | break; | ||
47 | } | ||
48 | if (i == 0) | ||
49 | return rate; | ||
50 | else | ||
51 | return prev_rate; | ||
52 | } | ||
53 | |||
54 | static unsigned long clk_factor_recalc_rate(struct clk_hw *hw, | ||
55 | unsigned long parent_rate) | ||
56 | { | ||
57 | struct clk_factor *factor = to_clk_factor(hw); | ||
58 | struct clk_factor_masks *masks = factor->masks; | ||
59 | unsigned int val, num, den; | ||
60 | |||
61 | val = readl_relaxed(factor->base); | ||
62 | |||
63 | /* calculate numerator */ | ||
64 | num = (val >> masks->num_shift) & masks->num_mask; | ||
65 | |||
66 | /* calculate denominator */ | ||
67 | den = (val >> masks->den_shift) & masks->num_mask; | ||
68 | |||
69 | if (!den) | ||
70 | return 0; | ||
71 | |||
72 | return (((parent_rate / 10000) * den) / | ||
73 | (num * factor->masks->factor)) * 10000; | ||
74 | } | ||
75 | |||
76 | /* Configures new clock rate*/ | ||
77 | static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate, | ||
78 | unsigned long prate) | ||
79 | { | ||
80 | struct clk_factor *factor = to_clk_factor(hw); | ||
81 | struct clk_factor_masks *masks = factor->masks; | ||
82 | int i; | ||
83 | unsigned long val; | ||
84 | unsigned long prev_rate, rate = 0; | ||
85 | |||
86 | for (i = 0; i < factor->ftbl_cnt; i++) { | ||
87 | prev_rate = rate; | ||
88 | rate = (((prate / 10000) * factor->ftbl[i].num) / | ||
89 | (factor->ftbl[i].den * factor->masks->factor)) * 10000; | ||
90 | if (rate > drate) | ||
91 | break; | ||
92 | } | ||
93 | if (i > 0) | ||
94 | i--; | ||
95 | |||
96 | val = readl_relaxed(factor->base); | ||
97 | |||
98 | val &= ~(masks->num_mask << masks->num_shift); | ||
99 | val |= (factor->ftbl[i].num & masks->num_mask) << masks->num_shift; | ||
100 | |||
101 | val &= ~(masks->den_mask << masks->den_shift); | ||
102 | val |= (factor->ftbl[i].den & masks->den_mask) << masks->den_shift; | ||
103 | |||
104 | writel_relaxed(val, factor->base); | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | static struct clk_ops clk_factor_ops = { | ||
110 | .recalc_rate = clk_factor_recalc_rate, | ||
111 | .round_rate = clk_factor_round_rate, | ||
112 | .set_rate = clk_factor_set_rate, | ||
113 | }; | ||
114 | |||
115 | struct clk *mmp_clk_register_factor(const char *name, const char *parent_name, | ||
116 | unsigned long flags, void __iomem *base, | ||
117 | struct clk_factor_masks *masks, struct clk_factor_tbl *ftbl, | ||
118 | unsigned int ftbl_cnt) | ||
119 | { | ||
120 | struct clk_factor *factor; | ||
121 | struct clk_init_data init; | ||
122 | struct clk *clk; | ||
123 | |||
124 | if (!masks) { | ||
125 | pr_err("%s: must pass a clk_factor_mask\n", __func__); | ||
126 | return ERR_PTR(-EINVAL); | ||
127 | } | ||
128 | |||
129 | factor = kzalloc(sizeof(*factor), GFP_KERNEL); | ||
130 | if (!factor) { | ||
131 | pr_err("%s: could not allocate factor clk\n", __func__); | ||
132 | return ERR_PTR(-ENOMEM); | ||
133 | } | ||
134 | |||
135 | /* struct clk_aux assignments */ | ||
136 | factor->base = base; | ||
137 | factor->masks = masks; | ||
138 | factor->ftbl = ftbl; | ||
139 | factor->ftbl_cnt = ftbl_cnt; | ||
140 | factor->hw.init = &init; | ||
141 | |||
142 | init.name = name; | ||
143 | init.ops = &clk_factor_ops; | ||
144 | init.flags = flags; | ||
145 | init.parent_names = &parent_name; | ||
146 | init.num_parents = 1; | ||
147 | |||
148 | clk = clk_register(NULL, &factor->hw); | ||
149 | if (IS_ERR_OR_NULL(clk)) | ||
150 | kfree(factor); | ||
151 | |||
152 | return clk; | ||
153 | } | ||
diff --git a/drivers/clk/mmp/clk.h b/drivers/clk/mmp/clk.h new file mode 100644 index 000000000000..ab86dd4a416a --- /dev/null +++ b/drivers/clk/mmp/clk.h | |||
@@ -0,0 +1,35 @@ | |||
1 | #ifndef __MACH_MMP_CLK_H | ||
2 | #define __MACH_MMP_CLK_H | ||
3 | |||
4 | #include <linux/clk-provider.h> | ||
5 | #include <linux/clkdev.h> | ||
6 | |||
7 | #define APBC_NO_BUS_CTRL BIT(0) | ||
8 | #define APBC_POWER_CTRL BIT(1) | ||
9 | |||
10 | struct clk_factor_masks { | ||
11 | unsigned int factor; | ||
12 | unsigned int num_mask; | ||
13 | unsigned int den_mask; | ||
14 | unsigned int num_shift; | ||
15 | unsigned int den_shift; | ||
16 | }; | ||
17 | |||
18 | struct clk_factor_tbl { | ||
19 | unsigned int num; | ||
20 | unsigned int den; | ||
21 | }; | ||
22 | |||
23 | extern struct clk *mmp_clk_register_pll2(const char *name, | ||
24 | const char *parent_name, unsigned long flags); | ||
25 | extern struct clk *mmp_clk_register_apbc(const char *name, | ||
26 | const char *parent_name, void __iomem *base, | ||
27 | unsigned int delay, unsigned int apbc_flags, spinlock_t *lock); | ||
28 | extern struct clk *mmp_clk_register_apmu(const char *name, | ||
29 | const char *parent_name, void __iomem *base, u32 enable_mask, | ||
30 | spinlock_t *lock); | ||
31 | extern struct clk *mmp_clk_register_factor(const char *name, | ||
32 | const char *parent_name, unsigned long flags, | ||
33 | void __iomem *base, struct clk_factor_masks *masks, | ||
34 | struct clk_factor_tbl *ftbl, unsigned int ftbl_cnt); | ||
35 | #endif | ||