1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2017, Intel Corporation
5 #include <linux/slab.h>
6 #include <linux/clk-provider.h>
8 #include "stratix10-clk.h"
11 #define CLK_MGR_FREE_SHIFT 16
12 #define CLK_MGR_FREE_MASK 0x7
13 #define SWCTRLBTCLKSEN_SHIFT 8
15 #define to_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw)
17 static unsigned long clk_peri_c_clk_recalc_rate(struct clk_hw
*hwclk
,
18 unsigned long parent_rate
)
20 struct socfpga_periph_clk
*socfpgaclk
= to_periph_clk(hwclk
);
21 unsigned long div
= 1;
24 val
= readl(socfpgaclk
->hw
.reg
);
25 val
&= GENMASK(SWCTRLBTCLKSEN_SHIFT
- 1, 0);
28 return parent_rate
/ div
;
31 static unsigned long clk_peri_cnt_clk_recalc_rate(struct clk_hw
*hwclk
,
32 unsigned long parent_rate
)
34 struct socfpga_periph_clk
*socfpgaclk
= to_periph_clk(hwclk
);
35 unsigned long div
= 1;
37 if (socfpgaclk
->fixed_div
) {
38 div
= socfpgaclk
->fixed_div
;
40 if (!socfpgaclk
->bypass_reg
)
41 div
= ((readl(socfpgaclk
->hw
.reg
) & 0x7ff) + 1);
44 return parent_rate
/ div
;
47 static u8
clk_periclk_get_parent(struct clk_hw
*hwclk
)
49 struct socfpga_periph_clk
*socfpgaclk
= to_periph_clk(hwclk
);
53 if (socfpgaclk
->bypass_reg
) {
54 mask
= (0x1 << socfpgaclk
->bypass_shift
);
55 parent
= ((readl(socfpgaclk
->bypass_reg
) & mask
) >>
56 socfpgaclk
->bypass_shift
);
58 clk_src
= readl(socfpgaclk
->hw
.reg
);
59 parent
= (clk_src
>> CLK_MGR_FREE_SHIFT
) &
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
,
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
,
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
)
81 struct socfpga_periph_clk
*periph_clk
;
82 struct clk_init_data init
;
84 periph_clk
= kzalloc(sizeof(*periph_clk
), GFP_KERNEL
);
85 if (WARN_ON(!periph_clk
))
88 periph_clk
->hw
.reg
= reg
+ offset
;
91 init
.ops
= &peri_c_clk_ops
;
94 init
.num_parents
= num_parents
;
95 init
.parent_names
= parent_names
? parent_names
: &parent_name
;
97 periph_clk
->hw
.hw
.init
= &init
;
99 clk
= clk_register(NULL
, &periph_clk
->hw
.hw
);
100 if (WARN_ON(IS_ERR(clk
))) {
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
)
115 struct socfpga_periph_clk
*periph_clk
;
116 struct clk_init_data init
;
118 periph_clk
= kzalloc(sizeof(*periph_clk
), GFP_KERNEL
);
119 if (WARN_ON(!periph_clk
))
123 periph_clk
->hw
.reg
= regbase
+ offset
;
125 periph_clk
->hw
.reg
= NULL
;
128 periph_clk
->bypass_reg
= regbase
+ bypass_reg
;
130 periph_clk
->bypass_reg
= NULL
;
131 periph_clk
->bypass_shift
= bypass_shift
;
132 periph_clk
->fixed_div
= fixed_divider
;
135 init
.ops
= &peri_cnt_clk_ops
;
138 init
.num_parents
= num_parents
;
139 init
.parent_names
= parent_names
? parent_names
: &parent_name
;
141 periph_clk
->hw
.hw
.init
= &init
;
143 clk
= clk_register(NULL
, &periph_clk
->hw
.hw
);
144 if (WARN_ON(IS_ERR(clk
))) {