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 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