linux/drivers/clk/at91/clk-peripheral.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
  20DEFINE_SPINLOCK(pmc_pcr_lock);
  21
  22#define PERIPHERAL_MAX          64
  23
  24#define PERIPHERAL_AT91RM9200   0
  25#define PERIPHERAL_AT91SAM9X5   1
  26
  27#define PERIPHERAL_ID_MIN       2
  28#define PERIPHERAL_ID_MAX       31
  29#define PERIPHERAL_MASK(id)     (1 << ((id) & PERIPHERAL_ID_MAX))
  30
  31#define PERIPHERAL_RSHIFT_MASK  0x3
  32#define PERIPHERAL_RSHIFT(val)  (((val) >> 16) & PERIPHERAL_RSHIFT_MASK)
  33
  34#define PERIPHERAL_MAX_SHIFT    3
  35
  36struct clk_peripheral {
  37        struct clk_hw hw;
  38        struct regmap *regmap;
  39        u32 id;
  40};
  41
  42#define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw)
  43
  44struct clk_sam9x5_peripheral {
  45        struct clk_hw hw;
  46        struct regmap *regmap;
  47        struct clk_range range;
  48        spinlock_t *lock;
  49        u32 id;
  50        u32 div;
  51        bool auto_div;
  52};
  53
  54#define to_clk_sam9x5_peripheral(hw) \
  55        container_of(hw, struct clk_sam9x5_peripheral, hw)
  56
  57static int clk_peripheral_enable(struct clk_hw *hw)
  58{
  59        struct clk_peripheral *periph = to_clk_peripheral(hw);
  60        int offset = AT91_PMC_PCER;
  61        u32 id = periph->id;
  62
  63        if (id < PERIPHERAL_ID_MIN)
  64                return 0;
  65        if (id > PERIPHERAL_ID_MAX)
  66                offset = AT91_PMC_PCER1;
  67        regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
  68
  69        return 0;
  70}
  71
  72static void clk_peripheral_disable(struct clk_hw *hw)
  73{
  74        struct clk_peripheral *periph = to_clk_peripheral(hw);
  75        int offset = AT91_PMC_PCDR;
  76        u32 id = periph->id;
  77
  78        if (id < PERIPHERAL_ID_MIN)
  79                return;
  80        if (id > PERIPHERAL_ID_MAX)
  81                offset = AT91_PMC_PCDR1;
  82        regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
  83}
  84
  85static int clk_peripheral_is_enabled(struct clk_hw *hw)
  86{
  87        struct clk_peripheral *periph = to_clk_peripheral(hw);
  88        int offset = AT91_PMC_PCSR;
  89        unsigned int status;
  90        u32 id = periph->id;
  91
  92        if (id < PERIPHERAL_ID_MIN)
  93                return 1;
  94        if (id > PERIPHERAL_ID_MAX)
  95                offset = AT91_PMC_PCSR1;
  96        regmap_read(periph->regmap, offset, &status);
  97
  98        return status & PERIPHERAL_MASK(id) ? 1 : 0;
  99}
 100
 101static const struct clk_ops peripheral_ops = {
 102        .enable = clk_peripheral_enable,
 103        .disable = clk_peripheral_disable,
 104        .is_enabled = clk_peripheral_is_enabled,
 105};
 106
 107static struct clk_hw * __init
 108at91_clk_register_peripheral(struct regmap *regmap, const char *name,
 109                             const char *parent_name, u32 id)
 110{
 111        struct clk_peripheral *periph;
 112        struct clk_init_data init;
 113        struct clk_hw *hw;
 114        int ret;
 115
 116        if (!name || !parent_name || id > PERIPHERAL_ID_MAX)
 117                return ERR_PTR(-EINVAL);
 118
 119        periph = kzalloc(sizeof(*periph), GFP_KERNEL);
 120        if (!periph)
 121                return ERR_PTR(-ENOMEM);
 122
 123        init.name = name;
 124        init.ops = &peripheral_ops;
 125        init.parent_names = (parent_name ? &parent_name : NULL);
 126        init.num_parents = (parent_name ? 1 : 0);
 127        init.flags = 0;
 128
 129        periph->id = id;
 130        periph->hw.init = &init;
 131        periph->regmap = regmap;
 132
 133        hw = &periph->hw;
 134        ret = clk_hw_register(NULL, &periph->hw);
 135        if (ret) {
 136                kfree(periph);
 137                hw = ERR_PTR(ret);
 138        }
 139
 140        return hw;
 141}
 142
 143static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
 144{
 145        struct clk_hw *parent;
 146        unsigned long parent_rate;
 147        int shift = 0;
 148
 149        if (!periph->auto_div)
 150                return;
 151
 152        if (periph->range.max) {
 153                parent = clk_hw_get_parent_by_index(&periph->hw, 0);
 154                parent_rate = clk_hw_get_rate(parent);
 155                if (!parent_rate)
 156                        return;
 157
 158                for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
 159                        if (parent_rate >> shift <= periph->range.max)
 160                                break;
 161                }
 162        }
 163
 164        periph->auto_div = false;
 165        periph->div = shift;
 166}
 167
 168static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
 169{
 170        struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
 171        unsigned long flags;
 172
 173        if (periph->id < PERIPHERAL_ID_MIN)
 174                return 0;
 175
 176        spin_lock_irqsave(periph->lock, flags);
 177        regmap_write(periph->regmap, AT91_PMC_PCR,
 178                     (periph->id & AT91_PMC_PCR_PID_MASK));
 179        regmap_update_bits(periph->regmap, AT91_PMC_PCR,
 180                           AT91_PMC_PCR_DIV_MASK | AT91_PMC_PCR_CMD |
 181                           AT91_PMC_PCR_EN,
 182                           AT91_PMC_PCR_DIV(periph->div) |
 183                           AT91_PMC_PCR_CMD |
 184                           AT91_PMC_PCR_EN);
 185        spin_unlock_irqrestore(periph->lock, flags);
 186
 187        return 0;
 188}
 189
 190static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
 191{
 192        struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
 193        unsigned long flags;
 194
 195        if (periph->id < PERIPHERAL_ID_MIN)
 196                return;
 197
 198        spin_lock_irqsave(periph->lock, flags);
 199        regmap_write(periph->regmap, AT91_PMC_PCR,
 200                     (periph->id & AT91_PMC_PCR_PID_MASK));
 201        regmap_update_bits(periph->regmap, AT91_PMC_PCR,
 202                           AT91_PMC_PCR_EN | AT91_PMC_PCR_CMD,
 203                           AT91_PMC_PCR_CMD);
 204        spin_unlock_irqrestore(periph->lock, flags);
 205}
 206
 207static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
 208{
 209        struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
 210        unsigned long flags;
 211        unsigned int status;
 212
 213        if (periph->id < PERIPHERAL_ID_MIN)
 214                return 1;
 215
 216        spin_lock_irqsave(periph->lock, flags);
 217        regmap_write(periph->regmap, AT91_PMC_PCR,
 218                     (periph->id & AT91_PMC_PCR_PID_MASK));
 219        regmap_read(periph->regmap, AT91_PMC_PCR, &status);
 220        spin_unlock_irqrestore(periph->lock, flags);
 221
 222        return status & AT91_PMC_PCR_EN ? 1 : 0;
 223}
 224
 225static unsigned long
 226clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
 227                                  unsigned long parent_rate)
 228{
 229        struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
 230        unsigned long flags;
 231        unsigned int status;
 232
 233        if (periph->id < PERIPHERAL_ID_MIN)
 234                return parent_rate;
 235
 236        spin_lock_irqsave(periph->lock, flags);
 237        regmap_write(periph->regmap, AT91_PMC_PCR,
 238                     (periph->id & AT91_PMC_PCR_PID_MASK));
 239        regmap_read(periph->regmap, AT91_PMC_PCR, &status);
 240        spin_unlock_irqrestore(periph->lock, flags);
 241
 242        if (status & AT91_PMC_PCR_EN) {
 243                periph->div = PERIPHERAL_RSHIFT(status);
 244                periph->auto_div = false;
 245        } else {
 246                clk_sam9x5_peripheral_autodiv(periph);
 247        }
 248
 249        return parent_rate >> periph->div;
 250}
 251
 252static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
 253                                             unsigned long rate,
 254                                             unsigned long *parent_rate)
 255{
 256        int shift = 0;
 257        unsigned long best_rate;
 258        unsigned long best_diff;
 259        unsigned long cur_rate = *parent_rate;
 260        unsigned long cur_diff;
 261        struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
 262
 263        if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
 264                return *parent_rate;
 265
 266        if (periph->range.max) {
 267                for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
 268                        cur_rate = *parent_rate >> shift;
 269                        if (cur_rate <= periph->range.max)
 270                                break;
 271                }
 272        }
 273
 274        if (rate >= cur_rate)
 275                return cur_rate;
 276
 277        best_diff = cur_rate - rate;
 278        best_rate = cur_rate;
 279        for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
 280                cur_rate = *parent_rate >> shift;
 281                if (cur_rate < rate)
 282                        cur_diff = rate - cur_rate;
 283                else
 284                        cur_diff = cur_rate - rate;
 285
 286                if (cur_diff < best_diff) {
 287                        best_diff = cur_diff;
 288                        best_rate = cur_rate;
 289                }
 290
 291                if (!best_diff || cur_rate < rate)
 292                        break;
 293        }
 294
 295        return best_rate;
 296}
 297
 298static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
 299                                          unsigned long rate,
 300                                          unsigned long parent_rate)
 301{
 302        int shift;
 303        struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
 304        if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
 305                if (parent_rate == rate)
 306                        return 0;
 307                else
 308                        return -EINVAL;
 309        }
 310
 311        if (periph->range.max && rate > periph->range.max)
 312                return -EINVAL;
 313
 314        for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
 315                if (parent_rate >> shift == rate) {
 316                        periph->auto_div = false;
 317                        periph->div = shift;
 318                        return 0;
 319                }
 320        }
 321
 322        return -EINVAL;
 323}
 324
 325static const struct clk_ops sam9x5_peripheral_ops = {
 326        .enable = clk_sam9x5_peripheral_enable,
 327        .disable = clk_sam9x5_peripheral_disable,
 328        .is_enabled = clk_sam9x5_peripheral_is_enabled,
 329        .recalc_rate = clk_sam9x5_peripheral_recalc_rate,
 330        .round_rate = clk_sam9x5_peripheral_round_rate,
 331        .set_rate = clk_sam9x5_peripheral_set_rate,
 332};
 333
 334static struct clk_hw * __init
 335at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
 336                                    const char *name, const char *parent_name,
 337                                    u32 id, const struct clk_range *range)
 338{
 339        struct clk_sam9x5_peripheral *periph;
 340        struct clk_init_data init;
 341        struct clk_hw *hw;
 342        int ret;
 343
 344        if (!name || !parent_name)
 345                return ERR_PTR(-EINVAL);
 346
 347        periph = kzalloc(sizeof(*periph), GFP_KERNEL);
 348        if (!periph)
 349                return ERR_PTR(-ENOMEM);
 350
 351        init.name = name;
 352        init.ops = &sam9x5_peripheral_ops;
 353        init.parent_names = (parent_name ? &parent_name : NULL);
 354        init.num_parents = (parent_name ? 1 : 0);
 355        init.flags = 0;
 356
 357        periph->id = id;
 358        periph->hw.init = &init;
 359        periph->div = 0;
 360        periph->regmap = regmap;
 361        periph->lock = lock;
 362        periph->auto_div = true;
 363        periph->range = *range;
 364
 365        hw = &periph->hw;
 366        ret = clk_hw_register(NULL, &periph->hw);
 367        if (ret) {
 368                kfree(periph);
 369                hw = ERR_PTR(ret);
 370        } else {
 371                clk_sam9x5_peripheral_autodiv(periph);
 372                pmc_register_id(id);
 373        }
 374
 375        return hw;
 376}
 377
 378static void __init
 379of_at91_clk_periph_setup(struct device_node *np, u8 type)
 380{
 381        int num;
 382        u32 id;
 383        struct clk_hw *hw;
 384        const char *parent_name;
 385        const char *name;
 386        struct device_node *periphclknp;
 387        struct regmap *regmap;
 388
 389        parent_name = of_clk_get_parent_name(np, 0);
 390        if (!parent_name)
 391                return;
 392
 393        num = of_get_child_count(np);
 394        if (!num || num > PERIPHERAL_MAX)
 395                return;
 396
 397        regmap = syscon_node_to_regmap(of_get_parent(np));
 398        if (IS_ERR(regmap))
 399                return;
 400
 401        for_each_child_of_node(np, periphclknp) {
 402                if (of_property_read_u32(periphclknp, "reg", &id))
 403                        continue;
 404
 405                if (id >= PERIPHERAL_MAX)
 406                        continue;
 407
 408                if (of_property_read_string(np, "clock-output-names", &name))
 409                        name = periphclknp->name;
 410
 411                if (type == PERIPHERAL_AT91RM9200) {
 412                        hw = at91_clk_register_peripheral(regmap, name,
 413                                                           parent_name, id);
 414                } else {
 415                        struct clk_range range = CLK_RANGE(0, 0);
 416
 417                        of_at91_get_clk_range(periphclknp,
 418                                              "atmel,clk-output-range",
 419                                              &range);
 420
 421                        hw = at91_clk_register_sam9x5_peripheral(regmap,
 422                                                                  &pmc_pcr_lock,
 423                                                                  name,
 424                                                                  parent_name,
 425                                                                  id, &range);
 426                }
 427
 428                if (IS_ERR(hw))
 429                        continue;
 430
 431                of_clk_add_hw_provider(periphclknp, of_clk_hw_simple_get, hw);
 432        }
 433}
 434
 435static void __init of_at91rm9200_clk_periph_setup(struct device_node *np)
 436{
 437        of_at91_clk_periph_setup(np, PERIPHERAL_AT91RM9200);
 438}
 439CLK_OF_DECLARE(at91rm9200_clk_periph, "atmel,at91rm9200-clk-peripheral",
 440               of_at91rm9200_clk_periph_setup);
 441
 442static void __init of_at91sam9x5_clk_periph_setup(struct device_node *np)
 443{
 444        of_at91_clk_periph_setup(np, PERIPHERAL_AT91SAM9X5);
 445}
 446CLK_OF_DECLARE(at91sam9x5_clk_periph, "atmel,at91sam9x5-clk-peripheral",
 447               of_at91sam9x5_clk_periph_setup);
 448
 449