/* * linux/drivers/misc/simpad-battery.c * * Copyright (C) 2005 Holger Hans Peter Freyther * Copyright (C) 2001 Juergen Messerer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. * * 2007 Bernhard Guillon changed code to fit on the new ucb1x00 * driver initialization and added CL4 Class (WP50,T-Sinuspad) support. * * Read the Battery Level through the UCB1x00 chip. * */ #include #include #include "../../mfd/ucb1x00.h" /* * Conversion from AD -> mV * 7.5V = 1023 7.3313mV/Digit * * 400 Units == 9.7V * a = ADC value * 21 = ADC error * 12600 = Divident to get 2*7.3242 * 860 = Divider to get 2*7.3242 * 170 = Voltagedrop over */ #define CALIBRATE_BATTERY(a) ((((a + 3)*12610)/860) + 170) /* * We have two types of batteries a small and a large one * To get the right value we to distinguish between those two * 450 Units == 15 V */ #define CALIBRATE_SUPPLY(a) (((a) * 1500) / 45) #define MIN_SUPPLY 8500 /* Less then 8.5V means no powersupply */ /* * Charging Current * if value is >= 50 then charging is on */ #define CALIBRATE_CHARGING(a) (((a)* 1000)/(152/4))) /* * Module parameters. If you have a small battery you need to give the driver * the right module parameter small_battery=1. As default the driver uses * the big size battery. */ static int small_battery = 0; module_param( small_battery, int, 0 ); MODULE_PARM_DESC (small_battery, "Choose 0 (default) for SL4 and SLC (big size battery) and 1 for CL4,WP50 and T-Sinuspad (small size battery)"); struct simpad_battery_t { struct battery battery; struct ucb1x00* ucb; int min_voltage; int min_current; int min_charge; int max_voltage; int max_current; int max_charge; int min_supply; int charging_led_label; int charging_max_label; int batt_full; int batt_low; int batt_critical; int batt_empty; }; static int simpad_get_min_voltage(struct battery* _battery ) { struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; return battery->min_voltage; } static int simpad_get_min_current(struct battery* _battery) { struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; return battery->min_current; } static int simpad_get_min_charge(struct battery* _battery) { struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; return battery->min_charge; } static int simpad_get_max_voltage(struct battery* _battery) { struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; return battery->max_voltage; } static int simpad_get_max_current(struct battery* _battery) { struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; return battery->max_current; } static int simpad_get_max_charge(struct battery* _battery) { struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; return battery->max_charge; } static int simpad_get_temp(struct battery* _battery) { return 0; } static int simpad_get_voltage(struct battery* _battery) { int val; struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; ucb1x00_adc_enable(battery->ucb); val = ucb1x00_adc_read(battery->ucb, UCB_ADC_INP_AD1, UCB_NOSYNC); ucb1x00_adc_disable(battery->ucb); return CALIBRATE_BATTERY(val); } static int simpad_get_current(struct battery* _battery) { int val; struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; ucb1x00_adc_enable(battery->ucb); val = ucb1x00_adc_read(battery->ucb, UCB_ADC_INP_AD3, UCB_NOSYNC); ucb1x00_adc_disable(battery->ucb); return val; } static int simpad_get_charge(struct battery* _battery) { int val; struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; ucb1x00_adc_enable(battery->ucb); val = ucb1x00_adc_read(battery->ucb, UCB_ADC_INP_AD2, UCB_NOSYNC); ucb1x00_adc_disable(battery->ucb); return CALIBRATE_SUPPLY(val); } static int simpad_get_status(struct battery* _battery) { struct simpad_battery_t* battery = (struct simpad_battery_t*)(_battery); int vcharger = simpad_get_charge(_battery); int icharger = simpad_get_current(_battery); int status = BATTERY_STATUS_UNKNOWN; if(icharger > battery->charging_led_label) status = BATTERY_STATUS_CHARGING; else if(vcharger > battery->min_supply) status = BATTERY_STATUS_NOT_CHARGING; else status = BATTERY_STATUS_DISCHARGING; return status; } /* * For big size batterys (CL4, WP50, T-Sinuspad) */ static struct simpad_battery_t simpad_battery_big = { .battery = { .name = "simpad-battery", .get_min_voltage = simpad_get_min_voltage, .get_min_current = simpad_get_min_current, .get_min_charge = simpad_get_min_charge, .get_max_voltage = simpad_get_max_voltage, .get_max_current = simpad_get_max_current, .get_max_charge = simpad_get_max_charge, .get_temp = simpad_get_temp, .get_voltage = simpad_get_voltage, .get_current = simpad_get_current, .get_charge = simpad_get_charge, .get_status = simpad_get_status, }, .min_voltage = 0, .min_current = 0, .min_charge = 0, .max_voltage = 0, .max_current = 0, .max_charge = 0, .min_supply = 12000, .charging_led_label = 28, .charging_max_label = 265, .batt_full = 8300, .batt_low = 7400, .batt_critical = 6800, .batt_empty = 6500, }; /* * For small size batterys (CL4, WP50, T-Sinuspad) */ static struct simpad_battery_t simpad_battery_small = { .battery = { .name = "simpad-battery", .get_min_voltage = simpad_get_min_voltage, .get_min_current = simpad_get_min_current, .get_min_charge = simpad_get_min_charge, .get_max_voltage = simpad_get_max_voltage, .get_max_current = simpad_get_max_current, .get_max_charge = simpad_get_max_charge, .get_temp = simpad_get_temp, .get_voltage = simpad_get_voltage, .get_current = simpad_get_current, .get_charge = simpad_get_charge, .get_status = simpad_get_status, }, .min_voltage = 0, .min_current = 0, .min_charge = 0, .max_voltage = 0, .max_current = 0, .max_charge = 0, .min_supply = 8500, .charging_led_label = 12, .charging_max_label = 120, .batt_full = 8100, .batt_low = 7300, .batt_critical = 6700, .batt_empty = 6400, }; /* * UCB glue code */ static int ucb1x00_battery_add(struct ucb1x00_dev *dev) { if (small_battery==1) { simpad_battery_small.ucb = dev->ucb; battery_class_register(&simpad_battery_small.battery); printk("USING SMALL BAT\n"); } else if (small_battery==0) { simpad_battery_big.ucb = dev->ucb; battery_class_register(&simpad_battery_big.battery); printk("USING BIG BAT\n"); } return 0; } static void ucb1x00_battery_remove(struct ucb1x00_dev *dev) { if (small_battery==1) return battery_class_unregister(&simpad_battery_small.battery); else if (small_battery==0) return battery_class_unregister(&simpad_battery_big.battery); } static struct ucb1x00_driver ucb1x00_battery_driver = { .add = ucb1x00_battery_add, .remove = ucb1x00_battery_remove, }; static int __init ucb1x00_battery_init(void) { return ucb1x00_register_driver(&ucb1x00_battery_driver); } static void __exit ucb1x00_battery_exit(void) { ucb1x00_unregister_driver(&ucb1x00_battery_driver); } module_init(ucb1x00_battery_init); module_exit(ucb1x00_battery_exit); MODULE_AUTHOR("Holger Hans Peter Freyther"); MODULE_LICENSE("GPL");