xref: /openbmc/linux/drivers/clk/sunxi/clk-sun8i-mbus.c (revision 8f2bf2ad9673e187b5c2956497003f60e0885e5d)
19c8176bfSChen-Yu Tsai /*
29c8176bfSChen-Yu Tsai  * Copyright 2014 Chen-Yu Tsai
39c8176bfSChen-Yu Tsai  *
49c8176bfSChen-Yu Tsai  * Chen-Yu Tsai <wens@csie.org>
59c8176bfSChen-Yu Tsai  *
69c8176bfSChen-Yu Tsai  * This program is free software; you can redistribute it and/or modify
79c8176bfSChen-Yu Tsai  * it under the terms of the GNU General Public License as published by
89c8176bfSChen-Yu Tsai  * the Free Software Foundation; either version 2 of the License, or
99c8176bfSChen-Yu Tsai  * (at your option) any later version.
109c8176bfSChen-Yu Tsai  *
119c8176bfSChen-Yu Tsai  * This program is distributed in the hope that it will be useful,
129c8176bfSChen-Yu Tsai  * but WITHOUT ANY WARRANTY; without even the implied warranty of
139c8176bfSChen-Yu Tsai  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
149c8176bfSChen-Yu Tsai  * GNU General Public License for more details.
159c8176bfSChen-Yu Tsai  */
169c8176bfSChen-Yu Tsai 
179dfefe8cSStephen Boyd #include <linux/clk.h>
18*8f2bf2adSChen-Yu Tsai #include <linux/clkdev.h>
199c8176bfSChen-Yu Tsai #include <linux/clk-provider.h>
20*8f2bf2adSChen-Yu Tsai #include <linux/slab.h>
21*8f2bf2adSChen-Yu Tsai #include <linux/spinlock.h>
229c8176bfSChen-Yu Tsai #include <linux/of_address.h>
239c8176bfSChen-Yu Tsai 
24*8f2bf2adSChen-Yu Tsai #define SUN8I_MBUS_ENABLE	31
25*8f2bf2adSChen-Yu Tsai #define SUN8I_MBUS_MUX_SHIFT	24
26*8f2bf2adSChen-Yu Tsai #define SUN8I_MBUS_MUX_MASK	0x3
27*8f2bf2adSChen-Yu Tsai #define SUN8I_MBUS_DIV_SHIFT	0
28*8f2bf2adSChen-Yu Tsai #define SUN8I_MBUS_DIV_WIDTH	3
29*8f2bf2adSChen-Yu Tsai #define SUN8I_MBUS_MAX_PARENTS	4
309c8176bfSChen-Yu Tsai 
319c8176bfSChen-Yu Tsai static DEFINE_SPINLOCK(sun8i_a23_mbus_lock);
329c8176bfSChen-Yu Tsai 
339c8176bfSChen-Yu Tsai static void __init sun8i_a23_mbus_setup(struct device_node *node)
349c8176bfSChen-Yu Tsai {
35*8f2bf2adSChen-Yu Tsai 	int num_parents = of_clk_get_parent_count(node);
36*8f2bf2adSChen-Yu Tsai 	const char *parents[num_parents];
37*8f2bf2adSChen-Yu Tsai 	const char *clk_name = node->name;
38*8f2bf2adSChen-Yu Tsai 	struct resource res;
39*8f2bf2adSChen-Yu Tsai 	struct clk_divider *div;
40*8f2bf2adSChen-Yu Tsai 	struct clk_gate *gate;
41*8f2bf2adSChen-Yu Tsai 	struct clk_mux *mux;
42*8f2bf2adSChen-Yu Tsai 	struct clk *clk;
437c74c220SHans de Goede 	void __iomem *reg;
44*8f2bf2adSChen-Yu Tsai 	int err;
457c74c220SHans de Goede 
46*8f2bf2adSChen-Yu Tsai 	reg = of_io_request_and_map(node, 0, of_node_full_name(node));
477c74c220SHans de Goede 	if (!reg) {
48*8f2bf2adSChen-Yu Tsai 		pr_err("Could not get registers for sun8i-mbus-clk\n");
497c74c220SHans de Goede 		return;
507c74c220SHans de Goede 	}
517c74c220SHans de Goede 
52*8f2bf2adSChen-Yu Tsai 	div = kzalloc(sizeof(*div), GFP_KERNEL);
53*8f2bf2adSChen-Yu Tsai 	if (!div)
54*8f2bf2adSChen-Yu Tsai 		goto err_unmap;
55*8f2bf2adSChen-Yu Tsai 
56*8f2bf2adSChen-Yu Tsai 	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
57*8f2bf2adSChen-Yu Tsai 	if (!mux)
58*8f2bf2adSChen-Yu Tsai 		goto err_free_div;
59*8f2bf2adSChen-Yu Tsai 
60*8f2bf2adSChen-Yu Tsai 	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
61*8f2bf2adSChen-Yu Tsai 	if (!gate)
62*8f2bf2adSChen-Yu Tsai 		goto err_free_mux;
63*8f2bf2adSChen-Yu Tsai 
64*8f2bf2adSChen-Yu Tsai 	of_property_read_string(node, "clock-output-names", &clk_name);
65*8f2bf2adSChen-Yu Tsai 	of_clk_parent_fill(node, parents, num_parents);
66*8f2bf2adSChen-Yu Tsai 
67*8f2bf2adSChen-Yu Tsai 	gate->reg = reg;
68*8f2bf2adSChen-Yu Tsai 	gate->bit_idx = SUN8I_MBUS_ENABLE;
69*8f2bf2adSChen-Yu Tsai 	gate->lock = &sun8i_a23_mbus_lock;
70*8f2bf2adSChen-Yu Tsai 
71*8f2bf2adSChen-Yu Tsai 	div->reg = reg;
72*8f2bf2adSChen-Yu Tsai 	div->shift = SUN8I_MBUS_DIV_SHIFT;
73*8f2bf2adSChen-Yu Tsai 	div->width = SUN8I_MBUS_DIV_WIDTH;
74*8f2bf2adSChen-Yu Tsai 	div->lock = &sun8i_a23_mbus_lock;
75*8f2bf2adSChen-Yu Tsai 
76*8f2bf2adSChen-Yu Tsai 	mux->reg = reg;
77*8f2bf2adSChen-Yu Tsai 	mux->shift = SUN8I_MBUS_MUX_SHIFT;
78*8f2bf2adSChen-Yu Tsai 	mux->mask = SUN8I_MBUS_MUX_MASK;
79*8f2bf2adSChen-Yu Tsai 	mux->lock = &sun8i_a23_mbus_lock;
80*8f2bf2adSChen-Yu Tsai 
81*8f2bf2adSChen-Yu Tsai 	clk = clk_register_composite(NULL, clk_name, parents, num_parents,
82*8f2bf2adSChen-Yu Tsai 				     &mux->hw, &clk_mux_ops,
83*8f2bf2adSChen-Yu Tsai 				     &div->hw, &clk_divider_ops,
84*8f2bf2adSChen-Yu Tsai 				     &gate->hw, &clk_gate_ops,
85*8f2bf2adSChen-Yu Tsai 				     0);
86*8f2bf2adSChen-Yu Tsai 	if (IS_ERR(clk))
87*8f2bf2adSChen-Yu Tsai 		goto err_free_gate;
88*8f2bf2adSChen-Yu Tsai 
89*8f2bf2adSChen-Yu Tsai 	err = of_clk_add_provider(node, of_clk_src_simple_get, clk);
90*8f2bf2adSChen-Yu Tsai 	if (err)
91*8f2bf2adSChen-Yu Tsai 		goto err_unregister_clk;
929c8176bfSChen-Yu Tsai 
939c8176bfSChen-Yu Tsai 	/* The MBUS clocks needs to be always enabled */
94*8f2bf2adSChen-Yu Tsai 	__clk_get(clk);
95*8f2bf2adSChen-Yu Tsai 	clk_prepare_enable(clk);
96*8f2bf2adSChen-Yu Tsai 
97*8f2bf2adSChen-Yu Tsai 	return;
98*8f2bf2adSChen-Yu Tsai 
99*8f2bf2adSChen-Yu Tsai err_unregister_clk:
100*8f2bf2adSChen-Yu Tsai 	/* TODO: The composite clock stuff will leak a bit here. */
101*8f2bf2adSChen-Yu Tsai 	clk_unregister(clk);
102*8f2bf2adSChen-Yu Tsai err_free_gate:
103*8f2bf2adSChen-Yu Tsai 	kfree(gate);
104*8f2bf2adSChen-Yu Tsai err_free_mux:
105*8f2bf2adSChen-Yu Tsai 	kfree(mux);
106*8f2bf2adSChen-Yu Tsai err_free_div:
107*8f2bf2adSChen-Yu Tsai 	kfree(div);
108*8f2bf2adSChen-Yu Tsai err_unmap:
109*8f2bf2adSChen-Yu Tsai 	iounmap(reg);
110*8f2bf2adSChen-Yu Tsai 	of_address_to_resource(node, 0, &res);
111*8f2bf2adSChen-Yu Tsai 	release_mem_region(res.start, resource_size(&res));
1129c8176bfSChen-Yu Tsai }
1139c8176bfSChen-Yu Tsai CLK_OF_DECLARE(sun8i_a23_mbus, "allwinner,sun8i-a23-mbus-clk", sun8i_a23_mbus_setup);
114