summaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen/syntm12xx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/touchscreen/syntm12xx.c')
-rw-r--r--drivers/input/touchscreen/syntm12xx.c3545
1 files changed, 3545 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/syntm12xx.c b/drivers/input/touchscreen/syntm12xx.c
new file mode 100644
index 00000000000..c0ab7f7933a
--- /dev/null
+++ b/drivers/input/touchscreen/syntm12xx.c
@@ -0,0 +1,3545 @@
+/*
+ * syntm12xx.c
+ * Synaptic TM12XX touchscreen driver
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Mika Kuoppala <mika.kuoppala@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+
+#include <plat/syntm12xx.h>
+
+#define DRIVER_DESC "Synaptic TM12xx Touchscreen Driver"
+#define DRIVER_NAME "tm12xx_ts"
+
+/* Do validation of firmware image on device */
+#define FIRMWARE_VERIFY
+
+/* Number of touch points and input devices supported */
+#define MAX_TOUCH_POINTS 6
+
+#define REG_PDT_PROPERTIES 0xef
+#define REG_PAGE_SELECT 0xff
+
+#define FUNC_DEVICE_CONTROL 0x01
+#define FUNC_BIST 0x08
+#define FUNC_2D 0x11
+#define FUNC_BUTTONS 0x19
+#define FUNC_TIMER 0x32
+#define FUNC_FLASH 0x34
+#define FUNC_PROXIMITY 0x40
+
+#define MAX_FUNC_DESCS 7
+
+/* Device Control Functionality */
+#define DEVICE_CONTROL_DATA_STATUS 0x00
+#define DEVICE_CONTROL_DATA_INTR_STATUS 0x01
+
+#define DEVICE_CONTROL_STATUS_SCODE_MASK 0x0f
+#define STATUS_CODE_NO_ERROR 0x00
+#define STATUS_CODE_RESET 0x01
+#define STATUS_CODE_INVALID_CONF 0x02
+#define STATUS_CODE_DEVICE_FAILURE 0x03
+
+#define DEVICE_CONTROL_CTRL 0
+#define DEVICE_CONTROL_CONFIGURED (1 << 7)
+#define DEVICE_CONTROL_SLEEP_NORMAL 0x00
+#define DEVICE_CONTROL_SLEEP_SENSOR 0x01
+#define DEVICE_CONTROL_INTR_ENABLE 1
+#define INTR_FLASH (1 << 0)
+#define INTR_STATUS (1 << 1)
+#define INTR_BIST (1 << 2)
+#define INTR_2D (1 << 3)
+#define INTR_BUTTON (1 << 4)
+#define INTR_UADC (1 << 5)
+#define INTR_ALL 0x3f
+
+#define DEVICE_CONTROL_STATUS_FLASH_PROG (1 << 6)
+#define DEVICE_CONTROL_STATUS_UNCONFIGURED (1 << 7)
+
+#define DEVICE_CONTROL_COMMAND 0x00
+#define DEVICE_COMMAND_RESET (1 << 0)
+#define DEVICE_COMMAND_SHUTDOWN (1 << 1)
+
+#define DEVICE_CONTROL_QUERY_MANID 0
+#define DEVICE_CONTROL_QUERY_PROD_PROPERTIES 1
+#define DEVICE_CONTROL_QUERY_PROD_FAMILY 2
+#define DEVICE_CONTROL_QUERY_FW_VER 3
+#define DEVICE_CONTROL_QUERY_PROD_ID 11
+#define DEVICE_CONTROL_QUERY_PROD_ID_LAST 20
+#define PRODUCT_ID_LEN (DEVICE_CONTROL_QUERY_PROD_ID_LAST - \
+ DEVICE_CONTROL_QUERY_PROD_ID + 1)
+
+
+/* Capasitive Buttons */
+
+#define MAX_BUTTONS 31
+
+#define BUTTON_QUERY_QUERY0 0
+#define BUTTON_QUERY_BUTTON_COUNT 1
+#define BUTTON_QUERY_BUTTON_MASK 0x1f
+
+#define BUTTON_CONFIGURABLE (1 << 0)
+
+/* 2D Functionality */
+
+#define TOUCH_QUERY_NUM_SENSORS 0 /* Zero based */
+#define TOUCH_QUERY_LEN 6
+
+#define TOUCH_CONTROL_REPORT_MODE 0
+#define TOUCH_CONTROL_SENSOR_MAX_X 6
+#define TOUCH_CONTROL_SENSOR_MAX_Y 8
+#define TOUCH_CONTROL_SENSOR_MAPPING 10
+
+/* This is for now fixed for 2 touch points */
+#define TOUCH_DATA_LEN (1 + (MAX_TOUCH_POINTS * 5))
+
+#define FINGER_STATE_NOT_PRESENT 0
+#define FINGER_STATE_ACCURATE 1
+#define FINGER_STATE_INACCURATE 2
+#define FINGER_STATE_RESERVED 3
+
+/* Flashing */
+#define FLASH_MAX_SIZE (16*1024)
+#define FW_MAX_NAME_SIZE 31
+
+#define FLASH_QUERY_BOOTLOADER_ID_0 0
+#define FLASH_QUERY_BOOTLOADER_ID_1 1
+#define FLASH_QUERY_PROPERTIES 2
+#define FLASH_QUERY_BLOCK_SIZE_0 3
+#define FLASH_QUERY_BLOCK_SIZE_1 4
+#define FLASH_QUERY_FW_BLOCK_COUNT_0 5
+#define FLASH_QUERY_FW_BLOCK_COUNT_1 6
+#define FLASH_QUERY_CONF_BLOCK_COUNT_0 7
+#define FLASH_QUERY_CONF_BLOCK_COUNT_1 8
+
+/* Flash Properties */
+#define FLASH_PROPERTY_REGMAP_VERSION (1 << 0)
+
+/* Commands for FLASH_DATA_COMMAND */
+#define FLASH_CMD_IDLE 0x00
+#define FLASH_CMD_FW_CRC_BLOCK 0x01
+#define FLASH_CMD_FW_WRITE_BLOCK 0x02
+#define FLASH_CMD_ERASE_ALL 0x03
+#define FLASH_CMD_CONF_READ_BLOCK 0x05
+#define FLASH_CMD_CONF_WRITE_BLOCK 0x06
+#define FLASH_CMD_CONF_ERASE_BLOCK 0x07
+#define FLASH_CMD_PROGRAM_ENABLE 0x0f
+
+#define FLASH_ERROR_SUCCESS 0
+#define FLASH_ERROR_RESERVED 1
+#define FLASH_ERROR_NOT_ENABLED 2
+#define FLASH_ERROR_INVALID_BLOCK 3
+#define FLASH_ERROR_BLOCK_NOT_ERASED 4
+#define FLASH_ERROR_ERASE_KEY 5
+#define FLASH_ERROR_UNKNOWN 6
+#define FLASH_ERROR_RESET 7
+#define FLASH_ERROR_COUNT 8
+
+#define DEVICE_STATUS_NO_ERROR 0
+#define DEVICE_STATUS_RESET_OCCURRED 1
+#define DEVICE_STATUS_INVALID_CONFIG 2
+#define DEVICE_STATUS_DEVICE_FAILURE 3
+#define DEVICE_STATUS_CFG_CRC_FAILURE 4
+#define DEVICE_STATUS_FW_CRC_FAILURE 5
+#define DEVICE_STATUS_CRC_IN_PROGRESS 6
+#define DEVICE_STATUS_UNKNOWN 7
+#define DEVICE_STATUS_COUNT 8
+
+/* Selftest */
+#define BIST_QUERY_LIMIT_REG_COUNT 0
+#define BIST_DATA_TEST_NUMBER_CTRL 0
+#define BIST_DATA_OVERALL_RESULT 1
+#define BIST_DATA_TEST_RESULT 2
+#define BIST_CONTROL_COMMAND 0
+
+static const char *const device_status_str[DEVICE_STATUS_COUNT] = {
+ "no error",
+ "reset occurred",
+ "invalid configuration",
+ "device failure",
+ "configuration crc failure",
+ "firmware crc failure",
+ "crc in progress",
+ "unknown",
+};
+
+static const char *const flash_error_str[FLASH_ERROR_COUNT] = {
+ "success",
+ "reserved",
+ "programming not enabled",
+ "invalid block number",
+ "block not erased",
+ "erase key incorrect",
+ "unknown",
+ "device reset",
+};
+
+/* Offsets within firmware image */
+#define FW_FILE_CHECKSUM 0x0000
+#define FW_VERSION 0x0007
+#define FW_FW_SIZE 0x0008
+#define FW_CONFIG_SIZE 0x000C
+#define FW_PRODUCT_ID 0x0010
+#define FW_PRODUCT_INFO_0 0x001E
+#define FW_PRODUCT_INFO_1 0x001F
+#define FW_FW_DATA 0x0100
+
+/* How many times we try to re-initialize chip in row */
+#define MAX_FAILED_INITS 4
+
+/* How much data we can put into single write block */
+#define MAX_I2C_WRITE_BLOCK_SIZE 32
+
+#define DFLAG_VERBOSE (1 << 0)
+#define DFLAG_I2C_DUMP (1 << 1)
+
+struct syn;
+
+struct func_desc {
+ void (*intr_handler)(struct syn *sd, u8 bits);
+ u8 num;
+ u8 version;
+ u8 query;
+ u8 command;
+ u8 control;
+ u8 data;
+ u8 exists;
+
+ u8 intr_start_bit;
+ u8 intr_sources;
+};
+
+struct touch_sensor_caps {
+ unsigned is_configurable:1;
+ unsigned has_gestures:1;
+ unsigned has_abs_mode:1;
+ unsigned has_rel_mode:1;
+ unsigned finger_count:4;
+ unsigned x_electrodes:5;
+ unsigned y_electrodes:5;
+ unsigned max_electrodes:5;
+ unsigned abs_data_size:2;
+
+ /* XXX These are not yet in use
+ unsigned has_pinch:1;
+ unsigned has_press:1;
+ unsigned has_flick:1;
+ unsigned has_early_tap:1;
+ unsigned has_double_tap:1;
+ unsigned had_tap_and_hold:1;
+ unsigned has_single_tap:1;
+ unsigned has_anchored_finger:1;
+ unsigned has_palm_detect:1;
+ */
+
+ u16 max_x;
+ u16 max_y;
+};
+
+struct button_caps {
+ u8 button_count;
+};
+
+struct flash_caps {
+ u16 bootloader_id;
+ u16 block_size;
+ u16 fw_block_count;
+ u16 conf_block_count;
+ u8 properties;
+};
+
+struct fw_image {
+ u32 file_checksum;
+ u8 version;
+ u8 product_id[PRODUCT_ID_LEN + 1];
+ u8 product_info_0;
+ u8 product_info_1;
+ const u8 *fw_data;
+ u32 fw_size;
+ const u8 *config_data;
+ u32 config_size;
+ u32 config_checksum;
+};
+
+struct touch_data {
+ u16 x;
+ u16 y;
+ u8 wx;
+ u8 wy;
+ u8 z;
+ u8 finger_state;
+};
+
+struct touch_point {
+ struct input_dev *idev;
+ struct touch_data cur_td;
+ struct touch_data prev_td;
+ int num;
+};
+
+struct bist_test_result {
+ u8 failed; /* == 0, if all passed */
+ u8 result; /* specific error for failed test */
+};
+
+struct syn {
+ struct mutex lock;
+ struct i2c_client *client;
+ struct workqueue_struct *wq;
+ struct work_struct isr_work;
+
+ struct func_desc *control;
+ u8 device_control_ctrl; /* saved state */
+
+ struct func_desc *touch;
+ struct touch_point tp[MAX_TOUCH_POINTS];
+ struct touch_sensor_caps touch_caps;
+
+ struct func_desc *buttons;
+ struct button_caps button_caps;
+ u32 button_state;
+
+ struct func_desc func_desc[MAX_FUNC_DESCS];
+ unsigned func_desc_num;
+ unsigned interrupt_sources;
+
+ struct func_desc *flash;
+ struct flash_caps flash_caps;
+ struct fw_image fw_image;
+ const struct firmware *fw_entry;
+
+ struct func_desc *bist;
+ struct func_desc *timer;
+ struct func_desc *prox;
+
+ int gpio_intr;
+ int func_descs_valid;
+
+ int failed_inits;
+ u32 debug_flag;
+
+ unsigned long ts_intr;
+ unsigned long ts_work;
+ unsigned long ts_done;
+
+ unsigned long t_wakeup_min;
+ unsigned long t_wakeup_max;
+ unsigned long t_wakeup_c;
+
+ unsigned long t_work_min;
+ unsigned long t_work_max;
+ unsigned long t_work_c;
+
+ unsigned long t_wakeup;
+ unsigned long t_work;
+
+ unsigned long t_count;
+ unsigned long f_measure;
+
+ int virtualized;
+};
+
+static int syn_initialize(struct syn *sd);
+static int syn_reset_device(struct syn *sd);
+static int syn_read_func_descs(struct syn *sd);
+
+static int syn_write_block(struct syn *sd, int reg, const u8 *data, int len)
+{
+ unsigned char wb[MAX_I2C_WRITE_BLOCK_SIZE + 1];
+ struct i2c_msg msg;
+ int r;
+ int i;
+
+ if (len < 1 ||
+ len > MAX_I2C_WRITE_BLOCK_SIZE) {
+ dev_info(&sd->client->dev, "too long syn_write_block len %d\n",
+ len);
+ return -EIO;
+ }
+
+ wb[0] = reg & 0xff;
+
+ for (i = 0; i < len; i++)
+ wb[i + 1] = data[i];
+
+ msg.addr = sd->client->addr;
+ msg.flags = 0;
+ msg.len = len + 1;
+ msg.buf = wb;
+
+ r = i2c_transfer(sd->client->adapter, &msg, 1);
+
+ if (sd->debug_flag & DFLAG_I2C_DUMP) {
+ if (r == 1) {
+ for (i = 0; i < len; i++)
+ dev_info(&sd->client->dev,
+ "bw 0x%02x[%d]: 0x%02x\n",
+ reg + i, i, data[i]);
+ }
+ }
+
+ if (r == 1)
+ return 0;
+
+ return r;
+}
+
+static int syn_read_block(struct syn *sd, int reg, u8 *data, int len)
+{
+ unsigned char wb[1];
+ struct i2c_msg msg[2];
+ int r;
+
+ wb[0] = reg & 0xff;
+
+ msg[0].addr = sd->client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = wb;
+
+ msg[1].addr = msg[0].addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = len;
+ msg[1].buf = data;
+
+ r = i2c_transfer(sd->client->adapter, msg, 2);
+
+ if (sd->debug_flag & DFLAG_I2C_DUMP) {
+ if (r == 2) {
+ int i;
+
+ for (i = 0; i < len; i++)
+ dev_info(&sd->client->dev,
+ "br 0x%02x[%d]: 0x%02x\n",
+ reg + i, i, data[i]);
+ }
+ }
+
+ if (r == 2)
+ return len;
+
+ return r;
+}
+
+static int syn_read_u8(struct syn *sd, int reg)
+{
+ unsigned char b[1];
+ int r;
+
+ r = syn_read_block(sd, reg, b, 1);
+ if (r == 1)
+ return (int)b[0];
+
+ return r;
+}
+
+static int syn_write_u8(struct syn *sd, u8 reg, u8 value)
+{
+ return syn_write_block(sd, reg, &value, 1);
+}
+
+static int syn_read_u16(struct syn *sd, int reg)
+{
+ int r;
+ u8 data[2];
+
+ r = syn_read_block(sd, reg, data, 2);
+ if (r < 0)
+ return r;
+
+ return (int)data[0] | ((int)(data[1]) << 8);
+}
+
+static int syn_write_u16(struct syn *sd, int reg, u16 value)
+{
+ u8 data[2];
+
+ data[1] = (value & 0xFF00) >> 8;
+ data[0] = value & 0x00FF;
+
+ return syn_write_block(sd, reg, data, 2);
+}
+
+static int syn_control_query_read(struct syn *sd, int reg)
+{
+ return syn_read_u8(sd, sd->control->query + reg);
+}
+
+static int syn_control_data_read(struct syn *sd, int reg)
+{
+ return syn_read_u8(sd, sd->control->data + reg);
+}
+
+static const char *syn_device_status_str(u8 dev_status)
+{
+ if (dev_status >= DEVICE_STATUS_COUNT)
+ return device_status_str[DEVICE_STATUS_UNKNOWN];
+
+ return device_status_str[dev_status];
+}
+
+static int syn_get_nosleep(struct syn *sd)
+{
+ int r;
+
+ r = syn_read_u8(sd, sd->control->control + DEVICE_CONTROL_CTRL);
+ if (r < 0)
+ return r;
+
+ return (r & 0x04) >> 2;
+}
+
+static int syn_set_nosleep(struct syn *sd, int val)
+{
+ int r;
+
+ r = syn_read_u8(sd, sd->control->control + DEVICE_CONTROL_CTRL);
+ if (r < 0)
+ return r;
+
+ val = (r & (~0x04)) | ((!!val) << 2);
+
+ r = syn_write_u8(sd, sd->control->control + DEVICE_CONTROL_CTRL,
+ val);
+
+ return r;
+}
+
+static int syn_get_proximity_state(struct syn *sd)
+{
+ int r;
+
+ if (!sd->prox)
+ return -ENODEV;
+
+ r = syn_read_u8(sd, sd->prox->control);
+ if (r < 0)
+ return r;
+
+ return (r & (1 << 7)) == 0;
+}
+
+static int syn_set_proximity_state(struct syn *sd, int val)
+{
+ int r;
+
+ if (!sd->prox)
+ return -ENODEV;
+
+ r = syn_read_u8(sd, sd->prox->control);
+ if (r < 0)
+ return r;
+
+ /* It is called 'inhibit proximity' in register */
+ val = (r & (~0x80)) | ((!val) << 7);
+
+ r = syn_write_u8(sd, sd->prox->control,
+ val);
+ if (r < 0)
+ return r;
+
+ return r;
+}
+
+static void syn_report_device_status(struct syn *sd, u8 dev_status)
+{
+ struct device *dev = &sd->client->dev;
+
+ if (dev_status & (1 << 7))
+ dev_info(dev, "device is unconfigured\n");
+
+ if (dev_status & (1 << 6))
+ dev_info(dev, "device is in flashing mode\n");
+
+ dev_info(dev, "device status: 0x%x, %s\n", dev_status,
+ syn_device_status_str(dev_status & 0x0f));
+}
+
+static void syn_device_change_state(struct syn *sd, u8 dev_status)
+{
+ int r;
+
+ /* Unconfigured or reset, we initialize*/
+ if ((dev_status & (1 << 7)) ||
+ (dev_status & 0x0f) == DEVICE_STATUS_RESET_OCCURRED) {
+ r = syn_initialize(sd);
+ if (r)
+ dev_err(&sd->client->dev,
+ "error %d in syn_initialize\n", r);
+ }
+}
+
+static void syn_recalculate_latency_data(struct syn *sd)
+{
+ if (sd->ts_intr <= sd->ts_work && sd->ts_work < sd->ts_done) {
+ sd->t_wakeup = sd->ts_work - sd->ts_intr;
+ do_div(sd->t_wakeup, 1000);
+ sd->t_work = sd->ts_done - sd->ts_work;
+ do_div(sd->t_work, 1000);
+
+ if (sd->t_wakeup > sd->t_wakeup_max)
+ sd->t_wakeup_max = sd->t_wakeup;
+
+ if (sd->t_wakeup < sd->t_wakeup_min)
+ sd->t_wakeup_min = sd->t_wakeup;
+
+ if (sd->t_work > sd->t_work_max)
+ sd->t_work_max = sd->t_work;
+
+ if (sd->t_work < sd->t_work_min)
+ sd->t_work_min = sd->t_work;
+
+ if ((sd->t_work_c + sd->t_work) < sd->t_work_c ||
+ (sd->t_wakeup_c + sd->t_wakeup) < sd->t_wakeup_c) {
+
+ /* Wrap */
+ sd->t_work_c = sd->t_work;
+ sd->t_wakeup_c = sd->t_wakeup;
+ sd->t_count = 0;
+ } else {
+ sd->t_work_c += sd->t_work;
+ sd->t_wakeup_c += sd->t_wakeup;
+ sd->t_count++;
+ }
+ } else {
+ /* Time wrap or something else, discard */
+ }
+}
+
+static void syn_isr_device_control(struct syn *sd, u8 bits)
+{
+ int dev_status;
+
+ dev_status = syn_control_data_read(sd, DEVICE_CONTROL_DATA_STATUS);
+ if (dev_status < 0) {
+ dev_err(&sd->client->dev, "error %d reading device status\n",
+ dev_status);
+ }
+
+ syn_report_device_status(sd, dev_status);
+
+ syn_device_change_state(sd, dev_status);
+}
+
+static void syn_isr_flash(struct syn *sd, u8 bits)
+{
+ int dev_status;
+
+ dev_info(&sd->client->dev, "flash interrupt\n");
+
+ if (!sd->flash) {
+ dev_warn(&sd->client->dev,
+ "flash interrupt without registered functionality\n");
+ return;
+ }
+
+ dev_status = syn_control_data_read(sd, DEVICE_CONTROL_DATA_STATUS);
+ syn_report_device_status(sd, dev_status);
+
+ syn_device_change_state(sd, dev_status);
+}
+
+static void syn_isr_timer(struct syn *sd, u8 bits)
+{
+ if (!sd->timer) {
+ dev_warn(&sd->client->dev,
+ "flash interrupt without registered functionality\n");
+ return;
+ }
+}
+
+static void syn_isr_bist(struct syn *sd, u8 bits)
+{
+ if (!sd->bist) {
+ dev_warn(&sd->client->dev,
+ "bist interrupt without registered functionality\n");
+ return;
+ }
+}
+
+static void syn_isr_proximity(struct syn *sd, u8 bits)
+{
+ unsigned char data[6];
+ struct input_dev *idev;
+ int r;
+
+ idev = sd->tp[0].idev;
+
+ if (!sd->prox) {
+ dev_warn(&sd->client->dev,
+ "spurious proximity interrupty\n");
+ return;
+ }
+
+ r = syn_read_block(sd, sd->prox->data, data, 6);
+ if (r < 0) {
+ dev_warn(&sd->client->dev,
+ "error %d reading proximity data\n", r);
+ return;
+ }
+
+ if (data[0] & 0x01) {
+ input_report_abs(idev, ABS_RZ, data[1]);
+ input_report_abs(idev, ABS_HAT0X, data[2]); /* BH */
+ input_report_abs(idev, ABS_HAT0Y, data[4]); /* LH */
+ input_report_abs(idev, ABS_HAT1X, data[3]); /* TH */
+ input_report_abs(idev, ABS_HAT1Y, data[5]); /* RH */
+ } else
+ input_report_abs(idev, ABS_RZ, 0);
+
+ input_sync(idev);
+}
+
+static void syn_touch_parse_one_touch_data(struct syn *sd, struct touch_data *p,
+ u8 finger_state, const u8 *d)
+{
+ p->finger_state = finger_state;
+ p->x = (u16)(d[2] & 0x0f) | (u16)(d[0] << 4);
+ p->y = ((u16)(d[2] & 0xf0) >> 4) | (u16)(d[1] << 4);
+ p->wx = d[3] & 0x0f;
+ p->wy = (d[3] & 0xf0) >> 4;
+ p->z = d[4];
+}
+
+static void syn_touch_parse_touch_data(struct syn *sd, const u8 *d)
+{
+ unsigned i;
+ u8 fc;
+ u8 fs;
+ struct touch_data *p;
+
+ fc = sd->touch_caps.finger_count;
+
+ if (fc > MAX_TOUCH_POINTS)
+ dev_err(&sd->client->dev, "error in finger count %d\n", fc);
+
+ for (i = 0; i < fc; i++) {
+ p = &sd->tp[i].cur_td;
+
+ fs = (d[(i >> 2)] >> ((i % 4) << 1)) & 0x03;
+
+ /* dev_warn(&sd->client->dev, "fs[%d] = %d\n", i, fs); */
+
+ syn_touch_parse_one_touch_data(sd, p, fs,
+ d + ((fc >> 2) + 1) +
+ i * 5);
+
+ }
+}
+
+static int syn_touch_get_data(struct syn *sd)
+{
+ int r;
+ u8 data[TOUCH_DATA_LEN];
+
+ r = syn_read_block(sd, sd->touch->data, data, TOUCH_DATA_LEN);
+ if (r < 0)
+ return r;
+
+ if (r != TOUCH_DATA_LEN)
+ return -EIO;
+
+ syn_touch_parse_touch_data(sd, data);
+
+ return 0;
+}
+
+static void syn_touch_report_data(struct syn *sd, int dev_number)
+{
+ struct tm12xx_ts_platform_data *pdata = sd->client->dev.platform_data;
+ struct input_dev *idev;
+ struct touch_data *d;
+ struct touch_data *p;
+
+ idev = sd->tp[dev_number].idev;
+ d = &sd->tp[dev_number].cur_td;
+ p = &sd->tp[dev_number].prev_td;
+
+ if (memcmp(p, d, sizeof(struct touch_data)) == 0)
+ return;
+
+ memcpy(p, d, sizeof(struct touch_data));
+
+ switch (d->finger_state) {
+
+ case FINGER_STATE_ACCURATE:
+ input_report_key(idev, BTN_TOUCH, 1);
+ input_report_key(idev, BTN_MODE, 0);
+ break;
+
+ case FINGER_STATE_INACCURATE:
+ input_report_key(idev, BTN_TOUCH, 1);
+ input_report_key(idev, BTN_MODE, 1);
+ break;
+
+ case FINGER_STATE_RESERVED:
+ /* Intentional fall thru */
+
+ case FINGER_STATE_NOT_PRESENT:
+ input_report_key(idev, BTN_MODE, 0);
+ input_report_key(idev, BTN_TOUCH, 0);
+ goto out;
+
+ default:
+ dev_err(&sd->client->dev, "unknown finger state 0x%x\n",
+ d->finger_state);
+ goto out;
+ }
+
+ if (sd->virtualized && pdata->swap_xy &&
+ dev_name(&sd->client->dev)[0] != '2')
+ d->x = sd->touch_caps.max_x + d->x;
+
+ if (pdata->swap_xy) {
+ input_report_abs(idev, ABS_X, d->y);
+ input_report_abs(idev, ABS_Y, d->x);
+ input_report_abs(idev, ABS_TOOL_WIDTH, d->wy);
+ } else {
+ input_report_abs(idev, ABS_X, d->x);
+ input_report_abs(idev, ABS_Y, d->y);
+ input_report_abs(idev, ABS_TOOL_WIDTH, d->wx);
+ }
+
+ input_report_abs(idev, ABS_Z, d->z);
+ input_report_abs(idev, ABS_VOLUME, d->wx * d->wy);
+
+out:
+ input_sync(idev);
+}
+
+static void syn_isr_2d(struct syn *sd, u8 bits)
+{
+ int r;
+ int i;
+
+ if (!sd->touch) {
+ dev_warn(&sd->client->dev,
+ "2d interrupt without registered functionality\n");
+ return;
+ }
+
+ r = syn_touch_get_data(sd);
+ if (r) {
+ dev_err(&sd->client->dev, "error getting touch data\n");
+ return;
+ }
+
+ if (sd->debug_flag & DFLAG_VERBOSE) {
+ dev_info(&sd->client->dev,
+ "1: state=0x%x, x=%d, y=%d, z=%d, wx=%d, wy=%d\n",
+ sd->tp[0].cur_td.finger_state,
+ sd->tp[0].cur_td.x, sd->tp[0].cur_td.y,
+ sd->tp[0].cur_td.z, sd->tp[0].cur_td.wx,
+ sd->tp[0].cur_td.wy);
+ dev_info(&sd->client->dev,
+ "2: state=0x%x, x=%d, y=%d, z=%d, wx=%d, wy=%d\n",
+ sd->tp[1].cur_td.finger_state,
+ sd->tp[1].cur_td.x, sd->tp[1].cur_td.y,
+ sd->tp[1].cur_td.z, sd->tp[1].cur_td.wx,
+ sd->tp[1].cur_td.wy);
+ }
+
+ for (i = 0; i < sd->touch_caps.finger_count; i++)
+ syn_touch_report_data(sd, i);
+}
+
+static int syn_button_report(struct syn *sd, unsigned button_nr, const int val)
+{
+ struct tm12xx_ts_platform_data *pdata = sd->client->dev.platform_data;
+ unsigned max_buttons;
+
+ max_buttons = pdata->num_buttons;
+
+ if (button_nr >= max_buttons)
+ return -EINVAL;
+
+ input_report_key(sd->tp[0].idev, pdata->button_map[button_nr], val);
+ input_sync(sd->tp[0].idev);
+
+ return 0;
+}
+
+static void syn_isr_buttons(struct syn *sd, u8 bits)
+{
+ int data_reg_count = (sd->button_caps.button_count + 7) / 8;
+ int i = 0;
+ int r;
+ u32 state = 0;
+ u32 last_state;
+
+ if (!sd->buttons) {
+ dev_warn(&sd->client->dev,
+ "button interrupt without registered functionality\n");
+ return;
+ }
+
+ if (data_reg_count > MAX_BUTTONS/8 + 1)
+ data_reg_count = MAX_BUTTONS/8 + 1;
+
+ while (i < data_reg_count) {
+ r = syn_read_u8(sd, sd->buttons->data + i);
+ if (r < 0) {
+ dev_err(&sd->client->dev,
+ "error reading button state\n");
+ return;
+ }
+
+ state |= (u32)r << (i * 8);
+ i++;
+ }
+
+ last_state = sd->button_state;
+
+ for (i = 0; i < sd->button_caps.button_count; i++) {
+ const u32 mask = (1 << i);
+ if ((state & mask) ^ (last_state & mask)) {
+ r = syn_button_report(sd, i, state & mask ? 1 : 0);
+ if (r) {
+ dev_warn(&sd->client->dev,
+ "error reporting button "
+ "(no mapping for %d ?)\n", i);
+ }
+ }
+ }
+
+ sd->button_state = state;
+}
+
+static u8 syn_get_status_bits(u8 *int_status, int start, int bits)
+{
+ const u16 mask = ((1 << bits) - 1) << start;
+ u8 val;
+
+ val = (*int_status & mask) >> start;
+
+ *int_status &= ~mask;
+
+ return val;
+}
+
+static void syn_isr_call_handlers(struct syn *sd, u8 int_status)
+{
+ int i;
+ u8 bits;
+
+ for (i = 0; i < sd->func_desc_num; i++) {
+ struct func_desc * const f = &sd->func_desc[i];
+
+ if (!int_status)
+ return;
+
+ bits = syn_get_status_bits(&int_status, f->intr_start_bit,
+ f->intr_sources);
+
+ if (likely(bits)) {
+ if (likely(f->intr_handler))
+ f->intr_handler(sd, bits);
+ else {
+ if (printk_ratelimit())
+ dev_warn(&sd->client->dev,
+ "no intr handler for %d\n", i);
+ }
+ }
+ }
+
+ if (unlikely(int_status))
+ dev_warn(&sd->client->dev,
+ "unhandled attentions: 0x%x\n", int_status);
+}
+
+static void syn_clear_device_state(struct syn *sd)
+{
+ int i;
+
+ sd->control = NULL;
+
+ sd->touch = NULL;
+ /*
+ * We don't clear previous exported devices.
+ * If after firmware upgrade there would be different
+ * amount of touchpoints rmmod/insmod cycle is needed.
+ */
+ memset(&sd->touch_caps, 0, sizeof(struct touch_sensor_caps));
+
+ sd->buttons = NULL;
+ memset(&sd->button_caps, 0, sizeof(struct button_caps));
+ sd->button_state = 0;
+
+ for (i = 0; i < MAX_FUNC_DESCS; i++)
+ memset(&sd->func_desc[i], 0, sizeof(struct func_desc));
+
+ sd->func_desc_num = 0;
+ sd->interrupt_sources = 0;
+
+ sd->flash = NULL;
+ memset(&sd->flash_caps, 0, sizeof(struct flash_caps));
+ memset(&sd->fw_image, 0, sizeof(struct fw_image));
+
+ sd->bist = NULL;
+}
+
+static void syn_isr_work(struct work_struct *work)
+{
+ int is;
+ int r;
+ struct syn *sd = container_of(work, struct syn, isr_work);
+
+ mutex_lock(&sd->lock);
+
+ if (sd->f_measure) {
+ sd->ts_work = cpu_clock(get_cpu());
+ put_cpu();
+ }
+
+ if (sd->func_descs_valid == 0) {
+ if (sd->failed_inits < MAX_FAILED_INITS) {
+ r = syn_initialize(sd);
+ if (r) {
+ dev_err(&sd->client->dev,
+ "error initializing\n");
+ goto out;
+ }
+ }
+ }
+
+ is = syn_control_data_read(sd, DEVICE_CONTROL_DATA_INTR_STATUS);
+
+ if (unlikely(is < 0)) {
+ dev_err(&sd->client->dev,
+ "unable to read intr status\n");
+ syn_reset_device(sd);
+ goto out;
+ }
+
+ if (likely(is))
+ syn_isr_call_handlers(sd, is);
+
+out:
+ if (sd->f_measure) {
+ sd->ts_done = cpu_clock(get_cpu());
+ put_cpu();
+ syn_recalculate_latency_data(sd);
+ sd->f_measure = 0;
+ }
+ enable_irq(gpio_to_irq(sd->gpio_intr));
+ mutex_unlock(&sd->lock);
+}
+
+static irqreturn_t syn_isr(int irq, void *data)
+{
+ struct syn *sd = data;
+
+ if (sd->wq != NULL) {
+ int r;
+ r = queue_work(sd->wq, &sd->isr_work);
+ if (r) {
+ if (sd->f_measure == 0) {
+ sd->ts_intr = cpu_clock(get_cpu());
+ put_cpu();
+ sd->f_measure = 1;
+ }
+ disable_irq_nosync(gpio_to_irq(sd->gpio_intr));
+ }
+
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int syn_set_input_dev_params(struct syn *sd, int dev_number)
+{
+ struct input_dev *dev;
+ struct tm12xx_ts_platform_data *pdata;
+ int i;
+
+ if (!sd || dev_number < 0 || dev_number >= MAX_TOUCH_POINTS)
+ return -EINVAL;
+
+ dev = sd->tp[dev_number].idev;
+ if (!dev)
+ return -ENODEV;
+
+ pdata = sd->client->dev.platform_data;
+
+ set_bit(EV_KEY, dev->evbit);
+ set_bit(EV_ABS, dev->evbit);
+
+ set_bit(BTN_TOUCH, dev->keybit);
+ set_bit(BTN_MODE, dev->keybit);
+
+ set_bit(ABS_X, dev->absbit);
+ set_bit(ABS_Y, dev->absbit);
+ set_bit(ABS_Z, dev->absbit);
+ set_bit(ABS_VOLUME, dev->absbit);
+ set_bit(ABS_TOOL_WIDTH, dev->absbit);
+
+ if (dev_number == 0) {
+ set_bit(ABS_RZ, dev->absbit);
+ set_bit(ABS_HAT0X, dev->absbit);
+ set_bit(ABS_HAT0Y, dev->absbit);
+ set_bit(ABS_HAT1X, dev->absbit);
+ set_bit(ABS_HAT1Y, dev->absbit);
+ }
+
+ if (pdata->repeat)
+ set_bit(EV_REP, dev->evbit);
+
+ for (i = 0; i < pdata->num_buttons; i++)
+ set_bit(pdata->button_map[i], dev->keybit);
+
+ if (pdata->swap_xy) {
+ input_set_abs_params(dev, ABS_X, 0, sd->touch_caps.max_y, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 0, sd->touch_caps.max_x, 0, 0);
+ } else {
+ input_set_abs_params(dev, ABS_X, 0, sd->touch_caps.max_x, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 0, sd->touch_caps.max_y, 0, 0);
+ }
+
+ input_set_abs_params(dev, ABS_Z, 0, 255, 0, 0);
+ input_set_abs_params(dev, ABS_VOLUME, 0, 225, 0, 0);
+ input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
+
+ if (dev_number == 0) {
+ input_set_abs_params(dev, ABS_RZ, 0, 255, 0, 0);
+ input_set_abs_params(dev, ABS_HAT0X, 0, 255, 0, 0);
+ input_set_abs_params(dev, ABS_HAT0Y, 0, 255, 0, 0);
+ input_set_abs_params(dev, ABS_HAT1X, 0, 255, 0, 0);
+ input_set_abs_params(dev, ABS_HAT1Y, 0, 255, 0, 0);
+ }
+
+ dev->dev.parent = &sd->client->dev;
+
+ return 0;
+}
+
+static int syn_register_input_devices(struct syn *sd, int num_devices)
+{
+ struct tm12xx_ts_platform_data *pdata;
+ int r;
+ int i;
+ int j;
+
+ if (!sd || num_devices < 0 || num_devices > MAX_TOUCH_POINTS)
+ return -EINVAL;
+
+ pdata = sd->client->dev.platform_data;
+
+ /* After firmware upgrade no need to reregister */
+ if (sd->tp[0].idev != NULL) {
+ if (sd->debug_flag & DFLAG_VERBOSE)
+ dev_info(&sd->client->dev,
+ "input device already existing\n");
+ return 0;
+ }
+
+ for (i = 0; i < num_devices; i++) {
+ sd->tp[i].idev = input_allocate_device();
+ if (!sd->tp[i].idev) {
+ dev_err(&sd->client->dev,
+ "not enough memory for input device %d\n", i);
+ r = -ENOMEM;
+ goto err_alloc;
+ }
+ }
+
+ for (i = 0; i < num_devices; i++) {
+ r = syn_set_input_dev_params(sd, i);
+ if (r != 0)
+ goto err_alloc;
+
+ if (pdata->idev_name[i])
+ sd->tp[i].idev->name = pdata->idev_name[i];
+ else {
+ dev_err(&sd->client->dev,
+ "no input device name. check platform data\n");
+ goto err_alloc;
+ }
+ }
+
+ /* We use j in the err_alloc to unregister */
+ for (j = 0; j < num_devices; j++) {
+ r = input_register_device(sd->tp[j].idev);
+ if (r) {
+ dev_err(&sd->client->dev,
+ "failed to register input device %d\n", j);
+ goto err_register;
+ }
+ }
+
+ return 0;
+
+err_register:
+ for (i = 0; i < j; i++)
+ input_unregister_device(sd->tp[i].idev);
+err_alloc:
+ for (i = 0; i < num_devices; i++) {
+ if (sd->tp[i].idev) {
+ input_free_device(sd->tp[i].idev);
+ sd->tp[i].idev = NULL;
+ }
+ }
+
+ return r;
+}
+
+static struct func_desc *syn_get_func_desc(struct syn *sd, int func)
+{
+ int i;
+
+ for (i = 0; i < sd->func_desc_num; i++) {
+ struct func_desc * const f = &sd->func_desc[i];
+
+ if (f->num == func)
+ return f;
+ }
+
+ return NULL;
+}
+
+static int syn_register_intr_handler(struct syn *sd, int func,
+ void (*h)(struct syn *sd, u8 bits))
+{
+ struct func_desc * const f = syn_get_func_desc(sd, func);
+
+ if (!f)
+ return -EINVAL;
+
+ if (f->intr_handler == NULL) {
+ f->intr_handler = h;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Flashing support
+ */
+static int syn_flash_query_caps(struct syn *sd)
+{
+ int r;
+
+ r = syn_read_u16(sd, sd->flash->query + FLASH_QUERY_BOOTLOADER_ID_0);
+ if (r < 0)
+ return r;
+
+ sd->flash_caps.bootloader_id = r;
+
+ r = syn_read_u8(sd, sd->flash->query + FLASH_QUERY_PROPERTIES);
+ if (r < 0)
+ return r;
+
+ sd->flash_caps.properties = r;
+
+ r = syn_read_u16(sd, sd->flash->query + FLASH_QUERY_BLOCK_SIZE_0);
+ if (r < 0)
+ return r;
+
+ sd->flash_caps.block_size = r;
+
+ r = syn_read_u16(sd, sd->flash->query + FLASH_QUERY_FW_BLOCK_COUNT_0);
+ if (r < 0)
+ return r;
+
+ sd->flash_caps.fw_block_count = r;
+
+ r = syn_read_u16(sd, sd->flash->query + FLASH_QUERY_CONF_BLOCK_COUNT_0);
+ if (r < 0)
+ return r;
+
+ sd->flash_caps.conf_block_count = r;
+
+ return 0;
+}
+
+static u32 syn_crc_fletcher32(const u16 *data, unsigned int len)
+{
+ u32 sum1 = 0xffff;
+ u32 sum2 = 0xffff;
+
+ while (len--) {
+ sum1 += *data++;
+ sum2 += sum1;
+
+ sum1 = (sum1 & 0xffff) + (sum1 >> 16);
+ sum2 = (sum2 & 0xffff) + (sum2 >> 16);
+ }
+
+ return sum1 | (sum2 << 16);
+}
+
+static int syn_fw_read_bytes(struct syn *sd, u8 *b, int offset, int len)
+{
+ if (offset + len > sd->fw_entry->size) {
+ dev_err(&sd->client->dev,
+ "fw_read_bytes overflow on offset %d\n", offset);
+ return -EINVAL;
+ }
+
+ memcpy(b, sd->fw_entry->data + offset, len);
+
+ return len;
+}
+
+static int syn_fw_read_u32(struct syn *sd, u32 *d, int offset)
+{
+ const u8 *l;
+
+ if (offset + 4 > sd->fw_entry->size) {
+ dev_err(&sd->client->dev,
+ "fw_read_u32 overflow on offset %d\n", offset);
+
+ return -EINVAL;
+ }
+
+ l = sd->fw_entry->data + offset;
+
+ *d = l[0] | (l[1] << 8) | (l[2] << 16) | (l[3] << 24);
+
+ return 4;
+}
+
+static int syn_request_firmware(struct syn *sd, const char *filename)
+{
+ int r;
+
+ r = request_firmware(&sd->fw_entry, filename, &sd->client->dev);
+
+ return r;
+}
+
+static int syn_check_firmware(struct syn *sd)
+{
+ u32 calc_file_crc;
+ u32 calc_config_crc;
+ struct fw_image *d = &sd->fw_image;
+ int r;
+
+ /*
+ * Restrict sizes.
+ * Absolute minimum is zero sized fw data and config
+ * areas.
+ */
+
+ if (sd->fw_entry->size > FLASH_MAX_SIZE ||
+ sd->fw_entry->size < FW_FW_DATA + 4) {
+ dev_err(&sd->client->dev, "illegal firmware size\n");
+ return -EINVAL;
+ }
+
+ r = syn_fw_read_u32(sd, &d->file_checksum, FW_FILE_CHECKSUM);
+ if (r < 0)
+ return r;
+
+ r = syn_fw_read_bytes(sd, &d->version, FW_VERSION, 1);
+ if (r < 0)
+ return r;
+
+ r = syn_fw_read_u32(sd, &d->fw_size, FW_FW_SIZE);
+ if (r < 0)
+ return r;
+
+ r = syn_fw_read_u32(sd, &d->config_size, FW_CONFIG_SIZE);
+ if (r < 0)
+ return r;
+
+ if ((d->fw_size + d->config_size + FW_FW_DATA) > sd->fw_entry->size) {
+ dev_err(&sd->client->dev, "fw size mismatch\n");
+ return -EINVAL;
+ }
+
+ /* These have to be u32 aligned */
+ if ((d->fw_size & 0x0f) || (d->config_size & 0x0f)) {
+ dev_err(&sd->client->dev, "fw areas not aligned to 4 bytes\n");
+ return -EINVAL;
+ }
+
+ r = syn_fw_read_bytes(sd, d->product_id, FW_PRODUCT_ID,
+ PRODUCT_ID_LEN);
+ if (r < 10)
+ return r;
+
+ r = syn_fw_read_bytes(sd, &d->product_info_0,
+ FW_PRODUCT_INFO_0, 1);
+ if (r < 0)
+ return r;
+
+ r = syn_fw_read_bytes(sd, &d->product_info_1,
+ FW_PRODUCT_INFO_1, 1);
+ if (r < 0)
+ return r;
+
+ r = syn_fw_read_u32(sd, &d->config_checksum,
+ d->fw_size + FW_FW_DATA + d->config_size - 4);
+ if (r < 0)
+ return r;
+
+ d->fw_data = sd->fw_entry->data + FW_FW_DATA;
+ d->config_data = sd->fw_entry->data + FW_FW_DATA + d->fw_size;
+
+ calc_file_crc = syn_crc_fletcher32((u16 *)(sd->fw_entry->data + 4),
+ (sd->fw_entry->size - 4) >> 1);
+
+ calc_config_crc = syn_crc_fletcher32((u16 *)(d->config_data),
+ (d->config_size - 4) >> 1);
+
+ if (calc_file_crc != d->file_checksum) {
+ dev_err(&sd->client->dev, "fw file crc failed\n");
+ return -EINVAL;
+ }
+
+ if (calc_config_crc != d->config_checksum) {
+ dev_err(&sd->client->dev, "fw config area crc failed\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const char *syn_flash_error_str(u8 flash_error)
+{
+ if (flash_error >= FLASH_ERROR_COUNT)
+ return flash_error_str[FLASH_ERROR_UNKNOWN];
+
+ return flash_error_str[flash_error];
+}
+
+static u8 syn_flash_error(int flash_status)
+{
+ return ((u8)(flash_status) >> 4) & 0x07;
+}
+
+/*
+ * There are two different ways the flash register are organized
+ */
+static u8 syn_flash_data_command_offset(struct syn *sd)
+{
+ if (sd->flash_caps.properties & FLASH_PROPERTY_REGMAP_VERSION)
+ return 2 + sd->flash_caps.block_size;
+ else
+ return 0;
+}
+
+static u8 syn_flash_block_data_offset(struct syn *sd)
+{
+ if (sd->flash_caps.properties & FLASH_PROPERTY_REGMAP_VERSION)
+ return 2;
+ else
+ return 3;
+}
+
+static u8 syn_flash_block_num_offset(struct syn *sd)
+{
+ if (sd->flash_caps.properties & FLASH_PROPERTY_REGMAP_VERSION)
+ return 0;
+ else
+ return 1;
+}
+
+static int syn_flash_status(struct syn *sd)
+{
+ return syn_read_u8(sd, sd->flash->data +
+ syn_flash_data_command_offset(sd));
+}
+
+static int syn_wait_for_attn(struct syn *sd, unsigned long timeout_usecs,
+ int state)
+{
+ const unsigned long one_wait = 20;
+ unsigned long waited = 0;
+ int r;
+
+ do {
+ r = gpio_get_value(sd->gpio_intr);
+ if (r == state)
+ break;
+
+ udelay(one_wait);
+ waited += one_wait;
+ } while (waited < timeout_usecs);
+
+ if (waited > 500000 ||
+ waited >= timeout_usecs ||
+ (sd->debug_flag & DFLAG_VERBOSE))
+ dev_info(&sd->client->dev, "waited %lu usecs for attn\n",
+ waited);
+
+ return r == state ? 0 : -1;
+}
+
+static int syn_flash_command(struct syn *sd, u8 flash_command)
+{
+ int r;
+ int s;
+
+ if (flash_command & 0xf0)
+ return -EINVAL;
+
+ r = syn_wait_for_attn(sd, 200 * 1000, 1);
+ if (r) {
+ dev_err(&sd->client->dev,
+ "timeout: attn didn't clear for 200 ms\n");
+ return r;
+ }
+
+ r = syn_write_u8(sd,
+ sd->flash->data + syn_flash_data_command_offset(sd),
+ flash_command);
+
+ if (r) {
+ dev_err(&sd->client->dev, "flash command error %d\n", r);
+ return r;
+ }
+
+ r = syn_wait_for_attn(sd, 700 * 1000, 0);
+ if (r) {
+ dev_err(&sd->client->dev,
+ "timeout attn didn't assert for 700 ms\n");
+ }
+
+ s = syn_control_data_read(sd, DEVICE_CONTROL_DATA_INTR_STATUS);
+ if (s < 0)
+ dev_err(&sd->client->dev, "error reading int status\n");
+
+ if (sd->debug_flag & DFLAG_VERBOSE) {
+ if (s != 0x01)
+ dev_info(&sd->client->dev,
+ "wait for attn intr status 0x%x\n", s);
+ }
+
+ return r == 0 ? 0 : -1;
+}
+
+static int syn_flash_enable(struct syn *sd)
+{
+ int r;
+
+ r = syn_control_data_read(sd, DEVICE_CONTROL_DATA_STATUS);
+ if (r < 0)
+ return r;
+
+ if (r & (1 << 6)) {
+ dev_err(&sd->client->dev, "flashing mode already enabled\n");
+ return 0;
+ }
+
+ r = syn_write_u16(sd, sd->flash->data + syn_flash_block_data_offset(sd),
+ sd->flash_caps.bootloader_id);
+ if (r < 0)
+ return r;
+
+ r = syn_flash_command(sd, FLASH_CMD_PROGRAM_ENABLE);
+ if (r < 0)
+ return r;
+
+ r = syn_read_func_descs(sd);
+ if (r < 0)
+ return r;
+
+ r = syn_flash_status(sd);
+ if (r < 0) {
+ dev_info(&sd->client->dev, "flash error in enable %s\n",
+ syn_flash_error_str(syn_flash_error(r)));
+ return r;
+ }
+
+ if (syn_flash_error(r) != FLASH_ERROR_SUCCESS &&
+ syn_flash_error(r) != FLASH_ERROR_RESET) {
+ dev_err(&sd->client->dev, "flash error: %s\n",
+ syn_flash_error_str(syn_flash_error(r)));
+
+ return -EINVAL;
+ }
+
+ if (!(r & 0x80)) {
+ dev_err(&sd->client->dev, "flashing not enabled 0x%02x\n", r);
+ return -EINVAL;
+ }
+
+ return r;
+}
+
+static int syn_flash_write_block_cmd_old(struct syn *sd, const u8 *data,
+ int blocks, u8 flash_command)
+{
+ int block;
+ int r;
+ const u8 block_num_offset = syn_flash_block_num_offset(sd);
+ const u8 block_data_offset = syn_flash_block_data_offset(sd);
+
+ for (block = 0; block < blocks; block++) {
+ if (!(block % 10))
+ dev_info(&sd->client->dev, "0x%x writing block %d\n",
+ flash_command, block);
+
+ r = syn_write_u16(sd, sd->flash->data + block_num_offset,
+ block);
+ if (r < 0)
+ return r;
+
+ r = syn_write_block(sd, sd->flash->data + block_data_offset,
+ data + (block * sd->flash_caps.block_size),
+ sd->flash_caps.block_size);
+ if (r < 0)
+ return r;
+
+ r = syn_flash_command(sd, flash_command);
+ if (r < 0)
+ return r;
+
+ r = syn_flash_status(sd);
+ if (r < 0)
+ return r;
+
+ if (r != 0x80) {
+ dev_err(&sd->client->dev,
+ "flash_write error : %s\n",
+ syn_flash_error_str(syn_flash_error(r)));
+ return syn_flash_error(r);
+ }
+ }
+
+ return 0;
+}
+
+static int syn_flash_write_block_cmd_fast(struct syn *sd, const u8 *data,
+ int blocks, u8 flash_command)
+{
+ const u16 block_size = sd->flash_caps.block_size;
+ u8 *d;
+ u16 block;
+ int r;
+
+ d = kmalloc(block_size + 3, GFP_KERNEL);
+ if (d == NULL)
+ return -ENOMEM;
+
+ for (block = 0; block < blocks; block++) {
+ if (!(block % 10))
+ dev_info(&sd->client->dev,
+ "0x%x fast writing block %d\n",
+ flash_command, block);
+ d[0] = block & 0xff;
+ d[1] = (block & 0xff00) >> 8;
+ memcpy(&d[2], data + (block * block_size), block_size);
+ d[2 + block_size] = flash_command;
+
+ r = syn_wait_for_attn(sd, 200 * 1000, 1);
+ if (r) {
+ dev_err(&sd->client->dev,
+ "timeout: attn didn't clear for 200 ms\n");
+ goto out;
+ }
+
+ r = syn_write_block(sd, sd->flash->data,
+ d, block_size + 3);
+
+
+ r = syn_wait_for_attn(sd, 700 * 1000, 0);
+ if (r) {
+ dev_err(&sd->client->dev,
+ "timeout attn didn't assert for 700 ms\n");
+ goto out;
+ }
+
+ /* We need to do this read to release ATTN */
+ syn_control_data_read(sd, DEVICE_CONTROL_DATA_INTR_STATUS);
+ }
+out:
+ kfree(d);
+
+ return r;
+}
+
+static int syn_flash_write_block_cmd(struct syn *sd, const u8 *data,
+ int blocks, u8 flash_command)
+{
+ if (sd->flash_caps.properties & FLASH_PROPERTY_REGMAP_VERSION)
+ return syn_flash_write_block_cmd_fast(sd, data,
+ blocks, flash_command);
+ else
+ return syn_flash_write_block_cmd_old(sd, data,
+ blocks, flash_command);
+}
+
+static int syn_flash_read_config(struct syn *sd, u8 *d)
+{
+ const u8 flash_command = FLASH_CMD_CONF_READ_BLOCK;
+ const u8 block_num_offset = syn_flash_block_num_offset(sd);
+ const u8 block_data_offset = syn_flash_block_data_offset(sd);
+ const int blocks = sd->fw_image.config_size / sd->flash_caps.block_size;
+ int i;
+ int r;
+
+ for (i = 0; i < blocks; i++) {
+ if (!(i % 10))
+ dev_info(&sd->client->dev,
+ "0x%x reading conf block %d\n",
+ flash_command, i);
+
+ r = syn_write_u16(sd, sd->flash->data + block_num_offset, i);
+ if (r < 0)
+ return r;
+
+ r = syn_flash_command(sd, flash_command);
+ if (r < 0)
+ return r;
+
+ r = syn_flash_status(sd);
+ if (r < 0)
+ return r;
+
+ if (r != 0x80) {
+ dev_err(&sd->client->dev,
+ "flash_read error : %s\n",
+ syn_flash_error_str(syn_flash_error(r)));
+ return syn_flash_error(r);
+ }
+
+ r = syn_read_block(sd, sd->flash->data + block_data_offset, d,
+ sd->flash_caps.block_size);
+ if (r < 0)
+ return r;
+
+ d += sd->flash_caps.block_size;
+ }
+
+ return 0;
+}
+
+static int syn_flash_write_config(struct syn *sd)
+{
+ int r;
+ const int config_size = sd->fw_image.config_size;
+ const int blocks = config_size / sd->flash_caps.block_size;
+
+ r = syn_flash_write_block_cmd(sd, sd->fw_image.config_data, blocks,
+ FLASH_CMD_CONF_WRITE_BLOCK);
+ if (r < 0)
+ return r;
+
+ if (r) {
+ dev_err(&sd->client->dev,
+ "flash write fw error : %s\n",
+ syn_flash_error_str(r));
+ return -EIO;
+ }
+
+ return 0;
+}
+
+#ifdef FIRMWARE_VERIFY
+static int syn_flash_validate(struct syn *sd)
+{
+ int r;
+ const int fw_size = sd->fw_image.fw_size;
+ const int blocks = fw_size / sd->flash_caps.block_size;
+
+ if (sd->flash_caps.fw_block_count !=
+ (fw_size / sd->flash_caps.block_size) ||
+ (fw_size % sd->flash_caps.block_size)) {
+ dev_err(&sd->client->dev,
+ "fw size not aligned to block count\n");
+ return -EINVAL;
+ }
+
+ r = syn_flash_write_block_cmd(sd, sd->fw_image.fw_data, blocks,
+ FLASH_CMD_FW_CRC_BLOCK);
+ if (r < 0)
+ return r;
+
+ if (r) {
+ dev_err(&sd->client->dev,
+ "flash validate error : %s\n",
+ syn_flash_error_str(r));
+ return -EIO;
+ }
+
+ r = syn_read_u8(sd, sd->flash->data + syn_flash_block_data_offset(sd));
+ if (r < 0)
+ return r;
+
+ if (r == 0x00) {
+ dev_info(&sd->client->dev, "flash_validate: image is valid\n");
+ return 0;
+ } else if (r == 0xff)
+ dev_info(&sd->client->dev,
+ "flash_validate: image is invalid\n");
+ else
+ dev_info(&sd->client->dev,
+ "flash_validate: image status unknown: 0x%02x\n", r);
+
+ return -1;
+}
+
+static int syn_flash_compare_config(struct syn *sd)
+{
+ u32 cfg_crc_c;
+ u32 cfg_crc;
+ u32 cfg_crc_r;
+ u8 *cfg_data_r;
+ int r;
+
+ cfg_data_r = kmalloc(sd->fw_image.config_size, GFP_KERNEL);
+ if (cfg_data_r == NULL)
+ return -ENOMEM;
+
+ r = syn_flash_read_config(sd, cfg_data_r);
+ if (r < 0) {
+ kfree(cfg_data_r);
+ return r;
+ }
+
+ dev_info(&sd->client->dev, "Flash read config done\n");
+
+ cfg_crc = sd->fw_image.config_checksum;
+ cfg_crc_c = syn_crc_fletcher32((u16 *)(sd->fw_image.config_data),
+ (sd->fw_image.config_size - 4) >> 1);
+ cfg_crc_r = syn_crc_fletcher32((u16 *)(cfg_data_r),
+ (sd->fw_image.config_size - 4) >> 1);
+
+ kfree(cfg_data_r);
+ cfg_data_r = NULL;
+
+ dev_info(&sd->client->dev, "calculated fw image crc 0x%x\n", cfg_crc_c);
+ dev_info(&sd->client->dev, "read fw image crc 0x%x\n", cfg_crc);
+ dev_info(&sd->client->dev, "calculated cfg read crc 0x%x\n", cfg_crc_r);
+
+ if (cfg_crc != cfg_crc_c) {
+ dev_err(&sd->client->dev,
+ "fw crc differs from calculated\n");
+ return -EINVAL;
+ }
+
+ if (cfg_crc != cfg_crc_r) {
+ dev_err(&sd->client->dev,
+ "fw crc differs from read from device\n");
+ return -EINVAL;
+ }
+
+ if (cfg_crc_c != cfg_crc_r) {
+ dev_err(&sd->client->dev,
+ "fw calculated crc differs from read from device\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+#else /* FIRMWARE_VERIFY */
+static int syn_flash_compare_config(struct syn *sd)
+{
+ return 0;
+}
+
+static int syn_flash_validate(struct syn *sd)
+{
+ return 0;
+}
+#endif
+
+static int syn_flash_erase_all(struct syn *sd)
+{
+ int r;
+
+ r = syn_write_u16(sd, sd->flash->data + syn_flash_block_data_offset(sd),
+ sd->flash_caps.bootloader_id);
+
+ r = syn_flash_command(sd, FLASH_CMD_ERASE_ALL);
+ if (r < 0)
+ return r;
+
+ r = syn_flash_status(sd);
+ if (r < 0)
+ return r;
+
+ if (r != 0x80) {
+ dev_err(&sd->client->dev,
+ "flash erase_all error : %d %s\n", syn_flash_error(r),
+ syn_flash_error_str(syn_flash_error(r)));
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int syn_flash_write_fw(struct syn *sd)
+{
+ int r;
+ const int fw_size = sd->fw_image.fw_size;
+ const int blocks = fw_size / sd->flash_caps.block_size;
+
+ r = syn_flash_write_block_cmd(sd, sd->fw_image.fw_data, blocks,
+ FLASH_CMD_FW_WRITE_BLOCK);
+ if (r < 0)
+ return r;
+
+ r = syn_flash_status(sd);
+ if (r < 0)
+ return r;
+
+ if (r != 0x80) {
+ dev_err(&sd->client->dev,
+ "flash write fw : %d %s\n", syn_flash_error(r),
+ syn_flash_error_str(syn_flash_error(r)));
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int syn_flash_firmware(struct syn *sd)
+{
+ int r;
+ int flash_status;
+
+ /* Restrict interrupts during flashing */
+ r = syn_write_u8(sd, sd->control->control + DEVICE_CONTROL_INTR_ENABLE,
+ INTR_FLASH | INTR_STATUS);
+
+ r = syn_flash_enable(sd);
+ if (r < 0)
+ goto flash_out;
+
+ r = syn_flash_validate(sd);
+ if (r < 0)
+ goto flash_out;
+
+ r = syn_flash_erase_all(sd);
+ if (r < 0)
+ goto flash_out;
+
+ r = syn_flash_write_fw(sd);
+ if (r < 0)
+ goto flash_out;
+
+ r = syn_flash_write_config(sd);
+ if (r < 0)
+ goto flash_out;
+
+ r = syn_flash_compare_config(sd);
+ if (r < 0)
+ goto flash_out;
+
+flash_out:
+ flash_status = syn_flash_status(sd);
+ dev_info(&sd->client->dev,
+ "flash status: %x %s\n", flash_status,
+ syn_flash_error_str(syn_flash_error(flash_status)));
+
+ /*
+ * We reset and interrupt handler should notice that
+ * everything has changed and reread the page descriptor table.
+ */
+ syn_reset_device(sd);
+
+ return syn_flash_error(flash_status) == FLASH_ERROR_SUCCESS ? 0 : -1;
+}
+
+static ssize_t syn_show_attr_flash(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ const ssize_t size = PAGE_SIZE;
+ ssize_t l = 0;
+ int r;
+
+ if (!sd->flash) {
+ l += snprintf(buf + l, size - l, "N/A\n");
+ return l;
+ }
+
+ mutex_lock(&sd->lock);
+
+ r = syn_control_data_read(sd, DEVICE_CONTROL_DATA_STATUS);
+
+ mutex_unlock(&sd->lock);
+
+ if (r < 0)
+ dev_err(&sd->client->dev, "read error %d\n", r);
+ else
+ l += snprintf(buf + l, size - l, "%d\n",
+ (r & (1 << 6)) ? 1 : 0);
+
+ return l;
+}
+
+static ssize_t syn_store_attr_flash(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ const struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ int r;
+ char name[FW_MAX_NAME_SIZE + 1];
+
+ if (count > FW_MAX_NAME_SIZE || count == 0) {
+ dev_err(&sd->client->dev, "firmware name check failure\n");
+ return 0;
+ }
+
+ memcpy(name, buf, count);
+ name[count] = 0;
+
+ mutex_lock(&sd->lock);
+
+ if (name[count - 1] == '\n')
+ name[count - 1] = 0;
+
+ if (sd->fw_entry) {
+ dev_err(&sd->client->dev, "firmware already in memory\n");
+ goto firmware_out;
+ }
+
+ r = syn_request_firmware(sd, name);
+ if (r < 0) {
+ dev_err(&sd->client->dev, "firmware not found\n");
+ goto store_out;
+ }
+
+ memset(&sd->fw_image, 0, sizeof(struct fw_image));
+
+ r = syn_check_firmware(sd);
+ if (r != 0) {
+ dev_err(&sd->client->dev,
+ "consistency check of firmware failed\n");
+ goto firmware_out;
+ }
+
+ dev_info(&sd->client->dev, "firmware consistency check ok\n");
+
+ r = syn_flash_firmware(sd);
+ if (r != 0) {
+ dev_err(&sd->client->dev,
+ "flashing of firmware failed\n");
+ goto firmware_out;
+ }
+
+ dev_info(&sd->client->dev, "flashing done with success\n");
+
+firmware_out:
+ memset(&sd->fw_image, 0, sizeof(struct fw_image));
+ release_firmware(sd->fw_entry);
+ sd->fw_entry = NULL;
+
+store_out:
+ mutex_unlock(&sd->lock);
+
+ return count;
+}
+
+static ssize_t syn_show_attr_product_id(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ const ssize_t size = PAGE_SIZE;
+ u8 product_id[PRODUCT_ID_LEN + 1];
+ ssize_t l = 0;
+ int r;
+
+ if (!sd->flash) {
+ l += snprintf(buf + l, size - l, "N/A\n");
+ return l;
+ }
+
+ mutex_lock(&sd->lock);
+
+ r = syn_read_block(sd, sd->control->query +
+ DEVICE_CONTROL_QUERY_PROD_ID,
+ product_id, PRODUCT_ID_LEN);
+
+ mutex_unlock(&sd->lock);
+
+ product_id[PRODUCT_ID_LEN] = 0;
+
+ if (r < 0)
+ dev_warn(&sd->client->dev,
+ "error %d reading product id\n", r);
+ else
+ l += snprintf(buf + l, size - l, "%s\n",
+ product_id);
+
+ return l;
+}
+
+static ssize_t syn_show_attr_product_family(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ const ssize_t size = PAGE_SIZE;
+ ssize_t l = 0;
+ int r;
+
+ if (!sd->flash) {
+ l += snprintf(buf + l, size - l, "N/A\n");
+ return l;
+ }
+
+ mutex_lock(&sd->lock);
+
+ r = syn_control_query_read(sd,
+ DEVICE_CONTROL_QUERY_PROD_FAMILY);
+
+ mutex_unlock(&sd->lock);
+
+ if (r < 0)
+ dev_warn(&sd->client->dev,
+ "error %d reading product family\n", r);
+ else
+ l += snprintf(buf + l, size - l, "%d\n",
+ r);
+
+ return l;
+}
+
+static ssize_t syn_show_attr_firmware_version(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ const ssize_t size = PAGE_SIZE;
+ ssize_t l = 0;
+ int r;
+
+ if (!sd->flash) {
+ l += snprintf(buf + l, size - l, "N/A\n");
+ return l;
+ }
+
+ mutex_lock(&sd->lock);
+
+ r = syn_control_query_read(sd,
+ DEVICE_CONTROL_QUERY_FW_VER);
+
+ mutex_unlock(&sd->lock);
+
+ if (r < 0)
+ dev_warn(&sd->client->dev,
+ "error %d reading fw version\n", r);
+ else
+ l += snprintf(buf + l, size - l, "%d\n",
+ r);
+
+ return l;
+}
+
+/*
+ * BIST (selftest) support
+ */
+
+static int syn_bist_run_test(struct syn *sd, struct bist_test_result *tr,
+ u8 test)
+{
+ int r;
+ u8 intr_mask = INTR_ALL;
+
+ if (!sd || !tr || !sd->bist)
+ return -EINVAL;
+
+ mutex_lock(&sd->lock);
+
+ r = syn_read_u8(sd, sd->control->control +
+ DEVICE_CONTROL_INTR_ENABLE);
+ if (r < 0)
+ goto out;
+
+ intr_mask = r & 0xFF;
+
+ /* Restrict interrupt sources*/
+ r = syn_write_u8(sd, sd->control->control + DEVICE_CONTROL_INTR_ENABLE,
+ INTR_BIST | INTR_STATUS);
+
+ r = syn_write_u8(sd, sd->bist->data + BIST_DATA_TEST_NUMBER_CTRL,
+ test);
+ if (r < 0)
+ goto out;
+
+ r = syn_write_u8(sd, sd->bist->command + BIST_CONTROL_COMMAND,
+ 0x01);
+ if (r < 0)
+ goto out;
+
+ r = syn_wait_for_attn(sd, 5 * 1000 * 1000, 0);
+ if (r < 0) {
+ dev_err(&sd->client->dev, "timeout running bist test\n");
+ goto out;
+ }
+
+ r = syn_control_data_read(sd, DEVICE_CONTROL_DATA_INTR_STATUS);
+ if (r < 0)
+ dev_err(&sd->client->dev, "error reading int status\n");
+
+ r = syn_read_u8(sd, sd->bist->command + BIST_CONTROL_COMMAND);
+ if (r < 0)
+ goto out;
+
+ if (r & (1 << 0)) {
+ r = -EBUSY;
+ goto out;
+ }
+
+ r = syn_read_u8(sd, sd->bist->data + BIST_DATA_OVERALL_RESULT);
+ if (r < 0)
+ goto out;
+
+ tr->failed = r;
+
+ r = syn_read_u8(sd, sd->bist->data + BIST_DATA_TEST_RESULT);
+ if (r < 0)
+ goto out;
+
+ tr->result = r;
+
+out:
+ r = syn_write_u8(sd, sd->control->control + DEVICE_CONTROL_INTR_ENABLE,
+ intr_mask);
+ mutex_unlock(&sd->lock);
+
+ return r;
+}
+
+static int syn_test_i2c_wr(struct syn *sd, const u8 value)
+{
+ int r;
+
+ r = syn_write_u8(sd, sd->bist->data + BIST_DATA_TEST_NUMBER_CTRL,
+ value);
+ if (r < 0)
+ goto out;
+
+ r = syn_read_u8(sd, sd->bist->data + BIST_DATA_TEST_NUMBER_CTRL);
+ if (r < 0)
+ goto out;
+
+ if (r != value) {
+ dev_err(&sd->client->dev,
+ "write verify error: wrote 0x%x got 0x%x\n",
+ value, r);
+ r = -EINVAL;
+ goto out;
+ }
+
+ r = 0;
+out:
+
+ return r;
+}
+
+static int syn_test_i2c(struct syn *sd)
+{
+ int r;
+ int i;
+
+ mutex_lock(&sd->lock);
+
+ for (i = 0; i < 8; i++) {
+ r = syn_test_i2c_wr(sd, 1 << i);
+ if (r < 0)
+ goto out;
+ }
+
+ for (i = 0; i < 256; i++) {
+ r = syn_test_i2c_wr(sd, i);
+ if (r < 0)
+ goto out;
+ }
+
+out:
+ mutex_unlock(&sd->lock);
+
+ return r;
+}
+
+static int syn_bist_selftest(struct syn *sd, struct bist_test_result *tr)
+{
+ int r;
+
+ r = syn_test_i2c(sd);
+ if (r != 0)
+ return r;
+
+ r = syn_bist_run_test(sd, tr, 0x00);
+
+ return r;
+}
+
+static ssize_t syn_show_attr_selftest(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ const ssize_t size = PAGE_SIZE;
+ ssize_t l = 0;
+ struct bist_test_result tr = { 0, };
+ int r;
+
+ if (!sd->bist) {
+ l += snprintf(buf + l, size - l, "Not available\n");
+ return l;
+ }
+
+ r = syn_bist_selftest(sd, &tr);
+ if (r != 0)
+ l += snprintf(buf + l, size - l,
+ "FAIL (io error %d)\n", r);
+ else {
+ if (tr.failed == 0)
+ l += snprintf(buf + l, size - l,
+ "PASS\n");
+ else
+ l += snprintf(buf + l, size - l,
+ "FAIL (test %d, result %d)\n",
+ tr.failed, tr.result);
+ }
+
+ return l;
+}
+
+static ssize_t syn_store_attr_reset(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+
+ mutex_lock(&sd->lock);
+
+ syn_reset_device(sd);
+
+ mutex_unlock(&sd->lock);
+
+ return count;
+}
+
+static ssize_t syn_show_attr_doze(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ const ssize_t size = PAGE_SIZE;
+ ssize_t l = 0;
+ int r;
+
+ mutex_lock(&sd->lock);
+ r = syn_get_nosleep(sd);
+ mutex_unlock(&sd->lock);
+
+ if (r < 0)
+ goto out;
+
+ l += snprintf(buf + l, size - l, "%d\n", !r);
+out:
+ return l;
+}
+
+static ssize_t syn_store_attr_doze(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ int val;
+
+ if (sscanf(buf, "%i", &val) != 1) {
+ dev_info(&sd->client->dev,
+ "error parsing debug\n");
+ return count;
+ }
+
+ if (val & (~0x01))
+ return count;
+
+ mutex_lock(&sd->lock);
+ syn_set_nosleep(sd, !val);
+ mutex_unlock(&sd->lock);
+
+ return count;
+}
+
+static ssize_t syn_show_attr_sleepmode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ const ssize_t size = PAGE_SIZE;
+ ssize_t l = 0;
+ int r;
+
+ mutex_lock(&sd->lock);
+
+ r = syn_read_u8(sd, sd->control->control + DEVICE_CONTROL_CTRL);
+ if (r < 0)
+ goto out;
+
+ l += snprintf(buf + l, size - l, "%d\n", (r & 0x03));
+out:
+ mutex_unlock(&sd->lock);
+ return l;
+}
+
+static ssize_t syn_store_attr_sleepmode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ int val;
+ int r;
+
+ if (sscanf(buf, "%i", &val) != 1) {
+ dev_info(&sd->client->dev,
+ "error parsing debug\n");
+ return count;
+ }
+
+ if (val & (~0x03))
+ return count;
+
+ mutex_lock(&sd->lock);
+
+ r = syn_read_u8(sd, sd->control->control + DEVICE_CONTROL_CTRL);
+ if (r < 0)
+ goto out;
+
+ val = (r & (~0x03)) | (val & 0x03);
+
+ r = syn_write_u8(sd, sd->control->control + DEVICE_CONTROL_CTRL,
+ val);
+
+out:
+ mutex_unlock(&sd->lock);
+
+ return count;
+}
+
+static ssize_t syn_show_attr_proximity(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ const ssize_t size = PAGE_SIZE;
+ ssize_t l = 0;
+ int r;
+
+ mutex_lock(&sd->lock);
+ r = syn_get_proximity_state(sd);
+ mutex_unlock(&sd->lock);
+
+ if (r < 0)
+ r = 0;
+
+ l += snprintf(buf + l, size - l, "%d\n", r);
+
+ return l;
+}
+
+static ssize_t syn_store_attr_proximity(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ int val;
+
+ if (sscanf(buf, "%i", &val) != 1) {
+ dev_info(&sd->client->dev,
+ "error parsing debug\n");
+ return count;
+ }
+
+ if (val & (~0x01))
+ return count;
+
+ mutex_lock(&sd->lock);
+ syn_set_proximity_state(sd, !!val);
+ mutex_unlock(&sd->lock);
+
+ return count;
+}
+
+static ssize_t syn_show_attr_sensitivity(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ const ssize_t size = PAGE_SIZE;
+ ssize_t l = 0;
+ int i;
+ u8 addr;
+ int r;
+
+ if (!sd->touch) {
+ l += snprintf(buf + l, size - l, "Not available\n");
+ return l;
+ }
+
+ addr = sd->touch->control + TOUCH_CONTROL_SENSOR_MAPPING +
+ sd->touch_caps.max_electrodes;
+
+ if (sd->touch_caps.has_gestures)
+ addr += 2;
+
+ mutex_lock(&sd->lock);
+
+ for (i = 0; i < sd->touch_caps.max_electrodes; i++) {
+ r = syn_read_u8(sd, addr + i);
+ if (r < 0) {
+ l += snprintf(buf + l, size - l, "Read error %d\n", r);
+ goto out;
+ } else
+ l += snprintf(buf + l, size - l, "0x%x ", r);
+ }
+
+ l += snprintf(buf + l, size - l, "\n");
+
+out:
+ mutex_unlock(&sd->lock);
+
+ return l;
+}
+
+static ssize_t syn_store_attr_sensitivity(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ int r;
+ int addr;
+ int base;
+ int value;
+
+ if (!sd->touch)
+ return count;
+
+ if (sscanf(buf, "%d %i", &addr, &value) != 2) {
+ dev_info(&sd->client->dev,
+ "error parsing sensitivity info <addr> <value>\n");
+ return count;
+ }
+
+ if (addr >= sd->touch_caps.max_electrodes) {
+ dev_info(&sd->client->dev,
+ "electorode number out of bounds %d > %d\n", addr,
+ sd->touch_caps.max_electrodes);
+ return count;
+ }
+
+ base = sd->touch->control + TOUCH_CONTROL_SENSOR_MAPPING +
+ sd->touch_caps.max_electrodes;
+
+ if (sd->touch_caps.has_gestures)
+ base += 2;
+
+ mutex_lock(&sd->lock);
+
+ r = syn_write_u8(sd, base + addr, value);
+ if (r < 0) {
+ dev_err(&sd->client->dev, "write failed with %d\n", r);
+ goto out;
+ }
+
+ r = syn_read_u8(sd, base + addr);
+ if (r < 0) {
+ dev_err(&sd->client->dev, "read failed with %d\n", r);
+ goto out;
+ }
+
+ if (r != value) {
+ dev_warn(&sd->client->dev,
+ "value verify error 0x%x != 0x%x\n", r, value);
+ }
+out:
+ mutex_unlock(&sd->lock);
+
+ return count;
+}
+
+static ssize_t syn_show_attr_sensormap(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ const ssize_t size = PAGE_SIZE;
+ ssize_t l = 0;
+ int i;
+ u8 addr;
+ int r;
+
+ if (!sd->touch) {
+ l += snprintf(buf + l, size - l, "Not available\n");
+ return l;
+ }
+
+ addr = sd->touch->control + TOUCH_CONTROL_SENSOR_MAPPING;
+
+ if (sd->touch_caps.has_gestures)
+ addr += 2;
+
+ mutex_lock(&sd->lock);
+
+ for (i = 0; i < sd->touch_caps.max_electrodes; i++) {
+ r = syn_read_u8(sd, addr + i);
+ if (r < 0) {
+ l += snprintf(buf + l, size - l, "Read error %d\n", r);
+ goto out;
+ } else
+ l += snprintf(buf + l, size - l, "%s: %d\n",
+ r & 0x80 ? "Y" : "X", r & 0x1F);
+ }
+
+ l += snprintf(buf + l, size - l, "\n");
+
+out:
+ mutex_unlock(&sd->lock);
+
+ return l;
+}
+
+static ssize_t syn_show_attr_reg_dump(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ const ssize_t size = PAGE_SIZE;
+ ssize_t l = 0;
+ int i;
+ int r;
+
+ if (!sd->touch) {
+ l += snprintf(buf + l, size - l, "Not available\n");
+ return l;
+ }
+
+ mutex_lock(&sd->lock);
+
+ for (i = 0; i < 0xff; i++) {
+ r = syn_read_u8(sd, i);
+ if (r < 0) {
+ l += snprintf(buf + l, size - l,
+ "Read error at 0x%x %d\n", i, r);
+ goto out;
+ } else
+ l += snprintf(buf + l, size - l,
+ "0x%02x: 0x%02x (%d)\n", i, r, r);
+ }
+
+ l += snprintf(buf + l, size - l, "\n");
+
+out:
+ mutex_unlock(&sd->lock);
+ return l;
+}
+
+static ssize_t syn_store_attr_reg_dump(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ int addr;
+ int value;
+
+ if (!sd->touch)
+ return count;
+
+ if (sscanf(buf, "%i", &addr) != 1) {
+ dev_info(&sd->client->dev,
+ "error parsing addr\n");
+ return count;
+ }
+
+ if (addr < 0 || addr > 0xff) {
+ dev_info(&sd->client->dev,
+ "error 0x%x out of bounds\n", addr);
+ return count;
+ }
+
+ mutex_lock(&sd->lock);
+
+ value = syn_read_u8(sd, addr);
+ if (value < 0) {
+ dev_info(&sd->client->dev,
+ "error %d reading from 0x%x\n", value, addr);
+ goto out;
+ }
+
+ dev_info(&sd->client->dev, "0x%x: 0x%x\n", addr, value);
+
+out:
+ mutex_unlock(&sd->lock);
+
+ return count;
+}
+
+static ssize_t syn_show_attr_debug(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ const ssize_t size = PAGE_SIZE;
+ ssize_t l = 0;
+
+ mutex_lock(&sd->lock);
+
+ l += snprintf(buf + l, size - l, "0x%02x\n", sd->debug_flag);
+
+ mutex_unlock(&sd->lock);
+
+ return l;
+}
+
+static ssize_t syn_store_attr_debug(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ int flag = 0;
+
+ if (sscanf(buf, "%i", &flag) != 1) {
+ dev_info(&sd->client->dev,
+ "error parsing debug\n");
+ return count;
+ }
+
+ mutex_lock(&sd->lock);
+
+ sd->debug_flag = flag;
+
+ mutex_unlock(&sd->lock);
+
+ return count;
+}
+
+static ssize_t syn_store_attr_latency(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+
+ mutex_lock(&sd->lock);
+
+ sd->t_work_min = ULONG_MAX;
+ sd->t_work_max = 0;
+ sd->t_work = 0;
+ sd->t_work_c = 0;
+
+ sd->t_wakeup_min = ULONG_MAX;
+ sd->t_wakeup_max = 0;
+ sd->t_wakeup = 0;
+ sd->t_wakeup_c = 0;
+
+ sd->t_count = 0;
+
+ mutex_unlock(&sd->lock);
+
+ return count;
+}
+
+static ssize_t syn_show_attr_latency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ const ssize_t size = PAGE_SIZE;
+ ssize_t l = 0;
+
+ mutex_lock(&sd->lock);
+
+ l += snprintf(buf + l, size - l,
+ "count %lu: wakeup %lu (%lu, %lu, %lu), "
+ "work %lu (%lu, %lu, %lu)\n",
+ sd->t_count,
+ sd->t_wakeup, sd->t_wakeup_min, sd->t_wakeup_max,
+ sd->t_count ? (sd->t_wakeup_c / sd->t_count) : 0,
+ sd->t_work, sd->t_work_min, sd->t_work_max,
+ sd->t_count ? (sd->t_work_c / sd->t_count) : 0);
+
+ mutex_unlock(&sd->lock);
+
+ return l;
+}
+
+static ssize_t syn_show_attr_virtualized(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ const ssize_t size = PAGE_SIZE;
+ ssize_t l = 0;
+
+ mutex_lock(&sd->lock);
+
+ l += snprintf(buf + l, size - l, "%d\n", sd->virtualized);
+
+ mutex_unlock(&sd->lock);
+
+ return l;
+}
+
+static ssize_t syn_store_attr_virtualized(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct syn *sd = platform_get_drvdata(pdev);
+ int val;
+
+ if (sscanf(buf, "%i", &val) != 1) {
+ dev_info(&sd->client->dev,
+ "error parsing virtualized\n");
+ return count;
+ }
+
+ if (val & (~0x01))
+ return count;
+
+ mutex_lock(&sd->lock);
+ sd->virtualized = val;
+ mutex_unlock(&sd->lock);
+
+ return count;
+}
+
+
+
+static DEVICE_ATTR(flash, S_IWUSR | S_IRUGO,
+ syn_show_attr_flash, syn_store_attr_flash);
+
+static DEVICE_ATTR(product_id, S_IRUGO,
+ syn_show_attr_product_id, NULL);
+
+static DEVICE_ATTR(product_family, S_IRUGO,
+ syn_show_attr_product_family, NULL);
+
+static DEVICE_ATTR(firmware_version, S_IRUGO,
+ syn_show_attr_firmware_version, NULL);
+
+static DEVICE_ATTR(selftest, S_IRUGO,
+ syn_show_attr_selftest, NULL);
+
+static DEVICE_ATTR(reset, S_IWUSR, NULL, syn_store_attr_reset);
+
+static DEVICE_ATTR(doze, S_IRUGO | S_IWUSR,
+ syn_show_attr_doze, syn_store_attr_doze);
+
+static DEVICE_ATTR(sleepmode, S_IRUGO | S_IWUSR,
+ syn_show_attr_sleepmode, syn_store_attr_sleepmode);
+
+static DEVICE_ATTR(proximity, S_IRUGO | S_IWUSR,
+ syn_show_attr_proximity, syn_store_attr_proximity);
+
+static DEVICE_ATTR(sensitivity, S_IRUGO | S_IWUSR,
+ syn_show_attr_sensitivity, syn_store_attr_sensitivity);
+
+static DEVICE_ATTR(sensormap, S_IRUGO,
+ syn_show_attr_sensormap, NULL);
+
+static DEVICE_ATTR(dump, S_IRUGO | S_IWUSR,
+ syn_show_attr_reg_dump, syn_store_attr_reg_dump);
+
+static DEVICE_ATTR(debug, S_IRUGO | S_IWUSR,
+ syn_show_attr_debug, syn_store_attr_debug);
+
+static DEVICE_ATTR(latency, S_IRUGO | S_IWUSR,
+ syn_show_attr_latency, syn_store_attr_latency);
+
+static DEVICE_ATTR(virtualized, S_IRUGO | S_IWUSR,
+ syn_show_attr_virtualized, syn_store_attr_virtualized);
+
+static struct attribute *syn_attrs[] = {
+ &dev_attr_flash.attr,
+ &dev_attr_product_id.attr,
+ &dev_attr_product_family.attr,
+ &dev_attr_firmware_version.attr,
+ &dev_attr_selftest.attr,
+ &dev_attr_reset.attr,
+ &dev_attr_doze.attr,
+ &dev_attr_sleepmode.attr,
+ &dev_attr_proximity.attr,
+ &dev_attr_sensitivity.attr,
+ &dev_attr_sensormap.attr,
+ &dev_attr_dump.attr,
+ &dev_attr_debug.attr,
+ &dev_attr_latency.attr,
+ &dev_attr_virtualized.attr,
+ NULL,
+};
+
+static struct attribute_group syn_attr_group = {
+ .attrs = syn_attrs,
+};
+
+static void syn_create_sysfs(struct syn *sd)
+{
+ int r;
+
+ r = sysfs_create_group(&sd->client->dev.kobj, &syn_attr_group);
+ if (r) {
+ dev_err(&sd->client->dev,
+ "failed to create flash sysfs files\n");
+ }
+}
+
+static void syn_remove_sysfs(struct syn *sd)
+{
+ sysfs_remove_group(&sd->client->dev.kobj, &syn_attr_group);
+}
+
+static int syn_read_func_descs(struct syn *sd)
+{
+ int properties;
+ int fun;
+ int r;
+ int addr = REG_PDT_PROPERTIES;
+
+ sd->func_descs_valid = 0;
+ sd->interrupt_sources = 0;
+
+ properties = syn_read_u8(sd, addr--);
+ if (properties < 0)
+ return properties;
+
+ /* We don't support non standard page select register addr */
+ if (properties != 0x00) {
+ dev_err(&sd->client->dev, "unsupported pdt\n");
+ return -ENODEV;
+ }
+
+ sd->func_desc_num = 0;
+
+ while (sd->func_desc_num < MAX_FUNC_DESCS) {
+ struct func_desc * const f = &sd->func_desc[sd->func_desc_num];
+ u8 regs[5];
+
+ fun = syn_read_u8(sd, addr);
+ if (fun < 0)
+ return fun;
+
+ if (fun == 0x00)
+ goto out;
+
+ if (sd->debug_flag & DFLAG_VERBOSE)
+ dev_info(&sd->client->dev,
+ "found function 0x%x\n", fun);
+
+ f->num = fun;
+ addr -= 5;
+ r = syn_read_block(sd, addr, regs, 5);
+ if (r != 5) {
+ dev_err(&sd->client->dev, "error reading pdt\n");
+ return -ENODEV;
+ }
+
+ f->query = regs[0];
+ f->command = regs[1];
+ f->control = regs[2];
+ f->data = regs[3];
+ f->intr_sources = regs[4] & 0x07;
+ f->version = (regs[4] >> 5) & 0x3;
+
+ if (f->intr_sources) {
+ f->intr_start_bit = sd->interrupt_sources;
+ sd->interrupt_sources += f->intr_sources;
+ }
+
+ sd->func_desc_num++;
+ addr--;
+ }
+
+out:
+ /* Put shortcuts in place */
+ sd->control = syn_get_func_desc(sd, FUNC_DEVICE_CONTROL);
+ sd->flash = syn_get_func_desc(sd, FUNC_FLASH);
+ sd->touch = syn_get_func_desc(sd, FUNC_2D);
+ sd->buttons = syn_get_func_desc(sd, FUNC_BUTTONS);
+ sd->bist = syn_get_func_desc(sd, FUNC_BIST);
+ sd->prox = syn_get_func_desc(sd, FUNC_PROXIMITY);
+
+ if (sd->touch == NULL)
+ dev_warn(&sd->client->dev,
+ "no 2d functionality found! (in flash mode ?)\n");
+
+ if (sd->flash) {
+ r = syn_flash_query_caps(sd);
+ if (r)
+ return r;
+ } else
+ dev_warn(&sd->client->dev, "no flash functionality found!\n");
+
+ if (sd->control) {
+ sd->func_descs_valid = 1;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int syn_register_handlers(struct syn *sd)
+{
+ int r;
+
+ r = syn_register_intr_handler(sd, FUNC_DEVICE_CONTROL,
+ syn_isr_device_control);
+ if (r < 0) {
+ dev_err(&sd->client->dev,
+ "no device control, cant continue\n");
+ return r;
+ }
+
+ /*
+ * Don't care about return values here.
+ * Install interrupt handlers for every function
+ * (even if they dont exist)
+ */
+
+ syn_register_intr_handler(sd, FUNC_BIST, syn_isr_bist);
+ syn_register_intr_handler(sd, FUNC_BUTTONS, syn_isr_buttons);
+ syn_register_intr_handler(sd, FUNC_TIMER, syn_isr_timer);
+ syn_register_intr_handler(sd, FUNC_2D, syn_isr_2d);
+ syn_register_intr_handler(sd, FUNC_FLASH, syn_isr_flash);
+ syn_register_intr_handler(sd, FUNC_PROXIMITY, syn_isr_proximity);
+
+ return 0;
+}
+
+static int syn_touch_get_max_pos(struct syn *sd, u8 reg)
+{
+ int data;
+
+ data = syn_read_u16(sd, sd->touch->control + reg);
+ if (data < 0)
+ return data;
+
+ return data & 0x00000fff;
+}
+
+static int syn_touch_get_max_x_pos(struct syn *sd)
+{
+ return syn_touch_get_max_pos(sd, TOUCH_CONTROL_SENSOR_MAX_X);
+}
+
+static int syn_touch_get_max_y_pos(struct syn *sd)
+{
+ return syn_touch_get_max_pos(sd, TOUCH_CONTROL_SENSOR_MAX_Y);
+}
+
+static int syn_button_query_caps(struct syn *sd)
+{
+ int r;
+
+ r = syn_read_u8(sd, sd->buttons->query + BUTTON_QUERY_BUTTON_COUNT);
+ if (r < 0)
+ return r;
+
+ if (r > MAX_BUTTONS)
+ return -EINVAL;
+
+ sd->button_caps.button_count = r & BUTTON_QUERY_BUTTON_MASK;
+
+ return 0;
+}
+
+static int syn_touch_query_caps(struct syn *sd)
+{
+ int r;
+ u8 data[TOUCH_QUERY_LEN];
+
+ if (sd->touch == NULL)
+ return -ENODEV;
+
+ r = syn_read_block(sd, sd->touch->query + TOUCH_QUERY_NUM_SENSORS,
+ data, TOUCH_QUERY_LEN);
+ if (r < 0)
+ return r;
+
+ if (data[0] != 0) {
+ dev_err(&sd->client->dev, "no support for multiple sensors\n");
+ return -ENODEV;
+ }
+
+ sd->touch_caps.is_configurable = (data[1] & (1 << 7)) ? 1 : 0;
+ sd->touch_caps.has_gestures = (data[1] & (1 << 5)) ? 1 : 0;
+ sd->touch_caps.has_abs_mode = (data[1] & (1 << 4)) ? 1 : 0;
+ sd->touch_caps.has_rel_mode = (data[1] & (1 << 3)) ? 1 : 0;
+ sd->touch_caps.finger_count = (data[1] & 0x07) + 1;
+ sd->touch_caps.x_electrodes = (data[2] & 0x1f);
+ sd->touch_caps.y_electrodes = (data[3] & 0x1f);
+ sd->touch_caps.max_electrodes = (data[4] & 0x1f);
+ sd->touch_caps.abs_data_size = (data[5] & 0x03);
+
+ if (sd->touch_caps.finger_count > MAX_TOUCH_POINTS ||
+ sd->touch_caps.has_rel_mode ||
+ /* sd->touch_caps.has_gestures || */
+ sd->touch_caps.abs_data_size) {
+ dev_err(&sd->client->dev, "no support for this sensor\n");
+ return -ENODEV;
+ }
+
+ r = syn_touch_get_max_x_pos(sd);
+ if (r < 0)
+ return r;
+
+ sd->touch_caps.max_x = r;
+
+ r = syn_touch_get_max_y_pos(sd);
+ if (r < 0)
+ return r;
+
+ sd->touch_caps.max_y = r;
+
+ return 0;
+}
+
+static int syn_reset_device(struct syn *sd)
+{
+ int r;
+
+ /*
+ * We get called from various contexts, for example after firmware
+ * change. Resetting with state registermap would be disastrous
+ * so we reread everything in order to be sure to write
+ * to correct register.
+ */
+ r = syn_read_func_descs(sd);
+
+ if (!sd->control) {
+ dev_err(&sd->client->dev, "no control. can't reset!\n");
+ return -1;
+ }
+
+ dev_info(&sd->client->dev, "resetting device (reg 0x%x)\n",
+ sd->control->command + DEVICE_CONTROL_COMMAND);
+
+ r = syn_write_u8(sd, sd->control->control + DEVICE_CONTROL_INTR_ENABLE,
+ 0);
+
+ r = syn_control_data_read(sd, DEVICE_CONTROL_DATA_INTR_STATUS);
+
+ sd->func_descs_valid = 0;
+
+ r = syn_write_u8(sd, sd->control->command +
+ DEVICE_CONTROL_COMMAND, DEVICE_COMMAND_RESET);
+
+ r = syn_wait_for_attn(sd, 100 * 1000, 0);
+ r = syn_wait_for_attn(sd, 100 * 1000, 1);
+ r = syn_wait_for_attn(sd, 500 * 1000, 0);
+
+ return 0;
+}
+
+static int syn_initialize(struct syn *sd)
+{
+ int r;
+ char prod_id[PRODUCT_ID_LEN + 1];
+ int prod_family;
+ int prod_fw_version;
+
+ syn_clear_device_state(sd);
+
+ r = syn_read_u8(sd, REG_PAGE_SELECT);
+ if (r < 0) {
+ dev_err(&sd->client->dev, "error reading page select\n");
+ goto err_out;
+ }
+
+ if (r != 0x00) {
+ dev_err(&sd->client->dev, "page select non zero\n");
+ r = -ENODEV;
+ goto err_out;
+ }
+
+ r = syn_read_func_descs(sd);
+ if (r < 0) {
+ dev_err(&sd->client->dev, "error reading func descs\n");
+ goto err_out;
+ }
+
+ if (!sd->control) {
+ dev_err(&sd->client->dev, "no control functions found\n");
+ r = -ENODEV;
+ goto err_out;
+ }
+
+ /* Clear the Unconfigured bit */
+ r = syn_write_u8(sd, sd->control->control + DEVICE_CONTROL_CTRL,
+ DEVICE_CONTROL_CONFIGURED);
+
+ if (!sd->bist)
+ dev_warn(&sd->client->dev, "no bist capabilities found\n");
+
+ if (sd->touch) {
+ r = syn_touch_query_caps(sd);
+ if (r < 0) {
+ dev_err(&sd->client->dev,
+ "error reading touch capabilities\n");
+ sd->touch = NULL;
+ }
+ } else {
+ dev_err(&sd->client->dev, "no touch capabilities found\n");
+ }
+
+ if (sd->buttons) {
+ r = syn_button_query_caps(sd);
+ if (r < 0) {
+ dev_err(&sd->client->dev,
+ "error reading button capabilities\n");
+ sd->buttons = NULL;
+ }
+ } else {
+ dev_err(&sd->client->dev, "no button capabilities found\n");
+ }
+
+ r = syn_read_block(sd, sd->control->query +
+ DEVICE_CONTROL_QUERY_PROD_ID,
+ prod_id, PRODUCT_ID_LEN);
+
+ if (r != PRODUCT_ID_LEN) {
+ dev_err(&sd->client->dev, "unable to read product id\n");
+ r = -ENODEV;
+ goto err_out;
+ }
+ prod_id[r] = 0;
+
+ prod_family = syn_control_query_read(sd,
+ DEVICE_CONTROL_QUERY_PROD_FAMILY);
+ if (prod_family < 0) {
+ dev_err(&sd->client->dev, "unable to read product family\n");
+ goto err_out;
+ }
+
+ prod_fw_version = syn_control_query_read(sd,
+ DEVICE_CONTROL_QUERY_FW_VER);
+ if (prod_fw_version < 0) {
+ dev_err(&sd->client->dev, "unable to read product family\n");
+ goto err_out;
+ }
+
+ printk(KERN_INFO DRIVER_NAME ": product ID: %s family:%d fw:%d\n",
+ prod_id, prod_family, prod_fw_version);
+
+ r = syn_register_handlers(sd);
+ if (r) {
+ dev_err(&sd->client->dev, "failed to register_handlers\n");
+ r = -ENODEV;
+ goto err_out;
+ }
+
+ if (sd->touch) {
+ r = syn_register_input_devices(sd, sd->touch_caps.finger_count);
+ if (r) {
+ dev_err(&sd->client->dev,
+ "failed to register input devices\n");
+ r = -ENODEV;
+ goto err_out;
+ }
+ }
+
+ if (sd->prox) {
+ /* By default disable it */
+ syn_set_proximity_state(sd, 0);
+ }
+
+ r = syn_control_data_read(sd, DEVICE_CONTROL_DATA_STATUS);
+ if (r < 0) {
+ dev_err(&sd->client->dev, "error %d reading device status\n",
+ r);
+ }
+
+ /*
+ * If we get reset during initialization, unconfigured should be on
+ */
+ if (r & (1 << 7)) {
+ dev_warn(&sd->client->dev, "lost config during initialize\n");
+ sd->failed_inits++;
+ syn_reset_device(sd);
+ goto err_out;
+ }
+
+ sd->failed_inits = 0;
+ return 0;
+
+err_out:
+ return r;
+}
+
+static int syn_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct syn *sd;
+ struct tm12xx_ts_platform_data *pdata;
+ int r;
+ int man_id;
+
+ sd = kzalloc(sizeof(struct syn), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+
+ INIT_WORK(&sd->isr_work, syn_isr_work);
+ i2c_set_clientdata(client, sd);
+ sd->client = client;
+
+ mutex_init(&sd->lock);
+
+ sd->t_work_min = ULONG_MAX;
+ sd->t_wakeup_min = ULONG_MAX;
+
+ sd->wq = create_singlethread_workqueue("tm12xx_wq");
+ if (!sd->wq) {
+ r = -ENOMEM;
+ goto err_free_dev;
+ }
+
+ pdata = sd->client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->dev, "no platform data found\n");
+ r = -ENODEV;
+ goto err_free_dev;
+ }
+
+ sd->gpio_intr = pdata->gpio_intr;
+
+ r = syn_read_func_descs(sd);
+ if (r < 0) {
+ dev_err(&client->dev, "error reading func descs\n");
+ goto err_free_dev;
+ }
+
+ r = syn_write_u8(sd, sd->control->control + DEVICE_CONTROL_INTR_ENABLE,
+ 0);
+ if (r < 0)
+ goto err_free_dev;
+
+ r = syn_control_data_read(sd, DEVICE_CONTROL_DATA_INTR_STATUS);
+ if (r < 0)
+ goto err_free_dev;
+
+ man_id = syn_control_query_read(sd, DEVICE_CONTROL_QUERY_MANID);
+ if (man_id < 0) {
+ dev_dbg(&client->dev, "unable to get manufacturer id\n");
+ r = -ENODEV;
+ goto err_free_dev;
+ }
+
+ printk(KERN_INFO DRIVER_NAME ": " DRIVER_DESC
+ " found man id %x (%d)\n", man_id, man_id);
+
+ r = gpio_request(sd->gpio_intr, "Synaptic TM12XX Interrupt");
+ if (r < 0) {
+ dev_dbg(&client->dev, "unable to get INT GPIO\n");
+ r = -ENODEV;
+ goto err_free_dev;
+ }
+
+ gpio_direction_input(sd->gpio_intr);
+
+ r = syn_register_intr_handler(sd, FUNC_DEVICE_CONTROL,
+ syn_isr_device_control);
+ if (r < 0) {
+ dev_err(&sd->client->dev,
+ "no device control, can't continue\n");
+ r = -ENODEV;
+ goto err_free_int_gpio;
+ }
+
+ mutex_lock(&sd->lock);
+
+ r = request_irq(gpio_to_irq(sd->gpio_intr), syn_isr,
+ IRQF_DISABLED | IRQF_TRIGGER_LOW | IRQF_TRIGGER_FALLING,
+ DRIVER_NAME, sd);
+ if (r) {
+ dev_dbg(&client->dev, "can't get IRQ %d (%d), err %d\n",
+ gpio_to_irq(sd->gpio_intr), sd->gpio_intr, r);
+ goto err_release_mutex;
+ }
+
+ syn_create_sysfs(sd);
+
+ /* Initialize thru reset */
+ r = syn_reset_device(sd);
+ if (r < 0) {
+ dev_err(&client->dev, "error in reset device\n");
+ goto err_free_irq;
+ }
+
+ mutex_unlock(&sd->lock);
+
+ return 0;
+
+err_free_irq:
+ free_irq(gpio_to_irq(sd->gpio_intr), sd);
+
+err_release_mutex:
+ mutex_unlock(&sd->lock);
+
+err_free_int_gpio:
+ gpio_free(sd->gpio_intr);
+
+err_free_dev:
+ kfree(sd);
+ sd = NULL;
+ return r;
+}
+
+static int __exit syn_remove(struct i2c_client *client)
+{
+ struct syn *sd = i2c_get_clientdata(client);
+ int i;
+
+ mutex_lock(&sd->lock);
+
+ if (sd->control)
+ syn_write_u8(sd,
+ sd->control->control + DEVICE_CONTROL_INTR_ENABLE,
+ 0);
+
+ free_irq(gpio_to_irq(sd->gpio_intr), sd);
+
+ mutex_unlock(&sd->lock);
+
+ destroy_workqueue(sd->wq);
+ sd->wq = NULL;
+
+ syn_remove_sysfs(sd);
+
+ for (i = 0; i < MAX_TOUCH_POINTS; i++) {
+ if (sd->tp[i].idev) {
+ input_unregister_device(sd->tp[i].idev);
+ input_free_device(sd->tp[i].idev);
+ sd->tp[i].idev = NULL;
+ }
+ }
+
+ gpio_free(sd->gpio_intr);
+
+ kfree(sd);
+ i2c_set_clientdata(client, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int syn_suspend(struct i2c_client *client, pm_message_t msg)
+{
+ struct syn *sd = i2c_get_clientdata(client);
+ int r;
+
+ mutex_lock(&sd->lock);
+
+ r = syn_read_u8(sd, sd->control->control + DEVICE_CONTROL_CTRL);
+ /* If we fail to get previous finetuned power mode, we don't care */
+ if (r < 0)
+ goto out;
+
+ sd->device_control_ctrl = r & 0xff;
+
+ r = syn_write_u8(sd, sd->control->control + DEVICE_CONTROL_CTRL,
+ DEVICE_CONTROL_SLEEP_SENSOR);
+out:
+ mutex_unlock(&sd->lock);
+
+ return 0;
+}
+
+static int syn_resume(struct i2c_client *client)
+{
+ struct syn *sd = i2c_get_clientdata(client);
+ int r;
+
+ mutex_lock(&sd->lock);
+ r = syn_write_u8(sd, sd->control->control + DEVICE_CONTROL_CTRL,
+ sd->device_control_ctrl);
+ if (r < 0)
+ dev_err(&sd->client->dev, "error %d restoring device state\n",
+ r);
+ mutex_unlock(&sd->lock);
+
+ return 0;
+}
+
+#endif
+
+
+static const struct i2c_device_id syn_id[] = {
+ { "tm12xx_ts_primary", 0 },
+ { "tm12xx_ts_secondary", 1},
+ { }
+};
+
+static struct i2c_driver syn_i2c_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = syn_probe,
+ .remove = __exit_p(syn_remove),
+ .id_table = syn_id,
+
+#ifdef CONFIG_PM
+ .suspend = syn_suspend,
+ .resume = syn_resume,
+#endif
+};
+
+static int __init syn_init(void)
+{
+ int r;
+
+ r = i2c_add_driver(&syn_i2c_driver);
+ if (r < 0) {
+ printk(KERN_WARNING DRIVER_NAME
+ " driver registration failed\n");
+ return r;
+ }
+
+ return 0;
+}
+
+static void __exit syn_exit(void)
+{
+ i2c_del_driver(&syn_i2c_driver);
+}
+
+module_init(syn_init);
+module_exit(syn_exit);
+
+MODULE_AUTHOR("Mika Kuoppala <mika.kuoppala@nokia.com>");
+MODULE_DESCRIPTION("Synaptic TM12xx Touch controller driver");
+MODULE_LICENSE("GPL");