162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 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/device.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 1762306a36Sopenharmony_ci/** 1862306a36Sopenharmony_ci * DOC: basic gatable clock which can gate and ungate it's ouput 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Traits of this clock: 2162306a36Sopenharmony_ci * prepare - clk_(un)prepare only ensures parent is (un)prepared 2262306a36Sopenharmony_ci * enable - clk_enable and clk_disable are functional & control gating 2362306a36Sopenharmony_ci * rate - inherits rate from parent. No clk_set_rate support 2462306a36Sopenharmony_ci * parent - fixed parent. No clk_set_parent support 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic inline u32 clk_gate_readl(struct clk_gate *gate) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci if (gate->flags & CLK_GATE_BIG_ENDIAN) 3062306a36Sopenharmony_ci return ioread32be(gate->reg); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci return readl(gate->reg); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic inline void clk_gate_writel(struct clk_gate *gate, u32 val) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci if (gate->flags & CLK_GATE_BIG_ENDIAN) 3862306a36Sopenharmony_ci iowrite32be(val, gate->reg); 3962306a36Sopenharmony_ci else 4062306a36Sopenharmony_ci writel(val, gate->reg); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* 4462306a36Sopenharmony_ci * It works on following logic: 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * For enabling clock, enable = 1 4762306a36Sopenharmony_ci * set2dis = 1 -> clear bit -> set = 0 4862306a36Sopenharmony_ci * set2dis = 0 -> set bit -> set = 1 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * For disabling clock, enable = 0 5162306a36Sopenharmony_ci * set2dis = 1 -> set bit -> set = 1 5262306a36Sopenharmony_ci * set2dis = 0 -> clear bit -> set = 0 5362306a36Sopenharmony_ci * 5462306a36Sopenharmony_ci * So, result is always: enable xor set2dis. 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_cistatic void clk_gate_endisable(struct clk_hw *hw, int enable) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct clk_gate *gate = to_clk_gate(hw); 5962306a36Sopenharmony_ci int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; 6062306a36Sopenharmony_ci unsigned long flags; 6162306a36Sopenharmony_ci u32 reg; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci set ^= enable; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (gate->lock) 6662306a36Sopenharmony_ci spin_lock_irqsave(gate->lock, flags); 6762306a36Sopenharmony_ci else 6862306a36Sopenharmony_ci __acquire(gate->lock); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (gate->flags & CLK_GATE_HIWORD_MASK) { 7162306a36Sopenharmony_ci reg = BIT(gate->bit_idx + 16); 7262306a36Sopenharmony_ci if (set) 7362306a36Sopenharmony_ci reg |= BIT(gate->bit_idx); 7462306a36Sopenharmony_ci } else { 7562306a36Sopenharmony_ci reg = clk_gate_readl(gate); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (set) 7862306a36Sopenharmony_ci reg |= BIT(gate->bit_idx); 7962306a36Sopenharmony_ci else 8062306a36Sopenharmony_ci reg &= ~BIT(gate->bit_idx); 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci clk_gate_writel(gate, reg); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (gate->lock) 8662306a36Sopenharmony_ci spin_unlock_irqrestore(gate->lock, flags); 8762306a36Sopenharmony_ci else 8862306a36Sopenharmony_ci __release(gate->lock); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic int clk_gate_enable(struct clk_hw *hw) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci clk_gate_endisable(hw, 1); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic void clk_gate_disable(struct clk_hw *hw) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci clk_gate_endisable(hw, 0); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ciint clk_gate_is_enabled(struct clk_hw *hw) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci u32 reg; 10662306a36Sopenharmony_ci struct clk_gate *gate = to_clk_gate(hw); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci reg = clk_gate_readl(gate); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* if a set bit disables this clk, flip it before masking */ 11162306a36Sopenharmony_ci if (gate->flags & CLK_GATE_SET_TO_DISABLE) 11262306a36Sopenharmony_ci reg ^= BIT(gate->bit_idx); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci reg &= BIT(gate->bit_idx); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return reg ? 1 : 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_gate_is_enabled); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ciconst struct clk_ops clk_gate_ops = { 12162306a36Sopenharmony_ci .enable = clk_gate_enable, 12262306a36Sopenharmony_ci .disable = clk_gate_disable, 12362306a36Sopenharmony_ci .is_enabled = clk_gate_is_enabled, 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_gate_ops); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistruct clk_hw *__clk_hw_register_gate(struct device *dev, 12862306a36Sopenharmony_ci struct device_node *np, const char *name, 12962306a36Sopenharmony_ci const char *parent_name, const struct clk_hw *parent_hw, 13062306a36Sopenharmony_ci const struct clk_parent_data *parent_data, 13162306a36Sopenharmony_ci unsigned long flags, 13262306a36Sopenharmony_ci void __iomem *reg, u8 bit_idx, 13362306a36Sopenharmony_ci u8 clk_gate_flags, spinlock_t *lock) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct clk_gate *gate; 13662306a36Sopenharmony_ci struct clk_hw *hw; 13762306a36Sopenharmony_ci struct clk_init_data init = {}; 13862306a36Sopenharmony_ci int ret = -EINVAL; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (clk_gate_flags & CLK_GATE_HIWORD_MASK) { 14162306a36Sopenharmony_ci if (bit_idx > 15) { 14262306a36Sopenharmony_ci pr_err("gate bit exceeds LOWORD field\n"); 14362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* allocate the gate */ 14862306a36Sopenharmony_ci gate = kzalloc(sizeof(*gate), GFP_KERNEL); 14962306a36Sopenharmony_ci if (!gate) 15062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci init.name = name; 15362306a36Sopenharmony_ci init.ops = &clk_gate_ops; 15462306a36Sopenharmony_ci init.flags = flags; 15562306a36Sopenharmony_ci init.parent_names = parent_name ? &parent_name : NULL; 15662306a36Sopenharmony_ci init.parent_hws = parent_hw ? &parent_hw : NULL; 15762306a36Sopenharmony_ci init.parent_data = parent_data; 15862306a36Sopenharmony_ci if (parent_name || parent_hw || parent_data) 15962306a36Sopenharmony_ci init.num_parents = 1; 16062306a36Sopenharmony_ci else 16162306a36Sopenharmony_ci init.num_parents = 0; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* struct clk_gate assignments */ 16462306a36Sopenharmony_ci gate->reg = reg; 16562306a36Sopenharmony_ci gate->bit_idx = bit_idx; 16662306a36Sopenharmony_ci gate->flags = clk_gate_flags; 16762306a36Sopenharmony_ci gate->lock = lock; 16862306a36Sopenharmony_ci gate->hw.init = &init; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci hw = &gate->hw; 17162306a36Sopenharmony_ci if (dev || !np) 17262306a36Sopenharmony_ci ret = clk_hw_register(dev, hw); 17362306a36Sopenharmony_ci else if (np) 17462306a36Sopenharmony_ci ret = of_clk_hw_register(np, hw); 17562306a36Sopenharmony_ci if (ret) { 17662306a36Sopenharmony_ci kfree(gate); 17762306a36Sopenharmony_ci hw = ERR_PTR(ret); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return hw; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__clk_hw_register_gate); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistruct clk *clk_register_gate(struct device *dev, const char *name, 18662306a36Sopenharmony_ci const char *parent_name, unsigned long flags, 18762306a36Sopenharmony_ci void __iomem *reg, u8 bit_idx, 18862306a36Sopenharmony_ci u8 clk_gate_flags, spinlock_t *lock) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct clk_hw *hw; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci hw = clk_hw_register_gate(dev, name, parent_name, flags, reg, 19362306a36Sopenharmony_ci bit_idx, clk_gate_flags, lock); 19462306a36Sopenharmony_ci if (IS_ERR(hw)) 19562306a36Sopenharmony_ci return ERR_CAST(hw); 19662306a36Sopenharmony_ci return hw->clk; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_register_gate); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_civoid clk_unregister_gate(struct clk *clk) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct clk_gate *gate; 20362306a36Sopenharmony_ci struct clk_hw *hw; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci hw = __clk_get_hw(clk); 20662306a36Sopenharmony_ci if (!hw) 20762306a36Sopenharmony_ci return; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci gate = to_clk_gate(hw); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci clk_unregister(clk); 21262306a36Sopenharmony_ci kfree(gate); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_unregister_gate); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_civoid clk_hw_unregister_gate(struct clk_hw *hw) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct clk_gate *gate; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci gate = to_clk_gate(hw); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci clk_hw_unregister(hw); 22362306a36Sopenharmony_ci kfree(gate); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_hw_unregister_gate); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic void devm_clk_hw_release_gate(struct device *dev, void *res) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci clk_hw_unregister_gate(*(struct clk_hw **)res); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistruct clk_hw *__devm_clk_hw_register_gate(struct device *dev, 23362306a36Sopenharmony_ci struct device_node *np, const char *name, 23462306a36Sopenharmony_ci const char *parent_name, const struct clk_hw *parent_hw, 23562306a36Sopenharmony_ci const struct clk_parent_data *parent_data, 23662306a36Sopenharmony_ci unsigned long flags, 23762306a36Sopenharmony_ci void __iomem *reg, u8 bit_idx, 23862306a36Sopenharmony_ci u8 clk_gate_flags, spinlock_t *lock) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct clk_hw **ptr, *hw; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci ptr = devres_alloc(devm_clk_hw_release_gate, sizeof(*ptr), GFP_KERNEL); 24362306a36Sopenharmony_ci if (!ptr) 24462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci hw = __clk_hw_register_gate(dev, np, name, parent_name, parent_hw, 24762306a36Sopenharmony_ci parent_data, flags, reg, bit_idx, 24862306a36Sopenharmony_ci clk_gate_flags, lock); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (!IS_ERR(hw)) { 25162306a36Sopenharmony_ci *ptr = hw; 25262306a36Sopenharmony_ci devres_add(dev, ptr); 25362306a36Sopenharmony_ci } else { 25462306a36Sopenharmony_ci devres_free(ptr); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return hw; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__devm_clk_hw_register_gate); 260