Linux Audio

Check our new training course

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