| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Copyright (C) 2019-2020 Linaro Limited |
| */ |
| #include <common.h> |
| #include <clk-uclass.h> |
| #include <dm.h> |
| #include <scmi_agent.h> |
| #include <scmi_protocols.h> |
| #include <asm/types.h> |
| |
| static int scmi_clk_gate(struct clk *clk, int enable) |
| { |
| struct scmi_clk_state_in in = { |
| .clock_id = clk->id, |
| .attributes = enable, |
| }; |
| struct scmi_clk_state_out out; |
| struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, |
| SCMI_CLOCK_CONFIG_SET, |
| in, out); |
| int ret; |
| |
| ret = devm_scmi_process_msg(clk->dev->parent, &msg); |
| if (ret) |
| return ret; |
| |
| return scmi_to_linux_errno(out.status); |
| } |
| |
| static int scmi_clk_enable(struct clk *clk) |
| { |
| return scmi_clk_gate(clk, 1); |
| } |
| |
| static int scmi_clk_disable(struct clk *clk) |
| { |
| return scmi_clk_gate(clk, 0); |
| } |
| |
| static ulong scmi_clk_get_rate(struct clk *clk) |
| { |
| struct scmi_clk_rate_get_in in = { |
| .clock_id = clk->id, |
| }; |
| struct scmi_clk_rate_get_out out; |
| struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, |
| SCMI_CLOCK_RATE_GET, |
| in, out); |
| int ret; |
| |
| ret = devm_scmi_process_msg(clk->dev->parent, &msg); |
| if (ret < 0) |
| return ret; |
| |
| ret = scmi_to_linux_errno(out.status); |
| if (ret < 0) |
| return ret; |
| |
| return (ulong)(((u64)out.rate_msb << 32) | out.rate_lsb); |
| } |
| |
| static ulong scmi_clk_set_rate(struct clk *clk, ulong rate) |
| { |
| struct scmi_clk_rate_set_in in = { |
| .clock_id = clk->id, |
| .flags = SCMI_CLK_RATE_ROUND_CLOSEST, |
| .rate_lsb = (u32)rate, |
| .rate_msb = (u32)((u64)rate >> 32), |
| }; |
| struct scmi_clk_rate_set_out out; |
| struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, |
| SCMI_CLOCK_RATE_SET, |
| in, out); |
| int ret; |
| |
| ret = devm_scmi_process_msg(clk->dev->parent, &msg); |
| if (ret < 0) |
| return ret; |
| |
| ret = scmi_to_linux_errno(out.status); |
| if (ret < 0) |
| return ret; |
| |
| return scmi_clk_get_rate(clk); |
| } |
| |
| static const struct clk_ops scmi_clk_ops = { |
| .enable = scmi_clk_enable, |
| .disable = scmi_clk_disable, |
| .get_rate = scmi_clk_get_rate, |
| .set_rate = scmi_clk_set_rate, |
| }; |
| |
| U_BOOT_DRIVER(scmi_clock) = { |
| .name = "scmi_clk", |
| .id = UCLASS_CLK, |
| .ops = &scmi_clk_ops, |
| }; |