Imx6ull pwm issue in kernel space

Hello!
I am working with colibri imx6ull 512 1T som.
BSP Version 5.4.0
I have my custom image that I changed backlight pwm to pwm_B (pwm 5).
I have a buzzer connected to PWM_A (pwm 4).
I have a big app runnning on my system.
My goal is that the buzzer sounds with a beep when I press touchscreen, so I modified ad7879.c driver file where I enable pwm for a brief period of time and then disable It.
Here copy the diff with my code and attach ad7879.c original driver.
I wil explain a little that I done:
I use deferred schedule work adding linux/workqueue.h library.
Define pwm parameters (pwm period, duty_cycle) and pwm device.
Define schedule work functions.
In function ad7879_probe() function I do the request for pwm4 and configure Its parameters.
In ad7879_ts_event_release() function I scheduled work and delay so when I release the touchscreen first beep_on_workqueue function is executed (where pwm is enabled) and after that beep_off_workqueue is executed (where pwm is disabled).
This is working but I have a bug that is sometimes the pwm buzzer is enabled but It not disabled untill press touchscreen again.
I debug the code and the function beep_off_workqueue is executed but It seems that pwm_disable(pwm4) dosn’t take effect.

diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c
index 556a2af46e18..1705bf70a048 100644
--- a/drivers/input/touchscreen/ad7879.c
+++ b/drivers/input/touchscreen/ad7879.c
@@ -29,7 +29,10 @@
 #include <linux/regmap.h>
 #include <linux/slab.h>
 #include <linux/gpio/driver.h>
-
+//Section added by Me
+#include <linux/pwm.h>
+#include <linux/workqueue.h>
+//end of section
 #include <linux/input/touchscreen.h>
 #include <linux/module.h>
 #include "ad7879.h"
@@ -131,7 +134,46 @@ struct ad7879 {
 	int			y;
 	int			Rt;
 };
+//Section added by Me
+// Define PWM parameters
+#define PWM_RATE	500000 //Period value in nano seconds - Freq = 2 kHz. Freq and duty_cycle buzzer technicals specifications, for more details see buzzer datasheet.
+#define PWM_DUTTY_CYCLE	250000 // Duty_cycle 50 %
+static bool success_initialization = true;
+// Declaration of deferreded work structs and functions
+static struct pwm_device *pwm4 = NULL;
+static void beep_on_workqueue(struct work_struct *work); /*Schedule work to power ON the Buzzer. */
+static void beep_off_workqueue(struct work_struct *work);/*Schedule delayed work to OFF the Buzzer after a time. */
+
+static  unsigned long time_delay; /*Time to be delayed to power OFF the Buzzer*/
+static DECLARE_WORK(beep_on_touch_workqueue, beep_on_workqueue);/*Add this work to the WorkQueue. Just add, not run it*/
 
+static DECLARE_DELAYED_WORK(beep_off_touch_workqueue, beep_off_workqueue);/*Add this work to the WorkQueue. Just add, not run it*/
+
+static void beep_on_workqueue(struct work_struct *work)
+{
+	if(success_initialization)
+	{
+		if (pwm_enable(pwm4) < 0)
+		{
+			printk(KERN_ERR "Couldn't set beep on to buzzer. PWM4 is disabled \n");
+		}
+	}
+	else
+	{
+		printk(KERN_ERR "Couldn't set beep on to buzzer. The buzzer wasn't initialized. \n");
+	}
+}
+ 
+static void beep_off_workqueue(struct work_struct *work)
+{
+	if(success_initialization)
+	{
+		pwm_disable(pwm4);
+	}
+	else
+	{
+		printk(KERN_ERR "Couldn't set beep off to buzzer. The buzzer wasn't initialized. \n");
+	}
+}
+// End of section added by Cuffia
 static int ad7879_read(struct ad7879 *ts, u8 reg)
 {
 	unsigned int val;
@@ -229,7 +271,10 @@ static int ad7879_report(struct ad7879 *ts)
 static void ad7879_ts_event_release(struct ad7879 *ts)
 {
 	struct input_dev *input_dev = ts->input;
-
+	// Section added byMe
+	schedule_work(&beep_on_touch_workqueue); /*Schedule this work into the queue*/
+	schedule_delayed_work(&beep_off_touch_workqueue,time_delay);/*Schedule this work into the queue with time_delay*/
+	// End section added by Me
 	input_report_abs(input_dev, ABS_PRESSURE, 0);
 	input_report_key(input_dev, BTN_TOUCH, 0);
 	input_sync(input_dev);
@@ -521,6 +566,17 @@ int ad7879_probe(struct device *dev, struct regmap *regmap,
 	struct input_dev *input_dev;
 	int err;
 	u16 revid;
+	//Section added by Me
+	time_delay = jiffies + 200 * HZ / 1000; /*Time used to delay the work. 200 is time in millisecond. 1000 is a constant*/
+	pwm4 = pwm_request(0, "pwmchip0");
+	if (pwm4 == NULL || (pwm_config(pwm4, PWM_DUTTY_CYCLE, PWM_RATE) < 0))
+	{
+		printk(KERN_ERR "Pwm4 config error \n");
+		success_initialization = false;
+	}
+	pwm_disable(pwm4);
+	pwm_free(pwm4);
+	//End of section added by Me
 
 	if (irq <= 0) {
 		dev_err(dev, "No IRQ specified\n");

Where is my problem? It can be the use of threads o cpu?

Hello @emmaperea,
It’s hard to be sure of what is happening in your case.

I have one idea of something that could be going wrong:

Because you are using two separate work queues, they will be scheduled independently. I don’t think there’s a way to guarantee that your “on” scheduled work runs before your “off” scheduled work, even with the delay. I would say that one possibility of failure would be that your disable function runs before your enable one, and because of that, you get your PWM enabled for the whole duration between that moment and the next time you touch the string.

If I can make a suggestion, I would say that you could remove your “off” queue altogether, and implement the entire thing on your beep_on_workqueue function, like this piece of code:

#include <delay.h>
static void beep_on_workqueue(struct work_struct *work)
{
	if(success_initialization)
	{
       pwm_enable(pwm4);
       msleep(200);
       pwm_disable(pwm4);
	}
	else
	{
		printk(KERN_ERR "Couldn't set beep on to buzzer. The buzzer wasn't initialized. \n");
	}
}

That way you guarantee that your disable call will always run after your enable call.

Hello @rafael.tx
Thanks for your fast reply.
I made your suggested change and it seems that It works.