|  | /* | 
|  | * Copyright (c) 2013 MundoReader S.L. | 
|  | * Author: Heiko Stuebner <heiko@sntech.de> | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or modify | 
|  | * it under the terms of the GNU General Public License as published by | 
|  | * the Free Software Foundation; either version 2 of the License, or | 
|  | * (at your option) any later version. | 
|  | * | 
|  | * This program is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | * GNU General Public License for more details. | 
|  | */ | 
|  |  | 
|  | #include <linux/clk-provider.h> | 
|  | #include <linux/clkdev.h> | 
|  | #include <linux/of.h> | 
|  | #include <linux/of_address.h> | 
|  |  | 
|  | static DEFINE_SPINLOCK(clk_lock); | 
|  |  | 
|  | /* | 
|  | * Gate clocks | 
|  | */ | 
|  |  | 
|  | static void __init rk2928_gate_clk_init(struct device_node *node) | 
|  | { | 
|  | struct clk_onecell_data *clk_data; | 
|  | const char *clk_parent; | 
|  | const char *clk_name; | 
|  | void __iomem *reg; | 
|  | void __iomem *reg_idx; | 
|  | int flags; | 
|  | int qty; | 
|  | int reg_bit; | 
|  | int clkflags = CLK_SET_RATE_PARENT; | 
|  | int i; | 
|  |  | 
|  | qty = of_property_count_strings(node, "clock-output-names"); | 
|  | if (qty < 0) { | 
|  | pr_err("%s: error in clock-output-names %d\n", __func__, qty); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (qty == 0) { | 
|  | pr_info("%s: nothing to do\n", __func__); | 
|  | return; | 
|  | } | 
|  |  | 
|  | reg = of_iomap(node, 0); | 
|  | if (!reg) | 
|  | return; | 
|  |  | 
|  | clk_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); | 
|  | if (!clk_data) { | 
|  | iounmap(reg); | 
|  | return; | 
|  | } | 
|  |  | 
|  | clk_data->clks = kzalloc(qty * sizeof(struct clk *), GFP_KERNEL); | 
|  | if (!clk_data->clks) { | 
|  | kfree(clk_data); | 
|  | iounmap(reg); | 
|  | return; | 
|  | } | 
|  |  | 
|  | flags = CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE; | 
|  |  | 
|  | for (i = 0; i < qty; i++) { | 
|  | of_property_read_string_index(node, "clock-output-names", | 
|  | i, &clk_name); | 
|  |  | 
|  | /* ignore empty slots */ | 
|  | if (!strcmp("reserved", clk_name)) | 
|  | continue; | 
|  |  | 
|  | clk_parent = of_clk_get_parent_name(node, i); | 
|  |  | 
|  | /* keep all gates untouched for now */ | 
|  | clkflags |= CLK_IGNORE_UNUSED; | 
|  |  | 
|  | reg_idx = reg + (4 * (i / 16)); | 
|  | reg_bit = (i % 16); | 
|  |  | 
|  | clk_data->clks[i] = clk_register_gate(NULL, clk_name, | 
|  | clk_parent, clkflags, | 
|  | reg_idx, reg_bit, | 
|  | flags, | 
|  | &clk_lock); | 
|  | WARN_ON(IS_ERR(clk_data->clks[i])); | 
|  | } | 
|  |  | 
|  | clk_data->clk_num = qty; | 
|  |  | 
|  | of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); | 
|  | } | 
|  | CLK_OF_DECLARE(rk2928_gate, "rockchip,rk2928-gate-clk", rk2928_gate_clk_init); |