From ee63c07ee8a20a7f31ddb96263752feeb4bea8c8 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E7=BD=97=E4=BC=9F?= Date: Wed, 19 May 2010 12:19:14 +0000 Subject: [PATCH] add battery module on 100520 --- .config | 3 +- arch/arm/mach-rk2818/adc.c | 3 +- arch/arm/mach-rk2818/board-midsdk.c | 1 + arch/arm/mach-rk2818/devices.c | 5 + arch/arm/mach-rk2818/devices.h | 1 + drivers/input/keyboard/Kconfig | 2 +- drivers/input/keyboard/rk2818_adckey.c | 81 +++-- drivers/power/Kconfig | 5 + drivers/power/Makefile | 1 + drivers/power/rk2818_battery.c | 410 +++++++++++++++++++++++++ 10 files changed, 480 insertions(+), 32 deletions(-) create mode 100644 drivers/power/rk2818_battery.c diff --git a/.config b/.config index 721e216f1858..93dd88c4b0ba 100644 --- a/.config +++ b/.config @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux kernel version: 2.6.32.9 -# Tue May 18 22:11:51 2010 +# Wed May 19 19:32:08 2010 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -819,6 +819,7 @@ CONFIG_POWER_SUPPLY=y # CONFIG_BATTERY_DS2782 is not set # CONFIG_BATTERY_BQ27x00 is not set # CONFIG_BATTERY_MAX17040 is not set +CONFIG_BATTERY_RK2818=y # CONFIG_HWMON is not set # CONFIG_THERMAL is not set # CONFIG_WATCHDOG is not set diff --git a/arch/arm/mach-rk2818/adc.c b/arch/arm/mach-rk2818/adc.c index 9b770e2b3f32..f3e762cfc01a 100644 --- a/arch/arm/mach-rk2818/adc.c +++ b/arch/arm/mach-rk2818/adc.c @@ -376,7 +376,7 @@ static int rk28_adc_probe(struct platform_device *pdev) rk28_adc_int_disable(adc); pAdcDev = adc; - printk("Enter::%s,LINE=%d\n",__FUNCTION__,__LINE__); + printk(KERN_INFO "rk2818 adc: driver initialized\n"); return 0; err_clk: @@ -452,7 +452,6 @@ static int __init adc_init(void) ret = platform_driver_register(&rk28_adc_driver); if (ret) printk(KERN_ERR "%s: failed to add adc driver\n", __func__); - printk("Enter::%s,LINE=%d\n",__FUNCTION__,__LINE__); return ret; } diff --git a/arch/arm/mach-rk2818/board-midsdk.c b/arch/arm/mach-rk2818/board-midsdk.c index 9e709645c749..85fe4c1d8e42 100644 --- a/arch/arm/mach-rk2818/board-midsdk.c +++ b/arch/arm/mach-rk2818/board-midsdk.c @@ -291,6 +291,7 @@ static struct platform_device *devices[] __initdata = { &rk2818_device_pmem, &rk2818_device_adc, &rk2818_device_adckey, + &rk2818_device_battery, &rk2818_device_fb, &rk2818_device_backlight, &rk2818_device_dsp, diff --git a/arch/arm/mach-rk2818/devices.c b/arch/arm/mach-rk2818/devices.c index c85174224f1b..d5f70bbeb380 100644 --- a/arch/arm/mach-rk2818/devices.c +++ b/arch/arm/mach-rk2818/devices.c @@ -279,6 +279,11 @@ struct platform_device rk2818_device_adckey = { .dev.parent = &rk2818_device_adc.dev, }; +struct platform_device rk2818_device_battery = { + .name = "rk2818-battery", + .id = -1, +}; + /* * rk2818 dsp device diff --git a/arch/arm/mach-rk2818/devices.h b/arch/arm/mach-rk2818/devices.h index fb31f0cf6636..fdfda9e564c4 100644 --- a/arch/arm/mach-rk2818/devices.h +++ b/arch/arm/mach-rk2818/devices.h @@ -30,6 +30,7 @@ extern struct platform_device rk2818_device_pmem; extern struct platform_device rk2818_device_fb; extern struct platform_device rk2818_device_adc; extern struct platform_device rk2818_device_adckey; +extern struct platform_device rk2818_device_battery; extern struct platform_device rk2818_device_backlight; extern struct platform_device rk2818_device_dsp; #endif diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 5003bda21d01..7f51714d1772 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -424,7 +424,7 @@ config KEYBOARD_W90P910 module will be called w90p910_keypad. config KEYBOARD_RK28ADC tristate "RK2818 ADC Keypad support" - depends on ARCH_RK2818 + depends on RK28_ADC default y help Say Y here to enable the adc keypad on SDK board diff --git a/drivers/input/keyboard/rk2818_adckey.c b/drivers/input/keyboard/rk2818_adckey.c index fbdd066d8d63..e8e2a82c392d 100644 --- a/drivers/input/keyboard/rk2818_adckey.c +++ b/drivers/input/keyboard/rk2818_adckey.c @@ -23,10 +23,10 @@ #include #include #include - +#include #include -#if 0 +#if 1 #define DBG(x...) printk(x) #else #define DBG(x...) @@ -34,14 +34,16 @@ //ROCKCHIP AD KEY CODE ,for demo board // key ---> EV -#define AD2KEY1 114 ///VOLUME_DOWN -#define AD2KEY2 115 ///VOLUME_UP -#define AD2KEY3 59 ///MENU -#define AD2KEY4 102 ///HOME -#define AD2KEY5 158 ///BACK -#define AD2KEY6 61 ///CALL - -#define KEYMENU AD2KEY6 +#define AD2KEY1 114 ///VOLUME_DOWN +#define AD2KEY2 115 ///VOLUME_UP +#define AD2KEY3 59 ///MENU +#define AD2KEY4 102 ///HOME +#define AD2KEY5 158 ///BACK +#define AD2KEY6 61 ///CALL + +#define KEYSTART 28 //ENTER +#define KEYMENU AD2KEY6 ///CALL +#define KEY_PLAYON_PIN RK2818_PIN_PE1 #define ENDCALL 62 #define Valuedrift 50 @@ -56,6 +58,7 @@ volatile int gADSampleTimes = 0; volatile int gAdcChanel = 0; volatile int gAdcValue[4]={0, 0, 0, 0}; //0->ch0 1->ch1 2->ch2 3->ch3 +volatile int gStatePlaykey = 0; volatile unsigned int gCodeCount = 0; volatile unsigned int gThisCode = 0; @@ -94,7 +97,6 @@ struct rk28_adckey struct input_dev *input_dev; struct timer_list timer; unsigned char keycodes[ADKEYNUM]; - struct clk *clk; void __iomem *mmio_base; }; @@ -112,6 +114,17 @@ unsigned int rk28_get_keycode(unsigned int advalue,pADC_keyst ptab) return 0; } +static irqreturn_t rk2818_playkey_irq(int irq, void *handle) +{ + input_report_key(pRk28AdcKey->input_dev,KEYSTART,1); + input_sync(pRk28AdcKey->input_dev); + input_report_key(pRk28AdcKey->input_dev,KEYSTART,0); + input_sync(pRk28AdcKey->input_dev); + printk("Enter::%s,LINE=%d,KEYSTART=%d,0\n",__FUNCTION__,__LINE__,KEYSTART); + + return IRQ_HANDLED; +} + static int rk28_adckey_open(struct input_dev *dev) { //struct rk28_adckey *adckey = input_get_drvdata(dev); @@ -128,26 +141,20 @@ static void rk28_adckey_close(struct input_dev *dev) #ifdef CONFIG_PM static int rk28_adckey_suspend(struct platform_device *pdev, pm_message_t state) { - struct rk28_adckey *adckey = platform_get_drvdata(pdev); + //struct rk28_adckey *adckey = platform_get_drvdata(pdev); - clk_disable(adckey->clk); return 0; } static int rk28_adckey_resume(struct platform_device *pdev) { - struct rk28_adckey *adckey = platform_get_drvdata(pdev); - struct input_dev *input_dev = adckey->input_dev; - + //struct rk28_adckey *adckey = platform_get_drvdata(pdev); + //struct input_dev *input_dev = adckey->input_dev; +#if 0 mutex_lock(&input_dev->mutex); - if (input_dev->users) { - /* Enable unit clock */ - clk_enable(adckey->clk); - } - mutex_unlock(&input_dev->mutex); - +#endif return 0; } #else @@ -267,7 +274,7 @@ static int __devinit rk28_adckey_probe(struct platform_device *pdev) if (!input_dev) { dev_err(&pdev->dev, "failed to allocate input device\n"); error = -ENOMEM; - goto failed_put_clk; + goto failed_free; } input_dev->name = pdev->name; @@ -314,17 +321,33 @@ static int __devinit rk28_adckey_probe(struct platform_device *pdev) goto failed_free_dev; } + error = gpio_request(KEY_PLAYON_PIN, "play key gpio"); + if (error) { + dev_err(&pdev->dev, "failed to request play key gpio\n"); + goto free_gpio; + } + + gpio_pull_updown(KEY_PLAYON_PIN,GPIOPullUp); + error = request_irq(gpio_to_irq(KEY_PLAYON_PIN),rk2818_playkey_irq,IRQF_TRIGGER_FALLING,NULL,NULL); + if(error) + { + printk("unable to request play key irq\n"); + goto free_gpio_irq; + } + setup_timer(&adckey->timer, rk28_adkeyscan_timer, (unsigned long)adckey); adckey->timer.expires = jiffies+50; add_timer(&adckey->timer); - DBG("Enter::%s,LINE=%d\n",__FUNCTION__,__LINE__); + printk(KERN_INFO "rk2818_adckey: driver initialized\n"); return 0; - + +free_gpio_irq: + free_irq(gpio_to_irq(KEY_PLAYON_PIN),NULL); +free_gpio: + gpio_free(KEY_PLAYON_PIN); failed_free_dev: platform_set_drvdata(pdev, NULL); input_free_device(input_dev); -failed_put_clk: - //clk_put(adckey->clk); failed_free: kfree(adckey); return error; @@ -339,6 +362,9 @@ static int __devexit rk28_adckey_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); rk28_adc_release(adckey->client); kfree(adckey); + free_irq(gpio_to_irq(KEY_PLAYON_PIN),NULL); + gpio_free(KEY_PLAYON_PIN); + return 0; } @@ -356,7 +382,6 @@ static struct platform_driver rk28_adckey_driver = int __init rk28_adckey_init(void) { - DBG("Enter::%s,LINE=%d\n",__FUNCTION__,__LINE__); return platform_driver_register(&rk28_adckey_driver); } diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index cea6cef27e89..c91ed7f7e27f 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -110,4 +110,9 @@ config CHARGER_PCF50633 help Say Y to include support for NXP PCF50633 Main Battery Charger. +config BATTERY_RK2818 + tristate "RK2818 battery" + depends on KEYBOARD_RK28ADC + help + Say Y to enable support for the battery on the RK2818. endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index b96f29d91c28..10caf2200b85 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -29,3 +29,4 @@ obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o +obj-$(CONFIG_BATTERY_RK2818) += rk2818_battery.o diff --git a/drivers/power/rk2818_battery.c b/drivers/power/rk2818_battery.c new file mode 100644 index 000000000000..31921f3342cb --- /dev/null +++ b/drivers/power/rk2818_battery.c @@ -0,0 +1,410 @@ +/* drivers/power/rk2818_battery.c + * + * Power supply driver for the rk2818 emulator + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 1 +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#define CHN_BAT_ADC 0 +#define CHN_USB_ADC 2 +#define BATT_LEVEL_EMPTY 0 +#define BATT_PRESENT_TRUE 1 +#define BATT_PRESENT_FALSE 0 +#define BATT_NOMAL_VOL_VALUE 4000 + +static int gBatStatus = POWER_SUPPLY_STATUS_UNKNOWN; +static int gBatHealth = POWER_SUPPLY_HEALTH_GOOD; +static int gBatCapacity = BATT_LEVEL_EMPTY; +static int gBatPresent = BATT_PRESENT_TRUE; +static int gBatVoltage = BATT_NOMAL_VOL_VALUE; +extern int dwc_vbus_status(void); + +struct rk2818_battery_data { + int irq; + spinlock_t lock; + struct timer_list timer; + struct power_supply battery; + struct power_supply usb; + struct power_supply ac; + + int adc_bat_divider; + int bat_max; + int bat_min; +}; + +#define RK2818_BATTERY_WRITE(data, addr, x) (writel(x, data->reg_base + addr)) + + +/* temporary variable used between rk2818_battery_probe() and rk2818_battery_open() */ +static struct rk2818_battery_data *gBatteryData; + +enum { + BATTERY_STATUS = 0, + BATTERY_HEALTH = 1, + BATTERY_PRESENT = 2, + BATTERY_CAPACITY = 3, + BATTERY_AC_ONLINE = 4, + BATTERY_STATUS_CHANGED = 5, + AC_STATUS_CHANGED = 6, + BATTERY_INT_STATUS = 7, + BATTERY_INT_ENABLE = 8, +}; + +typedef enum { + CHARGER_BATTERY = 0, + CHARGER_USB, + CHARGER_AC +} charger_type_t; + +static int rk2818_get_charge_status(void) +{ + //return dwc_vbus_status(); + return 1; +} + +static void rk2818_get_bat_status(struct rk2818_battery_data *bat) +{ + if(rk2818_get_charge_status() == 1) + gBatStatus = POWER_SUPPLY_STATUS_CHARGING; + else + gBatStatus = POWER_SUPPLY_STATUS_NOT_CHARGING; +} + +static void rk2818_get_bat_health(struct rk2818_battery_data *bat) +{ + gBatHealth = POWER_SUPPLY_HEALTH_GOOD; +} + +static void rk2818_get_bat_present(struct rk2818_battery_data *bat) +{ + if(gBatVoltage < bat->bat_min) + gBatPresent = 0; + else + gBatPresent = 1; +} + +static void rk2818_get_bat_voltage(struct rk2818_battery_data *bat) +{ + unsigned long value; + value = gAdcValue[CHN_BAT_ADC]; + gBatVoltage = value * 1000000 / bat->adc_bat_divider; +} + +static void rk2818_get_bat_capacity(struct rk2818_battery_data *bat) +{ + gBatCapacity = (gBatVoltage * 100) / bat->bat_max; +} + + +static void rk2818_batscan_timer(unsigned long data) +{ + gBatteryData->timer.expires = jiffies + msecs_to_jiffies(20); + add_timer(&gBatteryData->timer); + rk2818_get_bat_status(gBatteryData); + rk2818_get_bat_health(gBatteryData); + rk2818_get_bat_present(gBatteryData); + rk2818_get_bat_voltage(gBatteryData); + rk2818_get_bat_capacity(gBatteryData); + +} + + +static int rk2818_usb_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + charger_type_t charger; + //todo + charger = CHARGER_USB; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + if (psy->type == POWER_SUPPLY_TYPE_USB) + val->intval = (charger == CHARGER_AC ? 1 : 0); + else + val->intval = 0; + break; + default: + return -EINVAL; + } + + return 0; + +} + + +static int rk2818_ac_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ +// struct rk2818_battery_data *data = container_of(psy, +// struct rk2818_battery_data, ac); + int ret = 0; + charger_type_t charger; + //todo + charger = CHARGER_USB; + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + if (psy->type == POWER_SUPPLY_TYPE_MAINS) + val->intval = (charger == CHARGER_AC ? 1 : 0); + else if (psy->type == POWER_SUPPLY_TYPE_USB) + val->intval = (charger == CHARGER_USB ? 1 : 0); + else + val->intval = 0; + //val->intval = RK2818_BATTERY_READ(data, BATTERY_AC_ONLINE); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int rk2818_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct rk2818_battery_data *data = container_of(psy, + struct rk2818_battery_data, battery); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = gBatStatus; + DBG("gBatStatus=0x%x\n",val->intval); + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = gBatHealth; + DBG("gBatHealth=0x%x\n",val->intval); + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = gBatPresent; + DBG("gBatPresent=0x%x\n",val->intval); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val ->intval = gBatVoltage; + DBG("gBatVoltage=0x%x\n",val->intval); + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = gBatCapacity; + DBG("gBatCapacity=0x%x\n",val->intval); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = data->bat_max; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = data->bat_min; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static enum power_supply_property rk2818_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, +}; + +static enum power_supply_property rk2818_usb_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + + +static enum power_supply_property rk2818_ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; +#if 0 +static irqreturn_t rk2818_battery_interrupt(int irq, void *dev_id) +{ + + unsigned long irq_flags; + struct rk2818_battery_data *data = dev_id; + uint32_t status; + + spin_lock_irqsave(&data->lock, irq_flags); + /* read status flags, which will clear the interrupt */ + //status = RK2818_BATTERY_READ(data, BATTERY_INT_STATUS); + status &= BATTERY_INT_MASK; + + if (status & BATTERY_STATUS_CHANGED) + power_supply_changed(&data->battery); + if (status & AC_STATUS_CHANGED) + power_supply_changed(&data->ac); + + spin_unlock_irqrestore(&data->lock, irq_flags); + return status ? IRQ_HANDLED : IRQ_NONE; + + return IRQ_HANDLED; +} +#endif + +static int rk2818_battery_probe(struct platform_device *pdev) +{ + int ret; + //struct resource *r; + struct rk2818_battery_data *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) { + ret = -ENOMEM; + goto err_data_alloc_failed; + } + spin_lock_init(&data->lock); + + data->battery.properties = rk2818_battery_props; + data->battery.num_properties = ARRAY_SIZE(rk2818_battery_props); + data->battery.get_property = rk2818_battery_get_property; + data->battery.name = "battery"; + data->battery.type = POWER_SUPPLY_TYPE_BATTERY; + //data->adc_bat_divider = gAdcValue[3]; + data->adc_bat_divider = 414; + DBG("adc_bat_divider=%d\n",data->adc_bat_divider); + data->bat_max = 4310000; + data->bat_min = 1551 * 1000000 / 414; + + data->usb.properties = rk2818_usb_props; + data->usb.num_properties = ARRAY_SIZE(rk2818_ac_props); + data->usb.get_property = rk2818_usb_get_property; + data->usb.name = "usb"; + data->usb.type = POWER_SUPPLY_TYPE_USB; + + data->ac.properties = rk2818_ac_props; + data->ac.num_properties = ARRAY_SIZE(rk2818_ac_props); + data->ac.get_property = rk2818_ac_get_property; + data->ac.name = "ac"; + data->ac.type = POWER_SUPPLY_TYPE_MAINS; + +#if 0 + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + printk(KERN_ERR "%s: platform_get_resource failed\n", pdev->name); + ret = -ENODEV; + goto err_no_io_base; + } + + data->irq = platform_get_irq(pdev, 0); + if (data->irq < 0) { + printk(KERN_ERR "%s: platform_get_irq failed\n", pdev->name); + ret = -ENODEV; + goto err_no_irq; + } + + ret = request_irq(data->irq, rk2818_battery_interrupt, IRQF_SHARED, pdev->name, data); + if (ret) + goto err_request_irq_failed; +#endif + ret = power_supply_register(&pdev->dev, &data->ac); + if (ret) + goto err_ac_failed; + + ret = power_supply_register(&pdev->dev, &data->usb); + if (ret) + goto err_usb_failed; + + ret = power_supply_register(&pdev->dev, &data->battery); + if (ret) + goto err_battery_failed; + + platform_set_drvdata(pdev, data); + gBatteryData = data; + + setup_timer(&data->timer, rk2818_batscan_timer, (unsigned long)data); + data->timer.expires = jiffies+50; + add_timer(&data->timer); + printk(KERN_INFO "rk2818_battery: driver initialized\n"); + //RK2818_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK); + + return 0; + +err_battery_failed: + power_supply_unregister(&data->usb); +err_usb_failed: + power_supply_unregister(&data->ac); +err_ac_failed: + //free_irq(data->irq, data); +//err_request_irq_failed: +//err_no_irq: +//err_no_io_base: + kfree(data); +err_data_alloc_failed: + return ret; +} + +static int rk2818_battery_remove(struct platform_device *pdev) +{ + struct rk2818_battery_data *data = platform_get_drvdata(pdev); + + power_supply_unregister(&data->battery); + power_supply_unregister(&data->ac); + + free_irq(data->irq, data); + kfree(data); + gBatteryData = NULL; + return 0; +} + +static struct platform_driver rk2818_battery_device = { + .probe = rk2818_battery_probe, + .remove = rk2818_battery_remove, + .driver = { + .name = "rk2818-battery", + .owner = THIS_MODULE, + } +}; + +static int __init rk2818_battery_init(void) +{ + return platform_driver_register(&rk2818_battery_device); +} + +static void __exit rk2818_battery_exit(void) +{ + platform_driver_unregister(&rk2818_battery_device); +} + +module_init(rk2818_battery_init); +module_exit(rk2818_battery_exit); + +MODULE_AUTHOR("Mike Lockwood lockwood@android.com"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Battery driver for the Goldfish emulator"); + -- 2.34.1