1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2020-2022 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 /**
29  * struct scmi_regulator_priv - Private data for SCMI voltage regulator
30  * @channel: Reference to the SCMI channel to use
31  */
32 struct scmi_regulator_priv {
33 	struct scmi_channel *channel;
34 };
35 
scmi_voltd_set_enable(struct udevice * dev,bool enable)36 static int scmi_voltd_set_enable(struct udevice *dev, bool enable)
37 {
38 	struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
39 	struct scmi_regulator_priv *priv = dev_get_priv(dev);
40 	struct scmi_voltd_config_set_in in = {
41 		.domain_id = pdata->domain_id,
42 		.config = enable ? SCMI_VOLTD_CONFIG_ON : SCMI_VOLTD_CONFIG_OFF,
43 	};
44 	struct scmi_voltd_config_set_out out;
45 	struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN,
46 					  SCMI_VOLTAGE_DOMAIN_CONFIG_SET,
47 					  in, out);
48 	int ret;
49 
50 	ret = devm_scmi_process_msg(dev, priv->channel, &msg);
51 	if (ret)
52 		return ret;
53 
54 	return scmi_to_linux_errno(out.status);
55 }
56 
scmi_voltd_get_enable(struct udevice * dev)57 static int scmi_voltd_get_enable(struct udevice *dev)
58 {
59 	struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
60 	struct scmi_regulator_priv *priv = dev_get_priv(dev);
61 	struct scmi_voltd_config_get_in in = {
62 		.domain_id = pdata->domain_id,
63 	};
64 	struct scmi_voltd_config_get_out out;
65 	struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN,
66 					  SCMI_VOLTAGE_DOMAIN_CONFIG_GET,
67 					  in, out);
68 	int ret;
69 
70 	ret = devm_scmi_process_msg(dev, priv->channel, &msg);
71 	if (ret < 0)
72 		return ret;
73 
74 	ret = scmi_to_linux_errno(out.status);
75 	if (ret < 0)
76 		return ret;
77 
78 	return out.config == SCMI_VOLTD_CONFIG_ON;
79 }
80 
scmi_voltd_set_voltage_level(struct udevice * dev,int uV)81 static int scmi_voltd_set_voltage_level(struct udevice *dev, int uV)
82 {
83 	struct scmi_regulator_priv *priv = dev_get_priv(dev);
84 	struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
85 	struct scmi_voltd_level_set_in in = {
86 		.domain_id = pdata->domain_id,
87 		.voltage_level = uV,
88 	};
89 	struct scmi_voltd_level_set_out out;
90 	struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN,
91 					  SCMI_VOLTAGE_DOMAIN_LEVEL_SET,
92 					  in, out);
93 	int ret;
94 
95 	ret = devm_scmi_process_msg(dev, priv->channel, &msg);
96 	if (ret < 0)
97 		return ret;
98 
99 	return scmi_to_linux_errno(out.status);
100 }
101 
scmi_voltd_get_voltage_level(struct udevice * dev)102 static int scmi_voltd_get_voltage_level(struct udevice *dev)
103 {
104 	struct scmi_regulator_priv *priv = dev_get_priv(dev);
105 	struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
106 	struct scmi_voltd_level_get_in in = {
107 		.domain_id = pdata->domain_id,
108 	};
109 	struct scmi_voltd_level_get_out out;
110 	struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN,
111 					  SCMI_VOLTAGE_DOMAIN_LEVEL_GET,
112 					  in, out);
113 	int ret;
114 
115 	ret = devm_scmi_process_msg(dev, priv->channel, &msg);
116 	if (ret < 0)
117 		return ret;
118 
119 	ret = scmi_to_linux_errno(out.status);
120 	if (ret < 0)
121 		return ret;
122 
123 	return out.voltage_level;
124 }
125 
scmi_regulator_of_to_plat(struct udevice * dev)126 static int scmi_regulator_of_to_plat(struct udevice *dev)
127 {
128 	struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
129 	fdt_addr_t reg;
130 
131 	reg = dev_read_addr(dev);
132 	if (reg == FDT_ADDR_T_NONE)
133 		return -EINVAL;
134 
135 	pdata->domain_id = (u32)reg;
136 
137 	return 0;
138 }
139 
scmi_regulator_probe(struct udevice * dev)140 static int scmi_regulator_probe(struct udevice *dev)
141 {
142 	struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
143 	struct scmi_regulator_priv *priv = dev_get_priv(dev);
144 	struct scmi_voltd_attr_in in = { 0 };
145 	struct scmi_voltd_attr_out out = { 0 };
146 	struct scmi_msg scmi_msg = {
147 		.protocol_id = SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN,
148 		.message_id = SCMI_VOLTAGE_DOMAIN_ATTRIBUTES,
149 		.in_msg = (u8 *)&in,
150 		.in_msg_sz = sizeof(in),
151 		.out_msg = (u8 *)&out,
152 		.out_msg_sz = sizeof(out),
153 	};
154 	int ret;
155 
156 	ret = devm_scmi_of_get_channel(dev->parent, &priv->channel);
157 	if (ret)
158 		return ret;
159 
160 	/* Check voltage domain is known from SCMI server */
161 	in.domain_id = pdata->domain_id;
162 
163 	ret = devm_scmi_process_msg(dev, priv->channel, &scmi_msg);
164 	if (ret) {
165 		dev_err(dev, "Failed to query voltage domain %u: %d\n",
166 			pdata->domain_id, ret);
167 		return -ENXIO;
168 	}
169 
170 	return 0;
171 }
172 
173 static const struct dm_regulator_ops scmi_voltd_ops = {
174 	.get_value = scmi_voltd_get_voltage_level,
175 	.set_value = scmi_voltd_set_voltage_level,
176 	.get_enable = scmi_voltd_get_enable,
177 	.set_enable = scmi_voltd_set_enable,
178 };
179 
180 U_BOOT_DRIVER(scmi_regulator) = {
181 	.name = "scmi_regulator",
182 	.id = UCLASS_REGULATOR,
183 	.ops = &scmi_voltd_ops,
184 	.probe = scmi_regulator_probe,
185 	.of_to_plat = scmi_regulator_of_to_plat,
186 	.plat_auto = sizeof(struct scmi_regulator_platdata),
187 	.priv_auto = sizeof(struct scmi_regulator_priv *),
188 };
189 
scmi_regulator_bind(struct udevice * dev)190 static int scmi_regulator_bind(struct udevice *dev)
191 {
192 	struct driver *drv;
193 	ofnode node;
194 	int ret;
195 
196 	drv = DM_DRIVER_GET(scmi_regulator);
197 
198 	ofnode_for_each_subnode(node, dev_ofnode(dev)) {
199 		ret = device_bind(dev, drv, ofnode_get_name(node),
200 				  NULL, node, NULL);
201 		if (ret)
202 			return ret;
203 	}
204 
205 	return 0;
206 }
207 
208 U_BOOT_DRIVER(scmi_voltage_domain) = {
209 	.name = "scmi_voltage_domain",
210 	.id = UCLASS_NOP,
211 	.bind = scmi_regulator_bind,
212 };
213