blob: 7d2db1e2bee73bd043e72f679f1cbd429e95dd09 [file] [log] [blame]
Etienne Carriered68663a2021-03-08 22:38:06 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
Etienne Carriere95dce1a2022-05-31 18:09:27 +02003 * Copyright (C) 2020-2022 Linaro Limited
Etienne Carriered68663a2021-03-08 22:38:06 +01004 */
Patrick Delaunayf1f25162021-10-28 19:13:14 +02005
6#define LOG_CATEGORY UCLASS_REGULATOR
7
Etienne Carriered68663a2021-03-08 22:38:06 +01008#include <dm.h>
9#include <errno.h>
10#include <scmi_agent.h>
Alice Guof71894b2025-04-28 18:37:25 +080011#include <scmi_agent-uclass.h>
Etienne Carriered68663a2021-03-08 22:38:06 +010012#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 */
24struct scmi_regulator_platdata {
25 u32 domain_id;
26};
27
28static 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
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +090041 ret = devm_scmi_process_msg(dev, &msg);
Etienne Carriered68663a2021-03-08 22:38:06 +010042 if (ret)
43 return ret;
44
Etienne Carriereebc2b1e2022-05-31 18:09:28 +020045 return scmi_to_linux_errno(out.status);
Etienne Carriered68663a2021-03-08 22:38:06 +010046}
47
48static int scmi_voltd_get_enable(struct udevice *dev)
49{
50 struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
51 struct scmi_voltd_config_get_in in = {
52 .domain_id = pdata->domain_id,
53 };
54 struct scmi_voltd_config_get_out out;
55 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN,
56 SCMI_VOLTAGE_DOMAIN_CONFIG_GET,
57 in, out);
58 int ret;
59
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +090060 ret = devm_scmi_process_msg(dev, &msg);
Etienne Carriered68663a2021-03-08 22:38:06 +010061 if (ret < 0)
62 return ret;
63
64 ret = scmi_to_linux_errno(out.status);
65 if (ret < 0)
66 return ret;
67
68 return out.config == SCMI_VOLTD_CONFIG_ON;
69}
70
71static int scmi_voltd_set_voltage_level(struct udevice *dev, int uV)
72{
73 struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
74 struct scmi_voltd_level_set_in in = {
75 .domain_id = pdata->domain_id,
76 .voltage_level = uV,
77 };
78 struct scmi_voltd_level_set_out out;
79 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN,
80 SCMI_VOLTAGE_DOMAIN_LEVEL_SET,
81 in, out);
82 int ret;
83
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +090084 ret = devm_scmi_process_msg(dev, &msg);
Etienne Carriered68663a2021-03-08 22:38:06 +010085 if (ret < 0)
86 return ret;
87
88 return scmi_to_linux_errno(out.status);
89}
90
91static int scmi_voltd_get_voltage_level(struct udevice *dev)
92{
93 struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
94 struct scmi_voltd_level_get_in in = {
95 .domain_id = pdata->domain_id,
96 };
97 struct scmi_voltd_level_get_out out;
98 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN,
99 SCMI_VOLTAGE_DOMAIN_LEVEL_GET,
100 in, out);
101 int ret;
102
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900103 ret = devm_scmi_process_msg(dev, &msg);
Etienne Carriered68663a2021-03-08 22:38:06 +0100104 if (ret < 0)
105 return ret;
106
107 ret = scmi_to_linux_errno(out.status);
108 if (ret < 0)
109 return ret;
110
111 return out.voltage_level;
112}
113
114static int scmi_regulator_of_to_plat(struct udevice *dev)
115{
116 struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
117 fdt_addr_t reg;
118
119 reg = dev_read_addr(dev);
120 if (reg == FDT_ADDR_T_NONE)
121 return -EINVAL;
122
123 pdata->domain_id = (u32)reg;
124
125 return 0;
126}
127
128static int scmi_regulator_probe(struct udevice *dev)
129{
130 struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
131 struct scmi_voltd_attr_in in = { 0 };
132 struct scmi_voltd_attr_out out = { 0 };
133 struct scmi_msg scmi_msg = {
134 .protocol_id = SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN,
135 .message_id = SCMI_VOLTAGE_DOMAIN_ATTRIBUTES,
136 .in_msg = (u8 *)&in,
137 .in_msg_sz = sizeof(in),
138 .out_msg = (u8 *)&out,
139 .out_msg_sz = sizeof(out),
140 };
141 int ret;
142
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900143 ret = devm_scmi_of_get_channel(dev);
Etienne Carriere95dce1a2022-05-31 18:09:27 +0200144 if (ret)
145 return ret;
146
Etienne Carriered68663a2021-03-08 22:38:06 +0100147 /* Check voltage domain is known from SCMI server */
148 in.domain_id = pdata->domain_id;
149
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900150 ret = devm_scmi_process_msg(dev, &scmi_msg);
Etienne Carriered68663a2021-03-08 22:38:06 +0100151 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
160static 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
167U_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),
Etienne Carriered68663a2021-03-08 22:38:06 +0100174};
175
176static int scmi_regulator_bind(struct udevice *dev)
177{
178 struct driver *drv;
Marek Vasut25fd2ad2025-03-22 02:44:40 +0100179 ofnode regul_node;
Etienne Carriered68663a2021-03-08 22:38:06 +0100180 ofnode node;
181 int ret;
182
Marek Vasut25fd2ad2025-03-22 02:44:40 +0100183 regul_node = ofnode_find_subnode(dev_ofnode(dev), "regulators");
184 if (!ofnode_valid(regul_node)) {
185 dev_err(dev, "no regulators node\n");
186 return -ENXIO;
187 }
188
Etienne Carriered68663a2021-03-08 22:38:06 +0100189 drv = DM_DRIVER_GET(scmi_regulator);
190
Marek Vasut25fd2ad2025-03-22 02:44:40 +0100191 ofnode_for_each_subnode(node, regul_node) {
Etienne Carriered68663a2021-03-08 22:38:06 +0100192 ret = device_bind(dev, drv, ofnode_get_name(node),
193 NULL, node, NULL);
194 if (ret)
195 return ret;
196 }
197
198 return 0;
199}
200
201U_BOOT_DRIVER(scmi_voltage_domain) = {
202 .name = "scmi_voltage_domain",
203 .id = UCLASS_NOP,
204 .bind = scmi_regulator_bind,
205};
Alice Guof71894b2025-04-28 18:37:25 +0800206
207static struct scmi_proto_match match[] = {
208 { .proto_id = SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN },
209 { /* Sentinel */ }
210};
211
212U_BOOT_SCMI_PROTO_DRIVER(scmi_voltage_domain, match);