linux/drivers/clk/berlin/berlin2-div.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (c) 2014 Marvell Technology Group Ltd.
   4 *
   5 * Alexandre Belloni <alexandre.belloni@free-electrons.com>
   6 * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
   7 */
   8#include <linux/bitops.h>
   9#include <linux/clk-provider.h>
  10#include <linux/io.h>
  11#include <linux/of.h>
  12#include <linux/of_address.h>
  13#include <linux/slab.h>
  14#include <linux/spinlock.h>
  15
  16#include "berlin2-div.h"
  17
  18/*
  19 * Clock dividers in Berlin2 SoCs comprise a complex cell to select
  20 * input pll and divider. The virtual structure as it is used in Marvell
  21 * BSP code can be seen as:
  22 *
  23 *                      +---+
  24 * pll0 --------------->| 0 |                   +---+
  25 *           +---+      |(B)|--+--------------->| 0 |      +---+
  26 * pll1.0 -->| 0 |  +-->| 1 |  |   +--------+   |(E)|----->| 0 |   +---+
  27 * pll1.1 -->| 1 |  |   +---+  +-->|(C) 1:M |-->| 1 |      |(F)|-->|(G)|->
  28 * ...    -->|(A)|--+          |   +--------+   +---+  +-->| 1 |   +---+
  29 * ...    -->|   |             +-->|(D) 1:3 |----------+   +---+
  30 * pll1.N -->| N |                 +---------
  31 *           +---+
  32 *
  33 * (A) input pll clock mux controlled by               <PllSelect[1:n]>
  34 * (B) input pll bypass mux controlled by              <PllSwitch>
  35 * (C) programmable clock divider controlled by        <Select[1:n]>
  36 * (D) constant div-by-3 clock divider
  37 * (E) programmable clock divider bypass controlled by <Switch>
  38 * (F) constant div-by-3 clock mux controlled by       <D3Switch>
  39 * (G) clock gate controlled by                        <Enable>
  40 *
  41 * For whatever reason, above control signals come in two flavors:
  42 * - single register dividers with all bits in one register
  43 * - shared register dividers with bits spread over multiple registers
  44 *   (including signals for the same cell spread over consecutive registers)
  45 *
  46 * Also, clock gate and pll mux is not available on every div cell, so
  47 * we have to deal with those, too. We reuse common clock composite driver
  48 * for it.
  49 */
  50
  51#define PLL_SELECT_MASK 0x7
  52#define DIV_SELECT_MASK 0x7
  53
  54struct berlin2_div {
  55        struct clk_hw hw;
  56        void __iomem *base;
  57        struct berlin2_div_map map;
  58        spinlock_t *lock;
  59};
  60
  61#define to_berlin2_div(hw) container_of(hw, struct berlin2_div, hw)
  62
  63static u8 clk_div[] = { 1, 2, 4, 6, 8, 12, 1, 1 };
  64
  65static int berlin2_div_is_enabled(struct clk_hw *hw)
  66{
  67        struct berlin2_div *div = to_berlin2_div(hw);
  68        struct berlin2_div_map *map = &div->map;
  69        u32 reg;
  70
  71        if (div->lock)
  72                spin_lock(div->lock);
  73
  74        reg = readl_relaxed(div->base + map->gate_offs);
  75        reg >>= map->gate_shift;
  76
  77        if (div->lock)
  78                spin_unlock(div->lock);
  79
  80        return (reg & 0x1);
  81}
  82
  83static int berlin2_div_enable(struct clk_hw *hw)
  84{
  85        struct berlin2_div *div = to_berlin2_div(hw);
  86        struct berlin2_div_map *map = &div->map;
  87        u32 reg;
  88
  89        if (div->lock)
  90                spin_lock(div->lock);
  91
  92        reg = readl_relaxed(div->base + map->gate_offs);
  93        reg |= BIT(map->gate_shift);
  94        writel_relaxed(reg, div->base + map->gate_offs);
  95
  96        if (div->lock)
  97                spin_unlock(div->lock);
  98
  99        return 0;
 100}
 101
 102static void berlin2_div_disable(struct clk_hw *hw)
 103{
 104        struct berlin2_div *div = to_berlin2_div(hw);
 105        struct berlin2_div_map *map = &div->map;
 106        u32 reg;
 107
 108        if (div->lock)
 109                spin_lock(div->lock);
 110
 111        reg = readl_relaxed(div->base + map->gate_offs);
 112        reg &= ~BIT(map->gate_shift);
 113        writel_relaxed(reg, div->base + map->gate_offs);
 114
 115        if (div->lock)
 116                spin_unlock(div->lock);
 117}
 118
 119static int berlin2_div_set_parent(struct clk_hw *hw, u8 index)
 120{
 121        struct berlin2_div *div = to_berlin2_div(hw);
 122        struct berlin2_div_map *map = &div->map;
 123        u32 reg;
 124
 125        if (div->lock)
 126                spin_lock(div->lock);
 127
 128        /* index == 0 is PLL_SWITCH */
 129        reg = readl_relaxed(div->base + map->pll_switch_offs);
 130        if (index == 0)
 131                reg &= ~BIT(map->pll_switch_shift);
 132        else
 133                reg |= BIT(map->pll_switch_shift);
 134        writel_relaxed(reg, div->base + map->pll_switch_offs);
 135
 136        /* index > 0 is PLL_SELECT */
 137        if (index > 0) {
 138                reg = readl_relaxed(div->base + map->pll_select_offs);
 139                reg &= ~(PLL_SELECT_MASK << map->pll_select_shift);
 140                reg |= (index - 1) << map->pll_select_shift;
 141                writel_relaxed(reg, div->base + map->pll_select_offs);
 142        }
 143
 144        if (div->lock)
 145                spin_unlock(div->lock);
 146
 147        return 0;
 148}
 149
 150static u8 berlin2_div_get_parent(struct clk_hw *hw)
 151{
 152        struct berlin2_div *div = to_berlin2_div(hw);
 153        struct berlin2_div_map *map = &div->map;
 154        u32 reg;
 155        u8 index = 0;
 156
 157        if (div->lock)
 158                spin_lock(div->lock);
 159
 160        /* PLL_SWITCH == 0 is index 0 */
 161        reg = readl_relaxed(div->base + map->pll_switch_offs);
 162        reg &= BIT(map->pll_switch_shift);
 163        if (reg) {
 164                reg = readl_relaxed(div->base + map->pll_select_offs);
 165                reg >>= map->pll_select_shift;
 166                reg &= PLL_SELECT_MASK;
 167                index = 1 + reg;
 168        }
 169
 170        if (div->lock)
 171                spin_unlock(div->lock);
 172
 173        return index;
 174}
 175
 176static unsigned long berlin2_div_recalc_rate(struct clk_hw *hw,
 177                                             unsigned long parent_rate)
 178{
 179        struct berlin2_div *div = to_berlin2_div(hw);
 180        struct berlin2_div_map *map = &div->map;
 181        u32 divsw, div3sw, divider = 1;
 182
 183        if (div->lock)
 184                spin_lock(div->lock);
 185
 186        divsw = readl_relaxed(div->base + map->div_switch_offs) &
 187                (1 << map->div_switch_shift);
 188        div3sw = readl_relaxed(div->base + map->div3_switch_offs) &
 189                (1 << map->div3_switch_shift);
 190
 191        /* constant divide-by-3 (dominant) */
 192        if (div3sw != 0) {
 193                divider = 3;
 194        /* divider can be bypassed with DIV_SWITCH == 0 */
 195        } else if (divsw == 0) {
 196                divider = 1;
 197        /* clock divider determined by DIV_SELECT */
 198        } else {
 199                u32 reg;
 200                reg = readl_relaxed(div->base + map->div_select_offs);
 201                reg >>= map->div_select_shift;
 202                reg &= DIV_SELECT_MASK;
 203                divider = clk_div[reg];
 204        }
 205
 206        if (div->lock)
 207                spin_unlock(div->lock);
 208
 209        return parent_rate / divider;
 210}
 211
 212static const struct clk_ops berlin2_div_rate_ops = {
 213        .recalc_rate    = berlin2_div_recalc_rate,
 214};
 215
 216static const struct clk_ops berlin2_div_gate_ops = {
 217        .is_enabled     = berlin2_div_is_enabled,
 218        .enable         = berlin2_div_enable,
 219        .disable        = berlin2_div_disable,
 220};
 221
 222static const struct clk_ops berlin2_div_mux_ops = {
 223        .set_parent     = berlin2_div_set_parent,
 224        .get_parent     = berlin2_div_get_parent,
 225};
 226
 227struct clk_hw * __init
 228berlin2_div_register(const struct berlin2_div_map *map,
 229                     void __iomem *base, const char *name, u8 div_flags,
 230                     const char **parent_names, int num_parents,
 231                     unsigned long flags, spinlock_t *lock)
 232{
 233        const struct clk_ops *mux_ops = &berlin2_div_mux_ops;
 234        const struct clk_ops *rate_ops = &berlin2_div_rate_ops;
 235        const struct clk_ops *gate_ops = &berlin2_div_gate_ops;
 236        struct berlin2_div *div;
 237
 238        div = kzalloc(sizeof(*div), GFP_KERNEL);
 239        if (!div)
 240                return ERR_PTR(-ENOMEM);
 241
 242        /* copy div_map to allow __initconst */
 243        memcpy(&div->map, map, sizeof(*map));
 244        div->base = base;
 245        div->lock = lock;
 246
 247        if ((div_flags & BERLIN2_DIV_HAS_GATE) == 0)
 248                gate_ops = NULL;
 249        if ((div_flags & BERLIN2_DIV_HAS_MUX) == 0)
 250                mux_ops = NULL;
 251
 252        return clk_hw_register_composite(NULL, name, parent_names, num_parents,
 253                                      &div->hw, mux_ops, &div->hw, rate_ops,
 254                                      &div->hw, gate_ops, flags);
 255}
 256