1*7c4b6f22SEtienne Carriere // SPDX-License-Identifier: GPL-2.0+
2*7c4b6f22SEtienne Carriere /*
3*7c4b6f22SEtienne Carriere * Copyright (C) 2019-2020 Linaro Limited
4*7c4b6f22SEtienne Carriere */
5*7c4b6f22SEtienne Carriere #include <common.h>
6*7c4b6f22SEtienne Carriere #include <clk-uclass.h>
7*7c4b6f22SEtienne Carriere #include <dm.h>
8*7c4b6f22SEtienne Carriere #include <scmi_agent.h>
9*7c4b6f22SEtienne Carriere #include <scmi_protocols.h>
10*7c4b6f22SEtienne Carriere #include <asm/types.h>
11*7c4b6f22SEtienne Carriere
scmi_clk_gate(struct clk * clk,int enable)12*7c4b6f22SEtienne Carriere static int scmi_clk_gate(struct clk *clk, int enable)
13*7c4b6f22SEtienne Carriere {
14*7c4b6f22SEtienne Carriere struct scmi_clk_state_in in = {
15*7c4b6f22SEtienne Carriere .clock_id = clk->id,
16*7c4b6f22SEtienne Carriere .attributes = enable,
17*7c4b6f22SEtienne Carriere };
18*7c4b6f22SEtienne Carriere struct scmi_clk_state_out out;
19*7c4b6f22SEtienne Carriere struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
20*7c4b6f22SEtienne Carriere SCMI_CLOCK_CONFIG_SET,
21*7c4b6f22SEtienne Carriere in, out);
22*7c4b6f22SEtienne Carriere int ret;
23*7c4b6f22SEtienne Carriere
24*7c4b6f22SEtienne Carriere ret = devm_scmi_process_msg(clk->dev->parent, &msg);
25*7c4b6f22SEtienne Carriere if (ret)
26*7c4b6f22SEtienne Carriere return ret;
27*7c4b6f22SEtienne Carriere
28*7c4b6f22SEtienne Carriere return scmi_to_linux_errno(out.status);
29*7c4b6f22SEtienne Carriere }
30*7c4b6f22SEtienne Carriere
scmi_clk_enable(struct clk * clk)31*7c4b6f22SEtienne Carriere static int scmi_clk_enable(struct clk *clk)
32*7c4b6f22SEtienne Carriere {
33*7c4b6f22SEtienne Carriere return scmi_clk_gate(clk, 1);
34*7c4b6f22SEtienne Carriere }
35*7c4b6f22SEtienne Carriere
scmi_clk_disable(struct clk * clk)36*7c4b6f22SEtienne Carriere static int scmi_clk_disable(struct clk *clk)
37*7c4b6f22SEtienne Carriere {
38*7c4b6f22SEtienne Carriere return scmi_clk_gate(clk, 0);
39*7c4b6f22SEtienne Carriere }
40*7c4b6f22SEtienne Carriere
scmi_clk_get_rate(struct clk * clk)41*7c4b6f22SEtienne Carriere static ulong scmi_clk_get_rate(struct clk *clk)
42*7c4b6f22SEtienne Carriere {
43*7c4b6f22SEtienne Carriere struct scmi_clk_rate_get_in in = {
44*7c4b6f22SEtienne Carriere .clock_id = clk->id,
45*7c4b6f22SEtienne Carriere };
46*7c4b6f22SEtienne Carriere struct scmi_clk_rate_get_out out;
47*7c4b6f22SEtienne Carriere struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
48*7c4b6f22SEtienne Carriere SCMI_CLOCK_RATE_GET,
49*7c4b6f22SEtienne Carriere in, out);
50*7c4b6f22SEtienne Carriere int ret;
51*7c4b6f22SEtienne Carriere
52*7c4b6f22SEtienne Carriere ret = devm_scmi_process_msg(clk->dev->parent, &msg);
53*7c4b6f22SEtienne Carriere if (ret < 0)
54*7c4b6f22SEtienne Carriere return ret;
55*7c4b6f22SEtienne Carriere
56*7c4b6f22SEtienne Carriere ret = scmi_to_linux_errno(out.status);
57*7c4b6f22SEtienne Carriere if (ret < 0)
58*7c4b6f22SEtienne Carriere return ret;
59*7c4b6f22SEtienne Carriere
60*7c4b6f22SEtienne Carriere return (ulong)(((u64)out.rate_msb << 32) | out.rate_lsb);
61*7c4b6f22SEtienne Carriere }
62*7c4b6f22SEtienne Carriere
scmi_clk_set_rate(struct clk * clk,ulong rate)63*7c4b6f22SEtienne Carriere static ulong scmi_clk_set_rate(struct clk *clk, ulong rate)
64*7c4b6f22SEtienne Carriere {
65*7c4b6f22SEtienne Carriere struct scmi_clk_rate_set_in in = {
66*7c4b6f22SEtienne Carriere .clock_id = clk->id,
67*7c4b6f22SEtienne Carriere .flags = SCMI_CLK_RATE_ROUND_CLOSEST,
68*7c4b6f22SEtienne Carriere .rate_lsb = (u32)rate,
69*7c4b6f22SEtienne Carriere .rate_msb = (u32)((u64)rate >> 32),
70*7c4b6f22SEtienne Carriere };
71*7c4b6f22SEtienne Carriere struct scmi_clk_rate_set_out out;
72*7c4b6f22SEtienne Carriere struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
73*7c4b6f22SEtienne Carriere SCMI_CLOCK_RATE_SET,
74*7c4b6f22SEtienne Carriere in, out);
75*7c4b6f22SEtienne Carriere int ret;
76*7c4b6f22SEtienne Carriere
77*7c4b6f22SEtienne Carriere ret = devm_scmi_process_msg(clk->dev->parent, &msg);
78*7c4b6f22SEtienne Carriere if (ret < 0)
79*7c4b6f22SEtienne Carriere return ret;
80*7c4b6f22SEtienne Carriere
81*7c4b6f22SEtienne Carriere ret = scmi_to_linux_errno(out.status);
82*7c4b6f22SEtienne Carriere if (ret < 0)
83*7c4b6f22SEtienne Carriere return ret;
84*7c4b6f22SEtienne Carriere
85*7c4b6f22SEtienne Carriere return scmi_clk_get_rate(clk);
86*7c4b6f22SEtienne Carriere }
87*7c4b6f22SEtienne Carriere
88*7c4b6f22SEtienne Carriere static const struct clk_ops scmi_clk_ops = {
89*7c4b6f22SEtienne Carriere .enable = scmi_clk_enable,
90*7c4b6f22SEtienne Carriere .disable = scmi_clk_disable,
91*7c4b6f22SEtienne Carriere .get_rate = scmi_clk_get_rate,
92*7c4b6f22SEtienne Carriere .set_rate = scmi_clk_set_rate,
93*7c4b6f22SEtienne Carriere };
94*7c4b6f22SEtienne Carriere
95*7c4b6f22SEtienne Carriere U_BOOT_DRIVER(scmi_clock) = {
96*7c4b6f22SEtienne Carriere .name = "scmi_clk",
97*7c4b6f22SEtienne Carriere .id = UCLASS_CLK,
98*7c4b6f22SEtienne Carriere .ops = &scmi_clk_ops,
99*7c4b6f22SEtienne Carriere };
100