| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved. |
| * Copyright 2019 NXP |
| */ |
| |
| #define LOG_CATEGORY UCLASS_CLK |
| |
| #include <common.h> |
| #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, |
| }; |