clk-gate2.c (4041B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com> 4 * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org> 5 * 6 * Gated clock implementation 7 */ 8 9#include <linux/clk-provider.h> 10#include <linux/export.h> 11#include <linux/module.h> 12#include <linux/slab.h> 13#include <linux/io.h> 14#include <linux/err.h> 15#include <linux/string.h> 16#include "clk.h" 17 18/** 19 * DOC: basic gateable clock which can gate and ungate its output 20 * 21 * Traits of this clock: 22 * prepare - clk_(un)prepare only ensures parent is (un)prepared 23 * enable - clk_enable and clk_disable are functional & control gating 24 * rate - inherits rate from parent. No clk_set_rate support 25 * parent - fixed parent. No clk_set_parent support 26 */ 27 28struct clk_gate2 { 29 struct clk_hw hw; 30 void __iomem *reg; 31 u8 bit_idx; 32 u8 cgr_val; 33 u8 cgr_mask; 34 u8 flags; 35 spinlock_t *lock; 36 unsigned int *share_count; 37}; 38 39#define to_clk_gate2(_hw) container_of(_hw, struct clk_gate2, hw) 40 41static void clk_gate2_do_shared_clks(struct clk_hw *hw, bool enable) 42{ 43 struct clk_gate2 *gate = to_clk_gate2(hw); 44 u32 reg; 45 46 reg = readl(gate->reg); 47 reg &= ~(gate->cgr_mask << gate->bit_idx); 48 if (enable) 49 reg |= (gate->cgr_val & gate->cgr_mask) << gate->bit_idx; 50 writel(reg, gate->reg); 51} 52 53static int clk_gate2_enable(struct clk_hw *hw) 54{ 55 struct clk_gate2 *gate = to_clk_gate2(hw); 56 unsigned long flags; 57 58 spin_lock_irqsave(gate->lock, flags); 59 60 if (gate->share_count && (*gate->share_count)++ > 0) 61 goto out; 62 63 clk_gate2_do_shared_clks(hw, true); 64out: 65 spin_unlock_irqrestore(gate->lock, flags); 66 67 return 0; 68} 69 70static void clk_gate2_disable(struct clk_hw *hw) 71{ 72 struct clk_gate2 *gate = to_clk_gate2(hw); 73 unsigned long flags; 74 75 spin_lock_irqsave(gate->lock, flags); 76 77 if (gate->share_count) { 78 if (WARN_ON(*gate->share_count == 0)) 79 goto out; 80 else if (--(*gate->share_count) > 0) 81 goto out; 82 } 83 84 clk_gate2_do_shared_clks(hw, false); 85out: 86 spin_unlock_irqrestore(gate->lock, flags); 87} 88 89static int clk_gate2_reg_is_enabled(void __iomem *reg, u8 bit_idx, 90 u8 cgr_val, u8 cgr_mask) 91{ 92 u32 val = readl(reg); 93 94 if (((val >> bit_idx) & cgr_mask) == cgr_val) 95 return 1; 96 97 return 0; 98} 99 100static int clk_gate2_is_enabled(struct clk_hw *hw) 101{ 102 struct clk_gate2 *gate = to_clk_gate2(hw); 103 unsigned long flags; 104 int ret = 0; 105 106 spin_lock_irqsave(gate->lock, flags); 107 108 ret = clk_gate2_reg_is_enabled(gate->reg, gate->bit_idx, 109 gate->cgr_val, gate->cgr_mask); 110 111 spin_unlock_irqrestore(gate->lock, flags); 112 113 return ret; 114} 115 116static void clk_gate2_disable_unused(struct clk_hw *hw) 117{ 118 struct clk_gate2 *gate = to_clk_gate2(hw); 119 unsigned long flags; 120 121 spin_lock_irqsave(gate->lock, flags); 122 123 if (!gate->share_count || *gate->share_count == 0) 124 clk_gate2_do_shared_clks(hw, false); 125 126 spin_unlock_irqrestore(gate->lock, flags); 127} 128 129static const struct clk_ops clk_gate2_ops = { 130 .enable = clk_gate2_enable, 131 .disable = clk_gate2_disable, 132 .disable_unused = clk_gate2_disable_unused, 133 .is_enabled = clk_gate2_is_enabled, 134}; 135 136struct clk_hw *clk_hw_register_gate2(struct device *dev, const char *name, 137 const char *parent_name, unsigned long flags, 138 void __iomem *reg, u8 bit_idx, u8 cgr_val, u8 cgr_mask, 139 u8 clk_gate2_flags, spinlock_t *lock, 140 unsigned int *share_count) 141{ 142 struct clk_gate2 *gate; 143 struct clk_hw *hw; 144 struct clk_init_data init; 145 int ret; 146 147 gate = kzalloc(sizeof(struct clk_gate2), GFP_KERNEL); 148 if (!gate) 149 return ERR_PTR(-ENOMEM); 150 151 /* struct clk_gate2 assignments */ 152 gate->reg = reg; 153 gate->bit_idx = bit_idx; 154 gate->cgr_val = cgr_val; 155 gate->cgr_mask = cgr_mask; 156 gate->flags = clk_gate2_flags; 157 gate->lock = lock; 158 gate->share_count = share_count; 159 160 init.name = name; 161 init.ops = &clk_gate2_ops; 162 init.flags = flags; 163 init.parent_names = parent_name ? &parent_name : NULL; 164 init.num_parents = parent_name ? 1 : 0; 165 166 gate->hw.init = &init; 167 hw = &gate->hw; 168 169 ret = clk_hw_register(dev, hw); 170 if (ret) { 171 kfree(gate); 172 return ERR_PTR(ret); 173 } 174 175 return hw; 176} 177EXPORT_SYMBOL_GPL(clk_hw_register_gate2);