linux/drivers/clk/tegra/clk-periph-gate.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
   4 */
   5
   6#include <linux/clk-provider.h>
   7#include <linux/slab.h>
   8#include <linux/io.h>
   9#include <linux/delay.h>
  10#include <linux/err.h>
  11
  12#include <soc/tegra/fuse.h>
  13
  14#include "clk.h"
  15
  16static DEFINE_SPINLOCK(periph_ref_lock);
  17
  18/* Macros to assist peripheral gate clock */
  19#define read_enb(gate) \
  20        readl_relaxed(gate->clk_base + (gate->regs->enb_reg))
  21#define write_enb_set(val, gate) \
  22        writel_relaxed(val, gate->clk_base + (gate->regs->enb_set_reg))
  23#define write_enb_clr(val, gate) \
  24        writel_relaxed(val, gate->clk_base + (gate->regs->enb_clr_reg))
  25
  26#define read_rst(gate) \
  27        readl_relaxed(gate->clk_base + (gate->regs->rst_reg))
  28#define write_rst_clr(val, gate) \
  29        writel_relaxed(val, gate->clk_base + (gate->regs->rst_clr_reg))
  30
  31#define periph_clk_to_bit(gate) (1 << (gate->clk_num % 32))
  32
  33#define LVL2_CLK_GATE_OVRE 0x554
  34
  35/* Peripheral gate clock ops */
  36static int clk_periph_is_enabled(struct clk_hw *hw)
  37{
  38        struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
  39        int state = 1;
  40
  41        if (!(read_enb(gate) & periph_clk_to_bit(gate)))
  42                state = 0;
  43
  44        if (!(gate->flags & TEGRA_PERIPH_NO_RESET))
  45                if (read_rst(gate) & periph_clk_to_bit(gate))
  46                        state = 0;
  47
  48        return state;
  49}
  50
  51static void clk_periph_enable_locked(struct clk_hw *hw)
  52{
  53        struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
  54
  55        write_enb_set(periph_clk_to_bit(gate), gate);
  56        udelay(2);
  57
  58        if (!(gate->flags & TEGRA_PERIPH_NO_RESET) &&
  59            !(gate->flags & TEGRA_PERIPH_MANUAL_RESET)) {
  60                if (read_rst(gate) & periph_clk_to_bit(gate)) {
  61                        udelay(5); /* reset propogation delay */
  62                        write_rst_clr(periph_clk_to_bit(gate), gate);
  63                }
  64        }
  65
  66        if (gate->flags & TEGRA_PERIPH_WAR_1005168) {
  67                writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE);
  68                writel_relaxed(BIT(22), gate->clk_base + LVL2_CLK_GATE_OVRE);
  69                udelay(1);
  70                writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE);
  71        }
  72}
  73
  74static void clk_periph_disable_locked(struct clk_hw *hw)
  75{
  76        struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
  77
  78        /*
  79         * If peripheral is in the APB bus then read the APB bus to
  80         * flush the write operation in apb bus. This will avoid the
  81         * peripheral access after disabling clock
  82         */
  83        if (gate->flags & TEGRA_PERIPH_ON_APB)
  84                tegra_read_chipid();
  85
  86        write_enb_clr(periph_clk_to_bit(gate), gate);
  87}
  88
  89static int clk_periph_enable(struct clk_hw *hw)
  90{
  91        struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
  92        unsigned long flags = 0;
  93
  94        spin_lock_irqsave(&periph_ref_lock, flags);
  95
  96        if (!gate->enable_refcnt[gate->clk_num]++)
  97                clk_periph_enable_locked(hw);
  98
  99        spin_unlock_irqrestore(&periph_ref_lock, flags);
 100
 101        return 0;
 102}
 103
 104static void clk_periph_disable(struct clk_hw *hw)
 105{
 106        struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
 107        unsigned long flags = 0;
 108
 109        spin_lock_irqsave(&periph_ref_lock, flags);
 110
 111        WARN_ON(!gate->enable_refcnt[gate->clk_num]);
 112
 113        if (--gate->enable_refcnt[gate->clk_num] == 0)
 114                clk_periph_disable_locked(hw);
 115
 116        spin_unlock_irqrestore(&periph_ref_lock, flags);
 117}
 118
 119static void clk_periph_disable_unused(struct clk_hw *hw)
 120{
 121        struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
 122        unsigned long flags = 0;
 123
 124        spin_lock_irqsave(&periph_ref_lock, flags);
 125
 126        /*
 127         * Some clocks are duplicated and some of them are marked as critical,
 128         * like fuse and fuse_burn for example, thus the enable_refcnt will
 129         * be non-zero here if the "unused" duplicate is disabled by CCF.
 130         */
 131        if (!gate->enable_refcnt[gate->clk_num])
 132                clk_periph_disable_locked(hw);
 133
 134        spin_unlock_irqrestore(&periph_ref_lock, flags);
 135}
 136
 137const struct clk_ops tegra_clk_periph_gate_ops = {
 138        .is_enabled = clk_periph_is_enabled,
 139        .enable = clk_periph_enable,
 140        .disable = clk_periph_disable,
 141        .disable_unused = clk_periph_disable_unused,
 142};
 143
 144struct clk *tegra_clk_register_periph_gate(const char *name,
 145                const char *parent_name, u8 gate_flags, void __iomem *clk_base,
 146                unsigned long flags, int clk_num, int *enable_refcnt)
 147{
 148        struct tegra_clk_periph_gate *gate;
 149        struct clk *clk;
 150        struct clk_init_data init;
 151        const struct tegra_clk_periph_regs *pregs;
 152
 153        pregs = get_reg_bank(clk_num);
 154        if (!pregs)
 155                return ERR_PTR(-EINVAL);
 156
 157        gate = kzalloc(sizeof(*gate), GFP_KERNEL);
 158        if (!gate) {
 159                pr_err("%s: could not allocate periph gate clk\n", __func__);
 160                return ERR_PTR(-ENOMEM);
 161        }
 162
 163        init.name = name;
 164        init.flags = flags;
 165        init.parent_names = parent_name ? &parent_name : NULL;
 166        init.num_parents = parent_name ? 1 : 0;
 167        init.ops = &tegra_clk_periph_gate_ops;
 168
 169        gate->magic = TEGRA_CLK_PERIPH_GATE_MAGIC;
 170        gate->clk_base = clk_base;
 171        gate->clk_num = clk_num;
 172        gate->flags = gate_flags;
 173        gate->enable_refcnt = enable_refcnt;
 174        gate->regs = pregs;
 175
 176        /* Data in .init is copied by clk_register(), so stack variable OK */
 177        gate->hw.init = &init;
 178
 179        clk = clk_register(NULL, &gate->hw);
 180        if (IS_ERR(clk))
 181                kfree(gate);
 182
 183        return clk;
 184}
 185