2 * tsc210x_sensors.c - hwmon interface to TI TSC210x sensors
4 * Copyright (c) 2005-2007 Andrzej Zaborowski <balrog@zabor.org>
6 * This package is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This package is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this package; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 #include <linux/init.h>
21 #include <linux/err.h>
22 #include <linux/hwmon.h>
23 #include <linux/hwmon-sysfs.h>
24 #include <linux/platform_device.h>
25 #include <linux/spinlock.h>
26 #include <linux/autoconf.h>
28 # include <linux/apm-emulation.h>
31 #include <linux/spi/tsc210x.h>
35 * TI TSC210x chips include an ADC that's shared between various
36 * sensors (temperature, battery, vAUX, etc) and the touchscreen.
37 * This driver packages access to the non-touchscreen sensors
38 * available on a given board.
41 struct tsc210x_hwmon {
42 int bat[2], aux[2], temp[2];
44 struct class_device *dev;
45 struct tsc210x_config *pdata;
47 /* prevent APM from colliding with normal hwmon accessors */
53 # define apm_lock(h) spin_lock(&(h)->apm_lock)
54 # define apm_unlock(h) spin_unlock(&(h)->apm_lock)
56 # define apm_lock(h) do { } while (0)
57 # define apm_unlock(h) do { } while (0)
60 static void tsc210x_ports(void *context, int bat[], int aux[])
62 struct tsc210x_hwmon *hwmon = context;
66 /* FIXME for tsc2101 and tsc2111, battery voltage is:
67 * VBAT = (5 * VREF * (bat[x])) / (2 ^ bits)
68 * For tsc2100 and tsc2102, use "6" not "5"; that formula ignores
69 * an external 100-300 Ohm resistor making the right value be just
70 * a bit over 5 (or 6).
72 * FIXME the vAUX measurements need scaling too, but in that case
73 * there's no *internal* voltage divider so just scale to VREF.
75 * --> This code needs to know VREF, the VBAT multiplier, and
76 * the precision. For now, assume VREF 1.25V and 12 bits.
77 * When an external reference is used, it normally won't
78 * match the 1.25V (or 2.5V) values supported internally...
80 * --> Output units should become milliVolts; currently they are
83 hwmon->bat[0] = bat[0];
84 hwmon->bat[1] = bat[1];
86 hwmon->aux[0] = aux[0];
87 hwmon->aux[1] = aux[1];
92 /* FIXME temp sensors also need scaling so values are milliVolts...
93 * temperature (given calibration data) should be millidegrees C.
96 static void tsc210x_temp1(void *context, int temp)
98 struct tsc210x_hwmon *hwmon = context;
101 hwmon->temp[0] = temp;
105 static void tsc210x_temp2(void *context, int temp)
107 struct tsc210x_hwmon *hwmon = context;
110 hwmon->temp[1] = temp;
114 #define TSC210X_INPUT(devname, field) \
115 static ssize_t tsc_show_ ## devname(struct device *dev, \
116 struct device_attribute *devattr, char *buf) \
118 struct tsc210x_hwmon *hwmon = dev_get_drvdata(dev); \
119 return sprintf(buf, "%i\n", hwmon->field); \
121 static DEVICE_ATTR(devname ## _input, S_IRUGO, tsc_show_ ## devname, NULL);
123 TSC210X_INPUT(in0, bat[0])
124 TSC210X_INPUT(in1, bat[1])
125 TSC210X_INPUT(in2, aux[0])
126 TSC210X_INPUT(in3, aux[1])
127 TSC210X_INPUT(in4, temp[0])
128 TSC210X_INPUT(in5, temp[1])
130 static ssize_t tsc_show_temp1(struct device *dev,
131 struct device_attribute *devattr, char *buf)
133 struct tsc210x_hwmon *hwmon = dev_get_drvdata(dev);
134 int t1 = hwmon->temp[0];
135 int t2 = hwmon->temp[1];
140 * Use method #2 (differential) to calculate current temperature.
141 * The difference between TEMP2 and TEMP1 input values is
142 * multiplied by a constant to obtain current temperature.
143 * To find this constant we use the values measured at 25 C as
144 * thermometer calibration data.
146 * 298150 is 25 degrees Celcius represented in Kelvins and
147 * multiplied by 1000 for fixed point precision (273.15 + 25).
148 * 273150 is zero degrees Celcius.
150 diff = hwmon->pdata->temp_at25c[1] - hwmon->pdata->temp_at25c[0];
151 value = (t2 - t1) * 298150 / diff; /* This is in Kelvins now */
153 value -= 273150; /* Celcius millidegree */
154 return sprintf(buf, "%i\n", value);
156 static DEVICE_ATTR(temp1_input, S_IRUGO, tsc_show_temp1, NULL);
159 static struct tsc210x_hwmon *apm_hwmon;
161 static void tsc210x_get_power_status(struct apm_power_info *info)
163 struct tsc210x_hwmon *hwmon = apm_hwmon;
166 hwmon->pdata->apm_report(info, hwmon->bat);
171 static int tsc210x_hwmon_probe(struct platform_device *pdev)
173 struct tsc210x_hwmon *hwmon;
174 struct tsc210x_config *pdata = pdev->dev.platform_data;
177 hwmon = (struct tsc210x_hwmon *)
178 kzalloc(sizeof(struct tsc210x_hwmon), GFP_KERNEL);
180 dev_dbg(&pdev->dev, "allocation failed\n");
184 hwmon->dev = hwmon_device_register(&pdev->dev);
185 if (IS_ERR(hwmon->dev)) {
187 dev_dbg(&pdev->dev, "registration failed\n");
188 return PTR_ERR(hwmon->dev);
191 hwmon->pdata = pdata;
194 spin_lock_init(&hwmon->apm_lock);
196 if (pdata->apm_report) {
198 apm_get_power_status = tsc210x_get_power_status;
202 platform_set_drvdata(pdev, hwmon);
204 if (pdata->monitor & (TSC_BAT1 | TSC_BAT2 | TSC_AUX1 | TSC_AUX2))
205 status |= tsc210x_ports_cb(pdev->dev.parent,
206 tsc210x_ports, hwmon);
207 if (pdata->monitor & TSC_TEMP) {
208 status |= tsc210x_temp1_cb(pdev->dev.parent,
209 tsc210x_temp1, hwmon);
210 status |= tsc210x_temp2_cb(pdev->dev.parent,
211 tsc210x_temp2, hwmon);
215 tsc210x_ports_cb(pdev->dev.parent, NULL, NULL);
216 tsc210x_temp1_cb(pdev->dev.parent, NULL, NULL);
217 tsc210x_temp2_cb(pdev->dev.parent, NULL, NULL);
218 platform_set_drvdata(pdev, NULL);
220 if (pdata->apm_report)
221 apm_get_power_status = 0;
223 hwmon_device_unregister(hwmon->dev);
228 if (pdata->monitor & TSC_BAT1)
229 status |= device_create_file(&pdev->dev, &dev_attr_in0_input);
230 if (pdata->monitor & TSC_BAT2)
231 status |= device_create_file(&pdev->dev, &dev_attr_in1_input);
232 if (pdata->monitor & TSC_AUX1)
233 status |= device_create_file(&pdev->dev, &dev_attr_in2_input);
234 if (pdata->monitor & TSC_AUX2)
235 status |= device_create_file(&pdev->dev, &dev_attr_in3_input);
236 if (pdata->monitor & TSC_TEMP) {
237 status |= device_create_file(&pdev->dev, &dev_attr_in4_input);
238 status |= device_create_file(&pdev->dev, &dev_attr_in5_input);
240 if ((pdata->temp_at25c[1] - pdata->temp_at25c[0]) == 0)
241 dev_warn(&pdev->dev, "No temp calibration data.\n");
243 status |= device_create_file(&pdev->dev,
244 &dev_attr_temp1_input);
246 if (status) /* Not fatal */
247 dev_dbg(&pdev->dev, "Creating one or more "
248 "attribute files failed\n");
253 static int __exit tsc210x_hwmon_remove(struct platform_device *pdev)
255 struct tsc210x_hwmon *dev = platform_get_drvdata(pdev);
257 tsc210x_ports_cb(pdev->dev.parent, NULL, NULL);
258 tsc210x_temp1_cb(pdev->dev.parent, NULL, NULL);
259 tsc210x_temp2_cb(pdev->dev.parent, NULL, NULL);
260 platform_set_drvdata(pdev, NULL);
262 if (dev->pdata->apm_report)
263 apm_get_power_status = 0;
265 hwmon_device_unregister(dev->dev);
270 static struct platform_driver tsc210x_hwmon_driver = {
271 .probe = tsc210x_hwmon_probe,
272 .remove = __exit_p(tsc210x_hwmon_remove),
273 /* Nothing to do on suspend/resume */
275 .name = "tsc210x-hwmon",
279 static int __init tsc210x_hwmon_init(void)
281 /* can't use driver_probe() here since the parent device
282 * gets registered "late"
284 return platform_driver_register(&tsc210x_hwmon_driver);
286 module_init(tsc210x_hwmon_init);
288 static void __exit tsc210x_hwmon_exit(void)
290 platform_driver_unregister(&tsc210x_hwmon_driver);
292 module_exit(tsc210x_hwmon_exit);
294 MODULE_AUTHOR("Andrzej Zaborowski");
295 MODULE_DESCRIPTION("hwmon driver for TI TSC210x-connected sensors.");
296 MODULE_LICENSE("GPL");