diff options
author | Andrey Dolnikov <andrey.dolnikov@cogentembedded.com> | 2018-04-11 17:03:45 +0300 |
---|---|---|
committer | Andrey Dolnikov <andrey.dolnikov@cogentembedded.com> | 2018-04-11 17:03:45 +0300 |
commit | 3c34681dd3fe562d5d65d83229caa921afc67a1f (patch) | |
tree | 06912cf66a5102c4707b75ae75da388665f6b523 /meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas | |
parent | dd06e952c5efab2914849b703a94f8b5e63bec86 (diff) |
Add QSPI support and Hyperflash devicetree support.
Diffstat (limited to 'meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas')
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 |