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