blob: a28692f39f4dbbd3e4ab81c5463e49094b319d78 [file] [log] [blame]
Etienne Carriere02fd1262020-09-09 18:44:00 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2020 Linaro Limited.
4 */
5
Patrick Delaunay5dd84d42021-02-24 11:19:44 +01006#define LOG_CATEGORY UCLASS_SCMI_AGENT
7
Etienne Carriere02fd1262020-09-09 18:44:00 +02008#include <common.h>
9#include <dm.h>
10#include <errno.h>
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +090011#include <scmi_agent.h>
Etienne Carriere02fd1262020-09-09 18:44:00 +020012#include <scmi_agent-uclass.h>
13#include <scmi_protocols.h>
Patrick Delaunayd4c9f682021-02-24 11:19:45 +010014#include <dm/device_compat.h>
Etienne Carriere02fd1262020-09-09 18:44:00 +020015#include <dm/device-internal.h>
16#include <linux/compat.h>
17
18/**
19 * struct error_code - Helper structure for SCMI error code conversion
20 * @scmi: SCMI error code
21 * @errno: Related standard error number
22 */
23struct error_code {
24 int scmi;
25 int errno;
26};
27
28static const struct error_code scmi_linux_errmap[] = {
29 { .scmi = SCMI_NOT_SUPPORTED, .errno = -EOPNOTSUPP, },
30 { .scmi = SCMI_INVALID_PARAMETERS, .errno = -EINVAL, },
31 { .scmi = SCMI_DENIED, .errno = -EACCES, },
32 { .scmi = SCMI_NOT_FOUND, .errno = -ENOENT, },
33 { .scmi = SCMI_OUT_OF_RANGE, .errno = -ERANGE, },
34 { .scmi = SCMI_BUSY, .errno = -EBUSY, },
35 { .scmi = SCMI_COMMS_ERROR, .errno = -ECOMM, },
36 { .scmi = SCMI_GENERIC_ERROR, .errno = -EIO, },
37 { .scmi = SCMI_HARDWARE_ERROR, .errno = -EREMOTEIO, },
38 { .scmi = SCMI_PROTOCOL_ERROR, .errno = -EPROTO, },
39};
40
41int scmi_to_linux_errno(s32 scmi_code)
42{
43 int n;
44
45 if (!scmi_code)
46 return 0;
47
48 for (n = 0; n < ARRAY_SIZE(scmi_linux_errmap); n++)
49 if (scmi_code == scmi_linux_errmap[n].scmi)
AKASHI Takahiroc9371542023-06-13 10:30:45 +090050 return scmi_linux_errmap[n].errno;
Etienne Carriere02fd1262020-09-09 18:44:00 +020051
52 return -EPROTO;
53}
54
55/*
56 * SCMI agent devices binds devices of various uclasses depeding on
57 * the FDT description. scmi_bind_protocol() is a generic bind sequence
58 * called by the uclass at bind stage, that is uclass post_bind.
59 */
60static int scmi_bind_protocols(struct udevice *dev)
61{
62 int ret = 0;
63 ofnode node;
Patrick Delaunaya9e97d62022-11-25 12:56:29 +010064 const char *name;
Etienne Carriere02fd1262020-09-09 18:44:00 +020065
66 dev_for_each_subnode(node, dev) {
Etienne Carriere78928e12020-09-09 18:44:04 +020067 struct driver *drv = NULL;
Etienne Carriere02fd1262020-09-09 18:44:00 +020068 u32 protocol_id;
69
Simon Glass2e4938b2022-09-06 20:27:17 -060070 if (!ofnode_is_enabled(node))
Etienne Carriere02fd1262020-09-09 18:44:00 +020071 continue;
72
73 if (ofnode_read_u32(node, "reg", &protocol_id))
74 continue;
75
Patrick Delaunaya9e97d62022-11-25 12:56:29 +010076 name = ofnode_get_name(node);
Etienne Carriere02fd1262020-09-09 18:44:00 +020077 switch (protocol_id) {
Etienne Carriere78928e12020-09-09 18:44:04 +020078 case SCMI_PROTOCOL_ID_CLOCK:
Jonas Karlman3cf87082023-04-17 19:07:18 +000079 if (CONFIG_IS_ENABLED(CLK_SCMI))
Simon Glass65130cd2020-12-28 20:34:56 -070080 drv = DM_DRIVER_GET(scmi_clock);
Etienne Carriere78928e12020-09-09 18:44:04 +020081 break;
Etienne Carrierec6e9af32020-09-09 18:44:06 +020082 case SCMI_PROTOCOL_ID_RESET_DOMAIN:
83 if (IS_ENABLED(CONFIG_RESET_SCMI))
Simon Glass65130cd2020-12-28 20:34:56 -070084 drv = DM_DRIVER_GET(scmi_reset_domain);
Etienne Carrierec6e9af32020-09-09 18:44:06 +020085 break;
Etienne Carriered68663a2021-03-08 22:38:06 +010086 case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN:
87 if (IS_ENABLED(CONFIG_DM_REGULATOR_SCMI)) {
88 node = ofnode_find_subnode(node, "regulators");
89 if (!ofnode_valid(node)) {
90 dev_err(dev, "no regulators node\n");
91 return -ENXIO;
92 }
93 drv = DM_DRIVER_GET(scmi_voltage_domain);
94 }
95 break;
Etienne Carriere02fd1262020-09-09 18:44:00 +020096 default:
Etienne Carriere78928e12020-09-09 18:44:04 +020097 break;
98 }
99
100 if (!drv) {
101 dev_dbg(dev, "Ignore unsupported SCMI protocol %#x\n",
102 protocol_id);
Etienne Carriere02fd1262020-09-09 18:44:00 +0200103 continue;
104 }
105
Patrick Delaunaya9e97d62022-11-25 12:56:29 +0100106 ret = device_bind(dev, drv, name, NULL, node, NULL);
Etienne Carriere02fd1262020-09-09 18:44:00 +0200107 if (ret)
108 break;
109 }
110
111 return ret;
112}
113
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900114static struct udevice *find_scmi_protocol_device(struct udevice *dev)
Etienne Carriere2ee64582022-05-31 18:09:20 +0200115{
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900116 struct udevice *parent = NULL, *protocol;
Etienne Carriere2ee64582022-05-31 18:09:20 +0200117
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900118 for (protocol = dev; protocol; protocol = parent) {
119 parent = dev_get_parent(protocol);
120 if (!parent ||
121 device_get_uclass_id(parent) == UCLASS_SCMI_AGENT)
122 break;
123 }
Etienne Carriere2ee64582022-05-31 18:09:20 +0200124
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900125 if (!parent) {
Etienne Carriere2ee64582022-05-31 18:09:20 +0200126 dev_err(dev, "Invalid SCMI device, agent not found\n");
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900127 return NULL;
128 }
Etienne Carriere2ee64582022-05-31 18:09:20 +0200129
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900130 return protocol;
Etienne Carriere2ee64582022-05-31 18:09:20 +0200131}
132
Etienne Carriere02fd1262020-09-09 18:44:00 +0200133static const struct scmi_agent_ops *transport_dev_ops(struct udevice *dev)
134{
135 return (const struct scmi_agent_ops *)dev->driver->ops;
136}
137
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900138/**
139 * scmi_of_get_channel() - Get SCMI channel handle
140 *
141 * @dev: SCMI agent device
142 * @channel: Output reference to the SCMI channel upon success
143 *
144 * On return, @channel will be set.
145 * Return 0 on success and a negative errno on failure
146 */
AKASHI Takahiro589ec9a2023-10-11 19:06:55 +0900147static int scmi_of_get_channel(struct udevice *dev, struct udevice *protocol,
148 struct scmi_channel **channel)
Etienne Carriere187cea12022-05-31 18:09:21 +0200149{
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900150 const struct scmi_agent_ops *ops;
Etienne Carriere187cea12022-05-31 18:09:21 +0200151
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900152 ops = transport_dev_ops(dev);
153 if (ops->of_get_channel)
AKASHI Takahiro589ec9a2023-10-11 19:06:55 +0900154 return ops->of_get_channel(dev, protocol, channel);
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900155 else
156 return -EPROTONOSUPPORT;
157}
158
159int devm_scmi_of_get_channel(struct udevice *dev)
160{
161 struct udevice *protocol;
162 struct scmi_agent_proto_priv *priv;
163 int ret;
164
165 protocol = find_scmi_protocol_device(dev);
166 if (!protocol)
Etienne Carriere187cea12022-05-31 18:09:21 +0200167 return -ENODEV;
168
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900169 priv = dev_get_parent_priv(protocol);
AKASHI Takahiro589ec9a2023-10-11 19:06:55 +0900170 ret = scmi_of_get_channel(protocol->parent, protocol, &priv->channel);
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900171 if (ret == -EPROTONOSUPPORT) {
172 /* Drivers without a get_channel operator don't need a channel ref */
173 priv->channel = NULL;
Etienne Carriere187cea12022-05-31 18:09:21 +0200174
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900175 return 0;
176 }
Etienne Carriere187cea12022-05-31 18:09:21 +0200177
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900178 return ret;
Etienne Carriere187cea12022-05-31 18:09:21 +0200179}
180
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900181/**
182 * scmi_process_msg() - Send and process an SCMI message
183 *
184 * Send a message to an SCMI server.
185 * Caller sets scmi_msg::out_msg_sz to the output message buffer size.
186 *
187 * @dev: SCMI agent device
188 * @channel: Communication channel for the device
189 * @msg: Message structure reference
190 *
191 * On return, scmi_msg::out_msg_sz stores the response payload size.
192 * Return: 0 on success and a negative errno on failure
193 */
194static int scmi_process_msg(struct udevice *dev, struct scmi_channel *channel,
195 struct scmi_msg *msg)
Etienne Carriere02fd1262020-09-09 18:44:00 +0200196{
Etienne Carriere2f26c042022-02-21 09:22:40 +0100197 const struct scmi_agent_ops *ops;
Etienne Carriere2f26c042022-02-21 09:22:40 +0100198
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900199 ops = transport_dev_ops(dev);
200 if (ops->process_msg)
201 return ops->process_msg(dev, channel, msg);
202 else
203 return -EPROTONOSUPPORT;
204}
Etienne Carriere2f26c042022-02-21 09:22:40 +0100205
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900206int devm_scmi_process_msg(struct udevice *dev, struct scmi_msg *msg)
207{
208 struct udevice *protocol;
209 struct scmi_agent_proto_priv *priv;
Etienne Carriere02fd1262020-09-09 18:44:00 +0200210
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900211 protocol = find_scmi_protocol_device(dev);
212 if (!protocol)
213 return -ENODEV;
214
215 priv = dev_get_parent_priv(protocol);
Etienne Carriere02fd1262020-09-09 18:44:00 +0200216
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900217 return scmi_process_msg(protocol->parent, priv->channel, msg);
Etienne Carriere02fd1262020-09-09 18:44:00 +0200218}
219
220UCLASS_DRIVER(scmi_agent) = {
221 .id = UCLASS_SCMI_AGENT,
222 .name = "scmi_agent",
223 .post_bind = scmi_bind_protocols,
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900224 .per_child_auto = sizeof(struct scmi_agent_proto_priv),
Etienne Carriere02fd1262020-09-09 18:44:00 +0200225};