* [PATCH] input synaptics-rmi4: stop scanning PDT after blank page
From: Christopher Heiny @ 2014-03-08 2:04 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
Vivian Ly, Daniel Rosenberg, Linus Walleij, Benjamin Tissoires,
David Herrmann, Jiri Kosina
When scanning the Page Descriptor Table, the end of the PDT is marked by a
page where the first PDT entry is empty. Stop scanning when we find this
page.
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>
---
drivers/input/rmi4/rmi_driver.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index 2172c80..dd3ccf5 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -505,7 +505,8 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
return retval;
}
- return data->f01_bootloader_mode ? RMI_SCAN_DONE : RMI_SCAN_CONTINUE;
+ return (data->f01_bootloader_mode || addr == pdt_start) ?
+ RMI_SCAN_DONE : RMI_SCAN_CONTINUE;
}
static int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
^ permalink raw reply related
* [PATCH 02/05] input synaptics-rmi4: Add some additional F01 properties for the use of reflash.
From: Christopher Heiny @ 2014-03-08 2:29 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
Vivian Ly, Daniel Rosenberg, Linus Walleij, Benjamin Tissoires,
David Herrmann, Jiri Kosina
In-Reply-To: <1394245795-17347-1-git-send-email-cheiny@synaptics.com>
The reflash code will need access to the firmware build ID and
some other info. As long as we're taking the scenic route to
the firmware build ID, we remember a number of other interesting
settings for use by diagnostic modules.
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>
---
drivers/input/rmi4/rmi_f01.c | 170 ++++++++++++++++++++++++++++++++++++++++---
drivers/input/rmi4/rmi_f01.h | 101 +++++++++++++++++++++++++
2 files changed, 262 insertions(+), 9 deletions(-)
diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c
index 41cb795..8504865 100644
--- a/drivers/input/rmi4/rmi_f01.c
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -48,11 +48,18 @@ struct f01_data {
unsigned int num_of_irq_regs;
};
+#define PACKAGE_ID_BYTES 4
+#define BUILD_ID_BYTES 3
+
int rmi_f01_read_properties(struct rmi_device *rmi_dev, u16 query_base_addr,
struct f01_basic_properties *props)
{
u8 basic_query[RMI_F01_BASIC_QUERY_LEN];
+ u8 info_buf[4];
int error;
+ int i;
+ u16 query_addr = query_base_addr;
+ u16 prod_info_addr;
error = rmi_read_block(rmi_dev, query_base_addr,
basic_query, sizeof(basic_query));
@@ -70,19 +77,164 @@ int rmi_f01_read_properties(struct rmi_device *rmi_dev, u16 query_base_addr,
basic_query[1] & RMI_F01_QRY1_HAS_ADJ_DOZE;
props->has_adjustable_doze_holdoff =
basic_query[1] & RMI_F01_QRY1_HAS_ADJ_DOZE_HOFF;
+ props->has_query42 = basic_query[1] & RMI_F01_QRY1_HAS_PROPS_2;
+
+ props->productinfo =
+ ((basic_query[2] & RMI_F01_QRY2_PRODINFO_MASK) << 7) |
+ (basic_query[3] & RMI_F01_QRY2_PRODINFO_MASK);
snprintf(props->dom, sizeof(props->dom), "20%02d/%02d/%02d",
basic_query[5] & RMI_F01_QRY5_YEAR_MASK,
basic_query[6] & RMI_F01_QRY6_MONTH_MASK,
basic_query[7] & RMI_F01_QRY7_DAY_MASK);
- memcpy(props->product_id, &basic_query[11],
- RMI_PRODUCT_ID_LENGTH);
+ memcpy(props->product_id, &basic_query[11], RMI_PRODUCT_ID_LENGTH);
props->product_id[RMI_PRODUCT_ID_LENGTH] = '\0';
+ query_addr += 11;
- props->productinfo =
- ((basic_query[2] & RMI_F01_QRY2_PRODINFO_MASK) << 7) |
- (basic_query[3] & RMI_F01_QRY2_PRODINFO_MASK);
+
+ error = rmi_read_block(rmi_dev, query_addr, props->product_id,
+ RMI_PRODUCT_ID_LENGTH);
+ if (error < 0) {
+ dev_err(&rmi_dev->dev, "Failed to read product ID.\n");
+ return error;
+ }
+ props->product_id[RMI_PRODUCT_ID_LENGTH] = '\0';
+
+ /* We'll come back and use this later, depending on some other query
+ * bits.
+ */
+ prod_info_addr = query_addr + 6;
+
+ query_addr += RMI_PRODUCT_ID_LENGTH;
+ if (props->has_lts) {
+ error = rmi_read(rmi_dev, query_addr, info_buf);
+ if (error < 0) {
+ dev_err(&rmi_dev->dev, "Failed to read LTS info.\n");
+ return error;
+ }
+ props->slave_asic_rows = info_buf[0] &
+ RMI_F01_QRY21_SLAVE_ROWS_MASK;
+ props->slave_asic_columns = (info_buf[1] &
+ RMI_F01_QRY21_SLAVE_COLUMNS_MASK) >> 3;
+ query_addr++;
+ }
+
+ if (props->has_sensor_id) {
+ error = rmi_read(rmi_dev, query_addr, &props->sensor_id);
+ if (error < 0) {
+ dev_err(&rmi_dev->dev, "Failed to read sensor ID.\n");
+ return error;
+ }
+ query_addr++;
+ }
+
+ /* Maybe skip a block of undefined LTS registers. */
+ if (props->has_lts)
+ query_addr += RMI_F01_LTS_RESERVED_SIZE;
+
+ if (props->has_query42) {
+ error = rmi_read(rmi_dev, query_addr, info_buf);
+ if (error < 0) {
+ dev_err(&rmi_dev->dev, "Failed to read additional properties.\n");
+ return error;
+ }
+ props->has_ds4_queries = info_buf[0] &
+ RMI_F01_QRY42_DS4_QUERIES;
+ props->has_multi_physical = info_buf[0] &
+ RMI_F01_QRY42_MULTI_PHYS;
+ props->has_guest = info_buf[0] & RMI_F01_QRY42_GUEST;
+ props->has_swr = info_buf[0] & RMI_F01_QRY42_SWR;
+ props->has_nominal_report_rate = info_buf[0] &
+ RMI_F01_QRY42_NOMINAL_REPORT;
+ props->has_recalibration_interval = info_buf[0] &
+ RMI_F01_QRY42_RECAL_INTERVAL;
+ query_addr++;
+ }
+
+ if (props->has_ds4_queries) {
+ error = rmi_read(rmi_dev, query_addr, &props->ds4_query_length);
+ if (error < 0) {
+ dev_err(&rmi_dev->dev, "Failed to read DS4 query length size.\n");
+ return error;
+ }
+ query_addr++;
+ }
+
+ for (i = 1; i <= props->ds4_query_length; i++) {
+ u8 val;
+ error = rmi_read(rmi_dev, query_addr, &val);
+ query_addr++;
+ if (error < 0) {
+ dev_err(&rmi_dev->dev, "Failed to read F01_RMI_QUERY43.%02d, code: %d.\n",
+ i, error);
+ continue;
+ }
+ switch (i) {
+ case 1:
+ props->has_package_id_query = val &
+ RMI_F01_QRY43_01_PACKAGE_ID;
+ props->has_build_id_query = val &
+ RMI_F01_QRY43_01_BUILD_ID;
+ props->has_reset_query = val & RMI_F01_QRY43_01_RESET;
+ props->has_maskrev_query = val &
+ RMI_F01_QRY43_01_PACKAGE_ID;
+ break;
+ case 2:
+ props->has_i2c_control = val & RMI_F01_QRY43_02_I2C_CTL;
+ props->has_spi_control = val & RMI_F01_QRY43_02_SPI_CTL;
+ props->has_attn_control = val &
+ RMI_F01_QRY43_02_ATTN_CTL;
+ props->has_win8_vendor_info = val &
+ RMI_F01_QRY43_02_WIN8;
+ props->has_timestamp = val & RMI_F01_QRY43_02_TIMESTAMP;
+ break;
+ case 3:
+ props->has_tool_id_query = val &
+ RMI_F01_QRY43_03_TOOL_ID;
+ props->has_fw_revision_query = val &
+ RMI_F01_QRY43_03_FW_REVISION;
+ break;
+ default:
+ dev_warn(&rmi_dev->dev, "No handling for F01_RMI_QUERY43.%02d.\n",
+ i);
+ }
+ }
+
+ /* If present, the ASIC package ID registers are overlaid on the
+ * product ID. Go back to the right address (saved previously) and
+ * read them.
+ */
+ if (props->has_package_id_query) {
+ error = rmi_read_block(rmi_dev, prod_info_addr, info_buf,
+ PACKAGE_ID_BYTES);
+ if (error < 0)
+ dev_warn(&rmi_dev->dev, "Failed to read package ID.\n");
+ else {
+ u16 *val = (u16 *)info_buf;
+ props->package_id = le16_to_cpu(*val);
+ val = (u16 *)(info_buf + 2);
+ props->package_rev = le16_to_cpu(*val);
+ }
+ }
+ prod_info_addr++;
+
+ /* The firmware build id (if present) is similarly overlaid on product
+ * ID registers. Go back again and read that data.
+ */
+ if (props->has_build_id_query) {
+ error = rmi_read_block(rmi_dev, prod_info_addr, info_buf,
+ BUILD_ID_BYTES);
+ if (error < 0)
+ dev_warn(&rmi_dev->dev, "Failed to read FW build ID.\n");
+ else {
+ u16 *val = (u16 *)info_buf;
+ props->build_id = le16_to_cpu(*val);
+ props->build_id += info_buf[2] * 65536;
+ dev_info(&rmi_dev->dev, "FW build ID: %#08x (%u).\n",
+ props->build_id, props->build_id);
+ }
+ }
return 0;
}
@@ -164,10 +316,10 @@ static int rmi_f01_probe(struct rmi_function *fn)
dev_err(&fn->dev, "Failed to read F01 properties.\n");
return error;
}
-
- dev_info(&fn->dev, "found RMI device, manufacturer: %s, product: %s\n",
- f01->properties.manufacturer_id == 1 ? "Synaptics" : "unknown",
- f01->properties.product_id);
+ dev_info(&fn->dev, "found RMI device, manufacturer: %s, product: %s, fw build: %d.\n",
+ f01->properties.manufacturer_id == 1 ?
+ "Synaptics" : "unknown",
+ f01->properties.product_id, f01->properties.build_id);
/* Advance to interrupt control registers, then skip over them. */
ctrl_base_addr++;
diff --git a/drivers/input/rmi4/rmi_f01.h b/drivers/input/rmi4/rmi_f01.h
index 9e5cc2b..af82fb7 100644
--- a/drivers/input/rmi4/rmi_f01.h
+++ b/drivers/input/rmi4/rmi_f01.h
@@ -21,6 +21,9 @@
/* Force a firmware reset of the sensor */
#define RMI_F01_CMD_DEVICE_RESET 1
+#define RMI_F01_DEFAULT_RESET_DELAY_MS 100
+
+#define F01_SERIALIZATION_SIZE 7
/* Various F01_RMI_QueryX bits */
@@ -41,20 +44,118 @@
#define RMI_F01_BASIC_QUERY_LEN 21 /* From Query 00 through 20 */
+#define RMI_F01_QRY21_SLAVE_ROWS_MASK 0x07
+#define RMI_F01_QRY21_SLAVE_COLUMNS_MASK 0x38
+
+#define RMI_F01_LTS_RESERVED_SIZE 19
+
+#define RMI_F01_QRY42_DS4_QUERIES (1 << 0)
+#define RMI_F01_QRY42_MULTI_PHYS (1 << 1)
+#define RMI_F01_QRY42_GUEST (1 << 2)
+#define RMI_F01_QRY42_SWR (1 << 3)
+#define RMI_F01_QRY42_NOMINAL_REPORT (1 << 4)
+#define RMI_F01_QRY42_RECAL_INTERVAL (1 << 5)
+
+#define RMI_F01_QRY43_01_PACKAGE_ID (1 << 0)
+#define RMI_F01_QRY43_01_BUILD_ID (1 << 1)
+#define RMI_F01_QRY43_01_RESET (1 << 2)
+#define RMI_F01_QRY43_01_MASK_REV (1 << 3)
+
+#define RMI_F01_QRY43_02_I2C_CTL (1 << 0)
+#define RMI_F01_QRY43_02_SPI_CTL (1 << 1)
+#define RMI_F01_QRY43_02_ATTN_CTL (1 << 2)
+#define RMI_F01_QRY43_02_WIN8 (1 << 3)
+#define RMI_F01_QRY43_02_TIMESTAMP (1 << 4)
+
+#define RMI_F01_QRY43_03_TOOL_ID (1 << 0)
+#define RMI_F01_QRY43_03_FW_REVISION (1 << 1)
+
+#define RMI_F01_QRY44_RST_ENABLED (1 << 0)
+#define RMI_F01_QRY44_RST_POLARITY (1 << 1)
+#define RMI_F01_QRY44_PULLUP_ENABLED (1 << 2)
+#define RMI_F01_QRY44_RST_PIN_MASK 0xF0
+
+#define RMI_TOOL_ID_LENGTH 16
+#define RMI_FW_REVISION_LENGTH 16
+
struct f01_basic_properties {
u8 manufacturer_id;
bool has_lts;
+ bool has_sensor_id;
bool has_adjustable_doze;
bool has_adjustable_doze_holdoff;
+ bool has_query42;
char dom[11]; /* YYYY/MM/DD + '\0' */
u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];
u16 productinfo;
+ u16 package_id;
+ u16 package_rev;
+ u32 build_id;
+
+ /* These are meaningful only if has_lts is true. */
+ u8 slave_asic_rows;
+ u8 slave_asic_columns;
+
+ /* This is meaningful only if has_sensor_id is true. */
+ u8 sensor_id;
+
+ /* These are meaningful only if has_query42 is true. */
+ bool has_ds4_queries;
+ bool has_multi_physical;
+ bool has_guest;
+ bool has_swr;
+ bool has_nominal_report_rate;
+ bool has_recalibration_interval;
+
+ /* Tells how many of the Query43.xx registers are present.
+ */
+ u8 ds4_query_length;
+
+ /* Query 43.1 */
+ bool has_package_id_query;
+ bool has_build_id_query;
+ bool has_reset_query;
+ bool has_maskrev_query;
+
+ /* Query 43.2 */
+ bool has_i2c_control;
+ bool has_spi_control;
+ bool has_attn_control;
+ bool has_win8_vendor_info;
+ bool has_timestamp;
+
+ /* Query 43.3 */
+ bool has_tool_id_query;
+ bool has_fw_revision_query;
+
+ /* Query 44 */
+ bool reset_enabled;
+ bool reset_polarity;
+ bool pullup_enabled;
+ u8 reset_pin;
+
+ /* Query 45 */
+ char tool_id[RMI_TOOL_ID_LENGTH + 1];
+
+ /* Query 46 */
+ char fw_revision[RMI_FW_REVISION_LENGTH + 1];
};
+
+/** Read the F01 query registers and populate the basic_properties structure.
+ * @rmi_dev - the device to be queries.
+ * @query_base_addr - address of the start of the query registers.
+ * @props - pointer to the structure to be filled in.
+ */
+int rmi_f01_read_properties(struct rmi_device *rmi_dev, u16 query_base_addr,
+ struct f01_basic_properties *props);
+
/* F01 device status bits */
/* Most recent device status event */
#define RMI_F01_STATUS_CODE(status) ((status) & 0x0f)
+/* Indicates that flash programming is enabled (bootloader mode). */
+#define RMI_F01_STATUS_BOOTLOADER(status) (!!((status) & 0x40))
/* The device has lost its configuration for some reason. */
#define RMI_F01_STATUS_UNCONFIGURED(status) (!!((status) & 0x80))
^ permalink raw reply related
* [PATCH 01/05] input synaptics-rmi4: Split F01 definitions out into header file for use by reflash.
From: Christopher Heiny @ 2014-03-08 2:29 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
Vivian Ly, Daniel Rosenberg, Linus Walleij, Benjamin Tissoires,
David Herrmann, Jiri Kosina
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>
---
drivers/input/rmi4/rmi_f01.c | 96 ++-----------------------------------
drivers/input/rmi4/rmi_f01.h | 110 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 114 insertions(+), 92 deletions(-)
diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c
index ee5f4a1..41cb795 100644
--- a/drivers/input/rmi4/rmi_f01.c
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -13,95 +13,7 @@
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "rmi_driver.h"
-
-#define RMI_PRODUCT_ID_LENGTH 10
-#define RMI_PRODUCT_INFO_LENGTH 2
-
-#define RMI_DATE_CODE_LENGTH 3
-
-#define PRODUCT_ID_OFFSET 0x10
-#define PRODUCT_INFO_OFFSET 0x1E
-
-
-/* Force a firmware reset of the sensor */
-#define RMI_F01_CMD_DEVICE_RESET 1
-
-/* Various F01_RMI_QueryX bits */
-
-#define RMI_F01_QRY1_CUSTOM_MAP (1 << 0)
-#define RMI_F01_QRY1_NON_COMPLIANT (1 << 1)
-#define RMI_F01_QRY1_HAS_LTS (1 << 2)
-#define RMI_F01_QRY1_HAS_SENSOR_ID (1 << 3)
-#define RMI_F01_QRY1_HAS_CHARGER_INP (1 << 4)
-#define RMI_F01_QRY1_HAS_ADJ_DOZE (1 << 5)
-#define RMI_F01_QRY1_HAS_ADJ_DOZE_HOFF (1 << 6)
-#define RMI_F01_QRY1_HAS_PROPS_2 (1 << 7)
-
-#define RMI_F01_QRY5_YEAR_MASK 0x1f
-#define RMI_F01_QRY6_MONTH_MASK 0x0f
-#define RMI_F01_QRY7_DAY_MASK 0x1f
-
-#define RMI_F01_QRY2_PRODINFO_MASK 0x7f
-
-#define RMI_F01_BASIC_QUERY_LEN 21 /* From Query 00 through 20 */
-
-struct f01_basic_properties {
- u8 manufacturer_id;
- bool has_lts;
- bool has_adjustable_doze;
- bool has_adjustable_doze_holdoff;
- char dom[11]; /* YYYY/MM/DD + '\0' */
- u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];
- u16 productinfo;
-};
-
-/* F01 device status bits */
-
-/* Most recent device status event */
-#define RMI_F01_STATUS_CODE(status) ((status) & 0x0f)
-/* The device has lost its configuration for some reason. */
-#define RMI_F01_STATUS_UNCONFIGURED(status) (!!((status) & 0x80))
-
-/* Control register bits */
-
-/*
- * Sleep mode controls power management on the device and affects all
- * functions of the device.
- */
-#define RMI_F01_CTRL0_SLEEP_MODE_MASK 0x03
-
-#define RMI_SLEEP_MODE_NORMAL 0x00
-#define RMI_SLEEP_MODE_SENSOR_SLEEP 0x01
-#define RMI_SLEEP_MODE_RESERVED0 0x02
-#define RMI_SLEEP_MODE_RESERVED1 0x03
-
-#define RMI_IS_VALID_SLEEPMODE(mode) \
- (mode >= RMI_SLEEP_MODE_NORMAL && mode <= RMI_SLEEP_MODE_RESERVED1)
-
-/*
- * This bit disables whatever sleep mode may be selected by the sleep_mode
- * field and forces the device to run at full power without sleeping.
- */
-#define RMI_F01_CRTL0_NOSLEEP_BIT (1 << 2)
-
-/*
- * When this bit is set, the touch controller employs a noise-filtering
- * algorithm designed for use with a connected battery charger.
- */
-#define RMI_F01_CRTL0_CHARGER_BIT (1 << 5)
-
-/*
- * Sets the report rate for the device. The effect of this setting is
- * highly product dependent. Check the spec sheet for your particular
- * touch sensor.
- */
-#define RMI_F01_CRTL0_REPORTRATE_BIT (1 << 6)
-
-/*
- * Written by the host as an indicator that the device has been
- * successfully configured.
- */
-#define RMI_F01_CRTL0_CONFIGURED_BIT (1 << 7)
+#include "rmi_f01.h"
/**
* @ctrl0 - see the bit definitions above.
@@ -136,8 +48,7 @@ struct f01_data {
unsigned int num_of_irq_regs;
};
-static int rmi_f01_read_properties(struct rmi_device *rmi_dev,
- u16 query_base_addr,
+int rmi_f01_read_properties(struct rmi_device *rmi_dev, u16 query_base_addr,
struct f01_basic_properties *props)
{
u8 basic_query[RMI_F01_BASIC_QUERY_LEN];
@@ -180,7 +91,8 @@ static int rmi_f01_probe(struct rmi_function *fn)
{
struct rmi_device *rmi_dev = fn->rmi_dev;
struct rmi_driver_data *driver_data = dev_get_drvdata(&rmi_dev->dev);
- const struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev);
+ const struct rmi_device_platform_data *pdata =
+ rmi_get_platform_data(rmi_dev);
struct f01_data *f01;
int error;
u16 ctrl_base_addr = fn->fd.control_base_addr;
diff --git a/drivers/input/rmi4/rmi_f01.h b/drivers/input/rmi4/rmi_f01.h
new file mode 100644
index 0000000..9e5cc2b
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f01.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2014 Synaptics Incorporated
+ *
+ * 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.
+ */
+
+#ifndef _RMI_F01_H
+#define _RMI_F01_H
+
+
+#define RMI_PRODUCT_ID_LENGTH 10
+#define RMI_PRODUCT_INFO_LENGTH 2
+
+#define RMI_DATE_CODE_LENGTH 3
+
+#define PRODUCT_ID_OFFSET 0x10
+#define PRODUCT_INFO_OFFSET 0x1E
+
+
+/* Force a firmware reset of the sensor */
+#define RMI_F01_CMD_DEVICE_RESET 1
+
+/* Various F01_RMI_QueryX bits */
+
+#define RMI_F01_QRY1_CUSTOM_MAP (1 << 0)
+#define RMI_F01_QRY1_NON_COMPLIANT (1 << 1)
+#define RMI_F01_QRY1_HAS_LTS (1 << 2)
+#define RMI_F01_QRY1_HAS_SENSOR_ID (1 << 3)
+#define RMI_F01_QRY1_HAS_CHARGER_INP (1 << 4)
+#define RMI_F01_QRY1_HAS_ADJ_DOZE (1 << 5)
+#define RMI_F01_QRY1_HAS_ADJ_DOZE_HOFF (1 << 6)
+#define RMI_F01_QRY1_HAS_PROPS_2 (1 << 7)
+
+#define RMI_F01_QRY5_YEAR_MASK 0x1f
+#define RMI_F01_QRY6_MONTH_MASK 0x0f
+#define RMI_F01_QRY7_DAY_MASK 0x1f
+
+#define RMI_F01_QRY2_PRODINFO_MASK 0x7f
+
+#define RMI_F01_BASIC_QUERY_LEN 21 /* From Query 00 through 20 */
+
+struct f01_basic_properties {
+ u8 manufacturer_id;
+ bool has_lts;
+ bool has_adjustable_doze;
+ bool has_adjustable_doze_holdoff;
+ char dom[11]; /* YYYY/MM/DD + '\0' */
+ u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];
+ u16 productinfo;
+};
+
+/* F01 device status bits */
+
+/* Most recent device status event */
+#define RMI_F01_STATUS_CODE(status) ((status) & 0x0f)
+/* The device has lost its configuration for some reason. */
+#define RMI_F01_STATUS_UNCONFIGURED(status) (!!((status) & 0x80))
+
+/* Control register bits */
+
+/*
+ * Sleep mode controls power management on the device and affects all
+ * functions of the device.
+ */
+#define RMI_F01_CTRL0_SLEEP_MODE_MASK 0x03
+
+#define RMI_SLEEP_MODE_NORMAL 0x00
+#define RMI_SLEEP_MODE_SENSOR_SLEEP 0x01
+#define RMI_SLEEP_MODE_RESERVED0 0x02
+#define RMI_SLEEP_MODE_RESERVED1 0x03
+
+#define RMI_IS_VALID_SLEEPMODE(mode) \
+ (mode >= RMI_SLEEP_MODE_NORMAL && mode <= RMI_SLEEP_MODE_RESERVED1)
+
+/*
+ * This bit disables whatever sleep mode may be selected by the sleep_mode
+ * field and forces the device to run at full power without sleeping.
+ */
+#define RMI_F01_CRTL0_NOSLEEP_BIT (1 << 2)
+
+/*
+ * When this bit is set, the touch controller employs a noise-filtering
+ * algorithm designed for use with a connected battery charger.
+ */
+#define RMI_F01_CRTL0_CHARGER_BIT (1 << 5)
+
+/*
+ * Sets the report rate for the device. The effect of this setting is
+ * highly product dependent. Check the spec sheet for your particular
+ * touch sensor.
+ */
+#define RMI_F01_CRTL0_REPORTRATE_BIT (1 << 6)
+
+/*
+ * Written by the host as an indicator that the device has been
+ * successfully configured.
+ */
+#define RMI_F01_CRTL0_CONFIGURED_BIT (1 << 7)
+
+/** Read the F01 query registers and populate the basic_properties structure.
+ * @rmi_dev - the device to be queries.
+ * @query_base_addr - address of the start of the query registers.
+ * @props - pointer to the structure to be filled in.
+ */
+int rmi_f01_read_properties(struct rmi_device *rmi_dev, u16 query_base_addr,
+ struct f01_basic_properties *props);
+
+#endif
^ permalink raw reply related
* [PATCH 03/05] input synaptics-rmi4: rmi_f01 - Fix a comment and add a diagnostic output message.
From: Christopher Heiny @ 2014-03-08 2:29 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
Vivian Ly, Daniel Rosenberg, Linus Walleij, Benjamin Tissoires,
David Herrmann, Jiri Kosina
In-Reply-To: <1394245795-17347-1-git-send-email-cheiny@synaptics.com>
In debugging certain touch sensor failures, it's useful to know
whether the device is stuck in bootloader, so print a message
to that effect.
Also, point to the actual location of the defs for the F01 CTRL0
bitfields.
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>
---
drivers/input/rmi4/rmi_f01.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c
index 8504865..a078d7d 100644
--- a/drivers/input/rmi4/rmi_f01.c
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -16,7 +16,7 @@
#include "rmi_f01.h"
/**
- * @ctrl0 - see the bit definitions above.
+ * @ctrl0 - see the bit definitions in rmi_f01.h.
* @doze_interval - controls the interval between checks for finger presence
* when the touch sensor is in doze mode, in units of 10ms.
* @wakeup_threshold - controls the capacitance threshold at which the touch
@@ -415,6 +415,13 @@ static int rmi_f01_probe(struct rmi_function *fn)
return error;
}
+ driver_data->f01_bootloader_mode =
+ RMI_F01_STATUS_BOOTLOADER(device_status);
+ if (driver_data->f01_bootloader_mode)
+ dev_warn(&rmi_dev->dev,
+ "WARNING: RMI4 device is in bootloader mode!\n");
+
+
if (RMI_F01_STATUS_UNCONFIGURED(device_status)) {
dev_err(&fn->dev,
"Device was reset during configuration process, status: %#02x!\n",
^ permalink raw reply related
* [PATCH 04/05] input synaptics-rmi4: rmi_driver - Export some symbols and functions for use by reflash.
From: Christopher Heiny @ 2014-03-08 2:29 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
Vivian Ly, Daniel Rosenberg, Linus Walleij, Benjamin Tissoires,
David Herrmann, Jiri Kosina
In-Reply-To: <1394245795-17347-1-git-send-email-cheiny@synaptics.com>
Reflash functionality will need to unload the existing functions and
rescan the PDT before starting reflash; then reload the functions
afterwards.
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Signed-off-by: Vincent Huang <vincent.huang@tw.synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>
---
drivers/input/rmi4/rmi_driver.c | 165 ++++++++++++++++++++++------------------
drivers/input/rmi4/rmi_driver.h | 22 +++---
2 files changed, 101 insertions(+), 86 deletions(-)
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index 2172c80..70410e8 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -31,14 +31,20 @@
#include <uapi/linux/input.h>
#include "rmi_bus.h"
#include "rmi_driver.h"
+#include "rmi_f01.h"
#define HAS_NONSTANDARD_PDT_MASK 0x40
#define RMI4_MAX_PAGE 0xff
#define RMI4_PAGE_SIZE 0x100
#define RMI4_PAGE_MASK 0xFF00
-#define RMI_DEVICE_RESET_CMD 0x01
-#define DEFAULT_RESET_DELAY_MS 100
+#define RMI_PDT_ENTRY_SIZE 6
+#define RMI_PDT_FUNCTION_VERSION_MASK 0x60
+#define RMI_PDT_INT_SOURCE_COUNT_MASK 0x07
+
+#define PDT_START_SCAN_LOCATION 0x00e9
+#define PDT_END_SCAN_LOCATION 0x0005
+#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
#define DEFAULT_POLL_INTERVAL_MS 13
@@ -174,7 +180,7 @@ static int enable_sensor(struct rmi_device *rmi_dev)
return process_interrupt_requests(rmi_dev);
}
-static void rmi_free_function_list(struct rmi_device *rmi_dev)
+void rmi_free_function_list(struct rmi_device *rmi_dev)
{
struct rmi_function *fn, *tmp;
struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
@@ -435,8 +441,8 @@ static int rmi_driver_reset_handler(struct rmi_device *rmi_dev)
return 0;
}
-int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry,
- u16 pdt_address)
+static int rmi_read_pdt_entry(struct rmi_device *rmi_dev,
+ struct pdt_entry *entry, u16 pdt_address)
{
u8 buf[RMI_PDT_ENTRY_SIZE];
int error;
@@ -459,7 +465,6 @@ int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry,
return 0;
}
-EXPORT_SYMBOL_GPL(rmi_read_pdt_entry);
static void rmi_driver_copy_pdt_to_fd(const struct pdt_entry *pdt,
struct rmi_function_descriptor *fd)
@@ -473,9 +478,6 @@ static void rmi_driver_copy_pdt_to_fd(const struct pdt_entry *pdt,
fd->function_version = pdt->function_version;
}
-#define RMI_SCAN_CONTINUE 0
-#define RMI_SCAN_DONE 1
-
static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
int page,
void *ctx,
@@ -508,10 +510,9 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
return data->f01_bootloader_mode ? RMI_SCAN_DONE : RMI_SCAN_CONTINUE;
}
-static int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
- int (*callback)(struct rmi_device *rmi_dev,
- void *ctx,
- const struct pdt_entry *entry))
+int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
+ int (*callback)(struct rmi_device *rmi_dev,
+ void *ctx, const struct pdt_entry *entry))
{
int page;
int retval = RMI_SCAN_DONE;
@@ -525,16 +526,13 @@ static int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
return retval < 0 ? retval : 0;
}
-/* Indicates that flash programming is enabled (bootloader mode). */
-#define RMI_F01_STATUS_BOOTLOADER(status) (!!((status) & 0x40))
-
/*
* Given the PDT entry for F01, read the device status register to determine
* if we're stuck in bootloader mode or not.
*
*/
-static int rmi_check_bootloader_mode(struct rmi_device *rmi_dev,
- const struct pdt_entry *pdt)
+int rmi_check_bootloader_mode(struct rmi_device *rmi_dev,
+ const struct pdt_entry *pdt)
{
int error;
u8 device_status;
@@ -575,7 +573,7 @@ static int rmi_initial_reset(struct rmi_device *rmi_dev,
if (pdt->function_number == 0x01) {
u16 cmd_addr = pdt->page_start + pdt->command_base_addr;
- u8 cmd_buf = RMI_DEVICE_RESET_CMD;
+ u8 cmd_buf = RMI_F01_CMD_DEVICE_RESET;
const struct rmi_device_platform_data *pdata =
rmi_get_platform_data(rmi_dev);
@@ -586,7 +584,7 @@ static int rmi_initial_reset(struct rmi_device *rmi_dev,
return error;
}
- mdelay(pdata->reset_delay_ms ?: DEFAULT_RESET_DELAY_MS);
+ mdelay(pdata->reset_delay_ms ?: RMI_F01_DEFAULT_RESET_DELAY_MS);
return RMI_SCAN_DONE;
}
@@ -728,15 +726,80 @@ static int rmi_driver_remove(struct device *dev)
return 0;
}
+int rmi_driver_detect_functions(struct rmi_device *rmi_dev)
+{
+ int error;
+ int irq_count;
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ void *irq_memory;
+ size_t size;
+
+ /*
+ * We need to count the IRQs and allocate their storage before scanning
+ * the PDT and creating the function entries, because adding a new
+ * function can trigger events that result in the IRQ related storage
+ * being accessed.
+ */
+ dev_dbg(&rmi_dev->dev, "Counting IRQs.\n");
+ irq_count = 0;
+ error = rmi_scan_pdt(rmi_dev, &irq_count, rmi_count_irqs);
+ if (error) {
+ error = -ENODEV;
+ dev_err(&rmi_dev->dev, "IRQ counting failed with code %d.\n",
+ error);
+ goto err_free_mem;
+ }
+ data->irq_count = irq_count;
+ data->num_of_irq_regs = (data->irq_count + 7) / 8;
+
+ size = BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long);
+ irq_memory = kzalloc(size * 4, GFP_KERNEL);
+ if (!irq_memory) {
+ dev_err(&rmi_dev->dev, "Failed to allocate memory for irq masks.\n");
+ goto err_free_mem;
+ }
+ kfree(data->irq_status); /* free the memory if it's allocated already */
+ data->irq_status = irq_memory + size * 0;
+ data->fn_irq_bits = irq_memory + size * 1;
+ data->current_irq_mask = irq_memory + size * 2;
+ data->new_irq_mask = irq_memory + size * 3;
+
+ irq_count = 0;
+ dev_dbg(&rmi_dev->dev, "Creating functions.");
+ error = rmi_scan_pdt(rmi_dev, &irq_count, rmi_create_function);
+ if (error < 0) {
+ dev_err(&rmi_dev->dev, "Function creation failed with code %d.\n",
+ error);
+ goto err_destroy_functions;
+ }
+ if (!data->f01_container) {
+ dev_err(&rmi_dev->dev, "missing F01 container!\n");
+ error = -EINVAL;
+ goto err_destroy_functions;
+ }
+ error = rmi_read_block(rmi_dev,
+ data->f01_container->fd.control_base_addr+1,
+ data->current_irq_mask, data->num_of_irq_regs);
+ if (error) {
+ dev_err(&rmi_dev->dev, "%s: Failed to read current IRQ mask.\n",
+ __func__);
+ goto err_destroy_functions;
+ }
+ return 0;
+
+err_destroy_functions:
+ rmi_free_function_list(rmi_dev);
+ kfree(irq_memory);
+err_free_mem:
+ return error;
+}
+
static int rmi_driver_probe(struct device *dev)
{
struct rmi_driver *rmi_driver;
struct rmi_driver_data *data;
const struct rmi_device_platform_data *pdata;
struct rmi_device *rmi_dev;
- size_t size;
- void *irq_memory;
- int irq_count;
int retval;
dev_dbg(dev, "%s: Starting probe.\n", __func__);
@@ -796,59 +859,11 @@ static int rmi_driver_probe(struct device *dev)
dev_warn(dev, "Could not read PDT properties from %#06x (code %d). Assuming 0x00.\n",
PDT_PROPERTIES_LOCATION, retval);
}
-
- /*
- * We need to count the IRQs and allocate their storage before scanning
- * the PDT and creating the function entries, because adding a new
- * function can trigger events that result in the IRQ related storage
- * being accessed.
- */
- dev_dbg(dev, "Counting IRQs.\n");
- irq_count = 0;
- retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_count_irqs);
- if (retval < 0) {
- dev_err(dev, "IRQ counting for %s failed with code %d.\n",
- pdata->sensor_name, retval);
- goto err_free_mem;
- }
- data->irq_count = irq_count;
- data->num_of_irq_regs = (data->irq_count + 7) / 8;
-
mutex_init(&data->irq_mutex);
- size = BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long);
- irq_memory = kzalloc(size * 4, GFP_KERNEL);
- if (!irq_memory) {
- dev_err(dev, "Failed to allocate memory for irq masks.\n");
- goto err_free_mem;
- }
-
- data->irq_status = irq_memory + size * 0;
- data->fn_irq_bits = irq_memory + size * 1;
- data->current_irq_mask = irq_memory + size * 2;
- data->new_irq_mask = irq_memory + size * 3;
-
- irq_count = 0;
- dev_dbg(dev, "Creating functions.");
- retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_create_function);
- if (retval < 0) {
- dev_err(dev, "Function creation failed with code %d.\n",
- retval);
- goto err_destroy_functions;
- }
-
- if (!data->f01_container) {
- dev_err(dev, "Missing F01 container!\n");
- retval = -EINVAL;
- goto err_destroy_functions;
- }
-
- retval = rmi_read_block(rmi_dev,
- data->f01_container->fd.control_base_addr + 1,
- data->current_irq_mask, data->num_of_irq_regs);
- if (retval < 0) {
- dev_err(dev, "%s: Failed to read current IRQ mask.\n",
- __func__);
+ retval = rmi_driver_detect_functions(rmi_dev);
+ if (retval) {
+ dev_err(dev, "Failed to detect functions!\n");
goto err_destroy_functions;
}
@@ -918,8 +933,6 @@ static int rmi_driver_probe(struct device *dev)
err_destroy_functions:
rmi_free_function_list(rmi_dev);
- kfree(irq_memory);
-err_free_mem:
if (data->gpio_held)
gpio_free(pdata->attn_gpio);
kfree(data);
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index 34f7a7d..0cc9089 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -84,14 +84,6 @@ struct rmi_driver_data {
void *data;
};
-#define RMI_PDT_ENTRY_SIZE 6
-#define RMI_PDT_FUNCTION_VERSION_MASK 0x60
-#define RMI_PDT_INT_SOURCE_COUNT_MASK 0x07
-
-#define PDT_START_SCAN_LOCATION 0x00e9
-#define PDT_END_SCAN_LOCATION 0x0005
-#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
-
struct pdt_entry {
u16 page_start;
u8 query_base_addr;
@@ -103,8 +95,12 @@ struct pdt_entry {
u8 function_number;
};
-int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry,
- u16 pdt_address);
+#define RMI_SCAN_CONTINUE 0
+#define RMI_SCAN_DONE 1
+
+int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
+ int (*callback)(struct rmi_device *rmi_dev,
+ void *ctx, const struct pdt_entry *entry));
bool rmi_is_physical_driver(struct device_driver *);
int rmi_register_physical_driver(void);
@@ -113,4 +109,10 @@ void rmi_unregister_physical_driver(void);
int rmi_register_f01_handler(void);
void rmi_unregister_f01_handler(void);
+int check_bootloader_mode(struct rmi_device *rmi_dev,
+ const struct pdt_entry *pdt);
+void rmi_free_function_list(struct rmi_device *rmi_dev);
+int rmi_driver_detect_functions(struct rmi_device *rmi_dev);
+
+
#endif
^ permalink raw reply related
* [PATCH 05/05] input synaptics-rmi4: Add reflash support
From: Christopher Heiny @ 2014-03-08 2:29 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
Vivian Ly, Daniel Rosenberg, Linus Walleij, Benjamin Tissoires,
David Herrmann, Jiri Kosina
In-Reply-To: <1394245795-17347-1-git-send-email-cheiny@synaptics.com>
Add support for reflashing V5 bootloader firmwares into
RMI4 devices.
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Signed-off-by: Vincent Huang <vincent.huang@tw.synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Linux Walleij <linus.walleij@linaro.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>
---
drivers/input/rmi4/Kconfig | 9 +
drivers/input/rmi4/Makefile | 1 +
drivers/input/rmi4/rmi_bus.c | 3 +
drivers/input/rmi4/rmi_driver.h | 11 +
drivers/input/rmi4/rmi_fw_update.c | 961 +++++++++++++++++++++++++++++++++++++
5 files changed, 985 insertions(+)
diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
index d0c7b6e..9b88b6a 100644
--- a/drivers/input/rmi4/Kconfig
+++ b/drivers/input/rmi4/Kconfig
@@ -25,6 +25,15 @@ config RMI4_DEBUG
If unsure, say N.
+config RMI4_FWLIB
+ bool "RMI4 Firmware Update"
+ depends on RMI4_CORE
+ help
+ Say Y here to enable in-kernel firmware update capability.
+
+ Add support to the RMI4 core to enable reflashing of device
+ firmware.
+
config RMI4_I2C
tristate "RMI4 I2C Support"
depends on RMI4_CORE && I2C
diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile
index 5c6bad5..570ea80 100644
--- a/drivers/input/rmi4/Makefile
+++ b/drivers/input/rmi4/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_RMI4_CORE) += rmi_core.o
rmi_core-y := rmi_bus.o rmi_driver.o rmi_f01.o
+rmi_core-$(CONFIG_RMI4_FWLIB) += rmi_fw_update.o
# Function drivers
obj-$(CONFIG_RMI4_F11) += rmi_f11.o
diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c
index 6e0454a..3c93d08 100644
--- a/drivers/input/rmi4/rmi_bus.c
+++ b/drivers/input/rmi4/rmi_bus.c
@@ -117,6 +117,8 @@ int rmi_register_transport_device(struct rmi_transport_dev *xport)
if (error)
goto err_put_device;
+ rmi_reflash_init(rmi_dev);
+
dev_dbg(xport->dev, "%s: Registered %s as %s.\n", __func__,
pdata->sensor_name, dev_name(&rmi_dev->dev));
@@ -139,6 +141,7 @@ void rmi_unregister_transport_device(struct rmi_transport_dev *xport)
struct rmi_device *rmi_dev = xport->rmi_dev;
device_del(&rmi_dev->dev);
+ rmi_reflash_cleanup(rmi_dev);
rmi_physical_teardown_debugfs(rmi_dev);
put_device(&rmi_dev->dev);
}
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index 0cc9089..c49b123 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -29,6 +29,8 @@
#define RMI_PDT_PROPS_HAS_BSR 0x02
+struct rmi_reflash_data;
+
struct rmi_driver_data {
struct list_head function_list;
@@ -81,6 +83,8 @@ struct rmi_driver_data {
u8 reg_debug_size;
#endif
+ struct rmi_reflash_data *reflash_data;
+
void *data;
};
@@ -114,5 +118,12 @@ int check_bootloader_mode(struct rmi_device *rmi_dev,
void rmi_free_function_list(struct rmi_device *rmi_dev);
int rmi_driver_detect_functions(struct rmi_device *rmi_dev);
+#ifdef CONFIG_RMI4_FWLIB
+void rmi_reflash_init(struct rmi_device *rmi_dev);
+void rmi_reflash_cleanup(struct rmi_device *rmi_dev);
+#else
+#define rmi_reflash_init(rmi_dev)
+#define rmi_reflash_cleanup(rmi_dev)
+#endif
#endif
diff --git a/drivers/input/rmi4/rmi_fw_update.c b/drivers/input/rmi4/rmi_fw_update.c
new file mode 100644
index 0000000..7118401
--- /dev/null
+++ b/drivers/input/rmi4/rmi_fw_update.c
@@ -0,0 +1,961 @@
+/*
+ * Copyright (c) 2012-2014 Synaptics Incorporated
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/ihex.h>
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include "rmi_driver.h"
+#include "rmi_f01.h"
+
+/* F34 Query register defs. */
+
+#define RMI_F34_QUERY_SIZE 7
+#define RMI_F34_HAS_NEW_REG_MAP (1 << 0)
+#define RMI_F34_IS_UNLOCKED (1 << 1)
+#define RMI_F34_HAS_CONFIG_ID (1 << 2)
+#define RMI_F34_BLOCK_SIZE_OFFSET 1
+#define RMI_F34_FW_BLOCKS_OFFSET 3
+#define RMI_F34_CONFIG_BLOCKS_OFFSET 5
+
+struct rmi_f34_queries {
+ bool new_reg_map;
+ bool unlocked;
+ bool has_config_id;
+ u16 block_size;
+ u16 fw_block_count;
+ u16 config_block_count;
+};
+
+/* F34 Data register defs. */
+
+#define RMI_F34_BLOCK_DATA_OFFSET 2
+
+#define RMI_F34_COMMAND_MASK 0x0F
+#define RMI_F34_STATUS_MASK 0x07
+#define RMI_F34_STATUS_SHIFT 4
+#define RMI_F34_ENABLED_MASK 0x80
+
+#define RMI_F34_WRITE_FW_BLOCK 0x02
+#define RMI_F34_ERASE_ALL 0x03
+#define RMI_F34_WRITE_CONFIG_BLOCK 0x06
+#define RMI_F34_ENABLE_FLASH_PROG 0x0f
+
+struct rmi_f34_control_status {
+ u8 command;
+ u8 status;
+ bool program_enabled;
+};
+
+/* Timeouts for various F34 operations. */
+#define RMI_F34_ENABLE_WAIT_MS 300
+#define RMI_F34_ERASE_WAIT_MS (5 * 1000)
+#define RMI_F34_IDLE_WAIT_MS 500
+
+#define IS_IDLE(ctl_ptr) ((!ctl_ptr->status) && (!ctl_ptr->command))
+
+
+/* Image file defs. */
+#define RMI_IMG_CHECKSUM_OFFSET 0
+#define RMI_IMG_IO_OFFSET 0x06
+#define RMI_IMG_BOOTLOADER_VERSION_OFFSET 0x07
+#define RMI_IMG_IMAGE_SIZE_OFFSET 0x08
+#define RMI_IMG_CONFIG_SIZE_OFFSET 0x0C
+#define RMI_IMG_PACKAGE_ID_OFFSET 0x1A
+#define RMI_IMG_FW_BUILD_ID_OFFSET 0x50
+
+#define RMI_IMG_PRODUCT_INFO_LENGTH 2
+
+#define RMI_IMG_PRODUCT_ID_OFFSET 0x10
+#define RMI_IMG_PRODUCT_INFO_OFFSET 0x1E
+
+#define RMI_F34_FW_IMAGE_OFFSET 0x100
+
+/* Image file V5, Option 0 */
+struct rmi_image_header {
+ u32 checksum;
+ unsigned int image_size;
+ unsigned int config_size;
+ u8 options;
+ u8 io;
+ u32 fw_build_id;
+ u32 package_id;
+ u8 bootloader_version;
+ u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];
+ u8 product_info[RMI_IMG_PRODUCT_INFO_LENGTH];
+};
+
+static u32 rmi_extract_u32(const u8 *ptr)
+{
+ return (u32)ptr[0] +
+ (u32)ptr[1] * 0x100 +
+ (u32)ptr[2] * 0x10000 +
+ (u32)ptr[3] * 0x1000000;
+}
+
+#define RMI_NAME_BUFFER_SIZE 64
+
+struct rmi_reflash_data {
+ struct rmi_device *rmi_dev;
+ bool force;
+ ulong busy;
+ char name_buf[RMI_NAME_BUFFER_SIZE];
+ char *img_name;
+ struct pdt_entry f01_pdt;
+ struct f01_basic_properties f01_props;
+ u8 device_status;
+ struct pdt_entry f34_pdt;
+ u8 bootloader_id[2];
+ struct rmi_f34_queries f34_queries;
+ u16 f34_status_address;
+ struct rmi_f34_control_status f34_controls;
+ const u8 *firmware_data;
+ const u8 *config_data;
+ struct work_struct reflash_work;
+};
+
+static void rmi_extract_header(const u8 *data, int pos,
+ struct rmi_image_header *header)
+{
+ header->checksum =
+ rmi_extract_u32(&data[pos + RMI_IMG_CHECKSUM_OFFSET]);
+ header->io = data[pos + RMI_IMG_IO_OFFSET];
+ header->bootloader_version =
+ data[pos + RMI_IMG_BOOTLOADER_VERSION_OFFSET];
+ header->image_size =
+ rmi_extract_u32(&data[pos + RMI_IMG_IMAGE_SIZE_OFFSET]);
+ header->config_size =
+ rmi_extract_u32(&data[pos + RMI_IMG_CONFIG_SIZE_OFFSET]);
+ if (header->io == 1) {
+ header->fw_build_id =
+ rmi_extract_u32(&data[pos + RMI_IMG_FW_BUILD_ID_OFFSET]);
+ header->package_id =
+ rmi_extract_u32(&data[pos + RMI_IMG_PACKAGE_ID_OFFSET]);
+ }
+ memcpy(header->product_id, &data[pos + RMI_IMG_PRODUCT_ID_OFFSET],
+ RMI_PRODUCT_ID_LENGTH);
+ header->product_id[RMI_PRODUCT_ID_LENGTH] = 0;
+ memcpy(header->product_info, &data[pos + RMI_IMG_PRODUCT_INFO_OFFSET],
+ RMI_IMG_PRODUCT_INFO_LENGTH);
+}
+
+static int rmi_find_functions(struct rmi_device *rmi_dev,
+ void *ctx, const struct pdt_entry *pdt)
+{
+ struct rmi_reflash_data *data = ctx;
+
+ if (pdt->page_start > 0)
+ return RMI_SCAN_DONE;
+
+ if (pdt->function_number == 0x01)
+ memcpy(&data->f01_pdt, pdt, sizeof(struct pdt_entry));
+ else if (pdt->function_number == 0x34)
+ memcpy(&data->f34_pdt, pdt, sizeof(struct pdt_entry));
+
+ return RMI_SCAN_CONTINUE;
+}
+
+static int rmi_find_f01_and_f34(struct rmi_reflash_data *data)
+{
+ struct rmi_device *rmi_dev = data->rmi_dev;
+ int retval;
+
+ data->f01_pdt.function_number = data->f34_pdt.function_number = 0;
+ retval = rmi_scan_pdt(rmi_dev, data, rmi_find_functions);
+ if (retval < 0)
+ return retval;
+
+ if (!data->f01_pdt.function_number) {
+ dev_err(&rmi_dev->dev, "Failed to find F01 for reflash.\n");
+ return -ENODEV;
+ }
+
+ if (!data->f34_pdt.function_number) {
+ dev_err(&rmi_dev->dev, "Failed to find F34 for reflash.\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int rmi_read_f34_controls(struct rmi_reflash_data *data)
+{
+ int retval;
+ u8 buf;
+
+ retval = rmi_read(data->rmi_dev, data->f34_status_address, &buf);
+ if (retval)
+ return retval;
+
+ data->f34_controls.command = buf & RMI_F34_COMMAND_MASK;
+ data->f34_controls.status = (buf >> RMI_F34_STATUS_SHIFT)
+ & RMI_F34_STATUS_MASK;
+ data->f34_controls.program_enabled = !!(buf & RMI_F34_ENABLED_MASK);
+
+ return 0;
+}
+
+static int rmi_read_f01_status(struct rmi_reflash_data *data)
+{
+ int retval;
+
+ retval = rmi_read(data->rmi_dev, data->f01_pdt.data_base_addr,
+ &data->device_status);
+ if (retval)
+ return retval;
+
+ return 0;
+}
+
+#define RMI_MIN_SLEEP_TIME_US 50
+#define RMI_MAX_SLEEP_TIME_US 100
+
+/*
+ * Wait until the status is idle and we're ready to continue.
+ *
+ * In order to indicate that the previous command has succeeded, the
+ * bootloader is supposed to signal idle state by:
+ *
+ * + atomically do the following by writing 0x80 to F34_FLASH_Data3
+ * - set status to 0,
+ * - set command to 0, and
+ * - leave progam_enabled as 1
+ * + assert attn
+ *
+ * and then we're supposed to be able to see that we're in IDLE state.
+ * But some (most?) bootloaders do this
+ *
+ * + clear F34_FLASH_Data3
+ * + assert attn
+ * + set the program_enabled bit
+ *
+ * and a significant number of those don't even bother to assert
+ * ATTN, so you've got to poll them (which is what we're doing
+ * here). Regardless of whether you're polling or using ATTN,
+ * there's a race condition between clearing Data3 and setting
+ * program_enabled. So when we lose that race, we emit this warning
+ * (using dev_WARN_ONCE to avoid filling the log with complaints)
+ * and retry a few times. If a correct idle state is reached during
+ * the retries, then we just continue with the process. If it's not
+ * reached (that is, if Data3 contains anything but 0x80 after the
+ * timeout), then something has gone horribly wrong, and we abort the
+ * reflash process.
+ *
+ */
+static int rmi_wait_for_idle(struct rmi_reflash_data *data, int timeout_ms)
+{
+ int timeout_count = ((timeout_ms * 1000) / RMI_MAX_SLEEP_TIME_US) + 1;
+ int count = 0;
+ struct rmi_f34_control_status *controls = &data->f34_controls;
+ int retval;
+
+ do {
+ if (count || timeout_count == 1)
+ usleep_range(RMI_MIN_SLEEP_TIME_US,
+ RMI_MAX_SLEEP_TIME_US);
+ retval = rmi_read_f34_controls(data);
+ count++;
+ if (retval)
+ continue;
+ else if (IS_IDLE(controls)) {
+ if (dev_WARN_ONCE(&data->rmi_dev->dev,
+ !data->f34_controls.program_enabled,
+ "Reflash is idle but program_enabled bit isn't set.\n"
+ ))
+ /*
+ * This works around a bug in certain device
+ * firmwares, where the idle state is reached,
+ * but the program_enabled bit is not yet set.
+ */
+ continue;
+ return 0;
+ }
+ } while (count < timeout_count);
+
+ dev_err(&data->rmi_dev->dev,
+ "ERROR: Timeout waiting for idle status.\n");
+ dev_err(&data->rmi_dev->dev, "Command: %#04x\n", controls->command);
+ dev_err(&data->rmi_dev->dev, "Status: %#04x\n", controls->status);
+ dev_err(&data->rmi_dev->dev, "Enabled: %d\n",
+ controls->program_enabled);
+ dev_err(&data->rmi_dev->dev, "Idle: %d\n", IS_IDLE(controls));
+ return -ETIMEDOUT;
+}
+
+static int rmi_read_f34_queries(struct rmi_reflash_data *data)
+{
+ int retval;
+ u8 id_str[3];
+ u8 buf[RMI_F34_QUERY_SIZE];
+
+ retval = rmi_read_block(data->rmi_dev, data->f34_pdt.query_base_addr,
+ data->bootloader_id, 2);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev,
+ "Failed to read F34 bootloader_id (code %d).\n",
+ retval);
+ return retval;
+ }
+
+ retval = rmi_read_block(data->rmi_dev, data->f34_pdt.query_base_addr+2,
+ buf, RMI_F34_QUERY_SIZE);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev,
+ "Failed to read F34 queries (code %d).\n", retval);
+ return retval;
+ }
+
+ data->f34_queries.new_reg_map = buf[0] & RMI_F34_HAS_NEW_REG_MAP;
+ data->f34_queries.unlocked = buf[0] & RMI_F34_IS_UNLOCKED;
+ data->f34_queries.has_config_id = buf[0] & RMI_F34_HAS_CONFIG_ID;
+ data->f34_queries.block_size =
+ le16_to_cpu(*((u16 *)(buf + RMI_F34_BLOCK_SIZE_OFFSET)));
+ data->f34_queries.fw_block_count =
+ le16_to_cpu(*((u16 *)(buf + RMI_F34_FW_BLOCKS_OFFSET)));
+ data->f34_queries.config_block_count =
+ le16_to_cpu(*((u16 *)(buf + RMI_F34_CONFIG_BLOCKS_OFFSET)));
+
+ id_str[0] = data->bootloader_id[0];
+ id_str[1] = data->bootloader_id[1];
+ id_str[2] = 0;
+
+ dev_dbg(&data->rmi_dev->dev, "F34 bootloader id: %s (%#04x %#04x)\n",
+ id_str, data->bootloader_id[0], data->bootloader_id[1]);
+ dev_dbg(&data->rmi_dev->dev, "F34 has config id: %d\n",
+ data->f34_queries.has_config_id);
+ dev_dbg(&data->rmi_dev->dev, "F34 unlocked: %d\n",
+ data->f34_queries.unlocked);
+ dev_dbg(&data->rmi_dev->dev, "F34 new reg map: %d\n",
+ data->f34_queries.new_reg_map);
+ dev_dbg(&data->rmi_dev->dev, "F34 block size: %d\n",
+ data->f34_queries.block_size);
+ dev_dbg(&data->rmi_dev->dev, "F34 fw blocks: %d\n",
+ data->f34_queries.fw_block_count);
+ dev_dbg(&data->rmi_dev->dev, "F34 config blocks: %d\n",
+ data->f34_queries.config_block_count);
+
+ data->f34_status_address = data->f34_pdt.data_base_addr +
+ RMI_F34_BLOCK_DATA_OFFSET + data->f34_queries.block_size;
+
+ return 0;
+}
+
+static int rmi_write_bootloader_id(struct rmi_reflash_data *data)
+{
+ int retval;
+ struct rmi_device *rmi_dev = data->rmi_dev;
+
+ retval = rmi_write_block(rmi_dev,
+ data->f34_pdt.data_base_addr + RMI_F34_BLOCK_DATA_OFFSET,
+ data->bootloader_id, ARRAY_SIZE(data->bootloader_id));
+ if (retval < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to write bootloader ID. Code: %d.\n", retval);
+ return retval;
+ }
+
+ return 0;
+}
+
+static int rmi_write_f34_command(struct rmi_reflash_data *data, u8 command)
+{
+ int retval;
+ struct rmi_device *rmi_dev = data->rmi_dev;
+
+ retval = rmi_write(rmi_dev, data->f34_status_address, command);
+ if (retval < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to write F34 command %#04x. Code: %d.\n",
+ command, retval);
+ return retval;
+ }
+
+ return 0;
+}
+
+static int rmi_enter_flash_programming(struct rmi_reflash_data *data)
+{
+ int retval;
+ struct rmi_device *rmi_dev = data->rmi_dev;
+ u8 f01_control_0;
+
+ retval = rmi_write_bootloader_id(data);
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(&rmi_dev->dev, "Enabling flash programming.\n");
+ retval = rmi_write_f34_command(data, RMI_F34_ENABLE_FLASH_PROG);
+ if (retval < 0)
+ return retval;
+
+ retval = rmi_wait_for_idle(data, RMI_F34_ENABLE_WAIT_MS);
+ if (retval) {
+ dev_err(&rmi_dev->dev, "Did not reach idle state after %d ms. Code: %d.\n",
+ RMI_F34_ENABLE_WAIT_MS, retval);
+ return retval;
+ }
+ if (!data->f34_controls.program_enabled) {
+ dev_err(&rmi_dev->dev, "Reached idle, but programming not enabled.\n");
+ return -EINVAL;
+ }
+ dev_dbg(&rmi_dev->dev, "HOORAY! Programming is enabled!\n");
+
+ retval = rmi_find_f01_and_f34(data);
+ if (retval) {
+ dev_err(&rmi_dev->dev, "Failed to rescan pdt. Code: %d.\n",
+ retval);
+ return retval;
+ }
+
+ retval = rmi_read_f01_status(data);
+ if (retval) {
+ dev_err(&rmi_dev->dev, "Failed to read F01 status after enabling reflash. Code: %d.\n",
+ retval);
+ return retval;
+ }
+ if (!RMI_F01_STATUS_BOOTLOADER(data->device_status)) {
+ dev_err(&rmi_dev->dev, "Device reports as not in flash programming mode.\n");
+ return -EINVAL;
+ }
+
+ retval = rmi_read_f34_queries(data);
+ if (retval) {
+ dev_err(&rmi_dev->dev, "F34 queries failed, code = %d.\n",
+ retval);
+ return retval;
+ }
+
+ retval = rmi_read(rmi_dev, data->f01_pdt.control_base_addr,
+ &f01_control_0);
+ if (retval) {
+ dev_err(&rmi_dev->dev, "F01_CTRL_0 read failed, code = %d.\n",
+ retval);
+ return retval;
+ }
+ f01_control_0 |= RMI_F01_CRTL0_NOSLEEP_BIT;
+ f01_control_0 = (f01_control_0 & ~RMI_F01_CTRL0_SLEEP_MODE_MASK)
+ | RMI_SLEEP_MODE_NORMAL;
+
+ retval = rmi_write(rmi_dev, data->f01_pdt.control_base_addr,
+ f01_control_0);
+ if (retval < 0) {
+ dev_err(&rmi_dev->dev, "F01_CTRL_0 write failed, code = %d.\n",
+ retval);
+ return retval;
+ }
+
+ return 0;
+}
+
+static void rmi_reset_device(struct rmi_reflash_data *data)
+{
+ int retval;
+ const struct rmi_device_platform_data *pdata =
+ rmi_get_platform_data(data->rmi_dev);
+
+ dev_dbg(&data->rmi_dev->dev, "Resetting...\n");
+ retval = rmi_write(data->rmi_dev, data->f01_pdt.command_base_addr,
+ RMI_F01_CMD_DEVICE_RESET);
+ if (retval < 0)
+ dev_warn(&data->rmi_dev->dev,
+ "WARNING - post-flash reset failed, code: %d.\n",
+ retval);
+ msleep(pdata->reset_delay_ms ?: RMI_F01_DEFAULT_RESET_DELAY_MS);
+ dev_dbg(&data->rmi_dev->dev, "Reset completed.\n");
+}
+
+/*
+ * Send data to the device one block at a time.
+ */
+static int rmi_write_blocks(struct rmi_reflash_data *data, u8 *block_ptr,
+ u16 block_count, u8 cmd)
+{
+ int block_num;
+ u8 zeros[] = {0, 0};
+ int retval;
+ u16 addr = data->f34_pdt.data_base_addr + RMI_F34_BLOCK_DATA_OFFSET;
+
+ retval = rmi_write_block(data->rmi_dev, data->f34_pdt.data_base_addr,
+ zeros, ARRAY_SIZE(zeros));
+ if (retval < 0) {
+ dev_err(&data->rmi_dev->dev, "Failed to write initial zeros. Code=%d.\n",
+ retval);
+ return retval;
+ }
+
+ for (block_num = 0; block_num < block_count; ++block_num) {
+ retval = rmi_write_block(data->rmi_dev, addr, block_ptr,
+ data->f34_queries.block_size);
+ if (retval < 0) {
+ dev_err(&data->rmi_dev->dev, "Failed to write block %d. Code=%d.\n",
+ block_num, retval);
+ return retval;
+ }
+
+ retval = rmi_write_f34_command(data, cmd);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev, "Failed to write command for block %d. Code=%d.\n",
+ block_num, retval);
+ return retval;
+ }
+
+
+ retval = rmi_wait_for_idle(data, RMI_F34_IDLE_WAIT_MS);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev, "Failed to go idle after writing block %d. Code=%d.\n",
+ block_num, retval);
+ return retval;
+ }
+
+ block_ptr += data->f34_queries.block_size;
+ }
+
+ return 0;
+}
+
+static int rmi_write_firmware(struct rmi_reflash_data *data)
+{
+ return rmi_write_blocks(data, (u8 *) data->firmware_data,
+ data->f34_queries.fw_block_count, RMI_F34_WRITE_FW_BLOCK);
+}
+
+static int rmi_write_configuration(struct rmi_reflash_data *data)
+{
+ return rmi_write_blocks(data, (u8 *) data->config_data,
+ data->f34_queries.config_block_count,
+ RMI_F34_WRITE_CONFIG_BLOCK);
+}
+
+static void rmi_reflash_firmware(struct rmi_reflash_data *data)
+{
+ struct timespec start;
+ struct timespec end;
+ s64 duration_ns;
+ int retval = 0;
+
+ retval = rmi_enter_flash_programming(data);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev, "Failed to enter flash programming (code: %d).\n",
+ retval);
+ return;
+ }
+
+ retval = rmi_write_bootloader_id(data);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev, "Failed to enter write bootloader ID (code: %d).\n",
+ retval);
+ return;
+ }
+
+ dev_dbg(&data->rmi_dev->dev, "Erasing FW...\n");
+ getnstimeofday(&start);
+ retval = rmi_write_f34_command(data, RMI_F34_ERASE_ALL);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev, "Erase failed (code: %d).\n",
+ retval);
+ return;
+ }
+
+ retval = rmi_wait_for_idle(data, RMI_F34_ERASE_WAIT_MS);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev,
+ "Failed to reach idle state. Code: %d.\n", retval);
+ return;
+ }
+ getnstimeofday(&end);
+ duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+ dev_dbg(&data->rmi_dev->dev,
+ "Erase complete, time: %lld ns.\n", duration_ns);
+
+ if (data->firmware_data) {
+ dev_dbg(&data->rmi_dev->dev, "Writing firmware...\n");
+ getnstimeofday(&start);
+ retval = rmi_write_firmware(data);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev,
+ "Failed to write FW (code: %d).\n", retval);
+ return;
+ }
+ getnstimeofday(&end);
+ duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+ dev_dbg(&data->rmi_dev->dev,
+ "Done writing FW, time: %lld ns.\n", duration_ns);
+ }
+
+ if (data->config_data) {
+ dev_dbg(&data->rmi_dev->dev, "Writing configuration...\n");
+ getnstimeofday(&start);
+ retval = rmi_write_configuration(data);
+ if (retval) {
+ dev_err(&data->rmi_dev->dev,
+ "Failed to write config (code: %d).\n", retval);
+ return;
+ }
+ getnstimeofday(&end);
+ duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+ dev_dbg(&data->rmi_dev->dev,
+ "Done writing config, time: %lld ns.\n", duration_ns);
+ }
+}
+
+static bool rmi_go_nogo(struct rmi_reflash_data *data,
+ struct rmi_image_header *header)
+{
+ if (data->force) {
+ dev_dbg(&data->rmi_dev->dev, "Reflash force flag in effect.\n");
+ return true;
+ }
+
+ if (header->io == 1) {
+ if (header->fw_build_id > data->f01_props.build_id) {
+ dev_dbg(&data->rmi_dev->dev, "Image file has newer Packrat.\n");
+ return true;
+ } else {
+ dev_dbg(&data->rmi_dev->dev, "Image file has lower Packrat ID than device.\n");
+ }
+ }
+
+ return false;
+}
+
+static const char rmi_fw_name_format[] = "%s.img";
+
+static void rmi_fw_update(struct rmi_device *rmi_dev)
+{
+ struct timespec start;
+ struct timespec end;
+ s64 duration_ns;
+ char *firmware_name;
+ const struct firmware *fw_entry = NULL;
+ int retval;
+ struct rmi_image_header *header = NULL;
+ u8 pdt_props;
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_reflash_data *data = drv_data->reflash_data;
+ const struct rmi_device_platform_data *pdata =
+ rmi_get_platform_data(rmi_dev);
+
+ dev_dbg(&rmi_dev->dev, "%s called.\n", __func__);
+ dev_dbg(&rmi_dev->dev, "force: %d\n", data->force);
+ dev_dbg(&rmi_dev->dev, "img_name: %s\n", data->img_name);
+ dev_dbg(&rmi_dev->dev, "firmware_name: %s\n", pdata->firmware_name);
+
+ getnstimeofday(&start);
+
+ firmware_name = kcalloc(RMI_NAME_BUFFER_SIZE, sizeof(char), GFP_KERNEL);
+ if (!firmware_name) {
+ dev_err(&rmi_dev->dev, "Failed to allocate firmware_name.\n");
+ goto done;
+ }
+ header = kzalloc(sizeof(struct rmi_image_header), GFP_KERNEL);
+ if (!header) {
+ dev_err(&rmi_dev->dev, "Failed to allocate header.\n");
+ goto done;
+ }
+
+ retval = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION, &pdt_props);
+ if (retval) {
+ dev_warn(&rmi_dev->dev,
+ "Failed to read PDT props at %#06x (code %d). Assuming 0x00.\n",
+ PDT_PROPERTIES_LOCATION, retval);
+ }
+ if (pdt_props & RMI_PDT_PROPS_HAS_BSR) {
+ dev_warn(&rmi_dev->dev,
+ "Firmware update for LTS not currently supported.\n");
+ goto done;
+ }
+
+ retval = rmi_f01_read_properties(rmi_dev, data->f01_pdt.query_base_addr,
+ &data->f01_props);
+ if (retval) {
+ dev_err(&rmi_dev->dev, "F01 queries failed, code = %d.\n",
+ retval);
+ goto done;
+ }
+ retval = rmi_read_f34_queries(data);
+ if (retval) {
+ dev_err(&rmi_dev->dev, "F34 queries failed, code = %d.\n",
+ retval);
+ goto done;
+ }
+ if (data->img_name && strlen(data->img_name))
+ snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
+ rmi_fw_name_format, data->img_name);
+ else if (pdata->firmware_name && strlen(pdata->firmware_name))
+ snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
+ rmi_fw_name_format, pdata->firmware_name);
+ else {
+ if (!strlen(data->f01_props.product_id)) {
+ dev_err(&rmi_dev->dev, "Product ID is missing or empty - will not reflash.\n");
+ goto done;
+ }
+ snprintf(firmware_name, RMI_NAME_BUFFER_SIZE,
+ rmi_fw_name_format, data->f01_props.product_id);
+ }
+ dev_info(&rmi_dev->dev, "Requesting %s.\n", firmware_name);
+ retval = request_firmware(&fw_entry, firmware_name, &rmi_dev->dev);
+ if (retval != 0) {
+ dev_err(&rmi_dev->dev, "Firmware %s not available, code = %d\n",
+ firmware_name, retval);
+ goto done;
+ }
+
+ dev_dbg(&rmi_dev->dev, "Got firmware %s, size: %d.\n", firmware_name,
+ fw_entry->size);
+ rmi_extract_header(fw_entry->data, 0, header);
+ dev_dbg(&rmi_dev->dev, "Img checksum: %#08X\n",
+ header->checksum);
+ dev_dbg(&rmi_dev->dev, "Img io: %#04X\n",
+ header->io);
+ dev_dbg(&rmi_dev->dev, "Img image size: %d\n",
+ header->image_size);
+ dev_dbg(&rmi_dev->dev, "Img config size: %d\n",
+ header->config_size);
+ dev_dbg(&rmi_dev->dev, "Img bootloader version: %d\n",
+ header->bootloader_version);
+ dev_dbg(&rmi_dev->dev, "Img product id: %s\n",
+ header->product_id);
+ dev_dbg(&rmi_dev->dev, "Img product info: %#04x %#04x\n",
+ header->product_info[0], header->product_info[1]);
+ if (header->io == 1) {
+ dev_dbg(&rmi_dev->dev, "Img Packrat: %d\n",
+ header->fw_build_id);
+ dev_dbg(&rmi_dev->dev, "Img package: %d\n",
+ header->package_id);
+ }
+
+ if (header->image_size)
+ data->firmware_data = fw_entry->data + RMI_F34_FW_IMAGE_OFFSET;
+ if (header->config_size)
+ data->config_data = fw_entry->data + RMI_F34_FW_IMAGE_OFFSET +
+ header->image_size;
+
+ if (rmi_go_nogo(data, header)) {
+ dev_dbg(&rmi_dev->dev, "Go/NoGo said go.\n");
+ rmi_free_function_list(rmi_dev);
+ rmi_reflash_firmware(data);
+ rmi_reset_device(data);
+ rmi_driver_detect_functions(rmi_dev);
+ } else
+ dev_dbg(&rmi_dev->dev, "Go/NoGo said don't reflash.\n");
+
+ if (fw_entry)
+ release_firmware(fw_entry);
+
+
+done:
+ getnstimeofday(&end);
+ duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+ dev_dbg(&rmi_dev->dev, "Time to reflash: %lld ns.\n", duration_ns);
+
+ kfree(firmware_name);
+ kfree(header);
+ return;
+}
+
+/*
+ * We also reflash the device if (a) in kernel reflashing is
+ * enabled, and (b) the reflash module decides it requires reflashing.
+ *
+ * We have to do this before actually building the PDT because the reflash
+ * might cause various registers to move around.
+ */
+static int rmi_device_reflash(struct rmi_device *rmi_dev)
+{
+ struct device *dev = &rmi_dev->dev;
+ struct rmi_driver_data *drv_data = dev_get_drvdata(dev);
+ struct rmi_reflash_data *data = drv_data->reflash_data;
+ int retval;
+
+ retval = rmi_find_f01_and_f34(data);
+ if (retval < 0)
+ return retval;
+
+ rmi_fw_update(rmi_dev);
+
+ clear_bit(0, &data->busy);
+
+ return 0;
+}
+
+static ssize_t rmi_reflash_img_name_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_reflash_data *data = drv_data->reflash_data;
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", data->img_name);
+}
+
+static ssize_t rmi_reflash_img_name_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_reflash_data *data = drv_data->reflash_data;
+
+ if (test_and_set_bit(0, &data->busy))
+ return -EBUSY;
+
+ if (!count) {
+ data->img_name = NULL;
+ } else {
+ strlcpy(data->name_buf, buf, count);
+ data->img_name = strstrip(data->name_buf);
+ }
+
+ clear_bit(0, &data->busy);
+ return count;
+}
+
+static ssize_t rmi_reflash_force_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_reflash_data *data = drv_data->reflash_data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", data->force);
+}
+
+static ssize_t rmi_reflash_force_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_reflash_data *data = drv_data->reflash_data;
+ int retval;
+ unsigned long val;
+
+ if (test_and_set_bit(0, &data->busy))
+ return -EBUSY;
+
+ retval = kstrtoul(buf, 10, &val);
+ if (retval)
+ count = retval;
+ else
+ data->force = !!val;
+
+ clear_bit(0, &data->busy);
+
+ return count;
+}
+
+static ssize_t rmi_reflash_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_reflash_data *data = drv_data->reflash_data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", test_bit(0, &data->busy));
+}
+
+static ssize_t rmi_reflash_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int retval;
+ unsigned long val;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_reflash_data *data = drv_data->reflash_data;
+
+ retval = kstrtoul(buf, 10, &val);
+ if (retval)
+ return retval;
+
+ if (test_and_set_bit(0, &data->busy))
+ return -EBUSY;
+
+ if (val)
+ /*
+ * TODO: Here we start a work thread to go do the reflash, but
+ * maybe we can just use request_firmware_timeout().
+ */
+ schedule_work(&data->reflash_work);
+ else
+ clear_bit(0, &data->busy);
+
+ return count;
+}
+
+static void rmi_reflash_work(struct work_struct *work)
+{
+ struct rmi_reflash_data *data =
+ container_of(work, struct rmi_reflash_data, reflash_work);
+ struct rmi_device *rmi_dev = data->rmi_dev;
+ int error;
+
+ dev_dbg(&rmi_dev->dev, "%s runs.\n", __func__);
+ error = rmi_device_reflash(rmi_dev);
+ if (error < 0)
+ dev_err(&rmi_dev->dev, "Reflash attempt failed with code: %d.",
+ error);
+ clear_bit(0, &data->busy);
+}
+
+static DEVICE_ATTR(reflash_force,
+ (S_IRUGO | S_IWUGO),
+ rmi_reflash_force_show, rmi_reflash_force_store);
+static DEVICE_ATTR(reflash_name,
+ (S_IRUGO | S_IWUGO),
+ rmi_reflash_img_name_show, rmi_reflash_img_name_store);
+static DEVICE_ATTR(reflash,
+ (S_IRUGO | S_IWUGO),
+ rmi_reflash_show, rmi_reflash_store);
+
+static struct attribute *rmi_reflash_attrs[] = {
+ &dev_attr_reflash_force.attr,
+ &dev_attr_reflash_name.attr,
+ &dev_attr_reflash.attr,
+ NULL
+};
+
+static const struct attribute_group rmi_reflash_attributes = {
+ .attrs = rmi_reflash_attrs,
+};
+
+void rmi_reflash_init(struct rmi_device *rmi_dev)
+{
+ int error;
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_reflash_data *data;
+
+ dev_dbg(&rmi_dev->dev, "%s called.\n", __func__);
+
+ data = devm_kzalloc(&rmi_dev->dev, sizeof(struct rmi_reflash_data),
+ GFP_KERNEL);
+
+ error = sysfs_create_group(&rmi_dev->dev.kobj, &rmi_reflash_attributes);
+ if (error) {
+ dev_warn(&rmi_dev->dev, "Failed to create reflash sysfs attributes.\n");
+ return;
+ }
+
+ INIT_WORK(&data->reflash_work, rmi_reflash_work);
+ data->rmi_dev = rmi_dev;
+ drv_data->reflash_data = data;
+}
+
+void rmi_reflash_cleanup(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_reflash_data *data = drv_data->reflash_data;
+
+ sysfs_remove_group(&rmi_dev->dev.kobj, &rmi_reflash_attributes);
+ devm_kfree(&rmi_dev->dev, data);
+}
^ permalink raw reply related
* Re: Suspending access to opened/active /dev/nodes during application runtime
From: Lennart Poettering @ 2014-03-08 2:39 UTC (permalink / raw)
To: Lukasz Pawelczyk
Cc: libvir-list, systemd-devel, linux-kernel, lxc-devel, linux-input
In-Reply-To: <9E972401-6FA3-439B-9531-49D1FCC8D61D@gmail.com>
On Fri, 07.03.14 21:51, Lukasz Pawelczyk (havner@gmail.com) wrote:
> >> Problem:
> >> Has anyone thought about a mechanism to limit/remove an access to a
> >> device during an application runtime? Meaning we have an
> >> application that has an open file descriptor to some /dev/node and
> >> depending on *something* it gains or looses the access to it
> >> gracefully (with or without a notification, but without any fatal
> >> consequences).
> >
> > logind can mute input devices as sessions are switched, to enable
> > unpriviliged X11 and wayland compositors.
>
> Would you please elaborate on this? Where is this mechanism? How does
> it work without kernel space support? Is there some kernel space
> support I’m not aware of?
There's EVIOCREVOKE for input devices and
DRM_IOCTL_SET_MASTER/DRM_IOCTL_DROP_MASTER for DRM devices. See logind
sources.
> > Before you think about doing something like this, you need to fix the
> > kernel to provide namespaced devices (good luck!)
>
> Precisly! That’s the generic idea. I’m not for implementing it though
> at this moment. I just wanted to know whether anybody actually though
> about it or maybe someone is interested in starting such a work, etc.
It's not just about turning on and turning off access to the event
stream. It's mostly about enumeration and probing which doesn't work in
containers, and is particularly messy if you intend to share devices
between containers.
> > logind can do this for you between sessions. But such a container setup
> > will never work without proper device namespacing.
>
> So how can it do it when there is no kernel support? You mean it could
> be doing this if the support were there?
EVIOCREVOKE and the DRM ioctls are pretty real...
Lennart
--
Lennart Poettering, Red Hat
_______________________________________________
systemd-devel mailing list
systemd-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/systemd-devel
^ permalink raw reply
* RE: [RFC V1 2/2] input: misc: da9063: OnKey driver
From: Opensource [Steve Twiss] @ 2014-03-08 10:17 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: David Dajun Chen, LKML-INPUT, LKML-KERNEL
In-Reply-To: <20140307174614.GA25283@core.coreip.homeip.net>
On 07 March 2014 17:46, Dmitry Torokhov wrote:
>Hi Steve,
>
>On Fri, Mar 07, 2014 at 12:37:45PM +0000, Opensource [Steve Twiss] wrote:
>> From: Opensource [Steve Twiss] <stwiss.opensource@diasemi.com>
>>
>> Add the OnKey driver for DA9063.
>>
>>
>> Signed-off-by: Opensource [Steve Twiss] <stwiss.opensource@diasemi.com>
>> ---
>> Checks performed with next-20140307/scripts/checkpatch.pl
>> da9063-onkey.c total: 0 errors, 0 warnings, 202 lines checked
>> Kconfig total: 0 errors, 11 warnings, 679 lines checked
>> Makefile total: 0 errors, 0 warnings, 66 lines checked
>>
>> Hello,
>>
>> This is a RFC for the Dialog DA9063 Onkey driver.
>>
>> Dependencies:
>>
>> This driver makes use of the name field "ONKEY" as part of the
>> function call: platform_get_irq_byname();
>>
>> This driver is therefore dependent on this change to the name field
>> in the properties of the the OnKey IORESOURCE_IRQ resource structure
>> (part of the mfd_cell Onkey resource inside da9063-core.c). This
>> change will be added as part of the final set when I submit all patches
>> as a single PATCH set.
>>
>> This patch applies against kernel version linux-next next-20140307
>>
>> Regards,
>> Steve Twiss, Dialog Semiconductor Ltd.
>>
>>
>>
>> drivers/input/misc/Kconfig | 10 ++
>> drivers/input/misc/Makefile | 1 +
>> drivers/input/misc/da9063-onkey.c | 202
>+++++++++++++++++++++++++++++++++++++
>> 3 files changed, 213 insertions(+)
>> create mode 100644 drivers/input/misc/da9063-onkey.c
>>
>> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
>> index 762e6d2..3deb008 100644
>> --- a/drivers/input/misc/Kconfig
>> +++ b/drivers/input/misc/Kconfig
>> @@ -522,6 +522,16 @@ config INPUT_DA9055_ONKEY
>> To compile this driver as a module, choose M here: the module
>> will be called da9055_onkey.
>>
>> +config INPUT_DA9063_ONKEY
>> + tristate "Dialog DA9063 OnKey"
>> + depends on MFD_DA9063
>> + help
>> + Support the ONKEY of Dialog DA9063 Power Management IC as an
>> + input device reporting power button statue.
>> +
>> + To compile this driver as a module, choose M here: the module
>> + will be called da9063-onkey.
>> +
>> config INPUT_DM355EVM
>> tristate "TI DaVinci DM355 EVM Keypad and IR Remote"
>> depends on MFD_DM355EVM_MSP
>> diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
>> index cda71fc..f40caa7 100644
>> --- a/drivers/input/misc/Makefile
>> +++ b/drivers/input/misc/Makefile
>> @@ -25,6 +25,7 @@ obj-$(CONFIG_INPUT_CMA3000_I2C) +=
>cma3000_d0x_i2c.o
>> obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
>> obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o
>> obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o
>> +obj-$(CONFIG_INPUT_DA9063_ONKEY) += da9063-onkey.o
>> obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
>> obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o
>> obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o
>> diff --git a/drivers/input/misc/da9063-onkey.c b/drivers/input/misc/da9063-
>onkey.c
>> new file mode 100644
>> index 0000000..654f52b
>> --- /dev/null
>> +++ b/drivers/input/misc/da9063-onkey.c
>> @@ -0,0 +1,202 @@
>> +/* da9063-onkey.c - Onkey device driver for DA9063
>> + * Copyright (C) 2013 Dialog Semiconductor Ltd.
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Library General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2 of the License, or (at your option) any later version.
>> + *
>> + * This library 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
>> + * Library General Public License for more details.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/errno.h>
>> +#include <linux/input.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/workqueue.h>
>> +#include <linux/regmap.h>
>> +
>> +#include <linux/mfd/da9063/core.h>
>> +#include <linux/mfd/da9063/pdata.h>
>> +#include <linux/mfd/da9063/registers.h>
>> +
>> +struct da9063_onkey {
>> + struct da9063 *hw;
>> + struct delayed_work work;
>> + struct input_dev *input;
>> + bool key_power;
>> +};
>> +
>> +static void da9063_poll_on(struct work_struct *work)
>> +{
>> + struct da9063_onkey *onkey = container_of(work, struct da9063_onkey,
>> + work.work);
>> + unsigned int val;
>> + int poll = 1;
>
>Please use booleans for boolean data:
>
> bool poll = true;
>
Yes, I'll add those changes.
>> + int ret;
>> +
>> + /* poll to see when the pin is deasserted */
>> + ret = regmap_read(onkey->hw->regmap, DA9063_REG_STATUS_A, &val);
>> + if (ret < 0) {
>> + dev_err(&onkey->input->dev,
>> + "Failed to read ON status: %d\n", ret);
>> + goto err_poll;
>> + }
>> +
>> + if (!(val & DA9063_NONKEY)) {
>> + ret = regmap_update_bits(onkey->hw->regmap,
>> + DA9063_REG_CONTROL_B,
>> + DA9063_NONKEY_LOCK, 0);
>> + if (ret < 0) {
>> + dev_err(&onkey->input->dev,
>> + "Failed to reset the Key Delay %d\n", ret);
>> + goto err_poll;
>> + }
>> +
>> + input_report_key(onkey->input, KEY_POWER, 0);
>> + input_sync(onkey->input);
>
>This is confusing. Why we report key only if we successfully did the
>first write but we do not care about the second?
>
I'll clarify this ..
>> +
>> + /* unmask the onkey interrupt again */
>> + ret = regmap_update_bits(onkey->hw->regmap,
>> + DA9063_REG_IRQ_MASK_A,
>> + DA9063_NONKEY, 0);
>> + if (ret < 0) {
>> + dev_err(&onkey->input->dev,
>> + "Failed to unmask the onkey IRQ: %d\n", ret);
>> + goto err_poll;
>> + }
>> +
>> + poll = 0;
>
> poll = false;
>
Yep..
This also
>> + }
>> +
>> +err_poll:
>> + if (poll)
>> + schedule_delayed_work(&onkey->work, 50);
>> +}
>> +
>> +static irqreturn_t da9063_onkey_irq_handler(int irq, void *data)
>> +{
>> + struct da9063_onkey *onkey = data;
>> + unsigned int val;
>> + int ret;
>> +
>> + ret = regmap_read(onkey->hw->regmap, DA9063_REG_STATUS_A, &val);
>> + if (onkey->key_power && (ret >= 0) && (val & DA9063_NONKEY)) {
>> + ret = regmap_update_bits(onkey->hw->regmap,
>> + DA9063_REG_IRQ_MASK_A,
>> + DA9063_NONKEY, 1);
>> + if (ret < 0)
>> + dev_err(&onkey->input->dev,
>> + "Failed to mask the onkey IRQ: %d\n", ret);
>> +
>> + input_report_key(onkey->input, KEY_POWER, 1);
>> + input_sync(onkey->input);
>> +
>> + schedule_delayed_work(&onkey->work, 0);
>> + dev_dbg(&onkey->input->dev, "KEY_POWER pressed.\n");
>> + } else {
>> + input_report_key(onkey->input, KEY_SLEEP, 1);
>
>You need input_sync() here as well.
>
Okay, thanks.
>> + input_report_key(onkey->input, KEY_SLEEP, 0);
>> + input_sync(onkey->input);
>> + dev_dbg(&onkey->input->dev, "KEY_SLEEP pressed.\n");
>> + }
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static int da9063_onkey_probe(struct platform_device *pdev)
>> +{
>> + struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent);
>> + struct da9063_pdata *pdata = dev_get_platdata(da9063->dev);
>> + struct da9063_onkey *onkey;
>> + bool kp_tmp = true;
>> + int ret = 0;
>> + int irq;
>> +
>> + if (pdata)
>> + kp_tmp = pdata->key_power;
>> +
>> + if (!kp_tmp)
>> + dev_err(&pdev->dev,
>> + "Software power down key is not set.\n");
>
>Why is this an error?
>
Um, yeah, this isn't an error at all.
Sorry
>> +
>> + onkey = devm_kzalloc(&pdev->dev, sizeof(struct da9063_onkey),
>> + GFP_KERNEL);
>> + if (!onkey) {
>> + dev_err(&pdev->dev, "Failed to allocate memory.\n");
>> + return -ENOMEM;
>> + }
>> +
>> + INIT_DELAYED_WORK(&onkey->work, da9063_poll_on);
>> +
>> + onkey->input = devm_input_allocate_device(&pdev->dev);
>> + if (!onkey->input) {
>> + dev_err(&pdev->dev, "Failed to allocated input device.\n");
>> + return -ENOMEM;
>> + }
>> +
>> + irq = platform_get_irq_byname(pdev, "ONKEY");
>> + if (irq < 0) {
>> + dev_err(&pdev->dev, "Failed to get platform IRQ.\n");
>> + return irq;
>> + }
>> +
>> + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
>> + da9063_onkey_irq_handler,
>> + IRQF_TRIGGER_LOW | IRQF_ONESHOT,
>> + "ONKEY", onkey);
>> + if (ret) {
>> + dev_err(&pdev->dev,
>> + "Failed to request input device IRQ.\n");
>> + return ret;
>> + }
>> +
>> + onkey->hw = da9063;
>> + onkey->key_power = kp_tmp;
>> + onkey->input->evbit[0] = BIT_MASK(EV_KEY);
>> + onkey->input->name = DA9063_DRVNAME_ONKEY;
>> + onkey->input->phys = DA9063_DRVNAME_ONKEY "/input0";
>> + onkey->input->dev.parent = &pdev->dev;
>> +
>> + if (onkey->key_power)
>> + input_set_capability(onkey->input, EV_KEY, KEY_POWER);
>> + input_set_capability(onkey->input, EV_KEY, KEY_SLEEP);
>> +
>> + ret = input_register_device(onkey->input);
>> + if (ret) {
>> + dev_err(&pdev->dev,
>> + "Failed to register input device.\n");
>> + return ret;
>> + }
>> +
>> + platform_set_drvdata(pdev, onkey);
>> + return ret;
>> +}
>> +
>> +static int da9063_onkey_remove(struct platform_device *pdev)
>> +{
>> + struct da9063_onkey *onkey = platform_get_drvdata(pdev);
>> + cancel_delayed_work_sync(&onkey->work);
>
>This is racy. Nothing stops IRQ from firing again and rescheduling the
>work item. You are also missing canceling work item in error path pf
>probe().
>
Ah.
I will look at this whole item again, especially the error paths
>> + input_unregister_device(onkey->input);
>> + return 0;
>> +}
>> +
>> +static struct platform_driver da9063_onkey_driver = {
>> + .probe = da9063_onkey_probe,
>> + .remove = da9063_onkey_remove,
>> + .driver = {
>> + .name = DA9063_DRVNAME_ONKEY,
>> + .owner = THIS_MODULE,
>> + },
>> +};
>> +
>> +module_platform_driver(da9063_onkey_driver);
>> +
>> +MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");
>> +MODULE_DESCRIPTION("Onkey device driver for Dialog DA9063");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("platform:" DA9063_DRVNAME_ONKEY);
>> --
>> end-of-patch for RFC V1
>>
>
>Thanks.
>
>--
>Dmitry
Thank you for taking the time to work through this.
I will make the changes you have listed and then resubmit.
Regards,
Steve
^ permalink raw reply
* Re: [PATCH 1/7] mfd: AXP20x: Add mfd driver for AXP20x PMIC
From: Carlo Caione @ 2014-03-08 11:31 UTC (permalink / raw)
To: Maxime Ripard
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
hdegoede-H+wXaHxf7aLQT0dZR+AlfA, emilio-0Z03zUJReD5OxF6Tv1QG9Q,
wens-jdAy2FN1RRM, sameo-VuQAYsv1563Yd54FQh9/CA,
lee.jones-QSEj5FYQhm4dnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA,
dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A
In-Reply-To: <20140307180936.GY607@lukather>
On Fri, Mar 07, 2014 at 07:09:36PM +0100, Maxime Ripard wrote:
> Hi,
>
> On Sat, Mar 01, 2014 at 05:45:46PM +0100, Carlo Caione wrote:
> > This patch introduces the preliminary support for PMICs X-Powers AXP202
> > and AXP209. The AXP209 and AXP202 are the PMUs (Power Management Unit)
> > used by A10, A13 and A20 SoCs and developed by X-Powers, a sister company
> > of Allwinner.
> >
> > The core enables support for two subsystems:
> > - PEK (Power Enable Key)
> > - Regulators
> >
> > Signed-off-by: Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
> > ---
> > arch/arm/configs/sunxi_defconfig | 1 +
> > drivers/mfd/Kconfig | 12 ++
> > drivers/mfd/Makefile | 1 +
> > drivers/mfd/axp20x.c | 250 +++++++++++++++++++++++++++++++++++++++
> > include/linux/mfd/axp20x.h | 180 ++++++++++++++++++++++++++++
> > 5 files changed, 444 insertions(+)
> > create mode 100644 drivers/mfd/axp20x.c
> > create mode 100644 include/linux/mfd/axp20x.h
> >
> > diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig
> > index 3e2259b..f8aa7e6 100644
> > --- a/arch/arm/configs/sunxi_defconfig
> > +++ b/arch/arm/configs/sunxi_defconfig
> > @@ -52,6 +52,7 @@ CONFIG_GPIO_SYSFS=y
> > # CONFIG_HWMON is not set
> > CONFIG_WATCHDOG=y
> > CONFIG_SUNXI_WATCHDOG=y
> > +CONFIG_MFD_AXP20X=y
> > # CONFIG_USB_SUPPORT is not set
> > CONFIG_NEW_LEDS=y
> > CONFIG_LEDS_CLASS=y
>
> Please provide a separate patch for both sunxi_defconfig and
> multi_v7_defconfig.
Ok
> > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> > index dd67158..33d38c4 100644
> > --- a/drivers/mfd/Kconfig
> > +++ b/drivers/mfd/Kconfig
> > @@ -59,6 +59,18 @@ config MFD_AAT2870_CORE
> > additional drivers must be enabled in order to use the
> > functionality of the device.
> >
> > +config MFD_AXP20X
> > + bool "X-Powers AXP20X"
> > + select MFD_CORE
> > + select REGMAP_I2C
> > + select REGMAP_IRQ
> > + depends on I2C=y
> > + help
> > + If you say Y here you get support for the AXP20X.
> > + This driver provides common support for accessing the device,
> > + additional drivers must be enabled in order to use the
> > + functionality of the device.
> > +
> > config MFD_CROS_EC
> > tristate "ChromeOS Embedded Controller"
> > select MFD_CORE
> > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> > index 8a28dc9..371020e 100644
> > --- a/drivers/mfd/Makefile
> > +++ b/drivers/mfd/Makefile
> > @@ -101,6 +101,7 @@ obj-$(CONFIG_PMIC_DA9052) += da9052-irq.o
> > obj-$(CONFIG_PMIC_DA9052) += da9052-core.o
> > obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o
> > obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o
> > +obj-$(CONFIG_MFD_AXP20X) += axp20x.o
> >
> > obj-$(CONFIG_MFD_LP8788) += lp8788.o lp8788-irq.o
> >
> > diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
> > new file mode 100644
> > index 0000000..92e5b0f
> > --- /dev/null
> > +++ b/drivers/mfd/axp20x.c
> > @@ -0,0 +1,250 @@
> > +/*
> > + * axp20x.c - mfd core driver for the X-Powers AXP202 and AXP209
> > + *
> > + * Author: Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
> > + *
> > + * 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.
> > + */
> > +
> > +#include <linux/err.h>
> > +#include <linux/i2c.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <linux/slab.h>
> > +#include <linux/regulator/consumer.h>
> > +#include <linux/mfd/axp20x.h>
> > +#include <linux/mfd/core.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of_irq.h>
> > +
> > +#define AXP20X_OFF 0x80
> > +
> > +static const struct regmap_range axp20x_writeable_ranges[] = {
> > + regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE),
> > + regmap_reg_range(AXP20X_DCDC_MODE, AXP20X_FG_RES),
> > +};
> > +
> > +static const struct regmap_range axp20x_volatile_ranges[] = {
> > + regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ5_STATE),
> > +};
> > +
> > +static const struct regmap_access_table axp20x_writeable_table = {
> > + .yes_ranges = axp20x_writeable_ranges,
> > + .n_yes_ranges = ARRAY_SIZE(axp20x_writeable_ranges),
> > +};
> > +
> > +static const struct regmap_access_table axp20x_volatile_table = {
> > + .yes_ranges = axp20x_volatile_ranges,
> > + .n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges),
> > +};
> > +
> > +static struct resource axp20x_pek_resources[] = {
> > + {
> > + .name = "PEK_DBR",
> > + .start = AXP20X_IRQ_PEK_RIS_EDGE,
> > + .end = AXP20X_IRQ_PEK_RIS_EDGE,
> > + .flags = IORESOURCE_IRQ,
> > + },
> > + {
> > + .name = "PEK_DBF",
> > + .start = AXP20X_IRQ_PEK_FAL_EDGE,
> > + .end = AXP20X_IRQ_PEK_FAL_EDGE,
> > + .flags = IORESOURCE_IRQ,
> > + },
> > +};
> > +
> > +static const struct regmap_config axp20x_regmap_config = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .wr_table = &axp20x_writeable_table,
> > + .volatile_table = &axp20x_volatile_table,
> > + .max_register = AXP20X_FG_RES,
> > + .cache_type = REGCACHE_RBTREE,
> > +};
> > +
> > +#define AXP20X_IRQ(_irq, _off, _mask) \
> > + [AXP20X_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) }
> > +
> > +static const struct regmap_irq axp20x_regmap_irqs[] = {
> > + AXP20X_IRQ(ACIN_OVER_V, 0, 7),
> > + AXP20X_IRQ(ACIN_PLUGIN, 0, 6),
> > + AXP20X_IRQ(ACIN_REMOVAL, 0, 5),
> > + AXP20X_IRQ(VBUS_OVER_V, 0, 4),
> > + AXP20X_IRQ(VBUS_PLUGIN, 0, 3),
> > + AXP20X_IRQ(VBUS_REMOVAL, 0, 2),
> > + AXP20X_IRQ(VBUS_V_LOW, 0, 1),
> > + AXP20X_IRQ(BATT_PLUGIN, 1, 7),
> > + AXP20X_IRQ(BATT_REMOVAL, 1, 6),
> > + AXP20X_IRQ(BATT_ENT_ACT_MODE, 1, 5),
> > + AXP20X_IRQ(BATT_EXIT_ACT_MODE, 1, 4),
> > + AXP20X_IRQ(CHARG, 1, 3),
> > + AXP20X_IRQ(CHARG_DONE, 1, 2),
> > + AXP20X_IRQ(BATT_TEMP_HIGH, 1, 1),
> > + AXP20X_IRQ(BATT_TEMP_LOW, 1, 0),
> > + AXP20X_IRQ(DIE_TEMP_HIGH, 2, 7),
> > + AXP20X_IRQ(CHARG_I_LOW, 2, 6),
> > + AXP20X_IRQ(DCDC1_V_LONG, 2, 5),
> > + AXP20X_IRQ(DCDC2_V_LONG, 2, 4),
> > + AXP20X_IRQ(DCDC3_V_LONG, 2, 3),
> > + AXP20X_IRQ(PEK_SHORT, 2, 1),
> > + AXP20X_IRQ(PEK_LONG, 2, 0),
> > + AXP20X_IRQ(N_OE_PWR_ON, 3, 7),
> > + AXP20X_IRQ(N_OE_PWR_OFF, 3, 6),
> > + AXP20X_IRQ(VBUS_VALID, 3, 5),
> > + AXP20X_IRQ(VBUS_NOT_VALID, 3, 4),
> > + AXP20X_IRQ(VBUS_SESS_VALID, 3, 3),
> > + AXP20X_IRQ(VBUS_SESS_END, 3, 2),
> > + AXP20X_IRQ(LOW_PWR_LVL1, 3, 1),
> > + AXP20X_IRQ(LOW_PWR_LVL2, 3, 0),
> > + AXP20X_IRQ(TIMER, 4, 7),
> > + AXP20X_IRQ(PEK_RIS_EDGE, 4, 6),
> > + AXP20X_IRQ(PEK_FAL_EDGE, 4, 5),
> > + AXP20X_IRQ(GPIO3_INPUT, 4, 3),
> > + AXP20X_IRQ(GPIO2_INPUT, 4, 2),
> > + AXP20X_IRQ(GPIO1_INPUT, 4, 1),
> > + AXP20X_IRQ(GPIO0_INPUT, 4, 0),
> > +};
> > +
> > +static const struct regmap_irq_chip axp20x_regmap_irq_chip = {
> > + .name = "axp20x_irq_chip",
> > + .status_base = AXP20X_IRQ1_STATE,
> > + .ack_base = AXP20X_IRQ1_STATE,
> > + .mask_base = AXP20X_IRQ1_EN,
> > + .num_regs = 5,
> > + .irqs = axp20x_regmap_irqs,
> > + .num_irqs = ARRAY_SIZE(axp20x_regmap_irqs),
> > + .mask_invert = true,
> > + .init_ack_masked = true,
> > +};
> > +
> > +static struct mfd_cell axp20x_cells[] = {
> > + {
> > + .name = "axp20x-pek",
> > + .of_compatible = "x-powers,axp20x-pek",
>
> You need to add the x-powers prefix to
> Documentation/devicetree/bindings/vendor-prefixes.txt, preferably in a
> separate patch.
Right, thanks
> > + .num_resources = ARRAY_SIZE(axp20x_pek_resources),
> > + .resources = axp20x_pek_resources,
> > + }, {
> > + .name = "axp20x-regulator",
> > + },
> > +};
> > +
> > +const struct of_device_id axp20x_of_match[] = {
> > + { .compatible = "x-powers,axp202", .data = (void *) AXP202_ID },
> > + { .compatible = "x-powers,axp209", .data = (void *) AXP209_ID },
> > + { },
> > +};
> > +
> > +static struct axp20x_dev *axp20x_pm_power_off;
> > +static void axp20x_power_off(void)
> > +{
> > + regmap_write(axp20x_pm_power_off->regmap, AXP20X_OFF_CTRL,
> > + AXP20X_OFF);
> > +}
> > +
> > +static int axp20x_i2c_probe(struct i2c_client *i2c,
> > + const struct i2c_device_id *id)
> > +{
> > + struct axp20x_dev *axp20x;
> > + const struct of_device_id *of_id;
> > + struct device_node *node = i2c->dev.of_node;
> > + int ret;
> > +
> > + axp20x = devm_kzalloc(&i2c->dev, sizeof(*axp20x), GFP_KERNEL);
> > + if (!axp20x)
> > + return -ENOMEM;
> > +
> > + of_id = of_match_device(axp20x_of_match, &i2c->dev);
> > + if (!of_id) {
> > + dev_err(&i2c->dev, "Unable to setup AXP20X data\n");
> > + return -ENODEV;
> > + }
> > + axp20x->variant = (int) of_id->data;
> > +
> > + axp20x->i2c_client = i2c;
> > + axp20x->dev = &i2c->dev;
> > + dev_set_drvdata(axp20x->dev, axp20x);
> > +
> > + axp20x->regmap = devm_regmap_init_i2c(i2c, &axp20x_regmap_config);
> > + if (IS_ERR(axp20x->regmap)) {
> > + ret = PTR_ERR(axp20x->regmap);
> > + dev_err(&i2c->dev, "regmap init failed: %d\n", ret);
> > + return ret;
> > + }
> > +
> > + axp20x->irq = i2c->irq;
> > + ret = regmap_add_irq_chip(axp20x->regmap, axp20x->irq,
> > + IRQF_ONESHOT | IRQF_SHARED, -1,
> > + &axp20x_regmap_irq_chip,
> > + &axp20x->regmap_irqc);
> > + if (ret) {
> > + dev_err(&i2c->dev, "failed to add irq chip: %d\n", ret);
> > + return ret;
> > + }
> > +
> > + ret = mfd_add_devices(axp20x->dev, -1, axp20x_cells,
> > + ARRAY_SIZE(axp20x_cells), NULL, 0, NULL);
> > + if (ret) {
> > + dev_err(&i2c->dev, "failed to add MFD devices: %d\n", ret);
> > + goto mfd_err;
> > + }
> > +
> > + axp20x->pm_off = of_property_read_bool(node, "axp,system-power-controller");
> > +
> > + if (axp20x->pm_off && !pm_power_off) {
> > + axp20x_pm_power_off = axp20x;
> > + pm_power_off = axp20x_power_off;
> > + }
>
> I don't think we have any other way to actually power down the board.
>
> Is there any reason why we would not need this property?
In case you have multiple PMIC on the board in this way you con define
which is the one apt to power off.
> > + dev_info(&i2c->dev, "AXP20X driver loaded\n");
> > +
> > + return 0;
> > +
> > +mfd_err:
> > + regmap_del_irq_chip(axp20x->irq, axp20x->regmap_irqc);
> > +
> > + return ret;
> > +}
> > +
> > +static int axp20x_i2c_remove(struct i2c_client *i2c)
> > +{
> > + struct axp20x_dev *axp20x = i2c_get_clientdata(i2c);
> > +
> > + if (axp20x == axp20x_pm_power_off) {
> > + axp20x_pm_power_off = NULL;
> > + pm_power_off = NULL;
> > + }
> > +
> > + mfd_remove_devices(axp20x->dev);
> > + regmap_del_irq_chip(axp20x->i2c_client->irq, axp20x->regmap_irqc);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct i2c_device_id axp20x_i2c_id[] = {
> > + { "axp202", AXP202_ID },
> > + { "axp209", AXP209_ID },
> > + { }
> > +};
> > +MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id);
> > +
> > +static struct i2c_driver axp20x_i2c_driver = {
> > + .driver = {
> > + .name = "axp20x",
> > + .owner = THIS_MODULE,
> > + .of_match_table = of_match_ptr(axp20x_of_match),
> > + },
> > + .probe = axp20x_i2c_probe,
> > + .remove = axp20x_i2c_remove,
> > + .id_table = axp20x_i2c_id,
> > +};
> > +
> > +module_i2c_driver(axp20x_i2c_driver);
> > +
> > +MODULE_DESCRIPTION("PMIC MFD core driver for AXP20X");
> > +MODULE_AUTHOR("Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>");
> > +MODULE_LICENSE("GPL");
> > diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
> > new file mode 100644
> > index 0000000..fcef32c
> > --- /dev/null
> > +++ b/include/linux/mfd/axp20x.h
> > @@ -0,0 +1,180 @@
> > +/*
> > + * Functions to access AXP20X power management chip.
> > + *
> > + * Copyright (C) 2013, Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
> > + *
> > + * 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.
> > + */
> > +
> > +#ifndef __LINUX_MFD_AXP20X_H
> > +#define __LINUX_MFD_AXP20X_H
> > +
> > +#define AXP202_ID 0
> > +#define AXP209_ID 1
> > +
> > +#define AXP20X_DATACACHE(m) (0x04 + (m))
> > +
> > +/* Power supply */
> > +#define AXP20X_PWR_INPUT_STATUS 0x00
> > +#define AXP20X_PWR_OP_MODE 0x01
> > +#define AXP20X_USB_OTG_STATUS 0x02
> > +#define AXP20X_PWR_OUT_CTRL 0x12
> > +#define AXP20X_DCDC2_V_OUT 0x23
> > +#define AXP20X_DCDC2_LDO3_V_SCAL 0x25
> > +#define AXP20X_DCDC3_V_OUT 0x27
> > +#define AXP20X_LDO24_V_OUT 0x28
> > +#define AXP20X_LDO3_V_OUT 0x29
> > +#define AXP20X_VBUS_IPSOUT_MGMT 0x30
> > +#define AXP20X_V_OFF 0x31
> > +#define AXP20X_OFF_CTRL 0x32
> > +#define AXP20X_CHRG_CTRL1 0x33
> > +#define AXP20X_CHRG_CTRL2 0x34
> > +#define AXP20X_CHRG_BAK_CTRL 0x35
> > +#define AXP20X_PEK_KEY 0x36
> > +#define AXP20X_DCDC_FREQ 0x37
> > +#define AXP20X_V_LTF_CHRG 0x38
> > +#define AXP20X_V_HTF_CHRG 0x39
> > +#define AXP20X_APS_WARN_L1 0x3a
> > +#define AXP20X_APS_WARN_L2 0x3b
> > +#define AXP20X_V_LTF_DISCHRG 0x3c
> > +#define AXP20X_V_HTF_DISCHRG 0x3d
> > +
> > +/* Interrupt */
> > +#define AXP20X_IRQ1_EN 0x40
> > +#define AXP20X_IRQ2_EN 0x41
> > +#define AXP20X_IRQ3_EN 0x42
> > +#define AXP20X_IRQ4_EN 0x43
> > +#define AXP20X_IRQ5_EN 0x44
> > +#define AXP20X_IRQ1_STATE 0x48
> > +#define AXP20X_IRQ2_STATE 0x49
> > +#define AXP20X_IRQ3_STATE 0x4a
> > +#define AXP20X_IRQ4_STATE 0x4b
> > +#define AXP20X_IRQ5_STATE 0x4c
> > +
> > +/* ADC */
> > +#define AXP20X_ACIN_V_ADC_H 0x56
> > +#define AXP20X_ACIN_V_ADC_L 0x57
> > +#define AXP20X_ACIN_I_ADC_H 0x58
> > +#define AXP20X_ACIN_I_ADC_L 0x59
> > +#define AXP20X_VBUS_V_ADC_H 0x5a
> > +#define AXP20X_VBUS_V_ADC_L 0x5b
> > +#define AXP20X_VBUS_I_ADC_H 0x5c
> > +#define AXP20X_VBUS_I_ADC_L 0x5d
> > +#define AXP20X_TEMP_ADC_H 0x5e
> > +#define AXP20X_TEMP_ADC_L 0x5f
> > +#define AXP20X_TS_IN_H 0x62
> > +#define AXP20X_TS_IN_L 0x63
> > +#define AXP20X_GPIO0_V_ADC_H 0x64
> > +#define AXP20X_GPIO0_V_ADC_L 0x65
> > +#define AXP20X_GPIO1_V_ADC_H 0x66
> > +#define AXP20X_GPIO1_V_ADC_L 0x67
> > +#define AXP20X_PWR_BATT_H 0x70
> > +#define AXP20X_PWR_BATT_M 0x71
> > +#define AXP20X_PWR_BATT_L 0x72
> > +#define AXP20X_BATT_V_H 0x78
> > +#define AXP20X_BATT_V_L 0x79
> > +#define AXP20X_BATT_CHRG_I_H 0x7a
> > +#define AXP20X_BATT_CHRG_I_L 0x7b
> > +#define AXP20X_BATT_DISCHRG_I_H 0x7c
> > +#define AXP20X_BATT_DISCHRG_I_L 0x7d
> > +#define AXP20X_IPSOUT_V_HIGH_H 0x7e
> > +#define AXP20X_IPSOUT_V_HIGH_L 0x7f
> > +
> > +/* Power supply */
> > +#define AXP20X_DCDC_MODE 0x80
> > +#define AXP20X_ADC_EN1 0x82
> > +#define AXP20X_ADC_EN2 0x83
> > +#define AXP20X_ADC_RATE 0x84
> > +#define AXP20X_GPIO10_IN_RANGE 0x85
> > +#define AXP20X_GPIO1_ADC_IRQ_RIS 0x86
> > +#define AXP20X_GPIO1_ADC_IRQ_FAL 0x87
> > +#define AXP20X_TIMER_CTRL 0x8a
> > +#define AXP20X_VBUS_MON 0x8b
> > +#define AXP20X_OVER_TMP 0x8f
> > +
> > +/* GPIO */
> > +#define AXP20X_GPIO0_CTRL 0x90
> > +#define AXP20X_LDO5_V_OUT 0x91
> > +#define AXP20X_GPIO1_CTRL 0x92
> > +#define AXP20X_GPIO2_CTRL 0x93
> > +#define AXP20X_GPIO20_SS 0x94
> > +#define AXP20X_GPIO3_CTRL 0x95
> > +
> > +/* Battery */
> > +#define AXP20X_CHRG_CC_31_24 0xb0
> > +#define AXP20X_CHRG_CC_23_16 0xb1
> > +#define AXP20X_CHRG_CC_15_8 0xb2
> > +#define AXP20X_CHRG_CC_7_0 0xb3
> > +#define AXP20X_DISCHRG_CC_31_24 0xb4
> > +#define AXP20X_DISCHRG_CC_23_16 0xb5
> > +#define AXP20X_DISCHRG_CC_15_8 0xb6
> > +#define AXP20X_DISCHRG_CC_7_0 0xb7
> > +#define AXP20X_CC_CTRL 0xb8
> > +#define AXP20X_FG_RES 0xb9
> > +
> > +/* Regulators IDs */
> > +enum {
> > + AXP20X_LDO1 = 0,
> > + AXP20X_LDO2,
> > + AXP20X_LDO3,
> > + AXP20X_LDO4,
> > + AXP20X_LDO5,
> > + AXP20X_DCDC2,
> > + AXP20X_DCDC3,
> > + AXP20X_REG_ID_MAX,
> > +};
> > +
> > +/* IRQs */
> > +enum {
> > + AXP20X_IRQ_ACIN_OVER_V = 1,
> > + AXP20X_IRQ_ACIN_PLUGIN,
> > + AXP20X_IRQ_ACIN_REMOVAL,
> > + AXP20X_IRQ_VBUS_OVER_V,
> > + AXP20X_IRQ_VBUS_PLUGIN,
> > + AXP20X_IRQ_VBUS_REMOVAL,
> > + AXP20X_IRQ_VBUS_V_LOW,
> > + AXP20X_IRQ_BATT_PLUGIN,
> > + AXP20X_IRQ_BATT_REMOVAL,
> > + AXP20X_IRQ_BATT_ENT_ACT_MODE,
> > + AXP20X_IRQ_BATT_EXIT_ACT_MODE,
> > + AXP20X_IRQ_CHARG,
> > + AXP20X_IRQ_CHARG_DONE,
> > + AXP20X_IRQ_BATT_TEMP_HIGH,
> > + AXP20X_IRQ_BATT_TEMP_LOW,
> > + AXP20X_IRQ_DIE_TEMP_HIGH,
> > + AXP20X_IRQ_CHARG_I_LOW,
> > + AXP20X_IRQ_DCDC1_V_LONG,
> > + AXP20X_IRQ_DCDC2_V_LONG,
> > + AXP20X_IRQ_DCDC3_V_LONG,
> > + AXP20X_IRQ_PEK_SHORT = 22,
> > + AXP20X_IRQ_PEK_LONG,
> > + AXP20X_IRQ_N_OE_PWR_ON,
> > + AXP20X_IRQ_N_OE_PWR_OFF,
> > + AXP20X_IRQ_VBUS_VALID,
> > + AXP20X_IRQ_VBUS_NOT_VALID,
> > + AXP20X_IRQ_VBUS_SESS_VALID,
> > + AXP20X_IRQ_VBUS_SESS_END,
> > + AXP20X_IRQ_LOW_PWR_LVL1,
> > + AXP20X_IRQ_LOW_PWR_LVL2,
> > + AXP20X_IRQ_TIMER,
> > + AXP20X_IRQ_PEK_RIS_EDGE,
> > + AXP20X_IRQ_PEK_FAL_EDGE,
> > + AXP20X_IRQ_GPIO3_INPUT,
> > + AXP20X_IRQ_GPIO2_INPUT,
> > + AXP20X_IRQ_GPIO1_INPUT,
> > + AXP20X_IRQ_GPIO0_INPUT,
> > +};
> > +
> > +struct axp20x_dev {
> > + struct device *dev;
> > + struct i2c_client *i2c_client;
> > + struct regmap *regmap;
> > + struct regmap_irq_chip_data *regmap_irqc;
> > + int variant;
> > + int irq;
> > + bool pm_off;
> > +};
> > +
> > +#endif /* __LINUX_MFD_AXP20X_H */
> > --
> > 1.8.3.2
> >
>
> Thanks!
> Maxime
Thank you,
--
Carlo Caione
^ permalink raw reply
* [PATCH] input: synaptics add manual min/max quirk for ThinkPad X240
From: Hans de Goede @ 2014-03-08 11:35 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Benjamin Tissoires, Christopher Heiny, Andrew Duggan, linux-input,
Peter Hutterer, Stephen Chandler Paul, Hans de Goede, stable
In-Reply-To: <1394278521-6870-1-git-send-email-hdegoede@redhat.com>
This extends Benjamin Tissoires manual min/max quirk table with support for
the ThinkPad X240.
Cc: stable@vger.kernel.org
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/input/mouse/synaptics.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index ff6a4cf..b522bdc 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -1505,6 +1505,14 @@ static const struct dmi_system_id min_max_dmi_table[] __initconst = {
.driver_data = (int []){1024, 5052, 2258, 4832},
},
{
+ /* Lenovo ThinkPad X240 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X240"),
+ },
+ .driver_data = (int []){1232, 5710, 1156, 4696},
+ },
+ {
/* Lenovo ThinkPad T440s */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
--
1.8.4.2
^ permalink raw reply related
* [PATCH 0/1] input: synaptics add manual min/max quirk for ThinkPad X240
From: Hans de Goede @ 2014-03-08 11:35 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Benjamin Tissoires, Christopher Heiny, Andrew Duggan, linux-input,
Peter Hutterer, Stephen Chandler Paul
Hi All,
Here is a follow up patch to Benjamin's quirk patch, which adds the
necessary min/max overrides for the ThinkPad X240
As such this patch depends on Benjamin's patch going in first.
Regards,
Hans
^ permalink raw reply
* Re: Re: [PATCH 2/7] mfd: AXP20x: Add bindings documentation
From: Carlo Caione @ 2014-03-08 11:38 UTC (permalink / raw)
To: Maxime Ripard
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
hdegoede-H+wXaHxf7aLQT0dZR+AlfA, emilio-0Z03zUJReD5OxF6Tv1QG9Q,
wens-jdAy2FN1RRM, sameo-VuQAYsv1563Yd54FQh9/CA,
lee.jones-QSEj5FYQhm4dnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA,
dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A
In-Reply-To: <20140307181300.GZ607@lukather>
On Fri, Mar 07, 2014 at 07:13:00PM +0100, Maxime Ripard wrote:
> On Sat, Mar 01, 2014 at 05:45:47PM +0100, Carlo Caione wrote:
> > Bindings documentation for the AXP20x driver. In this file also two
> > sub-nodes (PEK and regulators) are documented.
> >
> > Signed-off-by: Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
> > ---
> > Documentation/devicetree/bindings/mfd/axp20x.txt | 93 ++++++++++++++++++++++++
> > 1 file changed, 93 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/mfd/axp20x.txt
> >
> > diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt
> > new file mode 100644
> > index 0000000..ae3e3c4
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/mfd/axp20x.txt
> > @@ -0,0 +1,93 @@
> > +* axp20x device tree bindings
> > +
> > +The axp20x family current members :-
> > +axp202 (X-Powers)
> > +axp209 (X-Powers)
> > +
> > +Required properties:
> > +- compatible : Should be "x-powers,axp202" or "x-powers,axp209"
> > +- interrupt-controller : axp20x has its own internal IRQs
> > +- #interrupt-cells : Should be set to 1
> > +- interrupt-parent : The parent interrupt controller
> > +- interrupts : Interrupt specifiers for interrupt sources
> > +- reg : The I2C slave address for the AXP chip
> > +- axp,system-power-controller : Telling whether or not this pmic is
> > + controlling the system power
> > +
> > +Sub-nodes:
> > +* regulators : Contain the regulator nodes. The regulators are bound using
> > + their name as listed here: dcdc2, dcdc3, ldo1, ldo2, ldo3,
> > + ldo4, ldo5.
> > + The bindings details of individual regulator device can be found in:
> > + Documentation/devicetree/bindings/regulator/regulator.txt with the
> > + exception of:
> > +
> > + - dcdc-freq : defines the work frequency of DC-DC in KHz
> > + (range: 750-1875)
> > + - dcdc-workmode : 1 for PWM mode, 0 for AUTO mode
>
> You don't seem to always set this. You should mention that it is
> optional, and which default value it has.
Fix in v2.
> > +
> > +* axp20x-pek : Power Enable Key
> > + - compatible : should be "x-powers,axp20x-pek"
>
> Why is this needed for?
>
> Plus, please don't use any generic, or pattern matching compatibles,
> but rather precise ones, so that if it is needed, we can add any quirk
> we want.
It is not needed, in fact I deleted it from dts. It is a refuse from the
previous submission. I'll fix it.
> > +Example:
> > +
> > +axp: axp20x@34 {
> > + reg = <0x34>;
> > + interrupt-parent = <&nmi_intc>;
> > + interrupts = <0 8>;
> > +
> > + axp,system-power-controller;
> > +
> > + compatible = "x-powers,axp209";
> > + interrupt-controller;
> > + #interrupt-cells = <1>;
> > +
> > + axp20x-pek {
> > + compatible = "x-powers,axp20x-pek";
> > + };
> > +
> > + regulators {
> > + dcdc-freq = "8";
> > +
> > + axp_dcdc2: dcdc2 {
> > + regulator-min-microvolt = <700000>;
> > + regulator-max-microvolt = <2275000>;
> > + dcdc-workmode = <0>;
> > + regulator-always-on;
> > + };
> > +
> > + axp_dcdc3: dcdc3 {
> > + regulator-min-microvolt = <700000>;
> > + regulator-max-microvolt = <3500000>;
> > + dcdc-workmode = <0>;
> > + regulator-always-on;
> > + };
> > +
> > + axp_ldo1: ldo1 {
> > + regulator-min-microvolt = <1300000>;
> > + regulator-max-microvolt = <1300000>;
> > + };
> > +
> > + axp_ldo2: ldo2 {
> > + regulator-min-microvolt = <1800000>;
> > + regulator-max-microvolt = <3300000>;
> > + regulator-always-on;
> > + };
> > +
> > + axp_ldo3: ldo3 {
> > + regulator-min-microvolt = <700000>;
> > + regulator-max-microvolt = <3500000>;
> > + };
> > +
> > + axp_ldo4: ldo4 {
> > + regulator-min-microvolt = <1250000>;
> > + regulator-max-microvolt = <3300000>;
> > + };
> > +
> > + axp_ldo5: ldo5 {
> > + regulator-min-microvolt = <1800000>;
> > + regulator-max-microvolt = <3300000>;
> > + };
> > + };
> > +};
> > +
> > --
> > 1.8.3.2
> >
--
Carlo Caione
^ permalink raw reply
* Re: Re: [PATCH 6/7] regulator: AXP20x: Add support for regulators subsystem
From: Carlo Caione @ 2014-03-08 11:43 UTC (permalink / raw)
To: Maxime Ripard
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
hdegoede-H+wXaHxf7aLQT0dZR+AlfA, emilio-0Z03zUJReD5OxF6Tv1QG9Q,
wens-jdAy2FN1RRM, sameo-VuQAYsv1563Yd54FQh9/CA,
lee.jones-QSEj5FYQhm4dnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA,
dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <20140307182213.GC607@lukather>
On Fri, Mar 7, 2014 at 7:22 PM, Maxime Ripard
<maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org> wrote:
> On Sat, Mar 01, 2014 at 05:45:51PM +0100, Carlo Caione wrote:
>> +static struct platform_driver axp20x_regulator_driver = {
>> + .probe = axp20x_regulator_probe,
>> + .remove = axp20x_regulator_remove,
>> + .driver = {
>> + .name = "axp20x-regulator",
>> + .owner = THIS_MODULE,
>> + },
>> +};
>> +
>> +static int __init axp20x_regulator_init(void)
>> +{
>> + return platform_driver_register(&axp20x_regulator_driver);
>> +}
>> +
>> +subsys_initcall(axp20x_regulator_init);
>
> Hmmm, really?
>
> I thought the AXP was only connected through I2C? How is that a
> platform device then?
Not really. It is plain wrong.
I'll fix it in v2.
Thanks,
--
Carlo Caione
^ permalink raw reply
* Re: [PATCH v2 4/8] Input: pixcir_i2c_ts: Use Type-B Multi-Touch protocol
From: Henrik Rydberg @ 2014-03-08 15:11 UTC (permalink / raw)
To: Roger Quadros, dmitry.torokhov
Cc: jcbian, balbi, dmurphy, mugunthanvnm, linux-input, linux-kernel,
devicetree
In-Reply-To: <1393428486-15001-5-git-send-email-rogerq@ti.com>
Hi Roger,
the MT implementation seems mostly fine, just one curiosity:
> static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
> {
> struct pixcir_i2c_ts_data *tsdata = dev_id;
> const struct pixcir_ts_platform_data *pdata = tsdata->chip;
> + struct pixcir_report_data report;
>
> while (!tsdata->exiting) {
> - pixcir_ts_poscheck(tsdata);
> -
> - if (gpio_get_value(pdata->gpio_attb))
> + /* parse packet */
> + pixcir_ts_parse(tsdata, &report);
> +
> + /* report it */
> + pixcir_ts_report(tsdata, &report);
> +
> + if (gpio_get_value(pdata->gpio_attb)) {
> + if (report.num_touches) {
> + /*
> + * Last report with no finger up?
> + * Do it now then.
> + */
> + input_mt_sync_frame(tsdata->input);
> + input_sync(tsdata->input);
Why is this special handling needed?
Thanks,
Henrik
^ permalink raw reply
* Re: [PATCH v2 1/4] HID: cp2112: remove various hid_out_raw_report calls
From: Benjamin Tissoires @ 2014-03-09 3:23 UTC (permalink / raw)
To: David Herrmann
Cc: Benjamin Tissoires, Jiri Kosina, David Barksdale, Antonio Ospite,
open list:HID CORE LAYER, linux-kernel
In-Reply-To: <CANq1E4T_Xm7xCb5vGKBsb1EH-h6g4V-L9mrjZxThWa-GdtbPhQ@mail.gmail.com>
On Fri, Mar 7, 2014 at 4:47 AM, David Herrmann <dh.herrmann@gmail.com> wrote:
> Hi
>
> On Wed, Mar 5, 2014 at 10:18 PM, Benjamin Tissoires
> <benjamin.tissoires@redhat.com> wrote:
>> hid_out_raw_report is going to be obsoleted as it is not part of the
>> unified HID low level transport documentation
>> (Documentation/hid/hid-transport.txt)
>>
>> hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
>> is strictly equivalent to:
>> hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
>> HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
>
> This time you might be right that feature-reports always put the
> report-id into the first byte, but I'd still prefer if we avoid using
> this. Besides, the code is much nicer imho if we pass the ID directly,
> see below..
Yes you are completely right.
Will send a v3 ASAP.
Cheers,
Benjamin
>
>>
>> So use the new api.
>>
>> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
>> ---
>>
>> no changes since v1
>>
>> drivers/hid/hid-cp2112.c | 12 ++++++------
>> 1 file changed, 6 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c
>> index 1025982..860db694 100644
>> --- a/drivers/hid/hid-cp2112.c
>> +++ b/drivers/hid/hid-cp2112.c
>> @@ -185,8 +185,8 @@ static int cp2112_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
>> buf[1] &= ~(1 << offset);
>> buf[2] = gpio_push_pull;
>>
>> - ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf),
>> - HID_FEATURE_REPORT);
>> + ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
>> + HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
>
> buf[0] => CP2112_GPIO_CONFIG
>
>> if (ret < 0) {
>> hid_err(hdev, "error setting GPIO config: %d\n", ret);
>> return ret;
>> @@ -207,8 +207,8 @@ static void cp2112_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
>> buf[1] = value ? 0xff : 0;
>> buf[2] = 1 << offset;
>>
>> - ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf),
>> - HID_FEATURE_REPORT);
>> + ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
>> + HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
>
> Here buf[0] seems fine as it is set explicitly just 3 lines above to
> CP2112_GPIO_SET.
>
>> if (ret < 0)
>> hid_err(hdev, "error setting GPIO values: %d\n", ret);
>> }
>> @@ -253,8 +253,8 @@ static int cp2112_gpio_direction_output(struct gpio_chip *chip,
>> buf[1] |= 1 << offset;
>> buf[2] = gpio_push_pull;
>>
>> - ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf),
>> - HID_FEATURE_REPORT);
>> + ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
>> + HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
>
> Here an explicit CP2112_GPIO_CONFIG seems nicer, imho.
>
> Thanks
> David
>
>> if (ret < 0) {
>> hid_err(hdev, "error setting GPIO config: %d\n", ret);
>> return ret;
>> --
>> 1.8.5.3
>>
^ permalink raw reply
* Re: [PATCH v2 0/4] HID: ll transport cleanup: final round
From: Benjamin Tissoires @ 2014-03-09 3:40 UTC (permalink / raw)
To: David Herrmann
Cc: Benjamin Tissoires, Jiri Kosina, David Barksdale, Antonio Ospite,
open list:HID CORE LAYER, linux-kernel
In-Reply-To: <CANq1E4QRc+cwAJYFJg2F8WpSOrx2xXuPeiZYF6np_2C5CoqRqA@mail.gmail.com>
On Fri, Mar 7, 2014 at 4:52 AM, David Herrmann <dh.herrmann@gmail.com> wrote:
> Hi
>
> On Wed, Mar 5, 2014 at 10:18 PM, Benjamin Tissoires
> <benjamin.tissoires@redhat.com> wrote:
>> Alright, this is the re-spin of the last round of transport cleanup.
>>
>> Some minor but important modifications are here, but nothing very enthousiastic.
>>
>> Thanks for the reviews and the tests so far.
>
> Apart from some minor comments on #1 and #2, this is:
Hmm, neither my mailboxes nor patchwork gives me your comments on #2...
By looking at the code, (assuming you mean the same comment that you
made on #1), this is not so obvious.
What we can do is add an arg to cp2112_hid_output() with the report
number (and pass it to hid_hw_raw_request() ). However, this function
is also used by cp2112_xfer(), and there is chances that I introduce a
bug if I am not careful enough.
I'd rather split cp2112_hid_output() in two, one for output reports,
and one for features (with the reportID as an arg). I'll send this one
after this series because I would like to do some more tests.
>
> Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
>
Thanks a lot!
Cheers,
Benjamin
> Thanks
> David
>
>> Cheers,
>> Benjamin
>>
>> Benjamin Tissoires (4):
>> HID: cp2112: remove various hid_out_raw_report calls
>> HID: cp2112: remove the last hid_output_raw_report() call
>> HID: sony: do not rely on hid_output_raw_report
>> HID: remove hid_output_raw_report transport implementations
>>
>> drivers/hid/hid-cp2112.c | 19 +++++++++-----
>> drivers/hid/hid-sony.c | 60 ++++++++++---------------------------------
>> drivers/hid/hidraw.c | 3 ++-
>> drivers/hid/i2c-hid/i2c-hid.c | 14 ----------
>> drivers/hid/uhid.c | 1 -
>> drivers/hid/usbhid/hid-core.c | 19 +++++---------
>> include/linux/hid.h | 21 ++-------------
>> net/bluetooth/hidp/core.c | 14 ----------
>> 8 files changed, 36 insertions(+), 115 deletions(-)
>>
>> --
>> 1.8.5.3
^ permalink raw reply
* [PATCH v3 0/4] HID: ll transport cleanup: final round
From: Benjamin Tissoires @ 2014-03-09 3:52 UTC (permalink / raw)
To: Benjamin Tissoires, Jiri Kosina, David Herrmann, David Barksdale,
Antonio Ospite, linux-input, linux-kernel
Hop, a new version.
As mentioned in the v2, I did not changed 2/4 but I'll add a patch later
to remove buf[0] from the call. But I need to do tests first.
Thanks for all the work to everybody who took a part in this cleanup.
Cheers,
Benjamin
Benjamin Tissoires (4):
HID: cp2112: remove various hid_out_raw_report calls
HID: cp2112: remove the last hid_output_raw_report() call
HID: sony: do not rely on hid_output_raw_report
HID: remove hid_output_raw_report transport implementations
drivers/hid/hid-cp2112.c | 19 +++++++++-----
drivers/hid/hid-sony.c | 60 ++++++++++---------------------------------
drivers/hid/hidraw.c | 3 ++-
drivers/hid/i2c-hid/i2c-hid.c | 14 ----------
drivers/hid/uhid.c | 1 -
drivers/hid/usbhid/hid-core.c | 19 +++++---------
include/linux/hid.h | 21 ++-------------
net/bluetooth/hidp/core.c | 14 ----------
8 files changed, 36 insertions(+), 115 deletions(-)
--
1.8.5.3
^ permalink raw reply
* [PATCH v3 1/4] HID: cp2112: remove various hid_out_raw_report calls
From: Benjamin Tissoires @ 2014-03-09 3:52 UTC (permalink / raw)
To: Benjamin Tissoires, Jiri Kosina, David Herrmann, David Barksdale,
Antonio Ospite, linux-input, linux-kernel
In-Reply-To: <1394337163-32478-1-git-send-email-benjamin.tissoires@redhat.com>
hid_out_raw_report is going to be obsoleted as it is not part of the
unified HID low level transport documentation
(Documentation/hid/hid-transport.txt)
hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
is strictly equivalent to:
hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
Then replace buf[0] by the appropriate define.
So use the new api.
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
changes in v2:
- replaces buf[0] by the appropriate report number.
drivers/hid/hid-cp2112.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c
index 1025982..3913eb9 100644
--- a/drivers/hid/hid-cp2112.c
+++ b/drivers/hid/hid-cp2112.c
@@ -185,8 +185,8 @@ static int cp2112_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
buf[1] &= ~(1 << offset);
buf[2] = gpio_push_pull;
- ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf),
- HID_FEATURE_REPORT);
+ ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, sizeof(buf),
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
if (ret < 0) {
hid_err(hdev, "error setting GPIO config: %d\n", ret);
return ret;
@@ -207,8 +207,8 @@ static void cp2112_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
buf[1] = value ? 0xff : 0;
buf[2] = 1 << offset;
- ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf),
- HID_FEATURE_REPORT);
+ ret = hid_hw_raw_request(hdev, CP2112_GPIO_SET, buf, sizeof(buf),
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
if (ret < 0)
hid_err(hdev, "error setting GPIO values: %d\n", ret);
}
@@ -253,8 +253,8 @@ static int cp2112_gpio_direction_output(struct gpio_chip *chip,
buf[1] |= 1 << offset;
buf[2] = gpio_push_pull;
- ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf),
- HID_FEATURE_REPORT);
+ ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, sizeof(buf),
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
if (ret < 0) {
hid_err(hdev, "error setting GPIO config: %d\n", ret);
return ret;
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 2/4] HID: cp2112: remove the last hid_output_raw_report() call
From: Benjamin Tissoires @ 2014-03-09 3:52 UTC (permalink / raw)
To: Benjamin Tissoires, Jiri Kosina, David Herrmann, David Barksdale,
Antonio Ospite, linux-input, linux-kernel
In-Reply-To: <1394337163-32478-1-git-send-email-benjamin.tissoires@redhat.com>
tests have shown that output reports use hid_hw_output_report().
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
changes in v3:
- no changes
changes in v2:
- removed FIXME
- actually use the proper calls and do not guess
drivers/hid/hid-cp2112.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c
index 3913eb9..56be85a 100644
--- a/drivers/hid/hid-cp2112.c
+++ b/drivers/hid/hid-cp2112.c
@@ -290,7 +290,12 @@ static int cp2112_hid_output(struct hid_device *hdev, u8 *data, size_t count,
if (!buf)
return -ENOMEM;
- ret = hdev->hid_output_raw_report(hdev, buf, count, report_type);
+ if (report_type == HID_OUTPUT_REPORT)
+ ret = hid_hw_output_report(hdev, buf, count);
+ else
+ ret = hid_hw_raw_request(hdev, buf[0], buf, count, report_type,
+ HID_REQ_SET_REPORT);
+
kfree(buf);
return ret;
}
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 4/4] HID: remove hid_output_raw_report transport implementations
From: Benjamin Tissoires @ 2014-03-09 3:52 UTC (permalink / raw)
To: Benjamin Tissoires, Jiri Kosina, David Herrmann, David Barksdale,
Antonio Ospite, linux-input, linux-kernel
In-Reply-To: <1394337163-32478-1-git-send-email-benjamin.tissoires@redhat.com>
Nobody calls hid_output_raw_report anymore, and nobody should.
We can now remove the various implementation in the different
transport drivers and the declarations.
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
no changes since v1 (what did you expected?)
drivers/hid/i2c-hid/i2c-hid.c | 14 --------------
drivers/hid/uhid.c | 1 -
drivers/hid/usbhid/hid-core.c | 12 ------------
include/linux/hid.h | 19 -------------------
net/bluetooth/hidp/core.c | 14 --------------
5 files changed, 60 deletions(-)
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
index cbd44a7..8c52a07 100644
--- a/drivers/hid/i2c-hid/i2c-hid.c
+++ b/drivers/hid/i2c-hid/i2c-hid.c
@@ -596,19 +596,6 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
return ret;
}
-static int __i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
- size_t count, unsigned char report_type)
-{
- struct i2c_client *client = hid->driver_data;
- struct i2c_hid *ihid = i2c_get_clientdata(client);
- bool data = true; /* SET_REPORT */
-
- if (report_type == HID_OUTPUT_REPORT)
- data = le16_to_cpu(ihid->hdesc.wMaxOutputLength) == 0;
-
- return i2c_hid_output_raw_report(hid, buf, count, report_type, data);
-}
-
static int i2c_hid_output_report(struct hid_device *hid, __u8 *buf,
size_t count)
{
@@ -1037,7 +1024,6 @@ static int i2c_hid_probe(struct i2c_client *client,
hid->driver_data = client;
hid->ll_driver = &i2c_hid_ll_driver;
- hid->hid_output_raw_report = __i2c_hid_output_raw_report;
hid->dev.parent = &client->dev;
ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev));
hid->bus = BUS_I2C;
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index 60acee4..7ed79be 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -400,7 +400,6 @@ static int uhid_dev_create(struct uhid_device *uhid,
hid->uniq[63] = 0;
hid->ll_driver = &uhid_hid_driver;
- hid->hid_output_raw_report = uhid_hid_output_raw;
hid->bus = ev->u.create.bus;
hid->vendor = ev->u.create.vendor;
hid->product = ev->u.create.product;
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 3bc7cad..7b88f4c 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -950,17 +950,6 @@ static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count)
return ret;
}
-static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf,
- size_t count, unsigned char report_type)
-{
- struct usbhid_device *usbhid = hid->driver_data;
-
- if (usbhid->urbout && report_type != HID_FEATURE_REPORT)
- return usbhid_output_report(hid, buf, count);
-
- return usbhid_set_raw_report(hid, buf[0], buf, count, report_type);
-}
-
static void usbhid_restart_queues(struct usbhid_device *usbhid)
{
if (usbhid->urbout && !test_bit(HID_OUT_RUNNING, &usbhid->iofl))
@@ -1294,7 +1283,6 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
usb_set_intfdata(intf, hid);
hid->ll_driver = &usb_hid_driver;
- hid->hid_output_raw_report = usbhid_output_raw_report;
hid->ff_init = hid_pidff_init;
#ifdef CONFIG_USB_HIDDEV
hid->hiddev_connect = hiddev_connect;
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 2cd7174..720e3a1 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -513,9 +513,6 @@ struct hid_device { /* device report descriptor */
struct hid_usage *, __s32);
void (*hiddev_report_event) (struct hid_device *, struct hid_report *);
- /* handler for raw output data, used by hidraw */
- int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t, unsigned char);
-
/* debugging support via debugfs */
unsigned short debug;
struct dentry *debug_dir;
@@ -1023,22 +1020,6 @@ static inline int hid_hw_output_report(struct hid_device *hdev, __u8 *buf,
}
/**
- * hid_output_raw_report - send an output or a feature report to the device
- *
- * @hdev: hid device
- * @buf: raw data to transfer
- * @len: length of buf
- * @report_type: HID_FEATURE_REPORT or HID_OUTPUT_REPORT
- *
- * @return: count of data transfered, negative if error
- */
-static inline int hid_output_raw_report(struct hid_device *hdev, __u8 *buf,
- size_t len, unsigned char report_type)
-{
- return hdev->hid_output_raw_report(hdev, buf, len, report_type);
-}
-
-/**
* hid_hw_idle - send idle request to device
*
* @hdev: hid device
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 98e4840..514ddb5 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -382,18 +382,6 @@ static int hidp_output_report(struct hid_device *hid, __u8 *data, size_t count)
data, count);
}
-static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data,
- size_t count, unsigned char report_type)
-{
- if (report_type == HID_OUTPUT_REPORT) {
- return hidp_output_report(hid, data, count);
- } else if (report_type != HID_FEATURE_REPORT) {
- return -EINVAL;
- }
-
- return hidp_set_raw_report(hid, data[0], data, count, report_type);
-}
-
static int hidp_raw_request(struct hid_device *hid, unsigned char reportnum,
__u8 *buf, size_t len, unsigned char rtype,
int reqtype)
@@ -776,8 +764,6 @@ static int hidp_setup_hid(struct hidp_session *session,
hid->dev.parent = &session->conn->hcon->dev;
hid->ll_driver = &hidp_hid_driver;
- hid->hid_output_raw_report = hidp_output_raw_report;
-
/* True if device is blacklisted in drivers/hid/hid-core.c */
if (hid_ignore(hid)) {
hid_destroy_device(session->hid);
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 3/4] HID: sony: do not rely on hid_output_raw_report
From: Benjamin Tissoires @ 2014-03-09 3:52 UTC (permalink / raw)
To: Benjamin Tissoires, Jiri Kosina, David Herrmann, David Barksdale,
Antonio Ospite, linux-input, linux-kernel
In-Reply-To: <1394337163-32478-1-git-send-email-benjamin.tissoires@redhat.com>
hid_out_raw_report is going to be obsoleted as it is not part of the
unified HID low level transport documentation
(Documentation/hid/hid-transport.txt)
To do so, we need to introduce two new quirks:
* HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP: this quirks prevents the
transport driver to use the interrupt channel to send output report
(and thus force to use HID_REQ_SET_REPORT command)
* HID_QUIRK_SKIP_OUTPUT_REPORT_ID: this one forces usbhid to not
include the report ID in the buffer it sends to the device through
HID_REQ_SET_REPORT in case of an output report
This also fixes a regression introduced in commit 3a75b24949a8
(HID: hidraw: replace hid_output_raw_report() calls by appropriates ones).
The hidraw API was not able to communicate with the PS3 SixAxis
controllers in USB mode.
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
changes in v3:
- no changes
changes in v2:
- removed usb.h include
- renamed HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP
- fix typo
drivers/hid/hid-sony.c | 60 ++++++++++---------------------------------
drivers/hid/hidraw.c | 3 ++-
drivers/hid/usbhid/hid-core.c | 7 ++++-
include/linux/hid.h | 2 ++
4 files changed, 24 insertions(+), 48 deletions(-)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index b5fe65e..4884bb5 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -29,7 +29,6 @@
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/usb.h>
#include <linux/leds.h>
#include <linux/power_supply.h>
#include <linux/spinlock.h>
@@ -1007,45 +1006,6 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
}
/*
- * The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP
- * like it should according to usbhid/hid-core.c::usbhid_output_raw_report()
- * so we need to override that forcing HID Output Reports on the Control EP.
- *
- * There is also another issue about HID Output Reports via USB, the Sixaxis
- * does not want the report_id as part of the data packet, so we have to
- * discard buf[0] when sending the actual control message, even for numbered
- * reports, humpf!
- */
-static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf,
- size_t count, unsigned char report_type)
-{
- struct usb_interface *intf = to_usb_interface(hid->dev.parent);
- struct usb_device *dev = interface_to_usbdev(intf);
- struct usb_host_interface *interface = intf->cur_altsetting;
- int report_id = buf[0];
- int ret;
-
- if (report_type == HID_OUTPUT_REPORT) {
- /* Don't send the Report ID */
- buf++;
- count--;
- }
-
- ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- HID_REQ_SET_REPORT,
- USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- ((report_type + 1) << 8) | report_id,
- interface->desc.bInterfaceNumber, buf, count,
- USB_CTRL_SET_TIMEOUT);
-
- /* Count also the Report ID, in case of an Output report. */
- if (ret > 0 && report_type == HID_OUTPUT_REPORT)
- ret++;
-
- return ret;
-}
-
-/*
* Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
* to "operational". Without this, the ps3 controller will not report any
* events.
@@ -1305,11 +1265,8 @@ static void sixaxis_state_worker(struct work_struct *work)
buf[10] |= sc->led_state[2] << 3;
buf[10] |= sc->led_state[3] << 4;
- if (sc->quirks & SIXAXIS_CONTROLLER_USB)
- hid_output_raw_report(sc->hdev, buf, sizeof(buf), HID_OUTPUT_REPORT);
- else
- hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf),
- HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
+ hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf), HID_OUTPUT_REPORT,
+ HID_REQ_SET_REPORT);
}
static void dualshock4_state_worker(struct work_struct *work)
@@ -1659,7 +1616,18 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
- hdev->hid_output_raw_report = sixaxis_usb_output_raw_report;
+ /*
+ * The Sony Sixaxis does not handle HID Output Reports on the
+ * Interrupt EP like it could, so we need to force HID Output
+ * Reports to use HID_REQ_SET_REPORT on the Control EP.
+ *
+ * There is also another issue about HID Output Reports via USB,
+ * the Sixaxis does not want the report_id as part of the data
+ * packet, so we have to discard buf[0] when sending the actual
+ * control message, even for numbered reports, humpf!
+ */
+ hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
+ hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
ret = sixaxis_set_operational_usb(hdev);
sc->worker_initialized = 1;
INIT_WORK(&sc->state_worker, sixaxis_state_worker);
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index 2cc484c..ffa648c 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -149,7 +149,8 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
goto out_free;
}
- if (report_type == HID_OUTPUT_REPORT) {
+ if ((report_type == HID_OUTPUT_REPORT) &&
+ !(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) {
ret = hid_hw_output_report(dev, buf, count);
/*
* compatibility with old implementation of USB-HID and I2C-HID:
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 0d1d875..3bc7cad 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -894,7 +894,12 @@ static int usbhid_set_raw_report(struct hid_device *hid, unsigned int reportnum,
int ret, skipped_report_id = 0;
/* Byte 0 is the report number. Report data starts at byte 1.*/
- buf[0] = reportnum;
+ if ((rtype == HID_OUTPUT_REPORT) &&
+ (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORT_ID))
+ buf[0] = 0;
+ else
+ buf[0] = reportnum;
+
if (buf[0] == 0x0) {
/* Don't send the Report ID */
buf++;
diff --git a/include/linux/hid.h b/include/linux/hid.h
index e224516..2cd7174 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -290,6 +290,8 @@ struct hid_item {
#define HID_QUIRK_NO_EMPTY_INPUT 0x00000100
#define HID_QUIRK_NO_INIT_INPUT_REPORTS 0x00000200
#define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000
+#define HID_QUIRK_SKIP_OUTPUT_REPORT_ID 0x00020000
+#define HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP 0x00040000
#define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000
#define HID_QUIRK_NO_INIT_REPORTS 0x20000000
#define HID_QUIRK_NO_IGNORE 0x40000000
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 0/3] input: appletouch: fixes for jagged/uneven cursor movement
From: Clinton Sprain @ 2014-03-09 6:03 UTC (permalink / raw)
To: linux-input; +Cc: Dmitry Torokhov, Henrik Rydberg
In-Reply-To: <52D9DE7A.7040509@gmail.com>
Changes from last time:
1: Dropped fuzz as a module parameter; also changed threshold defaults back to 5, as I found an outside case where the lower threshold was problematic for finger counting
2: Nix negative subscripts, make smooth array large enough that boundary condition is basically irrelevant (though still handled just in case), scale up and down a little bit more effectively, style
3: Unchanged, except for setting dev->fingers_old to 0 when x and y don't come back with coordinates
Signed-off-by: Clinton Sprain <clintonsprain@gmail.com>
Clinton Sprain (3):
input: appletouch: dial back fuzz setting
input: appletouch: implement sensor data smoothing
input: appletouch: fix jumps when additional fingers are detected
drivers/input/mouse/appletouch.c | 114 +++++++++++++++++++++++++++-----------
1 file changed, 83 insertions(+), 31 deletions(-)
--
1.7.9.5
^ permalink raw reply
* [PATCH v3 1/3] input: appletouch: dial back fuzz setting
From: Clinton Sprain @ 2014-03-09 6:05 UTC (permalink / raw)
To: linux-input; +Cc: Dmitry Torokhov, Henrik Rydberg
In-Reply-To: <531C042D.5090103@gmail.com>
Dials back the default fuzz setting for most devices using this driver, based
on values from user feedback from forums and bug reports.
Signed-off-by: Clinton Sprain <clintonsprain@gmail.com>
---
drivers/input/mouse/appletouch.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c
index 800ca7d..2745832 100644
--- a/drivers/input/mouse/appletouch.c
+++ b/drivers/input/mouse/appletouch.c
@@ -48,6 +48,7 @@ struct atp_info {
int yfact; /* Y multiplication factor */
int datalen; /* size of USB transfers */
void (*callback)(struct urb *); /* callback function */
+ int fuzz; /* fuzz touchpad generates */
};
static void atp_complete_geyser_1_2(struct urb *urb);
@@ -61,6 +62,7 @@ static const struct atp_info fountain_info = {
.yfact = 43,
.datalen = 81,
.callback = atp_complete_geyser_1_2,
+ .fuzz = 16,
};
static const struct atp_info geyser1_info = {
@@ -71,6 +73,7 @@ static const struct atp_info geyser1_info = {
.yfact = 43,
.datalen = 81,
.callback = atp_complete_geyser_1_2,
+ .fuzz = 16,
};
static const struct atp_info geyser2_info = {
@@ -81,6 +84,7 @@ static const struct atp_info geyser2_info = {
.yfact = 43,
.datalen = 64,
.callback = atp_complete_geyser_1_2,
+ .fuzz = 0,
};
static const struct atp_info geyser3_info = {
@@ -90,6 +94,7 @@ static const struct atp_info geyser3_info = {
.yfact = 64,
.datalen = 64,
.callback = atp_complete_geyser_3_4,
+ .fuzz = 0,
};
static const struct atp_info geyser4_info = {
@@ -99,6 +104,7 @@ static const struct atp_info geyser4_info = {
.yfact = 64,
.datalen = 64,
.callback = atp_complete_geyser_3_4,
+ .fuzz = 0,
};
#define ATP_DEVICE(prod, info) \
@@ -155,9 +161,6 @@ MODULE_DEVICE_TABLE(usb, atp_table);
#define ATP_XSENSORS 26
#define ATP_YSENSORS 16
-/* amount of fuzz this touchpad generates */
-#define ATP_FUZZ 16
-
/* maximum pressure this driver will report */
#define ATP_PRESSURE 300
@@ -455,7 +458,7 @@ static void atp_detect_size(struct atp *dev)
input_set_abs_params(dev->input, ABS_X, 0,
(dev->info->xsensors_17 - 1) *
dev->info->xfact - 1,
- ATP_FUZZ, 0);
+ dev->info->fuzz, 0);
break;
}
}
@@ -843,10 +846,10 @@ static int atp_probe(struct usb_interface *iface,
input_set_abs_params(input_dev, ABS_X, 0,
(dev->info->xsensors - 1) * dev->info->xfact - 1,
- ATP_FUZZ, 0);
+ dev->info->fuzz, 0);
input_set_abs_params(input_dev, ABS_Y, 0,
(dev->info->ysensors - 1) * dev->info->yfact - 1,
- ATP_FUZZ, 0);
+ dev->info->fuzz, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);
set_bit(EV_KEY, input_dev->evbit);
--
1.7.9.5
^ permalink raw reply related
* [PATCH v3 2/3] input: appletouch: implement sensor data smoothing
From: Clinton Sprain @ 2014-03-09 6:17 UTC (permalink / raw)
To: linux-input; +Cc: Dmitry Torokhov, Henrik Rydberg
In-Reply-To: <531C042D.5090103@gmail.com>
Use smoothed version of sensor array data to calculate movement and add weight
to prior values when calculating average. This gives more granular and more
predictable movement.
Signed-off-by: Clinton Sprain <clintonsprain@gmail.com>
---
drivers/input/mouse/appletouch.c | 72 ++++++++++++++++++++++++++++----------
1 file changed, 53 insertions(+), 19 deletions(-)
diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c
index 2745832..0252bab 100644
--- a/drivers/input/mouse/appletouch.c
+++ b/drivers/input/mouse/appletouch.c
@@ -332,7 +332,11 @@ static void atp_reinit(struct work_struct *work)
static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
int *z, int *fingers)
{
- int i;
+ int i, k;
+ int smooth[nb_sensors + 8];
+ int smooth_tmp[nb_sensors + 8];
+ int scale = 12;
+
/* values to calculate mean */
int pcum = 0, psum = 0;
int is_increasing = 0;
@@ -344,9 +348,6 @@ static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
if (is_increasing)
is_increasing = 0;
- continue;
- }
-
/*
* Makes the finger detection more versatile. For example,
* two fingers with no gap will be detected. Also, my
@@ -361,27 +362,60 @@ static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
*
* - Jason Parekh <jasonparekh@gmail.com>
*/
- if (i < 1 ||
+
+ } else if (i < 1 ||
(!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) {
(*fingers)++;
is_increasing = 1;
} else if (i > 0 && (xy_sensors[i - 1] - xy_sensors[i] > threshold)) {
is_increasing = 0;
}
+ }
- /*
- * Subtracts threshold so a high sensor that just passes the
- * threshold won't skew the calculated absolute coordinate.
- * Fixes an issue where slowly moving the mouse would
- * occasionally jump a number of pixels (slowly moving the
- * finger makes this issue most apparent.)
- */
- pcum += (xy_sensors[i] - threshold) * i;
- psum += (xy_sensors[i] - threshold);
+ /*
+ * Use a smoothed version of sensor data for movement calculations, to
+ * combat noise without needing to rely so heavily on a threshold.
+ * This improves tracking.
+ *
+ * The smoothed array is bigger than the original so that the smoothing
+ * doesn't result in edge values being truncated.
+ */
+
+ for (i = 0; i < 4; i++)
+ smooth[i] = 0;
+ for (i = nb_sensors + 4; i < nb_sensors + 8; i++)
+ smooth[i] = 0;
+
+ /* Pull base values, scaled up to help avoid truncation errors. */
+
+ for (i = 0; i < nb_sensors; i++)
+ smooth[i + 4] = xy_sensors[i] << scale;
+
+ for (k = 0; k < 4; k++) {
+
+ /* Handle edge. */
+ smooth_tmp[0] = (smooth[0] * 1 + smooth[1]) >> 1;
+
+ /* Average values with neighbors. */
+ for (i = 1; i < nb_sensors + 7; i++)
+ smooth_tmp[i] = (smooth[i - 1] + smooth[i] * 2 + smooth[i + 1]) >> 2;
+
+ /* Handle other edge. */
+ smooth_tmp[nb_sensors + 7] = (smooth[nb_sensors + 7] + smooth[nb_sensors + 6]) >> 1;
+
+ for (i = 0; i < nb_sensors + 7; i++)
+ smooth[i] = smooth_tmp[i];
+ }
+
+ for (i = 0; i < nb_sensors + 8; i++) {
+ if ((smooth[i] >> scale) > 0) { /* Skip individual values if */
+ pcum += (smooth[i]) * i; /* they are small enough to */
+ psum += (smooth[i]); /* be truncated to 0 by our */
+ } /* scale; mostly just noise. */
}
if (psum > 0) {
- *z = psum;
+ *z = psum >> scale; /* Scale down pressure output. */
return pcum * fact / psum;
}
@@ -559,8 +593,8 @@ static void atp_complete_geyser_1_2(struct urb *urb)
if (x && y) {
if (dev->x_old != -1) {
- x = (dev->x_old * 3 + x) >> 2;
- y = (dev->y_old * 3 + y) >> 2;
+ x = (dev->x_old * 7 + x) >> 3;
+ y = (dev->y_old * 7 + y) >> 3;
dev->x_old = x;
dev->y_old = y;
@@ -671,8 +705,8 @@ static void atp_complete_geyser_3_4(struct urb *urb)
if (x && y) {
if (dev->x_old != -1) {
- x = (dev->x_old * 3 + x) >> 2;
- y = (dev->y_old * 3 + y) >> 2;
+ x = (dev->x_old * 7 + x) >> 3;
+ y = (dev->y_old * 7 + y) >> 3;
dev->x_old = x;
dev->y_old = y;
--
1.7.9.5
^ permalink raw reply related
* [PATCH v3 3/3] input: appletouch: fix jumps when additional fingers are detected
From: Clinton Sprain @ 2014-03-09 6:19 UTC (permalink / raw)
To: linux-input; +Cc: Dmitry Torokhov, Henrik Rydberg
In-Reply-To: <531C042D.5090103@gmail.com>
Addresses issues related to when a second finger enters or leaves the field,
causing the cursor to jump or the page to scroll unexpectedly; now, we
discard any movement change that happens at the exact moment we detect a
change in the number of fingers touching the trackpad. This doesn't
completely resolve the issue but does greatly mitigate it.
Signed-off-by: Clinton Sprain <clintonsprain@gmail.com>
---
drivers/input/mouse/appletouch.c | 27 +++++++++++++++++++++------
1 file changed, 21 insertions(+), 6 deletions(-)
diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c
index 0252bab..0c7bc6e 100644
--- a/drivers/input/mouse/appletouch.c
+++ b/drivers/input/mouse/appletouch.c
@@ -206,6 +206,7 @@ struct atp {
bool valid; /* are the samples valid? */
bool size_detect_done;
bool overflow_warned;
+ int fingers_old; /* last reported finger count */
int x_old; /* last reported x/y, */
int y_old; /* used for smoothing */
signed char xy_cur[ATP_XSENSORS + ATP_YSENSORS];
@@ -508,7 +509,7 @@ static void atp_complete_geyser_1_2(struct urb *urb)
{
int x, y, x_z, y_z, x_f, y_f;
int retval, i, j;
- int key;
+ int key, fingers;
struct atp *dev = urb->context;
int status = atp_status_check(urb);
@@ -591,7 +592,9 @@ static void atp_complete_geyser_1_2(struct urb *urb)
dev->info->yfact, &y_z, &y_f);
key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
- if (x && y) {
+ fingers = max(x_f, y_f);
+
+ if (x && y && (fingers == dev->fingers_old)) {
if (dev->x_old != -1) {
x = (dev->x_old * 7 + x) >> 3;
y = (dev->y_old * 7 + y) >> 3;
@@ -608,7 +611,7 @@ static void atp_complete_geyser_1_2(struct urb *urb)
input_report_abs(dev->input, ABS_Y, y);
input_report_abs(dev->input, ABS_PRESSURE,
min(ATP_PRESSURE, x_z + y_z));
- atp_report_fingers(dev->input, max(x_f, y_f));
+ atp_report_fingers(dev->input, fingers);
}
dev->x_old = x;
dev->y_old = y;
@@ -616,6 +619,7 @@ static void atp_complete_geyser_1_2(struct urb *urb)
} else if (!x && !y) {
dev->x_old = dev->y_old = -1;
+ dev->fingers_old = 0;
input_report_key(dev->input, BTN_TOUCH, 0);
input_report_abs(dev->input, ABS_PRESSURE, 0);
atp_report_fingers(dev->input, 0);
@@ -624,6 +628,10 @@ static void atp_complete_geyser_1_2(struct urb *urb)
memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
}
+ if (fingers != dev->fingers_old)
+ dev->x_old = dev->y_old = -1;
+ dev->fingers_old = fingers;
+
input_report_key(dev->input, BTN_LEFT, key);
input_sync(dev->input);
@@ -641,7 +649,7 @@ static void atp_complete_geyser_3_4(struct urb *urb)
{
int x, y, x_z, y_z, x_f, y_f;
int retval, i, j;
- int key;
+ int key, fingers;
struct atp *dev = urb->context;
int status = atp_status_check(urb);
@@ -703,7 +711,9 @@ static void atp_complete_geyser_3_4(struct urb *urb)
dev->info->yfact, &y_z, &y_f);
key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
- if (x && y) {
+ fingers = max(x_f, y_f);
+
+ if (x && y && (fingers == dev->fingers_old)) {
if (dev->x_old != -1) {
x = (dev->x_old * 7 + x) >> 3;
y = (dev->y_old * 7 + y) >> 3;
@@ -720,7 +730,7 @@ static void atp_complete_geyser_3_4(struct urb *urb)
input_report_abs(dev->input, ABS_Y, y);
input_report_abs(dev->input, ABS_PRESSURE,
min(ATP_PRESSURE, x_z + y_z));
- atp_report_fingers(dev->input, max(x_f, y_f));
+ atp_report_fingers(dev->input, fingers);
}
dev->x_old = x;
dev->y_old = y;
@@ -728,6 +738,7 @@ static void atp_complete_geyser_3_4(struct urb *urb)
} else if (!x && !y) {
dev->x_old = dev->y_old = -1;
+ dev->fingers_old = 0;
input_report_key(dev->input, BTN_TOUCH, 0);
input_report_abs(dev->input, ABS_PRESSURE, 0);
atp_report_fingers(dev->input, 0);
@@ -736,6 +747,10 @@ static void atp_complete_geyser_3_4(struct urb *urb)
memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
}
+ if (fingers != dev->fingers_old)
+ dev->x_old = dev->y_old = -1;
+ dev->fingers_old = fingers;
+
input_report_key(dev->input, BTN_LEFT, key);
input_sync(dev->input);
--
1.7.9.5
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox