137bd4469SLauri Leukkunen /* 237bd4469SLauri Leukkunen * TSC2005 touchscreen driver 337bd4469SLauri Leukkunen * 437bd4469SLauri Leukkunen * Copyright (C) 2006-2010 Nokia Corporation 537bd4469SLauri Leukkunen * 637bd4469SLauri Leukkunen * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com> 737bd4469SLauri Leukkunen * based on TSC2301 driver by Klaus K. Pedersen <klaus.k.pedersen@nokia.com> 837bd4469SLauri Leukkunen * 937bd4469SLauri Leukkunen * This program is free software; you can redistribute it and/or modify 1037bd4469SLauri Leukkunen * it under the terms of the GNU General Public License as published by 1137bd4469SLauri Leukkunen * the Free Software Foundation; either version 2 of the License, or 1237bd4469SLauri Leukkunen * (at your option) any later version. 1337bd4469SLauri Leukkunen * 1437bd4469SLauri Leukkunen * This program is distributed in the hope that it will be useful, 1537bd4469SLauri Leukkunen * but WITHOUT ANY WARRANTY; without even the implied warranty of 1637bd4469SLauri Leukkunen * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1737bd4469SLauri Leukkunen * GNU General Public License for more details. 1837bd4469SLauri Leukkunen * 1937bd4469SLauri Leukkunen * You should have received a copy of the GNU General Public License 2037bd4469SLauri Leukkunen * along with this program; if not, write to the Free Software 2137bd4469SLauri Leukkunen * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 2237bd4469SLauri Leukkunen * 2337bd4469SLauri Leukkunen */ 2437bd4469SLauri Leukkunen 2537bd4469SLauri Leukkunen #include <linux/kernel.h> 2637bd4469SLauri Leukkunen #include <linux/module.h> 2737bd4469SLauri Leukkunen #include <linux/input.h> 2837bd4469SLauri Leukkunen #include <linux/interrupt.h> 2937bd4469SLauri Leukkunen #include <linux/delay.h> 303ff8ff53SDmitry Torokhov #include <linux/pm.h> 3137bd4469SLauri Leukkunen #include <linux/spi/spi.h> 3237bd4469SLauri Leukkunen #include <linux/spi/tsc2005.h> 3337bd4469SLauri Leukkunen 3437bd4469SLauri Leukkunen /* 3537bd4469SLauri Leukkunen * The touchscreen interface operates as follows: 3637bd4469SLauri Leukkunen * 3737bd4469SLauri Leukkunen * 1) Pen is pressed against the touchscreen. 3837bd4469SLauri Leukkunen * 2) TSC2005 performs AD conversion. 3937bd4469SLauri Leukkunen * 3) After the conversion is done TSC2005 drives DAV line down. 4037bd4469SLauri Leukkunen * 4) GPIO IRQ is received and tsc2005_irq_thread() is scheduled. 4137bd4469SLauri Leukkunen * 5) tsc2005_irq_thread() queues up an spi transfer to fetch the x, y, z1, z2 4237bd4469SLauri Leukkunen * values. 4337bd4469SLauri Leukkunen * 6) tsc2005_irq_thread() reports coordinates to input layer and sets up 4437bd4469SLauri Leukkunen * tsc2005_penup_timer() to be called after TSC2005_PENUP_TIME_MS (40ms). 4537bd4469SLauri Leukkunen * 7) When the penup timer expires, there have not been touch or DAV interrupts 4637bd4469SLauri Leukkunen * during the last 40ms which means the pen has been lifted. 4737bd4469SLauri Leukkunen * 4837bd4469SLauri Leukkunen * ESD recovery via a hardware reset is done if the TSC2005 doesn't respond 4937bd4469SLauri Leukkunen * after a configurable period (in ms) of activity. If esd_timeout is 0, the 5037bd4469SLauri Leukkunen * watchdog is disabled. 5137bd4469SLauri Leukkunen */ 5237bd4469SLauri Leukkunen 5337bd4469SLauri Leukkunen /* control byte 1 */ 5437bd4469SLauri Leukkunen #define TSC2005_CMD 0x80 5537bd4469SLauri Leukkunen #define TSC2005_CMD_NORMAL 0x00 5637bd4469SLauri Leukkunen #define TSC2005_CMD_STOP 0x01 5737bd4469SLauri Leukkunen #define TSC2005_CMD_12BIT 0x04 5837bd4469SLauri Leukkunen 5937bd4469SLauri Leukkunen /* control byte 0 */ 6037bd4469SLauri Leukkunen #define TSC2005_REG_READ 0x0001 6137bd4469SLauri Leukkunen #define TSC2005_REG_PND0 0x0002 6237bd4469SLauri Leukkunen #define TSC2005_REG_X 0x0000 6337bd4469SLauri Leukkunen #define TSC2005_REG_Y 0x0008 6437bd4469SLauri Leukkunen #define TSC2005_REG_Z1 0x0010 6537bd4469SLauri Leukkunen #define TSC2005_REG_Z2 0x0018 6637bd4469SLauri Leukkunen #define TSC2005_REG_TEMP_HIGH 0x0050 6737bd4469SLauri Leukkunen #define TSC2005_REG_CFR0 0x0060 6837bd4469SLauri Leukkunen #define TSC2005_REG_CFR1 0x0068 6937bd4469SLauri Leukkunen #define TSC2005_REG_CFR2 0x0070 7037bd4469SLauri Leukkunen 7137bd4469SLauri Leukkunen /* configuration register 0 */ 7237bd4469SLauri Leukkunen #define TSC2005_CFR0_PRECHARGE_276US 0x0040 7337bd4469SLauri Leukkunen #define TSC2005_CFR0_STABTIME_1MS 0x0300 7437bd4469SLauri Leukkunen #define TSC2005_CFR0_CLOCK_1MHZ 0x1000 7537bd4469SLauri Leukkunen #define TSC2005_CFR0_RESOLUTION12 0x2000 7637bd4469SLauri Leukkunen #define TSC2005_CFR0_PENMODE 0x8000 7737bd4469SLauri Leukkunen #define TSC2005_CFR0_INITVALUE (TSC2005_CFR0_STABTIME_1MS | \ 7837bd4469SLauri Leukkunen TSC2005_CFR0_CLOCK_1MHZ | \ 7937bd4469SLauri Leukkunen TSC2005_CFR0_RESOLUTION12 | \ 8037bd4469SLauri Leukkunen TSC2005_CFR0_PRECHARGE_276US | \ 8137bd4469SLauri Leukkunen TSC2005_CFR0_PENMODE) 8237bd4469SLauri Leukkunen 8337bd4469SLauri Leukkunen /* bits common to both read and write of configuration register 0 */ 8437bd4469SLauri Leukkunen #define TSC2005_CFR0_RW_MASK 0x3fff 8537bd4469SLauri Leukkunen 8637bd4469SLauri Leukkunen /* configuration register 1 */ 8737bd4469SLauri Leukkunen #define TSC2005_CFR1_BATCHDELAY_4MS 0x0003 8837bd4469SLauri Leukkunen #define TSC2005_CFR1_INITVALUE TSC2005_CFR1_BATCHDELAY_4MS 8937bd4469SLauri Leukkunen 9037bd4469SLauri Leukkunen /* configuration register 2 */ 9137bd4469SLauri Leukkunen #define TSC2005_CFR2_MAVE_Z 0x0004 9237bd4469SLauri Leukkunen #define TSC2005_CFR2_MAVE_Y 0x0008 9337bd4469SLauri Leukkunen #define TSC2005_CFR2_MAVE_X 0x0010 9437bd4469SLauri Leukkunen #define TSC2005_CFR2_AVG_7 0x0800 9537bd4469SLauri Leukkunen #define TSC2005_CFR2_MEDIUM_15 0x3000 9637bd4469SLauri Leukkunen #define TSC2005_CFR2_INITVALUE (TSC2005_CFR2_MAVE_X | \ 9737bd4469SLauri Leukkunen TSC2005_CFR2_MAVE_Y | \ 9837bd4469SLauri Leukkunen TSC2005_CFR2_MAVE_Z | \ 9937bd4469SLauri Leukkunen TSC2005_CFR2_MEDIUM_15 | \ 10037bd4469SLauri Leukkunen TSC2005_CFR2_AVG_7) 10137bd4469SLauri Leukkunen 10237bd4469SLauri Leukkunen #define MAX_12BIT 0xfff 10337bd4469SLauri Leukkunen #define TSC2005_SPI_MAX_SPEED_HZ 10000000 10437bd4469SLauri Leukkunen #define TSC2005_PENUP_TIME_MS 40 10537bd4469SLauri Leukkunen 10637bd4469SLauri Leukkunen struct tsc2005_spi_rd { 10737bd4469SLauri Leukkunen struct spi_transfer spi_xfer; 10837bd4469SLauri Leukkunen u32 spi_tx; 10937bd4469SLauri Leukkunen u32 spi_rx; 11037bd4469SLauri Leukkunen }; 11137bd4469SLauri Leukkunen 11237bd4469SLauri Leukkunen struct tsc2005 { 11337bd4469SLauri Leukkunen struct spi_device *spi; 11437bd4469SLauri Leukkunen 11537bd4469SLauri Leukkunen struct spi_message spi_read_msg; 11637bd4469SLauri Leukkunen struct tsc2005_spi_rd spi_x; 11737bd4469SLauri Leukkunen struct tsc2005_spi_rd spi_y; 11837bd4469SLauri Leukkunen struct tsc2005_spi_rd spi_z1; 11937bd4469SLauri Leukkunen struct tsc2005_spi_rd spi_z2; 12037bd4469SLauri Leukkunen 12137bd4469SLauri Leukkunen struct input_dev *idev; 12237bd4469SLauri Leukkunen char phys[32]; 12337bd4469SLauri Leukkunen 12437bd4469SLauri Leukkunen struct mutex mutex; 12537bd4469SLauri Leukkunen 12637bd4469SLauri Leukkunen /* raw copy of previous x,y,z */ 12737bd4469SLauri Leukkunen int in_x; 12837bd4469SLauri Leukkunen int in_y; 12937bd4469SLauri Leukkunen int in_z1; 13037bd4469SLauri Leukkunen int in_z2; 13137bd4469SLauri Leukkunen 13280cc2f0cSDmitry Torokhov spinlock_t lock; 13337bd4469SLauri Leukkunen struct timer_list penup_timer; 13437bd4469SLauri Leukkunen 13537bd4469SLauri Leukkunen unsigned int esd_timeout; 1360b950d3dSDmitry Torokhov struct delayed_work esd_work; 1370b950d3dSDmitry Torokhov unsigned long last_valid_interrupt; 13837bd4469SLauri Leukkunen 13937bd4469SLauri Leukkunen unsigned int x_plate_ohm; 14037bd4469SLauri Leukkunen 1410b950d3dSDmitry Torokhov bool opened; 1420b950d3dSDmitry Torokhov bool suspended; 143c8b6846aSDmitry Torokhov 144c8b6846aSDmitry Torokhov bool pen_down; 14537bd4469SLauri Leukkunen 14637bd4469SLauri Leukkunen void (*set_reset)(bool enable); 14737bd4469SLauri Leukkunen }; 14837bd4469SLauri Leukkunen 14971f80045SDmitry Torokhov static int tsc2005_cmd(struct tsc2005 *ts, u8 cmd) 15037bd4469SLauri Leukkunen { 1519a6e180aSDmitry Torokhov u8 tx = TSC2005_CMD | TSC2005_CMD_12BIT | cmd; 1529a6e180aSDmitry Torokhov struct spi_transfer xfer = { 1539a6e180aSDmitry Torokhov .tx_buf = &tx, 1549a6e180aSDmitry Torokhov .len = 1, 1559a6e180aSDmitry Torokhov .bits_per_word = 8, 1569a6e180aSDmitry Torokhov }; 15737bd4469SLauri Leukkunen struct spi_message msg; 15871f80045SDmitry Torokhov int error; 15937bd4469SLauri Leukkunen 16037bd4469SLauri Leukkunen spi_message_init(&msg); 16137bd4469SLauri Leukkunen spi_message_add_tail(&xfer, &msg); 16271f80045SDmitry Torokhov 16371f80045SDmitry Torokhov error = spi_sync(ts->spi, &msg); 16471f80045SDmitry Torokhov if (error) { 16571f80045SDmitry Torokhov dev_err(&ts->spi->dev, "%s: failed, command: %x, error: %d\n", 16671f80045SDmitry Torokhov __func__, cmd, error); 16771f80045SDmitry Torokhov return error; 16837bd4469SLauri Leukkunen } 16937bd4469SLauri Leukkunen 17071f80045SDmitry Torokhov return 0; 17171f80045SDmitry Torokhov } 17271f80045SDmitry Torokhov 17371f80045SDmitry Torokhov static int tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value) 17437bd4469SLauri Leukkunen { 1759a6e180aSDmitry Torokhov u32 tx = ((reg | TSC2005_REG_PND0) << 16) | value; 1769a6e180aSDmitry Torokhov struct spi_transfer xfer = { 1779a6e180aSDmitry Torokhov .tx_buf = &tx, 1789a6e180aSDmitry Torokhov .len = 4, 1799a6e180aSDmitry Torokhov .bits_per_word = 24, 1809a6e180aSDmitry Torokhov }; 18137bd4469SLauri Leukkunen struct spi_message msg; 18271f80045SDmitry Torokhov int error; 18337bd4469SLauri Leukkunen 18437bd4469SLauri Leukkunen spi_message_init(&msg); 18537bd4469SLauri Leukkunen spi_message_add_tail(&xfer, &msg); 18671f80045SDmitry Torokhov 18771f80045SDmitry Torokhov error = spi_sync(ts->spi, &msg); 18871f80045SDmitry Torokhov if (error) { 18971f80045SDmitry Torokhov dev_err(&ts->spi->dev, 19071f80045SDmitry Torokhov "%s: failed, register: %x, value: %x, error: %d\n", 19171f80045SDmitry Torokhov __func__, reg, value, error); 19271f80045SDmitry Torokhov return error; 19371f80045SDmitry Torokhov } 19471f80045SDmitry Torokhov 19571f80045SDmitry Torokhov return 0; 19637bd4469SLauri Leukkunen } 19737bd4469SLauri Leukkunen 19837bd4469SLauri Leukkunen static void tsc2005_setup_read(struct tsc2005_spi_rd *rd, u8 reg, bool last) 19937bd4469SLauri Leukkunen { 2009a6e180aSDmitry Torokhov memset(rd, 0, sizeof(*rd)); 2019a6e180aSDmitry Torokhov 20237bd4469SLauri Leukkunen rd->spi_tx = (reg | TSC2005_REG_READ) << 16; 20337bd4469SLauri Leukkunen rd->spi_xfer.tx_buf = &rd->spi_tx; 20437bd4469SLauri Leukkunen rd->spi_xfer.rx_buf = &rd->spi_rx; 20537bd4469SLauri Leukkunen rd->spi_xfer.len = 4; 20637bd4469SLauri Leukkunen rd->spi_xfer.bits_per_word = 24; 20737bd4469SLauri Leukkunen rd->spi_xfer.cs_change = !last; 20837bd4469SLauri Leukkunen } 20937bd4469SLauri Leukkunen 21071f80045SDmitry Torokhov static int tsc2005_read(struct tsc2005 *ts, u8 reg, u16 *value) 21137bd4469SLauri Leukkunen { 2129a6e180aSDmitry Torokhov struct tsc2005_spi_rd spi_rd; 21337bd4469SLauri Leukkunen struct spi_message msg; 21471f80045SDmitry Torokhov int error; 21537bd4469SLauri Leukkunen 216c8b6846aSDmitry Torokhov tsc2005_setup_read(&spi_rd, reg, true); 21737bd4469SLauri Leukkunen 21837bd4469SLauri Leukkunen spi_message_init(&msg); 21937bd4469SLauri Leukkunen spi_message_add_tail(&spi_rd.spi_xfer, &msg); 22071f80045SDmitry Torokhov 22171f80045SDmitry Torokhov error = spi_sync(ts->spi, &msg); 22271f80045SDmitry Torokhov if (error) 22371f80045SDmitry Torokhov return error; 2249a6e180aSDmitry Torokhov 22537bd4469SLauri Leukkunen *value = spi_rd.spi_rx; 22671f80045SDmitry Torokhov return 0; 22737bd4469SLauri Leukkunen } 22837bd4469SLauri Leukkunen 22937bd4469SLauri Leukkunen static void tsc2005_update_pen_state(struct tsc2005 *ts, 23037bd4469SLauri Leukkunen int x, int y, int pressure) 23137bd4469SLauri Leukkunen { 23237bd4469SLauri Leukkunen if (pressure) { 23337bd4469SLauri Leukkunen input_report_abs(ts->idev, ABS_X, x); 23437bd4469SLauri Leukkunen input_report_abs(ts->idev, ABS_Y, y); 23537bd4469SLauri Leukkunen input_report_abs(ts->idev, ABS_PRESSURE, pressure); 23637bd4469SLauri Leukkunen if (!ts->pen_down) { 23737bd4469SLauri Leukkunen input_report_key(ts->idev, BTN_TOUCH, !!pressure); 238c8b6846aSDmitry Torokhov ts->pen_down = true; 23937bd4469SLauri Leukkunen } 24037bd4469SLauri Leukkunen } else { 24137bd4469SLauri Leukkunen input_report_abs(ts->idev, ABS_PRESSURE, 0); 24237bd4469SLauri Leukkunen if (ts->pen_down) { 24337bd4469SLauri Leukkunen input_report_key(ts->idev, BTN_TOUCH, 0); 244c8b6846aSDmitry Torokhov ts->pen_down = false; 24537bd4469SLauri Leukkunen } 24637bd4469SLauri Leukkunen } 24737bd4469SLauri Leukkunen input_sync(ts->idev); 24837bd4469SLauri Leukkunen dev_dbg(&ts->spi->dev, "point(%4d,%4d), pressure (%4d)\n", x, y, 24937bd4469SLauri Leukkunen pressure); 25037bd4469SLauri Leukkunen } 25137bd4469SLauri Leukkunen 25237bd4469SLauri Leukkunen static irqreturn_t tsc2005_irq_thread(int irq, void *_ts) 25337bd4469SLauri Leukkunen { 25437bd4469SLauri Leukkunen struct tsc2005 *ts = _ts; 25580cc2f0cSDmitry Torokhov unsigned long flags; 25637bd4469SLauri Leukkunen unsigned int pressure; 25780cc2f0cSDmitry Torokhov u32 x, y; 25880cc2f0cSDmitry Torokhov u32 z1, z2; 25971f80045SDmitry Torokhov int error; 26037bd4469SLauri Leukkunen 26137bd4469SLauri Leukkunen /* read the coordinates */ 26271f80045SDmitry Torokhov error = spi_sync(ts->spi, &ts->spi_read_msg); 26371f80045SDmitry Torokhov if (unlikely(error)) 26471f80045SDmitry Torokhov goto out; 26571f80045SDmitry Torokhov 26637bd4469SLauri Leukkunen x = ts->spi_x.spi_rx; 26737bd4469SLauri Leukkunen y = ts->spi_y.spi_rx; 26837bd4469SLauri Leukkunen z1 = ts->spi_z1.spi_rx; 26937bd4469SLauri Leukkunen z2 = ts->spi_z2.spi_rx; 27037bd4469SLauri Leukkunen 27137bd4469SLauri Leukkunen /* validate position */ 27237bd4469SLauri Leukkunen if (unlikely(x > MAX_12BIT || y > MAX_12BIT)) 27337bd4469SLauri Leukkunen goto out; 27437bd4469SLauri Leukkunen 27580cc2f0cSDmitry Torokhov /* Skip reading if the pressure components are out of range */ 27637bd4469SLauri Leukkunen if (unlikely(z1 == 0 || z2 > MAX_12BIT || z1 >= z2)) 27737bd4469SLauri Leukkunen goto out; 27837bd4469SLauri Leukkunen 27980cc2f0cSDmitry Torokhov /* 28080cc2f0cSDmitry Torokhov * Skip point if this is a pen down with the exact same values as 28137bd4469SLauri Leukkunen * the value before pen-up - that implies SPI fed us stale data 28237bd4469SLauri Leukkunen */ 28337bd4469SLauri Leukkunen if (!ts->pen_down && 28480cc2f0cSDmitry Torokhov ts->in_x == x && ts->in_y == y && 28580cc2f0cSDmitry Torokhov ts->in_z1 == z1 && ts->in_z2 == z2) { 28637bd4469SLauri Leukkunen goto out; 28780cc2f0cSDmitry Torokhov } 28837bd4469SLauri Leukkunen 28980cc2f0cSDmitry Torokhov /* 29080cc2f0cSDmitry Torokhov * At this point we are happy we have a valid and useful reading. 29180cc2f0cSDmitry Torokhov * Remember it for later comparisons. We may now begin downsampling. 29237bd4469SLauri Leukkunen */ 29337bd4469SLauri Leukkunen ts->in_x = x; 29437bd4469SLauri Leukkunen ts->in_y = y; 29537bd4469SLauri Leukkunen ts->in_z1 = z1; 29637bd4469SLauri Leukkunen ts->in_z2 = z2; 29737bd4469SLauri Leukkunen 29880cc2f0cSDmitry Torokhov /* Compute touch pressure resistance using equation #1 */ 29937bd4469SLauri Leukkunen pressure = x * (z2 - z1) / z1; 30037bd4469SLauri Leukkunen pressure = pressure * ts->x_plate_ohm / 4096; 30137bd4469SLauri Leukkunen if (unlikely(pressure > MAX_12BIT)) 30237bd4469SLauri Leukkunen goto out; 30337bd4469SLauri Leukkunen 30480cc2f0cSDmitry Torokhov spin_lock_irqsave(&ts->lock, flags); 30580cc2f0cSDmitry Torokhov 30637bd4469SLauri Leukkunen tsc2005_update_pen_state(ts, x, y, pressure); 30737bd4469SLauri Leukkunen mod_timer(&ts->penup_timer, 30837bd4469SLauri Leukkunen jiffies + msecs_to_jiffies(TSC2005_PENUP_TIME_MS)); 30937bd4469SLauri Leukkunen 31080cc2f0cSDmitry Torokhov spin_unlock_irqrestore(&ts->lock, flags); 31137bd4469SLauri Leukkunen 3120b950d3dSDmitry Torokhov ts->last_valid_interrupt = jiffies; 31337bd4469SLauri Leukkunen out: 31437bd4469SLauri Leukkunen return IRQ_HANDLED; 31537bd4469SLauri Leukkunen } 31637bd4469SLauri Leukkunen 31737bd4469SLauri Leukkunen static void tsc2005_penup_timer(unsigned long data) 31837bd4469SLauri Leukkunen { 31937bd4469SLauri Leukkunen struct tsc2005 *ts = (struct tsc2005 *)data; 32080cc2f0cSDmitry Torokhov unsigned long flags; 32137bd4469SLauri Leukkunen 32280cc2f0cSDmitry Torokhov spin_lock_irqsave(&ts->lock, flags); 32337bd4469SLauri Leukkunen tsc2005_update_pen_state(ts, 0, 0, 0); 32480cc2f0cSDmitry Torokhov spin_unlock_irqrestore(&ts->lock, flags); 32537bd4469SLauri Leukkunen } 32637bd4469SLauri Leukkunen 32737bd4469SLauri Leukkunen static void tsc2005_start_scan(struct tsc2005 *ts) 32837bd4469SLauri Leukkunen { 32937bd4469SLauri Leukkunen tsc2005_write(ts, TSC2005_REG_CFR0, TSC2005_CFR0_INITVALUE); 33037bd4469SLauri Leukkunen tsc2005_write(ts, TSC2005_REG_CFR1, TSC2005_CFR1_INITVALUE); 33137bd4469SLauri Leukkunen tsc2005_write(ts, TSC2005_REG_CFR2, TSC2005_CFR2_INITVALUE); 33237bd4469SLauri Leukkunen tsc2005_cmd(ts, TSC2005_CMD_NORMAL); 33337bd4469SLauri Leukkunen } 33437bd4469SLauri Leukkunen 33537bd4469SLauri Leukkunen static void tsc2005_stop_scan(struct tsc2005 *ts) 33637bd4469SLauri Leukkunen { 33737bd4469SLauri Leukkunen tsc2005_cmd(ts, TSC2005_CMD_STOP); 33837bd4469SLauri Leukkunen } 33937bd4469SLauri Leukkunen 3400b950d3dSDmitry Torokhov /* must be called with ts->mutex held */ 3410b950d3dSDmitry Torokhov static void __tsc2005_disable(struct tsc2005 *ts) 34237bd4469SLauri Leukkunen { 34337bd4469SLauri Leukkunen tsc2005_stop_scan(ts); 3440b950d3dSDmitry Torokhov 3450b950d3dSDmitry Torokhov disable_irq(ts->spi->irq); 3460b950d3dSDmitry Torokhov del_timer_sync(&ts->penup_timer); 3470b950d3dSDmitry Torokhov 3480b950d3dSDmitry Torokhov cancel_delayed_work_sync(&ts->esd_work); 3490b950d3dSDmitry Torokhov 3500b950d3dSDmitry Torokhov enable_irq(ts->spi->irq); 35137bd4469SLauri Leukkunen } 35237bd4469SLauri Leukkunen 3530b950d3dSDmitry Torokhov /* must be called with ts->mutex held */ 3540b950d3dSDmitry Torokhov static void __tsc2005_enable(struct tsc2005 *ts) 35537bd4469SLauri Leukkunen { 35637bd4469SLauri Leukkunen tsc2005_start_scan(ts); 3570b950d3dSDmitry Torokhov 3580b950d3dSDmitry Torokhov if (ts->esd_timeout && ts->set_reset) { 3590b950d3dSDmitry Torokhov ts->last_valid_interrupt = jiffies; 3600b950d3dSDmitry Torokhov schedule_delayed_work(&ts->esd_work, 36190342795SAaro Koskinen round_jiffies_relative( 3620b950d3dSDmitry Torokhov msecs_to_jiffies(ts->esd_timeout))); 3630b950d3dSDmitry Torokhov } 3640b950d3dSDmitry Torokhov 36537bd4469SLauri Leukkunen } 36637bd4469SLauri Leukkunen 36737bd4469SLauri Leukkunen static ssize_t tsc2005_selftest_show(struct device *dev, 36837bd4469SLauri Leukkunen struct device_attribute *attr, 36937bd4469SLauri Leukkunen char *buf) 37037bd4469SLauri Leukkunen { 3716b007d62SDmitry Torokhov struct spi_device *spi = to_spi_device(dev); 3726b007d62SDmitry Torokhov struct tsc2005 *ts = spi_get_drvdata(spi); 37337bd4469SLauri Leukkunen u16 temp_high; 37437bd4469SLauri Leukkunen u16 temp_high_orig; 37537bd4469SLauri Leukkunen u16 temp_high_test; 37671f80045SDmitry Torokhov bool success = true; 37771f80045SDmitry Torokhov int error; 37837bd4469SLauri Leukkunen 37937bd4469SLauri Leukkunen mutex_lock(&ts->mutex); 38037bd4469SLauri Leukkunen 38137bd4469SLauri Leukkunen /* 38237bd4469SLauri Leukkunen * Test TSC2005 communications via temp high register. 38337bd4469SLauri Leukkunen */ 3840b950d3dSDmitry Torokhov __tsc2005_disable(ts); 38571f80045SDmitry Torokhov 38671f80045SDmitry Torokhov error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high_orig); 38771f80045SDmitry Torokhov if (error) { 38871f80045SDmitry Torokhov dev_warn(dev, "selftest failed: read error %d\n", error); 38971f80045SDmitry Torokhov success = false; 39071f80045SDmitry Torokhov goto out; 39171f80045SDmitry Torokhov } 39271f80045SDmitry Torokhov 39337bd4469SLauri Leukkunen temp_high_test = (temp_high_orig - 1) & MAX_12BIT; 39471f80045SDmitry Torokhov 39571f80045SDmitry Torokhov error = tsc2005_write(ts, TSC2005_REG_TEMP_HIGH, temp_high_test); 39671f80045SDmitry Torokhov if (error) { 39771f80045SDmitry Torokhov dev_warn(dev, "selftest failed: write error %d\n", error); 39871f80045SDmitry Torokhov success = false; 39971f80045SDmitry Torokhov goto out; 40071f80045SDmitry Torokhov } 40171f80045SDmitry Torokhov 40271f80045SDmitry Torokhov error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high); 40371f80045SDmitry Torokhov if (error) { 40471f80045SDmitry Torokhov dev_warn(dev, "selftest failed: read error %d after write\n", 40571f80045SDmitry Torokhov error); 40671f80045SDmitry Torokhov success = false; 40771f80045SDmitry Torokhov goto out; 40871f80045SDmitry Torokhov } 40971f80045SDmitry Torokhov 41037bd4469SLauri Leukkunen if (temp_high != temp_high_test) { 41137bd4469SLauri Leukkunen dev_warn(dev, "selftest failed: %d != %d\n", 41237bd4469SLauri Leukkunen temp_high, temp_high_test); 41371f80045SDmitry Torokhov success = false; 41437bd4469SLauri Leukkunen } 41537bd4469SLauri Leukkunen 41637bd4469SLauri Leukkunen /* hardware reset */ 417c8b6846aSDmitry Torokhov ts->set_reset(false); 41837bd4469SLauri Leukkunen usleep_range(100, 500); /* only 10us required */ 419c8b6846aSDmitry Torokhov ts->set_reset(true); 42071f80045SDmitry Torokhov 42171f80045SDmitry Torokhov if (!success) 42271f80045SDmitry Torokhov goto out; 42337bd4469SLauri Leukkunen 42437bd4469SLauri Leukkunen /* test that the reset really happened */ 42571f80045SDmitry Torokhov error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high); 42671f80045SDmitry Torokhov if (error) { 42771f80045SDmitry Torokhov dev_warn(dev, "selftest failed: read error %d after reset\n", 42871f80045SDmitry Torokhov error); 42971f80045SDmitry Torokhov success = false; 43071f80045SDmitry Torokhov goto out; 43171f80045SDmitry Torokhov } 43271f80045SDmitry Torokhov 43337bd4469SLauri Leukkunen if (temp_high != temp_high_orig) { 43437bd4469SLauri Leukkunen dev_warn(dev, "selftest failed after reset: %d != %d\n", 43537bd4469SLauri Leukkunen temp_high, temp_high_orig); 43671f80045SDmitry Torokhov success = false; 43737bd4469SLauri Leukkunen } 43837bd4469SLauri Leukkunen 43971f80045SDmitry Torokhov out: 4400b950d3dSDmitry Torokhov __tsc2005_enable(ts); 44137bd4469SLauri Leukkunen mutex_unlock(&ts->mutex); 44237bd4469SLauri Leukkunen 44371f80045SDmitry Torokhov return sprintf(buf, "%d\n", success); 44437bd4469SLauri Leukkunen } 4458dbcc432SDmitry Torokhov 44637bd4469SLauri Leukkunen static DEVICE_ATTR(selftest, S_IRUGO, tsc2005_selftest_show, NULL); 44737bd4469SLauri Leukkunen 4488dbcc432SDmitry Torokhov static struct attribute *tsc2005_attrs[] = { 4498dbcc432SDmitry Torokhov &dev_attr_selftest.attr, 4508dbcc432SDmitry Torokhov NULL 4518dbcc432SDmitry Torokhov }; 4528dbcc432SDmitry Torokhov 453587a1f16SAl Viro static umode_t tsc2005_attr_is_visible(struct kobject *kobj, 4548dbcc432SDmitry Torokhov struct attribute *attr, int n) 4558dbcc432SDmitry Torokhov { 4568dbcc432SDmitry Torokhov struct device *dev = container_of(kobj, struct device, kobj); 4578dbcc432SDmitry Torokhov struct spi_device *spi = to_spi_device(dev); 4588dbcc432SDmitry Torokhov struct tsc2005 *ts = spi_get_drvdata(spi); 459587a1f16SAl Viro umode_t mode = attr->mode; 4608dbcc432SDmitry Torokhov 4618dbcc432SDmitry Torokhov if (attr == &dev_attr_selftest.attr) { 4628dbcc432SDmitry Torokhov if (!ts->set_reset) 4638dbcc432SDmitry Torokhov mode = 0; 4648dbcc432SDmitry Torokhov } 4658dbcc432SDmitry Torokhov 4668dbcc432SDmitry Torokhov return mode; 4678dbcc432SDmitry Torokhov } 4688dbcc432SDmitry Torokhov 4698dbcc432SDmitry Torokhov static const struct attribute_group tsc2005_attr_group = { 4708dbcc432SDmitry Torokhov .is_visible = tsc2005_attr_is_visible, 4718dbcc432SDmitry Torokhov .attrs = tsc2005_attrs, 4728dbcc432SDmitry Torokhov }; 4738dbcc432SDmitry Torokhov 47437bd4469SLauri Leukkunen static void tsc2005_esd_work(struct work_struct *work) 47537bd4469SLauri Leukkunen { 4760b950d3dSDmitry Torokhov struct tsc2005 *ts = container_of(work, struct tsc2005, esd_work.work); 47771f80045SDmitry Torokhov int error; 47837bd4469SLauri Leukkunen u16 r; 47937bd4469SLauri Leukkunen 480a0fa2206SAaro Koskinen if (!mutex_trylock(&ts->mutex)) { 481a0fa2206SAaro Koskinen /* 482a0fa2206SAaro Koskinen * If the mutex is taken, it means that disable or enable is in 483a0fa2206SAaro Koskinen * progress. In that case just reschedule the work. If the work 484a0fa2206SAaro Koskinen * is not needed, it will be canceled by disable. 485a0fa2206SAaro Koskinen */ 486a0fa2206SAaro Koskinen goto reschedule; 487a0fa2206SAaro Koskinen } 48837bd4469SLauri Leukkunen 4890b950d3dSDmitry Torokhov if (time_is_after_jiffies(ts->last_valid_interrupt + 4900b950d3dSDmitry Torokhov msecs_to_jiffies(ts->esd_timeout))) 49137bd4469SLauri Leukkunen goto out; 49237bd4469SLauri Leukkunen 4930b950d3dSDmitry Torokhov /* We should be able to read register without disabling interrupts. */ 49471f80045SDmitry Torokhov error = tsc2005_read(ts, TSC2005_REG_CFR0, &r); 4950b950d3dSDmitry Torokhov if (!error && 4960b950d3dSDmitry Torokhov !((r ^ TSC2005_CFR0_INITVALUE) & TSC2005_CFR0_RW_MASK)) { 4970b950d3dSDmitry Torokhov goto out; 49837bd4469SLauri Leukkunen } 49937bd4469SLauri Leukkunen 5000b950d3dSDmitry Torokhov /* 5010b950d3dSDmitry Torokhov * If we could not read our known value from configuration register 0 5020b950d3dSDmitry Torokhov * then we should reset the controller as if from power-up and start 5030b950d3dSDmitry Torokhov * scanning again. 5040b950d3dSDmitry Torokhov */ 5050b950d3dSDmitry Torokhov dev_info(&ts->spi->dev, "TSC2005 not responding - resetting\n"); 5060b950d3dSDmitry Torokhov 5070b950d3dSDmitry Torokhov disable_irq(ts->spi->irq); 5080b950d3dSDmitry Torokhov del_timer_sync(&ts->penup_timer); 5090b950d3dSDmitry Torokhov 5100b950d3dSDmitry Torokhov tsc2005_update_pen_state(ts, 0, 0, 0); 5110b950d3dSDmitry Torokhov 5120b950d3dSDmitry Torokhov ts->set_reset(false); 5130b950d3dSDmitry Torokhov usleep_range(100, 500); /* only 10us required */ 5140b950d3dSDmitry Torokhov ts->set_reset(true); 5150b950d3dSDmitry Torokhov 5160b950d3dSDmitry Torokhov enable_irq(ts->spi->irq); 5170b950d3dSDmitry Torokhov tsc2005_start_scan(ts); 51837bd4469SLauri Leukkunen 51937bd4469SLauri Leukkunen out: 520a0fa2206SAaro Koskinen mutex_unlock(&ts->mutex); 521a0fa2206SAaro Koskinen reschedule: 5220b950d3dSDmitry Torokhov /* re-arm the watchdog */ 5230b950d3dSDmitry Torokhov schedule_delayed_work(&ts->esd_work, 52490342795SAaro Koskinen round_jiffies_relative( 5250b950d3dSDmitry Torokhov msecs_to_jiffies(ts->esd_timeout))); 5260b950d3dSDmitry Torokhov } 5270b950d3dSDmitry Torokhov 5280b950d3dSDmitry Torokhov static int tsc2005_open(struct input_dev *input) 5290b950d3dSDmitry Torokhov { 5300b950d3dSDmitry Torokhov struct tsc2005 *ts = input_get_drvdata(input); 5310b950d3dSDmitry Torokhov 5320b950d3dSDmitry Torokhov mutex_lock(&ts->mutex); 5330b950d3dSDmitry Torokhov 5345cb81d19SDmitry Torokhov if (!ts->suspended) 5350b950d3dSDmitry Torokhov __tsc2005_enable(ts); 5360b950d3dSDmitry Torokhov 5370b950d3dSDmitry Torokhov ts->opened = true; 5380b950d3dSDmitry Torokhov 5390b950d3dSDmitry Torokhov mutex_unlock(&ts->mutex); 5400b950d3dSDmitry Torokhov 5410b950d3dSDmitry Torokhov return 0; 5420b950d3dSDmitry Torokhov } 5430b950d3dSDmitry Torokhov 5440b950d3dSDmitry Torokhov static void tsc2005_close(struct input_dev *input) 5450b950d3dSDmitry Torokhov { 5460b950d3dSDmitry Torokhov struct tsc2005 *ts = input_get_drvdata(input); 5470b950d3dSDmitry Torokhov 5480b950d3dSDmitry Torokhov mutex_lock(&ts->mutex); 5490b950d3dSDmitry Torokhov 5505cb81d19SDmitry Torokhov if (!ts->suspended) 5510b950d3dSDmitry Torokhov __tsc2005_disable(ts); 5520b950d3dSDmitry Torokhov 5530b950d3dSDmitry Torokhov ts->opened = false; 5540b950d3dSDmitry Torokhov 55537bd4469SLauri Leukkunen mutex_unlock(&ts->mutex); 55637bd4469SLauri Leukkunen } 55737bd4469SLauri Leukkunen 5585298cc4cSBill Pemberton static void tsc2005_setup_spi_xfer(struct tsc2005 *ts) 55937bd4469SLauri Leukkunen { 560c8b6846aSDmitry Torokhov tsc2005_setup_read(&ts->spi_x, TSC2005_REG_X, false); 561c8b6846aSDmitry Torokhov tsc2005_setup_read(&ts->spi_y, TSC2005_REG_Y, false); 562c8b6846aSDmitry Torokhov tsc2005_setup_read(&ts->spi_z1, TSC2005_REG_Z1, false); 563c8b6846aSDmitry Torokhov tsc2005_setup_read(&ts->spi_z2, TSC2005_REG_Z2, true); 56437bd4469SLauri Leukkunen 56537bd4469SLauri Leukkunen spi_message_init(&ts->spi_read_msg); 56637bd4469SLauri Leukkunen spi_message_add_tail(&ts->spi_x.spi_xfer, &ts->spi_read_msg); 56737bd4469SLauri Leukkunen spi_message_add_tail(&ts->spi_y.spi_xfer, &ts->spi_read_msg); 56837bd4469SLauri Leukkunen spi_message_add_tail(&ts->spi_z1.spi_xfer, &ts->spi_read_msg); 56937bd4469SLauri Leukkunen spi_message_add_tail(&ts->spi_z2.spi_xfer, &ts->spi_read_msg); 57037bd4469SLauri Leukkunen } 57137bd4469SLauri Leukkunen 5725298cc4cSBill Pemberton static int tsc2005_probe(struct spi_device *spi) 57337bd4469SLauri Leukkunen { 57499bb892dSDmitry Torokhov const struct tsc2005_platform_data *pdata = spi->dev.platform_data; 57537bd4469SLauri Leukkunen struct tsc2005 *ts; 57699bb892dSDmitry Torokhov struct input_dev *input_dev; 57799bb892dSDmitry Torokhov unsigned int max_x, max_y, max_p; 57899bb892dSDmitry Torokhov unsigned int fudge_x, fudge_y, fudge_p; 57999bb892dSDmitry Torokhov int error; 58037bd4469SLauri Leukkunen 58137bd4469SLauri Leukkunen if (!pdata) { 58237bd4469SLauri Leukkunen dev_dbg(&spi->dev, "no platform data\n"); 58337bd4469SLauri Leukkunen return -ENODEV; 58437bd4469SLauri Leukkunen } 58537bd4469SLauri Leukkunen 58699bb892dSDmitry Torokhov fudge_x = pdata->ts_x_fudge ? : 4; 58799bb892dSDmitry Torokhov fudge_y = pdata->ts_y_fudge ? : 8; 58899bb892dSDmitry Torokhov fudge_p = pdata->ts_pressure_fudge ? : 2; 58999bb892dSDmitry Torokhov max_x = pdata->ts_x_max ? : MAX_12BIT; 59099bb892dSDmitry Torokhov max_y = pdata->ts_y_max ? : MAX_12BIT; 59199bb892dSDmitry Torokhov max_p = pdata->ts_pressure_max ? : MAX_12BIT; 59237bd4469SLauri Leukkunen 59399bb892dSDmitry Torokhov if (spi->irq <= 0) { 59499bb892dSDmitry Torokhov dev_dbg(&spi->dev, "no irq\n"); 59599bb892dSDmitry Torokhov return -ENODEV; 59699bb892dSDmitry Torokhov } 59799bb892dSDmitry Torokhov 59837bd4469SLauri Leukkunen spi->mode = SPI_MODE_0; 59937bd4469SLauri Leukkunen spi->bits_per_word = 8; 60037bd4469SLauri Leukkunen if (!spi->max_speed_hz) 60137bd4469SLauri Leukkunen spi->max_speed_hz = TSC2005_SPI_MAX_SPEED_HZ; 60237bd4469SLauri Leukkunen 60399bb892dSDmitry Torokhov error = spi_setup(spi); 60499bb892dSDmitry Torokhov if (error) 60599bb892dSDmitry Torokhov return error; 60699bb892dSDmitry Torokhov 60799bb892dSDmitry Torokhov ts = kzalloc(sizeof(*ts), GFP_KERNEL); 60899bb892dSDmitry Torokhov input_dev = input_allocate_device(); 60999bb892dSDmitry Torokhov if (!ts || !input_dev) { 61099bb892dSDmitry Torokhov error = -ENOMEM; 61199bb892dSDmitry Torokhov goto err_free_mem; 6122721a89aSDmitry Torokhov } 61399bb892dSDmitry Torokhov 61499bb892dSDmitry Torokhov ts->spi = spi; 61599bb892dSDmitry Torokhov ts->idev = input_dev; 61699bb892dSDmitry Torokhov 61799bb892dSDmitry Torokhov ts->x_plate_ohm = pdata->ts_x_plate_ohm ? : 280; 61899bb892dSDmitry Torokhov ts->esd_timeout = pdata->esd_timeout_ms; 61999bb892dSDmitry Torokhov ts->set_reset = pdata->set_reset; 62099bb892dSDmitry Torokhov 62199bb892dSDmitry Torokhov mutex_init(&ts->mutex); 62299bb892dSDmitry Torokhov 62380cc2f0cSDmitry Torokhov spin_lock_init(&ts->lock); 62499bb892dSDmitry Torokhov setup_timer(&ts->penup_timer, tsc2005_penup_timer, (unsigned long)ts); 62599bb892dSDmitry Torokhov 6260b950d3dSDmitry Torokhov INIT_DELAYED_WORK(&ts->esd_work, tsc2005_esd_work); 62799bb892dSDmitry Torokhov 62899bb892dSDmitry Torokhov tsc2005_setup_spi_xfer(ts); 62999bb892dSDmitry Torokhov 63099bb892dSDmitry Torokhov snprintf(ts->phys, sizeof(ts->phys), 63199bb892dSDmitry Torokhov "%s/input-ts", dev_name(&spi->dev)); 63299bb892dSDmitry Torokhov 63399bb892dSDmitry Torokhov input_dev->name = "TSC2005 touchscreen"; 63499bb892dSDmitry Torokhov input_dev->phys = ts->phys; 63599bb892dSDmitry Torokhov input_dev->id.bustype = BUS_SPI; 63699bb892dSDmitry Torokhov input_dev->dev.parent = &spi->dev; 63799bb892dSDmitry Torokhov input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); 63899bb892dSDmitry Torokhov input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 63999bb892dSDmitry Torokhov 64099bb892dSDmitry Torokhov input_set_abs_params(input_dev, ABS_X, 0, max_x, fudge_x, 0); 64199bb892dSDmitry Torokhov input_set_abs_params(input_dev, ABS_Y, 0, max_y, fudge_y, 0); 64299bb892dSDmitry Torokhov input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0); 64399bb892dSDmitry Torokhov 6440b950d3dSDmitry Torokhov input_dev->open = tsc2005_open; 6450b950d3dSDmitry Torokhov input_dev->close = tsc2005_close; 6460b950d3dSDmitry Torokhov 6470b950d3dSDmitry Torokhov input_set_drvdata(input_dev, ts); 6480b950d3dSDmitry Torokhov 6490b950d3dSDmitry Torokhov /* Ensure the touchscreen is off */ 6500b950d3dSDmitry Torokhov tsc2005_stop_scan(ts); 6510b950d3dSDmitry Torokhov 652dacb650fSDmitry Torokhov error = request_threaded_irq(spi->irq, NULL, tsc2005_irq_thread, 6539b7e31bbSLars-Peter Clausen IRQF_TRIGGER_RISING | IRQF_ONESHOT, 6549b7e31bbSLars-Peter Clausen "tsc2005", ts); 65599bb892dSDmitry Torokhov if (error) { 65699bb892dSDmitry Torokhov dev_err(&spi->dev, "Failed to request irq, err: %d\n", error); 65799bb892dSDmitry Torokhov goto err_free_mem; 65899bb892dSDmitry Torokhov } 65999bb892dSDmitry Torokhov 66099bb892dSDmitry Torokhov spi_set_drvdata(spi, ts); 66199bb892dSDmitry Torokhov error = sysfs_create_group(&spi->dev.kobj, &tsc2005_attr_group); 66299bb892dSDmitry Torokhov if (error) { 66399bb892dSDmitry Torokhov dev_err(&spi->dev, 66499bb892dSDmitry Torokhov "Failed to create sysfs attributes, err: %d\n", error); 66599bb892dSDmitry Torokhov goto err_clear_drvdata; 66699bb892dSDmitry Torokhov } 66799bb892dSDmitry Torokhov 66899bb892dSDmitry Torokhov error = input_register_device(ts->idev); 66999bb892dSDmitry Torokhov if (error) { 67099bb892dSDmitry Torokhov dev_err(&spi->dev, 67199bb892dSDmitry Torokhov "Failed to register input device, err: %d\n", error); 67299bb892dSDmitry Torokhov goto err_remove_sysfs; 67399bb892dSDmitry Torokhov } 67499bb892dSDmitry Torokhov 675ddca6a31SGeert Uytterhoeven irq_set_irq_wake(spi->irq, 1); 67699bb892dSDmitry Torokhov return 0; 67799bb892dSDmitry Torokhov 67899bb892dSDmitry Torokhov err_remove_sysfs: 67999bb892dSDmitry Torokhov sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group); 68099bb892dSDmitry Torokhov err_clear_drvdata: 68199bb892dSDmitry Torokhov spi_set_drvdata(spi, NULL); 68299bb892dSDmitry Torokhov free_irq(spi->irq, ts); 68399bb892dSDmitry Torokhov err_free_mem: 68499bb892dSDmitry Torokhov input_free_device(input_dev); 68599bb892dSDmitry Torokhov kfree(ts); 68699bb892dSDmitry Torokhov return error; 68737bd4469SLauri Leukkunen } 68837bd4469SLauri Leukkunen 689e2619cf7SBill Pemberton static int tsc2005_remove(struct spi_device *spi) 69037bd4469SLauri Leukkunen { 6916b007d62SDmitry Torokhov struct tsc2005 *ts = spi_get_drvdata(spi); 69237bd4469SLauri Leukkunen 69399bb892dSDmitry Torokhov sysfs_remove_group(&ts->spi->dev.kobj, &tsc2005_attr_group); 69499bb892dSDmitry Torokhov 69537bd4469SLauri Leukkunen free_irq(ts->spi->irq, ts); 69637bd4469SLauri Leukkunen input_unregister_device(ts->idev); 69737bd4469SLauri Leukkunen kfree(ts); 69837bd4469SLauri Leukkunen 6992721a89aSDmitry Torokhov spi_set_drvdata(spi, NULL); 70037bd4469SLauri Leukkunen return 0; 70137bd4469SLauri Leukkunen } 70237bd4469SLauri Leukkunen 7033ff8ff53SDmitry Torokhov #ifdef CONFIG_PM_SLEEP 7043ff8ff53SDmitry Torokhov static int tsc2005_suspend(struct device *dev) 70537bd4469SLauri Leukkunen { 7066b007d62SDmitry Torokhov struct spi_device *spi = to_spi_device(dev); 7076b007d62SDmitry Torokhov struct tsc2005 *ts = spi_get_drvdata(spi); 70837bd4469SLauri Leukkunen 70937bd4469SLauri Leukkunen mutex_lock(&ts->mutex); 7100b950d3dSDmitry Torokhov 7115cb81d19SDmitry Torokhov if (!ts->suspended && ts->opened) 7120b950d3dSDmitry Torokhov __tsc2005_disable(ts); 7130b950d3dSDmitry Torokhov 7140b950d3dSDmitry Torokhov ts->suspended = true; 7150b950d3dSDmitry Torokhov 71637bd4469SLauri Leukkunen mutex_unlock(&ts->mutex); 71737bd4469SLauri Leukkunen 71837bd4469SLauri Leukkunen return 0; 71937bd4469SLauri Leukkunen } 72037bd4469SLauri Leukkunen 7213ff8ff53SDmitry Torokhov static int tsc2005_resume(struct device *dev) 72237bd4469SLauri Leukkunen { 7236b007d62SDmitry Torokhov struct spi_device *spi = to_spi_device(dev); 7246b007d62SDmitry Torokhov struct tsc2005 *ts = spi_get_drvdata(spi); 72537bd4469SLauri Leukkunen 72637bd4469SLauri Leukkunen mutex_lock(&ts->mutex); 7270b950d3dSDmitry Torokhov 7285cb81d19SDmitry Torokhov if (ts->suspended && ts->opened) 7290b950d3dSDmitry Torokhov __tsc2005_enable(ts); 7300b950d3dSDmitry Torokhov 7310b950d3dSDmitry Torokhov ts->suspended = false; 7320b950d3dSDmitry Torokhov 73337bd4469SLauri Leukkunen mutex_unlock(&ts->mutex); 73437bd4469SLauri Leukkunen 73537bd4469SLauri Leukkunen return 0; 73637bd4469SLauri Leukkunen } 73737bd4469SLauri Leukkunen #endif 73837bd4469SLauri Leukkunen 7393ff8ff53SDmitry Torokhov static SIMPLE_DEV_PM_OPS(tsc2005_pm_ops, tsc2005_suspend, tsc2005_resume); 7403ff8ff53SDmitry Torokhov 74137bd4469SLauri Leukkunen static struct spi_driver tsc2005_driver = { 74237bd4469SLauri Leukkunen .driver = { 74337bd4469SLauri Leukkunen .name = "tsc2005", 74437bd4469SLauri Leukkunen .owner = THIS_MODULE, 7453ff8ff53SDmitry Torokhov .pm = &tsc2005_pm_ops, 74637bd4469SLauri Leukkunen }, 74737bd4469SLauri Leukkunen .probe = tsc2005_probe, 7481cb0aa88SBill Pemberton .remove = tsc2005_remove, 74937bd4469SLauri Leukkunen }; 75037bd4469SLauri Leukkunen 751ca83922eSAxel Lin module_spi_driver(tsc2005_driver); 75237bd4469SLauri Leukkunen 75337bd4469SLauri Leukkunen MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>"); 754b88aa494SDmitry Torokhov MODULE_DESCRIPTION("TSC2005 Touchscreen Driver"); 75537bd4469SLauri Leukkunen MODULE_LICENSE("GPL"); 756*938789feSPali Rohár MODULE_ALIAS("spi:tsc2005"); 757