PWMSS control by PRU with kernel 4.19

Hello everyone,

I’m using the latest official image for Beaglebone Black (kernel 4.19-ti) and I would like to control PWMSS from PRU.

I’ve found two earlier topics about PRU and PWMSS control:

As far as I understand, the PWMSS is initially disabled by Linux kernel and PRU cannot enable it, so I have either to issue a command to enable PWMSS through sysfs interface, or create and load a device tree overlay.

I would like to try the second approach, which refers to patching files in /opt/source/dtb-4.9-ti/src/arm directory, but I have no such path (with amendments regarding the kernel version). I have only u-boot_v2019.04 and u-boot_v2019.07-rc4 subdirectories inside /opt/source/.

What is recommended for controlling PWMSS from PRU? Or should I ditch it and control PWMSS from Linux instead?

All pwm’s are enabled in v4.19.x-ti… You can enable/control them thru sysfs… Are you looking to adjust them in realtime, or just set a value and let it run?

Regards,

I’d like to adjust them in real-time. That’s why I’m evaluating different options - which is better: to control them from PRU or from Linux?

For anything Real Time, the PRU works best… which kernel version (uname -r) then we can point you to the kernel source…

Regards,

The kernel version is 4.19.94-ti-r42, if I remember correctly (I can’t access my BBB right now to get the exact version, but it’s certainly 4.19-ti branch).

It’s the kernel in the latest official image for BBB, I didn’t update it yet.

Every release is tagged:

Regards,

Thank you. What should I do to enable the PWMSS clocks by default, so that PRU could control the peripheral? Should I create a device tree overlay? How do I do that?

These should already be enabled out of the box… (if your kernel is new enough, i don’t remember the change over time-frame…)

Regards,

That’s wrong. You don’t need any kernel magic (perhaps out-dated in next version).

PRUSS can enable/disable the PWMSS clocks. In order to check the state and if the system is working, just read the ID register: null signals that it’s disabled, otherwise the default ID gets read.

SYS_PWMSS_IDVER register reads all zeroes unless I enable one PWM output channel via sysfs.
Should I try updating the kernel?

That’s different then the old “bug”, unless you enable it, Power Management has it disabled…

You’re on PRU, you don’t need the kernel for that.

Just enable the clock in the CM module by setting the second bit (writing the value 2) to register

  • PWMSS-0: 0x44E000D4
  • PWMSS-1: 0x44E000CC
  • PWMSS-2: 0x44E000D8

Note: it takes some time for the subsystem to start (it’s ready when ID != 0). Clearing that bit (writing 0 = zero) disables the clock (the subsystem).

1 Like

Thank you, it did the trick! Now I can access PWMSS0 registers.

@RobertCNelson @DTJF one more question, if I may. If I understand correctly, PWMSS has one interface and functional clock, that is, CORE_CLKOUTM4 / 2 as stated in 15.1.2.3 chapter of AM335x Technical Reference Manual (although it’s stated

“The PWMSS controllers have separate bus interface and functional clocks”

, but I see no other clocks for them).

Can I rely on it being always 100 MHz? Is it stated somewhere in the TRM?

AFAIK the clock is hard wired to 100 MHz signal.

But you can internally skip pulses (slow down clock) by setting the CLKDIV bits in the TBCTL register.

1 Like

@RobertCNelson @DTJF If I understand correctly, CM_PER_EPWMSS0_CLKCTRL register enables only bus clock for PWMSS, so that I can access its registers. The functional clock is still disabled, so the peripheral still doesn’t work. It is enabled by writing into CONTROL_PWMSS_CTRL, but it is writable only in privileged mode of operation.

How can I achieve writing into CONTROL_PWMSS_CTRL register? Seems it’s sufficient to do it once, perhaps at boot? Or at any later moment. I think this is the only blocker for controlling PWMSS from PRU.

Okay, solved it with https://groups.google.com/g/beagleboard/c/eVgyVduT288/m/V4B7yUhACwAJ

Namely:

  1. git clone GitHub - mvduin/overlay-utils: Making BBB overlays easier
  2. in it, create “pwm-hackery.dtsi” containing the relevant declarations
  3. run “make pwm-hackery.dtbo” and place the resulting file in /lib/firmware
  4. edit /boot/uEnv.txt to load the overlay.

The contents of “pwm-hackery.dtsi” file:

&ehrpwm0_tbclk {  ti,set-bit-to-disable;  };
&ehrpwm1_tbclk {  ti,set-bit-to-disable;  };
&ehrpwm2_tbclk {  ti,set-bit-to-disable;  };

Remarks for future readers: this causes to invert the driver control of PWMSS module clock, so if you’ll want to control them via sysfs (or /dev/pwm/ subfolder) you’ll need to write 0 to enable file to enable it.

Yes, sorry, I forgot that register. In the early kernel versions (3.8) all subsystems were enabled after boot. Later in kernel 4.? it changed and all subsystems are disabled now.

In libpruio I enable the sybsystems when custom firmware starts PWM, so that the PWM signals can get synchronized (all start at the same time). See function PwmMod::Sync() for details.

This gets done by a LKM (loadable kernel module). You can install it by

sudo apt install libpruio-modules-`uname -r`

Once the module is loaded, write (mind privileges)

0xFFmm

to its sysfs file at

/sys/devices/platform/libpruio/state

where mm is the two byte hex number for the desired register value (ie 07 to enable all three subsystems, or 00 to disable them).

BTW:
This LKM also supports any kind of pinmuxing.

You should either control a peripheral from PRU or use the kernel driver, not both at the same time. To reserve an eHRPWM peripheral to use by PRU, disable the normal kernel driver (e.g. by putting status = "disabled"; on the peripheral) while forcing the kernel to keep the subsystem clocked by putting ti,no-idle; on the subsystem. You can also use these example overlays in my overlay-utils project (instead of your pwm-hackery.dtbo):

make uio/ehrpwm0-P9_22-P9_21.dtbo
make uio/ehrpwm1-P9_14-P9_16.dtbo
make uio/ehrpwm2-P8_19-P8_13.dtbo

These overlays are technically intended to configure the peripherals for UIO (direct peripheral register access from linux userspace), but they work equally well to configure them for use by PRU. See uio/README for an explanation of the various overlays in that directory. There are also variants that don’t configure any pins (e.g. uio/ehrpwm1.dtbo) if you prefer to use config-pin for that instead.

(Using these overlays also creates the possibility of using UIO to initialize the eHRPWM peripheral from userspace before PRU takes over (thus simplifying the pru code), or to inspect the peripheral registers while the PRU code is running to verify that PRU is setting them correctly. My py-uio project actually has classes for pwmss and ehrpwm including initialization code, but there’s currently no example or documentation for this. If you’re interested in this let me know and I’ll see if I can make a quick demo or something.)

Thanks. I agree there should be only one controller of the peripheral, either PRU or kernel driver, but not both. Disabling the kernel driver ensures only PRU can control the peripheral.

So the contents of the device tree include file should be like this?

&ehrpwm0_tbclk {  ti,set-bit-to-disable;  };
&ehrpwm1_tbclk {  ti,set-bit-to-disable;  };
&ehrpwm2_tbclk {  ti,set-bit-to-disable;  };
&ehrpwm0 {status = "disabled";};
&ehrpwm1 {status = "disabled";};
&ehrpwm2 {status = "disabled";};
&epwmss0 {ti,no-idle;};
&epwmss1 {ti,no-idle;};
&epwmss2 {ti,no-idle;};

That should work yes.

(If you want the overlay to also work if cape-universal is not enabled you’d also need status = "enabled"; on the pwmss nodes since these are otherwise not enabled by default, but then again if you’re not using the overlay for setting pinmux (which you can’t attach to the disabled ehrpwm nodes btw but you could attach to the pwmss nodes) you need cape-universal anyway to be able to use config-pin.)