1/* 2 * Backup battery driver for Wolfson Microelectronics wm831x PMICs 3 * 4 * Copyright 2009 Wolfson Microelectronics PLC. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 */ 10 11#include <linux/module.h> 12#include <linux/err.h> 13#include <linux/platform_device.h> 14#include <linux/power_supply.h> 15#include <linux/slab.h> 16 17#include <linux/mfd/wm831x/core.h> 18#include <linux/mfd/wm831x/auxadc.h> 19#include <linux/mfd/wm831x/pmu.h> 20#include <linux/mfd/wm831x/pdata.h> 21 22struct wm831x_backup { 23 struct wm831x *wm831x; 24 struct power_supply backup; 25 char name[20]; 26}; 27 28static int wm831x_backup_read_voltage(struct wm831x *wm831x, 29 enum wm831x_auxadc src, 30 union power_supply_propval *val) 31{ 32 int ret; 33 34 ret = wm831x_auxadc_read_uv(wm831x, src); 35 if (ret >= 0) 36 val->intval = ret; 37 38 return ret; 39} 40 41/********************************************************************* 42 * Backup supply properties 43 *********************************************************************/ 44 45static void wm831x_config_backup(struct wm831x *wm831x) 46{ 47 struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data; 48 struct wm831x_backup_pdata *pdata; 49 int ret, reg; 50 51 if (!wm831x_pdata || !wm831x_pdata->backup) { 52 dev_warn(wm831x->dev, 53 "No backup battery charger configuration\n"); 54 return; 55 } 56 57 pdata = wm831x_pdata->backup; 58 59 reg = 0; 60 61 if (pdata->charger_enable) 62 reg |= WM831X_BKUP_CHG_ENA | WM831X_BKUP_BATT_DET_ENA; 63 if (pdata->no_constant_voltage) 64 reg |= WM831X_BKUP_CHG_MODE; 65 66 switch (pdata->vlim) { 67 case 2500: 68 break; 69 case 3100: 70 reg |= WM831X_BKUP_CHG_VLIM; 71 break; 72 default: 73 dev_err(wm831x->dev, "Invalid backup voltage limit %dmV\n", 74 pdata->vlim); 75 } 76 77 switch (pdata->ilim) { 78 case 100: 79 break; 80 case 200: 81 reg |= 1; 82 break; 83 case 300: 84 reg |= 2; 85 break; 86 case 400: 87 reg |= 3; 88 break; 89 default: 90 dev_err(wm831x->dev, "Invalid backup current limit %duA\n", 91 pdata->ilim); 92 } 93 94 ret = wm831x_reg_unlock(wm831x); 95 if (ret != 0) { 96 dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret); 97 return; 98 } 99 100 ret = wm831x_set_bits(wm831x, WM831X_BACKUP_CHARGER_CONTROL, 101 WM831X_BKUP_CHG_ENA_MASK | 102 WM831X_BKUP_CHG_MODE_MASK | 103 WM831X_BKUP_BATT_DET_ENA_MASK | 104 WM831X_BKUP_CHG_VLIM_MASK | 105 WM831X_BKUP_CHG_ILIM_MASK, 106 reg); 107 if (ret != 0) 108 dev_err(wm831x->dev, 109 "Failed to set backup charger config: %d\n", ret); 110 111 wm831x_reg_lock(wm831x); 112} 113 114static int wm831x_backup_get_prop(struct power_supply *psy, 115 enum power_supply_property psp, 116 union power_supply_propval *val) 117{ 118 struct wm831x_backup *devdata = dev_get_drvdata(psy->dev->parent); 119 struct wm831x *wm831x = devdata->wm831x; 120 int ret = 0; 121 122 ret = wm831x_reg_read(wm831x, WM831X_BACKUP_CHARGER_CONTROL); 123 if (ret < 0) 124 return ret; 125 126 switch (psp) { 127 case POWER_SUPPLY_PROP_STATUS: 128 if (ret & WM831X_BKUP_CHG_STS) 129 val->intval = POWER_SUPPLY_STATUS_CHARGING; 130 else 131 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 132 break; 133 134 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 135 ret = wm831x_backup_read_voltage(wm831x, WM831X_AUX_BKUP_BATT, 136 val); 137 break; 138 139 case POWER_SUPPLY_PROP_PRESENT: 140 if (ret & WM831X_BKUP_CHG_STS) 141 val->intval = 1; 142 else 143 val->intval = 0; 144 break; 145 146 default: 147 ret = -EINVAL; 148 break; 149 } 150 151 return ret; 152} 153 154static enum power_supply_property wm831x_backup_props[] = { 155 POWER_SUPPLY_PROP_STATUS, 156 POWER_SUPPLY_PROP_VOLTAGE_NOW, 157 POWER_SUPPLY_PROP_PRESENT, 158}; 159 160/********************************************************************* 161 * Initialisation 162 *********************************************************************/ 163 164static int wm831x_backup_probe(struct platform_device *pdev) 165{ 166 struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); 167 struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data; 168 struct wm831x_backup *devdata; 169 struct power_supply *backup; 170 int ret; 171 172 devdata = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_backup), 173 GFP_KERNEL); 174 if (devdata == NULL) 175 return -ENOMEM; 176 177 devdata->wm831x = wm831x; 178 platform_set_drvdata(pdev, devdata); 179 180 backup = &devdata->backup; 181 182 /* We ignore configuration failures since we can still read 183 * back the status without enabling the charger (which may 184 * already be enabled anyway). 185 */ 186 wm831x_config_backup(wm831x); 187 188 if (wm831x_pdata && wm831x_pdata->wm831x_num) 189 snprintf(devdata->name, sizeof(devdata->name), 190 "wm831x-backup.%d", wm831x_pdata->wm831x_num); 191 else 192 snprintf(devdata->name, sizeof(devdata->name), 193 "wm831x-backup"); 194 195 backup->name = devdata->name; 196 backup->type = POWER_SUPPLY_TYPE_BATTERY; 197 backup->properties = wm831x_backup_props; 198 backup->num_properties = ARRAY_SIZE(wm831x_backup_props); 199 backup->get_property = wm831x_backup_get_prop; 200 ret = power_supply_register(&pdev->dev, backup); 201 202 return ret; 203} 204 205static int wm831x_backup_remove(struct platform_device *pdev) 206{ 207 struct wm831x_backup *devdata = platform_get_drvdata(pdev); 208 209 power_supply_unregister(&devdata->backup); 210 211 return 0; 212} 213 214static struct platform_driver wm831x_backup_driver = { 215 .probe = wm831x_backup_probe, 216 .remove = wm831x_backup_remove, 217 .driver = { 218 .name = "wm831x-backup", 219 }, 220}; 221 222module_platform_driver(wm831x_backup_driver); 223 224MODULE_DESCRIPTION("Backup battery charger driver for WM831x PMICs"); 225MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 226MODULE_LICENSE("GPL"); 227MODULE_ALIAS("platform:wm831x-backup"); 228