u-boot.lists.denx.de archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH v1 0/9] Renesas RZ/N1 SoC initial support
@ 2022-08-09 12:59 Ralph Siemsen
  2022-08-09 12:59 ` [RFC PATCH v1 1/9] ARM: armv7: add non-SPL enable for Cortex SMPEN Ralph Siemsen
                   ` (8 more replies)
  0 siblings, 9 replies; 29+ messages in thread
From: Ralph Siemsen @ 2022-08-09 12:59 UTC (permalink / raw)
  To: u-boot
  Cc: Ralph Siemsen, AKASHI Takahiro, Andre Przywara, Andrew Davis,
	Ashok Reddy Soma, Aswath Govindraju, Bharat Gooty,
	Damien Le Moal, David Huang, Heiko Thiery, Jim Liu,
	Lukasz Majewski, Mark Kettenis, Niklas Cassel, Nishanth Menon,
	Pali Rohár, Rayagonda Kokatanur, Samuel Holland,
	Sean Anderson, Simon Glass, Stefan Roese, Suman Anna

The RZ/N1 is a family of SoC devices from Renesas [1], featuring ARM
Cortex-A7 and/or Cortex-M3 CPU, industrial ethernet protocols,
integrated Ethernet switch, and numerous peripherals.

This is a first step in upstreaming support for the RZ/N1 family.
Currently it contains just enough to boot to the u-boot prompt.
Additional patches will follow to support flash, SD, USB, Ethernet, etc.

At this point, I am looking for general feedback about the overall
direction and structure. I'm aware of several checkpatch warnings, and
some TODO/FIXME items, which will be addressed.

This work is based on a vendor-supplied u-boot 2017.01 tree [2],
which supports several eval boards, none of which I have access to.
Instead development has been done on a Schneider LCES2 board, which
is fairly similar to the Renesas RZ/N1D-DB Demo board.

The patches are currently against v2022.10-rc2.

[1] https://www.renesas.com/us/en/products/microcontrollers-microprocessors/rz-mpus/rzn1
[2] https://github.com/renesas-rz/rzn1_u-boot/tree/rzn1-stable

Michel Pollet (1):
  tools: Add tool to create Renesas SPKG images

Ralph Siemsen (8):
  ARM: armv7: add non-SPL enable for Cortex SMPEN
  clk: renesas: prepare for non-RCAR clock drivers
  clk: renesas: add R906G032 driver
  pinctrl: renesas: add R906G032 driver
  ram: cadence: add driver for Cadence EDAC
  dts: basic devicetree for Renesas RZ/N1 SoC
  ARM: rzn1: basic support for Renesas RZ/N1 SoC
  board: schneider: add LCES board support

 arch/arm/Kconfig                              |  17 +
 arch/arm/Makefile                             |   1 +
 arch/arm/cpu/armv7/Kconfig                    |   5 +
 arch/arm/dts/r9a06g032-rzn1d400-lces.dts      |  50 ++
 arch/arm/dts/r9a06g032.dtsi                   | 225 ++++++
 arch/arm/mach-rzn1/Kconfig                    |  32 +
 arch/arm/mach-rzn1/Makefile                   |   3 +
 arch/arm/mach-rzn1/cpu_info.c                 |  20 +
 board/schneider/lces/Kconfig                  |  12 +
 board/schneider/lces/Makefile                 |   3 +
 board/schneider/lces/ddr_timing.c             | 140 ++++
 .../lces/jedec_ddr3_2g_x16_1333h_500_cl8.h    | 399 ++++++++++
 board/schneider/lces/lces.c                   |  41 +
 configs/lces_defconfig                        |  26 +
 drivers/clk/renesas/Kconfig                   |   8 +-
 drivers/clk/renesas/Makefile                  |   6 +-
 drivers/clk/renesas/r9a06g032-clocks.c        | 734 ++++++++++++++++++
 drivers/pinctrl/Makefile                      |   1 +
 drivers/pinctrl/renesas/Kconfig               |   7 +
 drivers/pinctrl/renesas/Makefile              |   1 +
 drivers/pinctrl/renesas/pinctrl-rzn1.c        | 398 ++++++++++
 drivers/ram/Kconfig                           |   1 +
 drivers/ram/Makefile                          |   2 +
 drivers/ram/cadence/Kconfig                   |  24 +
 drivers/ram/cadence/Makefile                  |   1 +
 drivers/ram/cadence/ddr_async.c               | 295 +++++++
 drivers/ram/cadence/ddr_ctrl.c                | 414 ++++++++++
 drivers/ram/cadence/ddr_ctrl.h                | 172 ++++
 include/configs/lces.h                        |  22 +
 include/dt-bindings/clock/r9a06g032-sysctrl.h | 148 ++++
 include/dt-bindings/pinctrl/rzn1-pinctrl.h    | 141 ++++
 tools/Makefile                                |   2 +
 tools/spkg_header.h                           |  49 ++
 tools/spkg_utility.c                          | 306 ++++++++
 34 files changed, 3702 insertions(+), 4 deletions(-)
 create mode 100644 arch/arm/dts/r9a06g032-rzn1d400-lces.dts
 create mode 100644 arch/arm/dts/r9a06g032.dtsi
 create mode 100644 arch/arm/mach-rzn1/Kconfig
 create mode 100644 arch/arm/mach-rzn1/Makefile
 create mode 100644 arch/arm/mach-rzn1/cpu_info.c
 create mode 100644 board/schneider/lces/Kconfig
 create mode 100644 board/schneider/lces/Makefile
 create mode 100644 board/schneider/lces/ddr_timing.c
 create mode 100644 board/schneider/lces/jedec_ddr3_2g_x16_1333h_500_cl8.h
 create mode 100644 board/schneider/lces/lces.c
 create mode 100644 configs/lces_defconfig
 create mode 100644 drivers/clk/renesas/r9a06g032-clocks.c
 create mode 100644 drivers/pinctrl/renesas/pinctrl-rzn1.c
 create mode 100644 drivers/ram/cadence/Kconfig
 create mode 100644 drivers/ram/cadence/Makefile
 create mode 100644 drivers/ram/cadence/ddr_async.c
 create mode 100644 drivers/ram/cadence/ddr_ctrl.c
 create mode 100644 drivers/ram/cadence/ddr_ctrl.h
 create mode 100644 include/configs/lces.h
 create mode 100644 include/dt-bindings/clock/r9a06g032-sysctrl.h
 create mode 100644 include/dt-bindings/pinctrl/rzn1-pinctrl.h
 create mode 100644 tools/spkg_header.h
 create mode 100644 tools/spkg_utility.c

-- 
2.25.1


^ permalink raw reply	[flat|nested] 29+ messages in thread

* [RFC PATCH v1 1/9] ARM: armv7: add non-SPL enable for Cortex SMPEN
  2022-08-09 12:59 [RFC PATCH v1 0/9] Renesas RZ/N1 SoC initial support Ralph Siemsen
@ 2022-08-09 12:59 ` Ralph Siemsen
  2022-08-09 12:59 ` [RFC PATCH v1 2/9] clk: renesas: prepare for non-RCAR clock drivers Ralph Siemsen
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 29+ messages in thread
From: Ralph Siemsen @ 2022-08-09 12:59 UTC (permalink / raw)
  To: u-boot; +Cc: Ralph Siemsen, Andre Przywara

Commit 2564fce7eea3 ("sunxi: move Cortex SMPEN setting into start.S")
added SPL_ARMV7_SET_CORTEX_SMPEN to enable setting SMP bit. For
platforms not using SPL boot, add the corresponding non-SPL config,
so that CONFIG_IS_ENABLED(ARMV7_SET_CORTEX_SMPEN) works as expected.

Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
---
This will be used by the following commit that adds ARCH_RZN1.

 arch/arm/cpu/armv7/Kconfig | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm/cpu/armv7/Kconfig b/arch/arm/cpu/armv7/Kconfig
index f1e4e26b8f..e33e53636a 100644
--- a/arch/arm/cpu/armv7/Kconfig
+++ b/arch/arm/cpu/armv7/Kconfig
@@ -107,6 +107,11 @@ config ARMV7_LPAE
 	Say Y here to use the long descriptor page table format. This is
 	required if U-Boot runs in HYP mode.
 
+config ARMV7_SET_CORTEX_SMPEN
+	bool
+	help
+	  Enable the ARM Cortex ACTLR.SMP enable bit in U-boot.
+
 config SPL_ARMV7_SET_CORTEX_SMPEN
 	bool
 	help
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v1 2/9] clk: renesas: prepare for non-RCAR clock drivers
  2022-08-09 12:59 [RFC PATCH v1 0/9] Renesas RZ/N1 SoC initial support Ralph Siemsen
  2022-08-09 12:59 ` [RFC PATCH v1 1/9] ARM: armv7: add non-SPL enable for Cortex SMPEN Ralph Siemsen
@ 2022-08-09 12:59 ` Ralph Siemsen
  2022-08-13  4:37   ` Sean Anderson
  2022-08-09 12:59 ` [RFC PATCH v1 3/9] clk: renesas: add R906G032 driver Ralph Siemsen
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 29+ messages in thread
From: Ralph Siemsen @ 2022-08-09 12:59 UTC (permalink / raw)
  To: u-boot; +Cc: Ralph Siemsen, Lukasz Majewski, Sean Anderson

Allow CONFIG_CLK_RENESAS to be set without bringing in RCAR-GEN2/3 code.

CONFIG_RENESAS is used in drivers/clk/Makefile to control recursion into
the drivers/clk/renesas subdirectory. It also controls compilation of
renesas-cpg-mssr.c support code for the RCAR-GEN2 and RCAR-GEN3 devices.

The support code contains platform specific hardware access (TMU_BASE),
and it is not needed for other Renesas devices such as RZ/N1. Therefore,
alter Makefile to build renesas-cpg-mssr.c only for RCAR-GEN2 and RCAR-GEN3.

Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
---

 drivers/clk/renesas/Kconfig  | 2 +-
 drivers/clk/renesas/Makefile | 5 ++---
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig
index f4d6ef9f93..c53ff3ce01 100644
--- a/drivers/clk/renesas/Kconfig
+++ b/drivers/clk/renesas/Kconfig
@@ -1,6 +1,6 @@
 config CLK_RENESAS
 	bool "Renesas clock drivers"
-	depends on CLK && ARCH_RMOBILE
+	depends on CLK && (ARCH_RMOBILE || ARCH_RZN1)
 	help
 	  Enable support for clock present on Renesas RCar SoCs.
 
diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile
index 36a5ca65f4..2cd2c69f68 100644
--- a/drivers/clk/renesas/Makefile
+++ b/drivers/clk/renesas/Makefile
@@ -1,5 +1,4 @@
-obj-$(CONFIG_CLK_RENESAS) += renesas-cpg-mssr.o
-obj-$(CONFIG_CLK_RCAR_GEN2) += clk-rcar-gen2.o
+obj-$(CONFIG_CLK_RCAR_GEN2) += clk-rcar-gen2.o renesas-cpg-mssr.o
 obj-$(CONFIG_CLK_R8A774A1) += r8a774a1-cpg-mssr.o
 obj-$(CONFIG_CLK_R8A774B1) += r8a774b1-cpg-mssr.o
 obj-$(CONFIG_CLK_R8A774C0) += r8a774c0-cpg-mssr.o
@@ -9,7 +8,7 @@ obj-$(CONFIG_CLK_R8A7791) += r8a7791-cpg-mssr.o
 obj-$(CONFIG_CLK_R8A7792) += r8a7792-cpg-mssr.o
 obj-$(CONFIG_CLK_R8A7793) += r8a7791-cpg-mssr.o
 obj-$(CONFIG_CLK_R8A7794) += r8a7794-cpg-mssr.o
-obj-$(CONFIG_CLK_RCAR_GEN3) += clk-rcar-gen3.o
+obj-$(CONFIG_CLK_RCAR_GEN3) += clk-rcar-gen3.o renesas-cpg-mssr.o
 obj-$(CONFIG_CLK_R8A7795) += r8a7795-cpg-mssr.o
 obj-$(CONFIG_CLK_R8A7796) += r8a7796-cpg-mssr.o
 obj-$(CONFIG_CLK_R8A77965) += r8a77965-cpg-mssr.o
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v1 3/9] clk: renesas: add R906G032 driver
  2022-08-09 12:59 [RFC PATCH v1 0/9] Renesas RZ/N1 SoC initial support Ralph Siemsen
  2022-08-09 12:59 ` [RFC PATCH v1 1/9] ARM: armv7: add non-SPL enable for Cortex SMPEN Ralph Siemsen
  2022-08-09 12:59 ` [RFC PATCH v1 2/9] clk: renesas: prepare for non-RCAR clock drivers Ralph Siemsen
@ 2022-08-09 12:59 ` Ralph Siemsen
  2022-08-13  5:30   ` Sean Anderson
  2022-08-09 12:59 ` [RFC PATCH v1 4/9] pinctrl: " Ralph Siemsen
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 29+ messages in thread
From: Ralph Siemsen @ 2022-08-09 12:59 UTC (permalink / raw)
  To: u-boot; +Cc: Ralph Siemsen, Lukasz Majewski, Sean Anderson

Clock driver for the Renesas RZ/N1 SoC family. This is based
on the Linux kernel drivers/clk/renesas/r9a06g032-clocks.c.

Notable difference: this version avoids allocating a 'struct clk'
for each clock source, as this is problematic before relocation.
Instead, it uses the same approach as existing Renesas RCAR2/3
clock drivers, using a temporary structure filled on-the-fly.

Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
---
- TODO: add support for div_table

 drivers/clk/renesas/Kconfig            |   6 +
 drivers/clk/renesas/Makefile           |   1 +
 drivers/clk/renesas/r9a06g032-clocks.c | 734 +++++++++++++++++++++++++
 3 files changed, 741 insertions(+)
 create mode 100644 drivers/clk/renesas/r9a06g032-clocks.c

diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig
index c53ff3ce01..e2f72fc04f 100644
--- a/drivers/clk/renesas/Kconfig
+++ b/drivers/clk/renesas/Kconfig
@@ -120,3 +120,9 @@ config CLK_R8A779A0
 	depends on CLK_RCAR_GEN3
 	help
 	  Enable this to support the clocks on Renesas R8A779A0 SoC.
+
+config CLK_R9A06G032
+	bool "Renesas R9A06G032 clock driver"
+	depends on CLK_RENESAS
+	help
+	  Enable this to support the clocks on Renesas R9A06G032 SoC.
diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile
index 2cd2c69f68..9981f1a0bc 100644
--- a/drivers/clk/renesas/Makefile
+++ b/drivers/clk/renesas/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_CLK_R8A77980) += r8a77980-cpg-mssr.o
 obj-$(CONFIG_CLK_R8A77990) += r8a77990-cpg-mssr.o
 obj-$(CONFIG_CLK_R8A77995) += r8a77995-cpg-mssr.o
 obj-$(CONFIG_CLK_R8A779A0) += r8a779a0-cpg-mssr.o
+obj-$(CONFIG_CLK_R9A06G032) += r9a06g032-clocks.o
diff --git a/drivers/clk/renesas/r9a06g032-clocks.c b/drivers/clk/renesas/r9a06g032-clocks.c
new file mode 100644
index 0000000000..9c8f51eb96
--- /dev/null
+++ b/drivers/clk/renesas/r9a06g032-clocks.c
@@ -0,0 +1,734 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * R9A06G032 clock driver
+ *
+ * Copyright (C) 2018 Renesas Electronics Europe Limited
+ *
+ * Michel Pollet <michel.pollet@bp.renesas.com>, <buserror@gmail.com>
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <linux/bitops.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#include <dt-bindings/clock/r9a06g032-sysctrl.h>
+
+struct r9a06g032_gate {
+	u16 gate, reset, ready, midle,
+		scon, mirack, mistat;
+};
+
+/* This is used to describe a clock for instantiation */
+struct r9a06g032_clkdesc {
+	const char *name;
+	uint32_t managed: 1;
+	uint32_t type: 3;
+	uint32_t index: 8;
+	uint32_t source : 8; /* source index + 1 (0 == none) */
+	/* these are used to populate the bitsel struct */
+	union {
+		struct r9a06g032_gate gate;
+		/* for dividers */
+		struct {
+			unsigned int div_min : 10, div_max : 10, reg: 10;
+			u16 div_table[4];
+		};
+		/* For fixed-factor ones */
+		struct {
+			u16 div, mul;
+		};
+		/* for dual gate */
+		struct {
+			uint16_t group : 1;
+			u16 sel, g1, r1, g2, r2;
+		} dual;
+	};
+};
+
+#define I_GATE(_clk, _rst, _rdy, _midle, _scon, _mirack, _mistat) \
+	{ .gate = _clk, .reset = _rst, \
+		.ready = _rdy, .midle = _midle, \
+		.scon = _scon, .mirack = _mirack, .mistat = _mistat }
+#define D_GATE(_idx, _n, _src, ...) \
+	{ .type = K_GATE, .index = R9A06G032_##_idx, \
+		.source = 1 + R9A06G032_##_src, .name = _n, \
+		.gate = I_GATE(__VA_ARGS__) }
+#define D_MODULE(_idx, _n, _src, ...) \
+	{ .type = K_GATE, .index = R9A06G032_##_idx, \
+		.source = 1 + R9A06G032_##_src, .name = _n, \
+		.managed = 1, .gate = I_GATE(__VA_ARGS__) }
+#define D_ROOT(_idx, _n, _mul, _div) \
+	{ .type = K_FFC, .index = R9A06G032_##_idx, .name = _n, \
+		.div = _div, .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}
+#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__ } }
+#define D_UGATE(_idx, _n, _src, _g, _g1, _r1, _g2, _r2) \
+	{ .type = K_DUALGATE, .index = R9A06G032_##_idx, \
+		.source = 1 + R9A06G032_##_src, .name = _n, \
+		.dual = { .group = _g, \
+			.g1 = _g1, .r1 = _r1, .g2 = _g2, .r2 = _r2 }, }
+
+enum { K_GATE = 0, K_FFC, K_DIV, K_BITSEL, K_DUALGATE };
+
+/* Internal clock IDs */
+#define R9A06G032_CLKOUT		0
+#define R9A06G032_CLKOUT_D10		2
+#define R9A06G032_CLKOUT_D16		3
+#define R9A06G032_CLKOUT_D160		4
+#define R9A06G032_CLKOUT_D1OR2		5
+#define R9A06G032_CLKOUT_D20		6
+#define R9A06G032_CLKOUT_D40		7
+#define R9A06G032_CLKOUT_D5		8
+#define R9A06G032_CLKOUT_D8		9
+#define R9A06G032_DIV_ADC		10
+#define R9A06G032_DIV_I2C		11
+#define R9A06G032_DIV_NAND		12
+#define R9A06G032_DIV_P1_PG		13
+#define R9A06G032_DIV_P2_PG		14
+#define R9A06G032_DIV_P3_PG		15
+#define R9A06G032_DIV_P4_PG		16
+#define R9A06G032_DIV_P5_PG		17
+#define R9A06G032_DIV_P6_PG		18
+#define R9A06G032_DIV_QSPI0		19
+#define R9A06G032_DIV_QSPI1		20
+#define R9A06G032_DIV_REF_SYNC		21
+#define R9A06G032_DIV_SDIO0		22
+#define R9A06G032_DIV_SDIO1		23
+#define R9A06G032_DIV_SWITCH		24
+#define R9A06G032_DIV_UART		25
+#define R9A06G032_DIV_MOTOR		64
+#define R9A06G032_CLK_DDRPHY_PLLCLK_D4	78
+#define R9A06G032_CLK_ECAT100_D4	79
+#define R9A06G032_CLK_HSR100_D2		80
+#define R9A06G032_CLK_REF_SYNC_D4	81
+#define R9A06G032_CLK_REF_SYNC_D8	82
+#define R9A06G032_CLK_SERCOS100_D2	83
+#define R9A06G032_DIV_CA7		84
+
+#define R9A06G032_UART_GROUP_012	154
+#define R9A06G032_UART_GROUP_34567	155
+
+#define R9A06G032_CLOCK_COUNT		(R9A06G032_UART_GROUP_34567 + 1)
+
+static const struct r9a06g032_clkdesc r9a06g032_clocks[] = {
+	D_ROOT(CLKOUT, "clkout", 25, 1),
+	D_ROOT(CLK_PLL_USB, "clk_pll_usb", 12, 10),
+	D_FFC(CLKOUT_D10, "clkout_d10", CLKOUT, 10),
+	D_FFC(CLKOUT_D16, "clkout_d16", CLKOUT, 16),
+	D_FFC(CLKOUT_D160, "clkout_d160", CLKOUT, 160),
+	D_DIV(CLKOUT_D1OR2, "clkout_d1or2", CLKOUT, 0, 1, 2),
+	D_FFC(CLKOUT_D20, "clkout_d20", CLKOUT, 20),
+	D_FFC(CLKOUT_D40, "clkout_d40", CLKOUT, 40),
+	D_FFC(CLKOUT_D5, "clkout_d5", CLKOUT, 5),
+	D_FFC(CLKOUT_D8, "clkout_d8", CLKOUT, 8),
+	D_DIV(DIV_ADC, "div_adc", CLKOUT, 77, 50, 250),
+	D_DIV(DIV_I2C, "div_i2c", CLKOUT, 78, 12, 16),
+	D_DIV(DIV_NAND, "div_nand", CLKOUT, 82, 12, 32),
+	D_DIV(DIV_P1_PG, "div_p1_pg", CLKOUT, 68, 12, 200),
+	D_DIV(DIV_P2_PG, "div_p2_pg", CLKOUT, 62, 12, 128),
+	D_DIV(DIV_P3_PG, "div_p3_pg", CLKOUT, 64, 8, 128),
+	D_DIV(DIV_P4_PG, "div_p4_pg", CLKOUT, 66, 8, 128),
+	D_DIV(DIV_P5_PG, "div_p5_pg", CLKOUT, 71, 10, 40),
+	D_DIV(DIV_P6_PG, "div_p6_pg", CLKOUT, 18, 12, 64),
+	D_DIV(DIV_QSPI0, "div_qspi0", CLKOUT, 73, 3, 7),
+	D_DIV(DIV_QSPI1, "div_qspi1", CLKOUT, 25, 3, 7),
+	D_DIV(DIV_REF_SYNC, "div_ref_sync", CLKOUT, 56, 2, 16, 2, 4, 8, 16),
+	D_DIV(DIV_SDIO0, "div_sdio0", CLKOUT, 74, 20, 128),
+	D_DIV(DIV_SDIO1, "div_sdio1", CLKOUT, 75, 20, 128),
+	D_DIV(DIV_SWITCH, "div_switch", CLKOUT, 37, 5, 40),
+	D_DIV(DIV_UART, "div_uart", CLKOUT, 79, 12, 128),
+	D_GATE(CLK_25_PG4, "clk_25_pg4", CLKOUT_D40, 0x749, 0x74a, 0x74b, 0, 0xae3, 0, 0),
+	D_GATE(CLK_25_PG5, "clk_25_pg5", CLKOUT_D40, 0x74c, 0x74d, 0x74e, 0, 0xae4, 0, 0),
+	D_GATE(CLK_25_PG6, "clk_25_pg6", CLKOUT_D40, 0x74f, 0x750, 0x751, 0, 0xae5, 0, 0),
+	D_GATE(CLK_25_PG7, "clk_25_pg7", CLKOUT_D40, 0x752, 0x753, 0x754, 0, 0xae6, 0, 0),
+	D_GATE(CLK_25_PG8, "clk_25_pg8", CLKOUT_D40, 0x755, 0x756, 0x757, 0, 0xae7, 0, 0),
+	D_GATE(CLK_ADC, "clk_adc", DIV_ADC, 0x1ea, 0x1eb, 0, 0, 0, 0, 0),
+	D_GATE(CLK_ECAT100, "clk_ecat100", CLKOUT_D10, 0x405, 0, 0, 0, 0, 0, 0),
+	D_GATE(CLK_HSR100, "clk_hsr100", CLKOUT_D10, 0x483, 0, 0, 0, 0, 0, 0),
+	D_GATE(CLK_I2C0, "clk_i2c0", DIV_I2C, 0x1e6, 0x1e7, 0, 0, 0, 0, 0),
+	D_GATE(CLK_I2C1, "clk_i2c1", DIV_I2C, 0x1e8, 0x1e9, 0, 0, 0, 0, 0),
+	D_GATE(CLK_MII_REF, "clk_mii_ref", CLKOUT_D40, 0x342, 0, 0, 0, 0, 0, 0),
+	D_GATE(CLK_NAND, "clk_nand", DIV_NAND, 0x284, 0x285, 0, 0, 0, 0, 0),
+	D_GATE(CLK_NOUSBP2_PG6, "clk_nousbp2_pg6", DIV_P2_PG, 0x774, 0x775, 0, 0, 0, 0, 0),
+	D_GATE(CLK_P1_PG2, "clk_p1_pg2", DIV_P1_PG, 0x862, 0x863, 0, 0, 0, 0, 0),
+	D_GATE(CLK_P1_PG3, "clk_p1_pg3", DIV_P1_PG, 0x864, 0x865, 0, 0, 0, 0, 0),
+	D_GATE(CLK_P1_PG4, "clk_p1_pg4", DIV_P1_PG, 0x866, 0x867, 0, 0, 0, 0, 0),
+	D_GATE(CLK_P4_PG3, "clk_p4_pg3", DIV_P4_PG, 0x824, 0x825, 0, 0, 0, 0, 0),
+	D_GATE(CLK_P4_PG4, "clk_p4_pg4", DIV_P4_PG, 0x826, 0x827, 0, 0, 0, 0, 0),
+	D_GATE(CLK_P6_PG1, "clk_p6_pg1", DIV_P6_PG, 0x8a0, 0x8a1, 0x8a2, 0, 0xb60, 0, 0),
+	D_GATE(CLK_P6_PG2, "clk_p6_pg2", DIV_P6_PG, 0x8a3, 0x8a4, 0x8a5, 0, 0xb61, 0, 0),
+	D_GATE(CLK_P6_PG3, "clk_p6_pg3", DIV_P6_PG, 0x8a6, 0x8a7, 0x8a8, 0, 0xb62, 0, 0),
+	D_GATE(CLK_P6_PG4, "clk_p6_pg4", DIV_P6_PG, 0x8a9, 0x8aa, 0x8ab, 0, 0xb63, 0, 0),
+	D_MODULE(CLK_PCI_USB, "clk_pci_usb", CLKOUT_D40, 0xe6, 0, 0, 0, 0, 0, 0),
+	D_GATE(CLK_QSPI0, "clk_qspi0", DIV_QSPI0, 0x2a4, 0x2a5, 0, 0, 0, 0, 0),
+	D_GATE(CLK_QSPI1, "clk_qspi1", DIV_QSPI1, 0x484, 0x485, 0, 0, 0, 0, 0),
+	D_GATE(CLK_RGMII_REF, "clk_rgmii_ref", CLKOUT_D8, 0x340, 0, 0, 0, 0, 0, 0),
+	D_GATE(CLK_RMII_REF, "clk_rmii_ref", CLKOUT_D20, 0x341, 0, 0, 0, 0, 0, 0),
+	D_GATE(CLK_SDIO0, "clk_sdio0", DIV_SDIO0, 0x64, 0, 0, 0, 0, 0, 0),
+	D_GATE(CLK_SDIO1, "clk_sdio1", DIV_SDIO1, 0x644, 0, 0, 0, 0, 0, 0),
+	D_GATE(CLK_SERCOS100, "clk_sercos100", CLKOUT_D10, 0x425, 0, 0, 0, 0, 0, 0),
+	D_GATE(CLK_SLCD, "clk_slcd", DIV_P1_PG, 0x860, 0x861, 0, 0, 0, 0, 0),
+	D_GATE(CLK_SPI0, "clk_spi0", DIV_P3_PG, 0x7e0, 0x7e1, 0, 0, 0, 0, 0),
+	D_GATE(CLK_SPI1, "clk_spi1", DIV_P3_PG, 0x7e2, 0x7e3, 0, 0, 0, 0, 0),
+	D_GATE(CLK_SPI2, "clk_spi2", DIV_P3_PG, 0x7e4, 0x7e5, 0, 0, 0, 0, 0),
+	D_GATE(CLK_SPI3, "clk_spi3", DIV_P3_PG, 0x7e6, 0x7e7, 0, 0, 0, 0, 0),
+	D_GATE(CLK_SPI4, "clk_spi4", DIV_P4_PG, 0x820, 0x821, 0, 0, 0, 0, 0),
+	D_GATE(CLK_SPI5, "clk_spi5", DIV_P4_PG, 0x822, 0x823, 0, 0, 0, 0, 0),
+	D_GATE(CLK_SWITCH, "clk_switch", DIV_SWITCH, 0x982, 0x983, 0, 0, 0, 0, 0),
+	D_DIV(DIV_MOTOR, "div_motor", CLKOUT_D5, 84, 2, 8),
+	D_MODULE(HCLK_ECAT125, "hclk_ecat125", CLKOUT_D8, 0x400, 0x401, 0, 0x402, 0, 0x440, 0x441),
+	D_MODULE(HCLK_PINCONFIG, "hclk_pinconfig", CLKOUT_D40, 0x740, 0x741, 0x742, 0, 0xae0, 0, 0),
+	D_MODULE(HCLK_SERCOS, "hclk_sercos", CLKOUT_D10, 0x420, 0x422, 0, 0x421, 0, 0x460, 0x461),
+	D_MODULE(HCLK_SGPIO2, "hclk_sgpio2", DIV_P5_PG, 0x8c3, 0x8c4, 0x8c5, 0, 0xb41, 0, 0),
+	D_MODULE(HCLK_SGPIO3, "hclk_sgpio3", DIV_P5_PG, 0x8c6, 0x8c7, 0x8c8, 0, 0xb42, 0, 0),
+	D_MODULE(HCLK_SGPIO4, "hclk_sgpio4", DIV_P5_PG, 0x8c9, 0x8ca, 0x8cb, 0, 0xb43, 0, 0),
+	D_MODULE(HCLK_TIMER0, "hclk_timer0", CLKOUT_D40, 0x743, 0x744, 0x745, 0, 0xae1, 0, 0),
+	D_MODULE(HCLK_TIMER1, "hclk_timer1", CLKOUT_D40, 0x746, 0x747, 0x748, 0, 0xae2, 0, 0),
+	D_MODULE(HCLK_USBF, "hclk_usbf", CLKOUT_D8, 0xe3, 0, 0, 0xe4, 0, 0x102, 0x103),
+	D_MODULE(HCLK_USBH, "hclk_usbh", CLKOUT_D8, 0xe0, 0xe1, 0, 0xe2, 0, 0x100, 0x101),
+	D_MODULE(HCLK_USBPM, "hclk_usbpm", CLKOUT_D8, 0xe5, 0, 0, 0, 0, 0, 0),
+	D_GATE(CLK_48_PG_F, "clk_48_pg_f", CLK_48, 0x78c, 0x78d, 0, 0x78e, 0, 0xb04, 0xb05),
+	D_GATE(CLK_48_PG4, "clk_48_pg4", CLK_48, 0x789, 0x78a, 0x78b, 0, 0xb03, 0, 0),
+	D_FFC(CLK_DDRPHY_PLLCLK_D4, "clk_ddrphy_pllclk_d4", CLK_DDRPHY_PLLCLK, 4),
+	D_FFC(CLK_ECAT100_D4, "clk_ecat100_d4", CLK_ECAT100, 4),
+	D_FFC(CLK_HSR100_D2, "clk_hsr100_d2", CLK_HSR100, 2),
+	D_FFC(CLK_REF_SYNC_D4, "clk_ref_sync_d4", CLK_REF_SYNC, 4),
+	D_FFC(CLK_REF_SYNC_D8, "clk_ref_sync_d8", CLK_REF_SYNC, 8),
+	D_FFC(CLK_SERCOS100_D2, "clk_sercos100_d2", CLK_SERCOS100, 2),
+	D_DIV(DIV_CA7, "div_ca7", CLK_REF_SYNC, 57, 1, 4, 1, 2, 4),
+	D_MODULE(HCLK_CAN0, "hclk_can0", CLK_48, 0x783, 0x784, 0x785, 0, 0xb01, 0, 0),
+	D_MODULE(HCLK_CAN1, "hclk_can1", CLK_48, 0x786, 0x787, 0x788, 0, 0xb02, 0, 0),
+	D_MODULE(HCLK_DELTASIGMA, "hclk_deltasigma", DIV_MOTOR, 0x1ef, 0x1f0, 0x1f1, 0, 0, 0, 0),
+	D_MODULE(HCLK_PWMPTO, "hclk_pwmpto", DIV_MOTOR, 0x1ec, 0x1ed, 0x1ee, 0, 0, 0, 0),
+	D_MODULE(HCLK_RSV, "hclk_rsv", CLK_48, 0x780, 0x781, 0x782, 0, 0xb00, 0, 0),
+	D_MODULE(HCLK_SGPIO0, "hclk_sgpio0", DIV_MOTOR, 0x1e0, 0x1e1, 0x1e2, 0, 0, 0, 0),
+	D_MODULE(HCLK_SGPIO1, "hclk_sgpio1", DIV_MOTOR, 0x1e3, 0x1e4, 0x1e5, 0, 0, 0, 0),
+	D_DIV(RTOS_MDC, "rtos_mdc", CLK_REF_SYNC, 100, 80, 640, 80, 160, 320, 640),
+	D_GATE(CLK_CM3, "clk_cm3", CLK_REF_SYNC_D4, 0xba0, 0xba1, 0, 0xba2, 0, 0xbc0, 0xbc1),
+	D_GATE(CLK_DDRC, "clk_ddrc", CLK_DDRPHY_PLLCLK_D4, 0x323, 0x324, 0, 0, 0, 0, 0),
+	D_GATE(CLK_ECAT25, "clk_ecat25", CLK_ECAT100_D4, 0x403, 0x404, 0, 0, 0, 0, 0),
+	D_GATE(CLK_HSR50, "clk_hsr50", CLK_HSR100_D2, 0x484, 0x485, 0, 0, 0, 0, 0),
+	D_GATE(CLK_HW_RTOS, "clk_hw_rtos", CLK_REF_SYNC_D4, 0xc60, 0xc61, 0, 0, 0, 0, 0),
+	D_GATE(CLK_SERCOS50, "clk_sercos50", CLK_SERCOS100_D2, 0x424, 0x423, 0, 0, 0, 0, 0),
+	D_MODULE(HCLK_ADC, "hclk_adc", CLK_REF_SYNC_D8, 0x1af, 0x1b0, 0x1b1, 0, 0, 0, 0),
+	D_MODULE(HCLK_CM3, "hclk_cm3", CLK_REF_SYNC_D4, 0xc20, 0xc21, 0xc22, 0, 0, 0, 0),
+	D_MODULE(HCLK_CRYPTO_EIP150, "hclk_crypto_eip150", CLK_REF_SYNC_D4, 0x123, 0x124, 0x125, 0, 0x142, 0, 0),
+	D_MODULE(HCLK_CRYPTO_EIP93, "hclk_crypto_eip93", CLK_REF_SYNC_D4, 0x120, 0x121, 0, 0x122, 0, 0x140, 0x141),
+	D_MODULE(HCLK_DDRC, "hclk_ddrc", CLK_REF_SYNC_D4, 0x320, 0x322, 0, 0x321, 0, 0x3a0, 0x3a1),
+	D_MODULE(HCLK_DMA0, "hclk_dma0", CLK_REF_SYNC_D4, 0x260, 0x261, 0x262, 0x263, 0x2c0, 0x2c1, 0x2c2),
+	D_MODULE(HCLK_DMA1, "hclk_dma1", CLK_REF_SYNC_D4, 0x264, 0x265, 0x266, 0x267, 0x2c3, 0x2c4, 0x2c5),
+	D_MODULE(HCLK_GMAC0, "hclk_gmac0", CLK_REF_SYNC_D4, 0x360, 0x361, 0x362, 0x363, 0x3c0, 0x3c1, 0x3c2),
+	D_MODULE(HCLK_GMAC1, "hclk_gmac1", CLK_REF_SYNC_D4, 0x380, 0x381, 0x382, 0x383, 0x3e0, 0x3e1, 0x3e2),
+	D_MODULE(HCLK_GPIO0, "hclk_gpio0", CLK_REF_SYNC_D4, 0x212, 0x213, 0x214, 0, 0, 0, 0),
+	D_MODULE(HCLK_GPIO1, "hclk_gpio1", CLK_REF_SYNC_D4, 0x215, 0x216, 0x217, 0, 0, 0, 0),
+	D_MODULE(HCLK_GPIO2, "hclk_gpio2", CLK_REF_SYNC_D4, 0x229, 0x22a, 0x22b, 0, 0, 0, 0),
+	D_MODULE(HCLK_HSR, "hclk_hsr", CLK_HSR100_D2, 0x480, 0x482, 0, 0x481, 0, 0x4c0, 0x4c1),
+	D_MODULE(HCLK_I2C0, "hclk_i2c0", CLK_REF_SYNC_D8, 0x1a9, 0x1aa, 0x1ab, 0, 0, 0, 0),
+	D_MODULE(HCLK_I2C1, "hclk_i2c1", CLK_REF_SYNC_D8, 0x1ac, 0x1ad, 0x1ae, 0, 0, 0, 0),
+	D_MODULE(HCLK_LCD, "hclk_lcd", CLK_REF_SYNC_D4, 0x7a0, 0x7a1, 0x7a2, 0, 0xb20, 0, 0),
+	D_MODULE(HCLK_MSEBI_M, "hclk_msebi_m", CLK_REF_SYNC_D4, 0x164, 0x165, 0x166, 0, 0x183, 0, 0),
+	D_MODULE(HCLK_MSEBI_S, "hclk_msebi_s", CLK_REF_SYNC_D4, 0x160, 0x161, 0x162, 0x163, 0x180, 0x181, 0x182),
+	D_MODULE(HCLK_NAND, "hclk_nand", CLK_REF_SYNC_D4, 0x280, 0x281, 0x282, 0x283, 0x2e0, 0x2e1, 0x2e2),
+	D_MODULE(HCLK_PG_I, "hclk_pg_i", CLK_REF_SYNC_D4, 0x7ac, 0x7ad, 0, 0x7ae, 0, 0xb24, 0xb25),
+	D_MODULE(HCLK_PG19, "hclk_pg19", CLK_REF_SYNC_D4, 0x22c, 0x22d, 0x22e, 0, 0, 0, 0),
+	D_MODULE(HCLK_PG20, "hclk_pg20", CLK_REF_SYNC_D4, 0x22f, 0x230, 0x231, 0, 0, 0, 0),
+	D_MODULE(HCLK_PG3, "hclk_pg3", CLK_REF_SYNC_D4, 0x7a6, 0x7a7, 0x7a8, 0, 0xb22, 0, 0),
+	D_MODULE(HCLK_PG4, "hclk_pg4", CLK_REF_SYNC_D4, 0x7a9, 0x7aa, 0x7ab, 0, 0xb23, 0, 0),
+	D_MODULE(HCLK_QSPI0, "hclk_qspi0", CLK_REF_SYNC_D4, 0x2a0, 0x2a1, 0x2a2, 0x2a3, 0x300, 0x301, 0x302),
+	D_MODULE(HCLK_QSPI1, "hclk_qspi1", CLK_REF_SYNC_D4, 0x480, 0x481, 0x482, 0x483, 0x4c0, 0x4c1, 0x4c2),
+	D_MODULE(HCLK_ROM, "hclk_rom", CLK_REF_SYNC_D4, 0xaa0, 0xaa1, 0xaa2, 0, 0xb80, 0, 0),
+	D_MODULE(HCLK_RTC, "hclk_rtc", CLK_REF_SYNC_D8, 0xa00, 0, 0, 0, 0, 0, 0),
+	D_MODULE(HCLK_SDIO0, "hclk_sdio0", CLK_REF_SYNC_D4, 0x60, 0x61, 0x62, 0x63, 0x80, 0x81, 0x82),
+	D_MODULE(HCLK_SDIO1, "hclk_sdio1", CLK_REF_SYNC_D4, 0x640, 0x641, 0x642, 0x643, 0x660, 0x661, 0x662),
+	D_MODULE(HCLK_SEMAP, "hclk_semap", CLK_REF_SYNC_D4, 0x7a3, 0x7a4, 0x7a5, 0, 0xb21, 0, 0),
+	D_MODULE(HCLK_SPI0, "hclk_spi0", CLK_REF_SYNC_D4, 0x200, 0x201, 0x202, 0, 0, 0, 0),
+	D_MODULE(HCLK_SPI1, "hclk_spi1", CLK_REF_SYNC_D4, 0x203, 0x204, 0x205, 0, 0, 0, 0),
+	D_MODULE(HCLK_SPI2, "hclk_spi2", CLK_REF_SYNC_D4, 0x206, 0x207, 0x208, 0, 0, 0, 0),
+	D_MODULE(HCLK_SPI3, "hclk_spi3", CLK_REF_SYNC_D4, 0x209, 0x20a, 0x20b, 0, 0, 0, 0),
+	D_MODULE(HCLK_SPI4, "hclk_spi4", CLK_REF_SYNC_D4, 0x20c, 0x20d, 0x20e, 0, 0, 0, 0),
+	D_MODULE(HCLK_SPI5, "hclk_spi5", CLK_REF_SYNC_D4, 0x20f, 0x210, 0x211, 0, 0, 0, 0),
+	D_MODULE(HCLK_SWITCH, "hclk_switch", CLK_REF_SYNC_D4, 0x980, 0, 0x981, 0, 0, 0, 0),
+	D_MODULE(HCLK_SWITCH_RG, "hclk_switch_rg", CLK_REF_SYNC_D4, 0xc40, 0xc41, 0xc42, 0, 0, 0, 0),
+	D_MODULE(HCLK_UART0, "hclk_uart0", CLK_REF_SYNC_D8, 0x1a0, 0x1a1, 0x1a2, 0, 0, 0, 0),
+	D_MODULE(HCLK_UART1, "hclk_uart1", CLK_REF_SYNC_D8, 0x1a3, 0x1a4, 0x1a5, 0, 0, 0, 0),
+	D_MODULE(HCLK_UART2, "hclk_uart2", CLK_REF_SYNC_D8, 0x1a6, 0x1a7, 0x1a8, 0, 0, 0, 0),
+	D_MODULE(HCLK_UART3, "hclk_uart3", CLK_REF_SYNC_D4, 0x218, 0x219, 0x21a, 0, 0, 0, 0),
+	D_MODULE(HCLK_UART4, "hclk_uart4", CLK_REF_SYNC_D4, 0x21b, 0x21c, 0x21d, 0, 0, 0, 0),
+	D_MODULE(HCLK_UART5, "hclk_uart5", CLK_REF_SYNC_D4, 0x220, 0x221, 0x222, 0, 0, 0, 0),
+	D_MODULE(HCLK_UART6, "hclk_uart6", CLK_REF_SYNC_D4, 0x223, 0x224, 0x225, 0, 0, 0, 0),
+	D_MODULE(HCLK_UART7, "hclk_uart7", CLK_REF_SYNC_D4, 0x226, 0x227, 0x228, 0, 0, 0, 0),
+	/*
+	 * These are not hardware clocks, but are needed to handle the special
+	 * case where we have a 'selector bit' that doesn't just change the
+	 * parent for a clock, but also the gate it's supposed to use.
+	 */
+	{
+		.index = R9A06G032_UART_GROUP_012,
+		.name = "uart_group_012",
+		.type = K_BITSEL,
+		.source = 1 + R9A06G032_DIV_UART,
+		/* R9A06G032_SYSCTRL_REG_PWRCTRL_PG0_0 */
+		.dual.sel = ((0x34 / 4) << 5) | 30,
+		.dual.group = 0,
+	},
+	{
+		.index = R9A06G032_UART_GROUP_34567,
+		.name = "uart_group_34567",
+		.type = K_BITSEL,
+		.source = 1 + R9A06G032_DIV_P2_PG,
+		/* R9A06G032_SYSCTRL_REG_PWRCTRL_PG1_PR2 */
+		.dual.sel = ((0xec / 4) << 5) | 24,
+		.dual.group = 1,
+	},
+	D_UGATE(CLK_UART0, "clk_uart0", UART_GROUP_012, 0, 0x1b2, 0x1b3, 0x1b4, 0x1b5),
+	D_UGATE(CLK_UART1, "clk_uart1", UART_GROUP_012, 0, 0x1b6, 0x1b7, 0x1b8, 0x1b9),
+	D_UGATE(CLK_UART2, "clk_uart2", UART_GROUP_012, 0, 0x1ba, 0x1bb, 0x1bc, 0x1bd),
+	D_UGATE(CLK_UART3, "clk_uart3", UART_GROUP_34567, 1, 0x760, 0x761, 0x762, 0x763),
+	D_UGATE(CLK_UART4, "clk_uart4", UART_GROUP_34567, 1, 0x764, 0x765, 0x766, 0x767),
+	D_UGATE(CLK_UART5, "clk_uart5", UART_GROUP_34567, 1, 0x768, 0x769, 0x76a, 0x76b),
+	D_UGATE(CLK_UART6, "clk_uart6", UART_GROUP_34567, 1, 0x76c, 0x76d, 0x76e, 0x76f),
+	D_UGATE(CLK_UART7, "clk_uart7", UART_GROUP_34567, 1, 0x770, 0x771, 0x772, 0x773),
+};
+
+struct r9a06g032_priv {
+	struct regmap		*regmap;
+	struct clk		mclk;
+};
+
+static const struct r9a06g032_clkdesc *r9a06g032_clk_get(struct clk *clk)
+{
+	const unsigned long clkid = clk->id & 0xffff;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(r9a06g032_clocks); i++) {
+		if (r9a06g032_clocks[i].index == clkid)
+			return &r9a06g032_clocks[i];
+	}
+
+	return NULL;
+}
+
+static int r9a06g032_clk_get_parent(struct clk *clk, struct clk *parent)
+{
+	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
+
+	if (!desc)
+		return -ENODEV;
+
+	if (desc->source == 0)
+		parent->id = ~0;	/* Top-level clock */
+	else
+		parent->id = desc->source - 1;
+
+	parent->dev = clk->dev;
+
+	return 0;
+}
+
+static ulong r9a06g032_clk_get_parent_rate(struct clk *clk)
+{
+	struct clk parent;
+
+	if (r9a06g032_clk_get_parent(clk, &parent)) {
+		debug("Failed to get parent clock for id=%lu\b", clk->id);
+		return 0;
+	}
+
+	if (parent.id == ~0) {
+		struct r9a06g032_priv *clocks = dev_get_priv(clk->dev);
+		ulong rate = clk_get_rate(&clocks->mclk);
+		return rate;
+	}
+
+	return clk_get_rate(&parent);
+}
+
+/* register/bit pairs are encoded as an uint16_t */
+static void
+clk_rdesc_set(struct r9a06g032_priv *clocks,
+	      u16 one, unsigned int on)
+{
+	uint offset = 4 * (one >> 5);
+	uint mask = 1U << (one & 0x1f);
+	uint val = ((!!on) << (one & 0x1f));
+
+	regmap_update_bits(clocks->regmap, offset, mask, val);
+}
+
+static int
+clk_rdesc_get(struct r9a06g032_priv *clocks,
+	      uint16_t one)
+{
+	uint offset = 4 * (one >> 5);
+	u32 val = 0;
+
+	regmap_read(clocks->regmap, offset, &val);
+
+	return !!(val & (1U << (one & 0x1f)));
+}
+
+/*
+ * Cheating a little bit here: leverage the existing code to control the
+ * per-clock reset. It should really be handled by a reset controller instead.
+ */
+void clk_rzn1_reset_state(struct clk *clk, int on)
+{
+	struct r9a06g032_priv *clocks = dev_get_priv(clk->dev);
+	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
+	assert(desc);
+	assert(desc->type == K_GATE);
+	const struct r9a06g032_gate *g = &desc->gate;
+	assert(g->reset);
+
+	clk_rdesc_set(clocks, g->reset, on);
+}
+
+/*
+ * This implements the R9A06G032 clock gate 'driver'. We cannot use the system's
+ * clock gate framework as the gates on the R9A06G032 have a special enabling
+ * sequence, therefore we use this little proxy.
+ */
+static int r9a06g032_clk_gate_set(struct clk *clk, int on)
+{
+	struct r9a06g032_priv *clocks = dev_get_priv(clk->dev);
+	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
+	assert(desc);
+	assert(desc->type == K_GATE);
+	const struct r9a06g032_gate *g = &desc->gate;
+
+	clk_rdesc_set(clocks, g->gate, on);
+	/* De-assert reset */
+	if (g->reset)
+		clk_rdesc_set(clocks, g->reset, 1);
+
+	/* Hardware manual recommends 5us delay after enabling clock & reset */
+	udelay(5);
+
+	/* If the peripheral is memory mapped (i.e. an AXI slave), there is an
+	 * associated SLVRDY bit in the System Controller that needs to be set
+	 * so that the FlexWAY bus fabric passes on the read/write requests.
+	 */
+	if (g->ready || g->midle) {
+		if (g->ready)
+			clk_rdesc_set(clocks, g->ready, on);
+		/* Clear 'Master Idle Request' bit */
+		if (g->midle)
+			clk_rdesc_set(clocks, g->midle, !on);
+	}
+	/* Note: We don't wait for FlexWAY Socket Connection signal */
+
+	return 0;
+}
+
+static int r9a06g032_clk_gate_enable(struct clk *clk)
+{
+	return r9a06g032_clk_gate_set(clk, 1);
+}
+
+static int r9a06g032_clk_gate_disable(struct clk *clk)
+{
+	return r9a06g032_clk_gate_set(clk, 0);
+}
+
+/*
+ * Fixed factor clock
+ */
+static ulong r9a06g032_ffc_get_rate(struct clk *clk)
+{
+	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
+	unsigned long parent_rate = r9a06g032_clk_get_parent_rate(clk);
+	unsigned long long rate;
+
+	if (parent_rate == 0) {
+		debug("%s: parent_rate is zero\n", __func__);
+		return 0;
+	}
+
+	rate = (unsigned long long)parent_rate * desc->mul;
+	rate = DIV_ROUND_UP(rate, desc->div);
+	return (ulong)rate;
+}
+
+/*
+ * This implements R9A06G032 clock divider 'driver'. This differs from the
+ * standard clk_divider because the set_rate method must also set b[31] to
+ * trigger the hardware rate change. In theory it should also wait for this
+ * bit to clear.
+ */
+static ulong r9a06g032_div_get_rate(struct clk *clk)
+{
+	struct r9a06g032_priv *clocks = dev_get_priv(clk->dev);
+	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
+	unsigned long parent_rate = r9a06g032_clk_get_parent_rate(clk);
+	u32 div = 0;
+
+	if (parent_rate == 0) {
+		debug("%s: parent_rate is zero\n", __func__);
+		return 0;
+	}
+
+	regmap_read(clocks->regmap, 4 * desc->reg, &div);
+
+	if (div < desc->div_min)
+		div = desc->div_min;
+	else if (div > desc->div_max)
+		div = desc->div_max;
+	return DIV_ROUND_UP(parent_rate, div);
+}
+
+static ulong r9a06g032_div_set_rate(struct clk *clk, ulong rate)
+{
+	struct r9a06g032_priv *clocks = dev_get_priv(clk->dev);
+	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
+	unsigned long parent_rate = r9a06g032_clk_get_parent_rate(clk);
+
+	if (parent_rate == 0) {
+		debug("%s: parent_rate is zero\n", __func__);
+		return 0;
+	}
+
+	/* + 1 to cope with rates that have the remainder dropped */
+	u32 div = DIV_ROUND_UP(parent_rate, rate + 1);
+
+	/* Clamp to allowable range */
+	if (div < desc->div_min)
+		div = desc->div_min;
+	else if (div > desc->div_max)
+		div = desc->div_max;
+
+	/* TODO: use the .div_table if provided */
+	if (desc->div_table[0])
+		pr_err("ERROR: %s: div_table not implemented\n", __func__);
+
+	pr_devel("%s clkid %lu rate %ld parent %ld div %d\n", __func__, clk->id,
+		 rate, parent_rate, div);
+
+	/*
+	 * Need to write the bit 31 with the divider value to
+	 * latch it. Technically we should wait until it has been
+	 * cleared too.
+	 * TODO: Find whether this callback is sleepable, in case
+	 * the hardware /does/ require some sort of spinloop here.
+	 */
+	regmap_write(clocks->regmap, 4 * desc->reg, div | BIT(31));
+
+	return 0;
+}
+
+/*
+ * Dual gate. This handles toggling the approprate clock/reset bits,
+ * which depends on the mux setting above.
+ */
+static int r9a06g032_clk_dualgate_setenable(struct r9a06g032_priv *clocks,
+					    const struct r9a06g032_clkdesc *desc,
+					    int enable)
+{
+	u8 sel_bit = clk_rdesc_get(clocks, desc->dual.sel);
+	u16 gate[2] = { desc->dual.g1, desc->dual.g2 };
+	u16 reset[2] = { desc->dual.r1, desc->dual.r2 };
+
+	/* we always turn off the 'other' gate, regardless */
+	clk_rdesc_set(clocks, gate[!sel_bit], 0);
+	if (reset[!sel_bit])
+		clk_rdesc_set(clocks, reset[!sel_bit], 1);
+
+	/* set the gate as requested */
+	clk_rdesc_set(clocks, gate[sel_bit], enable);
+	if (reset[sel_bit])
+		clk_rdesc_set(clocks, reset[sel_bit], 1);
+
+	return 0;
+}
+
+static int r9a06g032_clk_dualgate_enable(struct clk *clk)
+{
+	struct r9a06g032_priv *clocks = dev_get_priv(clk->dev);
+	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
+
+	return r9a06g032_clk_dualgate_setenable(clocks, desc, 1);
+}
+
+static int r9a06g032_clk_dualgate_disable(struct clk *clk)
+{
+	struct r9a06g032_priv *clocks = dev_get_priv(clk->dev);
+	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
+
+	return r9a06g032_clk_dualgate_setenable(clocks, desc, 0);
+}
+
+static int r9a06g032_clk_dualgate_is_enabled(struct clk *clk)
+{
+	struct r9a06g032_priv *clocks = dev_get_priv(clk->dev);
+	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
+	u8 sel_bit = clk_rdesc_get(clocks, desc->dual.sel);
+	u16 gate[2] = { desc->dual.g1, desc->dual.g2 };
+
+	return clk_rdesc_get(clocks, gate[sel_bit]);
+}
+
+/*
+ * Main clock driver
+ */
+static int r9a06g032_clk_enable(struct clk *clk)
+{
+	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
+
+	switch (desc->type) {
+	case K_GATE:
+		return r9a06g032_clk_gate_enable(clk);
+	case K_DUALGATE:
+		return r9a06g032_clk_dualgate_enable(clk);
+	default:
+		printf("ERROR: %s:%d unhandled type=%d\n", __func__, __LINE__, desc->type);
+		break;
+	}
+
+	return 0;
+}
+
+static int r9a06g032_clk_disable(struct clk *clk)
+{
+	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
+
+	switch (desc->type) {
+	case K_GATE:
+		return r9a06g032_clk_gate_disable(clk);
+	case K_DUALGATE:
+		return r9a06g032_clk_dualgate_disable(clk);
+	default:
+		printf("ERROR: %s:%d unhandled type=%d\n", __func__, __LINE__, desc->type);
+		break;
+	}
+
+	return 0;
+}
+
+static ulong r9a06g032_clk_get_rate(struct clk *clk)
+{
+	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
+	ulong ret = 0;
+
+	assert(desc);
+
+	switch (desc->type) {
+	case K_FFC:
+		ret = r9a06g032_ffc_get_rate(clk);
+		break;
+	case K_GATE:
+		ret = r9a06g032_clk_get_parent_rate(clk);
+		break;
+	case K_DIV:
+		ret = r9a06g032_div_get_rate(clk);
+		break;
+	case K_BITSEL:
+		/*
+		 * Look at the mux to determine parent.
+		 * 0 means it is coming from UART DIV (group 012 or 34567)
+		 * 1 means it is coming from USB_PLL
+		 */
+		if (r9a06g032_clk_dualgate_is_enabled(clk)) {
+			struct clk clk = { .id = R9A06G032_CLK_PLL_USB };
+			ret = r9a06g032_clk_get_parent_rate(&clk);
+		}
+		ret = r9a06g032_clk_get_parent_rate(clk);
+		break;
+	case K_DUALGATE:
+		ret = r9a06g032_clk_get_parent_rate(clk);
+		break;
+	}
+
+	return ret;
+}
+
+static ulong r9a06g032_clk_set_rate(struct clk *clk, ulong rate)
+{
+	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
+	ulong ret = 0;
+
+	assert(desc);
+
+	switch (desc->type) {
+	case K_DIV:
+		ret = r9a06g032_div_set_rate(clk, rate);
+		break;
+	default:
+		printf("ERROR: %s:%d not implemented yet\n", __func__, __LINE__);
+	};
+
+	return ret;
+}
+
+static int r9a06g032_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
+{
+	if (args->args_count != 1) {
+		debug("Invalid args_count: %d\n", args->args_count);
+		return -EINVAL;
+	}
+
+	clk->id = args->args[0];
+
+	return 0;
+}
+
+static const struct clk_ops r9a06g032_clk_ops = {
+	.enable		= r9a06g032_clk_enable,
+	.disable	= r9a06g032_clk_disable,
+	.get_rate	= r9a06g032_clk_get_rate,
+	.set_rate	= r9a06g032_clk_set_rate,
+	.of_xlate	= r9a06g032_clk_of_xlate,
+};
+
+static int r9a06g032_clk_probe(struct udevice *dev)
+{
+	struct r9a06g032_priv *priv = dev_get_priv(dev);
+	int err;
+
+	priv->regmap = syscon_regmap_lookup_by_phandle(dev, "regmap");
+	if (!priv->regmap) {
+		pr_err("unable to find regmap\n");
+		return -ENODEV;
+	}
+
+	/* Enable S/W reset */
+	regmap_write(priv->regmap, 0x120, 0x41);
+
+	err = clk_get_by_name(dev, "mclk", &priv->mclk);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int r9a06g032_clk_remove(struct udevice *dev)
+{
+	return 0;
+}
+
+static const struct udevice_id r9a06g032_clk_ids[] = {
+	{ .compatible = "renesas,r9a06g032-sysctrl" },
+	{ }
+};
+
+U_BOOT_DRIVER(clk_r9a06g032) = {
+	.name		= "clk_r9a06g032",
+	.id		= UCLASS_CLK,
+	.of_match	= r9a06g032_clk_ids,
+	.priv_auto	= sizeof(struct r9a06g032_priv),
+	.ops		= &r9a06g032_clk_ops,
+	.probe		= &r9a06g032_clk_probe,
+	.remove		= &r9a06g032_clk_remove,
+	.flags		= DM_FLAG_PRE_RELOC,
+};
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v1 4/9] pinctrl: renesas: add R906G032 driver
  2022-08-09 12:59 [RFC PATCH v1 0/9] Renesas RZ/N1 SoC initial support Ralph Siemsen
                   ` (2 preceding siblings ...)
  2022-08-09 12:59 ` [RFC PATCH v1 3/9] clk: renesas: add R906G032 driver Ralph Siemsen
@ 2022-08-09 12:59 ` Ralph Siemsen
  2022-08-09 12:59 ` [RFC PATCH v1 5/9] ram: cadence: add driver for Cadence EDAC Ralph Siemsen
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 29+ messages in thread
From: Ralph Siemsen @ 2022-08-09 12:59 UTC (permalink / raw)
  To: u-boot
  Cc: Ralph Siemsen, Ashok Reddy Soma, Damien Le Moal, Jim Liu,
	Mark Kettenis, Niklas Cassel, Samuel Holland

Pinctrl/pinconf driver for Renesas RZ/N1 (R906G032) SoC.

This is quite rudimentary right now, and only supports applying a
default pin configuration as specified by the device tree.

TODO:

- iterate over subnodes, so we can handle groups of pins with
  different bias/strength settings

- use the regmap_update_bits for level1/level2 register updates
  --> does not exist for regmap_ranges, consider adding?

Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
---

 drivers/pinctrl/Makefile                   |   1 +
 drivers/pinctrl/renesas/Kconfig            |   7 +
 drivers/pinctrl/renesas/Makefile           |   1 +
 drivers/pinctrl/renesas/pinctrl-rzn1.c     | 398 +++++++++++++++++++++
 include/dt-bindings/pinctrl/rzn1-pinctrl.h | 141 ++++++++
 5 files changed, 548 insertions(+)
 create mode 100644 drivers/pinctrl/renesas/pinctrl-rzn1.c
 create mode 100644 include/dt-bindings/pinctrl/rzn1-pinctrl.h

diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 3b167d099f..450267732c 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_PINCTRL_INTEL) += intel/
 obj-$(CONFIG_ARCH_MTMIPS) += mtmips/
 obj-$(CONFIG_ARCH_NPCM)         += nuvoton/
 obj-$(CONFIG_ARCH_RMOBILE) += renesas/
+obj-$(CONFIG_ARCH_RZN1) += renesas/
 obj-$(CONFIG_PINCTRL_SANDBOX)	+= pinctrl-sandbox.o
 obj-$(CONFIG_PINCTRL_SUNXI)	+= sunxi/
 obj-$(CONFIG_PINCTRL_UNIPHIER)	+= uniphier/
diff --git a/drivers/pinctrl/renesas/Kconfig b/drivers/pinctrl/renesas/Kconfig
index 1fedf63252..3fbc293e17 100644
--- a/drivers/pinctrl/renesas/Kconfig
+++ b/drivers/pinctrl/renesas/Kconfig
@@ -121,3 +121,10 @@ config PINCTRL_PFC_R7S72100
 	  Support pin multiplexing control on Renesas RZ/A1 R7S72100 SoCs.
 
 endif
+
+config PINCTRL_RZN1
+	bool "Renesas RZ/N1 R906G032 pin control driver"
+	depends on ARCH_RZN1
+	default y if ARCH_RZN1
+	help
+	  Support pin multiplexing control on Renesas RZ/N1 R906G032 SoCs.
diff --git a/drivers/pinctrl/renesas/Makefile b/drivers/pinctrl/renesas/Makefile
index 1c65505eff..8f26f16098 100644
--- a/drivers/pinctrl/renesas/Makefile
+++ b/drivers/pinctrl/renesas/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_PINCTRL_PFC_R8A77990) += pfc-r8a77990.o
 obj-$(CONFIG_PINCTRL_PFC_R8A77995) += pfc-r8a77995.o
 obj-$(CONFIG_PINCTRL_PFC_R8A779A0) += pfc-r8a779a0.o
 obj-$(CONFIG_PINCTRL_PFC_R7S72100) += pfc-r7s72100.o
+obj-$(CONFIG_PINCTRL_RZN1) += pinctrl-rzn1.o
diff --git a/drivers/pinctrl/renesas/pinctrl-rzn1.c b/drivers/pinctrl/renesas/pinctrl-rzn1.c
new file mode 100644
index 0000000000..39ff1fa8a1
--- /dev/null
+++ b/drivers/pinctrl/renesas/pinctrl-rzn1.c
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2014-2018 Renesas Electronics Europe Limited
+ *
+ * Phil Edworthy <phil.edworthy@renesas.com>
+ * Based on a driver originally written by Michel Pollet at Renesas.
+ */
+
+#include <dt-bindings/pinctrl/rzn1-pinctrl.h>
+
+#include <dm/device.h>
+#include <dm/device_compat.h>
+#include <dm/pinctrl.h>
+#include <dm/read.h>
+#include <regmap.h>
+
+/* Field positions and masks in the pinmux registers */
+#define RZN1_L1_PIN_DRIVE_STRENGTH	10
+#define RZN1_L1_PIN_DRIVE_STRENGTH_4MA	0
+#define RZN1_L1_PIN_DRIVE_STRENGTH_6MA	1
+#define RZN1_L1_PIN_DRIVE_STRENGTH_8MA	2
+#define RZN1_L1_PIN_DRIVE_STRENGTH_12MA	3
+#define RZN1_L1_PIN_PULL		8
+#define RZN1_L1_PIN_PULL_NONE		0
+#define RZN1_L1_PIN_PULL_UP		1
+#define RZN1_L1_PIN_PULL_DOWN		3
+#define RZN1_L1_FUNCTION		0
+#define RZN1_L1_FUNC_MASK		0xf
+#define RZN1_L1_FUNCTION_L2		0xf
+
+/*
+ * The hardware manual describes two levels of multiplexing, but it's more
+ * logical to think of the hardware as three levels, with level 3 consisting of
+ * the multiplexing for Ethernet MDIO signals.
+ *
+ * Level 1 functions go from 0 to 9, with level 1 function '15' (0xf) specifying
+ * that level 2 functions are used instead. Level 2 has a lot more options,
+ * going from 0 to 61. Level 3 allows selection of MDIO functions which can be
+ * floating, or one of seven internal peripherals. Unfortunately, there are two
+ * level 2 functions that can select MDIO, and two MDIO channels so we have four
+ * sets of level 3 functions.
+ *
+ * For this driver, we've compounded the numbers together, so:
+ *    0 to   9 is level 1
+ *   10 to  71 is 10 + level 2 number
+ *   72 to  79 is 72 + MDIO0 source for level 2 MDIO function.
+ *   80 to  87 is 80 + MDIO0 source for level 2 MDIO_E1 function.
+ *   88 to  95 is 88 + MDIO1 source for level 2 MDIO function.
+ *   96 to 103 is 96 + MDIO1 source for level 2 MDIO_E1 function.
+ * Examples:
+ *  Function 28 corresponds UART0
+ *  Function 73 corresponds to MDIO0 to GMAC0
+ *
+ * There are 170 configurable pins (called PL_GPIO in the datasheet).
+ */
+
+/*
+ * Structure detailing the HW registers on the RZ/N1 devices.
+ * Both the Level 1 mux registers and Level 2 mux registers have the same
+ * structure. The only difference is that Level 2 has additional MDIO registers
+ * at the end.
+ */
+struct rzn1_pinctrl_regs {
+	u32	conf[170];
+	u32	pad0[86];
+	u32	status_protect;	/* 0x400 */
+	/* MDIO mux registers, level2 only */
+	u32	l2_mdio[2];
+};
+
+#define NUM_CONF	ARRAY_SIZE(((struct rzn1_pinctrl_regs *)0)->conf)
+
+#define level1_write(map, member, val) \
+	regmap_range_set(map, 0, struct rzn1_pinctrl_regs, member, val)
+
+#define level1_read(map, member, valp) \
+	regmap_range_get(map, 0, struct rzn1_pinctrl_regs, member, valp)
+
+#define level2_write(map, member, val) \
+	regmap_range_set(map, 1, struct rzn1_pinctrl_regs, member, val)
+
+#define level2_read(map, member, valp) \
+	regmap_range_get(map, 1, struct rzn1_pinctrl_regs, member, valp)
+
+/**
+ * struct rzn1_pmx_func - describes rzn1 pinmux functions
+ * @name: the name of this specific function
+ * @groups: corresponding pin groups
+ * @num_groups: the number of groups
+ */
+struct rzn1_pmx_func {
+	const char *name;
+	const char **groups;
+	unsigned int num_groups;
+};
+
+/**
+ * struct rzn1_pin_group - describes an rzn1 pin group
+ * @name: the name of this specific pin group
+ * @func: the name of the function selected by this group
+ * @npins: the number of pins in this group array, i.e. the number of
+ *	elements in .pins so we can iterate over that array
+ * @pins: array of pins. Needed due to pinctrl_ops.get_group_pins()
+ * @pin_ids: array of pin_ids, i.e. the value used to select the mux
+ */
+struct rzn1_pin_group {
+	const char *name;
+	const char *func;
+	unsigned int npins;
+	unsigned int *pins;
+	u8 *pin_ids;
+};
+
+struct rzn1_pinctrl {
+	struct device *dev;
+	struct clk *clk;
+	struct pinctrl_dev *pctl;
+	u32 lev1_protect_phys;
+	u32 lev2_protect_phys;
+	int mdio_func[2];
+
+	struct rzn1_pin_group *groups;
+	unsigned int ngroups;
+
+	struct rzn1_pmx_func *functions;
+	unsigned int nfunctions;
+};
+
+struct rzn1_pinctrl_priv {
+	struct regmap *regmap;
+	u32 lev1_protect_phys;
+	u32 lev2_protect_phys;
+
+	struct clk *clk;
+};
+
+enum {
+	LOCK_LEVEL1 = 0x1,
+	LOCK_LEVEL2 = 0x2,
+	LOCK_ALL = LOCK_LEVEL1 | LOCK_LEVEL2,
+};
+
+static void rzn1_hw_set_lock(struct rzn1_pinctrl_priv *priv, u8 lock, u8 value)
+{
+	/*
+	 * The pinmux configuration is locked by writing the physical address of
+	 * the status_protect register to itself. It is unlocked by writing the
+	 * address | 1.
+	 */
+	if (lock & LOCK_LEVEL1) {
+		u32 val = priv->lev1_protect_phys | !(value & LOCK_LEVEL1);
+
+		level1_write(priv->regmap, status_protect, val);
+	}
+
+	if (lock & LOCK_LEVEL2) {
+		u32 val = priv->lev2_protect_phys | !(value & LOCK_LEVEL2);
+
+		level2_write(priv->regmap, status_protect, val);
+	}
+}
+
+static void rzn1_pinctrl_mdio_select(struct rzn1_pinctrl_priv *priv, int mdio,
+				     u32 func)
+{
+	debug("setting mdio%d to %u\n", mdio, func);
+
+	level2_write(priv->regmap, l2_mdio[mdio], func);
+}
+
+/*
+ * Using a composite pin description, set the hardware pinmux registers
+ * with the corresponding values.
+ * Make sure to unlock write protection and reset it afterward.
+ *
+ * NOTE: There is no protection for potential concurrency, it is assumed these
+ * calls are serialized already.
+ */
+static int rzn1_set_hw_pin_func(struct rzn1_pinctrl_priv *priv,
+				unsigned int pin, unsigned int func)
+{
+	u32 l1_cache;
+	u32 l2_cache;
+	u32 l1;
+	u32 l2;
+
+	/* Level 3 MDIO multiplexing */
+	if (func >= RZN1_FUNC_MDIO0_HIGHZ &&
+	    func <= RZN1_FUNC_MDIO1_E1_SWITCH) {
+		int mdio_channel;
+		u32 mdio_func;
+
+		if (func <= RZN1_FUNC_MDIO1_HIGHZ)
+			mdio_channel = 0;
+		else
+			mdio_channel = 1;
+
+		/* Get MDIO func, and convert the func to the level 2 number */
+		if (func <= RZN1_FUNC_MDIO0_SWITCH) {
+			mdio_func = func - RZN1_FUNC_MDIO0_HIGHZ;
+			func = RZN1_FUNC_ETH_MDIO;
+		} else if (func <= RZN1_FUNC_MDIO0_E1_SWITCH) {
+			mdio_func = func - RZN1_FUNC_MDIO0_E1_HIGHZ;
+			func = RZN1_FUNC_ETH_MDIO_E1;
+		} else if (func <= RZN1_FUNC_MDIO1_SWITCH) {
+			mdio_func = func - RZN1_FUNC_MDIO1_HIGHZ;
+			func = RZN1_FUNC_ETH_MDIO;
+		} else {
+			mdio_func = func - RZN1_FUNC_MDIO1_E1_HIGHZ;
+			func = RZN1_FUNC_ETH_MDIO_E1;
+		}
+		rzn1_pinctrl_mdio_select(priv, mdio_channel, mdio_func);
+	}
+
+	/* Note here, we do not allow anything past the MDIO Mux values */
+	if (pin >= NUM_CONF || func >= RZN1_FUNC_MDIO0_HIGHZ)
+		return -EINVAL;
+
+	level1_read(priv->regmap, conf[pin], &l1);
+	l1_cache = l1;
+	level2_read(priv->regmap, conf[pin], &l2);
+	l2_cache = l2;
+
+	debug("setting func for pin %u to %u\n", pin, func);
+
+	l1 &= ~(RZN1_L1_FUNC_MASK << RZN1_L1_FUNCTION);
+
+	if (func < RZN1_FUNC_L2_OFFSET) {
+		l1 |= (func << RZN1_L1_FUNCTION);
+	} else {
+		l1 |= (RZN1_L1_FUNCTION_L2 << RZN1_L1_FUNCTION);
+
+		l2 = func - RZN1_FUNC_L2_OFFSET;
+	}
+
+	/* If either configuration changes, we update both anyway */
+	if (l1 != l1_cache || l2 != l2_cache) {
+		level1_write(priv->regmap, conf[pin], l1);
+		level2_write(priv->regmap, conf[pin], l2);
+	}
+
+	return 0;
+}
+
+static int rzn1_pinconf_set(struct rzn1_pinctrl_priv *priv, unsigned int pin,
+			    unsigned int bias, unsigned int strength)
+{
+	u32 l1, l1_cache;
+	u32 drv = RZN1_L1_PIN_DRIVE_STRENGTH_8MA;
+
+	level1_read(priv->regmap, conf[pin], &l1);
+	l1_cache = l1;
+
+	switch (bias) {
+	case PIN_CONFIG_BIAS_PULL_UP:
+		debug("set pin %d pull up\n", pin);
+		l1 &= ~(0x3 << RZN1_L1_PIN_PULL);
+		l1 |= (RZN1_L1_PIN_PULL_UP << RZN1_L1_PIN_PULL);
+		break;
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		debug("set pin %d pull down\n", pin);
+		l1 &= ~(0x3 << RZN1_L1_PIN_PULL);
+		l1 |= (RZN1_L1_PIN_PULL_DOWN << RZN1_L1_PIN_PULL);
+		break;
+	case PIN_CONFIG_BIAS_DISABLE:
+		debug("set pin %d bias off\n", pin);
+		l1 &= ~(0x3 << RZN1_L1_PIN_PULL);
+		l1 |= (RZN1_L1_PIN_PULL_NONE << RZN1_L1_PIN_PULL);
+		break;
+	}
+
+	switch (strength) {
+	case 4:
+		drv = RZN1_L1_PIN_DRIVE_STRENGTH_4MA;
+		break;
+	case 6:
+		drv = RZN1_L1_PIN_DRIVE_STRENGTH_6MA;
+		break;
+	case 8:
+		drv = RZN1_L1_PIN_DRIVE_STRENGTH_8MA;
+		break;
+	case 12:
+		drv = RZN1_L1_PIN_DRIVE_STRENGTH_12MA;
+		break;
+	}
+
+	debug("set pin %d drv %umA\n", pin, drv);
+
+	l1 &= ~(0x3 << RZN1_L1_PIN_DRIVE_STRENGTH);
+	l1 |= (drv << RZN1_L1_PIN_DRIVE_STRENGTH);
+
+	if (l1 != l1_cache)
+		level1_write(priv->regmap, conf[pin], l1);
+
+	return 0;
+}
+
+static int rzn1_pinctrl_set_state_simple(struct udevice *dev,
+					 struct udevice *periph)
+{
+	// FIXME this gets called for all device nodes which do not
+	// have a pinctrl-0 phandle property. If the function is missing
+	// then a warning gets printed by pinctrl-uclass.
+
+	debug("%s:%d dev=%s periph=%s\n", __func__, __LINE__, dev->name, periph->name);
+
+	return 0;
+}
+
+static int rzn1_pinctrl_set_state(struct udevice *dev, struct udevice *config)
+{
+	struct rzn1_pinctrl_priv *priv = dev_get_priv(dev);
+	int size;
+	int ret;
+	u32 val;
+
+	/* TODO: need to iterate over subgroups, needed so that it is possible
+	 * to have different bias/strength settings for some pins. Currently
+	 * none of our devices need that.
+	 */
+
+	/* Pullup/down bias, common to all pins in group */
+	u32 bias = PIN_CONFIG_BIAS_PULL_UP;
+	if (dev_read_bool(config, "bias-disable"))
+		bias = PIN_CONFIG_BIAS_DISABLE;
+	else if (dev_read_bool(config, "bias-pull-up"))
+		bias = PIN_CONFIG_BIAS_PULL_UP;
+	else if (dev_read_bool(config, "bias-pull-down"))
+		bias = PIN_CONFIG_BIAS_PULL_DOWN;
+
+	/* Drive strength, common to all pins in group */
+	u32 strength = dev_read_u32_default(config, "drive-strength", 8);
+
+	/* Number of pins */
+	ret = dev_read_size(config, "pinmux");
+	if (ret < 0)
+		return ret;
+
+	size = ret / sizeof(val);
+
+	for (int i = 0; i < size; i++) {
+		ret = dev_read_u32_index(config, "pinmux", i, &val);
+		if (ret)
+			return ret;
+		unsigned int pin = val & 0xff;
+		unsigned int func = val >> 8;
+
+		debug("%s pin %d func %d bias %d strength %d\n",
+		      config->name, pin, func, bias, strength);
+
+		rzn1_hw_set_lock(priv, LOCK_ALL, LOCK_ALL);
+		rzn1_set_hw_pin_func(priv, pin, func);
+		rzn1_pinconf_set(priv, pin, bias, strength);
+		rzn1_hw_set_lock(priv, LOCK_ALL, 0);
+	}
+
+	return 0;
+}
+
+static struct pinctrl_ops rzn1_pinctrl_ops = {
+	.set_state = rzn1_pinctrl_set_state,
+
+	.set_state_simple = rzn1_pinctrl_set_state_simple,
+
+};
+
+static int rzn1_pinctrl_probe(struct udevice *dev)
+{
+	struct rzn1_pinctrl_priv *priv = dev_get_priv(dev);
+	ofnode node = dev_ofnode(dev);
+	int ret;
+
+	ret = regmap_init_mem(node, &priv->regmap);
+	if (ret)
+		return ret;
+
+	priv->lev1_protect_phys = (u32)regmap_get_range(priv->regmap, 0) +
+		offsetof(struct rzn1_pinctrl_regs, status_protect);
+	priv->lev2_protect_phys = (u32)regmap_get_range(priv->regmap, 1) +
+		offsetof(struct rzn1_pinctrl_regs, status_protect);
+
+	return 0;
+}
+
+static const struct udevice_id rzn1_pinctrl_ids[] = {
+	{ .compatible = "renesas,rzn1-pinctrl", },
+	{ },
+};
+
+U_BOOT_DRIVER(pinctrl_rzn1) = {
+	.name	= "rzn1-pinctrl",
+	.id	= UCLASS_PINCTRL,
+	.of_match = rzn1_pinctrl_ids,
+	.priv_auto = sizeof(struct rzn1_pinctrl_priv),
+	.ops = &rzn1_pinctrl_ops,
+	.probe = rzn1_pinctrl_probe,
+	.flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/include/dt-bindings/pinctrl/rzn1-pinctrl.h b/include/dt-bindings/pinctrl/rzn1-pinctrl.h
new file mode 100644
index 0000000000..21d6cc4d59
--- /dev/null
+++ b/include/dt-bindings/pinctrl/rzn1-pinctrl.h
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Defines macros and constants for Renesas RZ/N1 pin controller pin
+ * muxing functions.
+ */
+#ifndef __DT_BINDINGS_RZN1_PINCTRL_H
+#define __DT_BINDINGS_RZN1_PINCTRL_H
+
+#define RZN1_PINMUX(_gpio, _func) \
+	(((_func) << 8) | (_gpio))
+
+/*
+ * Given the different levels of muxing on the SoC, it was decided to
+ * 'linearize' them into one numerical space. So mux level 1, 2 and the MDIO
+ * muxes are all represented by one single value.
+ *
+ * You can derive the hardware value pretty easily too, as
+ * 0...9   are Level 1
+ * 10...71 are Level 2. The Level 2 mux will be set to this
+ *         value - RZN1_FUNC_L2_OFFSET, and the Level 1 mux will be
+ *         set accordingly.
+ * 72...103 are for the 2 MDIO muxes.
+ */
+#define RZN1_FUNC_HIGHZ				0
+#define RZN1_FUNC_0L				1
+#define RZN1_FUNC_CLK_ETH_MII_RGMII_RMII	2
+#define RZN1_FUNC_CLK_ETH_NAND			3
+#define RZN1_FUNC_QSPI				4
+#define RZN1_FUNC_SDIO				5
+#define RZN1_FUNC_LCD				6
+#define RZN1_FUNC_LCD_E				7
+#define RZN1_FUNC_MSEBIM			8
+#define RZN1_FUNC_MSEBIS			9
+#define RZN1_FUNC_L2_OFFSET			10	/* I'm Special */
+
+#define RZN1_FUNC_HIGHZ1			(RZN1_FUNC_L2_OFFSET + 0)
+#define RZN1_FUNC_ETHERCAT			(RZN1_FUNC_L2_OFFSET + 1)
+#define RZN1_FUNC_SERCOS3			(RZN1_FUNC_L2_OFFSET + 2)
+#define RZN1_FUNC_SDIO_E			(RZN1_FUNC_L2_OFFSET + 3)
+#define RZN1_FUNC_ETH_MDIO			(RZN1_FUNC_L2_OFFSET + 4)
+#define RZN1_FUNC_ETH_MDIO_E1			(RZN1_FUNC_L2_OFFSET + 5)
+#define RZN1_FUNC_USB				(RZN1_FUNC_L2_OFFSET + 6)
+#define RZN1_FUNC_MSEBIM_E			(RZN1_FUNC_L2_OFFSET + 7)
+#define RZN1_FUNC_MSEBIS_E			(RZN1_FUNC_L2_OFFSET + 8)
+#define RZN1_FUNC_RSV				(RZN1_FUNC_L2_OFFSET + 9)
+#define RZN1_FUNC_RSV_E				(RZN1_FUNC_L2_OFFSET + 10)
+#define RZN1_FUNC_RSV_E1			(RZN1_FUNC_L2_OFFSET + 11)
+#define RZN1_FUNC_UART0_I			(RZN1_FUNC_L2_OFFSET + 12)
+#define RZN1_FUNC_UART0_I_E			(RZN1_FUNC_L2_OFFSET + 13)
+#define RZN1_FUNC_UART1_I			(RZN1_FUNC_L2_OFFSET + 14)
+#define RZN1_FUNC_UART1_I_E			(RZN1_FUNC_L2_OFFSET + 15)
+#define RZN1_FUNC_UART2_I			(RZN1_FUNC_L2_OFFSET + 16)
+#define RZN1_FUNC_UART2_I_E			(RZN1_FUNC_L2_OFFSET + 17)
+#define RZN1_FUNC_UART0				(RZN1_FUNC_L2_OFFSET + 18)
+#define RZN1_FUNC_UART0_E			(RZN1_FUNC_L2_OFFSET + 19)
+#define RZN1_FUNC_UART1				(RZN1_FUNC_L2_OFFSET + 20)
+#define RZN1_FUNC_UART1_E			(RZN1_FUNC_L2_OFFSET + 21)
+#define RZN1_FUNC_UART2				(RZN1_FUNC_L2_OFFSET + 22)
+#define RZN1_FUNC_UART2_E			(RZN1_FUNC_L2_OFFSET + 23)
+#define RZN1_FUNC_UART3				(RZN1_FUNC_L2_OFFSET + 24)
+#define RZN1_FUNC_UART3_E			(RZN1_FUNC_L2_OFFSET + 25)
+#define RZN1_FUNC_UART4				(RZN1_FUNC_L2_OFFSET + 26)
+#define RZN1_FUNC_UART4_E			(RZN1_FUNC_L2_OFFSET + 27)
+#define RZN1_FUNC_UART5				(RZN1_FUNC_L2_OFFSET + 28)
+#define RZN1_FUNC_UART5_E			(RZN1_FUNC_L2_OFFSET + 29)
+#define RZN1_FUNC_UART6				(RZN1_FUNC_L2_OFFSET + 30)
+#define RZN1_FUNC_UART6_E			(RZN1_FUNC_L2_OFFSET + 31)
+#define RZN1_FUNC_UART7				(RZN1_FUNC_L2_OFFSET + 32)
+#define RZN1_FUNC_UART7_E			(RZN1_FUNC_L2_OFFSET + 33)
+#define RZN1_FUNC_SPI0_M			(RZN1_FUNC_L2_OFFSET + 34)
+#define RZN1_FUNC_SPI0_M_E			(RZN1_FUNC_L2_OFFSET + 35)
+#define RZN1_FUNC_SPI1_M			(RZN1_FUNC_L2_OFFSET + 36)
+#define RZN1_FUNC_SPI1_M_E			(RZN1_FUNC_L2_OFFSET + 37)
+#define RZN1_FUNC_SPI2_M			(RZN1_FUNC_L2_OFFSET + 38)
+#define RZN1_FUNC_SPI2_M_E			(RZN1_FUNC_L2_OFFSET + 39)
+#define RZN1_FUNC_SPI3_M			(RZN1_FUNC_L2_OFFSET + 40)
+#define RZN1_FUNC_SPI3_M_E			(RZN1_FUNC_L2_OFFSET + 41)
+#define RZN1_FUNC_SPI4_S			(RZN1_FUNC_L2_OFFSET + 42)
+#define RZN1_FUNC_SPI4_S_E			(RZN1_FUNC_L2_OFFSET + 43)
+#define RZN1_FUNC_SPI5_S			(RZN1_FUNC_L2_OFFSET + 44)
+#define RZN1_FUNC_SPI5_S_E			(RZN1_FUNC_L2_OFFSET + 45)
+#define RZN1_FUNC_SGPIO0_M			(RZN1_FUNC_L2_OFFSET + 46)
+#define RZN1_FUNC_SGPIO1_M			(RZN1_FUNC_L2_OFFSET + 47)
+#define RZN1_FUNC_GPIO				(RZN1_FUNC_L2_OFFSET + 48)
+#define RZN1_FUNC_CAN				(RZN1_FUNC_L2_OFFSET + 49)
+#define RZN1_FUNC_I2C				(RZN1_FUNC_L2_OFFSET + 50)
+#define RZN1_FUNC_SAFE				(RZN1_FUNC_L2_OFFSET + 51)
+#define RZN1_FUNC_PTO_PWM			(RZN1_FUNC_L2_OFFSET + 52)
+#define RZN1_FUNC_PTO_PWM1			(RZN1_FUNC_L2_OFFSET + 53)
+#define RZN1_FUNC_PTO_PWM2			(RZN1_FUNC_L2_OFFSET + 54)
+#define RZN1_FUNC_PTO_PWM3			(RZN1_FUNC_L2_OFFSET + 55)
+#define RZN1_FUNC_PTO_PWM4			(RZN1_FUNC_L2_OFFSET + 56)
+#define RZN1_FUNC_DELTA_SIGMA			(RZN1_FUNC_L2_OFFSET + 57)
+#define RZN1_FUNC_SGPIO2_M			(RZN1_FUNC_L2_OFFSET + 58)
+#define RZN1_FUNC_SGPIO3_M			(RZN1_FUNC_L2_OFFSET + 59)
+#define RZN1_FUNC_SGPIO4_S			(RZN1_FUNC_L2_OFFSET + 60)
+#define RZN1_FUNC_MAC_MTIP_SWITCH		(RZN1_FUNC_L2_OFFSET + 61)
+
+#define RZN1_FUNC_MDIO_OFFSET			(RZN1_FUNC_L2_OFFSET + 62)
+
+/* These are MDIO0 peripherals for the RZN1_FUNC_ETH_MDIO function */
+#define RZN1_FUNC_MDIO0_HIGHZ			(RZN1_FUNC_MDIO_OFFSET + 0)
+#define RZN1_FUNC_MDIO0_GMAC0			(RZN1_FUNC_MDIO_OFFSET + 1)
+#define RZN1_FUNC_MDIO0_GMAC1			(RZN1_FUNC_MDIO_OFFSET + 2)
+#define RZN1_FUNC_MDIO0_ECAT			(RZN1_FUNC_MDIO_OFFSET + 3)
+#define RZN1_FUNC_MDIO0_S3_MDIO0		(RZN1_FUNC_MDIO_OFFSET + 4)
+#define RZN1_FUNC_MDIO0_S3_MDIO1		(RZN1_FUNC_MDIO_OFFSET + 5)
+#define RZN1_FUNC_MDIO0_HWRTOS			(RZN1_FUNC_MDIO_OFFSET + 6)
+#define RZN1_FUNC_MDIO0_SWITCH			(RZN1_FUNC_MDIO_OFFSET + 7)
+/* These are MDIO0 peripherals for the RZN1_FUNC_ETH_MDIO_E1 function */
+#define RZN1_FUNC_MDIO0_E1_HIGHZ		(RZN1_FUNC_MDIO_OFFSET + 8)
+#define RZN1_FUNC_MDIO0_E1_GMAC0		(RZN1_FUNC_MDIO_OFFSET + 9)
+#define RZN1_FUNC_MDIO0_E1_GMAC1		(RZN1_FUNC_MDIO_OFFSET + 10)
+#define RZN1_FUNC_MDIO0_E1_ECAT			(RZN1_FUNC_MDIO_OFFSET + 11)
+#define RZN1_FUNC_MDIO0_E1_S3_MDIO0		(RZN1_FUNC_MDIO_OFFSET + 12)
+#define RZN1_FUNC_MDIO0_E1_S3_MDIO1		(RZN1_FUNC_MDIO_OFFSET + 13)
+#define RZN1_FUNC_MDIO0_E1_HWRTOS		(RZN1_FUNC_MDIO_OFFSET + 14)
+#define RZN1_FUNC_MDIO0_E1_SWITCH		(RZN1_FUNC_MDIO_OFFSET + 15)
+
+/* These are MDIO1 peripherals for the RZN1_FUNC_ETH_MDIO function */
+#define RZN1_FUNC_MDIO1_HIGHZ			(RZN1_FUNC_MDIO_OFFSET + 16)
+#define RZN1_FUNC_MDIO1_GMAC0			(RZN1_FUNC_MDIO_OFFSET + 17)
+#define RZN1_FUNC_MDIO1_GMAC1			(RZN1_FUNC_MDIO_OFFSET + 18)
+#define RZN1_FUNC_MDIO1_ECAT			(RZN1_FUNC_MDIO_OFFSET + 19)
+#define RZN1_FUNC_MDIO1_S3_MDIO0		(RZN1_FUNC_MDIO_OFFSET + 20)
+#define RZN1_FUNC_MDIO1_S3_MDIO1		(RZN1_FUNC_MDIO_OFFSET + 21)
+#define RZN1_FUNC_MDIO1_HWRTOS			(RZN1_FUNC_MDIO_OFFSET + 22)
+#define RZN1_FUNC_MDIO1_SWITCH			(RZN1_FUNC_MDIO_OFFSET + 23)
+/* These are MDIO1 peripherals for the RZN1_FUNC_ETH_MDIO_E1 function */
+#define RZN1_FUNC_MDIO1_E1_HIGHZ		(RZN1_FUNC_MDIO_OFFSET + 24)
+#define RZN1_FUNC_MDIO1_E1_GMAC0		(RZN1_FUNC_MDIO_OFFSET + 25)
+#define RZN1_FUNC_MDIO1_E1_GMAC1		(RZN1_FUNC_MDIO_OFFSET + 26)
+#define RZN1_FUNC_MDIO1_E1_ECAT			(RZN1_FUNC_MDIO_OFFSET + 27)
+#define RZN1_FUNC_MDIO1_E1_S3_MDIO0		(RZN1_FUNC_MDIO_OFFSET + 28)
+#define RZN1_FUNC_MDIO1_E1_S3_MDIO1		(RZN1_FUNC_MDIO_OFFSET + 29)
+#define RZN1_FUNC_MDIO1_E1_HWRTOS		(RZN1_FUNC_MDIO_OFFSET + 30)
+#define RZN1_FUNC_MDIO1_E1_SWITCH		(RZN1_FUNC_MDIO_OFFSET + 31)
+
+#define RZN1_FUNC_MAX				(RZN1_FUNC_MDIO_OFFSET + 32)
+
+#endif /* __DT_BINDINGS_RZN1_PINCTRL_H */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v1 5/9] ram: cadence: add driver for Cadence EDAC
  2022-08-09 12:59 [RFC PATCH v1 0/9] Renesas RZ/N1 SoC initial support Ralph Siemsen
                   ` (3 preceding siblings ...)
  2022-08-09 12:59 ` [RFC PATCH v1 4/9] pinctrl: " Ralph Siemsen
@ 2022-08-09 12:59 ` Ralph Siemsen
  2022-08-09 12:59 ` [RFC PATCH v1 6/9] dts: basic devicetree for Renesas RZ/N1 SoC Ralph Siemsen
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 29+ messages in thread
From: Ralph Siemsen @ 2022-08-09 12:59 UTC (permalink / raw)
  To: u-boot
  Cc: Ralph Siemsen, Andrew Davis, Aswath Govindraju, David Huang,
	Nishanth Menon, Suman Anna

Driver for Cadence EDAC DDR controller, as found in the Renesas RZ/N1.
Only basic setup, not using the ECC features.

Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
---

 drivers/ram/Kconfig             |   1 +
 drivers/ram/Makefile            |   2 +
 drivers/ram/cadence/Kconfig     |  24 ++
 drivers/ram/cadence/Makefile    |   1 +
 drivers/ram/cadence/ddr_async.c | 295 +++++++++++++++++++++++
 drivers/ram/cadence/ddr_ctrl.c  | 414 ++++++++++++++++++++++++++++++++
 drivers/ram/cadence/ddr_ctrl.h  | 172 +++++++++++++
 7 files changed, 909 insertions(+)
 create mode 100644 drivers/ram/cadence/Kconfig
 create mode 100644 drivers/ram/cadence/Makefile
 create mode 100644 drivers/ram/cadence/ddr_async.c
 create mode 100644 drivers/ram/cadence/ddr_ctrl.c
 create mode 100644 drivers/ram/cadence/ddr_ctrl.h

diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig
index 86857c0627..ec0ab9ce8a 100644
--- a/drivers/ram/Kconfig
+++ b/drivers/ram/Kconfig
@@ -97,6 +97,7 @@ config IMXRT_SDRAM
 	  This driver is for the sdram memory interface with the SEMC.
 
 source "drivers/ram/aspeed/Kconfig"
+source "drivers/ram/cadence/Kconfig"
 source "drivers/ram/rockchip/Kconfig"
 source "drivers/ram/sifive/Kconfig"
 source "drivers/ram/stm32mp1/Kconfig"
diff --git a/drivers/ram/Makefile b/drivers/ram/Makefile
index 5a39611349..2a015cda48 100644
--- a/drivers/ram/Makefile
+++ b/drivers/ram/Makefile
@@ -22,3 +22,5 @@ obj-$(CONFIG_IMXRT_SDRAM) += imxrt_sdram.o
 obj-$(CONFIG_RAM_SIFIVE) += sifive/
 
 obj-$(CONFIG_ARCH_OCTEON) += octeon/
+
+obj-$(CONFIG_CADENCE_DDR_CTRL) += cadence/
diff --git a/drivers/ram/cadence/Kconfig b/drivers/ram/cadence/Kconfig
new file mode 100644
index 0000000000..864cadfd10
--- /dev/null
+++ b/drivers/ram/cadence/Kconfig
@@ -0,0 +1,24 @@
+if RAM || SPL_RAM
+
+config CADENCE_DDR_CTRL
+	bool "Enable Cadence DDR controller"
+	depends on DM
+	help
+	  Enable support for Cadence DDR controller, as found on
+	  the Renesas RZ/N1 SoC. This controller has a large number
+	  of registers which need to be programmed, mostly using values
+	  obtained from Denali SOMA files via a TCL script.
+
+if CADENCE_DDR_CTRL
+
+# TODO: convert this to devicetree option
+config CADENCE_DDR_CTRL_ENABLE_ECC
+	bool "Enable ECC support"
+
+# TODO: conver this to devicetree option
+config CADENCE_DDR_CTRL_8BIT_WIDTH
+	bool "Reduce data width from 16 to 8 bits"
+
+endif
+
+endif
diff --git a/drivers/ram/cadence/Makefile b/drivers/ram/cadence/Makefile
new file mode 100644
index 0000000000..16c7fe8488
--- /dev/null
+++ b/drivers/ram/cadence/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_CADENCE_DDR_CTRL) += ddr_async.o ddr_ctrl.o
diff --git a/drivers/ram/cadence/ddr_async.c b/drivers/ram/cadence/ddr_async.c
new file mode 100644
index 0000000000..1d6d3bdc79
--- /dev/null
+++ b/drivers/ram/cadence/ddr_async.c
@@ -0,0 +1,295 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * RZ/N1 DDR Controller initialisation
+ *
+ * The DDR Controller register values for a specific DDR device, mode and
+ * frequency are generated using a Cadence tool.
+ *
+ * Copyright (C) 2015 Renesas Electronics Europe Ltd
+ */
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <ram.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include "ddr_ctrl.h"
+
+void clk_rzn1_reset_state(struct clk *clk, int on);
+
+extern u32 ddr_00_87_async[];
+extern u32 ddr_350_374_async[];
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct cadence_ddr_info {
+	struct udevice *dev;
+	void __iomem *ddrc;
+	void __iomem *phy;
+	struct clk clk_ddrc;
+	struct clk hclk_ddrc;
+};
+
+static inline u32 cadence_readl(void __iomem *addr, unsigned int offset)
+{
+	return readl(addr + offset);
+}
+
+static inline void cadence_writel(void __iomem *addr, unsigned int offset,
+				  u32 data)
+{
+	debug("%s: addr = 0x%p, value = 0x%08x\n", __func__, addr + offset, data);
+	writel(data, addr + offset);
+}
+
+#define ddrc_readl(off)		cadence_readl(priv->ddrc, off)
+#define ddrc_writel(val, off)	cadence_writel(priv->ddrc, off, val)
+
+#define phy_readl(off)		cadence_readl(priv->phy, off)
+#define phy_writel(val, off)	cadence_writel(priv->phy, off, val)
+
+#define RZN1_DDR3_SINGLE_BANK 3
+#define RZN1_DDR3_DUAL_BANK 32
+
+#define FUNCCTRL	0x00
+#define  FUNCCTRL_MASKSDLOFS	(0x18 << 16)
+#define  FUNCCTRL_DVDDQ_1_5V	(1 << 8)
+#define  FUNCCTRL_RESET_N	(1 << 0)
+#define DLLCTRL		0x04
+#define  DLLCTRL_ASDLLOCK	(1 << 26)
+#define  DLLCTRL_MFSL_500MHz	(2 << 1)
+#define  DLLCTRL_MDLLSTBY	(1 << 0)
+#define ZQCALCTRL	0x08
+#define  ZQCALCTRL_ZQCALEND	(1 << 30)
+#define  ZQCALCTRL_ZQCALRSTB	(1 << 0)
+#define ZQODTCTRL	0x0c
+#define RDCTRL		0x10
+#define RDTMG		0x14
+#define FIFOINIT	0x18
+#define  FIFOINIT_RDPTINITEXE	(1 << 8)
+#define  FIFOINIT_WRPTINITEXE	(1 << 0)
+#define OUTCTRL		0x1c
+#define  OUTCTRL_ADCMDOE	(1 << 0)
+#define WLCTRL1		0x40
+#define  WLCTRL1_WLSTR		(1 << 24)
+#define DQCALOFS1	0xe8
+
+/* DDR PHY setup */
+void ddr_phy_init(struct cadence_ddr_info *priv, int ddr_type)
+{
+	u32 val;
+
+	/* Disable DDR Controller clock and FlexWAY connection */
+	clk_disable(&priv->hclk_ddrc);
+	clk_disable(&priv->clk_ddrc);
+
+	clk_rzn1_reset_state(&priv->hclk_ddrc, 0);
+	clk_rzn1_reset_state(&priv->clk_ddrc, 0);
+
+	/* Enable DDR Controller clock and FlexWAY connection */
+	clk_enable(&priv->clk_ddrc);
+	clk_enable(&priv->hclk_ddrc);
+
+	/* DDR PHY Soft reset assert */
+	ddrc_writel(FUNCCTRL_MASKSDLOFS | FUNCCTRL_DVDDQ_1_5V, FUNCCTRL);
+
+	clk_rzn1_reset_state(&priv->hclk_ddrc, 1);
+	clk_rzn1_reset_state(&priv->clk_ddrc, 1);
+
+	/* DDR PHY setup */
+	phy_writel(DLLCTRL_MFSL_500MHz | DLLCTRL_MDLLSTBY, DLLCTRL);
+	phy_writel(0x00000182, ZQCALCTRL);
+	if (ddr_type == RZN1_DDR3_DUAL_BANK)
+		phy_writel(0xAB330031, ZQODTCTRL);
+	else if (ddr_type == RZN1_DDR3_SINGLE_BANK)
+		phy_writel(0xAB320051, ZQODTCTRL);
+	else /* DDR2 */
+		phy_writel(0xAB330071, ZQODTCTRL);
+	phy_writel(0xB545B544, RDCTRL);
+	phy_writel(0x000000B0, RDTMG);
+	phy_writel(0x020A0806, OUTCTRL);
+	if (ddr_type == RZN1_DDR3_DUAL_BANK)
+		phy_writel(0x80005556, WLCTRL1);
+	else
+		phy_writel(0x80005C5D, WLCTRL1);
+	phy_writel(0x00000101, FIFOINIT);
+	phy_writel(0x00004545, DQCALOFS1);
+
+	/* Step 9 MDLL reset release */
+	val = phy_readl(DLLCTRL);
+	val &= ~DLLCTRL_MDLLSTBY;
+	phy_writel(val, DLLCTRL);
+
+	/* Step 12 Soft reset release */
+	val = phy_readl(FUNCCTRL);
+	val |= FUNCCTRL_RESET_N;
+	phy_writel(val, FUNCCTRL);
+
+	/* Step 13 FIFO pointer initialize */
+	phy_writel(FIFOINIT_RDPTINITEXE | FIFOINIT_WRPTINITEXE, FIFOINIT);
+
+	/* Step 14 Execute ZQ Calibration */
+	val = phy_readl(ZQCALCTRL);
+	val |= ZQCALCTRL_ZQCALRSTB;
+	phy_writel(val, ZQCALCTRL);
+
+	/* Step 15 Wait for 200us or more, or wait for DFIINITCOMPLETE to be "1" */
+	while (!(phy_readl(DLLCTRL) & DLLCTRL_ASDLLOCK))
+		;
+	while (!(phy_readl(ZQCALCTRL) & ZQCALCTRL_ZQCALEND))
+		;
+
+	/* Step 16 Enable Address and Command output */
+	val = phy_readl(OUTCTRL);
+	val |= OUTCTRL_ADCMDOE;
+	phy_writel(val, OUTCTRL);
+
+	/* Step 17 Wait for 200us or more(from MRESETB=0) */
+	udelay(200);
+}
+
+void ddr_phy_enable_wl(struct cadence_ddr_info *priv)
+{
+	u32 val;
+
+	/* Step 26 (Set Write Leveling) */
+	val = phy_readl(WLCTRL1);
+	val |= WLCTRL1_WLSTR;
+	phy_writel(val, WLCTRL1);
+}
+
+#define RZN1_DDR_BASE                 0x4000D000      /* RZ/N1D only */
+#define RZN1_V_DDR_BASE               0x80000000      /* RZ/N1D only */
+
+void rzn1_ddr3_single_bank(void *ddr_ctrl_base)
+{
+	/* CS0 */
+	cdns_ddr_set_mr1(ddr_ctrl_base, 0,
+			 MR1_ODT_IMPEDANCE_60_OHMS,
+			 MR1_DRIVE_STRENGTH_40_OHMS);
+	cdns_ddr_set_mr2(ddr_ctrl_base, 0,
+			 MR2_DYNAMIC_ODT_OFF,
+			 MR2_SELF_REFRESH_TEMP_EXT);
+
+	/* ODT_WR_MAP_CS0 = 1, ODT_RD_MAP_CS0 = 0 */
+	cdns_ddr_set_odt_map(ddr_ctrl_base, 0, 0x0100);
+}
+
+int rzn1_dram_init(struct cadence_ddr_info *priv)
+{
+	u32 ddr_start_addr = 0;
+
+	ddr_phy_init(priv, RZN1_DDR3_SINGLE_BANK);
+
+	/*
+	 * Override DDR PHY Interface (DFI) related settings
+	 * DFI is the internal interface between the DDR controller and the DDR PHY.
+	 * These settings are specific to LCES2 and can't be known by the settings
+	 * provided for each DDR model within the generated include.
+	 */
+	ddr_350_374_async[351 - 350] = 0x001e0000;
+	ddr_350_374_async[352 - 350] = 0x1e680000;
+	ddr_350_374_async[353 - 350] = 0x02000020;
+	ddr_350_374_async[354 - 350] = 0x02000200;
+	ddr_350_374_async[355 - 350] = 0x00000c30;
+	ddr_350_374_async[356 - 350] = 0x00009808;
+	ddr_350_374_async[357 - 350] = 0x020a0706;
+	ddr_350_374_async[372 - 350] = 0x01000000;
+
+#if 0
+	/*
+	 * On ES1.0 devices, the DDR start address that the DDR Controller sees
+	 * is the physical address of the DDR. However, later devices changed it
+	 * to be 0 in order to fix an issue with DDR out-of-range detection.
+	 */
+	if (sysctrl_readl(RZN1_SYSCTRL_REG_VERSION) == 0x10)
+		ddr_start_addr = RZN1_V_DDR_BASE;
+#endif
+
+	/* DDR Controller is always in ASYNC mode */
+	cdns_ddr_ctrl_init((void *)RZN1_DDR_BASE, 1,
+			   ddr_00_87_async, ddr_350_374_async,
+			   ddr_start_addr, CONFIG_SYS_SDRAM_SIZE);
+
+	rzn1_ddr3_single_bank((void *)RZN1_DDR_BASE);
+	cdns_ddr_set_diff_cs_delays((void *)RZN1_DDR_BASE, 2, 7, 2, 2);
+	cdns_ddr_set_same_cs_delays((void *)RZN1_DDR_BASE, 0, 7, 0, 0);
+	cdns_ddr_set_odt_times((void *)RZN1_DDR_BASE, 5, 6, 6, 0, 4);
+	cdns_ddr_ctrl_start((void *)RZN1_DDR_BASE);
+
+	ddr_phy_enable_wl(priv);
+
+	if (IS_ENABLED(CONFIG_CADENCE_DDR_CTRL_ENABLE_ECC)) {
+		/*
+		 * Any read before a write will trigger an ECC un-correctable error,
+		 * causing a data abort. However, this is also true for any read with a
+		 * size less than the AXI bus width. So, the only sensible solution is
+		 * to write to all of DDR now and take the hit...
+		 */
+		memset((void *)RZN1_V_DDR_BASE, 0xff, CONFIG_SYS_SDRAM_SIZE);
+	}
+
+	return 0;
+}
+
+static int cadence_ddr_get_info(struct udevice *udev, struct ram_info *info)
+{
+	info->base = 0;
+	info->size = gd->ram_size;
+
+	return 0;
+}
+
+static struct ram_ops cadence_ddr_ops = {
+	.get_info = cadence_ddr_get_info,
+};
+
+static int cadence_ddr_probe(struct udevice *dev)
+{
+	struct cadence_ddr_info *priv = dev_get_priv(dev);
+	int ret;
+
+	priv->ddrc = dev_remap_addr_name(dev, "ddrc");
+	if (!priv->ddrc) {
+		dev_err(dev, "No reg property for Cadence DDR CTRL\n");
+		return -EINVAL;
+	}
+
+	priv->phy = dev_remap_addr_name(dev, "phy");
+	if (!priv->phy) {
+		dev_err(dev, "No reg property for Cadence DDR PHY\n");
+		return -EINVAL;
+	}
+
+	ret = clk_get_by_name(dev, "clk_ddrc", &priv->clk_ddrc);
+	if (ret) {
+		dev_err(dev, "No clock for Cadence DDR\n");
+		return ret;
+	}
+
+	ret = clk_get_by_name(dev, "hclk_ddrc", &priv->hclk_ddrc);
+	if (ret) {
+		dev_err(dev, "No HCLK for Cadence DDR\n");
+		return ret;
+	}
+
+	rzn1_dram_init(priv);
+
+	return 0;
+}
+
+static const struct udevice_id cadence_ddr_ids[] = {
+	{ .compatible = "cadence,ddr-ctrl" },
+	{ }
+};
+
+U_BOOT_DRIVER(cadence_ddr) = {
+	.name		= "cadence_ddr",
+	.id		= UCLASS_RAM,
+	.of_match	= cadence_ddr_ids,
+	.ops		= &cadence_ddr_ops,
+	.probe		= cadence_ddr_probe,
+	.priv_auto	= sizeof(struct cadence_ddr_info),
+	.flags		= DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/ram/cadence/ddr_ctrl.c b/drivers/ram/cadence/ddr_ctrl.c
new file mode 100644
index 0000000000..833d5d9197
--- /dev/null
+++ b/drivers/ram/cadence/ddr_ctrl.c
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Cadence DDR Controller
+ *
+ * Copyright (C) 2015 Renesas Electronics Europe Ltd
+ */
+
+/*
+ * The Cadence DDR Controller has a huge number of registers that principally
+ * cover two aspects, DDR specific timing information and AXI bus interfacing.
+ * Cadence's TCL script generates all of the register values for specific
+ * DDR devices operating at a specific frequency. The TCL script uses Denali
+ * SOMA files as inputs. The tool also generates the AXI bus register values as
+ * well, however this driver assumes that users will want to modifiy these to
+ * meet a specific application's needs.
+ * Therefore, this driver is passed two arrays containing register values for
+ * the DDR device specific information, and explicity sets the AXI registers.
+ *
+ * AXI bus interfacing:
+ *  The controller has four AXI slaves connections, and each of these can be
+ * programmed to accept requests from specific AXI masters (using their IDs).
+ * The regions of DDR that can be accessed by each AXI slave can be set such
+ * as to isolate DDR used by one AXI master from another. Further, the maximum
+ * bandwidth allocated to each AXI slave can be set.
+ */
+
+#include <common.h>
+#include <linux/delay.h>
+#include <linux/sizes.h>
+#include <asm/io.h>
+#include "ddr_ctrl.h"
+
+/* avoid warning for real pr_debug in <linux/printk.h> */
+#ifdef pr_debug
+#undef pr_debug
+#endif
+
+#ifdef DEBUG
+	#define pr_debug(fmt, args...)	printf(fmt, ##args)
+	#define pr_debug2(fmt, args...)	printf(fmt, ##args)
+#else
+	#define pr_debug(fmt, args...)
+	#define pr_debug2(fmt, args...)
+#endif
+
+#define DDR_NR_AXI_PORTS		4
+#define DDR_NR_ENTRIES			16
+
+#define DDR_START_REG			(0)		/* DENALI_CTL_00 */
+#define DDR_CS0_MR1_REG			(32 * 4)	/* DENALI_CTL_32 */
+#define DDR_CS0_MR2_REG			(32 * 4 + 2)	/* DENALI_CTL_32 */
+#define DDR_CS1_MR1_REG			(34 * 4 + 2)	/* DENALI_CTL_34 */
+#define DDR_CS1_MR2_REG			(35 * 4)	/* DENALI_CTL_35 */
+#define DDR_ECC_ENABLE_REG		(36 * 4 + 2)	/* DENALI_CTL_36 */
+#define DDR_ECC_DISABLE_W_UC_ERR_REG	(37 * 4 + 2)	/* DENALI_CTL_37 */
+#define DDR_HALF_DATAPATH_REG		(54 * 4)	/* DENALI_CTL_54 */
+#define DDR_INTERRUPT_STATUS		(56 * 4)	/* DENALI_CTL_56 */
+#define DDR_INTERRUPT_ACK		(57 * 4)	/* DENALI_CTL_57 */
+#define DDR_INTERRUPT_MASK		(58 * 4)	/* DENALI_CTL_58 */
+#define DDR_CS0_ODT_MAP_REG		(62 * 4 + 2)	/* DENALI_CTL_62 */
+#define DDR_CS1_ODT_MAP_REG		(63 * 4)	/* DENALI_CTL_63 */
+#define DDR_ODT_TODTL_2CMD		(63 * 4 + 2)	/* DENALI_CTL_63 */
+#define DDR_ODT_TODTH_WR		(63 * 4 + 3)	/* DENALI_CTL_63 */
+#define DDR_ODT_TODTH_RD		(64 * 4 + 0)	/* DENALI_CTL_64 */
+#define DDR_ODT_EN			(64 * 4 + 1)	/* DENALI_CTL_64 */
+#define DDR_ODT_WR_TO_ODTH		(64 * 4 + 2)	/* DENALI_CTL_64 */
+#define DDR_ODT_RD_TO_ODTH		(64 * 4 + 3)	/* DENALI_CTL_64 */
+#define DDR_DIFF_CS_DELAY_REG		(66 * 4)	/* DENALI_CTL_66 */
+#define DDR_SAME_CS_DELAY_REG		(67 * 4)	/* DENALI_CTL_67 */
+#define DDR_RW_PRIORITY_REGS		(87 * 4 + 2)	/* DENALI_CTL_87 */
+#define DDR_RW_FIFO_TYPE_REGS		(88 * 4)	/* DENALI_CTL_88 */
+#define DDR_AXI_PORT_PROT_ENABLE_REG	(90 * 4 + 3)	/* DENALI_CTL_90 */
+#define DDR_ADDR_RANGE_REGS		(91 * 4)	/* DENALI_CTL_91 */
+#define DDR_RANGE_PROT_REGS		(218 * 4 + 2)	/* DENALI_CTL_218 */
+#define DDR_ARB_CMD_Q_THRESHOLD_REG	(346 * 4 + 2)	/* DENALI_CTL_346 */
+#define DDR_AXI_PORT_BANDWIDTH_REG	(346 * 4 + 3)	/* DENALI_CTL_346 */
+#define DDR_OPT_RMODW_REG		(372 * 4 + 3)	/* DENALI_CTL_372 */
+
+static void ddrc_writeb(u8 val, void *p)
+{
+	pr_debug2("DDR: %p = 0x%02x\n", p, val);
+	writeb(val, p);
+}
+
+static void ddrc_writew(u16 val, void *p)
+{
+	pr_debug2("DDR: %p = 0x%04x\n", p, val);
+	writew(val, p);
+}
+
+static void ddrc_writel(u32 val, void *p)
+{
+	pr_debug2("DDR: %p = 0x%08x\n", p, val);
+	writel(val, p);
+}
+
+void cdns_ddr_set_mr1(void *base, int cs, u16 odt_impedance, u16 drive_strength)
+{
+	void *reg;
+	u16 tmp;
+
+	if (cs == 0)
+		reg = (u8 *)base + DDR_CS0_MR1_REG;
+	else
+		reg = (u8 *)base + DDR_CS1_MR1_REG;
+
+	tmp = readw(reg);
+
+	tmp &= ~MODE_REGISTER_MASK;
+	tmp |=  MODE_REGISTER_MR1;
+
+	tmp &= ~MR1_ODT_IMPEDANCE_MASK;
+	tmp |=  odt_impedance;
+
+	tmp &= ~MR1_DRIVE_STRENGTH_MASK;
+	tmp |=  drive_strength;
+
+	writew(tmp, reg);
+}
+
+void cdns_ddr_set_mr2(void *base, int cs, u16 dynamic_odt, u16 self_refresh_temp)
+{
+	void *reg;
+	u16 tmp;
+
+	if (cs == 0)
+		reg = (u8 *)base + DDR_CS0_MR2_REG;
+	else
+		reg = (u8 *)base + DDR_CS1_MR2_REG;
+
+	tmp = readw(reg);
+
+	tmp &= ~MODE_REGISTER_MASK;
+	tmp |=  MODE_REGISTER_MR2;
+
+	tmp &= ~MR2_DYNAMIC_ODT_MASK;
+	tmp |=  dynamic_odt;
+
+	tmp &= ~MR2_SELF_REFRESH_TEMP_MASK;
+	tmp |=  self_refresh_temp;
+
+	writew(tmp, reg);
+}
+
+void cdns_ddr_set_odt_map(void *base, int cs, u16 odt_map)
+{
+	void *reg;
+
+	if (cs == 0)
+		reg = (u8 *)base + DDR_CS0_ODT_MAP_REG;
+	else
+		reg = (u8 *)base + DDR_CS1_ODT_MAP_REG;
+
+	writew(odt_map, reg);
+}
+
+void cdns_ddr_set_odt_times(void *base, u8 TODTL_2CMD, u8 TODTH_WR, u8 TODTH_RD,
+			    u8 WR_TO_ODTH, u8 RD_TO_ODTH)
+{
+	writeb(TODTL_2CMD,	(u8 *)base + DDR_ODT_TODTL_2CMD);
+	writeb(TODTH_WR,	(u8 *)base + DDR_ODT_TODTH_WR);
+	writeb(TODTH_RD,	(u8 *)base + DDR_ODT_TODTH_RD);
+	writeb(1,		(u8 *)base + DDR_ODT_EN);
+	writeb(WR_TO_ODTH,	(u8 *)base + DDR_ODT_WR_TO_ODTH);
+	writeb(RD_TO_ODTH,	(u8 *)base + DDR_ODT_RD_TO_ODTH);
+}
+
+void cdns_ddr_set_same_cs_delays(void *base, u8 r2r, u8 r2w, u8 w2r, u8 w2w)
+{
+	u32 val = (w2w << 24) | (w2r << 16) | (r2w << 8) | r2r;
+
+	writel(val, (u8 *)base + DDR_SAME_CS_DELAY_REG);
+}
+
+void cdns_ddr_set_diff_cs_delays(void *base, u8 r2r, u8 r2w, u8 w2r, u8 w2w)
+{
+	u32 val = (w2w << 24) | (w2r << 16) | (r2w << 8) | r2r;
+
+	writel(val, (u8 *)base + DDR_DIFF_CS_DELAY_REG);
+}
+
+void cdns_ddr_set_port_rw_priority(void *base, int port,
+				   u8 read_pri, u8 write_pri)
+{
+	u8 *reg8 = (u8 *)base + DDR_RW_PRIORITY_REGS;
+
+	reg8 += (port * 3);
+	pr_debug("%s port %d (reg8=%p, DENALI_CTL_%d)\n",
+		 __func__, port, reg8, (reg8 - (u8 *)base) / 4);
+
+	ddrc_writeb(read_pri, reg8++);
+	ddrc_writeb(write_pri, reg8++);
+}
+
+/* The DDR Controller has 16 entries. Each entry can specify an allowed address
+ * range (with 16KB resolution) for one of the 4 AXI slave ports.
+ */
+void cdns_ddr_enable_port_addr_range(void *base, int port, int entry,
+				     u32 addr_start, u32 size)
+{
+	u32 addr_end;
+	u32 *reg32 = (u32 *)((u8 *)base + DDR_ADDR_RANGE_REGS);
+	u32 tmp;
+
+	reg32 += (port * DDR_NR_ENTRIES * 2);
+	reg32 += (entry * 2);
+	pr_debug("%s port %d, entry %d (reg32=%p, DENALI_CTL_%d)\n",
+		 __func__, port, entry, reg32, ((u8 *)reg32 - (u8 *)base) / 4);
+
+	/* These registers represent 16KB address blocks */
+	addr_start /= SZ_16K;
+	size /= SZ_16K;
+	if (size)
+		addr_end = addr_start + size - 1;
+	else
+		addr_end = addr_start;
+
+	ddrc_writel(addr_start, reg32++);
+
+	/*
+	 * end_addr: Ensure we only set the bottom 18-bits as DENALI_CTL_218
+	 * also contains the AXI0 range protection bits.
+	 */
+	tmp = readl(reg32);
+	tmp &= ~(BIT(18) - 1);
+	tmp |= addr_end;
+	ddrc_writel(tmp, reg32);
+}
+
+void cdns_ddr_enable_addr_range(void *base, int entry,
+				u32 addr_start, u32 size)
+{
+	int axi;
+
+	for (axi = 0; axi < DDR_NR_AXI_PORTS; axi++)
+		cdns_ddr_enable_port_addr_range(base, axi, entry,
+						addr_start, size);
+}
+
+void cdns_ddr_enable_port_prot(void *base, int port, int entry,
+			       enum cdns_ddr_range_prot range_protection_bits,
+			       u16 range_RID_check_bits,
+			       u16 range_WID_check_bits,
+			       u8 range_RID_check_bits_ID_lookup,
+			       u8 range_WID_check_bits_ID_lookup)
+{
+	/*
+	 * Technically, the offset here points to the byte before the start of
+	 * the range protection registers. However, all entries consist of 8
+	 * bytes, except the first one (which is missing a padding byte) so we
+	 * work around that subtlely.
+	 */
+	u8 *reg8 = (u8 *)base + DDR_RANGE_PROT_REGS;
+
+	reg8 += (port * DDR_NR_ENTRIES * 8);
+	reg8 += (entry * 8);
+	pr_debug("%s port %d, entry %d (reg8=%p, DENALI_CTL_%d)\n",
+		 __func__, port, entry, reg8, (reg8 - (u8 *)base) / 4);
+
+	if (port == 0 && entry == 0)
+		ddrc_writeb(range_protection_bits, reg8 + 1);
+	else
+		ddrc_writeb(range_protection_bits, reg8);
+
+	ddrc_writew(range_RID_check_bits, reg8 + 2);
+	ddrc_writew(range_WID_check_bits, reg8 + 4);
+	ddrc_writeb(range_RID_check_bits_ID_lookup, reg8 + 6);
+	ddrc_writeb(range_WID_check_bits_ID_lookup, reg8 + 7);
+}
+
+void cdns_ddr_enable_prot(void *base, int entry,
+			  enum cdns_ddr_range_prot range_protection_bits,
+			  u16 range_RID_check_bits,
+			  u16 range_WID_check_bits,
+			  u8 range_RID_check_bits_ID_lookup,
+			  u8 range_WID_check_bits_ID_lookup)
+{
+	int axi;
+
+	for (axi = 0; axi < DDR_NR_AXI_PORTS; axi++)
+		cdns_ddr_enable_port_prot(base, axi, entry,
+					  range_protection_bits,
+					  range_RID_check_bits,
+					  range_WID_check_bits,
+					  range_RID_check_bits_ID_lookup,
+					  range_WID_check_bits_ID_lookup);
+}
+
+void cdns_ddr_set_port_bandwidth(void *base, int port,
+				 u8 max_percent, u8 overflow_ok)
+{
+	u8 *reg8 = (u8 *)base + DDR_AXI_PORT_BANDWIDTH_REG;
+
+	reg8 += (port * 3);
+	pr_debug("%s port %d, (reg8=%p, DENALI_CTL_%d)\n",
+		 __func__, port, reg8, (reg8 - (u8 *)base) / 4);
+
+	ddrc_writeb(max_percent, reg8++);	/* Maximum bandwidth percentage */
+	ddrc_writeb(overflow_ok, reg8++);	/* Bandwidth overflow allowed */
+}
+
+void cdns_ddr_ctrl_init(void *ddr_ctrl_basex, int async,
+			const u32 *reg0, const u32 *reg350,
+			u32 ddr_start_addr, u32 ddr_size)
+{
+	int i, axi, entry;
+	u32 *ddr_ctrl_base = (u32 *)ddr_ctrl_basex;
+	u8 *base8 = (u8 *)ddr_ctrl_basex;
+
+	pr_debug("%s\n", __func__);
+	ddrc_writel(*reg0, ddr_ctrl_base + 0);
+	/* 1 to 6 are read only */
+	for (i = 7; i <= 26; i++)
+		ddrc_writel(*(reg0 + i), ddr_ctrl_base + i);
+	/* 27 to 29 are not changed */
+	for (i = 30; i <= 87; i++)
+		ddrc_writel(*(reg0 + i), ddr_ctrl_base + i);
+
+	/* Enable/disable ECC */
+	if (IS_ENABLED(CONFIG_CADENCE_DDR_CTRL_ENABLE_ECC)) {
+		pr_debug("%s enabling ECC\n", __func__);
+		ddrc_writeb(1, base8 + DDR_ECC_ENABLE_REG);
+	} else {
+		ddrc_writeb(0, base8 + DDR_ECC_ENABLE_REG);
+	}
+
+	/* ECC: Disable corruption for read/modify/write operations */
+	ddrc_writeb(1, base8 + DDR_ECC_DISABLE_W_UC_ERR_REG);
+
+	/* Set 8/16-bit data width using reduc bit (enable half datapath)*/
+	if (IS_ENABLED(CONFIG_CADENCE_DDR_CTRL_8BIT_WIDTH)) {
+		pr_debug("%s using 8-bit data\n", __func__);
+		ddrc_writeb(1, base8 + DDR_HALF_DATAPATH_REG);
+	} else {
+		ddrc_writeb(0, base8 + DDR_HALF_DATAPATH_REG);
+	}
+
+	/* Threshold for command queue */
+	ddrc_writeb(4, base8 + DDR_ARB_CMD_Q_THRESHOLD_REG);
+
+	/* AXI port protection => enable */
+	ddrc_writeb(0x01, base8 + DDR_AXI_PORT_PROT_ENABLE_REG);
+
+	/* Set port interface type, default port priority and bandwidths */
+	for (axi = 0; axi < DDR_NR_AXI_PORTS; axi++) {
+		/* port interface type: synchronous or asynchronous AXI clock */
+		u8 *fifo_reg = base8 + DDR_RW_FIFO_TYPE_REGS + (axi * 3);
+
+		if (async)
+			ddrc_writeb(0, fifo_reg);
+		else
+			ddrc_writeb(3, fifo_reg);
+
+		/* R/W priorities */
+		cdns_ddr_set_port_rw_priority(ddr_ctrl_base, axi, 2, 2);
+
+		/* AXI bandwidth */
+		cdns_ddr_set_port_bandwidth(ddr_ctrl_base, axi, 50, 1);
+	}
+
+	/*
+	 * The hardware requires that the valid address ranges must not overlap.
+	 * So, we initialise all address ranges to be above the DDR, length 0.
+	 */
+	for (entry = 0; entry < DDR_NR_ENTRIES; entry++)
+		cdns_ddr_enable_addr_range(ddr_ctrl_base, entry,
+					   ddr_start_addr + ddr_size, 0);
+
+	for (i = 350; i <= 374; i++)
+		ddrc_writel(*(reg350 - 350 + i), ddr_ctrl_base + i);
+
+	/* Disable optimised read-modify-write logic */
+	ddrc_writeb(0, base8 + DDR_OPT_RMODW_REG);
+
+	/*
+	 * Disable all interrupts, we are not handling them.
+	 * For detail of the interrupt mask, ack and status bits, see the
+	 * manual's description of the 'int_status' parameter.
+	 */
+	ddrc_writel(0, base8 + DDR_INTERRUPT_MASK);
+
+	/*
+	 * Default settings to enable full access to the entire DDR.
+	 * Users can set different ranges and access rights by calling these
+	 * functions before calling cdns_ddr_ctrl_start().
+	 */
+	cdns_ddr_enable_addr_range(ddr_ctrl_base, 0,
+				   ddr_start_addr, ddr_size);
+	cdns_ddr_enable_prot(ddr_ctrl_base, 0, CDNS_DDR_RANGE_PROT_BITS_FULL,
+			     0xffff, 0xffff, 0x0f, 0x0f);
+}
+
+void cdns_ddr_ctrl_start(void *ddr_ctrl_basex)
+{
+	u32 *ddr_ctrl_base = (u32 *)ddr_ctrl_basex;
+	u8 *base8 = (u8 *)ddr_ctrl_basex;
+
+	/* Start */
+	ddrc_writeb(1, base8 + DDR_START_REG);
+
+	/* Wait for controller to be ready (interrupt status) */
+	while (!(readl(base8 + DDR_INTERRUPT_STATUS) & 0x100))
+		;
+
+	/* clear all interrupts */
+	ddrc_writel(~0, base8 + DDR_INTERRUPT_ACK);
+
+	/* Step 19 Wait 500us from MRESETB=1 */
+	udelay(500);
+
+	/* Step 20 tCKSRX wait (From supply stable clock for MCK) */
+	/* DENALI_CTL_19 TREF_ENABLE=0x1(=1), AREFRESH=0x1(=1) */
+	ddrc_writel(0x01000100, ddr_ctrl_base + 19);
+}
diff --git a/drivers/ram/cadence/ddr_ctrl.h b/drivers/ram/cadence/ddr_ctrl.h
new file mode 100644
index 0000000000..638d986261
--- /dev/null
+++ b/drivers/ram/cadence/ddr_ctrl.h
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Cadence DDR Controller
+ *
+ * Copyright (C) 2015 Renesas Electronics Europe Ltd
+ */
+
+#ifndef CADENCE_DDR_CTRL_H
+#define CADENCE_DDR_CTRL_H
+
+enum cdns_ddr_range_prot {
+	CDNS_DDR_RANGE_PROT_BITS_PRIV_SECURE = 0,
+	CDNS_DDR_RANGE_PROT_BITS_SECURE = 1,
+	CDNS_DDR_RANGE_PROT_BITS_PRIV = 2,
+	CDNS_DDR_RANGE_PROT_BITS_FULL = 3,
+};
+
+/**
+ * Initialise the Cadence DDR Controller, but doesn't start it.
+ *
+ * It sets up the controller so that all 4 AXI slave ports allow access to all
+ * of the DDR with the same settings. This means that:
+ *  - Full access permisions.
+ *  - All read/write priorities are set to 2.
+ *  - Bandwidth is set to 50%, overflow is allowed, i.e. it's a soft limit.
+ * If you want different properties for different ports and/or addr ranges, call
+ * the other functions before calling cdns_ddr_ctrl_start().
+ *
+ * @ddr_ctrl_base  Physical address of the DDR Controller.
+ * @async          0 if DDR clock is synchronous with the controller clock
+ *                 otherwise 1.
+ * @reg0           Pointer to array of 32-bit values to be written to registers
+ *                 0 to 87. The values are generated by Cadence TCL scripts.
+ * @reg350         Pointer to array of 32-bit values to be written to registers
+ *                 350 to 374. The values are generated by Cadence TCL scripts.
+ * @ddr_start_addr Physical address of the start of DDR.
+ * @ddr_size       Size of the DDR in bytes. The controller will set the port
+ *                 protection range to match this size.
+ */
+void cdns_ddr_ctrl_init(void *ddr_ctrl_base, int async,
+			const u32 *reg0, const u32 *reg350,
+			u32 ddr_start_addr, u32 ddr_size);
+
+/**
+ * Start the Cadence DDR Controller.
+ *
+ * @ddr_ctrl_base  Physical address of the DDR Controller.
+ */
+void cdns_ddr_ctrl_start(void *ddr_ctrl_base);
+
+/**
+ * Set the priority for read and write operations for a specific AXI slave port.
+ *
+ * @base      Physical address of the DDR Controller.
+ * @port      Port number. Range is 0 to 3.
+ * @read_pri  Priority for reads.  Range is 0 to 3, where 0 is highest priority.
+ * @write_pri Priority for writes. Range is 0 to 3, where 0 is highest priority.
+ */
+void cdns_ddr_set_port_rw_priority(void *base, int port,
+				   u8 read_pri, u8 write_pri);
+
+/**
+ * Specify address range for a protection entry, for a specific AXI slave port.
+ *
+ * @base       Physical address of the DDR Controller.
+ * @port       Port number. Range is 0 to 3.
+ * @entry      The protection entry. Range is 0 to 15.
+ * @start_addr Physical of the address range, must be aligned to 16KB.
+ * @size       Size of the address range, must be multiple of 16KB.
+ */
+void cdns_ddr_enable_port_addr_range(void *base, int port, int entry,
+				     u32 addr_start, u32 size);
+
+/**
+ * Specify address range for a protection entry, for all AXI slave ports.
+ *
+ * @base       Physical address of the DDR Controller.
+ * @entry      The protection entry. Range is 0 to 15.
+ * @start_addr Physical of the address range, must be aligned to 16KB.
+ * @size       Size of the address range, must be multiple of 16KB.
+ */
+void cdns_ddr_enable_addr_range(void *base, int entry,
+				u32 addr_start, u32 size);
+
+/**
+ * Specify protection entry details, for a specific AXI slave port.
+ *
+ * See the hardware manual for details of the range check bits.
+ *
+ * @base       Physical address of the DDR Controller.
+ * @port       Port number. Range is 0 to 3.
+ * @entry      The protection entry. Range is 0 to 15.
+ */
+void cdns_ddr_enable_port_prot(void *base, int port, int entry,
+			       enum cdns_ddr_range_prot range_protection_bits,
+			       u16 range_RID_check_bits,
+			       u16 range_WID_check_bits,
+			       u8 range_RID_check_bits_ID_lookup,
+			       u8 range_WID_check_bits_ID_lookup);
+
+/**
+ * Specify protection entry details, for all AXI slave ports.
+ *
+ * See the hardware manual for details of the range check bits.
+ *
+ * @base       Physical address of the DDR Controller.
+ * @entry      The protection entry. Range is 0 to 15.
+ */
+void cdns_ddr_enable_prot(void *base, int entry,
+			  enum cdns_ddr_range_prot range_protection_bits,
+			  u16 range_RID_check_bits,
+			  u16 range_WID_check_bits,
+			  u8 range_RID_check_bits_ID_lookup,
+			  u8 range_WID_check_bits_ID_lookup);
+
+/**
+ * Specify bandwidth for each AXI port.
+ *
+ * See the hardware manual for details of the range check bits.
+ *
+ * @base       Physical address of the DDR Controller.
+ * @port       Port number. Range is 0 to 3.
+ * @max_percent     0 to 100.
+ */
+void cdns_ddr_set_port_bandwidth(void *base, int port,
+				 u8 max_percent, u8 overflow_ok);
+
+/* Standard JEDEC registers */
+#define MODE_REGISTER_MASK		(3 << 14)
+#define MODE_REGISTER_MR0		(0 << 14)
+#define MODE_REGISTER_MR1		(1 << 14)
+#define MODE_REGISTER_MR2		(2 << 14)
+#define MODE_REGISTER_MR3		(3 << 14)
+#define MR1_DRIVE_STRENGTH_MASK		((1 << 5) | (1 << 1))
+#define MR1_DRIVE_STRENGTH_34_OHMS	((0 << 5) | (1 << 1))
+#define MR1_DRIVE_STRENGTH_40_OHMS	((0 << 5) | (0 << 1))
+#define MR1_ODT_IMPEDANCE_MASK		((1 << 9) | (1 << 6) | (1 << 2))
+#define MR1_ODT_IMPEDANCE_60_OHMS	((0 << 9) | (0 << 6) | (1 << 2))
+#define MR1_ODT_IMPEDANCE_120_OHMS	((0 << 9) | (1 << 6) | (0 << 2))
+#define MR1_ODT_IMPEDANCE_40_OHMS	((0 << 9) | (1 << 6) | (1 << 2))
+#define MR1_ODT_IMPEDANCE_20_OHMS	((1 << 9) | (0 << 6) | (0 << 2))
+#define MR1_ODT_IMPEDANCE_30_OHMS	((1 << 9) | (0 << 6) | (1 << 2))
+#define MR2_DYNAMIC_ODT_MASK		(3 << 9)
+#define MR2_DYNAMIC_ODT_OFF		(0 << 9)
+#define MR2_SELF_REFRESH_TEMP_MASK	(1 << 7)
+#define MR2_SELF_REFRESH_TEMP_EXT	(1 << 7)
+
+/**
+ * Set certain fields of the JEDEC MR1 register.
+ */
+void cdns_ddr_set_mr1(void *base, int cs, u16 odt_impedance, u16 drive_strength);
+
+/**
+ * Set certain fields of the JEDEC MR2 register.
+ */
+void cdns_ddr_set_mr2(void *base, int cs, u16 dynamic_odt, u16 self_refresh_temp);
+
+/**
+ * Set ODT map of the DDR Controller.
+ */
+void cdns_ddr_set_odt_map(void *base, int cs, u16 odt_map);
+
+/**
+ * Set ODT settings in the DDR Controller.
+ */
+void cdns_ddr_set_odt_times(void *base, u8 TODTL_2CMD, u8 TODTH_WR, u8 TODTH_RD,
+			    u8 WR_TO_ODTH, u8 RD_TO_ODTH);
+
+void cdns_ddr_set_same_cs_delays(void *base, u8 r2r, u8 r2w, u8 w2r, u8 w2w);
+void cdns_ddr_set_diff_cs_delays(void *base, u8 r2r, u8 r2w, u8 w2r, u8 w2w);
+
+#endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v1 6/9] dts: basic devicetree for Renesas RZ/N1 SoC
  2022-08-09 12:59 [RFC PATCH v1 0/9] Renesas RZ/N1 SoC initial support Ralph Siemsen
                   ` (4 preceding siblings ...)
  2022-08-09 12:59 ` [RFC PATCH v1 5/9] ram: cadence: add driver for Cadence EDAC Ralph Siemsen
@ 2022-08-09 12:59 ` Ralph Siemsen
  2022-08-09 12:59 ` [RFC PATCH v1 7/9] ARM: rzn1: basic support " Ralph Siemsen
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 29+ messages in thread
From: Ralph Siemsen @ 2022-08-09 12:59 UTC (permalink / raw)
  To: u-boot; +Cc: Ralph Siemsen

This is taken from Linux kernel 5.17, and contains just bare minimum
functionality: CPU, UART and system timer.

Additional functionality (from newer kernel versions) will be added
later. Note that the Linux side is under active development.

Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
---
The following changes were made, compared with Linux 5.17:

1) Add node for system controller registers.
   Declare it as syscon to provide a regmap interface.

2) In the clock controller node (renesas,r9a06g032-sysctrl),
   replace regs with regmap.

3) Add syscon-reset node, making use of the syscon regmap.

I could not find a way to avoid 1) and 2). Putting "syscon" in the
compatible string for the clock controller leads to a catch-22,
where the driver fails to initialize, and then boot hangs.

 arch/arm/dts/r9a06g032.dtsi                   | 225 ++++++++++++++++++
 include/dt-bindings/clock/r9a06g032-sysctrl.h | 148 ++++++++++++
 2 files changed, 373 insertions(+)
 create mode 100644 arch/arm/dts/r9a06g032.dtsi
 create mode 100644 include/dt-bindings/clock/r9a06g032-sysctrl.h

diff --git a/arch/arm/dts/r9a06g032.dtsi b/arch/arm/dts/r9a06g032.dtsi
new file mode 100644
index 0000000000..a276a43e72
--- /dev/null
+++ b/arch/arm/dts/r9a06g032.dtsi
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Base Device Tree Source for the Renesas RZ/N1D (R9A06G032)
+ *
+ * Copyright (C) 2018 Renesas Electronics Europe Limited
+ *
+ */
+
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/clock/r9a06g032-sysctrl.h>
+
+/ {
+	compatible = "renesas,r9a06g032";
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu@0 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a7";
+			reg = <0>;
+			clocks = <&sysctrl R9A06G032_CLK_A7MP>;
+		};
+
+		cpu@1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a7";
+			reg = <1>;
+			clocks = <&sysctrl R9A06G032_CLK_A7MP>;
+			enable-method = "renesas,r9a06g032-smp";
+			cpu-release-addr = <0 0x4000c204>;
+		};
+	};
+
+	ext_jtag_clk: extjtagclk {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <0>;
+	};
+
+	ext_mclk: extmclk {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <40000000>;
+	};
+
+	ext_rgmii_ref: extrgmiiref {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <0>;
+	};
+
+	ext_rtc_clk: extrtcclk {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <0>;
+	};
+
+	soc {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		interrupt-parent = <&gic>;
+		ranges;
+
+		plat_regs: system-controller@4000c000 {
+			compatible = "syscon";
+			reg = <0x4000c000 0x1000>;
+			u-boot,dm-pre-reloc;
+		};
+
+		sysctrl: clock {
+			compatible = "renesas,r9a06g032-sysctrl";
+			#clock-cells = <1>;
+			regmap = <&plat_regs>;
+			u-boot,dm-pre-reloc;
+
+			clocks = <&ext_mclk>, <&ext_rtc_clk>,
+					<&ext_jtag_clk>, <&ext_rgmii_ref>;
+			clock-names = "mclk", "rtc", "jtag", "rgmii_ref_ext";
+		};
+
+		ddrctrl: memory-controller@4000d000 {
+			compatible = "cadence,ddr-ctrl";
+			reg = <0x4000d000 0x1000>, <0x4000e000 0x100>;
+			reg-names = "ddrc", "phy";
+			interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&sysctrl R9A06G032_CLK_DDRC>, <&sysctrl R9A06G032_HCLK_DDRC>;
+			clock-names = "clk_ddrc", "hclk_ddrc";
+			status = "disabled";
+		};
+
+		uart0: serial@40060000 {
+			compatible = "renesas,r9a06g032-uart", "renesas,rzn1-uart", "snps,dw-apb-uart";
+			reg = <0x40060000 0x400>;
+			interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+			reg-shift = <2>;
+			reg-io-width = <4>;
+			clocks = <&sysctrl R9A06G032_CLK_UART0>, <&sysctrl R9A06G032_HCLK_UART0>;
+			clock-names = "baudclk", "apb_pclk";
+			status = "disabled";
+		};
+
+		uart1: serial@40061000 {
+			compatible = "renesas,r9a06g032-uart", "renesas,rzn1-uart", "snps,dw-apb-uart";
+			reg = <0x40061000 0x400>;
+			interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
+			reg-shift = <2>;
+			reg-io-width = <4>;
+			clocks = <&sysctrl R9A06G032_CLK_UART1>, <&sysctrl R9A06G032_HCLK_UART1>;
+			clock-names = "baudclk", "apb_pclk";
+			status = "disabled";
+		};
+
+		uart2: serial@40062000 {
+			compatible = "renesas,r9a06g032-uart", "renesas,rzn1-uart", "snps,dw-apb-uart";
+			reg = <0x40062000 0x400>;
+			interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+			reg-shift = <2>;
+			reg-io-width = <4>;
+			clocks = <&sysctrl R9A06G032_CLK_UART2>, <&sysctrl R9A06G032_HCLK_UART2>;
+			clock-names = "baudclk", "apb_pclk";
+			status = "disabled";
+		};
+
+		uart3: serial@50000000 {
+			compatible = "renesas,r9a06g032-uart", "renesas,rzn1-uart";
+			reg = <0x50000000 0x400>;
+			interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
+			reg-shift = <2>;
+			reg-io-width = <4>;
+			clocks = <&sysctrl R9A06G032_CLK_UART3>, <&sysctrl R9A06G032_HCLK_UART3>;
+			clock-names = "baudclk", "apb_pclk";
+			status = "disabled";
+		};
+
+		uart4: serial@50001000 {
+			compatible = "renesas,r9a06g032-uart", "renesas,rzn1-uart";
+			reg = <0x50001000 0x400>;
+			interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
+			reg-shift = <2>;
+			reg-io-width = <4>;
+			clocks = <&sysctrl R9A06G032_CLK_UART4>, <&sysctrl R9A06G032_HCLK_UART4>;
+			clock-names = "baudclk", "apb_pclk";
+			status = "disabled";
+		};
+
+		uart5: serial@50002000 {
+			compatible = "renesas,r9a06g032-uart", "renesas,rzn1-uart";
+			reg = <0x50002000 0x400>;
+			interrupts = <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>;
+			reg-shift = <2>;
+			reg-io-width = <4>;
+			clocks = <&sysctrl R9A06G032_CLK_UART5>, <&sysctrl R9A06G032_HCLK_UART5>;
+			clock-names = "baudclk", "apb_pclk";
+			status = "disabled";
+		};
+
+		uart6: serial@50003000 {
+			compatible = "renesas,r9a06g032-uart", "renesas,rzn1-uart";
+			reg = <0x50003000 0x400>;
+			interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
+			reg-shift = <2>;
+			reg-io-width = <4>;
+			clocks = <&sysctrl R9A06G032_CLK_UART6>, <&sysctrl R9A06G032_HCLK_UART6>;
+			clock-names = "baudclk", "apb_pclk";
+			status = "disabled";
+		};
+
+		uart7: serial@50004000 {
+			compatible = "renesas,r9a06g032-uart", "renesas,rzn1-uart";
+			reg = <0x50004000 0x400>;
+			interrupts = <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
+			reg-shift = <2>;
+			reg-io-width = <4>;
+			clocks = <&sysctrl R9A06G032_CLK_UART7>, <&sysctrl R9A06G032_HCLK_UART7>;
+			clock-names = "baudclk", "apb_pclk";
+			status = "disabled";
+		};
+
+		pinctrl: pinctrl@40067000 {
+			compatible = "renesas,r9a06g032-pinctrl", "renesas,rzn1-pinctrl";
+			reg = <0x40067000 0x1000>, <0x51000000 0x480>;
+			clocks = <&sysctrl R9A06G032_HCLK_PINCONFIG>;
+			clock-names = "bus";
+			status = "disabled";
+		};
+
+		gic: interrupt-controller@44101000 {
+			compatible = "arm,gic-400", "arm,cortex-a7-gic";
+			interrupt-controller;
+			#interrupt-cells = <3>;
+			reg = <0x44101000 0x1000>, /* Distributer */
+			      <0x44102000 0x2000>, /* CPU interface */
+			      <0x44104000 0x2000>, /* Virt interface control */
+			      <0x44106000 0x2000>; /* Virt CPU interface */
+			interrupts =
+				<GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>;
+		};
+	};
+
+	timer {
+		compatible = "arm,cortex-a7-timer",
+			     "arm,armv7-timer";
+		interrupt-parent = <&gic>;
+		arm,cpu-registers-not-fw-configured;
+		always-on;
+		interrupts =
+			<GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>,
+			<GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>,
+			<GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>,
+			<GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>;
+	};
+
+	reboot {
+		compatible = "syscon-reboot";
+		regmap = <&plat_regs>;
+		offset = <0x198>;       /* sysctrl.RSTEN */
+		mask = <0x40>;          /* bit 6 = SWRST_REQ */
+		value = <0x40>;
+	};
+};
diff --git a/include/dt-bindings/clock/r9a06g032-sysctrl.h b/include/dt-bindings/clock/r9a06g032-sysctrl.h
new file mode 100644
index 0000000000..90c0f3dc1b
--- /dev/null
+++ b/include/dt-bindings/clock/r9a06g032-sysctrl.h
@@ -0,0 +1,148 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * R9A06G032 sysctrl IDs
+ *
+ * Copyright (C) 2018 Renesas Electronics Europe Limited
+ *
+ * Michel Pollet <michel.pollet@bp.renesas.com>, <buserror@gmail.com>
+ */
+
+#ifndef __DT_BINDINGS_R9A06G032_SYSCTRL_H__
+#define __DT_BINDINGS_R9A06G032_SYSCTRL_H__
+
+#define R9A06G032_CLK_PLL_USB		1
+#define R9A06G032_CLK_48		1	/* AKA CLK_PLL_USB */
+#define R9A06G032_MSEBIS_CLK		3	/* AKA CLKOUT_D16 */
+#define R9A06G032_MSEBIM_CLK		3	/* AKA CLKOUT_D16 */
+#define R9A06G032_CLK_DDRPHY_PLLCLK	5	/* AKA CLKOUT_D1OR2 */
+#define R9A06G032_CLK50			6	/* AKA CLKOUT_D20 */
+#define R9A06G032_CLK25			7	/* AKA CLKOUT_D40 */
+#define R9A06G032_CLK125		9	/* AKA CLKOUT_D8 */
+#define R9A06G032_CLK_P5_PG1		17	/* AKA DIV_P5_PG */
+#define R9A06G032_CLK_REF_SYNC		21	/* AKA DIV_REF_SYNC */
+#define R9A06G032_CLK_25_PG4		26
+#define R9A06G032_CLK_25_PG5		27
+#define R9A06G032_CLK_25_PG6		28
+#define R9A06G032_CLK_25_PG7		29
+#define R9A06G032_CLK_25_PG8		30
+#define R9A06G032_CLK_ADC		31
+#define R9A06G032_CLK_ECAT100		32
+#define R9A06G032_CLK_HSR100		33
+#define R9A06G032_CLK_I2C0		34
+#define R9A06G032_CLK_I2C1		35
+#define R9A06G032_CLK_MII_REF		36
+#define R9A06G032_CLK_NAND		37
+#define R9A06G032_CLK_NOUSBP2_PG6	38
+#define R9A06G032_CLK_P1_PG2		39
+#define R9A06G032_CLK_P1_PG3		40
+#define R9A06G032_CLK_P1_PG4		41
+#define R9A06G032_CLK_P4_PG3		42
+#define R9A06G032_CLK_P4_PG4		43
+#define R9A06G032_CLK_P6_PG1		44
+#define R9A06G032_CLK_P6_PG2		45
+#define R9A06G032_CLK_P6_PG3		46
+#define R9A06G032_CLK_P6_PG4		47
+#define R9A06G032_CLK_PCI_USB		48
+#define R9A06G032_CLK_QSPI0		49
+#define R9A06G032_CLK_QSPI1		50
+#define R9A06G032_CLK_RGMII_REF		51
+#define R9A06G032_CLK_RMII_REF		52
+#define R9A06G032_CLK_SDIO0		53
+#define R9A06G032_CLK_SDIO1		54
+#define R9A06G032_CLK_SERCOS100		55
+#define R9A06G032_CLK_SLCD		56
+#define R9A06G032_CLK_SPI0		57
+#define R9A06G032_CLK_SPI1		58
+#define R9A06G032_CLK_SPI2		59
+#define R9A06G032_CLK_SPI3		60
+#define R9A06G032_CLK_SPI4		61
+#define R9A06G032_CLK_SPI5		62
+#define R9A06G032_CLK_SWITCH		63
+#define R9A06G032_HCLK_ECAT125		65
+#define R9A06G032_HCLK_PINCONFIG	66
+#define R9A06G032_HCLK_SERCOS		67
+#define R9A06G032_HCLK_SGPIO2		68
+#define R9A06G032_HCLK_SGPIO3		69
+#define R9A06G032_HCLK_SGPIO4		70
+#define R9A06G032_HCLK_TIMER0		71
+#define R9A06G032_HCLK_TIMER1		72
+#define R9A06G032_HCLK_USBF		73
+#define R9A06G032_HCLK_USBH		74
+#define R9A06G032_HCLK_USBPM		75
+#define R9A06G032_CLK_48_PG_F		76
+#define R9A06G032_CLK_48_PG4		77
+#define R9A06G032_CLK_DDRPHY_PCLK	81	/* AKA CLK_REF_SYNC_D4 */
+#define R9A06G032_CLK_FW		81	/* AKA CLK_REF_SYNC_D4 */
+#define R9A06G032_CLK_CRYPTO		81	/* AKA CLK_REF_SYNC_D4 */
+#define R9A06G032_CLK_A7MP		84	/* AKA DIV_CA7 */
+#define R9A06G032_HCLK_CAN0		85
+#define R9A06G032_HCLK_CAN1		86
+#define R9A06G032_HCLK_DELTASIGMA	87
+#define R9A06G032_HCLK_PWMPTO		88
+#define R9A06G032_HCLK_RSV		89
+#define R9A06G032_HCLK_SGPIO0		90
+#define R9A06G032_HCLK_SGPIO1		91
+#define R9A06G032_RTOS_MDC		92
+#define R9A06G032_CLK_CM3		93
+#define R9A06G032_CLK_DDRC		94
+#define R9A06G032_CLK_ECAT25		95
+#define R9A06G032_CLK_HSR50		96
+#define R9A06G032_CLK_HW_RTOS		97
+#define R9A06G032_CLK_SERCOS50		98
+#define R9A06G032_HCLK_ADC		99
+#define R9A06G032_HCLK_CM3		100
+#define R9A06G032_HCLK_CRYPTO_EIP150	101
+#define R9A06G032_HCLK_CRYPTO_EIP93	102
+#define R9A06G032_HCLK_DDRC		103
+#define R9A06G032_HCLK_DMA0		104
+#define R9A06G032_HCLK_DMA1		105
+#define R9A06G032_HCLK_GMAC0		106
+#define R9A06G032_HCLK_GMAC1		107
+#define R9A06G032_HCLK_GPIO0		108
+#define R9A06G032_HCLK_GPIO1		109
+#define R9A06G032_HCLK_GPIO2		110
+#define R9A06G032_HCLK_HSR		111
+#define R9A06G032_HCLK_I2C0		112
+#define R9A06G032_HCLK_I2C1		113
+#define R9A06G032_HCLK_LCD		114
+#define R9A06G032_HCLK_MSEBI_M		115
+#define R9A06G032_HCLK_MSEBI_S		116
+#define R9A06G032_HCLK_NAND		117
+#define R9A06G032_HCLK_PG_I		118
+#define R9A06G032_HCLK_PG19		119
+#define R9A06G032_HCLK_PG20		120
+#define R9A06G032_HCLK_PG3		121
+#define R9A06G032_HCLK_PG4		122
+#define R9A06G032_HCLK_QSPI0		123
+#define R9A06G032_HCLK_QSPI1		124
+#define R9A06G032_HCLK_ROM		125
+#define R9A06G032_HCLK_RTC		126
+#define R9A06G032_HCLK_SDIO0		127
+#define R9A06G032_HCLK_SDIO1		128
+#define R9A06G032_HCLK_SEMAP		129
+#define R9A06G032_HCLK_SPI0		130
+#define R9A06G032_HCLK_SPI1		131
+#define R9A06G032_HCLK_SPI2		132
+#define R9A06G032_HCLK_SPI3		133
+#define R9A06G032_HCLK_SPI4		134
+#define R9A06G032_HCLK_SPI5		135
+#define R9A06G032_HCLK_SWITCH		136
+#define R9A06G032_HCLK_SWITCH_RG	137
+#define R9A06G032_HCLK_UART0		138
+#define R9A06G032_HCLK_UART1		139
+#define R9A06G032_HCLK_UART2		140
+#define R9A06G032_HCLK_UART3		141
+#define R9A06G032_HCLK_UART4		142
+#define R9A06G032_HCLK_UART5		143
+#define R9A06G032_HCLK_UART6		144
+#define R9A06G032_HCLK_UART7		145
+#define R9A06G032_CLK_UART0		146
+#define R9A06G032_CLK_UART1		147
+#define R9A06G032_CLK_UART2		148
+#define R9A06G032_CLK_UART3		149
+#define R9A06G032_CLK_UART4		150
+#define R9A06G032_CLK_UART5		151
+#define R9A06G032_CLK_UART6		152
+#define R9A06G032_CLK_UART7		153
+
+#endif /* __DT_BINDINGS_R9A06G032_SYSCTRL_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v1 7/9] ARM: rzn1: basic support for Renesas RZ/N1 SoC
  2022-08-09 12:59 [RFC PATCH v1 0/9] Renesas RZ/N1 SoC initial support Ralph Siemsen
                   ` (5 preceding siblings ...)
  2022-08-09 12:59 ` [RFC PATCH v1 6/9] dts: basic devicetree for Renesas RZ/N1 SoC Ralph Siemsen
@ 2022-08-09 12:59 ` Ralph Siemsen
  2022-08-09 12:59 ` [RFC PATCH v1 8/9] board: schneider: add LCES board support Ralph Siemsen
  2022-08-09 12:59 ` [RFC PATCH v1 9/9] tools: Add tool to create Renesas SPKG images Ralph Siemsen
  8 siblings, 0 replies; 29+ messages in thread
From: Ralph Siemsen @ 2022-08-09 12:59 UTC (permalink / raw)
  To: u-boot; +Cc: Ralph Siemsen, Bharat Gooty, Rayagonda Kokatanur

The RZ/N1 is a family of SoC devics from Renesas, featuring:

* ARM Cortex-A7 CPU (single/dual core) and/or Cortex-M3
* Integrated SRAM up to 6MB
* Integrated gigabit ethernet switch
* Optional DDR2/3 controller
* I2C, SPI, UART, NAND, QSPI, SDIO, USB, CAN, RTC, LCD

Add basic support in the form of ARCH_RZN1 symbol.

Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
---

 arch/arm/Kconfig              | 17 +++++++++++++++++
 arch/arm/Makefile             |  1 +
 arch/arm/mach-rzn1/Kconfig    | 18 ++++++++++++++++++
 arch/arm/mach-rzn1/Makefile   |  3 +++
 arch/arm/mach-rzn1/cpu_info.c | 20 ++++++++++++++++++++
 5 files changed, 59 insertions(+)
 create mode 100644 arch/arm/mach-rzn1/Kconfig
 create mode 100644 arch/arm/mach-rzn1/Makefile
 create mode 100644 arch/arm/mach-rzn1/cpu_info.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 949ebb46ba..e4a4aba4bc 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1083,6 +1083,21 @@ config ARCH_RMOBILE
 	imply SYS_THUMB_BUILD
 	imply ARCH_MISC_INIT if DISPLAY_CPUINFO
 
+config ARCH_RZN1
+	bool "Reneasa RZ/N1 SoC"
+	select CLK
+	select CLK_RENESAS
+	select CLK_R9A06G032
+	select DM
+	select DM_ETH
+	select DM_SERIAL
+	select PINCTRL
+	select PINCONF
+	select REGMAP
+	select SYSRESET
+	select SYSRESET_SYSCON
+	imply CMD_DM
+
 config ARCH_SNAPDRAGON
 	bool "Qualcomm Snapdragon SoCs"
 	select ARM64
@@ -2243,6 +2258,8 @@ source "arch/arm/mach-owl/Kconfig"
 
 source "arch/arm/mach-rmobile/Kconfig"
 
+source "arch/arm/mach-rzn1/Kconfig"
+
 source "arch/arm/mach-meson/Kconfig"
 
 source "arch/arm/mach-mediatek/Kconfig"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 1f4a1d5788..f8b6b35a47 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -76,6 +76,7 @@ machine-$(CONFIG_ARCH_ORION5X)		+= orion5x
 machine-$(CONFIG_ARCH_OWL)		+= owl
 machine-$(CONFIG_ARCH_RMOBILE)		+= rmobile
 machine-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip
+machine-$(CONFIG_ARCH_RZN1)		+= rzn1
 machine-$(CONFIG_ARCH_S5PC1XX)		+= s5pc1xx
 machine-$(CONFIG_ARCH_SNAPDRAGON)	+= snapdragon
 machine-$(CONFIG_ARCH_SOCFPGA)		+= socfpga
diff --git a/arch/arm/mach-rzn1/Kconfig b/arch/arm/mach-rzn1/Kconfig
new file mode 100644
index 0000000000..707895874d
--- /dev/null
+++ b/arch/arm/mach-rzn1/Kconfig
@@ -0,0 +1,18 @@
+if ARCH_RZN1
+
+choice
+	prompt "Target Renesas RZ/N1 SoC select"
+	default RZN1
+
+config RZN1
+	bool "Renesas ARM SoCs RZ/N1 (32bit)"
+	select CPU_V7A
+	select ARMV7_SET_CORTEX_SMPEN if !SPL
+	select SPL_ARMV7_SET_CORTEX_SMPEN if SPL
+
+endchoice
+
+config SYS_SOC
+	default "rzn1"
+
+endif
diff --git a/arch/arm/mach-rzn1/Makefile b/arch/arm/mach-rzn1/Makefile
new file mode 100644
index 0000000000..b20f845c0f
--- /dev/null
+++ b/arch/arm/mach-rzn1/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-y = cpu_info.o
diff --git a/arch/arm/mach-rzn1/cpu_info.c b/arch/arm/mach-rzn1/cpu_info.c
new file mode 100644
index 0000000000..af02a26af8
--- /dev/null
+++ b/arch/arm/mach-rzn1/cpu_info.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <common.h>
+#include <cpu_func.h>
+
+#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF)
+void enable_caches(void)
+{
+	/* FIXME: when enabled, boot hangs at relocate_code */
+	//dcache_enable();
+}
+#endif
+
+#ifdef CONFIG_DISPLAY_CPUINFO
+int print_cpuinfo(void)
+{
+	printf("CPU: Renesas Electronics RZ/N1\n");
+	return 0;
+}
+#endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v1 8/9] board: schneider: add LCES board support
  2022-08-09 12:59 [RFC PATCH v1 0/9] Renesas RZ/N1 SoC initial support Ralph Siemsen
                   ` (6 preceding siblings ...)
  2022-08-09 12:59 ` [RFC PATCH v1 7/9] ARM: rzn1: basic support " Ralph Siemsen
@ 2022-08-09 12:59 ` Ralph Siemsen
  2022-08-09 12:59 ` [RFC PATCH v1 9/9] tools: Add tool to create Renesas SPKG images Ralph Siemsen
  8 siblings, 0 replies; 29+ messages in thread
From: Ralph Siemsen @ 2022-08-09 12:59 UTC (permalink / raw)
  To: u-boot; +Cc: Ralph Siemsen

Add support for Schneider Electronics LCES1 / LCES2 boards, which are
based on the Reneasas RZ/N1 SoC devices.

The intention is to support both boards using a single defconfig, and to
handle the differences at runtime.

Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
---
TODO: remove the debug UART settings from lces_defconfig.

 arch/arm/dts/r9a06g032-rzn1d400-lces.dts      |  50 +++
 arch/arm/mach-rzn1/Kconfig                    |  14 +
 board/schneider/lces/Kconfig                  |  12 +
 board/schneider/lces/Makefile                 |   3 +
 board/schneider/lces/ddr_timing.c             | 140 ++++++
 .../lces/jedec_ddr3_2g_x16_1333h_500_cl8.h    | 399 ++++++++++++++++++
 board/schneider/lces/lces.c                   |  41 ++
 configs/lces_defconfig                        |  26 ++
 include/configs/lces.h                        |  22 +
 9 files changed, 707 insertions(+)
 create mode 100644 arch/arm/dts/r9a06g032-rzn1d400-lces.dts
 create mode 100644 board/schneider/lces/Kconfig
 create mode 100644 board/schneider/lces/Makefile
 create mode 100644 board/schneider/lces/ddr_timing.c
 create mode 100644 board/schneider/lces/jedec_ddr3_2g_x16_1333h_500_cl8.h
 create mode 100644 board/schneider/lces/lces.c
 create mode 100644 configs/lces_defconfig
 create mode 100644 include/configs/lces.h

diff --git a/arch/arm/dts/r9a06g032-rzn1d400-lces.dts b/arch/arm/dts/r9a06g032-rzn1d400-lces.dts
new file mode 100644
index 0000000000..7ba4dedc10
--- /dev/null
+++ b/arch/arm/dts/r9a06g032-rzn1d400-lces.dts
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device Tree Source for Schneider LCES Board
+ *
+ * Based on r9a06g032-rzn1d400-db.dts
+ */
+
+/dts-v1/;
+
+#include "r9a06g032.dtsi"
+#include <dt-bindings/pinctrl/rzn1-pinctrl.h>
+
+/ {
+	model = "Schneider LCES Board";
+	compatible = "schneider,rzn1d400-lces", "renesas,r9a06g032";
+
+	chosen {
+		stdout-path = "serial0:115200n8";
+	};
+
+	aliases {
+		serial0 = &uart0;
+	};
+
+	/* TODO: (RFS) only RZN1D has DDR */
+	memory {
+		device_type = "memory";
+		reg = <0x80000000 0x10000000>;
+	};
+};
+
+&ddrctrl {
+	status = "okay";
+};
+&pinctrl {
+	status = "okay";
+
+	pins_uart0: pins_uart0 {
+		    pinmux = <
+			    RZN1_PINMUX(103, RZN1_FUNC_UART0_I)     /* UART0_TXD */
+			    RZN1_PINMUX(104, RZN1_FUNC_UART0_I)     /* UART0_RXD */
+			    >;
+		    bias-disable;
+	    };
+};
+&uart0 {
+	pinctrl-0 = <&pins_uart0>;
+	pinctrl-names = "default";
+	status = "okay";
+};
diff --git a/arch/arm/mach-rzn1/Kconfig b/arch/arm/mach-rzn1/Kconfig
index 707895874d..9bbafeb6db 100644
--- a/arch/arm/mach-rzn1/Kconfig
+++ b/arch/arm/mach-rzn1/Kconfig
@@ -15,4 +15,18 @@ endchoice
 config SYS_SOC
 	default "rzn1"
 
+choice
+	prompt "Board select"
+	default TARGET_SCHNEIDER_LCES
+
+config TARGET_SCHNEIDER_LCES
+	bool "Schneider LCES board"
+	help
+	  Support the Schneider LCES1 and LCES2 boards, which are based
+	  on the Renesas RZ/N1 SoC.
+
+endchoice
+
+source "board/schneider/lces/Kconfig"
+
 endif
diff --git a/board/schneider/lces/Kconfig b/board/schneider/lces/Kconfig
new file mode 100644
index 0000000000..6a1448bbd7
--- /dev/null
+++ b/board/schneider/lces/Kconfig
@@ -0,0 +1,12 @@
+if TARGET_SCHNEIDER_LCES
+
+config SYS_BOARD
+	default "lces"
+
+config SYS_VENDOR
+	default "schneider"
+
+config SYS_CONFIG_NAME
+	default "lces"
+
+endif
diff --git a/board/schneider/lces/Makefile b/board/schneider/lces/Makefile
new file mode 100644
index 0000000000..b8f6a8f53f
--- /dev/null
+++ b/board/schneider/lces/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier:	GPL-2.0+
+
+obj-y	:= lces.o ddr_timing.o
diff --git a/board/schneider/lces/ddr_timing.c b/board/schneider/lces/ddr_timing.c
new file mode 100644
index 0000000000..8bc3fe7be4
--- /dev/null
+++ b/board/schneider/lces/ddr_timing.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <asm/types.h>
+
+#include "jedec_ddr3_2g_x16_1333h_500_cl8.h"
+
+u32 ddr_00_87_async[] = {
+	DENALI_CTL_00_DATA,
+	DENALI_CTL_01_DATA,
+	DENALI_CTL_02_DATA,
+	DENALI_CTL_03_DATA,
+	DENALI_CTL_04_DATA,
+	DENALI_CTL_05_DATA,
+	DENALI_CTL_06_DATA,
+	DENALI_CTL_07_DATA,
+	DENALI_CTL_08_DATA,
+	DENALI_CTL_09_DATA,
+
+	DENALI_CTL_10_DATA,
+	DENALI_CTL_11_DATA,
+	DENALI_CTL_12_DATA,
+	DENALI_CTL_13_DATA,
+	DENALI_CTL_14_DATA,
+	DENALI_CTL_15_DATA,
+	DENALI_CTL_16_DATA,
+	DENALI_CTL_17_DATA,
+	DENALI_CTL_18_DATA,
+	DENALI_CTL_19_DATA,
+
+	DENALI_CTL_20_DATA,
+	DENALI_CTL_21_DATA,
+	DENALI_CTL_22_DATA,
+	DENALI_CTL_23_DATA,
+	DENALI_CTL_24_DATA,
+	DENALI_CTL_25_DATA,
+	DENALI_CTL_26_DATA,
+	DENALI_CTL_27_DATA,
+	DENALI_CTL_28_DATA,
+	DENALI_CTL_29_DATA,
+
+	DENALI_CTL_30_DATA,
+	DENALI_CTL_31_DATA,
+	DENALI_CTL_32_DATA,
+	DENALI_CTL_33_DATA,
+	DENALI_CTL_34_DATA,
+	DENALI_CTL_35_DATA,
+	DENALI_CTL_36_DATA,
+	DENALI_CTL_37_DATA,
+	DENALI_CTL_38_DATA,
+	DENALI_CTL_39_DATA,
+
+	DENALI_CTL_40_DATA,
+	DENALI_CTL_41_DATA,
+	DENALI_CTL_42_DATA,
+	DENALI_CTL_43_DATA,
+	DENALI_CTL_44_DATA,
+	DENALI_CTL_45_DATA,
+	DENALI_CTL_46_DATA,
+	DENALI_CTL_47_DATA,
+	DENALI_CTL_48_DATA,
+	DENALI_CTL_49_DATA,
+
+	DENALI_CTL_50_DATA,
+	DENALI_CTL_51_DATA,
+	DENALI_CTL_52_DATA,
+	DENALI_CTL_53_DATA,
+	DENALI_CTL_54_DATA,
+	DENALI_CTL_55_DATA,
+	DENALI_CTL_56_DATA,
+	DENALI_CTL_57_DATA,
+	DENALI_CTL_58_DATA,
+	DENALI_CTL_59_DATA,
+
+	DENALI_CTL_60_DATA,
+	DENALI_CTL_61_DATA,
+	DENALI_CTL_62_DATA,
+	DENALI_CTL_63_DATA,
+	DENALI_CTL_64_DATA,
+	DENALI_CTL_65_DATA,
+	DENALI_CTL_66_DATA,
+	DENALI_CTL_67_DATA,
+	DENALI_CTL_68_DATA,
+	DENALI_CTL_69_DATA,
+
+	DENALI_CTL_70_DATA,
+	DENALI_CTL_71_DATA,
+	DENALI_CTL_72_DATA,
+	DENALI_CTL_73_DATA,
+	DENALI_CTL_74_DATA,
+	DENALI_CTL_75_DATA,
+	DENALI_CTL_76_DATA,
+	DENALI_CTL_77_DATA,
+	DENALI_CTL_78_DATA,
+	DENALI_CTL_79_DATA,
+
+	DENALI_CTL_80_DATA,
+	DENALI_CTL_81_DATA,
+	DENALI_CTL_82_DATA,
+	DENALI_CTL_83_DATA,
+	DENALI_CTL_84_DATA,
+	DENALI_CTL_85_DATA,
+	DENALI_CTL_86_DATA,
+	DENALI_CTL_87_DATA,
+	DENALI_CTL_88_DATA,
+	DENALI_CTL_89_DATA,
+
+	DENALI_CTL_90_DATA,
+	DENALI_CTL_91_DATA,
+	DENALI_CTL_92_DATA,
+};
+
+u32 ddr_350_374_async[] = {
+	DENALI_CTL_350_DATA,
+	DENALI_CTL_351_DATA,
+	DENALI_CTL_352_DATA,
+	DENALI_CTL_353_DATA,
+	DENALI_CTL_354_DATA,
+	DENALI_CTL_355_DATA,
+	DENALI_CTL_356_DATA,
+	DENALI_CTL_357_DATA,
+	DENALI_CTL_358_DATA,
+	DENALI_CTL_359_DATA,
+
+	DENALI_CTL_360_DATA,
+	DENALI_CTL_361_DATA,
+	DENALI_CTL_362_DATA,
+	DENALI_CTL_363_DATA,
+	DENALI_CTL_364_DATA,
+	DENALI_CTL_365_DATA,
+	DENALI_CTL_366_DATA,
+	DENALI_CTL_367_DATA,
+	DENALI_CTL_368_DATA,
+	DENALI_CTL_369_DATA,
+
+	DENALI_CTL_370_DATA,
+	DENALI_CTL_371_DATA,
+	DENALI_CTL_372_DATA,
+	DENALI_CTL_373_DATA,
+	DENALI_CTL_374_DATA,
+};
diff --git a/board/schneider/lces/jedec_ddr3_2g_x16_1333h_500_cl8.h b/board/schneider/lces/jedec_ddr3_2g_x16_1333h_500_cl8.h
new file mode 100644
index 0000000000..5c55518bc1
--- /dev/null
+++ b/board/schneider/lces/jedec_ddr3_2g_x16_1333h_500_cl8.h
@@ -0,0 +1,399 @@
+
+/* ****************************************************************
+ *        CADENCE                    Copyright (c) 2001-2011      *
+ *                                   Cadence Design Systems, Inc. *
+ *                                   All rights reserved.         *
+ *                                                                *
+ ******************************************************************
+ *  The values calculated from this script are meant to be        *
+ *  representative programmings.   The values may not reflect the *
+ *  actual required programming for production use.   Please      *
+ *  closely review all programmed values for technical accuracy   *
+ *  before use in production parts.                               *
+ ******************************************************************
+ *
+ *   Module:         regconfig.h
+ *   Documentation:  Register programming header file
+ *
+ ******************************************************************
+ ******************************************************************
+ * WARNING:  This file was automatically generated.  Manual
+ * editing may result in undetermined behavior.
+ ******************************************************************
+ ******************************************************************/
+
+#define               DENALI_CTL_00_DATA 0x00000600
+#define               DENALI_CTL_01_DATA 0x00000000
+#define               DENALI_CTL_02_DATA 0x00000000
+#define               DENALI_CTL_03_DATA 0x00000000
+#define               DENALI_CTL_04_DATA 0x00000000
+#define               DENALI_CTL_05_DATA 0x00000000
+#define               DENALI_CTL_06_DATA 0x00000000
+#define               DENALI_CTL_07_DATA 0x00000005
+#define               DENALI_CTL_08_DATA 0x000186a0
+#define               DENALI_CTL_09_DATA 0x0003d090
+#define               DENALI_CTL_10_DATA 0x00000000
+#define               DENALI_CTL_11_DATA 0x10000200
+#define               DENALI_CTL_12_DATA 0x04040006
+#define               DENALI_CTL_13_DATA 0x04121904
+#define               DENALI_CTL_14_DATA 0x04041707
+#define               DENALI_CTL_15_DATA 0x00891c0c
+#define               DENALI_CTL_16_DATA 0x07000503
+#define               DENALI_CTL_17_DATA 0x01010008
+#define               DENALI_CTL_18_DATA 0x0007030f
+#define               DENALI_CTL_19_DATA 0x01000000
+#define               DENALI_CTL_20_DATA 0x0f340050
+#define               DENALI_CTL_21_DATA 0x00000005
+#define               DENALI_CTL_22_DATA 0x000c0003
+#define               DENALI_CTL_23_DATA 0x00000000
+#define               DENALI_CTL_24_DATA 0x00550200
+#define               DENALI_CTL_25_DATA 0x00010000
+#define               DENALI_CTL_26_DATA 0x00050500
+#define               DENALI_CTL_27_DATA 0x00000000
+#define               DENALI_CTL_28_DATA 0x00000000
+#define               DENALI_CTL_29_DATA 0x00000000
+#define               DENALI_CTL_30_DATA 0x00000000
+#define               DENALI_CTL_31_DATA 0x00084000
+#define               DENALI_CTL_32_DATA 0x00080046
+#define               DENALI_CTL_33_DATA 0x00000000
+#define               DENALI_CTL_34_DATA 0x00460840
+#define               DENALI_CTL_35_DATA 0x00000008
+#define               DENALI_CTL_36_DATA 0x00010000
+#define               DENALI_CTL_37_DATA 0x00000000
+#define               DENALI_CTL_38_DATA 0x00000000
+#define               DENALI_CTL_39_DATA 0x00000000
+#define               DENALI_CTL_40_DATA 0x00000000
+#define               DENALI_CTL_41_DATA 0x00000000
+#define               DENALI_CTL_42_DATA 0x00000000
+#define               DENALI_CTL_43_DATA 0x00000000
+#define               DENALI_CTL_44_DATA 0x00000000
+#define               DENALI_CTL_45_DATA 0x01000200
+#define               DENALI_CTL_46_DATA 0x02000040
+#define               DENALI_CTL_47_DATA 0x00000040
+#define               DENALI_CTL_48_DATA 0x02000100
+#define               DENALI_CTL_49_DATA 0xffff0a01
+#define               DENALI_CTL_50_DATA 0x01010101
+#define               DENALI_CTL_51_DATA 0x01010101
+#define               DENALI_CTL_52_DATA 0x01030101
+#define               DENALI_CTL_53_DATA 0x0c030000
+#define               DENALI_CTL_54_DATA 0x00000000
+#define               DENALI_CTL_55_DATA 0x00000100
+#define               DENALI_CTL_56_DATA 0x00000000
+#define               DENALI_CTL_57_DATA 0x00000000
+#define               DENALI_CTL_58_DATA 0x00000000
+#define               DENALI_CTL_59_DATA 0x00000000
+#define               DENALI_CTL_60_DATA 0x00000000
+#define               DENALI_CTL_61_DATA 0x00000000
+#define               DENALI_CTL_62_DATA 0x01020000
+#define               DENALI_CTL_63_DATA 0x06050201
+#define               DENALI_CTL_64_DATA 0x02000106
+#define               DENALI_CTL_65_DATA 0x00000000
+#define               DENALI_CTL_66_DATA 0x02020202
+#define               DENALI_CTL_67_DATA 0x00000200
+#define               DENALI_CTL_68_DATA 0x00000000
+#define               DENALI_CTL_69_DATA 0x00000000
+#define               DENALI_CTL_70_DATA 0x00000000
+#define               DENALI_CTL_71_DATA 0x00280d00
+#define               DENALI_CTL_72_DATA 0x00000000
+#define               DENALI_CTL_73_DATA 0x00000100
+#define               DENALI_CTL_74_DATA 0x00010001
+#define               DENALI_CTL_75_DATA 0x00000000
+#define               DENALI_CTL_76_DATA 0x00000000
+#define               DENALI_CTL_77_DATA 0x00000000
+#define               DENALI_CTL_78_DATA 0x00000000
+#define               DENALI_CTL_79_DATA 0x00222200
+#define               DENALI_CTL_80_DATA 0x00000001
+#define               DENALI_CTL_81_DATA 0x00000000
+#define               DENALI_CTL_82_DATA 0x00000000
+#define               DENALI_CTL_83_DATA 0x00012222
+#define               DENALI_CTL_84_DATA 0x00000000
+#define               DENALI_CTL_85_DATA 0x00000000
+#define               DENALI_CTL_86_DATA 0x00222200
+#define               DENALI_CTL_87_DATA 0x02020001
+#define               DENALI_CTL_88_DATA 0x00020200
+#define               DENALI_CTL_89_DATA 0x02000202
+#define               DENALI_CTL_90_DATA 0x01000002
+#define               DENALI_CTL_91_DATA 0x00000000
+#define               DENALI_CTL_92_DATA 0x0003ffff
+#define               DENALI_CTL_93_DATA 0x00000000
+#define               DENALI_CTL_94_DATA 0x0003ffff
+#define               DENALI_CTL_95_DATA 0x00000000
+#define               DENALI_CTL_96_DATA 0x0003ffff
+#define               DENALI_CTL_97_DATA 0x00000000
+#define               DENALI_CTL_98_DATA 0x0003ffff
+#define               DENALI_CTL_99_DATA 0x00000000
+#define              DENALI_CTL_100_DATA 0x0003ffff
+#define              DENALI_CTL_101_DATA 0x00000000
+#define              DENALI_CTL_102_DATA 0x0003ffff
+#define              DENALI_CTL_103_DATA 0x00000000
+#define              DENALI_CTL_104_DATA 0x0003ffff
+#define              DENALI_CTL_105_DATA 0x00000000
+#define              DENALI_CTL_106_DATA 0x0003ffff
+#define              DENALI_CTL_107_DATA 0x00000000
+#define              DENALI_CTL_108_DATA 0x0003ffff
+#define              DENALI_CTL_109_DATA 0x00000000
+#define              DENALI_CTL_110_DATA 0x0003ffff
+#define              DENALI_CTL_111_DATA 0x00000000
+#define              DENALI_CTL_112_DATA 0x0003ffff
+#define              DENALI_CTL_113_DATA 0x00000000
+#define              DENALI_CTL_114_DATA 0x0003ffff
+#define              DENALI_CTL_115_DATA 0x00000000
+#define              DENALI_CTL_116_DATA 0x0003ffff
+#define              DENALI_CTL_117_DATA 0x00000000
+#define              DENALI_CTL_118_DATA 0x0003ffff
+#define              DENALI_CTL_119_DATA 0x00000000
+#define              DENALI_CTL_120_DATA 0x0003ffff
+#define              DENALI_CTL_121_DATA 0x00000000
+#define              DENALI_CTL_122_DATA 0x0003ffff
+#define              DENALI_CTL_123_DATA 0x00000000
+#define              DENALI_CTL_124_DATA 0x0003ffff
+#define              DENALI_CTL_125_DATA 0x00000000
+#define              DENALI_CTL_126_DATA 0x0003ffff
+#define              DENALI_CTL_127_DATA 0x00000000
+#define              DENALI_CTL_128_DATA 0x0003ffff
+#define              DENALI_CTL_129_DATA 0x00000000
+#define              DENALI_CTL_130_DATA 0x0003ffff
+#define              DENALI_CTL_131_DATA 0x00000000
+#define              DENALI_CTL_132_DATA 0x0003ffff
+#define              DENALI_CTL_133_DATA 0x00000000
+#define              DENALI_CTL_134_DATA 0x0003ffff
+#define              DENALI_CTL_135_DATA 0x00000000
+#define              DENALI_CTL_136_DATA 0x0003ffff
+#define              DENALI_CTL_137_DATA 0x00000000
+#define              DENALI_CTL_138_DATA 0x0003ffff
+#define              DENALI_CTL_139_DATA 0x00000000
+#define              DENALI_CTL_140_DATA 0x0003ffff
+#define              DENALI_CTL_141_DATA 0x00000000
+#define              DENALI_CTL_142_DATA 0x0003ffff
+#define              DENALI_CTL_143_DATA 0x00000000
+#define              DENALI_CTL_144_DATA 0x0003ffff
+#define              DENALI_CTL_145_DATA 0x00000000
+#define              DENALI_CTL_146_DATA 0x0003ffff
+#define              DENALI_CTL_147_DATA 0x00000000
+#define              DENALI_CTL_148_DATA 0x0003ffff
+#define              DENALI_CTL_149_DATA 0x00000000
+#define              DENALI_CTL_150_DATA 0x0003ffff
+#define              DENALI_CTL_151_DATA 0x00000000
+#define              DENALI_CTL_152_DATA 0x0003ffff
+#define              DENALI_CTL_153_DATA 0x00000000
+#define              DENALI_CTL_154_DATA 0x0003ffff
+#define              DENALI_CTL_155_DATA 0x00000000
+#define              DENALI_CTL_156_DATA 0x0003ffff
+#define              DENALI_CTL_157_DATA 0x00000000
+#define              DENALI_CTL_158_DATA 0x0003ffff
+#define              DENALI_CTL_159_DATA 0x00000000
+#define              DENALI_CTL_160_DATA 0x0003ffff
+#define              DENALI_CTL_161_DATA 0x00000000
+#define              DENALI_CTL_162_DATA 0x0003ffff
+#define              DENALI_CTL_163_DATA 0x00000000
+#define              DENALI_CTL_164_DATA 0x0003ffff
+#define              DENALI_CTL_165_DATA 0x00000000
+#define              DENALI_CTL_166_DATA 0x0003ffff
+#define              DENALI_CTL_167_DATA 0x00000000
+#define              DENALI_CTL_168_DATA 0x0003ffff
+#define              DENALI_CTL_169_DATA 0x00000000
+#define              DENALI_CTL_170_DATA 0x0003ffff
+#define              DENALI_CTL_171_DATA 0x00000000
+#define              DENALI_CTL_172_DATA 0x0003ffff
+#define              DENALI_CTL_173_DATA 0x00000000
+#define              DENALI_CTL_174_DATA 0x0003ffff
+#define              DENALI_CTL_175_DATA 0x00000000
+#define              DENALI_CTL_176_DATA 0x0003ffff
+#define              DENALI_CTL_177_DATA 0x00000000
+#define              DENALI_CTL_178_DATA 0x0003ffff
+#define              DENALI_CTL_179_DATA 0x00000000
+#define              DENALI_CTL_180_DATA 0x0003ffff
+#define              DENALI_CTL_181_DATA 0x00000000
+#define              DENALI_CTL_182_DATA 0x0003ffff
+#define              DENALI_CTL_183_DATA 0x00000000
+#define              DENALI_CTL_184_DATA 0x0003ffff
+#define              DENALI_CTL_185_DATA 0x00000000
+#define              DENALI_CTL_186_DATA 0x0003ffff
+#define              DENALI_CTL_187_DATA 0x00000000
+#define              DENALI_CTL_188_DATA 0x0003ffff
+#define              DENALI_CTL_189_DATA 0x00000000
+#define              DENALI_CTL_190_DATA 0x0003ffff
+#define              DENALI_CTL_191_DATA 0x00000000
+#define              DENALI_CTL_192_DATA 0x0003ffff
+#define              DENALI_CTL_193_DATA 0x00000000
+#define              DENALI_CTL_194_DATA 0x0003ffff
+#define              DENALI_CTL_195_DATA 0x00000000
+#define              DENALI_CTL_196_DATA 0x0003ffff
+#define              DENALI_CTL_197_DATA 0x00000000
+#define              DENALI_CTL_198_DATA 0x0003ffff
+#define              DENALI_CTL_199_DATA 0x00000000
+#define              DENALI_CTL_200_DATA 0x0003ffff
+#define              DENALI_CTL_201_DATA 0x00000000
+#define              DENALI_CTL_202_DATA 0x0003ffff
+#define              DENALI_CTL_203_DATA 0x00000000
+#define              DENALI_CTL_204_DATA 0x0003ffff
+#define              DENALI_CTL_205_DATA 0x00000000
+#define              DENALI_CTL_206_DATA 0x0003ffff
+#define              DENALI_CTL_207_DATA 0x00000000
+#define              DENALI_CTL_208_DATA 0x0003ffff
+#define              DENALI_CTL_209_DATA 0x00000000
+#define              DENALI_CTL_210_DATA 0x0003ffff
+#define              DENALI_CTL_211_DATA 0x00000000
+#define              DENALI_CTL_212_DATA 0x0003ffff
+#define              DENALI_CTL_213_DATA 0x00000000
+#define              DENALI_CTL_214_DATA 0x0003ffff
+#define              DENALI_CTL_215_DATA 0x00000000
+#define              DENALI_CTL_216_DATA 0x0003ffff
+#define              DENALI_CTL_217_DATA 0x00000000
+#define              DENALI_CTL_218_DATA 0x0303ffff
+#define              DENALI_CTL_219_DATA 0xffffffff
+#define              DENALI_CTL_220_DATA 0x00030f0f
+#define              DENALI_CTL_221_DATA 0xffffffff
+#define              DENALI_CTL_222_DATA 0x00030f0f
+#define              DENALI_CTL_223_DATA 0xffffffff
+#define              DENALI_CTL_224_DATA 0x00030f0f
+#define              DENALI_CTL_225_DATA 0xffffffff
+#define              DENALI_CTL_226_DATA 0x00030f0f
+#define              DENALI_CTL_227_DATA 0xffffffff
+#define              DENALI_CTL_228_DATA 0x00030f0f
+#define              DENALI_CTL_229_DATA 0xffffffff
+#define              DENALI_CTL_230_DATA 0x00030f0f
+#define              DENALI_CTL_231_DATA 0xffffffff
+#define              DENALI_CTL_232_DATA 0x00030f0f
+#define              DENALI_CTL_233_DATA 0xffffffff
+#define              DENALI_CTL_234_DATA 0x00030f0f
+#define              DENALI_CTL_235_DATA 0xffffffff
+#define              DENALI_CTL_236_DATA 0x00030f0f
+#define              DENALI_CTL_237_DATA 0xffffffff
+#define              DENALI_CTL_238_DATA 0x00030f0f
+#define              DENALI_CTL_239_DATA 0xffffffff
+#define              DENALI_CTL_240_DATA 0x00030f0f
+#define              DENALI_CTL_241_DATA 0xffffffff
+#define              DENALI_CTL_242_DATA 0x00030f0f
+#define              DENALI_CTL_243_DATA 0xffffffff
+#define              DENALI_CTL_244_DATA 0x00030f0f
+#define              DENALI_CTL_245_DATA 0xffffffff
+#define              DENALI_CTL_246_DATA 0x00030f0f
+#define              DENALI_CTL_247_DATA 0xffffffff
+#define              DENALI_CTL_248_DATA 0x00030f0f
+#define              DENALI_CTL_249_DATA 0xffffffff
+#define              DENALI_CTL_250_DATA 0x00030f0f
+#define              DENALI_CTL_251_DATA 0xffffffff
+#define              DENALI_CTL_252_DATA 0x00030f0f
+#define              DENALI_CTL_253_DATA 0xffffffff
+#define              DENALI_CTL_254_DATA 0x00030f0f
+#define              DENALI_CTL_255_DATA 0xffffffff
+#define              DENALI_CTL_256_DATA 0x00030f0f
+#define              DENALI_CTL_257_DATA 0xffffffff
+#define              DENALI_CTL_258_DATA 0x00030f0f
+#define              DENALI_CTL_259_DATA 0xffffffff
+#define              DENALI_CTL_260_DATA 0x00030f0f
+#define              DENALI_CTL_261_DATA 0xffffffff
+#define              DENALI_CTL_262_DATA 0x00030f0f
+#define              DENALI_CTL_263_DATA 0xffffffff
+#define              DENALI_CTL_264_DATA 0x00030f0f
+#define              DENALI_CTL_265_DATA 0xffffffff
+#define              DENALI_CTL_266_DATA 0x00030f0f
+#define              DENALI_CTL_267_DATA 0xffffffff
+#define              DENALI_CTL_268_DATA 0x00030f0f
+#define              DENALI_CTL_269_DATA 0xffffffff
+#define              DENALI_CTL_270_DATA 0x00030f0f
+#define              DENALI_CTL_271_DATA 0xffffffff
+#define              DENALI_CTL_272_DATA 0x00030f0f
+#define              DENALI_CTL_273_DATA 0xffffffff
+#define              DENALI_CTL_274_DATA 0x00030f0f
+#define              DENALI_CTL_275_DATA 0xffffffff
+#define              DENALI_CTL_276_DATA 0x00030f0f
+#define              DENALI_CTL_277_DATA 0xffffffff
+#define              DENALI_CTL_278_DATA 0x00030f0f
+#define              DENALI_CTL_279_DATA 0xffffffff
+#define              DENALI_CTL_280_DATA 0x00030f0f
+#define              DENALI_CTL_281_DATA 0xffffffff
+#define              DENALI_CTL_282_DATA 0x00030f0f
+#define              DENALI_CTL_283_DATA 0xffffffff
+#define              DENALI_CTL_284_DATA 0x00030f0f
+#define              DENALI_CTL_285_DATA 0xffffffff
+#define              DENALI_CTL_286_DATA 0x00030f0f
+#define              DENALI_CTL_287_DATA 0xffffffff
+#define              DENALI_CTL_288_DATA 0x00030f0f
+#define              DENALI_CTL_289_DATA 0xffffffff
+#define              DENALI_CTL_290_DATA 0x00030f0f
+#define              DENALI_CTL_291_DATA 0xffffffff
+#define              DENALI_CTL_292_DATA 0x00030f0f
+#define              DENALI_CTL_293_DATA 0xffffffff
+#define              DENALI_CTL_294_DATA 0x00030f0f
+#define              DENALI_CTL_295_DATA 0xffffffff
+#define              DENALI_CTL_296_DATA 0x00030f0f
+#define              DENALI_CTL_297_DATA 0xffffffff
+#define              DENALI_CTL_298_DATA 0x00030f0f
+#define              DENALI_CTL_299_DATA 0xffffffff
+#define              DENALI_CTL_300_DATA 0x00030f0f
+#define              DENALI_CTL_301_DATA 0xffffffff
+#define              DENALI_CTL_302_DATA 0x00030f0f
+#define              DENALI_CTL_303_DATA 0xffffffff
+#define              DENALI_CTL_304_DATA 0x00030f0f
+#define              DENALI_CTL_305_DATA 0xffffffff
+#define              DENALI_CTL_306_DATA 0x00030f0f
+#define              DENALI_CTL_307_DATA 0xffffffff
+#define              DENALI_CTL_308_DATA 0x00030f0f
+#define              DENALI_CTL_309_DATA 0xffffffff
+#define              DENALI_CTL_310_DATA 0x00030f0f
+#define              DENALI_CTL_311_DATA 0xffffffff
+#define              DENALI_CTL_312_DATA 0x00030f0f
+#define              DENALI_CTL_313_DATA 0xffffffff
+#define              DENALI_CTL_314_DATA 0x00030f0f
+#define              DENALI_CTL_315_DATA 0xffffffff
+#define              DENALI_CTL_316_DATA 0x00030f0f
+#define              DENALI_CTL_317_DATA 0xffffffff
+#define              DENALI_CTL_318_DATA 0x00030f0f
+#define              DENALI_CTL_319_DATA 0xffffffff
+#define              DENALI_CTL_320_DATA 0x00030f0f
+#define              DENALI_CTL_321_DATA 0xffffffff
+#define              DENALI_CTL_322_DATA 0x00030f0f
+#define              DENALI_CTL_323_DATA 0xffffffff
+#define              DENALI_CTL_324_DATA 0x00030f0f
+#define              DENALI_CTL_325_DATA 0xffffffff
+#define              DENALI_CTL_326_DATA 0x00030f0f
+#define              DENALI_CTL_327_DATA 0xffffffff
+#define              DENALI_CTL_328_DATA 0x00030f0f
+#define              DENALI_CTL_329_DATA 0xffffffff
+#define              DENALI_CTL_330_DATA 0x00030f0f
+#define              DENALI_CTL_331_DATA 0xffffffff
+#define              DENALI_CTL_332_DATA 0x00030f0f
+#define              DENALI_CTL_333_DATA 0xffffffff
+#define              DENALI_CTL_334_DATA 0x00030f0f
+#define              DENALI_CTL_335_DATA 0xffffffff
+#define              DENALI_CTL_336_DATA 0x00030f0f
+#define              DENALI_CTL_337_DATA 0xffffffff
+#define              DENALI_CTL_338_DATA 0x00030f0f
+#define              DENALI_CTL_339_DATA 0xffffffff
+#define              DENALI_CTL_340_DATA 0x00030f0f
+#define              DENALI_CTL_341_DATA 0xffffffff
+#define              DENALI_CTL_342_DATA 0x00030f0f
+#define              DENALI_CTL_343_DATA 0xffffffff
+#define              DENALI_CTL_344_DATA 0x00030f0f
+#define              DENALI_CTL_345_DATA 0xffffffff
+#define              DENALI_CTL_346_DATA 0x32030f0f
+#define              DENALI_CTL_347_DATA 0x01320001
+#define              DENALI_CTL_348_DATA 0x00013200
+#define              DENALI_CTL_349_DATA 0x00000132
+#define              DENALI_CTL_350_DATA 0x00000000
+#define              DENALI_CTL_351_DATA 0x000d0000
+#define              DENALI_CTL_352_DATA 0x1e680000
+#define              DENALI_CTL_353_DATA 0x02000200
+#define              DENALI_CTL_354_DATA 0x02000200
+#define              DENALI_CTL_355_DATA 0x00001e68
+#define              DENALI_CTL_356_DATA 0x00009808
+#define              DENALI_CTL_357_DATA 0x00020608
+#define              DENALI_CTL_358_DATA 0x000a0a01
+#define              DENALI_CTL_359_DATA 0x00000000
+#define              DENALI_CTL_360_DATA 0x00000000
+#define              DENALI_CTL_361_DATA 0x04038000
+#define              DENALI_CTL_362_DATA 0x07030a07
+#define              DENALI_CTL_363_DATA 0x00ffff22
+#define              DENALI_CTL_364_DATA 0x000f0010
+#define              DENALI_CTL_365_DATA 0x00000000
+#define              DENALI_CTL_366_DATA 0x00000000
+#define              DENALI_CTL_367_DATA 0x00000000
+#define              DENALI_CTL_368_DATA 0x00000000
+#define              DENALI_CTL_369_DATA 0x00000000
+#define              DENALI_CTL_370_DATA 0x00000204
+#define              DENALI_CTL_371_DATA 0x00000000
+#define              DENALI_CTL_372_DATA 0x01000001
+#define              DENALI_CTL_373_DATA 0x00000001
+#define              DENALI_CTL_374_DATA 0x00000000
diff --git a/board/schneider/lces/lces.c b/board/schneider/lces/lces.c
new file mode 100644
index 0000000000..e520b1e9e4
--- /dev/null
+++ b/board/schneider/lces/lces.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <common.h>
+#include <dm.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int board_init(void)
+{
+	gd->bd->bi_boot_params = (CONFIG_SYS_SDRAM_BASE + 0x100);
+
+	return 0;
+}
+
+int dram_init(void)
+{
+	struct udevice *dev;
+	int err;
+
+	/* This will end up calling cadence_ddr_probe() */
+	err = uclass_get_device(UCLASS_RAM, 0, &dev);
+	if (err) {
+		debug("DRAM init failed: %d\n", err);
+		return err;
+	}
+
+	// FIXME: checkpatch says to use dev_read_...
+	if (fdtdec_setup_mem_size_base() != 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+int dram_init_banksize(void)
+{
+	// FIXME: checkpatch says to use dev_read_...
+	fdtdec_setup_memory_banksize();
+
+	return 0;
+}
diff --git a/configs/lces_defconfig b/configs/lces_defconfig
new file mode 100644
index 0000000000..c10f1a2133
--- /dev/null
+++ b/configs/lces_defconfig
@@ -0,0 +1,26 @@
+CONFIG_ARM=y
+CONFIG_SYS_ARCH_TIMER=y
+CONFIG_SYS_THUMB_BUILD=y
+CONFIG_ARCH_RZN1=y
+CONFIG_SYS_TEXT_BASE=0x20040000
+CONFIG_SYS_MALLOC_LEN=0xb0000
+CONFIG_SYS_MALLOC_F_LEN=0x800
+CONFIG_NR_DRAM_BANKS=1
+CONFIG_DEFAULT_DEVICE_TREE="r9a06g032-rzn1d400-lces"
+CONFIG_DEBUG_UART_BASE=0x40060000
+CONFIG_DEBUG_UART_CLOCK=20000000
+CONFIG_SYS_LOAD_ADDR=0x80008000
+CONFIG_DEBUG_UART=y
+CONFIG_SYS_MEMTEST_START=0x80000000
+CONFIG_SYS_MEMTEST_END=0x8fffffff
+CONFIG_CMD_MEMTEST=y
+CONFIG_SYS_ALT_MEMTEST=y
+# CONFIG_SYS_ALT_MEMTEST_BITFLIP is not set
+CONFIG_CMD_CLK=y
+CONFIG_OF_CONTROL=y
+CONFIG_RAM=y
+CONFIG_CADENCE_DDR_CTRL=y
+CONFIG_DEBUG_UART_SHIFT=2
+CONFIG_DEBUG_UART_ANNOUNCE=y
+CONFIG_SYS_NS16550=y
+# CONFIG_EFI_LOADER is not set
diff --git a/include/configs/lces.h b/include/configs/lces.h
new file mode 100644
index 0000000000..37e9854758
--- /dev/null
+++ b/include/configs/lces.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Configuration settings for the Schneider LCES board
+ */
+
+#ifndef __LCES_H
+#define __LCES_H
+
+/* Internal RAM */
+#define CONFIG_SYS_INIT_RAM_ADDR	0x20000000
+#define CONFIG_SYS_INIT_RAM_SIZE	(1 * 1024 * 1024)
+
+/* DDR */
+#define CONFIG_SYS_SDRAM_BASE		0x80000000
+#define CONFIG_SYS_SDRAM_SIZE		(256 * 1024 * 1024)
+
+#define CONFIG_SYS_MONITOR_LEN		(512 * 1024)
+
+/* Serial port is memory-mapped as 32-bit registers */
+#define CONFIG_SYS_NS16550_MEM32
+
+#endif	/* __LCES2_H */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v1 9/9] tools: Add tool to create Renesas SPKG images
  2022-08-09 12:59 [RFC PATCH v1 0/9] Renesas RZ/N1 SoC initial support Ralph Siemsen
                   ` (7 preceding siblings ...)
  2022-08-09 12:59 ` [RFC PATCH v1 8/9] board: schneider: add LCES board support Ralph Siemsen
@ 2022-08-09 12:59 ` Ralph Siemsen
  2022-08-09 13:03   ` Pali Rohár
  2022-08-12 17:03   ` [RFC PATCH v2 9/9] tools: spkgimage: add Renesas SPKG format Ralph Siemsen
  8 siblings, 2 replies; 29+ messages in thread
From: Ralph Siemsen @ 2022-08-09 12:59 UTC (permalink / raw)
  To: u-boot
  Cc: Michel Pollet, Ralph Siemsen, AKASHI Takahiro, Andre Przywara,
	Heiko Thiery, Pali Rohár, Samuel Holland, Simon Glass,
	Stefan Roese

From: Michel Pollet <michel.pollet@bp.renesas.com>

Renesas RZ/N1 devices contain BootROM code that loads a custom SPKG
image from QSPI, NAND or USB DFU. This tool converts a binary image into
an SPKG.

SPKGs can optionally be signed, however this tool does not currently
support signed SPKGs.

Signed-off-by: Michel Pollet <michel.pollet@bp.renesas.com>
Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
---
This tool could possibly be incorporated into mkimage / imagetools.
However it is unclear how to handle the extra commandline parameters
(NAND ECC settings, etc). So for now it is stand-alone tool.

 tools/Makefile       |   2 +
 tools/spkg_header.h  |  49 +++++++
 tools/spkg_utility.c | 306 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 357 insertions(+)
 create mode 100644 tools/spkg_header.h
 create mode 100644 tools/spkg_utility.c

diff --git a/tools/Makefile b/tools/Makefile
index 005e7362a3..c5dc92a13f 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -68,6 +68,8 @@ HOSTCFLAGS_img2srec.o := -pedantic
 hostprogs-$(CONFIG_XWAY_SWAP_BYTES) += xway-swap-bytes
 HOSTCFLAGS_xway-swap-bytes.o := -pedantic
 
+hostprogs-$(CONFIG_ARCH_RZN1) += spkg_utility
+
 hostprogs-y += mkenvimage
 mkenvimage-objs := mkenvimage.o os_support.o lib/crc32.o
 
diff --git a/tools/spkg_header.h b/tools/spkg_header.h
new file mode 100644
index 0000000000..029930cbf8
--- /dev/null
+++ b/tools/spkg_header.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Renesas RZ/N1 Linux tools: Package Table format
+ * (C) 2015-2016 Renesas Electronics Europe, LTD
+ * All rights reserved.
+ */
+
+#ifndef _SKGT_HEADER_H_
+#define _SKGT_HEADER_H_
+
+#define SPKG_HEADER_SIGNATURE (('R'<<0)|('Z'<<8)|('N'<<16)|('1'<<24))
+#define SPKG_HEADER_COUNT	8
+#define SPKG_BLP_SIZE		264
+
+#define SPKG_HEADER_SIZE	24
+#define SPKG_HEADER_SIZE_ALL	(SPKG_HEADER_SIZE * SPKG_HEADER_COUNT)
+#define SPKG_HEADER_CRC_SIZE	4
+
+/* Index into SPKG */
+#define INDEX_BLP_START		SPKG_HEADER_SIZE_ALL
+#define INDEX_IMAGE_START	(INDEX_BLP_START + SPKG_BLP_SIZE)
+
+/* Flags, not supported by ROM code, only used for U-Boot SPL */
+enum {
+	SPKG_CODE_NONSEC_BIT = 0,
+	SPKG_CODE_HYP_BIT,
+};
+
+/* SPKG header */
+struct spkg_hdr {
+	uint32_t	signature;
+	uint8_t		version;
+	uint8_t		ecc;
+	uint8_t		ecc_scheme;
+	uint8_t		ecc_bytes;
+	uint32_t	payload_length; /* only HIGHER 24 bits */
+	uint32_t	load_address;
+	uint32_t	execution_offset;
+	uint32_t	crc; /* of this header */
+} __attribute__((packed));
+
+struct spkg_file {
+	struct spkg_hdr		header[SPKG_HEADER_COUNT];
+	uint8_t			blp[SPKG_BLP_SIZE];
+	uint8_t			data[0];
+	/* then the CRC */
+} __attribute__((packed));
+
+#endif
diff --git a/tools/spkg_utility.c b/tools/spkg_utility.c
new file mode 100644
index 0000000000..235e23e0f1
--- /dev/null
+++ b/tools/spkg_utility.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * This is a utility to create a SPKG file.
+ * It packages the binary code into the SPKG.
+ *
+ * (C) Copyright 2016 Renesas Electronics Europe Ltd
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "spkg_header.h"
+
+/* For Windows compatibility */
+#ifndef htole32
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ #define htole32(x)	(x)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ #define htole32(x)	__builtin_bswap32((uint32_t)(x))
+#endif
+#endif
+
+#define MAX_PATH		300
+
+// Note: Order of bit fields is not used, this is purely just holding the SPKG information.
+struct spkg_header {
+	char input[MAX_PATH];
+	char output[MAX_PATH + 5];
+	unsigned int version:4;
+	unsigned int ecc_enable:1;
+	unsigned int ecc_block_size:2;
+	unsigned int ecc_scheme:3;
+	unsigned int ecc_bytes:8;
+	unsigned int dummy_blp_length:10;
+	unsigned int payload_length:24;
+	unsigned int spl_nonsec:1;
+	unsigned int spl_hyp:1;
+	unsigned int load_address;
+	unsigned int execution_offset;
+	uint32_t padding;
+};
+
+struct spkg_header g_header = {
+	.version = 1,
+	.padding = 256,
+};
+
+int verbose;
+
+static uint32_t crc32(const uint8_t *message, uint32_t l)
+{
+	uint32_t crc = ~0;
+
+	while (l--) {
+		uint32_t byte = *message++;		// Get next byte.
+
+		crc = crc ^ byte;
+		for (int8_t j = 7; j >= 0; j--) {	// Do eight times.
+			uint32_t mask = -(crc & 1);
+
+			crc = (crc >> 1) ^ (0xEDB88320 & mask);
+		}
+	}
+	return ~crc;
+}
+
+static int spkg_write(struct spkg_header *h, FILE *file_input, FILE *file_SPKG)
+{
+	int i;
+	uint32_t length_inputfile;
+	uint32_t length_read;
+	uint32_t length_written;
+	uint32_t length_total;
+	uint32_t padding = 0;
+	uint8_t *data, *start;
+	uint32_t crc;
+
+	/* Calculate length of input file */
+	fseek(file_input, 0, SEEK_END);	// seek to end of file
+	length_inputfile = ftell(file_input);	// get current file pointer
+	fseek(file_input, 0, SEEK_SET);	// seek back to beginning of file
+
+	/* Set payload_length field. */
+	h->payload_length =
+	    length_inputfile + h->dummy_blp_length + SPKG_HEADER_CRC_SIZE;
+
+	/* Calculate total length of SPKG */
+	length_total =
+	    (SPKG_HEADER_SIZE * SPKG_HEADER_COUNT) + h->dummy_blp_length +
+	    length_inputfile + SPKG_HEADER_CRC_SIZE;
+	padding = h->padding ? h->padding - (length_total % h->padding) : 0;
+	length_total += padding;
+	/* Padding needs to be part of the payload size, otherwise the ROM DFU
+	 * refuses to accept the extra bytes and return and error.
+	 */
+	h->payload_length += padding;
+
+	printf("Addr: 0x%08x ", h->load_address);
+	printf("In: %8d ", length_inputfile);
+	printf("padding to %3dKB: %6d ", h->padding / 1024, padding);
+	printf("Total: 0x%08x ", length_total);
+	printf("%s\n", h->output);
+
+	/* Create and zero array for SPKG */
+	data = malloc(length_total);
+	memset(data, 0, length_total);
+
+	/* Fill the SPKG with the headers */
+	{
+		struct spkg_hdr head = {
+			.signature = SPKG_HEADER_SIGNATURE,
+			.version = h->version,
+			.ecc = (h->ecc_enable << 5) | (h->ecc_block_size << 1),
+			.ecc_scheme = h->ecc_scheme,
+			.ecc_bytes = h->ecc_bytes,
+			.payload_length = htole32((h->payload_length << 8) |
+				(h->spl_nonsec << SPKG_CODE_NONSEC_BIT) |
+				(h->spl_hyp << SPKG_CODE_HYP_BIT)),
+			.load_address = htole32(h->load_address),
+			.execution_offset = htole32(h->execution_offset),
+		};
+
+		head.crc = crc32((uint8_t *)&head, sizeof(head) - SPKG_HEADER_CRC_SIZE);
+		for (i = 0; i < SPKG_HEADER_COUNT; i++)
+			((struct spkg_hdr *)data)[i] = head;
+	}
+
+	start = data + INDEX_BLP_START;
+
+	/* Fill the SPKG with the Dummy BLp */
+	for (i = 0; i < h->dummy_blp_length; i++)
+		*start++ = 0x88;
+	/* Fill the SPKG with the data from the code file. */
+	length_read =
+	    fread(start, sizeof(char), length_inputfile,
+		  file_input);
+
+	if (length_read != length_inputfile) {
+		fprintf(stderr, "Error reading %s: ferror=%d, feof=%d\n",
+			h->input, ferror(file_input), feof(file_input));
+
+		return -1;
+	}
+	/* fill padding with flash friendly one bits */
+	memset(start + length_inputfile + SPKG_HEADER_CRC_SIZE, 0xff, padding);
+
+	/* Add Payload CRC */
+	crc = crc32(&data[INDEX_BLP_START],
+		    h->dummy_blp_length + length_inputfile + padding);
+
+	start += length_inputfile + padding;
+	start[0] = crc;
+	start[1] = crc >> 8;
+	start[2] = crc >> 16;
+	start[3] = crc >> 24;
+
+	/* Write the completed SKPG to file */
+	length_written = fwrite(data, sizeof(char), length_total, file_SPKG);
+
+	if (length_written != length_total) {
+		fprintf(stderr, "Error writing to %s\n", h->output);
+		return -1;
+	}
+
+	return 0;
+}
+
+const char *usage =
+	"%s\n"
+	"  [-i <filename>]	: input file\n"
+	"  [-o <filename>]	: output file\n"
+	"  [--load_address <hex constant>] : code load address\n"
+	"  [--execution_offset <hex constant>] : starting offset\n"
+	"  [--nand_ecc_enable] : Enable nand ECC\n"
+	"  [--nand_ecc_blksize <hex constant>] : Block size code\n"
+	"        0=256 bytes, 1=512 bytes, 2=1024 bytes\n"
+	"  [--nand_ecc_scheme <hex constant>] : ECC scheme code\n"
+	"        0=BCH2 1=BCH4 2=BCH8 3=BCH16 4=BCH24 5=BCH32\n"
+	"  [--add_dummy_blp] : Add a passthru BLP\n"
+	"  [--spl_nonsec] : Code package run in NONSEC\n"
+	"  [--spl_hyp] : Code package run in HYP (and NONSEC)\n"
+	"  [--padding <value>[K|M]] : Pass SPKG to <value> size block\n"
+	;
+
+static int spkg_parse_option(struct spkg_header *h, const char *name,
+			     const char *sz, uint32_t value)
+{
+//	printf("%s %s=%s %08x\n", __func__, name, sz, value);
+	if (!strcmp("file_input", name) || !strcmp("i", name)) {
+		strncpy(h->input, sz, sizeof(h->input) - 1);
+		h->input[sizeof(h->input) - 1] = 0;
+	} else if (!strcmp("file_output", name) || !strcmp("o", name)) {
+		strncpy(h->output, sz, sizeof(h->output) - 1);
+		h->output[sizeof(h->output) - 1] = 0;
+	} else if (!strcmp("version", name)) {
+		h->version = value;
+	} else if (!strcmp("load_address", name)) {
+		h->load_address = value;
+	} else if (!strcmp("execution_offset", name)) {
+		h->execution_offset = value;
+	} else if (!strcmp("nand_ecc_enable", name)) {
+		h->ecc_enable = value;
+	} else if (!strcmp("nand_ecc_blksize", name)) {
+		h->ecc_block_size = value;
+	} else if (!strcmp("nand_ecc_scheme", name)) {
+		h->ecc_scheme = value;
+	} else if (!strcmp("nand_bytes_per_ecc_block", name)) {
+		h->ecc_bytes = value;
+	} else if (!strcmp("add_dummy_blp", name)) {
+		h->dummy_blp_length = value ? SPKG_BLP_SIZE : 0;
+	} else if (!strcmp("spl_nonsec", name)) {
+		h->spl_nonsec = !!value;
+	} else if (!strcmp("spl_hyp", name)) {
+		h->spl_hyp = !!value;
+	} else if (!strcmp("help", name) || !strcmp("h", name)) {
+		fprintf(stderr, usage, "spkg_utility");
+		exit(0);
+	} else if (!strcmp("padding", name) && sz && value) {
+		if (strchr(sz, 'K'))
+			h->padding = value * 1024;
+		else if (strchr(sz, 'M'))
+			h->padding = value * 1024 * 1024;
+		else
+			h->padding = value;
+	} else {
+		return -1;
+	}
+
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	FILE *file_SPKG = NULL;
+	FILE *file_input = NULL;
+	int result = -1;
+
+	for (int i = 1; i < argc; i++) {
+		unsigned long value = 0;
+		char *name = argv[i];
+		char *sz = NULL;
+
+		if (!name || name[0] != '-') {
+			fprintf(stderr, "%s invalid argument '%s'\n",
+				argv[0], argv[i]);
+			return -1;
+		}
+		name++;
+		if (name[0] == '-')
+			name++;
+
+		if (i < argc - 1 && argv[i + 1][0] != '-')
+			sz = argv[++i];
+
+		if (sz) {
+			if (!sscanf(sz, "0x%lx", &value))
+				sscanf(sz, "%lu", &value);
+		} else {
+			value = 1;
+		}
+		if (spkg_parse_option(&g_header, name, sz, value)) {
+			fprintf(stderr, "%s Error invalid '%s'\n",
+				argv[0],  argv[i]);
+			return -1;
+		}
+	}
+
+	if (!g_header.input[0]) {
+		fprintf(stderr, usage, argv[0]);
+		exit(1);
+	}
+	if (!g_header.output[0])
+		snprintf(g_header.output, sizeof(g_header.output),
+			 "%s.spkg", g_header.input);
+	if (verbose)
+		printf("%s -> %s\n", g_header.input, g_header.output);
+
+	/*NOTE: Using binary mode as this seems necessary if running in Windows */
+	file_SPKG = fopen(g_header.output, "wb");
+	file_input = fopen(g_header.input, "rb");
+
+	if (!file_SPKG)
+		perror(g_header.output);
+	if (!file_input)
+		perror(g_header.input);
+
+	if (file_input && file_SPKG)
+		result = spkg_write(&g_header, file_input, file_SPKG);
+
+	if (file_SPKG)
+		fclose(file_SPKG);
+	if (file_input)
+		fclose(file_input);
+
+	if (result >= 0) {
+		if (verbose)
+			printf("%s created\n", g_header.output);
+	} else {
+		fprintf(stderr, "ERROR creating %s\n", g_header.output);
+	}
+
+	return result;
+}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v1 9/9] tools: Add tool to create Renesas SPKG images
  2022-08-09 12:59 ` [RFC PATCH v1 9/9] tools: Add tool to create Renesas SPKG images Ralph Siemsen
@ 2022-08-09 13:03   ` Pali Rohár
  2022-08-09 13:07     ` Pali Rohár
  2022-08-12 17:03   ` [RFC PATCH v2 9/9] tools: spkgimage: add Renesas SPKG format Ralph Siemsen
  1 sibling, 1 reply; 29+ messages in thread
From: Pali Rohár @ 2022-08-09 13:03 UTC (permalink / raw)
  To: Ralph Siemsen
  Cc: u-boot, Michel Pollet, AKASHI Takahiro, Andre Przywara,
	Heiko Thiery, Samuel Holland, Simon Glass, Stefan Roese

On Tuesday 09 August 2022 08:59:59 Ralph Siemsen wrote:
> From: Michel Pollet <michel.pollet@bp.renesas.com>
> 
> Renesas RZ/N1 devices contain BootROM code that loads a custom SPKG
> image from QSPI, NAND or USB DFU. This tool converts a binary image into
> an SPKG.
> 
> SPKGs can optionally be signed, however this tool does not currently
> support signed SPKGs.
> 
> Signed-off-by: Michel Pollet <michel.pollet@bp.renesas.com>
> Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
> ---
> This tool could possibly be incorporated into mkimage / imagetools.
> However it is unclear how to handle the extra commandline parameters
> (NAND ECC settings, etc). So for now it is stand-alone tool.

Hello! You can use for example config file, like it has kwbimage.c which
is integrated into mkimage and has support for NAND ECC settings.

It is really better if BootROM specific image format is included in
standard mkimage and dumpimage tools instead of new custom vendor tools.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v1 9/9] tools: Add tool to create Renesas SPKG images
  2022-08-09 13:03   ` Pali Rohár
@ 2022-08-09 13:07     ` Pali Rohár
  2022-08-09 15:54       ` Ralph Siemsen
  0 siblings, 1 reply; 29+ messages in thread
From: Pali Rohár @ 2022-08-09 13:07 UTC (permalink / raw)
  To: Ralph Siemsen
  Cc: u-boot, AKASHI Takahiro, Andre Przywara, Heiko Thiery,
	Samuel Holland, Simon Glass, Stefan Roese

On Tuesday 09 August 2022 15:03:48 Pali Rohár wrote:
> On Tuesday 09 August 2022 08:59:59 Ralph Siemsen wrote:
> > From: Michel Pollet <michel.pollet@bp.renesas.com>
> > 
> > Renesas RZ/N1 devices contain BootROM code that loads a custom SPKG
> > image from QSPI, NAND or USB DFU. This tool converts a binary image into
> > an SPKG.
> > 
> > SPKGs can optionally be signed, however this tool does not currently
> > support signed SPKGs.
> > 
> > Signed-off-by: Michel Pollet <michel.pollet@bp.renesas.com>
> > Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
> > ---
> > This tool could possibly be incorporated into mkimage / imagetools.
> > However it is unclear how to handle the extra commandline parameters
> > (NAND ECC settings, etc). So for now it is stand-alone tool.
> 
> Hello! You can use for example config file, like it has kwbimage.c which
> is integrated into mkimage and has support for NAND ECC settings.

Or another option could be to extend mkimage tool to accept new argument
for specifying NAND settings. As we can see more image formats have
support for it, so some abstraction in mkimage makes sense here.

> It is really better if BootROM specific image format is included in
> standard mkimage and dumpimage tools instead of new custom vendor tools.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v1 9/9] tools: Add tool to create Renesas SPKG images
  2022-08-09 13:07     ` Pali Rohár
@ 2022-08-09 15:54       ` Ralph Siemsen
  2022-08-09 16:06         ` Pali Rohár
  2022-08-09 17:15         ` Sean Anderson
  0 siblings, 2 replies; 29+ messages in thread
From: Ralph Siemsen @ 2022-08-09 15:54 UTC (permalink / raw)
  To: Pali Rohár
  Cc: u-boot, AKASHI Takahiro, Andre Przywara, Heiko Thiery,
	Samuel Holland, Simon Glass, Stefan Roese

Hi Pali,

On Tuesday 09 August 2022 15:03:48 Pali Rohár wrote:
>
> Hello! You can use for example config file, like it has kwbimage.c which
> is integrated into mkimage and has support for NAND ECC settings.

Thank you, I was unaware of this config file approach. From a quick
look at doc/README.kwbimage it seems quite reasonable for
device-specific parameters.

On Tue, Aug 9, 2022 at 9:07 AM Pali Rohár <pali@kernel.org> wrote:
>
> Or another option could be to extend mkimage tool to accept new argument
> for specifying NAND settings. As we can see more image formats have
> support for it, so some abstraction in mkimage makes sense here.

In principle I agree. But practically speaking I see two problems:
1) mkimage already has far too many options
2) covering all possible vendor-specific values/encodings of NAND
parameters would be quite a task

So I am inclined to keep it simple for now, and try using a config file.

Ralph

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v1 9/9] tools: Add tool to create Renesas SPKG images
  2022-08-09 15:54       ` Ralph Siemsen
@ 2022-08-09 16:06         ` Pali Rohár
  2022-08-09 17:02           ` Ralph Siemsen
  2022-08-09 17:15         ` Sean Anderson
  1 sibling, 1 reply; 29+ messages in thread
From: Pali Rohár @ 2022-08-09 16:06 UTC (permalink / raw)
  To: Ralph Siemsen
  Cc: u-boot, AKASHI Takahiro, Andre Przywara, Heiko Thiery,
	Samuel Holland, Simon Glass, Stefan Roese

Hello!

On Tuesday 09 August 2022 11:54:30 Ralph Siemsen wrote:
> Hi Pali,
> 
> On Tuesday 09 August 2022 15:03:48 Pali Rohár wrote:
> >
> > Hello! You can use for example config file, like it has kwbimage.c which
> > is integrated into mkimage and has support for NAND ECC settings.
> 
> Thank you, I was unaware of this config file approach. From a quick
> look at doc/README.kwbimage it seems quite reasonable for
> device-specific parameters.

This documentation is not probably up-to-date. List of all kwbimage
config options can be visible in kwbimage_generate_config() function.

> On Tue, Aug 9, 2022 at 9:07 AM Pali Rohár <pali@kernel.org> wrote:
> >
> > Or another option could be to extend mkimage tool to accept new argument
> > for specifying NAND settings. As we can see more image formats have
> > support for it, so some abstraction in mkimage makes sense here.
> 
> In principle I agree. But practically speaking I see two problems:
> 1) mkimage already has far too many options

I know. But for nand there can be just --nand suboption1,suboption2,...
format. For other non-nand vendor specific option it would be an issue.
Maybe something like --vendor option or --image-options ... could be
used?

> 2) covering all possible vendor-specific values/encodings of NAND
> parameters would be quite a task

Yea, it is problematic. But... is not everything related to NAND just
common to what can be specified in device tree properties for nand node?
And de-facto already known and well-defined?

Because I cannot imagine what else for what there is not already device
tree binding, could be required for vendor bootrom. (But maybe I just do
not see it...)

> So I am inclined to keep it simple for now, and try using a config file.
> 
> Ralph

Keep it simple is the best option!

In the worst case, in the future, some of the options introduced by your
config file format, would be possible to specify also via command line
arguments. So I do not see any issue with this config file approach.


Just one suggestion: It is a good idea to also implement "verify_header"
mkimage callback. Build process then use it to verify that generated
image is really correct.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v1 9/9] tools: Add tool to create Renesas SPKG images
  2022-08-09 16:06         ` Pali Rohár
@ 2022-08-09 17:02           ` Ralph Siemsen
  0 siblings, 0 replies; 29+ messages in thread
From: Ralph Siemsen @ 2022-08-09 17:02 UTC (permalink / raw)
  To: Pali Rohár
  Cc: u-boot, AKASHI Takahiro, Andre Przywara, Heiko Thiery,
	Samuel Holland, Simon Glass, Stefan Roese

On Tue, Aug 9, 2022 at 12:07 PM Pali Rohár <pali@kernel.org> wrote:
>
> This documentation is not probably up-to-date. List of all kwbimage
> config options can be visible in kwbimage_generate_config() function.

I will check the code as well.

> > 1) mkimage already has far too many options
>
> I know. But for nand there can be just --nand suboption1,suboption2,...
> format. For other non-nand vendor specific option it would be an issue.
> Maybe something like --vendor option or --image-options ... could be
> used?

In my case this would work, as there are only 3 parameters for NAND.
However in general, it could get pretty messy, I see at least ten
different NAND parameters described in the NAND DT binding.

> > 2) covering all possible vendor-specific values/encodings of NAND
> > parameters would be quite a task
>
> Yea, it is problematic. But... is not everything related to NAND just
> common to what can be specified in device tree properties for nand node?
> And de-facto already known and well-defined?

I did have the thought of using device tree. It does have the
advantage that it is a known format with defined parameters.

> Because I cannot imagine what else for what there is not already device
> tree binding, could be required for vendor bootrom. (But maybe I just do
> not see it...)

In quite a few cases, the DT parameters are incomplete, or just hints
(see "nand-ecc-maximize"), that trigger various run-time decisions
about the actual parameters.

Duplicating this logic in mkimage seems difficult, bordering on
impossible if it depends on run-time identification of the flash chip,
for example.

> Just one suggestion: It is a good idea to also implement "verify_header"
> mkimage callback. Build process then use it to verify that generated
> image is really correct.

I'll check it out, thanks!

Ralph

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v1 9/9] tools: Add tool to create Renesas SPKG images
  2022-08-09 15:54       ` Ralph Siemsen
  2022-08-09 16:06         ` Pali Rohár
@ 2022-08-09 17:15         ` Sean Anderson
  2022-08-12 17:00           ` Ralph Siemsen
  1 sibling, 1 reply; 29+ messages in thread
From: Sean Anderson @ 2022-08-09 17:15 UTC (permalink / raw)
  To: Ralph Siemsen, Pali Rohár
  Cc: u-boot, AKASHI Takahiro, Andre Przywara, Heiko Thiery,
	Samuel Holland, Simon Glass, Stefan Roese

Hi Ralph,

On 8/9/22 11:54 AM, Ralph Siemsen wrote:
> Hi Pali,
> 
> On Tuesday 09 August 2022 15:03:48 Pali Rohár wrote:
>>
>> Hello! You can use for example config file, like it has kwbimage.c which
>> is integrated into mkimage and has support for NAND ECC settings.
> 
> Thank you, I was unaware of this config file approach. From a quick
> look at doc/README.kwbimage it seems quite reasonable for
> device-specific parameters.
> 
> On Tue, Aug 9, 2022 at 9:07 AM Pali Rohár <pali@kernel.org> wrote:
>>
>> Or another option could be to extend mkimage tool to accept new argument
>> for specifying NAND settings. As we can see more image formats have
>> support for it, so some abstraction in mkimage makes sense here.
> 
> In principle I agree. But practically speaking I see two problems:
> 1) mkimage already has far too many options
> 2) covering all possible vendor-specific values/encodings of NAND
> parameters would be quite a task
> 
> So I am inclined to keep it simple for now, and try using a config file.

The traditional way to handle this is to specify a config file with -n.
See e.g. mtk_image

--Sean

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v1 9/9] tools: Add tool to create Renesas SPKG images
  2022-08-09 17:15         ` Sean Anderson
@ 2022-08-12 17:00           ` Ralph Siemsen
  0 siblings, 0 replies; 29+ messages in thread
From: Ralph Siemsen @ 2022-08-12 17:00 UTC (permalink / raw)
  To: Sean Anderson
  Cc: Pali Rohár, u-boot, AKASHI Takahiro, Andre Przywara,
	Heiko Thiery, Samuel Holland, Simon Glass, Stefan Roese

On Tue, Aug 9, 2022 at 1:15 PM Sean Anderson <sean.anderson@seco.com> wrote:
>
> The traditional way to handle this is to specify a config file with -n.
> See e.g. mtk_image

Thanks Pali and Sean. I have converted this tool to work as part of
mkimage, with a config file for the extra parameters. Patch v2 to
follow.

Ralph

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 9/9] tools: spkgimage: add Renesas SPKG format
  2022-08-09 12:59 ` [RFC PATCH v1 9/9] tools: Add tool to create Renesas SPKG images Ralph Siemsen
  2022-08-09 13:03   ` Pali Rohár
@ 2022-08-12 17:03   ` Ralph Siemsen
  2022-08-13 14:47     ` Sean Anderson
  1 sibling, 1 reply; 29+ messages in thread
From: Ralph Siemsen @ 2022-08-12 17:03 UTC (permalink / raw)
  To: ralph.siemsen
  Cc: andre.przywara, heiko.thiery, pali, samuel, sjg, sr,
	takahiro.akashi, u-boot, sean.anderson

Renesas RZ/N1 devices contain BootROM code that loads a custom SPKG
image from QSPI, NAND or USB DFU. Support this format in mkimage tool.

SPKGs can optionally be signed, however creation of signed SPKG is not
currently supported.

Example of how to use it:

tools/mkimage -n board/schneider/lces/spkgimage.cfg \
	-T spkgimage -a 0x20040000 -e 0x20040000 \
	-d u-boot.bin u-boot.bin.spkg

The config file (spkgimage.cfg in this example) contains additional
parameters such as NAND ECC settings.

Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
---

Changes in v2:
- rewrote the stand-alone spkg_utility to integrate into mkimage

 board/schneider/lces/spkgimage.cfg |  26 +++
 boot/image.c                       |   1 +
 include/image.h                    |   1 +
 tools/Makefile                     |   1 +
 tools/spkgimage.c                  | 303 +++++++++++++++++++++++++++++
 tools/spkgimage.h                  |  39 ++++
 6 files changed, 371 insertions(+)
 create mode 100644 board/schneider/lces/spkgimage.cfg
 create mode 100644 tools/spkgimage.c
 create mode 100644 tools/spkgimage.h

diff --git a/board/schneider/lces/spkgimage.cfg b/board/schneider/lces/spkgimage.cfg
new file mode 100644
index 0000000000..b5faf96b00
--- /dev/null
+++ b/board/schneider/lces/spkgimage.cfg
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2022 Schneider Electric
+#
+# SPKG image header, for booting on RZ/N1
+
+# b[35:32] SPKG version
+VERSION			1
+
+# b[42:41]  ECC Block size: 0=256 bytes, 1=512 bytes, 2=1024 bytes
+NAND_ECC_BLOCK_SIZE	1
+
+# b[45]     NAND enable (boolean)
+NAND_ECC_ENABLE		1
+
+# b[50:48]  ECC Scheme: 0=BCH2 1=BCH4 2=BCH8 3=BCH16 4=BCH24 5=BCH32
+NAND_ECC_SCHEME		3
+
+# b[63:56]  ECC bytes per block
+NAND_BYTES_PER_ECC_BLOCK 28
+
+# Provide dummy BLp header (boolean)
+ADD_DUMMY_BLP		1
+
+# Pad the image to a multiple of
+PADDING			64K
diff --git a/boot/image.c b/boot/image.c
index 5dcb55ba46..7d50d708ab 100644
--- a/boot/image.c
+++ b/boot/image.c
@@ -179,6 +179,7 @@ static const table_entry_t uimage_type[] = {
 	{	IH_TYPE_COPRO, "copro", "Coprocessor Image"},
 	{	IH_TYPE_SUNXI_EGON, "sunxi_egon",  "Allwinner eGON Boot Image" },
 	{	IH_TYPE_SUNXI_TOC0, "sunxi_toc0",  "Allwinner TOC0 Boot Image" },
+	{	IH_TYPE_RENESAS_SPKG, "spkgimage", "Renesas SPKG Image" },
 	{	-1,		    "",		  "",			},
 };
 
diff --git a/include/image.h b/include/image.h
index e4c6a50b88..98eaa384f4 100644
--- a/include/image.h
+++ b/include/image.h
@@ -229,6 +229,7 @@ enum {
 	IH_TYPE_COPRO,			/* Coprocessor Image for remoteproc*/
 	IH_TYPE_SUNXI_EGON,		/* Allwinner eGON Boot Image */
 	IH_TYPE_SUNXI_TOC0,		/* Allwinner TOC0 Boot Image */
+	IH_TYPE_RENESAS_SPKG,		/* Renesas SPKG image */
 
 	IH_TYPE_COUNT,			/* Number of image types */
 };
diff --git a/tools/Makefile b/tools/Makefile
index 005e7362a3..7e24f3ecb9 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -131,6 +131,7 @@ dumpimage-mkimage-objs := aisimage.o \
 			stm32image.o \
 			$(ROCKCHIP_OBS) \
 			socfpgaimage.o \
+			spkgimage.o \
 			sunxi_egon.o \
 			lib/crc16-ccitt.o \
 			lib/hash-checksum.o \
diff --git a/tools/spkgimage.c b/tools/spkgimage.c
new file mode 100644
index 0000000000..2e8c17d94a
--- /dev/null
+++ b/tools/spkgimage.c
@@ -0,0 +1,303 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Generate Renesas RZ/N1 BootROM header (SPKG)
+ * (C) Copyright 2022 Schneider Electric
+ *
+ * Based on spkg_utility.c
+ * (C) Copyright 2016 Renesas Electronics Europe Ltd
+ */
+
+#include "imagetool.h"
+#include <limits.h>
+#include <image.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <u-boot/crc.h>
+#include "spkgimage.h"
+
+static struct spkg_file out_buf;
+
+static uint32_t padding;
+
+/* Note: the ordering of the bitfields does not matter */
+static struct config_file {
+	unsigned int version:1;
+	unsigned int ecc_block_size:2;
+	unsigned int ecc_enable:1;
+	unsigned int ecc_scheme:3;
+	unsigned int ecc_bytes:8;
+	unsigned int blp_len;
+	unsigned int padding;
+} conf;
+
+static int spkgimage_parse_config_line(char *line)
+{
+	char *saveptr;
+	char *delim = "\t ";
+	char *name = strtok_r(line, delim, &saveptr);
+	char *val_str = strtok_r(NULL, delim, &saveptr);
+	int value = atoi(val_str);
+
+	if (!strcmp("VERSION", name)) {
+		conf.version = value;
+	} else if (!strcmp("NAND_ECC_ENABLE", name)) {
+		conf.ecc_enable = value;
+	} else if (!strcmp("NAND_ECC_BLOCK_SIZE", name)) {
+		conf.ecc_block_size = value;
+	} else if (!strcmp("NAND_ECC_SCHEME", name)) {
+		conf.ecc_scheme = value;
+	} else if (!strcmp("NAND_BYTES_PER_ECC_BLOCK", name)) {
+		conf.ecc_bytes = value;
+	} else if (!strcmp("ADD_DUMMY_BLP", name)) {
+		conf.blp_len = value ? SPKG_BLP_SIZE : 0;
+	} else if (!strcmp("PADDING", name)) {
+		if (strrchr(val_str, 'K'))
+			conf.padding = value * 1024;
+		else if (strrchr(val_str, 'M'))
+			conf.padding = value * 1024 * 1024;
+		else
+			conf.padding = value;
+	} else {
+		fprintf(stderr, "Error: unknown keyword '%s' in config\n",
+			name);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int spkgimage_parse_config_file(char *filename)
+{
+	FILE *fcfg;
+	char line[256];
+	size_t len;
+
+	fcfg = fopen(filename, "r");
+	if (!fcfg)
+		return -EINVAL;
+
+	while (fgets(line, sizeof(line), fcfg)) {
+		/* Skip blank lines and comments */
+		if (line[0] == '\n' || line[0] == '#')
+			continue;
+
+		/* Strip the trailing newline */
+		len = strlen(line);
+		if (line[len - 1] == '\n')
+			line[--len] = 0;
+
+		/* Parse the line */
+		if (spkgimage_parse_config_line(line))
+			return -EINVAL;
+	}
+
+	fclose(fcfg);
+
+	/* Avoid divide-by-zero later on */
+	if (conf.padding == 0)
+		conf.padding = 1;
+
+	return 0;
+}
+
+static int spkgimage_check_params(struct image_tool_params *params)
+{
+	if (!params->addr) {
+		fprintf(stderr, "Error: Load Address must be set.\n");
+		return -EINVAL;
+	}
+
+	if (!params->imagename || !params->imagename[0]) {
+		fprintf(stderr, "Error: Image name must be set.\n");
+		return -EINVAL;
+	}
+
+	if (!params->datafile) {
+		fprintf(stderr, "Error: Data filename must be set.\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int spkgimage_verify_header(unsigned char *ptr, int size,
+				   struct image_tool_params *param)
+{
+	struct spkg_file *file = (struct spkg_file *)ptr;
+	struct spkg_hdr *header = (struct spkg_hdr *)ptr;
+	char signature[4] = SPKG_HEADER_SIGNATURE;
+	uint32_t payload_length;
+	uint32_t crc;
+	uint8_t *crc_buf;
+
+	/* Check the signature */
+	if (memcmp(header->signature, signature, 4)) {
+		fprintf(stderr, "Error: invalid signature bytes\n");
+		return -EINVAL;
+	}
+
+	/* Check the CRC */
+	crc = crc32(0, ptr, SPKG_HEADER_SIZE - SPKG_CRC_SIZE);
+	if (crc != header->crc) {
+		fprintf(stderr, "Error: invalid header CRC=\n");
+		return -EINVAL;
+	}
+
+	/* Check all copies of header are the same */
+	for (int i = 1; i < SPKG_HEADER_COUNT; i++) {
+		if (memcmp(&header[0], &header[i], SPKG_HEADER_SIZE)) {
+			fprintf(stderr, "Error: header %d mismatch\n", i);
+			return -EINVAL;
+		}
+	}
+
+	/* Check the payload CRC */
+	payload_length = le32_to_cpu(header->payload_length) >> 8;
+	crc_buf = file->payload + payload_length - SPKG_CRC_SIZE;
+	crc = crc32(0, file->payload, payload_length - SPKG_CRC_SIZE);
+	if (crc_buf[0] != (crc & 0xff) ||
+	    crc_buf[1] != (crc >> 8 & 0xff) ||
+	    crc_buf[2] != (crc >> 16 & 0xff) ||
+	    crc_buf[3] != (crc >> 24 & 0xff)) {
+		fprintf(stderr, "Error: invalid payload CRC\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void spkgimage_print_header(const void *ptr)
+{
+	const struct spkg_hdr *h = ptr;
+	uint32_t offset = le32_to_cpu(h->execution_offset);
+
+	printf("Image type\t: Renesas SPKG Image\n");
+	printf("Marker\t\t: %c%c%c%c\n", h->signature[0], h->signature[1],
+					 h->signature[2], h->signature[3]);
+	printf("Version\t\t: %d\n", h->version);
+	printf("ECC\t\t: ");
+	if (h->ecc & 0x20)
+		printf("Scheme %d, Block size %d, Strength %d\n",
+		       h->ecc_scheme, (h->ecc >> 1) & 3, h->ecc_bytes);
+	else
+		printf("Not enabled\n");
+	printf("Payload length\t: %d\n", le32_to_cpu(h->payload_length) >> 8);
+	printf("Load address\t: 0x%08x\n", le32_to_cpu(h->load_address));
+	printf("Execution offset: 0x%08x (%s mode)\n", offset & ~1,
+	       offset & 1 ? "THUMB" : "ARM");
+	printf("Header checksum\t: 0x%08x\n", le32_to_cpu(h->crc));
+}
+
+static inline uint32_t roundup(uint32_t x, uint32_t y)
+{
+	return ((x + y - 1) / y) * y;
+}
+
+static int spkgimage_vrec_header(struct image_tool_params *params,
+				 struct image_type_params *tparams)
+{
+	struct stat s;
+
+	/* Parse the config file */
+	if (spkgimage_parse_config_file(params->imagename)) {
+		fprintf(stderr, "Error parsing config file\n");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Get size of input data file */
+	if (stat(params->datafile, &s)) {
+		fprintf(stderr, "Could not stat data file: %s: %s\n",
+			params->datafile, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+	params->orig_file_size = s.st_size;
+
+	/* Determine size of resulting SPKG file */
+	uint32_t header_len = SPKG_HEADER_SIZE * SPKG_HEADER_COUNT;
+	uint32_t payload_len = conf.blp_len + s.st_size + SPKG_CRC_SIZE;
+	uint32_t total_len = header_len + payload_len;
+
+	/* Round up to next multiple of padding size */
+	uint32_t padded_len = roundup(total_len, conf.padding);
+
+	/* Number of padding bytes to add */
+	padding = padded_len - total_len;
+
+	/* Fixup payload_len to include padding bytes */
+	payload_len += padding;
+
+	/* Prepare the header */
+	struct spkg_hdr header = {
+		.signature = SPKG_HEADER_SIGNATURE,
+		.version = conf.version,
+		.ecc = (conf.ecc_enable << 5) | (conf.ecc_block_size << 1),
+		.ecc_scheme = conf.ecc_scheme,
+		.ecc_bytes = conf.ecc_bytes,
+		.payload_length = cpu_to_le32(payload_len << 8),
+		.load_address = cpu_to_le32(params->addr),
+		.execution_offset = cpu_to_le32(params->ep - params->addr),
+	};
+	header.crc = crc32(0, (uint8_t *)&header,
+			   sizeof(header) - SPKG_CRC_SIZE);
+
+	/* Fill the SPKG with the headers */
+	for (int i = 0; i < SPKG_HEADER_COUNT; i++)
+		memcpy(&out_buf.header[i], &header, sizeof(header));
+
+	/* Extra bytes to allocate in the output file */
+	return conf.blp_len + padding + 4;
+}
+
+static void spkgimage_set_header(void *ptr, struct stat *sbuf, int ifd,
+				 struct image_tool_params *params)
+{
+	uint8_t *payload = ptr + SPKG_HEADER_SIZE * SPKG_HEADER_COUNT;
+	uint8_t *file_end = payload + conf.blp_len + params->orig_file_size;
+	uint8_t *crc_buf = file_end + padding;
+	uint32_t crc;
+
+	/* Make room for the Dummy BLp header */
+	memmove(payload + conf.blp_len, payload, params->orig_file_size);
+
+	/* Fill the SPKG with the Dummy BLp */
+	memset(payload, 0x88, conf.blp_len);
+
+	/*
+	 * mkimage copy_file() pads the input file with zeros.
+	 * Replace those zeros with flash friendly one bits.
+	 * The original version skipped the fist 4 bytes,
+	 * probably an oversight, but for consistency we
+	 * keep the same behaviour.
+	 */
+	memset(file_end + 4, 0xff, padding - 4);
+
+	/* Add Payload CRC */
+	crc = crc32(0, payload, crc_buf - payload);
+	crc_buf[0] = crc;
+	crc_buf[1] = crc >> 8;
+	crc_buf[2] = crc >> 16;
+	crc_buf[3] = crc >> 24;
+}
+
+static int spkgimage_check_image_types(uint8_t type)
+{
+	return type == IH_TYPE_RENESAS_SPKG ? 0 : 1;
+}
+
+/*
+ * spkgimage type parameter definition
+ */
+U_BOOT_IMAGE_TYPE(
+	spkgimage,
+	"Renesas SPKG Image",
+	sizeof(out_buf),
+	&out_buf,
+	spkgimage_check_params,
+	spkgimage_verify_header,
+	spkgimage_print_header,
+	spkgimage_set_header,
+	NULL,
+	spkgimage_check_image_types,
+	NULL,
+	spkgimage_vrec_header
+);
diff --git a/tools/spkgimage.h b/tools/spkgimage.h
new file mode 100644
index 0000000000..db153bfc3f
--- /dev/null
+++ b/tools/spkgimage.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Renesas RZ/N1 Package Table format
+ * (C) 2015-2016 Renesas Electronics Europe, LTD
+ * All rights reserved.
+ *
+ * Converted to mkimage plug-in
+ * (C) Copyright 2022 Schneider Electric
+ */
+
+#ifndef _SPKGIMAGE_H_
+#define _SPKGIMAGE_H_
+
+#define SPKG_HEADER_SIGNATURE	{'R', 'Z', 'N', '1'}
+#define SPKG_HEADER_SIZE	24
+#define SPKG_HEADER_COUNT	8
+#define SPKG_BLP_SIZE		264
+#define SPKG_CRC_SIZE		4
+
+/* SPKG header */
+struct spkg_hdr {
+	uint8_t		signature[4];
+	uint8_t		version;
+	uint8_t		ecc;
+	uint8_t		ecc_scheme;
+	uint8_t		ecc_bytes;
+	uint32_t	payload_length; /* only HIGHER 24 bits */
+	uint32_t	load_address;
+	uint32_t	execution_offset;
+	uint32_t	crc; /* of this header */
+} __packed;
+
+struct spkg_file {
+	struct spkg_hdr	header[SPKG_HEADER_COUNT];
+	uint8_t		payload[0];
+	/* then the CRC */
+} __packed;
+
+#endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v1 2/9] clk: renesas: prepare for non-RCAR clock drivers
  2022-08-09 12:59 ` [RFC PATCH v1 2/9] clk: renesas: prepare for non-RCAR clock drivers Ralph Siemsen
@ 2022-08-13  4:37   ` Sean Anderson
  0 siblings, 0 replies; 29+ messages in thread
From: Sean Anderson @ 2022-08-13  4:37 UTC (permalink / raw)
  To: Ralph Siemsen, u-boot; +Cc: Lukasz Majewski

On 8/9/22 8:59 AM, Ralph Siemsen wrote:
> Allow CONFIG_CLK_RENESAS to be set without bringing in RCAR-GEN2/3 code.
> 
> CONFIG_RENESAS is used in drivers/clk/Makefile to control recursion into
> the drivers/clk/renesas subdirectory. It also controls compilation of
> renesas-cpg-mssr.c support code for the RCAR-GEN2 and RCAR-GEN3 devices.
> 
> The support code contains platform specific hardware access (TMU_BASE),
> and it is not needed for other Renesas devices such as RZ/N1. Therefore,
> alter Makefile to build renesas-cpg-mssr.c only for RCAR-GEN2 and RCAR-GEN3.
> 
> Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
> ---
> 
>   drivers/clk/renesas/Kconfig  | 2 +-
>   drivers/clk/renesas/Makefile | 5 ++---
>   2 files changed, 3 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig
> index f4d6ef9f93..c53ff3ce01 100644
> --- a/drivers/clk/renesas/Kconfig
> +++ b/drivers/clk/renesas/Kconfig
> @@ -1,6 +1,6 @@
>   config CLK_RENESAS
>   	bool "Renesas clock drivers"
> -	depends on CLK && ARCH_RMOBILE
> +	depends on CLK && (ARCH_RMOBILE || ARCH_RZN1)
>   	help
>   	  Enable support for clock present on Renesas RCar SoCs.
>   
> diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile
> index 36a5ca65f4..2cd2c69f68 100644
> --- a/drivers/clk/renesas/Makefile
> +++ b/drivers/clk/renesas/Makefile
> @@ -1,5 +1,4 @@
> -obj-$(CONFIG_CLK_RENESAS) += renesas-cpg-mssr.o
> -obj-$(CONFIG_CLK_RCAR_GEN2) += clk-rcar-gen2.o
> +obj-$(CONFIG_CLK_RCAR_GEN2) += clk-rcar-gen2.o renesas-cpg-mssr.o
>   obj-$(CONFIG_CLK_R8A774A1) += r8a774a1-cpg-mssr.o
>   obj-$(CONFIG_CLK_R8A774B1) += r8a774b1-cpg-mssr.o
>   obj-$(CONFIG_CLK_R8A774C0) += r8a774c0-cpg-mssr.o
> @@ -9,7 +8,7 @@ obj-$(CONFIG_CLK_R8A7791) += r8a7791-cpg-mssr.o
>   obj-$(CONFIG_CLK_R8A7792) += r8a7792-cpg-mssr.o
>   obj-$(CONFIG_CLK_R8A7793) += r8a7791-cpg-mssr.o
>   obj-$(CONFIG_CLK_R8A7794) += r8a7794-cpg-mssr.o
> -obj-$(CONFIG_CLK_RCAR_GEN3) += clk-rcar-gen3.o
> +obj-$(CONFIG_CLK_RCAR_GEN3) += clk-rcar-gen3.o renesas-cpg-mssr.o
>   obj-$(CONFIG_CLK_R8A7795) += r8a7795-cpg-mssr.o
>   obj-$(CONFIG_CLK_R8A7796) += r8a7796-cpg-mssr.o
>   obj-$(CONFIG_CLK_R8A77965) += r8a77965-cpg-mssr.o
> 

Reviewed-by: Sean Anderson <seanga2@gmail.com>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v1 3/9] clk: renesas: add R906G032 driver
  2022-08-09 12:59 ` [RFC PATCH v1 3/9] clk: renesas: add R906G032 driver Ralph Siemsen
@ 2022-08-13  5:30   ` Sean Anderson
  2022-08-15  2:48     ` Ralph Siemsen
  0 siblings, 1 reply; 29+ messages in thread
From: Sean Anderson @ 2022-08-13  5:30 UTC (permalink / raw)
  To: Ralph Siemsen, u-boot; +Cc: Lukasz Majewski

On 8/9/22 8:59 AM, Ralph Siemsen wrote:
> Clock driver for the Renesas RZ/N1 SoC family. This is based
> on the Linux kernel drivers/clk/renesas/r9a06g032-clocks.c.
> 
> Notable difference: this version avoids allocating a 'struct clk'
> for each clock source, as this is problematic before relocation.
> Instead, it uses the same approach as existing Renesas RCAR2/3
> clock drivers, using a temporary structure filled on-the-fly.
> 
> Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
> ---
> - TODO: add support for div_table
> 
>   drivers/clk/renesas/Kconfig            |   6 +
>   drivers/clk/renesas/Makefile           |   1 +
>   drivers/clk/renesas/r9a06g032-clocks.c | 734 +++++++++++++++++++++++++
>   3 files changed, 741 insertions(+)
>   create mode 100644 drivers/clk/renesas/r9a06g032-clocks.c
> 
> diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig
> index c53ff3ce01..e2f72fc04f 100644
> --- a/drivers/clk/renesas/Kconfig
> +++ b/drivers/clk/renesas/Kconfig
> @@ -120,3 +120,9 @@ config CLK_R8A779A0
>   	depends on CLK_RCAR_GEN3
>   	help
>   	  Enable this to support the clocks on Renesas R8A779A0 SoC.
> +
> +config CLK_R9A06G032
> +	bool "Renesas R9A06G032 clock driver"
> +	depends on CLK_RENESAS
> +	help
> +	  Enable this to support the clocks on Renesas R9A06G032 SoC.

nit: on the

...

> diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile
> index 2cd2c69f68..9981f1a0bc 100644
> --- a/drivers/clk/renesas/Makefile
> +++ b/drivers/clk/renesas/Makefile
> @@ -17,3 +17,4 @@ obj-$(CONFIG_CLK_R8A77980) += r8a77980-cpg-mssr.o
>   obj-$(CONFIG_CLK_R8A77990) += r8a77990-cpg-mssr.o
>   obj-$(CONFIG_CLK_R8A77995) += r8a77995-cpg-mssr.o
>   obj-$(CONFIG_CLK_R8A779A0) += r8a779a0-cpg-mssr.o
> +obj-$(CONFIG_CLK_R9A06G032) += r9a06g032-clocks.o
> diff --git a/drivers/clk/renesas/r9a06g032-clocks.c b/drivers/clk/renesas/r9a06g032-clocks.c
> new file mode 100644
> index 0000000000..9c8f51eb96
> --- /dev/null
> +++ b/drivers/clk/renesas/r9a06g032-clocks.c
> @@ -0,0 +1,734 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * R9A06G032 clock driver
> + *
> + * Copyright (C) 2018 Renesas Electronics Europe Limited
> + *
> + * Michel Pollet <michel.pollet@bp.renesas.com>, <buserror@gmail.com>
> + */
> +
> +#include <common.h>
> +#include <clk-uclass.h>
> +#include <dm.h>
> +#include <regmap.h>
> +#include <syscon.h>
> +#include <linux/bitops.h>
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +#include <asm/io.h>
> +
> +#include <dt-bindings/clock/r9a06g032-sysctrl.h>
> +
> +struct r9a06g032_gate {

Can you add some documentation for each of the fields? Same for r9a06g032_clkdesc.

https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html

> +	u16 gate, reset, ready, midle,
> +		scon, mirack, mistat;

What are the scon/mirack/mistat fields for? You define them for a lot
of clocks, but I don't see them used in the driver.

> +};
> +
> +/* This is used to describe a clock for instantiation */
> +struct r9a06g032_clkdesc {
> +	const char *name;
> +	uint32_t managed: 1;
> +	uint32_t type: 3;

I wonder if we could define the enum here?

> +	uint32_t index: 8;
> +	uint32_t source : 8; /* source index + 1 (0 == none) */
> +	/* these are used to populate the bitsel struct */
> +	union {
> +		struct r9a06g032_gate gate;
> +		/* for dividers */
> +		struct {
> +			unsigned int div_min : 10, div_max : 10, reg: 10;
> +			u16 div_table[4];
> +		};
> +		/* For fixed-factor ones */
> +		struct {
> +			u16 div, mul;
> +		};
> +		/* for dual gate */
> +		struct {
> +			uint16_t group : 1;
> +			u16 sel, g1, r1, g2, r2;
> +		} dual;
> +	};
> +};
> +
> +#define I_GATE(_clk, _rst, _rdy, _midle, _scon, _mirack, _mistat) \
> +	{ .gate = _clk, .reset = _rst, \

If these fields have bitfield inside them, then those bitfields should
be assigned/constructed separately. That is, if .reset is actually a combined
offset/bit, then you need to expose those in the macro. Since you have a lot of these, you might want to do something like

#define BIT_OFFSET	GENMASK(15, 5)
#define BIT_SHIFT	GENMASK(4, 0)

#define PACK_BIT(offset, shift)		(FIELD_PREP(BIT_OFFSET, offset) | FIELD_PREP(BIT_SHIFT, shift))

> +		.ready = _rdy, .midle = _midle, \
> +		.scon = _scon, .mirack = _mirack, .mistat = _mistat }

Please put each assignment on a separate line

> +#define D_GATE(_idx, _n, _src, ...) \
> +	{ .type = K_GATE, .index = R9A06G032_##_idx, \
> +		.source = 1 + R9A06G032_##_src, .name = _n, \
> +		.gate = I_GATE(__VA_ARGS__) }
> +#define D_MODULE(_idx, _n, _src, ...) \
> +	{ .type = K_GATE, .index = R9A06G032_##_idx, \
> +		.source = 1 + R9A06G032_##_src, .name = _n, \
> +		.managed = 1, .gate = I_GATE(__VA_ARGS__) }
> +#define D_ROOT(_idx, _n, _mul, _div) \
> +	{ .type = K_FFC, .index = R9A06G032_##_idx, .name = _n, \
> +		.div = _div, .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}
> +#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__ } }
> +#define D_UGATE(_idx, _n, _src, _g, _g1, _r1, _g2, _r2) \
> +	{ .type = K_DUALGATE, .index = R9A06G032_##_idx, \
> +		.source = 1 + R9A06G032_##_src, .name = _n, \
> +		.dual = { .group = _g, \
> +			.g1 = _g1, .r1 = _r1, .g2 = _g2, .r2 = _r2 }, }
> +
> +enum { K_GATE = 0, K_FFC, K_DIV, K_BITSEL, K_DUALGATE };

Please put each member on a separate line.

> +
> +/* Internal clock IDs */
> +#define R9A06G032_CLKOUT		0
> +#define R9A06G032_CLKOUT_D10		2
> +#define R9A06G032_CLKOUT_D16		3
> +#define R9A06G032_CLKOUT_D160		4
> +#define R9A06G032_CLKOUT_D1OR2		5
> +#define R9A06G032_CLKOUT_D20		6
> +#define R9A06G032_CLKOUT_D40		7
> +#define R9A06G032_CLKOUT_D5		8
> +#define R9A06G032_CLKOUT_D8		9
> +#define R9A06G032_DIV_ADC		10
> +#define R9A06G032_DIV_I2C		11
> +#define R9A06G032_DIV_NAND		12
> +#define R9A06G032_DIV_P1_PG		13
> +#define R9A06G032_DIV_P2_PG		14
> +#define R9A06G032_DIV_P3_PG		15
> +#define R9A06G032_DIV_P4_PG		16
> +#define R9A06G032_DIV_P5_PG		17
> +#define R9A06G032_DIV_P6_PG		18
> +#define R9A06G032_DIV_QSPI0		19
> +#define R9A06G032_DIV_QSPI1		20
> +#define R9A06G032_DIV_REF_SYNC		21
> +#define R9A06G032_DIV_SDIO0		22
> +#define R9A06G032_DIV_SDIO1		23
> +#define R9A06G032_DIV_SWITCH		24
> +#define R9A06G032_DIV_UART		25
> +#define R9A06G032_DIV_MOTOR		64
> +#define R9A06G032_CLK_DDRPHY_PLLCLK_D4	78
> +#define R9A06G032_CLK_ECAT100_D4	79
> +#define R9A06G032_CLK_HSR100_D2		80
> +#define R9A06G032_CLK_REF_SYNC_D4	81
> +#define R9A06G032_CLK_REF_SYNC_D8	82
> +#define R9A06G032_CLK_SERCOS100_D2	83
> +#define R9A06G032_DIV_CA7		84
> +
> +#define R9A06G032_UART_GROUP_012	154
> +#define R9A06G032_UART_GROUP_34567	155

Can you put these in your dt-bindings header? I think that would make it
much clearer why there are gaps, and would avoid someone accidentally
duplicating a clock id (although I suppose your array initializer below
might complain?)

> +#define R9A06G032_CLOCK_COUNT		(R9A06G032_UART_GROUP_34567 + 1)
> +
> +static const struct r9a06g032_clkdesc r9a06g032_clocks[] = {
> +	D_ROOT(CLKOUT, "clkout", 25, 1),
> +	D_ROOT(CLK_PLL_USB, "clk_pll_usb", 12, 10),
> +	D_FFC(CLKOUT_D10, "clkout_d10", CLKOUT, 10),
> +	D_FFC(CLKOUT_D16, "clkout_d16", CLKOUT, 16),
> +	D_FFC(CLKOUT_D160, "clkout_d160", CLKOUT, 160),
> +	D_DIV(CLKOUT_D1OR2, "clkout_d1or2", CLKOUT, 0, 1, 2),
> +	D_FFC(CLKOUT_D20, "clkout_d20", CLKOUT, 20),
> +	D_FFC(CLKOUT_D40, "clkout_d40", CLKOUT, 40),
> +	D_FFC(CLKOUT_D5, "clkout_d5", CLKOUT, 5),
> +	D_FFC(CLKOUT_D8, "clkout_d8", CLKOUT, 8),
> +	D_DIV(DIV_ADC, "div_adc", CLKOUT, 77, 50, 250),
> +	D_DIV(DIV_I2C, "div_i2c", CLKOUT, 78, 12, 16),
> +	D_DIV(DIV_NAND, "div_nand", CLKOUT, 82, 12, 32),
> +	D_DIV(DIV_P1_PG, "div_p1_pg", CLKOUT, 68, 12, 200),
> +	D_DIV(DIV_P2_PG, "div_p2_pg", CLKOUT, 62, 12, 128),
> +	D_DIV(DIV_P3_PG, "div_p3_pg", CLKOUT, 64, 8, 128),
> +	D_DIV(DIV_P4_PG, "div_p4_pg", CLKOUT, 66, 8, 128),
> +	D_DIV(DIV_P5_PG, "div_p5_pg", CLKOUT, 71, 10, 40),
> +	D_DIV(DIV_P6_PG, "div_p6_pg", CLKOUT, 18, 12, 64),
> +	D_DIV(DIV_QSPI0, "div_qspi0", CLKOUT, 73, 3, 7),
> +	D_DIV(DIV_QSPI1, "div_qspi1", CLKOUT, 25, 3, 7),
> +	D_DIV(DIV_REF_SYNC, "div_ref_sync", CLKOUT, 56, 2, 16, 2, 4, 8, 16),
> +	D_DIV(DIV_SDIO0, "div_sdio0", CLKOUT, 74, 20, 128),
> +	D_DIV(DIV_SDIO1, "div_sdio1", CLKOUT, 75, 20, 128),
> +	D_DIV(DIV_SWITCH, "div_switch", CLKOUT, 37, 5, 40),
> +	D_DIV(DIV_UART, "div_uart", CLKOUT, 79, 12, 128),
> +	D_GATE(CLK_25_PG4, "clk_25_pg4", CLKOUT_D40, 0x749, 0x74a, 0x74b, 0, 0xae3, 0, 0),
> +	D_GATE(CLK_25_PG5, "clk_25_pg5", CLKOUT_D40, 0x74c, 0x74d, 0x74e, 0, 0xae4, 0, 0),
> +	D_GATE(CLK_25_PG6, "clk_25_pg6", CLKOUT_D40, 0x74f, 0x750, 0x751, 0, 0xae5, 0, 0),
> +	D_GATE(CLK_25_PG7, "clk_25_pg7", CLKOUT_D40, 0x752, 0x753, 0x754, 0, 0xae6, 0, 0),
> +	D_GATE(CLK_25_PG8, "clk_25_pg8", CLKOUT_D40, 0x755, 0x756, 0x757, 0, 0xae7, 0, 0),
> +	D_GATE(CLK_ADC, "clk_adc", DIV_ADC, 0x1ea, 0x1eb, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_ECAT100, "clk_ecat100", CLKOUT_D10, 0x405, 0, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_HSR100, "clk_hsr100", CLKOUT_D10, 0x483, 0, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_I2C0, "clk_i2c0", DIV_I2C, 0x1e6, 0x1e7, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_I2C1, "clk_i2c1", DIV_I2C, 0x1e8, 0x1e9, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_MII_REF, "clk_mii_ref", CLKOUT_D40, 0x342, 0, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_NAND, "clk_nand", DIV_NAND, 0x284, 0x285, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_NOUSBP2_PG6, "clk_nousbp2_pg6", DIV_P2_PG, 0x774, 0x775, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_P1_PG2, "clk_p1_pg2", DIV_P1_PG, 0x862, 0x863, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_P1_PG3, "clk_p1_pg3", DIV_P1_PG, 0x864, 0x865, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_P1_PG4, "clk_p1_pg4", DIV_P1_PG, 0x866, 0x867, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_P4_PG3, "clk_p4_pg3", DIV_P4_PG, 0x824, 0x825, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_P4_PG4, "clk_p4_pg4", DIV_P4_PG, 0x826, 0x827, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_P6_PG1, "clk_p6_pg1", DIV_P6_PG, 0x8a0, 0x8a1, 0x8a2, 0, 0xb60, 0, 0),
> +	D_GATE(CLK_P6_PG2, "clk_p6_pg2", DIV_P6_PG, 0x8a3, 0x8a4, 0x8a5, 0, 0xb61, 0, 0),
> +	D_GATE(CLK_P6_PG3, "clk_p6_pg3", DIV_P6_PG, 0x8a6, 0x8a7, 0x8a8, 0, 0xb62, 0, 0),
> +	D_GATE(CLK_P6_PG4, "clk_p6_pg4", DIV_P6_PG, 0x8a9, 0x8aa, 0x8ab, 0, 0xb63, 0, 0),
> +	D_MODULE(CLK_PCI_USB, "clk_pci_usb", CLKOUT_D40, 0xe6, 0, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_QSPI0, "clk_qspi0", DIV_QSPI0, 0x2a4, 0x2a5, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_QSPI1, "clk_qspi1", DIV_QSPI1, 0x484, 0x485, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_RGMII_REF, "clk_rgmii_ref", CLKOUT_D8, 0x340, 0, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_RMII_REF, "clk_rmii_ref", CLKOUT_D20, 0x341, 0, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_SDIO0, "clk_sdio0", DIV_SDIO0, 0x64, 0, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_SDIO1, "clk_sdio1", DIV_SDIO1, 0x644, 0, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_SERCOS100, "clk_sercos100", CLKOUT_D10, 0x425, 0, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_SLCD, "clk_slcd", DIV_P1_PG, 0x860, 0x861, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_SPI0, "clk_spi0", DIV_P3_PG, 0x7e0, 0x7e1, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_SPI1, "clk_spi1", DIV_P3_PG, 0x7e2, 0x7e3, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_SPI2, "clk_spi2", DIV_P3_PG, 0x7e4, 0x7e5, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_SPI3, "clk_spi3", DIV_P3_PG, 0x7e6, 0x7e7, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_SPI4, "clk_spi4", DIV_P4_PG, 0x820, 0x821, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_SPI5, "clk_spi5", DIV_P4_PG, 0x822, 0x823, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_SWITCH, "clk_switch", DIV_SWITCH, 0x982, 0x983, 0, 0, 0, 0, 0),
> +	D_DIV(DIV_MOTOR, "div_motor", CLKOUT_D5, 84, 2, 8),
> +	D_MODULE(HCLK_ECAT125, "hclk_ecat125", CLKOUT_D8, 0x400, 0x401, 0, 0x402, 0, 0x440, 0x441),
> +	D_MODULE(HCLK_PINCONFIG, "hclk_pinconfig", CLKOUT_D40, 0x740, 0x741, 0x742, 0, 0xae0, 0, 0),
> +	D_MODULE(HCLK_SERCOS, "hclk_sercos", CLKOUT_D10, 0x420, 0x422, 0, 0x421, 0, 0x460, 0x461),
> +	D_MODULE(HCLK_SGPIO2, "hclk_sgpio2", DIV_P5_PG, 0x8c3, 0x8c4, 0x8c5, 0, 0xb41, 0, 0),
> +	D_MODULE(HCLK_SGPIO3, "hclk_sgpio3", DIV_P5_PG, 0x8c6, 0x8c7, 0x8c8, 0, 0xb42, 0, 0),
> +	D_MODULE(HCLK_SGPIO4, "hclk_sgpio4", DIV_P5_PG, 0x8c9, 0x8ca, 0x8cb, 0, 0xb43, 0, 0),
> +	D_MODULE(HCLK_TIMER0, "hclk_timer0", CLKOUT_D40, 0x743, 0x744, 0x745, 0, 0xae1, 0, 0),
> +	D_MODULE(HCLK_TIMER1, "hclk_timer1", CLKOUT_D40, 0x746, 0x747, 0x748, 0, 0xae2, 0, 0),
> +	D_MODULE(HCLK_USBF, "hclk_usbf", CLKOUT_D8, 0xe3, 0, 0, 0xe4, 0, 0x102, 0x103),
> +	D_MODULE(HCLK_USBH, "hclk_usbh", CLKOUT_D8, 0xe0, 0xe1, 0, 0xe2, 0, 0x100, 0x101),
> +	D_MODULE(HCLK_USBPM, "hclk_usbpm", CLKOUT_D8, 0xe5, 0, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_48_PG_F, "clk_48_pg_f", CLK_48, 0x78c, 0x78d, 0, 0x78e, 0, 0xb04, 0xb05),
> +	D_GATE(CLK_48_PG4, "clk_48_pg4", CLK_48, 0x789, 0x78a, 0x78b, 0, 0xb03, 0, 0),
> +	D_FFC(CLK_DDRPHY_PLLCLK_D4, "clk_ddrphy_pllclk_d4", CLK_DDRPHY_PLLCLK, 4),
> +	D_FFC(CLK_ECAT100_D4, "clk_ecat100_d4", CLK_ECAT100, 4),
> +	D_FFC(CLK_HSR100_D2, "clk_hsr100_d2", CLK_HSR100, 2),
> +	D_FFC(CLK_REF_SYNC_D4, "clk_ref_sync_d4", CLK_REF_SYNC, 4),
> +	D_FFC(CLK_REF_SYNC_D8, "clk_ref_sync_d8", CLK_REF_SYNC, 8),
> +	D_FFC(CLK_SERCOS100_D2, "clk_sercos100_d2", CLK_SERCOS100, 2),
> +	D_DIV(DIV_CA7, "div_ca7", CLK_REF_SYNC, 57, 1, 4, 1, 2, 4),
> +	D_MODULE(HCLK_CAN0, "hclk_can0", CLK_48, 0x783, 0x784, 0x785, 0, 0xb01, 0, 0),
> +	D_MODULE(HCLK_CAN1, "hclk_can1", CLK_48, 0x786, 0x787, 0x788, 0, 0xb02, 0, 0),
> +	D_MODULE(HCLK_DELTASIGMA, "hclk_deltasigma", DIV_MOTOR, 0x1ef, 0x1f0, 0x1f1, 0, 0, 0, 0),
> +	D_MODULE(HCLK_PWMPTO, "hclk_pwmpto", DIV_MOTOR, 0x1ec, 0x1ed, 0x1ee, 0, 0, 0, 0),
> +	D_MODULE(HCLK_RSV, "hclk_rsv", CLK_48, 0x780, 0x781, 0x782, 0, 0xb00, 0, 0),
> +	D_MODULE(HCLK_SGPIO0, "hclk_sgpio0", DIV_MOTOR, 0x1e0, 0x1e1, 0x1e2, 0, 0, 0, 0),
> +	D_MODULE(HCLK_SGPIO1, "hclk_sgpio1", DIV_MOTOR, 0x1e3, 0x1e4, 0x1e5, 0, 0, 0, 0),
> +	D_DIV(RTOS_MDC, "rtos_mdc", CLK_REF_SYNC, 100, 80, 640, 80, 160, 320, 640),
> +	D_GATE(CLK_CM3, "clk_cm3", CLK_REF_SYNC_D4, 0xba0, 0xba1, 0, 0xba2, 0, 0xbc0, 0xbc1),
> +	D_GATE(CLK_DDRC, "clk_ddrc", CLK_DDRPHY_PLLCLK_D4, 0x323, 0x324, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_ECAT25, "clk_ecat25", CLK_ECAT100_D4, 0x403, 0x404, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_HSR50, "clk_hsr50", CLK_HSR100_D2, 0x484, 0x485, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_HW_RTOS, "clk_hw_rtos", CLK_REF_SYNC_D4, 0xc60, 0xc61, 0, 0, 0, 0, 0),
> +	D_GATE(CLK_SERCOS50, "clk_sercos50", CLK_SERCOS100_D2, 0x424, 0x423, 0, 0, 0, 0, 0),
> +	D_MODULE(HCLK_ADC, "hclk_adc", CLK_REF_SYNC_D8, 0x1af, 0x1b0, 0x1b1, 0, 0, 0, 0),
> +	D_MODULE(HCLK_CM3, "hclk_cm3", CLK_REF_SYNC_D4, 0xc20, 0xc21, 0xc22, 0, 0, 0, 0),
> +	D_MODULE(HCLK_CRYPTO_EIP150, "hclk_crypto_eip150", CLK_REF_SYNC_D4, 0x123, 0x124, 0x125, 0, 0x142, 0, 0),
> +	D_MODULE(HCLK_CRYPTO_EIP93, "hclk_crypto_eip93", CLK_REF_SYNC_D4, 0x120, 0x121, 0, 0x122, 0, 0x140, 0x141),
> +	D_MODULE(HCLK_DDRC, "hclk_ddrc", CLK_REF_SYNC_D4, 0x320, 0x322, 0, 0x321, 0, 0x3a0, 0x3a1),
> +	D_MODULE(HCLK_DMA0, "hclk_dma0", CLK_REF_SYNC_D4, 0x260, 0x261, 0x262, 0x263, 0x2c0, 0x2c1, 0x2c2),
> +	D_MODULE(HCLK_DMA1, "hclk_dma1", CLK_REF_SYNC_D4, 0x264, 0x265, 0x266, 0x267, 0x2c3, 0x2c4, 0x2c5),
> +	D_MODULE(HCLK_GMAC0, "hclk_gmac0", CLK_REF_SYNC_D4, 0x360, 0x361, 0x362, 0x363, 0x3c0, 0x3c1, 0x3c2),
> +	D_MODULE(HCLK_GMAC1, "hclk_gmac1", CLK_REF_SYNC_D4, 0x380, 0x381, 0x382, 0x383, 0x3e0, 0x3e1, 0x3e2),
> +	D_MODULE(HCLK_GPIO0, "hclk_gpio0", CLK_REF_SYNC_D4, 0x212, 0x213, 0x214, 0, 0, 0, 0),
> +	D_MODULE(HCLK_GPIO1, "hclk_gpio1", CLK_REF_SYNC_D4, 0x215, 0x216, 0x217, 0, 0, 0, 0),
> +	D_MODULE(HCLK_GPIO2, "hclk_gpio2", CLK_REF_SYNC_D4, 0x229, 0x22a, 0x22b, 0, 0, 0, 0),
> +	D_MODULE(HCLK_HSR, "hclk_hsr", CLK_HSR100_D2, 0x480, 0x482, 0, 0x481, 0, 0x4c0, 0x4c1),
> +	D_MODULE(HCLK_I2C0, "hclk_i2c0", CLK_REF_SYNC_D8, 0x1a9, 0x1aa, 0x1ab, 0, 0, 0, 0),
> +	D_MODULE(HCLK_I2C1, "hclk_i2c1", CLK_REF_SYNC_D8, 0x1ac, 0x1ad, 0x1ae, 0, 0, 0, 0),
> +	D_MODULE(HCLK_LCD, "hclk_lcd", CLK_REF_SYNC_D4, 0x7a0, 0x7a1, 0x7a2, 0, 0xb20, 0, 0),
> +	D_MODULE(HCLK_MSEBI_M, "hclk_msebi_m", CLK_REF_SYNC_D4, 0x164, 0x165, 0x166, 0, 0x183, 0, 0),
> +	D_MODULE(HCLK_MSEBI_S, "hclk_msebi_s", CLK_REF_SYNC_D4, 0x160, 0x161, 0x162, 0x163, 0x180, 0x181, 0x182),
> +	D_MODULE(HCLK_NAND, "hclk_nand", CLK_REF_SYNC_D4, 0x280, 0x281, 0x282, 0x283, 0x2e0, 0x2e1, 0x2e2),
> +	D_MODULE(HCLK_PG_I, "hclk_pg_i", CLK_REF_SYNC_D4, 0x7ac, 0x7ad, 0, 0x7ae, 0, 0xb24, 0xb25),
> +	D_MODULE(HCLK_PG19, "hclk_pg19", CLK_REF_SYNC_D4, 0x22c, 0x22d, 0x22e, 0, 0, 0, 0),
> +	D_MODULE(HCLK_PG20, "hclk_pg20", CLK_REF_SYNC_D4, 0x22f, 0x230, 0x231, 0, 0, 0, 0),
> +	D_MODULE(HCLK_PG3, "hclk_pg3", CLK_REF_SYNC_D4, 0x7a6, 0x7a7, 0x7a8, 0, 0xb22, 0, 0),
> +	D_MODULE(HCLK_PG4, "hclk_pg4", CLK_REF_SYNC_D4, 0x7a9, 0x7aa, 0x7ab, 0, 0xb23, 0, 0),
> +	D_MODULE(HCLK_QSPI0, "hclk_qspi0", CLK_REF_SYNC_D4, 0x2a0, 0x2a1, 0x2a2, 0x2a3, 0x300, 0x301, 0x302),
> +	D_MODULE(HCLK_QSPI1, "hclk_qspi1", CLK_REF_SYNC_D4, 0x480, 0x481, 0x482, 0x483, 0x4c0, 0x4c1, 0x4c2),
> +	D_MODULE(HCLK_ROM, "hclk_rom", CLK_REF_SYNC_D4, 0xaa0, 0xaa1, 0xaa2, 0, 0xb80, 0, 0),
> +	D_MODULE(HCLK_RTC, "hclk_rtc", CLK_REF_SYNC_D8, 0xa00, 0, 0, 0, 0, 0, 0),
> +	D_MODULE(HCLK_SDIO0, "hclk_sdio0", CLK_REF_SYNC_D4, 0x60, 0x61, 0x62, 0x63, 0x80, 0x81, 0x82),
> +	D_MODULE(HCLK_SDIO1, "hclk_sdio1", CLK_REF_SYNC_D4, 0x640, 0x641, 0x642, 0x643, 0x660, 0x661, 0x662),
> +	D_MODULE(HCLK_SEMAP, "hclk_semap", CLK_REF_SYNC_D4, 0x7a3, 0x7a4, 0x7a5, 0, 0xb21, 0, 0),
> +	D_MODULE(HCLK_SPI0, "hclk_spi0", CLK_REF_SYNC_D4, 0x200, 0x201, 0x202, 0, 0, 0, 0),
> +	D_MODULE(HCLK_SPI1, "hclk_spi1", CLK_REF_SYNC_D4, 0x203, 0x204, 0x205, 0, 0, 0, 0),
> +	D_MODULE(HCLK_SPI2, "hclk_spi2", CLK_REF_SYNC_D4, 0x206, 0x207, 0x208, 0, 0, 0, 0),
> +	D_MODULE(HCLK_SPI3, "hclk_spi3", CLK_REF_SYNC_D4, 0x209, 0x20a, 0x20b, 0, 0, 0, 0),
> +	D_MODULE(HCLK_SPI4, "hclk_spi4", CLK_REF_SYNC_D4, 0x20c, 0x20d, 0x20e, 0, 0, 0, 0),
> +	D_MODULE(HCLK_SPI5, "hclk_spi5", CLK_REF_SYNC_D4, 0x20f, 0x210, 0x211, 0, 0, 0, 0),
> +	D_MODULE(HCLK_SWITCH, "hclk_switch", CLK_REF_SYNC_D4, 0x980, 0, 0x981, 0, 0, 0, 0),
> +	D_MODULE(HCLK_SWITCH_RG, "hclk_switch_rg", CLK_REF_SYNC_D4, 0xc40, 0xc41, 0xc42, 0, 0, 0, 0),
> +	D_MODULE(HCLK_UART0, "hclk_uart0", CLK_REF_SYNC_D8, 0x1a0, 0x1a1, 0x1a2, 0, 0, 0, 0),
> +	D_MODULE(HCLK_UART1, "hclk_uart1", CLK_REF_SYNC_D8, 0x1a3, 0x1a4, 0x1a5, 0, 0, 0, 0),
> +	D_MODULE(HCLK_UART2, "hclk_uart2", CLK_REF_SYNC_D8, 0x1a6, 0x1a7, 0x1a8, 0, 0, 0, 0),
> +	D_MODULE(HCLK_UART3, "hclk_uart3", CLK_REF_SYNC_D4, 0x218, 0x219, 0x21a, 0, 0, 0, 0),
> +	D_MODULE(HCLK_UART4, "hclk_uart4", CLK_REF_SYNC_D4, 0x21b, 0x21c, 0x21d, 0, 0, 0, 0),
> +	D_MODULE(HCLK_UART5, "hclk_uart5", CLK_REF_SYNC_D4, 0x220, 0x221, 0x222, 0, 0, 0, 0),
> +	D_MODULE(HCLK_UART6, "hclk_uart6", CLK_REF_SYNC_D4, 0x223, 0x224, 0x225, 0, 0, 0, 0),
> +	D_MODULE(HCLK_UART7, "hclk_uart7", CLK_REF_SYNC_D4, 0x226, 0x227, 0x228, 0, 0, 0, 0),
> +	/*
> +	 * These are not hardware clocks, but are needed to handle the special
> +	 * case where we have a 'selector bit' that doesn't just change the
> +	 * parent for a clock, but also the gate it's supposed to use.
> +	 */
> +	{
> +		.index = R9A06G032_UART_GROUP_012,
> +		.name = "uart_group_012",
> +		.type = K_BITSEL,
> +		.source = 1 + R9A06G032_DIV_UART,
> +		/* R9A06G032_SYSCTRL_REG_PWRCTRL_PG0_0 */
> +		.dual.sel = ((0x34 / 4) << 5) | 30,
> +		.dual.group = 0,
> +	},
> +	{
> +		.index = R9A06G032_UART_GROUP_34567,
> +		.name = "uart_group_34567",
> +		.type = K_BITSEL,
> +		.source = 1 + R9A06G032_DIV_P2_PG,
> +		/* R9A06G032_SYSCTRL_REG_PWRCTRL_PG1_PR2 */
> +		.dual.sel = ((0xec / 4) << 5) | 24,
> +		.dual.group = 1,
> +	},
> +	D_UGATE(CLK_UART0, "clk_uart0", UART_GROUP_012, 0, 0x1b2, 0x1b3, 0x1b4, 0x1b5),
> +	D_UGATE(CLK_UART1, "clk_uart1", UART_GROUP_012, 0, 0x1b6, 0x1b7, 0x1b8, 0x1b9),
> +	D_UGATE(CLK_UART2, "clk_uart2", UART_GROUP_012, 0, 0x1ba, 0x1bb, 0x1bc, 0x1bd),
> +	D_UGATE(CLK_UART3, "clk_uart3", UART_GROUP_34567, 1, 0x760, 0x761, 0x762, 0x763),
> +	D_UGATE(CLK_UART4, "clk_uart4", UART_GROUP_34567, 1, 0x764, 0x765, 0x766, 0x767),
> +	D_UGATE(CLK_UART5, "clk_uart5", UART_GROUP_34567, 1, 0x768, 0x769, 0x76a, 0x76b),
> +	D_UGATE(CLK_UART6, "clk_uart6", UART_GROUP_34567, 1, 0x76c, 0x76d, 0x76e, 0x76f),
> +	D_UGATE(CLK_UART7, "clk_uart7", UART_GROUP_34567, 1, 0x770, 0x771, 0x772, 0x773),
> +};
> +
> +struct r9a06g032_priv {
> +	struct regmap		*regmap;
> +	struct clk		mclk;
> +};
> +
> +static const struct r9a06g032_clkdesc *r9a06g032_clk_get(struct clk *clk)
> +{
> +	const unsigned long clkid = clk->id & 0xffff;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(r9a06g032_clocks); i++) {
> +		if (r9a06g032_clocks[i].index == clkid)
> +			return &r9a06g032_clocks[i];
> +	}
> +
> +	return NULL;
> +}
> +
> +static int r9a06g032_clk_get_parent(struct clk *clk, struct clk *parent)
> +{
> +	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
> +
> +	if (!desc)
> +		return -ENODEV;

ENOENT please

see https://u-boot.readthedocs.io/en/latest/develop/driver-model/design.html#error-codes

> +
> +	if (desc->source == 0)

if (!desc->source)

although I would reverse the clauses to make the condition clearer

> +		parent->id = ~0;	/* Top-level clock */

Can you use a define for this (instead of referring to ~0 everywhere)

> +	else
> +		parent->id = desc->source - 1;
> +
> +	parent->dev = clk->dev;

I think you need to clk_request here.

> +	return 0;
> +}
> +
> +static ulong r9a06g032_clk_get_parent_rate(struct clk *clk)
> +{
> +	struct clk parent;
> +
> +	if (r9a06g032_clk_get_parent(clk, &parent)) {
> +		debug("Failed to get parent clock for id=%lu\b", clk->id);

dev_dbg please

> +		return 0;

Return -ENOENT here please. You can check for this with IS_ERR.

> +	}
> +
> +	if (parent.id == ~0) {
> +		struct r9a06g032_priv *clocks = dev_get_priv(clk->dev);
> +		ulong rate = clk_get_rate(&clocks->mclk);

You need a newline here

> +		return rate;
> +	}
> +
> +	return clk_get_rate(&parent);
> +}
> +
> +/* register/bit pairs are encoded as an uint16_t */
> +static void
> +clk_rdesc_set(struct r9a06g032_priv *clocks,
> +	      u16 one, unsigned int on)
> +{
> +	uint offset = 4 * (one >> 5);
> +	uint mask = 1U << (one & 0x1f);
> +	uint val = ((!!on) << (one & 0x1f));

Please either use bitfields for this, or use FIELD_GET() and friends.

> +	regmap_update_bits(clocks->regmap, offset, mask, val);
> +}
> +
> +static int
> +clk_rdesc_get(struct r9a06g032_priv *clocks,
> +	      uint16_t one)

I think this all fits on one line?

> +{
> +	uint offset = 4 * (one >> 5);
> +	u32 val = 0;
> +
> +	regmap_read(clocks->regmap, offset, &val);
> +
> +	return !!(val & (1U << (one & 0x1f)));
> +}
> +
> +/*
> + * Cheating a little bit here: leverage the existing code to control the
> + * per-clock reset. It should really be handled by a reset controller instead.
> + */
> +void clk_rzn1_reset_state(struct clk *clk, int on)
> +{
> +	struct r9a06g032_priv *clocks = dev_get_priv(clk->dev);
> +	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
> +	assert(desc);
> +	assert(desc->type == K_GATE);
> +	const struct r9a06g032_gate *g = &desc->gate;
> +	assert(g->reset);

Please order declarations all at the beginning. In this case, you will need to do something like

	struct r9a06g032_priv *clocks = dev_get_priv(clk->dev);
	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
	const struct r9a06g032_gate *g;

	assert(desc);
	assert(desc->type == K_GATE);
	g = &desc->gate
	assert(g->reset);

> +	clk_rdesc_set(clocks, g->reset, on);
> +}
> +
> +/*
> + * This implements the R9A06G032 clock gate 'driver'. We cannot use the system's
> + * clock gate framework as the gates on the R9A06G032 have a special enabling
> + * sequence, therefore we use this little proxy.
> + */
> +static int r9a06g032_clk_gate_set(struct clk *clk, int on)
> +{
> +	struct r9a06g032_priv *clocks = dev_get_priv(clk->dev);
> +	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
> +	assert(desc);
> +	assert(desc->type == K_GATE);
> +	const struct r9a06g032_gate *g = &desc->gate;

ditto

> +	clk_rdesc_set(clocks, g->gate, on);
> +	/* De-assert reset */
> +	if (g->reset)
> +		clk_rdesc_set(clocks, g->reset, 1);
> +
> +	/* Hardware manual recommends 5us delay after enabling clock & reset */
> +	udelay(5);
> +
> +	/* If the peripheral is memory mapped (i.e. an AXI slave), there is an
> +	 * associated SLVRDY bit in the System Controller that needs to be set
> +	 * so that the FlexWAY bus fabric passes on the read/write requests.
> +	 */
> +	if (g->ready || g->midle) {
> +		if (g->ready)
> +			clk_rdesc_set(clocks, g->ready, on);
> +		/* Clear 'Master Idle Request' bit */
> +		if (g->midle)
> +			clk_rdesc_set(clocks, g->midle, !on);
> +	}
> +	/* Note: We don't wait for FlexWAY Socket Connection signal */
> +
> +	return 0;
> +}
> +
> +static int r9a06g032_clk_gate_enable(struct clk *clk)
> +{
> +	return r9a06g032_clk_gate_set(clk, 1);
> +}
> +
> +static int r9a06g032_clk_gate_disable(struct clk *clk)
> +{
> +	return r9a06g032_clk_gate_set(clk, 0);
> +}
> +
> +/*
> + * Fixed factor clock
> + */
> +static ulong r9a06g032_ffc_get_rate(struct clk *clk)
> +{
> +	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
> +	unsigned long parent_rate = r9a06g032_clk_get_parent_rate(clk);
> +	unsigned long long rate;
> +
> +	if (parent_rate == 0) {
> +		debug("%s: parent_rate is zero\n", __func__);
> +		return 0;
> +	}
> +
> +	rate = (unsigned long long)parent_rate * desc->mul;
> +	rate = DIV_ROUND_UP(rate, desc->div);
> +	return (ulong)rate;
> +}
> +
> +/*
> + * This implements R9A06G032 clock divider 'driver'. This differs from the
> + * standard clk_divider because the set_rate method must also set b[31] to
> + * trigger the hardware rate change. In theory it should also wait for this
> + * bit to clear.
> + */
> +static ulong r9a06g032_div_get_rate(struct clk *clk)
> +{
> +	struct r9a06g032_priv *clocks = dev_get_priv(clk->dev);
> +	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
> +	unsigned long parent_rate = r9a06g032_clk_get_parent_rate(clk);
> +	u32 div = 0;
> +
> +	if (parent_rate == 0) {
> +		debug("%s: parent_rate is zero\n", __func__);

Didn't you already log this?

> +		return 0;
> +	}
> +
> +	regmap_read(clocks->regmap, 4 * desc->reg, &div);
> +
> +	if (div < desc->div_min)
> +		div = desc->div_min;
> +	else if (div > desc->div_max)
> +		div = desc->div_max;
> +	return DIV_ROUND_UP(parent_rate, div);

DIV_ROUND_CLOSEST?

> +}
> +
> +static ulong r9a06g032_div_set_rate(struct clk *clk, ulong rate)
> +{
> +	struct r9a06g032_priv *clocks = dev_get_priv(clk->dev);
> +	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
> +	unsigned long parent_rate = r9a06g032_clk_get_parent_rate(clk);
> +
> +	if (parent_rate == 0) {
> +		debug("%s: parent_rate is zero\n", __func__);
> +		return 0;
> +	}
> +
> +	/* + 1 to cope with rates that have the remainder dropped */
> +	u32 div = DIV_ROUND_UP(parent_rate, rate + 1);
> +
> +	/* Clamp to allowable range */
> +	if (div < desc->div_min)
> +		div = desc->div_min;
> +	else if (div > desc->div_max)
> +		div = desc->div_max;
> +
> +	/* TODO: use the .div_table if provided */
> +	if (desc->div_table[0])
> +		pr_err("ERROR: %s: div_table not implemented\n", __func__);

dev_err

But can't you just leave out the div_table member?

> +
> +	pr_devel("%s clkid %lu rate %ld parent %ld div %d\n", __func__, clk->id,
> +		 rate, parent_rate, div);

dev_dbg

> +
> +	/*
> +	 * Need to write the bit 31 with the divider value to
> +	 * latch it. Technically we should wait until it has been
> +	 * cleared too.
> +	 * TODO: Find whether this callback is sleepable, in case
> +	 * the hardware /does/ require some sort of spinloop here.
> +	 */
> +	regmap_write(clocks->regmap, 4 * desc->reg, div | BIT(31));
> +
> +	return 0;
> +}
> +
> +/*
> + * Dual gate. This handles toggling the approprate clock/reset bits,
> + * which depends on the mux setting above.
> + */
> +static int r9a06g032_clk_dualgate_setenable(struct r9a06g032_priv *clocks,
> +					    const struct r9a06g032_clkdesc *desc,
> +					    int enable)
> +{
> +	u8 sel_bit = clk_rdesc_get(clocks, desc->dual.sel);
> +	u16 gate[2] = { desc->dual.g1, desc->dual.g2 };
> +	u16 reset[2] = { desc->dual.r1, desc->dual.r2 };
> +
> +	/* we always turn off the 'other' gate, regardless */
> +	clk_rdesc_set(clocks, gate[!sel_bit], 0);
> +	if (reset[!sel_bit])
> +		clk_rdesc_set(clocks, reset[!sel_bit], 1);
> +
> +	/* set the gate as requested */
> +	clk_rdesc_set(clocks, gate[sel_bit], enable);
> +	if (reset[sel_bit])
> +		clk_rdesc_set(clocks, reset[sel_bit], 1);
> +
> +	return 0;
> +}
> +
> +static int r9a06g032_clk_dualgate_enable(struct clk *clk)
> +{
> +	struct r9a06g032_priv *clocks = dev_get_priv(clk->dev);
> +	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
> +
> +	return r9a06g032_clk_dualgate_setenable(clocks, desc, 1);
> +}
> +
> +static int r9a06g032_clk_dualgate_disable(struct clk *clk)
> +{
> +	struct r9a06g032_priv *clocks = dev_get_priv(clk->dev);
> +	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
> +
> +	return r9a06g032_clk_dualgate_setenable(clocks, desc, 0);
> +}
> +
> +static int r9a06g032_clk_dualgate_is_enabled(struct clk *clk)
> +{
> +	struct r9a06g032_priv *clocks = dev_get_priv(clk->dev);
> +	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
> +	u8 sel_bit = clk_rdesc_get(clocks, desc->dual.sel);
> +	u16 gate[2] = { desc->dual.g1, desc->dual.g2 };
> +
> +	return clk_rdesc_get(clocks, gate[sel_bit]);
> +}
> +
> +/*
> + * Main clock driver
> + */
> +static int r9a06g032_clk_enable(struct clk *clk)
> +{
> +	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
> +
> +	switch (desc->type) {
> +	case K_GATE:
> +		return r9a06g032_clk_gate_enable(clk);
> +	case K_DUALGATE:
> +		return r9a06g032_clk_dualgate_enable(clk);
> +	default:
> +		printf("ERROR: %s:%d unhandled type=%d\n", __func__, __LINE__, desc->type);

Assert or dev_dbg is better here. This is "impossible" so we try and
avoid increasing image size in these cases.

> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int r9a06g032_clk_disable(struct clk *clk)
> +{
> +	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
> +
> +	switch (desc->type) {
> +	case K_GATE:
> +		return r9a06g032_clk_gate_disable(clk);
> +	case K_DUALGATE:
> +		return r9a06g032_clk_dualgate_disable(clk);
> +	default:
> +		printf("ERROR: %s:%d unhandled type=%d\n", __func__, __LINE__, desc->type);
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static ulong r9a06g032_clk_get_rate(struct clk *clk)
> +{
> +	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
> +	ulong ret = 0;
> +
> +	assert(desc);
> +
> +	switch (desc->type) {
> +	case K_FFC:
> +		ret = r9a06g032_ffc_get_rate(clk);
> +		break;
> +	case K_GATE:
> +		ret = r9a06g032_clk_get_parent_rate(clk);
> +		break;
> +	case K_DIV:
> +		ret = r9a06g032_div_get_rate(clk);
> +		break;
> +	case K_BITSEL:
> +		/*
> +		 * Look at the mux to determine parent.
> +		 * 0 means it is coming from UART DIV (group 012 or 34567)
> +		 * 1 means it is coming from USB_PLL
> +		 */
> +		if (r9a06g032_clk_dualgate_is_enabled(clk)) {
> +			struct clk clk = { .id = R9A06G032_CLK_PLL_USB };
> +			ret = r9a06g032_clk_get_parent_rate(&clk);
> +		}
> +		ret = r9a06g032_clk_get_parent_rate(clk);
> +		break;
> +	case K_DUALGATE:
> +		ret = r9a06g032_clk_get_parent_rate(clk);
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static ulong r9a06g032_clk_set_rate(struct clk *clk, ulong rate)
> +{
> +	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
> +	ulong ret = 0;
> +
> +	assert(desc);
> +
> +	switch (desc->type) {
> +	case K_DIV:
> +		ret = r9a06g032_div_set_rate(clk, rate);
> +		break;
> +	default:
> +		printf("ERROR: %s:%d not implemented yet\n", __func__, __LINE__);
> +	};
> +
> +	return ret;
> +}
> +
> +static int r9a06g032_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
> +{
> +	if (args->args_count != 1) {
> +		debug("Invalid args_count: %d\n", args->args_count);
> +		return -EINVAL;
> +	}
> +
> +	clk->id = args->args[0];
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops r9a06g032_clk_ops = {
> +	.enable		= r9a06g032_clk_enable,
> +	.disable	= r9a06g032_clk_disable,
> +	.get_rate	= r9a06g032_clk_get_rate,
> +	.set_rate	= r9a06g032_clk_set_rate,
> +	.of_xlate	= r9a06g032_clk_of_xlate,
> +};
> +
> +static int r9a06g032_clk_probe(struct udevice *dev)
> +{
> +	struct r9a06g032_priv *priv = dev_get_priv(dev);
> +	int err;
> +
> +	priv->regmap = syscon_regmap_lookup_by_phandle(dev, "regmap");
> +	if (!priv->regmap) {

IS_ERR(priv->regmap)

> +		pr_err("unable to find regmap\n");

dev_dbg

> +		return -ENODEV;

return ERR_PTR(priv->regmap)

> +	}
> +
> +	/* Enable S/W reset */
> +	regmap_write(priv->regmap, 0x120, 0x41);
> +
> +	err = clk_get_by_name(dev, "mclk", &priv->mclk);
> +	if (err)
> +		return err;
> +
> +	return 0;
> +}
> +
> +static int r9a06g032_clk_remove(struct udevice *dev)
> +{
> +	return 0;
> +}

Not necessary.

> +
> +static const struct udevice_id r9a06g032_clk_ids[] = {
> +	{ .compatible = "renesas,r9a06g032-sysctrl" },
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(clk_r9a06g032) = {
> +	.name		= "clk_r9a06g032",
> +	.id		= UCLASS_CLK,
> +	.of_match	= r9a06g032_clk_ids,
> +	.priv_auto	= sizeof(struct r9a06g032_priv),
> +	.ops		= &r9a06g032_clk_ops,
> +	.probe		= &r9a06g032_clk_probe,
> +	.remove		= &r9a06g032_clk_remove,
> +	.flags		= DM_FLAG_PRE_RELOC,
> +};
> 

The overall structure looks good; most of these things you
should be able to iron out fairly easily.

--Sean

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v2 9/9] tools: spkgimage: add Renesas SPKG format
  2022-08-12 17:03   ` [RFC PATCH v2 9/9] tools: spkgimage: add Renesas SPKG format Ralph Siemsen
@ 2022-08-13 14:47     ` Sean Anderson
  2022-08-14  1:45       ` Ralph Siemsen
  0 siblings, 1 reply; 29+ messages in thread
From: Sean Anderson @ 2022-08-13 14:47 UTC (permalink / raw)
  To: Ralph Siemsen
  Cc: andre.przywara, heiko.thiery, pali, samuel, sjg, sr,
	takahiro.akashi, u-boot, sean.anderson

On 8/12/22 1:03 PM, Ralph Siemsen wrote:
> Renesas RZ/N1 devices contain BootROM code that loads a custom SPKG
> image from QSPI, NAND or USB DFU. Support this format in mkimage tool.
> 
> SPKGs can optionally be signed, however creation of signed SPKG is not
> currently supported.
> 
> Example of how to use it:
> 
> tools/mkimage -n board/schneider/lces/spkgimage.cfg \
> 	-T spkgimage -a 0x20040000 -e 0x20040000 \
> 	-d u-boot.bin u-boot.bin.spkg
> 
> The config file (spkgimage.cfg in this example) contains additional
> parameters such as NAND ECC settings.
> 
> Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
> ---
> 
> Changes in v2:
> - rewrote the stand-alone spkg_utility to integrate into mkimage
> 
>   board/schneider/lces/spkgimage.cfg |  26 +++
>   boot/image.c                       |   1 +
>   include/image.h                    |   1 +
>   tools/Makefile                     |   1 +
>   tools/spkgimage.c                  | 303 +++++++++++++++++++++++++++++
>   tools/spkgimage.h                  |  39 ++++

Please document your format in doc/mkimage.1

>   6 files changed, 371 insertions(+)
>   create mode 100644 board/schneider/lces/spkgimage.cfg
>   create mode 100644 tools/spkgimage.c
>   create mode 100644 tools/spkgimage.h
> 
> diff --git a/board/schneider/lces/spkgimage.cfg b/board/schneider/lces/spkgimage.cfg
> new file mode 100644
> index 0000000000..b5faf96b00
> --- /dev/null
> +++ b/board/schneider/lces/spkgimage.cfg
> @@ -0,0 +1,26 @@
> +# SPDX-License-Identifier: GPL-2.0+
> +#
> +# (C) Copyright 2022 Schneider Electric
> +#
> +# SPKG image header, for booting on RZ/N1
> +
> +# b[35:32] SPKG version
> +VERSION			1
> +
> +# b[42:41]  ECC Block size: 0=256 bytes, 1=512 bytes, 2=1024 bytes
> +NAND_ECC_BLOCK_SIZE	1
> +
> +# b[45]     NAND enable (boolean)
> +NAND_ECC_ENABLE		1
> +
> +# b[50:48]  ECC Scheme: 0=BCH2 1=BCH4 2=BCH8 3=BCH16 4=BCH24 5=BCH32
> +NAND_ECC_SCHEME		3
> +
> +# b[63:56]  ECC bytes per block
> +NAND_BYTES_PER_ECC_BLOCK 28
> +
> +# Provide dummy BLp header (boolean)
> +ADD_DUMMY_BLP		1
> +
> +# Pad the image to a multiple of
> +PADDING			64K
> diff --git a/boot/image.c b/boot/image.c
> index 5dcb55ba46..7d50d708ab 100644
> --- a/boot/image.c
> +++ b/boot/image.c
> @@ -179,6 +179,7 @@ static const table_entry_t uimage_type[] = {
>   	{	IH_TYPE_COPRO, "copro", "Coprocessor Image"},
>   	{	IH_TYPE_SUNXI_EGON, "sunxi_egon",  "Allwinner eGON Boot Image" },
>   	{	IH_TYPE_SUNXI_TOC0, "sunxi_toc0",  "Allwinner TOC0 Boot Image" },
> +	{	IH_TYPE_RENESAS_SPKG, "spkgimage", "Renesas SPKG Image" },
>   	{	-1,		    "",		  "",			},
>   };
>   
> diff --git a/include/image.h b/include/image.h
> index e4c6a50b88..98eaa384f4 100644
> --- a/include/image.h
> +++ b/include/image.h
> @@ -229,6 +229,7 @@ enum {
>   	IH_TYPE_COPRO,			/* Coprocessor Image for remoteproc*/
>   	IH_TYPE_SUNXI_EGON,		/* Allwinner eGON Boot Image */
>   	IH_TYPE_SUNXI_TOC0,		/* Allwinner TOC0 Boot Image */
> +	IH_TYPE_RENESAS_SPKG,		/* Renesas SPKG image */
>   
>   	IH_TYPE_COUNT,			/* Number of image types */
>   };
> diff --git a/tools/Makefile b/tools/Makefile
> index 005e7362a3..7e24f3ecb9 100644
> --- a/tools/Makefile
> +++ b/tools/Makefile
> @@ -131,6 +131,7 @@ dumpimage-mkimage-objs := aisimage.o \
>   			stm32image.o \
>   			$(ROCKCHIP_OBS) \
>   			socfpgaimage.o \
> +			spkgimage.o \
>   			sunxi_egon.o \
>   			lib/crc16-ccitt.o \
>   			lib/hash-checksum.o \
> diff --git a/tools/spkgimage.c b/tools/spkgimage.c
> new file mode 100644
> index 0000000000..2e8c17d94a
> --- /dev/null
> +++ b/tools/spkgimage.c
> @@ -0,0 +1,303 @@
> +// SPDX-License-Identifier: BSD-2-Clause
> +/*
> + * Generate Renesas RZ/N1 BootROM header (SPKG)
> + * (C) Copyright 2022 Schneider Electric
> + *
> + * Based on spkg_utility.c
> + * (C) Copyright 2016 Renesas Electronics Europe Ltd
> + */
> +
> +#include "imagetool.h"
> +#include <limits.h>
> +#include <image.h>
> +#include <stdarg.h>
> +#include <stdint.h>
> +#include <u-boot/crc.h>
> +#include "spkgimage.h"
> +
> +static struct spkg_file out_buf;
> +
> +static uint32_t padding;
> +
> +/* Note: the ordering of the bitfields does not matter */
> +static struct config_file {
> +	unsigned int version:1;
> +	unsigned int ecc_block_size:2;
> +	unsigned int ecc_enable:1;
> +	unsigned int ecc_scheme:3;
> +	unsigned int ecc_bytes:8;
> +	unsigned int blp_len;
> +	unsigned int padding;
> +} conf;

I wonder if you could just fill in the header directly. This is
for a userspace tool, and this struct will be created at most
once. It's OK to use 10 bytes :)

> +static int spkgimage_parse_config_line(char *line)
> +{
> +	char *saveptr;
> +	char *delim = "\t ";
> +	char *name = strtok_r(line, delim, &saveptr);
> +	char *val_str = strtok_r(NULL, delim, &saveptr);
> +	int value = atoi(val_str);
> +
> +	if (!strcmp("VERSION", name)) {
> +		conf.version = value;
> +	} else if (!strcmp("NAND_ECC_ENABLE", name)) {
> +		conf.ecc_enable = value;

Can you add some checks for the valid range of values? E.g.
NAND_ECC_SCHEME should be 0 <= value <= 5

> +	} else if (!strcmp("NAND_ECC_BLOCK_SIZE", name)) {
> +		conf.ecc_block_size = value;
> +	} else if (!strcmp("NAND_ECC_SCHEME", name)) {
> +		conf.ecc_scheme = value;
> +	} else if (!strcmp("NAND_BYTES_PER_ECC_BLOCK", name)) {
> +		conf.ecc_bytes = value;
> +	} else if (!strcmp("ADD_DUMMY_BLP", name)) {
> +		conf.blp_len = value ? SPKG_BLP_SIZE : 0;
> +	} else if (!strcmp("PADDING", name)) {
> +		if (strrchr(val_str, 'K'))
> +			conf.padding = value * 1024;
> +		else if (strrchr(val_str, 'M'))
> +			conf.padding = value * 1024 * 1024;
> +		else
> +			conf.padding = value;
> +	} else {
> +		fprintf(stderr, "Error: unknown keyword '%s' in config\n",
> +			name);

perhaps print the line number?

> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int spkgimage_parse_config_file(char *filename)
> +{
> +	FILE *fcfg;
> +	char line[256];
> +	size_t len;
> +
> +	fcfg = fopen(filename, "r");
> +	if (!fcfg)
> +		return -EINVAL;
> +
> +	while (fgets(line, sizeof(line), fcfg)) {
> +		/* Skip blank lines and comments */
> +		if (line[0] == '\n' || line[0] == '#')
> +			continue;
> +
> +		/* Strip the trailing newline */
> +		len = strlen(line);
> +		if (line[len - 1] == '\n')
> +			line[--len] = 0;
> +
> +		/* Parse the line */
> +		if (spkgimage_parse_config_line(line))
> +			return -EINVAL;
> +	}
> +
> +	fclose(fcfg);
> +
> +	/* Avoid divide-by-zero later on */
> +	if (conf.padding == 0)

if (!conf.padding)

> +		conf.padding = 1;
> +
> +	return 0;
> +}
> +
> +static int spkgimage_check_params(struct image_tool_params *params)
> +{
> +	if (!params->addr) {
> +		fprintf(stderr, "Error: Load Address must be set.\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!params->imagename || !params->imagename[0]) {
> +		fprintf(stderr, "Error: Image name must be set.\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!params->datafile) {
> +		fprintf(stderr, "Error: Data filename must be set.\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int spkgimage_verify_header(unsigned char *ptr, int size,
> +				   struct image_tool_params *param)
> +{
> +	struct spkg_file *file = (struct spkg_file *)ptr;
> +	struct spkg_hdr *header = (struct spkg_hdr *)ptr;
> +	char signature[4] = SPKG_HEADER_SIGNATURE;

If this naming does not come from documentation, I would suggest
something like SPKG_HEADER_MAGIC, since this is not a signature,
or even a CRC.

> +	uint32_t payload_length;
> +	uint32_t crc;
> +	uint8_t *crc_buf;
> +
> +	/* Check the signature */
> +	if (memcmp(header->signature, signature, 4)) {
> +		fprintf(stderr, "Error: invalid signature bytes\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Check the CRC */
> +	crc = crc32(0, ptr, SPKG_HEADER_SIZE - SPKG_CRC_SIZE);
> +	if (crc != header->crc) {
> +		fprintf(stderr, "Error: invalid header CRC=\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Check all copies of header are the same */
> +	for (int i = 1; i < SPKG_HEADER_COUNT; i++) {
> +		if (memcmp(&header[0], &header[i], SPKG_HEADER_SIZE)) {
> +			fprintf(stderr, "Error: header %d mismatch\n", i);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	/* Check the payload CRC */
> +	payload_length = le32_to_cpu(header->payload_length) >> 8;
> +	crc_buf = file->payload + payload_length - SPKG_CRC_SIZE;
> +	crc = crc32(0, file->payload, payload_length - SPKG_CRC_SIZE);
> +	if (crc_buf[0] != (crc & 0xff) ||
> +	    crc_buf[1] != (crc >> 8 & 0xff) ||
> +	    crc_buf[2] != (crc >> 16 & 0xff) ||
> +	    crc_buf[3] != (crc >> 24 & 0xff)) {
> +		fprintf(stderr, "Error: invalid payload CRC\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void spkgimage_print_header(const void *ptr)
> +{
> +	const struct spkg_hdr *h = ptr;
> +	uint32_t offset = le32_to_cpu(h->execution_offset);
> +
> +	printf("Image type\t: Renesas SPKG Image\n");
> +	printf("Marker\t\t: %c%c%c%c\n", h->signature[0], h->signature[1],
> +					 h->signature[2], h->signature[3]);
> +	printf("Version\t\t: %d\n", h->version);
> +	printf("ECC\t\t: ");
> +	if (h->ecc & 0x20)
> +		printf("Scheme %d, Block size %d, Strength %d\n",
> +		       h->ecc_scheme, (h->ecc >> 1) & 3, h->ecc_bytes);
> +	else
> +		printf("Not enabled\n");
> +	printf("Payload length\t: %d\n", le32_to_cpu(h->payload_length) >> 8);
> +	printf("Load address\t: 0x%08x\n", le32_to_cpu(h->load_address));
> +	printf("Execution offset: 0x%08x (%s mode)\n", offset & ~1,
> +	       offset & 1 ? "THUMB" : "ARM");
> +	printf("Header checksum\t: 0x%08x\n", le32_to_cpu(h->crc));
> +}
> +
> +static inline uint32_t roundup(uint32_t x, uint32_t y)
> +{
> +	return ((x + y - 1) / y) * y;
> +}
> +
> +static int spkgimage_vrec_header(struct image_tool_params *params,
> +				 struct image_type_params *tparams)
> +{
> +	struct stat s;
> +
> +	/* Parse the config file */
> +	if (spkgimage_parse_config_file(params->imagename)) {
> +		fprintf(stderr, "Error parsing config file\n");
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	/* Get size of input data file */
> +	if (stat(params->datafile, &s)) {
> +		fprintf(stderr, "Could not stat data file: %s: %s\n",
> +			params->datafile, strerror(errno));
> +		exit(EXIT_FAILURE);
> +	}
> +	params->orig_file_size = s.st_size;
> +
> +	/* Determine size of resulting SPKG file */
> +	uint32_t header_len = SPKG_HEADER_SIZE * SPKG_HEADER_COUNT;
> +	uint32_t payload_len = conf.blp_len + s.st_size + SPKG_CRC_SIZE;
> +	uint32_t total_len = header_len + payload_len;
> +
> +	/* Round up to next multiple of padding size */
> +	uint32_t padded_len = roundup(total_len, conf.padding);
> +
> +	/* Number of padding bytes to add */
> +	padding = padded_len - total_len;
> +
> +	/* Fixup payload_len to include padding bytes */
> +	payload_len += padding;
> +
> +	/* Prepare the header */
> +	struct spkg_hdr header = {
> +		.signature = SPKG_HEADER_SIGNATURE,
> +		.version = conf.version,
> +		.ecc = (conf.ecc_enable << 5) | (conf.ecc_block_size << 1),
> +		.ecc_scheme = conf.ecc_scheme,
> +		.ecc_bytes = conf.ecc_bytes,
> +		.payload_length = cpu_to_le32(payload_len << 8),
> +		.load_address = cpu_to_le32(params->addr),
> +		.execution_offset = cpu_to_le32(params->ep - params->addr),
> +	};
> +	header.crc = crc32(0, (uint8_t *)&header,
> +			   sizeof(header) - SPKG_CRC_SIZE);
> +
> +	/* Fill the SPKG with the headers */
> +	for (int i = 0; i < SPKG_HEADER_COUNT; i++)
> +		memcpy(&out_buf.header[i], &header, sizeof(header));
> +
> +	/* Extra bytes to allocate in the output file */
> +	return conf.blp_len + padding + 4;
> +}
> +
> +static void spkgimage_set_header(void *ptr, struct stat *sbuf, int ifd,
> +				 struct image_tool_params *params)
> +{
> +	uint8_t *payload = ptr + SPKG_HEADER_SIZE * SPKG_HEADER_COUNT;
> +	uint8_t *file_end = payload + conf.blp_len + params->orig_file_size;
> +	uint8_t *crc_buf = file_end + padding;
> +	uint32_t crc;
> +
> +	/* Make room for the Dummy BLp header */
> +	memmove(payload + conf.blp_len, payload, params->orig_file_size);
> +
> +	/* Fill the SPKG with the Dummy BLp */
> +	memset(payload, 0x88, conf.blp_len);
> +
> +	/*
> +	 * mkimage copy_file() pads the input file with zeros.
> +	 * Replace those zeros with flash friendly one bits.
> +	 * The original version skipped the fist 4 bytes,

nit: first

> +	 * probably an oversight, but for consistency we
> +	 * keep the same behaviour.
> +	 */
> +	memset(file_end + 4, 0xff, padding - 4);
> +
> +	/* Add Payload CRC */
> +	crc = crc32(0, payload, crc_buf - payload);
> +	crc_buf[0] = crc;
> +	crc_buf[1] = crc >> 8;
> +	crc_buf[2] = crc >> 16;
> +	crc_buf[3] = crc >> 24;
> +}
> +
> +static int spkgimage_check_image_types(uint8_t type)
> +{
> +	return type == IH_TYPE_RENESAS_SPKG ? 0 : 1;

This function is not necessary if you only support one type.

> +}
> +
> +/*
> + * spkgimage type parameter definition
> + */
> +U_BOOT_IMAGE_TYPE(
> +	spkgimage,
> +	"Renesas SPKG Image",
> +	sizeof(out_buf),
> +	&out_buf,
> +	spkgimage_check_params,
> +	spkgimage_verify_header,
> +	spkgimage_print_header,
> +	spkgimage_set_header,
> +	NULL,
> +	spkgimage_check_image_types,
> +	NULL,
> +	spkgimage_vrec_header
> +);
> diff --git a/tools/spkgimage.h b/tools/spkgimage.h
> new file mode 100644
> index 0000000000..db153bfc3f
> --- /dev/null
> +++ b/tools/spkgimage.h
> @@ -0,0 +1,39 @@
> +/* SPDX-License-Identifier: BSD-2-Clause */
> +/*
> + * Renesas RZ/N1 Package Table format
> + * (C) 2015-2016 Renesas Electronics Europe, LTD
> + * All rights reserved.
> + *
> + * Converted to mkimage plug-in
> + * (C) Copyright 2022 Schneider Electric
> + */
> +
> +#ifndef _SPKGIMAGE_H_
> +#define _SPKGIMAGE_H_
> +
> +#define SPKG_HEADER_SIGNATURE	{'R', 'Z', 'N', '1'}
> +#define SPKG_HEADER_SIZE	24
> +#define SPKG_HEADER_COUNT	8

What are the other 7 headers for? Should you print them out above?

> +#define SPKG_BLP_SIZE		264
> +#define SPKG_CRC_SIZE		4
> +
> +/* SPKG header */
> +struct spkg_hdr {
> +	uint8_t		signature[4];
> +	uint8_t		version;
> +	uint8_t		ecc;
> +	uint8_t		ecc_scheme;
> +	uint8_t		ecc_bytes;
> +	uint32_t	payload_length; /* only HIGHER 24 bits */
> +	uint32_t	load_address;
> +	uint32_t	execution_offset;
> +	uint32_t	crc; /* of this header */
> +} __packed;
> +
> +struct spkg_file {
> +	struct spkg_hdr	header[SPKG_HEADER_COUNT];
> +	uint8_t		payload[0];
> +	/* then the CRC */
> +} __packed;
> +
> +#endif
> 

--Sean

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v2 9/9] tools: spkgimage: add Renesas SPKG format
  2022-08-13 14:47     ` Sean Anderson
@ 2022-08-14  1:45       ` Ralph Siemsen
  2022-08-16 14:33         ` Ralph Siemsen
  0 siblings, 1 reply; 29+ messages in thread
From: Ralph Siemsen @ 2022-08-14  1:45 UTC (permalink / raw)
  To: Sean Anderson
  Cc: andre.przywara, heiko.thiery, pali, samuel, sjg, sr,
	takahiro.akashi, u-boot, sean.anderson

On Sat, Aug 13, 2022 at 10:47 AM Sean Anderson <seanga2@gmail.com> wrote:
> >
> >   board/schneider/lces/spkgimage.cfg |  26 +++
> >   boot/image.c                       |   1 +
> >   include/image.h                    |   1 +
> >   tools/Makefile                     |   1 +
> >   tools/spkgimage.c                  | 303 +++++++++++++++++++++++++++++
> >   tools/spkgimage.h                  |  39 ++++
>
> Please document your format in doc/mkimage.1

Okay, will be added  in the next version.

> > +/* Note: the ordering of the bitfields does not matter */
> > +static struct config_file {
> > +     unsigned int version:1;
> > +     unsigned int ecc_block_size:2;
> > +     unsigned int ecc_enable:1;
> > +     unsigned int ecc_scheme:3;
> > +     unsigned int ecc_bytes:8;
> > +     unsigned int blp_len;
> > +     unsigned int padding;
> > +} conf;
>
> I wonder if you could just fill in the header directly. This is
> for a userspace tool, and this struct will be created at most
> once. It's OK to use 10 bytes :)

I could fill the header directly, but I figured it would be cleaner to
keep the config file parsing separate from header generation.

As for the use of bitfields, this was not really about saving space,
but rather a cheap way to ensure the values from the config file do
not exceed their allocated bit-width in the header. The previous
stand-alone code has a somewhat similar construct, from which I
pinched the idea.

>
> > +static int spkgimage_parse_config_line(char *line)
> > +{
> > +     char *saveptr;
> > +     char *delim = "\t ";
> > +     char *name = strtok_r(line, delim, &saveptr);
> > +     char *val_str = strtok_r(NULL, delim, &saveptr);
> > +     int value = atoi(val_str);
> > +
> > +     if (!strcmp("VERSION", name)) {
> > +             conf.version = value;
> > +     } else if (!strcmp("NAND_ECC_ENABLE", name)) {
> > +             conf.ecc_enable = value;
>
> Can you add some checks for the valid range of values? E.g.
> NAND_ECC_SCHEME should be 0 <= value <= 5

Yes, will add some range checks where they make sense. Probably with a
helper function.

> > +     } else if (!strcmp("NAND_ECC_BLOCK_SIZE", name)) {
> > +             conf.ecc_block_size = value;
> > +     } else if (!strcmp("NAND_ECC_SCHEME", name)) {
> > +             conf.ecc_scheme = value;
> > +     } else if (!strcmp("NAND_BYTES_PER_ECC_BLOCK", name)) {
> > +             conf.ecc_bytes = value;
> > +     } else if (!strcmp("ADD_DUMMY_BLP", name)) {
> > +             conf.blp_len = value ? SPKG_BLP_SIZE : 0;
> > +     } else if (!strcmp("PADDING", name)) {
> > +             if (strrchr(val_str, 'K'))
> > +                     conf.padding = value * 1024;
> > +             else if (strrchr(val_str, 'M'))
> > +                     conf.padding = value * 1024 * 1024;
> > +             else
> > +                     conf.padding = value;
> > +     } else {
> > +             fprintf(stderr, "Error: unknown keyword '%s' in config\n",
> > +                     name);
>
> perhaps print the line number?

Good idea, will do.
> > +     /* Avoid divide-by-zero later on */
> > +     if (conf.padding == 0)
>
> if (!conf.padding)

Will do.

> > +static int spkgimage_verify_header(unsigned char *ptr, int size,
> > +                                struct image_tool_params *param)
> > +{
> > +     struct spkg_file *file = (struct spkg_file *)ptr;
> > +     struct spkg_hdr *header = (struct spkg_hdr *)ptr;
> > +     char signature[4] = SPKG_HEADER_SIGNATURE;
>
> If this naming does not come from documentation, I would suggest
> something like SPKG_HEADER_MAGIC, since this is not a signature,
> or even a CRC.

The name does in fact come from the RZ/N1 documentation. However I
agree that SPKG_HEADER_MAGIC would better reflect what these bytes
actually are.

> > +     /*
> > +      * mkimage copy_file() pads the input file with zeros.
> > +      * Replace those zeros with flash friendly one bits.
> > +      * The original version skipped the fist 4 bytes,
>
> nit: first

Well spotted, thanks.

> > +static int spkgimage_check_image_types(uint8_t type)
> > +{
> > +     return type == IH_TYPE_RENESAS_SPKG ? 0 : 1;
>
> This function is not necessary if you only support one type.

Without this function, mkimage kept telling me that my format
(spkgimage) was not supported, and none of my callbacks got invoked.
It only complained when trying to generate a header. When listing the
supported formats, spkgimage showed up correctly.

I'll take another look on Monday, maybe I missed something obvious.

> > +#define SPKG_HEADER_SIGNATURE        {'R', 'Z', 'N', '1'}
> > +#define SPKG_HEADER_SIZE     24
> > +#define SPKG_HEADER_COUNT    8
>
> What are the other 7 headers for? Should you print them out above?

There are 8 identical copies of the 24-byte header. This is meant to
help with NAND booting, where the header is read before ECC settings
are known. The BootROM validates the header CRC and will try up to 8
headers before giving up.

Since they are identical, I only printed values from one header.
However in the validation function, I do check that all eight copies
match.

Thanks for your feedback!
-Ralph

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v1 3/9] clk: renesas: add R906G032 driver
  2022-08-13  5:30   ` Sean Anderson
@ 2022-08-15  2:48     ` Ralph Siemsen
  2022-08-23  4:14       ` Sean Anderson
  0 siblings, 1 reply; 29+ messages in thread
From: Ralph Siemsen @ 2022-08-15  2:48 UTC (permalink / raw)
  To: Sean Anderson; +Cc: u-boot, Lukasz Majewski

On Sat, Aug 13, 2022 at 01:30:19AM -0400, Sean Anderson wrote:
>+
>>+	u16 gate, reset, ready, midle,
>>+		scon, mirack, mistat;
>
>What are the scon/mirack/mistat fields for? You define them for a lot
>of clocks, but I don't see them used in the driver.

These came from the Linux driver of the same name. I can only speculate 
that in turn, the Linux driver definitions were auto-generated from 
vendor provided XML or similar documentation.

I figured that it would be best to match the Linux kernel clock driver. 
That way fixes can easily be shared. In fact while doing this work, I 
found an error in the clock table [1] and I also made some 
simplifications [2].

Regarding the unused fields (scon, mirack, mistat): I am not really sure 
what their purpose is. Maybe there is some value in having them. I'll 
try to find out more information about them. If we do decide to drop 
them, I would like to keep it synchronised with the Linux driver.

[1] 
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2dee50ab9e72a3cae75b65e5934c8dd3e9bf01bc

[2] 
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f46efcc4746f5c1a539df9db625c04321f75e494

>>+};
>>+
>>+/* This is used to describe a clock for instantiation */
>>+struct r9a06g032_clkdesc {
>>+	const char *name;
>>+	uint32_t managed: 1;
>>+	uint32_t type: 3;
>
>I wonder if we could define the enum here?

This is still part of the code which I've intentionally kept identical 
to the Linux driver. I do agree that moving the enum seems reasonable, 
I'll put together a patch for this (and test it) and see if I can get it 
accepted on the kernel side.

>>+	uint32_t index: 8;
>>+	uint32_t source : 8; /* source index + 1 (0 == none) */
>>+	/* these are used to populate the bitsel struct */
>>+	union {
>>+		struct r9a06g032_gate gate;
>>+		/* for dividers */
>>+		struct {
>>+			unsigned int div_min : 10, div_max : 10, reg: 10;
>>+			u16 div_table[4];
>>+		};
>>+		/* For fixed-factor ones */
>>+		struct {
>>+			u16 div, mul;
>>+		};
>>+		/* for dual gate */
>>+		struct {
>>+			uint16_t group : 1;
>>+			u16 sel, g1, r1, g2, r2;
>>+		} dual;
>>+	};
>>+};
>>+
>>+#define I_GATE(_clk, _rst, _rdy, _midle, _scon, _mirack, _mistat) \
>>+	{ .gate = _clk, .reset = _rst, \
>
>If these fields have bitfield inside them, then those bitfields should
>be assigned/constructed separately. That is, if .reset is actually a combined
>offset/bit, then you need to expose those in the macro. Since you have a lot of these, you might want to do something like
>
>#define BIT_OFFSET	GENMASK(15, 5)
>#define BIT_SHIFT	GENMASK(4, 0)
>
>#define PACK_BIT(offset, shift)		(FIELD_PREP(BIT_OFFSET, offset) | FIELD_PREP(BIT_SHIFT, shift))

I think it happened before I started working on RZ/N1, but there seemed 
to be quite a few iterations on how to represent the clock tree. At one 
point there were macros to assign/construct the bitfield values. And 
then a different way, and eventually the direct hex values you now see 
in the clock tables.

At the risk of re-opening old wounds (luckily not mine) I decided to 
just leave this part exactly as-is in the Linux driver.

>>+
>>+/* Internal clock IDs */
>>+#define R9A06G032_CLKOUT		0
>>+#define R9A06G032_CLKOUT_D10		2
>>+#define R9A06G032_CLKOUT_D16		3
>>+#define R9A06G032_CLKOUT_D160		4
>>[...]
>>+#define R9A06G032_UART_GROUP_012	154
>>+#define R9A06G032_UART_GROUP_34567	155
>
>Can you put these in your dt-bindings header? I think that would make it
>much clearer why there are gaps, and would avoid someone accidentally
>duplicating a clock id (although I suppose your array initializer below
>might complain?)

In fact quite a few of them are in the dt-bindings already, see 
include/dt-bindings/clock/r9a06g032-sysctrl.h

I'm not really sure why some of these are defined in the .C file while 
others are in the dt-bindings header. Like much of the other bits, this 
was something I just carried over as-is from the Linux driver.

>>+		parent->id = ~0;	/* Top-level clock */
>
>Can you use a define for this (instead of referring to ~0 everywhere)

Yes, that sounds reasonable. My list of fixes (for both Linux driver and 
the u-boot one) is going to get rather long ;-)

>>+	else
>>+		parent->id = desc->source - 1;
>>+
>>+	parent->dev = clk->dev;
>
>I think you need to clk_request here.

Normally clk_request is called by a driver wishing to use a particular 
clock. That is not the case here. This is in a helper function used to 
compute the current rate of a given clock. It only looks at the local 
table (struct r9a06g032_clkdsc).

>>+/* register/bit pairs are encoded as an uint16_t */
>>+static void
>>+clk_rdesc_set(struct r9a06g032_priv *clocks,
>>+	      u16 one, unsigned int on)
>>+{
>>+	uint offset = 4 * (one >> 5);
>>+	uint mask = 1U << (one & 0x1f);
>>+	uint val = ((!!on) << (one & 0x1f));
>
>Please either use bitfields for this, or use FIELD_GET() and friends.

Yes, this would be clearer - however as mentioned above, there was 
already quite a bit of teeth-gnashing about this encoding. I will 
prepare a patch for the Linux side and see what kind of reply I get.

I would very much prefer to keep both in sync as much as possible.

>>+/*
>>+ * Fixed factor clock
>>+ */
>>+static ulong r9a06g032_ffc_get_rate(struct clk *clk)
>>+{
>>+	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
>>+	unsigned long parent_rate = r9a06g032_clk_get_parent_rate(clk);
>>+	unsigned long long rate;
>>+
>>+	if (parent_rate == 0) {
>>+		debug("%s: parent_rate is zero\n", __func__);
>>+		return 0;
>>+	}
>>+
>>+	rate = (unsigned long long)parent_rate * desc->mul;
>>+	rate = DIV_ROUND_UP(rate, desc->div);
>>+	return (ulong)rate;
>>+}
>>+
>>+/*
>>+ * This implements R9A06G032 clock divider 'driver'. This differs from the
>>+ * standard clk_divider because the set_rate method must also set b[31] to
>>+ * trigger the hardware rate change. In theory it should also wait for this
>>+ * bit to clear.
>>+ */
>>+static ulong r9a06g032_div_get_rate(struct clk *clk)
>>+{
>>+	struct r9a06g032_priv *clocks = dev_get_priv(clk->dev);
>>+	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
>>+	unsigned long parent_rate = r9a06g032_clk_get_parent_rate(clk);
>>+	u32 div = 0;
>>+
>>+	if (parent_rate == 0) {
>>+		debug("%s: parent_rate is zero\n", __func__);
>
>Didn't you already log this?

It is for a different clock type. However I will see if I do something 
to avoid the duplication.

>
>>+		return 0;
>>+	}
>>+
>>+	regmap_read(clocks->regmap, 4 * desc->reg, &div);
>>+
>>+	if (div < desc->div_min)
>>+		div = desc->div_min;
>>+	else if (div > desc->div_max)
>>+		div = desc->div_max;
>>+	return DIV_ROUND_UP(parent_rate, div);
>
>DIV_ROUND_CLOSEST?

I'm hesitant to change the logic on this, as it could subtly alter the 
values.

>>+	/* TODO: use the .div_table if provided */
>>+	if (desc->div_table[0])
>>+		pr_err("ERROR: %s: div_table not implemented\n", __func__);
>
>dev_err
>
>But can't you just leave out the div_table member?

Only a few of the clocks have a div_table, but for those, the table 
specifies the allowable divider values. So right now, the code cannot 
correctly set such clocks, as it may try programming illegal values 
(most likely with the result that you don't get the expected rate).

The Linux driver does implement the div_table logic, I simply did not 
carry it over yet to u-boot. Mostly because I have not yet had need for 
one of the few clocks which does use the table. I do plan to add it.

>>+/*
>>+ * Main clock driver
>>+ */
>>+static int r9a06g032_clk_enable(struct clk *clk)
>>+{
>>+	const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
>>+
>>+	switch (desc->type) {
>>+	case K_GATE:
>>+		return r9a06g032_clk_gate_enable(clk);
>>+	case K_DUALGATE:
>>+		return r9a06g032_clk_dualgate_enable(clk);
>>+	default:
>>+		printf("ERROR: %s:%d unhandled type=%d\n", __func__, __LINE__, desc->type);
>
>Assert or dev_dbg is better here. This is "impossible" so we try and
>avoid increasing image size in these cases.

Fair enough. Though I have to say I have been bitten by this kind of 
thing a few times. After having spent time debugging-via-printf, I would 
then discover an assert or dev_dbg that points out the exact problem. If 
only I had enabled DEBUG for that file! And yet if I enable DEBUG 
globally, there is so much noise, that I don't notice the one message 
that might have been helpful.

>The overall structure looks good; most of these things you
>should be able to iron out fairly easily.

I have skipped over a few of the smaller points regarding return values, 
etc, but I will address them in next version of the patch. Thanks again 
for your time reviewing this.

Regards,
-Ralph

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v2 9/9] tools: spkgimage: add Renesas SPKG format
  2022-08-14  1:45       ` Ralph Siemsen
@ 2022-08-16 14:33         ` Ralph Siemsen
  2022-08-23  3:42           ` Sean Anderson
  0 siblings, 1 reply; 29+ messages in thread
From: Ralph Siemsen @ 2022-08-16 14:33 UTC (permalink / raw)
  To: Sean Anderson
  Cc: andre.przywara, heiko.thiery, pali, samuel, sjg, sr,
	takahiro.akashi, u-boot, sean.anderson

Hi Sean,

I've implemented most of the suggestions. I will post an updated
series, since it seems that sending v2 of just one patch has confused
patchwork.

However so as not to entirely remove confusion, the updated series
will be v3, since I already used v2 for the one patch. :-P

On Sat, Aug 13, 2022 at 9:45 PM Ralph Siemsen <ralph.siemsen@linaro.org> wrote:
> >
> > I wonder if you could just fill in the header directly. This is
> > for a userspace tool, and this struct will be created at most
> > once. It's OK to use 10 bytes :)
>
> I could fill the header directly, but I figured it would be cleaner to
> keep the config file parsing separate from header generation.

Does it seem reasonable to keep these structures separated?

> > > +static int spkgimage_verify_header(unsigned char *ptr, int size,
> > > +                                struct image_tool_params *param)
> > > +{
> > > +     struct spkg_file *file = (struct spkg_file *)ptr;
> > > +     struct spkg_hdr *header = (struct spkg_hdr *)ptr;
> > > +     char signature[4] = SPKG_HEADER_SIGNATURE;
> >
> > If this naming does not come from documentation, I would suggest
> > something like SPKG_HEADER_MAGIC, since this is not a signature,
> > or even a CRC.
>
> The name does in fact come from the RZ/N1 documentation. However I
> agree that SPKG_HEADER_MAGIC would better reflect what these bytes
> actually are.

Upon checking the documentation, it turns out they use the term
"marker" rather than "signature" for these bytes. So I have switched
the code to match.

> > > +static int spkgimage_check_image_types(uint8_t type)
> > > +{
> > > +     return type == IH_TYPE_RENESAS_SPKG ? 0 : 1;
> >
> > This function is not necessary if you only support one type.
>
> Without this function, mkimage kept telling me that my format
> (spkgimage) was not supported, and none of my callbacks got invoked.
> It only complained when trying to generate a header. When listing the
> supported formats, spkgimage showed up correctly.
>
> I'll take another look on Monday, maybe I missed something obvious.

I have re-checked this:
- without the function, mkimage complains that spkgimage is unknown
- with a function that unconditionally returns 0, it works fine

If it really is meant to work without the function, then a bug must
have crept in elsewhere...

Regards,
-Ralph

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v2 9/9] tools: spkgimage: add Renesas SPKG format
  2022-08-16 14:33         ` Ralph Siemsen
@ 2022-08-23  3:42           ` Sean Anderson
  2022-08-26 15:01             ` Ralph Siemsen
  0 siblings, 1 reply; 29+ messages in thread
From: Sean Anderson @ 2022-08-23  3:42 UTC (permalink / raw)
  To: Ralph Siemsen
  Cc: andre.przywara, heiko.thiery, pali, samuel, sjg, sr,
	takahiro.akashi, u-boot, sean.anderson

On 8/16/22 10:33 AM, Ralph Siemsen wrote:
> Hi Sean,
> 
> I've implemented most of the suggestions. I will post an updated
> series, since it seems that sending v2 of just one patch has confused
> patchwork.
> 
> However so as not to entirely remove confusion, the updated series
> will be v3, since I already used v2 for the one patch. :-P
> 
> On Sat, Aug 13, 2022 at 9:45 PM Ralph Siemsen <ralph.siemsen@linaro.org> wrote:
>>>
>>> I wonder if you could just fill in the header directly. This is
>>> for a userspace tool, and this struct will be created at most
>>> once. It's OK to use 10 bytes :)
>>
>> I could fill the header directly, but I figured it would be cleaner to
>> keep the config file parsing separate from header generation.
> 
> Does it seem reasonable to keep these structures separated?

Yes, that is fine.

>>>> +static int spkgimage_verify_header(unsigned char *ptr, int size,
>>>> +                                struct image_tool_params *param)
>>>> +{
>>>> +     struct spkg_file *file = (struct spkg_file *)ptr;
>>>> +     struct spkg_hdr *header = (struct spkg_hdr *)ptr;
>>>> +     char signature[4] = SPKG_HEADER_SIGNATURE;
>>>
>>> If this naming does not come from documentation, I would suggest
>>> something like SPKG_HEADER_MAGIC, since this is not a signature,
>>> or even a CRC.
>>
>> The name does in fact come from the RZ/N1 documentation. However I
>> agree that SPKG_HEADER_MAGIC would better reflect what these bytes
>> actually are.
> 
> Upon checking the documentation, it turns out they use the term
> "marker" rather than "signature" for these bytes. So I have switched
> the code to match.

That sounds good.

>>>> +static int spkgimage_check_image_types(uint8_t type)
>>>> +{
>>>> +     return type == IH_TYPE_RENESAS_SPKG ? 0 : 1;
>>>
>>> This function is not necessary if you only support one type.
>>
>> Without this function, mkimage kept telling me that my format
>> (spkgimage) was not supported, and none of my callbacks got invoked.
>> It only complained when trying to generate a header. When listing the
>> supported formats, spkgimage showed up correctly.
>>
>> I'll take another look on Monday, maybe I missed something obvious.
> 
> I have re-checked this:
> - without the function, mkimage complains that spkgimage is unknown
> - with a function that unconditionally returns 0, it works fine
> 
> If it really is meant to work without the function, then a bug must
> have crept in elsewhere...

Huh. I did a quick grep so maybe I missed something. IMO this *should*
work without a function, because we have tons of drivers which just
have an equality check. In any case, you can just do

return type == IH_TYPE_RENESAS_SPKG ? 0 : -EINVAL;

--Sean


^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v1 3/9] clk: renesas: add R906G032 driver
  2022-08-15  2:48     ` Ralph Siemsen
@ 2022-08-23  4:14       ` Sean Anderson
  2022-08-26 15:47         ` Ralph Siemsen
  0 siblings, 1 reply; 29+ messages in thread
From: Sean Anderson @ 2022-08-23  4:14 UTC (permalink / raw)
  To: Ralph Siemsen; +Cc: u-boot, Lukasz Majewski

On 8/14/22 10:48 PM, Ralph Siemsen wrote:
> On Sat, Aug 13, 2022 at 01:30:19AM -0400, Sean Anderson wrote:
>> +
>>> +    u16 gate, reset, ready, midle,
>>> +        scon, mirack, mistat;
>>
>> What are the scon/mirack/mistat fields for? You define them for a lot
>> of clocks, but I don't see them used in the driver.
> 
> These came from the Linux driver of the same name. I can only speculate that in turn, the Linux driver definitions were auto-generated from vendor provided XML or similar documentation.
> 
> I figured that it would be best to match the Linux kernel clock driver. That way fixes can easily be shared. In fact while doing this work, I found an error in the clock table [1] and I also made some simplifications [2].
> 
> Regarding the unused fields (scon, mirack, mistat): I am not really sure what their purpose is. Maybe there is some value in having them. I'll try to find out more information about them. If we do decide to drop them, I would like to keep it synchronised with the Linux driver.

OK, well if you don't use them then perhaps you can just leave them in
the macro but remove them from the struct. That way you can add support
for them later if you need to, but they don't take up space in the mean
time. A comment summarizing your explanation above would be helpful.

> [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2dee50ab9e72a3cae75b65e5934c8dd3e9bf01bc
> 
> [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f46efcc4746f5c1a539df9db625c04321f75e494
> 
>>> +};
>>> +
>>> +/* This is used to describe a clock for instantiation */
>>> +struct r9a06g032_clkdesc {
>>> +    const char *name;
>>> +    uint32_t managed: 1;
>>> +    uint32_t type: 3;
>>
>> I wonder if we could define the enum here?
> 
> This is still part of the code which I've intentionally kept identical to the Linux driver. I do agree that moving the enum seems reasonable, I'll put together a patch for this (and test it) and see if I can get it accepted on the kernel side.
> 
>>> +    uint32_t index: 8;
>>> +    uint32_t source : 8; /* source index + 1 (0 == none) */
>>> +    /* these are used to populate the bitsel struct */
>>> +    union {
>>> +        struct r9a06g032_gate gate;
>>> +        /* for dividers */
>>> +        struct {
>>> +            unsigned int div_min : 10, div_max : 10, reg: 10;
>>> +            u16 div_table[4];
>>> +        };
>>> +        /* For fixed-factor ones */
>>> +        struct {
>>> +            u16 div, mul;
>>> +        };
>>> +        /* for dual gate */
>>> +        struct {
>>> +            uint16_t group : 1;
>>> +            u16 sel, g1, r1, g2, r2;
>>> +        } dual;
>>> +    };
>>> +};
>>> +
>>> +#define I_GATE(_clk, _rst, _rdy, _midle, _scon, _mirack, _mistat) \
>>> +    { .gate = _clk, .reset = _rst, \
>>
>> If these fields have bitfield inside them, then those bitfields should
>> be assigned/constructed separately. That is, if .reset is actually a combined
>> offset/bit, then you need to expose those in the macro. Since you have a lot of these, you might want to do something like
>>
>> #define BIT_OFFSET    GENMASK(15, 5)
>> #define BIT_SHIFT    GENMASK(4, 0)
>>
>> #define PACK_BIT(offset, shift)        (FIELD_PREP(BIT_OFFSET, offset) | FIELD_PREP(BIT_SHIFT, shift))
> 
> I think it happened before I started working on RZ/N1, but there seemed to be quite a few iterations on how to represent the clock tree. At one point there were macros to assign/construct the bitfield values. And then a different way, and eventually the direct hex values you now see in the clock tables.
> 
> At the risk of re-opening old wounds (luckily not mine) I decided to just leave this part exactly as-is in the Linux driver.

Can you link to that discussion? The earliest discussion of that
series I could find was [1], and there's no mention of the encoding.
This encoding scheme seems to be used only for this SoC, and not for
any of the other renesas drivers. I suspect that this just wasn't
reviewed in detail the first time around...

[1] https://lore.kernel.org/all/1527154169-32380-6-git-send-email-michel.pollet@bp.renesas.com/

>>> +
>>> +/* Internal clock IDs */
>>> +#define R9A06G032_CLKOUT        0
>>> +#define R9A06G032_CLKOUT_D10        2
>>> +#define R9A06G032_CLKOUT_D16        3
>>> +#define R9A06G032_CLKOUT_D160        4
>>> [...]
>>> +#define R9A06G032_UART_GROUP_012    154
>>> +#define R9A06G032_UART_GROUP_34567    155
>>
>> Can you put these in your dt-bindings header? I think that would make it
>> much clearer why there are gaps, and would avoid someone accidentally
>> duplicating a clock id (although I suppose your array initializer below
>> might complain?)
> 
> In fact quite a few of them are in the dt-bindings already, see include/dt-bindings/clock/r9a06g032-sysctrl.h
> 
> I'm not really sure why some of these are defined in the .C file while others are in the dt-bindings header. Like much of the other bits, this was something I just carried over as-is from the Linux driver.

I think these are "internal" clocks (that is, clocks which don't really
exist like intermediate dividers) whereas the others are public-facing
clocks. It's up to you, but maybe have a comment noting where the other
ids come from.

>>> +        parent->id = ~0;    /* Top-level clock */
>>
>> Can you use a define for this (instead of referring to ~0 everywhere)
> 
> Yes, that sounds reasonable. My list of fixes (for both Linux driver and the u-boot one) is going to get rather long ;-)
> 
>>> +    else
>>> +        parent->id = desc->source - 1;
>>> +
>>> +    parent->dev = clk->dev;
>>
>> I think you need to clk_request here.
> 
> Normally clk_request is called by a driver wishing to use a particular clock. That is not the case here. This is in a helper function used to compute the current rate of a given clock. It only looks at the local table (struct r9a06g032_clkdsc).

You call clk_get_rate on it. Any time you "create" a new clock, you
must call clk_request.

>>> +/* register/bit pairs are encoded as an uint16_t */
>>> +static void
>>> +clk_rdesc_set(struct r9a06g032_priv *clocks,
>>> +          u16 one, unsigned int on)
>>> +{
>>> +    uint offset = 4 * (one >> 5);
>>> +    uint mask = 1U << (one & 0x1f);
>>> +    uint val = ((!!on) << (one & 0x1f));
>>
>> Please either use bitfields for this, or use FIELD_GET() and friends.
> 
> Yes, this would be clearer - however as mentioned above, there was already quite a bit of teeth-gnashing about this encoding. I will prepare a patch for the Linux side and see what kind of reply I get.
> 
> I would very much prefer to keep both in sync as much as possible.
> 
>>> +/*
>>> + * Fixed factor clock
>>> + */
>>> +static ulong r9a06g032_ffc_get_rate(struct clk *clk)
>>> +{
>>> +    const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
>>> +    unsigned long parent_rate = r9a06g032_clk_get_parent_rate(clk);
>>> +    unsigned long long rate;
>>> +
>>> +    if (parent_rate == 0) {
>>> +        debug("%s: parent_rate is zero\n", __func__);
>>> +        return 0;
>>> +    }
>>> +
>>> +    rate = (unsigned long long)parent_rate * desc->mul;
>>> +    rate = DIV_ROUND_UP(rate, desc->div);
>>> +    return (ulong)rate;
>>> +}
>>> +
>>> +/*
>>> + * This implements R9A06G032 clock divider 'driver'. This differs from the
>>> + * standard clk_divider because the set_rate method must also set b[31] to
>>> + * trigger the hardware rate change. In theory it should also wait for this
>>> + * bit to clear.
>>> + */
>>> +static ulong r9a06g032_div_get_rate(struct clk *clk)
>>> +{
>>> +    struct r9a06g032_priv *clocks = dev_get_priv(clk->dev);
>>> +    const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
>>> +    unsigned long parent_rate = r9a06g032_clk_get_parent_rate(clk);
>>> +    u32 div = 0;
>>> +
>>> +    if (parent_rate == 0) {
>>> +        debug("%s: parent_rate is zero\n", __func__);
>>
>> Didn't you already log this?
> 
> It is for a different clock type. However I will see if I do something to avoid the duplication.

I mean in r9a06g032_clk_get_parent_rate. You can also just do

if (!parent_rate)
	...

>>
>>> +        return 0;
>>> +    }
>>> +
>>> +    regmap_read(clocks->regmap, 4 * desc->reg, &div);
>>> +
>>> +    if (div < desc->div_min)
>>> +        div = desc->div_min;
>>> +    else if (div > desc->div_max)
>>> +        div = desc->div_max;
>>> +    return DIV_ROUND_UP(parent_rate, div);
>>
>> DIV_ROUND_CLOSEST?
> 
> I'm hesitant to change the logic on this, as it could subtly alter the values.

Well if you have 2MHz divided by 3, the resulting rate is closer to
666667 kHz than 666666 Hz.

>>> +    /* TODO: use the .div_table if provided */
>>> +    if (desc->div_table[0])
>>> +        pr_err("ERROR: %s: div_table not implemented\n", __func__);
>>
>> dev_err
>>
>> But can't you just leave out the div_table member?
> 
> Only a few of the clocks have a div_table, but for those, the table specifies the allowable divider values. So right now, the code cannot correctly set such clocks, as it may try programming illegal values (most likely with the result that you don't get the expected rate).
> 
> The Linux driver does implement the div_table logic, I simply did not carry it over yet to u-boot. Mostly because I have not yet had need for one of the few clocks which does use the table. I do plan to add it.

OK

>>> +/*
>>> + * Main clock driver
>>> + */
>>> +static int r9a06g032_clk_enable(struct clk *clk)
>>> +{
>>> +    const struct r9a06g032_clkdesc *desc = r9a06g032_clk_get(clk);
>>> +
>>> +    switch (desc->type) {
>>> +    case K_GATE:
>>> +        return r9a06g032_clk_gate_enable(clk);
>>> +    case K_DUALGATE:
>>> +        return r9a06g032_clk_dualgate_enable(clk);
>>> +    default:
>>> +        printf("ERROR: %s:%d unhandled type=%d\n", __func__, __LINE__, desc->type);
>>
>> Assert or dev_dbg is better here. This is "impossible" so we try and
>> avoid increasing image size in these cases.
> 
> Fair enough. Though I have to say I have been bitten by this kind of thing a few times. After having spent time debugging-via-printf, I would then discover an assert or dev_dbg that points out the exact problem. If only I had enabled DEBUG for that file! And yet if I enable DEBUG globally, there is so much noise, that I don't notice the one message that might have been helpful.

I generally stick a `#define DEBUG` at the top of a file any time I get
an error with no message. You still return 0, so I suggest returning
-EINVAL (or something else uncommon) so you have somewhere to start.

>> The overall structure looks good; most of these things you
>> should be able to iron out fairly easily.
> 
> I have skipped over a few of the smaller points regarding return values, etc, but I will address them in next version of the patch. Thanks again for your time reviewing this.
> 
> Regards,
> -Ralph



^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v2 9/9] tools: spkgimage: add Renesas SPKG format
  2022-08-23  3:42           ` Sean Anderson
@ 2022-08-26 15:01             ` Ralph Siemsen
  0 siblings, 0 replies; 29+ messages in thread
From: Ralph Siemsen @ 2022-08-26 15:01 UTC (permalink / raw)
  To: Sean Anderson
  Cc: andre.przywara, heiko.thiery, pali, samuel, sjg, sr,
	takahiro.akashi, u-boot, sean.anderson

On Mon, Aug 22, 2022 at 11:42:54PM -0400, Sean Anderson wrote:
>>>>>+static int spkgimage_check_image_types(uint8_t type)
>>>>>+{
>>>>>+     return type == IH_TYPE_RENESAS_SPKG ? 0 : 1;
>>>>
>>>>This function is not necessary if you only support one type.
>>>
>>>Without this function, mkimage kept telling me that my format
>>>(spkgimage) was not supported, and none of my callbacks got invoked.
>>>It only complained when trying to generate a header. When listing the
>>>supported formats, spkgimage showed up correctly.
>>>
>>>I'll take another look on Monday, maybe I missed something obvious.
>>
>>I have re-checked this:
>>- without the function, mkimage complains that spkgimage is unknown
>>- with a function that unconditionally returns 0, it works fine
>>
>>If it really is meant to work without the function, then a bug must
>>have crept in elsewhere...
>
>Huh. I did a quick grep so maybe I missed something. IMO this *should*
>work without a function, because we have tons of drivers which just
>have an equality check. In any case, you can just do
>
>return type == IH_TYPE_RENESAS_SPKG ? 0 : -EINVAL;

It works fine when I use the following for the function:

static int spkgimage_check_image_types(uint8_t type)
{
	return 0;
}

However if no function is provided, i.e. U_BOOT_IMAGE_TYPE has
NULL for check_image_type field, then mkimage fails with the error:

tools/mkimage: unsupported type Renesas SPKG Image

Looking at this a bit more, it seems to be due to:

struct image_type_params *imagetool_get_type(int type)
{
	...snip...

	for (curr = start; curr != end; curr++) {
		if ((*curr)->check_image_type) {
			if (!(*curr)->check_image_type(type))
				return *curr;
		}
	}
	return NULL;
}

So the only way to get non-NULL from imagetool_get_type is for
there to be a callback function, and it must return zero. And
this in turn causes mkimage to bail out quite early in main():

	/* set tparams as per input type_id */
	tparams = imagetool_get_type(params.type);
	if (tparams == NULL && !params.lflag) {
		fprintf (stderr, "%s: unsupported type %s\n",
			params.cmdname, genimg_get_type_name(params.type));
		exit (EXIT_FAILURE);
	}

Unless I am missing something, it seems I must provide a function.

-Ralph

PS I will post an updated series (v3) eventually. I'm working on making 
changes to the clock driver on the kernel side, to keep it in sync with 
the changes you requested in the u-boot side.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v1 3/9] clk: renesas: add R906G032 driver
  2022-08-23  4:14       ` Sean Anderson
@ 2022-08-26 15:47         ` Ralph Siemsen
  2023-02-22 17:39           ` Ralph Siemsen
  0 siblings, 1 reply; 29+ messages in thread
From: Ralph Siemsen @ 2022-08-26 15:47 UTC (permalink / raw)
  To: Sean Anderson; +Cc: u-boot, Lukasz Majewski

On Tue, Aug 23, 2022 at 12:14:31AM -0400, Sean Anderson wrote:
>>Regarding the unused fields (scon, mirack, mistat): I am not really 
>>sure what their purpose is. Maybe there is some value in having them. 
>>I'll try to find out more information about them. If we do decide to 
>>drop them, I would like to keep it synchronised with the Linux driver.
>
>OK, well if you don't use them then perhaps you can just leave them in
>the macro but remove them from the struct. That way you can add support
>for them later if you need to, but they don't take up space in the mean
>time. A comment summarizing your explanation above would be helpful.

I did figure out (mostly) what they are for, so I can see some value in 
keeping at least some of them. But as you said, they are currently 
unused, so dropping them from the structure make sense.

I have prepared patches for this firstly on the kernel side, and then I 
will make the same change in this u-boot driver. Stay tuned :-)

>>I think it happened before I started working on RZ/N1, but there 
>>seemed to be quite a few iterations on how to represent the clock 
>>tree. At one point there were macros to assign/construct the bitfield 
>>values. And then a different way, and eventually the direct hex values 
>>you now see in the clock tables.
>>
>>At the risk of re-opening old wounds (luckily not mine) I decided to 
>>just leave this part exactly as-is in the Linux driver.
>
>Can you link to that discussion? The earliest discussion of that
>series I could find was [1], and there's no mention of the encoding.
>This encoding scheme seems to be used only for this SoC, and not for
>any of the other renesas drivers. I suspect that this just wasn't
>reviewed in detail the first time around...
>
>[1] https://lore.kernel.org/all/1527154169-32380-6-git-send-email-michel.pollet@bp.renesas.com/

That link [1] is the current driver, which uses the packed encoding 
(with a register offset and bit number stored as a packed uint16_t).

This is based (loosely) on an earlier version of the driver, which you 
can find in the Schneider kernel repo [2] on the 4.19 and older branch.
This version stores clock information is in the device tree [3] and uses 
_BIT() macro in the clock tables [4]

[2] https://github.com/renesas-rz/rzn1_linux/tree/rzn1-stable-v4.19/drivers/clk/rzn1

[3] https://github.com/renesas-rz/rzn1_linux/blob/rzn1-stable-v4.19/arch/arm/boot/dts/rzn1-clocks.dtsi

[4] https://github.com/renesas-rz/rzn1_linux/blob/rzn1-stable-v4.19/drivers/clk/rzn1/rzn1-clkctrl-tables.h

Evidently this was not deemed suitable, and thus morphed into the 
version that did get merged [1]. At least that is my guess, I don't know 
for sure what transpired.

I have modified the clock table so that the register offset and bitnum 
are explicit values, rather than packed together. I will run this up the 
kernel side and see if they agree. Am still trying to test it though...


>>In fact quite a few of them are in the dt-bindings already, see 
>>include/dt-bindings/clock/r9a06g032-sysctrl.h
>>
>>I'm not really sure why some of these are defined in the .C file while 
>>others are in the dt-bindings header. Like much of the other bits, 
>>this was something I just carried over as-is from the Linux driver.
>
>I think these are "internal" clocks (that is, clocks which don't really
>exist like intermediate dividers) whereas the others are public-facing
>clocks. It's up to you, but maybe have a comment noting where the other
>ids come from.

I guess that is plausible explanation.. I will add a comment...

>>>>+    else
>>>>+        parent->id = desc->source - 1;
>>>>+
>>>>+    parent->dev = clk->dev;
>>>
>>>I think you need to clk_request here.
>>
>>Normally clk_request is called by a driver wishing to use a particular 
>>clock. That is not the case here. This is in a helper function used to 
>>compute the current rate of a given clock. It only looks at the local 
>>table (struct r9a06g032_clkdsc).
>
>You call clk_get_rate on it. Any time you "create" a new clock, you
>must call clk_request.

So the situation here is similar to that on the mediatek patches from 
Weijie Gao [5], where you made a similar comment. These are not real 
clocks, they have no ops->request, and the only field used is clk->id.
This is done primarily to avoid a bunch of malloc of struct clk, 
particularly in the early u-boot (before relocation).
  
[5] https://lore.kernel.org/all/31b0e1313267c8d342e0e3d1c9f15eaa8e666114.camel@mediatek.com/

>>It is for a different clock type. However I will see if I do something 
>>to avoid the duplication.
>
>I mean in r9a06g032_clk_get_parent_rate. You can also just do
>
>if (!parent_rate)
>	...

I've fixed this (and several similar instances elsewhere).

>>>DIV_ROUND_CLOSEST?
>>
>>I'm hesitant to change the logic on this, as it could subtly alter the values.
>
>Well if you have 2MHz divided by 3, the resulting rate is closer to
>666667 kHz than 666666 Hz.

While I can't argue with the math, the linux driver upon which this is 
based uses DIV_ROUND_UP everywhere. Maybe that is something to also 
re-consider, but for the time being, I don't want to make the change 
even more complicated...

>>Fair enough. Though I have to say I have been bitten by this kind of 
>>thing a few times. After having spent time debugging-via-printf, I 
>>would then discover an assert or dev_dbg that points out the exact 
>>problem. If only I had enabled DEBUG for that file! And yet if I 
>>enable DEBUG globally, there is so much noise, that I don't notice the 
>>one message that might have been helpful.
>
>I generally stick a `#define DEBUG` at the top of a file any time I get
>an error with no message. You still return 0, so I suggest returning
>-EINVAL (or something else uncommon) so you have somewhere to start.

Yep, that works fine when developing a driver... but if you are 
debugging a board, it is often not clear which file(s) should get 
sprinkled with DEBUG... and turning it on globally is too verbose to be 
useful in most cases.

Anyhow, thanks for your review, and v3 will be posted eventually, once I
get the kernel side sorted.

-Ralph

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v1 3/9] clk: renesas: add R906G032 driver
  2022-08-26 15:47         ` Ralph Siemsen
@ 2023-02-22 17:39           ` Ralph Siemsen
  0 siblings, 0 replies; 29+ messages in thread
From: Ralph Siemsen @ 2023-02-22 17:39 UTC (permalink / raw)
  To: Sean Anderson; +Cc: u-boot, Lukasz Majewski

Hi Sean,

I finally got around to posting v3 of this patch series. I wanted to
touch on a few issues you had mentioned in previous review.

On Fri, Aug 26, 2022 at 11:47 AM Ralph Siemsen <ralph.siemsen@linaro.org> wrote:
>
> On Tue, Aug 23, 2022 at 12:14:31AM -0400, Sean Anderson wrote:
> >>Regarding the unused fields (scon, mirack, mistat): I am not really
> >>sure what their purpose is. Maybe there is some value in having them.
> >>I'll try to find out more information about them. If we do decide to
> >>drop them, I would like to keep it synchronised with the Linux driver.
> >
> >OK, well if you don't use them then perhaps you can just leave them in
> >the macro but remove them from the struct. That way you can add support
> >for them later if you need to, but they don't take up space in the mean
> >time. A comment summarizing your explanation above would be helpful.

So I ended up doing as you suggested: keeping the values in the
tables, in case they are needed in the future, but omitting them from
the structure to save space.

> >>>DIV_ROUND_CLOSEST?
> >>
> >>I'm hesitant to change the logic on this, as it could subtly alter the values.
> >
> >Well if you have 2MHz divided by 3, the resulting rate is closer to
> >666667 kHz than 666666 Hz.

This could result in the chosen clock rate being higher than what was
requested. And in turn that could result in violating the spec of a
hardware device.

So I have kept the original logic here.

Regards
Ralph

^ permalink raw reply	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2023-02-22 17:39 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-09 12:59 [RFC PATCH v1 0/9] Renesas RZ/N1 SoC initial support Ralph Siemsen
2022-08-09 12:59 ` [RFC PATCH v1 1/9] ARM: armv7: add non-SPL enable for Cortex SMPEN Ralph Siemsen
2022-08-09 12:59 ` [RFC PATCH v1 2/9] clk: renesas: prepare for non-RCAR clock drivers Ralph Siemsen
2022-08-13  4:37   ` Sean Anderson
2022-08-09 12:59 ` [RFC PATCH v1 3/9] clk: renesas: add R906G032 driver Ralph Siemsen
2022-08-13  5:30   ` Sean Anderson
2022-08-15  2:48     ` Ralph Siemsen
2022-08-23  4:14       ` Sean Anderson
2022-08-26 15:47         ` Ralph Siemsen
2023-02-22 17:39           ` Ralph Siemsen
2022-08-09 12:59 ` [RFC PATCH v1 4/9] pinctrl: " Ralph Siemsen
2022-08-09 12:59 ` [RFC PATCH v1 5/9] ram: cadence: add driver for Cadence EDAC Ralph Siemsen
2022-08-09 12:59 ` [RFC PATCH v1 6/9] dts: basic devicetree for Renesas RZ/N1 SoC Ralph Siemsen
2022-08-09 12:59 ` [RFC PATCH v1 7/9] ARM: rzn1: basic support " Ralph Siemsen
2022-08-09 12:59 ` [RFC PATCH v1 8/9] board: schneider: add LCES board support Ralph Siemsen
2022-08-09 12:59 ` [RFC PATCH v1 9/9] tools: Add tool to create Renesas SPKG images Ralph Siemsen
2022-08-09 13:03   ` Pali Rohár
2022-08-09 13:07     ` Pali Rohár
2022-08-09 15:54       ` Ralph Siemsen
2022-08-09 16:06         ` Pali Rohár
2022-08-09 17:02           ` Ralph Siemsen
2022-08-09 17:15         ` Sean Anderson
2022-08-12 17:00           ` Ralph Siemsen
2022-08-12 17:03   ` [RFC PATCH v2 9/9] tools: spkgimage: add Renesas SPKG format Ralph Siemsen
2022-08-13 14:47     ` Sean Anderson
2022-08-14  1:45       ` Ralph Siemsen
2022-08-16 14:33         ` Ralph Siemsen
2022-08-23  3:42           ` Sean Anderson
2022-08-26 15:01             ` Ralph Siemsen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).