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 <[email protected]>"); MODULE_DESCRIPTION("S3C battery driver for SMDK Board"); MODULE_LICENSE("GPL");
加入了AD相关的代码,因为电池接在AD0上,通过采集AD0上的数据来判断电量,改动不是很大,仅供大家参考。
源码下载地址:点击打开链接 积分多的可以给我点