1 // SPDX-License-Identifier: GPL-2.0
3 * LPASS Audio CC and Always ON CC Glitch Free Mux clock driver
5 * Copyright (c) 2020 Linaro Ltd.
6 * Author: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
9 #include <linux/kernel.h>
10 #include <linux/module.h>
11 #include <linux/clk-provider.h>
13 #include <linux/slab.h>
14 #include <linux/err.h>
15 #include <linux/pm_clock.h>
16 #include <linux/pm_runtime.h>
17 #include <linux/device.h>
18 #include <linux/platform_device.h>
19 #include <linux/of_device.h>
20 #include <dt-bindings/clock/qcom,sm8250-lpass-audiocc.h>
21 #include <dt-bindings/clock/qcom,sm8250-lpass-aoncc.h>
30 unsigned int mux_mask
;
32 struct lpass_gfm
*priv
;
33 void __iomem
*gfm_mux
;
36 #define GFM_MASK BIT(1)
37 #define to_clk_gfm(_hw) container_of(_hw, struct clk_gfm, hw)
39 static u8
clk_gfm_get_parent(struct clk_hw
*hw
)
41 struct clk_gfm
*clk
= to_clk_gfm(hw
);
43 return readl(clk
->gfm_mux
) & GFM_MASK
;
46 static int clk_gfm_set_parent(struct clk_hw
*hw
, u8 index
)
48 struct clk_gfm
*clk
= to_clk_gfm(hw
);
51 val
= readl(clk
->gfm_mux
);
58 writel(val
, clk
->gfm_mux
);
63 static const struct clk_ops clk_gfm_ops
= {
64 .get_parent
= clk_gfm_get_parent
,
65 .set_parent
= clk_gfm_set_parent
,
66 .determine_rate
= __clk_mux_determine_rate
,
69 static struct clk_gfm lpass_gfm_va_mclk
= {
72 .hw
.init
= &(struct clk_init_data
) {
75 .flags
= CLK_SET_RATE_PARENT
| CLK_OPS_PARENT_ENABLE
,
77 .parent_data
= (const struct clk_parent_data
[]){
80 .fw_name
= "LPASS_CLK_ID_TX_CORE_MCLK",
83 .fw_name
= "LPASS_CLK_ID_VA_CORE_MCLK",
89 static struct clk_gfm lpass_gfm_tx_npl
= {
92 .hw
.init
= &(struct clk_init_data
) {
95 .flags
= CLK_SET_RATE_PARENT
| CLK_OPS_PARENT_ENABLE
,
96 .parent_data
= (const struct clk_parent_data
[]){
99 .fw_name
= "LPASS_CLK_ID_TX_CORE_NPL_MCLK",
102 .fw_name
= "LPASS_CLK_ID_VA_CORE_2X_MCLK",
109 static struct clk_gfm lpass_gfm_wsa_mclk
= {
112 .hw
.init
= &(struct clk_init_data
) {
115 .flags
= CLK_SET_RATE_PARENT
| CLK_OPS_PARENT_ENABLE
,
116 .parent_data
= (const struct clk_parent_data
[]){
119 .fw_name
= "LPASS_CLK_ID_TX_CORE_MCLK",
122 .fw_name
= "LPASS_CLK_ID_WSA_CORE_MCLK",
129 static struct clk_gfm lpass_gfm_wsa_npl
= {
132 .hw
.init
= &(struct clk_init_data
) {
135 .flags
= CLK_SET_RATE_PARENT
| CLK_OPS_PARENT_ENABLE
,
136 .parent_data
= (const struct clk_parent_data
[]){
139 .fw_name
= "LPASS_CLK_ID_TX_CORE_NPL_MCLK",
142 .fw_name
= "LPASS_CLK_ID_WSA_CORE_NPL_MCLK",
149 static struct clk_gfm lpass_gfm_rx_mclk_mclk2
= {
152 .hw
.init
= &(struct clk_init_data
) {
153 .name
= "RX_MCLK_MCLK2",
155 .flags
= CLK_SET_RATE_PARENT
| CLK_OPS_PARENT_ENABLE
,
156 .parent_data
= (const struct clk_parent_data
[]){
159 .fw_name
= "LPASS_CLK_ID_TX_CORE_MCLK",
162 .fw_name
= "LPASS_CLK_ID_RX_CORE_MCLK",
169 static struct clk_gfm lpass_gfm_rx_npl
= {
172 .hw
.init
= &(struct clk_init_data
) {
175 .flags
= CLK_SET_RATE_PARENT
| CLK_OPS_PARENT_ENABLE
,
176 .parent_data
= (const struct clk_parent_data
[]){
179 .fw_name
= "LPASS_CLK_ID_TX_CORE_NPL_MCLK",
182 .fw_name
= "LPASS_CLK_ID_RX_CORE_NPL_MCLK",
189 static struct clk_gfm
*aoncc_gfm_clks
[] = {
190 [LPASS_CDC_VA_MCLK
] = &lpass_gfm_va_mclk
,
191 [LPASS_CDC_TX_NPL
] = &lpass_gfm_tx_npl
,
194 static struct clk_hw_onecell_data aoncc_hw_onecell_data
= {
196 [LPASS_CDC_VA_MCLK
] = &lpass_gfm_va_mclk
.hw
,
197 [LPASS_CDC_TX_NPL
] = &lpass_gfm_tx_npl
.hw
,
199 .num
= ARRAY_SIZE(aoncc_gfm_clks
),
202 static struct clk_gfm
*audiocc_gfm_clks
[] = {
203 [LPASS_CDC_WSA_NPL
] = &lpass_gfm_wsa_npl
,
204 [LPASS_CDC_WSA_MCLK
] = &lpass_gfm_wsa_mclk
,
205 [LPASS_CDC_RX_NPL
] = &lpass_gfm_rx_npl
,
206 [LPASS_CDC_RX_MCLK_MCLK2
] = &lpass_gfm_rx_mclk_mclk2
,
209 static struct clk_hw_onecell_data audiocc_hw_onecell_data
= {
211 [LPASS_CDC_WSA_NPL
] = &lpass_gfm_wsa_npl
.hw
,
212 [LPASS_CDC_WSA_MCLK
] = &lpass_gfm_wsa_mclk
.hw
,
213 [LPASS_CDC_RX_NPL
] = &lpass_gfm_rx_npl
.hw
,
214 [LPASS_CDC_RX_MCLK_MCLK2
] = &lpass_gfm_rx_mclk_mclk2
.hw
,
216 .num
= ARRAY_SIZE(audiocc_gfm_clks
),
219 struct lpass_gfm_data
{
220 struct clk_hw_onecell_data
*onecell_data
;
221 struct clk_gfm
**gfm_clks
;
224 static struct lpass_gfm_data audiocc_data
= {
225 .onecell_data
= &audiocc_hw_onecell_data
,
226 .gfm_clks
= audiocc_gfm_clks
,
229 static struct lpass_gfm_data aoncc_data
= {
230 .onecell_data
= &aoncc_hw_onecell_data
,
231 .gfm_clks
= aoncc_gfm_clks
,
234 static int lpass_gfm_clk_driver_probe(struct platform_device
*pdev
)
236 const struct lpass_gfm_data
*data
;
237 struct device
*dev
= &pdev
->dev
;
239 struct lpass_gfm
*cc
;
242 data
= of_device_get_match_data(dev
);
246 cc
= devm_kzalloc(dev
, sizeof(*cc
), GFP_KERNEL
);
250 cc
->base
= devm_platform_ioremap_resource(pdev
, 0);
251 if (IS_ERR(cc
->base
))
252 return PTR_ERR(cc
->base
);
254 pm_runtime_enable(dev
);
255 err
= pm_clk_create(dev
);
259 err
= of_pm_clk_add_clks(dev
);
261 dev_dbg(dev
, "Failed to get lpass core voting clocks\n");
265 for (i
= 0; i
< data
->onecell_data
->num
; i
++) {
266 if (!data
->gfm_clks
[i
])
269 gfm
= data
->gfm_clks
[i
];
271 gfm
->gfm_mux
= cc
->base
;
272 gfm
->gfm_mux
= gfm
->gfm_mux
+ data
->gfm_clks
[i
]->mux_reg
;
274 err
= devm_clk_hw_register(dev
, &data
->gfm_clks
[i
]->hw
);
280 err
= devm_of_clk_add_hw_provider(dev
, of_clk_hw_onecell_get
,
290 pm_runtime_disable(dev
);
294 static const struct of_device_id lpass_gfm_clk_match_table
[] = {
296 .compatible
= "qcom,sm8250-lpass-aoncc",
300 .compatible
= "qcom,sm8250-lpass-audiocc",
301 .data
= &audiocc_data
,
305 MODULE_DEVICE_TABLE(of
, lpass_gfm_clk_match_table
);
307 static const struct dev_pm_ops lpass_gfm_pm_ops
= {
308 SET_RUNTIME_PM_OPS(pm_clk_suspend
, pm_clk_resume
, NULL
)
311 static struct platform_driver lpass_gfm_clk_driver
= {
312 .probe
= lpass_gfm_clk_driver_probe
,
314 .name
= "lpass-gfm-clk",
315 .of_match_table
= lpass_gfm_clk_match_table
,
316 .pm
= &lpass_gfm_pm_ops
,
319 module_platform_driver(lpass_gfm_clk_driver
);
320 MODULE_LICENSE("GPL v2");