在spci_pm_domian.c中会为支持power on 和 power off的设备提供scp 的接口。
其入口函数是scpi_pm_domain_probe
static int scpi_pm_domain_probe(struct platform_device *pdev)
{
#每个设备可能有多个power domain
ret = of_property_read_u32(np, "num-domains", &num_domains);
if (ret) {
dev_err(dev, "number of domains not found\n");
return -EINVAL;
}
for (i = 0; i < num_domains; i++, scpi_pd++) {
domains[i] = &scpi_pd->genpd;
scpi_pd->domain = i;
scpi_pd->ops = scpi_ops;
sprintf(scpi_pd->name, "%pOFn.%d", np, i);
scpi_pd->genpd.name = scpi_pd->name;
scpi_pd->genpd.power_off = scpi_pd_power_off;
scpi_pd->genpd.power_on = scpi_pd_power_on;
/*
* Treat all power domains as off at boot.
*
* The SCP firmware itself may have switched on some domains,
* but for reference counting purpose, keep it this way.
*/
给domain 赋值,这样这个设备的domain 就不为空
pm_genpd_init(&scpi_pd->genpd, NULL, true);
}
scpi_pd_data->domains = domains;
scpi_pd_data->num_domains = num_domains;
#将这个设备的power domian 添加到全局链表of_genpd_providers中
of_genpd_add_provider_onecell(np, scpi_pd_data);
return 0;
}
#of_genpd_add_provider_onecell -genpd_add_provider
static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
void *data)
{
struct of_genpd_provider *cp;
cp = kzalloc(sizeof(*cp), GFP_KERNEL);
if (!cp)
return -ENOMEM;
cp->node = of_node_get(np);
cp->data = data;
cp->xlate = xlate;
mutex_lock(&of_genpd_mutex);
list_add(&cp->link, &of_genpd_providers);
mutex_unlock(&of_genpd_mutex);
pr_debug("Added domain provider from %pOF\n", np);
return 0;
}
这样当设备注册自己时
int __platform_driver_register(struct platform_driver *drv,
struct module *owner)
{
drv->driver.owner = owner;
drv->driver.bus = &platform_bus_type;
drv->driver.probe = platform_drv_probe;
drv->driver.remove = platform_drv_remove;
drv->driver.shutdown = platform_drv_shutdown;
return driver_register(&drv->driver);
}
会调用platform_drv_probe->dev_pm_domain_attach->genpd_dev_pm_attach->__genpd_dev_pm_attach
int dev_pm_domain_attach(struct device *dev, bool power_on)
{
int ret;
#以apci为例的话,一般在acpi)dev_pmattach调用dev_pm_domain-set来设置pm_domain为acpi_general_pm_domain。这里的意思如果已经设置过,就不用重复设置了
if (dev->pm_domain)
return 0;
ret = acpi_dev_pm_attach(dev, power_on);
if (!ret)
ret = genpd_dev_pm_attach(dev);
return ret < 0 ? ret : 0;
}
static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev,
unsigned int index, bool power_on)
{
struct of_phandle_args pd_args;
struct generic_pm_domain *pd;
int ret;
ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
"#power-domain-cells", index, &pd_args);
if (ret < 0)
return ret;
#从全局链表中找到of_genpd_providers 找到pm domain
mutex_lock(&gpd_list_lock);
pd = genpd_get_from_provider(&pd_args);
of_node_put(pd_args.np);
if (IS_ERR(pd)) {
mutex_unlock(&gpd_list_lock);
dev_dbg(dev, "%s() failed to find PM domain: %ld\n",
__func__, PTR_ERR(pd));
return driver_deferred_probe_check_state(base_dev);
}
dev_dbg(dev, "adding to PM domain %s\n", pd->name);
#调用attach_dev
ret = genpd_add_device(pd, dev, base_dev);
mutex_unlock(&gpd_list_lock);
#power on这个设备
if (power_on) {
genpd_lock(pd);
ret = genpd_power_on(pd, 0);
genpd_unlock(pd);
}
if (ret)
genpd_remove_device(pd, dev);
return ret ? -EPROBE_DEFER : 1;
}