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