2010-11-12 01:05:22 +03:00
/*
2014-05-05 15:46:54 +04:00
* lp5523 . c - LP5523 , LP55231 LED Driver
2010-11-12 01:05:22 +03:00
*
* Copyright ( C ) 2010 Nokia Corporation
2013-02-05 14:28:00 +04:00
* Copyright ( C ) 2012 Texas Instruments
2010-11-12 01:05:22 +03:00
*
* Contact : Samu Onkalo < samu . p . onkalo @ nokia . com >
2013-02-05 14:28:00 +04:00
* Milo ( Woogyom ) Kim < milo . kim @ ti . com >
2010-11-12 01:05:22 +03:00
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA
* 02110 - 1301 USA
*/
# include <linux/delay.h>
2013-02-05 14:26:14 +04:00
# include <linux/firmware.h>
# include <linux/i2c.h>
2010-11-12 01:05:22 +03:00
# include <linux/leds.h>
2013-02-05 14:26:14 +04:00
# include <linux/module.h>
# include <linux/mutex.h>
2013-09-28 15:38:30 +04:00
# include <linux/of.h>
2013-02-05 13:03:08 +04:00
# include <linux/platform_data/leds-lp55xx.h>
2013-02-05 14:26:14 +04:00
# include <linux/slab.h>
2013-02-05 13:03:08 +04:00
# include "leds-lp55xx-common.h"
2010-11-12 01:05:22 +03:00
2014-01-10 03:14:05 +04:00
# define LP5523_PROGRAM_LENGTH 32 /* bytes */
/* Memory is used like this:
0x00 engine 1 program
0x10 engine 2 program
0x20 engine 3 program
0x30 engine 1 muxing info
0x40 engine 2 muxing info
0x50 engine 3 muxing info
*/
2013-02-05 14:25:26 +04:00
# define LP5523_MAX_LEDS 9
/* Registers */
2010-11-12 01:05:22 +03:00
# define LP5523_REG_ENABLE 0x00
# define LP5523_REG_OP_MODE 0x01
# define LP5523_REG_ENABLE_LEDS_MSB 0x04
# define LP5523_REG_ENABLE_LEDS_LSB 0x05
# define LP5523_REG_LED_PWM_BASE 0x16
# define LP5523_REG_LED_CURRENT_BASE 0x26
# define LP5523_REG_CONFIG 0x36
2013-02-05 14:25:26 +04:00
# define LP5523_REG_STATUS 0x3A
# define LP5523_REG_RESET 0x3D
2010-11-12 01:05:22 +03:00
# define LP5523_REG_LED_TEST_CTRL 0x41
# define LP5523_REG_LED_TEST_ADC 0x42
2013-08-08 08:57:35 +04:00
# define LP5523_REG_CH1_PROG_START 0x4C
# define LP5523_REG_CH2_PROG_START 0x4D
# define LP5523_REG_CH3_PROG_START 0x4E
2013-02-05 14:25:26 +04:00
# define LP5523_REG_PROG_PAGE_SEL 0x4F
2010-11-12 01:05:22 +03:00
# define LP5523_REG_PROG_MEM 0x50
2013-02-05 14:25:26 +04:00
/* Bit description in registers */
2010-11-12 01:05:22 +03:00
# define LP5523_ENABLE 0x40
# define LP5523_AUTO_INC 0x40
# define LP5523_PWR_SAVE 0x20
# define LP5523_PWM_PWR_SAVE 0x04
# define LP5523_CP_AUTO 0x18
# define LP5523_AUTO_CLK 0x02
2013-02-05 14:25:26 +04:00
2010-11-12 01:05:22 +03:00
# define LP5523_EN_LEDTEST 0x80
# define LP5523_LEDTEST_DONE 0x80
2013-02-05 13:08:49 +04:00
# define LP5523_RESET 0xFF
2010-11-12 01:05:22 +03:00
# define LP5523_ADC_SHORTCIRC_LIM 80
# define LP5523_EXT_CLK_USED 0x08
2013-08-08 08:57:35 +04:00
# define LP5523_ENG_STATUS_MASK 0x07
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:51 +04:00
/* Memory Page Selection */
# define LP5523_PAGE_ENG1 0
# define LP5523_PAGE_ENG2 1
# define LP5523_PAGE_ENG3 2
2013-08-08 09:02:29 +04:00
# define LP5523_PAGE_MUX1 3
# define LP5523_PAGE_MUX2 4
# define LP5523_PAGE_MUX3 5
2013-02-05 14:18:51 +04:00
/* Program Memory Operations */
# define LP5523_MODE_ENG1_M 0x30 /* Operation Mode Register */
# define LP5523_MODE_ENG2_M 0x0C
# define LP5523_MODE_ENG3_M 0x03
# define LP5523_LOAD_ENG1 0x10
# define LP5523_LOAD_ENG2 0x04
# define LP5523_LOAD_ENG3 0x01
# define LP5523_ENG1_IS_LOADING(mode) \
( ( mode & LP5523_MODE_ENG1_M ) = = LP5523_LOAD_ENG1 )
# define LP5523_ENG2_IS_LOADING(mode) \
( ( mode & LP5523_MODE_ENG2_M ) = = LP5523_LOAD_ENG2 )
# define LP5523_ENG3_IS_LOADING(mode) \
( ( mode & LP5523_MODE_ENG3_M ) = = LP5523_LOAD_ENG3 )
# define LP5523_EXEC_ENG1_M 0x30 /* Enable Register */
# define LP5523_EXEC_ENG2_M 0x0C
# define LP5523_EXEC_ENG3_M 0x03
# define LP5523_EXEC_M 0x3F
# define LP5523_RUN_ENG1 0x20
# define LP5523_RUN_ENG2 0x08
# define LP5523_RUN_ENG3 0x02
2013-08-08 09:02:29 +04:00
# define LED_ACTIVE(mux, led) (!!(mux & (0x0001 << led)))
2012-09-04 11:06:18 +04:00
enum lp5523_chip_id {
LP5523 ,
LP55231 ,
} ;
2013-08-08 08:57:35 +04:00
static int lp5523_init_program_engine ( struct lp55xx_chip * chip ) ;
2013-02-05 14:18:51 +04:00
static inline void lp5523_wait_opmode_done ( void )
{
usleep_range ( 1000 , 2000 ) ;
}
2013-02-05 14:09:32 +04:00
static void lp5523_set_led_current ( struct lp55xx_led * led , u8 led_current )
{
led - > led_current = led_current ;
lp55xx_write ( led - > chip , LP5523_REG_LED_CURRENT_BASE + led - > chan_nr ,
led_current ) ;
}
2013-02-05 13:57:36 +04:00
static int lp5523_post_init_device ( struct lp55xx_chip * chip )
2010-11-12 01:05:22 +03:00
{
2013-02-05 13:57:36 +04:00
int ret ;
2010-11-12 01:05:22 +03:00
2013-02-05 13:57:36 +04:00
ret = lp55xx_write ( chip , LP5523_REG_ENABLE , LP5523_ENABLE ) ;
2013-02-05 13:04:50 +04:00
if ( ret )
return ret ;
2010-11-12 01:05:22 +03:00
2010-11-24 23:57:04 +03:00
/* Chip startup time is 500 us, 1 - 2 ms gives some margin */
usleep_range ( 1000 , 2000 ) ;
2010-11-12 01:05:22 +03:00
2013-02-05 13:57:36 +04:00
ret = lp55xx_write ( chip , LP5523_REG_CONFIG ,
2010-11-12 01:05:22 +03:00
LP5523_AUTO_INC | LP5523_PWR_SAVE |
LP5523_CP_AUTO | LP5523_AUTO_CLK |
LP5523_PWM_PWR_SAVE ) ;
2013-02-05 13:04:50 +04:00
if ( ret )
return ret ;
2010-11-12 01:05:22 +03:00
/* turn on all leds */
2013-02-05 13:57:36 +04:00
ret = lp55xx_write ( chip , LP5523_REG_ENABLE_LEDS_MSB , 0x01 ) ;
2013-02-05 13:04:50 +04:00
if ( ret )
2012-10-29 05:45:11 +04:00
return ret ;
2013-08-08 08:57:35 +04:00
ret = lp55xx_write ( chip , LP5523_REG_ENABLE_LEDS_LSB , 0xff ) ;
if ( ret )
return ret ;
return lp5523_init_program_engine ( chip ) ;
2010-11-12 01:05:22 +03:00
}
2013-02-05 14:18:51 +04:00
static void lp5523_load_engine ( struct lp55xx_chip * chip )
2010-11-12 01:05:22 +03:00
{
2013-02-05 14:18:51 +04:00
enum lp55xx_engine_index idx = chip - > engine_idx ;
u8 mask [ ] = {
[ LP55XX_ENGINE_1 ] = LP5523_MODE_ENG1_M ,
[ LP55XX_ENGINE_2 ] = LP5523_MODE_ENG2_M ,
[ LP55XX_ENGINE_3 ] = LP5523_MODE_ENG3_M ,
} ;
u8 val [ ] = {
[ LP55XX_ENGINE_1 ] = LP5523_LOAD_ENG1 ,
[ LP55XX_ENGINE_2 ] = LP5523_LOAD_ENG2 ,
[ LP55XX_ENGINE_3 ] = LP5523_LOAD_ENG3 ,
} ;
2013-08-08 08:56:01 +04:00
lp55xx_update_bits ( chip , LP5523_REG_OP_MODE , mask [ idx ] , val [ idx ] ) ;
lp5523_wait_opmode_done ( ) ;
}
static void lp5523_load_engine_and_select_page ( struct lp55xx_chip * chip )
{
enum lp55xx_engine_index idx = chip - > engine_idx ;
2013-02-05 14:18:51 +04:00
u8 page_sel [ ] = {
[ LP55XX_ENGINE_1 ] = LP5523_PAGE_ENG1 ,
[ LP55XX_ENGINE_2 ] = LP5523_PAGE_ENG2 ,
[ LP55XX_ENGINE_3 ] = LP5523_PAGE_ENG3 ,
} ;
2013-08-08 08:56:01 +04:00
lp5523_load_engine ( chip ) ;
2013-02-05 14:18:51 +04:00
lp55xx_write ( chip , LP5523_REG_PROG_PAGE_SEL , page_sel [ idx ] ) ;
2010-11-12 01:05:22 +03:00
}
2013-11-21 10:13:34 +04:00
static void lp5523_stop_all_engines ( struct lp55xx_chip * chip )
2010-11-12 01:05:22 +03:00
{
2013-02-05 14:18:51 +04:00
lp55xx_write ( chip , LP5523_REG_OP_MODE , 0 ) ;
lp5523_wait_opmode_done ( ) ;
}
2010-11-12 01:05:22 +03:00
2013-11-21 10:13:34 +04:00
static void lp5523_stop_engine ( struct lp55xx_chip * chip )
{
enum lp55xx_engine_index idx = chip - > engine_idx ;
u8 mask [ ] = {
[ LP55XX_ENGINE_1 ] = LP5523_MODE_ENG1_M ,
[ LP55XX_ENGINE_2 ] = LP5523_MODE_ENG2_M ,
[ LP55XX_ENGINE_3 ] = LP5523_MODE_ENG3_M ,
} ;
lp55xx_update_bits ( chip , LP5523_REG_OP_MODE , mask [ idx ] , 0 ) ;
lp5523_wait_opmode_done ( ) ;
}
2013-02-05 14:18:51 +04:00
static void lp5523_turn_off_channels ( struct lp55xx_chip * chip )
{
int i ;
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:51 +04:00
for ( i = 0 ; i < LP5523_MAX_LEDS ; i + + )
lp55xx_write ( chip , LP5523_REG_LED_PWM_BASE + i , 0 ) ;
2010-11-12 01:05:22 +03:00
}
2013-02-05 14:18:51 +04:00
static void lp5523_run_engine ( struct lp55xx_chip * chip , bool start )
2010-11-12 01:05:22 +03:00
{
2013-02-05 14:18:51 +04:00
int ret ;
u8 mode ;
u8 exec ;
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:51 +04:00
/* stop engine */
if ( ! start ) {
lp5523_stop_engine ( chip ) ;
lp5523_turn_off_channels ( chip ) ;
return ;
}
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:51 +04:00
/*
* To run the engine ,
* operation mode and enable register should updated at the same time
*/
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:51 +04:00
ret = lp55xx_read ( chip , LP5523_REG_OP_MODE , & mode ) ;
if ( ret )
return ;
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:51 +04:00
ret = lp55xx_read ( chip , LP5523_REG_ENABLE , & exec ) ;
if ( ret )
return ;
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:51 +04:00
/* change operation mode to RUN only when each engine is loading */
if ( LP5523_ENG1_IS_LOADING ( mode ) ) {
mode = ( mode & ~ LP5523_MODE_ENG1_M ) | LP5523_RUN_ENG1 ;
exec = ( exec & ~ LP5523_EXEC_ENG1_M ) | LP5523_RUN_ENG1 ;
}
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:51 +04:00
if ( LP5523_ENG2_IS_LOADING ( mode ) ) {
mode = ( mode & ~ LP5523_MODE_ENG2_M ) | LP5523_RUN_ENG2 ;
exec = ( exec & ~ LP5523_EXEC_ENG2_M ) | LP5523_RUN_ENG2 ;
}
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:51 +04:00
if ( LP5523_ENG3_IS_LOADING ( mode ) ) {
mode = ( mode & ~ LP5523_MODE_ENG3_M ) | LP5523_RUN_ENG3 ;
exec = ( exec & ~ LP5523_EXEC_ENG3_M ) | LP5523_RUN_ENG3 ;
}
lp55xx_write ( chip , LP5523_REG_OP_MODE , mode ) ;
lp5523_wait_opmode_done ( ) ;
lp55xx_update_bits ( chip , LP5523_REG_ENABLE , LP5523_EXEC_M , exec ) ;
2010-11-12 01:05:22 +03:00
}
2013-08-08 08:57:35 +04:00
static int lp5523_init_program_engine ( struct lp55xx_chip * chip )
{
int i ;
int j ;
int ret ;
u8 status ;
/* one pattern per engine setting LED MUX start and stop addresses */
static const u8 pattern [ ] [ LP5523_PROGRAM_LENGTH ] = {
{ 0x9c , 0x30 , 0x9c , 0xb0 , 0x9d , 0x80 , 0xd8 , 0x00 , 0 } ,
{ 0x9c , 0x40 , 0x9c , 0xc0 , 0x9d , 0x80 , 0xd8 , 0x00 , 0 } ,
{ 0x9c , 0x50 , 0x9c , 0xd0 , 0x9d , 0x80 , 0xd8 , 0x00 , 0 } ,
} ;
/* hardcode 32 bytes of memory for each engine from program memory */
ret = lp55xx_write ( chip , LP5523_REG_CH1_PROG_START , 0x00 ) ;
if ( ret )
return ret ;
ret = lp55xx_write ( chip , LP5523_REG_CH2_PROG_START , 0x10 ) ;
if ( ret )
return ret ;
ret = lp55xx_write ( chip , LP5523_REG_CH3_PROG_START , 0x20 ) ;
if ( ret )
return ret ;
/* write LED MUX address space for each engine */
for ( i = LP55XX_ENGINE_1 ; i < = LP55XX_ENGINE_3 ; i + + ) {
chip - > engine_idx = i ;
lp5523_load_engine_and_select_page ( chip ) ;
for ( j = 0 ; j < LP5523_PROGRAM_LENGTH ; j + + ) {
ret = lp55xx_write ( chip , LP5523_REG_PROG_MEM + j ,
pattern [ i - 1 ] [ j ] ) ;
if ( ret )
goto out ;
}
}
lp5523_run_engine ( chip , true ) ;
/* Let the programs run for couple of ms and check the engine status */
usleep_range ( 3000 , 6000 ) ;
lp55xx_read ( chip , LP5523_REG_STATUS , & status ) ;
status & = LP5523_ENG_STATUS_MASK ;
if ( status ! = LP5523_ENG_STATUS_MASK ) {
dev_err ( & chip - > cl - > dev ,
" cound not configure LED engine, status = 0x%.2x \n " ,
status ) ;
ret = - 1 ;
}
out :
2013-11-21 10:13:34 +04:00
lp5523_stop_all_engines ( chip ) ;
2013-08-08 08:57:35 +04:00
return ret ;
}
2013-02-05 14:18:51 +04:00
static int lp5523_update_program_memory ( struct lp55xx_chip * chip ,
const u8 * data , size_t size )
2010-11-12 01:05:22 +03:00
{
2013-02-05 14:18:51 +04:00
u8 pattern [ LP5523_PROGRAM_LENGTH ] = { 0 } ;
unsigned cmd ;
char c [ 3 ] ;
int nrchars ;
int ret ;
2013-08-08 09:09:24 +04:00
int offset = 0 ;
int i = 0 ;
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:51 +04:00
while ( ( offset < size - 1 ) & & ( i < LP5523_PROGRAM_LENGTH ) ) {
/* separate sscanfs because length is working only for %s */
ret = sscanf ( data + offset , " %2s%n " , c , & nrchars ) ;
if ( ret ! = 1 )
goto err ;
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:51 +04:00
ret = sscanf ( c , " %2x " , & cmd ) ;
if ( ret ! = 1 )
goto err ;
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:51 +04:00
pattern [ i ] = ( u8 ) cmd ;
offset + = nrchars ;
i + + ;
}
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:51 +04:00
/* Each instruction is 16bit long. Check that length is even */
if ( i % 2 )
goto err ;
2010-11-12 01:05:22 +03:00
2013-08-08 09:09:24 +04:00
for ( i = 0 ; i < LP5523_PROGRAM_LENGTH ; i + + ) {
2013-08-08 09:02:29 +04:00
ret = lp55xx_write ( chip , LP5523_REG_PROG_MEM + i , pattern [ i ] ) ;
2013-12-03 05:21:44 +04:00
if ( ret )
2013-08-08 09:02:29 +04:00
return - EINVAL ;
}
return size ;
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:51 +04:00
err :
dev_err ( & chip - > cl - > dev , " wrong pattern format \n " ) ;
return - EINVAL ;
2010-11-12 01:05:22 +03:00
}
2013-02-05 14:18:51 +04:00
static void lp5523_firmware_loaded ( struct lp55xx_chip * chip )
2010-11-12 01:05:22 +03:00
{
2013-02-05 14:18:51 +04:00
const struct firmware * fw = chip - > fw ;
2011-01-13 03:59:18 +03:00
2013-02-05 14:18:51 +04:00
if ( fw - > size > LP5523_PROGRAM_LENGTH ) {
dev_err ( & chip - > cl - > dev , " firmware data size overflow: %zu \n " ,
fw - > size ) ;
return ;
}
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:51 +04:00
/*
* Program momery sequence
* 1 ) set engine mode to " LOAD "
* 2 ) write firmware data into program memory
*/
2010-11-12 01:05:22 +03:00
2013-08-08 08:56:01 +04:00
lp5523_load_engine_and_select_page ( chip ) ;
2013-02-05 14:18:51 +04:00
lp5523_update_program_memory ( chip , fw - > data , fw - > size ) ;
2010-11-12 01:05:22 +03:00
}
2013-08-08 09:02:29 +04:00
static ssize_t show_engine_mode ( struct device * dev ,
struct device_attribute * attr ,
char * buf , int nr )
{
struct lp55xx_led * led = i2c_get_clientdata ( to_i2c_client ( dev ) ) ;
struct lp55xx_chip * chip = led - > chip ;
enum lp55xx_engine_mode mode = chip - > engines [ nr - 1 ] . mode ;
switch ( mode ) {
case LP55XX_ENGINE_RUN :
return sprintf ( buf , " run \n " ) ;
case LP55XX_ENGINE_LOAD :
return sprintf ( buf , " load \n " ) ;
case LP55XX_ENGINE_DISABLED :
default :
return sprintf ( buf , " disabled \n " ) ;
}
}
show_mode ( 1 )
show_mode ( 2 )
show_mode ( 3 )
static ssize_t store_engine_mode ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t len , int nr )
{
struct lp55xx_led * led = i2c_get_clientdata ( to_i2c_client ( dev ) ) ;
struct lp55xx_chip * chip = led - > chip ;
struct lp55xx_engine * engine = & chip - > engines [ nr - 1 ] ;
mutex_lock ( & chip - > lock ) ;
chip - > engine_idx = nr ;
if ( ! strncmp ( buf , " run " , 3 ) ) {
lp5523_run_engine ( chip , true ) ;
engine - > mode = LP55XX_ENGINE_RUN ;
} else if ( ! strncmp ( buf , " load " , 4 ) ) {
lp5523_stop_engine ( chip ) ;
lp5523_load_engine ( chip ) ;
engine - > mode = LP55XX_ENGINE_LOAD ;
} else if ( ! strncmp ( buf , " disabled " , 8 ) ) {
lp5523_stop_engine ( chip ) ;
engine - > mode = LP55XX_ENGINE_DISABLED ;
}
mutex_unlock ( & chip - > lock ) ;
return len ;
}
store_mode ( 1 )
store_mode ( 2 )
store_mode ( 3 )
static int lp5523_mux_parse ( const char * buf , u16 * mux , size_t len )
{
u16 tmp_mux = 0 ;
int i ;
len = min_t ( int , len , LP5523_MAX_LEDS ) ;
for ( i = 0 ; i < len ; i + + ) {
switch ( buf [ i ] ) {
case ' 1 ' :
tmp_mux | = ( 1 < < i ) ;
break ;
case ' 0 ' :
break ;
case ' \n ' :
i = len ;
break ;
default :
return - 1 ;
}
}
* mux = tmp_mux ;
return 0 ;
}
static void lp5523_mux_to_array ( u16 led_mux , char * array )
{
int i , pos = 0 ;
for ( i = 0 ; i < LP5523_MAX_LEDS ; i + + )
pos + = sprintf ( array + pos , " %x " , LED_ACTIVE ( led_mux , i ) ) ;
array [ pos ] = ' \0 ' ;
}
static ssize_t show_engine_leds ( struct device * dev ,
struct device_attribute * attr ,
char * buf , int nr )
{
struct lp55xx_led * led = i2c_get_clientdata ( to_i2c_client ( dev ) ) ;
struct lp55xx_chip * chip = led - > chip ;
char mux [ LP5523_MAX_LEDS + 1 ] ;
lp5523_mux_to_array ( chip - > engines [ nr - 1 ] . led_mux , mux ) ;
return sprintf ( buf , " %s \n " , mux ) ;
}
show_leds ( 1 )
show_leds ( 2 )
show_leds ( 3 )
static int lp5523_load_mux ( struct lp55xx_chip * chip , u16 mux , int nr )
{
struct lp55xx_engine * engine = & chip - > engines [ nr - 1 ] ;
int ret ;
u8 mux_page [ ] = {
[ LP55XX_ENGINE_1 ] = LP5523_PAGE_MUX1 ,
[ LP55XX_ENGINE_2 ] = LP5523_PAGE_MUX2 ,
[ LP55XX_ENGINE_3 ] = LP5523_PAGE_MUX3 ,
} ;
lp5523_load_engine ( chip ) ;
ret = lp55xx_write ( chip , LP5523_REG_PROG_PAGE_SEL , mux_page [ nr ] ) ;
if ( ret )
return ret ;
ret = lp55xx_write ( chip , LP5523_REG_PROG_MEM , ( u8 ) ( mux > > 8 ) ) ;
if ( ret )
return ret ;
ret = lp55xx_write ( chip , LP5523_REG_PROG_MEM + 1 , ( u8 ) ( mux ) ) ;
if ( ret )
return ret ;
engine - > led_mux = mux ;
return 0 ;
}
static ssize_t store_engine_leds ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t len , int nr )
{
struct lp55xx_led * led = i2c_get_clientdata ( to_i2c_client ( dev ) ) ;
struct lp55xx_chip * chip = led - > chip ;
struct lp55xx_engine * engine = & chip - > engines [ nr - 1 ] ;
u16 mux = 0 ;
ssize_t ret ;
if ( lp5523_mux_parse ( buf , & mux , len ) )
return - EINVAL ;
mutex_lock ( & chip - > lock ) ;
chip - > engine_idx = nr ;
ret = - EINVAL ;
if ( engine - > mode ! = LP55XX_ENGINE_LOAD )
goto leave ;
if ( lp5523_load_mux ( chip , mux , nr ) )
goto leave ;
ret = len ;
leave :
mutex_unlock ( & chip - > lock ) ;
return ret ;
}
store_leds ( 1 )
store_leds ( 2 )
store_leds ( 3 )
static ssize_t store_engine_load ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t len , int nr )
{
struct lp55xx_led * led = i2c_get_clientdata ( to_i2c_client ( dev ) ) ;
struct lp55xx_chip * chip = led - > chip ;
2013-12-03 05:21:44 +04:00
int ret ;
2013-08-08 09:02:29 +04:00
mutex_lock ( & chip - > lock ) ;
chip - > engine_idx = nr ;
lp5523_load_engine_and_select_page ( chip ) ;
2013-12-03 05:21:44 +04:00
ret = lp5523_update_program_memory ( chip , buf , len ) ;
2013-08-08 09:02:29 +04:00
mutex_unlock ( & chip - > lock ) ;
2013-12-03 05:21:44 +04:00
return ret ;
2013-08-08 09:02:29 +04:00
}
store_load ( 1 )
store_load ( 2 )
store_load ( 3 )
2010-11-12 01:05:22 +03:00
static ssize_t lp5523_selftest ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
2013-02-05 14:21:43 +04:00
struct lp55xx_led * led = i2c_get_clientdata ( to_i2c_client ( dev ) ) ;
struct lp55xx_chip * chip = led - > chip ;
struct lp55xx_platform_data * pdata = chip - > pdata ;
2010-11-12 01:05:22 +03:00
int i , ret , pos = 0 ;
u8 status , adc , vdd ;
mutex_lock ( & chip - > lock ) ;
2013-02-05 14:21:43 +04:00
ret = lp55xx_read ( chip , LP5523_REG_STATUS , & status ) ;
2010-11-12 01:05:22 +03:00
if ( ret < 0 )
goto fail ;
/* Check that ext clock is really in use if requested */
2013-02-05 14:21:43 +04:00
if ( pdata - > clock_mode = = LP55XX_CLOCK_EXT ) {
2010-11-12 01:05:22 +03:00
if ( ( status & LP5523_EXT_CLK_USED ) = = 0 )
goto fail ;
2013-02-05 14:21:43 +04:00
}
2010-11-12 01:05:22 +03:00
/* Measure VDD (i.e. VBAT) first (channel 16 corresponds to VDD) */
2013-02-05 14:21:43 +04:00
lp55xx_write ( chip , LP5523_REG_LED_TEST_CTRL , LP5523_EN_LEDTEST | 16 ) ;
2010-11-24 23:57:04 +03:00
usleep_range ( 3000 , 6000 ) ; /* ADC conversion time is typically 2.7 ms */
2013-02-05 14:21:43 +04:00
ret = lp55xx_read ( chip , LP5523_REG_STATUS , & status ) ;
2012-10-29 05:45:11 +04:00
if ( ret < 0 )
goto fail ;
2010-11-12 01:05:22 +03:00
if ( ! ( status & LP5523_LEDTEST_DONE ) )
2010-11-24 23:57:04 +03:00
usleep_range ( 3000 , 6000 ) ; /* Was not ready. Wait little bit */
2010-11-12 01:05:22 +03:00
2013-02-05 14:21:43 +04:00
ret = lp55xx_read ( chip , LP5523_REG_LED_TEST_ADC , & vdd ) ;
2012-10-29 05:45:11 +04:00
if ( ret < 0 )
goto fail ;
2010-11-12 01:05:22 +03:00
vdd - - ; /* There may be some fluctuation in measurement */
2013-02-05 14:07:34 +04:00
for ( i = 0 ; i < LP5523_MAX_LEDS ; i + + ) {
2010-11-12 01:05:22 +03:00
/* Skip non-existing channels */
2013-02-05 14:21:43 +04:00
if ( pdata - > led_config [ i ] . led_current = = 0 )
2010-11-12 01:05:22 +03:00
continue ;
/* Set default current */
2013-02-05 14:21:43 +04:00
lp55xx_write ( chip , LP5523_REG_LED_CURRENT_BASE + i ,
pdata - > led_config [ i ] . led_current ) ;
2010-11-12 01:05:22 +03:00
2013-02-05 14:21:43 +04:00
lp55xx_write ( chip , LP5523_REG_LED_PWM_BASE + i , 0xff ) ;
2010-11-24 23:57:04 +03:00
/* let current stabilize 2 - 4ms before measurements start */
usleep_range ( 2000 , 4000 ) ;
2013-02-05 14:21:43 +04:00
lp55xx_write ( chip , LP5523_REG_LED_TEST_CTRL ,
2010-11-12 01:05:22 +03:00
LP5523_EN_LEDTEST | i ) ;
2010-11-24 23:57:04 +03:00
/* ADC conversion time is 2.7 ms typically */
usleep_range ( 3000 , 6000 ) ;
2013-02-05 14:21:43 +04:00
ret = lp55xx_read ( chip , LP5523_REG_STATUS , & status ) ;
2012-10-29 05:45:11 +04:00
if ( ret < 0 )
goto fail ;
2010-11-12 01:05:22 +03:00
if ( ! ( status & LP5523_LEDTEST_DONE ) )
2010-11-24 23:57:04 +03:00
usleep_range ( 3000 , 6000 ) ; /* Was not ready. Wait. */
2013-02-05 14:21:43 +04:00
ret = lp55xx_read ( chip , LP5523_REG_LED_TEST_ADC , & adc ) ;
2012-10-29 05:45:11 +04:00
if ( ret < 0 )
goto fail ;
2010-11-12 01:05:22 +03:00
if ( adc > = vdd | | adc < LP5523_ADC_SHORTCIRC_LIM )
pos + = sprintf ( buf + pos , " LED %d FAIL \n " , i ) ;
2013-02-05 14:21:43 +04:00
lp55xx_write ( chip , LP5523_REG_LED_PWM_BASE + i , 0x00 ) ;
2010-11-12 01:05:22 +03:00
/* Restore current */
2013-02-05 14:21:43 +04:00
lp55xx_write ( chip , LP5523_REG_LED_CURRENT_BASE + i ,
led - > led_current ) ;
2010-11-12 01:05:22 +03:00
led + + ;
}
if ( pos = = 0 )
pos = sprintf ( buf , " OK \n " ) ;
goto release_lock ;
fail :
pos = sprintf ( buf , " FAIL \n " ) ;
release_lock :
mutex_unlock ( & chip - > lock ) ;
return pos ;
}
static void lp5523_led_brightness_work ( struct work_struct * work )
{
2013-02-05 14:08:40 +04:00
struct lp55xx_led * led = container_of ( work , struct lp55xx_led ,
2010-11-12 01:05:22 +03:00
brightness_work ) ;
2013-02-05 14:08:40 +04:00
struct lp55xx_chip * chip = led - > chip ;
2010-11-12 01:05:22 +03:00
mutex_lock ( & chip - > lock ) ;
2013-02-05 14:08:40 +04:00
lp55xx_write ( chip , LP5523_REG_LED_PWM_BASE + led - > chan_nr ,
2010-11-12 01:05:22 +03:00
led - > brightness ) ;
mutex_unlock ( & chip - > lock ) ;
}
2013-08-08 09:02:29 +04:00
static LP55XX_DEV_ATTR_RW ( engine1_mode , show_engine1_mode , store_engine1_mode ) ;
static LP55XX_DEV_ATTR_RW ( engine2_mode , show_engine2_mode , store_engine2_mode ) ;
static LP55XX_DEV_ATTR_RW ( engine3_mode , show_engine3_mode , store_engine3_mode ) ;
static LP55XX_DEV_ATTR_RW ( engine1_leds , show_engine1_leds , store_engine1_leds ) ;
static LP55XX_DEV_ATTR_RW ( engine2_leds , show_engine2_leds , store_engine2_leds ) ;
static LP55XX_DEV_ATTR_RW ( engine3_leds , show_engine3_leds , store_engine3_leds ) ;
static LP55XX_DEV_ATTR_WO ( engine1_load , store_engine1_load ) ;
static LP55XX_DEV_ATTR_WO ( engine2_load , store_engine2_load ) ;
static LP55XX_DEV_ATTR_WO ( engine3_load , store_engine3_load ) ;
static LP55XX_DEV_ATTR_RO ( selftest , lp5523_selftest ) ;
2010-11-12 01:05:22 +03:00
static struct attribute * lp5523_attributes [ ] = {
2013-08-08 09:02:29 +04:00
& dev_attr_engine1_mode . attr ,
& dev_attr_engine2_mode . attr ,
& dev_attr_engine3_mode . attr ,
& dev_attr_engine1_load . attr ,
& dev_attr_engine2_load . attr ,
& dev_attr_engine3_load . attr ,
& dev_attr_engine1_leds . attr ,
& dev_attr_engine2_leds . attr ,
& dev_attr_engine3_leds . attr ,
2010-11-12 01:05:22 +03:00
& dev_attr_selftest . attr ,
2012-09-12 16:16:03 +04:00
NULL ,
2010-11-12 01:05:22 +03:00
} ;
static const struct attribute_group lp5523_group = {
. attrs = lp5523_attributes ,
} ;
2013-02-05 13:08:49 +04:00
/* Chip specific configurations */
static struct lp55xx_device_config lp5523_cfg = {
. reset = {
. addr = LP5523_REG_RESET ,
. val = LP5523_RESET ,
} ,
2013-02-05 13:09:56 +04:00
. enable = {
. addr = LP5523_REG_ENABLE ,
. val = LP5523_ENABLE ,
} ,
2013-02-05 14:07:34 +04:00
. max_channel = LP5523_MAX_LEDS ,
2013-02-05 13:57:36 +04:00
. post_init_device = lp5523_post_init_device ,
2013-02-05 14:08:40 +04:00
. brightness_work_fn = lp5523_led_brightness_work ,
2013-02-05 14:09:32 +04:00
. set_led_current = lp5523_set_led_current ,
2013-02-05 14:18:51 +04:00
. firmware_cb = lp5523_firmware_loaded ,
. run_engine = lp5523_run_engine ,
2013-02-05 14:20:45 +04:00
. dev_attr_group = & lp5523_group ,
2013-02-05 13:08:49 +04:00
} ;
2012-11-19 22:23:02 +04:00
static int lp5523_probe ( struct i2c_client * client ,
2010-11-12 01:05:22 +03:00
const struct i2c_device_id * id )
{
2013-02-05 13:58:35 +04:00
int ret ;
2013-02-05 13:03:08 +04:00
struct lp55xx_chip * chip ;
struct lp55xx_led * led ;
2013-04-23 15:52:59 +04:00
struct lp55xx_platform_data * pdata ;
struct device_node * np = client - > dev . of_node ;
2013-07-30 12:07:35 +04:00
if ( ! dev_get_platdata ( & client - > dev ) ) {
2013-04-23 15:52:59 +04:00
if ( np ) {
ret = lp55xx_of_populate_pdata ( & client - > dev , np ) ;
if ( ret < 0 )
return ret ;
} else {
dev_err ( & client - > dev , " no platform data \n " ) ;
return - EINVAL ;
}
2010-11-12 01:05:22 +03:00
}
2013-07-30 12:07:35 +04:00
pdata = dev_get_platdata ( & client - > dev ) ;
2010-11-12 01:05:22 +03:00
2013-02-05 13:03:08 +04:00
chip = devm_kzalloc ( & client - > dev , sizeof ( * chip ) , GFP_KERNEL ) ;
if ( ! chip )
return - ENOMEM ;
led = devm_kzalloc ( & client - > dev ,
sizeof ( * led ) * pdata - > num_channels , GFP_KERNEL ) ;
if ( ! led )
return - ENOMEM ;
chip - > cl = client ;
chip - > pdata = pdata ;
2013-02-05 13:08:49 +04:00
chip - > cfg = & lp5523_cfg ;
2013-02-05 13:03:08 +04:00
mutex_init ( & chip - > lock ) ;
2010-11-12 01:05:22 +03:00
2013-02-05 13:03:08 +04:00
i2c_set_clientdata ( client , led ) ;
2010-11-12 01:05:22 +03:00
2013-02-05 13:58:35 +04:00
ret = lp55xx_init_device ( chip ) ;
2010-11-12 01:05:22 +03:00
if ( ret )
2013-02-05 12:58:01 +04:00
goto err_init ;
2010-11-12 01:05:22 +03:00
2012-09-04 11:06:26 +04:00
dev_info ( & client - > dev , " %s Programmable led chip found \n " , id - > name ) ;
2010-11-12 01:05:22 +03:00
2013-02-05 14:06:27 +04:00
ret = lp55xx_register_leds ( led , chip ) ;
2013-02-05 12:53:40 +04:00
if ( ret )
2013-02-05 14:06:27 +04:00
goto err_register_leds ;
2010-11-12 01:05:22 +03:00
2013-02-05 14:20:45 +04:00
ret = lp55xx_register_sysfs ( chip ) ;
2010-11-12 01:05:22 +03:00
if ( ret ) {
dev_err ( & client - > dev , " registering sysfs failed \n " ) ;
2013-02-05 14:20:45 +04:00
goto err_register_sysfs ;
2010-11-12 01:05:22 +03:00
}
2013-02-05 14:20:45 +04:00
return 0 ;
err_register_sysfs :
2013-02-05 14:11:18 +04:00
lp55xx_unregister_leds ( led , chip ) ;
2013-02-05 14:06:27 +04:00
err_register_leds :
2013-02-05 14:03:02 +04:00
lp55xx_deinit_device ( chip ) ;
2013-02-05 12:58:01 +04:00
err_init :
2010-11-12 01:05:22 +03:00
return ret ;
}
static int lp5523_remove ( struct i2c_client * client )
{
2013-02-05 14:03:02 +04:00
struct lp55xx_led * led = i2c_get_clientdata ( client ) ;
struct lp55xx_chip * chip = led - > chip ;
2010-11-12 01:05:22 +03:00
2013-11-21 10:13:34 +04:00
lp5523_stop_all_engines ( chip ) ;
2013-02-05 14:23:51 +04:00
lp55xx_unregister_sysfs ( chip ) ;
2013-02-05 14:11:18 +04:00
lp55xx_unregister_leds ( led , chip ) ;
2013-02-05 14:03:02 +04:00
lp55xx_deinit_device ( chip ) ;
2010-11-12 01:05:22 +03:00
return 0 ;
}
static const struct i2c_device_id lp5523_id [ ] = {
2012-09-04 11:06:18 +04:00
{ " lp5523 " , LP5523 } ,
{ " lp55231 " , LP55231 } ,
2010-11-12 01:05:22 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , lp5523_id ) ;
2013-05-09 08:48:57 +04:00
# ifdef CONFIG_OF
static const struct of_device_id of_lp5523_leds_match [ ] = {
{ . compatible = " national,lp5523 " , } ,
2014-05-05 15:46:54 +04:00
{ . compatible = " ti,lp55231 " , } ,
2013-05-09 08:48:57 +04:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , of_lp5523_leds_match ) ;
# endif
2010-11-12 01:05:22 +03:00
static struct i2c_driver lp5523_driver = {
. driver = {
2012-09-04 11:06:18 +04:00
. name = " lp5523x " ,
2013-05-09 08:48:57 +04:00
. of_match_table = of_match_ptr ( of_lp5523_leds_match ) ,
2010-11-12 01:05:22 +03:00
} ,
. probe = lp5523_probe ,
. remove = lp5523_remove ,
. id_table = lp5523_id ,
} ;
2012-01-11 03:09:27 +04:00
module_i2c_driver ( lp5523_driver ) ;
2010-11-12 01:05:22 +03:00
MODULE_AUTHOR ( " Mathias Nyman <mathias.nyman@nokia.com> " ) ;
2013-02-05 14:28:00 +04:00
MODULE_AUTHOR ( " Milo Kim <milo.kim@ti.com> " ) ;
2010-11-12 01:05:22 +03:00
MODULE_DESCRIPTION ( " LP5523 LED engine " ) ;
MODULE_LICENSE ( " GPL " ) ;