2019-05-27 08:55:06 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2017-03-20 14:47:26 +08:00
/*
* LED driver for Mediatek MT6323 PMIC
*
* Copyright ( C ) 2017 Sean Wang < sean . wang @ mediatek . com >
*/
# include <linux/kernel.h>
# include <linux/leds.h>
# include <linux/mfd/mt6323/registers.h>
# include <linux/mfd/mt6397/core.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
/*
2023-06-01 13:08:10 +02:00
* Register field for TOP_CKPDN0 to enable
2017-03-20 14:47:26 +08:00
* 32 K clock common for LED device .
*/
2023-06-01 13:08:10 +02:00
# define RG_DRV_32K_CK_PDN BIT(11)
# define RG_DRV_32K_CK_PDN_MASK BIT(11)
2017-03-20 14:47:26 +08:00
2023-06-01 13:08:13 +02:00
/* 32K/1M/6M clock common for WLED device */
# define RG_VWLED_1M_CK_PDN BIT(0)
# define RG_VWLED_32K_CK_PDN BIT(12)
# define RG_VWLED_6M_CK_PDN BIT(13)
2017-03-20 14:47:26 +08:00
/*
2023-06-01 13:08:10 +02:00
* Register field for TOP_CKPDN2 to enable
2017-03-20 14:47:26 +08:00
* individual clock for LED device .
*/
2023-06-01 13:08:10 +02:00
# define RG_ISINK_CK_PDN(i) BIT(i)
# define RG_ISINK_CK_PDN_MASK(i) BIT(i)
2017-03-20 14:47:26 +08:00
/*
2023-06-01 13:08:10 +02:00
* Register field for TOP_CKCON1 to select
2017-03-20 14:47:26 +08:00
* clock source .
*/
2023-06-01 13:08:10 +02:00
# define RG_ISINK_CK_SEL_MASK(i) (BIT(10) << (i))
2017-03-20 14:47:26 +08:00
2023-06-01 13:08:10 +02:00
# define ISINK_CON(r, i) (r + 0x8 * (i))
2023-06-01 13:08:09 +02:00
/* ISINK_CON0: Register to setup the duty cycle of the blink. */
2023-06-01 13:08:10 +02:00
# define ISINK_DIM_DUTY_MASK (0x1f << 8)
# define ISINK_DIM_DUTY(i) (((i) << 8) & ISINK_DIM_DUTY_MASK)
2017-03-20 14:47:26 +08:00
2023-06-01 13:08:09 +02:00
/* ISINK_CON1: Register to setup the period of the blink. */
2023-06-01 13:08:10 +02:00
# define ISINK_DIM_FSEL_MASK (0xffff)
# define ISINK_DIM_FSEL(i) ((i) & ISINK_DIM_FSEL_MASK)
2017-03-20 14:47:26 +08:00
2023-06-01 13:08:09 +02:00
/* ISINK_CON2: Register to control the brightness. */
2023-06-01 13:08:10 +02:00
# define ISINK_CH_STEP_SHIFT 12
# define ISINK_CH_STEP_MASK (0x7 << 12)
# define ISINK_CH_STEP(i) (((i) << 12) & ISINK_CH_STEP_MASK)
# define ISINK_SFSTR0_TC_MASK (0x3 << 1)
# define ISINK_SFSTR0_TC(i) (((i) << 1) & ISINK_SFSTR0_TC_MASK)
# define ISINK_SFSTR0_EN_MASK BIT(0)
# define ISINK_SFSTR0_EN BIT(0)
2017-03-20 14:47:26 +08:00
/* Register to LED channel enablement. */
2023-06-01 13:08:10 +02:00
# define ISINK_CH_EN_MASK(i) BIT(i)
# define ISINK_CH_EN(i) BIT(i)
2017-03-20 14:47:26 +08:00
2023-06-01 13:08:09 +02:00
# define MAX_SUPPORTED_LEDS 8
2017-03-20 14:47:26 +08:00
struct mt6323_leds ;
/**
* struct mt6323_led - state container for the LED device
* @ id : the identifier in MT6323 LED device
* @ parent : the pointer to MT6323 LED controller
* @ cdev : LED class device for this LED device
* @ current_brightness : current state of the LED device
*/
struct mt6323_led {
int id ;
struct mt6323_leds * parent ;
struct led_classdev cdev ;
2023-06-22 16:12:41 +00:00
enum led_brightness current_brightness ;
2017-03-20 14:47:26 +08:00
} ;
2023-06-01 13:08:09 +02:00
/**
* struct mt6323_regs - register spec for the LED device
* @ top_ckpdn : Offset to ISINK_CKPDN [ 0. . x ] registers
* @ num_top_ckpdn : Number of ISINK_CKPDN registers
* @ top_ckcon : Offset to ISINK_CKCON [ 0. . x ] registers
* @ num_top_ckcon : Number of ISINK_CKCON registers
* @ isink_con : Offset to ISINKx_CON [ 0. . x ] registers
* @ num_isink_con : Number of ISINKx_CON registers
* @ isink_max_regs : Number of ISINK [ 0. . x ] registers
* @ isink_en_ctrl : Offset to ISINK_EN_CTRL register
2023-06-01 13:08:13 +02:00
* @ iwled_en_ctrl : Offset to IWLED_EN_CTRL register
2023-06-01 13:08:09 +02:00
*/
struct mt6323_regs {
const u16 * top_ckpdn ;
u8 num_top_ckpdn ;
const u16 * top_ckcon ;
u8 num_top_ckcon ;
const u16 * isink_con ;
u8 num_isink_con ;
u8 isink_max_regs ;
u16 isink_en_ctrl ;
2023-06-01 13:08:13 +02:00
u16 iwled_en_ctrl ;
2023-06-01 13:08:09 +02:00
} ;
/**
* struct mt6323_hwspec - hardware specific parameters
* @ max_period : Maximum period for all LEDs
* @ max_leds : Maximum number of supported LEDs
2023-06-01 13:08:13 +02:00
* @ max_wleds : Maximum number of WLEDs
2023-06-01 13:08:09 +02:00
* @ max_brightness : Maximum brightness for all LEDs
* @ unit_duty : Steps of duty per period
*/
struct mt6323_hwspec {
u16 max_period ;
u8 max_leds ;
2023-06-01 13:08:13 +02:00
u8 max_wleds ;
2023-06-01 13:08:09 +02:00
u16 max_brightness ;
u16 unit_duty ;
} ;
/**
* struct mt6323_data - device specific data
* @ regs : Register spec for this device
* @ spec : Hardware specific parameters
*/
struct mt6323_data {
const struct mt6323_regs * regs ;
const struct mt6323_hwspec * spec ;
} ;
2017-03-20 14:47:26 +08:00
/**
* struct mt6323_leds - state container for holding LED controller
* of the driver
* @ dev : the device pointer
* @ hw : the underlying hardware providing shared
* bus for the register operations
2023-06-01 13:08:09 +02:00
* @ pdata : device specific data
2017-03-20 14:47:26 +08:00
* @ lock : the lock among process context
* @ led : the array that contains the state of individual
* LED device
*/
struct mt6323_leds {
struct device * dev ;
struct mt6397_chip * hw ;
2023-06-01 13:08:09 +02:00
const struct mt6323_data * pdata ;
2017-03-20 14:47:26 +08:00
/* protect among process context */
struct mutex lock ;
2023-06-01 13:08:09 +02:00
struct mt6323_led * led [ MAX_SUPPORTED_LEDS ] ;
2017-03-20 14:47:26 +08:00
} ;
static int mt6323_led_hw_brightness ( struct led_classdev * cdev ,
enum led_brightness brightness )
{
struct mt6323_led * led = container_of ( cdev , struct mt6323_led , cdev ) ;
struct mt6323_leds * leds = led - > parent ;
2023-06-01 13:08:09 +02:00
const struct mt6323_regs * regs = leds - > pdata - > regs ;
2017-03-20 14:47:26 +08:00
struct regmap * regmap = leds - > hw - > regmap ;
u32 con2_mask = 0 , con2_val = 0 ;
int ret ;
/*
* Setup current output for the corresponding
* brightness level .
*/
2023-06-01 13:08:10 +02:00
con2_mask | = ISINK_CH_STEP_MASK |
ISINK_SFSTR0_TC_MASK |
ISINK_SFSTR0_EN_MASK ;
con2_val | = ISINK_CH_STEP ( brightness - 1 ) |
ISINK_SFSTR0_TC ( 2 ) |
ISINK_SFSTR0_EN ;
ret = regmap_update_bits ( regmap , ISINK_CON ( regs - > isink_con [ 2 ] , led - > id ) ,
2017-03-20 14:47:26 +08:00
con2_mask , con2_val ) ;
return ret ;
}
static int mt6323_led_hw_off ( struct led_classdev * cdev )
{
struct mt6323_led * led = container_of ( cdev , struct mt6323_led , cdev ) ;
struct mt6323_leds * leds = led - > parent ;
2023-06-01 13:08:09 +02:00
const struct mt6323_regs * regs = leds - > pdata - > regs ;
2017-03-20 14:47:26 +08:00
struct regmap * regmap = leds - > hw - > regmap ;
unsigned int status ;
int ret ;
2023-06-01 13:08:10 +02:00
status = ISINK_CH_EN ( led - > id ) ;
2023-06-01 13:08:09 +02:00
ret = regmap_update_bits ( regmap , regs - > isink_en_ctrl ,
2023-06-01 13:08:10 +02:00
ISINK_CH_EN_MASK ( led - > id ) , ~ status ) ;
2017-03-20 14:47:26 +08:00
if ( ret < 0 )
return ret ;
usleep_range ( 100 , 300 ) ;
2023-06-01 13:08:09 +02:00
ret = regmap_update_bits ( regmap , regs - > top_ckpdn [ 2 ] ,
2023-06-01 13:08:10 +02:00
RG_ISINK_CK_PDN_MASK ( led - > id ) ,
RG_ISINK_CK_PDN ( led - > id ) ) ;
2017-03-20 14:47:26 +08:00
if ( ret < 0 )
return ret ;
return 0 ;
}
static enum led_brightness
mt6323_get_led_hw_brightness ( struct led_classdev * cdev )
{
struct mt6323_led * led = container_of ( cdev , struct mt6323_led , cdev ) ;
struct mt6323_leds * leds = led - > parent ;
2023-06-01 13:08:09 +02:00
const struct mt6323_regs * regs = leds - > pdata - > regs ;
2017-03-20 14:47:26 +08:00
struct regmap * regmap = leds - > hw - > regmap ;
unsigned int status ;
int ret ;
2023-06-01 13:08:09 +02:00
ret = regmap_read ( regmap , regs - > top_ckpdn [ 2 ] , & status ) ;
2017-03-20 14:47:26 +08:00
if ( ret < 0 )
return ret ;
2023-06-01 13:08:10 +02:00
if ( status & RG_ISINK_CK_PDN_MASK ( led - > id ) )
2017-03-20 14:47:26 +08:00
return 0 ;
2023-06-01 13:08:09 +02:00
ret = regmap_read ( regmap , regs - > isink_en_ctrl , & status ) ;
2017-03-20 14:47:26 +08:00
if ( ret < 0 )
return ret ;
2023-06-01 13:08:10 +02:00
if ( ! ( status & ISINK_CH_EN ( led - > id ) ) )
2017-03-20 14:47:26 +08:00
return 0 ;
2023-06-01 13:08:10 +02:00
ret = regmap_read ( regmap , ISINK_CON ( regs - > isink_con [ 2 ] , led - > id ) , & status ) ;
2017-03-20 14:47:26 +08:00
if ( ret < 0 )
return ret ;
2023-06-01 13:08:10 +02:00
return ( ( status & ISINK_CH_STEP_MASK )
> > ISINK_CH_STEP_SHIFT ) + 1 ;
2017-03-20 14:47:26 +08:00
}
static int mt6323_led_hw_on ( struct led_classdev * cdev ,
enum led_brightness brightness )
{
struct mt6323_led * led = container_of ( cdev , struct mt6323_led , cdev ) ;
struct mt6323_leds * leds = led - > parent ;
2023-06-01 13:08:09 +02:00
const struct mt6323_regs * regs = leds - > pdata - > regs ;
2017-03-20 14:47:26 +08:00
struct regmap * regmap = leds - > hw - > regmap ;
unsigned int status ;
int ret ;
/*
* Setup required clock source , enable the corresponding
* clock and channel and let work with continuous blink as
* the default .
*/
2023-06-01 13:08:09 +02:00
ret = regmap_update_bits ( regmap , regs - > top_ckcon [ 1 ] ,
2023-06-01 13:08:10 +02:00
RG_ISINK_CK_SEL_MASK ( led - > id ) , 0 ) ;
2017-03-20 14:47:26 +08:00
if ( ret < 0 )
return ret ;
2023-06-01 13:08:10 +02:00
status = RG_ISINK_CK_PDN ( led - > id ) ;
2023-06-01 13:08:09 +02:00
ret = regmap_update_bits ( regmap , regs - > top_ckpdn [ 2 ] ,
2023-06-01 13:08:10 +02:00
RG_ISINK_CK_PDN_MASK ( led - > id ) ,
2017-03-20 14:47:26 +08:00
~ status ) ;
if ( ret < 0 )
return ret ;
usleep_range ( 100 , 300 ) ;
2023-06-01 13:08:09 +02:00
ret = regmap_update_bits ( regmap , regs - > isink_en_ctrl ,
2023-06-01 13:08:10 +02:00
ISINK_CH_EN_MASK ( led - > id ) ,
ISINK_CH_EN ( led - > id ) ) ;
2017-03-20 14:47:26 +08:00
if ( ret < 0 )
return ret ;
ret = mt6323_led_hw_brightness ( cdev , brightness ) ;
if ( ret < 0 )
return ret ;
2023-06-01 13:08:10 +02:00
ret = regmap_update_bits ( regmap , ISINK_CON ( regs - > isink_con [ 0 ] , led - > id ) ,
ISINK_DIM_DUTY_MASK ,
ISINK_DIM_DUTY ( 31 ) ) ;
2017-03-20 14:47:26 +08:00
if ( ret < 0 )
return ret ;
2023-06-01 13:08:10 +02:00
ret = regmap_update_bits ( regmap , ISINK_CON ( regs - > isink_con [ 1 ] , led - > id ) ,
ISINK_DIM_FSEL_MASK ,
ISINK_DIM_FSEL ( 1000 ) ) ;
2017-03-20 14:47:26 +08:00
if ( ret < 0 )
return ret ;
return 0 ;
}
static int mt6323_led_set_blink ( struct led_classdev * cdev ,
unsigned long * delay_on ,
unsigned long * delay_off )
{
struct mt6323_led * led = container_of ( cdev , struct mt6323_led , cdev ) ;
struct mt6323_leds * leds = led - > parent ;
2023-06-01 13:08:09 +02:00
const struct mt6323_regs * regs = leds - > pdata - > regs ;
const struct mt6323_hwspec * spec = leds - > pdata - > spec ;
2017-03-20 14:47:26 +08:00
struct regmap * regmap = leds - > hw - > regmap ;
unsigned long period ;
u8 duty_hw ;
int ret ;
/*
* LED subsystem requires a default user
* friendly blink pattern for the LED so using
* 1 Hz duty cycle 50 % here if without specific
* value delay_on and delay off being assigned .
*/
if ( ! * delay_on & & ! * delay_off ) {
* delay_on = 500 ;
* delay_off = 500 ;
}
2020-08-09 09:32:21 -07:00
/*
* Units are in ms , if over the hardware able
* to support , fallback into software blink
*/
period = * delay_on + * delay_off ;
2023-06-01 13:08:09 +02:00
if ( period > spec - > max_period )
2020-08-09 09:32:21 -07:00
return - EINVAL ;
2017-03-20 14:47:26 +08:00
/*
* Calculate duty_hw based on the percentage of period during
* which the led is ON .
*/
2023-06-01 13:08:11 +02:00
duty_hw = DIV_ROUND_CLOSEST ( * delay_on * 100000ul , period * spec - > unit_duty ) ;
2017-03-20 14:47:26 +08:00
/* hardware doesn't support zero duty cycle. */
if ( ! duty_hw )
return - EINVAL ;
mutex_lock ( & leds - > lock ) ;
/*
* Set max_brightness as the software blink behavior
* when no blink brightness .
*/
if ( ! led - > current_brightness ) {
ret = mt6323_led_hw_on ( cdev , cdev - > max_brightness ) ;
if ( ret < 0 )
goto out ;
led - > current_brightness = cdev - > max_brightness ;
}
2023-06-01 13:08:10 +02:00
ret = regmap_update_bits ( regmap , ISINK_CON ( regs - > isink_con [ 0 ] , led - > id ) ,
ISINK_DIM_DUTY_MASK ,
ISINK_DIM_DUTY ( duty_hw - 1 ) ) ;
2017-03-20 14:47:26 +08:00
if ( ret < 0 )
goto out ;
2023-06-01 13:08:10 +02:00
ret = regmap_update_bits ( regmap , ISINK_CON ( regs - > isink_con [ 1 ] , led - > id ) ,
ISINK_DIM_FSEL_MASK ,
ISINK_DIM_FSEL ( period - 1 ) ) ;
2017-03-20 14:47:26 +08:00
out :
mutex_unlock ( & leds - > lock ) ;
return ret ;
}
static int mt6323_led_set_brightness ( struct led_classdev * cdev ,
enum led_brightness brightness )
{
struct mt6323_led * led = container_of ( cdev , struct mt6323_led , cdev ) ;
struct mt6323_leds * leds = led - > parent ;
int ret ;
mutex_lock ( & leds - > lock ) ;
if ( ! led - > current_brightness & & brightness ) {
ret = mt6323_led_hw_on ( cdev , brightness ) ;
if ( ret < 0 )
goto out ;
} else if ( brightness ) {
ret = mt6323_led_hw_brightness ( cdev , brightness ) ;
if ( ret < 0 )
goto out ;
} else {
ret = mt6323_led_hw_off ( cdev ) ;
if ( ret < 0 )
goto out ;
}
led - > current_brightness = brightness ;
out :
mutex_unlock ( & leds - > lock ) ;
return ret ;
}
2023-06-01 13:08:13 +02:00
static int mtk_wled_hw_on ( struct led_classdev * cdev )
{
struct mt6323_led * led = container_of ( cdev , struct mt6323_led , cdev ) ;
struct mt6323_leds * leds = led - > parent ;
const struct mt6323_regs * regs = leds - > pdata - > regs ;
struct regmap * regmap = leds - > hw - > regmap ;
int ret ;
ret = regmap_clear_bits ( regmap , regs - > top_ckpdn [ 0 ] , RG_VWLED_32K_CK_PDN ) ;
if ( ret )
return ret ;
ret = regmap_clear_bits ( regmap , regs - > top_ckpdn [ 0 ] , RG_VWLED_6M_CK_PDN ) ;
if ( ret )
return ret ;
ret = regmap_clear_bits ( regmap , regs - > top_ckpdn [ 0 ] , RG_VWLED_1M_CK_PDN ) ;
if ( ret )
return ret ;
usleep_range ( 5000 , 6000 ) ;
/* Enable WLED channel pair */
ret = regmap_set_bits ( regmap , regs - > iwled_en_ctrl , BIT ( led - > id ) ) ;
if ( ret )
return ret ;
ret = regmap_set_bits ( regmap , regs - > iwled_en_ctrl , BIT ( led - > id + 1 ) ) ;
if ( ret )
return ret ;
return 0 ;
}
static int mtk_wled_hw_off ( struct led_classdev * cdev )
{
struct mt6323_led * led = container_of ( cdev , struct mt6323_led , cdev ) ;
struct mt6323_leds * leds = led - > parent ;
const struct mt6323_regs * regs = leds - > pdata - > regs ;
struct regmap * regmap = leds - > hw - > regmap ;
int ret ;
ret = regmap_clear_bits ( regmap , regs - > iwled_en_ctrl , BIT ( led - > id + 1 ) ) ;
if ( ret )
return ret ;
ret = regmap_clear_bits ( regmap , regs - > iwled_en_ctrl , BIT ( led - > id ) ) ;
if ( ret )
return ret ;
ret = regmap_set_bits ( regmap , regs - > top_ckpdn [ 0 ] , RG_VWLED_32K_CK_PDN ) ;
if ( ret )
return ret ;
ret = regmap_set_bits ( regmap , regs - > top_ckpdn [ 0 ] , RG_VWLED_6M_CK_PDN ) ;
if ( ret )
return ret ;
ret = regmap_set_bits ( regmap , regs - > top_ckpdn [ 0 ] , RG_VWLED_1M_CK_PDN ) ;
if ( ret )
return ret ;
return 0 ;
}
2023-06-22 16:12:41 +00:00
static enum led_brightness mt6323_get_wled_brightness ( struct led_classdev * cdev )
2023-06-01 13:08:13 +02:00
{
struct mt6323_led * led = container_of ( cdev , struct mt6323_led , cdev ) ;
struct mt6323_leds * leds = led - > parent ;
const struct mt6323_regs * regs = leds - > pdata - > regs ;
struct regmap * regmap = leds - > hw - > regmap ;
unsigned int status ;
int ret ;
ret = regmap_read ( regmap , regs - > iwled_en_ctrl , & status ) ;
if ( ret )
return 0 ;
/* Always two channels per WLED */
status & = BIT ( led - > id ) | BIT ( led - > id + 1 ) ;
return status ? led - > current_brightness : 0 ;
}
static int mt6323_wled_set_brightness ( struct led_classdev * cdev ,
2023-06-22 16:12:41 +00:00
enum led_brightness brightness )
2023-06-01 13:08:13 +02:00
{
struct mt6323_led * led = container_of ( cdev , struct mt6323_led , cdev ) ;
struct mt6323_leds * leds = led - > parent ;
int ret = 0 ;
mutex_lock ( & leds - > lock ) ;
if ( brightness ) {
if ( ! led - > current_brightness )
ret = mtk_wled_hw_on ( cdev ) ;
if ( ret )
goto out ;
} else {
ret = mtk_wled_hw_off ( cdev ) ;
if ( ret )
goto out ;
}
led - > current_brightness = brightness ;
out :
mutex_unlock ( & leds - > lock ) ;
return ret ;
}
2017-03-20 14:47:26 +08:00
static int mt6323_led_set_dt_default ( struct led_classdev * cdev ,
struct device_node * np )
{
struct mt6323_led * led = container_of ( cdev , struct mt6323_led , cdev ) ;
2023-01-03 15:12:51 +02:00
enum led_default_state state ;
2017-03-20 14:47:26 +08:00
int ret = 0 ;
2023-01-03 15:12:51 +02:00
state = led_init_default_state_get ( of_fwnode_handle ( np ) ) ;
switch ( state ) {
case LEDS_DEFSTATE_ON :
ret = mt6323_led_set_brightness ( cdev , cdev - > max_brightness ) ;
break ;
case LEDS_DEFSTATE_KEEP :
ret = mt6323_get_led_hw_brightness ( cdev ) ;
if ( ret < 0 )
return ret ;
led - > current_brightness = ret ;
ret = 0 ;
break ;
default :
ret = mt6323_led_set_brightness ( cdev , LED_OFF ) ;
2017-03-20 14:47:26 +08:00
}
return ret ;
}
static int mt6323_led_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
2020-09-18 00:33:07 +02:00
struct device_node * np = dev_of_node ( dev ) ;
2017-03-20 14:47:26 +08:00
struct device_node * child ;
2020-09-18 00:33:07 +02:00
struct mt6397_chip * hw = dev_get_drvdata ( dev - > parent ) ;
2017-03-20 14:47:26 +08:00
struct mt6323_leds * leds ;
struct mt6323_led * led ;
2023-06-01 13:08:09 +02:00
const struct mt6323_regs * regs ;
const struct mt6323_hwspec * spec ;
2017-03-20 14:47:26 +08:00
int ret ;
unsigned int status ;
u32 reg ;
2023-06-01 13:08:13 +02:00
u8 max_leds ;
2017-03-20 14:47:26 +08:00
leds = devm_kzalloc ( dev , sizeof ( * leds ) , GFP_KERNEL ) ;
if ( ! leds )
return - ENOMEM ;
platform_set_drvdata ( pdev , leds ) ;
leds - > dev = dev ;
2023-06-01 13:08:09 +02:00
leds - > pdata = device_get_match_data ( dev ) ;
regs = leds - > pdata - > regs ;
spec = leds - > pdata - > spec ;
2023-06-01 13:08:13 +02:00
max_leds = spec - > max_leds + spec - > max_wleds ;
2017-03-20 14:47:26 +08:00
/*
* leds - > hw points to the underlying bus for the register
* controlled .
*/
leds - > hw = hw ;
mutex_init ( & leds - > lock ) ;
2023-06-01 13:08:10 +02:00
status = RG_DRV_32K_CK_PDN ;
2023-06-01 13:08:09 +02:00
ret = regmap_update_bits ( leds - > hw - > regmap , regs - > top_ckpdn [ 0 ] ,
2023-06-01 13:08:10 +02:00
RG_DRV_32K_CK_PDN_MASK , ~ status ) ;
2017-03-20 14:47:26 +08:00
if ( ret < 0 ) {
dev_err ( leds - > dev ,
2023-06-01 13:08:09 +02:00
" Failed to update TOP_CKPDN0 Register \n " ) ;
2017-03-20 14:47:26 +08:00
return ret ;
}
for_each_available_child_of_node ( np , child ) {
2020-09-18 00:33:06 +02:00
struct led_init_data init_data = { } ;
2023-06-01 13:08:13 +02:00
bool is_wled ;
2020-09-18 00:33:06 +02:00
2017-03-20 14:47:26 +08:00
ret = of_property_read_u32 ( child , " reg " , & reg ) ;
if ( ret ) {
dev_err ( dev , " Failed to read led 'reg' property \n " ) ;
goto put_child_node ;
}
2023-06-01 13:08:13 +02:00
if ( reg > = max_leds | | reg > = MAX_SUPPORTED_LEDS | |
2023-06-01 13:08:09 +02:00
leds - > led [ reg ] ) {
2017-03-20 14:47:26 +08:00
dev_err ( dev , " Invalid led reg %u \n " , reg ) ;
ret = - EINVAL ;
goto put_child_node ;
}
led = devm_kzalloc ( dev , sizeof ( * led ) , GFP_KERNEL ) ;
if ( ! led ) {
ret = - ENOMEM ;
goto put_child_node ;
}
2023-06-01 13:08:13 +02:00
is_wled = of_property_read_bool ( child , " mediatek,is-wled " ) ;
2017-03-20 14:47:26 +08:00
leds - > led [ reg ] = led ;
leds - > led [ reg ] - > id = reg ;
2023-06-01 13:08:09 +02:00
leds - > led [ reg ] - > cdev . max_brightness = spec - > max_brightness ;
2023-06-01 13:08:13 +02:00
if ( is_wled ) {
leds - > led [ reg ] - > cdev . brightness_set_blocking =
mt6323_wled_set_brightness ;
leds - > led [ reg ] - > cdev . brightness_get =
mt6323_get_wled_brightness ;
} else {
leds - > led [ reg ] - > cdev . brightness_set_blocking =
mt6323_led_set_brightness ;
leds - > led [ reg ] - > cdev . blink_set = mt6323_led_set_blink ;
leds - > led [ reg ] - > cdev . brightness_get =
mt6323_get_led_hw_brightness ;
}
2017-03-20 14:47:26 +08:00
leds - > led [ reg ] - > parent = leds ;
ret = mt6323_led_set_dt_default ( & leds - > led [ reg ] - > cdev , child ) ;
if ( ret < 0 ) {
dev_err ( leds - > dev ,
" Failed to LED set default from devicetree \n " ) ;
goto put_child_node ;
}
2020-09-18 00:33:06 +02:00
init_data . fwnode = of_fwnode_handle ( child ) ;
ret = devm_led_classdev_register_ext ( dev , & leds - > led [ reg ] - > cdev ,
& init_data ) ;
2017-03-20 14:47:26 +08:00
if ( ret ) {
2020-09-18 00:33:07 +02:00
dev_err ( dev , " Failed to register LED: %d \n " , ret ) ;
2017-03-20 14:47:26 +08:00
goto put_child_node ;
}
}
return 0 ;
put_child_node :
of_node_put ( child ) ;
return ret ;
}
2023-09-17 15:09:47 +02:00
static void mt6323_led_remove ( struct platform_device * pdev )
2017-03-20 14:47:26 +08:00
{
struct mt6323_leds * leds = platform_get_drvdata ( pdev ) ;
2023-06-01 13:08:09 +02:00
const struct mt6323_regs * regs = leds - > pdata - > regs ;
2017-03-20 14:47:26 +08:00
int i ;
/* Turn the LEDs off on driver removal. */
for ( i = 0 ; leds - > led [ i ] ; i + + )
mt6323_led_hw_off ( & leds - > led [ i ] - > cdev ) ;
2023-06-01 13:08:09 +02:00
regmap_update_bits ( leds - > hw - > regmap , regs - > top_ckpdn [ 0 ] ,
2023-06-01 13:08:10 +02:00
RG_DRV_32K_CK_PDN_MASK ,
RG_DRV_32K_CK_PDN ) ;
2017-03-20 14:47:26 +08:00
mutex_destroy ( & leds - > lock ) ;
}
2023-06-01 13:08:09 +02:00
static const struct mt6323_regs mt6323_registers = {
. top_ckpdn = ( const u16 [ ] ) { 0x102 , 0x106 , 0x10e } ,
. num_top_ckpdn = 3 ,
. top_ckcon = ( const u16 [ ] ) { 0x120 , 0x126 } ,
. num_top_ckcon = 2 ,
. isink_con = ( const u16 [ ] ) { 0x330 , 0x332 , 0x334 } ,
. num_isink_con = 3 ,
. isink_max_regs = 4 , /* ISINK[0..3] */
. isink_en_ctrl = 0x356 ,
} ;
2023-06-01 13:08:12 +02:00
static const struct mt6323_regs mt6331_registers = {
. top_ckpdn = ( const u16 [ ] ) { 0x138 , 0x13e , 0x144 } ,
. num_top_ckpdn = 3 ,
. top_ckcon = ( const u16 [ ] ) { 0x14c , 0x14a } ,
. num_top_ckcon = 2 ,
. isink_con = ( const u16 [ ] ) { 0x40c , 0x40e , 0x410 , 0x412 , 0x414 } ,
. num_isink_con = 5 ,
. isink_max_regs = 4 , /* ISINK[0..3] */
. isink_en_ctrl = 0x43a ,
} ;
2023-06-01 13:08:13 +02:00
static const struct mt6323_regs mt6332_registers = {
. top_ckpdn = ( const u16 [ ] ) { 0x8094 , 0x809a , 0x80a0 } ,
. num_top_ckpdn = 3 ,
. top_ckcon = ( const u16 [ ] ) { 0x80a6 , 0x80ac } ,
. num_top_ckcon = 2 ,
. isink_con = ( const u16 [ ] ) { 0x8cd4 } ,
. num_isink_con = 1 ,
. isink_max_regs = 12 , /* IWLED[0..2, 3..9] */
. iwled_en_ctrl = 0x8cda ,
} ;
2023-06-01 13:08:09 +02:00
static const struct mt6323_hwspec mt6323_spec = {
. max_period = 10000 ,
. max_leds = 4 ,
. max_brightness = 6 ,
. unit_duty = 3125 ,
} ;
2023-06-01 13:08:13 +02:00
static const struct mt6323_hwspec mt6332_spec = {
/* There are no LEDs in MT6332. Only WLEDs are present. */
. max_leds = 0 ,
. max_wleds = 1 ,
. max_brightness = 1024 ,
} ;
2023-06-01 13:08:09 +02:00
static const struct mt6323_data mt6323_pdata = {
. regs = & mt6323_registers ,
. spec = & mt6323_spec ,
} ;
2023-06-01 13:08:12 +02:00
static const struct mt6323_data mt6331_pdata = {
. regs = & mt6331_registers ,
. spec = & mt6323_spec ,
} ;
2023-06-01 13:08:13 +02:00
static const struct mt6323_data mt6332_pdata = {
. regs = & mt6332_registers ,
. spec = & mt6332_spec ,
} ;
2017-03-20 14:47:26 +08:00
static const struct of_device_id mt6323_led_dt_match [ ] = {
2023-06-01 13:08:09 +02:00
{ . compatible = " mediatek,mt6323-led " , . data = & mt6323_pdata } ,
2023-06-01 13:08:12 +02:00
{ . compatible = " mediatek,mt6331-led " , . data = & mt6331_pdata } ,
2023-06-01 13:08:13 +02:00
{ . compatible = " mediatek,mt6332-led " , . data = & mt6332_pdata } ,
2017-03-20 14:47:26 +08:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , mt6323_led_dt_match ) ;
static struct platform_driver mt6323_led_driver = {
. probe = mt6323_led_probe ,
2023-09-17 15:09:47 +02:00
. remove_new = mt6323_led_remove ,
2017-03-20 14:47:26 +08:00
. driver = {
. name = " mt6323-led " ,
. of_match_table = mt6323_led_dt_match ,
} ,
} ;
module_platform_driver ( mt6323_led_driver ) ;
MODULE_DESCRIPTION ( " LED driver for Mediatek MT6323 PMIC " ) ;
MODULE_AUTHOR ( " Sean Wang <sean.wang@mediatek.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;