linux/drivers/clk/at91/clk-programmable.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 PROG_SOURCE_MAX         5
  21#define PROG_ID_MAX             7
  22
  23#define PROG_STATUS_MASK(id)    (1 << ((id) + 8))
  24#define PROG_PRES_MASK          0x7
  25#define PROG_PRES(layout, pckr) ((pckr >> layout->pres_shift) & PROG_PRES_MASK)
  26#define PROG_MAX_RM9200_CSS     3
  27
  28struct clk_programmable_layout {
  29        u8 pres_shift;
  30        u8 css_mask;
  31        u8 have_slck_mck;
  32};
  33
  34struct clk_programmable {
  35        struct clk_hw hw;
  36        struct regmap *regmap;
  37        u8 id;
  38        const struct clk_programmable_layout *layout;
  39};
  40
  41#define to_clk_programmable(hw) container_of(hw, struct clk_programmable, hw)
  42
  43static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw,
  44                                                  unsigned long parent_rate)
  45{
  46        struct clk_programmable *prog = to_clk_programmable(hw);
  47        unsigned int pckr;
  48
  49        regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr);
  50
  51        return parent_rate >> PROG_PRES(prog->layout, pckr);
  52}
  53
  54static int clk_programmable_determine_rate(struct clk_hw *hw,
  55                                           struct clk_rate_request *req)
  56{
  57        struct clk_hw *parent;
  58        long best_rate = -EINVAL;
  59        unsigned long parent_rate;
  60        unsigned long tmp_rate;
  61        int shift;
  62        int i;
  63
  64        for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
  65                parent = clk_hw_get_parent_by_index(hw, i);
  66                if (!parent)
  67                        continue;
  68
  69                parent_rate = clk_hw_get_rate(parent);
  70                for (shift = 0; shift < PROG_PRES_MASK; shift++) {
  71                        tmp_rate = parent_rate >> shift;
  72                        if (tmp_rate <= req->rate)
  73                                break;
  74                }
  75
  76                if (tmp_rate > req->rate)
  77                        continue;
  78
  79                if (best_rate < 0 ||
  80                    (req->rate - tmp_rate) < (req->rate - best_rate)) {
  81                        best_rate = tmp_rate;
  82                        req->best_parent_rate = parent_rate;
  83                        req->best_parent_hw = parent;
  84                }
  85
  86                if (!best_rate)
  87                        break;
  88        }
  89
  90        if (best_rate < 0)
  91                return best_rate;
  92
  93        req->rate = best_rate;
  94        return 0;
  95}
  96
  97static int clk_programmable_set_parent(struct clk_hw *hw, u8 index)
  98{
  99        struct clk_programmable *prog = to_clk_programmable(hw);
 100        const struct clk_programmable_layout *layout = prog->layout;
 101        unsigned int mask = layout->css_mask;
 102        unsigned int pckr = index;
 103
 104        if (layout->have_slck_mck)
 105                mask |= AT91_PMC_CSSMCK_MCK;
 106
 107        if (index > layout->css_mask) {
 108                if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck)
 109                        return -EINVAL;
 110
 111                pckr |= AT91_PMC_CSSMCK_MCK;
 112        }
 113
 114        regmap_update_bits(prog->regmap, AT91_PMC_PCKR(prog->id), mask, pckr);
 115
 116        return 0;
 117}
 118
 119static u8 clk_programmable_get_parent(struct clk_hw *hw)
 120{
 121        struct clk_programmable *prog = to_clk_programmable(hw);
 122        const struct clk_programmable_layout *layout = prog->layout;
 123        unsigned int pckr;
 124        u8 ret;
 125
 126        regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr);
 127
 128        ret = pckr & layout->css_mask;
 129
 130        if (layout->have_slck_mck && (pckr & AT91_PMC_CSSMCK_MCK) && !ret)
 131                ret = PROG_MAX_RM9200_CSS + 1;
 132
 133        return ret;
 134}
 135
 136static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate,
 137                                     unsigned long parent_rate)
 138{
 139        struct clk_programmable *prog = to_clk_programmable(hw);
 140        const struct clk_programmable_layout *layout = prog->layout;
 141        unsigned long div = parent_rate / rate;
 142        unsigned int pckr;
 143        int shift = 0;
 144
 145        regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr);
 146
 147        if (!div)
 148                return -EINVAL;
 149
 150        shift = fls(div) - 1;
 151
 152        if (div != (1 << shift))
 153                return -EINVAL;
 154
 155        if (shift >= PROG_PRES_MASK)
 156                return -EINVAL;
 157
 158        regmap_update_bits(prog->regmap, AT91_PMC_PCKR(prog->id),
 159                           PROG_PRES_MASK << layout->pres_shift,
 160                           shift << layout->pres_shift);
 161
 162        return 0;
 163}
 164
 165static const struct clk_ops programmable_ops = {
 166        .recalc_rate = clk_programmable_recalc_rate,
 167        .determine_rate = clk_programmable_determine_rate,
 168        .get_parent = clk_programmable_get_parent,
 169        .set_parent = clk_programmable_set_parent,
 170        .set_rate = clk_programmable_set_rate,
 171};
 172
 173static struct clk_hw * __init
 174at91_clk_register_programmable(struct regmap *regmap,
 175                               const char *name, const char **parent_names,
 176                               u8 num_parents, u8 id,
 177                               const struct clk_programmable_layout *layout)
 178{
 179        struct clk_programmable *prog;
 180        struct clk_hw *hw;
 181        struct clk_init_data init;
 182        int ret;
 183
 184        if (id > PROG_ID_MAX)
 185                return ERR_PTR(-EINVAL);
 186
 187        prog = kzalloc(sizeof(*prog), GFP_KERNEL);
 188        if (!prog)
 189                return ERR_PTR(-ENOMEM);
 190
 191        init.name = name;
 192        init.ops = &programmable_ops;
 193        init.parent_names = parent_names;
 194        init.num_parents = num_parents;
 195        init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
 196
 197        prog->id = id;
 198        prog->layout = layout;
 199        prog->hw.init = &init;
 200        prog->regmap = regmap;
 201
 202        hw = &prog->hw;
 203        ret = clk_hw_register(NULL, &prog->hw);
 204        if (ret) {
 205                kfree(prog);
 206                hw = ERR_PTR(ret);
 207        } else {
 208                pmc_register_pck(id);
 209        }
 210
 211        return hw;
 212}
 213
 214static const struct clk_programmable_layout at91rm9200_programmable_layout = {
 215        .pres_shift = 2,
 216        .css_mask = 0x3,
 217        .have_slck_mck = 0,
 218};
 219
 220static const struct clk_programmable_layout at91sam9g45_programmable_layout = {
 221        .pres_shift = 2,
 222        .css_mask = 0x3,
 223        .have_slck_mck = 1,
 224};
 225
 226static const struct clk_programmable_layout at91sam9x5_programmable_layout = {
 227        .pres_shift = 4,
 228        .css_mask = 0x7,
 229        .have_slck_mck = 0,
 230};
 231
 232static void __init
 233of_at91_clk_prog_setup(struct device_node *np,
 234                       const struct clk_programmable_layout *layout)
 235{
 236        int num;
 237        u32 id;
 238        struct clk_hw *hw;
 239        unsigned int num_parents;
 240        const char *parent_names[PROG_SOURCE_MAX];
 241        const char *name;
 242        struct device_node *progclknp;
 243        struct regmap *regmap;
 244
 245        num_parents = of_clk_get_parent_count(np);
 246        if (num_parents == 0 || num_parents > PROG_SOURCE_MAX)
 247                return;
 248
 249        of_clk_parent_fill(np, parent_names, num_parents);
 250
 251        num = of_get_child_count(np);
 252        if (!num || num > (PROG_ID_MAX + 1))
 253                return;
 254
 255        regmap = syscon_node_to_regmap(of_get_parent(np));
 256        if (IS_ERR(regmap))
 257                return;
 258
 259        for_each_child_of_node(np, progclknp) {
 260                if (of_property_read_u32(progclknp, "reg", &id))
 261                        continue;
 262
 263                if (of_property_read_string(np, "clock-output-names", &name))
 264                        name = progclknp->name;
 265
 266                hw = at91_clk_register_programmable(regmap, name,
 267                                                     parent_names, num_parents,
 268                                                     id, layout);
 269                if (IS_ERR(hw))
 270                        continue;
 271
 272                of_clk_add_hw_provider(progclknp, of_clk_hw_simple_get, hw);
 273        }
 274}
 275
 276
 277static void __init of_at91rm9200_clk_prog_setup(struct device_node *np)
 278{
 279        of_at91_clk_prog_setup(np, &at91rm9200_programmable_layout);
 280}
 281CLK_OF_DECLARE(at91rm9200_clk_prog, "atmel,at91rm9200-clk-programmable",
 282               of_at91rm9200_clk_prog_setup);
 283
 284static void __init of_at91sam9g45_clk_prog_setup(struct device_node *np)
 285{
 286        of_at91_clk_prog_setup(np, &at91sam9g45_programmable_layout);
 287}
 288CLK_OF_DECLARE(at91sam9g45_clk_prog, "atmel,at91sam9g45-clk-programmable",
 289               of_at91sam9g45_clk_prog_setup);
 290
 291static void __init of_at91sam9x5_clk_prog_setup(struct device_node *np)
 292{
 293        of_at91_clk_prog_setup(np, &at91sam9x5_programmable_layout);
 294}
 295CLK_OF_DECLARE(at91sam9x5_clk_prog, "atmel,at91sam9x5-clk-programmable",
 296               of_at91sam9x5_clk_prog_setup);
 297