X-Git-Url: https://git.rohieb.name/openwrt.git/blobdiff_plain/809c03fe4c0f90c640b26e6a2792553b592c68f2..3c05ba24723fa368dd070c212376b7dbaa4269a6:/target/linux/xburst/files-2.6.32/drivers/power/jz4740-battery.c?ds=inline diff --git a/target/linux/xburst/files-2.6.32/drivers/power/jz4740-battery.c b/target/linux/xburst/files-2.6.32/drivers/power/jz4740-battery.c index fe03d8076..70c80ebd7 100644 --- a/target/linux/xburst/files-2.6.32/drivers/power/jz4740-battery.c +++ b/target/linux/xburst/files-2.6.32/drivers/power/jz4740-battery.c @@ -1,11 +1,13 @@ /* * Battery measurement code for Ingenic JZ SOC. * + * Copyright (C) 2009 Jiejing Zhang + * Copyright (C) 2010, Lars-Peter Clausen + * * based on tosa_battery.c * * Copyright (C) 2008 Marek Vasut - * Copyright (C) 2009 Jiejing Zhang - * +* * 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. @@ -19,120 +21,56 @@ #include #include #include +#include #include #include -struct jz_battery_info { - struct power_supply usb; - struct power_supply bat; - struct power_supply ac; - int bat_status; - struct jz_batt_info *pdata; - struct mutex work_lock; - struct workqueue_struct *monitor_wqueue; - struct delayed_work bat_work; -}; - -#define ps_to_jz_battery(x) container_of((x), struct jz_battery_info, bat); +struct jz_battery { + struct jz_battery_platform_data *pdata; -/********************************************************************* - * Power - *********************************************************************/ + int charge_irq; + int status; + long voltage; -static int jz_get_power_prop(struct jz_battery_info *bat_info, - struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - int gpio; - - if (bat_info == 0 || bat_info->pdata == 0) - return -EINVAL; - gpio = (psy->type == POWER_SUPPLY_TYPE_MAINS) ? - bat_info->pdata->dc_dect_gpio : - bat_info->pdata->usb_dect_gpio; - if (!gpio_is_valid(gpio)) - return -EINVAL; - switch (psp) { - case POWER_SUPPLY_PROP_ONLINE: - val->intval = !gpio_get_value(gpio); - break; - default: - return -EINVAL; - } - - return 0; -} + struct power_supply battery; + struct delayed_work work; +}; -static int jz_usb_get_power_prop(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) +static inline struct jz_battery *psy_to_jz_battery(struct power_supply *psy) { - struct jz_battery_info *bat_info = container_of(psy, struct jz_battery_info, usb); - return jz_get_power_prop(bat_info, psy, psp, val); + return container_of(psy, struct jz_battery, battery); } -static int jz_ac_get_power_prop(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) +static long jz_battery_read_voltage(struct jz_battery *jz_battery) { - struct jz_battery_info *bat_info = container_of(psy, struct jz_battery_info, ac); - return jz_get_power_prop(bat_info, psy, psp, val); -} - - -static enum power_supply_property jz_power_props[] = { - POWER_SUPPLY_PROP_ONLINE, -}; - -static struct power_supply jz_ac = { - .name = "ac", - .type = POWER_SUPPLY_TYPE_MAINS, - .properties = jz_power_props, - .num_properties = ARRAY_SIZE(jz_power_props), - .get_property = jz_ac_get_power_prop, -}; - -static struct power_supply jz_usb = { - .name = "usb", - .type = POWER_SUPPLY_TYPE_USB, - .properties = jz_power_props, - .num_properties = ARRAY_SIZE(jz_power_props), - .get_property = jz_usb_get_power_prop, -}; - - -/********************************************************************* - * Battery properties - *********************************************************************/ - -static long jz_read_bat(struct power_supply *psy) -{ - struct jz_battery_info *bat_info = ps_to_jz_battery(psy); + struct device *adc = jz_battery->battery.dev->parent->parent; enum jz_adc_battery_scale scale; - if (bat_info->pdata->max_voltag > 2500000) + if (jz_battery->pdata->info.voltage_max_design > 2500000) scale = JZ_ADC_BATTERY_SCALE_7V5; else scale = JZ_ADC_BATTERY_SCALE_2V5; - return jz4740_adc_read_battery_voltage(psy->dev->parent->parent, scale); + return jz4740_adc_read_battery_voltage(adc, scale); } -static int jz_bat_get_capacity(struct power_supply *psy) +static int jz_battery_get_capacity(struct power_supply *psy) { + struct jz_battery *jz_battery = psy_to_jz_battery(psy); + struct power_supply_info *info = &jz_battery->pdata->info; + long voltage; int ret; - struct jz_battery_info *bat_info = ps_to_jz_battery(psy); + int voltage_span; - ret = jz_read_bat(psy); + voltage = jz_battery_read_voltage(jz_battery); - if (ret < 0) - return ret; + if (voltage < 0) + return voltage; - ret = (ret - bat_info->pdata->min_voltag) * 100 - / (bat_info->pdata->max_voltag - bat_info->pdata->min_voltag); + voltage_span = info->voltage_max_design - info->voltage_min_design; + ret = ((voltage - info->voltage_min_design) * 100) / voltage_span; if (ret > 100) ret = 100; @@ -142,46 +80,41 @@ static int jz_bat_get_capacity(struct power_supply *psy) return ret; } -static int jz_bat_get_property(struct power_supply *psy, +static int jz_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct jz_battery_info *bat_info = ps_to_jz_battery(psy) - + struct jz_battery *jz_battery = psy_to_jz_battery(psy); + struct power_supply_info *info = &jz_battery->pdata->info; + long voltage; + switch (psp) { case POWER_SUPPLY_PROP_STATUS: - val->intval = bat_info->bat_status; + val->intval = jz_battery->status; break; case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = bat_info->pdata->batt_tech; + val->intval = jz_battery->pdata->info.technology; break; case POWER_SUPPLY_PROP_HEALTH: - if(jz_read_bat(psy) < bat_info->pdata->min_voltag) { - dev_dbg(psy->dev, "%s: battery is dead," - "voltage too low!\n", __func__); + voltage = jz_battery_read_voltage(jz_battery); + if (voltage < info->voltage_min_design) val->intval = POWER_SUPPLY_HEALTH_DEAD; - } else { - dev_dbg(psy->dev, "%s: battery is good," - "voltage normal.\n", __func__); + else val->intval = POWER_SUPPLY_HEALTH_GOOD; - } break; case POWER_SUPPLY_PROP_CAPACITY: - val->intval = jz_bat_get_capacity(psy); - dev_dbg(psy->dev, "%s: battery_capacity = %d\n", - __func__, val->intval); + val->intval = jz_battery_get_capacity(psy); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = jz_read_bat(psy); + val->intval = jz_battery_read_voltage(jz_battery); if (val->intval < 0) return val->intval; break; - case POWER_SUPPLY_PROP_VOLTAGE_MAX: case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - val->intval = bat_info->pdata->max_voltag; + val->intval = info->voltage_max_design; break; case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: - val->intval = bat_info->pdata->min_voltag; + val->intval = info->voltage_min_design; break; case POWER_SUPPLY_PROP_PRESENT: val->intval = 1; @@ -192,279 +125,225 @@ static int jz_bat_get_property(struct power_supply *psy, return 0; } -static void jz_bat_external_power_changed(struct power_supply *psy) +static void jz_battery_external_power_changed(struct power_supply *psy) { - struct jz_battery_info *bat_info = ps_to_jz_battery(psy); - - cancel_delayed_work(&bat_info->bat_work); - queue_delayed_work(bat_info->monitor_wqueue, &bat_info->bat_work, HZ / 8); + struct jz_battery *jz_battery = psy_to_jz_battery(psy); + + cancel_delayed_work(&jz_battery->work); + schedule_delayed_work(&jz_battery->work, 0); } -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", -}; +static irqreturn_t jz_battery_charge_irq(int irq, void *data) +{ + struct jz_battery *jz_battery = data; + + cancel_delayed_work(&jz_battery->work); + schedule_delayed_work(&jz_battery->work, 0); + + return IRQ_HANDLED; +} -static void jz_bat_update(struct power_supply *psy) +static void jz_battery_update(struct jz_battery *jz_battery) { - struct jz_battery_info *bat_info = ps_to_jz_battery(psy); - - int old_status = bat_info->bat_status; - static unsigned long old_batt_vol = 0; - unsigned long batt_vol = jz_read_bat(psy); - - mutex_lock(&bat_info->work_lock); - - if (gpio_is_valid(bat_info->pdata->charg_stat_gpio)) { - if(!gpio_get_value(bat_info->pdata->charg_stat_gpio)) - bat_info->bat_status = POWER_SUPPLY_STATUS_CHARGING; + int status; + long voltage; + long voltage_difference; + bool has_changed = 0; + + if (gpio_is_valid(jz_battery->pdata->gpio_charge)) { + int is_charging; + + is_charging = gpio_get_value(jz_battery->pdata->gpio_charge); + is_charging ^= jz_battery->pdata->gpio_charge_active_low; + if (is_charging) + status = POWER_SUPPLY_STATUS_CHARGING; else - bat_info->bat_status = POWER_SUPPLY_STATUS_NOT_CHARGING; - dev_dbg(psy->dev, "%s: battery status=%s\n", - __func__, status_text[bat_info->bat_status]); - - if (old_status != bat_info->bat_status) { - dev_dbg(psy->dev, "%s %s -> %s\n", - psy->name, - status_text[old_status], - status_text[bat_info->bat_status]); - - power_supply_changed(psy); + status = POWER_SUPPLY_STATUS_NOT_CHARGING; + + if (status != jz_battery->status) { + jz_battery->status = status; + has_changed = 1; } } - if (old_batt_vol - batt_vol > 50000) { - dev_dbg(psy->dev, "voltage change : %ld -> %ld\n", - old_batt_vol, batt_vol); - power_supply_changed(psy); - old_batt_vol = batt_vol; + voltage = jz_battery_read_voltage(jz_battery); + voltage_difference = voltage - jz_battery->voltage; + if (voltage_difference > 50000 || voltage_difference < 50000) { + jz_battery->voltage = voltage; + has_changed = 1; } - - mutex_unlock(&bat_info->work_lock); + if (has_changed) + power_supply_changed(&jz_battery->battery); } -static enum power_supply_property jz_bat_main_props[] = { +static enum power_supply_property jz_battery_properties[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_HEALTH, - POWER_SUPPLY_PROP_CAPACITY, /* in percents! */ + POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, POWER_SUPPLY_PROP_PRESENT, }; -struct power_supply bat_ps = { - .name = "battery", - .type = POWER_SUPPLY_TYPE_BATTERY, - .properties = jz_bat_main_props, - .num_properties = ARRAY_SIZE(jz_bat_main_props), - .get_property = jz_bat_get_property, - .external_power_changed = jz_bat_external_power_changed, - .use_for_apm = 1, -}; - -static void jz_bat_work(struct work_struct *work) +static void jz_battery_work(struct work_struct *work) { - /* query interval too small will increase system workload*/ + /* Too small interval will increase system workload */ const int interval = HZ * 30; - struct jz_battery_info *bat_info = container_of(work,struct jz_battery_info, bat_work.work); + struct jz_battery *jz_battery = container_of(work, struct jz_battery, + work.work); - jz_bat_update(&bat_info->bat); - queue_delayed_work(bat_info->monitor_wqueue, - &bat_info->bat_work, interval); + jz_battery_update(jz_battery); + schedule_delayed_work(&jz_battery->work, interval); } -#ifdef CONFIG_PM -static int jz_bat_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct jz_battery_info *bat_info = platform_get_drvdata(pdev); - - bat_info->bat_status = POWER_SUPPLY_STATUS_UNKNOWN; - - return 0; -} - -static int jz_bat_resume(struct platform_device *pdev) -{ - struct jz_battery_info *bat_info = platform_get_drvdata(pdev); - - bat_info->bat_status = POWER_SUPPLY_STATUS_UNKNOWN; - - cancel_delayed_work(&bat_info->bat_work); - queue_delayed_work(bat_info->monitor_wqueue, &bat_info->bat_work, HZ/10); - - return 0; -} -#else -#define jz_bat_suspend NULL -#define jz_bat_resume NULL -#endif - -static int jz_bat_probe(struct platform_device *pdev) +static int jz_battery_probe(struct platform_device *pdev) { int ret = 0; - struct jz_battery_info *bat_info; - - bat_info = kzalloc(sizeof(struct jz_battery_info), GFP_KERNEL); - - if (!bat_info) { - return -ENOMEM; - } + struct jz_battery_platform_data *pdata = pdev->dev.platform_data; + struct jz_battery *jz_battery; + struct power_supply *battery; if (!pdev->dev.platform_data) { - dev_err(&pdev->dev, "Please set battery info\n"); - ret = -EINVAL; - goto err_platform_data; + dev_err(&pdev->dev, "No platform data\n"); + return -EINVAL; } - platform_set_drvdata(pdev, bat_info); - bat_info->pdata = pdev->dev.platform_data; - bat_info->bat = bat_ps; - bat_info->usb = jz_usb; - bat_info->ac = jz_ac; - mutex_init(&bat_info->work_lock); - INIT_DELAYED_WORK(&bat_info->bat_work, jz_bat_work); - - if (gpio_is_valid(bat_info->pdata->dc_dect_gpio)) { - ret = gpio_request(bat_info->pdata->dc_dect_gpio, "AC/DC DECT"); - if (ret) { - dev_err(&pdev->dev, "ac/dc dect gpio request failed.\n"); - goto err_dc_gpio_request; - } - ret = gpio_direction_input(bat_info->pdata->dc_dect_gpio); - if (ret) { - dev_err(&pdev->dev, "ac/dc dect gpio direction failed.\n"); + jz_battery = kzalloc(sizeof(*jz_battery), GFP_KERNEL); - goto err_dc_gpio_direction; - } + if (!jz_battery) { + dev_err(&pdev->dev, "Failed to allocate driver structure\n"); + return -ENOMEM; } - if (gpio_is_valid(bat_info->pdata->usb_dect_gpio)) { - ret = gpio_request(bat_info->pdata->usb_dect_gpio, "USB DECT"); - if (ret) { - dev_err(&pdev->dev, "usb dect gpio request failed.\n"); + battery = &jz_battery->battery; + battery->name = pdata->info.name; + battery->type = POWER_SUPPLY_TYPE_BATTERY; + battery->properties = jz_battery_properties; + battery->num_properties = ARRAY_SIZE(jz_battery_properties); + battery->get_property = jz_battery_get_property; + battery->external_power_changed = jz_battery_external_power_changed; + battery->use_for_apm = 1; - goto err_usb_gpio_request; - } - ret = gpio_direction_input(bat_info->pdata->usb_dect_gpio); - if (ret) { - dev_err(&pdev->dev, "usb dect gpio set direction failed.\n"); - goto err_usb_gpio_direction; - } + jz_battery->pdata = pdata; - jz_gpio_disable_pullup(bat_info->pdata->usb_dect_gpio); - /* TODO: Use generic gpio is better */ - } + INIT_DELAYED_WORK(&jz_battery->work, jz_battery_work); - if (gpio_is_valid(bat_info->pdata->charg_stat_gpio)) { - ret = gpio_request(bat_info->pdata->charg_stat_gpio, "CHARG STAT"); + if (gpio_is_valid(pdata->gpio_charge)) { + ret = gpio_request(pdata->gpio_charge, dev_name(&pdev->dev)); if (ret) { dev_err(&pdev->dev, "charger state gpio request failed.\n"); - goto err_charg_gpio_request; + goto err_free; } - ret = gpio_direction_input(bat_info->pdata->charg_stat_gpio); + ret = gpio_direction_input(pdata->gpio_charge); if (ret) { dev_err(&pdev->dev, "charger state gpio set direction failed.\n"); - goto err_charg_gpio_direction; + goto err_free_gpio; } - } - - if (gpio_is_valid(bat_info->pdata->dc_dect_gpio)) { - ret = power_supply_register(&pdev->dev, &bat_info->ac); - if (ret) { - dev_err(&pdev->dev, "power supply ac/dc register failed.\n"); - goto err_power_register_ac; - } - } - if (gpio_is_valid(bat_info->pdata->usb_dect_gpio)) { - ret = power_supply_register(&pdev->dev, &bat_info->usb); - if (ret) { - dev_err(&pdev->dev, "power supply usb register failed.\n"); - goto err_power_register_usb; - } - } + jz_battery->charge_irq = gpio_to_irq(pdata->gpio_charge); - if (gpio_is_valid(bat_info->pdata->charg_stat_gpio)) { - ret = power_supply_register(&pdev->dev, &bat_info->bat); - if (ret) { - dev_err(&pdev->dev, "power supply battery register failed.\n"); - goto err_power_register_bat; - } else { - bat_info->monitor_wqueue = create_singlethread_workqueue("jz_battery"); - if (!bat_info->monitor_wqueue) { - return -ESRCH; + if (jz_battery->charge_irq >= 0) { + ret = request_irq(jz_battery->charge_irq, + jz_battery_charge_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + dev_name(&pdev->dev), jz_battery); + if (ret) { + dev_err(&pdev->dev, "Failed to request charge irq: %d\n", ret); + goto err_free_gpio; } - queue_delayed_work(bat_info->monitor_wqueue, &bat_info->bat_work, HZ * 1); } + } else { + jz_battery->charge_irq = -1; } - printk(KERN_INFO "jz_bat init success.\n"); - return ret; -err_power_register_bat: - power_supply_unregister(&bat_info->usb); -err_power_register_usb: - power_supply_unregister(&bat_info->ac); -err_power_register_ac: -err_charg_gpio_direction: - gpio_free(bat_info->pdata->charg_stat_gpio); -err_charg_gpio_request: -err_usb_gpio_direction: - gpio_free(bat_info->pdata->usb_dect_gpio); -err_usb_gpio_request: -err_dc_gpio_direction: - gpio_free(bat_info->pdata->dc_dect_gpio); -err_dc_gpio_request: -err_platform_data: - kfree(bat_info); + + ret = power_supply_register(&pdev->dev, &jz_battery->battery); + if (ret) { + dev_err(&pdev->dev, "power supply battery register failed.\n"); + goto err_free_irq; + } + + platform_set_drvdata(pdev, jz_battery); + schedule_delayed_work(&jz_battery->work, 0); + + return 0; + +err_free_irq: + if (jz_battery->charge_irq >= 0) + free_irq(jz_battery->charge_irq, jz_battery); +err_free_gpio: + if (gpio_is_valid(pdata->gpio_charge)) + gpio_free(jz_battery->pdata->gpio_charge); +err_free: + kfree(jz_battery); return ret; } -static int jz_bat_remove(struct platform_device *pdev) +static int jz_battery_remove(struct platform_device *pdev) { - struct jz_battery_info *bat_info = platform_get_drvdata(pdev); - - if (bat_info->pdata) { - if (gpio_is_valid(bat_info->pdata->dc_dect_gpio)) - gpio_free(bat_info->pdata->dc_dect_gpio); - if (gpio_is_valid(bat_info->pdata->usb_dect_gpio)) - gpio_free(bat_info->pdata->usb_dect_gpio); - if (gpio_is_valid(bat_info->pdata->charg_stat_gpio)) - gpio_free(bat_info->pdata->charg_stat_gpio); + struct jz_battery *jz_battery = platform_get_drvdata(pdev); + + cancel_delayed_work_sync(&jz_battery->work); + + if (gpio_is_valid(jz_battery->pdata->gpio_charge)) { + if (jz_battery->charge_irq >= 0) + free_irq(jz_battery->charge_irq, jz_battery); + gpio_free(jz_battery->pdata->gpio_charge); } - power_supply_unregister(&bat_ps); - power_supply_unregister(&jz_ac); - power_supply_unregister(&jz_usb); + power_supply_unregister(&jz_battery->battery); + + return 0; +} + +#ifdef CONFIG_PM +static int jz_battery_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct jz_battery *jz_battery = platform_get_drvdata(pdev); + + cancel_delayed_work_sync(&jz_battery->work); + jz_battery->status = POWER_SUPPLY_STATUS_UNKNOWN; return 0; } -static struct platform_driver jz_bat_driver = { - .probe = jz_bat_probe, - .remove = __devexit_p(jz_bat_remove), - .suspend = jz_bat_suspend, - .resume = jz_bat_resume, +static int jz_battery_resume(struct platform_device *pdev) +{ + struct jz_battery *jz_battery = platform_get_drvdata(pdev); + + schedule_delayed_work(&jz_battery->work, 0); + + return 0; +} +#else +#define jz_battery_suspend NULL +#define jz_battery_resume NULL +#endif + +static struct platform_driver jz_battery_driver = { + .probe = jz_battery_probe, + .remove = __devexit_p(jz_battery_remove), + .suspend = jz_battery_suspend, + .resume = jz_battery_resume, .driver = { .name = "jz4740-battery", .owner = THIS_MODULE, }, }; -static int __init jz_bat_init(void) +static int __init jz_battery_init(void) { - return platform_driver_register(&jz_bat_driver); + return platform_driver_register(&jz_battery_driver); } -module_init(jz_bat_init); +module_init(jz_battery_init); -static void __exit jz_bat_exit(void) +static void __exit jz_battery_exit(void) { - platform_driver_unregister(&jz_bat_driver); + platform_driver_unregister(&jz_battery_driver); } -module_exit(jz_bat_exit); +module_exit(jz_battery_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jiejing Zhang ");