linux/drivers/clk/sunxi-ng/ccu_gate.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2016 Maxime Ripard
   4 * Maxime Ripard <maxime.ripard@free-electrons.com>
   5 */
   6
   7#include <linux/clk-provider.h>
   8#include <linux/io.h>
   9
  10#include "ccu_gate.h"
  11
  12void ccu_gate_helper_disable(struct ccu_common *common, u32 gate)
  13{
  14        unsigned long flags;
  15        u32 reg;
  16
  17        if (!gate)
  18                return;
  19
  20        spin_lock_irqsave(common->lock, flags);
  21
  22        reg = readl(common->base + common->reg);
  23        writel(reg & ~gate, common->base + common->reg);
  24
  25        spin_unlock_irqrestore(common->lock, flags);
  26}
  27
  28static void ccu_gate_disable(struct clk_hw *hw)
  29{
  30        struct ccu_gate *cg = hw_to_ccu_gate(hw);
  31
  32        return ccu_gate_helper_disable(&cg->common, cg->enable);
  33}
  34
  35int ccu_gate_helper_enable(struct ccu_common *common, u32 gate)
  36{
  37        unsigned long flags;
  38        u32 reg;
  39
  40        if (!gate)
  41                return 0;
  42
  43        spin_lock_irqsave(common->lock, flags);
  44
  45        reg = readl(common->base + common->reg);
  46        writel(reg | gate, common->base + common->reg);
  47
  48        spin_unlock_irqrestore(common->lock, flags);
  49
  50        return 0;
  51}
  52
  53static int ccu_gate_enable(struct clk_hw *hw)
  54{
  55        struct ccu_gate *cg = hw_to_ccu_gate(hw);
  56
  57        return ccu_gate_helper_enable(&cg->common, cg->enable);
  58}
  59
  60int ccu_gate_helper_is_enabled(struct ccu_common *common, u32 gate)
  61{
  62        if (!gate)
  63                return 1;
  64
  65        return readl(common->base + common->reg) & gate;
  66}
  67
  68static int ccu_gate_is_enabled(struct clk_hw *hw)
  69{
  70        struct ccu_gate *cg = hw_to_ccu_gate(hw);
  71
  72        return ccu_gate_helper_is_enabled(&cg->common, cg->enable);
  73}
  74
  75static unsigned long ccu_gate_recalc_rate(struct clk_hw *hw,
  76                                          unsigned long parent_rate)
  77{
  78        struct ccu_gate *cg = hw_to_ccu_gate(hw);
  79        unsigned long rate = parent_rate;
  80
  81        if (cg->common.features & CCU_FEATURE_ALL_PREDIV)
  82                rate /= cg->common.prediv;
  83
  84        return rate;
  85}
  86
  87static long ccu_gate_round_rate(struct clk_hw *hw, unsigned long rate,
  88                                unsigned long *prate)
  89{
  90        struct ccu_gate *cg = hw_to_ccu_gate(hw);
  91        int div = 1;
  92
  93        if (cg->common.features & CCU_FEATURE_ALL_PREDIV)
  94                div = cg->common.prediv;
  95
  96        if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
  97                unsigned long best_parent = rate;
  98
  99                if (cg->common.features & CCU_FEATURE_ALL_PREDIV)
 100                        best_parent *= div;
 101                *prate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent);
 102        }
 103
 104        return *prate / div;
 105}
 106
 107static int ccu_gate_set_rate(struct clk_hw *hw, unsigned long rate,
 108                             unsigned long parent_rate)
 109{
 110        /*
 111         * We must report success but we can do so unconditionally because
 112         * clk_factor_round_rate returns values that ensure this call is a
 113         * nop.
 114         */
 115
 116        return 0;
 117}
 118
 119const struct clk_ops ccu_gate_ops = {
 120        .disable        = ccu_gate_disable,
 121        .enable         = ccu_gate_enable,
 122        .is_enabled     = ccu_gate_is_enabled,
 123        .round_rate     = ccu_gate_round_rate,
 124        .set_rate       = ccu_gate_set_rate,
 125        .recalc_rate    = ccu_gate_recalc_rate,
 126};
 127