uboot/drivers/clk/clk-gate.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
   4 * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
   5 * Copyright 2019 NXP
   6 *
   7 * Gated clock implementation
   8 */
   9
  10#include <common.h>
  11#include <asm/io.h>
  12#include <malloc.h>
  13#include <clk-uclass.h>
  14#include <dm/device.h>
  15#include <dm/devres.h>
  16#include <linux/bitops.h>
  17#include <linux/clk-provider.h>
  18#include <clk.h>
  19#include "clk.h"
  20#include <linux/err.h>
  21
  22#define UBOOT_DM_CLK_GATE "clk_gate"
  23
  24/**
  25 * DOC: basic gatable clock which can gate and ungate it's output
  26 *
  27 * Traits of this clock:
  28 * prepare - clk_(un)prepare only ensures parent is (un)prepared
  29 * enable - clk_enable and clk_disable are functional & control gating
  30 * rate - inherits rate from parent.  No clk_set_rate support
  31 * parent - fixed parent.  No clk_set_parent support
  32 */
  33
  34/*
  35 * It works on following logic:
  36 *
  37 * For enabling clock, enable = 1
  38 *      set2dis = 1     -> clear bit    -> set = 0
  39 *      set2dis = 0     -> set bit      -> set = 1
  40 *
  41 * For disabling clock, enable = 0
  42 *      set2dis = 1     -> set bit      -> set = 1
  43 *      set2dis = 0     -> clear bit    -> set = 0
  44 *
  45 * So, result is always: enable xor set2dis.
  46 */
  47static void clk_gate_endisable(struct clk *clk, int enable)
  48{
  49        struct clk_gate *gate = to_clk_gate(clk);
  50        int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
  51        u32 reg;
  52
  53        set ^= enable;
  54
  55        if (gate->flags & CLK_GATE_HIWORD_MASK) {
  56                reg = BIT(gate->bit_idx + 16);
  57                if (set)
  58                        reg |= BIT(gate->bit_idx);
  59        } else {
  60#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
  61                reg = gate->io_gate_val;
  62#else
  63                reg = readl(gate->reg);
  64#endif
  65
  66                if (set)
  67                        reg |= BIT(gate->bit_idx);
  68                else
  69                        reg &= ~BIT(gate->bit_idx);
  70        }
  71
  72        writel(reg, gate->reg);
  73}
  74
  75static int clk_gate_enable(struct clk *clk)
  76{
  77        clk_gate_endisable(clk, 1);
  78
  79        return 0;
  80}
  81
  82static int clk_gate_disable(struct clk *clk)
  83{
  84        clk_gate_endisable(clk, 0);
  85
  86        return 0;
  87}
  88
  89int clk_gate_is_enabled(struct clk *clk)
  90{
  91        struct clk_gate *gate = to_clk_gate(clk);
  92        u32 reg;
  93
  94#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
  95        reg = gate->io_gate_val;
  96#else
  97        reg = readl(gate->reg);
  98#endif
  99
 100        /* if a set bit disables this clk, flip it before masking */
 101        if (gate->flags & CLK_GATE_SET_TO_DISABLE)
 102                reg ^= BIT(gate->bit_idx);
 103
 104        reg &= BIT(gate->bit_idx);
 105
 106        return reg ? 1 : 0;
 107}
 108
 109const struct clk_ops clk_gate_ops = {
 110        .enable = clk_gate_enable,
 111        .disable = clk_gate_disable,
 112        .get_rate = clk_generic_get_rate,
 113};
 114
 115struct clk *clk_register_gate(struct device *dev, const char *name,
 116                              const char *parent_name, unsigned long flags,
 117                              void __iomem *reg, u8 bit_idx,
 118                              u8 clk_gate_flags, spinlock_t *lock)
 119{
 120        struct clk_gate *gate;
 121        struct clk *clk;
 122        int ret;
 123
 124        if (clk_gate_flags & CLK_GATE_HIWORD_MASK) {
 125                if (bit_idx > 15) {
 126                        pr_err("gate bit exceeds LOWORD field\n");
 127                        return ERR_PTR(-EINVAL);
 128                }
 129        }
 130
 131        /* allocate the gate */
 132        gate = kzalloc(sizeof(*gate), GFP_KERNEL);
 133        if (!gate)
 134                return ERR_PTR(-ENOMEM);
 135
 136        /* struct clk_gate assignments */
 137        gate->reg = reg;
 138        gate->bit_idx = bit_idx;
 139        gate->flags = clk_gate_flags;
 140#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
 141        gate->io_gate_val = *(u32 *)reg;
 142#endif
 143
 144        clk = &gate->clk;
 145        clk->flags = flags;
 146
 147        ret = clk_register(clk, UBOOT_DM_CLK_GATE, name, parent_name);
 148        if (ret) {
 149                kfree(gate);
 150                return ERR_PTR(ret);
 151        }
 152
 153        return clk;
 154}
 155
 156U_BOOT_DRIVER(clk_gate) = {
 157        .name   = UBOOT_DM_CLK_GATE,
 158        .id     = UCLASS_CLK,
 159        .ops    = &clk_gate_ops,
 160        .flags = DM_FLAG_PRE_RELOC,
 161};
 162