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 >
2013-08-15 01:23:47 +04:00
* Author : Ricardo Ribalda < ricardo . ribalda @ gmail . com >
2012-03-24 02:02:10 +04:00
*
* Based on leds - pca955x . c
*
* This file is subject to the terms and conditions of version 2 of
* the GNU General Public License . See the file COPYING in the main
* directory of this archive for more details .
*
* 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>
# include <linux/workqueue.h>
# include <linux/slab.h>
2013-06-26 16:52:49 +04:00
# include <linux/of.h>
2013-08-15 01:23:50 +04:00
# include <linux/platform_data/leds-pca963x.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
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 ;
} ;
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
2013-08-15 01:23:50 +04:00
enum pca963x_cmd {
2013-07-25 21:16:41 +04:00
BRIGHTNESS_SET ,
BLINK_SET ,
} ;
2013-08-15 01:23:50 +04:00
struct pca963x_led ;
2013-08-15 01:23:49 +04:00
2013-08-15 01:23:50 +04:00
struct pca963x {
struct pca963x_chipdef * chipdef ;
2013-08-15 01:23:49 +04:00
struct mutex mutex ;
struct i2c_client * client ;
2013-08-15 01:23:50 +04:00
struct pca963x_led * leds ;
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 work_struct work ;
enum led_brightness brightness ;
struct led_classdev led_cdev ;
2014-07-03 09:50:34 +04:00
int led_num ; /* 0 .. 15 potentially */
2013-08-15 01:23:50 +04:00
enum pca963x_cmd cmd ;
2012-03-24 02:02:10 +04:00
char name [ 32 ] ;
2013-07-25 21:16:41 +04:00
u8 gdc ;
u8 gfrq ;
2012-03-24 02:02:10 +04:00
} ;
2013-08-15 01:23:50 +04:00
static void pca963x_brightness_work ( struct pca963x_led * pca963x )
2012-03-24 02:02:10 +04:00
{
2013-08-15 01:23:50 +04:00
u8 ledout_addr = pca963x - > chip - > chipdef - > ledout_base
+ ( pca963x - > led_num / 4 ) ;
2013-08-15 01:23:47 +04:00
u8 ledout ;
2013-08-15 01:23:50 +04:00
int shift = 2 * ( pca963x - > led_num % 4 ) ;
2012-03-24 02:02:10 +04:00
u8 mask = 0x3 < < shift ;
2013-08-15 01:23:50 +04:00
mutex_lock ( & pca963x - > chip - > mutex ) ;
ledout = i2c_smbus_read_byte_data ( pca963x - > chip - > client , ledout_addr ) ;
switch ( pca963x - > brightness ) {
2012-03-24 02:02:10 +04:00
case LED_FULL :
2013-08-15 01:23:50 +04:00
i2c_smbus_write_byte_data ( pca963x - > chip - > client , ledout_addr ,
( ledout & ~ mask ) | ( PCA963X_LED_ON < < shift ) ) ;
2012-03-24 02:02:10 +04:00
break ;
case LED_OFF :
2013-08-15 01:23:50 +04:00
i2c_smbus_write_byte_data ( pca963x - > chip - > client , ledout_addr ,
2012-03-24 02:02:10 +04:00
ledout & ~ mask ) ;
break ;
default :
2013-08-15 01:23:50 +04:00
i2c_smbus_write_byte_data ( pca963x - > chip - > client ,
PCA963X_PWM_BASE + pca963x - > led_num ,
pca963x - > brightness ) ;
i2c_smbus_write_byte_data ( pca963x - > chip - > client , ledout_addr ,
( ledout & ~ mask ) | ( PCA963X_LED_PWM < < shift ) ) ;
2012-03-24 02:02:10 +04:00
break ;
}
2013-08-15 01:23:50 +04:00
mutex_unlock ( & pca963x - > chip - > mutex ) ;
2012-03-24 02:02:10 +04:00
}
2013-08-15 01:23:50 +04:00
static void pca963x_blink_work ( struct pca963x_led * pca963x )
2013-07-25 21:16:41 +04:00
{
2013-08-15 01:23:50 +04:00
u8 ledout_addr = pca963x - > chip - > chipdef - > ledout_base +
( pca963x - > led_num / 4 ) ;
2013-08-15 01:23:49 +04:00
u8 ledout ;
2013-08-15 01:23:50 +04:00
u8 mode2 = i2c_smbus_read_byte_data ( pca963x - > chip - > client ,
PCA963X_MODE2 ) ;
int shift = 2 * ( pca963x - > led_num % 4 ) ;
2013-07-25 21:16:41 +04:00
u8 mask = 0x3 < < shift ;
2013-08-15 01:23:50 +04:00
i2c_smbus_write_byte_data ( pca963x - > chip - > client ,
pca963x - > chip - > chipdef - > grppwm , pca963x - > gdc ) ;
2013-07-25 21:16:41 +04:00
2013-08-15 01:23:50 +04:00
i2c_smbus_write_byte_data ( pca963x - > chip - > client ,
pca963x - > chip - > chipdef - > grpfreq , pca963x - > gfrq ) ;
2013-07-25 21:16:41 +04:00
2013-08-15 01:23:50 +04:00
if ( ! ( mode2 & PCA963X_MODE2_DMBLNK ) )
i2c_smbus_write_byte_data ( pca963x - > chip - > client , PCA963X_MODE2 ,
mode2 | PCA963X_MODE2_DMBLNK ) ;
2013-07-25 21:16:41 +04:00
2013-08-15 01:23:50 +04:00
mutex_lock ( & pca963x - > chip - > mutex ) ;
ledout = i2c_smbus_read_byte_data ( pca963x - > chip - > client , ledout_addr ) ;
if ( ( ledout & mask ) ! = ( PCA963X_LED_GRP_PWM < < shift ) )
i2c_smbus_write_byte_data ( pca963x - > chip - > client , ledout_addr ,
( ledout & ~ mask ) | ( PCA963X_LED_GRP_PWM < < shift ) ) ;
mutex_unlock ( & pca963x - > chip - > mutex ) ;
2013-07-25 21:16:41 +04:00
}
2013-08-15 01:23:50 +04:00
static void pca963x_work ( struct work_struct * work )
2013-07-25 21:16:41 +04:00
{
2013-08-15 01:23:50 +04:00
struct pca963x_led * pca963x = container_of ( work ,
struct pca963x_led , work ) ;
2013-07-25 21:16:41 +04:00
2013-08-15 01:23:50 +04:00
switch ( pca963x - > cmd ) {
2013-07-25 21:16:41 +04:00
case BRIGHTNESS_SET :
2013-08-15 01:23:50 +04:00
pca963x_brightness_work ( pca963x ) ;
2013-07-25 21:16:41 +04:00
break ;
case BLINK_SET :
2013-08-15 01:23:50 +04:00
pca963x_blink_work ( pca963x ) ;
2013-07-25 21:16:41 +04:00
break ;
}
}
2013-08-15 01:23:50 +04:00
static void pca963x_led_set ( struct led_classdev * led_cdev ,
2012-03-24 02:02:10 +04:00
enum led_brightness value )
{
2013-08-15 01:23:50 +04:00
struct pca963x_led * pca963x ;
2012-03-24 02:02:10 +04:00
2013-08-15 01:23:50 +04:00
pca963x = container_of ( led_cdev , struct pca963x_led , led_cdev ) ;
2012-03-24 02:02:10 +04:00
2013-08-15 01:23:50 +04:00
pca963x - > cmd = BRIGHTNESS_SET ;
pca963x - > brightness = value ;
2012-03-24 02:02:10 +04:00
/*
* Must use workqueue for the actual I / O since I2C operations
* can sleep .
*/
2013-08-15 01:23:50 +04:00
schedule_work ( & pca963x - > work ) ;
2012-03-24 02:02:10 +04:00
}
2013-08-15 01:23:50 +04:00
static int pca963x_blink_set ( struct led_classdev * led_cdev ,
2013-07-25 21:16:41 +04:00
unsigned long * delay_on , unsigned long * delay_off )
{
2013-08-15 01:23:50 +04:00
struct pca963x_led * pca963x ;
2013-07-25 21:16:41 +04:00
unsigned long time_on , time_off , period ;
u8 gdc , gfrq ;
2013-08-15 01:23:50 +04:00
pca963x = 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 ;
}
period = time_on + time_off ;
/* 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 ;
period = time_on + time_off ;
}
/*
* From manual : duty cycle = ( GDC / 256 ) - >
* ( time_on / period ) = ( GDC / 256 ) - >
* GDC = ( ( time_on * 256 ) / period )
*/
gdc = ( time_on * 256 ) / period ;
/*
* 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 ;
2013-08-15 01:23:50 +04:00
pca963x - > cmd = BLINK_SET ;
pca963x - > gdc = gdc ;
pca963x - > gfrq = gfrq ;
2013-07-25 21:16:41 +04:00
/*
* Must use workqueue for the actual I / O since I2C operations
* can sleep .
*/
2013-08-15 01:23:50 +04:00
schedule_work ( & pca963x - > work ) ;
2013-07-25 21:16:41 +04:00
* delay_on = time_on ;
* delay_off = time_off ;
return 0 ;
}
2013-06-26 16:52:49 +04:00
# if IS_ENABLED(CONFIG_OF)
2013-08-15 01:23:50 +04:00
static struct pca963x_platform_data *
pca963x_dt_init ( struct i2c_client * client , struct pca963x_chipdef * chip )
2013-06-26 16:52:49 +04:00
{
struct device_node * np = client - > dev . of_node , * child ;
2013-08-15 01:23:50 +04:00
struct pca963x_platform_data * pdata ;
struct led_info * pca963x_leds ;
2013-06-26 16:52:49 +04:00
int count ;
count = of_get_child_count ( np ) ;
2013-08-15 01:23:47 +04:00
if ( ! count | | count > chip - > n_leds )
2013-06-26 16:52:49 +04:00
return ERR_PTR ( - ENODEV ) ;
2013-08-15 01:23:50 +04:00
pca963x_leds = devm_kzalloc ( & client - > dev ,
2013-08-15 01:23:47 +04:00
sizeof ( struct led_info ) * chip - > n_leds , GFP_KERNEL ) ;
2013-08-15 01:23:50 +04:00
if ( ! pca963x_leds )
2013-06-26 16:52:49 +04:00
return ERR_PTR ( - ENOMEM ) ;
for_each_child_of_node ( np , child ) {
struct led_info led ;
u32 reg ;
int res ;
2013-08-15 01:23:51 +04:00
res = of_property_read_u32 ( child , " reg " , & reg ) ;
if ( ( res ! = 0 ) | | ( reg > = chip - > n_leds ) )
continue ;
2013-06-26 16:52:49 +04:00
led . name =
of_get_property ( child , " label " , NULL ) ? : child - > name ;
led . default_trigger =
of_get_property ( child , " linux,default-trigger " , NULL ) ;
2013-08-15 01:23:50 +04:00
pca963x_leds [ reg ] = led ;
2013-06-26 16:52:49 +04:00
}
pdata = devm_kzalloc ( & client - > dev ,
2013-08-15 01:23:50 +04:00
sizeof ( struct pca963x_platform_data ) , GFP_KERNEL ) ;
2013-06-26 16:52:49 +04:00
if ( ! pdata )
return ERR_PTR ( - ENOMEM ) ;
2013-08-15 01:23:50 +04:00
pdata - > leds . leds = pca963x_leds ;
2013-08-15 01:23:51 +04:00
pdata - > leds . num_leds = chip - > n_leds ;
2013-06-26 16:52:49 +04:00
/* default to open-drain unless totem pole (push-pull) is specified */
if ( of_property_read_bool ( np , " nxp,totem-pole " ) )
2013-08-15 01:23:50 +04:00
pdata - > outdrv = PCA963X_TOTEM_POLE ;
2013-06-26 16:52:49 +04:00
else
2013-08-15 01:23:50 +04:00
pdata - > outdrv = PCA963X_OPEN_DRAIN ;
2013-06-26 16:52:49 +04:00
2013-07-25 21:16:41 +04:00
/* default to software blinking unless hardware blinking is specified */
if ( of_property_read_bool ( np , " nxp,hw-blink " ) )
2013-08-15 01:23:50 +04:00
pdata - > blink_type = PCA963X_HW_BLINK ;
2013-07-25 21:16:41 +04:00
else
2013-08-15 01:23:50 +04:00
pdata - > blink_type = PCA963X_SW_BLINK ;
2013-07-25 21:16:41 +04:00
2013-06-26 16:52:49 +04:00
return pdata ;
}
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
{ } ,
} ;
# else
2013-08-15 01:23:50 +04:00
static struct pca963x_platform_data *
pca963x_dt_init ( struct i2c_client * client , struct pca963x_chipdef * chip )
2013-06-26 16:52:49 +04:00
{
return ERR_PTR ( - ENODEV ) ;
}
# endif
2013-08-15 01:23:50 +04:00
static int pca963x_probe ( struct i2c_client * client ,
2012-03-24 02:02:10 +04:00
const struct i2c_device_id * id )
{
2013-08-15 01:23:50 +04:00
struct pca963x * pca963x_chip ;
struct pca963x_led * pca963x ;
struct pca963x_platform_data * pdata ;
struct pca963x_chipdef * chip ;
2012-03-24 02:02:10 +04:00
int i , err ;
2013-08-15 01:23:50 +04:00
chip = & pca963x_chipdefs [ id - > driver_data ] ;
2013-07-30 12:07:35 +04:00
pdata = dev_get_platdata ( & client - > dev ) ;
2012-03-24 02:02:10 +04:00
2013-06-26 16:52:49 +04:00
if ( ! pdata ) {
2013-08-15 01:23:50 +04:00
pdata = pca963x_dt_init ( client , chip ) ;
2013-06-26 16:52:49 +04:00
if ( IS_ERR ( pdata ) ) {
dev_warn ( & client - > dev , " could not parse configuration \n " ) ;
pdata = NULL ;
}
}
2013-08-15 01:23:47 +04:00
if ( pdata & & ( pdata - > leds . num_leds < 1 | |
pdata - > leds . num_leds > chip - > n_leds ) ) {
dev_err ( & client - > dev , " board info must claim 1-%d LEDs " ,
chip - > n_leds ) ;
return - EINVAL ;
2012-03-24 02:02:10 +04:00
}
2013-08-15 01:23:50 +04:00
pca963x_chip = devm_kzalloc ( & client - > dev , sizeof ( * pca963x_chip ) ,
2013-08-15 01:23:49 +04:00
GFP_KERNEL ) ;
2013-08-15 01:23:50 +04:00
if ( ! pca963x_chip )
2013-08-15 01:23:49 +04:00
return - ENOMEM ;
2013-08-15 01:23:50 +04:00
pca963x = devm_kzalloc ( & client - > dev , chip - > n_leds * sizeof ( * pca963x ) ,
2013-08-15 01:23:47 +04:00
GFP_KERNEL ) ;
2013-08-15 01:23:50 +04:00
if ( ! pca963x )
2012-03-24 02:02:10 +04:00
return - ENOMEM ;
2013-08-15 01:23:50 +04:00
i2c_set_clientdata ( client , pca963x_chip ) ;
2013-08-15 01:23:49 +04:00
2013-08-15 01:23:50 +04:00
mutex_init ( & pca963x_chip - > mutex ) ;
pca963x_chip - > chipdef = chip ;
pca963x_chip - > client = client ;
pca963x_chip - > leds = pca963x ;
2013-08-15 01:23:49 +04:00
/* Turn off LEDs by default*/
2014-07-03 09:50:34 +04:00
for ( i = 0 ; i < chip - > n_leds / 4 ; i + + )
i2c_smbus_write_byte_data ( client , chip - > ledout_base + i , 0x00 ) ;
2012-03-24 02:02:10 +04:00
2013-08-15 01:23:47 +04:00
for ( i = 0 ; i < chip - > n_leds ; i + + ) {
2013-08-15 01:23:50 +04:00
pca963x [ i ] . led_num = i ;
pca963x [ i ] . chip = pca963x_chip ;
2012-03-24 02:02:10 +04:00
/* Platform data can specify LED names and default triggers */
2012-09-24 10:53:49 +04:00
if ( pdata & & i < pdata - > leds . num_leds ) {
if ( pdata - > leds . leds [ i ] . name )
2013-08-15 01:23:50 +04:00
snprintf ( pca963x [ i ] . name ,
sizeof ( pca963x [ i ] . name ) , " pca963x:%s " ,
2012-09-24 10:53:49 +04:00
pdata - > leds . leds [ i ] . name ) ;
if ( pdata - > leds . leds [ i ] . default_trigger )
2013-08-15 01:23:50 +04:00
pca963x [ i ] . led_cdev . default_trigger =
2012-09-24 10:53:49 +04:00
pdata - > leds . leds [ i ] . default_trigger ;
2012-03-24 02:02:10 +04:00
}
2013-08-15 01:23:48 +04:00
if ( ! pdata | | i > = pdata - > leds . num_leds | |
! pdata - > leds . leds [ i ] . name )
2013-08-15 01:23:50 +04:00
snprintf ( pca963x [ i ] . name , sizeof ( pca963x [ i ] . name ) ,
" pca963x:%d:%.2x:%d " , client - > adapter - > nr ,
2013-08-15 01:23:48 +04:00
client - > addr , i ) ;
2012-03-24 02:02:10 +04:00
2013-08-15 01:23:50 +04:00
pca963x [ i ] . led_cdev . name = pca963x [ i ] . name ;
pca963x [ i ] . led_cdev . brightness_set = pca963x_led_set ;
2012-03-24 02:02:10 +04:00
2013-08-15 01:23:50 +04:00
if ( pdata & & pdata - > blink_type = = PCA963X_HW_BLINK )
pca963x [ i ] . led_cdev . blink_set = pca963x_blink_set ;
2013-07-25 21:16:41 +04:00
2013-08-15 01:23:50 +04:00
INIT_WORK ( & pca963x [ i ] . work , pca963x_work ) ;
2012-03-24 02:02:10 +04:00
2013-08-15 01:23:50 +04:00
err = led_classdev_register ( & client - > dev , & pca963x [ i ] . led_cdev ) ;
2012-03-24 02:02:10 +04:00
if ( err < 0 )
goto exit ;
}
/* Disable LED all-call address and set normal mode */
2013-08-15 01:23:50 +04:00
i2c_smbus_write_byte_data ( client , PCA963X_MODE1 , 0x00 ) ;
2012-03-24 02:02:10 +04:00
2012-09-24 10:53:49 +04:00
/* Configure output: open-drain or totem pole (push-pull) */
2013-08-15 01:23:50 +04:00
if ( pdata & & pdata - > outdrv = = PCA963X_OPEN_DRAIN )
i2c_smbus_write_byte_data ( client , PCA963X_MODE2 , 0x01 ) ;
2012-09-24 10:53:49 +04:00
2012-03-24 02:02:10 +04:00
return 0 ;
exit :
while ( i - - ) {
2013-08-15 01:23:50 +04:00
led_classdev_unregister ( & pca963x [ i ] . led_cdev ) ;
cancel_work_sync ( & pca963x [ i ] . work ) ;
2012-03-24 02:02:10 +04:00
}
return err ;
}
2013-08-15 01:23:50 +04:00
static int pca963x_remove ( struct i2c_client * client )
2012-03-24 02:02:10 +04:00
{
2013-08-15 01:23:50 +04:00
struct pca963x * pca963x = i2c_get_clientdata ( client ) ;
2012-03-24 02:02:10 +04:00
int i ;
2013-08-15 01:23:50 +04:00
for ( i = 0 ; i < pca963x - > chipdef - > n_leds ; i + + ) {
led_classdev_unregister ( & pca963x - > leds [ i ] . led_cdev ) ;
cancel_work_sync ( & pca963x - > leds [ i ] . work ) ;
2013-08-15 01:23:49 +04:00
}
2012-03-24 02:02:10 +04:00
return 0 ;
}
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 " ,
2012-03-24 02:02:10 +04:00
. owner = THIS_MODULE ,
2013-08-15 01:23:50 +04:00
. of_match_table = of_match_ptr ( of_pca963x_match ) ,
2012-03-24 02:02:10 +04:00
} ,
2013-08-15 01:23:50 +04:00
. probe = pca963x_probe ,
. remove = pca963x_remove ,
. 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 " ) ;