linux/arch/arm/mach-imx/clk-pfd.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012 Freescale Semiconductor, Inc.
   3 * Copyright 2012 Linaro Ltd.
   4 *
   5 * The code contained herein is licensed under the GNU General Public
   6 * License. You may obtain a copy of the GNU General Public License
   7 * Version 2 or later at the following locations:
   8 *
   9 * http://www.opensource.org/licenses/gpl-license.html
  10 * http://www.gnu.org/copyleft/gpl.html
  11 */
  12
  13#include <linux/clk.h>
  14#include <linux/clk-provider.h>
  15#include <linux/io.h>
  16#include <linux/slab.h>
  17#include <linux/err.h>
  18#include "clk.h"
  19
  20/**
  21 * struct clk_pfd - IMX PFD clock
  22 * @clk_hw:     clock source
  23 * @reg:        PFD register address
  24 * @idx:        the index of PFD encoded in the register
  25 *
  26 * PFD clock found on i.MX6 series.  Each register for PFD has 4 clk_pfd
  27 * data encoded, and member idx is used to specify the one.  And each
  28 * register has SET, CLR and TOG registers at offset 0x4 0x8 and 0xc.
  29 */
  30struct clk_pfd {
  31        struct clk_hw   hw;
  32        void __iomem    *reg;
  33        u8              idx;
  34};
  35
  36#define to_clk_pfd(_hw) container_of(_hw, struct clk_pfd, hw)
  37
  38#define SET     0x4
  39#define CLR     0x8
  40#define OTG     0xc
  41
  42static int clk_pfd_enable(struct clk_hw *hw)
  43{
  44        struct clk_pfd *pfd = to_clk_pfd(hw);
  45
  46        writel_relaxed(1 << ((pfd->idx + 1) * 8 - 1), pfd->reg + CLR);
  47
  48        return 0;
  49}
  50
  51static void clk_pfd_disable(struct clk_hw *hw)
  52{
  53        struct clk_pfd *pfd = to_clk_pfd(hw);
  54
  55        writel_relaxed(1 << ((pfd->idx + 1) * 8 - 1), pfd->reg + SET);
  56}
  57
  58static unsigned long clk_pfd_recalc_rate(struct clk_hw *hw,
  59                                         unsigned long parent_rate)
  60{
  61        struct clk_pfd *pfd = to_clk_pfd(hw);
  62        u64 tmp = parent_rate;
  63        u8 frac = (readl_relaxed(pfd->reg) >> (pfd->idx * 8)) & 0x3f;
  64
  65        tmp *= 18;
  66        do_div(tmp, frac);
  67
  68        return tmp;
  69}
  70
  71static long clk_pfd_round_rate(struct clk_hw *hw, unsigned long rate,
  72                               unsigned long *prate)
  73{
  74        u64 tmp = *prate;
  75        u8 frac;
  76
  77        tmp = tmp * 18 + rate / 2;
  78        do_div(tmp, rate);
  79        frac = tmp;
  80        if (frac < 12)
  81                frac = 12;
  82        else if (frac > 35)
  83                frac = 35;
  84        tmp = *prate;
  85        tmp *= 18;
  86        do_div(tmp, frac);
  87
  88        return tmp;
  89}
  90
  91static int clk_pfd_set_rate(struct clk_hw *hw, unsigned long rate,
  92                unsigned long parent_rate)
  93{
  94        struct clk_pfd *pfd = to_clk_pfd(hw);
  95        u64 tmp = parent_rate;
  96        u8 frac;
  97
  98        tmp = tmp * 18 + rate / 2;
  99        do_div(tmp, rate);
 100        frac = tmp;
 101        if (frac < 12)
 102                frac = 12;
 103        else if (frac > 35)
 104                frac = 35;
 105
 106        writel_relaxed(0x3f << (pfd->idx * 8), pfd->reg + CLR);
 107        writel_relaxed(frac << (pfd->idx * 8), pfd->reg + SET);
 108
 109        return 0;
 110}
 111
 112static const struct clk_ops clk_pfd_ops = {
 113        .enable         = clk_pfd_enable,
 114        .disable        = clk_pfd_disable,
 115        .recalc_rate    = clk_pfd_recalc_rate,
 116        .round_rate     = clk_pfd_round_rate,
 117        .set_rate       = clk_pfd_set_rate,
 118};
 119
 120struct clk *imx_clk_pfd(const char *name, const char *parent_name,
 121                        void __iomem *reg, u8 idx)
 122{
 123        struct clk_pfd *pfd;
 124        struct clk *clk;
 125        struct clk_init_data init;
 126
 127        pfd = kzalloc(sizeof(*pfd), GFP_KERNEL);
 128        if (!pfd)
 129                return ERR_PTR(-ENOMEM);
 130
 131        pfd->reg = reg;
 132        pfd->idx = idx;
 133
 134        init.name = name;
 135        init.ops = &clk_pfd_ops;
 136        init.flags = 0;
 137        init.parent_names = &parent_name;
 138        init.num_parents = 1;
 139
 140        pfd->hw.init = &init;
 141
 142        clk = clk_register(NULL, &pfd->hw);
 143        if (IS_ERR(clk))
 144                kfree(pfd);
 145
 146        return clk;
 147}
 148