xref: /rk3399_rockchip-uboot/drivers/clk/clk_scmi.c (revision 7c4b6f223c370d11c12d314111186b35e650119a)
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