linux/drivers/clk/socfpga/clk-periph-s10.c
<<
>>
Prefs
   1// SPDX-License-Identifier:     GPL-2.0
   2/*
   3 * Copyright (C) 2017, Intel Corporation
   4 */
   5#include <linux/slab.h>
   6#include <linux/clk-provider.h>
   7#include <linux/io.h>
   8
   9#include "stratix10-clk.h"
  10#include "clk.h"
  11
  12#define CLK_MGR_FREE_SHIFT              16
  13#define CLK_MGR_FREE_MASK               0x7
  14#define SWCTRLBTCLKSEN_SHIFT            8
  15
  16#define to_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw)
  17
  18static unsigned long clk_peri_c_clk_recalc_rate(struct clk_hw *hwclk,
  19                                             unsigned long parent_rate)
  20{
  21        struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
  22        unsigned long div = 1;
  23        u32 val;
  24
  25        val = readl(socfpgaclk->hw.reg);
  26        val &= GENMASK(SWCTRLBTCLKSEN_SHIFT - 1, 0);
  27        parent_rate /= val;
  28
  29        return parent_rate / div;
  30}
  31
  32static unsigned long clk_peri_cnt_clk_recalc_rate(struct clk_hw *hwclk,
  33                                             unsigned long parent_rate)
  34{
  35        struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
  36        unsigned long div = 1;
  37
  38        if (socfpgaclk->fixed_div) {
  39                div = socfpgaclk->fixed_div;
  40        } else {
  41                if (socfpgaclk->hw.reg)
  42                        div = ((readl(socfpgaclk->hw.reg) & 0x7ff) + 1);
  43        }
  44
  45        return parent_rate / div;
  46}
  47
  48static u8 clk_periclk_get_parent(struct clk_hw *hwclk)
  49{
  50        struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
  51        u32 clk_src, mask;
  52        u8 parent;
  53
  54        if (socfpgaclk->bypass_reg) {
  55                mask = (0x1 << socfpgaclk->bypass_shift);
  56                parent = ((readl(socfpgaclk->bypass_reg) & mask) >>
  57                           socfpgaclk->bypass_shift);
  58        } else {
  59                clk_src = readl(socfpgaclk->hw.reg);
  60                parent = (clk_src >> CLK_MGR_FREE_SHIFT) &
  61                        CLK_MGR_FREE_MASK;
  62        }
  63        return parent;
  64}
  65
  66static const struct clk_ops peri_c_clk_ops = {
  67        .recalc_rate = clk_peri_c_clk_recalc_rate,
  68        .get_parent = clk_periclk_get_parent,
  69};
  70
  71static const struct clk_ops peri_cnt_clk_ops = {
  72        .recalc_rate = clk_peri_cnt_clk_recalc_rate,
  73        .get_parent = clk_periclk_get_parent,
  74};
  75
  76struct clk *s10_register_periph(const struct stratix10_perip_c_clock *clks,
  77                                void __iomem *reg)
  78{
  79        struct clk *clk;
  80        struct socfpga_periph_clk *periph_clk;
  81        struct clk_init_data init;
  82        const char *name = clks->name;
  83        const char *parent_name = clks->parent_name;
  84
  85        periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
  86        if (WARN_ON(!periph_clk))
  87                return NULL;
  88
  89        periph_clk->hw.reg = reg + clks->offset;
  90
  91        init.name = name;
  92        init.ops = &peri_c_clk_ops;
  93        init.flags = clks->flags;
  94
  95        init.num_parents = clks->num_parents;
  96        init.parent_names = parent_name ? &parent_name : NULL;
  97        if (init.parent_names == NULL)
  98                init.parent_data = clks->parent_data;
  99
 100        periph_clk->hw.hw.init = &init;
 101
 102        clk = clk_register(NULL, &periph_clk->hw.hw);
 103        if (WARN_ON(IS_ERR(clk))) {
 104                kfree(periph_clk);
 105                return NULL;
 106        }
 107        return clk;
 108}
 109
 110struct clk *s10_register_cnt_periph(const struct stratix10_perip_cnt_clock *clks,
 111                                    void __iomem *regbase)
 112{
 113        struct clk *clk;
 114        struct socfpga_periph_clk *periph_clk;
 115        struct clk_init_data init;
 116        const char *name = clks->name;
 117        const char *parent_name = clks->parent_name;
 118
 119        periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
 120        if (WARN_ON(!periph_clk))
 121                return NULL;
 122
 123        if (clks->offset)
 124                periph_clk->hw.reg = regbase + clks->offset;
 125        else
 126                periph_clk->hw.reg = NULL;
 127
 128        if (clks->bypass_reg)
 129                periph_clk->bypass_reg = regbase + clks->bypass_reg;
 130        else
 131                periph_clk->bypass_reg = NULL;
 132        periph_clk->bypass_shift = clks->bypass_shift;
 133        periph_clk->fixed_div = clks->fixed_divider;
 134
 135        init.name = name;
 136        init.ops = &peri_cnt_clk_ops;
 137        init.flags = clks->flags;
 138
 139        init.num_parents = clks->num_parents;
 140        init.parent_names = parent_name ? &parent_name : NULL;
 141        if (init.parent_names == NULL)
 142                init.parent_data = clks->parent_data;
 143
 144        periph_clk->hw.hw.init = &init;
 145
 146        clk = clk_register(NULL, &periph_clk->hw.hw);
 147        if (WARN_ON(IS_ERR(clk))) {
 148                kfree(periph_clk);
 149                return NULL;
 150        }
 151        return clk;
 152}
 153