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