18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// max8998_charger.c - Power supply consumer driver for the Maxim 8998/LP3974 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2009-2010 Samsung Electronics 68c2ecf20Sopenharmony_ci// MyungJoo Ham <myungjoo.ham@samsung.com> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/err.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 148c2ecf20Sopenharmony_ci#include <linux/mfd/max8998.h> 158c2ecf20Sopenharmony_ci#include <linux/mfd/max8998-private.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistruct max8998_battery_data { 188c2ecf20Sopenharmony_ci struct device *dev; 198c2ecf20Sopenharmony_ci struct max8998_dev *iodev; 208c2ecf20Sopenharmony_ci struct power_supply *battery; 218c2ecf20Sopenharmony_ci}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic enum power_supply_property max8998_battery_props[] = { 248c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, /* the presence of battery */ 258c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, /* charger is active or not */ 268c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, /* charger is charging/discharging/full */ 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* Note that the charger control is done by a current regulator "CHARGER" */ 308c2ecf20Sopenharmony_cistatic int max8998_battery_get_property(struct power_supply *psy, 318c2ecf20Sopenharmony_ci enum power_supply_property psp, 328c2ecf20Sopenharmony_ci union power_supply_propval *val) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct max8998_battery_data *max8998 = power_supply_get_drvdata(psy); 358c2ecf20Sopenharmony_ci struct i2c_client *i2c = max8998->iodev->i2c; 368c2ecf20Sopenharmony_ci int ret; 378c2ecf20Sopenharmony_ci u8 reg; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci switch (psp) { 408c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 418c2ecf20Sopenharmony_ci ret = max8998_read_reg(i2c, MAX8998_REG_STATUS2, ®); 428c2ecf20Sopenharmony_ci if (ret) 438c2ecf20Sopenharmony_ci return ret; 448c2ecf20Sopenharmony_ci if (reg & (1 << 4)) 458c2ecf20Sopenharmony_ci val->intval = 0; 468c2ecf20Sopenharmony_ci else 478c2ecf20Sopenharmony_ci val->intval = 1; 488c2ecf20Sopenharmony_ci break; 498c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 508c2ecf20Sopenharmony_ci ret = max8998_read_reg(i2c, MAX8998_REG_STATUS2, ®); 518c2ecf20Sopenharmony_ci if (ret) 528c2ecf20Sopenharmony_ci return ret; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (reg & (1 << 5)) 558c2ecf20Sopenharmony_ci val->intval = 1; 568c2ecf20Sopenharmony_ci else 578c2ecf20Sopenharmony_ci val->intval = 0; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci break; 608c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 618c2ecf20Sopenharmony_ci ret = max8998_read_reg(i2c, MAX8998_REG_STATUS2, ®); 628c2ecf20Sopenharmony_ci if (ret) 638c2ecf20Sopenharmony_ci return ret; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (!(reg & (1 << 5))) { 668c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 678c2ecf20Sopenharmony_ci } else { 688c2ecf20Sopenharmony_ci if (reg & (1 << 6)) 698c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_FULL; 708c2ecf20Sopenharmony_ci else if (reg & (1 << 3)) 718c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_CHARGING; 728c2ecf20Sopenharmony_ci else 738c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci break; 768c2ecf20Sopenharmony_ci default: 778c2ecf20Sopenharmony_ci return -EINVAL; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return 0; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic const struct power_supply_desc max8998_battery_desc = { 848c2ecf20Sopenharmony_ci .name = "max8998_pmic", 858c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_BATTERY, 868c2ecf20Sopenharmony_ci .get_property = max8998_battery_get_property, 878c2ecf20Sopenharmony_ci .properties = max8998_battery_props, 888c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(max8998_battery_props), 898c2ecf20Sopenharmony_ci}; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int max8998_battery_probe(struct platform_device *pdev) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct max8998_dev *iodev = dev_get_drvdata(pdev->dev.parent); 948c2ecf20Sopenharmony_ci struct max8998_platform_data *pdata = iodev->pdata; 958c2ecf20Sopenharmony_ci struct power_supply_config psy_cfg = {}; 968c2ecf20Sopenharmony_ci struct max8998_battery_data *max8998; 978c2ecf20Sopenharmony_ci struct i2c_client *i2c; 988c2ecf20Sopenharmony_ci int ret = 0; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (!pdata) { 1018c2ecf20Sopenharmony_ci dev_err(pdev->dev.parent, "No platform init data supplied\n"); 1028c2ecf20Sopenharmony_ci return -ENODEV; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci max8998 = devm_kzalloc(&pdev->dev, sizeof(struct max8998_battery_data), 1068c2ecf20Sopenharmony_ci GFP_KERNEL); 1078c2ecf20Sopenharmony_ci if (!max8998) 1088c2ecf20Sopenharmony_ci return -ENOMEM; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci max8998->dev = &pdev->dev; 1118c2ecf20Sopenharmony_ci max8998->iodev = iodev; 1128c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, max8998); 1138c2ecf20Sopenharmony_ci i2c = max8998->iodev->i2c; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* Setup "End of Charge" */ 1168c2ecf20Sopenharmony_ci /* If EOC value equals 0, 1178c2ecf20Sopenharmony_ci * remain value set from bootloader or default value */ 1188c2ecf20Sopenharmony_ci if (pdata->eoc >= 10 && pdata->eoc <= 45) { 1198c2ecf20Sopenharmony_ci max8998_update_reg(i2c, MAX8998_REG_CHGR1, 1208c2ecf20Sopenharmony_ci (pdata->eoc / 5 - 2) << 5, 0x7 << 5); 1218c2ecf20Sopenharmony_ci } else if (pdata->eoc == 0) { 1228c2ecf20Sopenharmony_ci dev_dbg(max8998->dev, 1238c2ecf20Sopenharmony_ci "EOC value not set: leave it unchanged.\n"); 1248c2ecf20Sopenharmony_ci } else { 1258c2ecf20Sopenharmony_ci dev_err(max8998->dev, "Invalid EOC value\n"); 1268c2ecf20Sopenharmony_ci return -EINVAL; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* Setup Charge Restart Level */ 1308c2ecf20Sopenharmony_ci switch (pdata->restart) { 1318c2ecf20Sopenharmony_ci case 100: 1328c2ecf20Sopenharmony_ci max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x1 << 3, 0x3 << 3); 1338c2ecf20Sopenharmony_ci break; 1348c2ecf20Sopenharmony_ci case 150: 1358c2ecf20Sopenharmony_ci max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x0 << 3, 0x3 << 3); 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci case 200: 1388c2ecf20Sopenharmony_ci max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x2 << 3, 0x3 << 3); 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci case -1: 1418c2ecf20Sopenharmony_ci max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x3 << 3, 0x3 << 3); 1428c2ecf20Sopenharmony_ci break; 1438c2ecf20Sopenharmony_ci case 0: 1448c2ecf20Sopenharmony_ci dev_dbg(max8998->dev, 1458c2ecf20Sopenharmony_ci "Restart Level not set: leave it unchanged.\n"); 1468c2ecf20Sopenharmony_ci break; 1478c2ecf20Sopenharmony_ci default: 1488c2ecf20Sopenharmony_ci dev_err(max8998->dev, "Invalid Restart Level\n"); 1498c2ecf20Sopenharmony_ci return -EINVAL; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Setup Charge Full Timeout */ 1538c2ecf20Sopenharmony_ci switch (pdata->timeout) { 1548c2ecf20Sopenharmony_ci case 5: 1558c2ecf20Sopenharmony_ci max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x0 << 4, 0x3 << 4); 1568c2ecf20Sopenharmony_ci break; 1578c2ecf20Sopenharmony_ci case 6: 1588c2ecf20Sopenharmony_ci max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x1 << 4, 0x3 << 4); 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci case 7: 1618c2ecf20Sopenharmony_ci max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x2 << 4, 0x3 << 4); 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci case -1: 1648c2ecf20Sopenharmony_ci max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x3 << 4, 0x3 << 4); 1658c2ecf20Sopenharmony_ci break; 1668c2ecf20Sopenharmony_ci case 0: 1678c2ecf20Sopenharmony_ci dev_dbg(max8998->dev, 1688c2ecf20Sopenharmony_ci "Full Timeout not set: leave it unchanged.\n"); 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci default: 1718c2ecf20Sopenharmony_ci dev_err(max8998->dev, "Invalid Full Timeout value\n"); 1728c2ecf20Sopenharmony_ci return -EINVAL; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci psy_cfg.drv_data = max8998; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci max8998->battery = devm_power_supply_register(max8998->dev, 1788c2ecf20Sopenharmony_ci &max8998_battery_desc, 1798c2ecf20Sopenharmony_ci &psy_cfg); 1808c2ecf20Sopenharmony_ci if (IS_ERR(max8998->battery)) { 1818c2ecf20Sopenharmony_ci ret = PTR_ERR(max8998->battery); 1828c2ecf20Sopenharmony_ci dev_err(max8998->dev, "failed: power supply register: %d\n", 1838c2ecf20Sopenharmony_ci ret); 1848c2ecf20Sopenharmony_ci return ret; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic const struct platform_device_id max8998_battery_id[] = { 1918c2ecf20Sopenharmony_ci { "max8998-battery", TYPE_MAX8998 }, 1928c2ecf20Sopenharmony_ci { } 1938c2ecf20Sopenharmony_ci}; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic struct platform_driver max8998_battery_driver = { 1968c2ecf20Sopenharmony_ci .driver = { 1978c2ecf20Sopenharmony_ci .name = "max8998-battery", 1988c2ecf20Sopenharmony_ci }, 1998c2ecf20Sopenharmony_ci .probe = max8998_battery_probe, 2008c2ecf20Sopenharmony_ci .id_table = max8998_battery_id, 2018c2ecf20Sopenharmony_ci}; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cimodule_platform_driver(max8998_battery_driver); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MAXIM 8998 battery control driver"); 2068c2ecf20Sopenharmony_ciMODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); 2078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2088c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:max8998-battery"); 209