[08/10] pwm: core: add pulse feature to the PWM framework

Message ID 1445895161-2317-9-git-send-email-o.schinagl@ultimaker.com
State New
Headers show

Commit Message

Olliver Schinagl Oct. 26, 2015, 9:32 p.m.
From: Olliver Schinagl <oliver@schinagl.nl>

Some hardware PWM's have the possibility to only send out one (or more)
pulses. This can be quite a useful feature in case one wants or needs
only a single pulse, but at the exact width.

Additionally, if multiple pulses are possible, outputting a fixed amount
of pulses can be useful for various timing specific purposes.

A few new functions have been expanded or added for this new behavior.

* pwm_config()	now takes an additional parameter to setup the number of
		pulses to output. The driver may force this to 0 or 1
		for if example if this feature is not or only partially
		supported
* pwm_[sg]et_pulse_count()	get or set the number of pulses the pwm
				framework is configured for
* pwm_get_pulse_count_max()	get the maximum number of pulses the pwm
				driver supports
* pwm_pulse()		Tell the PWM to emit a pre-configured number of pulses
* pwm_pulse_done()	an internal function for drivers to call when
			they have completed their pre-configured number
			of pulses
* pwm_is_pulsing()	tells the callers if the pwm is busy pulsing,
			yielding a little more information than just
			pwm_is_enabled()

Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
---
 drivers/pwm/core.c      | 30 +++++++++++++++++++----
 drivers/pwm/pwm-gpio.c  |  3 ++-
 drivers/pwm/pwm-sun4i.c |  3 ++-
 drivers/pwm/sysfs.c     | 58 ++++++++++++++++++++++++++++++++++++++++++--
 include/linux/pwm.h     | 64 ++++++++++++++++++++++++++++++++++++++++++++++---
 5 files changed, 147 insertions(+), 11 deletions(-)

Comments

kbuild test robot Oct. 26, 2015, 9:59 p.m. | #1
Hi Olliver,

[auto build test ERROR on pwm/for-next -- if it's inappropriate base, please suggest rules for selecting the more suitable base]

url:    https://github.com/0day-ci/linux/commits/Olliver-Schinagl/pwm-lpc18xx_pwm-use-pwm_set_chip_data/20151027-053853
config: x86_64-randconfig-n0-10270521 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All errors (new ones prefixed by >>):

   drivers/gpu/drm/i915/intel_panel.c: In function 'pwm_set_backlight':
>> drivers/gpu/drm/i915/intel_panel.c:652:2: error: too few arguments to function 'pwm_config'
     pwm_config(panel->backlight.pwm, duty_ns, CRC_PMIC_PWM_PERIOD_NS);
     ^
   In file included from drivers/gpu/drm/i915/intel_panel.c:35:0:
   include/linux/pwm.h:52:19: note: declared here
    static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
                      ^
   drivers/gpu/drm/i915/intel_panel.c: In function 'pwm_disable_backlight':
   drivers/gpu/drm/i915/intel_panel.c:797:2: error: too few arguments to function 'pwm_config'
     pwm_config(panel->backlight.pwm, 0, CRC_PMIC_PWM_PERIOD_NS);
     ^
   In file included from drivers/gpu/drm/i915/intel_panel.c:35:0:
   include/linux/pwm.h:52:19: note: declared here
    static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
                      ^
   drivers/gpu/drm/i915/intel_panel.c: In function 'pwm_setup_backlight':
   drivers/gpu/drm/i915/intel_panel.c:1442:11: error: too few arguments to function 'pwm_config'
     retval = pwm_config(panel->backlight.pwm, CRC_PMIC_PWM_PERIOD_NS,
              ^
   In file included from drivers/gpu/drm/i915/intel_panel.c:35:0:
   include/linux/pwm.h:52:19: note: declared here
    static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
                      ^

vim +/pwm_config +652 drivers/gpu/drm/i915/intel_panel.c

0fb890c0 Vandana Kannan 2015-05-05  646  
b029e66f Shobhit Kumar  2015-06-26  647  static void pwm_set_backlight(struct intel_connector *connector, u32 level)
b029e66f Shobhit Kumar  2015-06-26  648  {
b029e66f Shobhit Kumar  2015-06-26  649  	struct intel_panel *panel = &connector->panel;
b029e66f Shobhit Kumar  2015-06-26  650  	int duty_ns = DIV_ROUND_UP(level * CRC_PMIC_PWM_PERIOD_NS, 100);
b029e66f Shobhit Kumar  2015-06-26  651  
b029e66f Shobhit Kumar  2015-06-26 @652  	pwm_config(panel->backlight.pwm, duty_ns, CRC_PMIC_PWM_PERIOD_NS);
b029e66f Shobhit Kumar  2015-06-26  653  }
b029e66f Shobhit Kumar  2015-06-26  654  
7bd688cd Jani Nikula    2013-11-08  655  static void

:::::: The code at line 652 was first introduced by commit
:::::: b029e66fa8e39ba10dcc47b114be8da8b082493b drm/i915: Backlight control using CRC PMIC based PWM driver

:::::: TO: Shobhit Kumar <shobhit.kumar@intel.com>
:::::: CC: Daniel Vetter <daniel.vetter@ffwll.ch>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
kbuild test robot Oct. 26, 2015, 10:09 p.m. | #2
Hi Olliver,

[auto build test WARNING on pwm/for-next -- if it's inappropriate base, please suggest rules for selecting the more suitable base]

url:    https://github.com/0day-ci/linux/commits/Olliver-Schinagl/pwm-lpc18xx_pwm-use-pwm_set_chip_data/20151027-053853
config: avr32-atstk1006_defconfig (attached as .config)
reproduce:
        wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=avr32 

All warnings (new ones prefixed by >>):

>> drivers/pwm/pwm-atmel.c:278: warning: initialization from incompatible pointer type

vim +278 drivers/pwm/pwm-atmel.c

472ac3dc Alexandre Belloni 2015-05-25  262  	mutex_lock(&atmel_pwm->isr_lock);
472ac3dc Alexandre Belloni 2015-05-25  263  	atmel_pwm->updated_pwms |= atmel_pwm_readl(atmel_pwm, PWM_ISR);
472ac3dc Alexandre Belloni 2015-05-25  264  
472ac3dc Alexandre Belloni 2015-05-25  265  	while (!(atmel_pwm->updated_pwms & (1 << pwm->hwpwm)) &&
472ac3dc Alexandre Belloni 2015-05-25  266  	       time_before(jiffies, timeout)) {
472ac3dc Alexandre Belloni 2015-05-25  267  		usleep_range(10, 100);
472ac3dc Alexandre Belloni 2015-05-25  268  		atmel_pwm->updated_pwms |= atmel_pwm_readl(atmel_pwm, PWM_ISR);
472ac3dc Alexandre Belloni 2015-05-25  269  	}
32b16d46 Bo Shen           2013-12-13  270  
472ac3dc Alexandre Belloni 2015-05-25  271  	mutex_unlock(&atmel_pwm->isr_lock);
32b16d46 Bo Shen           2013-12-13  272  	atmel_pwm_writel(atmel_pwm, PWM_DIS, 1 << pwm->hwpwm);
32b16d46 Bo Shen           2013-12-13  273  
32b16d46 Bo Shen           2013-12-13  274  	clk_disable(atmel_pwm->clk);
32b16d46 Bo Shen           2013-12-13  275  }
32b16d46 Bo Shen           2013-12-13  276  
32b16d46 Bo Shen           2013-12-13  277  static const struct pwm_ops atmel_pwm_ops = {
32b16d46 Bo Shen           2013-12-13 @278  	.config = atmel_pwm_config,
32b16d46 Bo Shen           2013-12-13  279  	.set_polarity = atmel_pwm_set_polarity,
32b16d46 Bo Shen           2013-12-13  280  	.enable = atmel_pwm_enable,
32b16d46 Bo Shen           2013-12-13  281  	.disable = atmel_pwm_disable,
32b16d46 Bo Shen           2013-12-13  282  	.owner = THIS_MODULE,
32b16d46 Bo Shen           2013-12-13  283  };
32b16d46 Bo Shen           2013-12-13  284  
32b16d46 Bo Shen           2013-12-13  285  struct atmel_pwm_data {
32b16d46 Bo Shen           2013-12-13  286  	void (*config)(struct pwm_chip *chip, struct pwm_device *pwm,

:::::: The code at line 278 was first introduced by commit
:::::: 32b16d46e4154d6f9d9c1d5dd35f64cdf08b7aea pwm: atmel-pwm: Add Atmel PWM controller driver

:::::: TO: Bo Shen <voice.shen@atmel.com>
:::::: CC: Thierry Reding <thierry.reding@gmail.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
kbuild test robot Oct. 26, 2015, 10:10 p.m. | #3
Hi Olliver,

[auto build test ERROR on pwm/for-next -- if it's inappropriate base, please suggest rules for selecting the more suitable base]

url:    https://github.com/0day-ci/linux/commits/Olliver-Schinagl/pwm-lpc18xx_pwm-use-pwm_set_chip_data/20151027-053853
config: i386-randconfig-s1-201543 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All errors (new ones prefixed by >>):

   drivers/input/misc/max77693-haptic.c: In function 'max77693_haptic_set_duty_cycle':
>> drivers/input/misc/max77693-haptic.c:76:10: error: too few arguments to function 'pwm_config'
     error = pwm_config(haptic->pwm_dev, delta, haptic->pwm_dev->period);
             ^
   In file included from drivers/input/misc/max77693-haptic.c:23:0:
   include/linux/pwm.h:25:5: note: declared here
    int pwm_config(struct pwm_device *pwm, int duty_ns,
        ^
--
   drivers/input/misc/max8997_haptic.c: In function 'max8997_haptic_set_duty_cycle':
>> drivers/input/misc/max8997_haptic.c:76:9: error: too few arguments to function 'pwm_config'
      ret = pwm_config(chip->pwm, duty, chip->pwm_period);
            ^
   In file included from drivers/input/misc/max8997_haptic.c:29:0:
   include/linux/pwm.h:25:5: note: declared here
    int pwm_config(struct pwm_device *pwm, int duty_ns,
        ^

vim +/pwm_config +76 drivers/input/misc/max77693-haptic.c

a3b3ca75 Jaewon Kim 2014-09-11  70  
a3b3ca75 Jaewon Kim 2014-09-11  71  static int max77693_haptic_set_duty_cycle(struct max77693_haptic *haptic)
a3b3ca75 Jaewon Kim 2014-09-11  72  {
a3b3ca75 Jaewon Kim 2014-09-11  73  	int delta = (haptic->pwm_dev->period + haptic->pwm_duty) / 2;
a3b3ca75 Jaewon Kim 2014-09-11  74  	int error;
a3b3ca75 Jaewon Kim 2014-09-11  75  
a3b3ca75 Jaewon Kim 2014-09-11 @76  	error = pwm_config(haptic->pwm_dev, delta, haptic->pwm_dev->period);
a3b3ca75 Jaewon Kim 2014-09-11  77  	if (error) {
a3b3ca75 Jaewon Kim 2014-09-11  78  		dev_err(haptic->dev, "failed to configure pwm: %d\n", error);
a3b3ca75 Jaewon Kim 2014-09-11  79  		return error;

:::::: The code at line 76 was first introduced by commit
:::::: a3b3ca753cdc92c7d5f57404afed3115b3b79cc6 Input: add haptic driver on max77693

:::::: TO: Jaewon Kim <jaewon02.kim@samsung.com>
:::::: CC: Dmitry Torokhov <dmitry.torokhov@gmail.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
kbuild test robot Oct. 26, 2015, 10:11 p.m. | #4
Hi Olliver,

[auto build test WARNING on pwm/for-next -- if it's inappropriate base, please suggest rules for selecting the more suitable base]

url:    https://github.com/0day-ci/linux/commits/Olliver-Schinagl/pwm-lpc18xx_pwm-use-pwm_set_chip_data/20151027-053853
config: arm-at91_dt_defconfig (attached as .config)
reproduce:
        wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=arm 

All warnings (new ones prefixed by >>):

>> drivers/pwm/pwm-atmel-tcb.c:359:2: warning: initialization from incompatible pointer type
     .config = atmel_tcb_pwm_config,
     ^
   drivers/pwm/pwm-atmel-tcb.c:359:2: warning: (near initialization for 'atmel_tcb_pwm_ops.config')

vim +359 drivers/pwm/pwm-atmel-tcb.c

9421bade Boris BREZILLON 2013-01-08  343  	}
9421bade Boris BREZILLON 2013-01-08  344  
9421bade Boris BREZILLON 2013-01-08  345  	tcbpwm->period = period;
9421bade Boris BREZILLON 2013-01-08  346  	tcbpwm->div = i;
9421bade Boris BREZILLON 2013-01-08  347  	tcbpwm->duty = duty;
9421bade Boris BREZILLON 2013-01-08  348  
9421bade Boris BREZILLON 2013-01-08  349  	/* If the PWM is enabled, call enable to apply the new conf */
5c31252c Boris Brezillon 2015-07-01  350  	if (pwm_is_enabled(pwm))
9421bade Boris BREZILLON 2013-01-08  351  		atmel_tcb_pwm_enable(chip, pwm);
9421bade Boris BREZILLON 2013-01-08  352  
9421bade Boris BREZILLON 2013-01-08  353  	return 0;
9421bade Boris BREZILLON 2013-01-08  354  }
9421bade Boris BREZILLON 2013-01-08  355  
9421bade Boris BREZILLON 2013-01-08  356  static const struct pwm_ops atmel_tcb_pwm_ops = {
9421bade Boris BREZILLON 2013-01-08  357  	.request = atmel_tcb_pwm_request,
9421bade Boris BREZILLON 2013-01-08  358  	.free = atmel_tcb_pwm_free,
9421bade Boris BREZILLON 2013-01-08 @359  	.config = atmel_tcb_pwm_config,
9421bade Boris BREZILLON 2013-01-08  360  	.set_polarity = atmel_tcb_pwm_set_polarity,
9421bade Boris BREZILLON 2013-01-08  361  	.enable = atmel_tcb_pwm_enable,
9421bade Boris BREZILLON 2013-01-08  362  	.disable = atmel_tcb_pwm_disable,
83c80dc5 Axel Lin        2013-03-31  363  	.owner = THIS_MODULE,
9421bade Boris BREZILLON 2013-01-08  364  };
9421bade Boris BREZILLON 2013-01-08  365  
9421bade Boris BREZILLON 2013-01-08  366  static int atmel_tcb_pwm_probe(struct platform_device *pdev)
9421bade Boris BREZILLON 2013-01-08  367  {

:::::: The code at line 359 was first introduced by commit
:::::: 9421bade0765d8ffb86b8a99213b611278a3542a pwm: atmel: add Timer Counter Block PWM driver

:::::: TO: Boris BREZILLON <linux-arm@overkiz.com>
:::::: CC: Thierry Reding <thierry.reding@avionic-design.de>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
kbuild test robot Oct. 26, 2015, 11:06 p.m. | #5
Hi Olliver,

[auto build test WARNING on pwm/for-next -- if it's inappropriate base, please suggest rules for selecting the more suitable base]

url:    https://github.com/0day-ci/linux/commits/Olliver-Schinagl/pwm-lpc18xx_pwm-use-pwm_set_chip_data/20151027-053853
reproduce:
        # apt-get install sparse
        make ARCH=x86_64 allmodconfig
        make C=1 CF=-D__CHECK_ENDIAN__


sparse warnings: (new ones prefixed by >>)

>> drivers/clk/clk-pwm.c:89:25: sparse: not enough arguments for function pwm_config
   drivers/clk/clk-pwm.c: In function 'clk_pwm_probe':
   drivers/clk/clk-pwm.c:89:8: error: too few arguments to function 'pwm_config'
     ret = pwm_config(pwm, (pwm->period + 1) >> 1, pwm->period);
           ^
   In file included from drivers/clk/clk-pwm.c:15:0:
   include/linux/pwm.h:25:5: note: declared here
    int pwm_config(struct pwm_device *pwm, int duty_ns,
        ^
--
>> drivers/gpu/drm/i915/intel_panel.c:652:19: sparse: not enough arguments for function pwm_config
   drivers/gpu/drm/i915/intel_panel.c:797:19: sparse: not enough arguments for function pwm_config
   drivers/gpu/drm/i915/intel_panel.c:1442:28: sparse: not enough arguments for function pwm_config
   drivers/gpu/drm/i915/intel_panel.c: In function 'pwm_set_backlight':
   drivers/gpu/drm/i915/intel_panel.c:652:2: error: too few arguments to function 'pwm_config'
     pwm_config(panel->backlight.pwm, duty_ns, CRC_PMIC_PWM_PERIOD_NS);
     ^
   In file included from drivers/gpu/drm/i915/intel_panel.c:35:0:
   include/linux/pwm.h:25:5: note: declared here
    int pwm_config(struct pwm_device *pwm, int duty_ns,
        ^
   drivers/gpu/drm/i915/intel_panel.c: In function 'pwm_disable_backlight':
   drivers/gpu/drm/i915/intel_panel.c:797:2: error: too few arguments to function 'pwm_config'
     pwm_config(panel->backlight.pwm, 0, CRC_PMIC_PWM_PERIOD_NS);
     ^
   In file included from drivers/gpu/drm/i915/intel_panel.c:35:0:
   include/linux/pwm.h:25:5: note: declared here
    int pwm_config(struct pwm_device *pwm, int duty_ns,
        ^
   drivers/gpu/drm/i915/intel_panel.c: In function 'pwm_setup_backlight':
   drivers/gpu/drm/i915/intel_panel.c:1442:11: error: too few arguments to function 'pwm_config'
     retval = pwm_config(panel->backlight.pwm, CRC_PMIC_PWM_PERIOD_NS,
              ^
   In file included from drivers/gpu/drm/i915/intel_panel.c:35:0:
   include/linux/pwm.h:25:5: note: declared here
    int pwm_config(struct pwm_device *pwm, int duty_ns,
        ^
--
>> drivers/hwmon/pwm-fan.c:51:25: sparse: not enough arguments for function pwm_config
   drivers/hwmon/pwm-fan.c:240:25: sparse: not enough arguments for function pwm_config
   drivers/hwmon/pwm-fan.c:313:25: sparse: not enough arguments for function pwm_config
   drivers/hwmon/pwm-fan.c: In function '__set_pwm':
   drivers/hwmon/pwm-fan.c:51:8: error: too few arguments to function 'pwm_config'
     ret = pwm_config(ctx->pwm, duty, ctx->pwm->period);
           ^
   In file included from drivers/hwmon/pwm-fan.c:25:0:
   include/linux/pwm.h:25:5: note: declared here
    int pwm_config(struct pwm_device *pwm, int duty_ns,
        ^
   drivers/hwmon/pwm-fan.c: In function 'pwm_fan_probe':
   drivers/hwmon/pwm-fan.c:240:8: error: too few arguments to function 'pwm_config'
     ret = pwm_config(ctx->pwm, duty_cycle, ctx->pwm->period);
           ^
   In file included from drivers/hwmon/pwm-fan.c:25:0:
   include/linux/pwm.h:25:5: note: declared here
    int pwm_config(struct pwm_device *pwm, int duty_ns,
        ^
   drivers/hwmon/pwm-fan.c: In function 'pwm_fan_resume':
   drivers/hwmon/pwm-fan.c:313:8: error: too few arguments to function 'pwm_config'
     ret = pwm_config(ctx->pwm, duty, ctx->pwm->period);
           ^
   In file included from drivers/hwmon/pwm-fan.c:25:0:
   include/linux/pwm.h:25:5: note: declared here
    int pwm_config(struct pwm_device *pwm, int duty_ns,
        ^
--
>> drivers/input/misc/pwm-beeper.c:56:33: sparse: not enough arguments for function pwm_config
   drivers/input/misc/pwm-beeper.c:161:27: sparse: not enough arguments for function pwm_config
   drivers/input/misc/pwm-beeper.c: In function 'pwm_beeper_event':
   drivers/input/misc/pwm-beeper.c:56:9: error: too few arguments to function 'pwm_config'
      ret = pwm_config(beeper->pwm, period / 2, period);
            ^
   In file included from drivers/input/misc/pwm-beeper.c:21:0:
   include/linux/pwm.h:25:5: note: declared here
    int pwm_config(struct pwm_device *pwm, int duty_ns,
        ^
   drivers/input/misc/pwm-beeper.c: In function 'pwm_beeper_resume':
   drivers/input/misc/pwm-beeper.c:161:3: error: too few arguments to function 'pwm_config'
      pwm_config(beeper->pwm, beeper->period / 2, beeper->period);
      ^
   In file included from drivers/input/misc/pwm-beeper.c:21:0:
   include/linux/pwm.h:25:5: note: declared here
    int pwm_config(struct pwm_device *pwm, int duty_ns,
        ^
--
>> drivers/leds/leds-pwm.c:46:19: sparse: not enough arguments for function pwm_config
   drivers/leds/leds-pwm.c: In function '__led_pwm_set':
   drivers/leds/leds-pwm.c:46:2: error: too few arguments to function 'pwm_config'
     pwm_config(led_dat->pwm, new_duty, led_dat->period);
     ^
   In file included from drivers/leds/leds-pwm.c:22:0:
   include/linux/pwm.h:25:5: note: declared here
    int pwm_config(struct pwm_device *pwm, int duty_ns,
        ^
--
>> drivers/regulator/pwm-regulator.c:64:25: sparse: not enough arguments for function pwm_config
   drivers/regulator/pwm-regulator.c:123:25: sparse: not enough arguments for function pwm_config
   drivers/regulator/pwm-regulator.c: In function 'pwm_regulator_set_voltage_sel':
   drivers/regulator/pwm-regulator.c:64:8: error: too few arguments to function 'pwm_config'
     ret = pwm_config(drvdata->pwm, dutycycle, pwm_reg_period);
           ^
   In file included from drivers/regulator/pwm-regulator.c:22:0:
   include/linux/pwm.h:25:5: note: declared here
    int pwm_config(struct pwm_device *pwm, int duty_ns,
        ^
   drivers/regulator/pwm-regulator.c: In function 'pwm_regulator_set_voltage':
   drivers/regulator/pwm-regulator.c:123:8: error: too few arguments to function 'pwm_config'
     ret = pwm_config(drvdata->pwm, (period / 100) * duty_cycle, period);
           ^
   In file included from drivers/regulator/pwm-regulator.c:22:0:
   include/linux/pwm.h:25:5: note: declared here
    int pwm_config(struct pwm_device *pwm, int duty_ns,
        ^
--
>> drivers/video/backlight/lm3630a_bl.c:168:19: sparse: not enough arguments for function pwm_config
   drivers/video/backlight/lm3630a_bl.c: In function 'lm3630a_pwm_ctrl':
   drivers/video/backlight/lm3630a_bl.c:168:2: error: too few arguments to function 'pwm_config'
     pwm_config(pchip->pwmd, duty, period);
     ^
   In file included from drivers/video/backlight/lm3630a_bl.c:19:0:
   include/linux/pwm.h:25:5: note: declared here
    int pwm_config(struct pwm_device *pwm, int duty_ns,
        ^
--
>> drivers/video/backlight/lp855x_bl.c:251:19: sparse: not enough arguments for function pwm_config
   drivers/video/backlight/lp855x_bl.c: In function 'lp855x_pwm_ctrl':
   drivers/video/backlight/lp855x_bl.c:251:2: error: too few arguments to function 'pwm_config'
     pwm_config(lp->pwm, duty, period);
     ^
   In file included from drivers/video/backlight/lp855x_bl.c:19:0:
   include/linux/pwm.h:25:5: note: declared here
    int pwm_config(struct pwm_device *pwm, int duty_ns,
        ^
--
>> drivers/video/backlight/pwm_bl.c:69:19: sparse: not enough arguments for function pwm_config
   drivers/video/backlight/pwm_bl.c:108:27: sparse: not enough arguments for function pwm_config
   drivers/video/backlight/pwm_bl.c: In function 'pwm_backlight_power_off':
   drivers/video/backlight/pwm_bl.c:69:2: error: too few arguments to function 'pwm_config'
     pwm_config(pb->pwm, 0, pb->period);
     ^
   In file included from drivers/video/backlight/pwm_bl.c:22:0:
   include/linux/pwm.h:25:5: note: declared here
    int pwm_config(struct pwm_device *pwm, int duty_ns,
        ^
   drivers/video/backlight/pwm_bl.c: In function 'pwm_backlight_update_status':
   drivers/video/backlight/pwm_bl.c:108:3: error: too few arguments to function 'pwm_config'
      pwm_config(pb->pwm, duty_cycle, pb->period);
      ^
   In file included from drivers/video/backlight/pwm_bl.c:22:0:
   include/linux/pwm.h:25:5: note: declared here
    int pwm_config(struct pwm_device *pwm, int duty_ns,
        ^
--
   drivers/video/fbdev/ssd1307fb.c:150:29: sparse: incorrect type in initializer (different address spaces)
   drivers/video/fbdev/ssd1307fb.c:150:29:    expected unsigned char [usertype] *vmem
   drivers/video/fbdev/ssd1307fb.c:150:29:    got char [noderef] <asn:2>*screen_base
   drivers/video/fbdev/ssd1307fb.c:226:13: sparse: incorrect type in assignment (different address spaces)
   drivers/video/fbdev/ssd1307fb.c:226:13:    expected unsigned char [noderef] [usertype] <asn:2>*dst
   drivers/video/fbdev/ssd1307fb.c:226:13:    got void *<noident>
   drivers/video/fbdev/ssd1307fb.c:228:28: sparse: incorrect type in argument 1 (different address spaces)
   drivers/video/fbdev/ssd1307fb.c:228:28:    expected void *to
   drivers/video/fbdev/ssd1307fb.c:228:28:    got unsigned char [noderef] [usertype] <asn:2>*dst
>> drivers/video/fbdev/ssd1307fb.c:299:27: sparse: not enough arguments for function pwm_config
   drivers/video/fbdev/ssd1307fb.c: In function 'ssd1307fb_init':
   drivers/video/fbdev/ssd1307fb.c:299:3: error: too few arguments to function 'pwm_config'
      pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
      ^
   In file included from drivers/video/fbdev/ssd1307fb.c:17:0:
   include/linux/pwm.h:25:5: note: declared here
    int pwm_config(struct pwm_device *pwm, int duty_ns,
        ^

vim +89 drivers/clk/clk-pwm.c

9a74ccdb Philipp Zabel 2015-02-13  73  
9a74ccdb Philipp Zabel 2015-02-13  74  	if (!pwm->period) {
9a74ccdb Philipp Zabel 2015-02-13  75  		dev_err(&pdev->dev, "invalid PWM period\n");
9a74ccdb Philipp Zabel 2015-02-13  76  		return -EINVAL;
9a74ccdb Philipp Zabel 2015-02-13  77  	}
9a74ccdb Philipp Zabel 2015-02-13  78  
9a74ccdb Philipp Zabel 2015-02-13  79  	if (of_property_read_u32(node, "clock-frequency", &clk_pwm->fixed_rate))
9a74ccdb Philipp Zabel 2015-02-13  80  		clk_pwm->fixed_rate = NSEC_PER_SEC / pwm->period;
9a74ccdb Philipp Zabel 2015-02-13  81  
9a74ccdb Philipp Zabel 2015-02-13  82  	if (pwm->period != NSEC_PER_SEC / clk_pwm->fixed_rate &&
9a74ccdb Philipp Zabel 2015-02-13  83  	    pwm->period != DIV_ROUND_UP(NSEC_PER_SEC, clk_pwm->fixed_rate)) {
9a74ccdb Philipp Zabel 2015-02-13  84  		dev_err(&pdev->dev,
9a74ccdb Philipp Zabel 2015-02-13  85  			"clock-frequency does not match PWM period\n");
9a74ccdb Philipp Zabel 2015-02-13  86  		return -EINVAL;
9a74ccdb Philipp Zabel 2015-02-13  87  	}
9a74ccdb Philipp Zabel 2015-02-13  88  
9a74ccdb Philipp Zabel 2015-02-13 @89  	ret = pwm_config(pwm, (pwm->period + 1) >> 1, pwm->period);
9a74ccdb Philipp Zabel 2015-02-13  90  	if (ret < 0)
9a74ccdb Philipp Zabel 2015-02-13  91  		return ret;
9a74ccdb Philipp Zabel 2015-02-13  92  
9a74ccdb Philipp Zabel 2015-02-13  93  	clk_name = node->name;
9a74ccdb Philipp Zabel 2015-02-13  94  	of_property_read_string(node, "clock-output-names", &clk_name);
9a74ccdb Philipp Zabel 2015-02-13  95  
9a74ccdb Philipp Zabel 2015-02-13  96  	init.name = clk_name;
9a74ccdb Philipp Zabel 2015-02-13  97  	init.ops = &clk_pwm_ops;

:::::: The code at line 89 was first introduced by commit
:::::: 9a74ccdbbb8fa6302ae1ba606f2ef0c03d3242ab clk: Add PWM clock driver

:::::: TO: Philipp Zabel <p.zabel@pengutronix.de>
:::::: CC: Michael Turquette <mturquette@linaro.org>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
--
To unsubscribe from this list: send the line "unsubscribe linux-pwm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Thierry Reding Nov. 6, 2015, 3:18 p.m. | #6
On Mon, Oct 26, 2015 at 10:32:39PM +0100, Olliver Schinagl wrote:
> From: Olliver Schinagl <oliver@schinagl.nl>
> 
> Some hardware PWM's have the possibility to only send out one (or more)
> pulses. This can be quite a useful feature in case one wants or needs
> only a single pulse, but at the exact width.
> 
> Additionally, if multiple pulses are possible, outputting a fixed amount
> of pulses can be useful for various timing specific purposes.

I see how theoretically this would be nice to have. But I'm reluctant to
merge this feature if there aren't any users. What drivers in the kernel
would want to use this feature? Are there new drivers being worked on
that will need this?

> A few new functions have been expanded or added for this new behavior.
> 
> * pwm_config()	now takes an additional parameter to setup the number of
> 		pulses to output. The driver may force this to 0 or 1
> 		for if example if this feature is not or only partially
> 		supported

This is problematic because you need to atomically update all drivers
and users (the kbuild robot already told you that you didn't do this).
To make things easier I suggest you wait with this change until the
atomic PWM patches have been merged, at which point it should become a
lot easier to deal with this kind of extension.

> * pwm_[sg]et_pulse_count()	get or set the number of pulses the pwm
> 				framework is configured for
> * pwm_get_pulse_count_max()	get the maximum number of pulses the pwm
> 				driver supports
> * pwm_pulse()		Tell the PWM to emit a pre-configured number of pulses

Isn't this essentially the same as pwm_enable()? I'd think that if the
PWM is configured to output pulses, then pwm_enable() would simply do
what it's been configured to do (emit the pulses). Why the need for an
additional function?

> * pwm_pulse_done()	an internal function for drivers to call when
> 			they have completed their pre-configured number
> 			of pulses
> * pwm_is_pulsing()	tells the callers if the pwm is busy pulsing,
> 			yielding a little more information than just
> 			pwm_is_enabled()

Similarily, I'd think that once the PWM is done executing the series of
pulses that it was configured for it would be automatically disabled. A
consumer could then simply use pwm_is_enabled() and drivers could call
pwm_disable() on their PWM to mark them as disabled when they're done
pulsing.

> Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
> ---
>  drivers/pwm/core.c      | 30 +++++++++++++++++++----
>  drivers/pwm/pwm-gpio.c  |  3 ++-
>  drivers/pwm/pwm-sun4i.c |  3 ++-
>  drivers/pwm/sysfs.c     | 58 ++++++++++++++++++++++++++++++++++++++++++--
>  include/linux/pwm.h     | 64 ++++++++++++++++++++++++++++++++++++++++++++++---
>  5 files changed, 147 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
> index 3f9df3e..e2c1c0a 100644
> --- a/drivers/pwm/core.c
> +++ b/drivers/pwm/core.c
> @@ -432,22 +432,29 @@ EXPORT_SYMBOL_GPL(pwm_free);
>   * @pwm: PWM device
>   * @duty_ns: "on" time (in nanoseconds)
>   * @period_ns: duration (in nanoseconds) of one cycle
> + * @pulse_count: number of pulses (periods) to output on pwm_pulse
>   *
>   * Returns: 0 on success or a negative error code on failure.
>   */
> -int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
> +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns,
> +	       unsigned int pulse_count)

Like I said, this is problematic because every driver and every consumer
now needs to be aware of pulsing. Once the PWM atomic patches are merged
this will become easier to do because the pulse configuration would be a
part of the atomic state, and hence can be conveniently ignored by users
and driver alike.

Thierry
Olliver Schinagl Nov. 6, 2015, 3:46 p.m. | #7
Hey Thierry,

On 06-11-15 16:18, Thierry Reding wrote:
> On Mon, Oct 26, 2015 at 10:32:39PM +0100, Olliver Schinagl wrote:
>> From: Olliver Schinagl <oliver@schinagl.nl>
>>
>> Some hardware PWM's have the possibility to only send out one (or more)
>> pulses. This can be quite a useful feature in case one wants or needs
>> only a single pulse, but at the exact width.
>>
>> Additionally, if multiple pulses are possible, outputting a fixed amount
>> of pulses can be useful for various timing specific purposes.
> I see how theoretically this would be nice to have. But I'm reluctant to
> merge this feature if there aren't any users. What drivers in the kernel
> would want to use this feature? Are there new drivers being worked on
> that will need this?
I should have brought this up as to why I added this, I'm working on a 
stepper driver framework (inspired by the pwm framework actually) and 
rotating moters by x degree's you do by sending pulses, using controlled 
pulses (timing wise) you can precisely move stepper motors. Yes we can 
do this reasonably accurate in software, but doing it in hardware is so 
much nicer.
>
>> A few new functions have been expanded or added for this new behavior.
>>
>> * pwm_config()	now takes an additional parameter to setup the number of
>> 		pulses to output. The driver may force this to 0 or 1
>> 		for if example if this feature is not or only partially
>> 		supported
> This is problematic because you need to atomically update all drivers
> and users (the kbuild robot already told you that you didn't do this).
> To make things easier I suggest you wait with this change until the
> atomic PWM patches have been merged, at which point it should become a
> lot easier to deal with this kind of extension.
yes, I think i mentioned this in the cover letter, I wanted to get your 
input whilst waiting for Boris's patches. So I deffinatly want to 
combine it then, just getting some head work started :)
>
>> * pwm_[sg]et_pulse_count()	get or set the number of pulses the pwm
>> 				framework is configured for
>> * pwm_get_pulse_count_max()	get the maximum number of pulses the pwm
>> 				driver supports
>> * pwm_pulse()		Tell the PWM to emit a pre-configured number of pulses
> Isn't this essentially the same as pwm_enable()? I'd think that if the
> PWM is configured to output pulses, then pwm_enable() would simply do
> what it's been configured to do (emit the pulses). Why the need for an
> additional function?
pwm_pulse() should be dropped, I think I accidentally left that in the 
documentation, sorry.
>
>> * pwm_pulse_done()	an internal function for drivers to call when
>> 			they have completed their pre-configured number
>> 			of pulses
>> * pwm_is_pulsing()	tells the callers if the pwm is busy pulsing,
>> 			yielding a little more information than just
>> 			pwm_is_enabled()
> Similarily, I'd think that once the PWM is done executing the series of
> pulses that it was configured for it would be automatically disabled. A
> consumer could then simply use pwm_is_enabled() and drivers could call
> pwm_disable() on their PWM to mark them as disabled when they're done
> pulsing.
I agree, pulseating can be dropped too as we know that a) the pulse flag 
is set, b) we are enabled. But I'm not sure now if the flag is exported 
to sysfs, in any case, sysfs should just check the pulseating flag?
>
>> Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
>> ---
>>   drivers/pwm/core.c      | 30 +++++++++++++++++++----
>>   drivers/pwm/pwm-gpio.c  |  3 ++-
>>   drivers/pwm/pwm-sun4i.c |  3 ++-
>>   drivers/pwm/sysfs.c     | 58 ++++++++++++++++++++++++++++++++++++++++++--
>>   include/linux/pwm.h     | 64 ++++++++++++++++++++++++++++++++++++++++++++++---
>>   5 files changed, 147 insertions(+), 11 deletions(-)
>>
>> diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
>> index 3f9df3e..e2c1c0a 100644
>> --- a/drivers/pwm/core.c
>> +++ b/drivers/pwm/core.c
>> @@ -432,22 +432,29 @@ EXPORT_SYMBOL_GPL(pwm_free);
>>    * @pwm: PWM device
>>    * @duty_ns: "on" time (in nanoseconds)
>>    * @period_ns: duration (in nanoseconds) of one cycle
>> + * @pulse_count: number of pulses (periods) to output on pwm_pulse
>>    *
>>    * Returns: 0 on success or a negative error code on failure.
>>    */
>> -int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
>> +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns,
>> +	       unsigned int pulse_count)
> Like I said, this is problematic because every driver and every consumer
> now needs to be aware of pulsing. Once the PWM atomic patches are merged
> this will become easier to do because the pulse configuration would be a
> part of the atomic state, and hence can be conveniently ignored by users
> and driver alike.
I agree :) I'll take your initial comments and work with those so far in 
cleaning stuff up. Feel free to get back to me about the validity of the 
pwm_pulse for steppers generally
> Thierry
Thierry Reding Nov. 6, 2015, 4:05 p.m. | #8
On Fri, Nov 06, 2015 at 04:46:54PM +0100, Olliver Schinagl wrote:
> Hey Thierry,
> 
> On 06-11-15 16:18, Thierry Reding wrote:
> >On Mon, Oct 26, 2015 at 10:32:39PM +0100, Olliver Schinagl wrote:
> >>From: Olliver Schinagl <oliver@schinagl.nl>
> >>
> >>Some hardware PWM's have the possibility to only send out one (or more)
> >>pulses. This can be quite a useful feature in case one wants or needs
> >>only a single pulse, but at the exact width.
> >>
> >>Additionally, if multiple pulses are possible, outputting a fixed amount
> >>of pulses can be useful for various timing specific purposes.
> >I see how theoretically this would be nice to have. But I'm reluctant to
> >merge this feature if there aren't any users. What drivers in the kernel
> >would want to use this feature? Are there new drivers being worked on
> >that will need this?
> I should have brought this up as to why I added this, I'm working on a
> stepper driver framework (inspired by the pwm framework actually) and
> rotating moters by x degree's you do by sending pulses, using controlled
> pulses (timing wise) you can precisely move stepper motors. Yes we can do
> this reasonably accurate in software, but doing it in hardware is so much
> nicer.

So is this going to be a kernel framework for stepper motors? If you say
you rotate the motors by sending pulses, doesn't that mean that the PWM
framework with pulse support would be enough? Or are there dedicated
stepper chips that you plan to support, with PWM + pulses being the
fallback for when you don't have one of those chips?

> >>* pwm_pulse_done()	an internal function for drivers to call when
> >>			they have completed their pre-configured number
> >>			of pulses
> >>* pwm_is_pulsing()	tells the callers if the pwm is busy pulsing,
> >>			yielding a little more information than just
> >>			pwm_is_enabled()
> >Similarily, I'd think that once the PWM is done executing the series of
> >pulses that it was configured for it would be automatically disabled. A
> >consumer could then simply use pwm_is_enabled() and drivers could call
> >pwm_disable() on their PWM to mark them as disabled when they're done
> >pulsing.
> I agree, pulseating can be dropped too as we know that a) the pulse flag is
> set, b) we are enabled. But I'm not sure now if the flag is exported to
> sysfs, in any case, sysfs should just check the pulseating flag?

Can't you derive that information simply by looking at the enable and
pulses attributes? If enable == 1 and pulses > 0 you know the PWM is
pulsing. If enable == 1 and pulses == 0 you know it's in regular mode.

Thierry
Olliver Schinagl Nov. 6, 2015, 4:18 p.m. | #9
Hey Thierry,

On 06-11-15 17:05, Thierry Reding wrote:
> On Fri, Nov 06, 2015 at 04:46:54PM +0100, Olliver Schinagl wrote:
>> Hey Thierry,
>>
>> On 06-11-15 16:18, Thierry Reding wrote:
>>> On Mon, Oct 26, 2015 at 10:32:39PM +0100, Olliver Schinagl wrote:
>>>> From: Olliver Schinagl <oliver@schinagl.nl>
>>>>
>>>> Some hardware PWM's have the possibility to only send out one (or more)
>>>> pulses. This can be quite a useful feature in case one wants or needs
>>>> only a single pulse, but at the exact width.
>>>>
>>>> Additionally, if multiple pulses are possible, outputting a fixed amount
>>>> of pulses can be useful for various timing specific purposes.
>>> I see how theoretically this would be nice to have. But I'm reluctant to
>>> merge this feature if there aren't any users. What drivers in the kernel
>>> would want to use this feature? Are there new drivers being worked on
>>> that will need this?
>> I should have brought this up as to why I added this, I'm working on a
>> stepper driver framework (inspired by the pwm framework actually) and
>> rotating moters by x degree's you do by sending pulses, using controlled
>> pulses (timing wise) you can precisely move stepper motors. Yes we can do
>> this reasonably accurate in software, but doing it in hardware is so much
>> nicer.
> So is this going to be a kernel framework for stepper motors? If you say
> you rotate the motors by sending pulses, doesn't that mean that the PWM
> framework with pulse support would be enough? Or are there dedicated
> stepper chips that you plan to support, with PWM + pulses being the
> fallback for when you don't have one of those chips?
Well I'll have to investigate more into what other chips do, but 
generally speaking from what I know so far, that often you supply a 
stepper driver chip a variable voltage (via a regular pwm) to setup the 
current control, you have gpio's for direction, enable etc, and you 
'pulse' for each step you want the motor to take. There are of course 
some chips that have more logic, that work via i2c and spi interfaces.
>
>>>> * pwm_pulse_done()	an internal function for drivers to call when
>>>> 			they have completed their pre-configured number
>>>> 			of pulses
>>>> * pwm_is_pulsing()	tells the callers if the pwm is busy pulsing,
>>>> 			yielding a little more information than just
>>>> 			pwm_is_enabled()
>>> Similarily, I'd think that once the PWM is done executing the series of
>>> pulses that it was configured for it would be automatically disabled. A
>>> consumer could then simply use pwm_is_enabled() and drivers could call
>>> pwm_disable() on their PWM to mark them as disabled when they're done
>>> pulsing.
>> I agree, pulseating can be dropped too as we know that a) the pulse flag is
>> set, b) we are enabled. But I'm not sure now if the flag is exported to
>> sysfs, in any case, sysfs should just check the pulseating flag?
> Can't you derive that information simply by looking at the enable and
> pulses attributes? If enable == 1 and pulses > 0 you know the PWM is
> pulsing. If enable == 1 and pulses == 0 you know it's in regular mode.
oh right, yes you can :)

olliver
>
> Thierry

Patch

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 3f9df3e..e2c1c0a 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -432,22 +432,29 @@  EXPORT_SYMBOL_GPL(pwm_free);
  * @pwm: PWM device
  * @duty_ns: "on" time (in nanoseconds)
  * @period_ns: duration (in nanoseconds) of one cycle
+ * @pulse_count: number of pulses (periods) to output on pwm_pulse
  *
  * Returns: 0 on success or a negative error code on failure.
  */
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns,
+	       unsigned int pulse_count)
 {
 	int err;
 
 	if (!pwm || duty_ns < 0 || period_ns <= 0 || duty_ns > period_ns)
 		return -EINVAL;
 
-	err = pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns);
+	if (pulse_count > pwm->pulse_count_max)
+		return -EINVAL;
+
+	err = pwm->chip->ops->config(pwm->chip, pwm, duty_ns,
+				     period_ns, pulse_count);
 	if (err)
 		return err;
 
 	pwm->duty_cycle = duty_ns;
 	pwm->period = period_ns;
+	pwm->pulse_count = pulse_count;
 
 	return 0;
 }
@@ -487,6 +494,18 @@  int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity)
 EXPORT_SYMBOL_GPL(pwm_set_polarity);
 
 /**
+ * pwm_pulse_done() - notify the PWM framework that pulse_count pulses are done
+ * @pwm: PWM device
+ */
+void pwm_pulse_done(struct pwm_device *pwm)
+{
+	if (pwm && !test_and_clear_bit(PWMF_ENABLED | PWMF_PULSING,
+				       &pwm->flags))
+		return pwm->chip->ops->disable(pwm->chip, pwm);
+}
+EXPORT_SYMBOL_GPL(pwm_pulse_done);
+
+/**
  * pwm_enable() - start a PWM output toggling
  * @pwm: PWM device
  *
@@ -494,7 +513,9 @@  EXPORT_SYMBOL_GPL(pwm_set_polarity);
  */
 int pwm_enable(struct pwm_device *pwm)
 {
-	if (pwm && !test_and_set_bit(PWMF_ENABLED, &pwm->flags))
+	if (pwm && !test_and_set_bit(
+			PWMF_ENABLED | !pwm->pulse_count ? : PWMF_PULSING,
+			&pwm->flags))
 		return pwm->chip->ops->enable(pwm->chip, pwm);
 
 	return pwm ? 0 : -EINVAL;
@@ -507,7 +528,8 @@  EXPORT_SYMBOL_GPL(pwm_enable);
  */
 void pwm_disable(struct pwm_device *pwm)
 {
-	if (pwm && test_and_clear_bit(PWMF_ENABLED, &pwm->flags))
+	if (pwm && test_and_clear_bit(PWMF_ENABLED | PWMF_PULSING,
+				      &pwm->flags))
 		pwm->chip->ops->disable(pwm->chip, pwm);
 }
 EXPORT_SYMBOL_GPL(pwm_disable);
diff --git a/drivers/pwm/pwm-gpio.c b/drivers/pwm/pwm-gpio.c
index 8b588fb..cf4b170 100644
--- a/drivers/pwm/pwm-gpio.c
+++ b/drivers/pwm/pwm-gpio.c
@@ -84,7 +84,7 @@  enum hrtimer_restart gpio_pwm_timer(struct hrtimer *timer)
 }
 
 static int gpio_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
-			    int duty_ns, int period_ns)
+			    int duty_ns, int period_ns, unsigned int count)
 {
 	struct gpio_pwm_data *gpio_data = pwm_get_chip_data(pwm);
 
@@ -202,6 +202,7 @@  static int gpio_pwm_probe(struct platform_device *pdev)
 			hrtimer++;
 
 		pwm_set_chip_data(&gpio_chip->chip.pwms[i], gpio_data);
+		pwm_set_pulse_count_max(&gpio_chip->chip.pwms[i], UINT_MAX);
 	}
 	if (!hrtimer)
 		dev_warn(&pdev->dev, "unable to use High-Resolution timer,");
diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c
index 4d84d9d..6347ca8 100644
--- a/drivers/pwm/pwm-sun4i.c
+++ b/drivers/pwm/pwm-sun4i.c
@@ -97,7 +97,8 @@  static inline void sun4i_pwm_writel(struct sun4i_pwm_chip *chip,
 }
 
 static int sun4i_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
-			    int duty_ns, int period_ns)
+			    int duty_ns, int period_ns,
+			    unsigned int pulse_count)
 {
 	struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip);
 	u32 prd, dty, val, clk_gate;
diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c
index 9c90886..9b7413c 100644
--- a/drivers/pwm/sysfs.c
+++ b/drivers/pwm/sysfs.c
@@ -61,7 +61,8 @@  static ssize_t period_store(struct device *child,
 	if (ret)
 		return ret;
 
-	ret = pwm_config(pwm, pwm_get_duty_cycle(pwm), val);
+	ret = pwm_config(pwm, pwm_get_duty_cycle(pwm),
+			 val, pwm_get_pulse_count(pwm));
 
 	return ret ? : size;
 }
@@ -87,7 +88,8 @@  static ssize_t duty_cycle_store(struct device *child,
 	if (ret)
 		return ret;
 
-	ret = pwm_config(pwm, val, pwm_get_period(pwm));
+	ret = pwm_config(pwm, val, pwm_get_period(pwm),
+			 pwm_get_pulse_count(pwm));
 
 	return ret ? : size;
 }
@@ -167,16 +169,68 @@  static ssize_t polarity_store(struct device *child,
 	return ret ? : size;
 }
 
+static ssize_t pulse_count_show(struct device *child,
+				struct device_attribute *attr,
+				char *buf)
+{
+	const struct pwm_device *pwm = child_to_pwm_device(child);
+
+	return sprintf(buf, "%u\n", pwm_get_pulse_count(pwm));
+}
+
+static ssize_t pulse_count_store(struct device *child,
+				 struct device_attribute *attr,
+				 const char *buf, size_t size)
+{
+	struct pwm_device *pwm = child_to_pwm_device(child);
+	unsigned int val;
+	int ret;
+
+	ret = kstrtouint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	ret = pwm_config(pwm, pwm_get_duty_cycle(pwm),
+			 pwm_get_period(pwm), val);
+
+	return ret ? : size;
+
+}
+
+static ssize_t pulse_count_max_show(struct device *child,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	const struct pwm_device *pwm = child_to_pwm_device(child);
+
+	return sprintf(buf, "%u\n", pwm_get_pulse_count_max(pwm));
+}
+
+static ssize_t pulsing_show(struct device *child,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	const struct pwm_device *pwm = child_to_pwm_device(child);
+
+	return sprintf(buf, "%d\n", pwm_is_pulsing(pwm));
+}
+
 static DEVICE_ATTR_RW(period);
 static DEVICE_ATTR_RW(duty_cycle);
 static DEVICE_ATTR_RW(enable);
 static DEVICE_ATTR_RW(polarity);
+static DEVICE_ATTR_RW(pulse_count);
+static DEVICE_ATTR_RO(pulse_count_max);
+static DEVICE_ATTR_RO(pulsing);
 
 static struct attribute *pwm_attrs[] = {
 	&dev_attr_period.attr,
 	&dev_attr_duty_cycle.attr,
 	&dev_attr_enable.attr,
 	&dev_attr_polarity.attr,
+	&dev_attr_pulse_count.attr,
+	&dev_attr_pulse_count_max.attr,
+	&dev_attr_pulsing.attr,
 	NULL
 };
 ATTRIBUTE_GROUPS(pwm);
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 29315ad..c6042cf 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -22,7 +22,13 @@  void pwm_free(struct pwm_device *pwm);
 /*
  * pwm_config - change a PWM device configuration
  */
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
+int pwm_config(struct pwm_device *pwm, int duty_ns,
+	       int period_ns, unsigned int pulse_count);
+
+/*
+ * pwm_pulse_done - notify the PWM framework that pulse_count pulses are done
+ */
+void pwm_pulse_done(struct pwm_device *pwm);
 
 /*
  * pwm_enable - start a PWM output toggling
@@ -43,7 +49,8 @@  static inline void pwm_free(struct pwm_device *pwm)
 {
 }
 
-static inline int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
+			     int period_ns, unsigned int pulse_count)
 {
 	return -EINVAL;
 }
@@ -53,6 +60,11 @@  static inline int pwm_enable(struct pwm_device *pwm)
 	return -EINVAL;
 }
 
+static inline int pwm_pulse_done(struct pwm_device *pwm)
+{
+	return -EINVAL;
+}
+
 static inline void pwm_disable(struct pwm_device *pwm)
 {
 }
@@ -78,6 +90,7 @@  enum {
 	PWMF_REQUESTED = BIT(0),
 	PWMF_ENABLED = BIT(1),
 	PWMF_EXPORTED = BIT(2),
+	PWMF_PULSING = BIT(3),
 };
 
 /**
@@ -91,6 +104,8 @@  enum {
  * @period: period of the PWM signal (in nanoseconds)
  * @duty_cycle: duty cycle of the PWM signal (in nanoseconds)
  * @polarity: polarity of the PWM signal
+ * @pulse_count: number of PWM pulses to toggle
+ * @pulse_count_max: maximum number of pulses that can be set to pulse
  */
 struct pwm_device {
 	const char *label;
@@ -103,6 +118,8 @@  struct pwm_device {
 	unsigned int period;
 	unsigned int duty_cycle;
 	enum pwm_polarity polarity;
+	unsigned int pulse_count;
+	unsigned int pulse_count_max;
 };
 
 static inline bool pwm_is_enabled(const struct pwm_device *pwm)
@@ -110,6 +127,11 @@  static inline bool pwm_is_enabled(const struct pwm_device *pwm)
 	return test_bit(PWMF_ENABLED, &pwm->flags);
 }
 
+static inline bool pwm_is_pulsing(const struct pwm_device *pwm)
+{
+	return test_bit(PWMF_ENABLED | PWMF_PULSING, &pwm->flags);
+}
+
 static inline void pwm_set_period(struct pwm_device *pwm, unsigned int period)
 {
 	if (pwm)
@@ -142,6 +164,42 @@  static inline enum pwm_polarity pwm_get_polarity(const struct pwm_device *pwm)
 	return pwm ? pwm->polarity : PWM_POLARITY_NORMAL;
 }
 
+/*
+ * pwm_set_pulse_count - configure the number of pulses of a pwm_pulse
+ */
+static inline void pwm_set_pulse_count(struct pwm_device *pwm,
+				       unsigned int pulse_count)
+{
+	if (pwm)
+		pwm->pulse_count = 0;
+}
+
+/*
+ * pwm_get_pulse_count - retrieve the number of pules to pulse of a pwm_pulse
+ */
+static inline unsigned int pwm_get_pulse_count(const struct pwm_device *pwm)
+{
+	return pwm ? pwm->pulse_count : 0;
+}
+
+/*
+ * pwm_get_pulse_count_max - retrieve the maximum number of pulses
+ */
+static inline unsigned int pwm_get_pulse_count_max(const struct pwm_device *pwm)
+{
+	return pwm ? pwm->pulse_count_max : 0;
+}
+
+/*
+ * pwm_set_pulse_count_max - set the maximum number of pulses
+ */
+static inline void pwm_set_pulse_count_max(struct pwm_device *pwm,
+					   unsigned int pulse_count_max)
+{
+	if (pwm)
+		pwm->pulse_count_max = pulse_count_max;
+}
+
 /**
  * struct pwm_ops - PWM controller operations
  * @request: optional hook for requesting a PWM
@@ -157,7 +215,7 @@  struct pwm_ops {
 	int (*request)(struct pwm_chip *chip, struct pwm_device *pwm);
 	void (*free)(struct pwm_chip *chip, struct pwm_device *pwm);
 	int (*config)(struct pwm_chip *chip, struct pwm_device *pwm,
-		      int duty_ns, int period_ns);
+		      int duty_ns, int period_ns, unsigned int pulse_count);
 	int (*set_polarity)(struct pwm_chip *chip, struct pwm_device *pwm,
 			    enum pwm_polarity polarity);
 	int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm);