From patchwork Wed Dec 2 20:42:25 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nick Dyer X-Patchwork-Id: 7751791 Return-Path: X-Original-To: patchwork-linux-input@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 79B409F387 for ; Wed, 2 Dec 2015 20:54:13 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 74F1920532 for ; Wed, 2 Dec 2015 20:54:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 48472203F1 for ; Wed, 2 Dec 2015 20:54:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757062AbbLBUxk (ORCPT ); Wed, 2 Dec 2015 15:53:40 -0500 Received: from kdh-gw.itdev.co.uk ([89.21.227.133]:48498 "EHLO hermes.kdh.itdev.co.uk" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1754544AbbLBUvR (ORCPT ); Wed, 2 Dec 2015 15:51:17 -0500 Received: from localhost.localdomain (unknown [80.229.148.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) by hermes.kdh.itdev.co.uk (Postfix) with ESMTPSA id 36F4380996; Wed, 2 Dec 2015 20:42:36 +0000 (GMT) From: Nick Dyer To: Dmitry Torokhov Cc: Yufeng Shen , Benson Leung , linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Alan Bowens , Javier Martinez Canillas , Chris Healy , Nick Dyer Subject: [PATCH RFC 2/8] Input: atmel_mxt_ts - add diagnostic data retrieval via debugfs Date: Wed, 2 Dec 2015 20:42:25 +0000 Message-Id: <1449088951-7069-3-git-send-email-nick.dyer@itdev.co.uk> X-Mailer: git-send-email 2.5.0 In-Reply-To: <1449088951-7069-1-git-send-email-nick.dyer@itdev.co.uk> References: <1449088951-7069-1-git-send-email-nick.dyer@itdev.co.uk> Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Retrieve refs data from the T37 diagnostic data object and expose it via a binary attribute in debugfs. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 212 +++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 4a1d218..a96fec4 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -28,6 +28,7 @@ #include #include #include +#include /* Firmware files */ #define MXT_FW_NAME "maxtouch.fw" @@ -124,6 +125,17 @@ struct t9_range { #define MXT_COMMS_CTRL 0 #define MXT_COMMS_CMD 1 +/* MXT_DEBUG_DIAGNOSTIC_T37 */ +#define MXT_DIAGNOSTIC_PAGEUP 0x01 +#define MXT_DIAGNOSTIC_DELTAS 0x10 +#define MXT_DIAGNOSTIC_SIZE 128 + +struct t37_debug { + u8 mode; + u8 page; + u8 data[MXT_DIAGNOSTIC_SIZE]; +}; + /* Define for MXT_GEN_COMMAND_T6 */ #define MXT_BOOT_VALUE 0xa5 #define MXT_RESET_VALUE 0x01 @@ -205,6 +217,20 @@ struct mxt_object { u8 num_report_ids; } __packed; +#ifdef CONFIG_DEBUG_FS +struct mxt_dbg { + u16 t37_address; + u16 diag_cmd_address; + struct t37_debug *t37_buf; + u16 *debug_buf; + unsigned int t37_pages; + unsigned int t37_nodes; + + struct dentry *debugfs_dir; + struct dentry *deltas_file; +}; +#endif + /* Each client has this additional data */ struct mxt_data { struct i2c_client *client; @@ -233,6 +259,7 @@ struct mxt_data { u8 num_touchids; u8 multitouch; struct t7_config t7_cfg; + struct mxt_dbg dbg; /* Cached parameters from object table */ u16 T5_address; @@ -2043,6 +2070,188 @@ recheck: return 0; } +#ifdef CONFIG_DEBUG_FS +static u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x, + unsigned int y) +{ + struct mxt_dbg *dbg = &data->dbg; + unsigned int ofs, page; + + ofs = (y + (x * (data->info.matrix_ysize))) * sizeof(u16); + page = ofs / MXT_DIAGNOSTIC_SIZE; + ofs %= MXT_DIAGNOSTIC_SIZE; + + return get_unaligned_le16(&dbg->t37_buf[page].data[ofs]); +} + +static void mxt_convert_debug_pages(struct mxt_data *data) +{ + struct mxt_dbg *dbg = &data->dbg; + unsigned int x = 0; + unsigned int y = 0; + unsigned int i; + + for (i = 0; i < dbg->t37_nodes; i++) { + dbg->debug_buf[i] = mxt_get_debug_value(data, x, y); + + /* Next value */ + if (++x >= data->info.matrix_xsize) { + x = 0; + y++; + } + } +} + +static int mxt_open_deltas(struct inode *inode, struct file *file) +{ + struct mxt_data *data = inode->i_private; + struct mxt_dbg *dbg = &data->dbg; + int retries = 0; + int page; + int ret; + u8 mode = MXT_DIAGNOSTIC_DELTAS; + u8 cmd = mode; + struct t37_debug *p; + + for (page = 0; page < dbg->t37_pages; page++) { + p = dbg->t37_buf + page; + + ret = mxt_write_reg(data->client, dbg->diag_cmd_address, + cmd); + if (ret) + return ret; + + retries = 0; + + /* Poll until command is actioned */ + msleep(20); +wait_cmd: + /* Read first two bytes only */ + ret = __mxt_read_reg(data->client, dbg->t37_address, + 2, p); + if (ret) + return ret; + + if ((p->mode != mode) || (p->page != page)) { + if (retries++ > 100) + return -EINVAL; + + msleep(20); + goto wait_cmd; + } + + /* Read entire T37 page */ + ret = __mxt_read_reg(data->client, dbg->t37_address, + sizeof(struct t37_debug), p); + if (ret) + return ret; + + dev_dbg(&data->client->dev, "%s page:%d retries:%d\n", + __func__, page, retries); + + /* For remaining pages, write PAGEUP rather than mode */ + cmd = MXT_DIAGNOSTIC_PAGEUP; + } + + mxt_convert_debug_pages(data); + file->private_data = data; + + return 0; +} + +static ssize_t mxt_read_deltas(struct file *file, char __user *ubuf, + size_t count, loff_t *offp) +{ + struct mxt_data *data = file->private_data; + + return simple_read_from_buffer(ubuf, count, offp, + data->dbg.debug_buf, + data->dbg.t37_nodes * sizeof(u16)); +} + +static const struct file_operations atmel_mxt_deltas_fops = { + .open = mxt_open_deltas, + .read = mxt_read_deltas, +}; + +static void mxt_debugfs_remove(struct mxt_data *data) +{ + debugfs_remove_recursive(data->dbg.debugfs_dir); +} + +static void mxt_debugfs_init(struct mxt_data *data) +{ + struct mxt_dbg *dbg = &data->dbg; + struct mxt_object *object; + char dirname[50]; + + object = mxt_get_object(data, MXT_GEN_COMMAND_T6); + if (!object) + return; + + dbg->diag_cmd_address = object->start_address + MXT_COMMAND_DIAGNOSTIC; + + object = mxt_get_object(data, MXT_DEBUG_DIAGNOSTIC_T37); + if (!object) + return; + + if (mxt_obj_size(object) != sizeof(struct t37_debug)) { + dev_warn(&data->client->dev, "Bad T37 size"); + return; + } + + dbg->t37_address = object->start_address; + + snprintf(dirname, sizeof(dirname), "heatmap-%s-%s", + dev_driver_string(&data->client->dev), + dev_name(&data->client->dev)); + + dbg->debugfs_dir = debugfs_create_dir(dirname, NULL); + if (!dbg->debugfs_dir) { + dev_err(&data->client->dev, "Error creating debugfs dir\n"); + return; + } + + /* Calculate size of data and allocate buffer */ + dbg->t37_nodes = data->info.matrix_xsize * data->info.matrix_ysize; + dbg->t37_pages = dbg->t37_nodes * sizeof(u16) + / sizeof(dbg->t37_buf->data) + 1; + dbg->debug_buf = devm_kzalloc(&data->client->dev, + dbg->t37_nodes * sizeof(u16), + GFP_KERNEL); + if (!dbg->debug_buf) + goto error; + + dbg->t37_buf = devm_kzalloc(&data->client->dev, + sizeof(struct t37_debug) * dbg->t37_pages, + GFP_KERNEL); + if (!dbg->t37_buf) + goto error; + + dbg->deltas_file = debugfs_create_file("deltas", S_IRUGO, + dbg->debugfs_dir, data, + &atmel_mxt_deltas_fops); + if (!dbg->deltas_file) + goto error; + + dbg->deltas_file->d_inode->i_size = dbg->t37_nodes * sizeof(u16); + + return; + +error: + dev_err(&data->client->dev, "Error creating debugfs entry\n"); + mxt_debugfs_remove(data); +} +#else +static inline void mxt_debugfs_remove(struct mxt_data *data) +{ +} + +static inline void mxt_debugfs_init(struct mxt_data *data) +{ +} +#endif /* CONFIG_DEBUG_FS */ + static int mxt_configure_objects(struct mxt_data *data, const struct firmware *cfg) { @@ -2070,6 +2279,8 @@ static int mxt_configure_objects(struct mxt_data *data, dev_warn(dev, "No touch object detected\n"); } + mxt_debugfs_init(data); + dev_info(dev, "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", info->family_id, info->variant_id, info->version >> 4, @@ -2617,6 +2828,7 @@ static int mxt_remove(struct i2c_client *client) { struct mxt_data *data = i2c_get_clientdata(client); + mxt_debugfs_remove(data); sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); free_irq(data->irq, data); mxt_free_input_device(data);