brintos

brintos / linux-shallow public Read only

0
0
Text · 19.0 KiB · 88406e9 Raw
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