linux/drivers/clk/at91/clk-generated.c
<<
>>
Prefs
   1/*
   2 *  Copyright (C) 2015 Atmel Corporation,
   3 *                     Nicolas Ferre <nicolas.ferre@atmel.com>
   4 *
   5 * Based on clk-programmable & clk-peripheral drivers by Boris BREZILLON.
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License, or
  10 * (at your option) any later version.
  11 *
  12 */
  13
  14#include <linux/clk-provider.h>
  15#include <linux/clkdev.h>
  16#include <linux/clk/at91_pmc.h>
  17#include <linux/of.h>
  18#include <linux/mfd/syscon.h>
  19#include <linux/regmap.h>
  20
  21#include "pmc.h"
  22
  23#define PERIPHERAL_MAX          64
  24#define PERIPHERAL_ID_MIN       2
  25
  26#define GENERATED_SOURCE_MAX    6
  27#define GENERATED_MAX_DIV       255
  28
  29#define GCK_ID_SSC0             43
  30#define GCK_ID_SSC1             44
  31#define GCK_ID_I2S0             54
  32#define GCK_ID_I2S1             55
  33#define GCK_ID_CLASSD           59
  34#define GCK_INDEX_DT_AUDIO_PLL  5
  35
  36struct clk_generated {
  37        struct clk_hw hw;
  38        struct regmap *regmap;
  39        struct clk_range range;
  40        spinlock_t *lock;
  41        u32 id;
  42        u32 gckdiv;
  43        u8 parent_id;
  44        bool audio_pll_allowed;
  45};
  46
  47#define to_clk_generated(hw) \
  48        container_of(hw, struct clk_generated, hw)
  49
  50static int clk_generated_enable(struct clk_hw *hw)
  51{
  52        struct clk_generated *gck = to_clk_generated(hw);
  53        unsigned long flags;
  54
  55        pr_debug("GCLK: %s, gckdiv = %d, parent id = %d\n",
  56                 __func__, gck->gckdiv, gck->parent_id);
  57
  58        spin_lock_irqsave(gck->lock, flags);
  59        regmap_write(gck->regmap, AT91_PMC_PCR,
  60                     (gck->id & AT91_PMC_PCR_PID_MASK));
  61        regmap_update_bits(gck->regmap, AT91_PMC_PCR,
  62                           AT91_PMC_PCR_GCKDIV_MASK | AT91_PMC_PCR_GCKCSS_MASK |
  63                           AT91_PMC_PCR_CMD | AT91_PMC_PCR_GCKEN,
  64                           AT91_PMC_PCR_GCKCSS(gck->parent_id) |
  65                           AT91_PMC_PCR_CMD |
  66                           AT91_PMC_PCR_GCKDIV(gck->gckdiv) |
  67                           AT91_PMC_PCR_GCKEN);
  68        spin_unlock_irqrestore(gck->lock, flags);
  69        return 0;
  70}
  71
  72static void clk_generated_disable(struct clk_hw *hw)
  73{
  74        struct clk_generated *gck = to_clk_generated(hw);
  75        unsigned long flags;
  76
  77        spin_lock_irqsave(gck->lock, flags);
  78        regmap_write(gck->regmap, AT91_PMC_PCR,
  79                     (gck->id & AT91_PMC_PCR_PID_MASK));
  80        regmap_update_bits(gck->regmap, AT91_PMC_PCR,
  81                           AT91_PMC_PCR_CMD | AT91_PMC_PCR_GCKEN,
  82                           AT91_PMC_PCR_CMD);
  83        spin_unlock_irqrestore(gck->lock, flags);
  84}
  85
  86static int clk_generated_is_enabled(struct clk_hw *hw)
  87{
  88        struct clk_generated *gck = to_clk_generated(hw);
  89        unsigned long flags;
  90        unsigned int status;
  91
  92        spin_lock_irqsave(gck->lock, flags);
  93        regmap_write(gck->regmap, AT91_PMC_PCR,
  94                     (gck->id & AT91_PMC_PCR_PID_MASK));
  95        regmap_read(gck->regmap, AT91_PMC_PCR, &status);
  96        spin_unlock_irqrestore(gck->lock, flags);
  97
  98        return status & AT91_PMC_PCR_GCKEN ? 1 : 0;
  99}
 100
 101static unsigned long
 102clk_generated_recalc_rate(struct clk_hw *hw,
 103                          unsigned long parent_rate)
 104{
 105        struct clk_generated *gck = to_clk_generated(hw);
 106
 107        return DIV_ROUND_CLOSEST(parent_rate, gck->gckdiv + 1);
 108}
 109
 110static void clk_generated_best_diff(struct clk_rate_request *req,
 111                                    struct clk_hw *parent,
 112                                    unsigned long parent_rate, u32 div,
 113                                    int *best_diff, long *best_rate)
 114{
 115        unsigned long tmp_rate;
 116        int tmp_diff;
 117
 118        if (!div)
 119                tmp_rate = parent_rate;
 120        else
 121                tmp_rate = parent_rate / div;
 122        tmp_diff = abs(req->rate - tmp_rate);
 123
 124        if (*best_diff < 0 || *best_diff > tmp_diff) {
 125                *best_rate = tmp_rate;
 126                *best_diff = tmp_diff;
 127                req->best_parent_rate = parent_rate;
 128                req->best_parent_hw = parent;
 129        }
 130}
 131
 132static int clk_generated_determine_rate(struct clk_hw *hw,
 133                                        struct clk_rate_request *req)
 134{
 135        struct clk_generated *gck = to_clk_generated(hw);
 136        struct clk_hw *parent = NULL;
 137        struct clk_rate_request req_parent = *req;
 138        long best_rate = -EINVAL;
 139        unsigned long min_rate, parent_rate;
 140        int best_diff = -1;
 141        int i;
 142        u32 div;
 143
 144        for (i = 0; i < clk_hw_get_num_parents(hw) - 1; i++) {
 145                parent = clk_hw_get_parent_by_index(hw, i);
 146                if (!parent)
 147                        continue;
 148
 149                parent_rate = clk_hw_get_rate(parent);
 150                min_rate = DIV_ROUND_CLOSEST(parent_rate, GENERATED_MAX_DIV + 1);
 151                if (!parent_rate ||
 152                    (gck->range.max && min_rate > gck->range.max))
 153                        continue;
 154
 155                div = DIV_ROUND_CLOSEST(parent_rate, req->rate);
 156
 157                clk_generated_best_diff(req, parent, parent_rate, div,
 158                                        &best_diff, &best_rate);
 159
 160                if (!best_diff)
 161                        break;
 162        }
 163
 164        /*
 165         * The audio_pll rate can be modified, unlike the five others clocks
 166         * that should never be altered.
 167         * The audio_pll can technically be used by multiple consumers. However,
 168         * with the rate locking, the first consumer to enable to clock will be
 169         * the one definitely setting the rate of the clock.
 170         * Since audio IPs are most likely to request the same rate, we enforce
 171         * that the only clks able to modify gck rate are those of audio IPs.
 172         */
 173
 174        if (!gck->audio_pll_allowed)
 175                goto end;
 176
 177        parent = clk_hw_get_parent_by_index(hw, GCK_INDEX_DT_AUDIO_PLL);
 178        if (!parent)
 179                goto end;
 180
 181        for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
 182                req_parent.rate = req->rate * div;
 183                __clk_determine_rate(parent, &req_parent);
 184                clk_generated_best_diff(req, parent, req_parent.rate, div,
 185                                        &best_diff, &best_rate);
 186
 187                if (!best_diff)
 188                        break;
 189        }
 190
 191end:
 192        pr_debug("GCLK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
 193                 __func__, best_rate,
 194                 __clk_get_name((req->best_parent_hw)->clk),
 195                 req->best_parent_rate);
 196
 197        if (best_rate < 0)
 198                return best_rate;
 199
 200        req->rate = best_rate;
 201        return 0;
 202}
 203
 204/* No modification of hardware as we have the flag CLK_SET_PARENT_GATE set */
 205static int clk_generated_set_parent(struct clk_hw *hw, u8 index)
 206{
 207        struct clk_generated *gck = to_clk_generated(hw);
 208
 209        if (index >= clk_hw_get_num_parents(hw))
 210                return -EINVAL;
 211
 212        gck->parent_id = index;
 213        return 0;
 214}
 215
 216static u8 clk_generated_get_parent(struct clk_hw *hw)
 217{
 218        struct clk_generated *gck = to_clk_generated(hw);
 219
 220        return gck->parent_id;
 221}
 222
 223/* No modification of hardware as we have the flag CLK_SET_RATE_GATE set */
 224static int clk_generated_set_rate(struct clk_hw *hw,
 225                                  unsigned long rate,
 226                                  unsigned long parent_rate)
 227{
 228        struct clk_generated *gck = to_clk_generated(hw);
 229        u32 div;
 230
 231        if (!rate)
 232                return -EINVAL;
 233
 234        if (gck->range.max && rate > gck->range.max)
 235                return -EINVAL;
 236
 237        div = DIV_ROUND_CLOSEST(parent_rate, rate);
 238        if (div > GENERATED_MAX_DIV + 1 || !div)
 239                return -EINVAL;
 240
 241        gck->gckdiv = div - 1;
 242        return 0;
 243}
 244
 245static const struct clk_ops generated_ops = {
 246        .enable = clk_generated_enable,
 247        .disable = clk_generated_disable,
 248        .is_enabled = clk_generated_is_enabled,
 249        .recalc_rate = clk_generated_recalc_rate,
 250        .determine_rate = clk_generated_determine_rate,
 251        .get_parent = clk_generated_get_parent,
 252        .set_parent = clk_generated_set_parent,
 253        .set_rate = clk_generated_set_rate,
 254};
 255
 256/**
 257 * clk_generated_startup - Initialize a given clock to its default parent and
 258 * divisor parameter.
 259 *
 260 * @gck:        Generated clock to set the startup parameters for.
 261 *
 262 * Take parameters from the hardware and update local clock configuration
 263 * accordingly.
 264 */
 265static void clk_generated_startup(struct clk_generated *gck)
 266{
 267        u32 tmp;
 268        unsigned long flags;
 269
 270        spin_lock_irqsave(gck->lock, flags);
 271        regmap_write(gck->regmap, AT91_PMC_PCR,
 272                     (gck->id & AT91_PMC_PCR_PID_MASK));
 273        regmap_read(gck->regmap, AT91_PMC_PCR, &tmp);
 274        spin_unlock_irqrestore(gck->lock, flags);
 275
 276        gck->parent_id = (tmp & AT91_PMC_PCR_GCKCSS_MASK)
 277                                        >> AT91_PMC_PCR_GCKCSS_OFFSET;
 278        gck->gckdiv = (tmp & AT91_PMC_PCR_GCKDIV_MASK)
 279                                        >> AT91_PMC_PCR_GCKDIV_OFFSET;
 280}
 281
 282static struct clk_hw * __init
 283at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
 284                            const char *name, const char **parent_names,
 285                            u8 num_parents, u8 id,
 286                            const struct clk_range *range)
 287{
 288        struct clk_generated *gck;
 289        struct clk_init_data init;
 290        struct clk_hw *hw;
 291        int ret;
 292
 293        gck = kzalloc(sizeof(*gck), GFP_KERNEL);
 294        if (!gck)
 295                return ERR_PTR(-ENOMEM);
 296
 297        init.name = name;
 298        init.ops = &generated_ops;
 299        init.parent_names = parent_names;
 300        init.num_parents = num_parents;
 301        init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
 302                CLK_SET_RATE_PARENT;
 303
 304        gck->id = id;
 305        gck->hw.init = &init;
 306        gck->regmap = regmap;
 307        gck->lock = lock;
 308        gck->range = *range;
 309
 310        clk_generated_startup(gck);
 311        hw = &gck->hw;
 312        ret = clk_hw_register(NULL, &gck->hw);
 313        if (ret) {
 314                kfree(gck);
 315                hw = ERR_PTR(ret);
 316        } else {
 317                pmc_register_id(id);
 318        }
 319
 320        return hw;
 321}
 322
 323static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
 324{
 325        int num;
 326        u32 id;
 327        const char *name;
 328        struct clk_hw *hw;
 329        unsigned int num_parents;
 330        const char *parent_names[GENERATED_SOURCE_MAX];
 331        struct device_node *gcknp;
 332        struct clk_range range = CLK_RANGE(0, 0);
 333        struct regmap *regmap;
 334        struct clk_generated *gck;
 335
 336        num_parents = of_clk_get_parent_count(np);
 337        if (num_parents == 0 || num_parents > GENERATED_SOURCE_MAX)
 338                return;
 339
 340        of_clk_parent_fill(np, parent_names, num_parents);
 341
 342        num = of_get_child_count(np);
 343        if (!num || num > PERIPHERAL_MAX)
 344                return;
 345
 346        regmap = syscon_node_to_regmap(of_get_parent(np));
 347        if (IS_ERR(regmap))
 348                return;
 349
 350        for_each_child_of_node(np, gcknp) {
 351                if (of_property_read_u32(gcknp, "reg", &id))
 352                        continue;
 353
 354                if (id < PERIPHERAL_ID_MIN || id >= PERIPHERAL_MAX)
 355                        continue;
 356
 357                if (of_property_read_string(np, "clock-output-names", &name))
 358                        name = gcknp->name;
 359
 360                of_at91_get_clk_range(gcknp, "atmel,clk-output-range",
 361                                      &range);
 362
 363                hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, name,
 364                                                  parent_names, num_parents,
 365                                                  id, &range);
 366
 367                gck = to_clk_generated(hw);
 368
 369                if (of_device_is_compatible(np,
 370                                            "atmel,sama5d2-clk-generated")) {
 371                        if (gck->id == GCK_ID_SSC0 || gck->id == GCK_ID_SSC1 ||
 372                            gck->id == GCK_ID_I2S0 || gck->id == GCK_ID_I2S1 ||
 373                            gck->id == GCK_ID_CLASSD)
 374                                gck->audio_pll_allowed = true;
 375                        else
 376                                gck->audio_pll_allowed = false;
 377                } else {
 378                        gck->audio_pll_allowed = false;
 379                }
 380
 381                if (IS_ERR(hw))
 382                        continue;
 383
 384                of_clk_add_hw_provider(gcknp, of_clk_hw_simple_get, hw);
 385        }
 386}
 387CLK_OF_DECLARE(of_sama5d2_clk_generated_setup, "atmel,sama5d2-clk-generated",
 388               of_sama5d2_clk_generated_setup);
 389