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