Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 // Copyright (c) 2020, Linaro Limited
0003 
0004 #include <linux/err.h>
0005 #include <linux/init.h>
0006 #include <linux/clk-provider.h>
0007 #include <linux/module.h>
0008 #include <linux/device.h>
0009 #include <linux/platform_device.h>
0010 #include <linux/of.h>
0011 #include <linux/of_device.h>
0012 #include <linux/slab.h>
0013 #include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
0014 #include "q6dsp-lpass-clocks.h"
0015 
0016 #define Q6DSP_MAX_CLK_ID            104
0017 #define Q6DSP_LPASS_CLK_ROOT_DEFAULT        0
0018 
0019 
0020 struct q6dsp_clk {
0021     struct device *dev;
0022     int q6dsp_clk_id;
0023     int attributes;
0024     int rate;
0025     uint32_t handle;
0026     struct clk_hw hw;
0027 };
0028 
0029 #define to_q6dsp_clk(_hw) container_of(_hw, struct q6dsp_clk, hw)
0030 
0031 struct q6dsp_cc {
0032     struct device *dev;
0033     struct q6dsp_clk *clks[Q6DSP_MAX_CLK_ID];
0034     const struct q6dsp_clk_desc *desc;
0035 };
0036 
0037 static int clk_q6dsp_prepare(struct clk_hw *hw)
0038 {
0039     struct q6dsp_clk *clk = to_q6dsp_clk(hw);
0040     struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
0041 
0042     return cc->desc->lpass_set_clk(clk->dev, clk->q6dsp_clk_id, clk->attributes,
0043                      Q6DSP_LPASS_CLK_ROOT_DEFAULT, clk->rate);
0044 }
0045 
0046 static void clk_q6dsp_unprepare(struct clk_hw *hw)
0047 {
0048     struct q6dsp_clk *clk = to_q6dsp_clk(hw);
0049     struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
0050 
0051     cc->desc->lpass_set_clk(clk->dev, clk->q6dsp_clk_id, clk->attributes,
0052                   Q6DSP_LPASS_CLK_ROOT_DEFAULT, 0);
0053 }
0054 
0055 static int clk_q6dsp_set_rate(struct clk_hw *hw, unsigned long rate,
0056                   unsigned long parent_rate)
0057 {
0058     struct q6dsp_clk *clk = to_q6dsp_clk(hw);
0059 
0060     clk->rate = rate;
0061 
0062     return 0;
0063 }
0064 
0065 static unsigned long clk_q6dsp_recalc_rate(struct clk_hw *hw,
0066                        unsigned long parent_rate)
0067 {
0068     struct q6dsp_clk *clk = to_q6dsp_clk(hw);
0069 
0070     return clk->rate;
0071 }
0072 
0073 static long clk_q6dsp_round_rate(struct clk_hw *hw, unsigned long rate,
0074                  unsigned long *parent_rate)
0075 {
0076     return rate;
0077 }
0078 
0079 static const struct clk_ops clk_q6dsp_ops = {
0080     .prepare    = clk_q6dsp_prepare,
0081     .unprepare  = clk_q6dsp_unprepare,
0082     .set_rate   = clk_q6dsp_set_rate,
0083     .round_rate = clk_q6dsp_round_rate,
0084     .recalc_rate    = clk_q6dsp_recalc_rate,
0085 };
0086 
0087 static int clk_vote_q6dsp_block(struct clk_hw *hw)
0088 {
0089     struct q6dsp_clk *clk = to_q6dsp_clk(hw);
0090     struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
0091 
0092     return cc->desc->lpass_vote_clk(clk->dev, clk->q6dsp_clk_id,
0093                   clk_hw_get_name(&clk->hw), &clk->handle);
0094 }
0095 
0096 static void clk_unvote_q6dsp_block(struct clk_hw *hw)
0097 {
0098     struct q6dsp_clk *clk = to_q6dsp_clk(hw);
0099     struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
0100 
0101     cc->desc->lpass_unvote_clk(clk->dev, clk->q6dsp_clk_id, clk->handle);
0102 }
0103 
0104 static const struct clk_ops clk_vote_q6dsp_ops = {
0105     .prepare    = clk_vote_q6dsp_block,
0106     .unprepare  = clk_unvote_q6dsp_block,
0107 };
0108 
0109 
0110 static struct clk_hw *q6dsp_of_clk_hw_get(struct of_phandle_args *clkspec,
0111                       void *data)
0112 {
0113     struct q6dsp_cc *cc = data;
0114     unsigned int idx = clkspec->args[0];
0115     unsigned int attr = clkspec->args[1];
0116 
0117     if (idx >= Q6DSP_MAX_CLK_ID || attr > LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR) {
0118         dev_err(cc->dev, "Invalid clk specifier (%d, %d)\n", idx, attr);
0119         return ERR_PTR(-EINVAL);
0120     }
0121 
0122     if (cc->clks[idx]) {
0123         cc->clks[idx]->attributes = attr;
0124         return &cc->clks[idx]->hw;
0125     }
0126 
0127     return ERR_PTR(-ENOENT);
0128 }
0129 
0130 int q6dsp_clock_dev_probe(struct platform_device *pdev)
0131 {
0132     struct q6dsp_cc *cc;
0133     struct device *dev = &pdev->dev;
0134     const struct q6dsp_clk_init *q6dsp_clks;
0135     const struct q6dsp_clk_desc *desc;
0136     int i, ret;
0137 
0138     cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
0139     if (!cc)
0140         return -ENOMEM;
0141 
0142     desc = of_device_get_match_data(&pdev->dev);
0143     if (!desc)
0144         return -EINVAL;
0145 
0146     cc->desc = desc;
0147     cc->dev = dev;
0148     q6dsp_clks = desc->clks;
0149 
0150     for (i = 0; i < desc->num_clks; i++) {
0151         unsigned int id = q6dsp_clks[i].clk_id;
0152         struct clk_init_data init = {
0153             .name =  q6dsp_clks[i].name,
0154         };
0155         struct q6dsp_clk *clk;
0156 
0157         clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL);
0158         if (!clk)
0159             return -ENOMEM;
0160 
0161         clk->dev = dev;
0162         clk->q6dsp_clk_id = q6dsp_clks[i].q6dsp_clk_id;
0163         clk->rate = q6dsp_clks[i].rate;
0164         clk->hw.init = &init;
0165 
0166         if (clk->rate)
0167             init.ops = &clk_q6dsp_ops;
0168         else
0169             init.ops = &clk_vote_q6dsp_ops;
0170 
0171         cc->clks[id] = clk;
0172 
0173         ret = devm_clk_hw_register(dev, &clk->hw);
0174         if (ret)
0175             return ret;
0176     }
0177 
0178     ret = devm_of_clk_add_hw_provider(dev, q6dsp_of_clk_hw_get, cc);
0179     if (ret)
0180         return ret;
0181 
0182     dev_set_drvdata(dev, cc);
0183 
0184     return 0;
0185 }
0186 EXPORT_SYMBOL_GPL(q6dsp_clock_dev_probe);