uboot/drivers/clk/ti/clk-mux.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * TI multiplexer clock support
   4 *
   5 * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
   6 *
   7 * Based on Linux kernel drivers/clk/ti/mux.c
   8 */
   9
  10#include <common.h>
  11#include <dm.h>
  12#include <dm/device_compat.h>
  13#include <clk-uclass.h>
  14#include <asm/io.h>
  15#include <linux/clk-provider.h>
  16#include "clk.h"
  17
  18struct clk_ti_mux_priv {
  19        struct clk_bulk parents;
  20        struct clk_ti_reg reg;
  21        u32 flags;
  22        u32 mux_flags;
  23        u32 mask;
  24        u32 shift;
  25        s32 latch;
  26};
  27
  28static struct clk *clk_ti_mux_get_parent_by_index(struct clk_bulk *parents,
  29                                                  int index)
  30{
  31        if (index < 0 || !parents)
  32                return ERR_PTR(-EINVAL);
  33
  34        if (index >= parents->count)
  35                return ERR_PTR(-ENODEV);
  36
  37        return &parents->clks[index];
  38}
  39
  40static int clk_ti_mux_get_parent_index(struct clk_bulk *parents,
  41                                       struct clk *parent)
  42{
  43        int i;
  44
  45        if (!parents || !parent)
  46                return -EINVAL;
  47
  48        for (i = 0; i < parents->count; i++) {
  49                if (parents->clks[i].dev == parent->dev)
  50                        return i;
  51        }
  52
  53        return -ENODEV;
  54}
  55
  56static int clk_ti_mux_get_index(struct clk *clk)
  57{
  58        struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
  59        u32 val;
  60
  61        val = clk_ti_readl(&priv->reg);
  62        val >>= priv->shift;
  63        val &= priv->mask;
  64
  65        if (val && (priv->flags & CLK_MUX_INDEX_BIT))
  66                val = ffs(val) - 1;
  67
  68        if (val && (priv->flags & CLK_MUX_INDEX_ONE))
  69                val--;
  70
  71        if (val >= priv->parents.count)
  72                return -EINVAL;
  73
  74        return val;
  75}
  76
  77static int clk_ti_mux_set_parent(struct clk *clk, struct clk *parent)
  78{
  79        struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
  80        int index;
  81        u32 val;
  82
  83        index = clk_ti_mux_get_parent_index(&priv->parents, parent);
  84        if (index < 0) {
  85                dev_err(clk->dev, "failed to get parent clock\n");
  86                return index;
  87        }
  88
  89        index = clk_mux_index_to_val(NULL, priv->flags, index);
  90
  91        if (priv->flags & CLK_MUX_HIWORD_MASK) {
  92                val = priv->mask << (priv->shift + 16);
  93        } else {
  94                val = clk_ti_readl(&priv->reg);
  95                val &= ~(priv->mask << priv->shift);
  96        }
  97
  98        val |= index << priv->shift;
  99        clk_ti_writel(val, &priv->reg);
 100        clk_ti_latch(&priv->reg, priv->latch);
 101        return 0;
 102}
 103
 104static ulong clk_ti_mux_set_rate(struct clk *clk, ulong rate)
 105{
 106        struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
 107        struct clk *parent;
 108        int index;
 109
 110        if ((clk->flags & CLK_SET_RATE_PARENT) == 0)
 111                return -ENOSYS;
 112
 113        index = clk_ti_mux_get_index(clk);
 114        parent = clk_ti_mux_get_parent_by_index(&priv->parents, index);
 115        if (IS_ERR(parent))
 116                return PTR_ERR(parent);
 117
 118        rate = clk_set_rate(parent, rate);
 119        dev_dbg(clk->dev, "rate=%ld\n", rate);
 120        return rate;
 121}
 122
 123static ulong clk_ti_mux_get_rate(struct clk *clk)
 124{
 125        struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
 126        int index;
 127        struct clk *parent;
 128        ulong rate;
 129
 130        index = clk_ti_mux_get_index(clk);
 131        parent = clk_ti_mux_get_parent_by_index(&priv->parents, index);
 132        if (IS_ERR(parent))
 133                return PTR_ERR(parent);
 134
 135        rate = clk_get_rate(parent);
 136        dev_dbg(clk->dev, "rate=%ld\n", rate);
 137        return rate;
 138}
 139
 140static ulong clk_ti_mux_round_rate(struct clk *clk, ulong rate)
 141{
 142        struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
 143        struct clk *parent;
 144        int index;
 145
 146        if ((clk->flags & CLK_SET_RATE_PARENT) == 0)
 147                return -ENOSYS;
 148
 149        index = clk_ti_mux_get_index(clk);
 150        parent = clk_ti_mux_get_parent_by_index(&priv->parents, index);
 151        if (IS_ERR(parent))
 152                return PTR_ERR(parent);
 153
 154        rate = clk_round_rate(parent, rate);
 155        dev_dbg(clk->dev, "rate=%ld\n", rate);
 156        return rate;
 157}
 158
 159static int clk_ti_mux_request(struct clk *clk)
 160{
 161        struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
 162        struct clk *parent;
 163        int index;
 164
 165        clk->flags = priv->flags;
 166
 167        index = clk_ti_mux_get_index(clk);
 168        parent = clk_ti_mux_get_parent_by_index(&priv->parents, index);
 169        if (IS_ERR(parent))
 170                return PTR_ERR(parent);
 171
 172        return clk_ti_mux_set_parent(clk, parent);
 173}
 174
 175static struct clk_ops clk_ti_mux_ops = {
 176        .request = clk_ti_mux_request,
 177        .round_rate = clk_ti_mux_round_rate,
 178        .get_rate = clk_ti_mux_get_rate,
 179        .set_rate = clk_ti_mux_set_rate,
 180        .set_parent = clk_ti_mux_set_parent,
 181};
 182
 183static int clk_ti_mux_remove(struct udevice *dev)
 184{
 185        struct clk_ti_mux_priv *priv = dev_get_priv(dev);
 186        int err;
 187
 188        err = clk_release_all(priv->parents.clks, priv->parents.count);
 189        if (err)
 190                dev_dbg(dev, "could not release all parents' clocks\n");
 191
 192        return err;
 193}
 194
 195static int clk_ti_mux_probe(struct udevice *dev)
 196{
 197        struct clk_ti_mux_priv *priv = dev_get_priv(dev);
 198        int err;
 199
 200        err = clk_get_bulk(dev, &priv->parents);
 201        if (err || priv->parents.count < 2) {
 202                dev_err(dev, "mux-clock must have parents\n");
 203                return err ? err : -EFAULT;
 204        }
 205
 206        /* Generate bit-mask based on parents info */
 207        priv->mask = priv->parents.count;
 208        if (!(priv->mux_flags & CLK_MUX_INDEX_ONE))
 209                priv->mask--;
 210
 211        priv->mask = (1 << fls(priv->mask)) - 1;
 212        return 0;
 213}
 214
 215static int clk_ti_mux_of_to_plat(struct udevice *dev)
 216{
 217        struct clk_ti_mux_priv *priv = dev_get_priv(dev);
 218        int err;
 219
 220        err = clk_ti_get_reg_addr(dev, 0, &priv->reg);
 221        if (err) {
 222                dev_err(dev, "failed to get register address\n");
 223                return err;
 224        }
 225
 226        priv->shift = dev_read_u32_default(dev, "ti,bit-shift", 0);
 227        priv->latch = dev_read_s32_default(dev, "ti,latch-bit", -EINVAL);
 228
 229        priv->flags = CLK_SET_RATE_NO_REPARENT;
 230        if (dev_read_bool(dev, "ti,set-rate-parent"))
 231                priv->flags |= CLK_SET_RATE_PARENT;
 232
 233        if (dev_read_bool(dev, "ti,index-starts-at-one"))
 234                priv->mux_flags |= CLK_MUX_INDEX_ONE;
 235
 236        return 0;
 237}
 238
 239static const struct udevice_id clk_ti_mux_of_match[] = {
 240        {.compatible = "ti,mux-clock"},
 241        {},
 242};
 243
 244U_BOOT_DRIVER(clk_ti_mux) = {
 245        .name = "ti_mux_clock",
 246        .id = UCLASS_CLK,
 247        .of_match = clk_ti_mux_of_match,
 248        .of_to_plat = clk_ti_mux_of_to_plat,
 249        .probe = clk_ti_mux_probe,
 250        .remove = clk_ti_mux_remove,
 251        .priv_auto = sizeof(struct clk_ti_mux_priv),
 252        .ops = &clk_ti_mux_ops,
 253};
 254