blob: 801148036ff6cf1adeaeffc0e61092f293d72b94 [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 <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 */
24struct scmi_regulator_platdata {
25 u32 domain_id;
26};
27
Etienne Carriere95dce1a2022-05-31 18:09:27 +020028/**
29 * struct scmi_regulator_priv - Private data for SCMI voltage regulator
30 * @channel: Reference to the SCMI channel to use
31 */
32struct scmi_regulator_priv {
33 struct scmi_channel *channel;
34};
35
Etienne Carriered68663a2021-03-08 22:38:06 +010036static int scmi_voltd_set_enable(struct udevice *dev, bool enable)
37{
38 struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
Etienne Carriere95dce1a2022-05-31 18:09:27 +020039 struct scmi_regulator_priv *priv = dev_get_priv(dev);
Etienne Carriered68663a2021-03-08 22:38:06 +010040 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
Etienne Carriere95dce1a2022-05-31 18:09:27 +020050 ret = devm_scmi_process_msg(dev, priv->channel, &msg);
Etienne Carriered68663a2021-03-08 22:38:06 +010051 if (ret)
52 return ret;
53
Etienne Carriereebc2b1e2022-05-31 18:09:28 +020054 return scmi_to_linux_errno(out.status);
Etienne Carriered68663a2021-03-08 22:38:06 +010055}
56
57static int scmi_voltd_get_enable(struct udevice *dev)
58{
59 struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
Etienne Carriere95dce1a2022-05-31 18:09:27 +020060 struct scmi_regulator_priv *priv = dev_get_priv(dev);
Etienne Carriered68663a2021-03-08 22:38:06 +010061 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
Etienne Carriere95dce1a2022-05-31 18:09:27 +020070 ret = devm_scmi_process_msg(dev, priv->channel, &msg);
Etienne Carriered68663a2021-03-08 22:38:06 +010071 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
81static int scmi_voltd_set_voltage_level(struct udevice *dev, int uV)
82{
Etienne Carriere95dce1a2022-05-31 18:09:27 +020083 struct scmi_regulator_priv *priv = dev_get_priv(dev);
Etienne Carriered68663a2021-03-08 22:38:06 +010084 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
Etienne Carriere95dce1a2022-05-31 18:09:27 +020095 ret = devm_scmi_process_msg(dev, priv->channel, &msg);
Etienne Carriered68663a2021-03-08 22:38:06 +010096 if (ret < 0)
97 return ret;
98
99 return scmi_to_linux_errno(out.status);
100}
101
102static int scmi_voltd_get_voltage_level(struct udevice *dev)
103{
Etienne Carriere95dce1a2022-05-31 18:09:27 +0200104 struct scmi_regulator_priv *priv = dev_get_priv(dev);
Etienne Carriered68663a2021-03-08 22:38:06 +0100105 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
Etienne Carriere95dce1a2022-05-31 18:09:27 +0200115 ret = devm_scmi_process_msg(dev, priv->channel, &msg);
Etienne Carriered68663a2021-03-08 22:38:06 +0100116 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
126static 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
140static int scmi_regulator_probe(struct udevice *dev)
141{
142 struct scmi_regulator_platdata *pdata = dev_get_plat(dev);
Etienne Carriere95dce1a2022-05-31 18:09:27 +0200143 struct scmi_regulator_priv *priv = dev_get_priv(dev);
Etienne Carriered68663a2021-03-08 22:38:06 +0100144 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
Etienne Carriere95dce1a2022-05-31 18:09:27 +0200156 ret = devm_scmi_of_get_channel(dev->parent, &priv->channel);
157 if (ret)
158 return ret;
159
Etienne Carriered68663a2021-03-08 22:38:06 +0100160 /* Check voltage domain is known from SCMI server */
161 in.domain_id = pdata->domain_id;
162
Etienne Carriere95dce1a2022-05-31 18:09:27 +0200163 ret = devm_scmi_process_msg(dev, priv->channel, &scmi_msg);
Etienne Carriered68663a2021-03-08 22:38:06 +0100164 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
173static 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
180U_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),
Etienne Carriere95dce1a2022-05-31 18:09:27 +0200187 .priv_auto = sizeof(struct scmi_regulator_priv *),
Etienne Carriered68663a2021-03-08 22:38:06 +0100188};
189
190static 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
208U_BOOT_DRIVER(scmi_voltage_domain) = {
209 .name = "scmi_voltage_domain",
210 .id = UCLASS_NOP,
211 .bind = scmi_regulator_bind,
212};