From 659cb49d6a8ba8d5651e53dd22a0addab6a479e6 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 3 May 2006 05:14:10 -0700 Subject: [PATCH] [PATCH] ARM: OMAP: ads7846 updates, mostly for hwmon The ads784x chips include not just touchscreen sensors but also temperature and voltage sensors, which are traditional hwmon fodder. - Register with the hwmon framework * hwmon needs to use subsys_init() so it's available early enough - Adopt hwmon convention for presenting voltages: * Report voltages in millivolts * Attribute names are "in[0-8]_input" (meaningless, ugh) - Be more accurate for the hwmon values: * vBATT gets range-adjusted * Discard the extra bit sent before the sample. Battery measurements help during battery recharge monitoring. On OSK/Mistral, the measured voltage agreed with a multimeter until the fourth decimal place. NOTE: this version is only for the OMAP tree, which has some byteswap bugs (and other changes) that are not found in the upstream kernels. Signed-off-by: David Brownell Signed-off-by: Tony Lindgren --- drivers/hwmon/hwmon.c | 2 +- drivers/input/touchscreen/Kconfig | 5 +- drivers/input/touchscreen/ads7846.c | 183 ++++++++++++++++++---------- 3 files changed, 126 insertions(+), 64 deletions(-) diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 106fa01cdb6..affcc00764d 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -101,7 +101,7 @@ static void __exit hwmon_exit(void) class_destroy(hwmon_class); } -module_init(hwmon_init); +subsys_initcall(hwmon_init); module_exit(hwmon_exit); EXPORT_SYMBOL_GPL(hwmon_device_register); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 7c294b00dde..a3e9ceb9592 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -14,10 +14,13 @@ if INPUT_TOUCHSCREEN config TOUCHSCREEN_ADS7846 tristate "ADS 7846 based touchscreens" depends on SPI_MASTER + select HWMON help Say Y here if you have a touchscreen interface using the ADS7846 controller, and your board-specific initialization - code includes that in its table of SPI devices. + code includes that in its table of SPI devices. You will + also get hwmon interfaces for the temperature and voltage + sensors this chip provides. If unsure, say N (but it's safe to say "Y"). diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index c01214fd5e7..e1f94fb4518 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -16,8 +16,9 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#include +#include #include +#include #include #include #include @@ -36,13 +37,9 @@ /* * This code has been heavily tested on a Nokia 770, and lightly - * tested on an other ads7846 device. + * tested on other ads7846 devices (OSK/Mistral, Lubbock). * Support for ads7843 and ads7845 has only been stubbed in. * - * Not yet done: How accurate are the temperature and voltage - * readings? (System-specific calibration should support - * accuracy of 0.3 degrees C; otherwise it's 2.0 degrees.) - * * IRQ handling needs a workaround because of a shortcoming in handling * edge triggered IRQs on some platforms like the OMAP1/2. These * platforms don't handle the ARM lazy IRQ disabling properly, thus we @@ -78,6 +75,7 @@ struct ads7846 { char phys[32]; struct spi_device *spi; + struct class_device *hwmon; u16 model; u16 vref_delay_usecs; u16 x_plate_ohms; @@ -164,7 +162,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; @@ -186,50 +189,55 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) struct ser_req *req = kzalloc(sizeof *req, SLAB_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); + } disable_irq(spi->irq); status = spi_sync(spi, &req->msg); @@ -237,28 +245,70 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) if (req->msg.status) status = req->msg.status; + + /* on-wire is a must-ignore bit, a BE12 value, then padding */ sample = be16_to_cpu(req->sample); - sample = sample >> 4; - kfree(req); + sample = sample >> 3; + sample &= 0x0fff; + kfree(req); return status ? status : sample; } -#define SHOW(name) static ssize_t \ +#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) + static int is_pen_down(struct device *dev) { @@ -326,13 +376,13 @@ static void ads7846_rx(void *ads) u16 x, y, z1, z2; unsigned long flags; - /* adjust: 12 bit samples (left aligned), built from - * two 8 bit values writen msb-first. + /* adjust: on-wire is a must-ignore bit, a BE12 value, then padding; + * built from two 8 bit values written msb-first. */ - x = ts->tc.x >> 3; - y = ts->tc.y >> 3; - z1 = ts->tc.z1 >> 3; - z2 = ts->tc.z2 >> 3; + x = (be16_to_cpu(ts->tc.x) >> 3) & 0x0fff; + y = (be16_to_cpu(ts->tc.y) >> 3) & 0x0fff; + z1 = (be16_to_cpu(ts->tc.z1) >> 3) & 0x0fff; + z2 = (be16_to_cpu(ts->tc.z2) >> 3) & 0x0fff; /* range filtering */ if (x == MAX_12BIT) @@ -560,6 +610,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) { struct ads7846 *ts; struct input_dev *input_dev; + struct class_device *hwmon = ERR_PTR(-ENOMEM); struct ads7846_platform_data *pdata = spi->dev.platform_data; struct spi_message *m; struct spi_transfer *x; @@ -585,12 +636,14 @@ static int __devinit ads7846_probe(struct spi_device *spi) /* We'd set the wordsize to 12 bits ... except that some controllers * will then treat the 8 bit command words as 12 bits (and drop the * four MSBs of the 12 bit result). Result: inputs must be shifted - * to discard the four garbage LSBs. + * to discard the four garbage LSBs. (Also, not all controllers can + * support 12 bit words.) */ ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL); input_dev = input_allocate_device(); - if (!ts || !input_dev) { + hwmon = hwmon_device_register(&spi->dev); + if (!ts || !input_dev || IS_ERR(hwmon)) { err = -ENOMEM; goto err_free_mem; } @@ -600,6 +653,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) ts->spi = spi; ts->input = input_dev; + ts->hwmon = hwmon; init_timer(&ts->timer); ts->timer.data = (unsigned long) ts; @@ -737,27 +791,29 @@ static int __devinit ads7846_probe(struct spi_device *spi) goto err_free_mem; } - dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq); + dev_info(&spi->dev, "touchscreen + hwmon, 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); /* ads7843/7845 don't have temperature sensors, and - * use the other sensors a bit differently too + * use the other ADC lines a bit differently too */ if (ts->model == 7846) { device_create_file(&spi->dev, &dev_attr_temp0); device_create_file(&spi->dev, &dev_attr_temp1); } + /* in1 == vBAT (7846), or a non-scaled ADC input */ if (ts->model != 7845) - device_create_file(&spi->dev, &dev_attr_vbatt); - device_create_file(&spi->dev, &dev_attr_vaux); + device_create_file(&spi->dev, &dev_attr_in1_input); + /* in0 == a non-scaled ADC input */ + device_create_file(&spi->dev, &dev_attr_in0_input); + /* non-hwmon device attributes */ device_create_file(&spi->dev, &dev_attr_pen_down); - device_create_file(&spi->dev, &dev_attr_disable); err = input_register_device(input_dev); @@ -774,11 +830,13 @@ static int __devinit ads7846_probe(struct spi_device *spi) device_remove_file(&spi->dev, &dev_attr_temp0); } if (ts->model != 7845) - device_remove_file(&spi->dev, &dev_attr_vbatt); - device_remove_file(&spi->dev, &dev_attr_vaux); + device_remove_file(&spi->dev, &dev_attr_in1_input); + device_remove_file(&spi->dev, &dev_attr_in0_input); free_irq(spi->irq, ts); err_free_mem: + if (!IS_ERR(hwmon)) + hwmon_device_unregister(hwmon); input_free_device(input_dev); kfree(ts); return err; @@ -788,6 +846,7 @@ static int __devexit ads7846_remove(struct spi_device *spi) { struct ads7846 *ts = dev_get_drvdata(&spi->dev); + hwmon_device_unregister(ts->hwmon); input_unregister_device(ts->input); ads7846_suspend(spi, PMSG_SUSPEND); @@ -799,8 +858,8 @@ static int __devexit ads7846_remove(struct spi_device *spi) device_remove_file(&spi->dev, &dev_attr_temp0); } if (ts->model != 7845) - device_remove_file(&spi->dev, &dev_attr_vbatt); - device_remove_file(&spi->dev, &dev_attr_vaux); + device_remove_file(&spi->dev, &dev_attr_in1_input); + device_remove_file(&spi->dev, &dev_attr_in0_input); free_irq(ts->spi->irq, ts); if (ts->irq_disabled) @@ -853,7 +912,7 @@ static int __init ads7846_init(void) return spi_register_driver(&ads7846_driver); } -module_init(ads7846_init); +device_initcall(ads7846_init); static void __exit ads7846_exit(void) { -- 2.41.1