linux/drivers/firmware/arm_scmi/scmi_pm_domain.c

130 lines
3.1 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0
/*
* SCMI Generic power domain support.
*
* Copyright (C) 2018 ARM Ltd.
*/
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/pm_domain.h>
#include <linux/scmi_protocol.h>
struct scmi_pm_domain {
struct generic_pm_domain genpd;
const struct scmi_handle *handle;
const char *name;
u32 domain;
};
#define to_scmi_pd(gpd) container_of(gpd, struct scmi_pm_domain, genpd)
static int scmi_pd_power(struct generic_pm_domain *domain, bool power_on)
{
int ret;
u32 state, ret_state;
struct scmi_pm_domain *pd = to_scmi_pd(domain);
const struct scmi_power_ops *ops = pd->handle->power_ops;
if (power_on)
state = SCMI_POWER_STATE_GENERIC_ON;
else
state = SCMI_POWER_STATE_GENERIC_OFF;
ret = ops->state_set(pd->handle, pd->domain, state);
if (!ret)
ret = ops->state_get(pd->handle, pd->domain, &ret_state);
if (!ret && state != ret_state)
return -EIO;
return ret;
}
static int scmi_pd_power_on(struct generic_pm_domain *domain)
{
return scmi_pd_power(domain, true);
}
static int scmi_pd_power_off(struct generic_pm_domain *domain)
{
return scmi_pd_power(domain, false);
}
static int scmi_pm_domain_probe(struct scmi_device *sdev)
{
int num_domains, i;
struct device *dev = &sdev->dev;
struct device_node *np = dev->of_node;
struct scmi_pm_domain *scmi_pd;
struct genpd_onecell_data *scmi_pd_data;
struct generic_pm_domain **domains;
const struct scmi_handle *handle = sdev->handle;
if (!handle || !handle->power_ops)
return -ENODEV;
num_domains = handle->power_ops->num_domains_get(handle);
if (num_domains < 0) {
dev_err(dev, "number of domains not found\n");
return num_domains;
}
scmi_pd = devm_kcalloc(dev, num_domains, sizeof(*scmi_pd), GFP_KERNEL);
if (!scmi_pd)
return -ENOMEM;
scmi_pd_data = devm_kzalloc(dev, sizeof(*scmi_pd_data), GFP_KERNEL);
if (!scmi_pd_data)
return -ENOMEM;
domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL);
if (!domains)
return -ENOMEM;
for (i = 0; i < num_domains; i++, scmi_pd++) {
u32 state;
firmware: arm_scmi: Fix SCMI genpd domain probing When, at probe time, an SCMI communication failure inhibits the capacity to query power domains states, such domains should be skipped. Registering partially initialized SCMI power domains with genpd will causes kernel panic. arm-scmi timed out in resp(caller: scmi_power_state_get+0xa4/0xd0) scmi-power-domain scmi_dev.2: failed to get state for domain 9 Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000 Mem abort info: ESR = 0x96000006 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 Data abort info: ISV = 0, ISS = 0x00000006 CM = 0, WnR = 0 user pgtable: 4k pages, 48-bit VAs, pgdp=00000009f3691000 [0000000000000000] pgd=00000009f1ca0003, p4d=00000009f1ca0003, pud=00000009f35ea003, pmd=0000000000000000 Internal error: Oops: 96000006 [#1] PREEMPT SMP CPU: 2 PID: 381 Comm: bash Not tainted 5.8.0-rc1-00011-gebd118c2cca8 #2 Hardware name: ARM LTD ARM Juno Development Platform/ARM Juno Development Platform, BIOS EDK II Jan 3 2020 Internal error: Oops: 96000006 [#1] PREEMPT SMP pstate: 80000005 (Nzcv daif -PAN -UAO BTYPE=--) pc : of_genpd_add_provider_onecell+0x98/0x1f8 lr : of_genpd_add_provider_onecell+0x48/0x1f8 Call trace: of_genpd_add_provider_onecell+0x98/0x1f8 scmi_pm_domain_probe+0x174/0x1e8 scmi_dev_probe+0x90/0xe0 really_probe+0xe4/0x448 driver_probe_device+0xfc/0x168 device_driver_attach+0x7c/0x88 bind_store+0xe8/0x128 drv_attr_store+0x2c/0x40 sysfs_kf_write+0x4c/0x60 kernfs_fop_write+0x114/0x230 __vfs_write+0x24/0x50 vfs_write+0xbc/0x1e0 ksys_write+0x70/0xf8 __arm64_sys_write+0x24/0x30 el0_svc_common.constprop.3+0x94/0x160 do_el0_svc+0x2c/0x98 el0_sync_handler+0x148/0x1a8 el0_sync+0x158/0x180 Do not register any power domain that failed to be queried with genpd. Fixes: 898216c97ed2 ("firmware: arm_scmi: add device power domain support using genpd") Link: https://lore.kernel.org/r/20200619220330.12217-1-cristian.marussi@arm.com Signed-off-by: Cristian Marussi <cristian.marussi@arm.com> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
2020-06-20 06:03:30 +08:00
if (handle->power_ops->state_get(handle, i, &state)) {
dev_warn(dev, "failed to get state for domain %d\n", i);
continue;
}
scmi_pd->domain = i;
scmi_pd->handle = handle;
scmi_pd->name = handle->power_ops->name_get(handle, i);
scmi_pd->genpd.name = scmi_pd->name;
scmi_pd->genpd.power_off = scmi_pd_power_off;
scmi_pd->genpd.power_on = scmi_pd_power_on;
pm_genpd_init(&scmi_pd->genpd, NULL,
state == SCMI_POWER_STATE_GENERIC_OFF);
firmware: arm_scmi: Fix SCMI genpd domain probing When, at probe time, an SCMI communication failure inhibits the capacity to query power domains states, such domains should be skipped. Registering partially initialized SCMI power domains with genpd will causes kernel panic. arm-scmi timed out in resp(caller: scmi_power_state_get+0xa4/0xd0) scmi-power-domain scmi_dev.2: failed to get state for domain 9 Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000 Mem abort info: ESR = 0x96000006 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 Data abort info: ISV = 0, ISS = 0x00000006 CM = 0, WnR = 0 user pgtable: 4k pages, 48-bit VAs, pgdp=00000009f3691000 [0000000000000000] pgd=00000009f1ca0003, p4d=00000009f1ca0003, pud=00000009f35ea003, pmd=0000000000000000 Internal error: Oops: 96000006 [#1] PREEMPT SMP CPU: 2 PID: 381 Comm: bash Not tainted 5.8.0-rc1-00011-gebd118c2cca8 #2 Hardware name: ARM LTD ARM Juno Development Platform/ARM Juno Development Platform, BIOS EDK II Jan 3 2020 Internal error: Oops: 96000006 [#1] PREEMPT SMP pstate: 80000005 (Nzcv daif -PAN -UAO BTYPE=--) pc : of_genpd_add_provider_onecell+0x98/0x1f8 lr : of_genpd_add_provider_onecell+0x48/0x1f8 Call trace: of_genpd_add_provider_onecell+0x98/0x1f8 scmi_pm_domain_probe+0x174/0x1e8 scmi_dev_probe+0x90/0xe0 really_probe+0xe4/0x448 driver_probe_device+0xfc/0x168 device_driver_attach+0x7c/0x88 bind_store+0xe8/0x128 drv_attr_store+0x2c/0x40 sysfs_kf_write+0x4c/0x60 kernfs_fop_write+0x114/0x230 __vfs_write+0x24/0x50 vfs_write+0xbc/0x1e0 ksys_write+0x70/0xf8 __arm64_sys_write+0x24/0x30 el0_svc_common.constprop.3+0x94/0x160 do_el0_svc+0x2c/0x98 el0_sync_handler+0x148/0x1a8 el0_sync+0x158/0x180 Do not register any power domain that failed to be queried with genpd. Fixes: 898216c97ed2 ("firmware: arm_scmi: add device power domain support using genpd") Link: https://lore.kernel.org/r/20200619220330.12217-1-cristian.marussi@arm.com Signed-off-by: Cristian Marussi <cristian.marussi@arm.com> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
2020-06-20 06:03:30 +08:00
domains[i] = &scmi_pd->genpd;
}
scmi_pd_data->domains = domains;
scmi_pd_data->num_domains = num_domains;
of_genpd_add_provider_onecell(np, scmi_pd_data);
return 0;
}
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_POWER, "genpd" },
{ },
};
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
static struct scmi_driver scmi_power_domain_driver = {
.name = "scmi-power-domain",
.probe = scmi_pm_domain_probe,
.id_table = scmi_id_table,
};
module_scmi_driver(scmi_power_domain_driver);
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
MODULE_DESCRIPTION("ARM SCMI power domain driver");
MODULE_LICENSE("GPL v2");