aboutsummaryrefslogtreecommitdiffstats
path: root/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas
diff options
context:
space:
mode:
Diffstat (limited to 'meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas')
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0110-Renesas-clk-Add-RPC-clock-source.patch263
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0111-Renesas-r8a7798-Add-RPC-clock.patch46
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0112-Renesas-r8a7798-pinctrl-Add-RPC-pin-control.patch119
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0113-Renesas-RPC-Add-RPC-driver.patch1483
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0114-R8A7798-dtsi-Add-RPC-node-to-dtsi.patch35
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0115-R8A7798-condor-dts-Add-qspi-flash.patch98
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0116-spi-nor-Add-s25fs512-flash-support.patch126
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0117-spi-nor-Add-flash-array-support.patch89
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0118-r8a7797-clk-Add-rpc-clock.patch45
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0119-r8a7797-pinctrl-Add-qspi-pins.patch118
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0120-r8a7797-dtsi-Add-rpc-node.patch35
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0121-r8a7797-eagle-dts-Add-spi-flash-node.patch96
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0122-r8a7797-v3msk-r8a7797-v3mzf-dts-Add-spi-flash-s25fs5.patch172
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0123-V3HSK-dts-Add-qspi-node.patch101
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0124-RPC-Hyperflash-Add-devicetree-support.patch219
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0125-r8a7797-pinctrl-Add-pin-function-for-hyperflash.patch70
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0126-r8a7798-pinctrl-Add-pin-function-for-hyperflash.patch69
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/qspi.cfg3
18 files changed, 3187 insertions, 0 deletions
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0110-Renesas-clk-Add-RPC-clock-source.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0110-Renesas-clk-Add-RPC-clock-source.patch
new file mode 100644
index 0000000..37d08e9
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0110-Renesas-clk-Add-RPC-clock-source.patch
@@ -0,0 +1,263 @@
+From b4acae6eda19307e0051763a532f522006a82d5c Mon Sep 17 00:00:00 2001
+From: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+Date: Mon, 19 Mar 2018 10:37:04 +0300
+Subject: [PATCH 01/12] Renesas: clk: Add RPC clock source
+
+Add RPC clock source functionality
+
+Signed-off-by: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+---
+ drivers/clk/renesas/rcar-gen3-cpg.c | 200 ++++++++++++++++++++++++++++++++++++
+ drivers/clk/renesas/rcar-gen3-cpg.h | 4 +
+ 2 files changed, 204 insertions(+)
+
+diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c
+index 99acba2..969e1b2 100644
+--- a/drivers/clk/renesas/rcar-gen3-cpg.c
++++ b/drivers/clk/renesas/rcar-gen3-cpg.c
+@@ -599,6 +599,7 @@ static const struct clk_div_table cpg_sd01_div_table[] = {
+ { 10, 36 }, { 11, 48 }, { 12, 10 }, { 0, 0 },
+ };
+
++
+ #define CPG_SD_STP_HCK BIT(9)
+ #define CPG_SD_STP_CK BIT(8)
+
+@@ -811,6 +812,202 @@ static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core,
+ return clk;
+ }
+
++/**************** RPCSCK ***********************************/
++
++
++
++struct rpc_clock {
++ struct clk_hw hw;
++ void __iomem *reg;
++ const struct clk_div_table *div_table;
++ unsigned int div_num;
++ unsigned int div_min;
++ unsigned int div_max;
++};
++
++static const struct clk_div_table cpg_rpc_div_table[] = {
++ { 0x11, 20 }, { 0x13, 40 }, { 0x15, 60 }, { 0x17, 80 },
++ { 0x19, 24 }, { 0x1B, 48 }, { 0x1D, 72 }, { 0x1F, 96 },
++};
++
++#define to_rpc_clock(_hw) container_of(_hw, struct rpc_clock, hw)
++
++#define CPG_RPC_ENA_MASK (BIT(9) | BIT(8))
++#define CPG_RPC_SCALE_MASK (0x1F)
++
++
++static int cpg_rpc_clock_endisable(void __iomem *reg, bool enable)
++{
++ u32 val;
++
++ val = readl(reg);
++
++ if (enable)
++ val &= ~CPG_RPC_ENA_MASK;
++ else
++ val |= CPG_RPC_ENA_MASK;
++
++ writel(val, reg);
++ return 0;
++};
++
++static int cpg_rpc_clock_enable(struct clk_hw *hw)
++{
++ struct rpc_clock *clock = to_rpc_clock(hw);
++
++ cpg_rpc_clock_endisable(clock->reg, true);
++
++ return 0;
++}
++
++static void cpg_rpc_clock_disable(struct clk_hw *hw)
++{
++ struct rpc_clock *clock = to_rpc_clock(hw);
++
++ cpg_rpc_clock_endisable(clock->reg, false);
++}
++
++static int cpg_rpc_clock_is_enabled(struct clk_hw *hw)
++{
++ struct rpc_clock *clock = to_rpc_clock(hw);
++
++ return !(readl(clock->reg) & CPG_RPC_ENA_MASK);
++}
++
++static unsigned long cpg_rpc_clock_recalc_rate(struct clk_hw *hw,
++ unsigned long parent_rate)
++{
++ struct rpc_clock *clock = to_rpc_clock(hw);
++ unsigned long rate = parent_rate;
++ u32 val, scale;
++ unsigned int i;
++
++ val = readl(clock->reg);
++
++ scale = val & CPG_RPC_SCALE_MASK;
++ for (i = 0; i < clock->div_num; i++)
++ if (scale == clock->div_table[i].val)
++ break;
++
++ if (i >= clock->div_num)
++ return 0;
++
++ return DIV_ROUND_CLOSEST(rate, clock->div_table[i].div);
++}
++
++static unsigned int cpg_rpc_clock_calc_div(struct rpc_clock *clock,
++ unsigned long rate,
++ unsigned long parent_rate)
++{
++ unsigned int div;
++
++ if (!rate)
++ rate = 1;
++
++ div = DIV_ROUND_CLOSEST(parent_rate, rate);
++
++ return clamp_t(unsigned int, div, clock->div_min, clock->div_max);
++}
++
++static long cpg_rpc_clock_round_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long *parent_rate)
++{
++ struct rpc_clock *clock = to_rpc_clock(hw);
++ unsigned int div = cpg_rpc_clock_calc_div(clock, rate, *parent_rate);
++
++ return DIV_ROUND_CLOSEST(*parent_rate, div);
++}
++
++static int cpg_rpc_clock_set_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long parent_rate)
++{
++ struct rpc_clock *clock = to_rpc_clock(hw);
++ unsigned int div = cpg_rpc_clock_calc_div(clock, rate, parent_rate);
++ u32 val;
++ unsigned int i, nearest_i, nearest_div;
++
++
++ for (i = 0; i < clock->div_num; i++)
++ if (div == clock->div_table[i].div)
++ break;
++
++ if (i >= clock->div_num) {
++ /* find nearest */
++ nearest_div = -1;
++ nearest_i = clock->div_num;
++ for (i = 0; i < clock->div_num; i++) {
++ if (clock->div_table[i].div >= div &&
++ clock->div_table[i].div <= nearest_div) {
++ nearest_i = i;
++ nearest_div = clock->div_table[i].div;
++ }
++ }
++
++ i = nearest_i;
++ }
++
++ if (i >= clock->div_num)
++ return -EINVAL;
++
++ val = readl(clock->reg);
++ val &= ~CPG_RPC_SCALE_MASK;
++ val |= clock->div_table[i].val;
++ writel(val, clock->reg);
++
++ return 0;
++}
++
++static const struct clk_ops cpg_rpc_clock_ops = {
++ .enable = cpg_rpc_clock_enable,
++ .disable = cpg_rpc_clock_disable,
++ .is_enabled = cpg_rpc_clock_is_enabled,
++ .recalc_rate = cpg_rpc_clock_recalc_rate,
++ .round_rate = cpg_rpc_clock_round_rate,
++ .set_rate = cpg_rpc_clock_set_rate,
++};
++
++static struct clk * __init cpg_rpc_clk_register(const struct cpg_core_clk *core,
++ void __iomem *base,
++ const char *parent_name)
++{
++ struct clk_init_data init;
++ struct rpc_clock *clock;
++ struct clk *clk;
++ unsigned int i;
++
++ clock = kzalloc(sizeof(*clock), GFP_KERNEL);
++ if (!clock)
++ return ERR_PTR(-ENOMEM);
++
++ init.name = core->name;
++ init.ops = &cpg_rpc_clock_ops;
++ init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT;
++ init.parent_names = &parent_name;
++ init.num_parents = 1;
++
++ clock->reg = base + core->offset;
++ clock->hw.init = &init;
++ clock->div_table = cpg_rpc_div_table;
++ clock->div_num = ARRAY_SIZE(cpg_rpc_div_table);
++
++ clock->div_max = clock->div_table[0].div;
++ clock->div_min = clock->div_max;
++ for (i = 1; i < clock->div_num; i++) {
++ clock->div_max = max(clock->div_max, clock->div_table[i].div);
++ clock->div_min = min(clock->div_min, clock->div_table[i].div);
++ }
++
++ clk = clk_register(NULL, &clock->hw);
++ if (IS_ERR(clk))
++ kfree(clock);
++
++ return clk;
++}
++
++/***********************************************************/
++
++
++
+
+ static const struct rcar_gen3_cpg_pll_config *cpg_pll_config __initdata;
+ static unsigned int cpg_clk_extalr __initdata;
+@@ -943,6 +1140,9 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev,
+ return cpg_zg_clk_register(core->name, __clk_get_name(parent),
+ base);
+
++ case CLK_TYPE_GEN3_RPCSRC:
++ return cpg_rpc_clk_register(core, base, __clk_get_name(parent));
++
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+diff --git a/drivers/clk/renesas/rcar-gen3-cpg.h b/drivers/clk/renesas/rcar-gen3-cpg.h
+index 694bedc..636ef54 100644
+--- a/drivers/clk/renesas/rcar-gen3-cpg.h
++++ b/drivers/clk/renesas/rcar-gen3-cpg.h
+@@ -27,6 +27,7 @@ enum rcar_gen3_clk_types {
+ CLK_TYPE_GEN3_ZG,
+ CLK_TYPE_GEN3_RINT,
+ CLK_TYPE_GEN3_OSC,
++ CLK_TYPE_GEN3_RPCSRC,
+ };
+
+ #define DEF_GEN3_SD(_name, _id, _parent, _offset) \
+@@ -36,6 +37,9 @@ enum rcar_gen3_clk_types {
+ #define DEF_GEN3_SD0H(_name, _id, _parent, _offset) \
+ DEF_BASE(_name, _id, CLK_TYPE_GEN3_SD0H, _parent, .offset = _offset)
+
++#define DEF_GEN3_RPCSRC(_name, _id, _parent, _offset) \
++ DEF_BASE(_name, _id, CLK_TYPE_GEN3_RPCSRC, _parent, .offset = _offset)
++
+ struct rcar_gen3_cpg_pll_config {
+ unsigned int extal_div;
+ unsigned int pll1_mult;
+--
+2.7.4
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0111-Renesas-r8a7798-Add-RPC-clock.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0111-Renesas-r8a7798-Add-RPC-clock.patch
new file mode 100644
index 0000000..e0fb7d2
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0111-Renesas-r8a7798-Add-RPC-clock.patch
@@ -0,0 +1,46 @@
+From 05ab83db0e0012674a0255fe37a51713a1601732 Mon Sep 17 00:00:00 2001
+From: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+Date: Mon, 19 Mar 2018 10:42:50 +0300
+Subject: [PATCH 02/12] Renesas: r8a7798: Add RPC clock
+
+Signed-off-by: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+---
+ drivers/clk/renesas/r8a7798-cpg-mssr.c | 3 +++
+ include/dt-bindings/clock/r8a7798-cpg-mssr.h | 1 +
+ 2 files changed, 4 insertions(+)
+
+diff --git a/drivers/clk/renesas/r8a7798-cpg-mssr.c b/drivers/clk/renesas/r8a7798-cpg-mssr.c
+index e407916..2614147 100644
+--- a/drivers/clk/renesas/r8a7798-cpg-mssr.c
++++ b/drivers/clk/renesas/r8a7798-cpg-mssr.c
+@@ -90,6 +90,8 @@ static const struct cpg_core_clk r8a7798_core_clks[] __initconst = {
+
+ DEF_GEN3_SD("sd0", R8A7798_CLK_SD0, CLK_SDSRC, 0x0074), /* OK? */
+
++ DEF_GEN3_RPCSRC("rpcsrc", R8A7798_CLK_RPCSRC, CLK_PLL1, 0x0238),
++
+ DEF_FIXED("cl", R8A7798_CLK_CL, CLK_PLL1_DIV2, 48, 1),
+ DEF_FIXED("cp", R8A7798_CLK_CP, CLK_EXTAL, 2, 1),
+
+@@ -194,6 +196,7 @@ static const struct mssr_mod_clk r8a7798_mod_clks[] __initconst = {
+ DEF_MOD("gpio1", 911, R8A7798_CLK_CP),
+ DEF_MOD("gpio0", 912, R8A7798_CLK_CP),
+ DEF_MOD("can-fd", 914, R8A7798_CLK_S3D2),
++ DEF_MOD("rpc", 917, R8A7798_CLK_RPCSRC),
+ /* FIXME missing MSSR for i2c5; should it be 919 as in H3/M3? */
+ /* DEF_MOD("i2c4", 919, R8A7798_CLK_S3D2), */
+ DEF_MOD("i2c4", 927, R8A7798_CLK_S0D6),
+diff --git a/include/dt-bindings/clock/r8a7798-cpg-mssr.h b/include/dt-bindings/clock/r8a7798-cpg-mssr.h
+index 3d85730..821383d3 100644
+--- a/include/dt-bindings/clock/r8a7798-cpg-mssr.h
++++ b/include/dt-bindings/clock/r8a7798-cpg-mssr.h
+@@ -52,5 +52,6 @@
+ #define R8A7798_CLK_CPEX 36
+ #define R8A7798_CLK_R 37
+ #define R8A7798_CLK_OSC 38
++#define R8A7798_CLK_RPCSRC 39
+
+ #endif /* __DT_BINDINGS_CLOCK_R8A7798_CPG_MSSR_H__ */
+--
+2.7.4
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0112-Renesas-r8a7798-pinctrl-Add-RPC-pin-control.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0112-Renesas-r8a7798-pinctrl-Add-RPC-pin-control.patch
new file mode 100644
index 0000000..5c91f56
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0112-Renesas-r8a7798-pinctrl-Add-RPC-pin-control.patch
@@ -0,0 +1,119 @@
+From ba6a829c33809d780c40c3f60e97e8a9e2a99a92 Mon Sep 17 00:00:00 2001
+From: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+Date: Mon, 19 Mar 2018 10:48:44 +0300
+Subject: [PATCH 03/12] Renesas: r8a7798: pinctrl: Add RPC pin control
+
+Signed-off-by: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+---
+ drivers/pinctrl/sh-pfc/pfc-r8a7798.c | 74 ++++++++++++++++++++++++++++++++++++
+ 1 file changed, 74 insertions(+)
+
+diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7798.c b/drivers/pinctrl/sh-pfc/pfc-r8a7798.c
+index 39aba74..f20afd1 100644
+--- a/drivers/pinctrl/sh-pfc/pfc-r8a7798.c
++++ b/drivers/pinctrl/sh-pfc/pfc-r8a7798.c
+@@ -1472,6 +1472,60 @@ static const unsigned int scif_clk_b_mux[] = {
+ SCIF_CLK_B_MARK,
+ };
+
++/* - QSPI ------------------------------------------------------------------- */
++static const unsigned int qspi0_ctrl_pins[] = {
++ /* QSPI0_SPCLK QSPI0_SSL */
++ RCAR_GP_PIN(5, 5), RCAR_GP_PIN(5, 0),
++};
++static const unsigned int qspi0_ctrl_mux[] = {
++ QSPI0_SPCLK_MARK, QSPI0_SSL_MARK,
++};
++
++static const unsigned int qspi0_data2_pins[] = {
++ /* QSPI0_MOSI_IO0, QSPI0_MISO_IO1 */
++ RCAR_GP_PIN(5, 1), RCAR_GP_PIN(5, 2),
++};
++static const unsigned int qspi0_data2_mux[] = {
++ QSPI0_MOSI_IO0_MARK, QSPI0_MISO_IO1_MARK,
++};
++
++static const unsigned int qspi0_data4_pins[] = {
++ /* QSPI0_MOSI_IO0, QSPI0_MISO_IO1, QSPI0_IO2, QSPI0_IO3 */
++ RCAR_GP_PIN(5, 1), RCAR_GP_PIN(5, 2),
++ RCAR_GP_PIN(5, 3), RCAR_GP_PIN(5, 4),
++};
++static const unsigned int qspi0_data4_mux[] = {
++ QSPI0_MOSI_IO0_MARK, QSPI0_MISO_IO1_MARK,
++ QSPI0_IO2_MARK, QSPI0_IO3_MARK
++};
++
++static const unsigned int qspi1_ctrl_pins[] = {
++ /* QSPI1_SPCLK QSPI1_SSL */
++ RCAR_GP_PIN(5, 6), RCAR_GP_PIN(5, 11),
++};
++static const unsigned int qspi1_ctrl_mux[] = {
++ QSPI1_SPCLK_MARK, QSPI1_SSL_MARK,
++};
++
++static const unsigned int qspi1_data2_pins[] = {
++ /* QSPI1_MOSI_IO0, QSPI1_MISO_IO1 */
++ RCAR_GP_PIN(5, 7), RCAR_GP_PIN(5, 8),
++};
++static const unsigned int qspi1_data2_mux[] = {
++ QSPI1_MOSI_IO0_MARK, QSPI1_MISO_IO1_MARK,
++};
++
++static const unsigned int qspi1_data4_pins[] = {
++ /* QSPI1_MOSI_IO0, QSPI1_MISO_IO1, QSPI1_IO2, QSPI1_IO3 */
++ RCAR_GP_PIN(5, 7), RCAR_GP_PIN(5, 8),
++ RCAR_GP_PIN(5, 9), RCAR_GP_PIN(5, 10),
++};
++static const unsigned int qspi1_data4_mux[] = {
++ QSPI1_MOSI_IO0_MARK, QSPI1_MISO_IO1_MARK,
++ QSPI1_IO2_MARK, QSPI1_IO3_MARK
++};
++
++
+ /* - I2C -------------------------------------------------------------------- */
+ static const unsigned int i2c0_pins[] = {
+ /* SDA0, SCL0 */
+@@ -2419,6 +2473,12 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
+ SH_PFC_PIN_GROUP(vin1_field),
+ SH_PFC_PIN_GROUP(vin1_clkenb),
+ SH_PFC_PIN_GROUP(vin1_clk),
++ SH_PFC_PIN_GROUP(qspi0_ctrl),
++ SH_PFC_PIN_GROUP(qspi0_data2),
++ SH_PFC_PIN_GROUP(qspi0_data4),
++ SH_PFC_PIN_GROUP(qspi1_ctrl),
++ SH_PFC_PIN_GROUP(qspi1_data2),
++ SH_PFC_PIN_GROUP(qspi1_data4),
+ };
+
+ static const char * const avb_groups[] = {
+@@ -2683,6 +2743,18 @@ static const char * const vin1_groups[] = {
+ "vin1_clk",
+ };
+
++static const char * const qspi0_groups[] = {
++ "qspi0_ctrl",
++ "qspi0_data2",
++ "qspi0_data4",
++};
++
++static const char * const qspi1_groups[] = {
++ "qspi1_ctrl",
++ "qspi1_data2",
++ "qspi1_data4",
++};
++
+ static const struct sh_pfc_function pinmux_functions[] = {
+ SH_PFC_FUNCTION(avb),
+ SH_PFC_FUNCTION(gether),
+@@ -2719,6 +2791,8 @@ static const struct sh_pfc_function pinmux_functions[] = {
+ SH_PFC_FUNCTION(tmu),
+ SH_PFC_FUNCTION(vin0),
+ SH_PFC_FUNCTION(vin1),
++ SH_PFC_FUNCTION(qspi0),
++ SH_PFC_FUNCTION(qspi1),
+ };
+
+ static const struct pinmux_cfg_reg pinmux_config_regs[] = {
+--
+2.7.4
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0113-Renesas-RPC-Add-RPC-driver.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0113-Renesas-RPC-Add-RPC-driver.patch
new file mode 100644
index 0000000..86b2d6c
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0113-Renesas-RPC-Add-RPC-driver.patch
@@ -0,0 +1,1483 @@
+From 931484f9bd4eb1741588dc1574080fa4534ac5dc Mon Sep 17 00:00:00 2001
+From: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+Date: Mon, 19 Mar 2018 11:08:16 +0300
+Subject: [PATCH 04/12] Renesas: RPC: Add RPC driver
+
+This is initial commit. Driver supports single and dual mode.
+Dual mode means that there are two spi-nor flashes connected.
+Supports qspi flashes.
+
+Signed-off-by: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+---
+ drivers/mtd/spi-nor/Kconfig | 8 +
+ drivers/mtd/spi-nor/Makefile | 1 +
+ drivers/mtd/spi-nor/renesas-rpc.c | 1429 +++++++++++++++++++++++++++++++++++++
+ 3 files changed, 1438 insertions(+)
+ create mode 100644 drivers/mtd/spi-nor/renesas-rpc.c
+
+diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
+index 4a682ee..298dc1d 100644
+--- a/drivers/mtd/spi-nor/Kconfig
++++ b/drivers/mtd/spi-nor/Kconfig
+@@ -7,6 +7,14 @@ menuconfig MTD_SPI_NOR
+
+ if MTD_SPI_NOR
+
++
++config SPI_RENESAS_RPC
++ tristate "Renesas RPC controller"
++ depends on ARCH_R8A7795 || ARCH_R8A7796 || ARCH_R8A7797 || ARCH_R8A7798 || COMPILE_TEST
++ help
++ QSPI driver for Renesas SPI Multi I/O Bus controller. This controller
++ supports normal, dual and quad spi for one or two SPI NOR Flashes.
++
+ config MTD_MT81xx_NOR
+ tristate "Mediatek MT81xx SPI NOR flash controller"
+ depends on HAS_IOMEM
+diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile
+index 121695e..e99d775 100644
+--- a/drivers/mtd/spi-nor/Makefile
++++ b/drivers/mtd/spi-nor/Makefile
+@@ -5,3 +5,4 @@ obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
+ obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o
+ obj-$(CONFIG_MTD_MT81xx_NOR) += mtk-quadspi.o
+ obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o
++obj-$(CONFIG_SPI_RENESAS_RPC) += renesas-rpc.o
+diff --git a/drivers/mtd/spi-nor/renesas-rpc.c b/drivers/mtd/spi-nor/renesas-rpc.c
+new file mode 100644
+index 0000000..1e93c98
+--- /dev/null
++++ b/drivers/mtd/spi-nor/renesas-rpc.c
+@@ -0,0 +1,1429 @@
++/*
++ * Renesas RPC driver
++ *
++ * Copyright (C) 2018, Cogent Embedded Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/clk.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++#include <linux/mtd/spi-nor.h>
++
++
++
++#define CMNCR 0x0000
++#define SSLDR 0x0004
++#define DRCR 0x000C
++#define DRCMR 0x0010
++#define DREAR 0x0014
++#define DROPR 0x0018
++#define DRENR 0x001C
++#define SMCR 0x0020
++#define SMCMR 0x0024
++#define SMADR 0x0028
++#define SMOPR 0x002C
++#define SMENR 0x0030
++#define SMRDR0 0x0038
++#define SMRDR1 0x003C
++#define SMWDR0 0x0040
++#define SMWDR1 0x0044
++#define CMNSR 0x0048
++#define DRDMCR 0x0058
++#define DRDRENR 0x005C
++#define SMDMCR 0x0060
++#define SMDRENR 0x0064
++#define PHYCNT 0x007C
++#define PHYOFFSET1 0x0080
++#define PHYOFFSET2 0x0084
++#define PHYINT 0x0088
++#define DIV_REG 0x00A8
++
++/* CMNCR */
++#define CMNCR_BSZ_MASK (0x03)
++#define CMNCR_BSZ_4x1 (0x0)
++#define CMNCR_BSZ_8x1 (0x1)
++#define CMNCR_BSZ_4x2 (0x1)
++#define CMNCR_MD (0x1 << 31)
++#define CMNCR_MOIIO3_MASK (0x3 << 22)
++#define CMNCR_MOIIO3_HIZ (0x3 << 22)
++#define CMNCR_MOIIO2_MASK (0x3 << 20)
++#define CMNCR_MOIIO2_HIZ (0x3 << 20)
++#define CMNCR_MOIIO1_MASK (0x3 << 18)
++#define CMNCR_MOIIO1_HIZ (0x3 << 18)
++#define CMNCR_MOIIO0_MASK (0x3 << 16)
++#define CMNCR_MOIIO0_HIZ (0x3 << 16)
++#define CMNCR_IO0FV_MASK (0x3 << 8)
++#define CMNCR_IO0FV_HIZ (0x3 << 8)
++
++
++/* DRCR */
++#define DRCR_RBURST_MASK (0x1f << 16)
++#define DRCR_RBURST(v) (((v) & 0x1f) << 16)
++#define DRCR_SSLE (0x1)
++#define DRCR_RBE (0x1 << 8)
++#define DRCR_RCF (0x1 << 9)
++#define DRCR_RBURST_32 (0x1f << 16)
++
++
++/* SMENR */
++#define SMENR_CDB_MASK (0x03 << 30)
++#define SMENR_CDB(v) (((v) & 0x03) << 30)
++#define SMENR_CDB_1B (0)
++#define SMENR_CDB_2B (0x1 << 30)
++#define SMENR_CDB_4B (0x2 << 30)
++#define SMENR_OCDB_MASK (0x03 << 28)
++#define SMENR_OCDB_1B (0)
++#define SMENR_OCDB_2B (0x1 << 28)
++#define SMENR_OCDB_4B (0x2 << 28)
++#define SMENR_ADB_MASK (0x03 << 24)
++#define SMENR_ADB(v) (((v) & 0x03) << 24)
++#define SMENR_ADB_1B (0)
++#define SMENR_ADB_2B (0x1 << 24)
++#define SMENR_ADB_4B (0x2 << 24)
++#define SMENR_OPDB_MASK (0x03 << 20)
++#define SMENR_OPDB_1B (0)
++#define SMENR_OPDB_2B (0x1 << 20)
++#define SMENR_OPDB_4B (0x2 << 20)
++#define SMENR_SPIDB_MASK (0x03 << 16)
++#define SMENR_SPIDB(v) (((v) & 0x03) << 16)
++#define SMENR_SPIDB_1B (0)
++#define SMENR_SPIDB_2B (0x1 << 16)
++#define SMENR_SPIDB_4B (0x2 << 16)
++#define SMENR_OPDE_MASK (0xf << 4)
++#define SMENR_OPDE_DISABLE (0)
++#define SMENR_OPDE3 (0x8 << 4)
++#define SMENR_OPDE32 (0xC << 4)
++#define SMENR_OPDE321 (0xE << 4)
++#define SMENR_OPDE3210 (0xF << 4)
++#define SMENR_SPIDE_MASK (0x0F)
++#define SMENR_SPIDE_DISABLE (0)
++#define SMENR_SPIDE_8B (0x08)
++#define SMENR_SPIDE_16B (0x0C)
++#define SMENR_SPIDE_32B (0x0F)
++#define SMENR_DME (1<<15)
++#define SMENR_CDE (1<<14)
++#define SMENR_OCDE (1<<12)
++#define SMENR_ADE_MASK (0xf << 8)
++#define SMENR_ADE_DISABLE (0)
++#define SMENR_ADE_23_16 (0x4 << 8)
++#define SMENR_ADE_23_8 (0x6 << 8)
++#define SMENR_ADE_23_0 (0x7 << 8)
++#define SMENR_ADE_31_0 (0xf << 8)
++
++
++/* SMCMR */
++#define SMCMR_CMD(cmd) (((cmd) & 0xff) << 16)
++#define SMCMR_CMD_MASK (0xff << 16)
++#define SMCMR_OCMD(cmd) (((cmd) & 0xff))
++#define SMCMR_OCMD_MASK (0xff)
++
++
++/* SMDRENR */
++#define SMDRENR_HYPE_MASK (0x7 << 12)
++#define SMDRENR_HYPE_SPI_FLASH (0x0)
++#define SMDRENR_ADDRE (0x1 << 8)
++#define SMDRENR_OPDRE (0x1 << 4)
++#define SMDRENR_SPIDRE (0x1)
++
++/* PHYCNT */
++#define PHYCNT_CAL (0x1 << 31)
++#define PHYCNT_OCTA_MASK (0x3 << 22)
++#define PHYCNT_EXDS (0x1 << 21)
++#define PHYCNT_OCT (0x1 << 20)
++#define PHYCNT_DDRCAL (0x1 << 19)
++#define PHYCNT_HS (0x1 << 18)
++#define PHYCNT_STREAM_MASK (0x7 << 15)
++#define PHYCNT_STREAM(o) (((o) & 0x7) << 15)
++#define PHYCNT_WBUF2 (0x1 << 4)
++#define PHYCNT_WBUF (0x1 << 2)
++#define PHYCNT_PHYMEM_MASK (0x3)
++
++/* SMCR */
++#define SMCR_SSLKP (0x1 << 8)
++#define SMCR_SPIRE (0x1 << 2)
++#define SMCR_SPIWE (0x1 << 1)
++#define SMCR_SPIE (0x1)
++
++/* CMNSR */
++#define CMNSR_TEND (0x1 << 0)
++
++/* SSLDR */
++#define SSLDR_SPNDL(v) (((v) & 0x7) << 16)
++#define SSLDR_SLNDL(v) ((((v) | 0x4) & 0x7) << 8)
++#define SSLDR_SCKDL(v) ((v) & 0x7)
++
++/* DREAR */
++#define DREAR_EAV_MASK (0xff << 16)
++#define DREAR_EAV(v) (((v) & 0xff) << 16)
++#define DREAR_EAC_MASK (0x7)
++#define DREAR_24B (0)
++#define DREAR_25B (1)
++
++/* DRENR */
++#define DRENR_CDB_MASK (0x03 << 30)
++#define DRENR_CDB(v) (((v) & 0x3) << 30)
++#define DRENR_CDB_1B (0)
++#define DRENR_CDB_2B (0x1 << 30)
++#define DRENR_CDB_4B (0x2 << 30)
++#define DRENR_OCDB_MASK (0x03 << 28)
++#define DRENR_OCDB_1B (0)
++#define DRENR_OCDB_2B (0x1 << 28)
++#define DRENR_OCDB_4B (0x2 << 28)
++#define DRENR_ADB_MASK (0x03 << 24)
++#define DRENR_ADB(v) (((v) & 0x3) << 24)
++#define DRENR_ADB_1B (0)
++#define DRENR_ADB_2B (0x1 << 24)
++#define DRENR_ADB_4B (0x2 << 24)
++#define DRENR_OPDB_MASK (0x03 << 20)
++#define DRENR_OPDB_1B (0)
++#define DRENR_OPDB_2B (0x1 << 20)
++#define DRENR_OPDB_4B (0x2 << 20)
++#define DRENR_DRDB_MASK (0x03 << 16)
++#define DRENR_DRDB(v) (((v) & 0x3) << 16)
++#define DRENR_DRDB_1B (0)
++#define DRENR_DRDB_2B (0x1 << 16)
++#define DRENR_DRDB_4B (0x2 << 16)
++#define DRENR_OPDE_MASK (0xf << 4)
++#define DRENR_OPDE_DISABLE (0)
++#define DRENR_OPDE3 (0x8 << 4)
++#define DRENR_OPDE32 (0xC << 4)
++#define DRENR_OPDE321 (0xE << 4)
++#define DRENR_OPDE3210 (0xF << 4)
++#define DRENR_DME (1<<15)
++#define DRENR_CDE (1<<14)
++#define DRENR_OCDE (1<<12)
++#define DRENR_ADE_MASK (0xf << 8)
++#define DRENR_ADE_DISABLE (0)
++#define DRENR_ADE_23_0 (0x7 << 8)
++#define DRENR_ADE_31_0 (0xf << 8)
++
++/* DRCMR */
++#define DRCMR_CMD(cmd) (((cmd) & 0xff) << 16)
++#define DRCMR_CMD_MASK (0xff << 16)
++#define DRCMR_OCMD(cmd) (((cmd) & 0xff))
++#define DRCMR_OCMD_MASK (0xff)
++
++/* DRCMR */
++#define DRDMCR_DMCYC(v) ((v) & 0x1f)
++#define DRDMCR_DMCYC_MASK (0x1f)
++
++/* SMDMCR */
++#define SMDMCR_DMCYC(v) ((v) & 0x0f)
++#define SMDMCR_DMCYC_MASK (0x0f)
++
++/* PHYOFFSET1 */
++#define PHYOFFSET1_DDRTMG (1 << 28)
++
++/* DIVREG */
++#define DIVREG_RATIO_MASK (0x03)
++#define DIVREG_RATIO(v) ((v) & 0x03)
++#define DIVREG_RATIO_MAX (0x2)
++
++
++#define DEFAULT_TO (100)
++#define WRITE_BUF_SIZE (0x100)
++#define WRITE_BUF_ADR_MASK (0xff)
++
++#define REPEAT_MAX (20)
++#define REPEAT_TIME (10)
++
++
++struct rpc_spi {
++ struct platform_device *pdev;
++ void __iomem *base;
++ void __iomem *read_area;
++ void __iomem *write_area;
++ struct clk *clk;
++ unsigned int irq;
++ struct spi_nor spi_nor;
++
++#define MTD_QSPI_1x 0
++#define MTD_QSPI_2x 1
++
++ u32 mtdtype;
++};
++
++
++
++/* IP block use it's own clock divigion register */
++#define OWN_CLOCK_DIVIDER BIT(0)
++
++
++
++static void regs_dump(struct rpc_spi *rpc)
++{
++ static u32 regs[] = {
++ CMNCR, SSLDR, DRCR, DRCMR, DREAR,
++ DROPR, DRENR, SMCR, SMCMR, SMADR,
++ SMOPR, SMENR, SMRDR0, SMRDR1, SMWDR0,
++ SMWDR1, CMNSR, DRDMCR, DRDRENR, SMDMCR,
++ SMDRENR, PHYCNT, PHYOFFSET1, PHYOFFSET2,
++ PHYINT
++ };
++
++ static const char *const names[] = {
++ "CMNCR", "SSLDR", "DRCR", "DRCMR", "DREAR",
++ "DROPR", "DRENR", "SMCR", "SMCMR", "SMADR",
++ "SMOPR", "SMENR", "SMRDR0", "SMRDR1", "SMWDR0",
++ "SMWDR1", "CMNSR", "DRDMCR", "DRDRENR", "SMDMCR",
++ "SMDRENR", "PHYCNT", "PHYOFFSET1", "PHYOFFSET2",
++ "PHYINT"
++ };
++
++ int i;
++
++ dev_dbg(&rpc->pdev->dev, "RPC regs dump:\n");
++ for (i = 0; i < ARRAY_SIZE(regs); i++)
++ dev_dbg(&rpc->pdev->dev, "%s = 0x%08x\n", names[i],
++ readl(rpc->base + regs[i]));
++}
++
++
++
++
++
++static u32 rpc_read(struct rpc_spi *rpc, unsigned int reg)
++{
++ u32 val;
++
++ val = readl(rpc->base + reg);
++ return val;
++}
++
++static void rpc_write(struct rpc_spi *rpc, unsigned int reg, u32 val)
++{
++ writel(val, rpc->base + reg);
++}
++
++
++static int rpc_wait(struct rpc_spi *rpc, u32 to)
++{
++ u32 val;
++ int i;
++
++ for (i = 0; i < to; i++) {
++ val = rpc_read(rpc, CMNSR);
++ val &= CMNSR_TEND;
++ if (val)
++ break;
++
++ udelay(100);
++ }
++
++ if (i == to) {
++ dev_err(&rpc->pdev->dev, "timeout waiting for operation end %d\n",
++ rpc_read(rpc, CMNSR));
++ return -ETIMEDOUT;
++ }
++
++ return 0;
++}
++
++
++
++
++
++
++
++
++
++
++
++
++
++static int rpc_setup_clk_ratio(struct rpc_spi *rpc, u32 max_clk_rate)
++{
++ unsigned long rate = clk_get_rate(rpc->clk);
++ u32 ratio;
++ u32 val;
++
++ ratio = DIV_ROUND_UP(rate, max_clk_rate * 2) >> 1;
++ if (ratio > DIVREG_RATIO_MAX)
++ ratio = DIVREG_RATIO_MAX;
++
++ val = rpc_read(rpc, DIV_REG);
++ val &= DIVREG_RATIO_MASK;
++ val |= DIVREG_RATIO(ratio);
++ rpc_write(rpc, DIV_REG, val);
++
++ return 0;
++}
++
++static int rpc_endisable_write_buf(struct rpc_spi *rpc, bool en)
++{
++ u32 val;
++
++ val = rpc_read(rpc, PHYCNT);
++
++ if (en)
++ val |= PHYCNT_WBUF | PHYCNT_WBUF2;
++ else
++ val &= ~(PHYCNT_WBUF | PHYCNT_WBUF2);
++
++ rpc_write(rpc, PHYCNT, val);
++
++ return 0;
++}
++
++
++static int rpc_begin(struct rpc_spi *rpc,
++ bool rx, bool tx, bool last)
++{
++ u32 val = SMCR_SPIE;
++
++ if (rx)
++ val |= SMCR_SPIRE;
++
++ if (tx)
++ val |= SMCR_SPIWE;
++
++ if (!last)
++ val |= SMCR_SSLKP;
++
++ rpc_write(rpc, SMCR, val);
++
++ return 0;
++}
++
++
++static int rpc_setup_reg_mode(struct rpc_spi *rpc)
++{
++ u32 val;
++
++ rpc_wait(rpc, DEFAULT_TO);
++
++ rpc_endisable_write_buf(rpc, false);
++
++ /* ...setup manual mode */
++ val = rpc_read(rpc, CMNCR);
++ val |= CMNCR_MD;
++ rpc_write(rpc, CMNCR, val);
++
++ /* disable ddr */
++ val = rpc_read(rpc, SMDRENR);
++ val &= ~(SMDRENR_ADDRE | SMDRENR_OPDRE | SMDRENR_SPIDRE);
++ rpc_write(rpc, SMDRENR, val);
++
++ /* enable 1bit command */
++ val = rpc_read(rpc, SMENR);
++ val &= ~(SMENR_CDB_MASK | SMENR_OCDB_MASK | SMENR_DME
++ | SMENR_OCDE | SMENR_SPIDB_MASK
++ | SMENR_ADE_MASK | SMENR_ADB_MASK
++ | SMENR_OPDE_MASK | SMENR_SPIDE_MASK);
++ val |= SMENR_CDB_1B | SMENR_CDE | SMENR_SPIDE_32B;
++ rpc_write(rpc, SMENR, val);
++
++
++ return 0;
++}
++
++static void rpc_flush_cache(struct rpc_spi *rpc)
++{
++ u32 val;
++
++ val = rpc_read(rpc, DRCR);
++ val |= DRCR_RCF;
++
++ rpc_write(rpc, DRCR, val);
++}
++
++
++static int rpc_setup_ext_mode(struct rpc_spi *rpc)
++{
++ u32 val;
++ u32 cmncr;
++
++ rpc_wait(rpc, DEFAULT_TO);
++
++ rpc_endisable_write_buf(rpc, false);
++
++ /* ...setup ext mode */
++ val = rpc_read(rpc, CMNCR);
++ cmncr = val;
++ val &= ~(CMNCR_MD);
++ rpc_write(rpc, CMNCR, val);
++
++ /* ...enable burst and clear cache */
++ val = rpc_read(rpc, DRCR);
++ val &= ~(DRCR_RBURST_MASK | DRCR_RBE | DRCR_SSLE);
++ val |= DRCR_RBURST(0x1f) | DRCR_RBE;
++
++ if (cmncr & CMNCR_MD)
++ val |= DRCR_RCF;
++
++ rpc_write(rpc, DRCR, val);
++
++ return 0;
++}
++
++
++static int rpc_setup_data_size(struct rpc_spi *rpc, u32 size, bool copy)
++{
++ u32 val;
++
++ val = rpc_read(rpc, SMENR);
++ val &= ~(SMENR_SPIDE_MASK);
++
++ if (rpc->mtdtype == MTD_QSPI_2x && !copy)
++ size >>= 1;
++
++ switch (size) {
++ case 0:
++ break;
++ case 1:
++ val |= SMENR_SPIDE_8B;
++ break;
++ case 2:
++ val |= SMENR_SPIDE_16B;
++ break;
++ case 4:
++ val |= SMENR_SPIDE_32B;
++ break;
++ default:
++ dev_err(&rpc->pdev->dev, "Unsupported data width %d\n", size);
++ return -EINVAL;
++ }
++ rpc_write(rpc, SMENR, val);
++
++ return 0;
++}
++
++
++static int rpc_setup_extmode_read_addr(struct rpc_spi *rpc,
++ int adr_width, loff_t adr)
++{
++ u32 val;
++ u32 v;
++
++ val = rpc_read(rpc, DREAR);
++ val &= ~(DREAR_EAV_MASK | DREAR_EAC_MASK);
++
++ if (adr_width == 4) {
++ v = adr >> 25;
++ val |= DREAR_EAV(v) | DREAR_25B;
++ }
++ rpc_write(rpc, DREAR, val);
++
++ val = rpc_read(rpc, DRENR);
++ val &= ~(DRENR_ADE_MASK);
++ if (adr_width == 4)
++ val |= DRENR_ADE_31_0;
++ else
++ val |= DRENR_ADE_23_0;
++ rpc_write(rpc, DRENR, val);
++
++ return 0;
++}
++
++
++
++#define NBITS_TO_VAL(v) ((v >> 1) & 3)
++static int rpc_setup_extmode_nbits(struct rpc_spi *rpc, int cnb,
++ int anb, int dnb)
++{
++ u32 val;
++
++ val = rpc_read(rpc, DRENR);
++ val &= ~(DRENR_CDB_MASK | DRENR_ADB_MASK | DRENR_DRDB_MASK);
++ val |= DRENR_CDB(NBITS_TO_VAL(cnb))
++ | DRENR_ADB(NBITS_TO_VAL(anb))
++ | DRENR_DRDB(NBITS_TO_VAL(dnb));
++ rpc_write(rpc, DRENR, val);
++
++ return 0;
++}
++
++static int rpc_setup_writemode_nbits(struct rpc_spi *rpc, int cnb,
++ int anb, int dnb)
++{
++ u32 val;
++
++ val = rpc_read(rpc, SMENR);
++ val &= ~(SMENR_CDB_MASK | SMENR_ADB_MASK | SMENR_SPIDB_MASK);
++ val |= SMENR_CDB(NBITS_TO_VAL(cnb))
++ | SMENR_ADB(NBITS_TO_VAL(anb))
++ | SMENR_SPIDB(NBITS_TO_VAL(dnb));
++ rpc_write(rpc, SMENR, val);
++
++ return 0;
++}
++
++static void rpc_setup_write_mode_command_and_adr(struct rpc_spi *rpc,
++ int adr_width, bool ena)
++{
++ u32 val;
++
++ val = rpc_read(rpc, SMENR);
++ val &= ~(SMENR_CDB_MASK | SMENR_CDE | SMENR_ADE_MASK);
++
++ if (ena) {
++ /* enable 1bit command */
++ val |= SMENR_CDB_1B | SMENR_CDE;
++
++ if (adr_width == 4)
++ val |= SMENR_ADE_31_0;
++ else
++ val |= SMENR_ADE_23_0;
++ }
++ rpc_write(rpc, SMENR, val);
++}
++
++static int rpc_setup_write_mode(struct rpc_spi *rpc)
++{
++ u32 val;
++
++ rpc_wait(rpc, DEFAULT_TO);
++
++ rpc_endisable_write_buf(rpc, true);
++
++ /* ...setup manual mode */
++ val = rpc_read(rpc, CMNCR);
++ val |= CMNCR_MD;
++ rpc_write(rpc, CMNCR, val);
++
++ /* disable ddr */
++ val = rpc_read(rpc, SMDRENR);
++ val &= ~(SMDRENR_ADDRE | SMDRENR_OPDRE | SMDRENR_SPIDRE);
++ rpc_write(rpc, SMDRENR, val);
++
++ val = rpc_read(rpc, SMENR);
++ val &= ~(SMENR_OCDB_MASK | SMENR_DME | SMENR_OCDE | SMENR_SPIDB_MASK
++ | SMENR_ADB_MASK | SMENR_OPDE_MASK | SMENR_SPIDE_MASK);
++ val |= SMENR_SPIDE_32B;
++ rpc_write(rpc, SMENR, val);
++
++ return 0;
++}
++
++static void rpc_read_manual_data(struct rpc_spi *rpc, u32 *pv0, u32 *pv1)
++{
++ u32 val0, val1, rd0, rd1;
++
++ val0 = rpc_read(rpc, SMRDR0);
++ val1 = rpc_read(rpc, SMRDR1);
++
++ if (rpc->mtdtype == MTD_QSPI_2x) {
++ rd1 = (val0 & 0xff000000) | ((val0 << 8) & 0xff0000) |
++ ((val1 >> 16) & 0xff00) | ((val1 >> 8) & 0xff);
++ rd0 = ((val0 & 0xff0000) << 8) | ((val0 << 16) & 0xff0000) |
++ ((val1 >> 8) & 0xff00) | (val1 & 0xff);
++ } else
++ rd0 = val0;
++
++ if (pv0)
++ *pv0 = rd0;
++
++ if (pv1 && rpc->mtdtype == MTD_QSPI_2x)
++ *pv1 = rd1;
++}
++
++
++static int rpc_datalen2trancfersize(struct rpc_spi *rpc, int len, bool copy)
++{
++ int sz = len;
++
++ if (len >= 2)
++ sz = 2;
++
++ if (len >= 4)
++ sz = 4;
++
++ if (rpc->mtdtype == MTD_QSPI_2x
++ && len >= 8 && !copy)
++ sz = 8;
++
++ return sz;
++}
++
++static int __rpc_write_data2reg(struct rpc_spi *rpc, int off,
++ const u8 *buf, int sz)
++{
++ const u32 *b32 = (const u32 *)buf;
++ const u16 *b16 = (const u16 *)buf;
++
++ if (sz == 4)
++ rpc_write(rpc, off, *b32);
++ else if (sz == 2)
++ writew(*b16, rpc->base+off);
++ else if (sz == 1)
++ writeb(*buf, rpc->base+off);
++ else if (sz != 0) {
++ dev_err(&rpc->pdev->dev, "incorrect data size %d\n", sz);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++#define __SETVAL(x) ((((x) & 0xff) << 8) | ((x) & 0xff))
++static int rpc_write_data2reg(struct rpc_spi *rpc, const u8 *buf,
++ int sz, bool copy)
++{
++ int i, ret;
++ u32 v = 0;
++
++ if (rpc->mtdtype == MTD_QSPI_2x) {
++ if (copy) {
++ for (i = 0; i < sz && i < 2; i++)
++ v |= (__SETVAL(buf[i]) << 16*i);
++
++ ret = __rpc_write_data2reg(rpc,
++ sz == 4 ? SMWDR1 : SMWDR0,
++ (u8 *)&v,
++ sz == 4 ? sz : sz * 2);
++ if (ret)
++ return ret;
++
++ v = 0;
++ for (; i < sz; i++)
++ v |= (__SETVAL(buf[i]) << 16*i);
++
++
++ ret = __rpc_write_data2reg(rpc,
++ sz == 4 ? SMWDR0 : SMWDR1,
++ (u8 *)&v,
++ sz == 4 ? sz : sz * 2);
++ if (ret)
++ return ret;
++
++ return 0;
++ }
++
++ sz >>= 1;
++ ret = __rpc_write_data2reg(rpc,
++ sz == 4 ? SMWDR1 : SMWDR0,
++ buf,
++ sz == 4 ? sz : sz * 2);
++ if (ret)
++ return ret;
++ buf += sz;
++
++ return __rpc_write_data2reg(rpc,
++ sz == 4 ? SMWDR0 : SMWDR1,
++ buf, sz == 4 ? sz : sz * 2);
++ }
++
++ return __rpc_write_data2reg(rpc, SMWDR0, buf, sz);
++}
++
++static ssize_t rpc_write_unaligned(struct spi_nor *nor, loff_t to, size_t len,
++ const u_char *buf, size_t fullen)
++{
++ int ret = len, dsize;
++ struct rpc_spi *rpc = nor->priv;
++ bool copy = false, last;
++ loff_t _to;
++
++ rpc_endisable_write_buf(rpc, false);
++
++ while (len > 0) {
++ _to = to;
++ if (rpc->mtdtype == MTD_QSPI_2x)
++ _to >>= 1;
++ rpc_write(rpc, SMADR, _to);
++ dsize = rpc_datalen2trancfersize(rpc, len, copy);
++
++ if (rpc_setup_data_size(rpc, dsize, copy))
++ return -EINVAL;
++
++ rpc_write_data2reg(rpc, buf, dsize, copy);
++
++ last = (len <= dsize && fullen <= ret);
++ rpc_begin(rpc, false, true, last);
++ if (rpc_wait(rpc, DEFAULT_TO))
++ return -ETIMEDOUT;
++
++ /* ...disable command */
++ rpc_setup_write_mode_command_and_adr(rpc,
++ nor->addr_width, false);
++
++ buf += dsize;
++ len -= dsize;
++ to += dsize;
++ }
++
++ return ret;
++}
++
++
++
++
++static ssize_t rpc_write_flash(struct spi_nor *nor, loff_t to, size_t len,
++ const u_char *buf)
++{
++ ssize_t res = len, full = len;
++ u32 val;
++ u8 rval[2];
++ struct rpc_spi *rpc = nor->priv;
++ loff_t bo;
++ loff_t offset;
++ loff_t _to;
++ bool is_rounded = false;
++
++ /* ...len should be rounded to 2 bytes */
++ if (rpc->mtdtype == MTD_QSPI_2x && (len & 1)) {
++ is_rounded = true;
++ len &= ~(1);
++ }
++
++ bo = to & (WRITE_BUF_ADR_MASK);
++
++ rpc_flush_cache(rpc);
++ rpc_setup_write_mode(rpc);
++ rpc_setup_write_mode_command_and_adr(rpc, nor->addr_width, true);
++ rpc_setup_writemode_nbits(rpc, 1, 1, 1);
++
++ /* ...setup command */
++ val = rpc_read(rpc, SMCMR);
++ val &= ~(SMCMR_CMD_MASK);
++ val |= SMCMR_CMD(nor->program_opcode);
++ rpc_write(rpc, SMCMR, val);
++
++ offset = (to & (~WRITE_BUF_ADR_MASK));
++
++ /* ...write unaligned first bytes */
++ if (bo) {
++ rpc_write_unaligned(nor, to, WRITE_BUF_SIZE - bo, buf, full);
++ rpc_setup_write_mode(rpc);
++
++ len -= WRITE_BUF_SIZE - bo;
++ buf += WRITE_BUF_SIZE - bo;
++ to += WRITE_BUF_SIZE - bo;
++ full -= WRITE_BUF_SIZE - bo;
++ }
++
++ /* Unfortunatly RPC does not write properly in write buf mode
++ * without transferring command.
++ * May be it is better just remove this code
++ */
++ if (len >= WRITE_BUF_SIZE && !bo) {
++ _to = to;
++ if (rpc->mtdtype == MTD_QSPI_2x)
++ _to >>= 1;
++ rpc_write(rpc, SMADR, _to);
++ memcpy_toio(rpc->write_area, buf, WRITE_BUF_SIZE);
++ buf += WRITE_BUF_SIZE;
++ len -= WRITE_BUF_SIZE;
++ to += WRITE_BUF_SIZE;
++ full -= WRITE_BUF_SIZE;
++
++ rpc_begin(rpc, false, true, full <= 0);
++ if (rpc_wait(rpc, DEFAULT_TO))
++ return -ETIMEDOUT;
++
++ rpc_setup_write_mode_command_and_adr(rpc,
++ nor->addr_width, false);
++ }
++
++ if (len) {
++ rpc_write_unaligned(nor, to, len, buf, full);
++ buf += len;
++ to += len;
++ full -= len;
++ len = 0;
++ }
++
++ if (is_rounded) {
++ rval[0] = *buf;
++ rval[1] = 0xFF;
++ rpc_write_unaligned(nor, to, 2, rval, full);
++ }
++
++ rpc_flush_cache(rpc);
++
++ return res;
++}
++
++static inline unsigned int rpc_rx_nbits(struct spi_nor *nor)
++{
++ switch (nor->flash_read) {
++ case SPI_NOR_DUAL:
++ return 2;
++ case SPI_NOR_QUAD:
++ return 4;
++ default:
++ return 0;
++ }
++}
++
++
++#if 0
++//manual read
++static ssize_t rpc_read_flash(struct spi_nor *nor, loff_t from, size_t len,
++ u_char *buf)
++{
++ ssize_t ret = len;
++ struct rpc_spi *rpc = nor->priv;
++ int adr_width = nor->addr_width;
++ int opcode_nbits = 1;//SPI_NBITS_SINGLE;
++ int addr_nbits = spi_nor_get_read_addr_nbits(nor->read_opcode);
++ int data_nbits = rpc_rx_nbits(nor);
++ int dummy = nor->read_dummy - 1;
++ u32 val, val2;
++ u32 *buf32;
++ int sz = 4;
++ int i, j;
++ loff_t adr = from;
++
++ rpc_wait(rpc, DEFAULT_TO);
++ if (rpc->mtdtype == MTD_QSPI_2x)
++ sz <<= 1;
++
++ /* ...setup manual mode */
++ val = rpc_read(rpc, CMNCR);
++ val |= CMNCR_MD;
++ rpc_write(rpc, CMNCR, val);
++
++ /*...setup dummy */
++ val = rpc_read(rpc, SMDMCR);
++ val &= ~(SMDMCR_DMCYC_MASK);
++ val |= SMDMCR_DMCYC(dummy);
++ rpc_write(rpc, SMDMCR, val);
++
++ /* disable ddr */
++ val = rpc_read(rpc, SMDRENR);
++ val &= ~(SMDRENR_ADDRE | SMDRENR_OPDRE | SMDRENR_SPIDRE);
++ rpc_write(rpc, SMDRENR, val);
++
++ /* enable 1bit command */
++ val = rpc_read(rpc, SMENR);
++ val &= ~(SMENR_CDB_MASK | SMENR_OCDB_MASK | SMENR_DME
++ | SMENR_OCDE | SMENR_SPIDB_MASK
++ | SMENR_ADE_MASK | SMENR_ADB_MASK
++ | SMENR_OPDE_MASK | SMENR_SPIDE_MASK);
++ val |= SMENR_CDB_1B | SMENR_ADB_4B | SMENR_SPIDB_4B
++ | SMENR_CDE | SMENR_ADE_31_0 | SMENR_DME | SMENR_SPIDE_32B;
++ rpc_write(rpc, SMENR, val);
++
++ /* ...setup command */
++ val = rpc_read(rpc, SMCMR);
++ val &= ~(SMCMR_CMD_MASK);
++ val |= SMCMR_CMD(nor->read_opcode);
++ rpc_write(rpc, SMCMR, val);
++
++ buf32 = (u32 *)buf;
++
++ while (len > 0) {
++ adr = from + ret - len;
++
++ if (rpc->mtdtype == MTD_QSPI_2x)
++ adr >>= 1;
++
++ rpc_write(rpc, SMADR, adr);
++
++ rpc_begin(rpc, true, false, len <= sz);
++ if (rpc_wait(rpc, DEFAULT_TO))
++ return -ETIMEDOUT;
++
++ rpc_read_manual_data(rpc, &val, &val2);
++
++ j = 0;
++ do {
++ if (len > 4) {
++ *buf32 = val;
++ buf32++;
++ len -= 4;
++ } else {
++ buf = (u8 *)buf32;
++ for (i = 0; i < len; i++) {
++ *buf = (val >> (8 * i)) & 0x000000ff;
++ buf++;
++ }
++ len = 0;
++ }
++ val = val2;
++ j++;
++ } while (j < 2 && rpc->mtdtype == MTD_QSPI_2x);
++ }
++
++ return ret;
++}
++#else
++#define READ_ADR_MASK (BIT(26) - 1)
++static ssize_t rpc_read_flash(struct spi_nor *nor, loff_t from, size_t len,
++ u_char *buf)
++{
++ u32 val;
++ struct rpc_spi *rpc = nor->priv;
++ int adr_width = nor->addr_width;
++ int opcode_nbits = 1;
++ int addr_nbits = spi_nor_get_read_addr_nbits(nor->read_opcode);
++ int data_nbits = rpc_rx_nbits(nor);
++ int dummy = nor->read_dummy - 1;
++ ssize_t ret = len;
++ ssize_t readlen;
++ loff_t _from;
++
++ rpc_setup_ext_mode(rpc);
++ /* ...setup n bits */
++ rpc_setup_extmode_nbits(rpc, opcode_nbits, addr_nbits, data_nbits);
++
++ /* TODO: setup DDR */
++
++ /* ...setup command */
++ val = rpc_read(rpc, DRCMR);
++ val &= ~(DRCMR_CMD_MASK);
++ val |= DRCMR_CMD(nor->read_opcode);
++ rpc_write(rpc, DRCMR, val);
++
++ /* ...setup dummy cycles */
++ val = rpc_read(rpc, DRDMCR);
++ val &= ~(DRDMCR_DMCYC_MASK);
++ val |= DRDMCR_DMCYC(dummy);
++ rpc_write(rpc, DRDMCR, val);
++
++ /* ...setup read sequence */
++ val = rpc_read(rpc, DRENR);
++ val |= DRENR_DME | DRENR_CDE;
++ rpc_write(rpc, DRENR, val);
++
++ while (len > 0) {
++ /* ...setup address */
++ rpc_setup_extmode_read_addr(rpc, adr_width, from);
++ /* ...use adr [25...0] */
++ _from = from & READ_ADR_MASK;
++
++ readlen = READ_ADR_MASK - _from + 1;
++ readlen = readlen > len ? len : readlen;
++
++ memcpy_fromio(buf, rpc->read_area + _from, readlen);
++ buf += readlen;
++ from += readlen;
++ len -= readlen;
++ }
++
++ return ret;
++}
++#endif
++
++static int __rpc_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
++{
++ u32 val, val2;
++ u32 *buf32;
++ int i;
++ u32 mask = 0, type;
++ struct rpc_spi *rpc = nor->priv;
++
++ type = rpc->mtdtype;
++
++ rpc_setup_reg_mode(rpc);
++ val = rpc_read(rpc, SMCMR);
++ val &= ~(SMCMR_CMD_MASK);
++ val |= SMCMR_CMD(opcode);
++ rpc_write(rpc, SMCMR, val);
++
++ rpc_begin(rpc, true, false, len <= 4);
++ if (rpc_wait(rpc, DEFAULT_TO))
++ return -ETIMEDOUT;
++
++ /* ...disable command */
++ val = rpc_read(rpc, SMENR);
++ val &= ~(SMENR_CDE);
++ rpc_write(rpc, SMENR, val);
++
++ buf32 = (u32 *)buf;
++
++ while (len > 0) {
++ rpc_read_manual_data(rpc, &val, &val2);
++
++ if (mask) {
++ dev_warn(&rpc->pdev->dev,
++ "Using mask workaround (0x%x)\n", mask);
++ val &= ~(mask);
++ val2 &= ~(mask);
++ }
++
++ /* ... spi flashes should be the same */
++ if (type == MTD_QSPI_2x && val != val2) {
++ /* clear cs */
++ rpc_begin(rpc, true, false, true);
++ return -EAGAIN;
++ }
++
++ if (len > 4) {
++ *buf32 = val;
++ buf32++;
++ len -= 4;
++ } else {
++ buf = (u8 *)buf32;
++ for (i = 0; i < len; i++) {
++ *buf = (val >> (8 * i)) & 0x000000ff;
++ buf++;
++ }
++ len = 0;
++ }
++
++ if (!len)
++ break;
++
++ mask = 0xff;
++
++ rpc_begin(rpc, true, false, len <= 4);
++ if (rpc_wait(rpc, DEFAULT_TO))
++ return -ETIMEDOUT;
++
++ }
++
++ return 0;
++}
++
++
++static int rpc_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
++{
++ int i, ret;
++
++ /* A few read commands like read status can
++ * generate different answers. We repeat reading
++ * in that case
++ */
++ for (i = 0; i < REPEAT_MAX; i++) {
++ ret = __rpc_read_reg(nor, opcode, buf, len);
++ if (!ret || ret != -EAGAIN)
++ break;
++ mdelay(REPEAT_TIME);
++ }
++
++ return ret;
++}
++
++
++
++static int rpc_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
++{
++ struct rpc_spi *rpc = nor->priv;
++ u32 val;
++ int dsize;
++ bool copy = true;
++
++ rpc_setup_reg_mode(rpc);
++
++ val = rpc_read(rpc, SMCMR);
++ val &= ~(SMCMR_CMD_MASK);
++ val |= SMCMR_CMD(opcode);
++ rpc_write(rpc, SMCMR, val);
++
++ dsize = rpc_datalen2trancfersize(rpc, len, copy);
++
++ if (rpc_setup_data_size(rpc, dsize, copy))
++ return -EINVAL;
++
++ if (rpc_write_data2reg(rpc, buf, dsize, copy))
++ return -EINVAL;
++ buf += dsize;
++ len -= dsize;
++ rpc_begin(rpc, false, dsize > 0, len == 0);
++
++ if (rpc_wait(rpc, DEFAULT_TO))
++ return -ETIMEDOUT;
++
++ /* ...disable command */
++ val = rpc_read(rpc, SMENR);
++ val &= ~(SMENR_CDE);
++ rpc_write(rpc, SMENR, val);
++
++ while (len > 0) {
++ dsize = rpc_datalen2trancfersize(rpc, len, copy);
++ if (rpc_setup_data_size(rpc, dsize, copy))
++ return -EINVAL;
++ rpc_write_data2reg(rpc, buf, dsize, copy);
++ buf += dsize;
++ len -= dsize;
++
++ rpc_begin(rpc, false, dsize, len == 0);
++
++ if (rpc_wait(rpc, DEFAULT_TO))
++ return -ETIMEDOUT;
++
++ }
++
++ return 0;
++}
++
++
++
++/* hw init for spi-nor flashes */
++static int rpc_hw_init_1x2x(struct rpc_spi *rpc)
++{
++ u32 val;
++
++ /* Exec calibration */
++ val = rpc_read(rpc, PHYCNT);
++ val &= ~(PHYCNT_OCTA_MASK | PHYCNT_EXDS | PHYCNT_OCT
++ | PHYCNT_DDRCAL | PHYCNT_HS | PHYCNT_STREAM_MASK
++ | PHYCNT_WBUF2 | PHYCNT_WBUF | PHYCNT_PHYMEM_MASK);
++ val |= (PHYCNT_CAL) | PHYCNT_STREAM(6);
++ rpc_write(rpc, PHYCNT, val);
++
++ /* disable rpc_* pins */
++ val = rpc_read(rpc, PHYINT);
++ val &= ~((1<<24) | (7<<16));
++ rpc_write(rpc, PHYINT, val);
++
++ val = rpc_read(rpc, SMDRENR);
++ val &= ~(SMDRENR_HYPE_MASK);
++ val |= SMDRENR_HYPE_SPI_FLASH;
++ rpc_write(rpc, SMDRENR, val);
++
++ val = rpc_read(rpc, CMNCR);
++ val &= ~(CMNCR_BSZ_MASK);
++ if (rpc->mtdtype != MTD_QSPI_1x)
++ val |= CMNCR_BSZ_4x2;
++ rpc_write(rpc, CMNCR, val);
++
++ val = rpc_read(rpc, PHYOFFSET1);
++ val |= PHYOFFSET1_DDRTMG;
++ rpc_write(rpc, PHYOFFSET1, val);
++
++ val = SSLDR_SPNDL(0) | SSLDR_SLNDL(4) | SSLDR_SCKDL(0);
++ rpc_write(rpc, SSLDR, val);
++
++ return 0;
++}
++
++
++static int rpc_hw_init(struct rpc_spi *rpc)
++{
++ switch (rpc->mtdtype) {
++ case MTD_QSPI_1x:
++ case MTD_QSPI_2x:
++ return rpc_hw_init_1x2x(rpc);
++
++ default:
++ dev_err(&rpc->pdev->dev, "Unsupported connection mode\n");
++ return -ENODEV;
++ }
++}
++
++
++static int rpc_erase_sector(struct spi_nor *nor, loff_t addr)
++{
++ struct rpc_spi *rpc = nor->priv;
++ u8 buf[6];
++ int i;
++
++ if (rpc->mtdtype == MTD_QSPI_2x)
++ addr >>= 1;
++
++ for (i = nor->addr_width - 1; i >= 0; i--) {
++ buf[i] = addr & 0xff;
++ addr >>= 8;
++ }
++
++ return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width);
++}
++
++
++
++
++static const struct of_device_id rpc_of_match[] = {
++ { .compatible = "renesas,qspi-rpc-r8a7798" },
++ { .compatible = "renesas,qspi-rpc-r8a7797", .data = (void *)OWN_CLOCK_DIVIDER },
++ { },
++};
++
++MODULE_DEVICE_TABLE(of, rpc_of_match);
++
++
++
++static int rpc_spi_probe(struct platform_device *pdev)
++{
++ struct device_node *flash_np;
++ struct spi_nor *nor;
++ struct rpc_spi *rpc;
++ struct resource *res;
++ enum read_mode mode = SPI_NOR_NORMAL;
++ u32 max_clk_rate = 50000000;
++ u32 property;
++ int ret;
++ int own_clk;
++
++
++ flash_np = of_get_next_available_child(pdev->dev.of_node, NULL);
++ if (!flash_np) {
++ dev_err(&pdev->dev, "no SPI flash device to configure\n");
++ return -ENODEV;
++ }
++
++ if (!of_property_read_u32(flash_np, "spi-rx-bus-width", &property)) {
++ switch (property) {
++ case 1:
++ break;
++ case 2:
++ mode = SPI_NOR_DUAL;
++ break;
++ case 4:
++ mode = SPI_NOR_QUAD;
++ break;
++ default:
++ dev_err(&pdev->dev, "unsupported rx-bus-width\n");
++ return -EINVAL;
++ }
++ }
++
++ of_property_read_u32(flash_np, "spi-max-frequency", &max_clk_rate);
++ own_clk = of_device_get_match_data(&pdev->dev);
++
++ rpc = devm_kzalloc(&pdev->dev, sizeof(*rpc), GFP_KERNEL);
++ if (!rpc)
++ return -ENOMEM;
++
++ rpc->pdev = pdev;
++
++ /* ... setup nor hooks */
++ nor = &rpc->spi_nor;
++ nor->dev = &pdev->dev;
++ spi_nor_set_flash_node(nor, flash_np);
++ nor->read = rpc_read_flash;
++ nor->write = rpc_write_flash;
++ nor->read_reg = rpc_read_reg;
++ nor->write_reg = rpc_write_reg;
++ nor->priv = rpc;
++ rpc->mtdtype = MTD_QSPI_1x;
++ if (of_find_property(pdev->dev.of_node, "dual", NULL)) {
++ rpc->mtdtype = MTD_QSPI_2x;
++ spi_nor_set_array_size(nor, 2);
++ }
++ if (rpc->mtdtype == MTD_QSPI_2x)
++ nor->erase = rpc_erase_sector;
++
++ /* ...get memory */
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ rpc->base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(rpc->base)) {
++ dev_err(&pdev->dev, "cannot get resources\n");
++ ret = PTR_ERR(rpc->base);
++ goto error;
++ }
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
++
++ rpc->read_area = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(rpc->base)) {
++ dev_err(&pdev->dev, "cannot get resources\n");
++ ret = PTR_ERR(rpc->base);
++ goto error;
++ }
++
++ /* ...get memory */
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
++ rpc->write_area = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(rpc->base)) {
++ dev_err(&pdev->dev, "cannot get resources\n");
++ ret = PTR_ERR(rpc->base);
++ goto error;
++ }
++
++ /* ...get clk */
++ rpc->clk = devm_clk_get(&pdev->dev, NULL);
++ if (IS_ERR(rpc->clk)) {
++ dev_err(&pdev->dev, "cannot get clock\n");
++ ret = PTR_ERR(rpc->clk);
++ goto error;
++ }
++
++ /* ...set max clk rate */
++ if (!own_clk) {
++ ret = clk_set_rate(rpc->clk, max_clk_rate);
++ if (ret) {
++ dev_err(&pdev->dev, "cannot set clock rate\n");
++ goto error;
++ }
++ }
++
++ /* ... enable clk */
++ ret = clk_prepare_enable(rpc->clk);
++ if (ret) {
++ dev_err(&pdev->dev, "cannot prepare clock\n");
++ goto error;
++ }
++
++ /* ...init device */
++ ret = rpc_hw_init(rpc);
++ if (ret < 0) {
++ dev_err(&pdev->dev, "rpc_hw_init error.\n");
++ goto error_clk_disable;
++ }
++
++ /* ...set clk ratio */
++ if (own_clk) {
++ ret = rpc_setup_clk_ratio(rpc, max_clk_rate);
++ if (ret) {
++ dev_err(&pdev->dev, "cannot set clock ratio\n");
++ goto error;
++ }
++ }
++
++ platform_set_drvdata(pdev, rpc);
++
++ ret = spi_nor_scan(nor, NULL, mode);
++ if (ret) {
++ dev_err(&pdev->dev, "spi_nor_scan error.\n");
++ goto error_clk_disable;
++ }
++
++ ret = mtd_device_register(&nor->mtd, NULL, 0);
++ if (ret) {
++ dev_err(&pdev->dev, "mtd_device_register error.\n");
++ goto error_clk_disable;
++ }
++
++ dev_info(&pdev->dev, "probed as %s\n",
++ rpc->mtdtype == MTD_QSPI_1x ? "single" : "dual");
++
++ return 0;
++
++
++error_clk_disable:
++ clk_disable_unprepare(rpc->clk);
++error:
++ return ret;
++}
++
++
++
++
++static int rpc_spi_remove(struct platform_device *pdev)
++{
++ struct rpc_spi *rpc = platform_get_drvdata(pdev);
++
++ /* HW shutdown */
++ clk_disable_unprepare(rpc->clk);
++ mtd_device_unregister(&rpc->spi_nor.mtd);
++ return 0;
++}
++
++
++
++
++
++/* platform driver interface */
++static struct platform_driver rpc_platform_driver = {
++ .probe = rpc_spi_probe,
++ .remove = rpc_spi_remove,
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = "rpc",
++ .of_match_table = of_match_ptr(rpc_of_match),
++ },
++};
++
++module_platform_driver(rpc_platform_driver);
++
++
++MODULE_ALIAS("rpc");
++MODULE_AUTHOR("Cogent Embedded Inc. <sources@cogentembedded.com>");
++MODULE_DESCRIPTION("Renesas RPC Driver");
++MODULE_LICENSE("GPL");
+--
+2.7.4
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0114-R8A7798-dtsi-Add-RPC-node-to-dtsi.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0114-R8A7798-dtsi-Add-RPC-node-to-dtsi.patch
new file mode 100644
index 0000000..7169baa
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0114-R8A7798-dtsi-Add-RPC-node-to-dtsi.patch
@@ -0,0 +1,35 @@
+From 80ebc1949eb5d7db33c60faa8537029fcf6379ba Mon Sep 17 00:00:00 2001
+From: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+Date: Mon, 19 Mar 2018 11:14:26 +0300
+Subject: [PATCH 05/12] R8A7798: dtsi: Add RPC node to dtsi
+
+Signed-off-by: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+---
+ arch/arm64/boot/dts/renesas/r8a7798.dtsi | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/renesas/r8a7798.dtsi b/arch/arm64/boot/dts/renesas/r8a7798.dtsi
+index e2b3404..fac60e6 100644
+--- a/arch/arm64/boot/dts/renesas/r8a7798.dtsi
++++ b/arch/arm64/boot/dts/renesas/r8a7798.dtsi
+@@ -1026,6 +1026,17 @@
+ status = "disabled";
+ };
+
++ qspi0: qspi@ee200000 {
++ compatible = "renesas,qspi-rpc-r8a7798";
++ reg = <0 0xee200000 0 0x1f0>,
++ <0 0x08000000 0 0x04000000>,
++ <0 0xee208000 0 0x100>;
++ interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&cpg CPG_MOD 917>;
++ power-domains = <&sysc R8A7798_PD_ALWAYS_ON>;
++ status = "disabled";
++ };
++
+ msiof0: spi@e6e90000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+--
+2.7.4
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0115-R8A7798-condor-dts-Add-qspi-flash.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0115-R8A7798-condor-dts-Add-qspi-flash.patch
new file mode 100644
index 0000000..213421c
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0115-R8A7798-condor-dts-Add-qspi-flash.patch
@@ -0,0 +1,98 @@
+From 45ac82977a015e6ec80a1fb119b26c5b5841b760 Mon Sep 17 00:00:00 2001
+From: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+Date: Mon, 19 Mar 2018 11:19:54 +0300
+Subject: [PATCH 06/12] R8A7798-condor: dts: Add qspi flash
+
+Add s25fs512 flash support
+
+Signed-off-by: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+---
+ arch/arm64/boot/dts/renesas/r8a7798-condor.dts | 72 ++++++++++++++++++++++++++
+ 1 file changed, 72 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/renesas/r8a7798-condor.dts b/arch/arm64/boot/dts/renesas/r8a7798-condor.dts
+index cdd9844..81f984f1 100644
+--- a/arch/arm64/boot/dts/renesas/r8a7798-condor.dts
++++ b/arch/arm64/boot/dts/renesas/r8a7798-condor.dts
+@@ -240,6 +240,78 @@
+ groups = "tpu_to0";
+ function = "tpu";
+ };
++
++ qspi0_pins: qspi0 {
++ groups = "qspi0_ctrl", "qspi0_data4";
++ function = "qspi0";
++ };
++
++ qspi1_pins: qspi1 {
++ groups = "qspi1_ctrl", "qspi1_data4";
++ function = "qspi1";
++ };
++};
++
++&qspi0 {
++ pinctrl-0 = <&qspi0_pins &qspi1_pins>;
++ pinctrl-names = "default";
++
++ status = "okay";
++
++ flash@0 {
++ compatible = "spansion,s25fs512s", "jedec,spi-nor";
++ reg = <0>;
++ spi-max-frequency = <50000000>;
++ spi-rx-bus-width = <4>;
++
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ bootparam@0 {
++ reg = <0x00000000 0x040000>;
++ read-only;
++ };
++ cr7@00040000 {
++ reg = <0x00040000 0x080000>;
++ read-only;
++ };
++ cert_header_sa3@000C0000 {
++ reg = <0x000C0000 0x080000>;
++ read-only;
++ };
++ bl2@00140000 {
++ reg = <0x00140000 0x040000>;
++ read-only;
++ };
++ cert_header_sa6@00180000 {
++ reg = <0x00180000 0x040000>;
++ read-only;
++ };
++ bl31@001C0000 {
++ reg = <0x001C0000 0x460000>;
++ read-only;
++ };
++ uboot@00640000 {
++ reg = <0x00640000 0x0C0000>;
++ read-only;
++ };
++ uboot-env@00700000 {
++ reg = <0x00700000 0x040000>;
++ read-only;
++ };
++ dtb@00740000 {
++ reg = <0x00740000 0x080000>;
++ };
++ kernel@007C0000 {
++ reg = <0x007C0000 0x1400000>;
++ };
++ user@01BC0000 {
++ reg = <0x01BC0000 0x2440000>;
++ };
++ };
++ };
+ };
+
+ &scif0 {
+--
+2.7.4
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0116-spi-nor-Add-s25fs512-flash-support.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0116-spi-nor-Add-s25fs512-flash-support.patch
new file mode 100644
index 0000000..7a3500b
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0116-spi-nor-Add-s25fs512-flash-support.patch
@@ -0,0 +1,126 @@
+From 1b2a145d72c4202fc41b2af138b862a8be94b0f0 Mon Sep 17 00:00:00 2001
+From: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+Date: Mon, 19 Mar 2018 11:29:21 +0300
+Subject: [PATCH 07/12] spi-nor: Add s25fs512 flash support
+
+Signed-off-by: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 31 ++++++++++++++++++++++++++++---
+ include/linux/mtd/spi-nor.h | 15 +++++++++++++++
+ 2 files changed, 43 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
+index d0fc165..1855bbe 100644
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -75,6 +75,7 @@ struct flash_info {
+ * bit. Must be used with
+ * SPI_NOR_HAS_LOCK.
+ */
++#define SPANSION_S512FS_QUIRK BIT(10)
+ };
+
+ #define JEDEC_MFR(info) ((info)->id[0])
+@@ -222,8 +223,18 @@ static inline int spi_nor_sr_ready(struct spi_nor *nor)
+ int sr = read_sr(nor);
+ if (sr < 0)
+ return sr;
+- else
+- return !(sr & SR_WIP);
++
++ if (nor->flags & SNOR_F_USE_CLSR && sr & (SR_E_ERR | SR_P_ERR)) {
++ if (sr & SR_E_ERR)
++ dev_err(nor->dev, "Erase Error occurred\n");
++ else
++ dev_err(nor->dev, "Programming Error occurred\n");
++
++ nor->write_reg(nor, SPINOR_OP_CLSR, NULL, 0);
++ return -EIO;
++ }
++
++ return !(sr & SR_WIP);
+ }
+
+ static inline int spi_nor_fsr_ready(struct spi_nor *nor)
+@@ -901,7 +912,21 @@ static const struct flash_info spi_nor_ids[] = {
+ { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) },
+ { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+- { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++ { "s25fl512s", INFO6(0x010220, 0x4d0080, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++
++ {
++ "s25fs512s", INFO6(0x010220, 0x4d0081, 256 * 1024, 256,
++ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SECT_4K | SPANSION_S512FS_QUIRK)
++ },
++ {
++ "s25fs128s0", INFO6(0x012018, 0x4d0381, 256 * 1024, 64,
++ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SECT_4K | SPANSION_S512FS_QUIRK)
++ },
++ {
++ "s25fs128s1", INFO6(0x012018, 0x4d0181, 64 * 1024, 256,
++ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SECT_4K | SPANSION_S512FS_QUIRK)
++ },
++
+ { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
+ { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
+ { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
+diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
+index c425c7b..99f7b2a 100644
+--- a/include/linux/mtd/spi-nor.h
++++ b/include/linux/mtd/spi-nor.h
+@@ -60,8 +60,10 @@
+ #define SPINOR_OP_READ4_FAST 0x0c /* Read data bytes (high frequency) */
+ #define SPINOR_OP_READ4_1_1_2 0x3c /* Read data bytes (Dual SPI) */
+ #define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */
++#define SPINOR_OP_READ4_1_4_4 0xec /* Read data bytes (Quad I/O SPI) */
+ #define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */
+ #define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */
++#define SPINOR_OP_BE_4K_4B 0x21 /* Erase 4KiB block */
+
+ /* Used for SST flashes only. */
+ #define SPINOR_OP_BP 0x02 /* Byte program */
+@@ -74,6 +76,7 @@
+
+ /* Used for Spansion flashes only. */
+ #define SPINOR_OP_BRWR 0x17 /* Bank register write */
++#define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */
+
+ /* Used for Micron flashes only. */
+ #define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */
+@@ -88,6 +91,9 @@
+ #define SR_BP2 BIT(4) /* Block protect 2 */
+ #define SR_TB BIT(5) /* Top/Bottom protect */
+ #define SR_SRWD BIT(7) /* SR write protect */
++/* Spansion/Cypress specific status bits */
++#define SR_E_ERR BIT(5)
++#define SR_P_ERR BIT(6)
+
+ #define SR_QUAD_EN_MX BIT(6) /* Macronix Quad I/O */
+
+@@ -119,6 +125,7 @@ enum spi_nor_ops {
+ enum spi_nor_option_flags {
+ SNOR_F_USE_FSR = BIT(0),
+ SNOR_F_HAS_SR_TB = BIT(1),
++ SNOR_F_USE_CLSR = BIT(5),
+ };
+
+ /**
+@@ -197,6 +204,14 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
+ return mtd_get_of_node(&nor->mtd);
+ }
+
++static inline int spi_nor_get_read_addr_nbits(u8 opcode)
++{
++ if (opcode == SPINOR_OP_READ4_1_4_4)
++ return 4;
++ return 1;
++}
++
++
+ /**
+ * spi_nor_scan() - scan the SPI NOR
+ * @nor: the spi_nor structure
+--
+2.7.4
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0117-spi-nor-Add-flash-array-support.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0117-spi-nor-Add-flash-array-support.patch
new file mode 100644
index 0000000..5003ae6
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0117-spi-nor-Add-flash-array-support.patch
@@ -0,0 +1,89 @@
+From 0d10eb085d28a442b147e63383d1836cb3e62ae4 Mon Sep 17 00:00:00 2001
+From: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+Date: Mon, 19 Mar 2018 11:32:58 +0300
+Subject: [PATCH 08/12] spi-nor: Add flash array support
+
+Signed-off-by: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 20 ++++++++++++++++++--
+ include/linux/mtd/spi-nor.h | 12 ++++++++++++
+ 2 files changed, 30 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
+index 1855bbe..3ced558 100644
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -1338,6 +1338,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
+ if (ret)
+ return ret;
+
++ if (!nor->array_size)
++ nor->array_size = 1;
++
+ if (name)
+ info = spi_nor_match_id(name);
+ /* Try to auto-detect if chip name wasn't specified or not found */
+@@ -1500,7 +1503,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
+ /* Dedicated 4-byte command set */
+ switch (nor->flash_read) {
+ case SPI_NOR_QUAD:
+- nor->read_opcode = SPINOR_OP_READ4_1_1_4;
++ if (info->flags & SPANSION_S512FS_QUIRK)
++ nor->read_opcode = SPINOR_OP_READ4_1_4_4;
++ else
++ nor->read_opcode = SPINOR_OP_READ4_1_1_4;
+ break;
+ case SPI_NOR_DUAL:
+ nor->read_opcode = SPINOR_OP_READ4_1_1_2;
+@@ -1528,7 +1534,17 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
+ return -EINVAL;
+ }
+
+- nor->read_dummy = spi_nor_read_dummy_cycles(nor);
++ if (info->flags & SPANSION_S512FS_QUIRK) {
++ nor->read_dummy = 10;
++ nor->flags |= SNOR_F_USE_CLSR;
++ } else
++ nor->read_dummy = spi_nor_read_dummy_cycles(nor);
++
++ /* recount parameters using array size */
++ mtd->erasesize *= nor->array_size;
++ mtd->size *= nor->array_size;
++ nor->page_size *= nor->array_size;
++ mtd->writebufsize *= nor->array_size;
+
+ dev_info(dev, "%s (%lld Kbytes)\n", info->name,
+ (long long)mtd->size >> 10);
+diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
+index 99f7b2a..7216b09 100644
+--- a/include/linux/mtd/spi-nor.h
++++ b/include/linux/mtd/spi-nor.h
+@@ -174,6 +174,7 @@ struct spi_nor {
+ bool sst_write_second;
+ u32 flags;
+ u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
++ u8 array_size;
+
+ int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
+ void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
+@@ -204,6 +205,17 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
+ return mtd_get_of_node(&nor->mtd);
+ }
+
++static inline void spi_nor_set_array_size(struct spi_nor *nor,
++ u8 size)
++{
++ nor->array_size = size;
++}
++
++static inline u8 spi_nor_get_array_size(struct spi_nor *nor)
++{
++ return nor->array_size;
++}
++
+ static inline int spi_nor_get_read_addr_nbits(u8 opcode)
+ {
+ if (opcode == SPINOR_OP_READ4_1_4_4)
+--
+2.7.4
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0118-r8a7797-clk-Add-rpc-clock.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0118-r8a7797-clk-Add-rpc-clock.patch
new file mode 100644
index 0000000..7910790
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0118-r8a7797-clk-Add-rpc-clock.patch
@@ -0,0 +1,45 @@
+From 730f3d906fa599bf76bb0b1fdf35b44a7cbdc2ed Mon Sep 17 00:00:00 2001
+From: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+Date: Thu, 22 Mar 2018 14:57:46 +0300
+Subject: [PATCH 09/12] r8a7797: clk: Add rpc clock
+
+Signed-off-by: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+---
+ drivers/clk/renesas/r8a7797-cpg-mssr.c | 2 ++
+ include/dt-bindings/clock/r8a7797-cpg-mssr.h | 1 +
+ 2 files changed, 3 insertions(+)
+
+diff --git a/drivers/clk/renesas/r8a7797-cpg-mssr.c b/drivers/clk/renesas/r8a7797-cpg-mssr.c
+index a3eed41..9763f6c 100644
+--- a/drivers/clk/renesas/r8a7797-cpg-mssr.c
++++ b/drivers/clk/renesas/r8a7797-cpg-mssr.c
+@@ -74,6 +74,7 @@ static const struct cpg_core_clk r8a7797_core_clks[] __initconst = {
+ DEF_FIXED("s2d1", R8A7797_CLK_S2D1, CLK_S2, 1, 1),
+ DEF_FIXED("s2d2", R8A7797_CLK_S2D2, CLK_S2, 2, 1),
+ DEF_FIXED("s2d4", R8A7797_CLK_S2D4, CLK_S2, 4, 1),
++ DEF_FIXED("rpcsrc", R8A7797_CLK_RPCSRC, CLK_PLL1_DIV2, 5, 1),
+
+ DEF_GEN3_SD0H("sd0h", R8A7797_CLK_SD0H, CLK_PLL1_DIV4, 0x0074),
+ DEF_GEN3_SD0("sd0", R8A7797_CLK_SD0, CLK_PLL1_DIV4, 0x0074),
+@@ -158,6 +159,7 @@ static const struct mssr_mod_clk r8a7797_mod_clks[] __initconst = {
+ DEF_MOD("i2c2", 929, R8A7797_CLK_S2D2),
+ DEF_MOD("i2c1", 930, R8A7797_CLK_S2D2),
+ DEF_MOD("i2c0", 931, R8A7797_CLK_S2D2),
++ DEF_MOD("rpc", 917, R8A7797_CLK_RPCSRC),
+ };
+
+ static const unsigned int r8a7797_crit_mod_clks[] __initconst = {
+diff --git a/include/dt-bindings/clock/r8a7797-cpg-mssr.h b/include/dt-bindings/clock/r8a7797-cpg-mssr.h
+index ae6b3af..c3bd0a7 100644
+--- a/include/dt-bindings/clock/r8a7797-cpg-mssr.h
++++ b/include/dt-bindings/clock/r8a7797-cpg-mssr.h
+@@ -44,5 +44,6 @@
+ #define R8A7797_CLK_CPEX 29
+ #define R8A7797_CLK_R 30
+ #define R8A7797_CLK_OSC 31
++#define R8A7797_CLK_RPCSRC 32
+
+ #endif /* __DT_BINDINGS_CLOCK_R8A7797_CPG_MSSR_H__ */
+--
+2.7.4
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0119-r8a7797-pinctrl-Add-qspi-pins.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0119-r8a7797-pinctrl-Add-qspi-pins.patch
new file mode 100644
index 0000000..a0b4330
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0119-r8a7797-pinctrl-Add-qspi-pins.patch
@@ -0,0 +1,118 @@
+From 441ae85fe3fec270ea1284a669cdd348a8432d85 Mon Sep 17 00:00:00 2001
+From: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+Date: Thu, 22 Mar 2018 15:00:02 +0300
+Subject: [PATCH 10/12] r8a7797: pinctrl: Add qspi pins
+
+Signed-off-by: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+---
+ drivers/pinctrl/sh-pfc/pfc-r8a7797.c | 73 ++++++++++++++++++++++++++++++++++++
+ 1 file changed, 73 insertions(+)
+
+diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7797.c b/drivers/pinctrl/sh-pfc/pfc-r8a7797.c
+index 6b83f44..4f451c4 100644
+--- a/drivers/pinctrl/sh-pfc/pfc-r8a7797.c
++++ b/drivers/pinctrl/sh-pfc/pfc-r8a7797.c
+@@ -1143,6 +1143,59 @@ static const unsigned int scif_clk_b_mux[] = {
+ SCIF_CLK_B_MARK,
+ };
+
++/* - QSPI ------------------------------------------------------------------- */
++static const unsigned int qspi0_ctrl_pins[] = {
++ /* QSPI0_SPCLK QSPI0_SSL */
++ RCAR_GP_PIN(5, 5), RCAR_GP_PIN(5, 0),
++};
++static const unsigned int qspi0_ctrl_mux[] = {
++ QSPI0_SPCLK_MARK, QSPI0_SSL_MARK,
++};
++
++static const unsigned int qspi0_data2_pins[] = {
++ /* QSPI0_MOSI_IO0, QSPI0_MISO_IO1 */
++ RCAR_GP_PIN(5, 1), RCAR_GP_PIN(5, 2),
++};
++static const unsigned int qspi0_data2_mux[] = {
++ QSPI0_MOSI_IO0_MARK, QSPI0_MISO_IO1_MARK,
++};
++
++static const unsigned int qspi0_data4_pins[] = {
++ /* QSPI0_MOSI_IO0, QSPI0_MISO_IO1, QSPI0_IO2, QSPI0_IO3 */
++ RCAR_GP_PIN(5, 1), RCAR_GP_PIN(5, 2),
++ RCAR_GP_PIN(5, 3), RCAR_GP_PIN(5, 4),
++};
++static const unsigned int qspi0_data4_mux[] = {
++ QSPI0_MOSI_IO0_MARK, QSPI0_MISO_IO1_MARK,
++ QSPI0_IO2_MARK, QSPI0_IO3_MARK
++};
++
++static const unsigned int qspi1_ctrl_pins[] = {
++ /* QSPI1_SPCLK QSPI1_SSL */
++ RCAR_GP_PIN(5, 6), RCAR_GP_PIN(5, 11),
++};
++static const unsigned int qspi1_ctrl_mux[] = {
++ QSPI1_SPCLK_MARK, QSPI1_SSL_MARK,
++};
++
++static const unsigned int qspi1_data2_pins[] = {
++ /* QSPI1_MOSI_IO0, QSPI1_MISO_IO1 */
++ RCAR_GP_PIN(5, 7), RCAR_GP_PIN(5, 8),
++};
++static const unsigned int qspi1_data2_mux[] = {
++ QSPI1_MOSI_IO0_MARK, QSPI1_MISO_IO1_MARK,
++};
++
++static const unsigned int qspi1_data4_pins[] = {
++ /* QSPI1_MOSI_IO0, QSPI1_MISO_IO1, QSPI1_IO2, QSPI1_IO3 */
++ RCAR_GP_PIN(5, 7), RCAR_GP_PIN(5, 8),
++ RCAR_GP_PIN(5, 9), RCAR_GP_PIN(5, 10),
++};
++static const unsigned int qspi1_data4_mux[] = {
++ QSPI1_MOSI_IO0_MARK, QSPI1_MISO_IO1_MARK,
++ QSPI1_IO2_MARK, QSPI1_IO3_MARK
++};
++
+ /* - I2C -------------------------------------------------------------------- */
+ static const unsigned int i2c0_pins[] = {
+ /* SDA0, SCL0 */
+@@ -1971,6 +2024,12 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
+ SH_PFC_PIN_GROUP(vin1_field),
+ SH_PFC_PIN_GROUP(vin1_clkenb),
+ SH_PFC_PIN_GROUP(vin1_clk),
++ SH_PFC_PIN_GROUP(qspi0_ctrl),
++ SH_PFC_PIN_GROUP(qspi0_data2),
++ SH_PFC_PIN_GROUP(qspi0_data4),
++ SH_PFC_PIN_GROUP(qspi1_ctrl),
++ SH_PFC_PIN_GROUP(qspi1_data2),
++ SH_PFC_PIN_GROUP(qspi1_data4),
+ };
+
+ static const char * const avb0_groups[] = {
+@@ -2201,6 +2260,18 @@ static const char * const vin1_groups[] = {
+ "vin1_clk",
+ };
+
++static const char * const qspi0_groups[] = {
++ "qspi0_ctrl",
++ "qspi0_data2",
++ "qspi0_data4",
++};
++
++static const char * const qspi1_groups[] = {
++ "qspi1_ctrl",
++ "qspi1_data2",
++ "qspi1_data4",
++};
++
+ #define POCCTRL0 0x380
+ #define POCCTRL1 0x384
+ #define PIN2POCCTRL0_SHIFT(a) ({ \
+@@ -2244,6 +2315,8 @@ static const struct sh_pfc_function pinmux_functions[] = {
+ SH_PFC_FUNCTION(tmu),
+ SH_PFC_FUNCTION(vin0),
+ SH_PFC_FUNCTION(vin1),
++ SH_PFC_FUNCTION(qspi0),
++ SH_PFC_FUNCTION(qspi1),
+ };
+
+ static const struct pinmux_cfg_reg pinmux_config_regs[] = {
+--
+2.7.4
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0120-r8a7797-dtsi-Add-rpc-node.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0120-r8a7797-dtsi-Add-rpc-node.patch
new file mode 100644
index 0000000..8d782ca
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0120-r8a7797-dtsi-Add-rpc-node.patch
@@ -0,0 +1,35 @@
+From 847b14e65374563ec3ee1d06eec05b4004233e7d Mon Sep 17 00:00:00 2001
+From: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+Date: Thu, 22 Mar 2018 15:27:49 +0300
+Subject: [PATCH 11/12] r8a7797: dtsi: Add rpc node
+
+Signed-off-by: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+---
+ arch/arm64/boot/dts/renesas/r8a7797.dtsi | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/renesas/r8a7797.dtsi b/arch/arm64/boot/dts/renesas/r8a7797.dtsi
+index a5552d6..2beca53 100644
+--- a/arch/arm64/boot/dts/renesas/r8a7797.dtsi
++++ b/arch/arm64/boot/dts/renesas/r8a7797.dtsi
+@@ -889,6 +889,17 @@
+ status = "disabled";
+ };
+
++ qspi0: qspi@ee200000 {
++ compatible = "renesas,qspi-rpc-r8a7797";
++ reg = <0 0xee200000 0 0x1f0>,
++ <0 0x08000000 0 0x04000000>,
++ <0 0xee208000 0 0x100>;
++ interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&cpg CPG_MOD 917>;
++ power-domains = <&sysc R8A7797_PD_ALWAYS_ON>;
++ status = "disabled";
++ };
++
+ msiof0: spi@e6e90000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+--
+2.7.4
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0121-r8a7797-eagle-dts-Add-spi-flash-node.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0121-r8a7797-eagle-dts-Add-spi-flash-node.patch
new file mode 100644
index 0000000..8dabedc
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0121-r8a7797-eagle-dts-Add-spi-flash-node.patch
@@ -0,0 +1,96 @@
+From 0e884fcce89a6b15bd3971392b5641b48d1ad9ab Mon Sep 17 00:00:00 2001
+From: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+Date: Thu, 22 Mar 2018 15:31:48 +0300
+Subject: [PATCH 12/12] r8a7797-eagle: dts: Add spi flash node
+
+Signed-off-by: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+---
+ arch/arm64/boot/dts/renesas/r8a7797-eagle.dts | 72 +++++++++++++++++++++++++++
+ 1 file changed, 72 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/renesas/r8a7797-eagle.dts b/arch/arm64/boot/dts/renesas/r8a7797-eagle.dts
+index ce7a88e..c8150d9 100644
+--- a/arch/arm64/boot/dts/renesas/r8a7797-eagle.dts
++++ b/arch/arm64/boot/dts/renesas/r8a7797-eagle.dts
+@@ -203,6 +203,78 @@
+ groups = "du_rgb666", "du_sync", "du_clk_out_0", "du_disp";
+ function = "du";
+ };
++
++ qspi0_pins: qspi0 {
++ groups = "qspi0_ctrl", "qspi0_data4";
++ function = "qspi0";
++ };
++
++ qspi1_pins: qspi1 {
++ groups = "qspi1_ctrl", "qspi1_data4";
++ function = "qspi1";
++ };
++};
++
++&qspi0 {
++ pinctrl-0 = <&qspi0_pins &qspi1_pins>;
++ pinctrl-names = "default";
++
++ status = "okay";
++
++ flash@0 {
++ compatible = "spansion,s25fs512s", "jedec,spi-nor";
++ reg = <0>;
++ spi-max-frequency = <50000000>;
++ spi-rx-bus-width = <4>;
++
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ bootparam@0 {
++ reg = <0x00000000 0x040000>;
++ read-only;
++ };
++ cr7@00040000 {
++ reg = <0x00040000 0x080000>;
++ read-only;
++ };
++ cert_header_sa3@000C0000 {
++ reg = <0x000C0000 0x080000>;
++ read-only;
++ };
++ bl2@00140000 {
++ reg = <0x00140000 0x040000>;
++ read-only;
++ };
++ cert_header_sa6@00180000 {
++ reg = <0x00180000 0x040000>;
++ read-only;
++ };
++ bl31@001C0000 {
++ reg = <0x001C0000 0x460000>;
++ read-only;
++ };
++ uboot@00640000 {
++ reg = <0x00640000 0x0C0000>;
++ read-only;
++ };
++ uboot-env@00700000 {
++ reg = <0x00700000 0x040000>;
++ read-only;
++ };
++ dtb@00740000 {
++ reg = <0x00740000 0x080000>;
++ };
++ kernel@007C0000 {
++ reg = <0x007C0000 0x1400000>;
++ };
++ user@01BC0000 {
++ reg = <0x01BC0000 0x2440000>;
++ };
++ };
++ };
+ };
+
+ &scif0 {
+--
+2.7.4
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0122-r8a7797-v3msk-r8a7797-v3mzf-dts-Add-spi-flash-s25fs5.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0122-r8a7797-v3msk-r8a7797-v3mzf-dts-Add-spi-flash-s25fs5.patch
new file mode 100644
index 0000000..07a55d5
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0122-r8a7797-v3msk-r8a7797-v3mzf-dts-Add-spi-flash-s25fs5.patch
@@ -0,0 +1,172 @@
+From 22f31ca58e24e9c8b92361e0d1f57a59d1fdbf64 Mon Sep 17 00:00:00 2001
+From: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+Date: Fri, 23 Mar 2018 16:32:07 +0300
+Subject: [PATCH] r8a7797-v3msk,r8a7797-v3mzf: dts: Add spi flash s25fs512
+
+Signed-off-by: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+---
+ arch/arm64/boot/dts/renesas/r8a7797-v3msk.dts | 68 +++++++++++++++++++++++++++
+ arch/arm64/boot/dts/renesas/r8a7797-v3mzf.dts | 68 +++++++++++++++++++++++++++
+ 2 files changed, 136 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/renesas/r8a7797-v3msk.dts b/arch/arm64/boot/dts/renesas/r8a7797-v3msk.dts
+index 33c6c0d..604475d 100644
+--- a/arch/arm64/boot/dts/renesas/r8a7797-v3msk.dts
++++ b/arch/arm64/boot/dts/renesas/r8a7797-v3msk.dts
+@@ -213,6 +213,74 @@
+ function = "mmc";
+ power-source = <3300>;
+ };
++
++ qspi0_pins: qspi0 {
++ groups = "qspi0_ctrl", "qspi0_data4";
++ function = "qspi0";
++ };
++
++ qspi1_pins: qspi1 {
++ groups = "qspi1_ctrl", "qspi1_data4";
++ function = "qspi1";
++ };
++};
++
++&qspi0 {
++ pinctrl-0 = <&qspi0_pins &qspi1_pins>;
++ pinctrl-names = "default";
++
++ status = "okay";
++
++ flash@0 {
++ compatible = "spansion,s25fs512s", "jedec,spi-nor";
++ reg = <0>;
++ spi-max-frequency = <50000000>;
++ spi-rx-bus-width = <4>;
++
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ bootparam@0 {
++ reg = <0x00000000 0x040000>;
++ read-only;
++ };
++ cr7@00040000 {
++ reg = <0x00040000 0x080000>;
++ read-only;
++ };
++ cert_header_sa3@000C0000 {
++ reg = <0x000C0000 0x080000>;
++ read-only;
++ };
++ bl2@00140000 {
++ reg = <0x00140000 0x040000>;
++ read-only;
++ };
++ cert_header_sa6@00180000 {
++ reg = <0x00180000 0x040000>;
++ read-only;
++ };
++ bl31@001C0000 {
++ reg = <0x001C0000 0x460000>;
++ read-only;
++ };
++ uboot@00640000 {
++ reg = <0x00640000 0x100000>;
++ read-only;
++ };
++ dtb@00740000 {
++ reg = <0x00740000 0x080000>;
++ };
++ kernel@007C0000 {
++ reg = <0x007C0000 0x1400000>;
++ };
++ user@01BC0000 {
++ reg = <0x01BC0000 0x2440000>;
++ };
++ };
++ };
+ };
+
+ &scif0 {
+diff --git a/arch/arm64/boot/dts/renesas/r8a7797-v3mzf.dts b/arch/arm64/boot/dts/renesas/r8a7797-v3mzf.dts
+index 71d7bad..4a64881 100644
+--- a/arch/arm64/boot/dts/renesas/r8a7797-v3mzf.dts
++++ b/arch/arm64/boot/dts/renesas/r8a7797-v3mzf.dts
+@@ -401,6 +401,74 @@
+ function = "mmc";
+ power-source = <3300>;
+ };
++
++ qspi0_pins: qspi0 {
++ groups = "qspi0_ctrl", "qspi0_data4";
++ function = "qspi0";
++ };
++
++ qspi1_pins: qspi1 {
++ groups = "qspi1_ctrl", "qspi1_data4";
++ function = "qspi1";
++ };
++};
++
++&qspi0 {
++ pinctrl-0 = <&qspi0_pins &qspi1_pins>;
++ pinctrl-names = "default";
++
++ status = "okay";
++
++ flash@0 {
++ compatible = "spansion,s25fs512s", "jedec,spi-nor";
++ reg = <0>;
++ spi-max-frequency = <50000000>;
++ spi-rx-bus-width = <4>;
++
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ bootparam@0 {
++ reg = <0x00000000 0x040000>;
++ read-only;
++ };
++ cr7@00040000 {
++ reg = <0x00040000 0x080000>;
++ read-only;
++ };
++ cert_header_sa3@000C0000 {
++ reg = <0x000C0000 0x080000>;
++ read-only;
++ };
++ bl2@00140000 {
++ reg = <0x00140000 0x040000>;
++ read-only;
++ };
++ cert_header_sa6@00180000 {
++ reg = <0x00180000 0x040000>;
++ read-only;
++ };
++ bl31@001C0000 {
++ reg = <0x001C0000 0x460000>;
++ read-only;
++ };
++ uboot@00640000 {
++ reg = <0x00640000 0x100000>;
++ read-only;
++ };
++ dtb@00740000 {
++ reg = <0x00740000 0x080000>;
++ };
++ kernel@007C0000 {
++ reg = <0x007C0000 0x1400000>;
++ };
++ user@01BC0000 {
++ reg = <0x01BC0000 0x2440000>;
++ };
++ };
++ };
+ };
+
+ &scif0 {
+--
+2.7.4
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0123-V3HSK-dts-Add-qspi-node.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0123-V3HSK-dts-Add-qspi-node.patch
new file mode 100644
index 0000000..4e39ed6
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0123-V3HSK-dts-Add-qspi-node.patch
@@ -0,0 +1,101 @@
+From 2e5e9bce98281417fec62cb8af23c086c3d5b01a Mon Sep 17 00:00:00 2001
+From: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+Date: Thu, 5 Apr 2018 17:23:14 +0300
+Subject: [PATCH] V3HSK: dts: Add qspi node
+
+Signed-off-by: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+---
+ arch/arm64/boot/dts/renesas/r8a7798-v3hsk.dts | 73 +++++++++++++++++++++++++++
+ 1 file changed, 73 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/renesas/r8a7798-v3hsk.dts b/arch/arm64/boot/dts/renesas/r8a7798-v3hsk.dts
+index bf8abe6..d863a95 100644
+--- a/arch/arm64/boot/dts/renesas/r8a7798-v3hsk.dts
++++ b/arch/arm64/boot/dts/renesas/r8a7798-v3hsk.dts
+@@ -225,6 +225,17 @@
+ groups = "tpu_to0";
+ function = "tpu";
+ };
++
++
++ qspi0_pins: qspi0 {
++ groups = "qspi0_ctrl", "qspi0_data4";
++ function = "qspi0";
++ };
++
++ qspi1_pins: qspi1 {
++ groups = "qspi1_ctrl", "qspi1_data4";
++ function = "qspi1";
++ };
+ };
+
+ &scif0 {
+@@ -356,3 +367,65 @@
+ max-speed = <1000>;
+ };
+ };
++
++&qspi0 {
++ pinctrl-0 = <&qspi0_pins &qspi1_pins>;
++ pinctrl-names = "default";
++
++ status = "okay";
++
++ flash@0 {
++ compatible = "spansion,s25fs512s", "jedec,spi-nor";
++ reg = <0>;
++ spi-max-frequency = <50000000>;
++ spi-rx-bus-width = <4>;
++
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ bootparam@0 {
++ reg = <0x00000000 0x040000>;
++ read-only;
++ };
++ cr7@00040000 {
++ reg = <0x00040000 0x080000>;
++ read-only;
++ };
++ cert_header_sa3@000C0000 {
++ reg = <0x000C0000 0x080000>;
++ read-only;
++ };
++ bl2@00140000 {
++ reg = <0x00140000 0x040000>;
++ read-only;
++ };
++ cert_header_sa6@00180000 {
++ reg = <0x00180000 0x040000>;
++ read-only;
++ };
++ bl31@001C0000 {
++ reg = <0x001C0000 0x460000>;
++ read-only;
++ };
++ uboot@00640000 {
++ reg = <0x00640000 0x0C0000>;
++ read-only;
++ };
++ uboot-env@00700000 {
++ reg = <0x00700000 0x040000>;
++ read-only;
++ };
++ dtb@00740000 {
++ reg = <0x00740000 0x080000>;
++ };
++ kernel@007C0000 {
++ reg = <0x007C0000 0x1400000>;
++ };
++ user@01BC0000 {
++ reg = <0x01BC0000 0x2440000>;
++ };
++ };
++ };
++};
+--
+2.7.4
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0124-RPC-Hyperflash-Add-devicetree-support.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0124-RPC-Hyperflash-Add-devicetree-support.patch
new file mode 100644
index 0000000..f347d47
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0124-RPC-Hyperflash-Add-devicetree-support.patch
@@ -0,0 +1,219 @@
+From 466608ee2217ce02df6df977a35c4afa6ee1e350 Mon Sep 17 00:00:00 2001
+From: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+Date: Wed, 4 Apr 2018 12:29:47 +0300
+Subject: [PATCH 1/2] RPC: Hyperflash: Add devicetree support
+
+Signed-off-by: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+---
+ drivers/mtd/rpc_hyperflash.c | 142 +++++++++++++++++++++++--------------------
+ 1 file changed, 77 insertions(+), 65 deletions(-)
+
+diff --git a/drivers/mtd/rpc_hyperflash.c b/drivers/mtd/rpc_hyperflash.c
+index cf4d56e..c15e520 100644
+--- a/drivers/mtd/rpc_hyperflash.c
++++ b/drivers/mtd/rpc_hyperflash.c
+@@ -15,8 +15,10 @@
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
+ #include <linux/of.h>
++#include <linux/clk.h>
+ #include <linux/rwsem.h>
+ #include <linux/slab.h>
++#include <linux/platform_device.h>
+
+ /* RPC */
+ #define RPC_BASE 0xEE200000
+@@ -156,10 +158,9 @@
+
+ struct rpc_info {
+ struct rw_semaphore lock;
++ struct clk *clk;
+ void __iomem *rpc_base;
+ void __iomem *flash_base;
+- struct resource *rpc_res;
+- struct resource *flash_res;
+ u32 flash_id;
+ struct mtd_info mtd;
+ };
+@@ -243,8 +244,6 @@ enum rpc_hf_size {
+ RPC_HF_SIZE_64BIT = RPC_SMENR_SPIDE(0xF),
+ };
+
+-struct rpc_info *rpc_info;
+-
+ static void rpc_hf_mode_man(struct rpc_info *info)
+ {
+ rpc_wait_tend(info);
+@@ -861,8 +860,8 @@ static int rpc_hf_init_mtd(struct rpc_info *info)
+ rpc_hf_read_reg(info, 0x27 << 1, data, RPC_HF_SIZE_16BIT);
+ size = 1 << data[0];
+
+- if (size > resource_size(info->flash_res))
+- size = resource_size(info->flash_res);
++ if (size > RPC_FLASH_SIZE)
++ size = RPC_FLASH_SIZE;
+
+ if (size & (RPC_HF_ERASE_SIZE - 1)) {
+ retval = -EINVAL;
+@@ -891,86 +890,99 @@ static int rpc_hf_init_mtd(struct rpc_info *info)
+ return retval;
+ }
+
+-static int rpc_flash_init(void)
++static int rpc_flash_probe(struct platform_device *pdev)
+ {
+- struct rpc_info *info;
++ struct rpc_info *rpc;
+ struct resource *res;
+- void __iomem *base;
+- int retval = -ENODEV;
+-
+- if (!of_machine_is_compatible("renesas,r8a7795"))
+- return -ENODEV;
++ int ret;
+
+- info = kzalloc(sizeof(*info), GFP_KERNEL);
+- if (!info)
++ rpc = devm_kzalloc(&pdev->dev, sizeof(*rpc), GFP_KERNEL);
++ if (!rpc)
+ return -ENOMEM;
+
+- res = request_mem_region(RPC_BASE, RPC_SIZE, "RPC");
+- if (!res)
+- goto out_info;
++ /* ...get memory */
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ rpc->rpc_base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(rpc->rpc_base)) {
++ dev_err(&pdev->dev, "cannot get resources\n");
++ ret = PTR_ERR(rpc->rpc_base);
++ goto error;
++ }
+
+- info->rpc_res = res;
+- base = ioremap(res->start, resource_size(res));
+- if (!base)
+- goto out_rpc_res;
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
++ rpc->flash_base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(rpc->flash_base)) {
++ dev_err(&pdev->dev, "cannot get resources\n");
++ ret = PTR_ERR(rpc->flash_base);
++ goto error;
++ }
+
+- info->rpc_base = base;
+- res = request_mem_region(RPC_FLASH_BASE, RPC_FLASH_SIZE, "RPC-ext");
+- if (!res)
+- goto out_rpc_base;
++ /* ...get clk */
++ rpc->clk = devm_clk_get(&pdev->dev, NULL);
++ if (IS_ERR(rpc->clk)) {
++ dev_err(&pdev->dev, "cannot get clock\n");
++ ret = PTR_ERR(rpc->clk);
++ goto error;
++ }
+
+- info->flash_res = res;
+- base = ioremap(res->start, resource_size(res));
+- if (!base)
+- goto out_flash_res;
++ /* ... enable clk */
++ ret = clk_prepare_enable(rpc->clk);
++ if (ret) {
++ dev_err(&pdev->dev, "cannot prepare clock\n");
++ goto error;
++ }
++
++ platform_set_drvdata(pdev, rpc);
+
+- info->flash_base = base;
+- retval = rpc_hf_init_mtd(info);
+- if (retval)
+- goto out_flash_base;
++ mtd_set_of_node(&rpc->mtd, pdev->dev.of_node);
++ ret = rpc_hf_init_mtd(rpc);
++ if (ret) {
++ dev_err(&pdev->dev, "mtd device register error.\n");
++ goto error_clk_disable;
++ }
+
+- pr_info("HyperFlash Id: %x\n", info->flash_id);
++ dev_info(&pdev->dev, "HyperFlash Id: %x\n", rpc->flash_id);
+
+- rpc_info = info;
+ return 0;
+
+-out_flash_base:
+- iounmap(info->flash_base);
+-out_flash_res:
+- release_mem_region(info->flash_res->start,
+- resource_size(info->flash_res));
+-out_rpc_base:
+- iounmap(info->rpc_base);
+-out_rpc_res:
+- release_mem_region(info->rpc_res->start,
+- resource_size(info->rpc_res));
+-out_info:
+- kfree(info);
+- return retval;
++
++error_clk_disable:
++ clk_disable_unprepare(rpc->clk);
++error:
++ return ret;
+ }
+
+-static void rpc_flash_exit(void)
++static int rpc_flash_exit(struct platform_device *pdev)
+ {
+- struct rpc_info *info = rpc_info;
++ struct rpc_info *rpc = platform_get_drvdata(pdev);
++
++ /* HW shutdown */
++ clk_disable_unprepare(rpc->clk);
++ mtd_device_unregister(&rpc->mtd);
++ return 0;
++}
+
+- if (!info)
+- return;
+
+- rpc_info = NULL;
++static const struct of_device_id rpc_flash_of_match[] = {
++ { .compatible = "renesas,rpc-hyperflash-r8a7798" },
++ { .compatible = "renesas,rpc-hyperflash-r8a7797" },
++ { },
++};
+
+- mtd_device_unregister(&info->mtd);
++MODULE_DEVICE_TABLE(of, rpc_flash_of_match);
+
+- iounmap(info->flash_base);
+- release_mem_region(info->flash_res->start,
+- resource_size(info->flash_res));
+- iounmap(info->rpc_base);
+- release_mem_region(info->rpc_res->start,
+- resource_size(info->rpc_res));
+- kfree(info);
+-}
++/* platform driver interface */
++static struct platform_driver rpc_flash_platform_driver = {
++ .probe = rpc_flash_probe,
++ .remove = rpc_flash_exit,
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = "rpc",
++ .of_match_table = of_match_ptr(rpc_flash_of_match),
++ },
++};
+
+-module_init(rpc_flash_init);
+-module_exit(rpc_flash_exit);
++module_platform_driver(rpc_flash_platform_driver);
+
+ MODULE_LICENSE("GPL v2");
+ MODULE_DESCRIPTION("Renesas RPC HyperFlash MTD driver");
+--
+2.7.4
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0125-r8a7797-pinctrl-Add-pin-function-for-hyperflash.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0125-r8a7797-pinctrl-Add-pin-function-for-hyperflash.patch
new file mode 100644
index 0000000..08bee0d
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0125-r8a7797-pinctrl-Add-pin-function-for-hyperflash.patch
@@ -0,0 +1,70 @@
+From 4b5dc172d87cc8291ddb7377d3252b6777cf8d4b Mon Sep 17 00:00:00 2001
+From: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+Date: Wed, 4 Apr 2018 12:59:59 +0300
+Subject: [PATCH 2/2] r8a7797: pinctrl: Add pin function for hyperflash
+
+Signed-off-by: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+---
+ drivers/pinctrl/sh-pfc/pfc-r8a7797.c | 25 +++++++++++++++++++++++++
+ 1 file changed, 25 insertions(+)
+
+diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7797.c b/drivers/pinctrl/sh-pfc/pfc-r8a7797.c
+index 4f451c4..d989acd 100644
+--- a/drivers/pinctrl/sh-pfc/pfc-r8a7797.c
++++ b/drivers/pinctrl/sh-pfc/pfc-r8a7797.c
+@@ -1196,6 +1196,25 @@ static const unsigned int qspi1_data4_mux[] = {
+ QSPI1_IO2_MARK, QSPI1_IO3_MARK
+ };
+
++static const unsigned int rpc_hyperflash_pins[] = {
++ RCAR_GP_PIN(5, 14), RCAR_GP_PIN(5, 12),
++ RCAR_GP_PIN(5, 11), RCAR_GP_PIN(5, 7),
++ RCAR_GP_PIN(5, 8), RCAR_GP_PIN(5, 9),
++ RCAR_GP_PIN(5, 10), RCAR_GP_PIN(5, 6),
++ RCAR_GP_PIN(5, 5), RCAR_GP_PIN(5, 4),
++ RCAR_GP_PIN(5, 3), RCAR_GP_PIN(5, 2),
++ RCAR_GP_PIN(5, 1), RCAR_GP_PIN(5, 0)
++};
++static const unsigned int rpc_hyperflash_mux[] = {
++ RPC_INT_N_MARK, RPC_RESET_N_MARK,
++ QSPI1_SSL_MARK, QSPI1_IO3_MARK,
++ QSPI1_IO2_MARK, QSPI1_MISO_IO1_MARK,
++ QSPI1_MOSI_IO0_MARK, QSPI1_SPCLK_MARK,
++ QSPI0_SSL_MARK, QSPI0_IO3_MARK,
++ QSPI0_IO2_MARK, QSPI0_MISO_IO1_MARK,
++ QSPI0_MOSI_IO0_MARK, QSPI0_SPCLK_MARK
++};
++
+ /* - I2C -------------------------------------------------------------------- */
+ static const unsigned int i2c0_pins[] = {
+ /* SDA0, SCL0 */
+@@ -2030,6 +2049,7 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
+ SH_PFC_PIN_GROUP(qspi1_ctrl),
+ SH_PFC_PIN_GROUP(qspi1_data2),
+ SH_PFC_PIN_GROUP(qspi1_data4),
++ SH_PFC_PIN_GROUP(rpc_hyperflash),
+ };
+
+ static const char * const avb0_groups[] = {
+@@ -2272,6 +2292,10 @@ static const char * const qspi1_groups[] = {
+ "qspi1_data4",
+ };
+
++static const char * const rpc_hyperflash_groups[] = {
++ "rpc_hyperflash",
++};
++
+ #define POCCTRL0 0x380
+ #define POCCTRL1 0x384
+ #define PIN2POCCTRL0_SHIFT(a) ({ \
+@@ -2317,6 +2341,7 @@ static const struct sh_pfc_function pinmux_functions[] = {
+ SH_PFC_FUNCTION(vin1),
+ SH_PFC_FUNCTION(qspi0),
+ SH_PFC_FUNCTION(qspi1),
++ SH_PFC_FUNCTION(rpc_hyperflash),
+ };
+
+ static const struct pinmux_cfg_reg pinmux_config_regs[] = {
+--
+2.7.4
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0126-r8a7798-pinctrl-Add-pin-function-for-hyperflash.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0126-r8a7798-pinctrl-Add-pin-function-for-hyperflash.patch
new file mode 100644
index 0000000..0babaa4
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0126-r8a7798-pinctrl-Add-pin-function-for-hyperflash.patch
@@ -0,0 +1,69 @@
+From 6633820967dcedde29b2e0507b7583c08901a12f Mon Sep 17 00:00:00 2001
+From: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+Date: Thu, 5 Apr 2018 18:32:59 +0300
+Subject: [PATCH] r8a7798: pinctrl: Add pin function for hyperflash
+
+Signed-off-by: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
+---
+ drivers/pinctrl/sh-pfc/pfc-r8a7798.c | 24 ++++++++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7798.c b/drivers/pinctrl/sh-pfc/pfc-r8a7798.c
+index f20afd1..f6f159e 100644
+--- a/drivers/pinctrl/sh-pfc/pfc-r8a7798.c
++++ b/drivers/pinctrl/sh-pfc/pfc-r8a7798.c
+@@ -1525,6 +1525,24 @@ static const unsigned int qspi1_data4_mux[] = {
+ QSPI1_IO2_MARK, QSPI1_IO3_MARK
+ };
+
++static const unsigned int rpc_hyperflash_pins[] = {
++ RCAR_GP_PIN(5, 14), RCAR_GP_PIN(5, 12),
++ RCAR_GP_PIN(5, 11), RCAR_GP_PIN(5, 7),
++ RCAR_GP_PIN(5, 8), RCAR_GP_PIN(5, 9),
++ RCAR_GP_PIN(5, 10), RCAR_GP_PIN(5, 6),
++ RCAR_GP_PIN(5, 5), RCAR_GP_PIN(5, 4),
++ RCAR_GP_PIN(5, 3), RCAR_GP_PIN(5, 2),
++ RCAR_GP_PIN(5, 1), RCAR_GP_PIN(5, 0)
++};
++static const unsigned int rpc_hyperflash_mux[] = {
++ RPC_INT_N_MARK, RPC_RESET_N_MARK,
++ QSPI1_SSL_MARK, QSPI1_IO3_MARK,
++ QSPI1_IO2_MARK, QSPI1_MISO_IO1_MARK,
++ QSPI1_MOSI_IO0_MARK, QSPI1_SPCLK_MARK,
++ QSPI0_SSL_MARK, QSPI0_IO3_MARK,
++ QSPI0_IO2_MARK, QSPI0_MISO_IO1_MARK,
++ QSPI0_MOSI_IO0_MARK, QSPI0_SPCLK_MARK
++};
+
+ /* - I2C -------------------------------------------------------------------- */
+ static const unsigned int i2c0_pins[] = {
+@@ -2479,6 +2497,7 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
+ SH_PFC_PIN_GROUP(qspi1_ctrl),
+ SH_PFC_PIN_GROUP(qspi1_data2),
+ SH_PFC_PIN_GROUP(qspi1_data4),
++ SH_PFC_PIN_GROUP(rpc_hyperflash),
+ };
+
+ static const char * const avb_groups[] = {
+@@ -2755,6 +2774,10 @@ static const char * const qspi1_groups[] = {
+ "qspi1_data4",
+ };
+
++static const char * const rpc_hyperflash_groups[] = {
++ "rpc_hyperflash",
++};
++
+ static const struct sh_pfc_function pinmux_functions[] = {
+ SH_PFC_FUNCTION(avb),
+ SH_PFC_FUNCTION(gether),
+@@ -2793,6 +2816,7 @@ static const struct sh_pfc_function pinmux_functions[] = {
+ SH_PFC_FUNCTION(vin1),
+ SH_PFC_FUNCTION(qspi0),
+ SH_PFC_FUNCTION(qspi1),
++ SH_PFC_FUNCTION(rpc_hyperflash),
+ };
+
+ static const struct pinmux_cfg_reg pinmux_config_regs[] = {
+--
+2.7.4
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/qspi.cfg b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/qspi.cfg
new file mode 100644
index 0000000..9a1c695
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/qspi.cfg
@@ -0,0 +1,3 @@
+CONFIG_SPI_RENESAS_RPC=y
+CONFIG_MTD_BLOCK=y
+CONFIG_JFFS2_FS=y