blob: bb2dc83fc697d3f6d7548af340d2b18451de1f22 [file] [log] [blame]
Maxime Ripard98b85252016-04-25 15:22:42 +02001/*
2 * Copyright 2015 Maxime Ripard
3 *
4 * Maxime Ripard <maxime.ripard@free-electrons.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#include <linux/clk-provider.h>
Stephen Boyd62e59c42019-04-18 15:20:22 -070018#include <linux/io.h>
Maxime Ripard98b85252016-04-25 15:22:42 +020019#include <linux/kernel.h>
20#include <linux/of_address.h>
21#include <linux/reset-controller.h>
22#include <linux/slab.h>
23#include <linux/spinlock.h>
24
25struct sun4i_a10_display_clk_data {
26 bool has_div;
27 u8 num_rst;
28 u8 parents;
29
30 u8 offset_en;
31 u8 offset_div;
32 u8 offset_mux;
33 u8 offset_rst;
34
35 u8 width_div;
36 u8 width_mux;
Maxime Ripard07ea0b42016-04-02 12:28:31 +020037
38 u32 flags;
Maxime Ripard98b85252016-04-25 15:22:42 +020039};
40
41struct reset_data {
42 void __iomem *reg;
43 spinlock_t *lock;
44 struct reset_controller_dev rcdev;
45 u8 offset;
46};
47
48static DEFINE_SPINLOCK(sun4i_a10_display_lock);
49
50static inline struct reset_data *rcdev_to_reset_data(struct reset_controller_dev *rcdev)
51{
52 return container_of(rcdev, struct reset_data, rcdev);
53};
54
55static int sun4i_a10_display_assert(struct reset_controller_dev *rcdev,
56 unsigned long id)
57{
58 struct reset_data *data = rcdev_to_reset_data(rcdev);
59 unsigned long flags;
60 u32 reg;
61
62 spin_lock_irqsave(data->lock, flags);
63
64 reg = readl(data->reg);
65 writel(reg & ~BIT(data->offset + id), data->reg);
66
67 spin_unlock_irqrestore(data->lock, flags);
68
69 return 0;
70}
71
72static int sun4i_a10_display_deassert(struct reset_controller_dev *rcdev,
73 unsigned long id)
74{
75 struct reset_data *data = rcdev_to_reset_data(rcdev);
76 unsigned long flags;
77 u32 reg;
78
79 spin_lock_irqsave(data->lock, flags);
80
81 reg = readl(data->reg);
82 writel(reg | BIT(data->offset + id), data->reg);
83
84 spin_unlock_irqrestore(data->lock, flags);
85
86 return 0;
87}
88
89static int sun4i_a10_display_status(struct reset_controller_dev *rcdev,
90 unsigned long id)
91{
92 struct reset_data *data = rcdev_to_reset_data(rcdev);
93
94 return !(readl(data->reg) & BIT(data->offset + id));
95}
96
97static const struct reset_control_ops sun4i_a10_display_reset_ops = {
98 .assert = sun4i_a10_display_assert,
99 .deassert = sun4i_a10_display_deassert,
100 .status = sun4i_a10_display_status,
101};
102
103static int sun4i_a10_display_reset_xlate(struct reset_controller_dev *rcdev,
104 const struct of_phandle_args *spec)
105{
106 /* We only have a single reset signal */
107 return 0;
108}
109
110static void __init sun4i_a10_display_init(struct device_node *node,
111 const struct sun4i_a10_display_clk_data *data)
112{
113 const char *parents[4];
114 const char *clk_name = node->name;
115 struct reset_data *reset_data;
116 struct clk_divider *div = NULL;
117 struct clk_gate *gate;
118 struct resource res;
119 struct clk_mux *mux;
120 void __iomem *reg;
121 struct clk *clk;
122 int ret;
123
124 of_property_read_string(node, "clock-output-names", &clk_name);
125
126 reg = of_io_request_and_map(node, 0, of_node_full_name(node));
127 if (IS_ERR(reg)) {
128 pr_err("%s: Could not map the clock registers\n", clk_name);
129 return;
130 }
131
132 ret = of_clk_parent_fill(node, parents, data->parents);
133 if (ret != data->parents) {
134 pr_err("%s: Could not retrieve the parents\n", clk_name);
135 goto unmap;
136 }
137
138 mux = kzalloc(sizeof(*mux), GFP_KERNEL);
139 if (!mux)
140 goto unmap;
141
142 mux->reg = reg;
143 mux->shift = data->offset_mux;
144 mux->mask = (1 << data->width_mux) - 1;
145 mux->lock = &sun4i_a10_display_lock;
146
147 gate = kzalloc(sizeof(*gate), GFP_KERNEL);
148 if (!gate)
149 goto free_mux;
150
151 gate->reg = reg;
152 gate->bit_idx = data->offset_en;
153 gate->lock = &sun4i_a10_display_lock;
154
155 if (data->has_div) {
156 div = kzalloc(sizeof(*div), GFP_KERNEL);
157 if (!div)
158 goto free_gate;
159
160 div->reg = reg;
161 div->shift = data->offset_div;
162 div->width = data->width_div;
163 div->lock = &sun4i_a10_display_lock;
164 }
165
166 clk = clk_register_composite(NULL, clk_name,
167 parents, data->parents,
168 &mux->hw, &clk_mux_ops,
169 data->has_div ? &div->hw : NULL,
170 data->has_div ? &clk_divider_ops : NULL,
171 &gate->hw, &clk_gate_ops,
Maxime Ripard07ea0b42016-04-02 12:28:31 +0200172 data->flags);
Maxime Ripard98b85252016-04-25 15:22:42 +0200173 if (IS_ERR(clk)) {
174 pr_err("%s: Couldn't register the clock\n", clk_name);
175 goto free_div;
176 }
177
178 ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
179 if (ret) {
180 pr_err("%s: Couldn't register DT provider\n", clk_name);
181 goto free_clk;
182 }
183
184 if (!data->num_rst)
185 return;
186
187 reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
188 if (!reset_data)
189 goto free_of_clk;
190
191 reset_data->reg = reg;
192 reset_data->offset = data->offset_rst;
193 reset_data->lock = &sun4i_a10_display_lock;
194 reset_data->rcdev.nr_resets = data->num_rst;
195 reset_data->rcdev.ops = &sun4i_a10_display_reset_ops;
196 reset_data->rcdev.of_node = node;
197
198 if (data->num_rst == 1) {
199 reset_data->rcdev.of_reset_n_cells = 0;
200 reset_data->rcdev.of_xlate = &sun4i_a10_display_reset_xlate;
201 } else {
202 reset_data->rcdev.of_reset_n_cells = 1;
203 }
204
205 if (reset_controller_register(&reset_data->rcdev)) {
206 pr_err("%s: Couldn't register the reset controller\n",
207 clk_name);
208 goto free_reset;
209 }
210
211 return;
212
213free_reset:
214 kfree(reset_data);
215free_of_clk:
216 of_clk_del_provider(node);
217free_clk:
218 clk_unregister_composite(clk);
219free_div:
220 kfree(div);
221free_gate:
222 kfree(gate);
223free_mux:
224 kfree(mux);
225unmap:
226 iounmap(reg);
227 of_address_to_resource(node, 0, &res);
228 release_mem_region(res.start, resource_size(&res));
229}
230
231static const struct sun4i_a10_display_clk_data sun4i_a10_tcon_ch0_data __initconst = {
232 .num_rst = 2,
233 .parents = 4,
234 .offset_en = 31,
235 .offset_rst = 29,
236 .offset_mux = 24,
237 .width_mux = 2,
Maxime Ripard07ea0b42016-04-02 12:28:31 +0200238 .flags = CLK_SET_RATE_PARENT,
Maxime Ripard98b85252016-04-25 15:22:42 +0200239};
240
241static void __init sun4i_a10_tcon_ch0_setup(struct device_node *node)
242{
243 sun4i_a10_display_init(node, &sun4i_a10_tcon_ch0_data);
244}
245CLK_OF_DECLARE(sun4i_a10_tcon_ch0, "allwinner,sun4i-a10-tcon-ch0-clk",
246 sun4i_a10_tcon_ch0_setup);
247
248static const struct sun4i_a10_display_clk_data sun4i_a10_display_data __initconst = {
249 .has_div = true,
250 .num_rst = 1,
251 .parents = 3,
252 .offset_en = 31,
253 .offset_rst = 30,
254 .offset_mux = 24,
255 .offset_div = 0,
256 .width_mux = 2,
257 .width_div = 4,
258};
259
260static void __init sun4i_a10_display_setup(struct device_node *node)
261{
262 sun4i_a10_display_init(node, &sun4i_a10_display_data);
263}
264CLK_OF_DECLARE(sun4i_a10_display, "allwinner,sun4i-a10-display-clk",
265 sun4i_a10_display_setup);