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