xref: /linux/drivers/clk/sunxi/clk-sun4i-display.c (revision 0883c2c06fb5bcf5b9e008270827e63c09a88c1e)
1 /*
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>
18 #include <linux/kernel.h>
19 #include <linux/of_address.h>
20 #include <linux/reset-controller.h>
21 #include <linux/slab.h>
22 #include <linux/spinlock.h>
23 
24 struct sun4i_a10_display_clk_data {
25 	bool	has_div;
26 	u8	num_rst;
27 	u8	parents;
28 
29 	u8	offset_en;
30 	u8	offset_div;
31 	u8	offset_mux;
32 	u8	offset_rst;
33 
34 	u8	width_div;
35 	u8	width_mux;
36 };
37 
38 struct reset_data {
39 	void __iomem			*reg;
40 	spinlock_t			*lock;
41 	struct reset_controller_dev	rcdev;
42 	u8				offset;
43 };
44 
45 static DEFINE_SPINLOCK(sun4i_a10_display_lock);
46 
47 static inline struct reset_data *rcdev_to_reset_data(struct reset_controller_dev *rcdev)
48 {
49 	return container_of(rcdev, struct reset_data, rcdev);
50 };
51 
52 static int sun4i_a10_display_assert(struct reset_controller_dev *rcdev,
53 				    unsigned long id)
54 {
55 	struct reset_data *data = rcdev_to_reset_data(rcdev);
56 	unsigned long flags;
57 	u32 reg;
58 
59 	spin_lock_irqsave(data->lock, flags);
60 
61 	reg = readl(data->reg);
62 	writel(reg & ~BIT(data->offset + id), data->reg);
63 
64 	spin_unlock_irqrestore(data->lock, flags);
65 
66 	return 0;
67 }
68 
69 static int sun4i_a10_display_deassert(struct reset_controller_dev *rcdev,
70 				      unsigned long id)
71 {
72 	struct reset_data *data = rcdev_to_reset_data(rcdev);
73 	unsigned long flags;
74 	u32 reg;
75 
76 	spin_lock_irqsave(data->lock, flags);
77 
78 	reg = readl(data->reg);
79 	writel(reg | BIT(data->offset + id), data->reg);
80 
81 	spin_unlock_irqrestore(data->lock, flags);
82 
83 	return 0;
84 }
85 
86 static int sun4i_a10_display_status(struct reset_controller_dev *rcdev,
87 				    unsigned long id)
88 {
89 	struct reset_data *data = rcdev_to_reset_data(rcdev);
90 
91 	return !(readl(data->reg) & BIT(data->offset + id));
92 }
93 
94 static const struct reset_control_ops sun4i_a10_display_reset_ops = {
95 	.assert		= sun4i_a10_display_assert,
96 	.deassert	= sun4i_a10_display_deassert,
97 	.status		= sun4i_a10_display_status,
98 };
99 
100 static int sun4i_a10_display_reset_xlate(struct reset_controller_dev *rcdev,
101 					 const struct of_phandle_args *spec)
102 {
103 	/* We only have a single reset signal */
104 	return 0;
105 }
106 
107 static void __init sun4i_a10_display_init(struct device_node *node,
108 					  const struct sun4i_a10_display_clk_data *data)
109 {
110 	const char *parents[4];
111 	const char *clk_name = node->name;
112 	struct reset_data *reset_data;
113 	struct clk_divider *div = NULL;
114 	struct clk_gate *gate;
115 	struct resource res;
116 	struct clk_mux *mux;
117 	void __iomem *reg;
118 	struct clk *clk;
119 	int ret;
120 
121 	of_property_read_string(node, "clock-output-names", &clk_name);
122 
123 	reg = of_io_request_and_map(node, 0, of_node_full_name(node));
124 	if (IS_ERR(reg)) {
125 		pr_err("%s: Could not map the clock registers\n", clk_name);
126 		return;
127 	}
128 
129 	ret = of_clk_parent_fill(node, parents, data->parents);
130 	if (ret != data->parents) {
131 		pr_err("%s: Could not retrieve the parents\n", clk_name);
132 		goto unmap;
133 	}
134 
135 	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
136 	if (!mux)
137 		goto unmap;
138 
139 	mux->reg = reg;
140 	mux->shift = data->offset_mux;
141 	mux->mask = (1 << data->width_mux) - 1;
142 	mux->lock = &sun4i_a10_display_lock;
143 
144 	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
145 	if (!gate)
146 		goto free_mux;
147 
148 	gate->reg = reg;
149 	gate->bit_idx = data->offset_en;
150 	gate->lock = &sun4i_a10_display_lock;
151 
152 	if (data->has_div) {
153 		div = kzalloc(sizeof(*div), GFP_KERNEL);
154 		if (!div)
155 			goto free_gate;
156 
157 		div->reg = reg;
158 		div->shift = data->offset_div;
159 		div->width = data->width_div;
160 		div->lock = &sun4i_a10_display_lock;
161 	}
162 
163 	clk = clk_register_composite(NULL, clk_name,
164 				     parents, data->parents,
165 				     &mux->hw, &clk_mux_ops,
166 				     data->has_div ? &div->hw : NULL,
167 				     data->has_div ? &clk_divider_ops : NULL,
168 				     &gate->hw, &clk_gate_ops,
169 				     0);
170 	if (IS_ERR(clk)) {
171 		pr_err("%s: Couldn't register the clock\n", clk_name);
172 		goto free_div;
173 	}
174 
175 	ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
176 	if (ret) {
177 		pr_err("%s: Couldn't register DT provider\n", clk_name);
178 		goto free_clk;
179 	}
180 
181 	if (!data->num_rst)
182 		return;
183 
184 	reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
185 	if (!reset_data)
186 		goto free_of_clk;
187 
188 	reset_data->reg = reg;
189 	reset_data->offset = data->offset_rst;
190 	reset_data->lock = &sun4i_a10_display_lock;
191 	reset_data->rcdev.nr_resets = data->num_rst;
192 	reset_data->rcdev.ops = &sun4i_a10_display_reset_ops;
193 	reset_data->rcdev.of_node = node;
194 
195 	if (data->num_rst == 1) {
196 		reset_data->rcdev.of_reset_n_cells = 0;
197 		reset_data->rcdev.of_xlate = &sun4i_a10_display_reset_xlate;
198 	} else {
199 		reset_data->rcdev.of_reset_n_cells = 1;
200 	}
201 
202 	if (reset_controller_register(&reset_data->rcdev)) {
203 		pr_err("%s: Couldn't register the reset controller\n",
204 		       clk_name);
205 		goto free_reset;
206 	}
207 
208 	return;
209 
210 free_reset:
211 	kfree(reset_data);
212 free_of_clk:
213 	of_clk_del_provider(node);
214 free_clk:
215 	clk_unregister_composite(clk);
216 free_div:
217 	kfree(div);
218 free_gate:
219 	kfree(gate);
220 free_mux:
221 	kfree(mux);
222 unmap:
223 	iounmap(reg);
224 	of_address_to_resource(node, 0, &res);
225 	release_mem_region(res.start, resource_size(&res));
226 }
227 
228 static const struct sun4i_a10_display_clk_data sun4i_a10_tcon_ch0_data __initconst = {
229 	.num_rst	= 2,
230 	.parents	= 4,
231 	.offset_en	= 31,
232 	.offset_rst	= 29,
233 	.offset_mux	= 24,
234 	.width_mux	= 2,
235 };
236 
237 static void __init sun4i_a10_tcon_ch0_setup(struct device_node *node)
238 {
239 	sun4i_a10_display_init(node, &sun4i_a10_tcon_ch0_data);
240 }
241 CLK_OF_DECLARE(sun4i_a10_tcon_ch0, "allwinner,sun4i-a10-tcon-ch0-clk",
242 	       sun4i_a10_tcon_ch0_setup);
243 
244 static const struct sun4i_a10_display_clk_data sun4i_a10_display_data __initconst = {
245 	.has_div	= true,
246 	.num_rst	= 1,
247 	.parents	= 3,
248 	.offset_en	= 31,
249 	.offset_rst	= 30,
250 	.offset_mux	= 24,
251 	.offset_div	= 0,
252 	.width_mux	= 2,
253 	.width_div	= 4,
254 };
255 
256 static void __init sun4i_a10_display_setup(struct device_node *node)
257 {
258 	sun4i_a10_display_init(node, &sun4i_a10_display_data);
259 }
260 CLK_OF_DECLARE(sun4i_a10_display, "allwinner,sun4i-a10-display-clk",
261 	       sun4i_a10_display_setup);
262