diff mbox

[3/3] ARM: at91: fix hanged boot

Message ID 1362747103-21445-4-git-send-email-jhovold@gmail.com
State Superseded
Headers show

Commit Message

Johan Hovold March 8, 2013, 12:51 p.m. UTC
Make sure the RTC and RTT-interrupts are masked at boot by adding a new
SOC-initialiser and helpers functions.

This fixes hanged boot on all AT91 SOCs but RM9200, for example, after a
reset during an RTC-update or if an RTC or RTT-alarm goes off after a
non-clean shutdown.

The RTC and RTT-peripherals are powered by backup power (VDDBU) (on all
AT91 SOCs but RM9200) and are not reset on wake-up, user, watchdog or
software reset. This means that their interrupts may be enabled during
early boot if, for example, they where not disabled during a previous
shutdown (e.g. due to a buggy driver or a non-clean shutdown such as a
user reset). Furthermore, an RTC or RTT-alarm may also be active.

The RTC and RTT-interrupts use the shared system-interrupt line, and if
an interrupt occurs before a handler (e.g. RTC-driver) has been
installed this leads to the system interrupt being disabled and prevents
the system from booting.

Note that when boot hangs due to an early RTC or RTT-interrupt, the only
way to get the system to start again is to remove the backup power (e.g.
battery) or to disable the interrupt manually from the bootloader. In
particular, a user reset is not sufficient.

Tested on at91sam9263 and at91sam9g45.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
---
 arch/arm/mach-at91/at91rm9200.c               |  9 ++++++++
 arch/arm/mach-at91/at91sam9260.c              |  6 ++++++
 arch/arm/mach-at91/at91sam9261.c              |  6 ++++++
 arch/arm/mach-at91/at91sam9263.c              |  7 ++++++
 arch/arm/mach-at91/at91sam9g45.c              |  7 ++++++
 arch/arm/mach-at91/at91sam9n12.c              |  6 ++++++
 arch/arm/mach-at91/at91sam9rl.c               |  7 ++++++
 arch/arm/mach-at91/at91sam9x5.c               |  6 ++++++
 arch/arm/mach-at91/generic.h                  |  2 ++
 arch/arm/mach-at91/include/mach/at91sam9n12.h |  5 +++++
 arch/arm/mach-at91/include/mach/at91sam9x5.h  |  5 +++++
 arch/arm/mach-at91/setup.c                    | 31 +++++++++++++++++++++++++++
 arch/arm/mach-at91/soc.h                      |  1 +
 13 files changed, 98 insertions(+)

Comments

Jean-Christophe PLAGNIOL-VILLARD March 8, 2013, 4:02 p.m. UTC | #1
On 13:51 Fri 08 Mar     , Johan Hovold wrote:
> Make sure the RTC and RTT-interrupts are masked at boot by adding a new
> SOC-initialiser and helpers functions.
> 
> This fixes hanged boot on all AT91 SOCs but RM9200, for example, after a
> reset during an RTC-update or if an RTC or RTT-alarm goes off after a
> non-clean shutdown.
> 
> The RTC and RTT-peripherals are powered by backup power (VDDBU) (on all
> AT91 SOCs but RM9200) and are not reset on wake-up, user, watchdog or
> software reset. This means that their interrupts may be enabled during
> early boot if, for example, they where not disabled during a previous
> shutdown (e.g. due to a buggy driver or a non-clean shutdown such as a
> user reset). Furthermore, an RTC or RTT-alarm may also be active.
> 
> The RTC and RTT-interrupts use the shared system-interrupt line, and if
> an interrupt occurs before a handler (e.g. RTC-driver) has been
> installed this leads to the system interrupt being disabled and prevents
> the system from booting.
> 
> Note that when boot hangs due to an early RTC or RTT-interrupt, the only
> way to get the system to start again is to remove the backup power (e.g.
> battery) or to disable the interrupt manually from the bootloader. In
> particular, a user reset is not sufficient.
> 
> Tested on at91sam9263 and at91sam9g45.
> 
> Signed-off-by: Johan Hovold <jhovold@gmail.com>
> ---
>  arch/arm/mach-at91/at91rm9200.c               |  9 ++++++++
>  arch/arm/mach-at91/at91sam9260.c              |  6 ++++++
>  arch/arm/mach-at91/at91sam9261.c              |  6 ++++++
>  arch/arm/mach-at91/at91sam9263.c              |  7 ++++++
>  arch/arm/mach-at91/at91sam9g45.c              |  7 ++++++
>  arch/arm/mach-at91/at91sam9n12.c              |  6 ++++++
>  arch/arm/mach-at91/at91sam9rl.c               |  7 ++++++
>  arch/arm/mach-at91/at91sam9x5.c               |  6 ++++++
>  arch/arm/mach-at91/generic.h                  |  2 ++
>  arch/arm/mach-at91/include/mach/at91sam9n12.h |  5 +++++
>  arch/arm/mach-at91/include/mach/at91sam9x5.h  |  5 +++++
nack for DT probe te address via DT
>  arch/arm/mach-at91/setup.c                    | 31 +++++++++++++++++++++++++++
>  arch/arm/mach-at91/soc.h                      |  1 +
>  13 files changed, 98 insertions(+)

at boot time we can disable all the irq as we need none of them
> 
> diff --git a/arch/arm/mach-at91/at91rm9200.c b/arch/arm/mach-at91/at91rm9200.c
> index 7aeb473..4651ebb 100644
> --- a/arch/arm/mach-at91/at91rm9200.c
> +++ b/arch/arm/mach-at91/at91rm9200.c
> @@ -325,6 +325,14 @@ static void __init at91rm9200_ioremap_registers(void)
>  	at91_ioremap_ramc(0, AT91RM9200_BASE_MC, 256);
>  }
>  
> +static void __init at91rm9200_sysirq_mask(void)
> +{
> +	/*
> +	 * No need to mask any system interrupts as the RM9200 has no backup
> +	 * power and resets all system peripherals at every reset.
> +	 */
> +}
no need drop it
> +
>  static void __init at91rm9200_initialize(void)
>  {
>  	arm_pm_idle = at91rm9200_idle;
> @@ -387,5 +395,6 @@ AT91_SOC_START(rm9200)
>  	.default_irq_priority = at91rm9200_default_irq_priority,
>  	.ioremap_registers = at91rm9200_ioremap_registers,
>  	.register_clocks = at91rm9200_register_clocks,
> +	.sysirq_mask = at91rm9200_sysirq_mask,
>  	.init = at91rm9200_initialize,
>  AT91_SOC_END
> diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c
> index b67cd53..c47a0db 100644
> --- a/arch/arm/mach-at91/at91sam9260.c
> +++ b/arch/arm/mach-at91/at91sam9260.c
> @@ -342,6 +342,11 @@ static void __init at91sam9260_ioremap_registers(void)
>  	at91_ioremap_matrix(AT91SAM9260_BASE_MATRIX);
>  }
>  
> +static void __init at91sam9260_sysirq_mask(void)
> +{
> +	at91_sysirq_mask_rtt(AT91SAM9260_BASE_RTT);
> +}
> +
>  static void __init at91sam9260_initialize(void)
>  {
>  	arm_pm_idle = at91sam9_idle;
> @@ -400,5 +405,6 @@ AT91_SOC_START(sam9260)
>  	.default_irq_priority = at91sam9260_default_irq_priority,
>  	.ioremap_registers = at91sam9260_ioremap_registers,
>  	.register_clocks = at91sam9260_register_clocks,
> +	.sysirq_mask = at91sam9260_sysirq_mask,
>  	.init = at91sam9260_initialize,
>  AT91_SOC_END
> diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c
> index 0204f4c..396e4cb 100644
> --- a/arch/arm/mach-at91/at91sam9261.c
> +++ b/arch/arm/mach-at91/at91sam9261.c
> @@ -286,6 +286,11 @@ static void __init at91sam9261_ioremap_registers(void)
>  	at91_ioremap_matrix(AT91SAM9261_BASE_MATRIX);
>  }
>  
> +static void __init at91sam9261_sysirq_mask(void)
> +{
> +	at91_sysirq_mask_rtt(AT91SAM9261_BASE_RTT);
> +}
> +
>  static void __init at91sam9261_initialize(void)
>  {
>  	arm_pm_idle = at91sam9_idle;
> @@ -344,5 +349,6 @@ AT91_SOC_START(sam9261)
>  	.default_irq_priority = at91sam9261_default_irq_priority,
>  	.ioremap_registers = at91sam9261_ioremap_registers,
>  	.register_clocks = at91sam9261_register_clocks,
> +	.sysirq_mask = at91sam9261_sysirq_mask,
>  	.init = at91sam9261_initialize,
>  AT91_SOC_END
> diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c
> index c0cbb81..a0166a3 100644
> --- a/arch/arm/mach-at91/at91sam9263.c
> +++ b/arch/arm/mach-at91/at91sam9263.c
> @@ -325,6 +325,12 @@ static void __init at91sam9263_ioremap_registers(void)
>  	at91_ioremap_matrix(AT91SAM9263_BASE_MATRIX);
>  }
>  
> +static void __init at91sam9263_sysirq_mask(void)
> +{
> +	at91_sysirq_mask_rtt(AT91SAM9263_BASE_RTT0);
> +	at91_sysirq_mask_rtt(AT91SAM9263_BASE_RTT1);
> +}
> +
>  static void __init at91sam9263_initialize(void)
>  {
>  	arm_pm_idle = at91sam9_idle;
> @@ -382,5 +388,6 @@ AT91_SOC_START(sam9263)
>  	.default_irq_priority = at91sam9263_default_irq_priority,
>  	.ioremap_registers = at91sam9263_ioremap_registers,
>  	.register_clocks = at91sam9263_register_clocks,
> +	.sysirq_mask = at91sam9263_sysirq_mask,
>  	.init = at91sam9263_initialize,
>  AT91_SOC_END
> diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c
> index b4968aa..3d5498e 100644
> --- a/arch/arm/mach-at91/at91sam9g45.c
> +++ b/arch/arm/mach-at91/at91sam9g45.c
> @@ -370,6 +370,12 @@ static void __init at91sam9g45_ioremap_registers(void)
>  	at91_ioremap_matrix(AT91SAM9G45_BASE_MATRIX);
>  }
>  
> +static void __init at91sam9g45_sysirq_mask(void)
> +{
> +	at91_sysirq_mask_rtc(AT91SAM9G45_BASE_RTC);
> +	at91_sysirq_mask_rtt(AT91SAM9G45_BASE_RTT);
> +}
> +
>  static void __init at91sam9g45_initialize(void)
>  {
>  	arm_pm_idle = at91sam9_idle;
> @@ -427,5 +433,6 @@ AT91_SOC_START(sam9g45)
>  	.default_irq_priority = at91sam9g45_default_irq_priority,
>  	.ioremap_registers = at91sam9g45_ioremap_registers,
>  	.register_clocks = at91sam9g45_register_clocks,
> +	.sysirq_mask = at91sam9g45_sysirq_mask,
>  	.init = at91sam9g45_initialize,
>  AT91_SOC_END
> diff --git a/arch/arm/mach-at91/at91sam9n12.c b/arch/arm/mach-at91/at91sam9n12.c
> index 5dfc8fd..e2ddb89 100644
> --- a/arch/arm/mach-at91/at91sam9n12.c
> +++ b/arch/arm/mach-at91/at91sam9n12.c
> @@ -221,6 +221,11 @@ static void __init at91sam9n12_map_io(void)
>  	at91_init_sram(0, AT91SAM9N12_SRAM_BASE, AT91SAM9N12_SRAM_SIZE);
>  }
>  
> +static void __init at91sam9n12_sysirq_mask(void)
> +{
> +	at91_sysirq_mask_rtc(AT91SAM9N12_BASE_RTC);
> +}
> +
>  void __init at91sam9n12_initialize(void)
>  {
>  	at91_extern_irq = (1 << AT91SAM9N12_ID_IRQ0);
> @@ -229,5 +234,6 @@ void __init at91sam9n12_initialize(void)
>  AT91_SOC_START(sam9n12)
>  	.map_io = at91sam9n12_map_io,
>  	.register_clocks = at91sam9n12_register_clocks,
> +	.sysirq_mask = at91sam9n12_sysirq_mask,
>  	.init = at91sam9n12_initialize,
>  AT91_SOC_END
> diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c
> index 3de3e04..bde999f 100644
> --- a/arch/arm/mach-at91/at91sam9rl.c
> +++ b/arch/arm/mach-at91/at91sam9rl.c
> @@ -289,6 +289,12 @@ static void __init at91sam9rl_ioremap_registers(void)
>  	at91_ioremap_matrix(AT91SAM9RL_BASE_MATRIX);
>  }
>  
> +static void __init at91sam9rl_sysirq_mask(void)
> +{
> +	at91_sysirq_mask_rtc(AT91SAM9RL_BASE_RTC);
> +	at91_sysirq_mask_rtt(AT91SAM9RL_BASE_RTT);
> +}
> +
>  static void __init at91sam9rl_initialize(void)
>  {
>  	arm_pm_idle = at91sam9_idle;
> @@ -346,5 +352,6 @@ AT91_SOC_START(sam9rl)
>  	.default_irq_priority = at91sam9rl_default_irq_priority,
>  	.ioremap_registers = at91sam9rl_ioremap_registers,
>  	.register_clocks = at91sam9rl_register_clocks,
> +	.sysirq_mask = at91sam9rl_sysirq_mask,
>  	.init = at91sam9rl_initialize,
>  AT91_SOC_END
> diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c
> index 44a9a62..20a214f 100644
> --- a/arch/arm/mach-at91/at91sam9x5.c
> +++ b/arch/arm/mach-at91/at91sam9x5.c
> @@ -316,6 +316,11 @@ static void __init at91sam9x5_map_io(void)
>  	at91_init_sram(0, AT91SAM9X5_SRAM_BASE, AT91SAM9X5_SRAM_SIZE);
>  }
>  
> +static void __init at91sam9x5_sysirq_mask(void)
> +{
> +	at91_sysirq_mask_rtc(AT91SAM9X5_BASE_RTC);
> +}
> +
>  /* --------------------------------------------------------------------
>   *  Interrupt initialization
>   * -------------------------------------------------------------------- */
> @@ -323,4 +328,5 @@ static void __init at91sam9x5_map_io(void)
>  AT91_SOC_START(sam9x5)
>  	.map_io = at91sam9x5_map_io,
>  	.register_clocks = at91sam9x5_register_clocks,
> +	.sysirq_mask = at91sam9x5_sysirq_mask,
>  AT91_SOC_END
> diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h
> index fc593d6..90a854d 100644
> --- a/arch/arm/mach-at91/generic.h
> +++ b/arch/arm/mach-at91/generic.h
> @@ -33,6 +33,8 @@ extern int  __init at91_aic_of_init(struct device_node *node,
>  				    struct device_node *parent);
>  extern int  __init at91_aic5_of_init(struct device_node *node,
>  				    struct device_node *parent);
> +extern void __init at91_sysirq_mask_rtc(u32 rtc_base);
> +extern void __init at91_sysirq_mask_rtt(u32 rtt_base);
>  
>  
>   /* Timer */
> diff --git a/arch/arm/mach-at91/include/mach/at91sam9n12.h b/arch/arm/mach-at91/include/mach/at91sam9n12.h
> index d374b87..0151bcf 100644
> --- a/arch/arm/mach-at91/include/mach/at91sam9n12.h
> +++ b/arch/arm/mach-at91/include/mach/at91sam9n12.h
> @@ -49,6 +49,11 @@
>  #define AT91SAM9N12_BASE_USART3	0xf8028000
>  
>  /*
> + * System Peripherals
> + */
> +#define AT91SAM9N12_BASE_RTC	0xfffffeb0
> +
> +/*
>   * Internal Memory.
>   */
>  #define AT91SAM9N12_SRAM_BASE	0x00300000	/* Internal SRAM base address */
> diff --git a/arch/arm/mach-at91/include/mach/at91sam9x5.h b/arch/arm/mach-at91/include/mach/at91sam9x5.h
> index c75ee19..2fc76c4 100644
> --- a/arch/arm/mach-at91/include/mach/at91sam9x5.h
> +++ b/arch/arm/mach-at91/include/mach/at91sam9x5.h
> @@ -55,6 +55,11 @@
>  #define AT91SAM9X5_BASE_USART2	0xf8024000
>  
>  /*
> + * System Peripherals
> + */
> +#define AT91SAM9X5_BASE_RTC	0xfffffeb0
> +
> +/*
>   * Internal Memory.
>   */
>  #define AT91SAM9X5_SRAM_BASE	0x00300000	/* Internal SRAM base address */
> diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c
> index 4b67847..ac05453 100644
> --- a/arch/arm/mach-at91/setup.c
> +++ b/arch/arm/mach-at91/setup.c
> @@ -19,6 +19,8 @@
>  #include <mach/cpu.h>
>  #include <mach/at91_dbgu.h>
>  #include <mach/at91_pmc.h>
> +#include <mach/at91_rtc.h>
> +#include <mach/at91_rtt.h>
>  
>  #include "at91_shdwc.h"
>  #include "soc.h"
> @@ -311,6 +313,31 @@ void __init at91_ioremap_matrix(u32 base_addr)
>  		panic("Impossible to ioremap at91_matrix_base\n");
>  }
>  
> +void __init at91_sysirq_mask_rtc(u32 rtc_base)
> +{
> +	void *base = AT91_IO_P2V(rtc_base);
> +	u32 mask;
> +
> +	mask = __raw_readl(base + AT91_RTC_IMR);
> +	if (mask) {
> +		pr_info("AT91: Disabling rtc irq\n");
> +		__raw_writel(mask, base + AT91_RTC_IDR);
> +	}
> +}
> +
> +void __init at91_sysirq_mask_rtt(u32 rtt_base)
> +{
> +	void *base = AT91_IO_P2V(rtt_base);
> +	u32 mr;
> +
> +	mr = __raw_readl(base + AT91_RTT_MR);
> +	if (mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)) {
> +		pr_info("AT91: Disabling rtt irq\n");
> +		mr &= ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN);
> +		__raw_writel(mr, base + AT91_RTT_MR);
> +	}
> +}
> +
>  #if defined(CONFIG_OF)
>  static struct of_device_id rstc_ids[] = {
>  	{ .compatible = "atmel,at91sam9260-rstc", .data = at91sam9_alt_restart },
> @@ -467,6 +494,8 @@ void __init at91_dt_initialize(void)
>  
>  	if (at91_boot_soc.init)
>  		at91_boot_soc.init();
> +
> +	at91_boot_soc.sysirq_mask();
>  }
>  #endif
>  
> @@ -482,5 +511,7 @@ void __init at91_initialize(unsigned long main_clock)
>  
>  	at91_boot_soc.init();
>  
> +	at91_boot_soc.sysirq_mask();
> +
>  	pinctrl_provide_dummies();
>  }
> diff --git a/arch/arm/mach-at91/soc.h b/arch/arm/mach-at91/soc.h
> index 9c6d3d4..134f4c4 100644
> --- a/arch/arm/mach-at91/soc.h
> +++ b/arch/arm/mach-at91/soc.h
> @@ -10,6 +10,7 @@ struct at91_init_soc {
>  	void (*map_io)(void);
>  	void (*ioremap_registers)(void);
>  	void (*register_clocks)(void);
> +	void (*sysirq_mask)(void);
>  	void (*init)(void);
>  };
>  
> -- 
> 1.8.1.1
> 
> -- 
> -- 
> You received this message because you are subscribed to "rtc-linux".
> Membership options at http://groups.google.com/group/rtc-linux .
> Please read http://groups.google.com/group/rtc-linux/web/checklist
> before submitting a driver.
> --- 
> You received this message because you are subscribed to the Google Groups "rtc-linux" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
> 
>
Johan Hovold March 11, 2013, 10:02 a.m. UTC | #2
On Fri, Mar 08, 2013 at 05:02:58PM +0100, Jean-Christophe PLAGNIOL-VILLARD wrote:
> On 13:51 Fri 08 Mar     , Johan Hovold wrote:
> > Make sure the RTC and RTT-interrupts are masked at boot by adding a new
> > SOC-initialiser and helpers functions.
> > 
> > This fixes hanged boot on all AT91 SOCs but RM9200, for example, after a
> > reset during an RTC-update or if an RTC or RTT-alarm goes off after a
> > non-clean shutdown.
> > 
> > The RTC and RTT-peripherals are powered by backup power (VDDBU) (on all
> > AT91 SOCs but RM9200) and are not reset on wake-up, user, watchdog or
> > software reset. This means that their interrupts may be enabled during
> > early boot if, for example, they where not disabled during a previous
> > shutdown (e.g. due to a buggy driver or a non-clean shutdown such as a
> > user reset). Furthermore, an RTC or RTT-alarm may also be active.
> > 
> > The RTC and RTT-interrupts use the shared system-interrupt line, and if
> > an interrupt occurs before a handler (e.g. RTC-driver) has been
> > installed this leads to the system interrupt being disabled and prevents
> > the system from booting.
> > 
> > Note that when boot hangs due to an early RTC or RTT-interrupt, the only
> > way to get the system to start again is to remove the backup power (e.g.
> > battery) or to disable the interrupt manually from the bootloader. In
> > particular, a user reset is not sufficient.
> > 
> > Tested on at91sam9263 and at91sam9g45.
> > 
> > Signed-off-by: Johan Hovold <jhovold@gmail.com>
> > ---
> >  arch/arm/mach-at91/at91rm9200.c               |  9 ++++++++
> >  arch/arm/mach-at91/at91sam9260.c              |  6 ++++++
> >  arch/arm/mach-at91/at91sam9261.c              |  6 ++++++
> >  arch/arm/mach-at91/at91sam9263.c              |  7 ++++++
> >  arch/arm/mach-at91/at91sam9g45.c              |  7 ++++++
> >  arch/arm/mach-at91/at91sam9n12.c              |  6 ++++++
> >  arch/arm/mach-at91/at91sam9rl.c               |  7 ++++++
> >  arch/arm/mach-at91/at91sam9x5.c               |  6 ++++++
> >  arch/arm/mach-at91/generic.h                  |  2 ++
> >  arch/arm/mach-at91/include/mach/at91sam9n12.h |  5 +++++
> >  arch/arm/mach-at91/include/mach/at91sam9x5.h  |  5 +++++
> nack for DT probe te address via DT

Fair enough. I'll respin and add proper DT-support. 

> >  arch/arm/mach-at91/setup.c                    | 31 +++++++++++++++++++++++++++
> >  arch/arm/mach-at91/soc.h                      |  1 +
> >  13 files changed, 98 insertions(+)
> 
> at boot time we can disable all the irq as we need none of them

Yes, but all but the VDDBU-powered-peripheral ones will already have
been disabled at reset. If a buggy bootloader enables something it
should not, then the bootloader should be fixed.

> > diff --git a/arch/arm/mach-at91/at91rm9200.c b/arch/arm/mach-at91/at91rm9200.c
> > index 7aeb473..4651ebb 100644
> > --- a/arch/arm/mach-at91/at91rm9200.c
> > +++ b/arch/arm/mach-at91/at91rm9200.c
> > @@ -325,6 +325,14 @@ static void __init at91rm9200_ioremap_registers(void)
> >  	at91_ioremap_ramc(0, AT91RM9200_BASE_MC, 256);
> >  }
> >  
> > +static void __init at91rm9200_sysirq_mask(void)
> > +{
> > +	/*
> > +	 * No need to mask any system interrupts as the RM9200 has no backup
> > +	 * power and resets all system peripherals at every reset.
> > +	 */
> > +}
> no need drop it

Ok.

Thanks,
Johan

> > +
> >  static void __init at91rm9200_initialize(void)
> >  {
> >  	arm_pm_idle = at91rm9200_idle;
> > @@ -387,5 +395,6 @@ AT91_SOC_START(rm9200)
> >  	.default_irq_priority = at91rm9200_default_irq_priority,
> >  	.ioremap_registers = at91rm9200_ioremap_registers,
> >  	.register_clocks = at91rm9200_register_clocks,
> > +	.sysirq_mask = at91rm9200_sysirq_mask,
> >  	.init = at91rm9200_initialize,
> >  AT91_SOC_END
Jean-Christophe PLAGNIOL-VILLARD March 11, 2013, 11:06 a.m. UTC | #3
On 11:02 Mon 11 Mar     , Johan Hovold wrote:
> On Fri, Mar 08, 2013 at 05:02:58PM +0100, Jean-Christophe PLAGNIOL-VILLARD wrote:
> > On 13:51 Fri 08 Mar     , Johan Hovold wrote:
> > > Make sure the RTC and RTT-interrupts are masked at boot by adding a new
> > > SOC-initialiser and helpers functions.
> > > 
> > > This fixes hanged boot on all AT91 SOCs but RM9200, for example, after a
> > > reset during an RTC-update or if an RTC or RTT-alarm goes off after a
> > > non-clean shutdown.
> > > 
> > > The RTC and RTT-peripherals are powered by backup power (VDDBU) (on all
> > > AT91 SOCs but RM9200) and are not reset on wake-up, user, watchdog or
> > > software reset. This means that their interrupts may be enabled during
> > > early boot if, for example, they where not disabled during a previous
> > > shutdown (e.g. due to a buggy driver or a non-clean shutdown such as a
> > > user reset). Furthermore, an RTC or RTT-alarm may also be active.
> > > 
> > > The RTC and RTT-interrupts use the shared system-interrupt line, and if
> > > an interrupt occurs before a handler (e.g. RTC-driver) has been
> > > installed this leads to the system interrupt being disabled and prevents
> > > the system from booting.
> > > 
> > > Note that when boot hangs due to an early RTC or RTT-interrupt, the only
> > > way to get the system to start again is to remove the backup power (e.g.
> > > battery) or to disable the interrupt manually from the bootloader. In
> > > particular, a user reset is not sufficient.
> > > 
> > > Tested on at91sam9263 and at91sam9g45.
> > > 
> > > Signed-off-by: Johan Hovold <jhovold@gmail.com>
> > > ---
> > >  arch/arm/mach-at91/at91rm9200.c               |  9 ++++++++
> > >  arch/arm/mach-at91/at91sam9260.c              |  6 ++++++
> > >  arch/arm/mach-at91/at91sam9261.c              |  6 ++++++
> > >  arch/arm/mach-at91/at91sam9263.c              |  7 ++++++
> > >  arch/arm/mach-at91/at91sam9g45.c              |  7 ++++++
> > >  arch/arm/mach-at91/at91sam9n12.c              |  6 ++++++
> > >  arch/arm/mach-at91/at91sam9rl.c               |  7 ++++++
> > >  arch/arm/mach-at91/at91sam9x5.c               |  6 ++++++
> > >  arch/arm/mach-at91/generic.h                  |  2 ++
> > >  arch/arm/mach-at91/include/mach/at91sam9n12.h |  5 +++++
> > >  arch/arm/mach-at91/include/mach/at91sam9x5.h  |  5 +++++
> > nack for DT probe te address via DT
> 
> Fair enough. I'll respin and add proper DT-support. 
> 
> > >  arch/arm/mach-at91/setup.c                    | 31 +++++++++++++++++++++++++++
> > >  arch/arm/mach-at91/soc.h                      |  1 +
> > >  13 files changed, 98 insertions(+)
> > 
> > at boot time we can disable all the irq as we need none of them
> 
> Yes, but all but the VDDBU-powered-peripheral ones will already have
> been disabled at reset. If a buggy bootloader enables something it
> should not, then the bootloader should be fixed.
so fix the bootloader NACK in the kernel
it's too much ugly

the kernel requirere the interrupt to be disabled

Best Regards,
J.
Johan Hovold March 11, 2013, 6:06 p.m. UTC | #4
On Mon, Mar 11, 2013 at 12:06:37PM +0100, Jean-Christophe PLAGNIOL-VILLARD wrote:
> On 11:02 Mon 11 Mar     , Johan Hovold wrote:
> > On Fri, Mar 08, 2013 at 05:02:58PM +0100, Jean-Christophe PLAGNIOL-VILLARD wrote:
> > > On 13:51 Fri 08 Mar     , Johan Hovold wrote:
> > > > Make sure the RTC and RTT-interrupts are masked at boot by adding a new
> > > > SOC-initialiser and helpers functions.
> > > > 
> > > > This fixes hanged boot on all AT91 SOCs but RM9200, for example, after a
> > > > reset during an RTC-update or if an RTC or RTT-alarm goes off after a
> > > > non-clean shutdown.
> > > > 
> > > > The RTC and RTT-peripherals are powered by backup power (VDDBU) (on all
> > > > AT91 SOCs but RM9200) and are not reset on wake-up, user, watchdog or
> > > > software reset. This means that their interrupts may be enabled during
> > > > early boot if, for example, they where not disabled during a previous
> > > > shutdown (e.g. due to a buggy driver or a non-clean shutdown such as a
> > > > user reset). Furthermore, an RTC or RTT-alarm may also be active.
> > > > 
> > > > The RTC and RTT-interrupts use the shared system-interrupt line, and if
> > > > an interrupt occurs before a handler (e.g. RTC-driver) has been
> > > > installed this leads to the system interrupt being disabled and prevents
> > > > the system from booting.
> > > > 
> > > > Note that when boot hangs due to an early RTC or RTT-interrupt, the only
> > > > way to get the system to start again is to remove the backup power (e.g.
> > > > battery) or to disable the interrupt manually from the bootloader. In
> > > > particular, a user reset is not sufficient.
> > > > 
> > > > Tested on at91sam9263 and at91sam9g45.
> > > > 
> > > > Signed-off-by: Johan Hovold <jhovold@gmail.com>
> > > > ---
> > > >  arch/arm/mach-at91/at91rm9200.c               |  9 ++++++++
> > > >  arch/arm/mach-at91/at91sam9260.c              |  6 ++++++
> > > >  arch/arm/mach-at91/at91sam9261.c              |  6 ++++++
> > > >  arch/arm/mach-at91/at91sam9263.c              |  7 ++++++
> > > >  arch/arm/mach-at91/at91sam9g45.c              |  7 ++++++
> > > >  arch/arm/mach-at91/at91sam9n12.c              |  6 ++++++
> > > >  arch/arm/mach-at91/at91sam9rl.c               |  7 ++++++
> > > >  arch/arm/mach-at91/at91sam9x5.c               |  6 ++++++
> > > >  arch/arm/mach-at91/generic.h                  |  2 ++
> > > >  arch/arm/mach-at91/include/mach/at91sam9n12.h |  5 +++++
> > > >  arch/arm/mach-at91/include/mach/at91sam9x5.h  |  5 +++++
> > > nack for DT probe te address via DT
> > 
> > Fair enough. I'll respin and add proper DT-support. 
> > 
> > > >  arch/arm/mach-at91/setup.c                    | 31 +++++++++++++++++++++++++++
> > > >  arch/arm/mach-at91/soc.h                      |  1 +
> > > >  13 files changed, 98 insertions(+)
> > > 
> > > at boot time we can disable all the irq as we need none of them
> > 
> > Yes, but all but the VDDBU-powered-peripheral ones will already have
> > been disabled at reset. If a buggy bootloader enables something it
> > should not, then the bootloader should be fixed.
> so fix the bootloader NACK in the kernel

If it had been enabled by the bootloader, it would no doubt be a
bootloader bug. The problem is that bootloader may not know anything
about RTT/RTC and it was Linux that enabled the interrupts in the first
place. The question is whether it should still be the bootloader's
responsibility to clean it up.

> it's too much ugly

Agreed, but this needs to be fixed somewhere.

> the kernel requirere the interrupt to be disabled

And this is the problem -- the interrupts _are_ disabled when the kernel
is executed. It is Linux that enables the system interrupt without
making sure it can handle the interrupts that could arrive.

But sure, I can see arguments in favor of either solution (fix in
bootloader or kernel).

It is a severe bug, which affects all AT91 systems (but RM9200). It's
fairly hard to track down, and this has apparently already been done
over and over again. In my opinion, fixing it once and for all in the
kernel has some appeal.

To give an idea of what it could look like, I'm responding to this mail
with with a v2 of my series with added DT-support (the DT-bits are
compile-only tested).

Thanks,
Johan
Johan Hovold March 11, 2013, 6:07 p.m. UTC | #5
These patches fix a few severe issues affecting most AT91 SOCs where
boot can hang after a non-general reset, and where the only way to get
the system booting again is to do a general reset -- something which
could require physically removing any backup battery.

The problems stem from the fact that the RTC and RTT-peripherals are
powered by backup power (VDDBU) and are not reset on wake-up, user,
watchdog or software reset. Consequently, RTC and RTT-alarms and their
interrupts may be enabled at boot, leading to a system lock-up when an
interrupt arrives on the shared system-interrupt line before the
appropriate handler (e.g. RTC-driver) has been installed.

The easiest way to trigger this is to simply wake up from an RTC-alarm
on at91sam9g45. The RTC-driver currently does not disable interrupts at
shutdown so even after a clean shut-down the system will always hang
after waking up.

The first patch fixes this very general case of RTC-wake up after a
clean shutdown in the RTC-driver and is marked for stable as it is
perfectly straight-forward. [ Note that the other, RTT-based, AT91
RTC-driver already disables its interrupts at shutdown. ]

The more general problem can be triggered, for example, by doing a
user-reset while updating the RTC-time or if an RTC or RTT-alarm goes
off after a non-clean shutdown.

To fix this I propose that arch-code should mask the relevant interrupts
before enabling the system interrupt at early boot, and this is what
the fifth patch does. To access the RTC-registers I choose to revert a
recent patch that moved the register definitions to drivers/rtc.

Arguably, the relevant interrupts could also be disabled in bootloaders,
but I suggest fixing it in the kernel once and for all.

The patches have been tested on at91sam9263 and at91sam9g45 (non-DT),
and compile-tested for the other SOCs and DT.

Johan


v2:
 - add DT-support
 - make sys_irq_mask non-mandatory


Johan Hovold (5):
  ARM: at91/rtc: fix boot after RTC wake-up
  ARM: at91/dts: add RTC nodes
  ARM: at91/dts: add RTT nodes
  Revert "arm: at91: move at91rm9200 rtc header in drivers/rtc"
  ARM: at91: fix hanged boot

 arch/arm/boot/dts/at91rm9200.dtsi          |  5 ++
 arch/arm/boot/dts/at91sam9260.dtsi         |  5 ++
 arch/arm/boot/dts/at91sam9263.dtsi         | 10 ++++
 arch/arm/boot/dts/at91sam9g45.dtsi         | 10 ++++
 arch/arm/boot/dts/at91sam9n12.dtsi         |  5 ++
 arch/arm/boot/dts/at91sam9x5.dtsi          |  5 ++
 arch/arm/mach-at91/at91sam9260.c           |  6 ++
 arch/arm/mach-at91/at91sam9261.c           |  6 ++
 arch/arm/mach-at91/at91sam9263.c           |  7 +++
 arch/arm/mach-at91/at91sam9g45.c           |  7 +++
 arch/arm/mach-at91/at91sam9rl.c            |  7 +++
 arch/arm/mach-at91/generic.h               |  2 +
 arch/arm/mach-at91/include/mach/at91_rtc.h | 75 +++++++++++++++++++++++++
 arch/arm/mach-at91/setup.c                 | 88 ++++++++++++++++++++++++++++++
 arch/arm/mach-at91/soc.h                   |  1 +
 drivers/rtc/rtc-at91rm9200.c               | 11 +++-
 drivers/rtc/rtc-at91rm9200.h               | 75 -------------------------
 17 files changed, 249 insertions(+), 76 deletions(-)
 create mode 100644 arch/arm/mach-at91/include/mach/at91_rtc.h
 delete mode 100644 drivers/rtc/rtc-at91rm9200.h
Johan Hovold April 11, 2013, 3:55 p.m. UTC | #6
On Mon, Mar 11, 2013 at 07:07:54PM +0100, Johan Hovold wrote:
> These patches fix a few severe issues affecting most AT91 SOCs where
> boot can hang after a non-general reset, and where the only way to get
> the system booting again is to do a general reset -- something which
> could require physically removing any backup battery.

Have you had time to look at these patches yet, Nicolas?

I don't think not having decided on the path forward for DT-support for
rtc-at91sam9 needs to be a blocker. The rtt-nodes will be needed in any
case.

I could respin the series on top of the DT-patch for rtc-at91rm9200, and
add interrupt and status-disabled properties to the DT-nodes as well.

Thanks,
Johan

> The problems stem from the fact that the RTC and RTT-peripherals are
> powered by backup power (VDDBU) and are not reset on wake-up, user,
> watchdog or software reset. Consequently, RTC and RTT-alarms and their
> interrupts may be enabled at boot, leading to a system lock-up when an
> interrupt arrives on the shared system-interrupt line before the
> appropriate handler (e.g. RTC-driver) has been installed.
> 
> The easiest way to trigger this is to simply wake up from an RTC-alarm
> on at91sam9g45. The RTC-driver currently does not disable interrupts at
> shutdown so even after a clean shut-down the system will always hang
> after waking up.
> 
> The first patch fixes this very general case of RTC-wake up after a
> clean shutdown in the RTC-driver and is marked for stable as it is
> perfectly straight-forward. [ Note that the other, RTT-based, AT91
> RTC-driver already disables its interrupts at shutdown. ]
> 
> The more general problem can be triggered, for example, by doing a
> user-reset while updating the RTC-time or if an RTC or RTT-alarm goes
> off after a non-clean shutdown.
> 
> To fix this I propose that arch-code should mask the relevant interrupts
> before enabling the system interrupt at early boot, and this is what
> the fifth patch does. To access the RTC-registers I choose to revert a
> recent patch that moved the register definitions to drivers/rtc.
> 
> Arguably, the relevant interrupts could also be disabled in bootloaders,
> but I suggest fixing it in the kernel once and for all.
> 
> The patches have been tested on at91sam9263 and at91sam9g45 (non-DT),
> and compile-tested for the other SOCs and DT.
> 
> Johan
> 
> 
> v2:
>  - add DT-support
>  - make sys_irq_mask non-mandatory
> 
> 
> Johan Hovold (5):
>   ARM: at91/rtc: fix boot after RTC wake-up
>   ARM: at91/dts: add RTC nodes
>   ARM: at91/dts: add RTT nodes
>   Revert "arm: at91: move at91rm9200 rtc header in drivers/rtc"
>   ARM: at91: fix hanged boot
Jean-Christophe PLAGNIOL-VILLARD April 11, 2013, 4:54 p.m. UTC | #7
On 17:55 Thu 11 Apr     , Johan Hovold wrote:
> On Mon, Mar 11, 2013 at 07:07:54PM +0100, Johan Hovold wrote:
> > These patches fix a few severe issues affecting most AT91 SOCs where
> > boot can hang after a non-general reset, and where the only way to get
> > the system booting again is to do a general reset -- something which
> > could require physically removing any backup battery.
> 
> Have you had time to look at these patches yet, Nicolas?
> 
> I don't think not having decided on the path forward for DT-support for
> rtc-at91sam9 needs to be a blocker. The rtt-nodes will be needed in any
> case.
> 
> I could respin the series on top of the DT-patch for rtc-at91rm9200, and
> add interrupt and status-disabled properties to the DT-nodes as well.
for this this is still a no go

this way too much ugly

Best Regards,
J.
> 
> Thanks,
> Johan
> 
> > The problems stem from the fact that the RTC and RTT-peripherals are
> > powered by backup power (VDDBU) and are not reset on wake-up, user,
> > watchdog or software reset. Consequently, RTC and RTT-alarms and their
> > interrupts may be enabled at boot, leading to a system lock-up when an
> > interrupt arrives on the shared system-interrupt line before the
> > appropriate handler (e.g. RTC-driver) has been installed.
> > 
> > The easiest way to trigger this is to simply wake up from an RTC-alarm
> > on at91sam9g45. The RTC-driver currently does not disable interrupts at
> > shutdown so even after a clean shut-down the system will always hang
> > after waking up.
> > 
> > The first patch fixes this very general case of RTC-wake up after a
> > clean shutdown in the RTC-driver and is marked for stable as it is
> > perfectly straight-forward. [ Note that the other, RTT-based, AT91
> > RTC-driver already disables its interrupts at shutdown. ]
> > 
> > The more general problem can be triggered, for example, by doing a
> > user-reset while updating the RTC-time or if an RTC or RTT-alarm goes
> > off after a non-clean shutdown.
> > 
> > To fix this I propose that arch-code should mask the relevant interrupts
> > before enabling the system interrupt at early boot, and this is what
> > the fifth patch does. To access the RTC-registers I choose to revert a
> > recent patch that moved the register definitions to drivers/rtc.
> > 
> > Arguably, the relevant interrupts could also be disabled in bootloaders,
> > but I suggest fixing it in the kernel once and for all.
> > 
> > The patches have been tested on at91sam9263 and at91sam9g45 (non-DT),
> > and compile-tested for the other SOCs and DT.
> > 
> > Johan
> > 
> > 
> > v2:
> >  - add DT-support
> >  - make sys_irq_mask non-mandatory
> > 
> > 
> > Johan Hovold (5):
> >   ARM: at91/rtc: fix boot after RTC wake-up
> >   ARM: at91/dts: add RTC nodes
> >   ARM: at91/dts: add RTT nodes
> >   Revert "arm: at91: move at91rm9200 rtc header in drivers/rtc"
> >   ARM: at91: fix hanged boot
> 
> -- 
> -- 
> You received this message because you are subscribed to "rtc-linux".
> Membership options at http://groups.google.com/group/rtc-linux .
> Please read http://groups.google.com/group/rtc-linux/web/checklist
> before submitting a driver.
> --- 
> You received this message because you are subscribed to the Google Groups "rtc-linux" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
> 
>
Johan Hovold April 12, 2013, 9:33 a.m. UTC | #8
On Thu, Apr 11, 2013 at 06:54:14PM +0200, Jean-Christophe PLAGNIOL-VILLARD wrote:
> On 17:55 Thu 11 Apr     , Johan Hovold wrote:
> > On Mon, Mar 11, 2013 at 07:07:54PM +0100, Johan Hovold wrote:
> > > These patches fix a few severe issues affecting most AT91 SOCs where
> > > boot can hang after a non-general reset, and where the only way to get
> > > the system booting again is to do a general reset -- something which
> > > could require physically removing any backup battery.
> > 
> > Have you had time to look at these patches yet, Nicolas?
> > 
> > I don't think not having decided on the path forward for DT-support for
> > rtc-at91sam9 needs to be a blocker. The rtt-nodes will be needed in any
> > case.
> > 
> > I could respin the series on top of the DT-patch for rtc-at91rm9200, and
> > add interrupt and status-disabled properties to the DT-nodes as well.
> for this this is still a no go
> 
> this way too much ugly

I understand that you prefer fixing every bootloader. I was just making
sure everyone agrees that that is the best solution.

The two interrupt masks has to be cleared before the kernel enables the
system interrupt; either it needs to be done by the bootloader or by the
at91 arch code.

The various bootloaders may not know anything about RTT or RTC, but
have all made sure interrupts are disabled before executing the kernel.
That is, they have fulfilled the requirement that interrupts must be
disabled.

So the trade-off seems to be: Either we fix this once and for all using
the infrastructure already in place in the kernel (DT), or risk further
(apparently) bricked systems as there are bound to be bootloaders that
won't get updated.

[...]

> > > The problems stem from the fact that the RTC and RTT-peripherals are
> > > powered by backup power (VDDBU) and are not reset on wake-up, user,
> > > watchdog or software reset. Consequently, RTC and RTT-alarms and their
> > > interrupts may be enabled at boot, leading to a system lock-up when an
> > > interrupt arrives on the shared system-interrupt line before the
> > > appropriate handler (e.g. RTC-driver) has been installed.
> > > 
> > > The easiest way to trigger this is to simply wake up from an RTC-alarm
> > > on at91sam9g45. The RTC-driver currently does not disable interrupts at
> > > shutdown so even after a clean shut-down the system will always hang
> > > after waking up.
> > > 
> > > The first patch fixes this very general case of RTC-wake up after a
> > > clean shutdown in the RTC-driver and is marked for stable as it is
> > > perfectly straight-forward. [ Note that the other, RTT-based, AT91
> > > RTC-driver already disables its interrupts at shutdown. ]

And what about this patch? If it's decided that every bootloader needs
to be updated, then perhaps it's better to risk bricked systems also
after a clean shutdown to enforce those updates? Should we then remove
the corresponding disable of interrupts at shutdown from the rtc-at91sam9
driver by the same logic?

> > > The more general problem can be triggered, for example, by doing a
> > > user-reset while updating the RTC-time or if an RTC or RTT-alarm goes
> > > off after a non-clean shutdown.
> > > 
> > > To fix this I propose that arch-code should mask the relevant interrupts
> > > before enabling the system interrupt at early boot, and this is what
> > > the fifth patch does. To access the RTC-registers I choose to revert a
> > > recent patch that moved the register definitions to drivers/rtc.
> > > 
> > > Arguably, the relevant interrupts could also be disabled in bootloaders,
> > > but I suggest fixing it in the kernel once and for all.

Thanks,
Johan
Nicolas Ferre April 12, 2013, 12:09 p.m. UTC | #9
On 04/12/2013 11:33 AM, Johan Hovold :
> On Thu, Apr 11, 2013 at 06:54:14PM +0200, Jean-Christophe PLAGNIOL-VILLARD wrote:
>> On 17:55 Thu 11 Apr     , Johan Hovold wrote:
>>> On Mon, Mar 11, 2013 at 07:07:54PM +0100, Johan Hovold wrote:
>>>> These patches fix a few severe issues affecting most AT91 SOCs where
>>>> boot can hang after a non-general reset, and where the only way to get
>>>> the system booting again is to do a general reset -- something which
>>>> could require physically removing any backup battery.
>>>
>>> Have you had time to look at these patches yet, Nicolas?
>>>
>>> I don't think not having decided on the path forward for DT-support for
>>> rtc-at91sam9 needs to be a blocker. The rtt-nodes will be needed in any
>>> case.
>>>
>>> I could respin the series on top of the DT-patch for rtc-at91rm9200, and
>>> add interrupt and status-disabled properties to the DT-nodes as well.
>> for this this is still a no go
>>
>> this way too much ugly
> 
> I understand that you prefer fixing every bootloader. I was just making
> sure everyone agrees that that is the best solution.
> 
> The two interrupt masks has to be cleared before the kernel enables the
> system interrupt; either it needs to be done by the bootloader or by the
> at91 arch code.
> 
> The various bootloaders may not know anything about RTT or RTC, but
> have all made sure interrupts are disabled before executing the kernel.
> That is, they have fulfilled the requirement that interrupts must be
> disabled.
> 
> So the trade-off seems to be: Either we fix this once and for all using
> the infrastructure already in place in the kernel (DT), or risk further
> (apparently) bricked systems as there are bound to be bootloaders that
> won't get updated.

Note that I didn't read your patch series yet, so I am not commenting on
the implementation.

BUT, from my experience with customers facing this issue, I do thing
that we must provide a solution (even in Linux kernel itself).


> [...]
> 
>>>> The problems stem from the fact that the RTC and RTT-peripherals are
>>>> powered by backup power (VDDBU) and are not reset on wake-up, user,
>>>> watchdog or software reset. Consequently, RTC and RTT-alarms and their
>>>> interrupts may be enabled at boot, leading to a system lock-up when an
>>>> interrupt arrives on the shared system-interrupt line before the
>>>> appropriate handler (e.g. RTC-driver) has been installed.
>>>>
>>>> The easiest way to trigger this is to simply wake up from an RTC-alarm
>>>> on at91sam9g45. The RTC-driver currently does not disable interrupts at
>>>> shutdown so even after a clean shut-down the system will always hang
>>>> after waking up.
>>>>
>>>> The first patch fixes this very general case of RTC-wake up after a
>>>> clean shutdown in the RTC-driver and is marked for stable as it is
>>>> perfectly straight-forward. [ Note that the other, RTT-based, AT91
>>>> RTC-driver already disables its interrupts at shutdown. ]
> 
> And what about this patch? If it's decided that every bootloader needs
> to be updated, then perhaps it's better to risk bricked systems also
> after a clean shutdown to enforce those updates? Should we then remove
> the corresponding disable of interrupts at shutdown from the rtc-at91sam9
> driver by the same logic?
> 
>>>> The more general problem can be triggered, for example, by doing a
>>>> user-reset while updating the RTC-time or if an RTC or RTT-alarm goes
>>>> off after a non-clean shutdown.
>>>>
>>>> To fix this I propose that arch-code should mask the relevant interrupts
>>>> before enabling the system interrupt at early boot, and this is what
>>>> the fifth patch does. To access the RTC-registers I choose to revert a
>>>> recent patch that moved the register definitions to drivers/rtc.
>>>>
>>>> Arguably, the relevant interrupts could also be disabled in bootloaders,
>>>> but I suggest fixing it in the kernel once and for all.
> 
> Thanks,
> Johan
> 
>
Johan Hovold Oct. 16, 2013, 9:56 a.m. UTC | #10
These patches fix a few severe issues affecting most AT91 SOCs where
boot can hang after a non-general reset, and where the only way to get
the system booting again is to do a general reset -- something which
could require physically removing any backup battery.

The problems stem from the fact that the RTC and RTT-peripherals are
powered by backup power (VDDBU) and are not reset on wake-up, user,
watchdog or software reset. Consequently, RTC and RTT-alarms and their
interrupts may be enabled at boot, leading to a system lock-up when an
interrupt arrives on the shared system-interrupt line before the
appropriate handler (e.g. RTC-driver) has been installed.

The easiest way to trigger this is to simply wake up from an RTC-alarm
on at91sam9g45. The RTC-driver currently does not disable interrupts at
shutdown so even after a clean shut-down the system will always hang
after waking up.

The more general problem can be triggered, for example, by doing a
user-reset while updating the RTC-time or if an RTC or RTT-alarm goes
off after a non-clean shutdown.

To fix this I add two helper functions to be called called by arch-code
to mask the relevant interrupts before enabling the system interrupt at
early boot.

The patches have been tested on at91sam9g45 and compile-tested for the
other SOCs.

Johan


v2:
 - add DT-support
 - make sys_irq_mask non-mandatory

v3
 - rebase on v3.12-rc5
 - drop DT-support (interrupt masking is needed even if RTC or RTT nodes
   are missing)
 - drop dedicated SOC-initialiser and call helpers directly from init
 - drop revert-patch of move of RTC-register definitions to drivers/ and
   copy the two needed definitions instead
 - move helper functions from setup.c to a separate file
 - add fix for new sama5d3 SOC
 - add a register read back to make sure write has reached the devices
 - split fix in two patches for RTC and RTT, respectively


Johan Hovold (3):
  ARM: at91: fix hanged boot due to early rtc-interrupt
  ARM: at91: fix hanged boot due to early rtt-interrupt
  ARM: at91/rtc: disable interrupts at shutdown

 arch/arm/mach-at91/Makefile                   |  2 +-
 arch/arm/mach-at91/at91sam9260.c              |  2 +
 arch/arm/mach-at91/at91sam9261.c              |  2 +
 arch/arm/mach-at91/at91sam9263.c              |  3 ++
 arch/arm/mach-at91/at91sam9g45.c              |  3 ++
 arch/arm/mach-at91/at91sam9n12.c              |  6 +++
 arch/arm/mach-at91/at91sam9rl.c               |  3 ++
 arch/arm/mach-at91/at91sam9x5.c               |  6 +++
 arch/arm/mach-at91/generic.h                  |  2 +
 arch/arm/mach-at91/include/mach/at91sam9n12.h |  5 ++
 arch/arm/mach-at91/include/mach/at91sam9x5.h  |  5 ++
 arch/arm/mach-at91/include/mach/sama5d3.h     |  5 ++
 arch/arm/mach-at91/sama5d3.c                  |  6 +++
 arch/arm/mach-at91/sysirq_mask.c              | 71 +++++++++++++++++++++++++++
 drivers/rtc/rtc-at91rm9200.c                  |  9 ++++
 15 files changed, 129 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/mach-at91/sysirq_mask.c
diff mbox

Patch

diff --git a/arch/arm/mach-at91/at91rm9200.c b/arch/arm/mach-at91/at91rm9200.c
index 7aeb473..4651ebb 100644
--- a/arch/arm/mach-at91/at91rm9200.c
+++ b/arch/arm/mach-at91/at91rm9200.c
@@ -325,6 +325,14 @@  static void __init at91rm9200_ioremap_registers(void)
 	at91_ioremap_ramc(0, AT91RM9200_BASE_MC, 256);
 }
 
+static void __init at91rm9200_sysirq_mask(void)
+{
+	/*
+	 * No need to mask any system interrupts as the RM9200 has no backup
+	 * power and resets all system peripherals at every reset.
+	 */
+}
+
 static void __init at91rm9200_initialize(void)
 {
 	arm_pm_idle = at91rm9200_idle;
@@ -387,5 +395,6 @@  AT91_SOC_START(rm9200)
 	.default_irq_priority = at91rm9200_default_irq_priority,
 	.ioremap_registers = at91rm9200_ioremap_registers,
 	.register_clocks = at91rm9200_register_clocks,
+	.sysirq_mask = at91rm9200_sysirq_mask,
 	.init = at91rm9200_initialize,
 AT91_SOC_END
diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c
index b67cd53..c47a0db 100644
--- a/arch/arm/mach-at91/at91sam9260.c
+++ b/arch/arm/mach-at91/at91sam9260.c
@@ -342,6 +342,11 @@  static void __init at91sam9260_ioremap_registers(void)
 	at91_ioremap_matrix(AT91SAM9260_BASE_MATRIX);
 }
 
+static void __init at91sam9260_sysirq_mask(void)
+{
+	at91_sysirq_mask_rtt(AT91SAM9260_BASE_RTT);
+}
+
 static void __init at91sam9260_initialize(void)
 {
 	arm_pm_idle = at91sam9_idle;
@@ -400,5 +405,6 @@  AT91_SOC_START(sam9260)
 	.default_irq_priority = at91sam9260_default_irq_priority,
 	.ioremap_registers = at91sam9260_ioremap_registers,
 	.register_clocks = at91sam9260_register_clocks,
+	.sysirq_mask = at91sam9260_sysirq_mask,
 	.init = at91sam9260_initialize,
 AT91_SOC_END
diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c
index 0204f4c..396e4cb 100644
--- a/arch/arm/mach-at91/at91sam9261.c
+++ b/arch/arm/mach-at91/at91sam9261.c
@@ -286,6 +286,11 @@  static void __init at91sam9261_ioremap_registers(void)
 	at91_ioremap_matrix(AT91SAM9261_BASE_MATRIX);
 }
 
+static void __init at91sam9261_sysirq_mask(void)
+{
+	at91_sysirq_mask_rtt(AT91SAM9261_BASE_RTT);
+}
+
 static void __init at91sam9261_initialize(void)
 {
 	arm_pm_idle = at91sam9_idle;
@@ -344,5 +349,6 @@  AT91_SOC_START(sam9261)
 	.default_irq_priority = at91sam9261_default_irq_priority,
 	.ioremap_registers = at91sam9261_ioremap_registers,
 	.register_clocks = at91sam9261_register_clocks,
+	.sysirq_mask = at91sam9261_sysirq_mask,
 	.init = at91sam9261_initialize,
 AT91_SOC_END
diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c
index c0cbb81..a0166a3 100644
--- a/arch/arm/mach-at91/at91sam9263.c
+++ b/arch/arm/mach-at91/at91sam9263.c
@@ -325,6 +325,12 @@  static void __init at91sam9263_ioremap_registers(void)
 	at91_ioremap_matrix(AT91SAM9263_BASE_MATRIX);
 }
 
+static void __init at91sam9263_sysirq_mask(void)
+{
+	at91_sysirq_mask_rtt(AT91SAM9263_BASE_RTT0);
+	at91_sysirq_mask_rtt(AT91SAM9263_BASE_RTT1);
+}
+
 static void __init at91sam9263_initialize(void)
 {
 	arm_pm_idle = at91sam9_idle;
@@ -382,5 +388,6 @@  AT91_SOC_START(sam9263)
 	.default_irq_priority = at91sam9263_default_irq_priority,
 	.ioremap_registers = at91sam9263_ioremap_registers,
 	.register_clocks = at91sam9263_register_clocks,
+	.sysirq_mask = at91sam9263_sysirq_mask,
 	.init = at91sam9263_initialize,
 AT91_SOC_END
diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c
index b4968aa..3d5498e 100644
--- a/arch/arm/mach-at91/at91sam9g45.c
+++ b/arch/arm/mach-at91/at91sam9g45.c
@@ -370,6 +370,12 @@  static void __init at91sam9g45_ioremap_registers(void)
 	at91_ioremap_matrix(AT91SAM9G45_BASE_MATRIX);
 }
 
+static void __init at91sam9g45_sysirq_mask(void)
+{
+	at91_sysirq_mask_rtc(AT91SAM9G45_BASE_RTC);
+	at91_sysirq_mask_rtt(AT91SAM9G45_BASE_RTT);
+}
+
 static void __init at91sam9g45_initialize(void)
 {
 	arm_pm_idle = at91sam9_idle;
@@ -427,5 +433,6 @@  AT91_SOC_START(sam9g45)
 	.default_irq_priority = at91sam9g45_default_irq_priority,
 	.ioremap_registers = at91sam9g45_ioremap_registers,
 	.register_clocks = at91sam9g45_register_clocks,
+	.sysirq_mask = at91sam9g45_sysirq_mask,
 	.init = at91sam9g45_initialize,
 AT91_SOC_END
diff --git a/arch/arm/mach-at91/at91sam9n12.c b/arch/arm/mach-at91/at91sam9n12.c
index 5dfc8fd..e2ddb89 100644
--- a/arch/arm/mach-at91/at91sam9n12.c
+++ b/arch/arm/mach-at91/at91sam9n12.c
@@ -221,6 +221,11 @@  static void __init at91sam9n12_map_io(void)
 	at91_init_sram(0, AT91SAM9N12_SRAM_BASE, AT91SAM9N12_SRAM_SIZE);
 }
 
+static void __init at91sam9n12_sysirq_mask(void)
+{
+	at91_sysirq_mask_rtc(AT91SAM9N12_BASE_RTC);
+}
+
 void __init at91sam9n12_initialize(void)
 {
 	at91_extern_irq = (1 << AT91SAM9N12_ID_IRQ0);
@@ -229,5 +234,6 @@  void __init at91sam9n12_initialize(void)
 AT91_SOC_START(sam9n12)
 	.map_io = at91sam9n12_map_io,
 	.register_clocks = at91sam9n12_register_clocks,
+	.sysirq_mask = at91sam9n12_sysirq_mask,
 	.init = at91sam9n12_initialize,
 AT91_SOC_END
diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c
index 3de3e04..bde999f 100644
--- a/arch/arm/mach-at91/at91sam9rl.c
+++ b/arch/arm/mach-at91/at91sam9rl.c
@@ -289,6 +289,12 @@  static void __init at91sam9rl_ioremap_registers(void)
 	at91_ioremap_matrix(AT91SAM9RL_BASE_MATRIX);
 }
 
+static void __init at91sam9rl_sysirq_mask(void)
+{
+	at91_sysirq_mask_rtc(AT91SAM9RL_BASE_RTC);
+	at91_sysirq_mask_rtt(AT91SAM9RL_BASE_RTT);
+}
+
 static void __init at91sam9rl_initialize(void)
 {
 	arm_pm_idle = at91sam9_idle;
@@ -346,5 +352,6 @@  AT91_SOC_START(sam9rl)
 	.default_irq_priority = at91sam9rl_default_irq_priority,
 	.ioremap_registers = at91sam9rl_ioremap_registers,
 	.register_clocks = at91sam9rl_register_clocks,
+	.sysirq_mask = at91sam9rl_sysirq_mask,
 	.init = at91sam9rl_initialize,
 AT91_SOC_END
diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c
index 44a9a62..20a214f 100644
--- a/arch/arm/mach-at91/at91sam9x5.c
+++ b/arch/arm/mach-at91/at91sam9x5.c
@@ -316,6 +316,11 @@  static void __init at91sam9x5_map_io(void)
 	at91_init_sram(0, AT91SAM9X5_SRAM_BASE, AT91SAM9X5_SRAM_SIZE);
 }
 
+static void __init at91sam9x5_sysirq_mask(void)
+{
+	at91_sysirq_mask_rtc(AT91SAM9X5_BASE_RTC);
+}
+
 /* --------------------------------------------------------------------
  *  Interrupt initialization
  * -------------------------------------------------------------------- */
@@ -323,4 +328,5 @@  static void __init at91sam9x5_map_io(void)
 AT91_SOC_START(sam9x5)
 	.map_io = at91sam9x5_map_io,
 	.register_clocks = at91sam9x5_register_clocks,
+	.sysirq_mask = at91sam9x5_sysirq_mask,
 AT91_SOC_END
diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h
index fc593d6..90a854d 100644
--- a/arch/arm/mach-at91/generic.h
+++ b/arch/arm/mach-at91/generic.h
@@ -33,6 +33,8 @@  extern int  __init at91_aic_of_init(struct device_node *node,
 				    struct device_node *parent);
 extern int  __init at91_aic5_of_init(struct device_node *node,
 				    struct device_node *parent);
+extern void __init at91_sysirq_mask_rtc(u32 rtc_base);
+extern void __init at91_sysirq_mask_rtt(u32 rtt_base);
 
 
  /* Timer */
diff --git a/arch/arm/mach-at91/include/mach/at91sam9n12.h b/arch/arm/mach-at91/include/mach/at91sam9n12.h
index d374b87..0151bcf 100644
--- a/arch/arm/mach-at91/include/mach/at91sam9n12.h
+++ b/arch/arm/mach-at91/include/mach/at91sam9n12.h
@@ -49,6 +49,11 @@ 
 #define AT91SAM9N12_BASE_USART3	0xf8028000
 
 /*
+ * System Peripherals
+ */
+#define AT91SAM9N12_BASE_RTC	0xfffffeb0
+
+/*
  * Internal Memory.
  */
 #define AT91SAM9N12_SRAM_BASE	0x00300000	/* Internal SRAM base address */
diff --git a/arch/arm/mach-at91/include/mach/at91sam9x5.h b/arch/arm/mach-at91/include/mach/at91sam9x5.h
index c75ee19..2fc76c4 100644
--- a/arch/arm/mach-at91/include/mach/at91sam9x5.h
+++ b/arch/arm/mach-at91/include/mach/at91sam9x5.h
@@ -55,6 +55,11 @@ 
 #define AT91SAM9X5_BASE_USART2	0xf8024000
 
 /*
+ * System Peripherals
+ */
+#define AT91SAM9X5_BASE_RTC	0xfffffeb0
+
+/*
  * Internal Memory.
  */
 #define AT91SAM9X5_SRAM_BASE	0x00300000	/* Internal SRAM base address */
diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c
index 4b67847..ac05453 100644
--- a/arch/arm/mach-at91/setup.c
+++ b/arch/arm/mach-at91/setup.c
@@ -19,6 +19,8 @@ 
 #include <mach/cpu.h>
 #include <mach/at91_dbgu.h>
 #include <mach/at91_pmc.h>
+#include <mach/at91_rtc.h>
+#include <mach/at91_rtt.h>
 
 #include "at91_shdwc.h"
 #include "soc.h"
@@ -311,6 +313,31 @@  void __init at91_ioremap_matrix(u32 base_addr)
 		panic("Impossible to ioremap at91_matrix_base\n");
 }
 
+void __init at91_sysirq_mask_rtc(u32 rtc_base)
+{
+	void *base = AT91_IO_P2V(rtc_base);
+	u32 mask;
+
+	mask = __raw_readl(base + AT91_RTC_IMR);
+	if (mask) {
+		pr_info("AT91: Disabling rtc irq\n");
+		__raw_writel(mask, base + AT91_RTC_IDR);
+	}
+}
+
+void __init at91_sysirq_mask_rtt(u32 rtt_base)
+{
+	void *base = AT91_IO_P2V(rtt_base);
+	u32 mr;
+
+	mr = __raw_readl(base + AT91_RTT_MR);
+	if (mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)) {
+		pr_info("AT91: Disabling rtt irq\n");
+		mr &= ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN);
+		__raw_writel(mr, base + AT91_RTT_MR);
+	}
+}
+
 #if defined(CONFIG_OF)
 static struct of_device_id rstc_ids[] = {
 	{ .compatible = "atmel,at91sam9260-rstc", .data = at91sam9_alt_restart },
@@ -467,6 +494,8 @@  void __init at91_dt_initialize(void)
 
 	if (at91_boot_soc.init)
 		at91_boot_soc.init();
+
+	at91_boot_soc.sysirq_mask();
 }
 #endif
 
@@ -482,5 +511,7 @@  void __init at91_initialize(unsigned long main_clock)
 
 	at91_boot_soc.init();
 
+	at91_boot_soc.sysirq_mask();
+
 	pinctrl_provide_dummies();
 }
diff --git a/arch/arm/mach-at91/soc.h b/arch/arm/mach-at91/soc.h
index 9c6d3d4..134f4c4 100644
--- a/arch/arm/mach-at91/soc.h
+++ b/arch/arm/mach-at91/soc.h
@@ -10,6 +10,7 @@  struct at91_init_soc {
 	void (*map_io)(void);
 	void (*ioremap_registers)(void);
 	void (*register_clocks)(void);
+	void (*sysirq_mask)(void);
 	void (*init)(void);
 };