From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
To: Javier Martinez Canillas <javier@osg.samsung.com>
Cc: Jaechul Lee <jcsing.lee@samsung.com>,
Rob Herring <robh+dt@kernel.org>,
Mark Rutland <mark.rutland@arm.com>,
Catalin Marinas <catalin.marinas@arm.com>,
Will Deacon <will.deacon@arm.com>, Kukjin Kim <kgene@kernel.org>,
Krzysztof Kozlowski <krzk@kernel.org>,
Andi Shyti <andi.shyti@samsung.com>,
Chanwoo Choi <cw00.choi@samsung.com>,
beomho.seo@samsung.com, galaxyra@gmail.com,
linux-arm-kernel@lists.infradead.org,
linux-input@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-samsung-soc@vger.kernel.org
Subject: Re: [PATCH 2/4] input: tm2-touchkey: Add touchkey driver support for TM2
Date: Tue, 3 Jan 2017 10:56:25 -0800 [thread overview]
Message-ID: <20170103185625.GC16032@dtor-ws> (raw)
In-Reply-To: <7794c9b2-48e6-1451-9a3e-fe24410a145d@osg.samsung.com>
On Tue, Jan 03, 2017 at 10:11:39AM -0300, Javier Martinez Canillas wrote:
> Hello Jaechul,
>
> On 01/03/2017 04:57 AM, Jaechul Lee wrote:
> > This patch adds support for the TM2 touch key and led
> > functionlity.
> >
>
> s/functionlity/functionality
>
> > The driver interfaces with userspace through an input device and
> > reports KEY_PHONE and KEY_BACK event types. LED brightness can be
> > controlled by "/sys/class/leds/tm2-touchkey/brightness".
> >
> > Signed-off-by: Jaechul Lee <jcsing.lee@samsung.com>
> > Signed-off-by: Beomho Seo <beomho.seo@samsung.com>
> > ---
> > drivers/input/keyboard/Kconfig | 11 ++
> > drivers/input/keyboard/Makefile | 1 +
> > drivers/input/keyboard/tm2-touchkey.c | 326 ++++++++++++++++++++++++++++++++++
> > 3 files changed, 338 insertions(+)
> > create mode 100644 drivers/input/keyboard/tm2-touchkey.c
> >
> > diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
> > index cbd75cf..72c0ba1 100644
> > --- a/drivers/input/keyboard/Kconfig
> > +++ b/drivers/input/keyboard/Kconfig
> > @@ -666,6 +666,17 @@ config KEYBOARD_TC3589X
> > To compile this driver as a module, choose M here: the
> > module will be called tc3589x-keypad.
> >
> > +config KEYBOARD_TM2_TOUCHKEY
> > + tristate "tm2-touchkey support"
> > + depends on I2C
There should be LED dependency as well.
> > + help
> > + Say Y here to enable the tm2-touchkey.
> > + touchkey driver for tm2. This driver can enable
> > + the interrupt and make input events and control led brightness.
> > +
> > + To compile this driver as a module, choose M here.
> > + module will be called tm2-touchkey
> > +
> > config KEYBOARD_TWL4030
> > tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
> > depends on TWL4030_CORE
> > diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
> > index d9f4cfc..7d9acff 100644
> > --- a/drivers/input/keyboard/Makefile
> > +++ b/drivers/input/keyboard/Makefile
> > @@ -61,6 +61,7 @@ obj-$(CONFIG_KEYBOARD_SUN4I_LRADC) += sun4i-lradc-keys.o
> > obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
> > obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o
> > obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o
> > +obj-$(CONFIG_KEYBOARD_TM2_TOUCHKEY) += tm2-touchkey.o
> > obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
> > obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
> > obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o
> > diff --git a/drivers/input/keyboard/tm2-touchkey.c b/drivers/input/keyboard/tm2-touchkey.c
> > new file mode 100644
> > index 0000000..d9575d8
> > --- /dev/null
> > +++ b/drivers/input/keyboard/tm2-touchkey.c
> > @@ -0,0 +1,326 @@
> > +/*
> > + * Driver for keys on GPIO lines capable of generating interrupts.
This does not match what the driver does.
> > + *
> > + * Copyright 2005 Phil Blundell
> > + * Copyright 2016 Samsung Electronics Co., Ltd.
> > + *
> > + * Author: Beomho Seo <beomho.seo@samsung.com>
> > + * Author: Jaechul Lee <jcsing.lee@samsung.com>
> > + *
> > + * 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/bitops.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/i2c.h>
> > +#include <linux/input.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/irq.h>
> > +#include <linux/leds.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/pm.h>
> > +#include <linux/regulator/consumer.h>
> > +#include <linux/workqueue.h>
> > +
> > +#define TM2_TOUCHKEY_DEV_NAME "tm2-touchkey"
> > +#define TM2_TOUCHKEY_KEYCODE_REG 0x03
> > +#define TM2_TOUCHKEY_BASE_REG 0x00
> > +#define TM2_TOUCHKEY_CMD_LED_ON 0x10
> > +#define TM2_TOUCHKEY_CMD_LED_OFF 0x20
> > +#define TM2_TOUCHKEY_BIT_PRESS_EV BIT(3)
> > +#define TM2_TOUCHKEY_BIT_KEYCODE GENMASK(2, 0)
> > +#define TM2_TOUCHKEY_LED_VOLTAGE_MIN 2500000
> > +#define TM2_TOUCHKEY_LED_VOLTAGE_MAX 3300000
> > +
> > +enum {
> > + TM2_TOUCHKEY_KEY_MENU = 0x1,
> > + TM2_TOUCHKEY_KEY_BACK,
> > +};
> > +
> > +#define tm2_touchkey_power_enable(x) __tm2_touchkey_power_onoff(x, 1)
> > +#define tm2_touchkey_power_disable(x) __tm2_touchkey_power_onoff(x, 0)
> > +
> > +struct tm2_touchkey_data {
> > + struct i2c_client *client;
> > + struct input_dev *input_dev;
> > + struct led_classdev led_dev;
> > +
> > + u8 keycode_type;
> > + u8 pressed;
> > + struct work_struct irq_work;
> > +
> > + bool power_onoff;
> > + struct regulator *regulator_vcc; /* 1.8V */
> > + struct regulator *regulator_vdd; /* 3.3V */
> > +};
> > +
> > +static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev,
> > + enum led_brightness brightness)
> > +{
> > + struct tm2_touchkey_data *samsung_touchkey =
Just call it "touchkey", "samsung_touchkey" is too long for a local
variable.
> > + container_of(led_dev, struct tm2_touchkey_data, led_dev);
> > + u32 volt;
> > + u8 data;
> > +
> > + if (brightness == LED_OFF) {
> > + volt = TM2_TOUCHKEY_LED_VOLTAGE_MIN;
> > + data = TM2_TOUCHKEY_CMD_LED_OFF;
> > + } else {
> > + volt = TM2_TOUCHKEY_LED_VOLTAGE_MAX;
> > + data = TM2_TOUCHKEY_CMD_LED_ON;
> > + }
> > +
> > + regulator_set_voltage(samsung_touchkey->regulator_vdd, volt, volt);
> > + i2c_smbus_write_byte_data(samsung_touchkey->client,
> > + TM2_TOUCHKEY_BASE_REG, data);
> > +}
> > +
> > +static int __tm2_touchkey_power_onoff(struct tm2_touchkey_data
> > + *samsung_touchkey, bool onoff)
> > +{
> > + int ret = 0;
> > +
> > + if (samsung_touchkey->power_onoff == onoff)
> > + return ret;
> > +
> > + if (onoff) {
> > + ret = regulator_enable(samsung_touchkey->regulator_vcc);
> > + if (ret)
> > + return ret;
> > +
> > + ret = regulator_enable(samsung_touchkey->regulator_vdd);
> > + if (ret) {
> > + regulator_disable(samsung_touchkey->regulator_vcc);
> > + return ret;
> > + }
>
> I would add a comment about the sleep here.
Also "bulk" regulator API can be useful here.
>
> > + msleep(150);
> > + } else {
> > + int err;
> > +
> > + err = regulator_disable(samsung_touchkey->regulator_vcc);
> > + if (err)
> > + ret = err;
> > +
> > + err = regulator_disable(samsung_touchkey->regulator_vdd);
> > + if (err && !ret)
> > + ret = err;
> > + }
> > + samsung_touchkey->power_onoff = onoff;
> > +
> > + return ret;
> > +}
> > +
> > +static void tm2_touchkey_irq_work(struct work_struct *irq_work)
> > +{
> > + struct tm2_touchkey_data *samsung_touchkey =
> > + container_of(irq_work, struct tm2_touchkey_data, irq_work);
> > +
> > + if (!samsung_touchkey->pressed) {
> > + input_report_key(samsung_touchkey->input_dev, KEY_PHONE, 0);
> > + input_report_key(samsung_touchkey->input_dev, KEY_BACK, 0);
> > + } else {
> > + if (samsung_touchkey->keycode_type == TM2_TOUCHKEY_KEY_MENU)
> > + input_report_key(samsung_touchkey->input_dev,
> > + KEY_PHONE, 1);
> > + else
> > + input_report_key(samsung_touchkey->input_dev,
> > + KEY_BACK, 1);
> > + }
> > + input_sync(samsung_touchkey->input_dev);
> > +}
There is absolutely no reason for doing this in a work. Just call
input_report_key/input_sync from your threaded IRQ routine.
> > +
> > +static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid)
> > +{
> > + struct tm2_touchkey_data *samsung_touchkey = devid;
> > + u32 data;
> > +
> > + data = i2c_smbus_read_byte_data(samsung_touchkey->client,
> > + TM2_TOUCHKEY_KEYCODE_REG);
> > +
> > + if (data < 0) {
> > + dev_err(&samsung_touchkey->client->dev, "Failed to read i2c data\n");
> > + return IRQ_HANDLED;
> > + }
> > +
> > + samsung_touchkey->keycode_type = data & TM2_TOUCHKEY_BIT_KEYCODE;
> > + samsung_touchkey->pressed = !(data & TM2_TOUCHKEY_BIT_PRESS_EV);
> > +
> > + if (samsung_touchkey->keycode_type != TM2_TOUCHKEY_KEY_MENU &&
> > + samsung_touchkey->keycode_type != TM2_TOUCHKEY_KEY_BACK)
>
> Shouldn't at least a debug message be printed here so the user can
> know that an error occurred and a correct keycode was not received?
>
> > + return IRQ_HANDLED;
> > +
> > + schedule_work(&samsung_touchkey->irq_work);
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static int tm2_touchkey_probe(struct i2c_client *client,
> > + const struct i2c_device_id *id)
> > +{
> > + struct tm2_touchkey_data *samsung_touchkey;
> > + int ret;
> > +
> > + ret = i2c_check_functionality(client->adapter,
> > + I2C_FUNC_SMBUS_BYTE |
> > + I2C_FUNC_SMBUS_BYTE_DATA);
> > + if (!ret) {
> > + dev_err(&client->dev, "No I2C functionality found\n");
> > + return -ENODEV;
> > + }
> > +
> > + samsung_touchkey = devm_kzalloc(&client->dev,
> > + sizeof(struct tm2_touchkey_data), GFP_KERNEL);
sizeof(*touchkey)
> > +
> > + if (!samsung_touchkey) {
> > + dev_err(&client->dev, "Failed to allocate memory.\n");
> > + return -ENOMEM;
> > + }
> > +
> > + samsung_touchkey->client = client;
> > + i2c_set_clientdata(client, samsung_touchkey);
> > + INIT_WORK(&samsung_touchkey->irq_work, tm2_touchkey_irq_work);
Work is not needed.
> > +
> > + /* regulator */
> > + samsung_touchkey->regulator_vcc =
> > + devm_regulator_get(&client->dev, "vcc");
> > + if (IS_ERR(samsung_touchkey->regulator_vcc)) {
> > + dev_err(&client->dev, "Failed to get vcc regulator\n");
> > + return PTR_ERR(samsung_touchkey->regulator_vcc);
> > + }
> > +
> > + samsung_touchkey->regulator_vdd =
> > + devm_regulator_get(&client->dev, "vdd");
> > + if (IS_ERR(samsung_touchkey->regulator_vdd)) {
> > + dev_err(&client->dev, "Failed to get vdd regulator\n");
> > + return PTR_ERR(samsung_touchkey->regulator_vcc);
> > + }
devm_regulator_bulk_get
> > +
> > + /* power */
> > + ret = tm2_touchkey_power_enable(samsung_touchkey);
> > + if (ret) {
> > + dev_err(&client->dev, "Failed to enable power\n");
> > + return ret;
> > + }
You need to install devm action (devm_add_action_or_reset) to disable
power when unloading driver (or if initialization fails).
> > +
> > + /* irq */
> > + ret = devm_request_threaded_irq(&client->dev,
> > + client->irq, NULL,
> > + tm2_touchkey_irq_handler,
> > + IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
I'd rather we rely on IRQ trigger from DT, so just use IRQF_PNESHOT
here.
I'd rather you allocated input device before requesting IRQ. The
registering input device is fine to do after getting IRQ.
> > + TM2_TOUCHKEY_DEV_NAME,
> > + samsung_touchkey);
> > + if (ret) {
> > + dev_err(&client->dev, "Failed to request threaded irq\n");
> > + return ret;
> > + }
> > +
> > + /* input device */
> > + samsung_touchkey->input_dev = devm_input_allocate_device(&client->dev);
> > + if (!samsung_touchkey->input_dev) {
> > + dev_err(&client->dev, "Failed to alloc input device.\n");
> > + return -ENOMEM;
> > + }
> > + samsung_touchkey->input_dev->name = TM2_TOUCHKEY_DEV_NAME;
> > + samsung_touchkey->input_dev->id.bustype = BUS_I2C;
> > + samsung_touchkey->input_dev->dev.parent = &client->dev;
No need to set parent when using devm_input_allocate_device(), it is
done automatically.
> > +
> > + set_bit(EV_KEY, samsung_touchkey->input_dev->evbit);
> > + set_bit(KEY_PHONE, samsung_touchkey->input_dev->keybit);
> > + set_bit(KEY_BACK, samsung_touchkey->input_dev->keybit);
Just do
input_set_capability(touchkey->input_dev, EV_KEY, KEY_PHONE);
input_set_capability(touchkey->input_dev, EV_KEY, KEY_BACK);
> > + input_set_drvdata(samsung_touchkey->input_dev, samsung_touchkey);
> > +
> > + ret = input_register_device(samsung_touchkey->input_dev);
> > + if (ret) {
> > + dev_err(&client->dev, "Failed to register input device.\n");
> > + return ret;
> > + }
> > +
> > + /* led device */
> > + samsung_touchkey->led_dev.name = TM2_TOUCHKEY_DEV_NAME;
> > + samsung_touchkey->led_dev.brightness = LED_FULL;
> > + samsung_touchkey->led_dev.max_brightness = LED_FULL;
> > + samsung_touchkey->led_dev.brightness_set =
> > + tm2_touchkey_led_brightness_set;
> > +
> > + ret = devm_led_classdev_register(&client->dev,
> > + &samsung_touchkey->led_dev);
> > + if (ret < 0) {
> > + dev_err(&client->dev, "Failed to register touchkey led\n");
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void tm2_touchkey_shutdown(struct i2c_client *client)
> > +{
This is something not normally done. Does your platform really keep
rails on when AP is off?
> > + struct tm2_touchkey_data *samsung_touchkey =
> > + i2c_get_clientdata(client);
> > + int ret;
> > +
> > + disable_irq(client->irq);
> > + ret = tm2_touchkey_power_disable(samsung_touchkey);
> > + if (ret)
> > + dev_err(&client->dev, "Failed to disable power\n");
> > +}
> > +
> > +static int tm2_touchkey_suspend(struct device *dev)
__maybe_unused
> > +{
> > + struct tm2_touchkey_data *samsung_touchkey = dev_get_drvdata(dev);
> > + int ret;
> > +
> > + disable_irq(samsung_touchkey->client->irq);
> > + ret = tm2_touchkey_power_disable(samsung_touchkey);
> > + if (ret)
> > + dev_err(dev, "Failed to disable power\n");
> > +
> > + return ret;
> > +}
>
> These two functions are basically the same, can you factor it out?
>
> > +
> > +static int tm2_touchkey_resume(struct device *dev)
__maybe_unused
> > +{
> > + struct tm2_touchkey_data *samsung_touchkey = dev_get_drvdata(dev);
> > + int ret;
> > +
> > + enable_irq(samsung_touchkey->client->irq);
> > + ret = tm2_touchkey_power_enable(samsung_touchkey);
> > + if (ret)
> > + dev_err(dev, "Failed to enable power\n");
> > +
> > + return ret;
> > +}
> > +
> > +static SIMPLE_DEV_PM_OPS(tm2_touchkey_pm_ops, tm2_touchkey_suspend,
> > + tm2_touchkey_resume);
> > +
> > +static const struct i2c_device_id tm2_touchkey_id_table[] = {
> > + {TM2_TOUCHKEY_DEV_NAME, 0},
> > + {},
> > +};
> > +
>
> You need a MODULE_DEVICE_TABLE(i2c, tm2_touchkey_id_table) here so the
> module can be autoloaded when the device is registered.
>
> > +static const struct of_device_id tm2_touchkey_of_match[] = {
> > + {.compatible = "samsung,tm2-touchkey",},
> > + {},
> > +};
> > +
>
> Here a MODULE_DEVICE_TABLE(of, tm2_touchkey_of_match) is not strictly
> needed since the I2C core always reports MODALIAS of the form i2c:<dev>
> but still is good to have so the I2C core can be fixed at some point.
Yes, please add MODULE_DEVICE_TABLE(of, ...).
>
> > +static struct i2c_driver tm2_touchkey_driver = {
> > + .driver = {
> > + .name = TM2_TOUCHKEY_DEV_NAME,
> > + .pm = &tm2_touchkey_pm_ops,
> > + .of_match_table = of_match_ptr(tm2_touchkey_of_match),
> > + },
> > + .probe = tm2_touchkey_probe,
> > + .shutdown = tm2_touchkey_shutdown,
> > + .id_table = tm2_touchkey_id_table,
> > +};
> > +
> > +module_i2c_driver(tm2_touchkey_driver);
> > +
> > +MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>");
> > +MODULE_AUTHOR("Jaechul Lee <jcsing.lee@samsung.com>");
> > +MODULE_DESCRIPTION("Samsung touchkey driver");
> > +MODULE_LICENSE("GPL v2");
> >
Thanks.
--
Dmitry
WARNING: multiple messages have this Message-ID (diff)
From: dmitry.torokhov@gmail.com (Dmitry Torokhov)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 2/4] input: tm2-touchkey: Add touchkey driver support for TM2
Date: Tue, 3 Jan 2017 10:56:25 -0800 [thread overview]
Message-ID: <20170103185625.GC16032@dtor-ws> (raw)
In-Reply-To: <7794c9b2-48e6-1451-9a3e-fe24410a145d@osg.samsung.com>
On Tue, Jan 03, 2017 at 10:11:39AM -0300, Javier Martinez Canillas wrote:
> Hello Jaechul,
>
> On 01/03/2017 04:57 AM, Jaechul Lee wrote:
> > This patch adds support for the TM2 touch key and led
> > functionlity.
> >
>
> s/functionlity/functionality
>
> > The driver interfaces with userspace through an input device and
> > reports KEY_PHONE and KEY_BACK event types. LED brightness can be
> > controlled by "/sys/class/leds/tm2-touchkey/brightness".
> >
> > Signed-off-by: Jaechul Lee <jcsing.lee@samsung.com>
> > Signed-off-by: Beomho Seo <beomho.seo@samsung.com>
> > ---
> > drivers/input/keyboard/Kconfig | 11 ++
> > drivers/input/keyboard/Makefile | 1 +
> > drivers/input/keyboard/tm2-touchkey.c | 326 ++++++++++++++++++++++++++++++++++
> > 3 files changed, 338 insertions(+)
> > create mode 100644 drivers/input/keyboard/tm2-touchkey.c
> >
> > diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
> > index cbd75cf..72c0ba1 100644
> > --- a/drivers/input/keyboard/Kconfig
> > +++ b/drivers/input/keyboard/Kconfig
> > @@ -666,6 +666,17 @@ config KEYBOARD_TC3589X
> > To compile this driver as a module, choose M here: the
> > module will be called tc3589x-keypad.
> >
> > +config KEYBOARD_TM2_TOUCHKEY
> > + tristate "tm2-touchkey support"
> > + depends on I2C
There should be LED dependency as well.
> > + help
> > + Say Y here to enable the tm2-touchkey.
> > + touchkey driver for tm2. This driver can enable
> > + the interrupt and make input events and control led brightness.
> > +
> > + To compile this driver as a module, choose M here.
> > + module will be called tm2-touchkey
> > +
> > config KEYBOARD_TWL4030
> > tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
> > depends on TWL4030_CORE
> > diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
> > index d9f4cfc..7d9acff 100644
> > --- a/drivers/input/keyboard/Makefile
> > +++ b/drivers/input/keyboard/Makefile
> > @@ -61,6 +61,7 @@ obj-$(CONFIG_KEYBOARD_SUN4I_LRADC) += sun4i-lradc-keys.o
> > obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
> > obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o
> > obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o
> > +obj-$(CONFIG_KEYBOARD_TM2_TOUCHKEY) += tm2-touchkey.o
> > obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
> > obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
> > obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o
> > diff --git a/drivers/input/keyboard/tm2-touchkey.c b/drivers/input/keyboard/tm2-touchkey.c
> > new file mode 100644
> > index 0000000..d9575d8
> > --- /dev/null
> > +++ b/drivers/input/keyboard/tm2-touchkey.c
> > @@ -0,0 +1,326 @@
> > +/*
> > + * Driver for keys on GPIO lines capable of generating interrupts.
This does not match what the driver does.
> > + *
> > + * Copyright 2005 Phil Blundell
> > + * Copyright 2016 Samsung Electronics Co., Ltd.
> > + *
> > + * Author: Beomho Seo <beomho.seo@samsung.com>
> > + * Author: Jaechul Lee <jcsing.lee@samsung.com>
> > + *
> > + * 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/bitops.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/i2c.h>
> > +#include <linux/input.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/irq.h>
> > +#include <linux/leds.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/pm.h>
> > +#include <linux/regulator/consumer.h>
> > +#include <linux/workqueue.h>
> > +
> > +#define TM2_TOUCHKEY_DEV_NAME "tm2-touchkey"
> > +#define TM2_TOUCHKEY_KEYCODE_REG 0x03
> > +#define TM2_TOUCHKEY_BASE_REG 0x00
> > +#define TM2_TOUCHKEY_CMD_LED_ON 0x10
> > +#define TM2_TOUCHKEY_CMD_LED_OFF 0x20
> > +#define TM2_TOUCHKEY_BIT_PRESS_EV BIT(3)
> > +#define TM2_TOUCHKEY_BIT_KEYCODE GENMASK(2, 0)
> > +#define TM2_TOUCHKEY_LED_VOLTAGE_MIN 2500000
> > +#define TM2_TOUCHKEY_LED_VOLTAGE_MAX 3300000
> > +
> > +enum {
> > + TM2_TOUCHKEY_KEY_MENU = 0x1,
> > + TM2_TOUCHKEY_KEY_BACK,
> > +};
> > +
> > +#define tm2_touchkey_power_enable(x) __tm2_touchkey_power_onoff(x, 1)
> > +#define tm2_touchkey_power_disable(x) __tm2_touchkey_power_onoff(x, 0)
> > +
> > +struct tm2_touchkey_data {
> > + struct i2c_client *client;
> > + struct input_dev *input_dev;
> > + struct led_classdev led_dev;
> > +
> > + u8 keycode_type;
> > + u8 pressed;
> > + struct work_struct irq_work;
> > +
> > + bool power_onoff;
> > + struct regulator *regulator_vcc; /* 1.8V */
> > + struct regulator *regulator_vdd; /* 3.3V */
> > +};
> > +
> > +static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev,
> > + enum led_brightness brightness)
> > +{
> > + struct tm2_touchkey_data *samsung_touchkey =
Just call it "touchkey", "samsung_touchkey" is too long for a local
variable.
> > + container_of(led_dev, struct tm2_touchkey_data, led_dev);
> > + u32 volt;
> > + u8 data;
> > +
> > + if (brightness == LED_OFF) {
> > + volt = TM2_TOUCHKEY_LED_VOLTAGE_MIN;
> > + data = TM2_TOUCHKEY_CMD_LED_OFF;
> > + } else {
> > + volt = TM2_TOUCHKEY_LED_VOLTAGE_MAX;
> > + data = TM2_TOUCHKEY_CMD_LED_ON;
> > + }
> > +
> > + regulator_set_voltage(samsung_touchkey->regulator_vdd, volt, volt);
> > + i2c_smbus_write_byte_data(samsung_touchkey->client,
> > + TM2_TOUCHKEY_BASE_REG, data);
> > +}
> > +
> > +static int __tm2_touchkey_power_onoff(struct tm2_touchkey_data
> > + *samsung_touchkey, bool onoff)
> > +{
> > + int ret = 0;
> > +
> > + if (samsung_touchkey->power_onoff == onoff)
> > + return ret;
> > +
> > + if (onoff) {
> > + ret = regulator_enable(samsung_touchkey->regulator_vcc);
> > + if (ret)
> > + return ret;
> > +
> > + ret = regulator_enable(samsung_touchkey->regulator_vdd);
> > + if (ret) {
> > + regulator_disable(samsung_touchkey->regulator_vcc);
> > + return ret;
> > + }
>
> I would add a comment about the sleep here.
Also "bulk" regulator API can be useful here.
>
> > + msleep(150);
> > + } else {
> > + int err;
> > +
> > + err = regulator_disable(samsung_touchkey->regulator_vcc);
> > + if (err)
> > + ret = err;
> > +
> > + err = regulator_disable(samsung_touchkey->regulator_vdd);
> > + if (err && !ret)
> > + ret = err;
> > + }
> > + samsung_touchkey->power_onoff = onoff;
> > +
> > + return ret;
> > +}
> > +
> > +static void tm2_touchkey_irq_work(struct work_struct *irq_work)
> > +{
> > + struct tm2_touchkey_data *samsung_touchkey =
> > + container_of(irq_work, struct tm2_touchkey_data, irq_work);
> > +
> > + if (!samsung_touchkey->pressed) {
> > + input_report_key(samsung_touchkey->input_dev, KEY_PHONE, 0);
> > + input_report_key(samsung_touchkey->input_dev, KEY_BACK, 0);
> > + } else {
> > + if (samsung_touchkey->keycode_type == TM2_TOUCHKEY_KEY_MENU)
> > + input_report_key(samsung_touchkey->input_dev,
> > + KEY_PHONE, 1);
> > + else
> > + input_report_key(samsung_touchkey->input_dev,
> > + KEY_BACK, 1);
> > + }
> > + input_sync(samsung_touchkey->input_dev);
> > +}
There is absolutely no reason for doing this in a work. Just call
input_report_key/input_sync from your threaded IRQ routine.
> > +
> > +static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid)
> > +{
> > + struct tm2_touchkey_data *samsung_touchkey = devid;
> > + u32 data;
> > +
> > + data = i2c_smbus_read_byte_data(samsung_touchkey->client,
> > + TM2_TOUCHKEY_KEYCODE_REG);
> > +
> > + if (data < 0) {
> > + dev_err(&samsung_touchkey->client->dev, "Failed to read i2c data\n");
> > + return IRQ_HANDLED;
> > + }
> > +
> > + samsung_touchkey->keycode_type = data & TM2_TOUCHKEY_BIT_KEYCODE;
> > + samsung_touchkey->pressed = !(data & TM2_TOUCHKEY_BIT_PRESS_EV);
> > +
> > + if (samsung_touchkey->keycode_type != TM2_TOUCHKEY_KEY_MENU &&
> > + samsung_touchkey->keycode_type != TM2_TOUCHKEY_KEY_BACK)
>
> Shouldn't at least a debug message be printed here so the user can
> know that an error occurred and a correct keycode was not received?
>
> > + return IRQ_HANDLED;
> > +
> > + schedule_work(&samsung_touchkey->irq_work);
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static int tm2_touchkey_probe(struct i2c_client *client,
> > + const struct i2c_device_id *id)
> > +{
> > + struct tm2_touchkey_data *samsung_touchkey;
> > + int ret;
> > +
> > + ret = i2c_check_functionality(client->adapter,
> > + I2C_FUNC_SMBUS_BYTE |
> > + I2C_FUNC_SMBUS_BYTE_DATA);
> > + if (!ret) {
> > + dev_err(&client->dev, "No I2C functionality found\n");
> > + return -ENODEV;
> > + }
> > +
> > + samsung_touchkey = devm_kzalloc(&client->dev,
> > + sizeof(struct tm2_touchkey_data), GFP_KERNEL);
sizeof(*touchkey)
> > +
> > + if (!samsung_touchkey) {
> > + dev_err(&client->dev, "Failed to allocate memory.\n");
> > + return -ENOMEM;
> > + }
> > +
> > + samsung_touchkey->client = client;
> > + i2c_set_clientdata(client, samsung_touchkey);
> > + INIT_WORK(&samsung_touchkey->irq_work, tm2_touchkey_irq_work);
Work is not needed.
> > +
> > + /* regulator */
> > + samsung_touchkey->regulator_vcc =
> > + devm_regulator_get(&client->dev, "vcc");
> > + if (IS_ERR(samsung_touchkey->regulator_vcc)) {
> > + dev_err(&client->dev, "Failed to get vcc regulator\n");
> > + return PTR_ERR(samsung_touchkey->regulator_vcc);
> > + }
> > +
> > + samsung_touchkey->regulator_vdd =
> > + devm_regulator_get(&client->dev, "vdd");
> > + if (IS_ERR(samsung_touchkey->regulator_vdd)) {
> > + dev_err(&client->dev, "Failed to get vdd regulator\n");
> > + return PTR_ERR(samsung_touchkey->regulator_vcc);
> > + }
devm_regulator_bulk_get
> > +
> > + /* power */
> > + ret = tm2_touchkey_power_enable(samsung_touchkey);
> > + if (ret) {
> > + dev_err(&client->dev, "Failed to enable power\n");
> > + return ret;
> > + }
You need to install devm action (devm_add_action_or_reset) to disable
power when unloading driver (or if initialization fails).
> > +
> > + /* irq */
> > + ret = devm_request_threaded_irq(&client->dev,
> > + client->irq, NULL,
> > + tm2_touchkey_irq_handler,
> > + IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
I'd rather we rely on IRQ trigger from DT, so just use IRQF_PNESHOT
here.
I'd rather you allocated input device before requesting IRQ. The
registering input device is fine to do after getting IRQ.
> > + TM2_TOUCHKEY_DEV_NAME,
> > + samsung_touchkey);
> > + if (ret) {
> > + dev_err(&client->dev, "Failed to request threaded irq\n");
> > + return ret;
> > + }
> > +
> > + /* input device */
> > + samsung_touchkey->input_dev = devm_input_allocate_device(&client->dev);
> > + if (!samsung_touchkey->input_dev) {
> > + dev_err(&client->dev, "Failed to alloc input device.\n");
> > + return -ENOMEM;
> > + }
> > + samsung_touchkey->input_dev->name = TM2_TOUCHKEY_DEV_NAME;
> > + samsung_touchkey->input_dev->id.bustype = BUS_I2C;
> > + samsung_touchkey->input_dev->dev.parent = &client->dev;
No need to set parent when using devm_input_allocate_device(), it is
done automatically.
> > +
> > + set_bit(EV_KEY, samsung_touchkey->input_dev->evbit);
> > + set_bit(KEY_PHONE, samsung_touchkey->input_dev->keybit);
> > + set_bit(KEY_BACK, samsung_touchkey->input_dev->keybit);
Just do
input_set_capability(touchkey->input_dev, EV_KEY, KEY_PHONE);
input_set_capability(touchkey->input_dev, EV_KEY, KEY_BACK);
> > + input_set_drvdata(samsung_touchkey->input_dev, samsung_touchkey);
> > +
> > + ret = input_register_device(samsung_touchkey->input_dev);
> > + if (ret) {
> > + dev_err(&client->dev, "Failed to register input device.\n");
> > + return ret;
> > + }
> > +
> > + /* led device */
> > + samsung_touchkey->led_dev.name = TM2_TOUCHKEY_DEV_NAME;
> > + samsung_touchkey->led_dev.brightness = LED_FULL;
> > + samsung_touchkey->led_dev.max_brightness = LED_FULL;
> > + samsung_touchkey->led_dev.brightness_set =
> > + tm2_touchkey_led_brightness_set;
> > +
> > + ret = devm_led_classdev_register(&client->dev,
> > + &samsung_touchkey->led_dev);
> > + if (ret < 0) {
> > + dev_err(&client->dev, "Failed to register touchkey led\n");
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void tm2_touchkey_shutdown(struct i2c_client *client)
> > +{
This is something not normally done. Does your platform really keep
rails on when AP is off?
> > + struct tm2_touchkey_data *samsung_touchkey =
> > + i2c_get_clientdata(client);
> > + int ret;
> > +
> > + disable_irq(client->irq);
> > + ret = tm2_touchkey_power_disable(samsung_touchkey);
> > + if (ret)
> > + dev_err(&client->dev, "Failed to disable power\n");
> > +}
> > +
> > +static int tm2_touchkey_suspend(struct device *dev)
__maybe_unused
> > +{
> > + struct tm2_touchkey_data *samsung_touchkey = dev_get_drvdata(dev);
> > + int ret;
> > +
> > + disable_irq(samsung_touchkey->client->irq);
> > + ret = tm2_touchkey_power_disable(samsung_touchkey);
> > + if (ret)
> > + dev_err(dev, "Failed to disable power\n");
> > +
> > + return ret;
> > +}
>
> These two functions are basically the same, can you factor it out?
>
> > +
> > +static int tm2_touchkey_resume(struct device *dev)
__maybe_unused
> > +{
> > + struct tm2_touchkey_data *samsung_touchkey = dev_get_drvdata(dev);
> > + int ret;
> > +
> > + enable_irq(samsung_touchkey->client->irq);
> > + ret = tm2_touchkey_power_enable(samsung_touchkey);
> > + if (ret)
> > + dev_err(dev, "Failed to enable power\n");
> > +
> > + return ret;
> > +}
> > +
> > +static SIMPLE_DEV_PM_OPS(tm2_touchkey_pm_ops, tm2_touchkey_suspend,
> > + tm2_touchkey_resume);
> > +
> > +static const struct i2c_device_id tm2_touchkey_id_table[] = {
> > + {TM2_TOUCHKEY_DEV_NAME, 0},
> > + {},
> > +};
> > +
>
> You need a MODULE_DEVICE_TABLE(i2c, tm2_touchkey_id_table) here so the
> module can be autoloaded when the device is registered.
>
> > +static const struct of_device_id tm2_touchkey_of_match[] = {
> > + {.compatible = "samsung,tm2-touchkey",},
> > + {},
> > +};
> > +
>
> Here a MODULE_DEVICE_TABLE(of, tm2_touchkey_of_match) is not strictly
> needed since the I2C core always reports MODALIAS of the form i2c:<dev>
> but still is good to have so the I2C core can be fixed at some point.
Yes, please add MODULE_DEVICE_TABLE(of, ...).
>
> > +static struct i2c_driver tm2_touchkey_driver = {
> > + .driver = {
> > + .name = TM2_TOUCHKEY_DEV_NAME,
> > + .pm = &tm2_touchkey_pm_ops,
> > + .of_match_table = of_match_ptr(tm2_touchkey_of_match),
> > + },
> > + .probe = tm2_touchkey_probe,
> > + .shutdown = tm2_touchkey_shutdown,
> > + .id_table = tm2_touchkey_id_table,
> > +};
> > +
> > +module_i2c_driver(tm2_touchkey_driver);
> > +
> > +MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>");
> > +MODULE_AUTHOR("Jaechul Lee <jcsing.lee@samsung.com>");
> > +MODULE_DESCRIPTION("Samsung touchkey driver");
> > +MODULE_LICENSE("GPL v2");
> >
Thanks.
--
Dmitry
next prev parent reply other threads:[~2017-01-03 18:56 UTC|newest]
Thread overview: 61+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <CGME20170103075733epcas5p48985c789a816f1f2eae84c52e070cc14@epcas5p4.samsung.com>
2017-01-03 7:57 ` [PATCH 0/4] Add touch key driver support for TM2 Jaechul Lee
2017-01-03 7:57 ` Jaechul Lee
2017-01-03 7:57 ` Jaechul Lee
2017-01-03 7:57 ` [PATCH 1/4] input: Add support for the tm2 touchkey device driver Jaechul Lee
2017-01-03 7:57 ` Jaechul Lee
2017-01-03 12:26 ` Javier Martinez Canillas
2017-01-03 12:26 ` Javier Martinez Canillas
2017-01-04 14:15 ` Rob Herring
2017-01-04 14:15 ` Rob Herring
2017-01-03 7:57 ` [PATCH 2/4] input: tm2-touchkey: Add touchkey driver support for TM2 Jaechul Lee
2017-01-03 7:57 ` Jaechul Lee
2017-01-03 13:11 ` Javier Martinez Canillas
2017-01-03 13:11 ` Javier Martinez Canillas
2017-01-03 13:11 ` Javier Martinez Canillas
2017-01-03 18:56 ` Dmitry Torokhov [this message]
2017-01-03 18:56 ` Dmitry Torokhov
2017-01-03 16:59 ` Krzysztof Kozlowski
2017-01-03 16:59 ` Krzysztof Kozlowski
2017-01-03 16:59 ` Krzysztof Kozlowski
2017-01-03 7:57 ` [PATCH 3/4] arm64: dts: exynos: make tm2 and tm2e independent from each other Jaechul Lee
2017-01-03 7:57 ` Jaechul Lee
2017-01-03 8:16 ` Andi Shyti
2017-01-03 8:16 ` Andi Shyti
2017-01-03 8:27 ` Krzysztof Kozlowski
2017-01-03 8:27 ` Krzysztof Kozlowski
2017-01-03 9:58 ` Andi Shyti
2017-01-03 9:58 ` Andi Shyti
[not found] ` <20170103095842.h3hl64amje4qv4ts-8vUhnHFVuGn35fTxX1Dczw@public.gmane.org>
2017-01-03 10:01 ` Krzysztof Kozlowski
2017-01-03 10:01 ` Krzysztof Kozlowski
2017-01-03 10:01 ` Krzysztof Kozlowski
2017-01-03 10:25 ` Andi Shyti
2017-01-03 10:25 ` Andi Shyti
2017-01-03 11:47 ` Chanwoo Choi
2017-01-03 11:47 ` Chanwoo Choi
[not found] ` <CAGTfZH3ez4DPTE7-bDeayjhSGxJ_8Mj1MQz1M7O1qhwOGOnmMQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2017-01-03 11:55 ` Andi Shyti
2017-01-03 11:55 ` Andi Shyti
2017-01-03 11:55 ` Andi Shyti
[not found] ` <20170103115530.zhjwn7bzmqmumy23-8vUhnHFVuGn35fTxX1Dczw@public.gmane.org>
2017-01-03 12:15 ` Javier Martinez Canillas
2017-01-03 12:15 ` Javier Martinez Canillas
2017-01-03 12:15 ` Javier Martinez Canillas
2017-01-03 14:40 ` Andi Shyti
2017-01-03 14:40 ` Andi Shyti
2017-01-03 14:57 ` Javier Martinez Canillas
2017-01-03 14:57 ` Javier Martinez Canillas
2017-01-03 14:57 ` Javier Martinez Canillas
2017-01-03 15:29 ` Chanwoo Choi
2017-01-03 15:29 ` Chanwoo Choi
2017-01-03 15:29 ` Chanwoo Choi
2017-01-03 16:31 ` Krzysztof Kozlowski
2017-01-03 16:31 ` Krzysztof Kozlowski
2017-01-03 16:31 ` Krzysztof Kozlowski
2017-01-03 16:41 ` Krzysztof Kozlowski
2017-01-03 16:41 ` Krzysztof Kozlowski
2017-01-03 16:41 ` Krzysztof Kozlowski
2017-01-03 7:57 ` [PATCH 4/4] arm64: dts: exynos: Add tm2 touchkey node Jaechul Lee
2017-01-03 7:57 ` Jaechul Lee
2017-01-03 13:14 ` Javier Martinez Canillas
2017-01-03 13:14 ` Javier Martinez Canillas
2017-01-03 16:49 ` Krzysztof Kozlowski
2017-01-03 16:49 ` Krzysztof Kozlowski
2017-01-03 16:49 ` Krzysztof Kozlowski
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=20170103185625.GC16032@dtor-ws \
--to=dmitry.torokhov@gmail.com \
--cc=andi.shyti@samsung.com \
--cc=beomho.seo@samsung.com \
--cc=catalin.marinas@arm.com \
--cc=cw00.choi@samsung.com \
--cc=devicetree@vger.kernel.org \
--cc=galaxyra@gmail.com \
--cc=javier@osg.samsung.com \
--cc=jcsing.lee@samsung.com \
--cc=kgene@kernel.org \
--cc=krzk@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-samsung-soc@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=robh+dt@kernel.org \
--cc=will.deacon@arm.com \
/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: link
Be 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.