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 >
*/
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>
2012-06-25 16:16:25 +02:00
# include <linux/of_device.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
/* i.MX1 and i.MX21 share the same PWM function block: */
2014-05-28 18:50:12 +08:00
# define MX1_PWMC 0x00 /* PWM Control Register */
# define MX1_PWMS 0x04 /* PWM Sample Register */
# define MX1_PWMP 0x08 /* PWM Period Register */
2009-04-14 12:50:20 +02:00
2018-10-01 16:19:47 +02:00
# define MX1_PWMC_EN BIT(4)
2009-04-14 12:50:20 +02:00
/* i.MX27, i.MX31, i.MX35 share the same PWM function block: */
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
2012-03-15 10:04:35 +01:00
struct imx_chip {
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 ;
2009-01-16 15:17:16 +01:00
} ;
2012-03-15 10:04:35 +01:00
# define to_imx_chip(chip) container_of(chip, struct imx_chip, chip)
2018-10-01 16:19:48 +02:00
static void imx_pwm_get_state ( struct pwm_chip * chip ,
struct pwm_device * pwm , struct pwm_state * state )
{
struct imx_chip * imx = to_imx_chip ( chip ) ;
u32 period , prescaler , pwm_clk , ret , val ;
u64 tmp ;
val = readl ( imx - > mmio_base + MX3_PWMCR ) ;
if ( val & MX3_PWMCR_EN ) {
state - > enabled = true ;
ret = clk_prepare_enable ( imx - > clk_per ) ;
if ( ret )
return ;
} else {
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 ) ;
pwm_clk = DIV_ROUND_CLOSEST_ULL ( pwm_clk , prescaler ) ;
val = readl ( imx - > mmio_base + MX3_PWMPR ) ;
period = val > = MX3_PWMPR_MAX ? MX3_PWMPR_MAX : val ;
/* PWMOUT (Hz) = PWMCLK / (PWMPR + 2) */
tmp = NSEC_PER_SEC * ( u64 ) ( period + 2 ) ;
state - > period = DIV_ROUND_CLOSEST_ULL ( tmp , pwm_clk ) ;
/* PWMSAR can be read only if PWM is enabled */
if ( state - > enabled ) {
val = readl ( imx - > mmio_base + MX3_PWMSAR ) ;
tmp = NSEC_PER_SEC * ( u64 ) ( val ) ;
state - > duty_cycle = DIV_ROUND_CLOSEST_ULL ( tmp , pwm_clk ) ;
} else {
state - > duty_cycle = 0 ;
}
}
2012-07-03 17:28:14 +02:00
static int imx_pwm_config_v1 ( struct pwm_chip * chip ,
2012-03-15 10:04:35 +01:00
struct pwm_device * pwm , int duty_ns , int period_ns )
2009-01-16 15:17:16 +01:00
{
2012-03-15 10:04:35 +01:00
struct imx_chip * imx = to_imx_chip ( chip ) ;
2009-01-16 15:17:16 +01:00
2012-07-03 17:28:14 +02:00
/*
* The PWM subsystem allows for exact frequencies . However ,
* I cannot connect a scope on my device to the PWM line and
* thus cannot provide the program the PWM controller
* exactly . Instead , I ' m relying on the fact that the
* Bootloader ( u - boot or WinCE + haret ) has programmed the PWM
* function group already . So I ' ll just modify the PWM sample
* register to follow the ratio of duty_ns vs . period_ns
* accordingly .
*
* This is good enough for programming the brightness of
* the LCD backlight .
*
* The real implementation would divide PERCLK [ 0 ] first by
* both the prescaler ( / 1 . . / 128 ) and then by CLKSEL
* ( / 2 . . / 16 ) .
*/
u32 max = readl ( imx - > mmio_base + MX1_PWMP ) ;
u32 p = max * duty_ns / period_ns ;
writel ( max - p , imx - > mmio_base + MX1_PWMS ) ;
return 0 ;
}
2017-01-29 22:54:08 +01:00
static int imx_pwm_enable_v1 ( struct pwm_chip * chip , struct pwm_device * pwm )
2012-08-28 11:39:25 +02:00
{
struct imx_chip * imx = to_imx_chip ( chip ) ;
u32 val ;
2017-01-29 22:54:08 +01:00
int ret ;
2012-08-28 11:39:25 +02:00
2017-01-29 22:54:08 +01:00
ret = clk_prepare_enable ( imx - > clk_per ) ;
if ( ret < 0 )
return ret ;
2012-08-28 11:39:25 +02:00
val = readl ( imx - > mmio_base + MX1_PWMC ) ;
2017-01-29 22:54:08 +01:00
val | = MX1_PWMC_EN ;
2012-08-28 11:39:25 +02:00
writel ( val , imx - > mmio_base + MX1_PWMC ) ;
2009-01-16 15:17:16 +01:00
return 0 ;
}
2017-01-29 22:54:08 +01:00
static void imx_pwm_disable_v1 ( struct pwm_chip * chip , struct pwm_device * pwm )
2012-08-28 11:39:25 +02:00
{
struct imx_chip * imx = to_imx_chip ( chip ) ;
u32 val ;
2017-01-29 22:54:08 +01:00
val = readl ( imx - > mmio_base + MX1_PWMC ) ;
val & = ~ MX1_PWMC_EN ;
2012-08-28 11:39:25 +02:00
writel ( val , imx - > mmio_base + MX1_PWMC ) ;
2017-01-29 22:54:08 +01:00
clk_disable_unprepare ( imx - > clk_per ) ;
2012-08-28 11:39:25 +02:00
}
2017-01-29 22:54:09 +01:00
static void imx_pwm_sw_reset ( struct pwm_chip * chip )
2012-07-03 17:28:14 +02:00
{
struct imx_chip * imx = to_imx_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
2017-01-29 22:54:10 +01:00
static void imx_pwm_wait_fifo_slot ( struct pwm_chip * chip ,
struct pwm_device * pwm )
{
struct imx_chip * imx = to_imx_chip ( chip ) ;
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 ) {
period_ms = DIV_ROUND_UP ( pwm_get_period ( pwm ) ,
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
}
2017-01-29 22:54:11 +01:00
static int imx_pwm_apply_v2 ( struct pwm_chip * chip , struct pwm_device * pwm ,
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 ;
2012-03-15 10:04:35 +01:00
struct imx_chip * imx = to_imx_chip ( chip ) ;
2017-01-29 22:54:11 +01:00
struct pwm_state cstate ;
unsigned long long c ;
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 ) ;
if ( state - > enabled ) {
c = clk_get_rate ( imx - > clk_per ) ;
c * = state - > period ;
do_div ( c , 1000000000 ) ;
period_cycles = c ;
prescale = period_cycles / 0x10000 + 1 ;
period_cycles / = prescale ;
c = ( unsigned long long ) period_cycles * state - > duty_cycle ;
do_div ( c , state - > period ) ;
duty_cycles = c ;
/*
* 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 ) {
imx_pwm_wait_fifo_slot ( chip , pwm ) ;
} else {
ret = clk_prepare_enable ( imx - > clk_per ) ;
if ( ret )
return ret ;
imx_pwm_sw_reset ( chip ) ;
}
2009-01-16 15:17:16 +01:00
2017-01-29 22:54:11 +01:00
writel ( duty_cycles , imx - > mmio_base + MX3_PWMSAR ) ;
writel ( period_cycles , imx - > mmio_base + MX3_PWMPR ) ;
2012-08-28 09:12:01 +02:00
2018-10-01 16:19:47 +02:00
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 | MX3_PWMCR_EN ;
2012-08-28 11:39:25 +02:00
2017-01-29 22:54:15 +01:00
if ( state - > polarity = = PWM_POLARITY_INVERSED )
2018-10-01 16:19:47 +02:00
cr | = FIELD_PREP ( MX3_PWMCR_POUTC ,
MX3_PWMCR_POUTC_INVERTED ) ;
2009-01-16 15:17:16 +01:00
2017-01-29 22:54:15 +01:00
writel ( cr , imx - > mmio_base + MX3_PWMCR ) ;
2017-01-29 22:54:11 +01:00
} else if ( cstate . enabled ) {
writel ( 0 , imx - > mmio_base + MX3_PWMCR ) ;
2009-01-16 15:17:16 +01:00
2017-01-29 22:54:11 +01:00
clk_disable_unprepare ( imx - > clk_per ) ;
}
2009-01-16 15:17:16 +01:00
2017-01-29 22:54:11 +01:00
return 0 ;
2009-01-16 15:17:16 +01:00
}
2017-01-29 22:54:07 +01:00
static const struct pwm_ops imx_pwm_ops_v1 = {
2017-01-29 22:54:08 +01:00
. enable = imx_pwm_enable_v1 ,
. disable = imx_pwm_disable_v1 ,
. config = imx_pwm_config_v1 ,
2017-01-29 22:54:07 +01:00
. owner = THIS_MODULE ,
} ;
static const struct pwm_ops imx_pwm_ops_v2 = {
2017-01-29 22:54:11 +01:00
. apply = imx_pwm_apply_v2 ,
2018-10-01 16:19:48 +02:00
. get_state = imx_pwm_get_state ,
2012-03-15 10:04:35 +01:00
. owner = THIS_MODULE ,
} ;
2009-01-16 15:17:16 +01:00
2012-06-25 16:16:25 +02:00
struct imx_pwm_data {
2017-01-29 22:54:15 +01:00
bool polarity_supported ;
2017-01-29 22:54:07 +01:00
const struct pwm_ops * ops ;
2012-06-25 16:16:25 +02:00
} ;
static struct imx_pwm_data imx_pwm_data_v1 = {
2017-01-29 22:54:07 +01:00
. ops = & imx_pwm_ops_v1 ,
2012-06-25 16:16:25 +02:00
} ;
static struct imx_pwm_data imx_pwm_data_v2 = {
2017-01-29 22:54:15 +01:00
. polarity_supported = true ,
2017-01-29 22:54:07 +01:00
. ops = & imx_pwm_ops_v2 ,
2012-06-25 16:16:25 +02:00
} ;
static const struct of_device_id imx_pwm_dt_ids [ ] = {
{ . compatible = " fsl,imx1-pwm " , . data = & imx_pwm_data_v1 , } ,
{ . compatible = " fsl,imx27-pwm " , . data = & imx_pwm_data_v2 , } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , imx_pwm_dt_ids ) ;
2012-11-19 13:23:14 -05:00
static int imx_pwm_probe ( struct platform_device * pdev )
2009-01-16 15:17:16 +01:00
{
2012-06-25 16:16:25 +02:00
const struct of_device_id * of_id =
of_match_device ( imx_pwm_dt_ids , & pdev - > dev ) ;
2012-12-05 16:34:41 +01:00
const struct imx_pwm_data * data ;
2012-03-15 10:04:35 +01:00
struct imx_chip * imx ;
2009-01-16 15:17:16 +01:00
struct resource * r ;
int ret = 0 ;
2012-06-25 16:16:25 +02:00
if ( ! of_id )
return - ENODEV ;
2017-01-29 22:54:07 +01:00
data = of_id - > data ;
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 ;
2012-06-25 16:15:20 +02:00
imx - > clk_per = devm_clk_get ( & pdev - > dev , " per " ) ;
if ( IS_ERR ( imx - > clk_per ) ) {
dev_err ( & pdev - > dev , " getting per clock failed with %ld \n " ,
PTR_ERR ( imx - > clk_per ) ) ;
return PTR_ERR ( imx - > clk_per ) ;
}
2009-01-16 15:17:16 +01:00
2017-01-29 22:54:07 +01:00
imx - > chip . ops = data - > ops ;
2012-03-15 10:04:35 +01:00
imx - > chip . dev = & pdev - > dev ;
imx - > chip . base = - 1 ;
imx - > chip . npwm = 1 ;
2009-01-16 15:17:16 +01:00
2017-01-29 22:54:15 +01:00
if ( data - > polarity_supported ) {
dev_dbg ( & pdev - > dev , " PWM supports output inversion \n " ) ;
imx - > chip . of_xlate = of_pwm_xlate_with_flags ;
imx - > chip . of_pwm_n_cells = 3 ;
}
2009-01-16 15:17:16 +01:00
r = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-01-21 11:09:16 +01:00
imx - > mmio_base = devm_ioremap_resource ( & pdev - > dev , r ) ;
if ( IS_ERR ( imx - > mmio_base ) )
return PTR_ERR ( imx - > mmio_base ) ;
2009-01-16 15:17:16 +01:00
2012-03-15 10:04:35 +01:00
ret = pwmchip_add ( & imx - > chip ) ;
if ( ret < 0 )
2012-07-01 08:27:23 +08:00
return ret ;
2009-01-16 15:17:16 +01:00
2012-03-15 10:04:35 +01:00
platform_set_drvdata ( pdev , imx ) ;
2009-01-16 15:17:16 +01:00
return 0 ;
}
2012-11-19 13:26:09 -05:00
static int imx_pwm_remove ( struct platform_device * pdev )
2009-01-16 15:17:16 +01:00
{
2012-03-15 10:04:35 +01:00
struct imx_chip * imx ;
2009-01-16 15:17:16 +01:00
2012-03-15 10:04:35 +01:00
imx = platform_get_drvdata ( pdev ) ;
if ( imx = = NULL )
2009-01-16 15:17:16 +01:00
return - ENODEV ;
2012-07-01 08:27:23 +08:00
return pwmchip_remove ( & 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 = {
2009-01-16 15:17:16 +01:00
. driver = {
2012-06-25 16:16:25 +02:00
. name = " imx-pwm " ,
2013-09-30 08:56:41 +05:30
. of_match_table = imx_pwm_dt_ids ,
2009-01-16 15:17:16 +01:00
} ,
2012-03-15 10:04:35 +01:00
. probe = imx_pwm_probe ,
2012-11-19 13:21:28 -05:00
. remove = imx_pwm_remove ,
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> " ) ;