Merge tag 'v2022.04-rc5' into next
[platform/kernel/u-boot.git] / drivers / power / regulator / scmi_regulator.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2020-2021 Linaro Limited
4  */
5
6 #define LOG_CATEGORY UCLASS_REGULATOR
7
8 #include <common.h>
9 #include <dm.h>
10 #include <errno.h>
11 #include <scmi_agent.h>
12 #include <scmi_protocols.h>
13 #include <asm/types.h>
14 #include <dm/device.h>
15 #include <dm/device_compat.h>
16 #include <dm/device-internal.h>
17 #include <linux/kernel.h>
18 #include <power/regulator.h>
19
20 /**
21  * struct scmi_regulator_platdata - Platform data for a scmi voltage domain regulator
22  * @domain_id: ID representing the regulator for the related SCMI agent
23  */
24 struct scmi_regulator_platdata {
25         u32 domain_id;
26 };
27
28 static int scmi_voltd_set_enable(struct udevice *dev, bool enable)
29 {
30         struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
31         struct scmi_voltd_config_set_in in = {
32                 .domain_id = pdata->domain_id,
33                 .config = enable ? SCMI_VOLTD_CONFIG_ON : SCMI_VOLTD_CONFIG_OFF,
34         };
35         struct scmi_voltd_config_set_out out;
36         struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN,
37                                           SCMI_VOLTAGE_DOMAIN_CONFIG_SET,
38                                           in, out);
39         int ret;
40
41         ret = devm_scmi_process_msg(dev, &msg);
42         if (ret)
43                 return ret;
44
45         ret = scmi_to_linux_errno(out.status);
46         if (ret)
47                 return ret;
48
49         return ret;
50 }
51
52 static int scmi_voltd_get_enable(struct udevice *dev)
53 {
54         struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
55         struct scmi_voltd_config_get_in in = {
56                 .domain_id = pdata->domain_id,
57         };
58         struct scmi_voltd_config_get_out out;
59         struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN,
60                                           SCMI_VOLTAGE_DOMAIN_CONFIG_GET,
61                                           in, out);
62         int ret;
63
64         ret = devm_scmi_process_msg(dev, &msg);
65         if (ret < 0)
66                 return ret;
67
68         ret = scmi_to_linux_errno(out.status);
69         if (ret < 0)
70                 return ret;
71
72         return out.config == SCMI_VOLTD_CONFIG_ON;
73 }
74
75 static int scmi_voltd_set_voltage_level(struct udevice *dev, int uV)
76 {
77         struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
78         struct scmi_voltd_level_set_in in = {
79                 .domain_id = pdata->domain_id,
80                 .voltage_level = uV,
81         };
82         struct scmi_voltd_level_set_out out;
83         struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN,
84                                           SCMI_VOLTAGE_DOMAIN_LEVEL_SET,
85                                           in, out);
86         int ret;
87
88         ret = devm_scmi_process_msg(dev, &msg);
89         if (ret < 0)
90                 return ret;
91
92         return scmi_to_linux_errno(out.status);
93 }
94
95 static int scmi_voltd_get_voltage_level(struct udevice *dev)
96 {
97         struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
98         struct scmi_voltd_level_get_in in = {
99                 .domain_id = pdata->domain_id,
100         };
101         struct scmi_voltd_level_get_out out;
102         struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN,
103                                           SCMI_VOLTAGE_DOMAIN_LEVEL_GET,
104                                           in, out);
105         int ret;
106
107         ret = devm_scmi_process_msg(dev, &msg);
108         if (ret < 0)
109                 return ret;
110
111         ret = scmi_to_linux_errno(out.status);
112         if (ret < 0)
113                 return ret;
114
115         return out.voltage_level;
116 }
117
118 static int scmi_regulator_of_to_plat(struct udevice *dev)
119 {
120         struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
121         fdt_addr_t reg;
122
123         reg = dev_read_addr(dev);
124         if (reg == FDT_ADDR_T_NONE)
125                 return -EINVAL;
126
127         pdata->domain_id = (u32)reg;
128
129         return 0;
130 }
131
132 static int scmi_regulator_probe(struct udevice *dev)
133 {
134         struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
135         struct scmi_voltd_attr_in in = { 0 };
136         struct scmi_voltd_attr_out out = { 0 };
137         struct scmi_msg scmi_msg = {
138                 .protocol_id = SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN,
139                 .message_id = SCMI_VOLTAGE_DOMAIN_ATTRIBUTES,
140                 .in_msg = (u8 *)&in,
141                 .in_msg_sz = sizeof(in),
142                 .out_msg = (u8 *)&out,
143                 .out_msg_sz = sizeof(out),
144         };
145         int ret;
146
147         /* Check voltage domain is known from SCMI server */
148         in.domain_id = pdata->domain_id;
149
150         ret = devm_scmi_process_msg(dev, &scmi_msg);
151         if (ret) {
152                 dev_err(dev, "Failed to query voltage domain %u: %d\n",
153                         pdata->domain_id, ret);
154                 return -ENXIO;
155         }
156
157         return 0;
158 }
159
160 static const struct dm_regulator_ops scmi_voltd_ops = {
161         .get_value = scmi_voltd_get_voltage_level,
162         .set_value = scmi_voltd_set_voltage_level,
163         .get_enable = scmi_voltd_get_enable,
164         .set_enable = scmi_voltd_set_enable,
165 };
166
167 U_BOOT_DRIVER(scmi_regulator) = {
168         .name = "scmi_regulator",
169         .id = UCLASS_REGULATOR,
170         .ops = &scmi_voltd_ops,
171         .probe = scmi_regulator_probe,
172         .of_to_plat = scmi_regulator_of_to_plat,
173         .plat_auto = sizeof(struct scmi_regulator_platdata),
174 };
175
176 static int scmi_regulator_bind(struct udevice *dev)
177 {
178         struct driver *drv;
179         ofnode node;
180         int ret;
181
182         drv = DM_DRIVER_GET(scmi_regulator);
183
184         ofnode_for_each_subnode(node, dev_ofnode(dev)) {
185                 ret = device_bind(dev, drv, ofnode_get_name(node),
186                                   NULL, node, NULL);
187                 if (ret)
188                         return ret;
189         }
190
191         return 0;
192 }
193
194 U_BOOT_DRIVER(scmi_voltage_domain) = {
195         .name = "scmi_voltage_domain",
196         .id = UCLASS_NOP,
197         .bind = scmi_regulator_bind,
198 };