root/drivers/clk/at91/clk-master.c

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

DEFINITIONS

This source file includes following definitions.
  1. clk_master_ready
  2. clk_master_prepare
  3. clk_master_is_prepared
  4. clk_master_recalc_rate
  5. clk_master_get_parent
  6. at91_clk_register_master

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
   4  */
   5 
   6 #include <linux/clk-provider.h>
   7 #include <linux/clkdev.h>
   8 #include <linux/clk/at91_pmc.h>
   9 #include <linux/of.h>
  10 #include <linux/mfd/syscon.h>
  11 #include <linux/regmap.h>
  12 
  13 #include "pmc.h"
  14 
  15 #define MASTER_PRES_MASK        0x7
  16 #define MASTER_PRES_MAX         MASTER_PRES_MASK
  17 #define MASTER_DIV_SHIFT        8
  18 #define MASTER_DIV_MASK         0x3
  19 
  20 #define to_clk_master(hw) container_of(hw, struct clk_master, hw)
  21 
  22 struct clk_master {
  23         struct clk_hw hw;
  24         struct regmap *regmap;
  25         const struct clk_master_layout *layout;
  26         const struct clk_master_characteristics *characteristics;
  27         u32 mckr;
  28 };
  29 
  30 static inline bool clk_master_ready(struct regmap *regmap)
  31 {
  32         unsigned int status;
  33 
  34         regmap_read(regmap, AT91_PMC_SR, &status);
  35 
  36         return status & AT91_PMC_MCKRDY ? 1 : 0;
  37 }
  38 
  39 static int clk_master_prepare(struct clk_hw *hw)
  40 {
  41         struct clk_master *master = to_clk_master(hw);
  42 
  43         while (!clk_master_ready(master->regmap))
  44                 cpu_relax();
  45 
  46         return 0;
  47 }
  48 
  49 static int clk_master_is_prepared(struct clk_hw *hw)
  50 {
  51         struct clk_master *master = to_clk_master(hw);
  52 
  53         return clk_master_ready(master->regmap);
  54 }
  55 
  56 static unsigned long clk_master_recalc_rate(struct clk_hw *hw,
  57                                             unsigned long parent_rate)
  58 {
  59         u8 pres;
  60         u8 div;
  61         unsigned long rate = parent_rate;
  62         struct clk_master *master = to_clk_master(hw);
  63         const struct clk_master_layout *layout = master->layout;
  64         const struct clk_master_characteristics *characteristics =
  65                                                 master->characteristics;
  66         unsigned int mckr;
  67 
  68         regmap_read(master->regmap, master->layout->offset, &mckr);
  69         mckr &= layout->mask;
  70 
  71         pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
  72         div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
  73 
  74         if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
  75                 rate /= 3;
  76         else
  77                 rate >>= pres;
  78 
  79         rate /= characteristics->divisors[div];
  80 
  81         if (rate < characteristics->output.min)
  82                 pr_warn("master clk is underclocked");
  83         else if (rate > characteristics->output.max)
  84                 pr_warn("master clk is overclocked");
  85 
  86         return rate;
  87 }
  88 
  89 static u8 clk_master_get_parent(struct clk_hw *hw)
  90 {
  91         struct clk_master *master = to_clk_master(hw);
  92         unsigned int mckr;
  93 
  94         regmap_read(master->regmap, master->layout->offset, &mckr);
  95 
  96         return mckr & AT91_PMC_CSS;
  97 }
  98 
  99 static const struct clk_ops master_ops = {
 100         .prepare = clk_master_prepare,
 101         .is_prepared = clk_master_is_prepared,
 102         .recalc_rate = clk_master_recalc_rate,
 103         .get_parent = clk_master_get_parent,
 104 };
 105 
 106 struct clk_hw * __init
 107 at91_clk_register_master(struct regmap *regmap,
 108                 const char *name, int num_parents,
 109                 const char **parent_names,
 110                 const struct clk_master_layout *layout,
 111                 const struct clk_master_characteristics *characteristics)
 112 {
 113         struct clk_master *master;
 114         struct clk_init_data init;
 115         struct clk_hw *hw;
 116         int ret;
 117 
 118         if (!name || !num_parents || !parent_names)
 119                 return ERR_PTR(-EINVAL);
 120 
 121         master = kzalloc(sizeof(*master), GFP_KERNEL);
 122         if (!master)
 123                 return ERR_PTR(-ENOMEM);
 124 
 125         init.name = name;
 126         init.ops = &master_ops;
 127         init.parent_names = parent_names;
 128         init.num_parents = num_parents;
 129         init.flags = 0;
 130 
 131         master->hw.init = &init;
 132         master->layout = layout;
 133         master->characteristics = characteristics;
 134         master->regmap = regmap;
 135 
 136         hw = &master->hw;
 137         ret = clk_hw_register(NULL, &master->hw);
 138         if (ret) {
 139                 kfree(master);
 140                 hw = ERR_PTR(ret);
 141         }
 142 
 143         return hw;
 144 }
 145 
 146 const struct clk_master_layout at91rm9200_master_layout = {
 147         .mask = 0x31F,
 148         .pres_shift = 2,
 149         .offset = AT91_PMC_MCKR,
 150 };
 151 
 152 const struct clk_master_layout at91sam9x5_master_layout = {
 153         .mask = 0x373,
 154         .pres_shift = 4,
 155         .offset = AT91_PMC_MCKR,
 156 };

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