162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Battery charger driver for Dialog Semiconductor DA9030 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008 Compulab, Ltd. 662306a36Sopenharmony_ci * Mike Rapoport <mike@compulab.co.il> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/device.h> 1462306a36Sopenharmony_ci#include <linux/workqueue.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/power_supply.h> 1862306a36Sopenharmony_ci#include <linux/mfd/da903x.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/debugfs.h> 2162306a36Sopenharmony_ci#include <linux/seq_file.h> 2262306a36Sopenharmony_ci#include <linux/notifier.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define DA9030_FAULT_LOG 0x0a 2562306a36Sopenharmony_ci#define DA9030_FAULT_LOG_OVER_TEMP (1 << 7) 2662306a36Sopenharmony_ci#define DA9030_FAULT_LOG_VBAT_OVER (1 << 4) 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define DA9030_CHARGE_CONTROL 0x28 2962306a36Sopenharmony_ci#define DA9030_CHRG_CHARGER_ENABLE (1 << 7) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define DA9030_ADC_MAN_CONTROL 0x30 3262306a36Sopenharmony_ci#define DA9030_ADC_TBATREF_ENABLE (1 << 5) 3362306a36Sopenharmony_ci#define DA9030_ADC_LDO_INT_ENABLE (1 << 4) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define DA9030_ADC_AUTO_CONTROL 0x31 3662306a36Sopenharmony_ci#define DA9030_ADC_TBAT_ENABLE (1 << 5) 3762306a36Sopenharmony_ci#define DA9030_ADC_VBAT_IN_TXON (1 << 4) 3862306a36Sopenharmony_ci#define DA9030_ADC_VCH_ENABLE (1 << 3) 3962306a36Sopenharmony_ci#define DA9030_ADC_ICH_ENABLE (1 << 2) 4062306a36Sopenharmony_ci#define DA9030_ADC_VBAT_ENABLE (1 << 1) 4162306a36Sopenharmony_ci#define DA9030_ADC_AUTO_SLEEP_ENABLE (1 << 0) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define DA9030_VBATMON 0x32 4462306a36Sopenharmony_ci#define DA9030_VBATMONTXON 0x33 4562306a36Sopenharmony_ci#define DA9030_TBATHIGHP 0x34 4662306a36Sopenharmony_ci#define DA9030_TBATHIGHN 0x35 4762306a36Sopenharmony_ci#define DA9030_TBATLOW 0x36 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define DA9030_VBAT_RES 0x41 5062306a36Sopenharmony_ci#define DA9030_VBATMIN_RES 0x42 5162306a36Sopenharmony_ci#define DA9030_VBATMINTXON_RES 0x43 5262306a36Sopenharmony_ci#define DA9030_ICHMAX_RES 0x44 5362306a36Sopenharmony_ci#define DA9030_ICHMIN_RES 0x45 5462306a36Sopenharmony_ci#define DA9030_ICHAVERAGE_RES 0x46 5562306a36Sopenharmony_ci#define DA9030_VCHMAX_RES 0x47 5662306a36Sopenharmony_ci#define DA9030_VCHMIN_RES 0x48 5762306a36Sopenharmony_ci#define DA9030_TBAT_RES 0x49 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistruct da9030_adc_res { 6062306a36Sopenharmony_ci uint8_t vbat_res; 6162306a36Sopenharmony_ci uint8_t vbatmin_res; 6262306a36Sopenharmony_ci uint8_t vbatmintxon; 6362306a36Sopenharmony_ci uint8_t ichmax_res; 6462306a36Sopenharmony_ci uint8_t ichmin_res; 6562306a36Sopenharmony_ci uint8_t ichaverage_res; 6662306a36Sopenharmony_ci uint8_t vchmax_res; 6762306a36Sopenharmony_ci uint8_t vchmin_res; 6862306a36Sopenharmony_ci uint8_t tbat_res; 6962306a36Sopenharmony_ci uint8_t adc_in4_res; 7062306a36Sopenharmony_ci uint8_t adc_in5_res; 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistruct da9030_battery_thresholds { 7462306a36Sopenharmony_ci int tbat_low; 7562306a36Sopenharmony_ci int tbat_high; 7662306a36Sopenharmony_ci int tbat_restart; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci int vbat_low; 7962306a36Sopenharmony_ci int vbat_crit; 8062306a36Sopenharmony_ci int vbat_charge_start; 8162306a36Sopenharmony_ci int vbat_charge_stop; 8262306a36Sopenharmony_ci int vbat_charge_restart; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci int vcharge_min; 8562306a36Sopenharmony_ci int vcharge_max; 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistruct da9030_charger { 8962306a36Sopenharmony_ci struct power_supply *psy; 9062306a36Sopenharmony_ci struct power_supply_desc psy_desc; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci struct device *master; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci struct da9030_adc_res adc; 9562306a36Sopenharmony_ci struct delayed_work work; 9662306a36Sopenharmony_ci unsigned int interval; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci struct power_supply_info *battery_info; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci struct da9030_battery_thresholds thresholds; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci unsigned int charge_milliamp; 10362306a36Sopenharmony_ci unsigned int charge_millivolt; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* charger status */ 10662306a36Sopenharmony_ci bool chdet; 10762306a36Sopenharmony_ci uint8_t fault; 10862306a36Sopenharmony_ci int mA; 10962306a36Sopenharmony_ci int mV; 11062306a36Sopenharmony_ci bool is_on; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci struct notifier_block nb; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* platform callbacks for battery low and critical events */ 11562306a36Sopenharmony_ci void (*battery_low)(void); 11662306a36Sopenharmony_ci void (*battery_critical)(void); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci struct dentry *debug_file; 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic inline int da9030_reg_to_mV(int reg) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci return ((reg * 2650) >> 8) + 2650; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic inline int da9030_millivolt_to_reg(int mV) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci return ((mV - 2650) << 8) / 2650; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic inline int da9030_reg_to_mA(int reg) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci return ((reg * 24000) >> 8) / 15; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 13762306a36Sopenharmony_cistatic int bat_debug_show(struct seq_file *s, void *data) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct da9030_charger *charger = s->private; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci seq_printf(s, "charger is %s\n", charger->is_on ? "on" : "off"); 14262306a36Sopenharmony_ci if (charger->chdet) { 14362306a36Sopenharmony_ci seq_printf(s, "iset = %dmA, vset = %dmV\n", 14462306a36Sopenharmony_ci charger->mA, charger->mV); 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci seq_printf(s, "vbat_res = %d (%dmV)\n", 14862306a36Sopenharmony_ci charger->adc.vbat_res, 14962306a36Sopenharmony_ci da9030_reg_to_mV(charger->adc.vbat_res)); 15062306a36Sopenharmony_ci seq_printf(s, "vbatmin_res = %d (%dmV)\n", 15162306a36Sopenharmony_ci charger->adc.vbatmin_res, 15262306a36Sopenharmony_ci da9030_reg_to_mV(charger->adc.vbatmin_res)); 15362306a36Sopenharmony_ci seq_printf(s, "vbatmintxon = %d (%dmV)\n", 15462306a36Sopenharmony_ci charger->adc.vbatmintxon, 15562306a36Sopenharmony_ci da9030_reg_to_mV(charger->adc.vbatmintxon)); 15662306a36Sopenharmony_ci seq_printf(s, "ichmax_res = %d (%dmA)\n", 15762306a36Sopenharmony_ci charger->adc.ichmax_res, 15862306a36Sopenharmony_ci da9030_reg_to_mV(charger->adc.ichmax_res)); 15962306a36Sopenharmony_ci seq_printf(s, "ichmin_res = %d (%dmA)\n", 16062306a36Sopenharmony_ci charger->adc.ichmin_res, 16162306a36Sopenharmony_ci da9030_reg_to_mA(charger->adc.ichmin_res)); 16262306a36Sopenharmony_ci seq_printf(s, "ichaverage_res = %d (%dmA)\n", 16362306a36Sopenharmony_ci charger->adc.ichaverage_res, 16462306a36Sopenharmony_ci da9030_reg_to_mA(charger->adc.ichaverage_res)); 16562306a36Sopenharmony_ci seq_printf(s, "vchmax_res = %d (%dmV)\n", 16662306a36Sopenharmony_ci charger->adc.vchmax_res, 16762306a36Sopenharmony_ci da9030_reg_to_mA(charger->adc.vchmax_res)); 16862306a36Sopenharmony_ci seq_printf(s, "vchmin_res = %d (%dmV)\n", 16962306a36Sopenharmony_ci charger->adc.vchmin_res, 17062306a36Sopenharmony_ci da9030_reg_to_mV(charger->adc.vchmin_res)); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return 0; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(bat_debug); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci charger->debug_file = debugfs_create_file("charger", 0666, NULL, 18062306a36Sopenharmony_ci charger, &bat_debug_fops); 18162306a36Sopenharmony_ci return charger->debug_file; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic void da9030_bat_remove_debugfs(struct da9030_charger *charger) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci debugfs_remove(charger->debug_file); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci#else 18962306a36Sopenharmony_cistatic inline struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci return NULL; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_cistatic inline void da9030_bat_remove_debugfs(struct da9030_charger *charger) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci#endif 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic inline void da9030_read_adc(struct da9030_charger *charger, 19962306a36Sopenharmony_ci struct da9030_adc_res *adc) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci da903x_reads(charger->master, DA9030_VBAT_RES, 20262306a36Sopenharmony_ci sizeof(*adc), (uint8_t *)adc); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void da9030_charger_update_state(struct da9030_charger *charger) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci uint8_t val; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci da903x_read(charger->master, DA9030_CHARGE_CONTROL, &val); 21062306a36Sopenharmony_ci charger->is_on = (val & DA9030_CHRG_CHARGER_ENABLE) ? 1 : 0; 21162306a36Sopenharmony_ci charger->mA = ((val >> 3) & 0xf) * 100; 21262306a36Sopenharmony_ci charger->mV = (val & 0x7) * 50 + 4000; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci da9030_read_adc(charger, &charger->adc); 21562306a36Sopenharmony_ci da903x_read(charger->master, DA9030_FAULT_LOG, &charger->fault); 21662306a36Sopenharmony_ci charger->chdet = da903x_query_status(charger->master, 21762306a36Sopenharmony_ci DA9030_STATUS_CHDET); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic void da9030_set_charge(struct da9030_charger *charger, int on) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci uint8_t val; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (on) { 22562306a36Sopenharmony_ci val = DA9030_CHRG_CHARGER_ENABLE; 22662306a36Sopenharmony_ci val |= (charger->charge_milliamp / 100) << 3; 22762306a36Sopenharmony_ci val |= (charger->charge_millivolt - 4000) / 50; 22862306a36Sopenharmony_ci charger->is_on = 1; 22962306a36Sopenharmony_ci } else { 23062306a36Sopenharmony_ci val = 0; 23162306a36Sopenharmony_ci charger->is_on = 0; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci da903x_write(charger->master, DA9030_CHARGE_CONTROL, val); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci power_supply_changed(charger->psy); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic void da9030_charger_check_state(struct da9030_charger *charger) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci da9030_charger_update_state(charger); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* we wake or boot with external power on */ 24462306a36Sopenharmony_ci if (!charger->is_on) { 24562306a36Sopenharmony_ci if ((charger->chdet) && 24662306a36Sopenharmony_ci (charger->adc.vbat_res < 24762306a36Sopenharmony_ci charger->thresholds.vbat_charge_start)) { 24862306a36Sopenharmony_ci da9030_set_charge(charger, 1); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci } else { 25162306a36Sopenharmony_ci /* Charger has been pulled out */ 25262306a36Sopenharmony_ci if (!charger->chdet) { 25362306a36Sopenharmony_ci da9030_set_charge(charger, 0); 25462306a36Sopenharmony_ci return; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (charger->adc.vbat_res >= 25862306a36Sopenharmony_ci charger->thresholds.vbat_charge_stop) { 25962306a36Sopenharmony_ci da9030_set_charge(charger, 0); 26062306a36Sopenharmony_ci da903x_write(charger->master, DA9030_VBATMON, 26162306a36Sopenharmony_ci charger->thresholds.vbat_charge_restart); 26262306a36Sopenharmony_ci } else if (charger->adc.vbat_res > 26362306a36Sopenharmony_ci charger->thresholds.vbat_low) { 26462306a36Sopenharmony_ci /* we are charging and passed LOW_THRESH, 26562306a36Sopenharmony_ci so upate DA9030 VBAT threshold 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci da903x_write(charger->master, DA9030_VBATMON, 26862306a36Sopenharmony_ci charger->thresholds.vbat_low); 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci if (charger->adc.vchmax_res > charger->thresholds.vcharge_max || 27162306a36Sopenharmony_ci charger->adc.vchmin_res < charger->thresholds.vcharge_min || 27262306a36Sopenharmony_ci /* Tempreture readings are negative */ 27362306a36Sopenharmony_ci charger->adc.tbat_res < charger->thresholds.tbat_high || 27462306a36Sopenharmony_ci charger->adc.tbat_res > charger->thresholds.tbat_low) { 27562306a36Sopenharmony_ci /* disable charger */ 27662306a36Sopenharmony_ci da9030_set_charge(charger, 0); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic void da9030_charging_monitor(struct work_struct *work) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct da9030_charger *charger; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci charger = container_of(work, struct da9030_charger, work.work); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci da9030_charger_check_state(charger); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* reschedule for the next time */ 29062306a36Sopenharmony_ci schedule_delayed_work(&charger->work, charger->interval); 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic enum power_supply_property da9030_battery_props[] = { 29462306a36Sopenharmony_ci POWER_SUPPLY_PROP_MODEL_NAME, 29562306a36Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 29662306a36Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 29762306a36Sopenharmony_ci POWER_SUPPLY_PROP_TECHNOLOGY, 29862306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 29962306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 30062306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 30162306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_AVG, 30262306a36Sopenharmony_ci}; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic void da9030_battery_check_status(struct da9030_charger *charger, 30562306a36Sopenharmony_ci union power_supply_propval *val) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci if (charger->chdet) { 30862306a36Sopenharmony_ci if (charger->is_on) 30962306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_CHARGING; 31062306a36Sopenharmony_ci else 31162306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 31262306a36Sopenharmony_ci } else { 31362306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic void da9030_battery_check_health(struct da9030_charger *charger, 31862306a36Sopenharmony_ci union power_supply_propval *val) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci if (charger->fault & DA9030_FAULT_LOG_OVER_TEMP) 32162306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; 32262306a36Sopenharmony_ci else if (charger->fault & DA9030_FAULT_LOG_VBAT_OVER) 32362306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 32462306a36Sopenharmony_ci else 32562306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_GOOD; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int da9030_battery_get_property(struct power_supply *psy, 32962306a36Sopenharmony_ci enum power_supply_property psp, 33062306a36Sopenharmony_ci union power_supply_propval *val) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct da9030_charger *charger = power_supply_get_drvdata(psy); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci switch (psp) { 33562306a36Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 33662306a36Sopenharmony_ci da9030_battery_check_status(charger, val); 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 33962306a36Sopenharmony_ci da9030_battery_check_health(charger, val); 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_TECHNOLOGY: 34262306a36Sopenharmony_ci val->intval = charger->battery_info->technology; 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 34562306a36Sopenharmony_ci val->intval = charger->battery_info->voltage_max_design; 34662306a36Sopenharmony_ci break; 34762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 34862306a36Sopenharmony_ci val->intval = charger->battery_info->voltage_min_design; 34962306a36Sopenharmony_ci break; 35062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 35162306a36Sopenharmony_ci val->intval = da9030_reg_to_mV(charger->adc.vbat_res) * 1000; 35262306a36Sopenharmony_ci break; 35362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_AVG: 35462306a36Sopenharmony_ci val->intval = 35562306a36Sopenharmony_ci da9030_reg_to_mA(charger->adc.ichaverage_res) * 1000; 35662306a36Sopenharmony_ci break; 35762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_MODEL_NAME: 35862306a36Sopenharmony_ci val->strval = charger->battery_info->name; 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci default: 36162306a36Sopenharmony_ci break; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return 0; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic void da9030_battery_vbat_event(struct da9030_charger *charger) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci da9030_read_adc(charger, &charger->adc); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (charger->is_on) 37262306a36Sopenharmony_ci return; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (charger->adc.vbat_res < charger->thresholds.vbat_low) { 37562306a36Sopenharmony_ci /* set VBAT threshold for critical */ 37662306a36Sopenharmony_ci da903x_write(charger->master, DA9030_VBATMON, 37762306a36Sopenharmony_ci charger->thresholds.vbat_crit); 37862306a36Sopenharmony_ci if (charger->battery_low) 37962306a36Sopenharmony_ci charger->battery_low(); 38062306a36Sopenharmony_ci } else if (charger->adc.vbat_res < 38162306a36Sopenharmony_ci charger->thresholds.vbat_crit) { 38262306a36Sopenharmony_ci /* notify the system of battery critical */ 38362306a36Sopenharmony_ci if (charger->battery_critical) 38462306a36Sopenharmony_ci charger->battery_critical(); 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic int da9030_battery_event(struct notifier_block *nb, unsigned long event, 38962306a36Sopenharmony_ci void *data) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci struct da9030_charger *charger = 39262306a36Sopenharmony_ci container_of(nb, struct da9030_charger, nb); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci switch (event) { 39562306a36Sopenharmony_ci case DA9030_EVENT_CHDET: 39662306a36Sopenharmony_ci cancel_delayed_work_sync(&charger->work); 39762306a36Sopenharmony_ci schedule_work(&charger->work.work); 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci case DA9030_EVENT_VBATMON: 40062306a36Sopenharmony_ci da9030_battery_vbat_event(charger); 40162306a36Sopenharmony_ci break; 40262306a36Sopenharmony_ci case DA9030_EVENT_CHIOVER: 40362306a36Sopenharmony_ci case DA9030_EVENT_TBAT: 40462306a36Sopenharmony_ci da9030_set_charge(charger, 0); 40562306a36Sopenharmony_ci break; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic void da9030_battery_convert_thresholds(struct da9030_charger *charger, 41262306a36Sopenharmony_ci struct da9030_battery_info *pdata) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci charger->thresholds.tbat_low = pdata->tbat_low; 41562306a36Sopenharmony_ci charger->thresholds.tbat_high = pdata->tbat_high; 41662306a36Sopenharmony_ci charger->thresholds.tbat_restart = pdata->tbat_restart; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci charger->thresholds.vbat_low = 41962306a36Sopenharmony_ci da9030_millivolt_to_reg(pdata->vbat_low); 42062306a36Sopenharmony_ci charger->thresholds.vbat_crit = 42162306a36Sopenharmony_ci da9030_millivolt_to_reg(pdata->vbat_crit); 42262306a36Sopenharmony_ci charger->thresholds.vbat_charge_start = 42362306a36Sopenharmony_ci da9030_millivolt_to_reg(pdata->vbat_charge_start); 42462306a36Sopenharmony_ci charger->thresholds.vbat_charge_stop = 42562306a36Sopenharmony_ci da9030_millivolt_to_reg(pdata->vbat_charge_stop); 42662306a36Sopenharmony_ci charger->thresholds.vbat_charge_restart = 42762306a36Sopenharmony_ci da9030_millivolt_to_reg(pdata->vbat_charge_restart); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci charger->thresholds.vcharge_min = 43062306a36Sopenharmony_ci da9030_millivolt_to_reg(pdata->vcharge_min); 43162306a36Sopenharmony_ci charger->thresholds.vcharge_max = 43262306a36Sopenharmony_ci da9030_millivolt_to_reg(pdata->vcharge_max); 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic void da9030_battery_setup_psy(struct da9030_charger *charger) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct power_supply_desc *psy_desc = &charger->psy_desc; 43862306a36Sopenharmony_ci struct power_supply_info *info = charger->battery_info; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci psy_desc->name = info->name; 44162306a36Sopenharmony_ci psy_desc->use_for_apm = info->use_for_apm; 44262306a36Sopenharmony_ci psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; 44362306a36Sopenharmony_ci psy_desc->get_property = da9030_battery_get_property; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci psy_desc->properties = da9030_battery_props; 44662306a36Sopenharmony_ci psy_desc->num_properties = ARRAY_SIZE(da9030_battery_props); 44762306a36Sopenharmony_ci}; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic int da9030_battery_charger_init(struct da9030_charger *charger) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci char v[5]; 45262306a36Sopenharmony_ci int ret; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci v[0] = v[1] = charger->thresholds.vbat_low; 45562306a36Sopenharmony_ci v[2] = charger->thresholds.tbat_high; 45662306a36Sopenharmony_ci v[3] = charger->thresholds.tbat_restart; 45762306a36Sopenharmony_ci v[4] = charger->thresholds.tbat_low; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci ret = da903x_writes(charger->master, DA9030_VBATMON, 5, v); 46062306a36Sopenharmony_ci if (ret) 46162306a36Sopenharmony_ci return ret; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* 46462306a36Sopenharmony_ci * Enable reference voltage supply for ADC from the LDO_INTERNAL 46562306a36Sopenharmony_ci * regulator. Must be set before ADC measurements can be made. 46662306a36Sopenharmony_ci */ 46762306a36Sopenharmony_ci ret = da903x_write(charger->master, DA9030_ADC_MAN_CONTROL, 46862306a36Sopenharmony_ci DA9030_ADC_LDO_INT_ENABLE | 46962306a36Sopenharmony_ci DA9030_ADC_TBATREF_ENABLE); 47062306a36Sopenharmony_ci if (ret) 47162306a36Sopenharmony_ci return ret; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* enable auto ADC measuremnts */ 47462306a36Sopenharmony_ci return da903x_write(charger->master, DA9030_ADC_AUTO_CONTROL, 47562306a36Sopenharmony_ci DA9030_ADC_TBAT_ENABLE | DA9030_ADC_VBAT_IN_TXON | 47662306a36Sopenharmony_ci DA9030_ADC_VCH_ENABLE | DA9030_ADC_ICH_ENABLE | 47762306a36Sopenharmony_ci DA9030_ADC_VBAT_ENABLE | 47862306a36Sopenharmony_ci DA9030_ADC_AUTO_SLEEP_ENABLE); 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic int da9030_battery_probe(struct platform_device *pdev) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct da9030_charger *charger; 48462306a36Sopenharmony_ci struct power_supply_config psy_cfg = {}; 48562306a36Sopenharmony_ci struct da9030_battery_info *pdata = pdev->dev.platform_data; 48662306a36Sopenharmony_ci int ret; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (pdata == NULL) 48962306a36Sopenharmony_ci return -EINVAL; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (pdata->charge_milliamp >= 1500 || 49262306a36Sopenharmony_ci pdata->charge_millivolt < 4000 || 49362306a36Sopenharmony_ci pdata->charge_millivolt > 4350) 49462306a36Sopenharmony_ci return -EINVAL; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL); 49762306a36Sopenharmony_ci if (charger == NULL) 49862306a36Sopenharmony_ci return -ENOMEM; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci charger->master = pdev->dev.parent; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* 10 seconds between monitor runs unless platform defines other 50362306a36Sopenharmony_ci interval */ 50462306a36Sopenharmony_ci charger->interval = msecs_to_jiffies( 50562306a36Sopenharmony_ci (pdata->batmon_interval ? : 10) * 1000); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci charger->charge_milliamp = pdata->charge_milliamp; 50862306a36Sopenharmony_ci charger->charge_millivolt = pdata->charge_millivolt; 50962306a36Sopenharmony_ci charger->battery_info = pdata->battery_info; 51062306a36Sopenharmony_ci charger->battery_low = pdata->battery_low; 51162306a36Sopenharmony_ci charger->battery_critical = pdata->battery_critical; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci da9030_battery_convert_thresholds(charger, pdata); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci ret = da9030_battery_charger_init(charger); 51662306a36Sopenharmony_ci if (ret) 51762306a36Sopenharmony_ci goto err_charger_init; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci INIT_DELAYED_WORK(&charger->work, da9030_charging_monitor); 52062306a36Sopenharmony_ci schedule_delayed_work(&charger->work, charger->interval); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci charger->nb.notifier_call = da9030_battery_event; 52362306a36Sopenharmony_ci ret = da903x_register_notifier(charger->master, &charger->nb, 52462306a36Sopenharmony_ci DA9030_EVENT_CHDET | 52562306a36Sopenharmony_ci DA9030_EVENT_VBATMON | 52662306a36Sopenharmony_ci DA9030_EVENT_CHIOVER | 52762306a36Sopenharmony_ci DA9030_EVENT_TBAT); 52862306a36Sopenharmony_ci if (ret) 52962306a36Sopenharmony_ci goto err_notifier; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci da9030_battery_setup_psy(charger); 53262306a36Sopenharmony_ci psy_cfg.drv_data = charger; 53362306a36Sopenharmony_ci charger->psy = power_supply_register(&pdev->dev, &charger->psy_desc, 53462306a36Sopenharmony_ci &psy_cfg); 53562306a36Sopenharmony_ci if (IS_ERR(charger->psy)) { 53662306a36Sopenharmony_ci ret = PTR_ERR(charger->psy); 53762306a36Sopenharmony_ci goto err_ps_register; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci charger->debug_file = da9030_bat_create_debugfs(charger); 54162306a36Sopenharmony_ci platform_set_drvdata(pdev, charger); 54262306a36Sopenharmony_ci return 0; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cierr_ps_register: 54562306a36Sopenharmony_ci da903x_unregister_notifier(charger->master, &charger->nb, 54662306a36Sopenharmony_ci DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON | 54762306a36Sopenharmony_ci DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT); 54862306a36Sopenharmony_cierr_notifier: 54962306a36Sopenharmony_ci cancel_delayed_work(&charger->work); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cierr_charger_init: 55262306a36Sopenharmony_ci return ret; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic int da9030_battery_remove(struct platform_device *dev) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci struct da9030_charger *charger = platform_get_drvdata(dev); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci da9030_bat_remove_debugfs(charger); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci da903x_unregister_notifier(charger->master, &charger->nb, 56262306a36Sopenharmony_ci DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON | 56362306a36Sopenharmony_ci DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT); 56462306a36Sopenharmony_ci cancel_delayed_work_sync(&charger->work); 56562306a36Sopenharmony_ci da9030_set_charge(charger, 0); 56662306a36Sopenharmony_ci power_supply_unregister(charger->psy); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci return 0; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic struct platform_driver da903x_battery_driver = { 57262306a36Sopenharmony_ci .driver = { 57362306a36Sopenharmony_ci .name = "da903x-battery", 57462306a36Sopenharmony_ci }, 57562306a36Sopenharmony_ci .probe = da9030_battery_probe, 57662306a36Sopenharmony_ci .remove = da9030_battery_remove, 57762306a36Sopenharmony_ci}; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cimodule_platform_driver(da903x_battery_driver); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ciMODULE_DESCRIPTION("DA9030 battery charger driver"); 58262306a36Sopenharmony_ciMODULE_AUTHOR("Mike Rapoport, CompuLab"); 58362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 584