Enabling the PRU on BeagleBone from userspace

I am trying to enable the PRU from userspace, just to run some butt
simple tests, with no luck so far. I wrote a really simple shell
script to do this (see it pasted below).

My thought is this, please correct me if you see anything wrong. I
need to do these things:

* Enable the PRUSS clock in CM_PER_PRUSS_CLKCTRL
* Enable power to the PRUSS in PM_PER_PWRSTCTRL
* Take the PRUSS out of reset in RM_PER_PWRRSTCTRL

I don't think I should have to mess with the PLL configuration
because, if I read the AM335x TRM correction, table 4-2 says that it
needs:

* an interface clock, which is CORE_CLKOUTM4, which must already be
running or the system wouldn't be
* a functional clock, which is also CORE_CLKOUTM4
* A uart clock, which is from PER_CLKOUTM2, which I don't care about
right now.

I'm trying to verify that it's there by just reading the REVID
register out of the PRUSS_CFG block, offset 0. The address of that is

   PRUSS base + PRUSS_CFG offset + REVID offset = 0x43A00000 +
0x00026000 + 0x00000000

I'm getting bus errors, so I'm obviously missing something. My
assumption was that to be able to read REVID, I would need to have the
PRUSS enabled and the interface clocks up so I could talk to it
through the L4 interconnect. I also took the subsystem out of reset,
but I'm not 100% sure that's even necessary based on the source code
patches that supposedly get uio_pruss.ko up and running.

What am I missing?

(script below)

--Chris

#!/bin/sh
set -e

CM_PER=0x44E00000
CM_PER_PRUSS_CLKCTRL=$(( $CM_PER + 0xE0 ))

PRM_PER=0x44E00C00
RM_PER_PWRRSTCTRL=$(( $PRM_PER + 0x00 ))
PM_PER_PWRSTST=$(( $PRM_PER + 0x08 ))
PM_PER_PWRSTCTRL=$(( $PRM_PER + 0x0C ))

write32() {
        printf '\tWRITING 0x%08x : 0x%08x\n' $1 $2
        devmem2 $1 w $2 | grep -v mapped | grep -v opened
}

read32() {
        printf '\tREADING 0x%08x\n' $1
        devmem2 $1 w
}

echo
echo "Enabling PRU clock"
write32 ${CM_PER_PRUSS_CLKCTRL} 0x00000004

echo
echo "Enabling PRU power"
write32 ${PM_PER_PWRSTCTRL} 0x660000E3

echo
echo "Taking PRU out of reset"
write32 ${RM_PER_PWRRSTCTRL} 0x00000000

echo
echo "Trying to read some data"

# This should show the revision ID of the PRU
read32 0x4a326000

I really hope nobody wastes time answering me. The problem is that,
apparently, I need to go to the eye doctor, beacuse I had an offset
for CM_PER_PRUSS_CLKCTRL of 0xE0 when it should have been 0xE8. '0'
and '8' are only different by what, four pixels at most? Sheesh.
Sorry for the noise.

Well, in case this helps anybody else along the way, let me post an
updated version of the script. This one works, it returns REVID =
0x47000000 (which is correct, according to TRM table 4-190) and a
SYSCFG that seems to make sense.

#!/bin/sh
set -e

CM_PER=0x44E00000
CM_PER_PRUSS_CLKCTRL=$(( $CM_PER + 0xE8 ))

PRM_PER=0x44E00C00
RM_PER_PWRRSTCTRL=$(( $PRM_PER + 0x00 ))
PM_PER_PWRSTST=$(( $PRM_PER + 0x08 ))
PM_PER_PWRSTCTRL=$(( $PRM_PER + 0x0C ))

write32() {
        printf '\tWRITING 0x%08x : 0x%08x\n' $1 $2
        devmem2 $1 w $2 | grep -v mapped | grep -v opened
}

read32() {
        printf '\tREADING 0x%08x\n' $1
        devmem2 $1 w
}

echo
echo "Enabling PRU clock"
write32 ${CM_PER_PRUSS_CLKCTRL} 0x00000002

echo
echo "Enabling PRU power"
write32 ${PM_PER_PWRSTCTRL} 0x660000E3

echo
echo "Taking PRU out of reset"
write32 ${RM_PER_PWRRSTCTRL} 0x00000000

echo
echo "Trying to read REVID"
read32 0x4a326000

echo
echo "Trying to read SYSCFG"
read32 0x4a326004

I really hope nobody wastes time answering me. The problem is that,
apparently, I need to go to the eye doctor, beacuse I had an offset
for CM_PER_PRUSS_CLKCTRL of 0xE0 when it should have been 0xE8. ‘0’
and ‘8’ are only different by what, four pixels at most? Sheesh.
Sorry for the noise.

Thanks for all of the information below. This is really useful to have documented like this, so this type of noise is quite welcome. I do hope, however, people use this approach as a solution. There is a uio driver for the PRU by my understanding that can be used to control the PRU. I haven’t started to mess it with it myself yet, but I hope the existing Linux kernel infrastructure is examined before throwing it away to poke the hardware directly.

Thanks for all of the information below. This is really useful to have
documented like this, so this type of noise is quite welcome. I do hope,
however, people use this approach as a solution. There is a uio driver for
the PRU by my understanding that can be used to control the PRU.

I haven't actually tried it, but there's one with the latest angstrom
and latest T.I. kernels. They *look* correct to me. I spent a lot of
time Friday (with a lot of help from some very nice people on
freenode/#beagle) figuring all of this out, but in the process I
looked at uio_pruss.c (the UIO driver) and it actually looks to like
like it should work. Consultation with a gentleman at T.I. first lead
me to believe I was going to need kernel patches, but I really don't
think that's going to turn out to be the case.

The uio_pruss.c code is actually pretty simple. There is some
external SRAM mapping that doesn't apply to AM335x (at least not yet),
but the AM335x has a lot more RAM than older PRUs so I don't think it
really matters that it's not available here.

One thing that was initially very confusing is that in the kernel, the
PRUSS is referred to as ICSS, which, had I older OMAP experience, I
would have known stands for Industrial Communication Subsystem. I
gather that's an older name for the PRU.

The uio driver turns the PRU on by enabling its clock like this:

        gdev->pruss_clk = clk_get(&dev->dev, "pruss");

For the bone, "pruss" is actually a clock alias:

        board-am335xevm.c: if (clk_add_alias("pruss", NULL,
"icss_uart_gclk", NULL)) ...

So the power on sequence is that the UIO driver turs on the PRU UART
lock, which is part of a clock domain "icss_ocp_clkdm" and whose
parent is per_192mhz_clk. Essentially, what seems to happen is that
enabling the PRU UART clock causes everything else to be enabled by
parent relationship. Were it me I wouldn't have designed it that way,
because if I don't need the PRU UART, why un-gate its clock? (Not
100% certain that is really legal - the TRM makes it sound like you
have to turn on all three, but I'm skeptical).

The next issue, after clocks, is powering the PRU up. The UIO driver
makes no direct attempt to do that but, again, it all seems to happen
because of the data stuctures set up in clk33xx_data.c -- enabling the
clock causes the power to be turned on. I'm not clear exactly how
this happens but if you look in powerdomains33xx_data.c there's a
structure per_33xx_pwrdm.... and it specifically references the
enables that turn on ICSS.

I'm talking here out of code review; I haven't actually tried this
yet. I started with my devmem2 script because I wanted an easy way to
peer into the memory. I don't think it's a good idea to use this
approach for anything real, I just think it's an interesting, quick
and dirty debugging tool.

This is really useful to have documented like this, so this type of noise is quite welcome

OK, as I keep learning I'll share. Right now my mood is "quite optmistic."

--Chris