1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (C) 2019-2022 Linaro Limited 4 */ 5 6#define LOG_CATEGORY UCLASS_CLK 7 8#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> 14#include <linux/clk-provider.h> 15 16static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks) 17{ 18 struct scmi_clk_protocol_attr_out out; 19 struct scmi_msg msg = { 20 .protocol_id = SCMI_PROTOCOL_ID_CLOCK, 21 .message_id = SCMI_PROTOCOL_ATTRIBUTES, 22 .out_msg = (u8 *)&out, 23 .out_msg_sz = sizeof(out), 24 }; 25 int ret; 26 27 ret = devm_scmi_process_msg(dev, &msg); 28 if (ret) 29 return ret; 30 31 *num_clocks = out.attributes & SCMI_CLK_PROTO_ATTR_COUNT_MASK; 32 33 return 0; 34} 35 36static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name) 37{ 38 struct scmi_clk_attribute_in in = { 39 .clock_id = clkid, 40 }; 41 struct scmi_clk_attribute_out out; 42 struct scmi_msg msg = { 43 .protocol_id = SCMI_PROTOCOL_ID_CLOCK, 44 .message_id = SCMI_CLOCK_ATTRIBUTES, 45 .in_msg = (u8 *)&in, 46 .in_msg_sz = sizeof(in), 47 .out_msg = (u8 *)&out, 48 .out_msg_sz = sizeof(out), 49 }; 50 int ret; 51 52 ret = devm_scmi_process_msg(dev, &msg); 53 if (ret) 54 return ret; 55 56 *name = strdup(out.clock_name); 57 58 return 0; 59} 60 61static int scmi_clk_gate(struct clk *clk, int enable) 62{ 63 struct scmi_clk_state_in in = { 64 .clock_id = clk->id, 65 .attributes = enable, 66 }; 67 struct scmi_clk_state_out out; 68 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, 69 SCMI_CLOCK_CONFIG_SET, 70 in, out); 71 int ret; 72 73 ret = devm_scmi_process_msg(clk->dev, &msg); 74 if (ret) 75 return ret; 76 77 return scmi_to_linux_errno(out.status); 78} 79 80static int scmi_clk_enable(struct clk *clk) 81{ 82 return scmi_clk_gate(clk, 1); 83} 84 85static int scmi_clk_disable(struct clk *clk) 86{ 87 return scmi_clk_gate(clk, 0); 88} 89 90static ulong scmi_clk_get_rate(struct clk *clk) 91{ 92 struct scmi_clk_rate_get_in in = { 93 .clock_id = clk->id, 94 }; 95 struct scmi_clk_rate_get_out out; 96 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, 97 SCMI_CLOCK_RATE_GET, 98 in, out); 99 int ret; 100 101 ret = devm_scmi_process_msg(clk->dev, &msg); 102 if (ret < 0) 103 return ret; 104 105 ret = scmi_to_linux_errno(out.status); 106 if (ret < 0) 107 return ret; 108 109 return (ulong)(((u64)out.rate_msb << 32) | out.rate_lsb); 110} 111 112static ulong scmi_clk_set_rate(struct clk *clk, ulong rate) 113{ 114 struct scmi_clk_rate_set_in in = { 115 .clock_id = clk->id, 116 .flags = SCMI_CLK_RATE_ROUND_CLOSEST, 117 .rate_lsb = (u32)rate, 118 .rate_msb = (u32)((u64)rate >> 32), 119 }; 120 struct scmi_clk_rate_set_out out; 121 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, 122 SCMI_CLOCK_RATE_SET, 123 in, out); 124 int ret; 125 126 ret = devm_scmi_process_msg(clk->dev, &msg); 127 if (ret < 0) 128 return ret; 129 130 ret = scmi_to_linux_errno(out.status); 131 if (ret < 0) 132 return ret; 133 134 return scmi_clk_get_rate(clk); 135} 136 137static int scmi_clk_probe(struct udevice *dev) 138{ 139 struct clk *clk; 140 size_t num_clocks, i; 141 int ret; 142 143 ret = devm_scmi_of_get_channel(dev); 144 if (ret) 145 return ret; 146 147 if (!CONFIG_IS_ENABLED(CLK_CCF)) 148 return 0; 149 150 /* register CCF children: CLK UCLASS, no probed again */ 151 if (device_get_uclass_id(dev->parent) == UCLASS_CLK) 152 return 0; 153 154 ret = scmi_clk_get_num_clock(dev, &num_clocks); 155 if (ret) 156 return ret; 157 158 for (i = 0; i < num_clocks; i++) { 159 char *clock_name; 160 161 if (!scmi_clk_get_attibute(dev, i, &clock_name)) { 162 clk = kzalloc(sizeof(*clk), GFP_KERNEL); 163 if (!clk || !clock_name) 164 ret = -ENOMEM; 165 else 166 ret = clk_register(clk, dev->driver->name, 167 clock_name, dev->name); 168 169 if (ret) { 170 free(clk); 171 free(clock_name); 172 return ret; 173 } 174 175 clk_dm(i, clk); 176 } 177 } 178 179 return 0; 180} 181 182static const struct clk_ops scmi_clk_ops = { 183 .enable = scmi_clk_enable, 184 .disable = scmi_clk_disable, 185 .get_rate = scmi_clk_get_rate, 186 .set_rate = scmi_clk_set_rate, 187}; 188 189U_BOOT_DRIVER(scmi_clock) = { 190 .name = "scmi_clk", 191 .id = UCLASS_CLK, 192 .ops = &scmi_clk_ops, 193 .probe = scmi_clk_probe, 194}; 195