Input: ads7846 - be more compatible with the hwmon framework

- Hook up to hwmon
     * show sensor attributes only if hwmon is present
     * ... and the board's reference voltage is known
     * otherwise be just a touchscreen
 - Report voltages per hwmon convention
     * measure in millivolts
     * voltages are named in[0-8]_input (ugh)
     * for 7846 chips, properly range-adjust vBATT/in1_input

Battery measurements help during recharge monitoring.  On OSK/Mistral,
the measured voltage agreed with a multimeter to several decimal places.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
This commit is contained in:
David Brownell 2007-01-18 00:45:48 -05:00 committed by Dmitry Torokhov
parent 15e3589e59
commit 2c8dc07151
2 changed files with 222 additions and 89 deletions

View File

@ -12,13 +12,18 @@ menuconfig INPUT_TOUCHSCREEN
if INPUT_TOUCHSCREEN
config TOUCHSCREEN_ADS7846
tristate "ADS 7846 based touchscreens"
tristate "ADS 7846/7843 based touchscreens"
depends on SPI_MASTER
depends on HWMON = n || HWMON
help
Say Y here if you have a touchscreen interface using the
ADS7846 controller, and your board-specific initialization
ADS7846 or ADS7843 controller, and your board-specific setup
code includes that in its table of SPI devices.
If HWMON is selected, and the driver is told the reference voltage
on your board, you will also get hwmon interfaces for the voltage
(and on ads7846, temperature) sensors of this chip.
If unsure, say N (but it's safe to say "Y").
To compile this driver as a module, choose M here: the

View File

@ -17,8 +17,9 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/device.h>
#include <linux/hwmon.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/interrupt.h>
@ -69,7 +70,7 @@ struct ts_event {
u16 x;
u16 y;
u16 z1, z2;
int ignore;
int ignore;
};
struct ads7846 {
@ -77,7 +78,12 @@ struct ads7846 {
char phys[32];
struct spi_device *spi;
#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE)
struct attribute_group *attr_group;
struct class_device *hwmon;
#endif
u16 model;
u16 vref_delay_usecs;
u16 x_plate_ohms;
@ -170,7 +176,12 @@ struct ads7846 {
/*
* Non-touchscreen sensors only use single-ended conversions.
* The range is GND..vREF. The ads7843 and ads7835 must use external vREF;
* ads7846 lets that pin be unconnected, to use internal vREF.
*/
static unsigned vREF_mV;
module_param(vREF_mV, uint, 0);
MODULE_PARM_DESC(vREF_mV, "external vREF voltage, in milliVolts");
struct ser_req {
u8 ref_on;
@ -198,50 +209,55 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
struct ser_req *req = kzalloc(sizeof *req, GFP_KERNEL);
int status;
int sample;
int i;
int use_internal;
if (!req)
return -ENOMEM;
spi_message_init(&req->msg);
/* activate reference, so it has time to settle; */
req->ref_on = REF_ON;
req->xfer[0].tx_buf = &req->ref_on;
req->xfer[0].len = 1;
req->xfer[1].rx_buf = &req->scratch;
req->xfer[1].len = 2;
/* FIXME boards with ads7846 might use external vref instead ... */
use_internal = (ts->model == 7846);
/*
* for external VREF, 0 usec (and assume it's always on);
* for 1uF, use 800 usec;
* no cap, 100 usec.
*/
req->xfer[1].delay_usecs = ts->vref_delay_usecs;
/* maybe turn on internal vREF, and let it settle */
if (use_internal) {
req->ref_on = REF_ON;
req->xfer[0].tx_buf = &req->ref_on;
req->xfer[0].len = 1;
spi_message_add_tail(&req->xfer[0], &req->msg);
req->xfer[1].rx_buf = &req->scratch;
req->xfer[1].len = 2;
/* for 1uF, settle for 800 usec; no cap, 100 usec. */
req->xfer[1].delay_usecs = ts->vref_delay_usecs;
spi_message_add_tail(&req->xfer[1], &req->msg);
}
/* take sample */
req->command = (u8) command;
req->xfer[2].tx_buf = &req->command;
req->xfer[2].len = 1;
spi_message_add_tail(&req->xfer[2], &req->msg);
req->xfer[3].rx_buf = &req->sample;
req->xfer[3].len = 2;
spi_message_add_tail(&req->xfer[3], &req->msg);
/* REVISIT: take a few more samples, and compare ... */
/* turn off reference */
req->ref_off = REF_OFF;
req->xfer[4].tx_buf = &req->ref_off;
req->xfer[4].len = 1;
req->xfer[5].rx_buf = &req->scratch;
req->xfer[5].len = 2;
/* maybe off internal vREF */
if (use_internal) {
req->ref_off = REF_OFF;
req->xfer[4].tx_buf = &req->ref_off;
req->xfer[4].len = 1;
spi_message_add_tail(&req->xfer[4], &req->msg);
CS_CHANGE(req->xfer[5]);
/* group all the transfers together, so we can't interfere with
* reading touchscreen state; disable penirq while sampling
*/
for (i = 0; i < 6; i++)
spi_message_add_tail(&req->xfer[i], &req->msg);
req->xfer[5].rx_buf = &req->scratch;
req->xfer[5].len = 2;
CS_CHANGE(req->xfer[5]);
spi_message_add_tail(&req->xfer[5], &req->msg);
}
ts->irq_disabled = 1;
disable_irq(spi->irq);
@ -261,25 +277,173 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
return status ? status : sample;
}
#define SHOW(name) static ssize_t \
#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE)
#define SHOW(name, var, adjust) static ssize_t \
name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct ads7846 *ts = dev_get_drvdata(dev); \
ssize_t v = ads7846_read12_ser(dev, \
READ_12BIT_SER(name) | ADS_PD10_ALL_ON); \
READ_12BIT_SER(var) | ADS_PD10_ALL_ON); \
if (v < 0) \
return v; \
return sprintf(buf, "%u\n", (unsigned) v); \
return sprintf(buf, "%u\n", adjust(ts, v)); \
} \
static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL);
SHOW(temp0)
SHOW(temp1)
SHOW(vaux)
SHOW(vbatt)
/* Sysfs conventions report temperatures in millidegrees Celcius.
* ADS7846 could use the low-accuracy two-sample scheme, but can't do the high
* accuracy scheme without calibration data. For now we won't try either;
* userspace sees raw sensor values, and must scale/calibrate appropriately.
*/
static inline unsigned null_adjust(struct ads7846 *ts, ssize_t v)
{
return v;
}
SHOW(temp0, temp0, null_adjust) /* temp1_input */
SHOW(temp1, temp1, null_adjust) /* temp2_input */
/* sysfs conventions report voltages in millivolts. We can convert voltages
* if we know vREF. userspace may need to scale vAUX to match the board's
* external resistors; we assume that vBATT only uses the internal ones.
*/
static inline unsigned vaux_adjust(struct ads7846 *ts, ssize_t v)
{
unsigned retval = v;
/* external resistors may scale vAUX into 0..vREF */
retval *= vREF_mV;
retval = retval >> 12;
return retval;
}
static inline unsigned vbatt_adjust(struct ads7846 *ts, ssize_t v)
{
unsigned retval = vaux_adjust(ts, v);
/* ads7846 has a resistor ladder to scale this signal down */
if (ts->model == 7846)
retval *= 4;
return retval;
}
SHOW(in0_input, vaux, vaux_adjust)
SHOW(in1_input, vbatt, vbatt_adjust)
static struct attribute *ads7846_attributes[] = {
&dev_attr_temp0.attr,
&dev_attr_temp1.attr,
&dev_attr_in0_input.attr,
&dev_attr_in1_input.attr,
NULL,
};
static struct attribute_group ads7846_attr_group = {
.attrs = ads7846_attributes,
};
static struct attribute *ads7843_attributes[] = {
&dev_attr_in0_input.attr,
&dev_attr_in1_input.attr,
NULL,
};
static struct attribute_group ads7843_attr_group = {
.attrs = ads7843_attributes,
};
static struct attribute *ads7845_attributes[] = {
&dev_attr_in0_input.attr,
NULL,
};
static struct attribute_group ads7845_attr_group = {
.attrs = ads7845_attributes,
};
static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts)
{
struct class_device *hwmon;
int err;
/* hwmon sensors need a reference voltage */
switch (ts->model) {
case 7846:
if (!vREF_mV) {
dev_dbg(&spi->dev, "assuming 2.5V internal vREF\n");
vREF_mV = 2500;
}
break;
case 7845:
case 7843:
if (!vREF_mV) {
dev_warn(&spi->dev,
"external vREF for ADS%d not specified\n",
ts->model);
return 0;
}
break;
}
/* different chips have different sensor groups */
switch (ts->model) {
case 7846:
ts->attr_group = &ads7846_attr_group;
break;
case 7845:
ts->attr_group = &ads7845_attr_group;
break;
case 7843:
ts->attr_group = &ads7843_attr_group;
break;
default:
dev_dbg(&spi->dev, "ADS%d not recognized\n", ts->model);
return 0;
}
err = sysfs_create_group(&spi->dev.kobj, ts->attr_group);
if (err)
return err;
hwmon = hwmon_device_register(&spi->dev);
if (IS_ERR(hwmon)) {
sysfs_remove_group(&spi->dev.kobj, ts->attr_group);
return PTR_ERR(hwmon);
}
ts->hwmon = hwmon;
return 0;
}
static void ads784x_hwmon_unregister(struct spi_device *spi,
struct ads7846 *ts)
{
if (ts->hwmon) {
sysfs_remove_group(&spi->dev.kobj, ts->attr_group);
hwmon_device_unregister(ts->hwmon);
}
}
#else
static inline int ads784x_hwmon_register(struct spi_device *spi,
struct ads7846 *ts)
{
return 0;
}
static inline void ads784x_hwmon_unregister(struct spi_device *spi,
struct ads7846 *ts)
{
}
#endif
static int is_pen_down(struct device *dev)
{
struct ads7846 *ts = dev_get_drvdata(dev);
struct ads7846 *ts = dev_get_drvdata(dev);
return ts->pendown;
}
@ -323,46 +487,14 @@ static ssize_t ads7846_disable_store(struct device *dev,
static DEVICE_ATTR(disable, 0664, ads7846_disable_show, ads7846_disable_store);
static struct attribute *ads7846_attributes[] = {
&dev_attr_temp0.attr,
&dev_attr_temp1.attr,
&dev_attr_vbatt.attr,
&dev_attr_vaux.attr,
static struct attribute *ads784x_attributes[] = {
&dev_attr_pen_down.attr,
&dev_attr_disable.attr,
NULL,
};
static struct attribute_group ads7846_attr_group = {
.attrs = ads7846_attributes,
};
/*
* ads7843/7845 don't have temperature sensors, and
* use the other sensors a bit differently too
*/
static struct attribute *ads7843_attributes[] = {
&dev_attr_vbatt.attr,
&dev_attr_vaux.attr,
&dev_attr_pen_down.attr,
&dev_attr_disable.attr,
NULL,
};
static struct attribute_group ads7843_attr_group = {
.attrs = ads7843_attributes,
};
static struct attribute *ads7845_attributes[] = {
&dev_attr_vaux.attr,
&dev_attr_pen_down.attr,
&dev_attr_disable.attr,
NULL,
};
static struct attribute_group ads7845_attr_group = {
.attrs = ads7845_attributes,
static struct attribute_group ads784x_attr_group = {
.attrs = ads784x_attributes,
};
/*--------------------------------------------------------------------------*/
@ -886,28 +1018,21 @@ static int __devinit ads7846_probe(struct spi_device *spi)
goto err_cleanup_filter;
}
err = ads784x_hwmon_register(spi, ts);
if (err)
goto err_free_irq;
dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq);
/* take a first sample, leaving nPENIRQ active; avoid
/* take a first sample, leaving nPENIRQ active and vREF off; avoid
* the touchscreen, in case it's not connected.
*/
(void) ads7846_read12_ser(&spi->dev,
READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON);
switch (ts->model) {
case 7846:
ts->attr_group = &ads7846_attr_group;
break;
case 7845:
ts->attr_group = &ads7845_attr_group;
break;
default:
ts->attr_group = &ads7843_attr_group;
break;
}
err = sysfs_create_group(&spi->dev.kobj, ts->attr_group);
err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group);
if (err)
goto err_free_irq;
goto err_remove_hwmon;
err = input_register_device(input_dev);
if (err)
@ -916,7 +1041,9 @@ static int __devinit ads7846_probe(struct spi_device *spi)
return 0;
err_remove_attr_group:
sysfs_remove_group(&spi->dev.kobj, ts->attr_group);
sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group);
err_remove_hwmon:
ads784x_hwmon_unregister(spi, ts);
err_free_irq:
free_irq(spi->irq, ts);
err_cleanup_filter:
@ -932,11 +1059,12 @@ static int __devexit ads7846_remove(struct spi_device *spi)
{
struct ads7846 *ts = dev_get_drvdata(&spi->dev);
ads784x_hwmon_unregister(spi, ts);
input_unregister_device(ts->input);
ads7846_suspend(spi, PMSG_SUSPEND);
sysfs_remove_group(&spi->dev.kobj, ts->attr_group);
sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group);
free_irq(ts->spi->irq, ts);
/* suspend left the IRQ disabled */