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/slab.h>
   7#include "stratix10-clk.h"
   8#include "clk.h"
   9
  10#define SOCFPGA_CS_PDBG_CLK     "cs_pdbg_clk"
  11#define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw)
  12
  13static unsigned long socfpga_gate_clk_recalc_rate(struct clk_hw *hwclk,
  14                                                  unsigned long parent_rate)
  15{
  16        struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
  17        u32 div = 1, val;
  18
  19        if (socfpgaclk->fixed_div) {
  20                div = socfpgaclk->fixed_div;
  21        } else if (socfpgaclk->div_reg) {
  22                val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
  23                val &= GENMASK(socfpgaclk->width - 1, 0);
  24                div = (1 << val);
  25        }
  26        return parent_rate / div;
  27}
  28
  29static unsigned long socfpga_dbg_clk_recalc_rate(struct clk_hw *hwclk,
  30                                                  unsigned long parent_rate)
  31{
  32        struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
  33        u32 div = 1, val;
  34
  35        val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
  36        val &= GENMASK(socfpgaclk->width - 1, 0);
  37        div = (1 << val);
  38        div = div ? 4 : 1;
  39
  40        return parent_rate / div;
  41}
  42
  43static u8 socfpga_gate_get_parent(struct clk_hw *hwclk)
  44{
  45        struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
  46        u32 mask;
  47        u8 parent = 0;
  48
  49        if (socfpgaclk->bypass_reg) {
  50                mask = (0x1 << socfpgaclk->bypass_shift);
  51                parent = ((readl(socfpgaclk->bypass_reg) & mask) >>
  52                          socfpgaclk->bypass_shift);
  53        }
  54        return parent;
  55}
  56
  57static struct clk_ops gateclk_ops = {
  58        .recalc_rate = socfpga_gate_clk_recalc_rate,
  59        .get_parent = socfpga_gate_get_parent,
  60};
  61
  62static const struct clk_ops dbgclk_ops = {
  63        .recalc_rate = socfpga_dbg_clk_recalc_rate,
  64        .get_parent = socfpga_gate_get_parent,
  65};
  66
  67struct clk *s10_register_gate(const char *name, const char *parent_name,
  68                              const char * const *parent_names,
  69                              u8 num_parents, unsigned long flags,
  70                              void __iomem *regbase, unsigned long gate_reg,
  71                              unsigned long gate_idx, unsigned long div_reg,
  72                              unsigned long div_offset, u8 div_width,
  73                              unsigned long bypass_reg, u8 bypass_shift,
  74                              u8 fixed_div)
  75{
  76        struct clk *clk;
  77        struct socfpga_gate_clk *socfpga_clk;
  78        struct clk_init_data init;
  79
  80        socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
  81        if (!socfpga_clk)
  82                return NULL;
  83
  84        socfpga_clk->hw.reg = regbase + gate_reg;
  85        socfpga_clk->hw.bit_idx = gate_idx;
  86
  87        gateclk_ops.enable = clk_gate_ops.enable;
  88        gateclk_ops.disable = clk_gate_ops.disable;
  89
  90        socfpga_clk->fixed_div = fixed_div;
  91
  92        if (div_reg)
  93                socfpga_clk->div_reg = regbase + div_reg;
  94        else
  95                socfpga_clk->div_reg = NULL;
  96
  97        socfpga_clk->width = div_width;
  98        socfpga_clk->shift = div_offset;
  99
 100        if (bypass_reg)
 101                socfpga_clk->bypass_reg = regbase + bypass_reg;
 102        else
 103                socfpga_clk->bypass_reg = NULL;
 104        socfpga_clk->bypass_shift = bypass_shift;
 105
 106        if (streq(name, "cs_pdbg_clk"))
 107                init.ops = &dbgclk_ops;
 108        else
 109                init.ops = &gateclk_ops;
 110
 111        init.name = name;
 112        init.flags = flags;
 113
 114        init.num_parents = num_parents;
 115        init.parent_names = parent_names ? parent_names : &parent_name;
 116        socfpga_clk->hw.hw.init = &init;
 117
 118        clk = clk_register(NULL, &socfpga_clk->hw.hw);
 119        if (WARN_ON(IS_ERR(clk))) {
 120                kfree(socfpga_clk);
 121                return NULL;
 122        }
 123
 124        return clk;
 125}
 126