blob: d172fed24c9d93cedbb66e200af8087d2fdf97b8 [file] [log] [blame]
Etienne Carriere60388842020-09-09 18:44:04 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
Etienne Carriere38a905e2022-05-31 18:09:25 +02003 * Copyright (C) 2019-2022 Linaro Limited
Etienne Carriere60388842020-09-09 18:44:04 +02004 */
Patrick Delaunay31dc56f2021-10-28 19:13:13 +02005
6#define LOG_CATEGORY UCLASS_CLK
7
Etienne Carriere60388842020-09-09 18:44:04 +02008#include <common.h>
9#include <clk-uclass.h>
10#include <dm.h>
11#include <scmi_agent.h>
12#include <scmi_protocols.h>
13#include <asm/types.h>
Etienne Carriere7c33f782022-02-21 09:22:42 +010014#include <linux/clk-provider.h>
15
Etienne Carriere38a905e2022-05-31 18:09:25 +020016/**
17 * struct scmi_clk_priv - Private data for SCMI clocks
18 * @channel: Reference to the SCMI channel to use
19 */
20struct scmi_clk_priv {
21 struct scmi_channel *channel;
22};
23
Etienne Carriere7c33f782022-02-21 09:22:42 +010024static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks)
25{
Etienne Carriere38a905e2022-05-31 18:09:25 +020026 struct scmi_clk_priv *priv = dev_get_priv(dev);
Etienne Carriere7c33f782022-02-21 09:22:42 +010027 struct scmi_clk_protocol_attr_out out;
28 struct scmi_msg msg = {
29 .protocol_id = SCMI_PROTOCOL_ID_CLOCK,
30 .message_id = SCMI_PROTOCOL_ATTRIBUTES,
31 .out_msg = (u8 *)&out,
32 .out_msg_sz = sizeof(out),
33 };
34 int ret;
35
Etienne Carriere38a905e2022-05-31 18:09:25 +020036 ret = devm_scmi_process_msg(dev, priv->channel, &msg);
Etienne Carriere7c33f782022-02-21 09:22:42 +010037 if (ret)
38 return ret;
39
40 *num_clocks = out.attributes & SCMI_CLK_PROTO_ATTR_COUNT_MASK;
41
42 return 0;
43}
44
45static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name)
46{
Etienne Carriere38a905e2022-05-31 18:09:25 +020047 struct scmi_clk_priv *priv = dev_get_priv(dev);
Etienne Carriere7c33f782022-02-21 09:22:42 +010048 struct scmi_clk_attribute_in in = {
49 .clock_id = clkid,
50 };
51 struct scmi_clk_attribute_out out;
52 struct scmi_msg msg = {
53 .protocol_id = SCMI_PROTOCOL_ID_CLOCK,
54 .message_id = SCMI_CLOCK_ATTRIBUTES,
55 .in_msg = (u8 *)&in,
56 .in_msg_sz = sizeof(in),
57 .out_msg = (u8 *)&out,
58 .out_msg_sz = sizeof(out),
59 };
60 int ret;
61
Etienne Carriere38a905e2022-05-31 18:09:25 +020062 ret = devm_scmi_process_msg(dev, priv->channel, &msg);
Etienne Carriere7c33f782022-02-21 09:22:42 +010063 if (ret)
64 return ret;
65
Heinrich Schuchardtd6577662022-04-26 23:26:31 +020066 *name = strdup(out.clock_name);
Etienne Carriere7c33f782022-02-21 09:22:42 +010067
68 return 0;
69}
Etienne Carriere60388842020-09-09 18:44:04 +020070
71static int scmi_clk_gate(struct clk *clk, int enable)
72{
Etienne Carriere38a905e2022-05-31 18:09:25 +020073 struct scmi_clk_priv *priv = dev_get_priv(clk->dev);
Etienne Carriere60388842020-09-09 18:44:04 +020074 struct scmi_clk_state_in in = {
75 .clock_id = clk->id,
76 .attributes = enable,
77 };
78 struct scmi_clk_state_out out;
79 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
80 SCMI_CLOCK_CONFIG_SET,
81 in, out);
82 int ret;
83
Etienne Carriere38a905e2022-05-31 18:09:25 +020084 ret = devm_scmi_process_msg(clk->dev, priv->channel, &msg);
Etienne Carriere60388842020-09-09 18:44:04 +020085 if (ret)
86 return ret;
87
88 return scmi_to_linux_errno(out.status);
89}
90
91static int scmi_clk_enable(struct clk *clk)
92{
93 return scmi_clk_gate(clk, 1);
94}
95
96static int scmi_clk_disable(struct clk *clk)
97{
98 return scmi_clk_gate(clk, 0);
99}
100
101static ulong scmi_clk_get_rate(struct clk *clk)
102{
Etienne Carriere38a905e2022-05-31 18:09:25 +0200103 struct scmi_clk_priv *priv = dev_get_priv(clk->dev);
Etienne Carriere60388842020-09-09 18:44:04 +0200104 struct scmi_clk_rate_get_in in = {
105 .clock_id = clk->id,
106 };
107 struct scmi_clk_rate_get_out out;
108 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
109 SCMI_CLOCK_RATE_GET,
110 in, out);
111 int ret;
112
Etienne Carriere38a905e2022-05-31 18:09:25 +0200113 ret = devm_scmi_process_msg(clk->dev, priv->channel, &msg);
Etienne Carriere60388842020-09-09 18:44:04 +0200114 if (ret < 0)
115 return ret;
116
117 ret = scmi_to_linux_errno(out.status);
118 if (ret < 0)
119 return ret;
120
121 return (ulong)(((u64)out.rate_msb << 32) | out.rate_lsb);
122}
123
124static ulong scmi_clk_set_rate(struct clk *clk, ulong rate)
125{
Etienne Carriere38a905e2022-05-31 18:09:25 +0200126 struct scmi_clk_priv *priv = dev_get_priv(clk->dev);
Etienne Carriere60388842020-09-09 18:44:04 +0200127 struct scmi_clk_rate_set_in in = {
128 .clock_id = clk->id,
129 .flags = SCMI_CLK_RATE_ROUND_CLOSEST,
130 .rate_lsb = (u32)rate,
131 .rate_msb = (u32)((u64)rate >> 32),
132 };
133 struct scmi_clk_rate_set_out out;
134 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
135 SCMI_CLOCK_RATE_SET,
136 in, out);
137 int ret;
138
Etienne Carriere38a905e2022-05-31 18:09:25 +0200139 ret = devm_scmi_process_msg(clk->dev, priv->channel, &msg);
Etienne Carriere60388842020-09-09 18:44:04 +0200140 if (ret < 0)
141 return ret;
142
143 ret = scmi_to_linux_errno(out.status);
144 if (ret < 0)
145 return ret;
146
147 return scmi_clk_get_rate(clk);
148}
149
Etienne Carriere7c33f782022-02-21 09:22:42 +0100150static int scmi_clk_probe(struct udevice *dev)
151{
Etienne Carriere38a905e2022-05-31 18:09:25 +0200152 struct scmi_clk_priv *priv = dev_get_priv(dev);
Etienne Carriere7c33f782022-02-21 09:22:42 +0100153 struct clk *clk;
154 size_t num_clocks, i;
155 int ret;
156
Etienne Carriere38a905e2022-05-31 18:09:25 +0200157 ret = devm_scmi_of_get_channel(dev, &priv->channel);
158 if (ret)
159 return ret;
160
Etienne Carriere7c33f782022-02-21 09:22:42 +0100161 if (!CONFIG_IS_ENABLED(CLK_CCF))
162 return 0;
163
164 /* register CCF children: CLK UCLASS, no probed again */
165 if (device_get_uclass_id(dev->parent) == UCLASS_CLK)
166 return 0;
167
168 ret = scmi_clk_get_num_clock(dev, &num_clocks);
169 if (ret)
170 return ret;
171
172 for (i = 0; i < num_clocks; i++) {
Heinrich Schuchardtd6577662022-04-26 23:26:31 +0200173 char *clock_name;
Etienne Carriere7c33f782022-02-21 09:22:42 +0100174
Heinrich Schuchardtd6577662022-04-26 23:26:31 +0200175 if (!scmi_clk_get_attibute(dev, i, &clock_name)) {
Etienne Carriere7c33f782022-02-21 09:22:42 +0100176 clk = kzalloc(sizeof(*clk), GFP_KERNEL);
177 if (!clk || !clock_name)
178 ret = -ENOMEM;
179 else
180 ret = clk_register(clk, dev->driver->name,
181 clock_name, dev->name);
182
183 if (ret) {
184 free(clk);
185 free(clock_name);
186 return ret;
187 }
188
189 clk_dm(i, clk);
190 }
191 }
192
193 return 0;
194}
195
Etienne Carriere60388842020-09-09 18:44:04 +0200196static const struct clk_ops scmi_clk_ops = {
197 .enable = scmi_clk_enable,
198 .disable = scmi_clk_disable,
199 .get_rate = scmi_clk_get_rate,
200 .set_rate = scmi_clk_set_rate,
201};
202
203U_BOOT_DRIVER(scmi_clock) = {
204 .name = "scmi_clk",
205 .id = UCLASS_CLK,
206 .ops = &scmi_clk_ops,
Etienne Carriere38a905e2022-05-31 18:09:25 +0200207 .probe = scmi_clk_probe,
208 .priv_auto = sizeof(struct scmi_clk_priv *),
Etienne Carriere60388842020-09-09 18:44:04 +0200209};