// SPDX-License-Identifier: GPL-2.0-only/* * ADS7846 based touchscreen and sensor driver * * Copyright (c) 2005 David Brownell * Copyright (c) 2006 Nokia Corporation * Various changes: Imre Deak <imre.deak@nokia.com> * * Using code from: * - corgi_ts.c * Copyright (C) 2004-2005 Richard Purdie * - omap_ts.[hc], ads7846.h, ts_osk.c * Copyright (C) 2002 MontaVista Software * Copyright (C) 2004 Texas Instruments * Copyright (C) 2005 Dirk Behme */#include <linux/types.h>#include <linux/hwmon.h>#include <linux/err.h>#include <linux/sched.h>#include <linux/delay.h>#include <linux/input.h>#include <linux/input/touchscreen.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/pm.h>#include <linux/property.h>#include <linux/gpio/consumer.h>#include <linux/spi/spi.h>#include <linux/spi/ads7846.h>#include <linux/regulator/consumer.h>#include <linux/module.h>#include <linux/unaligned.h> /* * This code has been heavily tested on a Nokia 770, and lightly * tested on other ads7846 devices (OSK/Mistral, Lubbock, Spitz). * TSC2046 is just newer ads7846 silicon. * Support for ads7843 tested on Atmel at91sam926x-EK. * Support for ads7845 has only been stubbed in. * Support for Analog Devices AD7873 and AD7843 tested. * * 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 * have to maintain our own SW IRQ disabled status. This should be * removed as soon as the affected platform's IRQ handling is fixed. * * App note sbaa036 talks in more detail about accurate sampling... * that ought to help in situations like LCDs inducing noise (which * can also be helped by using synch signals) and more generally. * This driver tries to utilize the measures described in the app * note. The strength of filtering can be set in the board-* specific * files. */ #define TS_POLL_DELAY 1 /* ms delay before the first sample */#define TS_POLL_PERIOD 5 /* ms delay between samples */ /* this driver doesn't aim at the peak continuous sample rate */#define SAMPLE_BITS (8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */) struct ads7846_buf { u8 cmd; __be16 data;} __packed; struct ads7846_buf_layout { unsigned int offset; unsigned int count; unsigned int skip;}; /* * We allocate this separately to avoid cache line sharing issues when * driver is used with DMA-based SPI controllers (like atmel_spi) on * systems where main memory is not DMA-coherent (most non-x86 boards). */struct ads7846_packet { unsigned int count; unsigned int count_skip; unsigned int cmds; unsigned int last_cmd_idx; struct ads7846_buf_layout l[5]; struct ads7846_buf *rx; struct ads7846_buf *tx; struct ads7846_buf pwrdown_cmd; bool ignore; u16 x, y, z1, z2;}; struct ads7846 { struct input_dev *input; char phys[32]; char name[32]; struct spi_device *spi; struct regulator *reg; u16 model; u16 vref_mv; u16 vref_delay_usecs; u16 x_plate_ohms; u16 pressure_max; bool swap_xy; bool use_internal; struct ads7846_packet *packet; struct spi_transfer xfer[18]; struct spi_message msg[5]; int msg_count; wait_queue_head_t wait; bool pendown; int read_cnt; int read_rep; int last_read; u16 debounce_max; u16 debounce_tol; u16 debounce_rep; u16 penirq_recheck_delay_usecs; struct touchscreen_properties core_prop; struct mutex lock; bool stopped; /* P: lock */ bool disabled; /* P: lock */ bool suspended; /* P: lock */ int (*setup_spi_msg)(struct ads7846 *ts, const struct ads7846_platform_data *pdata); void (*read_state)(struct ads7846 *ts); int (*filter)(void *data, int data_idx, int *val); void *filter_data; int (*get_pendown_state)(void); struct gpio_desc *gpio_pendown; struct gpio_desc *gpio_hsync; void (*wait_for_sync)(void);}; enum ads7846_filter { ADS7846_FILTER_OK, ADS7846_FILTER_REPEAT, ADS7846_FILTER_IGNORE,}; /* leave chip selected when we're done, for quicker re-select? */#if 0#define CS_CHANGE(xfer) ((xfer).cs_change = 1)#else#define CS_CHANGE(xfer) ((xfer).cs_change = 0)#endif /*--------------------------------------------------------------------------*/ /* The ADS7846 has touchscreen and other sensors. * Earlier ads784x chips are somewhat compatible. */#define ADS_START (1 << 7)#define ADS_A2A1A0_d_y (1 << 4) /* differential */#define ADS_A2A1A0_d_z1 (3 << 4) /* differential */#define ADS_A2A1A0_d_z2 (4 << 4) /* differential */#define ADS_A2A1A0_d_x (5 << 4) /* differential */#define ADS_A2A1A0_temp0 (0 << 4) /* non-differential */#define ADS_A2A1A0_vbatt (2 << 4) /* non-differential */#define ADS_A2A1A0_vaux (6 << 4) /* non-differential */#define ADS_A2A1A0_temp1 (7 << 4) /* non-differential */#define ADS_8_BIT (1 << 3)#define ADS_12_BIT (0 << 3)#define ADS_SER (1 << 2) /* non-differential */#define ADS_DFR (0 << 2) /* differential */#define ADS_PD10_PDOWN (0 << 0) /* low power mode + penirq */#define ADS_PD10_ADC_ON (1 << 0) /* ADC on */#define ADS_PD10_REF_ON (2 << 0) /* vREF on + penirq */#define ADS_PD10_ALL_ON (3 << 0) /* ADC + vREF on */ #define MAX_12BIT ((1<<12)-1) /* leave ADC powered up (disables penirq) between differential samples */#define READ_12BIT_DFR(x, adc, vref) (ADS_START | ADS_A2A1A0_d_ ## x \ | ADS_12_BIT | ADS_DFR | \ (adc ? ADS_PD10_ADC_ON : 0) | (vref ? ADS_PD10_REF_ON : 0)) #define READ_Y(vref) (READ_12BIT_DFR(y, 1, vref))#define READ_Z1(vref) (READ_12BIT_DFR(z1, 1, vref))#define READ_Z2(vref) (READ_12BIT_DFR(z2, 1, vref))#define READ_X(vref) (READ_12BIT_DFR(x, 1, vref))#define PWRDOWN (READ_12BIT_DFR(y, 0, 0)) /* LAST */ /* single-ended samples need to first power up reference voltage; * we leave both ADC and VREF powered */#define READ_12BIT_SER(x) (ADS_START | ADS_A2A1A0_ ## x \ | ADS_12_BIT | ADS_SER) #define REF_ON (READ_12BIT_DFR(x, 1, 1))#define REF_OFF (READ_12BIT_DFR(y, 0, 0)) /* Order commands in the most optimal way to reduce Vref switching and * settling time: * Measure: X; Vref: X+, X-; IN: Y+ * Measure: Y; Vref: Y+, Y-; IN: X+ * Measure: Z1; Vref: Y+, X-; IN: X+ * Measure: Z2; Vref: Y+, X-; IN: Y- */enum ads7846_cmds { ADS7846_X, ADS7846_Y, ADS7846_Z1, ADS7846_Z2, ADS7846_PWDOWN,}; static int get_pendown_state(struct ads7846 *ts){ if (ts->get_pendown_state) return ts->get_pendown_state(); return gpiod_get_value(ts->gpio_pendown);} static void ads7846_report_pen_up(struct ads7846 *ts){ struct input_dev *input = ts->input; input_report_key(input, BTN_TOUCH, 0); input_report_abs(input, ABS_PRESSURE, 0); input_sync(input); ts->pendown = false; dev_vdbg(&ts->spi->dev, "UP\n");} /* Must be called with ts->lock held */static void ads7846_stop(struct ads7846 *ts){ if (!ts->disabled && !ts->suspended) { /* Signal IRQ thread to stop polling and disable the handler. */ ts->stopped = true; mb(); wake_up(&ts->wait); disable_irq(ts->spi->irq); }} /* Must be called with ts->lock held */static void ads7846_restart(struct ads7846 *ts){ if (!ts->disabled && !ts->suspended) { /* Check if pen was released since last stop */ if (ts->pendown && !get_pendown_state(ts)) ads7846_report_pen_up(ts); /* Tell IRQ thread that it may poll the device. */ ts->stopped = false; mb(); enable_irq(ts->spi->irq); }} /* Must be called with ts->lock held */static void __ads7846_disable(struct ads7846 *ts){ ads7846_stop(ts); regulator_disable(ts->reg); /* * We know the chip's in low power mode since we always * leave it that way after every request */} /* Must be called with ts->lock held */static void __ads7846_enable(struct ads7846 *ts){ int error; error = regulator_enable(ts->reg); if (error != 0) dev_err(&ts->spi->dev, "Failed to enable supply: %d\n", error); ads7846_restart(ts);} static void ads7846_disable(struct ads7846 *ts){ guard(mutex)(&ts->lock); if (!ts->disabled) { if (!ts->suspended) __ads7846_disable(ts); ts->disabled = true; }} static void ads7846_enable(struct ads7846 *ts){ guard(mutex)(&ts->lock); if (ts->disabled) { ts->disabled = false; if (!ts->suspended) __ads7846_enable(ts); }} /*--------------------------------------------------------------------------*/ /* * 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. */ struct ser_req { u8 ref_on; u8 command; u8 ref_off; struct spi_message msg; struct spi_transfer xfer[8]; /* * DMA (thus cache coherency maintenance) requires the * transfer buffers to live in their own cache lines. */ __be16 sample ____cacheline_aligned; u16 scratch;}; struct ads7845_ser_req { u8 command[3]; struct spi_message msg; struct spi_transfer xfer[2]; /* * DMA (thus cache coherency maintenance) requires the * transfer buffers to live in their own cache lines. */ u8 sample[3] ____cacheline_aligned;}; static int ads7846_read12_ser(struct device *dev, unsigned command){ struct spi_device *spi = to_spi_device(dev); struct ads7846 *ts = dev_get_drvdata(dev); int status; struct ser_req *req __free(kfree) = kzalloc_obj(*req); if (!req) return -ENOMEM; spi_message_init(&req->msg); /* maybe turn on internal vREF, and let it settle */ if (ts->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.value = ts->vref_delay_usecs; req->xfer[1].delay.unit = SPI_DELAY_UNIT_USECS; spi_message_add_tail(&req->xfer[1], &req->msg); /* Enable reference voltage */ command |= ADS_PD10_REF_ON; } /* Enable ADC in every case */ command |= ADS_PD10_ADC_ON; /* 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 ... */ /* converter in low power mode & enable PENIRQ */ req->ref_off = PWRDOWN; 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; spi_message_add_tail(&req->xfer[5], &req->msg); /* clear the command register */ req->xfer[6].rx_buf = &req->scratch; req->xfer[6].len = 1; spi_message_add_tail(&req->xfer[6], &req->msg); req->xfer[7].rx_buf = &req->scratch; req->xfer[7].len = 2; CS_CHANGE(req->xfer[7]); spi_message_add_tail(&req->xfer[7], &req->msg); scoped_guard(mutex, &ts->lock) { ads7846_stop(ts); status = spi_sync(spi, &req->msg); ads7846_restart(ts); } if (status == 0) { /* on-wire is a must-ignore bit, a BE12 value, then padding */ status = be16_to_cpu(req->sample); status = status >> 3; status &= 0x0fff; } return status;} static int ads7845_read12_ser(struct device *dev, unsigned command){ struct spi_device *spi = to_spi_device(dev); struct ads7846 *ts = dev_get_drvdata(dev); int status; struct ads7845_ser_req *req __free(kfree) = kzalloc_obj(*req); if (!req) return -ENOMEM; spi_message_init(&req->msg); req->command[0] = (u8) command; req->xfer[0].tx_buf = req->command; req->xfer[0].rx_buf = req->sample; req->xfer[0].len = 3; spi_message_add_tail(&req->xfer[0], &req->msg); scoped_guard(mutex, &ts->lock) { ads7846_stop(ts); status = spi_sync(spi, &req->msg); ads7846_restart(ts); } if (status == 0) { /* BE12 value, then padding */ status = get_unaligned_be16(&req->sample[1]); status = status >> 3; status &= 0x0fff; } return status;} #if IS_ENABLED(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(&ts->spi->dev, \ READ_12BIT_SER(var)); \ if (v < 0) \ return v; \ return sprintf(buf, "%u\n", adjust(ts, v)); \} \static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL); /* Sysfs conventions report temperatures in millidegrees Celsius. * 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. */Lines 1–500 of 1589. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * ADS7846 based touchscreen and sensor driver * * Copyright (c) 2005 David Brownell * Copyright (c) 2006 Nokia Corporation * Various changes: Imre Deak <imre.deak@nokia.com> * * Using code from: * - corgi_ts.c * Copyright (C) 2004-2005 Richard Purdie * - omap_ts.[hc], ads7846.h, ts_osk.c * Copyright (C) 2002 MontaVista Software * Copyright (C) 2004 Texas Instruments * Copyright (C) 2005 Dirk Behme */ #include <linux/types.h> #include <linux/hwmon.h> #include <linux/err.h> #include <linux/sched.h> #include <linux/delay.h> #include <linux/input.h> #include <linux/input/touchscreen.h> #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/pm.h> #include <linux/property.h> #include <linux/gpio/consumer.h> #include <linux/spi/spi.h> #include <linux/spi/ads7846.h> #include <linux/regulator/consumer.h> #include <linux/module.h> #include <linux/unaligned.h> /* * This code has been heavily tested on a Nokia 770, and lightly * tested on other ads7846 devices (OSK/Mistral, Lubbock, Spitz). * TSC2046 is just newer ads7846 silicon. * Support for ads7843 tested on Atmel at91sam926x-EK. * Support for ads7845 has only been stubbed in. * Support for Analog Devices AD7873 and AD7843 tested. * * 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 * have to maintain our own SW IRQ disabled status. This should be * removed as soon as the affected platform's IRQ handling is fixed. * * App note sbaa036 talks in more detail about accurate sampling... * that ought to help in situations like LCDs inducing noise (which * can also be helped by using synch signals) and more generally. * This driver tries to utilize the measures described in the app * note. The strength of filtering can be set in the board-* specific * files. */ #define TS_POLL_DELAY 1 /* ms delay before the first sample */ #define TS_POLL_PERIOD 5 /* ms delay between samples */ /* this driver doesn't aim at the peak continuous sample rate */ #define SAMPLE_BITS (8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */) struct ads7846_buf { u8 cmd; __be16 data; } __packed; struct ads7846_buf_layout { unsigned int offset; unsigned int count; unsigned int skip; }; /* * We allocate this separately to avoid cache line sharing issues when * driver is used with DMA-based SPI controllers (like atmel_spi) on * systems where main memory is not DMA-coherent (most non-x86 boards). */ struct ads7846_packet { unsigned int count; unsigned int count_skip; unsigned int cmds; unsigned int last_cmd_idx; struct ads7846_buf_layout l[5]; struct ads7846_buf *rx; struct ads7846_buf *tx; struct ads7846_buf pwrdown_cmd; bool ignore; u16 x, y, z1, z2; }; struct ads7846 { struct input_dev *input; char phys[32]; char name[32]; struct spi_device *spi; struct regulator *reg; u16 model; u16 vref_mv; u16 vref_delay_usecs; u16 x_plate_ohms; u16 pressure_max; bool swap_xy; bool use_internal; struct ads7846_packet *packet; struct spi_transfer xfer[18]; struct spi_message msg[5]; int msg_count; wait_queue_head_t wait; bool pendown; int read_cnt; int read_rep; int last_read; u16 debounce_max; u16 debounce_tol; u16 debounce_rep; u16 penirq_recheck_delay_usecs; struct touchscreen_properties core_prop; struct mutex lock; bool stopped; /* P: lock */ bool disabled; /* P: lock */ bool suspended; /* P: lock */ int (*setup_spi_msg)(struct ads7846 *ts, const struct ads7846_platform_data *pdata); void (*read_state)(struct ads7846 *ts); int (*filter)(void *data, int data_idx, int *val); void *filter_data; int (*get_pendown_state)(void); struct gpio_desc *gpio_pendown; struct gpio_desc *gpio_hsync; void (*wait_for_sync)(void); }; enum ads7846_filter { ADS7846_FILTER_OK, ADS7846_FILTER_REPEAT, ADS7846_FILTER_IGNORE, }; /* leave chip selected when we're done, for quicker re-select? */ #if 0 #define CS_CHANGE(xfer) ((xfer).cs_change = 1) #else #define CS_CHANGE(xfer) ((xfer).cs_change = 0) #endif /*--------------------------------------------------------------------------*/ /* The ADS7846 has touchscreen and other sensors. * Earlier ads784x chips are somewhat compatible. */ #define ADS_START (1 << 7) #define ADS_A2A1A0_d_y (1 << 4) /* differential */ #define ADS_A2A1A0_d_z1 (3 << 4) /* differential */ #define ADS_A2A1A0_d_z2 (4 << 4) /* differential */ #define ADS_A2A1A0_d_x (5 << 4) /* differential */ #define ADS_A2A1A0_temp0 (0 << 4) /* non-differential */ #define ADS_A2A1A0_vbatt (2 << 4) /* non-differential */ #define ADS_A2A1A0_vaux (6 << 4) /* non-differential */ #define ADS_A2A1A0_temp1 (7 << 4) /* non-differential */ #define ADS_8_BIT (1 << 3) #define ADS_12_BIT (0 << 3) #define ADS_SER (1 << 2) /* non-differential */ #define ADS_DFR (0 << 2) /* differential */ #define ADS_PD10_PDOWN (0 << 0) /* low power mode + penirq */ #define ADS_PD10_ADC_ON (1 << 0) /* ADC on */ #define ADS_PD10_REF_ON (2 << 0) /* vREF on + penirq */ #define ADS_PD10_ALL_ON (3 << 0) /* ADC + vREF on */ #define MAX_12BIT ((1<<12)-1) /* leave ADC powered up (disables penirq) between differential samples */ #define READ_12BIT_DFR(x, adc, vref) (ADS_START | ADS_A2A1A0_d_ ## x \ | ADS_12_BIT | ADS_DFR | \ (adc ? ADS_PD10_ADC_ON : 0) | (vref ? ADS_PD10_REF_ON : 0)) #define READ_Y(vref) (READ_12BIT_DFR(y, 1, vref)) #define READ_Z1(vref) (READ_12BIT_DFR(z1, 1, vref)) #define READ_Z2(vref) (READ_12BIT_DFR(z2, 1, vref)) #define READ_X(vref) (READ_12BIT_DFR(x, 1, vref)) #define PWRDOWN (READ_12BIT_DFR(y, 0, 0)) /* LAST */ /* single-ended samples need to first power up reference voltage; * we leave both ADC and VREF powered */ #define READ_12BIT_SER(x) (ADS_START | ADS_A2A1A0_ ## x \ | ADS_12_BIT | ADS_SER) #define REF_ON (READ_12BIT_DFR(x, 1, 1)) #define REF_OFF (READ_12BIT_DFR(y, 0, 0)) /* Order commands in the most optimal way to reduce Vref switching and * settling time: * Measure: X; Vref: X+, X-; IN: Y+ * Measure: Y; Vref: Y+, Y-; IN: X+ * Measure: Z1; Vref: Y+, X-; IN: X+ * Measure: Z2; Vref: Y+, X-; IN: Y- */ enum ads7846_cmds { ADS7846_X, ADS7846_Y, ADS7846_Z1, ADS7846_Z2, ADS7846_PWDOWN, }; static int get_pendown_state(struct ads7846 *ts) { if (ts->get_pendown_state) return ts->get_pendown_state(); return gpiod_get_value(ts->gpio_pendown); } static void ads7846_report_pen_up(struct ads7846 *ts) { struct input_dev *input = ts->input; input_report_key(input, BTN_TOUCH, 0); input_report_abs(input, ABS_PRESSURE, 0); input_sync(input); ts->pendown = false; dev_vdbg(&ts->spi->dev, "UP\n"); } /* Must be called with ts->lock held */ static void ads7846_stop(struct ads7846 *ts) { if (!ts->disabled && !ts->suspended) { /* Signal IRQ thread to stop polling and disable the handler. */ ts->stopped = true; mb(); wake_up(&ts->wait); disable_irq(ts->spi->irq); } } /* Must be called with ts->lock held */ static void ads7846_restart(struct ads7846 *ts) { if (!ts->disabled && !ts->suspended) { /* Check if pen was released since last stop */ if (ts->pendown && !get_pendown_state(ts)) ads7846_report_pen_up(ts); /* Tell IRQ thread that it may poll the device. */ ts->stopped = false; mb(); enable_irq(ts->spi->irq); } } /* Must be called with ts->lock held */ static void __ads7846_disable(struct ads7846 *ts) { ads7846_stop(ts); regulator_disable(ts->reg); /* * We know the chip's in low power mode since we always * leave it that way after every request */ } /* Must be called with ts->lock held */ static void __ads7846_enable(struct ads7846 *ts) { int error; error = regulator_enable(ts->reg); if (error != 0) dev_err(&ts->spi->dev, "Failed to enable supply: %d\n", error); ads7846_restart(ts); } static void ads7846_disable(struct ads7846 *ts) { guard(mutex)(&ts->lock); if (!ts->disabled) { if (!ts->suspended) __ads7846_disable(ts); ts->disabled = true; } } static void ads7846_enable(struct ads7846 *ts) { guard(mutex)(&ts->lock); if (ts->disabled) { ts->disabled = false; if (!ts->suspended) __ads7846_enable(ts); } } /*--------------------------------------------------------------------------*/ /* * 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. */ struct ser_req { u8 ref_on; u8 command; u8 ref_off; struct spi_message msg; struct spi_transfer xfer[8]; /* * DMA (thus cache coherency maintenance) requires the * transfer buffers to live in their own cache lines. */ __be16 sample ____cacheline_aligned; u16 scratch; }; struct ads7845_ser_req { u8 command[3]; struct spi_message msg; struct spi_transfer xfer[2]; /* * DMA (thus cache coherency maintenance) requires the * transfer buffers to live in their own cache lines. */ u8 sample[3] ____cacheline_aligned; }; static int ads7846_read12_ser(struct device *dev, unsigned command) { struct spi_device *spi = to_spi_device(dev); struct ads7846 *ts = dev_get_drvdata(dev); int status; struct ser_req *req __free(kfree) = kzalloc_obj(*req); if (!req) return -ENOMEM; spi_message_init(&req->msg); /* maybe turn on internal vREF, and let it settle */ if (ts->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.value = ts->vref_delay_usecs; req->xfer[1].delay.unit = SPI_DELAY_UNIT_USECS; spi_message_add_tail(&req->xfer[1], &req->msg); /* Enable reference voltage */ command |= ADS_PD10_REF_ON; } /* Enable ADC in every case */ command |= ADS_PD10_ADC_ON; /* 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 ... */ /* converter in low power mode & enable PENIRQ */ req->ref_off = PWRDOWN; 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; spi_message_add_tail(&req->xfer[5], &req->msg); /* clear the command register */ req->xfer[6].rx_buf = &req->scratch; req->xfer[6].len = 1; spi_message_add_tail(&req->xfer[6], &req->msg); req->xfer[7].rx_buf = &req->scratch; req->xfer[7].len = 2; CS_CHANGE(req->xfer[7]); spi_message_add_tail(&req->xfer[7], &req->msg); scoped_guard(mutex, &ts->lock) { ads7846_stop(ts); status = spi_sync(spi, &req->msg); ads7846_restart(ts); } if (status == 0) { /* on-wire is a must-ignore bit, a BE12 value, then padding */ status = be16_to_cpu(req->sample); status = status >> 3; status &= 0x0fff; } return status; } static int ads7845_read12_ser(struct device *dev, unsigned command) { struct spi_device *spi = to_spi_device(dev); struct ads7846 *ts = dev_get_drvdata(dev); int status; struct ads7845_ser_req *req __free(kfree) = kzalloc_obj(*req); if (!req) return -ENOMEM; spi_message_init(&req->msg); req->command[0] = (u8) command; req->xfer[0].tx_buf = req->command; req->xfer[0].rx_buf = req->sample; req->xfer[0].len = 3; spi_message_add_tail(&req->xfer[0], &req->msg); scoped_guard(mutex, &ts->lock) { ads7846_stop(ts); status = spi_sync(spi, &req->msg); ads7846_restart(ts); } if (status == 0) { /* BE12 value, then padding */ status = get_unaligned_be16(&req->sample[1]); status = status >> 3; status &= 0x0fff; } return status; } #if IS_ENABLED(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(&ts->spi->dev, \ READ_12BIT_SER(var)); \ if (v < 0) \ return v; \ return sprintf(buf, "%u\n", adjust(ts, v)); \ } \ static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL); /* Sysfs conventions report temperatures in millidegrees Celsius. * 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. */