| Mike Rapoport | 342d765 | 2008-12-30 22:44:53 +0100 | [diff] [blame] | 1 | /* | 
|  | 2 | * Battery charger driver for Dialog Semiconductor DA9030 | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2008 Compulab, Ltd. | 
|  | 5 | * 	Mike Rapoport <mike@compulab.co.il> | 
|  | 6 | * | 
|  | 7 | * This program is free software; you can redistribute it and/or modify | 
|  | 8 | * it under the terms of the GNU General Public License version 2 as | 
|  | 9 | * published by the Free Software Foundation. | 
|  | 10 | */ | 
|  | 11 |  | 
|  | 12 | #include <linux/kernel.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 13 | #include <linux/slab.h> | 
| Mike Rapoport | 342d765 | 2008-12-30 22:44:53 +0100 | [diff] [blame] | 14 | #include <linux/init.h> | 
|  | 15 | #include <linux/types.h> | 
|  | 16 | #include <linux/device.h> | 
|  | 17 | #include <linux/workqueue.h> | 
|  | 18 | #include <linux/module.h> | 
|  | 19 | #include <linux/platform_device.h> | 
|  | 20 | #include <linux/power_supply.h> | 
|  | 21 | #include <linux/mfd/da903x.h> | 
|  | 22 |  | 
|  | 23 | #include <linux/debugfs.h> | 
|  | 24 | #include <linux/seq_file.h> | 
|  | 25 |  | 
| Mike Rapoport | 342d765 | 2008-12-30 22:44:53 +0100 | [diff] [blame] | 26 | #define DA9030_FAULT_LOG		0x0a | 
|  | 27 | #define DA9030_FAULT_LOG_OVER_TEMP	(1 << 7) | 
|  | 28 | #define DA9030_FAULT_LOG_VBAT_OVER	(1 << 4) | 
|  | 29 |  | 
|  | 30 | #define DA9030_CHARGE_CONTROL		0x28 | 
|  | 31 | #define DA9030_CHRG_CHARGER_ENABLE	(1 << 7) | 
|  | 32 |  | 
|  | 33 | #define DA9030_ADC_MAN_CONTROL		0x30 | 
|  | 34 | #define DA9030_ADC_TBATREF_ENABLE	(1 << 5) | 
|  | 35 | #define DA9030_ADC_LDO_INT_ENABLE	(1 << 4) | 
|  | 36 |  | 
|  | 37 | #define DA9030_ADC_AUTO_CONTROL		0x31 | 
|  | 38 | #define DA9030_ADC_TBAT_ENABLE		(1 << 5) | 
|  | 39 | #define DA9030_ADC_VBAT_IN_TXON		(1 << 4) | 
|  | 40 | #define DA9030_ADC_VCH_ENABLE		(1 << 3) | 
|  | 41 | #define DA9030_ADC_ICH_ENABLE		(1 << 2) | 
|  | 42 | #define DA9030_ADC_VBAT_ENABLE		(1 << 1) | 
|  | 43 | #define DA9030_ADC_AUTO_SLEEP_ENABLE	(1 << 0) | 
|  | 44 |  | 
|  | 45 | #define DA9030_VBATMON		0x32 | 
|  | 46 | #define DA9030_VBATMONTXON	0x33 | 
|  | 47 | #define DA9030_TBATHIGHP	0x34 | 
|  | 48 | #define DA9030_TBATHIGHN	0x35 | 
|  | 49 | #define DA9030_TBATLOW		0x36 | 
|  | 50 |  | 
|  | 51 | #define DA9030_VBAT_RES		0x41 | 
|  | 52 | #define DA9030_VBATMIN_RES	0x42 | 
|  | 53 | #define DA9030_VBATMINTXON_RES	0x43 | 
|  | 54 | #define DA9030_ICHMAX_RES	0x44 | 
|  | 55 | #define DA9030_ICHMIN_RES	0x45 | 
|  | 56 | #define DA9030_ICHAVERAGE_RES	0x46 | 
|  | 57 | #define DA9030_VCHMAX_RES	0x47 | 
|  | 58 | #define DA9030_VCHMIN_RES	0x48 | 
|  | 59 | #define DA9030_TBAT_RES		0x49 | 
|  | 60 |  | 
|  | 61 | struct da9030_adc_res { | 
|  | 62 | uint8_t vbat_res; | 
|  | 63 | uint8_t vbatmin_res; | 
|  | 64 | uint8_t vbatmintxon; | 
|  | 65 | uint8_t ichmax_res; | 
|  | 66 | uint8_t ichmin_res; | 
|  | 67 | uint8_t ichaverage_res; | 
|  | 68 | uint8_t vchmax_res; | 
|  | 69 | uint8_t vchmin_res; | 
|  | 70 | uint8_t tbat_res; | 
|  | 71 | uint8_t adc_in4_res; | 
|  | 72 | uint8_t adc_in5_res; | 
|  | 73 | }; | 
|  | 74 |  | 
|  | 75 | struct da9030_battery_thresholds { | 
|  | 76 | int tbat_low; | 
|  | 77 | int tbat_high; | 
|  | 78 | int tbat_restart; | 
|  | 79 |  | 
|  | 80 | int vbat_low; | 
|  | 81 | int vbat_crit; | 
|  | 82 | int vbat_charge_start; | 
|  | 83 | int vbat_charge_stop; | 
|  | 84 | int vbat_charge_restart; | 
|  | 85 |  | 
|  | 86 | int vcharge_min; | 
|  | 87 | int vcharge_max; | 
|  | 88 | }; | 
|  | 89 |  | 
|  | 90 | struct da9030_charger { | 
|  | 91 | struct power_supply psy; | 
|  | 92 |  | 
|  | 93 | struct device *master; | 
|  | 94 |  | 
|  | 95 | struct da9030_adc_res adc; | 
|  | 96 | struct delayed_work work; | 
|  | 97 | unsigned int interval; | 
|  | 98 |  | 
|  | 99 | struct power_supply_info *battery_info; | 
|  | 100 |  | 
|  | 101 | struct da9030_battery_thresholds thresholds; | 
|  | 102 |  | 
|  | 103 | unsigned int charge_milliamp; | 
|  | 104 | unsigned int charge_millivolt; | 
|  | 105 |  | 
|  | 106 | /* charger status */ | 
|  | 107 | bool chdet; | 
|  | 108 | uint8_t fault; | 
|  | 109 | int mA; | 
|  | 110 | int mV; | 
|  | 111 | bool is_on; | 
|  | 112 |  | 
|  | 113 | struct notifier_block nb; | 
|  | 114 |  | 
|  | 115 | /* platform callbacks for battery low and critical events */ | 
|  | 116 | void (*battery_low)(void); | 
|  | 117 | void (*battery_critical)(void); | 
|  | 118 |  | 
|  | 119 | struct dentry *debug_file; | 
|  | 120 | }; | 
|  | 121 |  | 
|  | 122 | static inline int da9030_reg_to_mV(int reg) | 
|  | 123 | { | 
|  | 124 | return ((reg * 2650) >> 8) + 2650; | 
|  | 125 | } | 
|  | 126 |  | 
|  | 127 | static inline int da9030_millivolt_to_reg(int mV) | 
|  | 128 | { | 
|  | 129 | return ((mV - 2650) << 8) / 2650; | 
|  | 130 | } | 
|  | 131 |  | 
|  | 132 | static inline int da9030_reg_to_mA(int reg) | 
|  | 133 | { | 
|  | 134 | return ((reg * 24000) >> 8) / 15; | 
|  | 135 | } | 
|  | 136 |  | 
|  | 137 | #ifdef CONFIG_DEBUG_FS | 
|  | 138 | static int bat_debug_show(struct seq_file *s, void *data) | 
|  | 139 | { | 
|  | 140 | struct da9030_charger *charger = s->private; | 
|  | 141 |  | 
|  | 142 | seq_printf(s, "charger is %s\n", charger->is_on ? "on" : "off"); | 
|  | 143 | if (charger->chdet) { | 
|  | 144 | seq_printf(s, "iset = %dmA, vset = %dmV\n", | 
|  | 145 | charger->mA, charger->mV); | 
|  | 146 | } | 
|  | 147 |  | 
|  | 148 | seq_printf(s, "vbat_res = %d (%dmV)\n", | 
|  | 149 | charger->adc.vbat_res, | 
|  | 150 | da9030_reg_to_mV(charger->adc.vbat_res)); | 
|  | 151 | seq_printf(s, "vbatmin_res = %d (%dmV)\n", | 
|  | 152 | charger->adc.vbatmin_res, | 
|  | 153 | da9030_reg_to_mV(charger->adc.vbatmin_res)); | 
|  | 154 | seq_printf(s, "vbatmintxon = %d (%dmV)\n", | 
|  | 155 | charger->adc.vbatmintxon, | 
|  | 156 | da9030_reg_to_mV(charger->adc.vbatmintxon)); | 
|  | 157 | seq_printf(s, "ichmax_res = %d (%dmA)\n", | 
|  | 158 | charger->adc.ichmax_res, | 
|  | 159 | da9030_reg_to_mV(charger->adc.ichmax_res)); | 
|  | 160 | seq_printf(s, "ichmin_res = %d (%dmA)\n", | 
|  | 161 | charger->adc.ichmin_res, | 
|  | 162 | da9030_reg_to_mA(charger->adc.ichmin_res)); | 
|  | 163 | seq_printf(s, "ichaverage_res = %d (%dmA)\n", | 
|  | 164 | charger->adc.ichaverage_res, | 
|  | 165 | da9030_reg_to_mA(charger->adc.ichaverage_res)); | 
|  | 166 | seq_printf(s, "vchmax_res = %d (%dmV)\n", | 
|  | 167 | charger->adc.vchmax_res, | 
|  | 168 | da9030_reg_to_mA(charger->adc.vchmax_res)); | 
|  | 169 | seq_printf(s, "vchmin_res = %d (%dmV)\n", | 
|  | 170 | charger->adc.vchmin_res, | 
|  | 171 | da9030_reg_to_mV(charger->adc.vchmin_res)); | 
|  | 172 |  | 
|  | 173 | return 0; | 
|  | 174 | } | 
|  | 175 |  | 
|  | 176 | static int debug_open(struct inode *inode, struct file *file) | 
|  | 177 | { | 
|  | 178 | return single_open(file, bat_debug_show, inode->i_private); | 
|  | 179 | } | 
|  | 180 |  | 
|  | 181 | static const struct file_operations bat_debug_fops = { | 
|  | 182 | .open		= debug_open, | 
|  | 183 | .read		= seq_read, | 
|  | 184 | .llseek		= seq_lseek, | 
|  | 185 | .release	= single_release, | 
|  | 186 | }; | 
|  | 187 |  | 
|  | 188 | static struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger) | 
|  | 189 | { | 
|  | 190 | charger->debug_file = debugfs_create_file("charger", 0666, 0, charger, | 
|  | 191 | &bat_debug_fops); | 
|  | 192 | return charger->debug_file; | 
|  | 193 | } | 
|  | 194 |  | 
|  | 195 | static void da9030_bat_remove_debugfs(struct da9030_charger *charger) | 
|  | 196 | { | 
|  | 197 | debugfs_remove(charger->debug_file); | 
|  | 198 | } | 
|  | 199 | #else | 
|  | 200 | static inline struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger) | 
|  | 201 | { | 
|  | 202 | return NULL; | 
|  | 203 | } | 
|  | 204 | static inline void da9030_bat_remove_debugfs(struct da9030_charger *charger) | 
|  | 205 | { | 
|  | 206 | } | 
|  | 207 | #endif | 
|  | 208 |  | 
|  | 209 | static inline void da9030_read_adc(struct da9030_charger *charger, | 
|  | 210 | struct da9030_adc_res *adc) | 
|  | 211 | { | 
|  | 212 | da903x_reads(charger->master, DA9030_VBAT_RES, | 
|  | 213 | sizeof(*adc), (uint8_t *)adc); | 
|  | 214 | } | 
|  | 215 |  | 
|  | 216 | static void da9030_charger_update_state(struct da9030_charger *charger) | 
|  | 217 | { | 
|  | 218 | uint8_t val; | 
|  | 219 |  | 
|  | 220 | da903x_read(charger->master, DA9030_CHARGE_CONTROL, &val); | 
|  | 221 | charger->is_on = (val & DA9030_CHRG_CHARGER_ENABLE) ? 1 : 0; | 
|  | 222 | charger->mA = ((val >> 3) & 0xf) * 100; | 
|  | 223 | charger->mV = (val & 0x7) * 50 + 4000; | 
|  | 224 |  | 
|  | 225 | da9030_read_adc(charger, &charger->adc); | 
|  | 226 | da903x_read(charger->master, DA9030_FAULT_LOG, &charger->fault); | 
|  | 227 | charger->chdet = da903x_query_status(charger->master, | 
|  | 228 | DA9030_STATUS_CHDET); | 
|  | 229 | } | 
|  | 230 |  | 
|  | 231 | static void da9030_set_charge(struct da9030_charger *charger, int on) | 
|  | 232 | { | 
|  | 233 | uint8_t val; | 
|  | 234 |  | 
|  | 235 | if (on) { | 
|  | 236 | val = DA9030_CHRG_CHARGER_ENABLE; | 
|  | 237 | val |= (charger->charge_milliamp / 100) << 3; | 
|  | 238 | val |= (charger->charge_millivolt - 4000) / 50; | 
|  | 239 | charger->is_on = 1; | 
|  | 240 | } else { | 
|  | 241 | val = 0; | 
|  | 242 | charger->is_on = 0; | 
|  | 243 | } | 
|  | 244 |  | 
|  | 245 | da903x_write(charger->master, DA9030_CHARGE_CONTROL, val); | 
| Mike Rapoport | a35d01a | 2009-06-09 01:09:45 +0400 | [diff] [blame] | 246 |  | 
|  | 247 | power_supply_changed(&charger->psy); | 
| Mike Rapoport | 342d765 | 2008-12-30 22:44:53 +0100 | [diff] [blame] | 248 | } | 
|  | 249 |  | 
|  | 250 | static void da9030_charger_check_state(struct da9030_charger *charger) | 
|  | 251 | { | 
|  | 252 | da9030_charger_update_state(charger); | 
|  | 253 |  | 
|  | 254 | /* we wake or boot with external power on */ | 
|  | 255 | if (!charger->is_on) { | 
|  | 256 | if ((charger->chdet) && | 
|  | 257 | (charger->adc.vbat_res < | 
|  | 258 | charger->thresholds.vbat_charge_start)) { | 
|  | 259 | da9030_set_charge(charger, 1); | 
|  | 260 | } | 
|  | 261 | } else { | 
| Mike Rapoport | a35d01a | 2009-06-09 01:09:45 +0400 | [diff] [blame] | 262 | /* Charger has been pulled out */ | 
|  | 263 | if (!charger->chdet) { | 
|  | 264 | da9030_set_charge(charger, 0); | 
|  | 265 | return; | 
|  | 266 | } | 
|  | 267 |  | 
| Mike Rapoport | 342d765 | 2008-12-30 22:44:53 +0100 | [diff] [blame] | 268 | if (charger->adc.vbat_res >= | 
|  | 269 | charger->thresholds.vbat_charge_stop) { | 
|  | 270 | da9030_set_charge(charger, 0); | 
|  | 271 | da903x_write(charger->master, DA9030_VBATMON, | 
|  | 272 | charger->thresholds.vbat_charge_restart); | 
|  | 273 | } else if (charger->adc.vbat_res > | 
|  | 274 | charger->thresholds.vbat_low) { | 
|  | 275 | /* we are charging and passed LOW_THRESH, | 
|  | 276 | so upate DA9030 VBAT threshold | 
|  | 277 | */ | 
|  | 278 | da903x_write(charger->master, DA9030_VBATMON, | 
|  | 279 | charger->thresholds.vbat_low); | 
|  | 280 | } | 
|  | 281 | if (charger->adc.vchmax_res > charger->thresholds.vcharge_max || | 
|  | 282 | charger->adc.vchmin_res < charger->thresholds.vcharge_min || | 
|  | 283 | /* Tempreture readings are negative */ | 
|  | 284 | charger->adc.tbat_res < charger->thresholds.tbat_high || | 
|  | 285 | charger->adc.tbat_res > charger->thresholds.tbat_low) { | 
|  | 286 | /* disable charger */ | 
|  | 287 | da9030_set_charge(charger, 0); | 
|  | 288 | } | 
|  | 289 | } | 
|  | 290 | } | 
|  | 291 |  | 
|  | 292 | static void da9030_charging_monitor(struct work_struct *work) | 
|  | 293 | { | 
|  | 294 | struct da9030_charger *charger; | 
|  | 295 |  | 
|  | 296 | charger = container_of(work, struct da9030_charger, work.work); | 
|  | 297 |  | 
|  | 298 | da9030_charger_check_state(charger); | 
|  | 299 |  | 
|  | 300 | /* reschedule for the next time */ | 
|  | 301 | schedule_delayed_work(&charger->work, charger->interval); | 
|  | 302 | } | 
|  | 303 |  | 
|  | 304 | static enum power_supply_property da9030_battery_props[] = { | 
|  | 305 | POWER_SUPPLY_PROP_MODEL_NAME, | 
|  | 306 | POWER_SUPPLY_PROP_STATUS, | 
|  | 307 | POWER_SUPPLY_PROP_HEALTH, | 
|  | 308 | POWER_SUPPLY_PROP_TECHNOLOGY, | 
|  | 309 | POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, | 
|  | 310 | POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, | 
|  | 311 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | 
|  | 312 | POWER_SUPPLY_PROP_CURRENT_AVG, | 
|  | 313 | }; | 
|  | 314 |  | 
|  | 315 | static void da9030_battery_check_status(struct da9030_charger *charger, | 
|  | 316 | union power_supply_propval *val) | 
|  | 317 | { | 
|  | 318 | if (charger->chdet) { | 
|  | 319 | if (charger->is_on) | 
|  | 320 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | 
|  | 321 | else | 
|  | 322 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; | 
|  | 323 | } else { | 
|  | 324 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | 
|  | 325 | } | 
|  | 326 | } | 
|  | 327 |  | 
|  | 328 | static void da9030_battery_check_health(struct da9030_charger *charger, | 
|  | 329 | union power_supply_propval *val) | 
|  | 330 | { | 
|  | 331 | if (charger->fault & DA9030_FAULT_LOG_OVER_TEMP) | 
|  | 332 | val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; | 
|  | 333 | else if (charger->fault & DA9030_FAULT_LOG_VBAT_OVER) | 
|  | 334 | val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; | 
|  | 335 | else | 
|  | 336 | val->intval = POWER_SUPPLY_HEALTH_GOOD; | 
|  | 337 | } | 
|  | 338 |  | 
|  | 339 | static int da9030_battery_get_property(struct power_supply *psy, | 
|  | 340 | enum power_supply_property psp, | 
|  | 341 | union power_supply_propval *val) | 
|  | 342 | { | 
|  | 343 | struct da9030_charger *charger; | 
|  | 344 | charger = container_of(psy, struct da9030_charger, psy); | 
|  | 345 |  | 
|  | 346 | switch (psp) { | 
|  | 347 | case POWER_SUPPLY_PROP_STATUS: | 
|  | 348 | da9030_battery_check_status(charger, val); | 
|  | 349 | break; | 
|  | 350 | case POWER_SUPPLY_PROP_HEALTH: | 
|  | 351 | da9030_battery_check_health(charger, val); | 
|  | 352 | break; | 
|  | 353 | case POWER_SUPPLY_PROP_TECHNOLOGY: | 
|  | 354 | val->intval = charger->battery_info->technology; | 
|  | 355 | break; | 
|  | 356 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: | 
|  | 357 | val->intval = charger->battery_info->voltage_max_design; | 
|  | 358 | break; | 
|  | 359 | case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: | 
|  | 360 | val->intval = charger->battery_info->voltage_min_design; | 
|  | 361 | break; | 
|  | 362 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | 
|  | 363 | val->intval = da9030_reg_to_mV(charger->adc.vbat_res) * 1000; | 
|  | 364 | break; | 
|  | 365 | case POWER_SUPPLY_PROP_CURRENT_AVG: | 
|  | 366 | val->intval = | 
|  | 367 | da9030_reg_to_mA(charger->adc.ichaverage_res) * 1000; | 
|  | 368 | break; | 
|  | 369 | case POWER_SUPPLY_PROP_MODEL_NAME: | 
|  | 370 | val->strval = charger->battery_info->name; | 
|  | 371 | break; | 
|  | 372 | default: | 
|  | 373 | break; | 
|  | 374 | } | 
|  | 375 |  | 
|  | 376 | return 0; | 
|  | 377 | } | 
|  | 378 |  | 
|  | 379 | static void da9030_battery_vbat_event(struct da9030_charger *charger) | 
|  | 380 | { | 
|  | 381 | da9030_read_adc(charger, &charger->adc); | 
|  | 382 |  | 
|  | 383 | if (charger->is_on) | 
|  | 384 | return; | 
|  | 385 |  | 
|  | 386 | if (charger->adc.vbat_res < charger->thresholds.vbat_low) { | 
|  | 387 | /* set VBAT threshold for critical */ | 
|  | 388 | da903x_write(charger->master, DA9030_VBATMON, | 
|  | 389 | charger->thresholds.vbat_crit); | 
|  | 390 | if (charger->battery_low) | 
|  | 391 | charger->battery_low(); | 
|  | 392 | } else if (charger->adc.vbat_res < | 
|  | 393 | charger->thresholds.vbat_crit) { | 
|  | 394 | /* notify the system of battery critical */ | 
|  | 395 | if (charger->battery_critical) | 
|  | 396 | charger->battery_critical(); | 
|  | 397 | } | 
|  | 398 | } | 
|  | 399 |  | 
|  | 400 | static int da9030_battery_event(struct notifier_block *nb, unsigned long event, | 
|  | 401 | void *data) | 
|  | 402 | { | 
|  | 403 | struct da9030_charger *charger = | 
|  | 404 | container_of(nb, struct da9030_charger, nb); | 
| Mike Rapoport | 342d765 | 2008-12-30 22:44:53 +0100 | [diff] [blame] | 405 |  | 
|  | 406 | switch (event) { | 
|  | 407 | case DA9030_EVENT_CHDET: | 
| Mike Rapoport | a35d01a | 2009-06-09 01:09:45 +0400 | [diff] [blame] | 408 | cancel_delayed_work_sync(&charger->work); | 
|  | 409 | schedule_work(&charger->work.work); | 
| Mike Rapoport | 342d765 | 2008-12-30 22:44:53 +0100 | [diff] [blame] | 410 | break; | 
|  | 411 | case DA9030_EVENT_VBATMON: | 
|  | 412 | da9030_battery_vbat_event(charger); | 
|  | 413 | break; | 
|  | 414 | case DA9030_EVENT_CHIOVER: | 
|  | 415 | case DA9030_EVENT_TBAT: | 
|  | 416 | da9030_set_charge(charger, 0); | 
|  | 417 | break; | 
|  | 418 | } | 
|  | 419 |  | 
|  | 420 | return 0; | 
|  | 421 | } | 
|  | 422 |  | 
|  | 423 | static void da9030_battery_convert_thresholds(struct da9030_charger *charger, | 
|  | 424 | struct da9030_battery_info *pdata) | 
|  | 425 | { | 
|  | 426 | charger->thresholds.tbat_low = pdata->tbat_low; | 
|  | 427 | charger->thresholds.tbat_high = pdata->tbat_high; | 
|  | 428 | charger->thresholds.tbat_restart  = pdata->tbat_restart; | 
|  | 429 |  | 
|  | 430 | charger->thresholds.vbat_low = | 
|  | 431 | da9030_millivolt_to_reg(pdata->vbat_low); | 
|  | 432 | charger->thresholds.vbat_crit = | 
|  | 433 | da9030_millivolt_to_reg(pdata->vbat_crit); | 
|  | 434 | charger->thresholds.vbat_charge_start = | 
|  | 435 | da9030_millivolt_to_reg(pdata->vbat_charge_start); | 
|  | 436 | charger->thresholds.vbat_charge_stop = | 
|  | 437 | da9030_millivolt_to_reg(pdata->vbat_charge_stop); | 
|  | 438 | charger->thresholds.vbat_charge_restart = | 
|  | 439 | da9030_millivolt_to_reg(pdata->vbat_charge_restart); | 
|  | 440 |  | 
|  | 441 | charger->thresholds.vcharge_min = | 
|  | 442 | da9030_millivolt_to_reg(pdata->vcharge_min); | 
|  | 443 | charger->thresholds.vcharge_max = | 
|  | 444 | da9030_millivolt_to_reg(pdata->vcharge_max); | 
|  | 445 | } | 
|  | 446 |  | 
|  | 447 | static void da9030_battery_setup_psy(struct da9030_charger *charger) | 
|  | 448 | { | 
|  | 449 | struct power_supply *psy = &charger->psy; | 
|  | 450 | struct power_supply_info *info = charger->battery_info; | 
|  | 451 |  | 
|  | 452 | psy->name = info->name; | 
|  | 453 | psy->use_for_apm = info->use_for_apm; | 
|  | 454 | psy->type = POWER_SUPPLY_TYPE_BATTERY; | 
|  | 455 | psy->get_property = da9030_battery_get_property; | 
|  | 456 |  | 
|  | 457 | psy->properties = da9030_battery_props; | 
|  | 458 | psy->num_properties = ARRAY_SIZE(da9030_battery_props); | 
|  | 459 | }; | 
|  | 460 |  | 
|  | 461 | static int da9030_battery_charger_init(struct da9030_charger *charger) | 
|  | 462 | { | 
|  | 463 | char v[5]; | 
|  | 464 | int ret; | 
|  | 465 |  | 
|  | 466 | v[0] = v[1] = charger->thresholds.vbat_low; | 
|  | 467 | v[2] = charger->thresholds.tbat_high; | 
|  | 468 | v[3] = charger->thresholds.tbat_restart; | 
|  | 469 | v[4] = charger->thresholds.tbat_low; | 
|  | 470 |  | 
|  | 471 | ret = da903x_writes(charger->master, DA9030_VBATMON, 5, v); | 
|  | 472 | if (ret) | 
|  | 473 | return ret; | 
|  | 474 |  | 
|  | 475 | /* | 
|  | 476 | * Enable reference voltage supply for ADC from the LDO_INTERNAL | 
|  | 477 | * regulator. Must be set before ADC measurements can be made. | 
|  | 478 | */ | 
|  | 479 | ret = da903x_write(charger->master, DA9030_ADC_MAN_CONTROL, | 
|  | 480 | DA9030_ADC_LDO_INT_ENABLE | | 
|  | 481 | DA9030_ADC_TBATREF_ENABLE); | 
|  | 482 | if (ret) | 
|  | 483 | return ret; | 
|  | 484 |  | 
|  | 485 | /* enable auto ADC measuremnts */ | 
|  | 486 | return da903x_write(charger->master, DA9030_ADC_AUTO_CONTROL, | 
|  | 487 | DA9030_ADC_TBAT_ENABLE | DA9030_ADC_VBAT_IN_TXON | | 
|  | 488 | DA9030_ADC_VCH_ENABLE | DA9030_ADC_ICH_ENABLE | | 
|  | 489 | DA9030_ADC_VBAT_ENABLE | | 
|  | 490 | DA9030_ADC_AUTO_SLEEP_ENABLE); | 
|  | 491 | } | 
|  | 492 |  | 
|  | 493 | static int da9030_battery_probe(struct platform_device *pdev) | 
|  | 494 | { | 
|  | 495 | struct da9030_charger *charger; | 
|  | 496 | struct da9030_battery_info *pdata = pdev->dev.platform_data; | 
|  | 497 | int ret; | 
|  | 498 |  | 
|  | 499 | if (pdata == NULL) | 
|  | 500 | return -EINVAL; | 
|  | 501 |  | 
|  | 502 | if (pdata->charge_milliamp >= 1500 || | 
|  | 503 | pdata->charge_millivolt < 4000 || | 
|  | 504 | pdata->charge_millivolt > 4350) | 
|  | 505 | return -EINVAL; | 
|  | 506 |  | 
|  | 507 | charger = kzalloc(sizeof(*charger), GFP_KERNEL); | 
|  | 508 | if (charger == NULL) | 
|  | 509 | return -ENOMEM; | 
|  | 510 |  | 
|  | 511 | charger->master = pdev->dev.parent; | 
|  | 512 |  | 
| Stefan Weil | 5324dc0 | 2010-02-02 14:45:04 -0800 | [diff] [blame] | 513 | /* 10 seconds between monitor runs unless platform defines other | 
| Mike Rapoport | 342d765 | 2008-12-30 22:44:53 +0100 | [diff] [blame] | 514 | interval */ | 
|  | 515 | charger->interval = msecs_to_jiffies( | 
|  | 516 | (pdata->batmon_interval ? : 10) * 1000); | 
|  | 517 |  | 
|  | 518 | charger->charge_milliamp = pdata->charge_milliamp; | 
|  | 519 | charger->charge_millivolt = pdata->charge_millivolt; | 
|  | 520 | charger->battery_info = pdata->battery_info; | 
|  | 521 | charger->battery_low = pdata->battery_low; | 
|  | 522 | charger->battery_critical = pdata->battery_critical; | 
|  | 523 |  | 
|  | 524 | da9030_battery_convert_thresholds(charger, pdata); | 
|  | 525 |  | 
|  | 526 | ret = da9030_battery_charger_init(charger); | 
|  | 527 | if (ret) | 
|  | 528 | goto err_charger_init; | 
|  | 529 |  | 
|  | 530 | INIT_DELAYED_WORK(&charger->work, da9030_charging_monitor); | 
|  | 531 | schedule_delayed_work(&charger->work, charger->interval); | 
|  | 532 |  | 
|  | 533 | charger->nb.notifier_call = da9030_battery_event; | 
|  | 534 | ret = da903x_register_notifier(charger->master, &charger->nb, | 
|  | 535 | DA9030_EVENT_CHDET | | 
|  | 536 | DA9030_EVENT_VBATMON | | 
|  | 537 | DA9030_EVENT_CHIOVER | | 
|  | 538 | DA9030_EVENT_TBAT); | 
|  | 539 | if (ret) | 
|  | 540 | goto err_notifier; | 
|  | 541 |  | 
|  | 542 | da9030_battery_setup_psy(charger); | 
|  | 543 | ret = power_supply_register(&pdev->dev, &charger->psy); | 
|  | 544 | if (ret) | 
|  | 545 | goto err_ps_register; | 
|  | 546 |  | 
|  | 547 | charger->debug_file = da9030_bat_create_debugfs(charger); | 
|  | 548 | platform_set_drvdata(pdev, charger); | 
|  | 549 | return 0; | 
|  | 550 |  | 
|  | 551 | err_ps_register: | 
|  | 552 | da903x_unregister_notifier(charger->master, &charger->nb, | 
|  | 553 | DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON | | 
|  | 554 | DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT); | 
|  | 555 | err_notifier: | 
|  | 556 | cancel_delayed_work(&charger->work); | 
|  | 557 |  | 
|  | 558 | err_charger_init: | 
|  | 559 | kfree(charger); | 
|  | 560 |  | 
|  | 561 | return ret; | 
|  | 562 | } | 
|  | 563 |  | 
|  | 564 | static int da9030_battery_remove(struct platform_device *dev) | 
|  | 565 | { | 
|  | 566 | struct da9030_charger *charger = platform_get_drvdata(dev); | 
|  | 567 |  | 
|  | 568 | da9030_bat_remove_debugfs(charger); | 
|  | 569 |  | 
|  | 570 | da903x_unregister_notifier(charger->master, &charger->nb, | 
|  | 571 | DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON | | 
|  | 572 | DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT); | 
| Mike Rapoport | a35d01a | 2009-06-09 01:09:45 +0400 | [diff] [blame] | 573 | cancel_delayed_work_sync(&charger->work); | 
|  | 574 | da9030_set_charge(charger, 0); | 
| Mike Rapoport | 342d765 | 2008-12-30 22:44:53 +0100 | [diff] [blame] | 575 | power_supply_unregister(&charger->psy); | 
|  | 576 |  | 
|  | 577 | kfree(charger); | 
|  | 578 |  | 
|  | 579 | return 0; | 
|  | 580 | } | 
|  | 581 |  | 
|  | 582 | static struct platform_driver da903x_battery_driver = { | 
|  | 583 | .driver	= { | 
|  | 584 | .name	= "da903x-battery", | 
|  | 585 | .owner	= THIS_MODULE, | 
|  | 586 | }, | 
|  | 587 | .probe = da9030_battery_probe, | 
|  | 588 | .remove = da9030_battery_remove, | 
|  | 589 | }; | 
|  | 590 |  | 
|  | 591 | static int da903x_battery_init(void) | 
|  | 592 | { | 
|  | 593 | return platform_driver_register(&da903x_battery_driver); | 
|  | 594 | } | 
|  | 595 |  | 
|  | 596 | static void da903x_battery_exit(void) | 
|  | 597 | { | 
|  | 598 | platform_driver_unregister(&da903x_battery_driver); | 
|  | 599 | } | 
|  | 600 |  | 
|  | 601 | module_init(da903x_battery_init); | 
|  | 602 | module_exit(da903x_battery_exit); | 
|  | 603 |  | 
|  | 604 | MODULE_DESCRIPTION("DA9030 battery charger driver"); | 
|  | 605 | MODULE_AUTHOR("Mike Rapoport, CompuLab"); | 
|  | 606 | MODULE_LICENSE("GPL"); |