1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2020, Linaro Limited
5 #include <linux/init.h>
6 #include <linux/clk-provider.h>
7 #include <linux/module.h>
8 #include <linux/device.h>
9 #include <linux/platform_device.h>
11 #include <linux/slab.h>
12 #include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
13 #include "q6dsp-lpass-clocks.h"
15 #define Q6DSP_MAX_CLK_ID 104
16 #define Q6DSP_LPASS_CLK_ROOT_DEFAULT 0
28 #define to_q6dsp_clk(_hw) container_of(_hw, struct q6dsp_clk, hw)
32 struct q6dsp_clk
*clks
[Q6DSP_MAX_CLK_ID
];
33 const struct q6dsp_clk_desc
*desc
;
36 static int clk_q6dsp_prepare(struct clk_hw
*hw
)
38 struct q6dsp_clk
*clk
= to_q6dsp_clk(hw
);
39 struct q6dsp_cc
*cc
= dev_get_drvdata(clk
->dev
);
41 return cc
->desc
->lpass_set_clk(clk
->dev
, clk
->q6dsp_clk_id
, clk
->attributes
,
42 Q6DSP_LPASS_CLK_ROOT_DEFAULT
, clk
->rate
);
45 static void clk_q6dsp_unprepare(struct clk_hw
*hw
)
47 struct q6dsp_clk
*clk
= to_q6dsp_clk(hw
);
48 struct q6dsp_cc
*cc
= dev_get_drvdata(clk
->dev
);
50 cc
->desc
->lpass_set_clk(clk
->dev
, clk
->q6dsp_clk_id
, clk
->attributes
,
51 Q6DSP_LPASS_CLK_ROOT_DEFAULT
, 0);
54 static int clk_q6dsp_set_rate(struct clk_hw
*hw
, unsigned long rate
,
55 unsigned long parent_rate
)
57 struct q6dsp_clk
*clk
= to_q6dsp_clk(hw
);
64 static unsigned long clk_q6dsp_recalc_rate(struct clk_hw
*hw
,
65 unsigned long parent_rate
)
67 struct q6dsp_clk
*clk
= to_q6dsp_clk(hw
);
72 static long clk_q6dsp_round_rate(struct clk_hw
*hw
, unsigned long rate
,
73 unsigned long *parent_rate
)
78 static const struct clk_ops clk_q6dsp_ops
= {
79 .prepare
= clk_q6dsp_prepare
,
80 .unprepare
= clk_q6dsp_unprepare
,
81 .set_rate
= clk_q6dsp_set_rate
,
82 .round_rate
= clk_q6dsp_round_rate
,
83 .recalc_rate
= clk_q6dsp_recalc_rate
,
86 static int clk_vote_q6dsp_block(struct clk_hw
*hw
)
88 struct q6dsp_clk
*clk
= to_q6dsp_clk(hw
);
89 struct q6dsp_cc
*cc
= dev_get_drvdata(clk
->dev
);
91 return cc
->desc
->lpass_vote_clk(clk
->dev
, clk
->q6dsp_clk_id
,
92 clk_hw_get_name(&clk
->hw
), &clk
->handle
);
95 static void clk_unvote_q6dsp_block(struct clk_hw
*hw
)
97 struct q6dsp_clk
*clk
= to_q6dsp_clk(hw
);
98 struct q6dsp_cc
*cc
= dev_get_drvdata(clk
->dev
);
100 cc
->desc
->lpass_unvote_clk(clk
->dev
, clk
->q6dsp_clk_id
, clk
->handle
);
103 static const struct clk_ops clk_vote_q6dsp_ops
= {
104 .prepare
= clk_vote_q6dsp_block
,
105 .unprepare
= clk_unvote_q6dsp_block
,
109 static struct clk_hw
*q6dsp_of_clk_hw_get(struct of_phandle_args
*clkspec
,
112 struct q6dsp_cc
*cc
= data
;
113 unsigned int idx
= clkspec
->args
[0];
114 unsigned int attr
= clkspec
->args
[1];
116 if (idx
>= Q6DSP_MAX_CLK_ID
|| attr
> LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR
) {
117 dev_err(cc
->dev
, "Invalid clk specifier (%d, %d)\n", idx
, attr
);
118 return ERR_PTR(-EINVAL
);
122 cc
->clks
[idx
]->attributes
= attr
;
123 return &cc
->clks
[idx
]->hw
;
126 return ERR_PTR(-ENOENT
);
129 int q6dsp_clock_dev_probe(struct platform_device
*pdev
)
132 struct device
*dev
= &pdev
->dev
;
133 const struct q6dsp_clk_init
*q6dsp_clks
;
134 const struct q6dsp_clk_desc
*desc
;
137 cc
= devm_kzalloc(dev
, sizeof(*cc
), GFP_KERNEL
);
141 desc
= of_device_get_match_data(&pdev
->dev
);
147 q6dsp_clks
= desc
->clks
;
149 for (i
= 0; i
< desc
->num_clks
; i
++) {
150 unsigned int id
= q6dsp_clks
[i
].clk_id
;
151 struct clk_init_data init
= {
152 .name
= q6dsp_clks
[i
].name
,
154 struct q6dsp_clk
*clk
;
156 clk
= devm_kzalloc(dev
, sizeof(*clk
), GFP_KERNEL
);
161 clk
->q6dsp_clk_id
= q6dsp_clks
[i
].q6dsp_clk_id
;
162 clk
->rate
= q6dsp_clks
[i
].rate
;
163 clk
->hw
.init
= &init
;
166 init
.ops
= &clk_q6dsp_ops
;
168 init
.ops
= &clk_vote_q6dsp_ops
;
172 ret
= devm_clk_hw_register(dev
, &clk
->hw
);
177 ret
= devm_of_clk_add_hw_provider(dev
, q6dsp_of_clk_hw_get
, cc
);
181 dev_set_drvdata(dev
, cc
);
185 EXPORT_SYMBOL_GPL(q6dsp_clock_dev_probe
);