Skip to content

Commit

Permalink
cpuidle: Factor-out power domain related code from PSCI domain driver
Browse files Browse the repository at this point in the history
The generic power domain related code in PSCI domain driver is largely
independent of PSCI and can be shared with RISC-V SBI domain driver
hence we factor-out this code into dt_idle_genpd.c and dt_idle_genpd.h.

Signed-off-by: Anup Patel <[email protected]>
Signed-off-by: Anup Patel <[email protected]>
Reviewed-by: Ulf Hansson <[email protected]>
Signed-off-by: Palmer Dabbelt <[email protected]>
  • Loading branch information
avpatel authored and palmer-dabbelt committed Mar 10, 2022
1 parent b820090 commit 9d976d6
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 135 deletions.
7 changes: 7 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -5051,6 +5051,13 @@ S: Supported
F: drivers/cpuidle/cpuidle-psci.h
F: drivers/cpuidle/cpuidle-psci-domain.c

CPUIDLE DRIVER - DT IDLE PM DOMAIN
M: Ulf Hansson <[email protected]>
L: [email protected]
S: Supported
F: drivers/cpuidle/dt_idle_genpd.c
F: drivers/cpuidle/dt_idle_genpd.h

CRAMFS FILESYSTEM
M: Nicolas Pitre <[email protected]>
S: Maintained
Expand Down
4 changes: 4 additions & 0 deletions drivers/cpuidle/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ config CPU_IDLE_GOV_HALTPOLL
config DT_IDLE_STATES
bool

config DT_IDLE_GENPD
depends on PM_GENERIC_DOMAINS_OF
bool

menu "ARM CPU Idle Drivers"
depends on ARM || ARM64
source "drivers/cpuidle/Kconfig.arm"
Expand Down
1 change: 1 addition & 0 deletions drivers/cpuidle/Kconfig.arm
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ config ARM_PSCI_CPUIDLE_DOMAIN
bool "PSCI CPU idle Domain"
depends on ARM_PSCI_CPUIDLE
depends on PM_GENERIC_DOMAINS_OF
select DT_IDLE_GENPD
default y
help
Select this to enable the PSCI based CPUidle driver to use PM domains,
Expand Down
1 change: 1 addition & 0 deletions drivers/cpuidle/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
obj-y += cpuidle.o driver.o governor.o sysfs.o governors/
obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
obj-$(CONFIG_DT_IDLE_STATES) += dt_idle_states.o
obj-$(CONFIG_DT_IDLE_GENPD) += dt_idle_genpd.o
obj-$(CONFIG_ARCH_HAS_CPU_RELAX) += poll_state.o
obj-$(CONFIG_HALTPOLL_CPUIDLE) += cpuidle-haltpoll.o

Expand Down
138 changes: 5 additions & 133 deletions drivers/cpuidle/cpuidle-psci-domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,96 +47,21 @@ static int psci_pd_power_off(struct generic_pm_domain *pd)
return 0;
}

static int psci_pd_parse_state_nodes(struct genpd_power_state *states,
int state_count)
{
int i, ret;
u32 psci_state, *psci_state_buf;

for (i = 0; i < state_count; i++) {
ret = psci_dt_parse_state_node(to_of_node(states[i].fwnode),
&psci_state);
if (ret)
goto free_state;

psci_state_buf = kmalloc(sizeof(u32), GFP_KERNEL);
if (!psci_state_buf) {
ret = -ENOMEM;
goto free_state;
}
*psci_state_buf = psci_state;
states[i].data = psci_state_buf;
}

return 0;

free_state:
i--;
for (; i >= 0; i--)
kfree(states[i].data);
return ret;
}

static int psci_pd_parse_states(struct device_node *np,
struct genpd_power_state **states, int *state_count)
{
int ret;

/* Parse the domain idle states. */
ret = of_genpd_parse_idle_states(np, states, state_count);
if (ret)
return ret;

/* Fill out the PSCI specifics for each found state. */
ret = psci_pd_parse_state_nodes(*states, *state_count);
if (ret)
kfree(*states);

return ret;
}

static void psci_pd_free_states(struct genpd_power_state *states,
unsigned int state_count)
{
int i;

for (i = 0; i < state_count; i++)
kfree(states[i].data);
kfree(states);
}

static int psci_pd_init(struct device_node *np, bool use_osi)
{
struct generic_pm_domain *pd;
struct psci_pd_provider *pd_provider;
struct dev_power_governor *pd_gov;
struct genpd_power_state *states = NULL;
int ret = -ENOMEM, state_count = 0;

pd = kzalloc(sizeof(*pd), GFP_KERNEL);
pd = dt_idle_pd_alloc(np, psci_dt_parse_state_node);
if (!pd)
goto out;

pd_provider = kzalloc(sizeof(*pd_provider), GFP_KERNEL);
if (!pd_provider)
goto free_pd;

pd->name = kasprintf(GFP_KERNEL, "%pOF", np);
if (!pd->name)
goto free_pd_prov;

/*
* Parse the domain idle states and let genpd manage the state selection
* for those being compatible with "domain-idle-state".
*/
ret = psci_pd_parse_states(np, &states, &state_count);
if (ret)
goto free_name;

pd->free_states = psci_pd_free_states;
pd->name = kbasename(pd->name);
pd->states = states;
pd->state_count = state_count;
pd->flags |= GENPD_FLAG_IRQ_SAFE | GENPD_FLAG_CPU_DOMAIN;

/* Allow power off when OSI has been successfully enabled. */
Expand All @@ -149,10 +74,8 @@ static int psci_pd_init(struct device_node *np, bool use_osi)
pd_gov = state_count > 0 ? &pm_domain_cpu_gov : NULL;

ret = pm_genpd_init(pd, pd_gov, false);
if (ret) {
psci_pd_free_states(states, state_count);
goto free_name;
}
if (ret)
goto free_pd_prov;

ret = of_genpd_add_provider_simple(np, pd);
if (ret)
Expand All @@ -166,12 +89,10 @@ static int psci_pd_init(struct device_node *np, bool use_osi)

remove_pd:
pm_genpd_remove(pd);
free_name:
kfree(pd->name);
free_pd_prov:
kfree(pd_provider);
free_pd:
kfree(pd);
dt_idle_pd_free(pd);
out:
pr_err("failed to init PM domain ret=%d %pOF\n", ret, np);
return ret;
Expand All @@ -195,30 +116,6 @@ static void psci_pd_remove(void)
}
}

static int psci_pd_init_topology(struct device_node *np)
{
struct device_node *node;
struct of_phandle_args child, parent;
int ret;

for_each_child_of_node(np, node) {
if (of_parse_phandle_with_args(node, "power-domains",
"#power-domain-cells", 0, &parent))
continue;

child.np = node;
child.args_count = 0;
ret = of_genpd_add_subdomain(&parent, &child);
of_node_put(parent.np);
if (ret) {
of_node_put(node);
return ret;
}
}

return 0;
}

static bool psci_pd_try_set_osi_mode(void)
{
int ret;
Expand Down Expand Up @@ -282,7 +179,7 @@ static int psci_cpuidle_domain_probe(struct platform_device *pdev)
goto no_pd;

/* Link genpd masters/subdomains to model the CPU topology. */
ret = psci_pd_init_topology(np);
ret = dt_idle_pd_init_topology(np);
if (ret)
goto remove_pd;

Expand Down Expand Up @@ -314,28 +211,3 @@ static int __init psci_idle_init_domains(void)
return platform_driver_register(&psci_cpuidle_domain_driver);
}
subsys_initcall(psci_idle_init_domains);

struct device *psci_dt_attach_cpu(int cpu)
{
struct device *dev;

dev = dev_pm_domain_attach_by_name(get_cpu_device(cpu), "psci");
if (IS_ERR_OR_NULL(dev))
return dev;

pm_runtime_irq_safe(dev);
if (cpu_online(cpu))
pm_runtime_get_sync(dev);

dev_pm_syscore_device(dev, true);

return dev;
}

void psci_dt_detach_cpu(struct device *dev)
{
if (IS_ERR_OR_NULL(dev))
return;

dev_pm_domain_detach(dev, false);
}
15 changes: 13 additions & 2 deletions drivers/cpuidle/cpuidle-psci.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,19 @@ void psci_set_domain_state(u32 state);
int psci_dt_parse_state_node(struct device_node *np, u32 *state);

#ifdef CONFIG_ARM_PSCI_CPUIDLE_DOMAIN
struct device *psci_dt_attach_cpu(int cpu);
void psci_dt_detach_cpu(struct device *dev);

#include "dt_idle_genpd.h"

static inline struct device *psci_dt_attach_cpu(int cpu)
{
return dt_idle_attach_cpu(cpu, "psci");
}

static inline void psci_dt_detach_cpu(struct device *dev)
{
dt_idle_detach_cpu(dev);
}

#else
static inline struct device *psci_dt_attach_cpu(int cpu) { return NULL; }
static inline void psci_dt_detach_cpu(struct device *dev) { }
Expand Down
Loading

0 comments on commit 9d976d6

Please sign in to comment.