Inter-revision diff: patch 2

Comparing v1 (message) to v6 (message)

--- v1
+++ v6
@@ -1,21 +1,24 @@
 This patch adds support for the TM2 touch key and led
-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: Beomho Seo <beomho.seo@samsung.com>
 Signed-off-by: Jaechul Lee <jcsing.lee@samsung.com>
-Signed-off-by: Beomho Seo <beomho.seo@samsung.com>
+Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com>
+Reviewed-by: Andi Shyti <andi.shyti@samsung.com>
+Acked-by: Krzysztof Kozlowski <krzk@kernel.org>
 ---
  drivers/input/keyboard/Kconfig        |  11 ++
  drivers/input/keyboard/Makefile       |   1 +
- drivers/input/keyboard/tm2-touchkey.c | 326 ++++++++++++++++++++++++++++++++++
- 3 files changed, 338 insertions(+)
+ drivers/input/keyboard/tm2-touchkey.c | 287 ++++++++++++++++++++++++++++++++++
+ 3 files changed, 299 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
+index cbd75cf..97acd65 100644
 --- a/drivers/input/keyboard/Kconfig
 +++ b/drivers/input/keyboard/Kconfig
 @@ -666,6 +666,17 @@ config KEYBOARD_TC3589X
@@ -23,15 +26,15 @@
  	  module will be called tc3589x-keypad.
  
 +config KEYBOARD_TM2_TOUCHKEY
-+	tristate "tm2-touchkey support"
++	tristate "TM2 touchkey support"
 +	depends on I2C
++	depends on LEDS_CLASS
 +	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.
++	  Say Y here to enable device driver for tm2-touchkey with
++	  LED control for the Exynos5433 TM2 board.
 +
 +	  To compile this driver as a module, choose M here.
-+	  module will be called tm2-touchkey
++	  module will be called tm2-touchkey.
 +
  config KEYBOARD_TWL4030
  	tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
@@ -50,12 +53,12 @@
  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
+index 0000000..e927d06
 --- /dev/null
 +++ b/drivers/input/keyboard/tm2-touchkey.c
-@@ -0,0 +1,326 @@
+@@ -0,0 +1,287 @@
 +/*
-+ * Driver for keys on GPIO lines capable of generating interrupts.
++ * TM2 touchkey device driver
 + *
 + * Copyright 2005 Phil Blundell
 + * Copyright 2016 Samsung Electronics Co., Ltd.
@@ -80,44 +83,41 @@
 +#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
++
++#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)
++enum {
++	TM2_TOUCHKEY_SUPPLIES_VCC,
++	TM2_TOUCHKEY_SUPPLIES_VDD,
++};
 +
 +struct tm2_touchkey_data {
 +	struct i2c_client *client;
 +	struct input_dev *input_dev;
 +	struct led_classdev led_dev;
++	struct regulator_bulk_data regulators[2];
 +
 +	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 =
++	struct tm2_touchkey_data *touchkey =
 +	    container_of(led_dev, struct tm2_touchkey_data, led_dev);
 +	u32 volt;
 +	u8 data;
@@ -130,94 +130,79 @@
 +		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)
++	regulator_set_voltage(
++		touchkey->regulators[TM2_TOUCHKEY_SUPPLIES_VDD].consumer,
++		volt, volt);
++	i2c_smbus_write_byte_data(touchkey->client,
++						TM2_TOUCHKEY_BASE_REG, data);
++}
++
++static int tm2_touchkey_power_enable(struct tm2_touchkey_data *touchkey)
 +{
 +	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;
-+		}
-+		msleep(150);
++	ret = regulator_bulk_enable(ARRAY_SIZE(touchkey->regulators),
++						touchkey->regulators);
++	if (ret)
++		return ret;
++
++	/* waiting for device initialization, at least 150ms */
++	msleep(150);
++
++	return 0;
++}
++
++static void tm2_touchkey_power_disable(void *data)
++{
++	struct tm2_touchkey_data *touchkey = data;
++
++	regulator_bulk_disable(ARRAY_SIZE(touchkey->regulators),
++						touchkey->regulators);
++}
++
++static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid)
++{
++	struct tm2_touchkey_data *touchkey = devid;
++	u32 data;
++
++	data = i2c_smbus_read_byte_data(touchkey->client,
++					TM2_TOUCHKEY_KEYCODE_REG);
++
++	if (data < 0) {
++		dev_err(&touchkey->client->dev, "Failed to read i2c data\n");
++		return IRQ_HANDLED;
++	}
++
++	touchkey->keycode_type = data & TM2_TOUCHKEY_BIT_KEYCODE;
++	touchkey->pressed = !(data & TM2_TOUCHKEY_BIT_PRESS_EV);
++
++	if (touchkey->keycode_type != TM2_TOUCHKEY_KEY_MENU &&
++	    touchkey->keycode_type != TM2_TOUCHKEY_KEY_BACK) {
++		dev_warn(&touchkey->client->dev, "Skip unhandled keycode(%d)\n",
++							touchkey->keycode_type);
++		return IRQ_HANDLED;
++	}
++
++	if (!touchkey->pressed) {
++		input_report_key(touchkey->input_dev, KEY_PHONE, 0);
++		input_report_key(touchkey->input_dev, KEY_BACK, 0);
 +	} 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,
++		if (touchkey->keycode_type == TM2_TOUCHKEY_KEY_MENU)
++			input_report_key(touchkey->input_dev,
 +					 KEY_PHONE, 1);
 +		else
-+			input_report_key(samsung_touchkey->input_dev,
++			input_report_key(touchkey->input_dev,
 +					 KEY_BACK, 1);
 +	}
-+	input_sync(samsung_touchkey->input_dev);
-+}
-+
-+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)
-+		return IRQ_HANDLED;
-+
-+	schedule_work(&samsung_touchkey->irq_work);
++	input_sync(touchkey->input_dev);
 +
 +	return IRQ_HANDLED;
 +}
 +
 +static int tm2_touchkey_probe(struct i2c_client *client,
-+				  const struct i2c_device_id *id)
-+{
-+	struct tm2_touchkey_data *samsung_touchkey;
++					const struct i2c_device_id *id)
++{
++	struct tm2_touchkey_data *touchkey;
 +	int ret;
 +
 +	ret = i2c_check_functionality(client->adapter,
@@ -228,37 +213,54 @@
 +		return -ENODEV;
 +	}
 +
-+	samsung_touchkey = devm_kzalloc(&client->dev,
-+			sizeof(struct tm2_touchkey_data), GFP_KERNEL);
-+
-+	if (!samsung_touchkey) {
-+		dev_err(&client->dev, "Failed to allocate memory.\n");
++	touchkey = devm_kzalloc(&client->dev, sizeof(*touchkey), GFP_KERNEL);
++	if (!touchkey)
 +		return -ENOMEM;
-+	}
-+
-+	samsung_touchkey->client = client;
-+	i2c_set_clientdata(client, samsung_touchkey);
-+	INIT_WORK(&samsung_touchkey->irq_work, tm2_touchkey_irq_work);
-+
-+	/* 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);
++
++	touchkey->client = client;
++	i2c_set_clientdata(client, touchkey);
++
++	/* regulators */
++	touchkey->regulators[TM2_TOUCHKEY_SUPPLIES_VCC].supply = "vcc";
++	touchkey->regulators[TM2_TOUCHKEY_SUPPLIES_VDD].supply = "vdd";
++	ret = devm_regulator_bulk_get(&client->dev,
++					ARRAY_SIZE(touchkey->regulators),
++					touchkey->regulators);
++	if (ret) {
++		dev_err(&client->dev, "Failed to get regulators\n");
++		return ret;
 +	}
 +
 +	/* power */
-+	ret = tm2_touchkey_power_enable(samsung_touchkey);
++	ret = tm2_touchkey_power_enable(touchkey);
 +	if (ret) {
 +		dev_err(&client->dev, "Failed to enable power\n");
++		return ret;
++	}
++
++	ret = devm_add_action_or_reset(&client->dev,
++					tm2_touchkey_power_disable, touchkey);
++	if (ret)
++		return ret;
++
++	/* input device */
++	touchkey->input_dev = devm_input_allocate_device(&client->dev);
++	if (!touchkey->input_dev) {
++		dev_err(&client->dev, "Failed to alloc input device\n");
++		return -ENOMEM;
++	}
++	touchkey->input_dev->name = TM2_TOUCHKEY_DEV_NAME;
++	touchkey->input_dev->id.bustype = BUS_I2C;
++
++	set_bit(EV_KEY, touchkey->input_dev->evbit);
++	input_set_capability(touchkey->input_dev, EV_KEY, KEY_PHONE);
++	input_set_capability(touchkey->input_dev, EV_KEY, KEY_BACK);
++
++	input_set_drvdata(touchkey->input_dev, touchkey);
++
++	ret = input_register_device(touchkey->input_dev);
++	if (ret) {
++		dev_err(&client->dev, "Failed to register input device\n");
 +		return ret;
 +	}
 +
@@ -266,44 +268,20 @@
 +	ret = devm_request_threaded_irq(&client->dev,
 +					client->irq, NULL,
 +					tm2_touchkey_irq_handler,
-+					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-+					TM2_TOUCHKEY_DEV_NAME,
-+					samsung_touchkey);
++					IRQF_ONESHOT, TM2_TOUCHKEY_DEV_NAME,
++					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;
-+
-+	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);
-+	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);
++	touchkey->led_dev.name = TM2_TOUCHKEY_DEV_NAME;
++	touchkey->led_dev.brightness = LED_FULL;
++	touchkey->led_dev.max_brightness = LED_FULL;
++	touchkey->led_dev.brightness_set = tm2_touchkey_led_brightness_set;
++
++	ret = devm_led_classdev_register(&client->dev, &touchkey->led_dev);
 +	if (ret < 0) {
 +		dev_err(&client->dev, "Failed to register touchkey led\n");
 +		return ret;
@@ -312,38 +290,23 @@
 +	return 0;
 +}
 +
-+static void tm2_touchkey_shutdown(struct i2c_client *client)
-+{
-+	struct tm2_touchkey_data *samsung_touchkey =
-+						i2c_get_clientdata(client);
++static int __maybe_unused tm2_touchkey_suspend(struct device *dev)
++{
++	struct tm2_touchkey_data *touchkey = dev_get_drvdata(dev);
++
++	disable_irq(touchkey->client->irq);
++	tm2_touchkey_power_disable(touchkey);
++
++	return 0;
++}
++
++static int __maybe_unused tm2_touchkey_resume(struct device *dev)
++{
++	struct tm2_touchkey_data *touchkey = dev_get_drvdata(dev);
 +	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)
-+{
-+	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;
-+}
-+
-+static int tm2_touchkey_resume(struct device *dev)
-+{
-+	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);
++	enable_irq(touchkey->client->irq);
++	ret = tm2_touchkey_power_enable(touchkey);
 +	if (ret)
 +		dev_err(dev, "Failed to enable power\n");
 +
@@ -357,11 +320,13 @@
 +	{TM2_TOUCHKEY_DEV_NAME, 0},
 +	{},
 +};
++MODULE_DEVICE_TABLE(i2c, tm2_touchkey_id_table);
 +
 +static const struct of_device_id tm2_touchkey_of_match[] = {
-+	{.compatible = "samsung,tm2-touchkey",},
++	{.compatible = "cypress,tm2-touchkey",},
 +	{},
 +};
++MODULE_DEVICE_TABLE(of, tm2_touchkey_of_match);
 +
 +static struct i2c_driver tm2_touchkey_driver = {
 +	.driver = {
@@ -370,7 +335,6 @@
 +		.of_match_table = of_match_ptr(tm2_touchkey_of_match),
 +	},
 +	.probe = tm2_touchkey_probe,
-+	.shutdown = tm2_touchkey_shutdown,
 +	.id_table = tm2_touchkey_id_table,
 +};
 +
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help