summaryrefslogtreecommitdiff
path: root/drivers/power/domain/ti-sci-power-domain.c
blob: 7ee121dfb26bae43a0ddcc746aeb6f72bd40440e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// SPDX-License-Identifier: GPL-2.0+
/*
 * Texas Instruments System Control Interface (TI SCI) power domain driver
 *
 * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
 *	Andreas Dannenberg <dannenberg@ti.com>
 *
 * Loosely based on Linux kernel ti_sci_pm_domains.c...
 */

#include <common.h>
#include <dm.h>
#include <errno.h>
#include <log.h>
#include <malloc.h>
#include <power-domain-uclass.h>
#include <dm/device_compat.h>
#include <linux/err.h>
#include <linux/soc/ti/ti_sci_protocol.h>
#include <dt-bindings/soc/ti,sci_pm_domain.h>

/**
 * struct ti_sci_power_domain_data - pm domain controller information structure
 * @sci: TI SCI handle used for communication with system controller
 */
struct ti_sci_power_domain_data {
	const struct ti_sci_handle *sci;
};

static int ti_sci_power_domain_probe(struct udevice *dev)
{
	struct ti_sci_power_domain_data *data = dev_get_priv(dev);

	debug("%s(dev=%p)\n", __func__, dev);

	if (!data)
		return -ENOMEM;

	/* Store handle for communication with the system controller */
	data->sci = ti_sci_get_handle(dev);
	if (IS_ERR(data->sci))
		return PTR_ERR(data->sci);

	return 0;
}

static int ti_sci_power_domain_request(struct power_domain *pd)
{
	debug("%s(pd=%p)\n", __func__, pd);
	return 0;
}

static int ti_sci_power_domain_free(struct power_domain *pd)
{
	debug("%s(pd=%p)\n", __func__, pd);
	return 0;
}

static int ti_sci_power_domain_on(struct power_domain *pd)
{
	struct ti_sci_power_domain_data *data = dev_get_priv(pd->dev);
	const struct ti_sci_handle *sci = data->sci;
	const struct ti_sci_dev_ops *dops = &sci->ops.dev_ops;
	u8 flags = (uintptr_t)pd->priv;
	int ret;

	debug("%s(pd=%p)\n", __func__, pd);

	if (flags & TI_SCI_PD_EXCLUSIVE)
		ret = dops->get_device_exclusive(sci, pd->id);
	else
		ret = dops->get_device(sci, pd->id);

	if (ret)
		dev_err(pd->dev, "%s: get_device(%lu) failed (%d)\n",
			__func__, pd->id, ret);

	return ret;
}

static int ti_sci_power_domain_off(struct power_domain *pd)
{
	struct ti_sci_power_domain_data *data = dev_get_priv(pd->dev);
	const struct ti_sci_handle *sci = data->sci;
	const struct ti_sci_dev_ops *dops = &sci->ops.dev_ops;
	int ret;

	debug("%s(pd=%p)\n", __func__, pd);

	ret = dops->put_device(sci, pd->id);
	if (ret)
		dev_err(pd->dev, "%s: put_device(%lu) failed (%d)\n",
			__func__, pd->id, ret);

	return ret;
}

static int ti_sci_power_domain_of_xlate(struct power_domain *pd,
					struct ofnode_phandle_args *args)
{
	u8 flags;

	debug("%s(power_domain=%p)\n", __func__, pd);

	if (args->args_count < 1) {
		debug("Invalid args_count: %d\n", args->args_count);
		return -EINVAL;
	}

	pd->id = args->args[0];
	/* By default request for device exclusive */
	flags = TI_SCI_PD_EXCLUSIVE;
	if (args->args_count == 2)
		flags = args->args[1] & TI_SCI_PD_EXCLUSIVE;
	pd->priv = (void *)(uintptr_t)flags;

	return 0;
}

static const struct udevice_id ti_sci_power_domain_of_match[] = {
	{ .compatible = "ti,sci-pm-domain" },
	{ /* sentinel */ }
};

static struct power_domain_ops ti_sci_power_domain_ops = {
	.request = ti_sci_power_domain_request,
	.rfree = ti_sci_power_domain_free,
	.on = ti_sci_power_domain_on,
	.off = ti_sci_power_domain_off,
	.of_xlate = ti_sci_power_domain_of_xlate,
};

U_BOOT_DRIVER(ti_sci_pm_domains) = {
	.name = "ti-sci-pm-domains",
	.id = UCLASS_POWER_DOMAIN,
	.of_match = ti_sci_power_domain_of_match,
	.probe = ti_sci_power_domain_probe,
	.priv_auto_alloc_size = sizeof(struct ti_sci_power_domain_data),
	.ops = &ti_sci_power_domain_ops,
};