Skip to content

Commit 1b72c59

Browse files
Haylen Chudlan17
authored andcommitted
clk: spacemit: Add clock support for SpacemiT K1 SoC
The clock tree of K1 SoC contains three main types of clock hardware (PLL/DDN/MIX) and has control registers split into several multifunction devices: APBS (PLLs), MPMU, APBC and APMU. All register operations are done through regmap to ensure atomicity between concurrent operations of clock driver and reset, power-domain driver that will be introduced in the future. Signed-off-by: Haylen Chu <heylenay@4d2.org> Reviewed-by: Alex Elder <elder@riscstar.com> Reviewed-by: Inochi Amaoto <inochiama@outlook.com> Reviewed-by: Yixun Lan <dlan@gentoo.org> Link: https://lore.kernel.org/r/20250416135406.16284-4-heylenay@4d2.org Signed-off-by: Yixun Lan <dlan@gentoo.org>
1 parent 8090804 commit 1b72c59

File tree

12 files changed

+2087
-0
lines changed

12 files changed

+2087
-0
lines changed

drivers/clk/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,7 @@ source "drivers/clk/samsung/Kconfig"
517517
source "drivers/clk/sifive/Kconfig"
518518
source "drivers/clk/socfpga/Kconfig"
519519
source "drivers/clk/sophgo/Kconfig"
520+
source "drivers/clk/spacemit/Kconfig"
520521
source "drivers/clk/sprd/Kconfig"
521522
source "drivers/clk/starfive/Kconfig"
522523
source "drivers/clk/sunxi/Kconfig"

drivers/clk/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
145145
obj-$(CONFIG_CLK_SIFIVE) += sifive/
146146
obj-y += socfpga/
147147
obj-y += sophgo/
148+
obj-y += spacemit/
148149
obj-$(CONFIG_PLAT_SPEAR) += spear/
149150
obj-y += sprd/
150151
obj-$(CONFIG_ARCH_STI) += st/

drivers/clk/spacemit/Kconfig

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# SPDX-License-Identifier: GPL-2.0-only
2+
3+
config SPACEMIT_CCU
4+
tristate "Clock support for SpacemiT SoCs"
5+
depends on ARCH_SPACEMIT || COMPILE_TEST
6+
select MFD_SYSCON
7+
help
8+
Say Y to enable clock controller unit support for SpacemiT SoCs.
9+
10+
if SPACEMIT_CCU
11+
12+
config SPACEMIT_K1_CCU
13+
tristate "Support for SpacemiT K1 SoC"
14+
depends on ARCH_SPACEMIT || COMPILE_TEST
15+
help
16+
Support for clock controller unit in SpacemiT K1 SoC.
17+
18+
endif

drivers/clk/spacemit/Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
3+
obj-$(CONFIG_SPACEMIT_K1_CCU) = spacemit-ccu-k1.o
4+
spacemit-ccu-k1-y = ccu_pll.o ccu_mix.o ccu_ddn.o
5+
spacemit-ccu-k1-y += ccu-k1.o

drivers/clk/spacemit/ccu-k1.c

Lines changed: 1154 additions & 0 deletions
Large diffs are not rendered by default.

drivers/clk/spacemit/ccu_common.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* Copyright (c) 2024 SpacemiT Technology Co. Ltd
4+
* Copyright (c) 2024-2025 Haylen Chu <heylenay@4d2.org>
5+
*/
6+
7+
#ifndef _CCU_COMMON_H_
8+
#define _CCU_COMMON_H_
9+
10+
#include <linux/regmap.h>
11+
12+
struct ccu_common {
13+
struct regmap *regmap;
14+
struct regmap *lock_regmap;
15+
16+
union {
17+
/* For DDN and MIX */
18+
struct {
19+
u32 reg_ctrl;
20+
u32 reg_fc;
21+
u32 mask_fc;
22+
};
23+
24+
/* For PLL */
25+
struct {
26+
u32 reg_swcr1;
27+
u32 reg_swcr3;
28+
};
29+
};
30+
31+
struct clk_hw hw;
32+
};
33+
34+
static inline struct ccu_common *hw_to_ccu_common(struct clk_hw *hw)
35+
{
36+
return container_of(hw, struct ccu_common, hw);
37+
}
38+
39+
#define ccu_read(c, reg) \
40+
({ \
41+
u32 tmp; \
42+
regmap_read((c)->regmap, (c)->reg_##reg, &tmp); \
43+
tmp; \
44+
})
45+
#define ccu_update(c, reg, mask, val) \
46+
regmap_update_bits((c)->regmap, (c)->reg_##reg, mask, val)
47+
48+
#endif /* _CCU_COMMON_H_ */

drivers/clk/spacemit/ccu_ddn.c

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright (c) 2024 SpacemiT Technology Co. Ltd
4+
* Copyright (c) 2024-2025 Haylen Chu <heylenay@4d2.org>
5+
*
6+
* DDN stands for "Divider Denominator Numerator", it's M/N clock with a
7+
* constant x2 factor. This clock hardware follows the equation below,
8+
*
9+
* numerator Fin
10+
* 2 * ------------- = -------
11+
* denominator Fout
12+
*
13+
* Thus, Fout could be calculated with,
14+
*
15+
* Fin denominator
16+
* Fout = ----- * -------------
17+
* 2 numerator
18+
*/
19+
20+
#include <linux/clk-provider.h>
21+
#include <linux/rational.h>
22+
23+
#include "ccu_ddn.h"
24+
25+
static unsigned long ccu_ddn_calc_rate(unsigned long prate,
26+
unsigned long num, unsigned long den)
27+
{
28+
return prate * den / 2 / num;
29+
}
30+
31+
static unsigned long ccu_ddn_calc_best_rate(struct ccu_ddn *ddn,
32+
unsigned long rate, unsigned long prate,
33+
unsigned long *num, unsigned long *den)
34+
{
35+
rational_best_approximation(rate, prate / 2,
36+
ddn->den_mask >> ddn->den_shift,
37+
ddn->num_mask >> ddn->num_shift,
38+
den, num);
39+
return ccu_ddn_calc_rate(prate, *num, *den);
40+
}
41+
42+
static long ccu_ddn_round_rate(struct clk_hw *hw, unsigned long rate,
43+
unsigned long *prate)
44+
{
45+
struct ccu_ddn *ddn = hw_to_ccu_ddn(hw);
46+
unsigned long num, den;
47+
48+
return ccu_ddn_calc_best_rate(ddn, rate, *prate, &num, &den);
49+
}
50+
51+
static unsigned long ccu_ddn_recalc_rate(struct clk_hw *hw, unsigned long prate)
52+
{
53+
struct ccu_ddn *ddn = hw_to_ccu_ddn(hw);
54+
unsigned int val, num, den;
55+
56+
val = ccu_read(&ddn->common, ctrl);
57+
58+
num = (val & ddn->num_mask) >> ddn->num_shift;
59+
den = (val & ddn->den_mask) >> ddn->den_shift;
60+
61+
return ccu_ddn_calc_rate(prate, num, den);
62+
}
63+
64+
static int ccu_ddn_set_rate(struct clk_hw *hw, unsigned long rate,
65+
unsigned long prate)
66+
{
67+
struct ccu_ddn *ddn = hw_to_ccu_ddn(hw);
68+
unsigned long num, den;
69+
70+
ccu_ddn_calc_best_rate(ddn, rate, prate, &num, &den);
71+
72+
ccu_update(&ddn->common, ctrl,
73+
ddn->num_mask | ddn->den_mask,
74+
(num << ddn->num_shift) | (den << ddn->den_shift));
75+
76+
return 0;
77+
}
78+
79+
const struct clk_ops spacemit_ccu_ddn_ops = {
80+
.recalc_rate = ccu_ddn_recalc_rate,
81+
.round_rate = ccu_ddn_round_rate,
82+
.set_rate = ccu_ddn_set_rate,
83+
};

drivers/clk/spacemit/ccu_ddn.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* Copyright (c) 2024 SpacemiT Technology Co. Ltd
4+
* Copyright (c) 2024-2025 Haylen Chu <heylenay@4d2.org>
5+
*/
6+
7+
#ifndef _CCU_DDN_H_
8+
#define _CCU_DDN_H_
9+
10+
#include <linux/bitops.h>
11+
#include <linux/clk-provider.h>
12+
13+
#include "ccu_common.h"
14+
15+
struct ccu_ddn {
16+
struct ccu_common common;
17+
unsigned int num_mask;
18+
unsigned int num_shift;
19+
unsigned int den_mask;
20+
unsigned int den_shift;
21+
};
22+
23+
#define CCU_DDN_INIT(_name, _parent, _flags) \
24+
CLK_HW_INIT_HW(#_name, &_parent.common.hw, &spacemit_ccu_ddn_ops, _flags)
25+
26+
#define CCU_DDN_DEFINE(_name, _parent, _reg_ctrl, _num_shift, _num_width, \
27+
_den_shift, _den_width, _flags) \
28+
static struct ccu_ddn _name = { \
29+
.common = { \
30+
.reg_ctrl = _reg_ctrl, \
31+
.hw.init = CCU_DDN_INIT(_name, _parent, _flags), \
32+
}, \
33+
.num_mask = GENMASK(_num_shift + _num_width - 1, _num_shift), \
34+
.num_shift = _num_shift, \
35+
.den_mask = GENMASK(_den_shift + _den_width - 1, _den_shift), \
36+
.den_shift = _den_shift, \
37+
}
38+
39+
static inline struct ccu_ddn *hw_to_ccu_ddn(struct clk_hw *hw)
40+
{
41+
struct ccu_common *common = hw_to_ccu_common(hw);
42+
43+
return container_of(common, struct ccu_ddn, common);
44+
}
45+
46+
extern const struct clk_ops spacemit_ccu_ddn_ops;
47+
48+
#endif

0 commit comments

Comments
 (0)