ret = clk_prepare_enable(data->xo_clk); if (ret) {
dev_err(dev, "error enabling xo_clk: %d\n", ret); goto err_xo_clk;
}
ret = clk_prepare_enable(data->aggre2_clk); if (ret) {
dev_err(dev, "error enabling aggre2_clk: %d\n", ret); goto err_aggre2_clk;
}
ret = clk_prepare_enable(data->gcc_im_sleep_clk); if (ret) {
dev_err(dev, "error enabling gcc_im_sleep_clk: %d\n", ret); goto err_gcc_im_sleep_clk;
}
/* * We need to intervene here because the HW logic driving these signals cannot handle * initialization after power collapse by itself.
*/
reg32_clear_bits(data->reg_mpm_sscaon_config0,
SSCAON_CONFIG0_CLAMP_EN_OVRD | SSCAON_CONFIG0_CLAMP_EN_OVRD_VAL); /* override few_ack/rest_ack */
reg32_clear_bits(data->reg_mpm_sscaon_config1, BIT(31));
ret = clk_prepare_enable(data->aggre2_north_clk); if (ret) {
dev_err(dev, "error enabling aggre2_north_clk: %d\n", ret); goto err_aggre2_north_clk;
}
ret = reset_control_deassert(data->ssc_reset); if (ret) {
dev_err(dev, "error deasserting ssc_reset: %d\n", ret); goto err_ssc_reset;
}
ret = reset_control_deassert(data->ssc_bcr); if (ret) {
dev_err(dev, "error deasserting ssc_bcr: %d\n", ret); goto err_ssc_bcr;
}
for (i = 0; i < num_pds; i++)
dev_pm_domain_detach(pds[i], false);
}
staticint qcom_ssc_block_bus_pds_enable(struct device **pds, size_t num_pds)
{ int ret; int i;
for (i = 0; i < num_pds; i++) {
dev_pm_genpd_set_performance_state(pds[i], INT_MAX);
ret = pm_runtime_get_sync(pds[i]); if (ret < 0) goto unroll_pd_votes;
}
return 0;
unroll_pd_votes: for (i--; i >= 0; i--) {
dev_pm_genpd_set_performance_state(pds[i], 0);
pm_runtime_put(pds[i]);
}
return ret;
};
staticvoid qcom_ssc_block_bus_pds_disable(struct device **pds, size_t num_pds)
{ int i;
for (i = 0; i < num_pds; i++) {
dev_pm_genpd_set_performance_state(pds[i], 0);
pm_runtime_put(pds[i]);
}
}
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM;
platform_set_drvdata(pdev, data);
/* low level overrides for when the HW logic doesn't "just work" */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpm_sscaon_config0");
data->reg_mpm_sscaon_config0 = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(data->reg_mpm_sscaon_config0)) return dev_err_probe(&pdev->dev, PTR_ERR(data->reg_mpm_sscaon_config0), "Failed to ioremap mpm_sscaon_config0\n");
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpm_sscaon_config1");
data->reg_mpm_sscaon_config1 = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(data->reg_mpm_sscaon_config1)) return dev_err_probe(&pdev->dev, PTR_ERR(data->reg_mpm_sscaon_config1), "Failed to ioremap mpm_sscaon_config1\n");
/* resets */
data->ssc_bcr = devm_reset_control_get_exclusive(&pdev->dev, "ssc_bcr"); if (IS_ERR(data->ssc_bcr)) return dev_err_probe(&pdev->dev, PTR_ERR(data->ssc_bcr), "Failed to acquire reset: scc_bcr\n");
data->ssc_reset = devm_reset_control_get_exclusive(&pdev->dev, "ssc_reset"); if (IS_ERR(data->ssc_reset)) return dev_err_probe(&pdev->dev, PTR_ERR(data->ssc_reset), "Failed to acquire reset: ssc_reset:\n");
/* clocks */
data->xo_clk = devm_clk_get(&pdev->dev, "xo"); if (IS_ERR(data->xo_clk)) return dev_err_probe(&pdev->dev, PTR_ERR(data->xo_clk), "Failed to get clock: xo\n");
data->aggre2_clk = devm_clk_get(&pdev->dev, "aggre2"); if (IS_ERR(data->aggre2_clk)) return dev_err_probe(&pdev->dev, PTR_ERR(data->aggre2_clk), "Failed to get clock: aggre2\n");
data->gcc_im_sleep_clk = devm_clk_get(&pdev->dev, "gcc_im_sleep"); if (IS_ERR(data->gcc_im_sleep_clk)) return dev_err_probe(&pdev->dev, PTR_ERR(data->gcc_im_sleep_clk), "Failed to get clock: gcc_im_sleep\n");
data->aggre2_north_clk = devm_clk_get(&pdev->dev, "aggre2_north"); if (IS_ERR(data->aggre2_north_clk)) return dev_err_probe(&pdev->dev, PTR_ERR(data->aggre2_north_clk), "Failed to get clock: aggre2_north\n");
data->ssc_xo_clk = devm_clk_get(&pdev->dev, "ssc_xo"); if (IS_ERR(data->ssc_xo_clk)) return dev_err_probe(&pdev->dev, PTR_ERR(data->ssc_xo_clk), "Failed to get clock: ssc_xo\n");
data->ssc_ahbs_clk = devm_clk_get(&pdev->dev, "ssc_ahbs"); if (IS_ERR(data->ssc_ahbs_clk)) return dev_err_probe(&pdev->dev, PTR_ERR(data->ssc_ahbs_clk), "Failed to get clock: ssc_ahbs\n");
ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, "qcom,halt-regs", 1, 0,
&halt_args); if (ret < 0) return dev_err_probe(&pdev->dev, ret, "Failed to parse qcom,halt-regs\n");
data->halt_map = syscon_node_to_regmap(halt_args.np);
of_node_put(halt_args.np); if (IS_ERR(data->halt_map)) return PTR_ERR(data->halt_map);
data->ssc_axi_halt = halt_args.args[0];
/* power domains */
data->pd_names = qcom_ssc_block_pd_names;
data->num_pds = ARRAY_SIZE(qcom_ssc_block_pd_names);
ret = qcom_ssc_block_bus_pds_attach(&pdev->dev, data->pds, data->pd_names, data->num_pds); if (ret < 0) return dev_err_probe(&pdev->dev, ret, "error when attaching power domains\n");
ret = qcom_ssc_block_bus_pds_enable(data->pds, data->num_pds); if (ret < 0) {
dev_err_probe(&pdev->dev, ret, "error when enabling power domains\n"); goto err_detach_pds_bus;
}
MODULE_DESCRIPTION("A driver for handling the init sequence needed for accessing the SSC block on (some) qcom SoCs over AHB");
MODULE_AUTHOR("Michael Srba ");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.