/*
* Copyright 2012 ST Ericsson.
*
* Power supply driver for ST Ericsson pm2xxx_charger charger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/completion.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/workqueue.h>
#include <linux/kobject.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500/ab8500-bm.h>
#include <linux/mfd/abx500/ab8500-gpadc.h>
#include <linux/mfd/abx500/ux500_chargalg.h>
#include <linux/pm2301_charger.h>
#include <linux/gpio.h>
#include "pm2301_charger.h"
#define to_pm2xxx_charger_ac_device_info(x) container_of((x), \
struct pm2xxx_charger, ac_chg)
static int pm2xxx_interrupt_registers[] = {
PM2XXX_REG_INT1,
PM2XXX_REG_INT2,
PM2XXX_REG_INT3,
PM2XXX_REG_INT4,
PM2XXX_REG_INT5,
PM2XXX_REG_INT6,
};
static enum power_supply_property pm2xxx_charger_ac_props[] = {
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_VOLTAGE_AVG,
};
static int pm2xxx_charger_voltage_map[] = {
3500,
3525,
3550,
3575,
3600,
3625,
3650,
3675,
3700,
3725,
3750,
3775,
3800,
3825,
3850,
3875,
3900,
3925,
3950,
3975,
4000,
4025,
4050,
4075,
4100,
4125,
4150,
4175,
4200,
4225,
4250,
4275,
4300,
};
static int pm2xxx_charger_current_map[] = {
200,
200,
400,
600,
800,
1000,
1200,
1400,
1600,
1800,
2000,
2200,
2400,
2600,
2800,
3000,
};
static const struct i2c_device_id pm2xxx_ident[] = {
{ "pm2301", 0 },
{ }
};
static void set_lpn_pin(struct pm2xxx_charger *pm2)
{
if (pm2->ac.charger_connected)
return;
gpio_set_value(pm2->lpn_pin, 1);
return;
}
static void clear_lpn_pin(struct pm2xxx_charger *pm2)
{
if (pm2->ac.charger_connected)
return;
gpio_set_value(pm2->lpn_pin, 0);
return;
}
static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
{
int ret;
/*
* When AC adaptor is unplugged, the host
* must put LPN high to be able to
* communicate by I2C with PM2301
* and receive I2C "acknowledge" from PM2301.
*/
mutex_lock(&pm2->lock);
set_lpn_pin(pm2);
ret = i2c_smbus_read_i2c_block_data(pm2->config.pm2xxx_i2c, reg,
1, val);
if (ret < 0)
dev_err(pm2->dev, "Error reading register at 0x%x\n", reg);
else
ret = 0;
clear_lpn_pin(pm2);
mutex_unlock(&pm2->lock);
return ret;
}
static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val)
{
int ret;
/*
* When AC adaptor is unplugged, the host
* must put LPN high to be able to
* communicate by I2C with PM2301
* and receive I2C "acknowledge" from PM2301.
*/
mutex_lock(&pm2->lock);
set_lpn_pin(pm2);
ret = i2c_smbus_write_i2c_block_data(pm2->config.pm2xxx_i2c, reg,
1, &val);
if (ret < 0)
dev_err(pm2->dev, "Error writing register at 0x%x\n", reg);
else
ret = 0;
clear_lpn_pin(pm2);
mutex_unlock(&pm2->lock);
return ret;
}
static int pm2xxx_charging_enable_mngt(struct pm2xxx_charger *pm2)
{
int ret;
/* Enable charging */
ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2,
(