2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2013-10-16 05:40:03 +04:00
/*
* PWM framework driver for Cirrus Logic EP93xx
*
* Copyright ( c ) 2009 Matthieu Crapet < mcrapet @ gmail . com >
* Copyright ( c ) 2009 , 2013 H Hartley Sweeten < hsweeten @ visionengravers . com >
*
* EP9301 / 02 have only one channel :
* platform device ep93xx - pwm .1 - PWMOUT1 ( EGPIO14 )
*
* EP9307 has only one channel :
* platform device ep93xx - pwm .0 - PWMOUT
*
* EP9312 / 15 have two channels :
* platform device ep93xx - pwm .0 - PWMOUT
* platform device ep93xx - pwm .1 - PWMOUT1 ( EGPIO14 )
*/
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/pwm.h>
# include <asm/div64.h>
2019-04-15 23:17:11 +03:00
# include <linux/soc/cirrus/ep93xx.h> /* for ep93xx_pwm_{acquire,release}_gpio() */
2013-10-16 05:40:03 +04:00
# define EP93XX_PWMx_TERM_COUNT 0x00
# define EP93XX_PWMx_DUTY_CYCLE 0x04
# define EP93XX_PWMx_ENABLE 0x08
# define EP93XX_PWMx_INVERT 0x0c
struct ep93xx_pwm {
void __iomem * base ;
struct clk * clk ;
struct pwm_chip chip ;
} ;
static inline struct ep93xx_pwm * to_ep93xx_pwm ( struct pwm_chip * chip )
{
return container_of ( chip , struct ep93xx_pwm , chip ) ;
}
static int ep93xx_pwm_request ( struct pwm_chip * chip , struct pwm_device * pwm )
{
struct platform_device * pdev = to_platform_device ( chip - > dev ) ;
return ep93xx_pwm_acquire_gpio ( pdev ) ;
}
static void ep93xx_pwm_free ( struct pwm_chip * chip , struct pwm_device * pwm )
{
struct platform_device * pdev = to_platform_device ( chip - > dev ) ;
ep93xx_pwm_release_gpio ( pdev ) ;
}
2021-06-23 17:02:39 +03:00
static int ep93xx_pwm_apply ( struct pwm_chip * chip , struct pwm_device * pwm ,
const struct pwm_state * state )
{
int ret ;
2021-06-23 17:02:40 +03:00
struct ep93xx_pwm * ep93xx_pwm = to_ep93xx_pwm ( chip ) ;
2021-06-23 17:02:39 +03:00
bool enabled = state - > enabled ;
2021-07-01 11:27:55 +03:00
void __iomem * base = ep93xx_pwm - > base ;
unsigned long long c ;
unsigned long period_cycles ;
unsigned long duty_cycles ;
unsigned long term ;
2021-06-23 17:02:39 +03:00
if ( state - > polarity ! = pwm - > state . polarity ) {
if ( enabled ) {
2021-06-23 17:02:40 +03:00
writew ( 0x0 , ep93xx_pwm - > base + EP93XX_PWMx_ENABLE ) ;
2021-06-14 02:30:41 +03:00
clk_disable_unprepare ( ep93xx_pwm - > clk ) ;
2021-06-23 17:02:39 +03:00
enabled = false ;
}
2021-06-23 17:02:40 +03:00
/*
* The clock needs to be enabled to access the PWM registers .
* Polarity can only be changed when the PWM is disabled .
*/
2021-06-14 02:30:41 +03:00
ret = clk_prepare_enable ( ep93xx_pwm - > clk ) ;
2021-06-23 17:02:39 +03:00
if ( ret )
return ret ;
2021-06-23 17:02:40 +03:00
if ( state - > polarity = = PWM_POLARITY_INVERSED )
writew ( 0x1 , ep93xx_pwm - > base + EP93XX_PWMx_INVERT ) ;
else
writew ( 0x0 , ep93xx_pwm - > base + EP93XX_PWMx_INVERT ) ;
2021-06-14 02:30:41 +03:00
clk_disable_unprepare ( ep93xx_pwm - > clk ) ;
2021-06-23 17:02:39 +03:00
}
if ( ! state - > enabled ) {
2021-06-23 17:02:40 +03:00
if ( enabled ) {
writew ( 0x0 , ep93xx_pwm - > base + EP93XX_PWMx_ENABLE ) ;
2021-06-14 02:30:41 +03:00
clk_disable_unprepare ( ep93xx_pwm - > clk ) ;
2021-06-23 17:02:40 +03:00
}
2021-06-23 17:02:39 +03:00
return 0 ;
}
2021-07-01 11:27:55 +03:00
/*
* The clock needs to be enabled to access the PWM registers .
* Configuration can be changed at any time .
*/
if ( ! pwm_is_enabled ( pwm ) ) {
ret = clk_prepare_enable ( ep93xx_pwm - > clk ) ;
if ( ret )
return ret ;
}
2021-06-23 17:02:40 +03:00
2021-07-01 11:27:55 +03:00
c = clk_get_rate ( ep93xx_pwm - > clk ) ;
c * = state - > period ;
do_div ( c , 1000000000 ) ;
period_cycles = c ;
2021-06-23 17:02:40 +03:00
2021-07-01 11:27:55 +03:00
c = period_cycles ;
c * = state - > duty_cycle ;
do_div ( c , state - > period ) ;
duty_cycles = c ;
if ( period_cycles < 0x10000 & & duty_cycles < 0x10000 ) {
term = readw ( base + EP93XX_PWMx_TERM_COUNT ) ;
/* Order is important if PWM is running */
if ( period_cycles > term ) {
writew ( period_cycles , base + EP93XX_PWMx_TERM_COUNT ) ;
writew ( duty_cycles , base + EP93XX_PWMx_DUTY_CYCLE ) ;
2021-06-23 17:02:40 +03:00
} else {
2021-07-01 11:27:55 +03:00
writew ( duty_cycles , base + EP93XX_PWMx_DUTY_CYCLE ) ;
writew ( period_cycles , base + EP93XX_PWMx_TERM_COUNT ) ;
2021-06-23 17:02:40 +03:00
}
2021-07-01 11:27:55 +03:00
ret = 0 ;
} else {
ret = - EINVAL ;
}
2021-06-23 17:02:40 +03:00
2021-07-01 11:27:55 +03:00
if ( ! pwm_is_enabled ( pwm ) )
clk_disable_unprepare ( ep93xx_pwm - > clk ) ;
2021-06-23 17:02:40 +03:00
2021-07-01 11:27:55 +03:00
if ( ret )
return ret ;
2021-06-23 17:02:39 +03:00
2021-06-23 17:02:40 +03:00
if ( ! enabled ) {
2021-06-14 02:30:41 +03:00
ret = clk_prepare_enable ( ep93xx_pwm - > clk ) ;
2021-06-23 17:02:40 +03:00
if ( ret )
return ret ;
writew ( 0x1 , ep93xx_pwm - > base + EP93XX_PWMx_ENABLE ) ;
}
2021-06-23 17:02:39 +03:00
return 0 ;
}
2013-10-16 05:40:03 +04:00
static const struct pwm_ops ep93xx_pwm_ops = {
. request = ep93xx_pwm_request ,
. free = ep93xx_pwm_free ,
2021-06-23 17:02:39 +03:00
. apply = ep93xx_pwm_apply ,
2013-10-16 05:40:03 +04:00
. owner = THIS_MODULE ,
} ;
static int ep93xx_pwm_probe ( struct platform_device * pdev )
{
struct ep93xx_pwm * ep93xx_pwm ;
int ret ;
ep93xx_pwm = devm_kzalloc ( & pdev - > dev , sizeof ( * ep93xx_pwm ) , GFP_KERNEL ) ;
if ( ! ep93xx_pwm )
return - ENOMEM ;
2019-12-29 11:05:43 +03:00
ep93xx_pwm - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2013-10-16 05:40:03 +04:00
if ( IS_ERR ( ep93xx_pwm - > base ) )
return PTR_ERR ( ep93xx_pwm - > base ) ;
ep93xx_pwm - > clk = devm_clk_get ( & pdev - > dev , " pwm_clk " ) ;
if ( IS_ERR ( ep93xx_pwm - > clk ) )
return PTR_ERR ( ep93xx_pwm - > clk ) ;
ep93xx_pwm - > chip . dev = & pdev - > dev ;
ep93xx_pwm - > chip . ops = & ep93xx_pwm_ops ;
ep93xx_pwm - > chip . npwm = 1 ;
2021-07-07 19:28:02 +03:00
ret = devm_pwmchip_add ( & pdev - > dev , & ep93xx_pwm - > chip ) ;
2013-10-16 05:40:03 +04:00
if ( ret < 0 )
return ret ;
return 0 ;
}
static struct platform_driver ep93xx_pwm_driver = {
. driver = {
. name = " ep93xx-pwm " ,
} ,
. probe = ep93xx_pwm_probe ,
} ;
module_platform_driver ( ep93xx_pwm_driver ) ;
MODULE_DESCRIPTION ( " Cirrus Logic EP93xx PWM driver " ) ;
2013-12-19 08:32:45 +04:00
MODULE_AUTHOR ( " Matthieu Crapet <mcrapet@gmail.com> " ) ;
MODULE_AUTHOR ( " H Hartley Sweeten <hsweeten@visionengravers.com> " ) ;
2013-10-16 05:40:03 +04:00
MODULE_ALIAS ( " platform:ep93xx-pwm " ) ;
MODULE_LICENSE ( " GPL " ) ;