blob: e24eeafec5da19b17fc6eb220ed8a7f03b825fda [file] [log] [blame]
Siddartha Mohanadoss7b116e12012-06-05 23:27:46 -07001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#define pr_fmt(fmt) "%s: " fmt, __func__
14
15#include <linux/kernel.h>
16#include <linux/of.h>
17#include <linux/err.h>
18#include <linux/init.h>
19#include <linux/slab.h>
20#include <linux/delay.h>
21#include <linux/mutex.h>
22#include <linux/types.h>
23#include <linux/hwmon.h>
24#include <linux/module.h>
25#include <linux/debugfs.h>
26#include <linux/spmi.h>
27#include <linux/of_irq.h>
28#include <linux/interrupt.h>
29#include <linux/qpnp/qpnp-adc.h>
30#include <linux/platform_device.h>
31
32/* Min ADC code represets 0V */
33#define QPNP_VADC_MIN_ADC_CODE 0x6000
34/* Max ADC code represents full-scale range of 1.8V */
35#define QPNP_VADC_MAX_ADC_CODE 0xA800
36
37int32_t qpnp_adc_scale_default(int32_t adc_code,
38 const struct qpnp_adc_properties *adc_properties,
39 const struct qpnp_vadc_chan_properties *chan_properties,
40 struct qpnp_vadc_result *adc_chan_result)
41{
42 bool negative_rawfromoffset = 0, negative_offset = 0;
43 int64_t scale_voltage = 0;
44
45 if (!chan_properties || !chan_properties->offset_gain_numerator ||
46 !chan_properties->offset_gain_denominator || !adc_properties
47 || !adc_chan_result)
48 return -EINVAL;
49
50 scale_voltage = (adc_code -
51 chan_properties->adc_graph[CALIB_ABSOLUTE].adc_gnd)
52 * chan_properties->adc_graph[CALIB_ABSOLUTE].dx;
53 if (scale_voltage < 0) {
54 negative_offset = 1;
55 scale_voltage = -scale_voltage;
56 }
57 do_div(scale_voltage,
58 chan_properties->adc_graph[CALIB_ABSOLUTE].dy);
59 if (negative_offset)
60 scale_voltage = -scale_voltage;
61 scale_voltage += chan_properties->adc_graph[CALIB_ABSOLUTE].dx;
62
63 if (scale_voltage < 0) {
64 if (adc_properties->bipolar) {
65 scale_voltage = -scale_voltage;
66 negative_rawfromoffset = 1;
67 } else {
68 scale_voltage = 0;
69 }
70 }
71
72 adc_chan_result->measurement = scale_voltage *
73 chan_properties->offset_gain_denominator;
74
75 /* do_div only perform positive integer division! */
76 do_div(adc_chan_result->measurement,
77 chan_properties->offset_gain_numerator);
78
79 if (negative_rawfromoffset)
80 adc_chan_result->measurement = -adc_chan_result->measurement;
81
82 /*
83 * Note: adc_chan_result->measurement is in the unit of
84 * adc_properties.adc_reference. For generic channel processing,
85 * channel measurement is a scale/ratio relative to the adc
86 * reference input
87 */
88 adc_chan_result->physical = adc_chan_result->measurement;
89
90 return 0;
91}
92EXPORT_SYMBOL_GPL(qpnp_adc_scale_default);
93
94int32_t qpnp_vadc_check_result(int32_t *data)
95{
96 if (*data < QPNP_VADC_MIN_ADC_CODE)
97 *data = QPNP_VADC_MIN_ADC_CODE;
98 else if (*data > QPNP_VADC_MAX_ADC_CODE)
99 *data = QPNP_VADC_MAX_ADC_CODE;
100
101 return 0;
102}
103EXPORT_SYMBOL_GPL(qpnp_vadc_check_result);
104
105int32_t qpnp_adc_get_devicetree_data(struct spmi_device *spmi,
106 struct qpnp_adc_drv *adc_qpnp)
107{
108 struct device_node *node = spmi->dev.of_node;
109 struct resource *res;
110 struct device_node *child;
111 struct qpnp_vadc_amux *adc_channel_list;
112 struct qpnp_adc_properties *adc_prop;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700113 struct qpnp_adc_amux_properties *amux_prop;
Siddartha Mohanadoss7b116e12012-06-05 23:27:46 -0700114 int count_adc_channel_list = 0, decimation, rc = 0;
115
116 if (!node)
117 return -EINVAL;
118
119 for_each_child_of_node(node, child)
120 count_adc_channel_list++;
121
122 if (!count_adc_channel_list) {
123 pr_err("No channel listing\n");
124 return -EINVAL;
125 }
126
127 adc_qpnp->spmi = spmi;
128
129 adc_prop = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_adc_properties),
130 GFP_KERNEL);
131 if (!adc_prop) {
132 dev_err(&spmi->dev, "Unable to allocate memory\n");
133 return -ENOMEM;
134 }
135 adc_channel_list = devm_kzalloc(&spmi->dev,
136 (sizeof(struct qpnp_vadc_amux) * count_adc_channel_list),
137 GFP_KERNEL);
138 if (!adc_channel_list) {
139 dev_err(&spmi->dev, "Unable to allocate memory\n");
140 return -ENOMEM;
141 }
142
143 amux_prop = devm_kzalloc(&spmi->dev,
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700144 sizeof(struct qpnp_adc_amux_properties) +
Siddartha Mohanadoss7b116e12012-06-05 23:27:46 -0700145 sizeof(struct qpnp_vadc_chan_properties), GFP_KERNEL);
146 if (!amux_prop) {
147 dev_err(&spmi->dev, "Unable to allocate memory\n");
148 return -ENOMEM;
149 }
150
151 for_each_child_of_node(node, child) {
152 int channel_num, scaling, post_scaling, hw_settle_time;
153 int fast_avg_setup, calib_type, i = 0, rc;
154 const char *calibration_param, *channel_name;
155
156 channel_name = of_get_property(child,
157 "label", NULL) ? : child->name;
158 if (!channel_name) {
159 pr_err("Invalid channel name\n");
160 return -EINVAL;
161 }
162
163 rc = of_property_read_u32(child, "qcom,channel-num",
164 &channel_num);
165 if (rc) {
166 pr_err("Invalid channel num\n");
167 return -EINVAL;
168 }
169 rc = of_property_read_u32(child, "qcom,decimation",
170 &decimation);
171 if (rc) {
172 pr_err("Invalid channel decimation property\n");
173 return -EINVAL;
174 }
175 rc = of_property_read_u32(child,
176 "qcom,pre-div-channel-scaling", &scaling);
177 if (rc) {
178 pr_err("Invalid channel scaling property\n");
179 return -EINVAL;
180 }
181 rc = of_property_read_u32(child,
182 "qcom,scale-function", &post_scaling);
183 if (rc) {
184 pr_err("Invalid channel post scaling property\n");
185 return -EINVAL;
186 }
187 rc = of_property_read_u32(child,
188 "qcom,hw-settle-time", &hw_settle_time);
189 if (rc) {
190 pr_err("Invalid channel hw settle time property\n");
191 return -EINVAL;
192 }
193 rc = of_property_read_u32(child,
194 "qcom,fast-avg-setup", &fast_avg_setup);
195 if (rc) {
196 pr_err("Invalid channel fast average setup\n");
197 return -EINVAL;
198 }
199 calibration_param = of_get_property(child,
200 "qcom,calibration-type", NULL);
201 if (!strncmp(calibration_param, "absolute", 8))
202 calib_type = CALIB_ABSOLUTE;
203 else if (!strncmp(calibration_param, "historical", 9))
204 calib_type = CALIB_RATIOMETRIC;
205 else {
206 pr_err("%s: Invalid calibration property\n", __func__);
207 return -EINVAL;
208 }
209 /* Individual channel properties */
210 adc_channel_list[i].name = (char *)channel_name;
211 adc_channel_list[i].channel_num = channel_num;
212 adc_channel_list[i].chan_path_prescaling = scaling;
213 adc_channel_list[i].adc_decimation = decimation;
214 adc_channel_list[i].adc_scale_fn = post_scaling;
215 adc_channel_list[i].hw_settle_time = hw_settle_time;
216 adc_channel_list[i].fast_avg_setup = fast_avg_setup;
217 i++;
218 }
219 adc_qpnp->adc_channels = adc_channel_list;
220 adc_qpnp->amux_prop = amux_prop;
221
222 /* Get the ADC VDD reference voltage and ADC bit resolution */
223 rc = of_property_read_u32(node, "qcom,adc-vdd-reference",
224 &adc_prop->adc_vdd_reference);
225 if (rc) {
226 pr_err("Invalid adc vdd reference property\n");
227 return -EINVAL;
228 }
229 rc = of_property_read_u32(node, "qcom,adc-bit-resolution",
230 &adc_prop->bitresolution);
231 if (rc) {
232 pr_err("Invalid adc bit resolution property\n");
233 return -EINVAL;
234 }
235 adc_qpnp->adc_prop = adc_prop;
236
237 /* Get the peripheral address */
238 res = spmi_get_resource(spmi, 0, IORESOURCE_MEM, 0);
239 if (!res) {
240 pr_err("No base address definition\n");
241 return -EINVAL;
242 }
243
244 adc_qpnp->slave = spmi->sid;
245 adc_qpnp->offset = res->start;
246
247 /* Register the ADC peripheral interrupt */
248 adc_qpnp->adc_irq = spmi_get_irq(spmi, 0, 0);
249 if (adc_qpnp->adc_irq < 0) {
250 pr_err("Invalid irq\n");
251 return -ENXIO;
252 }
253
254 mutex_init(&adc_qpnp->adc_lock);
255
256 return 0;
257}
258EXPORT_SYMBOL(qpnp_adc_get_devicetree_data);