// SPDX-License-Identifier: GPL-2.0+ /* * TI multiplexer clock support * * Copyright (C) 2020 Dario Binacchi * * Based on Linux kernel drivers/clk/ti/mux.c */ #include #include #include #include #include #include #include "clk.h" struct clk_ti_mux_priv { struct clk_bulk parents; struct clk_ti_reg reg; u32 flags; u32 mux_flags; u32 mask; u32 shift; s32 latch; }; static struct clk *clk_ti_mux_get_parent_by_index(struct clk_bulk *parents, int index) { if (index < 0 || !parents) return ERR_PTR(-EINVAL); if (index >= parents->count) return ERR_PTR(-ENODEV); return &parents->clks[index]; } static int clk_ti_mux_get_parent_index(struct clk_bulk *parents, struct clk *parent) { int i; if (!parents || !parent) return -EINVAL; for (i = 0; i < parents->count; i++) { if (parents->clks[i].dev == parent->dev) return i; } return -ENODEV; } static int clk_ti_mux_get_index(struct clk *clk) { struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev); u32 val; val = clk_ti_readl(&priv->reg); val >>= priv->shift; val &= priv->mask; if (val && (priv->flags & CLK_MUX_INDEX_BIT)) val = ffs(val) - 1; if (val && (priv->flags & CLK_MUX_INDEX_ONE)) val--; if (val >= priv->parents.count) return -EINVAL; return val; } static int clk_ti_mux_set_parent(struct clk *clk, struct clk *parent) { struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev); int index; u32 val; index = clk_ti_mux_get_parent_index(&priv->parents, parent); if (index < 0) { dev_err(clk->dev, "failed to get parent clock\n"); return index; } index = clk_mux_index_to_val(NULL, priv->flags, index); if (priv->flags & CLK_MUX_HIWORD_MASK) { val = priv->mask << (priv->shift + 16); } else { val = clk_ti_readl(&priv->reg); val &= ~(priv->mask << priv->shift); } val |= index << priv->shift; clk_ti_writel(val, &priv->reg); clk_ti_latch(&priv->reg, priv->latch); return 0; } static ulong clk_ti_mux_set_rate(struct clk *clk, ulong rate) { struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev); struct clk *parent; int index; if ((clk->flags & CLK_SET_RATE_PARENT) == 0) return -ENOSYS; index = clk_ti_mux_get_index(clk); parent = clk_ti_mux_get_parent_by_index(&priv->parents, index); if (IS_ERR(parent)) return PTR_ERR(parent); rate = clk_set_rate(parent, rate); dev_dbg(clk->dev, "rate=%ld\n", rate); return rate; } static ulong clk_ti_mux_get_rate(struct clk *clk) { struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev); int index; struct clk *parent; ulong rate; index = clk_ti_mux_get_index(clk); parent = clk_ti_mux_get_parent_by_index(&priv->parents, index); if (IS_ERR(parent)) return PTR_ERR(parent); rate = clk_get_rate(parent); dev_dbg(clk->dev, "rate=%ld\n", rate); return rate; } static ulong clk_ti_mux_round_rate(struct clk *clk, ulong rate) { struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev); struct clk *parent; int index; if ((clk->flags & CLK_SET_RATE_PARENT) == 0) return -ENOSYS; index = clk_ti_mux_get_index(clk); parent = clk_ti_mux_get_parent_by_index(&priv->parents, index); if (IS_ERR(parent)) return PTR_ERR(parent); rate = clk_round_rate(parent, rate); dev_dbg(clk->dev, "rate=%ld\n", rate); return rate; } static int clk_ti_mux_request(struct clk *clk) { struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev); struct clk *parent; int index; clk->flags = priv->flags; index = clk_ti_mux_get_index(clk); parent = clk_ti_mux_get_parent_by_index(&priv->parents, index); if (IS_ERR(parent)) return PTR_ERR(parent); return clk_ti_mux_set_parent(clk, parent); } static struct clk_ops clk_ti_mux_ops = { .request = clk_ti_mux_request, .round_rate = clk_ti_mux_round_rate, .get_rate = clk_ti_mux_get_rate, .set_rate = clk_ti_mux_set_rate, .set_parent = clk_ti_mux_set_parent, }; static int clk_ti_mux_remove(struct udevice *dev) { struct clk_ti_mux_priv *priv = dev_get_priv(dev); int err; err = clk_release_all(priv->parents.clks, priv->parents.count); if (err) dev_dbg(dev, "could not release all parents' clocks\n"); return err; } static int clk_ti_mux_probe(struct udevice *dev) { struct clk_ti_mux_priv *priv = dev_get_priv(dev); int err; err = clk_get_bulk(dev, &priv->parents); if (err || priv->parents.count < 2) { dev_err(dev, "mux-clock must have parents\n"); return err ? err : -EFAULT; } /* Generate bit-mask based on parents info */ priv->mask = priv->parents.count; if (!(priv->mux_flags & CLK_MUX_INDEX_ONE)) priv->mask--; priv->mask = (1 << fls(priv->mask)) - 1; return 0; } static int clk_ti_mux_of_to_plat(struct udevice *dev) { struct clk_ti_mux_priv *priv = dev_get_priv(dev); int err; err = clk_ti_get_reg_addr(dev, 0, &priv->reg); if (err) { dev_err(dev, "failed to get register address\n"); return err; } priv->shift = dev_read_u32_default(dev, "ti,bit-shift", 0); priv->latch = dev_read_s32_default(dev, "ti,latch-bit", -EINVAL); priv->flags = CLK_SET_RATE_NO_REPARENT; if (dev_read_bool(dev, "ti,set-rate-parent")) priv->flags |= CLK_SET_RATE_PARENT; if (dev_read_bool(dev, "ti,index-starts-at-one")) priv->mux_flags |= CLK_MUX_INDEX_ONE; return 0; } static const struct udevice_id clk_ti_mux_of_match[] = { {.compatible = "ti,mux-clock"}, {}, }; U_BOOT_DRIVER(clk_ti_mux) = { .name = "ti_mux_clock", .id = UCLASS_CLK, .of_match = clk_ti_mux_of_match, .of_to_plat = clk_ti_mux_of_to_plat, .probe = clk_ti_mux_probe, .remove = clk_ti_mux_remove, .priv_auto = sizeof(struct clk_ti_mux_priv), .ops = &clk_ti_mux_ops, };