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