linux/drivers/clk/socfpga/clk-gate-s10.c
<<
>>
Prefs
   1// SPDX-License-Identifier:     GPL-2.0
   2/*
   3 * Copyright (C) 2017, Intel Corporation
   4 */
   5#include <linux/clk-provider.h>
   6#include <linux/io.h>
   7#include <linux/slab.h>
   8#include "stratix10-clk.h"
   9#include "clk.h"
  10
  11#define SOCFPGA_CS_PDBG_CLK     "cs_pdbg_clk"
  12#define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw)
  13
  14#define SOCFPGA_EMAC0_CLK               "emac0_clk"
  15#define SOCFPGA_EMAC1_CLK               "emac1_clk"
  16#define SOCFPGA_EMAC2_CLK               "emac2_clk"
  17#define AGILEX_BYPASS_OFFSET            0xC
  18#define STRATIX10_BYPASS_OFFSET         0x2C
  19#define BOOTCLK_BYPASS                  2
  20
  21static unsigned long socfpga_gate_clk_recalc_rate(struct clk_hw *hwclk,
  22                                                  unsigned long parent_rate)
  23{
  24        struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
  25        u32 div = 1, val;
  26
  27        if (socfpgaclk->fixed_div) {
  28                div = socfpgaclk->fixed_div;
  29        } else if (socfpgaclk->div_reg) {
  30                val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
  31                val &= GENMASK(socfpgaclk->width - 1, 0);
  32                div = (1 << val);
  33        }
  34        return parent_rate / div;
  35}
  36
  37static unsigned long socfpga_dbg_clk_recalc_rate(struct clk_hw *hwclk,
  38                                                  unsigned long parent_rate)
  39{
  40        struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
  41        u32 div, val;
  42
  43        val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
  44        val &= GENMASK(socfpgaclk->width - 1, 0);
  45        div = (1 << val);
  46        div = div ? 4 : 1;
  47
  48        return parent_rate / div;
  49}
  50
  51static u8 socfpga_gate_get_parent(struct clk_hw *hwclk)
  52{
  53        struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
  54        u32 mask, second_bypass;
  55        u8 parent = 0;
  56        const char *name = clk_hw_get_name(hwclk);
  57
  58        if (socfpgaclk->bypass_reg) {
  59                mask = (0x1 << socfpgaclk->bypass_shift);
  60                parent = ((readl(socfpgaclk->bypass_reg) & mask) >>
  61                          socfpgaclk->bypass_shift);
  62        }
  63
  64        if (streq(name, SOCFPGA_EMAC0_CLK) ||
  65            streq(name, SOCFPGA_EMAC1_CLK) ||
  66            streq(name, SOCFPGA_EMAC2_CLK)) {
  67                second_bypass = readl(socfpgaclk->bypass_reg -
  68                                      STRATIX10_BYPASS_OFFSET);
  69                /* EMACA bypass to bootclk @0xB0 offset */
  70                if (second_bypass & 0x1)
  71                        if (parent == 0) /* only applicable if parent is maca */
  72                                parent = BOOTCLK_BYPASS;
  73
  74                if (second_bypass & 0x2)
  75                        if (parent == 1) /* only applicable if parent is macb */
  76                                parent = BOOTCLK_BYPASS;
  77        }
  78        return parent;
  79}
  80
  81static u8 socfpga_agilex_gate_get_parent(struct clk_hw *hwclk)
  82{
  83        struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
  84        u32 mask, second_bypass;
  85        u8 parent = 0;
  86        const char *name = clk_hw_get_name(hwclk);
  87
  88        if (socfpgaclk->bypass_reg) {
  89                mask = (0x1 << socfpgaclk->bypass_shift);
  90                parent = ((readl(socfpgaclk->bypass_reg) & mask) >>
  91                          socfpgaclk->bypass_shift);
  92        }
  93
  94        if (streq(name, SOCFPGA_EMAC0_CLK) ||
  95            streq(name, SOCFPGA_EMAC1_CLK) ||
  96            streq(name, SOCFPGA_EMAC2_CLK)) {
  97                second_bypass = readl(socfpgaclk->bypass_reg -
  98                                      AGILEX_BYPASS_OFFSET);
  99                /* EMACA bypass to bootclk @0x88 offset */
 100                if (second_bypass & 0x1)
 101                        if (parent == 0) /* only applicable if parent is maca */
 102                                parent = BOOTCLK_BYPASS;
 103
 104                if (second_bypass & 0x2)
 105                        if (parent == 1) /* only applicable if parent is macb */
 106                                parent = BOOTCLK_BYPASS;
 107        }
 108
 109        return parent;
 110}
 111
 112static struct clk_ops gateclk_ops = {
 113        .recalc_rate = socfpga_gate_clk_recalc_rate,
 114        .get_parent = socfpga_gate_get_parent,
 115};
 116
 117static const struct clk_ops agilex_gateclk_ops = {
 118        .recalc_rate = socfpga_gate_clk_recalc_rate,
 119        .get_parent = socfpga_agilex_gate_get_parent,
 120};
 121
 122static const struct clk_ops dbgclk_ops = {
 123        .recalc_rate = socfpga_dbg_clk_recalc_rate,
 124        .get_parent = socfpga_gate_get_parent,
 125};
 126
 127struct clk_hw *s10_register_gate(const struct stratix10_gate_clock *clks, void __iomem *regbase)
 128{
 129        struct clk_hw *hw_clk;
 130        struct socfpga_gate_clk *socfpga_clk;
 131        struct clk_init_data init;
 132        const char *parent_name = clks->parent_name;
 133        int ret;
 134
 135        socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
 136        if (!socfpga_clk)
 137                return NULL;
 138
 139        socfpga_clk->hw.reg = regbase + clks->gate_reg;
 140        socfpga_clk->hw.bit_idx = clks->gate_idx;
 141
 142        gateclk_ops.enable = clk_gate_ops.enable;
 143        gateclk_ops.disable = clk_gate_ops.disable;
 144
 145        socfpga_clk->fixed_div = clks->fixed_div;
 146
 147        if (clks->div_reg)
 148                socfpga_clk->div_reg = regbase + clks->div_reg;
 149        else
 150                socfpga_clk->div_reg = NULL;
 151
 152        socfpga_clk->width = clks->div_width;
 153        socfpga_clk->shift = clks->div_offset;
 154
 155        if (clks->bypass_reg)
 156                socfpga_clk->bypass_reg = regbase + clks->bypass_reg;
 157        else
 158                socfpga_clk->bypass_reg = NULL;
 159        socfpga_clk->bypass_shift = clks->bypass_shift;
 160
 161        if (streq(clks->name, "cs_pdbg_clk"))
 162                init.ops = &dbgclk_ops;
 163        else
 164                init.ops = &gateclk_ops;
 165
 166        init.name = clks->name;
 167        init.flags = clks->flags;
 168
 169        init.num_parents = clks->num_parents;
 170        init.parent_names = parent_name ? &parent_name : NULL;
 171        if (init.parent_names == NULL)
 172                init.parent_data = clks->parent_data;
 173        socfpga_clk->hw.hw.init = &init;
 174
 175        hw_clk = &socfpga_clk->hw.hw;
 176
 177        ret = clk_hw_register(NULL, &socfpga_clk->hw.hw);
 178        if (ret) {
 179                kfree(socfpga_clk);
 180                return ERR_PTR(ret);
 181        }
 182        return hw_clk;
 183}
 184
 185struct clk_hw *agilex_register_gate(const struct stratix10_gate_clock *clks, void __iomem *regbase)
 186{
 187        struct clk_hw *hw_clk;
 188        struct socfpga_gate_clk *socfpga_clk;
 189        struct clk_init_data init;
 190        const char *parent_name = clks->parent_name;
 191        int ret;
 192
 193        socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
 194        if (!socfpga_clk)
 195                return NULL;
 196
 197        socfpga_clk->hw.reg = regbase + clks->gate_reg;
 198        socfpga_clk->hw.bit_idx = clks->gate_idx;
 199
 200        gateclk_ops.enable = clk_gate_ops.enable;
 201        gateclk_ops.disable = clk_gate_ops.disable;
 202
 203        socfpga_clk->fixed_div = clks->fixed_div;
 204
 205        if (clks->div_reg)
 206                socfpga_clk->div_reg = regbase + clks->div_reg;
 207        else
 208                socfpga_clk->div_reg = NULL;
 209
 210        socfpga_clk->width = clks->div_width;
 211        socfpga_clk->shift = clks->div_offset;
 212
 213        if (clks->bypass_reg)
 214                socfpga_clk->bypass_reg = regbase + clks->bypass_reg;
 215        else
 216                socfpga_clk->bypass_reg = NULL;
 217        socfpga_clk->bypass_shift = clks->bypass_shift;
 218
 219        if (streq(clks->name, "cs_pdbg_clk"))
 220                init.ops = &dbgclk_ops;
 221        else
 222                init.ops = &agilex_gateclk_ops;
 223
 224        init.name = clks->name;
 225        init.flags = clks->flags;
 226
 227        init.num_parents = clks->num_parents;
 228        init.parent_names = parent_name ? &parent_name : NULL;
 229        if (init.parent_names == NULL)
 230                init.parent_data = clks->parent_data;
 231        socfpga_clk->hw.hw.init = &init;
 232
 233        hw_clk = &socfpga_clk->hw.hw;
 234
 235        ret = clk_hw_register(NULL, &socfpga_clk->hw.hw);
 236        if (ret) {
 237                kfree(socfpga_clk);
 238                return ERR_PTR(ret);
 239        }
 240        return hw_clk;
 241}
 242