[patch/rfc 2.6.20-rc1] ads7846 better hwmon support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Trying to sync the version of this driver in the OMAP tree with the one
in mainline ... they diverged a bit, so I'm splitting out versions of
the original patches and sending them right now for comment/review before
I re-test things.

- Dave

======================	CUT HERE
Be more compatible with the hwmon framework:

 - Hook up to hwmon
     * show sensor attributes only if hwmon is present
     * 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 four decimal places.
 
Signed-off-by: David Brownell <dbrownell at users.sourceforge.net>

Index: at91/drivers/input/touchscreen/Kconfig
===================================================================
--- at91.orig/drivers/input/touchscreen/Kconfig	2006-12-21 04:31:01.000000000 -0800
+++ at91/drivers/input/touchscreen/Kconfig	2006-12-21 04:32:59.000000000 -0800
@@ -12,12 +12,15 @@ menuconfig INPUT_TOUCHSCREEN
 if INPUT_TOUCHSCREEN
 
 config TOUCHSCREEN_ADS7846
-	tristate "ADS 7846 based touchscreens"
+	tristate "ADS 7846/7843 based touchscreens"
 	depends on SPI_MASTER
 	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, you will also get hwmon interfaces for
+	  the voltage (and for ads7846, temperature) sensors on this chip.
 
 	  If unsure, say N (but it's safe to say "Y").
 
Index: at91/drivers/input/touchscreen/ads7846.c
===================================================================
--- at91.orig/drivers/input/touchscreen/ads7846.c	2006-12-21 04:32:50.000000000 -0800
+++ at91/drivers/input/touchscreen/ads7846.c	2006-12-21 04:32:59.000000000 -0800
@@ -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>
@@ -77,6 +78,7 @@ struct ads7846 {
 
 	struct spi_device	*spi;
 	struct attribute_group	*attr_group;
+	struct class_device	*hwmon;
 	u16			model;
 	u16			vref_delay_usecs;
 	u16			x_plate_ohms;
@@ -169,7 +171,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.
+ *
+ * FIXME make external vREF_mV be a module option, and use that as needed...
  */
+static const unsigned vREF_mV = 2500;
 
 struct ser_req {
 	u8			ref_on;
@@ -197,50 +204,55 @@ static int ads7846_read12_ser(struct dev
 	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;
-
-	/*
-	 * 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;
+	/* FIXME boards with ads7846 might use external vref instead ... */
+	use_internal = (ts->model == 7846);
+
+	/* 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;
-
-	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);
+	/* 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);
+
+		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);
@@ -260,21 +272,63 @@ static int ads7846_read12_ser(struct dev
 	return status ? status : sample;
 }
 
-#define SHOW(name) static ssize_t \
+#ifdef	CONFIG_HWMON
+
+#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.
+ * We 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 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)
+
+#endif
 
 static int is_pen_down(struct device *dev)
 {
@@ -323,10 +377,12 @@ static ssize_t ads7846_disable_store(str
 static DEVICE_ATTR(disable, 0664, ads7846_disable_show, ads7846_disable_store);
 
 static struct attribute *ads7846_attributes[] = {
+#ifdef	CONFIG_HWMON
 	&dev_attr_temp0.attr,
 	&dev_attr_temp1.attr,
-	&dev_attr_vbatt.attr,
-	&dev_attr_vaux.attr,
+	&dev_attr_in0_input.attr,
+	&dev_attr_in1_input.attr,
+#endif
 	&dev_attr_pen_down.attr,
 	&dev_attr_disable.attr,
 	NULL,
@@ -336,14 +392,16 @@ static struct attribute_group ads7846_at
 	.attrs = ads7846_attributes,
 };
 
+#ifdef	CONFIG_HWMON
+
 /*
  * 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_in0_input.attr,
+	&dev_attr_in1_input.attr,
 	&dev_attr_pen_down.attr,
 	&dev_attr_disable.attr,
 	NULL,
@@ -354,7 +412,7 @@ static struct attribute_group ads7843_at
 };
 
 static struct attribute *ads7845_attributes[] = {
-	&dev_attr_vaux.attr,
+	&dev_attr_in0_input.attr,
 	&dev_attr_pen_down.attr,
 	&dev_attr_disable.attr,
 	NULL,
@@ -364,6 +422,11 @@ static struct attribute_group ads7845_at
 	.attrs = ads7845_attributes,
 };
 
+#else
+#define ads7843_attr_group ads7846_attr_group
+#define ads7845_attr_group ads7846_attr_group
+#endif
+
 /*--------------------------------------------------------------------------*/
 
 /*
@@ -708,6 +771,14 @@ static int __devinit ads7846_probe(struc
 		err = -ENOMEM;
 		goto err_free_mem;
 	}
+#ifdef	CONFIG_HWMON
+	ts->hwmon = hwmon_device_register(&spi->dev);
+	if (IS_ERR(ts->hwmon)) {
+		err = PTR_ERR(ts->hwmon);
+		ts->hwmon = NULL;
+		goto err_free_mem;
+	}
+#endif
 
 	dev_set_drvdata(&spi->dev, ts);
 	spi->dev.power.power_state = PMSG_ON;
@@ -872,9 +943,11 @@ static int __devinit ads7846_probe(struc
 		goto err_cleanup_filter;
 	}
 
-	dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq);
+	dev_info(&spi->dev, "touchscreen%s, irq %d\n",
+			ts->hwmon ? " + hwmon" : "",
+			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,
@@ -909,6 +982,10 @@ static int __devinit ads7846_probe(struc
 	if (ts->filter_cleanup)
 		ts->filter_cleanup(ts->filter_data);
  err_free_mem:
+#ifdef	CONFIG_HWMON
+	if (ts->hwmon)
+		hwmon_device_unregister(ts->hwmon);
+#endif
 	input_free_device(input_dev);
 	kfree(ts);
 	return err;
@@ -918,6 +995,9 @@ static int __devexit ads7846_remove(stru
 {
 	struct ads7846		*ts = dev_get_drvdata(&spi->dev);
 
+#ifdef	CONFIG_HWMON
+	hwmon_device_unregister(ts->hwmon);
+#endif
 	input_unregister_device(ts->input);
 
 	ads7846_suspend(spi, PMSG_SUSPEND);




[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux