18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Backup battery driver for Wolfson Microelectronics wm831x PMICs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2009 Wolfson Microelectronics PLC. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/err.h> 108c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 118c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/mfd/wm831x/core.h> 158c2ecf20Sopenharmony_ci#include <linux/mfd/wm831x/auxadc.h> 168c2ecf20Sopenharmony_ci#include <linux/mfd/wm831x/pmu.h> 178c2ecf20Sopenharmony_ci#include <linux/mfd/wm831x/pdata.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistruct wm831x_backup { 208c2ecf20Sopenharmony_ci struct wm831x *wm831x; 218c2ecf20Sopenharmony_ci struct power_supply *backup; 228c2ecf20Sopenharmony_ci struct power_supply_desc backup_desc; 238c2ecf20Sopenharmony_ci char name[20]; 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int wm831x_backup_read_voltage(struct wm831x *wm831x, 278c2ecf20Sopenharmony_ci enum wm831x_auxadc src, 288c2ecf20Sopenharmony_ci union power_supply_propval *val) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci int ret; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci ret = wm831x_auxadc_read_uv(wm831x, src); 338c2ecf20Sopenharmony_ci if (ret >= 0) 348c2ecf20Sopenharmony_ci val->intval = ret; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci return ret; 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/********************************************************************* 408c2ecf20Sopenharmony_ci * Backup supply properties 418c2ecf20Sopenharmony_ci *********************************************************************/ 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic void wm831x_config_backup(struct wm831x *wm831x) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data; 468c2ecf20Sopenharmony_ci struct wm831x_backup_pdata *pdata; 478c2ecf20Sopenharmony_ci int ret, reg; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (!wm831x_pdata || !wm831x_pdata->backup) { 508c2ecf20Sopenharmony_ci dev_warn(wm831x->dev, 518c2ecf20Sopenharmony_ci "No backup battery charger configuration\n"); 528c2ecf20Sopenharmony_ci return; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci pdata = wm831x_pdata->backup; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci reg = 0; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (pdata->charger_enable) 608c2ecf20Sopenharmony_ci reg |= WM831X_BKUP_CHG_ENA | WM831X_BKUP_BATT_DET_ENA; 618c2ecf20Sopenharmony_ci if (pdata->no_constant_voltage) 628c2ecf20Sopenharmony_ci reg |= WM831X_BKUP_CHG_MODE; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci switch (pdata->vlim) { 658c2ecf20Sopenharmony_ci case 2500: 668c2ecf20Sopenharmony_ci break; 678c2ecf20Sopenharmony_ci case 3100: 688c2ecf20Sopenharmony_ci reg |= WM831X_BKUP_CHG_VLIM; 698c2ecf20Sopenharmony_ci break; 708c2ecf20Sopenharmony_ci default: 718c2ecf20Sopenharmony_ci dev_err(wm831x->dev, "Invalid backup voltage limit %dmV\n", 728c2ecf20Sopenharmony_ci pdata->vlim); 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci switch (pdata->ilim) { 768c2ecf20Sopenharmony_ci case 100: 778c2ecf20Sopenharmony_ci break; 788c2ecf20Sopenharmony_ci case 200: 798c2ecf20Sopenharmony_ci reg |= 1; 808c2ecf20Sopenharmony_ci break; 818c2ecf20Sopenharmony_ci case 300: 828c2ecf20Sopenharmony_ci reg |= 2; 838c2ecf20Sopenharmony_ci break; 848c2ecf20Sopenharmony_ci case 400: 858c2ecf20Sopenharmony_ci reg |= 3; 868c2ecf20Sopenharmony_ci break; 878c2ecf20Sopenharmony_ci default: 888c2ecf20Sopenharmony_ci dev_err(wm831x->dev, "Invalid backup current limit %duA\n", 898c2ecf20Sopenharmony_ci pdata->ilim); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci ret = wm831x_reg_unlock(wm831x); 938c2ecf20Sopenharmony_ci if (ret != 0) { 948c2ecf20Sopenharmony_ci dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret); 958c2ecf20Sopenharmony_ci return; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci ret = wm831x_set_bits(wm831x, WM831X_BACKUP_CHARGER_CONTROL, 998c2ecf20Sopenharmony_ci WM831X_BKUP_CHG_ENA_MASK | 1008c2ecf20Sopenharmony_ci WM831X_BKUP_CHG_MODE_MASK | 1018c2ecf20Sopenharmony_ci WM831X_BKUP_BATT_DET_ENA_MASK | 1028c2ecf20Sopenharmony_ci WM831X_BKUP_CHG_VLIM_MASK | 1038c2ecf20Sopenharmony_ci WM831X_BKUP_CHG_ILIM_MASK, 1048c2ecf20Sopenharmony_ci reg); 1058c2ecf20Sopenharmony_ci if (ret != 0) 1068c2ecf20Sopenharmony_ci dev_err(wm831x->dev, 1078c2ecf20Sopenharmony_ci "Failed to set backup charger config: %d\n", ret); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci wm831x_reg_lock(wm831x); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int wm831x_backup_get_prop(struct power_supply *psy, 1138c2ecf20Sopenharmony_ci enum power_supply_property psp, 1148c2ecf20Sopenharmony_ci union power_supply_propval *val) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct wm831x_backup *devdata = dev_get_drvdata(psy->dev.parent); 1178c2ecf20Sopenharmony_ci struct wm831x *wm831x = devdata->wm831x; 1188c2ecf20Sopenharmony_ci int ret = 0; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci ret = wm831x_reg_read(wm831x, WM831X_BACKUP_CHARGER_CONTROL); 1218c2ecf20Sopenharmony_ci if (ret < 0) 1228c2ecf20Sopenharmony_ci return ret; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci switch (psp) { 1258c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 1268c2ecf20Sopenharmony_ci if (ret & WM831X_BKUP_CHG_STS) 1278c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_CHARGING; 1288c2ecf20Sopenharmony_ci else 1298c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 1338c2ecf20Sopenharmony_ci ret = wm831x_backup_read_voltage(wm831x, WM831X_AUX_BKUP_BATT, 1348c2ecf20Sopenharmony_ci val); 1358c2ecf20Sopenharmony_ci break; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 1388c2ecf20Sopenharmony_ci if (ret & WM831X_BKUP_CHG_STS) 1398c2ecf20Sopenharmony_ci val->intval = 1; 1408c2ecf20Sopenharmony_ci else 1418c2ecf20Sopenharmony_ci val->intval = 0; 1428c2ecf20Sopenharmony_ci break; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci default: 1458c2ecf20Sopenharmony_ci ret = -EINVAL; 1468c2ecf20Sopenharmony_ci break; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return ret; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic enum power_supply_property wm831x_backup_props[] = { 1538c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 1548c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 1558c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 1568c2ecf20Sopenharmony_ci}; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/********************************************************************* 1598c2ecf20Sopenharmony_ci * Initialisation 1608c2ecf20Sopenharmony_ci *********************************************************************/ 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic int wm831x_backup_probe(struct platform_device *pdev) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); 1658c2ecf20Sopenharmony_ci struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data; 1668c2ecf20Sopenharmony_ci struct wm831x_backup *devdata; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci devdata = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_backup), 1698c2ecf20Sopenharmony_ci GFP_KERNEL); 1708c2ecf20Sopenharmony_ci if (devdata == NULL) 1718c2ecf20Sopenharmony_ci return -ENOMEM; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci devdata->wm831x = wm831x; 1748c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, devdata); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* We ignore configuration failures since we can still read 1778c2ecf20Sopenharmony_ci * back the status without enabling the charger (which may 1788c2ecf20Sopenharmony_ci * already be enabled anyway). 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_ci wm831x_config_backup(wm831x); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (wm831x_pdata && wm831x_pdata->wm831x_num) 1838c2ecf20Sopenharmony_ci snprintf(devdata->name, sizeof(devdata->name), 1848c2ecf20Sopenharmony_ci "wm831x-backup.%d", wm831x_pdata->wm831x_num); 1858c2ecf20Sopenharmony_ci else 1868c2ecf20Sopenharmony_ci snprintf(devdata->name, sizeof(devdata->name), 1878c2ecf20Sopenharmony_ci "wm831x-backup"); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci devdata->backup_desc.name = devdata->name; 1908c2ecf20Sopenharmony_ci devdata->backup_desc.type = POWER_SUPPLY_TYPE_BATTERY; 1918c2ecf20Sopenharmony_ci devdata->backup_desc.properties = wm831x_backup_props; 1928c2ecf20Sopenharmony_ci devdata->backup_desc.num_properties = ARRAY_SIZE(wm831x_backup_props); 1938c2ecf20Sopenharmony_ci devdata->backup_desc.get_property = wm831x_backup_get_prop; 1948c2ecf20Sopenharmony_ci devdata->backup = power_supply_register(&pdev->dev, 1958c2ecf20Sopenharmony_ci &devdata->backup_desc, NULL); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(devdata->backup); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic int wm831x_backup_remove(struct platform_device *pdev) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct wm831x_backup *devdata = platform_get_drvdata(pdev); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci power_supply_unregister(devdata->backup); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic struct platform_driver wm831x_backup_driver = { 2108c2ecf20Sopenharmony_ci .probe = wm831x_backup_probe, 2118c2ecf20Sopenharmony_ci .remove = wm831x_backup_remove, 2128c2ecf20Sopenharmony_ci .driver = { 2138c2ecf20Sopenharmony_ci .name = "wm831x-backup", 2148c2ecf20Sopenharmony_ci }, 2158c2ecf20Sopenharmony_ci}; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cimodule_platform_driver(wm831x_backup_driver); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Backup battery charger driver for WM831x PMICs"); 2208c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 2218c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2228c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:wm831x-backup"); 223