225 lines
5.6 KiB
ArmAsm

/*
* (C) Copyright 2010
* TomTom International B.V.
* Martin Jackson <martin.jackson@tomtom.com>
*
*
* Check whether we have previously tried all happy booting scenarios,
* and power off if there are no more options (an 'epic fail')
*
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <asm/arch/mux.h>
#include <config.h>
#ifdef EPICFAIL_POWEROFF
/* Unfortunately, we need this pin mapping _really_ early */
# ifdef __VARIANT_A1
# define CP_PWR_HOLD CONTROL_PADCONF_GPMC_A9 /* GPIO 42 */
/* GPIO 42 Bank related */
# define CLKEN_PER_EN_PWR_HOLD_BIT CLKEN_PER_EN_GPIO2_BIT /* Bit to toggle bank clock */
# define OMAP34XX_PWR_HOLD_BASE OMAP34XX_GPIO2_BASE /* Base of bank */
# define PWR_HOLD_BANK_GPIO 10 /* Offset within bank */
# elif defined(__VARIANT_A2)
# define CP_PWR_HOLD CONTROL_PADCONF_GPMC_nBE1 /* GPIO 61 */
/* GPIO 61 Bank related */
# define CLKEN_PER_EN_PWR_HOLD_BIT CLKEN_PER_EN_GPIO2_BIT /* Bit to toggle bank clock */
# define OMAP34XX_PWR_HOLD_BASE OMAP34XX_GPIO2_BASE /* Base of bank */
# define PWR_HOLD_BANK_GPIO 29 /* Offset within bank */
# else
# error Unrecognized variant!
# endif
#endif /* EPICFAIL_POWEROFF */
#define TOUT_01S 0xffff7fff /* Watchdog timeout of 1s */
#define TOUT_10S 0xfffaffff /* Watchdog timeout of 10s */
#define TOUT_60S 0xffe1ffff /* Watchdog timeout of 60s */
#define WD_TIMEOUT TOUT_60S
@ Return value in r0
.macro readl addr
ldr r0, =\addr
ldr r0, [r0]
.endm
@ Clobbers r0, r1
.macro writel val, addr
ldr r1, =\addr
ldr r0, =\val
str r0, [r1]
.endm
@ Clobbers r0, r1
.macro writew val, addr
ldr r1, =\addr
ldr r0, =\val
strh r0, [r1]
.endm
@ Clobbers r0, r1
.macro __modify_bit nr, addr, op
ldr r1, =\addr
ldr r0, [r1]
\op r0, r0, #1<<\nr
str r0, [r1]
.endm
@ Clobbers r0, r1
.macro set_bit nr, addr
__modify_bit \nr, \addr, orr
.endm
@ Clobbers r0, r1
.macro clr_bit nr, addr
__modify_bit \nr, \addr, bic
.endm
@ Sets CPSR status bits, clobbers r0
.macro tst_bit nr, addr
readl \addr
ands r0, r0, #1<<\nr
.endm
.macro wdsync
ldr r0, =WD2_BASE+WWPS @ Wait for watchdog reg writes to drain
1: ldr r1, [r0]
cmp r1, #0
bne 1b
.endm
.macro wd_wr val, reg
writel \val, WD2_BASE + \reg
wdsync
.endm
@ Check whether an epic fail condition occurred
.global check_epicfail
/*
* We don't push any context in case the stack or bits of memory are buggered.
* Clobbers:
* - r0: Scratch register
* - r1: Scratch register
* - r3: Saved CPSR state
* - r12: Saved LR
*/
check_epicfail:
#ifdef EPICFAIL_POWEROFF
mov r12, lr
mrs r3, cpsr @ Disable all interrupts
mov r0, r3
orr r0, r0, #0xc0
msr cpsr, r0
@ PWR_HOLD probably isn't asserted here anyway, but deassert it just in case.
bl deassert_pwrhold
@ Arm the watchdog with timeout of 60s
set_bit 5, CM_FCLKEN_WKUP @ Enable watchdog FCLK
set_bit 5, CM_ICLKEN_WKUP @ Enable watchdog ICLK
ldr r0, =CM_IDLEST_WKUP @ Wait for watchdog to become accessible
1: ldr r1, [r0]
ands r1, #0x20
bne 1b
bl wd_disable @ Changing the timeout fails if we don't do this
wd_wr WD_TIMEOUT, WLDR @ Load timeout value
wd_wr 0xaa55aa55, WTGR @ Reload the watchdog timeout
@ Write the magic sequence 0xbbbb, 0x4444 that starts the watchdog count
wd_wr 0xbbbb, WSPR
wd_wr 0x4444, WSPR
tst_bit 4, PRM_RSTTST @ Check whether it's a watchdog reset
beq no_epicfail
@ Check the epic fail bit: did boot and altboot both fail already?
tst_bit EPICFAIL_BIT, FLIP_FLOP_LOCATION
bne epicfail @ epic fail bit set + watchdog reboot == epic fail
no_epicfail:
@ Set the epic fail bit: this has to be cleared later by userland
@ (after the kernel boots) to prove no epic fail occurred.
set_bit EPICFAIL_BIT, FLIP_FLOP_LOCATION
msr cpsr, r3 @ Restore interrupts
#endif /* EPICFAIL_POWEROFF */
mov pc, r12
.ltorg
@ Epic fail occurred: power the system down. Does NOT return
.global epicfail
epicfail:
#ifdef EPICFAIL_POWEROFF
clr_bit EPICFAIL_BIT, FLIP_FLOP_LOCATION @ System will attempt to boot next time SYSTEM_ON is asserted
bl deassert_pwrhold
bl wd_disable
@ Could print a helpful message saying what happened. But don't want to
@ risk crashing in the UART code :P
1: b 1b @ Hang here until power goes down
#else
mov pc, lr
#endif /* EPICFAIL_POWEROFF */
.ltorg
#ifdef EPICFAIL_POWEROFF
@ Disable the watchdog
wd_disable:
@ Disable the watchdog with the magic 0xaaaa, 0x5555 sequence
wd_wr 0xaaaa, WSPR
wd_wr 0x5555, WSPR
.ltorg
@ Deassert the PWR_HOLD pin
deassert_pwrhold:
writew (IDIS | PTD | DIS | M4), \
OMAP34XX_CTRL_BASE+(CP_PWR_HOLD) @ Configure PWR_HOLD pin mux as GPIO
set_bit CLKEN_PER_EN_PWR_HOLD_BIT, \
CM_FCLKEN_PER @ Enable PWR_HOLD GPIO bank FCLK
set_bit CLKEN_PER_EN_PWR_HOLD_BIT, \
CM_ICLKEN_PER @ Enable PWR_HOLD GPIO bank ICLK
clr_bit PWR_HOLD_BANK_GPIO, OMAP34XX_PWR_HOLD_BASE + \
OMAP34XX_GPIO_OE @ Set PWR_HOLD pin to output
clr_bit PWR_HOLD_BANK_GPIO, OMAP34XX_PWR_HOLD_BASE + \
OMAP34XX_GPIO_DATAOUT @ Set PWR_HOLD pin low
mov pc, lr
.ltorg
#endif /* EPICFAIL_POWEROFF */
.end