blob: b3142bf4e1fd7c1822e35e026e14d6f573f2fadb [file] [log] [blame]
Etienne Carriered68663a2021-03-08 22:38:06 +01001// 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 */
21struct scmi_regulator_platdata {
22 u32 domain_id;
23};
24
25static 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
49static 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
72static 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
92static 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
115static 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
129static 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
157static 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
164U_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
173static 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
191U_BOOT_DRIVER(scmi_voltage_domain) = {
192 .name = "scmi_voltage_domain",
193 .id = UCLASS_NOP,
194 .bind = scmi_regulator_bind,
195};