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