blob: 05a26a94b2df3184dbe99915f09653970c6291b2 [file] [log] [blame]
/*
* Samsung Power and Charger.
*
* drivers/power/spa_power.c
*
* Drivers for samsung battery and charger.
* (distributed from spa.c and linear-power.c)
*
* Copyright (C) 2012, Samsung Electronics.
*
* 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
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/wakelock.h>
#include <linux/power_supply.h>
#include <linux/spa_power.h>
#define BATT_TYPE "SDI_SDI"
#define CONFIG_SEC_BATT_EXT_ATTRS
#define SPA_MODEL_NAME_LEN 20
struct spa_batt_status
{
int capacity;
int voltage;
int temp;
int temp_adc;
int present;
int capacity_lvl;
int status;
int health;
int lp_charging;
int charging_source;
int ac_online;
int usb_online;
};
struct spa_ps
{
struct spa_power_desc *spa_power_iter;
struct power_supply ac;
struct power_supply batt;
struct power_supply usb;
struct spa_batt_status state;
char model[SPA_MODEL_NAME_LEN+1];
};
static enum power_supply_property spa_batt_props[] = {
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_BATT_TEMP_ADC,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_MODEL_NAME,
};
static enum power_supply_property spa_charger_props[] = {
POWER_SUPPLY_PROP_ONLINE,
};
static int spa_batt_get_property( struct power_supply *batt, enum power_supply_property property, union power_supply_propval *propval)
{
int ret = 0;
struct spa_ps *spa_ps_iter =
container_of(batt, struct spa_ps, batt);
switch (property) {
case POWER_SUPPLY_PROP_STATUS:
propval->intval = spa_ps_iter->state.status;
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
propval->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
case POWER_SUPPLY_PROP_CAPACITY:
propval->intval = spa_ps_iter->state.capacity;
break;
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
propval->intval = spa_ps_iter->state.capacity_lvl;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
propval->intval = spa_ps_iter->state.voltage * 1000;
break;
case POWER_SUPPLY_PROP_TEMP: // Kelvin unit to Celsius (C = K - 273.15). x10ed unit
propval->intval = spa_ps_iter->state.temp;
break;
case POWER_SUPPLY_PROP_HEALTH:
propval->intval = spa_ps_iter->state.health;
break;
case POWER_SUPPLY_PROP_PRESENT:
propval->intval = spa_ps_iter->state.present;
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
propval->strval = spa_ps_iter->model;
break;
case POWER_SUPPLY_PROP_BATT_TEMP_ADC:
propval->intval = spa_ps_iter->state.temp_adc;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int spa_batt_set_property( struct power_supply *batt, enum power_supply_property property, union power_supply_propval *propval)
{
int ret = 0;
struct spa_ps *spa_ps_iter = container_of(batt,struct spa_ps, batt);
switch (property) {
case POWER_SUPPLY_PROP_STATUS:
spa_ps_iter->state.status = propval->intval;
break;
case POWER_SUPPLY_PROP_CAPACITY:
spa_ps_iter->state.capacity = propval->intval;
break;
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
spa_ps_iter->state.capacity_lvl = propval->intval;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
spa_ps_iter->state.voltage = propval->intval;
break;
case POWER_SUPPLY_PROP_TEMP: // Celsius to Kelvin ( K = C + 273.15). x10ed unit
spa_ps_iter->state.temp = propval->intval;
break;
case POWER_SUPPLY_PROP_HEALTH:
spa_ps_iter->state.health = propval->intval;
break;
case POWER_SUPPLY_PROP_PRESENT:
spa_ps_iter->state.present = propval->intval;
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
strncpy(spa_ps_iter->model, propval->strval, SPA_MODEL_NAME_LEN);
break;
case POWER_SUPPLY_PROP_BATT_TEMP_ADC:
spa_ps_iter->state.temp_adc = propval->intval;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int spa_ac_get_property( struct power_supply *ac, enum power_supply_property property, union power_supply_propval *propval)
{
int ret = 0;
struct spa_ps *spa_ps_iter = container_of(ac,struct spa_ps, ac);
switch(property)
{
case POWER_SUPPLY_PROP_ONLINE:
propval->intval = spa_ps_iter->state.ac_online;
break;
default:
break;
}
return ret;
}
static int spa_ac_set_property( struct power_supply *ac, enum power_supply_property property, union power_supply_propval *propval)
{
int ret = 0;
struct spa_ps *spa_ps_iter = container_of(ac,struct spa_ps, ac);
switch(property)
{
case POWER_SUPPLY_PROP_ONLINE:
spa_ps_iter->state.ac_online = propval->intval;
break;
default:
break;
}
return ret;
}
static int spa_usb_get_property( struct power_supply *usb, enum power_supply_property property, union power_supply_propval *propval)
{
int ret = 0;
struct spa_ps *spa_ps_iter = container_of(usb,struct spa_ps, usb);
switch(property)
{
case POWER_SUPPLY_PROP_ONLINE:
propval->intval = spa_ps_iter->state.usb_online;
break;
default:
break;
}
return ret;
}
static int spa_usb_set_property( struct power_supply *usb, enum power_supply_property property, union power_supply_propval *propval)
{
int ret = 0;
struct spa_ps *spa_ps_iter = container_of(usb,struct spa_ps, usb);
switch(property)
{
case POWER_SUPPLY_PROP_ONLINE:
spa_ps_iter->state.usb_online = propval->intval;
break;
default:
break;
}
return ret;
}
#if 0 // defined(CONFIG_SEC_BATT_EXT_ATTRS)
enum
{
SS_BATT_LP_CHARGING,
SS_BATT_CHARGING_SOURCE,
SS_BATT_TEMP_AVER,
SS_BATT_TEMP_ADC_AVER,
SS_BATT_TYPE,
SS_BATT_READ_ADJ_SOC,
SS_BATT_RESET_SOC,
};
static ssize_t ss_batt_ext_attrs_show(struct device *pdev, struct device_attribute *attr, char *buf);
static ssize_t ss_batt_ext_attrs_store(struct device *pdev, struct device_attribute *attr, const char *buf, size_t count);
static struct device_attribute ss_batt_ext_attrs[]=
{
__ATTR(batt_lp_charging, 0644, ss_batt_ext_attrs_show, ss_batt_ext_attrs_store),
__ATTR(batt_charging_source, 0644, ss_batt_ext_attrs_show, ss_batt_ext_attrs_store),
__ATTR(batt_temp_aver, 0644, ss_batt_ext_attrs_show, ss_batt_ext_attrs_store),
__ATTR(batt_temp_adc_aver, 0644, ss_batt_ext_attrs_show, ss_batt_ext_attrs_store),
__ATTR(batt_type, 0644, ss_batt_ext_attrs_show, NULL),
__ATTR(batt_read_adj_soc, 0644, ss_batt_ext_attrs_show , NULL),
__ATTR(batt_reset_soc, 0664, NULL, ss_batt_ext_attrs_store),
};
static ssize_t ss_batt_ext_attrs_show(struct device *pdev, struct device_attribute *attr, char *buf)
{
ssize_t count=0;
int lp_charging=0;
const ptrdiff_t off = attr-ss_batt_ext_attrs;
struct power_supply *ps;
union power_supply_propval propval;
propval.intval=0;
propval.strval=0;
switch(off)
{
case SS_BATT_LP_CHARGING:
lp_charging = spa_ps_iter->state.lp_charging;
count+=scnprintf(buf+count, PAGE_SIZE-count, "%d\n", lp_charging);
break;
}
return 0;
}
static ssize_t ss_batt_ext_attrs_store(struct device *pdev, struct device_attribute *attr, const char *buf, size_t count)
{
return 0;
}
unsigned int lp_boot_mode;
static int get_boot_mode(char *str)
{
get_option(&str, &lp_boot_mode);
return 1;
}
__setup("lpcharge=",get_boot_mode);
#endif
//static int spa_ps_probe(struct platform_device *pdev)
int spa_ps_init(struct platform_device *pdev)
{
int ret=0;
struct spa_ps *spa_ps;
printk("%s : spa_ps start\n", __func__);
spa_ps = kzalloc(sizeof(struct spa_ps), GFP_KERNEL);
if(spa_ps == NULL)
{
printk("%s: Failed to allocate memory\n", __func__);
return -ENOMEM;
}
spa_ps->model[SPA_MODEL_NAME_LEN]=0;
spa_ps->batt.properties= spa_batt_props;
spa_ps->batt.num_properties = ARRAY_SIZE(spa_batt_props);
spa_ps->batt.get_property = spa_batt_get_property;
spa_ps->batt.set_property = spa_batt_set_property;
spa_ps->batt.name = "battery";
spa_ps->batt.type = POWER_SUPPLY_TYPE_BATTERY;
ret = power_supply_register(&pdev->dev, &spa_ps->batt);
if( ret )
{
printk("%s : Failed to register ps battery\n", __func__);
goto LB_SPA_PS_PROBE_ERR_S3;
}
spa_ps->ac.properties= spa_charger_props;
spa_ps->ac.num_properties = ARRAY_SIZE(spa_charger_props);
spa_ps->ac.get_property = spa_ac_get_property;
spa_ps->ac.set_property = spa_ac_set_property;
spa_ps->ac.name = "ac";
spa_ps->ac.type = POWER_SUPPLY_TYPE_MAINS;
ret = power_supply_register(&pdev->dev, &spa_ps->ac);
if( ret )
{
printk("%s : Failed to register ps ac\n", __func__);
goto LB_SPA_PS_PROBE_ERR_S2;
}
spa_ps->usb.properties= spa_charger_props;
spa_ps->usb.num_properties = ARRAY_SIZE(spa_charger_props);
spa_ps->usb.get_property = spa_usb_get_property;
spa_ps->usb.set_property = spa_usb_set_property;
spa_ps->usb.name = "usb";
spa_ps->usb.type = POWER_SUPPLY_TYPE_USB;
ret = power_supply_register(&pdev->dev, &spa_ps->usb);
if( ret )
{
printk("%s : Failed to register ps usb\n", __func__);
goto LB_SPA_PS_PROBE_ERR_S1;
}
spa_ps->state.ac_online=0;
spa_ps->state.usb_online=0;
spa_ps->state.status=POWER_SUPPLY_STATUS_DISCHARGING;
spa_ps->state.temp=0;
spa_ps->state.temp_adc=0;
spa_ps->state.health=POWER_SUPPLY_HEALTH_GOOD;
spa_ps->state.voltage= 0;
spa_ps->state.capacity= 50;
spa_ps->state.present=1;
power_supply_changed(&spa_ps->batt);
printk("%s : spa_ps end\n", __func__);
goto LB_SPA_PS_PROBE_SUCCESS;
LB_SPA_PS_PROBE_ERR_S1:
power_supply_unregister(&spa_ps->ac);
LB_SPA_PS_PROBE_ERR_S2:
power_supply_unregister(&spa_ps->batt);
LB_SPA_PS_PROBE_ERR_S3:
kfree(spa_ps);
return ret;
LB_SPA_PS_PROBE_SUCCESS:
return 0;
}
EXPORT_SYMBOL(spa_ps_init);
#if 0
static int __devexit spa_ps_remove(struct platform_device *pdev)
{
return 0;
}
static struct platform_driver spa_ps_driver = {
.driver = {
.name = "spa_ps",
},
.probe = spa_ps_probe,
.remove = spa_ps_remove,
};
static int __init spa_ps_init(void)
{
return platform_driver_register(&spa_ps_driver);
}
static void __exit spa_ps_exit(void)
{
platform_driver_unregister(&spa_ps_driver);
}
module_init(spa_ps_init);
module_exit(spa_ps_exit);
#endif
MODULE_DESCRIPTION("SPA PS");
MODULE_LICENSE("GPL");