[Letux-kernel] [RFC 07/28] clock/ingenic/tcu: add JZ4730 definitons

H. Nikolaus Schaller hns at goldelico.com
Sat Jan 23 17:28:33 CET 2021


From: Paul Boddie <paul at boddie.org.uk>

Add jz4730 TCU clock definitions and to the existing ingenic tcu driver.

Signed-off-by: Paul Boddie <paul at boddie.org.uk>
Signed-off-by: H. Nikolaus Schaller <hns at goldelico.com>
---
 drivers/clk/ingenic/tcu.c       | 185 +++++++++++++++++++++++++++-----
 include/linux/mfd/ingenic-tcu.h |  25 ++++-
 2 files changed, 180 insertions(+), 30 deletions(-)

diff --git a/drivers/clk/ingenic/tcu.c b/drivers/clk/ingenic/tcu.c
index 9382dc3aa27e6..91392ecb6d013 100644
--- a/drivers/clk/ingenic/tcu.c
+++ b/drivers/clk/ingenic/tcu.c
@@ -2,6 +2,7 @@
 /*
  * JZ47xx SoCs TCU clocks driver
  * Copyright (C) 2019 Paul Cercueil <paul at crapouillou.net>
+ * Copyright (C) 2019, 2020 Paul Boddie <paul at boddie.org.uk>
  */
 
 #include <linux/clk.h>
@@ -22,15 +23,21 @@
 #define pr_fmt(fmt) "ingenic-tcu-clk: " fmt
 
 enum tcu_clk_parent {
-	TCU_PARENT_PCLK,
-	TCU_PARENT_RTC,
-	TCU_PARENT_EXT,
-};
 
-struct ingenic_soc_info {
-	unsigned int num_channels;
-	bool has_ost;
-	bool has_tcu_clk;
+	/* TCU_TCSRx bit values. */
+
+	TCU_PARENT_PCLK	= 0,
+	TCU_PARENT_RTC	= 1,
+	TCU_PARENT_EXT	= 2,
+
+	/* OST_TCSRx bit values. */
+
+	TCU_JZ4730_PARENT_PCLK_DIV_4	= 0,
+	TCU_JZ4730_PARENT_PCLK_DIV_16	= 1,
+	TCU_JZ4730_PARENT_PCLK_DIV_64	= 2,
+	TCU_JZ4730_PARENT_PCLK_DIV_256	= 3,
+	TCU_JZ4730_PARENT_RTC		= 4,
+	TCU_JZ4730_PARENT_EXT		= 5,
 };
 
 struct ingenic_tcu_clk_info {
@@ -39,6 +46,18 @@ struct ingenic_tcu_clk_info {
 	u8 tcsr_reg;
 };
 
+struct ingenic_soc_info {
+	unsigned int num_channels;
+	bool has_ost;
+	bool has_wdt;
+	bool has_tcu_clk;
+	bool has_prescale;
+	bool jz4740_regs;
+	const struct ingenic_tcu_clk_info *clk_info;
+	enum tcu_clk_parent parent_rtc;
+	enum tcu_clk_parent parent_ext;
+};
+
 struct ingenic_tcu_clk {
 	struct clk_hw hw;
 	unsigned int idx;
@@ -67,7 +86,10 @@ static int ingenic_tcu_enable(struct clk_hw *hw)
 	const struct ingenic_tcu_clk_info *info = tcu_clk->info;
 	struct ingenic_tcu *tcu = tcu_clk->tcu;
 
-	regmap_write(tcu->map, TCU_REG_TSCR, BIT(info->gate_bit));
+	if (tcu->soc_info->jz4740_regs)
+		regmap_write(tcu->map, TCU_REG_TSCR, BIT(info->gate_bit));
+	else
+		regmap_set_bits(tcu->map, TCU_JZ4730_REG_TER, BIT(info->gate_bit));
 
 	return 0;
 }
@@ -78,7 +100,10 @@ static void ingenic_tcu_disable(struct clk_hw *hw)
 	const struct ingenic_tcu_clk_info *info = tcu_clk->info;
 	struct ingenic_tcu *tcu = tcu_clk->tcu;
 
-	regmap_write(tcu->map, TCU_REG_TSSR, BIT(info->gate_bit));
+	if (tcu->soc_info->jz4740_regs)
+		regmap_write(tcu->map, TCU_REG_TSSR, BIT(info->gate_bit));
+	else
+		regmap_clear_bits(tcu->map, TCU_JZ4730_REG_TER, BIT(info->gate_bit));
 }
 
 static int ingenic_tcu_is_enabled(struct clk_hw *hw)
@@ -87,7 +112,10 @@ static int ingenic_tcu_is_enabled(struct clk_hw *hw)
 	const struct ingenic_tcu_clk_info *info = tcu_clk->info;
 	unsigned int value;
 
-	regmap_read(tcu_clk->tcu->map, TCU_REG_TSR, &value);
+	if (tcu_clk->tcu->soc_info->jz4740_regs)
+		regmap_read(tcu_clk->tcu->map, TCU_REG_TSR, &value);
+	else
+		regmap_read(tcu_clk->tcu->map, TCU_JZ4730_REG_TER, &value);
 
 	return !(value & BIT(info->gate_bit));
 }
@@ -107,7 +135,10 @@ static bool ingenic_tcu_enable_regs(struct clk_hw *hw)
 	 */
 	if (!tcu->clk) {
 		enabled = !!ingenic_tcu_is_enabled(hw);
-		regmap_write(tcu->map, TCU_REG_TSCR, BIT(info->gate_bit));
+		if (tcu->soc_info->jz4740_regs)
+			regmap_write(tcu->map, TCU_REG_TER, BIT(info->gate_bit));
+		else
+			regmap_set_bits(tcu->map, TCU_JZ4730_REG_TER, BIT(info->gate_bit));
 	}
 
 	return enabled;
@@ -119,34 +150,48 @@ static void ingenic_tcu_disable_regs(struct clk_hw *hw)
 	const struct ingenic_tcu_clk_info *info = tcu_clk->info;
 	struct ingenic_tcu *tcu = tcu_clk->tcu;
 
-	if (!tcu->clk)
-		regmap_write(tcu->map, TCU_REG_TSSR, BIT(info->gate_bit));
+	if (!tcu->clk) {
+		if (tcu->soc_info->jz4740_regs)
+			regmap_write(tcu->map, TCU_REG_TSSR, BIT(info->gate_bit));
+		else
+			regmap_clear_bits(tcu->map, TCU_JZ4730_REG_TER, BIT(info->gate_bit));
+	}
 }
 
 static u8 ingenic_tcu_get_parent(struct clk_hw *hw)
 {
 	struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);
 	const struct ingenic_tcu_clk_info *info = tcu_clk->info;
+	struct ingenic_tcu *tcu = tcu_clk->tcu;
 	unsigned int val = 0;
 	int ret;
 
 	ret = regmap_read(tcu_clk->tcu->map, info->tcsr_reg, &val);
 	WARN_ONCE(ret < 0, "Unable to read TCSR %d", tcu_clk->idx);
 
-	return ffs(val & TCU_TCSR_PARENT_CLOCK_MASK) - 1;
+	if (tcu->soc_info->jz4740_regs)
+		return ffs(val & TCU_TCSR_PARENT_CLOCK_MASK) - 1;
+	else
+		return val & TCU_JZ4730_TCSR_PARENT_CLOCK_MASK;
 }
 
 static int ingenic_tcu_set_parent(struct clk_hw *hw, u8 idx)
 {
 	struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);
 	const struct ingenic_tcu_clk_info *info = tcu_clk->info;
+	struct ingenic_tcu *tcu = tcu_clk->tcu;
 	bool was_enabled;
 	int ret;
 
 	was_enabled = ingenic_tcu_enable_regs(hw);
 
-	ret = regmap_update_bits(tcu_clk->tcu->map, info->tcsr_reg,
-				 TCU_TCSR_PARENT_CLOCK_MASK, BIT(idx));
+	if (tcu->soc_info->jz4740_regs) {
+		ret = regmap_update_bits(tcu_clk->tcu->map, info->tcsr_reg,
+					 TCU_TCSR_PARENT_CLOCK_MASK, BIT(idx));
+	} else {
+		ret = regmap_update_bits(tcu_clk->tcu->map, info->tcsr_reg,
+					 TCU_JZ4730_TCSR_PARENT_CLOCK_MASK, idx);
+	}
 	WARN_ONCE(ret < 0, "Unable to update TCSR %d", tcu_clk->idx);
 
 	if (!was_enabled)
@@ -191,6 +236,9 @@ static long ingenic_tcu_round_rate(struct clk_hw *hw, unsigned long req_rate,
 	if (req_rate > rate)
 		return rate;
 
+	if (!to_tcu_clk(hw)->tcu->soc_info->has_prescale)
+		return rate;
+
 	prescale = ingenic_tcu_get_prescale(rate, req_rate);
 
 	return rate >> (prescale * 2);
@@ -201,10 +249,14 @@ static int ingenic_tcu_set_rate(struct clk_hw *hw, unsigned long req_rate,
 {
 	struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);
 	const struct ingenic_tcu_clk_info *info = tcu_clk->info;
-	u8 prescale = ingenic_tcu_get_prescale(parent_rate, req_rate);
+	u8 prescale;
 	bool was_enabled;
 	int ret;
 
+	if (!tcu_clk->tcu->soc_info->has_prescale)
+		return 0;
+
+	prescale = ingenic_tcu_get_prescale(parent_rate, req_rate);
 	was_enabled = ingenic_tcu_enable_regs(hw);
 
 	ret = regmap_update_bits(tcu_clk->tcu->map, info->tcsr_reg,
@@ -237,6 +289,15 @@ static const char * const ingenic_tcu_timer_parents[] = {
 	[TCU_PARENT_EXT]  = "ext",
 };
 
+static const char * const ingenic_tcu_timer_jz4730_parents[] = {
+	[TCU_JZ4730_PARENT_PCLK_DIV_4] = "pclk_div_4",
+	[TCU_JZ4730_PARENT_PCLK_DIV_16] = "pclk_div_16",
+	[TCU_JZ4730_PARENT_PCLK_DIV_64] = "pclk_div_64",
+	[TCU_JZ4730_PARENT_PCLK_DIV_256] = "pclk_div_256",
+	[TCU_JZ4730_PARENT_RTC]  = "rtc",
+	[TCU_JZ4730_PARENT_EXT]  = "ext",
+};
+
 #define DEF_TIMER(_name, _gate_bit, _tcsr)				\
 	{								\
 		.init_data = {						\
@@ -260,11 +321,30 @@ static const struct ingenic_tcu_clk_info ingenic_tcu_clk_info[] = {
 	[TCU_CLK_TIMER7] = DEF_TIMER("timer7", 7, TCU_REG_TCSRc(7)),
 };
 
+#define DEF_TIMER_JZ4730(_name, _gate_bit, _tcsr)				\
+	{								\
+		.init_data = {						\
+			.name = _name,					\
+			.parent_names = ingenic_tcu_timer_jz4730_parents,\
+			.num_parents = ARRAY_SIZE(ingenic_tcu_timer_jz4730_parents),\
+			.ops = &ingenic_tcu_clk_ops,			\
+			.flags = CLK_SET_RATE_UNGATE,			\
+		},							\
+		.gate_bit = _gate_bit,					\
+		.tcsr_reg = _tcsr,					\
+	}
+static const struct ingenic_tcu_clk_info ingenic_tcu_jz4730_clk_info[] = {
+	[TCU_CLK_TIMER0] = DEF_TIMER_JZ4730("timer0", 0, TCU_JZ4730_REG_TCSRc(0)),
+	[TCU_CLK_TIMER1] = DEF_TIMER_JZ4730("timer1", 1, TCU_JZ4730_REG_TCSRc(1)),
+	[TCU_CLK_TIMER2] = DEF_TIMER_JZ4730("timer2", 2, TCU_JZ4730_REG_TCSRc(2)),
+};
+
 static const struct ingenic_tcu_clk_info ingenic_tcu_watchdog_clk_info =
 					 DEF_TIMER("wdt", 16, TCU_REG_WDT_TCSR);
 static const struct ingenic_tcu_clk_info ingenic_tcu_ost_clk_info =
 					 DEF_TIMER("ost", 15, TCU_REG_OST_TCSR);
 #undef DEF_TIMER
+#undef DEF_TIMER_JZ4730
 
 static int __init ingenic_tcu_register_clock(struct ingenic_tcu *tcu,
 			unsigned int idx, enum tcu_clk_parent parent,
@@ -285,7 +365,12 @@ static int __init ingenic_tcu_register_clock(struct ingenic_tcu *tcu,
 
 	/* Reset channel and clock divider, set default parent */
 	ingenic_tcu_enable_regs(&tcu_clk->hw);
-	regmap_update_bits(tcu->map, info->tcsr_reg, 0xffff, BIT(parent));
+	if (tcu->soc_info->jz4740_regs) {
+		regmap_update_bits(tcu->map, info->tcsr_reg, 0xffff, BIT(parent));
+	} else {
+		regmap_update_bits(tcu->map, info->tcsr_reg,
+				   0xffff & ~TCU_JZ4730_TCSR_EN, parent);
+	}
 	ingenic_tcu_disable_regs(&tcu_clk->hw);
 
 	err = clk_hw_register(NULL, &tcu_clk->hw);
@@ -302,30 +387,67 @@ static int __init ingenic_tcu_register_clock(struct ingenic_tcu *tcu,
 static const struct ingenic_soc_info jz4740_soc_info = {
 	.num_channels = 8,
 	.has_ost = false,
+	.has_wdt = true,
 	.has_tcu_clk = true,
+	.has_prescale = true,
+	.jz4740_regs = true,
+	.clk_info = ingenic_tcu_clk_info,
+	.parent_rtc = TCU_PARENT_RTC,
+	.parent_ext = TCU_PARENT_EXT,
 };
 
 static const struct ingenic_soc_info jz4725b_soc_info = {
 	.num_channels = 6,
 	.has_ost = true,
+	.has_wdt = true,
+	.has_tcu_clk = true,
+	.has_prescale = true,
+	.jz4740_regs = true,
+	.clk_info = ingenic_tcu_clk_info,
+	.parent_rtc = TCU_PARENT_RTC,
+	.parent_ext = TCU_PARENT_EXT,
+};
+
+static const struct ingenic_soc_info jz4730_soc_info = {
+	.num_channels = 3,
+	.has_ost = false,	/* JZ4730 uses OST channels as TCU channels */
+	.has_wdt = false,	/* JZ4730 has a separate watchdog timer */
 	.has_tcu_clk = true,
+	.has_prescale = false,	/* JZ4730 TCSR has no apparent prescale field */
+	.jz4740_regs = false,	/* JZ4730 uses different register layout */
+	.clk_info = ingenic_tcu_jz4730_clk_info,
+	.parent_rtc = TCU_JZ4730_PARENT_RTC,
+	.parent_ext = TCU_JZ4730_PARENT_EXT,
 };
 
 static const struct ingenic_soc_info jz4770_soc_info = {
 	.num_channels = 8,
 	.has_ost = true,
+	.has_wdt = true,
 	.has_tcu_clk = false,
+	.has_prescale = true,
+	.jz4740_regs = true,
+	.clk_info = ingenic_tcu_clk_info,
+	.parent_rtc = TCU_PARENT_RTC,
+	.parent_ext = TCU_PARENT_EXT,
 };
 
 static const struct ingenic_soc_info x1000_soc_info = {
 	.num_channels = 8,
 	.has_ost = false, /* X1000 has OST, but it not belong TCU */
+	.has_wdt = true,
 	.has_tcu_clk = false,
+	.has_prescale = true,
+	.jz4740_regs = true,
+	.clk_info = ingenic_tcu_clk_info,
+	.parent_rtc = TCU_PARENT_RTC,
+	.parent_ext = TCU_PARENT_EXT,
 };
 
 static const struct of_device_id __maybe_unused ingenic_tcu_of_match[] __initconst = {
 	{ .compatible = "ingenic,jz4740-tcu", .data = &jz4740_soc_info, },
 	{ .compatible = "ingenic,jz4725b-tcu", .data = &jz4725b_soc_info, },
+	{ .compatible = "ingenic,jz4730-tcu", .data = &jz4730_soc_info, },
 	{ .compatible = "ingenic,jz4770-tcu", .data = &jz4770_soc_info, },
 	{ .compatible = "ingenic,x1000-tcu", .data = &x1000_soc_info, },
 	{ /* sentinel */ }
@@ -375,8 +497,8 @@ static int __init ingenic_tcu_probe(struct device_node *np)
 	tcu->clocks->num = TCU_CLK_COUNT;
 
 	for (i = 0; i < tcu->soc_info->num_channels; i++) {
-		ret = ingenic_tcu_register_clock(tcu, i, TCU_PARENT_EXT,
-						 &ingenic_tcu_clk_info[i],
+		ret = ingenic_tcu_register_clock(tcu, i, tcu->soc_info->parent_ext,
+						 &tcu->soc_info->clk_info[i],
 						 tcu->clocks);
 		if (ret) {
 			pr_crit("cannot register clock %d\n", i);
@@ -391,17 +513,19 @@ static int __init ingenic_tcu_probe(struct device_node *np)
 	 * the watchdog would kick after a maximum time of 5s, and we might
 	 * want a slower kicking time.
 	 */
-	ret = ingenic_tcu_register_clock(tcu, TCU_CLK_WDT, TCU_PARENT_RTC,
-					 &ingenic_tcu_watchdog_clk_info,
-					 tcu->clocks);
-	if (ret) {
-		pr_crit("cannot register watchdog clock\n");
-		goto err_unregister_timer_clocks;
+	if (tcu->soc_info->has_wdt) {
+		ret = ingenic_tcu_register_clock(tcu, TCU_CLK_WDT, tcu->soc_info->parent_rtc,
+						 &ingenic_tcu_watchdog_clk_info,
+						 tcu->clocks);
+		if (ret) {
+			pr_crit("cannot register watchdog clock\n");
+			goto err_unregister_timer_clocks;
+		}
 	}
 
 	if (tcu->soc_info->has_ost) {
 		ret = ingenic_tcu_register_clock(tcu, TCU_CLK_OST,
-						 TCU_PARENT_EXT,
+						 tcu->soc_info->parent_ext,
 						 &ingenic_tcu_ost_clk_info,
 						 tcu->clocks);
 		if (ret) {
@@ -424,7 +548,8 @@ static int __init ingenic_tcu_probe(struct device_node *np)
 	if (tcu->soc_info->has_ost)
 		clk_hw_unregister(tcu->clocks->hws[i + 1]);
 err_unregister_watchdog_clock:
-	clk_hw_unregister(tcu->clocks->hws[i]);
+	if (tcu->soc_info->has_wdt)
+		clk_hw_unregister(tcu->clocks->hws[i]);
 err_unregister_timer_clocks:
 	for (i = 0; i < tcu->clocks->num; i++)
 		if (tcu->clocks->hws[i])
@@ -477,5 +602,7 @@ static void __init ingenic_tcu_init(struct device_node *np)
 
 CLK_OF_DECLARE_DRIVER(jz4740_cgu, "ingenic,jz4740-tcu", ingenic_tcu_init);
 CLK_OF_DECLARE_DRIVER(jz4725b_cgu, "ingenic,jz4725b-tcu", ingenic_tcu_init);
+CLK_OF_DECLARE_DRIVER(jz4730_cgu, "ingenic,jz4730-tcu", ingenic_tcu_init);
 CLK_OF_DECLARE_DRIVER(jz4770_cgu, "ingenic,jz4770-tcu", ingenic_tcu_init);
 CLK_OF_DECLARE_DRIVER(x1000_cgu, "ingenic,x1000-tcu", ingenic_tcu_init);
+MODULE_AUTHOR("Paul Boddie <paul at boddie.org.uk>");
diff --git a/include/linux/mfd/ingenic-tcu.h b/include/linux/mfd/ingenic-tcu.h
index 2083fa20821df..c040308945a7d 100644
--- a/include/linux/mfd/ingenic-tcu.h
+++ b/include/linux/mfd/ingenic-tcu.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Header file for the Ingenic JZ47xx TCU driver
  */
@@ -53,4 +53,27 @@
 #define TCU_REG_TCNTc(c)	(TCU_REG_TCNT0 + ((c) * TCU_CHANNEL_STRIDE))
 #define TCU_REG_TCSRc(c)	(TCU_REG_TCSR0 + ((c) * TCU_CHANNEL_STRIDE))
 
+/* JZ4730 register layout. */
+
+#define WDT_JZ4730_REG_TCSR	0x00
+#define WDT_JZ4730_REG_TCNT	0x04
+
+#define WDT_JZ4730_TCSR_EN	BIT(4)	/* Watchdog timer enable */
+
+#define TCU_JZ4730_REG_TER	0x00
+#define TCU_JZ4730_REG_TRDR0	0x10
+#define TCU_JZ4730_REG_TCNT0	0x14
+#define TCU_JZ4730_REG_TCSR0	0x18
+
+#define TCU_JZ4730_TCSR_PARENT_CLOCK_MASK	0x07
+
+#define TCU_JZ4730_TCSR_BUSY	BIT(7)
+#define TCU_JZ4730_TCSR_FLAG	BIT(6)
+#define TCU_JZ4730_TCSR_EN	BIT(5)
+
+#define TCU_JZ4730_CHANNEL_STRIDE	0x20
+#define TCU_JZ4730_REG_TRDRc(c)		(TCU_JZ4730_REG_TRDR0 + ((c) * TCU_JZ4730_CHANNEL_STRIDE))
+#define TCU_JZ4730_REG_TCNTc(c)		(TCU_JZ4730_REG_TCNT0 + ((c) * TCU_JZ4730_CHANNEL_STRIDE))
+#define TCU_JZ4730_REG_TCSRc(c)		(TCU_JZ4730_REG_TCSR0 + ((c) * TCU_JZ4730_CHANNEL_STRIDE))
+
 #endif /* __LINUX_MFD_INGENIC_TCU_H_ */
-- 
2.26.2



More information about the Letux-kernel mailing list