|
@@ -11,6 +11,52 @@
|
|
|
#include <scmi_agent.h>
|
|
|
#include <scmi_protocols.h>
|
|
|
#include <asm/types.h>
|
|
|
+#include <linux/clk-provider.h>
|
|
|
+
|
|
|
+static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks)
|
|
|
+{
|
|
|
+ struct scmi_clk_protocol_attr_out out;
|
|
|
+ struct scmi_msg msg = {
|
|
|
+ .protocol_id = SCMI_PROTOCOL_ID_CLOCK,
|
|
|
+ .message_id = SCMI_PROTOCOL_ATTRIBUTES,
|
|
|
+ .out_msg = (u8 *)&out,
|
|
|
+ .out_msg_sz = sizeof(out),
|
|
|
+ };
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = devm_scmi_process_msg(dev, &msg);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ *num_clocks = out.attributes & SCMI_CLK_PROTO_ATTR_COUNT_MASK;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name)
|
|
|
+{
|
|
|
+ struct scmi_clk_attribute_in in = {
|
|
|
+ .clock_id = clkid,
|
|
|
+ };
|
|
|
+ struct scmi_clk_attribute_out out;
|
|
|
+ struct scmi_msg msg = {
|
|
|
+ .protocol_id = SCMI_PROTOCOL_ID_CLOCK,
|
|
|
+ .message_id = SCMI_CLOCK_ATTRIBUTES,
|
|
|
+ .in_msg = (u8 *)&in,
|
|
|
+ .in_msg_sz = sizeof(in),
|
|
|
+ .out_msg = (u8 *)&out,
|
|
|
+ .out_msg_sz = sizeof(out),
|
|
|
+ };
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = devm_scmi_process_msg(dev, &msg);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ *name = out.clock_name;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
static int scmi_clk_gate(struct clk *clk, int enable)
|
|
|
{
|
|
@@ -88,6 +134,49 @@ static ulong scmi_clk_set_rate(struct clk *clk, ulong rate)
|
|
|
return scmi_clk_get_rate(clk);
|
|
|
}
|
|
|
|
|
|
+static int scmi_clk_probe(struct udevice *dev)
|
|
|
+{
|
|
|
+ struct clk *clk;
|
|
|
+ size_t num_clocks, i;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (!CONFIG_IS_ENABLED(CLK_CCF))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* register CCF children: CLK UCLASS, no probed again */
|
|
|
+ if (device_get_uclass_id(dev->parent) == UCLASS_CLK)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ ret = scmi_clk_get_num_clock(dev, &num_clocks);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ for (i = 0; i < num_clocks; i++) {
|
|
|
+ char *name;
|
|
|
+
|
|
|
+ if (!scmi_clk_get_attibute(dev, i, &name)) {
|
|
|
+ char *clock_name = strdup(name);
|
|
|
+
|
|
|
+ clk = kzalloc(sizeof(*clk), GFP_KERNEL);
|
|
|
+ if (!clk || !clock_name)
|
|
|
+ ret = -ENOMEM;
|
|
|
+ else
|
|
|
+ ret = clk_register(clk, dev->driver->name,
|
|
|
+ clock_name, dev->name);
|
|
|
+
|
|
|
+ if (ret) {
|
|
|
+ free(clk);
|
|
|
+ free(clock_name);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ clk_dm(i, clk);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static const struct clk_ops scmi_clk_ops = {
|
|
|
.enable = scmi_clk_enable,
|
|
|
.disable = scmi_clk_disable,
|
|
@@ -99,4 +188,5 @@ U_BOOT_DRIVER(scmi_clock) = {
|
|
|
.name = "scmi_clk",
|
|
|
.id = UCLASS_CLK,
|
|
|
.ops = &scmi_clk_ops,
|
|
|
+ .probe = &scmi_clk_probe,
|
|
|
};
|