lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1345241877-16200-15-git-send-email-cheiny@synaptics.com>
Date:	Fri, 17 Aug 2012 15:17:54 -0700
From:	Christopher Heiny <cheiny@...aptics.com>
To:	Dmitry Torokhov <dmitry.torokhov@...il.com>
Cc:	Jean Delvare <khali@...ux-fr.org>,
	Linux Kernel <linux-kernel@...r.kernel.org>,
	Linux Input <linux-input@...r.kernel.org>,
	Christopher Heiny <cheiny@...aptics.com>,
	Allie Xiong <axiong@...aptics.com>,
	William Manson <wmanson@...aptics.com>,
	Peichen Chang <peichen.chang@...aptics.com>,
	Joerie de Gram <j.de.gram@...il.com>,
	Wolfram Sang <w.sang@...gutronix.de>,
	Mathieu Poirier <mathieu.poirier@...aro.org>,
	Linus Walleij <linus.walleij@...ricsson.com>,
	Naveen Kumar Gaddipati <naveen.gaddipati@...ricsson.com>
Subject: [RFC PATCH 14/17] input: RMI4 F30 GPIO/LED control

Signed-off-by: Christopher Heiny <cheiny@...aptics.com>

Cc: Dmitry Torokhov <dmitry.torokhov@...il.com>
Cc: Linus Walleij <linus.walleij@...ricsson.com>
Cc: Naveen Kumar Gaddipati <naveen.gaddipati@...ricsson.com>
Cc: Joeri de Gram <j.de.gram@...il.com>

Acked-by: Jean Delvare <khali@...ux-fr.org>

---

 drivers/input/rmi4/rmi_f30.c | 1247 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1247 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c
new file mode 100644
index 0000000..4bc2932
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f30.c
@@ -0,0 +1,1247 @@
+/*
+ * Copyright (c) 2012 Synaptics Incorporated
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#define FUNCTION_DATA rmi_fn_30_data
+#define FNUM 30
+
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include "rmi_driver.h"
+
+
+#define MAX_LEN 256
+
+/* data specific to fn $30 that needs to be kept around */
+union f30_query {
+	struct {
+		u8 extended_patterns:1;
+		u8 has_mappable_buttons:1;
+		u8 has_led:1;
+		u8 has_gpio:1;
+		u8 has_haptic:1;
+		u8 has_gpio_driver_control:1;
+		u8 gpio_led_count:5;
+	};
+	struct {
+		u8 regs[2];
+		u16 address;
+	};
+};
+
+struct f30_gpio_ctrl_0n {
+	u8 led_sel;
+};
+
+struct f30_gpio_ctrl_0 {
+	struct f30_gpio_ctrl_0n *regs;
+	u16 address;
+	u8 length;
+};
+
+union f30_gpio_ctrl_1 {
+	struct {
+		u8 gpio_debounce:1;
+		u8 reserved:3;
+		u8 halt:1;
+		u8 halted:1;
+		u8 reserved2:2;
+	};
+	struct {
+		u8 regs[1];
+		u16 address;
+	};
+};
+
+struct f30_gpio_ctrl_2n {
+	u8 dir;
+};
+
+struct f30_gpio_ctrl_2 {
+	struct f30_gpio_ctrl_2n *regs;
+	u16 address;
+	u8 length;
+};
+
+struct f30_gpio_ctrl_3n {
+	u8 gpiodata;
+};
+
+struct f30_gpio_ctrl_3 {
+	struct f30_gpio_ctrl_3n *regs;
+	u16 address;
+	u8 length;
+};
+
+struct f30_gpio_ctrl_4n {
+	u8 led_act;
+};
+
+struct f30_gpio_ctrl_4 {
+	struct f30_gpio_ctrl_4n *regs;
+	u16 address;
+	u8 length;
+};
+
+struct f30_gpio_ctrl_5n {
+	u8 ramp_period_a;
+	u8 ramp_period_b;
+};
+
+struct f30_gpio_ctrl_5 {
+	struct f30_gpio_ctrl_5n *regs;
+	u16 address;
+	u8 length;
+};
+
+union f30_gpio_ctrl_6n {
+	struct {
+		u8 reserved:1;
+		u8 SPCTRL:3;
+		u8 STRPD:1;
+		u8 reserved2:1;
+		u8 STRPU:1;
+		u8 reserved3:1;
+	};
+	struct {
+		u8 brightness:4;
+		u8 pattern:4;
+	};
+};
+
+struct f30_gpio_ctrl_6 {
+	union f30_gpio_ctrl_6n *regs;
+	u16 address;
+	u8 length;
+};
+
+struct f30_gpio_ctrl_7n {
+	u8 capacity_btn_nbr:5;
+	u8 valid:1;
+	u8 invert:1;
+	u8 open_drain:1;
+};
+
+struct f30_gpio_ctrl_7 {
+	struct f30_gpio_ctrl_7n *regs;
+	u16 address;
+	u8 length;
+};
+
+struct f30_gpio_ctrl_8n {
+	u8 gpio_ctrl8_0;
+	u8 gpio_ctrl8_1;
+};
+
+struct f30_gpio_ctrl_8 {
+	struct f30_gpio_ctrl_8n *regs;
+	u16 address;
+	u8 length;
+};
+
+union f30_gpio_ctrl_9 {
+	struct {
+		u8 haptic_duration;
+	};
+	struct {
+		u8 regs[1];
+		u16 address;
+	};
+};
+
+struct f30_control {
+	struct f30_gpio_ctrl_0 *reg_0;
+	union f30_gpio_ctrl_1 *reg_1;
+	struct f30_gpio_ctrl_2 *reg_2;
+	struct f30_gpio_ctrl_3 *reg_3;
+	struct f30_gpio_ctrl_4 *reg_4;
+	struct f30_gpio_ctrl_5 *reg_5;
+	struct f30_gpio_ctrl_6 *reg_6;
+	struct f30_gpio_ctrl_7 *reg_7;
+	struct f30_gpio_ctrl_8 *reg_8;
+	union f30_gpio_ctrl_9 *reg_9;
+};
+
+struct f30_data_0n {
+	u8 gpi_led_data:1;
+};
+
+struct f30_data_0 {
+	struct f30_data_0n *regs;
+	u16 address;
+	u8 length;
+};
+
+struct f30_data {
+	struct f30_data_0 *datareg_0;
+	u16 address;
+	u8 length;
+};
+
+struct rmi_fn_30_data {
+	union f30_query query;
+	struct f30_data data;
+	struct f30_control control;
+	unsigned char gpioled_count;
+	unsigned char gpioled_bitmask_size;
+	unsigned char gpioled_byte_size;
+	unsigned char *button_data_buffer;
+	unsigned char button_bitmask_size;
+	unsigned short *gpioled_map;
+	char input_name[MAX_LEN];
+	char input_phys[MAX_LEN];
+	struct input_dev *input;
+	struct mutex control_mutex;
+	struct mutex data_mutex;
+};
+
+static int rmi_f30_alloc_memory(struct rmi_function_container *fc);
+
+static void rmi_f30_free_memory(struct rmi_function_container *fc);
+
+static int rmi_f30_initialize(struct rmi_function_container *fc);
+
+static int rmi_f30_register_device(struct rmi_function_container *fc);
+
+static int rmi_f30_config(struct rmi_function_container *fc);
+
+static int rmi_f30_create_sysfs(struct rmi_function_container *fc);
+
+
+/* Query sysfs files */
+
+show_union_struct_prototype(extended_patterns)
+show_union_struct_prototype(has_mappable_buttons)
+show_union_struct_prototype(has_led)
+show_union_struct_prototype(has_gpio)
+show_union_struct_prototype(has_haptic)
+show_union_struct_prototype(has_gpio_driver_control)
+show_union_struct_prototype(gpio_led_count)
+
+static struct attribute *attrs1[] = {
+	attrify(extended_patterns),
+	attrify(has_mappable_buttons),
+	attrify(has_led),
+	attrify(has_gpio),
+	attrify(has_haptic),
+	attrify(has_gpio_driver_control),
+	attrify(gpio_led_count),
+	NULL
+};
+
+static struct attribute_group attrs_query = GROUP(attrs1);
+
+/* Control sysfs files */
+
+show_store_union_struct_prototype(led_sel)
+show_store_union_struct_prototype(gpio_debounce)
+show_store_union_struct_prototype(halt)
+show_store_union_struct_prototype(halted)
+show_store_union_struct_prototype(dir)
+show_store_union_struct_prototype(gpiodata)
+show_store_union_struct_prototype(led_act)
+show_store_union_struct_prototype(ramp_period_a)
+show_store_union_struct_prototype(ramp_period_b)
+show_store_union_struct_prototype(SPCTRL)
+show_store_union_struct_prototype(STRPD)
+show_store_union_struct_prototype(STRPU)
+show_store_union_struct_prototype(brightness)
+show_store_union_struct_prototype(pattern)
+
+show_store_union_struct_prototype(capacity_btn_nbr)
+show_store_union_struct_prototype(valid)
+show_store_union_struct_prototype(invert)
+show_store_union_struct_prototype(open_drain)
+show_store_union_struct_prototype(gpio_ctrl8_0)
+show_store_union_struct_prototype(gpio_ctrl8_1)
+show_store_union_struct_prototype(haptic_duration)
+
+/* Data sysfs files */
+show_store_union_struct_prototype(gpi_led_data)
+
+static struct attribute *attrs_ctrl_reg_0[] = {
+	attrify(led_sel),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_1[] = {
+	attrify(gpio_debounce),
+	attrify(halt),
+	attrify(halted),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_2[] = {
+	attrify(dir),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_3[] = {
+	attrify(gpiodata),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_4[] = {
+	attrify(led_act),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_5[] = {
+	attrify(ramp_period_a),
+	attrify(ramp_period_b),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_6_gpio[] = {
+	attrify(SPCTRL),
+	attrify(STRPD),
+	attrify(STRPU),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_6_led[] = {
+	attrify(brightness),
+	attrify(pattern),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_7[] = {
+	attrify(capacity_btn_nbr),
+	attrify(valid),
+	attrify(invert),
+	attrify(open_drain),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_8[] = {
+	attrify(gpio_ctrl8_0),
+	attrify(gpio_ctrl8_1),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_9[] = {
+	attrify(haptic_duration),
+	NULL
+};
+
+static struct attribute_group attrs_ctrl_regs[] = {
+	GROUP(attrs_ctrl_reg_0),
+	GROUP(attrs_ctrl_reg_1),
+	GROUP(attrs_ctrl_reg_2),
+	GROUP(attrs_ctrl_reg_3),
+	GROUP(attrs_ctrl_reg_4),
+	GROUP(attrs_ctrl_reg_5),
+	GROUP(attrs_ctrl_reg_6_gpio),
+	GROUP(attrs_ctrl_reg_6_led),
+	GROUP(attrs_ctrl_reg_7),
+	GROUP(attrs_ctrl_reg_8),
+	GROUP(attrs_ctrl_reg_9),
+};
+
+bool f30_attrs_regs_exist[ARRAY_SIZE(attrs_ctrl_regs)];
+
+static struct attribute *attrs_gpileddata[] = {
+	attrify(gpi_led_data),
+	NULL
+};
+
+static struct attribute_group attrs_data = GROUP(attrs_gpileddata);
+
+int rmi_f30_read_control_parameters(struct rmi_device *rmi_dev,
+	struct rmi_fn_30_data *f30)
+{
+	int retval = 0;
+	struct f30_control *control = &f30->control;
+
+	retval = rmi_read_block(rmi_dev, control->reg_0->address,
+			(u8 *)control->reg_0->regs,
+			control->reg_0->length);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg0 to 0x%x\n",
+			__func__, control->reg_0->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_1->address,
+			(u8 *)control->reg_1->regs,
+			sizeof(control->reg_1->regs));
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg1 to 0x%x\n",
+			 __func__, control->reg_1->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_2->address,
+			(u8 *)control->reg_2->regs, control->reg_2->length);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg_2 to 0x%x\n",
+			 __func__, control->reg_2->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_3->address,
+			(u8 *)control->reg_3->regs, control->reg_3->length);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg_3 to 0x%x\n",
+			 __func__, control->reg_3->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_4->address,
+			(u8 *)control->reg_4->regs, control->reg_4->length);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg4 to 0x%x\n",
+			 __func__, control->reg_4->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_5->address,
+			(u8 *)control->reg_5->regs,
+			control->reg_5->length);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg5 to 0x%x\n", __func__,
+			control->reg_5->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_6->address,
+			(u8 *)control->reg_6->regs,
+			control->reg_6->length);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg6 to 0x%x\n", __func__,
+		control->reg_6->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_7->address,
+		(u8 *)control->reg_7->regs,
+		control->reg_7->length);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg7 to 0x%x\n", __func__,
+		control->reg_7->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_8->address,
+		(u8 *)control->reg_8->regs,
+		control->reg_8->length);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg8 to 0x%x\n", __func__,
+		control->reg_8->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_9->address,
+		(u8 *)control->reg_9->regs,
+		sizeof(control->reg_9->regs));
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg9 to 0x%x\n", __func__,
+		control->reg_9->address);
+		return retval;
+	}
+	return 0;
+}
+
+static int rmi_f30_init(struct rmi_function_container *fc)
+{
+	int rc;
+	struct rmi_fn_30_data *f30 = fc->data;
+
+	rc = rmi_f30_alloc_memory(fc);
+	if (rc < 0)
+		goto error_exit;
+
+	rc = rmi_f30_initialize(fc);
+	if (rc < 0)
+		goto error_exit;
+
+	rc = rmi_f30_register_device(fc);
+	if (rc < 0)
+		goto error_exit;
+
+	rc = rmi_f30_create_sysfs(fc);
+	if (rc < 0)
+		goto error_uregister_exit;
+	return 0;
+
+error_uregister_exit:
+	input_unregister_device(f30->input);
+
+error_exit:
+	rmi_f30_free_memory(fc);
+
+	return rc;
+
+}
+
+static inline int rmi_f30_alloc_memory(struct rmi_function_container *fc)
+{
+	struct rmi_fn_30_data *f30;
+	int retval;
+
+	f30 = kzalloc(sizeof(struct rmi_fn_30_data), GFP_KERNEL);
+	if (!f30) {
+		dev_err(&fc->dev, "Failed to allocate rmi_fn_30_data.\n");
+		return -ENOMEM;
+	}
+	fc->data = f30;
+
+	retval = rmi_read_block(fc->rmi_dev,
+						fc->fd.query_base_addr,
+						f30->query.regs,
+					ARRAY_SIZE(f30->query.regs));
+
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to read query register.\n");
+		return retval;
+	}
+
+	f30->gpioled_count = f30->query.gpio_led_count;
+	f30->button_bitmask_size = sizeof(u8)*(f30->gpioled_count + 7) / 8;
+	f30->button_data_buffer =
+	    kcalloc(f30->button_bitmask_size,
+		    sizeof(unsigned char), GFP_KERNEL);
+	if (!f30->button_data_buffer) {
+		dev_err(&fc->dev, "Failed to allocate button data buffer.\n");
+		return -ENOMEM;
+	}
+
+	f30->gpioled_map = kcalloc(f30->gpioled_count,
+				sizeof(unsigned short), GFP_KERNEL);
+	if (!f30->gpioled_map) {
+		dev_err(&fc->dev, "Failed to allocate button map.\n");
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static inline void rmi_f30_free_memory(struct rmi_function_container *fc)
+{
+	struct rmi_fn_30_data *f30 = fc->data;
+	u8 reg_num = 0;
+	sysfs_remove_group(&fc->dev.kobj, &attrs_query);
+	sysfs_remove_group(&fc->dev.kobj, &attrs_data);
+	for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_ctrl_regs); reg_num++)
+		sysfs_remove_group(&fc->dev.kobj, &attrs_ctrl_regs[reg_num]);
+	if (f30) {
+		if (f30->control.reg_0)
+			kfree(f30->control.reg_0->regs);
+		kfree(f30->control.reg_0);
+
+		if (f30->control.reg_1)
+			kfree(f30->control.reg_1->regs);
+		kfree(f30->control.reg_1);
+
+		if (f30->control.reg_2)
+			kfree(f30->control.reg_2->regs);
+		kfree(f30->control.reg_2);
+
+		if (f30->control.reg_3)
+			kfree(f30->control.reg_3->regs);
+		kfree(f30->control.reg_3);
+
+		if (f30->control.reg_4)
+			kfree(f30->control.reg_4->regs);
+		kfree(f30->control.reg_4);
+
+		if (f30->control.reg_5)
+			kfree(f30->control.reg_5->regs);
+		kfree(f30->control.reg_5);
+
+		if (f30->control.reg_6)
+			kfree(f30->control.reg_6->regs);
+		kfree(f30->control.reg_6);
+
+		if (f30->control.reg_7)
+			kfree(f30->control.reg_7->regs);
+		kfree(f30->control.reg_7);
+
+		if (f30->control.reg_8)
+			kfree(f30->control.reg_8->regs);
+		kfree(f30->control.reg_8);
+
+		if (f30->control.reg_9)
+			kfree(f30->control.reg_9->regs);
+		kfree(f30->control.reg_9);
+
+		if (!f30->data.datareg_0)
+			kfree(f30->data.datareg_0->regs);
+		kfree(f30->data.datareg_0);
+
+		kfree(f30->button_data_buffer);
+		kfree(f30->gpioled_map);
+		kfree(f30);
+		fc->data = NULL;
+	}
+}
+
+int rmi_f30_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	int data_base_addr = fc->fd.data_base_addr;
+	struct rmi_fn_30_data *f30 = fc->data;
+	int error;
+	int gpiled;
+	bool gpiled_status = false;
+	int status = 0;
+
+	/* Read the button data. */
+
+	error = rmi_read_block(rmi_dev, data_base_addr,
+			f30->button_data_buffer,
+			f30->button_bitmask_size);
+	if (error < 0) {
+		dev_err(&fc->dev,
+			"%s: Failed to read button data registers.\n",
+			__func__);
+		return error;
+	}
+
+	/* Read the gpi led data. */
+	f30->data.address = fc->fd.data_base_addr;
+	error = rmi_read_block(fc->rmi_dev, f30->data.address,
+		(u8 *)&f30->data, f30->gpioled_count);
+
+	if (error < 0) {
+		dev_err(&fc->dev, "%s: Failed to read f30 data registers.\n",
+			__func__);
+		return error;
+	}
+	/* Generate events for buttons that change state. */
+	for (gpiled = 0; gpiled < f30->gpioled_count; gpiled++) {
+		status = f30->data.datareg_0->regs[gpiled].gpi_led_data;
+		dev_warn(&fc->dev,
+			"rmi_f30 attention gpiled=%d data status=%d\n",
+			gpiled,
+			f30->data.datareg_0->regs[gpiled].gpi_led_data);
+		/* check if gpio */
+		if (!(f30->control.reg_0->regs[gpiled].led_sel)) {
+			if (f30->control.reg_2->regs[gpiled].dir == 0) {
+				gpiled_status = status != 0;
+
+		/* if the gpiled data state changed from the
+		* last time report it and store the new state */
+		/* Generate an event here. */
+			dev_warn(&fc->dev,
+			"rmi_f30 attention call input_report_key\n");
+			input_report_key(f30->input,
+				f30->data.datareg_0->regs[gpiled].gpi_led_data,
+				gpiled_status);
+			}
+		}
+	}
+	input_sync(f30->input); /* sync after groups of events */
+	return 0;
+}
+
+static int rmi_f30_register_device(struct rmi_function_container *fc)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct input_dev *input_dev;
+	struct rmi_fn_30_data *f30 = fc->data;
+	int i;
+	int rc;
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		dev_err(&fc->dev, "Failed to allocate input device.\n");
+		return -ENOMEM;
+	}
+
+	f30->input = input_dev;
+	snprintf(f30->input_name, MAX_LEN, "%sfn%02x", dev_name(&rmi_dev->dev),
+		fc->fd.function_number);
+	input_dev->name = f30->input_name;
+	snprintf(f30->input_phys, MAX_LEN, "%s/input0", input_dev->name);
+	input_dev->phys = f30->input_phys;
+	input_dev->dev.parent = &rmi_dev->dev;
+	input_set_drvdata(input_dev, f30);
+
+	/* Set up any input events. */
+	set_bit(EV_SYN, input_dev->evbit);
+	set_bit(EV_KEY, input_dev->evbit);
+	input_dev->keycode = f30->gpioled_map;
+	input_dev->keycodesize = 1;
+	input_dev->keycodemax = f30->gpioled_count;
+	/* set bits for each qpio led pin... */
+	for (i = 0; i < f30->gpioled_count; i++) {
+		set_bit(f30->gpioled_map[i], input_dev->keybit);
+		input_set_capability(input_dev, EV_KEY, f30->gpioled_map[i]);
+	}
+
+	rc = input_register_device(input_dev);
+	if (rc < 0) {
+		dev_err(&fc->dev, "Failed to register input device.\n");
+		goto error_free_device;
+	}
+	return 0;
+
+error_free_device:
+	input_free_device(input_dev);
+
+	return rc;
+}
+
+
+static int rmi_f30_config(struct rmi_function_container *fc)
+{
+	struct rmi_fn_30_data *data = fc->data;
+	int gpio_led_cnt = data->query.gpio_led_count;
+	int bytecnt = gpio_led_cnt / 7 + 1;
+	int regs_size = 0;
+	int rc;
+	/* repeated register functions */
+
+	/* Write Control Register values back to device */
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_0->address,
+				(u8 *)data->control.reg_0,
+				bytecnt * sizeof(struct f30_gpio_ctrl_0n));
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 0 to 0x%x\n",
+				__func__, rc, data->control.reg_0->address);
+		return rc;
+	}
+
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_1->address,
+			(u8 *) data->control.reg_1->regs,
+			sizeof(union f30_gpio_ctrl_1));
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 1 to 0x%x\n",
+				__func__, rc, data->control.reg_1->address);
+		return rc;
+	}
+
+	regs_size = data->control.reg_2->length;
+
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_2->address,
+			(u8 *) data->control.reg_2->regs,
+			regs_size);
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 2 to 0x%x\n",
+				__func__, rc, data->control.reg_2->address);
+		return rc;
+	}
+
+	regs_size = data->control.reg_3->length;
+
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_3->address,
+			(u8 *) data->control.reg_3->regs,
+			regs_size);
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 3 to 0x%x\n",
+				__func__, rc, data->control.reg_3->address);
+		return rc;
+	}
+
+	regs_size = data->control.reg_4->length;
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_4->address,
+			(u8 *) data->control.reg_4->regs,
+			regs_size);
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 4 to 0x%x\n",
+				__func__, rc, data->control.reg_4->address);
+		return rc;
+	}
+
+	regs_size = data->control.reg_5->length;
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_5->address,
+			(u8 *) data->control.reg_5->regs,
+			regs_size);
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 5 to 0x%x\n",
+				__func__, rc, data->control.reg_5->address);
+		return rc;
+	}
+
+	regs_size = data->control.reg_6->length;
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_6->address,
+			(u8 *) data->control.reg_6->regs,
+			regs_size);
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 6 to 0x%x\n",
+				__func__, rc, data->control.reg_6->address);
+		return rc;
+	}
+
+	regs_size = data->control.reg_7->length;
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_7->address,
+			(u8 *) data->control.reg_7->regs,
+			regs_size);
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 7 to 0x%x\n",
+			__func__, rc, data->control.reg_7->address);
+		return rc;
+	}
+
+	regs_size = data->control.reg_8->length;
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_8->address,
+			(u8 *) data->control.reg_8->regs, regs_size);
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 9 to 0x%x\n",
+			__func__, rc, data->control.reg_8->address);
+		return rc;
+	}
+
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_9->address,
+			(u8 *) data->control.reg_9->regs,
+			sizeof(union f30_gpio_ctrl_9));
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 9 to 0x%x\n",
+				__func__, rc, data->control.reg_9->address);
+		return rc;
+	}
+
+	return 0;
+}
+
+static inline int rmi_f30_initialize(struct rmi_function_container *fc)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_device_platform_data *pdata;
+	struct rmi_fn_30_data *instance_data = fc->data;
+
+	int retval = 0;
+	u16 next_loc;
+	int  gpio_led_cnt = 0;
+	int regs_size = 0;
+	u8 reg_num = 0;
+	int reg_flg;
+	int hasgpio, hasled, hasmbtn, hashaptic;
+	struct f30_control *control = &instance_data->control;
+
+	/* Read F30 Query Data */
+	instance_data->query.address = fc->fd.query_base_addr;
+	retval = rmi_read_block(fc->rmi_dev, instance_data->query.address,
+		(u8 *)&instance_data->query, sizeof(instance_data->query));
+	if (retval < 0) {
+		dev_err(&fc->dev,
+		"Could not read query registers from 0x%04x\n",
+		instance_data->query.address);
+		return retval;
+	}
+
+	/* initialize gpioled_map data */
+	hasgpio = instance_data->query.has_gpio;
+	hasled = instance_data->query.has_led;
+	hasmbtn = instance_data->query.has_mappable_buttons;
+	hashaptic = instance_data->query.has_haptic ;
+	gpio_led_cnt = instance_data->query.gpio_led_count;
+
+	pdata = to_rmi_platform_data(rmi_dev);
+	if (pdata) {
+		if (!pdata->gpioled_map) {
+			dev_warn(&fc->dev,
+			"%s - gpioled_map is NULL", __func__);
+		} else if (pdata->gpioled_map->ngpioleds != gpio_led_cnt) {
+			dev_warn(&fc->dev,
+				"Platformdata gpioled map size (%d) != number "
+				"of buttons on device (%d) - ignored.\n",
+				pdata->gpioled_map->ngpioleds,
+				gpio_led_cnt);
+		} else if (!pdata->gpioled_map->map) {
+			dev_warn(&fc->dev,
+				 "Platformdata button map is missing!\n");
+		} else {
+			int i;
+			for (i = 0; i < pdata->gpioled_map->ngpioleds; i++)
+				instance_data->gpioled_map[i] =
+					pdata->gpioled_map->map[i];
+		}
+	}
+
+	/* Initialize Control Data */
+
+	next_loc = fc->fd.control_base_addr;
+
+	/* calculate reg size */
+
+	instance_data->gpioled_bitmask_size = sizeof(u8)*(gpio_led_cnt + 7) / 8;
+	instance_data->gpioled_byte_size = sizeof(u8)*gpio_led_cnt;
+
+	/* reg_0 */
+	control->reg_0 =
+			kzalloc(sizeof(struct f30_gpio_ctrl_0), GFP_KERNEL);
+	if (!control->reg_0) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+
+	if (hasgpio && hasled)
+		reg_flg = 1;
+
+	f30_attrs_regs_exist[reg_num] = true;
+	regs_size = max(sizeof(struct f30_gpio_ctrl_0n) * reg_flg *
+					instance_data->gpioled_bitmask_size, 1);
+	control->reg_0->regs =
+			kzalloc(regs_size, GFP_KERNEL);
+
+	if (!control->reg_0->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_0->address = next_loc;
+	control->reg_0->length = regs_size;
+	next_loc += regs_size;
+	reg_num++;
+
+	/* reg_1 */
+	control->reg_1 =
+			kzalloc(sizeof(union f30_gpio_ctrl_1), GFP_KERNEL);
+	if (!control->reg_1) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	f30_attrs_regs_exist[reg_num] = true;
+	reg_num++;
+	instance_data->control.reg_1->address = next_loc;
+	next_loc += regs_size;
+
+	/* reg_2 */
+	instance_data->control.reg_2 =
+			kzalloc(sizeof(struct f30_gpio_ctrl_2), GFP_KERNEL);
+	if (!control->reg_2) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+
+	reg_flg = hasgpio;
+	f30_attrs_regs_exist[reg_num] = true;
+	regs_size = max(sizeof(struct f30_gpio_ctrl_2n)*reg_flg*
+					instance_data->gpioled_bitmask_size, 1);
+	control->reg_2->regs =
+			kzalloc(regs_size, GFP_KERNEL);
+
+	if (!control->reg_2->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_2->address = next_loc;
+	control->reg_2->length = regs_size;
+	next_loc += regs_size;
+	reg_num++;
+
+	/* reg_3 */
+	instance_data->control.reg_3 =
+			kzalloc(sizeof(struct f30_gpio_ctrl_3), GFP_KERNEL);
+	if (!control->reg_3) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+
+	reg_flg = hasgpio;
+	f30_attrs_regs_exist[reg_num] = true;
+	regs_size = max(sizeof(struct f30_gpio_ctrl_3n) * reg_flg *
+					instance_data->gpioled_bitmask_size, 1);
+	control->reg_3->regs =
+			kzalloc(regs_size, GFP_KERNEL);
+
+	if (!control->reg_3->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_3->address = next_loc;
+	control->reg_3->length = regs_size;
+	next_loc += regs_size;
+	reg_num++;
+
+	/* reg_4 */
+	control->reg_4 =
+			kzalloc(sizeof(struct f30_gpio_ctrl_4), GFP_KERNEL);
+	if (!control->reg_4) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+
+	reg_flg = hasled;
+	f30_attrs_regs_exist[reg_num] = true;
+	regs_size = max(sizeof(struct f30_gpio_ctrl_4n)*reg_flg*
+					instance_data->gpioled_bitmask_size,
+					sizeof(struct f30_gpio_ctrl_4n));
+	control->reg_4->regs =
+			kzalloc(regs_size, GFP_KERNEL);
+	if (!control->reg_4->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_4->address = next_loc;
+	control->reg_4->length = regs_size;
+	next_loc += regs_size;
+	reg_num++;
+
+	/* reg_5 */
+	control->reg_5 =
+			kzalloc(sizeof(struct f30_gpio_ctrl_5), GFP_KERNEL);
+	if (!control->reg_5) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+
+	reg_flg = hasled;
+	f30_attrs_regs_exist[reg_num] = true;
+	regs_size = max(6 * reg_flg, 2);
+	control->reg_5->regs =
+			kzalloc(regs_size, GFP_KERNEL);
+	if (!control->reg_5->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_5->address = next_loc;
+	control->reg_5->length = regs_size;
+	next_loc += regs_size;
+	reg_num++;
+
+	/* reg_6 */
+	control->reg_6 =
+			kzalloc(sizeof(struct f30_gpio_ctrl_6), GFP_KERNEL);
+	if (!control->reg_6) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	reg_flg = hasled || (!hasled
+		&& instance_data->query.has_gpio_driver_control);
+
+	regs_size = max(sizeof(union f30_gpio_ctrl_6n)*reg_flg*gpio_led_cnt,
+					sizeof(union f30_gpio_ctrl_6n));
+	if (!hasled
+		&& instance_data->query.has_gpio_driver_control)
+		f30_attrs_regs_exist[reg_num] = true;
+
+	reg_num++;
+	if (hasled)
+		f30_attrs_regs_exist[reg_num] = true;
+
+	reg_num++;
+
+	control->reg_6->regs =
+			kzalloc(regs_size, GFP_KERNEL);
+	if (!control->reg_6->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_6->address = next_loc;
+	control->reg_6->length = regs_size;
+	next_loc += regs_size;
+
+	/* reg_7 */
+	reg_flg = hasmbtn;
+	control->reg_7 =
+			kzalloc(sizeof(struct f30_gpio_ctrl_7), GFP_KERNEL);
+	if (!control->reg_7) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	if (hasmbtn)
+		regs_size = sizeof(struct f30_gpio_ctrl_7n)*gpio_led_cnt;
+	else
+		regs_size = sizeof(struct f30_gpio_ctrl_7n);
+
+	regs_size = max(sizeof(struct f30_gpio_ctrl_7n)*reg_flg*
+					gpio_led_cnt,
+					sizeof(struct f30_gpio_ctrl_7n));
+	f30_attrs_regs_exist[reg_num] = true;
+	control->reg_7->regs =
+			kzalloc(regs_size, GFP_KERNEL);
+	if (!control->reg_7->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_7->address = next_loc;
+	control->reg_7->length = regs_size;
+	next_loc += regs_size;
+	reg_num++;
+
+	/* reg_8 */
+	control->reg_8 =
+			kzalloc(sizeof(struct f30_gpio_ctrl_8), GFP_KERNEL);
+	if (!control->reg_8) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+
+	regs_size = max(sizeof(struct f30_gpio_ctrl_8n)*hashaptic*
+					gpio_led_cnt,
+					sizeof(struct f30_gpio_ctrl_8n));
+	f30_attrs_regs_exist[reg_num] = true;
+	control->reg_8->regs =
+			kzalloc(regs_size, GFP_KERNEL);
+	if (!control->reg_8->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_8->address = next_loc;
+	control->reg_8->length = regs_size;
+	next_loc += regs_size;
+	reg_num++;
+
+	/* reg_9 */
+	control->reg_9 =
+			kzalloc(sizeof(union f30_gpio_ctrl_9), GFP_KERNEL);
+	if (!control->reg_9) {
+		dev_err(&fc->dev, "Failed to allocate control register.");
+		return -ENOMEM;
+	}
+	if (instance_data->query.has_haptic)
+		f30_attrs_regs_exist[reg_num] = true;
+	control->reg_9->address = next_loc;
+	next_loc += sizeof(control->reg_9->regs);
+
+	/* data reg_0 */
+	instance_data->data.datareg_0 =
+			kzalloc(sizeof(struct f30_data_0), GFP_KERNEL);
+	if (!instance_data->data.datareg_0) {
+		dev_err(&fc->dev, "Failed to allocate control register.");
+		return -ENOMEM;
+	}
+
+	regs_size = sizeof(struct f30_data_0n)*
+				instance_data->gpioled_byte_size;
+	instance_data->data.datareg_0->address = fc->fd.data_base_addr;
+	next_loc += sizeof(instance_data->data.datareg_0->regs);
+
+	retval = rmi_f30_read_control_parameters(rmi_dev, instance_data);
+	if (retval < 0) {
+		dev_err(&fc->dev,
+			"Failed to initialize F19 control params.\n");
+		return retval;
+	}
+
+	mutex_init(&instance_data->control_mutex);
+	mutex_init(&instance_data->data_mutex);
+	return 0;
+}
+
+static int rmi_f30_create_sysfs(struct rmi_function_container *fc)
+{
+	u8 reg_num;
+
+	dev_dbg(&fc->dev, "Creating sysfs files.");
+
+	/* Set up sysfs device attributes. */
+	if (sysfs_create_group(&fc->dev.kobj, &attrs_query) < 0) {
+		dev_err(&fc->dev, "Failed to create query sysfs files.");
+		return -ENODEV;
+	}
+	if (sysfs_create_group(&fc->dev.kobj, &attrs_data) < 0) {
+		dev_err(&fc->dev, "Failed to create data sysfs files.");
+		return -ENODEV;
+	}
+
+	for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_ctrl_regs);
+		reg_num++) {
+		if (f30_attrs_regs_exist[reg_num]) {
+			if (sysfs_create_group(&fc->dev.kobj,
+					&attrs_ctrl_regs[reg_num]) < 0) {
+				dev_err(&fc->dev,
+					"Failed to create "
+					"sysfs file group for reg"
+					"group %d.",
+					reg_num);
+				return -ENODEV;
+			}
+
+		}
+	}
+
+	return 0;
+}
+
+static void rmi_f30_remove(struct rmi_function_container *fc)
+{
+	dev_info(&fc->dev, "Removing F30.");
+	rmi_f30_free_memory(fc);
+}
+
+static struct rmi_function_handler function_handler = {
+	.func = 0x30,
+	.init = rmi_f30_init,
+	.config = rmi_f30_config,
+	.attention = rmi_f30_attention,
+	.remove = rmi_f30_remove
+};
+
+static int __init rmi_f30_module_init(void)
+{
+	int error;
+
+	error = rmi_register_function_driver(&function_handler);
+	if (error < 0) {
+		pr_err("%s: register failed!\n", __func__);
+		return error;
+	}
+	return 0;
+}
+
+static void rmi_f30_module_exit(void)
+{
+	rmi_unregister_function_driver(&function_handler);
+}
+
+/* sysfs functions */
+/* Query */
+simple_show_union_struct_unsigned(query, extended_patterns)
+simple_show_union_struct_unsigned(query, has_mappable_buttons)
+simple_show_union_struct_unsigned(query, has_led)
+simple_show_union_struct_unsigned(query, has_gpio)
+simple_show_union_struct_unsigned(query, has_haptic)
+simple_show_union_struct_unsigned(query, has_gpio_driver_control)
+simple_show_union_struct_unsigned(query, gpio_led_count)
+
+/* Control */
+show_store_union_struct_unsigned(control, reg_1, gpio_debounce)
+show_store_union_struct_unsigned(control, reg_1, halt)
+show_store_union_struct_unsigned(control, reg_1, halted)
+show_store_union_struct_unsigned(control, reg_9, haptic_duration)
+
+/* repeated register functions */
+show_store_repeated_union_struct_unsigned(control, reg_0, led_sel)
+show_store_repeated_union_struct_unsigned(control, reg_2, dir)
+show_store_repeated_union_struct_unsigned(control, reg_3, gpiodata)
+show_store_repeated_union_struct_unsigned(control, reg_4, led_act)
+show_store_repeated_union_struct_unsigned(control, reg_5, ramp_period_a)
+show_store_repeated_union_struct_unsigned(control, reg_5, ramp_period_b)
+show_store_repeated_union_struct_unsigned(control, reg_6, SPCTRL)
+show_store_repeated_union_struct_unsigned(control, reg_6, STRPD)
+show_store_repeated_union_struct_unsigned(control, reg_6, STRPU)
+show_store_repeated_union_struct_unsigned(control, reg_6, brightness)
+show_store_repeated_union_struct_unsigned(control, reg_6, pattern)
+show_store_repeated_union_struct_unsigned(control, reg_7, capacity_btn_nbr)
+show_store_repeated_union_struct_unsigned(control, reg_7, valid)
+show_store_repeated_union_struct_unsigned(control, reg_7, invert)
+show_store_repeated_union_struct_unsigned(control, reg_7, open_drain)
+show_store_repeated_union_struct_unsigned(control, reg_8, gpio_ctrl8_0)
+show_store_repeated_union_struct_unsigned(control, reg_8, gpio_ctrl8_1)
+
+/* Data */
+show_store_repeated_union_struct_unsigned(data, datareg_0, gpi_led_data)
+
+module_init(rmi_f30_module_init);
+module_exit(rmi_f30_module_exit);
+
+MODULE_AUTHOR("Allie Xiong <axiong@...aptics.com>");
+MODULE_DESCRIPTION("RMI f30 module");
+MODULE_LICENSE("GPL");
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ