blob: c122270c55a27ef8b5586b9985880246cec5f2dd [file] [log] [blame]
/* Copyright (c) 2012, Code Aurora Forum. 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.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/hwmon.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/spmi.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/qpnp/qpnp-adc.h>
#include <linux/platform_device.h>
/* Min ADC code represets 0V */
#define QPNP_VADC_MIN_ADC_CODE 0x6000
/* Max ADC code represents full-scale range of 1.8V */
#define QPNP_VADC_MAX_ADC_CODE 0xA800
int32_t qpnp_adc_scale_default(int32_t adc_code,
const struct qpnp_adc_properties *adc_properties,
const struct qpnp_vadc_chan_properties *chan_properties,
struct qpnp_vadc_result *adc_chan_result)
{
bool negative_rawfromoffset = 0, negative_offset = 0;
int64_t scale_voltage = 0;
if (!chan_properties || !chan_properties->offset_gain_numerator ||
!chan_properties->offset_gain_denominator || !adc_properties
|| !adc_chan_result)
return -EINVAL;
scale_voltage = (adc_code -
chan_properties->adc_graph[CALIB_ABSOLUTE].adc_gnd)
* chan_properties->adc_graph[CALIB_ABSOLUTE].dx;
if (scale_voltage < 0) {
negative_offset = 1;
scale_voltage = -scale_voltage;
}
do_div(scale_voltage,
chan_properties->adc_graph[CALIB_ABSOLUTE].dy);
if (negative_offset)
scale_voltage = -scale_voltage;
scale_voltage += chan_properties->adc_graph[CALIB_ABSOLUTE].dx;
if (scale_voltage < 0) {
if (adc_properties->bipolar) {
scale_voltage = -scale_voltage;
negative_rawfromoffset = 1;
} else {
scale_voltage = 0;
}
}
adc_chan_result->measurement = scale_voltage *
chan_properties->offset_gain_denominator;
/* do_div only perform positive integer division! */
do_div(adc_chan_result->measurement,
chan_properties->offset_gain_numerator);
if (negative_rawfromoffset)
adc_chan_result->measurement = -adc_chan_result->measurement;
/*
* Note: adc_chan_result->measurement is in the unit of
* adc_properties.adc_reference. For generic channel processing,
* channel measurement is a scale/ratio relative to the adc
* reference input
*/
adc_chan_result->physical = adc_chan_result->measurement;
return 0;
}
EXPORT_SYMBOL_GPL(qpnp_adc_scale_default);
int32_t qpnp_vadc_check_result(int32_t *data)
{
if (*data < QPNP_VADC_MIN_ADC_CODE)
*data = QPNP_VADC_MIN_ADC_CODE;
else if (*data > QPNP_VADC_MAX_ADC_CODE)
*data = QPNP_VADC_MAX_ADC_CODE;
return 0;
}
EXPORT_SYMBOL_GPL(qpnp_vadc_check_result);
int32_t qpnp_adc_get_devicetree_data(struct spmi_device *spmi,
struct qpnp_adc_drv *adc_qpnp)
{
struct device_node *node = spmi->dev.of_node;
struct resource *res;
struct device_node *child;
struct qpnp_vadc_amux *adc_channel_list;
struct qpnp_adc_properties *adc_prop;
struct qpnp_adc_amux_properties *amux_prop;
int count_adc_channel_list = 0, decimation, rc = 0, i = 0;
if (!node)
return -EINVAL;
for_each_child_of_node(node, child)
count_adc_channel_list++;
if (!count_adc_channel_list) {
pr_err("No channel listing\n");
return -EINVAL;
}
adc_qpnp->spmi = spmi;
adc_prop = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_adc_properties),
GFP_KERNEL);
if (!adc_prop) {
dev_err(&spmi->dev, "Unable to allocate memory\n");
return -ENOMEM;
}
adc_channel_list = devm_kzalloc(&spmi->dev,
sizeof(struct qpnp_vadc_amux) * count_adc_channel_list,
GFP_KERNEL);
if (!adc_channel_list) {
dev_err(&spmi->dev, "Unable to allocate memory\n");
return -ENOMEM;
}
amux_prop = devm_kzalloc(&spmi->dev,
sizeof(struct qpnp_adc_amux_properties) +
sizeof(struct qpnp_vadc_chan_properties), GFP_KERNEL);
if (!amux_prop) {
dev_err(&spmi->dev, "Unable to allocate memory\n");
return -ENOMEM;
}
adc_qpnp->adc_channels = adc_channel_list;
adc_qpnp->amux_prop = amux_prop;
for_each_child_of_node(node, child) {
int channel_num, scaling, post_scaling, hw_settle_time;
int fast_avg_setup, calib_type, rc;
const char *calibration_param, *channel_name;
channel_name = of_get_property(child,
"label", NULL) ? : child->name;
if (!channel_name) {
pr_err("Invalid channel name\n");
return -EINVAL;
}
rc = of_property_read_u32(child, "qcom,channel-num",
&channel_num);
if (rc) {
pr_err("Invalid channel num\n");
return -EINVAL;
}
rc = of_property_read_u32(child, "qcom,decimation",
&decimation);
if (rc) {
pr_err("Invalid channel decimation property\n");
return -EINVAL;
}
rc = of_property_read_u32(child,
"qcom,pre-div-channel-scaling", &scaling);
if (rc) {
pr_err("Invalid channel scaling property\n");
return -EINVAL;
}
rc = of_property_read_u32(child,
"qcom,scale-function", &post_scaling);
if (rc) {
pr_err("Invalid channel post scaling property\n");
return -EINVAL;
}
rc = of_property_read_u32(child,
"qcom,hw-settle-time", &hw_settle_time);
if (rc) {
pr_err("Invalid channel hw settle time property\n");
return -EINVAL;
}
rc = of_property_read_u32(child,
"qcom,fast-avg-setup", &fast_avg_setup);
if (rc) {
pr_err("Invalid channel fast average setup\n");
return -EINVAL;
}
calibration_param = of_get_property(child,
"qcom,calibration-type", NULL);
if (!strncmp(calibration_param, "absolute", 8))
calib_type = CALIB_ABSOLUTE;
else if (!strncmp(calibration_param, "historical", 9))
calib_type = CALIB_RATIOMETRIC;
else {
pr_err("%s: Invalid calibration property\n", __func__);
return -EINVAL;
}
/* Individual channel properties */
adc_channel_list[i].name = (char *)channel_name;
adc_channel_list[i].channel_num = channel_num;
adc_channel_list[i].chan_path_prescaling = scaling;
adc_channel_list[i].adc_decimation = decimation;
adc_channel_list[i].adc_scale_fn = post_scaling;
adc_channel_list[i].hw_settle_time = hw_settle_time;
adc_channel_list[i].fast_avg_setup = fast_avg_setup;
i++;
}
/* Get the ADC VDD reference voltage and ADC bit resolution */
rc = of_property_read_u32(node, "qcom,adc-vdd-reference",
&adc_prop->adc_vdd_reference);
if (rc) {
pr_err("Invalid adc vdd reference property\n");
return -EINVAL;
}
rc = of_property_read_u32(node, "qcom,adc-bit-resolution",
&adc_prop->bitresolution);
if (rc) {
pr_err("Invalid adc bit resolution property\n");
return -EINVAL;
}
adc_qpnp->adc_prop = adc_prop;
/* Get the peripheral address */
res = spmi_get_resource(spmi, 0, IORESOURCE_MEM, 0);
if (!res) {
pr_err("No base address definition\n");
return -EINVAL;
}
adc_qpnp->slave = spmi->sid;
adc_qpnp->offset = res->start;
/* Register the ADC peripheral interrupt */
adc_qpnp->adc_irq = spmi_get_irq(spmi, 0, 0);
if (adc_qpnp->adc_irq < 0) {
pr_err("Invalid irq\n");
return -ENXIO;
}
mutex_init(&adc_qpnp->adc_lock);
return 0;
}
EXPORT_SYMBOL(qpnp_adc_get_devicetree_data);