| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved. | |
| * Copyright 2019 NXP | |
| */ | |
| #define LOG_CATEGORY UCLASS_CLK | |
| #include <clk.h> | |
| #include <clk-uclass.h> | |
| #include <log.h> | |
| #include <malloc.h> | |
| #include <asm/io.h> | |
| #include <dm/device.h> | |
| #include <dm/devres.h> | |
| #include <linux/clk-provider.h> | |
| #include <linux/err.h> | |
| #include "clk.h" | |
| #define UBOOT_DM_CLK_COMPOSITE "clk_composite" | |
| static u8 clk_composite_get_parent(struct clk *clk) | |
| { | |
| struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ? | |
| (struct clk *)dev_get_clk_ptr(clk->dev) : clk); | |
| struct clk *mux = composite->mux; | |
| if (mux) | |
| return clk_mux_get_parent(mux); | |
| else | |
| return 0; | |
| } | |
| static int clk_composite_set_parent(struct clk *clk, struct clk *parent) | |
| { | |
| struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ? | |
| (struct clk *)dev_get_clk_ptr(clk->dev) : clk); | |
| const struct clk_ops *mux_ops = composite->mux_ops; | |
| struct clk *mux = composite->mux; | |
| if (!mux || !mux_ops) | |
| return -ENOSYS; | |
| return mux_ops->set_parent(mux, parent); | |
| } | |
| static unsigned long clk_composite_recalc_rate(struct clk *clk) | |
| { | |
| struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ? | |
| (struct clk *)dev_get_clk_ptr(clk->dev) : clk); | |
| const struct clk_ops *rate_ops = composite->rate_ops; | |
| struct clk *rate = composite->rate; | |
| if (rate && rate_ops) | |
| return rate_ops->get_rate(rate); | |
| else | |
| return clk_get_parent_rate(clk); | |
| } | |
| static ulong clk_composite_set_rate(struct clk *clk, unsigned long rate) | |
| { | |
| struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ? | |
| (struct clk *)dev_get_clk_ptr(clk->dev) : clk); | |
| const struct clk_ops *rate_ops = composite->rate_ops; | |
| struct clk *clk_rate = composite->rate; | |
| if (rate && rate_ops && rate_ops->set_rate) | |
| return rate_ops->set_rate(clk_rate, rate); | |
| else | |
| return clk_get_rate(clk); | |
| } | |
| static int clk_composite_enable(struct clk *clk) | |
| { | |
| struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ? | |
| (struct clk *)dev_get_clk_ptr(clk->dev) : clk); | |
| const struct clk_ops *gate_ops = composite->gate_ops; | |
| struct clk *gate = composite->gate; | |
| if (gate && gate_ops) | |
| return gate_ops->enable(gate); | |
| else | |
| return 0; | |
| } | |
| static int clk_composite_disable(struct clk *clk) | |
| { | |
| struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ? | |
| (struct clk *)dev_get_clk_ptr(clk->dev) : clk); | |
| const struct clk_ops *gate_ops = composite->gate_ops; | |
| struct clk *gate = composite->gate; | |
| if (gate && gate_ops) | |
| return gate_ops->disable(gate); | |
| else | |
| return 0; | |
| } | |
| struct clk *clk_register_composite(struct device *dev, const char *name, | |
| const char * const *parent_names, | |
| int num_parents, struct clk *mux, | |
| const struct clk_ops *mux_ops, | |
| struct clk *rate, | |
| const struct clk_ops *rate_ops, | |
| struct clk *gate, | |
| const struct clk_ops *gate_ops, | |
| unsigned long flags) | |
| { | |
| struct clk *clk; | |
| struct clk_composite *composite; | |
| int ret; | |
| if (!num_parents || (num_parents != 1 && !mux)) | |
| return ERR_PTR(-EINVAL); | |
| composite = kzalloc(sizeof(*composite), GFP_KERNEL); | |
| if (!composite) | |
| return ERR_PTR(-ENOMEM); | |
| if (mux && mux_ops) { | |
| composite->mux = mux; | |
| composite->mux_ops = mux_ops; | |
| mux->data = (ulong)composite; | |
| } | |
| if (rate && rate_ops) { | |
| if (!rate_ops->get_rate) { | |
| clk = ERR_PTR(-EINVAL); | |
| goto err; | |
| } | |
| composite->rate = rate; | |
| composite->rate_ops = rate_ops; | |
| rate->data = (ulong)composite; | |
| } | |
| if (gate && gate_ops) { | |
| if (!gate_ops->enable || !gate_ops->disable) { | |
| clk = ERR_PTR(-EINVAL); | |
| goto err; | |
| } | |
| composite->gate = gate; | |
| composite->gate_ops = gate_ops; | |
| gate->data = (ulong)composite; | |
| } | |
| clk = &composite->clk; | |
| clk->flags = flags; | |
| ret = clk_register(clk, UBOOT_DM_CLK_COMPOSITE, name, | |
| parent_names[clk_composite_get_parent(clk)]); | |
| if (ret) { | |
| clk = ERR_PTR(ret); | |
| goto err; | |
| } | |
| if (composite->mux) | |
| composite->mux->dev = clk->dev; | |
| if (composite->rate) | |
| composite->rate->dev = clk->dev; | |
| if (composite->gate) | |
| composite->gate->dev = clk->dev; | |
| return clk; | |
| err: | |
| kfree(composite); | |
| return clk; | |
| } | |
| static const struct clk_ops clk_composite_ops = { | |
| .set_parent = clk_composite_set_parent, | |
| .get_rate = clk_composite_recalc_rate, | |
| .set_rate = clk_composite_set_rate, | |
| .enable = clk_composite_enable, | |
| .disable = clk_composite_disable, | |
| }; | |
| U_BOOT_DRIVER(clk_composite) = { | |
| .name = UBOOT_DM_CLK_COMPOSITE, | |
| .id = UCLASS_CLK, | |
| .ops = &clk_composite_ops, | |
| .flags = DM_FLAG_PRE_RELOC, | |
| }; |