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