diff --git a/kernel/arch/earm/Makefile.inc b/kernel/arch/earm/Makefile.inc index ca94480a8..7d048fd85 100644 --- a/kernel/arch/earm/Makefile.inc +++ b/kernel/arch/earm/Makefile.inc @@ -52,7 +52,7 @@ CPPFLAGS.findfp.c+= -I ${NETBSDSRCDIR}/lib/libc/include # that must live in their own unique namespace. # .for unpaged_obj in head.o pre_init.o direct_tty_utils.o \ - pg_utils.o klib.o omap_serial.o utility.o arch_reset.o \ + pg_utils.o klib.o omap_serial.o omap_rtc.o utility.o arch_reset.o \ ${MINLIB_OBJS_UNPAGED} ${MINC_OBJS_UNPAGED} ${SYS_OBJS_UNPAGED} unpaged_${unpaged_obj}: ${unpaged_obj} ${OBJCOPY} --prefix-symbols=__k_unpaged_ ${.OBJDIR}/${unpaged_obj} $@ @@ -63,8 +63,8 @@ ORIG_UNPAGED_OBJS += ${unpaged_obj} CLEANFILES+= ${ORIG_UNPAGED_OBJS} SRCS+= mpx.S arch_clock.c arch_do_vmctl.c arch_system.c \ - omap_serial.c omap_timer.c omap_padconf.c omap_intr.c exception.c \ - klib.S memory.c \ + omap_serial.c omap_timer.c omap_padconf.c omap_intr.c omap_rtc.c \ + exception.c klib.S memory.c \ protect.c direct_tty_utils.c arch_reset.c \ pg_utils.c phys_copy.S phys_memset.S exc.S OBJS.kernel+= ${UNPAGED_OBJS} diff --git a/kernel/arch/earm/arch_reset.c b/kernel/arch/earm/arch_reset.c index 2269711f6..fc0d3a623 100644 --- a/kernel/arch/earm/arch_reset.c +++ b/kernel/arch/earm/arch_reset.c @@ -8,12 +8,15 @@ #include #include #include +#include +#include #include #include "archconst.h" #include "arch_proto.h" #include "serial.h" +#include "omap_rtc.h" #include "kernel/proc.h" #include "kernel/debug.h" #include "direct_utils.h" @@ -34,9 +37,48 @@ reset(void) while (1); } +void +poweroff(void) +{ + +/* + * The am335x can signal an external power management chip to cut the power + * by toggling the PMIC_POWER_EN pin. It might fail if there isn't an + * external PMIC or if the PMIC hasn't been configured to respond to toggles. + * The only way to pull the pin low is via ALARM2 (see TRM 20.3.3.8). + * At this point PM should have already signaled readclock to set the alarm. + */ +#ifdef AM335X + + /* Powers down the SoC within 3 seconds */ + direct_print("PMIC Power-Off in 3 Seconds\n"); + + /* rtc was frozen to prevent premature power-off, unfreeze it now */ + omap3_rtc_run(); + + /* wait for the alarm to go off and PMIC to disable power to SoC */ + while (1); + +#endif /* AM335X */ + + /* fallback option: hang */ + direct_print("Unable to power-off this device."); + while (1); +} + __dead void arch_shutdown(int how) { + switch (how) { + + case RBT_POWEROFF: + /* Power off if possible, hang otherwise */ + poweroff(); + NOT_REACHABLE; + + default: + break; + } while (1); } diff --git a/kernel/arch/earm/arch_system.c b/kernel/arch/earm/arch_system.c index 03c0fa280..0f0fc3a0b 100644 --- a/kernel/arch/earm/arch_system.c +++ b/kernel/arch/earm/arch_system.c @@ -128,6 +128,9 @@ void arch_init(void) /* configure i2c pinmux */ omap3_padconf_init(); + + /* map memory for rtc */ + omap3_rtc_init(); } /*===========================================================================* diff --git a/kernel/arch/earm/omap_rtc.c b/kernel/arch/earm/omap_rtc.c new file mode 100644 index 000000000..849d77fbd --- /dev/null +++ b/kernel/arch/earm/omap_rtc.c @@ -0,0 +1,55 @@ +/* + * This is a mini driver for the AM335X Real Time Clock. The majority of the + * work is done in user space in readclock, but for power-off the clock needs + * to be put into run mode at the last possible moment in arch_reset.c. This + * driver just implements mapping the memory and re-starting the clock. + */ + +#include +#include +#include +#include +#include + +#include "kernel/kernel.h" +#include "kernel/proc.h" +#include "kernel/vm.h" +#include "kernel/proto.h" +#include "arch_proto.h" +#include "omap_rtc.h" + +#define RTC_SS_BASE 0x44e3e000 +#define RTC_SS_SIZE 0x1000 +#define RTC_CTRL_REG 0x40 +#define RTC_CTRL_RTC_STOP_BIT 0 + +struct omap_rtc +{ + vir_bytes base; + vir_bytes size; +}; + +static struct omap_rtc omap_rtc = { + .base = RTC_SS_BASE, + .size = RTC_SS_SIZE +}; + +static kern_phys_map rtc_phys_map; + +void +omap3_rtc_init(void) +{ +#ifdef AM335X + kern_phys_map_ptr(omap_rtc.base, omap_rtc.size, &rtc_phys_map, + &omap_rtc.base); +#endif /* AM335X */ +} + +void +omap3_rtc_run(void) +{ +#ifdef AM335X + /* Setting the stop bit starts the RTC running */ + mmio_set((omap_rtc.base + RTC_CTRL_REG), (1 << RTC_CTRL_RTC_STOP_BIT)); +#endif /* AM335X */ +} diff --git a/kernel/arch/earm/omap_rtc.h b/kernel/arch/earm/omap_rtc.h new file mode 100644 index 000000000..cbed3a561 --- /dev/null +++ b/kernel/arch/earm/omap_rtc.h @@ -0,0 +1,7 @@ +#ifndef __OMAP_RTC_H +#define __OMAP_RTC_H + +void omap3_rtc_init(void); +void omap3_rtc_run(void); + +#endif /* __OMAP_RTC_H */