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