1 // SPDX-License-Identifier: GPL-2.0-only
2 // Copyright (c) 2021, Michael Srba
5 #include <linux/delay.h>
7 #include <linux/mfd/syscon.h>
8 #include <linux/module.h>
9 #include <linux/of_platform.h>
10 #include <linux/platform_device.h>
11 #include <linux/pm_clock.h>
12 #include <linux/pm_domain.h>
13 #include <linux/pm_runtime.h>
14 #include <linux/regmap.h>
15 #include <linux/reset.h>
17 /* AXI Halt Register Offsets */
18 #define AXI_HALTREQ_REG 0x0
19 #define AXI_HALTACK_REG 0x4
20 #define AXI_IDLE_REG 0x8
22 #define SSCAON_CONFIG0_CLAMP_EN_OVRD BIT(4)
23 #define SSCAON_CONFIG0_CLAMP_EN_OVRD_VAL BIT(5)
25 static const char *const qcom_ssc_block_pd_names
[] = {
30 struct qcom_ssc_block_bus_data
{
31 const char *const *pd_names
;
32 struct device
*pds
[ARRAY_SIZE(qcom_ssc_block_pd_names
)];
33 char __iomem
*reg_mpm_sscaon_config0
;
34 char __iomem
*reg_mpm_sscaon_config1
;
35 struct regmap
*halt_map
;
37 struct clk
*aggre2_clk
;
38 struct clk
*gcc_im_sleep_clk
;
39 struct clk
*aggre2_north_clk
;
40 struct clk
*ssc_xo_clk
;
41 struct clk
*ssc_ahbs_clk
;
42 struct reset_control
*ssc_bcr
;
43 struct reset_control
*ssc_reset
;
48 static void reg32_set_bits(char __iomem
*reg
, u32 value
)
50 u32 tmp
= ioread32(reg
);
52 iowrite32(tmp
| value
, reg
);
55 static void reg32_clear_bits(char __iomem
*reg
, u32 value
)
57 u32 tmp
= ioread32(reg
);
59 iowrite32(tmp
& (~value
), reg
);
62 static int qcom_ssc_block_bus_init(struct device
*dev
)
66 struct qcom_ssc_block_bus_data
*data
= dev_get_drvdata(dev
);
68 ret
= clk_prepare_enable(data
->xo_clk
);
70 dev_err(dev
, "error enabling xo_clk: %d\n", ret
);
74 ret
= clk_prepare_enable(data
->aggre2_clk
);
76 dev_err(dev
, "error enabling aggre2_clk: %d\n", ret
);
80 ret
= clk_prepare_enable(data
->gcc_im_sleep_clk
);
82 dev_err(dev
, "error enabling gcc_im_sleep_clk: %d\n", ret
);
83 goto err_gcc_im_sleep_clk
;
87 * We need to intervene here because the HW logic driving these signals cannot handle
88 * initialization after power collapse by itself.
90 reg32_clear_bits(data
->reg_mpm_sscaon_config0
,
91 SSCAON_CONFIG0_CLAMP_EN_OVRD
| SSCAON_CONFIG0_CLAMP_EN_OVRD_VAL
);
92 /* override few_ack/rest_ack */
93 reg32_clear_bits(data
->reg_mpm_sscaon_config1
, BIT(31));
95 ret
= clk_prepare_enable(data
->aggre2_north_clk
);
97 dev_err(dev
, "error enabling aggre2_north_clk: %d\n", ret
);
98 goto err_aggre2_north_clk
;
101 ret
= reset_control_deassert(data
->ssc_reset
);
103 dev_err(dev
, "error deasserting ssc_reset: %d\n", ret
);
107 ret
= reset_control_deassert(data
->ssc_bcr
);
109 dev_err(dev
, "error deasserting ssc_bcr: %d\n", ret
);
113 regmap_write(data
->halt_map
, data
->ssc_axi_halt
+ AXI_HALTREQ_REG
, 0);
115 ret
= clk_prepare_enable(data
->ssc_xo_clk
);
117 dev_err(dev
, "error deasserting ssc_xo_clk: %d\n", ret
);
121 ret
= clk_prepare_enable(data
->ssc_ahbs_clk
);
123 dev_err(dev
, "error deasserting ssc_ahbs_clk: %d\n", ret
);
124 goto err_ssc_ahbs_clk
;
130 clk_disable(data
->ssc_xo_clk
);
133 regmap_write(data
->halt_map
, data
->ssc_axi_halt
+ AXI_HALTREQ_REG
, 1);
135 reset_control_assert(data
->ssc_bcr
);
138 reset_control_assert(data
->ssc_reset
);
141 clk_disable(data
->aggre2_north_clk
);
143 err_aggre2_north_clk
:
144 reg32_set_bits(data
->reg_mpm_sscaon_config0
, BIT(4) | BIT(5));
145 reg32_set_bits(data
->reg_mpm_sscaon_config1
, BIT(31));
147 clk_disable(data
->gcc_im_sleep_clk
);
149 err_gcc_im_sleep_clk
:
150 clk_disable(data
->aggre2_clk
);
153 clk_disable(data
->xo_clk
);
159 static void qcom_ssc_block_bus_deinit(struct device
*dev
)
163 struct qcom_ssc_block_bus_data
*data
= dev_get_drvdata(dev
);
165 clk_disable(data
->ssc_xo_clk
);
166 clk_disable(data
->ssc_ahbs_clk
);
168 ret
= reset_control_assert(data
->ssc_bcr
);
170 dev_err(dev
, "error asserting ssc_bcr: %d\n", ret
);
172 regmap_write(data
->halt_map
, data
->ssc_axi_halt
+ AXI_HALTREQ_REG
, 1);
174 reg32_set_bits(data
->reg_mpm_sscaon_config1
, BIT(31));
175 reg32_set_bits(data
->reg_mpm_sscaon_config0
, BIT(4) | BIT(5));
177 ret
= reset_control_assert(data
->ssc_reset
);
179 dev_err(dev
, "error asserting ssc_reset: %d\n", ret
);
181 clk_disable(data
->gcc_im_sleep_clk
);
183 clk_disable(data
->aggre2_north_clk
);
185 clk_disable(data
->aggre2_clk
);
186 clk_disable(data
->xo_clk
);
189 static int qcom_ssc_block_bus_pds_attach(struct device
*dev
, struct device
**pds
,
190 const char *const *pd_names
, size_t num_pds
)
195 for (i
= 0; i
< num_pds
; i
++) {
196 pds
[i
] = dev_pm_domain_attach_by_name(dev
, pd_names
[i
]);
197 if (IS_ERR_OR_NULL(pds
[i
])) {
198 ret
= PTR_ERR(pds
[i
]) ? : -ENODATA
;
206 for (i
--; i
>= 0; i
--)
207 dev_pm_domain_detach(pds
[i
], false);
212 static void qcom_ssc_block_bus_pds_detach(struct device
*dev
, struct device
**pds
, size_t num_pds
)
216 for (i
= 0; i
< num_pds
; i
++)
217 dev_pm_domain_detach(pds
[i
], false);
220 static int qcom_ssc_block_bus_pds_enable(struct device
**pds
, size_t num_pds
)
225 for (i
= 0; i
< num_pds
; i
++) {
226 dev_pm_genpd_set_performance_state(pds
[i
], INT_MAX
);
227 ret
= pm_runtime_get_sync(pds
[i
]);
229 goto unroll_pd_votes
;
235 for (i
--; i
>= 0; i
--) {
236 dev_pm_genpd_set_performance_state(pds
[i
], 0);
237 pm_runtime_put(pds
[i
]);
243 static void qcom_ssc_block_bus_pds_disable(struct device
**pds
, size_t num_pds
)
247 for (i
= 0; i
< num_pds
; i
++) {
248 dev_pm_genpd_set_performance_state(pds
[i
], 0);
249 pm_runtime_put(pds
[i
]);
253 static int qcom_ssc_block_bus_probe(struct platform_device
*pdev
)
255 struct qcom_ssc_block_bus_data
*data
;
256 struct device_node
*np
= pdev
->dev
.of_node
;
257 struct of_phandle_args halt_args
;
258 struct resource
*res
;
261 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
265 platform_set_drvdata(pdev
, data
);
267 data
->pd_names
= qcom_ssc_block_pd_names
;
268 data
->num_pds
= ARRAY_SIZE(qcom_ssc_block_pd_names
);
271 ret
= qcom_ssc_block_bus_pds_attach(&pdev
->dev
, data
->pds
, data
->pd_names
, data
->num_pds
);
273 return dev_err_probe(&pdev
->dev
, ret
, "error when attaching power domains\n");
275 ret
= qcom_ssc_block_bus_pds_enable(data
->pds
, data
->num_pds
);
277 return dev_err_probe(&pdev
->dev
, ret
, "error when enabling power domains\n");
279 /* low level overrides for when the HW logic doesn't "just work" */
280 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "mpm_sscaon_config0");
281 data
->reg_mpm_sscaon_config0
= devm_ioremap_resource(&pdev
->dev
, res
);
282 if (IS_ERR(data
->reg_mpm_sscaon_config0
))
283 return dev_err_probe(&pdev
->dev
, PTR_ERR(data
->reg_mpm_sscaon_config0
),
284 "Failed to ioremap mpm_sscaon_config0\n");
286 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "mpm_sscaon_config1");
287 data
->reg_mpm_sscaon_config1
= devm_ioremap_resource(&pdev
->dev
, res
);
288 if (IS_ERR(data
->reg_mpm_sscaon_config1
))
289 return dev_err_probe(&pdev
->dev
, PTR_ERR(data
->reg_mpm_sscaon_config1
),
290 "Failed to ioremap mpm_sscaon_config1\n");
293 data
->ssc_bcr
= devm_reset_control_get_exclusive(&pdev
->dev
, "ssc_bcr");
294 if (IS_ERR(data
->ssc_bcr
))
295 return dev_err_probe(&pdev
->dev
, PTR_ERR(data
->ssc_bcr
),
296 "Failed to acquire reset: scc_bcr\n");
298 data
->ssc_reset
= devm_reset_control_get_exclusive(&pdev
->dev
, "ssc_reset");
299 if (IS_ERR(data
->ssc_reset
))
300 return dev_err_probe(&pdev
->dev
, PTR_ERR(data
->ssc_reset
),
301 "Failed to acquire reset: ssc_reset:\n");
304 data
->xo_clk
= devm_clk_get(&pdev
->dev
, "xo");
305 if (IS_ERR(data
->xo_clk
))
306 return dev_err_probe(&pdev
->dev
, PTR_ERR(data
->xo_clk
),
307 "Failed to get clock: xo\n");
309 data
->aggre2_clk
= devm_clk_get(&pdev
->dev
, "aggre2");
310 if (IS_ERR(data
->aggre2_clk
))
311 return dev_err_probe(&pdev
->dev
, PTR_ERR(data
->aggre2_clk
),
312 "Failed to get clock: aggre2\n");
314 data
->gcc_im_sleep_clk
= devm_clk_get(&pdev
->dev
, "gcc_im_sleep");
315 if (IS_ERR(data
->gcc_im_sleep_clk
))
316 return dev_err_probe(&pdev
->dev
, PTR_ERR(data
->gcc_im_sleep_clk
),
317 "Failed to get clock: gcc_im_sleep\n");
319 data
->aggre2_north_clk
= devm_clk_get(&pdev
->dev
, "aggre2_north");
320 if (IS_ERR(data
->aggre2_north_clk
))
321 return dev_err_probe(&pdev
->dev
, PTR_ERR(data
->aggre2_north_clk
),
322 "Failed to get clock: aggre2_north\n");
324 data
->ssc_xo_clk
= devm_clk_get(&pdev
->dev
, "ssc_xo");
325 if (IS_ERR(data
->ssc_xo_clk
))
326 return dev_err_probe(&pdev
->dev
, PTR_ERR(data
->ssc_xo_clk
),
327 "Failed to get clock: ssc_xo\n");
329 data
->ssc_ahbs_clk
= devm_clk_get(&pdev
->dev
, "ssc_ahbs");
330 if (IS_ERR(data
->ssc_ahbs_clk
))
331 return dev_err_probe(&pdev
->dev
, PTR_ERR(data
->ssc_ahbs_clk
),
332 "Failed to get clock: ssc_ahbs\n");
334 ret
= of_parse_phandle_with_fixed_args(pdev
->dev
.of_node
, "qcom,halt-regs", 1, 0,
337 return dev_err_probe(&pdev
->dev
, ret
, "Failed to parse qcom,halt-regs\n");
339 data
->halt_map
= syscon_node_to_regmap(halt_args
.np
);
340 of_node_put(halt_args
.np
);
341 if (IS_ERR(data
->halt_map
))
342 return PTR_ERR(data
->halt_map
);
344 data
->ssc_axi_halt
= halt_args
.args
[0];
346 qcom_ssc_block_bus_init(&pdev
->dev
);
348 of_platform_populate(np
, NULL
, NULL
, &pdev
->dev
);
353 static void qcom_ssc_block_bus_remove(struct platform_device
*pdev
)
355 struct qcom_ssc_block_bus_data
*data
= platform_get_drvdata(pdev
);
357 qcom_ssc_block_bus_deinit(&pdev
->dev
);
359 iounmap(data
->reg_mpm_sscaon_config0
);
360 iounmap(data
->reg_mpm_sscaon_config1
);
362 qcom_ssc_block_bus_pds_disable(data
->pds
, data
->num_pds
);
363 qcom_ssc_block_bus_pds_detach(&pdev
->dev
, data
->pds
, data
->num_pds
);
364 pm_runtime_disable(&pdev
->dev
);
365 pm_clk_destroy(&pdev
->dev
);
368 static const struct of_device_id qcom_ssc_block_bus_of_match
[] = {
369 { .compatible
= "qcom,ssc-block-bus", },
372 MODULE_DEVICE_TABLE(of
, qcom_ssc_block_bus_of_match
);
374 static struct platform_driver qcom_ssc_block_bus_driver
= {
375 .probe
= qcom_ssc_block_bus_probe
,
376 .remove
= qcom_ssc_block_bus_remove
,
378 .name
= "qcom-ssc-block-bus",
379 .of_match_table
= qcom_ssc_block_bus_of_match
,
383 module_platform_driver(qcom_ssc_block_bus_driver
);
385 MODULE_DESCRIPTION("A driver for handling the init sequence needed for accessing the SSC block on (some) qcom SoCs over AHB");
386 MODULE_AUTHOR("Michael Srba <Michael.Srba@seznam.cz>");