/*
* Summit Microelectronics SMB347 Battery Charger Driver
*
* Copyright (C) 2011, Intel Corporation
*
* Authors: Bruce E. Robertson <bruce.e.robertson@intel.com>
* Mika Westerberg <mika.westerberg@linux.intel.com>
*
* 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/err.h>
#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/power_supply.h>
#include <linux/power/smb347-charger.h>
#include <linux/regmap.h>
/*
* Configuration registers. These are mirrored to volatile RAM and can be
* written once %CMD_A_ALLOW_WRITE is set in %CMD_A register. They will be
* reloaded from non-volatile registers after POR.
*/
#define CFG_CHARGE_CURRENT 0x00
#define CFG_CHARGE_CURRENT_FCC_MASK 0xe0
#define CFG_CHARGE_CURRENT_FCC_SHIFT 5
#define CFG_CHARGE_CURRENT_PCC_MASK 0x18
#define CFG_CHARGE_CURRENT_PCC_SHIFT 3
#define CFG_CHARGE_CURRENT_TC_MASK 0x07
#define CFG_CURRENT_LIMIT 0x01
#define CFG_CURRENT_LIMIT_DC_MASK 0xf0
#define CFG_CURRENT_LIMIT_DC_SHIFT 4
#define CFG_CURRENT_LIMIT_USB_MASK 0x0f
#define CFG_FLOAT_VOLTAGE 0x03
#define CFG_FLOAT_VOLTAGE_FLOAT_MASK 0x3f
#define CFG_FLOAT_VOLTAGE_THRESHOLD_MASK 0xc0
#define CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT 6
#define CFG_STAT 0x05
#define CFG_STAT_DISABLED BIT(5)
#define CFG_STAT_ACTIVE_HIGH BIT(7)
#define CFG_PIN 0x06
#define CFG_PIN_EN_CTRL_MASK 0x60
#define CFG_PIN_EN_CTRL_ACTIVE_HIGH 0x40
#define CFG_PIN_EN_CTRL_ACTIVE_LOW 0x60
#define CFG_PIN_EN_APSD_IRQ BIT(1)
#define CFG_PIN_EN_CHARGER_ERROR BIT(2)
#define CFG_THERM 0x07
#define CFG_THERM_SOFT_HOT_COMPENSATION_MASK 0x03
#define CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT 0
#define CFG_THERM_SOFT_COLD_COMPENSATION_MASK 0x0c
#define CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT 2
#define CFG_THERM_MONITOR_DISABLED BIT(4)
#define CFG_SYSOK 0x08
#define CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED BIT(2)
#define CFG_OTHER 0x09
#define CFG_OTHER_RID_MASK 0xc0
#define CFG_OTHER_RID_ENABLED_AUTO_OTG 0xc0
#define CFG_OTG 0x0a
#define CFG_OTG_TEMP_THRESHOLD_MASK 0x30
#define CFG_OTG_TEMP_THRESHOLD_SHIFT 4
#define CFG_OTG_CC_COMPENSATION_MASK 0xc0
#define CFG_OTG_CC_COMPENSATION_SHIFT 6
#define CFG_TEMP_LIMIT 0x0b
#define CFG_TEMP_LIMIT_SOFT_HOT_MASK 0x03
#define CFG_TEMP_LIMIT_SOFT_HOT_SHIFT 0
#define CFG_TEMP_LIMIT_SOFT_COLD_MASK 0x0c
#define CFG_TEMP_LIMIT_SOFT_COLD_SHIFT 2
#define CFG_TEMP_LIMIT_HARD_HOT_MASK 0x30
#define CFG_TEMP_LIMIT_HARD_HOT_SHIFT 4
#define CFG_TEMP_LIMIT_HARD_COLD_MASK 0xc0
#define CFG_TEMP_LIMIT_HARD_COLD_SHIFT 6
#define CFG_FAULT_IRQ 0x0c
#define CFG_FAULT_IRQ_DCIN_UV BIT(2)
#define CFG_STATUS_IRQ 0x0d
#define CFG_STATUS_IRQ_TERMINATION_OR_TAPER BIT(4)
#define CFG_ADDRESS 0x0e
/* Command registers */
#define CMD_A 0x30
#define CMD_A_CHG_ENABLED BIT(1)
#define CMD_A_SUSPEND_ENABLED BIT(2)
#define CMD_A_ALLOW_WRITE BIT(7)
#define CMD_B 0x31
#define CMD_C 0x33
/* Interrupt Status registers */
#define IRQSTAT_A 0x35
#define IRQSTAT_C 0x37
#define IRQSTAT_C_TERMINATION_STAT BIT(0)
#define IRQSTAT_C_TERMINATION_IRQ BIT(1)
#define IRQSTAT_C_TAPER_IRQ BIT(3)
#define IRQSTAT_E 0x39
#define IRQSTAT_E_USBIN_UV_STAT BIT(0)
#define IRQSTAT_E_USBIN_UV_IRQ BIT(1)
#define IRQSTAT_E_DCIN_UV_STAT BIT(4)
#define IRQSTAT_E_DCIN_UV_IRQ BIT(5)
#define IRQSTAT_F 0x3a
/* Status registers */
#define STAT_A 0x3b
#define STAT_A_FLOAT_VOLTAGE_MASK 0x3f
#define STAT_B 0x3c
#define STAT_C 0x3d
#define STAT_C_CHG_ENABLED BIT(0)
#define STAT_C_CHG_MASK 0x06
#define STAT_C_CHG_SHIFT 1
#define STAT_C_CHARGER_ERROR BIT(6)
#define STAT_E 0x3f
#define SMB347_MAX_REGISTER 0x3f
/**
* struct smb347_charger - smb347 charger instance
* @lock: protects concurrent access to online variables
* @dev: pointer to device
* @regmap: pointer to driver regmap
* @mains: power_supply instance for AC/DC power
* @usb: power_supply instance for USB power
* @battery: power_supply instance for battery
* @mains_online: is AC/DC input connected
* @usb_online: is USB input connected
* @charging_enabled: is charging enabled
* @pdata: pointer to platform data
*/
struct smb347_charger {
struct mutex lock;
struct device *dev;
struct regmap *regmap;
struct power_supply mains;
struct power_supply usb;
struct power_supply battery;
bool mains_online;
bool usb_online;
bool charging_enabled;
const struct smb347_charger_platform_data *pdata;
};
/* Fast charge current in uA */
static const unsigned int fcc_tbl[] = {
700000,
900000,
1200000,
1500000,
1800000,
2000000,
2200000,
2500000,
};
/* Pre-charge current in uA */
static const unsigned int pcc_tbl[] = {
100000,
150000,
200000,
250000,
};
/* Termination current in uA */
static const unsigned int tc_tbl[] = {
37500,
50000,
100000,
150000,
200000,
250000,
500000,
600000,
};
/* Input current limit in uA */
static const unsigned int icl_tbl[] = {
300000,
500000,
700000,
900000,
1200000,
1500000,
1800000,
2000000,
2200000,
2500000,
};
/* Charge current compensation in uA */
static const unsigned int ccc_tbl[] = {
250000,
700000,
900000,
1200000,
};
/* Convert current to register value using lookup table */
static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val)
{
size_t i;
for (i = 0; i < size; i++)
if (val < tbl[i])
break;
return i > 0 ? i - 1 : -EINVAL;
}
/**
* smb347_update_ps_status - refreshes the power source status