1*c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
29c8176bfSChen-Yu Tsai /*
39c8176bfSChen-Yu Tsai * Copyright 2014 Chen-Yu Tsai
49c8176bfSChen-Yu Tsai *
59c8176bfSChen-Yu Tsai * Chen-Yu Tsai <wens@csie.org>
69c8176bfSChen-Yu Tsai */
79c8176bfSChen-Yu Tsai
89dfefe8cSStephen Boyd #include <linux/clk.h>
99c8176bfSChen-Yu Tsai #include <linux/clk-provider.h>
1062e59c4eSStephen Boyd #include <linux/io.h>
118f2bf2adSChen-Yu Tsai #include <linux/slab.h>
128f2bf2adSChen-Yu Tsai #include <linux/spinlock.h>
139c8176bfSChen-Yu Tsai #include <linux/of_address.h>
149c8176bfSChen-Yu Tsai
158f2bf2adSChen-Yu Tsai #define SUN8I_MBUS_ENABLE 31
168f2bf2adSChen-Yu Tsai #define SUN8I_MBUS_MUX_SHIFT 24
178f2bf2adSChen-Yu Tsai #define SUN8I_MBUS_MUX_MASK 0x3
188f2bf2adSChen-Yu Tsai #define SUN8I_MBUS_DIV_SHIFT 0
198f2bf2adSChen-Yu Tsai #define SUN8I_MBUS_DIV_WIDTH 3
208f2bf2adSChen-Yu Tsai #define SUN8I_MBUS_MAX_PARENTS 4
219c8176bfSChen-Yu Tsai
229c8176bfSChen-Yu Tsai static DEFINE_SPINLOCK(sun8i_a23_mbus_lock);
239c8176bfSChen-Yu Tsai
sun8i_a23_mbus_setup(struct device_node * node)249c8176bfSChen-Yu Tsai static void __init sun8i_a23_mbus_setup(struct device_node *node)
259c8176bfSChen-Yu Tsai {
268f2bf2adSChen-Yu Tsai int num_parents = of_clk_get_parent_count(node);
271295e36aSStephen Boyd const char **parents;
288f2bf2adSChen-Yu Tsai const char *clk_name = node->name;
298f2bf2adSChen-Yu Tsai struct resource res;
308f2bf2adSChen-Yu Tsai struct clk_divider *div;
318f2bf2adSChen-Yu Tsai struct clk_gate *gate;
328f2bf2adSChen-Yu Tsai struct clk_mux *mux;
338f2bf2adSChen-Yu Tsai struct clk *clk;
347c74c220SHans de Goede void __iomem *reg;
358f2bf2adSChen-Yu Tsai int err;
367c74c220SHans de Goede
371295e36aSStephen Boyd parents = kcalloc(num_parents, sizeof(*parents), GFP_KERNEL);
381295e36aSStephen Boyd if (!parents)
391295e36aSStephen Boyd return;
401295e36aSStephen Boyd
418f2bf2adSChen-Yu Tsai reg = of_io_request_and_map(node, 0, of_node_full_name(node));
42fbe359f1SWei Yongjun if (IS_ERR(reg)) {
438f2bf2adSChen-Yu Tsai pr_err("Could not get registers for sun8i-mbus-clk\n");
441295e36aSStephen Boyd goto err_free_parents;
457c74c220SHans de Goede }
467c74c220SHans de Goede
478f2bf2adSChen-Yu Tsai div = kzalloc(sizeof(*div), GFP_KERNEL);
488f2bf2adSChen-Yu Tsai if (!div)
498f2bf2adSChen-Yu Tsai goto err_unmap;
508f2bf2adSChen-Yu Tsai
518f2bf2adSChen-Yu Tsai mux = kzalloc(sizeof(*mux), GFP_KERNEL);
528f2bf2adSChen-Yu Tsai if (!mux)
538f2bf2adSChen-Yu Tsai goto err_free_div;
548f2bf2adSChen-Yu Tsai
558f2bf2adSChen-Yu Tsai gate = kzalloc(sizeof(*gate), GFP_KERNEL);
568f2bf2adSChen-Yu Tsai if (!gate)
578f2bf2adSChen-Yu Tsai goto err_free_mux;
588f2bf2adSChen-Yu Tsai
598f2bf2adSChen-Yu Tsai of_property_read_string(node, "clock-output-names", &clk_name);
608f2bf2adSChen-Yu Tsai of_clk_parent_fill(node, parents, num_parents);
618f2bf2adSChen-Yu Tsai
628f2bf2adSChen-Yu Tsai gate->reg = reg;
638f2bf2adSChen-Yu Tsai gate->bit_idx = SUN8I_MBUS_ENABLE;
648f2bf2adSChen-Yu Tsai gate->lock = &sun8i_a23_mbus_lock;
658f2bf2adSChen-Yu Tsai
668f2bf2adSChen-Yu Tsai div->reg = reg;
678f2bf2adSChen-Yu Tsai div->shift = SUN8I_MBUS_DIV_SHIFT;
688f2bf2adSChen-Yu Tsai div->width = SUN8I_MBUS_DIV_WIDTH;
698f2bf2adSChen-Yu Tsai div->lock = &sun8i_a23_mbus_lock;
708f2bf2adSChen-Yu Tsai
718f2bf2adSChen-Yu Tsai mux->reg = reg;
728f2bf2adSChen-Yu Tsai mux->shift = SUN8I_MBUS_MUX_SHIFT;
738f2bf2adSChen-Yu Tsai mux->mask = SUN8I_MBUS_MUX_MASK;
748f2bf2adSChen-Yu Tsai mux->lock = &sun8i_a23_mbus_lock;
758f2bf2adSChen-Yu Tsai
769919d44fSStephen Boyd /* The MBUS clocks needs to be always enabled */
778f2bf2adSChen-Yu Tsai clk = clk_register_composite(NULL, clk_name, parents, num_parents,
788f2bf2adSChen-Yu Tsai &mux->hw, &clk_mux_ops,
798f2bf2adSChen-Yu Tsai &div->hw, &clk_divider_ops,
808f2bf2adSChen-Yu Tsai &gate->hw, &clk_gate_ops,
819919d44fSStephen Boyd CLK_IS_CRITICAL);
828f2bf2adSChen-Yu Tsai if (IS_ERR(clk))
838f2bf2adSChen-Yu Tsai goto err_free_gate;
848f2bf2adSChen-Yu Tsai
858f2bf2adSChen-Yu Tsai err = of_clk_add_provider(node, of_clk_src_simple_get, clk);
868f2bf2adSChen-Yu Tsai if (err)
878f2bf2adSChen-Yu Tsai goto err_unregister_clk;
889c8176bfSChen-Yu Tsai
891295e36aSStephen Boyd kfree(parents); /* parents is deep copied */
908f2bf2adSChen-Yu Tsai
918f2bf2adSChen-Yu Tsai return;
928f2bf2adSChen-Yu Tsai
938f2bf2adSChen-Yu Tsai err_unregister_clk:
948f2bf2adSChen-Yu Tsai /* TODO: The composite clock stuff will leak a bit here. */
958f2bf2adSChen-Yu Tsai clk_unregister(clk);
968f2bf2adSChen-Yu Tsai err_free_gate:
978f2bf2adSChen-Yu Tsai kfree(gate);
988f2bf2adSChen-Yu Tsai err_free_mux:
998f2bf2adSChen-Yu Tsai kfree(mux);
1008f2bf2adSChen-Yu Tsai err_free_div:
1018f2bf2adSChen-Yu Tsai kfree(div);
1028f2bf2adSChen-Yu Tsai err_unmap:
1038f2bf2adSChen-Yu Tsai iounmap(reg);
1048f2bf2adSChen-Yu Tsai of_address_to_resource(node, 0, &res);
1058f2bf2adSChen-Yu Tsai release_mem_region(res.start, resource_size(&res));
1061295e36aSStephen Boyd err_free_parents:
1071295e36aSStephen Boyd kfree(parents);
1089c8176bfSChen-Yu Tsai }
1099c8176bfSChen-Yu Tsai CLK_OF_DECLARE(sun8i_a23_mbus, "allwinner,sun8i-a23-mbus-clk", sun8i_a23_mbus_setup);
110