summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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/rcar-cpg-lib.c15
-rw-r--r--drivers/clk/renesas/rzg2l-cpg.c459
-rw-r--r--drivers/clk/renesas/rzg2l-cpg.h38
7 files changed, 495 insertions, 128 deletions
diff --git a/drivers/clk/renesas/r8a7795-cpg-mssr.c b/drivers/clk/renesas/r8a7795-cpg-mssr.c
index ad20b3301ef6..e47d9b1fcc0a 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 55db63c7041a..c1348e2d450c 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 1a7a6d60aca4..b70bb378ab46 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 c597414a94d8..1047278c9079 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/rcar-cpg-lib.c b/drivers/clk/renesas/rcar-cpg-lib.c
index e2e0447de190..5a15f8788b92 100644
--- a/drivers/clk/renesas/rcar-cpg-lib.c
+++ b/drivers/clk/renesas/rcar-cpg-lib.c
@@ -70,8 +70,21 @@ void cpg_simple_notifier_register(struct raw_notifier_head *notifiers,
#define STPnHCK BIT(9 - SDnSRCFC_SHIFT)
static const struct clk_div_table cpg_sdh_div_table[] = {
+ /*
+ * These values are recommended by the datasheet. Because they come
+ * first, Linux will only use these.
+ */
{ 0, 1 }, { 1, 2 }, { STPnHCK | 2, 4 }, { STPnHCK | 3, 8 },
- { STPnHCK | 4, 16 }, { 0, 0 },
+ { STPnHCK | 4, 16 },
+ /*
+ * These values are not recommended because STPnHCK is wrong. But they
+ * have been seen because of broken firmware. So, we support reading
+ * them but Linux will sanitize them when initializing through
+ * recalc_rate.
+ */
+ { STPnHCK | 0, 1 }, { STPnHCK | 1, 2 }, { 2, 4 }, { 3, 8 }, { 4, 16 },
+ /* Sentinel */
+ { 0, 0 }
};
struct clk * __init cpg_sdh_clk_register(const char *name,
diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c
index 47f488387f33..49dc6c5c0b0c 100644
--- a/drivers/clk/renesas/rzg2l-cpg.c
+++ b/drivers/clk/renesas/rzg2l-cpg.c
@@ -11,6 +11,7 @@
* Copyright (C) 2015 Renesas Electronics Corp.
*/
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clk/renesas.h>
@@ -38,14 +39,18 @@
#define WARN_DEBUG(x) do { } while (0)
#endif
-#define DIV_RSMASK(v, s, m) ((v >> s) & m)
#define GET_SHIFT(val) ((val >> 12) & 0xff)
#define GET_WIDTH(val) ((val >> 8) & 0xf)
-#define KDIV(val) DIV_RSMASK(val, 16, 0xffff)
-#define MDIV(val) DIV_RSMASK(val, 6, 0x3ff)
-#define PDIV(val) DIV_RSMASK(val, 0, 0x3f)
-#define SDIV(val) DIV_RSMASK(val, 0, 0x7)
+#define KDIV(val) ((s16)FIELD_GET(GENMASK(31, 16), val))
+#define MDIV(val) FIELD_GET(GENMASK(15, 6), val)
+#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))
@@ -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 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;
- if (index != clk_src_266) {
- u32 msk, val;
- int ret;
+ val = clk_mux_index_to_val(sd_mux_hw_data->mtable, CLK_MUX_ROUND_CLOSEST, index);
- writel(bitmask | ((clk_src_266 + 1) << shift), priv->base + off);
+ spin_lock_irqsave(&priv->rmw_lock, flags);
- msk = off ? CPG_CLKSTATUS_SELSDHI1_STS : CPG_CLKSTATUS_SELSDHI0_STS;
+ writel((CPG_WEN_BIT | val) << shift, priv->base + off);
- ret = readl_poll_timeout(priv->base + CPG_CLKSTATUS, val,
- !(val & msk), 100,
- CPG_SDHI_CLK_SWITCH_STATUS_TIMEOUT_US);
- if (ret) {
- dev_err(priv->dev, "failed to switch clk source\n");
- return ret;
- }
- }
+ /* Wait for the update done. */
+ ret = rzg2l_cpg_wait_clk_update_done(priv->base, clk_hw_data->sconf);
- writel(bitmask | ((index + 1) << shift), priv->base + off);
+ spin_unlock_irqrestore(&priv->rmw_lock, flags);
- return 0;
+ if (ret)
+ 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));
-
- val >>= GET_SHIFT(hwdata->conf);
- val &= GENMASK(GET_WIDTH(hwdata->conf) - 1, 0);
- if (val) {
- val--;
- } else {
- /* Prohibited clk source, change it to 533 MHz(reset value) */
- rzg2l_cpg_sd_clk_mux_set_parent(hw, 0);
- }
+ 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 = 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;
+ 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;
}
@@ -695,29 +965,61 @@ static unsigned long rzg2l_cpg_pll_clk_recalc_rate(struct clk_hw *hw,
struct pll_clk *pll_clk = to_pll(hw);
struct rzg2l_cpg_priv *priv = pll_clk->priv;
unsigned int val1, val2;
- unsigned int mult = 1;
- unsigned int div = 1;
+ u64 rate;
if (pll_clk->type != CLK_TYPE_SAM_PLL)
return parent_rate;
val1 = readl(priv->base + GET_REG_SAMPLL_CLK1(pll_clk->conf));
val2 = readl(priv->base + GET_REG_SAMPLL_CLK2(pll_clk->conf));
- mult = MDIV(val1) + KDIV(val1) / 65536;
- div = PDIV(val1) << SDIV(val2);
- return DIV_ROUND_CLOSEST_ULL((u64)parent_rate * mult, div);
+ rate = mul_u64_u32_shr(parent_rate, (MDIV(val1) << 16) + KDIV(val1),
+ 16 + SDIV(val2));
+
+ return DIV_ROUND_CLOSEST_ULL(rate, PDIV(val1));
}
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;
@@ -907,15 +1215,12 @@ static int rzg2l_mod_clock_endisable(struct clk_hw *hw, bool enable)
dev_dbg(dev, "CLK_ON %u/%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;
diff --git a/drivers/clk/renesas/rzg2l-cpg.h b/drivers/clk/renesas/rzg2l-cpg.h
index 6cee9e56acc7..4755befaf38e 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 20000
-
/* 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) \
@@ -273,4 +286,7 @@ extern const struct rzg2l_cpg_info r9a07g044_cpg_info;
extern const struct rzg2l_cpg_info r9a07g054_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