1e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar/* 2e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * Generic battery driver code using IIO 3e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * Copyright (C) 2012, Anish Kumar <anish198519851985@gmail.com> 4e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * based on jz4740-battery.c 5e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * based on s3c_adc_battery.c 6e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * 7e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * This file is subject to the terms and conditions of the GNU General Public 8e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * License. See the file COPYING in the main directory of this archive for 9e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * more details. 10e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * 11e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar */ 12e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#include <linux/interrupt.h> 13e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#include <linux/platform_device.h> 14e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#include <linux/power_supply.h> 15e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#include <linux/gpio.h> 16e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#include <linux/err.h> 17e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#include <linux/timer.h> 18e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#include <linux/jiffies.h> 19e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#include <linux/errno.h> 20e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#include <linux/init.h> 21e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#include <linux/module.h> 22e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#include <linux/slab.h> 23e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#include <linux/iio/consumer.h> 24e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#include <linux/iio/types.h> 25e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#include <linux/power/generic-adc-battery.h> 26e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 27e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#define JITTER_DEFAULT 10 /* hope 10ms is enough */ 28e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 29e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarenum gab_chan_type { 30e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar GAB_VOLTAGE = 0, 31e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar GAB_CURRENT, 32e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar GAB_POWER, 33e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar GAB_MAX_CHAN_TYPE 34e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar}; 35e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 36e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar/* 37e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * gab_chan_name suggests the standard channel names for commonly used 38e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * channel types. 39e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar */ 40e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarstatic const char *const gab_chan_name[] = { 41e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar [GAB_VOLTAGE] = "voltage", 42e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar [GAB_CURRENT] = "current", 43e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar [GAB_POWER] = "power", 44e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar}; 45e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 46e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarstruct gab { 47e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct power_supply psy; 48e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct iio_channel *channel[GAB_MAX_CHAN_TYPE]; 49e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct gab_platform_data *pdata; 50e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct delayed_work bat_work; 51e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar int level; 52e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar int status; 53e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar bool cable_plugged; 54e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar}; 55e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 56e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarstatic struct gab *to_generic_bat(struct power_supply *psy) 57e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar{ 58e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return container_of(psy, struct gab, psy); 59e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar} 60e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 61e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarstatic void gab_ext_power_changed(struct power_supply *psy) 62e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar{ 63e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct gab *adc_bat = to_generic_bat(psy); 64e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 65e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar schedule_delayed_work(&adc_bat->bat_work, msecs_to_jiffies(0)); 66e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar} 67e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 68e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarstatic const enum power_supply_property gab_props[] = { 69e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar POWER_SUPPLY_PROP_STATUS, 70e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 71e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN, 72e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar POWER_SUPPLY_PROP_CHARGE_NOW, 73e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar POWER_SUPPLY_PROP_VOLTAGE_NOW, 74e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar POWER_SUPPLY_PROP_CURRENT_NOW, 75e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar POWER_SUPPLY_PROP_TECHNOLOGY, 76e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 77e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 78e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar POWER_SUPPLY_PROP_MODEL_NAME, 79e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar}; 80e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 81e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar/* 82e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * This properties are set based on the received platform data and this 83e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * should correspond one-to-one with enum chan_type. 84e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar */ 85e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarstatic const enum power_supply_property gab_dyn_props[] = { 86e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar POWER_SUPPLY_PROP_VOLTAGE_NOW, 87e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar POWER_SUPPLY_PROP_CURRENT_NOW, 88e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar POWER_SUPPLY_PROP_POWER_NOW, 89e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar}; 90e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 91e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarstatic bool gab_charge_finished(struct gab *adc_bat) 92e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar{ 93e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct gab_platform_data *pdata = adc_bat->pdata; 94e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar bool ret = gpio_get_value(pdata->gpio_charge_finished); 95e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar bool inv = pdata->gpio_inverted; 96e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 97e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar if (!gpio_is_valid(pdata->gpio_charge_finished)) 98e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return false; 99e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return ret ^ inv; 100e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar} 101e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 102e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarstatic int gab_get_status(struct gab *adc_bat) 103e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar{ 104e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct gab_platform_data *pdata = adc_bat->pdata; 105e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct power_supply_info *bat_info; 106e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 107e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar bat_info = &pdata->battery_info; 108e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar if (adc_bat->level == bat_info->charge_full_design) 109e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return POWER_SUPPLY_STATUS_FULL; 110e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return adc_bat->status; 111e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar} 112e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 113e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarstatic enum gab_chan_type gab_prop_to_chan(enum power_supply_property psp) 114e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar{ 115e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar switch (psp) { 116e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar case POWER_SUPPLY_PROP_POWER_NOW: 117e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return GAB_POWER; 118e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar case POWER_SUPPLY_PROP_VOLTAGE_NOW: 119e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return GAB_VOLTAGE; 120e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar case POWER_SUPPLY_PROP_CURRENT_NOW: 121e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return GAB_CURRENT; 122e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar default: 123e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar WARN_ON(1); 124e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar break; 125e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar } 126e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return GAB_POWER; 127e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar} 128e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 129e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarstatic int read_channel(struct gab *adc_bat, enum power_supply_property psp, 130e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar int *result) 131e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar{ 132e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar int ret; 133e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar int chan_index; 134e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 135e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar chan_index = gab_prop_to_chan(psp); 136e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar ret = iio_read_channel_processed(adc_bat->channel[chan_index], 137e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar result); 138e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar if (ret < 0) 139e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar pr_err("read channel error\n"); 140e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return ret; 141e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar} 142e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 143e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarstatic int gab_get_property(struct power_supply *psy, 144e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar enum power_supply_property psp, union power_supply_propval *val) 145e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar{ 146e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct gab *adc_bat; 147e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct gab_platform_data *pdata; 148e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct power_supply_info *bat_info; 149e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar int result = 0; 150e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar int ret = 0; 151e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 152e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar adc_bat = to_generic_bat(psy); 153e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar if (!adc_bat) { 154e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar dev_err(psy->dev, "no battery infos ?!\n"); 155e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return -EINVAL; 156e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar } 157e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar pdata = adc_bat->pdata; 158e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar bat_info = &pdata->battery_info; 159e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 160e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar switch (psp) { 161e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar case POWER_SUPPLY_PROP_STATUS: 162e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar gab_get_status(adc_bat); 163e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar break; 164e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN: 165e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar val->intval = 0; 166e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar break; 167e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar case POWER_SUPPLY_PROP_CHARGE_NOW: 168e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar val->intval = pdata->cal_charge(result); 169e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar break; 170e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar case POWER_SUPPLY_PROP_VOLTAGE_NOW: 171e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar case POWER_SUPPLY_PROP_CURRENT_NOW: 172e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar case POWER_SUPPLY_PROP_POWER_NOW: 173e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar ret = read_channel(adc_bat, psp, &result); 174e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar if (ret < 0) 175e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar goto err; 176e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar val->intval = result; 177e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar break; 178e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar case POWER_SUPPLY_PROP_TECHNOLOGY: 179e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar val->intval = bat_info->technology; 180e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar break; 181e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 182e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar val->intval = bat_info->voltage_min_design; 183e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar break; 184e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 185e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar val->intval = bat_info->voltage_max_design; 186e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar break; 187e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 188e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar val->intval = bat_info->charge_full_design; 189e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar break; 190e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar case POWER_SUPPLY_PROP_MODEL_NAME: 191e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar val->strval = bat_info->name; 192e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar break; 193e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar default: 194e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return -EINVAL; 195e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar } 196e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarerr: 197e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return ret; 198e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar} 199e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 200e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarstatic void gab_work(struct work_struct *work) 201e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar{ 202e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct gab *adc_bat; 203e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct gab_platform_data *pdata; 204e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct delayed_work *delayed_work; 205e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar bool is_plugged; 206e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar int status; 207e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 208e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar delayed_work = container_of(work, struct delayed_work, work); 209e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar adc_bat = container_of(delayed_work, struct gab, bat_work); 210e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar pdata = adc_bat->pdata; 211e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar status = adc_bat->status; 212e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 213e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar is_plugged = power_supply_am_i_supplied(&adc_bat->psy); 214e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar adc_bat->cable_plugged = is_plugged; 215e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 216e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar if (!is_plugged) 217e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar adc_bat->status = POWER_SUPPLY_STATUS_DISCHARGING; 218e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar else if (gab_charge_finished(adc_bat)) 219e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar adc_bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING; 220e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar else 221e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar adc_bat->status = POWER_SUPPLY_STATUS_CHARGING; 222e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 223e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar if (status != adc_bat->status) 224e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar power_supply_changed(&adc_bat->psy); 225e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar} 226e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 227e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarstatic irqreturn_t gab_charged(int irq, void *dev_id) 228e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar{ 229e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct gab *adc_bat = dev_id; 230e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct gab_platform_data *pdata = adc_bat->pdata; 231e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar int delay; 232e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 233e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar delay = pdata->jitter_delay ? pdata->jitter_delay : JITTER_DEFAULT; 234e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar schedule_delayed_work(&adc_bat->bat_work, 235e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar msecs_to_jiffies(delay)); 236e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return IRQ_HANDLED; 237e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar} 238e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 239c8afa6406e60aec6ff90033e5ffe41a206609296Bill Pembertonstatic int gab_probe(struct platform_device *pdev) 240e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar{ 241e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct gab *adc_bat; 242e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct power_supply *psy; 243e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct gab_platform_data *pdata = pdev->dev.platform_data; 244e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar enum power_supply_property *properties; 245e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar int ret = 0; 246e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar int chan; 247e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar int index = 0; 248e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 249e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar adc_bat = devm_kzalloc(&pdev->dev, sizeof(*adc_bat), GFP_KERNEL); 250e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar if (!adc_bat) { 251e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar dev_err(&pdev->dev, "failed to allocate memory\n"); 252e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return -ENOMEM; 253e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar } 254e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 255e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar psy = &adc_bat->psy; 256e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar psy->name = pdata->battery_info.name; 257e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 258e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar /* bootup default values for the battery */ 259e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar adc_bat->cable_plugged = false; 260e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar adc_bat->status = POWER_SUPPLY_STATUS_DISCHARGING; 261e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar psy->type = POWER_SUPPLY_TYPE_BATTERY; 262e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar psy->get_property = gab_get_property; 263e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar psy->external_power_changed = gab_ext_power_changed; 264e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar adc_bat->pdata = pdata; 265e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 266e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar /* 267e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * copying the static properties and allocating extra memory for holding 268e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * the extra configurable properties received from platform data. 269e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar */ 270e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar psy->properties = kcalloc(ARRAY_SIZE(gab_props) + 271e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar ARRAY_SIZE(gab_chan_name), 272e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar sizeof(*psy->properties), GFP_KERNEL); 273e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar if (!psy->properties) { 274e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar ret = -ENOMEM; 275e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar goto first_mem_fail; 276e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar } 277e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 278e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar memcpy(psy->properties, gab_props, sizeof(gab_props)); 279a77d60aec70183faa68b7df4a3fa89fbaf592cf5Dan Carpenter properties = (enum power_supply_property *) 280a77d60aec70183faa68b7df4a3fa89fbaf592cf5Dan Carpenter ((char *)psy->properties + sizeof(gab_props)); 281e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 282e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar /* 283e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * getting channel from iio and copying the battery properties 284e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * based on the channel supported by consumer device. 285e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar */ 286e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) { 2875aa57f0a655276f62683c0cc714cd6328d98e08aGuenter Roeck adc_bat->channel[chan] = iio_channel_get(&pdev->dev, 2885aa57f0a655276f62683c0cc714cd6328d98e08aGuenter Roeck gab_chan_name[chan]); 289e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar if (IS_ERR(adc_bat->channel[chan])) { 290e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar ret = PTR_ERR(adc_bat->channel[chan]); 29164d26f225fefe06c870634e7bfe026a063e7f776Dan Carpenter adc_bat->channel[chan] = NULL; 292e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar } else { 293e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar /* copying properties for supported channels only */ 294e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar memcpy(properties + sizeof(*(psy->properties)) * index, 295e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar &gab_dyn_props[chan], 296e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar sizeof(gab_dyn_props[chan])); 297e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar index++; 298e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar } 299e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar } 300e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 301e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar /* none of the channels are supported so let's bail out */ 302d211c6e82435dfa4ae9f4d80bb2ee75930bde5d4Axel Lin if (index == 0) { 303d211c6e82435dfa4ae9f4d80bb2ee75930bde5d4Axel Lin ret = -ENODEV; 304e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar goto second_mem_fail; 305d211c6e82435dfa4ae9f4d80bb2ee75930bde5d4Axel Lin } 306e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 307e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar /* 308e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * Total number of properties is equal to static properties 309e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * plus the dynamic properties.Some properties may not be set 310e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * as come channels may be not be supported by the device.So 311e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar * we need to take care of that. 312e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar */ 313e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar psy->num_properties = ARRAY_SIZE(gab_props) + index; 314e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 315e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar ret = power_supply_register(&pdev->dev, psy); 316e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar if (ret) 317e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar goto err_reg_fail; 318e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 319e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar INIT_DELAYED_WORK(&adc_bat->bat_work, gab_work); 320e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 321e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar if (gpio_is_valid(pdata->gpio_charge_finished)) { 322e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar int irq; 323e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar ret = gpio_request(pdata->gpio_charge_finished, "charged"); 324e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar if (ret) 325e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar goto gpio_req_fail; 326e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 327e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar irq = gpio_to_irq(pdata->gpio_charge_finished); 328e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar ret = request_any_context_irq(irq, gab_charged, 329e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 330e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar "battery charged", adc_bat); 331a5af092245a33965a4b089e8ba489c2db0f7db16Axel Lin if (ret < 0) 332e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar goto err_gpio; 333e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar } 334e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 335e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar platform_set_drvdata(pdev, adc_bat); 336e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 337e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar /* Schedule timer to check current status */ 338e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar schedule_delayed_work(&adc_bat->bat_work, 339e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar msecs_to_jiffies(0)); 340e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return 0; 341e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 342e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarerr_gpio: 343e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar gpio_free(pdata->gpio_charge_finished); 344e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumargpio_req_fail: 345e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar power_supply_unregister(psy); 346e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarerr_reg_fail: 34764d26f225fefe06c870634e7bfe026a063e7f776Dan Carpenter for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) { 34864d26f225fefe06c870634e7bfe026a063e7f776Dan Carpenter if (adc_bat->channel[chan]) 34964d26f225fefe06c870634e7bfe026a063e7f776Dan Carpenter iio_channel_release(adc_bat->channel[chan]); 35064d26f225fefe06c870634e7bfe026a063e7f776Dan Carpenter } 351e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarsecond_mem_fail: 352e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar kfree(psy->properties); 353e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarfirst_mem_fail: 354e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return ret; 355e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar} 356e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 357415ec69fb1861fc377c65cb30ddc76999891b8e1Bill Pembertonstatic int gab_remove(struct platform_device *pdev) 358e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar{ 359e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar int chan; 360e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct gab *adc_bat = platform_get_drvdata(pdev); 361e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct gab_platform_data *pdata = adc_bat->pdata; 362e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 363e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar power_supply_unregister(&adc_bat->psy); 364e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 365e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar if (gpio_is_valid(pdata->gpio_charge_finished)) { 366e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar free_irq(gpio_to_irq(pdata->gpio_charge_finished), adc_bat); 367e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar gpio_free(pdata->gpio_charge_finished); 368e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar } 369e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 37064d26f225fefe06c870634e7bfe026a063e7f776Dan Carpenter for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) { 37164d26f225fefe06c870634e7bfe026a063e7f776Dan Carpenter if (adc_bat->channel[chan]) 37264d26f225fefe06c870634e7bfe026a063e7f776Dan Carpenter iio_channel_release(adc_bat->channel[chan]); 37364d26f225fefe06c870634e7bfe026a063e7f776Dan Carpenter } 374e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 375e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar kfree(adc_bat->psy.properties); 376e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar cancel_delayed_work(&adc_bat->bat_work); 377e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return 0; 378e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar} 379e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 380e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#ifdef CONFIG_PM 381e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarstatic int gab_suspend(struct device *dev) 382e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar{ 383e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct gab *adc_bat = dev_get_drvdata(dev); 384e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 385e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar cancel_delayed_work_sync(&adc_bat->bat_work); 386e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar adc_bat->status = POWER_SUPPLY_STATUS_UNKNOWN; 387e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return 0; 388e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar} 389e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 390e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarstatic int gab_resume(struct device *dev) 391e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar{ 392e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct gab *adc_bat = dev_get_drvdata(dev); 393e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar struct gab_platform_data *pdata = adc_bat->pdata; 394e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar int delay; 395e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 396e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar delay = pdata->jitter_delay ? pdata->jitter_delay : JITTER_DEFAULT; 397e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 398e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar /* Schedule timer to check current status */ 399e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar schedule_delayed_work(&adc_bat->bat_work, 400e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar msecs_to_jiffies(delay)); 401e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar return 0; 402e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar} 403e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 404e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarstatic const struct dev_pm_ops gab_pm_ops = { 405e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar .suspend = gab_suspend, 406e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar .resume = gab_resume, 407e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar}; 408e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 409e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#define GAB_PM_OPS (&gab_pm_ops) 410e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#else 411e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#define GAB_PM_OPS (NULL) 412e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar#endif 413e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 414e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarstatic struct platform_driver gab_driver = { 415e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar .driver = { 416e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar .name = "generic-adc-battery", 417e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar .owner = THIS_MODULE, 418e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar .pm = GAB_PM_OPS 419e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar }, 420e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar .probe = gab_probe, 42128ea73f4c67cb3dd8c972b21d9fdf84ea78d6daaBill Pemberton .remove = gab_remove, 422e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar}; 423e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarmodule_platform_driver(gab_driver); 424e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumar 425e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarMODULE_AUTHOR("anish kumar <anish198519851985@gmail.com>"); 426e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarMODULE_DESCRIPTION("generic battery driver using IIO"); 427e60fea794e6ecb9ea4df2623c9498412afe31d4danish kumarMODULE_LICENSE("GPL"); 428