summaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen/iqs5xx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/touchscreen/iqs5xx.c')
-rw-r--r--drivers/input/touchscreen/iqs5xx.c387
1 files changed, 176 insertions, 211 deletions
diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c
index 5875bb1099a8..4ebd7565ae6e 100644
--- a/drivers/input/touchscreen/iqs5xx.c
+++ b/drivers/input/touchscreen/iqs5xx.c
@@ -2,16 +2,16 @@
/*
* Azoteq IQS550/572/525 Trackpad/Touchscreen Controller
*
- * Copyright (C) 2018
- * Author: Jeff LaBundy <jeff@labundy.com>
+ * Copyright (C) 2018 Jeff LaBundy <jeff@labundy.com>
*
* These devices require firmware exported from a PC-based configuration tool
* made available by the vendor. Firmware files may be pushed to the device's
* nonvolatile memory by writing the filename to the 'fw_file' sysfs control.
*
- * Link to PC-based configuration tool and data sheet: http://www.azoteq.com/
+ * Link to PC-based configuration tool and datasheet: https://www.azoteq.com/
*/
+#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -23,48 +23,44 @@
#include <linux/input/touchscreen.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of_device.h>
#include <linux/slab.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#define IQS5XX_FW_FILE_LEN 64
#define IQS5XX_NUM_RETRIES 10
-#define IQS5XX_NUM_POINTS 256
#define IQS5XX_NUM_CONTACTS 5
#define IQS5XX_WR_BYTES_MAX 2
#define IQS5XX_PROD_NUM_IQS550 40
#define IQS5XX_PROD_NUM_IQS572 58
#define IQS5XX_PROD_NUM_IQS525 52
-#define IQS5XX_PROJ_NUM_A000 0
-#define IQS5XX_PROJ_NUM_B000 15
-#define IQS5XX_MAJOR_VER_MIN 2
-#define IQS5XX_RESUME 0x00
-#define IQS5XX_SUSPEND 0x01
+#define IQS5XX_SHOW_RESET BIT(7)
+#define IQS5XX_ACK_RESET BIT(7)
-#define IQS5XX_SW_INPUT_EVENT 0x10
-#define IQS5XX_SETUP_COMPLETE 0x40
-#define IQS5XX_EVENT_MODE 0x01
-#define IQS5XX_TP_EVENT 0x04
+#define IQS5XX_SUSPEND BIT(0)
+#define IQS5XX_RESUME 0
-#define IQS5XX_FLIP_X 0x01
-#define IQS5XX_FLIP_Y 0x02
-#define IQS5XX_SWITCH_XY_AXIS 0x04
+#define IQS5XX_SETUP_COMPLETE BIT(6)
+#define IQS5XX_WDT BIT(5)
+#define IQS5XX_ALP_REATI BIT(3)
+#define IQS5XX_REATI BIT(2)
+
+#define IQS5XX_TP_EVENT BIT(2)
+#define IQS5XX_EVENT_MODE BIT(0)
#define IQS5XX_PROD_NUM 0x0000
-#define IQS5XX_ABS_X 0x0016
-#define IQS5XX_ABS_Y 0x0018
+#define IQS5XX_SYS_INFO0 0x000F
+#define IQS5XX_SYS_INFO1 0x0010
#define IQS5XX_SYS_CTRL0 0x0431
#define IQS5XX_SYS_CTRL1 0x0432
#define IQS5XX_SYS_CFG0 0x058E
#define IQS5XX_SYS_CFG1 0x058F
-#define IQS5XX_TOTAL_RX 0x063D
-#define IQS5XX_TOTAL_TX 0x063E
-#define IQS5XX_XY_CFG0 0x0669
#define IQS5XX_X_RES 0x066E
#define IQS5XX_Y_RES 0x0670
+#define IQS5XX_EXP_FILE 0x0677
#define IQS5XX_CHKSM 0x83C0
#define IQS5XX_APP 0x8400
#define IQS5XX_CSTM 0xBE00
@@ -88,21 +84,11 @@
#define IQS5XX_BL_CMD_CRC 0x03
#define IQS5XX_BL_BLK_LEN_MAX 64
#define IQS5XX_BL_ID 0x0200
-#define IQS5XX_BL_STATUS_RESET 0x00
-#define IQS5XX_BL_STATUS_AVAIL 0xA5
#define IQS5XX_BL_STATUS_NONE 0xEE
#define IQS5XX_BL_CRC_PASS 0x00
#define IQS5XX_BL_CRC_FAIL 0x01
#define IQS5XX_BL_ATTEMPTS 3
-struct iqs5xx_private {
- struct i2c_client *client;
- struct input_dev *input;
- struct gpio_desc *reset_gpio;
- struct mutex lock;
- u8 bl_status;
-};
-
struct iqs5xx_dev_id_info {
__be16 prod_num;
__be16 proj_num;
@@ -126,6 +112,24 @@ struct iqs5xx_touch_data {
u8 area;
} __packed;
+struct iqs5xx_status {
+ u8 sys_info[2];
+ u8 num_active;
+ __be16 rel_x;
+ __be16 rel_y;
+ struct iqs5xx_touch_data touch_data[IQS5XX_NUM_CONTACTS];
+} __packed;
+
+struct iqs5xx_private {
+ struct i2c_client *client;
+ struct input_dev *input;
+ struct gpio_desc *reset_gpio;
+ struct touchscreen_properties prop;
+ struct mutex lock;
+ struct iqs5xx_dev_id_info dev_id_info;
+ u8 exp_file[2];
+};
+
static int iqs5xx_read_burst(struct i2c_client *client,
u16 reg, void *val, u16 len)
{
@@ -182,11 +186,6 @@ static int iqs5xx_read_word(struct i2c_client *client, u16 reg, u16 *val)
return 0;
}
-static int iqs5xx_read_byte(struct i2c_client *client, u16 reg, u8 *val)
-{
- return iqs5xx_read_burst(client, reg, val, sizeof(*val));
-}
-
static int iqs5xx_write_burst(struct i2c_client *client,
u16 reg, const void *val, u16 len)
{
@@ -289,7 +288,7 @@ static int iqs5xx_bl_cmd(struct i2c_client *client, u8 bl_cmd, u16 bl_addr)
break;
case IQS5XX_BL_CMD_EXEC:
usleep_range(10000, 10100);
- /* fall through */
+ fallthrough;
default:
return 0;
}
@@ -337,11 +336,16 @@ static int iqs5xx_bl_open(struct i2c_client *client)
*/
for (i = 0; i < IQS5XX_BL_ATTEMPTS; i++) {
iqs5xx_reset(client);
+ usleep_range(350, 400);
for (j = 0; j < IQS5XX_NUM_RETRIES; j++) {
error = iqs5xx_bl_cmd(client, IQS5XX_BL_CMD_VER, 0);
- if (!error || error == -EINVAL)
- return error;
+ if (!error)
+ usleep_range(10000, 10100);
+ else if (error != -EINVAL)
+ continue;
+
+ return error;
}
}
@@ -438,7 +442,7 @@ static int iqs5xx_set_state(struct i2c_client *client, u8 state)
struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
int error1, error2;
- if (iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET)
+ if (!iqs5xx->dev_id_info.bl_status)
return 0;
mutex_lock(&iqs5xx->lock);
@@ -481,14 +485,12 @@ static void iqs5xx_close(struct input_dev *input)
static int iqs5xx_axis_init(struct i2c_client *client)
{
struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
- struct touchscreen_properties prop;
- struct input_dev *input;
+ struct touchscreen_properties *prop = &iqs5xx->prop;
+ struct input_dev *input = iqs5xx->input;
+ u16 max_x, max_y;
int error;
- u16 max_x, max_x_hw;
- u16 max_y, max_y_hw;
- u8 val;
- if (!iqs5xx->input) {
+ if (!input) {
input = devm_input_allocate_device(&client->dev);
if (!input)
return -ENOMEM;
@@ -498,98 +500,47 @@ static int iqs5xx_axis_init(struct i2c_client *client)
input->open = iqs5xx_open;
input->close = iqs5xx_close;
- input_set_capability(input, EV_ABS, ABS_MT_POSITION_X);
- input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y);
- input_set_capability(input, EV_ABS, ABS_MT_PRESSURE);
-
input_set_drvdata(input, iqs5xx);
iqs5xx->input = input;
}
- touchscreen_parse_properties(iqs5xx->input, true, &prop);
-
- error = iqs5xx_read_byte(client, IQS5XX_TOTAL_RX, &val);
+ error = iqs5xx_read_word(client, IQS5XX_X_RES, &max_x);
if (error)
return error;
- max_x_hw = (val - 1) * IQS5XX_NUM_POINTS;
- error = iqs5xx_read_byte(client, IQS5XX_TOTAL_TX, &val);
+ error = iqs5xx_read_word(client, IQS5XX_Y_RES, &max_y);
if (error)
return error;
- max_y_hw = (val - 1) * IQS5XX_NUM_POINTS;
-
- error = iqs5xx_read_byte(client, IQS5XX_XY_CFG0, &val);
- if (error)
- return error;
-
- if (val & IQS5XX_SWITCH_XY_AXIS)
- swap(max_x_hw, max_y_hw);
- if (prop.swap_x_y)
- val ^= IQS5XX_SWITCH_XY_AXIS;
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_x, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_y, 0, 0);
+ input_set_abs_params(input, ABS_MT_PRESSURE, 0, U16_MAX, 0, 0);
- if (prop.invert_x)
- val ^= prop.swap_x_y ? IQS5XX_FLIP_Y : IQS5XX_FLIP_X;
+ touchscreen_parse_properties(input, true, prop);
- if (prop.invert_y)
- val ^= prop.swap_x_y ? IQS5XX_FLIP_X : IQS5XX_FLIP_Y;
-
- error = iqs5xx_write_byte(client, IQS5XX_XY_CFG0, val);
- if (error)
- return error;
-
- if (prop.max_x > max_x_hw) {
- dev_err(&client->dev, "Invalid maximum x-coordinate: %u > %u\n",
- prop.max_x, max_x_hw);
+ /*
+ * The device reserves 0xFFFF for coordinates that correspond to slots
+ * which are not in a state of touch.
+ */
+ if (prop->max_x >= U16_MAX || prop->max_y >= U16_MAX) {
+ dev_err(&client->dev, "Invalid touchscreen size: %u*%u\n",
+ prop->max_x, prop->max_y);
return -EINVAL;
- } else if (prop.max_x == 0) {
- error = iqs5xx_read_word(client, IQS5XX_X_RES, &max_x);
+ }
+
+ if (prop->max_x != max_x) {
+ error = iqs5xx_write_word(client, IQS5XX_X_RES, prop->max_x);
if (error)
return error;
-
- input_abs_set_max(iqs5xx->input,
- prop.swap_x_y ? ABS_MT_POSITION_Y :
- ABS_MT_POSITION_X,
- max_x);
- } else {
- max_x = (u16)prop.max_x;
}
- if (prop.max_y > max_y_hw) {
- dev_err(&client->dev, "Invalid maximum y-coordinate: %u > %u\n",
- prop.max_y, max_y_hw);
- return -EINVAL;
- } else if (prop.max_y == 0) {
- error = iqs5xx_read_word(client, IQS5XX_Y_RES, &max_y);
+ if (prop->max_y != max_y) {
+ error = iqs5xx_write_word(client, IQS5XX_Y_RES, prop->max_y);
if (error)
return error;
-
- input_abs_set_max(iqs5xx->input,
- prop.swap_x_y ? ABS_MT_POSITION_X :
- ABS_MT_POSITION_Y,
- max_y);
- } else {
- max_y = (u16)prop.max_y;
}
- /*
- * Write horizontal and vertical resolution to the device in case its
- * original defaults were overridden or swapped as per the properties
- * specified in the device tree.
- */
- error = iqs5xx_write_word(client,
- prop.swap_x_y ? IQS5XX_Y_RES : IQS5XX_X_RES,
- max_x);
- if (error)
- return error;
-
- error = iqs5xx_write_word(client,
- prop.swap_x_y ? IQS5XX_X_RES : IQS5XX_Y_RES,
- max_y);
- if (error)
- return error;
-
- error = input_mt_init_slots(iqs5xx->input, IQS5XX_NUM_CONTACTS,
+ error = input_mt_init_slots(input, IQS5XX_NUM_CONTACTS,
INPUT_MT_DIRECT);
if (error)
dev_err(&client->dev, "Failed to initialize slots: %d\n",
@@ -603,7 +554,6 @@ static int iqs5xx_dev_init(struct i2c_client *client)
struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
struct iqs5xx_dev_id_info *dev_id_info;
int error;
- u8 val;
u8 buf[sizeof(*dev_id_info) + 1];
error = iqs5xx_read_burst(client, IQS5XX_PROD_NUM,
@@ -619,7 +569,7 @@ static int iqs5xx_dev_init(struct i2c_client *client)
* the missing zero is prepended).
*/
buf[0] = 0;
- dev_id_info = (struct iqs5xx_dev_id_info *)&buf[(buf[1] > 0) ? 0 : 1];
+ dev_id_info = (struct iqs5xx_dev_id_info *)&buf[buf[1] ? 0 : 1];
switch (be16_to_cpu(dev_id_info->prod_num)) {
case IQS5XX_PROD_NUM_IQS550:
@@ -632,52 +582,37 @@ static int iqs5xx_dev_init(struct i2c_client *client)
return -EINVAL;
}
- switch (be16_to_cpu(dev_id_info->proj_num)) {
- case IQS5XX_PROJ_NUM_A000:
- dev_err(&client->dev, "Unsupported project number: %u\n",
- be16_to_cpu(dev_id_info->proj_num));
- return iqs5xx_bl_open(client);
- case IQS5XX_PROJ_NUM_B000:
- break;
- default:
- dev_err(&client->dev, "Unrecognized project number: %u\n",
- be16_to_cpu(dev_id_info->proj_num));
- return -EINVAL;
- }
-
- if (dev_id_info->major_ver < IQS5XX_MAJOR_VER_MIN) {
- dev_err(&client->dev, "Unsupported major version: %u\n",
- dev_id_info->major_ver);
+ /*
+ * With the product number recognized yet shifted by one byte, open the
+ * bootloader and wait for user space to convert the A000 device into a
+ * B000 device via new firmware.
+ */
+ if (buf[1]) {
+ dev_err(&client->dev, "Opening bootloader for A000 device\n");
return iqs5xx_bl_open(client);
}
- switch (dev_id_info->bl_status) {
- case IQS5XX_BL_STATUS_AVAIL:
- case IQS5XX_BL_STATUS_NONE:
- break;
- default:
- dev_err(&client->dev,
- "Unrecognized bootloader status: 0x%02X\n",
- dev_id_info->bl_status);
- return -EINVAL;
- }
+ error = iqs5xx_read_burst(client, IQS5XX_EXP_FILE,
+ iqs5xx->exp_file, sizeof(iqs5xx->exp_file));
+ if (error)
+ return error;
error = iqs5xx_axis_init(client);
if (error)
return error;
- error = iqs5xx_read_byte(client, IQS5XX_SYS_CFG0, &val);
+ error = iqs5xx_write_byte(client, IQS5XX_SYS_CTRL0, IQS5XX_ACK_RESET);
if (error)
return error;
- val |= IQS5XX_SETUP_COMPLETE;
- val &= ~IQS5XX_SW_INPUT_EVENT;
- error = iqs5xx_write_byte(client, IQS5XX_SYS_CFG0, val);
+ error = iqs5xx_write_byte(client, IQS5XX_SYS_CFG0,
+ IQS5XX_SETUP_COMPLETE | IQS5XX_WDT |
+ IQS5XX_ALP_REATI | IQS5XX_REATI);
if (error)
return error;
- val = IQS5XX_TP_EVENT | IQS5XX_EVENT_MODE;
- error = iqs5xx_write_byte(client, IQS5XX_SYS_CFG1, val);
+ error = iqs5xx_write_byte(client, IQS5XX_SYS_CFG1,
+ IQS5XX_TP_EVENT | IQS5XX_EVENT_MODE);
if (error)
return error;
@@ -685,16 +620,15 @@ static int iqs5xx_dev_init(struct i2c_client *client)
if (error)
return error;
- iqs5xx->bl_status = dev_id_info->bl_status;
+ iqs5xx->dev_id_info = *dev_id_info;
/*
- * Closure of the first communication window that appears following the
- * release of reset appears to kick off an initialization period during
- * which further communication is met with clock stretching. The return
- * from this function is delayed so that further communication attempts
- * avoid this period.
+ * The following delay allows ATI to complete before the open and close
+ * callbacks are free to elicit I2C communication. Any attempts to read
+ * from or write to the device during this time may face extended clock
+ * stretching and prompt the I2C controller to report an error.
*/
- msleep(100);
+ msleep(250);
return 0;
}
@@ -702,7 +636,7 @@ static int iqs5xx_dev_init(struct i2c_client *client)
static irqreturn_t iqs5xx_irq(int irq, void *data)
{
struct iqs5xx_private *iqs5xx = data;
- struct iqs5xx_touch_data touch_data[IQS5XX_NUM_CONTACTS];
+ struct iqs5xx_status status;
struct i2c_client *client = iqs5xx->client;
struct input_dev *input = iqs5xx->input;
int error, i;
@@ -712,24 +646,38 @@ static irqreturn_t iqs5xx_irq(int irq, void *data)
* RDY output during bootloader mode. If the device operates outside of
* bootloader mode, the input device is guaranteed to be allocated.
*/
- if (iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET)
+ if (!iqs5xx->dev_id_info.bl_status)
return IRQ_NONE;
- error = iqs5xx_read_burst(client, IQS5XX_ABS_X,
- touch_data, sizeof(touch_data));
+ error = iqs5xx_read_burst(client, IQS5XX_SYS_INFO0,
+ &status, sizeof(status));
if (error)
return IRQ_NONE;
- for (i = 0; i < ARRAY_SIZE(touch_data); i++) {
- u16 pressure = be16_to_cpu(touch_data[i].strength);
+ if (status.sys_info[0] & IQS5XX_SHOW_RESET) {
+ dev_err(&client->dev, "Unexpected device reset\n");
+
+ error = iqs5xx_dev_init(client);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to re-initialize device: %d\n", error);
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(status.touch_data); i++) {
+ struct iqs5xx_touch_data *touch_data = &status.touch_data[i];
+ u16 pressure = be16_to_cpu(touch_data->strength);
input_mt_slot(input, i);
if (input_mt_report_slot_state(input, MT_TOOL_FINGER,
pressure != 0)) {
- input_report_abs(input, ABS_MT_POSITION_X,
- be16_to_cpu(touch_data[i].abs_x));
- input_report_abs(input, ABS_MT_POSITION_Y,
- be16_to_cpu(touch_data[i].abs_y));
+ touchscreen_report_pos(input, &iqs5xx->prop,
+ be16_to_cpu(touch_data->abs_x),
+ be16_to_cpu(touch_data->abs_y),
+ true);
input_report_abs(input, ABS_MT_PRESSURE, pressure);
}
}
@@ -884,12 +832,9 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client,
static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file)
{
struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
- int error;
+ int error, error_init = 0;
u8 *pmap;
- if (iqs5xx->bl_status == IQS5XX_BL_STATUS_NONE)
- return -EPERM;
-
pmap = kzalloc(IQS5XX_PMAP_LEN, GFP_KERNEL);
if (!pmap)
return -ENOMEM;
@@ -907,7 +852,7 @@ static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file)
*/
disable_irq(client->irq);
- iqs5xx->bl_status = IQS5XX_BL_STATUS_RESET;
+ iqs5xx->dev_id_info.bl_status = 0;
error = iqs5xx_bl_cmd(client, IQS5XX_BL_CMD_VER, 0);
if (error) {
@@ -927,20 +872,14 @@ static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file)
error = iqs5xx_bl_verify(client, IQS5XX_CSTM,
pmap + IQS5XX_CHKSM_LEN + IQS5XX_APP_LEN,
IQS5XX_CSTM_LEN);
- if (error)
- goto err_reset;
-
- error = iqs5xx_bl_cmd(client, IQS5XX_BL_CMD_EXEC, 0);
err_reset:
- if (error) {
- iqs5xx_reset(client);
- usleep_range(10000, 10100);
- }
+ iqs5xx_reset(client);
+ usleep_range(15000, 15100);
- error = iqs5xx_dev_init(client);
- if (!error && iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET)
- error = -EINVAL;
+ error_init = iqs5xx_dev_init(client);
+ if (!iqs5xx->dev_id_info.bl_status)
+ error_init = error_init ? : -EINVAL;
enable_irq(client->irq);
@@ -949,11 +888,12 @@ err_reset:
err_kfree:
kfree(pmap);
- return error;
+ return error ? : error_init;
}
-static ssize_t fw_file_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t fw_file_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
{
struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev);
struct i2c_client *client = iqs5xx->client;
@@ -995,29 +935,63 @@ static ssize_t fw_file_store(struct device *dev, struct device_attribute *attr,
return count;
}
+static ssize_t fw_info_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev);
+
+ if (!iqs5xx->dev_id_info.bl_status)
+ return -ENODATA;
+
+ return sysfs_emit(buf, "%u.%u.%u.%u:%u.%u\n",
+ be16_to_cpu(iqs5xx->dev_id_info.prod_num),
+ be16_to_cpu(iqs5xx->dev_id_info.proj_num),
+ iqs5xx->dev_id_info.major_ver,
+ iqs5xx->dev_id_info.minor_ver,
+ iqs5xx->exp_file[0], iqs5xx->exp_file[1]);
+}
+
static DEVICE_ATTR_WO(fw_file);
+static DEVICE_ATTR_RO(fw_info);
static struct attribute *iqs5xx_attrs[] = {
&dev_attr_fw_file.attr,
+ &dev_attr_fw_info.attr,
NULL,
};
-static const struct attribute_group iqs5xx_attr_group = {
+static umode_t iqs5xx_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int i)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev);
+
+ if (attr == &dev_attr_fw_file.attr &&
+ (iqs5xx->dev_id_info.bl_status == IQS5XX_BL_STATUS_NONE ||
+ !iqs5xx->reset_gpio))
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group iqs5xx_group = {
+ .is_visible = iqs5xx_attr_is_visible,
.attrs = iqs5xx_attrs,
};
+__ATTRIBUTE_GROUPS(iqs5xx);
-static int __maybe_unused iqs5xx_suspend(struct device *dev)
+static int iqs5xx_suspend(struct device *dev)
{
struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev);
struct input_dev *input = iqs5xx->input;
int error = 0;
- if (!input)
+ if (!input || device_may_wakeup(dev))
return error;
mutex_lock(&input->mutex);
- if (input->users)
+ if (input_device_enabled(input))
error = iqs5xx_set_state(iqs5xx->client, IQS5XX_SUSPEND);
mutex_unlock(&input->mutex);
@@ -1025,18 +999,18 @@ static int __maybe_unused iqs5xx_suspend(struct device *dev)
return error;
}
-static int __maybe_unused iqs5xx_resume(struct device *dev)
+static int iqs5xx_resume(struct device *dev)
{
struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev);
struct input_dev *input = iqs5xx->input;
int error = 0;
- if (!input)
+ if (!input || device_may_wakeup(dev))
return error;
mutex_lock(&input->mutex);
- if (input->users)
+ if (input_device_enabled(input))
error = iqs5xx_set_state(iqs5xx->client, IQS5XX_RESUME);
mutex_unlock(&input->mutex);
@@ -1044,10 +1018,9 @@ static int __maybe_unused iqs5xx_resume(struct device *dev)
return error;
}
-static SIMPLE_DEV_PM_OPS(iqs5xx_pm, iqs5xx_suspend, iqs5xx_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(iqs5xx_pm, iqs5xx_suspend, iqs5xx_resume);
-static int iqs5xx_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int iqs5xx_probe(struct i2c_client *client)
{
struct iqs5xx_private *iqs5xx;
int error;
@@ -1059,8 +1032,8 @@ static int iqs5xx_probe(struct i2c_client *client,
i2c_set_clientdata(client, iqs5xx);
iqs5xx->client = client;
- iqs5xx->reset_gpio = devm_gpiod_get(&client->dev,
- "reset", GPIOD_OUT_LOW);
+ iqs5xx->reset_gpio = devm_gpiod_get_optional(&client->dev,
+ "reset", GPIOD_OUT_LOW);
if (IS_ERR(iqs5xx->reset_gpio)) {
error = PTR_ERR(iqs5xx->reset_gpio);
dev_err(&client->dev, "Failed to request GPIO: %d\n", error);
@@ -1069,9 +1042,6 @@ static int iqs5xx_probe(struct i2c_client *client,
mutex_init(&iqs5xx->lock);
- iqs5xx_reset(client);
- usleep_range(10000, 10100);
-
error = iqs5xx_dev_init(client);
if (error)
return error;
@@ -1084,12 +1054,6 @@ static int iqs5xx_probe(struct i2c_client *client,
return error;
}
- error = devm_device_add_group(&client->dev, &iqs5xx_attr_group);
- if (error) {
- dev_err(&client->dev, "Failed to add attributes: %d\n", error);
- return error;
- }
-
if (iqs5xx->input) {
error = input_register_device(iqs5xx->input);
if (error)
@@ -1120,8 +1084,9 @@ MODULE_DEVICE_TABLE(of, iqs5xx_of_match);
static struct i2c_driver iqs5xx_i2c_driver = {
.driver = {
.name = "iqs5xx",
+ .dev_groups = iqs5xx_groups,
.of_match_table = iqs5xx_of_match,
- .pm = &iqs5xx_pm,
+ .pm = pm_sleep_ptr(&iqs5xx_pm),
},
.id_table = iqs5xx_id,
.probe = iqs5xx_probe,