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