xref: /linux/drivers/clk/sunxi/clk-a10-ve.c (revision d0034a7a4ac7fae708146ac0059b9c47a1543f0d)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
23cdd9f5cSChen-Yu Tsai /*
33cdd9f5cSChen-Yu Tsai  * Copyright 2015 Chen-Yu Tsai
43cdd9f5cSChen-Yu Tsai  *
53cdd9f5cSChen-Yu Tsai  * Chen-Yu Tsai <wens@csie.org>
63cdd9f5cSChen-Yu Tsai  */
73cdd9f5cSChen-Yu Tsai 
83cdd9f5cSChen-Yu Tsai #include <linux/clk-provider.h>
962e59c4eSStephen Boyd #include <linux/io.h>
103cdd9f5cSChen-Yu Tsai #include <linux/of.h>
113cdd9f5cSChen-Yu Tsai #include <linux/of_address.h>
123cdd9f5cSChen-Yu Tsai #include <linux/reset-controller.h>
133cdd9f5cSChen-Yu Tsai #include <linux/slab.h>
143cdd9f5cSChen-Yu Tsai #include <linux/spinlock.h>
153cdd9f5cSChen-Yu Tsai 
163cdd9f5cSChen-Yu Tsai static DEFINE_SPINLOCK(ve_lock);
173cdd9f5cSChen-Yu Tsai 
183cdd9f5cSChen-Yu Tsai #define SUN4I_VE_ENABLE		31
193cdd9f5cSChen-Yu Tsai #define SUN4I_VE_DIVIDER_SHIFT	16
203cdd9f5cSChen-Yu Tsai #define SUN4I_VE_DIVIDER_WIDTH	3
213cdd9f5cSChen-Yu Tsai #define SUN4I_VE_RESET		0
223cdd9f5cSChen-Yu Tsai 
23*29f476d4SLee Jones /*
243cdd9f5cSChen-Yu Tsai  * sunxi_ve_reset... - reset bit in ve clk registers handling
253cdd9f5cSChen-Yu Tsai  */
263cdd9f5cSChen-Yu Tsai 
273cdd9f5cSChen-Yu Tsai struct ve_reset_data {
283cdd9f5cSChen-Yu Tsai 	void __iomem			*reg;
293cdd9f5cSChen-Yu Tsai 	spinlock_t			*lock;
303cdd9f5cSChen-Yu Tsai 	struct reset_controller_dev	rcdev;
313cdd9f5cSChen-Yu Tsai };
323cdd9f5cSChen-Yu Tsai 
sunxi_ve_reset_assert(struct reset_controller_dev * rcdev,unsigned long id)333cdd9f5cSChen-Yu Tsai static int sunxi_ve_reset_assert(struct reset_controller_dev *rcdev,
343cdd9f5cSChen-Yu Tsai 				 unsigned long id)
353cdd9f5cSChen-Yu Tsai {
363cdd9f5cSChen-Yu Tsai 	struct ve_reset_data *data = container_of(rcdev,
373cdd9f5cSChen-Yu Tsai 						  struct ve_reset_data,
383cdd9f5cSChen-Yu Tsai 						  rcdev);
393cdd9f5cSChen-Yu Tsai 	unsigned long flags;
403cdd9f5cSChen-Yu Tsai 	u32 reg;
413cdd9f5cSChen-Yu Tsai 
423cdd9f5cSChen-Yu Tsai 	spin_lock_irqsave(data->lock, flags);
433cdd9f5cSChen-Yu Tsai 
443cdd9f5cSChen-Yu Tsai 	reg = readl(data->reg);
453cdd9f5cSChen-Yu Tsai 	writel(reg & ~BIT(SUN4I_VE_RESET), data->reg);
463cdd9f5cSChen-Yu Tsai 
473cdd9f5cSChen-Yu Tsai 	spin_unlock_irqrestore(data->lock, flags);
483cdd9f5cSChen-Yu Tsai 
493cdd9f5cSChen-Yu Tsai 	return 0;
503cdd9f5cSChen-Yu Tsai }
513cdd9f5cSChen-Yu Tsai 
sunxi_ve_reset_deassert(struct reset_controller_dev * rcdev,unsigned long id)523cdd9f5cSChen-Yu Tsai static int sunxi_ve_reset_deassert(struct reset_controller_dev *rcdev,
533cdd9f5cSChen-Yu Tsai 				   unsigned long id)
543cdd9f5cSChen-Yu Tsai {
553cdd9f5cSChen-Yu Tsai 	struct ve_reset_data *data = container_of(rcdev,
563cdd9f5cSChen-Yu Tsai 						  struct ve_reset_data,
573cdd9f5cSChen-Yu Tsai 						  rcdev);
583cdd9f5cSChen-Yu Tsai 	unsigned long flags;
593cdd9f5cSChen-Yu Tsai 	u32 reg;
603cdd9f5cSChen-Yu Tsai 
613cdd9f5cSChen-Yu Tsai 	spin_lock_irqsave(data->lock, flags);
623cdd9f5cSChen-Yu Tsai 
633cdd9f5cSChen-Yu Tsai 	reg = readl(data->reg);
643cdd9f5cSChen-Yu Tsai 	writel(reg | BIT(SUN4I_VE_RESET), data->reg);
653cdd9f5cSChen-Yu Tsai 
663cdd9f5cSChen-Yu Tsai 	spin_unlock_irqrestore(data->lock, flags);
673cdd9f5cSChen-Yu Tsai 
683cdd9f5cSChen-Yu Tsai 	return 0;
693cdd9f5cSChen-Yu Tsai }
703cdd9f5cSChen-Yu Tsai 
sunxi_ve_of_xlate(struct reset_controller_dev * rcdev,const struct of_phandle_args * reset_spec)713cdd9f5cSChen-Yu Tsai static int sunxi_ve_of_xlate(struct reset_controller_dev *rcdev,
723cdd9f5cSChen-Yu Tsai 			     const struct of_phandle_args *reset_spec)
733cdd9f5cSChen-Yu Tsai {
743cdd9f5cSChen-Yu Tsai 	if (WARN_ON(reset_spec->args_count != 0))
753cdd9f5cSChen-Yu Tsai 		return -EINVAL;
763cdd9f5cSChen-Yu Tsai 
773cdd9f5cSChen-Yu Tsai 	return 0;
783cdd9f5cSChen-Yu Tsai }
793cdd9f5cSChen-Yu Tsai 
805e7bc9c6SPhilipp Zabel static const struct reset_control_ops sunxi_ve_reset_ops = {
813cdd9f5cSChen-Yu Tsai 	.assert		= sunxi_ve_reset_assert,
823cdd9f5cSChen-Yu Tsai 	.deassert	= sunxi_ve_reset_deassert,
833cdd9f5cSChen-Yu Tsai };
843cdd9f5cSChen-Yu Tsai 
sun4i_ve_clk_setup(struct device_node * node)853cdd9f5cSChen-Yu Tsai static void __init sun4i_ve_clk_setup(struct device_node *node)
863cdd9f5cSChen-Yu Tsai {
873cdd9f5cSChen-Yu Tsai 	struct clk *clk;
883cdd9f5cSChen-Yu Tsai 	struct clk_divider *div;
893cdd9f5cSChen-Yu Tsai 	struct clk_gate *gate;
903cdd9f5cSChen-Yu Tsai 	struct ve_reset_data *reset_data;
913cdd9f5cSChen-Yu Tsai 	const char *parent;
923cdd9f5cSChen-Yu Tsai 	const char *clk_name = node->name;
933cdd9f5cSChen-Yu Tsai 	void __iomem *reg;
943cdd9f5cSChen-Yu Tsai 	int err;
953cdd9f5cSChen-Yu Tsai 
963cdd9f5cSChen-Yu Tsai 	reg = of_io_request_and_map(node, 0, of_node_full_name(node));
973cdd9f5cSChen-Yu Tsai 	if (IS_ERR(reg))
983cdd9f5cSChen-Yu Tsai 		return;
993cdd9f5cSChen-Yu Tsai 
1003cdd9f5cSChen-Yu Tsai 	div = kzalloc(sizeof(*div), GFP_KERNEL);
1013cdd9f5cSChen-Yu Tsai 	if (!div)
1023cdd9f5cSChen-Yu Tsai 		goto err_unmap;
1033cdd9f5cSChen-Yu Tsai 
1043cdd9f5cSChen-Yu Tsai 	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
1053cdd9f5cSChen-Yu Tsai 	if (!gate)
1063cdd9f5cSChen-Yu Tsai 		goto err_free_div;
1073cdd9f5cSChen-Yu Tsai 
1083cdd9f5cSChen-Yu Tsai 	of_property_read_string(node, "clock-output-names", &clk_name);
1093cdd9f5cSChen-Yu Tsai 	parent = of_clk_get_parent_name(node, 0);
1103cdd9f5cSChen-Yu Tsai 
1113cdd9f5cSChen-Yu Tsai 	gate->reg = reg;
1123cdd9f5cSChen-Yu Tsai 	gate->bit_idx = SUN4I_VE_ENABLE;
1133cdd9f5cSChen-Yu Tsai 	gate->lock = &ve_lock;
1143cdd9f5cSChen-Yu Tsai 
1153cdd9f5cSChen-Yu Tsai 	div->reg = reg;
1163cdd9f5cSChen-Yu Tsai 	div->shift = SUN4I_VE_DIVIDER_SHIFT;
1173cdd9f5cSChen-Yu Tsai 	div->width = SUN4I_VE_DIVIDER_WIDTH;
1183cdd9f5cSChen-Yu Tsai 	div->lock = &ve_lock;
1193cdd9f5cSChen-Yu Tsai 
1203cdd9f5cSChen-Yu Tsai 	clk = clk_register_composite(NULL, clk_name, &parent, 1,
1213cdd9f5cSChen-Yu Tsai 				     NULL, NULL,
1223cdd9f5cSChen-Yu Tsai 				     &div->hw, &clk_divider_ops,
1233cdd9f5cSChen-Yu Tsai 				     &gate->hw, &clk_gate_ops,
1243cdd9f5cSChen-Yu Tsai 				     CLK_SET_RATE_PARENT);
1253cdd9f5cSChen-Yu Tsai 	if (IS_ERR(clk))
1263cdd9f5cSChen-Yu Tsai 		goto err_free_gate;
1273cdd9f5cSChen-Yu Tsai 
1283cdd9f5cSChen-Yu Tsai 	err = of_clk_add_provider(node, of_clk_src_simple_get, clk);
1293cdd9f5cSChen-Yu Tsai 	if (err)
1303cdd9f5cSChen-Yu Tsai 		goto err_unregister_clk;
1313cdd9f5cSChen-Yu Tsai 
1323cdd9f5cSChen-Yu Tsai 	reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
1333cdd9f5cSChen-Yu Tsai 	if (!reset_data)
1343cdd9f5cSChen-Yu Tsai 		goto err_del_provider;
1353cdd9f5cSChen-Yu Tsai 
1363cdd9f5cSChen-Yu Tsai 	reset_data->reg = reg;
1373cdd9f5cSChen-Yu Tsai 	reset_data->lock = &ve_lock;
1383cdd9f5cSChen-Yu Tsai 	reset_data->rcdev.nr_resets = 1;
1393cdd9f5cSChen-Yu Tsai 	reset_data->rcdev.ops = &sunxi_ve_reset_ops;
1403cdd9f5cSChen-Yu Tsai 	reset_data->rcdev.of_node = node;
1413cdd9f5cSChen-Yu Tsai 	reset_data->rcdev.of_xlate = sunxi_ve_of_xlate;
1423cdd9f5cSChen-Yu Tsai 	reset_data->rcdev.of_reset_n_cells = 0;
1433cdd9f5cSChen-Yu Tsai 	err = reset_controller_register(&reset_data->rcdev);
1443cdd9f5cSChen-Yu Tsai 	if (err)
1453cdd9f5cSChen-Yu Tsai 		goto err_free_reset;
1463cdd9f5cSChen-Yu Tsai 
1473cdd9f5cSChen-Yu Tsai 	return;
1483cdd9f5cSChen-Yu Tsai 
1493cdd9f5cSChen-Yu Tsai err_free_reset:
1503cdd9f5cSChen-Yu Tsai 	kfree(reset_data);
1513cdd9f5cSChen-Yu Tsai err_del_provider:
1523cdd9f5cSChen-Yu Tsai 	of_clk_del_provider(node);
1533cdd9f5cSChen-Yu Tsai err_unregister_clk:
1543cdd9f5cSChen-Yu Tsai 	clk_unregister(clk);
1553cdd9f5cSChen-Yu Tsai err_free_gate:
1563cdd9f5cSChen-Yu Tsai 	kfree(gate);
1573cdd9f5cSChen-Yu Tsai err_free_div:
1583cdd9f5cSChen-Yu Tsai 	kfree(div);
1593cdd9f5cSChen-Yu Tsai err_unmap:
1603cdd9f5cSChen-Yu Tsai 	iounmap(reg);
1613cdd9f5cSChen-Yu Tsai }
1623cdd9f5cSChen-Yu Tsai CLK_OF_DECLARE(sun4i_ve, "allwinner,sun4i-a10-ve-clk",
1633cdd9f5cSChen-Yu Tsai 	       sun4i_ve_clk_setup);
164