Any progress here?
Yes, some. I’ll document it here for posterity.
With the kernel I’m currently using (it’s an Angstrom 3.2.0 something) allocating a timer from the kernel driver works and is easy:
struct omap_dm_timer *timer1;
struct omap_dm_timer *t = omap_dm_timer_request();
printk(KERN_INFO DRV_NAME “: Allocated timer %d, irq %d, base address %p\n”, t->id, t->irq, t->io_base);
On my system, this returned timer0 for the first call, and timer3 for the second call (I allocated two timers). One thing that does bother me is that the “base address” returned is all wrong … it looks like a virtual memory address. What I did was to ignore that address and simply pass the PRU the t->id then let the PRU figure out where that lived.
I seemed to be on the right path, but remember: my point here was to get a timer running continuously generating an event visible to the PRU. That last part turns out to be a problem on the AM335x.
The timers on the AM335x map to:
Timer 0: IRQ66
Timer 1: IRQ67
Timer 2: IRQ68
Timer 3: IRQ69
Timer 4: IRQ92
Timer 5: IRQ93
Timer 6: IRQ94
Timer 7: IRQ95
However, take a look at the PRU chapter of the TRM, in the Functional Description of the PRUSS interrupt controller:
The PRUSS INTC supports up to 64 system interrupts from different peripherals …
Interrupts on the PRU get mapped from IRQ to Channel to Host (see TRM section 4.6.2). The first level of mapping (IRQ to channel) is controlled by registers CMR0 through CMR15, and if you look at the last one it confirms the above statement. The highest interrupt that can be mapped to a PRUSS INTC channel is 63.
Big problem. There is no way to map a DMTIMER event to a PRUSS interrupt that I can tell. I asked the E2E forums and they concurred.
There are a couple of ways around this:
Don’t use DMTIMERs at all. More on this in a second
If you’ve got control of the hardware, turn on the DMTIMER’s output pin, and wire it back to an input that the PRU can see directly through an EGPIO pin. This stinks, though, because GPIO pins to the PRUSS are fairly prescious (you only get 6 inputs and 6 outputs for the entire subsystem, and if your PRUs are doing some communications-related job then you probably need them for other things)
Set up the timer in userspace, and have an ISR (on the MPU side) that signals an event visible to the PRU. This is means introducing latency, and imposing a bit of a real-time requirement on the MPU (the whole point of the PRU is to relieve the MPU of real time requirements, so this is a fairly bad solution)
You could have the timer run without interrupts, and have the PRU poll for the interrupt status bit (or overflow bit or anything else you wanted) in the timer directly, via L3/L4. The PRU machine doesn’t really have ‘interrupts’ per se - waiting for an interrupt is a busywait process from the PRU’s perspective, so busywaiting for the timer overflow flag isn’t that different, EXCEPT that you’re doing so over the interconnect. I don’t know enough about the implications of this to say whether or not that’s a good idea. Intuition tells me “bad” – if you’re making requests over L3/L4 in a busy loop at 200 MHz, I have to think that’ll have a negative impact on anybody else trying to use that bus, possibly even starving the MPU. I really don’t have good insight into how this works, though. If somebody else out there in beagleland understands this better than me please let me know - I would like to learn from you.
Now, fortunately, not all hope is lost here. Someone on the E2E forums gave me another idea, and I tried it (and it works). There’s an eCAP hooked directly to the PRUSS, and it’s capable of generating a periodic timer. What you do is put the eCAP into APWM mode, and set registers TCAP1 and TCAP2 for a fixed period of your choosing. At that point, you can decide to either map that interrupt (which is within the mapping range of the PRUSS INTC) to an event (HOST0 or HOST1, meaning the event is visible as a bit in R31) … OR … you can just poll the eCAP’s interrupt flag to see when it wrapped. The internal eCAP is on the LOCAL PRU bus, so you’re polling it over the internal 32-bit interconnect SCR, but I think that’s OK (at least better than polling an external one over L3/L4). There’s not a lot of detail on how that 32-bit internal interconnect works, so I’m not sure if you could possibly starve the other PRU if you poll eCAP in a tight loop. I guess it’s possible. If that’s the case, then maybe mapping eCAP to a HOST interrupt channel would be more efficient. Again, though – those are a limited resource (there are two channels that map to the event register). You can map multiple interrupts to them, but as soon as you do that, you have to implement some kind of polling strategy to figure out which one was the cause.
So, there are lots of choices, but none of them seem like they are as clean as the approach in the L138 OMAP, where the timers are in an IRQ range that’s mappable to the PRUSS. I am perplexed why the designers chose to do it this way, because having flexible timers feed into the PRUSS seems to me to be critical to the PRUSS’s primary intended use case (custom communications). They say, for example, that PRUSS is a good way to build custom UARTs. Well, that’s not a particularly easy thing to do without a clock
Finally … I’m not an expert at PRU, but for the things I have tried, I’m definitely a fan. People seem to be complaining a lot about “Why didn’t they put in some M3 cores instead!” but there’s a certain beauty in the simplicity of the PRU and its clean little assembly language. Maybe if most people are perplexed by it we can use this new found knowledge to get higher paying jobs.