linux/drivers/clk/clk-highbank.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright 2011-2012 Calxeda, Inc.
   4 */
   5
   6#include <linux/kernel.h>
   7#include <linux/slab.h>
   8#include <linux/err.h>
   9#include <linux/clk-provider.h>
  10#include <linux/io.h>
  11#include <linux/of.h>
  12#include <linux/of_address.h>
  13
  14#define HB_PLL_LOCK_500         0x20000000
  15#define HB_PLL_LOCK             0x10000000
  16#define HB_PLL_DIVF_SHIFT       20
  17#define HB_PLL_DIVF_MASK        0x0ff00000
  18#define HB_PLL_DIVQ_SHIFT       16
  19#define HB_PLL_DIVQ_MASK        0x00070000
  20#define HB_PLL_DIVR_SHIFT       8
  21#define HB_PLL_DIVR_MASK        0x00001f00
  22#define HB_PLL_RANGE_SHIFT      4
  23#define HB_PLL_RANGE_MASK       0x00000070
  24#define HB_PLL_BYPASS           0x00000008
  25#define HB_PLL_RESET            0x00000004
  26#define HB_PLL_EXT_BYPASS       0x00000002
  27#define HB_PLL_EXT_ENA          0x00000001
  28
  29#define HB_PLL_VCO_MIN_FREQ     2133000000
  30#define HB_PLL_MAX_FREQ         HB_PLL_VCO_MIN_FREQ
  31#define HB_PLL_MIN_FREQ         (HB_PLL_VCO_MIN_FREQ / 64)
  32
  33#define HB_A9_BCLK_DIV_MASK     0x00000006
  34#define HB_A9_BCLK_DIV_SHIFT    1
  35#define HB_A9_PCLK_DIV          0x00000001
  36
  37struct hb_clk {
  38        struct clk_hw   hw;
  39        void __iomem    *reg;
  40        char *parent_name;
  41};
  42#define to_hb_clk(p) container_of(p, struct hb_clk, hw)
  43
  44static int clk_pll_prepare(struct clk_hw *hwclk)
  45        {
  46        struct hb_clk *hbclk = to_hb_clk(hwclk);
  47        u32 reg;
  48
  49        reg = readl(hbclk->reg);
  50        reg &= ~HB_PLL_RESET;
  51        writel(reg, hbclk->reg);
  52
  53        while ((readl(hbclk->reg) & HB_PLL_LOCK) == 0)
  54                ;
  55        while ((readl(hbclk->reg) & HB_PLL_LOCK_500) == 0)
  56                ;
  57
  58        return 0;
  59}
  60
  61static void clk_pll_unprepare(struct clk_hw *hwclk)
  62{
  63        struct hb_clk *hbclk = to_hb_clk(hwclk);
  64        u32 reg;
  65
  66        reg = readl(hbclk->reg);
  67        reg |= HB_PLL_RESET;
  68        writel(reg, hbclk->reg);
  69}
  70
  71static int clk_pll_enable(struct clk_hw *hwclk)
  72{
  73        struct hb_clk *hbclk = to_hb_clk(hwclk);
  74        u32 reg;
  75
  76        reg = readl(hbclk->reg);
  77        reg |= HB_PLL_EXT_ENA;
  78        writel(reg, hbclk->reg);
  79
  80        return 0;
  81}
  82
  83static void clk_pll_disable(struct clk_hw *hwclk)
  84{
  85        struct hb_clk *hbclk = to_hb_clk(hwclk);
  86        u32 reg;
  87
  88        reg = readl(hbclk->reg);
  89        reg &= ~HB_PLL_EXT_ENA;
  90        writel(reg, hbclk->reg);
  91}
  92
  93static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
  94                                         unsigned long parent_rate)
  95{
  96        struct hb_clk *hbclk = to_hb_clk(hwclk);
  97        unsigned long divf, divq, vco_freq, reg;
  98
  99        reg = readl(hbclk->reg);
 100        if (reg & HB_PLL_EXT_BYPASS)
 101                return parent_rate;
 102
 103        divf = (reg & HB_PLL_DIVF_MASK) >> HB_PLL_DIVF_SHIFT;
 104        divq = (reg & HB_PLL_DIVQ_MASK) >> HB_PLL_DIVQ_SHIFT;
 105        vco_freq = parent_rate * (divf + 1);
 106
 107        return vco_freq / (1 << divq);
 108}
 109
 110static void clk_pll_calc(unsigned long rate, unsigned long ref_freq,
 111                        u32 *pdivq, u32 *pdivf)
 112{
 113        u32 divq, divf;
 114        unsigned long vco_freq;
 115
 116        if (rate < HB_PLL_MIN_FREQ)
 117                rate = HB_PLL_MIN_FREQ;
 118        if (rate > HB_PLL_MAX_FREQ)
 119                rate = HB_PLL_MAX_FREQ;
 120
 121        for (divq = 1; divq <= 6; divq++) {
 122                if ((rate * (1 << divq)) >= HB_PLL_VCO_MIN_FREQ)
 123                        break;
 124        }
 125
 126        vco_freq = rate * (1 << divq);
 127        divf = (vco_freq + (ref_freq / 2)) / ref_freq;
 128        divf--;
 129
 130        *pdivq = divq;
 131        *pdivf = divf;
 132}
 133
 134static long clk_pll_round_rate(struct clk_hw *hwclk, unsigned long rate,
 135                               unsigned long *parent_rate)
 136{
 137        u32 divq, divf;
 138        unsigned long ref_freq = *parent_rate;
 139
 140        clk_pll_calc(rate, ref_freq, &divq, &divf);
 141
 142        return (ref_freq * (divf + 1)) / (1 << divq);
 143}
 144
 145static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate,
 146                            unsigned long parent_rate)
 147{
 148        struct hb_clk *hbclk = to_hb_clk(hwclk);
 149        u32 divq, divf;
 150        u32 reg;
 151
 152        clk_pll_calc(rate, parent_rate, &divq, &divf);
 153
 154        reg = readl(hbclk->reg);
 155        if (divf != ((reg & HB_PLL_DIVF_MASK) >> HB_PLL_DIVF_SHIFT)) {
 156                /* Need to re-lock PLL, so put it into bypass mode */
 157                reg |= HB_PLL_EXT_BYPASS;
 158                writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg);
 159
 160                writel(reg | HB_PLL_RESET, hbclk->reg);
 161                reg &= ~(HB_PLL_DIVF_MASK | HB_PLL_DIVQ_MASK);
 162                reg |= (divf << HB_PLL_DIVF_SHIFT) | (divq << HB_PLL_DIVQ_SHIFT);
 163                writel(reg | HB_PLL_RESET, hbclk->reg);
 164                writel(reg, hbclk->reg);
 165
 166                while ((readl(hbclk->reg) & HB_PLL_LOCK) == 0)
 167                        ;
 168                while ((readl(hbclk->reg) & HB_PLL_LOCK_500) == 0)
 169                        ;
 170                reg |= HB_PLL_EXT_ENA;
 171                reg &= ~HB_PLL_EXT_BYPASS;
 172        } else {
 173                writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg);
 174                reg &= ~HB_PLL_DIVQ_MASK;
 175                reg |= divq << HB_PLL_DIVQ_SHIFT;
 176                writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg);
 177        }
 178        writel(reg, hbclk->reg);
 179
 180        return 0;
 181}
 182
 183static const struct clk_ops clk_pll_ops = {
 184        .prepare = clk_pll_prepare,
 185        .unprepare = clk_pll_unprepare,
 186        .enable = clk_pll_enable,
 187        .disable = clk_pll_disable,
 188        .recalc_rate = clk_pll_recalc_rate,
 189        .round_rate = clk_pll_round_rate,
 190        .set_rate = clk_pll_set_rate,
 191};
 192
 193static unsigned long clk_cpu_periphclk_recalc_rate(struct clk_hw *hwclk,
 194                                                   unsigned long parent_rate)
 195{
 196        struct hb_clk *hbclk = to_hb_clk(hwclk);
 197        u32 div = (readl(hbclk->reg) & HB_A9_PCLK_DIV) ? 8 : 4;
 198        return parent_rate / div;
 199}
 200
 201static const struct clk_ops a9periphclk_ops = {
 202        .recalc_rate = clk_cpu_periphclk_recalc_rate,
 203};
 204
 205static unsigned long clk_cpu_a9bclk_recalc_rate(struct clk_hw *hwclk,
 206                                                unsigned long parent_rate)
 207{
 208        struct hb_clk *hbclk = to_hb_clk(hwclk);
 209        u32 div = (readl(hbclk->reg) & HB_A9_BCLK_DIV_MASK) >> HB_A9_BCLK_DIV_SHIFT;
 210
 211        return parent_rate / (div + 2);
 212}
 213
 214static const struct clk_ops a9bclk_ops = {
 215        .recalc_rate = clk_cpu_a9bclk_recalc_rate,
 216};
 217
 218static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk,
 219                                             unsigned long parent_rate)
 220{
 221        struct hb_clk *hbclk = to_hb_clk(hwclk);
 222        u32 div;
 223
 224        div = readl(hbclk->reg) & 0x1f;
 225        div++;
 226        div *= 2;
 227
 228        return parent_rate / div;
 229}
 230
 231static long clk_periclk_round_rate(struct clk_hw *hwclk, unsigned long rate,
 232                                   unsigned long *parent_rate)
 233{
 234        u32 div;
 235
 236        div = *parent_rate / rate;
 237        div++;
 238        div &= ~0x1;
 239
 240        return *parent_rate / div;
 241}
 242
 243static int clk_periclk_set_rate(struct clk_hw *hwclk, unsigned long rate,
 244                                unsigned long parent_rate)
 245{
 246        struct hb_clk *hbclk = to_hb_clk(hwclk);
 247        u32 div;
 248
 249        div = parent_rate / rate;
 250        if (div & 0x1)
 251                return -EINVAL;
 252
 253        writel(div >> 1, hbclk->reg);
 254        return 0;
 255}
 256
 257static const struct clk_ops periclk_ops = {
 258        .recalc_rate = clk_periclk_recalc_rate,
 259        .round_rate = clk_periclk_round_rate,
 260        .set_rate = clk_periclk_set_rate,
 261};
 262
 263static void __init hb_clk_init(struct device_node *node, const struct clk_ops *ops, unsigned long clkflags)
 264{
 265        u32 reg;
 266        struct hb_clk *hb_clk;
 267        const char *clk_name = node->name;
 268        const char *parent_name;
 269        struct clk_init_data init;
 270        struct device_node *srnp;
 271        int rc;
 272
 273        rc = of_property_read_u32(node, "reg", &reg);
 274        if (WARN_ON(rc))
 275                return;
 276
 277        hb_clk = kzalloc(sizeof(*hb_clk), GFP_KERNEL);
 278        if (WARN_ON(!hb_clk))
 279                return;
 280
 281        /* Map system registers */
 282        srnp = of_find_compatible_node(NULL, NULL, "calxeda,hb-sregs");
 283        hb_clk->reg = of_iomap(srnp, 0);
 284        of_node_put(srnp);
 285        BUG_ON(!hb_clk->reg);
 286        hb_clk->reg += reg;
 287
 288        of_property_read_string(node, "clock-output-names", &clk_name);
 289
 290        init.name = clk_name;
 291        init.ops = ops;
 292        init.flags = clkflags;
 293        parent_name = of_clk_get_parent_name(node, 0);
 294        init.parent_names = &parent_name;
 295        init.num_parents = 1;
 296
 297        hb_clk->hw.init = &init;
 298
 299        rc = clk_hw_register(NULL, &hb_clk->hw);
 300        if (WARN_ON(rc)) {
 301                kfree(hb_clk);
 302                return;
 303        }
 304        of_clk_add_hw_provider(node, of_clk_hw_simple_get, &hb_clk->hw);
 305}
 306
 307static void __init hb_pll_init(struct device_node *node)
 308{
 309        hb_clk_init(node, &clk_pll_ops, 0);
 310}
 311CLK_OF_DECLARE(hb_pll, "calxeda,hb-pll-clock", hb_pll_init);
 312
 313static void __init hb_a9periph_init(struct device_node *node)
 314{
 315        hb_clk_init(node, &a9periphclk_ops, 0);
 316}
 317CLK_OF_DECLARE(hb_a9periph, "calxeda,hb-a9periph-clock", hb_a9periph_init);
 318
 319static void __init hb_a9bus_init(struct device_node *node)
 320{
 321        hb_clk_init(node, &a9bclk_ops, CLK_IS_CRITICAL);
 322}
 323CLK_OF_DECLARE(hb_a9bus, "calxeda,hb-a9bus-clock", hb_a9bus_init);
 324
 325static void __init hb_emmc_init(struct device_node *node)
 326{
 327        hb_clk_init(node, &periclk_ops, 0);
 328}
 329CLK_OF_DECLARE(hb_emmc, "calxeda,hb-emmc-clock", hb_emmc_init);
 330