xref: /linux/drivers/clk/sunxi/clk-a10-ve.c (revision 62e59c4e69b3cdbad67e3c2d49e4df4cfe1679e3)
13cdd9f5cSChen-Yu Tsai /*
23cdd9f5cSChen-Yu Tsai  * Copyright 2015 Chen-Yu Tsai
33cdd9f5cSChen-Yu Tsai  *
43cdd9f5cSChen-Yu Tsai  * Chen-Yu Tsai <wens@csie.org>
53cdd9f5cSChen-Yu Tsai  *
63cdd9f5cSChen-Yu Tsai  * This program is free software; you can redistribute it and/or modify
73cdd9f5cSChen-Yu Tsai  * it under the terms of the GNU General Public License as published by
83cdd9f5cSChen-Yu Tsai  * the Free Software Foundation; either version 2 of the License, or
93cdd9f5cSChen-Yu Tsai  * (at your option) any later version.
103cdd9f5cSChen-Yu Tsai  *
113cdd9f5cSChen-Yu Tsai  * This program is distributed in the hope that it will be useful,
123cdd9f5cSChen-Yu Tsai  * but WITHOUT ANY WARRANTY; without even the implied warranty of
133cdd9f5cSChen-Yu Tsai  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
143cdd9f5cSChen-Yu Tsai  * GNU General Public License for more details.
153cdd9f5cSChen-Yu Tsai  */
163cdd9f5cSChen-Yu Tsai 
173cdd9f5cSChen-Yu Tsai #include <linux/clk-provider.h>
18*62e59c4eSStephen Boyd #include <linux/io.h>
193cdd9f5cSChen-Yu Tsai #include <linux/of.h>
203cdd9f5cSChen-Yu Tsai #include <linux/of_address.h>
213cdd9f5cSChen-Yu Tsai #include <linux/reset-controller.h>
223cdd9f5cSChen-Yu Tsai #include <linux/slab.h>
233cdd9f5cSChen-Yu Tsai #include <linux/spinlock.h>
243cdd9f5cSChen-Yu Tsai 
253cdd9f5cSChen-Yu Tsai static DEFINE_SPINLOCK(ve_lock);
263cdd9f5cSChen-Yu Tsai 
273cdd9f5cSChen-Yu Tsai #define SUN4I_VE_ENABLE		31
283cdd9f5cSChen-Yu Tsai #define SUN4I_VE_DIVIDER_SHIFT	16
293cdd9f5cSChen-Yu Tsai #define SUN4I_VE_DIVIDER_WIDTH	3
303cdd9f5cSChen-Yu Tsai #define SUN4I_VE_RESET		0
313cdd9f5cSChen-Yu Tsai 
323cdd9f5cSChen-Yu Tsai /**
333cdd9f5cSChen-Yu Tsai  * sunxi_ve_reset... - reset bit in ve clk registers handling
343cdd9f5cSChen-Yu Tsai  */
353cdd9f5cSChen-Yu Tsai 
363cdd9f5cSChen-Yu Tsai struct ve_reset_data {
373cdd9f5cSChen-Yu Tsai 	void __iomem			*reg;
383cdd9f5cSChen-Yu Tsai 	spinlock_t			*lock;
393cdd9f5cSChen-Yu Tsai 	struct reset_controller_dev	rcdev;
403cdd9f5cSChen-Yu Tsai };
413cdd9f5cSChen-Yu Tsai 
423cdd9f5cSChen-Yu Tsai static int sunxi_ve_reset_assert(struct reset_controller_dev *rcdev,
433cdd9f5cSChen-Yu Tsai 				 unsigned long id)
443cdd9f5cSChen-Yu Tsai {
453cdd9f5cSChen-Yu Tsai 	struct ve_reset_data *data = container_of(rcdev,
463cdd9f5cSChen-Yu Tsai 						  struct ve_reset_data,
473cdd9f5cSChen-Yu Tsai 						  rcdev);
483cdd9f5cSChen-Yu Tsai 	unsigned long flags;
493cdd9f5cSChen-Yu Tsai 	u32 reg;
503cdd9f5cSChen-Yu Tsai 
513cdd9f5cSChen-Yu Tsai 	spin_lock_irqsave(data->lock, flags);
523cdd9f5cSChen-Yu Tsai 
533cdd9f5cSChen-Yu Tsai 	reg = readl(data->reg);
543cdd9f5cSChen-Yu Tsai 	writel(reg & ~BIT(SUN4I_VE_RESET), data->reg);
553cdd9f5cSChen-Yu Tsai 
563cdd9f5cSChen-Yu Tsai 	spin_unlock_irqrestore(data->lock, flags);
573cdd9f5cSChen-Yu Tsai 
583cdd9f5cSChen-Yu Tsai 	return 0;
593cdd9f5cSChen-Yu Tsai }
603cdd9f5cSChen-Yu Tsai 
613cdd9f5cSChen-Yu Tsai static int sunxi_ve_reset_deassert(struct reset_controller_dev *rcdev,
623cdd9f5cSChen-Yu Tsai 				   unsigned long id)
633cdd9f5cSChen-Yu Tsai {
643cdd9f5cSChen-Yu Tsai 	struct ve_reset_data *data = container_of(rcdev,
653cdd9f5cSChen-Yu Tsai 						  struct ve_reset_data,
663cdd9f5cSChen-Yu Tsai 						  rcdev);
673cdd9f5cSChen-Yu Tsai 	unsigned long flags;
683cdd9f5cSChen-Yu Tsai 	u32 reg;
693cdd9f5cSChen-Yu Tsai 
703cdd9f5cSChen-Yu Tsai 	spin_lock_irqsave(data->lock, flags);
713cdd9f5cSChen-Yu Tsai 
723cdd9f5cSChen-Yu Tsai 	reg = readl(data->reg);
733cdd9f5cSChen-Yu Tsai 	writel(reg | BIT(SUN4I_VE_RESET), data->reg);
743cdd9f5cSChen-Yu Tsai 
753cdd9f5cSChen-Yu Tsai 	spin_unlock_irqrestore(data->lock, flags);
763cdd9f5cSChen-Yu Tsai 
773cdd9f5cSChen-Yu Tsai 	return 0;
783cdd9f5cSChen-Yu Tsai }
793cdd9f5cSChen-Yu Tsai 
803cdd9f5cSChen-Yu Tsai static int sunxi_ve_of_xlate(struct reset_controller_dev *rcdev,
813cdd9f5cSChen-Yu Tsai 			     const struct of_phandle_args *reset_spec)
823cdd9f5cSChen-Yu Tsai {
833cdd9f5cSChen-Yu Tsai 	if (WARN_ON(reset_spec->args_count != 0))
843cdd9f5cSChen-Yu Tsai 		return -EINVAL;
853cdd9f5cSChen-Yu Tsai 
863cdd9f5cSChen-Yu Tsai 	return 0;
873cdd9f5cSChen-Yu Tsai }
883cdd9f5cSChen-Yu Tsai 
895e7bc9c6SPhilipp Zabel static const struct reset_control_ops sunxi_ve_reset_ops = {
903cdd9f5cSChen-Yu Tsai 	.assert		= sunxi_ve_reset_assert,
913cdd9f5cSChen-Yu Tsai 	.deassert	= sunxi_ve_reset_deassert,
923cdd9f5cSChen-Yu Tsai };
933cdd9f5cSChen-Yu Tsai 
943cdd9f5cSChen-Yu Tsai static void __init sun4i_ve_clk_setup(struct device_node *node)
953cdd9f5cSChen-Yu Tsai {
963cdd9f5cSChen-Yu Tsai 	struct clk *clk;
973cdd9f5cSChen-Yu Tsai 	struct clk_divider *div;
983cdd9f5cSChen-Yu Tsai 	struct clk_gate *gate;
993cdd9f5cSChen-Yu Tsai 	struct ve_reset_data *reset_data;
1003cdd9f5cSChen-Yu Tsai 	const char *parent;
1013cdd9f5cSChen-Yu Tsai 	const char *clk_name = node->name;
1023cdd9f5cSChen-Yu Tsai 	void __iomem *reg;
1033cdd9f5cSChen-Yu Tsai 	int err;
1043cdd9f5cSChen-Yu Tsai 
1053cdd9f5cSChen-Yu Tsai 	reg = of_io_request_and_map(node, 0, of_node_full_name(node));
1063cdd9f5cSChen-Yu Tsai 	if (IS_ERR(reg))
1073cdd9f5cSChen-Yu Tsai 		return;
1083cdd9f5cSChen-Yu Tsai 
1093cdd9f5cSChen-Yu Tsai 	div = kzalloc(sizeof(*div), GFP_KERNEL);
1103cdd9f5cSChen-Yu Tsai 	if (!div)
1113cdd9f5cSChen-Yu Tsai 		goto err_unmap;
1123cdd9f5cSChen-Yu Tsai 
1133cdd9f5cSChen-Yu Tsai 	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
1143cdd9f5cSChen-Yu Tsai 	if (!gate)
1153cdd9f5cSChen-Yu Tsai 		goto err_free_div;
1163cdd9f5cSChen-Yu Tsai 
1173cdd9f5cSChen-Yu Tsai 	of_property_read_string(node, "clock-output-names", &clk_name);
1183cdd9f5cSChen-Yu Tsai 	parent = of_clk_get_parent_name(node, 0);
1193cdd9f5cSChen-Yu Tsai 
1203cdd9f5cSChen-Yu Tsai 	gate->reg = reg;
1213cdd9f5cSChen-Yu Tsai 	gate->bit_idx = SUN4I_VE_ENABLE;
1223cdd9f5cSChen-Yu Tsai 	gate->lock = &ve_lock;
1233cdd9f5cSChen-Yu Tsai 
1243cdd9f5cSChen-Yu Tsai 	div->reg = reg;
1253cdd9f5cSChen-Yu Tsai 	div->shift = SUN4I_VE_DIVIDER_SHIFT;
1263cdd9f5cSChen-Yu Tsai 	div->width = SUN4I_VE_DIVIDER_WIDTH;
1273cdd9f5cSChen-Yu Tsai 	div->lock = &ve_lock;
1283cdd9f5cSChen-Yu Tsai 
1293cdd9f5cSChen-Yu Tsai 	clk = clk_register_composite(NULL, clk_name, &parent, 1,
1303cdd9f5cSChen-Yu Tsai 				     NULL, NULL,
1313cdd9f5cSChen-Yu Tsai 				     &div->hw, &clk_divider_ops,
1323cdd9f5cSChen-Yu Tsai 				     &gate->hw, &clk_gate_ops,
1333cdd9f5cSChen-Yu Tsai 				     CLK_SET_RATE_PARENT);
1343cdd9f5cSChen-Yu Tsai 	if (IS_ERR(clk))
1353cdd9f5cSChen-Yu Tsai 		goto err_free_gate;
1363cdd9f5cSChen-Yu Tsai 
1373cdd9f5cSChen-Yu Tsai 	err = of_clk_add_provider(node, of_clk_src_simple_get, clk);
1383cdd9f5cSChen-Yu Tsai 	if (err)
1393cdd9f5cSChen-Yu Tsai 		goto err_unregister_clk;
1403cdd9f5cSChen-Yu Tsai 
1413cdd9f5cSChen-Yu Tsai 	reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
1423cdd9f5cSChen-Yu Tsai 	if (!reset_data)
1433cdd9f5cSChen-Yu Tsai 		goto err_del_provider;
1443cdd9f5cSChen-Yu Tsai 
1453cdd9f5cSChen-Yu Tsai 	reset_data->reg = reg;
1463cdd9f5cSChen-Yu Tsai 	reset_data->lock = &ve_lock;
1473cdd9f5cSChen-Yu Tsai 	reset_data->rcdev.nr_resets = 1;
1483cdd9f5cSChen-Yu Tsai 	reset_data->rcdev.ops = &sunxi_ve_reset_ops;
1493cdd9f5cSChen-Yu Tsai 	reset_data->rcdev.of_node = node;
1503cdd9f5cSChen-Yu Tsai 	reset_data->rcdev.of_xlate = sunxi_ve_of_xlate;
1513cdd9f5cSChen-Yu Tsai 	reset_data->rcdev.of_reset_n_cells = 0;
1523cdd9f5cSChen-Yu Tsai 	err = reset_controller_register(&reset_data->rcdev);
1533cdd9f5cSChen-Yu Tsai 	if (err)
1543cdd9f5cSChen-Yu Tsai 		goto err_free_reset;
1553cdd9f5cSChen-Yu Tsai 
1563cdd9f5cSChen-Yu Tsai 	return;
1573cdd9f5cSChen-Yu Tsai 
1583cdd9f5cSChen-Yu Tsai err_free_reset:
1593cdd9f5cSChen-Yu Tsai 	kfree(reset_data);
1603cdd9f5cSChen-Yu Tsai err_del_provider:
1613cdd9f5cSChen-Yu Tsai 	of_clk_del_provider(node);
1623cdd9f5cSChen-Yu Tsai err_unregister_clk:
1633cdd9f5cSChen-Yu Tsai 	clk_unregister(clk);
1643cdd9f5cSChen-Yu Tsai err_free_gate:
1653cdd9f5cSChen-Yu Tsai 	kfree(gate);
1663cdd9f5cSChen-Yu Tsai err_free_div:
1673cdd9f5cSChen-Yu Tsai 	kfree(div);
1683cdd9f5cSChen-Yu Tsai err_unmap:
1693cdd9f5cSChen-Yu Tsai 	iounmap(reg);
1703cdd9f5cSChen-Yu Tsai }
1713cdd9f5cSChen-Yu Tsai CLK_OF_DECLARE(sun4i_ve, "allwinner,sun4i-a10-ve-clk",
1723cdd9f5cSChen-Yu Tsai 	       sun4i_ve_clk_setup);
173