wkeeling
Posts: 211
Joined: Fri Aug 25, 2017 2:16 pm
Location: Houston Texas

PWM driver questions

Tue Feb 27, 2024 12:15 am

I have a out of tree PWM module that uses Linux PWM Interface which I think is provided by pwm_bcm2835 module (in my pi3 case). I am loading pwm_bcm2835 with config.txt entry of dtoverlay=pwm,pin=12,func=4. All works fine using “modprobe mydriver” to load after boot but if /etc/modules includes mydriver it load before the needed pwm_bcm2835.

What is the correct way to set up a dependency in mydriver to the pwm_bcm2835?

What is the correct way to load these 2 module in the correct order at boot time?

How do I make that depend Acy and load order Pi hardware independent?
Willie Keeling

cleverca22
Posts: 8828
Joined: Sat Aug 18, 2012 2:33 pm

Re: PWM driver questions

Tue Feb 27, 2024 1:02 am

i think what you want to do, is create a custom compatible in device-tree and mark that as depending on the pwm node

look at how the pi5 fan controller depends on the pwm controller for examples on both the c and dts sides

PhilE
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 5946
Joined: Mon Sep 29, 2014 1:07 pm
Location: Cambridge

Re: PWM driver questions

Tue Feb 27, 2024 8:26 am

Although there have been a few changes in this area in recent years, in the general case the kernel does not use the Device Tree to determine probe order for devices. Instead, drivers are supposed to handle the absence of something they depend on - a clock, PWM, etc. - by returning -EPROBEDEFER. The kernel will keep track of deferring devices, repeatedly calling their probe functions after other devices have been created in the hope that their dependencies have now been met.

wkeeling
Posts: 211
Joined: Fri Aug 25, 2017 2:16 pm
Location: Houston Texas

Re: PWM driver questions

Tue Feb 27, 2024 3:06 pm

PhilE wrote:
Tue Feb 27, 2024 8:26 am
Although there have been a few changes in this area in recent years, in the general case the kernel does not use the Device Tree to determine probe order for devices. Instead, drivers are supposed to handle the absence of something they depend on - a clock, PWM, etc. - by returning -EPROBEDEFER. The kernel will keep track of deferring devices, repeatedly calling their probe functions after other devices have been created in the hope that their dependencies have now been met.
Assuming what I should be doing in my driver init function is to check the if the PWM module (PWM_2835) is loaded and ready if not undo my driver init and return -EPROBEDEFER?

If I understand correctly how should that check be done? And how can it made to be hardware independent (PWM_2835 vs. whatever provides this in BCM2711/RP1 land)?
Willie Keeling

PhilE
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 5946
Joined: Mon Sep 29, 2014 1:07 pm
Location: Cambridge

Re: PWM driver questions

Tue Feb 27, 2024 3:14 pm

You don't need to do anything special. Presumably at some point the absence of the dependency will lead to an error during probing - when getting a PWM, clock etc. (if not, it isn't really a dependency). When that happens, return -EPROBE_DEFER, in the expectation that your probe function will be called again.

6by9
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 16123
Joined: Wed Dec 04, 2013 11:27 am
Location: ZZ9 Plural Z Alpha, aka just outside Cambridge.

Re: PWM driver questions

Tue Feb 27, 2024 4:50 pm

PhilE wrote:
Tue Feb 27, 2024 3:14 pm
You don't need to do anything special. Presumably at some point the absence of the dependency will lead to an error during probing - when getting a PWM, clock etc. (if not, it isn't really a dependency). When that happens, return -EPROBE_DEFER, in the expectation that your probe function will be called again.
Generally you'll get that error back from the call trying to claim the resources, hence why you'll see probe sequences such as in https://github.com/raspberrypi/linux/bl ... #L512-L517

Code: Select all

	pb->pwm = devm_pwm_get(&pdev->dev, NULL);
	if (IS_ERR(pb->pwm)) {
		ret = dev_err_probe(&pdev->dev, PTR_ERR(pb->pwm),
				    "unable to request PWM\n");
		goto err_alloc;
	}
devm_pwm_get will return -EPROBE_DEFER if the PWM driver isn't loaded yet. Your driver returns that, and the driver will get probed again later on.
Software Engineer at Raspberry Pi Ltd. Views expressed are still personal views.
I'm not interested in doing contracts for bespoke functionality - please don't ask.

wkeeling
Posts: 211
Joined: Fri Aug 25, 2017 2:16 pm
Location: Houston Texas

Re: PWM driver questions

Thu Feb 29, 2024 5:29 pm

PhilE, you are correct, I was just checking pwm_request() for returning a null pointer. After changing it to use IS_ERR_OR_NULL() macro and returning -EPROBE_DEFER on error the module does not load until getting a good return from pwm_request(). This solves the bad address error.
The bad news is the driver does not work when loaded in /etc/modules or loading it from rc.local with modprobe. With either of these ways the driver does not function until removed and reloaded (rmmod and modprobe) then it works fine (also when loaded manually without being first loaded automatically) .

Also the pwm_bcm285 when loaded with dtoverlay=pwm,pin=12,func=4 takes the pin high. Is there a what to force it low or duty cycle of 0?

Any ideas what I am doing wrong?

6by9 that example bring up another question – the example is creating a backlight device, backlight_device_register(), I am using character device. Might there be a better fit to what I am trying to do than a character device? Where do I find any info on these linux devices and/or platform devices?

All this device needs to do is take a command from userspace of time and duty cycle, schedule the PWM to run for that time and duty cycle and return status of if the command was accepted or not.

Thanks in advance for any ideas anyone has.

Code: Select all

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/pwm.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/err.h>


/* Meta Information */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("William Keeling");
MODULE_DESCRIPTION("PWM driver for fish feeder");

/* Variables for device and device class */
static dev_t pwm_device_num;
static struct class *pwm_class;
static struct cdev char_device;
struct pwm_device *pwm0 = NULL;

#define DRIVER_NAME "pwm_driver"
#define DRIVER_CLASS "PWMClass"
// should be 100 hz
#define PWM_TIME 10000000
#define STARTTIME 500

struct buff {
unsigned char time;
unsigned char dc;
unsigned char check;
};

struct queue_work {
int msecs;
int on_time;
struct work_struct pwm_work;
} work_data;
 
// write from user space 
static ssize_t driver_write(struct file *File, const char *user_buffer, size_t count, loff_t *offs) 
{
	static int in_use=0;
	int status=0;
	struct buff in;
	
	if (in_use) return in_use;
	
	in_use=-1;
	if (count != sizeof(in)) {
		printk("PWM driver: Invalid request length\n");
		status=-2; 
		goto skip_write;
	}
	if (copy_from_user(&in, user_buffer, 3) != 0) {
		printk("PWM driver: Copy from user space failed\n");
		status=-2; 
		goto skip_write;
	}
	if ((unsigned char)~(in.time+in.dc+in.check)) {
		printk("PWM driver: Invalid checksum\n");
		status=-2; 
		goto skip_write;
	}
	if ((in.time < 1) || (in.time > 60)) {
		printk("PWM driver: Invalid time\n");
		status=-2; 
		goto skip_write;
	}
	if ((in.dc <= 0) || (in.dc > 100)) {
		printk("PWM driver: Invalid duty cycle\n");
		status=-2; 
		goto skip_write;
	}
		
	work_data.on_time = (in.dc) ? (PWM_TIME*in.dc/100) : 0;
	work_data.msecs = in.time*1000;
	
	if (!(work_busy(&work_data.pwm_work))) {
		schedule_work(&work_data.pwm_work);
		in_use = 0;
		return count;
	} else {
		status =-1;
	}
	
skip_write:
	in_use=0;
	return status;
}
// set device file permission
static char *pwm_devnode(struct device *dev, umode_t *mode)
{
	if (mode) *mode = 0666;
	return NULL;
}
// file operations structure
static struct file_operations fops = {
	.owner = THIS_MODULE,
	.write = driver_write
};
// scheduled work to run feeder without waiting for write to driver
static void run_feeder(struct work_struct *work)
{
	struct queue_work *data = container_of(work,struct queue_work, pwm_work);
	int msecs = data->msecs-STARTTIME;
  	if (pwm_config(pwm0, PWM_TIME, PWM_TIME)) printk("PWM driver: config failed\n");
	msleep(STARTTIME);
	if (pwm_config(pwm0, data->on_time, PWM_TIME)) printk("PWM driver: config failed\n");
	msleep(msecs);
	if (pwm_config(pwm0, 0, PWM_TIME)) printk("PWM driver: config failed\n");
	return;
}

// install module fuction
static int __init ModuleInit(void) {
	printk("PWM driver: Installing\n");
	
	INIT_WORK(&work_data.pwm_work, run_feeder);

	if( alloc_chrdev_region(&pwm_device_num, 0, 1, DRIVER_NAME) < 0) {
		printk("PWM driver: Device number not allocated!\n");
		return -1;
	}
	printk("PWM driver: Device Major: %d, Minor: %d\n", 
		pwm_device_num >> 20, pwm_device_num && 0xfffff);

	if((pwm_class = class_create(THIS_MODULE, DRIVER_CLASS)) == NULL) {
		printk("PWM driver: Class not created!\n");
		goto ClassError;
	}
	pwm_class->devnode = pwm_devnode;

	if (device_create(pwm_class, NULL, pwm_device_num, NULL, DRIVER_NAME) == NULL) {
		printk("PWM driver: Can not create device file!\n");
		goto FileError;
	}

	cdev_init(&char_device, &fops);

	if(cdev_add(&char_device, pwm_device_num, 1) == -1) {
		printk("PWM driver: Registering device to kernel failed!\n");
		goto AddError;
	}

	pwm0 = pwm_request(0, "my-pwm");
	if (IS_ERR_OR_NULL(pwm0)) {
		printk("PWM driver: Could not get PWM0! %ld\n", PTR_ERR(pwm0));
		goto AddError;
	}

	if (pwm_config(pwm0, 0, PWM_TIME)) printk("PWM driver: config failed\n");
	if (pwm_enable(pwm0)) printk("PWM driver: endable failed\n");

	printk("PWM driver: Installed\n");
	return 0;
AddError:
	device_destroy(pwm_class, pwm_device_num);
FileError:
	class_destroy(pwm_class);
ClassError:
	unregister_chrdev_region(pwm_device_num, 1);
	if (pwm0) return -EPROBE_DEFER;
	return -1;
}
// remove module fuction
static void __exit ModuleExit(void) {
	pwm_disable(pwm0);
	pwm_free(pwm0);
	cdev_del(&char_device);
	device_destroy(pwm_class, pwm_device_num);
	class_destroy(pwm_class);
	unregister_chrdev_region(pwm_device_num, 1);
	printk("PWM driver: Removed\n");
}
module_init(ModuleInit);
module_exit(ModuleExit);
Willie Keeling

6by9
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 16123
Joined: Wed Dec 04, 2013 11:27 am
Location: ZZ9 Plural Z Alpha, aka just outside Cambridge.

Re: PWM driver questions

Thu Feb 29, 2024 6:10 pm

Whilst writing kernel modules is a useful skill, is this better achieved just from userspace and the sysfs API for PWM?

Code: Select all

echo 0 > /sys/class/pwm/pwmchip0/export
echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/period
echo 500000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable
would set up a 50% duty cycle 1kHz PWM signal.

I think your problem is that just loading a driver from modprobe or /etc/modules is a one off trigger, so it fails and moves on.

If your driver is loaded from device tree, the kernel knows that it's meant to be loaded, knows it failed with a EPROBDEFER, and will automatically retry loading it. You also get to provide a proper DT link to your desired PWM node, rather than hard coding the PWM device name.
Software Engineer at Raspberry Pi Ltd. Views expressed are still personal views.
I'm not interested in doing contracts for bespoke functionality - please don't ask.

wkeeling
Posts: 211
Joined: Fri Aug 25, 2017 2:16 pm
Location: Houston Texas

Re: PWM driver questions

Thu Feb 29, 2024 7:28 pm

What does the 'echo 0 > /sys/class/pwm/pwmchip0/export' do?

I would still need a daemon or something as I don't want the user space program to wait while between enable and disable time. Now I have another option.

Also sysfs interface will only work if the initial status is low or 0 duty cycle.
Willie Keeling

6by9
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 16123
Joined: Wed Dec 04, 2013 11:27 am
Location: ZZ9 Plural Z Alpha, aka just outside Cambridge.

Re: PWM driver questions

Thu Feb 29, 2024 10:37 pm

wkeeling wrote:
Thu Feb 29, 2024 7:28 pm
What does the 'echo 0 > /sys/class/pwm/pwmchip0/export' do?
It sets up pwm 0 as being exported via sysfs, and creates the /sys/class/pwm/pwmchip0/pwm0/ subdirectory.
Echo 1 and you'd get the other pwm channel.

You may also find "cat /sys/kernel/debug/pwm" useful as it dumps the state of all PWM devices on the system.
Software Engineer at Raspberry Pi Ltd. Views expressed are still personal views.
I'm not interested in doing contracts for bespoke functionality - please don't ask.

wkeeling
Posts: 211
Joined: Fri Aug 25, 2017 2:16 pm
Location: Houston Texas

Re: PWM driver questions

Fri Mar 01, 2024 7:38 pm

I have resolved my issue; it was my mistake in that the sound kernel module was being loaded in config.txt.

# Enable audio (loads snd_bcm2835)
dtparam=audio=on

Once the “dtparam=audio=on” was comment out both of my issues are resolved. The pwm0 pin is not driven high on boot and I am able to load my driver via /etc/modules at boot with no issues.

I am not going to run this project on Pi5 but would like to test it there just to see about compatibility in the bcm2711/RP1 world. I am not sure how to set pwm0 to GPIO12 pin.
Willie Keeling

aBUGSworstnightmare
Posts: 9701
Joined: Tue Jun 30, 2015 1:35 pm

Re: PWM driver questions

Sat Mar 02, 2024 7:41 am

wkeeling wrote:
Fri Mar 01, 2024 7:38 pm
..
I am not sure how to set pwm0 to GPIO12 pin.

Code: Select all

    compatible = "brcm,bcm2835";

    /* PWM0 function */
    fragment@0 {
        target = <&gpio>;
        __overlay__ {
            pwm_pins: pwm_pins {
                brcm,pins = <12>;
                brcm,function = <4>;
                brcm,pull = <0>;
            };
        };
    };

    fragment@1 {
        target = <&pwm>;
        frag1: __overlay__ {
            pinctrl-names = "default";
            pinctrl-0 = <&pwm_pins>;
            assigned-clock-rates = <100000000>;
            status = "okay";
        };
    };
This needs to go into your overlay.
As PWM on GPIO12 is PWM0_0 below is still valid

Code: Select all

pwms = <&pwm 0 5000000 0>;

wkeeling
Posts: 211
Joined: Fri Aug 25, 2017 2:16 pm
Location: Houston Texas

Re: PWM driver questions

Sun Mar 03, 2024 7:41 pm

aBUGSworstnightmare wrote:
Sat Mar 02, 2024 7:41 am

Code: Select all

    compatible = "brcm,bcm2835";

    /* PWM0 function */
    fragment@0 {
        target = <&gpio>;
        __overlay__ {
            pwm_pins: pwm_pins {
                brcm,pins = <12>;
                brcm,function = <4>;
                brcm,pull = <0>;
            };
        };
    };

    fragment@1 {
        target = <&pwm>;
        frag1: __overlay__ {
            pinctrl-names = "default";
            pinctrl-0 = <&pwm_pins>;
            assigned-clock-rates = <100000000>;
            status = "okay";
        };
    };
This needs to go into your overlay.
As PWM on GPIO12 is PWM0_0 below is still valid
I am missing something I create pwm-gpio12.dtbo in /boot/overlays and added dtoverlay=pwm-gpio12 to /boot/config.txt from. This on Pi5 running 6.1.0-rpi6-rpi-2712.

Code: Select all

/dts-v1/;
/plugin/;

/ {
    compatible = "brcm,bcm2835";

    /* PWM0 function */
    fragment@0 {
        target = <&gpio>;
        __overlay__ {
            pwm_pins: pwm_pins {
                brcm,pins = <12>;
                brcm,function = <4>;
                brcm,pull = <0>;
            };
        };
    };

    fragment@1 {
        target = <&pwm>;
        frag1: __overlay__ {
            pinctrl-names = "default";
            pinctrl-0 = <&pwm_pins>;
            assigned-clock-rates = <100000000>;
            status = "okay";
        };
    };
};

neither my driver/program or the sysfs interface seem to work on gpio12. Let me know if you see what I a doing wrong.

Thanks
Willie Keeling

wkeeling
Posts: 211
Joined: Fri Aug 25, 2017 2:16 pm
Location: Houston Texas

Re: PWM driver questions

Sun Mar 03, 2024 7:49 pm

On the ~EPROBE_DEFER to retry the probe of the module it looks like it only retry once (or retry while the modules are being loaded and we only get 2 tries on the 2 test I have done.

On Pi3:
wkeeling@pi3:~ $ dmesg | grep PWM
[ 5.890954] PWM driver: Installing
[ 5.890970] PWM driver: Device Major: 242, Minor: 1
[ 5.901841] PWM driver: Could not get PWM0! -517
[ 5.905925] PWM driver: Returning ~EPROBE_DEFER!
[ 8.437442] PWM driver: Installing
[ 8.437471] PWM driver: Device Major: 240, Minor: 1
[ 8.437791] PWM driver: Installed
wkeeling@pi3:~ $

On Pi Zero it tries twice and even with a probe defer returned the second time i do not get any more atempts;
koicafe_user@koicafe:~ $ dmesg | grep PWM
[ 10.393707] PWM driver: Installing
[ 10.430094] PWM driver: Device Major: 242, Minor: 1
[ 10.484645] PWM driver: Could not get PWM0! -517
[ 10.515711] PWM driver: Returning ~EPROBE_DEFER!
[ 18.910832] PWM driver: Installing
[ 18.910886] PWM driver: Device Major: 240, Minor: 1
[ 18.918275] PWM driver: Could not get PWM0! -517
[ 18.918753] PWM driver: Returning ~EPROBE_DEFER!
koicafe_user@koicafe:~ $

On the Pi Zero I have the work around of installing thru crontab @reboot.

Just wanting to understand the workings of EPROBE_DEFER better.

Thanks
Willie Keeling

aBUGSworstnightmare
Posts: 9701
Joined: Tue Jun 30, 2015 1:35 pm

Re: PWM driver questions

Mon Mar 04, 2024 7:28 am

Oh sorry, PWM0_0 on GPIO12 is ALT0

Code: Select all

..
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/pinctrl/bcm2835.h>
..


    fragment@0 {
        target = <&gpio>;
        __overlay__ {
            pwm_pins: pwm_pins {
                brcm,pins = <12>;
                brcm,function = <BCM2835_FSEL_ALT0>;
                brcm,pull = <0>;
            };
        };
    };
I'm always referring to bcm2835.h in my overlays.

PhilE
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 5946
Joined: Mon Sep 29, 2014 1:07 pm
Location: Cambridge

Re: PWM driver questions

Mon Mar 04, 2024 9:28 am

@wkeeling Can you see the effects of your overlay being applied in /proc/device-tree? What does "pinctrl 12" show?

@aBUGSworstnightmare It would have been considerate to make it clear that "BCM2835_FSEL_ALT0" and "4" are equivalent - you make it appear like a significant change. The problem with using #includes is that dtc doesn't understand them, and even if it did one would have to give it the path to a kernel include tree. For people not normally compiling kernels it makes overlay building more painful than it needs to be.

aBUGSworstnightmare
Posts: 9701
Joined: Tue Jun 30, 2015 1:35 pm

Re: PWM driver questions

Mon Mar 04, 2024 10:11 am

PhilE wrote:
Mon Mar 04, 2024 9:28 am
@wkeeling Can you see the effects of your overlay being applied in /proc/device-tree? What does "pinctrl 12" show?

@aBUGSworstnightmare It would have been considerate to make it clear that "BCM2835_FSEL_ALT0" and "4" are equivalent - you make it appear like a significant change. The problem with using #includes is that dtc doesn't understand them, and even if it did one would have to give it the path to a kernel include tree. For people not normally compiling kernels it makes overlay building more painful than it needs to be.
It is indeed the same, sorry if my last statement caused confusion.

I need to say that '4' for what is called 'ALT0' and '2' for 'ALT5' makes no sense to me and overcomplicates things for me. When I look i.e. at RP1 data sheet i.e. it only mentioned an ALT-mode. If I need to fiddle out what this is (ALT0 =4) etc each time from MY reference I need to say that I prefer to use the ALT-mode as reference - simply because it's easier to me to understand what I want to achieve (and I don't need to check the header what it translates to)

Having this 'logical assignment' added to the data sheets (i.e headline of table#4 of the RP1 data sheet and headline of table#1 of the CM4) would make life easier for all of us.

Code: Select all

/* brcm,function property */
#define BCM2835_FSEL_GPIO_IN	0
#define BCM2835_FSEL_GPIO_OUT	1
#define BCM2835_FSEL_ALT5	2
#define BCM2835_FSEL_ALT4	3
#define BCM2835_FSEL_ALT0	4
#define BCM2835_FSEL_ALT1	5
#define BCM2835_FSEL_ALT2	6
#define BCM2835_FSEL_ALT3	7

/* brcm,pull property */
#define BCM2835_PUD_OFF		0
#define BCM2835_PUD_DOWN	1
#define BCM2835_PUD_UP		2
btw, why got rpi1.h removed from include/dt-binding/pinctrl/ - https://github.com/raspberrypi/linux/co ... fa25c3cf72 ?

Code: Select all

/* brcm,function property */
#define RP1_FSEL_GPIO_IN	0
#define RP1_FSEL_GPIO_OUT	1
#define RP1_FSEL_ALT0_LEGACY	4
#define RP1_FSEL_ALT1_LEGACY	5
#define RP1_FSEL_ALT2_LEGACY	6
#define RP1_FSEL_ALT3_LEGACY	7
#define RP1_FSEL_ALT4_LEGACY	3
#define RP1_FSEL_ALT5_LEGACY	2
#define RP1_FSEL_ALT0		0x08
#define RP1_FSEL_ALT0INV	0x09
#define RP1_FSEL_ALT1		0x0a
#define RP1_FSEL_ALT1INV	0x0b
#define RP1_FSEL_ALT2		0x0c
#define RP1_FSEL_ALT2INV	0x0d
#define RP1_FSEL_ALT3		0x0e
#define RP1_FSEL_ALT3INV	0x0f
#define RP1_FSEL_ALT4		0x10
#define RP1_FSEL_ALT4INV	0x11
#define RP1_FSEL_ALT5		0x12
#define RP1_FSEL_ALT5INV	0x13
#define RP1_FSEL_ALT6		0x14
#define RP1_FSEL_ALT6INV	0x15
#define RP1_FSEL_ALT7		0x16
#define RP1_FSEL_ALT7INV	0x17
#define RP1_FSEL_ALT8		0x18
#define RP1_FSEL_ALT8INV	0x19
#define RP1_FSEL_NONE		0x1a

/* brcm,pull property */
#define RP1_PUD_OFF		0
#define RP1_PUD_DOWN		1
#define RP1_PUD_UP		2
#define RP1_PUD_KEEP		3

PhilE
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 5946
Joined: Mon Sep 29, 2014 1:07 pm
Location: Cambridge

Re: PWM driver questions

Mon Mar 04, 2024 10:36 am

The numbers are confusing because you are looking at two levels of history:
1. VideoCore versions before VC3 (VC2?) only supported four ALT functions - 0-3 - which were encoded as 4-7 (100-111), with 000 being GPIO input and 001 being GPIO output. In order to squeeze in 2 more alt functions, the 2 reserved code points (010 and 011) were repurposed.

2. You are using the legacy BCM2835-compatibility feature of the RP1 pinctrl driver. Look at rp1.dtsi and you'll see the modern, standard generic pinctrl declarations:

Code: Select all

			rp1_pwm1_gpio45: rp1_pwm1_gpio45 {
				function = "pwm1";
				pins = "gpio45";
				bias-pull-down;
			};

			rp1_spi0_gpio9: rp1_spi0_gpio9 {
				function = "spi0";
				pins = "gpio9", "gpio10", "gpio11";
				bias-disable;
				drive-strength = <12>;
				slew-rate = <1>;
			};

aBUGSworstnightmare
Posts: 9701
Joined: Tue Jun 30, 2015 1:35 pm

Re: PWM driver questions

Mon Mar 04, 2024 10:44 am

PhilE wrote:
Mon Mar 04, 2024 10:36 am
The numbers are confusing because you are looking at two levels of history:
1. VideoCore versions before VC3 (VC2?) only supported four ALT functions - 0-3 - which were encoded as 4-7 (100-111), with 000 being GPIO input and 001 being GPIO output. In order to squeeze in 2 more alt functions, the 2 reserved code points (010 and 011) were repurposed.

2. You are using the legacy BCM2835-compatibility feature of the RP1 pinctrl driver. Look at rp1.dtsi and you'll see the modern, standard generic pinctrl declarations:

Code: Select all

			rp1_pwm1_gpio45: rp1_pwm1_gpio45 {
				function = "pwm1";
				pins = "gpio45";
				bias-pull-down;
			};

			rp1_spi0_gpio9: rp1_spi0_gpio9 {
				function = "spi0";
				pins = "gpio9", "gpio10", "gpio11";
				bias-disable;
				drive-strength = <12>;
				slew-rate = <1>;
			};
well, these are perfectly clear as the include the pin# and the function requested (i.e. SPI0, starting from GPIO9 for MISO/MOSI/SCLK)).

wkeeling
Posts: 211
Joined: Fri Aug 25, 2017 2:16 pm
Location: Houston Texas

Re: PWM driver questions

Mon Mar 04, 2024 6:52 pm

PhilE wrote:
Mon Mar 04, 2024 9:28 am
@wkeeling Can you see the effects of your overlay being applied in /proc/device-tree? What does "pinctrl 12" show?
no changes to /proc/device-tree when dtoverlay=pwm-gpio12 is added.

wkeeling@pi5dev:~ $ pinctrl 12
12: a0 pn | lo // PIN32/GPIO12 = PWM0_CHAN0

that looks correct to me; now more confused why driver or sysfs don't drive pin.
Willie Keeling

aBUGSworstnightmare
Posts: 9701
Joined: Tue Jun 30, 2015 1:35 pm

Re: PWM driver questions

Mon Mar 04, 2024 7:08 pm

wkeeling wrote:
Mon Mar 04, 2024 6:52 pm
PhilE wrote:
Mon Mar 04, 2024 9:28 am
@wkeeling Can you see the effects of your overlay being applied in /proc/device-tree? What does "pinctrl 12" show?
no changes to /proc/device-tree when dtoverlay=pwm-gpio12 is added.

wkeeling@pi5dev:~ $ pinctrl 12
12: a0 pn | lo // PIN32/GPIO12 = PWM0_CHAN0

that looks correct to me; now more confused why driver or sysfs don't drive pin.
so you're using my overlay from here viewtopic.php?p=2199962#p2199962

Do you have multiple posts on this topic?

I've tested my overlay on a Pi5 4GB - working, evidence in in the linjed post.

wkeeling
Posts: 211
Joined: Fri Aug 25, 2017 2:16 pm
Location: Houston Texas

Re: PWM driver questions

Mon Mar 04, 2024 8:02 pm

aBUGSworstnightmare wrote:
Mon Mar 04, 2024 7:08 pm
wkeeling wrote:
Mon Mar 04, 2024 6:52 pm
PhilE wrote:
Mon Mar 04, 2024 9:28 am
@wkeeling Can you see the effects of your overlay being applied in /proc/device-tree? What does "pinctrl 12" show?
no changes to /proc/device-tree when dtoverlay=pwm-gpio12 is added.

wkeeling@pi5dev:~ $ pinctrl 12
12: a0 pn | lo // PIN32/GPIO12 = PWM0_CHAN0

that looks correct to me; now more confused why driver or sysfs don't drive pin.
so you're using my overlay from here viewtopic.php?p=2199962#p2199962

Do you have multiple posts on this topic?

I've tested my overlay on a Pi5 4GB - working, evidence in in the linjed post.
no using this it does not have the last part of you from your other thread

Code: Select all

/dts-v1/;
/plugin/;

/ {
    compatible = "brcm,bcm2835";

    /* PWM0 function */
    fragment@0 {
        target = <&gpio>;
        __overlay__ {
            pwm_pins: pwm_pins {
                brcm,pins = <12>;
                brcm,function = <4>;
                brcm,pull = <0>;
            };
        };
    };

    fragment@1 {
        target = <&pwm>;
        frag1: __overlay__ {
            pinctrl-names = "default";
            pinctrl-0 = <&pwm_pins>;
            assigned-clock-rates = <100000000>;
            status = "okay";
        };
    };
};
not sure if I have one that one thread on this; but this is thee current one.

I see your other tread not but I am only driving a led for test -- the real load of my driver controls the duty cycle of Microchip Technology
MIC5018YM4-TR (high-side MOSFET driver is designed to switch an N-channel enhancement-type MOSFET from a TTL compatible control signal in high- or low-side switch applications). My driver use pwm_request(0, 'my_label') to get the pwm resource.
Willie Keeling

aBUGSworstnightmare
Posts: 9701
Joined: Tue Jun 30, 2015 1:35 pm

Re: PWM driver questions

Mon Mar 04, 2024 9:01 pm

The linked threat has a PWM overlay which defines a pwm-led. The dtbo file is there, just test it and use ithe overlay as reference to your own.

wkeeling
Posts: 211
Joined: Fri Aug 25, 2017 2:16 pm
Location: Houston Texas

Re: PWM driver questions

Wed Mar 06, 2024 4:43 pm

Thanks to Phil’s post in viewtopic.php?t=359251 I understand where I went wrong and have test a fix but that fix of hardcoding the correct global PWM device index can’t be the proper way.

On older Pi’s (tested on < 3s) there is only one PWM chip and PWM0 is always the first entry in the global PWM device index. But on the Pi5, as Phil notes in the above thead, that can not be assumed.

With the “dtoverlay=pwm,pin=12,func=4” the PWM device I need to use is first on the second chip (the 3rd in the global index). Using that the driver or the sysfs interface works. But my guess is can not be depended on.

I am used pwm_request() as it is simple but it is deprecated and we should use one of the pwm_get() functions. But the questions becomes what is the correct way to find either the correct device or consumer name to pass to pwm_get()?

Code: Select all

struct pwm_device * pwm_get (struct device * dev, const char * con_id);

Arguments
dev=device for PWM consumer
con_id=consumer name

Description
Lookup is first attempted using DT. If the device was not instantiated from a device tree, a PWM chip and a relative index is looked up via a 
table supplied by board setup code (see pwm_add_table). Once a PWM chip has been found the specified PWM device will be requested and 
is ready to be used.

Returns
A pointer to the requested PWM device or an ERR_PTR-encoded error code on failure.  
The other improvement to the driver I would to add is a check that the overlay has been loaded and pin 12 is now set to alt func 4.

Please let me know the best practice for either of these.
Last edited by wkeeling on Wed Mar 06, 2024 7:31 pm, edited 1 time in total.
Willie Keeling

aBUGSworstnightmare
Posts: 9701
Joined: Tue Jun 30, 2015 1:35 pm

Re: PWM driver questions

Wed Mar 06, 2024 5:04 pm

wkeeling wrote:
Wed Mar 06, 2024 4:43 pm
Thanks to Phil’s post in viewtopic.php?t=359251 I understand where I went wrong and have test a fix but that fix of hardcoding the correct global PWM device index can’t be the proper way.

On older Pi’s (tested on < 3s) there is only one PWM chip and PWM0 is always the first entry in the global PWM device index. But on the Pi5, as Phil notes in the above thead, that can not be assumed.

With the “dtoverlay=pwm,pin=12,func=4” the PWM device I need to use is first on the second chip (the 3rd in the global index). Using that the driver or the sysfs interface works. But my guess is can not be depended on.

I am used pwm_request() as it is simple but it is deprecated and we should use one of the pwm_get() functions. But the questions becomes what is the correct way to find either the correct device or consumer name to pass to pwm_get().

Code: Select all

struct pwm_device * pwm_get (struct device * dev, const char * con_id);

Arguments
dev=device for PWM consumer
con_id=consumer name

Description
Lookup is first attempted using DT. If the device was not instantiated from a device tree, a PWM chip and a relative index is looked up via a 
table supplied by board setup code (see pwm_add_table). Once a PWM chip has been found the specified PWM device will be requested and 
is ready to be used.

Returns
A pointer to the requested PWM device or an ERR_PTR-encoded error code on failure.  
The other improvement to the driver I would to add is a check that the overlay has been loaded and pin 12 is now set to alt func 4.

Please let me know the best practice for either of these.
sorry, don't get that! you're using a kernel driver, you have assigned a label to the PWM, you know which PWM channel got configure in your DT overlay, so what else do you need? gpiochip4 is the RP1.

Code: Select all

pi@book13th:~ $ sudo cat /sys/kernel/debug/pwm
platform/1f0009c000.pwm, 4 PWM devices
 pwm-0   ((null)              ): period: 0 ns duty: 0 ns polarity: normal
 pwm-1   ((null)              ): period: 0 ns duty: 0 ns polarity: normal
 pwm-2   ((null)              ): period: 0 ns duty: 0 ns polarity: normal
 pwm-3   (cooling_fan         ): requested enabled period: 41566 ns duty: 14670 ns polarity: inverse usage_power

platform/1f00098000.pwm, 4 PWM devices
 pwm-0   (status0             ): requested enabled period: 3500000 ns duty: 0 ns polarity: normal
 pwm-1   (status1             ): requested enabled period: 3500000 ns duty: 3500000 ns polarity: normal
 pwm-2   (status2             ): requested enabled period: 3500000 ns duty: 1756862 ns polarity: normal
 pwm-3   (status3             ): requested enabled period: 3500000 ns duty: 0 ns polarity: normal

platform/107d517a80.pwm, 2 PWM devices
 pwm-0   ((null)              ): period: 0 ns duty: 0 ns polarity: normal
 pwm-1   ((null)              ): period: 0 ns duty: 0 ns polarity: normal
'platform/1f00098000.pwm' is the PWM controller for PMW0.0 to PWM0.3 - a 4-channel PWM controller

Return to “Device Tree”