162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com> 462306a36Sopenharmony_ci * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Gated clock implementation 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/clk-provider.h> 1062306a36Sopenharmony_ci#include <linux/export.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <linux/err.h> 1562306a36Sopenharmony_ci#include <linux/string.h> 1662306a36Sopenharmony_ci#include "clk.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/** 1962306a36Sopenharmony_ci * DOC: basic gateable clock which can gate and ungate its output 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * Traits of this clock: 2262306a36Sopenharmony_ci * prepare - clk_(un)prepare only ensures parent is (un)prepared 2362306a36Sopenharmony_ci * enable - clk_enable and clk_disable are functional & control gating 2462306a36Sopenharmony_ci * rate - inherits rate from parent. No clk_set_rate support 2562306a36Sopenharmony_ci * parent - fixed parent. No clk_set_parent support 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct clk_gate2 { 2962306a36Sopenharmony_ci struct clk_hw hw; 3062306a36Sopenharmony_ci void __iomem *reg; 3162306a36Sopenharmony_ci u8 bit_idx; 3262306a36Sopenharmony_ci u8 cgr_val; 3362306a36Sopenharmony_ci u8 cgr_mask; 3462306a36Sopenharmony_ci u8 flags; 3562306a36Sopenharmony_ci spinlock_t *lock; 3662306a36Sopenharmony_ci unsigned int *share_count; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define to_clk_gate2(_hw) container_of(_hw, struct clk_gate2, hw) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void clk_gate2_do_shared_clks(struct clk_hw *hw, bool enable) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct clk_gate2 *gate = to_clk_gate2(hw); 4462306a36Sopenharmony_ci u32 reg; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci reg = readl(gate->reg); 4762306a36Sopenharmony_ci reg &= ~(gate->cgr_mask << gate->bit_idx); 4862306a36Sopenharmony_ci if (enable) 4962306a36Sopenharmony_ci reg |= (gate->cgr_val & gate->cgr_mask) << gate->bit_idx; 5062306a36Sopenharmony_ci writel(reg, gate->reg); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic int clk_gate2_enable(struct clk_hw *hw) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct clk_gate2 *gate = to_clk_gate2(hw); 5662306a36Sopenharmony_ci unsigned long flags; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci spin_lock_irqsave(gate->lock, flags); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (gate->share_count && (*gate->share_count)++ > 0) 6162306a36Sopenharmony_ci goto out; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci clk_gate2_do_shared_clks(hw, true); 6462306a36Sopenharmony_ciout: 6562306a36Sopenharmony_ci spin_unlock_irqrestore(gate->lock, flags); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic void clk_gate2_disable(struct clk_hw *hw) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct clk_gate2 *gate = to_clk_gate2(hw); 7362306a36Sopenharmony_ci unsigned long flags; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci spin_lock_irqsave(gate->lock, flags); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (gate->share_count) { 7862306a36Sopenharmony_ci if (WARN_ON(*gate->share_count == 0)) 7962306a36Sopenharmony_ci goto out; 8062306a36Sopenharmony_ci else if (--(*gate->share_count) > 0) 8162306a36Sopenharmony_ci goto out; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci clk_gate2_do_shared_clks(hw, false); 8562306a36Sopenharmony_ciout: 8662306a36Sopenharmony_ci spin_unlock_irqrestore(gate->lock, flags); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int clk_gate2_reg_is_enabled(void __iomem *reg, u8 bit_idx, 9062306a36Sopenharmony_ci u8 cgr_val, u8 cgr_mask) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci u32 val = readl(reg); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (((val >> bit_idx) & cgr_mask) == cgr_val) 9562306a36Sopenharmony_ci return 1; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int clk_gate2_is_enabled(struct clk_hw *hw) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct clk_gate2 *gate = to_clk_gate2(hw); 10362306a36Sopenharmony_ci unsigned long flags; 10462306a36Sopenharmony_ci int ret = 0; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci spin_lock_irqsave(gate->lock, flags); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci ret = clk_gate2_reg_is_enabled(gate->reg, gate->bit_idx, 10962306a36Sopenharmony_ci gate->cgr_val, gate->cgr_mask); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci spin_unlock_irqrestore(gate->lock, flags); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return ret; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void clk_gate2_disable_unused(struct clk_hw *hw) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct clk_gate2 *gate = to_clk_gate2(hw); 11962306a36Sopenharmony_ci unsigned long flags; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci spin_lock_irqsave(gate->lock, flags); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (!gate->share_count || *gate->share_count == 0) 12462306a36Sopenharmony_ci clk_gate2_do_shared_clks(hw, false); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci spin_unlock_irqrestore(gate->lock, flags); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic const struct clk_ops clk_gate2_ops = { 13062306a36Sopenharmony_ci .enable = clk_gate2_enable, 13162306a36Sopenharmony_ci .disable = clk_gate2_disable, 13262306a36Sopenharmony_ci .disable_unused = clk_gate2_disable_unused, 13362306a36Sopenharmony_ci .is_enabled = clk_gate2_is_enabled, 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistruct clk_hw *clk_hw_register_gate2(struct device *dev, const char *name, 13762306a36Sopenharmony_ci const char *parent_name, unsigned long flags, 13862306a36Sopenharmony_ci void __iomem *reg, u8 bit_idx, u8 cgr_val, u8 cgr_mask, 13962306a36Sopenharmony_ci u8 clk_gate2_flags, spinlock_t *lock, 14062306a36Sopenharmony_ci unsigned int *share_count) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct clk_gate2 *gate; 14362306a36Sopenharmony_ci struct clk_hw *hw; 14462306a36Sopenharmony_ci struct clk_init_data init; 14562306a36Sopenharmony_ci int ret; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci gate = kzalloc(sizeof(struct clk_gate2), GFP_KERNEL); 14862306a36Sopenharmony_ci if (!gate) 14962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* struct clk_gate2 assignments */ 15262306a36Sopenharmony_ci gate->reg = reg; 15362306a36Sopenharmony_ci gate->bit_idx = bit_idx; 15462306a36Sopenharmony_ci gate->cgr_val = cgr_val; 15562306a36Sopenharmony_ci gate->cgr_mask = cgr_mask; 15662306a36Sopenharmony_ci gate->flags = clk_gate2_flags; 15762306a36Sopenharmony_ci gate->lock = lock; 15862306a36Sopenharmony_ci gate->share_count = share_count; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci init.name = name; 16162306a36Sopenharmony_ci init.ops = &clk_gate2_ops; 16262306a36Sopenharmony_ci init.flags = flags; 16362306a36Sopenharmony_ci init.parent_names = parent_name ? &parent_name : NULL; 16462306a36Sopenharmony_ci init.num_parents = parent_name ? 1 : 0; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci gate->hw.init = &init; 16762306a36Sopenharmony_ci hw = &gate->hw; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci ret = clk_hw_register(dev, hw); 17062306a36Sopenharmony_ci if (ret) { 17162306a36Sopenharmony_ci kfree(gate); 17262306a36Sopenharmony_ci return ERR_PTR(ret); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return hw; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_hw_register_gate2); 178