217 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2017, Intel Corporation4 */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 1613#define CLK_MGR_FREE_MASK 0x714#define SWCTRLBTCLKSEN_SHIFT 815 16#define to_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw)17 18static unsigned long n5x_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;23 unsigned long shift = socfpgaclk->shift;24 u32 val;25 26 val = readl(socfpgaclk->hw.reg);27 val &= (0x1f << shift);28 div = (val >> shift) + 1;29 30 return parent_rate / div;31}32 33static unsigned long clk_peri_c_clk_recalc_rate(struct clk_hw *hwclk,34 unsigned long parent_rate)35{36 struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);37 unsigned long div = 1;38 u32 val;39 40 val = readl(socfpgaclk->hw.reg);41 val &= GENMASK(SWCTRLBTCLKSEN_SHIFT - 1, 0);42 parent_rate /= val;43 44 return parent_rate / div;45}46 47static unsigned long clk_peri_cnt_clk_recalc_rate(struct clk_hw *hwclk,48 unsigned long parent_rate)49{50 struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);51 unsigned long div = 1;52 53 if (socfpgaclk->fixed_div) {54 div = socfpgaclk->fixed_div;55 } else {56 if (socfpgaclk->hw.reg)57 div = ((readl(socfpgaclk->hw.reg) & 0x7ff) + 1);58 }59 60 return parent_rate / div;61}62 63static u8 clk_periclk_get_parent(struct clk_hw *hwclk)64{65 struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);66 u32 clk_src, mask;67 u8 parent = 0;68 69 /* handle the bypass first */70 if (socfpgaclk->bypass_reg) {71 mask = (0x1 << socfpgaclk->bypass_shift);72 parent = ((readl(socfpgaclk->bypass_reg) & mask) >>73 socfpgaclk->bypass_shift);74 if (parent)75 return parent;76 }77 78 if (socfpgaclk->hw.reg) {79 clk_src = readl(socfpgaclk->hw.reg);80 parent = (clk_src >> CLK_MGR_FREE_SHIFT) &81 CLK_MGR_FREE_MASK;82 }83 return parent;84}85 86static const struct clk_ops n5x_peri_c_clk_ops = {87 .recalc_rate = n5x_clk_peri_c_clk_recalc_rate,88 .get_parent = clk_periclk_get_parent,89};90 91static const struct clk_ops peri_c_clk_ops = {92 .recalc_rate = clk_peri_c_clk_recalc_rate,93 .get_parent = clk_periclk_get_parent,94};95 96static const struct clk_ops peri_cnt_clk_ops = {97 .recalc_rate = clk_peri_cnt_clk_recalc_rate,98 .get_parent = clk_periclk_get_parent,99};100 101struct clk_hw *s10_register_periph(const struct stratix10_perip_c_clock *clks,102 void __iomem *reg)103{104 struct clk_hw *hw_clk;105 struct socfpga_periph_clk *periph_clk;106 struct clk_init_data init;107 const char *name = clks->name;108 const char *parent_name = clks->parent_name;109 int ret;110 111 periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);112 if (WARN_ON(!periph_clk))113 return NULL;114 115 periph_clk->hw.reg = reg + clks->offset;116 117 init.name = name;118 init.ops = &peri_c_clk_ops;119 init.flags = clks->flags;120 121 init.num_parents = clks->num_parents;122 init.parent_names = parent_name ? &parent_name : NULL;123 if (init.parent_names == NULL)124 init.parent_data = clks->parent_data;125 126 periph_clk->hw.hw.init = &init;127 hw_clk = &periph_clk->hw.hw;128 129 ret = clk_hw_register(NULL, hw_clk);130 if (ret) {131 kfree(periph_clk);132 return ERR_PTR(ret);133 }134 return hw_clk;135}136 137struct clk_hw *n5x_register_periph(const struct n5x_perip_c_clock *clks,138 void __iomem *regbase)139{140 struct clk_hw *hw_clk;141 struct socfpga_periph_clk *periph_clk;142 struct clk_init_data init;143 const char *name = clks->name;144 const char *parent_name = clks->parent_name;145 int ret;146 147 periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);148 if (WARN_ON(!periph_clk))149 return NULL;150 151 periph_clk->hw.reg = regbase + clks->offset;152 periph_clk->shift = clks->shift;153 154 init.name = name;155 init.ops = &n5x_peri_c_clk_ops;156 init.flags = clks->flags;157 158 init.num_parents = clks->num_parents;159 init.parent_names = parent_name ? &parent_name : NULL;160 161 periph_clk->hw.hw.init = &init;162 hw_clk = &periph_clk->hw.hw;163 164 ret = clk_hw_register(NULL, hw_clk);165 if (ret) {166 kfree(periph_clk);167 return ERR_PTR(ret);168 }169 return hw_clk;170}171 172struct clk_hw *s10_register_cnt_periph(const struct stratix10_perip_cnt_clock *clks,173 void __iomem *regbase)174{175 struct clk_hw *hw_clk;176 struct socfpga_periph_clk *periph_clk;177 struct clk_init_data init;178 const char *name = clks->name;179 const char *parent_name = clks->parent_name;180 int ret;181 182 periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);183 if (WARN_ON(!periph_clk))184 return NULL;185 186 if (clks->offset)187 periph_clk->hw.reg = regbase + clks->offset;188 else189 periph_clk->hw.reg = NULL;190 191 if (clks->bypass_reg)192 periph_clk->bypass_reg = regbase + clks->bypass_reg;193 else194 periph_clk->bypass_reg = NULL;195 periph_clk->bypass_shift = clks->bypass_shift;196 periph_clk->fixed_div = clks->fixed_divider;197 198 init.name = name;199 init.ops = &peri_cnt_clk_ops;200 init.flags = clks->flags;201 202 init.num_parents = clks->num_parents;203 init.parent_names = parent_name ? &parent_name : NULL;204 if (init.parent_names == NULL)205 init.parent_data = clks->parent_data;206 207 periph_clk->hw.hw.init = &init;208 hw_clk = &periph_clk->hw.hw;209 210 ret = clk_hw_register(NULL, hw_clk);211 if (ret) {212 kfree(periph_clk);213 return ERR_PTR(ret);214 }215 return hw_clk;216}217