uboot/drivers/clk/clk-mux.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2019 DENX Software Engineering
   4 * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
   5 *
   6 * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
   7 * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
   8 * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
   9 *
  10 * Simple multiplexer clock implementation
  11 */
  12
  13/*
  14 * U-Boot CCF porting node:
  15 *
  16 * The Linux kernel - as of tag: 5.0-rc3 is using also the imx_clk_fixup_mux()
  17 * version of CCF mux. It is used on e.g. imx6q to provide fixes (like
  18 * imx_cscmr1_fixup) for broken HW.
  19 *
  20 * At least for IMX6Q (but NOT IMX6QP) it is important when we set the parent
  21 * clock.
  22 */
  23
  24#define LOG_CATEGORY UCLASS_CLK
  25
  26#include <common.h>
  27#include <clk.h>
  28#include <clk-uclass.h>
  29#include <log.h>
  30#include <malloc.h>
  31#include <asm/io.h>
  32#include <dm/device.h>
  33#include <dm/device_compat.h>
  34#include <dm/devres.h>
  35#include <dm/uclass.h>
  36#include <linux/bitops.h>
  37#include <linux/clk-provider.h>
  38#include <linux/err.h>
  39
  40#include "clk.h"
  41
  42#define UBOOT_DM_CLK_CCF_MUX "ccf_clk_mux"
  43
  44int clk_mux_val_to_index(struct clk *clk, u32 *table, unsigned int flags,
  45                         unsigned int val)
  46{
  47        struct clk_mux *mux = to_clk_mux(clk);
  48        int num_parents = mux->num_parents;
  49
  50        if (table) {
  51                int i;
  52
  53                for (i = 0; i < num_parents; i++)
  54                        if (table[i] == val)
  55                                return i;
  56                return -EINVAL;
  57        }
  58
  59        if (val && (flags & CLK_MUX_INDEX_BIT))
  60                val = ffs(val) - 1;
  61
  62        if (val && (flags & CLK_MUX_INDEX_ONE))
  63                val--;
  64
  65        if (val >= num_parents)
  66                return -EINVAL;
  67
  68        return val;
  69}
  70
  71unsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index)
  72{
  73        unsigned int val = index;
  74
  75        if (table) {
  76                val = table[index];
  77        } else {
  78                if (flags & CLK_MUX_INDEX_BIT)
  79                        val = 1 << index;
  80
  81                if (flags & CLK_MUX_INDEX_ONE)
  82                        val++;
  83        }
  84
  85        return val;
  86}
  87
  88u8 clk_mux_get_parent(struct clk *clk)
  89{
  90        struct clk_mux *mux = to_clk_mux(clk);
  91        u32 val;
  92
  93#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
  94        val = mux->io_mux_val;
  95#else
  96        val = readl(mux->reg);
  97#endif
  98        val >>= mux->shift;
  99        val &= mux->mask;
 100
 101        return clk_mux_val_to_index(clk, mux->table, mux->flags, val);
 102}
 103
 104static int clk_fetch_parent_index(struct clk *clk,
 105                                  struct clk *parent)
 106{
 107        struct clk_mux *mux = to_clk_mux(clk);
 108
 109        int i;
 110
 111        if (!parent)
 112                return -EINVAL;
 113
 114        for (i = 0; i < mux->num_parents; i++) {
 115                if (!strcmp(parent->dev->name, mux->parent_names[i]))
 116                        return i;
 117        }
 118
 119        return -EINVAL;
 120}
 121
 122static int clk_mux_set_parent(struct clk *clk, struct clk *parent)
 123{
 124        struct clk_mux *mux = to_clk_mux(clk);
 125        int index;
 126        u32 val;
 127        u32 reg;
 128
 129        index = clk_fetch_parent_index(clk, parent);
 130        if (index < 0) {
 131                log_err("Could not fetch index\n");
 132                return index;
 133        }
 134
 135        val = clk_mux_index_to_val(mux->table, mux->flags, index);
 136
 137        if (mux->flags & CLK_MUX_HIWORD_MASK) {
 138                reg = mux->mask << (mux->shift + 16);
 139        } else {
 140#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
 141                reg = mux->io_mux_val;
 142#else
 143                reg = readl(mux->reg);
 144#endif
 145                reg &= ~(mux->mask << mux->shift);
 146        }
 147        val = val << mux->shift;
 148        reg |= val;
 149#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
 150        mux->io_mux_val = reg;
 151#else
 152        writel(reg, mux->reg);
 153#endif
 154
 155        return 0;
 156}
 157
 158const struct clk_ops clk_mux_ops = {
 159        .get_rate = clk_generic_get_rate,
 160        .set_parent = clk_mux_set_parent,
 161};
 162
 163struct clk *clk_hw_register_mux_table(struct device *dev, const char *name,
 164                const char * const *parent_names, u8 num_parents,
 165                unsigned long flags,
 166                void __iomem *reg, u8 shift, u32 mask,
 167                u8 clk_mux_flags, u32 *table)
 168{
 169        struct clk_mux *mux;
 170        struct clk *clk;
 171        u8 width = 0;
 172        int ret;
 173
 174        if (clk_mux_flags & CLK_MUX_HIWORD_MASK) {
 175                width = fls(mask) - ffs(mask) + 1;
 176                if (width + shift > 16) {
 177                        dev_err(dev, "mux value exceeds LOWORD field\n");
 178                        return ERR_PTR(-EINVAL);
 179                }
 180        }
 181
 182        /* allocate the mux */
 183        mux = kzalloc(sizeof(*mux), GFP_KERNEL);
 184        if (!mux)
 185                return ERR_PTR(-ENOMEM);
 186
 187        /* U-boot specific assignments */
 188        mux->parent_names = parent_names;
 189        mux->num_parents = num_parents;
 190
 191        /* struct clk_mux assignments */
 192        mux->reg = reg;
 193        mux->shift = shift;
 194        mux->mask = mask;
 195        mux->flags = clk_mux_flags;
 196        mux->table = table;
 197#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
 198        mux->io_mux_val = *(u32 *)reg;
 199#endif
 200
 201        clk = &mux->clk;
 202        clk->flags = flags;
 203
 204        /*
 205         * Read the current mux setup - so we assign correct parent.
 206         *
 207         * Changing parent would require changing internals of udevice struct
 208         * for the corresponding clock (to do that define .set_parent() method).
 209         */
 210        ret = clk_register(clk, UBOOT_DM_CLK_CCF_MUX, name,
 211                           parent_names[clk_mux_get_parent(clk)]);
 212        if (ret) {
 213                kfree(mux);
 214                return ERR_PTR(ret);
 215        }
 216
 217        return clk;
 218}
 219
 220struct clk *clk_register_mux_table(struct device *dev, const char *name,
 221                const char * const *parent_names, u8 num_parents,
 222                unsigned long flags,
 223                void __iomem *reg, u8 shift, u32 mask,
 224                u8 clk_mux_flags, u32 *table)
 225{
 226        struct clk *clk;
 227
 228        clk = clk_hw_register_mux_table(dev, name, parent_names, num_parents,
 229                                       flags, reg, shift, mask, clk_mux_flags,
 230                                       table);
 231        if (IS_ERR(clk))
 232                return ERR_CAST(clk);
 233        return clk;
 234}
 235
 236struct clk *clk_register_mux(struct device *dev, const char *name,
 237                const char * const *parent_names, u8 num_parents,
 238                unsigned long flags,
 239                void __iomem *reg, u8 shift, u8 width,
 240                u8 clk_mux_flags)
 241{
 242        u32 mask = BIT(width) - 1;
 243
 244        return clk_register_mux_table(dev, name, parent_names, num_parents,
 245                                      flags, reg, shift, mask, clk_mux_flags,
 246                                      NULL);
 247}
 248
 249U_BOOT_DRIVER(ccf_clk_mux) = {
 250        .name   = UBOOT_DM_CLK_CCF_MUX,
 251        .id     = UCLASS_CLK,
 252        .ops    = &clk_mux_ops,
 253        .flags = DM_FLAG_PRE_RELOC,
 254};
 255