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