2018-07-06 19:47:17 -03:00
// SPDX-License-Identifier: GPL-2.0
2009-01-16 15:17:16 +01:00
/*
* simple driver for PWM ( Pulse Width Modulator ) controller
*
* Derived from pxa PWM driver by eric miao < eric . miao @ marvell . com >
2019-07-30 14:45:27 +02:00
*
* Limitations :
* - When disabled the output is driven to 0 independent of the configured
* polarity .
2009-01-16 15:17:16 +01:00
*/
2018-10-01 16:19:47 +02:00
# include <linux/bitfield.h>
# include <linux/bitops.h>
2009-01-16 15:17:16 +01:00
# include <linux/clk.h>
2014-05-28 18:50:13 +08:00
# include <linux/delay.h>
2018-10-01 16:19:46 +02:00
# include <linux/err.h>
2009-01-16 15:17:16 +01:00
# include <linux/io.h>
2018-10-01 16:19:46 +02:00
# include <linux/kernel.h>
# include <linux/module.h>
2013-09-27 16:53:23 +05:30
# include <linux/of.h>
2018-10-01 16:19:46 +02:00
# include <linux/platform_device.h>
# include <linux/pwm.h>
# include <linux/slab.h>
2009-04-14 12:50:20 +02:00
2014-05-28 18:50:12 +08:00
# define MX3_PWMCR 0x00 /* PWM Control Register */
2014-05-28 18:50:13 +08:00
# define MX3_PWMSR 0x04 /* PWM Status Register */
2014-05-28 18:50:12 +08:00
# define MX3_PWMSAR 0x0C /* PWM Sample Register */
# define MX3_PWMPR 0x10 /* PWM Period Register */
2018-10-01 16:19:47 +02:00
# define MX3_PWMCR_FWM GENMASK(27, 26)
# define MX3_PWMCR_STOPEN BIT(25)
# define MX3_PWMCR_DOZEN BIT(24)
# define MX3_PWMCR_WAITEN BIT(23)
# define MX3_PWMCR_DBGEN BIT(22)
# define MX3_PWMCR_BCTR BIT(21)
# define MX3_PWMCR_HCTR BIT(20)
# define MX3_PWMCR_POUTC GENMASK(19, 18)
# define MX3_PWMCR_POUTC_NORMAL 0
# define MX3_PWMCR_POUTC_INVERTED 1
# define MX3_PWMCR_POUTC_OFF 2
# define MX3_PWMCR_CLKSRC GENMASK(17, 16)
# define MX3_PWMCR_CLKSRC_OFF 0
# define MX3_PWMCR_CLKSRC_IPG 1
# define MX3_PWMCR_CLKSRC_IPG_HIGH 2
# define MX3_PWMCR_CLKSRC_IPG_32K 3
# define MX3_PWMCR_PRESCALER GENMASK(15, 4)
# define MX3_PWMCR_SWR BIT(3)
# define MX3_PWMCR_REPEAT GENMASK(2, 1)
# define MX3_PWMCR_REPEAT_1X 0
# define MX3_PWMCR_REPEAT_2X 1
# define MX3_PWMCR_REPEAT_4X 2
# define MX3_PWMCR_REPEAT_8X 3
# define MX3_PWMCR_EN BIT(0)
# define MX3_PWMSR_FWE BIT(6)
# define MX3_PWMSR_CMP BIT(5)
# define MX3_PWMSR_ROV BIT(4)
# define MX3_PWMSR_FE BIT(3)
# define MX3_PWMSR_FIFOAV GENMASK(2, 0)
# define MX3_PWMSR_FIFOAV_EMPTY 0
# define MX3_PWMSR_FIFOAV_1WORD 1
# define MX3_PWMSR_FIFOAV_2WORDS 2
# define MX3_PWMSR_FIFOAV_3WORDS 3
# define MX3_PWMSR_FIFOAV_4WORDS 4
# define MX3_PWMCR_PRESCALER_SET(x) FIELD_PREP(MX3_PWMCR_PRESCALER, (x) - 1)
# define MX3_PWMCR_PRESCALER_GET(x) (FIELD_GET(MX3_PWMCR_PRESCALER, \
( x ) ) + 1 )
2014-05-28 18:50:13 +08:00
# define MX3_PWM_SWR_LOOP 5
2009-04-14 12:50:20 +02:00
2018-10-01 16:19:48 +02:00
/* PWMPR register value of 0xffff has the same effect as 0xfffe */
# define MX3_PWMPR_MAX 0xfffe
2019-01-07 20:53:52 +01:00
struct pwm_imx27_chip {
2018-12-19 05:24:58 +00:00
struct clk * clk_ipg ;
2012-06-25 16:15:20 +02:00
struct clk * clk_per ;
2009-01-16 15:17:16 +01:00
void __iomem * mmio_base ;
2012-03-15 10:04:35 +01:00
struct pwm_chip chip ;
2019-10-17 12:56:00 +02:00
/*
* The driver cannot read the current duty cycle from the hardware if
* the hardware is disabled . Cache the last programmed duty cycle
* value to return in that case .
*/
unsigned int duty_cycle ;
2009-01-16 15:17:16 +01:00
} ;
2019-01-07 20:53:52 +01:00
# define to_pwm_imx27_chip(chip) container_of(chip, struct pwm_imx27_chip, chip)
2012-03-15 10:04:35 +01:00
2020-02-10 22:22:38 +01:00
static int pwm_imx27_clk_prepare_enable ( struct pwm_imx27_chip * imx )
2018-12-19 05:24:58 +00:00
{
int ret ;
ret = clk_prepare_enable ( imx - > clk_ipg ) ;
if ( ret )
return ret ;
ret = clk_prepare_enable ( imx - > clk_per ) ;
if ( ret ) {
clk_disable_unprepare ( imx - > clk_ipg ) ;
return ret ;
}
return 0 ;
}
2020-02-10 22:22:38 +01:00
static void pwm_imx27_clk_disable_unprepare ( struct pwm_imx27_chip * imx )
2018-12-19 05:24:58 +00:00
{
clk_disable_unprepare ( imx - > clk_per ) ;
clk_disable_unprepare ( imx - > clk_ipg ) ;
}
2022-12-02 19:35:26 +01:00
static int pwm_imx27_get_state ( struct pwm_chip * chip ,
struct pwm_device * pwm , struct pwm_state * state )
2018-10-01 16:19:48 +02:00
{
2019-01-07 20:53:52 +01:00
struct pwm_imx27_chip * imx = to_pwm_imx27_chip ( chip ) ;
2019-01-09 11:27:47 +03:00
u32 period , prescaler , pwm_clk , val ;
2018-10-01 16:19:48 +02:00
u64 tmp ;
2019-01-09 11:27:47 +03:00
int ret ;
2018-10-01 16:19:48 +02:00
2020-02-10 22:22:38 +01:00
ret = pwm_imx27_clk_prepare_enable ( imx ) ;
2018-12-19 05:24:58 +00:00
if ( ret < 0 )
2022-12-02 19:35:32 +01:00
return ret ;
2018-12-19 05:24:58 +00:00
2018-10-01 16:19:48 +02:00
val = readl ( imx - > mmio_base + MX3_PWMCR ) ;
2019-01-10 20:33:53 +01:00
if ( val & MX3_PWMCR_EN )
2018-10-01 16:19:48 +02:00
state - > enabled = true ;
2019-01-10 20:33:53 +01:00
else
2018-10-01 16:19:48 +02:00
state - > enabled = false ;
switch ( FIELD_GET ( MX3_PWMCR_POUTC , val ) ) {
case MX3_PWMCR_POUTC_NORMAL :
state - > polarity = PWM_POLARITY_NORMAL ;
break ;
case MX3_PWMCR_POUTC_INVERTED :
state - > polarity = PWM_POLARITY_INVERSED ;
break ;
default :
dev_warn ( chip - > dev , " can't set polarity, output disconnected " ) ;
}
prescaler = MX3_PWMCR_PRESCALER_GET ( val ) ;
pwm_clk = clk_get_rate ( imx - > clk_per ) ;
val = readl ( imx - > mmio_base + MX3_PWMPR ) ;
period = val > = MX3_PWMPR_MAX ? MX3_PWMPR_MAX : val ;
/* PWMOUT (Hz) = PWMCLK / (PWMPR + 2) */
2020-04-16 10:02:45 +02:00
tmp = NSEC_PER_SEC * ( u64 ) ( period + 2 ) * prescaler ;
state - > period = DIV_ROUND_UP_ULL ( tmp , pwm_clk ) ;
2018-10-01 16:19:48 +02:00
2019-10-17 12:56:00 +02:00
/*
* PWMSAR can be read only if PWM is enabled . If the PWM is disabled ,
* use the cached value .
*/
if ( state - > enabled )
2018-10-01 16:19:48 +02:00
val = readl ( imx - > mmio_base + MX3_PWMSAR ) ;
2019-10-17 12:56:00 +02:00
else
val = imx - > duty_cycle ;
2020-04-16 10:02:45 +02:00
tmp = NSEC_PER_SEC * ( u64 ) ( val ) * prescaler ;
state - > duty_cycle = DIV_ROUND_UP_ULL ( tmp , pwm_clk ) ;
2018-12-19 05:24:58 +00:00
2020-02-10 22:22:40 +01:00
pwm_imx27_clk_disable_unprepare ( imx ) ;
2022-12-02 19:35:26 +01:00
return 0 ;
2012-08-28 11:39:25 +02:00
}
2019-01-07 20:53:52 +01:00
static void pwm_imx27_sw_reset ( struct pwm_chip * chip )
2012-07-03 17:28:14 +02:00
{
2019-01-07 20:53:52 +01:00
struct pwm_imx27_chip * imx = to_pwm_imx27_chip ( chip ) ;
2017-01-29 22:54:09 +01:00
struct device * dev = chip - > dev ;
int wait_count = 0 ;
u32 cr ;
writel ( MX3_PWMCR_SWR , imx - > mmio_base + MX3_PWMCR ) ;
do {
usleep_range ( 200 , 1000 ) ;
cr = readl ( imx - > mmio_base + MX3_PWMCR ) ;
} while ( ( cr & MX3_PWMCR_SWR ) & &
( wait_count + + < MX3_PWM_SWR_LOOP ) ) ;
if ( cr & MX3_PWMCR_SWR )
dev_warn ( dev , " software reset timeout \n " ) ;
}
2012-07-03 17:28:14 +02:00
2019-01-07 20:53:52 +01:00
static void pwm_imx27_wait_fifo_slot ( struct pwm_chip * chip ,
struct pwm_device * pwm )
2017-01-29 22:54:10 +01:00
{
2019-01-07 20:53:52 +01:00
struct pwm_imx27_chip * imx = to_pwm_imx27_chip ( chip ) ;
2017-01-29 22:54:10 +01:00
struct device * dev = chip - > dev ;
unsigned int period_ms ;
int fifoav ;
u32 sr ;
2012-06-25 16:15:20 +02:00
2017-01-29 22:54:10 +01:00
sr = readl ( imx - > mmio_base + MX3_PWMSR ) ;
2018-10-01 16:19:47 +02:00
fifoav = FIELD_GET ( MX3_PWMSR_FIFOAV , sr ) ;
2017-01-29 22:54:10 +01:00
if ( fifoav = = MX3_PWMSR_FIFOAV_4WORDS ) {
2020-06-02 15:31:11 -07:00
period_ms = DIV_ROUND_UP_ULL ( pwm_get_period ( pwm ) ,
2017-01-29 22:54:10 +01:00
NSEC_PER_MSEC ) ;
msleep ( period_ms ) ;
2012-06-25 16:15:20 +02:00
2017-01-29 22:54:10 +01:00
sr = readl ( imx - > mmio_base + MX3_PWMSR ) ;
2018-10-01 16:19:47 +02:00
if ( fifoav = = FIELD_GET ( MX3_PWMSR_FIFOAV , sr ) )
2017-01-29 22:54:10 +01:00
dev_warn ( dev , " there is no free FIFO slot \n " ) ;
}
2012-07-03 17:28:14 +02:00
}
2019-01-07 20:53:52 +01:00
static int pwm_imx27_apply ( struct pwm_chip * chip , struct pwm_device * pwm ,
2019-08-24 17:37:07 +02:00
const struct pwm_state * state )
2009-01-16 15:17:16 +01:00
{
2017-01-29 22:54:11 +01:00
unsigned long period_cycles , duty_cycles , prescale ;
2019-01-07 20:53:52 +01:00
struct pwm_imx27_chip * imx = to_pwm_imx27_chip ( chip ) ;
2017-01-29 22:54:11 +01:00
struct pwm_state cstate ;
unsigned long long c ;
2020-04-16 10:02:45 +02:00
unsigned long long clkrate ;
2012-08-28 09:12:01 +02:00
int ret ;
2017-01-29 22:54:15 +01:00
u32 cr ;
2017-01-29 22:54:11 +01:00
pwm_get_state ( pwm , & cstate ) ;
2020-04-16 10:02:45 +02:00
clkrate = clk_get_rate ( imx - > clk_per ) ;
c = clkrate * state - > period ;
2009-01-16 15:17:16 +01:00
2020-04-16 10:02:45 +02:00
do_div ( c , NSEC_PER_SEC ) ;
2019-10-17 17:11:41 +02:00
period_cycles = c ;
prescale = period_cycles / 0x10000 + 1 ;
period_cycles / = prescale ;
2020-04-16 10:02:45 +02:00
c = clkrate * state - > duty_cycle ;
2020-12-07 15:13:24 +01:00
do_div ( c , NSEC_PER_SEC ) ;
2019-10-17 17:11:41 +02:00
duty_cycles = c ;
2020-12-07 15:13:24 +01:00
duty_cycles / = prescale ;
2019-10-17 17:11:41 +02:00
/*
* according to imx pwm RM , the real period value should be PERIOD
* value in PWMPR plus 2.
*/
if ( period_cycles > 2 )
period_cycles - = 2 ;
else
period_cycles = 0 ;
/*
* Wait for a free FIFO slot if the PWM is already enabled , and flush
* the FIFO if the PWM was disabled and is about to be enabled .
*/
if ( cstate . enabled ) {
pwm_imx27_wait_fifo_slot ( chip , pwm ) ;
} else {
2020-02-10 22:22:38 +01:00
ret = pwm_imx27_clk_prepare_enable ( imx ) ;
2019-10-17 17:11:41 +02:00
if ( ret )
return ret ;
pwm_imx27_sw_reset ( chip ) ;
2017-01-29 22:54:11 +01:00
}
2009-01-16 15:17:16 +01:00
2019-10-17 17:11:41 +02:00
writel ( duty_cycles , imx - > mmio_base + MX3_PWMSAR ) ;
writel ( period_cycles , imx - > mmio_base + MX3_PWMPR ) ;
/*
* Store the duty cycle for future reference in cases where the
* MX3_PWMSAR register can ' t be read ( i . e . when the PWM is disabled ) .
*/
imx - > duty_cycle = duty_cycles ;
cr = MX3_PWMCR_PRESCALER_SET ( prescale ) |
MX3_PWMCR_STOPEN | MX3_PWMCR_DOZEN | MX3_PWMCR_WAITEN |
FIELD_PREP ( MX3_PWMCR_CLKSRC , MX3_PWMCR_CLKSRC_IPG_HIGH ) |
MX3_PWMCR_DBGEN ;
if ( state - > polarity = = PWM_POLARITY_INVERSED )
cr | = FIELD_PREP ( MX3_PWMCR_POUTC ,
MX3_PWMCR_POUTC_INVERTED ) ;
if ( state - > enabled )
cr | = MX3_PWMCR_EN ;
writel ( cr , imx - > mmio_base + MX3_PWMCR ) ;
2020-02-09 22:31:06 +01:00
if ( ! state - > enabled )
2020-02-10 22:22:38 +01:00
pwm_imx27_clk_disable_unprepare ( imx ) ;
2019-10-17 17:11:41 +02:00
2017-01-29 22:54:11 +01:00
return 0 ;
2009-01-16 15:17:16 +01:00
}
2019-01-07 20:53:52 +01:00
static const struct pwm_ops pwm_imx27_ops = {
. apply = pwm_imx27_apply ,
. get_state = pwm_imx27_get_state ,
2012-03-15 10:04:35 +01:00
. owner = THIS_MODULE ,
} ;
2009-01-16 15:17:16 +01:00
2019-01-07 20:53:52 +01:00
static const struct of_device_id pwm_imx27_dt_ids [ ] = {
{ . compatible = " fsl,imx27-pwm " , } ,
2012-06-25 16:16:25 +02:00
{ /* sentinel */ }
} ;
2019-01-07 20:53:52 +01:00
MODULE_DEVICE_TABLE ( of , pwm_imx27_dt_ids ) ;
2012-06-25 16:16:25 +02:00
2019-01-07 20:53:52 +01:00
static int pwm_imx27_probe ( struct platform_device * pdev )
2009-01-16 15:17:16 +01:00
{
2019-01-07 20:53:52 +01:00
struct pwm_imx27_chip * imx ;
2020-02-10 22:22:40 +01:00
int ret ;
u32 pwmcr ;
2009-01-16 15:17:16 +01:00
2012-07-01 08:27:23 +08:00
imx = devm_kzalloc ( & pdev - > dev , sizeof ( * imx ) , GFP_KERNEL ) ;
2014-04-23 18:39:49 +09:00
if ( imx = = NULL )
2009-01-16 15:17:16 +01:00
return - ENOMEM ;
2018-12-19 05:24:58 +00:00
imx - > clk_ipg = devm_clk_get ( & pdev - > dev , " ipg " ) ;
2020-08-11 14:24:31 +08:00
if ( IS_ERR ( imx - > clk_ipg ) )
return dev_err_probe ( & pdev - > dev , PTR_ERR ( imx - > clk_ipg ) ,
" getting ipg clock failed \n " ) ;
2018-12-19 05:24:58 +00:00
2012-06-25 16:15:20 +02:00
imx - > clk_per = devm_clk_get ( & pdev - > dev , " per " ) ;
2020-08-11 14:24:31 +08:00
if ( IS_ERR ( imx - > clk_per ) )
return dev_err_probe ( & pdev - > dev , PTR_ERR ( imx - > clk_per ) ,
" failed to get peripheral clock \n " ) ;
2009-01-16 15:17:16 +01:00
2019-01-07 20:53:52 +01:00
imx - > chip . ops = & pwm_imx27_ops ;
2012-03-15 10:04:35 +01:00
imx - > chip . dev = & pdev - > dev ;
imx - > chip . npwm = 1 ;
2009-01-16 15:17:16 +01:00
2019-04-01 05:24:02 +00:00
imx - > mmio_base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2013-01-21 11:09:16 +01:00
if ( IS_ERR ( imx - > mmio_base ) )
return PTR_ERR ( imx - > mmio_base ) ;
2009-01-16 15:17:16 +01:00
2020-02-10 22:22:40 +01:00
ret = pwm_imx27_clk_prepare_enable ( imx ) ;
if ( ret )
return ret ;
/* keep clks on if pwm is running */
pwmcr = readl ( imx - > mmio_base + MX3_PWMCR ) ;
if ( ! ( pwmcr & MX3_PWMCR_EN ) )
pwm_imx27_clk_disable_unprepare ( imx ) ;
2021-07-07 18:28:04 +02:00
return devm_pwmchip_add ( & pdev - > dev , & imx - > chip ) ;
2009-01-16 15:17:16 +01:00
}
2012-03-15 10:04:35 +01:00
static struct platform_driver imx_pwm_driver = {
2019-01-07 20:53:52 +01:00
. driver = {
. name = " pwm-imx27 " ,
. of_match_table = pwm_imx27_dt_ids ,
2009-01-16 15:17:16 +01:00
} ,
2019-01-07 20:53:52 +01:00
. probe = pwm_imx27_probe ,
2009-01-16 15:17:16 +01:00
} ;
2012-08-28 08:27:40 +02:00
module_platform_driver ( imx_pwm_driver ) ;
2009-01-16 15:17:16 +01:00
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_AUTHOR ( " Sascha Hauer <s.hauer@pengutronix.de> " ) ;