2019-03-19 17:51:56 +03:00
// SPDX-License-Identifier: GPL-2.0
2013-01-25 19:30:00 +04:00
/*
* Power supply driver for the goldfish emulator
*
* Copyright ( C ) 2008 Google , Inc .
* Copyright ( C ) 2012 Intel , Inc .
* Copyright ( C ) 2013 Intel , Inc .
* Author : Mike Lockwood < lockwood @ android . com >
*/
# include <linux/module.h>
# include <linux/err.h>
# include <linux/platform_device.h>
# include <linux/power_supply.h>
# include <linux/types.h>
# include <linux/pci.h>
# include <linux/interrupt.h>
# include <linux/io.h>
2016-03-02 02:46:10 +03:00
# include <linux/acpi.h>
2013-01-25 19:30:00 +04:00
struct goldfish_battery_data {
void __iomem * reg_base ;
int irq ;
spinlock_t lock ;
2015-03-12 10:44:11 +03:00
struct power_supply * battery ;
struct power_supply * ac ;
2013-01-25 19:30:00 +04:00
} ;
# define GOLDFISH_BATTERY_READ(data, addr) \
( readl ( data - > reg_base + addr ) )
# define GOLDFISH_BATTERY_WRITE(data, addr, x) \
( writel ( x , data - > reg_base + addr ) )
enum {
/* status register */
2018-12-19 03:31:37 +03:00
BATTERY_INT_STATUS = 0x00 ,
2013-01-25 19:30:00 +04:00
/* set this to enable IRQ */
2018-12-19 03:31:37 +03:00
BATTERY_INT_ENABLE = 0x04 ,
2013-01-25 19:30:00 +04:00
2018-12-19 03:31:38 +03:00
BATTERY_AC_ONLINE = 0x08 ,
BATTERY_STATUS = 0x0C ,
BATTERY_HEALTH = 0x10 ,
BATTERY_PRESENT = 0x14 ,
BATTERY_CAPACITY = 0x18 ,
2018-12-19 03:31:39 +03:00
BATTERY_VOLTAGE = 0x1C ,
BATTERY_TEMP = 0x20 ,
BATTERY_CHARGE_COUNTER = 0x24 ,
BATTERY_VOLTAGE_MAX = 0x28 ,
BATTERY_CURRENT_MAX = 0x2C ,
BATTERY_CURRENT_NOW = 0x30 ,
BATTERY_CURRENT_AVG = 0x34 ,
BATTERY_CHARGE_FULL_UAH = 0x38 ,
BATTERY_CYCLE_COUNT = 0x40 ,
2013-01-25 19:30:00 +04:00
BATTERY_STATUS_CHANGED = 1U < < 0 ,
AC_STATUS_CHANGED = 1U < < 1 ,
2018-12-19 03:31:38 +03:00
BATTERY_INT_MASK = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED ,
2013-01-25 19:30:00 +04:00
} ;
static int goldfish_ac_get_property ( struct power_supply * psy ,
enum power_supply_property psp ,
union power_supply_propval * val )
{
2015-03-12 10:44:11 +03:00
struct goldfish_battery_data * data = power_supply_get_drvdata ( psy ) ;
2013-01-25 19:30:00 +04:00
int ret = 0 ;
switch ( psp ) {
case POWER_SUPPLY_PROP_ONLINE :
val - > intval = GOLDFISH_BATTERY_READ ( data , BATTERY_AC_ONLINE ) ;
break ;
2018-12-19 03:31:39 +03:00
case POWER_SUPPLY_PROP_VOLTAGE_MAX :
val - > intval = GOLDFISH_BATTERY_READ ( data , BATTERY_VOLTAGE_MAX ) ;
break ;
case POWER_SUPPLY_PROP_CURRENT_MAX :
val - > intval = GOLDFISH_BATTERY_READ ( data , BATTERY_CURRENT_MAX ) ;
break ;
2013-01-25 19:30:00 +04:00
default :
ret = - EINVAL ;
break ;
}
return ret ;
}
static int goldfish_battery_get_property ( struct power_supply * psy ,
enum power_supply_property psp ,
union power_supply_propval * val )
{
2015-03-12 10:44:11 +03:00
struct goldfish_battery_data * data = power_supply_get_drvdata ( psy ) ;
2013-01-25 19:30:00 +04:00
int ret = 0 ;
switch ( psp ) {
case POWER_SUPPLY_PROP_STATUS :
val - > intval = GOLDFISH_BATTERY_READ ( data , BATTERY_STATUS ) ;
break ;
case POWER_SUPPLY_PROP_HEALTH :
val - > intval = GOLDFISH_BATTERY_READ ( data , BATTERY_HEALTH ) ;
break ;
case POWER_SUPPLY_PROP_PRESENT :
val - > intval = GOLDFISH_BATTERY_READ ( data , BATTERY_PRESENT ) ;
break ;
case POWER_SUPPLY_PROP_TECHNOLOGY :
val - > intval = POWER_SUPPLY_TECHNOLOGY_LION ;
break ;
case POWER_SUPPLY_PROP_CAPACITY :
val - > intval = GOLDFISH_BATTERY_READ ( data , BATTERY_CAPACITY ) ;
break ;
2018-12-19 03:31:39 +03:00
case POWER_SUPPLY_PROP_VOLTAGE_NOW :
val - > intval = GOLDFISH_BATTERY_READ ( data , BATTERY_VOLTAGE ) ;
break ;
case POWER_SUPPLY_PROP_TEMP :
val - > intval = GOLDFISH_BATTERY_READ ( data , BATTERY_TEMP ) ;
break ;
case POWER_SUPPLY_PROP_CHARGE_COUNTER :
val - > intval = GOLDFISH_BATTERY_READ ( data ,
BATTERY_CHARGE_COUNTER ) ;
break ;
case POWER_SUPPLY_PROP_CURRENT_NOW :
val - > intval = GOLDFISH_BATTERY_READ ( data , BATTERY_CURRENT_NOW ) ;
break ;
case POWER_SUPPLY_PROP_CURRENT_AVG :
val - > intval = GOLDFISH_BATTERY_READ ( data , BATTERY_CURRENT_AVG ) ;
break ;
case POWER_SUPPLY_PROP_CHARGE_FULL :
val - > intval = GOLDFISH_BATTERY_READ ( data ,
BATTERY_CHARGE_FULL_UAH ) ;
break ;
case POWER_SUPPLY_PROP_CYCLE_COUNT :
val - > intval = GOLDFISH_BATTERY_READ ( data , BATTERY_CYCLE_COUNT ) ;
break ;
2013-01-25 19:30:00 +04:00
default :
ret = - EINVAL ;
break ;
}
return ret ;
}
static enum power_supply_property goldfish_battery_props [ ] = {
POWER_SUPPLY_PROP_STATUS ,
POWER_SUPPLY_PROP_HEALTH ,
POWER_SUPPLY_PROP_PRESENT ,
POWER_SUPPLY_PROP_TECHNOLOGY ,
POWER_SUPPLY_PROP_CAPACITY ,
2018-12-19 03:31:39 +03:00
POWER_SUPPLY_PROP_VOLTAGE_NOW ,
POWER_SUPPLY_PROP_TEMP ,
POWER_SUPPLY_PROP_CHARGE_COUNTER ,
POWER_SUPPLY_PROP_CURRENT_NOW ,
POWER_SUPPLY_PROP_CURRENT_AVG ,
POWER_SUPPLY_PROP_CHARGE_FULL ,
POWER_SUPPLY_PROP_CYCLE_COUNT ,
2013-01-25 19:30:00 +04:00
} ;
static enum power_supply_property goldfish_ac_props [ ] = {
POWER_SUPPLY_PROP_ONLINE ,
2018-12-19 03:31:39 +03:00
POWER_SUPPLY_PROP_VOLTAGE_MAX ,
POWER_SUPPLY_PROP_CURRENT_MAX ,
2013-01-25 19:30:00 +04:00
} ;
static irqreturn_t goldfish_battery_interrupt ( int irq , void * dev_id )
{
unsigned long irq_flags ;
struct goldfish_battery_data * data = dev_id ;
uint32_t status ;
spin_lock_irqsave ( & data - > lock , irq_flags ) ;
/* read status flags, which will clear the interrupt */
status = GOLDFISH_BATTERY_READ ( data , BATTERY_INT_STATUS ) ;
status & = BATTERY_INT_MASK ;
if ( status & BATTERY_STATUS_CHANGED )
2015-03-12 10:44:11 +03:00
power_supply_changed ( data - > battery ) ;
2013-01-25 19:30:00 +04:00
if ( status & AC_STATUS_CHANGED )
2015-03-12 10:44:11 +03:00
power_supply_changed ( data - > ac ) ;
2013-01-25 19:30:00 +04:00
spin_unlock_irqrestore ( & data - > lock , irq_flags ) ;
return status ? IRQ_HANDLED : IRQ_NONE ;
}
2015-03-12 10:44:11 +03:00
static const struct power_supply_desc battery_desc = {
. properties = goldfish_battery_props ,
. num_properties = ARRAY_SIZE ( goldfish_battery_props ) ,
. get_property = goldfish_battery_get_property ,
. name = " battery " ,
. type = POWER_SUPPLY_TYPE_BATTERY ,
} ;
static const struct power_supply_desc ac_desc = {
. properties = goldfish_ac_props ,
. num_properties = ARRAY_SIZE ( goldfish_ac_props ) ,
. get_property = goldfish_ac_get_property ,
. name = " ac " ,
. type = POWER_SUPPLY_TYPE_MAINS ,
} ;
2013-01-25 19:30:00 +04:00
static int goldfish_battery_probe ( struct platform_device * pdev )
{
int ret ;
struct resource * r ;
struct goldfish_battery_data * data ;
2015-03-12 10:44:11 +03:00
struct power_supply_config psy_cfg = { } ;
2013-01-25 19:30:00 +04:00
data = devm_kzalloc ( & pdev - > dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( data = = NULL )
return - ENOMEM ;
spin_lock_init ( & data - > lock ) ;
r = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( r = = NULL ) {
dev_err ( & pdev - > dev , " platform_get_resource failed \n " ) ;
return - ENODEV ;
}
2013-03-17 12:01:34 +04:00
data - > reg_base = devm_ioremap ( & pdev - > dev , r - > start , resource_size ( r ) ) ;
2013-01-25 19:30:00 +04:00
if ( data - > reg_base = = NULL ) {
dev_err ( & pdev - > dev , " unable to remap MMIO \n " ) ;
return - ENOMEM ;
}
data - > irq = platform_get_irq ( pdev , 0 ) ;
if ( data - > irq < 0 ) {
dev_err ( & pdev - > dev , " platform_get_irq failed \n " ) ;
return - ENODEV ;
}
2018-12-19 03:31:35 +03:00
ret = devm_request_irq ( & pdev - > dev , data - > irq ,
goldfish_battery_interrupt ,
IRQF_SHARED , pdev - > name , data ) ;
2013-01-25 19:30:00 +04:00
if ( ret )
return ret ;
2015-03-12 10:44:11 +03:00
psy_cfg . drv_data = data ;
2013-01-25 19:30:00 +04:00
2015-03-12 10:44:11 +03:00
data - > ac = power_supply_register ( & pdev - > dev , & ac_desc , & psy_cfg ) ;
if ( IS_ERR ( data - > ac ) )
return PTR_ERR ( data - > ac ) ;
data - > battery = power_supply_register ( & pdev - > dev , & battery_desc ,
& psy_cfg ) ;
if ( IS_ERR ( data - > battery ) ) {
power_supply_unregister ( data - > ac ) ;
return PTR_ERR ( data - > battery ) ;
2013-01-25 19:30:00 +04:00
}
platform_set_drvdata ( pdev , data ) ;
GOLDFISH_BATTERY_WRITE ( data , BATTERY_INT_ENABLE , BATTERY_INT_MASK ) ;
return 0 ;
}
static int goldfish_battery_remove ( struct platform_device * pdev )
{
struct goldfish_battery_data * data = platform_get_drvdata ( pdev ) ;
2015-03-12 10:44:11 +03:00
power_supply_unregister ( data - > battery ) ;
power_supply_unregister ( data - > ac ) ;
2013-01-25 19:30:00 +04:00
return 0 ;
}
2016-02-26 21:45:30 +03:00
static const struct of_device_id goldfish_battery_of_match [ ] = {
{ . compatible = " google,goldfish-battery " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , goldfish_battery_of_match ) ;
2016-03-02 02:46:10 +03:00
static const struct acpi_device_id goldfish_battery_acpi_match [ ] = {
{ " GFSH0001 " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( acpi , goldfish_battery_acpi_match ) ;
2013-01-25 19:30:00 +04:00
static struct platform_driver goldfish_battery_device = {
. probe = goldfish_battery_probe ,
. remove = goldfish_battery_remove ,
. driver = {
2016-02-26 21:45:30 +03:00
. name = " goldfish-battery " ,
. of_match_table = goldfish_battery_of_match ,
2016-03-02 02:46:10 +03:00
. acpi_match_table = ACPI_PTR ( goldfish_battery_acpi_match ) ,
2013-01-25 19:30:00 +04:00
}
} ;
module_platform_driver ( goldfish_battery_device ) ;
MODULE_AUTHOR ( " Mike Lockwood lockwood@android.com " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " Battery driver for the Goldfish emulator " ) ;