mirror of
https://github.com/Dasharo/linux.git
synced 2026-03-06 15:25:10 -08:00
clk: sophgo: Add clock support for CV1800 SoC
Add clock definition and driver code for CV1800 SoC. Signed-off-by: Inochi Amaoto <inochiama@outlook.com> Link:6f4e9b8ecb/duo/datasheet/CV180X-Clock-v1.xlsxLink:6f4e9b8ecb/duo/datasheet/CV1800B-CV1801B-Preliminary-Datasheet-full-en.pdfLink: https://lore.kernel.org/r/IA1PR20MB49534F37F802CAF117364D66BB262@IA1PR20MB4953.namprd20.prod.outlook.com Signed-off-by: Stephen Boyd <sboyd@kernel.org>
This commit is contained in:
committed by
Stephen Boyd
parent
50e45d3b03
commit
80fd61ec46
@@ -489,6 +489,7 @@ source "drivers/clk/rockchip/Kconfig"
|
||||
source "drivers/clk/samsung/Kconfig"
|
||||
source "drivers/clk/sifive/Kconfig"
|
||||
source "drivers/clk/socfpga/Kconfig"
|
||||
source "drivers/clk/sophgo/Kconfig"
|
||||
source "drivers/clk/sprd/Kconfig"
|
||||
source "drivers/clk/starfive/Kconfig"
|
||||
source "drivers/clk/sunxi/Kconfig"
|
||||
|
||||
@@ -118,6 +118,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
|
||||
obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
|
||||
obj-$(CONFIG_CLK_SIFIVE) += sifive/
|
||||
obj-y += socfpga/
|
||||
obj-y += sophgo/
|
||||
obj-$(CONFIG_PLAT_SPEAR) += spear/
|
||||
obj-y += sprd/
|
||||
obj-$(CONFIG_ARCH_STI) += st/
|
||||
|
||||
11
drivers/clk/sophgo/Kconfig
Normal file
11
drivers/clk/sophgo/Kconfig
Normal file
@@ -0,0 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# common clock support for SOPHGO SoC family.
|
||||
|
||||
config CLK_SOPHGO_CV1800
|
||||
tristate "Support for the Sophgo CV1800 series SoCs clock controller"
|
||||
depends on ARCH_SOPHGO || COMPILE_TEST
|
||||
help
|
||||
This driver supports clock controller of Sophgo CV18XX series SoC.
|
||||
The driver require a 25MHz Oscillator to function generate clock.
|
||||
It includes PLLs, common clock function and some vendor clock for
|
||||
IPs of CV18XX series SoC
|
||||
7
drivers/clk/sophgo/Makefile
Normal file
7
drivers/clk/sophgo/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_CLK_SOPHGO_CV1800) += clk-sophgo-cv1800.o
|
||||
|
||||
clk-sophgo-cv1800-y += clk-cv1800.o
|
||||
clk-sophgo-cv1800-y += clk-cv18xx-common.o
|
||||
clk-sophgo-cv1800-y += clk-cv18xx-ip.o
|
||||
clk-sophgo-cv1800-y += clk-cv18xx-pll.o
|
||||
1327
drivers/clk/sophgo/clk-cv1800.c
Normal file
1327
drivers/clk/sophgo/clk-cv1800.c
Normal file
File diff suppressed because it is too large
Load Diff
122
drivers/clk/sophgo/clk-cv1800.h
Normal file
122
drivers/clk/sophgo/clk-cv1800.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
|
||||
*/
|
||||
|
||||
#ifndef _CLK_SOPHGO_CV1800_H_
|
||||
#define _CLK_SOPHGO_CV1800_H_
|
||||
|
||||
#include <dt-bindings/clock/sophgo,cv1800.h>
|
||||
|
||||
#define CV1800_CLK_MAX (CLK_XTAL_AP + 1)
|
||||
|
||||
#define REG_PLL_G2_CTRL 0x800
|
||||
#define REG_PLL_G2_STATUS 0x804
|
||||
#define REG_MIPIMPLL_CSR 0x808
|
||||
#define REG_A0PLL_CSR 0x80C
|
||||
#define REG_DISPPLL_CSR 0x810
|
||||
#define REG_CAM0PLL_CSR 0x814
|
||||
#define REG_CAM1PLL_CSR 0x818
|
||||
#define REG_PLL_G2_SSC_SYN_CTRL 0x840
|
||||
#define REG_A0PLL_SSC_SYN_CTRL 0x850
|
||||
#define REG_A0PLL_SSC_SYN_SET 0x854
|
||||
#define REG_A0PLL_SSC_SYN_SPAN 0x858
|
||||
#define REG_A0PLL_SSC_SYN_STEP 0x85C
|
||||
#define REG_DISPPLL_SSC_SYN_CTRL 0x860
|
||||
#define REG_DISPPLL_SSC_SYN_SET 0x864
|
||||
#define REG_DISPPLL_SSC_SYN_SPAN 0x868
|
||||
#define REG_DISPPLL_SSC_SYN_STEP 0x86C
|
||||
#define REG_CAM0PLL_SSC_SYN_CTRL 0x870
|
||||
#define REG_CAM0PLL_SSC_SYN_SET 0x874
|
||||
#define REG_CAM0PLL_SSC_SYN_SPAN 0x878
|
||||
#define REG_CAM0PLL_SSC_SYN_STEP 0x87C
|
||||
#define REG_CAM1PLL_SSC_SYN_CTRL 0x880
|
||||
#define REG_CAM1PLL_SSC_SYN_SET 0x884
|
||||
#define REG_CAM1PLL_SSC_SYN_SPAN 0x888
|
||||
#define REG_CAM1PLL_SSC_SYN_STEP 0x88C
|
||||
#define REG_APLL_FRAC_DIV_CTRL 0x890
|
||||
#define REG_APLL_FRAC_DIV_M 0x894
|
||||
#define REG_APLL_FRAC_DIV_N 0x898
|
||||
#define REG_MIPIMPLL_CLK_CSR 0x8A0
|
||||
#define REG_A0PLL_CLK_CSR 0x8A4
|
||||
#define REG_DISPPLL_CLK_CSR 0x8A8
|
||||
#define REG_CAM0PLL_CLK_CSR 0x8AC
|
||||
#define REG_CAM1PLL_CLK_CSR 0x8B0
|
||||
#define REG_CLK_CAM0_SRC_DIV 0x8C0
|
||||
#define REG_CLK_CAM1_SRC_DIV 0x8C4
|
||||
|
||||
/* top_pll_g6 */
|
||||
#define REG_PLL_G6_CTRL 0x900
|
||||
#define REG_PLL_G6_STATUS 0x904
|
||||
#define REG_MPLL_CSR 0x908
|
||||
#define REG_TPLL_CSR 0x90C
|
||||
#define REG_FPLL_CSR 0x910
|
||||
#define REG_PLL_G6_SSC_SYN_CTRL 0x940
|
||||
#define REG_DPLL_SSC_SYN_CTRL 0x950
|
||||
#define REG_DPLL_SSC_SYN_SET 0x954
|
||||
#define REG_DPLL_SSC_SYN_SPAN 0x958
|
||||
#define REG_DPLL_SSC_SYN_STEP 0x95C
|
||||
#define REG_MPLL_SSC_SYN_CTRL 0x960
|
||||
#define REG_MPLL_SSC_SYN_SET 0x964
|
||||
#define REG_MPLL_SSC_SYN_SPAN 0x968
|
||||
#define REG_MPLL_SSC_SYN_STEP 0x96C
|
||||
#define REG_TPLL_SSC_SYN_CTRL 0x970
|
||||
#define REG_TPLL_SSC_SYN_SET 0x974
|
||||
#define REG_TPLL_SSC_SYN_SPAN 0x978
|
||||
#define REG_TPLL_SSC_SYN_STEP 0x97C
|
||||
|
||||
/* clkgen */
|
||||
#define REG_CLK_EN_0 0x000
|
||||
#define REG_CLK_EN_1 0x004
|
||||
#define REG_CLK_EN_2 0x008
|
||||
#define REG_CLK_EN_3 0x00C
|
||||
#define REG_CLK_EN_4 0x010
|
||||
#define REG_CLK_SEL_0 0x020
|
||||
#define REG_CLK_BYP_0 0x030
|
||||
#define REG_CLK_BYP_1 0x034
|
||||
|
||||
#define REG_DIV_CLK_A53_0 0x040
|
||||
#define REG_DIV_CLK_A53_1 0x044
|
||||
#define REG_DIV_CLK_CPU_AXI0 0x048
|
||||
#define REG_DIV_CLK_CPU_GIC 0x050
|
||||
#define REG_DIV_CLK_TPU 0x054
|
||||
#define REG_DIV_CLK_EMMC 0x064
|
||||
#define REG_DIV_CLK_EMMC_100K 0x06C
|
||||
#define REG_DIV_CLK_SD0 0x070
|
||||
#define REG_DIV_CLK_SD0_100K 0x078
|
||||
#define REG_DIV_CLK_SD1 0x07C
|
||||
#define REG_DIV_CLK_SD1_100K 0x084
|
||||
#define REG_DIV_CLK_SPI_NAND 0x088
|
||||
#define REG_DIV_CLK_ETH0_500M 0x08C
|
||||
#define REG_DIV_CLK_ETH1_500M 0x090
|
||||
#define REG_DIV_CLK_GPIO_DB 0x094
|
||||
#define REG_DIV_CLK_SDMA_AUD0 0x098
|
||||
#define REG_DIV_CLK_SDMA_AUD1 0x09C
|
||||
#define REG_DIV_CLK_SDMA_AUD2 0x0A0
|
||||
#define REG_DIV_CLK_SDMA_AUD3 0x0A4
|
||||
#define REG_DIV_CLK_CAM0_200 0x0A8
|
||||
#define REG_DIV_CLK_AXI4 0x0B8
|
||||
#define REG_DIV_CLK_AXI6 0x0BC
|
||||
#define REG_DIV_CLK_DSI_ESC 0x0C4
|
||||
#define REG_DIV_CLK_AXI_VIP 0x0C8
|
||||
#define REG_DIV_CLK_SRC_VIP_SYS_0 0x0D0
|
||||
#define REG_DIV_CLK_SRC_VIP_SYS_1 0x0D8
|
||||
#define REG_DIV_CLK_DISP_SRC_VIP 0x0E0
|
||||
#define REG_DIV_CLK_AXI_VIDEO_CODEC 0x0E4
|
||||
#define REG_DIV_CLK_VC_SRC0 0x0EC
|
||||
#define REG_DIV_CLK_1M 0x0FC
|
||||
#define REG_DIV_CLK_SPI 0x100
|
||||
#define REG_DIV_CLK_I2C 0x104
|
||||
#define REG_DIV_CLK_SRC_VIP_SYS_2 0x110
|
||||
#define REG_DIV_CLK_AUDSRC 0x118
|
||||
#define REG_DIV_CLK_PWM_SRC_0 0x120
|
||||
#define REG_DIV_CLK_AP_DEBUG 0x128
|
||||
#define REG_DIV_CLK_RTCSYS_SRC_0 0x12C
|
||||
#define REG_DIV_CLK_C906_0_0 0x130
|
||||
#define REG_DIV_CLK_C906_0_1 0x134
|
||||
#define REG_DIV_CLK_C906_1_0 0x138
|
||||
#define REG_DIV_CLK_C906_1_1 0x13C
|
||||
#define REG_DIV_CLK_SRC_VIP_SYS_3 0x140
|
||||
#define REG_DIV_CLK_SRC_VIP_SYS_4 0x144
|
||||
|
||||
#endif /* _CLK_SOPHGO_CV1800_H_ */
|
||||
66
drivers/clk/sophgo/clk-cv18xx-common.c
Normal file
66
drivers/clk/sophgo/clk-cv18xx-common.c
Normal file
@@ -0,0 +1,66 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/bug.h>
|
||||
|
||||
#include "clk-cv18xx-common.h"
|
||||
|
||||
int cv1800_clk_setbit(struct cv1800_clk_common *common,
|
||||
struct cv1800_clk_regbit *field)
|
||||
{
|
||||
u32 mask = BIT(field->shift);
|
||||
u32 value;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(common->lock, flags);
|
||||
|
||||
value = readl(common->base + field->reg);
|
||||
writel(value | mask, common->base + field->reg);
|
||||
|
||||
spin_unlock_irqrestore(common->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cv1800_clk_clearbit(struct cv1800_clk_common *common,
|
||||
struct cv1800_clk_regbit *field)
|
||||
{
|
||||
u32 mask = BIT(field->shift);
|
||||
u32 value;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(common->lock, flags);
|
||||
|
||||
value = readl(common->base + field->reg);
|
||||
writel(value & ~mask, common->base + field->reg);
|
||||
|
||||
spin_unlock_irqrestore(common->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cv1800_clk_checkbit(struct cv1800_clk_common *common,
|
||||
struct cv1800_clk_regbit *field)
|
||||
{
|
||||
return readl(common->base + field->reg) & BIT(field->shift);
|
||||
}
|
||||
|
||||
#define PLL_LOCK_TIMEOUT_US (200 * 1000)
|
||||
|
||||
void cv1800_clk_wait_for_lock(struct cv1800_clk_common *common,
|
||||
u32 reg, u32 lock)
|
||||
{
|
||||
void __iomem *addr = common->base + reg;
|
||||
u32 regval;
|
||||
|
||||
if (!lock)
|
||||
return;
|
||||
|
||||
WARN_ON(readl_relaxed_poll_timeout(addr, regval, regval & lock,
|
||||
100, PLL_LOCK_TIMEOUT_US));
|
||||
}
|
||||
81
drivers/clk/sophgo/clk-cv18xx-common.h
Normal file
81
drivers/clk/sophgo/clk-cv18xx-common.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
|
||||
*/
|
||||
|
||||
#ifndef _CLK_SOPHGO_CV18XX_IP_H_
|
||||
#define _CLK_SOPHGO_CV18XX_IP_H_
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
struct cv1800_clk_common {
|
||||
void __iomem *base;
|
||||
spinlock_t *lock;
|
||||
struct clk_hw hw;
|
||||
unsigned long features;
|
||||
};
|
||||
|
||||
#define CV1800_CLK_COMMON(_name, _parents, _op, _flags) \
|
||||
{ \
|
||||
.hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parents, \
|
||||
_op, _flags), \
|
||||
}
|
||||
|
||||
static inline struct cv1800_clk_common *
|
||||
hw_to_cv1800_clk_common(struct clk_hw *hw)
|
||||
{
|
||||
return container_of(hw, struct cv1800_clk_common, hw);
|
||||
}
|
||||
|
||||
struct cv1800_clk_regbit {
|
||||
u16 reg;
|
||||
s8 shift;
|
||||
};
|
||||
|
||||
struct cv1800_clk_regfield {
|
||||
u16 reg;
|
||||
u8 shift;
|
||||
u8 width;
|
||||
s16 initval;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
#define CV1800_CLK_BIT(_reg, _shift) \
|
||||
{ \
|
||||
.reg = _reg, \
|
||||
.shift = _shift, \
|
||||
}
|
||||
|
||||
#define CV1800_CLK_REG(_reg, _shift, _width, _initval, _flags) \
|
||||
{ \
|
||||
.reg = _reg, \
|
||||
.shift = _shift, \
|
||||
.width = _width, \
|
||||
.initval = _initval, \
|
||||
.flags = _flags, \
|
||||
}
|
||||
|
||||
#define cv1800_clk_regfield_genmask(_reg) \
|
||||
GENMASK((_reg)->shift + (_reg)->width - 1, (_reg)->shift)
|
||||
#define cv1800_clk_regfield_get(_val, _reg) \
|
||||
(((_val) >> (_reg)->shift) & GENMASK((_reg)->width - 1, 0))
|
||||
#define cv1800_clk_regfield_set(_val, _new, _reg) \
|
||||
(((_val) & ~cv1800_clk_regfield_genmask((_reg))) | \
|
||||
(((_new) & GENMASK((_reg)->width - 1, 0)) << (_reg)->shift))
|
||||
|
||||
#define _CV1800_SET_FIELD(_reg, _val, _field) \
|
||||
(((_reg) & ~(_field)) | FIELD_PREP((_field), (_val)))
|
||||
|
||||
int cv1800_clk_setbit(struct cv1800_clk_common *common,
|
||||
struct cv1800_clk_regbit *field);
|
||||
int cv1800_clk_clearbit(struct cv1800_clk_common *common,
|
||||
struct cv1800_clk_regbit *field);
|
||||
int cv1800_clk_checkbit(struct cv1800_clk_common *common,
|
||||
struct cv1800_clk_regbit *field);
|
||||
|
||||
void cv1800_clk_wait_for_lock(struct cv1800_clk_common *common,
|
||||
u32 reg, u32 lock);
|
||||
|
||||
#endif // _CLK_SOPHGO_CV18XX_IP_H_
|
||||
887
drivers/clk/sophgo/clk-cv18xx-ip.c
Normal file
887
drivers/clk/sophgo/clk-cv18xx-ip.c
Normal file
File diff suppressed because it is too large
Load Diff
261
drivers/clk/sophgo/clk-cv18xx-ip.h
Normal file
261
drivers/clk/sophgo/clk-cv18xx-ip.h
Normal file
@@ -0,0 +1,261 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
|
||||
*/
|
||||
|
||||
#ifndef _CLK_SOPHGO_CV1800_IP_H_
|
||||
#define _CLK_SOPHGO_CV1800_IP_H_
|
||||
|
||||
#include "clk-cv18xx-common.h"
|
||||
|
||||
struct cv1800_clk_gate {
|
||||
struct cv1800_clk_common common;
|
||||
struct cv1800_clk_regbit gate;
|
||||
};
|
||||
|
||||
struct cv1800_clk_div_data {
|
||||
u32 reg;
|
||||
u32 mask;
|
||||
u32 width;
|
||||
u32 init;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
struct cv1800_clk_div {
|
||||
struct cv1800_clk_common common;
|
||||
struct cv1800_clk_regbit gate;
|
||||
struct cv1800_clk_regfield div;
|
||||
};
|
||||
|
||||
struct cv1800_clk_bypass_div {
|
||||
struct cv1800_clk_div div;
|
||||
struct cv1800_clk_regbit bypass;
|
||||
};
|
||||
|
||||
struct cv1800_clk_mux {
|
||||
struct cv1800_clk_common common;
|
||||
struct cv1800_clk_regbit gate;
|
||||
struct cv1800_clk_regfield div;
|
||||
struct cv1800_clk_regfield mux;
|
||||
};
|
||||
|
||||
struct cv1800_clk_bypass_mux {
|
||||
struct cv1800_clk_mux mux;
|
||||
struct cv1800_clk_regbit bypass;
|
||||
};
|
||||
|
||||
struct cv1800_clk_mmux {
|
||||
struct cv1800_clk_common common;
|
||||
struct cv1800_clk_regbit gate;
|
||||
struct cv1800_clk_regfield div[2];
|
||||
struct cv1800_clk_regfield mux[2];
|
||||
struct cv1800_clk_regbit bypass;
|
||||
struct cv1800_clk_regbit clk_sel;
|
||||
const s8 *parent2sel;
|
||||
const u8 *sel2parent[2];
|
||||
};
|
||||
|
||||
struct cv1800_clk_audio {
|
||||
struct cv1800_clk_common common;
|
||||
struct cv1800_clk_regbit src_en;
|
||||
struct cv1800_clk_regbit output_en;
|
||||
struct cv1800_clk_regbit div_en;
|
||||
struct cv1800_clk_regbit div_up;
|
||||
struct cv1800_clk_regfield m;
|
||||
struct cv1800_clk_regfield n;
|
||||
u32 target_rate;
|
||||
};
|
||||
|
||||
#define CV1800_GATE(_name, _parent, _gate_reg, _gate_shift, _flags) \
|
||||
struct cv1800_clk_gate _name = { \
|
||||
.common = CV1800_CLK_COMMON(#_name, _parent, \
|
||||
&cv1800_clk_gate_ops, \
|
||||
_flags), \
|
||||
.gate = CV1800_CLK_BIT(_gate_reg, _gate_shift), \
|
||||
}
|
||||
|
||||
#define _CV1800_DIV(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, _div_width, _div_init, \
|
||||
_div_flag, _ops, _flags) \
|
||||
{ \
|
||||
.common = CV1800_CLK_COMMON(#_name, _parent, \
|
||||
_ops, _flags), \
|
||||
.gate = CV1800_CLK_BIT(_gate_reg, \
|
||||
_gate_shift), \
|
||||
.div = CV1800_CLK_REG(_div_reg, _div_shift, \
|
||||
_div_width, _div_init, \
|
||||
_div_flag), \
|
||||
}
|
||||
|
||||
#define _CV1800_FIXED_DIV_FLAG \
|
||||
(CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ROUND_CLOSEST)
|
||||
|
||||
#define _CV1800_FIXED_DIV(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_fix_div, _ops, _flags) \
|
||||
{ \
|
||||
.common = CV1800_CLK_COMMON(#_name, _parent, \
|
||||
_ops, _flags), \
|
||||
.gate = CV1800_CLK_BIT(_gate_reg, \
|
||||
_gate_shift), \
|
||||
.div = CV1800_CLK_REG(0, 0, 0, \
|
||||
_fix_div, \
|
||||
_CV1800_FIXED_DIV_FLAG),\
|
||||
}
|
||||
|
||||
#define CV1800_DIV(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, _div_width, _div_init, \
|
||||
_div_flag, _flags) \
|
||||
struct cv1800_clk_div _name = \
|
||||
_CV1800_DIV(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, _div_width, _div_init,\
|
||||
_div_flag, &cv1800_clk_div_ops, _flags)
|
||||
|
||||
#define CV1800_BYPASS_DIV(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, _div_width, _div_init, \
|
||||
_div_flag, _bypass_reg, _bypass_shift, _flags)\
|
||||
struct cv1800_clk_bypass_div _name = { \
|
||||
.div = _CV1800_DIV(_name, _parent, \
|
||||
_gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, \
|
||||
_div_width, _div_init, _div_flag, \
|
||||
&cv1800_clk_bypass_div_ops, \
|
||||
_flags), \
|
||||
.bypass = CV1800_CLK_BIT(_bypass_reg, _bypass_shift), \
|
||||
}
|
||||
|
||||
#define CV1800_FIXED_DIV(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_fix_div, _flags) \
|
||||
struct cv1800_clk_div _name = \
|
||||
_CV1800_FIXED_DIV(_name, _parent, \
|
||||
_gate_reg, _gate_shift, \
|
||||
_fix_div, \
|
||||
&cv1800_clk_div_ops, _flags) \
|
||||
|
||||
#define CV1800_BYPASS_FIXED_DIV(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_fix_div, _bypass_reg, _bypass_shift, \
|
||||
_flags) \
|
||||
struct cv1800_clk_bypass_div _name = { \
|
||||
.div = _CV1800_FIXED_DIV(_name, _parent, \
|
||||
_gate_reg, _gate_shift, \
|
||||
_fix_div, \
|
||||
&cv1800_clk_bypass_div_ops, \
|
||||
_flags), \
|
||||
.bypass = CV1800_CLK_BIT(_bypass_reg, _bypass_shift), \
|
||||
}
|
||||
|
||||
#define _CV1800_MUX(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, _div_width, _div_init, \
|
||||
_div_flag, \
|
||||
_mux_reg, _mux_shift, _mux_width, \
|
||||
_ops, _flags) \
|
||||
{ \
|
||||
.common = CV1800_CLK_COMMON(#_name, _parent, \
|
||||
_ops, _flags), \
|
||||
.gate = CV1800_CLK_BIT(_gate_reg, \
|
||||
_gate_shift), \
|
||||
.div = CV1800_CLK_REG(_div_reg, _div_shift, \
|
||||
_div_width, _div_init, \
|
||||
_div_flag), \
|
||||
.mux = CV1800_CLK_REG(_mux_reg, _mux_shift, \
|
||||
_mux_width, 0, 0), \
|
||||
}
|
||||
|
||||
#define CV1800_MUX(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, _div_width, _div_init, \
|
||||
_div_flag, \
|
||||
_mux_reg, _mux_shift, _mux_width, _flags) \
|
||||
struct cv1800_clk_mux _name = \
|
||||
_CV1800_MUX(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, _div_width, _div_init,\
|
||||
_div_flag, _mux_reg, _mux_shift, _mux_width,\
|
||||
&cv1800_clk_mux_ops, _flags)
|
||||
|
||||
#define CV1800_BYPASS_MUX(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, _div_width, _div_init, \
|
||||
_div_flag, \
|
||||
_mux_reg, _mux_shift, _mux_width, \
|
||||
_bypass_reg, _bypass_shift, _flags) \
|
||||
struct cv1800_clk_bypass_mux _name = { \
|
||||
.mux = _CV1800_MUX(_name, _parent, \
|
||||
_gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, _div_width, \
|
||||
_div_init, _div_flag, \
|
||||
_mux_reg, _mux_shift, _mux_width, \
|
||||
&cv1800_clk_bypass_mux_ops, \
|
||||
_flags), \
|
||||
.bypass = CV1800_CLK_BIT(_bypass_reg, _bypass_shift), \
|
||||
}
|
||||
|
||||
#define CV1800_MMUX(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_div0_reg, _div0_shift, _div0_width, _div0_init, \
|
||||
_div0_flag, \
|
||||
_div1_reg, _div1_shift, _div1_width, _div1_init, \
|
||||
_div1_flag, \
|
||||
_mux0_reg, _mux0_shift, _mux0_width, \
|
||||
_mux1_reg, _mux1_shift, _mux1_width, \
|
||||
_bypass_reg, _bypass_shift, \
|
||||
_clk_sel_reg, _clk_sel_shift, \
|
||||
_parent2sel, _sel2parent0, _sel2parent1, _flags) \
|
||||
struct cv1800_clk_mmux _name = { \
|
||||
.common = CV1800_CLK_COMMON(#_name, _parent, \
|
||||
&cv1800_clk_mmux_ops,\
|
||||
_flags), \
|
||||
.gate = CV1800_CLK_BIT(_gate_reg, _gate_shift),\
|
||||
.div = { \
|
||||
CV1800_CLK_REG(_div0_reg, _div0_shift, \
|
||||
_div0_width, _div0_init, \
|
||||
_div0_flag), \
|
||||
CV1800_CLK_REG(_div1_reg, _div1_shift, \
|
||||
_div1_width, _div1_init, \
|
||||
_div1_flag), \
|
||||
}, \
|
||||
.mux = { \
|
||||
CV1800_CLK_REG(_mux0_reg, _mux0_shift, \
|
||||
_mux0_width, 0, 0), \
|
||||
CV1800_CLK_REG(_mux1_reg, _mux1_shift, \
|
||||
_mux1_width, 0, 0), \
|
||||
}, \
|
||||
.bypass = CV1800_CLK_BIT(_bypass_reg, \
|
||||
_bypass_shift), \
|
||||
.clk_sel = CV1800_CLK_BIT(_clk_sel_reg, \
|
||||
_clk_sel_shift), \
|
||||
.parent2sel = _parent2sel, \
|
||||
.sel2parent = { _sel2parent0, _sel2parent1 }, \
|
||||
}
|
||||
|
||||
#define CV1800_ACLK(_name, _parent, \
|
||||
_src_en_reg, _src_en_reg_shift, \
|
||||
_output_en_reg, _output_en_shift, \
|
||||
_div_en_reg, _div_en_reg_shift, \
|
||||
_div_up_reg, _div_up_reg_shift, \
|
||||
_m_reg, _m_shift, _m_width, _m_flag, \
|
||||
_n_reg, _n_shift, _n_width, _n_flag, \
|
||||
_target_rate, _flags) \
|
||||
struct cv1800_clk_audio _name = { \
|
||||
.common = CV1800_CLK_COMMON(#_name, _parent, \
|
||||
&cv1800_clk_audio_ops,\
|
||||
_flags), \
|
||||
.src_en = CV1800_CLK_BIT(_src_en_reg, \
|
||||
_src_en_reg_shift), \
|
||||
.output_en = CV1800_CLK_BIT(_output_en_reg, \
|
||||
_output_en_shift), \
|
||||
.div_en = CV1800_CLK_BIT(_div_en_reg, \
|
||||
_div_en_reg_shift), \
|
||||
.div_up = CV1800_CLK_BIT(_div_up_reg, \
|
||||
_div_up_reg_shift), \
|
||||
.m = CV1800_CLK_REG(_m_reg, _m_shift, \
|
||||
_m_width, 0, _m_flag), \
|
||||
.n = CV1800_CLK_REG(_n_reg, _n_shift, \
|
||||
_n_width, 0, _n_flag), \
|
||||
.target_rate = _target_rate, \
|
||||
}
|
||||
|
||||
extern const struct clk_ops cv1800_clk_gate_ops;
|
||||
extern const struct clk_ops cv1800_clk_div_ops;
|
||||
extern const struct clk_ops cv1800_clk_bypass_div_ops;
|
||||
extern const struct clk_ops cv1800_clk_mux_ops;
|
||||
extern const struct clk_ops cv1800_clk_bypass_mux_ops;
|
||||
extern const struct clk_ops cv1800_clk_mmux_ops;
|
||||
extern const struct clk_ops cv1800_clk_audio_ops;
|
||||
|
||||
#endif // _CLK_SOPHGO_CV1800_IP_H_
|
||||
420
drivers/clk/sophgo/clk-cv18xx-pll.c
Normal file
420
drivers/clk/sophgo/clk-cv18xx-pll.c
Normal file
@@ -0,0 +1,420 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "clk-cv18xx-pll.h"
|
||||
|
||||
static inline struct cv1800_clk_pll *hw_to_cv1800_clk_pll(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw);
|
||||
|
||||
return container_of(common, struct cv1800_clk_pll, common);
|
||||
}
|
||||
|
||||
static unsigned long ipll_calc_rate(unsigned long parent_rate,
|
||||
unsigned long pre_div_sel,
|
||||
unsigned long div_sel,
|
||||
unsigned long post_div_sel)
|
||||
{
|
||||
uint64_t rate = parent_rate;
|
||||
|
||||
rate *= div_sel;
|
||||
do_div(rate, pre_div_sel * post_div_sel);
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static unsigned long ipll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
u32 value;
|
||||
|
||||
value = readl(pll->common.base + pll->pll_reg);
|
||||
|
||||
return ipll_calc_rate(parent_rate,
|
||||
PLL_GET_PRE_DIV_SEL(value),
|
||||
PLL_GET_DIV_SEL(value),
|
||||
PLL_GET_POST_DIV_SEL(value));
|
||||
}
|
||||
|
||||
static int ipll_find_rate(const struct cv1800_clk_pll_limit *limit,
|
||||
unsigned long prate, unsigned long *rate,
|
||||
u32 *value)
|
||||
{
|
||||
unsigned long best_rate = 0;
|
||||
unsigned long trate = *rate;
|
||||
unsigned long pre_div_sel = 0, div_sel = 0, post_div_sel = 0;
|
||||
unsigned long pre, div, post;
|
||||
u32 detected = *value;
|
||||
unsigned long tmp;
|
||||
|
||||
for_each_pll_limit_range(pre, &limit->pre_div) {
|
||||
for_each_pll_limit_range(div, &limit->div) {
|
||||
for_each_pll_limit_range(post, &limit->post_div) {
|
||||
tmp = ipll_calc_rate(prate, pre, div, post);
|
||||
|
||||
if (tmp > trate)
|
||||
continue;
|
||||
|
||||
if ((trate - tmp) < (trate - best_rate)) {
|
||||
best_rate = tmp;
|
||||
pre_div_sel = pre;
|
||||
div_sel = div;
|
||||
post_div_sel = post;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best_rate) {
|
||||
detected = PLL_SET_PRE_DIV_SEL(detected, pre_div_sel);
|
||||
detected = PLL_SET_POST_DIV_SEL(detected, post_div_sel);
|
||||
detected = PLL_SET_DIV_SEL(detected, div_sel);
|
||||
*value = detected;
|
||||
*rate = best_rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ipll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
|
||||
{
|
||||
u32 val;
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
|
||||
return ipll_find_rate(pll->pll_limit, req->best_parent_rate,
|
||||
&req->rate, &val);
|
||||
}
|
||||
|
||||
static void pll_get_mode_ctrl(unsigned long div_sel,
|
||||
bool (*mode_ctrl_check)(unsigned long,
|
||||
unsigned long,
|
||||
unsigned long),
|
||||
const struct cv1800_clk_pll_limit *limit,
|
||||
u32 *value)
|
||||
{
|
||||
unsigned long ictrl = 0, mode = 0;
|
||||
u32 detected = *value;
|
||||
|
||||
for_each_pll_limit_range(mode, &limit->mode) {
|
||||
for_each_pll_limit_range(ictrl, &limit->ictrl) {
|
||||
if (mode_ctrl_check(div_sel, ictrl, mode)) {
|
||||
detected = PLL_SET_SEL_MODE(detected, mode);
|
||||
detected = PLL_SET_ICTRL(detected, ictrl);
|
||||
*value = detected;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool ipll_check_mode_ctrl_restrict(unsigned long div_sel,
|
||||
unsigned long ictrl,
|
||||
unsigned long mode)
|
||||
{
|
||||
unsigned long left_rest = 20 * div_sel;
|
||||
unsigned long right_rest = 35 * div_sel;
|
||||
unsigned long test = 184 * (1 + mode) * (1 + ictrl) / 2;
|
||||
|
||||
return test > left_rest && test <= right_rest;
|
||||
}
|
||||
|
||||
static int ipll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u32 regval, detected = 0;
|
||||
unsigned long flags;
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
|
||||
ipll_find_rate(pll->pll_limit, parent_rate, &rate, &detected);
|
||||
pll_get_mode_ctrl(PLL_GET_DIV_SEL(detected),
|
||||
ipll_check_mode_ctrl_restrict,
|
||||
pll->pll_limit, &detected);
|
||||
|
||||
spin_lock_irqsave(pll->common.lock, flags);
|
||||
|
||||
regval = readl(pll->common.base + pll->pll_reg);
|
||||
regval = PLL_COPY_REG(regval, detected);
|
||||
|
||||
writel(regval, pll->common.base + pll->pll_reg);
|
||||
|
||||
spin_unlock_irqrestore(pll->common.lock, flags);
|
||||
|
||||
cv1800_clk_wait_for_lock(&pll->common, pll->pll_status.reg,
|
||||
BIT(pll->pll_status.shift));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pll_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
|
||||
return cv1800_clk_clearbit(&pll->common, &pll->pll_pwd);
|
||||
}
|
||||
|
||||
static void pll_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
|
||||
cv1800_clk_setbit(&pll->common, &pll->pll_pwd);
|
||||
}
|
||||
|
||||
static int pll_is_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
|
||||
return cv1800_clk_checkbit(&pll->common, &pll->pll_pwd) == 0;
|
||||
}
|
||||
|
||||
const struct clk_ops cv1800_clk_ipll_ops = {
|
||||
.disable = pll_disable,
|
||||
.enable = pll_enable,
|
||||
.is_enabled = pll_is_enable,
|
||||
|
||||
.recalc_rate = ipll_recalc_rate,
|
||||
.determine_rate = ipll_determine_rate,
|
||||
.set_rate = ipll_set_rate,
|
||||
};
|
||||
|
||||
#define PLL_SYN_FACTOR_DOT_POS 26
|
||||
#define PLL_SYN_FACTOR_MINIMUM ((4 << PLL_SYN_FACTOR_DOT_POS) + 1)
|
||||
|
||||
static bool fpll_is_factional_mode(struct cv1800_clk_pll *pll)
|
||||
{
|
||||
return cv1800_clk_checkbit(&pll->common, &pll->pll_syn->en);
|
||||
}
|
||||
|
||||
static unsigned long fpll_calc_rate(unsigned long parent_rate,
|
||||
unsigned long pre_div_sel,
|
||||
unsigned long div_sel,
|
||||
unsigned long post_div_sel,
|
||||
unsigned long ssc_syn_set,
|
||||
bool is_full_parent)
|
||||
{
|
||||
u64 dividend = parent_rate * div_sel;
|
||||
u64 factor = ssc_syn_set * pre_div_sel * post_div_sel;
|
||||
unsigned long rate;
|
||||
|
||||
dividend <<= PLL_SYN_FACTOR_DOT_POS - 1;
|
||||
rate = dividend / factor;
|
||||
dividend %= factor;
|
||||
|
||||
if (is_full_parent) {
|
||||
dividend <<= 1;
|
||||
rate <<= 1;
|
||||
}
|
||||
|
||||
rate += DIV64_U64_ROUND_CLOSEST(dividend, factor);
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static unsigned long fpll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
u32 value;
|
||||
bool clk_full;
|
||||
u32 syn_set;
|
||||
|
||||
if (!fpll_is_factional_mode(pll))
|
||||
return ipll_recalc_rate(hw, parent_rate);
|
||||
|
||||
syn_set = readl(pll->common.base + pll->pll_syn->set);
|
||||
|
||||
if (syn_set == 0)
|
||||
return 0;
|
||||
|
||||
clk_full = cv1800_clk_checkbit(&pll->common,
|
||||
&pll->pll_syn->clk_half);
|
||||
|
||||
value = readl(pll->common.base + pll->pll_reg);
|
||||
|
||||
return fpll_calc_rate(parent_rate,
|
||||
PLL_GET_PRE_DIV_SEL(value),
|
||||
PLL_GET_DIV_SEL(value),
|
||||
PLL_GET_POST_DIV_SEL(value),
|
||||
syn_set, clk_full);
|
||||
}
|
||||
|
||||
static unsigned long fpll_find_synthesizer(unsigned long parent,
|
||||
unsigned long rate,
|
||||
unsigned long pre_div,
|
||||
unsigned long div,
|
||||
unsigned long post_div,
|
||||
bool is_full_parent,
|
||||
u32 *ssc_syn_set)
|
||||
{
|
||||
u32 test_max = U32_MAX, test_min = PLL_SYN_FACTOR_MINIMUM;
|
||||
unsigned long trate;
|
||||
|
||||
while (test_min < test_max) {
|
||||
u32 tssc = (test_max + test_min) / 2;
|
||||
|
||||
trate = fpll_calc_rate(parent, pre_div, div, post_div,
|
||||
tssc, is_full_parent);
|
||||
|
||||
if (trate == rate) {
|
||||
test_min = tssc;
|
||||
break;
|
||||
}
|
||||
|
||||
if (trate > rate)
|
||||
test_min = tssc + 1;
|
||||
else
|
||||
test_max = tssc - 1;
|
||||
}
|
||||
|
||||
if (trate != 0)
|
||||
*ssc_syn_set = test_min;
|
||||
|
||||
return trate;
|
||||
}
|
||||
|
||||
static int fpll_find_rate(struct cv1800_clk_pll *pll,
|
||||
const struct cv1800_clk_pll_limit *limit,
|
||||
unsigned long prate,
|
||||
unsigned long *rate,
|
||||
u32 *value, u32 *ssc_syn_set)
|
||||
{
|
||||
unsigned long best_rate = 0;
|
||||
unsigned long pre_div_sel = 0, div_sel = 0, post_div_sel = 0;
|
||||
unsigned long pre, div, post;
|
||||
unsigned long trate = *rate;
|
||||
u32 detected = *value;
|
||||
unsigned long tmp;
|
||||
bool clk_full = cv1800_clk_checkbit(&pll->common,
|
||||
&pll->pll_syn->clk_half);
|
||||
|
||||
for_each_pll_limit_range(pre, &limit->pre_div) {
|
||||
for_each_pll_limit_range(post, &limit->post_div) {
|
||||
for_each_pll_limit_range(div, &limit->div) {
|
||||
tmp = fpll_find_synthesizer(prate, trate,
|
||||
pre, div, post,
|
||||
clk_full,
|
||||
ssc_syn_set);
|
||||
|
||||
if ((trate - tmp) < (trate - best_rate)) {
|
||||
best_rate = tmp;
|
||||
pre_div_sel = pre;
|
||||
div_sel = div;
|
||||
post_div_sel = post;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best_rate) {
|
||||
detected = PLL_SET_PRE_DIV_SEL(detected, pre_div_sel);
|
||||
detected = PLL_SET_POST_DIV_SEL(detected, post_div_sel);
|
||||
detected = PLL_SET_DIV_SEL(detected, div_sel);
|
||||
*value = detected;
|
||||
*rate = best_rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int fpll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
|
||||
{
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
u32 val, ssc_syn_set;
|
||||
|
||||
if (!fpll_is_factional_mode(pll))
|
||||
return ipll_determine_rate(hw, req);
|
||||
|
||||
fpll_find_rate(pll, &pll->pll_limit[2], req->best_parent_rate,
|
||||
&req->rate, &val, &ssc_syn_set);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool fpll_check_mode_ctrl_restrict(unsigned long div_sel,
|
||||
unsigned long ictrl,
|
||||
unsigned long mode)
|
||||
{
|
||||
unsigned long left_rest = 10 * div_sel;
|
||||
unsigned long right_rest = 24 * div_sel;
|
||||
unsigned long test = 184 * (1 + mode) * (1 + ictrl) / 2;
|
||||
|
||||
return test > left_rest && test <= right_rest;
|
||||
}
|
||||
|
||||
static int fpll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u32 regval;
|
||||
u32 detected = 0, detected_ssc = 0;
|
||||
unsigned long flags;
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
|
||||
if (!fpll_is_factional_mode(pll))
|
||||
return ipll_set_rate(hw, rate, parent_rate);
|
||||
|
||||
fpll_find_rate(pll, &pll->pll_limit[2], parent_rate,
|
||||
&rate, &detected, &detected_ssc);
|
||||
pll_get_mode_ctrl(PLL_GET_DIV_SEL(detected),
|
||||
fpll_check_mode_ctrl_restrict,
|
||||
pll->pll_limit, &detected);
|
||||
|
||||
spin_lock_irqsave(pll->common.lock, flags);
|
||||
|
||||
writel(detected_ssc, pll->common.base + pll->pll_syn->set);
|
||||
|
||||
regval = readl(pll->common.base + pll->pll_reg);
|
||||
regval = PLL_COPY_REG(regval, detected);
|
||||
|
||||
writel(regval, pll->common.base + pll->pll_reg);
|
||||
|
||||
spin_unlock_irqrestore(pll->common.lock, flags);
|
||||
|
||||
cv1800_clk_wait_for_lock(&pll->common, pll->pll_status.reg,
|
||||
BIT(pll->pll_status.shift));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 fpll_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
|
||||
if (fpll_is_factional_mode(pll))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fpll_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
|
||||
if (index)
|
||||
cv1800_clk_setbit(&pll->common, &pll->pll_syn->en);
|
||||
else
|
||||
cv1800_clk_clearbit(&pll->common, &pll->pll_syn->en);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct clk_ops cv1800_clk_fpll_ops = {
|
||||
.disable = pll_disable,
|
||||
.enable = pll_enable,
|
||||
.is_enabled = pll_is_enable,
|
||||
|
||||
.recalc_rate = fpll_recalc_rate,
|
||||
.determine_rate = fpll_determine_rate,
|
||||
.set_rate = fpll_set_rate,
|
||||
|
||||
.set_parent = fpll_set_parent,
|
||||
.get_parent = fpll_get_parent,
|
||||
};
|
||||
118
drivers/clk/sophgo/clk-cv18xx-pll.h
Normal file
118
drivers/clk/sophgo/clk-cv18xx-pll.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
|
||||
*/
|
||||
|
||||
#ifndef _CLK_SOPHGO_CV1800_PLL_H_
|
||||
#define _CLK_SOPHGO_CV1800_PLL_H_
|
||||
|
||||
#include "clk-cv18xx-common.h"
|
||||
|
||||
struct cv1800_clk_pll_limit {
|
||||
struct {
|
||||
u8 min;
|
||||
u8 max;
|
||||
} pre_div, div, post_div, ictrl, mode;
|
||||
};
|
||||
|
||||
#define _CV1800_PLL_LIMIT(_min, _max) \
|
||||
{ \
|
||||
.min = _min, \
|
||||
.max = _max, \
|
||||
} \
|
||||
|
||||
#define for_each_pll_limit_range(_var, _restrict) \
|
||||
for (_var = (_restrict)->min; _var <= (_restrict)->max; _var++)
|
||||
|
||||
struct cv1800_clk_pll_synthesizer {
|
||||
struct cv1800_clk_regbit en;
|
||||
struct cv1800_clk_regbit clk_half;
|
||||
u32 ctrl;
|
||||
u32 set;
|
||||
};
|
||||
|
||||
#define _PLL_PRE_DIV_SEL_FIELD GENMASK(6, 0)
|
||||
#define _PLL_POST_DIV_SEL_FIELD GENMASK(14, 8)
|
||||
#define _PLL_SEL_MODE_FIELD GENMASK(16, 15)
|
||||
#define _PLL_DIV_SEL_FIELD GENMASK(23, 17)
|
||||
#define _PLL_ICTRL_FIELD GENMASK(26, 24)
|
||||
|
||||
#define _PLL_ALL_FIELD_MASK \
|
||||
(_PLL_PRE_DIV_SEL_FIELD | \
|
||||
_PLL_POST_DIV_SEL_FIELD | \
|
||||
_PLL_SEL_MODE_FIELD | \
|
||||
_PLL_DIV_SEL_FIELD | \
|
||||
_PLL_ICTRL_FIELD)
|
||||
|
||||
#define PLL_COPY_REG(_dest, _src) \
|
||||
(((_dest) & (~_PLL_ALL_FIELD_MASK)) | ((_src) & _PLL_ALL_FIELD_MASK))
|
||||
|
||||
#define PLL_GET_PRE_DIV_SEL(_reg) \
|
||||
FIELD_GET(_PLL_PRE_DIV_SEL_FIELD, (_reg))
|
||||
#define PLL_GET_POST_DIV_SEL(_reg) \
|
||||
FIELD_GET(_PLL_POST_DIV_SEL_FIELD, (_reg))
|
||||
#define PLL_GET_SEL_MODE(_reg) \
|
||||
FIELD_GET(_PLL_SEL_MODE_FIELD, (_reg))
|
||||
#define PLL_GET_DIV_SEL(_reg) \
|
||||
FIELD_GET(_PLL_DIV_SEL_FIELD, (_reg))
|
||||
#define PLL_GET_ICTRL(_reg) \
|
||||
FIELD_GET(_PLL_ICTRL_FIELD, (_reg))
|
||||
|
||||
#define PLL_SET_PRE_DIV_SEL(_reg, _val) \
|
||||
_CV1800_SET_FIELD((_reg), (_val), _PLL_PRE_DIV_SEL_FIELD)
|
||||
#define PLL_SET_POST_DIV_SEL(_reg, _val) \
|
||||
_CV1800_SET_FIELD((_reg), (_val), _PLL_POST_DIV_SEL_FIELD)
|
||||
#define PLL_SET_SEL_MODE(_reg, _val) \
|
||||
_CV1800_SET_FIELD((_reg), (_val), _PLL_SEL_MODE_FIELD)
|
||||
#define PLL_SET_DIV_SEL(_reg, _val) \
|
||||
_CV1800_SET_FIELD((_reg), (_val), _PLL_DIV_SEL_FIELD)
|
||||
#define PLL_SET_ICTRL(_reg, _val) \
|
||||
_CV1800_SET_FIELD((_reg), (_val), _PLL_ICTRL_FIELD)
|
||||
|
||||
struct cv1800_clk_pll {
|
||||
struct cv1800_clk_common common;
|
||||
u32 pll_reg;
|
||||
struct cv1800_clk_regbit pll_pwd;
|
||||
struct cv1800_clk_regbit pll_status;
|
||||
const struct cv1800_clk_pll_limit *pll_limit;
|
||||
struct cv1800_clk_pll_synthesizer *pll_syn;
|
||||
};
|
||||
|
||||
#define CV1800_INTEGRAL_PLL(_name, _parent, _pll_reg, \
|
||||
_pll_pwd_reg, _pll_pwd_shift, \
|
||||
_pll_status_reg, _pll_status_shift, \
|
||||
_pll_limit, _flags) \
|
||||
struct cv1800_clk_pll _name = { \
|
||||
.common = CV1800_CLK_COMMON(#_name, _parent, \
|
||||
&cv1800_clk_ipll_ops,\
|
||||
_flags), \
|
||||
.pll_reg = _pll_reg, \
|
||||
.pll_pwd = CV1800_CLK_BIT(_pll_pwd_reg, \
|
||||
_pll_pwd_shift), \
|
||||
.pll_status = CV1800_CLK_BIT(_pll_status_reg, \
|
||||
_pll_status_shift), \
|
||||
.pll_limit = _pll_limit, \
|
||||
.pll_syn = NULL, \
|
||||
}
|
||||
|
||||
#define CV1800_FACTIONAL_PLL(_name, _parent, _pll_reg, \
|
||||
_pll_pwd_reg, _pll_pwd_shift, \
|
||||
_pll_status_reg, _pll_status_shift, \
|
||||
_pll_limit, _pll_syn, _flags) \
|
||||
struct cv1800_clk_pll _name = { \
|
||||
.common = CV1800_CLK_COMMON(#_name, _parent, \
|
||||
&cv1800_clk_fpll_ops,\
|
||||
_flags), \
|
||||
.pll_reg = _pll_reg, \
|
||||
.pll_pwd = CV1800_CLK_BIT(_pll_pwd_reg, \
|
||||
_pll_pwd_shift), \
|
||||
.pll_status = CV1800_CLK_BIT(_pll_status_reg, \
|
||||
_pll_status_shift), \
|
||||
.pll_limit = _pll_limit, \
|
||||
.pll_syn = _pll_syn, \
|
||||
}
|
||||
|
||||
extern const struct clk_ops cv1800_clk_ipll_ops;
|
||||
extern const struct clk_ops cv1800_clk_fpll_ops;
|
||||
|
||||
#endif // _CLK_SOPHGO_CV1800_PLL_H_
|
||||
Reference in New Issue
Block a user