diff mbox series

[U-Boot,4/9] sunxi: introduce RMR switch to enter payloads in 64-bit mode

Message ID 20190221013034.9099-5-andre.przywara@arm.com
State Deferred
Delegated to: Jagannadha Sutradharudu Teki
Headers show
Series sunxi: Allow FEL capable SPLs with 32bit builds | expand

Commit Message

Andre Przywara Feb. 21, 2019, 1:30 a.m. UTC
The ARMv8 capable Allwinner A64 SoC comes out of reset in AArch32 mode.
To run AArch64 code, we have to trigger a warm reset via the RMR register,
which proceeds with code execution at the address stored in the RVBAR
register.
If the bootable payload in the FIT image is using a different
architecture than the SPL has been compiled for, enter it via this said
RMR switch mechanism, by writing the entry point address into the MMIO
mapped, writable version of the RVBAR register.
Then the warm reset is triggered via a system register write.
If the payload architecture is the same as the SPL, we use the normal
branch as usual.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 arch/arm/lib/spl.c               | 14 +++++++++
 arch/arm/mach-sunxi/Makefile     |  3 ++
 arch/arm/mach-sunxi/spl_switch.c | 64 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 81 insertions(+)
 create mode 100644 arch/arm/mach-sunxi/spl_switch.c

Comments

Alexander Graf Feb. 21, 2019, 11:52 a.m. UTC | #1
On 02/21/2019 02:30 AM, Andre Przywara wrote:
> The ARMv8 capable Allwinner A64 SoC comes out of reset in AArch32 mode.
> To run AArch64 code, we have to trigger a warm reset via the RMR register,
> which proceeds with code execution at the address stored in the RVBAR
> register.
> If the bootable payload in the FIT image is using a different
> architecture than the SPL has been compiled for, enter it via this said
> RMR switch mechanism, by writing the entry point address into the MMIO
> mapped, writable version of the RVBAR register.
> Then the warm reset is triggered via a system register write.
> If the payload architecture is the same as the SPL, we use the normal
> branch as usual.
>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>   arch/arm/lib/spl.c               | 14 +++++++++
>   arch/arm/mach-sunxi/Makefile     |  3 ++
>   arch/arm/mach-sunxi/spl_switch.c | 64 ++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 81 insertions(+)
>   create mode 100644 arch/arm/mach-sunxi/spl_switch.c
>
> diff --git a/arch/arm/lib/spl.c b/arch/arm/lib/spl.c
> index 33cc76ba3d..4d9370d232 100644
> --- a/arch/arm/lib/spl.c
> +++ b/arch/arm/lib/spl.c
> @@ -73,3 +73,17 @@ void __noreturn jump_to_image_linux(struct spl_image_info *spl_image)
>   }
>   #endif	/* CONFIG_ARM64 */
>   #endif
> +
> +u8 spl_genimg_get_arch_id(const char *arch_str)
> +{
> +	if (!arch_str)
> +		return IH_ARCH_DEFAULT;
> +
> +	if (!strcmp(arch_str, "arm"))
> +		return IH_ARCH_ARM;
> +
> +	if (!strcmp(arch_str, "arm64"))
> +		return IH_ARCH_ARM64;
> +
> +	return IH_ARCH_DEFAULT;
> +}
> diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
> index 43a93e3085..5a5e10a024 100644
> --- a/arch/arm/mach-sunxi/Makefile
> +++ b/arch/arm/mach-sunxi/Makefile
> @@ -39,4 +39,7 @@ obj-$(CONFIG_SPL_SPI_SUNXI)	+= spl_spi_sunxi.o
>   obj-$(CONFIG_SUNXI_DRAM_DW)	+= dram_sunxi_dw.o
>   obj-$(CONFIG_SUNXI_DRAM_DW)	+= dram_timings/
>   obj-$(CONFIG_DRAM_SUN50I_H6)	+= dram_sun50i_h6.o
> +obj-$(CONFIG_MACH_SUN50I)	+= spl_switch.o
> +obj-$(CONFIG_MACH_SUN50I_H5)	+= spl_switch.o
> +obj-$(CONFIG_MACH_SUN50I_H6)	+= spl_switch.o
>   endif
> diff --git a/arch/arm/mach-sunxi/spl_switch.c b/arch/arm/mach-sunxi/spl_switch.c
> new file mode 100644
> index 0000000000..1de43c2396
> --- /dev/null
> +++ b/arch/arm/mach-sunxi/spl_switch.c
> @@ -0,0 +1,64 @@
> +/*
> + * (C) Copyright 2016 ARM Ltd.
> + *
> + * SPDX-License-Identifier:     GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <spl.h>
> +
> +#include <asm/io.h>
> +#include <asm/barriers.h>
> +
> +static void __noreturn jump_to_image_native(struct spl_image_info *spl_image)
> +{
> +	typedef void __noreturn (*image_entry_noargs_t)(void);
> +
> +	image_entry_noargs_t image_entry =
> +				(image_entry_noargs_t)spl_image->entry_point;
> +
> +	image_entry();
> +}
> +
> +static void __noreturn reset_rmr_switch(void)
> +{
> +#ifdef CONFIG_ARM64
> +	__asm__ volatile ( "mrs	 x0, RMR_EL3\n\t"
> +			   "bic  x0, x0, #1\n\t"   /* Clear enter-in-64 bit */
> +			   "orr  x0, x0, #2\n\t"   /* set reset request bit */
> +			   "msr  RMR_EL3, x0\n\t"
> +			   "isb  sy\n\t"
> +			   "nop\n\t"
> +			   "wfi\n\t"
> +			   "b    .\n"
> +			   ::: "x0");
> +#else
> +	__asm__ volatile ( "mrc  15, 0, r0, cr12, cr0, 2\n\t"
> +			   "orr  r0, r0, #3\n\t"   /* request reset in 64 bit */
> +			   "mcr  15, 0, r0, cr12, cr0, 2\n\t"
> +			   "isb\n\t"
> +			   "nop\n\t"
> +			   "wfi\n\t"
> +			   "b    .\n"
> +			   ::: "r0");
> +#endif
> +	while (1);	/* to avoid a compiler warning about __noreturn */
> +}
> +
> +void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image)
> +{
> +	if (spl_image->arch == IH_ARCH_DEFAULT) {
> +		debug("entering by branch\n");
> +		jump_to_image_native(spl_image);
> +	} else {
> +		debug("entering by RMR switch\n");
> +#ifdef CONFIG_MACH_SUN50I_H6
> +		writel(spl_image->entry_point, 0x09010040);
> +#else
> +		writel(spl_image->entry_point, 0x017000a0);
> +#endif

How does the original entry point get preserved over this? Don't you 
have to set the reset vector register as well?


Alex

> +		DSB;
> +		ISB;
> +		reset_rmr_switch();
> +	}
> +}
Andre Przywara Feb. 21, 2019, 12:07 p.m. UTC | #2
On Thu, 21 Feb 2019 12:52:50 +0100
Alexander Graf <agraf@suse.de> wrote:

> On 02/21/2019 02:30 AM, Andre Przywara wrote:
> > The ARMv8 capable Allwinner A64 SoC comes out of reset in AArch32 mode.
> > To run AArch64 code, we have to trigger a warm reset via the RMR register,
> > which proceeds with code execution at the address stored in the RVBAR
> > register.
> > If the bootable payload in the FIT image is using a different
> > architecture than the SPL has been compiled for, enter it via this said
> > RMR switch mechanism, by writing the entry point address into the MMIO
> > mapped, writable version of the RVBAR register.
> > Then the warm reset is triggered via a system register write.
> > If the payload architecture is the same as the SPL, we use the normal
> > branch as usual.
> >
> > Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> > ---
> >   arch/arm/lib/spl.c               | 14 +++++++++
> >   arch/arm/mach-sunxi/Makefile     |  3 ++
> >   arch/arm/mach-sunxi/spl_switch.c | 64 ++++++++++++++++++++++++++++++++++++++++
> >   3 files changed, 81 insertions(+)
> >   create mode 100644 arch/arm/mach-sunxi/spl_switch.c
> >
> > diff --git a/arch/arm/lib/spl.c b/arch/arm/lib/spl.c
> > index 33cc76ba3d..4d9370d232 100644
> > --- a/arch/arm/lib/spl.c
> > +++ b/arch/arm/lib/spl.c
> > @@ -73,3 +73,17 @@ void __noreturn jump_to_image_linux(struct spl_image_info *spl_image)
> >   }
> >   #endif	/* CONFIG_ARM64 */
> >   #endif
> > +
> > +u8 spl_genimg_get_arch_id(const char *arch_str)
> > +{
> > +	if (!arch_str)
> > +		return IH_ARCH_DEFAULT;
> > +
> > +	if (!strcmp(arch_str, "arm"))
> > +		return IH_ARCH_ARM;
> > +
> > +	if (!strcmp(arch_str, "arm64"))
> > +		return IH_ARCH_ARM64;
> > +
> > +	return IH_ARCH_DEFAULT;
> > +}
> > diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
> > index 43a93e3085..5a5e10a024 100644
> > --- a/arch/arm/mach-sunxi/Makefile
> > +++ b/arch/arm/mach-sunxi/Makefile
> > @@ -39,4 +39,7 @@ obj-$(CONFIG_SPL_SPI_SUNXI)	+= spl_spi_sunxi.o
> >   obj-$(CONFIG_SUNXI_DRAM_DW)	+= dram_sunxi_dw.o
> >   obj-$(CONFIG_SUNXI_DRAM_DW)	+= dram_timings/
> >   obj-$(CONFIG_DRAM_SUN50I_H6)	+= dram_sun50i_h6.o
> > +obj-$(CONFIG_MACH_SUN50I)	+= spl_switch.o
> > +obj-$(CONFIG_MACH_SUN50I_H5)	+= spl_switch.o
> > +obj-$(CONFIG_MACH_SUN50I_H6)	+= spl_switch.o
> >   endif
> > diff --git a/arch/arm/mach-sunxi/spl_switch.c b/arch/arm/mach-sunxi/spl_switch.c
> > new file mode 100644
> > index 0000000000..1de43c2396
> > --- /dev/null
> > +++ b/arch/arm/mach-sunxi/spl_switch.c
> > @@ -0,0 +1,64 @@
> > +/*
> > + * (C) Copyright 2016 ARM Ltd.
> > + *
> > + * SPDX-License-Identifier:     GPL-2.0+
> > + */
> > +
> > +#include <common.h>
> > +#include <spl.h>
> > +
> > +#include <asm/io.h>
> > +#include <asm/barriers.h>
> > +
> > +static void __noreturn jump_to_image_native(struct spl_image_info *spl_image)
> > +{
> > +	typedef void __noreturn (*image_entry_noargs_t)(void);
> > +
> > +	image_entry_noargs_t image_entry =
> > +				(image_entry_noargs_t)spl_image->entry_point;
> > +
> > +	image_entry();
> > +}
> > +
> > +static void __noreturn reset_rmr_switch(void)
> > +{
> > +#ifdef CONFIG_ARM64
> > +	__asm__ volatile ( "mrs	 x0, RMR_EL3\n\t"
> > +			   "bic  x0, x0, #1\n\t"   /* Clear enter-in-64 bit */
> > +			   "orr  x0, x0, #2\n\t"   /* set reset request bit */
> > +			   "msr  RMR_EL3, x0\n\t"
> > +			   "isb  sy\n\t"
> > +			   "nop\n\t"
> > +			   "wfi\n\t"
> > +			   "b    .\n"
> > +			   ::: "x0");
> > +#else
> > +	__asm__ volatile ( "mrc  15, 0, r0, cr12, cr0, 2\n\t"
> > +			   "orr  r0, r0, #3\n\t"   /* request reset in 64 bit */
> > +			   "mcr  15, 0, r0, cr12, cr0, 2\n\t"
> > +			   "isb\n\t"
> > +			   "nop\n\t"
> > +			   "wfi\n\t"
> > +			   "b    .\n"
> > +			   ::: "r0");
> > +#endif
> > +	while (1);	/* to avoid a compiler warning about __noreturn */
> > +}
> > +
> > +void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image)
> > +{
> > +	if (spl_image->arch == IH_ARCH_DEFAULT) {
> > +		debug("entering by branch\n");
> > +		jump_to_image_native(spl_image);
> > +	} else {
> > +		debug("entering by RMR switch\n");
> > +#ifdef CONFIG_MACH_SUN50I_H6
> > +		writel(spl_image->entry_point, 0x09010040);
> > +#else
> > +		writel(spl_image->entry_point, 0x017000a0);
> > +#endif  
> 
> How does the original entry point get preserved over this? Don't you 
> have to set the reset vector register as well?

The RVBAR_ELx (Reset Vector Base Address Register) registers are
architecturally read only. Fortunately Allwinner decided to have an MMIO
mapped writable alias, which we exploit here. I *believe* this gets reset
on a hard reset (external signal, for instance via the watchdog) to the
initial 0x0 value.

Anyway we use this method already in the AArch64 SPL today [1], and it
happens to work ;-)

Cheers,
Andre.

[1] http://git.denx.de/?p=u-boot.git;a=blob;f=arch/arm/include/asm/arch-sunxi/boot0.h;hb=HEAD

> > +		DSB;
> > +		ISB;
> > +		reset_rmr_switch();
> > +	}
> > +}  
> 
>
Alexander Graf Feb. 21, 2019, 1:06 p.m. UTC | #3
On 02/21/2019 01:07 PM, Andre Przywara wrote:
> On Thu, 21 Feb 2019 12:52:50 +0100
> Alexander Graf <agraf@suse.de> wrote:
>
>> On 02/21/2019 02:30 AM, Andre Przywara wrote:
>>> The ARMv8 capable Allwinner A64 SoC comes out of reset in AArch32 mode.
>>> To run AArch64 code, we have to trigger a warm reset via the RMR register,
>>> which proceeds with code execution at the address stored in the RVBAR
>>> register.
>>> If the bootable payload in the FIT image is using a different
>>> architecture than the SPL has been compiled for, enter it via this said
>>> RMR switch mechanism, by writing the entry point address into the MMIO
>>> mapped, writable version of the RVBAR register.
>>> Then the warm reset is triggered via a system register write.
>>> If the payload architecture is the same as the SPL, we use the normal
>>> branch as usual.
>>>
>>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>>> ---
>>>    arch/arm/lib/spl.c               | 14 +++++++++
>>>    arch/arm/mach-sunxi/Makefile     |  3 ++
>>>    arch/arm/mach-sunxi/spl_switch.c | 64 ++++++++++++++++++++++++++++++++++++++++
>>>    3 files changed, 81 insertions(+)
>>>    create mode 100644 arch/arm/mach-sunxi/spl_switch.c
>>>
>>> diff --git a/arch/arm/lib/spl.c b/arch/arm/lib/spl.c
>>> index 33cc76ba3d..4d9370d232 100644
>>> --- a/arch/arm/lib/spl.c
>>> +++ b/arch/arm/lib/spl.c
>>> @@ -73,3 +73,17 @@ void __noreturn jump_to_image_linux(struct spl_image_info *spl_image)
>>>    }
>>>    #endif	/* CONFIG_ARM64 */
>>>    #endif
>>> +
>>> +u8 spl_genimg_get_arch_id(const char *arch_str)
>>> +{
>>> +	if (!arch_str)
>>> +		return IH_ARCH_DEFAULT;
>>> +
>>> +	if (!strcmp(arch_str, "arm"))
>>> +		return IH_ARCH_ARM;
>>> +
>>> +	if (!strcmp(arch_str, "arm64"))
>>> +		return IH_ARCH_ARM64;
>>> +
>>> +	return IH_ARCH_DEFAULT;
>>> +}
>>> diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
>>> index 43a93e3085..5a5e10a024 100644
>>> --- a/arch/arm/mach-sunxi/Makefile
>>> +++ b/arch/arm/mach-sunxi/Makefile
>>> @@ -39,4 +39,7 @@ obj-$(CONFIG_SPL_SPI_SUNXI)	+= spl_spi_sunxi.o
>>>    obj-$(CONFIG_SUNXI_DRAM_DW)	+= dram_sunxi_dw.o
>>>    obj-$(CONFIG_SUNXI_DRAM_DW)	+= dram_timings/
>>>    obj-$(CONFIG_DRAM_SUN50I_H6)	+= dram_sun50i_h6.o
>>> +obj-$(CONFIG_MACH_SUN50I)	+= spl_switch.o
>>> +obj-$(CONFIG_MACH_SUN50I_H5)	+= spl_switch.o
>>> +obj-$(CONFIG_MACH_SUN50I_H6)	+= spl_switch.o
>>>    endif
>>> diff --git a/arch/arm/mach-sunxi/spl_switch.c b/arch/arm/mach-sunxi/spl_switch.c
>>> new file mode 100644
>>> index 0000000000..1de43c2396
>>> --- /dev/null
>>> +++ b/arch/arm/mach-sunxi/spl_switch.c
>>> @@ -0,0 +1,64 @@
>>> +/*
>>> + * (C) Copyright 2016 ARM Ltd.
>>> + *
>>> + * SPDX-License-Identifier:     GPL-2.0+
>>> + */
>>> +
>>> +#include <common.h>
>>> +#include <spl.h>
>>> +
>>> +#include <asm/io.h>
>>> +#include <asm/barriers.h>
>>> +
>>> +static void __noreturn jump_to_image_native(struct spl_image_info *spl_image)
>>> +{
>>> +	typedef void __noreturn (*image_entry_noargs_t)(void);
>>> +
>>> +	image_entry_noargs_t image_entry =
>>> +				(image_entry_noargs_t)spl_image->entry_point;
>>> +
>>> +	image_entry();
>>> +}
>>> +
>>> +static void __noreturn reset_rmr_switch(void)
>>> +{
>>> +#ifdef CONFIG_ARM64
>>> +	__asm__ volatile ( "mrs	 x0, RMR_EL3\n\t"
>>> +			   "bic  x0, x0, #1\n\t"   /* Clear enter-in-64 bit */
>>> +			   "orr  x0, x0, #2\n\t"   /* set reset request bit */
>>> +			   "msr  RMR_EL3, x0\n\t"
>>> +			   "isb  sy\n\t"
>>> +			   "nop\n\t"
>>> +			   "wfi\n\t"
>>> +			   "b    .\n"
>>> +			   ::: "x0");
>>> +#else
>>> +	__asm__ volatile ( "mrc  15, 0, r0, cr12, cr0, 2\n\t"
>>> +			   "orr  r0, r0, #3\n\t"   /* request reset in 64 bit */
>>> +			   "mcr  15, 0, r0, cr12, cr0, 2\n\t"
>>> +			   "isb\n\t"
>>> +			   "nop\n\t"
>>> +			   "wfi\n\t"
>>> +			   "b    .\n"
>>> +			   ::: "r0");
>>> +#endif
>>> +	while (1);	/* to avoid a compiler warning about __noreturn */
>>> +}
>>> +
>>> +void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image)
>>> +{
>>> +	if (spl_image->arch == IH_ARCH_DEFAULT) {
>>> +		debug("entering by branch\n");
>>> +		jump_to_image_native(spl_image);
>>> +	} else {
>>> +		debug("entering by RMR switch\n");
>>> +#ifdef CONFIG_MACH_SUN50I_H6
>>> +		writel(spl_image->entry_point, 0x09010040);
>>> +#else
>>> +		writel(spl_image->entry_point, 0x017000a0);
>>> +#endif
>> How does the original entry point get preserved over this? Don't you
>> have to set the reset vector register as well?
> The RVBAR_ELx (Reset Vector Base Address Register) registers are
> architecturally read only. Fortunately Allwinner decided to have an MMIO
> mapped writable alias, which we exploit here. I *believe* this gets reset
> on a hard reset (external signal, for instance via the watchdog) to the
> initial 0x0 value.
>
> Anyway we use this method already in the AArch64 SPL today [1], and it
> happens to work ;-)

Ugh, I read writel() the wrong way around. Do you think you can give the 
constant a nice #define that makes it fully obvious what's going on here?


Thanks!

Alex
Philipp Tomsich Feb. 21, 2019, 1:27 p.m. UTC | #4
Andre,

> On 21.02.2019, at 14:06, Alexander Graf <agraf@suse.de> wrote:
> 
> On 02/21/2019 01:07 PM, Andre Przywara wrote:
>> On Thu, 21 Feb 2019 12:52:50 +0100
>> Alexander Graf <agraf@suse.de> wrote:
>> 
>>> On 02/21/2019 02:30 AM, Andre Przywara wrote:
>>>> The ARMv8 capable Allwinner A64 SoC comes out of reset in AArch32 mode.
>>>> To run AArch64 code, we have to trigger a warm reset via the RMR register,
>>>> which proceeds with code execution at the address stored in the RVBAR
>>>> register.
>>>> If the bootable payload in the FIT image is using a different
>>>> architecture than the SPL has been compiled for, enter it via this said
>>>> RMR switch mechanism, by writing the entry point address into the MMIO
>>>> mapped, writable version of the RVBAR register.
>>>> Then the warm reset is triggered via a system register write.
>>>> If the payload architecture is the same as the SPL, we use the normal
>>>> branch as usual.
>>>> 
>>>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>>>> ---
>>>>   arch/arm/lib/spl.c               | 14 +++++++++
>>>>   arch/arm/mach-sunxi/Makefile     |  3 ++
>>>>   arch/arm/mach-sunxi/spl_switch.c | 64 ++++++++++++++++++++++++++++++++++++++++
>>>>   3 files changed, 81 insertions(+)
>>>>   create mode 100644 arch/arm/mach-sunxi/spl_switch.c
>>>> 
>>>> diff --git a/arch/arm/lib/spl.c b/arch/arm/lib/spl.c
>>>> index 33cc76ba3d..4d9370d232 100644
>>>> --- a/arch/arm/lib/spl.c
>>>> +++ b/arch/arm/lib/spl.c
>>>> @@ -73,3 +73,17 @@ void __noreturn jump_to_image_linux(struct spl_image_info *spl_image)
>>>>   }
>>>>   #endif	/* CONFIG_ARM64 */
>>>>   #endif
>>>> +
>>>> +u8 spl_genimg_get_arch_id(const char *arch_str)
>>>> +{
>>>> +	if (!arch_str)
>>>> +		return IH_ARCH_DEFAULT;
>>>> +
>>>> +	if (!strcmp(arch_str, "arm"))
>>>> +		return IH_ARCH_ARM;
>>>> +
>>>> +	if (!strcmp(arch_str, "arm64"))
>>>> +		return IH_ARCH_ARM64;
>>>> +
>>>> +	return IH_ARCH_DEFAULT;
>>>> +}
>>>> diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
>>>> index 43a93e3085..5a5e10a024 100644
>>>> --- a/arch/arm/mach-sunxi/Makefile
>>>> +++ b/arch/arm/mach-sunxi/Makefile
>>>> @@ -39,4 +39,7 @@ obj-$(CONFIG_SPL_SPI_SUNXI)	+= spl_spi_sunxi.o
>>>>   obj-$(CONFIG_SUNXI_DRAM_DW)	+= dram_sunxi_dw.o
>>>>   obj-$(CONFIG_SUNXI_DRAM_DW)	+= dram_timings/
>>>>   obj-$(CONFIG_DRAM_SUN50I_H6)	+= dram_sun50i_h6.o
>>>> +obj-$(CONFIG_MACH_SUN50I)	+= spl_switch.o
>>>> +obj-$(CONFIG_MACH_SUN50I_H5)	+= spl_switch.o
>>>> +obj-$(CONFIG_MACH_SUN50I_H6)	+= spl_switch.o
>>>>   endif
>>>> diff --git a/arch/arm/mach-sunxi/spl_switch.c b/arch/arm/mach-sunxi/spl_switch.c
>>>> new file mode 100644
>>>> index 0000000000..1de43c2396
>>>> --- /dev/null
>>>> +++ b/arch/arm/mach-sunxi/spl_switch.c
>>>> @@ -0,0 +1,64 @@
>>>> +/*
>>>> + * (C) Copyright 2016 ARM Ltd.
>>>> + *
>>>> + * SPDX-License-Identifier:     GPL-2.0+
>>>> + */
>>>> +
>>>> +#include <common.h>
>>>> +#include <spl.h>
>>>> +
>>>> +#include <asm/io.h>
>>>> +#include <asm/barriers.h>
>>>> +
>>>> +static void __noreturn jump_to_image_native(struct spl_image_info *spl_image)
>>>> +{
>>>> +	typedef void __noreturn (*image_entry_noargs_t)(void);
>>>> +
>>>> +	image_entry_noargs_t image_entry =
>>>> +				(image_entry_noargs_t)spl_image->entry_point;
>>>> +
>>>> +	image_entry();
>>>> +}
>>>> +
>>>> +static void __noreturn reset_rmr_switch(void)
>>>> +{
>>>> +#ifdef CONFIG_ARM64
>>>> +	__asm__ volatile ( "mrs	 x0, RMR_EL3\n\t"
>>>> +			   "bic  x0, x0, #1\n\t"   /* Clear enter-in-64 bit */
>>>> +			   "orr  x0, x0, #2\n\t"   /* set reset request bit */
>>>> +			   "msr  RMR_EL3, x0\n\t"
>>>> +			   "isb  sy\n\t"
>>>> +			   "nop\n\t"
>>>> +			   "wfi\n\t"
>>>> +			   "b    .\n"
>>>> +			   ::: "x0");
>>>> +#else
>>>> +	__asm__ volatile ( "mrc  15, 0, r0, cr12, cr0, 2\n\t"
>>>> +			   "orr  r0, r0, #3\n\t"   /* request reset in 64 bit */
>>>> +			   "mcr  15, 0, r0, cr12, cr0, 2\n\t"
>>>> +			   "isb\n\t"
>>>> +			   "nop\n\t"
>>>> +			   "wfi\n\t"
>>>> +			   "b    .\n"
>>>> +			   ::: "r0");
>>>> +#endif
>>>> +	while (1);	/* to avoid a compiler warning about __noreturn */
>>>> +}
>>>> +
>>>> +void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image)
>>>> +{
>>>> +	if (spl_image->arch == IH_ARCH_DEFAULT) {
>>>> +		debug("entering by branch\n");
>>>> +		jump_to_image_native(spl_image);
>>>> +	} else {
>>>> +		debug("entering by RMR switch\n");
>>>> +#ifdef CONFIG_MACH_SUN50I_H6
>>>> +		writel(spl_image->entry_point, 0x09010040);
>>>> +#else
>>>> +		writel(spl_image->entry_point, 0x017000a0);
>>>> +#endif
>>> How does the original entry point get preserved over this? Don't you
>>> have to set the reset vector register as well?
>> The RVBAR_ELx (Reset Vector Base Address Register) registers are
>> architecturally read only. Fortunately Allwinner decided to have an MMIO
>> mapped writable alias, which we exploit here. I *believe* this gets reset
>> on a hard reset (external signal, for instance via the watchdog) to the
>> initial 0x0 value.
>> 
>> Anyway we use this method already in the AArch64 SPL today [1], and it
>> happens to work ;-)
> 
> Ugh, I read writel() the wrong way around. Do you think you can give the constant a nice #define that makes it fully obvious what's going on here?

I have been preaching this for rockchip-changes: please use a “u32 * const” instead of a define...
The compiler has a typesystem, so let’s provide the necessary info to fully use it ;-)

Cheers,
Philipp.
diff mbox series

Patch

diff --git a/arch/arm/lib/spl.c b/arch/arm/lib/spl.c
index 33cc76ba3d..4d9370d232 100644
--- a/arch/arm/lib/spl.c
+++ b/arch/arm/lib/spl.c
@@ -73,3 +73,17 @@  void __noreturn jump_to_image_linux(struct spl_image_info *spl_image)
 }
 #endif	/* CONFIG_ARM64 */
 #endif
+
+u8 spl_genimg_get_arch_id(const char *arch_str)
+{
+	if (!arch_str)
+		return IH_ARCH_DEFAULT;
+
+	if (!strcmp(arch_str, "arm"))
+		return IH_ARCH_ARM;
+
+	if (!strcmp(arch_str, "arm64"))
+		return IH_ARCH_ARM64;
+
+	return IH_ARCH_DEFAULT;
+}
diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
index 43a93e3085..5a5e10a024 100644
--- a/arch/arm/mach-sunxi/Makefile
+++ b/arch/arm/mach-sunxi/Makefile
@@ -39,4 +39,7 @@  obj-$(CONFIG_SPL_SPI_SUNXI)	+= spl_spi_sunxi.o
 obj-$(CONFIG_SUNXI_DRAM_DW)	+= dram_sunxi_dw.o
 obj-$(CONFIG_SUNXI_DRAM_DW)	+= dram_timings/
 obj-$(CONFIG_DRAM_SUN50I_H6)	+= dram_sun50i_h6.o
+obj-$(CONFIG_MACH_SUN50I)	+= spl_switch.o
+obj-$(CONFIG_MACH_SUN50I_H5)	+= spl_switch.o
+obj-$(CONFIG_MACH_SUN50I_H6)	+= spl_switch.o
 endif
diff --git a/arch/arm/mach-sunxi/spl_switch.c b/arch/arm/mach-sunxi/spl_switch.c
new file mode 100644
index 0000000000..1de43c2396
--- /dev/null
+++ b/arch/arm/mach-sunxi/spl_switch.c
@@ -0,0 +1,64 @@ 
+/*
+ * (C) Copyright 2016 ARM Ltd.
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <spl.h>
+
+#include <asm/io.h>
+#include <asm/barriers.h>
+
+static void __noreturn jump_to_image_native(struct spl_image_info *spl_image)
+{
+	typedef void __noreturn (*image_entry_noargs_t)(void);
+
+	image_entry_noargs_t image_entry =
+				(image_entry_noargs_t)spl_image->entry_point;
+
+	image_entry();
+}
+
+static void __noreturn reset_rmr_switch(void)
+{
+#ifdef CONFIG_ARM64
+	__asm__ volatile ( "mrs	 x0, RMR_EL3\n\t"
+			   "bic  x0, x0, #1\n\t"   /* Clear enter-in-64 bit */
+			   "orr  x0, x0, #2\n\t"   /* set reset request bit */
+			   "msr  RMR_EL3, x0\n\t"
+			   "isb  sy\n\t"
+			   "nop\n\t"
+			   "wfi\n\t"
+			   "b    .\n"
+			   ::: "x0");
+#else
+	__asm__ volatile ( "mrc  15, 0, r0, cr12, cr0, 2\n\t"
+			   "orr  r0, r0, #3\n\t"   /* request reset in 64 bit */
+			   "mcr  15, 0, r0, cr12, cr0, 2\n\t"
+			   "isb\n\t"
+			   "nop\n\t"
+			   "wfi\n\t"
+			   "b    .\n"
+			   ::: "r0");
+#endif
+	while (1);	/* to avoid a compiler warning about __noreturn */
+}
+
+void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image)
+{
+	if (spl_image->arch == IH_ARCH_DEFAULT) {
+		debug("entering by branch\n");
+		jump_to_image_native(spl_image);
+	} else {
+		debug("entering by RMR switch\n");
+#ifdef CONFIG_MACH_SUN50I_H6
+		writel(spl_image->entry_point, 0x09010040);
+#else
+		writel(spl_image->entry_point, 0x017000a0);
+#endif
+		DSB;
+		ISB;
+		reset_rmr_switch();
+	}
+}