linux/arch/arm/mach-omap2/dpll44xx.c
<<
>>
Prefs
   1/*
   2 * OMAP4-specific DPLL control functions
   3 *
   4 * Copyright (C) 2011 Texas Instruments, Inc.
   5 * Rajendra Nayak
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 */
  11
  12#include <linux/kernel.h>
  13#include <linux/errno.h>
  14#include <linux/clk.h>
  15#include <linux/io.h>
  16#include <linux/bitops.h>
  17
  18#include "clock.h"
  19
  20/*
  21 * Maximum DPLL input frequency (FINT) and output frequency (FOUT) that
  22 * can supported when using the DPLL low-power mode. Frequencies are
  23 * defined in OMAP4430/60 Public TRM section 3.6.3.3.2 "Enable Control,
  24 * Status, and Low-Power Operation Mode".
  25 */
  26#define OMAP4_DPLL_LP_FINT_MAX  1000000
  27#define OMAP4_DPLL_LP_FOUT_MAX  100000000
  28
  29/*
  30 * Bitfield declarations
  31 */
  32#define OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK             (1 << 8)
  33#define OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK           (1 << 10)
  34#define OMAP4430_DPLL_REGM4XEN_MASK                     (1 << 11)
  35
  36/* Static rate multiplier for OMAP4 REGM4XEN clocks */
  37#define OMAP4430_REGM4XEN_MULT                          4
  38
  39void omap4_dpllmx_allow_gatectrl(struct clk_hw_omap *clk)
  40{
  41        u32 v;
  42        u32 mask;
  43
  44        if (!clk || !clk->clksel_reg)
  45                return;
  46
  47        mask = clk->flags & CLOCK_CLKOUTX2 ?
  48                        OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK :
  49                        OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK;
  50
  51        v = omap2_clk_readl(clk, clk->clksel_reg);
  52        /* Clear the bit to allow gatectrl */
  53        v &= ~mask;
  54        omap2_clk_writel(v, clk, clk->clksel_reg);
  55}
  56
  57void omap4_dpllmx_deny_gatectrl(struct clk_hw_omap *clk)
  58{
  59        u32 v;
  60        u32 mask;
  61
  62        if (!clk || !clk->clksel_reg)
  63                return;
  64
  65        mask = clk->flags & CLOCK_CLKOUTX2 ?
  66                        OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK :
  67                        OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK;
  68
  69        v = omap2_clk_readl(clk, clk->clksel_reg);
  70        /* Set the bit to deny gatectrl */
  71        v |= mask;
  72        omap2_clk_writel(v, clk, clk->clksel_reg);
  73}
  74
  75const struct clk_hw_omap_ops clkhwops_omap4_dpllmx = {
  76        .allow_idle     = omap4_dpllmx_allow_gatectrl,
  77        .deny_idle      = omap4_dpllmx_deny_gatectrl,
  78};
  79
  80/**
  81 * omap4_dpll_lpmode_recalc - compute DPLL low-power setting
  82 * @dd: pointer to the dpll data structure
  83 *
  84 * Calculates if low-power mode can be enabled based upon the last
  85 * multiplier and divider values calculated. If low-power mode can be
  86 * enabled, then the bit to enable low-power mode is stored in the
  87 * last_rounded_lpmode variable. This implementation is based upon the
  88 * criteria for enabling low-power mode as described in the OMAP4430/60
  89 * Public TRM section 3.6.3.3.2 "Enable Control, Status, and Low-Power
  90 * Operation Mode".
  91 */
  92static void omap4_dpll_lpmode_recalc(struct dpll_data *dd)
  93{
  94        long fint, fout;
  95
  96        fint = __clk_get_rate(dd->clk_ref) / (dd->last_rounded_n + 1);
  97        fout = fint * dd->last_rounded_m;
  98
  99        if ((fint < OMAP4_DPLL_LP_FINT_MAX) && (fout < OMAP4_DPLL_LP_FOUT_MAX))
 100                dd->last_rounded_lpmode = 1;
 101        else
 102                dd->last_rounded_lpmode = 0;
 103}
 104
 105/**
 106 * omap4_dpll_regm4xen_recalc - compute DPLL rate, considering REGM4XEN bit
 107 * @clk: struct clk * of the DPLL to compute the rate for
 108 *
 109 * Compute the output rate for the OMAP4 DPLL represented by @clk.
 110 * Takes the REGM4XEN bit into consideration, which is needed for the
 111 * OMAP4 ABE DPLL.  Returns the DPLL's output rate (before M-dividers)
 112 * upon success, or 0 upon error.
 113 */
 114unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw,
 115                        unsigned long parent_rate)
 116{
 117        struct clk_hw_omap *clk = to_clk_hw_omap(hw);
 118        u32 v;
 119        unsigned long rate;
 120        struct dpll_data *dd;
 121
 122        if (!clk || !clk->dpll_data)
 123                return 0;
 124
 125        dd = clk->dpll_data;
 126
 127        rate = omap2_get_dpll_rate(clk);
 128
 129        /* regm4xen adds a multiplier of 4 to DPLL calculations */
 130        v = omap2_clk_readl(clk, dd->control_reg);
 131        if (v & OMAP4430_DPLL_REGM4XEN_MASK)
 132                rate *= OMAP4430_REGM4XEN_MULT;
 133
 134        return rate;
 135}
 136
 137/**
 138 * omap4_dpll_regm4xen_round_rate - round DPLL rate, considering REGM4XEN bit
 139 * @clk: struct clk * of the DPLL to round a rate for
 140 * @target_rate: the desired rate of the DPLL
 141 *
 142 * Compute the rate that would be programmed into the DPLL hardware
 143 * for @clk if set_rate() were to be provided with the rate
 144 * @target_rate.  Takes the REGM4XEN bit into consideration, which is
 145 * needed for the OMAP4 ABE DPLL.  Returns the rounded rate (before
 146 * M-dividers) upon success, -EINVAL if @clk is null or not a DPLL, or
 147 * ~0 if an error occurred in omap2_dpll_round_rate().
 148 */
 149long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw,
 150                                    unsigned long target_rate,
 151                                    unsigned long *parent_rate)
 152{
 153        struct clk_hw_omap *clk = to_clk_hw_omap(hw);
 154        struct dpll_data *dd;
 155        long r;
 156
 157        if (!clk || !clk->dpll_data)
 158                return -EINVAL;
 159
 160        dd = clk->dpll_data;
 161
 162        dd->last_rounded_m4xen = 0;
 163
 164        /*
 165         * First try to compute the DPLL configuration for
 166         * target rate without using the 4X multiplier.
 167         */
 168        r = omap2_dpll_round_rate(hw, target_rate, NULL);
 169        if (r != ~0)
 170                goto out;
 171
 172        /*
 173         * If we did not find a valid DPLL configuration, try again, but
 174         * this time see if using the 4X multiplier can help. Enabling the
 175         * 4X multiplier is equivalent to dividing the target rate by 4.
 176         */
 177        r = omap2_dpll_round_rate(hw, target_rate / OMAP4430_REGM4XEN_MULT,
 178                                  NULL);
 179        if (r == ~0)
 180                return r;
 181
 182        dd->last_rounded_rate *= OMAP4430_REGM4XEN_MULT;
 183        dd->last_rounded_m4xen = 1;
 184
 185out:
 186        omap4_dpll_lpmode_recalc(dd);
 187
 188        return dd->last_rounded_rate;
 189}
 190
 191/**
 192 * omap4_dpll_regm4xen_determine_rate - determine rate for a DPLL
 193 * @hw: pointer to the clock to determine rate for
 194 * @rate: target rate for the DPLL
 195 * @best_parent_rate: pointer for returning best parent rate
 196 * @best_parent_clk: pointer for returning best parent clock
 197 *
 198 * Determines which DPLL mode to use for reaching a desired rate.
 199 * Checks whether the DPLL shall be in bypass or locked mode, and if
 200 * locked, calculates the M,N values for the DPLL via round-rate.
 201 * Returns a positive clock rate with success, negative error value
 202 * in failure.
 203 */
 204long omap4_dpll_regm4xen_determine_rate(struct clk_hw *hw, unsigned long rate,
 205                                        unsigned long min_rate,
 206                                        unsigned long max_rate,
 207                                        unsigned long *best_parent_rate,
 208                                        struct clk_hw **best_parent_clk)
 209{
 210        struct clk_hw_omap *clk = to_clk_hw_omap(hw);
 211        struct dpll_data *dd;
 212
 213        if (!hw || !rate)
 214                return -EINVAL;
 215
 216        dd = clk->dpll_data;
 217        if (!dd)
 218                return -EINVAL;
 219
 220        if (__clk_get_rate(dd->clk_bypass) == rate &&
 221            (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) {
 222                *best_parent_clk = __clk_get_hw(dd->clk_bypass);
 223        } else {
 224                rate = omap4_dpll_regm4xen_round_rate(hw, rate,
 225                                                      best_parent_rate);
 226                *best_parent_clk = __clk_get_hw(dd->clk_ref);
 227        }
 228
 229        *best_parent_rate = rate;
 230
 231        return rate;
 232}
 233