2019-05-30 02:57:44 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-03-24 02:02:10 +04:00
/*
* Copyright 2011 bct electronic GmbH
2013-08-15 01:23:47 +04:00
* Copyright 2013 Qtechnology / AS
2012-03-24 02:02:10 +04:00
*
* Author : Peter Meerwald < p . meerwald @ bct - electronic . com >
2020-04-30 16:52:24 +03:00
* Author : Ricardo Ribalda < ribalda @ kernel . org >
2012-03-24 02:02:10 +04:00
*
* Based on leds - pca955x . c
*
* LED driver for the PCA9633 I2C LED driver ( 7 - bit slave address 0x62 )
2014-07-03 09:50:34 +04:00
* LED driver for the PCA9634 / 5 I2C LED driver ( 7 - bit slave address set by hw . )
2012-03-24 02:02:10 +04:00
*
2013-07-25 21:16:41 +04:00
* Note that hardware blinking violates the leds infrastructure driver
* interface since the hardware only supports blinking all LEDs with the
* same delay_on / delay_off rates . That is , only the LEDs that are set to
* blink will actually blink but all LEDs that are set to blink will blink
* in identical fashion . The delay_on / delay_off values of the last LED
* that is set to blink will be used for all of the blinking LEDs .
* Hardware blinking is disabled by default but can be enabled by setting
2013-08-15 01:23:50 +04:00
* the ' blink_type ' member in the platform_data struct to ' PCA963X_HW_BLINK '
2013-07-25 21:16:41 +04:00
* or by adding the ' nxp , hw - blink ' property to the DTS .
2012-03-24 02:02:10 +04:00
*/
# include <linux/module.h>
# include <linux/delay.h>
# include <linux/string.h>
# include <linux/ctype.h>
# include <linux/leds.h>
# include <linux/err.h>
# include <linux/i2c.h>
2019-03-25 17:02:07 +03:00
# include <linux/property.h>
2012-03-24 02:02:10 +04:00
# include <linux/slab.h>
2013-06-26 16:52:49 +04:00
# include <linux/of.h>
2012-03-24 02:02:10 +04:00
/* LED select registers determine the source that drives LED outputs */
2013-08-15 01:23:50 +04:00
# define PCA963X_LED_OFF 0x0 /* LED driver off */
# define PCA963X_LED_ON 0x1 /* LED driver on */
# define PCA963X_LED_PWM 0x2 /* Controlled through PWM */
# define PCA963X_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */
2012-03-24 02:02:10 +04:00
2019-11-19 00:02:55 +03:00
# define PCA963X_MODE2_OUTDRV 0x04 /* Open-drain or totem pole */
# define PCA963X_MODE2_INVRT 0x10 /* Normal or inverted direction */
2013-08-15 01:23:50 +04:00
# define PCA963X_MODE2_DMBLNK 0x20 /* Enable blinking */
2013-07-25 21:16:41 +04:00
2013-08-15 01:23:50 +04:00
# define PCA963X_MODE1 0x00
# define PCA963X_MODE2 0x01
# define PCA963X_PWM_BASE 0x02
2013-08-15 01:23:47 +04:00
2013-08-15 01:23:50 +04:00
enum pca963x_type {
2013-08-15 01:23:47 +04:00
pca9633 ,
pca9634 ,
2014-07-03 09:50:34 +04:00
pca9635 ,
2013-08-15 01:23:47 +04:00
} ;
2013-08-15 01:23:50 +04:00
struct pca963x_chipdef {
2013-08-15 01:23:47 +04:00
u8 grppwm ;
u8 grpfreq ;
u8 ledout_base ;
int n_leds ;
2016-10-13 16:16:12 +03:00
unsigned int scaling ;
2013-08-15 01:23:47 +04:00
} ;
2013-08-15 01:23:50 +04:00
static struct pca963x_chipdef pca963x_chipdefs [ ] = {
2013-08-15 01:23:47 +04:00
[ pca9633 ] = {
. grppwm = 0x6 ,
. grpfreq = 0x7 ,
. ledout_base = 0x8 ,
. n_leds = 4 ,
} ,
[ pca9634 ] = {
. grppwm = 0xa ,
. grpfreq = 0xb ,
. ledout_base = 0xc ,
. n_leds = 8 ,
} ,
2014-07-03 09:50:34 +04:00
[ pca9635 ] = {
. grppwm = 0x12 ,
. grpfreq = 0x13 ,
. ledout_base = 0x14 ,
. n_leds = 16 ,
} ,
2013-08-15 01:23:47 +04:00
} ;
2012-03-24 02:02:10 +04:00
2013-07-25 21:16:41 +04:00
/* Total blink period in milliseconds */
2013-08-15 01:23:50 +04:00
# define PCA963X_BLINK_PERIOD_MIN 42
# define PCA963X_BLINK_PERIOD_MAX 10667
2013-07-25 21:16:41 +04:00
2013-08-15 01:23:50 +04:00
static const struct i2c_device_id pca963x_id [ ] = {
2013-08-15 01:23:47 +04:00
{ " pca9632 " , pca9633 } ,
{ " pca9633 " , pca9633 } ,
{ " pca9634 " , pca9634 } ,
2014-07-03 09:50:34 +04:00
{ " pca9635 " , pca9635 } ,
2012-03-24 02:02:10 +04:00
{ }
} ;
2013-08-15 01:23:50 +04:00
MODULE_DEVICE_TABLE ( i2c , pca963x_id ) ;
2012-03-24 02:02:10 +04:00
2020-09-20 03:24:58 +03:00
struct pca963x ;
2013-08-15 01:23:49 +04:00
2013-08-15 01:23:50 +04:00
struct pca963x_led {
struct pca963x * chip ;
2012-03-24 02:02:10 +04:00
struct led_classdev led_cdev ;
2014-07-03 09:50:34 +04:00
int led_num ; /* 0 .. 15 potentially */
2013-07-25 21:16:41 +04:00
u8 gdc ;
u8 gfrq ;
2012-03-24 02:02:10 +04:00
} ;
2020-09-20 03:24:58 +03:00
struct pca963x {
struct pca963x_chipdef * chipdef ;
struct mutex mutex ;
struct i2c_client * client ;
unsigned long leds_on ;
struct pca963x_led leds [ ] ;
} ;
2020-09-20 03:24:57 +03:00
static int pca963x_brightness ( struct pca963x_led * led ,
2020-09-20 03:24:54 +03:00
enum led_brightness brightness )
2012-03-24 02:02:10 +04:00
{
2020-09-20 03:24:57 +03:00
struct i2c_client * client = led - > chip - > client ;
struct pca963x_chipdef * chipdef = led - > chip - > chipdef ;
2020-09-20 03:24:54 +03:00
u8 ledout_addr , ledout , mask , val ;
int shift ;
2015-08-20 13:38:44 +03:00
int ret ;
2012-03-24 02:02:10 +04:00
2020-09-20 03:24:57 +03:00
ledout_addr = chipdef - > ledout_base + ( led - > led_num / 4 ) ;
shift = 2 * ( led - > led_num % 4 ) ;
2020-09-20 03:24:54 +03:00
mask = 0x3 < < shift ;
ledout = i2c_smbus_read_byte_data ( client , ledout_addr ) ;
2015-08-20 13:38:44 +03:00
switch ( brightness ) {
2012-03-24 02:02:10 +04:00
case LED_FULL :
2020-09-20 03:24:54 +03:00
val = ( ledout & ~ mask ) | ( PCA963X_LED_ON < < shift ) ;
ret = i2c_smbus_write_byte_data ( client , ledout_addr , val ) ;
2012-03-24 02:02:10 +04:00
break ;
case LED_OFF :
2020-09-20 03:24:54 +03:00
val = ledout & ~ mask ;
ret = i2c_smbus_write_byte_data ( client , ledout_addr , val ) ;
2012-03-24 02:02:10 +04:00
break ;
default :
2020-09-20 03:24:54 +03:00
ret = i2c_smbus_write_byte_data ( client ,
PCA963X_PWM_BASE +
2020-09-20 03:24:57 +03:00
led - > led_num ,
2020-09-20 03:24:54 +03:00
brightness ) ;
2015-08-20 13:38:44 +03:00
if ( ret < 0 )
2016-10-29 04:20:42 +03:00
return ret ;
2020-09-20 03:24:54 +03:00
val = ( ledout & ~ mask ) | ( PCA963X_LED_PWM < < shift ) ;
ret = i2c_smbus_write_byte_data ( client , ledout_addr , val ) ;
2012-03-24 02:02:10 +04:00
break ;
}
2016-10-29 04:20:42 +03:00
2015-08-20 13:38:44 +03:00
return ret ;
2012-03-24 02:02:10 +04:00
}
2020-09-20 03:24:57 +03:00
static void pca963x_blink ( struct pca963x_led * led )
2013-07-25 21:16:41 +04:00
{
2020-09-20 03:24:57 +03:00
struct i2c_client * client = led - > chip - > client ;
struct pca963x_chipdef * chipdef = led - > chip - > chipdef ;
2020-09-20 03:24:54 +03:00
u8 ledout_addr , ledout , mask , val , mode2 ;
int shift ;
2020-09-20 03:24:57 +03:00
ledout_addr = chipdef - > ledout_base + ( led - > led_num / 4 ) ;
shift = 2 * ( led - > led_num % 4 ) ;
2020-09-20 03:24:54 +03:00
mask = 0x3 < < shift ;
mode2 = i2c_smbus_read_byte_data ( client , PCA963X_MODE2 ) ;
2013-07-25 21:16:41 +04:00
2020-09-20 03:24:57 +03:00
i2c_smbus_write_byte_data ( client , chipdef - > grppwm , led - > gdc ) ;
2013-07-25 21:16:41 +04:00
2020-09-20 03:24:57 +03:00
i2c_smbus_write_byte_data ( client , chipdef - > grpfreq , led - > gfrq ) ;
2013-07-25 21:16:41 +04:00
2013-08-15 01:23:50 +04:00
if ( ! ( mode2 & PCA963X_MODE2_DMBLNK ) )
2020-09-20 03:24:54 +03:00
i2c_smbus_write_byte_data ( client , PCA963X_MODE2 ,
mode2 | PCA963X_MODE2_DMBLNK ) ;
2013-07-25 21:16:41 +04:00
2020-09-20 03:24:57 +03:00
mutex_lock ( & led - > chip - > mutex ) ;
2020-09-20 03:24:54 +03:00
ledout = i2c_smbus_read_byte_data ( client , ledout_addr ) ;
if ( ( ledout & mask ) ! = ( PCA963X_LED_GRP_PWM < < shift ) ) {
val = ( ledout & ~ mask ) | ( PCA963X_LED_GRP_PWM < < shift ) ;
i2c_smbus_write_byte_data ( client , ledout_addr , val ) ;
}
2020-09-20 03:24:57 +03:00
mutex_unlock ( & led - > chip - > mutex ) ;
2013-07-25 21:16:41 +04:00
}
2020-09-20 03:24:57 +03:00
static int pca963x_power_state ( struct pca963x_led * led )
2016-10-29 04:20:42 +03:00
{
2020-09-20 03:24:57 +03:00
struct i2c_client * client = led - > chip - > client ;
unsigned long * leds_on = & led - > chip - > leds_on ;
2020-09-20 03:24:54 +03:00
unsigned long cached_leds = * leds_on ;
2016-10-29 04:20:42 +03:00
2020-09-20 03:24:57 +03:00
if ( led - > led_cdev . brightness )
set_bit ( led - > led_num , leds_on ) ;
2016-10-29 04:20:42 +03:00
else
2020-09-20 03:24:57 +03:00
clear_bit ( led - > led_num , leds_on ) ;
2016-10-29 04:20:42 +03:00
if ( ! ( * leds_on ) ! = ! cached_leds )
2020-09-20 03:24:54 +03:00
return i2c_smbus_write_byte_data ( client , PCA963X_MODE1 ,
* leds_on ? 0 : BIT ( 4 ) ) ;
2016-10-29 04:20:42 +03:00
return 0 ;
}
2015-08-20 13:38:44 +03:00
static int pca963x_led_set ( struct led_classdev * led_cdev ,
2020-09-20 03:24:54 +03:00
enum led_brightness value )
2012-03-24 02:02:10 +04:00
{
2020-09-20 03:24:57 +03:00
struct pca963x_led * led ;
2016-10-29 04:20:42 +03:00
int ret ;
2012-03-24 02:02:10 +04:00
2020-09-20 03:24:57 +03:00
led = container_of ( led_cdev , struct pca963x_led , led_cdev ) ;
2012-03-24 02:02:10 +04:00
2020-09-20 03:24:57 +03:00
mutex_lock ( & led - > chip - > mutex ) ;
2016-10-29 04:20:42 +03:00
2020-09-20 03:24:57 +03:00
ret = pca963x_brightness ( led , value ) ;
2016-10-29 04:20:42 +03:00
if ( ret < 0 )
goto unlock ;
2020-09-20 03:24:57 +03:00
ret = pca963x_power_state ( led ) ;
2016-10-29 04:20:42 +03:00
unlock :
2020-09-20 03:24:57 +03:00
mutex_unlock ( & led - > chip - > mutex ) ;
2016-10-29 04:20:42 +03:00
return ret ;
2012-03-24 02:02:10 +04:00
}
2020-09-20 03:24:57 +03:00
static unsigned int pca963x_period_scale ( struct pca963x_led * led ,
2020-09-20 03:24:54 +03:00
unsigned int val )
2016-10-13 16:16:12 +03:00
{
2020-09-20 03:24:57 +03:00
unsigned int scaling = led - > chip - > chipdef - > scaling ;
2016-10-13 16:16:12 +03:00
return scaling ? DIV_ROUND_CLOSEST ( val * scaling , 1000 ) : val ;
}
2013-08-15 01:23:50 +04:00
static int pca963x_blink_set ( struct led_classdev * led_cdev ,
2020-09-20 03:24:54 +03:00
unsigned long * delay_on , unsigned long * delay_off )
2013-07-25 21:16:41 +04:00
{
unsigned long time_on , time_off , period ;
2020-09-20 03:24:57 +03:00
struct pca963x_led * led ;
2013-07-25 21:16:41 +04:00
u8 gdc , gfrq ;
2020-09-20 03:24:57 +03:00
led = container_of ( led_cdev , struct pca963x_led , led_cdev ) ;
2013-07-25 21:16:41 +04:00
time_on = * delay_on ;
time_off = * delay_off ;
/* If both zero, pick reasonable defaults of 500ms each */
if ( ! time_on & & ! time_off ) {
time_on = 500 ;
time_off = 500 ;
}
2020-09-20 03:24:57 +03:00
period = pca963x_period_scale ( led , time_on + time_off ) ;
2013-07-25 21:16:41 +04:00
/* If period not supported by hardware, default to someting sane. */
2013-08-15 01:23:50 +04:00
if ( ( period < PCA963X_BLINK_PERIOD_MIN ) | |
( period > PCA963X_BLINK_PERIOD_MAX ) ) {
2013-07-25 21:16:41 +04:00
time_on = 500 ;
time_off = 500 ;
2020-09-20 03:24:57 +03:00
period = pca963x_period_scale ( led , 1000 ) ;
2013-07-25 21:16:41 +04:00
}
/*
* From manual : duty cycle = ( GDC / 256 ) - >
* ( time_on / period ) = ( GDC / 256 ) - >
* GDC = ( ( time_on * 256 ) / period )
*/
2020-09-20 03:24:57 +03:00
gdc = ( pca963x_period_scale ( led , time_on ) * 256 ) / period ;
2013-07-25 21:16:41 +04:00
/*
* From manual : period = ( ( GFRQ + 1 ) / 24 ) in seconds .
* So , period ( in ms ) = ( ( ( GFRQ + 1 ) / 24 ) * 1000 ) - >
* GFRQ = ( ( period * 24 / 1000 ) - 1 )
*/
gfrq = ( period * 24 / 1000 ) - 1 ;
2020-09-20 03:24:57 +03:00
led - > gdc = gdc ;
led - > gfrq = gfrq ;
2013-07-25 21:16:41 +04:00
2020-09-20 03:24:57 +03:00
pca963x_blink ( led ) ;
2013-07-25 21:16:41 +04:00
* delay_on = time_on ;
* delay_off = time_off ;
return 0 ;
}
2020-09-20 03:24:59 +03:00
static int pca963x_register_leds ( struct i2c_client * client ,
struct pca963x * chip )
2013-06-26 16:52:49 +04:00
{
2020-09-20 03:24:59 +03:00
struct pca963x_chipdef * chipdef = chip - > chipdef ;
struct pca963x_led * led = chip - > leds ;
struct device * dev = & client - > dev ;
2019-03-25 17:02:07 +03:00
struct fwnode_handle * child ;
2020-09-20 03:24:59 +03:00
bool hw_blink ;
s32 mode2 ;
u32 reg ;
int ret ;
2013-06-26 16:52:49 +04:00
2020-09-20 03:24:59 +03:00
if ( device_property_read_u32 ( dev , " nxp,period-scale " ,
& chipdef - > scaling ) )
chipdef - > scaling = 1000 ;
2013-06-26 16:52:49 +04:00
2020-09-20 03:24:59 +03:00
hw_blink = device_property_read_bool ( dev , " nxp,hw-blink " ) ;
2013-06-26 16:52:49 +04:00
2020-09-20 03:24:59 +03:00
mode2 = i2c_smbus_read_byte_data ( client , PCA963X_MODE2 ) ;
if ( mode2 < 0 )
return mode2 ;
2013-06-26 16:52:49 +04:00
2020-09-20 03:24:59 +03:00
/* default to open-drain unless totem pole (push-pull) is specified */
if ( device_property_read_bool ( dev , " nxp,totem-pole " ) )
mode2 | = PCA963X_MODE2_OUTDRV ;
else
mode2 & = ~ PCA963X_MODE2_OUTDRV ;
2019-03-25 17:02:07 +03:00
2020-09-20 03:24:59 +03:00
/* default to non-inverted output, unless inverted is specified */
if ( device_property_read_bool ( dev , " nxp,inverted-out " ) )
mode2 | = PCA963X_MODE2_INVRT ;
else
mode2 & = ~ PCA963X_MODE2_INVRT ;
2019-03-25 17:02:07 +03:00
2020-09-20 03:24:59 +03:00
ret = i2c_smbus_write_byte_data ( client , PCA963X_MODE2 , mode2 ) ;
if ( ret < 0 )
return ret ;
2019-03-25 17:02:07 +03:00
2020-09-20 03:24:59 +03:00
device_for_each_child_node ( dev , child ) {
2020-09-20 03:25:00 +03:00
struct led_init_data init_data = { } ;
char default_label [ 32 ] ;
2020-09-20 03:24:59 +03:00
ret = fwnode_property_read_u32 ( child , " reg " , & reg ) ;
if ( ret | | reg > = chipdef - > n_leds ) {
dev_err ( dev , " Invalid 'reg' property for node %pfw \n " ,
child ) ;
ret = - EINVAL ;
goto err ;
}
2013-06-26 16:52:49 +04:00
2020-09-20 03:24:59 +03:00
led - > led_num = reg ;
led - > chip = chip ;
led - > led_cdev . brightness_set_blocking = pca963x_led_set ;
if ( hw_blink )
led - > led_cdev . blink_set = pca963x_blink_set ;
2013-07-25 21:16:41 +04:00
2020-09-20 03:25:00 +03:00
init_data . fwnode = child ;
/* for backwards compatibility */
init_data . devicename = " pca963x " ;
snprintf ( default_label , sizeof ( default_label ) , " %d:%.2x:%u " ,
client - > adapter - > nr , client - > addr , reg ) ;
init_data . default_label = default_label ;
ret = devm_led_classdev_register_ext ( dev , & led - > led_cdev ,
& init_data ) ;
2020-09-20 03:24:59 +03:00
if ( ret ) {
dev_err ( dev , " Failed to register LED for node %pfw \n " ,
child ) ;
goto err ;
}
2016-10-13 16:16:12 +03:00
2020-09-20 03:24:59 +03:00
+ + led ;
}
2017-04-27 09:37:33 +03:00
2020-09-20 03:24:59 +03:00
return 0 ;
err :
fwnode_handle_put ( child ) ;
return ret ;
2013-06-26 16:52:49 +04:00
}
2013-08-15 01:23:50 +04:00
static const struct of_device_id of_pca963x_match [ ] = {
2013-08-15 01:23:47 +04:00
{ . compatible = " nxp,pca9632 " , } ,
{ . compatible = " nxp,pca9633 " , } ,
{ . compatible = " nxp,pca9634 " , } ,
2014-07-03 09:50:34 +04:00
{ . compatible = " nxp,pca9635 " , } ,
2013-06-26 16:52:49 +04:00
{ } ,
} ;
2015-08-25 09:31:16 +03:00
MODULE_DEVICE_TABLE ( of , of_pca963x_match ) ;
2013-06-26 16:52:49 +04:00
2013-08-15 01:23:50 +04:00
static int pca963x_probe ( struct i2c_client * client ,
2020-09-20 03:24:54 +03:00
const struct i2c_device_id * id )
2012-03-24 02:02:10 +04:00
{
2020-09-20 03:24:54 +03:00
struct device * dev = & client - > dev ;
2020-09-20 03:24:56 +03:00
struct pca963x_chipdef * chipdef ;
struct pca963x * chip ;
2020-09-20 03:24:59 +03:00
int i , count ;
2012-03-24 02:02:10 +04:00
2020-09-20 03:24:56 +03:00
chipdef = & pca963x_chipdefs [ id - > driver_data ] ;
2012-03-24 02:02:10 +04:00
2020-09-20 03:24:59 +03:00
count = device_get_child_node_count ( dev ) ;
if ( ! count | | count > chipdef - > n_leds ) {
dev_err ( dev , " Node %pfw must define between 1 and %d LEDs \n " ,
dev_fwnode ( dev ) , chipdef - > n_leds ) ;
2013-08-15 01:23:47 +04:00
return - EINVAL ;
2012-03-24 02:02:10 +04:00
}
2020-09-20 03:24:59 +03:00
chip = devm_kzalloc ( dev , struct_size ( chip , leds , count ) , GFP_KERNEL ) ;
2020-09-20 03:24:56 +03:00
if ( ! chip )
2013-08-15 01:23:49 +04:00
return - ENOMEM ;
2012-03-24 02:02:10 +04:00
2020-09-20 03:24:56 +03:00
i2c_set_clientdata ( client , chip ) ;
2013-08-15 01:23:49 +04:00
2020-09-20 03:24:56 +03:00
mutex_init ( & chip - > mutex ) ;
chip - > chipdef = chipdef ;
chip - > client = client ;
2013-08-15 01:23:49 +04:00
/* Turn off LEDs by default*/
2020-09-20 03:24:56 +03:00
for ( i = 0 ; i < chipdef - > n_leds / 4 ; i + + )
i2c_smbus_write_byte_data ( client , chipdef - > ledout_base + i , 0x00 ) ;
2012-03-24 02:02:10 +04:00
2016-10-29 04:20:42 +03:00
/* Disable LED all-call address, and power down initially */
i2c_smbus_write_byte_data ( client , PCA963X_MODE1 , BIT ( 4 ) ) ;
2012-03-24 02:02:10 +04:00
2020-09-20 03:24:59 +03:00
return pca963x_register_leds ( client , chip ) ;
2012-03-24 02:02:10 +04:00
}
2013-08-15 01:23:50 +04:00
static struct i2c_driver pca963x_driver = {
2012-03-24 02:02:10 +04:00
. driver = {
2013-08-15 01:23:50 +04:00
. name = " leds-pca963x " ,
2019-03-25 17:02:07 +03:00
. of_match_table = of_pca963x_match ,
2012-03-24 02:02:10 +04:00
} ,
2013-08-15 01:23:50 +04:00
. probe = pca963x_probe ,
. id_table = pca963x_id ,
2012-03-24 02:02:10 +04:00
} ;
2013-08-15 01:23:50 +04:00
module_i2c_driver ( pca963x_driver ) ;
2012-03-24 02:02:10 +04:00
MODULE_AUTHOR ( " Peter Meerwald <p.meerwald@bct-electronic.com> " ) ;
2013-08-15 01:23:50 +04:00
MODULE_DESCRIPTION ( " PCA963X LED driver " ) ;
2012-03-24 02:02:10 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;