android2.3内核版本2.6.35.7,内核中已经自带了电池驱动,做项目是不能用的,下面是我修改过后的驱动源码:
/*
* linux/drivers/power/s3c_fake_battery.c
*
* Battery measurement code for S3C platform.
*
* based on palmtx_battery.c
*
* Copyright (C) 2009 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/device.h>
#include <linux/module.h>
#include <linux/power_supply.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/irq.h>
#include <linux/wakelock.h>
#include <asm/mach-types.h>
#include <mach/hardware.h>
#include <plat/gpio-cfg.h>
#define DRIVER_NAME "sec-fake-battery"
#include <linux/clk.h>
#include <linux/miscdevice.h>
#include <mach/regs-clock.h>
#include <mach/regs-adc.h>
#include <mach/regs-gpio.h>
#include <plat/regs-timer.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
static void __iomem *base_addr;
typedef struct {
wait_queue_head_t wait;
int channel;
int prescale;
} ADC_DEV;
static int __ADC_locked = 0;
static ADC_DEV adcdev;
static volatile int ev_adc = 0;
static int adc_data;
static struct clk *adc_clock;
static int old_flag = 0;
#define __ADCREG(name) (*(volatile unsigned long *)(base_addr + name))
#define ADCCON __ADCREG(S3C_ADCCON) // ADC control
#define ADCTSC __ADCREG(S3C_ADCTSC) // ADC touch screen control
#define ADCDLY __ADCREG(S3C_ADCDLY) // ADC start or Interval Delay
#define ADCDAT0 __ADCREG(S3C_ADCDAT0) // ADC conversion data 0
#define ADCDAT1 __ADCREG(S3C_ADCDAT1) // ADC conversion data 1
#define ADCUPDN __ADCREG(S3C_ADCUPDN) // Stylus Up/Down interrupt status
#define PRESCALE_DIS (0 << 14)
#define PRESCALE_EN (1 << 14)
#define PRSCVL(x) ((x) << 6)
#define ADC_INPUT(x) ((x) << 3)
#define ADC_START (1 << 0)
#define ADC_ENDCVT (1 << 15)
#define START_ADC_AIN(ch, prescale) \
do { \
ADCCON = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT((ch)) ; \
ADCCON |= ADC_START; \
} while (0)
#include <linux/workqueue.h>
struct delayed_work monitor_work;
struct workqueue_struct *monitor_wqueue;
struct delayed_work ad_work;
struct workqueue_struct *ad_wqueue;
static int ad_value=0;
static int times = 0;
#define ACIRQ IRQ_EINT(0)
#define ACIRQSTA S5PV210_GPH0(0)
static irqreturn_t adcdone_int_handler(int irq, void *dev_id)
{
#if 1
if (__ADC_locked) {
adc_data = ADCDAT0 & 0x3ff;
ev_adc = 1;
wake_up_interruptible(&adcdev.wait);
/* clear interrupt */
__raw_writel(0x0, base_addr + S3C_ADCCLRINT);
}
#endif
return IRQ_HANDLED;
}
static struct wake_lock vbus_wake_lock;
/* Prototypes */
extern int s3c_adc_get_adc_data(int channel);
static ssize_t s3c_bat_show_property(struct device *dev,
struct device_attribute *attr,
char *buf);
static ssize_t s3c_bat_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count);
#define FAKE_BAT_LEVEL 80
static struct device *dev;
static int s3c_battery_initial;
static int force_update;
static char *status_text[] = {
[POWER_SUPPLY_STATUS_UNKNOWN] = "Unknown",
[POWER_SUPPLY_STATUS_CHARGING] = "Charging",
[POWER_SUPPLY_STATUS_DISCHARGING] = "Discharging",
[POWER_SUPPLY_STATUS_NOT_CHARGING] = "Not Charging",
[POWER_SUPPLY_STATUS_FULL] = "Full",
};
typedef enum {
CHARGER_BATTERY = 0,
CHARGER_USB,
CHARGER_AC,
CHARGER_DISCHARGE
} charger_type_t;
struct battery_info {
u32 batt_id; /* Battery ID from ADC */
u32 batt_vol; /* Battery voltage from ADC */
u32 batt_vol_adc; /* Battery ADC value */
u32 batt_vol_adc_cal; /* Battery ADC value (calibrated)*/
u32 batt_temp; /* Battery Temperature (C) from ADC */
u32 batt_temp_adc; /* Battery Temperature ADC value */
u32 batt_temp_adc_cal; /* Battery Temperature ADC value (calibrated) */
u32 batt_current; /* Battery current from ADC */
u32 level; /* formula */
u32 charging_source; /* 0: no cable, 1:usb, 2:AC */
u32 charging_enabled; /* 0: Disable, 1: Enable */
u32 batt_health; /* Battery Health (Authority) */
u32 batt_is_full; /* 0 : Not full 1: Full */
};
/* lock to protect the battery info */
static DEFINE_MUTEX(work_lock);
struct s3c_battery_info {
int present;
int polling;
unsigned long polling_interval;
struct battery_info bat_info;
};
static struct s3c_battery_info s3c_bat_info;
static int s3c_adc_read(void)
{
int value;
__ADC_locked = 1;
START_ADC_AIN(adcdev.channel, adcdev.prescale);
wait_event_interruptible(adcdev.wait, ev_adc);
ev_adc = 0;
value = adc_data;
__ADC_locked = 0;
return value;
}
static int s3c_get_bat_level(struct power_supply *bat_ps)
{
int level;
int voltage;
//level = s3c_adc_read();
//printk("times is %d\n",times);
level = ad_value / times;
ad_value = 0;
times = 0;
//printk("read ad is +++++++++++++++++++++++++ %d\n",level);
voltage = (level * 3300) / 10230;
//printk("voltage is +++++++++++++++++++++++++ %d\n",voltage);
if(voltage < 185)
level = 0;
else if(voltage > 210)
level = 100;
else
level = (voltage - 185) * 4;
//printk("report level is %d\n",level);
return level;
}
static int s3c_get_bat_vol(struct power_supply *bat_ps)
{
int bat_vol = 0;
return bat_vol;
}
static u32 s3c_get_bat_health(void)
{
return s3c_bat_info.bat_info.batt_health;
}
static int s3c_get_bat_temp(struct power_supply *bat_ps)
{
int temp = 0;
return temp;
}
static int s3c_bat_get_charging_status(void)
{
charger_type_t charger = CHARGER_BATTERY;
int ret = 0;
charger = s3c_bat_info.bat_info.charging_source;
switch (charger) {
case CHARGER_BATTERY:
ret = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
case CHARGER_USB:
case CHARGER_AC:
if (s3c_bat_info.bat_info.level == 100
&& s3c_bat_info.bat_info.batt_is_full) {
ret = POWER_SUPPLY_STATUS_FULL;
} else {
ret = POWER_SUPPLY_STATUS_CHARGING;
}
break;
case CHARGER_DISCHARGE:
ret = POWER_SUPPLY_STATUS_DISCHARGING;
break;
default:
ret = POWER_SUPPLY_STATUS_UNKNOWN;
}
dev_dbg(dev, "%s : %s\n", __func__, status_text[ret]);
return ret;
}
static int s3c_bat_get_property(struct power_supply *bat_ps,
enum power_supply_property psp,
union power_supply_propval *val)
{
dev_dbg(bat_ps->dev, "%s : psp = %d\n", __func__, psp);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
val->intval = s3c_bat_get_charging_status();
break;
case POWER_SUPPLY_PROP_HEALTH:
val->intval = s3c_get_bat_health();
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = s3c_bat_info.present;
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = s3c_bat_info.bat_info.level;
dev_dbg(dev, "%s : level = %d\n", __func__,
val->intval);
break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = s3c_bat_info.bat_info.batt_temp;
dev_dbg(bat_ps->dev, "%s : temp = %d\n", __func__,
val->intval);
break;
default:
return -EINVAL;
}
return 0;
}
static int s3c_power_get_property(struct power_supply *bat_ps,
enum power_supply_property psp,
union power_supply_propval *val)
{
charger_type_t charger;
dev_dbg(bat_ps->dev, "%s : psp = %d\n", __func__, psp);
charger = s3c_bat_info.bat_info.charging_source;
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
if (bat_ps->type == POWER_SUPPLY_TYPE_MAINS)
val->intval = (charger == CHARGER_AC ? 1 : 0);
else if (bat_ps->type == POWER_SUPPLY_TYPE_USB)
val->intval = (charger == CHARGER_USB ? 1 : 0);
else
val->intval = 0;
break;
default:
return -EINVAL;
}
return 0;
}
#define SEC_BATTERY_ATTR(_name) \
{ \
.attr = { .name = #_name, .mode = S_IRUGO | S_IWUGO, .owner = THIS_MODULE }, \
.show = s3c_bat_show_property, \
.store = s3c_bat_store, \
}
static struct device_attribute s3c_battery_attrs[] = {
SEC_BATTERY_ATTR(batt_vol),
SEC_BATTERY_ATTR(batt_vol_adc),
SEC_BATTERY_ATTR(batt_vol_adc_cal),
SEC_BATTERY_ATTR(batt_temp),
SEC_BATTERY_ATTR(batt_temp_adc),
SEC_BATTERY_ATTR(batt_temp_adc_cal),
};
enum {
BATT_VOL = 0,
BATT_VOL_ADC,
BATT_VOL_ADC_CAL,
BATT_TEMP,
BATT_TEMP_ADC,
BATT_TEMP_ADC_CAL,
};
static int s3c_bat_create_attrs(struct device * dev)
{
int i, rc;
for (i = 0; i < ARRAY_SIZE(s3c_battery_attrs); i++) {
rc = device_create_file(dev, &s3c_battery_attrs[i]);
if (rc)
goto s3c_attrs_failed;
}
goto succeed;
s3c_attrs_failed:
while (i--)
device_remove_file(dev, &s3c_battery_attrs[i]);
succeed:
return rc;
}
static ssize_t s3c_bat_show_property(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int i = 0;
const ptrdiff_t off = attr - s3c_battery_attrs;
switch (off) {
case BATT_VOL:
i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
s3c_bat_info.bat_info.batt_vol);
break;
case BATT_VOL_ADC:
i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
s3c_bat_info.bat_info.batt_vol_adc);
break;
case BATT_VOL_ADC_CAL:
i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
s3c_bat_info.bat_info.batt_vol_adc_cal);
break;
case BATT_TEMP:
i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
s3c_bat_info.bat_info.batt_temp);
break;
case BATT_TEMP_ADC:
i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
s3c_bat_info.bat_info.batt_temp_adc);
break;
case BATT_TEMP_ADC_CAL:
i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
s3c_bat_info.bat_info.batt_temp_adc_cal);
break;
default:
i = -EINVAL;
}
return i;
}
static ssize_t s3c_bat_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int x = 0;
int ret = 0;
const ptrdiff_t off = attr - s3c_battery_attrs;
switch (off) {
case BATT_VOL_ADC_CAL:
if (sscanf(buf, "%d\n", &x) == 1) {
s3c_bat_info.bat_info.batt_vol_adc_cal = x;
ret = count;
}
dev_info(dev, "%s : batt_vol_adc_cal = %d\n", __func__, x);
break;
case BATT_TEMP_ADC_CAL:
if (sscanf(buf, "%d\n", &x) == 1) {
s3c_bat_info.bat_info.batt_temp_adc_cal = x;
ret = count;
}
dev_info(dev, "%s : batt_temp_adc_cal = %d\n", __func__, x);
break;
default:
ret = -EINVAL;
}
return ret;
}
static enum power_supply_property s3c_battery_properties[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CAPACITY,
};
static enum power_supply_property s3c_power_properties[] = {
POWER_SUPPLY_PROP_ONLINE,
};
static char *supply_list[] = {
"battery",
};
static struct power_supply s3c_power_supplies[] = {
{
.name = "battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = s3c_battery_properties,
.num_properties = ARRAY_SIZE(s3c_battery_properties),
.get_property = s3c_bat_get_property,
},
{
.name = "usb",
.type = POWER_SUPPLY_TYPE_USB,
.supplied_to = supply_list,
.num_supplicants = ARRAY_SIZE(supply_list),
.properties = s3c_power_properties,
.num_properties = ARRAY_SIZE(s3c_power_properties),
.get_property = s3c_power_get_property,
},
{
.name = "ac",
.type = POWER_SUPPLY_TYPE_MAINS,
.supplied_to = supply_list,
.num_supplicants = ARRAY_SIZE(supply_list),
.properties = s3c_power_properties,
.num_properties = ARRAY_SIZE(s3c_power_properties),
.get_property = s3c_power_get_property,
},
};
static int s3c_cable_status_update(int status)
{
int ret = 0;
charger_type_t source = CHARGER_BATTERY;
dev_dbg(dev, "%s\n", __func__);
if(!s3c_battery_initial)
return -EPERM;
switch(status) {
case CHARGER_BATTERY:
dev_dbg(dev, "%s : cable NOT PRESENT\n", __func__);
s3c_bat_info.bat_info.charging_source = CHARGER_BATTERY;
break;
case CHARGER_USB:
dev_dbg(dev, "%s : cable USB\n", __func__);
s3c_bat_info.bat_info.charging_source = CHARGER_USB;
break;
case CHARGER_AC:
dev_dbg(dev, "%s : cable AC\n", __func__);
s3c_bat_info.bat_info.charging_source = CHARGER_AC;
break;
case CHARGER_DISCHARGE:
dev_dbg(dev, "%s : Discharge\n", __func__);
s3c_bat_info.bat_info.charging_source = CHARGER_DISCHARGE;
break;
default:
dev_err(dev, "%s : Nat supported status\n", __func__);
ret = -EINVAL;
}
source = s3c_bat_info.bat_info.charging_source;
if (source == CHARGER_USB || source == CHARGER_AC) {
wake_lock(&vbus_wake_lock);
} else {
/* give userspace some time to see the uevent and update
* LED state or whatnot...
*/
wake_lock_timeout(&vbus_wake_lock, HZ / 2);
}
/* if the power source changes, all power supplies may change state */
power_supply_changed(&s3c_power_supplies[CHARGER_BATTERY]);
/*
power_supply_changed(&s3c_power_supplies[CHARGER_USB]);
power_supply_changed(&s3c_power_supplies[CHARGER_AC]);
*/
dev_dbg(dev, "%s : call power_supply_changed\n", __func__);
return ret;
}
static void s3c_bat_status_update(struct power_supply *bat_ps)
{
int old_level, old_temp, old_is_full;
dev_dbg(dev, "%s ++\n", __func__);
if(!s3c_battery_initial)
return;
mutex_lock(&work_lock);
old_temp = s3c_bat_info.bat_info.batt_temp;
old_level = s3c_bat_info.bat_info.level;
old_is_full = s3c_bat_info.bat_info.batt_is_full;
s3c_bat_info.bat_info.batt_temp = s3c_get_bat_temp(bat_ps);
s3c_bat_info.bat_info.level = s3c_get_bat_level(bat_ps);
s3c_bat_info.bat_info.batt_vol = s3c_get_bat_vol(bat_ps);
if (old_level != s3c_bat_info.bat_info.level
|| old_temp != s3c_bat_info.bat_info.batt_temp
|| old_is_full != s3c_bat_info.bat_info.batt_is_full
|| force_update) {
force_update = 0;
power_supply_changed(bat_ps);
dev_dbg(dev, "%s : call power_supply_changed\n", __func__);
}
mutex_unlock(&work_lock);
dev_dbg(dev, "%s --\n", __func__);
}
void s3c_cable_check_status(int flag)
{
charger_type_t status = 0;
#ifdef USB_CHARG
#else
if(flag == 1) //usb not supports charging
return;
#endif
switch(flag) {
case 0: // Battery
status = CHARGER_BATTERY;
break;
case 1:
status = CHARGER_USB;
break;
case 2:
status = CHARGER_AC;
break;
default:
status = CHARGER_BATTERY;
break;
}
#ifdef USB_CHARG
#else
#endif
if(!gpio_get_value(ACIRQSTA)) {
status = CHARGER_AC;
flag = 2;
}
s3c_cable_status_update(status);
old_flag = flag;
}
EXPORT_SYMBOL(s3c_cable_check_status);
#ifdef CONFIG_PM
static int s3c_bat_suspend(struct platform_device *pdev,
pm_message_t state)
{
dev_info(dev, "%s\n", __func__);
return 0;
}
static int s3c_bat_resume(struct platform_device *pdev)
{
dev_info(dev, "%s\n", __func__);
return 0;
}
#else
#define s3c_bat_suspend NULL
#define s3c_bat_resume NULL
#endif /* CONFIG_PM */
static irqreturn_t ac_status(int irq, void *dev_id)
{
int status;
disable_irq_nosync(irq);
status = gpio_get_value(ACIRQSTA);
if(status)
s3c_cable_check_status(CHARGER_BATTERY);
else
s3c_cable_check_status(CHARGER_AC);
enable_irq(irq);
return IRQ_HANDLED;
}
static void s3c_battery_work(struct work_struct *work)
{
const int interval = HZ * 60;
s3c_bat_status_update(&s3c_power_supplies[CHARGER_BATTERY]);
queue_delayed_work(monitor_wqueue, &monitor_work, interval);
}
static void s3c_ad_work(struct work_struct *work)
{
const int interval = HZ * 1;
ad_value += s3c_adc_read();
times ++;
queue_delayed_work(ad_wqueue, &ad_work, interval);
}
static int __devinit s3c_bat_probe(struct platform_device *pdev)
{
int i;
int ret = 0;
dev = &pdev->dev;
dev_info(dev, "%s\n", __func__);
s3c_bat_info.present = 1;
s3c_bat_info.bat_info.batt_id = 0;
s3c_bat_info.bat_info.batt_vol = 0;
s3c_bat_info.bat_info.batt_vol_adc = 0;
s3c_bat_info.bat_info.batt_vol_adc_cal = 0;
s3c_bat_info.bat_info.batt_temp = 0;
s3c_bat_info.bat_info.batt_temp_adc = 0;
s3c_bat_info.bat_info.batt_temp_adc_cal = 0;
s3c_bat_info.bat_info.batt_current = 0;
s3c_bat_info.bat_info.level = 0;
s3c_bat_info.bat_info.charging_source = CHARGER_BATTERY;
s3c_bat_info.bat_info.charging_enabled = 0;
s3c_bat_info.bat_info.batt_health = POWER_SUPPLY_HEALTH_GOOD;
/* init power supplier framework */
for (i = 0; i < ARRAY_SIZE(s3c_power_supplies); i++) {
ret = power_supply_register(&pdev->dev,
&s3c_power_supplies[i]);
if (ret) {
dev_err(dev, "Failed to register"
"power supply %d,%d\n", i, ret);
goto __end__;
}
}
/* create sec detail attributes */
s3c_bat_create_attrs(s3c_power_supplies[CHARGER_BATTERY].dev);
s3c_battery_initial = 1;
force_update = 0;
//s3c_bat_status_update(
//&s3c_power_supplies[CHARGER_BATTERY]);
base_addr = ioremap(SAMSUNG_PA_ADC, 0x20);
if (base_addr == NULL) {
printk(KERN_ERR "Failed to remap register block\n");
return -ENOMEM;
}
adc_clock = clk_get(NULL, "adc");
if (!adc_clock) {
printk(KERN_ERR "failed to get adc clock source\n");
return -ENOENT;
}
clk_enable(adc_clock);
/* normal ADC */
ADCTSC = 0;
ret = request_irq(IRQ_ADC, adcdone_int_handler, IRQF_SHARED, DRIVER_NAME, &adcdev);
if (ret) {
printk("request IRQ %d failed for adc, %d\n", IRQ_ADC, ret);
iounmap(base_addr);
return ret;
}
init_waitqueue_head(&(adcdev.wait));
if(!gpio_get_value(ACIRQSTA))
s3c_cable_check_status(CHARGER_AC);
ret = request_irq(IRQ_EINT(0), ac_status, IRQ_TYPE_EDGE_BOTH, DRIVER_NAME, &dev);
if (ret) {
printk("request IRQ %d failed for adc, %d\n", IRQ_EINT(0), ret);
iounmap(base_addr);
return ret;
}
adcdev.channel=0;
adcdev.prescale=0xff;
INIT_DELAYED_WORK(&ad_work, s3c_ad_work);
ad_wqueue = create_singlethread_workqueue(dev_name(&pdev->dev));
queue_delayed_work(ad_wqueue, &ad_work, HZ * 1);
INIT_DELAYED_WORK(&monitor_work, s3c_battery_work);
monitor_wqueue = create_singlethread_workqueue(dev_name(&pdev->dev));
queue_delayed_work(monitor_wqueue, &monitor_work, HZ * 2);
__end__:
return ret;
}
static int __devexit s3c_bat_remove(struct platform_device *pdev)
{
int i;
dev_info(dev, "%s\n", __func__);
for (i = 0; i < ARRAY_SIZE(s3c_power_supplies); i++) {
power_supply_unregister(&s3c_power_supplies[i]);
}
free_irq(IRQ_ADC, &adcdev);
iounmap(base_addr);
if (adc_clock) {
clk_disable(adc_clock);
clk_put(adc_clock);
adc_clock = NULL;
}
return 0;
}
static struct platform_driver s3c_bat_driver = {
.driver.name = DRIVER_NAME,
.driver.owner = THIS_MODULE,
.probe = s3c_bat_probe,
.remove = __devexit_p(s3c_bat_remove),
.suspend = s3c_bat_suspend,
.resume = s3c_bat_resume,
};
/* Initailize GPIO */
static void s3c_bat_init_hw(void)
{
}
static int __init s3c_bat_init(void)
{
pr_info("%s\n", __func__);
s3c_bat_init_hw();
wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "vbus_present");
return platform_driver_register(&s3c_bat_driver);
}
static void __exit s3c_bat_exit(void)
{
pr_info("%s\n", __func__);
platform_driver_unregister(&s3c_bat_driver);
}
module_init(s3c_bat_init);
module_exit(s3c_bat_exit);
MODULE_AUTHOR("HuiSung Kang <hs1218.kang@samsung.com>");
MODULE_DESCRIPTION("S3C battery driver for SMDK Board");
MODULE_LICENSE("GPL");
加入了AD相关的代码,因为电池接在AD0上,通过采集AD0上的数据来判断电量,改动不是很大,仅供大家参考。
源码下载地址:点击打开链接 积分多的可以给我点