summaryrefslogtreecommitdiffstats
path: root/drivers/clk/renesas
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 17:35:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 17:39:31 +0000
commit85c675d0d09a45a135bddd15d7b385f8758c32fb (patch)
tree76267dbc9b9a130337be3640948fe397b04ac629 /drivers/clk/renesas
parentAdding upstream version 6.6.15. (diff)
downloadlinux-85c675d0d09a45a135bddd15d7b385f8758c32fb.tar.xz
linux-85c675d0d09a45a135bddd15d7b385f8758c32fb.zip
Adding upstream version 6.7.7.upstream/6.7.7
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/clk/renesas')
-rw-r--r--drivers/clk/renesas/Kconfig7
-rw-r--r--drivers/clk/renesas/Makefile1
-rw-r--r--drivers/clk/renesas/r8a7795-cpg-mssr.c4
-rw-r--r--drivers/clk/renesas/r9a06g032-clocks.c69
-rw-r--r--drivers/clk/renesas/r9a07g043-cpg.c19
-rw-r--r--drivers/clk/renesas/r9a07g044-cpg.c19
-rw-r--r--drivers/clk/renesas/r9a08g045-cpg.c248
-rw-r--r--drivers/clk/renesas/rzg2l-cpg.c435
-rw-r--r--drivers/clk/renesas/rzg2l-cpg.h39
9 files changed, 728 insertions, 113 deletions
diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig
index 37632a0659..69396e1979 100644
--- a/drivers/clk/renesas/Kconfig
+++ b/drivers/clk/renesas/Kconfig
@@ -37,6 +37,7 @@ config CLK_RENESAS
select CLK_R9A07G043 if ARCH_R9A07G043
select CLK_R9A07G044 if ARCH_R9A07G044
select CLK_R9A07G054 if ARCH_R9A07G054
+ select CLK_R9A08G045 if ARCH_R9A08G045
select CLK_R9A09G011 if ARCH_R9A09G011
select CLK_SH73A0 if ARCH_SH73A0
@@ -179,6 +180,10 @@ config CLK_R9A07G054
bool "RZ/V2L clock support" if COMPILE_TEST
select CLK_RZG2L
+config CLK_R9A08G045
+ bool "RZ/G3S clock support" if COMPILE_TEST
+ select CLK_RZG2L
+
config CLK_R9A09G011
bool "RZ/V2M clock support" if COMPILE_TEST
select CLK_RZG2L
@@ -215,7 +220,7 @@ config CLK_RCAR_USB2_CLOCK_SEL
This is a driver for R-Car USB2 clock selector
config CLK_RZG2L
- bool "Renesas RZ/{G2L,G2UL,V2L} family clock support" if COMPILE_TEST
+ bool "Renesas RZ/{G2L,G2UL,G3S,V2L} family clock support" if COMPILE_TEST
select RESET_CONTROLLER
# Generic
diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile
index de907623fe..879a07d445 100644
--- a/drivers/clk/renesas/Makefile
+++ b/drivers/clk/renesas/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_CLK_R9A06G032) += r9a06g032-clocks.o
obj-$(CONFIG_CLK_R9A07G043) += r9a07g043-cpg.o
obj-$(CONFIG_CLK_R9A07G044) += r9a07g044-cpg.o
obj-$(CONFIG_CLK_R9A07G054) += r9a07g044-cpg.o
+obj-$(CONFIG_CLK_R9A08G045) += r9a08g045-cpg.o
obj-$(CONFIG_CLK_R9A09G011) += r9a09g011-cpg.o
obj-$(CONFIG_CLK_SH73A0) += clk-sh73a0.o
diff --git a/drivers/clk/renesas/r8a7795-cpg-mssr.c b/drivers/clk/renesas/r8a7795-cpg-mssr.c
index ad20b3301e..e47d9b1fcc 100644
--- a/drivers/clk/renesas/r8a7795-cpg-mssr.c
+++ b/drivers/clk/renesas/r8a7795-cpg-mssr.c
@@ -51,7 +51,7 @@ enum clk_ids {
MOD_CLK_BASE
};
-static struct cpg_core_clk r8a7795_core_clks[] __initdata = {
+static const struct cpg_core_clk r8a7795_core_clks[] __initconst = {
/* External Clock Inputs */
DEF_INPUT("extal", CLK_EXTAL),
DEF_INPUT("extalr", CLK_EXTALR),
@@ -128,7 +128,7 @@ static struct cpg_core_clk r8a7795_core_clks[] __initdata = {
DEF_BASE("r", R8A7795_CLK_R, CLK_TYPE_GEN3_R, CLK_RINT),
};
-static struct mssr_mod_clk r8a7795_mod_clks[] __initdata = {
+static const struct mssr_mod_clk r8a7795_mod_clks[] __initconst = {
DEF_MOD("3dge", 112, R8A7795_CLK_ZG),
DEF_MOD("fdp1-1", 118, R8A7795_CLK_S0D1),
DEF_MOD("fdp1-0", 119, R8A7795_CLK_S0D1),
diff --git a/drivers/clk/renesas/r9a06g032-clocks.c b/drivers/clk/renesas/r9a06g032-clocks.c
index 55db63c704..c1348e2d45 100644
--- a/drivers/clk/renesas/r9a06g032-clocks.c
+++ b/drivers/clk/renesas/r9a06g032-clocks.c
@@ -102,19 +102,22 @@ enum gate_type {
* @source: the ID+1 of the parent clock element.
* Root clock uses ID of ~0 (PARENT_ID);
* @gate: clock enable/disable
- * @div_min: smallest permitted clock divider
- * @div_max: largest permitted clock divider
- * @reg: clock divider register offset, in 32-bit words
- * @div_table: optional list of fixed clock divider values;
+ * @div: substructure for clock divider
+ * @div.min: smallest permitted clock divider
+ * @div.max: largest permitted clock divider
+ * @div.reg: clock divider register offset, in 32-bit words
+ * @div.table: optional list of fixed clock divider values;
* must be in ascending order, zero for unused
- * @div: divisor for fixed-factor clock
- * @mul: multiplier for fixed-factor clock
- * @group: UART group, 0=UART0/1/2, 1=UART3/4/5/6/7
- * @sel: select either g1/r1 or g2/r2 as clock source
- * @g1: 1st source gate (clock enable/disable)
- * @r1: 1st source reset (module reset)
- * @g2: 2nd source gate (clock enable/disable)
- * @r2: 2nd source reset (module reset)
+ * @ffc: substructure for fixed-factor clocks
+ * @ffc.div: divisor for fixed-factor clock
+ * @ffc.mul: multiplier for fixed-factor clock
+ * @dual: substructure for dual clock gates
+ * @dual.group: UART group, 0=UART0/1/2, 1=UART3/4/5/6/7
+ * @dual.sel: select either g1/r1 or g2/r2 as clock source
+ * @dual.g1: 1st source gate (clock enable/disable)
+ * @dual.r1: 1st source reset (module reset)
+ * @dual.g2: 2nd source gate (clock enable/disable)
+ * @dual.r2: 2nd source reset (module reset)
*
* Describes a single element in the clock tree hierarchy.
* As there are quite a large number of clock elements, this
@@ -131,13 +134,13 @@ struct r9a06g032_clkdesc {
struct r9a06g032_gate gate;
/* type = K_DIV */
struct {
- unsigned int div_min:10, div_max:10, reg:10;
- u16 div_table[4];
- };
+ unsigned int min:10, max:10, reg:10;
+ u16 table[4];
+ } div;
/* type = K_FFC */
struct {
u16 div, mul;
- };
+ } ffc;
/* type = K_DUALGATE */
struct {
uint16_t group:1;
@@ -178,26 +181,26 @@ struct r9a06g032_clkdesc {
.type = K_FFC, \
.index = R9A06G032_##_idx, \
.name = _n, \
- .div = _div, \
- .mul = _mul \
+ .ffc.div = _div, \
+ .ffc.mul = _mul \
}
#define D_FFC(_idx, _n, _src, _div) { \
.type = K_FFC, \
.index = R9A06G032_##_idx, \
.source = 1 + R9A06G032_##_src, \
.name = _n, \
- .div = _div, \
- .mul = 1 \
+ .ffc.div = _div, \
+ .ffc.mul = 1 \
}
#define D_DIV(_idx, _n, _src, _reg, _min, _max, ...) { \
.type = K_DIV, \
.index = R9A06G032_##_idx, \
.source = 1 + R9A06G032_##_src, \
.name = _n, \
- .reg = _reg, \
- .div_min = _min, \
- .div_max = _max, \
- .div_table = { __VA_ARGS__ } \
+ .div.reg = _reg, \
+ .div.min = _min, \
+ .div.max = _max, \
+ .div.table = { __VA_ARGS__ } \
}
#define D_UGATE(_idx, _n, _src, _g, _g1, _r1, _g2, _r2) { \
.type = K_DUALGATE, \
@@ -1063,14 +1066,14 @@ r9a06g032_register_div(struct r9a06g032_priv *clocks,
div->clocks = clocks;
div->index = desc->index;
- div->reg = desc->reg;
+ div->reg = desc->div.reg;
div->hw.init = &init;
- div->min = desc->div_min;
- div->max = desc->div_max;
+ div->min = desc->div.min;
+ div->max = desc->div.max;
/* populate (optional) divider table fixed values */
for (i = 0; i < ARRAY_SIZE(div->table) &&
- i < ARRAY_SIZE(desc->div_table) && desc->div_table[i]; i++) {
- div->table[div->table_size++] = desc->div_table[i];
+ i < ARRAY_SIZE(desc->div.table) && desc->div.table[i]; i++) {
+ div->table[div->table_size++] = desc->div.table[i];
}
clk = clk_register(NULL, &div->hw);
@@ -1269,11 +1272,10 @@ static void r9a06g032_clocks_del_clk_provider(void *data)
static void __init r9a06g032_init_h2mode(struct r9a06g032_priv *clocks)
{
- struct device_node *usbf_np = NULL;
+ struct device_node *usbf_np;
u32 usb;
- while ((usbf_np = of_find_compatible_node(usbf_np, NULL,
- "renesas,rzn1-usbf"))) {
+ for_each_compatible_node(usbf_np, NULL, "renesas,rzn1-usbf") {
if (of_device_is_available(usbf_np))
break;
}
@@ -1333,7 +1335,8 @@ static int __init r9a06g032_clocks_probe(struct platform_device *pdev)
case K_FFC:
clk = clk_register_fixed_factor(NULL, d->name,
parent_name, 0,
- d->mul, d->div);
+ d->ffc.mul,
+ d->ffc.div);
break;
case K_GATE:
clk = r9a06g032_register_gate(clocks, parent_name, d);
diff --git a/drivers/clk/renesas/r9a07g043-cpg.c b/drivers/clk/renesas/r9a07g043-cpg.c
index 1a7a6d60ac..b70bb378ab 100644
--- a/drivers/clk/renesas/r9a07g043-cpg.c
+++ b/drivers/clk/renesas/r9a07g043-cpg.c
@@ -14,6 +14,17 @@
#include "rzg2l-cpg.h"
+/* Specific registers. */
+#define CPG_PL2SDHI_DSEL (0x218)
+
+/* Clock select configuration. */
+#define SEL_SDHI0 SEL_PLL_PACK(CPG_PL2SDHI_DSEL, 0, 2)
+#define SEL_SDHI1 SEL_PLL_PACK(CPG_PL2SDHI_DSEL, 4, 2)
+
+/* Clock status configuration. */
+#define SEL_SDHI0_STS SEL_PLL_PACK(CPG_CLKSTATUS, 28, 1)
+#define SEL_SDHI1_STS SEL_PLL_PACK(CPG_CLKSTATUS, 29, 1)
+
enum clk_ids {
/* Core Clock Outputs exported to DT */
LAST_DT_CORE_CLK = R9A07G043_CLK_P0_DIV2,
@@ -78,6 +89,8 @@ static const char * const sel_pll3_3[] = { ".pll3_533", ".pll3_400" };
static const char * const sel_pll6_2[] = { ".pll6_250", ".pll5_250" };
static const char * const sel_shdi[] = { ".clk_533", ".clk_400", ".clk_266" };
+static const u32 mtable_sdhi[] = { 1, 2, 3 };
+
static const struct cpg_core_clk r9a07g043_core_clks[] __initconst = {
/* External Clock Inputs */
DEF_INPUT("extal", CLK_EXTAL),
@@ -123,8 +136,10 @@ static const struct cpg_core_clk r9a07g043_core_clks[] __initconst = {
DEF_MUX("HP", R9A07G043_CLK_HP, SEL_PLL6_2, sel_pll6_2),
DEF_FIXED("SPI0", R9A07G043_CLK_SPI0, CLK_DIV_PLL3_C, 1, 2),
DEF_FIXED("SPI1", R9A07G043_CLK_SPI1, CLK_DIV_PLL3_C, 1, 4),
- DEF_SD_MUX("SD0", R9A07G043_CLK_SD0, SEL_SDHI0, sel_shdi),
- DEF_SD_MUX("SD1", R9A07G043_CLK_SD1, SEL_SDHI1, sel_shdi),
+ DEF_SD_MUX("SD0", R9A07G043_CLK_SD0, SEL_SDHI0, SEL_SDHI0_STS, sel_shdi,
+ mtable_sdhi, 0, rzg2l_cpg_sd_clk_mux_notifier),
+ DEF_SD_MUX("SD1", R9A07G043_CLK_SD1, SEL_SDHI1, SEL_SDHI0_STS, sel_shdi,
+ mtable_sdhi, 0, rzg2l_cpg_sd_clk_mux_notifier),
DEF_FIXED("SD0_DIV4", CLK_SD0_DIV4, R9A07G043_CLK_SD0, 1, 4),
DEF_FIXED("SD1_DIV4", CLK_SD1_DIV4, R9A07G043_CLK_SD1, 1, 4),
};
diff --git a/drivers/clk/renesas/r9a07g044-cpg.c b/drivers/clk/renesas/r9a07g044-cpg.c
index c597414a94..1047278c90 100644
--- a/drivers/clk/renesas/r9a07g044-cpg.c
+++ b/drivers/clk/renesas/r9a07g044-cpg.c
@@ -15,6 +15,17 @@
#include "rzg2l-cpg.h"
+/* Specific registers. */
+#define CPG_PL2SDHI_DSEL (0x218)
+
+/* Clock select configuration. */
+#define SEL_SDHI0 SEL_PLL_PACK(CPG_PL2SDHI_DSEL, 0, 2)
+#define SEL_SDHI1 SEL_PLL_PACK(CPG_PL2SDHI_DSEL, 4, 2)
+
+/* Clock status configuration. */
+#define SEL_SDHI0_STS SEL_PLL_PACK(CPG_CLKSTATUS, 28, 1)
+#define SEL_SDHI1_STS SEL_PLL_PACK(CPG_CLKSTATUS, 29, 1)
+
enum clk_ids {
/* Core Clock Outputs exported to DT */
LAST_DT_CORE_CLK = R9A07G054_CLK_DRP_A,
@@ -98,6 +109,8 @@ static const char * const sel_pll6_2[] = { ".pll6_250", ".pll5_250" };
static const char * const sel_shdi[] = { ".clk_533", ".clk_400", ".clk_266" };
static const char * const sel_gpu2[] = { ".pll6", ".pll3_div2_2" };
+static const u32 mtable_sdhi[] = { 1, 2, 3 };
+
static const struct {
struct cpg_core_clk common[56];
#ifdef CONFIG_CLK_R9A07G054
@@ -163,8 +176,10 @@ static const struct {
DEF_MUX("HP", R9A07G044_CLK_HP, SEL_PLL6_2, sel_pll6_2),
DEF_FIXED("SPI0", R9A07G044_CLK_SPI0, CLK_DIV_PLL3_C, 1, 2),
DEF_FIXED("SPI1", R9A07G044_CLK_SPI1, CLK_DIV_PLL3_C, 1, 4),
- DEF_SD_MUX("SD0", R9A07G044_CLK_SD0, SEL_SDHI0, sel_shdi),
- DEF_SD_MUX("SD1", R9A07G044_CLK_SD1, SEL_SDHI1, sel_shdi),
+ DEF_SD_MUX("SD0", R9A07G044_CLK_SD0, SEL_SDHI0, SEL_SDHI0_STS, sel_shdi,
+ mtable_sdhi, 0, rzg2l_cpg_sd_clk_mux_notifier),
+ DEF_SD_MUX("SD1", R9A07G044_CLK_SD1, SEL_SDHI1, SEL_SDHI0_STS, sel_shdi,
+ mtable_sdhi, 0, rzg2l_cpg_sd_clk_mux_notifier),
DEF_FIXED("SD0_DIV4", CLK_SD0_DIV4, R9A07G044_CLK_SD0, 1, 4),
DEF_FIXED("SD1_DIV4", CLK_SD1_DIV4, R9A07G044_CLK_SD1, 1, 4),
DEF_DIV("G", R9A07G044_CLK_G, CLK_SEL_GPU2, DIVGPU, dtable_1_8),
diff --git a/drivers/clk/renesas/r9a08g045-cpg.c b/drivers/clk/renesas/r9a08g045-cpg.c
new file mode 100644
index 0000000000..4394cb241d
--- /dev/null
+++ b/drivers/clk/renesas/r9a08g045-cpg.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RZ/G3S CPG driver
+ *
+ * Copyright (C) 2023 Renesas Electronics Corp.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+#include <dt-bindings/clock/r9a08g045-cpg.h>
+
+#include "rzg2l-cpg.h"
+
+/* RZ/G3S Specific registers. */
+#define G3S_CPG_PL2_DDIV (0x204)
+#define G3S_CPG_SDHI_DDIV (0x218)
+#define G3S_CPG_PLL_DSEL (0x240)
+#define G3S_CPG_SDHI_DSEL (0x244)
+#define G3S_CLKDIVSTATUS (0x280)
+#define G3S_CLKSELSTATUS (0x284)
+
+/* RZ/G3S Specific division configuration. */
+#define G3S_DIVPL2B DDIV_PACK(G3S_CPG_PL2_DDIV, 4, 3)
+#define G3S_DIV_SDHI0 DDIV_PACK(G3S_CPG_SDHI_DDIV, 0, 1)
+#define G3S_DIV_SDHI1 DDIV_PACK(G3S_CPG_SDHI_DDIV, 4, 1)
+#define G3S_DIV_SDHI2 DDIV_PACK(G3S_CPG_SDHI_DDIV, 8, 1)
+
+/* RZ/G3S Clock status configuration. */
+#define G3S_DIVPL1A_STS DDIV_PACK(G3S_CLKDIVSTATUS, 0, 1)
+#define G3S_DIVPL2B_STS DDIV_PACK(G3S_CLKDIVSTATUS, 5, 1)
+#define G3S_DIVPL3A_STS DDIV_PACK(G3S_CLKDIVSTATUS, 8, 1)
+#define G3S_DIVPL3B_STS DDIV_PACK(G3S_CLKDIVSTATUS, 9, 1)
+#define G3S_DIVPL3C_STS DDIV_PACK(G3S_CLKDIVSTATUS, 10, 1)
+#define G3S_DIV_SDHI0_STS DDIV_PACK(G3S_CLKDIVSTATUS, 24, 1)
+#define G3S_DIV_SDHI1_STS DDIV_PACK(G3S_CLKDIVSTATUS, 25, 1)
+#define G3S_DIV_SDHI2_STS DDIV_PACK(G3S_CLKDIVSTATUS, 26, 1)
+
+#define G3S_SEL_PLL4_STS SEL_PLL_PACK(G3S_CLKSELSTATUS, 6, 1)
+#define G3S_SEL_SDHI0_STS SEL_PLL_PACK(G3S_CLKSELSTATUS, 16, 1)
+#define G3S_SEL_SDHI1_STS SEL_PLL_PACK(G3S_CLKSELSTATUS, 17, 1)
+#define G3S_SEL_SDHI2_STS SEL_PLL_PACK(G3S_CLKSELSTATUS, 18, 1)
+
+/* RZ/G3S Specific clocks select. */
+#define G3S_SEL_PLL4 SEL_PLL_PACK(G3S_CPG_PLL_DSEL, 6, 1)
+#define G3S_SEL_SDHI0 SEL_PLL_PACK(G3S_CPG_SDHI_DSEL, 0, 2)
+#define G3S_SEL_SDHI1 SEL_PLL_PACK(G3S_CPG_SDHI_DSEL, 4, 2)
+#define G3S_SEL_SDHI2 SEL_PLL_PACK(G3S_CPG_SDHI_DSEL, 8, 2)
+
+/* PLL 1/4/6 configuration registers macro. */
+#define G3S_PLL146_CONF(clk1, clk2) ((clk1) << 22 | (clk2) << 12)
+
+#define DEF_G3S_MUX(_name, _id, _conf, _parent_names, _mux_flags, _clk_flags) \
+ DEF_TYPE(_name, _id, CLK_TYPE_MUX, .conf = (_conf), \
+ .parent_names = (_parent_names), \
+ .num_parents = ARRAY_SIZE((_parent_names)), \
+ .mux_flags = CLK_MUX_HIWORD_MASK | (_mux_flags), \
+ .flag = (_clk_flags))
+
+enum clk_ids {
+ /* Core Clock Outputs exported to DT */
+ LAST_DT_CORE_CLK = R9A08G045_SWD,
+
+ /* External Input Clocks */
+ CLK_EXTAL,
+
+ /* Internal Core Clocks */
+ CLK_OSC_DIV1000,
+ CLK_PLL1,
+ CLK_PLL2,
+ CLK_PLL2_DIV2,
+ CLK_PLL2_DIV2_8,
+ CLK_PLL2_DIV6,
+ CLK_PLL3,
+ CLK_PLL3_DIV2,
+ CLK_PLL3_DIV2_4,
+ CLK_PLL3_DIV2_8,
+ CLK_PLL3_DIV6,
+ CLK_PLL4,
+ CLK_PLL6,
+ CLK_PLL6_DIV2,
+ CLK_SEL_SDHI0,
+ CLK_SEL_SDHI1,
+ CLK_SEL_SDHI2,
+ CLK_SEL_PLL4,
+ CLK_P1_DIV2,
+ CLK_P3_DIV2,
+ CLK_SD0_DIV4,
+ CLK_SD1_DIV4,
+ CLK_SD2_DIV4,
+
+ /* Module Clocks */
+ MOD_CLK_BASE,
+};
+
+/* Divider tables */
+static const struct clk_div_table dtable_1_2[] = {
+ { 0, 1 },
+ { 1, 2 },
+ { 0, 0 },
+};
+
+static const struct clk_div_table dtable_1_8[] = {
+ { 0, 1 },
+ { 1, 2 },
+ { 2, 4 },
+ { 3, 8 },
+ { 0, 0 },
+};
+
+static const struct clk_div_table dtable_1_32[] = {
+ { 0, 1 },
+ { 1, 2 },
+ { 2, 4 },
+ { 3, 8 },
+ { 4, 32 },
+ { 0, 0 },
+};
+
+/* Mux clock names tables. */
+static const char * const sel_sdhi[] = { ".pll2_div2", ".pll6", ".pll2_div6" };
+static const char * const sel_pll4[] = { ".osc_div1000", ".pll4" };
+
+/* Mux clock indices tables. */
+static const u32 mtable_sd[] = { 0, 2, 3 };
+static const u32 mtable_pll4[] = { 0, 1 };
+
+static const struct cpg_core_clk r9a08g045_core_clks[] __initconst = {
+ /* External Clock Inputs */
+ DEF_INPUT("extal", CLK_EXTAL),
+
+ /* Internal Core Clocks */
+ DEF_FIXED(".osc_div1000", CLK_OSC_DIV1000, CLK_EXTAL, 1, 1000),
+ DEF_G3S_PLL(".pll1", CLK_PLL1, CLK_EXTAL, G3S_PLL146_CONF(0x4, 0x8)),
+ DEF_FIXED(".pll2", CLK_PLL2, CLK_EXTAL, 200, 3),
+ DEF_FIXED(".pll3", CLK_PLL3, CLK_EXTAL, 200, 3),
+ DEF_FIXED(".pll4", CLK_PLL4, CLK_EXTAL, 100, 3),
+ DEF_FIXED(".pll6", CLK_PLL6, CLK_EXTAL, 125, 6),
+ DEF_FIXED(".pll2_div2", CLK_PLL2_DIV2, CLK_PLL2, 1, 2),
+ DEF_FIXED(".pll2_div2_8", CLK_PLL2_DIV2_8, CLK_PLL2_DIV2, 1, 8),
+ DEF_FIXED(".pll2_div6", CLK_PLL2_DIV6, CLK_PLL2, 1, 6),
+ DEF_FIXED(".pll3_div2", CLK_PLL3_DIV2, CLK_PLL3, 1, 2),
+ DEF_FIXED(".pll3_div2_4", CLK_PLL3_DIV2_4, CLK_PLL3_DIV2, 1, 4),
+ DEF_FIXED(".pll3_div2_8", CLK_PLL3_DIV2_8, CLK_PLL3_DIV2, 1, 8),
+ DEF_FIXED(".pll3_div6", CLK_PLL3_DIV6, CLK_PLL3, 1, 6),
+ DEF_FIXED(".pll6_div2", CLK_PLL6_DIV2, CLK_PLL6, 1, 2),
+ DEF_SD_MUX(".sel_sd0", CLK_SEL_SDHI0, G3S_SEL_SDHI0, G3S_SEL_SDHI0_STS, sel_sdhi,
+ mtable_sd, 0, NULL),
+ DEF_SD_MUX(".sel_sd1", CLK_SEL_SDHI1, G3S_SEL_SDHI1, G3S_SEL_SDHI1_STS, sel_sdhi,
+ mtable_sd, 0, NULL),
+ DEF_SD_MUX(".sel_sd2", CLK_SEL_SDHI2, G3S_SEL_SDHI2, G3S_SEL_SDHI2_STS, sel_sdhi,
+ mtable_sd, 0, NULL),
+ DEF_SD_MUX(".sel_pll4", CLK_SEL_PLL4, G3S_SEL_PLL4, G3S_SEL_PLL4_STS, sel_pll4,
+ mtable_pll4, CLK_SET_PARENT_GATE, NULL),
+
+ /* Core output clk */
+ DEF_G3S_DIV("I", R9A08G045_CLK_I, CLK_PLL1, DIVPL1A, G3S_DIVPL1A_STS, dtable_1_8,
+ 0, 0, 0, NULL),
+ DEF_G3S_DIV("P0", R9A08G045_CLK_P0, CLK_PLL2_DIV2_8, G3S_DIVPL2B, G3S_DIVPL2B_STS,
+ dtable_1_32, 0, 0, 0, NULL),
+ DEF_G3S_DIV("SD0", R9A08G045_CLK_SD0, CLK_SEL_SDHI0, G3S_DIV_SDHI0, G3S_DIV_SDHI0_STS,
+ dtable_1_2, 800000000UL, 500000000UL, CLK_SET_RATE_PARENT,
+ rzg3s_cpg_div_clk_notifier),
+ DEF_G3S_DIV("SD1", R9A08G045_CLK_SD1, CLK_SEL_SDHI1, G3S_DIV_SDHI1, G3S_DIV_SDHI1_STS,
+ dtable_1_2, 800000000UL, 500000000UL, CLK_SET_RATE_PARENT,
+ rzg3s_cpg_div_clk_notifier),
+ DEF_G3S_DIV("SD2", R9A08G045_CLK_SD2, CLK_SEL_SDHI2, G3S_DIV_SDHI2, G3S_DIV_SDHI2_STS,
+ dtable_1_2, 800000000UL, 500000000UL, CLK_SET_RATE_PARENT,
+ rzg3s_cpg_div_clk_notifier),
+ DEF_FIXED(".sd0_div4", CLK_SD0_DIV4, R9A08G045_CLK_SD0, 1, 4),
+ DEF_FIXED(".sd1_div4", CLK_SD1_DIV4, R9A08G045_CLK_SD1, 1, 4),
+ DEF_FIXED(".sd2_div4", CLK_SD2_DIV4, R9A08G045_CLK_SD2, 1, 4),
+ DEF_FIXED("M0", R9A08G045_CLK_M0, CLK_PLL3_DIV2_4, 1, 1),
+ DEF_G3S_DIV("P1", R9A08G045_CLK_P1, CLK_PLL3_DIV2_4, DIVPL3A, G3S_DIVPL3A_STS,
+ dtable_1_32, 0, 0, 0, NULL),
+ DEF_FIXED("P1_DIV2", CLK_P1_DIV2, R9A08G045_CLK_P1, 1, 2),
+ DEF_G3S_DIV("P2", R9A08G045_CLK_P2, CLK_PLL3_DIV2_8, DIVPL3B, G3S_DIVPL3B_STS,
+ dtable_1_32, 0, 0, 0, NULL),
+ DEF_G3S_DIV("P3", R9A08G045_CLK_P3, CLK_PLL3_DIV2_4, DIVPL3C, G3S_DIVPL3C_STS,
+ dtable_1_32, 0, 0, 0, NULL),
+ DEF_FIXED("P3_DIV2", CLK_P3_DIV2, R9A08G045_CLK_P3, 1, 2),
+ DEF_FIXED("S0", R9A08G045_CLK_S0, CLK_SEL_PLL4, 1, 2),
+ DEF_FIXED("OSC", R9A08G045_OSCCLK, CLK_EXTAL, 1, 1),
+ DEF_FIXED("OSC2", R9A08G045_OSCCLK2, CLK_EXTAL, 1, 3),
+};
+
+static const struct rzg2l_mod_clk r9a08g045_mod_clks[] = {
+ DEF_MOD("gic_gicclk", R9A08G045_GIC600_GICCLK, R9A08G045_CLK_P1, 0x514, 0),
+ DEF_MOD("ia55_clk", R9A08G045_IA55_CLK, R9A08G045_CLK_P1, 0x518, 1),
+ DEF_MOD("dmac_aclk", R9A08G045_DMAC_ACLK, R9A08G045_CLK_P3, 0x52c, 0),
+ DEF_MOD("sdhi0_imclk", R9A08G045_SDHI0_IMCLK, CLK_SD0_DIV4, 0x554, 0),
+ DEF_MOD("sdhi0_imclk2", R9A08G045_SDHI0_IMCLK2, CLK_SD0_DIV4, 0x554, 1),
+ DEF_MOD("sdhi0_clk_hs", R9A08G045_SDHI0_CLK_HS, R9A08G045_CLK_SD0, 0x554, 2),
+ DEF_MOD("sdhi0_aclk", R9A08G045_SDHI0_ACLK, R9A08G045_CLK_P1, 0x554, 3),
+ DEF_MOD("sdhi1_imclk", R9A08G045_SDHI1_IMCLK, CLK_SD1_DIV4, 0x554, 4),
+ DEF_MOD("sdhi1_imclk2", R9A08G045_SDHI1_IMCLK2, CLK_SD1_DIV4, 0x554, 5),
+ DEF_MOD("sdhi1_clk_hs", R9A08G045_SDHI1_CLK_HS, R9A08G045_CLK_SD1, 0x554, 6),
+ DEF_MOD("sdhi1_aclk", R9A08G045_SDHI1_ACLK, R9A08G045_CLK_P1, 0x554, 7),
+ DEF_MOD("sdhi2_imclk", R9A08G045_SDHI2_IMCLK, CLK_SD2_DIV4, 0x554, 8),
+ DEF_MOD("sdhi2_imclk2", R9A08G045_SDHI2_IMCLK2, CLK_SD2_DIV4, 0x554, 9),
+ DEF_MOD("sdhi2_clk_hs", R9A08G045_SDHI2_CLK_HS, R9A08G045_CLK_SD2, 0x554, 10),
+ DEF_MOD("sdhi2_aclk", R9A08G045_SDHI2_ACLK, R9A08G045_CLK_P1, 0x554, 11),
+ DEF_MOD("scif0_clk_pck", R9A08G045_SCIF0_CLK_PCK, R9A08G045_CLK_P0, 0x584, 0),
+ DEF_MOD("gpio_hclk", R9A08G045_GPIO_HCLK, R9A08G045_OSCCLK, 0x598, 0),
+};
+
+static const struct rzg2l_reset r9a08g045_resets[] = {
+ DEF_RST(R9A08G045_GIC600_GICRESET_N, 0x814, 0),
+ DEF_RST(R9A08G045_GIC600_DBG_GICRESET_N, 0x814, 1),
+ DEF_RST(R9A08G045_SDHI0_IXRST, 0x854, 0),
+ DEF_RST(R9A08G045_SDHI1_IXRST, 0x854, 1),
+ DEF_RST(R9A08G045_SDHI2_IXRST, 0x854, 2),
+ DEF_RST(R9A08G045_SCIF0_RST_SYSTEM_N, 0x884, 0),
+ DEF_RST(R9A08G045_GPIO_RSTN, 0x898, 0),
+ DEF_RST(R9A08G045_GPIO_PORT_RESETN, 0x898, 1),
+ DEF_RST(R9A08G045_GPIO_SPARE_RESETN, 0x898, 2),
+};
+
+static const unsigned int r9a08g045_crit_mod_clks[] __initconst = {
+ MOD_CLK_BASE + R9A08G045_GIC600_GICCLK,
+ MOD_CLK_BASE + R9A08G045_IA55_CLK,
+ MOD_CLK_BASE + R9A08G045_DMAC_ACLK,
+};
+
+const struct rzg2l_cpg_info r9a08g045_cpg_info = {
+ /* Core Clocks */
+ .core_clks = r9a08g045_core_clks,
+ .num_core_clks = ARRAY_SIZE(r9a08g045_core_clks),
+ .last_dt_core_clk = LAST_DT_CORE_CLK,
+ .num_total_core_clks = MOD_CLK_BASE,
+
+ /* Critical Module Clocks */
+ .crit_mod_clks = r9a08g045_crit_mod_clks,
+ .num_crit_mod_clks = ARRAY_SIZE(r9a08g045_crit_mod_clks),
+
+ /* Module Clocks */
+ .mod_clks = r9a08g045_mod_clks,
+ .num_mod_clks = ARRAY_SIZE(r9a08g045_mod_clks),
+ .num_hw_mod_clks = R9A08G045_VBAT_BCLK + 1,
+
+ /* Resets */
+ .resets = r9a08g045_resets,
+ .num_resets = R9A08G045_VBAT_BRESETN + 1, /* Last reset ID + 1 */
+
+ .has_clk_mon_regs = true,
+};
diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c
index 75f9eca020..3d2daa4ba2 100644
--- a/drivers/clk/renesas/rzg2l-cpg.c
+++ b/drivers/clk/renesas/rzg2l-cpg.c
@@ -47,6 +47,11 @@
#define PDIV(val) FIELD_GET(GENMASK(5, 0), val)
#define SDIV(val) FIELD_GET(GENMASK(2, 0), val)
+#define RZG3S_DIV_P GENMASK(28, 26)
+#define RZG3S_DIV_M GENMASK(25, 22)
+#define RZG3S_DIV_NI GENMASK(21, 13)
+#define RZG3S_DIV_NF GENMASK(12, 1)
+
#define CLK_ON_R(reg) (reg)
#define CLK_MON_R(reg) (0x180 + (reg))
#define CLK_RST_R(reg) (reg)
@@ -56,15 +61,55 @@
#define GET_REG_SAMPLL_CLK1(val) ((val >> 22) & 0xfff)
#define GET_REG_SAMPLL_CLK2(val) ((val >> 12) & 0xfff)
+#define CPG_WEN_BIT BIT(16)
+
#define MAX_VCLK_FREQ (148500000)
-struct sd_hw_data {
+/**
+ * struct clk_hw_data - clock hardware data
+ * @hw: clock hw
+ * @conf: clock configuration (register offset, shift, width)
+ * @sconf: clock status configuration (register offset, shift, width)
+ * @priv: CPG private data structure
+ */
+struct clk_hw_data {
struct clk_hw hw;
u32 conf;
+ u32 sconf;
struct rzg2l_cpg_priv *priv;
};
-#define to_sd_hw_data(_hw) container_of(_hw, struct sd_hw_data, hw)
+#define to_clk_hw_data(_hw) container_of(_hw, struct clk_hw_data, hw)
+
+/**
+ * struct sd_mux_hw_data - SD MUX clock hardware data
+ * @hw_data: clock hw data
+ * @mtable: clock mux table
+ */
+struct sd_mux_hw_data {
+ struct clk_hw_data hw_data;
+ const u32 *mtable;
+};
+
+#define to_sd_mux_hw_data(_hw) container_of(_hw, struct sd_mux_hw_data, hw_data)
+
+/**
+ * struct div_hw_data - divider clock hardware data
+ * @hw_data: clock hw data
+ * @dtable: pointer to divider table
+ * @invalid_rate: invalid rate for divider
+ * @max_rate: maximum rate for divider
+ * @width: divider width
+ */
+struct div_hw_data {
+ struct clk_hw_data hw_data;
+ const struct clk_div_table *dtable;
+ unsigned long invalid_rate;
+ unsigned long max_rate;
+ u32 width;
+};
+
+#define to_div_hw_data(_hw) container_of(_hw, struct div_hw_data, hw_data)
struct rzg2l_pll5_param {
u32 pl5_fracin;
@@ -121,6 +166,241 @@ static void rzg2l_cpg_del_clk_provider(void *data)
of_clk_del_provider(data);
}
+/* Must be called in atomic context. */
+static int rzg2l_cpg_wait_clk_update_done(void __iomem *base, u32 conf)
+{
+ u32 bitmask = GENMASK(GET_WIDTH(conf) - 1, 0) << GET_SHIFT(conf);
+ u32 off = GET_REG_OFFSET(conf);
+ u32 val;
+
+ return readl_poll_timeout_atomic(base + off, val, !(val & bitmask), 10, 200);
+}
+
+int rzg2l_cpg_sd_clk_mux_notifier(struct notifier_block *nb, unsigned long event,
+ void *data)
+{
+ struct clk_notifier_data *cnd = data;
+ struct clk_hw *hw = __clk_get_hw(cnd->clk);
+ struct clk_hw_data *clk_hw_data = to_clk_hw_data(hw);
+ struct rzg2l_cpg_priv *priv = clk_hw_data->priv;
+ u32 off = GET_REG_OFFSET(clk_hw_data->conf);
+ u32 shift = GET_SHIFT(clk_hw_data->conf);
+ const u32 clk_src_266 = 3;
+ unsigned long flags;
+ int ret;
+
+ if (event != PRE_RATE_CHANGE || (cnd->new_rate / MEGA == 266))
+ return NOTIFY_DONE;
+
+ spin_lock_irqsave(&priv->rmw_lock, flags);
+
+ /*
+ * As per the HW manual, we should not directly switch from 533 MHz to
+ * 400 MHz and vice versa. To change the setting from 2’b01 (533 MHz)
+ * to 2’b10 (400 MHz) or vice versa, Switch to 2’b11 (266 MHz) first,
+ * and then switch to the target setting (2’b01 (533 MHz) or 2’b10
+ * (400 MHz)).
+ * Setting a value of '0' to the SEL_SDHI0_SET or SEL_SDHI1_SET clock
+ * switching register is prohibited.
+ * The clock mux has 3 input clocks(533 MHz, 400 MHz, and 266 MHz), and
+ * the index to value mapping is done by adding 1 to the index.
+ */
+
+ writel((CPG_WEN_BIT | clk_src_266) << shift, priv->base + off);
+
+ /* Wait for the update done. */
+ ret = rzg2l_cpg_wait_clk_update_done(priv->base, clk_hw_data->sconf);
+
+ spin_unlock_irqrestore(&priv->rmw_lock, flags);
+
+ if (ret)
+ dev_err(priv->dev, "failed to switch to safe clk source\n");
+
+ return notifier_from_errno(ret);
+}
+
+int rzg3s_cpg_div_clk_notifier(struct notifier_block *nb, unsigned long event,
+ void *data)
+{
+ struct clk_notifier_data *cnd = data;
+ struct clk_hw *hw = __clk_get_hw(cnd->clk);
+ struct clk_hw_data *clk_hw_data = to_clk_hw_data(hw);
+ struct div_hw_data *div_hw_data = to_div_hw_data(clk_hw_data);
+ struct rzg2l_cpg_priv *priv = clk_hw_data->priv;
+ u32 off = GET_REG_OFFSET(clk_hw_data->conf);
+ u32 shift = GET_SHIFT(clk_hw_data->conf);
+ unsigned long flags;
+ int ret = 0;
+ u32 val;
+
+ if (event != PRE_RATE_CHANGE || !div_hw_data->invalid_rate ||
+ div_hw_data->invalid_rate % cnd->new_rate)
+ return NOTIFY_DONE;
+
+ spin_lock_irqsave(&priv->rmw_lock, flags);
+
+ val = readl(priv->base + off);
+ val >>= shift;
+ val &= GENMASK(GET_WIDTH(clk_hw_data->conf) - 1, 0);
+
+ /*
+ * There are different constraints for the user of this notifiers as follows:
+ * 1/ SD div cannot be 1 (val == 0) if parent rate is 800MHz
+ * 2/ OCTA / SPI div cannot be 1 (val == 0) if parent rate is 400MHz
+ * As SD can have only one parent having 800MHz and OCTA div can have
+ * only one parent having 400MHz we took into account the parent rate
+ * at the beginning of function (by checking invalid_rate % new_rate).
+ * Now it is time to check the hardware divider and update it accordingly.
+ */
+ if (!val) {
+ writel((CPG_WEN_BIT | 1) << shift, priv->base + off);
+ /* Wait for the update done. */
+ ret = rzg2l_cpg_wait_clk_update_done(priv->base, clk_hw_data->sconf);
+ }
+
+ spin_unlock_irqrestore(&priv->rmw_lock, flags);
+
+ if (ret)
+ dev_err(priv->dev, "Failed to downgrade the div\n");
+
+ return notifier_from_errno(ret);
+}
+
+static int rzg2l_register_notifier(struct clk_hw *hw, const struct cpg_core_clk *core,
+ struct rzg2l_cpg_priv *priv)
+{
+ struct notifier_block *nb;
+
+ if (!core->notifier)
+ return 0;
+
+ nb = devm_kzalloc(priv->dev, sizeof(*nb), GFP_KERNEL);
+ if (!nb)
+ return -ENOMEM;
+
+ nb->notifier_call = core->notifier;
+
+ return clk_notifier_register(hw->clk, nb);
+}
+
+static unsigned long rzg3s_div_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_hw_data *clk_hw_data = to_clk_hw_data(hw);
+ struct div_hw_data *div_hw_data = to_div_hw_data(clk_hw_data);
+ struct rzg2l_cpg_priv *priv = clk_hw_data->priv;
+ u32 val;
+
+ val = readl(priv->base + GET_REG_OFFSET(clk_hw_data->conf));
+ val >>= GET_SHIFT(clk_hw_data->conf);
+ val &= GENMASK(GET_WIDTH(clk_hw_data->conf) - 1, 0);
+
+ return divider_recalc_rate(hw, parent_rate, val, div_hw_data->dtable,
+ CLK_DIVIDER_ROUND_CLOSEST, div_hw_data->width);
+}
+
+static int rzg3s_div_clk_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
+{
+ struct clk_hw_data *clk_hw_data = to_clk_hw_data(hw);
+ struct div_hw_data *div_hw_data = to_div_hw_data(clk_hw_data);
+
+ if (div_hw_data->max_rate && req->rate > div_hw_data->max_rate)
+ req->rate = div_hw_data->max_rate;
+
+ return divider_determine_rate(hw, req, div_hw_data->dtable, div_hw_data->width,
+ CLK_DIVIDER_ROUND_CLOSEST);
+}
+
+static int rzg3s_div_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_hw_data *clk_hw_data = to_clk_hw_data(hw);
+ struct div_hw_data *div_hw_data = to_div_hw_data(clk_hw_data);
+ struct rzg2l_cpg_priv *priv = clk_hw_data->priv;
+ u32 off = GET_REG_OFFSET(clk_hw_data->conf);
+ u32 shift = GET_SHIFT(clk_hw_data->conf);
+ unsigned long flags;
+ u32 val;
+ int ret;
+
+ val = divider_get_val(rate, parent_rate, div_hw_data->dtable, div_hw_data->width,
+ CLK_DIVIDER_ROUND_CLOSEST);
+
+ spin_lock_irqsave(&priv->rmw_lock, flags);
+ writel((CPG_WEN_BIT | val) << shift, priv->base + off);
+ /* Wait for the update done. */
+ ret = rzg2l_cpg_wait_clk_update_done(priv->base, clk_hw_data->sconf);
+ spin_unlock_irqrestore(&priv->rmw_lock, flags);
+
+ return ret;
+}
+
+static const struct clk_ops rzg3s_div_clk_ops = {
+ .recalc_rate = rzg3s_div_clk_recalc_rate,
+ .determine_rate = rzg3s_div_clk_determine_rate,
+ .set_rate = rzg3s_div_clk_set_rate,
+};
+
+static struct clk * __init
+rzg3s_cpg_div_clk_register(const struct cpg_core_clk *core, struct clk **clks,
+ void __iomem *base, struct rzg2l_cpg_priv *priv)
+{
+ struct div_hw_data *div_hw_data;
+ struct clk_init_data init = {};
+ const struct clk_div_table *clkt;
+ struct clk_hw *clk_hw;
+ const struct clk *parent;
+ const char *parent_name;
+ u32 max = 0;
+ int ret;
+
+ parent = clks[core->parent & 0xffff];
+ if (IS_ERR(parent))
+ return ERR_CAST(parent);
+
+ parent_name = __clk_get_name(parent);
+
+ div_hw_data = devm_kzalloc(priv->dev, sizeof(*div_hw_data), GFP_KERNEL);
+ if (!div_hw_data)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = core->name;
+ init.flags = core->flag;
+ init.ops = &rzg3s_div_clk_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ /* Get the maximum divider to retrieve div width. */
+ for (clkt = core->dtable; clkt->div; clkt++) {
+ if (max < clkt->div)
+ max = clkt->div;
+ }
+
+ div_hw_data->hw_data.priv = priv;
+ div_hw_data->hw_data.conf = core->conf;
+ div_hw_data->hw_data.sconf = core->sconf;
+ div_hw_data->dtable = core->dtable;
+ div_hw_data->invalid_rate = core->invalid_rate;
+ div_hw_data->max_rate = core->max_rate;
+ div_hw_data->width = fls(max) - 1;
+
+ clk_hw = &div_hw_data->hw_data.hw;
+ clk_hw->init = &init;
+
+ ret = devm_clk_hw_register(priv->dev, clk_hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = rzg2l_register_notifier(clk_hw, core, priv);
+ if (ret) {
+ dev_err(priv->dev, "Failed to register notifier for %s\n",
+ core->name);
+ return ERR_PTR(ret);
+ }
+
+ return clk_hw->clk;
+}
+
static struct clk * __init
rzg2l_cpg_div_clk_register(const struct cpg_core_clk *core,
struct clk **clks,
@@ -183,63 +463,44 @@ rzg2l_cpg_mux_clk_register(const struct cpg_core_clk *core,
static int rzg2l_cpg_sd_clk_mux_set_parent(struct clk_hw *hw, u8 index)
{
- struct sd_hw_data *hwdata = to_sd_hw_data(hw);
- struct rzg2l_cpg_priv *priv = hwdata->priv;
- u32 off = GET_REG_OFFSET(hwdata->conf);
- u32 shift = GET_SHIFT(hwdata->conf);
- const u32 clk_src_266 = 2;
- u32 msk, val, bitmask;
+ struct clk_hw_data *clk_hw_data = to_clk_hw_data(hw);
+ struct sd_mux_hw_data *sd_mux_hw_data = to_sd_mux_hw_data(clk_hw_data);
+ struct rzg2l_cpg_priv *priv = clk_hw_data->priv;
+ u32 off = GET_REG_OFFSET(clk_hw_data->conf);
+ u32 shift = GET_SHIFT(clk_hw_data->conf);
unsigned long flags;
+ u32 val;
int ret;
- /*
- * As per the HW manual, we should not directly switch from 533 MHz to
- * 400 MHz and vice versa. To change the setting from 2’b01 (533 MHz)
- * to 2’b10 (400 MHz) or vice versa, Switch to 2’b11 (266 MHz) first,
- * and then switch to the target setting (2’b01 (533 MHz) or 2’b10
- * (400 MHz)).
- * Setting a value of '0' to the SEL_SDHI0_SET or SEL_SDHI1_SET clock
- * switching register is prohibited.
- * The clock mux has 3 input clocks(533 MHz, 400 MHz, and 266 MHz), and
- * the index to value mapping is done by adding 1 to the index.
- */
- bitmask = (GENMASK(GET_WIDTH(hwdata->conf) - 1, 0) << shift) << 16;
- msk = off ? CPG_CLKSTATUS_SELSDHI1_STS : CPG_CLKSTATUS_SELSDHI0_STS;
+ val = clk_mux_index_to_val(sd_mux_hw_data->mtable, CLK_MUX_ROUND_CLOSEST, index);
+
spin_lock_irqsave(&priv->rmw_lock, flags);
- if (index != clk_src_266) {
- writel(bitmask | ((clk_src_266 + 1) << shift), priv->base + off);
-
- ret = readl_poll_timeout_atomic(priv->base + CPG_CLKSTATUS, val,
- !(val & msk), 10,
- CPG_SDHI_CLK_SWITCH_STATUS_TIMEOUT_US);
- if (ret)
- goto unlock;
- }
- writel(bitmask | ((index + 1) << shift), priv->base + off);
+ writel((CPG_WEN_BIT | val) << shift, priv->base + off);
+
+ /* Wait for the update done. */
+ ret = rzg2l_cpg_wait_clk_update_done(priv->base, clk_hw_data->sconf);
- ret = readl_poll_timeout_atomic(priv->base + CPG_CLKSTATUS, val,
- !(val & msk), 10,
- CPG_SDHI_CLK_SWITCH_STATUS_TIMEOUT_US);
-unlock:
spin_unlock_irqrestore(&priv->rmw_lock, flags);
if (ret)
- dev_err(priv->dev, "failed to switch clk source\n");
+ dev_err(priv->dev, "Failed to switch parent\n");
return ret;
}
static u8 rzg2l_cpg_sd_clk_mux_get_parent(struct clk_hw *hw)
{
- struct sd_hw_data *hwdata = to_sd_hw_data(hw);
- struct rzg2l_cpg_priv *priv = hwdata->priv;
- u32 val = readl(priv->base + GET_REG_OFFSET(hwdata->conf));
+ struct clk_hw_data *clk_hw_data = to_clk_hw_data(hw);
+ struct sd_mux_hw_data *sd_mux_hw_data = to_sd_mux_hw_data(clk_hw_data);
+ struct rzg2l_cpg_priv *priv = clk_hw_data->priv;
+ u32 val;
- val >>= GET_SHIFT(hwdata->conf);
- val &= GENMASK(GET_WIDTH(hwdata->conf) - 1, 0);
+ val = readl(priv->base + GET_REG_OFFSET(clk_hw_data->conf));
+ val >>= GET_SHIFT(clk_hw_data->conf);
+ val &= GENMASK(GET_WIDTH(clk_hw_data->conf) - 1, 0);
- return val ? val - 1 : 0;
+ return clk_mux_val_to_index(hw, sd_mux_hw_data->mtable, CLK_MUX_ROUND_CLOSEST, val);
}
static const struct clk_ops rzg2l_cpg_sd_clk_mux_ops = {
@@ -253,31 +514,40 @@ rzg2l_cpg_sd_mux_clk_register(const struct cpg_core_clk *core,
void __iomem *base,
struct rzg2l_cpg_priv *priv)
{
- struct sd_hw_data *clk_hw_data;
+ struct sd_mux_hw_data *sd_mux_hw_data;
struct clk_init_data init;
struct clk_hw *clk_hw;
int ret;
- clk_hw_data = devm_kzalloc(priv->dev, sizeof(*clk_hw_data), GFP_KERNEL);
- if (!clk_hw_data)
+ sd_mux_hw_data = devm_kzalloc(priv->dev, sizeof(*sd_mux_hw_data), GFP_KERNEL);
+ if (!sd_mux_hw_data)
return ERR_PTR(-ENOMEM);
- clk_hw_data->priv = priv;
- clk_hw_data->conf = core->conf;
+ sd_mux_hw_data->hw_data.priv = priv;
+ sd_mux_hw_data->hw_data.conf = core->conf;
+ sd_mux_hw_data->hw_data.sconf = core->sconf;
+ sd_mux_hw_data->mtable = core->mtable;
- init.name = GET_SHIFT(core->conf) ? "sd1" : "sd0";
+ init.name = core->name;
init.ops = &rzg2l_cpg_sd_clk_mux_ops;
- init.flags = 0;
+ init.flags = core->flag;
init.num_parents = core->num_parents;
init.parent_names = core->parent_names;
- clk_hw = &clk_hw_data->hw;
+ clk_hw = &sd_mux_hw_data->hw_data.hw;
clk_hw->init = &init;
ret = devm_clk_hw_register(priv->dev, clk_hw);
if (ret)
return ERR_PTR(ret);
+ ret = rzg2l_register_notifier(clk_hw, core, priv);
+ if (ret) {
+ dev_err(priv->dev, "Failed to register notifier for %s\n",
+ core->name);
+ return ERR_PTR(ret);
+ }
+
return clk_hw->clk;
}
@@ -713,11 +983,43 @@ static const struct clk_ops rzg2l_cpg_pll_ops = {
.recalc_rate = rzg2l_cpg_pll_clk_recalc_rate,
};
+static unsigned long rzg3s_cpg_pll_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct pll_clk *pll_clk = to_pll(hw);
+ struct rzg2l_cpg_priv *priv = pll_clk->priv;
+ u32 nir, nfr, mr, pr, val;
+ u64 rate;
+
+ if (pll_clk->type != CLK_TYPE_G3S_PLL)
+ return parent_rate;
+
+ val = readl(priv->base + GET_REG_SAMPLL_CLK1(pll_clk->conf));
+
+ pr = 1 << FIELD_GET(RZG3S_DIV_P, val);
+ /* Hardware interprets values higher than 8 as p = 16. */
+ if (pr > 8)
+ pr = 16;
+
+ mr = FIELD_GET(RZG3S_DIV_M, val) + 1;
+ nir = FIELD_GET(RZG3S_DIV_NI, val) + 1;
+ nfr = FIELD_GET(RZG3S_DIV_NF, val);
+
+ rate = mul_u64_u32_shr(parent_rate, 4096 * nir + nfr, 12);
+
+ return DIV_ROUND_CLOSEST_ULL(rate, (mr * pr));
+}
+
+static const struct clk_ops rzg3s_cpg_pll_ops = {
+ .recalc_rate = rzg3s_cpg_pll_clk_recalc_rate,
+};
+
static struct clk * __init
rzg2l_cpg_pll_clk_register(const struct cpg_core_clk *core,
struct clk **clks,
void __iomem *base,
- struct rzg2l_cpg_priv *priv)
+ struct rzg2l_cpg_priv *priv,
+ const struct clk_ops *ops)
{
struct device *dev = priv->dev;
const struct clk *parent;
@@ -735,7 +1037,7 @@ rzg2l_cpg_pll_clk_register(const struct cpg_core_clk *core,
parent_name = __clk_get_name(parent);
init.name = core->name;
- init.ops = &rzg2l_cpg_pll_ops;
+ init.ops = ops;
init.flags = 0;
init.parent_names = &parent_name;
init.num_parents = 1;
@@ -830,8 +1132,12 @@ rzg2l_cpg_register_core_clk(const struct cpg_core_clk *core,
core->mult, div);
break;
case CLK_TYPE_SAM_PLL:
- clk = rzg2l_cpg_pll_clk_register(core, priv->clks,
- priv->base, priv);
+ clk = rzg2l_cpg_pll_clk_register(core, priv->clks, priv->base, priv,
+ &rzg2l_cpg_pll_ops);
+ break;
+ case CLK_TYPE_G3S_PLL:
+ clk = rzg2l_cpg_pll_clk_register(core, priv->clks, priv->base, priv,
+ &rzg3s_cpg_pll_ops);
break;
case CLK_TYPE_SIPLL5:
clk = rzg2l_cpg_sipll5_register(core, priv->clks, priv);
@@ -840,6 +1146,9 @@ rzg2l_cpg_register_core_clk(const struct cpg_core_clk *core,
clk = rzg2l_cpg_div_clk_register(core, priv->clks,
priv->base, priv);
break;
+ case CLK_TYPE_G3S_DIV:
+ clk = rzg3s_cpg_div_clk_register(core, priv->clks, priv->base, priv);
+ break;
case CLK_TYPE_MUX:
clk = rzg2l_cpg_mux_clk_register(core, priv->base, priv);
break;
@@ -895,7 +1204,6 @@ static int rzg2l_mod_clock_endisable(struct clk_hw *hw, bool enable)
struct rzg2l_cpg_priv *priv = clock->priv;
unsigned int reg = clock->off;
struct device *dev = priv->dev;
- unsigned long flags;
u32 bitmask = BIT(clock->bit);
u32 value;
int error;
@@ -905,17 +1213,14 @@ static int rzg2l_mod_clock_endisable(struct clk_hw *hw, bool enable)
return 0;
}
- dev_dbg(dev, "CLK_ON %u/%pC %s\n", CLK_ON_R(reg), hw->clk,
+ dev_dbg(dev, "CLK_ON 0x%x/%pC %s\n", CLK_ON_R(reg), hw->clk,
enable ? "ON" : "OFF");
- spin_lock_irqsave(&priv->rmw_lock, flags);
+ value = bitmask << 16;
if (enable)
- value = (bitmask << 16) | bitmask;
- else
- value = bitmask << 16;
- writel(value, priv->base + CLK_ON_R(reg));
+ value |= bitmask;
- spin_unlock_irqrestore(&priv->rmw_lock, flags);
+ writel(value, priv->base + CLK_ON_R(reg));
if (!enable)
return 0;
@@ -1422,6 +1727,12 @@ static const struct of_device_id rzg2l_cpg_match[] = {
.data = &r9a07g054_cpg_info,
},
#endif
+#ifdef CONFIG_CLK_R9A08G045
+ {
+ .compatible = "renesas,r9a08g045-cpg",
+ .data = &r9a08g045_cpg_info,
+ },
+#endif
#ifdef CONFIG_CLK_R9A09G011
{
.compatible = "renesas,r9a09g011-cpg",
diff --git a/drivers/clk/renesas/rzg2l-cpg.h b/drivers/clk/renesas/rzg2l-cpg.h
index 91e9c2569f..6e38c8fc88 100644
--- a/drivers/clk/renesas/rzg2l-cpg.h
+++ b/drivers/clk/renesas/rzg2l-cpg.h
@@ -9,6 +9,8 @@
#ifndef __RENESAS_RZG2L_CPG_H__
#define __RENESAS_RZG2L_CPG_H__
+#include <linux/notifier.h>
+
#define CPG_SIPLL5_STBY (0x140)
#define CPG_SIPLL5_CLK1 (0x144)
#define CPG_SIPLL5_CLK3 (0x14C)
@@ -19,7 +21,6 @@
#define CPG_PL2_DDIV (0x204)
#define CPG_PL3A_DDIV (0x208)
#define CPG_PL6_DDIV (0x210)
-#define CPG_PL2SDHI_DSEL (0x218)
#define CPG_CLKSTATUS (0x280)
#define CPG_PL3_SSEL (0x408)
#define CPG_PL6_SSEL (0x414)
@@ -43,8 +44,6 @@
#define CPG_CLKSTATUS_SELSDHI0_STS BIT(28)
#define CPG_CLKSTATUS_SELSDHI1_STS BIT(29)
-#define CPG_SDHI_CLK_SWITCH_STATUS_TIMEOUT_US 200
-
/* n = 0/1/2 for PLL1/4/6 */
#define CPG_SAMPLL_CLK1(n) (0x04 + (16 * n))
#define CPG_SAMPLL_CLK2(n) (0x08 + (16 * n))
@@ -69,9 +68,6 @@
#define SEL_PLL6_2 SEL_PLL_PACK(CPG_PL6_ETH_SSEL, 0, 1)
#define SEL_GPU2 SEL_PLL_PACK(CPG_PL6_SSEL, 12, 1)
-#define SEL_SDHI0 DDIV_PACK(CPG_PL2SDHI_DSEL, 0, 2)
-#define SEL_SDHI1 DDIV_PACK(CPG_PL2SDHI_DSEL, 4, 2)
-
#define EXTAL_FREQ_IN_MEGA_HZ (24)
/**
@@ -90,10 +86,15 @@ struct cpg_core_clk {
unsigned int mult;
unsigned int type;
unsigned int conf;
+ unsigned int sconf;
const struct clk_div_table *dtable;
+ const u32 *mtable;
+ const unsigned long invalid_rate;
+ const unsigned long max_rate;
const char * const *parent_names;
- int flag;
- int mux_flags;
+ notifier_fn_t notifier;
+ u32 flag;
+ u32 mux_flags;
int num_parents;
};
@@ -102,9 +103,11 @@ enum clk_types {
CLK_TYPE_IN, /* External Clock Input */
CLK_TYPE_FF, /* Fixed Factor Clock */
CLK_TYPE_SAM_PLL,
+ CLK_TYPE_G3S_PLL,
/* Clock with divider */
CLK_TYPE_DIV,
+ CLK_TYPE_G3S_DIV,
/* Clock with clock source selector */
CLK_TYPE_MUX,
@@ -129,6 +132,8 @@ enum clk_types {
DEF_TYPE(_name, _id, _type, .parent = _parent)
#define DEF_SAMPLL(_name, _id, _parent, _conf) \
DEF_TYPE(_name, _id, CLK_TYPE_SAM_PLL, .parent = _parent, .conf = _conf)
+#define DEF_G3S_PLL(_name, _id, _parent, _conf) \
+ DEF_TYPE(_name, _id, CLK_TYPE_G3S_PLL, .parent = _parent, .conf = _conf)
#define DEF_INPUT(_name, _id) \
DEF_TYPE(_name, _id, CLK_TYPE_IN)
#define DEF_FIXED(_name, _id, _parent, _mult, _div) \
@@ -141,6 +146,13 @@ enum clk_types {
DEF_TYPE(_name, _id, CLK_TYPE_DIV, .conf = _conf, \
.parent = _parent, .dtable = _dtable, \
.flag = CLK_DIVIDER_READ_ONLY)
+#define DEF_G3S_DIV(_name, _id, _parent, _conf, _sconf, _dtable, _invalid_rate, \
+ _max_rate, _clk_flags, _notif) \
+ DEF_TYPE(_name, _id, CLK_TYPE_G3S_DIV, .conf = _conf, .sconf = _sconf, \
+ .parent = _parent, .dtable = _dtable, \
+ .invalid_rate = _invalid_rate, \
+ .max_rate = _max_rate, .flag = (_clk_flags), \
+ .notifier = _notif)
#define DEF_MUX(_name, _id, _conf, _parent_names) \
DEF_TYPE(_name, _id, CLK_TYPE_MUX, .conf = _conf, \
.parent_names = _parent_names, \
@@ -151,10 +163,11 @@ enum clk_types {
.parent_names = _parent_names, \
.num_parents = ARRAY_SIZE(_parent_names), \
.mux_flags = CLK_MUX_READ_ONLY)
-#define DEF_SD_MUX(_name, _id, _conf, _parent_names) \
- DEF_TYPE(_name, _id, CLK_TYPE_SD_MUX, .conf = _conf, \
+#define DEF_SD_MUX(_name, _id, _conf, _sconf, _parent_names, _mtable, _clk_flags, _notifier) \
+ DEF_TYPE(_name, _id, CLK_TYPE_SD_MUX, .conf = _conf, .sconf = _sconf, \
.parent_names = _parent_names, \
- .num_parents = ARRAY_SIZE(_parent_names))
+ .num_parents = ARRAY_SIZE(_parent_names), \
+ .mtable = _mtable, .flag = _clk_flags, .notifier = _notifier)
#define DEF_PLL5_FOUTPOSTDIV(_name, _id, _parent) \
DEF_TYPE(_name, _id, CLK_TYPE_SIPLL5, .parent = _parent)
#define DEF_PLL5_4_MUX(_name, _id, _conf, _parent_names) \
@@ -271,6 +284,10 @@ struct rzg2l_cpg_info {
extern const struct rzg2l_cpg_info r9a07g043_cpg_info;
extern const struct rzg2l_cpg_info r9a07g044_cpg_info;
extern const struct rzg2l_cpg_info r9a07g054_cpg_info;
+extern const struct rzg2l_cpg_info r9a08g045_cpg_info;
extern const struct rzg2l_cpg_info r9a09g011_cpg_info;
+int rzg2l_cpg_sd_clk_mux_notifier(struct notifier_block *nb, unsigned long event, void *data);
+int rzg3s_cpg_div_clk_notifier(struct notifier_block *nb, unsigned long event, void *data);
+
#endif