1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright 2013 Emilio López |
4 | * Emilio López <emilio@elopez.com.ar> |
5 | * |
6 | * Copyright 2015 Maxime Ripard |
7 | * Maxime Ripard <maxime.ripard@free-electrons.com> |
8 | */ |
9 | |
10 | #include <linux/clk-provider.h> |
11 | #include <linux/io.h> |
12 | #include <linux/of.h> |
13 | #include <linux/of_address.h> |
14 | #include <linux/slab.h> |
15 | |
16 | #include <dt-bindings/clock/sun4i-a10-pll2.h> |
17 | |
18 | #define SUN4I_PLL2_ENABLE 31 |
19 | |
20 | #define SUN4I_PLL2_PRE_DIV_SHIFT 0 |
21 | #define SUN4I_PLL2_PRE_DIV_WIDTH 5 |
22 | #define SUN4I_PLL2_PRE_DIV_MASK GENMASK(SUN4I_PLL2_PRE_DIV_WIDTH - 1, 0) |
23 | |
24 | #define SUN4I_PLL2_N_SHIFT 8 |
25 | #define SUN4I_PLL2_N_WIDTH 7 |
26 | #define SUN4I_PLL2_N_MASK GENMASK(SUN4I_PLL2_N_WIDTH - 1, 0) |
27 | |
28 | #define SUN4I_PLL2_POST_DIV_SHIFT 26 |
29 | #define SUN4I_PLL2_POST_DIV_WIDTH 4 |
30 | #define SUN4I_PLL2_POST_DIV_MASK GENMASK(SUN4I_PLL2_POST_DIV_WIDTH - 1, 0) |
31 | |
32 | #define SUN4I_PLL2_POST_DIV_VALUE 4 |
33 | |
34 | #define SUN4I_PLL2_OUTPUTS 4 |
35 | |
36 | static DEFINE_SPINLOCK(sun4i_a10_pll2_lock); |
37 | |
38 | static void __init sun4i_pll2_setup(struct device_node *node, |
39 | int post_div_offset) |
40 | { |
41 | const char *clk_name = node->name, *parent; |
42 | struct clk **clks, *base_clk, *prediv_clk; |
43 | struct clk_onecell_data *clk_data; |
44 | struct clk_multiplier *mult; |
45 | struct clk_gate *gate; |
46 | void __iomem *reg; |
47 | u32 val; |
48 | |
49 | reg = of_io_request_and_map(device: node, index: 0, name: of_node_full_name(np: node)); |
50 | if (IS_ERR(ptr: reg)) |
51 | return; |
52 | |
53 | clk_data = kzalloc(size: sizeof(*clk_data), GFP_KERNEL); |
54 | if (!clk_data) |
55 | goto err_unmap; |
56 | |
57 | clks = kcalloc(SUN4I_PLL2_OUTPUTS, size: sizeof(struct clk *), GFP_KERNEL); |
58 | if (!clks) |
59 | goto err_free_data; |
60 | |
61 | parent = of_clk_get_parent_name(np: node, index: 0); |
62 | prediv_clk = clk_register_divider(NULL, "pll2-prediv" , |
63 | parent, 0, reg, |
64 | SUN4I_PLL2_PRE_DIV_SHIFT, |
65 | SUN4I_PLL2_PRE_DIV_WIDTH, |
66 | CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, |
67 | &sun4i_a10_pll2_lock); |
68 | if (IS_ERR(ptr: prediv_clk)) { |
69 | pr_err("Couldn't register the prediv clock\n" ); |
70 | goto err_free_array; |
71 | } |
72 | |
73 | /* Setup the gate part of the PLL2 */ |
74 | gate = kzalloc(size: sizeof(struct clk_gate), GFP_KERNEL); |
75 | if (!gate) |
76 | goto err_unregister_prediv; |
77 | |
78 | gate->reg = reg; |
79 | gate->bit_idx = SUN4I_PLL2_ENABLE; |
80 | gate->lock = &sun4i_a10_pll2_lock; |
81 | |
82 | /* Setup the multiplier part of the PLL2 */ |
83 | mult = kzalloc(size: sizeof(struct clk_multiplier), GFP_KERNEL); |
84 | if (!mult) |
85 | goto err_free_gate; |
86 | |
87 | mult->reg = reg; |
88 | mult->shift = SUN4I_PLL2_N_SHIFT; |
89 | mult->width = 7; |
90 | mult->flags = CLK_MULTIPLIER_ZERO_BYPASS | |
91 | CLK_MULTIPLIER_ROUND_CLOSEST; |
92 | mult->lock = &sun4i_a10_pll2_lock; |
93 | |
94 | parent = __clk_get_name(clk: prediv_clk); |
95 | base_clk = clk_register_composite(NULL, name: "pll2-base" , |
96 | parent_names: &parent, num_parents: 1, |
97 | NULL, NULL, |
98 | rate_hw: &mult->hw, rate_ops: &clk_multiplier_ops, |
99 | gate_hw: &gate->hw, gate_ops: &clk_gate_ops, |
100 | CLK_SET_RATE_PARENT); |
101 | if (IS_ERR(ptr: base_clk)) { |
102 | pr_err("Couldn't register the base multiplier clock\n" ); |
103 | goto err_free_multiplier; |
104 | } |
105 | |
106 | parent = __clk_get_name(clk: base_clk); |
107 | |
108 | /* |
109 | * PLL2-1x |
110 | * |
111 | * This is supposed to have a post divider, but we won't need |
112 | * to use it, we just need to initialise it to 4, and use a |
113 | * fixed divider. |
114 | */ |
115 | val = readl(addr: reg); |
116 | val &= ~(SUN4I_PLL2_POST_DIV_MASK << SUN4I_PLL2_POST_DIV_SHIFT); |
117 | val |= (SUN4I_PLL2_POST_DIV_VALUE - post_div_offset) << SUN4I_PLL2_POST_DIV_SHIFT; |
118 | writel(val, addr: reg); |
119 | |
120 | of_property_read_string_index(np: node, propname: "clock-output-names" , |
121 | SUN4I_A10_PLL2_1X, output: &clk_name); |
122 | clks[SUN4I_A10_PLL2_1X] = clk_register_fixed_factor(NULL, name: clk_name, |
123 | parent_name: parent, |
124 | CLK_SET_RATE_PARENT, |
125 | mult: 1, |
126 | SUN4I_PLL2_POST_DIV_VALUE); |
127 | WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_1X])); |
128 | |
129 | /* |
130 | * PLL2-2x |
131 | * |
132 | * This clock doesn't use the post divider, and really is just |
133 | * a fixed divider from the PLL2 base clock. |
134 | */ |
135 | of_property_read_string_index(np: node, propname: "clock-output-names" , |
136 | SUN4I_A10_PLL2_2X, output: &clk_name); |
137 | clks[SUN4I_A10_PLL2_2X] = clk_register_fixed_factor(NULL, name: clk_name, |
138 | parent_name: parent, |
139 | CLK_SET_RATE_PARENT, |
140 | mult: 1, div: 2); |
141 | WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_2X])); |
142 | |
143 | /* PLL2-4x */ |
144 | of_property_read_string_index(np: node, propname: "clock-output-names" , |
145 | SUN4I_A10_PLL2_4X, output: &clk_name); |
146 | clks[SUN4I_A10_PLL2_4X] = clk_register_fixed_factor(NULL, name: clk_name, |
147 | parent_name: parent, |
148 | CLK_SET_RATE_PARENT, |
149 | mult: 1, div: 1); |
150 | WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_4X])); |
151 | |
152 | /* PLL2-8x */ |
153 | of_property_read_string_index(np: node, propname: "clock-output-names" , |
154 | SUN4I_A10_PLL2_8X, output: &clk_name); |
155 | clks[SUN4I_A10_PLL2_8X] = clk_register_fixed_factor(NULL, name: clk_name, |
156 | parent_name: parent, |
157 | CLK_SET_RATE_PARENT, |
158 | mult: 2, div: 1); |
159 | WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_8X])); |
160 | |
161 | clk_data->clks = clks; |
162 | clk_data->clk_num = SUN4I_PLL2_OUTPUTS; |
163 | of_clk_add_provider(np: node, clk_src_get: of_clk_src_onecell_get, data: clk_data); |
164 | |
165 | return; |
166 | |
167 | err_free_multiplier: |
168 | kfree(objp: mult); |
169 | err_free_gate: |
170 | kfree(objp: gate); |
171 | err_unregister_prediv: |
172 | clk_unregister_divider(clk: prediv_clk); |
173 | err_free_array: |
174 | kfree(objp: clks); |
175 | err_free_data: |
176 | kfree(objp: clk_data); |
177 | err_unmap: |
178 | iounmap(addr: reg); |
179 | } |
180 | |
181 | static void __init sun4i_a10_pll2_setup(struct device_node *node) |
182 | { |
183 | sun4i_pll2_setup(node, post_div_offset: 0); |
184 | } |
185 | |
186 | CLK_OF_DECLARE(sun4i_a10_pll2, "allwinner,sun4i-a10-pll2-clk" , |
187 | sun4i_a10_pll2_setup); |
188 | |
189 | static void __init sun5i_a13_pll2_setup(struct device_node *node) |
190 | { |
191 | sun4i_pll2_setup(node, post_div_offset: 1); |
192 | } |
193 | |
194 | CLK_OF_DECLARE(sun5i_a13_pll2, "allwinner,sun5i-a13-pll2-clk" , |
195 | sun5i_a13_pll2_setup); |
196 | |