1276333Sian// SPDX-License-Identifier: GPL-2.0-only
2276333Sian// Copyright (c) 2021, Michael Srba
3276333Sian
4276333Sian#include <linux/clk.h>
5276333Sian#include <linux/delay.h>
6276333Sian#include <linux/io.h>
7276333Sian#include <linux/mfd/syscon.h>
8276333Sian#include <linux/module.h>
9276333Sian#include <linux/of_platform.h>
10276333Sian#include <linux/platform_device.h>
11276333Sian#include <linux/pm_clock.h>
12276333Sian#include <linux/pm_domain.h>
13276333Sian#include <linux/pm_runtime.h>
14276333Sian#include <linux/regmap.h>
15276333Sian#include <linux/reset.h>
16276333Sian
17276333Sian/* AXI Halt Register Offsets */
18276333Sian#define AXI_HALTREQ_REG			0x0
19276333Sian#define AXI_HALTACK_REG			0x4
20276333Sian#define AXI_IDLE_REG			0x8
21276333Sian
22276333Sian#define SSCAON_CONFIG0_CLAMP_EN_OVRD		BIT(4)
23276333Sian#define SSCAON_CONFIG0_CLAMP_EN_OVRD_VAL	BIT(5)
24276333Sian
25276333Sianstatic const char *const qcom_ssc_block_pd_names[] = {
26276333Sian	"ssc_cx",
27276333Sian	"ssc_mx"
28276333Sian};
29276333Sian
30276333Sianstruct qcom_ssc_block_bus_data {
31276333Sian	const char *const *pd_names;
32282767Sandrew	struct device *pds[ARRAY_SIZE(qcom_ssc_block_pd_names)];
33282767Sandrew	char __iomem *reg_mpm_sscaon_config0;
34282767Sandrew	char __iomem *reg_mpm_sscaon_config1;
35282767Sandrew	struct regmap *halt_map;
36282767Sandrew	struct clk *xo_clk;
37289892Sian	struct clk *aggre2_clk;
38276333Sian	struct clk *gcc_im_sleep_clk;
39276333Sian	struct clk *aggre2_north_clk;
40276333Sian	struct clk *ssc_xo_clk;
41276333Sian	struct clk *ssc_ahbs_clk;
42276333Sian	struct reset_control *ssc_bcr;
43276333Sian	struct reset_control *ssc_reset;
44276333Sian	u32 ssc_axi_halt;
45279811Sian	int num_pds;
46279811Sian};
47279811Sian
48276333Sianstatic void reg32_set_bits(char __iomem *reg, u32 value)
49276333Sian{
50276333Sian	u32 tmp = ioread32(reg);
51276333Sian
52276333Sian	iowrite32(tmp | value, reg);
53276333Sian}
54276333Sian
55276333Sianstatic void reg32_clear_bits(char __iomem *reg, u32 value)
56276333Sian{
57276333Sian	u32 tmp = ioread32(reg);
58276333Sian
59276333Sian	iowrite32(tmp & (~value), reg);
60276333Sian}
61276333Sian
62280985Sandrewstatic int qcom_ssc_block_bus_init(struct device *dev)
63280985Sandrew{
64280985Sandrew	int ret;
65280985Sandrew
66280985Sandrew	struct qcom_ssc_block_bus_data *data = dev_get_drvdata(dev);
67280985Sandrew
68280985Sandrew	ret = clk_prepare_enable(data->xo_clk);
69280985Sandrew	if (ret) {
70280985Sandrew		dev_err(dev, "error enabling xo_clk: %d\n", ret);
71276333Sian		goto err_xo_clk;
72276333Sian	}
73276333Sian
74276333Sian	ret = clk_prepare_enable(data->aggre2_clk);
75276333Sian	if (ret) {
76276333Sian		dev_err(dev, "error enabling aggre2_clk: %d\n", ret);
77276333Sian		goto err_aggre2_clk;
78276333Sian	}
79276333Sian
80276333Sian	ret = clk_prepare_enable(data->gcc_im_sleep_clk);
81276333Sian	if (ret) {
82276333Sian		dev_err(dev, "error enabling gcc_im_sleep_clk: %d\n", ret);
83276333Sian		goto err_gcc_im_sleep_clk;
84276333Sian	}
85280985Sandrew
86280985Sandrew	/*
87280985Sandrew	 * We need to intervene here because the HW logic driving these signals cannot handle
88280985Sandrew	 * initialization after power collapse by itself.
89280985Sandrew	 */
90280985Sandrew	reg32_clear_bits(data->reg_mpm_sscaon_config0,
91280985Sandrew			 SSCAON_CONFIG0_CLAMP_EN_OVRD | SSCAON_CONFIG0_CLAMP_EN_OVRD_VAL);
92276333Sian	/* override few_ack/rest_ack */
93276333Sian	reg32_clear_bits(data->reg_mpm_sscaon_config1, BIT(31));
94276333Sian
95276333Sian	ret = clk_prepare_enable(data->aggre2_north_clk);
96276333Sian	if (ret) {
97276333Sian		dev_err(dev, "error enabling aggre2_north_clk: %d\n", ret);
98276333Sian		goto err_aggre2_north_clk;
99276333Sian	}
100276333Sian
101276333Sian	ret = reset_control_deassert(data->ssc_reset);
102276333Sian	if (ret) {
103276333Sian		dev_err(dev, "error deasserting ssc_reset: %d\n", ret);
104276333Sian		goto err_ssc_reset;
105276333Sian	}
106276333Sian
107276333Sian	ret = reset_control_deassert(data->ssc_bcr);
108276333Sian	if (ret) {
109276333Sian		dev_err(dev, "error deasserting ssc_bcr: %d\n", ret);
110276333Sian		goto err_ssc_bcr;
111276333Sian	}
112276333Sian
113276333Sian	regmap_write(data->halt_map, data->ssc_axi_halt + AXI_HALTREQ_REG, 0);
114276333Sian
115276333Sian	ret = clk_prepare_enable(data->ssc_xo_clk);
116276333Sian	if (ret) {
117276333Sian		dev_err(dev, "error deasserting ssc_xo_clk: %d\n", ret);
118276333Sian		goto err_ssc_xo_clk;
119276333Sian	}
120276333Sian
121276333Sian	ret = clk_prepare_enable(data->ssc_ahbs_clk);
122276333Sian	if (ret) {
123276333Sian		dev_err(dev, "error deasserting ssc_ahbs_clk: %d\n", ret);
124276333Sian		goto err_ssc_ahbs_clk;
125276333Sian	}
126276333Sian
127276333Sian	return 0;
128276333Sian
129276333Sianerr_ssc_ahbs_clk:
130276333Sian	clk_disable(data->ssc_xo_clk);
131276333Sian
132276333Sianerr_ssc_xo_clk:
133276333Sian	regmap_write(data->halt_map, data->ssc_axi_halt + AXI_HALTREQ_REG, 1);
134276333Sian
135276333Sian	reset_control_assert(data->ssc_bcr);
136276333Sian
137276333Sianerr_ssc_bcr:
138276333Sian	reset_control_assert(data->ssc_reset);
139276333Sian
140276333Sianerr_ssc_reset:
141294740Szbb	clk_disable(data->aggre2_north_clk);
142294740Szbb
143294740Szbberr_aggre2_north_clk:
144294740Szbb	reg32_set_bits(data->reg_mpm_sscaon_config0, BIT(4) | BIT(5));
145294740Szbb	reg32_set_bits(data->reg_mpm_sscaon_config1, BIT(31));
146294740Szbb
147294740Szbb	clk_disable(data->gcc_im_sleep_clk);
148294740Szbb
149294740Szbberr_gcc_im_sleep_clk:
150294740Szbb	clk_disable(data->aggre2_clk);
151294740Szbb
152294740Szbberr_aggre2_clk:
153276333Sian	clk_disable(data->xo_clk);
154276333Sian
155283365Sandrewerr_xo_clk:
156283365Sandrew	return ret;
157276333Sian}
158276333Sian
159276333Sianstatic void qcom_ssc_block_bus_deinit(struct device *dev)
160276333Sian{
161276333Sian	int ret;
162276333Sian
163276333Sian	struct qcom_ssc_block_bus_data *data = dev_get_drvdata(dev);
164276333Sian
165282547Szbb	clk_disable(data->ssc_xo_clk);
166276333Sian	clk_disable(data->ssc_ahbs_clk);
167283365Sandrew
168283365Sandrew	ret = reset_control_assert(data->ssc_bcr);
169283365Sandrew	if (ret)
170282547Szbb		dev_err(dev, "error asserting ssc_bcr: %d\n", ret);
171290656Sskra
172290656Sskra	regmap_write(data->halt_map, data->ssc_axi_halt + AXI_HALTREQ_REG, 1);
173290656Sskra
174282547Szbb	reg32_set_bits(data->reg_mpm_sscaon_config1, BIT(31));
175282547Szbb	reg32_set_bits(data->reg_mpm_sscaon_config0, BIT(4) | BIT(5));
176276333Sian
177276333Sian	ret = reset_control_assert(data->ssc_reset);
178276333Sian	if (ret)
179276333Sian		dev_err(dev, "error asserting ssc_reset: %d\n", ret);
180276333Sian
181276333Sian	clk_disable(data->gcc_im_sleep_clk);
182276333Sian
183276333Sian	clk_disable(data->aggre2_north_clk);
184283365Sandrew
185283365Sandrew	clk_disable(data->aggre2_clk);
186276333Sian	clk_disable(data->xo_clk);
187283365Sandrew}
188276333Sian
189276333Sianstatic int qcom_ssc_block_bus_pds_attach(struct device *dev, struct device **pds,
190276333Sian					 const char *const *pd_names, size_t num_pds)
191276333Sian{
192276333Sian	int ret;
193276333Sian	int i;
194276333Sian
195276333Sian	for (i = 0; i < num_pds; i++) {
196276333Sian		pds[i] = dev_pm_domain_attach_by_name(dev, pd_names[i]);
197276333Sian		if (IS_ERR_OR_NULL(pds[i])) {
198276333Sian			ret = PTR_ERR(pds[i]) ? : -ENODATA;
199276333Sian			goto unroll_attach;
200276333Sian		}
201276333Sian	}
202276333Sian
203276333Sian	return num_pds;
204276803Sian
205276803Sianunroll_attach:
206276803Sian	for (i--; i >= 0; i--)
207283365Sandrew		dev_pm_domain_detach(pds[i], false);
208283365Sandrew
209283365Sandrew	return ret;
210283365Sandrew};
211276803Sian
212276803Sianstatic void qcom_ssc_block_bus_pds_detach(struct device *dev, struct device **pds, size_t num_pds)
213276803Sian{
214276803Sian	int i;
215276803Sian
216276803Sian	for (i = 0; i < num_pds; i++)
217276803Sian		dev_pm_domain_detach(pds[i], false);
218276803Sian}
219276803Sian
220276803Sianstatic int qcom_ssc_block_bus_pds_enable(struct device **pds, size_t num_pds)
221276803Sian{
222276803Sian	int ret;
223276803Sian	int i;
224276803Sian
225276803Sian	for (i = 0; i < num_pds; i++) {
226276803Sian		dev_pm_genpd_set_performance_state(pds[i], INT_MAX);
227276803Sian		ret = pm_runtime_get_sync(pds[i]);
228276803Sian		if (ret < 0)
229276803Sian			goto unroll_pd_votes;
230276803Sian	}
231276803Sian
232276803Sian	return 0;
233276803Sian
234276803Sianunroll_pd_votes:
235276803Sian	for (i--; i >= 0; i--) {
236276803Sian		dev_pm_genpd_set_performance_state(pds[i], 0);
237277415Sandrew		pm_runtime_put(pds[i]);
238277415Sandrew	}
239277415Sandrew
240277415Sandrew	return ret;
241277415Sandrew};
242277415Sandrew
243277415Sandrewstatic void qcom_ssc_block_bus_pds_disable(struct device **pds, size_t num_pds)
244280985Sandrew{
245280985Sandrew	int i;
246280985Sandrew
247280985Sandrew	for (i = 0; i < num_pds; i++) {
248280985Sandrew		dev_pm_genpd_set_performance_state(pds[i], 0);
249280985Sandrew		pm_runtime_put(pds[i]);
250280985Sandrew	}
251280985Sandrew}
252280985Sandrew
253280985Sandrewstatic int qcom_ssc_block_bus_probe(struct platform_device *pdev)
254280985Sandrew{
255280985Sandrew	struct qcom_ssc_block_bus_data *data;
256280985Sandrew	struct device_node *np = pdev->dev.of_node;
257280985Sandrew	struct of_phandle_args halt_args;
258280985Sandrew	struct resource *res;
259280985Sandrew	int ret;
260280985Sandrew
261280985Sandrew	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
262280985Sandrew	if (!data)
263280985Sandrew		return -ENOMEM;
264280985Sandrew
265280985Sandrew	platform_set_drvdata(pdev, data);
266280985Sandrew
267280985Sandrew	data->pd_names = qcom_ssc_block_pd_names;
268280985Sandrew	data->num_pds = ARRAY_SIZE(qcom_ssc_block_pd_names);
269280985Sandrew
270280985Sandrew	/* power domains */
271280985Sandrew	ret = qcom_ssc_block_bus_pds_attach(&pdev->dev, data->pds, data->pd_names, data->num_pds);
272280985Sandrew	if (ret < 0)
273280985Sandrew		return dev_err_probe(&pdev->dev, ret, "error when attaching power domains\n");
274280985Sandrew
275276333Sian	ret = qcom_ssc_block_bus_pds_enable(data->pds, data->num_pds);
276276333Sian	if (ret < 0)
277276333Sian		return dev_err_probe(&pdev->dev, ret, "error when enabling power domains\n");
278276333Sian
279276333Sian	/* low level overrides for when the HW logic doesn't "just work" */
280289892Sian	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpm_sscaon_config0");
281276334Sian	data->reg_mpm_sscaon_config0 = devm_ioremap_resource(&pdev->dev, res);
282289892Sian	if (IS_ERR(data->reg_mpm_sscaon_config0))
283289892Sian		return dev_err_probe(&pdev->dev, PTR_ERR(data->reg_mpm_sscaon_config0),
284289892Sian				     "Failed to ioremap mpm_sscaon_config0\n");
285289892Sian
286289892Sian	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpm_sscaon_config1");
287276334Sian	data->reg_mpm_sscaon_config1 = devm_ioremap_resource(&pdev->dev, res);
288276334Sian	if (IS_ERR(data->reg_mpm_sscaon_config1))
289276334Sian		return dev_err_probe(&pdev->dev, PTR_ERR(data->reg_mpm_sscaon_config1),
290276334Sian				     "Failed to ioremap mpm_sscaon_config1\n");
291276334Sian
292276334Sian	/* resets */
293276334Sian	data->ssc_bcr = devm_reset_control_get_exclusive(&pdev->dev, "ssc_bcr");
294276334Sian	if (IS_ERR(data->ssc_bcr))
295276334Sian		return dev_err_probe(&pdev->dev, PTR_ERR(data->ssc_bcr),
296276334Sian				     "Failed to acquire reset: scc_bcr\n");
297276334Sian
298276334Sian	data->ssc_reset = devm_reset_control_get_exclusive(&pdev->dev, "ssc_reset");
299276334Sian	if (IS_ERR(data->ssc_reset))
300276334Sian		return dev_err_probe(&pdev->dev, PTR_ERR(data->ssc_reset),
301276334Sian				     "Failed to acquire reset: ssc_reset:\n");
302276334Sian
303276334Sian	/* clocks */
304276334Sian	data->xo_clk = devm_clk_get(&pdev->dev, "xo");
305276334Sian	if (IS_ERR(data->xo_clk))
306276334Sian		return dev_err_probe(&pdev->dev, PTR_ERR(data->xo_clk),
307276334Sian				     "Failed to get clock: xo\n");
308276334Sian
309276334Sian	data->aggre2_clk = devm_clk_get(&pdev->dev, "aggre2");
310276334Sian	if (IS_ERR(data->aggre2_clk))
311276334Sian		return dev_err_probe(&pdev->dev, PTR_ERR(data->aggre2_clk),
312276334Sian				     "Failed to get clock: aggre2\n");
313276334Sian
314282984Sian	data->gcc_im_sleep_clk = devm_clk_get(&pdev->dev, "gcc_im_sleep");
315276334Sian	if (IS_ERR(data->gcc_im_sleep_clk))
316276334Sian		return dev_err_probe(&pdev->dev, PTR_ERR(data->gcc_im_sleep_clk),
317282984Sian				     "Failed to get clock: gcc_im_sleep\n");
318282984Sian
319276334Sian	data->aggre2_north_clk = devm_clk_get(&pdev->dev, "aggre2_north");
320282984Sian	if (IS_ERR(data->aggre2_north_clk))
321276334Sian		return dev_err_probe(&pdev->dev, PTR_ERR(data->aggre2_north_clk),
322276334Sian				     "Failed to get clock: aggre2_north\n");
323276334Sian
324276334Sian	data->ssc_xo_clk = devm_clk_get(&pdev->dev, "ssc_xo");
325276334Sian	if (IS_ERR(data->ssc_xo_clk))
326282984Sian		return dev_err_probe(&pdev->dev, PTR_ERR(data->ssc_xo_clk),
327276334Sian				     "Failed to get clock: ssc_xo\n");
328282984Sian
329276334Sian	data->ssc_ahbs_clk = devm_clk_get(&pdev->dev, "ssc_ahbs");
330282984Sian	if (IS_ERR(data->ssc_ahbs_clk))
331282984Sian		return dev_err_probe(&pdev->dev, PTR_ERR(data->ssc_ahbs_clk),
332282984Sian				     "Failed to get clock: ssc_ahbs\n");
333282984Sian
334276334Sian	ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, "qcom,halt-regs", 1, 0,
335282984Sian					       &halt_args);
336282984Sian	if (ret < 0)
337276334Sian		return dev_err_probe(&pdev->dev, ret, "Failed to parse qcom,halt-regs\n");
338276334Sian
339276334Sian	data->halt_map = syscon_node_to_regmap(halt_args.np);
340276334Sian	of_node_put(halt_args.np);
341276803Sian	if (IS_ERR(data->halt_map))
342276334Sian		return PTR_ERR(data->halt_map);
343276334Sian
344276334Sian	data->ssc_axi_halt = halt_args.args[0];
345276334Sian
346276334Sian	qcom_ssc_block_bus_init(&pdev->dev);
347276334Sian
348276334Sian	of_platform_populate(np, NULL, NULL, &pdev->dev);
349276334Sian
350276334Sian	return 0;
351276334Sian}
352276334Sian
353276340Sianstatic void qcom_ssc_block_bus_remove(struct platform_device *pdev)
354276334Sian{
355276334Sian	struct qcom_ssc_block_bus_data *data = platform_get_drvdata(pdev);
356276334Sian
357276334Sian	qcom_ssc_block_bus_deinit(&pdev->dev);
358276334Sian
359276334Sian	iounmap(data->reg_mpm_sscaon_config0);
360276334Sian	iounmap(data->reg_mpm_sscaon_config1);
361276334Sian
362282984Sian	qcom_ssc_block_bus_pds_disable(data->pds, data->num_pds);
363276334Sian	qcom_ssc_block_bus_pds_detach(&pdev->dev, data->pds, data->num_pds);
364276334Sian	pm_runtime_disable(&pdev->dev);
365282984Sian	pm_clk_destroy(&pdev->dev);
366282984Sian}
367276334Sian
368282984Sianstatic const struct of_device_id qcom_ssc_block_bus_of_match[] = {
369276334Sian	{ .compatible = "qcom,ssc-block-bus", },
370276334Sian	{ /* sentinel */ }
371276334Sian};
372276334SianMODULE_DEVICE_TABLE(of, qcom_ssc_block_bus_of_match);
373282984Sian
374276334Sianstatic struct platform_driver qcom_ssc_block_bus_driver = {
375282984Sian	.probe = qcom_ssc_block_bus_probe,
376276334Sian	.remove_new = qcom_ssc_block_bus_remove,
377282984Sian	.driver = {
378282984Sian		.name = "qcom-ssc-block-bus",
379282984Sian		.of_match_table = qcom_ssc_block_bus_of_match,
380282984Sian	},
381276334Sian};
382282984Sian
383276334Sianmodule_platform_driver(qcom_ssc_block_bus_driver);
384276334Sian
385276334SianMODULE_DESCRIPTION("A driver for handling the init sequence needed for accessing the SSC block on (some) qcom SoCs over AHB");
386276803SianMODULE_AUTHOR("Michael Srba <Michael.Srba@seznam.cz>");
387276803Sian