2009-04-26 16:49:39 +03:00
/*
* userspace - consumer . c
*
* Copyright 2009 CompuLab , Ltd .
*
* Author : Mike Rapoport < mike @ compulab . co . il >
*
* Based of virtual consumer driver :
* Copyright 2008 Wolfson Microelectronics PLC .
* Author : Mark Brown < broonie @ opensource . wolfsonmicro . com >
*
* 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 .
*
*/
# include <linux/err.h>
# include <linux/mutex.h>
# include <linux/platform_device.h>
# include <linux/regulator/consumer.h>
# include <linux/regulator/userspace-consumer.h>
struct userspace_consumer_data {
const char * name ;
struct mutex lock ;
bool enabled ;
int num_supplies ;
struct regulator_bulk_data * supplies ;
} ;
2009-04-28 12:01:16 +01:00
static ssize_t reg_show_name ( struct device * dev ,
2009-04-26 16:49:39 +03:00
struct device_attribute * attr , char * buf )
{
struct userspace_consumer_data * data = dev_get_drvdata ( dev ) ;
return sprintf ( buf , " %s \n " , data - > name ) ;
}
2009-04-28 12:01:16 +01:00
static ssize_t reg_show_state ( struct device * dev ,
2009-04-26 16:49:39 +03:00
struct device_attribute * attr , char * buf )
{
struct userspace_consumer_data * data = dev_get_drvdata ( dev ) ;
if ( data - > enabled )
return sprintf ( buf , " enabled \n " ) ;
return sprintf ( buf , " disabled \n " ) ;
}
2009-04-28 12:01:16 +01:00
static ssize_t reg_set_state ( struct device * dev , struct device_attribute * attr ,
2009-04-26 16:49:39 +03:00
const char * buf , size_t count )
{
struct userspace_consumer_data * data = dev_get_drvdata ( dev ) ;
bool enabled ;
int ret ;
/*
* sysfs_streq ( ) doesn ' t need the \ n ' s , but we add them so the strings
* will be shared with show_state ( ) , above .
*/
if ( sysfs_streq ( buf , " enabled \n " ) | | sysfs_streq ( buf , " 1 " ) )
enabled = true ;
else if ( sysfs_streq ( buf , " disabled \n " ) | | sysfs_streq ( buf , " 0 " ) )
enabled = false ;
else {
dev_err ( dev , " Configuring invalid mode \n " ) ;
return count ;
}
mutex_lock ( & data - > lock ) ;
if ( enabled ! = data - > enabled ) {
if ( enabled )
ret = regulator_bulk_enable ( data - > num_supplies ,
data - > supplies ) ;
else
ret = regulator_bulk_disable ( data - > num_supplies ,
data - > supplies ) ;
if ( ret = = 0 )
data - > enabled = enabled ;
else
dev_err ( dev , " Failed to configure state: %d \n " , ret ) ;
}
mutex_unlock ( & data - > lock ) ;
return count ;
}
2009-04-28 12:01:16 +01:00
static DEVICE_ATTR ( name , 0444 , reg_show_name , NULL ) ;
static DEVICE_ATTR ( state , 0644 , reg_show_state , reg_set_state ) ;
2009-04-26 16:49:39 +03:00
2009-08-10 09:05:13 +03:00
static struct attribute * attributes [ ] = {
& dev_attr_name . attr ,
& dev_attr_state . attr ,
NULL ,
} ;
static const struct attribute_group attr_group = {
. attrs = attributes ,
2009-04-26 16:49:39 +03:00
} ;
static int regulator_userspace_consumer_probe ( struct platform_device * pdev )
{
struct regulator_userspace_consumer_data * pdata ;
struct userspace_consumer_data * drvdata ;
2009-08-10 09:05:13 +03:00
int ret ;
2009-04-26 16:49:39 +03:00
pdata = pdev - > dev . platform_data ;
if ( ! pdata )
return - EINVAL ;
drvdata = kzalloc ( sizeof ( struct userspace_consumer_data ) , GFP_KERNEL ) ;
if ( drvdata = = NULL )
return - ENOMEM ;
drvdata - > name = pdata - > name ;
drvdata - > num_supplies = pdata - > num_supplies ;
drvdata - > supplies = pdata - > supplies ;
mutex_init ( & drvdata - > lock ) ;
ret = regulator_bulk_get ( & pdev - > dev , drvdata - > num_supplies ,
drvdata - > supplies ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Failed to get supplies: %d \n " , ret ) ;
goto err_alloc_supplies ;
}
2009-08-10 09:05:13 +03:00
ret = sysfs_create_group ( & pdev - > dev . kobj , & attr_group ) ;
if ( ret ! = 0 )
goto err_create_attrs ;
2009-04-26 16:49:39 +03:00
2009-08-10 09:05:13 +03:00
if ( pdata - > init_on ) {
2009-04-26 16:49:39 +03:00
ret = regulator_bulk_enable ( drvdata - > num_supplies ,
drvdata - > supplies ) ;
2009-08-10 09:05:13 +03:00
if ( ret ) {
dev_err ( & pdev - > dev ,
" Failed to set initial state: %d \n " , ret ) ;
goto err_enable ;
}
2009-04-26 16:49:39 +03:00
}
2009-08-10 09:05:13 +03:00
drvdata - > enabled = pdata - > init_on ;
2009-04-26 16:49:39 +03:00
platform_set_drvdata ( pdev , drvdata ) ;
return 0 ;
2009-08-10 09:05:13 +03:00
err_enable :
sysfs_remove_group ( & pdev - > dev . kobj , & attr_group ) ;
2009-04-26 16:49:39 +03:00
2009-08-10 09:05:13 +03:00
err_create_attrs :
2009-04-26 16:49:39 +03:00
regulator_bulk_free ( drvdata - > num_supplies , drvdata - > supplies ) ;
err_alloc_supplies :
kfree ( drvdata ) ;
return ret ;
}
static int regulator_userspace_consumer_remove ( struct platform_device * pdev )
{
struct userspace_consumer_data * data = platform_get_drvdata ( pdev ) ;
2009-08-10 09:05:13 +03:00
sysfs_remove_group ( & pdev - > dev . kobj , & attr_group ) ;
2009-04-26 16:49:39 +03:00
if ( data - > enabled )
regulator_bulk_disable ( data - > num_supplies , data - > supplies ) ;
regulator_bulk_free ( data - > num_supplies , data - > supplies ) ;
kfree ( data ) ;
return 0 ;
}
static struct platform_driver regulator_userspace_consumer_driver = {
. probe = regulator_userspace_consumer_probe ,
. remove = regulator_userspace_consumer_remove ,
. driver = {
. name = " reg-userspace-consumer " ,
} ,
} ;
static int __init regulator_userspace_consumer_init ( void )
{
return platform_driver_register ( & regulator_userspace_consumer_driver ) ;
}
module_init ( regulator_userspace_consumer_init ) ;
static void __exit regulator_userspace_consumer_exit ( void )
{
platform_driver_unregister ( & regulator_userspace_consumer_driver ) ;
}
module_exit ( regulator_userspace_consumer_exit ) ;
MODULE_AUTHOR ( " Mike Rapoport <mike@compulab.co.il> " ) ;
MODULE_DESCRIPTION ( " Userspace consumer for voltage and current regulators " ) ;
MODULE_LICENSE ( " GPL " ) ;