linux/drivers/soc/mediatek/mtk-pm-domains.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2020 Collabora Ltd.
   4 */
   5#include <linux/clk.h>
   6#include <linux/clk-provider.h>
   7#include <linux/init.h>
   8#include <linux/io.h>
   9#include <linux/iopoll.h>
  10#include <linux/mfd/syscon.h>
  11#include <linux/of_clk.h>
  12#include <linux/of_device.h>
  13#include <linux/platform_device.h>
  14#include <linux/pm_domain.h>
  15#include <linux/regmap.h>
  16#include <linux/regulator/consumer.h>
  17#include <linux/soc/mediatek/infracfg.h>
  18
  19#include "mt8167-pm-domains.h"
  20#include "mt8173-pm-domains.h"
  21#include "mt8183-pm-domains.h"
  22#include "mt8192-pm-domains.h"
  23
  24#define MTK_POLL_DELAY_US               10
  25#define MTK_POLL_TIMEOUT                USEC_PER_SEC
  26
  27#define PWR_RST_B_BIT                   BIT(0)
  28#define PWR_ISO_BIT                     BIT(1)
  29#define PWR_ON_BIT                      BIT(2)
  30#define PWR_ON_2ND_BIT                  BIT(3)
  31#define PWR_CLK_DIS_BIT                 BIT(4)
  32#define PWR_SRAM_CLKISO_BIT             BIT(5)
  33#define PWR_SRAM_ISOINT_B_BIT           BIT(6)
  34
  35struct scpsys_domain {
  36        struct generic_pm_domain genpd;
  37        const struct scpsys_domain_data *data;
  38        struct scpsys *scpsys;
  39        int num_clks;
  40        struct clk_bulk_data *clks;
  41        int num_subsys_clks;
  42        struct clk_bulk_data *subsys_clks;
  43        struct regmap *infracfg;
  44        struct regmap *smi;
  45        struct regulator *supply;
  46};
  47
  48struct scpsys {
  49        struct device *dev;
  50        struct regmap *base;
  51        const struct scpsys_soc_data *soc_data;
  52        struct genpd_onecell_data pd_data;
  53        struct generic_pm_domain *domains[];
  54};
  55
  56#define to_scpsys_domain(gpd) container_of(gpd, struct scpsys_domain, genpd)
  57
  58static bool scpsys_domain_is_on(struct scpsys_domain *pd)
  59{
  60        struct scpsys *scpsys = pd->scpsys;
  61        u32 status, status2;
  62
  63        regmap_read(scpsys->base, scpsys->soc_data->pwr_sta_offs, &status);
  64        status &= pd->data->sta_mask;
  65
  66        regmap_read(scpsys->base, scpsys->soc_data->pwr_sta2nd_offs, &status2);
  67        status2 &= pd->data->sta_mask;
  68
  69        /* A domain is on when both status bits are set. */
  70        return status && status2;
  71}
  72
  73static int scpsys_sram_enable(struct scpsys_domain *pd)
  74{
  75        u32 pdn_ack = pd->data->sram_pdn_ack_bits;
  76        struct scpsys *scpsys = pd->scpsys;
  77        unsigned int tmp;
  78        int ret;
  79
  80        regmap_clear_bits(scpsys->base, pd->data->ctl_offs, pd->data->sram_pdn_bits);
  81
  82        /* Either wait until SRAM_PDN_ACK all 1 or 0 */
  83        ret = regmap_read_poll_timeout(scpsys->base, pd->data->ctl_offs, tmp,
  84                                       (tmp & pdn_ack) == 0, MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
  85        if (ret < 0)
  86                return ret;
  87
  88        if (MTK_SCPD_CAPS(pd, MTK_SCPD_SRAM_ISO)) {
  89                regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_SRAM_ISOINT_B_BIT);
  90                udelay(1);
  91                regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_SRAM_CLKISO_BIT);
  92        }
  93
  94        return 0;
  95}
  96
  97static int scpsys_sram_disable(struct scpsys_domain *pd)
  98{
  99        u32 pdn_ack = pd->data->sram_pdn_ack_bits;
 100        struct scpsys *scpsys = pd->scpsys;
 101        unsigned int tmp;
 102
 103        if (MTK_SCPD_CAPS(pd, MTK_SCPD_SRAM_ISO)) {
 104                regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_SRAM_CLKISO_BIT);
 105                udelay(1);
 106                regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_SRAM_ISOINT_B_BIT);
 107        }
 108
 109        regmap_set_bits(scpsys->base, pd->data->ctl_offs, pd->data->sram_pdn_bits);
 110
 111        /* Either wait until SRAM_PDN_ACK all 1 or 0 */
 112        return regmap_read_poll_timeout(scpsys->base, pd->data->ctl_offs, tmp,
 113                                        (tmp & pdn_ack) == pdn_ack, MTK_POLL_DELAY_US,
 114                                        MTK_POLL_TIMEOUT);
 115}
 116
 117static int _scpsys_bus_protect_enable(const struct scpsys_bus_prot_data *bpd, struct regmap *regmap)
 118{
 119        int i, ret;
 120
 121        for (i = 0; i < SPM_MAX_BUS_PROT_DATA; i++) {
 122                u32 val, mask = bpd[i].bus_prot_mask;
 123
 124                if (!mask)
 125                        break;
 126
 127                if (bpd[i].bus_prot_reg_update)
 128                        regmap_set_bits(regmap, bpd[i].bus_prot_set, mask);
 129                else
 130                        regmap_write(regmap, bpd[i].bus_prot_set, mask);
 131
 132                ret = regmap_read_poll_timeout(regmap, bpd[i].bus_prot_sta,
 133                                               val, (val & mask) == mask,
 134                                               MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
 135                if (ret)
 136                        return ret;
 137        }
 138
 139        return 0;
 140}
 141
 142static int scpsys_bus_protect_enable(struct scpsys_domain *pd)
 143{
 144        int ret;
 145
 146        ret = _scpsys_bus_protect_enable(pd->data->bp_infracfg, pd->infracfg);
 147        if (ret)
 148                return ret;
 149
 150        return _scpsys_bus_protect_enable(pd->data->bp_smi, pd->smi);
 151}
 152
 153static int _scpsys_bus_protect_disable(const struct scpsys_bus_prot_data *bpd,
 154                                       struct regmap *regmap)
 155{
 156        int i, ret;
 157
 158        for (i = SPM_MAX_BUS_PROT_DATA - 1; i >= 0; i--) {
 159                u32 val, mask = bpd[i].bus_prot_mask;
 160
 161                if (!mask)
 162                        continue;
 163
 164                if (bpd[i].bus_prot_reg_update)
 165                        regmap_clear_bits(regmap, bpd[i].bus_prot_clr, mask);
 166                else
 167                        regmap_write(regmap, bpd[i].bus_prot_clr, mask);
 168
 169                if (bpd[i].ignore_clr_ack)
 170                        continue;
 171
 172                ret = regmap_read_poll_timeout(regmap, bpd[i].bus_prot_sta,
 173                                               val, !(val & mask),
 174                                               MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
 175                if (ret)
 176                        return ret;
 177        }
 178
 179        return 0;
 180}
 181
 182static int scpsys_bus_protect_disable(struct scpsys_domain *pd)
 183{
 184        int ret;
 185
 186        ret = _scpsys_bus_protect_disable(pd->data->bp_smi, pd->smi);
 187        if (ret)
 188                return ret;
 189
 190        return _scpsys_bus_protect_disable(pd->data->bp_infracfg, pd->infracfg);
 191}
 192
 193static int scpsys_regulator_enable(struct regulator *supply)
 194{
 195        return supply ? regulator_enable(supply) : 0;
 196}
 197
 198static int scpsys_regulator_disable(struct regulator *supply)
 199{
 200        return supply ? regulator_disable(supply) : 0;
 201}
 202
 203static int scpsys_power_on(struct generic_pm_domain *genpd)
 204{
 205        struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd);
 206        struct scpsys *scpsys = pd->scpsys;
 207        bool tmp;
 208        int ret;
 209
 210        ret = scpsys_regulator_enable(pd->supply);
 211        if (ret)
 212                return ret;
 213
 214        ret = clk_bulk_prepare_enable(pd->num_clks, pd->clks);
 215        if (ret)
 216                goto err_reg;
 217
 218        /* subsys power on */
 219        regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_ON_BIT);
 220        regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_ON_2ND_BIT);
 221
 222        /* wait until PWR_ACK = 1 */
 223        ret = readx_poll_timeout(scpsys_domain_is_on, pd, tmp, tmp, MTK_POLL_DELAY_US,
 224                                 MTK_POLL_TIMEOUT);
 225        if (ret < 0)
 226                goto err_pwr_ack;
 227
 228        regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_CLK_DIS_BIT);
 229        regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_ISO_BIT);
 230        regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RST_B_BIT);
 231
 232        ret = clk_bulk_prepare_enable(pd->num_subsys_clks, pd->subsys_clks);
 233        if (ret)
 234                goto err_pwr_ack;
 235
 236        ret = scpsys_sram_enable(pd);
 237        if (ret < 0)
 238                goto err_disable_subsys_clks;
 239
 240        ret = scpsys_bus_protect_disable(pd);
 241        if (ret < 0)
 242                goto err_disable_sram;
 243
 244        return 0;
 245
 246err_disable_sram:
 247        scpsys_sram_disable(pd);
 248err_disable_subsys_clks:
 249        clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks);
 250err_pwr_ack:
 251        clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
 252err_reg:
 253        scpsys_regulator_disable(pd->supply);
 254        return ret;
 255}
 256
 257static int scpsys_power_off(struct generic_pm_domain *genpd)
 258{
 259        struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd);
 260        struct scpsys *scpsys = pd->scpsys;
 261        bool tmp;
 262        int ret;
 263
 264        ret = scpsys_bus_protect_enable(pd);
 265        if (ret < 0)
 266                return ret;
 267
 268        ret = scpsys_sram_disable(pd);
 269        if (ret < 0)
 270                return ret;
 271
 272        clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks);
 273
 274        /* subsys power off */
 275        regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_RST_B_BIT);
 276        regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_ISO_BIT);
 277        regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_CLK_DIS_BIT);
 278        regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_ON_2ND_BIT);
 279        regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_ON_BIT);
 280
 281        /* wait until PWR_ACK = 0 */
 282        ret = readx_poll_timeout(scpsys_domain_is_on, pd, tmp, !tmp, MTK_POLL_DELAY_US,
 283                                 MTK_POLL_TIMEOUT);
 284        if (ret < 0)
 285                return ret;
 286
 287        clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
 288
 289        scpsys_regulator_disable(pd->supply);
 290
 291        return 0;
 292}
 293
 294static struct
 295generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_node *node)
 296{
 297        const struct scpsys_domain_data *domain_data;
 298        struct scpsys_domain *pd;
 299        struct device_node *root_node = scpsys->dev->of_node;
 300        struct device_node *smi_node;
 301        struct property *prop;
 302        const char *clk_name;
 303        int i, ret, num_clks;
 304        struct clk *clk;
 305        int clk_ind = 0;
 306        u32 id;
 307
 308        ret = of_property_read_u32(node, "reg", &id);
 309        if (ret) {
 310                dev_err(scpsys->dev, "%pOF: failed to retrieve domain id from reg: %d\n",
 311                        node, ret);
 312                return ERR_PTR(-EINVAL);
 313        }
 314
 315        if (id >= scpsys->soc_data->num_domains) {
 316                dev_err(scpsys->dev, "%pOF: invalid domain id %d\n", node, id);
 317                return ERR_PTR(-EINVAL);
 318        }
 319
 320        domain_data = &scpsys->soc_data->domains_data[id];
 321        if (domain_data->sta_mask == 0) {
 322                dev_err(scpsys->dev, "%pOF: undefined domain id %d\n", node, id);
 323                return ERR_PTR(-EINVAL);
 324        }
 325
 326        pd = devm_kzalloc(scpsys->dev, sizeof(*pd), GFP_KERNEL);
 327        if (!pd)
 328                return ERR_PTR(-ENOMEM);
 329
 330        pd->data = domain_data;
 331        pd->scpsys = scpsys;
 332
 333        if (MTK_SCPD_CAPS(pd, MTK_SCPD_DOMAIN_SUPPLY)) {
 334                /*
 335                 * Find regulator in current power domain node.
 336                 * devm_regulator_get() finds regulator in a node and its child
 337                 * node, so set of_node to current power domain node then change
 338                 * back to original node after regulator is found for current
 339                 * power domain node.
 340                 */
 341                scpsys->dev->of_node = node;
 342                pd->supply = devm_regulator_get(scpsys->dev, "domain");
 343                scpsys->dev->of_node = root_node;
 344                if (IS_ERR(pd->supply)) {
 345                        dev_err_probe(scpsys->dev, PTR_ERR(pd->supply),
 346                                      "%pOF: failed to get power supply.\n",
 347                                      node);
 348                        return ERR_CAST(pd->supply);
 349                }
 350        }
 351
 352        pd->infracfg = syscon_regmap_lookup_by_phandle_optional(node, "mediatek,infracfg");
 353        if (IS_ERR(pd->infracfg))
 354                return ERR_CAST(pd->infracfg);
 355
 356        smi_node = of_parse_phandle(node, "mediatek,smi", 0);
 357        if (smi_node) {
 358                pd->smi = device_node_to_regmap(smi_node);
 359                of_node_put(smi_node);
 360                if (IS_ERR(pd->smi))
 361                        return ERR_CAST(pd->smi);
 362        }
 363
 364        num_clks = of_clk_get_parent_count(node);
 365        if (num_clks > 0) {
 366                /* Calculate number of subsys_clks */
 367                of_property_for_each_string(node, "clock-names", prop, clk_name) {
 368                        char *subsys;
 369
 370                        subsys = strchr(clk_name, '-');
 371                        if (subsys)
 372                                pd->num_subsys_clks++;
 373                        else
 374                                pd->num_clks++;
 375                }
 376
 377                pd->clks = devm_kcalloc(scpsys->dev, pd->num_clks, sizeof(*pd->clks), GFP_KERNEL);
 378                if (!pd->clks)
 379                        return ERR_PTR(-ENOMEM);
 380
 381                pd->subsys_clks = devm_kcalloc(scpsys->dev, pd->num_subsys_clks,
 382                                               sizeof(*pd->subsys_clks), GFP_KERNEL);
 383                if (!pd->subsys_clks)
 384                        return ERR_PTR(-ENOMEM);
 385
 386        }
 387
 388        for (i = 0; i < pd->num_clks; i++) {
 389                clk = of_clk_get(node, i);
 390                if (IS_ERR(clk)) {
 391                        ret = PTR_ERR(clk);
 392                        dev_err_probe(scpsys->dev, ret,
 393                                      "%pOF: failed to get clk at index %d: %d\n", node, i, ret);
 394                        goto err_put_clocks;
 395                }
 396
 397                pd->clks[clk_ind++].clk = clk;
 398        }
 399
 400        for (i = 0; i < pd->num_subsys_clks; i++) {
 401                clk = of_clk_get(node, i + clk_ind);
 402                if (IS_ERR(clk)) {
 403                        ret = PTR_ERR(clk);
 404                        dev_err_probe(scpsys->dev, ret,
 405                                      "%pOF: failed to get clk at index %d: %d\n", node,
 406                                      i + clk_ind, ret);
 407                        goto err_put_subsys_clocks;
 408                }
 409
 410                pd->subsys_clks[i].clk = clk;
 411        }
 412
 413        /*
 414         * Initially turn on all domains to make the domains usable
 415         * with !CONFIG_PM and to get the hardware in sync with the
 416         * software.  The unused domains will be switched off during
 417         * late_init time.
 418         */
 419        if (MTK_SCPD_CAPS(pd, MTK_SCPD_KEEP_DEFAULT_OFF)) {
 420                if (scpsys_domain_is_on(pd))
 421                        dev_warn(scpsys->dev,
 422                                 "%pOF: A default off power domain has been ON\n", node);
 423        } else {
 424                ret = scpsys_power_on(&pd->genpd);
 425                if (ret < 0) {
 426                        dev_err(scpsys->dev, "%pOF: failed to power on domain: %d\n", node, ret);
 427                        goto err_put_subsys_clocks;
 428                }
 429        }
 430
 431        if (scpsys->domains[id]) {
 432                ret = -EINVAL;
 433                dev_err(scpsys->dev,
 434                        "power domain with id %d already exists, check your device-tree\n", id);
 435                goto err_put_subsys_clocks;
 436        }
 437
 438        if (!pd->data->name)
 439                pd->genpd.name = node->name;
 440        else
 441                pd->genpd.name = pd->data->name;
 442
 443        pd->genpd.power_off = scpsys_power_off;
 444        pd->genpd.power_on = scpsys_power_on;
 445
 446        if (MTK_SCPD_CAPS(pd, MTK_SCPD_KEEP_DEFAULT_OFF))
 447                pm_genpd_init(&pd->genpd, NULL, true);
 448        else
 449                pm_genpd_init(&pd->genpd, NULL, false);
 450
 451        scpsys->domains[id] = &pd->genpd;
 452
 453        return scpsys->pd_data.domains[id];
 454
 455err_put_subsys_clocks:
 456        clk_bulk_put(pd->num_subsys_clks, pd->subsys_clks);
 457err_put_clocks:
 458        clk_bulk_put(pd->num_clks, pd->clks);
 459        return ERR_PTR(ret);
 460}
 461
 462static int scpsys_add_subdomain(struct scpsys *scpsys, struct device_node *parent)
 463{
 464        struct generic_pm_domain *child_pd, *parent_pd;
 465        struct device_node *child;
 466        int ret;
 467
 468        for_each_child_of_node(parent, child) {
 469                u32 id;
 470
 471                ret = of_property_read_u32(parent, "reg", &id);
 472                if (ret) {
 473                        dev_err(scpsys->dev, "%pOF: failed to get parent domain id\n", child);
 474                        goto err_put_node;
 475                }
 476
 477                if (!scpsys->pd_data.domains[id]) {
 478                        ret = -EINVAL;
 479                        dev_err(scpsys->dev, "power domain with id %d does not exist\n", id);
 480                        goto err_put_node;
 481                }
 482
 483                parent_pd = scpsys->pd_data.domains[id];
 484
 485                child_pd = scpsys_add_one_domain(scpsys, child);
 486                if (IS_ERR(child_pd)) {
 487                        ret = PTR_ERR(child_pd);
 488                        dev_err_probe(scpsys->dev, ret, "%pOF: failed to get child domain id\n",
 489                                      child);
 490                        goto err_put_node;
 491                }
 492
 493                ret = pm_genpd_add_subdomain(parent_pd, child_pd);
 494                if (ret) {
 495                        dev_err(scpsys->dev, "failed to add %s subdomain to parent %s\n",
 496                                child_pd->name, parent_pd->name);
 497                        goto err_put_node;
 498                } else {
 499                        dev_dbg(scpsys->dev, "%s add subdomain: %s\n", parent_pd->name,
 500                                child_pd->name);
 501                }
 502
 503                /* recursive call to add all subdomains */
 504                ret = scpsys_add_subdomain(scpsys, child);
 505                if (ret)
 506                        goto err_put_node;
 507        }
 508
 509        return 0;
 510
 511err_put_node:
 512        of_node_put(child);
 513        return ret;
 514}
 515
 516static void scpsys_remove_one_domain(struct scpsys_domain *pd)
 517{
 518        int ret;
 519
 520        if (scpsys_domain_is_on(pd))
 521                scpsys_power_off(&pd->genpd);
 522
 523        /*
 524         * We're in the error cleanup already, so we only complain,
 525         * but won't emit another error on top of the original one.
 526         */
 527        ret = pm_genpd_remove(&pd->genpd);
 528        if (ret < 0)
 529                dev_err(pd->scpsys->dev,
 530                        "failed to remove domain '%s' : %d - state may be inconsistent\n",
 531                        pd->genpd.name, ret);
 532
 533        clk_bulk_put(pd->num_clks, pd->clks);
 534        clk_bulk_put(pd->num_subsys_clks, pd->subsys_clks);
 535}
 536
 537static void scpsys_domain_cleanup(struct scpsys *scpsys)
 538{
 539        struct generic_pm_domain *genpd;
 540        struct scpsys_domain *pd;
 541        int i;
 542
 543        for (i = scpsys->pd_data.num_domains - 1; i >= 0; i--) {
 544                genpd = scpsys->pd_data.domains[i];
 545                if (genpd) {
 546                        pd = to_scpsys_domain(genpd);
 547                        scpsys_remove_one_domain(pd);
 548                }
 549        }
 550}
 551
 552static const struct of_device_id scpsys_of_match[] = {
 553        {
 554                .compatible = "mediatek,mt8167-power-controller",
 555                .data = &mt8167_scpsys_data,
 556        },
 557        {
 558                .compatible = "mediatek,mt8173-power-controller",
 559                .data = &mt8173_scpsys_data,
 560        },
 561        {
 562                .compatible = "mediatek,mt8183-power-controller",
 563                .data = &mt8183_scpsys_data,
 564        },
 565        {
 566                .compatible = "mediatek,mt8192-power-controller",
 567                .data = &mt8192_scpsys_data,
 568        },
 569        { }
 570};
 571
 572static int scpsys_probe(struct platform_device *pdev)
 573{
 574        struct device *dev = &pdev->dev;
 575        struct device_node *np = dev->of_node;
 576        const struct scpsys_soc_data *soc;
 577        struct device_node *node;
 578        struct device *parent;
 579        struct scpsys *scpsys;
 580        int ret;
 581
 582        soc = of_device_get_match_data(&pdev->dev);
 583        if (!soc) {
 584                dev_err(&pdev->dev, "no power controller data\n");
 585                return -EINVAL;
 586        }
 587
 588        scpsys = devm_kzalloc(dev, struct_size(scpsys, domains, soc->num_domains), GFP_KERNEL);
 589        if (!scpsys)
 590                return -ENOMEM;
 591
 592        scpsys->dev = dev;
 593        scpsys->soc_data = soc;
 594
 595        scpsys->pd_data.domains = scpsys->domains;
 596        scpsys->pd_data.num_domains = soc->num_domains;
 597
 598        parent = dev->parent;
 599        if (!parent) {
 600                dev_err(dev, "no parent for syscon devices\n");
 601                return -ENODEV;
 602        }
 603
 604        scpsys->base = syscon_node_to_regmap(parent->of_node);
 605        if (IS_ERR(scpsys->base)) {
 606                dev_err(dev, "no regmap available\n");
 607                return PTR_ERR(scpsys->base);
 608        }
 609
 610        ret = -ENODEV;
 611        for_each_available_child_of_node(np, node) {
 612                struct generic_pm_domain *domain;
 613
 614                domain = scpsys_add_one_domain(scpsys, node);
 615                if (IS_ERR(domain)) {
 616                        ret = PTR_ERR(domain);
 617                        of_node_put(node);
 618                        goto err_cleanup_domains;
 619                }
 620
 621                ret = scpsys_add_subdomain(scpsys, node);
 622                if (ret) {
 623                        of_node_put(node);
 624                        goto err_cleanup_domains;
 625                }
 626        }
 627
 628        if (ret) {
 629                dev_dbg(dev, "no power domains present\n");
 630                return ret;
 631        }
 632
 633        ret = of_genpd_add_provider_onecell(np, &scpsys->pd_data);
 634        if (ret) {
 635                dev_err(dev, "failed to add provider: %d\n", ret);
 636                goto err_cleanup_domains;
 637        }
 638
 639        return 0;
 640
 641err_cleanup_domains:
 642        scpsys_domain_cleanup(scpsys);
 643        return ret;
 644}
 645
 646static struct platform_driver scpsys_pm_domain_driver = {
 647        .probe = scpsys_probe,
 648        .driver = {
 649                .name = "mtk-power-controller",
 650                .suppress_bind_attrs = true,
 651                .of_match_table = scpsys_of_match,
 652        },
 653};
 654builtin_platform_driver(scpsys_pm_domain_driver);
 655