2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2015-06-26 12:02:08 +03:00
/*
* Copyright ( C ) 2015 Intel Corporation . All rights reserved .
*
* Author : Shobhit Kumar < shobhit . kumar @ intel . com >
*/
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <linux/mfd/intel_soc_pmic.h>
# include <linux/pwm.h>
# define PWM0_CLK_DIV 0x4B
# define PWM_OUTPUT_ENABLE BIT(7)
# define PWM_DIV_CLK_0 0x00 /* DIVIDECLK = BASECLK */
# define PWM_DIV_CLK_100 0x63 /* DIVIDECLK = BASECLK/100 */
# define PWM_DIV_CLK_128 0x7F /* DIVIDECLK = BASECLK/128 */
# define PWM0_DUTY_CYCLE 0x4E
# define BACKLIGHT_EN 0x51
# define PWM_MAX_LEVEL 0xFF
2020-09-03 14:23:28 +03:00
# define PWM_BASE_CLK_MHZ 6 /* 6 MHz */
2020-09-03 14:23:29 +03:00
# define PWM_MAX_PERIOD_NS 5461334 /* 183 Hz */
2015-06-26 12:02:08 +03:00
/**
* struct crystalcove_pwm - Crystal Cove PWM controller
* @ chip : the abstract pwm_chip structure .
* @ regmap : the regmap from the parent device .
*/
struct crystalcove_pwm {
struct pwm_chip chip ;
struct regmap * regmap ;
} ;
static inline struct crystalcove_pwm * to_crc_pwm ( struct pwm_chip * pc )
{
return container_of ( pc , struct crystalcove_pwm , chip ) ;
}
2020-09-03 14:23:29 +03:00
static int crc_pwm_calc_clk_div ( int period_ns )
{
int clk_div ;
clk_div = PWM_BASE_CLK_MHZ * period_ns / ( 256 * NSEC_PER_USEC ) ;
/* clk_div 1 - 128, maps to register values 0-127 */
if ( clk_div > 0 )
clk_div - - ;
return clk_div ;
}
2020-09-03 14:23:32 +03:00
static int crc_pwm_apply ( struct pwm_chip * chip , struct pwm_device * pwm ,
const struct pwm_state * state )
2015-06-26 12:02:08 +03:00
{
2020-09-03 14:23:32 +03:00
struct crystalcove_pwm * crc_pwm = to_crc_pwm ( chip ) ;
struct device * dev = crc_pwm - > chip . dev ;
int err ;
2015-06-26 12:02:08 +03:00
2020-09-03 14:23:32 +03:00
if ( state - > period > PWM_MAX_PERIOD_NS ) {
dev_err ( dev , " un-supported period_ns \n " ) ;
return - EINVAL ;
}
2015-06-26 12:02:08 +03:00
2020-09-03 14:23:32 +03:00
if ( state - > polarity ! = PWM_POLARITY_NORMAL )
2020-11-11 23:18:11 +03:00
return - EINVAL ;
2015-06-26 12:02:08 +03:00
2020-09-03 14:23:32 +03:00
if ( pwm_is_enabled ( pwm ) & & ! state - > enabled ) {
err = regmap_write ( crc_pwm - > regmap , BACKLIGHT_EN , 0 ) ;
if ( err ) {
dev_err ( dev , " Error writing BACKLIGHT_EN %d \n " , err ) ;
return err ;
}
}
2015-06-26 12:02:08 +03:00
2020-09-03 14:23:32 +03:00
if ( pwm_get_duty_cycle ( pwm ) ! = state - > duty_cycle | |
pwm_get_period ( pwm ) ! = state - > period ) {
u64 level = state - > duty_cycle * PWM_MAX_LEVEL ;
2015-06-26 12:02:08 +03:00
2020-09-03 14:23:32 +03:00
do_div ( level , state - > period ) ;
2015-06-26 12:02:08 +03:00
2020-09-03 14:23:32 +03:00
err = regmap_write ( crc_pwm - > regmap , PWM0_DUTY_CYCLE , level ) ;
if ( err ) {
dev_err ( dev , " Error writing PWM0_DUTY_CYCLE %d \n " , err ) ;
return err ;
}
2015-06-26 12:02:08 +03:00
}
2020-09-03 14:23:32 +03:00
if ( pwm_is_enabled ( pwm ) & & state - > enabled & &
pwm_get_period ( pwm ) ! = state - > period ) {
2020-09-03 14:23:30 +03:00
/* changing the clk divisor, clear PWM_OUTPUT_ENABLE first */
2020-09-03 14:23:32 +03:00
err = regmap_write ( crc_pwm - > regmap , PWM0_CLK_DIV , 0 ) ;
if ( err ) {
dev_err ( dev , " Error writing PWM0_CLK_DIV %d \n " , err ) ;
return err ;
}
}
2015-06-26 12:02:08 +03:00
2020-09-03 14:23:32 +03:00
if ( pwm_get_period ( pwm ) ! = state - > period | |
pwm_is_enabled ( pwm ) ! = state - > enabled ) {
int clk_div = crc_pwm_calc_clk_div ( state - > period ) ;
int pwm_output_enable = state - > enabled ? PWM_OUTPUT_ENABLE : 0 ;
err = regmap_write ( crc_pwm - > regmap , PWM0_CLK_DIV ,
clk_div | pwm_output_enable ) ;
if ( err ) {
dev_err ( dev , " Error writing PWM0_CLK_DIV %d \n " , err ) ;
return err ;
}
2015-06-26 12:02:08 +03:00
}
2020-09-03 14:23:32 +03:00
if ( ! pwm_is_enabled ( pwm ) & & state - > enabled ) {
err = regmap_write ( crc_pwm - > regmap , BACKLIGHT_EN , 1 ) ;
if ( err ) {
dev_err ( dev , " Error writing BACKLIGHT_EN %d \n " , err ) ;
return err ;
}
}
2015-06-26 12:02:08 +03:00
return 0 ;
}
2022-12-02 21:35:26 +03:00
static int crc_pwm_get_state ( struct pwm_chip * chip , struct pwm_device * pwm ,
struct pwm_state * state )
2020-09-03 14:23:33 +03:00
{
struct crystalcove_pwm * crc_pwm = to_crc_pwm ( chip ) ;
struct device * dev = crc_pwm - > chip . dev ;
unsigned int clk_div , clk_div_reg , duty_cycle_reg ;
int error ;
error = regmap_read ( crc_pwm - > regmap , PWM0_CLK_DIV , & clk_div_reg ) ;
if ( error ) {
dev_err ( dev , " Error reading PWM0_CLK_DIV %d \n " , error ) ;
2022-12-02 21:35:30 +03:00
return error ;
2020-09-03 14:23:33 +03:00
}
error = regmap_read ( crc_pwm - > regmap , PWM0_DUTY_CYCLE , & duty_cycle_reg ) ;
if ( error ) {
dev_err ( dev , " Error reading PWM0_DUTY_CYCLE %d \n " , error ) ;
2022-12-02 21:35:30 +03:00
return error ;
2020-09-03 14:23:33 +03:00
}
clk_div = ( clk_div_reg & ~ PWM_OUTPUT_ENABLE ) + 1 ;
state - > period =
DIV_ROUND_UP ( clk_div * NSEC_PER_USEC * 256 , PWM_BASE_CLK_MHZ ) ;
state - > duty_cycle =
DIV_ROUND_UP_ULL ( duty_cycle_reg * state - > period , PWM_MAX_LEVEL ) ;
state - > polarity = PWM_POLARITY_NORMAL ;
state - > enabled = ! ! ( clk_div_reg & PWM_OUTPUT_ENABLE ) ;
2022-12-02 21:35:26 +03:00
return 0 ;
2020-09-03 14:23:33 +03:00
}
2015-06-26 12:02:08 +03:00
static const struct pwm_ops crc_pwm_ops = {
2020-09-03 14:23:32 +03:00
. apply = crc_pwm_apply ,
2020-09-03 14:23:33 +03:00
. get_state = crc_pwm_get_state ,
2015-06-26 12:02:08 +03:00
} ;
static int crystalcove_pwm_probe ( struct platform_device * pdev )
{
struct crystalcove_pwm * pwm ;
struct device * dev = pdev - > dev . parent ;
struct intel_soc_pmic * pmic = dev_get_drvdata ( dev ) ;
pwm = devm_kzalloc ( & pdev - > dev , sizeof ( * pwm ) , GFP_KERNEL ) ;
if ( ! pwm )
return - ENOMEM ;
pwm - > chip . dev = & pdev - > dev ;
pwm - > chip . ops = & crc_pwm_ops ;
pwm - > chip . npwm = 1 ;
/* get the PMIC regmap */
pwm - > regmap = pmic - > regmap ;
2021-05-25 23:31:56 +03:00
return devm_pwmchip_add ( & pdev - > dev , & pwm - > chip ) ;
2015-06-26 12:02:08 +03:00
}
static struct platform_driver crystalcove_pwm_driver = {
. probe = crystalcove_pwm_probe ,
. driver = {
. name = " crystal_cove_pwm " ,
} ,
} ;
builtin_platform_driver ( crystalcove_pwm_driver ) ;