0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/clk-provider.h>
0010 #include <linux/export.h>
0011 #include <linux/module.h>
0012 #include <linux/slab.h>
0013 #include <linux/io.h>
0014 #include <linux/err.h>
0015 #include <linux/string.h>
0016 #include "clk.h"
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028 struct clk_gate2 {
0029 struct clk_hw hw;
0030 void __iomem *reg;
0031 u8 bit_idx;
0032 u8 cgr_val;
0033 u8 cgr_mask;
0034 u8 flags;
0035 spinlock_t *lock;
0036 unsigned int *share_count;
0037 };
0038
0039 #define to_clk_gate2(_hw) container_of(_hw, struct clk_gate2, hw)
0040
0041 static void clk_gate2_do_shared_clks(struct clk_hw *hw, bool enable)
0042 {
0043 struct clk_gate2 *gate = to_clk_gate2(hw);
0044 u32 reg;
0045
0046 reg = readl(gate->reg);
0047 reg &= ~(gate->cgr_mask << gate->bit_idx);
0048 if (enable)
0049 reg |= (gate->cgr_val & gate->cgr_mask) << gate->bit_idx;
0050 writel(reg, gate->reg);
0051 }
0052
0053 static int clk_gate2_enable(struct clk_hw *hw)
0054 {
0055 struct clk_gate2 *gate = to_clk_gate2(hw);
0056 unsigned long flags;
0057
0058 spin_lock_irqsave(gate->lock, flags);
0059
0060 if (gate->share_count && (*gate->share_count)++ > 0)
0061 goto out;
0062
0063 clk_gate2_do_shared_clks(hw, true);
0064 out:
0065 spin_unlock_irqrestore(gate->lock, flags);
0066
0067 return 0;
0068 }
0069
0070 static void clk_gate2_disable(struct clk_hw *hw)
0071 {
0072 struct clk_gate2 *gate = to_clk_gate2(hw);
0073 unsigned long flags;
0074
0075 spin_lock_irqsave(gate->lock, flags);
0076
0077 if (gate->share_count) {
0078 if (WARN_ON(*gate->share_count == 0))
0079 goto out;
0080 else if (--(*gate->share_count) > 0)
0081 goto out;
0082 }
0083
0084 clk_gate2_do_shared_clks(hw, false);
0085 out:
0086 spin_unlock_irqrestore(gate->lock, flags);
0087 }
0088
0089 static int clk_gate2_reg_is_enabled(void __iomem *reg, u8 bit_idx,
0090 u8 cgr_val, u8 cgr_mask)
0091 {
0092 u32 val = readl(reg);
0093
0094 if (((val >> bit_idx) & cgr_mask) == cgr_val)
0095 return 1;
0096
0097 return 0;
0098 }
0099
0100 static int clk_gate2_is_enabled(struct clk_hw *hw)
0101 {
0102 struct clk_gate2 *gate = to_clk_gate2(hw);
0103 unsigned long flags;
0104 int ret = 0;
0105
0106 spin_lock_irqsave(gate->lock, flags);
0107
0108 ret = clk_gate2_reg_is_enabled(gate->reg, gate->bit_idx,
0109 gate->cgr_val, gate->cgr_mask);
0110
0111 spin_unlock_irqrestore(gate->lock, flags);
0112
0113 return ret;
0114 }
0115
0116 static void clk_gate2_disable_unused(struct clk_hw *hw)
0117 {
0118 struct clk_gate2 *gate = to_clk_gate2(hw);
0119 unsigned long flags;
0120
0121 spin_lock_irqsave(gate->lock, flags);
0122
0123 if (!gate->share_count || *gate->share_count == 0)
0124 clk_gate2_do_shared_clks(hw, false);
0125
0126 spin_unlock_irqrestore(gate->lock, flags);
0127 }
0128
0129 static const struct clk_ops clk_gate2_ops = {
0130 .enable = clk_gate2_enable,
0131 .disable = clk_gate2_disable,
0132 .disable_unused = clk_gate2_disable_unused,
0133 .is_enabled = clk_gate2_is_enabled,
0134 };
0135
0136 struct clk_hw *clk_hw_register_gate2(struct device *dev, const char *name,
0137 const char *parent_name, unsigned long flags,
0138 void __iomem *reg, u8 bit_idx, u8 cgr_val, u8 cgr_mask,
0139 u8 clk_gate2_flags, spinlock_t *lock,
0140 unsigned int *share_count)
0141 {
0142 struct clk_gate2 *gate;
0143 struct clk_hw *hw;
0144 struct clk_init_data init;
0145 int ret;
0146
0147 gate = kzalloc(sizeof(struct clk_gate2), GFP_KERNEL);
0148 if (!gate)
0149 return ERR_PTR(-ENOMEM);
0150
0151
0152 gate->reg = reg;
0153 gate->bit_idx = bit_idx;
0154 gate->cgr_val = cgr_val;
0155 gate->cgr_mask = cgr_mask;
0156 gate->flags = clk_gate2_flags;
0157 gate->lock = lock;
0158 gate->share_count = share_count;
0159
0160 init.name = name;
0161 init.ops = &clk_gate2_ops;
0162 init.flags = flags;
0163 init.parent_names = parent_name ? &parent_name : NULL;
0164 init.num_parents = parent_name ? 1 : 0;
0165
0166 gate->hw.init = &init;
0167 hw = &gate->hw;
0168
0169 ret = clk_hw_register(dev, hw);
0170 if (ret) {
0171 kfree(gate);
0172 return ERR_PTR(ret);
0173 }
0174
0175 return hw;
0176 }
0177 EXPORT_SYMBOL_GPL(clk_hw_register_gate2);