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_WAR_1005168) {
  59                writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE);
  60                writel_relaxed(BIT(22), gate->clk_base + LVL2_CLK_GATE_OVRE);
  61                udelay(1);
  62                writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE);
  63        }
  64}
  65
  66static void clk_periph_disable_locked(struct clk_hw *hw)
  67{
  68        struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
  69
  70        /*
  71         * If peripheral is in the APB bus then read the APB bus to
  72         * flush the write operation in apb bus. This will avoid the
  73         * peripheral access after disabling clock
  74         */
  75        if (gate->flags & TEGRA_PERIPH_ON_APB)
  76                tegra_read_chipid();
  77
  78        write_enb_clr(periph_clk_to_bit(gate), gate);
  79}
  80
  81static int clk_periph_enable(struct clk_hw *hw)
  82{
  83        struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
  84        unsigned long flags = 0;
  85
  86        spin_lock_irqsave(&periph_ref_lock, flags);
  87
  88        if (!gate->enable_refcnt[gate->clk_num]++)
  89                clk_periph_enable_locked(hw);
  90
  91        spin_unlock_irqrestore(&periph_ref_lock, flags);
  92
  93        return 0;
  94}
  95
  96static void clk_periph_disable(struct clk_hw *hw)
  97{
  98        struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
  99        unsigned long flags = 0;
 100
 101        spin_lock_irqsave(&periph_ref_lock, flags);
 102
 103        WARN_ON(!gate->enable_refcnt[gate->clk_num]);
 104
 105        if (--gate->enable_refcnt[gate->clk_num] == 0)
 106                clk_periph_disable_locked(hw);
 107
 108        spin_unlock_irqrestore(&periph_ref_lock, flags);
 109}
 110
 111static void clk_periph_disable_unused(struct clk_hw *hw)
 112{
 113        struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
 114        unsigned long flags = 0;
 115
 116        spin_lock_irqsave(&periph_ref_lock, flags);
 117
 118        /*
 119         * Some clocks are duplicated and some of them are marked as critical,
 120         * like fuse and fuse_burn for example, thus the enable_refcnt will
 121         * be non-zero here if the "unused" duplicate is disabled by CCF.
 122         */
 123        if (!gate->enable_refcnt[gate->clk_num])
 124                clk_periph_disable_locked(hw);
 125
 126        spin_unlock_irqrestore(&periph_ref_lock, flags);
 127}
 128
 129const struct clk_ops tegra_clk_periph_gate_ops = {
 130        .is_enabled = clk_periph_is_enabled,
 131        .enable = clk_periph_enable,
 132        .disable = clk_periph_disable,
 133        .disable_unused = clk_periph_disable_unused,
 134};
 135
 136struct clk *tegra_clk_register_periph_gate(const char *name,
 137                const char *parent_name, u8 gate_flags, void __iomem *clk_base,
 138                unsigned long flags, int clk_num, int *enable_refcnt)
 139{
 140        struct tegra_clk_periph_gate *gate;
 141        struct clk *clk;
 142        struct clk_init_data init;
 143        const struct tegra_clk_periph_regs *pregs;
 144
 145        pregs = get_reg_bank(clk_num);
 146        if (!pregs)
 147                return ERR_PTR(-EINVAL);
 148
 149        gate = kzalloc(sizeof(*gate), GFP_KERNEL);
 150        if (!gate) {
 151                pr_err("%s: could not allocate periph gate clk\n", __func__);
 152                return ERR_PTR(-ENOMEM);
 153        }
 154
 155        init.name = name;
 156        init.flags = flags;
 157        init.parent_names = parent_name ? &parent_name : NULL;
 158        init.num_parents = parent_name ? 1 : 0;
 159        init.ops = &tegra_clk_periph_gate_ops;
 160
 161        gate->magic = TEGRA_CLK_PERIPH_GATE_MAGIC;
 162        gate->clk_base = clk_base;
 163        gate->clk_num = clk_num;
 164        gate->flags = gate_flags;
 165        gate->enable_refcnt = enable_refcnt;
 166        gate->regs = pregs;
 167
 168        /* Data in .init is copied by clk_register(), so stack variable OK */
 169        gate->hw.init = &init;
 170
 171        clk = clk_register(NULL, &gate->hw);
 172        if (IS_ERR(clk))
 173                kfree(gate);
 174
 175        return clk;
 176}
 177