root/drivers/clk/imx/clk-pfdv2.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. clk_pfdv2_wait
  2. clk_pfdv2_enable
  3. clk_pfdv2_disable
  4. clk_pfdv2_recalc_rate
  5. clk_pfdv2_round_rate
  6. clk_pfdv2_is_enabled
  7. clk_pfdv2_set_rate
  8. imx_clk_pfdv2

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * Copyright (C) 2016 Freescale Semiconductor, Inc.
   4  * Copyright 2017~2018 NXP
   5  *
   6  * Author: Dong Aisheng <aisheng.dong@nxp.com>
   7  *
   8  */
   9 
  10 #include <linux/clk-provider.h>
  11 #include <linux/err.h>
  12 #include <linux/io.h>
  13 #include <linux/iopoll.h>
  14 #include <linux/slab.h>
  15 
  16 #include "clk.h"
  17 
  18 /**
  19  * struct clk_pfdv2 - IMX PFD clock
  20  * @clk_hw:     clock source
  21  * @reg:        PFD register address
  22  * @gate_bit:   Gate bit offset
  23  * @vld_bit:    Valid bit offset
  24  * @frac_off:   PLL Fractional Divider offset
  25  */
  26 
  27 struct clk_pfdv2 {
  28         struct clk_hw   hw;
  29         void __iomem    *reg;
  30         u8              gate_bit;
  31         u8              vld_bit;
  32         u8              frac_off;
  33 };
  34 
  35 #define to_clk_pfdv2(_hw) container_of(_hw, struct clk_pfdv2, hw)
  36 
  37 #define CLK_PFDV2_FRAC_MASK 0x3f
  38 
  39 #define LOCK_TIMEOUT_US         USEC_PER_MSEC
  40 
  41 static DEFINE_SPINLOCK(pfd_lock);
  42 
  43 static int clk_pfdv2_wait(struct clk_pfdv2 *pfd)
  44 {
  45         u32 val;
  46 
  47         return readl_poll_timeout(pfd->reg, val, val & (1 << pfd->vld_bit),
  48                                   0, LOCK_TIMEOUT_US);
  49 }
  50 
  51 static int clk_pfdv2_enable(struct clk_hw *hw)
  52 {
  53         struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
  54         unsigned long flags;
  55         u32 val;
  56 
  57         spin_lock_irqsave(&pfd_lock, flags);
  58         val = readl_relaxed(pfd->reg);
  59         val &= ~(1 << pfd->gate_bit);
  60         writel_relaxed(val, pfd->reg);
  61         spin_unlock_irqrestore(&pfd_lock, flags);
  62 
  63         return clk_pfdv2_wait(pfd);
  64 }
  65 
  66 static void clk_pfdv2_disable(struct clk_hw *hw)
  67 {
  68         struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
  69         unsigned long flags;
  70         u32 val;
  71 
  72         spin_lock_irqsave(&pfd_lock, flags);
  73         val = readl_relaxed(pfd->reg);
  74         val |= (1 << pfd->gate_bit);
  75         writel_relaxed(val, pfd->reg);
  76         spin_unlock_irqrestore(&pfd_lock, flags);
  77 }
  78 
  79 static unsigned long clk_pfdv2_recalc_rate(struct clk_hw *hw,
  80                                            unsigned long parent_rate)
  81 {
  82         struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
  83         u64 tmp = parent_rate;
  84         u8 frac;
  85 
  86         frac = (readl_relaxed(pfd->reg) >> pfd->frac_off)
  87                 & CLK_PFDV2_FRAC_MASK;
  88 
  89         if (!frac) {
  90                 pr_debug("clk_pfdv2: %s invalid pfd frac value 0\n",
  91                          clk_hw_get_name(hw));
  92                 return 0;
  93         }
  94 
  95         tmp *= 18;
  96         do_div(tmp, frac);
  97 
  98         return tmp;
  99 }
 100 
 101 static long clk_pfdv2_round_rate(struct clk_hw *hw, unsigned long rate,
 102                                  unsigned long *prate)
 103 {
 104         u64 tmp = *prate;
 105         u8 frac;
 106 
 107         tmp = tmp * 18 + rate / 2;
 108         do_div(tmp, rate);
 109         frac = tmp;
 110 
 111         if (frac < 12)
 112                 frac = 12;
 113         else if (frac > 35)
 114                 frac = 35;
 115 
 116         tmp = *prate;
 117         tmp *= 18;
 118         do_div(tmp, frac);
 119 
 120         return tmp;
 121 }
 122 
 123 static int clk_pfdv2_is_enabled(struct clk_hw *hw)
 124 {
 125         struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
 126 
 127         if (readl_relaxed(pfd->reg) & (1 << pfd->gate_bit))
 128                 return 0;
 129 
 130         return 1;
 131 }
 132 
 133 static int clk_pfdv2_set_rate(struct clk_hw *hw, unsigned long rate,
 134                               unsigned long parent_rate)
 135 {
 136         struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
 137         unsigned long flags;
 138         u64 tmp = parent_rate;
 139         u32 val;
 140         u8 frac;
 141 
 142         tmp = tmp * 18 + rate / 2;
 143         do_div(tmp, rate);
 144         frac = tmp;
 145         if (frac < 12)
 146                 frac = 12;
 147         else if (frac > 35)
 148                 frac = 35;
 149 
 150         spin_lock_irqsave(&pfd_lock, flags);
 151         val = readl_relaxed(pfd->reg);
 152         val &= ~(CLK_PFDV2_FRAC_MASK << pfd->frac_off);
 153         val |= frac << pfd->frac_off;
 154         writel_relaxed(val, pfd->reg);
 155         spin_unlock_irqrestore(&pfd_lock, flags);
 156 
 157         return 0;
 158 }
 159 
 160 static const struct clk_ops clk_pfdv2_ops = {
 161         .enable         = clk_pfdv2_enable,
 162         .disable        = clk_pfdv2_disable,
 163         .recalc_rate    = clk_pfdv2_recalc_rate,
 164         .round_rate     = clk_pfdv2_round_rate,
 165         .set_rate       = clk_pfdv2_set_rate,
 166         .is_enabled     = clk_pfdv2_is_enabled,
 167 };
 168 
 169 struct clk_hw *imx_clk_pfdv2(const char *name, const char *parent_name,
 170                              void __iomem *reg, u8 idx)
 171 {
 172         struct clk_init_data init;
 173         struct clk_pfdv2 *pfd;
 174         struct clk_hw *hw;
 175         int ret;
 176 
 177         WARN_ON(idx > 3);
 178 
 179         pfd = kzalloc(sizeof(*pfd), GFP_KERNEL);
 180         if (!pfd)
 181                 return ERR_PTR(-ENOMEM);
 182 
 183         pfd->reg = reg;
 184         pfd->gate_bit = (idx + 1) * 8 - 1;
 185         pfd->vld_bit = pfd->gate_bit - 1;
 186         pfd->frac_off = idx * 8;
 187 
 188         init.name = name;
 189         init.ops = &clk_pfdv2_ops;
 190         init.parent_names = &parent_name;
 191         init.num_parents = 1;
 192         init.flags = CLK_SET_RATE_GATE;
 193 
 194         pfd->hw.init = &init;
 195 
 196         hw = &pfd->hw;
 197         ret = clk_hw_register(NULL, hw);
 198         if (ret) {
 199                 kfree(pfd);
 200                 hw = ERR_PTR(ret);
 201         }
 202 
 203         return hw;
 204 }

/* [<][>][^][v][top][bottom][index][help] */