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 28 struct clk_gate2 { 29 struct clk_hw hw; 30 void __iomem *reg; 31 u8 bit_idx; 32 u8 cgr_val; 33 u8 flags; 34 spinlock_t *lock; 35 unsigned int *share_count; 36 }; 37 38 #define to_clk_gate2(_hw) container_of(_hw, struct clk_gate2, hw) 39 40 static void clk_gate2_do_shared_clks(struct clk_hw *hw, bool enable) 41 { 42 struct clk_gate2 *gate = to_clk_gate2(hw); 43 u32 reg; 44 45 reg = readl(gate->reg); 46 reg &= ~(3 << gate->bit_idx); 47 if (enable) 48 reg |= gate->cgr_val << gate->bit_idx; 49 writel(reg, gate->reg); 50 } 51 52 static int clk_gate2_enable(struct clk_hw *hw) 53 { 54 struct clk_gate2 *gate = to_clk_gate2(hw); 55 unsigned long flags; 56 int ret = 0; 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); 64 out: 65 spin_unlock_irqrestore(gate->lock, flags); 66 67 return ret; 68 } 69 70 static 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); 85 out: 86 spin_unlock_irqrestore(gate->lock, flags); 87 } 88 89 static int clk_gate2_reg_is_enabled(void __iomem *reg, u8 bit_idx) 90 { 91 u32 val = readl(reg); 92 93 if (((val >> bit_idx) & 1) == 1) 94 return 1; 95 96 return 0; 97 } 98 99 static int clk_gate2_is_enabled(struct clk_hw *hw) 100 { 101 struct clk_gate2 *gate = to_clk_gate2(hw); 102 103 return clk_gate2_reg_is_enabled(gate->reg, gate->bit_idx); 104 } 105 106 static void clk_gate2_disable_unused(struct clk_hw *hw) 107 { 108 struct clk_gate2 *gate = to_clk_gate2(hw); 109 unsigned long flags; 110 111 spin_lock_irqsave(gate->lock, flags); 112 113 if (!gate->share_count || *gate->share_count == 0) 114 clk_gate2_do_shared_clks(hw, false); 115 116 spin_unlock_irqrestore(gate->lock, flags); 117 } 118 119 static const struct clk_ops clk_gate2_ops = { 120 .enable = clk_gate2_enable, 121 .disable = clk_gate2_disable, 122 .disable_unused = clk_gate2_disable_unused, 123 .is_enabled = clk_gate2_is_enabled, 124 }; 125 126 struct clk_hw *clk_hw_register_gate2(struct device *dev, const char *name, 127 const char *parent_name, unsigned long flags, 128 void __iomem *reg, u8 bit_idx, u8 cgr_val, 129 u8 clk_gate2_flags, spinlock_t *lock, 130 unsigned int *share_count) 131 { 132 struct clk_gate2 *gate; 133 struct clk_hw *hw; 134 struct clk_init_data init; 135 int ret; 136 137 gate = kzalloc(sizeof(struct clk_gate2), GFP_KERNEL); 138 if (!gate) 139 return ERR_PTR(-ENOMEM); 140 141 /* struct clk_gate2 assignments */ 142 gate->reg = reg; 143 gate->bit_idx = bit_idx; 144 gate->cgr_val = cgr_val; 145 gate->flags = clk_gate2_flags; 146 gate->lock = lock; 147 gate->share_count = share_count; 148 149 init.name = name; 150 init.ops = &clk_gate2_ops; 151 init.flags = flags; 152 init.parent_names = parent_name ? &parent_name : NULL; 153 init.num_parents = parent_name ? 1 : 0; 154 155 gate->hw.init = &init; 156 hw = &gate->hw; 157 158 ret = clk_hw_register(dev, hw); 159 if (ret) { 160 kfree(gate); 161 return ERR_PTR(ret); 162 } 163 164 return hw; 165 } 166 EXPORT_SYMBOL_GPL(clk_hw_register_gate2); 167