1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28c0984e5SSebastian Reichel /*
38c0984e5SSebastian Reichel * Battery charger driver for Dialog Semiconductor DA9030
48c0984e5SSebastian Reichel *
58c0984e5SSebastian Reichel * Copyright (C) 2008 Compulab, Ltd.
68c0984e5SSebastian Reichel * Mike Rapoport <mike@compulab.co.il>
78c0984e5SSebastian Reichel */
88c0984e5SSebastian Reichel
98c0984e5SSebastian Reichel #include <linux/kernel.h>
108c0984e5SSebastian Reichel #include <linux/slab.h>
118c0984e5SSebastian Reichel #include <linux/init.h>
128c0984e5SSebastian Reichel #include <linux/types.h>
138c0984e5SSebastian Reichel #include <linux/device.h>
148c0984e5SSebastian Reichel #include <linux/workqueue.h>
158c0984e5SSebastian Reichel #include <linux/module.h>
168c0984e5SSebastian Reichel #include <linux/platform_device.h>
178c0984e5SSebastian Reichel #include <linux/power_supply.h>
188c0984e5SSebastian Reichel #include <linux/mfd/da903x.h>
198c0984e5SSebastian Reichel
208c0984e5SSebastian Reichel #include <linux/debugfs.h>
218c0984e5SSebastian Reichel #include <linux/seq_file.h>
228c0984e5SSebastian Reichel #include <linux/notifier.h>
238c0984e5SSebastian Reichel
248c0984e5SSebastian Reichel #define DA9030_FAULT_LOG 0x0a
258c0984e5SSebastian Reichel #define DA9030_FAULT_LOG_OVER_TEMP (1 << 7)
268c0984e5SSebastian Reichel #define DA9030_FAULT_LOG_VBAT_OVER (1 << 4)
278c0984e5SSebastian Reichel
288c0984e5SSebastian Reichel #define DA9030_CHARGE_CONTROL 0x28
298c0984e5SSebastian Reichel #define DA9030_CHRG_CHARGER_ENABLE (1 << 7)
308c0984e5SSebastian Reichel
318c0984e5SSebastian Reichel #define DA9030_ADC_MAN_CONTROL 0x30
328c0984e5SSebastian Reichel #define DA9030_ADC_TBATREF_ENABLE (1 << 5)
338c0984e5SSebastian Reichel #define DA9030_ADC_LDO_INT_ENABLE (1 << 4)
348c0984e5SSebastian Reichel
358c0984e5SSebastian Reichel #define DA9030_ADC_AUTO_CONTROL 0x31
368c0984e5SSebastian Reichel #define DA9030_ADC_TBAT_ENABLE (1 << 5)
378c0984e5SSebastian Reichel #define DA9030_ADC_VBAT_IN_TXON (1 << 4)
388c0984e5SSebastian Reichel #define DA9030_ADC_VCH_ENABLE (1 << 3)
398c0984e5SSebastian Reichel #define DA9030_ADC_ICH_ENABLE (1 << 2)
408c0984e5SSebastian Reichel #define DA9030_ADC_VBAT_ENABLE (1 << 1)
418c0984e5SSebastian Reichel #define DA9030_ADC_AUTO_SLEEP_ENABLE (1 << 0)
428c0984e5SSebastian Reichel
438c0984e5SSebastian Reichel #define DA9030_VBATMON 0x32
448c0984e5SSebastian Reichel #define DA9030_VBATMONTXON 0x33
458c0984e5SSebastian Reichel #define DA9030_TBATHIGHP 0x34
468c0984e5SSebastian Reichel #define DA9030_TBATHIGHN 0x35
478c0984e5SSebastian Reichel #define DA9030_TBATLOW 0x36
488c0984e5SSebastian Reichel
498c0984e5SSebastian Reichel #define DA9030_VBAT_RES 0x41
508c0984e5SSebastian Reichel #define DA9030_VBATMIN_RES 0x42
518c0984e5SSebastian Reichel #define DA9030_VBATMINTXON_RES 0x43
528c0984e5SSebastian Reichel #define DA9030_ICHMAX_RES 0x44
538c0984e5SSebastian Reichel #define DA9030_ICHMIN_RES 0x45
548c0984e5SSebastian Reichel #define DA9030_ICHAVERAGE_RES 0x46
558c0984e5SSebastian Reichel #define DA9030_VCHMAX_RES 0x47
568c0984e5SSebastian Reichel #define DA9030_VCHMIN_RES 0x48
578c0984e5SSebastian Reichel #define DA9030_TBAT_RES 0x49
588c0984e5SSebastian Reichel
598c0984e5SSebastian Reichel struct da9030_adc_res {
608c0984e5SSebastian Reichel uint8_t vbat_res;
618c0984e5SSebastian Reichel uint8_t vbatmin_res;
628c0984e5SSebastian Reichel uint8_t vbatmintxon;
638c0984e5SSebastian Reichel uint8_t ichmax_res;
648c0984e5SSebastian Reichel uint8_t ichmin_res;
658c0984e5SSebastian Reichel uint8_t ichaverage_res;
668c0984e5SSebastian Reichel uint8_t vchmax_res;
678c0984e5SSebastian Reichel uint8_t vchmin_res;
688c0984e5SSebastian Reichel uint8_t tbat_res;
698c0984e5SSebastian Reichel uint8_t adc_in4_res;
708c0984e5SSebastian Reichel uint8_t adc_in5_res;
718c0984e5SSebastian Reichel };
728c0984e5SSebastian Reichel
738c0984e5SSebastian Reichel struct da9030_battery_thresholds {
748c0984e5SSebastian Reichel int tbat_low;
758c0984e5SSebastian Reichel int tbat_high;
768c0984e5SSebastian Reichel int tbat_restart;
778c0984e5SSebastian Reichel
788c0984e5SSebastian Reichel int vbat_low;
798c0984e5SSebastian Reichel int vbat_crit;
808c0984e5SSebastian Reichel int vbat_charge_start;
818c0984e5SSebastian Reichel int vbat_charge_stop;
828c0984e5SSebastian Reichel int vbat_charge_restart;
838c0984e5SSebastian Reichel
848c0984e5SSebastian Reichel int vcharge_min;
858c0984e5SSebastian Reichel int vcharge_max;
868c0984e5SSebastian Reichel };
878c0984e5SSebastian Reichel
888c0984e5SSebastian Reichel struct da9030_charger {
898c0984e5SSebastian Reichel struct power_supply *psy;
908c0984e5SSebastian Reichel struct power_supply_desc psy_desc;
918c0984e5SSebastian Reichel
928c0984e5SSebastian Reichel struct device *master;
938c0984e5SSebastian Reichel
948c0984e5SSebastian Reichel struct da9030_adc_res adc;
958c0984e5SSebastian Reichel struct delayed_work work;
968c0984e5SSebastian Reichel unsigned int interval;
978c0984e5SSebastian Reichel
988c0984e5SSebastian Reichel struct power_supply_info *battery_info;
998c0984e5SSebastian Reichel
1008c0984e5SSebastian Reichel struct da9030_battery_thresholds thresholds;
1018c0984e5SSebastian Reichel
1028c0984e5SSebastian Reichel unsigned int charge_milliamp;
1038c0984e5SSebastian Reichel unsigned int charge_millivolt;
1048c0984e5SSebastian Reichel
1058c0984e5SSebastian Reichel /* charger status */
1068c0984e5SSebastian Reichel bool chdet;
1078c0984e5SSebastian Reichel uint8_t fault;
1088c0984e5SSebastian Reichel int mA;
1098c0984e5SSebastian Reichel int mV;
1108c0984e5SSebastian Reichel bool is_on;
1118c0984e5SSebastian Reichel
1128c0984e5SSebastian Reichel struct notifier_block nb;
1138c0984e5SSebastian Reichel
1148c0984e5SSebastian Reichel /* platform callbacks for battery low and critical events */
1158c0984e5SSebastian Reichel void (*battery_low)(void);
1168c0984e5SSebastian Reichel void (*battery_critical)(void);
1178c0984e5SSebastian Reichel
1188c0984e5SSebastian Reichel struct dentry *debug_file;
1198c0984e5SSebastian Reichel };
1208c0984e5SSebastian Reichel
da9030_reg_to_mV(int reg)1218c0984e5SSebastian Reichel static inline int da9030_reg_to_mV(int reg)
1228c0984e5SSebastian Reichel {
1238c0984e5SSebastian Reichel return ((reg * 2650) >> 8) + 2650;
1248c0984e5SSebastian Reichel }
1258c0984e5SSebastian Reichel
da9030_millivolt_to_reg(int mV)1268c0984e5SSebastian Reichel static inline int da9030_millivolt_to_reg(int mV)
1278c0984e5SSebastian Reichel {
1288c0984e5SSebastian Reichel return ((mV - 2650) << 8) / 2650;
1298c0984e5SSebastian Reichel }
1308c0984e5SSebastian Reichel
da9030_reg_to_mA(int reg)1318c0984e5SSebastian Reichel static inline int da9030_reg_to_mA(int reg)
1328c0984e5SSebastian Reichel {
1338c0984e5SSebastian Reichel return ((reg * 24000) >> 8) / 15;
1348c0984e5SSebastian Reichel }
1358c0984e5SSebastian Reichel
1368c0984e5SSebastian Reichel #ifdef CONFIG_DEBUG_FS
bat_debug_show(struct seq_file * s,void * data)1378c0984e5SSebastian Reichel static int bat_debug_show(struct seq_file *s, void *data)
1388c0984e5SSebastian Reichel {
1398c0984e5SSebastian Reichel struct da9030_charger *charger = s->private;
1408c0984e5SSebastian Reichel
1418c0984e5SSebastian Reichel seq_printf(s, "charger is %s\n", charger->is_on ? "on" : "off");
1428c0984e5SSebastian Reichel if (charger->chdet) {
1438c0984e5SSebastian Reichel seq_printf(s, "iset = %dmA, vset = %dmV\n",
1448c0984e5SSebastian Reichel charger->mA, charger->mV);
1458c0984e5SSebastian Reichel }
1468c0984e5SSebastian Reichel
1478c0984e5SSebastian Reichel seq_printf(s, "vbat_res = %d (%dmV)\n",
1488c0984e5SSebastian Reichel charger->adc.vbat_res,
1498c0984e5SSebastian Reichel da9030_reg_to_mV(charger->adc.vbat_res));
1508c0984e5SSebastian Reichel seq_printf(s, "vbatmin_res = %d (%dmV)\n",
1518c0984e5SSebastian Reichel charger->adc.vbatmin_res,
1528c0984e5SSebastian Reichel da9030_reg_to_mV(charger->adc.vbatmin_res));
1538c0984e5SSebastian Reichel seq_printf(s, "vbatmintxon = %d (%dmV)\n",
1548c0984e5SSebastian Reichel charger->adc.vbatmintxon,
1558c0984e5SSebastian Reichel da9030_reg_to_mV(charger->adc.vbatmintxon));
1568c0984e5SSebastian Reichel seq_printf(s, "ichmax_res = %d (%dmA)\n",
1578c0984e5SSebastian Reichel charger->adc.ichmax_res,
1588c0984e5SSebastian Reichel da9030_reg_to_mV(charger->adc.ichmax_res));
1598c0984e5SSebastian Reichel seq_printf(s, "ichmin_res = %d (%dmA)\n",
1608c0984e5SSebastian Reichel charger->adc.ichmin_res,
1618c0984e5SSebastian Reichel da9030_reg_to_mA(charger->adc.ichmin_res));
1628c0984e5SSebastian Reichel seq_printf(s, "ichaverage_res = %d (%dmA)\n",
1638c0984e5SSebastian Reichel charger->adc.ichaverage_res,
1648c0984e5SSebastian Reichel da9030_reg_to_mA(charger->adc.ichaverage_res));
1658c0984e5SSebastian Reichel seq_printf(s, "vchmax_res = %d (%dmV)\n",
1668c0984e5SSebastian Reichel charger->adc.vchmax_res,
1678c0984e5SSebastian Reichel da9030_reg_to_mA(charger->adc.vchmax_res));
1688c0984e5SSebastian Reichel seq_printf(s, "vchmin_res = %d (%dmV)\n",
1698c0984e5SSebastian Reichel charger->adc.vchmin_res,
1708c0984e5SSebastian Reichel da9030_reg_to_mV(charger->adc.vchmin_res));
1718c0984e5SSebastian Reichel
1728c0984e5SSebastian Reichel return 0;
1738c0984e5SSebastian Reichel }
1748c0984e5SSebastian Reichel
1759d832cd3SYongqiang Liu DEFINE_SHOW_ATTRIBUTE(bat_debug);
1768c0984e5SSebastian Reichel
da9030_bat_create_debugfs(struct da9030_charger * charger)1778c0984e5SSebastian Reichel static struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger)
1788c0984e5SSebastian Reichel {
1798c0984e5SSebastian Reichel charger->debug_file = debugfs_create_file("charger", 0666, NULL,
1808c0984e5SSebastian Reichel charger, &bat_debug_fops);
1818c0984e5SSebastian Reichel return charger->debug_file;
1828c0984e5SSebastian Reichel }
1838c0984e5SSebastian Reichel
da9030_bat_remove_debugfs(struct da9030_charger * charger)1848c0984e5SSebastian Reichel static void da9030_bat_remove_debugfs(struct da9030_charger *charger)
1858c0984e5SSebastian Reichel {
1868c0984e5SSebastian Reichel debugfs_remove(charger->debug_file);
1878c0984e5SSebastian Reichel }
1888c0984e5SSebastian Reichel #else
da9030_bat_create_debugfs(struct da9030_charger * charger)1898c0984e5SSebastian Reichel static inline struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger)
1908c0984e5SSebastian Reichel {
1918c0984e5SSebastian Reichel return NULL;
1928c0984e5SSebastian Reichel }
da9030_bat_remove_debugfs(struct da9030_charger * charger)1938c0984e5SSebastian Reichel static inline void da9030_bat_remove_debugfs(struct da9030_charger *charger)
1948c0984e5SSebastian Reichel {
1958c0984e5SSebastian Reichel }
1968c0984e5SSebastian Reichel #endif
1978c0984e5SSebastian Reichel
da9030_read_adc(struct da9030_charger * charger,struct da9030_adc_res * adc)1988c0984e5SSebastian Reichel static inline void da9030_read_adc(struct da9030_charger *charger,
1998c0984e5SSebastian Reichel struct da9030_adc_res *adc)
2008c0984e5SSebastian Reichel {
2018c0984e5SSebastian Reichel da903x_reads(charger->master, DA9030_VBAT_RES,
2028c0984e5SSebastian Reichel sizeof(*adc), (uint8_t *)adc);
2038c0984e5SSebastian Reichel }
2048c0984e5SSebastian Reichel
da9030_charger_update_state(struct da9030_charger * charger)2058c0984e5SSebastian Reichel static void da9030_charger_update_state(struct da9030_charger *charger)
2068c0984e5SSebastian Reichel {
2078c0984e5SSebastian Reichel uint8_t val;
2088c0984e5SSebastian Reichel
2098c0984e5SSebastian Reichel da903x_read(charger->master, DA9030_CHARGE_CONTROL, &val);
2108c0984e5SSebastian Reichel charger->is_on = (val & DA9030_CHRG_CHARGER_ENABLE) ? 1 : 0;
2118c0984e5SSebastian Reichel charger->mA = ((val >> 3) & 0xf) * 100;
2128c0984e5SSebastian Reichel charger->mV = (val & 0x7) * 50 + 4000;
2138c0984e5SSebastian Reichel
2148c0984e5SSebastian Reichel da9030_read_adc(charger, &charger->adc);
2158c0984e5SSebastian Reichel da903x_read(charger->master, DA9030_FAULT_LOG, &charger->fault);
2168c0984e5SSebastian Reichel charger->chdet = da903x_query_status(charger->master,
2178c0984e5SSebastian Reichel DA9030_STATUS_CHDET);
2188c0984e5SSebastian Reichel }
2198c0984e5SSebastian Reichel
da9030_set_charge(struct da9030_charger * charger,int on)2208c0984e5SSebastian Reichel static void da9030_set_charge(struct da9030_charger *charger, int on)
2218c0984e5SSebastian Reichel {
2228c0984e5SSebastian Reichel uint8_t val;
2238c0984e5SSebastian Reichel
2248c0984e5SSebastian Reichel if (on) {
2258c0984e5SSebastian Reichel val = DA9030_CHRG_CHARGER_ENABLE;
2268c0984e5SSebastian Reichel val |= (charger->charge_milliamp / 100) << 3;
2278c0984e5SSebastian Reichel val |= (charger->charge_millivolt - 4000) / 50;
2288c0984e5SSebastian Reichel charger->is_on = 1;
2298c0984e5SSebastian Reichel } else {
2308c0984e5SSebastian Reichel val = 0;
2318c0984e5SSebastian Reichel charger->is_on = 0;
2328c0984e5SSebastian Reichel }
2338c0984e5SSebastian Reichel
2348c0984e5SSebastian Reichel da903x_write(charger->master, DA9030_CHARGE_CONTROL, val);
2358c0984e5SSebastian Reichel
2368c0984e5SSebastian Reichel power_supply_changed(charger->psy);
2378c0984e5SSebastian Reichel }
2388c0984e5SSebastian Reichel
da9030_charger_check_state(struct da9030_charger * charger)2398c0984e5SSebastian Reichel static void da9030_charger_check_state(struct da9030_charger *charger)
2408c0984e5SSebastian Reichel {
2418c0984e5SSebastian Reichel da9030_charger_update_state(charger);
2428c0984e5SSebastian Reichel
2438c0984e5SSebastian Reichel /* we wake or boot with external power on */
2448c0984e5SSebastian Reichel if (!charger->is_on) {
2458c0984e5SSebastian Reichel if ((charger->chdet) &&
2468c0984e5SSebastian Reichel (charger->adc.vbat_res <
2478c0984e5SSebastian Reichel charger->thresholds.vbat_charge_start)) {
2488c0984e5SSebastian Reichel da9030_set_charge(charger, 1);
2498c0984e5SSebastian Reichel }
2508c0984e5SSebastian Reichel } else {
2518c0984e5SSebastian Reichel /* Charger has been pulled out */
2528c0984e5SSebastian Reichel if (!charger->chdet) {
2538c0984e5SSebastian Reichel da9030_set_charge(charger, 0);
2548c0984e5SSebastian Reichel return;
2558c0984e5SSebastian Reichel }
2568c0984e5SSebastian Reichel
2578c0984e5SSebastian Reichel if (charger->adc.vbat_res >=
2588c0984e5SSebastian Reichel charger->thresholds.vbat_charge_stop) {
2598c0984e5SSebastian Reichel da9030_set_charge(charger, 0);
2608c0984e5SSebastian Reichel da903x_write(charger->master, DA9030_VBATMON,
2618c0984e5SSebastian Reichel charger->thresholds.vbat_charge_restart);
2628c0984e5SSebastian Reichel } else if (charger->adc.vbat_res >
2638c0984e5SSebastian Reichel charger->thresholds.vbat_low) {
2648c0984e5SSebastian Reichel /* we are charging and passed LOW_THRESH,
2658c0984e5SSebastian Reichel so upate DA9030 VBAT threshold
2668c0984e5SSebastian Reichel */
2678c0984e5SSebastian Reichel da903x_write(charger->master, DA9030_VBATMON,
2688c0984e5SSebastian Reichel charger->thresholds.vbat_low);
2698c0984e5SSebastian Reichel }
2708c0984e5SSebastian Reichel if (charger->adc.vchmax_res > charger->thresholds.vcharge_max ||
2718c0984e5SSebastian Reichel charger->adc.vchmin_res < charger->thresholds.vcharge_min ||
2728c0984e5SSebastian Reichel /* Tempreture readings are negative */
2738c0984e5SSebastian Reichel charger->adc.tbat_res < charger->thresholds.tbat_high ||
2748c0984e5SSebastian Reichel charger->adc.tbat_res > charger->thresholds.tbat_low) {
2758c0984e5SSebastian Reichel /* disable charger */
2768c0984e5SSebastian Reichel da9030_set_charge(charger, 0);
2778c0984e5SSebastian Reichel }
2788c0984e5SSebastian Reichel }
2798c0984e5SSebastian Reichel }
2808c0984e5SSebastian Reichel
da9030_charging_monitor(struct work_struct * work)2818c0984e5SSebastian Reichel static void da9030_charging_monitor(struct work_struct *work)
2828c0984e5SSebastian Reichel {
2838c0984e5SSebastian Reichel struct da9030_charger *charger;
2848c0984e5SSebastian Reichel
2858c0984e5SSebastian Reichel charger = container_of(work, struct da9030_charger, work.work);
2868c0984e5SSebastian Reichel
2878c0984e5SSebastian Reichel da9030_charger_check_state(charger);
2888c0984e5SSebastian Reichel
2898c0984e5SSebastian Reichel /* reschedule for the next time */
2908c0984e5SSebastian Reichel schedule_delayed_work(&charger->work, charger->interval);
2918c0984e5SSebastian Reichel }
2928c0984e5SSebastian Reichel
2938c0984e5SSebastian Reichel static enum power_supply_property da9030_battery_props[] = {
2948c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MODEL_NAME,
2958c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS,
2968c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH,
2978c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY,
2988c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
2998c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
3008c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW,
3018c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_AVG,
3028c0984e5SSebastian Reichel };
3038c0984e5SSebastian Reichel
da9030_battery_check_status(struct da9030_charger * charger,union power_supply_propval * val)3048c0984e5SSebastian Reichel static void da9030_battery_check_status(struct da9030_charger *charger,
3058c0984e5SSebastian Reichel union power_supply_propval *val)
3068c0984e5SSebastian Reichel {
3078c0984e5SSebastian Reichel if (charger->chdet) {
3088c0984e5SSebastian Reichel if (charger->is_on)
3098c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_CHARGING;
3108c0984e5SSebastian Reichel else
3118c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
3128c0984e5SSebastian Reichel } else {
3138c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
3148c0984e5SSebastian Reichel }
3158c0984e5SSebastian Reichel }
3168c0984e5SSebastian Reichel
da9030_battery_check_health(struct da9030_charger * charger,union power_supply_propval * val)3178c0984e5SSebastian Reichel static void da9030_battery_check_health(struct da9030_charger *charger,
3188c0984e5SSebastian Reichel union power_supply_propval *val)
3198c0984e5SSebastian Reichel {
3208c0984e5SSebastian Reichel if (charger->fault & DA9030_FAULT_LOG_OVER_TEMP)
3218c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
3228c0984e5SSebastian Reichel else if (charger->fault & DA9030_FAULT_LOG_VBAT_OVER)
3238c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
3248c0984e5SSebastian Reichel else
3258c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_GOOD;
3268c0984e5SSebastian Reichel }
3278c0984e5SSebastian Reichel
da9030_battery_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)3288c0984e5SSebastian Reichel static int da9030_battery_get_property(struct power_supply *psy,
3298c0984e5SSebastian Reichel enum power_supply_property psp,
3308c0984e5SSebastian Reichel union power_supply_propval *val)
3318c0984e5SSebastian Reichel {
3328c0984e5SSebastian Reichel struct da9030_charger *charger = power_supply_get_drvdata(psy);
3338c0984e5SSebastian Reichel
3348c0984e5SSebastian Reichel switch (psp) {
3358c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_STATUS:
3368c0984e5SSebastian Reichel da9030_battery_check_status(charger, val);
3378c0984e5SSebastian Reichel break;
3388c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_HEALTH:
3398c0984e5SSebastian Reichel da9030_battery_check_health(charger, val);
3408c0984e5SSebastian Reichel break;
3418c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TECHNOLOGY:
3428c0984e5SSebastian Reichel val->intval = charger->battery_info->technology;
3438c0984e5SSebastian Reichel break;
3448c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
3458c0984e5SSebastian Reichel val->intval = charger->battery_info->voltage_max_design;
3468c0984e5SSebastian Reichel break;
3478c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
3488c0984e5SSebastian Reichel val->intval = charger->battery_info->voltage_min_design;
3498c0984e5SSebastian Reichel break;
3508c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_VOLTAGE_NOW:
3518c0984e5SSebastian Reichel val->intval = da9030_reg_to_mV(charger->adc.vbat_res) * 1000;
3528c0984e5SSebastian Reichel break;
3538c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CURRENT_AVG:
3548c0984e5SSebastian Reichel val->intval =
3558c0984e5SSebastian Reichel da9030_reg_to_mA(charger->adc.ichaverage_res) * 1000;
3568c0984e5SSebastian Reichel break;
3578c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_MODEL_NAME:
3588c0984e5SSebastian Reichel val->strval = charger->battery_info->name;
3598c0984e5SSebastian Reichel break;
3608c0984e5SSebastian Reichel default:
3618c0984e5SSebastian Reichel break;
3628c0984e5SSebastian Reichel }
3638c0984e5SSebastian Reichel
3648c0984e5SSebastian Reichel return 0;
3658c0984e5SSebastian Reichel }
3668c0984e5SSebastian Reichel
da9030_battery_vbat_event(struct da9030_charger * charger)3678c0984e5SSebastian Reichel static void da9030_battery_vbat_event(struct da9030_charger *charger)
3688c0984e5SSebastian Reichel {
3698c0984e5SSebastian Reichel da9030_read_adc(charger, &charger->adc);
3708c0984e5SSebastian Reichel
3718c0984e5SSebastian Reichel if (charger->is_on)
3728c0984e5SSebastian Reichel return;
3738c0984e5SSebastian Reichel
3748c0984e5SSebastian Reichel if (charger->adc.vbat_res < charger->thresholds.vbat_low) {
3758c0984e5SSebastian Reichel /* set VBAT threshold for critical */
3768c0984e5SSebastian Reichel da903x_write(charger->master, DA9030_VBATMON,
3778c0984e5SSebastian Reichel charger->thresholds.vbat_crit);
3788c0984e5SSebastian Reichel if (charger->battery_low)
3798c0984e5SSebastian Reichel charger->battery_low();
3808c0984e5SSebastian Reichel } else if (charger->adc.vbat_res <
3818c0984e5SSebastian Reichel charger->thresholds.vbat_crit) {
3828c0984e5SSebastian Reichel /* notify the system of battery critical */
3838c0984e5SSebastian Reichel if (charger->battery_critical)
3848c0984e5SSebastian Reichel charger->battery_critical();
3858c0984e5SSebastian Reichel }
3868c0984e5SSebastian Reichel }
3878c0984e5SSebastian Reichel
da9030_battery_event(struct notifier_block * nb,unsigned long event,void * data)3888c0984e5SSebastian Reichel static int da9030_battery_event(struct notifier_block *nb, unsigned long event,
3898c0984e5SSebastian Reichel void *data)
3908c0984e5SSebastian Reichel {
3918c0984e5SSebastian Reichel struct da9030_charger *charger =
3928c0984e5SSebastian Reichel container_of(nb, struct da9030_charger, nb);
3938c0984e5SSebastian Reichel
3948c0984e5SSebastian Reichel switch (event) {
3958c0984e5SSebastian Reichel case DA9030_EVENT_CHDET:
3968c0984e5SSebastian Reichel cancel_delayed_work_sync(&charger->work);
3978c0984e5SSebastian Reichel schedule_work(&charger->work.work);
3988c0984e5SSebastian Reichel break;
3998c0984e5SSebastian Reichel case DA9030_EVENT_VBATMON:
4008c0984e5SSebastian Reichel da9030_battery_vbat_event(charger);
4018c0984e5SSebastian Reichel break;
4028c0984e5SSebastian Reichel case DA9030_EVENT_CHIOVER:
4038c0984e5SSebastian Reichel case DA9030_EVENT_TBAT:
4048c0984e5SSebastian Reichel da9030_set_charge(charger, 0);
4058c0984e5SSebastian Reichel break;
4068c0984e5SSebastian Reichel }
4078c0984e5SSebastian Reichel
4088c0984e5SSebastian Reichel return 0;
4098c0984e5SSebastian Reichel }
4108c0984e5SSebastian Reichel
da9030_battery_convert_thresholds(struct da9030_charger * charger,struct da9030_battery_info * pdata)4118c0984e5SSebastian Reichel static void da9030_battery_convert_thresholds(struct da9030_charger *charger,
4128c0984e5SSebastian Reichel struct da9030_battery_info *pdata)
4138c0984e5SSebastian Reichel {
4148c0984e5SSebastian Reichel charger->thresholds.tbat_low = pdata->tbat_low;
4158c0984e5SSebastian Reichel charger->thresholds.tbat_high = pdata->tbat_high;
4168c0984e5SSebastian Reichel charger->thresholds.tbat_restart = pdata->tbat_restart;
4178c0984e5SSebastian Reichel
4188c0984e5SSebastian Reichel charger->thresholds.vbat_low =
4198c0984e5SSebastian Reichel da9030_millivolt_to_reg(pdata->vbat_low);
4208c0984e5SSebastian Reichel charger->thresholds.vbat_crit =
4218c0984e5SSebastian Reichel da9030_millivolt_to_reg(pdata->vbat_crit);
4228c0984e5SSebastian Reichel charger->thresholds.vbat_charge_start =
4238c0984e5SSebastian Reichel da9030_millivolt_to_reg(pdata->vbat_charge_start);
4248c0984e5SSebastian Reichel charger->thresholds.vbat_charge_stop =
4258c0984e5SSebastian Reichel da9030_millivolt_to_reg(pdata->vbat_charge_stop);
4268c0984e5SSebastian Reichel charger->thresholds.vbat_charge_restart =
4278c0984e5SSebastian Reichel da9030_millivolt_to_reg(pdata->vbat_charge_restart);
4288c0984e5SSebastian Reichel
4298c0984e5SSebastian Reichel charger->thresholds.vcharge_min =
4308c0984e5SSebastian Reichel da9030_millivolt_to_reg(pdata->vcharge_min);
4318c0984e5SSebastian Reichel charger->thresholds.vcharge_max =
4328c0984e5SSebastian Reichel da9030_millivolt_to_reg(pdata->vcharge_max);
4338c0984e5SSebastian Reichel }
4348c0984e5SSebastian Reichel
da9030_battery_setup_psy(struct da9030_charger * charger)4358c0984e5SSebastian Reichel static void da9030_battery_setup_psy(struct da9030_charger *charger)
4368c0984e5SSebastian Reichel {
4378c0984e5SSebastian Reichel struct power_supply_desc *psy_desc = &charger->psy_desc;
4388c0984e5SSebastian Reichel struct power_supply_info *info = charger->battery_info;
4398c0984e5SSebastian Reichel
4408c0984e5SSebastian Reichel psy_desc->name = info->name;
4418c0984e5SSebastian Reichel psy_desc->use_for_apm = info->use_for_apm;
4428c0984e5SSebastian Reichel psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
4438c0984e5SSebastian Reichel psy_desc->get_property = da9030_battery_get_property;
4448c0984e5SSebastian Reichel
4458c0984e5SSebastian Reichel psy_desc->properties = da9030_battery_props;
4468c0984e5SSebastian Reichel psy_desc->num_properties = ARRAY_SIZE(da9030_battery_props);
4478c0984e5SSebastian Reichel };
4488c0984e5SSebastian Reichel
da9030_battery_charger_init(struct da9030_charger * charger)4498c0984e5SSebastian Reichel static int da9030_battery_charger_init(struct da9030_charger *charger)
4508c0984e5SSebastian Reichel {
4518c0984e5SSebastian Reichel char v[5];
4528c0984e5SSebastian Reichel int ret;
4538c0984e5SSebastian Reichel
4548c0984e5SSebastian Reichel v[0] = v[1] = charger->thresholds.vbat_low;
4558c0984e5SSebastian Reichel v[2] = charger->thresholds.tbat_high;
4568c0984e5SSebastian Reichel v[3] = charger->thresholds.tbat_restart;
4578c0984e5SSebastian Reichel v[4] = charger->thresholds.tbat_low;
4588c0984e5SSebastian Reichel
4598c0984e5SSebastian Reichel ret = da903x_writes(charger->master, DA9030_VBATMON, 5, v);
4608c0984e5SSebastian Reichel if (ret)
4618c0984e5SSebastian Reichel return ret;
4628c0984e5SSebastian Reichel
4638c0984e5SSebastian Reichel /*
4648c0984e5SSebastian Reichel * Enable reference voltage supply for ADC from the LDO_INTERNAL
4658c0984e5SSebastian Reichel * regulator. Must be set before ADC measurements can be made.
4668c0984e5SSebastian Reichel */
4678c0984e5SSebastian Reichel ret = da903x_write(charger->master, DA9030_ADC_MAN_CONTROL,
4688c0984e5SSebastian Reichel DA9030_ADC_LDO_INT_ENABLE |
4698c0984e5SSebastian Reichel DA9030_ADC_TBATREF_ENABLE);
4708c0984e5SSebastian Reichel if (ret)
4718c0984e5SSebastian Reichel return ret;
4728c0984e5SSebastian Reichel
4738c0984e5SSebastian Reichel /* enable auto ADC measuremnts */
4748c0984e5SSebastian Reichel return da903x_write(charger->master, DA9030_ADC_AUTO_CONTROL,
4758c0984e5SSebastian Reichel DA9030_ADC_TBAT_ENABLE | DA9030_ADC_VBAT_IN_TXON |
4768c0984e5SSebastian Reichel DA9030_ADC_VCH_ENABLE | DA9030_ADC_ICH_ENABLE |
4778c0984e5SSebastian Reichel DA9030_ADC_VBAT_ENABLE |
4788c0984e5SSebastian Reichel DA9030_ADC_AUTO_SLEEP_ENABLE);
4798c0984e5SSebastian Reichel }
4808c0984e5SSebastian Reichel
da9030_battery_probe(struct platform_device * pdev)4818c0984e5SSebastian Reichel static int da9030_battery_probe(struct platform_device *pdev)
4828c0984e5SSebastian Reichel {
4838c0984e5SSebastian Reichel struct da9030_charger *charger;
4848c0984e5SSebastian Reichel struct power_supply_config psy_cfg = {};
4858c0984e5SSebastian Reichel struct da9030_battery_info *pdata = pdev->dev.platform_data;
4868c0984e5SSebastian Reichel int ret;
4878c0984e5SSebastian Reichel
4888c0984e5SSebastian Reichel if (pdata == NULL)
4898c0984e5SSebastian Reichel return -EINVAL;
4908c0984e5SSebastian Reichel
4918c0984e5SSebastian Reichel if (pdata->charge_milliamp >= 1500 ||
4928c0984e5SSebastian Reichel pdata->charge_millivolt < 4000 ||
4938c0984e5SSebastian Reichel pdata->charge_millivolt > 4350)
4948c0984e5SSebastian Reichel return -EINVAL;
4958c0984e5SSebastian Reichel
4968c0984e5SSebastian Reichel charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
4978c0984e5SSebastian Reichel if (charger == NULL)
4988c0984e5SSebastian Reichel return -ENOMEM;
4998c0984e5SSebastian Reichel
5008c0984e5SSebastian Reichel charger->master = pdev->dev.parent;
5018c0984e5SSebastian Reichel
5028c0984e5SSebastian Reichel /* 10 seconds between monitor runs unless platform defines other
5038c0984e5SSebastian Reichel interval */
5048c0984e5SSebastian Reichel charger->interval = msecs_to_jiffies(
5058c0984e5SSebastian Reichel (pdata->batmon_interval ? : 10) * 1000);
5068c0984e5SSebastian Reichel
5078c0984e5SSebastian Reichel charger->charge_milliamp = pdata->charge_milliamp;
5088c0984e5SSebastian Reichel charger->charge_millivolt = pdata->charge_millivolt;
5098c0984e5SSebastian Reichel charger->battery_info = pdata->battery_info;
5108c0984e5SSebastian Reichel charger->battery_low = pdata->battery_low;
5118c0984e5SSebastian Reichel charger->battery_critical = pdata->battery_critical;
5128c0984e5SSebastian Reichel
5138c0984e5SSebastian Reichel da9030_battery_convert_thresholds(charger, pdata);
5148c0984e5SSebastian Reichel
5158c0984e5SSebastian Reichel ret = da9030_battery_charger_init(charger);
5168c0984e5SSebastian Reichel if (ret)
5178c0984e5SSebastian Reichel goto err_charger_init;
5188c0984e5SSebastian Reichel
5198c0984e5SSebastian Reichel INIT_DELAYED_WORK(&charger->work, da9030_charging_monitor);
5208c0984e5SSebastian Reichel schedule_delayed_work(&charger->work, charger->interval);
5218c0984e5SSebastian Reichel
5228c0984e5SSebastian Reichel charger->nb.notifier_call = da9030_battery_event;
5238c0984e5SSebastian Reichel ret = da903x_register_notifier(charger->master, &charger->nb,
5248c0984e5SSebastian Reichel DA9030_EVENT_CHDET |
5258c0984e5SSebastian Reichel DA9030_EVENT_VBATMON |
5268c0984e5SSebastian Reichel DA9030_EVENT_CHIOVER |
5278c0984e5SSebastian Reichel DA9030_EVENT_TBAT);
5288c0984e5SSebastian Reichel if (ret)
5298c0984e5SSebastian Reichel goto err_notifier;
5308c0984e5SSebastian Reichel
5318c0984e5SSebastian Reichel da9030_battery_setup_psy(charger);
5328c0984e5SSebastian Reichel psy_cfg.drv_data = charger;
533*98be59bdSAndrew Davis charger->psy = devm_power_supply_register(&pdev->dev,
534*98be59bdSAndrew Davis &charger->psy_desc,
5358c0984e5SSebastian Reichel &psy_cfg);
5368c0984e5SSebastian Reichel if (IS_ERR(charger->psy)) {
5378c0984e5SSebastian Reichel ret = PTR_ERR(charger->psy);
5388c0984e5SSebastian Reichel goto err_ps_register;
5398c0984e5SSebastian Reichel }
5408c0984e5SSebastian Reichel
5418c0984e5SSebastian Reichel charger->debug_file = da9030_bat_create_debugfs(charger);
5428c0984e5SSebastian Reichel platform_set_drvdata(pdev, charger);
5438c0984e5SSebastian Reichel return 0;
5448c0984e5SSebastian Reichel
5458c0984e5SSebastian Reichel err_ps_register:
5468c0984e5SSebastian Reichel da903x_unregister_notifier(charger->master, &charger->nb,
5478c0984e5SSebastian Reichel DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON |
5488c0984e5SSebastian Reichel DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT);
5498c0984e5SSebastian Reichel err_notifier:
5508c0984e5SSebastian Reichel cancel_delayed_work(&charger->work);
5518c0984e5SSebastian Reichel
5528c0984e5SSebastian Reichel err_charger_init:
5538c0984e5SSebastian Reichel return ret;
5548c0984e5SSebastian Reichel }
5558c0984e5SSebastian Reichel
da9030_battery_remove(struct platform_device * dev)556b5ba26abSUwe Kleine-König static void da9030_battery_remove(struct platform_device *dev)
5578c0984e5SSebastian Reichel {
5588c0984e5SSebastian Reichel struct da9030_charger *charger = platform_get_drvdata(dev);
5598c0984e5SSebastian Reichel
5608c0984e5SSebastian Reichel da9030_bat_remove_debugfs(charger);
5618c0984e5SSebastian Reichel
5628c0984e5SSebastian Reichel da903x_unregister_notifier(charger->master, &charger->nb,
5638c0984e5SSebastian Reichel DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON |
5648c0984e5SSebastian Reichel DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT);
5658c0984e5SSebastian Reichel cancel_delayed_work_sync(&charger->work);
5668c0984e5SSebastian Reichel da9030_set_charge(charger, 0);
5678c0984e5SSebastian Reichel }
5688c0984e5SSebastian Reichel
5698c0984e5SSebastian Reichel static struct platform_driver da903x_battery_driver = {
5708c0984e5SSebastian Reichel .driver = {
5718c0984e5SSebastian Reichel .name = "da903x-battery",
5728c0984e5SSebastian Reichel },
5738c0984e5SSebastian Reichel .probe = da9030_battery_probe,
574b5ba26abSUwe Kleine-König .remove_new = da9030_battery_remove,
5758c0984e5SSebastian Reichel };
5768c0984e5SSebastian Reichel
5778c0984e5SSebastian Reichel module_platform_driver(da903x_battery_driver);
5788c0984e5SSebastian Reichel
5798c0984e5SSebastian Reichel MODULE_DESCRIPTION("DA9030 battery charger driver");
5808c0984e5SSebastian Reichel MODULE_AUTHOR("Mike Rapoport, CompuLab");
5818c0984e5SSebastian Reichel MODULE_LICENSE("GPL");
582