162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Zynq UltraScale+ MPSoC clock controller 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2016-2018 Xilinx 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Gated clock implementation 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/clk-provider.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include "clk-zynqmp.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/** 1562306a36Sopenharmony_ci * struct zynqmp_clk_gate - gating clock 1662306a36Sopenharmony_ci * @hw: handle between common and hardware-specific interfaces 1762306a36Sopenharmony_ci * @flags: hardware-specific flags 1862306a36Sopenharmony_ci * @clk_id: Id of clock 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_cistruct zynqmp_clk_gate { 2162306a36Sopenharmony_ci struct clk_hw hw; 2262306a36Sopenharmony_ci u8 flags; 2362306a36Sopenharmony_ci u32 clk_id; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define to_zynqmp_clk_gate(_hw) container_of(_hw, struct zynqmp_clk_gate, hw) 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/** 2962306a36Sopenharmony_ci * zynqmp_clk_gate_enable() - Enable clock 3062306a36Sopenharmony_ci * @hw: handle between common and hardware-specific interfaces 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * Return: 0 on success else error code 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_cistatic int zynqmp_clk_gate_enable(struct clk_hw *hw) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw); 3762306a36Sopenharmony_ci const char *clk_name = clk_hw_get_name(hw); 3862306a36Sopenharmony_ci u32 clk_id = gate->clk_id; 3962306a36Sopenharmony_ci int ret; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci ret = zynqmp_pm_clock_enable(clk_id); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (ret) 4462306a36Sopenharmony_ci pr_debug("%s() clock enable failed for %s (id %d), ret = %d\n", 4562306a36Sopenharmony_ci __func__, clk_name, clk_id, ret); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci return ret; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* 5162306a36Sopenharmony_ci * zynqmp_clk_gate_disable() - Disable clock 5262306a36Sopenharmony_ci * @hw: handle between common and hardware-specific interfaces 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_cistatic void zynqmp_clk_gate_disable(struct clk_hw *hw) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw); 5762306a36Sopenharmony_ci const char *clk_name = clk_hw_get_name(hw); 5862306a36Sopenharmony_ci u32 clk_id = gate->clk_id; 5962306a36Sopenharmony_ci int ret; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci ret = zynqmp_pm_clock_disable(clk_id); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (ret) 6462306a36Sopenharmony_ci pr_debug("%s() clock disable failed for %s (id %d), ret = %d\n", 6562306a36Sopenharmony_ci __func__, clk_name, clk_id, ret); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/** 6962306a36Sopenharmony_ci * zynqmp_clk_gate_is_enabled() - Check clock state 7062306a36Sopenharmony_ci * @hw: handle between common and hardware-specific interfaces 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * Return: 1 if enabled, 0 if disabled else error code 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_cistatic int zynqmp_clk_gate_is_enabled(struct clk_hw *hw) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw); 7762306a36Sopenharmony_ci const char *clk_name = clk_hw_get_name(hw); 7862306a36Sopenharmony_ci u32 clk_id = gate->clk_id; 7962306a36Sopenharmony_ci int state, ret; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci ret = zynqmp_pm_clock_getstate(clk_id, &state); 8262306a36Sopenharmony_ci if (ret) { 8362306a36Sopenharmony_ci pr_debug("%s() clock get state failed for %s, ret = %d\n", 8462306a36Sopenharmony_ci __func__, clk_name, ret); 8562306a36Sopenharmony_ci return -EIO; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return state ? 1 : 0; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic const struct clk_ops zynqmp_clk_gate_ops = { 9262306a36Sopenharmony_ci .enable = zynqmp_clk_gate_enable, 9362306a36Sopenharmony_ci .disable = zynqmp_clk_gate_disable, 9462306a36Sopenharmony_ci .is_enabled = zynqmp_clk_gate_is_enabled, 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/** 9862306a36Sopenharmony_ci * zynqmp_clk_register_gate() - Register a gate clock with the clock framework 9962306a36Sopenharmony_ci * @name: Name of this clock 10062306a36Sopenharmony_ci * @clk_id: Id of this clock 10162306a36Sopenharmony_ci * @parents: Name of this clock's parents 10262306a36Sopenharmony_ci * @num_parents: Number of parents 10362306a36Sopenharmony_ci * @nodes: Clock topology node 10462306a36Sopenharmony_ci * 10562306a36Sopenharmony_ci * Return: clock hardware of the registered clock gate 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_cistruct clk_hw *zynqmp_clk_register_gate(const char *name, u32 clk_id, 10862306a36Sopenharmony_ci const char * const *parents, 10962306a36Sopenharmony_ci u8 num_parents, 11062306a36Sopenharmony_ci const struct clock_topology *nodes) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct zynqmp_clk_gate *gate; 11362306a36Sopenharmony_ci struct clk_hw *hw; 11462306a36Sopenharmony_ci int ret; 11562306a36Sopenharmony_ci struct clk_init_data init; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* allocate the gate */ 11862306a36Sopenharmony_ci gate = kzalloc(sizeof(*gate), GFP_KERNEL); 11962306a36Sopenharmony_ci if (!gate) 12062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci init.name = name; 12362306a36Sopenharmony_ci init.ops = &zynqmp_clk_gate_ops; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci init.flags = zynqmp_clk_map_common_ccf_flags(nodes->flag); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci init.parent_names = parents; 12862306a36Sopenharmony_ci init.num_parents = 1; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* struct clk_gate assignments */ 13162306a36Sopenharmony_ci gate->flags = nodes->type_flag; 13262306a36Sopenharmony_ci gate->hw.init = &init; 13362306a36Sopenharmony_ci gate->clk_id = clk_id; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci hw = &gate->hw; 13662306a36Sopenharmony_ci ret = clk_hw_register(NULL, hw); 13762306a36Sopenharmony_ci if (ret) { 13862306a36Sopenharmony_ci kfree(gate); 13962306a36Sopenharmony_ci hw = ERR_PTR(ret); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return hw; 14362306a36Sopenharmony_ci} 144