2007-05-04 00:27:45 +04:00
/*
* Universal power supply monitor class
*
* Copyright © 2007 Anton Vorontsov < cbou @ mail . ru >
* Copyright © 2004 Szabolcs Gyurko
* Copyright © 2003 Ian Molton < spyro @ f2s . com >
*
* Modified : 2004 , Oct Szabolcs Gyurko
*
* You may use this code as per GPL version 2
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/init.h>
2010-05-18 23:49:51 +04:00
# include <linux/slab.h>
2007-05-04 00:27:45 +04:00
# include <linux/device.h>
2013-11-19 14:18:03 +04:00
# include <linux/notifier.h>
2007-05-04 00:27:45 +04:00
# include <linux/err.h>
# include <linux/power_supply.h>
2012-05-09 19:06:47 +04:00
# include <linux/thermal.h>
2007-05-04 00:27:45 +04:00
# include "power_supply.h"
2009-07-30 17:42:31 +04:00
/* exported for the APM Power driver, APM emulation */
2007-05-04 00:27:45 +04:00
struct class * power_supply_class ;
2009-07-30 17:42:31 +04:00
EXPORT_SYMBOL_GPL ( power_supply_class ) ;
2007-05-04 00:27:45 +04:00
2013-11-19 14:18:03 +04:00
ATOMIC_NOTIFIER_HEAD ( power_supply_notifier ) ;
EXPORT_SYMBOL_GPL ( power_supply_notifier ) ;
2010-05-18 23:49:51 +04:00
static struct device_type power_supply_dev_type ;
2013-04-02 01:45:54 +04:00
static bool __power_supply_is_supplied_by ( struct power_supply * supplier ,
struct power_supply * supply )
{
int i ;
if ( ! supply - > supplied_from & & ! supplier - > supplied_to )
return false ;
/* Support both supplied_to and supplied_from modes */
if ( supply - > supplied_from ) {
if ( ! supplier - > name )
return false ;
for ( i = 0 ; i < supply - > num_supplies ; i + + )
if ( ! strcmp ( supplier - > name , supply - > supplied_from [ i ] ) )
return true ;
} else {
if ( ! supply - > name )
return false ;
for ( i = 0 ; i < supplier - > num_supplicants ; i + + )
if ( ! strcmp ( supplier - > supplied_to [ i ] , supply - > name ) )
return true ;
}
return false ;
}
2008-01-22 08:58:22 +03:00
static int __power_supply_changed_work ( struct device * dev , void * data )
{
struct power_supply * psy = ( struct power_supply * ) data ;
struct power_supply * pst = dev_get_drvdata ( dev ) ;
2013-04-02 01:45:54 +04:00
if ( __power_supply_is_supplied_by ( psy , pst ) ) {
if ( pst - > external_power_changed )
pst - > external_power_changed ( pst ) ;
}
2008-01-22 08:58:22 +03:00
return 0 ;
}
2007-05-04 00:27:45 +04:00
static void power_supply_changed_work ( struct work_struct * work )
{
2013-08-03 00:38:02 +04:00
unsigned long flags ;
2007-05-04 00:27:45 +04:00
struct power_supply * psy = container_of ( work , struct power_supply ,
changed_work ) ;
2008-04-29 11:58:29 +04:00
dev_dbg ( psy - > dev , " %s \n " , __func__ ) ;
2007-05-04 00:27:45 +04:00
2013-08-03 00:38:02 +04:00
spin_lock_irqsave ( & psy - > changed_lock , flags ) ;
if ( psy - > changed ) {
psy - > changed = false ;
spin_unlock_irqrestore ( & psy - > changed_lock , flags ) ;
class_for_each_device ( power_supply_class , NULL , psy ,
__power_supply_changed_work ) ;
power_supply_update_leds ( psy ) ;
2013-11-19 14:18:03 +04:00
atomic_notifier_call_chain ( & power_supply_notifier ,
PSY_EVENT_PROP_CHANGED , psy ) ;
2013-08-03 00:38:02 +04:00
kobject_uevent ( & psy - > dev - > kobj , KOBJ_CHANGE ) ;
spin_lock_irqsave ( & psy - > changed_lock , flags ) ;
}
/*
* Dependent power supplies ( e . g . battery ) may have changed state
* as a result of this event , so poll again and hold the
* wakeup_source until all events are processed .
*/
if ( ! psy - > changed )
pm_relax ( psy - > dev ) ;
spin_unlock_irqrestore ( & psy - > changed_lock , flags ) ;
2007-05-04 00:27:45 +04:00
}
void power_supply_changed ( struct power_supply * psy )
{
2013-08-03 00:38:02 +04:00
unsigned long flags ;
2008-04-29 11:58:29 +04:00
dev_dbg ( psy - > dev , " %s \n " , __func__ ) ;
2007-05-04 00:27:45 +04:00
2013-08-03 00:38:02 +04:00
spin_lock_irqsave ( & psy - > changed_lock , flags ) ;
psy - > changed = true ;
pm_stay_awake ( psy - > dev ) ;
spin_unlock_irqrestore ( & psy - > changed_lock , flags ) ;
2007-05-04 00:27:45 +04:00
schedule_work ( & psy - > changed_work ) ;
}
2009-07-30 17:42:31 +04:00
EXPORT_SYMBOL_GPL ( power_supply_changed ) ;
2007-05-04 00:27:45 +04:00
2013-04-02 01:45:55 +04:00
# ifdef CONFIG_OF
# include <linux/of.h>
static int __power_supply_populate_supplied_from ( struct device * dev ,
void * data )
{
struct power_supply * psy = ( struct power_supply * ) data ;
struct power_supply * epsy = dev_get_drvdata ( dev ) ;
struct device_node * np ;
int i = 0 ;
do {
np = of_parse_phandle ( psy - > of_node , " power-supplies " , i + + ) ;
if ( ! np )
continue ;
if ( np = = epsy - > of_node ) {
dev_info ( psy - > dev , " %s: Found supply : %s \n " ,
psy - > name , epsy - > name ) ;
psy - > supplied_from [ i - 1 ] = ( char * ) epsy - > name ;
psy - > num_supplies + + ;
2013-06-11 01:26:39 +04:00
of_node_put ( np ) ;
2013-04-02 01:45:55 +04:00
break ;
}
2013-06-11 01:26:39 +04:00
of_node_put ( np ) ;
2013-04-02 01:45:55 +04:00
} while ( np ) ;
return 0 ;
}
static int power_supply_populate_supplied_from ( struct power_supply * psy )
{
int error ;
error = class_for_each_device ( power_supply_class , NULL , psy ,
__power_supply_populate_supplied_from ) ;
dev_dbg ( psy - > dev , " %s %d \n " , __func__ , error ) ;
return error ;
}
static int __power_supply_find_supply_from_node ( struct device * dev ,
void * data )
{
struct device_node * np = ( struct device_node * ) data ;
struct power_supply * epsy = dev_get_drvdata ( dev ) ;
/* return error breaks out of class_for_each_device loop */
if ( epsy - > of_node = = np )
return - EINVAL ;
return 0 ;
}
static int power_supply_find_supply_from_node ( struct device_node * supply_node )
{
int error ;
struct device * dev ;
struct class_dev_iter iter ;
/*
* Use iterator to see if any other device is registered .
* This is required since class_for_each_device returns 0
* if there are no devices registered .
*/
class_dev_iter_init ( & iter , power_supply_class , NULL , NULL ) ;
dev = class_dev_iter_next ( & iter ) ;
if ( ! dev )
return - EPROBE_DEFER ;
/*
* We have to treat the return value as inverted , because if
* we return error on not found , then it won ' t continue looking .
* So we trick it by returning error on success to stop looking
* once the matching device is found .
*/
error = class_for_each_device ( power_supply_class , NULL , supply_node ,
__power_supply_find_supply_from_node ) ;
return error ? 0 : - EPROBE_DEFER ;
}
static int power_supply_check_supplies ( struct power_supply * psy )
{
struct device_node * np ;
int cnt = 0 ;
/* If there is already a list honor it */
if ( psy - > supplied_from & & psy - > num_supplies > 0 )
return 0 ;
/* No device node found, nothing to do */
if ( ! psy - > of_node )
return 0 ;
do {
int ret ;
np = of_parse_phandle ( psy - > of_node , " power-supplies " , cnt + + ) ;
if ( ! np )
continue ;
ret = power_supply_find_supply_from_node ( np ) ;
if ( ret ) {
dev_dbg ( psy - > dev , " Failed to find supply, defer! \n " ) ;
2013-06-11 01:26:39 +04:00
of_node_put ( np ) ;
2013-04-02 01:45:55 +04:00
return - EPROBE_DEFER ;
}
2013-06-11 01:26:39 +04:00
of_node_put ( np ) ;
2013-04-02 01:45:55 +04:00
} while ( np ) ;
/* All supplies found, allocate char ** array for filling */
psy - > supplied_from = devm_kzalloc ( psy - > dev , sizeof ( psy - > supplied_from ) ,
GFP_KERNEL ) ;
if ( ! psy - > supplied_from ) {
dev_err ( psy - > dev , " Couldn't allocate memory for supply list \n " ) ;
return - ENOMEM ;
}
* psy - > supplied_from = devm_kzalloc ( psy - > dev , sizeof ( char * ) * cnt ,
GFP_KERNEL ) ;
if ( ! * psy - > supplied_from ) {
dev_err ( psy - > dev , " Couldn't allocate memory for supply list \n " ) ;
return - ENOMEM ;
}
return power_supply_populate_supplied_from ( psy ) ;
}
# else
static inline int power_supply_check_supplies ( struct power_supply * psy )
{
return 0 ;
}
# endif
2008-01-22 08:58:22 +03:00
static int __power_supply_am_i_supplied ( struct device * dev , void * data )
2007-05-04 00:27:45 +04:00
{
union power_supply_propval ret = { 0 , } ;
2008-01-22 08:58:22 +03:00
struct power_supply * psy = ( struct power_supply * ) data ;
struct power_supply * epsy = dev_get_drvdata ( dev ) ;
2013-04-02 01:45:54 +04:00
if ( __power_supply_is_supplied_by ( epsy , psy ) )
if ( ! epsy - > get_property ( epsy , POWER_SUPPLY_PROP_ONLINE , & ret ) ) {
2008-01-22 08:58:22 +03:00
if ( ret . intval )
return ret . intval ;
2007-05-04 00:27:45 +04:00
}
2013-04-02 01:45:54 +04:00
2008-01-22 08:58:22 +03:00
return 0 ;
}
int power_supply_am_i_supplied ( struct power_supply * psy )
{
int error ;
2008-05-23 01:21:08 +04:00
error = class_for_each_device ( power_supply_class , NULL , psy ,
2008-01-22 08:58:22 +03:00
__power_supply_am_i_supplied ) ;
2007-05-04 00:27:45 +04:00
2008-04-29 11:58:29 +04:00
dev_dbg ( psy - > dev , " %s %d \n " , __func__ , error ) ;
2007-05-04 00:27:45 +04:00
2008-01-22 08:58:22 +03:00
return error ;
2007-05-04 00:27:45 +04:00
}
2009-07-30 17:42:31 +04:00
EXPORT_SYMBOL_GPL ( power_supply_am_i_supplied ) ;
2007-05-04 00:27:45 +04:00
2008-08-27 00:09:59 +04:00
static int __power_supply_is_system_supplied ( struct device * dev , void * data )
{
union power_supply_propval ret = { 0 , } ;
struct power_supply * psy = dev_get_drvdata ( dev ) ;
2011-12-11 01:53:36 +04:00
unsigned int * count = data ;
2008-08-27 00:09:59 +04:00
2011-12-11 01:53:36 +04:00
( * count ) + + ;
2008-08-27 00:09:59 +04:00
if ( psy - > type ! = POWER_SUPPLY_TYPE_BATTERY ) {
if ( psy - > get_property ( psy , POWER_SUPPLY_PROP_ONLINE , & ret ) )
return 0 ;
if ( ret . intval )
return ret . intval ;
}
return 0 ;
}
int power_supply_is_system_supplied ( void )
{
int error ;
2011-12-11 01:53:36 +04:00
unsigned int count = 0 ;
2008-08-27 00:09:59 +04:00
2011-12-11 01:53:36 +04:00
error = class_for_each_device ( power_supply_class , NULL , & count ,
2008-08-27 00:09:59 +04:00
__power_supply_is_system_supplied ) ;
2011-12-11 01:53:36 +04:00
/*
* If no power class device was found at all , most probably we are
* running on a desktop system , so assume we are on mains power .
*/
if ( count = = 0 )
return 1 ;
2008-08-27 00:09:59 +04:00
return error ;
}
2009-07-30 17:42:31 +04:00
EXPORT_SYMBOL_GPL ( power_supply_is_system_supplied ) ;
2008-08-27 00:09:59 +04:00
2009-07-23 22:35:53 +04:00
int power_supply_set_battery_charged ( struct power_supply * psy )
{
if ( psy - > type = = POWER_SUPPLY_TYPE_BATTERY & & psy - > set_charged ) {
psy - > set_charged ( psy ) ;
return 0 ;
}
return - EINVAL ;
}
EXPORT_SYMBOL_GPL ( power_supply_set_battery_charged ) ;
2013-02-01 23:40:17 +04:00
static int power_supply_match_device_by_name ( struct device * dev , const void * data )
2009-07-23 22:35:53 +04:00
{
const char * name = data ;
struct power_supply * psy = dev_get_drvdata ( dev ) ;
return strcmp ( psy - > name , name ) = = 0 ;
}
2013-02-01 23:40:17 +04:00
struct power_supply * power_supply_get_by_name ( const char * name )
2009-07-23 22:35:53 +04:00
{
struct device * dev = class_find_device ( power_supply_class , NULL , name ,
power_supply_match_device_by_name ) ;
return dev ? dev_get_drvdata ( dev ) : NULL ;
}
EXPORT_SYMBOL_GPL ( power_supply_get_by_name ) ;
2013-11-24 20:49:29 +04:00
# ifdef CONFIG_OF
static int power_supply_match_device_node ( struct device * dev , const void * data )
{
return dev - > parent & & dev - > parent - > of_node = = data ;
}
struct power_supply * power_supply_get_by_phandle ( struct device_node * np ,
const char * property )
{
struct device_node * power_supply_np ;
struct device * dev ;
power_supply_np = of_parse_phandle ( np , property , 0 ) ;
if ( ! power_supply_np )
return ERR_PTR ( - ENODEV ) ;
dev = class_find_device ( power_supply_class , NULL , power_supply_np ,
power_supply_match_device_node ) ;
of_node_put ( power_supply_np ) ;
return dev ? dev_get_drvdata ( dev ) : NULL ;
}
EXPORT_SYMBOL_GPL ( power_supply_get_by_phandle ) ;
# endif /* CONFIG_OF */
2011-12-07 21:15:45 +04:00
int power_supply_powers ( struct power_supply * psy , struct device * dev )
{
2012-01-05 19:17:25 +04:00
return sysfs_create_link ( & psy - > dev - > kobj , & dev - > kobj , " powers " ) ;
2011-12-07 21:15:45 +04:00
}
EXPORT_SYMBOL_GPL ( power_supply_powers ) ;
2010-05-18 23:49:51 +04:00
static void power_supply_dev_release ( struct device * dev )
{
pr_debug ( " device: '%s': %s \n " , dev_name ( dev ) , __func__ ) ;
kfree ( dev ) ;
}
2013-11-19 14:18:03 +04:00
int power_supply_reg_notifier ( struct notifier_block * nb )
{
return atomic_notifier_chain_register ( & power_supply_notifier , nb ) ;
}
EXPORT_SYMBOL_GPL ( power_supply_reg_notifier ) ;
void power_supply_unreg_notifier ( struct notifier_block * nb )
{
atomic_notifier_chain_unregister ( & power_supply_notifier , nb ) ;
}
EXPORT_SYMBOL_GPL ( power_supply_unreg_notifier ) ;
2012-05-09 19:06:47 +04:00
# ifdef CONFIG_THERMAL
static int power_supply_read_temp ( struct thermal_zone_device * tzd ,
unsigned long * temp )
{
struct power_supply * psy ;
union power_supply_propval val ;
int ret ;
WARN_ON ( tzd = = NULL ) ;
psy = tzd - > devdata ;
ret = psy - > get_property ( psy , POWER_SUPPLY_PROP_TEMP , & val ) ;
/* Convert tenths of degree Celsius to milli degree Celsius. */
if ( ! ret )
* temp = val . intval * 100 ;
return ret ;
}
static struct thermal_zone_device_ops psy_tzd_ops = {
. get_temp = power_supply_read_temp ,
} ;
static int psy_register_thermal ( struct power_supply * psy )
{
int i ;
/* Register battery zone device psy reports temperature */
for ( i = 0 ; i < psy - > num_properties ; i + + ) {
if ( psy - > properties [ i ] = = POWER_SUPPLY_PROP_TEMP ) {
2012-07-31 15:59:42 +04:00
psy - > tzd = thermal_zone_device_register ( psy - > name , 0 , 0 ,
2012-09-18 09:34:56 +04:00
psy , & psy_tzd_ops , NULL , 0 , 0 ) ;
2012-05-09 19:06:47 +04:00
if ( IS_ERR ( psy - > tzd ) )
return PTR_ERR ( psy - > tzd ) ;
break ;
}
}
return 0 ;
}
static void psy_unregister_thermal ( struct power_supply * psy )
{
if ( IS_ERR_OR_NULL ( psy - > tzd ) )
return ;
thermal_zone_device_unregister ( psy - > tzd ) ;
}
2012-10-09 20:55:59 +04:00
/* thermal cooling device callbacks */
static int ps_get_max_charge_cntl_limit ( struct thermal_cooling_device * tcd ,
unsigned long * state )
{
struct power_supply * psy ;
union power_supply_propval val ;
int ret ;
psy = tcd - > devdata ;
ret = psy - > get_property ( psy ,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX , & val ) ;
if ( ! ret )
* state = val . intval ;
return ret ;
}
static int ps_get_cur_chrage_cntl_limit ( struct thermal_cooling_device * tcd ,
unsigned long * state )
{
struct power_supply * psy ;
union power_supply_propval val ;
int ret ;
psy = tcd - > devdata ;
ret = psy - > get_property ( psy ,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT , & val ) ;
if ( ! ret )
* state = val . intval ;
return ret ;
}
static int ps_set_cur_charge_cntl_limit ( struct thermal_cooling_device * tcd ,
unsigned long state )
{
struct power_supply * psy ;
union power_supply_propval val ;
int ret ;
psy = tcd - > devdata ;
val . intval = state ;
ret = psy - > set_property ( psy ,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT , & val ) ;
return ret ;
}
static struct thermal_cooling_device_ops psy_tcd_ops = {
. get_max_state = ps_get_max_charge_cntl_limit ,
. get_cur_state = ps_get_cur_chrage_cntl_limit ,
. set_cur_state = ps_set_cur_charge_cntl_limit ,
} ;
static int psy_register_cooler ( struct power_supply * psy )
{
int i ;
/* Register for cooling device if psy can control charging */
for ( i = 0 ; i < psy - > num_properties ; i + + ) {
if ( psy - > properties [ i ] = =
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT ) {
psy - > tcd = thermal_cooling_device_register (
( char * ) psy - > name ,
psy , & psy_tcd_ops ) ;
if ( IS_ERR ( psy - > tcd ) )
return PTR_ERR ( psy - > tcd ) ;
break ;
}
}
return 0 ;
}
static void psy_unregister_cooler ( struct power_supply * psy )
{
if ( IS_ERR_OR_NULL ( psy - > tcd ) )
return ;
thermal_cooling_device_unregister ( psy - > tcd ) ;
}
2012-05-09 19:06:47 +04:00
# else
static int psy_register_thermal ( struct power_supply * psy )
{
return 0 ;
}
static void psy_unregister_thermal ( struct power_supply * psy )
{
}
2012-10-09 20:55:59 +04:00
static int psy_register_cooler ( struct power_supply * psy )
{
return 0 ;
}
static void psy_unregister_cooler ( struct power_supply * psy )
{
}
2012-05-09 19:06:47 +04:00
# endif
2014-05-28 11:23:37 +04:00
int __power_supply_register ( struct device * parent , struct power_supply * psy , bool ws )
2007-05-04 00:27:45 +04:00
{
2010-05-18 23:49:51 +04:00
struct device * dev ;
int rc ;
2007-05-04 00:27:45 +04:00
2010-05-18 23:49:51 +04:00
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
if ( ! dev )
return - ENOMEM ;
2007-05-04 00:27:45 +04:00
2010-05-18 23:49:51 +04:00
device_initialize ( dev ) ;
2007-05-04 00:27:45 +04:00
2010-05-18 23:49:51 +04:00
dev - > class = power_supply_class ;
dev - > type = & power_supply_dev_type ;
dev - > parent = parent ;
dev - > release = power_supply_dev_release ;
dev_set_drvdata ( dev , psy ) ;
psy - > dev = dev ;
2013-11-22 21:54:28 +04:00
rc = dev_set_name ( dev , " %s " , psy - > name ) ;
if ( rc )
goto dev_set_name_failed ;
2011-02-21 17:34:19 +03:00
INIT_WORK ( & psy - > changed_work , power_supply_changed_work ) ;
2013-04-02 01:45:55 +04:00
rc = power_supply_check_supplies ( psy ) ;
if ( rc ) {
dev_info ( dev , " Not all required supplies found, defer probe \n " ) ;
goto check_supplies_failed ;
}
2013-08-03 00:38:02 +04:00
spin_lock_init ( & psy - > changed_lock ) ;
2014-05-28 11:23:37 +04:00
rc = device_init_wakeup ( dev , ws ) ;
2013-08-03 00:38:02 +04:00
if ( rc )
goto wakeup_init_failed ;
2010-05-18 23:49:51 +04:00
rc = device_add ( dev ) ;
2007-05-04 00:27:45 +04:00
if ( rc )
2010-05-18 23:49:51 +04:00
goto device_add_failed ;
2012-05-09 19:06:47 +04:00
rc = psy_register_thermal ( psy ) ;
if ( rc )
goto register_thermal_failed ;
2012-10-09 20:55:59 +04:00
rc = psy_register_cooler ( psy ) ;
if ( rc )
goto register_cooler_failed ;
2007-05-04 00:27:45 +04:00
rc = power_supply_create_triggers ( psy ) ;
if ( rc )
goto create_triggers_failed ;
power_supply_changed ( psy ) ;
goto success ;
create_triggers_failed :
2012-10-09 20:55:59 +04:00
psy_unregister_cooler ( psy ) ;
register_cooler_failed :
2012-05-09 19:06:47 +04:00
psy_unregister_thermal ( psy ) ;
register_thermal_failed :
2010-11-19 21:41:58 +03:00
device_del ( dev ) ;
2010-05-18 23:49:51 +04:00
device_add_failed :
2013-11-22 21:54:28 +04:00
wakeup_init_failed :
2013-04-02 01:45:55 +04:00
check_supplies_failed :
2013-11-22 21:54:28 +04:00
dev_set_name_failed :
2010-11-19 21:41:58 +03:00
put_device ( dev ) ;
2007-05-04 00:27:45 +04:00
success :
return rc ;
}
2014-05-28 11:23:37 +04:00
int power_supply_register ( struct device * parent , struct power_supply * psy )
{
return __power_supply_register ( parent , psy , true ) ;
}
2009-07-30 17:42:31 +04:00
EXPORT_SYMBOL_GPL ( power_supply_register ) ;
2007-05-04 00:27:45 +04:00
2014-05-28 11:23:37 +04:00
int power_supply_register_no_ws ( struct device * parent , struct power_supply * psy )
{
return __power_supply_register ( parent , psy , false ) ;
}
EXPORT_SYMBOL_GPL ( power_supply_register_no_ws ) ;
2007-05-04 00:27:45 +04:00
void power_supply_unregister ( struct power_supply * psy )
{
2010-12-11 19:51:45 +03:00
cancel_work_sync ( & psy - > changed_work ) ;
2011-12-07 21:15:45 +04:00
sysfs_remove_link ( & psy - > dev - > kobj , " powers " ) ;
2007-05-04 00:27:45 +04:00
power_supply_remove_triggers ( psy ) ;
2012-10-09 20:55:59 +04:00
psy_unregister_cooler ( psy ) ;
2012-05-09 19:06:47 +04:00
psy_unregister_thermal ( psy ) ;
2013-08-03 00:38:02 +04:00
device_init_wakeup ( psy - > dev , false ) ;
2007-05-04 00:27:45 +04:00
device_unregister ( psy - > dev ) ;
}
2009-07-30 17:42:31 +04:00
EXPORT_SYMBOL_GPL ( power_supply_unregister ) ;
2007-05-04 00:27:45 +04:00
static int __init power_supply_class_init ( void )
{
power_supply_class = class_create ( THIS_MODULE , " power_supply " ) ;
if ( IS_ERR ( power_supply_class ) )
return PTR_ERR ( power_supply_class ) ;
power_supply_class - > dev_uevent = power_supply_uevent ;
2010-05-18 23:49:51 +04:00
power_supply_init_attrs ( & power_supply_dev_type ) ;
2007-05-04 00:27:45 +04:00
return 0 ;
}
static void __exit power_supply_class_exit ( void )
{
class_destroy ( power_supply_class ) ;
}
subsys_initcall ( power_supply_class_init ) ;
module_exit ( power_supply_class_exit ) ;
MODULE_DESCRIPTION ( " Universal power supply monitor class " ) ;
MODULE_AUTHOR ( " Ian Molton <spyro@f2s.com>, "
" Szabolcs Gyurko, "
" Anton Vorontsov <cbou@mail.ru> " ) ;
MODULE_LICENSE ( " GPL " ) ;