File Index
Files and directories
Sign in
FILE

ti_sci_pm_domains.c

File preview with syntax highlighting.

linux-6.19.14/drivers/pmdomain/ti/ti_sci_pm_domains.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * TI SCI Generic Power Domain Driver
  4 *
  5 * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/
  6 *	J Keerthy <j-keerthy@ti.com>
  7 *	Dave Gerlach <d-gerlach@ti.com>
  8 */
  9
 10#include <linux/err.h>
 11#include <linux/module.h>
 12#include <linux/of.h>
 13#include <linux/platform_device.h>
 14#include <linux/pm_domain.h>
 15#include <linux/pm_qos.h>
 16#include <linux/pm_runtime.h>
 17#include <linux/slab.h>
 18#include <linux/soc/ti/ti_sci_protocol.h>
 19#include <dt-bindings/soc/ti,sci_pm_domain.h>
 20
 21/**
 22 * struct ti_sci_genpd_provider: holds common TI SCI genpd provider data
 23 * @ti_sci: handle to TI SCI protocol driver that provides ops to
 24 *	    communicate with system control processor.
 25 * @dev: pointer to dev for the driver for devm allocs
 26 * @pd_list: list of all the power domains on the device
 27 * @data: onecell data for genpd core
 28 */
 29struct ti_sci_genpd_provider {
 30	const struct ti_sci_handle *ti_sci;
 31	struct device *dev;
 32	struct list_head pd_list;
 33	struct genpd_onecell_data data;
 34};
 35
 36/**
 37 * struct ti_sci_pm_domain: TI specific data needed for power domain
 38 * @idx: index of the device that identifies it with the system
 39 *	 control processor.
 40 * @exclusive: Permissions for exclusive request or shared request of the
 41 *	       device.
 42 * @pd: generic_pm_domain for use with the genpd framework
 43 * @node: link for the genpd list
 44 * @parent: link to the parent TI SCI genpd provider
 45 */
 46struct ti_sci_pm_domain {
 47	int idx;
 48	u8 exclusive;
 49	struct generic_pm_domain pd;
 50	struct list_head node;
 51	struct ti_sci_genpd_provider *parent;
 52};
 53
 54#define genpd_to_ti_sci_pd(gpd) container_of(gpd, struct ti_sci_pm_domain, pd)
 55
 56static inline bool ti_sci_pd_is_valid_constraint(s32 val)
 57{
 58	return val != PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
 59}
 60
 61#ifdef CONFIG_PM_SLEEP
 62static void ti_sci_pd_set_lat_constraint(struct device *dev, s32 val)
 63{
 64	struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain);
 65	struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(genpd);
 66	const struct ti_sci_handle *ti_sci = pd->parent->ti_sci;
 67	u16 val_ms;
 68	int ret;
 69
 70	/* PM QoS latency unit is usecs, TI SCI uses msecs */
 71	val_ms = val / USEC_PER_MSEC;
 72	ret = ti_sci->ops.pm_ops.set_latency_constraint(ti_sci, val_ms, TISCI_MSG_CONSTRAINT_SET);
 73	if (ret)
 74		dev_err(dev, "ti_sci_pd: set latency constraint failed: ret=%d\n",
 75			ret);
 76	else
 77		dev_dbg(dev, "ti_sci_pd: ID:%d set latency constraint %d\n",
 78			pd->idx, val);
 79}
 80#endif
 81
 82static inline void ti_sci_pd_set_wkup_constraint(struct device *dev)
 83{
 84	struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain);
 85	struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(genpd);
 86	const struct ti_sci_handle *ti_sci = pd->parent->ti_sci;
 87	int ret;
 88
 89	if (device_may_wakeup(dev)) {
 90		/*
 91		 * If device can wakeup using IO daisy chain wakeups,
 92		 * we do not want to set a constraint.
 93		 */
 94		if (dev->power.wakeirq) {
 95			dev_dbg(dev, "%s: has wake IRQ, not setting constraints\n", __func__);
 96			return;
 97		}
 98
 99		ret = ti_sci->ops.pm_ops.set_device_constraint(ti_sci, pd->idx,
100							       TISCI_MSG_CONSTRAINT_SET);
101		if (!ret)
102			dev_dbg(dev, "ti_sci_pd: ID:%d set device constraint.\n", pd->idx);
103	}
104}
105
106/*
107 * ti_sci_pd_power_off(): genpd power down hook
108 * @domain: pointer to the powerdomain to power off
109 */
110static int ti_sci_pd_power_off(struct generic_pm_domain *domain)
111{
112	struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(domain);
113	const struct ti_sci_handle *ti_sci = pd->parent->ti_sci;
114
115	return ti_sci->ops.dev_ops.put_device(ti_sci, pd->idx);
116}
117
118/*
119 * ti_sci_pd_power_on(): genpd power up hook
120 * @domain: pointer to the powerdomain to power on
121 */
122static int ti_sci_pd_power_on(struct generic_pm_domain *domain)
123{
124	struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(domain);
125	const struct ti_sci_handle *ti_sci = pd->parent->ti_sci;
126
127	if (pd->exclusive)
128		return ti_sci->ops.dev_ops.get_device_exclusive(ti_sci,
129								pd->idx);
130	else
131		return ti_sci->ops.dev_ops.get_device(ti_sci, pd->idx);
132}
133
134#ifdef CONFIG_PM_SLEEP
135static int ti_sci_pd_suspend(struct device *dev)
136{
137	int ret;
138	s32 val;
139
140	ret = pm_generic_suspend(dev);
141	if (ret)
142		return ret;
143
144	val = dev_pm_qos_read_value(dev, DEV_PM_QOS_RESUME_LATENCY);
145	if (ti_sci_pd_is_valid_constraint(val))
146		ti_sci_pd_set_lat_constraint(dev, val);
147
148	ti_sci_pd_set_wkup_constraint(dev);
149
150	return 0;
151}
152#else
153#define ti_sci_pd_suspend		NULL
154#endif
155
156/*
157 * ti_sci_pd_xlate(): translation service for TI SCI genpds
158 * @genpdspec: DT identification data for the genpd
159 * @data: genpd core data for all the powerdomains on the device
160 */
161static struct generic_pm_domain *ti_sci_pd_xlate(
162					const struct of_phandle_args *genpdspec,
163					void *data)
164{
165	struct genpd_onecell_data *genpd_data = data;
166	unsigned int idx = genpdspec->args[0];
167
168	if (genpdspec->args_count != 1 && genpdspec->args_count != 2)
169		return ERR_PTR(-EINVAL);
170
171	if (idx >= genpd_data->num_domains) {
172		pr_err("%s: invalid domain index %u\n", __func__, idx);
173		return ERR_PTR(-EINVAL);
174	}
175
176	if (!genpd_data->domains[idx])
177		return ERR_PTR(-ENOENT);
178
179	genpd_to_ti_sci_pd(genpd_data->domains[idx])->exclusive =
180		genpdspec->args[1];
181
182	return genpd_data->domains[idx];
183}
184
185static const struct of_device_id ti_sci_pm_domain_matches[] = {
186	{ .compatible = "ti,sci-pm-domain", },
187	{ },
188};
189MODULE_DEVICE_TABLE(of, ti_sci_pm_domain_matches);
190
191static bool ti_sci_pm_idx_exists(struct ti_sci_genpd_provider *pd_provider, u32 idx)
192{
193	struct ti_sci_pm_domain *pd;
194
195	list_for_each_entry(pd, &pd_provider->pd_list, node) {
196		if (pd->idx == idx)
197			return true;
198	}
199
200	return false;
201}
202
203static bool ti_sci_pm_pd_is_on(struct ti_sci_genpd_provider *pd_provider,
204			       int pd_idx)
205{
206	bool is_on;
207	int ret;
208
209	if (!pd_provider->ti_sci->ops.dev_ops.is_on)
210		return false;
211
212	ret = pd_provider->ti_sci->ops.dev_ops.is_on(pd_provider->ti_sci,
213						     pd_idx, NULL, &is_on);
214	if (ret)
215		return false;
216
217	return is_on;
218}
219
220static int ti_sci_pm_domain_probe(struct platform_device *pdev)
221{
222	struct device *dev = &pdev->dev;
223	struct ti_sci_genpd_provider *pd_provider;
224	struct ti_sci_pm_domain *pd;
225	struct device_node *np __free(device_node) = NULL;
226	struct of_phandle_args args;
227	u32 max_id = 0;
228	int index;
229
230	pd_provider = devm_kzalloc(dev, sizeof(*pd_provider), GFP_KERNEL);
231	if (!pd_provider)
232		return -ENOMEM;
233
234	pd_provider->ti_sci = devm_ti_sci_get_handle(dev);
235	if (IS_ERR(pd_provider->ti_sci))
236		return PTR_ERR(pd_provider->ti_sci);
237
238	pd_provider->dev = dev;
239
240	INIT_LIST_HEAD(&pd_provider->pd_list);
241
242	/* Find highest device ID used for power domains */
243	for_each_node_with_property(np, "power-domains") {
244		index = 0;
245
246		while (!of_parse_phandle_with_args(np, "power-domains",
247						   "#power-domain-cells",
248						   index, &args)) {
249
250			if (args.args_count >= 1 && args.np == dev->of_node) {
251				bool is_on;
252
253				of_node_put(args.np);
254				if (args.args[0] > max_id) {
255					max_id = args.args[0];
256				} else {
257					if (ti_sci_pm_idx_exists(pd_provider, args.args[0])) {
258						index++;
259						continue;
260					}
261				}
262
263				pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
264				if (!pd)
265					return -ENOMEM;
266
267				pd->pd.name = devm_kasprintf(dev, GFP_KERNEL,
268							     "pd:%d",
269							     args.args[0]);
270				if (!pd->pd.name)
271					return -ENOMEM;
272
273				pd->pd.power_off = ti_sci_pd_power_off;
274				pd->pd.power_on = ti_sci_pd_power_on;
275				pd->pd.flags |= GENPD_FLAG_ACTIVE_WAKEUP;
276				pd->idx = args.args[0];
277				pd->parent = pd_provider;
278				/*
279				 * If SCI constraint functions are present, then firmware
280				 * supports the constraints API.
281				 */
282				if (pd_provider->ti_sci->ops.pm_ops.set_device_constraint &&
283				    pd_provider->ti_sci->ops.pm_ops.set_latency_constraint)
284					pd->pd.domain.ops.suspend = ti_sci_pd_suspend;
285
286				is_on = ti_sci_pm_pd_is_on(pd_provider,
287							   pd->idx);
288
289				pm_genpd_init(&pd->pd, NULL, !is_on);
290
291				list_add(&pd->node, &pd_provider->pd_list);
292			} else {
293				of_node_put(args.np);
294			}
295
296			index++;
297		}
298	}
299
300	pd_provider->data.domains =
301		devm_kcalloc(dev, max_id + 1,
302			     sizeof(*pd_provider->data.domains),
303			     GFP_KERNEL);
304	if (!pd_provider->data.domains)
305		return -ENOMEM;
306
307	pd_provider->data.num_domains = max_id + 1;
308	pd_provider->data.xlate = ti_sci_pd_xlate;
309
310	list_for_each_entry(pd, &pd_provider->pd_list, node)
311		pd_provider->data.domains[pd->idx] = &pd->pd;
312
313	return of_genpd_add_provider_onecell(dev->of_node, &pd_provider->data);
314}
315
316static struct platform_driver ti_sci_pm_domains_driver = {
317	.probe = ti_sci_pm_domain_probe,
318	.driver = {
319		.name = "ti_sci_pm_domains",
320		.of_match_table = ti_sci_pm_domain_matches,
321	},
322};
323module_platform_driver(ti_sci_pm_domains_driver);
324MODULE_LICENSE("GPL v2");
325MODULE_DESCRIPTION("TI System Control Interface (SCI) Power Domain driver");
326MODULE_AUTHOR("Dave Gerlach");
~/ Read-only file preview.