android_kernel_google_msm/drivers/hwmon/pm8xxx-adc.c

1333 lines
36 KiB
C

/*
* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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.
*
* Qualcomm's PM8921/PM8018 ADC Arbiter driver
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/hwmon.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/hwmon-sysfs.h>
#include <linux/mfd/pm8xxx/mpp.h>
#include <linux/platform_device.h>
#include <linux/mfd/pm8xxx/core.h>
#include <linux/regulator/consumer.h>
#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
#include <mach/msm_xo.h>
/* User Bank register set */
#define PM8XXX_ADC_ARB_USRP_CNTRL1 0x197
#define PM8XXX_ADC_ARB_USRP_CNTRL1_EN_ARB BIT(0)
#define PM8XXX_ADC_ARB_USRP_CNTRL1_RSV1 BIT(1)
#define PM8XXX_ADC_ARB_USRP_CNTRL1_RSV2 BIT(2)
#define PM8XXX_ADC_ARB_USRP_CNTRL1_RSV3 BIT(3)
#define PM8XXX_ADC_ARB_USRP_CNTRL1_RSV4 BIT(4)
#define PM8XXX_ADC_ARB_USRP_CNTRL1_RSV5 BIT(5)
#define PM8XXX_ADC_ARB_USRP_CNTRL1_EOC BIT(6)
#define PM8XXX_ADC_ARB_USRP_CNTRL1_REQ BIT(7)
#define PM8XXX_ADC_ARB_USRP_AMUX_CNTRL 0x198
#define PM8XXX_ADC_ARB_USRP_AMUX_CNTRL_RSV0 BIT(0)
#define PM8XXX_ADC_ARB_USRP_AMUX_CNTRL_RSV1 BIT(1)
#define PM8XXX_ADC_ARB_USRP_AMUX_CNTRL_PREMUX0 BIT(2)
#define PM8XXX_ADC_ARB_USRP_AMUX_CNTRL_PREMUX1 BIT(3)
#define PM8XXX_ADC_ARB_USRP_AMUX_CNTRL_SEL0 BIT(4)
#define PM8XXX_ADC_ARB_USRP_AMUX_CNTRL_SEL1 BIT(5)
#define PM8XXX_ADC_ARB_USRP_AMUX_CNTRL_SEL2 BIT(6)
#define PM8XXX_ADC_ARB_USRP_AMUX_CNTRL_SEL3 BIT(7)
#define PM8XXX_ADC_ARB_USRP_ANA_PARAM 0x199
#define PM8XXX_ADC_ARB_USRP_DIG_PARAM 0x19A
#define PM8XXX_ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT0 BIT(0)
#define PM8XXX_ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT1 BIT(1)
#define PM8XXX_ADC_ARB_USRP_DIG_PARAM_CLK_RATE0 BIT(2)
#define PM8XXX_ADC_ARB_USRP_DIG_PARAM_CLK_RATE1 BIT(3)
#define PM8XXX_ADC_ARB_USRP_DIG_PARAM_EOC BIT(4)
#define PM8XXX_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0 BIT(5)
#define PM8XXX_ADC_ARB_USRP_DIG_PARAM_DEC_RATE1 BIT(6)
#define PM8XXX_ADC_ARB_USRP_DIG_PARAM_EN BIT(7)
#define PM8XXX_ADC_ARB_USRP_RSV 0x19B
#define PM8XXX_ADC_ARB_USRP_RSV_RST BIT(0)
#define PM8XXX_ADC_ARB_USRP_RSV_DTEST0 BIT(1)
#define PM8XXX_ADC_ARB_USRP_RSV_DTEST1 BIT(2)
#define PM8XXX_ADC_ARB_USRP_RSV_OP BIT(3)
#define PM8XXX_ADC_ARB_USRP_RSV_IP_SEL0 BIT(4)
#define PM8XXX_ADC_ARB_USRP_RSV_IP_SEL1 BIT(5)
#define PM8XXX_ADC_ARB_USRP_RSV_IP_SEL2 BIT(6)
#define PM8XXX_ADC_ARB_USRP_RSV_TRM BIT(7)
#define PM8XXX_ADC_ARB_USRP_DATA0 0x19D
#define PM8XXX_ADC_ARB_USRP_DATA1 0x19C
#define PM8XXX_ADC_ARB_BTM_CNTRL1 0x17e
#define PM8XXX_ADC_ARB_BTM_CNTRL1_EN_BTM BIT(0)
#define PM8XXX_ADC_ARB_BTM_CNTRL1_SEL_OP_MODE BIT(1)
#define PM8XXX_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL1 BIT(2)
#define PM8XXX_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL2 BIT(3)
#define PM8XXX_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL3 BIT(4)
#define PM8XXX_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL4 BIT(5)
#define PM8XXX_ADC_ARB_BTM_CNTRL1_EOC BIT(6)
#define PM8XXX_ADC_ARB_BTM_CNTRL1_REQ BIT(7)
#define PM8XXX_ADC_ARB_BTM_CNTRL2 0x18c
#define PM8XXX_ADC_ARB_BTM_AMUX_CNTRL 0x17f
#define PM8XXX_ADC_ARB_BTM_ANA_PARAM 0x180
#define PM8XXX_ADC_ARB_BTM_DIG_PARAM 0x181
#define PM8XXX_ADC_ARB_BTM_RSV 0x182
#define PM8XXX_ADC_ARB_BTM_DATA1 0x183
#define PM8XXX_ADC_ARB_BTM_DATA0 0x184
#define PM8XXX_ADC_ARB_BTM_BAT_COOL_THR1 0x185
#define PM8XXX_ADC_ARB_BTM_BAT_COOL_THR0 0x186
#define PM8XXX_ADC_ARB_BTM_BAT_WARM_THR1 0x187
#define PM8XXX_ADC_ARB_BTM_BAT_WARM_THR0 0x188
#define PM8XXX_ADC_ARB_ANA_DIG 0xa0
#define PM8XXX_ADC_BTM_RSV 0x10
#define PM8XXX_ADC_AMUX_MPP_SEL 2
#define PM8XXX_ADC_AMUX_SEL 4
#define PM8XXX_ADC_RSV_IP_SEL 4
#define PM8XXX_ADC_BTM_CHANNEL_SEL 4
#define PM8XXX_MAX_CHANNEL_PROPERTIES 2
#define PM8XXX_ADC_IRQ_0 0
#define PM8XXX_ADC_IRQ_1 1
#define PM8XXX_ADC_IRQ_2 2
#define PM8XXX_ADC_BTM_INTERVAL_SEL_MASK 0xF
#define PM8XXX_ADC_BTM_INTERVAL_SEL_SHIFT 2
#define PM8XXX_ADC_BTM_DECIMATION_SEL 5
#define PM8XXX_ADC_MUL 10
#define PM8XXX_ADC_CONV_TIME_MIN 2000
#define PM8XXX_ADC_CONV_TIME_MAX 2100
#define PM8XXX_ADC_MPP_SETTLE_TIME_MIN 200
#define PM8XXX_ADC_MPP_SETTLE_TIME_MAX 200
#define PM8XXX_ADC_PA_THERM_VREG_UV_MIN 1800000
#define PM8XXX_ADC_PA_THERM_VREG_UV_MAX 1800000
#define PM8XXX_ADC_PA_THERM_VREG_UA_LOAD 100000
#define PM8XXX_ADC_HWMON_NAME_LENGTH 32
#define PM8XXX_ADC_BTM_INTERVAL_MAX 0x14
#define PM8XXX_ADC_COMPLETION_TIMEOUT (2 * HZ)
struct pm8xxx_adc {
struct device *dev;
struct pm8xxx_adc_properties *adc_prop;
int adc_irq;
struct mutex adc_lock;
struct mutex mpp_adc_lock;
spinlock_t btm_lock;
uint32_t adc_num_board_channel;
struct completion adc_rslt_completion;
struct pm8xxx_adc_amux *adc_channel;
int btm_warm_irq;
int btm_cool_irq;
struct dentry *dent;
struct work_struct warm_work;
struct work_struct cool_work;
uint32_t mpp_base;
struct device *hwmon;
struct msm_xo_voter *adc_voter;
int msm_suspend_check;
struct pm8xxx_adc_amux_properties *conv;
struct pm8xxx_adc_arb_btm_param batt;
struct sensor_device_attribute sens_attr[0];
};
struct pm8xxx_adc_amux_properties {
uint32_t amux_channel;
uint32_t decimation;
uint32_t amux_ip_rsv;
uint32_t amux_mpp_channel;
struct pm8xxx_adc_chan_properties chan_prop[0];
};
static const struct pm8xxx_adc_scaling_ratio pm8xxx_amux_scaling_ratio[] = {
{1, 1},
{1, 3},
{1, 4},
{1, 6}
};
static struct pm8xxx_adc *pmic_adc;
static struct regulator *pa_therm;
static struct pm8xxx_adc_scale_fn adc_scale_fn[] = {
[ADC_SCALE_DEFAULT] = {pm8xxx_adc_scale_default},
[ADC_SCALE_BATT_THERM] = {pm8xxx_adc_scale_batt_therm},
[ADC_SCALE_PA_THERM] = {pm8xxx_adc_scale_pa_therm},
[ADC_SCALE_PMIC_THERM] = {pm8xxx_adc_scale_pmic_therm},
[ADC_SCALE_XOTHERM] = {pm8xxx_adc_tdkntcg_therm},
};
/* On PM8921 ADC the MPP needs to first be configured
as an analog input to the AMUX pre-mux channel before
issuing a read request. PM8921 MPP 8 is mapped to AMUX8
and is common between remote processor's.
On PM8018 ADC the MPP is directly connected to the AMUX
pre-mux. Therefore clients of the PM8018 MPP do not need
to configure the MPP as an analog input to the pre-mux.
Clients can directly issue request on the pre-mux AMUX
channel to read the ADC on the MPP */
static struct pm8xxx_mpp_config_data pm8xxx_adc_mpp_config = {
.type = PM8XXX_MPP_TYPE_A_INPUT,
/* AMUX6 is dedicated to be used for apps processor */
.level = PM8XXX_MPP_AIN_AMUX_CH6,
.control = PM8XXX_MPP_AOUT_CTRL_DISABLE,
};
/* MPP Configuration for default settings */
static struct pm8xxx_mpp_config_data pm8xxx_adc_mpp_unconfig = {
.type = PM8XXX_MPP_TYPE_SINK,
.level = PM8XXX_MPP_AIN_AMUX_CH5,
.control = PM8XXX_MPP_AOUT_CTRL_DISABLE,
};
static bool pm8xxx_adc_calib_first_adc;
static bool pm8xxx_adc_initialized, pm8xxx_adc_calib_device_init;
static int32_t pm8xxx_adc_check_channel_valid(uint32_t channel)
{
if (channel < CHANNEL_VCOIN ||
(channel > CHANNEL_MUXOFF && channel < ADC_MPP_1_ATEST_8) ||
(channel > ADC_MPP_1_ATEST_7 && channel < ADC_MPP_2_ATEST_8)
|| (channel >= ADC_CHANNEL_MAX_NUM))
return -EBADF;
else
return 0;
}
static int32_t pm8xxx_adc_arb_cntrl(uint32_t arb_cntrl,
uint32_t channel)
{
struct pm8xxx_adc *adc_pmic = pmic_adc;
int i, rc;
u8 data_arb_cntrl = 0;
if (arb_cntrl) {
if (adc_pmic->msm_suspend_check)
pr_err("PM8xxx ADC request made after suspend_noirq "
"with channel: %d\n", channel);
data_arb_cntrl |= PM8XXX_ADC_ARB_USRP_CNTRL1_EN_ARB;
}
/* Write twice to the CNTRL register for the arbiter settings
to take into effect */
for (i = 0; i < 2; i++) {
rc = pm8xxx_writeb(adc_pmic->dev->parent,
PM8XXX_ADC_ARB_USRP_CNTRL1, data_arb_cntrl);
if (rc < 0) {
pr_err("PM8xxx arb cntrl write failed with %d\n", rc);
return rc;
}
}
if (arb_cntrl) {
data_arb_cntrl |= PM8XXX_ADC_ARB_USRP_CNTRL1_REQ;
INIT_COMPLETION(adc_pmic->adc_rslt_completion);
rc = pm8xxx_writeb(adc_pmic->dev->parent,
PM8XXX_ADC_ARB_USRP_CNTRL1, data_arb_cntrl);
}
return 0;
}
static int32_t pm8xxx_adc_patherm_power(bool on)
{
int rc = 0;
if (!pa_therm) {
pr_err("pm8xxx adc pa_therm not valid\n");
return -EINVAL;
}
if (on) {
rc = regulator_set_voltage(pa_therm,
PM8XXX_ADC_PA_THERM_VREG_UV_MIN,
PM8XXX_ADC_PA_THERM_VREG_UV_MAX);
if (rc < 0) {
pr_err("failed to set the voltage for "
"pa_therm with error %d\n", rc);
return rc;
}
rc = regulator_set_optimum_mode(pa_therm,
PM8XXX_ADC_PA_THERM_VREG_UA_LOAD);
if (rc < 0) {
pr_err("failed to set optimum mode for "
"pa_therm with error %d\n", rc);
return rc;
}
rc = regulator_enable(pa_therm);
if (rc < 0) {
pr_err("failed to enable pa_therm vreg "
"with error %d\n", rc);
return rc;
}
} else {
rc = regulator_disable(pa_therm);
if (rc < 0) {
pr_err("failed to disable pa_therm vreg "
"with error %d\n", rc);
return rc;
}
}
return rc;
}
static int32_t pm8xxx_adc_xo_vote(bool on)
{
struct pm8xxx_adc *adc_pmic = pmic_adc;
if (on)
msm_xo_mode_vote(adc_pmic->adc_voter, MSM_XO_MODE_ON);
else
msm_xo_mode_vote(adc_pmic->adc_voter, MSM_XO_MODE_OFF);
return 0;
}
static int32_t pm8xxx_adc_channel_power_enable(uint32_t channel,
bool power_cntrl)
{
int rc = 0;
switch (channel) {
case ADC_MPP_1_AMUX8:
rc = pm8xxx_adc_patherm_power(power_cntrl);
break;
case CHANNEL_DIE_TEMP:
case CHANNEL_MUXOFF:
rc = pm8xxx_adc_xo_vote(power_cntrl);
break;
default:
break;
}
return rc;
}
static uint32_t pm8xxx_adc_read_reg(uint32_t reg, u8 *data)
{
struct pm8xxx_adc *adc_pmic = pmic_adc;
int rc;
rc = pm8xxx_readb(adc_pmic->dev->parent, reg, data);
if (rc < 0) {
pr_err("PM8xxx adc read reg %d failed with %d\n", reg, rc);
return rc;
}
return 0;
}
static uint32_t pm8xxx_adc_write_reg(uint32_t reg, u8 data)
{
struct pm8xxx_adc *adc_pmic = pmic_adc;
int rc;
rc = pm8xxx_writeb(adc_pmic->dev->parent, reg, data);
if (rc < 0) {
pr_err("PM8xxx adc write reg %d failed with %d\n", reg, rc);
return rc;
}
return 0;
}
static int32_t pm8xxx_adc_configure(
struct pm8xxx_adc_amux_properties *chan_prop)
{
u8 data_amux_chan = 0, data_arb_rsv = 0, data_dig_param = 0;
int rc;
data_amux_chan |= chan_prop->amux_channel << PM8XXX_ADC_AMUX_SEL;
if (chan_prop->amux_mpp_channel)
data_amux_chan |= chan_prop->amux_mpp_channel <<
PM8XXX_ADC_AMUX_MPP_SEL;
rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_USRP_AMUX_CNTRL,
data_amux_chan);
if (rc < 0)
return rc;
rc = pm8xxx_adc_read_reg(PM8XXX_ADC_ARB_USRP_RSV, &data_arb_rsv);
if (rc < 0)
return rc;
data_arb_rsv &= (PM8XXX_ADC_ARB_USRP_RSV_RST |
PM8XXX_ADC_ARB_USRP_RSV_DTEST0 |
PM8XXX_ADC_ARB_USRP_RSV_DTEST1 |
PM8XXX_ADC_ARB_USRP_RSV_OP);
data_arb_rsv |= (chan_prop->amux_ip_rsv << PM8XXX_ADC_RSV_IP_SEL |
PM8XXX_ADC_ARB_USRP_RSV_TRM);
rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_USRP_RSV, data_arb_rsv);
if (rc < 0)
return rc;
rc = pm8xxx_adc_read_reg(PM8XXX_ADC_ARB_USRP_DIG_PARAM,
&data_dig_param);
if (rc < 0)
return rc;
/* Default 2.4Mhz clock rate */
/* Client chooses the decimation */
switch (chan_prop->decimation) {
case ADC_DECIMATION_TYPE1:
data_dig_param |= PM8XXX_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0;
break;
case ADC_DECIMATION_TYPE2:
data_dig_param |= (PM8XXX_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0
| PM8XXX_ADC_ARB_USRP_DIG_PARAM_DEC_RATE1);
break;
default:
data_dig_param |= PM8XXX_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0;
break;
}
rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_USRP_DIG_PARAM,
PM8XXX_ADC_ARB_ANA_DIG);
if (rc < 0)
return rc;
rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_USRP_ANA_PARAM,
PM8XXX_ADC_ARB_ANA_DIG);
if (rc < 0)
return rc;
rc = pm8xxx_adc_arb_cntrl(1, data_amux_chan);
if (rc < 0) {
pr_err("Configuring ADC Arbiter"
"enable failed with %d\n", rc);
return rc;
}
return 0;
}
static uint32_t pm8xxx_adc_read_adc_code(int32_t *data)
{
struct pm8xxx_adc *adc_pmic = pmic_adc;
uint8_t rslt_lsb, rslt_msb;
int32_t rc, max_ideal_adc_code = 1 << adc_pmic->adc_prop->bitresolution;
rc = pm8xxx_readb(adc_pmic->dev->parent,
PM8XXX_ADC_ARB_USRP_DATA0, &rslt_lsb);
if (rc < 0) {
pr_err("PM8xxx adc result read failed with %d\n", rc);
return rc;
}
rc = pm8xxx_readb(adc_pmic->dev->parent,
PM8XXX_ADC_ARB_USRP_DATA1, &rslt_msb);
if (rc < 0) {
pr_err("PM8xxx adc result read failed with %d\n", rc);
return rc;
}
*data = (rslt_msb << 8) | rslt_lsb;
/* Use the midpoint to determine underflow or overflow */
if (*data > max_ideal_adc_code + (max_ideal_adc_code >> 1))
*data |= ((1 << (8 * sizeof(*data) -
adc_pmic->adc_prop->bitresolution)) - 1) <<
adc_pmic->adc_prop->bitresolution;
/* Default value for switching off the arbiter after reading
the ADC value. Bit 0 set to 0. */
rc = pm8xxx_adc_arb_cntrl(0, CHANNEL_NONE);
if (rc < 0) {
pr_err("%s: Configuring ADC Arbiter disable"
"failed\n", __func__);
return rc;
}
return 0;
}
static void pm8xxx_adc_btm_warm_scheduler_fn(struct work_struct *work)
{
struct pm8xxx_adc *adc_pmic = container_of(work, struct pm8xxx_adc,
warm_work);
unsigned long flags = 0;
bool warm_status;
spin_lock_irqsave(&adc_pmic->btm_lock, flags);
warm_status = irq_read_line(adc_pmic->btm_warm_irq);
if (adc_pmic->batt.btm_warm_fn != NULL)
adc_pmic->batt.btm_warm_fn(warm_status);
spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
}
static void pm8xxx_adc_btm_cool_scheduler_fn(struct work_struct *work)
{
struct pm8xxx_adc *adc_pmic = container_of(work, struct pm8xxx_adc,
cool_work);
unsigned long flags = 0;
bool cool_status;
spin_lock_irqsave(&adc_pmic->btm_lock, flags);
cool_status = irq_read_line(adc_pmic->btm_cool_irq);
if (adc_pmic->batt.btm_cool_fn != NULL)
adc_pmic->batt.btm_cool_fn(cool_status);
spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
}
void trigger_completion(struct work_struct *work)
{
struct pm8xxx_adc *adc_8xxx = pmic_adc;
complete(&adc_8xxx->adc_rslt_completion);
}
DECLARE_WORK(trigger_completion_work, trigger_completion);
static irqreturn_t pm8xxx_adc_isr(int irq, void *dev_id)
{
if (pm8xxx_adc_calib_first_adc)
return IRQ_HANDLED;
schedule_work(&trigger_completion_work);
return IRQ_HANDLED;
}
static irqreturn_t pm8xxx_btm_warm_isr(int irq, void *dev_id)
{
struct pm8xxx_adc *btm_8xxx = dev_id;
schedule_work(&btm_8xxx->warm_work);
return IRQ_HANDLED;
}
static irqreturn_t pm8xxx_btm_cool_isr(int irq, void *dev_id)
{
struct pm8xxx_adc *btm_8xxx = dev_id;
schedule_work(&btm_8xxx->cool_work);
return IRQ_HANDLED;
}
static uint32_t pm8xxx_adc_calib_device(void)
{
struct pm8xxx_adc *adc_pmic = pmic_adc;
struct pm8xxx_adc_amux_properties conv;
int rc, calib_read_1, calib_read_2;
u8 data_arb_usrp_cntrl1 = 0;
conv.amux_channel = CHANNEL_125V;
conv.decimation = ADC_DECIMATION_TYPE2;
conv.amux_ip_rsv = AMUX_RSV1;
conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
pm8xxx_adc_calib_first_adc = true;
rc = pm8xxx_adc_configure(&conv);
if (rc) {
pr_err("pm8xxx_adc configure failed with %d\n", rc);
goto calib_fail;
}
while (data_arb_usrp_cntrl1 != (PM8XXX_ADC_ARB_USRP_CNTRL1_EOC |
PM8XXX_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
rc = pm8xxx_adc_read_reg(PM8XXX_ADC_ARB_USRP_CNTRL1,
&data_arb_usrp_cntrl1);
if (rc < 0)
return rc;
usleep_range(PM8XXX_ADC_CONV_TIME_MIN,
PM8XXX_ADC_CONV_TIME_MAX);
}
data_arb_usrp_cntrl1 = 0;
rc = pm8xxx_adc_read_adc_code(&calib_read_1);
if (rc) {
pr_err("pm8xxx_adc read adc failed with %d\n", rc);
pm8xxx_adc_calib_first_adc = false;
goto calib_fail;
}
pm8xxx_adc_calib_first_adc = false;
conv.amux_channel = CHANNEL_625MV;
conv.decimation = ADC_DECIMATION_TYPE2;
conv.amux_ip_rsv = AMUX_RSV1;
conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
pm8xxx_adc_calib_first_adc = true;
rc = pm8xxx_adc_configure(&conv);
if (rc) {
pr_err("pm8xxx_adc configure failed with %d\n", rc);
goto calib_fail;
}
while (data_arb_usrp_cntrl1 != (PM8XXX_ADC_ARB_USRP_CNTRL1_EOC |
PM8XXX_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
rc = pm8xxx_adc_read_reg(PM8XXX_ADC_ARB_USRP_CNTRL1,
&data_arb_usrp_cntrl1);
if (rc < 0)
return rc;
usleep_range(PM8XXX_ADC_CONV_TIME_MIN,
PM8XXX_ADC_CONV_TIME_MAX);
}
data_arb_usrp_cntrl1 = 0;
rc = pm8xxx_adc_read_adc_code(&calib_read_2);
if (rc) {
pr_err("pm8xxx_adc read adc failed with %d\n", rc);
pm8xxx_adc_calib_first_adc = false;
goto calib_fail;
}
pm8xxx_adc_calib_first_adc = false;
adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_ABSOLUTE].dy =
(calib_read_1 - calib_read_2);
adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_ABSOLUTE].dx
= PM8XXX_CHANNEL_ADC_625_UV;
adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_ABSOLUTE].adc_vref =
calib_read_1;
adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_ABSOLUTE].adc_gnd =
calib_read_2;
rc = pm8xxx_adc_arb_cntrl(0, CHANNEL_NONE);
if (rc < 0) {
pr_err("%s: Configuring ADC Arbiter disable"
"failed\n", __func__);
return rc;
}
/* Ratiometric Calibration */
conv.amux_channel = CHANNEL_MUXOFF;
conv.decimation = ADC_DECIMATION_TYPE2;
conv.amux_ip_rsv = AMUX_RSV5;
conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
pm8xxx_adc_calib_first_adc = true;
rc = pm8xxx_adc_configure(&conv);
if (rc) {
pr_err("pm8xxx_adc configure failed with %d\n", rc);
goto calib_fail;
}
while (data_arb_usrp_cntrl1 != (PM8XXX_ADC_ARB_USRP_CNTRL1_EOC |
PM8XXX_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
rc = pm8xxx_adc_read_reg(PM8XXX_ADC_ARB_USRP_CNTRL1,
&data_arb_usrp_cntrl1);
if (rc < 0)
return rc;
usleep_range(PM8XXX_ADC_CONV_TIME_MIN,
PM8XXX_ADC_CONV_TIME_MAX);
}
data_arb_usrp_cntrl1 = 0;
rc = pm8xxx_adc_read_adc_code(&calib_read_1);
if (rc) {
pr_err("pm8xxx_adc read adc failed with %d\n", rc);
pm8xxx_adc_calib_first_adc = false;
goto calib_fail;
}
pm8xxx_adc_calib_first_adc = false;
conv.amux_channel = CHANNEL_MUXOFF;
conv.decimation = ADC_DECIMATION_TYPE2;
conv.amux_ip_rsv = AMUX_RSV4;
conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
pm8xxx_adc_calib_first_adc = true;
rc = pm8xxx_adc_configure(&conv);
if (rc) {
pr_err("pm8xxx_adc configure failed with %d\n", rc);
goto calib_fail;
}
while (data_arb_usrp_cntrl1 != (PM8XXX_ADC_ARB_USRP_CNTRL1_EOC |
PM8XXX_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
rc = pm8xxx_adc_read_reg(PM8XXX_ADC_ARB_USRP_CNTRL1,
&data_arb_usrp_cntrl1);
if (rc < 0)
return rc;
usleep_range(PM8XXX_ADC_CONV_TIME_MIN,
PM8XXX_ADC_CONV_TIME_MAX);
}
data_arb_usrp_cntrl1 = 0;
rc = pm8xxx_adc_read_adc_code(&calib_read_2);
if (rc) {
pr_err("pm8xxx_adc read adc failed with %d\n", rc);
pm8xxx_adc_calib_first_adc = false;
goto calib_fail;
}
pm8xxx_adc_calib_first_adc = false;
adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_RATIOMETRIC].dy =
(calib_read_1 - calib_read_2);
adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_RATIOMETRIC].dx =
adc_pmic->adc_prop->adc_vdd_reference;
adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_RATIOMETRIC].adc_vref =
calib_read_1;
adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_RATIOMETRIC].adc_gnd =
calib_read_2;
calib_fail:
rc = pm8xxx_adc_arb_cntrl(0, CHANNEL_NONE);
if (rc < 0) {
pr_err("%s: Configuring ADC Arbiter disable"
"failed\n", __func__);
}
return rc;
}
uint32_t pm8xxx_adc_read(enum pm8xxx_adc_channels channel,
struct pm8xxx_adc_chan_result *result)
{
struct pm8xxx_adc *adc_pmic = pmic_adc;
int i = 0, rc = 0, rc_fail, amux_prescaling, scale_type;
enum pm8xxx_adc_premux_mpp_scale_type mpp_scale;
if (!pm8xxx_adc_initialized)
return -ENODEV;
if (!pm8xxx_adc_calib_device_init) {
if (pm8xxx_adc_calib_device() == 0)
pm8xxx_adc_calib_device_init = true;
}
mutex_lock(&adc_pmic->adc_lock);
for (i = 0; i < adc_pmic->adc_num_board_channel; i++) {
if (channel == adc_pmic->adc_channel[i].channel_name)
break;
}
if (i == adc_pmic->adc_num_board_channel ||
(pm8xxx_adc_check_channel_valid(channel) != 0)) {
rc = -EBADF;
goto fail_unlock;
}
if (channel < PM8XXX_CHANNEL_MPP_SCALE1_IDX) {
mpp_scale = PREMUX_MPP_SCALE_0;
adc_pmic->conv->amux_channel = channel;
} else if (channel >= PM8XXX_CHANNEL_MPP_SCALE1_IDX &&
channel < PM8XXX_CHANNEL_MPP_SCALE3_IDX) {
mpp_scale = PREMUX_MPP_SCALE_1;
adc_pmic->conv->amux_channel = channel %
PM8XXX_CHANNEL_MPP_SCALE1_IDX;
} else {
mpp_scale = PREMUX_MPP_SCALE_1_DIV3;
adc_pmic->conv->amux_channel = channel %
PM8XXX_CHANNEL_MPP_SCALE3_IDX;
}
adc_pmic->conv->amux_mpp_channel = mpp_scale;
adc_pmic->conv->amux_ip_rsv = adc_pmic->adc_channel[i].adc_rsv;
adc_pmic->conv->decimation = adc_pmic->adc_channel[i].adc_decimation;
amux_prescaling = adc_pmic->adc_channel[i].chan_path_prescaling;
adc_pmic->conv->chan_prop->offset_gain_numerator =
pm8xxx_amux_scaling_ratio[amux_prescaling].num;
adc_pmic->conv->chan_prop->offset_gain_denominator =
pm8xxx_amux_scaling_ratio[amux_prescaling].den;
rc = pm8xxx_adc_channel_power_enable(channel, true);
if (rc) {
rc = -EINVAL;
goto fail_unlock;
}
rc = pm8xxx_adc_configure(adc_pmic->conv);
if (rc) {
rc = -EINVAL;
goto fail;
}
rc = wait_for_completion_timeout(&adc_pmic->adc_rslt_completion,
PM8XXX_ADC_COMPLETION_TIMEOUT);
if (!rc) {
u8 data_arb_usrp_cntrl1 = 0;
rc = pm8xxx_adc_read_reg(PM8XXX_ADC_ARB_USRP_CNTRL1,
&data_arb_usrp_cntrl1);
if (rc < 0)
goto fail;
if (data_arb_usrp_cntrl1 == (PM8XXX_ADC_ARB_USRP_CNTRL1_EOC |
PM8XXX_ADC_ARB_USRP_CNTRL1_EN_ARB))
pr_debug("End of conversion status set\n");
else {
pr_err("EOC interrupt not received\n");
rc = -EINVAL;
goto fail;
}
}
rc = pm8xxx_adc_read_adc_code(&result->adc_code);
if (rc) {
rc = -EINVAL;
goto fail;
}
scale_type = adc_pmic->adc_channel[i].adc_scale_fn;
if (scale_type >= ADC_SCALE_NONE) {
rc = -EBADF;
goto fail;
}
adc_scale_fn[scale_type].chan(result->adc_code,
adc_pmic->adc_prop, adc_pmic->conv->chan_prop, result);
rc = pm8xxx_adc_channel_power_enable(channel, false);
if (rc) {
rc = -EINVAL;
goto fail_unlock;
}
mutex_unlock(&adc_pmic->adc_lock);
return 0;
fail:
rc_fail = pm8xxx_adc_channel_power_enable(channel, false);
if (rc_fail)
pr_err("pm8xxx adc power disable failed\n");
fail_unlock:
mutex_unlock(&adc_pmic->adc_lock);
pr_err("pm8xxx adc error with %d\n", rc);
return rc;
}
EXPORT_SYMBOL_GPL(pm8xxx_adc_read);
uint32_t pm8xxx_adc_mpp_config_read(uint32_t mpp_num,
enum pm8xxx_adc_channels channel,
struct pm8xxx_adc_chan_result *result)
{
struct pm8xxx_adc *adc_pmic = pmic_adc;
int rc = 0;
if (!pm8xxx_adc_initialized)
return -ENODEV;
if (!adc_pmic->mpp_base) {
rc = -EINVAL;
pr_info("PM8xxx MPP base invalid with error %d\n", rc);
return rc;
}
if (mpp_num == PM8XXX_AMUX_MPP_8) {
rc = -EINVAL;
pr_info("PM8xxx MPP8 is already configured "
"to AMUX8. Use pm8xxx_adc_read() instead.\n");
return rc;
}
mutex_lock(&adc_pmic->mpp_adc_lock);
rc = pm8xxx_mpp_config(((mpp_num - 1) + adc_pmic->mpp_base),
&pm8xxx_adc_mpp_config);
if (rc < 0) {
pr_err("pm8xxx adc mpp config error with %d\n", rc);
goto fail;
}
usleep_range(PM8XXX_ADC_MPP_SETTLE_TIME_MIN,
PM8XXX_ADC_MPP_SETTLE_TIME_MAX);
rc = pm8xxx_adc_read(channel, result);
if (rc < 0)
pr_err("pm8xxx adc read error with %d\n", rc);
rc = pm8xxx_mpp_config(((mpp_num - 1) + adc_pmic->mpp_base),
&pm8xxx_adc_mpp_unconfig);
if (rc < 0)
pr_err("pm8xxx adc mpp config error with %d\n", rc);
fail:
mutex_unlock(&adc_pmic->mpp_adc_lock);
return rc;
}
EXPORT_SYMBOL_GPL(pm8xxx_adc_mpp_config_read);
uint32_t pm8xxx_adc_btm_configure(struct pm8xxx_adc_arb_btm_param *btm_param)
{
struct pm8xxx_adc *adc_pmic = pmic_adc;
u8 data_btm_cool_thr0, data_btm_cool_thr1;
u8 data_btm_warm_thr0, data_btm_warm_thr1;
u8 arb_btm_cntrl1;
unsigned long flags = 0;
int rc;
if (adc_pmic == NULL) {
pr_err("PMIC ADC not valid\n");
return -EINVAL;
}
if ((btm_param->btm_cool_fn == NULL) &&
(btm_param->btm_warm_fn == NULL)) {
pr_err("No BTM warm/cool notification??\n");
return -EINVAL;
}
rc = pm8xxx_adc_batt_scaler(btm_param, adc_pmic->adc_prop,
adc_pmic->conv->chan_prop);
if (rc < 0) {
pr_err("Failed to lookup the BTM thresholds\n");
return rc;
}
if (btm_param->interval > PM8XXX_ADC_BTM_INTERVAL_MAX) {
pr_info("Bug in PMIC BTM interval time and cannot set"
" a value greater than 0x14 %x\n", btm_param->interval);
btm_param->interval = PM8XXX_ADC_BTM_INTERVAL_MAX;
}
spin_lock_irqsave(&adc_pmic->btm_lock, flags);
data_btm_cool_thr0 = ((btm_param->low_thr_voltage << 24) >> 24);
data_btm_cool_thr1 = ((btm_param->low_thr_voltage << 16) >> 24);
data_btm_warm_thr0 = ((btm_param->high_thr_voltage << 24) >> 24);
data_btm_warm_thr1 = ((btm_param->high_thr_voltage << 16) >> 24);
if (btm_param->btm_cool_fn != NULL) {
rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_BAT_COOL_THR0,
data_btm_cool_thr0);
if (rc < 0)
goto write_err;
rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_BAT_COOL_THR1,
data_btm_cool_thr1);
if (rc < 0)
goto write_err;
adc_pmic->batt.btm_cool_fn = btm_param->btm_cool_fn;
}
if (btm_param->btm_warm_fn != NULL) {
rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_BAT_WARM_THR0,
data_btm_warm_thr0);
if (rc < 0)
goto write_err;
rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_BAT_WARM_THR1,
data_btm_warm_thr1);
if (rc < 0)
goto write_err;
adc_pmic->batt.btm_warm_fn = btm_param->btm_warm_fn;
}
rc = pm8xxx_adc_read_reg(PM8XXX_ADC_ARB_BTM_CNTRL1, &arb_btm_cntrl1);
if (rc < 0)
goto bail_out;
btm_param->interval &= PM8XXX_ADC_BTM_INTERVAL_SEL_MASK;
arb_btm_cntrl1 |=
btm_param->interval << PM8XXX_ADC_BTM_INTERVAL_SEL_SHIFT;
rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_CNTRL1, arb_btm_cntrl1);
if (rc < 0)
goto write_err;
spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
return rc;
bail_out:
write_err:
spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
pr_debug("%s: with error code %d\n", __func__, rc);
return rc;
}
EXPORT_SYMBOL_GPL(pm8xxx_adc_btm_configure);
static uint32_t pm8xxx_adc_btm_read(uint32_t channel)
{
struct pm8xxx_adc *adc_pmic = pmic_adc;
int rc, i;
u8 arb_btm_dig_param, arb_btm_ana_param, arb_btm_rsv;
u8 arb_btm_amux_cntrl, data_arb_btm_cntrl = 0;
unsigned long flags;
arb_btm_amux_cntrl = channel << PM8XXX_ADC_BTM_CHANNEL_SEL;
arb_btm_rsv = adc_pmic->adc_channel[channel].adc_rsv;
arb_btm_dig_param = arb_btm_ana_param = PM8XXX_ADC_ARB_ANA_DIG;
spin_lock_irqsave(&adc_pmic->btm_lock, flags);
rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_AMUX_CNTRL,
arb_btm_amux_cntrl);
if (rc < 0)
goto write_err;
arb_btm_rsv = PM8XXX_ADC_BTM_RSV;
rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_RSV, arb_btm_rsv);
if (rc < 0)
goto write_err;
rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_DIG_PARAM,
arb_btm_dig_param);
if (rc < 0)
goto write_err;
rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_ANA_PARAM,
arb_btm_ana_param);
if (rc < 0)
goto write_err;
data_arb_btm_cntrl |= PM8XXX_ADC_ARB_BTM_CNTRL1_EN_BTM;
for (i = 0; i < 2; i++) {
rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_CNTRL1,
data_arb_btm_cntrl);
if (rc < 0)
goto write_err;
}
data_arb_btm_cntrl |= PM8XXX_ADC_ARB_BTM_CNTRL1_REQ
| PM8XXX_ADC_ARB_BTM_CNTRL1_SEL_OP_MODE;
rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_CNTRL1,
data_arb_btm_cntrl);
if (rc < 0)
goto write_err;
if (pmic_adc->batt.btm_warm_fn != NULL)
enable_irq(adc_pmic->btm_warm_irq);
if (pmic_adc->batt.btm_cool_fn != NULL)
enable_irq(adc_pmic->btm_cool_irq);
write_err:
spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
return rc;
}
uint32_t pm8xxx_adc_btm_start(void)
{
return pm8xxx_adc_btm_read(CHANNEL_BATT_THERM);
}
EXPORT_SYMBOL_GPL(pm8xxx_adc_btm_start);
uint32_t pm8xxx_adc_btm_end(void)
{
struct pm8xxx_adc *adc_pmic = pmic_adc;
int i, rc;
u8 data_arb_btm_cntrl = 0;
unsigned long flags;
disable_irq_nosync(adc_pmic->btm_warm_irq);
disable_irq_nosync(adc_pmic->btm_cool_irq);
spin_lock_irqsave(&adc_pmic->btm_lock, flags);
/* Write twice to the CNTRL register for the arbiter settings
to take into effect */
for (i = 0; i < 2; i++) {
rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_BTM_CNTRL1,
data_arb_btm_cntrl);
if (rc < 0) {
spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
return rc;
}
}
spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
return rc;
}
EXPORT_SYMBOL_GPL(pm8xxx_adc_btm_end);
static ssize_t pm8xxx_adc_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct pm8xxx_adc_chan_result result;
int rc = -1;
rc = pm8xxx_adc_read(attr->index, &result);
if (rc)
return 0;
return snprintf(buf, PM8XXX_ADC_HWMON_NAME_LENGTH,
"Result:%lld Raw:%d\n", result.physical, result.adc_code);
}
static int get_adc(void *data, u64 *val)
{
struct pm8xxx_adc_chan_result result;
int i = (int)data;
int rc;
rc = pm8xxx_adc_read(i, &result);
if (!rc)
pr_info("ADC value raw:%x physical:%lld\n",
result.adc_code, result.physical);
*val = result.physical;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_adc, NULL, "%llu\n");
#ifdef CONFIG_DEBUG_FS
static void create_debugfs_entries(void)
{
int i = 0;
pmic_adc->dent = debugfs_create_dir("pm8xxx_adc", NULL);
if (IS_ERR(pmic_adc->dent)) {
pr_err("pmic adc debugfs dir not created\n");
return;
}
for (i = 0; i < pmic_adc->adc_num_board_channel; i++)
debugfs_create_file(pmic_adc->adc_channel[i].name,
0644, pmic_adc->dent,
(void *)pmic_adc->adc_channel[i].channel_name,
&reg_fops);
}
#else
static inline void create_debugfs_entries(void)
{
}
#endif
static struct sensor_device_attribute pm8xxx_adc_attr =
SENSOR_ATTR(NULL, S_IRUGO, pm8xxx_adc_show, NULL, 0);
static int32_t pm8xxx_adc_init_hwmon(struct platform_device *pdev)
{
struct pm8xxx_adc *adc_pmic = pmic_adc;
int rc = 0, i, channel;
for (i = 0; i < pmic_adc->adc_num_board_channel; i++) {
channel = adc_pmic->adc_channel[i].channel_name;
if (pm8xxx_adc_check_channel_valid(channel)) {
pr_err("Invalid ADC init HWMON channel: %d\n", channel);
continue;
}
pm8xxx_adc_attr.index = adc_pmic->adc_channel[i].channel_name;
pm8xxx_adc_attr.dev_attr.attr.name =
adc_pmic->adc_channel[i].name;
memcpy(&adc_pmic->sens_attr[i], &pm8xxx_adc_attr,
sizeof(pm8xxx_adc_attr));
sysfs_attr_init(&adc_pmic->sens_attr[i].dev_attr.attr);
rc = device_create_file(&pdev->dev,
&adc_pmic->sens_attr[i].dev_attr);
if (rc) {
dev_err(&pdev->dev, "device_create_file failed for "
"dev %s\n",
adc_pmic->adc_channel[i].name);
goto hwmon_err_sens;
}
}
return 0;
hwmon_err_sens:
pr_info("Init HWMON failed for pm8xxx_adc with %d\n", rc);
return rc;
}
#ifdef CONFIG_PM
static int pm8xxx_adc_suspend_noirq(struct device *dev)
{
struct pm8xxx_adc *adc_pmic = pmic_adc;
adc_pmic->msm_suspend_check = 1;
return 0;
}
static int pm8xxx_adc_resume_noirq(struct device *dev)
{
struct pm8xxx_adc *adc_pmic = pmic_adc;
adc_pmic->msm_suspend_check = 0;
return 0;
}
static const struct dev_pm_ops pm8xxx_adc_dev_pm_ops = {
.suspend_noirq = pm8xxx_adc_suspend_noirq,
.resume_noirq = pm8xxx_adc_resume_noirq,
};
#define PM8XXX_ADC_DEV_PM_OPS (&pm8xxx_adc_dev_pm_ops)
#else
#define PM8XXX_ADC_DEV_PM_OPS NULL
#endif
static int __devexit pm8xxx_adc_teardown(struct platform_device *pdev)
{
struct pm8xxx_adc *adc_pmic = pmic_adc;
int i;
msm_xo_put(adc_pmic->adc_voter);
platform_set_drvdata(pdev, NULL);
pmic_adc = NULL;
if (!pa_therm) {
regulator_put(pa_therm);
pa_therm = NULL;
}
for (i = 0; i < adc_pmic->adc_num_board_channel; i++)
device_remove_file(adc_pmic->dev,
&adc_pmic->sens_attr[i].dev_attr);
pm8xxx_adc_initialized = false;
return 0;
}
static int __devinit pm8xxx_adc_probe(struct platform_device *pdev)
{
const struct pm8xxx_adc_platform_data *pdata = pdev->dev.platform_data;
struct pm8xxx_adc *adc_pmic;
struct pm8xxx_adc_amux_properties *adc_amux_prop;
int rc = 0;
if (!pdata) {
dev_err(&pdev->dev, "no platform data?\n");
return -EINVAL;
}
adc_pmic = devm_kzalloc(&pdev->dev, sizeof(struct pm8xxx_adc) +
(sizeof(struct sensor_device_attribute) *
pdata->adc_num_board_channel), GFP_KERNEL);
if (!adc_pmic) {
dev_err(&pdev->dev, "Unable to allocate memory\n");
return -ENOMEM;
}
adc_amux_prop = devm_kzalloc(&pdev->dev,
sizeof(struct pm8xxx_adc_amux_properties) +
sizeof(struct pm8xxx_adc_chan_properties)
, GFP_KERNEL);
if (!adc_amux_prop) {
dev_err(&pdev->dev, "Unable to allocate memory\n");
return -ENOMEM;
}
adc_pmic->dev = &pdev->dev;
adc_pmic->adc_prop = pdata->adc_prop;
adc_pmic->conv = adc_amux_prop;
init_completion(&adc_pmic->adc_rslt_completion);
adc_pmic->adc_channel = pdata->adc_channel;
adc_pmic->adc_num_board_channel = pdata->adc_num_board_channel;
adc_pmic->mpp_base = pdata->adc_mpp_base;
mutex_init(&adc_pmic->adc_lock);
mutex_init(&adc_pmic->mpp_adc_lock);
spin_lock_init(&adc_pmic->btm_lock);
adc_pmic->adc_irq = platform_get_irq(pdev, PM8XXX_ADC_IRQ_0);
if (adc_pmic->adc_irq < 0)
return adc_pmic->adc_irq;
rc = devm_request_irq(&pdev->dev, adc_pmic->adc_irq,
pm8xxx_adc_isr,
IRQF_TRIGGER_RISING, "pm8xxx_adc_interrupt", adc_pmic);
if (rc) {
dev_err(&pdev->dev, "failed to request adc irq "
"with error %d\n", rc);
} else {
enable_irq_wake(adc_pmic->adc_irq);
}
adc_pmic->btm_warm_irq = platform_get_irq(pdev, PM8XXX_ADC_IRQ_1);
if (adc_pmic->btm_warm_irq < 0)
return adc_pmic->btm_warm_irq;
rc = devm_request_irq(&pdev->dev, adc_pmic->btm_warm_irq,
pm8xxx_btm_warm_isr,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
"pm8xxx_btm_warm_interrupt", adc_pmic);
if (rc) {
pr_err("btm warm irq failed %d with interrupt number %d\n",
rc, adc_pmic->btm_warm_irq);
dev_err(&pdev->dev, "failed to request btm irq\n");
}
disable_irq_nosync(adc_pmic->btm_warm_irq);
adc_pmic->btm_cool_irq = platform_get_irq(pdev, PM8XXX_ADC_IRQ_2);
if (adc_pmic->btm_cool_irq < 0)
return adc_pmic->btm_cool_irq;
rc = devm_request_irq(&pdev->dev, adc_pmic->btm_cool_irq,
pm8xxx_btm_cool_isr,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
"pm8xxx_btm_cool_interrupt", adc_pmic);
if (rc) {
pr_err("btm cool irq failed with return %d and number %d\n",
rc, adc_pmic->btm_cool_irq);
dev_err(&pdev->dev, "failed to request btm irq\n");
}
disable_irq_nosync(adc_pmic->btm_cool_irq);
platform_set_drvdata(pdev, adc_pmic);
adc_pmic->msm_suspend_check = 0;
pmic_adc = adc_pmic;
INIT_WORK(&adc_pmic->warm_work, pm8xxx_adc_btm_warm_scheduler_fn);
INIT_WORK(&adc_pmic->cool_work, pm8xxx_adc_btm_cool_scheduler_fn);
create_debugfs_entries();
pm8xxx_adc_calib_first_adc = false;
pm8xxx_adc_calib_device_init = false;
pm8xxx_adc_initialized = true;
rc = pm8xxx_adc_init_hwmon(pdev);
if (rc) {
pr_err("pm8xxx adc init hwmon failed with %d\n", rc);
dev_err(&pdev->dev, "failed to initialize pm8xxx hwmon adc\n");
}
adc_pmic->hwmon = hwmon_device_register(adc_pmic->dev);
if (adc_pmic->adc_voter == NULL) {
adc_pmic->adc_voter = msm_xo_get(MSM_XO_TCXO_D0, "pmic_xoadc");
if (IS_ERR(adc_pmic->adc_voter)) {
dev_err(&pdev->dev, "Failed to get XO vote\n");
return PTR_ERR(adc_pmic->adc_voter);
}
}
pa_therm = regulator_get(adc_pmic->dev, "pa_therm");
if (IS_ERR(pa_therm)) {
rc = PTR_ERR(pa_therm);
pr_err("failed to request pa_therm vreg with error %d\n", rc);
pa_therm = NULL;
}
return 0;
}
static struct platform_driver pm8xxx_adc_driver = {
.probe = pm8xxx_adc_probe,
.remove = __devexit_p(pm8xxx_adc_teardown),
.driver = {
.name = PM8XXX_ADC_DEV_NAME,
.owner = THIS_MODULE,
.pm = PM8XXX_ADC_DEV_PM_OPS,
},
};
static int __init pm8xxx_adc_init(void)
{
return platform_driver_register(&pm8xxx_adc_driver);
}
module_init(pm8xxx_adc_init);
static void __exit pm8xxx_adc_exit(void)
{
platform_driver_unregister(&pm8xxx_adc_driver);
}
module_exit(pm8xxx_adc_exit);
MODULE_ALIAS("platform:" PM8XXX_ADC_DEV_NAME);
MODULE_DESCRIPTION("PMIC8921/8018 ADC driver");
MODULE_LICENSE("GPL v2");