diff options
Diffstat (limited to 'drivers/pwm/pwm-stm32.c')
| -rw-r--r-- | drivers/pwm/pwm-stm32.c | 106 | 
1 files changed, 65 insertions, 41 deletions
| diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index 3303a754ea02..5f10cba492ec 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -52,21 +52,6 @@ static u32 active_channels(struct stm32_pwm *dev)  	return ccer & TIM_CCER_CCXE;  } -static int write_ccrx(struct stm32_pwm *dev, int ch, u32 value) -{ -	switch (ch) { -	case 0: -		return regmap_write(dev->regmap, TIM_CCR1, value); -	case 1: -		return regmap_write(dev->regmap, TIM_CCR2, value); -	case 2: -		return regmap_write(dev->regmap, TIM_CCR3, value); -	case 3: -		return regmap_write(dev->regmap, TIM_CCR4, value); -	} -	return -EINVAL; -} -  #define TIM_CCER_CC12P (TIM_CCER_CC1P | TIM_CCER_CC2P)  #define TIM_CCER_CC12E (TIM_CCER_CC1E | TIM_CCER_CC2E)  #define TIM_CCER_CC34P (TIM_CCER_CC3P | TIM_CCER_CC4P) @@ -323,7 +308,7 @@ unlock:  	return ret;  } -static int stm32_pwm_config(struct stm32_pwm *priv, int ch, +static int stm32_pwm_config(struct stm32_pwm *priv, unsigned int ch,  			    int duty_ns, int period_ns)  {  	unsigned long long prd, div, dty; @@ -369,7 +354,7 @@ static int stm32_pwm_config(struct stm32_pwm *priv, int ch,  	dty = prd * duty_ns;  	do_div(dty, period_ns); -	write_ccrx(priv, ch, dty); +	regmap_write(priv->regmap, TIM_CCR1 + 4 * ch, dty);  	/* Configure output mode */  	shift = (ch & 0x1) * CCMR_CHANNEL_SHIFT; @@ -386,7 +371,7 @@ static int stm32_pwm_config(struct stm32_pwm *priv, int ch,  	return 0;  } -static int stm32_pwm_set_polarity(struct stm32_pwm *priv, int ch, +static int stm32_pwm_set_polarity(struct stm32_pwm *priv, unsigned int ch,  				  enum pwm_polarity polarity)  {  	u32 mask; @@ -401,7 +386,7 @@ static int stm32_pwm_set_polarity(struct stm32_pwm *priv, int ch,  	return 0;  } -static int stm32_pwm_enable(struct stm32_pwm *priv, int ch) +static int stm32_pwm_enable(struct stm32_pwm *priv, unsigned int ch)  {  	u32 mask;  	int ret; @@ -426,7 +411,7 @@ static int stm32_pwm_enable(struct stm32_pwm *priv, int ch)  	return 0;  } -static void stm32_pwm_disable(struct stm32_pwm *priv, int ch) +static void stm32_pwm_disable(struct stm32_pwm *priv, unsigned int ch)  {  	u32 mask; @@ -486,8 +471,50 @@ static int stm32_pwm_apply_locked(struct pwm_chip *chip, struct pwm_device *pwm,  	return ret;  } +static int stm32_pwm_get_state(struct pwm_chip *chip, +			       struct pwm_device *pwm, struct pwm_state *state) +{ +	struct stm32_pwm *priv = to_stm32_pwm_dev(chip); +	int ch = pwm->hwpwm; +	unsigned long rate; +	u32 ccer, psc, arr, ccr; +	u64 dty, prd; +	int ret; + +	mutex_lock(&priv->lock); + +	ret = regmap_read(priv->regmap, TIM_CCER, &ccer); +	if (ret) +		goto out; + +	state->enabled = ccer & (TIM_CCER_CC1E << (ch * 4)); +	state->polarity = (ccer & (TIM_CCER_CC1P << (ch * 4))) ? +			  PWM_POLARITY_INVERSED : PWM_POLARITY_NORMAL; +	ret = regmap_read(priv->regmap, TIM_PSC, &psc); +	if (ret) +		goto out; +	ret = regmap_read(priv->regmap, TIM_ARR, &arr); +	if (ret) +		goto out; +	ret = regmap_read(priv->regmap, TIM_CCR1 + 4 * ch, &ccr); +	if (ret) +		goto out; + +	rate = clk_get_rate(priv->clk); + +	prd = (u64)NSEC_PER_SEC * (psc + 1) * (arr + 1); +	state->period = DIV_ROUND_UP_ULL(prd, rate); +	dty = (u64)NSEC_PER_SEC * (psc + 1) * ccr; +	state->duty_cycle = DIV_ROUND_UP_ULL(dty, rate); + +out: +	mutex_unlock(&priv->lock); +	return ret; +} +  static const struct pwm_ops stm32pwm_ops = {  	.apply = stm32_pwm_apply_locked, +	.get_state = stm32_pwm_get_state,  	.capture = IS_ENABLED(CONFIG_DMA_ENGINE) ? stm32_pwm_capture : NULL,  }; @@ -578,32 +605,23 @@ static void stm32_pwm_detect_complementary(struct stm32_pwm *priv)  	priv->have_complementary_output = (ccer != 0);  } -static int stm32_pwm_detect_channels(struct stm32_pwm *priv) +static unsigned int stm32_pwm_detect_channels(struct stm32_pwm *priv, +					      unsigned int *num_enabled)  { -	u32 ccer; -	int npwm = 0; +	u32 ccer, ccer_backup;  	/*  	 * If channels enable bits don't exist writing 1 will have no  	 * effect so we can detect and count them.  	 */ +	regmap_read(priv->regmap, TIM_CCER, &ccer_backup);  	regmap_set_bits(priv->regmap, TIM_CCER, TIM_CCER_CCXE);  	regmap_read(priv->regmap, TIM_CCER, &ccer); -	regmap_clear_bits(priv->regmap, TIM_CCER, TIM_CCER_CCXE); - -	if (ccer & TIM_CCER_CC1E) -		npwm++; - -	if (ccer & TIM_CCER_CC2E) -		npwm++; +	regmap_write(priv->regmap, TIM_CCER, ccer_backup); -	if (ccer & TIM_CCER_CC3E) -		npwm++; +	*num_enabled = hweight32(ccer_backup & TIM_CCER_CCXE); -	if (ccer & TIM_CCER_CC4E) -		npwm++; - -	return npwm; +	return hweight32(ccer & TIM_CCER_CCXE);  }  static int stm32_pwm_probe(struct platform_device *pdev) @@ -612,6 +630,8 @@ static int stm32_pwm_probe(struct platform_device *pdev)  	struct device_node *np = dev->of_node;  	struct stm32_timers *ddata = dev_get_drvdata(pdev->dev.parent);  	struct stm32_pwm *priv; +	unsigned int num_enabled; +	unsigned int i;  	int ret;  	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -634,7 +654,11 @@ static int stm32_pwm_probe(struct platform_device *pdev)  	priv->chip.dev = dev;  	priv->chip.ops = &stm32pwm_ops; -	priv->chip.npwm = stm32_pwm_detect_channels(priv); +	priv->chip.npwm = stm32_pwm_detect_channels(priv, &num_enabled); + +	/* Initialize clock refcount to number of enabled PWM channels. */ +	for (i = 0; i < num_enabled; i++) +		clk_enable(priv->clk);  	ret = devm_pwmchip_add(dev, &priv->chip);  	if (ret < 0) @@ -645,7 +669,7 @@ static int stm32_pwm_probe(struct platform_device *pdev)  	return 0;  } -static int __maybe_unused stm32_pwm_suspend(struct device *dev) +static int stm32_pwm_suspend(struct device *dev)  {  	struct stm32_pwm *priv = dev_get_drvdata(dev);  	unsigned int i; @@ -666,7 +690,7 @@ static int __maybe_unused stm32_pwm_suspend(struct device *dev)  	return pinctrl_pm_select_sleep_state(dev);  } -static int __maybe_unused stm32_pwm_resume(struct device *dev) +static int stm32_pwm_resume(struct device *dev)  {  	struct stm32_pwm *priv = dev_get_drvdata(dev);  	int ret; @@ -679,7 +703,7 @@ static int __maybe_unused stm32_pwm_resume(struct device *dev)  	return stm32_pwm_apply_breakinputs(priv);  } -static SIMPLE_DEV_PM_OPS(stm32_pwm_pm_ops, stm32_pwm_suspend, stm32_pwm_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(stm32_pwm_pm_ops, stm32_pwm_suspend, stm32_pwm_resume);  static const struct of_device_id stm32_pwm_of_match[] = {  	{ .compatible = "st,stm32-pwm",	}, @@ -692,7 +716,7 @@ static struct platform_driver stm32_pwm_driver = {  	.driver	= {  		.name = "stm32-pwm",  		.of_match_table = stm32_pwm_of_match, -		.pm = &stm32_pwm_pm_ops, +		.pm = pm_ptr(&stm32_pwm_pm_ops),  	},  };  module_platform_driver(stm32_pwm_driver); | 
