PRUSS activation in angstrom with 3.2 kernel / am335x / bone

Hi,

I've been making progress in my PRU experiments. Things are looking
promising, but there are still a couple of loose ends.

I made a new friend on freenode #beagle who contributed this:

    https://github.com/Angstrom-distribution/meta-ti/commit/d04a83f7ee7d4a5f51aebc97f444b1f0c19fb0a0

I have suggested that UIO_PDRV* be changed from 'y' to 'm' because I
really don't want those turned on unless I need them, and he agreed
that was probably a good idea. They don't take up much memory, but
they do register themselves as additional uio drivers, so if they're
not needed I think most people would prefer they be turned off.
Really, you could make the exact same argument about the UIO framework
(top level UIO driver) as well. To give a more concise view of what I
recommend, I'd like to see the default angstrom distribution set:

CONFIG_UIO=m
CONFIG_UIO_PDRV=m
CONFIG_UIO_PDRV_GENIRQ=m
CONFIG_UIO_PRUSS=m

However, I want to be somewhat humble about recommending that the
community do what I want just because it's what I want, vs. what's
best for the masses. The thing is, there's a pretty steep learning
curve involved in figuring out how to take what comes out of bitbake
and modifying it, so I think there's value in just saying compile
these things as modules, let bitbake generate opkg packages out of
them, then the end user can decide what to do about installing those
packages and letting modprobe handle dependencies.

The second thing is a little more difficult. The current release of
uio_pruss.c seems to be doing some things right:

* PRUSS memories are turned on
* PRUSS clocks are turned on
* PRUSS subsystem is powered on

HOWEVER: the PRUSS subsystem is still held in reset, and in that
state, it's offline to the L3/L4 interconnects, meaning I can't talk
to it at all. A temporary workaround is to install devmem2 and do:

   modprobe uio_pruss && devmem2 0x44E00C00 w 0

The address I'm setting there is RM_PER_RSTCTRL, and what I'm doing is
releasing the PRUSS from reset. For details see AM335x TRM section
8.1.3.2.1 (table 8-182).

If you want to verify that this is the problem, I suggest doing this.
Reboot the board, modprobe uio_pruss, then "devmem2 0x44E00C00 w" - in
my case ti told me it was "3" (not sure why it wasn't '2" but ok)
which means PRUSS local reset control = ASSERTED. Enabling the PRU
shouldn't make it run (can someone confirm I am right about this?) as
the run state is controlled by PRUSS_PRU_CTRL registers (there is one
for each PRU). The reset sstate of PRUSS_PRU_CTRL.CONTROL bi 1
(ENABLE) is 0, which means that even though the PRUSS is out of reset,
the individual PRUs should be disabled.

My question, then, is this: how do we get this code changed to take
the PRUSS out of reset when uio_pruss is loaded? Options:

1) Hack it into uio_pruss.c (probably not a good idea)

2) create the correct linkages so that enabling the PRUSS clock, which
enables the PER clock domain, which enables the PER power domain,
somewhere in there also takes the PRUSS out of reset. I THINK this is
the correct way to solve the problem, but this clock tree stuff is new
to me and I don't yet undersatnd all of the linkages -- so I'd like to
ask for help from someone on the mailing list who not only knows how
to do this but can actually make it happen and get it committed.

Tell me where I'm off target here, but be kind as I'm still in
learning mode.

--Chris

Have you made any progress on this?

Your (2) is correct, but haven’t looked at implementing it yet. I have a IIO driver that interfaces with the PRUSS, but with the 3.2.11 kernel I’m using it is stuck in reset. If I manually kick it with devmem2 it works, otherwise I get data aborts.

I can confirm that changing the register @ 0x44e00c00 from 0x3 → 0x0 the PRUSS does work. Both PRU cores, IRQs, eCAP, OCP and all. I’m thinking about publishing my pruss mfd driver I’ve been using if people are interested in implementing kernel drivers.

  • Kyle

Yes, I did. I originally patched the PRUSS UIO driver, but then
someone helped me figure out that the only reason that was necessary
was that enabling everything through clock tree is the way to go, and
that the work had already been done (just not made it into angstrom).
So, everything is working.

I'm now working on a debugger. It's an ncurses based application that
lets you load code, set/view registers and memory, set soft
breakpoints, and single step (the latter using facilities described in
the TRM). I made an interface to which I can adapt the UIO driver,
another skunkworks driver that I know about, and I imagine yours - you
can take a look at the interface if you want and let me know:

   https://github.com/wz2b/prude/blob/master/Pruss.hpp

and

   https://github.com/wz2b/prude/blob/master/Pru.hpp

are the relevant files. I don't want to overstate my progress on this
- I have really just gotten started, there's no real functionality yet
- just some interfaces and the start of the user interface.

I should say, too, that I'm not especially happy with the UIO approach
to this. I find it to be a pain from languages other than C/C++. In
particular, I am doing a lot of java work and the behavior of NIO's
MMAP on java is not good on something that's not a real file. The
Oracle JRE at least fails because before you can mmap a chunk of
memory it checks the file size to make sure it's at least that big.
When the "file" is a device, Java thinks its size is zero and it fails
to mmap. Non-Oracle JREs may behave differently, I don't know - and
it doesn't really matter to me, it'd be great if it also worked in
OpenJDK but I want it to work with Oracle as well (at least for a
while longer).

What I really want is a read/write/seek interface for simpler
operations that require lower performance - for example, if I could
just do this:

   # cat mypruapp.bin > /sys/pruss/pruss0/pru0/imem

it be awfully nice for certain simple testing. I wouldn't throw the
mmap interface out entirely for performance reasons (especially what
the UIO driver calls the "dma" memory) but a lot of operations are
linear enough that seek and read wouldn't be all that much more
expensive (in any relevant way).

My thought, then, was to add read/write/seek to the UIO driver, or, if
that's not possible, make a character-mode driver that also supports
mmap. The userspace libraries would have to be fixed (they are
missing a critical function anyway and need work).

How does that line up with your proposed driver?

--Chris

Hi Christopher,

Thanks for your prompt reply. Some more digging revealed 0002-Release-PRUSS-from-reset-on-enable-for-the-am33xx-be.patch from Jason Winnebeck which fixes the problem correctly.

As for my driver, it is completely kernel space. I use it to create kernel IIO devices that talk with the PRU to buffer data to high speed ADC devices. I’m not using UIO at all and don’t want to. UIO would be nice to avoid creating kernel code, but in my case I need my code to be in the kernel so I can use the IIO framework.

One comment on your code where you talk about loading the firmware, I’d suggest doing this with the the firmware_class code already in the kernel, look at request_firmware(). I use this in my driver and it works great. Drop your binary firmware in to /lib/firmware and then call request_firmware() and the kernel will copy it to kernel space for you.

Some more digging revealed 0002-Release-PRUSS-from-reset-on-enable-for-the-am33xx-be.patch from Jason Winnebeck which fixes the problem correctly.

Yep. Jason and I did that together. However, Vaibhav Hiremath said that wasn’t the “correct” way to fix the problem. What we did was put in a hook that says “When you activate this device, also release it from reset.” What Vaibhav said SHOULD happen is that our driver requests the clock, the clock tree gets activated from that point up (notice the driver requests the PRUSS UART clock, the bottom of the tree) and at some point a field in the tree of structures tells it “You also have to set this bit in this register to release it from reset.”

I didn’t understand that at the time we offered the patch you reference above.

Either way, your driver should not do anything but request the clock, and everything else should happen automatically, by either mechanism… my opinion is you should use our patch but only until the clocktree way Viabhav mentioned is available in whatever kernel you are using (am335x-linux, something from angstrom, something from Koen, whatever).

–C

OH boy, you are really testing my memory here - I haven’t touched this stuff in quite a while. Let me try to refresh my memory.

0x44E0_0C00 is the address of PRM_PER, the Power and Reset Module for peripherals. Register offset 0 from there is RM_PER_RSTCTRL which releases the PER. The bit taht matters is bit 1 i.e. b’00000010’.

If you see the value ‘1’ that shows that the PRUSS_LRST bit is in state 0 which means “CLEAR : Reset is cleared for the PRUSS.” That’s good, it means it’s out of reset. If it had said the value was 3, that would mean the PRUSS reset domain was asserted, which would mean the PRU wouldn’t do a darned thing.

It has been a while since I have touched this, but if the PRU isn’t running the first thing to check is that it has a clock enabled. I would tell you how I did this but I’m certain my information is out of date because I haven’t touched this in so long. Chances are pretty good that a recent kernel just does the right thing for you.

I’m not sure what test program you’re using, but when I was first playing around with this stuff I wrote a little pru program that puts DEADBEEF into a memory location and then just keeps incrementing a second location in a loop. There’s a small native program that just loads it (but you can use your own method if you wish), then I would manually run the program by taking the PRUSS out of reset, let it run for a while, stop it, then examine memory to see if it was running. Your mileage may vary, but you can find it here if it’s helpful to you:

http://www.autofrog.com/~chrisp/arm/pruss/count.zip

I’m sorry for being so rusty. I hope to get back to this stuff soon …

–Chris