1342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport/* 2342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport * Battery charger driver for Dialog Semiconductor DA9030 3342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport * 4342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport * Copyright (C) 2008 Compulab, Ltd. 5342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport * Mike Rapoport <mike@compulab.co.il> 6342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport * 7342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport * This program is free software; you can redistribute it and/or modify 8342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport * it under the terms of the GNU General Public License version 2 as 9342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport * published by the Free Software Foundation. 10342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport */ 11342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 12342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/kernel.h> 135a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 14342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/init.h> 15342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/types.h> 16342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/device.h> 17342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/workqueue.h> 18342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/module.h> 19342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/platform_device.h> 20342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/power_supply.h> 21342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/mfd/da903x.h> 22342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 23342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/debugfs.h> 24342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/seq_file.h> 25342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 26342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_FAULT_LOG 0x0a 27342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_FAULT_LOG_OVER_TEMP (1 << 7) 28342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_FAULT_LOG_VBAT_OVER (1 << 4) 29342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 30342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_CHARGE_CONTROL 0x28 31342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_CHRG_CHARGER_ENABLE (1 << 7) 32342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 33342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_MAN_CONTROL 0x30 34342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_TBATREF_ENABLE (1 << 5) 35342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_LDO_INT_ENABLE (1 << 4) 36342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 37342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_AUTO_CONTROL 0x31 38342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_TBAT_ENABLE (1 << 5) 39342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_VBAT_IN_TXON (1 << 4) 40342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_VCH_ENABLE (1 << 3) 41342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_ICH_ENABLE (1 << 2) 42342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_VBAT_ENABLE (1 << 1) 43342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_AUTO_SLEEP_ENABLE (1 << 0) 44342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 45342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_VBATMON 0x32 46342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_VBATMONTXON 0x33 47342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_TBATHIGHP 0x34 48342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_TBATHIGHN 0x35 49342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_TBATLOW 0x36 50342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 51342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_VBAT_RES 0x41 52342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_VBATMIN_RES 0x42 53342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_VBATMINTXON_RES 0x43 54342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ICHMAX_RES 0x44 55342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ICHMIN_RES 0x45 56342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ICHAVERAGE_RES 0x46 57342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_VCHMAX_RES 0x47 58342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_VCHMIN_RES 0x48 59342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_TBAT_RES 0x49 60342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 61342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstruct da9030_adc_res { 62342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport uint8_t vbat_res; 63342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport uint8_t vbatmin_res; 64342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport uint8_t vbatmintxon; 65342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport uint8_t ichmax_res; 66342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport uint8_t ichmin_res; 67342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport uint8_t ichaverage_res; 68342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport uint8_t vchmax_res; 69342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport uint8_t vchmin_res; 70342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport uint8_t tbat_res; 71342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport uint8_t adc_in4_res; 72342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport uint8_t adc_in5_res; 73342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}; 74342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 75342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstruct da9030_battery_thresholds { 76342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport int tbat_low; 77342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport int tbat_high; 78342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport int tbat_restart; 79342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 80342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport int vbat_low; 81342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport int vbat_crit; 82342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport int vbat_charge_start; 83342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport int vbat_charge_stop; 84342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport int vbat_charge_restart; 85342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 86342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport int vcharge_min; 87342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport int vcharge_max; 88342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}; 89342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 90342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstruct da9030_charger { 91342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport struct power_supply psy; 92342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 93342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport struct device *master; 94342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 95342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport struct da9030_adc_res adc; 96342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport struct delayed_work work; 97342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport unsigned int interval; 98342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 99342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport struct power_supply_info *battery_info; 100342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 101342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport struct da9030_battery_thresholds thresholds; 102342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 103342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport unsigned int charge_milliamp; 104342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport unsigned int charge_millivolt; 105342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 106342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport /* charger status */ 107342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport bool chdet; 108342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport uint8_t fault; 109342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport int mA; 110342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport int mV; 111342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport bool is_on; 112342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 113342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport struct notifier_block nb; 114342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 115342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport /* platform callbacks for battery low and critical events */ 116342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport void (*battery_low)(void); 117342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport void (*battery_critical)(void); 118342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 119342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport struct dentry *debug_file; 120342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}; 121342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 122342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic inline int da9030_reg_to_mV(int reg) 123342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 124342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport return ((reg * 2650) >> 8) + 2650; 125342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 126342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 127342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic inline int da9030_millivolt_to_reg(int mV) 128342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 129342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport return ((mV - 2650) << 8) / 2650; 130342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 131342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 132342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic inline int da9030_reg_to_mA(int reg) 133342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 134342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport return ((reg * 24000) >> 8) / 15; 135342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 136342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 137342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#ifdef CONFIG_DEBUG_FS 138342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic int bat_debug_show(struct seq_file *s, void *data) 139342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 140342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport struct da9030_charger *charger = s->private; 141342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 142342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport seq_printf(s, "charger is %s\n", charger->is_on ? "on" : "off"); 143342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (charger->chdet) { 144342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport seq_printf(s, "iset = %dmA, vset = %dmV\n", 145342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->mA, charger->mV); 146342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport } 147342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 148342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport seq_printf(s, "vbat_res = %d (%dmV)\n", 149342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->adc.vbat_res, 150342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_reg_to_mV(charger->adc.vbat_res)); 151342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport seq_printf(s, "vbatmin_res = %d (%dmV)\n", 152342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->adc.vbatmin_res, 153342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_reg_to_mV(charger->adc.vbatmin_res)); 154342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport seq_printf(s, "vbatmintxon = %d (%dmV)\n", 155342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->adc.vbatmintxon, 156342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_reg_to_mV(charger->adc.vbatmintxon)); 157342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport seq_printf(s, "ichmax_res = %d (%dmA)\n", 158342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->adc.ichmax_res, 159342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_reg_to_mV(charger->adc.ichmax_res)); 160342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport seq_printf(s, "ichmin_res = %d (%dmA)\n", 161342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->adc.ichmin_res, 162342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_reg_to_mA(charger->adc.ichmin_res)); 163342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport seq_printf(s, "ichaverage_res = %d (%dmA)\n", 164342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->adc.ichaverage_res, 165342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_reg_to_mA(charger->adc.ichaverage_res)); 166342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport seq_printf(s, "vchmax_res = %d (%dmV)\n", 167342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->adc.vchmax_res, 168342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_reg_to_mA(charger->adc.vchmax_res)); 169342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport seq_printf(s, "vchmin_res = %d (%dmV)\n", 170342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->adc.vchmin_res, 171342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_reg_to_mV(charger->adc.vchmin_res)); 172342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 173342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport return 0; 174342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 175342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 176342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic int debug_open(struct inode *inode, struct file *file) 177342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 178342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport return single_open(file, bat_debug_show, inode->i_private); 179342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 180342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 181342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic const struct file_operations bat_debug_fops = { 182342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport .open = debug_open, 183342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport .read = seq_read, 184342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport .llseek = seq_lseek, 185342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport .release = single_release, 186342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}; 187342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 188342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger) 189342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 190342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->debug_file = debugfs_create_file("charger", 0666, 0, charger, 191342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport &bat_debug_fops); 192342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport return charger->debug_file; 193342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 194342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 195342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_bat_remove_debugfs(struct da9030_charger *charger) 196342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 197342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport debugfs_remove(charger->debug_file); 198342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 199342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#else 200342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic inline struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger) 201342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 202342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport return NULL; 203342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 204342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic inline void da9030_bat_remove_debugfs(struct da9030_charger *charger) 205342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 206342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 207342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#endif 208342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 209342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic inline void da9030_read_adc(struct da9030_charger *charger, 210342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport struct da9030_adc_res *adc) 211342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 212342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da903x_reads(charger->master, DA9030_VBAT_RES, 213342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport sizeof(*adc), (uint8_t *)adc); 214342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 215342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 216342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_charger_update_state(struct da9030_charger *charger) 217342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 218342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport uint8_t val; 219342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 220342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da903x_read(charger->master, DA9030_CHARGE_CONTROL, &val); 221342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->is_on = (val & DA9030_CHRG_CHARGER_ENABLE) ? 1 : 0; 222342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->mA = ((val >> 3) & 0xf) * 100; 223342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->mV = (val & 0x7) * 50 + 4000; 224342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 225342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_read_adc(charger, &charger->adc); 226342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da903x_read(charger->master, DA9030_FAULT_LOG, &charger->fault); 227342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->chdet = da903x_query_status(charger->master, 228342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport DA9030_STATUS_CHDET); 229342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 230342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 231342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_set_charge(struct da9030_charger *charger, int on) 232342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 233342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport uint8_t val; 234342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 235342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (on) { 236342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport val = DA9030_CHRG_CHARGER_ENABLE; 237342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport val |= (charger->charge_milliamp / 100) << 3; 238342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport val |= (charger->charge_millivolt - 4000) / 50; 239342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->is_on = 1; 240342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport } else { 241342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport val = 0; 242342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->is_on = 0; 243342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport } 244342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 245342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da903x_write(charger->master, DA9030_CHARGE_CONTROL, val); 246a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport 247a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport power_supply_changed(&charger->psy); 248342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 249342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 250342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_charger_check_state(struct da9030_charger *charger) 251342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 252342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_charger_update_state(charger); 253342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 254342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport /* we wake or boot with external power on */ 255342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (!charger->is_on) { 256342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if ((charger->chdet) && 257342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport (charger->adc.vbat_res < 258342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->thresholds.vbat_charge_start)) { 259342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_set_charge(charger, 1); 260342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport } 261342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport } else { 262a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport /* Charger has been pulled out */ 263a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport if (!charger->chdet) { 264a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport da9030_set_charge(charger, 0); 265a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport return; 266a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport } 267a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport 268342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (charger->adc.vbat_res >= 269342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->thresholds.vbat_charge_stop) { 270342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_set_charge(charger, 0); 271342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da903x_write(charger->master, DA9030_VBATMON, 272342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->thresholds.vbat_charge_restart); 273342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport } else if (charger->adc.vbat_res > 274342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->thresholds.vbat_low) { 275342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport /* we are charging and passed LOW_THRESH, 276342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport so upate DA9030 VBAT threshold 277342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport */ 278342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da903x_write(charger->master, DA9030_VBATMON, 279342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->thresholds.vbat_low); 280342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport } 281342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (charger->adc.vchmax_res > charger->thresholds.vcharge_max || 282342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->adc.vchmin_res < charger->thresholds.vcharge_min || 283342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport /* Tempreture readings are negative */ 284342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->adc.tbat_res < charger->thresholds.tbat_high || 285342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->adc.tbat_res > charger->thresholds.tbat_low) { 286342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport /* disable charger */ 287342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_set_charge(charger, 0); 288342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport } 289342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport } 290342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 291342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 292342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_charging_monitor(struct work_struct *work) 293342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 294342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport struct da9030_charger *charger; 295342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 296342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger = container_of(work, struct da9030_charger, work.work); 297342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 298342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_charger_check_state(charger); 299342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 300342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport /* reschedule for the next time */ 301342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport schedule_delayed_work(&charger->work, charger->interval); 302342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 303342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 304342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic enum power_supply_property da9030_battery_props[] = { 305342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport POWER_SUPPLY_PROP_MODEL_NAME, 306342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport POWER_SUPPLY_PROP_STATUS, 307342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport POWER_SUPPLY_PROP_HEALTH, 308342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport POWER_SUPPLY_PROP_TECHNOLOGY, 309342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 310342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 311342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport POWER_SUPPLY_PROP_VOLTAGE_NOW, 312342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport POWER_SUPPLY_PROP_CURRENT_AVG, 313342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}; 314342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 315342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_battery_check_status(struct da9030_charger *charger, 316342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport union power_supply_propval *val) 317342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 318342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (charger->chdet) { 319342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (charger->is_on) 320342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport val->intval = POWER_SUPPLY_STATUS_CHARGING; 321342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport else 322342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 323342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport } else { 324342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 325342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport } 326342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 327342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 328342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_battery_check_health(struct da9030_charger *charger, 329342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport union power_supply_propval *val) 330342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 331342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (charger->fault & DA9030_FAULT_LOG_OVER_TEMP) 332342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; 333342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport else if (charger->fault & DA9030_FAULT_LOG_VBAT_OVER) 334342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 335342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport else 336342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport val->intval = POWER_SUPPLY_HEALTH_GOOD; 337342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 338342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 339342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic int da9030_battery_get_property(struct power_supply *psy, 340342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport enum power_supply_property psp, 341342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport union power_supply_propval *val) 342342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 343342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport struct da9030_charger *charger; 344342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger = container_of(psy, struct da9030_charger, psy); 345342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 346342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport switch (psp) { 347342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport case POWER_SUPPLY_PROP_STATUS: 348342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_battery_check_status(charger, val); 349342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport break; 350342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport case POWER_SUPPLY_PROP_HEALTH: 351342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_battery_check_health(charger, val); 352342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport break; 353342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport case POWER_SUPPLY_PROP_TECHNOLOGY: 354342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport val->intval = charger->battery_info->technology; 355342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport break; 356342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 357342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport val->intval = charger->battery_info->voltage_max_design; 358342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport break; 359342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 360342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport val->intval = charger->battery_info->voltage_min_design; 361342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport break; 362342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport case POWER_SUPPLY_PROP_VOLTAGE_NOW: 363342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport val->intval = da9030_reg_to_mV(charger->adc.vbat_res) * 1000; 364342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport break; 365342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport case POWER_SUPPLY_PROP_CURRENT_AVG: 366342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport val->intval = 367342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_reg_to_mA(charger->adc.ichaverage_res) * 1000; 368342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport break; 369342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport case POWER_SUPPLY_PROP_MODEL_NAME: 370342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport val->strval = charger->battery_info->name; 371342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport break; 372342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport default: 373342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport break; 374342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport } 375342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 376342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport return 0; 377342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 378342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 379342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_battery_vbat_event(struct da9030_charger *charger) 380342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 381342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_read_adc(charger, &charger->adc); 382342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 383342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (charger->is_on) 384342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport return; 385342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 386342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (charger->adc.vbat_res < charger->thresholds.vbat_low) { 387342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport /* set VBAT threshold for critical */ 388342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da903x_write(charger->master, DA9030_VBATMON, 389342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->thresholds.vbat_crit); 390342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (charger->battery_low) 391342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->battery_low(); 392342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport } else if (charger->adc.vbat_res < 393342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->thresholds.vbat_crit) { 394342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport /* notify the system of battery critical */ 395342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (charger->battery_critical) 396342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->battery_critical(); 397342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport } 398342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 399342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 400342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic int da9030_battery_event(struct notifier_block *nb, unsigned long event, 401342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport void *data) 402342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 403342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport struct da9030_charger *charger = 404342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport container_of(nb, struct da9030_charger, nb); 405342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 406342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport switch (event) { 407342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport case DA9030_EVENT_CHDET: 408a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport cancel_delayed_work_sync(&charger->work); 409a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport schedule_work(&charger->work.work); 410342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport break; 411342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport case DA9030_EVENT_VBATMON: 412342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_battery_vbat_event(charger); 413342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport break; 414342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport case DA9030_EVENT_CHIOVER: 415342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport case DA9030_EVENT_TBAT: 416342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_set_charge(charger, 0); 417342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport break; 418342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport } 419342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 420342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport return 0; 421342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 422342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 423342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_battery_convert_thresholds(struct da9030_charger *charger, 424342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport struct da9030_battery_info *pdata) 425342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 426342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->thresholds.tbat_low = pdata->tbat_low; 427342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->thresholds.tbat_high = pdata->tbat_high; 428342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->thresholds.tbat_restart = pdata->tbat_restart; 429342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 430342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->thresholds.vbat_low = 431342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_millivolt_to_reg(pdata->vbat_low); 432342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->thresholds.vbat_crit = 433342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_millivolt_to_reg(pdata->vbat_crit); 434342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->thresholds.vbat_charge_start = 435342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_millivolt_to_reg(pdata->vbat_charge_start); 436342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->thresholds.vbat_charge_stop = 437342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_millivolt_to_reg(pdata->vbat_charge_stop); 438342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->thresholds.vbat_charge_restart = 439342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_millivolt_to_reg(pdata->vbat_charge_restart); 440342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 441342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->thresholds.vcharge_min = 442342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_millivolt_to_reg(pdata->vcharge_min); 443342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->thresholds.vcharge_max = 444342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_millivolt_to_reg(pdata->vcharge_max); 445342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 446342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 447342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_battery_setup_psy(struct da9030_charger *charger) 448342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 449342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport struct power_supply *psy = &charger->psy; 450342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport struct power_supply_info *info = charger->battery_info; 451342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 452342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport psy->name = info->name; 453342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport psy->use_for_apm = info->use_for_apm; 454342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport psy->type = POWER_SUPPLY_TYPE_BATTERY; 455342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport psy->get_property = da9030_battery_get_property; 456342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 457342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport psy->properties = da9030_battery_props; 458342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport psy->num_properties = ARRAY_SIZE(da9030_battery_props); 459342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}; 460342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 461342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic int da9030_battery_charger_init(struct da9030_charger *charger) 462342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 463342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport char v[5]; 464342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport int ret; 465342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 466342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport v[0] = v[1] = charger->thresholds.vbat_low; 467342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport v[2] = charger->thresholds.tbat_high; 468342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport v[3] = charger->thresholds.tbat_restart; 469342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport v[4] = charger->thresholds.tbat_low; 470342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 471342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport ret = da903x_writes(charger->master, DA9030_VBATMON, 5, v); 472342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (ret) 473342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport return ret; 474342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 475342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport /* 476342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport * Enable reference voltage supply for ADC from the LDO_INTERNAL 477342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport * regulator. Must be set before ADC measurements can be made. 478342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport */ 479342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport ret = da903x_write(charger->master, DA9030_ADC_MAN_CONTROL, 480342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport DA9030_ADC_LDO_INT_ENABLE | 481342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport DA9030_ADC_TBATREF_ENABLE); 482342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (ret) 483342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport return ret; 484342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 485342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport /* enable auto ADC measuremnts */ 486342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport return da903x_write(charger->master, DA9030_ADC_AUTO_CONTROL, 487342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport DA9030_ADC_TBAT_ENABLE | DA9030_ADC_VBAT_IN_TXON | 488342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport DA9030_ADC_VCH_ENABLE | DA9030_ADC_ICH_ENABLE | 489342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport DA9030_ADC_VBAT_ENABLE | 490342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport DA9030_ADC_AUTO_SLEEP_ENABLE); 491342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 492342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 493342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic int da9030_battery_probe(struct platform_device *pdev) 494342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 495342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport struct da9030_charger *charger; 496342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport struct da9030_battery_info *pdata = pdev->dev.platform_data; 497342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport int ret; 498342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 499342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (pdata == NULL) 500342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport return -EINVAL; 501342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 502342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (pdata->charge_milliamp >= 1500 || 503342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport pdata->charge_millivolt < 4000 || 504342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport pdata->charge_millivolt > 4350) 505342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport return -EINVAL; 506342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 507342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger = kzalloc(sizeof(*charger), GFP_KERNEL); 508342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (charger == NULL) 509342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport return -ENOMEM; 510342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 511342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->master = pdev->dev.parent; 512342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 5135324dc0e3872324ed0bf372bce7bc437436910b6Stefan Weil /* 10 seconds between monitor runs unless platform defines other 514342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport interval */ 515342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->interval = msecs_to_jiffies( 516342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport (pdata->batmon_interval ? : 10) * 1000); 517342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 518342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->charge_milliamp = pdata->charge_milliamp; 519342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->charge_millivolt = pdata->charge_millivolt; 520342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->battery_info = pdata->battery_info; 521342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->battery_low = pdata->battery_low; 522342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->battery_critical = pdata->battery_critical; 523342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 524342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_battery_convert_thresholds(charger, pdata); 525342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 526342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport ret = da9030_battery_charger_init(charger); 527342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (ret) 528342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport goto err_charger_init; 529342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 530342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport INIT_DELAYED_WORK(&charger->work, da9030_charging_monitor); 531342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport schedule_delayed_work(&charger->work, charger->interval); 532342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 533342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->nb.notifier_call = da9030_battery_event; 534342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport ret = da903x_register_notifier(charger->master, &charger->nb, 535342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport DA9030_EVENT_CHDET | 536342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport DA9030_EVENT_VBATMON | 537342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport DA9030_EVENT_CHIOVER | 538342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport DA9030_EVENT_TBAT); 539342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (ret) 540342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport goto err_notifier; 541342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 542342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_battery_setup_psy(charger); 543342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport ret = power_supply_register(&pdev->dev, &charger->psy); 544342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport if (ret) 545342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport goto err_ps_register; 546342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 547342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport charger->debug_file = da9030_bat_create_debugfs(charger); 548342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport platform_set_drvdata(pdev, charger); 549342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport return 0; 550342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 551342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoporterr_ps_register: 552342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da903x_unregister_notifier(charger->master, &charger->nb, 553342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON | 554342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT); 555342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoporterr_notifier: 556342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport cancel_delayed_work(&charger->work); 557342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 558342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoporterr_charger_init: 559342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport kfree(charger); 560342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 561342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport return ret; 562342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 563342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 564342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic int da9030_battery_remove(struct platform_device *dev) 565342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{ 566342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport struct da9030_charger *charger = platform_get_drvdata(dev); 567342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 568342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da9030_bat_remove_debugfs(charger); 569342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 570342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport da903x_unregister_notifier(charger->master, &charger->nb, 571342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON | 572342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT); 573a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport cancel_delayed_work_sync(&charger->work); 574a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport da9030_set_charge(charger, 0); 575342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport power_supply_unregister(&charger->psy); 576342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 577342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport kfree(charger); 578342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 579342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport return 0; 580342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport} 581342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 582342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic struct platform_driver da903x_battery_driver = { 583342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport .driver = { 584342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport .name = "da903x-battery", 585342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport .owner = THIS_MODULE, 586342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport }, 587342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport .probe = da9030_battery_probe, 588342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport .remove = da9030_battery_remove, 589342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}; 590342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 591300bac7fb85a20b2704dc3645419057992f78565Axel Linmodule_platform_driver(da903x_battery_driver); 592342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport 593342d765e011f9cbe4292119a9164f76ccf0b922aMike RapoportMODULE_DESCRIPTION("DA9030 battery charger driver"); 594342d765e011f9cbe4292119a9164f76ccf0b922aMike RapoportMODULE_AUTHOR("Mike Rapoport, CompuLab"); 595342d765e011f9cbe4292119a9164f76ccf0b922aMike RapoportMODULE_LICENSE("GPL"); 596