1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Texas Instruments System Control Interface (TI SCI) power domain driver
4  *
5  * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
6  *	Andreas Dannenberg <dannenberg@ti.com>
7  *
8  * Loosely based on Linux kernel ti_sci_pm_domains.c...
9  */
10 
11 #include <common.h>
12 #include <dm.h>
13 #include <errno.h>
14 #include <log.h>
15 #include <malloc.h>
16 #include <power-domain-uclass.h>
17 #include <dm/device_compat.h>
18 #include <linux/err.h>
19 #include <linux/soc/ti/ti_sci_protocol.h>
20 #include <dt-bindings/soc/ti,sci_pm_domain.h>
21 
22 /**
23  * struct ti_sci_power_domain_data - pm domain controller information structure
24  * @sci: TI SCI handle used for communication with system controller
25  */
26 struct ti_sci_power_domain_data {
27 	const struct ti_sci_handle *sci;
28 };
29 
ti_sci_power_domain_probe(struct udevice * dev)30 static int ti_sci_power_domain_probe(struct udevice *dev)
31 {
32 	struct ti_sci_power_domain_data *data = dev_get_priv(dev);
33 
34 	debug("%s(dev=%p)\n", __func__, dev);
35 
36 	if (!data)
37 		return -ENOMEM;
38 
39 	/* Store handle for communication with the system controller */
40 	data->sci = ti_sci_get_handle(dev);
41 	if (IS_ERR(data->sci))
42 		return PTR_ERR(data->sci);
43 
44 	return 0;
45 }
46 
ti_sci_power_domain_request(struct power_domain * pd)47 static int ti_sci_power_domain_request(struct power_domain *pd)
48 {
49 	debug("%s(pd=%p)\n", __func__, pd);
50 	return 0;
51 }
52 
ti_sci_power_domain_free(struct power_domain * pd)53 static int ti_sci_power_domain_free(struct power_domain *pd)
54 {
55 	debug("%s(pd=%p)\n", __func__, pd);
56 	return 0;
57 }
58 
ti_sci_power_domain_on(struct power_domain * pd)59 static int ti_sci_power_domain_on(struct power_domain *pd)
60 {
61 	struct ti_sci_power_domain_data *data = dev_get_priv(pd->dev);
62 	const struct ti_sci_handle *sci = data->sci;
63 	const struct ti_sci_dev_ops *dops = &sci->ops.dev_ops;
64 	u8 flags = (uintptr_t)pd->priv;
65 	int ret;
66 
67 	debug("%s(pd=%p)\n", __func__, pd);
68 
69 	if (flags & TI_SCI_PD_EXCLUSIVE)
70 		ret = dops->get_device_exclusive(sci, pd->id);
71 	else
72 		ret = dops->get_device(sci, pd->id);
73 
74 	if (ret)
75 		dev_err(pd->dev, "%s: get_device(%lu) failed (%d)\n",
76 			__func__, pd->id, ret);
77 
78 	return ret;
79 }
80 
ti_sci_power_domain_off(struct power_domain * pd)81 static int ti_sci_power_domain_off(struct power_domain *pd)
82 {
83 	struct ti_sci_power_domain_data *data = dev_get_priv(pd->dev);
84 	const struct ti_sci_handle *sci = data->sci;
85 	const struct ti_sci_dev_ops *dops = &sci->ops.dev_ops;
86 	int ret;
87 
88 	debug("%s(pd=%p)\n", __func__, pd);
89 
90 	ret = dops->put_device(sci, pd->id);
91 	if (ret)
92 		dev_err(pd->dev, "%s: put_device(%lu) failed (%d)\n",
93 			__func__, pd->id, ret);
94 
95 	return ret;
96 }
97 
ti_sci_power_domain_of_xlate(struct power_domain * pd,struct ofnode_phandle_args * args)98 static int ti_sci_power_domain_of_xlate(struct power_domain *pd,
99 					struct ofnode_phandle_args *args)
100 {
101 	u8 flags;
102 
103 	debug("%s(power_domain=%p)\n", __func__, pd);
104 
105 	if (args->args_count < 1) {
106 		debug("Invalid args_count: %d\n", args->args_count);
107 		return -EINVAL;
108 	}
109 
110 	pd->id = args->args[0];
111 	/* By default request for device exclusive */
112 	flags = TI_SCI_PD_EXCLUSIVE;
113 	if (args->args_count == 2)
114 		flags = args->args[1] & TI_SCI_PD_EXCLUSIVE;
115 	pd->priv = (void *)(uintptr_t)flags;
116 
117 	return 0;
118 }
119 
120 static const struct udevice_id ti_sci_power_domain_of_match[] = {
121 	{ .compatible = "ti,sci-pm-domain" },
122 	{ /* sentinel */ }
123 };
124 
125 static struct power_domain_ops ti_sci_power_domain_ops = {
126 	.request = ti_sci_power_domain_request,
127 	.rfree = ti_sci_power_domain_free,
128 	.on = ti_sci_power_domain_on,
129 	.off = ti_sci_power_domain_off,
130 	.of_xlate = ti_sci_power_domain_of_xlate,
131 };
132 
133 U_BOOT_DRIVER(ti_sci_pm_domains) = {
134 	.name = "ti-sci-pm-domains",
135 	.id = UCLASS_POWER_DOMAIN,
136 	.of_match = ti_sci_power_domain_of_match,
137 	.probe = ti_sci_power_domain_probe,
138 	.priv_auto	= sizeof(struct ti_sci_power_domain_data),
139 	.ops = &ti_sci_power_domain_ops,
140 };
141