8 Commits

Author SHA1 Message Date
SalimTerryLi 8be49d94b5 board: spacemit-k3: Add support for boot console and its pinctrl
First usable state reached: SPL booted and console printed
2026-05-10 03:50:13 +08:00
SalimTerryLi 3e2f66ea03 board: spacemit-k3: SPL entrypoint 2026-05-10 03:50:13 +08:00
SalimTerryLi c5afbce6f0 board: spacemit-k3: BROM loadable SPL build
bootinfo.bin generation is added but not implemented
2026-05-10 03:50:13 +08:00
SalimTerryLi 6cab30ddfb board: spacemit-k3: enable FIT 2026-05-10 03:50:13 +08:00
SalimTerryLi 8cf3ca5aae board: spacemit-k3: BINMAN skeleton 2026-05-10 03:50:13 +08:00
SalimTerryLi c14843fe35 board: spacemit-k3: SPL skeleton 2026-05-10 03:50:13 +08:00
SalimTerryLi ba8fc652bb board: spacemit-k3: skeleton support 2026-05-10 03:50:13 +08:00
Chuan 4609811251 spi: fsl_qspi: add clock handling
Add support for the clocks used by the SpacemiT K3 QSPI
controller by retrieving and enabling the qspi_en and qspi
clocks during probe.

Signed-off-by: Jichuan Feng<jichuan.or@isrc.iscas.ac.cn>
2026-05-10 03:46:13 +08:00
16 changed files with 21 additions and 1976 deletions
+2 -12
View File
@@ -24,19 +24,9 @@ void board_boot_order(u32 *spl_boot_list)
spl_boot_list[2] = BOOT_DEVICE_NONE;
}
void board_init_f(ulong dummy)
int spl_board_init_f(void)
{
int ret;
ret = spl_early_init();
if (ret)
panic("spl_early_init() failed: %d\n", ret);
riscv_cpu_setup();
preloader_console_init();
ret = spl_board_init_f();
if (ret)
panic("spl_board_init_f() failed: %d\n", ret);
return 0;
}
-65
View File
@@ -17,50 +17,6 @@
serial0 = &serial0;
};
clocks {
clk0: osc {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <66667000>;
};
vctcxo_24: vctcxo_24 {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <24000000>;
clock-output-names = "vctcxo_24";
};
vctcxo_3: vctcxo_3 {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <3000000>;
clock-output-names = "vctcxo_3";
};
vctcxo_1: vctcxo_1 {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <1000000>;
clock-output-names = "vctcxo_1";
};
pll1_vco: pll1_vco {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <24576000>;
clock-output-names = "pll1_vco";
};
osc_32k: osc_32k {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <32000>;
clock-output-names = "osc_32k";
};
clk_dummy: clk_dummy {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <0>;
clock-output-names = "clk_dummy";
};
};
soc {
bootph-all;
#address-cells = <2>;
@@ -68,27 +24,6 @@
compatible = "simple-bus";
ranges;
ccu: clock-controller@d4050000 {
bootph-all;
compatible = "spacemit,k3-ccu";
reg = <0x0 0xd4050000 0x0 0x209c>,
<0x0 0xd4282800 0x0 0x400>,
<0x0 0xd4015000 0x0 0x1000>,
<0x0 0xd4090000 0x0 0x1000>,
<0x0 0xd4282c00 0x0 0x400>,
<0x0 0xd8440000 0x0 0x98>,
<0x0 0xc0000000 0x0 0x4280>,
<0x0 0xf0610000 0x0 0x20>,
<0x0 0xc0880000 0x0 0xd100>;
reg-names = "mpmu", "apmu", "apbc", "apbs", "ciu", "dciu", "ddrc", "apbc2", "rcpu";
clocks = <&vctcxo_24>, <&vctcxo_3>, <&vctcxo_1>, <&pll1_vco>,
<&osc_32k>, <&clk_dummy>;
clock-names = "vctcxo_24", "vctcxo_3", "vctcxo_1", "pll1_vco",
"osc_32k", "clk_dummy";
#clock-cells = <1>;
status = "okay";
};
pinctrl: pinctrl@d401e000 {
bootph-all;
compatible = "pinctrl-single";
-2
View File
@@ -2,5 +2,3 @@ CONFIG_RISCV=y
CONFIG_TARGET_SPACEMIT_K3=y
CONFIG_ARCH_RV64I=y
CONFIG_RISCV_SMODE=y
CONFIG_SPL_CLK=y
CONFIG_SPACEMIT_K3_CCU=y
-1
View File
@@ -284,6 +284,5 @@ source "drivers/clk/tegra/Kconfig"
source "drivers/clk/ti/Kconfig"
source "drivers/clk/thead/Kconfig"
source "drivers/clk/uniphier/Kconfig"
source "drivers/clk/spacemit/Kconfig"
endmenu
-1
View File
@@ -61,4 +61,3 @@ obj-$(CONFIG_MACH_PIC32) += clk_pic32.o
obj-$(CONFIG_SANDBOX_CLK_CCF) += clk_sandbox_ccf.o
obj-$(CONFIG_SANDBOX) += clk_sandbox.o
obj-$(CONFIG_SANDBOX) += clk_sandbox_test.o
obj-$(CONFIG_TARGET_SPACEMIT_K3) += spacemit/
-11
View File
@@ -1,11 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
# common clock support for SPACEMIT SoC family.
config SPACEMIT_K3_CCU
tristate "Clock support for Spacemit k3 SoCs"
depends on TARGET_SPACEMIT_K3
select CLK
select CLK_CCF
select SPL_CLK_CCF if SPL
help
Build the driver for Spacemit K3 Clock Driver.
-6
View File
@@ -1,6 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
#
# Spacemit Clock specific Makefile
#
#SoC support
obj-$(CONFIG_SPACEMIT_K3_CCU) += ccu-k3.o ccu_mix.o ccu_plla.o
-308
View File
@@ -1,308 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Spacemit k3 clock controller driver
*
* Copyright (c) 2023, spacemit Corporation.
*
*/
#include <linux/clk-provider.h>
#include <clk.h>
#include <clk-uclass.h>
#include <dm.h>
#include <log.h>
#include <asm/io.h>
#include "common.h"
#include "ccu_mix.h"
#include "ccu_plla.h"
#include "ccu-k3.h"
struct spacemit_ccu_clk k3_clock_controller;
struct clk vctcxo_24, vctcxo_3, vctcxo_1, pll1_vco, osc_32k, clk_dummy;
static SPACEMIT_CCU_FACTOR(pll1_2457p6_vco, "pll1_2457p6_vco", "pll1_vco",
1, 100);
static const struct ccu_plla_rate_tbl pll2_rate_tbl[] = {
PLLA_RATE(3000000000UL, 0x0b3e2000, 0x00000000, 0xa0558c8c),
};
static SPACEMIT_CCU_PLLA(pll2, "pll2", &pll2_rate_tbl, ARRAY_SIZE(pll2_rate_tbl),
BASE_TYPE_APBS, APBS_PLL2_SWCR1, APBS_PLL2_SWCR2, APBS_PLL2_SWCR3,
MPMU_POSR, POSR_PLL2_LOCK, 1,
0);
static SPACEMIT_CCU_GATE_FACTOR(pll1_d5, "pll1_d5", "pll1_2457p6_vco",
BASE_TYPE_APBS, APBS_PLL1_SWCR2,
BIT(4), BIT(4), 0x0,
5, 1, 0);
static SPACEMIT_CCU_GATE_FACTOR(pll1_d6, "pll1_d6", "pll1_2457p6_vco",
BASE_TYPE_APBS, APBS_PLL1_SWCR2,
BIT(5), BIT(5), 0x0,
6, 1, 0);
static SPACEMIT_CCU_GATE_FACTOR(pll1_d8, "pll1_d8", "pll1_2457p6_vco",
BASE_TYPE_APBS, APBS_PLL1_SWCR2,
BIT(7), BIT(7), 0x0,
8, 1, 0);
static SPACEMIT_CCU_DIV_GATE(pll1_dx, "pll1_dx", "pll1_2457p6_vco",
BASE_TYPE_APBS, APBS_PLL1_SWCR2,
23, 5, BIT(22), BIT(22), 0x0,
0);
static SPACEMIT_CCU_GATE_FACTOR(pll2_d8, "pll2_d8", "pll2",
BASE_TYPE_APBS, APBS_PLL2_SWCR2,
BIT(7), BIT(7), 0x0,
8, 1, 0);
static SPACEMIT_CCU_GATE(pll1_d8_307p2, "pll1_d8_307p2", "pll1_d8",
BASE_TYPE_MPMU, MPMU_ACGR,
BIT(13), BIT(13), 0x0,
0);
static SPACEMIT_CCU_GATE(pll1_d6_409p6, "pll1_d6_409p6", "pll1_d6",
BASE_TYPE_MPMU, MPMU_ACGR,
BIT(0), BIT(0), 0x0,
0);
static SPACEMIT_CCU_GATE(pll1_d5_491p52, "pll1_d5_491p52", "pll1_d5",
BASE_TYPE_MPMU, MPMU_ACGR,
BIT(21), BIT(21), 0x0,
0);
static SPACEMIT_CCU_GATE_FACTOR(pll1_d10_245p76, "pll1_d10_245p76", "pll1_d5",
BASE_TYPE_MPMU, MPMU_ACGR,
BIT(18), BIT(18), 0x0,
2, 1, 0);
static const char * const qspi_parent_names[] = {
"pll1_d6_409p6", "pll2_d8", "pll1_d8_307p2", "pll1_d10_245p76",
"clk_dummy", "pll1_dx", "pll1_d5_491p52", "clk_dummy"
};
static SPACEMIT_CCU_DIV_MFC_MUX_GATE(qspi_clk, "qspi_clk", qspi_parent_names,
BASE_TYPE_APMU, APMU_QSPI_CLK_RES_CTRL,
9, 3, BIT(12),
6, 3, BIT(4), BIT(4), 0x0,
0);
static SPACEMIT_CCU_GATE(qspi_bus_clk, "qspi_bus_clk", "clk_dummy",
BASE_TYPE_APMU, APMU_QSPI_CLK_RES_CTRL,
BIT(3), BIT(3), 0x0,
0);
static struct spacemit_clk_table spacemit_k3_clks = {
.clks = {
[CLK_PLL1_2457P6] = &pll1_2457p6_vco.common.clk,
[CLK_PLL2] = &pll2.common.clk,
[CLK_PLL1_D5] = &pll1_d5.common.clk,
[CLK_PLL1_D6] = &pll1_d6.common.clk,
[CLK_PLL1_D8] = &pll1_d8.common.clk,
[CLK_PLL1_DX] = &pll1_dx.common.clk,
[CLK_PLL2_D8] = &pll2_d8.common.clk,
[CLK_PLL1_307P2] = &pll1_d8_307p2.common.clk,
[CLK_PLL1_409P6] = &pll1_d6_409p6.common.clk,
[CLK_PLL1_491] = &pll1_d5_491p52.common.clk,
[CLK_PLL1_245P76] = &pll1_d10_245p76.common.clk,
[CLK_QSPI] = &qspi_clk.common.clk,
[CLK_QSPI_BUS] = &qspi_bus_clk.common.clk,
},
.num = CLK_MAX_NO,
};
static int ccu_of_xlate(struct clk *clk,
struct ofnode_phandle_args *args)
{
if (args->args_count > 1) {
pr_debug("Invalid args_count: %d\n", args->args_count);
return -EINVAL;
}
if (args->args_count) {
clk->id = args->args[0];
if (clk->id >= spacemit_k3_clks.num ||
!spacemit_k3_clks.clks[clk->id]) {
pr_debug("Unsupported clock id: %u\n", args->args[0]);
return -EINVAL;
}
} else {
clk->id = 0;
}
clk->data = 0;
return 0;
}
static ulong ccu_clk_round_rate(struct clk *clk, ulong rate)
{
struct clk *c;
int err = clk_get_by_id(clk->id, &c);
if (err)
return err;
return clk_round_rate(c, rate);
}
const struct clk_ops ccu_clk_ops = {
.get_rate = ccf_clk_get_rate,
.round_rate = ccu_clk_round_rate,
.set_parent = ccf_clk_set_parent,
.disable = ccf_clk_disable,
.set_rate = ccf_clk_set_rate,
.enable = ccf_clk_enable,
.of_xlate = ccu_of_xlate,
};
int ccu_common_init(struct clk * clk, struct spacemit_ccu_clk *clk_info, struct spacemit_clk_table *clks)
{
struct ccu_common *common = clk_to_ccu_common(clk);
int ret;
if (!common)
return -1;
switch(common->base_type){
case BASE_TYPE_MPMU:
common->base = clk_info->mpmu_base;
break;
case BASE_TYPE_APMU:
common->base = clk_info->apmu_base;
break;
case BASE_TYPE_APBC:
common->base = clk_info->apbc_base;
break;
case BASE_TYPE_APBS:
common->base = clk_info->apbs_base;
break;
case BASE_TYPE_CIU:
common->base = clk_info->ciu_base;
break;
case BASE_TYPE_DCIU:
common->base = clk_info->dciu_base;
break;
case BASE_TYPE_DDRC:
common->base = clk_info->ddrc_base;
break;
case BASE_TYPE_AUDC:
common->base = clk_info->audio_ctrl_base;
break;
case BASE_TYPE_APBC2:
common->base = clk_info->apbc2_base;
break;
case BASE_TYPE_RCPU:
common->base = clk_info->rcpu_base;
break;
default:
common->base = clk_info->apbc_base;
break;
}
common->clk_tbl = clks;
if (common->is_pll) {
struct ccu_plla *pll = clk_to_ccu_plla(clk);
pll->pll.lock_base = clk_info->mpmu_base;
}
if(common->parent_name == NULL && common->parent_names != NULL)
common->parent_name = common->parent_names[ccu_mix_get_parent(clk)];
ret = clk_register(clk, common->driver_name, common->name, common->parent_name);
return ret;
}
int spacemit_ccu_probe(struct spacemit_ccu_clk *clk_info,
struct spacemit_clk_table *clks)
{
int i;
for (i = CLK_PLL1_2457P6; i < clks->num ; i++) {
struct clk *clk = clks->clks[i];
int ret;
if (!clk)
continue;
if (i >= CLK_VCTCXO_24)
continue;
clk->id = i;
ret = ccu_common_init(clk, clk_info, clks);
if (ret)
return ret;
}
return 0;
}
static inline void ccu_clk_dm(ulong id, struct clk *clk)
{
if (!IS_ERR(clk)){
clk->id = id;
spacemit_k3_clks.clks[id] = clk;
}
}
static int spacemit_k3_ccu_probe(struct udevice *dev)
{
int ret = 0;
struct spacemit_ccu_clk *clk_info = &k3_clock_controller;
struct spacemit_clk_table *clks = &spacemit_k3_clks;
pr_debug("init clock start \n");
clk_info->mpmu_base = (void __iomem *)dev_remap_addr_index(dev, 0);
if (!clk_info->mpmu_base) {
pr_err("failed to map mpmu registers\n");
goto out;
}
clk_info->apmu_base = (void __iomem *)dev_remap_addr_index(dev, 1);
if (!clk_info->apmu_base) {
pr_err("failed to map apmu registers\n");
goto out;
}
clk_info->apbc_base = (void __iomem *)dev_remap_addr_index(dev, 2);
if (!clk_info->apbc_base) {
pr_err("failed to map apbc registers\n");
goto out;
}
clk_info->apbs_base = (void __iomem *)dev_remap_addr_index(dev, 3);
if (!clk_info->apbs_base) {
pr_err("failed to map apbs registers\n");
goto out;
}
clk_get_by_name(dev, "vctcxo_24", &vctcxo_24);
ccu_clk_dm(CLK_VCTCXO_24, dev_get_clk_ptr(vctcxo_24.dev));
clk_get_by_name(dev, "vctcxo_3", &vctcxo_3);
ccu_clk_dm(CLK_VCTCXO_3, dev_get_clk_ptr(vctcxo_3.dev));
clk_get_by_name(dev, "vctcxo_1", &vctcxo_1);
ccu_clk_dm(CLK_VCTCXO_1, dev_get_clk_ptr(vctcxo_1.dev));
clk_get_by_name(dev, "pll1_vco", &pll1_vco);
ccu_clk_dm(CLK_PLL1, dev_get_clk_ptr(pll1_vco.dev));
clk_get_by_name(dev, "osc_32k", &osc_32k);
ccu_clk_dm(OSC_32K, dev_get_clk_ptr(osc_32k.dev));
clk_get_by_name(dev, "clk_dummy", &clk_dummy);
ccu_clk_dm(CLK_DUMMY, dev_get_clk_ptr(clk_dummy.dev));
ret = spacemit_ccu_probe(clk_info, clks);
pr_debug("init clock finish ret=%d \n", ret);
if (!ret)
return 0;
out:
return -1;
}
static const struct udevice_id ccu_clk_ids[] = {
{ .compatible = "spacemit,k3-ccu" },
{ },
};
U_BOOT_DRIVER(spacemit_k3_ccu) = {
.name = "k3-ccu",
.id = UCLASS_CLK,
.of_match = ccu_clk_ids,
.ops = &ccu_clk_ops,
.probe = spacemit_k3_ccu_probe,
};
-22
View File
@@ -1,22 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2025, spacemit Corporation.
*
*/
#ifndef _CCU_SPACEMIT_K3_H_
#define _CCU_SPACEMIT_K3_H_
#define APBS_PLL1_SWCR2 0x104
#define APBS_PLL2_SWCR1 0x118
#define APBS_PLL2_SWCR2 0x11c
#define APBS_PLL2_SWCR3 0x120
#define MPMU_FCCR 0x0008
#define MPMU_POSR 0x0010
#define POSR_PLL2_LOCK BIT(25)
#define MPMU_ACGR 0x1024
#define APMU_QSPI_CLK_RES_CTRL 0x060
#endif /* _CCU_SPACEMIT_K3_H_ */
-453
View File
@@ -1,453 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Spacemit clock type mix(div/mux/gate/factor)
*
* Copyright (c) 2023, spacemit Corporation.
*
*/
#include <asm/io.h>
#include <malloc.h>
#include <clk-uclass.h>
#include <linux/bitops.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/iopoll.h>
#include <clk.h>
#include <div64.h>
#include <limits.h>
#include "ccu_mix.h"
#define TIMEOUT_LIMIT (20000) /* max timeout 10000us */
static unsigned long ccu_rate_delta(unsigned long rate1, unsigned long rate2)
{
return rate1 > rate2 ? rate1 - rate2 : rate2 - rate1;
}
static int ccu_mix_trigger_fc(struct clk *clk)
{
struct ccu_mix *mix = clk_to_ccu_mix(clk);
struct ccu_common *common = &mix->common;
unsigned long val = 0;
int ret = 0, timeout = 50;
if (common->reg_type == CLK_DIV_TYPE_1REG_FC_V2 ||
common->reg_type == CLK_DIV_TYPE_2REG_FC_V4 ||
common->reg_type == CLK_DIV_TYPE_1REG_FC_DIV_V5 ||
common->reg_type == CLK_DIV_TYPE_1REG_FC_MUX_V6) {
timeout = 50;
val = readl(common->base + common->reg_ctrl);
val |= common->fc;
writel(val, common->base + common->reg_ctrl);
do {
val = readl(common->base + common->reg_ctrl);
timeout--;
if (!(val & (common->fc)))
break;
} while (timeout);
if (timeout == 0) {
timeout = 5000;
do {
val = readl(common->base + common->reg_ctrl);
timeout--;
if (!(val & (common->fc)))
break;
} while (timeout);
if (timeout != 0) {
ret = 0;
} else {
ret = -1;
}
}
}
return ret;
}
static int ccu_mix_disable(struct clk *clk)
{
struct ccu_mix *mix = clk_to_ccu_mix(clk);
struct ccu_common *common = &mix->common;
struct ccu_gate_config *gate = mix->gate;
u32 tmp;
if (!gate)
return 0;
if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 ||
common->reg_type == CLK_DIV_TYPE_2REG_FC_V4)
tmp = readl(common->base + common->reg_sel);
else
tmp = readl(common->base + common->reg_ctrl);
tmp &= ~gate->gate_mask;
tmp |= gate->val_disable;
if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 ||
common->reg_type == CLK_DIV_TYPE_2REG_FC_V4)
writel(tmp, common->base + common->reg_sel);
else
writel(tmp, common->base + common->reg_ctrl);
if (gate->flags & SPACEMIT_CLK_GATE_NEED_DELAY) {
udelay(200);
}
return 0;
}
static ulong ccu_mix_round_rate(struct clk *clk, ulong rate)
{
return rate;
}
static int ccu_mix_set_parent(struct clk *clk, struct clk *parent)
{
struct ccu_mix *mix = clk_to_ccu_mix(clk);
struct ccu_common *common = &mix->common;
struct ccu_mux_config *mux = mix->mux;
int index = -ENOENT;
u32 reg, i;
int ret;
if (!parent)
return -EINVAL;
for (i = 0; i < common->num_parents; i++) {
if (!strcmp(parent->dev->name, common->parent_names[i])) {
index = i;
break;
}
}
if (index < 0) {
pr_info("Could not fetch index\n");
return index;
}
if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 ||
common->reg_type == CLK_DIV_TYPE_2REG_FC_V4)
reg = readl(common->base + common->reg_sel);
else
reg = readl(common->base + common->reg_ctrl);
reg &= ~GENMASK(mux->width + mux->shift - 1, mux->shift);
if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 ||
common->reg_type == CLK_DIV_TYPE_2REG_FC_V4)
writel(reg | (index << mux->shift),
common->base + common->reg_sel);
else
writel(reg | (index << mux->shift),
common->base + common->reg_ctrl);
if (common->reg_type == CLK_DIV_TYPE_1REG_FC_V2 ||
common->reg_type == CLK_DIV_TYPE_2REG_FC_V4 ||
common->reg_type == CLK_DIV_TYPE_1REG_FC_MUX_V6) {
ret = ccu_mix_trigger_fc(clk);
if (ret)
pr_info("%s of %s timeout\n", __func__, clk->dev->name);
}
return 0;
}
static int ccu_mix_enable(struct clk *clk)
{
struct ccu_mix *mix = clk_to_ccu_mix(clk);
struct ccu_common *common = &mix->common;
struct ccu_gate_config *gate = mix->gate;
u32 tmp;
u32 val = 0;
int timeout_power = 1;
if (!gate)
return 0;
if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 ||
common->reg_type == CLK_DIV_TYPE_2REG_FC_V4)
tmp = readl(common->base + common->reg_sel);
else
tmp = readl(common->base + common->reg_ctrl);
tmp &= ~gate->gate_mask;
tmp |= gate->val_enable;
if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 ||
common->reg_type == CLK_DIV_TYPE_2REG_FC_V4)
writel(tmp, common->base + common->reg_sel);
else
writel(tmp, common->base + common->reg_ctrl);
if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 ||
common->reg_type == CLK_DIV_TYPE_2REG_FC_V4)
val = readl(common->base + common->reg_sel);
else
val = readl(common->base + common->reg_ctrl);
while ((val & gate->gate_mask) != gate->val_enable &&
(timeout_power < TIMEOUT_LIMIT)) {
udelay(timeout_power);
if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 ||
common->reg_type == CLK_DIV_TYPE_2REG_FC_V4)
val = readl(common->base + common->reg_sel);
else
val = readl(common->base + common->reg_ctrl);
timeout_power *= 10;
}
if (timeout_power > 1) {
if (val == tmp)
pr_info("write clk_gate %s timeout occur, read pass after %d us delay\n",
clk_hw_get_name(&common->clk), timeout_power);
else
pr_info("write clk_gate %s timeout after %d us!\n",
clk_hw_get_name(&common->clk), timeout_power);
}
if (gate->flags & SPACEMIT_CLK_GATE_NEED_DELAY) {
udelay(200);
}
return 0;
}
static ulong ccu_mix_get_rate(struct clk *clk)
{
struct ccu_mix *mix = clk_to_ccu_mix(clk);
struct ccu_common *common = &mix->common;
struct ccu_div_config *div = mix->div;
unsigned long parent_rate = clk_get_parent_rate(clk);
unsigned long val;
u32 reg;
if (!div) {
if (mix->factor)
val = parent_rate * mix->factor->mul / mix->factor->div;
else
val = parent_rate;
return val;
}
if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 ||
common->reg_type == CLK_DIV_TYPE_2REG_FC_V4)
reg = readl(common->base + common->reg_sel);
else
reg = readl(common->base + common->reg_ctrl);
val = reg >> div->shift;
val &= (1 << div->width) - 1;
val = divider_recalc_rate(clk, parent_rate, val, div->table, div->flags,
div->width);
return val;
}
unsigned long ccu_mix_calc_best_rate(struct clk *clk, unsigned long rate,
u32 *mux_val, u32 *div_val, u32 *parent_id)
{
struct ccu_mix *mix = clk_to_ccu_mix(clk);
struct ccu_common *common = &mix->common;
struct ccu_div_config *div = mix->div ? mix->div : NULL;
struct ccu_mux_config *mux = mix->mux ? mix->mux : NULL;
struct clk *parent = NULL;
unsigned long parent_rate = 0, best_rate = 0, best_delta = ULONG_MAX;
u32 i, j, p_id, div_max;
if (mux) {
for (i = 0; i < common->num_parents; i++) {
parent = NULL;
for (p_id = 0; p_id < common->clk_tbl->num; p_id++) {
parent = common->clk_tbl->clks[p_id];
if (!parent)
continue;
if (!strcmp(parent->dev->name,
common->parent_names[i])) {
break;
}
parent = NULL;
}
if (!parent)
continue;
parent_rate = clk_get_rate(parent);
if (!parent_rate)
continue;
if (div)
div_max = 1 << div->width;
else
div_max = 1;
for (j = 1; j <= div_max; j++) {
unsigned long candidate_rate =
DIV_ROUND_UP_ULL(parent_rate, j);
unsigned long delta =
ccu_rate_delta(candidate_rate, rate);
if (delta < best_delta) {
best_rate = candidate_rate;
best_delta = delta;
*mux_val = i;
*div_val = j - 1;
*parent_id = p_id;
}
}
}
} else {
parent_rate = clk_get_parent_rate(clk);
if (!parent_rate)
return 0;
if (div)
div_max = 1 << div->width;
else
div_max = 1;
for (j = 1; j <= div_max; j++) {
unsigned long candidate_rate =
DIV_ROUND_UP_ULL(parent_rate, j);
unsigned long delta = ccu_rate_delta(candidate_rate,
rate);
if (delta < best_delta) {
best_rate = candidate_rate;
best_delta = delta;
*div_val = j - 1;
}
}
}
return best_rate;
}
static ulong ccu_mix_set_rate(struct clk *clk, unsigned long rate)
{
struct ccu_mix *mix = clk_to_ccu_mix(clk);
struct ccu_common *common = &mix->common;
struct ccu_div_config *div_config = mix->div ? mix->div : NULL;
struct ccu_mux_config *mux_config = mix->mux ? mix->mux : NULL;
unsigned long best_rate = 0;
u32 cur_mux, cur_div, mux_val = 0, div_val = 0, parent_id = 0;
struct clk *parent;
unsigned long val;
u32 reg;
int ret;
if (!div_config && !mux_config) {
return 0;
}
best_rate = ccu_mix_calc_best_rate(clk, rate, &mux_val, &div_val,
&parent_id);
if (!best_rate)
return -EINVAL;
if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 ||
common->reg_type == CLK_DIV_TYPE_2REG_FC_V4)
reg = readl(common->base + common->reg_sel);
else
reg = readl(common->base + common->reg_ctrl);
if (mux_config) {
cur_mux = reg >> mux_config->shift;
cur_mux &= (1 << mux_config->width) - 1;
if (cur_mux != mux_val) {
parent = common->clk_tbl->clks[parent_id];
if (!clk_valid(parent))
return -EINVAL;
ret = clk_set_parent(clk, parent);
if (ret)
return ret;
}
}
if (div_config) {
cur_div = reg >> div_config->shift;
cur_div &= (1 << div_config->width) - 1;
if (cur_div == div_val)
return 0;
} else {
return 0;
}
val = div_val;
if (val > BIT(div_config->width) - 1)
return 0;
if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 ||
common->reg_type == CLK_DIV_TYPE_2REG_FC_V4)
reg = readl(common->base + common->reg_sel);
else
reg = readl(common->base + common->reg_ctrl);
reg &= ~GENMASK(div_config->width + div_config->shift - 1,
div_config->shift);
if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 ||
common->reg_type == CLK_DIV_TYPE_2REG_FC_V4)
writel(reg | (val << div_config->shift),
common->base + common->reg_sel);
else
writel(reg | (val << div_config->shift),
common->base + common->reg_ctrl);
if (common->reg_type == CLK_DIV_TYPE_1REG_FC_V2 ||
common->reg_type == CLK_DIV_TYPE_2REG_FC_V4 ||
common->reg_type == CLK_DIV_TYPE_1REG_FC_DIV_V5 ||
common->reg_type == CLK_DIV_TYPE_1REG_FC_MUX_V6) {
ret = ccu_mix_trigger_fc(clk);
if (ret)
pr_info("%s of %s timeout\n", __func__, clk->dev->name);
}
return 0;
}
unsigned int ccu_mix_get_parent(struct clk *clk)
{
struct ccu_mix *mix = clk_to_ccu_mix(clk);
struct ccu_common *common = &mix->common;
struct ccu_mux_config *mux = mix->mux;
u32 reg;
unsigned int parent;
if (!mux)
return 0;
if (common->reg_type == CLK_DIV_TYPE_2REG_NOFC_V3 ||
common->reg_type == CLK_DIV_TYPE_2REG_FC_V4)
reg = readl(common->base + common->reg_sel);
else
reg = readl(common->base + common->reg_ctrl);
parent = reg >> mux->shift;
parent &= (1 << mux->width) - 1;
if (mux->table) {
int num_parents = common->num_parents;
int i;
for (i = 0; i < num_parents; i++)
if (mux->table[i] == parent)
return i;
}
return parent;
}
const struct clk_ops ccu_mix_ops = {
.disable = ccu_mix_disable,
.round_rate = ccu_mix_round_rate,
.set_parent = ccu_mix_set_parent,
.enable = ccu_mix_enable,
.get_rate = ccu_mix_get_rate,
.set_rate = ccu_mix_set_rate,
};
U_BOOT_DRIVER(ccu_clk_mix) = {
.name = CCU_CLK_MIX,
.id = UCLASS_CLK,
.ops = &ccu_mix_ops,
};
-368
View File
@@ -1,368 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023, spacemit Corporation.
*
*/
#ifndef _CCU_MIX_H_
#define _CCU_MIX_H_
#include "ccu-k3.h"
#include "common.h"
#define CCU_CLK_MIX "ccu_clk_mix"
#define SPACEMIT_CLK_GATE_NEED_DELAY BIT(0)
struct ccu_gate_config {
u32 gate_mask;
u32 val_enable;
u32 val_disable;
u32 flags;
};
struct ccu_factor_config {
u32 div;
u32 mul;
};
struct ccu_mux_config {
u8 shift;
u8 width;
const u8 *table;
u32 flags;
};
struct ccu_div_config {
u8 shift;
u8 width;
u32 max;
u32 offset;
u32 flags;
struct clk_div_table *table;
};
struct ccu_mix {
struct ccu_gate_config *gate;
struct ccu_factor_config *factor;
struct ccu_div_config *div;
struct ccu_mux_config *mux;
struct ccu_common common;
};
#define CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, _flags) \
(&(struct ccu_gate_config) { \
.gate_mask = _gate_mask, \
.val_enable = _val_enable, \
.val_disable = _val_disable, \
.flags = _flags, \
})
#define CCU_FACTOR_INIT(_div, _mul) \
(&(struct ccu_factor_config) { \
.div = _div, \
.mul = _mul, \
})
#define CCU_MUX_INIT(_shift, _width, _table, _flags) \
(&(struct ccu_mux_config) { \
.shift = _shift, \
.width = _width, \
.table = _table, \
.flags = _flags, \
})
#define CCU_DIV_INIT(_shift, _width, _table, _flags) \
(&(struct ccu_div_config) { \
.shift = _shift, \
.width = _width, \
.flags = _flags, \
.table = _table, \
})
#define SPACEMIT_CCU_GATE(_struct, _name, _parent, _base_type, _reg, \
_gate_mask, _val_enable, _val_disable, _flags) \
struct ccu_mix _struct = { \
.gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \
.common = { \
.reg_ctrl = _reg, \
.base_type = _base_type, \
.name = _name, \
.parent_name = _parent, \
.num_parents = 1, \
.driver_name = CCU_CLK_MIX, \
.flags = _flags, \
} \
}
#define SPACEMIT_CCU_GATE_NO_PARENT(_struct, _name, _parent, _base_type, _reg, \
_gate_mask, _val_enable, _val_disable, _flags) \
struct ccu_mix _struct = { \
.gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \
.common = { \
.reg_ctrl = _reg, \
.base_type = _base_type, \
.name = _name, \
.parent_name = SPACEMIT_CLK_NO_PARENT, \
.num_parents = 0, \
.driver_name = CCU_CLK_MIX, \
.flags = _flags, \
} \
}
#define SPACEMIT_CCU_FACTOR(_struct, _name, _parent, \
_div, _mul) \
struct ccu_mix _struct = { \
.factor = CCU_FACTOR_INIT(_div, _mul), \
.common = { \
.name = _name, \
.parent_name = _parent, \
.num_parents = 1, \
.driver_name = CCU_CLK_MIX, \
} \
}
#define SPACEMIT_CCU_MUX(_struct, _name, _parents, _base_type, _reg, \
_shift, _width, _flags) \
struct ccu_mix _struct = { \
.mux = CCU_MUX_INIT(_shift, _width, NULL, 0), \
.common = { \
.reg_ctrl = _reg, \
.base_type = _base_type, \
.name = _name, \
.parent_names = _parents, \
.num_parents = ARRAY_SIZE(_parents), \
.driver_name = CCU_CLK_MIX, \
.flags = _flags, \
} \
}
#define SPACEMIT_CCU_DIV(_struct, _name, _parent, _base_type, _reg, \
_shift, _width, _flags) \
struct ccu_mix _struct = { \
.div = CCU_DIV_INIT(_shift, _width, NULL, 0), \
.common = { \
.reg_ctrl = _reg, \
.base_type = _base_type, \
.name = _name, \
.parent_name = _parent, \
.num_parents = 1, \
.driver_name = CCU_CLK_MIX, \
.flags = _flags, \
} \
}
#define SPACEMIT_CCU_GATE_FACTOR(_struct, _name, _parent, _base_type, _reg, \
_gate_mask, _val_enable, _val_disable, \
_div, _mul, _flags) \
struct ccu_mix _struct = { \
.gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \
.factor = CCU_FACTOR_INIT(_div, _mul), \
.common = { \
.reg_ctrl = _reg, \
.base_type = _base_type, \
.name = _name, \
.parent_name = _parent, \
.num_parents = 1, \
.driver_name = CCU_CLK_MIX, \
.flags = _flags, \
} \
}
#define SPACEMIT_CCU_MUX_GATE(_struct, _name, _parents, _base_type, _reg, \
_shift, _width, _gate_mask, _val_enable, _val_disable, _flags) \
struct ccu_mix _struct = { \
.gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \
.mux = CCU_MUX_INIT(_shift, _width, NULL, 0), \
.common = { \
.reg_ctrl = _reg, \
.base_type = _base_type, \
.name = _name, \
.parent_names = _parents, \
.num_parents = ARRAY_SIZE(_parents), \
.driver_name = CCU_CLK_MIX, \
.flags = _flags, \
} \
}
#define SPACEMIT_CCU_DIV_GATE(_struct, _name, _parent, _base_type, _reg, \
_shift, _width, _gate_mask, _val_enable, _val_disable, _flags) \
struct ccu_mix _struct = { \
.gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \
.div = CCU_DIV_INIT(_shift, _width, NULL, 0), \
.common = { \
.reg_ctrl = _reg, \
.base_type = _base_type, \
.name = _name, \
.parent_name = _parent, \
.num_parents = 1, \
.driver_name = CCU_CLK_MIX, \
.flags = _flags, \
} \
}
#define SPACEMIT_CCU_DIV_MUX_GATE(_struct, _name, _parents, \
_base_type, _reg_ctrl, \
_mshift, _mwidth, \
_muxshift, _muxwidth, \
_gate_mask, _val_enable, _val_disable, _flags) \
struct ccu_mix _struct = { \
.gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \
.div = CCU_DIV_INIT(_mshift, _mwidth, NULL, 0), \
.mux = CCU_MUX_INIT(_muxshift, _muxwidth, NULL, 0), \
.common = { \
.reg_ctrl = _reg_ctrl, \
.base_type = _base_type, \
.name = _name, \
.parent_names = _parents, \
.num_parents = ARRAY_SIZE(_parents), \
.driver_name = CCU_CLK_MIX, \
.flags = _flags, \
}, \
}
#define SPACEMIT_CCU_DIV2_FC_MUX_GATE(_struct, _name, _parents, _base_type, _reg_ctrl, _reg_sel, \
_mshift, _mwidth, _fc, _muxshift, _muxwidth, _gate_mask, _val_enable, _val_disable, \
_flags) \
struct ccu_mix _struct = { \
.gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \
.div = CCU_DIV_INIT(_mshift, _mwidth, NULL, 0), \
.mux = CCU_MUX_INIT(_muxshift, _muxwidth, NULL, 0), \
.common = { \
.reg_type = CLK_DIV_TYPE_2REG_FC_V4, \
.reg_ctrl = _reg_ctrl, \
.reg_sel = _reg_sel, \
.fc = _fc, \
.base_type = _base_type, \
.name = _name, \
.parent_names = _parents, \
.num_parents = ARRAY_SIZE(_parents), \
.driver_name = CCU_CLK_MIX, \
.flags = _flags, \
}, \
}
#define SPACEMIT_CCU_DIV_FC_MUX_GATE(_struct, _name, _parents, _base_type, _reg_ctrl, \
_mshift, _mwidth, _fc, _muxshift, _muxwidth, _gate_mask, _val_enable, _val_disable, \
_flags) \
struct ccu_mix _struct = { \
.gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \
.div = CCU_DIV_INIT(_mshift, _mwidth, NULL, 0), \
.mux = CCU_MUX_INIT(_muxshift, _muxwidth, NULL, 0), \
.common = { \
.reg_type = CLK_DIV_TYPE_1REG_FC_V2, \
.reg_ctrl = _reg_ctrl, \
.fc = _fc, \
.base_type = _base_type, \
.name = _name, \
.parent_names = _parents, \
.num_parents = ARRAY_SIZE(_parents), \
.driver_name = CCU_CLK_MIX, \
.flags = _flags, \
}, \
}
#define SPACEMIT_CCU_DIV_MFC_MUX_GATE(_struct, _name, _parents, _base_type, _reg_ctrl, \
_mshift, _mwidth, _fc, _muxshift, _muxwidth, _gate_mask, _val_enable, _val_disable, \
_flags) \
struct ccu_mix _struct = { \
.gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \
.div = CCU_DIV_INIT(_mshift, _mwidth, NULL, 0), \
.mux = CCU_MUX_INIT(_muxshift, _muxwidth, NULL, 0), \
.common = { \
.reg_type = CLK_DIV_TYPE_1REG_FC_MUX_V6, \
.reg_ctrl = _reg_ctrl, \
.fc = _fc, \
.base_type = _base_type, \
.name = _name, \
.parent_names = _parents, \
.num_parents = ARRAY_SIZE(_parents), \
.driver_name = CCU_CLK_MIX, \
.flags = _flags, \
}, \
}
#define SPACEMIT_CCU_DIV_FC_WITH_GATE(_struct, _name, _parent, _base_type, _reg_ctrl, \
_mshift, _mwidth, _fc, _gate_mask, _val_enable, _val_disable, \
_flags) \
struct ccu_mix _struct = { \
.gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \
.div = CCU_DIV_INIT(_mshift, _mwidth, NULL, 0), \
.common = { \
.reg_type = CLK_DIV_TYPE_1REG_FC_V2, \
.reg_ctrl = _reg_ctrl, \
.fc = _fc, \
.base_type = _base_type, \
.name = _name, \
.parent_name = _parent, \
.num_parents = 1, \
.driver_name = CCU_CLK_MIX, \
.flags = _flags, \
}, \
}
#define SPACEMIT_CCU_DIV_FC_MUX(_struct, _name, _parents, _base_type, _reg_ctrl, \
_mshift, _mwidth, _fc, _muxshift, _muxwidth, _flags) \
struct ccu_mix _struct = { \
.div = CCU_DIV_INIT(_mshift, _mwidth, NULL, 0), \
.mux = CCU_MUX_INIT(_muxshift, _muxwidth, NULL, 0), \
.common = { \
.reg_type = CLK_DIV_TYPE_1REG_FC_V2, \
.reg_ctrl = _reg_ctrl, \
.fc = _fc, \
.base_type = _base_type, \
.name = _name, \
.parent_names = _parents, \
.num_parents = ARRAY_SIZE(_parents), \
.driver_name = CCU_CLK_MIX, \
.flags = _flags, \
}, \
}
#define SPACEMIT_CCU_MUX_FC_GATE(_struct, _name, _parents, _base_type, _reg_ctrl, \
_fc, _muxshift, _muxwidth, _gate_mask, _val_enable, _val_disable, _flags) \
struct ccu_mix _struct = { \
.gate = CCU_GATE_INIT(_gate_mask, _val_enable, _val_disable, 0), \
.mux = CCU_MUX_INIT(_muxshift, _muxwidth, NULL, 0), \
.common = { \
.reg_type = CLK_DIV_TYPE_1REG_FC_V2, \
.reg_ctrl = _reg_ctrl, \
.fc = _fc, \
.base_type = _base_type, \
.name = _name, \
.parent_names = _parents, \
.num_parents = ARRAY_SIZE(_parents), \
.driver_name = CCU_CLK_MIX, \
.flags = _flags, \
}, \
}
#define SPACEMIT_CCU_MUX_FC(_struct, _name, _parents, _base_type, _reg_ctrl, \
_fc, _muxshift, _muxwidth, _flags) \
struct ccu_mix _struct = { \
.mux = CCU_MUX_INIT(_muxshift, _muxwidth, NULL, 0), \
.common = { \
.reg_type = CLK_DIV_TYPE_1REG_FC_V2, \
.reg_ctrl = _reg_ctrl, \
.fc = _fc, \
.base_type = _base_type, \
.name = _name, \
.parent_names = _parents, \
.num_parents = ARRAY_SIZE(_parents), \
.driver_name = CCU_CLK_MIX, \
.flags = _flags, \
}, \
}
static inline struct ccu_mix *clk_to_ccu_mix(struct clk *clk)
{
struct ccu_common *common = clk_to_ccu_common(clk);
return container_of(common, struct ccu_mix, common);
}
unsigned int ccu_mix_get_parent(struct clk *clk);
#endif /* _CCU_DIV_H_ */
-267
View File
@@ -1,267 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2024 SpacemiT Technology Co. Ltd
* Copyright (c) 2024-2025 Haylen Chu <heylenay@4d2.org>
*/
#include <asm/io.h>
#include <malloc.h>
#include <clk-uclass.h>
#include <linux/bitops.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/iopoll.h>
#include <clk.h>
#include <div64.h>
#include "ccu_plla.h"
#define PLL_MIN_FREQ 600000000
#define PLL_MAX_FREQ 3400000000
#define PLL_DELAYTIME 590 //(590*5)us
#define PLLA_SWCR2_MASK GENMASK(15, 8)
#define plla_readl(reg) readl(reg)
#define plla_readl_pll_swcr1(p) plla_readl(p.base + p.reg_ctrl)
#define plla_readl_pll_swcr2(p) plla_readl(p.base + p.reg_sel)
#define plla_readl_pll_swcr3(p) plla_readl(p.base + p.reg_xtc)
#define plla_writel(val, reg) writel(val, reg)
#define plla_writel_pll_swcr1(val, p) plla_writel(val, p.base + p.reg_ctrl)
#define plla_writel_pll_swcr2(val, p) plla_writel(val, p.base + p.reg_sel)
#define plla_writel_pll_swcr3(val, p) plla_writel(val, p.base + p.reg_xtc)
/* unified pllax_swcr1 for plla */
union pllax_swcr1 {
struct {
unsigned int reg1:8;
unsigned int reg2:8;
unsigned int reg3:8;
unsigned int reg4:8;
} b;
unsigned int v;
};
/* unified pllax_swcr2 for plla */
union pllax_swcr2 {
struct {
unsigned int div1_en:1;
unsigned int div2_en:1;
unsigned int div3_en:1;
unsigned int div4_en:1;
unsigned int div5_en:1;
unsigned int div6_en:1;
unsigned int div7_en:1;
unsigned int div8_en:1;
unsigned int reg0:8;
unsigned int pll_en:1;
unsigned int mon_cfg:4;
unsigned int div10_en:1;
unsigned int mmd_en:1;
unsigned int mmd:5;
unsigned int reserved2:3;
unsigned int div64_en:1;
} b;
unsigned int v;
};
/* unified pllax_swcr3 for plla */
union pllax_swcr3{
struct {
unsigned int reg5:8;
unsigned int reg6:8;
unsigned int reg7:8;
unsigned int reg8:8;
} b;
unsigned int v;
};
static int ccu_plla_is_enabled(struct clk *clk)
{
struct ccu_plla *p = clk_to_ccu_plla(clk);
union pllax_swcr2 swcr2;
unsigned int enabled;
swcr2.v = plla_readl_pll_swcr2(p->common);
enabled = swcr2.b.pll_en;
return enabled;
}
/* frequency unit Mhz, return pll vco freq */
static ulong __get_vco_freq(struct clk *clk)
{
unsigned int size, i;
struct ccu_plla_rate_tbl *freq_pll_regs_table;
struct ccu_plla *p = clk_to_ccu_plla(clk);
union pllax_swcr1 swcr1;
union pllax_swcr2 swcr2;
union pllax_swcr3 swcr3;
swcr1.v = plla_readl_pll_swcr1(p->common);
swcr2.v = plla_readl_pll_swcr2(p->common);
swcr2.v &= PLLA_SWCR2_MASK;
swcr3.v = plla_readl_pll_swcr3(p->common);
freq_pll_regs_table = p->pll.rate_tbl;
size = p->pll.tbl_size;
for (i = 0; i < size; i++) {
if ((freq_pll_regs_table[i].swcr1 == swcr1.v) &&
(freq_pll_regs_table[i].swcr2 == swcr2.v) &&
(freq_pll_regs_table[i].swcr3 == swcr3.v))
return freq_pll_regs_table[i].rate;
}
pr_info("Unknown rate for clock\n");
return 0;
}
static int ccu_plla_enable(struct clk *clk)
{
unsigned int delaytime = PLL_DELAYTIME;
struct ccu_plla *p = clk_to_ccu_plla(clk);
union pllax_swcr2 swcr2;
if (ccu_plla_is_enabled(clk))
return 0;
swcr2.v = plla_readl_pll_swcr2(p->common);
swcr2.b.pll_en = 1;
plla_writel_pll_swcr2(swcr2.v, p->common);
/* check lock status */
udelay(50);
while ((!(readl(p->pll.lock_base + p->pll.reg_lock) & p->pll.lock_enable_bit))
&& delaytime) {
udelay(5);
delaytime--;
}
if (unlikely(!delaytime))
pr_err("ccu_pll_enable enabling didn't get stable within 3000us!!!\n" );
return 0;
}
static int ccu_plla_disable(struct clk *clk)
{
struct ccu_plla *p = clk_to_ccu_plla(clk);
union pllax_swcr2 swcr2;
swcr2.v = plla_readl_pll_swcr2(p->common);
swcr2.b.pll_en = 0;
plla_writel_pll_swcr2(swcr2.v, p->common);
return 0;
}
/*
* pll rate change requires sequence:
* clock off -> change rate setting -> clock on
* This function doesn't really change rate, but cache the config
*/
static ulong ccu_plla_set_rate(struct clk *clk, ulong rate)
{
unsigned int i, reg0 = 0;
struct ccu_plla *p = clk_to_ccu_plla(clk);
struct ccu_plla_config *params = &p->pll;
union pllax_swcr1 swcr1;
union pllax_swcr1 swcr2;
union pllax_swcr3 swcr3;
bool found = false;
bool pll_enabled = false;
if (ccu_plla_is_enabled(clk)) {
pll_enabled = true;
ccu_plla_disable(clk);
}
/* setp 1: calculate fbd frcd kvco and band */
if (params->rate_tbl) {
for (i = 0; i < params->tbl_size; i++) {
if (rate == params->rate_tbl[i].rate) {
found = true;
swcr1.v = params->rate_tbl[i].swcr1;
reg0 = params->rate_tbl[i].swcr2;
swcr3.v = params->rate_tbl[i].swcr3;
break;
}
}
} else {
pr_err("don't find freq table for pll\n");
if (pll_enabled)
ccu_plla_enable(clk);
return -EINVAL;
}
if (!found) {
pr_err("unsupported pll rate %lu\n", rate);
if (pll_enabled)
ccu_plla_enable(clk);
return -EINVAL;
}
/* setp 2: set pll kvco/band and fbd/frcd setting */
plla_writel_pll_swcr1(swcr1.v, p->common);
swcr2.v = plla_readl_pll_swcr2(p->common);
swcr2.v &= ~PLLA_SWCR2_MASK;
swcr2.v |= reg0;
plla_writel_pll_swcr2(swcr2.v, p->common);
plla_writel_pll_swcr3(swcr3.v, p->common);
if (pll_enabled)
ccu_plla_enable(clk);
return 0;
}
static ulong ccu_plla_get_rate(struct clk *clk)
{
ulong val;
val = __get_vco_freq(clk);
return val;
}
static ulong ccu_plla_round_rate(struct clk *clk, ulong rate)
{
struct ccu_plla *p = clk_to_ccu_plla(clk);
unsigned long max_rate = 0;
unsigned int i;
struct ccu_plla_config *params = &p->pll;
if (rate > PLL_MAX_FREQ || rate < PLL_MIN_FREQ) {
pr_err("%lu rate out of range!\n", rate);
return -EINVAL;
}
if (params->rate_tbl) {
for (i = 0; i < params->tbl_size; i++) {
if (params->rate_tbl[i].rate <= rate) {
if (max_rate < params->rate_tbl[i].rate)
max_rate = params->rate_tbl[i].rate;
}
}
} else {
pr_info("don't find freq table for pll\n");
}
return max_rate;
}
const struct clk_ops ccu_plla_ops = {
.enable = ccu_plla_enable,
.disable = ccu_plla_disable,
.set_rate = ccu_plla_set_rate,
.get_rate = ccu_plla_get_rate,
.round_rate = ccu_plla_round_rate,
};
U_BOOT_DRIVER(ccu_clk_plla) = {
.name = CCU_CLK_PLLA,
.id = UCLASS_CLK,
.ops = &ccu_plla_ops,
};
-76
View File
@@ -1,76 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2024 SpacemiT Technology Co. Ltd
* Copyright (c) 2024-2025 Haylen Chu <heylenay@4d2.org>
*/
#ifndef _CCU_PLLA_H_
#define _CCU_PLLA_H_
#include "common.h"
#define CCU_CLK_PLLA "ccu_clk_plla"
struct ccu_plla_rate_tbl {
unsigned long long rate;
u32 swcr1;
u32 swcr2;
u32 swcr3;
};
struct ccu_plla_config {
struct ccu_plla_rate_tbl *rate_tbl;
u32 tbl_size;
void __iomem *lock_base;
u32 reg_lock;
u32 lock_enable_bit;
};
#define PLLA_RATE(_rate, _swcr1, _swcr2, _swcr3) \
{ \
.rate = _rate, \
.swcr1 = _swcr1, \
.swcr2 = _swcr2, \
.swcr3 = _swcr3, \
}
struct ccu_plla {
struct ccu_plla_config pll;
struct ccu_common common;
};
#define _SPACEMIT_CCU_PLLA_CONFIG(_table, _size, _reg_lock, _lock_enable_bit) \
{ \
.rate_tbl = (struct ccu_plla_rate_tbl *)_table, \
.tbl_size = _size, \
.reg_lock = _reg_lock, \
.lock_enable_bit = _lock_enable_bit, \
}
#define SPACEMIT_CCU_PLLA(_struct, _name, _table, _size, \
_base_type, _reg_ctrl, _reg_sel, _reg_xtc, \
_reg_lock, _lock_enable_bit, _is_pll, \
_flags) \
struct ccu_plla _struct = { \
.pll = _SPACEMIT_CCU_PLLA_CONFIG(_table, _size, _reg_lock, _lock_enable_bit), \
.common = { \
.reg_ctrl = _reg_ctrl, \
.reg_sel = _reg_sel, \
.reg_xtc = _reg_xtc, \
.base_type = _base_type, \
.is_pll = _is_pll, \
.name = _name, \
.parent_name = SPACEMIT_CLK_NO_PARENT, \
.num_parents = 1, \
.driver_name = CCU_CLK_PLLA, \
} \
}
static inline struct ccu_plla *clk_to_ccu_plla(struct clk *clk)
{
struct ccu_common *common = clk_to_ccu_common(clk);
return container_of(common, struct ccu_plla, common);
}
#endif
-82
View File
@@ -1,82 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023, spacemit Corporation.
*
*/
#ifndef _CCU_SPACEMIT_COMMON_H_
#define _CCU_SPACEMIT_COMMON_H_
#include <clk.h>
#include <clk-uclass.h>
#include <dt-bindings/clock/spacemit-k3-clock.h>
#define SPACEMIT_CLK_NO_PARENT "clk_dummy"
enum ccu_base_type{
BASE_TYPE_MPMU = 0,
BASE_TYPE_APMU = 1,
BASE_TYPE_APBC = 2,
BASE_TYPE_APBS = 3,
BASE_TYPE_CIU = 4,
BASE_TYPE_DCIU = 5,
BASE_TYPE_DDRC = 6,
BASE_TYPE_AUDC = 7,
BASE_TYPE_APBC2 = 8,
BASE_TYPE_RCPU = 9,
};
enum {
CLK_DIV_TYPE_1REG_NOFC_V1 = 0,
CLK_DIV_TYPE_1REG_FC_V2,
CLK_DIV_TYPE_2REG_NOFC_V3,
CLK_DIV_TYPE_2REG_FC_V4,
CLK_DIV_TYPE_1REG_FC_DIV_V5,
CLK_DIV_TYPE_1REG_FC_MUX_V6,
};
struct ccu_common {
void __iomem *base;
enum ccu_base_type base_type;
u32 reg_type;
u32 reg_ctrl;
u32 reg_sel;
u32 reg_xtc;
u32 fc;
bool is_pll;
const char *name;
const char *driver_name;
const char *parent_name;
const char * const *parent_names;
u8 num_parents;
unsigned long flags;
struct clk clk;
struct spacemit_clk_table * clk_tbl;
};
struct spacemit_ccu_clk {
void __iomem *mpmu_base;
void __iomem *apmu_base;
void __iomem *apbc_base;
void __iomem *apbs_base;
void __iomem *ciu_base;
void __iomem *dciu_base;
void __iomem *ddrc_base;
void __iomem *audio_ctrl_base;
void __iomem *apbc2_base;
void __iomem *rcpu_base;
u32 pll2_freq;
};
struct spacemit_clk_table{
struct clk* clks[CLK_MAX_NO];
unsigned int num;
};
static inline struct ccu_common *clk_to_ccu_common(struct clk *clk)
{
return container_of(clk, struct ccu_common, clk);
}
#endif /* _CCU_SPACEMIT_COMMON_H_ */
+19
View File
@@ -38,6 +38,7 @@
#include <linux/sizes.h>
#include <linux/err.h>
#include <asm/io.h>
#include <clk.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -271,6 +272,10 @@ struct fsl_qspi {
struct udevice *dev;
void __iomem *iobase;
void __iomem *ahb_addr;
#if CONFIG_IS_ENABLED(CLK)
struct clk clk_en;
struct clk clk;
#endif
u32 memmap_phy;
u32 memmap_size;
const struct fsl_qspi_devtype_data *devtype_data;
@@ -820,7 +825,21 @@ static int fsl_qspi_probe(struct udevice *bus)
dm_bus->max_hz = fdtdec_get_int(blob, node, "spi-max-frequency",
66000000);
#if CONFIG_IS_ENABLED(CLK)
ret = clk_get_by_name(bus, "qspi_en", &q->clk_en);
if (ret) {
dev_err(bus, "cannot find qspi_en clock\n");
return ret;
}
ret = clk_get_by_name(bus, "qspi", &q->clk);
if (ret) {
dev_err(bus, "cannot find qspi clock\n");
return ret;
}
clk_enable(&q->clk_en);
clk_enable(&q->clk);
#endif
fsl_qspi_default_setup(q);
return 0;
@@ -1,302 +0,0 @@
// SPDX-License-Identifier: (GPL-2.0+ or MIT)
#ifndef _DT_BINDINGS_CLK_SPACEMIT_K3_H_
#define _DT_BINDINGS_CLK_SPACEMIT_K3_H_
#define CLK_PLL1_2457P6 0
#define CLK_PLL2 1
#define CLK_PLL3 2
#define CLK_PLL4 3
#define CLK_PLL5 4
#define CLK_PLL6 5
#define CLK_PLL7 6
#define CLK_PLL8 7
#define CLK_PLL1_D2 8
#define CLK_PLL1_D3 9
#define CLK_PLL1_D4 10
#define CLK_PLL1_D5 11
#define CLK_PLL1_D6 12
#define CLK_PLL1_D7 13
#define CLK_PLL1_D8 14
#define CLK_PLL1_DX 15
#define CLK_PLL1_D64 16
#define CLK_PLL1_D10_AUD 17
#define CLK_PLL1_D100_AUD 18
#define CLK_PLL2_D1 19
#define CLK_PLL2_D2 20
#define CLK_PLL2_D3 21
#define CLK_PLL2_D4 22
#define CLK_PLL2_D5 23
#define CLK_PLL2_D6 24
#define CLK_PLL2_D7 25
#define CLK_PLL2_D8 26
#define CLK_PLL2_66 27
#define CLK_PLL2_33 28
#define CLK_PLL2_50 29
#define CLK_PLL2_25 30
#define CLK_PLL2_20 31
#define CLK_PLL2_D24_125 32
#define CLK_PLL2_D120_25 33
#define CLK_PLL3_D1 34
#define CLK_PLL3_D2 35
#define CLK_PLL3_D3 36
#define CLK_PLL3_D4 37
#define CLK_PLL3_D5 38
#define CLK_PLL3_D6 39
#define CLK_PLL3_D7 40
#define CLK_PLL3_D8 41
#define CLK_PLL4_D1 42
#define CLK_PLL4_D2 43
#define CLK_PLL4_D3 44
#define CLK_PLL4_D4 45
#define CLK_PLL4_D5 46
#define CLK_PLL4_D6 47
#define CLK_PLL4_D7 48
#define CLK_PLL4_D8 49
#define CLK_PLL5_D1 50
#define CLK_PLL5_D2 51
#define CLK_PLL5_D3 52
#define CLK_PLL5_D4 53
#define CLK_PLL5_D5 54
#define CLK_PLL5_D6 55
#define CLK_PLL5_D7 56
#define CLK_PLL5_D8 57
#define CLK_PLL6_D1 58
#define CLK_PLL6_D2 59
#define CLK_PLL6_D3 60
#define CLK_PLL6_D4 61
#define CLK_PLL6_D5 62
#define CLK_PLL6_D6 63
#define CLK_PLL6_D7 64
#define CLK_PLL6_D8 65
#define CLK_PLL6_80 66
#define CLK_PLL6_40 67
#define CLK_PLL6_20 68
#define CLK_PLL7_D1 69
#define CLK_PLL7_D2 70
#define CLK_PLL7_D3 71
#define CLK_PLL7_D4 72
#define CLK_PLL7_D5 73
#define CLK_PLL7_D6 74
#define CLK_PLL7_D7 75
#define CLK_PLL7_D8 76
#define CLK_PLL8_D1 77
#define CLK_PLL8_D2 78
#define CLK_PLL8_D3 79
#define CLK_PLL8_D4 80
#define CLK_PLL8_D5 81
#define CLK_PLL8_D6 82
#define CLK_PLL8_D7 83
#define CLK_PLL8_D8 84
#define CLK_PLL1_307P2 85
#define CLK_PLL1_76P8 86
#define CLK_PLL1_61P44 87
#define CLK_PLL1_153P6 88
#define CLK_PLL1_102P4 89
#define CLK_PLL1_51P2 90
#define CLK_PLL1_51P2_AP 91
#define CLK_PLL1_57P6 92
#define CLK_PLL1_25P6 93
#define CLK_PLL1_12P8 94
#define CLK_PLL1_12P8_WDT 95
#define CLK_PLL1_6P4 96
#define CLK_PLL1_3P2 97
#define CLK_PLL1_1P6 98
#define CLK_PLL1_0P8 99
#define CLK_PLL1_351 100
#define CLK_PLL1_409P6 101
#define CLK_PLL1_204P8 102
#define CLK_PLL1_491 103
#define CLK_PLL1_245P76 104
#define CLK_PLL1_614 105
#define CLK_PLL1_47P26 106
#define CLK_PLL1_31P5 107
#define CLK_PLL1_819 108
#define CLK_PLL1_1228 109
#define CLK_APB 110
#define CLK_SLOW_UART1 111
#define CLK_SLOW_UART2 112
#define CLK_WDT 113
#define CLK_RIPC 114
#define CLK_UART1 115
#define CLK_UART2 116
#define CLK_UART3 117
#define CLK_UART4 118
#define CLK_UART5 119
#define CLK_UART6 120
#define CLK_UART7 121
#define CLK_UART8 122
#define CLK_UART9 123
#define CLK_UART10 124
#define CLK_GPIO 125
#define CLK_PWM0 126
#define CLK_PWM1 127
#define CLK_PWM2 128
#define CLK_PWM3 129
#define CLK_PWM4 130
#define CLK_PWM5 131
#define CLK_PWM6 132
#define CLK_PWM7 133
#define CLK_PWM8 134
#define CLK_PWM9 135
#define CLK_PWM10 136
#define CLK_PWM11 137
#define CLK_PWM12 138
#define CLK_PWM13 139
#define CLK_PWM14 140
#define CLK_PWM15 141
#define CLK_PWM16 142
#define CLK_PWM17 143
#define CLK_PWM18 144
#define CLK_PWM19 145
#define CLK_SPI0 146
#define CLK_SPI1 147
#define CLK_SPI3 148
#define CLK_RTC 149
#define CLK_TWSI0 150
#define CLK_TWSI1 151
#define CLK_TWSI2 152
#define CLK_TWSI4 153
#define CLK_TWSI5 154
#define CLK_TWSI6 155
#define CLK_TWSI8 156
#define CLK_TIMERS0 157
#define CLK_TIMERS1 158
#define CLK_TIMERS2 159
#define CLK_TIMERS3 160
#define CLK_TIMERS4 161
#define CLK_TIMERS5 162
#define CLK_TIMERS6 163
#define CLK_TIMERS7 164
#define CLK_AIB 165
#define CLK_ONEWIRE 166
#define CLK_I2S0 167
#define CLK_I2S1 168
#define CLK_I2S2 169
#define CLK_I2S3 170
#define CLK_I2S4 171
#define CLK_I2S5 172
#define CLK_DRO 173
#define CLK_IR0 174
#define CLK_IR1 175
#define CLK_TSEN 176
#define CLK_IPC_AP2RCPU 177
#define CLK_CAN0 178
#define CLK_CAN1 179
#define CLK_CAN2 180
#define CLK_CAN3 181
#define CLK_CAN4 182
#define CLK_CAN0_BUS 183
#define CLK_CAN1_BUS 184
#define CLK_CAN2_BUS 185
#define CLK_CAN3_BUS 186
#define CLK_CAN4_BUS 187
#define CLK_AXICLK 188
#define CLK_CCI550 189
#define CLK_CPU_C1_PLL_SRC 190
#define CLK_CPU_C3_PLL_SRC 191
#define CLK_CPU_C0_CORE 192
#define CLK_CPU_C1_CORE 193
#define CLK_CPU_C2_CORE 194
#define CLK_CPU_C3_CORE 195
#define CLK_CCIC2PHY 196
#define CLK_CCIC3PHY 197
#define CLK_CSI 198
#define CLK_ISP_BUS 199
#define CLK_D1P_1228P8 200
#define CLK_D1P_819P2 201
#define CLK_D1P_614P4 202
#define CLK_D1P_491P52 203
#define CLK_D1P_409P6 204
#define CLK_D1P_307P2 205
#define CLK_D1P_245P76 206
#define CLK_V2D 207
#define CLK_DSI_ESC 208
#define CLK_LCD_HCLK 209
#define CLK_LCD_DSC 210
#define CLK_LCD_PXCLK 211
#define CLK_LCD_MCLK 212
#define CLK_CCIC_4X 213
#define CLK_CCIC1PHY 214
#define CLK_SC2_HCLK 215
#define CLK_SDH_AXI 216
#define CLK_SDH0 217
#define CLK_SDH1 218
#define CLK_SDH2 219
#define CLK_USB2_BUS 220
#define CLK_USB3_PORTA_BUS 221
#define CLK_USB3_PORTB_BUS 222
#define CLK_USB3_PORTC_BUS 223
#define CLK_USB3_PORTD_BUS 224
#define CLK_QSPI 225
#define CLK_QSPI_BUS 226
#define CLK_DMA 227
#define CLK_AES_WTM 228
#define CLK_VPU 229
#define CLK_DTC 230
#define CLK_GPU 231
#define CLK_MC_AHB 232
#define CLK_TOP_DCLK 233
#define CLK_UCIE 234
#define CLK_UCIE_SBCLK 235
#define CLK_RCPU 236
#define CLK_DSI4LN2_DSI_ESC 237
#define CLK_DSI4LN2_LCD_DSC 238
#define CLK_DSI4LN2_LCD_PXCLK 239
#define CLK_DSI4LN2_LCD_MCLK 240
#define CLK_DSI4LN2_DPU_ACLK 241
#define CLK_DPU_ACLK 242
#define CLK_UFS_ACLK 243
#define CLK_EDP0_PXCLK 244
#define CLK_EDP1_PXCLK 245
#define CLK_PCIEA 246
#define CLK_PCIEB 247
#define CLK_PCIEC 248
#define CLK_PCIED 249
#define CLK_PCIEE 250
#define CLK_EMAC0_BUS 251
#define CLK_EMAC0_REF 252
#define CLK_EMAC0_1588 253
#define CLK_EMAC0_RGMII_TX 254
#define CLK_EMAC1_BUS 255
#define CLK_EMAC1_REF 256
#define CLK_EMAC1_1588 257
#define CLK_EMAC1_RGMII_TX 258
#define CLK_EMAC2_BUS 259
#define CLK_EMAC2_REF 260
#define CLK_EMAC2_1588 261
#define CLK_EMAC2_RGMII_TX 262
#define CLK_ESPI_SCLK_SRC 263
#define CLK_ESPI_SCLK 264
#define CLK_ESPI_MCLK 265
#define CLK_CAM_SRC1 266
#define CLK_CAM_SRC2 267
#define CLK_CAM_SRC3 268
#define CLK_CAM_SRC4 269
#define CLK_ISIM_VCLK0 270
#define CLK_ISIM_VCLK1 271
#define CLK_ISIM_VCLK2 272
#define CLK_ISIM_VCLK3 273
#define CLK_HDMA 274
#define CLK_DMA350 275
#define CLK_C2_TCM_PIPE 276
#define CLK_C3_TCM_PIPE 277
#define CLK_RCPU_RT24_CORE0 278
#define CLK_RCPU_RT24_CORE1 279
#define CLK_SEC_UART1 280
#define CLK_SEC_SSP2 281
#define CLK_SEC_TWSI3 282
#define CLK_SEC_RTC 283
#define CLK_SEC_TIMERS0 284
#define CLK_SEC_GPIO 285
#define CLK_VCTCXO_24 286
#define CLK_VCTCXO_3 287
#define CLK_VCTCXO_1 288
#define CLK_PLL1 289
#define OSC_32K 290
#define CLK_DUMMY 291
#define CLK_MAX_NO 292
#endif /* _DT_BINDINGS_CLK_SPACEMIT_K3_H_ */