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