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 18 static 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 33 static 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 47 static 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 63 static 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 86 static 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 91 static 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 96 static 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 101 struct 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 137 struct 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 172 struct 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 else 189 periph_clk->hw.reg = NULL; 190 191 if (clks->bypass_reg) 192 periph_clk->bypass_reg = regbase + clks->bypass_reg; 193 else 194 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