From: Patrick Vogelaar <Patrick.Vogelaar@gigatronik.com> To: unlisted-recipients:; (no To-header on input) Cc: <dmitry.torokhov@gmail.com>, <linux-kernel@vger.kernel.org>, <devicetree@vger.kernel.org>, <linux-input@vger.kernel.org>, <fengguang.wu@intel.com>, Patrick Vogelaar <Patrick.Vogelaar@gigatronik.com> Subject: [PATCH v2 1/2] add driver for cypress cy8cmbr3102 Date: Tue, 21 Feb 2017 07:40:20 +0100 [thread overview] Message-ID: <1487659221-14665-2-git-send-email-Patrick.Vogelaar@gigatronik.com> (raw) In-Reply-To: <1487659221-14665-1-git-send-email-Patrick.Vogelaar@gigatronik.com> Version 2: * removed .owner * based on branch next Signed-off-by: Patrick Vogelaar <Patrick.Vogelaar@gigatronik.com> --- drivers/input/misc/Kconfig | 12 +++ drivers/input/misc/Makefile | 1 + drivers/input/misc/cy8cmbr3102.c | 221 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+) create mode 100644 drivers/input/misc/cy8cmbr3102.c diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 5b6c522..be3071e 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -822,4 +822,16 @@ config INPUT_HISI_POWERKEY To compile this driver as a module, choose M here: the module will be called hisi_powerkey. +config INPUT_CY8CMBR3102 + tristate "Cypress CY8CMBR3102 CapSense Express controller" + depends on I2C + select INPUT_POLLDEV + default n + help + Say yes here to enable support for the Cypress CY8CMBR3102 + CapSense Express controller. + + To compile this driver as a module, choose M here: the module + will be called cy8cmbr3102. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index b10523f..f9959ed 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -77,3 +77,4 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o +obj-$(CONFIG_INPUT_CY8CMBR3102) += cy8cmbr3102.o diff --git a/drivers/input/misc/cy8cmbr3102.c b/drivers/input/misc/cy8cmbr3102.c new file mode 100644 index 0000000..ed67a63 --- /dev/null +++ b/drivers/input/misc/cy8cmbr3102.c @@ -0,0 +1,221 @@ +/* Driver for Cypress CY8CMBR3102 CapSense Express Controller + * + * (C) 2017 by Gigatronik Technologies GmbH + * Author: Patrick Vogelaar <patrick.vogelaar@gigatronik.com> + * All rights reserved. + * + * This code is based on mma8450.c and atmel_captouch.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * NOTE: This implementation does not implement the full range of functions the + * Cypress CY8CMBR3102 CapSense Express controller provides. It only implements + * its use for connected touch buttons (yet). + */ + +#define DRV_VERSION "0.1" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input-polldev.h> +#include <linux/of.h> + +/* I2C Registers */ +#define CY8CMBR3102_DEVICE_ID_REG 0x90 +#define CY8CMBR3102_BUTTON_STAT 0xAA + + +#define CY8CMBR3102_MAX_NUM_OF_BUTTONS 0x02 +#define CY8CMBR3102_DRV_NAME "cy8cmbr3102" +#define CY8CMBR3102_POLL_INTERVAL 200 +#define CY8CMBR3102_POLL_INTERVAL_MAX 300 +#define CY8CMBR3102_DEVICE_ID 2561 +#define CY8CMBR3102_MAX_RETRY 5 + +/* + * @i2c_client: I2C slave device client pointer + * @idev: Input (polled) device pointer + * @num_btn: Number of buttons + * @keycodes: map of button# to KeyCode + * @cy8cmbr3102_lock: mutex lock + */ +struct cy8cmbr3102_device { + struct i2c_client *client; + struct input_polled_dev *idev; + u32 num_btn; + u32 keycodes[CY8CMBR3102_MAX_NUM_OF_BUTTONS]; + struct mutex cy8cmbr3102_lock; +}; + +static const struct i2c_device_id cy8cmbr3102_idtable[] = { + {"cy8cmbr3102", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, cy8cmbr3102_idtable); + +static void cy8cmbr3102_poll(struct input_polled_dev *idev) +{ + struct cy8cmbr3102_device *dev = idev->private; + int i, ret, btn_state; + + mutex_lock(&dev->cy8cmbr3102_lock); + + ret = i2c_smbus_read_word_data(dev->client, CY8CMBR3102_BUTTON_STAT); + if (ret < 0) { + dev_err(&dev->client->dev, "i2c io error: %d\n", ret); + mutex_unlock(&dev->cy8cmbr3102_lock); + return; + } + + for (i = 0; i < dev->num_btn; i++) { + btn_state = ret & (0x1 << i); + input_report_key(idev->input, dev->keycodes[i], btn_state); + input_sync(idev->input); + } + + mutex_unlock(&dev->cy8cmbr3102_lock); +} + +static int cy8cmbr3102_remove(struct i2c_client *client) +{ + struct cy8cmbr3102_device *dev = i2c_get_clientdata(client); + struct input_polled_dev *idev = dev->idev; + + dev_dbg(&client->dev, "%s\n", __func__); + + mutex_destroy(&dev->cy8cmbr3102_lock); + input_unregister_polled_device(idev); + + return 0; +} + +static int cy8cmbr3102_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cy8cmbr3102_device *drvdata; + struct device *dev = &client->dev; + struct device_node *node; + int i, err, ret; + + dev_dbg(&client->dev, "%s\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) { + dev_err(dev, "needed i2c functionality is not supported\n"); + return -EINVAL; + } + + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->client = client; + i2c_set_clientdata(client, drvdata); + + /* device is in low-power mode and needs to be waken up */ + for (i = 0; (i < CY8CMBR3102_MAX_RETRY) && + (ret != CY8CMBR3102_DEVICE_ID); i++) { + ret = i2c_smbus_read_word_data(drvdata->client, + CY8CMBR3102_DEVICE_ID_REG); + dev_dbg(dev, "DEVICE_ID (%i): %i\n", i, ret); + } + + if (ret < 0) { + dev_err(&client->dev, "i2c io error: %d\n", ret); + return -EIO; + } else if (ret != CY8CMBR3102_DEVICE_ID) { + dev_err(&client->dev, "read device ID %i is not equal to %i!\n", + ret, CY8CMBR3102_DEVICE_ID); + return -ENXIO; + } + dev_dbg(dev, "device identified by device ID\n"); + + drvdata->idev = devm_input_allocate_polled_device(dev); + if (!drvdata->idev) { + dev_err(dev, "failed to allocate input device\n"); + return -ENOMEM; + } + + node = dev->of_node; + if (!node) { + dev_err(dev, "failed to find matching node in device tree\n"); + return -EINVAL; + } + + if (of_property_read_bool(node, "autorepeat")) + __set_bit(EV_REP, drvdata->idev->input->evbit); + + drvdata->num_btn = of_property_count_u32_elems(node, "linux,keycodes"); + if (drvdata->num_btn > CY8CMBR3102_MAX_NUM_OF_BUTTONS) + drvdata->num_btn = CY8CMBR3102_MAX_NUM_OF_BUTTONS; + + err = of_property_read_u32_array(node, "linux,keycodes", + drvdata->keycodes, drvdata->num_btn); + + if (err) { + dev_err(dev, "failed to read linux,keycodes property: %d\n", + err); + return err; + } + + for (i = 0; i < drvdata->num_btn; i++) + __set_bit(drvdata->keycodes[i], drvdata->idev->input->keybit); + + drvdata->idev->input->id.bustype = BUS_I2C; + drvdata->idev->input->id.product = 0x3102; + drvdata->idev->input->id.version = 0; + drvdata->idev->input->name = CY8CMBR3102_DRV_NAME; + drvdata->idev->poll = cy8cmbr3102_poll; + drvdata->idev->poll_interval = CY8CMBR3102_POLL_INTERVAL; + drvdata->idev->poll_interval_max = CY8CMBR3102_POLL_INTERVAL_MAX; + drvdata->idev->private = drvdata; + drvdata->idev->input->keycode = drvdata->keycodes; + drvdata->idev->input->keycodemax = drvdata->num_btn; + drvdata->idev->input->keycodesize = sizeof(drvdata->keycodes[0]); + __set_bit(EV_KEY, drvdata->idev->input->evbit); + + mutex_init(&drvdata->cy8cmbr3102_lock); + + err = input_register_polled_device(drvdata->idev); + if (err) + return err; + + dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id of_cy8cmbr3102_match[] = { + {.compatible = "cypress,cy8cmbr3102", }, + {} +}; +MODULE_DEVICE_TABLE(of, of_cy8cmbr3102_match); +#endif + +static struct i2c_driver cy8cmbr3102_driver = { + .driver = { + .name = "cy8cmbr3102", + .of_match_table = of_match_ptr(of_cy8cmbr3102_match), + }, + .probe = cy8cmbr3102_probe, + .remove = cy8cmbr3102_remove, + .id_table = cy8cmbr3102_idtable, +}; +module_i2c_driver(cy8cmbr3102_driver); + +MODULE_AUTHOR("Patrick Vogelaar <patrick.vogelaar@gigatronik.com>"); +MODULE_DESCRIPTION("Cypress CY8CMBR3102 CapSense Express controller"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); -- 2.7.4
WARNING: multiple messages have this Message-ID (diff)
From: Patrick Vogelaar <Patrick.Vogelaar@gigatronik.com> Cc: dmitry.torokhov@gmail.com, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-input@vger.kernel.org, fengguang.wu@intel.com, Patrick Vogelaar <Patrick.Vogelaar@gigatronik.com> Subject: [PATCH v2 1/2] add driver for cypress cy8cmbr3102 Date: Tue, 21 Feb 2017 07:40:20 +0100 [thread overview] Message-ID: <1487659221-14665-2-git-send-email-Patrick.Vogelaar@gigatronik.com> (raw) In-Reply-To: <1487659221-14665-1-git-send-email-Patrick.Vogelaar@gigatronik.com> Version 2: * removed .owner * based on branch next Signed-off-by: Patrick Vogelaar <Patrick.Vogelaar@gigatronik.com> --- drivers/input/misc/Kconfig | 12 +++ drivers/input/misc/Makefile | 1 + drivers/input/misc/cy8cmbr3102.c | 221 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+) create mode 100644 drivers/input/misc/cy8cmbr3102.c diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 5b6c522..be3071e 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -822,4 +822,16 @@ config INPUT_HISI_POWERKEY To compile this driver as a module, choose M here: the module will be called hisi_powerkey. +config INPUT_CY8CMBR3102 + tristate "Cypress CY8CMBR3102 CapSense Express controller" + depends on I2C + select INPUT_POLLDEV + default n + help + Say yes here to enable support for the Cypress CY8CMBR3102 + CapSense Express controller. + + To compile this driver as a module, choose M here: the module + will be called cy8cmbr3102. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index b10523f..f9959ed 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -77,3 +77,4 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o +obj-$(CONFIG_INPUT_CY8CMBR3102) += cy8cmbr3102.o diff --git a/drivers/input/misc/cy8cmbr3102.c b/drivers/input/misc/cy8cmbr3102.c new file mode 100644 index 0000000..ed67a63 --- /dev/null +++ b/drivers/input/misc/cy8cmbr3102.c @@ -0,0 +1,221 @@ +/* Driver for Cypress CY8CMBR3102 CapSense Express Controller + * + * (C) 2017 by Gigatronik Technologies GmbH + * Author: Patrick Vogelaar <patrick.vogelaar@gigatronik.com> + * All rights reserved. + * + * This code is based on mma8450.c and atmel_captouch.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * NOTE: This implementation does not implement the full range of functions the + * Cypress CY8CMBR3102 CapSense Express controller provides. It only implements + * its use for connected touch buttons (yet). + */ + +#define DRV_VERSION "0.1" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input-polldev.h> +#include <linux/of.h> + +/* I2C Registers */ +#define CY8CMBR3102_DEVICE_ID_REG 0x90 +#define CY8CMBR3102_BUTTON_STAT 0xAA + + +#define CY8CMBR3102_MAX_NUM_OF_BUTTONS 0x02 +#define CY8CMBR3102_DRV_NAME "cy8cmbr3102" +#define CY8CMBR3102_POLL_INTERVAL 200 +#define CY8CMBR3102_POLL_INTERVAL_MAX 300 +#define CY8CMBR3102_DEVICE_ID 2561 +#define CY8CMBR3102_MAX_RETRY 5 + +/* + * @i2c_client: I2C slave device client pointer + * @idev: Input (polled) device pointer + * @num_btn: Number of buttons + * @keycodes: map of button# to KeyCode + * @cy8cmbr3102_lock: mutex lock + */ +struct cy8cmbr3102_device { + struct i2c_client *client; + struct input_polled_dev *idev; + u32 num_btn; + u32 keycodes[CY8CMBR3102_MAX_NUM_OF_BUTTONS]; + struct mutex cy8cmbr3102_lock; +}; + +static const struct i2c_device_id cy8cmbr3102_idtable[] = { + {"cy8cmbr3102", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, cy8cmbr3102_idtable); + +static void cy8cmbr3102_poll(struct input_polled_dev *idev) +{ + struct cy8cmbr3102_device *dev = idev->private; + int i, ret, btn_state; + + mutex_lock(&dev->cy8cmbr3102_lock); + + ret = i2c_smbus_read_word_data(dev->client, CY8CMBR3102_BUTTON_STAT); + if (ret < 0) { + dev_err(&dev->client->dev, "i2c io error: %d\n", ret); + mutex_unlock(&dev->cy8cmbr3102_lock); + return; + } + + for (i = 0; i < dev->num_btn; i++) { + btn_state = ret & (0x1 << i); + input_report_key(idev->input, dev->keycodes[i], btn_state); + input_sync(idev->input); + } + + mutex_unlock(&dev->cy8cmbr3102_lock); +} + +static int cy8cmbr3102_remove(struct i2c_client *client) +{ + struct cy8cmbr3102_device *dev = i2c_get_clientdata(client); + struct input_polled_dev *idev = dev->idev; + + dev_dbg(&client->dev, "%s\n", __func__); + + mutex_destroy(&dev->cy8cmbr3102_lock); + input_unregister_polled_device(idev); + + return 0; +} + +static int cy8cmbr3102_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cy8cmbr3102_device *drvdata; + struct device *dev = &client->dev; + struct device_node *node; + int i, err, ret; + + dev_dbg(&client->dev, "%s\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) { + dev_err(dev, "needed i2c functionality is not supported\n"); + return -EINVAL; + } + + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->client = client; + i2c_set_clientdata(client, drvdata); + + /* device is in low-power mode and needs to be waken up */ + for (i = 0; (i < CY8CMBR3102_MAX_RETRY) && + (ret != CY8CMBR3102_DEVICE_ID); i++) { + ret = i2c_smbus_read_word_data(drvdata->client, + CY8CMBR3102_DEVICE_ID_REG); + dev_dbg(dev, "DEVICE_ID (%i): %i\n", i, ret); + } + + if (ret < 0) { + dev_err(&client->dev, "i2c io error: %d\n", ret); + return -EIO; + } else if (ret != CY8CMBR3102_DEVICE_ID) { + dev_err(&client->dev, "read device ID %i is not equal to %i!\n", + ret, CY8CMBR3102_DEVICE_ID); + return -ENXIO; + } + dev_dbg(dev, "device identified by device ID\n"); + + drvdata->idev = devm_input_allocate_polled_device(dev); + if (!drvdata->idev) { + dev_err(dev, "failed to allocate input device\n"); + return -ENOMEM; + } + + node = dev->of_node; + if (!node) { + dev_err(dev, "failed to find matching node in device tree\n"); + return -EINVAL; + } + + if (of_property_read_bool(node, "autorepeat")) + __set_bit(EV_REP, drvdata->idev->input->evbit); + + drvdata->num_btn = of_property_count_u32_elems(node, "linux,keycodes"); + if (drvdata->num_btn > CY8CMBR3102_MAX_NUM_OF_BUTTONS) + drvdata->num_btn = CY8CMBR3102_MAX_NUM_OF_BUTTONS; + + err = of_property_read_u32_array(node, "linux,keycodes", + drvdata->keycodes, drvdata->num_btn); + + if (err) { + dev_err(dev, "failed to read linux,keycodes property: %d\n", + err); + return err; + } + + for (i = 0; i < drvdata->num_btn; i++) + __set_bit(drvdata->keycodes[i], drvdata->idev->input->keybit); + + drvdata->idev->input->id.bustype = BUS_I2C; + drvdata->idev->input->id.product = 0x3102; + drvdata->idev->input->id.version = 0; + drvdata->idev->input->name = CY8CMBR3102_DRV_NAME; + drvdata->idev->poll = cy8cmbr3102_poll; + drvdata->idev->poll_interval = CY8CMBR3102_POLL_INTERVAL; + drvdata->idev->poll_interval_max = CY8CMBR3102_POLL_INTERVAL_MAX; + drvdata->idev->private = drvdata; + drvdata->idev->input->keycode = drvdata->keycodes; + drvdata->idev->input->keycodemax = drvdata->num_btn; + drvdata->idev->input->keycodesize = sizeof(drvdata->keycodes[0]); + __set_bit(EV_KEY, drvdata->idev->input->evbit); + + mutex_init(&drvdata->cy8cmbr3102_lock); + + err = input_register_polled_device(drvdata->idev); + if (err) + return err; + + dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id of_cy8cmbr3102_match[] = { + {.compatible = "cypress,cy8cmbr3102", }, + {} +}; +MODULE_DEVICE_TABLE(of, of_cy8cmbr3102_match); +#endif + +static struct i2c_driver cy8cmbr3102_driver = { + .driver = { + .name = "cy8cmbr3102", + .of_match_table = of_match_ptr(of_cy8cmbr3102_match), + }, + .probe = cy8cmbr3102_probe, + .remove = cy8cmbr3102_remove, + .id_table = cy8cmbr3102_idtable, +}; +module_i2c_driver(cy8cmbr3102_driver); + +MODULE_AUTHOR("Patrick Vogelaar <patrick.vogelaar@gigatronik.com>"); +MODULE_DESCRIPTION("Cypress CY8CMBR3102 CapSense Express controller"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); -- 2.7.4
next prev parent reply other threads:[~2017-02-21 6:41 UTC|newest] Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top 2017-02-21 6:40 [PATCH v2 0/2] add driver for cypress cy8cmbr3102 Patrick Vogelaar 2017-02-21 6:40 ` Patrick Vogelaar 2017-02-21 6:40 ` Patrick Vogelaar [this message] 2017-02-21 6:40 ` [PATCH v2 1/2] " Patrick Vogelaar 2017-03-11 0:42 ` Dmitry Torokhov 2017-03-11 0:42 ` Dmitry Torokhov 2017-03-13 13:55 ` AW: " Vogelaar, Patrick 2017-03-13 13:55 ` Vogelaar, Patrick 2017-02-21 6:40 ` [PATCH v2 2/2] add device tree documentation for cy8cmbr3102 Patrick Vogelaar 2017-02-21 6:40 ` Patrick Vogelaar 2017-02-27 19:52 ` Rob Herring
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=1487659221-14665-2-git-send-email-Patrick.Vogelaar@gigatronik.com \ --to=patrick.vogelaar@gigatronik.com \ --cc=devicetree@vger.kernel.org \ --cc=dmitry.torokhov@gmail.com \ --cc=fengguang.wu@intel.com \ --cc=linux-input@vger.kernel.org \ --cc=linux-kernel@vger.kernel.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.