1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
4 */
5
6#include <linux/bitops.h>
7#include <linux/err.h>
8#include <linux/module.h>
9#include <linux/platform_device.h>
10#include <linux/pm_clock.h>
11#include <linux/pm_runtime.h>
12#include <linux/regmap.h>
13
14#include <dt-bindings/clock/qcom,q6sstopcc-qcs404.h>
15
16#include "clk-regmap.h"
17#include "clk-branch.h"
18#include "common.h"
19#include "reset.h"
20
21static struct clk_branch lcc_ahbfabric_cbc_clk = {
22	.halt_reg = 0x1b004,
23	.halt_check = BRANCH_HALT,
24	.clkr = {
25		.enable_reg = 0x1b004,
26		.enable_mask = BIT(0),
27		.hw.init = &(struct clk_init_data){
28			.name = "lcc_ahbfabric_cbc_clk",
29			.ops = &clk_branch2_ops,
30		},
31	},
32};
33
34static struct clk_branch lcc_q6ss_ahbs_cbc_clk = {
35	.halt_reg = 0x22000,
36	.halt_check = BRANCH_VOTED,
37	.clkr = {
38		.enable_reg = 0x22000,
39		.enable_mask = BIT(0),
40		.hw.init = &(struct clk_init_data){
41			.name = "lcc_q6ss_ahbs_cbc_clk",
42			.ops = &clk_branch2_ops,
43		},
44	},
45};
46
47static struct clk_branch lcc_q6ss_tcm_slave_cbc_clk = {
48	.halt_reg = 0x1c000,
49	.halt_check = BRANCH_VOTED,
50	.clkr = {
51		.enable_reg = 0x1c000,
52		.enable_mask = BIT(0),
53		.hw.init = &(struct clk_init_data){
54			.name = "lcc_q6ss_tcm_slave_cbc_clk",
55			.ops = &clk_branch2_ops,
56		},
57	},
58};
59
60static struct clk_branch lcc_q6ss_ahbm_cbc_clk = {
61	.halt_reg = 0x22004,
62	.halt_check = BRANCH_VOTED,
63	.clkr = {
64		.enable_reg = 0x22004,
65		.enable_mask = BIT(0),
66		.hw.init = &(struct clk_init_data){
67			.name = "lcc_q6ss_ahbm_cbc_clk",
68			.ops = &clk_branch2_ops,
69		},
70	},
71};
72
73static struct clk_branch lcc_q6ss_axim_cbc_clk = {
74	.halt_reg = 0x1c004,
75	.halt_check = BRANCH_VOTED,
76	.clkr = {
77		.enable_reg = 0x1c004,
78		.enable_mask = BIT(0),
79		.hw.init = &(struct clk_init_data){
80			.name = "lcc_q6ss_axim_cbc_clk",
81			.ops = &clk_branch2_ops,
82		},
83	},
84};
85
86static struct clk_branch lcc_q6ss_bcr_sleep_clk = {
87	.halt_reg = 0x6004,
88	.halt_check = BRANCH_VOTED,
89	.clkr = {
90		.enable_reg = 0x6004,
91		.enable_mask = BIT(0),
92		.hw.init = &(struct clk_init_data){
93			.name = "lcc_q6ss_bcr_sleep_clk",
94			.ops = &clk_branch2_ops,
95		},
96	},
97};
98
99/* TCSR clock */
100static struct clk_branch tcsr_lcc_csr_cbcr_clk = {
101	.halt_reg = 0x8008,
102	.halt_check = BRANCH_VOTED,
103	.clkr = {
104		.enable_reg = 0x8008,
105		.enable_mask = BIT(0),
106		.hw.init = &(struct clk_init_data){
107			.name = "tcsr_lcc_csr_cbcr_clk",
108			.ops = &clk_branch2_ops,
109		},
110	},
111};
112
113static struct regmap_config q6sstop_regmap_config = {
114	.reg_bits	= 32,
115	.reg_stride	= 4,
116	.val_bits	= 32,
117	.fast_io	= true,
118};
119
120static struct clk_regmap *q6sstop_qcs404_clocks[] = {
121	[LCC_AHBFABRIC_CBC_CLK] = &lcc_ahbfabric_cbc_clk.clkr,
122	[LCC_Q6SS_AHBS_CBC_CLK] = &lcc_q6ss_ahbs_cbc_clk.clkr,
123	[LCC_Q6SS_TCM_SLAVE_CBC_CLK] = &lcc_q6ss_tcm_slave_cbc_clk.clkr,
124	[LCC_Q6SS_AHBM_CBC_CLK] = &lcc_q6ss_ahbm_cbc_clk.clkr,
125	[LCC_Q6SS_AXIM_CBC_CLK] = &lcc_q6ss_axim_cbc_clk.clkr,
126	[LCC_Q6SS_BCR_SLEEP_CLK] = &lcc_q6ss_bcr_sleep_clk.clkr,
127};
128
129static const struct qcom_reset_map q6sstop_qcs404_resets[] = {
130	[Q6SSTOP_BCR_RESET] = { 0x6000 },
131};
132
133static const struct qcom_cc_desc q6sstop_qcs404_desc = {
134	.config = &q6sstop_regmap_config,
135	.clks = q6sstop_qcs404_clocks,
136	.num_clks = ARRAY_SIZE(q6sstop_qcs404_clocks),
137	.resets = q6sstop_qcs404_resets,
138	.num_resets = ARRAY_SIZE(q6sstop_qcs404_resets),
139};
140
141static struct clk_regmap *tcsr_qcs404_clocks[] = {
142	[TCSR_Q6SS_LCC_CBCR_CLK] = &tcsr_lcc_csr_cbcr_clk.clkr,
143};
144
145static const struct qcom_cc_desc tcsr_qcs404_desc = {
146	.config = &q6sstop_regmap_config,
147	.clks = tcsr_qcs404_clocks,
148	.num_clks = ARRAY_SIZE(tcsr_qcs404_clocks),
149};
150
151static const struct of_device_id q6sstopcc_qcs404_match_table[] = {
152	{ .compatible = "qcom,qcs404-q6sstopcc" },
153	{ }
154};
155MODULE_DEVICE_TABLE(of, q6sstopcc_qcs404_match_table);
156
157static int q6sstopcc_qcs404_probe(struct platform_device *pdev)
158{
159	const struct qcom_cc_desc *desc;
160	int ret;
161
162	pm_runtime_enable(&pdev->dev);
163	ret = pm_clk_create(&pdev->dev);
164	if (ret)
165		goto disable_pm_runtime;
166
167	ret = pm_clk_add(&pdev->dev, NULL);
168	if (ret < 0) {
169		dev_err(&pdev->dev, "failed to acquire iface clock\n");
170		goto destroy_pm_clk;
171	}
172
173	q6sstop_regmap_config.name = "q6sstop_tcsr";
174	desc = &tcsr_qcs404_desc;
175
176	ret = qcom_cc_probe_by_index(pdev, 1, desc);
177	if (ret)
178		goto destroy_pm_clk;
179
180	q6sstop_regmap_config.name = "q6sstop_cc";
181	desc = &q6sstop_qcs404_desc;
182
183	ret = qcom_cc_probe_by_index(pdev, 0, desc);
184	if (ret)
185		goto destroy_pm_clk;
186
187	return 0;
188
189destroy_pm_clk:
190	pm_clk_destroy(&pdev->dev);
191
192disable_pm_runtime:
193	pm_runtime_disable(&pdev->dev);
194
195	return ret;
196}
197
198static int q6sstopcc_qcs404_remove(struct platform_device *pdev)
199{
200	pm_clk_destroy(&pdev->dev);
201	pm_runtime_disable(&pdev->dev);
202
203	return 0;
204}
205
206static const struct dev_pm_ops q6sstopcc_pm_ops = {
207	SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL)
208};
209
210static struct platform_driver q6sstopcc_qcs404_driver = {
211	.probe		= q6sstopcc_qcs404_probe,
212	.remove		= q6sstopcc_qcs404_remove,
213	.driver		= {
214		.name	= "qcs404-q6sstopcc",
215		.of_match_table = q6sstopcc_qcs404_match_table,
216		.pm = &q6sstopcc_pm_ops,
217	},
218};
219
220module_platform_driver(q6sstopcc_qcs404_driver);
221
222MODULE_DESCRIPTION("QTI QCS404 Q6SSTOP Clock Controller Driver");
223MODULE_LICENSE("GPL v2");
224