384 lines
		
	
	
	
		
			9.3 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			384 lines
		
	
	
	
		
			9.3 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * Copyright (c) 2012 National Instruments
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Josh Cartwright <josh.cartwright@ni.com>
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * This program is free software; you can redistribute it and/or modify it
							 | 
						||
| 
								 | 
							
								 * under the terms and conditions of the GNU General Public License,
							 | 
						||
| 
								 | 
							
								 * version 2, as published by the Free Software Foundation.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * This program is distributed in the hope 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.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * You should have received a copy of the GNU General Public License along with
							 | 
						||
| 
								 | 
							
								 * this program.  If not, see <http://www.gnu.org/licenses/>.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								#include <linux/io.h>
							 | 
						||
| 
								 | 
							
								#include <linux/of.h>
							 | 
						||
| 
								 | 
							
								#include <linux/slab.h>
							 | 
						||
| 
								 | 
							
								#include <linux/kernel.h>
							 | 
						||
| 
								 | 
							
								#include <linux/clk-provider.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void __iomem *slcr_base;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								struct zynq_pll_clk {
							 | 
						||
| 
								 | 
							
									struct clk_hw	hw;
							 | 
						||
| 
								 | 
							
									void __iomem	*pll_ctrl;
							 | 
						||
| 
								 | 
							
									void __iomem	*pll_cfg;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define to_zynq_pll_clk(hw)	container_of(hw, struct zynq_pll_clk, hw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define CTRL_PLL_FDIV(x)	((x) >> 12)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static unsigned long zynq_pll_recalc_rate(struct clk_hw *hw,
							 | 
						||
| 
								 | 
							
													  unsigned long parent_rate)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct zynq_pll_clk *pll = to_zynq_pll_clk(hw);
							 | 
						||
| 
								 | 
							
									return parent_rate * CTRL_PLL_FDIV(ioread32(pll->pll_ctrl));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static const struct clk_ops zynq_pll_clk_ops = {
							 | 
						||
| 
								 | 
							
									.recalc_rate	= zynq_pll_recalc_rate,
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void __init zynq_pll_clk_setup(struct device_node *np)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct clk_init_data init;
							 | 
						||
| 
								 | 
							
									struct zynq_pll_clk *pll;
							 | 
						||
| 
								 | 
							
									const char *parent_name;
							 | 
						||
| 
								 | 
							
									struct clk *clk;
							 | 
						||
| 
								 | 
							
									u32 regs[2];
							 | 
						||
| 
								 | 
							
									int ret;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									ret = of_property_read_u32_array(np, "reg", regs, ARRAY_SIZE(regs));
							 | 
						||
| 
								 | 
							
									if (WARN_ON(ret))
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									pll = kzalloc(sizeof(*pll), GFP_KERNEL);
							 | 
						||
| 
								 | 
							
									if (WARN_ON(!pll))
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									pll->pll_ctrl = slcr_base + regs[0];
							 | 
						||
| 
								 | 
							
									pll->pll_cfg  = slcr_base + regs[1];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									of_property_read_string(np, "clock-output-names", &init.name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									init.ops = &zynq_pll_clk_ops;
							 | 
						||
| 
								 | 
							
									parent_name = of_clk_get_parent_name(np, 0);
							 | 
						||
| 
								 | 
							
									init.parent_names = &parent_name;
							 | 
						||
| 
								 | 
							
									init.num_parents = 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									pll->hw.init = &init;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									clk = clk_register(NULL, &pll->hw);
							 | 
						||
| 
								 | 
							
									if (WARN_ON(IS_ERR(clk)))
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									ret = of_clk_add_provider(np, of_clk_src_simple_get, clk);
							 | 
						||
| 
								 | 
							
									if (WARN_ON(ret))
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								struct zynq_periph_clk {
							 | 
						||
| 
								 | 
							
									struct clk_hw		hw;
							 | 
						||
| 
								 | 
							
									struct clk_onecell_data	onecell_data;
							 | 
						||
| 
								 | 
							
									struct clk		*gates[2];
							 | 
						||
| 
								 | 
							
									void __iomem		*clk_ctrl;
							 | 
						||
| 
								 | 
							
									spinlock_t		clkact_lock;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define to_zynq_periph_clk(hw)	container_of(hw, struct zynq_periph_clk, hw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static const u8 periph_clk_parent_map[] = {
							 | 
						||
| 
								 | 
							
									0, 0, 1, 2
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								#define PERIPH_CLK_CTRL_SRC(x)	(periph_clk_parent_map[((x) & 0x30) >> 4])
							 | 
						||
| 
								 | 
							
								#define PERIPH_CLK_CTRL_DIV(x)	(((x) & 0x3F00) >> 8)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static unsigned long zynq_periph_recalc_rate(struct clk_hw *hw,
							 | 
						||
| 
								 | 
							
													     unsigned long parent_rate)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct zynq_periph_clk *periph = to_zynq_periph_clk(hw);
							 | 
						||
| 
								 | 
							
									return parent_rate / PERIPH_CLK_CTRL_DIV(ioread32(periph->clk_ctrl));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static u8 zynq_periph_get_parent(struct clk_hw *hw)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct zynq_periph_clk *periph = to_zynq_periph_clk(hw);
							 | 
						||
| 
								 | 
							
									return PERIPH_CLK_CTRL_SRC(ioread32(periph->clk_ctrl));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static const struct clk_ops zynq_periph_clk_ops = {
							 | 
						||
| 
								 | 
							
									.recalc_rate	= zynq_periph_recalc_rate,
							 | 
						||
| 
								 | 
							
									.get_parent	= zynq_periph_get_parent,
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void __init zynq_periph_clk_setup(struct device_node *np)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct zynq_periph_clk *periph;
							 | 
						||
| 
								 | 
							
									const char *parent_names[3];
							 | 
						||
| 
								 | 
							
									struct clk_init_data init;
							 | 
						||
| 
								 | 
							
									int clk_num = 0, err;
							 | 
						||
| 
								 | 
							
									const char *name;
							 | 
						||
| 
								 | 
							
									struct clk *clk;
							 | 
						||
| 
								 | 
							
									u32 reg;
							 | 
						||
| 
								 | 
							
									int i;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									err = of_property_read_u32(np, "reg", ®);
							 | 
						||
| 
								 | 
							
									if (WARN_ON(err))
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									periph = kzalloc(sizeof(*periph), GFP_KERNEL);
							 | 
						||
| 
								 | 
							
									if (WARN_ON(!periph))
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									periph->clk_ctrl = slcr_base + reg;
							 | 
						||
| 
								 | 
							
									spin_lock_init(&periph->clkact_lock);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									init.name = np->name;
							 | 
						||
| 
								 | 
							
									init.ops = &zynq_periph_clk_ops;
							 | 
						||
| 
								 | 
							
									for (i = 0; i < ARRAY_SIZE(parent_names); i++)
							 | 
						||
| 
								 | 
							
										parent_names[i] = of_clk_get_parent_name(np, i);
							 | 
						||
| 
								 | 
							
									init.parent_names = parent_names;
							 | 
						||
| 
								 | 
							
									init.num_parents = ARRAY_SIZE(parent_names);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									periph->hw.init = &init;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									clk = clk_register(NULL, &periph->hw);
							 | 
						||
| 
								 | 
							
									if (WARN_ON(IS_ERR(clk)))
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									err = of_clk_add_provider(np, of_clk_src_simple_get, clk);
							 | 
						||
| 
								 | 
							
									if (WARN_ON(err))
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									err = of_property_read_string_index(np, "clock-output-names", 0,
							 | 
						||
| 
								 | 
							
													    &name);
							 | 
						||
| 
								 | 
							
									if (WARN_ON(err))
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									periph->gates[0] = clk_register_gate(NULL, name, np->name, 0,
							 | 
						||
| 
								 | 
							
													     periph->clk_ctrl, 0, 0,
							 | 
						||
| 
								 | 
							
													     &periph->clkact_lock);
							 | 
						||
| 
								 | 
							
									if (WARN_ON(IS_ERR(periph->gates[0])))
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
									clk_num++;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/* some periph clks have 2 downstream gates */
							 | 
						||
| 
								 | 
							
									err = of_property_read_string_index(np, "clock-output-names", 1,
							 | 
						||
| 
								 | 
							
													    &name);
							 | 
						||
| 
								 | 
							
									if (err != -ENODATA) {
							 | 
						||
| 
								 | 
							
										periph->gates[1] = clk_register_gate(NULL, name, np->name, 0,
							 | 
						||
| 
								 | 
							
														     periph->clk_ctrl, 1, 0,
							 | 
						||
| 
								 | 
							
														     &periph->clkact_lock);
							 | 
						||
| 
								 | 
							
										if (WARN_ON(IS_ERR(periph->gates[1])))
							 | 
						||
| 
								 | 
							
											return;
							 | 
						||
| 
								 | 
							
										clk_num++;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									periph->onecell_data.clks = periph->gates;
							 | 
						||
| 
								 | 
							
									periph->onecell_data.clk_num = clk_num;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									err = of_clk_add_provider(np, of_clk_src_onecell_get,
							 | 
						||
| 
								 | 
							
												  &periph->onecell_data);
							 | 
						||
| 
								 | 
							
									if (WARN_ON(err))
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* CPU Clock domain is modelled as a mux with 4 children subclks, whose
							 | 
						||
| 
								 | 
							
								 * derivative rates depend on CLK_621_TRUE
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								struct zynq_cpu_clk {
							 | 
						||
| 
								 | 
							
									struct clk_hw		hw;
							 | 
						||
| 
								 | 
							
									struct clk_onecell_data	onecell_data;
							 | 
						||
| 
								 | 
							
									struct clk		*subclks[4];
							 | 
						||
| 
								 | 
							
									void __iomem		*clk_ctrl;
							 | 
						||
| 
								 | 
							
									spinlock_t		clkact_lock;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define to_zynq_cpu_clk(hw)	container_of(hw, struct zynq_cpu_clk, hw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static const u8 zynq_cpu_clk_parent_map[] = {
							 | 
						||
| 
								 | 
							
									1, 1, 2, 0
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								#define CPU_CLK_SRCSEL(x)	(zynq_cpu_clk_parent_map[(((x) & 0x30) >> 4)])
							 | 
						||
| 
								 | 
							
								#define CPU_CLK_CTRL_DIV(x)	(((x) & 0x3F00) >> 8)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static u8 zynq_cpu_clk_get_parent(struct clk_hw *hw)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct zynq_cpu_clk *cpuclk = to_zynq_cpu_clk(hw);
							 | 
						||
| 
								 | 
							
									return CPU_CLK_SRCSEL(ioread32(cpuclk->clk_ctrl));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static unsigned long zynq_cpu_clk_recalc_rate(struct clk_hw *hw,
							 | 
						||
| 
								 | 
							
													      unsigned long parent_rate)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct zynq_cpu_clk *cpuclk = to_zynq_cpu_clk(hw);
							 | 
						||
| 
								 | 
							
									return parent_rate / CPU_CLK_CTRL_DIV(ioread32(cpuclk->clk_ctrl));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static const struct clk_ops zynq_cpu_clk_ops = {
							 | 
						||
| 
								 | 
							
									.get_parent	= zynq_cpu_clk_get_parent,
							 | 
						||
| 
								 | 
							
									.recalc_rate	= zynq_cpu_clk_recalc_rate,
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								struct zynq_cpu_subclk {
							 | 
						||
| 
								 | 
							
									struct clk_hw	hw;
							 | 
						||
| 
								 | 
							
									void __iomem	*clk_621;
							 | 
						||
| 
								 | 
							
									enum {
							 | 
						||
| 
								 | 
							
										CPU_SUBCLK_6X4X,
							 | 
						||
| 
								 | 
							
										CPU_SUBCLK_3X2X,
							 | 
						||
| 
								 | 
							
										CPU_SUBCLK_2X,
							 | 
						||
| 
								 | 
							
										CPU_SUBCLK_1X,
							 | 
						||
| 
								 | 
							
									} which;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define CLK_621_TRUE(x)	((x) & 1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define to_zynq_cpu_subclk(hw)	container_of(hw, struct zynq_cpu_subclk, hw);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static unsigned long zynq_cpu_subclk_recalc_rate(struct clk_hw *hw,
							 | 
						||
| 
								 | 
							
														 unsigned long parent_rate)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									unsigned long uninitialized_var(rate);
							 | 
						||
| 
								 | 
							
									struct zynq_cpu_subclk *subclk;
							 | 
						||
| 
								 | 
							
									bool is_621;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									subclk = to_zynq_cpu_subclk(hw)
							 | 
						||
| 
								 | 
							
									is_621 = CLK_621_TRUE(ioread32(subclk->clk_621));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									switch (subclk->which) {
							 | 
						||
| 
								 | 
							
									case CPU_SUBCLK_6X4X:
							 | 
						||
| 
								 | 
							
										rate = parent_rate;
							 | 
						||
| 
								 | 
							
										break;
							 | 
						||
| 
								 | 
							
									case CPU_SUBCLK_3X2X:
							 | 
						||
| 
								 | 
							
										rate = parent_rate / 2;
							 | 
						||
| 
								 | 
							
										break;
							 | 
						||
| 
								 | 
							
									case CPU_SUBCLK_2X:
							 | 
						||
| 
								 | 
							
										rate = parent_rate / (is_621 ? 3 : 2);
							 | 
						||
| 
								 | 
							
										break;
							 | 
						||
| 
								 | 
							
									case CPU_SUBCLK_1X:
							 | 
						||
| 
								 | 
							
										rate = parent_rate / (is_621 ? 6 : 4);
							 | 
						||
| 
								 | 
							
										break;
							 | 
						||
| 
								 | 
							
									};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return rate;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static const struct clk_ops zynq_cpu_subclk_ops = {
							 | 
						||
| 
								 | 
							
									.recalc_rate	= zynq_cpu_subclk_recalc_rate,
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static struct clk *zynq_cpu_subclk_setup(struct device_node *np, u8 which,
							 | 
						||
| 
								 | 
							
													 void __iomem *clk_621)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct zynq_cpu_subclk *subclk;
							 | 
						||
| 
								 | 
							
									struct clk_init_data init;
							 | 
						||
| 
								 | 
							
									struct clk *clk;
							 | 
						||
| 
								 | 
							
									int err;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									err = of_property_read_string_index(np, "clock-output-names",
							 | 
						||
| 
								 | 
							
													    which, &init.name);
							 | 
						||
| 
								 | 
							
									if (WARN_ON(err))
							 | 
						||
| 
								 | 
							
										goto err_read_output_name;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									subclk = kzalloc(sizeof(*subclk), GFP_KERNEL);
							 | 
						||
| 
								 | 
							
									if (!subclk)
							 | 
						||
| 
								 | 
							
										goto err_subclk_alloc;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									subclk->clk_621 = clk_621;
							 | 
						||
| 
								 | 
							
									subclk->which = which;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									init.ops = &zynq_cpu_subclk_ops;
							 | 
						||
| 
								 | 
							
									init.parent_names = &np->name;
							 | 
						||
| 
								 | 
							
									init.num_parents = 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									subclk->hw.init = &init;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									clk = clk_register(NULL, &subclk->hw);
							 | 
						||
| 
								 | 
							
									if (WARN_ON(IS_ERR(clk)))
							 | 
						||
| 
								 | 
							
										goto err_clk_register;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return clk;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								err_clk_register:
							 | 
						||
| 
								 | 
							
									kfree(subclk);
							 | 
						||
| 
								 | 
							
								err_subclk_alloc:
							 | 
						||
| 
								 | 
							
								err_read_output_name:
							 | 
						||
| 
								 | 
							
									return ERR_PTR(-EINVAL);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void __init zynq_cpu_clk_setup(struct device_node *np)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct zynq_cpu_clk *cpuclk;
							 | 
						||
| 
								 | 
							
									const char *parent_names[3];
							 | 
						||
| 
								 | 
							
									struct clk_init_data init;
							 | 
						||
| 
								 | 
							
									void __iomem *clk_621;
							 | 
						||
| 
								 | 
							
									struct clk *clk;
							 | 
						||
| 
								 | 
							
									u32 reg[2];
							 | 
						||
| 
								 | 
							
									int err;
							 | 
						||
| 
								 | 
							
									int i;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									err = of_property_read_u32_array(np, "reg", reg, ARRAY_SIZE(reg));
							 | 
						||
| 
								 | 
							
									if (WARN_ON(err))
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
							 | 
						||
| 
								 | 
							
									if (WARN_ON(!cpuclk))
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									cpuclk->clk_ctrl = slcr_base + reg[0];
							 | 
						||
| 
								 | 
							
									clk_621 = slcr_base + reg[1];
							 | 
						||
| 
								 | 
							
									spin_lock_init(&cpuclk->clkact_lock);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									init.name = np->name;
							 | 
						||
| 
								 | 
							
									init.ops = &zynq_cpu_clk_ops;
							 | 
						||
| 
								 | 
							
									for (i = 0; i < ARRAY_SIZE(parent_names); i++)
							 | 
						||
| 
								 | 
							
										parent_names[i] = of_clk_get_parent_name(np, i);
							 | 
						||
| 
								 | 
							
									init.parent_names = parent_names;
							 | 
						||
| 
								 | 
							
									init.num_parents = ARRAY_SIZE(parent_names);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									cpuclk->hw.init = &init;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									clk = clk_register(NULL, &cpuclk->hw);
							 | 
						||
| 
								 | 
							
									if (WARN_ON(IS_ERR(clk)))
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									err = of_clk_add_provider(np, of_clk_src_simple_get, clk);
							 | 
						||
| 
								 | 
							
									if (WARN_ON(err))
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for (i = 0; i < 4; i++) {
							 | 
						||
| 
								 | 
							
										cpuclk->subclks[i] = zynq_cpu_subclk_setup(np, i, clk_621);
							 | 
						||
| 
								 | 
							
										if (WARN_ON(IS_ERR(cpuclk->subclks[i])))
							 | 
						||
| 
								 | 
							
											return;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									cpuclk->onecell_data.clks = cpuclk->subclks;
							 | 
						||
| 
								 | 
							
									cpuclk->onecell_data.clk_num = i;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									err = of_clk_add_provider(np, of_clk_src_onecell_get,
							 | 
						||
| 
								 | 
							
												  &cpuclk->onecell_data);
							 | 
						||
| 
								 | 
							
									if (WARN_ON(err))
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static const __initconst struct of_device_id zynq_clk_match[] = {
							 | 
						||
| 
								 | 
							
									{ .compatible = "fixed-clock", .data = of_fixed_clk_setup, },
							 | 
						||
| 
								 | 
							
									{ .compatible = "xlnx,zynq-pll", .data = zynq_pll_clk_setup, },
							 | 
						||
| 
								 | 
							
									{ .compatible = "xlnx,zynq-periph-clock",
							 | 
						||
| 
								 | 
							
										.data = zynq_periph_clk_setup, },
							 | 
						||
| 
								 | 
							
									{ .compatible = "xlnx,zynq-cpu-clock", .data = zynq_cpu_clk_setup, },
							 | 
						||
| 
								 | 
							
									{}
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void __init xilinx_zynq_clocks_init(void __iomem *slcr)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									slcr_base = slcr;
							 | 
						||
| 
								 | 
							
									of_clk_init(zynq_clk_match);
							 | 
						||
| 
								 | 
							
								}
							 |