197 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * PM domains for CPUs via genpd.4 *5 * Copyright (C) 2019 Linaro Ltd.6 * Author: Ulf Hansson <ulf.hansson@linaro.org>7 *8 * Copyright (c) 2021 Western Digital Corporation or its affiliates.9 * Copyright (c) 2022 Ventana Micro Systems Inc.10 */11 12#define pr_fmt(fmt) "dt-idle-genpd: " fmt13 14#include <linux/cpu.h>15#include <linux/device.h>16#include <linux/kernel.h>17#include <linux/pm_domain.h>18#include <linux/pm_runtime.h>19#include <linux/slab.h>20#include <linux/string.h>21 22#include "dt_idle_genpd.h"23 24static int pd_parse_state_nodes(25 int (*parse_state)(struct device_node *, u32 *),26 struct genpd_power_state *states, int state_count)27{28 int i, ret;29 u32 state, *state_buf;30 31 for (i = 0; i < state_count; i++) {32 ret = parse_state(to_of_node(states[i].fwnode), &state);33 if (ret)34 goto free_state;35 36 state_buf = kmalloc(sizeof(u32), GFP_KERNEL);37 if (!state_buf) {38 ret = -ENOMEM;39 goto free_state;40 }41 *state_buf = state;42 states[i].data = state_buf;43 }44 45 return 0;46 47free_state:48 i--;49 for (; i >= 0; i--)50 kfree(states[i].data);51 return ret;52}53 54static int pd_parse_states(struct device_node *np,55 int (*parse_state)(struct device_node *, u32 *),56 struct genpd_power_state **states,57 int *state_count)58{59 int ret;60 61 /* Parse the domain idle states. */62 ret = of_genpd_parse_idle_states(np, states, state_count);63 if (ret)64 return ret;65 66 /* Fill out the dt specifics for each found state. */67 ret = pd_parse_state_nodes(parse_state, *states, *state_count);68 if (ret)69 kfree(*states);70 71 return ret;72}73 74static void pd_free_states(struct genpd_power_state *states,75 unsigned int state_count)76{77 int i;78 79 for (i = 0; i < state_count; i++)80 kfree(states[i].data);81 kfree(states);82}83 84void dt_idle_pd_free(struct generic_pm_domain *pd)85{86 pd_free_states(pd->states, pd->state_count);87 kfree(pd->name);88 kfree(pd);89}90 91struct generic_pm_domain *dt_idle_pd_alloc(struct device_node *np,92 int (*parse_state)(struct device_node *, u32 *))93{94 struct generic_pm_domain *pd;95 struct genpd_power_state *states = NULL;96 int ret, state_count = 0;97 98 pd = kzalloc(sizeof(*pd), GFP_KERNEL);99 if (!pd)100 goto out;101 102 pd->name = kasprintf(GFP_KERNEL, "%pOF", np);103 if (!pd->name)104 goto free_pd;105 106 /*107 * Parse the domain idle states and let genpd manage the state selection108 * for those being compatible with "domain-idle-state".109 */110 ret = pd_parse_states(np, parse_state, &states, &state_count);111 if (ret)112 goto free_name;113 114 pd->free_states = pd_free_states;115 pd->name = kbasename(pd->name);116 pd->states = states;117 pd->state_count = state_count;118 119 pr_debug("alloc PM domain %s\n", pd->name);120 return pd;121 122free_name:123 kfree(pd->name);124free_pd:125 kfree(pd);126out:127 pr_err("failed to alloc PM domain %pOF\n", np);128 return NULL;129}130 131int dt_idle_pd_init_topology(struct device_node *np)132{133 struct of_phandle_args child, parent;134 int ret;135 136 for_each_child_of_node_scoped(np, node) {137 if (of_parse_phandle_with_args(node, "power-domains",138 "#power-domain-cells", 0, &parent))139 continue;140 141 child.np = node;142 child.args_count = 0;143 ret = of_genpd_add_subdomain(&parent, &child);144 of_node_put(parent.np);145 if (ret)146 return ret;147 }148 149 return 0;150}151 152int dt_idle_pd_remove_topology(struct device_node *np)153{154 struct of_phandle_args child, parent;155 int ret;156 157 for_each_child_of_node_scoped(np, node) {158 if (of_parse_phandle_with_args(node, "power-domains",159 "#power-domain-cells", 0, &parent))160 continue;161 162 child.np = node;163 child.args_count = 0;164 ret = of_genpd_remove_subdomain(&parent, &child);165 of_node_put(parent.np);166 if (ret)167 return ret;168 }169 170 return 0;171}172 173struct device *dt_idle_attach_cpu(int cpu, const char *name)174{175 struct device *dev;176 177 dev = dev_pm_domain_attach_by_name(get_cpu_device(cpu), name);178 if (IS_ERR_OR_NULL(dev))179 return dev;180 181 pm_runtime_irq_safe(dev);182 if (cpu_online(cpu))183 pm_runtime_get_sync(dev);184 185 dev_pm_syscore_device(dev, true);186 187 return dev;188}189 190void dt_idle_detach_cpu(struct device *dev)191{192 if (IS_ERR_OR_NULL(dev))193 return;194 195 dev_pm_domain_detach(dev, false);196}197