1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2020-2022 Linaro Limited
4 */
5
6#define LOG_CATEGORY UCLASS_REGULATOR
7
8#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
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
41	ret = devm_scmi_process_msg(dev, &msg);
42	if (ret)
43		return ret;
44
45	return scmi_to_linux_errno(out.status);
46}
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
60	ret = devm_scmi_process_msg(dev, &msg);
61	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
84	ret = devm_scmi_process_msg(dev, &msg);
85	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
103	ret = devm_scmi_process_msg(dev, &msg);
104	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
143	ret = devm_scmi_of_get_channel(dev);
144	if (ret)
145		return ret;
146
147	/* Check voltage domain is known from SCMI server */
148	in.domain_id = pdata->domain_id;
149
150	ret = devm_scmi_process_msg(dev, &scmi_msg);
151	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),
174};
175
176static int scmi_regulator_bind(struct udevice *dev)
177{
178	struct driver *drv;
179	ofnode node;
180	int ret;
181
182	drv = DM_DRIVER_GET(scmi_regulator);
183
184	ofnode_for_each_subnode(node, dev_ofnode(dev)) {
185		ret = device_bind(dev, drv, ofnode_get_name(node),
186				  NULL, node, NULL);
187		if (ret)
188			return ret;
189	}
190
191	return 0;
192}
193
194U_BOOT_DRIVER(scmi_voltage_domain) = {
195	.name = "scmi_voltage_domain",
196	.id = UCLASS_NOP,
197	.bind = scmi_regulator_bind,
198};
199