18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2012 ST Ericsson. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Power supply driver for ST Ericsson pm2xxx_charger charger 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 168c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 178c2ecf20Sopenharmony_ci#include <linux/err.h> 188c2ecf20Sopenharmony_ci#include <linux/i2c.h> 198c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 208c2ecf20Sopenharmony_ci#include <linux/mfd/abx500/ab8500.h> 218c2ecf20Sopenharmony_ci#include <linux/mfd/abx500/ab8500-bm.h> 228c2ecf20Sopenharmony_ci#include <linux/mfd/abx500/ux500_chargalg.h> 238c2ecf20Sopenharmony_ci#include <linux/pm2301_charger.h> 248c2ecf20Sopenharmony_ci#include <linux/gpio.h> 258c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 268c2ecf20Sopenharmony_ci#include <linux/pm.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "pm2301_charger.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define to_pm2xxx_charger_ac_device_info(x) container_of((x), \ 318c2ecf20Sopenharmony_ci struct pm2xxx_charger, ac_chg) 328c2ecf20Sopenharmony_ci#define SLEEP_MIN 50 338c2ecf20Sopenharmony_ci#define SLEEP_MAX 100 348c2ecf20Sopenharmony_ci#define PM2XXX_AUTOSUSPEND_DELAY 500 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int pm2xxx_interrupt_registers[] = { 378c2ecf20Sopenharmony_ci PM2XXX_REG_INT1, 388c2ecf20Sopenharmony_ci PM2XXX_REG_INT2, 398c2ecf20Sopenharmony_ci PM2XXX_REG_INT3, 408c2ecf20Sopenharmony_ci PM2XXX_REG_INT4, 418c2ecf20Sopenharmony_ci PM2XXX_REG_INT5, 428c2ecf20Sopenharmony_ci PM2XXX_REG_INT6, 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic enum power_supply_property pm2xxx_charger_ac_props[] = { 468c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 478c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 488c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 498c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_AVG, 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int pm2xxx_charger_voltage_map[] = { 538c2ecf20Sopenharmony_ci 3500, 548c2ecf20Sopenharmony_ci 3525, 558c2ecf20Sopenharmony_ci 3550, 568c2ecf20Sopenharmony_ci 3575, 578c2ecf20Sopenharmony_ci 3600, 588c2ecf20Sopenharmony_ci 3625, 598c2ecf20Sopenharmony_ci 3650, 608c2ecf20Sopenharmony_ci 3675, 618c2ecf20Sopenharmony_ci 3700, 628c2ecf20Sopenharmony_ci 3725, 638c2ecf20Sopenharmony_ci 3750, 648c2ecf20Sopenharmony_ci 3775, 658c2ecf20Sopenharmony_ci 3800, 668c2ecf20Sopenharmony_ci 3825, 678c2ecf20Sopenharmony_ci 3850, 688c2ecf20Sopenharmony_ci 3875, 698c2ecf20Sopenharmony_ci 3900, 708c2ecf20Sopenharmony_ci 3925, 718c2ecf20Sopenharmony_ci 3950, 728c2ecf20Sopenharmony_ci 3975, 738c2ecf20Sopenharmony_ci 4000, 748c2ecf20Sopenharmony_ci 4025, 758c2ecf20Sopenharmony_ci 4050, 768c2ecf20Sopenharmony_ci 4075, 778c2ecf20Sopenharmony_ci 4100, 788c2ecf20Sopenharmony_ci 4125, 798c2ecf20Sopenharmony_ci 4150, 808c2ecf20Sopenharmony_ci 4175, 818c2ecf20Sopenharmony_ci 4200, 828c2ecf20Sopenharmony_ci 4225, 838c2ecf20Sopenharmony_ci 4250, 848c2ecf20Sopenharmony_ci 4275, 858c2ecf20Sopenharmony_ci 4300, 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int pm2xxx_charger_current_map[] = { 898c2ecf20Sopenharmony_ci 200, 908c2ecf20Sopenharmony_ci 200, 918c2ecf20Sopenharmony_ci 400, 928c2ecf20Sopenharmony_ci 600, 938c2ecf20Sopenharmony_ci 800, 948c2ecf20Sopenharmony_ci 1000, 958c2ecf20Sopenharmony_ci 1200, 968c2ecf20Sopenharmony_ci 1400, 978c2ecf20Sopenharmony_ci 1600, 988c2ecf20Sopenharmony_ci 1800, 998c2ecf20Sopenharmony_ci 2000, 1008c2ecf20Sopenharmony_ci 2200, 1018c2ecf20Sopenharmony_ci 2400, 1028c2ecf20Sopenharmony_ci 2600, 1038c2ecf20Sopenharmony_ci 2800, 1048c2ecf20Sopenharmony_ci 3000, 1058c2ecf20Sopenharmony_ci}; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic void set_lpn_pin(struct pm2xxx_charger *pm2) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci if (!pm2->ac.charger_connected && gpio_is_valid(pm2->lpn_pin)) { 1108c2ecf20Sopenharmony_ci gpio_set_value(pm2->lpn_pin, 1); 1118c2ecf20Sopenharmony_ci usleep_range(SLEEP_MIN, SLEEP_MAX); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic void clear_lpn_pin(struct pm2xxx_charger *pm2) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci if (!pm2->ac.charger_connected && gpio_is_valid(pm2->lpn_pin)) 1188c2ecf20Sopenharmony_ci gpio_set_value(pm2->lpn_pin, 0); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci int ret; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* wake up the device */ 1268c2ecf20Sopenharmony_ci pm_runtime_get_sync(pm2->dev); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci ret = i2c_smbus_read_i2c_block_data(pm2->config.pm2xxx_i2c, reg, 1298c2ecf20Sopenharmony_ci 1, val); 1308c2ecf20Sopenharmony_ci if (ret < 0) 1318c2ecf20Sopenharmony_ci dev_err(pm2->dev, "Error reading register at 0x%x\n", reg); 1328c2ecf20Sopenharmony_ci else 1338c2ecf20Sopenharmony_ci ret = 0; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci pm_runtime_put_sync(pm2->dev); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return ret; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci int ret; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* wake up the device */ 1458c2ecf20Sopenharmony_ci pm_runtime_get_sync(pm2->dev); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci ret = i2c_smbus_write_i2c_block_data(pm2->config.pm2xxx_i2c, reg, 1488c2ecf20Sopenharmony_ci 1, &val); 1498c2ecf20Sopenharmony_ci if (ret < 0) 1508c2ecf20Sopenharmony_ci dev_err(pm2->dev, "Error writing register at 0x%x\n", reg); 1518c2ecf20Sopenharmony_ci else 1528c2ecf20Sopenharmony_ci ret = 0; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci pm_runtime_put_sync(pm2->dev); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return ret; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int pm2xxx_charging_enable_mngt(struct pm2xxx_charger *pm2) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci int ret; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* Enable charging */ 1648c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2, 1658c2ecf20Sopenharmony_ci (PM2XXX_CH_AUTO_RESUME_EN | PM2XXX_CHARGER_ENA)); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return ret; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic int pm2xxx_charging_disable_mngt(struct pm2xxx_charger *pm2) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci int ret; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* Disable SW EOC ctrl */ 1758c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG, PM2XXX_SWCTRL_HW); 1768c2ecf20Sopenharmony_ci if (ret < 0) { 1778c2ecf20Sopenharmony_ci dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); 1788c2ecf20Sopenharmony_ci return ret; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* Disable charging */ 1828c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2, 1838c2ecf20Sopenharmony_ci (PM2XXX_CH_AUTO_RESUME_DIS | PM2XXX_CHARGER_DIS)); 1848c2ecf20Sopenharmony_ci if (ret < 0) { 1858c2ecf20Sopenharmony_ci dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); 1868c2ecf20Sopenharmony_ci return ret; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int pm2xxx_charger_batt_therm_mngt(struct pm2xxx_charger *pm2, int val) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return 0; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic int pm2xxx_charger_die_therm_mngt(struct pm2xxx_charger *pm2, int val) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int pm2xxx_charger_ovv_mngt(struct pm2xxx_charger *pm2, int val) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci dev_err(pm2->dev, "Overvoltage detected\n"); 2108c2ecf20Sopenharmony_ci pm2->flags.ovv = true; 2118c2ecf20Sopenharmony_ci power_supply_changed(pm2->ac_chg.psy); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* Schedule a new HW failure check */ 2148c2ecf20Sopenharmony_ci queue_delayed_work(pm2->charger_wq, &pm2->check_hw_failure_work, 0); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int pm2xxx_charger_wd_exp_mngt(struct pm2xxx_charger *pm2, int val) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci dev_dbg(pm2->dev , "20 minutes watchdog expired\n"); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci pm2->ac.wd_expired = true; 2248c2ecf20Sopenharmony_ci power_supply_changed(pm2->ac_chg.psy); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int pm2xxx_charger_vbat_lsig_mngt(struct pm2xxx_charger *pm2, int val) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci int ret; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci switch (val) { 2348c2ecf20Sopenharmony_ci case PM2XXX_INT1_ITVBATLOWR: 2358c2ecf20Sopenharmony_ci dev_dbg(pm2->dev, "VBAT grows above VBAT_LOW level\n"); 2368c2ecf20Sopenharmony_ci /* Enable SW EOC ctrl */ 2378c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG, 2388c2ecf20Sopenharmony_ci PM2XXX_SWCTRL_SW); 2398c2ecf20Sopenharmony_ci if (ret < 0) { 2408c2ecf20Sopenharmony_ci dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); 2418c2ecf20Sopenharmony_ci return ret; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci break; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci case PM2XXX_INT1_ITVBATLOWF: 2468c2ecf20Sopenharmony_ci dev_dbg(pm2->dev, "VBAT drops below VBAT_LOW level\n"); 2478c2ecf20Sopenharmony_ci /* Disable SW EOC ctrl */ 2488c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG, 2498c2ecf20Sopenharmony_ci PM2XXX_SWCTRL_HW); 2508c2ecf20Sopenharmony_ci if (ret < 0) { 2518c2ecf20Sopenharmony_ci dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); 2528c2ecf20Sopenharmony_ci return ret; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci break; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci default: 2578c2ecf20Sopenharmony_ci dev_err(pm2->dev, "Unknown VBAT level\n"); 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic int pm2xxx_charger_bat_disc_mngt(struct pm2xxx_charger *pm2, int val) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci dev_dbg(pm2->dev, "battery disconnected\n"); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic int pm2xxx_charger_detection(struct pm2xxx_charger *pm2, u8 *val) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci int ret; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT2, val); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (ret < 0) { 2778c2ecf20Sopenharmony_ci dev_err(pm2->dev, "Charger detection failed\n"); 2788c2ecf20Sopenharmony_ci goto out; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci *val &= (PM2XXX_INT2_S_ITVPWR1PLUG | PM2XXX_INT2_S_ITVPWR2PLUG); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ciout: 2848c2ecf20Sopenharmony_ci return ret; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic int pm2xxx_charger_itv_pwr_plug_mngt(struct pm2xxx_charger *pm2, int val) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci int ret; 2918c2ecf20Sopenharmony_ci u8 read_val; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* 2948c2ecf20Sopenharmony_ci * Since we can't be sure that the events are received 2958c2ecf20Sopenharmony_ci * synchronously, we have the check if the main charger is 2968c2ecf20Sopenharmony_ci * connected by reading the interrupt source register. 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_ci ret = pm2xxx_charger_detection(pm2, &read_val); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if ((ret == 0) && read_val) { 3018c2ecf20Sopenharmony_ci pm2->ac.charger_connected = 1; 3028c2ecf20Sopenharmony_ci pm2->ac_conn = true; 3038c2ecf20Sopenharmony_ci queue_work(pm2->charger_wq, &pm2->ac_work); 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci return ret; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic int pm2xxx_charger_itv_pwr_unplug_mngt(struct pm2xxx_charger *pm2, 3118c2ecf20Sopenharmony_ci int val) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci pm2->ac.charger_connected = 0; 3148c2ecf20Sopenharmony_ci queue_work(pm2->charger_wq, &pm2->ac_work); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci return 0; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic int pm2_int_reg0(void *pm2_data, int val) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2 = pm2_data; 3228c2ecf20Sopenharmony_ci int ret = 0; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (val & PM2XXX_INT1_ITVBATLOWR) { 3258c2ecf20Sopenharmony_ci ret = pm2xxx_charger_vbat_lsig_mngt(pm2, 3268c2ecf20Sopenharmony_ci PM2XXX_INT1_ITVBATLOWR); 3278c2ecf20Sopenharmony_ci if (ret < 0) 3288c2ecf20Sopenharmony_ci goto out; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (val & PM2XXX_INT1_ITVBATLOWF) { 3328c2ecf20Sopenharmony_ci ret = pm2xxx_charger_vbat_lsig_mngt(pm2, 3338c2ecf20Sopenharmony_ci PM2XXX_INT1_ITVBATLOWF); 3348c2ecf20Sopenharmony_ci if (ret < 0) 3358c2ecf20Sopenharmony_ci goto out; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (val & PM2XXX_INT1_ITVBATDISCONNECT) { 3398c2ecf20Sopenharmony_ci ret = pm2xxx_charger_bat_disc_mngt(pm2, 3408c2ecf20Sopenharmony_ci PM2XXX_INT1_ITVBATDISCONNECT); 3418c2ecf20Sopenharmony_ci if (ret < 0) 3428c2ecf20Sopenharmony_ci goto out; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ciout: 3458c2ecf20Sopenharmony_ci return ret; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic int pm2_int_reg1(void *pm2_data, int val) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2 = pm2_data; 3518c2ecf20Sopenharmony_ci int ret = 0; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (val & (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG)) { 3548c2ecf20Sopenharmony_ci dev_dbg(pm2->dev , "Main charger plugged\n"); 3558c2ecf20Sopenharmony_ci ret = pm2xxx_charger_itv_pwr_plug_mngt(pm2, val & 3568c2ecf20Sopenharmony_ci (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG)); 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (val & 3608c2ecf20Sopenharmony_ci (PM2XXX_INT2_ITVPWR1UNPLUG | PM2XXX_INT2_ITVPWR2UNPLUG)) { 3618c2ecf20Sopenharmony_ci dev_dbg(pm2->dev , "Main charger unplugged\n"); 3628c2ecf20Sopenharmony_ci ret = pm2xxx_charger_itv_pwr_unplug_mngt(pm2, val & 3638c2ecf20Sopenharmony_ci (PM2XXX_INT2_ITVPWR1UNPLUG | 3648c2ecf20Sopenharmony_ci PM2XXX_INT2_ITVPWR2UNPLUG)); 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci return ret; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic int pm2_int_reg2(void *pm2_data, int val) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2 = pm2_data; 3738c2ecf20Sopenharmony_ci int ret = 0; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (val & PM2XXX_INT3_ITAUTOTIMEOUTWD) 3768c2ecf20Sopenharmony_ci ret = pm2xxx_charger_wd_exp_mngt(pm2, val); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (val & (PM2XXX_INT3_ITCHPRECHARGEWD | 3798c2ecf20Sopenharmony_ci PM2XXX_INT3_ITCHCCWD | PM2XXX_INT3_ITCHCVWD)) { 3808c2ecf20Sopenharmony_ci dev_dbg(pm2->dev, 3818c2ecf20Sopenharmony_ci "Watchdog occurred for precharge, CC and CV charge\n"); 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci return ret; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic int pm2_int_reg3(void *pm2_data, int val) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2 = pm2_data; 3908c2ecf20Sopenharmony_ci int ret = 0; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (val & (PM2XXX_INT4_ITCHARGINGON)) { 3938c2ecf20Sopenharmony_ci dev_dbg(pm2->dev , 3948c2ecf20Sopenharmony_ci "charging operation has started\n"); 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (val & (PM2XXX_INT4_ITVRESUME)) { 3988c2ecf20Sopenharmony_ci dev_dbg(pm2->dev, 3998c2ecf20Sopenharmony_ci "battery discharged down to VResume threshold\n"); 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (val & (PM2XXX_INT4_ITBATTFULL)) { 4038c2ecf20Sopenharmony_ci dev_dbg(pm2->dev , "battery fully detected\n"); 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (val & (PM2XXX_INT4_ITCVPHASE)) { 4078c2ecf20Sopenharmony_ci dev_dbg(pm2->dev, "CV phase enter with 0.5C charging\n"); 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (val & (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV)) { 4118c2ecf20Sopenharmony_ci pm2->failure_case = VPWR_OVV; 4128c2ecf20Sopenharmony_ci ret = pm2xxx_charger_ovv_mngt(pm2, val & 4138c2ecf20Sopenharmony_ci (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV)); 4148c2ecf20Sopenharmony_ci dev_dbg(pm2->dev, "VPWR/VSYSTEM overvoltage detected\n"); 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (val & (PM2XXX_INT4_S_ITBATTEMPCOLD | 4188c2ecf20Sopenharmony_ci PM2XXX_INT4_S_ITBATTEMPHOT)) { 4198c2ecf20Sopenharmony_ci ret = pm2xxx_charger_batt_therm_mngt(pm2, val & 4208c2ecf20Sopenharmony_ci (PM2XXX_INT4_S_ITBATTEMPCOLD | 4218c2ecf20Sopenharmony_ci PM2XXX_INT4_S_ITBATTEMPHOT)); 4228c2ecf20Sopenharmony_ci dev_dbg(pm2->dev, "BTEMP is too Low/High\n"); 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci return ret; 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic int pm2_int_reg4(void *pm2_data, int val) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2 = pm2_data; 4318c2ecf20Sopenharmony_ci int ret = 0; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (val & PM2XXX_INT5_ITVSYSTEMOVV) { 4348c2ecf20Sopenharmony_ci pm2->failure_case = VSYSTEM_OVV; 4358c2ecf20Sopenharmony_ci ret = pm2xxx_charger_ovv_mngt(pm2, val & 4368c2ecf20Sopenharmony_ci PM2XXX_INT5_ITVSYSTEMOVV); 4378c2ecf20Sopenharmony_ci dev_dbg(pm2->dev, "VSYSTEM overvoltage detected\n"); 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (val & (PM2XXX_INT5_ITTHERMALWARNINGFALL | 4418c2ecf20Sopenharmony_ci PM2XXX_INT5_ITTHERMALWARNINGRISE | 4428c2ecf20Sopenharmony_ci PM2XXX_INT5_ITTHERMALSHUTDOWNFALL | 4438c2ecf20Sopenharmony_ci PM2XXX_INT5_ITTHERMALSHUTDOWNRISE)) { 4448c2ecf20Sopenharmony_ci dev_dbg(pm2->dev, "BTEMP die temperature is too Low/High\n"); 4458c2ecf20Sopenharmony_ci ret = pm2xxx_charger_die_therm_mngt(pm2, val & 4468c2ecf20Sopenharmony_ci (PM2XXX_INT5_ITTHERMALWARNINGFALL | 4478c2ecf20Sopenharmony_ci PM2XXX_INT5_ITTHERMALWARNINGRISE | 4488c2ecf20Sopenharmony_ci PM2XXX_INT5_ITTHERMALSHUTDOWNFALL | 4498c2ecf20Sopenharmony_ci PM2XXX_INT5_ITTHERMALSHUTDOWNRISE)); 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci return ret; 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic int pm2_int_reg5(void *pm2_data, int val) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2 = pm2_data; 4588c2ecf20Sopenharmony_ci int ret = 0; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (val & (PM2XXX_INT6_ITVPWR2DROP | PM2XXX_INT6_ITVPWR1DROP)) { 4618c2ecf20Sopenharmony_ci dev_dbg(pm2->dev, "VMPWR drop to VBAT level\n"); 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (val & (PM2XXX_INT6_ITVPWR2VALIDRISE | 4658c2ecf20Sopenharmony_ci PM2XXX_INT6_ITVPWR1VALIDRISE | 4668c2ecf20Sopenharmony_ci PM2XXX_INT6_ITVPWR2VALIDFALL | 4678c2ecf20Sopenharmony_ci PM2XXX_INT6_ITVPWR1VALIDFALL)) { 4688c2ecf20Sopenharmony_ci dev_dbg(pm2->dev, "Falling/Rising edge on WPWR1/2\n"); 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci return ret; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic irqreturn_t pm2xxx_irq_int(int irq, void *data) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2 = data; 4778c2ecf20Sopenharmony_ci struct pm2xxx_interrupts *interrupt = pm2->pm2_int; 4788c2ecf20Sopenharmony_ci int i; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci /* wake up the device */ 4818c2ecf20Sopenharmony_ci pm_runtime_get_sync(pm2->dev); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci do { 4848c2ecf20Sopenharmony_ci for (i = 0; i < PM2XXX_NUM_INT_REG; i++) { 4858c2ecf20Sopenharmony_ci pm2xxx_reg_read(pm2, 4868c2ecf20Sopenharmony_ci pm2xxx_interrupt_registers[i], 4878c2ecf20Sopenharmony_ci &(interrupt->reg[i])); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (interrupt->reg[i] > 0) 4908c2ecf20Sopenharmony_ci interrupt->handler[i](pm2, interrupt->reg[i]); 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci } while (gpio_get_value(pm2->pdata->gpio_irq_number) == 0); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(pm2->dev); 4958c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(pm2->dev); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic int pm2xxx_charger_get_ac_cv(struct pm2xxx_charger *pm2) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci int ret = 0; 5038c2ecf20Sopenharmony_ci u8 val; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (pm2->ac.charger_connected && pm2->ac.charger_online) { 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT4, &val); 5088c2ecf20Sopenharmony_ci if (ret < 0) { 5098c2ecf20Sopenharmony_ci dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__); 5108c2ecf20Sopenharmony_ci goto out; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (val & PM2XXX_INT4_S_ITCVPHASE) 5148c2ecf20Sopenharmony_ci ret = PM2XXX_CONST_VOLT; 5158c2ecf20Sopenharmony_ci else 5168c2ecf20Sopenharmony_ci ret = PM2XXX_CONST_CURR; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ciout: 5198c2ecf20Sopenharmony_ci return ret; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic int pm2xxx_current_to_regval(int curr) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci int i; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci if (curr < pm2xxx_charger_current_map[0]) 5278c2ecf20Sopenharmony_ci return 0; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci for (i = 1; i < ARRAY_SIZE(pm2xxx_charger_current_map); i++) { 5308c2ecf20Sopenharmony_ci if (curr < pm2xxx_charger_current_map[i]) 5318c2ecf20Sopenharmony_ci return (i - 1); 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci i = ARRAY_SIZE(pm2xxx_charger_current_map) - 1; 5358c2ecf20Sopenharmony_ci if (curr == pm2xxx_charger_current_map[i]) 5368c2ecf20Sopenharmony_ci return i; 5378c2ecf20Sopenharmony_ci else 5388c2ecf20Sopenharmony_ci return -EINVAL; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic int pm2xxx_voltage_to_regval(int curr) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci int i; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (curr < pm2xxx_charger_voltage_map[0]) 5468c2ecf20Sopenharmony_ci return 0; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci for (i = 1; i < ARRAY_SIZE(pm2xxx_charger_voltage_map); i++) { 5498c2ecf20Sopenharmony_ci if (curr < pm2xxx_charger_voltage_map[i]) 5508c2ecf20Sopenharmony_ci return i - 1; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci i = ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1; 5548c2ecf20Sopenharmony_ci if (curr == pm2xxx_charger_voltage_map[i]) 5558c2ecf20Sopenharmony_ci return i; 5568c2ecf20Sopenharmony_ci else 5578c2ecf20Sopenharmony_ci return -EINVAL; 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistatic int pm2xxx_charger_update_charger_current(struct ux500_charger *charger, 5618c2ecf20Sopenharmony_ci int ich_out) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci int ret; 5648c2ecf20Sopenharmony_ci int curr_index; 5658c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2; 5668c2ecf20Sopenharmony_ci u8 val; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS) 5698c2ecf20Sopenharmony_ci pm2 = to_pm2xxx_charger_ac_device_info(charger); 5708c2ecf20Sopenharmony_ci else 5718c2ecf20Sopenharmony_ci return -ENXIO; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci curr_index = pm2xxx_current_to_regval(ich_out); 5748c2ecf20Sopenharmony_ci if (curr_index < 0) { 5758c2ecf20Sopenharmony_ci dev_err(pm2->dev, 5768c2ecf20Sopenharmony_ci "Charger current too high, charging not started\n"); 5778c2ecf20Sopenharmony_ci return -ENXIO; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6, &val); 5818c2ecf20Sopenharmony_ci if (ret >= 0) { 5828c2ecf20Sopenharmony_ci val &= ~PM2XXX_DIR_CH_CC_CURRENT_MASK; 5838c2ecf20Sopenharmony_ci val |= curr_index; 5848c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, val); 5858c2ecf20Sopenharmony_ci if (ret < 0) { 5868c2ecf20Sopenharmony_ci dev_err(pm2->dev, 5878c2ecf20Sopenharmony_ci "%s write failed\n", __func__); 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci else 5918c2ecf20Sopenharmony_ci dev_err(pm2->dev, "%s read failed\n", __func__); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci return ret; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic int pm2xxx_charger_ac_get_property(struct power_supply *psy, 5978c2ecf20Sopenharmony_ci enum power_supply_property psp, 5988c2ecf20Sopenharmony_ci union power_supply_propval *val) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci pm2 = to_pm2xxx_charger_ac_device_info(psy_to_ux500_charger(psy)); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci switch (psp) { 6058c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 6068c2ecf20Sopenharmony_ci if (pm2->flags.mainextchnotok) 6078c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 6088c2ecf20Sopenharmony_ci else if (pm2->ac.wd_expired) 6098c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_DEAD; 6108c2ecf20Sopenharmony_ci else if (pm2->flags.main_thermal_prot) 6118c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; 6128c2ecf20Sopenharmony_ci else if (pm2->flags.ovv) 6138c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 6148c2ecf20Sopenharmony_ci else 6158c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_GOOD; 6168c2ecf20Sopenharmony_ci break; 6178c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 6188c2ecf20Sopenharmony_ci val->intval = pm2->ac.charger_online; 6198c2ecf20Sopenharmony_ci break; 6208c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 6218c2ecf20Sopenharmony_ci val->intval = pm2->ac.charger_connected; 6228c2ecf20Sopenharmony_ci break; 6238c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_AVG: 6248c2ecf20Sopenharmony_ci pm2->ac.cv_active = pm2xxx_charger_get_ac_cv(pm2); 6258c2ecf20Sopenharmony_ci val->intval = pm2->ac.cv_active; 6268c2ecf20Sopenharmony_ci break; 6278c2ecf20Sopenharmony_ci default: 6288c2ecf20Sopenharmony_ci return -EINVAL; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci return 0; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic int pm2xxx_charging_init(struct pm2xxx_charger *pm2) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci int ret = 0; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci /* enable CC and CV watchdog */ 6388c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG3, 6398c2ecf20Sopenharmony_ci (PM2XXX_CH_WD_CV_PHASE_60MIN | PM2XXX_CH_WD_CC_PHASE_60MIN)); 6408c2ecf20Sopenharmony_ci if( ret < 0) 6418c2ecf20Sopenharmony_ci return ret; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* enable precharge watchdog */ 6448c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG4, 6458c2ecf20Sopenharmony_ci PM2XXX_CH_WD_PRECH_PHASE_60MIN); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci /* Disable auto timeout */ 6488c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG5, 6498c2ecf20Sopenharmony_ci PM2XXX_CH_WD_AUTO_TIMEOUT_20MIN); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci /* 6528c2ecf20Sopenharmony_ci * EOC current level = 100mA 6538c2ecf20Sopenharmony_ci * Precharge current level = 100mA 6548c2ecf20Sopenharmony_ci * CC current level = 1000mA 6558c2ecf20Sopenharmony_ci */ 6568c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, 6578c2ecf20Sopenharmony_ci (PM2XXX_DIR_CH_CC_CURRENT_1000MA | 6588c2ecf20Sopenharmony_ci PM2XXX_CH_PRECH_CURRENT_100MA | 6598c2ecf20Sopenharmony_ci PM2XXX_CH_EOC_CURRENT_100MA)); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci /* 6628c2ecf20Sopenharmony_ci * recharge threshold = 3.8V 6638c2ecf20Sopenharmony_ci * Precharge to CC threshold = 2.9V 6648c2ecf20Sopenharmony_ci */ 6658c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG7, 6668c2ecf20Sopenharmony_ci (PM2XXX_CH_PRECH_VOL_2_9 | PM2XXX_CH_VRESUME_VOL_3_8)); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci /* float voltage charger level = 4.2V */ 6698c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG8, 6708c2ecf20Sopenharmony_ci PM2XXX_CH_VOLT_4_2); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci /* Voltage drop between VBAT and VSYS in HW charging = 300mV */ 6738c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG9, 6748c2ecf20Sopenharmony_ci (PM2XXX_CH_150MV_DROP_300MV | PM2XXX_CHARCHING_INFO_DIS | 6758c2ecf20Sopenharmony_ci PM2XXX_CH_CC_REDUCED_CURRENT_IDENT | 6768c2ecf20Sopenharmony_ci PM2XXX_CH_CC_MODEDROP_DIS)); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* Input charger level of over voltage = 10V */ 6798c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_INP_VOLT_VPWR2, 6808c2ecf20Sopenharmony_ci PM2XXX_VPWR2_OVV_10); 6818c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_INP_VOLT_VPWR1, 6828c2ecf20Sopenharmony_ci PM2XXX_VPWR1_OVV_10); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci /* Input charger drop */ 6858c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_INP_DROP_VPWR2, 6868c2ecf20Sopenharmony_ci (PM2XXX_VPWR2_HW_OPT_DIS | PM2XXX_VPWR2_VALID_DIS | 6878c2ecf20Sopenharmony_ci PM2XXX_VPWR2_DROP_DIS)); 6888c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_INP_DROP_VPWR1, 6898c2ecf20Sopenharmony_ci (PM2XXX_VPWR1_HW_OPT_DIS | PM2XXX_VPWR1_VALID_DIS | 6908c2ecf20Sopenharmony_ci PM2XXX_VPWR1_DROP_DIS)); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci /* Disable battery low monitoring */ 6938c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_LOW_LEV_COMP_REG, 6948c2ecf20Sopenharmony_ci PM2XXX_VBAT_LOW_MONITORING_ENA); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci return ret; 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic int pm2xxx_charger_ac_en(struct ux500_charger *charger, 7008c2ecf20Sopenharmony_ci int enable, int vset, int iset) 7018c2ecf20Sopenharmony_ci{ 7028c2ecf20Sopenharmony_ci int ret; 7038c2ecf20Sopenharmony_ci int volt_index; 7048c2ecf20Sopenharmony_ci int curr_index; 7058c2ecf20Sopenharmony_ci u8 val; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2 = to_pm2xxx_charger_ac_device_info(charger); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (enable) { 7108c2ecf20Sopenharmony_ci if (!pm2->ac.charger_connected) { 7118c2ecf20Sopenharmony_ci dev_dbg(pm2->dev, "AC charger not connected\n"); 7128c2ecf20Sopenharmony_ci return -ENXIO; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci dev_dbg(pm2->dev, "Enable AC: %dmV %dmA\n", vset, iset); 7168c2ecf20Sopenharmony_ci if (!pm2->vddadc_en_ac) { 7178c2ecf20Sopenharmony_ci ret = regulator_enable(pm2->regu); 7188c2ecf20Sopenharmony_ci if (ret) 7198c2ecf20Sopenharmony_ci dev_warn(pm2->dev, 7208c2ecf20Sopenharmony_ci "Failed to enable vddadc regulator\n"); 7218c2ecf20Sopenharmony_ci else 7228c2ecf20Sopenharmony_ci pm2->vddadc_en_ac = true; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci ret = pm2xxx_charging_init(pm2); 7268c2ecf20Sopenharmony_ci if (ret < 0) { 7278c2ecf20Sopenharmony_ci dev_err(pm2->dev, "%s charging init failed\n", 7288c2ecf20Sopenharmony_ci __func__); 7298c2ecf20Sopenharmony_ci goto error_occured; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci volt_index = pm2xxx_voltage_to_regval(vset); 7338c2ecf20Sopenharmony_ci curr_index = pm2xxx_current_to_regval(iset); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci if (volt_index < 0 || curr_index < 0) { 7368c2ecf20Sopenharmony_ci dev_err(pm2->dev, 7378c2ecf20Sopenharmony_ci "Charger voltage or current too high, " 7388c2ecf20Sopenharmony_ci "charging not started\n"); 7398c2ecf20Sopenharmony_ci return -ENXIO; 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG8, &val); 7438c2ecf20Sopenharmony_ci if (ret < 0) { 7448c2ecf20Sopenharmony_ci dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__); 7458c2ecf20Sopenharmony_ci goto error_occured; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci val &= ~PM2XXX_CH_VOLT_MASK; 7488c2ecf20Sopenharmony_ci val |= volt_index; 7498c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG8, val); 7508c2ecf20Sopenharmony_ci if (ret < 0) { 7518c2ecf20Sopenharmony_ci dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); 7528c2ecf20Sopenharmony_ci goto error_occured; 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6, &val); 7568c2ecf20Sopenharmony_ci if (ret < 0) { 7578c2ecf20Sopenharmony_ci dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__); 7588c2ecf20Sopenharmony_ci goto error_occured; 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci val &= ~PM2XXX_DIR_CH_CC_CURRENT_MASK; 7618c2ecf20Sopenharmony_ci val |= curr_index; 7628c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, val); 7638c2ecf20Sopenharmony_ci if (ret < 0) { 7648c2ecf20Sopenharmony_ci dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); 7658c2ecf20Sopenharmony_ci goto error_occured; 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci if (!pm2->bat->enable_overshoot) { 7698c2ecf20Sopenharmony_ci ret = pm2xxx_reg_read(pm2, PM2XXX_LED_CTRL_REG, &val); 7708c2ecf20Sopenharmony_ci if (ret < 0) { 7718c2ecf20Sopenharmony_ci dev_err(pm2->dev, "%s pm2xxx read failed\n", 7728c2ecf20Sopenharmony_ci __func__); 7738c2ecf20Sopenharmony_ci goto error_occured; 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci val |= PM2XXX_ANTI_OVERSHOOT_EN; 7768c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG, val); 7778c2ecf20Sopenharmony_ci if (ret < 0) { 7788c2ecf20Sopenharmony_ci dev_err(pm2->dev, "%s pm2xxx write failed\n", 7798c2ecf20Sopenharmony_ci __func__); 7808c2ecf20Sopenharmony_ci goto error_occured; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci ret = pm2xxx_charging_enable_mngt(pm2); 7858c2ecf20Sopenharmony_ci if (ret < 0) { 7868c2ecf20Sopenharmony_ci dev_err(pm2->dev, "Failed to enable" 7878c2ecf20Sopenharmony_ci "pm2xxx ac charger\n"); 7888c2ecf20Sopenharmony_ci goto error_occured; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci pm2->ac.charger_online = 1; 7928c2ecf20Sopenharmony_ci } else { 7938c2ecf20Sopenharmony_ci pm2->ac.charger_online = 0; 7948c2ecf20Sopenharmony_ci pm2->ac.wd_expired = false; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci /* Disable regulator if enabled */ 7978c2ecf20Sopenharmony_ci if (pm2->vddadc_en_ac) { 7988c2ecf20Sopenharmony_ci regulator_disable(pm2->regu); 7998c2ecf20Sopenharmony_ci pm2->vddadc_en_ac = false; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci ret = pm2xxx_charging_disable_mngt(pm2); 8038c2ecf20Sopenharmony_ci if (ret < 0) { 8048c2ecf20Sopenharmony_ci dev_err(pm2->dev, "failed to disable" 8058c2ecf20Sopenharmony_ci "pm2xxx ac charger\n"); 8068c2ecf20Sopenharmony_ci goto error_occured; 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci dev_dbg(pm2->dev, "PM2301: " "Disabled AC charging\n"); 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci power_supply_changed(pm2->ac_chg.psy); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cierror_occured: 8148c2ecf20Sopenharmony_ci return ret; 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_cistatic int pm2xxx_charger_watchdog_kick(struct ux500_charger *charger) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci int ret; 8208c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS) 8238c2ecf20Sopenharmony_ci pm2 = to_pm2xxx_charger_ac_device_info(charger); 8248c2ecf20Sopenharmony_ci else 8258c2ecf20Sopenharmony_ci return -ENXIO; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_WD_KICK, WD_TIMER); 8288c2ecf20Sopenharmony_ci if (ret) 8298c2ecf20Sopenharmony_ci dev_err(pm2->dev, "Failed to kick WD!\n"); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci return ret; 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_cistatic void pm2xxx_charger_ac_work(struct work_struct *work) 8358c2ecf20Sopenharmony_ci{ 8368c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2 = container_of(work, 8378c2ecf20Sopenharmony_ci struct pm2xxx_charger, ac_work); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci power_supply_changed(pm2->ac_chg.psy); 8418c2ecf20Sopenharmony_ci sysfs_notify(&pm2->ac_chg.psy->dev.kobj, NULL, "present"); 8428c2ecf20Sopenharmony_ci}; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_cistatic void pm2xxx_charger_check_hw_failure_work(struct work_struct *work) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci u8 reg_value; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2 = container_of(work, 8498c2ecf20Sopenharmony_ci struct pm2xxx_charger, check_hw_failure_work.work); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci if (pm2->flags.ovv) { 8528c2ecf20Sopenharmony_ci pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT4, ®_value); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci if (!(reg_value & (PM2XXX_INT4_S_ITVPWR1OVV | 8558c2ecf20Sopenharmony_ci PM2XXX_INT4_S_ITVPWR2OVV))) { 8568c2ecf20Sopenharmony_ci pm2->flags.ovv = false; 8578c2ecf20Sopenharmony_ci power_supply_changed(pm2->ac_chg.psy); 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci /* If we still have a failure, schedule a new check */ 8628c2ecf20Sopenharmony_ci if (pm2->flags.ovv) { 8638c2ecf20Sopenharmony_ci queue_delayed_work(pm2->charger_wq, 8648c2ecf20Sopenharmony_ci &pm2->check_hw_failure_work, round_jiffies(HZ)); 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_cistatic void pm2xxx_charger_check_main_thermal_prot_work( 8698c2ecf20Sopenharmony_ci struct work_struct *work) 8708c2ecf20Sopenharmony_ci{ 8718c2ecf20Sopenharmony_ci int ret; 8728c2ecf20Sopenharmony_ci u8 val; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2 = container_of(work, struct pm2xxx_charger, 8758c2ecf20Sopenharmony_ci check_main_thermal_prot_work); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci /* Check if die temp warning is still active */ 8788c2ecf20Sopenharmony_ci ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT5, &val); 8798c2ecf20Sopenharmony_ci if (ret < 0) { 8808c2ecf20Sopenharmony_ci dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__); 8818c2ecf20Sopenharmony_ci return; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci if (val & (PM2XXX_INT5_S_ITTHERMALWARNINGRISE 8848c2ecf20Sopenharmony_ci | PM2XXX_INT5_S_ITTHERMALSHUTDOWNRISE)) 8858c2ecf20Sopenharmony_ci pm2->flags.main_thermal_prot = true; 8868c2ecf20Sopenharmony_ci else if (val & (PM2XXX_INT5_S_ITTHERMALWARNINGFALL 8878c2ecf20Sopenharmony_ci | PM2XXX_INT5_S_ITTHERMALSHUTDOWNFALL)) 8888c2ecf20Sopenharmony_ci pm2->flags.main_thermal_prot = false; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci power_supply_changed(pm2->ac_chg.psy); 8918c2ecf20Sopenharmony_ci} 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_cistatic struct pm2xxx_interrupts pm2xxx_int = { 8948c2ecf20Sopenharmony_ci .handler[0] = pm2_int_reg0, 8958c2ecf20Sopenharmony_ci .handler[1] = pm2_int_reg1, 8968c2ecf20Sopenharmony_ci .handler[2] = pm2_int_reg2, 8978c2ecf20Sopenharmony_ci .handler[3] = pm2_int_reg3, 8988c2ecf20Sopenharmony_ci .handler[4] = pm2_int_reg4, 8998c2ecf20Sopenharmony_ci .handler[5] = pm2_int_reg5, 9008c2ecf20Sopenharmony_ci}; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_cistatic struct pm2xxx_irq pm2xxx_charger_irq[] = { 9038c2ecf20Sopenharmony_ci {"PM2XXX_IRQ_INT", pm2xxx_irq_int}, 9048c2ecf20Sopenharmony_ci}; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_cistatic int __maybe_unused pm2xxx_wall_charger_resume(struct device *dev) 9078c2ecf20Sopenharmony_ci{ 9088c2ecf20Sopenharmony_ci struct i2c_client *i2c_client = to_i2c_client(dev); 9098c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(i2c_client); 9128c2ecf20Sopenharmony_ci set_lpn_pin(pm2); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci /* If we still have a HW failure, schedule a new check */ 9158c2ecf20Sopenharmony_ci if (pm2->flags.ovv) 9168c2ecf20Sopenharmony_ci queue_delayed_work(pm2->charger_wq, 9178c2ecf20Sopenharmony_ci &pm2->check_hw_failure_work, 0); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci return 0; 9208c2ecf20Sopenharmony_ci} 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_cistatic int __maybe_unused pm2xxx_wall_charger_suspend(struct device *dev) 9238c2ecf20Sopenharmony_ci{ 9248c2ecf20Sopenharmony_ci struct i2c_client *i2c_client = to_i2c_client(dev); 9258c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(i2c_client); 9288c2ecf20Sopenharmony_ci clear_lpn_pin(pm2); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci /* Cancel any pending HW failure check */ 9318c2ecf20Sopenharmony_ci if (delayed_work_pending(&pm2->check_hw_failure_work)) 9328c2ecf20Sopenharmony_ci cancel_delayed_work(&pm2->check_hw_failure_work); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci flush_work(&pm2->ac_work); 9358c2ecf20Sopenharmony_ci flush_work(&pm2->check_main_thermal_prot_work); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci return 0; 9388c2ecf20Sopenharmony_ci} 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_cistatic int __maybe_unused pm2xxx_runtime_suspend(struct device *dev) 9418c2ecf20Sopenharmony_ci{ 9428c2ecf20Sopenharmony_ci struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev); 9438c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client); 9468c2ecf20Sopenharmony_ci clear_lpn_pin(pm2); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci return 0; 9498c2ecf20Sopenharmony_ci} 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_cistatic int __maybe_unused pm2xxx_runtime_resume(struct device *dev) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev); 9548c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci if (gpio_is_valid(pm2->lpn_pin) && gpio_get_value(pm2->lpn_pin) == 0) 9598c2ecf20Sopenharmony_ci set_lpn_pin(pm2); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci return 0; 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_cistatic const struct dev_pm_ops pm2xxx_pm_ops __maybe_unused = { 9658c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm2xxx_wall_charger_suspend, 9668c2ecf20Sopenharmony_ci pm2xxx_wall_charger_resume) 9678c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(pm2xxx_runtime_suspend, pm2xxx_runtime_resume, NULL) 9688c2ecf20Sopenharmony_ci}; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_cistatic int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, 9718c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci struct pm2xxx_platform_data *pl_data = i2c_client->dev.platform_data; 9748c2ecf20Sopenharmony_ci struct power_supply_config psy_cfg = {}; 9758c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2; 9768c2ecf20Sopenharmony_ci int ret = 0; 9778c2ecf20Sopenharmony_ci u8 val; 9788c2ecf20Sopenharmony_ci int i; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci if (!pl_data) { 9818c2ecf20Sopenharmony_ci dev_err(&i2c_client->dev, "No platform data supplied\n"); 9828c2ecf20Sopenharmony_ci return -EINVAL; 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci pm2 = kzalloc(sizeof(struct pm2xxx_charger), GFP_KERNEL); 9868c2ecf20Sopenharmony_ci if (!pm2) { 9878c2ecf20Sopenharmony_ci dev_err(&i2c_client->dev, "pm2xxx_charger allocation failed\n"); 9888c2ecf20Sopenharmony_ci return -ENOMEM; 9898c2ecf20Sopenharmony_ci } 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci /* get parent data */ 9928c2ecf20Sopenharmony_ci pm2->dev = &i2c_client->dev; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci pm2->pm2_int = &pm2xxx_int; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci /* get charger spcific platform data */ 9978c2ecf20Sopenharmony_ci if (!pl_data->wall_charger) { 9988c2ecf20Sopenharmony_ci dev_err(pm2->dev, "no charger platform data supplied\n"); 9998c2ecf20Sopenharmony_ci ret = -EINVAL; 10008c2ecf20Sopenharmony_ci goto free_device_info; 10018c2ecf20Sopenharmony_ci } 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci pm2->pdata = pl_data->wall_charger; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci /* get battery specific platform data */ 10068c2ecf20Sopenharmony_ci if (!pl_data->battery) { 10078c2ecf20Sopenharmony_ci dev_err(pm2->dev, "no battery platform data supplied\n"); 10088c2ecf20Sopenharmony_ci ret = -EINVAL; 10098c2ecf20Sopenharmony_ci goto free_device_info; 10108c2ecf20Sopenharmony_ci } 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci pm2->bat = pl_data->battery; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci if (!i2c_check_functionality(i2c_client->adapter, 10158c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_BYTE_DATA | 10168c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_READ_WORD_DATA)) { 10178c2ecf20Sopenharmony_ci ret = -ENODEV; 10188c2ecf20Sopenharmony_ci dev_info(pm2->dev, "pm2301 i2c_check_functionality failed\n"); 10198c2ecf20Sopenharmony_ci goto free_device_info; 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci pm2->config.pm2xxx_i2c = i2c_client; 10238c2ecf20Sopenharmony_ci pm2->config.pm2xxx_id = (struct i2c_device_id *) id; 10248c2ecf20Sopenharmony_ci i2c_set_clientdata(i2c_client, pm2); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci /* AC supply */ 10278c2ecf20Sopenharmony_ci /* power_supply base class */ 10288c2ecf20Sopenharmony_ci pm2->ac_chg_desc.name = pm2->pdata->label; 10298c2ecf20Sopenharmony_ci pm2->ac_chg_desc.type = POWER_SUPPLY_TYPE_MAINS; 10308c2ecf20Sopenharmony_ci pm2->ac_chg_desc.properties = pm2xxx_charger_ac_props; 10318c2ecf20Sopenharmony_ci pm2->ac_chg_desc.num_properties = ARRAY_SIZE(pm2xxx_charger_ac_props); 10328c2ecf20Sopenharmony_ci pm2->ac_chg_desc.get_property = pm2xxx_charger_ac_get_property; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci psy_cfg.supplied_to = pm2->pdata->supplied_to; 10358c2ecf20Sopenharmony_ci psy_cfg.num_supplicants = pm2->pdata->num_supplicants; 10368c2ecf20Sopenharmony_ci /* pm2xxx_charger sub-class */ 10378c2ecf20Sopenharmony_ci pm2->ac_chg.ops.enable = &pm2xxx_charger_ac_en; 10388c2ecf20Sopenharmony_ci pm2->ac_chg.ops.kick_wd = &pm2xxx_charger_watchdog_kick; 10398c2ecf20Sopenharmony_ci pm2->ac_chg.ops.update_curr = &pm2xxx_charger_update_charger_current; 10408c2ecf20Sopenharmony_ci pm2->ac_chg.max_out_volt = pm2xxx_charger_voltage_map[ 10418c2ecf20Sopenharmony_ci ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1]; 10428c2ecf20Sopenharmony_ci pm2->ac_chg.max_out_curr = pm2xxx_charger_current_map[ 10438c2ecf20Sopenharmony_ci ARRAY_SIZE(pm2xxx_charger_current_map) - 1]; 10448c2ecf20Sopenharmony_ci pm2->ac_chg.wdt_refresh = WD_KICK_INTERVAL; 10458c2ecf20Sopenharmony_ci pm2->ac_chg.enabled = true; 10468c2ecf20Sopenharmony_ci pm2->ac_chg.external = true; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci /* Create a work queue for the charger */ 10498c2ecf20Sopenharmony_ci pm2->charger_wq = alloc_ordered_workqueue("pm2xxx_charger_wq", 10508c2ecf20Sopenharmony_ci WQ_MEM_RECLAIM); 10518c2ecf20Sopenharmony_ci if (pm2->charger_wq == NULL) { 10528c2ecf20Sopenharmony_ci ret = -ENOMEM; 10538c2ecf20Sopenharmony_ci dev_err(pm2->dev, "failed to create work queue\n"); 10548c2ecf20Sopenharmony_ci goto free_device_info; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci /* Init work for charger detection */ 10588c2ecf20Sopenharmony_ci INIT_WORK(&pm2->ac_work, pm2xxx_charger_ac_work); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci /* Init work for checking HW status */ 10618c2ecf20Sopenharmony_ci INIT_WORK(&pm2->check_main_thermal_prot_work, 10628c2ecf20Sopenharmony_ci pm2xxx_charger_check_main_thermal_prot_work); 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci /* Init work for HW failure check */ 10658c2ecf20Sopenharmony_ci INIT_DEFERRABLE_WORK(&pm2->check_hw_failure_work, 10668c2ecf20Sopenharmony_ci pm2xxx_charger_check_hw_failure_work); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci /* 10698c2ecf20Sopenharmony_ci * VDD ADC supply needs to be enabled from this driver when there 10708c2ecf20Sopenharmony_ci * is a charger connected to avoid erroneous BTEMP_HIGH/LOW 10718c2ecf20Sopenharmony_ci * interrupts during charging 10728c2ecf20Sopenharmony_ci */ 10738c2ecf20Sopenharmony_ci pm2->regu = regulator_get(pm2->dev, "vddadc"); 10748c2ecf20Sopenharmony_ci if (IS_ERR(pm2->regu)) { 10758c2ecf20Sopenharmony_ci ret = PTR_ERR(pm2->regu); 10768c2ecf20Sopenharmony_ci dev_err(pm2->dev, "failed to get vddadc regulator\n"); 10778c2ecf20Sopenharmony_ci goto free_charger_wq; 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci /* Register AC charger class */ 10818c2ecf20Sopenharmony_ci pm2->ac_chg.psy = power_supply_register(pm2->dev, &pm2->ac_chg_desc, 10828c2ecf20Sopenharmony_ci &psy_cfg); 10838c2ecf20Sopenharmony_ci if (IS_ERR(pm2->ac_chg.psy)) { 10848c2ecf20Sopenharmony_ci dev_err(pm2->dev, "failed to register AC charger\n"); 10858c2ecf20Sopenharmony_ci ret = PTR_ERR(pm2->ac_chg.psy); 10868c2ecf20Sopenharmony_ci goto free_regulator; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci /* Register interrupts */ 10908c2ecf20Sopenharmony_ci ret = request_threaded_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), 10918c2ecf20Sopenharmony_ci NULL, 10928c2ecf20Sopenharmony_ci pm2xxx_charger_irq[0].isr, 10938c2ecf20Sopenharmony_ci pm2->pdata->irq_type | IRQF_ONESHOT, 10948c2ecf20Sopenharmony_ci pm2xxx_charger_irq[0].name, pm2); 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci if (ret != 0) { 10978c2ecf20Sopenharmony_ci dev_err(pm2->dev, "failed to request %s IRQ %d: %d\n", 10988c2ecf20Sopenharmony_ci pm2xxx_charger_irq[0].name, 10998c2ecf20Sopenharmony_ci gpio_to_irq(pm2->pdata->gpio_irq_number), ret); 11008c2ecf20Sopenharmony_ci goto unregister_pm2xxx_charger; 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci ret = pm_runtime_set_active(pm2->dev); 11048c2ecf20Sopenharmony_ci if (ret) 11058c2ecf20Sopenharmony_ci dev_err(pm2->dev, "set active Error\n"); 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci pm_runtime_enable(pm2->dev); 11088c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(pm2->dev, PM2XXX_AUTOSUSPEND_DELAY); 11098c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(pm2->dev); 11108c2ecf20Sopenharmony_ci pm_runtime_resume(pm2->dev); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci /* pm interrupt can wake up system */ 11138c2ecf20Sopenharmony_ci ret = enable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number)); 11148c2ecf20Sopenharmony_ci if (ret) { 11158c2ecf20Sopenharmony_ci dev_err(pm2->dev, "failed to set irq wake\n"); 11168c2ecf20Sopenharmony_ci goto unregister_pm2xxx_interrupt; 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci mutex_init(&pm2->lock); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci if (gpio_is_valid(pm2->pdata->lpn_gpio)) { 11228c2ecf20Sopenharmony_ci /* get lpn GPIO from platform data */ 11238c2ecf20Sopenharmony_ci pm2->lpn_pin = pm2->pdata->lpn_gpio; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci /* 11268c2ecf20Sopenharmony_ci * Charger detection mechanism requires pulling up the LPN pin 11278c2ecf20Sopenharmony_ci * while i2c communication if Charger is not connected 11288c2ecf20Sopenharmony_ci * LPN pin of PM2301 is GPIO60 of AB9540 11298c2ecf20Sopenharmony_ci */ 11308c2ecf20Sopenharmony_ci ret = gpio_request(pm2->lpn_pin, "pm2301_lpm_gpio"); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci if (ret < 0) { 11338c2ecf20Sopenharmony_ci dev_err(pm2->dev, "pm2301_lpm_gpio request failed\n"); 11348c2ecf20Sopenharmony_ci goto disable_pm2_irq_wake; 11358c2ecf20Sopenharmony_ci } 11368c2ecf20Sopenharmony_ci ret = gpio_direction_output(pm2->lpn_pin, 0); 11378c2ecf20Sopenharmony_ci if (ret < 0) { 11388c2ecf20Sopenharmony_ci dev_err(pm2->dev, "pm2301_lpm_gpio direction failed\n"); 11398c2ecf20Sopenharmony_ci goto free_gpio; 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci set_lpn_pin(pm2); 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci /* read interrupt registers */ 11458c2ecf20Sopenharmony_ci for (i = 0; i < PM2XXX_NUM_INT_REG; i++) 11468c2ecf20Sopenharmony_ci pm2xxx_reg_read(pm2, 11478c2ecf20Sopenharmony_ci pm2xxx_interrupt_registers[i], 11488c2ecf20Sopenharmony_ci &val); 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci ret = pm2xxx_charger_detection(pm2, &val); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci if ((ret == 0) && val) { 11538c2ecf20Sopenharmony_ci pm2->ac.charger_connected = 1; 11548c2ecf20Sopenharmony_ci ab8500_override_turn_on_stat(~AB8500_POW_KEY_1_ON, 11558c2ecf20Sopenharmony_ci AB8500_MAIN_CH_DET); 11568c2ecf20Sopenharmony_ci pm2->ac_conn = true; 11578c2ecf20Sopenharmony_ci power_supply_changed(pm2->ac_chg.psy); 11588c2ecf20Sopenharmony_ci sysfs_notify(&pm2->ac_chg.psy->dev.kobj, NULL, "present"); 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci return 0; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cifree_gpio: 11648c2ecf20Sopenharmony_ci if (gpio_is_valid(pm2->lpn_pin)) 11658c2ecf20Sopenharmony_ci gpio_free(pm2->lpn_pin); 11668c2ecf20Sopenharmony_cidisable_pm2_irq_wake: 11678c2ecf20Sopenharmony_ci disable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number)); 11688c2ecf20Sopenharmony_ciunregister_pm2xxx_interrupt: 11698c2ecf20Sopenharmony_ci /* disable interrupt */ 11708c2ecf20Sopenharmony_ci free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2); 11718c2ecf20Sopenharmony_ciunregister_pm2xxx_charger: 11728c2ecf20Sopenharmony_ci /* unregister power supply */ 11738c2ecf20Sopenharmony_ci power_supply_unregister(pm2->ac_chg.psy); 11748c2ecf20Sopenharmony_cifree_regulator: 11758c2ecf20Sopenharmony_ci /* disable the regulator */ 11768c2ecf20Sopenharmony_ci regulator_put(pm2->regu); 11778c2ecf20Sopenharmony_cifree_charger_wq: 11788c2ecf20Sopenharmony_ci destroy_workqueue(pm2->charger_wq); 11798c2ecf20Sopenharmony_cifree_device_info: 11808c2ecf20Sopenharmony_ci kfree(pm2); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci return ret; 11838c2ecf20Sopenharmony_ci} 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_cistatic int pm2xxx_wall_charger_remove(struct i2c_client *i2c_client) 11868c2ecf20Sopenharmony_ci{ 11878c2ecf20Sopenharmony_ci struct pm2xxx_charger *pm2 = i2c_get_clientdata(i2c_client); 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci /* Disable pm_runtime */ 11908c2ecf20Sopenharmony_ci pm_runtime_disable(pm2->dev); 11918c2ecf20Sopenharmony_ci /* Disable AC charging */ 11928c2ecf20Sopenharmony_ci pm2xxx_charger_ac_en(&pm2->ac_chg, false, 0, 0); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci /* Disable wake by pm interrupt */ 11958c2ecf20Sopenharmony_ci disable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number)); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci /* Disable interrupts */ 11988c2ecf20Sopenharmony_ci free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2); 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci /* Delete the work queue */ 12018c2ecf20Sopenharmony_ci destroy_workqueue(pm2->charger_wq); 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci flush_scheduled_work(); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci /* disable the regulator */ 12068c2ecf20Sopenharmony_ci regulator_put(pm2->regu); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci power_supply_unregister(pm2->ac_chg.psy); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci if (gpio_is_valid(pm2->lpn_pin)) 12118c2ecf20Sopenharmony_ci gpio_free(pm2->lpn_pin); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci kfree(pm2); 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci return 0; 12168c2ecf20Sopenharmony_ci} 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_cistatic const struct i2c_device_id pm2xxx_id[] = { 12198c2ecf20Sopenharmony_ci { "pm2301", 0 }, 12208c2ecf20Sopenharmony_ci { } 12218c2ecf20Sopenharmony_ci}; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, pm2xxx_id); 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_cistatic struct i2c_driver pm2xxx_charger_driver = { 12268c2ecf20Sopenharmony_ci .probe = pm2xxx_wall_charger_probe, 12278c2ecf20Sopenharmony_ci .remove = pm2xxx_wall_charger_remove, 12288c2ecf20Sopenharmony_ci .driver = { 12298c2ecf20Sopenharmony_ci .name = "pm2xxx-wall_charger", 12308c2ecf20Sopenharmony_ci .pm = IS_ENABLED(CONFIG_PM) ? &pm2xxx_pm_ops : NULL, 12318c2ecf20Sopenharmony_ci }, 12328c2ecf20Sopenharmony_ci .id_table = pm2xxx_id, 12338c2ecf20Sopenharmony_ci}; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_cistatic int __init pm2xxx_charger_init(void) 12368c2ecf20Sopenharmony_ci{ 12378c2ecf20Sopenharmony_ci return i2c_add_driver(&pm2xxx_charger_driver); 12388c2ecf20Sopenharmony_ci} 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_cistatic void __exit pm2xxx_charger_exit(void) 12418c2ecf20Sopenharmony_ci{ 12428c2ecf20Sopenharmony_ci i2c_del_driver(&pm2xxx_charger_driver); 12438c2ecf20Sopenharmony_ci} 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_cidevice_initcall_sync(pm2xxx_charger_init); 12468c2ecf20Sopenharmony_cimodule_exit(pm2xxx_charger_exit); 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 12498c2ecf20Sopenharmony_ciMODULE_AUTHOR("Rajkumar kasirajan, Olivier Launay"); 12508c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PM2xxx charger management driver"); 1251