static int bcmpmu_get_fg_currsmpl(struct bcmpmu *bcmpmu, int *data)
{
	int ret;
	struct bcmpmu_adc_req req;
	struct bcmpmu_fg *pfg = bcmpmu->fginfo;
	int curr;
	ret = bcmpmu->write_dev(bcmpmu,
				PMU_REG_FG_FRZSMPL,
				bcmpmu->regmap[PMU_REG_FG_FRZSMPL].mask,
				bcmpmu->regmap[PMU_REG_FG_FRZSMPL].mask);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s failed to latch fg smpl.\n", __func__);
		return ret;
	}
	req.sig = PMU_ADC_FG_CURRSMPL;
	req.tm = PMU_ADC_TM_HK;
	req.flags = PMU_ADC_RAW_AND_UNIT;
	ret = bcmpmu->adc_req(bcmpmu, &req);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s failed to get adc result.\n", __func__);
		return ret;
	}
	curr = req.cnv;
	*data = (curr * pfg->fg_factor) / 1000;
	return ret;
}
Beispiel #2
0
int bcmpmu_adc_read(struct bcmpmu59xxx *bcmpmu, enum bcmpmu_adc_channel channel,
		enum bcmpmu_adc_req req, struct bcmpmu_adc_result *result)
{
	int ret = 0;
	if (channel < PMU_ADC_CHANN_VMBATT || channel >= PMU_ADC_CHANN_MAX) {
		pr_hwmon(ERROR, "%s channel:%d out of range\n",
							__func__, channel);
		return -EINVAL;
	}

	if (result == NULL) {
		pr_hwmon(ERROR, "%s result struct is NULL\n", __func__);
		return -EINVAL;
	}

	if (req == PMU_ADC_REQ_SAR_MODE) {
		ret = read_sar_adc(bcmpmu, channel, result);
		if (ret != 0)
			return -EAGAIN;
	}
	if (req == PMU_ADC_REQ_RTM_MODE) {
		ret = read_rtm_adc(bcmpmu, channel, result);
		if (ret != 0)
			return -EAGAIN;
	}

	bcmpmu_adc_convert(bcmpmu, channel, result, false);

	return 0;
}
Beispiel #3
0
static void adc_isr(enum bcmpmu_irq irq, void *data)
{
	struct bcmpmu_adc *padc = data;

	pr_hwmon(FLOW, "%s: called\n", __func__);

	if (irq == PMU_IRQ_RTM_DATA_RDY) {
		if (padc->rtmreq == NULL) {
			pr_hwmon(ERROR, "%s: RTM not requested\n", __func__);
			return;
		}
		padc->rtmreq->ready = 1;
		wake_up(&padc->wait);
		return;
	} else if (irq == PMU_IRQ_RTM_UPPER) {
		padc->rtm_upper_lmt = 1;
		pr_hwmon(FLOW, "%s: irq RTM reset limit hit'\n", __func__);
	} else if ((irq == PMU_IRQ_RTM_IN_CON_MEAS) ||
		   (irq == PMU_IRQ_RTM_UPPER) ||
		   (irq == PMU_IRQ_RTM_IGNORE) ||
		   (irq == PMU_IRQ_RTM_OVERRIDDEN)) {
		pr_hwmon(FLOW, "%s: irq %d 'Getting handled'\n", __func__, irq);
	} else {
		pr_hwmon(FLOW, "%s: irq %d unsupported\n", __func__, irq);
		return;
	}
}
static int adc_map_pmu_temp(struct bcmpmu_adc *padc, int adc,
						enum bcmpmu_adc_sig sig) {

	const struct bcmpmu_temp_map *pmu_temp_map;
						/*PMU Temperature Sensor map*/
	int delta = 0;
	int pmu_temp = 0; /* PMU Temperature */
	int index = 0;

	pmu_temp_map = padc->pmu_temp_map;
	if (pmu_temp_map == NULL) {
		pr_hwmon(ERROR, "%s pmu_temp_map is NULL\n", __func__);
		return -EINVAL;
	}
	index = return_index(pmu_temp_map, padc->ptmap_len, adc);
	delta = adc - pmu_temp_map[index].adc;
	pmu_temp = pmu_temp_map[index].temp + (delta/2);
	pr_hwmon(DATA, "%s adc-r = %d adc = %d, temp = %d pmu_temp = %d\n",
						__func__, adc,
						pmu_temp_map[index].adc,
						pmu_temp_map[index].temp,
						pmu_temp);

	return pmu_temp;

}
Beispiel #5
0
static int bcmpmu_get_fg_fst_currsmpl(struct bcmpmu *bcmpmu, int *data)
{
	int ret;
	struct bcmpmu_adc_req req;
	struct bcmpmu_fg *pfg = bcmpmu->fginfo;
	struct bcmpmu_adc *padc = bcmpmu->adcinfo;
	int curr, curr_raw;
	unsigned int val, val1;
	static int init_read;

	if (init_read == 0) {
		ret = bcmpmu->read_dev(bcmpmu,
			PMU_REG_FG_OFFSET0, &val, PMU_BITMASK_ALL);
		ret |= bcmpmu->read_dev(bcmpmu,
			PMU_REG_FG_OFFSET1, &val1, PMU_BITMASK_ALL);
	if (ret != 0) {
			pr_hwmon(ERROR, "%s failed to read fg offset.\n",
				__func__);
		return ret;
	}
		if ((val & 0x80) == 0)
			padc->fg_offset = (int)(val1 | (val << 8));
		else
			padc->fg_offset = (int)(val1 | (val << 8) | 0XFFFF0000);

		ret = bcmpmu->read_dev(bcmpmu,
			PMU_REG_FG_GAINTRIM, &val, PMU_BITMASK_ALL);
		if (ret != 0) {
			pr_hwmon(ERROR, "%s failed to read fg gain.\n",
				__func__);
		}
		padc->fg_gain = (int)(val | ((val & 0x80) << 8) |
				((val & 0x80) << 9) | ((~val & 0x80) << 10));
		padc->fg_gain = (padc->fg_gain * 10) / 1024;
		init_read = 1;
	}

	req.sig = PMU_ADC_FG_FST_CURRSMPL;
	req.tm = PMU_ADC_TM_HK;
	req.flags = PMU_ADC_RAW_AND_UNIT;
	ret = bcmpmu->adc_req(bcmpmu, &req);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s failed to get adc result.\n", __func__);
		return ret;
	}
	curr_raw = req.cnv;
	curr = ((curr_raw - padc->fg_offset) * padc->fg_gain) / 1000;

	pr_hwmon(DATA, "%s raw curr=%d, gain=%d, offset=%d, curr=%d\n",
		__func__, curr_raw, padc->fg_gain, padc->fg_offset, curr);

	*data = curr;
	return ret;
}
Beispiel #6
0
static int bcmpmu_adc_raw_to_actual(struct bcmpmu_adc *adc,
				enum bcmpmu_adc_channel channel,
					struct bcmpmu_adc_result *result)

{
	struct bcmpmu_adc_lut *lut;
	int index = 0;
	int len = 0;

	if (adc->pdata[channel].lut) {
		len = adc->pdata[channel].lut_len;
		lut = adc->pdata[channel].lut;
		index = return_index(lut, len, result->raw);
		if (result->raw < lut[0].raw ||
					result->raw > lut[len - 1].raw) {
			pr_hwmon(ERROR, "ADC out of range, raw = %d\n",
								result->raw);
			result->conv = 0;
			return -EINVAL;
		}
		result->conv = INTERPOLATE_LINEAR(result->raw,
					lut[index].raw, lut[index].map,
					lut[index + 1].raw, lut[index + 1].map);

		pr_hwmon(FLOW, "index = %d, raw = %d, map = %d\n",
				index, lut[index].raw, lut[index].map);
		pr_hwmon(FLOW, "%s channel:%d raw = %x conv_lut = %d\n",
				__func__, channel, result->raw, result->conv);
		return 0;
	}
	if (channel != PMU_ADC_CHANN_DIE_TEMP) {
		result->conv = (result->raw * adc->pdata[channel].volt_range) /
					BCMPMU_ADC_RESOLUTION +
					(adc->pdata[channel].adc_offset);
	} else {
		/* temp = raw * 0.497 - 275.7 C
		 * But for better precision below formulae
		 * gives the result in 10th multiple of Centigrade*/
		result->conv = ((result->raw * PMU_TEMP_MULTI_CONST) -
					(KELVIN_CONST * 1000)) / 100;
	}

	pr_hwmon(FLOW, "%s channel:%d raw = %x conv_formula = %d\n",
			__func__, channel, result->raw, result->conv);
	return 0;
}
static int bcmpmu_fg_offset_cal_read(struct bcmpmu *bcmpmu, int *data)
{
	int off, off0, off1, ret;

	ret = bcmpmu->read_dev(bcmpmu,
			       PMU_REG_FG_OFFSET0, &off0, PMU_BITMASK_ALL);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s failed to read fg offset0.\n", __func__);
		return ret;
	}
	ret = bcmpmu->read_dev(bcmpmu,
			       PMU_REG_FG_OFFSET1, &off1, PMU_BITMASK_ALL);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s failed to read fg offset1.\n", __func__);
		return ret;
	}
	off = off1 | (off0 << 8);
	*data = off;
	return 0;
}
static int bcmpmu_fg_offset_cal(struct bcmpmu *bcmpmu)
{
	int ret;
	ret = bcmpmu->write_dev(bcmpmu,
				PMU_REG_FG_CAL,
				1 << bcmpmu->regmap[PMU_REG_FG_CAL].shift,
				bcmpmu->regmap[PMU_REG_FG_CAL].mask);
	if (ret != 0)
		pr_hwmon(ERROR, "%s failed to write device.\n", __func__);
	return ret;
}
static int bcmpmu_fg_enable(struct bcmpmu *bcmpmu, int en)
{
	int ret;
	ret = bcmpmu->write_dev(bcmpmu,
				PMU_REG_FG_HOSTEN,
				en << bcmpmu->regmap[PMU_REG_FG_HOSTEN].shift,
				bcmpmu->regmap[PMU_REG_FG_HOSTEN].mask);
	if (ret != 0)
		pr_hwmon(ERROR, "%s failed to write device.\n", __func__);
	return ret;
}
static int bcmpmu_fg_trim_write(struct bcmpmu *bcmpmu, int data)
{
	int ret;
	ret = bcmpmu->write_dev(bcmpmu,
				PMU_REG_FG_GAINTRIM,
				data << bcmpmu->regmap[PMU_REG_FG_CAL].shift,
				bcmpmu->regmap[PMU_REG_FG_CAL].mask);
	if (ret != 0)
		pr_hwmon(ERROR, "%s failed to write device.\n", __func__);
	return ret;
}
Beispiel #11
0
static int __devinit bcmpmu_adc_probe(struct platform_device *pdev)
{
	struct bcmpmu59xxx *bcmpmu = dev_get_drvdata(pdev->dev.parent);
	struct bcmpmu_adc	*adc;
	int i, ret = 0;

	printk(KERN_DEBUG "%s: called\n", __func__);

	gbcmpmu = bcmpmu;
	adc = kzalloc(sizeof(struct bcmpmu_adc), GFP_KERNEL);
	if (adc == NULL) {
		pr_hwmon(ERROR, "%s failed to alloc mem.\n", __func__);
		return -ENOMEM;
	}
	bcmpmu->adc = (void *)adc;

	adc->pdata = (struct bcmpmu_adc_pdata *)pdev->dev.platform_data;

	adc->hwmon_dev = hwmon_device_register(&pdev->dev);
	if (IS_ERR(adc->hwmon_dev)) {
		ret = PTR_ERR(adc->hwmon_dev);
		dev_err(&pdev->dev, "Class registration failed (%d)\n", ret);
		goto error;
	}

	/* SAR per channel mutes */
	for (i = 0; i < PMU_ADC_CHANN_MAX; i++)
		mutex_init(&adc->chann_mutex[i]);

	/* RTM mutex */
	mutex_init(&adc->rtm_mutex);

	/* Mask interrupts */
	bcmpmu->mask_irq(bcmpmu, PMU_IRQ_RTM_DATA_RDY);
	bcmpmu->mask_irq(bcmpmu, PMU_IRQ_RTM_IN_CON_MEAS);
	bcmpmu->mask_irq(bcmpmu, PMU_IRQ_RTM_UPPER);
	bcmpmu->mask_irq(bcmpmu, PMU_IRQ_RTM_IGNORE);
	bcmpmu->mask_irq(bcmpmu, PMU_IRQ_RTM_OVERRIDDEN);

	ret = sysfs_create_group(&pdev->dev.kobj, &bcmpmu_hwmon_attr_group);
	if (ret != 0)
		goto exit_remove_files;
#ifdef CONFIG_DEBUG_FS
	bcmpmu_adc_debug_init(bcmpmu);
#endif
	return 0;
exit_remove_files:
	 sysfs_remove_group(&pdev->dev.kobj, &bcmpmu_hwmon_attr_group);
error:
	kfree(adc);
	return 0;
}
static int bcmpmu_get_fg_vmbatt(struct bcmpmu *bcmpmu, int *data)
{
	int ret;
	struct bcmpmu_adc_req req;
	req.sig = PMU_ADC_FG_VMBATT;
	req.tm = PMU_ADC_TM_HK;
	req.flags = PMU_ADC_RAW_AND_UNIT;
	ret = bcmpmu->adc_req(bcmpmu, &req);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s failed to get adc result.\n", __func__);
		return ret;
	}
	*data = req.cnv;
	return ret;
}
Beispiel #13
0
static int bcmpmu_get_fg_currsmpl(struct bcmpmu *bcmpmu, int *data)
{
	int ret;
	struct bcmpmu_adc_req req;
	struct bcmpmu_fg *pfg = bcmpmu->fginfo;
	int curr;

	req.sig = PMU_ADC_FG_CURRSMPL;
	req.tm = PMU_ADC_TM_HK;
	req.flags = PMU_ADC_RAW_AND_UNIT;
	ret = bcmpmu->adc_req(bcmpmu, &req);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s failed to get adc result.\n", __func__);
		return ret;
	}
	*data = req.cnv;
	return ret;
}
/* bcmpmu_set_adcunit_data
 * Input: signal number, pointer to adc_unit data to use
 * Descripton: Copies the adc_unit data from supplied pointer
 *to main structure
*/
static int bcmpmu_set_adcunit_data(struct bcmpmu *bcmpmu,
				   enum bcmpmu_adc_sig sig,
				   struct bcmpmu_adc_unit *unit)
{
	struct bcmpmu_adc *padc = bcmpmu->adcinfo;
	struct bcmpmu_adc_unit *adcunit;

	/* Do we have unit data at all */
	if (!padc->adcunit)
		return -EINVAL;

	/* Then verify that the channel is defined in the map */
	if ((padc->adcmap[sig].addr0 == 0) && (padc->adcmap[sig].addr1 == 0)) {
		pr_hwmon(ERROR, "%s: sig map failed\n", __func__);
		return -EINVAL;
	}
	adcunit = &padc->adcunit[sig];

	/* Then copy the contents of the unit structure */
	memcpy(adcunit, unit, sizeof(struct bcmpmu_adc_unit));
	return 0;

}
Beispiel #15
0
static int bcmpmu_adc_actual_to_raw(struct bcmpmu_adc *adc,
		enum bcmpmu_adc_channel channel,
		struct bcmpmu_adc_result *result)
{
	struct bcmpmu_adc_lut *lut;
	int len = 0;

	if (adc->pdata[channel].lut && is_temp_channel(channel)) {
		len = adc->pdata[channel].lut_len;
		lut = adc->pdata[channel].lut;
		result->raw = get_temp_to_raw(lut, len, result->conv);
	} else if (adc->pdata[channel].lut && !is_temp_channel(channel)) {
		pr_hwmon(ERROR, "%s: not implemented\n", __func__);
		result->raw = 0;
	} else if (channel != PMU_ADC_CHANN_DIE_TEMP)
		result->raw =
			(((result->conv  - adc->pdata[channel].adc_offset) *
				BCMPMU_ADC_RESOLUTION) /
			 adc->pdata[channel].volt_range);
	else
		result->raw = (((result->conv * 100) + (KELVIN_CONST * 1000)) /
			PMU_TEMP_MULTI_CONST);
	return 0;
}
Beispiel #16
0
int read_rtm_adc(struct bcmpmu59xxx *bcmpmu, enum bcmpmu_adc_channel channel,
					struct bcmpmu_adc_result *result) {
	struct bcmpmu_adc *adc = (struct bcmpmu_adc *)bcmpmu->adc;
	unsigned char rtm_read[ADC_READ_LEN] = {0, 0};
	u8 val = 0;
	int ret = 0;

	if (channel >= PMU_ADC_CHANN_MAX || channel == PMU_ADC_CHANN_RESERVED)
		return -EINVAL;

	wake_lock(&adc->wake_lock);

	mutex_lock(&adc->rtm_mutex);
	pr_hwmon(FLOW, "%s Start channel = %d\n", __func__, channel);	
	if (adc->rtm_upper_lmt != 0) {
		adc->rtm_upper_lmt = 0;
		msleep(20);
	}	

#if defined(CONFIG_MACH_HAWAII_SS_COMMON)|| defined(CONFIG_MACH_JAVA_SS_COMMON)
	if ((channel == PMU_ADC_CHANN_32KTEMP) || (channel == PMU_ADC_CHANN_ALS) ||
				(channel == PMU_ADC_CHANN_PATEMP))
		channel = PMU_ADC_CHANN_PATEMP;
#endif
			
	init_completion(&adc->rtm_ready_complete);
	adc->int_status = 0;
	val = channel << ADC_RTM_CHANN_SHIFT;
	val |= (ADC_RTM_CONV_ENABLE << ADC_RTM_CONVERSION_SHIFT);
	val |= (ADC_RTM_START << ADC_RTM_START_SHIFT);
	val |= ADC_RTM_MAX_RST_CNT_7;

	ret = bcmpmu->write_dev(bcmpmu, PMU_REG_ADCCTRL1, val);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s I2C write failed\n", __func__);
		goto err;
	}

	if (!wait_for_completion_timeout(&adc->rtm_ready_complete, msecs_to_jiffies(ADC_RTM_TIMEOUT))) {

		ret = bcmpmu->read_dev(bcmpmu, PMU_REG_INT9, &val);
		if (ret != 0) {
			pr_hwmon(ERROR, "%s I2C write failed\n", __func__);
			goto err;
		}
		pr_hwmon(ERROR,
			"%s: Timeout waiting for ADC_RTM_DATA_READY, INT9: 0x%x\n",
			__func__, val);
		goto err;
	}
	if (adc->int_status == PMU_IRQ_RTM_IGNORE) {
		pr_hwmon(ERROR, "%s RTM_IGNORE INT\n", __func__);
		goto err;
	}

	ret = bcmpmu->read_dev_bulk(bcmpmu, PMU_REG_ADCCTRL27, rtm_read,
								ADC_READ_LEN);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s I2C read failed :%d\n", __func__, __LINE__);
		goto err;
	}

	if (rtm_read[0] & ADC_READ_INVALID) {
		pr_hwmon(ERROR, "%s RTM read INVALID, try again\n", __func__);
		goto err;

	} else {
		/* clear all except 0, 1 bits*/
		result->raw = (rtm_read[0] & ADC_MSB_DATA);
		result->raw = (result->raw << ADC_MSB_SHIFT);
		result->raw |= rtm_read[1];
	}

	val = ADC_RTM_CONV_DISABLE << ADC_RTM_CONVERSION_SHIFT;
	ret = bcmpmu->write_dev(bcmpmu, PMU_REG_ADCCTRL1, val);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s I2C write failed\n", __func__);
		goto err;
	}

	mutex_unlock(&adc->rtm_mutex);
	wake_unlock(&adc->wake_lock);
	pr_hwmon(FLOW, "%s Done channel:%d, raw:%x\n", __func__, channel,	
								result->raw);

	return 0;
err:

	val = ADC_RTM_CONV_DISABLE << ADC_RTM_CONVERSION_SHIFT;
	ret = bcmpmu->write_dev(bcmpmu, PMU_REG_ADCCTRL1, val);
	mutex_unlock(&adc->rtm_mutex);
	wake_unlock(&adc->wake_lock);		
	if (ret != 0)
		pr_hwmon(ERROR, "%s I2C write failed\n", __func__);

	return -EAGAIN;

}
Beispiel #17
0
int read_sar_adc(struct bcmpmu59xxx *bcmpmu, enum bcmpmu_adc_channel channel,
					struct bcmpmu_adc_result *result)
{
	struct bcmpmu_adc *adc = (struct bcmpmu_adc *)bcmpmu->adc;
	unsigned char val[ADC_READ_LEN];
	int ret = 0;

	pr_hwmon(FLOW, "%s channel = %d\n", __func__, channel);

	switch (channel) {
	case PMU_ADC_CHANN_VMBATT:
		mutex_lock(&adc->chann_mutex[PMU_ADC_CHANN_VMBATT]);
		ret = bcmpmu->read_dev_bulk(bcmpmu, PMU_REG_ADCCTRL3,
				val, ADC_READ_LEN);
		mutex_unlock(&adc->chann_mutex[PMU_ADC_CHANN_VMBATT]);
		break;

	case PMU_ADC_CHANN_VBBATT:
		mutex_lock(&adc->chann_mutex[PMU_ADC_CHANN_VBBATT]);
		ret = bcmpmu->read_dev_bulk(bcmpmu, PMU_REG_ADCCTRL5,
						val, ADC_READ_LEN);
		mutex_unlock(&adc->chann_mutex[PMU_ADC_CHANN_VBBATT]);
		break;

	case PMU_ADC_CHANN_VBUS:
		mutex_lock(&adc->chann_mutex[PMU_ADC_CHANN_VBUS]);
		ret = bcmpmu->read_dev_bulk(bcmpmu, PMU_REG_ADCCTRL9,
						val, ADC_READ_LEN);
		mutex_unlock(&adc->chann_mutex[PMU_ADC_CHANN_VBUS]);
		break;

	case PMU_ADC_CHANN_IDIN:
		mutex_lock(&adc->chann_mutex[PMU_ADC_CHANN_IDIN]);
		ret = bcmpmu->read_dev_bulk(bcmpmu, PMU_REG_ADCCTRL11,
						val, ADC_READ_LEN);
		mutex_unlock(&adc->chann_mutex[PMU_ADC_CHANN_IDIN]);
		break;

	case PMU_ADC_CHANN_BSI:
		mutex_lock(&adc->chann_mutex[PMU_ADC_CHANN_BSI]);
		ret = bcmpmu->read_dev_bulk(bcmpmu, PMU_REG_ADCCTRL15,
						val, ADC_READ_LEN);
		mutex_unlock(&adc->chann_mutex[PMU_ADC_CHANN_BSI]);
		break;

	case PMU_ADC_CHANN_BOM:
		mutex_lock(&adc->chann_mutex[PMU_ADC_CHANN_BOM]);
		ret = bcmpmu->read_dev_bulk(bcmpmu, PMU_REG_ADCCTRL17,
						val, ADC_READ_LEN);
		mutex_unlock(&adc->chann_mutex[PMU_ADC_CHANN_BOM]);
		break;

#if defined(CONFIG_MACH_HAWAII_SS_COMMON)|| defined(CONFIG_MACH_JAVA_SS_COMMON)
	/* Logan R01 and future */
	case PMU_ADC_CHANN_NTC:
		mutex_lock(&adc->chann_mutex[PMU_ADC_CHANN_NTC]);
		ret = bcmpmu->read_dev_bulk(bcmpmu, PMU_REG_ADCCTRL13,
						val, ADC_READ_LEN);
		mutex_unlock(&adc->chann_mutex[PMU_ADC_CHANN_NTC]);
		break;

	case PMU_ADC_CHANN_32KTEMP:
	case PMU_ADC_CHANN_ALS:
	case PMU_ADC_CHANN_PATEMP:
		mutex_lock(&adc->chann_mutex[PMU_ADC_CHANN_PATEMP]);
		ret = bcmpmu->read_dev_bulk(bcmpmu, PMU_REG_ADCCTRL21,
						val, ADC_READ_LEN);
		mutex_unlock(&adc->chann_mutex[PMU_ADC_CHANN_PATEMP]);
		break;

#else
	case PMU_ADC_CHANN_NTC:
		mutex_lock(&adc->chann_mutex[PMU_ADC_CHANN_NTC]);
		ret = bcmpmu->read_dev_bulk(bcmpmu, PMU_REG_ADCCTRL13,
						val, ADC_READ_LEN);
		mutex_unlock(&adc->chann_mutex[PMU_ADC_CHANN_NTC]);
		break;

	case PMU_ADC_CHANN_32KTEMP:
		mutex_lock(&adc->chann_mutex[PMU_ADC_CHANN_32KTEMP]);
		ret = bcmpmu->read_dev_bulk(bcmpmu, PMU_REG_ADCCTRL19,
						val, ADC_READ_LEN);
		mutex_unlock(&adc->chann_mutex[PMU_ADC_CHANN_32KTEMP]);
		break;

	case PMU_ADC_CHANN_ALS:
		mutex_lock(&adc->chann_mutex[PMU_ADC_CHANN_ALS]);
		ret = bcmpmu->read_dev_bulk(bcmpmu, PMU_REG_ADCCTRL23,
						val, ADC_READ_LEN);
		mutex_unlock(&adc->chann_mutex[PMU_ADC_CHANN_ALS]);
		break;

	case PMU_ADC_CHANN_PATEMP:
		mutex_lock(&adc->chann_mutex[PMU_ADC_CHANN_PATEMP]);
		ret = bcmpmu->read_dev_bulk(bcmpmu, PMU_REG_ADCCTRL21,
						val, ADC_READ_LEN);
		mutex_unlock(&adc->chann_mutex[PMU_ADC_CHANN_PATEMP]);
		break;
#endif

	case PMU_ADC_CHANN_DIE_TEMP:
		mutex_lock(&adc->chann_mutex[PMU_ADC_CHANN_DIE_TEMP]);
		ret = bcmpmu->read_dev_bulk(bcmpmu, PMU_REG_ADCCTRL25,
						val, ADC_READ_LEN);
		mutex_unlock(&adc->chann_mutex[PMU_ADC_CHANN_DIE_TEMP]);
		break;

	default:
		pr_hwmon(ERROR, "INVALID SAR ADC read request\n");
		return -EINVAL;


	}

	if (ret != 0) {
		pr_hwmon(ERROR, "%s I2C bulk read failed\n", __func__);
		return -EAGAIN;
	}

	if (val[0] & ADC_READ_INVALID) {
		pr_hwmon(ERROR, "%s SAR ADC read fail, try again\n", __func__);
		return -EAGAIN;
	} else {
		/* clear all except 0, 1 bits*/
		result->raw = (val[0] & ADC_MSB_DATA);
		result->raw = (result->raw << ADC_MSB_SHIFT);
		result->raw |= val[1];
	}

	pr_hwmon(FLOW, "%s channel:%d, raw:%x\n", __func__, channel,
								result->raw);
	return 0;

}
Beispiel #18
0
int read_rtm_adc(struct bcmpmu59xxx *bcmpmu, enum bcmpmu_adc_channel channel,
					struct bcmpmu_adc_result *result) {
	struct bcmpmu_adc *adc = (struct bcmpmu_adc *)bcmpmu->adc;
	unsigned char rtm_read[ADC_READ_LEN] = {0, 0};
	u8 val = 0;
	int ret = 0;
	int poll;

	pr_hwmon(FLOW, "%s channel = %d\n", __func__, channel);
	if (channel >= PMU_ADC_CHANN_MAX || channel == PMU_ADC_CHANN_RESERVED)
		return -EINVAL;

	mutex_lock(&adc->rtm_mutex);

	val = channel << ADC_RTM_CHANN_SHIFT;
	val |= (ADC_RTM_CONV_ENABLE << ADC_RTM_CONVERSION_SHIFT);
	val |= (ADC_RTM_START << ADC_RTM_START_SHIFT);
	val |= ADC_RTM_MAX_RST_CNT;

	ret = bcmpmu->write_dev(bcmpmu, PMU_REG_ADCCTRL1, val);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s I2C write failed\n", __func__);
		goto err;
	}

	for (poll = 0; poll < ADC_RTM_MAX_POLL; poll++) {
		msleep(ADC_RTM_SLEEP);
		ret = bcmpmu->read_dev(bcmpmu, PMU_REG_INT9, &val);
		if (ret != 0) {
			pr_hwmon(ERROR, "%s I2C read failed\n", __func__);
			goto err;
		} else {
			if (val & ADC_RTM_DATA_READY) {
				bcmpmu->write_dev(bcmpmu,
						PMU_REG_INT9,
						(val & ~ADC_RTM_DATA_READY));
				break;
			}
		}
	}

	if (poll == (ADC_RTM_MAX_POLL - 1)) {
		pr_hwmon(ERROR, "%s: exceeded max polls\n", __func__);
		goto err;
	}


	ret = bcmpmu->read_dev_bulk(bcmpmu, PMU_REG_ADCCTRL27, rtm_read,
								ADC_READ_LEN);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s I2C read failed :%d\n", __func__, __LINE__);
		goto err;
	}

	if (rtm_read[0] & ADC_READ_INVALID) {
		pr_hwmon(ERROR, "%s RTM read INVALID, try again\n", __func__);
		goto err;

	} else {
		/* clear all except 0, 1 bits*/
		result->raw = (rtm_read[0] & ADC_MSB_DATA);
		result->raw = (result->raw << ADC_MSB_SHIFT);
		result->raw |= rtm_read[1];
	}
	pr_hwmon(FLOW, "%s channel:%d, raw:%x\n", __func__, channel,
								result->raw);
	mutex_unlock(&adc->rtm_mutex);
	return 0;
err:
	mutex_unlock(&adc->rtm_mutex);
	return -EAGAIN;

}
static int bcmpmu_adc_request(struct bcmpmu *bcmpmu, struct bcmpmu_adc_req *req)
{
	struct bcmpmu_adc *padc = bcmpmu->adcinfo;
	int ret = -EINVAL, timeout;
	struct pin_config StoredPinmux, TestPinMux;
	unsigned adcsyngpio;
	enum PIN_FUNC adcsyngpiomux;

	pr_hwmon(FLOW, "%s: called: ->sig %d, tm %d, flags %d\n", __func__,
		 req->sig, req->tm, req->flags);
	if (req->flags == PMU_ADC_RAW_ONLY ||
	    req->flags == PMU_ADC_RAW_AND_UNIT) {
		if ((req->tm == PMU_ADC_TM_RTM_SW) ||
		    (req->tm == PMU_ADC_TM_RTM_SW_TEST))
			timeout = padc->adcsetting->sw_timeout;
		else
			timeout = padc->adcsetting->txrx_timeout;

		switch (req->tm) {
		case PMU_ADC_TM_HK:
			ret = update_adc_result(padc, req);
			break;
		case PMU_ADC_TM_RTM_TX:
		case PMU_ADC_TM_RTM_RX:
		case PMU_ADC_TM_RTM_SW:
		case PMU_ADC_TM_RTM_SW_TEST:
			mutex_lock(&padc->lock);
			padc->rtmreq = req;
			if (req->tm == PMU_ADC_TM_RTM_SW_TEST) {
				pinmux_find_gpio(PN_ADCSYN, &adcsyngpio,
						 &adcsyngpiomux);
				pr_hwmon(FLOW, "%s: SW_TEST: Pin:%u, "
					 "Gpio:%u, Mux:%u\n", __func__,
					 PN_ADCSYN, adcsyngpio, adcsyngpiomux);
				/* Setup test pinmuxing */
				StoredPinmux.name = PN_ADCSYN;
				pinmux_get_pin_config(&StoredPinmux);
				TestPinMux.name = PN_ADCSYN;
				pinmux_get_pin_config(&TestPinMux);
				TestPinMux.func = adcsyngpiomux;
				pinmux_set_pin_config(&TestPinMux);
				gpio_request(adcsyngpio, "ADCSYN_GPIO");
				gpio_direction_output(adcsyngpio, 1);
				/* Use TX for test */
				padc->bcmpmu->write_dev_drct(padc->bcmpmu,
							     padc->
							     ctrlmap
							     [PMU_ADC_RTM_DLY].
							     map,
							     padc->
							     ctrlmap
							     [PMU_ADC_RTM_DLY].
							     addr, 0,
							     padc->
							     ctrlmap
							     [PMU_ADC_RTM_DLY].
							     mask);
				bcmpmu_sel_adcsync(PMU_ADC_TM_RTM_TX);
			}
			/* config hw for rtm adc */
			if (req->tm == PMU_ADC_TM_RTM_TX) {
				padc->bcmpmu->write_dev_drct(padc->bcmpmu,
							     padc->
							     ctrlmap
							     [PMU_ADC_RTM_DLY].
							     map,
							     padc->
							     ctrlmap
							     [PMU_ADC_RTM_DLY].
							     addr,
							     padc->adcsetting->
							     tx_delay << padc->
							     ctrlmap
							     [PMU_ADC_RTM_DLY].
							     shift,
							     padc->
							     ctrlmap
							     [PMU_ADC_RTM_DLY].
							     mask);
				bcmpmu_sel_adcsync(PMU_ADC_TM_RTM_TX);
			}
			if (req->tm == PMU_ADC_TM_RTM_RX) {
				padc->bcmpmu->write_dev_drct(padc->bcmpmu,
							     padc->
							     ctrlmap
							     [PMU_ADC_RTM_DLY].
							     map,
							     padc->
							     ctrlmap
							     [PMU_ADC_RTM_DLY].
							     addr,
							     padc->adcsetting->
							     rx_delay << padc->
							     ctrlmap
							     [PMU_ADC_RTM_DLY].
							     shift,
							     padc->
							     ctrlmap
							     [PMU_ADC_RTM_DLY].
							     mask);
				bcmpmu_sel_adcsync(PMU_ADC_TM_RTM_RX);
			}
			req->ready = 0;
			padc->bcmpmu->write_dev_drct(padc->bcmpmu,
						     padc->
						     ctrlmap[PMU_ADC_RTM_SEL].
						     map,
						     padc->
						     ctrlmap[PMU_ADC_RTM_SEL].
						     addr,
						     padc->adcmap[req->sig].
						     rtmsel << padc->
						     ctrlmap[PMU_ADC_RTM_SEL].
						     shift,
						     padc->
						     ctrlmap[PMU_ADC_RTM_SEL].
						     mask);
			padc->bcmpmu->write_dev_drct(padc->bcmpmu,
						     padc->
						     ctrlmap[PMU_ADC_RTM_MASK].
						     map,
						     padc->
						     ctrlmap[PMU_ADC_RTM_MASK].
						     addr, 0,
						     padc->
						     ctrlmap[PMU_ADC_RTM_MASK].
						     mask);
			if (req->tm == PMU_ADC_TM_RTM_SW) {
				padc->bcmpmu->write_dev_drct(padc->bcmpmu,
							     padc->
							     ctrlmap
							     [PMU_ADC_RTM_DLY].
							     map,
							     padc->
							     ctrlmap
							     [PMU_ADC_RTM_DLY].
							     addr, 0,
							     padc->
							     ctrlmap
							     [PMU_ADC_RTM_DLY].
							     mask);
				padc->bcmpmu->write_dev_drct(padc->bcmpmu,
							     padc->
							     ctrlmap
							     [PMU_ADC_RTM_MASK].
							     map,
							     padc->
							     ctrlmap
							    [PMU_ADC_RTM_START].
							     addr,
							     padc->
							     ctrlmap
							    [PMU_ADC_RTM_START].
							     mask,
							     padc->
							     ctrlmap
							    [PMU_ADC_RTM_START].
							     mask);
			}
			pr_hwmon(FLOW, "%s: start rtm adc\n", __func__);
			if (req->tm == PMU_ADC_TM_RTM_SW_TEST) {
				/* Set ADC_SYNC to Low */
				msleep(20);
				gpio_set_value(adcsyngpio, 0);
			}
			if (wait_event_interruptible_timeout(padc->wait,
							     req->ready,
							     timeout) == 0) {
				pr_hwmon(ERROR, "%s: RTM ADC timeout\n",
					 __func__);
				req->raw = 0;
				ret = -ETIMEDOUT;
			} else
				ret = update_adc_result(padc, req);
			padc->rtmreq = NULL;
			pr_hwmon(FLOW, "%s: Wait/update_adc_result returned %d",
				 __func__, ret);
			/* Need to disable RTM to avoid interrrupts from
			   ADC_SYN activated RTM reads */
			padc->bcmpmu->write_dev_drct(padc->bcmpmu,
						     padc->
						     ctrlmap[PMU_ADC_RTM_MASK].
						     map,
						     padc->
						     ctrlmap[PMU_ADC_RTM_MASK].
						     addr,
						     padc->
						     ctrlmap[PMU_ADC_RTM_MASK].
						     mask,
						     padc->
						     ctrlmap[PMU_ADC_RTM_MASK].
						     mask);
			if (req->tm == PMU_ADC_TM_RTM_SW_TEST) {
				/* Set ADC_SYNC to High */
				gpio_set_value(adcsyngpio, 1);

				/* Restore */
				gpio_free(adcsyngpio);
				pinmux_set_pin_config(&StoredPinmux);
			}
			mutex_unlock(&padc->lock);
			break;
		case PMU_ADC_TM_MAX:
		default:
			ret = -EINVAL;
		}

		if (ret < 0)
			return ret;
	}
	if ((req->flags == PMU_ADC_UNIT_ONLY ||
	     req->flags == PMU_ADC_RAW_AND_UNIT)) {

		/* This gives us a voltage in req->cal */
		cal_adc_result(padc, req);
		/* This updates the req->cnv with the value */
		cnv_adc_result(padc, req);
	} else {
		req->cal = req->raw;
		req->cnv = req->raw;
	}

	if(req->sig == PMU_ADC_VMBATT)
	{
	      req->cnv = spa_reward_voltage(req->cnv);
	}
   
	pr_hwmon(DATA, "%s: result sig=%d, raw=0x%X, cal=0x%X, cnv=%d\n",
		 __func__, req->sig, req->raw, req->cal, req->cnv);

	return ret;
}
/* cnv_adc_result
 * Input: request structure
 * Descripton: Converts the calibrated value (uV or raw copy) to the
 * appropiate channel unit.
 * Voltage channels will return mV, temperature channels K, current channels
 * mA and resitive
 *channels HOhm.
*/
static void cnv_adc_result(struct bcmpmu_adc *padc, struct bcmpmu_adc_req *req)
{
	struct bcmpmu_fg *pfg = padc->bcmpmu->fginfo;
	switch (req->sig) {
	case PMU_ADC_VMBATT:
	case PMU_ADC_VBBATT:
	case PMU_ADC_VBUS:
	case PMU_ADC_ID:
	case PMU_ADC_FG_VMBATT:
		if (padc->adcunit) {
			/* uV to mV, include rounding */
			req->cnv = (req->cal + 500) / 1000;
		} else
			req->cnv =
			    (req->cal * padc->adcmap[req->sig].vrng) / 1024;
		break;
	case PMU_ADC_NTC:
	case PMU_ADC_PATEMP:
	case PMU_ADC_32KTEMP:
		if (padc->adcunit) {
			/* req->cal is in uV, table is in mV */
			req->cnv = adc_map_batt_temp(padc, (req->cal / 1000),
						     req->sig);
		} else {
			/* Table is the raw value */
			req->cnv = adc_map_batt_temp(padc, req->cal, req->sig);
		}
		break;
	case PMU_ADC_FG_RAW:
	case PMU_ADC_FG_CURRSMPL:
		{
			int modifier = 0, ibat_to_return;
			int reading;
			int voffset = 0;
			reading = req->raw;
			if (req->cal & 0x8000)	/* negative offset */
				reading |= 0xffff0000;

			if (padc->adcunit && padc->adcunit[req->sig].fg_k) {
				modifier =
				    (padc->adcunit[req->sig].fg_k >
				     128) ? 768 +
				    padc->adcunit[req->sig].fg_k : 1024 +
				    padc->adcunit[req->sig].fg_k;
				ibat_to_return =
				    ((reading * modifier) / 1024) -
				    padc->adcunit[req->sig].voffset;
				pr_hwmon(DATA,
					 "%s: reading %d, ibat_to_return before"
					 " negation and 976 modification %d",
					 __func__, reading, ibat_to_return);
				voffset = padc->adcunit[req->sig].voffset;
			} else
				ibat_to_return = reading;
			ibat_to_return =
			    (ibat_to_return * pfg->fg_factor) / 1000;
			pr_hwmon(DATA,
				 "%s: raw %x, value %d, modifier %d Offset %d,"
				 " ibat %d",
				 __func__, req->raw, reading, modifier,
				 voffset, ibat_to_return);
			req->cnv = ibat_to_return;
		}
		break;
	case PMU_ADC_BSI:
		{
			struct bcmpmu_adc_req ibat_req;
			int iread;
			/* Compensate with ibat */
			if (!padc->adcunit) {
				req->cnv = req->cal;
				return;
			}
			ibat_req.sig = PMU_ADC_FG_RAW;
			ibat_req.tm = PMU_ADC_TM_HK;
			ibat_req.flags = PMU_ADC_RAW_AND_UNIT;
			padc->bcmpmu->adc_req(padc->bcmpmu, &ibat_req);
			pr_hwmon(DATA,
				 "%s: req->cal before %d, ibat_req.cnv %d",
				 __func__, req->cal, ibat_req.cnv);
			/* compensate reading with the 25 mOhm in the
			   FG and battery terminal */
			req->cal -= ((ibat_req.cnv + 20) / 40) * 1000;
			/* Calculate the current in the BSI resistor.
			   Unit is 10 nA */
			iread =
			    ((padc->adcunit[req->sig].vmax -
			      req->cal) * 100) /
			    (padc->adcunit[req->sig].rpullup);
			if (!iread) {
				/* No resistor is present. */
				req->cnv = 0;
				break;
			}
			pr_hwmon(DATA, "%s: req->cal adjusted %d, iread %d",
				 __func__, req->cal, iread);
			/* Calculate the resistor */
			/* Hecto ohm - iread>>1 is for rounding */
			req->cnv = req->cal / iread;
			pr_hwmon(DATA,
				 "%s: req->cnv %d", __func__, req->cnv);
		}
		break;
	case PMU_ADC_BOM:
		if (padc->adcunit[req->sig].lut_ptr) { /* bom_map */
			/* Lookup it up in the look-up table... */
			/* We have voltages in uV */
			req->cnv = adc_map_bom(padc, req->cal / 1000);
		} else
			req->cnv = 0;
		break;

	case PMU_ADC_TEMP_SNS:

		req->cnv = adc_map_pmu_temp(padc, req->raw, req->sig);
		break;

	default:
		req->cnv = req->cal;
		break;
	}
}
/* cal_adc_result
 * Input: request structure
 * Descripton: if adcunit is defined, req->cal will set to the calibrated
 *voltage in uV.
 * Else req->cal will be set to the raw value.
*/
static void cal_adc_result(struct bcmpmu_adc *padc, struct bcmpmu_adc_req *req)
{
	struct bcmpmu_adc_req cal_req;
	u16 i;
	static u16 read1, read2;
	static int last_temperature, new_temperature;
	static int gain, offset;
	static u32 now, last_time;

	if (!padc->adcunit) {
		req->cal = req->raw;
		return;
	}

	switch (req->sig) {
	case PMU_ADC_NTC:
		new_temperature = req->raw;
		/* fall through */
	case PMU_ADC_PATEMP:
	case PMU_ADC_32KTEMP:
	case PMU_ADC_BSI:
	case PMU_ADC_BOM:
		if (padc->adcsetting->compensation_interval) {
			/* check if calibration channels should be refreshed */
			mutex_lock(&padc->cal_lock);
			now = get_seconds();
			if ((!read1)
			    || (last_time +
				padc->adcsetting->compensation_interval < now)
			    || (abs(last_temperature - new_temperature) > 16)) {
				pr_hwmon(DATA,
					 "%s: Reading calibration channels:"
					 " last_time %d, now %d, last_temp %d,"
					 " new_temp %d",
					 __func__, last_time, now,
					 last_temperature, new_temperature);
				last_time = get_seconds();
				last_temperature = new_temperature;
				cal_req.tm = PMU_ADC_TM_RTM_SW;
				cal_req.flags = PMU_ADC_RAW_ONLY;
				read1 = 0;
				for (i = 0;
				     i <
				     padc->adcsetting->compensation_samples;) {
					cal_req.sig = PMU_ADC_NTC_CAL_LO;
					if (!padc->bcmpmu->adc_req(
						    padc->bcmpmu, &cal_req) &&
					    cal_req.raw != 0x3ff) {
						pr_hwmon(DATA, "%s: LO[%u]"
							      "cal_req.raw=%u",
							 __func__, i,
							  cal_req.raw);
						read1 += cal_req.raw;
						i++;
					}
				}
				/* add samples/2 for rounding */
				read1 += padc->adcsetting->
					compensation_samples / 2;
				/* Divide by the number of samples */
				read1 /= padc->adcsetting->compensation_samples;
				read2 = 0;
				for (i = 0;
				     i <
				     padc->adcsetting->compensation_samples;) {
					cal_req.sig = PMU_ADC_NTC_CAL_HI;
					if (!padc->bcmpmu->adc_req(
						    padc->bcmpmu, &cal_req) &&
					    cal_req.raw != 0x3ff) {
						pr_hwmon(DATA, "%s: HI[%u]"
							 "cal_req.raw=%u",
							 __func__, i,
							  cal_req.raw);
						read2 += cal_req.raw;
						i++;
					}
				}
				/* For rounding */
				read2 += padc->adcsetting->
					compensation_samples / 2;
				/* Divide to get average */
				read2 /= padc->adcsetting->compensation_samples;
				/* Calculate uvperbit and offset */
				if (read1 != read2) {
					gain =
					    ((padc->adcsetting->
					      compensation_volt_hi -
					      padc->adcsetting->
					      compensation_volt_lo) * 1000) /
					    (read2 - read1);
					offset =
					    padc->adcsetting->
					    compensation_volt_hi * 1000 -
					    (read2 * gain);
				}
				pr_hwmon(DATA,
					 "%s: Value %d, read1 %d, read2 %d, "
					 "gain %d, offset %d, vmax %d",
					 __func__, req->raw, read1, read2, gain,
					 offset, padc->adcunit[req->sig].vmax);
			}
			mutex_unlock(&padc->cal_lock);
		}
		padc->adcunit[req->sig].vstep = gain;
		padc->adcunit[req->sig].voffset = offset;
		break;
	default:
		break;
	}
	req->cal = req->raw * padc->adcunit[req->sig].vstep +
		padc->adcunit[req->sig].voffset; /* vstep, offset is in uV */
	pr_hwmon(DATA, "%s: raw %d, vstep %d, offset %d, cal %d uV", __func__,
		 req->raw, padc->adcunit[req->sig].vstep,
		 padc->adcunit[req->sig].voffset, req->cal);
}
static int read_adc_result(struct bcmpmu_adc *padc, struct bcmpmu_adc_req *req)
{
	int ret = 0;
	unsigned int val;
	unsigned int val1;
	unsigned int values[2];
	struct bcmpmu_adc_map adcmap;
	req->raw = 0;

	if (req->sig >= PMU_ADC_MAX)
		return -EINVAL;
	switch (req->tm) {
	case PMU_ADC_TM_HK:
		adcmap = padc->adcmap[req->sig];
		break;
	case PMU_ADC_TM_RTM_TX:
	case PMU_ADC_TM_RTM_RX:
	case PMU_ADC_TM_RTM_SW:
	case PMU_ADC_TM_RTM_SW_TEST:
		adcmap = padc->adcmap[PMU_ADC_RTM];
		break;
	case PMU_ADC_TM_MAX:
	default:
		return -EINVAL;
	}

	if ((adcmap.addr0 == 0) && (adcmap.addr1 == 0)) {
		pr_hwmon(ERROR, "%s: sig map failed\n", __func__);
		return -EINVAL;
	} else if (adcmap.addr0 == adcmap.addr1 + 1) {
		ret = padc->bcmpmu->read_dev_bulk(padc->bcmpmu,
						  adcmap.map, adcmap.addr1,
						  values, 2);

		val = values[0];
		val <<= 8;
		val |= values[1];
		pr_hwmon(DATA,
			 "%s: Signal %d, value[0] %x, value[1] %x ==> val %x",
			 __func__, req->sig, values[0], values[1], val);
	} else {
		ret = padc->bcmpmu->read_dev_drct(padc->bcmpmu,
						  adcmap.map, adcmap.addr1,
						  &val,
						  adcmap.dmask | adcmap.vmask);
		if (ret != 0) {
			pr_hwmon(ERROR, "%s: read adc add1 failed\n", __func__);
			return -EINVAL;
		}
		if (adcmap.addr0 != adcmap.addr1) {
			ret = padc->bcmpmu->read_dev_drct(padc->bcmpmu,
							  adcmap.map,
							  adcmap.addr0, &val1,
							  adcmap.dmask | adcmap.
							  vmask);
			if (ret != 0) {
				pr_hwmon(ERROR, "%s: read adc add0 failed\n",
					 __func__);
				return -EINVAL;
			}
			val = (val1 & 0xFF) | ((val << 8) & 0xFF00);
		}
	}
	if ((req->tm == PMU_ADC_TM_HK) && ((val & adcmap.vmask) != 0)) {
		req->raw = -EINVAL;
		pr_hwmon(FLOW, "%s: adc result invalid\n", __func__);
		return ret;
	}
	req->raw = val & adcmap.dmask;
	if (req->sig == PMU_ADC_FG_RAW || req->sig == PMU_ADC_FG_CURRSMPL) {
		int raw_data;
		if (req->raw & 0x8000)
			req->raw |= 0xffff0000;
		raw_data = (int) req->raw;
		if (raw_data > 0)
			raw_data += 2;
		else if (raw_data < 0)
			raw_data -= 2;
		raw_data = raw_data / 4;
		req->raw = (unsigned int) raw_data;
	}
	return ret;
}
static int bcmpmu_get_fg_acc_mas(struct bcmpmu *bcmpmu, int *data)
{
	int ret;
	unsigned int acc0, acc1, acc2, acc3;
	long int acc;
	unsigned int cnt, cnt0, cnt1;
	unsigned int slpcnt, slpcnt0, slpcnt1;
	struct bcmpmu_fg *pfg = bcmpmu->fginfo;
	int64_t actacc, slpacc;

	ret = bcmpmu->write_dev(bcmpmu,
				PMU_REG_FG_FRZREAD,
				bcmpmu->regmap[PMU_REG_FG_FRZREAD].mask,
				bcmpmu->regmap[PMU_REG_FG_FRZREAD].mask);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s failed to latch fg read.\n", __func__);
		return ret;
	}
	ret = bcmpmu->read_dev(bcmpmu,
			       PMU_REG_FG_ACCM0, &acc0, PMU_BITMASK_ALL);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s failed to read fg acc0.\n", __func__);
		return ret;
	}

	ret = bcmpmu->read_dev(bcmpmu,
			       PMU_REG_FG_ACCM1, &acc1, PMU_BITMASK_ALL);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s failed to read fg acc1.\n", __func__);
		return ret;
	}
	ret = bcmpmu->read_dev(bcmpmu,
			       PMU_REG_FG_ACCM2, &acc2, PMU_BITMASK_ALL);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s failed to read fg acc2.\n", __func__);
		return ret;
	}
	ret = bcmpmu->read_dev(bcmpmu,
			       PMU_REG_FG_ACCM3, &acc3, PMU_BITMASK_ALL);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s failed to read fg acc3.\n", __func__);
		return ret;
	}

	if ((acc3 & 0x80) == 0) {
		pr_hwmon(DATA, "%s fg data invalid.\n", __func__);
		return -EINVAL;
	}
	acc3 = acc3 & 0x03;
	if (acc3 >= 2)
		acc3 = acc3 | 0xFC;

	acc = (int)(acc0 | (acc1 << 8) | (acc2 << 16) | (acc3 << 24));
	pfg->fg_acc = acc;
	pr_hwmon(DATA, "%s: acc=%ld, acc3=%X acc2=%X acc1=%X acc0=%X\n",
		 __func__, acc, acc3, acc2, acc1, acc0);

	ret = bcmpmu->read_dev(bcmpmu, PMU_REG_FG_CNT0, &cnt0, PMU_BITMASK_ALL);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s failed to read fg cnt0.\n", __func__);
		return ret;
	}
	ret = bcmpmu->read_dev(bcmpmu, PMU_REG_FG_CNT1, &cnt1, PMU_BITMASK_ALL);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s failed to read fg cnt1.\n", __func__);
		return ret;
	}
	cnt = cnt0 | (cnt1 << 8);
	pfg->fg_smpl_cnt = cnt;

	ret = bcmpmu->read_dev(bcmpmu,
			       PMU_REG_FG_SLEEPCNT0, &slpcnt0, PMU_BITMASK_ALL);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s failed to read fg slpcnt0.\n", __func__);
		return ret;
	}
	ret = bcmpmu->read_dev(bcmpmu,
			       PMU_REG_FG_SLEEPCNT1, &slpcnt1, PMU_BITMASK_ALL);
	if (ret != 0) {
		pr_hwmon(ERROR, "%s failed to read fg slpcnt1.\n", __func__);
		return ret;
	}

	slpcnt = slpcnt0 | (slpcnt1 << 8);
	pfg->fg_slp_cnt = slpcnt;

	actacc = acc * pfg->fg_smpl_cnt_tm;
	actacc = actacc * pfg->fg_factor;
	slpacc = slpcnt * pfg->fg_slp_cnt_tm;
	slpacc = slpacc * pfg->fg_slp_curr_ua;
	pr_hwmon(DATA, "%s: actacc=%lld, actcnt=%d, slpacc=%lld, slpcnt=%d\n",
		 __func__, actacc, cnt, slpacc, slpcnt);
	actacc = actacc - slpacc;
	actacc = div_s64(actacc, 1000000);
	*data = (int)actacc;
	pfg->fg_columb_cnt += *data;

	if (slpcnt || cnt)
		pfg->fg_ibat_avg =
		    (*data * 1000) / (slpcnt * pfg->fg_slp_cnt_tm +
				      cnt * pfg->fg_smpl_cnt_tm);

	pr_hwmon(FLOW, "%s: fg acc mAsec = %d\n", __func__, *data);

	return ret;
}
static int __devinit bcmpmu_hwmon_probe(struct platform_device *pdev)
{
	int ret = -ENOMEM;

	struct bcmpmu *bcmpmu = pdev->dev.platform_data;
	struct bcmpmu_platform_data *pdata = bcmpmu->pdata;
	struct bcmpmu_adc *padc = NULL;
	struct bcmpmu_env *penv = NULL;
	struct bcmpmu_fg *pfg = NULL;
	int *envregs = NULL;

	pr_hwmon(INIT, "%s: called\n", __func__);

	padc = kzalloc(sizeof(struct bcmpmu_adc), GFP_KERNEL);
	if (padc == NULL) {
		pr_hwmon(ERROR, "%s failed to alloc mem.\n", __func__);
      ret = -ENOMEM;
		return ret;
	}
	init_waitqueue_head(&padc->wait);
	mutex_init(&padc->lock);
	mutex_init(&padc->cal_lock);
	pgadc = padc;
	padc->bcmpmu = bcmpmu;
	padc->adcmap = bcmpmu_get_adcmap(bcmpmu);
	padc->adcunit = bcmpmu_get_adcunit(bcmpmu);
	padc->ctrlmap = bcmpmu_get_adc_ctrl_map(bcmpmu);
	padc->btmap = pdata->batt_temp_map;
	padc->btmap_len = (int)pdata->batt_temp_map_len;
	padc->pmu_temp_map = pdata->pmu_temp_map;
	padc->ptmap_len = (int)pdata->pmu_temp_map_len;
	padc->rtmreq = NULL;
	padc->bom_map = pdata->bom_map;
	padc->bom_map_len = pdata->bom_map_len;
	padc->adcsetting = pdata->adc_setting;
	if (!padc->adcsetting->sw_timeout)
		padc->adcsetting->sw_timeout = 50;
	if (!padc->adcsetting->txrx_timeout)
		padc->adcsetting->txrx_timeout = 2000;
	bcmpmu->adcinfo = (void *)padc;
	bcmpmu->adc_req = bcmpmu_adc_request;
	bcmpmu->unit_get = bcmpmu_get_adcunit_data;
	bcmpmu->unit_set = bcmpmu_set_adcunit_data;

	if (padc->adcunit) {
		padc->adcunit[PMU_ADC_NTC].lut_ptr = pdata->batt_temp_voltmap;
		padc->adcunit[PMU_ADC_PATEMP].lut_ptr = pdata->pa_temp_voltmap;
		padc->adcunit[PMU_ADC_32KTEMP].lut_ptr =
		    pdata->x32_temp_voltmap;
		padc->adcunit[PMU_ADC_BOM].lut_ptr = pdata->bom_map;

		padc->adcunit[PMU_ADC_NTC].lut_len =
		    pdata->batt_temp_voltmap_len;
		padc->adcunit[PMU_ADC_PATEMP].lut_len =
		    pdata->pa_temp_voltmap_len;
		padc->adcunit[PMU_ADC_32KTEMP].lut_len =
		    pdata->x32_temp_voltmap_len;
		padc->adcunit[PMU_ADC_BOM].lut_len = pdata->bom_map_len;
	}

	penv = kzalloc(sizeof(struct bcmpmu_env), GFP_KERNEL);
	if (penv == NULL) {
		pr_hwmon(ERROR, "%s failed to alloc mem.\n", __func__);
      ret = -ENOMEM;
      goto err_penv;
	}
	penv->envregmap = bcmpmu_get_envregmap(bcmpmu, &penv->env_size);
	envregs = kzalloc((penv->env_size * sizeof(int)), GFP_KERNEL);
	if (envregs == NULL) {
		pr_hwmon(ERROR, "%s failed to alloc mem.\n", __func__);
      ret = -ENOMEM;
      goto err_envregs;
	}
	penv->bcmpmu = bcmpmu;
	penv->env_regs = envregs;
	penv->bcmpmu->update_env_status = bcmpmu_update_env_status;
	penv->bcmpmu->get_env_bit_status = bcmpmu_get_env_bit_status;
	penv->bcmpmu->is_env_bit_set = bcmpmu_is_env_bit_set;
	bcmpmu->envinfo = penv;

	pfg = kzalloc(sizeof(struct bcmpmu_fg), GFP_KERNEL);
	if (pfg == NULL) {
		pr_hwmon(ERROR, "%s failed to alloc mem.\n", __func__);
      ret = -ENOMEM;
      goto err_pfg;
	}
	pfg->bcmpmu = bcmpmu;
	if (pdata->fg_smpl_rate)
		pfg->fg_smpl_cnt_tm = 1000000 / pdata->fg_smpl_rate;
	else
		pfg->fg_smpl_cnt_tm = 1000000 / 2083;
	if (pdata->fg_slp_rate)
		pfg->fg_slp_cnt_tm = 1000000 / pdata->fg_slp_rate;
	else
		pfg->fg_slp_cnt_tm = 1000000 / 32000;
	if (pdata->fg_slp_curr_ua)
		pfg->fg_slp_curr_ua = pdata->fg_slp_curr_ua;
	else
		pfg->fg_slp_curr_ua = 1000;
	if (pdata->fg_sns_res)
		pfg->fg_sns_res = pdata->fg_sns_res;
	else
		pfg->fg_sns_res = 10;	/* default sense resistor */
	if (pdata->fg_factor)
		pfg->fg_factor = pdata->fg_factor;
	else
		pfg->fg_factor = 1000;

	pfg->bcmpmu->fg_currsmpl = bcmpmu_get_fg_currsmpl;
	pfg->bcmpmu->fg_vmbatt = bcmpmu_get_fg_vmbatt;
	pfg->bcmpmu->fg_acc_mas = bcmpmu_get_fg_acc_mas;
	pfg->bcmpmu->fg_enable = bcmpmu_fg_enable;
	pfg->bcmpmu->fg_reset = bcmpmu_fg_reset;
	pfg->bcmpmu->fg_offset_cal = bcmpmu_fg_offset_cal;
	pfg->bcmpmu->fg_offset_cal_read = bcmpmu_fg_offset_cal_read;
	pfg->bcmpmu->fg_trim_write = bcmpmu_fg_trim_write;
	bcmpmu->fginfo = pfg;

	if (pdata->support_fg) {
		bcmpmu_fg_enable(bcmpmu, 1);
		bcmpmu_fg_offset_cal(bcmpmu);
	}

	padc->hwmon_dev = hwmon_device_register(&pdev->dev);
	if (IS_ERR(padc->hwmon_dev)) {
		ret = PTR_ERR(padc->hwmon_dev);
		dev_err(&pdev->dev, "Class registration failed (%d)\n", ret);
		goto err_hwmon_device;
	}
	ret = sysfs_create_group(&pdev->dev.kobj, &bcmpmu_hwmon_attr_group);
	if (ret != 0)
		goto exit_remove_files;

	bcmpmu->register_irq(bcmpmu, PMU_IRQ_RTM_DATA_RDY, adc_isr, padc);
	/*bcmpmu->register_irq(bcmpmu, PMU_IRQ_RTM_IN_CON_MEAS, adc_isr, padc);
	bcmpmu->register_irq(bcmpmu, PMU_IRQ_RTM_UPPER, adc_isr, padc);
	 bcmpmu->register_irq(bcmpmu, PMU_IRQ_RTM_IGNORE, adc_isr, padc);
	 bcmpmu->register_irq(bcmpmu, PMU_IRQ_RTM_OVERRIDDEN, adc_isr, padc); */

	/*bcmpmu->unmask_irq(bcmpmu, PMU_IRQ_EOC); */
	bcmpmu->unmask_irq(bcmpmu, PMU_IRQ_RTM_DATA_RDY);
	/*bcmpmu->unmask_irq(bcmpmu, PMU_IRQ_RTM_IN_CON_MEAS);
	bcmpmu->unmask_irq(bcmpmu, PMU_IRQ_RTM_UPPER);
	   bcmpmu->unmask_irq(bcmpmu, PMU_IRQ_RTM_IGNORE);
	   bcmpmu->unmask_irq(bcmpmu, PMU_IRQ_RTM_OVERRIDDEN); */

#ifdef CONFIG_MFD_BCMPMU_DBG
	ret = device_create_file(&pdev->dev, &dev_attr_dbgmsk);
	ret = device_create_file(&pdev->dev, &dev_attr_fg_status);
	ret = device_create_file(&pdev->dev, &dev_attr_fg_factor);
#endif
	return ret;

exit_remove_files:
	sysfs_remove_group(&padc->hwmon_dev->kobj, &bcmpmu_hwmon_attr_group);
err_hwmon_device:   
   kfree(pfg);
err_pfg:
   kfree(envregs);
err_envregs:
	kfree(penv);
err_penv:
   kfree(padc);
   
	return ret;
}