blob: 7e80fe9e41e8226b7686b0cd6b7f96a257d00bef [file] [log] [blame]
/*
* Base driver for Marvell 88PM800
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/switch.h>
#include <linux/mfd/core.h>
#include <linux/mfd/88pm80x.h>
#include <linux/of_device.h>
#include <linux/reboot.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/mfd/pm802.h>
#include <linux/mfd/pm813.h>
#include <linux/cputype.h>
#include <soc/asr/addr-map.h>
#include <soc/asr/regs-addr.h>
static DEFINE_MUTEX(audio_mode_mutex);
struct pm80x_chip *pm80x_chip_g;
int ramdump_pm80x_flag;
#define PM80X_POWER_PAGE_REG_60 (0x60)
#define PM802_BASE_PAGE_REG_15 (0x15)
#define PM803_BASE_PAGE_REG_E8 (0xE8)
#define PM813_FAULT_WAKEUP_EN (0x1 << 0)
#define PM813_FAULT_WAKEUP (0x1 << 3)
static DEFINE_MUTEX(pm802_adc_mutex);
static void ramdump_pm80x_flag_ctrl(int on)
{
int data;
if (!pm80x_chip_g) {
pr_err("error: pm80x_chip_g not inited\n");
return;
}
if (CHIP_PM802 == pm80x_chip_g->type) {
/*store the flag on BIT_7 of power page reg 0x60 */
regmap_read(pm80x_chip_g->regmap,
PM802_BASE_PAGE_REG_15, &data);
if (on)
data |= (0x1 << 0);
else
data &= ~(0x1 << 0);
regmap_write(pm80x_chip_g->regmap,
PM802_BASE_PAGE_REG_15, data);
} else if (CHIP_PM803 == pm80x_chip_g->type) {
/*store the flag on BIT_7 of power page reg 0x60 */
regmap_read(pm80x_chip_g->regmap,
PM803_BASE_PAGE_REG_E8, &data);
if (on)
data |= (0x1 << 0);
else
data &= ~(0x1 << 0);
regmap_write(pm80x_chip_g->regmap,
PM803_BASE_PAGE_REG_E8, data);
} else if (CHIP_PM813 == pm80x_chip_g->type) {
/*store the flag on BIT_0 of power page reg 0xF7 */
regmap_read(pm80x_chip_g->regmap,
PM813S_RTC_BLANK_REG4, &data);
if (on)
data |= (0x1 << 0);
else
data &= ~(0x1 << 0);
regmap_write(pm80x_chip_g->regmap,
PM813S_RTC_BLANK_REG4, data);
} else {
/*store the flag on BIT_7 of power page reg 0x60 */
regmap_read(pm80x_chip_g->subchip->regmap_power,
PM80X_POWER_PAGE_REG_60, &data);
if (on)
data |= (0x1 << 7);
else
data &= ~(0x1 << 7);
regmap_write(pm80x_chip_g->subchip->regmap_power,
PM80X_POWER_PAGE_REG_60, data);
}
}
int
ramdump_pm80x_flag_sysctl(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp,
loff_t *ppos)
{
int ret = 0;
ret = proc_dointvec(table, write, buffer, lenp, ppos);
if (ret || !write)
goto out;
if (ramdump_pm80x_flag)
ramdump_pm80x_flag_ctrl(1);
else
ramdump_pm80x_flag_ctrl(0);
out:
return ret;
}
void buck1_audio_mode_ctrl(int on)
{
int data;
static int refcount;
if (!pm80x_chip_g) {
pr_err("error: pm80x_chip_g not inited\n");
return;
}
if (CHIP_PM802 == pm80x_chip_g->type
|| CHIP_PM803 == pm80x_chip_g->type
|| CHIP_PM813 == pm80x_chip_g->type) {
pr_info("pm802/3/13 buck1 not support audio mode\n");
return;
}
mutex_lock(&audio_mode_mutex);
if (on) {
if (refcount == 0) {
regmap_read(pm80x_chip_g->regmap,
PM800_LOW_POWER2,
&data);
data |= PM800_AUDIO_MODE_ENA;
regmap_write(pm80x_chip_g->regmap,
PM800_LOW_POWER2,
data);
}
refcount++;
} else {
if (refcount == 1) {
regmap_read(pm80x_chip_g->regmap,
PM800_LOW_POWER2,
&data);
data &= ~PM800_AUDIO_MODE_ENA;
regmap_write(pm80x_chip_g->regmap,
PM800_LOW_POWER2,
data);
}
refcount--;
}
mutex_unlock(&audio_mode_mutex);
}
EXPORT_SYMBOL(buck1_audio_mode_ctrl);
/* set buck1 audio mode voltage as 0.8v*/
void set_buck1_audio_mode_vol(u32 vol)
{
int data;
if (!pm80x_chip_g) {
pr_err("error: pm80x_chip_g not inited\n");
return;
}
if (CHIP_PM802 == pm80x_chip_g->type
|| CHIP_PM803 == pm80x_chip_g->type
|| CHIP_PM813 == pm80x_chip_g->type) {
pr_info("pm802/3/13 buck1 not support audio mode\n");
return;
}
/* the proper range is 0~0x54, corresponding voltage is 0.6v~1.8v */
if (vol >= 0x55)
return;
regmap_read(pm80x_chip_g->subchip->regmap_power,
PM800_AUDIO_MODE_CONFIG1,
&data);
data &= ~(0x7f);
data |= ((vol & 0x7f) | (0x1 << 7));
regmap_write(pm80x_chip_g->subchip->regmap_power,
PM800_AUDIO_MODE_CONFIG1,
data);
}
EXPORT_SYMBOL(set_buck1_audio_mode_vol);
void buck1_sleepvol_control_for_gps(int on)
{
int data;
static int data_old;
static bool get_data_old;
if (!pm80x_chip_g) {
pr_err("error: pm80x_chip_g not inited\n");
return;
}
if (CHIP_PM802 == pm80x_chip_g->type
|| CHIP_PM803 == pm80x_chip_g->type
|| CHIP_PM813 == pm80x_chip_g->type) {
pr_info("skip sleep volt setting for asr pmic\n");
return;
}
/*
* Helan2 need to set buck1 sleep voltage tobe 0.95v when power on gps,
* and set back to the old sleep voltage when power off gps.
* This function provide such an interface to satisfy it.
*/
if (!get_data_old) {
regmap_read(pm80x_chip_g->subchip->regmap_power,
PM800_BUCK1_SLEEP,
&data_old);
get_data_old = true;
}
if (on) {
/*
* this means gps power on
* buck1 sleep voltage is set to be 0.95v
*/
data = data_old;
data &= ~(0x7f);
data |= 0x1c;
regmap_write(pm80x_chip_g->subchip->regmap_power,
PM800_BUCK1_SLEEP,
data);
} else {
data = data_old;
regmap_write(pm80x_chip_g->subchip->regmap_power,
PM800_BUCK1_SLEEP,
data);
}
}
EXPORT_SYMBOL(buck1_sleepvol_control_for_gps);
static DEFINE_MUTEX(buck2_slp_ctrl_mutex);
static void pm802_buck2_sleepmode_control(int on)
{
int data;
static int refcount;
if (!pm80x_chip_g) {
pr_err("error: pm80x_chip_g not inited\n");
return;
}
mutex_lock(&buck2_slp_ctrl_mutex);
if (on) {
/*
* Disable VBUCK2's sleep mode, allow big current output
* even system enter to suspend
* As SD8787/SLIC use it as 1.8V supply, it would still work
* druing suspend and need much current
*
*/
if (refcount == 0) {
regmap_read(pm80x_chip_g->subchip->regmap_power,
PM802_BUCK_SLP2, &data);
data = (data & ~PM802_BUCK_SLP_MASK) |
(PM802_BUCK_SLP_PWR_ACT2 << PM802_BUCK_SLP_SHIFT);
regmap_write(pm80x_chip_g->subchip->regmap_power,
PM802_BUCK_SLP2,
data);
}
refcount++;
} else {
/*
* Enable VBUCK2's sleep mode again (Only 5mA ability)
* If SD8787/SLIC is power off, VBUCK2 sleep mode has not side
* impact to Sd8787, but has benifit to whole power consumption
*/
if (refcount == 1) {
regmap_read(pm80x_chip_g->subchip->regmap_power,
PM802_BUCK_SLP2, &data);
data = (data & ~PM802_BUCK_SLP_MASK) |
(PM802_BUCK_SLP_PWR_LOW << PM802_BUCK_SLP_SHIFT);
regmap_write(pm80x_chip_g->subchip->regmap_power,
PM802_BUCK_SLP2,
data);
}
refcount--;
}
mutex_unlock(&buck2_slp_ctrl_mutex);
}
static void buck2_sleepmode_control(int on)
{
int data;
static int refcount;
if (!pm80x_chip_g) {
pr_err("error: pm80x_chip_g not inited\n");
return;
}
if (CHIP_PM813 == pm80x_chip_g->type
|| CHIP_PM803 == pm80x_chip_g->type) {
pr_info("skip sleep volt setting for pm813/pm803\n");
return;
}
mutex_lock(&buck2_slp_ctrl_mutex);
if (on) {
/*
* Disable VBUCK2's sleep mode, allow big current output
* even system enter to suspend
* As SD8787/SLIC use it as 1.8V supply, it would still work
* druing suspend and need much current
*
*/
if (refcount == 0) {
regmap_read(pm80x_chip_g->subchip->regmap_power,
PM800_BUCK_SLP1, &data);
data = (data & ~PM800_BUCK2_SLP1_MASK) |
(PM800_BUCK_SLP_PWR_ACT2 << PM800_BUCK2_SLP1_SHIFT);
regmap_write(pm80x_chip_g->subchip->regmap_power,
PM800_BUCK_SLP1,
data);
}
refcount++;
} else {
/*
* Enable VBUCK2's sleep mode again (Only 5mA ability)
* If SD8787/SLIC is power off, VBUCK2 sleep mode has not side
* impact to Sd8787, but has benifit to whole power consumption
*/
if (refcount == 1) {
regmap_read(pm80x_chip_g->subchip->regmap_power,
PM800_BUCK_SLP1, &data);
data = (data & ~PM800_BUCK2_SLP1_MASK) |
(PM800_BUCK_SLP_PWR_LOW << PM800_BUCK2_SLP1_SHIFT);
regmap_write(pm80x_chip_g->subchip->regmap_power,
PM800_BUCK_SLP1,
data);
}
refcount--;
}
mutex_unlock(&buck2_slp_ctrl_mutex);
}
void buck2_sleepmode_control_for_wifi(int on)
{
if (!pm80x_chip_g) {
pr_err("error: pm80x_chip_g not inited\n");
return;
}
if (CHIP_PM803 == pm80x_chip_g->type)
return;
if (CHIP_PM813 == pm80x_chip_g->type)
return;
if (CHIP_PM802 == pm80x_chip_g->type)
pm802_buck2_sleepmode_control(on);
else
buck2_sleepmode_control(on);
}
EXPORT_SYMBOL(buck2_sleepmode_control_for_wifi);
static void ldo8_sleepmode_control(int on)
{
int data;
if (!pm80x_chip_g) {
pr_err("error: pm80x_chip_g not inited\n");
return;
}
if (CHIP_PM801 == pm80x_chip_g->type ||
CHIP_PM802 == pm80x_chip_g->type ||
CHIP_PM803 == pm80x_chip_g->type)
return;
if (on) {
/* Disable LDO8's sleep mode */
regmap_read(pm80x_chip_g->subchip->regmap_power,
PM800_LDO_SLP2, &data);
data |= (0x3 << 6);
regmap_write(pm80x_chip_g->subchip->regmap_power,
PM800_LDO_SLP2,
data);
} else {
/* Enable LDO8's sleep mode */
regmap_read(pm80x_chip_g->subchip->regmap_power,
PM800_LDO_SLP2, &data);
data = (data & ~(0x3 << 6)) | (0x2 << 6);
regmap_write(pm80x_chip_g->subchip->regmap_power,
PM800_LDO_SLP2,
data);
}
}
void buck2_ldo8_sleepmode_control_for_slic(int on)
{
buck2_sleepmode_control(on);
ldo8_sleepmode_control(on);
}
EXPORT_SYMBOL(buck2_ldo8_sleepmode_control_for_slic);
/* return gpadc voltage */
int get_gpadc_volt(struct pm80x_chip *chip, int gpadc_id)
{
int ret, volt;
unsigned char buf[2];
int gp_meas;
switch (gpadc_id) {
case PM800_GPADC0:
gp_meas = PM800_GPADC0_MEAS1;
break;
case PM800_GPADC1:
gp_meas = PM800_GPADC1_MEAS1;
break;
case PM800_GPADC2:
gp_meas = PM800_GPADC2_MEAS1;
break;
case PM800_GPADC3:
gp_meas = PM800_GPADC3_MEAS1;
break;
default:
dev_err(chip->dev, "get GPADC failed!\n");
return -EINVAL;
}
ret = regmap_bulk_read(chip->subchip->regmap_gpadc,
gp_meas, buf, 2);
if (ret < 0) {
dev_err(chip->dev, "Attention: failed to get volt!\n");
return -EINVAL;
}
volt = ((buf[0] & 0xff) << 4) | (buf[1] & 0x0f);
dev_dbg(chip->dev, "%s: volt value = 0x%x\n", __func__, volt);
/* volt = value * 1.4 * 1000 / (2^12) */
volt = ((volt & 0xfff) * 7 * 100) >> 11;
dev_dbg(chip->dev, "%s: voltage = %dmV\n", __func__, volt);
return volt;
}
/* return voltage via bias current from GPADC */
static int get_gpadc_bias_volt(struct pm80x_chip *chip, int gpadc_id, int bias)
{
int volt, data, gp_bias, gp_bias_shift;
switch (gpadc_id) {
case PM800_GPADC0:
gp_bias = PM800_GPADC_BIAS1;
gp_bias_shift = 0;
break;
case PM800_GPADC1:
gp_bias = PM800_GPADC_BIAS2;
gp_bias_shift = 0;
break;
case PM800_GPADC2:
gp_bias = PM800_GPADC_BIAS3;
gp_bias_shift = 0;
break;
case PM800_GPADC3:
gp_bias = PM800_GPADC_BIAS4;
gp_bias_shift = 0;
break;
default:
dev_err(chip->dev, "get GPADC failed!\n");
return -EINVAL;
}
/* get the register value */
if (bias > 76)
bias = 76;
if (bias < 1)
bias = 1;
bias = (bias - 1) / 5;
regmap_read(chip->subchip->regmap_gpadc, gp_bias, &data);
data &= ~(0xF << gp_bias_shift);
data |= (bias << gp_bias_shift);
regmap_write(chip->subchip->regmap_gpadc, gp_bias, data);
/* wait for voltage to be stable */
usleep_range(30, 40);
volt = get_gpadc_volt(chip, gpadc_id);
if ((volt < 0) || (volt > 1400)) {
dev_err(chip->dev, "%s return %dmV\n", __func__, volt);
return -EINVAL;
}
return volt;
}
static int pm801_get_batt_vol(struct pm80x_chip *chip, int *data)
{
int ret;
unsigned char buf[2];
if (!data)
return -EINVAL;
if (chip->type == CHIP_PM801) {
ret = regmap_bulk_read(chip->subchip->regmap_gpadc,
PM801_GPADC_VLDOIN_AVG1, buf, 2);
if (ret < 0)
return ret;
} else {
ret = regmap_bulk_read(chip->subchip->regmap_gpadc,
PM800_VBAT_AVG, buf, 2);
if (ret < 0)
return ret;
}
*data = ((buf[0] & 0xff) << 4) | (buf[1] & 0x0f);
/* measure(mv) = value * 4 * 1.4 *1000/(2^12) */
*data = ((*data & 0xfff) * 7 * 100) >> 9;
/*
* Calibrate vbat measurement only for un-trimed PMIC
* VBATmeas = VBATreal * gain + offset, so
* VBATreal = (VBATmeas - offset)/ gain;
* According to our test of vbat_sleep in bootup, the calculated
* gain as below:
* For Aruba 0.1, offset is -13.4ma, gain is 1.008.
* For Aruba 0.2, offset = -0.0087V and gain=1.007.
*/
/* data = (*data + 9) * 1000/1007; */
return 0;
}
static int pm802_get_batt_vol(struct pm80x_chip *chip, int *data)
{
int ret = 0;
unsigned char buf[2];
u32 adc_channel = 15;
if (!data)
return -EINVAL;
might_sleep();
mutex_lock(&pm802_adc_mutex);
regmap_update_bits(chip->subchip->regmap_gpadc, PM802_GPADC_CFG3, 0x7, ((adc_channel >> 3) & 0x3));
regmap_update_bits(chip->subchip->regmap_gpadc, PM802_GPADC_CFG2, (0x7 << 5), ((adc_channel & 0x7) << 5));
regmap_update_bits(chip->subchip->regmap_gpadc, PM802_GPADC_CFG3, (0x3 << 2), (0x3 << 2));
usleep_range(40, 40);
if (CHIP_PM813 == chip->type)
adc_channel = 5;
if (CHIP_PM813S_A1_ID == chip->chip_id
|| CHIP_PM813S_A0_ID == chip->chip_id)
ret = regmap_bulk_read(chip->subchip->regmap_gpadc,
PM813S_VLDOIN_MEAS1, buf, 2);
else
ret = regmap_bulk_read(chip->subchip->regmap_gpadc,
PM802_VLDOIN_MEAS1, buf, 2);
if (ret < 0)
goto out_ret;
if (CHIP_PM813 == chip->type)
*data = ((buf[0] & 0xff) << 4) | (buf[1] & 0x0f);
else
*data = ((buf[1] & 0x0f) << 8) | (buf[0] & 0xff);
/* measure(mv) = value * 6.5 *1000/(2^12) */
*data = (*data - chip->adc_sample_offset) * (13000 + chip->adc_vref_offset);
if (CHIP_PM813S_A1_ID == chip->chip_id
|| CHIP_PM813S_A0_ID == chip->chip_id)
*data = (*data >> 6) / (128 * 1);
else
*data = (*data >> 6) / (129 * 1);
if (*data > 6500)
*data = 6500;
if (*data < 0)
*data = 0;
ret = 0;
out_ret:
mutex_unlock(&pm802_adc_mutex);
return ret;
}
/* return gpadc voltage */
int pm802_get_gpadc_volt(struct pm80x_chip *chip, int gpadc_id)
{
int ret, volt;
unsigned char buf[2];
int gp_meas;
u32 adc_channel;
might_sleep();
mutex_lock(&pm802_adc_mutex);
switch (gpadc_id) {
case PM802_GPADC0:
gp_meas = PM802_GPADC0_MEAS1;
if (CHIP_PM813S_A1_ID == chip->chip_id
|| CHIP_PM813S_A0_ID == chip->chip_id)
gp_meas = PM813S_GPADC0_MEAS1;
if (CHIP_PM813 == chip->type)
adc_channel = 28;
else
adc_channel = 0x7;
break;
case PM802_GPADC1:
gp_meas = PM802_GPADC1_MEAS1;
if (CHIP_PM813S_A1_ID == chip->chip_id
|| CHIP_PM813S_A0_ID == chip->chip_id)
gp_meas = PM813S_GPADC1_MEAS1;
if (CHIP_PM813 == chip->type)
adc_channel = 9;
else
adc_channel = 0x8;
break;
default:
dev_err(chip->dev, "get GPADC failed!\n");
mutex_unlock(&pm802_adc_mutex);
return -EINVAL;
}
regmap_update_bits(chip->subchip->regmap_gpadc, PM802_GPADC_CFG3, 0x7, ((adc_channel >> 3) & 0x3));
regmap_update_bits(chip->subchip->regmap_gpadc, PM802_GPADC_CFG2, (0x7 << 5), ((adc_channel & 0x7) << 5));
regmap_update_bits(chip->subchip->regmap_gpadc, PM802_GPADC_CFG3, (0x3 << 2), (0x3 << 2));
usleep_range(40, 40);
ret = regmap_bulk_read(chip->subchip->regmap_gpadc,
gp_meas, buf, 2);
if (ret < 0) {
dev_err(chip->dev, "Attention: failed to get volt!\n");
mutex_unlock(&pm802_adc_mutex);
return -EINVAL;
}
if (CHIP_PM813 == chip->type)
volt = ((buf[0] & 0xff) << 4) | (buf[1] & 0x0f);
else
volt = ((buf[1] & 0x0f) << 8) | (buf[0] & 0xff);
dev_dbg(chip->dev, "%s: volt value = 0x%x\n", __func__, volt);
/* volt = value * 1.3 * 1000 / (2^12) */
/* volt = ((volt & 0xfff) * 650) >> 11; */
if (CHIP_PM813 == chip->type) {
if (gpadc_id == PM802_GPADC0) {
volt = (volt - chip->adc_sample_offset) * (13000 + chip->adc_vref_offset);
if (CHIP_PM813S_A1_ID == chip->chip_id
|| CHIP_PM813S_A0_ID == chip->chip_id)
volt = (volt >> 6) / (128 * 5);
else
volt = (volt >> 6) / (129 * 5);
if (volt < 0)
volt = 0;
if (volt > 1300)
volt = 1300;
} else {
volt = (volt - chip->adc_sample_offset - 0x800) * (13000 + chip->adc_vref_offset);
if (CHIP_PM813S_A1_ID == chip->chip_id
|| CHIP_PM813S_A0_ID == chip->chip_id)
volt = (volt >> 5) / (128 * 5);
else
volt = (volt >> 5) / (129 * 5);
if (volt < -1300)
volt = -1300;
if (volt > 1300)
volt = 1300;
}
} else {
volt = (volt - chip->adc_sample_offset) * (13000 + chip->adc_vref_offset);
volt = (volt >> 6) / (129 * 5);
if (volt > 1300)
volt = 1300;
if (volt < 0)
volt = 0;
}
dev_dbg(chip->dev, "%s: voltage = %dmV\n", __func__, volt);
mutex_unlock(&pm802_adc_mutex);
return volt;
}
/* return voltage via bias current from GPADC */
static int pm802_get_gpadc_bias_volt(struct pm80x_chip *chip, int gpadc_id, int bias)
{
int volt, data, gp_bias, gp_bias_shift;
switch (gpadc_id) {
case PM802_GPADC0:
gp_bias = PM802_GPADC_BIAS1;
gp_bias_shift = 0;
break;
case PM802_GPADC1:
gp_bias = PM802_GPADC_BIAS2;
gp_bias_shift = 4;
break;
default:
dev_err(chip->dev, "get GPADC failed!\n");
return -EINVAL;
}
/* get the register value */
if (bias > 76)
bias = 76;
if (bias < 1)
bias = 1;
bias = (bias - 1) / 5;
regmap_read(chip->subchip->regmap_gpadc, gp_bias, &data);
data &= ~(0xF << gp_bias_shift);
data |= (bias << gp_bias_shift);
regmap_write(chip->subchip->regmap_gpadc, gp_bias, data);
/* wait for voltage to be stable */
usleep_range(30, 40);
volt = pm802_get_gpadc_volt(chip, gpadc_id);
if ((volt < 0) || (volt > 1300)) {
dev_err(chip->dev, "%s return %dmV\n", __func__, volt);
return -EINVAL;
}
return volt;
}
#ifdef CONFIG_ASR_AUXADC
#define ASR_IPC_BIAS_EN (0x1 << 26)
#define APB_SPARE14_REG (0x134)
static int asr_ipc_set_bias(int gpadc_id, int bias)
{
u32 bias_mask, bias_val, bias_shift, val2;
void __iomem *apbs_base = regs_addr_get_va(REGS_ADDR_APBS);
if (bias > 30 || bias < 0 || (bias % 2) || gpadc_id > ASR_AUXADC3) {
pr_err("error: error bias value!, gp: %d, bias: %d\n", gpadc_id, bias);
return -EINVAL;
}
bias_shift = 8 + (gpadc_id - ASR_AUXADC1) * 4;
bias_mask = 0xf << bias_shift;
bias_val = (bias / 2) << bias_shift;
val2 = readl(apbs_base + APB_SPARE14_REG);
val2 &= ~(bias_mask);
val2 |= bias_val;
val2 |= ASR_IPC_BIAS_EN;
writel(val2, apbs_base + APB_SPARE14_REG);
usleep_range(1000, 1000);
return 0;
}
static int asr_ipc_clear_bias(int gpadc_id, int bias)
{
u32 bias_mask, bias_val, bias_shift, val2;
void __iomem *apbs_base = regs_addr_get_va(REGS_ADDR_APBS);
bias_shift = 8 + (gpadc_id - ASR_AUXADC1) * 4;
bias_mask = 0xf << bias_shift;
bias_val = 0;
val2 = readl(apbs_base + APB_SPARE14_REG);
val2 &= ~(bias_mask);
val2 |= bias_val;
val2 |= ASR_IPC_BIAS_EN;
writel(val2, apbs_base + APB_SPARE14_REG);
return 0;
}
/* return voltage via bias current from GPADC */
static int get_auxadc_bias_volt(struct pm80x_chip *chip, int gpadc_id, int bias)
{
int volt;
if (asr_ipc_set_bias(gpadc_id, bias) < 0) {
pr_err("error: pm80x_chip_g not inited\n");
return -EINVAL;
}
volt = extern_get_auxadc_volt(gpadc_id);
asr_ipc_clear_bias(gpadc_id, bias);
return volt;
}
#endif
/*
* used by non-pmic driver
* TODO: remove later
*/
int extern_get_gpadc_bias_volt(int gpadc_id, int bias)
{
if (!pm80x_chip_g) {
pr_err("error: pm80x_chip_g not inited\n");
return -ENODEV;
}
if (cpu_is_asr1903()) {
#ifdef CONFIG_ASR_AUXADC
if (gpadc_id >= ASR_AUXADC1 && gpadc_id <= ASR_AUXADC5)
return get_auxadc_bias_volt(pm80x_chip_g, gpadc_id, bias);
else if (CHIP_PM803 == pm80x_chip_g->type)
return -ENODEV;
#else
if (CHIP_PM803 == pm80x_chip_g->type)
return -ENODEV;
#endif
} else {
if (CHIP_PM803 == pm80x_chip_g->type)
return -ENODEV;
}
if (CHIP_PM802 == pm80x_chip_g->type
|| CHIP_PM813 == pm80x_chip_g->type)
return pm802_get_gpadc_bias_volt(pm80x_chip_g, gpadc_id, bias);
else
return get_gpadc_bias_volt(pm80x_chip_g, gpadc_id, bias);
}
EXPORT_SYMBOL(extern_get_gpadc_bias_volt);
/*
* used by non-pmic driver
*/
int extern_get_gpadc_volt(int gpadc_id)
{
#ifdef CONFIG_MFD_PM803
int value, ret;
#endif
#ifdef CONFIG_ASR_AUXADC
if (gpadc_id >= ASR_AUXADC1 && gpadc_id <= ASR_AUXADC5)
return extern_get_auxadc_volt(gpadc_id);
#endif
if (!pm80x_chip_g) {
pr_err("error: pm80x_chip_g not inited\n");
return -ENODEV;
}
#if defined(CONFIG_MFD_PM803) && defined(CONFIG_POWER_SUPPLY)
if (CHIP_PM803 == pm80x_chip_g->type) {
ret = pm803_get_adc_mvolt(gpadc_id, &value);
if (ret < 0)
return ret;
else
return value;
}
#endif
if (CHIP_PM802 == pm80x_chip_g->type
|| CHIP_PM813 == pm80x_chip_g->type)
return pm802_get_gpadc_volt(pm80x_chip_g, gpadc_id);
else
return get_gpadc_volt(pm80x_chip_g, gpadc_id);
}
EXPORT_SYMBOL(extern_get_gpadc_volt);
/*
* used by non-pmic driver
*/
int extern_get_batt_volt(int *data)
{
if (!pm80x_chip_g) {
pr_err("%s: error: pm80x_chip_g not inited\n", __func__);
return -ENODEV;
}
if (CHIP_PM802 == pm80x_chip_g->type
|| CHIP_PM813 == pm80x_chip_g->type) {
return pm802_get_batt_vol(pm80x_chip_g, data);
} else if (CHIP_PM800 == pm80x_chip_g->type
|| CHIP_PM801 == pm80x_chip_g->type) {
return pm801_get_batt_vol(pm80x_chip_g, data);
} else {
pr_err("%s unsupported pmic: %d\n", __func__, pm80x_chip_g->type);
return -EINVAL;
}
}
EXPORT_SYMBOL(extern_get_batt_volt);
/*
* used by non-pmic driver
*/
int extern_pmic_base_read(u32 reg, u32 *value)
{
int ret;
if (!pm80x_chip_g) {
pr_err("error: pm80x_chip_g not inited\n");
return -ENODEV;
}
ret = regmap_read(pm80x_chip_g->regmap,
reg,
value);
return ret;
}
EXPORT_SYMBOL(extern_pmic_base_read);
/*
* used by non-pmic driver
*/
int extern_pmic_base_write(u32 reg, u32 value)
{
int ret;
if (!pm80x_chip_g) {
pr_err("error: pm80x_chip_g not inited\n");
return -ENODEV;
}
ret = regmap_write(pm80x_chip_g->regmap,
reg,
value);
return ret;
}
EXPORT_SYMBOL(extern_pmic_base_write);
int pmic_rw_reg(u8 reg, u8 mask, u8 value)
{
int ret;
u8 data, buf[2];
struct i2c_client *client;
struct i2c_msg msgs[2];
if (!pm80x_chip_g) {
pr_err("error: pm80x_chip_g not inited\n");
return -ENODEV;
}
client = pm80x_chip_g->client;
msgs[0].addr = client->addr,
msgs[0].flags = 0,
msgs[0].len = 1,
msgs[0].buf = buf,
msgs[1].addr = client->addr,
msgs[1].flags = I2C_M_RD,
msgs[1].len = 1,
msgs[1].buf = &data,
/*
* I2C pins may be in non-AP pinstate, and __i2c_transfer
* won't change it back to AP pinstate like i2c_transfer,
* so change i2c pins to AP pinstate explicitly here.
*/
i2c_pxa_set_pinstate(client->adapter, "default");
/*
* set i2c to pio mode
* for in power off sequence, irq has been disabled
*/
i2c_set_pio_mode(client->adapter, 1);
buf[0] = reg;
ret = __i2c_transfer(client->adapter, msgs, 2);
if (ret < 0) {
pr_err("%s read register fails...\n", __func__);
WARN_ON(1);
return ret;
}
/* issue SW power down */
msgs[0].addr = client->addr;
msgs[0].flags = 0;
msgs[0].len = 2;
msgs[0].buf[0] = reg;
msgs[0].buf[1] = ((data & (~mask)) | value);
ret = __i2c_transfer(client->adapter, msgs, 1);
if (ret < 0) {
pr_err("%s write data fails: ret = %d\n", __func__, ret);
WARN_ON(1);
}
return ret;
}
static int __pmic_reset(void)
{
int ret = 0;
/* pmic reset contain two parts: fault wake up and sw_powerdown */
pr_info("pmic power down/up reset....\n");
if (!pm80x_chip_g) {
pr_err("error: pm80x_chip_g not inited\n");
return -ENODEV;
}
if (CHIP_PM802 == pm80x_chip_g->type
|| CHIP_PM803 == pm80x_chip_g->type
|| CHIP_PM813 == pm80x_chip_g->type) {
pr_info("asr pmic reset\n");
if (CHIP_PM802S_ID_A0 == pm80x_chip_g->chip_id
|| CHIP_PM802S_ID_A1 == pm80x_chip_g->chip_id
|| CHIP_PM802S_ID_A2 == pm80x_chip_g->chip_id
|| CHIP_PM813S_A1_ID == pm80x_chip_g->chip_id
|| CHIP_PM813S_A0_ID == pm80x_chip_g->chip_id) {
/* discharge timer should be set greater than or equal to 65ms */
ret = pmic_rw_reg(PM802_RTC_MISC2, 0xf, 0x3);
if (ret < 0) {
pr_err("%s, set PM802_RTC_MISC2 fail", __func__);
return ret;
}
} else {
/* discharge timer should be set greater than or equal to 1s */
ret = pmic_rw_reg(PM802_RTC_MISC2, 0xf, 0x1);
if (ret < 0) {
pr_err("%s, set PM802_RTC_MISC2 fail", __func__);
return ret;
}
}
ret = pmic_rw_reg(PM802_RTC_MISC4, (0x1 << 1), 0x0);
if (ret < 0) {
pr_err("%s, set PM802_RTC_MISC4 fail", __func__);
return ret;
}
}
if (CHIP_PM813S_A1_ID == pm80x_chip_g->chip_id
|| CHIP_PM813S_A0_ID == pm80x_chip_g->chip_id
|| CHIP_PM803 == pm80x_chip_g->type) {
ret = pmic_rw_reg(PM800_RTC_MISC5, PM803_FAULT_WAKEUP_EN, PM803_FAULT_WAKEUP_EN);
if (ret < 0) {
pr_err("%s, enbale pm803 fault wakeup en fail!", __func__);
return ret;
}
ret = pmic_rw_reg(PM800_RTC_MISC5, PM803_FAULT_WAKEUP, PM803_FAULT_WAKEUP);
if (ret < 0) {
pr_err("%s, enbale pm803 fault wakeup fail!", __func__);
return ret;
}
} else if (CHIP_PM813 == pm80x_chip_g->type) {
ret = pmic_rw_reg(PM800_RTC_MISC5, PM813_FAULT_WAKEUP_EN, PM813_FAULT_WAKEUP_EN);
if (ret < 0) {
pr_err("%s, enbale pm813 fault wakeup en fail!", __func__);
return ret;
}
ret = pmic_rw_reg(PM800_RTC_MISC5, PM813_FAULT_WAKEUP, PM813_FAULT_WAKEUP);
if (ret < 0) {
pr_err("%s, enbale pm813 fault wakeup fail!", __func__);
return ret;
}
} else {
ret = pmic_rw_reg(PM800_RTC_MISC5, PM800_FAULT_WAKEUP_EN, PM800_FAULT_WAKEUP_EN);
if (ret < 0) {
pr_err("%s, enbale pmic fault wakeup en fail!", __func__);
return ret;
}
ret = pmic_rw_reg(PM800_RTC_MISC5, PM800_FAULT_WAKEUP, PM800_FAULT_WAKEUP);
if (ret < 0) {
pr_err("%s, enbale pmic fault wakeup fail!", __func__);
return ret;
}
}
/* Issue SW power down */
ret = pmic_rw_reg(PM800_WAKEUP1, PM800_SW_PDOWN, PM800_SW_PDOWN);
if (ret < 0)
pr_err("%s, set PM800_SW_PDOWN fail\n", __func__);
return ret;
}
void pmic_reset(void)
{
int ret;
/* try 3 times to reduce the fail rate */
pr_info("PMIC reset....\n");
ret = __pmic_reset();
if (ret < 0)
ret = __pmic_reset();
if (ret < 0)
ret = __pmic_reset();
if (ret < 0)
pr_err("%s, pmic reset failed\n", __func__);
}