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