Make your BeagleBone Black am355x magically turn itself on with the built-in alarm clock
The am335x ARM chip in the BeagleBone Black has a Real Time Clock hidden inside that you can use to have the board spontaneously turn itself on anytime in the next 100 years.
Here I present a new utility to manage that built-in clock from the Linux command line, and explain how to use it to keep time, shutdown, software power cycle, and more!
It looks like the 4.x kernels break this code by locking the MDIO registers. See comments section below for more info. I only use the 3.x kernels because of issues with the PRUSS subsystem in the newer ones, so I am not going to try to get this working on 4.x (and if you use the PRU, I’d recommend sticking with 3.x. also). If you really need 4.x and decide to get it working, please send me a PR!
tldr;
Type these commands on your BBB running Debian and wait for about 10 seconds. You should see itself turn off… and then magically turn back on one minute later….
git clone https://github.com/bigjosh/bbbrtc cd bbbrtc make i2cset -f -y 0 0x24 0x0a 0x00 bbbrtc wake 170 bbbrtc sleep 110 sync bbbrtc now 100
Enter bbbrtc
alone to see all the parameters.
Demo
Most interesting thing I learned
The Linux date
command has a semi-documented, natural language processing, fuzzy logic expert system built-in. You can ask it to figure out what day of the week it was 10 years and 4 hours ago….
root@beaglebone:~# date -d "now 10 years 4 hours ago" | cut -c1-3 Sun
…or when the next episode of Saturday Night Live will begin…
root@beaglebone:~# date -d "11:30pm next saturday" Sat May 5 23:30:00 UTC 2018
The basic idea
The ARM processor chip in the BBB has a Real Time Clock (RTC) built-in. This RTC has connections to the Power Management IC (PMIC) that it can use to tell the PMIC to either shut off the power or turn it back on.
There are two “alarms” in the RTC that we can set. When these alarms go off, one will send the signal to the PMIC telling it to turn off, and the other alarm tells the PMIC to turn the power on.
Using an alarm to turn off makes sense, but how can the “on” alarm send a signal to the PMIC to tell it to turn on if the power is off!?
The PMIC has a bit called OFF
, and if that bit is not set then it only mostly turns off the the power to the ARM, and there is a big different between mostly off and all off. In this state, the PMIC continues to supply the very low power LDO1
rail, and this rail is connected directly to dedicated pins on the ARM that power the RTC.
To make all this work we just need a way to set the time and alarms on the RTC, and a way to clear the OFF
bit in the PMIC.
The PMIC has an i2c connection, so that is easy to get to from the Linux command line using the i2cset
command (shown below), but I could not find a good way to set the RTC registers, so I wrote…
The bbbrtc
utility
This little utility makes it possible to set up all the necessary registers inside the RTC.
There are three times you can get and set inside the RTC…
now
– the time right now. This increments each second.
sleep
– the time we should go to sleep.
wake
– the time we should wake up.
These times are specified using the standard “Unix time” system, which is the count of seconds since Jan 1, 1970.
Syntax
Running the bbbrtc
utility without any params gives…
Usage: bbbrtc dump bbbrtc now bbbrtc (sleep|wake) ({time}|never) Where: 'bbbrtc dump` dumps the contents of all registers to stdout if is present and non-zero, then the specified time is set if never is specified with sleep or wake, the event is disabled (clock value is left the same) time is in seconds since epoch, parsed leniently prints existing or newly set the value of the specified time Notes: wake should always be later than sleep or you will sleep for 100 years if you sleep without setting a wake then you must push the power button to wake Times: always specified in seconds since the epoch Jan 1, 1970 never turns off the specified interrupt Examples: set the rtc to the current system clock time... bbbrtc now $(date +%s) set the system clock to the current rtc time... date -s @$(bbbrtc now) set to sleep 10 seconds from now... bbbrtc sleep $[$(bbbrtc now)+10] set to wake 1 day after we go to sleep ... bbbrtc wake $(date +%s -d "$(date -d "@$(bbbrtc sleep)") + 1 day") start a bbbrtc party... bbbrtc now 946684770 Notes: If you want to be able to wake from sleeping, you must turn off the 'off' bit in the PMIC controller with this command before going to sleep.. i2cset -f -y 0 0x24 0x0a 0x00 The RTC doesn't know about timezones or DST, so best to use UTC time with it, but as long as you are consistent and set both all the times using the same base, then all relatives times should work. It looks like my Debian calls hwclock --systohc sometime during startup, even when Linux has no way of knowing what time it is (it is hardcoded to Jun15 2016). This sadly which clobbers the time stored in the RTC, which could otherwise be correct and used to set the linux clock.
Getting RTC times
You can see what any of these are currently set to by running bbbrtc
and the name of the time, so…
bbbrtc now
will print the time right now according to the RTC now
clock.
bbbrtc sleep
will print the time that is currently set to go to sleep.
Setting RTC times
You can set any of the times by specifying the new value after the time name.
To set the now
time in the RTC to one second past midnight on 1/1/1970, you could run…
bbbrtc now 1
…and it would print…
Setting NOW to 1...set.
Now if you wait for 15 seconds and then run…
bbbrtc now
…again you’ll see…
Reading NOW and printing to stdout...15
The RTC now thinks it is 00:00:15 1/1/1970, and will keep counting up from there.
Tips
- If you want to be able to turn back on after you power down, you have to execute this command before power down…
i2cset -f -y 0 0x24 0x0a 0x00
This tells the PMIC to allow power-up based on the signal from the RTC. Otherwise it will wait for one of the power sources to turn on. - If a
sleep
alarm happens and you have not set awake
alarm, or the wake time is past now then you will have to press the power button on the board to turn back on.
The RTC
In section 20.3.2 of the 4,728 page technical reference manual, you will find a description of the on-board real-time clock…
This part of the chip is special- it has its own special power supply connection so it stay running even when the rest of the chip is powered down.
On the BBB, it also has connections to the Power Management IC that can let it turn power off and on remotely. PMIC_POWER_EN
is used to tell the PMIC to shutdown or turn on power…
Here is the state machine diagram for the PMIC…
If you look at the ACTVE state, you can see we can get to SLEEP if OFF
is 0 by setting PWR_EN
to 0. Note that this is not the same as the sleep
on the ARM chip – which lets you keep RAM intact. The sleep
mode on the PMIC removes all power from the ARM except that special rail for the RTC.
OFF
is a bit inside the PMIC chip, and we can set it to 0 over the i2c bus. The PMIC datasheet tells us that it is at address 0x24…
…and the OFF bit is here…
…so we can clear it with this Linux command…
i2cset -f -y 0 0x24 0x0a 0x00
We need the -f
because something in Linux is already using this i2c so we would get a busy error. I am too lazy to figure out what it is and how to get it too let go.
The RTC will make POWER_EN
go low when ALARM2
goes off, and high when ALARM
goes off if we set this bit…
We also have to enable the associated interrupts even though we do not want the actual interrupts to get fired (it really does not work if you skip this)…
Thebbbrtc
programs does these for you when you set either sleep
or wake
times.
FAQ
Q: How much power does the board use when off?
A: About 40mA. I don’t know where this comes from since the ARM and PMIC should both be using much less than 1mA when in this state. There is a crapton of places this could be coming from on this board. If you care about low power, then you should pick a different board. In fact, you should probably pick a different board anyway since this one has so many issues like this (see last FAQ question).
Please LMK if you figure out what is using this extra power.
Q: This is AWESOME, now I can have my BBB know what time it is when it powers up even without a network connection!
A:You should be able to do this in theory, but in practice Linux or uboot seems to insist on setting the RTC now
time to sometime at the end of in 2015 on power up.
I tried doing this in /etc/default/hwclock
but it doesn’t seem to help…
# Set this to yes if it is possible to access the hardware clock, # or no if it is not. HWCLOCKACCESS=no
Please LMK if you figure out how to stop this from happening.
Q: What if I want to power down for more than 100 years?
A: The years register in the RTC is a two digit BCD number (WTF?) so the best you can do power down for 100 years, update some file in the file system (or one of the handy RTC scratch registers), and then set a new alarm and turn off again.
Q: Why didn’t you just use the built-in Linux stuff.
A: I tried, but so much mess and so many patches I just could not figure out if linux can even do this, much less get it to work. As far as I can tell, it can read the now
time from the RTC using the hwclock
stuff, but can not set the alarms or the important other registers you need for this stuff to happen.
Q: Why did you write bbbrtc
when you could just use a million devmem2
commands?
A: I started with a million devemem2
commands, but it quickly got too hard to keep track of everything in BASH. Since almost all disros do not come with devmem2
, and almost all do come with a C compiler I thought it would be easier to just make a short and clear C program.
Q: Why did you do this?
A: While I think it is cool that you can have a computer turn itself on, my only goal here was to find a way to drive the NRESET-INOUT
line low from software- and this is the first way I found that can do that.
Q: Why do you want to drive the NRESET-INOUT
line low so bad?
A: Tune in next time!
Hi Josh,
i have successfully installed your code on a BB black and a BB industrial. The code installs and executes just fine. I can see the logical flow of diagnostic prints and results are all to the affirmative. But, my boards don’t go to sleep. If I use, bbbrtc sleep 10, nothing happens.
For example here is the output for bbb-long-reset running as root.
root@beaglebone:/usr/sbin# bbb-long-reset
Opening /dev/mem…opened.
Mappng in 0x44e3e000…mapped at address 0xb6fa2000.
Unlocking Kick Registers
Unmaping memory block…unmaped.
Closing fd…closed.
Opening /dev/mem…opened.
Mappng in 0x44e3e000…mapped at address 0xb6f9c000.
Stopping RTC…waiting for stop…took 0 tries.
Checking RTC chip rev…checks good.
Setting SLEEP to 949377771…set.
Setting WAKE to 949377779…set.
Enable PWR_ENABLE_EN to be controlled ON->OFF by interrupts…
enabled.
Enable ALARM interrupt bit… enabled.
Enable IRQ WAKE ENABLE bit…
enabled.
Enable ALARM2 interrupt bit… enabled.
Reading clocks and printing to stdout…
NOW: 949381369
SLEEP: 949392171
WAKE: 949395779
done.
Starting RTC…
waiting for it to start…took 0 tries.
Unmaping memory block…unmaped.
Closing fd…closed.
But, the board never resets.
Here is uname -ar
root@beaglebone:/usr/sbin# uname -ar
Linux beaglebone 4.4.30-ti-r64 #1 SMP Fri Nov 4 21:23:33 UTC 2016 armv7l GNU/Linux
If I dump the registers. I get them all. The version 4 fix appears to work. So, the only thing that seems to not work for me is actually taking the device down.
Any recommendations or thoughts?
Thanks,
Dan
Please don’t post as a comment. Sorry, this is pretty much a support question.
This is a support question in case someone else has the same issue! :)
I really do not have any experience with the 4.x kernels at all, so have no idea what could be going on here. Any chance you’d be willing to throw a 3.x kernel onto the BBB and try my original code (pre 4.x fix) and see what happens so we can potentially narrow down the issue?
You could also simply use the standard way of waking up:
echo +10 > /sys/class/rtc/rtc0/wakealarm; echo mem > /sys/power/state
That would be great, but I could not figure out how to make it work!
root@beaglebone:~# sudo echo +90 > /sys/class/rtc/rtc0/wakealarm
-bash: /sys/class/rtc/rtc0/wakealarm: No such file or directory
It does show alarms here though…
root@beaglebone:/sys/class/rtc/rtc0# cat /proc/driver/rtc
rtc_time : 20:10:21
rtc_date : 2016-06-15
alrm_time : 00:01:50
alrm_date : 2070-01-01
alarm_IRQ : no
alrm_pending : no
update IRQ enabled : no
periodic IRQ enabled : no
periodic IRQ frequency : 1
max user IRQ frequency : 64
24hr : yes
I am running stock BBB Debian…
root@beaglebone:~# uname -r
3.8.13-bone80
Any ideas how I can get the
wakealarm
stuff to show up? Thanks!I encounter a bug similar to bigjosh2, when i was running bbb-long-reset, it was working the first time, but subsequent call to bbb-long-reset do nothing. I found that interrupt flag was not clear on register 0x44 of RTC on my board (beaglebone green running on custom debian). I fix the problem by writing 0xc0 in register RTC_STATUS_REG before enabling interrupt in bbbrtc.c
Sir,
Can you please tell me how to downgrade the kernel on my beaglebone black to 3.* version.
Thank you in advance.
Unfortunately it looks like they have taken down the older ready made images from the beagleboard.org website. Why do you want to downgrade?
With Debian buster and kernel “Linux beaglebone 4.19.94-ti-r42”
the the standard way of sleeping and waking up:
echo +10 > /sys/class/rtc/rtc0/wakealarm; echo mem > /sys/power/state
works just fine for me.
This tool does more than sleep and wake, it causes a physical hardware reset!
root@bbb:~/sleep/bbbrtc# ./bbbrtc dump
Opening /dev/mem…
Error opening /dev/mem: No such file or directory
root@bbb:~/sleep/bbbrtc# cd /dev/mem
-sh: cd: /dev/mem: No such file or directory
root@bbb:~/sleep/bbbrtc# cd /dev
root@bbb:/dev# ls
autofs full kmem mmcblk1rpmb stderr tty15 tty25 tty35 tty45 tty55 tty8 vcs
block gpiochip0 kmsg mtab stdin tty16 tty26 tty36 tty46 tty56 tty9 vcs1
bus gpiochip1 log null stdout tty17 tty27 tty37 tty47 tty57 ttyO0 vcsa
char gpiochip2 loop-control psaux tty tty18 tty28 tty38 tty48 tty58 ttyO1 vcsa1
console gpiochip3 mmcblk0 ptmx tty0 tty19 tty29 tty39 tty49 tty59 ttyO4 vcsu
cpu_dma_latency hwrng mmcblk0p1 pts tty1 tty2 tty3 tty4 tty5 tty6 ttyS0 vcsu1
crypto i2c-0 mmcblk0p2 random tty10 tty20 tty30 tty40 tty50 tty60 ttyS1 watchdog
disk i2c-2 mmcblk1 rtc tty11 tty21 tty31 tty41 tty51 tty61 ttyS2 watchdog0
dri iio:device0 mmcblk1boot0 rtc0 tty12 tty22 tty32 tty42 tty52 tty62 ttyS3 zero
fb0 initctl mmcblk1boot1 shm tty13 tty23 tty33 tty43 tty53 tty63 ubi_ctrl
fd input mmcblk1p1 spidev0.0 tty14 tty24 tty34 tty44 tty54 tty7 urandom
Are you sure you are logged in with root privileges?
Can you try running the same command with `su`?
Thanks, but I’m running as root, so I don’t need su. I’m also running Linux 5.4 where there is no /dev/mem directory. Using a Yocto build and it boots in 10 seconds, it’s really hard to go back to the older 3.x kernels. Your code might work if I knew what directory to point to instead of /dev/mem.
This code explicitly only runs under the 3.x kernals for several reasons, and just changing the location of `/dev/mem` will not fix them. While it would be possible to update the code to run under newer kernals, it would take significant effort.