From patchwork Fri May 17 13:12:50 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TWljaGFsIFZva8OhxI0=?= X-Patchwork-Id: 10947969 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 87A7B13AD for ; Fri, 17 May 2019 13:13:14 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7673F27DA4 for ; Fri, 17 May 2019 13:13:14 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6AC2327E63; Fri, 17 May 2019 13:13:14 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 437BF27DA4 for ; Fri, 17 May 2019 13:13:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728449AbfEQNNM (ORCPT ); Fri, 17 May 2019 09:13:12 -0400 Received: from uho.ysoft.cz ([81.19.3.130]:33846 "EHLO uho.ysoft.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728434AbfEQNNM (ORCPT ); Fri, 17 May 2019 09:13:12 -0400 Received: from iota-build.ysoft.local (unknown [10.1.5.151]) by uho.ysoft.cz (Postfix) with ESMTP id BBFD3A0795; Fri, 17 May 2019 15:13:09 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ysoft.com; s=20160406-ysoft-com; t=1558098789; bh=YOn6mM87TjxR80/dccn4u4pUAqod87oqS9b7+tHqE/E=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=As8u2HFFsehCUZ8yhzq1ZjgrEuTFXiGZ79yV0NvbyuJMzjC2tBW/dWp8sgT+8xTz9 X1OcEJAOPLKIXsDktkDQJ8vdzWi02mSIeXV+AonOgC37YCkjMLZbxy2A/Vdq5n66NM +f1WR8cG/VX3pelWaP1qid/KxC2QJIN6PEHQE/2Q= From: =?utf-8?b?TWljaGFsIFZva8OhxI0=?= To: Dmitry Torokhov , Rob Herring Cc: Mark Rutland , Shawn Guo , Sascha Hauer , Fabio Estevam , linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Pengutronix Kernel Team , =?utf-8?b?TWljaGFsIFZva8Oh?= =?utf-8?b?xI0=?= Subject: [RFC PATCH v2 1/4] dt-bindings: input: Add support for the MPR121 without interrupt line Date: Fri, 17 May 2019 15:12:50 +0200 Message-Id: <1558098773-47416-2-git-send-email-michal.vokac@ysoft.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1558098773-47416-1-git-send-email-michal.vokac@ysoft.com> References: <1558098773-47416-1-git-send-email-michal.vokac@ysoft.com> MIME-Version: 1.0 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Normally, the MPR121 controller uses separate interrupt line to notify the I2C host that a key was touched/released. To support platforms that can not use the interrupt line, polling of the MPR121 registers can be used. Signed-off-by: Michal Vokáč --- Changes since v1: - Document the polled binding in the original file, do not create a new one. (Rob) Documentation/devicetree/bindings/input/mpr121-touchkey.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/input/mpr121-touchkey.txt b/Documentation/devicetree/bindings/input/mpr121-touchkey.txt index b7c61ee5841b..97f55273d473 100644 --- a/Documentation/devicetree/bindings/input/mpr121-touchkey.txt +++ b/Documentation/devicetree/bindings/input/mpr121-touchkey.txt @@ -1,9 +1,14 @@ -* Freescale MPR121 Controllor +* Freescale MPR121 Controller Required Properties: -- compatible: Should be "fsl,mpr121-touchkey" +- compatible: Should be one of: + - "fsl,mpr121-touchkey" - MPR121 with interrupt line + - "fsl,mpr121-touchkey-polled" - MPR121 with polling - reg: The I2C slave address of the device. - interrupts: The interrupt number to the cpu. + In case of "fsl,mpr121-touchkey-polled" the interrupt + line is not used and hence the interrupts property is + not required. - vdd-supply: Phandle to the Vdd power supply. - linux,keycodes: Specifies an array of numeric keycode values to be used for reporting button presses. The array can From patchwork Fri May 17 13:12:51 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TWljaGFsIFZva8OhxI0=?= X-Patchwork-Id: 10947971 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 37A9B6C5 for ; Fri, 17 May 2019 13:13:19 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2441626E3C for ; Fri, 17 May 2019 13:13:19 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 18DFB27C05; Fri, 17 May 2019 13:13:19 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 05FBF27E01 for ; Fri, 17 May 2019 13:13:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728597AbfEQNNQ (ORCPT ); Fri, 17 May 2019 09:13:16 -0400 Received: from uho.ysoft.cz ([81.19.3.130]:33852 "EHLO uho.ysoft.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728436AbfEQNNN (ORCPT ); Fri, 17 May 2019 09:13:13 -0400 Received: from iota-build.ysoft.local (unknown [10.1.5.151]) by uho.ysoft.cz (Postfix) with ESMTP id 02946A0798; Fri, 17 May 2019 15:13:10 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ysoft.com; s=20160406-ysoft-com; t=1558098790; bh=fazVZE/9JB0aEaL8Pjb9pVhh2/e5QmZcZhX8s6G11fk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=QU4mT+5zWnOaXJobZMYfXHacdU5TlRBeubMstkHmzIM6lAbEaYGaocKIsYnVyrM6E Z9t2uuUMinXg+iKT4C++PqAGcO8YVZ5++a2roXeQ99GvrR1MOskw0ZlXHB4bvvdiyr RIWfn0sqXKrgSey9BS5z+BNjox+gLQB8h7SCCL/g= From: =?utf-8?b?TWljaGFsIFZva8OhxI0=?= To: Dmitry Torokhov , Rob Herring Cc: Mark Rutland , Shawn Guo , Sascha Hauer , Fabio Estevam , linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Pengutronix Kernel Team , =?utf-8?b?TWljaGFsIFZva8Oh?= =?utf-8?b?xI0=?= Subject: [RFC PATCH v2 2/4] Input: mpr121-polled: Add polling variant of the MPR121 touchkey driver Date: Fri, 17 May 2019 15:12:51 +0200 Message-Id: <1558098773-47416-3-git-send-email-michal.vokac@ysoft.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1558098773-47416-1-git-send-email-michal.vokac@ysoft.com> References: <1558098773-47416-1-git-send-email-michal.vokac@ysoft.com> MIME-Version: 1.0 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This driver is based on the original driver with interrupts. Polling driver may be used in cases where the MPR121 chip is connected using only the I2C interface and the interrupt line is not available. Signed-off-by: Michal Vokáč --- drivers/input/keyboard/Kconfig | 13 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/mpr121_touchkey_polled.c | 417 ++++++++++++++++++++++++ 3 files changed, 431 insertions(+) create mode 100644 drivers/input/keyboard/mpr121_touchkey_polled.c diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 52d7f55fca32..b61cf6e4f1ba 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -418,6 +418,19 @@ config KEYBOARD_MPR121 To compile this driver as a module, choose M here: the module will be called mpr121_touchkey. +config KEYBOARD_MPR121_POLLED + tristate "Polled Freescale MPR121 Touchkey" + depends on I2C + help + Say Y here if you have Freescale MPR121 touchkey controller + chip in your system connected only using the I2C line without + the interrupt line. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mpr121_touchkey_polled. + config KEYBOARD_SNVS_PWRKEY tristate "IMX SNVS Power Key Driver" depends on ARCH_MXC || COMPILE_TEST diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 182e92985dbf..903f50842844 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o +obj-$(CONFIG_KEYBOARD_MPR121_POLLED) += mpr121_touchkey_polled.o obj-$(CONFIG_KEYBOARD_MTK_PMIC) += mtk-pmic-keys.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o diff --git a/drivers/input/keyboard/mpr121_touchkey_polled.c b/drivers/input/keyboard/mpr121_touchkey_polled.c new file mode 100644 index 000000000000..e5e80530c9d8 --- /dev/null +++ b/drivers/input/keyboard/mpr121_touchkey_polled.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Touchkey driver for Freescale MPR121 Controllor +// +// Copyright (C) 2011 Freescale Semiconductor, Inc. +// Author: Zhang Jiejing +// +// Based on mcs_touchkey.c +// +// Copyright (C) 2019 Y Soft Corporation, a.s. +// Author: Pavel Staněk +// Author: Michal Vokáč +// +// Reworked into polled driver based on mpr121_touchkey.c + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ +#define ELE_TOUCH_STATUS_0_ADDR 0x0 +#define ELE_TOUCH_STATUS_1_ADDR 0X1 +#define MHD_RISING_ADDR 0x2b +#define NHD_RISING_ADDR 0x2c +#define NCL_RISING_ADDR 0x2d +#define FDL_RISING_ADDR 0x2e +#define MHD_FALLING_ADDR 0x2f +#define NHD_FALLING_ADDR 0x30 +#define NCL_FALLING_ADDR 0x31 +#define FDL_FALLING_ADDR 0x32 +#define ELE0_TOUCH_THRESHOLD_ADDR 0x41 +#define ELE0_RELEASE_THRESHOLD_ADDR 0x42 +#define AFE_CONF_ADDR 0x5c +#define FILTER_CONF_ADDR 0x5d + +/* + * ELECTRODE_CONF_ADDR: This register configures the number of + * enabled capacitance sensing inputs and its run/suspend mode. + */ +#define ELECTRODE_CONF_ADDR 0x5e +#define ELECTRODE_CONF_QUICK_CHARGE 0x80 +#define AUTO_CONFIG_CTRL_ADDR 0x7b +#define AUTO_CONFIG_USL_ADDR 0x7d +#define AUTO_CONFIG_LSL_ADDR 0x7e +#define AUTO_CONFIG_TL_ADDR 0x7f + +/* Threshold of touch/release trigger */ +#define TOUCH_THRESHOLD 0x08 +#define RELEASE_THRESHOLD 0x05 +/* Masks for touch and release triggers */ +#define TOUCH_STATUS_MASK 0xfff +/* MPR121 has 12 keys */ +#define MPR121_MAX_KEY_COUNT 12 + +#define MPR121_POLL_INTERVAL 50 +#define MPR121_POLL_INTERVAL_MIN 10 +#define MPR121_POLL_INTERVAL_MAX 200 +#define MPR121_POLL_INTERVAL_REINIT 500 +#define MPR121_POLL_RETRY_MAX 4 + +struct mpr121_polled { + struct i2c_client *client; + struct input_dev *input_dev; + struct input_polled_dev *poll_dev; + unsigned int statusbits; + unsigned int keycount; + u32 keycodes[MPR121_MAX_KEY_COUNT]; + u8 read_errors; + int vdd_uv; +}; + +struct mpr121_polled_init_register { + int addr; + u8 val; +}; + +static const struct mpr121_polled_init_register init_reg_table[] = { + { MHD_RISING_ADDR, 0x1 }, + { NHD_RISING_ADDR, 0x1 }, + { MHD_FALLING_ADDR, 0x1 }, + { NHD_FALLING_ADDR, 0x1 }, + { NCL_FALLING_ADDR, 0xff }, + { FDL_FALLING_ADDR, 0x02 }, + { FILTER_CONF_ADDR, 0x04 }, + { AFE_CONF_ADDR, 0x0b }, + { AUTO_CONFIG_CTRL_ADDR, 0x0b }, +}; + +static void mpr121_polled_vdd_supply_disable(void *data) +{ + struct regulator *vdd_supply = data; + + regulator_disable(vdd_supply); +} + +static struct regulator *mpr121_polled_vdd_supply_init(struct device *dev) +{ + struct regulator *vdd_supply; + int err; + + vdd_supply = devm_regulator_get(dev, "vdd"); + if (IS_ERR(vdd_supply)) { + dev_err(dev, "failed to get vdd regulator: %ld\n", + PTR_ERR(vdd_supply)); + return vdd_supply; + } + + err = regulator_enable(vdd_supply); + if (err) { + dev_err(dev, "failed to enable vdd regulator: %d\n", err); + return ERR_PTR(err); + } + + err = devm_add_action(dev, mpr121_polled_vdd_supply_disable, + vdd_supply); + if (err) { + regulator_disable(vdd_supply); + dev_err(dev, "failed to add disable regulator action: %d\n", + err); + return ERR_PTR(err); + } + + return vdd_supply; +} + +static int mpr121_polled_phys_init(struct mpr121_polled *mpr121, + struct i2c_client *client) +{ + const struct mpr121_polled_init_register *reg; + unsigned char usl, lsl, tl, eleconf; + int i, t, vdd, ret; + + /* Set stop mode prior to writing any register */ + ret = i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, 0x00); + if (ret < 0) + goto err_i2c_write; + + /* Set up touch/release threshold for ele0-ele11 */ + for (i = 0; i <= MPR121_MAX_KEY_COUNT; i++) { + t = ELE0_TOUCH_THRESHOLD_ADDR + (i * 2); + ret = i2c_smbus_write_byte_data(client, t, TOUCH_THRESHOLD); + if (ret < 0) + goto err_i2c_write; + ret = i2c_smbus_write_byte_data(client, t + 1, + RELEASE_THRESHOLD); + if (ret < 0) + goto err_i2c_write; + } + + /* Set up init register */ + for (i = 0; i < ARRAY_SIZE(init_reg_table); i++) { + reg = &init_reg_table[i]; + ret = i2c_smbus_write_byte_data(client, reg->addr, reg->val); + if (ret < 0) + goto err_i2c_write; + } + + /* + * Capacitance on sensing input varies and needs to be compensated. + * The internal MPR121-auto-configuration can do this if it's + * registers are set properly (based on vdd_uv). + */ + vdd = mpr121->vdd_uv / 1000; + usl = ((vdd - 700) * 256) / vdd; + lsl = (usl * 65) / 100; + tl = (usl * 90) / 100; + ret = i2c_smbus_write_byte_data(client, AUTO_CONFIG_USL_ADDR, usl); + ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_LSL_ADDR, lsl); + ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_TL_ADDR, tl); + + /* + * Quick charge bit will let the capacitive charge to ready + * state quickly, or the buttons may not function after system + * boot. + */ + eleconf = mpr121->keycount | ELECTRODE_CONF_QUICK_CHARGE; + ret |= i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, + eleconf); + if (ret != 0) + goto err_i2c_write; + + dev_dbg(&client->dev, "set up with %x keys.\n", mpr121->keycount); + + return 0; + +err_i2c_write: + dev_err(&client->dev, "i2c write error: %d\n", ret); + return ret; +} + +static void mpr121_polled_release_keys(struct mpr121_polled *mpr121) +{ + struct input_dev *input = mpr121->input_dev; + struct i2c_client *client = mpr121->client; + unsigned long statusbits; + unsigned int key_num; + + if (!mpr121->statusbits) + return; + + statusbits = mpr121->statusbits; + mpr121->statusbits = 0; + for_each_set_bit(key_num, &statusbits, mpr121->keycount) { + unsigned int key_val; + + key_val = mpr121->keycodes[key_num]; + + input_event(input, EV_MSC, MSC_SCAN, key_num); + input_report_key(input, key_val, 0); + + dev_dbg(&client->dev, "key %d %d %s\n", key_num, key_val, + "released"); + } + input_sync(input); +} + +static int mpr121_polled_process_keys(struct mpr121_polled *mpr121) +{ + struct input_dev *input = mpr121->input_dev; + struct i2c_client *client = mpr121->client; + unsigned long bit_changed; + unsigned int key_num; + int reg; + + reg = i2c_smbus_read_word_data(client, ELE_TOUCH_STATUS_0_ADDR); + if (reg < 0) { + dev_err(&client->dev, "i2c read error: %d\n", reg); + return reg; + } + + reg &= TOUCH_STATUS_MASK; + bit_changed = reg ^ mpr121->statusbits; + mpr121->statusbits = reg; + for_each_set_bit(key_num, &bit_changed, mpr121->keycount) { + unsigned int key_val, pressed; + + pressed = reg & BIT(key_num); + key_val = mpr121->keycodes[key_num]; + + input_event(input, EV_MSC, MSC_SCAN, key_num); + input_report_key(input, key_val, pressed); + + dev_dbg(&client->dev, "key %d %d %s\n", key_num, key_val, + pressed ? "pressed" : "released"); + } + input_sync(input); + + return 0; +} +static void mpr121_poll(struct input_polled_dev *dev) +{ + struct mpr121_polled *mpr121 = dev->private; + struct i2c_client *client = mpr121->client; + int ret; + + if (mpr121->read_errors > MPR121_POLL_RETRY_MAX) { + dev_warn(&client->dev, + "device does not respond, re-initializing\n"); + mpr121_polled_release_keys(mpr121); + ret = mpr121_polled_phys_init(mpr121, client); + if (ret >= 0) { + mpr121->read_errors = 0; + dev->poll_interval = MPR121_POLL_INTERVAL; + } else { + dev->poll_interval = MPR121_POLL_INTERVAL_REINIT; + } + } + + ret = mpr121_polled_process_keys(mpr121); + if (ret < 0) { + mpr121->read_errors++; + return; + } + + mpr121->read_errors = 0; +} + +static int mpr121_polled_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct regulator *vdd_supply; + struct mpr121_polled *mpr121; + struct input_dev *input_dev; + struct input_polled_dev *poll_dev; + int error; + int i; + + vdd_supply = mpr121_polled_vdd_supply_init(dev); + if (IS_ERR(vdd_supply)) + return PTR_ERR(vdd_supply); + + mpr121 = devm_kzalloc(dev, sizeof(*mpr121), GFP_KERNEL); + if (!mpr121) + return -ENOMEM; + + poll_dev = devm_input_allocate_polled_device(dev); + if (!poll_dev) + return -ENOMEM; + + mpr121->vdd_uv = regulator_get_voltage(vdd_supply); + mpr121->client = client; + mpr121->input_dev = poll_dev->input; + mpr121->poll_dev = poll_dev; + mpr121->keycount = device_property_read_u32_array(dev, "linux,keycodes", + NULL, 0); + if (mpr121->keycount > MPR121_MAX_KEY_COUNT) { + dev_err(dev, "too many keys defined (%d)\n", mpr121->keycount); + return -EINVAL; + } + + error = device_property_read_u32_array(dev, "linux,keycodes", + mpr121->keycodes, + mpr121->keycount); + if (error) { + dev_err(dev, + "failed to read linux,keycode property: %d\n", error); + return error; + } + + poll_dev->private = mpr121; + poll_dev->poll = mpr121_poll; + poll_dev->poll_interval = MPR121_POLL_INTERVAL; + poll_dev->poll_interval_max = MPR121_POLL_INTERVAL_MAX; + poll_dev->poll_interval_min = MPR121_POLL_INTERVAL_MIN; + + input_dev = poll_dev->input; + input_dev->name = "Freescale MPR121 Polled Touchkey"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = dev; + if (device_property_read_bool(dev, "autorepeat")) + __set_bit(EV_REP, input_dev->evbit); + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + input_dev->keycode = mpr121->keycodes; + input_dev->keycodesize = sizeof(mpr121->keycodes[0]); + input_dev->keycodemax = mpr121->keycount; + + for (i = 0; i < mpr121->keycount; i++) + input_set_capability(input_dev, EV_KEY, mpr121->keycodes[i]); + + error = mpr121_polled_phys_init(mpr121, client); + if (error) { + dev_err(dev, "Failed to init register\n"); + return error; + } + + error = input_register_polled_device(poll_dev); + if (error) + return error; + + i2c_set_clientdata(client, mpr121); + + return 0; +} + +static int __maybe_unused mpr121_polled_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, 0x00); + + return 0; +} + +static int __maybe_unused mpr121_polled_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mpr121_polled *mpr121 = i2c_get_clientdata(client); + + i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, + mpr121->keycount); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(mpr121_polled_pm_ops, mpr121_polled_suspend, + mpr121_polled_resume); + +static const struct i2c_device_id mpr121_polled_id[] = { + { "mpr121_polled", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mpr121_polled_id); + +#ifdef CONFIG_OF +static const struct of_device_id mpr121_polled_dt_match_table[] = { + { .compatible = "fsl,mpr121-touchkey-polled" }, + { }, +}; +MODULE_DEVICE_TABLE(of, mpr121_polled_dt_match_table); +#endif + +static struct i2c_driver mpr121_polled_driver = { + .driver = { + .name = "mpr121-polled", + .pm = &mpr121_polled_pm_ops, + .of_match_table = of_match_ptr(mpr121_polled_dt_match_table), + }, + .id_table = mpr121_polled_id, + .probe = mpr121_polled_probe, +}; + +module_i2c_driver(mpr121_polled_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michal Vokáč "); +MODULE_DESCRIPTION("Polled driver for Freescale MPR121 chip"); From patchwork Fri May 17 13:12:52 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TWljaGFsIFZva8OhxI0=?= X-Patchwork-Id: 10947973 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9D37A13AD for ; Fri, 17 May 2019 13:13:25 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8C20727C05 for ; Fri, 17 May 2019 13:13:25 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8022427E71; Fri, 17 May 2019 13:13:25 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D0A6527E01 for ; Fri, 17 May 2019 13:13:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728434AbfEQNNN (ORCPT ); Fri, 17 May 2019 09:13:13 -0400 Received: from uho.ysoft.cz ([81.19.3.130]:33858 "EHLO uho.ysoft.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728315AbfEQNNN (ORCPT ); Fri, 17 May 2019 09:13:13 -0400 Received: from iota-build.ysoft.local (unknown [10.1.5.151]) by uho.ysoft.cz (Postfix) with ESMTP id 34FF0A261B; Fri, 17 May 2019 15:13:10 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ysoft.com; s=20160406-ysoft-com; t=1558098790; bh=Hb5jFQIPCxZTBWzwQ8QSsG9NLvE8GRE3e3X2L85MB0U=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WarrosLAZNFZI6MltRWt9NKz2G3W8ySrQTAnORm44L5yhYleCL+FjuP5y8yGhvKHx CbjHsfXYUHO2NXn/D7kjrqZWQnQVlV00ic4ke+VCYVpX9k4dZhh5TA+IGJm106A6pc VEBm8sZWN1nTNDLY9MyvhzlqMFjcLSwcJZgkxYtY= From: =?utf-8?b?TWljaGFsIFZva8OhxI0=?= To: Dmitry Torokhov , Rob Herring Cc: Mark Rutland , Shawn Guo , Sascha Hauer , Fabio Estevam , linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Pengutronix Kernel Team , =?utf-8?b?TWljaGFsIFZva8Oh?= =?utf-8?b?xI0=?= Subject: [RFC PATCH v2 3/4] Input: mpr121-polled: Add write-through cache to detect corrupted registers Date: Fri, 17 May 2019 15:12:52 +0200 Message-Id: <1558098773-47416-4-git-send-email-michal.vokac@ysoft.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1558098773-47416-1-git-send-email-michal.vokac@ysoft.com> References: <1558098773-47416-1-git-send-email-michal.vokac@ysoft.com> MIME-Version: 1.0 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The MPR121 chip (and I2C bus in general) is quite sensitive to ESD. An electrostatic discharge can easily cause a reset of the MPR121 chip. Even though the chip then recovers and respond to read/write commands, it is not properly initialized. This state can be detected using a write-through cache of the internal registers. Each time a register is written to, its value is stored in the cache and marked as valid. Once per MPR121_REG_CACHE_CHECK_LIMIT polls one valid cache value is compared with its corresponding register value. In case of difference an error counter is increased. If the error counter limit is exceeded, the chip is re-initialized. Signed-off-by: Michal Vokáč --- drivers/input/keyboard/mpr121_touchkey_polled.c | 100 +++++++++++++++++++++--- 1 file changed, 88 insertions(+), 12 deletions(-) diff --git a/drivers/input/keyboard/mpr121_touchkey_polled.c b/drivers/input/keyboard/mpr121_touchkey_polled.c index e5e80530c9d8..6536d9b2eeb8 100644 --- a/drivers/input/keyboard/mpr121_touchkey_polled.c +++ b/drivers/input/keyboard/mpr121_touchkey_polled.c @@ -67,6 +67,19 @@ #define MPR121_POLL_INTERVAL_REINIT 500 #define MPR121_POLL_RETRY_MAX 4 +#define MPR121_REG_CACHE_MIN_ADDR 0x2b +#define MPR121_REG_CACHE_MAX_ADDR 0x7f +#define MPR121_REG_CACHE_SIZE \ + (MPR121_REG_CACHE_MAX_ADDR - MPR121_REG_CACHE_MIN_ADDR + 1) +#define MPR121_REG_CACHE_CHECK_LIMIT 8 +#define mpr121_addr_to_cache_idx(addr) (addr - MPR121_REG_CACHE_MIN_ADDR) +#define mpr121_cache_idx_to_addr(idx) (idx + MPR121_REG_CACHE_MIN_ADDR) + +struct mpr121_polled_reg_cache { + bool valid; + u8 value; +}; + struct mpr121_polled { struct i2c_client *client; struct input_dev *input_dev; @@ -76,6 +89,9 @@ struct mpr121_polled { u32 keycodes[MPR121_MAX_KEY_COUNT]; u8 read_errors; int vdd_uv; + struct mpr121_polled_reg_cache reg_cache[MPR121_REG_CACHE_SIZE]; + u8 reg_cache_check_count; + u8 reg_cache_next_check_item; }; struct mpr121_polled_init_register { @@ -95,6 +111,29 @@ static const struct mpr121_polled_init_register init_reg_table[] = { { AUTO_CONFIG_CTRL_ADDR, 0x0b }, }; +static int mpr121_polled_write_reg(struct mpr121_polled *mpr121, u8 addr, + u8 value) +{ + struct i2c_client *client = mpr121->client; + int ret; + + ret = i2c_smbus_write_byte_data(client, addr, value); + if (ret < 0) { + dev_err(&client->dev, "i2c write error: %d\n", ret); + return ret; + } + + if (addr >= MPR121_REG_CACHE_MIN_ADDR && + addr <= MPR121_REG_CACHE_MAX_ADDR) { + u8 i = mpr121_addr_to_cache_idx(addr); + + mpr121->reg_cache[i].valid = 1; + mpr121->reg_cache[i].value = value; + } + + return 0; +} + static void mpr121_polled_vdd_supply_disable(void *data) { struct regulator *vdd_supply = data; @@ -140,18 +179,18 @@ static int mpr121_polled_phys_init(struct mpr121_polled *mpr121, int i, t, vdd, ret; /* Set stop mode prior to writing any register */ - ret = i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, 0x00); + ret = mpr121_polled_write_reg(mpr121, ELECTRODE_CONF_ADDR, 0x00); if (ret < 0) goto err_i2c_write; /* Set up touch/release threshold for ele0-ele11 */ for (i = 0; i <= MPR121_MAX_KEY_COUNT; i++) { t = ELE0_TOUCH_THRESHOLD_ADDR + (i * 2); - ret = i2c_smbus_write_byte_data(client, t, TOUCH_THRESHOLD); + ret = mpr121_polled_write_reg(mpr121, t, TOUCH_THRESHOLD); if (ret < 0) goto err_i2c_write; - ret = i2c_smbus_write_byte_data(client, t + 1, - RELEASE_THRESHOLD); + ret = mpr121_polled_write_reg(mpr121, t + 1, + RELEASE_THRESHOLD); if (ret < 0) goto err_i2c_write; } @@ -159,7 +198,7 @@ static int mpr121_polled_phys_init(struct mpr121_polled *mpr121, /* Set up init register */ for (i = 0; i < ARRAY_SIZE(init_reg_table); i++) { reg = &init_reg_table[i]; - ret = i2c_smbus_write_byte_data(client, reg->addr, reg->val); + ret = mpr121_polled_write_reg(mpr121, reg->addr, reg->val); if (ret < 0) goto err_i2c_write; } @@ -173,9 +212,9 @@ static int mpr121_polled_phys_init(struct mpr121_polled *mpr121, usl = ((vdd - 700) * 256) / vdd; lsl = (usl * 65) / 100; tl = (usl * 90) / 100; - ret = i2c_smbus_write_byte_data(client, AUTO_CONFIG_USL_ADDR, usl); - ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_LSL_ADDR, lsl); - ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_TL_ADDR, tl); + ret = mpr121_polled_write_reg(mpr121, AUTO_CONFIG_USL_ADDR, usl); + ret |= mpr121_polled_write_reg(mpr121, AUTO_CONFIG_LSL_ADDR, lsl); + ret |= mpr121_polled_write_reg(mpr121, AUTO_CONFIG_TL_ADDR, tl); /* * Quick charge bit will let the capacitive charge to ready @@ -183,7 +222,7 @@ static int mpr121_polled_phys_init(struct mpr121_polled *mpr121, * boot. */ eleconf = mpr121->keycount | ELECTRODE_CONF_QUICK_CHARGE; - ret |= i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, + ret |= mpr121_polled_write_reg(mpr121, ELECTRODE_CONF_ADDR, eleconf); if (ret != 0) goto err_i2c_write; @@ -256,6 +295,36 @@ static int mpr121_polled_process_keys(struct mpr121_polled *mpr121) return 0; } + +static int mpr121_polled_check_regs(struct mpr121_polled *mpr121) +{ + struct i2c_client *client = mpr121->client; + int i, reg; + + /* Skip registers that were never written to (have invalid cache) */ + i = mpr121->reg_cache_next_check_item; + for (; i < MPR121_REG_CACHE_SIZE; i++) + if (mpr121->reg_cache[i].valid) + break; + + if (i == MPR121_REG_CACHE_SIZE) { + mpr121->reg_cache_next_check_item = 0; + return 0; + } + + reg = i2c_smbus_read_byte_data(client, mpr121_cache_idx_to_addr(i)); + if (reg < 0) { + dev_err(&client->dev, "i2c read error: %d\n", reg); + return -1; + } + + if (reg != mpr121->reg_cache[i].value) + return -1; + + mpr121->reg_cache_next_check_item = i + 1; + return 0; +} + static void mpr121_poll(struct input_polled_dev *dev) { struct mpr121_polled *mpr121 = dev->private; @@ -282,6 +351,13 @@ static void mpr121_poll(struct input_polled_dev *dev) } mpr121->read_errors = 0; + mpr121->reg_cache_check_count++; + if (mpr121->reg_cache_check_count > MPR121_REG_CACHE_CHECK_LIMIT) { + mpr121->reg_cache_check_count = 0; + ret = mpr121_polled_check_regs(mpr121); + if (ret < 0) + mpr121->read_errors++; + } } static int mpr121_polled_probe(struct i2c_client *client, @@ -366,8 +442,9 @@ static int mpr121_polled_probe(struct i2c_client *client, static int __maybe_unused mpr121_polled_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); + struct mpr121_polled *mpr121 = i2c_get_clientdata(client); - i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, 0x00); + mpr121_polled_write_reg(mpr121, ELECTRODE_CONF_ADDR, 0x00); return 0; } @@ -377,8 +454,7 @@ static int __maybe_unused mpr121_polled_resume(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct mpr121_polled *mpr121 = i2c_get_clientdata(client); - i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, - mpr121->keycount); + mpr121_polled_write_reg(mpr121, ELECTRODE_CONF_ADDR, mpr121->keycount); return 0; } From patchwork Fri May 17 13:12:53 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TWljaGFsIFZva8OhxI0=?= X-Patchwork-Id: 10947977 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B58356C5 for ; Fri, 17 May 2019 13:13:35 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A5CDA27C05 for ; Fri, 17 May 2019 13:13:35 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9A4CE27E63; Fri, 17 May 2019 13:13:35 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4EBF927EED for ; Fri, 17 May 2019 13:13:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728216AbfEQNNY (ORCPT ); Fri, 17 May 2019 09:13:24 -0400 Received: from uho.ysoft.cz ([81.19.3.130]:33865 "EHLO uho.ysoft.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728425AbfEQNNN (ORCPT ); Fri, 17 May 2019 09:13:13 -0400 Received: from iota-build.ysoft.local (unknown [10.1.5.151]) by uho.ysoft.cz (Postfix) with ESMTP id 6EDDDA5237; Fri, 17 May 2019 15:13:10 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ysoft.com; s=20160406-ysoft-com; t=1558098790; bh=3fqHTQWwFFxlsRLtnrTf/2ecqWmAHakKFLqkKHlonlU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Qvea1/7zBUyKK/QmHvm2t+OSYWM8gWwChVAPWRmYb0ba7LTRoheLc4Lv9OBilytKO hBOLHcvwAWo1lEBd6u3zZDDRuEcze6m5aLCEkWa44Z0KoyIlxKNxKxxwRyz+Z+8bxQ OBICb3iu/ZX6mfuxSkOj8POjlnsj2rSNh6RlM0uQ= From: =?utf-8?b?TWljaGFsIFZva8OhxI0=?= To: Dmitry Torokhov , Rob Herring Cc: Mark Rutland , Shawn Guo , Sascha Hauer , Fabio Estevam , linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Pengutronix Kernel Team , =?utf-8?b?TWljaGFsIFZva8Oh?= =?utf-8?b?xI0=?= Subject: [RFC PATCH v2 4/4] ARM: dts: imx6dl-yapp4: Enable MPR121 touch keypad on Hydra Date: Fri, 17 May 2019 15:12:53 +0200 Message-Id: <1558098773-47416-5-git-send-email-michal.vokac@ysoft.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1558098773-47416-1-git-send-email-michal.vokac@ysoft.com> References: <1558098773-47416-1-git-send-email-michal.vokac@ysoft.com> MIME-Version: 1.0 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Enable the I2C connected touch keypad on Hydra board. Use the polled binding as the interrupt line is not available. Signed-off-by: Michal Vokáč --- arch/arm/boot/dts/imx6dl-yapp4-common.dtsi | 12 ++++++++++++ arch/arm/boot/dts/imx6dl-yapp4-hydra.dts | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/arch/arm/boot/dts/imx6dl-yapp4-common.dtsi b/arch/arm/boot/dts/imx6dl-yapp4-common.dtsi index e8d800fec637..65a670e5bd4f 100644 --- a/arch/arm/boot/dts/imx6dl-yapp4-common.dtsi +++ b/arch/arm/boot/dts/imx6dl-yapp4-common.dtsi @@ -4,6 +4,7 @@ #include #include +#include #include / { @@ -330,6 +331,17 @@ vcc-supply = <&sw2_reg>; status = "disabled"; }; + + touchkeys: keys@5a { + compatible = "fsl,mpr121-touchkey-polled"; + reg = <0x5a>; + vdd-supply = <&sw2_reg>; + autorepeat; + linux,keycodes = , , , , , + , , , , + , , ; + status = "disabled"; + }; }; &iomuxc { diff --git a/arch/arm/boot/dts/imx6dl-yapp4-hydra.dts b/arch/arm/boot/dts/imx6dl-yapp4-hydra.dts index f97927064750..84c275bfdd38 100644 --- a/arch/arm/boot/dts/imx6dl-yapp4-hydra.dts +++ b/arch/arm/boot/dts/imx6dl-yapp4-hydra.dts @@ -45,6 +45,10 @@ status = "okay"; }; +&touchkeys { + status = "okay"; +}; + &usdhc3 { status = "okay"; };