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/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/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/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/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) 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; 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..f711aa9 --- /dev/null +++ b/board/renesas/porter/porter_spl.c @@ -0,0 +1,495 @@ +/* + * 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 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) +{ + /* UART clocks enabled and gd valid - init serial console */ + preloader_console_init(); +} + +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; + spl_boot_list[2] = BOOT_DEVICE_NONE; +} + +void reset_cpu(ulong addr) +{ +} diff --git a/configs/porter_defconfig b/configs/porter_defconfig index 72a3108..3e0900e 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 @@ -7,18 +8,36 @@ 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_RAM_SUPPORT=y +CONFIG_TPL_RAM_DEVICE=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_HUSH_PARSER=y CONFIG_CMD_BOOTZ=y # CONFIG_CMD_IMI is not set # CONFIG_CMD_XIMG is not set @@ -54,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 @@ -71,6 +90,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 +98,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/configs/r8a7795_salvator-x_defconfig b/configs/r8a7795_salvator-x_defconfig index 44921d9..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_UNIPHIER=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 e85d0a4..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_UNIPHIER=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/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..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_UNIPHIER=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 5a65982..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_UNIPHIER=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 3d421d2..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_UNIPHIER=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 99a8089..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_UNIPHIER=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 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 9583410..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) += uniphier-sd.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 diff --git a/drivers/mmc/matsushita-common.c b/drivers/mmc/matsushita-common.c new file mode 100644 index 0000000..e22a9de --- /dev/null +++ b/drivers/mmc/matsushita-common.c @@ -0,0 +1,787 @@ +/* + * 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 + +#include "matsushita-common.h" + +DECLARE_GLOBAL_DATA_PTR; + +static u64 matsu_sd_readq(struct matsu_sd_priv *priv, unsigned int reg) +{ + return readq(priv->regbase + (reg << 1)); +} + +static void matsu_sd_writeq(struct matsu_sd_priv *priv, + u64 val, unsigned int reg) +{ + 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)); +} + +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 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); +} + +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 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 || + reg == MATSU_SD_ARG) + writew(val >> 16, priv->regbase + (reg >> 1) + 2); + } 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; +} + +#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) +matsu_pio_read_fifo(16, w) + +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 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) + 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); + + 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) +matsu_pio_write_fifo(16, w) + +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 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) + 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); + + 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; + /* + * 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; + 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; + } + + matsu_sd_wait_for_irq(dev, MATSU_SD_INFO2, MATSU_SD_INFO2_SCLKDIVEN); + + 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 0: + 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 = (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) + 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); +} + +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); + 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); + matsu_sd_set_pins(dev); + + 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. + */ + if (priv->version >= 0x10) + matsu_sd_writel(priv, 0x101, 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); + 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, 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); + fdt_addr_t base; + struct clk clk; + int ret; + + 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 + device_get_supply_regulator(dev, "vqmmc-supply", &priv->vqmmc_dev); +#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; + } + + 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; + + 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..34631cb --- /dev/null +++ b/drivers/mmc/matsushita-common.h @@ -0,0 +1,151 @@ +/* + * 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_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 */ +#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_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 */ +#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 (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 +#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 */ +#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) +#ifdef CONFIG_DM_REGULATOR + struct udevice *vqmmc_dev; +#endif +}; + +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, 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__ */ diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c new file mode 100644 index 0000000..8564f42 --- /dev/null +++ b/drivers/mmc/renesas-sdhi.c @@ -0,0 +1,368 @@ +/* + * 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" + +#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); + + mdelay(10); + +#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 = 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 +#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 = 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 */ } +}; + +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; + + 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 (fdt_resource_size(®_res) == 0x100) + quirks |= MATSU_SD_CAP_16BIT; + } + + 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) = { + .name = "renesas-sdhi", + .id = UCLASS_MMC, + .of_match = renesas_sdhi_match, + .bind = matsu_sd_bind, + .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 525b170..42eb9c2 100644 --- a/drivers/mmc/uniphier-sd.c +++ b/drivers/mmc/uniphier-sd.c @@ -17,857 +17,31 @@ #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 */ } }; +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 = uniphier_sd_bind, + .bind = matsu_sd_bind, .probe = uniphier_sd_probe, - .priv_auto_alloc_size = sizeof(struct uniphier_sd_priv), - .platdata_auto_alloc_size = sizeof(struct uniphier_sd_plat), + .priv_auto_alloc_size = sizeof(struct matsu_sd_priv), + .platdata_auto_alloc_size = sizeof(struct matsu_sd_plat), .ops = &uniphier_sd_ops, }; 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, +}; 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, +}; diff --git a/drivers/spi/sh_qspi.c b/drivers/spi/sh_qspi.c index 75999c8..5075be3 100644 --- a/drivers/spi/sh_qspi.c +++ b/drivers/spi/sh_qspi.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -35,33 +36,35 @@ 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 { - 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 +203,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; - int ret = 0; - unsigned char dtdata = 0, drdata; - unsigned char *tdata = &dtdata, *rdata = &drdata; - unsigned long *spbmul0 = &ss->regs->spbmul0; + u32 nbyte, chunk; + int i, ret = 0; + 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,46 +233,44 @@ 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; while (nbyte > 0) { - while (!(readb(&ss->regs->spsr) & SPSR_SPTEF)) { - if (ctrlc()) { - puts("abort\n"); - return 1; - } - udelay(10); + /* + * 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; + + for (i = 0; i < chunk; i++) { + writeb(*tdata, &ss->regs->spdr); + if (dout != NULL) + tdata++; } - writeb(*tdata, (unsigned char *)(&ss->regs->spdr)); + ret = wait_for_bit_8(&ss->regs->spsr, SPSR_SPRFF, + true, 1000, true); + if (ret) + return ret; - while ((readw(&ss->regs->spbdcr) != SPBDCR_RXBC0)) { - if (ctrlc()) { - puts("abort\n"); - return 1; - } - udelay(1); + for (i = 0; i < chunk; i++) { + *rdata = readb(&ss->regs->spdr); + if (din != NULL) + rdata++; } - while (!(readb(&ss->regs->spsr) & SPSR_SPRFF)) { - if (ctrlc()) { - puts("abort\n"); - return 1; - } - udelay(10); - } - - *rdata = readb((unsigned char *)(&ss->regs->spdr)); - - if (dout != NULL) - tdata++; - if (din != NULL) - rdata++; - - nbyte--; + nbyte -= chunk; } if (flags & SPI_XFER_END) 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 */ 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