From ce19d4ca7dc43ff053d2a06f4bed9696cee97902 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 4 Apr 2018 13:34:40 +0200 Subject: [PATCH 01/36] ARM: rmobile: Split U-Boot and SPL sources on Porter Pull the SPL code from porter.c into a separate file in preparation for the addition of system initialization code. No functional change. Signed-off-by: Marek Vasut Cc: Nobuhiro Iwamatsu --- board/renesas/porter/Makefile | 4 +++ board/renesas/porter/porter.c | 22 ---------------- board/renesas/porter/porter_spl.c | 55 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 22 deletions(-) create mode 100644 board/renesas/porter/porter_spl.c diff --git a/board/renesas/porter/Makefile b/board/renesas/porter/Makefile index b0cfb1b..c237ee5 100644 --- a/board/renesas/porter/Makefile +++ b/board/renesas/porter/Makefile @@ -7,4 +7,8 @@ # SPDX-License-Identifier: GPL-2.0 # +ifdef CONFIG_SPL_BUILD +obj-y := porter_spl.o +else obj-y := porter.o qos.o +endif diff --git a/board/renesas/porter/porter.c b/board/renesas/porter/porter.c index 320841f..acd4f91 100644 --- a/board/renesas/porter/porter.c +++ b/board/renesas/porter/porter.c @@ -136,25 +136,3 @@ void reset_cpu(ulong addr) if (ret) hang(); } - -#ifdef CONFIG_SPL_BUILD -#include -void board_init_f(ulong dummy) -{ - board_early_init_f(); -} - -void spl_board_init(void) -{ - /* UART clocks enabled and gd valid - init serial console */ - preloader_console_init(); -} - -void board_boot_order(u32 *spl_boot_list) -{ - /* Boot from SPI NOR with YMODEM UART fallback. */ - spl_boot_list[0] = BOOT_DEVICE_SPI; - spl_boot_list[1] = BOOT_DEVICE_UART; - spl_boot_list[2] = BOOT_DEVICE_NONE; -} -#endif diff --git a/board/renesas/porter/porter_spl.c b/board/renesas/porter/porter_spl.c new file mode 100644 index 0000000..eb34469 --- /dev/null +++ b/board/renesas/porter/porter_spl.c @@ -0,0 +1,55 @@ +/* + * board/renesas/porter/porter_spl.c + * + * Copyright (C) 2018 Marek Vasut + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define TMU0_MSTP125 BIT(25) + +#define SD2CKCR 0xE615026C +#define SD_97500KHZ 0x7 + +void board_init_f(ulong dummy) +{ + mstp_clrbits_le32(MSTPSR1, SMSTPCR1, TMU0_MSTP125); + + /* + * SD0 clock is set to 97.5MHz by default. + * Set SD2 to the 97.5MHz as well. + */ + writel(SD_97500KHZ, SD2CKCR); +} + +void spl_board_init(void) +{ + /* UART clocks enabled and gd valid - init serial console */ + preloader_console_init(); +} + +void board_boot_order(u32 *spl_boot_list) +{ + /* Boot from SPI NOR with YMODEM UART fallback. */ + spl_boot_list[0] = BOOT_DEVICE_SPI; + spl_boot_list[1] = BOOT_DEVICE_UART; + spl_boot_list[2] = BOOT_DEVICE_NONE; +} + +void reset_cpu(ulong addr) +{ +} From c670607331dbdfdf74a52117b96426c50de9ecd0 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 4 Apr 2018 23:32:44 +0200 Subject: [PATCH 02/36] ARM: rmobile: Do not init caches in TPL before DRAM Skip the cache initialization, which can be done later on in U-Boot proper, since this interferes with early DRAM initialization in TPL. Signed-off-by: Marek Vasut Cc: Nobuhiro Iwamatsu --- arch/arm/mach-rmobile/lowlevel_init_ca15.S | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-rmobile/lowlevel_init_ca15.S b/arch/arm/mach-rmobile/lowlevel_init_ca15.S index a5dbbea..ef2280b 100644 --- a/arch/arm/mach-rmobile/lowlevel_init_ca15.S +++ b/arch/arm/mach-rmobile/lowlevel_init_ca15.S @@ -11,6 +11,7 @@ #include ENTRY(lowlevel_init) +#ifndef CONFIG_TPL_BUILD mrc p15, 0, r4, c0, c0, 5 /* mpidr */ orr r4, r4, r4, lsr #6 and r4, r4, #7 /* id 0-3 = ca15.0,1,2,3 */ @@ -83,6 +84,7 @@ _exit_init_l2_a15: bl s_init ldr lr, [sp] +#endif mov pc, lr nop ENDPROC(lowlevel_init) From 9a5483e9df9557c1387e9a0c15c881e6f0278448 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 3 Apr 2018 12:52:48 +0200 Subject: [PATCH 03/36] ARM: rmobile: Add TPL support on R8A7791 M2 Porter Add and enable TPL on M2 Porter. The TPL must fit into 16 kiB due to the Gen2 BootROM restriction. The TPL is running from MERAM and is capable of performing the initial initialization of PFC, Clock, GPIO, LBSC, DBSC and QSPI NOR. DBSC is responsible for bringing up the DDR DRAM access. The TPL is capable of loading the next stage, U-Boot, from either SPI NOR or UART as a fallback. If either does provide a valid U-Boot uImage, the system stops, which allows the operator to load U-Boot ie. via JTAG and start it manually. Signed-off-by: Marek Vasut Cc: Nobuhiro Iwamatsu --- arch/arm/mach-rmobile/Kconfig.32 | 1 + board/renesas/porter/porter_spl.c | 419 ++++++++++++++++++++++++++++++++++++++ configs/porter_defconfig | 17 ++ include/configs/porter.h | 6 + 4 files changed, 443 insertions(+) diff --git a/arch/arm/mach-rmobile/Kconfig.32 b/arch/arm/mach-rmobile/Kconfig.32 index a96938c..97260df 100644 --- a/arch/arm/mach-rmobile/Kconfig.32 +++ b/arch/arm/mach-rmobile/Kconfig.32 @@ -70,6 +70,7 @@ config TARGET_PORTER bool "Porter board" select DM select DM_SERIAL + select SUPPORT_TPL select SUPPORT_SPL select SPL_DM if SPL diff --git a/board/renesas/porter/porter_spl.c b/board/renesas/porter/porter_spl.c index eb34469..533b0b4 100644 --- a/board/renesas/porter/porter_spl.c +++ b/board/renesas/porter/porter_spl.c @@ -21,20 +21,439 @@ #include #define TMU0_MSTP125 BIT(25) +#define SCIF0_MSTP721 BIT(21) +#define QSPI_MSTP917 BIT(17) #define SD2CKCR 0xE615026C #define SD_97500KHZ 0x7 +#ifdef CONFIG_TPL_BUILD +struct reg_config { + u16 off; + u32 val; +}; + +static void dbsc_wait(u16 reg) +{ + static const u32 dbsc3_0_base = DBSC3_0_BASE; + static const u32 dbsc3_1_base = DBSC3_0_BASE + 0x10000; + + while (!(readl(dbsc3_0_base + reg) & BIT(0))) + ; + + while (!(readl(dbsc3_1_base + reg) & BIT(0))) + ; +} + +static void tpl_init_sys(void) +{ + u32 r0 = 0; + + writel(0xa5a5a500, 0xe6020004); + writel(0xa5a5a500, 0xe6030004); + + asm volatile( + /* ICIALLU - Invalidate I$ to PoU */ + "mcr 15, 0, %0, cr7, cr5, 0 \n" + /* BPIALL - Invalidate branch predictors */ + "mcr 15, 0, %0, cr7, cr5, 6 \n" + /* Set SCTLR[IZ] */ + "mrc 15, 0, %0, cr1, cr0, 0 \n" + "orr %0, #0x1800 \n" + "mcr 15, 0, %0, cr1, cr0, 0 \n" + "isb sy \n" + :"=r"(r0)); +} + +static void tpl_init_pfc(void) +{ + static const struct reg_config pfc_with_unlock[] = { + { 0x0090, 0x60000000 }, + { 0x0094, 0x60000000 }, + { 0x0098, 0x00800200 }, + { 0x009c, 0x00000000 }, + { 0x0020, 0x00000000 }, + { 0x0024, 0x00000000 }, + { 0x0028, 0x000244c8 }, + { 0x002c, 0x00000000 }, + { 0x0030, 0x00002400 }, + { 0x0034, 0x01520000 }, + { 0x0038, 0x00724003 }, + { 0x003c, 0x00000000 }, + { 0x0040, 0x00000000 }, + { 0x0044, 0x00000000 }, + { 0x0048, 0x00000000 }, + { 0x004c, 0x00000000 }, + { 0x0050, 0x00000000 }, + { 0x0054, 0x00000000 }, + { 0x0058, 0x00000000 }, + { 0x005c, 0x00000000 }, + { 0x0160, 0x00000000 }, + { 0x0004, 0xffffffff }, + { 0x0008, 0x00ec3fff }, + { 0x000c, 0x3bc001e7 }, + { 0x0010, 0x5bffffff }, + { 0x0014, 0x1ffffffb }, + { 0x0018, 0x01bffff0 }, + { 0x001c, 0xcf7fffff }, + { 0x0074, 0x0381fc00 }, + }; + + static const struct reg_config pfc_without_unlock[] = { + { 0x0100, 0xffffffdf }, + { 0x0104, 0xc883c3ff }, + { 0x0108, 0x1201f3c9 }, + { 0x010c, 0x00000000 }, + { 0x0110, 0xffffeb04 }, + { 0x0114, 0xc003ffff }, + { 0x0118, 0x0800000f }, + { 0x011c, 0x00187ff0 }, + }; + + static const u32 pfc_base = 0xe6060000; + + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(pfc_with_unlock); i++) { + writel(~pfc_with_unlock[i].val, pfc_base); + writel(pfc_with_unlock[i].val, + pfc_base | pfc_with_unlock[i].off); + } + + for (i = 0; i < ARRAY_SIZE(pfc_without_unlock); i++) + writel(pfc_without_unlock[i].val, + pfc_base | pfc_without_unlock[i].off); +} + +static void tpl_init_gpio(void) +{ + static const u16 gpio_offs[] = { + 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x5400, 0x5800 + }; + + static const struct reg_config gpio_set[] = { + { 0x2000, 0x04381000 }, + { 0x5000, 0x00000000 }, + { 0x5800, 0x000e0000 }, + }; + + static const struct reg_config gpio_clr[] = { + { 0x1000, 0x00000000 }, + { 0x2000, 0x04381010 }, + { 0x3000, 0x00000000 }, + { 0x4000, 0x00000000 }, + { 0x5000, 0x00400000 }, + { 0x5400, 0x00000000 }, + { 0x5800, 0x000e0380 }, + }; + + static const u32 gpio_base = 0xe6050000; + + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(gpio_offs); i++) + writel(0, gpio_base | 0x20 | gpio_offs[i]); + + for (i = 0; i < ARRAY_SIZE(gpio_offs); i++) + writel(0, gpio_base | 0x00 | gpio_offs[i]); + + for (i = 0; i < ARRAY_SIZE(gpio_set); i++) + writel(gpio_set[i].val, gpio_base | 0x08 | gpio_set[i].off); + + for (i = 0; i < ARRAY_SIZE(gpio_clr); i++) + writel(gpio_clr[i].val, gpio_base | 0x04 | gpio_clr[i].off); +} + +static void tpl_init_lbsc(void) +{ + static const struct reg_config lbsc_config[] = { + { 0x00, 0x00000020 }, + { 0x08, 0x00002020 }, + { 0x10, 0x2a103320 }, + { 0x18, 0xff70ff70 }, + }; + + static const u16 lbsc_offs[] = { + 0x80, 0x84, 0x88, 0x8c, 0xa0, 0xc0, 0xc4, 0xc8, 0x180 + }; + + static const u32 lbsc_base = 0xfec00200; + + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(lbsc_config); i++) { + writel(lbsc_config[i].val, + lbsc_base | lbsc_config[i].off); + writel(lbsc_config[i].val, + lbsc_base | (lbsc_config[i].off + 4)); + } + + for (i = 0; i < ARRAY_SIZE(lbsc_offs); i++) + writel(0, lbsc_base | lbsc_offs[i]); +} + +static void tpl_init_dbsc(void) +{ + static const struct reg_config dbsc_config1[] = { + { 0x0280, 0x0000a55a }, + { 0x4000, 0x0000a55a }, + { 0x4008, 0x00000001 }, + { 0x0018, 0x21000000 }, + { 0x0018, 0x11000000 }, + { 0x0018, 0x10000000 }, + { 0x0290, 0x00000001 }, + { 0x02a0, 0x80000000 }, + { 0x0290, 0x00000004 }, + }; + + static const struct reg_config dbsc_config2[] = { + { 0x0290, 0x00000006 }, + { 0x02a0, 0x0001c000 }, + }; + + static const struct reg_config dbsc_config3r0d0[] = { + { 0x0290, 0x0000000f }, + { 0x02a0, 0x00181885 }, + { 0x0290, 0x00000070 }, + { 0x02a0, 0x7c000887 }, + { 0x0290, 0x00000080 }, + { 0x02a0, 0x7c000887 }, + { 0x0290, 0x00000090 }, + { 0x02a0, 0x7c000887 }, + { 0x0290, 0x000000a0 }, + { 0x02a0, 0x7c000887 }, + { 0x0290, 0x000000b0 }, + { 0x02a0, 0x7c000880 }, + { 0x0290, 0x000000c0 }, + { 0x02a0, 0x7c000880 }, + { 0x0290, 0x000000d0 }, + { 0x02a0, 0x7c000880 }, + { 0x0290, 0x000000e0 }, + { 0x02a0, 0x7c000880 }, + }; + static const struct reg_config dbsc_config3r0d1[] = { + { 0x0290, 0x0000000f }, + { 0x02a0, 0x00181885 }, + { 0x0290, 0x00000070 }, + { 0x02a0, 0x7c000887 }, + { 0x0290, 0x00000080 }, + { 0x02a0, 0x7c000887 }, + { 0x0290, 0x00000090 }, + { 0x02a0, 0x7c000887 }, + { 0x0290, 0x000000a0 }, + { 0x02a0, 0x7c000887 }, + }; + + static const struct reg_config dbsc_config3r2[] = { + { 0x0290, 0x0000000f }, + { 0x02a0, 0x00181224 }, + }; + + static const struct reg_config dbsc_config4[] = { + { 0x0290, 0x00000010 }, + { 0x02a0, 0xf004649b }, + { 0x0290, 0x00000061 }, + { 0x02a0, 0x0000006d }, + { 0x0290, 0x00000001 }, + { 0x02a0, 0x00000073 }, + { 0x0020, 0x00000007 }, + { 0x0024, 0x0f030a02 }, + { 0x0030, 0x00000001 }, + { 0x00b0, 0x00000000 }, + { 0x0040, 0x0000000b }, + { 0x0044, 0x00000008 }, + { 0x0048, 0x00000000 }, + { 0x0050, 0x0000000b }, + { 0x0054, 0x000c000b }, + { 0x0058, 0x00000027 }, + { 0x005c, 0x0000001c }, + { 0x0060, 0x00000006 }, + { 0x0064, 0x00000020 }, + { 0x0068, 0x00000008 }, + { 0x006c, 0x0000000c }, + { 0x0070, 0x00000009 }, + { 0x0074, 0x00000012 }, + { 0x0078, 0x000000d0 }, + { 0x007c, 0x00140005 }, + { 0x0080, 0x00050004 }, + { 0x0084, 0x70233005 }, + { 0x0088, 0x000c0000 }, + { 0x008c, 0x00000200 }, + { 0x0090, 0x00000040 }, + { 0x0100, 0x00000001 }, + { 0x00c0, 0x00020001 }, + { 0x00c8, 0x20042004 }, + { 0x0380, 0x00020002 }, + { 0x0390, 0x0000001f }, + }; + + static const struct reg_config dbsc_config5[] = { + { 0x0244, 0x00000011 }, + { 0x0290, 0x00000003 }, + { 0x02a0, 0x0300c561 }, + { 0x0290, 0x00000023 }, + { 0x02a0, 0x00fcdb60 }, + { 0x0290, 0x00000011 }, + { 0x02a0, 0x1000040b }, + { 0x0290, 0x00000012 }, + { 0x02a0, 0x9d9cbb66 }, + { 0x0290, 0x00000013 }, + { 0x02a0, 0x1a868400 }, + { 0x0290, 0x00000014 }, + { 0x02a0, 0x300214d8 }, + { 0x0290, 0x00000015 }, + { 0x02a0, 0x00000d70 }, + { 0x0290, 0x00000016 }, + { 0x02a0, 0x00000006 }, + { 0x0290, 0x00000017 }, + { 0x02a0, 0x00000018 }, + { 0x0290, 0x0000001a }, + { 0x02a0, 0x910035c7 }, + { 0x0290, 0x00000004 }, + }; + + static const struct reg_config dbsc_config6[] = { + { 0x0290, 0x00000001 }, + { 0x02a0, 0x00000181 }, + { 0x0018, 0x11000000 }, + { 0x0290, 0x00000004 }, + }; + + static const struct reg_config dbsc_config7[] = { + { 0x0290, 0x00000001 }, + { 0x02a0, 0x0000fe01 }, + { 0x0304, 0x00000000 }, + { 0x00f4, 0x01004c20 }, + { 0x00f8, 0x014a00b9 }, + { 0x00e0, 0x00000140 }, + { 0x00e4, 0x00081860 }, + { 0x00e8, 0x00010000 }, + { 0x0290, 0x00000004 }, + }; + + static const struct reg_config dbsc_config8[] = { + { 0x0014, 0x00000001 }, + { 0x0290, 0x00000010 }, + { 0x02a0, 0xf00464db }, + { 0x4008, 0x00000000 }, + { 0x4000, 0x00000000 }, + { 0x0010, 0x00000001 }, + { 0x0280, 0x00000000 }, + }; + + static const u32 dbsc3_0_base = DBSC3_0_BASE; + static const u32 dbsc3_1_base = DBSC3_0_BASE + 0x10000; + static const u32 prr_base = 0xff000044; + const u16 prr_rev = readl(prr_base) & 0x7fff; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dbsc_config1); i++) { + writel(dbsc_config1[i].val, dbsc3_0_base | dbsc_config1[i].off); + writel(dbsc_config1[i].val, dbsc3_1_base | dbsc_config1[i].off); + } + + dbsc_wait(0x2a0); + + for (i = 0; i < ARRAY_SIZE(dbsc_config2); i++) { + writel(dbsc_config2[i].val, dbsc3_0_base | dbsc_config2[i].off); + writel(dbsc_config2[i].val, dbsc3_1_base | dbsc_config2[i].off); + } + + if (prr_rev == 0x4700) { + for (i = 0; i < ARRAY_SIZE(dbsc_config3r0d0); i++) { + writel(dbsc_config3r0d0[i].val, + dbsc3_0_base | dbsc_config3r0d0[i].off); + } + for (i = 0; i < ARRAY_SIZE(dbsc_config3r0d1); i++) { + writel(dbsc_config3r0d1[i].val, + dbsc3_1_base | dbsc_config3r0d1[i].off); + } + } else if (prr_rev != 0x4710) { + for (i = 0; i < ARRAY_SIZE(dbsc_config3r2); i++) { + writel(dbsc_config3r2[i].val, + dbsc3_0_base | dbsc_config3r2[i].off); + writel(dbsc_config3r2[i].val, + dbsc3_1_base | dbsc_config3r2[i].off); + } + } + + for (i = 0; i < ARRAY_SIZE(dbsc_config4); i++) { + writel(dbsc_config4[i].val, dbsc3_0_base | dbsc_config4[i].off); + writel(dbsc_config4[i].val, dbsc3_1_base | dbsc_config4[i].off); + } + + dbsc_wait(0x240); + + for (i = 0; i < ARRAY_SIZE(dbsc_config5); i++) { + writel(dbsc_config5[i].val, dbsc3_0_base | dbsc_config5[i].off); + writel(dbsc_config5[i].val, dbsc3_1_base | dbsc_config5[i].off); + } + + dbsc_wait(0x2a0); + + for (i = 0; i < ARRAY_SIZE(dbsc_config6); i++) { + writel(dbsc_config6[i].val, dbsc3_0_base | dbsc_config6[i].off); + writel(dbsc_config6[i].val, dbsc3_1_base | dbsc_config6[i].off); + } + + dbsc_wait(0x2a0); + + for (i = 0; i < ARRAY_SIZE(dbsc_config7); i++) { + writel(dbsc_config7[i].val, dbsc3_0_base | dbsc_config7[i].off); + writel(dbsc_config7[i].val, dbsc3_1_base | dbsc_config7[i].off); + } + + dbsc_wait(0x2a0); + + for (i = 0; i < ARRAY_SIZE(dbsc_config8); i++) { + writel(dbsc_config8[i].val, dbsc3_0_base | dbsc_config8[i].off); + writel(dbsc_config8[i].val, dbsc3_1_base | dbsc_config8[i].off); + } + +} + +static void tpl_init_qspi(void) +{ + mstp_clrbits_le32(MSTPSR9, SMSTPCR9, QSPI_MSTP917); + + static const u32 qspi_base = 0xe6b10000; + + writeb(0x08, qspi_base + 0x00); + writeb(0x00, qspi_base + 0x01); + writeb(0x06, qspi_base + 0x02); + writeb(0x01, qspi_base + 0x0a); + writeb(0x00, qspi_base + 0x0b); + writeb(0x00, qspi_base + 0x0c); + writeb(0x00, qspi_base + 0x0d); + writeb(0x00, qspi_base + 0x0e); + + writew(0xe080, qspi_base + 0x10); + + writeb(0xc0, qspi_base + 0x18); + writeb(0x00, qspi_base + 0x18); + writeb(0x00, qspi_base + 0x08); + writeb(0x48, qspi_base + 0x00); +} + void board_init_f(ulong dummy) { mstp_clrbits_le32(MSTPSR1, SMSTPCR1, TMU0_MSTP125); + mstp_clrbits_le32(MSTPSR7, SMSTPCR7, SCIF0_MSTP721); /* * SD0 clock is set to 97.5MHz by default. * Set SD2 to the 97.5MHz as well. */ writel(SD_97500KHZ, SD2CKCR); + + tpl_init_sys(); + tpl_init_pfc(); + tpl_init_gpio(); + tpl_init_lbsc(); + tpl_init_dbsc(); + tpl_init_qspi(); } +#endif void spl_board_init(void) { diff --git a/configs/porter_defconfig b/configs/porter_defconfig index 72a3108..a0e44df 100644 --- a/configs/porter_defconfig +++ b/configs/porter_defconfig @@ -7,18 +7,33 @@ CONFIG_SPL_LIBGENERIC_SUPPORT=y CONFIG_SYS_MALLOC_F_LEN=0x8000 CONFIG_R8A7791=y CONFIG_TARGET_PORTER=y +CONFIG_TPL_TEXT_BASE=0xe6300000 +CONFIG_TPL_MAX_SIZE=16384 CONFIG_SPL_SERIAL_SUPPORT=y +CONFIG_TPL_LIBCOMMON_SUPPORT=y +CONFIG_TPL_LIBGENERIC_SUPPORT=y CONFIG_SPL_SPI_FLASH_SUPPORT=y CONFIG_SPL_SPI_SUPPORT=y CONFIG_SPL=y CONFIG_DEFAULT_DEVICE_TREE="r8a7791-porter-u-boot" +CONFIG_TPL_SYS_MALLOC_F_LEN=0x2000 CONFIG_FIT=y CONFIG_BOOTDELAY=3 CONFIG_VERSION_VARIABLE=y CONFIG_SPL_BOARD_INIT=y +CONFIG_SPL_SYS_MALLOC_SIMPLE=y +CONFIG_TPL_SYS_MALLOC_SIMPLE=y CONFIG_SPL_I2C_SUPPORT=y CONFIG_SPL_SPI_LOAD=y CONFIG_SPL_YMODEM_SUPPORT=y +CONFIG_TPL=y +CONFIG_TPL_BOARD_INIT=y +CONFIG_TPL_NEEDS_SEPARATE_TEXT_BASE=y +CONFIG_TPL_SERIAL_SUPPORT=y +CONFIG_TPL_SPI_FLASH_SUPPORT=y +CONFIG_TPL_SPI_LOAD=y +CONFIG_TPL_SPI_SUPPORT=y +CONFIG_TPL_YMODEM_SUPPORT=y CONFIG_CMD_BOOTZ=y # CONFIG_CMD_IMI is not set # CONFIG_CMD_XIMG is not set @@ -71,6 +86,7 @@ CONFIG_PINCTRL_PFC=y CONFIG_DM_REGULATOR=y CONFIG_DM_REGULATOR_FIXED=y CONFIG_DM_REGULATOR_GPIO=y +# CONFIG_TPL_DM_SERIAL is not set CONFIG_SCIF_CONSOLE=y CONFIG_SH_QSPI=y CONFIG_USB=y @@ -78,3 +94,4 @@ CONFIG_DM_USB=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_EHCI_PCI=y CONFIG_USB_STORAGE=y +CONFIG_TPL_TINY_MEMSET=y diff --git a/include/configs/porter.h b/include/configs/porter.h index 48f4334..7c46b55 100644 --- a/include/configs/porter.h +++ b/include/configs/porter.h @@ -59,4 +59,10 @@ #define CONFIG_SPL_MAX_SIZE 0x40000 #define CONFIG_SYS_SPI_U_BOOT_OFFS 0x140000 +/* TPL support */ +#ifdef CONFIG_TPL_BUILD +#define CONFIG_CONS_SCIF0 +#define CONFIG_SH_SCIF_CLK_FREQ 65000000 +#endif + #endif /* __PORTER_H */ From 82239aa7cd6bca3fbb2335523e4f8ff782c401c8 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 7 Apr 2018 16:16:30 +0200 Subject: [PATCH 04/36] ARM: rmobile: Add JTAG recovery support for M2 Porter Add JTAG recovery support into the M2 Porter TPL. This allows the TPL to be loaded over JTAG, initialize the system, wait for the JTAG debugger to load U-Boot image into RAM and then resume and start U-Boot from RAM. The procedure is as follows: 1) Load u-boot-tpl.bin to 0xe6300000 2) Write magic number 0x1337c0de to 0xe6300020 TPL checks for this particular magic and starts JTAG recovery if this number is present. This is not present by default. 3) Start U-Boot TPL from 0xe6300000 4) Wait for a message from TPL on UART indicating JTAG boot: "JTAG boot detected!" 5) Halt the system in JTAG debugger 6) Load U-Boot image (u-boot.img) to 0x4fffffc0 7) Write magic number 0xb33fc0de to 0xe6300024 TPL checks for this particular magic to verify that the U-Boot image was loaded into DRAM by the JTAG debugger. 8) Resume the system in JTAG debugger Signed-off-by: Marek Vasut Cc: Nobuhiro Iwamatsu --- arch/arm/mach-rmobile/include/mach/boot0.h | 24 ++++++++++++++++++++++++ board/renesas/porter/porter_spl.c | 21 +++++++++++++++++++++ configs/porter_defconfig | 3 +++ 3 files changed, 48 insertions(+) create mode 100644 arch/arm/mach-rmobile/include/mach/boot0.h diff --git a/arch/arm/mach-rmobile/include/mach/boot0.h b/arch/arm/mach-rmobile/include/mach/boot0.h new file mode 100644 index 0000000..3edd461 --- /dev/null +++ b/arch/arm/mach-rmobile/include/mach/boot0.h @@ -0,0 +1,24 @@ +/* + * Specialty padding for the RCar Gen2 TPL JTAG loading + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __BOOT0_H +#define __BOOT0_H + +_start: + ARM_VECTORS + +#ifdef CONFIG_TPL_BUILD + .word 0x0badc0d3; + .word 0x0badc0d3; + .word 0x0badc0d3; + .word 0x0badc0d3; + .word 0x0badc0d3; + .word 0x0badc0d3; + .word 0x0badc0d3; + .word 0x0badc0d3; +#endif + +#endif /* __BOOT0_H */ diff --git a/board/renesas/porter/porter_spl.c b/board/renesas/porter/porter_spl.c index 533b0b4..f711aa9 100644 --- a/board/renesas/porter/porter_spl.c +++ b/board/renesas/porter/porter_spl.c @@ -463,6 +463,27 @@ void spl_board_init(void) void board_boot_order(u32 *spl_boot_list) { +#ifdef CONFIG_TPL_BUILD + const u32 jtag_magic = 0x1337c0de; + const u32 load_magic = 0xb33fc0de; + + /* + * If JTAG probe sets special word at 0xe6300020, then it must + * put U-Boot into RAM and TPL will start it from RAM. + */ + if (readl(CONFIG_TPL_TEXT_BASE + 0x20) == jtag_magic) { + printf("JTAG boot detected!\n"); + + while (readl(CONFIG_TPL_TEXT_BASE + 0x24) != load_magic) + ; + + spl_boot_list[0] = BOOT_DEVICE_RAM; + spl_boot_list[1] = BOOT_DEVICE_NONE; + + return; + } +#endif + /* Boot from SPI NOR with YMODEM UART fallback. */ spl_boot_list[0] = BOOT_DEVICE_SPI; spl_boot_list[1] = BOOT_DEVICE_UART; diff --git a/configs/porter_defconfig b/configs/porter_defconfig index a0e44df..e4a2828 100644 --- a/configs/porter_defconfig +++ b/configs/porter_defconfig @@ -1,4 +1,5 @@ CONFIG_ARM=y +CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK=y CONFIG_ARCH_RMOBILE=y CONFIG_SYS_TEXT_BASE=0x50000000 CONFIG_SPL_GPIO_SUPPORT=y @@ -29,6 +30,8 @@ CONFIG_SPL_YMODEM_SUPPORT=y CONFIG_TPL=y CONFIG_TPL_BOARD_INIT=y CONFIG_TPL_NEEDS_SEPARATE_TEXT_BASE=y +CONFIG_TPL_RAM_SUPPORT=y +CONFIG_TPL_RAM_DEVICE=y CONFIG_TPL_SERIAL_SUPPORT=y CONFIG_TPL_SPI_FLASH_SUPPORT=y CONFIG_TPL_SPI_LOAD=y From 78bf3f2180cd8d25c27cd6c25c80e2be95d0ccf6 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 7 Apr 2018 17:15:00 +0200 Subject: [PATCH 05/36] ARM: rmobile: Enable HUSH on M2 Porter Enable the HUSH shell, since it is far more capable. Signed-off-by: Marek Vasut Cc: Nobuhiro Iwamatsu --- configs/porter_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/porter_defconfig b/configs/porter_defconfig index e4a2828..7c58b13 100644 --- a/configs/porter_defconfig +++ b/configs/porter_defconfig @@ -37,6 +37,7 @@ CONFIG_TPL_SPI_FLASH_SUPPORT=y CONFIG_TPL_SPI_LOAD=y CONFIG_TPL_SPI_SUPPORT=y CONFIG_TPL_YMODEM_SUPPORT=y +CONFIG_HUSH_PARSER=y CONFIG_CMD_BOOTZ=y # CONFIG_CMD_IMI is not set # CONFIG_CMD_XIMG is not set From e94cad93b7f940de0eda446b0cd9a43da7a1e328 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 8 Apr 2018 15:22:58 +0200 Subject: [PATCH 06/36] mmc: uniphier: Split out SoC specific bits from the driver Factor out common code from the uniphier SD driver, change the prefix of the functions from uniphier_sd_ to matsu_sd_ and create separate renesas-sdhi.c driver. Thus far, all the code is still compiled when CONFIG_UNIPHIER_MMC is selected and there is no functional change. This patch is a preparation for further split of the SoC specific parts of the Matsushita SD driver, used both on Uniphier and R-Car. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/Makefile | 2 +- drivers/mmc/matsushita-common.c | 726 ++++++++++++++++++++++++++++++++++ drivers/mmc/matsushita-common.h | 136 +++++++ drivers/mmc/renesas-sdhi.c | 50 +++ drivers/mmc/uniphier-sd.c | 847 +--------------------------------------- 5 files changed, 921 insertions(+), 840 deletions(-) create mode 100644 drivers/mmc/matsushita-common.c create mode 100644 drivers/mmc/matsushita-common.h create mode 100644 drivers/mmc/renesas-sdhi.c diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 9583410..25eb669 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -62,5 +62,5 @@ obj-$(CONFIG_MMC_SDHCI_XENON) += xenon_sdhci.o obj-$(CONFIG_MMC_SDHCI_ZYNQ) += zynq_sdhci.o obj-$(CONFIG_MMC_SUNXI) += sunxi_mmc.o -obj-$(CONFIG_MMC_UNIPHIER) += uniphier-sd.o +obj-$(CONFIG_MMC_UNIPHIER) += matsushita-common.o uniphier-sd.o renesas-sdhi.o obj-$(CONFIG_MMC_BCM2835) += bcm2835_sdhost.o diff --git a/drivers/mmc/matsushita-common.c b/drivers/mmc/matsushita-common.c new file mode 100644 index 0000000..ed67710 --- /dev/null +++ b/drivers/mmc/matsushita-common.c @@ -0,0 +1,726 @@ +/* + * Copyright (C) 2016 Socionext Inc. + * Author: Masahiro Yamada + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "matsushita-common.h" + +DECLARE_GLOBAL_DATA_PTR; + +static u64 matsu_sd_readq(struct matsu_sd_priv *priv, unsigned int reg) +{ + if (priv->caps & MATSU_SD_CAP_64BIT) + return readq(priv->regbase + (reg << 1)); + else + return readq(priv->regbase + reg); +} + +static void matsu_sd_writeq(struct matsu_sd_priv *priv, + u64 val, unsigned int reg) +{ + if (priv->caps & MATSU_SD_CAP_64BIT) + writeq(val, priv->regbase + (reg << 1)); + else + writeq(val, priv->regbase + reg); +} + +static u32 matsu_sd_readl(struct matsu_sd_priv *priv, unsigned int reg) +{ + if (priv->caps & MATSU_SD_CAP_64BIT) + return readl(priv->regbase + (reg << 1)); + else + return readl(priv->regbase + reg); +} + +static void matsu_sd_writel(struct matsu_sd_priv *priv, + u32 val, unsigned int reg) +{ + if (priv->caps & MATSU_SD_CAP_64BIT) + writel(val, priv->regbase + (reg << 1)); + else + writel(val, priv->regbase + reg); +} + +static dma_addr_t __dma_map_single(void *ptr, size_t size, + enum dma_data_direction dir) +{ + unsigned long addr = (unsigned long)ptr; + + if (dir == DMA_FROM_DEVICE) + invalidate_dcache_range(addr, addr + size); + else + flush_dcache_range(addr, addr + size); + + return addr; +} + +static void __dma_unmap_single(dma_addr_t addr, size_t size, + enum dma_data_direction dir) +{ + if (dir != DMA_TO_DEVICE) + invalidate_dcache_range(addr, addr + size); +} + +static int matsu_sd_check_error(struct udevice *dev) +{ + struct matsu_sd_priv *priv = dev_get_priv(dev); + u32 info2 = matsu_sd_readl(priv, MATSU_SD_INFO2); + + if (info2 & MATSU_SD_INFO2_ERR_RTO) { + /* + * TIMEOUT must be returned for unsupported command. Do not + * display error log since this might be a part of sequence to + * distinguish between SD and MMC. + */ + return -ETIMEDOUT; + } + + if (info2 & MATSU_SD_INFO2_ERR_TO) { + dev_err(dev, "timeout error\n"); + return -ETIMEDOUT; + } + + if (info2 & (MATSU_SD_INFO2_ERR_END | MATSU_SD_INFO2_ERR_CRC | + MATSU_SD_INFO2_ERR_IDX)) { + dev_err(dev, "communication out of sync\n"); + return -EILSEQ; + } + + if (info2 & (MATSU_SD_INFO2_ERR_ILA | MATSU_SD_INFO2_ERR_ILR | + MATSU_SD_INFO2_ERR_ILW)) { + dev_err(dev, "illegal access\n"); + return -EIO; + } + + return 0; +} + +static int matsu_sd_wait_for_irq(struct udevice *dev, unsigned int reg, + u32 flag) +{ + struct matsu_sd_priv *priv = dev_get_priv(dev); + long wait = 1000000; + int ret; + + while (!(matsu_sd_readl(priv, reg) & flag)) { + if (wait-- < 0) { + dev_err(dev, "timeout\n"); + return -ETIMEDOUT; + } + + ret = matsu_sd_check_error(dev); + if (ret) + return ret; + + udelay(1); + } + + return 0; +} + +static int matsu_sd_pio_read_one_block(struct udevice *dev, char *pbuf, + uint blocksize) +{ + struct matsu_sd_priv *priv = dev_get_priv(dev); + int i, ret; + + /* wait until the buffer is filled with data */ + ret = matsu_sd_wait_for_irq(dev, MATSU_SD_INFO2, + MATSU_SD_INFO2_BRE); + if (ret) + return ret; + + /* + * Clear the status flag _before_ read the buffer out because + * MATSU_SD_INFO2_BRE is edge-triggered, not level-triggered. + */ + matsu_sd_writel(priv, 0, MATSU_SD_INFO2); + + if (priv->caps & MATSU_SD_CAP_64BIT) { + u64 *buf = (u64 *)pbuf; + if (likely(IS_ALIGNED((uintptr_t)buf, 8))) { + for (i = 0; i < blocksize / 8; i++) { + *buf++ = matsu_sd_readq(priv, + MATSU_SD_BUF); + } + } else { + for (i = 0; i < blocksize / 8; i++) { + u64 data; + data = matsu_sd_readq(priv, + MATSU_SD_BUF); + put_unaligned(data, buf++); + } + } + } else { + u32 *buf = (u32 *)pbuf; + if (likely(IS_ALIGNED((uintptr_t)buf, 4))) { + for (i = 0; i < blocksize / 4; i++) { + *buf++ = matsu_sd_readl(priv, + MATSU_SD_BUF); + } + } else { + for (i = 0; i < blocksize / 4; i++) { + u32 data; + data = matsu_sd_readl(priv, MATSU_SD_BUF); + put_unaligned(data, buf++); + } + } + } + + return 0; +} + +static int matsu_sd_pio_write_one_block(struct udevice *dev, + const char *pbuf, uint blocksize) +{ + struct matsu_sd_priv *priv = dev_get_priv(dev); + int i, ret; + + /* wait until the buffer becomes empty */ + ret = matsu_sd_wait_for_irq(dev, MATSU_SD_INFO2, + MATSU_SD_INFO2_BWE); + if (ret) + return ret; + + matsu_sd_writel(priv, 0, MATSU_SD_INFO2); + + if (priv->caps & MATSU_SD_CAP_64BIT) { + const u64 *buf = (const u64 *)pbuf; + if (likely(IS_ALIGNED((uintptr_t)buf, 8))) { + for (i = 0; i < blocksize / 8; i++) { + matsu_sd_writeq(priv, *buf++, + MATSU_SD_BUF); + } + } else { + for (i = 0; i < blocksize / 8; i++) { + u64 data = get_unaligned(buf++); + matsu_sd_writeq(priv, data, + MATSU_SD_BUF); + } + } + } else { + const u32 *buf = (const u32 *)pbuf; + if (likely(IS_ALIGNED((uintptr_t)buf, 4))) { + for (i = 0; i < blocksize / 4; i++) { + matsu_sd_writel(priv, *buf++, + MATSU_SD_BUF); + } + } else { + for (i = 0; i < blocksize / 4; i++) { + u32 data = get_unaligned(buf++); + matsu_sd_writel(priv, data, + MATSU_SD_BUF); + } + } + } + + return 0; +} + +static int matsu_sd_pio_xfer(struct udevice *dev, struct mmc_data *data) +{ + const char *src = data->src; + char *dest = data->dest; + int i, ret; + + for (i = 0; i < data->blocks; i++) { + if (data->flags & MMC_DATA_READ) + ret = matsu_sd_pio_read_one_block(dev, dest, + data->blocksize); + else + ret = matsu_sd_pio_write_one_block(dev, src, + data->blocksize); + if (ret) + return ret; + + if (data->flags & MMC_DATA_READ) + dest += data->blocksize; + else + src += data->blocksize; + } + + return 0; +} + +static void matsu_sd_dma_start(struct matsu_sd_priv *priv, + dma_addr_t dma_addr) +{ + u32 tmp; + + matsu_sd_writel(priv, 0, MATSU_SD_DMA_INFO1); + matsu_sd_writel(priv, 0, MATSU_SD_DMA_INFO2); + + /* enable DMA */ + tmp = matsu_sd_readl(priv, MATSU_SD_EXTMODE); + tmp |= MATSU_SD_EXTMODE_DMA_EN; + matsu_sd_writel(priv, tmp, MATSU_SD_EXTMODE); + + matsu_sd_writel(priv, dma_addr & U32_MAX, MATSU_SD_DMA_ADDR_L); + + /* suppress the warning "right shift count >= width of type" */ + dma_addr >>= min_t(int, 32, 8 * sizeof(dma_addr)); + + matsu_sd_writel(priv, dma_addr & U32_MAX, MATSU_SD_DMA_ADDR_H); + + matsu_sd_writel(priv, MATSU_SD_DMA_CTL_START, MATSU_SD_DMA_CTL); +} + +static int matsu_sd_dma_wait_for_irq(struct udevice *dev, u32 flag, + unsigned int blocks) +{ + struct matsu_sd_priv *priv = dev_get_priv(dev); + long wait = 1000000 + 10 * blocks; + + while (!(matsu_sd_readl(priv, MATSU_SD_DMA_INFO1) & flag)) { + if (wait-- < 0) { + dev_err(dev, "timeout during DMA\n"); + return -ETIMEDOUT; + } + + udelay(10); + } + + if (matsu_sd_readl(priv, MATSU_SD_DMA_INFO2)) { + dev_err(dev, "error during DMA\n"); + return -EIO; + } + + return 0; +} + +static int matsu_sd_dma_xfer(struct udevice *dev, struct mmc_data *data) +{ + struct matsu_sd_priv *priv = dev_get_priv(dev); + size_t len = data->blocks * data->blocksize; + void *buf; + enum dma_data_direction dir; + dma_addr_t dma_addr; + u32 poll_flag, tmp; + int ret; + + tmp = matsu_sd_readl(priv, MATSU_SD_DMA_MODE); + + if (data->flags & MMC_DATA_READ) { + buf = data->dest; + dir = DMA_FROM_DEVICE; + poll_flag = MATSU_SD_DMA_INFO1_END_RD2; + tmp |= MATSU_SD_DMA_MODE_DIR_RD; + } else { + buf = (void *)data->src; + dir = DMA_TO_DEVICE; + poll_flag = MATSU_SD_DMA_INFO1_END_WR; + tmp &= ~MATSU_SD_DMA_MODE_DIR_RD; + } + + matsu_sd_writel(priv, tmp, MATSU_SD_DMA_MODE); + + dma_addr = __dma_map_single(buf, len, dir); + + matsu_sd_dma_start(priv, dma_addr); + + ret = matsu_sd_dma_wait_for_irq(dev, poll_flag, data->blocks); + + __dma_unmap_single(dma_addr, len, dir); + + return ret; +} + +/* check if the address is DMA'able */ +static bool matsu_sd_addr_is_dmaable(unsigned long addr) +{ + if (!IS_ALIGNED(addr, MATSU_SD_DMA_MINALIGN)) + return false; + +#if defined(CONFIG_ARCH_UNIPHIER) && !defined(CONFIG_ARM64) && \ + defined(CONFIG_SPL_BUILD) + /* + * For UniPhier ARMv7 SoCs, the stack is allocated in the locked ways + * of L2, which is unreachable from the DMA engine. + */ + if (addr < CONFIG_SPL_STACK) + return false; +#endif + + return true; +} + +int matsu_sd_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct matsu_sd_priv *priv = dev_get_priv(dev); + int ret; + u32 tmp; + + if (matsu_sd_readl(priv, MATSU_SD_INFO2) & MATSU_SD_INFO2_CBSY) { + dev_err(dev, "command busy\n"); + return -EBUSY; + } + + /* clear all status flags */ + matsu_sd_writel(priv, 0, MATSU_SD_INFO1); + matsu_sd_writel(priv, 0, MATSU_SD_INFO2); + + /* disable DMA once */ + tmp = matsu_sd_readl(priv, MATSU_SD_EXTMODE); + tmp &= ~MATSU_SD_EXTMODE_DMA_EN; + matsu_sd_writel(priv, tmp, MATSU_SD_EXTMODE); + + matsu_sd_writel(priv, cmd->cmdarg, MATSU_SD_ARG); + + tmp = cmd->cmdidx; + + if (data) { + matsu_sd_writel(priv, data->blocksize, MATSU_SD_SIZE); + matsu_sd_writel(priv, data->blocks, MATSU_SD_SECCNT); + + /* Do not send CMD12 automatically */ + tmp |= MATSU_SD_CMD_NOSTOP | MATSU_SD_CMD_DATA; + + if (data->blocks > 1) + tmp |= MATSU_SD_CMD_MULTI; + + if (data->flags & MMC_DATA_READ) + tmp |= MATSU_SD_CMD_RD; + } + + /* + * Do not use the response type auto-detection on this hardware. + * CMD8, for example, has different response types on SD and eMMC, + * while this controller always assumes the response type for SD. + * Set the response type manually. + */ + switch (cmd->resp_type) { + case MMC_RSP_NONE: + tmp |= MATSU_SD_CMD_RSP_NONE; + break; + case MMC_RSP_R1: + tmp |= MATSU_SD_CMD_RSP_R1; + break; + case MMC_RSP_R1b: + tmp |= MATSU_SD_CMD_RSP_R1B; + break; + case MMC_RSP_R2: + tmp |= MATSU_SD_CMD_RSP_R2; + break; + case MMC_RSP_R3: + tmp |= MATSU_SD_CMD_RSP_R3; + break; + default: + dev_err(dev, "unknown response type\n"); + return -EINVAL; + } + + dev_dbg(dev, "sending CMD%d (SD_CMD=%08x, SD_ARG=%08x)\n", + cmd->cmdidx, tmp, cmd->cmdarg); + matsu_sd_writel(priv, tmp, MATSU_SD_CMD); + + ret = matsu_sd_wait_for_irq(dev, MATSU_SD_INFO1, + MATSU_SD_INFO1_RSP); + if (ret) + return ret; + + if (cmd->resp_type & MMC_RSP_136) { + u32 rsp_127_104 = matsu_sd_readl(priv, MATSU_SD_RSP76); + u32 rsp_103_72 = matsu_sd_readl(priv, MATSU_SD_RSP54); + u32 rsp_71_40 = matsu_sd_readl(priv, MATSU_SD_RSP32); + u32 rsp_39_8 = matsu_sd_readl(priv, MATSU_SD_RSP10); + + cmd->response[0] = ((rsp_127_104 & 0x00ffffff) << 8) | + ((rsp_103_72 & 0xff000000) >> 24); + cmd->response[1] = ((rsp_103_72 & 0x00ffffff) << 8) | + ((rsp_71_40 & 0xff000000) >> 24); + cmd->response[2] = ((rsp_71_40 & 0x00ffffff) << 8) | + ((rsp_39_8 & 0xff000000) >> 24); + cmd->response[3] = (rsp_39_8 & 0xffffff) << 8; + } else { + /* bit 39-8 */ + cmd->response[0] = matsu_sd_readl(priv, MATSU_SD_RSP10); + } + + if (data) { + /* use DMA if the HW supports it and the buffer is aligned */ + if (priv->caps & MATSU_SD_CAP_DMA_INTERNAL && + matsu_sd_addr_is_dmaable((long)data->src)) + ret = matsu_sd_dma_xfer(dev, data); + else + ret = matsu_sd_pio_xfer(dev, data); + + ret = matsu_sd_wait_for_irq(dev, MATSU_SD_INFO1, + MATSU_SD_INFO1_CMP); + if (ret) + return ret; + } + + return ret; +} + +static int matsu_sd_set_bus_width(struct matsu_sd_priv *priv, + struct mmc *mmc) +{ + u32 val, tmp; + + switch (mmc->bus_width) { + case 1: + val = MATSU_SD_OPTION_WIDTH_1; + break; + case 4: + val = MATSU_SD_OPTION_WIDTH_4; + break; + case 8: + val = MATSU_SD_OPTION_WIDTH_8; + break; + default: + return -EINVAL; + } + + tmp = matsu_sd_readl(priv, MATSU_SD_OPTION); + tmp &= ~MATSU_SD_OPTION_WIDTH_MASK; + tmp |= val; + matsu_sd_writel(priv, tmp, MATSU_SD_OPTION); + + return 0; +} + +static void matsu_sd_set_ddr_mode(struct matsu_sd_priv *priv, + struct mmc *mmc) +{ + u32 tmp; + + tmp = matsu_sd_readl(priv, MATSU_SD_IF_MODE); + if (mmc->ddr_mode) + tmp |= MATSU_SD_IF_MODE_DDR; + else + tmp &= ~MATSU_SD_IF_MODE_DDR; + matsu_sd_writel(priv, tmp, MATSU_SD_IF_MODE); +} + +static void matsu_sd_set_clk_rate(struct matsu_sd_priv *priv, + struct mmc *mmc) +{ + unsigned int divisor; + u32 val, tmp; + + if (!mmc->clock) + return; + + divisor = DIV_ROUND_UP(priv->mclk, mmc->clock); + + if (divisor <= 1) + val = MATSU_SD_CLKCTL_DIV1; + else if (divisor <= 2) + val = MATSU_SD_CLKCTL_DIV2; + else if (divisor <= 4) + val = MATSU_SD_CLKCTL_DIV4; + else if (divisor <= 8) + val = MATSU_SD_CLKCTL_DIV8; + else if (divisor <= 16) + val = MATSU_SD_CLKCTL_DIV16; + else if (divisor <= 32) + val = MATSU_SD_CLKCTL_DIV32; + else if (divisor <= 64) + val = MATSU_SD_CLKCTL_DIV64; + else if (divisor <= 128) + val = MATSU_SD_CLKCTL_DIV128; + else if (divisor <= 256) + val = MATSU_SD_CLKCTL_DIV256; + else if (divisor <= 512 || !(priv->caps & MATSU_SD_CAP_DIV1024)) + val = MATSU_SD_CLKCTL_DIV512; + else + val = MATSU_SD_CLKCTL_DIV1024; + + tmp = matsu_sd_readl(priv, MATSU_SD_CLKCTL); + if (tmp & MATSU_SD_CLKCTL_SCLKEN && + (tmp & MATSU_SD_CLKCTL_DIV_MASK) == val) + return; + + /* stop the clock before changing its rate to avoid a glitch signal */ + tmp &= ~MATSU_SD_CLKCTL_SCLKEN; + matsu_sd_writel(priv, tmp, MATSU_SD_CLKCTL); + + tmp &= ~MATSU_SD_CLKCTL_DIV_MASK; + tmp |= val | MATSU_SD_CLKCTL_OFFEN; + matsu_sd_writel(priv, tmp, MATSU_SD_CLKCTL); + + tmp |= MATSU_SD_CLKCTL_SCLKEN; + matsu_sd_writel(priv, tmp, MATSU_SD_CLKCTL); + + udelay(1000); +} + +int matsu_sd_set_ios(struct udevice *dev) +{ + struct matsu_sd_priv *priv = dev_get_priv(dev); + struct mmc *mmc = mmc_get_mmc_dev(dev); + int ret; + + dev_dbg(dev, "clock %uHz, DDRmode %d, width %u\n", + mmc->clock, mmc->ddr_mode, mmc->bus_width); + + ret = matsu_sd_set_bus_width(priv, mmc); + if (ret) + return ret; + matsu_sd_set_ddr_mode(priv, mmc); + matsu_sd_set_clk_rate(priv, mmc); + + return 0; +} + +int matsu_sd_get_cd(struct udevice *dev) +{ + struct matsu_sd_priv *priv = dev_get_priv(dev); + + if (priv->caps & MATSU_SD_CAP_NONREMOVABLE) + return 1; + + return !!(matsu_sd_readl(priv, MATSU_SD_INFO1) & + MATSU_SD_INFO1_CD); +} + +static void matsu_sd_host_init(struct matsu_sd_priv *priv) +{ + u32 tmp; + + /* soft reset of the host */ + tmp = matsu_sd_readl(priv, MATSU_SD_SOFT_RST); + tmp &= ~MATSU_SD_SOFT_RST_RSTX; + matsu_sd_writel(priv, tmp, MATSU_SD_SOFT_RST); + tmp |= MATSU_SD_SOFT_RST_RSTX; + matsu_sd_writel(priv, tmp, MATSU_SD_SOFT_RST); + + /* FIXME: implement eMMC hw_reset */ + + matsu_sd_writel(priv, MATSU_SD_STOP_SEC, MATSU_SD_STOP); + + /* + * Connected to 32bit AXI. + * This register dropped backward compatibility at version 0x10. + * Write an appropriate value depending on the IP version. + */ + matsu_sd_writel(priv, priv->version >= 0x10 ? 0x00000101 : 0x00000000, + MATSU_SD_HOST_MODE); + + if (priv->caps & MATSU_SD_CAP_DMA_INTERNAL) { + tmp = matsu_sd_readl(priv, MATSU_SD_DMA_MODE); + tmp |= MATSU_SD_DMA_MODE_ADDR_INC; + matsu_sd_writel(priv, tmp, MATSU_SD_DMA_MODE); + } +} + +int matsu_sd_bind(struct udevice *dev) +{ + struct matsu_sd_plat *plat = dev_get_platdata(dev); + + return mmc_bind(dev, &plat->mmc, &plat->cfg); +} + +int matsu_sd_probe(struct udevice *dev) +{ + struct matsu_sd_plat *plat = dev_get_platdata(dev); + struct matsu_sd_priv *priv = dev_get_priv(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + const u32 quirks = dev_get_driver_data(dev); + fdt_addr_t base; + struct clk clk; + int ret; +#ifdef CONFIG_DM_REGULATOR + struct udevice *vqmmc_dev; +#endif + + base = devfdt_get_addr(dev); + if (base == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regbase = devm_ioremap(dev, base, SZ_2K); + if (!priv->regbase) + return -ENOMEM; + +#ifdef CONFIG_DM_REGULATOR + ret = device_get_supply_regulator(dev, "vqmmc-supply", &vqmmc_dev); + if (!ret) { + /* Set the regulator to 3.3V until we support 1.8V modes */ + regulator_set_value(vqmmc_dev, 3300000); + regulator_set_enable(vqmmc_dev, true); + } +#endif + + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) { + dev_err(dev, "failed to get host clock\n"); + return ret; + } + + /* set to max rate */ + priv->mclk = clk_set_rate(&clk, ULONG_MAX); + if (IS_ERR_VALUE(priv->mclk)) { + dev_err(dev, "failed to set rate for host clock\n"); + clk_free(&clk); + return priv->mclk; + } + + ret = clk_enable(&clk); + clk_free(&clk); + if (ret) { + dev_err(dev, "failed to enable host clock\n"); + return ret; + } + + plat->cfg.name = dev->name; + plat->cfg.host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS; + + switch (fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "bus-width", + 1)) { + case 8: + plat->cfg.host_caps |= MMC_MODE_8BIT; + break; + case 4: + plat->cfg.host_caps |= MMC_MODE_4BIT; + break; + case 1: + break; + default: + dev_err(dev, "Invalid \"bus-width\" value\n"); + return -EINVAL; + } + + if (quirks) { + priv->caps = quirks; + } else { + priv->version = matsu_sd_readl(priv, MATSU_SD_VERSION) & + MATSU_SD_VERSION_IP; + dev_dbg(dev, "version %x\n", priv->version); + if (priv->version >= 0x10) { + priv->caps |= MATSU_SD_CAP_DMA_INTERNAL; + priv->caps |= MATSU_SD_CAP_DIV1024; + } + } + + if (fdt_get_property(gd->fdt_blob, dev_of_offset(dev), "non-removable", + NULL)) + priv->caps |= MATSU_SD_CAP_NONREMOVABLE; + + matsu_sd_host_init(priv); + + plat->cfg.voltages = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34; + plat->cfg.f_min = priv->mclk / + (priv->caps & MATSU_SD_CAP_DIV1024 ? 1024 : 512); + plat->cfg.f_max = priv->mclk; + plat->cfg.b_max = U32_MAX; /* max value of MATSU_SD_SECCNT */ + + upriv->mmc = &plat->mmc; + + return 0; +} diff --git a/drivers/mmc/matsushita-common.h b/drivers/mmc/matsushita-common.h new file mode 100644 index 0000000..e517a2d --- /dev/null +++ b/drivers/mmc/matsushita-common.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2016 Socionext Inc. + * Author: Masahiro Yamada + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __MATSUSHITA_COMMON_H__ +#define __MATSUSHITA_COMMON_H__ + +#define MATSU_SD_CMD 0x000 /* command */ +#define MATSU_SD_CMD_NOSTOP BIT(14) /* No automatic CMD12 issue */ +#define MATSU_SD_CMD_MULTI BIT(13) /* multiple block transfer */ +#define MATSU_SD_CMD_RD BIT(12) /* 1: read, 0: write */ +#define MATSU_SD_CMD_DATA BIT(11) /* data transfer */ +#define MATSU_SD_CMD_APP BIT(6) /* ACMD preceded by CMD55 */ +#define MATSU_SD_CMD_NORMAL (0 << 8)/* auto-detect of resp-type */ +#define MATSU_SD_CMD_RSP_NONE (3 << 8)/* response: none */ +#define MATSU_SD_CMD_RSP_R1 (4 << 8)/* response: R1, R5, R6, R7 */ +#define MATSU_SD_CMD_RSP_R1B (5 << 8)/* response: R1b, R5b */ +#define MATSU_SD_CMD_RSP_R2 (6 << 8)/* response: R2 */ +#define MATSU_SD_CMD_RSP_R3 (7 << 8)/* response: R3, R4 */ +#define MATSU_SD_ARG 0x008 /* command argument */ +#define MATSU_SD_STOP 0x010 /* stop action control */ +#define MATSU_SD_STOP_SEC BIT(8) /* use sector count */ +#define MATSU_SD_STOP_STP BIT(0) /* issue CMD12 */ +#define MATSU_SD_SECCNT 0x014 /* sector counter */ +#define MATSU_SD_RSP10 0x018 /* response[39:8] */ +#define MATSU_SD_RSP32 0x020 /* response[71:40] */ +#define MATSU_SD_RSP54 0x028 /* response[103:72] */ +#define MATSU_SD_RSP76 0x030 /* response[127:104] */ +#define MATSU_SD_INFO1 0x038 /* IRQ status 1 */ +#define MATSU_SD_INFO1_CD BIT(5) /* state of card detect */ +#define MATSU_SD_INFO1_INSERT BIT(4) /* card inserted */ +#define MATSU_SD_INFO1_REMOVE BIT(3) /* card removed */ +#define MATSU_SD_INFO1_CMP BIT(2) /* data complete */ +#define MATSU_SD_INFO1_RSP BIT(0) /* response complete */ +#define MATSU_SD_INFO2 0x03c /* IRQ status 2 */ +#define MATSU_SD_INFO2_ERR_ILA BIT(15) /* illegal access err */ +#define MATSU_SD_INFO2_CBSY BIT(14) /* command busy */ +#define MATSU_SD_INFO2_BWE BIT(9) /* write buffer ready */ +#define MATSU_SD_INFO2_BRE BIT(8) /* read buffer ready */ +#define MATSU_SD_INFO2_DAT0 BIT(7) /* SDDAT0 */ +#define MATSU_SD_INFO2_ERR_RTO BIT(6) /* response time out */ +#define MATSU_SD_INFO2_ERR_ILR BIT(5) /* illegal read err */ +#define MATSU_SD_INFO2_ERR_ILW BIT(4) /* illegal write err */ +#define MATSU_SD_INFO2_ERR_TO BIT(3) /* time out error */ +#define MATSU_SD_INFO2_ERR_END BIT(2) /* END bit error */ +#define MATSU_SD_INFO2_ERR_CRC BIT(1) /* CRC error */ +#define MATSU_SD_INFO2_ERR_IDX BIT(0) /* cmd index error */ +#define MATSU_SD_INFO1_MASK 0x040 +#define MATSU_SD_INFO2_MASK 0x044 +#define MATSU_SD_CLKCTL 0x048 /* clock divisor */ +#define MATSU_SD_CLKCTL_DIV_MASK 0x104ff +#define MATSU_SD_CLKCTL_DIV1024 BIT(16) /* SDCLK = CLK / 1024 */ +#define MATSU_SD_CLKCTL_DIV512 BIT(7) /* SDCLK = CLK / 512 */ +#define MATSU_SD_CLKCTL_DIV256 BIT(6) /* SDCLK = CLK / 256 */ +#define MATSU_SD_CLKCTL_DIV128 BIT(5) /* SDCLK = CLK / 128 */ +#define MATSU_SD_CLKCTL_DIV64 BIT(4) /* SDCLK = CLK / 64 */ +#define MATSU_SD_CLKCTL_DIV32 BIT(3) /* SDCLK = CLK / 32 */ +#define MATSU_SD_CLKCTL_DIV16 BIT(2) /* SDCLK = CLK / 16 */ +#define MATSU_SD_CLKCTL_DIV8 BIT(1) /* SDCLK = CLK / 8 */ +#define MATSU_SD_CLKCTL_DIV4 BIT(0) /* SDCLK = CLK / 4 */ +#define MATSU_SD_CLKCTL_DIV2 0 /* SDCLK = CLK / 2 */ +#define MATSU_SD_CLKCTL_DIV1 BIT(10) /* SDCLK = CLK */ +#define MATSU_SD_CLKCTL_OFFEN BIT(9) /* stop SDCLK when unused */ +#define MATSU_SD_CLKCTL_SCLKEN BIT(8) /* SDCLK output enable */ +#define MATSU_SD_SIZE 0x04c /* block size */ +#define MATSU_SD_OPTION 0x050 +#define MATSU_SD_OPTION_WIDTH_MASK (5 << 13) +#define MATSU_SD_OPTION_WIDTH_1 (4 << 13) +#define MATSU_SD_OPTION_WIDTH_4 (0 << 13) +#define MATSU_SD_OPTION_WIDTH_8 (1 << 13) +#define MATSU_SD_BUF 0x060 /* read/write buffer */ +#define MATSU_SD_EXTMODE 0x1b0 +#define MATSU_SD_EXTMODE_DMA_EN BIT(1) /* transfer 1: DMA, 0: pio */ +#define MATSU_SD_SOFT_RST 0x1c0 +#define MATSU_SD_SOFT_RST_RSTX BIT(0) /* reset deassert */ +#define MATSU_SD_VERSION 0x1c4 /* version register */ +#define MATSU_SD_VERSION_IP 0xff /* IP version */ +#define MATSU_SD_HOST_MODE 0x1c8 +#define MATSU_SD_IF_MODE 0x1cc +#define MATSU_SD_IF_MODE_DDR BIT(0) /* DDR mode */ +#define MATSU_SD_VOLT 0x1e4 /* voltage switch */ +#define MATSU_SD_VOLT_MASK (3 << 0) +#define MATSU_SD_VOLT_OFF (0 << 0) +#define MATSU_SD_VOLT_330 (1 << 0)/* 3.3V signal */ +#define MATSU_SD_VOLT_180 (2 << 0)/* 1.8V signal */ +#define MATSU_SD_DMA_MODE 0x410 +#define MATSU_SD_DMA_MODE_DIR_RD BIT(16) /* 1: from device, 0: to dev */ +#define MATSU_SD_DMA_MODE_ADDR_INC BIT(0) /* 1: address inc, 0: fixed */ +#define MATSU_SD_DMA_CTL 0x414 +#define MATSU_SD_DMA_CTL_START BIT(0) /* start DMA (auto cleared) */ +#define MATSU_SD_DMA_RST 0x418 +#define MATSU_SD_DMA_RST_RD BIT(9) +#define MATSU_SD_DMA_RST_WR BIT(8) +#define MATSU_SD_DMA_INFO1 0x420 +#define MATSU_SD_DMA_INFO1_END_RD2 BIT(20) /* DMA from device is complete*/ +#define MATSU_SD_DMA_INFO1_END_RD BIT(17) /* Don't use! Hardware bug */ +#define MATSU_SD_DMA_INFO1_END_WR BIT(16) /* DMA to device is complete */ +#define MATSU_SD_DMA_INFO1_MASK 0x424 +#define MATSU_SD_DMA_INFO2 0x428 +#define MATSU_SD_DMA_INFO2_ERR_RD BIT(17) +#define MATSU_SD_DMA_INFO2_ERR_WR BIT(16) +#define MATSU_SD_DMA_INFO2_MASK 0x42c +#define MATSU_SD_DMA_ADDR_L 0x440 +#define MATSU_SD_DMA_ADDR_H 0x444 + +/* alignment required by the DMA engine of this controller */ +#define MATSU_SD_DMA_MINALIGN 0x10 + +struct matsu_sd_plat { + struct mmc_config cfg; + struct mmc mmc; +}; + +struct matsu_sd_priv { + void __iomem *regbase; + unsigned long mclk; + unsigned int version; + u32 caps; +#define MATSU_SD_CAP_NONREMOVABLE BIT(0) /* Nonremovable e.g. eMMC */ +#define MATSU_SD_CAP_DMA_INTERNAL BIT(1) /* have internal DMA engine */ +#define MATSU_SD_CAP_DIV1024 BIT(2) /* divisor 1024 is available */ +#define MATSU_SD_CAP_64BIT BIT(3) /* Controller is 64bit */ +}; + +int matsu_sd_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, + struct mmc_data *data); +int matsu_sd_set_ios(struct udevice *dev); +int matsu_sd_get_cd(struct udevice *dev); + +int matsu_sd_bind(struct udevice *dev); +int matsu_sd_probe(struct udevice *dev); + +#endif /* __MATSUSHITA_COMMON_H__ */ diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c new file mode 100644 index 0000000..9b388b3 --- /dev/null +++ b/drivers/mmc/renesas-sdhi.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 Marek Vasut + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "matsushita-common.h" + +static const struct dm_mmc_ops renesas_sdhi_ops = { + .send_cmd = matsu_sd_send_cmd, + .set_ios = matsu_sd_set_ios, + .get_cd = matsu_sd_get_cd, +}; + +static const struct udevice_id renesas_sdhi_match[] = { + { .compatible = "renesas,sdhi-r8a7790", .data = 0 }, + { .compatible = "renesas,sdhi-r8a7791", .data = 0 }, + { .compatible = "renesas,sdhi-r8a7792", .data = 0 }, + { .compatible = "renesas,sdhi-r8a7793", .data = 0 }, + { .compatible = "renesas,sdhi-r8a7794", .data = 0 }, + { .compatible = "renesas,sdhi-r8a7795", .data = MATSU_SD_CAP_64BIT }, + { .compatible = "renesas,sdhi-r8a7796", .data = MATSU_SD_CAP_64BIT }, + { .compatible = "renesas,sdhi-r8a77965", .data = MATSU_SD_CAP_64BIT }, + { .compatible = "renesas,sdhi-r8a77970", .data = MATSU_SD_CAP_64BIT }, + { .compatible = "renesas,sdhi-r8a77995", .data = MATSU_SD_CAP_64BIT }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(renesas_sdhi) = { + .name = "renesas-sdhi", + .id = UCLASS_MMC, + .of_match = renesas_sdhi_match, + .bind = matsu_sd_bind, + .probe = matsu_sd_probe, + .priv_auto_alloc_size = sizeof(struct matsu_sd_priv), + .platdata_auto_alloc_size = sizeof(struct matsu_sd_plat), + .ops = &renesas_sdhi_ops, +}; diff --git a/drivers/mmc/uniphier-sd.c b/drivers/mmc/uniphier-sd.c index 525b170..72f0d46 100644 --- a/drivers/mmc/uniphier-sd.c +++ b/drivers/mmc/uniphier-sd.c @@ -17,846 +17,15 @@ #include #include -DECLARE_GLOBAL_DATA_PTR; - -#define UNIPHIER_SD_CMD 0x000 /* command */ -#define UNIPHIER_SD_CMD_NOSTOP BIT(14) /* No automatic CMD12 issue */ -#define UNIPHIER_SD_CMD_MULTI BIT(13) /* multiple block transfer */ -#define UNIPHIER_SD_CMD_RD BIT(12) /* 1: read, 0: write */ -#define UNIPHIER_SD_CMD_DATA BIT(11) /* data transfer */ -#define UNIPHIER_SD_CMD_APP BIT(6) /* ACMD preceded by CMD55 */ -#define UNIPHIER_SD_CMD_NORMAL (0 << 8)/* auto-detect of resp-type */ -#define UNIPHIER_SD_CMD_RSP_NONE (3 << 8)/* response: none */ -#define UNIPHIER_SD_CMD_RSP_R1 (4 << 8)/* response: R1, R5, R6, R7 */ -#define UNIPHIER_SD_CMD_RSP_R1B (5 << 8)/* response: R1b, R5b */ -#define UNIPHIER_SD_CMD_RSP_R2 (6 << 8)/* response: R2 */ -#define UNIPHIER_SD_CMD_RSP_R3 (7 << 8)/* response: R3, R4 */ -#define UNIPHIER_SD_ARG 0x008 /* command argument */ -#define UNIPHIER_SD_STOP 0x010 /* stop action control */ -#define UNIPHIER_SD_STOP_SEC BIT(8) /* use sector count */ -#define UNIPHIER_SD_STOP_STP BIT(0) /* issue CMD12 */ -#define UNIPHIER_SD_SECCNT 0x014 /* sector counter */ -#define UNIPHIER_SD_RSP10 0x018 /* response[39:8] */ -#define UNIPHIER_SD_RSP32 0x020 /* response[71:40] */ -#define UNIPHIER_SD_RSP54 0x028 /* response[103:72] */ -#define UNIPHIER_SD_RSP76 0x030 /* response[127:104] */ -#define UNIPHIER_SD_INFO1 0x038 /* IRQ status 1 */ -#define UNIPHIER_SD_INFO1_CD BIT(5) /* state of card detect */ -#define UNIPHIER_SD_INFO1_INSERT BIT(4) /* card inserted */ -#define UNIPHIER_SD_INFO1_REMOVE BIT(3) /* card removed */ -#define UNIPHIER_SD_INFO1_CMP BIT(2) /* data complete */ -#define UNIPHIER_SD_INFO1_RSP BIT(0) /* response complete */ -#define UNIPHIER_SD_INFO2 0x03c /* IRQ status 2 */ -#define UNIPHIER_SD_INFO2_ERR_ILA BIT(15) /* illegal access err */ -#define UNIPHIER_SD_INFO2_CBSY BIT(14) /* command busy */ -#define UNIPHIER_SD_INFO2_BWE BIT(9) /* write buffer ready */ -#define UNIPHIER_SD_INFO2_BRE BIT(8) /* read buffer ready */ -#define UNIPHIER_SD_INFO2_DAT0 BIT(7) /* SDDAT0 */ -#define UNIPHIER_SD_INFO2_ERR_RTO BIT(6) /* response time out */ -#define UNIPHIER_SD_INFO2_ERR_ILR BIT(5) /* illegal read err */ -#define UNIPHIER_SD_INFO2_ERR_ILW BIT(4) /* illegal write err */ -#define UNIPHIER_SD_INFO2_ERR_TO BIT(3) /* time out error */ -#define UNIPHIER_SD_INFO2_ERR_END BIT(2) /* END bit error */ -#define UNIPHIER_SD_INFO2_ERR_CRC BIT(1) /* CRC error */ -#define UNIPHIER_SD_INFO2_ERR_IDX BIT(0) /* cmd index error */ -#define UNIPHIER_SD_INFO1_MASK 0x040 -#define UNIPHIER_SD_INFO2_MASK 0x044 -#define UNIPHIER_SD_CLKCTL 0x048 /* clock divisor */ -#define UNIPHIER_SD_CLKCTL_DIV_MASK 0x104ff -#define UNIPHIER_SD_CLKCTL_DIV1024 BIT(16) /* SDCLK = CLK / 1024 */ -#define UNIPHIER_SD_CLKCTL_DIV512 BIT(7) /* SDCLK = CLK / 512 */ -#define UNIPHIER_SD_CLKCTL_DIV256 BIT(6) /* SDCLK = CLK / 256 */ -#define UNIPHIER_SD_CLKCTL_DIV128 BIT(5) /* SDCLK = CLK / 128 */ -#define UNIPHIER_SD_CLKCTL_DIV64 BIT(4) /* SDCLK = CLK / 64 */ -#define UNIPHIER_SD_CLKCTL_DIV32 BIT(3) /* SDCLK = CLK / 32 */ -#define UNIPHIER_SD_CLKCTL_DIV16 BIT(2) /* SDCLK = CLK / 16 */ -#define UNIPHIER_SD_CLKCTL_DIV8 BIT(1) /* SDCLK = CLK / 8 */ -#define UNIPHIER_SD_CLKCTL_DIV4 BIT(0) /* SDCLK = CLK / 4 */ -#define UNIPHIER_SD_CLKCTL_DIV2 0 /* SDCLK = CLK / 2 */ -#define UNIPHIER_SD_CLKCTL_DIV1 BIT(10) /* SDCLK = CLK */ -#define UNIPHIER_SD_CLKCTL_OFFEN BIT(9) /* stop SDCLK when unused */ -#define UNIPHIER_SD_CLKCTL_SCLKEN BIT(8) /* SDCLK output enable */ -#define UNIPHIER_SD_SIZE 0x04c /* block size */ -#define UNIPHIER_SD_OPTION 0x050 -#define UNIPHIER_SD_OPTION_WIDTH_MASK (5 << 13) -#define UNIPHIER_SD_OPTION_WIDTH_1 (4 << 13) -#define UNIPHIER_SD_OPTION_WIDTH_4 (0 << 13) -#define UNIPHIER_SD_OPTION_WIDTH_8 (1 << 13) -#define UNIPHIER_SD_BUF 0x060 /* read/write buffer */ -#define UNIPHIER_SD_EXTMODE 0x1b0 -#define UNIPHIER_SD_EXTMODE_DMA_EN BIT(1) /* transfer 1: DMA, 0: pio */ -#define UNIPHIER_SD_SOFT_RST 0x1c0 -#define UNIPHIER_SD_SOFT_RST_RSTX BIT(0) /* reset deassert */ -#define UNIPHIER_SD_VERSION 0x1c4 /* version register */ -#define UNIPHIER_SD_VERSION_IP 0xff /* IP version */ -#define UNIPHIER_SD_HOST_MODE 0x1c8 -#define UNIPHIER_SD_IF_MODE 0x1cc -#define UNIPHIER_SD_IF_MODE_DDR BIT(0) /* DDR mode */ -#define UNIPHIER_SD_VOLT 0x1e4 /* voltage switch */ -#define UNIPHIER_SD_VOLT_MASK (3 << 0) -#define UNIPHIER_SD_VOLT_OFF (0 << 0) -#define UNIPHIER_SD_VOLT_330 (1 << 0)/* 3.3V signal */ -#define UNIPHIER_SD_VOLT_180 (2 << 0)/* 1.8V signal */ -#define UNIPHIER_SD_DMA_MODE 0x410 -#define UNIPHIER_SD_DMA_MODE_DIR_RD BIT(16) /* 1: from device, 0: to dev */ -#define UNIPHIER_SD_DMA_MODE_ADDR_INC BIT(0) /* 1: address inc, 0: fixed */ -#define UNIPHIER_SD_DMA_CTL 0x414 -#define UNIPHIER_SD_DMA_CTL_START BIT(0) /* start DMA (auto cleared) */ -#define UNIPHIER_SD_DMA_RST 0x418 -#define UNIPHIER_SD_DMA_RST_RD BIT(9) -#define UNIPHIER_SD_DMA_RST_WR BIT(8) -#define UNIPHIER_SD_DMA_INFO1 0x420 -#define UNIPHIER_SD_DMA_INFO1_END_RD2 BIT(20) /* DMA from device is complete*/ -#define UNIPHIER_SD_DMA_INFO1_END_RD BIT(17) /* Don't use! Hardware bug */ -#define UNIPHIER_SD_DMA_INFO1_END_WR BIT(16) /* DMA to device is complete */ -#define UNIPHIER_SD_DMA_INFO1_MASK 0x424 -#define UNIPHIER_SD_DMA_INFO2 0x428 -#define UNIPHIER_SD_DMA_INFO2_ERR_RD BIT(17) -#define UNIPHIER_SD_DMA_INFO2_ERR_WR BIT(16) -#define UNIPHIER_SD_DMA_INFO2_MASK 0x42c -#define UNIPHIER_SD_DMA_ADDR_L 0x440 -#define UNIPHIER_SD_DMA_ADDR_H 0x444 - -/* alignment required by the DMA engine of this controller */ -#define UNIPHIER_SD_DMA_MINALIGN 0x10 - -struct uniphier_sd_plat { - struct mmc_config cfg; - struct mmc mmc; -}; - -struct uniphier_sd_priv { - void __iomem *regbase; - unsigned long mclk; - unsigned int version; - u32 caps; -#define UNIPHIER_SD_CAP_NONREMOVABLE BIT(0) /* Nonremovable e.g. eMMC */ -#define UNIPHIER_SD_CAP_DMA_INTERNAL BIT(1) /* have internal DMA engine */ -#define UNIPHIER_SD_CAP_DIV1024 BIT(2) /* divisor 1024 is available */ -#define UNIPHIER_SD_CAP_64BIT BIT(3) /* Controller is 64bit */ -}; - -static u64 uniphier_sd_readq(struct uniphier_sd_priv *priv, unsigned int reg) -{ - if (priv->caps & UNIPHIER_SD_CAP_64BIT) - return readq(priv->regbase + (reg << 1)); - else - return readq(priv->regbase + reg); -} - -static void uniphier_sd_writeq(struct uniphier_sd_priv *priv, - u64 val, unsigned int reg) -{ - if (priv->caps & UNIPHIER_SD_CAP_64BIT) - writeq(val, priv->regbase + (reg << 1)); - else - writeq(val, priv->regbase + reg); -} - -static u32 uniphier_sd_readl(struct uniphier_sd_priv *priv, unsigned int reg) -{ - if (priv->caps & UNIPHIER_SD_CAP_64BIT) - return readl(priv->regbase + (reg << 1)); - else - return readl(priv->regbase + reg); -} - -static void uniphier_sd_writel(struct uniphier_sd_priv *priv, - u32 val, unsigned int reg) -{ - if (priv->caps & UNIPHIER_SD_CAP_64BIT) - writel(val, priv->regbase + (reg << 1)); - else - writel(val, priv->regbase + reg); -} - -static dma_addr_t __dma_map_single(void *ptr, size_t size, - enum dma_data_direction dir) -{ - unsigned long addr = (unsigned long)ptr; - - if (dir == DMA_FROM_DEVICE) - invalidate_dcache_range(addr, addr + size); - else - flush_dcache_range(addr, addr + size); - - return addr; -} - -static void __dma_unmap_single(dma_addr_t addr, size_t size, - enum dma_data_direction dir) -{ - if (dir != DMA_TO_DEVICE) - invalidate_dcache_range(addr, addr + size); -} - -static int uniphier_sd_check_error(struct udevice *dev) -{ - struct uniphier_sd_priv *priv = dev_get_priv(dev); - u32 info2 = uniphier_sd_readl(priv, UNIPHIER_SD_INFO2); - - if (info2 & UNIPHIER_SD_INFO2_ERR_RTO) { - /* - * TIMEOUT must be returned for unsupported command. Do not - * display error log since this might be a part of sequence to - * distinguish between SD and MMC. - */ - return -ETIMEDOUT; - } - - if (info2 & UNIPHIER_SD_INFO2_ERR_TO) { - dev_err(dev, "timeout error\n"); - return -ETIMEDOUT; - } - - if (info2 & (UNIPHIER_SD_INFO2_ERR_END | UNIPHIER_SD_INFO2_ERR_CRC | - UNIPHIER_SD_INFO2_ERR_IDX)) { - dev_err(dev, "communication out of sync\n"); - return -EILSEQ; - } - - if (info2 & (UNIPHIER_SD_INFO2_ERR_ILA | UNIPHIER_SD_INFO2_ERR_ILR | - UNIPHIER_SD_INFO2_ERR_ILW)) { - dev_err(dev, "illegal access\n"); - return -EIO; - } - - return 0; -} - -static int uniphier_sd_wait_for_irq(struct udevice *dev, unsigned int reg, - u32 flag) -{ - struct uniphier_sd_priv *priv = dev_get_priv(dev); - long wait = 1000000; - int ret; - - while (!(uniphier_sd_readl(priv, reg) & flag)) { - if (wait-- < 0) { - dev_err(dev, "timeout\n"); - return -ETIMEDOUT; - } - - ret = uniphier_sd_check_error(dev); - if (ret) - return ret; - - udelay(1); - } - - return 0; -} - -static int uniphier_sd_pio_read_one_block(struct udevice *dev, char *pbuf, - uint blocksize) -{ - struct uniphier_sd_priv *priv = dev_get_priv(dev); - int i, ret; - - /* wait until the buffer is filled with data */ - ret = uniphier_sd_wait_for_irq(dev, UNIPHIER_SD_INFO2, - UNIPHIER_SD_INFO2_BRE); - if (ret) - return ret; - - /* - * Clear the status flag _before_ read the buffer out because - * UNIPHIER_SD_INFO2_BRE is edge-triggered, not level-triggered. - */ - uniphier_sd_writel(priv, 0, UNIPHIER_SD_INFO2); - - if (priv->caps & UNIPHIER_SD_CAP_64BIT) { - u64 *buf = (u64 *)pbuf; - if (likely(IS_ALIGNED((uintptr_t)buf, 8))) { - for (i = 0; i < blocksize / 8; i++) { - *buf++ = uniphier_sd_readq(priv, - UNIPHIER_SD_BUF); - } - } else { - for (i = 0; i < blocksize / 8; i++) { - u64 data; - data = uniphier_sd_readq(priv, - UNIPHIER_SD_BUF); - put_unaligned(data, buf++); - } - } - } else { - u32 *buf = (u32 *)pbuf; - if (likely(IS_ALIGNED((uintptr_t)buf, 4))) { - for (i = 0; i < blocksize / 4; i++) { - *buf++ = uniphier_sd_readl(priv, - UNIPHIER_SD_BUF); - } - } else { - for (i = 0; i < blocksize / 4; i++) { - u32 data; - data = uniphier_sd_readl(priv, UNIPHIER_SD_BUF); - put_unaligned(data, buf++); - } - } - } - - return 0; -} - -static int uniphier_sd_pio_write_one_block(struct udevice *dev, - const char *pbuf, uint blocksize) -{ - struct uniphier_sd_priv *priv = dev_get_priv(dev); - int i, ret; - - /* wait until the buffer becomes empty */ - ret = uniphier_sd_wait_for_irq(dev, UNIPHIER_SD_INFO2, - UNIPHIER_SD_INFO2_BWE); - if (ret) - return ret; - - uniphier_sd_writel(priv, 0, UNIPHIER_SD_INFO2); - - if (priv->caps & UNIPHIER_SD_CAP_64BIT) { - const u64 *buf = (const u64 *)pbuf; - if (likely(IS_ALIGNED((uintptr_t)buf, 8))) { - for (i = 0; i < blocksize / 8; i++) { - uniphier_sd_writeq(priv, *buf++, - UNIPHIER_SD_BUF); - } - } else { - for (i = 0; i < blocksize / 8; i++) { - u64 data = get_unaligned(buf++); - uniphier_sd_writeq(priv, data, - UNIPHIER_SD_BUF); - } - } - } else { - const u32 *buf = (const u32 *)pbuf; - if (likely(IS_ALIGNED((uintptr_t)buf, 4))) { - for (i = 0; i < blocksize / 4; i++) { - uniphier_sd_writel(priv, *buf++, - UNIPHIER_SD_BUF); - } - } else { - for (i = 0; i < blocksize / 4; i++) { - u32 data = get_unaligned(buf++); - uniphier_sd_writel(priv, data, - UNIPHIER_SD_BUF); - } - } - } - - return 0; -} - -static int uniphier_sd_pio_xfer(struct udevice *dev, struct mmc_data *data) -{ - const char *src = data->src; - char *dest = data->dest; - int i, ret; - - for (i = 0; i < data->blocks; i++) { - if (data->flags & MMC_DATA_READ) - ret = uniphier_sd_pio_read_one_block(dev, dest, - data->blocksize); - else - ret = uniphier_sd_pio_write_one_block(dev, src, - data->blocksize); - if (ret) - return ret; - - if (data->flags & MMC_DATA_READ) - dest += data->blocksize; - else - src += data->blocksize; - } - - return 0; -} - -static void uniphier_sd_dma_start(struct uniphier_sd_priv *priv, - dma_addr_t dma_addr) -{ - u32 tmp; - - uniphier_sd_writel(priv, 0, UNIPHIER_SD_DMA_INFO1); - uniphier_sd_writel(priv, 0, UNIPHIER_SD_DMA_INFO2); - - /* enable DMA */ - tmp = uniphier_sd_readl(priv, UNIPHIER_SD_EXTMODE); - tmp |= UNIPHIER_SD_EXTMODE_DMA_EN; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_EXTMODE); - - uniphier_sd_writel(priv, dma_addr & U32_MAX, UNIPHIER_SD_DMA_ADDR_L); - - /* suppress the warning "right shift count >= width of type" */ - dma_addr >>= min_t(int, 32, 8 * sizeof(dma_addr)); - - uniphier_sd_writel(priv, dma_addr & U32_MAX, UNIPHIER_SD_DMA_ADDR_H); - - uniphier_sd_writel(priv, UNIPHIER_SD_DMA_CTL_START, UNIPHIER_SD_DMA_CTL); -} - -static int uniphier_sd_dma_wait_for_irq(struct udevice *dev, u32 flag, - unsigned int blocks) -{ - struct uniphier_sd_priv *priv = dev_get_priv(dev); - long wait = 1000000 + 10 * blocks; - - while (!(uniphier_sd_readl(priv, UNIPHIER_SD_DMA_INFO1) & flag)) { - if (wait-- < 0) { - dev_err(dev, "timeout during DMA\n"); - return -ETIMEDOUT; - } - - udelay(10); - } - - if (uniphier_sd_readl(priv, UNIPHIER_SD_DMA_INFO2)) { - dev_err(dev, "error during DMA\n"); - return -EIO; - } - - return 0; -} - -static int uniphier_sd_dma_xfer(struct udevice *dev, struct mmc_data *data) -{ - struct uniphier_sd_priv *priv = dev_get_priv(dev); - size_t len = data->blocks * data->blocksize; - void *buf; - enum dma_data_direction dir; - dma_addr_t dma_addr; - u32 poll_flag, tmp; - int ret; - - tmp = uniphier_sd_readl(priv, UNIPHIER_SD_DMA_MODE); - - if (data->flags & MMC_DATA_READ) { - buf = data->dest; - dir = DMA_FROM_DEVICE; - poll_flag = UNIPHIER_SD_DMA_INFO1_END_RD2; - tmp |= UNIPHIER_SD_DMA_MODE_DIR_RD; - } else { - buf = (void *)data->src; - dir = DMA_TO_DEVICE; - poll_flag = UNIPHIER_SD_DMA_INFO1_END_WR; - tmp &= ~UNIPHIER_SD_DMA_MODE_DIR_RD; - } - - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_DMA_MODE); - - dma_addr = __dma_map_single(buf, len, dir); - - uniphier_sd_dma_start(priv, dma_addr); - - ret = uniphier_sd_dma_wait_for_irq(dev, poll_flag, data->blocks); - - __dma_unmap_single(dma_addr, len, dir); - - return ret; -} - -/* check if the address is DMA'able */ -static bool uniphier_sd_addr_is_dmaable(unsigned long addr) -{ - if (!IS_ALIGNED(addr, UNIPHIER_SD_DMA_MINALIGN)) - return false; - -#if defined(CONFIG_ARCH_UNIPHIER) && !defined(CONFIG_ARM64) && \ - defined(CONFIG_SPL_BUILD) - /* - * For UniPhier ARMv7 SoCs, the stack is allocated in the locked ways - * of L2, which is unreachable from the DMA engine. - */ - if (addr < CONFIG_SPL_STACK) - return false; -#endif - - return true; -} - -static int uniphier_sd_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, - struct mmc_data *data) -{ - struct uniphier_sd_priv *priv = dev_get_priv(dev); - int ret; - u32 tmp; - - if (uniphier_sd_readl(priv, UNIPHIER_SD_INFO2) & UNIPHIER_SD_INFO2_CBSY) { - dev_err(dev, "command busy\n"); - return -EBUSY; - } - - /* clear all status flags */ - uniphier_sd_writel(priv, 0, UNIPHIER_SD_INFO1); - uniphier_sd_writel(priv, 0, UNIPHIER_SD_INFO2); - - /* disable DMA once */ - tmp = uniphier_sd_readl(priv, UNIPHIER_SD_EXTMODE); - tmp &= ~UNIPHIER_SD_EXTMODE_DMA_EN; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_EXTMODE); - - uniphier_sd_writel(priv, cmd->cmdarg, UNIPHIER_SD_ARG); - - tmp = cmd->cmdidx; - - if (data) { - uniphier_sd_writel(priv, data->blocksize, UNIPHIER_SD_SIZE); - uniphier_sd_writel(priv, data->blocks, UNIPHIER_SD_SECCNT); - - /* Do not send CMD12 automatically */ - tmp |= UNIPHIER_SD_CMD_NOSTOP | UNIPHIER_SD_CMD_DATA; - - if (data->blocks > 1) - tmp |= UNIPHIER_SD_CMD_MULTI; - - if (data->flags & MMC_DATA_READ) - tmp |= UNIPHIER_SD_CMD_RD; - } - - /* - * Do not use the response type auto-detection on this hardware. - * CMD8, for example, has different response types on SD and eMMC, - * while this controller always assumes the response type for SD. - * Set the response type manually. - */ - switch (cmd->resp_type) { - case MMC_RSP_NONE: - tmp |= UNIPHIER_SD_CMD_RSP_NONE; - break; - case MMC_RSP_R1: - tmp |= UNIPHIER_SD_CMD_RSP_R1; - break; - case MMC_RSP_R1b: - tmp |= UNIPHIER_SD_CMD_RSP_R1B; - break; - case MMC_RSP_R2: - tmp |= UNIPHIER_SD_CMD_RSP_R2; - break; - case MMC_RSP_R3: - tmp |= UNIPHIER_SD_CMD_RSP_R3; - break; - default: - dev_err(dev, "unknown response type\n"); - return -EINVAL; - } - - dev_dbg(dev, "sending CMD%d (SD_CMD=%08x, SD_ARG=%08x)\n", - cmd->cmdidx, tmp, cmd->cmdarg); - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_CMD); - - ret = uniphier_sd_wait_for_irq(dev, UNIPHIER_SD_INFO1, - UNIPHIER_SD_INFO1_RSP); - if (ret) - return ret; - - if (cmd->resp_type & MMC_RSP_136) { - u32 rsp_127_104 = uniphier_sd_readl(priv, UNIPHIER_SD_RSP76); - u32 rsp_103_72 = uniphier_sd_readl(priv, UNIPHIER_SD_RSP54); - u32 rsp_71_40 = uniphier_sd_readl(priv, UNIPHIER_SD_RSP32); - u32 rsp_39_8 = uniphier_sd_readl(priv, UNIPHIER_SD_RSP10); - - cmd->response[0] = ((rsp_127_104 & 0x00ffffff) << 8) | - ((rsp_103_72 & 0xff000000) >> 24); - cmd->response[1] = ((rsp_103_72 & 0x00ffffff) << 8) | - ((rsp_71_40 & 0xff000000) >> 24); - cmd->response[2] = ((rsp_71_40 & 0x00ffffff) << 8) | - ((rsp_39_8 & 0xff000000) >> 24); - cmd->response[3] = (rsp_39_8 & 0xffffff) << 8; - } else { - /* bit 39-8 */ - cmd->response[0] = uniphier_sd_readl(priv, UNIPHIER_SD_RSP10); - } - - if (data) { - /* use DMA if the HW supports it and the buffer is aligned */ - if (priv->caps & UNIPHIER_SD_CAP_DMA_INTERNAL && - uniphier_sd_addr_is_dmaable((long)data->src)) - ret = uniphier_sd_dma_xfer(dev, data); - else - ret = uniphier_sd_pio_xfer(dev, data); - - ret = uniphier_sd_wait_for_irq(dev, UNIPHIER_SD_INFO1, - UNIPHIER_SD_INFO1_CMP); - if (ret) - return ret; - } - - return ret; -} - -static int uniphier_sd_set_bus_width(struct uniphier_sd_priv *priv, - struct mmc *mmc) -{ - u32 val, tmp; - - switch (mmc->bus_width) { - case 1: - val = UNIPHIER_SD_OPTION_WIDTH_1; - break; - case 4: - val = UNIPHIER_SD_OPTION_WIDTH_4; - break; - case 8: - val = UNIPHIER_SD_OPTION_WIDTH_8; - break; - default: - return -EINVAL; - } - - tmp = uniphier_sd_readl(priv, UNIPHIER_SD_OPTION); - tmp &= ~UNIPHIER_SD_OPTION_WIDTH_MASK; - tmp |= val; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_OPTION); - - return 0; -} - -static void uniphier_sd_set_ddr_mode(struct uniphier_sd_priv *priv, - struct mmc *mmc) -{ - u32 tmp; - - tmp = uniphier_sd_readl(priv, UNIPHIER_SD_IF_MODE); - if (mmc->ddr_mode) - tmp |= UNIPHIER_SD_IF_MODE_DDR; - else - tmp &= ~UNIPHIER_SD_IF_MODE_DDR; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_IF_MODE); -} - -static void uniphier_sd_set_clk_rate(struct uniphier_sd_priv *priv, - struct mmc *mmc) -{ - unsigned int divisor; - u32 val, tmp; - - if (!mmc->clock) - return; - - divisor = DIV_ROUND_UP(priv->mclk, mmc->clock); - - if (divisor <= 1) - val = UNIPHIER_SD_CLKCTL_DIV1; - else if (divisor <= 2) - val = UNIPHIER_SD_CLKCTL_DIV2; - else if (divisor <= 4) - val = UNIPHIER_SD_CLKCTL_DIV4; - else if (divisor <= 8) - val = UNIPHIER_SD_CLKCTL_DIV8; - else if (divisor <= 16) - val = UNIPHIER_SD_CLKCTL_DIV16; - else if (divisor <= 32) - val = UNIPHIER_SD_CLKCTL_DIV32; - else if (divisor <= 64) - val = UNIPHIER_SD_CLKCTL_DIV64; - else if (divisor <= 128) - val = UNIPHIER_SD_CLKCTL_DIV128; - else if (divisor <= 256) - val = UNIPHIER_SD_CLKCTL_DIV256; - else if (divisor <= 512 || !(priv->caps & UNIPHIER_SD_CAP_DIV1024)) - val = UNIPHIER_SD_CLKCTL_DIV512; - else - val = UNIPHIER_SD_CLKCTL_DIV1024; - - tmp = uniphier_sd_readl(priv, UNIPHIER_SD_CLKCTL); - if (tmp & UNIPHIER_SD_CLKCTL_SCLKEN && - (tmp & UNIPHIER_SD_CLKCTL_DIV_MASK) == val) - return; - - /* stop the clock before changing its rate to avoid a glitch signal */ - tmp &= ~UNIPHIER_SD_CLKCTL_SCLKEN; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_CLKCTL); - - tmp &= ~UNIPHIER_SD_CLKCTL_DIV_MASK; - tmp |= val | UNIPHIER_SD_CLKCTL_OFFEN; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_CLKCTL); - - tmp |= UNIPHIER_SD_CLKCTL_SCLKEN; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_CLKCTL); - - udelay(1000); -} - -static int uniphier_sd_set_ios(struct udevice *dev) -{ - struct uniphier_sd_priv *priv = dev_get_priv(dev); - struct mmc *mmc = mmc_get_mmc_dev(dev); - int ret; - - dev_dbg(dev, "clock %uHz, DDRmode %d, width %u\n", - mmc->clock, mmc->ddr_mode, mmc->bus_width); - - ret = uniphier_sd_set_bus_width(priv, mmc); - if (ret) - return ret; - uniphier_sd_set_ddr_mode(priv, mmc); - uniphier_sd_set_clk_rate(priv, mmc); - - return 0; -} - -static int uniphier_sd_get_cd(struct udevice *dev) -{ - struct uniphier_sd_priv *priv = dev_get_priv(dev); - - if (priv->caps & UNIPHIER_SD_CAP_NONREMOVABLE) - return 1; - - return !!(uniphier_sd_readl(priv, UNIPHIER_SD_INFO1) & - UNIPHIER_SD_INFO1_CD); -} +#include "matsushita-common.h" static const struct dm_mmc_ops uniphier_sd_ops = { - .send_cmd = uniphier_sd_send_cmd, - .set_ios = uniphier_sd_set_ios, - .get_cd = uniphier_sd_get_cd, + .send_cmd = matsu_sd_send_cmd, + .set_ios = matsu_sd_set_ios, + .get_cd = matsu_sd_get_cd, }; -static void uniphier_sd_host_init(struct uniphier_sd_priv *priv) -{ - u32 tmp; - - /* soft reset of the host */ - tmp = uniphier_sd_readl(priv, UNIPHIER_SD_SOFT_RST); - tmp &= ~UNIPHIER_SD_SOFT_RST_RSTX; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_SOFT_RST); - tmp |= UNIPHIER_SD_SOFT_RST_RSTX; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_SOFT_RST); - - /* FIXME: implement eMMC hw_reset */ - - uniphier_sd_writel(priv, UNIPHIER_SD_STOP_SEC, UNIPHIER_SD_STOP); - - /* - * Connected to 32bit AXI. - * This register dropped backward compatibility at version 0x10. - * Write an appropriate value depending on the IP version. - */ - uniphier_sd_writel(priv, priv->version >= 0x10 ? 0x00000101 : 0x00000000, - UNIPHIER_SD_HOST_MODE); - - if (priv->caps & UNIPHIER_SD_CAP_DMA_INTERNAL) { - tmp = uniphier_sd_readl(priv, UNIPHIER_SD_DMA_MODE); - tmp |= UNIPHIER_SD_DMA_MODE_ADDR_INC; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_DMA_MODE); - } -} - -static int uniphier_sd_bind(struct udevice *dev) -{ - struct uniphier_sd_plat *plat = dev_get_platdata(dev); - - return mmc_bind(dev, &plat->mmc, &plat->cfg); -} - -static int uniphier_sd_probe(struct udevice *dev) -{ - struct uniphier_sd_plat *plat = dev_get_platdata(dev); - struct uniphier_sd_priv *priv = dev_get_priv(dev); - struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); - const u32 quirks = dev_get_driver_data(dev); - fdt_addr_t base; - struct clk clk; - int ret; -#ifdef CONFIG_DM_REGULATOR - struct udevice *vqmmc_dev; -#endif - - base = devfdt_get_addr(dev); - if (base == FDT_ADDR_T_NONE) - return -EINVAL; - - priv->regbase = devm_ioremap(dev, base, SZ_2K); - if (!priv->regbase) - return -ENOMEM; - -#ifdef CONFIG_DM_REGULATOR - ret = device_get_supply_regulator(dev, "vqmmc-supply", &vqmmc_dev); - if (!ret) { - /* Set the regulator to 3.3V until we support 1.8V modes */ - regulator_set_value(vqmmc_dev, 3300000); - regulator_set_enable(vqmmc_dev, true); - } -#endif - - ret = clk_get_by_index(dev, 0, &clk); - if (ret < 0) { - dev_err(dev, "failed to get host clock\n"); - return ret; - } - - /* set to max rate */ - priv->mclk = clk_set_rate(&clk, ULONG_MAX); - if (IS_ERR_VALUE(priv->mclk)) { - dev_err(dev, "failed to set rate for host clock\n"); - clk_free(&clk); - return priv->mclk; - } - - ret = clk_enable(&clk); - clk_free(&clk); - if (ret) { - dev_err(dev, "failed to enable host clock\n"); - return ret; - } - - plat->cfg.name = dev->name; - plat->cfg.host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS; - - switch (fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "bus-width", - 1)) { - case 8: - plat->cfg.host_caps |= MMC_MODE_8BIT; - break; - case 4: - plat->cfg.host_caps |= MMC_MODE_4BIT; - break; - case 1: - break; - default: - dev_err(dev, "Invalid \"bus-width\" value\n"); - return -EINVAL; - } - - if (quirks) { - priv->caps = quirks; - } else { - priv->version = uniphier_sd_readl(priv, UNIPHIER_SD_VERSION) & - UNIPHIER_SD_VERSION_IP; - dev_dbg(dev, "version %x\n", priv->version); - if (priv->version >= 0x10) { - priv->caps |= UNIPHIER_SD_CAP_DMA_INTERNAL; - priv->caps |= UNIPHIER_SD_CAP_DIV1024; - } - } - - if (fdt_get_property(gd->fdt_blob, dev_of_offset(dev), "non-removable", - NULL)) - priv->caps |= UNIPHIER_SD_CAP_NONREMOVABLE; - - uniphier_sd_host_init(priv); - - plat->cfg.voltages = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34; - plat->cfg.f_min = priv->mclk / - (priv->caps & UNIPHIER_SD_CAP_DIV1024 ? 1024 : 512); - plat->cfg.f_max = priv->mclk; - plat->cfg.b_max = U32_MAX; /* max value of UNIPHIER_SD_SECCNT */ - - upriv->mmc = &plat->mmc; - - return 0; -} - static const struct udevice_id uniphier_sd_match[] = { - { .compatible = "renesas,sdhi-r8a7790", .data = 0 }, - { .compatible = "renesas,sdhi-r8a7791", .data = 0 }, - { .compatible = "renesas,sdhi-r8a7792", .data = 0 }, - { .compatible = "renesas,sdhi-r8a7793", .data = 0 }, - { .compatible = "renesas,sdhi-r8a7794", .data = 0 }, - { .compatible = "renesas,sdhi-r8a7795", .data = UNIPHIER_SD_CAP_64BIT }, - { .compatible = "renesas,sdhi-r8a7796", .data = UNIPHIER_SD_CAP_64BIT }, - { .compatible = "renesas,sdhi-r8a77965", .data = UNIPHIER_SD_CAP_64BIT }, - { .compatible = "renesas,sdhi-r8a77970", .data = UNIPHIER_SD_CAP_64BIT }, - { .compatible = "renesas,sdhi-r8a77995", .data = UNIPHIER_SD_CAP_64BIT }, { .compatible = "socionext,uniphier-sdhc", .data = 0 }, { /* sentinel */ } }; @@ -865,9 +34,9 @@ U_BOOT_DRIVER(uniphier_mmc) = { .name = "uniphier-mmc", .id = UCLASS_MMC, .of_match = uniphier_sd_match, - .bind = uniphier_sd_bind, - .probe = uniphier_sd_probe, - .priv_auto_alloc_size = sizeof(struct uniphier_sd_priv), - .platdata_auto_alloc_size = sizeof(struct uniphier_sd_plat), + .bind = matsu_sd_bind, + .probe = matsu_sd_probe, + .priv_auto_alloc_size = sizeof(struct matsu_sd_priv), + .platdata_auto_alloc_size = sizeof(struct matsu_sd_plat), .ops = &uniphier_sd_ops, }; From 7dfddc099d15309f06d2cf8495d9c6498177a7db Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 23 Sep 2017 13:30:30 +0200 Subject: [PATCH 07/36] mmc: renesas-sdhi: Add Renesas SDHI Kconfig entry Add Kconfig entry for the Renesas SDHI variant of the controller and split the Makefile entries accordingly. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/Kconfig | 15 ++++++++++++--- drivers/mmc/Makefile | 3 ++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index ff4a8f5..6935da2 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -267,13 +267,22 @@ config SH_SDHI Support for the on-chip SDHI host controller on SuperH/Renesas ARM SoCs platform config MMC_UNIPHIER - bool "UniPhier/RCar SD/MMC Host Controller support" - depends on ARCH_UNIPHIER || ARCH_RMOBILE + bool "UniPhier SD/MMC Host Controller support" + depends on ARCH_UNIPHIER depends on BLK && DM_MMC depends on OF_CONTROL help This selects support for the Matsushita SD/MMC Host Controller on - SocioNext UniPhier and Renesas RCar SoCs. + SocioNext UniPhier SoCs. + +config RENESAS_SDHI + bool "Renesas R-Car SD/MMC Host Controller support" + depends on ARCH_RMOBILE + depends on BLK && DM_MMC + depends on OF_CONTROL + help + This selects support for the Matsushita SD/MMC Host Controller on + Renesas R-Car SoCs. config MMC_BCM2835 bool "BCM2835 family custom SD/MMC Host Controller support" diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 25eb669..f2c6280 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -62,5 +62,6 @@ obj-$(CONFIG_MMC_SDHCI_XENON) += xenon_sdhci.o obj-$(CONFIG_MMC_SDHCI_ZYNQ) += zynq_sdhci.o obj-$(CONFIG_MMC_SUNXI) += sunxi_mmc.o -obj-$(CONFIG_MMC_UNIPHIER) += matsushita-common.o uniphier-sd.o renesas-sdhi.o +obj-$(CONFIG_MMC_UNIPHIER) += matsushita-common.o uniphier-sd.o +obj-$(CONFIG_RENESAS_SDHI) += matsushita-common.o renesas-sdhi.o obj-$(CONFIG_MMC_BCM2835) += bcm2835_sdhost.o From 33f65ec83ec1989e33f6510e3d9b094b863585ef Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 8 Apr 2018 19:17:55 +0200 Subject: [PATCH 08/36] ARM: rmobile: Switch to CONFIG_RENESAS_SDHI Since the Renesas SDHI has it's own custom driver sharing the common code with Uniphier one, adjust the Kconfig entries. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- configs/porter_defconfig | 2 +- configs/r8a7795_salvator-x_defconfig | 2 +- configs/r8a7795_ulcb_defconfig | 2 +- configs/r8a77965_salvator-x_defconfig | 2 +- configs/r8a7796_salvator-x_defconfig | 2 +- configs/r8a7796_ulcb_defconfig | 2 +- configs/r8a77970_eagle_defconfig | 2 +- configs/r8a77995_draak_defconfig | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/configs/porter_defconfig b/configs/porter_defconfig index 7c58b13..3e0900e 100644 --- a/configs/porter_defconfig +++ b/configs/porter_defconfig @@ -73,7 +73,7 @@ CONFIG_RCAR_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_RCAR_IIC=y CONFIG_DM_MMC=y -CONFIG_MMC_UNIPHIER=y +CONFIG_RENESAS_SDHI=y CONFIG_SPI_FLASH=y CONFIG_SPI_FLASH_BAR=y CONFIG_SPI_FLASH_SPANSION=y diff --git a/configs/r8a7795_salvator-x_defconfig b/configs/r8a7795_salvator-x_defconfig index 44921d9..0614894 100644 --- a/configs/r8a7795_salvator-x_defconfig +++ b/configs/r8a7795_salvator-x_defconfig @@ -37,7 +37,7 @@ CONFIG_RCAR_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_RCAR_IIC=y CONFIG_DM_MMC=y -CONFIG_MMC_UNIPHIER=y +CONFIG_RENESAS_SDHI=y CONFIG_PHY_MICREL=y CONFIG_PHY_MICREL_KSZ90X1=y CONFIG_DM_ETH=y diff --git a/configs/r8a7795_ulcb_defconfig b/configs/r8a7795_ulcb_defconfig index e85d0a4..cffa719 100644 --- a/configs/r8a7795_ulcb_defconfig +++ b/configs/r8a7795_ulcb_defconfig @@ -37,7 +37,7 @@ CONFIG_RCAR_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_RCAR_IIC=y CONFIG_DM_MMC=y -CONFIG_MMC_UNIPHIER=y +CONFIG_RENESAS_SDHI=y CONFIG_PHY_MICREL=y CONFIG_PHY_MICREL_KSZ90X1=y CONFIG_DM_ETH=y diff --git a/configs/r8a77965_salvator-x_defconfig b/configs/r8a77965_salvator-x_defconfig index 6420249..1bfd91f 100644 --- a/configs/r8a77965_salvator-x_defconfig +++ b/configs/r8a77965_salvator-x_defconfig @@ -38,7 +38,7 @@ CONFIG_RCAR_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_RCAR_IIC=y CONFIG_DM_MMC=y -CONFIG_MMC_UNIPHIER=y +CONFIG_RENESAS_SDHI=y CONFIG_PHY_MICREL=y CONFIG_PHY_MICREL_KSZ90X1=y CONFIG_DM_ETH=y diff --git a/configs/r8a7796_salvator-x_defconfig b/configs/r8a7796_salvator-x_defconfig index e0cf17a..535163f 100644 --- a/configs/r8a7796_salvator-x_defconfig +++ b/configs/r8a7796_salvator-x_defconfig @@ -38,7 +38,7 @@ CONFIG_RCAR_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_RCAR_IIC=y CONFIG_DM_MMC=y -CONFIG_MMC_UNIPHIER=y +CONFIG_RENESAS_SDHI=y CONFIG_PHY_MICREL=y CONFIG_PHY_MICREL_KSZ90X1=y CONFIG_DM_ETH=y diff --git a/configs/r8a7796_ulcb_defconfig b/configs/r8a7796_ulcb_defconfig index 5a65982..45c0ca2 100644 --- a/configs/r8a7796_ulcb_defconfig +++ b/configs/r8a7796_ulcb_defconfig @@ -38,7 +38,7 @@ CONFIG_RCAR_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_RCAR_IIC=y CONFIG_DM_MMC=y -CONFIG_MMC_UNIPHIER=y +CONFIG_RENESAS_SDHI=y CONFIG_PHY_MICREL=y CONFIG_PHY_MICREL_KSZ90X1=y CONFIG_DM_ETH=y diff --git a/configs/r8a77970_eagle_defconfig b/configs/r8a77970_eagle_defconfig index 3d421d2..2e7f7ac 100644 --- a/configs/r8a77970_eagle_defconfig +++ b/configs/r8a77970_eagle_defconfig @@ -37,7 +37,7 @@ CONFIG_RCAR_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_RCAR_IIC=y CONFIG_DM_MMC=y -CONFIG_MMC_UNIPHIER=y +CONFIG_RENESAS_SDHI=y CONFIG_PHY_MICREL=y CONFIG_PHY_MICREL_KSZ90X1=y CONFIG_DM_ETH=y diff --git a/configs/r8a77995_draak_defconfig b/configs/r8a77995_draak_defconfig index 99a8089..7a2e33e 100644 --- a/configs/r8a77995_draak_defconfig +++ b/configs/r8a77995_draak_defconfig @@ -38,7 +38,7 @@ CONFIG_RCAR_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_RCAR_IIC=y CONFIG_DM_MMC=y -CONFIG_MMC_UNIPHIER=y +CONFIG_RENESAS_SDHI=y CONFIG_MTD=y CONFIG_MTD_NOR_FLASH=y CONFIG_CFI_FLASH=y From 12a510e23fecbd4a7697595c0d9ca24d8ce3407c Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 8 Apr 2018 17:14:42 +0200 Subject: [PATCH 09/36] mmc: uniphier: Factor out FIFO accessors Add macros to generate the FIFO accessors, since the code is almost the same with only minor differences. This is done in preparation for adding 16bit variant of the IP. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/matsushita-common.c | 122 ++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 62 deletions(-) diff --git a/drivers/mmc/matsushita-common.c b/drivers/mmc/matsushita-common.c index ed67710..4553b16 100644 --- a/drivers/mmc/matsushita-common.c +++ b/drivers/mmc/matsushita-common.c @@ -132,11 +132,36 @@ static int matsu_sd_wait_for_irq(struct udevice *dev, unsigned int reg, return 0; } +#define matsu_pio_read_fifo(__width, __suffix) \ +static void matsu_pio_read_fifo_##__width(struct matsu_sd_priv *priv, \ + char *pbuf, uint blksz) \ +{ \ + u##__width *buf = (u##__width *)pbuf; \ + int i; \ + \ + if (likely(IS_ALIGNED((uintptr_t)buf, ((__width) / 8)))) { \ + for (i = 0; i < blksz / ((__width) / 8); i++) { \ + *buf++ = matsu_sd_read##__suffix(priv, \ + MATSU_SD_BUF); \ + } \ + } else { \ + for (i = 0; i < blksz / ((__width) / 8); i++) { \ + u##__width data; \ + data = matsu_sd_read##__suffix(priv, \ + MATSU_SD_BUF); \ + put_unaligned(data, buf++); \ + } \ + } \ +} + +matsu_pio_read_fifo(64, q) +matsu_pio_read_fifo(32, l) + static int matsu_sd_pio_read_one_block(struct udevice *dev, char *pbuf, uint blocksize) { struct matsu_sd_priv *priv = dev_get_priv(dev); - int i, ret; + int ret; /* wait until the buffer is filled with data */ ret = matsu_sd_wait_for_irq(dev, MATSU_SD_INFO2, @@ -150,83 +175,56 @@ static int matsu_sd_pio_read_one_block(struct udevice *dev, char *pbuf, */ matsu_sd_writel(priv, 0, MATSU_SD_INFO2); - if (priv->caps & MATSU_SD_CAP_64BIT) { - u64 *buf = (u64 *)pbuf; - if (likely(IS_ALIGNED((uintptr_t)buf, 8))) { - for (i = 0; i < blocksize / 8; i++) { - *buf++ = matsu_sd_readq(priv, - MATSU_SD_BUF); - } - } else { - for (i = 0; i < blocksize / 8; i++) { - u64 data; - data = matsu_sd_readq(priv, - MATSU_SD_BUF); - put_unaligned(data, buf++); - } - } - } else { - u32 *buf = (u32 *)pbuf; - if (likely(IS_ALIGNED((uintptr_t)buf, 4))) { - for (i = 0; i < blocksize / 4; i++) { - *buf++ = matsu_sd_readl(priv, - MATSU_SD_BUF); - } - } else { - for (i = 0; i < blocksize / 4; i++) { - u32 data; - data = matsu_sd_readl(priv, MATSU_SD_BUF); - put_unaligned(data, buf++); - } - } - } + if (priv->caps & MATSU_SD_CAP_64BIT) + matsu_pio_read_fifo_64(priv, pbuf, blocksize); + else + matsu_pio_read_fifo_32(priv, pbuf, blocksize); return 0; } +#define matsu_pio_write_fifo(__width, __suffix) \ +static void matsu_pio_write_fifo_##__width(struct matsu_sd_priv *priv, \ + const char *pbuf, uint blksz)\ +{ \ + const u##__width *buf = (const u##__width *)pbuf; \ + int i; \ + \ + if (likely(IS_ALIGNED((uintptr_t)buf, ((__width) / 8)))) { \ + for (i = 0; i < blksz / ((__width) / 8); i++) { \ + matsu_sd_write##__suffix(priv, *buf++, \ + MATSU_SD_BUF); \ + } \ + } else { \ + for (i = 0; i < blksz / ((__width) / 8); i++) { \ + u##__width data = get_unaligned(buf++); \ + matsu_sd_write##__suffix(priv, data, \ + MATSU_SD_BUF); \ + } \ + } \ +} + +matsu_pio_write_fifo(64, q) +matsu_pio_write_fifo(32, l) + static int matsu_sd_pio_write_one_block(struct udevice *dev, const char *pbuf, uint blocksize) { struct matsu_sd_priv *priv = dev_get_priv(dev); - int i, ret; + int ret; /* wait until the buffer becomes empty */ ret = matsu_sd_wait_for_irq(dev, MATSU_SD_INFO2, - MATSU_SD_INFO2_BWE); + MATSU_SD_INFO2_BWE); if (ret) return ret; matsu_sd_writel(priv, 0, MATSU_SD_INFO2); - if (priv->caps & MATSU_SD_CAP_64BIT) { - const u64 *buf = (const u64 *)pbuf; - if (likely(IS_ALIGNED((uintptr_t)buf, 8))) { - for (i = 0; i < blocksize / 8; i++) { - matsu_sd_writeq(priv, *buf++, - MATSU_SD_BUF); - } - } else { - for (i = 0; i < blocksize / 8; i++) { - u64 data = get_unaligned(buf++); - matsu_sd_writeq(priv, data, - MATSU_SD_BUF); - } - } - } else { - const u32 *buf = (const u32 *)pbuf; - if (likely(IS_ALIGNED((uintptr_t)buf, 4))) { - for (i = 0; i < blocksize / 4; i++) { - matsu_sd_writel(priv, *buf++, - MATSU_SD_BUF); - } - } else { - for (i = 0; i < blocksize / 4; i++) { - u32 data = get_unaligned(buf++); - matsu_sd_writel(priv, data, - MATSU_SD_BUF); - } - } - } + if (priv->caps & MATSU_SD_CAP_64BIT) + matsu_pio_write_fifo_64(priv, pbuf, blocksize); + else + matsu_pio_write_fifo_32(priv, pbuf, blocksize); return 0; } From 620fd85c0b650b4061ba5c989276d58c27280be2 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 8 Apr 2018 17:25:49 +0200 Subject: [PATCH 10/36] mmc: uniphier: Drop useless check Drop useless check in matsu_sd_{read,write}q(), this is only ever called to read the data from FIFO and only when 64bit variant of the block is used anyway. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/matsushita-common.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/matsushita-common.c b/drivers/mmc/matsushita-common.c index 4553b16..ec54698 100644 --- a/drivers/mmc/matsushita-common.c +++ b/drivers/mmc/matsushita-common.c @@ -23,19 +23,13 @@ DECLARE_GLOBAL_DATA_PTR; static u64 matsu_sd_readq(struct matsu_sd_priv *priv, unsigned int reg) { - if (priv->caps & MATSU_SD_CAP_64BIT) - return readq(priv->regbase + (reg << 1)); - else - return readq(priv->regbase + reg); + return readq(priv->regbase + (reg << 1)); } static void matsu_sd_writeq(struct matsu_sd_priv *priv, u64 val, unsigned int reg) { - if (priv->caps & MATSU_SD_CAP_64BIT) - writeq(val, priv->regbase + (reg << 1)); - else - writeq(val, priv->regbase + reg); + writeq(val, priv->regbase + (reg << 1)); } static u32 matsu_sd_readl(struct matsu_sd_priv *priv, unsigned int reg) From db1266d69648aa58a2c59273e4d3b773e0fd4f0f Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 8 Apr 2018 17:41:14 +0200 Subject: [PATCH 11/36] mmc: uniphier: Add support for 16bit variant Add support for 16bit mutation of the Matsushita SD IP. Since some registers are internally 32bit, the matsu_sd_{read,write}l() has to special-case this 16bit variant a bit. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/matsushita-common.c | 42 +++++++++++++++++++++++++++++++++++++---- drivers/mmc/matsushita-common.h | 1 + 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/matsushita-common.c b/drivers/mmc/matsushita-common.c index ec54698..9f7f47c 100644 --- a/drivers/mmc/matsushita-common.c +++ b/drivers/mmc/matsushita-common.c @@ -32,11 +32,31 @@ static void matsu_sd_writeq(struct matsu_sd_priv *priv, writeq(val, priv->regbase + (reg << 1)); } +static u16 matsu_sd_readw(struct matsu_sd_priv *priv, unsigned int reg) +{ + return readw(priv->regbase + (reg >> 1)); +} + +static void matsu_sd_writew(struct matsu_sd_priv *priv, + u16 val, unsigned int reg) +{ + writew(val, priv->regbase + (reg >> 1)); +} + static u32 matsu_sd_readl(struct matsu_sd_priv *priv, unsigned int reg) { + u32 val; + if (priv->caps & MATSU_SD_CAP_64BIT) return readl(priv->regbase + (reg << 1)); - else + else if (priv->caps & MATSU_SD_CAP_16BIT) { + val = readw(priv->regbase + (reg >> 1)) & 0xffff; + if ((reg == MATSU_SD_RSP10) || (reg == MATSU_SD_RSP32) || + (reg == MATSU_SD_RSP54) || (reg == MATSU_SD_RSP76)) { + val |= readw(priv->regbase + (reg >> 1) + 2) << 16; + } + return val; + } else return readl(priv->regbase + reg); } @@ -45,7 +65,11 @@ static void matsu_sd_writel(struct matsu_sd_priv *priv, { if (priv->caps & MATSU_SD_CAP_64BIT) writel(val, priv->regbase + (reg << 1)); - else + if (priv->caps & MATSU_SD_CAP_16BIT) { + writew(val & 0xffff, priv->regbase + (reg >> 1)); + if (val >> 16) + writew(val >> 16, priv->regbase + (reg >> 1) + 2); + } else writel(val, priv->regbase + reg); } @@ -150,6 +174,7 @@ static void matsu_pio_read_fifo_##__width(struct matsu_sd_priv *priv, \ matsu_pio_read_fifo(64, q) matsu_pio_read_fifo(32, l) +matsu_pio_read_fifo(16, w) static int matsu_sd_pio_read_one_block(struct udevice *dev, char *pbuf, uint blocksize) @@ -171,6 +196,8 @@ static int matsu_sd_pio_read_one_block(struct udevice *dev, char *pbuf, if (priv->caps & MATSU_SD_CAP_64BIT) matsu_pio_read_fifo_64(priv, pbuf, blocksize); + else if (priv->caps & MATSU_SD_CAP_16BIT) + matsu_pio_read_fifo_16(priv, pbuf, blocksize); else matsu_pio_read_fifo_32(priv, pbuf, blocksize); @@ -200,6 +227,7 @@ static void matsu_pio_write_fifo_##__width(struct matsu_sd_priv *priv, \ matsu_pio_write_fifo(64, q) matsu_pio_write_fifo(32, l) +matsu_pio_write_fifo(16, w) static int matsu_sd_pio_write_one_block(struct udevice *dev, const char *pbuf, uint blocksize) @@ -217,6 +245,8 @@ static int matsu_sd_pio_write_one_block(struct udevice *dev, if (priv->caps & MATSU_SD_CAP_64BIT) matsu_pio_write_fifo_64(priv, pbuf, blocksize); + else if (priv->caps & MATSU_SD_CAP_16BIT) + matsu_pio_write_fifo_16(priv, pbuf, blocksize); else matsu_pio_write_fifo_32(priv, pbuf, blocksize); @@ -602,8 +632,12 @@ static void matsu_sd_host_init(struct matsu_sd_priv *priv) * This register dropped backward compatibility at version 0x10. * Write an appropriate value depending on the IP version. */ - matsu_sd_writel(priv, priv->version >= 0x10 ? 0x00000101 : 0x00000000, - MATSU_SD_HOST_MODE); + if (priv->version >= 0x10) + matsu_sd_writel(priv, 0x101, MATSU_SD_HOST_MODE); + else if (priv->caps & MATSU_SD_CAP_16BIT) + matsu_sd_writel(priv, 0x1, MATSU_SD_HOST_MODE); + else + matsu_sd_writel(priv, 0x0, MATSU_SD_HOST_MODE); if (priv->caps & MATSU_SD_CAP_DMA_INTERNAL) { tmp = matsu_sd_readl(priv, MATSU_SD_DMA_MODE); diff --git a/drivers/mmc/matsushita-common.h b/drivers/mmc/matsushita-common.h index e517a2d..c1b28a0 100644 --- a/drivers/mmc/matsushita-common.h +++ b/drivers/mmc/matsushita-common.h @@ -123,6 +123,7 @@ struct matsu_sd_priv { #define MATSU_SD_CAP_DMA_INTERNAL BIT(1) /* have internal DMA engine */ #define MATSU_SD_CAP_DIV1024 BIT(2) /* divisor 1024 is available */ #define MATSU_SD_CAP_64BIT BIT(3) /* Controller is 64bit */ +#define MATSU_SD_CAP_16BIT BIT(4) /* Controller is 16bit */ }; int matsu_sd_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, From c769e609907ccf838801433cdb89e73972f5550b Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 8 Apr 2018 17:45:23 +0200 Subject: [PATCH 12/36] mmc: uniphier: Allow passing quirks to the probe function Certain instances of the SD IP require more elaborate digging in the DT to figure out which variant of the SD IP is in use. Allow explicit passing of the quirks into the probe function. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/matsushita-common.c | 3 +-- drivers/mmc/matsushita-common.h | 2 +- drivers/mmc/renesas-sdhi.c | 9 ++++++++- drivers/mmc/uniphier-sd.c | 7 ++++++- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/matsushita-common.c b/drivers/mmc/matsushita-common.c index 9f7f47c..0b0cbaf 100644 --- a/drivers/mmc/matsushita-common.c +++ b/drivers/mmc/matsushita-common.c @@ -653,12 +653,11 @@ int matsu_sd_bind(struct udevice *dev) return mmc_bind(dev, &plat->mmc, &plat->cfg); } -int matsu_sd_probe(struct udevice *dev) +int matsu_sd_probe(struct udevice *dev, u32 quirks) { struct matsu_sd_plat *plat = dev_get_platdata(dev); struct matsu_sd_priv *priv = dev_get_priv(dev); struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); - const u32 quirks = dev_get_driver_data(dev); fdt_addr_t base; struct clk clk; int ret; diff --git a/drivers/mmc/matsushita-common.h b/drivers/mmc/matsushita-common.h index c1b28a0..a03d8f9 100644 --- a/drivers/mmc/matsushita-common.h +++ b/drivers/mmc/matsushita-common.h @@ -132,6 +132,6 @@ int matsu_sd_set_ios(struct udevice *dev); int matsu_sd_get_cd(struct udevice *dev); int matsu_sd_bind(struct udevice *dev); -int matsu_sd_probe(struct udevice *dev); +int matsu_sd_probe(struct udevice *dev, u32 quirks); #endif /* __MATSUSHITA_COMMON_H__ */ diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c index 9b388b3..d6b3bfb 100644 --- a/drivers/mmc/renesas-sdhi.c +++ b/drivers/mmc/renesas-sdhi.c @@ -38,12 +38,19 @@ static const struct udevice_id renesas_sdhi_match[] = { { /* sentinel */ } }; +static int renesas_sdhi_probe(struct udevice *dev) +{ + u32 quirks = dev_get_driver_data(dev); + + return matsu_sd_probe(dev, quirks); +} + U_BOOT_DRIVER(renesas_sdhi) = { .name = "renesas-sdhi", .id = UCLASS_MMC, .of_match = renesas_sdhi_match, .bind = matsu_sd_bind, - .probe = matsu_sd_probe, + .probe = renesas_sdhi_probe, .priv_auto_alloc_size = sizeof(struct matsu_sd_priv), .platdata_auto_alloc_size = sizeof(struct matsu_sd_plat), .ops = &renesas_sdhi_ops, diff --git a/drivers/mmc/uniphier-sd.c b/drivers/mmc/uniphier-sd.c index 72f0d46..42eb9c2 100644 --- a/drivers/mmc/uniphier-sd.c +++ b/drivers/mmc/uniphier-sd.c @@ -30,12 +30,17 @@ static const struct udevice_id uniphier_sd_match[] = { { /* sentinel */ } }; +static int uniphier_sd_probe(struct udevice *dev) +{ + return matsu_sd_probe(dev, 0); +} + U_BOOT_DRIVER(uniphier_mmc) = { .name = "uniphier-mmc", .id = UCLASS_MMC, .of_match = uniphier_sd_match, .bind = matsu_sd_bind, - .probe = matsu_sd_probe, + .probe = uniphier_sd_probe, .priv_auto_alloc_size = sizeof(struct matsu_sd_priv), .platdata_auto_alloc_size = sizeof(struct matsu_sd_plat), .ops = &uniphier_sd_ops, From 7cf7ef81ed8b57194b5c4408c05963b664d44229 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 8 Apr 2018 18:14:22 +0200 Subject: [PATCH 13/36] mmc: renesas-sdhi: Handle 16bit IP The Renesas RCar Gen2 chips have a mix of 32bit and 16bit variants of the IP. There is no DT property which allows discerning those, so what Linux does is it checks the size of the register area and if it is 0x100, the IP is 16bit, otherwise the IP is 32bit. Handle the distinction the same way. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/renesas-sdhi.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c index d6b3bfb..5215743 100644 --- a/drivers/mmc/renesas-sdhi.c +++ b/drivers/mmc/renesas-sdhi.c @@ -41,6 +41,19 @@ static const struct udevice_id renesas_sdhi_match[] = { static int renesas_sdhi_probe(struct udevice *dev) { u32 quirks = dev_get_driver_data(dev); + struct fdt_resource reg_res; + DECLARE_GLOBAL_DATA_PTR; + int ret; + + ret = fdt_get_resource(gd->fdt_blob, dev_of_offset(dev), "reg", + 0, ®_res); + if (ret < 0) { + dev_err(dev, "\"reg\" resource not found, ret=%i\n", ret); + return ret; + } + + if (quirks == 0 && fdt_resource_size(®_res) == 0x100) + quirks = MATSU_SD_CAP_16BIT; return matsu_sd_probe(dev, quirks); } From 147169d9c9ed8d0e1ca6185c2e45677be9b18157 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 23 Sep 2017 13:22:14 +0200 Subject: [PATCH 14/36] mmc: matsushita-common: Use mmc_of_parse() Drop the ad-hoc DT caps parsing in favor of common framework function. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/matsushita-common.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/drivers/mmc/matsushita-common.c b/drivers/mmc/matsushita-common.c index 0b0cbaf..449f533 100644 --- a/drivers/mmc/matsushita-common.c +++ b/drivers/mmc/matsushita-common.c @@ -703,24 +703,15 @@ int matsu_sd_probe(struct udevice *dev, u32 quirks) return ret; } - plat->cfg.name = dev->name; - plat->cfg.host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS; - - switch (fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "bus-width", - 1)) { - case 8: - plat->cfg.host_caps |= MMC_MODE_8BIT; - break; - case 4: - plat->cfg.host_caps |= MMC_MODE_4BIT; - break; - case 1: - break; - default: - dev_err(dev, "Invalid \"bus-width\" value\n"); - return -EINVAL; + ret = mmc_of_parse(dev, &plat->cfg); + if (ret < 0) { + dev_err(dev, "failed to parse host caps\n"); + return ret; } + plat->cfg.name = dev->name; + plat->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; + if (quirks) { priv->caps = quirks; } else { From f98833dbe61e8784f6c7afed8f5ff9290973d211 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 8 Apr 2018 18:49:52 +0200 Subject: [PATCH 15/36] mmc: matsushita-common: Add Renesas RCar quirks Add a quirk to identify that the controller is Renesas RCar variant of the Matsushita SD IP and another quirk indicating it can support Renesas RCar HS200/HS400/SDR104 modes. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/matsushita-common.h | 5 +++++ drivers/mmc/renesas-sdhi.c | 43 ++++++++++++++++++++++++----------------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/drivers/mmc/matsushita-common.h b/drivers/mmc/matsushita-common.h index a03d8f9..c23dc1a 100644 --- a/drivers/mmc/matsushita-common.h +++ b/drivers/mmc/matsushita-common.h @@ -124,6 +124,11 @@ struct matsu_sd_priv { #define MATSU_SD_CAP_DIV1024 BIT(2) /* divisor 1024 is available */ #define MATSU_SD_CAP_64BIT BIT(3) /* Controller is 64bit */ #define MATSU_SD_CAP_16BIT BIT(4) /* Controller is 16bit */ +#define MATSU_SD_CAP_RCAR_GEN2 BIT(5) /* Renesas RCar version of IP */ +#define MATSU_SD_CAP_RCAR_GEN3 BIT(6) /* Renesas RCar version of IP */ +#define MATSU_SD_CAP_RCAR_UHS BIT(7) /* Renesas RCar UHS/SDR modes */ +#define MATSU_SD_CAP_RCAR \ + (MATSU_SD_CAP_RCAR_GEN2 | MATSU_SD_CAP_RCAR_GEN3) }; int matsu_sd_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c index 5215743..73f68c8 100644 --- a/drivers/mmc/renesas-sdhi.c +++ b/drivers/mmc/renesas-sdhi.c @@ -24,17 +24,21 @@ static const struct dm_mmc_ops renesas_sdhi_ops = { .get_cd = matsu_sd_get_cd, }; +#define RENESAS_GEN2_QUIRKS MATSU_SD_CAP_RCAR_GEN2 +#define RENESAS_GEN3_QUIRKS \ + MATSU_SD_CAP_64BIT | MATSU_SD_CAP_RCAR_GEN3 | MATSU_SD_CAP_RCAR_UHS + static const struct udevice_id renesas_sdhi_match[] = { - { .compatible = "renesas,sdhi-r8a7790", .data = 0 }, - { .compatible = "renesas,sdhi-r8a7791", .data = 0 }, - { .compatible = "renesas,sdhi-r8a7792", .data = 0 }, - { .compatible = "renesas,sdhi-r8a7793", .data = 0 }, - { .compatible = "renesas,sdhi-r8a7794", .data = 0 }, - { .compatible = "renesas,sdhi-r8a7795", .data = MATSU_SD_CAP_64BIT }, - { .compatible = "renesas,sdhi-r8a7796", .data = MATSU_SD_CAP_64BIT }, - { .compatible = "renesas,sdhi-r8a77965", .data = MATSU_SD_CAP_64BIT }, - { .compatible = "renesas,sdhi-r8a77970", .data = MATSU_SD_CAP_64BIT }, - { .compatible = "renesas,sdhi-r8a77995", .data = MATSU_SD_CAP_64BIT }, + { .compatible = "renesas,sdhi-r8a7790", .data = RENESAS_GEN2_QUIRKS }, + { .compatible = "renesas,sdhi-r8a7791", .data = RENESAS_GEN2_QUIRKS }, + { .compatible = "renesas,sdhi-r8a7792", .data = RENESAS_GEN2_QUIRKS }, + { .compatible = "renesas,sdhi-r8a7793", .data = RENESAS_GEN2_QUIRKS }, + { .compatible = "renesas,sdhi-r8a7794", .data = RENESAS_GEN2_QUIRKS }, + { .compatible = "renesas,sdhi-r8a7795", .data = RENESAS_GEN3_QUIRKS }, + { .compatible = "renesas,sdhi-r8a7796", .data = RENESAS_GEN3_QUIRKS }, + { .compatible = "renesas,sdhi-r8a77965", .data = RENESAS_GEN3_QUIRKS }, + { .compatible = "renesas,sdhi-r8a77970", .data = RENESAS_GEN3_QUIRKS }, + { .compatible = "renesas,sdhi-r8a77995", .data = RENESAS_GEN3_QUIRKS }, { /* sentinel */ } }; @@ -45,15 +49,18 @@ static int renesas_sdhi_probe(struct udevice *dev) DECLARE_GLOBAL_DATA_PTR; int ret; - ret = fdt_get_resource(gd->fdt_blob, dev_of_offset(dev), "reg", - 0, ®_res); - if (ret < 0) { - dev_err(dev, "\"reg\" resource not found, ret=%i\n", ret); - return ret; - } + if (quirks == RENESAS_GEN2_QUIRKS) { + ret = fdt_get_resource(gd->fdt_blob, dev_of_offset(dev), + "reg", 0, ®_res); + if (ret < 0) { + dev_err(dev, "\"reg\" resource not found, ret=%i\n", + ret); + return ret; + } - if (quirks == 0 && fdt_resource_size(®_res) == 0x100) - quirks = MATSU_SD_CAP_16BIT; + if (fdt_resource_size(®_res) == 0x100) + quirks |= MATSU_SD_CAP_16BIT; + } return matsu_sd_probe(dev, quirks); } From 78773f1467336f4d874a6de8e56a5092b786fde5 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 26 Sep 2017 20:34:35 +0200 Subject: [PATCH 16/36] mmc: matsushita-common: Handle Renesas div-by-1 On the Renesas version of the IP, the /1 divider is realized by setting the clock register [7:0] to 0xff instead of setting bit 10 of the register. Check the quirk and handle accordingly. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/matsushita-common.c | 3 ++- drivers/mmc/matsushita-common.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/matsushita-common.c b/drivers/mmc/matsushita-common.c index 449f533..7e05b1f 100644 --- a/drivers/mmc/matsushita-common.c +++ b/drivers/mmc/matsushita-common.c @@ -542,7 +542,8 @@ static void matsu_sd_set_clk_rate(struct matsu_sd_priv *priv, divisor = DIV_ROUND_UP(priv->mclk, mmc->clock); if (divisor <= 1) - val = MATSU_SD_CLKCTL_DIV1; + val = (priv->caps & MATSU_SD_CAP_RCAR) ? + MATSU_SD_CLKCTL_RCAR_DIV1 : MATSU_SD_CLKCTL_DIV1; else if (divisor <= 2) val = MATSU_SD_CLKCTL_DIV2; else if (divisor <= 4) diff --git a/drivers/mmc/matsushita-common.h b/drivers/mmc/matsushita-common.h index c23dc1a..a10ad20 100644 --- a/drivers/mmc/matsushita-common.h +++ b/drivers/mmc/matsushita-common.h @@ -63,6 +63,7 @@ #define MATSU_SD_CLKCTL_DIV4 BIT(0) /* SDCLK = CLK / 4 */ #define MATSU_SD_CLKCTL_DIV2 0 /* SDCLK = CLK / 2 */ #define MATSU_SD_CLKCTL_DIV1 BIT(10) /* SDCLK = CLK */ +#define MATSU_SD_CLKCTL_RCAR_DIV1 0xff /* SDCLK = CLK (RCar ver.) */ #define MATSU_SD_CLKCTL_OFFEN BIT(9) /* stop SDCLK when unused */ #define MATSU_SD_CLKCTL_SCLKEN BIT(8) /* SDCLK output enable */ #define MATSU_SD_SIZE 0x04c /* block size */ From 635ae6fefa0945fa5361de6f460d3dde93b143f7 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 2 Jan 2018 19:40:49 +0100 Subject: [PATCH 17/36] mmc: matsushita-common: Handle DMA completion flag differences The DMA READ completion flag position differs on Socionext and Renesas SoCs. It is bit 20 on Socionext SoCs and using bit 17 is a hardware bug and forbidden. It is bit 17 on Renesas SoCs and bit 20 does not work on them. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/matsushita-common.c | 10 +++++++++- drivers/mmc/matsushita-common.h | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/matsushita-common.c b/drivers/mmc/matsushita-common.c index 7e05b1f..11e4553 100644 --- a/drivers/mmc/matsushita-common.c +++ b/drivers/mmc/matsushita-common.c @@ -339,7 +339,15 @@ static int matsu_sd_dma_xfer(struct udevice *dev, struct mmc_data *data) if (data->flags & MMC_DATA_READ) { buf = data->dest; dir = DMA_FROM_DEVICE; - poll_flag = MATSU_SD_DMA_INFO1_END_RD2; + /* + * The DMA READ completion flag position differs on Socionext + * and Renesas SoCs. It is bit 20 on Socionext SoCs and using + * bit 17 is a hardware bug and forbidden. It is bit 17 on + * Renesas SoCs and bit 20 does not work on them. + */ + poll_flag = (priv->caps & MATSU_SD_CAP_RCAR) ? + MATSU_SD_DMA_INFO1_END_RD : + MATSU_SD_DMA_INFO1_END_RD2; tmp |= MATSU_SD_DMA_MODE_DIR_RD; } else { buf = (void *)data->src; diff --git a/drivers/mmc/matsushita-common.h b/drivers/mmc/matsushita-common.h index a10ad20..8c81bbc 100644 --- a/drivers/mmc/matsushita-common.h +++ b/drivers/mmc/matsushita-common.h @@ -96,8 +96,8 @@ #define MATSU_SD_DMA_RST_RD BIT(9) #define MATSU_SD_DMA_RST_WR BIT(8) #define MATSU_SD_DMA_INFO1 0x420 -#define MATSU_SD_DMA_INFO1_END_RD2 BIT(20) /* DMA from device is complete*/ -#define MATSU_SD_DMA_INFO1_END_RD BIT(17) /* Don't use! Hardware bug */ +#define MATSU_SD_DMA_INFO1_END_RD2 BIT(20) /* DMA from device is complete (uniphier) */ +#define MATSU_SD_DMA_INFO1_END_RD BIT(17) /* DMA from device is complete (renesas) */ #define MATSU_SD_DMA_INFO1_END_WR BIT(16) /* DMA to device is complete */ #define MATSU_SD_DMA_INFO1_MASK 0x424 #define MATSU_SD_DMA_INFO2 0x428 From a7b7401c786871ec65572e1cd66703a0eb461060 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 30 Jan 2018 00:41:42 +0100 Subject: [PATCH 18/36] mmc: matsushita-common: Handle bus width 0 Handle bus width 0 as 1-bit bus to assure valid content of MATSU_SD_OPTION register WIDTH field. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/matsushita-common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/matsushita-common.c b/drivers/mmc/matsushita-common.c index 11e4553..377f349 100644 --- a/drivers/mmc/matsushita-common.c +++ b/drivers/mmc/matsushita-common.c @@ -504,6 +504,7 @@ static int matsu_sd_set_bus_width(struct matsu_sd_priv *priv, u32 val, tmp; switch (mmc->bus_width) { + case 0: case 1: val = MATSU_SD_OPTION_WIDTH_1; break; From 58c35b17aa8f6192d44edcd016ab2f7045bc0e97 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 23 Sep 2017 13:01:20 +0200 Subject: [PATCH 19/36] mmc: matsushita-common: Always check controller version Handle the controller version even if quirks are set. The controller in Renesas Gen3 SoCs does provide the version register, which indicates a controller v10 and the controller does support internal DMA and /1024 divider. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/matsushita-common.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/matsushita-common.c b/drivers/mmc/matsushita-common.c index 377f349..d5facd9 100644 --- a/drivers/mmc/matsushita-common.c +++ b/drivers/mmc/matsushita-common.c @@ -722,16 +722,15 @@ int matsu_sd_probe(struct udevice *dev, u32 quirks) plat->cfg.name = dev->name; plat->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; - if (quirks) { + if (quirks) priv->caps = quirks; - } else { - priv->version = matsu_sd_readl(priv, MATSU_SD_VERSION) & - MATSU_SD_VERSION_IP; - dev_dbg(dev, "version %x\n", priv->version); - if (priv->version >= 0x10) { - priv->caps |= MATSU_SD_CAP_DMA_INTERNAL; - priv->caps |= MATSU_SD_CAP_DIV1024; - } + + priv->version = matsu_sd_readl(priv, MATSU_SD_VERSION) & + MATSU_SD_VERSION_IP; + dev_dbg(dev, "version %x\n", priv->version); + if (priv->version >= 0x10) { + priv->caps |= MATSU_SD_CAP_DMA_INTERNAL; + priv->caps |= MATSU_SD_CAP_DIV1024; } if (fdt_get_property(gd->fdt_blob, dev_of_offset(dev), "non-removable", From e10422f108584269e0a81b16d45d68a22c527878 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 25 Sep 2017 23:06:22 +0200 Subject: [PATCH 20/36] mmc: matsushita-common: Properly handle pin voltage configuration Factor out the regulator handling into set_ios and add support for selecting pin configuration based on the voltage to support UHS modes. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/matsushita-common.c | 52 ++++++++++++++++++++++++++++++++++------- drivers/mmc/matsushita-common.h | 3 +++ 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/matsushita-common.c b/drivers/mmc/matsushita-common.c index d5facd9..b143f5c 100644 --- a/drivers/mmc/matsushita-common.c +++ b/drivers/mmc/matsushita-common.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -593,6 +594,46 @@ static void matsu_sd_set_clk_rate(struct matsu_sd_priv *priv, udelay(1000); } +static void matsu_sd_set_pins(struct udevice *dev) +{ + __maybe_unused struct mmc *mmc = mmc_get_mmc_dev(dev); + +#ifdef CONFIG_DM_REGULATOR + struct matsu_sd_priv *priv = dev_get_priv(dev); + + if (priv->vqmmc_dev) { + if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180) + regulator_set_value(priv->vqmmc_dev, 1800000); + else + regulator_set_value(priv->vqmmc_dev, 3300000); + regulator_set_enable(priv->vqmmc_dev, true); + } +#endif + +#ifdef CONFIG_PINCTRL + switch (mmc->selected_mode) { + case MMC_LEGACY: + case SD_LEGACY: + case MMC_HS: + case SD_HS: + case MMC_HS_52: + case MMC_DDR_52: + pinctrl_select_state(dev, "default"); + break; + case UHS_SDR12: + case UHS_SDR25: + case UHS_SDR50: + case UHS_DDR50: + case UHS_SDR104: + case MMC_HS_200: + pinctrl_select_state(dev, "state_uhs"); + break; + default: + break; + } +#endif +} + int matsu_sd_set_ios(struct udevice *dev) { struct matsu_sd_priv *priv = dev_get_priv(dev); @@ -607,6 +648,7 @@ int matsu_sd_set_ios(struct udevice *dev) return ret; matsu_sd_set_ddr_mode(priv, mmc); matsu_sd_set_clk_rate(priv, mmc); + matsu_sd_set_pins(dev); return 0; } @@ -671,9 +713,6 @@ int matsu_sd_probe(struct udevice *dev, u32 quirks) fdt_addr_t base; struct clk clk; int ret; -#ifdef CONFIG_DM_REGULATOR - struct udevice *vqmmc_dev; -#endif base = devfdt_get_addr(dev); if (base == FDT_ADDR_T_NONE) @@ -684,12 +723,7 @@ int matsu_sd_probe(struct udevice *dev, u32 quirks) return -ENOMEM; #ifdef CONFIG_DM_REGULATOR - ret = device_get_supply_regulator(dev, "vqmmc-supply", &vqmmc_dev); - if (!ret) { - /* Set the regulator to 3.3V until we support 1.8V modes */ - regulator_set_value(vqmmc_dev, 3300000); - regulator_set_enable(vqmmc_dev, true); - } + device_get_supply_regulator(dev, "vqmmc-supply", &priv->vqmmc_dev); #endif ret = clk_get_by_index(dev, 0, &clk); diff --git a/drivers/mmc/matsushita-common.h b/drivers/mmc/matsushita-common.h index 8c81bbc..b019b72 100644 --- a/drivers/mmc/matsushita-common.h +++ b/drivers/mmc/matsushita-common.h @@ -130,6 +130,9 @@ struct matsu_sd_priv { #define MATSU_SD_CAP_RCAR_UHS BIT(7) /* Renesas RCar UHS/SDR modes */ #define MATSU_SD_CAP_RCAR \ (MATSU_SD_CAP_RCAR_GEN2 | MATSU_SD_CAP_RCAR_GEN3) +#ifdef CONFIG_DM_REGULATOR + struct udevice *vqmmc_dev; +#endif }; int matsu_sd_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, From 0e2bd5aa49ae2f71473d5d32714a79e3df1cf730 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 8 Apr 2018 19:56:46 +0200 Subject: [PATCH 21/36] mmc: matsushita-common: Export register access functions Export the matsu_sd_{read,write}l() common register access functions, so that they can be used by other drivers sharing the common code. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/matsushita-common.c | 4 ++-- drivers/mmc/matsushita-common.h | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/matsushita-common.c b/drivers/mmc/matsushita-common.c index b143f5c..3f538c3 100644 --- a/drivers/mmc/matsushita-common.c +++ b/drivers/mmc/matsushita-common.c @@ -44,7 +44,7 @@ static void matsu_sd_writew(struct matsu_sd_priv *priv, writew(val, priv->regbase + (reg >> 1)); } -static u32 matsu_sd_readl(struct matsu_sd_priv *priv, unsigned int reg) +u32 matsu_sd_readl(struct matsu_sd_priv *priv, unsigned int reg) { u32 val; @@ -61,7 +61,7 @@ static u32 matsu_sd_readl(struct matsu_sd_priv *priv, unsigned int reg) return readl(priv->regbase + reg); } -static void matsu_sd_writel(struct matsu_sd_priv *priv, +void matsu_sd_writel(struct matsu_sd_priv *priv, u32 val, unsigned int reg) { if (priv->caps & MATSU_SD_CAP_64BIT) diff --git a/drivers/mmc/matsushita-common.h b/drivers/mmc/matsushita-common.h index b019b72..3be91c3 100644 --- a/drivers/mmc/matsushita-common.h +++ b/drivers/mmc/matsushita-common.h @@ -143,4 +143,8 @@ int matsu_sd_get_cd(struct udevice *dev); int matsu_sd_bind(struct udevice *dev); int matsu_sd_probe(struct udevice *dev, u32 quirks); +u32 matsu_sd_readl(struct matsu_sd_priv *priv, unsigned int reg); +void matsu_sd_writel(struct matsu_sd_priv *priv, + u32 val, unsigned int reg); + #endif /* __MATSUSHITA_COMMON_H__ */ From f63968ba26162c9d6e6cb1138e8e50827c404f3e Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 8 Apr 2018 19:09:17 +0200 Subject: [PATCH 22/36] mmc: renesas-sdhi: Add Renesas SDR104/HS200 tuning support Add code for PHY tuning required for SDR104/HS200 support on Renesas RCar. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/renesas-sdhi.c | 292 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 290 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c index 73f68c8..e9edcf5 100644 --- a/drivers/mmc/renesas-sdhi.c +++ b/drivers/mmc/renesas-sdhi.c @@ -18,10 +18,293 @@ #include "matsushita-common.h" +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) + +/* SCC registers */ +#define RENESAS_SDHI_SCC_DTCNTL 0x800 +#define RENESAS_SDHI_SCC_DTCNTL_TAPEN BIT(0) +#define RENESAS_SDHI_SCC_DTCNTL_TAPNUM_SHIFT 16 +#define RENESAS_SDHI_SCC_DTCNTL_TAPNUM_MASK 0xff +#define RENESAS_SDHI_SCC_TAPSET 0x804 +#define RENESAS_SDHI_SCC_DT2FF 0x808 +#define RENESAS_SDHI_SCC_CKSEL 0x80c +#define RENESAS_SDHI_SCC_CKSEL_DTSEL BIT(0) +#define RENESAS_SDHI_SCC_RVSCNTL 0x810 +#define RENESAS_SDHI_SCC_RVSCNTL_RVSEN BIT(0) +#define RENESAS_SDHI_SCC_RVSREQ 0x814 +#define RENESAS_SDHI_SCC_RVSREQ_RVSERR BIT(2) +#define RENESAS_SDHI_SCC_SMPCMP 0x818 +#define RENESAS_SDHI_SCC_TMPPORT2 0x81c + +#define RENESAS_SDHI_MAX_TAP 3 + +static unsigned int renesas_sdhi_init_tuning(struct matsu_sd_priv *priv) +{ + u32 reg; + + /* Initialize SCC */ + matsu_sd_writel(priv, 0, MATSU_SD_INFO1); + + reg = matsu_sd_readl(priv, MATSU_SD_CLKCTL); + reg &= ~MATSU_SD_CLKCTL_SCLKEN; + matsu_sd_writel(priv, reg, MATSU_SD_CLKCTL); + + /* Set sampling clock selection range */ + matsu_sd_writel(priv, 0x8 << RENESAS_SDHI_SCC_DTCNTL_TAPNUM_SHIFT, + RENESAS_SDHI_SCC_DTCNTL); + + reg = matsu_sd_readl(priv, RENESAS_SDHI_SCC_DTCNTL); + reg |= RENESAS_SDHI_SCC_DTCNTL_TAPEN; + matsu_sd_writel(priv, reg, RENESAS_SDHI_SCC_DTCNTL); + + reg = matsu_sd_readl(priv, RENESAS_SDHI_SCC_CKSEL); + reg |= RENESAS_SDHI_SCC_CKSEL_DTSEL; + matsu_sd_writel(priv, reg, RENESAS_SDHI_SCC_CKSEL); + + reg = matsu_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL); + reg &= ~RENESAS_SDHI_SCC_RVSCNTL_RVSEN; + matsu_sd_writel(priv, reg, RENESAS_SDHI_SCC_RVSCNTL); + + matsu_sd_writel(priv, 0x300 /* scc_tappos */, + RENESAS_SDHI_SCC_DT2FF); + + reg = matsu_sd_readl(priv, MATSU_SD_CLKCTL); + reg |= MATSU_SD_CLKCTL_SCLKEN; + matsu_sd_writel(priv, reg, MATSU_SD_CLKCTL); + + /* Read TAPNUM */ + return (matsu_sd_readl(priv, RENESAS_SDHI_SCC_DTCNTL) >> + RENESAS_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) & + RENESAS_SDHI_SCC_DTCNTL_TAPNUM_MASK; +} + +static void renesas_sdhi_reset_tuning(struct matsu_sd_priv *priv) +{ + u32 reg; + + /* Reset SCC */ + reg = matsu_sd_readl(priv, MATSU_SD_CLKCTL); + reg &= ~MATSU_SD_CLKCTL_SCLKEN; + matsu_sd_writel(priv, reg, MATSU_SD_CLKCTL); + + reg = matsu_sd_readl(priv, RENESAS_SDHI_SCC_CKSEL); + reg &= ~RENESAS_SDHI_SCC_CKSEL_DTSEL; + matsu_sd_writel(priv, reg, RENESAS_SDHI_SCC_CKSEL); + + reg = matsu_sd_readl(priv, MATSU_SD_CLKCTL); + reg |= MATSU_SD_CLKCTL_SCLKEN; + matsu_sd_writel(priv, reg, MATSU_SD_CLKCTL); + + reg = matsu_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL); + reg &= ~RENESAS_SDHI_SCC_RVSCNTL_RVSEN; + matsu_sd_writel(priv, reg, RENESAS_SDHI_SCC_RVSCNTL); + + reg = matsu_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL); + reg &= ~RENESAS_SDHI_SCC_RVSCNTL_RVSEN; + matsu_sd_writel(priv, reg, RENESAS_SDHI_SCC_RVSCNTL); +} + +static void renesas_sdhi_prepare_tuning(struct matsu_sd_priv *priv, + unsigned long tap) +{ + /* Set sampling clock position */ + matsu_sd_writel(priv, tap, RENESAS_SDHI_SCC_TAPSET); +} + +static unsigned int renesas_sdhi_compare_scc_data(struct matsu_sd_priv *priv) +{ + /* Get comparison of sampling data */ + return matsu_sd_readl(priv, RENESAS_SDHI_SCC_SMPCMP); +} + +static int renesas_sdhi_select_tuning(struct matsu_sd_priv *priv, + unsigned int tap_num, unsigned int taps, + unsigned int smpcmp) +{ + unsigned long tap_cnt; /* counter of tuning success */ + unsigned long tap_set; /* tap position */ + unsigned long tap_start;/* start position of tuning success */ + unsigned long tap_end; /* end position of tuning success */ + unsigned long ntap; /* temporary counter of tuning success */ + unsigned long match_cnt;/* counter of matching data */ + unsigned long i; + bool select = false; + u32 reg; + + /* Clear SCC_RVSREQ */ + matsu_sd_writel(priv, 0, RENESAS_SDHI_SCC_RVSREQ); + + /* Merge the results */ + for (i = 0; i < tap_num * 2; i++) { + if (!(taps & BIT(i))) { + taps &= ~BIT(i % tap_num); + taps &= ~BIT((i % tap_num) + tap_num); + } + if (!(smpcmp & BIT(i))) { + smpcmp &= ~BIT(i % tap_num); + smpcmp &= ~BIT((i % tap_num) + tap_num); + } + } + + /* + * Find the longest consecutive run of successful probes. If that + * is more than RENESAS_SDHI_MAX_TAP probes long then use the + * center index as the tap. + */ + tap_cnt = 0; + ntap = 0; + tap_start = 0; + tap_end = 0; + for (i = 0; i < tap_num * 2; i++) { + if (taps & BIT(i)) + ntap++; + else { + if (ntap > tap_cnt) { + tap_start = i - ntap; + tap_end = i - 1; + tap_cnt = ntap; + } + ntap = 0; + } + } + + if (ntap > tap_cnt) { + tap_start = i - ntap; + tap_end = i - 1; + tap_cnt = ntap; + } + + /* + * If all of the TAP is OK, the sampling clock position is selected by + * identifying the change point of data. + */ + if (tap_cnt == tap_num * 2) { + match_cnt = 0; + ntap = 0; + tap_start = 0; + tap_end = 0; + for (i = 0; i < tap_num * 2; i++) { + if (smpcmp & BIT(i)) + ntap++; + else { + if (ntap > match_cnt) { + tap_start = i - ntap; + tap_end = i - 1; + match_cnt = ntap; + } + ntap = 0; + } + } + if (ntap > match_cnt) { + tap_start = i - ntap; + tap_end = i - 1; + match_cnt = ntap; + } + if (match_cnt) + select = true; + } else if (tap_cnt >= RENESAS_SDHI_MAX_TAP) + select = true; + + if (select) + tap_set = ((tap_start + tap_end) / 2) % tap_num; + else + return -EIO; + + /* Set SCC */ + matsu_sd_writel(priv, tap_set, RENESAS_SDHI_SCC_TAPSET); + + /* Enable auto re-tuning */ + reg = matsu_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL); + reg |= RENESAS_SDHI_SCC_RVSCNTL_RVSEN; + matsu_sd_writel(priv, reg, RENESAS_SDHI_SCC_RVSCNTL); + + return 0; +} + +int renesas_sdhi_execute_tuning(struct udevice *dev, uint opcode) +{ + struct matsu_sd_priv *priv = dev_get_priv(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct mmc *mmc = upriv->mmc; + unsigned int tap_num; + unsigned int taps = 0, smpcmp = 0; + int i, ret = 0; + u32 caps; + + /* Only supported on Renesas RCar */ + if (!(priv->caps & MATSU_SD_CAP_RCAR_UHS)) + return -EINVAL; + + /* clock tuning is not needed for upto 52MHz */ + if (!((mmc->selected_mode == MMC_HS_200) || + (mmc->selected_mode == UHS_SDR104) || + (mmc->selected_mode == UHS_SDR50))) + return 0; + + tap_num = renesas_sdhi_init_tuning(priv); + if (!tap_num) + /* Tuning is not supported */ + goto out; + + if (tap_num * 2 >= sizeof(taps) * 8) { + dev_err(dev, + "Too many taps, skipping tuning. Please consider updating size of taps field of tmio_mmc_host\n"); + goto out; + } + + /* Issue CMD19 twice for each tap */ + for (i = 0; i < 2 * tap_num; i++) { + renesas_sdhi_prepare_tuning(priv, i % tap_num); + + /* Force PIO for the tuning */ + caps = priv->caps; + priv->caps &= ~MATSU_SD_CAP_DMA_INTERNAL; + + ret = mmc_send_tuning(mmc, opcode, NULL); + + priv->caps = caps; + + if (ret == 0) + taps |= BIT(i); + + ret = renesas_sdhi_compare_scc_data(priv); + if (ret == 0) + smpcmp |= BIT(i); + + mdelay(1); + } + + ret = renesas_sdhi_select_tuning(priv, tap_num, taps, smpcmp); + +out: + if (ret < 0) { + dev_warn(dev, "Tuning procedure failed\n"); + renesas_sdhi_reset_tuning(priv); + } + + return ret; +} +#endif + +static int renesas_sdhi_set_ios(struct udevice *dev) +{ + int ret = matsu_sd_set_ios(dev); +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) + struct matsu_sd_priv *priv = dev_get_priv(dev); + + renesas_sdhi_reset_tuning(priv); +#endif + + return ret; +} + static const struct dm_mmc_ops renesas_sdhi_ops = { .send_cmd = matsu_sd_send_cmd, - .set_ios = matsu_sd_set_ios, + .set_ios = renesas_sdhi_set_ios, .get_cd = matsu_sd_get_cd, +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) + .execute_tuning = renesas_sdhi_execute_tuning, +#endif }; #define RENESAS_GEN2_QUIRKS MATSU_SD_CAP_RCAR_GEN2 @@ -62,7 +345,12 @@ static int renesas_sdhi_probe(struct udevice *dev) quirks |= MATSU_SD_CAP_16BIT; } - return matsu_sd_probe(dev, quirks); + ret = matsu_sd_probe(dev, quirks); +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) + if (!ret) + renesas_sdhi_reset_tuning(dev_get_priv(dev)); +#endif + return ret; } U_BOOT_DRIVER(renesas_sdhi) = { From 8af6737d4572e6d23e43da500b93179ac2640e9c Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 22 Dec 2017 09:37:04 +0100 Subject: [PATCH 23/36] ARM: rmobile: Enable HS200 mode on RCar Gen3 Enable the HS200 on RCar Gen3 platforms, since the SDHI core supports it. Signed-off-by: Marek Vasut Cc: Nobuhiro Iwamatsu --- configs/r8a7795_salvator-x_defconfig | 4 ++++ configs/r8a7795_ulcb_defconfig | 4 ++++ configs/r8a7796_salvator-x_defconfig | 4 ++++ configs/r8a7796_ulcb_defconfig | 4 ++++ configs/r8a77970_eagle_defconfig | 4 ++++ configs/r8a77995_draak_defconfig | 4 ++++ 6 files changed, 24 insertions(+) diff --git a/configs/r8a7795_salvator-x_defconfig b/configs/r8a7795_salvator-x_defconfig index 0614894..b48513c 100644 --- a/configs/r8a7795_salvator-x_defconfig +++ b/configs/r8a7795_salvator-x_defconfig @@ -37,7 +37,11 @@ CONFIG_RCAR_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_RCAR_IIC=y CONFIG_DM_MMC=y +CONFIG_MMC_IO_VOLTAGE=y +CONFIG_MMC_UHS_SUPPORT=y +CONFIG_MMC_HS200_SUPPORT=y CONFIG_RENESAS_SDHI=y +CONFIG_MMC_RENESAS_TUNING=y CONFIG_PHY_MICREL=y CONFIG_PHY_MICREL_KSZ90X1=y CONFIG_DM_ETH=y diff --git a/configs/r8a7795_ulcb_defconfig b/configs/r8a7795_ulcb_defconfig index cffa719..a45aec3 100644 --- a/configs/r8a7795_ulcb_defconfig +++ b/configs/r8a7795_ulcb_defconfig @@ -37,7 +37,11 @@ CONFIG_RCAR_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_RCAR_IIC=y CONFIG_DM_MMC=y +CONFIG_MMC_IO_VOLTAGE=y +CONFIG_MMC_UHS_SUPPORT=y +CONFIG_MMC_HS200_SUPPORT=y CONFIG_RENESAS_SDHI=y +CONFIG_MMC_RENESAS_TUNING=y CONFIG_PHY_MICREL=y CONFIG_PHY_MICREL_KSZ90X1=y CONFIG_DM_ETH=y diff --git a/configs/r8a7796_salvator-x_defconfig b/configs/r8a7796_salvator-x_defconfig index 535163f..680c7d9 100644 --- a/configs/r8a7796_salvator-x_defconfig +++ b/configs/r8a7796_salvator-x_defconfig @@ -38,7 +38,11 @@ CONFIG_RCAR_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_RCAR_IIC=y CONFIG_DM_MMC=y +CONFIG_MMC_IO_VOLTAGE=y +CONFIG_MMC_UHS_SUPPORT=y +CONFIG_MMC_HS200_SUPPORT=y CONFIG_RENESAS_SDHI=y +CONFIG_MMC_RENESAS_TUNING=y CONFIG_PHY_MICREL=y CONFIG_PHY_MICREL_KSZ90X1=y CONFIG_DM_ETH=y diff --git a/configs/r8a7796_ulcb_defconfig b/configs/r8a7796_ulcb_defconfig index 45c0ca2..2b552cc 100644 --- a/configs/r8a7796_ulcb_defconfig +++ b/configs/r8a7796_ulcb_defconfig @@ -38,7 +38,11 @@ CONFIG_RCAR_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_RCAR_IIC=y CONFIG_DM_MMC=y +CONFIG_MMC_IO_VOLTAGE=y +CONFIG_MMC_UHS_SUPPORT=y +CONFIG_MMC_HS200_SUPPORT=y CONFIG_RENESAS_SDHI=y +CONFIG_MMC_RENESAS_TUNING=y CONFIG_PHY_MICREL=y CONFIG_PHY_MICREL_KSZ90X1=y CONFIG_DM_ETH=y diff --git a/configs/r8a77970_eagle_defconfig b/configs/r8a77970_eagle_defconfig index 2e7f7ac..56c949f 100644 --- a/configs/r8a77970_eagle_defconfig +++ b/configs/r8a77970_eagle_defconfig @@ -37,7 +37,11 @@ CONFIG_RCAR_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_RCAR_IIC=y CONFIG_DM_MMC=y +CONFIG_MMC_IO_VOLTAGE=y +CONFIG_MMC_UHS_SUPPORT=y +CONFIG_MMC_HS200_SUPPORT=y CONFIG_RENESAS_SDHI=y +CONFIG_MMC_RENESAS_TUNING=y CONFIG_PHY_MICREL=y CONFIG_PHY_MICREL_KSZ90X1=y CONFIG_DM_ETH=y diff --git a/configs/r8a77995_draak_defconfig b/configs/r8a77995_draak_defconfig index 7a2e33e..9792f5e 100644 --- a/configs/r8a77995_draak_defconfig +++ b/configs/r8a77995_draak_defconfig @@ -38,7 +38,11 @@ CONFIG_RCAR_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_RCAR_IIC=y CONFIG_DM_MMC=y +CONFIG_MMC_IO_VOLTAGE=y +CONFIG_MMC_UHS_SUPPORT=y +CONFIG_MMC_HS200_SUPPORT=y CONFIG_RENESAS_SDHI=y +CONFIG_MMC_RENESAS_TUNING=y CONFIG_MTD=y CONFIG_MTD_NOR_FLASH=y CONFIG_CFI_FLASH=y From 01c0151a36565c653043d08a826d62d5744e511a Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 9 Apr 2018 20:47:31 +0200 Subject: [PATCH 24/36] mmc: matsushita-common: Special case only select registers in 16bit There are only a few registerse used in the 16bit mode which are 32bit internally. Special-case only those in the IO accessors and always write both halves. Any other register access is protected from accidentally overwriting neighboring register. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/matsushita-common.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/matsushita-common.c b/drivers/mmc/matsushita-common.c index 3f538c3..7395865 100644 --- a/drivers/mmc/matsushita-common.c +++ b/drivers/mmc/matsushita-common.c @@ -68,7 +68,9 @@ void matsu_sd_writel(struct matsu_sd_priv *priv, writel(val, priv->regbase + (reg << 1)); if (priv->caps & MATSU_SD_CAP_16BIT) { writew(val & 0xffff, priv->regbase + (reg >> 1)); - if (val >> 16) + if (reg == MATSU_SD_INFO1 || reg == MATSU_SD_INFO1_MASK || + reg == MATSU_SD_INFO2 || reg == MATSU_SD_INFO2_MASK || + reg == MATSU_SD_ARG) writew(val >> 16, priv->regbase + (reg >> 1) + 2); } else writel(val, priv->regbase + reg); From 8dc9a10e49381e1d5d7329d123656f19fd7f8131 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 9 Apr 2018 20:47:31 +0200 Subject: [PATCH 25/36] mmc: matsushita-common: Correctly set mode in 16bit The HOST_MODE register must be set to 0 when the IP is operated in 16bit mode, otherwise 16bit access to the data FIFO may fail. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/matsushita-common.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/mmc/matsushita-common.c b/drivers/mmc/matsushita-common.c index 7395865..e552a09 100644 --- a/drivers/mmc/matsushita-common.c +++ b/drivers/mmc/matsushita-common.c @@ -688,8 +688,6 @@ static void matsu_sd_host_init(struct matsu_sd_priv *priv) */ if (priv->version >= 0x10) matsu_sd_writel(priv, 0x101, MATSU_SD_HOST_MODE); - else if (priv->caps & MATSU_SD_CAP_16BIT) - matsu_sd_writel(priv, 0x1, MATSU_SD_HOST_MODE); else matsu_sd_writel(priv, 0x0, MATSU_SD_HOST_MODE); From f23b208ebe6a8e241b3801ccfbb2c49e680fad9b Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 9 Apr 2018 20:47:31 +0200 Subject: [PATCH 26/36] mmc: matsushita-common: Wait for command completion Make sure to wait for the command to complete altogether, including the trailing 8 clock cycles. This prevents the driver for accidentally writing the CMD register too fast before the previous command fully completed. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/matsushita-common.c | 2 ++ drivers/mmc/matsushita-common.h | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/mmc/matsushita-common.c b/drivers/mmc/matsushita-common.c index e552a09..33224bb 100644 --- a/drivers/mmc/matsushita-common.c +++ b/drivers/mmc/matsushita-common.c @@ -498,6 +498,8 @@ int matsu_sd_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, return ret; } + matsu_sd_wait_for_irq(dev, MATSU_SD_INFO2, MATSU_SD_INFO2_SCLKDIVEN); + return ret; } diff --git a/drivers/mmc/matsushita-common.h b/drivers/mmc/matsushita-common.h index 3be91c3..34631cb 100644 --- a/drivers/mmc/matsushita-common.h +++ b/drivers/mmc/matsushita-common.h @@ -38,6 +38,7 @@ #define MATSU_SD_INFO2 0x03c /* IRQ status 2 */ #define MATSU_SD_INFO2_ERR_ILA BIT(15) /* illegal access err */ #define MATSU_SD_INFO2_CBSY BIT(14) /* command busy */ +#define MATSU_SD_INFO2_SCLKDIVEN BIT(13) /* command setting reg ena */ #define MATSU_SD_INFO2_BWE BIT(9) /* write buffer ready */ #define MATSU_SD_INFO2_BRE BIT(8) /* read buffer ready */ #define MATSU_SD_INFO2_DAT0 BIT(7) /* SDDAT0 */ From 5ee7c9dc7777f9a0f8bb993ed36b48d4bf19f3d9 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 10 Apr 2018 01:44:23 +0200 Subject: [PATCH 27/36] mmc: matsushita-common: Add missing else Fix minor rebase omission, the else was missing which triggered two accesses to the register on 64bit variant of the IP. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/matsushita-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/matsushita-common.c b/drivers/mmc/matsushita-common.c index 33224bb..e22a9de 100644 --- a/drivers/mmc/matsushita-common.c +++ b/drivers/mmc/matsushita-common.c @@ -66,7 +66,7 @@ void matsu_sd_writel(struct matsu_sd_priv *priv, { if (priv->caps & MATSU_SD_CAP_64BIT) writel(val, priv->regbase + (reg << 1)); - if (priv->caps & MATSU_SD_CAP_16BIT) { + else if (priv->caps & MATSU_SD_CAP_16BIT) { writew(val & 0xffff, priv->regbase + (reg >> 1)); if (reg == MATSU_SD_INFO1 || reg == MATSU_SD_INFO1_MASK || reg == MATSU_SD_INFO2 || reg == MATSU_SD_INFO2_MASK || From cf39f3f304830b1e76b1a4af8d135ed9d26bb134 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 9 Apr 2018 20:47:31 +0200 Subject: [PATCH 28/36] mmc: renesas-sdhi: Wait after reconfiguring pins The IP requires some time to recuperate after the IO pin properties were changed. Add a delay to assure this. Signed-off-by: Marek Vasut Cc: Jaehoon Chung Cc: Masahiro Yamada --- drivers/mmc/renesas-sdhi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c index e9edcf5..8564f42 100644 --- a/drivers/mmc/renesas-sdhi.c +++ b/drivers/mmc/renesas-sdhi.c @@ -289,6 +289,9 @@ out: static int renesas_sdhi_set_ios(struct udevice *dev) { int ret = matsu_sd_set_ios(dev); + + mdelay(10); + #if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) struct matsu_sd_priv *priv = dev_get_priv(dev); From a405a55ba8daf70311a1831938bbdc1ed92c5a83 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 19 Aug 2017 23:24:08 +0200 Subject: [PATCH 29/36] mtd: rpc: Add Renesas RPC Hyperflash driver Add driver for the RPC block in Hyperflash mode. This driver allows access to a CFI Hyperflash attached to the RPC block and does not support RPC in SPI mode. Signed-off-by: Marek Vasut Cc: Nobuhiro Iwamatsu --- drivers/mtd/Kconfig | 7 + drivers/mtd/Makefile | 1 + drivers/mtd/renesas_rpc_hf.c | 398 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 406 insertions(+) create mode 100644 drivers/mtd/renesas_rpc_hf.c diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 1957980..707359d 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -40,6 +40,13 @@ config FLASH_PIC32 This enables access to Microchip PIC32 internal non-CFI flash chips through PIC32 Non-Volatile-Memory Controller. +config RENESAS_RPC_HF + bool "Renesas RCar Gen3 RPC Hyperflash driver" + depends on RCAR_GEN3 && MTD + help + This enables access to Hyperflash memory through the Renesas + RCar Gen3 RPC controller. + endmenu source "drivers/mtd/nand/Kconfig" diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 20c0d0a..e46cbd8 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -20,3 +20,4 @@ obj-$(CONFIG_MW_EEPROM) += mw_eeprom.o obj-$(CONFIG_FLASH_PIC32) += pic32_flash.o obj-$(CONFIG_ST_SMI) += st_smi.o obj-$(CONFIG_STM32_FLASH) += stm32_flash.o +obj-$(CONFIG_RENESAS_RPC_HF) += renesas_rpc_hf.o diff --git a/drivers/mtd/renesas_rpc_hf.c b/drivers/mtd/renesas_rpc_hf.c new file mode 100644 index 0000000..1ba6e35 --- /dev/null +++ b/drivers/mtd/renesas_rpc_hf.c @@ -0,0 +1,398 @@ +/* + * Renesas RCar Gen3 RPC Hyperflash driver + * + * Copyright (C) 2016 Renesas Electronics Corporation + * Copyright (C) 2016 Cogent Embedded, Inc. + * Copyright (C) 2017 Marek Vasut + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RPC_CMNCR 0x0000 /* R/W */ +#define RPC_CMNCR_MD BIT(31) +#define RPC_CMNCR_MOIIO0(val) (((val) & 0x3) << 16) +#define RPC_CMNCR_MOIIO1(val) (((val) & 0x3) << 18) +#define RPC_CMNCR_MOIIO2(val) (((val) & 0x3) << 20) +#define RPC_CMNCR_MOIIO3(val) (((val) & 0x3) << 22) +#define RPC_CMNCR_MOIIO_HIZ (RPC_CMNCR_MOIIO0(3) | RPC_CMNCR_MOIIO1(3) | \ + RPC_CMNCR_MOIIO2(3) | RPC_CMNCR_MOIIO3(3)) +#define RPC_CMNCR_IO0FV(val) (((val) & 0x3) << 8) +#define RPC_CMNCR_IO2FV(val) (((val) & 0x3) << 12) +#define RPC_CMNCR_IO3FV(val) (((val) & 0x3) << 14) +#define RPC_CMNCR_IOFV_HIZ (RPC_CMNCR_IO0FV(3) | RPC_CMNCR_IO2FV(3) | \ + RPC_CMNCR_IO3FV(3)) +#define RPC_CMNCR_BSZ(val) (((val) & 0x3) << 0) + +#define RPC_SSLDR 0x0004 /* R/W */ +#define RPC_SSLDR_SPNDL(d) (((d) & 0x7) << 16) +#define RPC_SSLDR_SLNDL(d) (((d) & 0x7) << 8) +#define RPC_SSLDR_SCKDL(d) (((d) & 0x7) << 0) + +#define RPC_DRCR 0x000C /* R/W */ +#define RPC_DRCR_SSLN BIT(24) +#define RPC_DRCR_RBURST(v) (((v) & 0x1F) << 16) +#define RPC_DRCR_RCF BIT(9) +#define RPC_DRCR_RBE BIT(8) +#define RPC_DRCR_SSLE BIT(0) + +#define RPC_DRCMR 0x0010 /* R/W */ +#define RPC_DRCMR_CMD(c) (((c) & 0xFF) << 16) +#define RPC_DRCMR_OCMD(c) (((c) & 0xFF) << 0) + +#define RPC_DREAR 0x0014 /* R/W */ +#define RPC_DREAR_EAV(v) (((v) & 0xFF) << 16) +#define RPC_DREAR_EAC(v) (((v) & 0x7) << 0) + +#define RPC_DROPR 0x0018 /* R/W */ +#define RPC_DROPR_OPD3(o) (((o) & 0xFF) << 24) +#define RPC_DROPR_OPD2(o) (((o) & 0xFF) << 16) +#define RPC_DROPR_OPD1(o) (((o) & 0xFF) << 8) +#define RPC_DROPR_OPD0(o) (((o) & 0xFF) << 0) + +#define RPC_DRENR 0x001C /* R/W */ +#define RPC_DRENR_CDB(o) (u32)((((o) & 0x3) << 30)) +#define RPC_DRENR_OCDB(o) (((o) & 0x3) << 28) +#define RPC_DRENR_ADB(o) (((o) & 0x3) << 24) +#define RPC_DRENR_OPDB(o) (((o) & 0x3) << 20) +#define RPC_DRENR_SPIDB(o) (((o) & 0x3) << 16) +#define RPC_DRENR_DME BIT(15) +#define RPC_DRENR_CDE BIT(14) +#define RPC_DRENR_OCDE BIT(12) +#define RPC_DRENR_ADE(v) (((v) & 0xF) << 8) +#define RPC_DRENR_OPDE(v) (((v) & 0xF) << 4) + +#define RPC_SMCR 0x0020 /* R/W */ +#define RPC_SMCR_SSLKP BIT(8) +#define RPC_SMCR_SPIRE BIT(2) +#define RPC_SMCR_SPIWE BIT(1) +#define RPC_SMCR_SPIE BIT(0) + +#define RPC_SMCMR 0x0024 /* R/W */ +#define RPC_SMCMR_CMD(c) (((c) & 0xFF) << 16) +#define RPC_SMCMR_OCMD(c) (((c) & 0xFF) << 0) + +#define RPC_SMADR 0x0028 /* R/W */ +#define RPC_SMOPR 0x002C /* R/W */ +#define RPC_SMOPR_OPD0(o) (((o) & 0xFF) << 0) +#define RPC_SMOPR_OPD1(o) (((o) & 0xFF) << 8) +#define RPC_SMOPR_OPD2(o) (((o) & 0xFF) << 16) +#define RPC_SMOPR_OPD3(o) (((o) & 0xFF) << 24) + +#define RPC_SMENR 0x0030 /* R/W */ +#define RPC_SMENR_CDB(o) (((o) & 0x3) << 30) +#define RPC_SMENR_OCDB(o) (((o) & 0x3) << 28) +#define RPC_SMENR_ADB(o) (((o) & 0x3) << 24) +#define RPC_SMENR_OPDB(o) (((o) & 0x3) << 20) +#define RPC_SMENR_SPIDB(o) (((o) & 0x3) << 16) +#define RPC_SMENR_DME BIT(15) +#define RPC_SMENR_CDE BIT(14) +#define RPC_SMENR_OCDE BIT(12) +#define RPC_SMENR_ADE(v) (((v) & 0xF) << 8) +#define RPC_SMENR_OPDE(v) (((v) & 0xF) << 4) +#define RPC_SMENR_SPIDE(v) (((v) & 0xF) << 0) + +#define RPC_SMRDR0 0x0038 /* R */ +#define RPC_SMRDR1 0x003C /* R */ +#define RPC_SMWDR0 0x0040 /* R/W */ +#define RPC_SMWDR1 0x0044 /* R/W */ +#define RPC_CMNSR 0x0048 /* R */ +#define RPC_CMNSR_SSLF BIT(1) +#define RPC_CMNSR_TEND BIT(0) + +#define RPC_DRDMCR 0x0058 /* R/W */ +#define RPC_DRDMCR_DMCYC(v) (((v) & 0xF) << 0) + +#define RPC_DRDRENR 0x005C /* R/W */ +#define RPC_DRDRENR_HYPE (0x5 << 12) +#define RPC_DRDRENR_ADDRE BIT(8) +#define RPC_DRDRENR_OPDRE BIT(4) +#define RPC_DRDRENR_DRDRE BIT(0) + +#define RPC_SMDMCR 0x0060 /* R/W */ +#define RPC_SMDMCR_DMCYC(v) (((v) & 0xF) << 0) + +#define RPC_SMDRENR 0x0064 /* R/W */ +#define RPC_SMDRENR_HYPE (0x5 << 12) +#define RPC_SMDRENR_ADDRE BIT(8) +#define RPC_SMDRENR_OPDRE BIT(4) +#define RPC_SMDRENR_SPIDRE BIT(0) + +#define RPC_PHYCNT 0x007C /* R/W */ +#define RPC_PHYCNT_CAL BIT(31) +#define PRC_PHYCNT_OCTA_AA BIT(22) +#define PRC_PHYCNT_OCTA_SA BIT(23) +#define PRC_PHYCNT_EXDS BIT(21) +#define RPC_PHYCNT_OCT BIT(20) +#define RPC_PHYCNT_WBUF2 BIT(4) +#define RPC_PHYCNT_WBUF BIT(2) +#define RPC_PHYCNT_MEM(v) (((v) & 0x3) << 0) + +#define RPC_PHYINT 0x0088 /* R/W */ +#define RPC_PHYINT_RSTEN BIT(18) +#define RPC_PHYINT_WPEN BIT(17) +#define RPC_PHYINT_INTEN BIT(16) +#define RPC_PHYINT_RST BIT(2) +#define RPC_PHYINT_WP BIT(1) +#define RPC_PHYINT_INT BIT(0) + +#define RPC_WBUF 0x8000 /* R/W size=4/8/16/32/64Bytes */ +#define RPC_WBUF_SIZE 0x100 + +static phys_addr_t rpc_base; + +enum rpc_hf_size { + RPC_HF_SIZE_16BIT = RPC_SMENR_SPIDE(0x8), + RPC_HF_SIZE_32BIT = RPC_SMENR_SPIDE(0xC), + RPC_HF_SIZE_64BIT = RPC_SMENR_SPIDE(0xF), +}; + +static int rpc_hf_wait_tend(void) +{ + void __iomem *reg = (void __iomem *)rpc_base + RPC_CMNSR; + return wait_for_bit_le32(reg, RPC_CMNSR_TEND, true, 1000, 0); +} + +static int rpc_hf_mode(bool man) +{ + int ret; + + ret = rpc_hf_wait_tend(); + if (ret) + return ret; + + clrsetbits_le32(rpc_base + RPC_PHYCNT, + RPC_PHYCNT_WBUF | RPC_PHYCNT_WBUF2 | + RPC_PHYCNT_CAL | RPC_PHYCNT_MEM(3), + RPC_PHYCNT_CAL | RPC_PHYCNT_MEM(3)); + + clrsetbits_le32(rpc_base + RPC_CMNCR, + RPC_CMNCR_MD | RPC_CMNCR_BSZ(3), + RPC_CMNCR_MOIIO_HIZ | RPC_CMNCR_IOFV_HIZ | + (man ? RPC_CMNCR_MD : 0) | RPC_CMNCR_BSZ(1)); + + if (man) + return 0; + + writel(RPC_DRCR_RBURST(0x1F) | RPC_DRCR_RCF | RPC_DRCR_RBE, + rpc_base + RPC_DRCR); + + writel(RPC_DRCMR_CMD(0xA0), rpc_base + RPC_DRCMR); + writel(RPC_DRENR_CDB(2) | RPC_DRENR_OCDB(2) | RPC_DRENR_ADB(2) | + RPC_DRENR_SPIDB(2) | RPC_DRENR_CDE | RPC_DRENR_OCDE | + RPC_DRENR_ADE(4), rpc_base + RPC_DRENR); + writel(RPC_DRDMCR_DMCYC(0xE), rpc_base + RPC_DRDMCR); + writel(RPC_DRDRENR_HYPE | RPC_DRDRENR_ADDRE | RPC_DRDRENR_DRDRE, + rpc_base + RPC_DRDRENR); + + /* Dummy read */ + readl(rpc_base + RPC_DRCR); + + return 0; +} + +static int rpc_hf_xfer(void *addr, u64 wdata, u64 *rdata, + enum rpc_hf_size size, bool write) +{ + int ret; + u32 val; + + ret = rpc_hf_mode(1); + if (ret) + return ret; + + /* Submit HF address, SMCMR CMD[7] ~= CA Bit# 47 (R/nW) */ + writel(write ? 0 : RPC_SMCMR_CMD(0x80), rpc_base + RPC_SMCMR); + writel((uintptr_t)addr >> 1, rpc_base + RPC_SMADR); + writel(0x0, rpc_base + RPC_SMOPR); + + writel(RPC_SMDRENR_HYPE | RPC_SMDRENR_ADDRE | RPC_SMDRENR_SPIDRE, + rpc_base + RPC_SMDRENR); + + val = RPC_SMENR_CDB(2) | RPC_SMENR_OCDB(2) | + RPC_SMENR_ADB(2) | RPC_SMENR_SPIDB(2) | + RPC_SMENR_CDE | RPC_SMENR_OCDE | RPC_SMENR_ADE(4) | size; + + if (write) { + writel(val, rpc_base + RPC_SMENR); + + if (size == RPC_HF_SIZE_64BIT) + writeq(cpu_to_be64(wdata), rpc_base + RPC_SMWDR0); + else + writel(cpu_to_be32(wdata), rpc_base + RPC_SMWDR0); + + writel(RPC_SMCR_SPIWE | RPC_SMCR_SPIE, rpc_base + RPC_SMCR); + } else { + val |= RPC_SMENR_DME; + + writel(RPC_SMDMCR_DMCYC(0xE), rpc_base + RPC_SMDMCR); + + writel(val, rpc_base + RPC_SMENR); + + writel(RPC_SMCR_SPIRE | RPC_SMCR_SPIE, rpc_base + RPC_SMCR); + + ret = rpc_hf_wait_tend(); + if (ret) + return ret; + + if (size == RPC_HF_SIZE_64BIT) + *rdata = be64_to_cpu(readq(rpc_base + RPC_SMRDR0)); + else + *rdata = be32_to_cpu(readl(rpc_base + RPC_SMRDR0)); + } + + return rpc_hf_mode(0); +} + +static void rpc_hf_write_cmd(void *addr, u64 wdata, enum rpc_hf_size size) +{ + int ret; + + ret = rpc_hf_xfer(addr, wdata, NULL, size, 1); + if (ret) + printf("RPC: Write failed, ret=%i\n", ret); +} + +static u64 rpc_hf_read_reg(void *addr, enum rpc_hf_size size) +{ + u64 rdata = 0; + int ret; + + ret = rpc_hf_xfer(addr, 0, &rdata, size, 0); + if (ret) + printf("RPC: Read failed, ret=%i\n", ret); + + return rdata; +} + +void flash_write8(u8 value, void *addr) +{ + rpc_hf_write_cmd(addr, value, RPC_HF_SIZE_16BIT); +} + +void flash_write16(u16 value, void *addr) +{ + rpc_hf_write_cmd(addr, value, RPC_HF_SIZE_16BIT); +} + +void flash_write32(u32 value, void *addr) +{ + rpc_hf_write_cmd(addr, value, RPC_HF_SIZE_32BIT); +} + +void flash_write64(u64 value, void *addr) +{ + rpc_hf_write_cmd(addr, value, RPC_HF_SIZE_64BIT); +} + +u8 flash_read8(void *addr) +{ + return rpc_hf_read_reg(addr, RPC_HF_SIZE_16BIT); +} + +u16 flash_read16(void *addr) +{ + return rpc_hf_read_reg(addr, RPC_HF_SIZE_16BIT); +} + +u32 flash_read32(void *addr) +{ + return rpc_hf_read_reg(addr, RPC_HF_SIZE_32BIT); +} + +u64 flash_read64(void *addr) +{ + return rpc_hf_read_reg(addr, RPC_HF_SIZE_64BIT); +} + +static int rpc_hf_bind(struct udevice *parent) +{ + const void *fdt = gd->fdt_blob; + ofnode node; + int ret, off; + + /* + * Check if there are any SPI NOR child nodes, if so, do NOT bind + * as this controller will be operated by the QSPI driver instead. + */ + dev_for_each_subnode(node, parent) { + off = ofnode_to_offset(node); + + ret = fdt_node_check_compatible(fdt, off, "spi-flash"); + if (!ret) + return -ENODEV; + + ret = fdt_node_check_compatible(fdt, off, "jedec,spi-nor"); + if (!ret) + return -ENODEV; + } + + return 0; +} + +static int rpc_hf_probe(struct udevice *dev) +{ + void *blob = (void *)gd->fdt_blob; + const fdt32_t *cell; + int node = dev_of_offset(dev); + int parent, addrc, sizec, len, ret; + struct clk clk; + phys_addr_t flash_base; + + parent = fdt_parent_offset(blob, node); + fdt_support_default_count_cells(blob, parent, &addrc, &sizec); + cell = fdt_getprop(blob, node, "reg", &len); + if (!cell) + return -ENOENT; + + if (addrc != 2 || sizec != 2) + return -EINVAL; + + + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) { + dev_err(dev, "Failed to get RPC clock\n"); + return ret; + } + + ret = clk_enable(&clk); + clk_free(&clk); + if (ret) { + dev_err(dev, "Failed to enable RPC clock\n"); + return ret; + } + + rpc_base = fdt_translate_address(blob, node, cell); + flash_base = fdt_translate_address(blob, node, cell + addrc + sizec); + + flash_info[0].dev = dev; + flash_info[0].base = flash_base; + cfi_flash_num_flash_banks = 1; + gd->bd->bi_flashstart = flash_base; + + return 0; +} + +static const struct udevice_id rpc_hf_ids[] = { + { .compatible = "renesas,rpc" }, + {} +}; + +U_BOOT_DRIVER(rpc_hf) = { + .name = "rpc_hf", + .id = UCLASS_MTD, + .of_match = rpc_hf_ids, + .bind = rpc_hf_bind, + .probe = rpc_hf_probe, +}; From fbebea27e16eb760f1f2109a7535003bf359ba2b Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 29 Nov 2017 06:29:46 +0100 Subject: [PATCH 30/36] mtd: spi: Add Renesas RPC SPI-flash driver Add driver for the RPC block in SPI-flash mode. This driver allows access to a SPI NOR flash attached to the RPC block and does not support RPC in Hyperflash mode. Note that this block is extremely selective when communicating with the SPI NOR. Signed-off-by: Marek Vasut Cc: Nobuhiro Iwamatsu --- drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/renesas_rpc_spi.c | 465 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 474 insertions(+) create mode 100644 drivers/spi/renesas_rpc_spi.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index d3e407e..ec92b84 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -114,6 +114,14 @@ config PIC32_SPI to access the SPI NOR flash, MMC-over-SPI on platforms based on Microchip PIC32 family devices. +config RENESAS_RPC_SPI + bool "Renesas RPC SPI driver" + depends on RCAR_GEN3 + help + Enable the Renesas RPC SPI driver, used to access SPI NOR flash + on Renesas RCar Gen3 SoCs. This uses driver model and requires a + device tree binding to operate. + config ROCKCHIP_SPI bool "Rockchip SPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 4b6000f..176bfa0 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_MXS_SPI) += mxs_spi.o obj-$(CONFIG_ATCSPI200_SPI) += atcspi200_spi.o obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o obj-$(CONFIG_PIC32_SPI) += pic32_spi.o +obj-$(CONFIG_RENESAS_RPC_SPI) += renesas_rpc_spi.o obj-$(CONFIG_ROCKCHIP_SPI) += rk_spi.o obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o obj-$(CONFIG_SH_SPI) += sh_spi.o diff --git a/drivers/spi/renesas_rpc_spi.c b/drivers/spi/renesas_rpc_spi.c new file mode 100644 index 0000000..e54f24c --- /dev/null +++ b/drivers/spi/renesas_rpc_spi.c @@ -0,0 +1,465 @@ +/* + * Renesas RCar Gen3 RPC QSPI driver + * + * Copyright (C) 2018 Marek Vasut + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RPC_CMNCR 0x0000 /* R/W */ +#define RPC_CMNCR_MD BIT(31) +#define RPC_CMNCR_SFDE BIT(24) +#define RPC_CMNCR_MOIIO3(val) (((val) & 0x3) << 22) +#define RPC_CMNCR_MOIIO2(val) (((val) & 0x3) << 20) +#define RPC_CMNCR_MOIIO1(val) (((val) & 0x3) << 18) +#define RPC_CMNCR_MOIIO0(val) (((val) & 0x3) << 16) +#define RPC_CMNCR_MOIIO_HIZ (RPC_CMNCR_MOIIO0(3) | RPC_CMNCR_MOIIO1(3) | \ + RPC_CMNCR_MOIIO2(3) | RPC_CMNCR_MOIIO3(3)) +#define RPC_CMNCR_IO3FV(val) (((val) & 0x3) << 14) +#define RPC_CMNCR_IO2FV(val) (((val) & 0x3) << 12) +#define RPC_CMNCR_IO0FV(val) (((val) & 0x3) << 8) +#define RPC_CMNCR_IOFV_HIZ (RPC_CMNCR_IO0FV(3) | RPC_CMNCR_IO2FV(3) | \ + RPC_CMNCR_IO3FV(3)) +#define RPC_CMNCR_CPHAT BIT(6) +#define RPC_CMNCR_CPHAR BIT(5) +#define RPC_CMNCR_SSLP BIT(4) +#define RPC_CMNCR_CPOL BIT(3) +#define RPC_CMNCR_BSZ(val) (((val) & 0x3) << 0) + +#define RPC_SSLDR 0x0004 /* R/W */ +#define RPC_SSLDR_SPNDL(d) (((d) & 0x7) << 16) +#define RPC_SSLDR_SLNDL(d) (((d) & 0x7) << 8) +#define RPC_SSLDR_SCKDL(d) (((d) & 0x7) << 0) + +#define RPC_DRCR 0x000C /* R/W */ +#define RPC_DRCR_SSLN BIT(24) +#define RPC_DRCR_RBURST(v) (((v) & 0x1F) << 16) +#define RPC_DRCR_RCF BIT(9) +#define RPC_DRCR_RBE BIT(8) +#define RPC_DRCR_SSLE BIT(0) + +#define RPC_DRCMR 0x0010 /* R/W */ +#define RPC_DRCMR_CMD(c) (((c) & 0xFF) << 16) +#define RPC_DRCMR_OCMD(c) (((c) & 0xFF) << 0) + +#define RPC_DREAR 0x0014 /* R/W */ +#define RPC_DREAR_EAV(v) (((v) & 0xFF) << 16) +#define RPC_DREAR_EAC(v) (((v) & 0x7) << 0) + +#define RPC_DROPR 0x0018 /* R/W */ +#define RPC_DROPR_OPD3(o) (((o) & 0xFF) << 24) +#define RPC_DROPR_OPD2(o) (((o) & 0xFF) << 16) +#define RPC_DROPR_OPD1(o) (((o) & 0xFF) << 8) +#define RPC_DROPR_OPD0(o) (((o) & 0xFF) << 0) + +#define RPC_DRENR 0x001C /* R/W */ +#define RPC_DRENR_CDB(o) (u32)((((o) & 0x3) << 30)) +#define RPC_DRENR_OCDB(o) (((o) & 0x3) << 28) +#define RPC_DRENR_ADB(o) (((o) & 0x3) << 24) +#define RPC_DRENR_OPDB(o) (((o) & 0x3) << 20) +#define RPC_DRENR_SPIDB(o) (((o) & 0x3) << 16) +#define RPC_DRENR_DME BIT(15) +#define RPC_DRENR_CDE BIT(14) +#define RPC_DRENR_OCDE BIT(12) +#define RPC_DRENR_ADE(v) (((v) & 0xF) << 8) +#define RPC_DRENR_OPDE(v) (((v) & 0xF) << 4) + +#define RPC_SMCR 0x0020 /* R/W */ +#define RPC_SMCR_SSLKP BIT(8) +#define RPC_SMCR_SPIRE BIT(2) +#define RPC_SMCR_SPIWE BIT(1) +#define RPC_SMCR_SPIE BIT(0) + +#define RPC_SMCMR 0x0024 /* R/W */ +#define RPC_SMCMR_CMD(c) (((c) & 0xFF) << 16) +#define RPC_SMCMR_OCMD(c) (((c) & 0xFF) << 0) + +#define RPC_SMADR 0x0028 /* R/W */ +#define RPC_SMOPR 0x002C /* R/W */ +#define RPC_SMOPR_OPD0(o) (((o) & 0xFF) << 0) +#define RPC_SMOPR_OPD1(o) (((o) & 0xFF) << 8) +#define RPC_SMOPR_OPD2(o) (((o) & 0xFF) << 16) +#define RPC_SMOPR_OPD3(o) (((o) & 0xFF) << 24) + +#define RPC_SMENR 0x0030 /* R/W */ +#define RPC_SMENR_CDB(o) (((o) & 0x3) << 30) +#define RPC_SMENR_OCDB(o) (((o) & 0x3) << 28) +#define RPC_SMENR_ADB(o) (((o) & 0x3) << 24) +#define RPC_SMENR_OPDB(o) (((o) & 0x3) << 20) +#define RPC_SMENR_SPIDB(o) (((o) & 0x3) << 16) +#define RPC_SMENR_DME BIT(15) +#define RPC_SMENR_CDE BIT(14) +#define RPC_SMENR_OCDE BIT(12) +#define RPC_SMENR_ADE(v) (((v) & 0xF) << 8) +#define RPC_SMENR_OPDE(v) (((v) & 0xF) << 4) +#define RPC_SMENR_SPIDE(v) (((v) & 0xF) << 0) + +#define RPC_SMRDR0 0x0038 /* R */ +#define RPC_SMRDR1 0x003C /* R */ +#define RPC_SMWDR0 0x0040 /* R/W */ +#define RPC_SMWDR1 0x0044 /* R/W */ +#define RPC_CMNSR 0x0048 /* R */ +#define RPC_CMNSR_SSLF BIT(1) +#define RPC_CMNSR_TEND BIT(0) + +#define RPC_DRDMCR 0x0058 /* R/W */ +#define RPC_DRDMCR_DMCYC(v) (((v) & 0xF) << 0) + +#define RPC_DRDRENR 0x005C /* R/W */ +#define RPC_DRDRENR_HYPE (0x5 << 12) +#define RPC_DRDRENR_ADDRE BIT(8) +#define RPC_DRDRENR_OPDRE BIT(4) +#define RPC_DRDRENR_DRDRE BIT(0) + +#define RPC_SMDMCR 0x0060 /* R/W */ +#define RPC_SMDMCR_DMCYC(v) (((v) & 0xF) << 0) + +#define RPC_SMDRENR 0x0064 /* R/W */ +#define RPC_SMDRENR_HYPE (0x5 << 12) +#define RPC_SMDRENR_ADDRE BIT(8) +#define RPC_SMDRENR_OPDRE BIT(4) +#define RPC_SMDRENR_SPIDRE BIT(0) + +#define RPC_PHYCNT 0x007C /* R/W */ +#define RPC_PHYCNT_CAL BIT(31) +#define PRC_PHYCNT_OCTA_AA BIT(22) +#define PRC_PHYCNT_OCTA_SA BIT(23) +#define PRC_PHYCNT_EXDS BIT(21) +#define RPC_PHYCNT_OCT BIT(20) +#define RPC_PHYCNT_STRTIM(v) (((v) & 0x7) << 15) +#define RPC_PHYCNT_WBUF2 BIT(4) +#define RPC_PHYCNT_WBUF BIT(2) +#define RPC_PHYCNT_MEM(v) (((v) & 0x3) << 0) + +#define RPC_PHYINT 0x0088 /* R/W */ +#define RPC_PHYINT_RSTEN BIT(18) +#define RPC_PHYINT_WPEN BIT(17) +#define RPC_PHYINT_INTEN BIT(16) +#define RPC_PHYINT_RST BIT(2) +#define RPC_PHYINT_WP BIT(1) +#define RPC_PHYINT_INT BIT(0) + +#define RPC_WBUF 0x8000 /* R/W size=4/8/16/32/64Bytes */ +#define RPC_WBUF_SIZE 0x100 + +DECLARE_GLOBAL_DATA_PTR; + +struct rpc_spi_platdata { + fdt_addr_t regs; + fdt_addr_t extr; + s32 freq; /* Default clock freq, -1 for none */ +}; + +struct rpc_spi_priv { + fdt_addr_t regs; + fdt_addr_t extr; + struct clk clk; + + u8 cmdcopy[8]; + u32 cmdlen; + bool cmdstarted; +}; + +static int rpc_spi_wait_sslf(struct udevice *dev) +{ + struct rpc_spi_priv *priv = dev_get_priv(dev->parent); + + return wait_for_bit_le32((void *)priv->regs + RPC_CMNSR, RPC_CMNSR_SSLF, + false, 1000, false); +} + +static int rpc_spi_wait_tend(struct udevice *dev) +{ + struct rpc_spi_priv *priv = dev_get_priv(dev->parent); + + return wait_for_bit_le32((void *)priv->regs + RPC_CMNSR, RPC_CMNSR_TEND, + true, 1000, false); +} + +static void rpc_spi_flush_read_cache(struct udevice *dev) +{ + struct udevice *bus = dev->parent; + struct rpc_spi_priv *priv = dev_get_priv(bus); + + /* Flush read cache */ + writel(RPC_DRCR_SSLN | RPC_DRCR_RBURST(0x1f) | + RPC_DRCR_RCF | RPC_DRCR_RBE | RPC_DRCR_SSLE, + priv->regs + RPC_DRCR); + readl(priv->regs + RPC_DRCR); + +} + +static int rpc_spi_claim_bus(struct udevice *dev, bool manual) +{ + struct udevice *bus = dev->parent; + struct rpc_spi_priv *priv = dev_get_priv(bus); + + /* + * NOTE: The 0x260 are undocumented bits, but they must be set. + * NOTE: On H3 ES1.x (not supported in mainline U-Boot), the + * RPC_PHYCNT_STRTIM shall be 0, while on newer parts, the + * RPC_PHYCNT_STRTIM shall be 6. + */ + writel(RPC_PHYCNT_CAL | RPC_PHYCNT_STRTIM(6) | 0x260, + priv->regs + RPC_PHYCNT); + writel((manual ? RPC_CMNCR_MD : 0) | RPC_CMNCR_SFDE | + RPC_CMNCR_MOIIO_HIZ | RPC_CMNCR_IOFV_HIZ | RPC_CMNCR_BSZ(0), + priv->regs + RPC_CMNCR); + + writel(RPC_SSLDR_SPNDL(7) | RPC_SSLDR_SLNDL(7) | + RPC_SSLDR_SCKDL(7), priv->regs + RPC_SSLDR); + + rpc_spi_flush_read_cache(dev); + + return 0; +} + +static int rpc_spi_release_bus(struct udevice *dev) +{ + struct udevice *bus = dev->parent; + struct rpc_spi_priv *priv = dev_get_priv(bus); + + /* NOTE: The 0x260 are undocumented bits, but they must be set. */ + writel(RPC_PHYCNT_STRTIM(6) | 0x260, priv->regs + RPC_PHYCNT); + + rpc_spi_flush_read_cache(dev); + + return 0; +} + +static int rpc_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev->parent; + struct rpc_spi_priv *priv = dev_get_priv(bus); + u32 wlen = dout ? (bitlen / 8) : 0; + u32 rlen = din ? (bitlen / 8) : 0; + u32 wloop = DIV_ROUND_UP(wlen, 4); + u32 smenr, smcr, offset; + int ret = 0; + + if (!priv->cmdstarted) { + if (!wlen || rlen) + BUG(); + + memcpy(priv->cmdcopy, dout, wlen); + priv->cmdlen = wlen; + + /* Command transfer start */ + priv->cmdstarted = true; + if (!(flags & SPI_XFER_END)) + return 0; + } + + offset = (priv->cmdcopy[1] << 16) | (priv->cmdcopy[2] << 8) | + (priv->cmdcopy[3] << 0); + + smenr = 0; + + if (wlen || (!rlen && !wlen) || flags == SPI_XFER_ONCE) { + if (wlen && flags == SPI_XFER_END) + smenr = RPC_SMENR_SPIDE(0xf); + + rpc_spi_claim_bus(dev, true); + + writel(0, priv->regs + RPC_SMCR); + + if (priv->cmdlen >= 1) { /* Command(1) */ + writel(RPC_SMCMR_CMD(priv->cmdcopy[0]), + priv->regs + RPC_SMCMR); + smenr |= RPC_SMENR_CDE; + } else { + writel(0, priv->regs + RPC_SMCMR); + } + + if (priv->cmdlen >= 4) { /* Address(3) */ + writel(offset, priv->regs + RPC_SMADR); + smenr |= RPC_SMENR_ADE(7); + } else { + writel(0, priv->regs + RPC_SMADR); + } + + if (priv->cmdlen >= 5) { /* Dummy(n) */ + writel(8 * (priv->cmdlen - 4) - 1, + priv->regs + RPC_SMDMCR); + smenr |= RPC_SMENR_DME; + } else { + writel(0, priv->regs + RPC_SMDMCR); + } + + writel(0, priv->regs + RPC_SMOPR); + + writel(0, priv->regs + RPC_SMDRENR); + + if (wlen && flags == SPI_XFER_END) { + u32 *datout = (u32 *)dout; + + while (wloop--) { + smcr = RPC_SMCR_SPIWE | RPC_SMCR_SPIE; + if (wloop >= 1) + smcr |= RPC_SMCR_SSLKP; + writel(smenr, priv->regs + RPC_SMENR); + writel(*datout, priv->regs + RPC_SMWDR0); + writel(smcr, priv->regs + RPC_SMCR); + ret = rpc_spi_wait_tend(dev); + if (ret) + goto err; + datout++; + smenr = RPC_SMENR_SPIDE(0xf); + } + + ret = rpc_spi_wait_sslf(dev); + + } else { + writel(smenr, priv->regs + RPC_SMENR); + writel(RPC_SMCR_SPIE, priv->regs + RPC_SMCR); + ret = rpc_spi_wait_tend(dev); + } + } else { /* Read data only, using DRx ext access */ + rpc_spi_claim_bus(dev, false); + + if (priv->cmdlen >= 1) { /* Command(1) */ + writel(RPC_DRCMR_CMD(priv->cmdcopy[0]), + priv->regs + RPC_DRCMR); + smenr |= RPC_DRENR_CDE; + } else { + writel(0, priv->regs + RPC_DRCMR); + } + + if (priv->cmdlen >= 4) /* Address(3) */ + smenr |= RPC_DRENR_ADE(7); + + if (priv->cmdlen >= 5) { /* Dummy(n) */ + writel(8 * (priv->cmdlen - 4) - 1, + priv->regs + RPC_DRDMCR); + smenr |= RPC_DRENR_DME; + } else { + writel(0, priv->regs + RPC_DRDMCR); + } + + writel(0, priv->regs + RPC_DROPR); + + writel(smenr, priv->regs + RPC_DRENR); + + if (rlen) + memcpy_fromio(din, (void *)(priv->extr + offset), rlen); + else + readl(priv->extr); /* Dummy read */ + } + +err: + priv->cmdstarted = false; + + rpc_spi_release_bus(dev); + + return ret; +} + +static int rpc_spi_set_speed(struct udevice *bus, uint speed) +{ + /* This is a SPI NOR controller, do nothing. */ + return 0; +} + +static int rpc_spi_set_mode(struct udevice *bus, uint mode) +{ + /* This is a SPI NOR controller, do nothing. */ + return 0; +} + +static int rpc_spi_bind(struct udevice *parent) +{ + const void *fdt = gd->fdt_blob; + ofnode node; + int ret, off; + + /* + * Check if there are any SPI NOR child nodes, if so, bind as + * this controller will be operated in SPI mode. + */ + dev_for_each_subnode(node, parent) { + off = ofnode_to_offset(node); + + ret = fdt_node_check_compatible(fdt, off, "spi-flash"); + if (!ret) + return 0; + + ret = fdt_node_check_compatible(fdt, off, "jedec,spi-nor"); + if (!ret) + return 0; + } + + return -ENODEV; +} + +static int rpc_spi_probe(struct udevice *dev) +{ + struct rpc_spi_platdata *plat = dev_get_platdata(dev); + struct rpc_spi_priv *priv = dev_get_priv(dev); + + priv->regs = plat->regs; + priv->extr = plat->extr; + + clk_enable(&priv->clk); + + return 0; +} + +static int rpc_spi_ofdata_to_platdata(struct udevice *bus) +{ + struct rpc_spi_platdata *plat = dev_get_platdata(bus); + struct rpc_spi_priv *priv = dev_get_priv(bus); + int ret; + + plat->regs = dev_read_addr_index(bus, 0); + plat->extr = dev_read_addr_index(bus, 1); + + ret = clk_get_by_index(bus, 0, &priv->clk); + if (ret < 0) { + printf("%s: Could not get clock for %s: %d\n", + __func__, bus->name, ret); + return ret; + } + + plat->freq = dev_read_u32_default(bus, "spi-max-freq", 50000000); + + return 0; +} + +static const struct dm_spi_ops rpc_spi_ops = { + .xfer = rpc_spi_xfer, + .set_speed = rpc_spi_set_speed, + .set_mode = rpc_spi_set_mode, +}; + +static const struct udevice_id rpc_spi_ids[] = { + { .compatible = "renesas,rpc-r8a7795" }, + { .compatible = "renesas,rpc-r8a7796" }, + { .compatible = "renesas,rpc-r8a77965" }, + { .compatible = "renesas,rpc-r8a77970" }, + { .compatible = "renesas,rpc-r8a77995" }, + { } +}; + +U_BOOT_DRIVER(rpc_spi) = { + .name = "rpc_spi", + .id = UCLASS_SPI, + .of_match = rpc_spi_ids, + .ops = &rpc_spi_ops, + .ofdata_to_platdata = rpc_spi_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct rpc_spi_platdata), + .priv_auto_alloc_size = sizeof(struct rpc_spi_priv), + .bind = rpc_spi_bind, + .probe = rpc_spi_probe, +}; From 0e6fa20b14ed27ef667ddb31c8e7c620417ee4f5 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 10 Apr 2018 16:47:38 +0200 Subject: [PATCH 31/36] spi: sh_qspi: Replace data types with short ones Just replace unsigned {char,short,long} with u{8,16,32}, no functional change. Signed-off-by: Marek Vasut Cc: Nobuhiro Iwamatsu --- drivers/spi/sh_qspi.c | 62 +++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/drivers/spi/sh_qspi.c b/drivers/spi/sh_qspi.c index 75999c8..fc0e1fc 100644 --- a/drivers/spi/sh_qspi.c +++ b/drivers/spi/sh_qspi.c @@ -38,30 +38,30 @@ /* SH QSPI register set */ struct sh_qspi_regs { - unsigned char spcr; - unsigned char sslp; - unsigned char sppcr; - unsigned char spsr; - unsigned long spdr; - unsigned char spscr; - unsigned char spssr; - unsigned char spbr; - unsigned char spdcr; - unsigned char spckd; - unsigned char sslnd; - unsigned char spnd; - unsigned char dummy0; - unsigned short spcmd0; - unsigned short spcmd1; - unsigned short spcmd2; - unsigned short spcmd3; - unsigned char spbfcr; - unsigned char dummy1; - unsigned short spbdcr; - unsigned long spbmul0; - unsigned long spbmul1; - unsigned long spbmul2; - unsigned long spbmul3; + u8 spcr; + u8 sslp; + u8 sppcr; + u8 spsr; + u32 spdr; + u8 spscr; + u8 spssr; + u8 spbr; + u8 spdcr; + u8 spckd; + u8 sslnd; + u8 spnd; + u8 dummy0; + u16 spcmd0; + u16 spcmd1; + u16 spcmd2; + u16 spcmd3; + u8 spbfcr; + u8 dummy1; + u16 spbdcr; + u32 spbmul0; + u32 spbmul1; + u32 spbmul2; + u32 spbmul3; }; struct sh_qspi_slave { @@ -200,11 +200,11 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, void *din, unsigned long flags) { struct sh_qspi_slave *ss = to_sh_qspi(slave); - unsigned long nbyte; + u32 nbyte; int ret = 0; - unsigned char dtdata = 0, drdata; - unsigned char *tdata = &dtdata, *rdata = &drdata; - unsigned long *spbmul0 = &ss->regs->spbmul0; + u8 dtdata = 0, drdata; + u8 *tdata = &dtdata, *rdata = &drdata; + u32 *spbmul0 = &ss->regs->spbmul0; if (dout == NULL && din == NULL) { if (flags & SPI_XFER_END) @@ -230,7 +230,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, writel(nbyte, spbmul0); if (dout != NULL) - tdata = (unsigned char *)dout; + tdata = (u8 *)dout; if (din != NULL) rdata = din; @@ -244,7 +244,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, udelay(10); } - writeb(*tdata, (unsigned char *)(&ss->regs->spdr)); + writeb(*tdata, (u8 *)(&ss->regs->spdr)); while ((readw(&ss->regs->spbdcr) != SPBDCR_RXBC0)) { if (ctrlc()) { @@ -262,7 +262,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, udelay(10); } - *rdata = readb((unsigned char *)(&ss->regs->spdr)); + *rdata = readb((u8 *)(&ss->regs->spdr)); if (dout != NULL) tdata++; From 118226495249268a35e604b2e6309801699f6224 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 10 Apr 2018 16:54:38 +0200 Subject: [PATCH 32/36] spi: sh_qspi: Drop SPBDCR wait Waiting for SPBDCR == 1 is not required and is covered by the subsequent wait for SPSR_SPRFF, so drop this. Signed-off-by: Marek Vasut Cc: Nobuhiro Iwamatsu --- drivers/spi/sh_qspi.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/spi/sh_qspi.c b/drivers/spi/sh_qspi.c index fc0e1fc..8eaa674 100644 --- a/drivers/spi/sh_qspi.c +++ b/drivers/spi/sh_qspi.c @@ -246,14 +246,6 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, writeb(*tdata, (u8 *)(&ss->regs->spdr)); - while ((readw(&ss->regs->spbdcr) != SPBDCR_RXBC0)) { - if (ctrlc()) { - puts("abort\n"); - return 1; - } - udelay(1); - } - while (!(readb(&ss->regs->spsr) & SPSR_SPRFF)) { if (ctrlc()) { puts("abort\n"); From 9573db654d1999a1dfde6469782aa8d7cf3d589f Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 10 Apr 2018 16:58:46 +0200 Subject: [PATCH 33/36] spi: sh_qspi: Replace ad hoc waiting with wait_for_bit Replace the ad-hoc endless loops with wait_for_bit() with reasonable timeout. Note that the loops had internal 10uS delays, although there is no reason for those on this HW, so they are dropped. Signed-off-by: Marek Vasut Cc: Nobuhiro Iwamatsu --- drivers/spi/sh_qspi.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/spi/sh_qspi.c b/drivers/spi/sh_qspi.c index 8eaa674..d7f558a 100644 --- a/drivers/spi/sh_qspi.c +++ b/drivers/spi/sh_qspi.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -236,23 +237,17 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, rdata = din; while (nbyte > 0) { - while (!(readb(&ss->regs->spsr) & SPSR_SPTEF)) { - if (ctrlc()) { - puts("abort\n"); - return 1; - } - udelay(10); - } + ret = wait_for_bit_8(&ss->regs->spsr, SPSR_SPTEF, + true, 1000, true); + if (ret) + return ret; writeb(*tdata, (u8 *)(&ss->regs->spdr)); - while (!(readb(&ss->regs->spsr) & SPSR_SPRFF)) { - if (ctrlc()) { - puts("abort\n"); - return 1; - } - udelay(10); - } + ret = wait_for_bit_8(&ss->regs->spsr, SPSR_SPRFF, + true, 1000, true); + if (ret) + return ret; *rdata = readb((u8 *)(&ss->regs->spdr)); From ea5512eb095067dda27930246792d2957feb9434 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 10 Apr 2018 16:43:47 +0200 Subject: [PATCH 34/36] spi: sh_qspi: Make use of the 32byte FIFO The QSPI controller on RCar Gen2 has 32byte FIFO. Instead of doing the SPI transmission 1 byte at time, if there is a 32byte chunk of data to be transferred, fill the FIFO completely and then transfer the data to/from the FIFO. This increases the SPI NOR access speed significantly. Signed-off-by: Marek Vasut Cc: Nobuhiro Iwamatsu --- drivers/spi/sh_qspi.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/drivers/spi/sh_qspi.c b/drivers/spi/sh_qspi.c index d7f558a..5075be3 100644 --- a/drivers/spi/sh_qspi.c +++ b/drivers/spi/sh_qspi.c @@ -36,6 +36,8 @@ SPCMD_BRDV0 #define SPBFCR_TXRST BIT(7) #define SPBFCR_RXRST BIT(6) +#define SPBFCR_TXTRG 0x30 +#define SPBFCR_RXTRG 0x07 /* SH QSPI register set */ struct sh_qspi_regs { @@ -201,8 +203,8 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, void *din, unsigned long flags) { struct sh_qspi_slave *ss = to_sh_qspi(slave); - u32 nbyte; - int ret = 0; + u32 nbyte, chunk; + int i, ret = 0; u8 dtdata = 0, drdata; u8 *tdata = &dtdata, *rdata = &drdata; u32 *spbmul0 = &ss->regs->spbmul0; @@ -237,26 +239,38 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, rdata = din; while (nbyte > 0) { + /* + * Check if there is 32 Byte chunk and if there is, transfer + * it in one burst, otherwise transfer on byte-by-byte basis. + */ + chunk = (nbyte >= 32) ? 32 : 1; + + clrsetbits_8(&ss->regs->spbfcr, SPBFCR_TXTRG | SPBFCR_RXTRG, + chunk == 32 ? SPBFCR_TXTRG | SPBFCR_RXTRG : 0); + ret = wait_for_bit_8(&ss->regs->spsr, SPSR_SPTEF, true, 1000, true); if (ret) return ret; - writeb(*tdata, (u8 *)(&ss->regs->spdr)); + for (i = 0; i < chunk; i++) { + writeb(*tdata, &ss->regs->spdr); + if (dout != NULL) + tdata++; + } ret = wait_for_bit_8(&ss->regs->spsr, SPSR_SPRFF, true, 1000, true); if (ret) return ret; - *rdata = readb((u8 *)(&ss->regs->spdr)); - - if (dout != NULL) - tdata++; - if (din != NULL) - rdata++; + for (i = 0; i < chunk; i++) { + *rdata = readb(&ss->regs->spdr); + if (din != NULL) + rdata++; + } - nbyte--; + nbyte -= chunk; } if (flags & SPI_XFER_END) From 7beccc52a1e3482d28bdfa52053583956d4d19e8 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 15 Feb 2018 13:19:33 +0100 Subject: [PATCH 35/36] ARM: rmobile: Fix the memory map on Gen3 Fix up the memory map on Gen3 to match datasheet properly. This simplifies the memory map setup as well, since we do no longer need this massive complexity. Signed-off-by: Marek Vasut Cc: Nobuhiro Iwamatsu --- arch/arm/mach-rmobile/cpu_info.c | 3 - arch/arm/mach-rmobile/include/mach/rmobile.h | 1 - arch/arm/mach-rmobile/memmap-gen3.c | 92 +++------------------------- 3 files changed, 9 insertions(+), 87 deletions(-) diff --git a/arch/arm/mach-rmobile/cpu_info.c b/arch/arm/mach-rmobile/cpu_info.c index ba87d21..ce15741 100644 --- a/arch/arm/mach-rmobile/cpu_info.c +++ b/arch/arm/mach-rmobile/cpu_info.c @@ -18,9 +18,6 @@ int arch_cpu_init(void) #ifndef CONFIG_SYS_DCACHE_OFF void enable_caches(void) { -#if defined(CONFIG_RCAR_GEN3) - rcar_gen3_memmap_fixup(); -#endif dcache_enable(); } #endif diff --git a/arch/arm/mach-rmobile/include/mach/rmobile.h b/arch/arm/mach-rmobile/include/mach/rmobile.h index ff0ca63..94ea366 100644 --- a/arch/arm/mach-rmobile/include/mach/rmobile.h +++ b/arch/arm/mach-rmobile/include/mach/rmobile.h @@ -41,7 +41,6 @@ u32 rmobile_get_cpu_type(void); u32 rmobile_get_cpu_rev_integer(void); u32 rmobile_get_cpu_rev_fraction(void); -void rcar_gen3_memmap_fixup(void); #endif /* __ASSEMBLY__ */ #endif /* __ASM_ARCH_RMOBILE_H */ diff --git a/arch/arm/mach-rmobile/memmap-gen3.c b/arch/arm/mach-rmobile/memmap-gen3.c index 801e392..52cd000 100644 --- a/arch/arm/mach-rmobile/memmap-gen3.c +++ b/arch/arm/mach-rmobile/memmap-gen3.c @@ -9,77 +9,24 @@ #include #include -static struct mm_region r8a7795_mem_map[] = { +static struct mm_region gen3_mem_map[] = { { .virt = 0x0UL, .phys = 0x0UL, - .size = 0x80000000UL, - .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | - PTE_BLOCK_INNER_SHARE - }, { - .virt = 0x80000000UL, - .phys = 0x80000000UL, - .size = 0x80000000UL, - .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | - PTE_BLOCK_NON_SHARE | - PTE_BLOCK_PXN | PTE_BLOCK_UXN - }, { - /* List terminator */ - 0, - } -}; - -static struct mm_region r8a7796_mem_map[] = { - { - .virt = 0x0UL, - .phys = 0x0UL, - .size = 0xe0000000UL, - .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | - PTE_BLOCK_INNER_SHARE - }, { - .virt = 0xe0000000UL, - .phys = 0xe0000000UL, - .size = 0xe0000000UL, + .size = 0x40000000UL, .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { - /* List terminator */ - 0, - } -}; - -static struct mm_region r8a77970_mem_map[] = { - { - .virt = 0x0UL, - .phys = 0x0UL, - .size = 0xe0000000UL, - .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | - PTE_BLOCK_INNER_SHARE - }, { - .virt = 0xe0000000UL, - .phys = 0xe0000000UL, - .size = 0xe0000000UL, - .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | - PTE_BLOCK_NON_SHARE | - PTE_BLOCK_PXN | PTE_BLOCK_UXN - }, { - /* List terminator */ - 0, - } -}; - -static struct mm_region r8a77995_mem_map[] = { - { - .virt = 0x0UL, - .phys = 0x0UL, - .size = 0xe0000000UL, + .virt = 0x40000000UL, + .phys = 0x40000000UL, + .size = 0x80000000UL, .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_INNER_SHARE }, { - .virt = 0xe0000000UL, - .phys = 0xe0000000UL, - .size = 0xe0000000UL, + .virt = 0xc0000000UL, + .phys = 0xc0000000UL, + .size = 0x40000000UL, .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN @@ -89,25 +36,4 @@ static struct mm_region r8a77995_mem_map[] = { } }; -struct mm_region *mem_map = r8a7795_mem_map; - -void rcar_gen3_memmap_fixup(void) -{ - u32 cpu_type = rmobile_get_cpu_type(); - - switch (cpu_type) { - case RMOBILE_CPU_TYPE_R8A7795: - mem_map = r8a7795_mem_map; - break; - case RMOBILE_CPU_TYPE_R8A7796: - case RMOBILE_CPU_TYPE_R8A77965: - mem_map = r8a7796_mem_map; - break; - case RMOBILE_CPU_TYPE_R8A77970: - mem_map = r8a77970_mem_map; - break; - case RMOBILE_CPU_TYPE_R8A77995: - mem_map = r8a77995_mem_map; - break; - } -} +struct mm_region *mem_map = gen3_mem_map; From 97ed677831e45b789fc19bd807273ff34486efb8 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 10 Apr 2018 16:06:40 +0200 Subject: [PATCH 36/36] ARM: rmobile: Set maximum kernel size to 64 MiB on Gen3 The Gen3 kernel images are often above 8 MiB, increase the maximum kernel size to 64 MiB to future-proof it, just like many other ARM64 boards do. Signed-off-by: Marek Vasut Cc: Nobuhiro Iwamatsu --- include/configs/rcar-gen3-common.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/configs/rcar-gen3-common.h b/include/configs/rcar-gen3-common.h index bbaab80..da82e44 100644 --- a/include/configs/rcar-gen3-common.h +++ b/include/configs/rcar-gen3-common.h @@ -51,6 +51,7 @@ #define CONFIG_SYS_MONITOR_LEN (256 * 1024) #define CONFIG_SYS_MALLOC_LEN (1 * 1024 * 1024) #define CONFIG_SYS_BOOTMAPSZ (8 * 1024 * 1024) +#define CONFIG_SYS_BOOTM_LEN (64 << 20) /* ENV setting */ #define CONFIG_ENV_OVERWRITE