uio_pruss.c question

Can somebody explain to me what this code snippet does? It's from the
uio_pruss.c driver. It looks like, for each event channel, it
establishes two separate memory regions for each event channel,
(which I think is a uio_mem) ... but that every event channel gets the
SAME two. What's the point of mapping once per event channel?

        for (cnt = 0, p = gdev->info; cnt < MAX_PRUSS_EVT; cnt++, p++)
{
                p->mem[0].addr = regs_prussio->start;
                p->mem[0].size = resource_size(regs_prussio);
                p->mem[0].memtype = UIO_MEM_PHYS;
                p->mem[1].size = sram_pool_sz;
                p->mem[1].memtype = UIO_MEM_PHYS;

                p->mem[2].addr = gdev->ddr_paddr;
                p->mem[2].size = extram_pool_sz;
                p->mem[2].memtype = UIO_MEM_PHYS;

                p->name = kasprintf(GFP_KERNEL, "pruss_evt%d", cnt);
                p->version = DRV_VERSION;

                /* Register PRUSS IRQ lines */
                p->irq = gdev->hostirq_start + cnt;
                p->handler = pruss_handler;
                p->priv = gdev;

                ret = uio_register_device(&dev->dev, p);
                if (ret < 0)
                        goto out_free;
        }

Hi Christopher,

I've been wondering myself about this implementation too. If you look at this code (and at an older version) you'll see that the second (mem[1]) region used to point to on-chip SRAM, for which there is no implementation for the 335x (yet?). It looks like someone just removed the line with the p->mem[1].addr assignment and a leading blank line. So I'm not sure about the state of this driver...

FYI: If you tweak the right registers the PRUSS can be used and the old pasm produces working code. I've been able to modulate ('glow') one of the user leds, depending on the system load. So communcation from user space to a PRU and from that PRU to GPIO can be done. But beware, the PRU - like a DMA controller - has access to almost the entire memory map, and it's very easy to overwrite kernel code or other data by mistake.

--- Bas

Oh, wow, I didn't even notice that there were THREE memory sections
defined in there.

It would help me if I could figure out what these two things return:

                p->mem[0].addr = regs_prussio->start;
                p->mem[0].size = resource_size(regs_prussio);

I guess I assume that regs_prussio is the global address in L3. I'm
wondering if resource_size(regs_prussion) returns enough that I can
see all the control registers, IRAM0, IRAM1, DMEM0, DMEM1, and the
SHARED 12K of data memory.

By tweaking registers, do you have to tweak registers in order to get
the old pasm to work? Reports are that a pasm_2 is going to be
released to developers in March some time, but I would like to get
started now if at all possible.

I'm really happy to know somebody besides me is thinking about these
things.

Another question. The IRQ handler does this:

        /* Disable interrupt */
        iowrite32(intr_bit, intrdis_reg);
        return IRQ_HANDLED;

What does this mean - does the userspace side have to turn the
interrupt back on if it wishes?

Oh, wow, I didn't even notice that there were THREE memory sections
defined in there.

It would help me if I could figure out what these two things return:

                 p->mem[0].addr = regs_prussio->start;
                 p->mem[0].size = resource_size(regs_prussio);

This looks like the io map for the PRUSS submodule. It spans all PRUSS registers and memories. See 4.3.2 in the TRM.
The other maps aren't needed to get the PRUSS working. But if some of the global settings are wrong (depends on the kernel you're using) you'll need access to some other parts of the chip to get the PRUSS running...

I guess I assume that regs_prussio is the global address in L3. I'm
wondering if resource_size(regs_prussion) returns enough that I can
see all the control registers, IRAM0, IRAM1, DMEM0, DMEM1, and the
SHARED 12K of data memory.

IIRC the region size is (correctly) set to 16K / $4000.

By tweaking registers, do you have to tweak registers in order to get
the old pasm to work? Reports are that a pasm_2 is going to be
released to developers in March some time, but I would like to get
started now if at all possible.

No, the old pasm just works. You'll need to tweak some settings to enable the PRUSS submodule, give it a clock, clock the outgoing bus, set some power management options. It's a steep learning curve but it can be done :wink:

I'm really happy to know somebody besides me is thinking about these
things.

There are more. I can't image all the others have given up already, so ask your questions and you might get an answer.

--- Bas

There are more. I can't image all the others have given up already, so
ask your questions and you might get an answer.

OK. What if you want to use the two PRUs to do two totally different
things, with two separate userspace drivers? Does this driver allow
that?

First of all, it's (uio_pruss.c) not really a driver. The only thing it does is to make the PRUSS I/O space available for a userspace driver. The latter has to be written by you and takes complete control over the PRUSS (i.e. both PRUs). You should also be able to get events from the PRUSS to userspace, but I haven't tried that yet.

So yes, you should be able to run two independent userspace drivers, but you might run into problems accessing the parts that are shared by both PRUs (locking, atomic access, synchronization etc.).

--- Bas

Ah, yes. That makes sense.

I am going to think about the idea that UIO isn't what I want here,
and do a more traditional kernel device driver. What I have to do is
integrate four high speed A/D converters for structural vibration
analysis. I like the UIO concepts of handling interrupts (events) and
mmap() but what I'm thinking is maybe it needs to be something smart
enough to enforce mutual exclusion of shared register access, as you
alluded to.

I'll have to give this some more thought.

You'll need to tweak some settings to enable the PRUSS submodule, give it a clock, clock the outgoing bus, set some power management options.

If I may clarify that, in uio_pruss.c, should this take care of
powering it on and configuring the clock?

        /* Power on PRU in case its not done as part of boot-loader */
        gdev->pruss_clk = clk_get(&dev->dev, "pruss");
        if (IS_ERR(gdev->pruss_clk)) {
                dev_err(&dev->dev, "Failed to get clock\n");
                kfree(gdev->info);
                kfree(gdev);
                ret = PTR_ERR(gdev->pruss_clk);
                return ret;
        } else {
                clk_enable(gdev->pruss_clk);
        }

On the PRU wiki exists a kernel patch that touches these files (just
the source files)

* arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c
* arch/arm/mach-davinci/devices-da8xx.c
* arch/arm/mach-davinci/include/mach/da8xx.h
* drivers/uio/uio_pru.c

However, those are all for different devices - I'm working on AM3359
whose arch is I believe mach-omap2. What I'm not sure of is whether
or not that machine type somehow inherits from davinci.

Looking at that patch in detail a lot of what it does is add platform
resources. I don't see that in my kernel, and I'm going to have to
figure that out.

  $ grep -R platform_device_register arch/arm/* | grep -i pru

returns nothing, making me think the support isn't there, and I may
have to make my own patch that does what the davinci one does.

Does this sound like the right track?

Bas thank you so much for your help and advice.

--C

I think I found it.

   arch/arm/mach-omap2/clock3xxx_data.c

defines a bunch of clocks ... but it looks like none for the PRU
subsystem. I'm working off of a kernel named

   linux-3.0+3.1-rc8-psp04.06.00.02.sdk-psp04.06.00.02.sdk

from the T.I. SDK, I think my next step better be to check newer
kernels from angstrom and see if any of this has been changed/
augmented.

Ah, yes. That makes sense.

I am going to think about the idea that UIO isn't what I want here,
and do a more traditional kernel device driver. What I have to do is
integrate four high speed A/D converters for structural vibration
analysis. I like the UIO concepts of handling interrupts (events) and
mmap() but what I'm thinking is maybe it needs to be something smart
enough to enforce mutual exclusion of shared register access, as you
alluded to.

I'll have to give this some more thought.

You'll need to tweak some settings to enable the PRUSS submodule, give it a clock, clock the outgoing bus, set some power management options.

If I may clarify that, in uio_pruss.c, should this take care of
powering it on and configuring the clock?

         /* Power on PRU in case its not done as part of boot-loader */
         gdev->pruss_clk = clk_get(&dev->dev, "pruss");
         if (IS_ERR(gdev->pruss_clk)) {
                 dev_err(&dev->dev, "Failed to get clock\n");
                 kfree(gdev->info);
                 kfree(gdev);
                 ret = PTR_ERR(gdev->pruss_clk);
                 return ret;
         } else {
                 clk_enable(gdev->pruss_clk);
         }

There are several bits that need to be set correctly. This may solve the clock issue, but I just tried with a recent the 3.2.0+ kernel and the PRUSS is left with the reset asserted, so you'll only get bus errors accessing the PRUSS I/O map. Also, if you want to access peripherals that are not connected to the PRU, you'll have to enable the clock for the outgoing bus first.

On the PRU wiki exists a kernel patch that touches these files (just
the source files)

  * arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c
  * arch/arm/mach-davinci/devices-da8xx.c
  * arch/arm/mach-davinci/include/mach/da8xx.h
  * drivers/uio/uio_pru.c

However, those are all for different devices - I'm working on AM3359
whose arch is I believe mach-omap2. What I'm not sure of is whether
or not that machine type somehow inherits from davinci.

Looking at that patch in detail a lot of what it does is add platform
resources. I don't see that in my kernel, and I'm going to have to
figure that out.

   $ grep -R platform_device_register arch/arm/* | grep -i pru

returns nothing, making me think the support isn't there, and I may
have to make my own patch that does what the davinci one does.

Does this sound like the right track?

There's a lot of work in progress wrt the arm port. But little documentation and it's a moving target.
If you have a schedule to meet I suggest you skip all the nice clock setting schemes that are being implemented and do direct access to the corresponding registers from a kernel driver.

--- Bas

Good starting point. I started with the TI kit too and moved to the angstrom kernel only recently. I found development in the oe tree a challenge and didn't notice any progress on the PRUSS code. So I might switch back to the TI kernel again.

--- Bas

I hope this isn't a really dumb question but ...

Why do I really need the userspace loader to load the PRU's IMEM? Why
can't I have an appropriately privileged program open /dev/mem and
mmap() to the global address of the PRUSS in L3, then just write the
PRU program directly to IMEM that way?

Sorry that I don't have anything to contribute at the moment but I
thought I should speak up and let you know that you have at least one
very, very interested lurker.

Evaluating the PRU's capabilities is my #1 priority for my Beaglebone
work and I am quite excited that it might be able to do some really
great things for me. So threads like this, which will help me find
tools and modules, are just pure gold as far as I am concerned.

(I might as well mention what I have in mind, just in case anybody has
some clever suggestions for me. I would like to program one PRU core
to poll a small number of input bits (5-20) at maximum possible rate.
Whenever there is a change in any one of these bits I would want to
record the bits plus a timestamp from the TCRR register (perhaps the
other PRU will pick these up, or perhaps the AM3359 will). These
input bits won't change very often (only a few times a second each),
but I would like to be able to get sub-microsecond timestamping of
when the changes happen.)

Bas and Christopher,

I’m just getting started with the BeagleBone PRUSS. I’m running kernel version 3.2.14 and have installed the uio_pruss kernel module.

I use the following to enable the PRUSS:

devmem2 0x44E00C00 w 0

And I can kind of run the GPIO toggling demo code provided on the TI web site. (i.e. I’m no longer getting a segfault, just a hang!) I made a PRUSS binary with only a single instruction (HALT) in it for debugging because the application kept hanging “waiting for HALT”. After some debugging I found that the following read statement is blocking in prussdrv.c waiting for values to read from /dev/uio0.

int prussdrv_pru_wait_event(unsigned int pru_evtout_num)
{
int event_count;
unsigned int *pruintc_io = (unsigned int *) prussdrv.intc_base;
read(prussdrv.fd[pru_evtout_num], &event_count, sizeof(int));
pruintc_io[PRU_INTC_HIEISR_REG >> 2] = pru_evtout_num+2;
return 0;
}

I’m still in the process of figuring out what I need to learn to resolve the issue, but obviously when the HALT instruction is executed (if it is even being executed) there is nothing being returned on /dev/uio0 and the read blocks.

So many questions…

  1. Am I even on the right track?

  2. Or is there a better way to approach this thing?

  3. Is this code even compatible with the uio_pruss kernel module that is available via opkg?

opkg list | grep pru

kernel-module-uio-pruss - 3.2-r9a+gitr09e9651bcf2ee8d86685f2a8075bc6557b1d3b91 - linux-ti33x-psp version 3.2-r9a+gitr09e9651bcf2ee8d86685f2a8075bc6557b1d3b91
uio-pruss kernel module

Thanks for your input.
Jerrill

Hi Jerrill,

I had set the uio_pruss stuff before I switched to working with my code completely in the kernel, so I haven’t worked with the uio_pruss driver in a while.

The place where you are most certainly stuck has to do with the IRQs not being setup correctly. If I remember correctly, TI’s demo firmware has a number of demos that does some stuff, sends an IRQ to the Cortex-A8 and then halts. Your code is waiting for the IRQ, which it sounds like you aren’t setting up as you just are sending the halt instruction.

The code you shared above is doing a blocking read on the uio driver waiting for an IRQ (“PRU event”) which doesn’t happen.

  1. Am I even on the right track?

I think so. My advice though is to ignore the events/interrupts for now and start with something much simpler - a PRU program that runs by itself, with no external requirements, that you can load, start, stop, and then inspect what it has done. Maybe you are already past this point, in which case you should ignore me. However, if you’re not, just try something like this:

start:
MOV32 r1, 0

l1:

// Increment r1
ADD r1, r1, 1

JMP l1

then use devmem2 to just see that r1 is counting up (at a very high rate). If you haven’t already done that, I’d start there first.

  1. Is this code even compatible with the uio_pruss kernel module that is available via opkg?

I believe so. The main issue I had was taking the PRUSS completely out of reset when the driver is loaded. There were three ways to fix this: work around it with something like devemem2; use the patch jason w. and I made; or do it the way Vaibhav did it - which means a fix to the machine definition in the kernel, that causes everything to turn on and get into the right state as soon as uio_pruss.ko tries to grab the clock.

Where I can’t help you with is whether or not that change has made it into an OE, TI, or some other kernel.

It shouldn’t stop your development, though - you can work around it with devmem2 or something similar for now (you could even have your userspace program do it if you’re willing to mmap the right place in physical memory - not a good long term solution but if it gets you going during development…)

I hope that helps. If not, write back.

Ah, yes. That makes sense.

I am going to think about the idea that UIO isn’t what I want here,
and do a more traditional kernel device driver. What I have to do is
integrate four high speed A/D converters for structural vibration
analysis. I like the UIO concepts of handling interrupts (events) and
mmap() but what I’m thinking is maybe it needs to be something smart
enough to enforce mutual exclusion of shared register access, as you
alluded to.

I’ll have to give this some more thought.

You’ll need to tweak some settings to enable the PRUSS submodule, give it a clock, clock the outgoing bus, set some power management options.
If I may clarify that, in uio_pruss.c, should this take care of
powering it on and configuring the clock?

/* Power on PRU in case its not done as part of boot-loader */
gdev->pruss_clk = clk_get(&dev->dev, “pruss”);
if (IS_ERR(gdev->pruss_clk)) {
dev_err(&dev->dev, “Failed to get clock\n”);
kfree(gdev->info);
kfree(gdev);
ret = PTR_ERR(gdev->pruss_clk);
return ret;
} else {
clk_enable(gdev->pruss_clk);
}

There are several bits that need to be set correctly. This may solve the
clock issue, but I just tried with a recent the 3.2.0+ kernel and the
PRUSS is left with the reset asserted, so you’ll only get bus errors
accessing the PRUSS I/O map. Also, if you want to access peripherals
that are not connected to the PRU, you’ll have to enable the clock for
the outgoing bus first.

On the PRU wiki exists a kernel patch that touches these files (just
the source files)

  • arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c
  • arch/arm/mach-davinci/devices-da8xx.c
  • arch/arm/mach-davinci/include/mach/da8xx.h
  • drivers/uio/uio_pru.c

However, those are all for different devices - I’m working on AM3359
whose arch is I believe mach-omap2. What I’m not sure of is whether
or not that machine type somehow inherits from davinci.

Looking at that patch in detail a lot of what it does is add platform
resources. I don’t see that in my kernel, and I’m going to have to
figure that out.

$ grep -R platform_device_register arch/arm/* | grep -i pru

returns nothing, making me think the support isn’t there, and I may
have to make my own patch that does what the davinci one does.

Does this sound like the right track?
There’s a lot of work in progress wrt the arm port. But little
documentation and it’s a moving target.
If you have a schedule to meet I suggest you skip all the nice clock
setting schemes that are being implemented and do direct access to the
corresponding registers from a kernel driver.

— Bas

Bas thank you so much for your help and advice.

–C

Bas and Christopher,

I’m just getting started with the BeagleBone PRUSS. I’m running kernel version 3.2.14 and have installed the uio_pruss kernel module.

I use the following to enable the PRUSS:

devmem2 0x44E00C00 w 0

And I can kind of run the GPIO toggling demo code provided on the TI web site. (i.e. I’m no longer getting a segfault, just a hang!) I made a PRUSS binary with only a single instruction (HALT) in it for debugging because the application kept hanging “waiting for HALT”. After some debugging I found that the following read statement is blocking in prussdrv.c waiting for values to read from /dev/uio0.

int prussdrv_pru_wait_event(unsigned int pru_evtout_num)
{
int event_count;
unsigned int *pruintc_io = (unsigned int *) prussdrv.intc_base;
read(prussdrv.fd[pru_evtout_num], &event_count, sizeof(int));
pruintc_io[PRU_INTC_HIEISR_REG >> 2] = pru_evtout_num+2;
return 0;
}

I’m still in the process of figuring out what I need to learn to resolve the issue, but obviously when the HALT instruction is executed (if it is even being executed) there is nothing being returned on /dev/uio0 and the read blocks.

So many questions…

  1. Am I even on the right track?

Make sure whether you really need an interrupt. There are other ways to signal a process in user space. I’m using fifo’s and shared memory right and have no need for interrupts up to now.

– Bas

One thing I want to add to what Bas said: holding it in reset was one
problem I had, but there was another. It also was leaving the L3
interconnect i some kind of "smart" state that made it so that if your
PRU program tried to access global resources over L3 (or L3 to L4) it
would just hang the PRU indefinitely. This is something else to watch
out for, but it won't be an issue if your PRU software doesn't use
these kinds of resources (for example main memory).

--C

Baas: the PRU core needs to notify the Cortex-A8 and Linux that something has happened. There is no other way to do this other then to poll the PRU RAM or some other memory space. The UIO driver allows a block reading that blocks until a interrupt occurs, you’d have to poll here instead. Communicating with userspace isn’t really the issue.

Chris: The STANSBY_INIT (as well as STANDBY_MODE) bit will disable access to L3 connected peripherals. I had this problem too when accessing GPIO and SPI peripherals as well as accessing the DDR2 memory.

Christopher,

Well, I think I have the counting program working but I’m stuck on the devmem2 step.

Which address do I use for R1 via devmem2? How can I figure that out for myself from the documentation?

I’ve been trying to figure this out from the TRM and haven’t had much luck, though admittedly I haven’t set down and read the 250-page PRUSS section cover-to-cover yet, much less the other 4250 pages. :slight_smile: I see that the PRUSS is 0x4A300000 and that the registers may be in the CFG space at offset +0x00026000 but not much beyond that.

I was able to find the PRUSS reset bit documentation working backward from address 0x44E00C00.

What’s the best way to figure these addresses out?

Thanks,

Jerrill