Make your BeagleBone Black am355x magically turn itself on with the built-in alarm clock

bbbrtc.gif

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!

IMPORTANT UPDATE
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.

2018-05-04_20-38-10.png

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 a wake 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…

2018-04-28 18_58_17-am335x_techref.pdf - Foxit Reader

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…

2018-04-28 19_09_01-beaglebone_revA3.opj.png

Here is the state machine diagram for the PMIC…

2018-04-28 19_04_40-TPS65217x Single-Chip PMIC for Battery-Powered Systems datasheet (Rev. I).png

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…

2018-04-30 14_30_12-TPS65217x Single-Chip PMIC for Battery-Powered Systems datasheet (Rev. I).png

…and the OFF bit is here…2018-04-30 14_31_49-TPS65217x Single-Chip PMIC for Battery-Powered Systems datasheet (Rev. I).png

…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…Snag_1f5889.png

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)…

Snag_202a9e.png

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!

13 comments

  1. Dan Lulich

    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.

    • bigjosh2

      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?

  2. Alexandre Belloni

    You could also simply use the standard way of waking up:
    echo +10 > /sys/class/rtc/rtc0/wakealarm; echo mem > /sys/power/state

    • bigjosh2

      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!

  3. Hugo Bayeur

    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

  4. Kiran

    Sir,
    Can you please tell me how to downgrade the kernel on my beaglebone black to 3.* version.
    Thank you in advance.

    • bigjosh2

      Unfortunately it looks like they have taken down the older ready made images from the beagleboard.org website. Why do you want to downgrade?

  5. TZU

    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.

  6. Bob

    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

      • Bob

        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.

        • bigjosh2

          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.

Leave a Reply