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