198b8525aSMaxime Ripard /*
298b8525aSMaxime Ripard  * Copyright 2015 Maxime Ripard
398b8525aSMaxime Ripard  *
498b8525aSMaxime Ripard  * Maxime Ripard <maxime.ripard@free-electrons.com>
598b8525aSMaxime Ripard  *
698b8525aSMaxime Ripard  * This program is free software; you can redistribute it and/or modify
798b8525aSMaxime Ripard  * it under the terms of the GNU General Public License as published by
898b8525aSMaxime Ripard  * the Free Software Foundation; either version 2 of the License, or
998b8525aSMaxime Ripard  * (at your option) any later version.
1098b8525aSMaxime Ripard  *
1198b8525aSMaxime Ripard  * This program is distributed in the hope that it will be useful,
1298b8525aSMaxime Ripard  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1398b8525aSMaxime Ripard  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1498b8525aSMaxime Ripard  * GNU General Public License for more details.
1598b8525aSMaxime Ripard  */
1698b8525aSMaxime Ripard 
1798b8525aSMaxime Ripard #include <linux/clk-provider.h>
1898b8525aSMaxime Ripard #include <linux/kernel.h>
1998b8525aSMaxime Ripard #include <linux/of_address.h>
2098b8525aSMaxime Ripard #include <linux/reset-controller.h>
2198b8525aSMaxime Ripard #include <linux/slab.h>
2298b8525aSMaxime Ripard #include <linux/spinlock.h>
2398b8525aSMaxime Ripard 
2498b8525aSMaxime Ripard struct sun4i_a10_display_clk_data {
2598b8525aSMaxime Ripard 	bool	has_div;
2698b8525aSMaxime Ripard 	u8	num_rst;
2798b8525aSMaxime Ripard 	u8	parents;
2898b8525aSMaxime Ripard 
2998b8525aSMaxime Ripard 	u8	offset_en;
3098b8525aSMaxime Ripard 	u8	offset_div;
3198b8525aSMaxime Ripard 	u8	offset_mux;
3298b8525aSMaxime Ripard 	u8	offset_rst;
3398b8525aSMaxime Ripard 
3498b8525aSMaxime Ripard 	u8	width_div;
3598b8525aSMaxime Ripard 	u8	width_mux;
3607ea0b4dSMaxime Ripard 
3707ea0b4dSMaxime Ripard 	u32	flags;
3898b8525aSMaxime Ripard };
3998b8525aSMaxime Ripard 
4098b8525aSMaxime Ripard struct reset_data {
4198b8525aSMaxime Ripard 	void __iomem			*reg;
4298b8525aSMaxime Ripard 	spinlock_t			*lock;
4398b8525aSMaxime Ripard 	struct reset_controller_dev	rcdev;
4498b8525aSMaxime Ripard 	u8				offset;
4598b8525aSMaxime Ripard };
4698b8525aSMaxime Ripard 
4798b8525aSMaxime Ripard static DEFINE_SPINLOCK(sun4i_a10_display_lock);
4898b8525aSMaxime Ripard 
4998b8525aSMaxime Ripard static inline struct reset_data *rcdev_to_reset_data(struct reset_controller_dev *rcdev)
5098b8525aSMaxime Ripard {
5198b8525aSMaxime Ripard 	return container_of(rcdev, struct reset_data, rcdev);
5298b8525aSMaxime Ripard };
5398b8525aSMaxime Ripard 
5498b8525aSMaxime Ripard static int sun4i_a10_display_assert(struct reset_controller_dev *rcdev,
5598b8525aSMaxime Ripard 				    unsigned long id)
5698b8525aSMaxime Ripard {
5798b8525aSMaxime Ripard 	struct reset_data *data = rcdev_to_reset_data(rcdev);
5898b8525aSMaxime Ripard 	unsigned long flags;
5998b8525aSMaxime Ripard 	u32 reg;
6098b8525aSMaxime Ripard 
6198b8525aSMaxime Ripard 	spin_lock_irqsave(data->lock, flags);
6298b8525aSMaxime Ripard 
6398b8525aSMaxime Ripard 	reg = readl(data->reg);
6498b8525aSMaxime Ripard 	writel(reg & ~BIT(data->offset + id), data->reg);
6598b8525aSMaxime Ripard 
6698b8525aSMaxime Ripard 	spin_unlock_irqrestore(data->lock, flags);
6798b8525aSMaxime Ripard 
6898b8525aSMaxime Ripard 	return 0;
6998b8525aSMaxime Ripard }
7098b8525aSMaxime Ripard 
7198b8525aSMaxime Ripard static int sun4i_a10_display_deassert(struct reset_controller_dev *rcdev,
7298b8525aSMaxime Ripard 				      unsigned long id)
7398b8525aSMaxime Ripard {
7498b8525aSMaxime Ripard 	struct reset_data *data = rcdev_to_reset_data(rcdev);
7598b8525aSMaxime Ripard 	unsigned long flags;
7698b8525aSMaxime Ripard 	u32 reg;
7798b8525aSMaxime Ripard 
7898b8525aSMaxime Ripard 	spin_lock_irqsave(data->lock, flags);
7998b8525aSMaxime Ripard 
8098b8525aSMaxime Ripard 	reg = readl(data->reg);
8198b8525aSMaxime Ripard 	writel(reg | BIT(data->offset + id), data->reg);
8298b8525aSMaxime Ripard 
8398b8525aSMaxime Ripard 	spin_unlock_irqrestore(data->lock, flags);
8498b8525aSMaxime Ripard 
8598b8525aSMaxime Ripard 	return 0;
8698b8525aSMaxime Ripard }
8798b8525aSMaxime Ripard 
8898b8525aSMaxime Ripard static int sun4i_a10_display_status(struct reset_controller_dev *rcdev,
8998b8525aSMaxime Ripard 				    unsigned long id)
9098b8525aSMaxime Ripard {
9198b8525aSMaxime Ripard 	struct reset_data *data = rcdev_to_reset_data(rcdev);
9298b8525aSMaxime Ripard 
9398b8525aSMaxime Ripard 	return !(readl(data->reg) & BIT(data->offset + id));
9498b8525aSMaxime Ripard }
9598b8525aSMaxime Ripard 
9698b8525aSMaxime Ripard static const struct reset_control_ops sun4i_a10_display_reset_ops = {
9798b8525aSMaxime Ripard 	.assert		= sun4i_a10_display_assert,
9898b8525aSMaxime Ripard 	.deassert	= sun4i_a10_display_deassert,
9998b8525aSMaxime Ripard 	.status		= sun4i_a10_display_status,
10098b8525aSMaxime Ripard };
10198b8525aSMaxime Ripard 
10298b8525aSMaxime Ripard static int sun4i_a10_display_reset_xlate(struct reset_controller_dev *rcdev,
10398b8525aSMaxime Ripard 					 const struct of_phandle_args *spec)
10498b8525aSMaxime Ripard {
10598b8525aSMaxime Ripard 	/* We only have a single reset signal */
10698b8525aSMaxime Ripard 	return 0;
10798b8525aSMaxime Ripard }
10898b8525aSMaxime Ripard 
10998b8525aSMaxime Ripard static void __init sun4i_a10_display_init(struct device_node *node,
11098b8525aSMaxime Ripard 					  const struct sun4i_a10_display_clk_data *data)
11198b8525aSMaxime Ripard {
11298b8525aSMaxime Ripard 	const char *parents[4];
11398b8525aSMaxime Ripard 	const char *clk_name = node->name;
11498b8525aSMaxime Ripard 	struct reset_data *reset_data;
11598b8525aSMaxime Ripard 	struct clk_divider *div = NULL;
11698b8525aSMaxime Ripard 	struct clk_gate *gate;
11798b8525aSMaxime Ripard 	struct resource res;
11898b8525aSMaxime Ripard 	struct clk_mux *mux;
11998b8525aSMaxime Ripard 	void __iomem *reg;
12098b8525aSMaxime Ripard 	struct clk *clk;
12198b8525aSMaxime Ripard 	int ret;
12298b8525aSMaxime Ripard 
12398b8525aSMaxime Ripard 	of_property_read_string(node, "clock-output-names", &clk_name);
12498b8525aSMaxime Ripard 
12598b8525aSMaxime Ripard 	reg = of_io_request_and_map(node, 0, of_node_full_name(node));
12698b8525aSMaxime Ripard 	if (IS_ERR(reg)) {
12798b8525aSMaxime Ripard 		pr_err("%s: Could not map the clock registers\n", clk_name);
12898b8525aSMaxime Ripard 		return;
12998b8525aSMaxime Ripard 	}
13098b8525aSMaxime Ripard 
13198b8525aSMaxime Ripard 	ret = of_clk_parent_fill(node, parents, data->parents);
13298b8525aSMaxime Ripard 	if (ret != data->parents) {
13398b8525aSMaxime Ripard 		pr_err("%s: Could not retrieve the parents\n", clk_name);
13498b8525aSMaxime Ripard 		goto unmap;
13598b8525aSMaxime Ripard 	}
13698b8525aSMaxime Ripard 
13798b8525aSMaxime Ripard 	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
13898b8525aSMaxime Ripard 	if (!mux)
13998b8525aSMaxime Ripard 		goto unmap;
14098b8525aSMaxime Ripard 
14198b8525aSMaxime Ripard 	mux->reg = reg;
14298b8525aSMaxime Ripard 	mux->shift = data->offset_mux;
14398b8525aSMaxime Ripard 	mux->mask = (1 << data->width_mux) - 1;
14498b8525aSMaxime Ripard 	mux->lock = &sun4i_a10_display_lock;
14598b8525aSMaxime Ripard 
14698b8525aSMaxime Ripard 	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
14798b8525aSMaxime Ripard 	if (!gate)
14898b8525aSMaxime Ripard 		goto free_mux;
14998b8525aSMaxime Ripard 
15098b8525aSMaxime Ripard 	gate->reg = reg;
15198b8525aSMaxime Ripard 	gate->bit_idx = data->offset_en;
15298b8525aSMaxime Ripard 	gate->lock = &sun4i_a10_display_lock;
15398b8525aSMaxime Ripard 
15498b8525aSMaxime Ripard 	if (data->has_div) {
15598b8525aSMaxime Ripard 		div = kzalloc(sizeof(*div), GFP_KERNEL);
15698b8525aSMaxime Ripard 		if (!div)
15798b8525aSMaxime Ripard 			goto free_gate;
15898b8525aSMaxime Ripard 
15998b8525aSMaxime Ripard 		div->reg = reg;
16098b8525aSMaxime Ripard 		div->shift = data->offset_div;
16198b8525aSMaxime Ripard 		div->width = data->width_div;
16298b8525aSMaxime Ripard 		div->lock = &sun4i_a10_display_lock;
16398b8525aSMaxime Ripard 	}
16498b8525aSMaxime Ripard 
16598b8525aSMaxime Ripard 	clk = clk_register_composite(NULL, clk_name,
16698b8525aSMaxime Ripard 				     parents, data->parents,
16798b8525aSMaxime Ripard 				     &mux->hw, &clk_mux_ops,
16898b8525aSMaxime Ripard 				     data->has_div ? &div->hw : NULL,
16998b8525aSMaxime Ripard 				     data->has_div ? &clk_divider_ops : NULL,
17098b8525aSMaxime Ripard 				     &gate->hw, &clk_gate_ops,
17107ea0b4dSMaxime Ripard 				     data->flags);
17298b8525aSMaxime Ripard 	if (IS_ERR(clk)) {
17398b8525aSMaxime Ripard 		pr_err("%s: Couldn't register the clock\n", clk_name);
17498b8525aSMaxime Ripard 		goto free_div;
17598b8525aSMaxime Ripard 	}
17698b8525aSMaxime Ripard 
17798b8525aSMaxime Ripard 	ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
17898b8525aSMaxime Ripard 	if (ret) {
17998b8525aSMaxime Ripard 		pr_err("%s: Couldn't register DT provider\n", clk_name);
18098b8525aSMaxime Ripard 		goto free_clk;
18198b8525aSMaxime Ripard 	}
18298b8525aSMaxime Ripard 
18398b8525aSMaxime Ripard 	if (!data->num_rst)
18498b8525aSMaxime Ripard 		return;
18598b8525aSMaxime Ripard 
18698b8525aSMaxime Ripard 	reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
18798b8525aSMaxime Ripard 	if (!reset_data)
18898b8525aSMaxime Ripard 		goto free_of_clk;
18998b8525aSMaxime Ripard 
19098b8525aSMaxime Ripard 	reset_data->reg = reg;
19198b8525aSMaxime Ripard 	reset_data->offset = data->offset_rst;
19298b8525aSMaxime Ripard 	reset_data->lock = &sun4i_a10_display_lock;
19398b8525aSMaxime Ripard 	reset_data->rcdev.nr_resets = data->num_rst;
19498b8525aSMaxime Ripard 	reset_data->rcdev.ops = &sun4i_a10_display_reset_ops;
19598b8525aSMaxime Ripard 	reset_data->rcdev.of_node = node;
19698b8525aSMaxime Ripard 
19798b8525aSMaxime Ripard 	if (data->num_rst == 1) {
19898b8525aSMaxime Ripard 		reset_data->rcdev.of_reset_n_cells = 0;
19998b8525aSMaxime Ripard 		reset_data->rcdev.of_xlate = &sun4i_a10_display_reset_xlate;
20098b8525aSMaxime Ripard 	} else {
20198b8525aSMaxime Ripard 		reset_data->rcdev.of_reset_n_cells = 1;
20298b8525aSMaxime Ripard 	}
20398b8525aSMaxime Ripard 
20498b8525aSMaxime Ripard 	if (reset_controller_register(&reset_data->rcdev)) {
20598b8525aSMaxime Ripard 		pr_err("%s: Couldn't register the reset controller\n",
20698b8525aSMaxime Ripard 		       clk_name);
20798b8525aSMaxime Ripard 		goto free_reset;
20898b8525aSMaxime Ripard 	}
20998b8525aSMaxime Ripard 
21098b8525aSMaxime Ripard 	return;
21198b8525aSMaxime Ripard 
21298b8525aSMaxime Ripard free_reset:
21398b8525aSMaxime Ripard 	kfree(reset_data);
21498b8525aSMaxime Ripard free_of_clk:
21598b8525aSMaxime Ripard 	of_clk_del_provider(node);
21698b8525aSMaxime Ripard free_clk:
21798b8525aSMaxime Ripard 	clk_unregister_composite(clk);
21898b8525aSMaxime Ripard free_div:
21998b8525aSMaxime Ripard 	kfree(div);
22098b8525aSMaxime Ripard free_gate:
22198b8525aSMaxime Ripard 	kfree(gate);
22298b8525aSMaxime Ripard free_mux:
22398b8525aSMaxime Ripard 	kfree(mux);
22498b8525aSMaxime Ripard unmap:
22598b8525aSMaxime Ripard 	iounmap(reg);
22698b8525aSMaxime Ripard 	of_address_to_resource(node, 0, &res);
22798b8525aSMaxime Ripard 	release_mem_region(res.start, resource_size(&res));
22898b8525aSMaxime Ripard }
22998b8525aSMaxime Ripard 
23098b8525aSMaxime Ripard static const struct sun4i_a10_display_clk_data sun4i_a10_tcon_ch0_data __initconst = {
23198b8525aSMaxime Ripard 	.num_rst	= 2,
23298b8525aSMaxime Ripard 	.parents	= 4,
23398b8525aSMaxime Ripard 	.offset_en	= 31,
23498b8525aSMaxime Ripard 	.offset_rst	= 29,
23598b8525aSMaxime Ripard 	.offset_mux	= 24,
23698b8525aSMaxime Ripard 	.width_mux	= 2,
23707ea0b4dSMaxime Ripard 	.flags		= CLK_SET_RATE_PARENT,
23898b8525aSMaxime Ripard };
23998b8525aSMaxime Ripard 
24098b8525aSMaxime Ripard static void __init sun4i_a10_tcon_ch0_setup(struct device_node *node)
24198b8525aSMaxime Ripard {
24298b8525aSMaxime Ripard 	sun4i_a10_display_init(node, &sun4i_a10_tcon_ch0_data);
24398b8525aSMaxime Ripard }
24498b8525aSMaxime Ripard CLK_OF_DECLARE(sun4i_a10_tcon_ch0, "allwinner,sun4i-a10-tcon-ch0-clk",
24598b8525aSMaxime Ripard 	       sun4i_a10_tcon_ch0_setup);
24698b8525aSMaxime Ripard 
24798b8525aSMaxime Ripard static const struct sun4i_a10_display_clk_data sun4i_a10_display_data __initconst = {
24898b8525aSMaxime Ripard 	.has_div	= true,
24998b8525aSMaxime Ripard 	.num_rst	= 1,
25098b8525aSMaxime Ripard 	.parents	= 3,
25198b8525aSMaxime Ripard 	.offset_en	= 31,
25298b8525aSMaxime Ripard 	.offset_rst	= 30,
25398b8525aSMaxime Ripard 	.offset_mux	= 24,
25498b8525aSMaxime Ripard 	.offset_div	= 0,
25598b8525aSMaxime Ripard 	.width_mux	= 2,
25698b8525aSMaxime Ripard 	.width_div	= 4,
25798b8525aSMaxime Ripard };
25898b8525aSMaxime Ripard 
25998b8525aSMaxime Ripard static void __init sun4i_a10_display_setup(struct device_node *node)
26098b8525aSMaxime Ripard {
26198b8525aSMaxime Ripard 	sun4i_a10_display_init(node, &sun4i_a10_display_data);
26298b8525aSMaxime Ripard }
26398b8525aSMaxime Ripard CLK_OF_DECLARE(sun4i_a10_display, "allwinner,sun4i-a10-display-clk",
26498b8525aSMaxime Ripard 	       sun4i_a10_display_setup);
265