rendered paste body/* * Battery driver for iPAQ blueangel * * Copyright 2007 Anton Vorontsov <cbou@mail.ru> * Copyright 2005 SDG Systems, LLC * Copyright 2005 Aric D. Blumer * Copyright 2005 Phil Blundell * * 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. * * History: * 2007/02/14 Anton Vorontsov Use ds2760_battery driver, cleanup */#include <linux/module.h>#include <linux/platform_device.h>#include <linux/timer.h>#include <linux/pm.h>#include <linux/irq.h>#include <linux/interrupt.h>#include <linux/leds.h>#include <linux/ds2760_battery.h>#include <linux/adc_battery.h>#include <asm/mach-types.h>#include <asm/hardware.h>#include <linux/soc/asic3_base.h>#include <asm/hardware/asic3_leds.h>#include <asm/arch/htcblueangel-gpio.h>#include <asm/arch/htcblueangel-asic.h>#define DRIVER_NAME "blueangel_power"#define POWER_NONE 0#define POWER_AC 1#define POWER_USB 2extern struct platform_device blueangel_asic3;static struct ds2760_device_info ds2760_di;static int supply_status;static unsigned int ac_irq, usb_irq;struct timer_list isr_timer;DEFINE_LED_TRIGGER(charging_trig);DEFINE_LED_TRIGGER(chargefull_trig);static void blueangel_update_leds(const struct ds2760_device_info *di){ if (supply_status) { if (di->charge_status == BATTERY_STATUS_FULL) { led_trigger_event(chargefull_trig, LED_FULL); led_trigger_event(charging_trig, LED_OFF); return; } else if (di->charge_status == BATTERY_STATUS_CHARGING) { led_trigger_event(chargefull_trig, LED_OFF); led_trigger_event(charging_trig, LED_FULL); return; } } led_trigger_event(chargefull_trig, LED_OFF); led_trigger_event(charging_trig, LED_OFF); return;}static void blueangel_supply_changed(void){/* if (supply_status & POWER_AC) { // If we're on AC, it doesn't matter if we're on USB or not, // use AC only SET_blueangel_GPIO_N(CHARGE_EN, 1); SET_blueangel_GPIO(USB_CHARGE_RATE, 0); } else if (supply_status & POWER_USB) { // We're not on AC, but we are on USB, so charge with that SET_blueangel_GPIO(USB_CHARGE_RATE, 1); SET_blueangel_GPIO_N(CHARGE_EN, 1); } else { // We're not on AC or USB, don't charge SET_blueangel_GPIO_N(CHARGE_EN, 0); SET_blueangel_GPIO(USB_CHARGE_RATE, 0); }*/ return;}static int blueangel_update_supply_status(void){ unsigned int val; int ac_in, usb_in; val = asic3_get_gpio_status_d( &blueangel_asic3.dev ); ac_in = (val & (1<<GPIOD_AC_CHARGER_N)) == 0; usb_in = (val & (1<<GPIOC_USB_PUEN)) == 0; supply_status = POWER_NONE; if (ac_in) supply_status |= POWER_AC; if (usb_in) supply_status |= POWER_USB; return supply_status;}static void blueangel_isr_timer_func(unsigned long enableirq){ supply_status = blueangel_update_supply_status(); printk(KERN_INFO "blueangel power_change: ac_in=%d, usb_in=%d\n", supply_status & POWER_AC, supply_status & POWER_USB ); blueangel_supply_changed(); blueangel_update_leds(&ds2760_di); if (ds2760_di.charge_power_changed) ds2760_di.charge_power_changed(&ds2760_di); if (supply_status & POWER_USB) set_irq_type(usb_irq, IRQF_TRIGGER_RISING); else set_irq_type(usb_irq, IRQF_TRIGGER_FALLING); if (supply_status & POWER_AC) set_irq_type(ac_irq, IRQF_TRIGGER_RISING); else set_irq_type(ac_irq, IRQF_TRIGGER_FALLING); if (enableirq) { enable_irq(usb_irq); enable_irq(ac_irq); } return;}static irqreturn_t blueangel_attach_isr(int irq, void *dev_id){ if(irq != usb_irq && irq != ac_irq) { printk(DRIVER_NAME " bad irq: %d, not %d or %d\n", irq, usb_irq, ac_irq); } disable_irq(usb_irq); disable_irq(ac_irq); mod_timer(&isr_timer, jiffies + HZ/10); return IRQ_HANDLED;}static void blueangel_charge_callback(const struct ds2760_device_info *di){ if (di->charge_status == BATTERY_STATUS_UNKNOWN) { /* ds2760_battery is about to suspend or resume. On suspend * we should turn off leds, and on resume we should update * supply status. */ int old_status = supply_status; blueangel_update_supply_status(); if (old_status != supply_status) blueangel_isr_timer_func(0); } blueangel_update_leds(di); return;}void blueangel_null_release(struct device *dev){ return; /* current platform_device api is weird */}static struct ds2760_device_info ds2760_di = { .battery_info = { .name = "blueangel_primary", .id = "main", .main_battery = 1, .max_voltage = 3700, .min_voltage = 0, .max_current = 1490, .min_current = -1490, .min_charge = 0, }, .is_charge_power_supplied = blueangel_update_supply_status, .charge_callback = blueangel_charge_callback,};static struct platform_device ds2760_pdev = { .name = "ds2760-battery", .dev = { .platform_data = &ds2760_di, .release = blueangel_null_release, },};static struct battery_adc_platform_data blueangel_backup_batt_params = { .battery_info = { .name = "blueangel_backup", .id = "backup", .max_voltage = 1400, .min_voltage = 1000, .max_current = 500, .min_current = 0, .min_charge = 0, .max_charge = 100, }, .voltage_pin = "ads7846-ssp:vaux",};static struct platform_device blueangel_backup_batt = { .name = "adc-battery", .id = -1, .dev = { .platform_data = &blueangel_backup_batt_params, }};static int blueangel_battery_init (void){ int ret; setup_timer(&isr_timer, blueangel_isr_timer_func, 1); ac_irq = asic3_irq_base(&blueangel_asic3.dev) + ASIC3_GPIOD_IRQ_BASE + GPIOD_AC_CHARGER_N; ret = request_irq (ac_irq, blueangel_attach_isr, IRQF_DISABLED, "blueangel AC Detect", NULL); if (ret) goto ac_irq_failed; usb_irq = asic3_irq_base(&blueangel_asic3.dev) + ASIC3_GPIOC_IRQ_BASE + GPIOC_USB_PUEN; ret = request_irq (usb_irq, blueangel_attach_isr, IRQF_DISABLED, "blueangel USB Detect", NULL); if (ret) goto usb_irq_failed; ret = platform_device_register(&ds2760_pdev); if (ret) goto platform_device_failed; ret = platform_device_register(&blueangel_backup_batt); /* Not being able to register backup battery is not fatal */ if (ret) printk(KERN_ERR DRIVER_NAME ": Could not register backup battery: err=%d\n", ret); led_trigger_register_simple("blueangel-chargefull", &chargefull_trig); led_trigger_register_hwtimer("blueangel-charging", &charging_trig); blueangel_isr_timer_func(0); goto success;platform_device_failed: printk (KERN_ERR "%s: failed to register device\n", __FUNCTION__); free_irq(usb_irq, NULL);usb_irq_failed: printk (KERN_ERR "%s: unable to grab USB in IRQ\n", __FUNCTION__); free_irq(ac_irq, NULL);ac_irq_failed: printk (KERN_ERR "%s: unable to grab AC in IRQ\n", __FUNCTION__);success: return ret;}static void blueangel_battery_exit(void){ led_trigger_unregister_simple(chargefull_trig); led_trigger_unregister_hwtimer(charging_trig); free_irq(ac_irq, NULL); free_irq(usb_irq, NULL); del_timer_sync(&isr_timer); platform_device_unregister(&ds2760_pdev); platform_device_unregister(&blueangel_backup_batt); return;}module_init(blueangel_battery_init);module_exit(blueangel_battery_exit);MODULE_LICENSE("GPL");