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