2021-04-19 03:00:07 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Toshiba Visconti pulse - width - modulation controller driver
*
* Copyright ( c ) 2020 - 2021 TOSHIBA CORPORATION
* Copyright ( c ) 2020 - 2021 Toshiba Electronic Devices & Storage Corporation
*
* Authors : Nobuhiro Iwamatsu < nobuhiro1 . iwamatsu @ toshiba . co . jp >
*
* Limitations :
* - The fixed input clock is running at 1 MHz and is divided by either 1 ,
* 2 , 4 or 8.
* - When the settings of the PWM are modified , the new values are shadowed
* in hardware until the PIPGM_PCSR register is written and the currently
* running period is completed . This way the hardware switches atomically
* from the old setting to the new .
* - Disabling the hardware completes the currently running period and keeps
* the output at low level at all times .
*/
# include <linux/err.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
# include <linux/pwm.h>
# define PIPGM_PCSR(ch) (0x400 + 4 * (ch))
# define PIPGM_PDUT(ch) (0x420 + 4 * (ch))
# define PIPGM_PWMC(ch) (0x440 + 4 * (ch))
# define PIPGM_PWMC_PWMACT BIT(5)
# define PIPGM_PWMC_CLK_MASK GENMASK(1, 0)
# define PIPGM_PWMC_POLARITY_MASK GENMASK(5, 5)
struct visconti_pwm_chip {
struct pwm_chip chip ;
void __iomem * base ;
} ;
static inline struct visconti_pwm_chip * visconti_pwm_from_chip ( struct pwm_chip * chip )
{
return container_of ( chip , struct visconti_pwm_chip , chip ) ;
}
static int visconti_pwm_apply ( struct pwm_chip * chip , struct pwm_device * pwm ,
const struct pwm_state * state )
{
struct visconti_pwm_chip * priv = visconti_pwm_from_chip ( chip ) ;
u32 period , duty_cycle , pwmc0 ;
if ( ! state - > enabled ) {
writel ( 0 , priv - > base + PIPGM_PCSR ( pwm - > hwpwm ) ) ;
return 0 ;
}
/*
* The biggest period the hardware can provide is
* ( 0xffff < < 3 ) * 1000 ns
* This value fits easily in an u32 , so simplify the maths by
* capping the values to 32 bit integers .
*/
if ( state - > period > ( 0xffff < < 3 ) * 1000 )
period = ( 0xffff < < 3 ) * 1000 ;
else
period = state - > period ;
if ( state - > duty_cycle > period )
duty_cycle = period ;
else
duty_cycle = state - > duty_cycle ;
/*
* The input clock runs fixed at 1 MHz , so we have only
* microsecond resolution and so can divide by
* NSEC_PER_SEC / CLKFREQ = 1000 without losing precision .
*/
period / = 1000 ;
duty_cycle / = 1000 ;
if ( ! period )
return - ERANGE ;
/*
2021-04-26 18:03:50 +03:00
* PWMC controls a divider that divides the input clk by a power of two
* between 1 and 8. As a smaller divider yields higher precision , pick
* the smallest possible one . As period is at most 0xffff < < 3 , pwmc0 is
* in the intended range [ 0. .3 ] .
2021-04-19 03:00:07 +03:00
*/
2021-04-26 18:03:50 +03:00
pwmc0 = fls ( period > > 16 ) ;
if ( WARN_ON ( pwmc0 > 3 ) )
return - EINVAL ;
2021-04-19 03:00:07 +03:00
period > > = pwmc0 ;
duty_cycle > > = pwmc0 ;
if ( state - > polarity = = PWM_POLARITY_INVERSED )
pwmc0 | = PIPGM_PWMC_PWMACT ;
writel ( pwmc0 , priv - > base + PIPGM_PWMC ( pwm - > hwpwm ) ) ;
writel ( duty_cycle , priv - > base + PIPGM_PDUT ( pwm - > hwpwm ) ) ;
writel ( period , priv - > base + PIPGM_PCSR ( pwm - > hwpwm ) ) ;
return 0 ;
}
static void visconti_pwm_get_state ( struct pwm_chip * chip , struct pwm_device * pwm ,
struct pwm_state * state )
{
struct visconti_pwm_chip * priv = visconti_pwm_from_chip ( chip ) ;
u32 period , duty , pwmc0 , pwmc0_clk ;
period = readl ( priv - > base + PIPGM_PCSR ( pwm - > hwpwm ) ) ;
duty = readl ( priv - > base + PIPGM_PDUT ( pwm - > hwpwm ) ) ;
pwmc0 = readl ( priv - > base + PIPGM_PWMC ( pwm - > hwpwm ) ) ;
pwmc0_clk = pwmc0 & PIPGM_PWMC_CLK_MASK ;
state - > period = ( period < < pwmc0_clk ) * NSEC_PER_USEC ;
state - > duty_cycle = ( duty < < pwmc0_clk ) * NSEC_PER_USEC ;
if ( pwmc0 & PIPGM_PWMC_POLARITY_MASK )
state - > polarity = PWM_POLARITY_INVERSED ;
else
state - > polarity = PWM_POLARITY_NORMAL ;
state - > enabled = true ;
}
static const struct pwm_ops visconti_pwm_ops = {
. apply = visconti_pwm_apply ,
. get_state = visconti_pwm_get_state ,
. owner = THIS_MODULE ,
} ;
static int visconti_pwm_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct visconti_pwm_chip * priv ;
int ret ;
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( priv - > base ) )
return PTR_ERR ( priv - > base ) ;
priv - > chip . dev = dev ;
priv - > chip . ops = & visconti_pwm_ops ;
priv - > chip . npwm = 4 ;
2021-09-13 09:15:53 +03:00
ret = devm_pwmchip_add ( & pdev - > dev , & priv - > chip ) ;
2021-04-19 03:00:07 +03:00
if ( ret < 0 )
return dev_err_probe ( & pdev - > dev , ret , " Cannot register visconti PWM \n " ) ;
return 0 ;
}
static const struct of_device_id visconti_pwm_of_match [ ] = {
{ . compatible = " toshiba,visconti-pwm " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , visconti_pwm_of_match ) ;
static struct platform_driver visconti_pwm_driver = {
. driver = {
. name = " pwm-visconti " ,
. of_match_table = visconti_pwm_of_match ,
} ,
. probe = visconti_pwm_probe ,
} ;
module_platform_driver ( visconti_pwm_driver ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_AUTHOR ( " Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp> " ) ;
MODULE_ALIAS ( " platform:pwm-visconti " ) ;