Part Number: SK-AM64
Am sharing this in case it helps others.
I have spent about two days trying to
- First, figure out why there was no power on the 3V3 and 5V0 pins of the RPi-compatible user expansion connector J4
- Second, figure out how to turn on the GPIO's that turn on the power on those pins
- Third, how to automatically turn on power at boot.
It is a bit remarkable to me that a board with a RPi-compatible connector has the power disabled by default in the as-provided boot images and meta-ti layer.
I had to teach myself a bunch of things which many folks probably already know, but maybe some do not. These are the things I wish I had known.
How Power Is Switched on the AM64x SK Board
3.3V and 5V power to the connector are switched by a pair of GPIO's that are attached to a TCA9538PWR I2C GPIO expander chip. This is on p. 38 of the board schematic. The signals of interest are RPI_PS_3V3_En and RPI_PS_5V0_En.
The good news is that this expander and these pins are defined in the device tree supplied by TI; you just need to figure out how to get to them.
How to Toggle the GPIO's
If you come from a Beaglebone world, you are used to what's called the "sysfs GPIO interface". This is not supported in the as-shipped resources, which is somewhat to be expected, as you can find repeatedly on the internet that the sysfs interface has been deprecated.
What's not often specified very clearly is what has replaced it. The answer is libgpiod. Now, libgpiod is just a set of libraries, no utilities. But there is also libgpiod-tools, and those include utilities like "gpioinfo", "gpioset", and so on, that you can use to access the GPIO's.
So, if you're building an OS image with Arago / Yocto, you want to include this in your image's recipe or in a packagegroup that it uses:
RDEPENDS_${PN} = "\
libgpiod \
libgpiod-tools \
...
Then once you bitbake and flash that image, to turn on the power, you can use this command.
gpioset 1-0070 5=1 6=1
Breaking it down,
- 1-0070 is the "GPIO chip"
- 5 is the 5th bit of the chip (starting from 0), and we set its value to 1. This is the 3.3V enable.
- 6 is the 6th bit of the chip, and we set its value to 1. This is the 5V enable.
Now, if you read the man page for gpioset, you find that the values you have set are not guaranteed to persist once gpioset returns. So your enabling of these pins might persist, but it may well simply be transient. You can get around this with the --mode=wait flag, but that ties up your command line.
Most likely what you want is to get your command line back yet have the values persist. You can use "gpioset --mode=wait ... &", but that's not really what you want - it does work, but it goes to sleep in the background waiting for user input.
Most likely what you want, at least in the case of a pin setting that that you want to be pseudo-permanent like power enable, is
gpioset --background --mode=signal 1-0070 5=1 6=1
The --background flag is similar to the "&", but it is compatible with "--mode=signal". This line will turn on the GPIO's, then the task will go into the background as a daemon, basically doing nothing but keeping its hold on the GPIO character device so that the pins do not revert to their default values.
If you do want to revert to 0 (power off), you simply kill the process and then just to be sure, follow up with
gpioset 1-0070 5=0 6=0
Where did the 1-0070 come from?
gpiodetect is a useful command (shown here on my machine segB1):
root@segB1:~# gpiodetect
gpiochip0 [omap-gpmc] (2 lines)
gpiochip1 [1-0070] (8 lines)
gpiochip2 [600000.gpio] (87 lines)
gpiochip3 [601000.gpio] (88 lines)
gpiochip4 [tpic2810] (8 lines)
We see the 1-0070.
You can then do this to see the individual lines that are available on that device
root@segB1:~# gpioinfo 1-0070
gpiochip1 - 8 lines:
line 0: "GPIO_CPSW2_RST" unused input active-high
line 1: "GPIO_CPSW1_RST" unused input active-high
line 2: "PRU_DETECT" unused input active-high
line 3: "MMC1_SD_EN" "fixed-regulator-sd" output active-high [used]
line 4: "VPP_LDO_EN" unused input active-high
line 5: "RPI_PS_3V3_En" "gpioset" output active-high [used]
line 6: "RPI_PS_5V0_En" "gpioset" output active-high [used]
line 7: "RPI_HAT_DETECT" unused input active-high
And we see our pins of interest there. Don't worry if they happen to be listed as inputs instead of outputs. That's just the OS guessing. Once you use the pin with gpioset, it will know.
When you're first trying to get your bearings, you can use gpiofind to find a pin by name. (The pin names are as given in the device tree file k3-sk642-evm.dts in the TI source tree.)
root@segB1:~# gpiofind RPI_PS_3V3_En
gpiochip1 5
It turns out that "gpiochip1" is just an alias for "1-0070" - you can use either term in the gpioset or gpioinfo commands:
Automatically Enabling Pi HAT Power on Boot
Assuming your Yocto/Arago build is using systemd (this is common, I believe), then what we can do is create a system service that turns on the power at boot. I named my service "rpi-connector-power". Once you've defined the service, then you can
- Turn on connector power with systemctl start rpi-connector-power
- Turn off connector power with systemctl stop rpi-connector-power
- Configure the system to automatically turn on connector power at boot with systemctl enable rpi-connector-power
- (And conversely, disable connector power at boot with systemctl disable rpi-connector-power)
To define the service, you create a file /lib/systemd/system/rpi-connector-power.service that defines the service. This file will basically specify the gpioset commands to turn power on and off.
I'll show you that file in a second, but most likely you simply want to have this be installed as part of your Yocto/Arago image. So here's how to do that.
The rpi-connector-power Recipe
This is what I did. I created a recipe that automatically sets up systemd with our service and that enables that service by default. We use the systemd.bbclass class from OpenEmbedded as a helper.
You should create these files and folders in your own custom Yocto layer (a.k.a. folder tree - I wish someone had told me in the beginning that a layer is just a fancy name for a folder tree), that you have added to build/conf/bblayers.conf.
Within your layer,
- Create a recipe folder "rpi-connector-power". Within that folder
- Create the file rpi-connector-power.bb with the contents shown below
- Create the file rpi-connector-power/rpi-connector-power.service (that's right, a second rpi-connector-power folder below the first) with the contents shown below.
- Add rpi-connector-power to your recipe's RDEPENDS. So it now should look like:
- RDEPENDS_${PN} = "\
libgpiod \
libgpiod-tools \
rpi-connector-power \
...
And lastly, here are the contents of those two files. Note that I list the license as "CLOSED" for the recipe, but that's just a convenience for me during development. Feel free to reuse this content and assign whatever license you like to it to your own work product.
rpi-connector-power.bb
DESCRIPTION = "Automatically turn on power to RPi HAT connector at boot"
# See the docs for systemd.bbclass for info
# Turns on the 3.3V power on bit 5, and the 5V power on bit 6, of I2C expander
# GPIO device 1-0070:
# gpioinfo 1-0070
#gpiochip1 - 8 lines:
# line 0: "GPIO_CPSW2_RST" unused input active-high
# line 1: "GPIO_CPSW1_RST" unused input active-high
# line 2: "PRU_DETECT" unused input active-high
# line 3: "MMC1_SD_EN" "fixed-regulator-sd" output active-high [used]
# line 4: "VPP_LDO_EN" unused input active-high
# line 5: "RPI_PS_3V3_En" unused output active-high
# line 6: "RPI_PS_5V0_En" unused input active-high
# line 7: "RPI_HAT_DETECT" unused input active-high
# If the license is anything other than CLOSED, then you have to provide LIC_FILES_CHKSUM.
LICENSE = "CLOSED"
inherit systemd
SYSTEMD_AUTO_ENABLE = "enable"
SYSTEMD_SERVICE:${PN} = "rpi-connector-power.service"
SRC_URI_append = " file://rpi-connector-power.service \
"
RDEPENDS_${PN} += " libgpiod-tools"
do_install() {
install -d ${D}/${systemd_unitdir}/system
install -m 0644 ${WORKDIR}/rpi-connector-power.service ${D}/${systemd_unitdir}/system
}
FILES:${PN} += "${systemd_unitdir}/system/rpi-connector-power.service"
rpi-connector-power.service
# Turns on and off the 3.3V power on bit 5, and the 5V power on bit 6, of I2C expander
# GPIO device 1-0070.
# To turn on the power, you start the service, then stop it to turn it off.
# $ gpioinfo 1-0070
# gpiochip1 - 8 lines:
# line 0: "GPIO_CPSW2_RST" unused input active-high
# line 1: "GPIO_CPSW1_RST" unused input active-high
# line 2: "PRU_DETECT" unused input active-high
# line 3: "MMC1_SD_EN" "fixed-regulator-sd" output active-high [used]
# line 4: "VPP_LDO_EN" unused input active-high
# line 5: "RPI_PS_3V3_En" unused output active-high
# line 6: "RPI_PS_5V0_En" unused input active-high
# line 7: "RPI_HAT_DETECT" unused input active-high
#
[Unit]
Description=Turn power on or off to the Raspberry Pi user connector on the AM64x SK board
[Service]
Type=forking
Restart=no
TimeoutSec=5min
IgnoreSIGPIPE=no
KillMode=process
GuessMainPID=no
RemainAfterExit=yes
ExecStart=/usr/bin/gpioset --background --mode=signal 1-0070 5=1 6=1
ExecStop=/bin/sh -c "/bin/kill -TERM `ps -ef | grep 'gpioset --background --mode=signal 1-0070 5=1 6=1' | grep -v grep | awk '{print $2}'` && gpioset 1-0070 5=0 6=0"
[Install]
WantedBy=multi-user.target
Now bitbake your image and flash it. On next boot, you can verify that it is working by verifying that the gpioset process is running in the background
root@segB1:~# ps -ef | grep gpioset
root 401 1 0 01:47 ? 00:00:00 /usr/bin/gpioset --background --mode=signal 1-0070 5=1 6=1
and by checking with your voltmeter that there is 3.3V between pins 6 and 1 of the user connector, and 5V between pins 6 and 2.
As stated earlier, you can turn the power off with
systemctl stop rpi-connector-power
and turn it back on with
systemctl start rpi-connector-power
Hope this has been useful.