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-provider.h>
  13#include <linux/err.h>
  14#include <linux/slab.h>
  15#include "clk.h"
  16
  17/**
  18 * struct clk_div - mxs integer divider clock
  19 * @divider: the parent class
  20 * @ops: pointer to clk_ops of parent class
  21 * @reg: register address
  22 * @busy: busy bit shift
  23 *
  24 * The mxs divider clock is a subclass of basic clk_divider with an
  25 * addtional busy bit.
  26 */
  27struct clk_div {
  28        struct clk_divider divider;
  29        const struct clk_ops *ops;
  30        void __iomem *reg;
  31        u8 busy;
  32};
  33
  34static inline struct clk_div *to_clk_div(struct clk_hw *hw)
  35{
  36        struct clk_divider *divider = to_clk_divider(hw);
  37
  38        return container_of(divider, struct clk_div, divider);
  39}
  40
  41static unsigned long clk_div_recalc_rate(struct clk_hw *hw,
  42                                         unsigned long parent_rate)
  43{
  44        struct clk_div *div = to_clk_div(hw);
  45
  46        return div->ops->recalc_rate(&div->divider.hw, parent_rate);
  47}
  48
  49static long clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
  50                               unsigned long *prate)
  51{
  52        struct clk_div *div = to_clk_div(hw);
  53
  54        return div->ops->round_rate(&div->divider.hw, rate, prate);
  55}
  56
  57static int clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
  58                            unsigned long parent_rate)
  59{
  60        struct clk_div *div = to_clk_div(hw);
  61        int ret;
  62
  63        ret = div->ops->set_rate(&div->divider.hw, rate, parent_rate);
  64        if (!ret)
  65                ret = mxs_clk_wait(div->reg, div->busy);
  66
  67        return ret;
  68}
  69
  70static const struct clk_ops clk_div_ops = {
  71        .recalc_rate = clk_div_recalc_rate,
  72        .round_rate = clk_div_round_rate,
  73        .set_rate = clk_div_set_rate,
  74};
  75
  76struct clk *mxs_clk_div(const char *name, const char *parent_name,
  77                        void __iomem *reg, u8 shift, u8 width, u8 busy)
  78{
  79        struct clk_div *div;
  80        struct clk *clk;
  81        struct clk_init_data init;
  82
  83        div = kzalloc(sizeof(*div), GFP_KERNEL);
  84        if (!div)
  85                return ERR_PTR(-ENOMEM);
  86
  87        init.name = name;
  88        init.ops = &clk_div_ops;
  89        init.flags = CLK_SET_RATE_PARENT;
  90        init.parent_names = (parent_name ? &parent_name: NULL);
  91        init.num_parents = (parent_name ? 1 : 0);
  92
  93        div->reg = reg;
  94        div->busy = busy;
  95
  96        div->divider.reg = reg;
  97        div->divider.shift = shift;
  98        div->divider.width = width;
  99        div->divider.flags = CLK_DIVIDER_ONE_BASED;
 100        div->divider.lock = &mxs_lock;
 101        div->divider.hw.init = &init;
 102        div->ops = &clk_divider_ops;
 103
 104        clk = clk_register(NULL, &div->divider.hw);
 105        if (IS_ERR(clk))
 106                kfree(div);
 107
 108        return clk;
 109}
 110