1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright 2014 Chen-Yu Tsai 4 * 5 * Chen-Yu Tsai <wens@csie.org> 6 */ 7 8 #include <linux/clk.h> 9 #include <linux/clk-provider.h> 10 #include <linux/io.h> 11 #include <linux/slab.h> 12 #include <linux/spinlock.h> 13 #include <linux/of_address.h> 14 15 #define SUN8I_MBUS_ENABLE 31 16 #define SUN8I_MBUS_MUX_SHIFT 24 17 #define SUN8I_MBUS_MUX_MASK 0x3 18 #define SUN8I_MBUS_DIV_SHIFT 0 19 #define SUN8I_MBUS_DIV_WIDTH 3 20 #define SUN8I_MBUS_MAX_PARENTS 4 21 22 static DEFINE_SPINLOCK(sun8i_a23_mbus_lock); 23 24 static void __init sun8i_a23_mbus_setup(struct device_node *node) 25 { 26 int num_parents = of_clk_get_parent_count(node); 27 const char **parents; 28 const char *clk_name = node->name; 29 struct resource res; 30 struct clk_divider *div; 31 struct clk_gate *gate; 32 struct clk_mux *mux; 33 struct clk *clk; 34 void __iomem *reg; 35 int err; 36 37 parents = kcalloc(num_parents, sizeof(*parents), GFP_KERNEL); 38 if (!parents) 39 return; 40 41 reg = of_io_request_and_map(node, 0, of_node_full_name(node)); 42 if (IS_ERR(reg)) { 43 pr_err("Could not get registers for sun8i-mbus-clk\n"); 44 goto err_free_parents; 45 } 46 47 div = kzalloc(sizeof(*div), GFP_KERNEL); 48 if (!div) 49 goto err_unmap; 50 51 mux = kzalloc(sizeof(*mux), GFP_KERNEL); 52 if (!mux) 53 goto err_free_div; 54 55 gate = kzalloc(sizeof(*gate), GFP_KERNEL); 56 if (!gate) 57 goto err_free_mux; 58 59 of_property_read_string(node, "clock-output-names", &clk_name); 60 of_clk_parent_fill(node, parents, num_parents); 61 62 gate->reg = reg; 63 gate->bit_idx = SUN8I_MBUS_ENABLE; 64 gate->lock = &sun8i_a23_mbus_lock; 65 66 div->reg = reg; 67 div->shift = SUN8I_MBUS_DIV_SHIFT; 68 div->width = SUN8I_MBUS_DIV_WIDTH; 69 div->lock = &sun8i_a23_mbus_lock; 70 71 mux->reg = reg; 72 mux->shift = SUN8I_MBUS_MUX_SHIFT; 73 mux->mask = SUN8I_MBUS_MUX_MASK; 74 mux->lock = &sun8i_a23_mbus_lock; 75 76 /* The MBUS clocks needs to be always enabled */ 77 clk = clk_register_composite(NULL, clk_name, parents, num_parents, 78 &mux->hw, &clk_mux_ops, 79 &div->hw, &clk_divider_ops, 80 &gate->hw, &clk_gate_ops, 81 CLK_IS_CRITICAL); 82 if (IS_ERR(clk)) 83 goto err_free_gate; 84 85 err = of_clk_add_provider(node, of_clk_src_simple_get, clk); 86 if (err) 87 goto err_unregister_clk; 88 89 kfree(parents); /* parents is deep copied */ 90 91 return; 92 93 err_unregister_clk: 94 /* TODO: The composite clock stuff will leak a bit here. */ 95 clk_unregister(clk); 96 err_free_gate: 97 kfree(gate); 98 err_free_mux: 99 kfree(mux); 100 err_free_div: 101 kfree(div); 102 err_unmap: 103 iounmap(reg); 104 of_address_to_resource(node, 0, &res); 105 release_mem_region(res.start, resource_size(&res)); 106 err_free_parents: 107 kfree(parents); 108 } 109 CLK_OF_DECLARE(sun8i_a23_mbus, "allwinner,sun8i-a23-mbus-clk", sun8i_a23_mbus_setup); 110