Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2014 MediaTek Inc.
0004  * Author: James Liao <jamesjj.liao@mediatek.com>
0005  */
0006 
0007 #include <linux/clk-provider.h>
0008 #include <linux/mfd/syscon.h>
0009 #include <linux/module.h>
0010 #include <linux/printk.h>
0011 #include <linux/regmap.h>
0012 #include <linux/slab.h>
0013 #include <linux/types.h>
0014 
0015 #include "clk-gate.h"
0016 
0017 struct mtk_clk_gate {
0018     struct clk_hw   hw;
0019     struct regmap   *regmap;
0020     int     set_ofs;
0021     int     clr_ofs;
0022     int     sta_ofs;
0023     u8      bit;
0024 };
0025 
0026 static inline struct mtk_clk_gate *to_mtk_clk_gate(struct clk_hw *hw)
0027 {
0028     return container_of(hw, struct mtk_clk_gate, hw);
0029 }
0030 
0031 static u32 mtk_get_clockgating(struct clk_hw *hw)
0032 {
0033     struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
0034     u32 val;
0035 
0036     regmap_read(cg->regmap, cg->sta_ofs, &val);
0037 
0038     return val & BIT(cg->bit);
0039 }
0040 
0041 static int mtk_cg_bit_is_cleared(struct clk_hw *hw)
0042 {
0043     return mtk_get_clockgating(hw) == 0;
0044 }
0045 
0046 static int mtk_cg_bit_is_set(struct clk_hw *hw)
0047 {
0048     return mtk_get_clockgating(hw) != 0;
0049 }
0050 
0051 static void mtk_cg_set_bit(struct clk_hw *hw)
0052 {
0053     struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
0054 
0055     regmap_write(cg->regmap, cg->set_ofs, BIT(cg->bit));
0056 }
0057 
0058 static void mtk_cg_clr_bit(struct clk_hw *hw)
0059 {
0060     struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
0061 
0062     regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit));
0063 }
0064 
0065 static void mtk_cg_set_bit_no_setclr(struct clk_hw *hw)
0066 {
0067     struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
0068 
0069     regmap_set_bits(cg->regmap, cg->sta_ofs, BIT(cg->bit));
0070 }
0071 
0072 static void mtk_cg_clr_bit_no_setclr(struct clk_hw *hw)
0073 {
0074     struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
0075 
0076     regmap_clear_bits(cg->regmap, cg->sta_ofs, BIT(cg->bit));
0077 }
0078 
0079 static int mtk_cg_enable(struct clk_hw *hw)
0080 {
0081     mtk_cg_clr_bit(hw);
0082 
0083     return 0;
0084 }
0085 
0086 static void mtk_cg_disable(struct clk_hw *hw)
0087 {
0088     mtk_cg_set_bit(hw);
0089 }
0090 
0091 static int mtk_cg_enable_inv(struct clk_hw *hw)
0092 {
0093     mtk_cg_set_bit(hw);
0094 
0095     return 0;
0096 }
0097 
0098 static void mtk_cg_disable_inv(struct clk_hw *hw)
0099 {
0100     mtk_cg_clr_bit(hw);
0101 }
0102 
0103 static int mtk_cg_enable_no_setclr(struct clk_hw *hw)
0104 {
0105     mtk_cg_clr_bit_no_setclr(hw);
0106 
0107     return 0;
0108 }
0109 
0110 static void mtk_cg_disable_no_setclr(struct clk_hw *hw)
0111 {
0112     mtk_cg_set_bit_no_setclr(hw);
0113 }
0114 
0115 static int mtk_cg_enable_inv_no_setclr(struct clk_hw *hw)
0116 {
0117     mtk_cg_set_bit_no_setclr(hw);
0118 
0119     return 0;
0120 }
0121 
0122 static void mtk_cg_disable_inv_no_setclr(struct clk_hw *hw)
0123 {
0124     mtk_cg_clr_bit_no_setclr(hw);
0125 }
0126 
0127 const struct clk_ops mtk_clk_gate_ops_setclr = {
0128     .is_enabled = mtk_cg_bit_is_cleared,
0129     .enable     = mtk_cg_enable,
0130     .disable    = mtk_cg_disable,
0131 };
0132 EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_setclr);
0133 
0134 const struct clk_ops mtk_clk_gate_ops_setclr_inv = {
0135     .is_enabled = mtk_cg_bit_is_set,
0136     .enable     = mtk_cg_enable_inv,
0137     .disable    = mtk_cg_disable_inv,
0138 };
0139 EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_setclr_inv);
0140 
0141 const struct clk_ops mtk_clk_gate_ops_no_setclr = {
0142     .is_enabled = mtk_cg_bit_is_cleared,
0143     .enable     = mtk_cg_enable_no_setclr,
0144     .disable    = mtk_cg_disable_no_setclr,
0145 };
0146 EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_no_setclr);
0147 
0148 const struct clk_ops mtk_clk_gate_ops_no_setclr_inv = {
0149     .is_enabled = mtk_cg_bit_is_set,
0150     .enable     = mtk_cg_enable_inv_no_setclr,
0151     .disable    = mtk_cg_disable_inv_no_setclr,
0152 };
0153 EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_no_setclr_inv);
0154 
0155 static struct clk_hw *mtk_clk_register_gate(const char *name,
0156                      const char *parent_name,
0157                      struct regmap *regmap, int set_ofs,
0158                      int clr_ofs, int sta_ofs, u8 bit,
0159                      const struct clk_ops *ops,
0160                      unsigned long flags, struct device *dev)
0161 {
0162     struct mtk_clk_gate *cg;
0163     int ret;
0164     struct clk_init_data init = {};
0165 
0166     cg = kzalloc(sizeof(*cg), GFP_KERNEL);
0167     if (!cg)
0168         return ERR_PTR(-ENOMEM);
0169 
0170     init.name = name;
0171     init.flags = flags | CLK_SET_RATE_PARENT;
0172     init.parent_names = parent_name ? &parent_name : NULL;
0173     init.num_parents = parent_name ? 1 : 0;
0174     init.ops = ops;
0175 
0176     cg->regmap = regmap;
0177     cg->set_ofs = set_ofs;
0178     cg->clr_ofs = clr_ofs;
0179     cg->sta_ofs = sta_ofs;
0180     cg->bit = bit;
0181 
0182     cg->hw.init = &init;
0183 
0184     ret = clk_hw_register(dev, &cg->hw);
0185     if (ret) {
0186         kfree(cg);
0187         return ERR_PTR(ret);
0188     }
0189 
0190     return &cg->hw;
0191 }
0192 
0193 static void mtk_clk_unregister_gate(struct clk_hw *hw)
0194 {
0195     struct mtk_clk_gate *cg;
0196     if (!hw)
0197         return;
0198 
0199     cg = to_mtk_clk_gate(hw);
0200 
0201     clk_hw_unregister(hw);
0202     kfree(cg);
0203 }
0204 
0205 int mtk_clk_register_gates_with_dev(struct device_node *node,
0206                     const struct mtk_gate *clks, int num,
0207                     struct clk_hw_onecell_data *clk_data,
0208                     struct device *dev)
0209 {
0210     int i;
0211     struct clk_hw *hw;
0212     struct regmap *regmap;
0213 
0214     if (!clk_data)
0215         return -ENOMEM;
0216 
0217     regmap = device_node_to_regmap(node);
0218     if (IS_ERR(regmap)) {
0219         pr_err("Cannot find regmap for %pOF: %pe\n", node, regmap);
0220         return PTR_ERR(regmap);
0221     }
0222 
0223     for (i = 0; i < num; i++) {
0224         const struct mtk_gate *gate = &clks[i];
0225 
0226         if (!IS_ERR_OR_NULL(clk_data->hws[gate->id])) {
0227             pr_warn("%pOF: Trying to register duplicate clock ID: %d\n",
0228                 node, gate->id);
0229             continue;
0230         }
0231 
0232         hw = mtk_clk_register_gate(gate->name, gate->parent_name,
0233                         regmap,
0234                         gate->regs->set_ofs,
0235                         gate->regs->clr_ofs,
0236                         gate->regs->sta_ofs,
0237                         gate->shift, gate->ops,
0238                         gate->flags, dev);
0239 
0240         if (IS_ERR(hw)) {
0241             pr_err("Failed to register clk %s: %pe\n", gate->name,
0242                    hw);
0243             goto err;
0244         }
0245 
0246         clk_data->hws[gate->id] = hw;
0247     }
0248 
0249     return 0;
0250 
0251 err:
0252     while (--i >= 0) {
0253         const struct mtk_gate *gate = &clks[i];
0254 
0255         if (IS_ERR_OR_NULL(clk_data->hws[gate->id]))
0256             continue;
0257 
0258         mtk_clk_unregister_gate(clk_data->hws[gate->id]);
0259         clk_data->hws[gate->id] = ERR_PTR(-ENOENT);
0260     }
0261 
0262     return PTR_ERR(hw);
0263 }
0264 
0265 int mtk_clk_register_gates(struct device_node *node,
0266                const struct mtk_gate *clks, int num,
0267                struct clk_hw_onecell_data *clk_data)
0268 {
0269     return mtk_clk_register_gates_with_dev(node, clks, num, clk_data, NULL);
0270 }
0271 EXPORT_SYMBOL_GPL(mtk_clk_register_gates);
0272 
0273 void mtk_clk_unregister_gates(const struct mtk_gate *clks, int num,
0274                   struct clk_hw_onecell_data *clk_data)
0275 {
0276     int i;
0277 
0278     if (!clk_data)
0279         return;
0280 
0281     for (i = num; i > 0; i--) {
0282         const struct mtk_gate *gate = &clks[i - 1];
0283 
0284         if (IS_ERR_OR_NULL(clk_data->hws[gate->id]))
0285             continue;
0286 
0287         mtk_clk_unregister_gate(clk_data->hws[gate->id]);
0288         clk_data->hws[gate->id] = ERR_PTR(-ENOENT);
0289     }
0290 }
0291 EXPORT_SYMBOL_GPL(mtk_clk_unregister_gates);
0292 
0293 MODULE_LICENSE("GPL");