Using PRU to control PWM peripheral on kernel 4.1 - DTO problem?

I wrote pasm code that has the PRU set up a 10%-duty-cycle 33kHz square wave using PWM on the ehrpwm1B peripheral (which maps to P8_34 in mode 2).

It worked on the 3.18 kernel but doesn’t work on 4.1.12.

When my C code tries to start the PRU code, prussdrv_open returns -1 (bad).

I think the problem is related to the device-tree. I’m using the overlays:

  • am33xx_pwm
  • bone_pwm_P8_34
  • EBB-PRU-Example (from Derek Molloy’s book)

When loading EBB-PRU-Example, I get a “No children” message from the pruss_uio subsystem:

[ 3822.302571] bone_capemgr bone_capemgr: part_number ‘EBB-PRU-Example’, version ‘N/A’
[ 3822.302633] bone_capemgr bone_capemgr: slot #8: override
[ 3822.302671] bone_capemgr bone_capemgr: Using override eeprom data at slot 8
[ 3822.302713] bone_capemgr bone_capemgr: slot #8: ‘Override Board Name,00A0,Override Manuf,EBB-PRU-Example’
[ 3822.308128] gpio-of-helper ocp:gpio_helper: ready
[ 3822.308324] bone_capemgr bone_capemgr: slot #8: dtbo ‘EBB-PRU-Example-00A0.dtbo’ loaded; overlay id #2
[ 3822.402747] pruss_uio 4a300000.pruss: No children

Also, my 4.1.12 kernel didn’t seem to include the bone_pwm_P8_34 overlay (so I found it online). Is bone_pwm_P8_34 the “old way”, and the “new way” involves loading the “universala.dts” overlay and then using the config-pin script?

Here is my code:

https://www.dropbox.com/sh/0mk6g4bd12spp9x/AAArsHYddApMdMWgKb4PbtcHa?dl=0

The ‘build-and-run.sh’ is how I compile and run everything.

I’d really appreciate any help – either with this specific problem, or resources for debugging problems with device-tree overlays. I’m a control-theory guy, not a kernel guy, but I’m happy to learn.

Thanks,
Justin

Sorry, I’m using

uname -a

Linux beaglebone 4.1.12-bone-rt-r16 #3 Mon Jan 9 19:44:59 PST 2017 armv7l GNU/Linux

lsb_release -a

No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 8.7 (jessie)
Release: 8.7
Codename: jessie

Hi Justin!

The initial device tree changed between kernel 3.8 and 4.x. In 4.x the Control Module register pwmss_ctrl (@664h) gets cleared at boot. This register can get used to sychronize several PWM subsystems. A feature that isn’t supported by the kernel nor by any of it’s drivers. So no reason to change the initial state and clear that register. This just disables all PWM subsystems and makes kernel versions incompatible. (You may call it an official kernel virus).

Anyway, in order to get the PWM subsystems working in kernel 4.x you have to make sure that the register doesn’t get cleared at boot. Ie. you can invert the bit-logic. Therefor download the device tree sources, edit file am33xx-clocks.dtsi, find the following nodes and make them look like (= add tag ti,set-bit-to-disable)

`
ehrpwm0_tbclk: ehrpwm0_tbclk@44e10664 {
#clock-cells = <0>;
compatible = “ti,gate-clock”;
clocks = <&l4ls_gclk>;
ti,bit-shift = <0>;
ti,set-bit-to-disable;
reg = <0x0664>;
};

ehrpwm1_tbclk: ehrpwm1_tbclk@44e10664 {
#clock-cells = <0>;
compatible = “ti,gate-clock”;
clocks = <&l4ls_gclk>;
ti,bit-shift = <1>;
ti,set-bit-to-disable;
reg = <0x0664>;
};

ehrpwm2_tbclk: ehrpwm2_tbclk@44e10664 {
#clock-cells = <0>;
compatible = “ti,gate-clock”;
clocks = <&l4ls_gclk>;
ti,bit-shift = <2>;
ti,set-bit-to-disable;
reg = <0x0664>;
};

`

Then compile and install the binary trees. PWM subsystems will work after reboot.

Regards

Hi TJF, thanks for your note. Are you saying that the only way to get PWM in a 4.x kernel is by modifying am33xx-clocks.dtsi? Also, how can I verify whether the register is set correctly?
Thanks,
Justin

Are you saying that the only way to get PWM in a 4.x kernel is by modifying am33xx-clocks.dtsi?

No. The kernel driver should also work (untested). But in your case (controlling the PWM subsystem by PRU code) you have to modify that file. (Or you can develop a loadable kernel module that writes to the register.)

Also, how can I verify whether the register is set correctly?

The PRU cannot write to the Control Module registers, but it can read them. Get a word from 0x44E10664 and pass it to the ARM code. Bits 0:2 must be set to get output from all PWM subsystems. In your case bit 1 set is sufficient to get ehrpwm1B working.

Regards

Thank you TJF, I never would have known about that register if you hadn’t mentioned it. One more question (see below):

Hi Justin!

I just tried to answer you mail, but the google board doesn't work (once
again). I try to answer from my mail client, which I never did before.

    On the 3.18 kernel, I was able to control the PWM with the PRU without needing the PRU to initialize the PWM. The trick was to load the PWM capes "am33xx_pwm" and "bone_pwm_P8_34" before running the PRU code. (And also loading Derek's cape to initialize the pruss.) Loading the capes causes some PWM kernel driver to initialize the PWM registers, I assume. Once that's done, the PRU just has to tweak a couple registers to change the duty cycle. Is this trick what you're referring to when you say "The kernel driver should also work"?

    Would this trick of pre-loading the capes also work in a 4.x kernel?

Sorry, I don't know any of the overlays you mentioned. And, as I said, I
never used any kernel driver for GPIO, QEP, CAP, PWM or ADC. Instead I
use an all-in-one driver (which I named libpruio), that handles all that
stuff easier, more flexible and faster than any kernel driver framework.
In order to get PWM output, there're just a few registers to set (see
file pruio_pwmss.p in the package), so no need for all that multi source
overhead.

BTW: You're developing PRU code and you're using a real time kernel. May
I ask why? Since the PRUSS are very efficient for real time tasks, I see
no reason for using additionally a real time kernel.

Regards

PS:
I just checked Derek's cape. From a first glance I found the line

compatible = "ti,beaglebone", "ti,beaglebone-black";

is wrong. The order is important, most specific first. It must be

compatible = "ti,beaglebone-black", "ti,beaglebone";

And it does more than just enabling the PRUSS (you're loading
unnecessary overhead). In the libpruio package there's the tool
dts_custom, which you can use to easy create, compile and install
minimal custom overlays (single source, no overhead).

Hi Jelena, thanks for your message. See my inline responses:

Hi Justin!

Thanks for the info on the rt needs.

Thank you for mentioning libpruio. I don't know very much about how device
tree overlays interact with linux kernel drivers, so until I can find a
good description of that, I'm inclined to use a more wasteful (but
well-documented) method of configuring the I/O. Do you know of a good way
to learn the details of how a device tree overlay is used by the kernel to
configure the SoC registers? So far I haven't found much good documentation
besides Derek Molloy's book.

As I said, I don't use kernel drivers. They are badly documented, slow,
not very flexible, they always miss the feature I need and they change
from version to version. For my buisiness it doesn't make sense to
follow a moving target. My systems must run reliable in a 24/7 manner.

So instead, I configure the SoC registers directly by PRU code. All I
need/use is the TRM for the SoC. This is much easier and faster than
learning how to use kernel drivers (and update that knowledge all the
time). And my code runs on any kernel version (as long as a new kernel
version doesn't change default settings of the related registers).

Regards

Hi Jelena, thanks for your message. See my inline responses:

Hi Justin

Are you using a disciplined clock on the BBB?

Regards,
John