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