0001
0002
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);