[BPI-R64] PWM-support

I’ve found only polarity examples with additional devices (like fan,backlight,leds). Maybe it is not possible to define inversion directly on pwm controller.

I asked pwm maintainers about dts and set_polarity but i got response that pwm driver needs to be changed first to using apply_state() instead of disable(), enable(), set_polarity() and config().

added pwm-fan-driver to check if i get further with inverted-state in client-driver…here i get a bit further

drivers/pwm/core.c                             
struct pwm_device *
of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args)
{
        struct pwm_device *pwm;

printk(KERN_ALERT "DEBUG: Passed %s %d \n",__FUNCTION__,__LINE__);
        /* check, whether the driver supports a third cell for flags */
        if (pc->of_pwm_n_cells < 3)
                return ERR_PTR(-EINVAL);
printk(KERN_ALERT "DEBUG: Passed %s %d \n",__FUNCTION__,__LINE__);
        /* flags in the third cell are optional */
        if (args->args_count < 2)
                return ERR_PTR(-EINVAL);

first printk is printed, second not, that means that pwm-cells is less than 3…but my pwm-node has setting pwm-cells to 3…

#pwm-cells=<3>;

looks like the of_pwm_n_cells is not read out of devicetree and needs to be set by driver (done in different drivers…)…it seems to be used only for dtc

my current code return to this:

root@bpi-r64:~# dmesg | grep -i debug                                           
[    0.000000] Kernel command line: board=bpi-r64 console=ttyS0,115200n1 earlyp2
[    0.075685] DEBUG: Passed pwm_mediatek_probe 266                             
[    1.196472] DEBUG: Passed devm_of_pwm_get 1190                               
[    1.201008] DEBUG: Passed devm_of_pwm_get 1194                               
[    1.205541] DEBUG: Passed of_pwm_get 816                                     
[    1.209553] DEBUG: Passed of_pwm_get 822                                     
[    1.213567] DEBUG: Passed of_pwm_get 829                                     
[    1.217583] DEBUG: Passed of_pwm_get 838                                     
[    1.221595] DEBUG: Passed of_pwm_xlate_with_flags 139 3                      
[    1.226822] DEBUG: Passed of_pwm_xlate_with_flags 143                        
[    1.231960] DEBUG: Passed of_pwm_xlate_with_flags 147                        
[    1.237099] DEBUG: Passed of_pwm_xlate_with_flags 150                        
[    1.242239] DEBUG: Passed of_pwm_xlate_with_flags 154                        
[    1.247380] DEBUG: Passed of_pwm_xlate_with_flags 158 pwm_no: 2,argc:3       
[    1.253995] DEBUG: Passed of_pwm_get 842                                     
[    1.258013] DEBUG: Passed devm_of_pwm_get 1202                               
[    1.262573] DEBUG: Passed pwm_mediatek_apply 243 PWM#2, inv:1==1?            
[    1.268796] DEBUG: Passed pwm_mediatek_apply 243 PWM#2, inv:1==1?

that means revertive-mode is set in driver (inv:1==1), not yet in hardware because set_polarity is not called. i guess it’s because pwm_mediatek_apply is new framework and disables old functions (enable/disable/config and set_polarity)

i guessed right…after removing callback to .apply .set_polarity gets called

[    1.250669] DEBUG: Passed of_pwm_xlate_with_flags 158 pwm_no: 2,argc:3       
[    1.257285] DEBUG: Passed of_pwm_get 842                                     
[    1.261302] DEBUG: Passed devm_of_pwm_get 1202                               
[    1.265862] DEBUG: Passed pwm_mediatek_set_polarity 222 PWM#2, inv:1

@Mbk_Kamble can you check with 5.7-pwm2 tree if duty-cycle is now right?

checked if the right bits are cleared/set:

[    1.201763] DEBUG: Passed pwm_mediatek_set_3dclm 205 value:0x400, clear:1    //clear bit10 (begin at 0) for disabling pwm3-base-mode
[    1.208554] DEBUG: Passed pwm_mediatek_set_3dclm 205 value:0x40000, clear:0  //set bit 18 (pwm_3dclm_3) for aux-mode
[    1.215517] DEBUG: Passed pwm_mediatek_set_3dclm 205 value:0x4, clear:0 //set bit 2 for invertive-mode => is wrong because bit 0 is 3dclm_enable (needs to be set to) and right bit here is 3 (+1)

i guess 3dclm is not enabled by default, maybe it needs to be set for the other pwm too (base-mode without inversion)

after modification i got 0x40009 (0000 0000 0000 0100 0000 0000 0000 1001) written to the register which should be right value

[    1.195725] DEBUG: Passed pwm_mediatek_set_polarity 223 PWM#2, inv:1
[    1.202085] DEBUG: Passed pwm_mediatek_set_3dclm 206 read val: 0x0 value:0x1, clear:0
[    1.209919] DEBUG: Passed pwm_mediatek_set_3dclm 212 write val: 0x1 value:0x1, clear:0
[    1.217838] DEBUG: Passed pwm_mediatek_set_3dclm 206 read val: 0x1 value:0x400, clear:1
[    1.225843] DEBUG: Passed pwm_mediatek_set_3dclm 212 write val: 0x1 value:0x400, clear:1
[    1.233935] DEBUG: Passed pwm_mediatek_set_3dclm 206 read val: 0x1 value:0x40000, clear:0
[    1.242114] DEBUG: Passed pwm_mediatek_set_3dclm 212 write val: 0x40001 value:0x40000, clear:0
[    1.250728] DEBUG: Passed pwm_mediatek_set_3dclm 206 read val: 0x40001 value:0x8, clear:0
[    1.258907] DEBUG: Passed pwm_mediatek_set_3dclm 212 write val: 0x40009 value:0x8, clear:0
  • set 0x1 (bit 0) => enable 3dclm
  • clear 0x400 (bit 10) => disable base mode for pwm2
  • set 0x40000 (bit 18) => set aux-bit for pwm2
  • set 0x8 (bit 3) => set invertive mode for pwm2

this should be right now for pwm2…but i guess i need to set the other pwm too to non-revertive.

@sam33 can you confirm this? maybe i can optimize the code a bit (so not always set/clear bits already set/cleared). i wrote initial value of 1f00 in probe to 3dclm-register and got 0x41b09 as final value which seems to be right (pwm1,2,4,5 in base mode,pwm3 in aux-mode and inverted + 3dclm enabled).

last state: https://github.com/frank-w/BPI-R2-4.14/tree/5.7-pwm2

atm i get ressource busy when i try to export pwm2

root@bpi-r64:~# ./pwm.sh                                                        
+ num=2                                                                         
+ cd /sys/class/pwm/pwmchip0                                                    
+ echo 2                                                                        
./pwm.sh: line 4: echo: write error: Device or resource busy                    
+ [[ -d pwm2 ]]                                           

using 5.7-pwm2 tree from my repo, i guess it’s because pwm2 is mapped by pwm-fan driver

Can i control the pwm manually through pwm-fan driver? I saw some examples eith thermal zones,but i did not really understand them

any idea? @Ryder.Lee @sam33

@niebieski20 @frank-w
If we assume that the clock speed has been set by others pwm registers like CLKDIV and CLKSEL …
and then In the old PWM mode, we only use ‘width’ register, “thresh” register to control PWM waveform

For example: We can fix ‘width’ register to a const vaule (e.g: 0xf06) first

  1. Set ‘thersh’ register to zero.
    The pwm output will be: “__________” (repeat…)

  2. Set ‘thersh’ register to half of the ‘width’ register
    The pwm output will be: “¯¯¯¯¯|_____” (repeat…)

  3. Set ‘thersh’ register to one third of the ‘width’ register
    The pwm output will be: “¯¯¯|_______” (repeat…)

Now we change the ‘width’ register to 0x1e0c,
and keep above relationship between ‘width’ register and ‘thersh’ register. The output wave of above 3 scenarios will be:

  1. ‘____________________’ … (repeat…) --> pwm duty 0% --> fan full speed?
  2. ‘¯¯¯¯¯¯¯¯¯¯|__________’ … (repeat…) --> pwm duty 50% --> fan 50% speed?
  3. ‘¯¯¯¯¯¯|______________’ … (repeat…) --> pwm duty 33% --> fan 67% speed?

and the hardware has no “polarity” setting in the pwm old mode,
so we can’t generate a waveform like ‘______|¯¯¯¯¯¯¯¯¯¯¯¯¯¯’ … (repeat…)
in above scenarios (3) with PWM_POLARITY_INVERTED (pwm duty 33% & fan 33% speed).

But maybe we can use software to calculate inverted register value by “thersh = width - thersh”
to convert pwm waveform of (3) to ‘¯¯¯¯¯¯¯¯¯¯¯¯|______’ … (repeat…) to make the fan work correctly?

Hi sam.

With the first examples you target the wrong waveform?

Is 3dlcm new mode? You mean userspace calculation (inverted_duty = period - duty_cycle)?

@sam33 can you confirm pwm7 is there on mt7622/bpi-r64? Moore says it’s a typo,but then there are many typo in dev pdf

just to refresh the thread…i have now a basic oscilloscope. I have only applied the dts-patches (5.9-rc)

root@bpi-r64:/sys/class/pwm/pwmchip0# echo 0 > export
root@bpi-r64:/sys/class/pwm/pwmchip0# cd pwm0/
root@bpi-r64:/sys/class/pwm/pwmchip0/pwm0# echo 10000 > period
root@bpi-r64:/sys/class/pwm/pwmchip0/pwm0# echo 5000 > duty_cycle
root@bpi-r64:/sys/class/pwm/pwmchip0/pwm0# echo 1 > enable

and my oscilloscope reports a 100khz square-wave (10us period), 50% duty-cycle and ~4V. on pwm2 (fan) i see only a level change on changing the enabled…no square-wave/pwm…0V on enable=1 and ~5V on enable=0

on pwm6 (7) i see ~120khz but only ~60mV! independend of enable-state…seems like affected by pwm0 (if i disable pwm0, i see the square-wave goes away)

any idea?

pwm channel 7 (linux: /sys/class/pwm/pwmchip0/pwm6) not exist in mt7622, It only have total 6 channels.

1 Like

Thanks then documentation and pinctrl driver needs to be cleaned up. I can send patch for adding pwm0-5 to dts and remove pwm6

https://github.com/frank-w/BPI-R2-4.14/commits/5.9-rc

@sam33 any idea why pwm3 (2) does not work (also not inverted)? Have not added inversion code…pwm driver is unmodified in 5.9-rc,only dts changes.

Have you test with real fan? I have connected one real fan to the FAN port of R64, but not working, can’t control the fan speed with pwm 0~5 (except pwm2). pwm2 control fan responds, but the behavior is incorrect. I have modified mt7622-bananapi-bpi-r64.dts as yours.

pwm_pins: pwm-pins {
                mux {
                        function = "pwm";
                        groups = "pwm_ch1_0", /* mt7622_pwm_ch1_0_pins[] = { 51, }; */
                                 "pwm_ch2_0", /* mt7622_pwm_ch2_0_pins[] = { 52, }; */
                                 "pwm_ch3_2", /* mt7622_pwm_ch3_2_pins[] = { 97, }; */
                                 "pwm_ch4_1", /* mt7622_pwm_ch4_1_pins[] = { 67, }; */
                                 "pwm_ch5_0", /* mt7622_pwm_ch5_0_pins[] = { 68, }; */
                                 "pwm_ch6_0", /* mt7622_pwm_ch6_0_pins[] = { 69, }; */
                                 "pwm_ch7_2"; /* mt7622_pwm_ch7_2_pins[] = { 101, }; */
                };
        };

&pwm {
        pinctrl-names = "default";
        /* pinctrl-0 = <&pwm7_pins>; */
        pinctrl-0 = <&pwm_pins>;
        status = "okay";
};

Any other patches needed?

Pwm2 (3) does not work as expected. I can only change voltage on (0) and off (1) as it is inverted. No squarewave, and no change if i modify period/duty cycle. Only the other pwm working.

I dropped pwm7 in 5.9-rc and related changes as mt7622 is confirmed to have only 6 channels

i rebased my patches on top of 5.9-rc branch @sam33 can you take a look on it?

https://github.com/frank-w/BPI-R2-4.14/commits/5.9-rc

Except unusable pwm chanel 7(6 in kernel pwm node),

there are 5 pwm pins on 40 pins header.

PIN11 (GPIO51/PWM_CH1) (0 in kernel pwm node) pin shared with group “uart2_1_tx_rx”
PIN13 (GPIO52/PWM_CH2) (1 in kernel pwm node) pin shared with group “uart2_1_tx_rx”
PIN23 (GPIO67/PWM_CH4) (3 in kernel pwm node) pin shared with group “spic1_0”
PIN19 (GPIO68/PWM_CH5) (4 in kernel pwm node) pin shared with group “spic1_0”
PIN21 (GPIO69/PWM_CH6) (5 in kernel pwm node) pin shared with group “spic1_0”

Test command:

    # PIN11
    root@OpenWrt:/sys/devices/platform/11006000.pwm/pwm/pwmchip0# echo 0 > export
    # PIN13
    root@OpenWrt:/sys/devices/platform/11006000.pwm/pwm/pwmchip0# echo 1 > export
    # PWM_CH3 (2 in kernel pwm node) not present on 40pins headers
    root@OpenWrt:/sys/devices/platform/11006000.pwm/pwm/pwmchip0# echo 2 > export
    # PIN23
     root@OpenWrt:/sys/devices/platform/11006000.pwm/pwm/pwmchip0# echo 3 > export
    # PIN19
    root@OpenWrt:/sys/devices/platform/11006000.pwm/pwm/pwmchip0# echo 4 > export
    # PIN21
    root@OpenWrt:/sys/devices/platform/11006000.pwm/pwm/pwmchip0# echo 5 > export
    root@OpenWrt:/sys/devices/platform/11006000.pwm/pwm/pwmchip0# ls
    device     npwm       pwm0       pwm2       pwm4       subsystem  unexport
    export     power      pwm1       pwm3       pwm5       uevent

    root@OpenWrt:/sys/devices/platform/11006000.pwm/pwm/pwmchip0# for i in pwm*; do echo 10000 > $i/period; done
    root@OpenWrt:/sys/devices/platform/11006000.pwm/pwm/pwmchip0# for i in pwm*; do echo 5000 > $i/duty_cycle; done
    root@OpenWrt:/sys/devices/platform/11006000.pwm/pwm/pwmchip0# for i in pwm*; do echo 1 > $i/enable; done

I have test above pwm pins and measure the waveform on openwrt (5.4 kernel) environment,
After apply the following change (based on the dts in native kernel: https://elixir.bootlin.com/linux/v5.4.69/source/arch/arm64/boot/dts/mediatek/mt7622-bananapi-bpi-r64.dts), all of PWM work as I expected,
(The scope connect to PIN11/13/23/19/21 showing a period about 10us waveform with 5us high 5us low)

I didn’t measure PWM3 (GPIO97/PWM_CH3) (2 in kernel pwm node) , because It seem that it not exist on the 40pins header

I will try to test this on kernel 5.9 environment again.

    //&uart2 {
    //      pinctrl-names = "default";
    //      pinctrl-0 = <&uart2_pins>;
    //      status = "okay";
    //};
    
    
    // &spi1 {
    //      pinctrl-names = "default";
    //      pinctrl-0 = <&spic1_pins>;
    //      status = "okay";
    // };

    pwm_pins: pwm-pins {
                    mux {
                            function = "pwm";
                            groups = "pwm_ch1_0", /* mt7622_pwm_ch1_0_pins[] = { 51, }; */
                                     "pwm_ch2_0", /* mt7622_pwm_ch2_0_pins[] = { 52, }; */
                                     "pwm_ch3_2", /* mt7622_pwm_ch3_2_pins[] = { 97, }; */
                                     "pwm_ch4_1", /* mt7622_pwm_ch4_1_pins[] = { 67, }; */
                                     "pwm_ch5_0", /* mt7622_pwm_ch5_0_pins[] = { 68, }; */
                                     "pwm_ch6_0"; /* mt7622_pwm_ch6_0_pins[] = { 69, }; */

                    };
            };
    
    &pwm {
            pinctrl-names = "default";
            pinctrl-0 = <&pwm_pins>;
            status = "okay";
    };

Hi Sam, except pwm3 (and 7) i have all working…it is the only one which does not work…only level 0/1 inverted, so it responds. right, it is not connected to 40pin-header, but connected to fan-socket (see documentation https://drive.google.com/file/d/1QzKmIwgSNbCIXQbqLsTUELJCEPik3VGr/view page 12 bottom center FAN_OUT and Page 2 connected to pwm_ch3)

I don’t know much about circuit diagrams…
PWM2(3) connect to FAN_OUT, but can we directly measure PWM signal on it ?
Or we can only measure signal via 5VD_FAN pin on BPIR64 ?
How to confirm whether this is a PWM2(3) problem or a bpir64 board problem?

which point do you measure with scope in below circuit diagrams ?

hi,

i measured directly on fan-socket (CN19) and found no other connecting point (and the circuit itself) on board on a quick look as i do not see any lanes going from the fan-socket.

as i did not know what exactly is Q49 (i guess a field effect transistor, but i have no knowledge about these and there seems to be some kind of diode between 2+3) i do not fully understand the circuit. it looks for me that Q33 inverts signal of fan_out (low=>high,high=>low) as high on 1 should pass current through 3>2 and then 3 is connected to GND (on low, 5V comes over R516 as Q33 blocks current). basicly pin 1 of socket should have always 5V and pin 2 is changed…a “normal” transistor will set pin2 (CN19) to unconnected state, but maybe field-effect transistors make a 0/1 level on Q40.3. i guess high on Q40.1 will pass GND to CN19.2.

Hi flank,

I use a LED (with a resistor) connect to fan socket, and use pwm command to test it.

I measure the waveform on the fan socket pin, the output waveform is not a PWM waveform, but the waveform is different when the PWM duty_cycle changed,
I can change the LED brightness by different duty_cycle value

Maybe this mean PWM3 is working ?

cd /sys/devices/platform/11006000.pwm/pwm/pwmchip0
echo 2 > export
cd pwm2
echo 100000 > period
echo 0 > duty_cycle
echo 1 > enable

# LED brightness is HIGH
echo 20000 > duty_cycle                   
# LED brightness is LOW
echo 80000 > duty_cycle

LED brightness is HIGH

LED brightness is LOW

Thanks for test, i try with your values (but they look similar to mine),but basicly i did same as you…with led,fan and oscilloskope. And i got only a level-change,no response to changing duty_cycle (led same brightness, fan same speed). Maybe my board (depending on circuit i guess gnd pin of fan socket) is broken.

Do you want to send patch for adding pwm or should i send it? It looks you used my pwm_pins block (based on comments after each pwm num)

Hi flank,
Due to the pin shared of pwm, “uart2_1_tx_rx” and “spic1_0”,
(this will make uart2 / spic1 not working),
we don’t want to enable all pwms on our reference board dts.

But I think you can send a patch for bpir64 that make pwm3 FAN working.

my patches were only bpi-r64, not rfb

i get warning for PWM, that pins already blocked, so that the pwms are not working, so i disabled the previous function (also in bpi-r64 dts):

have created a new branch for pwm-upstreaming (top 3 commits) as rc had changed…@sam33 any thoughts about cleaning pinctl-header?

https://github.com/frank-w/BPI-R2-4.14/commits/5.9-pwm

I posted the patches as rfc to mailinglist…lets see which response i get

Cannot control or set pwm?

root> cd /sys/class/pwm/pwmchip0
root> echo 0 > export
root> ls
device  export  npwm  power subsystem  uevent  unexport

no pwm0 or pwm2 file or dir.

Which device? This thread is about bpi-r64. In other thread you talk about r3…this is important to know as each board exposes different pwm to gpio header.

dmesg may show if something goes wrong (e.g. pinctrl blocked by other device)? You can only try to enable the pwms on the gpio header…pwm for fan is maybe assigned to pwm-fan (like on r3).