2014-05-22 02:34:16 +04:00
/*
* Generic Syscon LEDs Driver
*
* Copyright ( c ) 2014 , Linaro Limited
* Author : Linus Walleij < linus . walleij @ linaro . org >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 of
* the License , or ( at your option ) any later version .
*
* 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 . , 59 Temple Place , Suite 330 , Boston ,
* MA 02111 - 1307 USA
*/
# include <linux/io.h>
2015-12-14 00:45:51 +03:00
# include <linux/init.h>
2014-05-22 02:34:16 +04:00
# include <linux/of_device.h>
# include <linux/of_address.h>
# include <linux/platform_device.h>
# include <linux/stat.h>
# include <linux/slab.h>
# include <linux/mfd/syscon.h>
# include <linux/regmap.h>
# include <linux/leds.h>
/**
* struct syscon_led - state container for syscon based LEDs
* @ cdev : LED class device for this LED
* @ map : regmap to access the syscon device backing this LED
* @ offset : the offset into the syscon regmap for the LED register
* @ mask : the bit in the register corresponding to the LED
* @ state : current state of the LED
*/
struct syscon_led {
struct led_classdev cdev ;
struct regmap * map ;
u32 offset ;
u32 mask ;
bool state ;
} ;
static void syscon_led_set ( struct led_classdev * led_cdev ,
enum led_brightness value )
{
struct syscon_led * sled =
container_of ( led_cdev , struct syscon_led , cdev ) ;
u32 val ;
int ret ;
if ( value = = LED_OFF ) {
val = 0 ;
sled - > state = false ;
} else {
val = sled - > mask ;
sled - > state = true ;
}
ret = regmap_update_bits ( sled - > map , sled - > offset , sled - > mask , val ) ;
if ( ret < 0 )
dev_err ( sled - > cdev . dev , " error updating LED status \n " ) ;
}
2015-03-03 12:08:19 +03:00
static int syscon_led_probe ( struct platform_device * pdev )
2014-05-22 02:34:16 +04:00
{
2015-03-03 12:08:19 +03:00
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
struct device * parent ;
struct regmap * map ;
struct syscon_led * sled ;
const char * state ;
2014-05-22 02:34:16 +04:00
int ret ;
2015-03-03 12:08:19 +03:00
parent = dev - > parent ;
if ( ! parent ) {
dev_err ( dev , " no parent for syscon LED \n " ) ;
return - ENODEV ;
}
map = syscon_node_to_regmap ( parent - > of_node ) ;
2015-08-18 22:25:26 +03:00
if ( IS_ERR ( map ) ) {
2015-03-03 12:08:19 +03:00
dev_err ( dev , " no regmap for syscon LED parent \n " ) ;
2015-08-18 22:25:26 +03:00
return PTR_ERR ( map ) ;
2015-03-03 12:08:19 +03:00
}
sled = devm_kzalloc ( dev , sizeof ( * sled ) , GFP_KERNEL ) ;
if ( ! sled )
return - ENOMEM ;
sled - > map = map ;
if ( of_property_read_u32 ( np , " offset " , & sled - > offset ) )
return - EINVAL ;
if ( of_property_read_u32 ( np , " mask " , & sled - > mask ) )
return - EINVAL ;
sled - > cdev . name =
of_get_property ( np , " label " , NULL ) ? : np - > name ;
sled - > cdev . default_trigger =
of_get_property ( np , " linux,default-trigger " , NULL ) ;
state = of_get_property ( np , " default-state " , NULL ) ;
if ( state ) {
if ( ! strcmp ( state , " keep " ) ) {
u32 val ;
ret = regmap_read ( map , sled - > offset , & val ) ;
if ( ret < 0 )
return ret ;
sled - > state = ! ! ( val & sled - > mask ) ;
} else if ( ! strcmp ( state , " on " ) ) {
sled - > state = true ;
ret = regmap_update_bits ( map , sled - > offset ,
sled - > mask ,
sled - > mask ) ;
if ( ret < 0 )
return ret ;
} else {
sled - > state = false ;
ret = regmap_update_bits ( map , sled - > offset ,
sled - > mask , 0 ) ;
if ( ret < 0 )
return ret ;
2014-05-22 02:34:16 +04:00
}
2015-03-03 12:08:19 +03:00
}
sled - > cdev . brightness_set = syscon_led_set ;
2014-05-22 02:34:16 +04:00
2015-03-03 12:08:19 +03:00
ret = led_classdev_register ( dev , & sled - > cdev ) ;
if ( ret < 0 )
return ret ;
platform_set_drvdata ( pdev , sled ) ;
dev_info ( dev , " registered LED %s \n " , sled - > cdev . name ) ;
2014-05-22 02:34:16 +04:00
2014-10-24 14:57:19 +04:00
return 0 ;
}
2015-03-03 12:08:19 +03:00
static const struct of_device_id of_syscon_leds_match [ ] = {
{ . compatible = " register-bit-led " , } ,
{ } ,
} ;
2014-10-24 14:57:19 +04:00
2015-03-03 12:08:19 +03:00
static struct platform_driver syscon_led_driver = {
. probe = syscon_led_probe ,
. driver = {
. name = " leds-syscon " ,
. of_match_table = of_syscon_leds_match ,
2015-12-14 00:45:51 +03:00
. suppress_bind_attrs = true ,
2015-03-03 12:08:19 +03:00
} ,
} ;
2015-12-14 00:45:51 +03:00
builtin_platform_driver ( syscon_led_driver ) ;