lib: Add SG2044 CPPC support

Signed-off-by: haijiao.liu <haijiao.liu@sophgo.com>
This commit is contained in:
haijiao.liu
2025-03-26 22:01:23 +08:00
committed by Xiaoguang Xing
parent df02b2f353
commit 78f021d248
15 changed files with 961 additions and 0 deletions

32
include/sbi/sbi_clk.h Normal file
View File

@@ -0,0 +1,32 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 Sophgo Inc.
*
* Authors:
* Haijiao Liu <haijiao.liu@sophgo.com>
*/
#ifndef __SBI_CLK_H__
#define __SBI_CLK_H__
#include <sbi/sbi_types.h>
/* clock device */
struct sbi_clk_device {
char name[32];
int (*clk_set_rate)(const char *name, uint64_t rate);
uint64_t (*clk_get_rate)(const char *name);
int (*clk_enable)(const char *name);
int (*clk_disable)(const char *name);
};
int sbi_clk_set_rate(const char *name, uint64_t rate);
uint64_t sbi_clk_get_rate(const char *name);
int sbi_clk_enable(const char *name);
int sbi_clk_disable(const char *name);
const struct sbi_clk_device *sbi_clk_get_device(const void *fdt);
void sbi_clk_set_device(const struct sbi_clk_device *dev);
#endif

View File

@@ -0,0 +1,26 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 Sophgo Inc.
*
* Authors:
* Haijiao Liu <haijiao.liu@sophgo.com>
*/
#ifndef __FDT_CLK_H__
#define __FDT_CLK_H__
#include <sbi/sbi_types.h>
#include <sbi_utils/fdt/fdt_driver.h>
#ifdef CONFIG_FDT_CLK
void fdt_clk_init(const void *fdt);
#else
static inline void fdt_clk_init(const void *fdt) { }
#endif
#endif

View File

@@ -0,0 +1,104 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 Sophgo Inc.
*
* Authors:
* Haijiao Liu <haijiao.liu@sophgo.com>
*/
#ifndef _SG2044_CLK_H_
#define _SG2044_CLK_H_
#include <sbi/sbi_string.h>
#include <sbi/sbi_bitops.h>
#define KHZ 1000UL
#define MHZ (KHZ * KHZ)
#define SG2044_CLK_USE_INIT_VAL BIT(0) /* use default value */
#define SG2044_CLK_USE_REG_VAL BIT(1) /* use reg divider value */
#define SG2044_CLK_RO BIT(2) /* use reg divider value */
#define CLK_PLL BIT(0)
#define CLK_MUX BIT(1)
#define PLL_CTRL_OFFSET 0xC4
#define PLL_STAT_LOCK_OFFSET 0x10
#define PLL_SELECT_OFFSET 0x2020
#define CLK_MODE 0x4
#define CLK_MODE_MASK 0x3
#define FBDIV_SHIFT 0
#define FBDIV_WIDTH 12
#define REFDIV_SHIFT 12
#define REFDIV_WIDTH 6
#define POSTDIV1_SHIFT 18
#define POSTDIV1_WIDTH 3
#define POSTDIV2_SHIFT 21
#define POSTDIV2_WIDTH 3
#define REFDIV_MIN 1
#define REFDIV_MAX 63
#define FBDIV_MIN 8
#define FBDIV_MAX 1066
#define PLL_FREQ_MIN (1600 * MHZ)
#define PLL_FREQ_MAX (3200 * MHZ)
#define div_mask(width) ((1 << (width)) - 1)
#define TOP_PLL_CTRL(fbdiv, p1, p2, refdiv) \
(BIT(30) | (0x2 << 27) | BIT(24) | (((p2 - 1) & 0x7) << 21) | \
(((p1 - 1) & 0x7) << 18) | ((refdiv & 0x3f) << 12) | (fbdiv & 0xfff))
#define MPLL0_CLK 0
#define MPLL1_CLK 1
#define MPLL2_CLK 2
#define MPLL3_CLK 3
#define MPLL4_CLK 4
#define MPLL5_CLK 5
#define FPLL0_CLK 6
#define FPLL1_CLK 7
#define DPLL0_CLK 8
#define DPLL1_CLK 9
#define DPLL2_CLK 10
#define DPLL3_CLK 11
#define DPLL4_CLK 12
#define DPLL5_CLK 13
#define DPLL6_CLK 14
#define DPLL7_CLK 15
static inline char is_power_of_2(unsigned long n)
{
return (n != 0 && ((n & (n - 1)) == 0));
}
#define do_div(n, base) ({ \
uint32_t __base = (base); \
uint32_t __rem; \
__rem = (uint32_t)(n) % __base; \
(n) = (uint32_t)(n) / __base; \
__rem; \
})
struct sg2044_pll_ctrl {
unsigned int mode;
unsigned long freq;
unsigned int fbdiv;
unsigned int postdiv1;
unsigned int postdiv2;
unsigned int refdiv;
};
struct sg2044_pll_clock {
unsigned int id;
char *name;
unsigned int status_offset;
unsigned int enable_offset;
uint64_t default_rate;
struct sg2044_pll_ctrl pctrl_table[4];
};
#endif

View File

@@ -99,3 +99,4 @@ libsbi-objs-y += sbi_trap_v_ldst.o
libsbi-objs-y += sbi_unpriv.o
libsbi-objs-y += sbi_expected_trap.o
libsbi-objs-y += sbi_cppc.o
libsbi-objs-y += sbi_clk.o

64
lib/sbi/sbi_clk.c Normal file
View File

@@ -0,0 +1,64 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 Sophgo Inc.
*
* Authors:
* Haijiao Liu <haijiao.liu@sophgo.com>
*/
#include <sbi/sbi_error.h>
#include <sbi/sbi_clk.h>
#include <sbi_utils/clk/fdt_clk.h>
static const struct sbi_clk_device *clk_dev = NULL;
const struct sbi_clk_device *sbi_clk_get_device(const void *fdt)
{
if (!clk_dev)
fdt_clk_init(fdt);
return clk_dev ? clk_dev : NULL;
}
void sbi_clk_set_device(const struct sbi_clk_device *dev)
{
if (!dev || clk_dev)
return;
clk_dev = dev;
}
int sbi_clk_set_rate(const char *name, uint64_t rate)
{
if (!clk_dev || !clk_dev->clk_set_rate)
return SBI_EFAIL;
clk_dev->clk_set_rate(name, rate);
return 0;
}
uint64_t sbi_clk_get_rate(const char *name)
{
if (!clk_dev || !clk_dev->clk_get_rate)
return SBI_EFAIL;
return clk_dev->clk_get_rate(name);
}
int sbi_clk_enable(const char *name)
{
if (!clk_dev || !clk_dev->clk_enable)
return SBI_EFAIL;
return clk_dev->clk_enable(name);
}
int sbi_clk_disable(const char *name)
{
if (!clk_dev || !clk_dev->clk_disable)
return SBI_EFAIL;
return clk_dev->clk_disable(name);
}

View File

@@ -34,4 +34,6 @@ source "$(OPENSBI_SRC_DIR)/lib/utils/timer/Kconfig"
source "$(OPENSBI_SRC_DIR)/lib/utils/mpxy/Kconfig"
source "$(OPENSBI_SRC_DIR)/lib/utils/clk/Kconfig"
endmenu

18
lib/utils/clk/Kconfig Normal file
View File

@@ -0,0 +1,18 @@
# SPDX-License-Identifier: BSD-2-Clause
menu "Clock Support"
config FDT_CLK
bool "FDT based CLK drivers"
depends on FDT
default n
if FDT_CLK
config FDT_CLK_SG2044
bool "SG2044 clock drivers"
default n
endif
endmenu

18
lib/utils/clk/fdt_clk.c Normal file
View File

@@ -0,0 +1,18 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 Sophgo Inc.
*
* Authors:
* Haijiao Liu <haijiao.liu@sophgo.com>
*/
#include <sbi_utils/clk/fdt_clk.h>
/* List of FDT clock drivers generated at compile time */
extern const struct fdt_driver *const fdt_clk_drivers[];
void fdt_clk_init(const void *fdt)
{
fdt_driver_init_all(fdt, fdt_clk_drivers);
}

View File

@@ -0,0 +1,3 @@
HEADER: sbi_utils/clk/fdt_clk.h
TYPE: const struct fdt_driver
NAME: fdt_clk_drivers

14
lib/utils/clk/objects.mk Normal file
View File

@@ -0,0 +1,14 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (C) 2025 Sophgo Inc
#
# Authors:
# Haijiao Liu <haijiao.liu@sophgo.com>
#
libsbiutils-objs-$(CONFIG_FDT_CLK) += clk/fdt_clk.o
libsbiutils-objs-$(CONFIG_FDT_CLK) += clk/fdt_clk_drivers.carray.o
carray-fdt_clk_drivers-$(CONFIG_FDT_CLK_SG2044) += fdt_clk_sg2044
libsbiutils-objs-$(CONFIG_FDT_CLK_SG2044) += clk/sg2044_clk.o

553
lib/utils/clk/sg2044_clk.c Normal file
View File

@@ -0,0 +1,553 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 Sophgo Inc.
*
* Authors:
* Haijiao Liu <haijiao.liu@sophgo.com>
*/
#include <libfdt.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_string.h>
#include <sbi/sbi_console.h>
#include <sbi/sbi_timer.h>
#include <sbi/sbi_clk.h>
#include <sbi/riscv_io.h>
#include <sbi_utils/fdt/fdt_driver.h>
#include <sbi_utils/clk/sg2044_clk.h>
#define POSTDIV_RESULT_INDEX 2
static u64 top_base_addr;
static int sg2044_pll_mux[][2] = {
{MPLL0_CLK, 0}, {MPLL1_CLK, 1}, {MPLL2_CLK, 2}, {MPLL3_CLK, 3},
{MPLL4_CLK, 4}, {MPLL5_CLK, 5}, {FPLL1_CLK, 6}, {DPLL0_CLK, 7},
{DPLL1_CLK, 8}, {DPLL2_CLK, 9}, {DPLL3_CLK, 10}, {DPLL4_CLK, 11},
{DPLL5_CLK, 12}, {DPLL6_CLK, 13}, {DPLL7_CLK, 14}
};
static int postdiv1_2[][3] = {
{2, 4, 8}, {3, 3, 9}, {2, 5, 10}, {2, 6, 12},
{2, 7, 14}, {3, 5, 15}, {4, 4, 16}, {3, 6, 18},
{4, 5, 20}, {3, 7, 21}, {4, 6, 24}, {5, 5, 25},
{4, 7, 28}, {5, 6, 30}, {5, 7, 35}, {6, 6, 36},
{6, 7, 42}, {7, 7, 49}
};
static inline unsigned long abs_diff(unsigned long a, unsigned long b)
{
return (a > b) ? (a - b) : (b - a);
}
static struct sg2044_pll_clock sg2044_root_pll_clks[] = {
{
.id = MPLL0_CLK,
.name = "mpll0-clock",
.default_rate = 2000 * MHZ,
.status_offset = 0x98,
.enable_offset = 0x9c,
}, {
.id = MPLL1_CLK,
.name = "mpll1-clock",
.default_rate = 2000 * MHZ,
.status_offset = 0x98,
.enable_offset = 0x9c,
}, {
.id = MPLL2_CLK,
.name = "mpll2-clock",
.default_rate = 1000 * MHZ,
.status_offset = 0x98,
.enable_offset = 0x9c,
}, {
.id = MPLL3_CLK,
.name = "mpll3-clock",
.default_rate = 2000 * MHZ,
.status_offset = 0x98,
.enable_offset = 0x9c,
}, {
.id = MPLL4_CLK,
.name = "mpll4-clock",
.default_rate = 1050 * MHZ,
.status_offset = 0x98,
.enable_offset = 0x9c,
}, {
.id = MPLL5_CLK,
.name = "mpll5-clock",
.default_rate = 900 * MHZ,
.status_offset = 0x98,
.enable_offset = 0x9c,
}, {
.id = FPLL0_CLK,
.name = "fpll0-clock",
}, {
.id = FPLL1_CLK,
.name = "fpll1-clock",
}, {
.id = DPLL0_CLK,
.name = "dpll0-clock",
.status_offset = 0x98,
.enable_offset = 0x9c,
}, {
.id = DPLL1_CLK,
.name = "dpll1-clock",
.status_offset = 0x98,
.enable_offset = 0x9c,
}, {
.id = DPLL2_CLK,
.name = "dpll2-clock",
.status_offset = 0x98,
.enable_offset = 0x9c,
}, {
.id = DPLL3_CLK,
.name = "dpll3-clock",
.status_offset = 0x98,
.enable_offset = 0x9c,
}, {
.id = DPLL4_CLK,
.name = "dpll4-clock",
.status_offset = 0x98,
.enable_offset = 0x9c,
}, {
.id = DPLL5_CLK,
.name = "dpll5-clock",
.status_offset = 0x98,
.enable_offset = 0x9c,
}, {
.id = DPLL6_CLK,
.name = "dpll6-clock",
.status_offset = 0x98,
.enable_offset = 0x9c,
}, {
.id = DPLL7_CLK,
.name = "dpll7-clock",
.status_offset = 0x98,
.enable_offset = 0x9c,
}
};
static inline void top_misc_read(uintptr_t offset, uint32_t *value)
{
*value = readl((const volatile void *)top_base_addr + offset);
}
static inline void top_misc_write(uintptr_t offset, uint32_t value)
{
writel(value, (volatile void *)(top_base_addr + offset));
}
static void sg2044_pll_write_h(int id, int value)
{
top_misc_write(PLL_CTRL_OFFSET + (id << 3), value);
}
static void sg2044_pll_read_h(int id, uint32_t *pvalue)
{
top_misc_read(PLL_CTRL_OFFSET + (id << 3), pvalue);
}
static void sg2044_pll_write_l(int id, int value)
{
top_misc_write(PLL_CTRL_OFFSET + (id << 3) - 4, value);
}
static void sg2044_pll_read_l(int id, uint32_t *pvalue)
{
top_misc_read(PLL_CTRL_OFFSET + (id << 3) - 4, pvalue);
}
static int __pll_get_postdiv_1_2(uint64_t rate, uint64_t prate,
uint32_t fbdiv, uint32_t refdiv,
uint32_t *postdiv1, uint32_t *postdiv2)
{
int index = 0;
int ret = 0;
uint64_t tmp0;
/*
* calculate (parent_rate/refdiv)
* and result save to tmp0
*/
tmp0 = prate;
do_div(tmp0, refdiv);
/*
* calcuate ((parent_rate/REFDIV) x FBDIV)
* and result save to prate
*/
tmp0 *= fbdiv;
/*
* calcuate (((parent_rate/REFDIV) x FBDIV)/input_rate)
* and result save to tmp0
* here tmp0 is (POSTDIV1*POSTDIV2)
*/
do_div(tmp0, rate);
/* calculate div1 and div2 value */
if (tmp0 <= 7) {
/* (div1 * div2) <= 7, no need to use array search */
*postdiv1 = tmp0;
*postdiv2 = 1;
} else {
/* (div1 * div2) > 7, use array search */
for (index = 0; index < array_size(postdiv1_2); index++) {
if (tmp0 > postdiv1_2[index][POSTDIV_RESULT_INDEX]) {
continue;
} else {
/* found it */
break;
}
}
if (index < array_size(postdiv1_2)) {
*postdiv1 = postdiv1_2[index][1];
*postdiv2 = postdiv1_2[index][0];
} else {
sbi_printf("%s out of postdiv array range!\n", __func__);
ret = -1;
}
}
return ret;
}
/*
* The function uses the following mapping for frequency range selection:
* - 2'd2 (bit[17:16] = 0b10): for frequencies from 1.6 GHz to 2.4 GHz.
* - 2'd3 (bit[17:16] = 0b11): for frequencies from 2.4 GHz to 3.2 GHz.
*/
static void __set_pll_vcosel(struct sg2044_pll_clock *sg2044_pll, uint64_t foutvco)
{
int vcosel;
uint32_t value;
if (foutvco < (2400 * MHZ))
vcosel = 0x2;
else
vcosel = 0x3;
sg2044_pll_read_l(sg2044_pll->id, &value);
value &= ~(0x3 << 16);
value |= (vcosel << 16);
sg2044_pll_write_l(sg2044_pll->id, value);
}
static inline int sg2044_pll_id2shift(uint32_t id)
{
for (int i = 0; i < 15; i++) {
if (sg2044_pll_mux[i][0] == id)
return sg2044_pll_mux[i][1];
}
return -1;
}
static inline int sg2044_pll_switch_mux(struct sg2044_pll_clock *pll, char en)
{
uint32_t value;
uint32_t id = pll->id;
int shift;
shift = sg2044_pll_id2shift(id);
if (shift == -1) {
sbi_printf("%s Unable to find a suitable shift!\n", __func__);
return -1;
}
top_misc_read(PLL_SELECT_OFFSET, &value);
if (en)
top_misc_write(PLL_SELECT_OFFSET, value & (~(1 << shift)));
else
top_misc_write(PLL_SELECT_OFFSET, value | (1 << shift));
return 0;
}
static inline int sg2044_pll_enable(struct sg2044_pll_clock *pll, char en)
{
uint32_t value;
uint64_t wait = 0;
uint32_t id = pll->id;
if (en) {
/* wait pll lock */
top_misc_read(pll->status_offset, &value);
while (!((value >> (PLL_STAT_LOCK_OFFSET + id)) & 0x1)) {
top_misc_read(pll->status_offset, &value);
wait++;
sbi_timer_udelay(10);
if (wait > 10000)
sbi_printf("%s not locked\n", pll->name);
}
/* wait pll updating */
wait = 0;
top_misc_read(pll->status_offset, &value);
while (((value >> id) & 0x1)) {
top_misc_read(pll->status_offset, &value);
wait++;
sbi_timer_udelay(10);
if (wait > 10000)
sbi_printf("%s still updating\n", pll->name);
}
/* enable pll */
top_misc_read(pll->enable_offset, &value);
top_misc_write(pll->enable_offset, value | (1 << id));
} else {
/* disable pll */
top_misc_read(pll->enable_offset, &value);
top_misc_write(pll->enable_offset, value & (~(1 << id)));
}
return 0;
}
/**
* The PLL output frequency calculation is based on the formula:
* FOUTPOSTDIV = FREF * FBDIV / REFDIV / (POSTDIV1 + 1) * (POSTDIV2 + 1)
* where:
* - FREF: Reference Clock Input (12MHz to 1600MHz).
* - FOUTPOSTDIV: Output Clock (25MHz to 3200MHz).
* - REFDIV: Reference divide value (1 to 63).
* - FBDIV: Feedback divide value (8 to 1066).
* - POSTDIV1: Post Divide 1 setting (0 to 7).
* - POSTDIV2: Post Divide 2 setting (0 to 7).
*
* Additionally, other check points inside PLL are listed here:
* 1.FOUTVCO = FREF * FBDIV / REFDIV 1600MHz to 3200MHz
* - VCOSEL = 2'd2, FOUTVCO 1.6G~2.4G;
* - VCOSEL = 2'd3, FOUTVCO 2.4G~3.2G
* 2.POSTDIV1 >= POSTDIV2
*/
static int __get_pll_ctl_setting(struct sg2044_pll_ctrl *best,
uint64_t req_rate, uint64_t parent_rate)
{
int ret;
uint32_t fbdiv, refdiv, fref, postdiv1, postdiv2;
uint64_t tmp = 0, foutvco;
fref = parent_rate;
for (refdiv = REFDIV_MIN; refdiv < REFDIV_MAX + 1; refdiv++) {
for (fbdiv = FBDIV_MIN; fbdiv < FBDIV_MAX + 1; fbdiv++) {
foutvco = fref * fbdiv / refdiv;
/* check fpostdiv pfd */
if (foutvco < PLL_FREQ_MIN || foutvco > PLL_FREQ_MAX
|| (fref / refdiv) < 10)
continue;
ret = __pll_get_postdiv_1_2(req_rate, fref, fbdiv,
refdiv, &postdiv1, &postdiv2);
if (ret)
continue;
tmp = foutvco / (postdiv1 * postdiv2);
if (abs_diff(tmp, req_rate) < abs_diff(best->freq, req_rate)) {
best->freq = tmp;
best->refdiv = refdiv;
best->fbdiv = fbdiv;
best->postdiv1 = postdiv1;
best->postdiv2 = postdiv2;
if (tmp == req_rate)
return 0;
}
continue;
}
}
return -1;
}
/**
* The function performs the following steps:
* 1. Switch the PLL source to fpll before modifying settings.
* 2. Disable the MPLL to allow safe modifications to its configuration.
* 3. Calculate the new PLL settings based on the desired rate and the parent rate.
* 5. Set the frequency range based on the foutvco .
* 6. Write the new settings to the PLL control register.
* 7. Re-enable the PLL.
* 8. Switch back the PLL source to mpll after modifications.
*/
static int sg2044_clk_pll_set_rate(struct sg2044_pll_clock *sg2044_pll,
uint64_t rate, uint64_t parent_rate)
{
int ret = 0;
uint32_t value;
uint64_t foutvco;
struct sg2044_pll_ctrl pctrl_table;
sbi_memset(&pctrl_table, 0, sizeof(struct sg2044_pll_ctrl));
/* switch to fpll before modify mpll */
ret = sg2044_pll_switch_mux(sg2044_pll, 1);
if (ret == -1) {
sbi_printf("switch to fpll fail!\n");
goto out;
}
if (sg2044_pll_enable(sg2044_pll, 0)) {
sbi_printf("Can't disable pll(%s), status error\n", sg2044_pll->name);
ret = -1;
goto out;
}
sg2044_pll_read_h(sg2044_pll->id, &value);
__get_pll_ctl_setting(&pctrl_table, rate, parent_rate);
if (!pctrl_table.freq) {
sbi_printf("%s: Can't find a proper pll setting\n", sg2044_pll->name);
ret = -1;
goto out;
}
value = TOP_PLL_CTRL(pctrl_table.fbdiv, pctrl_table.postdiv1,
pctrl_table.postdiv2, pctrl_table.refdiv);
foutvco = parent_rate * pctrl_table.fbdiv / pctrl_table.refdiv;
__set_pll_vcosel(sg2044_pll, foutvco);
/* write the value to top register */
sg2044_pll_write_h(sg2044_pll->id, value);
sg2044_pll_enable(sg2044_pll, 1);
/* switch back to mpll after modify mpll */
ret = sg2044_pll_switch_mux(sg2044_pll, 0);
if (ret == -1) {
sbi_printf("switch back to mpll fail!\n");
goto out;
}
out:
return ret;
}
static uint64_t sg2044_clk_pll_get_rate(struct sg2044_pll_clock *sg2044_pll,
uint64_t parent_rate)
{
uint32_t value;
uint64_t fref;
int refdiv, fbdiv;
int postdiv1, postdiv2;
sg2044_pll_read_h(sg2044_pll->id, &value);
fref = parent_rate;
fbdiv = (value >> FBDIV_SHIFT) & div_mask(FBDIV_WIDTH);
refdiv = (value >> REFDIV_SHIFT) & div_mask(REFDIV_WIDTH);
postdiv1 = (value >> POSTDIV1_SHIFT) & div_mask(POSTDIV1_WIDTH);
postdiv2 = (value >> POSTDIV2_SHIFT) & div_mask(POSTDIV2_WIDTH);
return fref * fbdiv / refdiv / (postdiv1 + 1) * (postdiv2 + 1);
}
static struct sg2044_pll_clock *sg2044_get_clk_by_name(const char *name)
{
int index;
for (index = 0; index < array_size(sg2044_root_pll_clks); index++) {
if (!sbi_strcmp(name, sg2044_root_pll_clks[index].name))
return &sg2044_root_pll_clks[index];
}
return 0;
}
static int sg2044_clk_set_rate(const char *name, uint64_t rate)
{
int ret;
struct sg2044_pll_clock *clk;
clk = sg2044_get_clk_by_name(name);
if (!clk)
return SBI_EINVAL;
ret = sg2044_clk_pll_set_rate(clk, rate, 25 * MHZ);
if (ret != 0)
sbi_printf("%s set rate fail, ret = %d\n", name, ret);
return ret;
}
static uint64_t sg2044_clk_get_rate(const char *name)
{
struct sg2044_pll_clock *clk;
clk = sg2044_get_clk_by_name(name);
if (!clk)
return SBI_EINVAL;
return sg2044_clk_pll_get_rate(clk, 25 * MHZ);
}
static int sg2044_clk_enable(const char *name)
{
struct sg2044_pll_clock *clk;
clk = sg2044_get_clk_by_name(name);
if (!clk)
return SBI_EINVAL;
sg2044_pll_enable(clk, 1);
return 0;
}
static int sg2044_clk_disable(const char *name)
{
struct sg2044_pll_clock *clk;
clk = sg2044_get_clk_by_name(name);
if (!clk)
return SBI_EINVAL;
sg2044_pll_enable(clk, 0);
return 0;
}
static struct sbi_clk_device sbi_sg2044_clk = {
.name = "sg2044-clk",
.clk_set_rate = sg2044_clk_set_rate,
.clk_get_rate = sg2044_clk_get_rate,
.clk_enable = sg2044_clk_enable,
.clk_disable = sg2044_clk_disable,
};
static int sg2044_clk_init(const void *fdt, int nodeoff,
const struct fdt_match *match)
{
int top_offset, len;
const fdt32_t *phandle;
const fdt32_t *reg;
if (top_base_addr)
return 0;
phandle = fdt_getprop(fdt, nodeoff, "subctrl-syscon", &len);
if (!phandle)
return SBI_EINVAL;
top_offset = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*phandle));
if (top_offset < 0)
return top_offset;
reg = fdt_getprop(fdt, top_offset, "reg", &len);
if (!reg || len <= 0)
return SBI_EINVAL;
top_base_addr = fdt32_to_cpu(reg[0]);
top_base_addr = (top_base_addr << 32) | fdt32_to_cpu(reg[1]);
sbi_clk_set_device(&sbi_sg2044_clk);
return 0;
}
static const struct fdt_match sg2044_clk_match[] = {
{ .compatible = "sg2044, pll-clock" },
{},
};
struct fdt_driver fdt_clk_sg2044 = {
.match_table = sg2044_clk_match,
.init = sg2044_clk_init,
};

View File

@@ -14,6 +14,11 @@ config FDT_CPPC_RPMI
depends on FDT_MAILBOX && RPMI_MAILBOX
default n
config FDT_CPPC_SG2044
bool "FDT SG2044 CPPC driver"
select FDT_CLK_SG2044
default n
endif
endmenu

View File

@@ -0,0 +1,115 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 Sophgo Inc.
*
* Authors:
* Haijiao Liu <haijiao.liu@sophgo.com>
*/
#include <libfdt.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_string.h>
#include <sbi/sbi_console.h>
#include <sbi/sbi_ecall_interface.h>
#include <sbi/sbi_clk.h>
#include <sbi/sbi_cppc.h>
#include <sbi_utils/cppc/fdt_cppc.h>
#define CPPC_REGISTER_WIDTH 64
#define CPPC_REGISTER_NOT_IMPLEMENTED 0
static char clock_names[64];
static uint64_t clk_granularity;
static int sg2044_cppc_read(unsigned long reg, u64 *val)
{
int rc = SBI_SUCCESS;
switch (reg) {
case SBI_CPPC_DESIRED_PERF:
*val = sbi_clk_get_rate(clock_names) / clk_granularity;
break;
default:
rc = SBI_ERR_NOT_SUPPORTED;
}
return rc;
}
static int sg2044_cppc_write(unsigned long reg, u64 val)
{
int rc = SBI_SUCCESS;
switch (reg) {
case SBI_CPPC_DESIRED_PERF:
rc = sbi_clk_set_rate(clock_names, val * clk_granularity);
break;
default:
rc = SBI_ERR_NOT_SUPPORTED;
}
return rc;
}
static int sg2044_cppc_probe(unsigned long reg)
{
int rc;
switch (reg) {
case SBI_CPPC_DESIRED_PERF:
rc = CPPC_REGISTER_WIDTH;
break;
default:
rc = CPPC_REGISTER_NOT_IMPLEMENTED;
}
return rc;
}
static struct sbi_cppc_device sbi_sg2044_cppc = {
.name = "sg2044-cppc",
.cppc_read = sg2044_cppc_read,
.cppc_write = sg2044_cppc_write,
.cppc_probe = sg2044_cppc_probe,
};
static int sg2044_cppc_cold_init(const void *fdt, int nodeoff,
const struct fdt_match *match)
{
const struct sbi_clk_device *clk;
const fdt32_t *val;
const char *name;
int len;
clk = sbi_clk_get_device(fdt);
if (!clk)
return SBI_ENODEV;
val = fdt_getprop(fdt, nodeoff, "step", &len);
if (!val)
return SBI_EINVAL;
clk_granularity = fdt32_to_cpu(val[0]);
clk_granularity = (clk_granularity << 32) | fdt32_to_cpu(val[1]);
name = fdt_getprop(fdt, nodeoff, "clock-names", &len);
if (!name)
return SBI_EINVAL;
sbi_strncpy(clock_names, name, MIN(sizeof(clock_names), len));
sbi_cppc_set_device(&sbi_sg2044_cppc);
return 0;
}
static const struct fdt_match sg2044_cppc_match[] = {
{ .compatible = "sophgo,sg2044-cppc" },
{},
};
struct fdt_driver fdt_cppc_sg2044 = {
.match_table = sg2044_cppc_match,
.init = sg2044_cppc_cold_init,
};

View File

@@ -12,3 +12,6 @@ libsbiutils-objs-$(CONFIG_FDT_CPPC) += cppc/fdt_cppc_drivers.carray.o
carray-fdt_cppc_drivers-$(CONFIG_FDT_CPPC_RPMI) += fdt_cppc_rpmi
libsbiutils-objs-$(CONFIG_FDT_CPPC_RPMI) += cppc/fdt_cppc_rpmi.o
carray-fdt_cppc_drivers-$(CONFIG_FDT_CPPC_SG2044) += fdt_cppc_sg2044
libsbiutils-objs-$(CONFIG_FDT_CPPC_SG2044) += cppc/fdt_cppc_sg2044.o

View File

@@ -8,6 +8,9 @@ CONFIG_PLATFORM_STARFIVE_JH7110=y
CONFIG_PLATFORM_THEAD=y
CONFIG_FDT_CPPC=y
CONFIG_FDT_CPPC_RPMI=y
CONFIG_FDT_CPPC_SG2044=y
CONFIG_FDT_CLK=y
CONFIG_FDT_CLK_SG2044=y
CONFIG_FDT_GPIO=y
CONFIG_FDT_GPIO_DESIGNWARE=y
CONFIG_FDT_GPIO_SIFIVE=y