2019-05-20 09:19:02 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2012-07-25 16:58:18 +05:30
/*
* ECAP PWM driver
*
2020-07-08 19:59:24 +02:00
* Copyright ( C ) 2012 Texas Instruments , Inc . - https : //www.ti.com/
2012-07-25 16:58:18 +05:30
*/
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/io.h>
# include <linux/err.h>
# include <linux/clk.h>
# include <linux/pm_runtime.h>
# include <linux/pwm.h>
2012-11-27 14:18:09 +05:30
# include <linux/of_device.h>
2012-07-25 16:58:18 +05:30
/* ECAP registers and bits definitions */
# define CAP1 0x08
# define CAP2 0x0C
# define CAP3 0x10
# define CAP4 0x14
# define ECCTL2 0x2A
2012-09-06 10:40:02 +05:30
# define ECCTL2_APWM_POL_LOW BIT(10)
2012-07-25 16:58:18 +05:30
# define ECCTL2_APWM_MODE BIT(9)
# define ECCTL2_SYNC_SEL_DISA (BIT(7) | BIT(6))
# define ECCTL2_TSCTR_FREERUN BIT(4)
2013-01-17 14:50:03 +05:30
struct ecap_context {
2017-08-21 08:29:41 +02:00
u32 cap3 ;
u32 cap4 ;
u16 ecctl2 ;
2013-01-17 14:50:03 +05:30
} ;
2012-07-25 16:58:18 +05:30
struct ecap_pwm_chip {
2017-08-21 08:29:41 +02:00
struct pwm_chip chip ;
unsigned int clk_rate ;
void __iomem * mmio_base ;
2013-01-17 14:50:03 +05:30
struct ecap_context ctx ;
2012-07-25 16:58:18 +05:30
} ;
static inline struct ecap_pwm_chip * to_ecap_pwm_chip ( struct pwm_chip * chip )
{
return container_of ( chip , struct ecap_pwm_chip , chip ) ;
}
/*
* period_ns = 10 ^ 9 * period_cycles / PWM_CLK_RATE
* duty_ns = 10 ^ 9 * duty_cycles / PWM_CLK_RATE
*/
static int ecap_pwm_config ( struct pwm_chip * chip , struct pwm_device * pwm ,
int duty_ns , int period_ns )
{
struct ecap_pwm_chip * pc = to_ecap_pwm_chip ( chip ) ;
2017-08-21 08:29:41 +02:00
u32 period_cycles , duty_cycles ;
2012-07-25 16:58:18 +05:30
unsigned long long c ;
2017-08-21 08:29:41 +02:00
u16 value ;
2012-07-25 16:58:18 +05:30
2012-09-02 22:13:40 +02:00
if ( period_ns > NSEC_PER_SEC )
2012-07-25 16:58:18 +05:30
return - ERANGE ;
c = pc - > clk_rate ;
c = c * period_ns ;
do_div ( c , NSEC_PER_SEC ) ;
2017-08-21 08:29:41 +02:00
period_cycles = ( u32 ) c ;
2012-07-25 16:58:18 +05:30
if ( period_cycles < 1 ) {
period_cycles = 1 ;
duty_cycles = 1 ;
} else {
c = pc - > clk_rate ;
c = c * duty_ns ;
do_div ( c , NSEC_PER_SEC ) ;
2017-08-21 08:29:41 +02:00
duty_cycles = ( u32 ) c ;
2012-07-25 16:58:18 +05:30
}
pm_runtime_get_sync ( pc - > chip . dev ) ;
2017-08-21 08:29:41 +02:00
value = readw ( pc - > mmio_base + ECCTL2 ) ;
2012-07-25 16:58:18 +05:30
/* Configure APWM mode & disable sync option */
2017-08-21 08:29:41 +02:00
value | = ECCTL2_APWM_MODE | ECCTL2_SYNC_SEL_DISA ;
2012-07-25 16:58:18 +05:30
2017-08-21 08:29:41 +02:00
writew ( value , pc - > mmio_base + ECCTL2 ) ;
2012-07-25 16:58:18 +05:30
2015-07-01 10:21:47 +02:00
if ( ! pwm_is_enabled ( pwm ) ) {
2012-07-25 16:58:18 +05:30
/* Update active registers if not running */
writel ( duty_cycles , pc - > mmio_base + CAP2 ) ;
writel ( period_cycles , pc - > mmio_base + CAP1 ) ;
} else {
/*
* Update shadow registers to configure period and
* compare values . This helps current PWM period to
* complete on reconfiguring
*/
writel ( duty_cycles , pc - > mmio_base + CAP4 ) ;
writel ( period_cycles , pc - > mmio_base + CAP3 ) ;
}
2015-07-01 10:21:47 +02:00
if ( ! pwm_is_enabled ( pwm ) ) {
2017-08-21 08:29:41 +02:00
value = readw ( pc - > mmio_base + ECCTL2 ) ;
2012-08-23 12:29:46 +05:30
/* Disable APWM mode to put APWM output Low */
2017-08-21 08:29:41 +02:00
value & = ~ ECCTL2_APWM_MODE ;
writew ( value , pc - > mmio_base + ECCTL2 ) ;
2012-08-23 12:29:46 +05:30
}
2012-07-25 16:58:18 +05:30
pm_runtime_put_sync ( pc - > chip . dev ) ;
2017-08-21 08:29:41 +02:00
2012-07-25 16:58:18 +05:30
return 0 ;
}
2012-09-06 10:40:02 +05:30
static int ecap_pwm_set_polarity ( struct pwm_chip * chip , struct pwm_device * pwm ,
2017-08-21 08:29:41 +02:00
enum pwm_polarity polarity )
2012-09-06 10:40:02 +05:30
{
struct ecap_pwm_chip * pc = to_ecap_pwm_chip ( chip ) ;
2017-08-21 08:29:41 +02:00
u16 value ;
2012-09-06 10:40:02 +05:30
pm_runtime_get_sync ( pc - > chip . dev ) ;
2017-08-21 08:29:41 +02:00
value = readw ( pc - > mmio_base + ECCTL2 ) ;
2012-09-06 10:40:02 +05:30
if ( polarity = = PWM_POLARITY_INVERSED )
/* Duty cycle defines LOW period of PWM */
2017-08-21 08:29:41 +02:00
value | = ECCTL2_APWM_POL_LOW ;
2012-09-06 10:40:02 +05:30
else
/* Duty cycle defines HIGH period of PWM */
2017-08-21 08:29:41 +02:00
value & = ~ ECCTL2_APWM_POL_LOW ;
writew ( value , pc - > mmio_base + ECCTL2 ) ;
2012-09-06 10:40:02 +05:30
pm_runtime_put_sync ( pc - > chip . dev ) ;
2017-08-21 08:29:41 +02:00
2012-09-06 10:40:02 +05:30
return 0 ;
}
2012-07-25 16:58:18 +05:30
static int ecap_pwm_enable ( struct pwm_chip * chip , struct pwm_device * pwm )
{
struct ecap_pwm_chip * pc = to_ecap_pwm_chip ( chip ) ;
2017-08-21 08:29:41 +02:00
u16 value ;
2012-07-25 16:58:18 +05:30
/* Leave clock enabled on enabling PWM */
pm_runtime_get_sync ( pc - > chip . dev ) ;
/*
* Enable ' Free run Time stamp counter mode ' to start counter
* and ' APWM mode ' to enable APWM output
*/
2017-08-21 08:29:41 +02:00
value = readw ( pc - > mmio_base + ECCTL2 ) ;
value | = ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE ;
writew ( value , pc - > mmio_base + ECCTL2 ) ;
2012-07-25 16:58:18 +05:30
return 0 ;
}
static void ecap_pwm_disable ( struct pwm_chip * chip , struct pwm_device * pwm )
{
struct ecap_pwm_chip * pc = to_ecap_pwm_chip ( chip ) ;
2017-08-21 08:29:41 +02:00
u16 value ;
2012-07-25 16:58:18 +05:30
/*
* Disable ' Free run Time stamp counter mode ' to stop counter
* and ' APWM mode ' to put APWM output to low
*/
2017-08-21 08:29:41 +02:00
value = readw ( pc - > mmio_base + ECCTL2 ) ;
value & = ~ ( ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE ) ;
writew ( value , pc - > mmio_base + ECCTL2 ) ;
2012-07-25 16:58:18 +05:30
/* Disable clock on PWM disable */
pm_runtime_put_sync ( pc - > chip . dev ) ;
}
static const struct pwm_ops ecap_pwm_ops = {
2017-08-21 08:29:41 +02:00
. config = ecap_pwm_config ,
. set_polarity = ecap_pwm_set_polarity ,
. enable = ecap_pwm_enable ,
. disable = ecap_pwm_disable ,
. owner = THIS_MODULE ,
2012-07-25 16:58:18 +05:30
} ;
2012-11-27 14:18:09 +05:30
static const struct of_device_id ecap_of_match [ ] = {
2016-05-03 10:56:52 -05:00
{ . compatible = " ti,am3352-ecap " } ,
2012-11-27 14:18:09 +05:30
{ . compatible = " ti,am33xx-ecap " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , ecap_of_match ) ;
2012-11-19 13:23:14 -05:00
static int ecap_pwm_probe ( struct platform_device * pdev )
2012-07-25 16:58:18 +05:30
{
2016-05-03 10:56:52 -05:00
struct device_node * np = pdev - > dev . of_node ;
2017-08-21 08:29:41 +02:00
struct ecap_pwm_chip * pc ;
2012-07-25 16:58:18 +05:30
struct clk * clk ;
2017-08-21 08:29:41 +02:00
int ret ;
2012-07-25 16:58:18 +05:30
pc = devm_kzalloc ( & pdev - > dev , sizeof ( * pc ) , GFP_KERNEL ) ;
2014-04-23 18:41:27 +09:00
if ( ! pc )
2012-07-25 16:58:18 +05:30
return - ENOMEM ;
clk = devm_clk_get ( & pdev - > dev , " fck " ) ;
2016-05-03 10:56:52 -05:00
if ( IS_ERR ( clk ) ) {
if ( of_device_is_compatible ( np , " ti,am33xx-ecap " ) ) {
dev_warn ( & pdev - > dev , " Binding is obsolete. \n " ) ;
clk = devm_clk_get ( pdev - > dev . parent , " fck " ) ;
}
}
2012-07-25 16:58:18 +05:30
if ( IS_ERR ( clk ) ) {
dev_err ( & pdev - > dev , " failed to get clock \n " ) ;
return PTR_ERR ( clk ) ;
}
pc - > clk_rate = clk_get_rate ( clk ) ;
if ( ! pc - > clk_rate ) {
dev_err ( & pdev - > dev , " failed to get clock rate \n " ) ;
return - EINVAL ;
}
pc - > chip . dev = & pdev - > dev ;
pc - > chip . ops = & ecap_pwm_ops ;
pc - > chip . npwm = 1 ;
2019-12-29 08:05:59 +00:00
pc - > mmio_base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2013-01-21 11:09:16 +01:00
if ( IS_ERR ( pc - > mmio_base ) )
return PTR_ERR ( pc - > mmio_base ) ;
2012-07-25 16:58:18 +05:30
ret = pwmchip_add ( & pc - > chip ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " pwmchip_add() failed: %d \n " , ret ) ;
return ret ;
}
2017-08-21 08:31:37 +02:00
platform_set_drvdata ( pdev , pc ) ;
2012-07-25 16:58:18 +05:30
pm_runtime_enable ( & pdev - > dev ) ;
2012-11-27 14:18:09 +05:30
2012-07-25 16:58:18 +05:30
return 0 ;
}
2012-11-19 13:26:09 -05:00
static int ecap_pwm_remove ( struct platform_device * pdev )
2012-07-25 16:58:18 +05:30
{
struct ecap_pwm_chip * pc = platform_get_drvdata ( pdev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
2017-08-21 08:29:41 +02:00
2012-07-25 16:58:18 +05:30
return pwmchip_remove ( & pc - > chip ) ;
}
2013-08-02 15:11:18 +09:00
# ifdef CONFIG_PM_SLEEP
2013-03-26 22:54:58 +08:00
static void ecap_pwm_save_context ( struct ecap_pwm_chip * pc )
2013-01-17 14:50:03 +05:30
{
pm_runtime_get_sync ( pc - > chip . dev ) ;
pc - > ctx . ecctl2 = readw ( pc - > mmio_base + ECCTL2 ) ;
pc - > ctx . cap4 = readl ( pc - > mmio_base + CAP4 ) ;
pc - > ctx . cap3 = readl ( pc - > mmio_base + CAP3 ) ;
pm_runtime_put_sync ( pc - > chip . dev ) ;
}
2013-03-26 22:54:58 +08:00
static void ecap_pwm_restore_context ( struct ecap_pwm_chip * pc )
2013-01-17 14:50:03 +05:30
{
writel ( pc - > ctx . cap3 , pc - > mmio_base + CAP3 ) ;
writel ( pc - > ctx . cap4 , pc - > mmio_base + CAP4 ) ;
writew ( pc - > ctx . ecctl2 , pc - > mmio_base + ECCTL2 ) ;
}
static int ecap_pwm_suspend ( struct device * dev )
{
struct ecap_pwm_chip * pc = dev_get_drvdata ( dev ) ;
struct pwm_device * pwm = pc - > chip . pwms ;
ecap_pwm_save_context ( pc ) ;
/* Disable explicitly if PWM is running */
2015-07-01 10:21:47 +02:00
if ( pwm_is_enabled ( pwm ) )
2013-01-17 14:50:03 +05:30
pm_runtime_put_sync ( dev ) ;
return 0 ;
}
static int ecap_pwm_resume ( struct device * dev )
{
struct ecap_pwm_chip * pc = dev_get_drvdata ( dev ) ;
struct pwm_device * pwm = pc - > chip . pwms ;
/* Enable explicitly if PWM was running */
2015-07-01 10:21:47 +02:00
if ( pwm_is_enabled ( pwm ) )
2013-01-17 14:50:03 +05:30
pm_runtime_get_sync ( dev ) ;
ecap_pwm_restore_context ( pc ) ;
return 0 ;
}
2013-03-11 11:12:58 +09:00
# endif
2013-01-17 14:50:03 +05:30
static SIMPLE_DEV_PM_OPS ( ecap_pwm_pm_ops , ecap_pwm_suspend , ecap_pwm_resume ) ;
2012-07-25 16:58:18 +05:30
static struct platform_driver ecap_pwm_driver = {
. driver = {
2017-08-21 08:29:41 +02:00
. name = " ecap " ,
2012-11-27 14:18:09 +05:30
. of_match_table = ecap_of_match ,
2017-08-21 08:29:41 +02:00
. pm = & ecap_pwm_pm_ops ,
2012-07-25 16:58:18 +05:30
} ,
. probe = ecap_pwm_probe ,
2012-11-19 13:21:28 -05:00
. remove = ecap_pwm_remove ,
2012-07-25 16:58:18 +05:30
} ;
module_platform_driver ( ecap_pwm_driver ) ;
MODULE_DESCRIPTION ( " ECAP PWM driver " ) ;
MODULE_AUTHOR ( " Texas Instruments " ) ;
MODULE_LICENSE ( " GPL " ) ;