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