diff options
Diffstat (limited to 'drivers/clk/shmobile/clk-div6.c')
-rw-r--r-- | drivers/clk/shmobile/clk-div6.c | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/drivers/clk/shmobile/clk-div6.c b/drivers/clk/shmobile/clk-div6.c new file mode 100644 index 000000000000..aac4756ec52e --- /dev/null +++ b/drivers/clk/shmobile/clk-div6.c | |||
@@ -0,0 +1,185 @@ | |||
1 | /* | ||
2 | * r8a7790 Common Clock Framework support | ||
3 | * | ||
4 | * Copyright (C) 2013 Renesas Solutions Corp. | ||
5 | * | ||
6 | * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; version 2 of the License. | ||
11 | */ | ||
12 | |||
13 | #include <linux/clk-provider.h> | ||
14 | #include <linux/clkdev.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/io.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/of.h> | ||
19 | #include <linux/of_address.h> | ||
20 | |||
21 | #define CPG_DIV6_CKSTP BIT(8) | ||
22 | #define CPG_DIV6_DIV(d) ((d) & 0x3f) | ||
23 | #define CPG_DIV6_DIV_MASK 0x3f | ||
24 | |||
25 | /** | ||
26 | * struct div6_clock - MSTP gating clock | ||
27 | * @hw: handle between common and hardware-specific interfaces | ||
28 | * @reg: IO-remapped register | ||
29 | * @div: divisor value (1-64) | ||
30 | */ | ||
31 | struct div6_clock { | ||
32 | struct clk_hw hw; | ||
33 | void __iomem *reg; | ||
34 | unsigned int div; | ||
35 | }; | ||
36 | |||
37 | #define to_div6_clock(_hw) container_of(_hw, struct div6_clock, hw) | ||
38 | |||
39 | static int cpg_div6_clock_enable(struct clk_hw *hw) | ||
40 | { | ||
41 | struct div6_clock *clock = to_div6_clock(hw); | ||
42 | |||
43 | clk_writel(CPG_DIV6_DIV(clock->div - 1), clock->reg); | ||
44 | |||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | static void cpg_div6_clock_disable(struct clk_hw *hw) | ||
49 | { | ||
50 | struct div6_clock *clock = to_div6_clock(hw); | ||
51 | |||
52 | /* DIV6 clocks require the divisor field to be non-zero when stopping | ||
53 | * the clock. | ||
54 | */ | ||
55 | clk_writel(CPG_DIV6_CKSTP | CPG_DIV6_DIV(CPG_DIV6_DIV_MASK), | ||
56 | clock->reg); | ||
57 | } | ||
58 | |||
59 | static int cpg_div6_clock_is_enabled(struct clk_hw *hw) | ||
60 | { | ||
61 | struct div6_clock *clock = to_div6_clock(hw); | ||
62 | |||
63 | return !(clk_readl(clock->reg) & CPG_DIV6_CKSTP); | ||
64 | } | ||
65 | |||
66 | static unsigned long cpg_div6_clock_recalc_rate(struct clk_hw *hw, | ||
67 | unsigned long parent_rate) | ||
68 | { | ||
69 | struct div6_clock *clock = to_div6_clock(hw); | ||
70 | unsigned int div = (clk_readl(clock->reg) & CPG_DIV6_DIV_MASK) + 1; | ||
71 | |||
72 | return parent_rate / div; | ||
73 | } | ||
74 | |||
75 | static unsigned int cpg_div6_clock_calc_div(unsigned long rate, | ||
76 | unsigned long parent_rate) | ||
77 | { | ||
78 | unsigned int div; | ||
79 | |||
80 | div = DIV_ROUND_CLOSEST(parent_rate, rate); | ||
81 | return clamp_t(unsigned int, div, 1, 64); | ||
82 | } | ||
83 | |||
84 | static long cpg_div6_clock_round_rate(struct clk_hw *hw, unsigned long rate, | ||
85 | unsigned long *parent_rate) | ||
86 | { | ||
87 | unsigned int div = cpg_div6_clock_calc_div(rate, *parent_rate); | ||
88 | |||
89 | return *parent_rate / div; | ||
90 | } | ||
91 | |||
92 | static int cpg_div6_clock_set_rate(struct clk_hw *hw, unsigned long rate, | ||
93 | unsigned long parent_rate) | ||
94 | { | ||
95 | struct div6_clock *clock = to_div6_clock(hw); | ||
96 | unsigned int div = cpg_div6_clock_calc_div(rate, parent_rate); | ||
97 | |||
98 | clock->div = div; | ||
99 | |||
100 | /* Only program the new divisor if the clock isn't stopped. */ | ||
101 | if (!(clk_readl(clock->reg) & CPG_DIV6_CKSTP)) | ||
102 | clk_writel(CPG_DIV6_DIV(clock->div - 1), clock->reg); | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static const struct clk_ops cpg_div6_clock_ops = { | ||
108 | .enable = cpg_div6_clock_enable, | ||
109 | .disable = cpg_div6_clock_disable, | ||
110 | .is_enabled = cpg_div6_clock_is_enabled, | ||
111 | .recalc_rate = cpg_div6_clock_recalc_rate, | ||
112 | .round_rate = cpg_div6_clock_round_rate, | ||
113 | .set_rate = cpg_div6_clock_set_rate, | ||
114 | }; | ||
115 | |||
116 | static void __init cpg_div6_clock_init(struct device_node *np) | ||
117 | { | ||
118 | struct clk_init_data init; | ||
119 | struct div6_clock *clock; | ||
120 | const char *parent_name; | ||
121 | const char *name; | ||
122 | struct clk *clk; | ||
123 | int ret; | ||
124 | |||
125 | clock = kzalloc(sizeof(*clock), GFP_KERNEL); | ||
126 | if (!clock) { | ||
127 | pr_err("%s: failed to allocate %s DIV6 clock\n", | ||
128 | __func__, np->name); | ||
129 | return; | ||
130 | } | ||
131 | |||
132 | /* Remap the clock register and read the divisor. Disabling the | ||
133 | * clock overwrites the divisor, so we need to cache its value for the | ||
134 | * enable operation. | ||
135 | */ | ||
136 | clock->reg = of_iomap(np, 0); | ||
137 | if (clock->reg == NULL) { | ||
138 | pr_err("%s: failed to map %s DIV6 clock register\n", | ||
139 | __func__, np->name); | ||
140 | goto error; | ||
141 | } | ||
142 | |||
143 | clock->div = (clk_readl(clock->reg) & CPG_DIV6_DIV_MASK) + 1; | ||
144 | |||
145 | /* Parse the DT properties. */ | ||
146 | ret = of_property_read_string(np, "clock-output-names", &name); | ||
147 | if (ret < 0) { | ||
148 | pr_err("%s: failed to get %s DIV6 clock output name\n", | ||
149 | __func__, np->name); | ||
150 | goto error; | ||
151 | } | ||
152 | |||
153 | parent_name = of_clk_get_parent_name(np, 0); | ||
154 | if (parent_name == NULL) { | ||
155 | pr_err("%s: failed to get %s DIV6 clock parent name\n", | ||
156 | __func__, np->name); | ||
157 | goto error; | ||
158 | } | ||
159 | |||
160 | /* Register the clock. */ | ||
161 | init.name = name; | ||
162 | init.ops = &cpg_div6_clock_ops; | ||
163 | init.flags = CLK_IS_BASIC; | ||
164 | init.parent_names = &parent_name; | ||
165 | init.num_parents = 1; | ||
166 | |||
167 | clock->hw.init = &init; | ||
168 | |||
169 | clk = clk_register(NULL, &clock->hw); | ||
170 | if (IS_ERR(clk)) { | ||
171 | pr_err("%s: failed to register %s DIV6 clock (%ld)\n", | ||
172 | __func__, np->name, PTR_ERR(clk)); | ||
173 | goto error; | ||
174 | } | ||
175 | |||
176 | of_clk_add_provider(np, of_clk_src_simple_get, clk); | ||
177 | |||
178 | return; | ||
179 | |||
180 | error: | ||
181 | if (clock->reg) | ||
182 | iounmap(clock->reg); | ||
183 | kfree(clock); | ||
184 | } | ||
185 | CLK_OF_DECLARE(cpg_div6_clk, "renesas,cpg-div6-clock", cpg_div6_clock_init); | ||