linux/drivers/clk/mxs/clk-div.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012 Freescale Semiconductor, Inc.
   3 *
   4 * The code contained herein is licensed under the GNU General Public
   5 * License. You may obtain a copy of the GNU General Public License
   6 * Version 2 or later at the following locations:
   7 *
   8 * http://www.opensource.org/licenses/gpl-license.html
   9 * http://www.gnu.org/copyleft/gpl.html
  10 */
  11
  12#include <linux/clk.h>
  13#include <linux/clk-provider.h>
  14#include <linux/err.h>
  15#include <linux/slab.h>
  16#include "clk.h"
  17
  18/**
  19 * struct clk_div - mxs integer divider clock
  20 * @divider: the parent class
  21 * @ops: pointer to clk_ops of parent class
  22 * @reg: register address
  23 * @busy: busy bit shift
  24 *
  25 * The mxs divider clock is a subclass of basic clk_divider with an
  26 * addtional busy bit.
  27 */
  28struct clk_div {
  29        struct clk_divider divider;
  30        const struct clk_ops *ops;
  31        void __iomem *reg;
  32        u8 busy;
  33};
  34
  35static inline struct clk_div *to_clk_div(struct clk_hw *hw)
  36{
  37        struct clk_divider *divider = container_of(hw, struct clk_divider, hw);
  38
  39        return container_of(divider, struct clk_div, divider);
  40}
  41
  42static unsigned long clk_div_recalc_rate(struct clk_hw *hw,
  43                                         unsigned long parent_rate)
  44{
  45        struct clk_div *div = to_clk_div(hw);
  46
  47        return div->ops->recalc_rate(&div->divider.hw, parent_rate);
  48}
  49
  50static long clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
  51                               unsigned long *prate)
  52{
  53        struct clk_div *div = to_clk_div(hw);
  54
  55        return div->ops->round_rate(&div->divider.hw, rate, prate);
  56}
  57
  58static int clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
  59                            unsigned long parent_rate)
  60{
  61        struct clk_div *div = to_clk_div(hw);
  62        int ret;
  63
  64        ret = div->ops->set_rate(&div->divider.hw, rate, parent_rate);
  65        if (!ret)
  66                ret = mxs_clk_wait(div->reg, div->busy);
  67
  68        return ret;
  69}
  70
  71static struct clk_ops clk_div_ops = {
  72        .recalc_rate = clk_div_recalc_rate,
  73        .round_rate = clk_div_round_rate,
  74        .set_rate = clk_div_set_rate,
  75};
  76
  77struct clk *mxs_clk_div(const char *name, const char *parent_name,
  78                        void __iomem *reg, u8 shift, u8 width, u8 busy)
  79{
  80        struct clk_div *div;
  81        struct clk *clk;
  82        struct clk_init_data init;
  83
  84        div = kzalloc(sizeof(*div), GFP_KERNEL);
  85        if (!div)
  86                return ERR_PTR(-ENOMEM);
  87
  88        init.name = name;
  89        init.ops = &clk_div_ops;
  90        init.flags = CLK_SET_RATE_PARENT;
  91        init.parent_names = (parent_name ? &parent_name: NULL);
  92        init.num_parents = (parent_name ? 1 : 0);
  93
  94        div->reg = reg;
  95        div->busy = busy;
  96
  97        div->divider.reg = reg;
  98        div->divider.shift = shift;
  99        div->divider.width = width;
 100        div->divider.flags = CLK_DIVIDER_ONE_BASED;
 101        div->divider.lock = &mxs_lock;
 102        div->divider.hw.init = &init;
 103        div->ops = &clk_divider_ops;
 104
 105        clk = clk_register(NULL, &div->divider.hw);
 106        if (IS_ERR(clk))
 107                kfree(div);
 108
 109        return clk;
 110}
 111