2017-12-05 17:57:21 +03:00
// SPDX-License-Identifier: GPL-2.0
2017-08-28 13:04:09 +03:00
/*
* STM32 Low - Power Timer PWM driver
*
* Copyright ( C ) STMicroelectronics 2017
*
* Author : Gerald Baeza < gerald . baeza @ st . com >
*
* Inspired by Gerald Baeza ' s pwm - stm32 driver
*/
# include <linux/bitfield.h>
# include <linux/mfd/stm32-lptimer.h>
# include <linux/module.h>
# include <linux/of.h>
2019-04-18 12:37:46 +03:00
# include <linux/pinctrl/consumer.h>
2017-08-28 13:04:09 +03:00
# include <linux/platform_device.h>
# include <linux/pwm.h>
struct stm32_pwm_lp {
struct pwm_chip chip ;
struct clk * clk ;
struct regmap * regmap ;
} ;
static inline struct stm32_pwm_lp * to_stm32_pwm_lp ( struct pwm_chip * chip )
{
return container_of ( chip , struct stm32_pwm_lp , chip ) ;
}
/* STM32 Low-Power Timer is preceded by a configurable power-of-2 prescaler */
# define STM32_LPTIM_MAX_PRESCALER 128
static int stm32_pwm_lp_apply ( struct pwm_chip * chip , struct pwm_device * pwm ,
2019-08-24 18:37:07 +03:00
const struct pwm_state * state )
2017-08-28 13:04:09 +03:00
{
struct stm32_pwm_lp * priv = to_stm32_pwm_lp ( chip ) ;
unsigned long long prd , div , dty ;
struct pwm_state cstate ;
u32 val , mask , cfgr , presc = 0 ;
bool reenable ;
int ret ;
pwm_get_state ( pwm , & cstate ) ;
reenable = ! cstate . enabled ;
if ( ! state - > enabled ) {
if ( cstate . enabled ) {
/* Disable LP timer */
ret = regmap_write ( priv - > regmap , STM32_LPTIM_CR , 0 ) ;
if ( ret )
return ret ;
/* disable clock to PWM counter */
clk_disable ( priv - > clk ) ;
}
return 0 ;
}
/* Calculate the period and prescaler value */
div = ( unsigned long long ) clk_get_rate ( priv - > clk ) * state - > period ;
do_div ( div , NSEC_PER_SEC ) ;
2019-09-18 17:54:21 +03:00
if ( ! div ) {
/* Clock is too slow to achieve requested period. */
2020-06-03 01:31:16 +03:00
dev_dbg ( priv - > chip . dev , " Can't reach %llu ns \n " , state - > period ) ;
2019-09-18 17:54:21 +03:00
return - EINVAL ;
}
2017-08-28 13:04:09 +03:00
prd = div ;
while ( div > STM32_LPTIM_MAX_ARR ) {
presc + + ;
if ( ( 1 < < presc ) > STM32_LPTIM_MAX_PRESCALER ) {
dev_err ( priv - > chip . dev , " max prescaler exceeded \n " ) ;
return - EINVAL ;
}
div = prd > > presc ;
}
prd = div ;
/* Calculate the duty cycle */
dty = prd * state - > duty_cycle ;
do_div ( dty , state - > period ) ;
if ( ! cstate . enabled ) {
/* enable clock to drive PWM counter */
ret = clk_enable ( priv - > clk ) ;
if ( ret )
return ret ;
}
ret = regmap_read ( priv - > regmap , STM32_LPTIM_CFGR , & cfgr ) ;
if ( ret )
goto err ;
if ( ( FIELD_GET ( STM32_LPTIM_PRESC , cfgr ) ! = presc ) | |
( FIELD_GET ( STM32_LPTIM_WAVPOL , cfgr ) ! = state - > polarity ) ) {
val = FIELD_PREP ( STM32_LPTIM_PRESC , presc ) ;
val | = FIELD_PREP ( STM32_LPTIM_WAVPOL , state - > polarity ) ;
mask = STM32_LPTIM_PRESC | STM32_LPTIM_WAVPOL ;
/* Must disable LP timer to modify CFGR */
reenable = true ;
ret = regmap_write ( priv - > regmap , STM32_LPTIM_CR , 0 ) ;
if ( ret )
goto err ;
ret = regmap_update_bits ( priv - > regmap , STM32_LPTIM_CFGR , mask ,
val ) ;
if ( ret )
goto err ;
}
if ( reenable ) {
/* Must (re)enable LP timer to modify CMP & ARR */
ret = regmap_write ( priv - > regmap , STM32_LPTIM_CR ,
STM32_LPTIM_ENABLE ) ;
if ( ret )
goto err ;
}
ret = regmap_write ( priv - > regmap , STM32_LPTIM_ARR , prd - 1 ) ;
if ( ret )
goto err ;
ret = regmap_write ( priv - > regmap , STM32_LPTIM_CMP , prd - ( 1 + dty ) ) ;
if ( ret )
goto err ;
/* ensure CMP & ARR registers are properly written */
ret = regmap_read_poll_timeout ( priv - > regmap , STM32_LPTIM_ISR , val ,
( val & STM32_LPTIM_CMPOK_ARROK ) ,
100 , 1000 ) ;
if ( ret ) {
dev_err ( priv - > chip . dev , " ARR/CMP registers write issue \n " ) ;
goto err ;
}
ret = regmap_write ( priv - > regmap , STM32_LPTIM_ICR ,
STM32_LPTIM_CMPOKCF_ARROKCF ) ;
if ( ret )
goto err ;
if ( reenable ) {
/* Start LP timer in continuous mode */
ret = regmap_update_bits ( priv - > regmap , STM32_LPTIM_CR ,
STM32_LPTIM_CNTSTRT ,
STM32_LPTIM_CNTSTRT ) ;
if ( ret ) {
regmap_write ( priv - > regmap , STM32_LPTIM_CR , 0 ) ;
goto err ;
}
}
return 0 ;
err :
if ( ! cstate . enabled )
clk_disable ( priv - > clk ) ;
return ret ;
}
static void stm32_pwm_lp_get_state ( struct pwm_chip * chip ,
struct pwm_device * pwm ,
struct pwm_state * state )
{
struct stm32_pwm_lp * priv = to_stm32_pwm_lp ( chip ) ;
unsigned long rate = clk_get_rate ( priv - > clk ) ;
u32 val , presc , prd ;
u64 tmp ;
regmap_read ( priv - > regmap , STM32_LPTIM_CR , & val ) ;
state - > enabled = ! ! FIELD_GET ( STM32_LPTIM_ENABLE , val ) ;
/* Keep PWM counter clock refcount in sync with PWM initial state */
if ( state - > enabled )
clk_enable ( priv - > clk ) ;
regmap_read ( priv - > regmap , STM32_LPTIM_CFGR , & val ) ;
presc = FIELD_GET ( STM32_LPTIM_PRESC , val ) ;
state - > polarity = FIELD_GET ( STM32_LPTIM_WAVPOL , val ) ;
regmap_read ( priv - > regmap , STM32_LPTIM_ARR , & prd ) ;
tmp = prd + 1 ;
tmp = ( tmp < < presc ) * NSEC_PER_SEC ;
state - > period = DIV_ROUND_CLOSEST_ULL ( tmp , rate ) ;
regmap_read ( priv - > regmap , STM32_LPTIM_CMP , & val ) ;
tmp = prd - val ;
tmp = ( tmp < < presc ) * NSEC_PER_SEC ;
state - > duty_cycle = DIV_ROUND_CLOSEST_ULL ( tmp , rate ) ;
}
static const struct pwm_ops stm32_pwm_lp_ops = {
. owner = THIS_MODULE ,
. apply = stm32_pwm_lp_apply ,
. get_state = stm32_pwm_lp_get_state ,
} ;
static int stm32_pwm_lp_probe ( struct platform_device * pdev )
{
struct stm32_lptimer * ddata = dev_get_drvdata ( pdev - > dev . parent ) ;
struct stm32_pwm_lp * priv ;
int ret ;
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > regmap = ddata - > regmap ;
priv - > clk = ddata - > clk ;
priv - > chip . dev = & pdev - > dev ;
priv - > chip . ops = & stm32_pwm_lp_ops ;
priv - > chip . npwm = 1 ;
ret = pwmchip_add ( & priv - > chip ) ;
if ( ret < 0 )
return ret ;
platform_set_drvdata ( pdev , priv ) ;
return 0 ;
}
static int stm32_pwm_lp_remove ( struct platform_device * pdev )
{
struct stm32_pwm_lp * priv = platform_get_drvdata ( pdev ) ;
return pwmchip_remove ( & priv - > chip ) ;
}
2019-04-18 12:37:46 +03:00
static int __maybe_unused stm32_pwm_lp_suspend ( struct device * dev )
{
struct stm32_pwm_lp * priv = dev_get_drvdata ( dev ) ;
struct pwm_state state ;
pwm_get_state ( & priv - > chip . pwms [ 0 ] , & state ) ;
if ( state . enabled ) {
dev_err ( dev , " The consumer didn't stop us (%s) \n " ,
priv - > chip . pwms [ 0 ] . label ) ;
return - EBUSY ;
}
return pinctrl_pm_select_sleep_state ( dev ) ;
}
static int __maybe_unused stm32_pwm_lp_resume ( struct device * dev )
{
return pinctrl_pm_select_default_state ( dev ) ;
}
static SIMPLE_DEV_PM_OPS ( stm32_pwm_lp_pm_ops , stm32_pwm_lp_suspend ,
stm32_pwm_lp_resume ) ;
2017-08-28 13:04:09 +03:00
static const struct of_device_id stm32_pwm_lp_of_match [ ] = {
{ . compatible = " st,stm32-pwm-lp " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , stm32_pwm_lp_of_match ) ;
static struct platform_driver stm32_pwm_lp_driver = {
. probe = stm32_pwm_lp_probe ,
. remove = stm32_pwm_lp_remove ,
. driver = {
. name = " stm32-pwm-lp " ,
. of_match_table = of_match_ptr ( stm32_pwm_lp_of_match ) ,
2019-04-18 12:37:46 +03:00
. pm = & stm32_pwm_lp_pm_ops ,
2017-08-28 13:04:09 +03:00
} ,
} ;
module_platform_driver ( stm32_pwm_lp_driver ) ;
MODULE_ALIAS ( " platform:stm32-pwm-lp " ) ;
MODULE_DESCRIPTION ( " STMicroelectronics STM32 PWM LP driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;