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-16-git-send-email-cheiny@synaptics.com>
Date:	Fri, 17 Aug 2012 15:17:55 -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 15/17] input: RMI4 F34 device reflash

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_f34.c |  917 ++++++++++++++++++++++++++++++++++++++++++
 drivers/input/rmi4/rmi_f34.h |   74 ++++
 2 files changed, 991 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f34.c b/drivers/input/rmi4/rmi_f34.c
new file mode 100644
index 0000000..a07f7f9
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f34.c
@@ -0,0 +1,917 @@
+/*
+ * Copyright (c) 2011, 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.
+ */
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include "rmi_driver.h"
+#include "rmi_f34.h"
+
+#define BLK_SZ_OFF	3
+#define IMG_BLK_CNT_OFF	5
+#define CFG_BLK_CNT_OFF	7
+
+#define BLK_NUM_OFF 2
+
+/* data specific to fn $34 that needs to be kept around */
+struct rmi_fn_34_data {
+	unsigned char status;
+	unsigned char cmd;
+	unsigned short bootloaderid;
+	unsigned short blocksize;
+	unsigned short imageblockcount;
+	unsigned short configblockcount;
+	unsigned short blocknum;
+	bool inflashprogmode;
+	struct mutex attn_mutex;
+};
+
+static ssize_t rmi_fn_34_status_show(struct device *dev,
+				     struct device_attribute *attr, char *buf);
+
+
+static ssize_t rmi_fn_34_status_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count);
+
+static ssize_t rmi_fn_34_cmd_show(struct device *dev,
+				  struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_34_cmd_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count);
+
+static ssize_t rmi_fn_34_data_read(struct file *data_file, struct kobject *kobj,
+				   struct bin_attribute *attributes,
+				   char *buf, loff_t pos, size_t count);
+
+static ssize_t rmi_fn_34_data_write(struct file *data_file,
+				    struct kobject *kobj,
+				    struct bin_attribute *attributes, char *buf,
+				    loff_t pos, size_t count);
+
+static ssize_t rmi_fn_34_bootloaderid_show(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf);
+
+static ssize_t rmi_fn_34_bootloaderid_store(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf, size_t count);
+
+static ssize_t rmi_fn_34_blocksize_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf);
+
+static ssize_t rmi_fn_34_imageblockcount_show(struct device *dev,
+					      struct device_attribute *attr,
+					      char *buf);
+
+static ssize_t rmi_fn_34_configblockcount_show(struct device *dev,
+					       struct device_attribute *attr,
+					       char *buf);
+
+static ssize_t rmi_fn_34_blocknum_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf);
+
+static ssize_t rmi_fn_34_blocknum_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count);
+
+static ssize_t rmi_fn_34_rescanPDT_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count);
+
+
+static int rmi_f34_alloc_memory(struct rmi_function_container *fc);
+
+static void rmi_f34_free_memory(struct rmi_function_container *fc);
+
+static int rmi_f34_initialize(struct rmi_function_container *fc);
+
+static int rmi_f34_config(struct rmi_function_container *fc);
+
+static int rmi_f34_reset(struct rmi_function_container *fc);
+
+static int rmi_f34_create_sysfs(struct rmi_function_container *fc);
+
+
+
+static struct device_attribute attrs[] = {
+	__ATTR(status, RMI_RW_ATTR,
+	       rmi_fn_34_status_show, rmi_fn_34_status_store),
+
+	/* Also, sysfs will need to have a file set up to distinguish
+	 * between commands - like Config write/read, Image write/verify. */
+	__ATTR(cmd, RMI_RW_ATTR,
+	       rmi_fn_34_cmd_show, rmi_fn_34_cmd_store),
+	__ATTR(bootloaderid, RMI_RW_ATTR,
+	       rmi_fn_34_bootloaderid_show, rmi_fn_34_bootloaderid_store),
+	__ATTR(blocksize, RMI_RO_ATTR,
+	       rmi_fn_34_blocksize_show, rmi_store_error),
+	__ATTR(imageblockcount, RMI_RO_ATTR,
+	       rmi_fn_34_imageblockcount_show, rmi_store_error),
+	__ATTR(configblockcount, RMI_RO_ATTR,
+	       rmi_fn_34_configblockcount_show, rmi_store_error),
+	__ATTR(blocknum, RMI_RW_ATTR,
+	       rmi_fn_34_blocknum_show, rmi_fn_34_blocknum_store),
+	__ATTR(rescanPDT, RMI_WO_ATTR,
+	       rmi_show_error, rmi_fn_34_rescanPDT_store)
+};
+
+struct bin_attribute dev_attr_data = {
+	.attr = {
+		 .name = "data",
+		 .mode = 0666},
+	.size = 0,
+	.read = rmi_fn_34_data_read,
+	.write = rmi_fn_34_data_write,
+};
+
+
+static int rmi_f34_init(struct rmi_function_container *fc)
+{
+	int retval;
+
+	dev_info(&fc->dev, "Intializing f34 values.");
+
+	/* init instance data, fill in values and create any sysfs files */
+	retval = rmi_f34_alloc_memory(fc);
+	if (retval < 0)
+		goto exit_free_data;
+
+	retval = rmi_f34_initialize(fc);
+	if (retval < 0)
+		goto exit_free_data;
+
+	retval = rmi_f34_create_sysfs(fc);
+	if (retval < 0)
+		goto exit_free_data;
+
+	return 0;
+
+exit_free_data:
+	rmi_f34_free_memory(fc);
+
+	return retval;
+}
+
+static int rmi_f34_alloc_memory(struct rmi_function_container *fc)
+{
+	struct rmi_fn_34_data *f34;
+
+	f34 = kzalloc(sizeof(struct rmi_fn_34_data), GFP_KERNEL);
+	if (!f34) {
+		dev_err(&fc->dev, "Failed to allocate rmi_fn_34_data.\n");
+		return -ENOMEM;
+	}
+	fc->data = f34;
+
+	return 0;
+}
+
+static void rmi_f34_free_memory(struct rmi_function_container *fc)
+{
+	kfree(fc->data);
+	fc->data = NULL;
+}
+
+static int rmi_f34_initialize(struct rmi_function_container *fc)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_device_platform_data *pdata;
+	int retval = 0;
+	struct rmi_fn_34_data *f34 = fc->data;
+	u16 query_base_addr;
+	u16 control_base_addr;
+	unsigned char buf[2];
+
+	pdata = to_rmi_platform_data(rmi_dev);
+	dev_dbg(&fc->dev, "Initializing F34 values for %s.\n",
+		pdata->sensor_name);
+
+	mutex_init(&f34->attn_mutex);
+
+	/* get the Bootloader ID and Block Size. */
+	query_base_addr = fc->fd.query_base_addr;
+	control_base_addr = fc->fd.control_base_addr;
+
+	retval = rmi_read_block(fc->rmi_dev, query_base_addr, buf,
+			ARRAY_SIZE(buf));
+
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not read bootloaderid from 0x%04x.\n",
+			query_base_addr);
+		return retval;
+	}
+
+	batohs(&f34->bootloaderid, buf);
+
+	retval = rmi_read_block(fc->rmi_dev, query_base_addr + BLK_SZ_OFF, buf,
+			ARRAY_SIZE(buf));
+
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not read block size from 0x%04x, "
+			"error=%d.\n", query_base_addr + BLK_SZ_OFF, retval);
+		return retval;
+	}
+	batohs(&f34->blocksize, buf);
+
+	/* Get firmware image block count and store it in the instance data */
+	retval = rmi_read_block(fc->rmi_dev, query_base_addr + IMG_BLK_CNT_OFF,
+			buf, ARRAY_SIZE(buf));
+
+	if (retval < 0) {
+		dev_err(&fc->dev, "Couldn't read image block count from 0x%x, "
+			"error=%d.\n", query_base_addr + IMG_BLK_CNT_OFF,
+			retval);
+		return retval;
+	}
+	batohs(&f34->imageblockcount, buf);
+
+	/* Get config block count and store it in the instance data */
+	retval = rmi_read_block(fc->rmi_dev, query_base_addr + 7, buf,
+			ARRAY_SIZE(buf));
+
+	if (retval < 0) {
+		dev_err(&fc->dev, "Couldn't read config block count from 0x%x, "
+			"error=%d.\n", query_base_addr + CFG_BLK_CNT_OFF,
+			retval);
+		return retval;
+	}
+	batohs(&f34->configblockcount, buf);
+
+	return 0;
+}
+
+static int rmi_f34_create_sysfs(struct rmi_function_container *fc)
+{
+	int attr_count = 0;
+	int rc;
+
+	dev_dbg(&fc->dev, "Creating sysfs files.");
+
+	/* We need a sysfs file for the image/config block to write or read.
+	 * Set up sysfs bin file for binary data block. Since the image is
+	 * already in our format there is no need to convert the data for
+	 * endianess. */
+	rc = sysfs_create_bin_file(&fc->dev.kobj,
+				&dev_attr_data);
+	if (rc < 0) {
+		dev_err(&fc->dev, "Failed to create sysfs file for F34 data "
+		     "(error = %d).\n", rc);
+		return -ENODEV;
+	}
+
+	/* Set up sysfs device attributes. */
+	for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+		if (sysfs_create_file
+		    (&fc->dev.kobj, &attrs[attr_count].attr) < 0) {
+			dev_err(&fc->dev, "Failed to create sysfs file for %s.",
+			     attrs[attr_count].attr.name);
+			rc = -ENODEV;
+			goto err_remove_sysfs;
+		}
+	}
+
+	return 0;
+
+err_remove_sysfs:
+	sysfs_remove_bin_file(&fc->dev.kobj, &dev_attr_data);
+
+	for (attr_count--; attr_count >= 0; attr_count--)
+		sysfs_remove_file(&fc->dev.kobj,
+				  &attrs[attr_count].attr);
+	return rc;
+}
+
+static int rmi_f34_config(struct rmi_function_container *fc)
+{
+	/* for this function we should do nothing here */
+	return 0;
+}
+
+
+static int rmi_f34_reset(struct rmi_function_container *fc)
+{
+	struct  rmi_fn_34_data  *instance_data = fc->data;
+
+	instance_data->status = ECONNRESET;
+
+	return 0;
+}
+
+static void rmi_f34_remove(struct rmi_function_container *fc)
+{
+	int attr_count;
+
+	sysfs_remove_bin_file(&fc->dev.kobj,
+						  &dev_attr_data);
+
+	for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
+		sysfs_remove_file(&fc->dev.kobj,
+				  &attrs[attr_count].attr);
+
+	rmi_f34_free_memory(fc);
+}
+
+static int f34_read_status(struct rmi_function_container *fc)
+{
+	struct rmi_fn_34_data *instance_data = fc->data;
+	u16 data_base_addr = fc->fd.data_base_addr;
+	u8 status;
+	int retval;
+
+	if (instance_data->status == ECONNRESET)
+		return instance_data->status;
+
+	/* Read the Fn $34 status from F34_Flash_Data3 to see the previous
+	 * commands status. F34_Flash_Data3 will be the address after the
+	 * 2 block number registers plus blocksize Data registers.
+	 *  inform user space - through a sysfs param. */
+	retval = rmi_read(fc->rmi_dev,
+			  data_base_addr + instance_data->blocksize +
+			  BLK_NUM_OFF, &status);
+
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not read status from 0x%x\n",
+		       data_base_addr + instance_data->blocksize + BLK_NUM_OFF);
+		status = 0xff;	/* failure */
+	}
+
+	/* set a sysfs value that the user mode can read - only
+	 * upper 4 bits are the status. successful is $80, anything
+	 * else is failure */
+	instance_data->status = status & 0xf0;
+
+	/* put mode into Flash Prog Mode when we successfully do
+	 * an Enable Flash Prog cmd. */
+	if ((instance_data->status == F34_STATUS_IDLE) &&
+		(instance_data->cmd == F34_ENABLE_FLASH_PROG))
+		instance_data->inflashprogmode = true;
+
+	return retval;
+}
+
+int rmi_f34_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+	int retval;
+	struct rmi_fn_34_data *data = fc->data;
+
+	mutex_lock(&data->attn_mutex);
+	retval = f34_read_status(fc);
+	mutex_unlock(&data->attn_mutex);
+	return retval;
+}
+
+static struct rmi_function_handler function_handler = {
+	.func = 0x34,
+	.init = rmi_f34_init,
+	.config = rmi_f34_config,
+	.reset = rmi_f34_reset,
+	.attention = rmi_f34_attention,
+	.remove = rmi_f34_remove
+};
+
+static ssize_t rmi_fn_34_bootloaderid_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct rmi_function_container *fc;
+	struct rmi_fn_34_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->bootloaderid);
+}
+
+static ssize_t rmi_fn_34_bootloaderid_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	int error;
+	unsigned long val;
+	unsigned char data[2];
+	struct rmi_function_container *fc;
+	struct rmi_fn_34_data *instance_data;
+	u16 data_base_addr;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	/* need to convert the string data to an actual value */
+	error = strict_strtoul(buf, 10, &val);
+
+	if (error)
+		return error;
+
+	instance_data->bootloaderid = val;
+
+	/* Write the Bootloader ID key data back to the first two Block
+	 * Data registers (F34_Flash_Data2.0 and F34_Flash_Data2.1). */
+	hstoba(data, (unsigned short)val);
+	data_base_addr = fc->fd.data_base_addr;
+
+	error = rmi_write_block(fc->rmi_dev,
+				data_base_addr + BLK_NUM_OFF,
+				data,
+				ARRAY_SIZE(data));
+
+	if (error < 0) {
+		dev_err(dev, "%s : Could not write bootloader id to 0x%x\n",
+		       __func__, data_base_addr + BLK_NUM_OFF);
+		return error;
+	}
+
+	return count;
+}
+
+static ssize_t rmi_fn_34_blocksize_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct rmi_function_container *fc;
+	struct rmi_fn_34_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->blocksize);
+}
+
+static ssize_t rmi_fn_34_imageblockcount_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct rmi_function_container *fc;
+	struct rmi_fn_34_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			instance_data->imageblockcount);
+}
+
+static ssize_t rmi_fn_34_configblockcount_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct rmi_function_container *fc;
+	struct rmi_fn_34_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			instance_data->configblockcount);
+}
+
+static ssize_t rmi_fn_34_status_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct rmi_function_container *fc;
+	struct rmi_fn_34_data *instance_data;
+	int retval;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	mutex_lock(&instance_data->attn_mutex);
+	retval = f34_read_status(fc);
+	mutex_unlock(&instance_data->attn_mutex);
+
+	if (retval < 0)
+		return retval;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->status);
+}
+
+
+static ssize_t rmi_fn_34_status_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct rmi_function_container *fc;
+	struct rmi_fn_34_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	instance_data->status = 0;
+
+	return 0;
+}
+
+
+static ssize_t rmi_fn_34_cmd_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct rmi_function_container *fc;
+	struct rmi_fn_34_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->cmd);
+}
+
+static ssize_t rmi_fn_34_cmd_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf,
+				size_t count)
+{
+	struct rmi_function_container *fc;
+	struct rmi_fn_34_data *instance_data;
+	unsigned long val;
+	u16 data_base_addr;
+	int error;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+	data_base_addr = fc->fd.data_base_addr;
+
+	/* need to convert the string data to an actual value */
+	error = strict_strtoul(buf, 10, &val);
+	if (error)
+		return error;
+
+	/* make sure we are in Flash Prog mode for all cmds except the
+	 * Enable Flash Programming cmd - otherwise we are in error */
+	if ((val != F34_ENABLE_FLASH_PROG) && !instance_data->inflashprogmode) {
+		dev_err(dev, "%s: CANNOT SEND CMD %d TO SENSOR - "
+			"NOT IN FLASH PROG MODE\n"
+			, __func__, data_base_addr);
+		return -EINVAL;
+	}
+
+	instance_data->cmd = val;
+
+	/* Validate command value and (if necessary) write it to the command
+	 * register.
+	 */
+	switch (instance_data->cmd) {
+	case F34_ENABLE_FLASH_PROG:
+	case F34_ERASE_ALL:
+	case F34_ERASE_CONFIG:
+	case F34_WRITE_FW_BLOCK:
+	case F34_READ_CONFIG_BLOCK:
+	case F34_WRITE_CONFIG_BLOCK:
+		/* Reset the status to indicate we are in progress on a cmd. */
+		/* The status will change when the ATTN interrupt happens
+		   and the status of the cmd that was issued is read from
+		   the F34_Flash_Data3 register - result should be 0x80 for
+		   success - any other value indicates an error */
+
+		/* Issue the command to the device. */
+		error = rmi_write(fc->rmi_dev,
+				data_base_addr + instance_data->blocksize +
+				BLK_NUM_OFF, instance_data->cmd);
+
+		if (error < 0) {
+			dev_err(dev, "%s: Could not write command 0x%02x "
+				"to 0x%04x\n", __func__, instance_data->cmd,
+				data_base_addr + instance_data->blocksize +
+				BLK_NUM_OFF);
+			return error;
+		}
+
+		if (instance_data->cmd == F34_ENABLE_FLASH_PROG)
+			instance_data->inflashprogmode = true;
+
+		/* set status to indicate we are in progress */
+		instance_data->status = F34_STATUS_IN_PROGRESS;
+		break;
+	default:
+		dev_dbg(dev, "%s: RMI4 function $34 - "
+				"unknown command 0x%02lx.\n", __func__, val);
+		count = -EINVAL;
+		break;
+	}
+
+	return count;
+}
+
+static ssize_t rmi_fn_34_blocknum_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct rmi_function_container *fc;
+	struct rmi_fn_34_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->blocknum);
+}
+
+static ssize_t rmi_fn_34_blocknum_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	int error;
+	unsigned long val;
+	unsigned char data[2];
+	struct rmi_function_container *fc;
+	struct rmi_fn_34_data *instance_data;
+	u16 data_base_addr;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+	data_base_addr = fc->fd.data_base_addr;
+
+	/* need to convert the string data to an actual value */
+	error = strict_strtoul(buf, 10, &val);
+
+	if (error)
+		return error;
+
+	instance_data->blocknum = val;
+
+	/* Write the Block Number data back to the first two Block
+	 * Data registers (F34_Flash_Data_0 and F34_Flash_Data_1). */
+	hstoba(data, (unsigned short)val);
+
+	error = rmi_write_block(fc->rmi_dev, data_base_addr,
+				data, ARRAY_SIZE(data));
+
+	if (error < 0) {
+		dev_err(dev, "%s : Could not write block number %u to 0x%x\n",
+		       __func__, instance_data->blocknum, data_base_addr);
+		return error;
+	}
+
+	return count;
+}
+
+static ssize_t rmi_fn_34_rescanPDT_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct rmi_function_container *fc;
+	struct rmi_fn_34_data *instance_data;
+	struct rmi_device *rmi_dev;
+	struct rmi_driver_data *driver_data;
+	struct pdt_entry pdt_entry;
+	bool fn01found = false;
+	bool fn34found = false;
+	unsigned int rescan;
+	int irq_count = 0;
+	int retval = 0;
+	int i;
+
+	/* Rescan of the PDT is needed since issuing the Flash Enable cmd
+	 * the device registers for Fn$01 and Fn$34 moving around because
+	 * of the change from Bootloader mode to Flash Programming mode
+	 * may change to a different PDT with only Fn$01 and Fn$34 that
+	 * could have addresses for query, control, data, command registers
+	 * that differ from the PDT scan done at device initialization. */
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+	rmi_dev = fc->rmi_dev;
+	driver_data = rmi_get_driverdata(rmi_dev);
+
+	/* Make sure we are only in Flash Programming mode  - DON'T
+	 * ALLOW THIS IN UI MODE. */
+	if (instance_data->cmd != F34_ENABLE_FLASH_PROG) {
+		dev_err(dev, "%s: NOT IN FLASH PROG MODE - CAN'T RESCAN PDT.\n"
+				, __func__);
+		return -EINVAL;
+	}
+
+	/* The only good value to write to this is 1, we allow 0, but with
+	 * no effect (this is consistent with the way the command bit works. */
+	if (sscanf(buf, "%u", &rescan) != 1)
+		return -EINVAL;
+	if (rescan < 0 || rescan > 1)
+		return -EINVAL;
+
+	/* 0 has no effect, so we skip it entirely. */
+	if (rescan) {
+		/* rescan the PDT - filling in Fn01 and Fn34 addresses -
+		 * this is only temporary - the device will need to be reset
+		 * to return the PDT to the normal values. */
+
+		/* mini-parse the PDT - we only have to get Fn$01 and Fn$34 and
+		   since we are Flash Programming mode we only have page 0. */
+		for (i = PDT_START_SCAN_LOCATION; i >= PDT_END_SCAN_LOCATION;
+			i -= sizeof(pdt_entry)) {
+			retval = rmi_read_block(rmi_dev, i, (u8 *)&pdt_entry,
+					       sizeof(pdt_entry));
+			if (retval != sizeof(pdt_entry)) {
+				dev_err(dev, "%s: err frm rmi_read_block pdt "
+					"entry data from PDT, "
+					"error = %d.", __func__, retval);
+				return retval;
+			}
+
+			if ((pdt_entry.function_number == 0x00) ||
+				(pdt_entry.function_number == 0xff))
+				break;
+
+			dev_dbg(dev, "%s: Found F%.2X\n",
+				__func__, pdt_entry.function_number);
+
+			/* f01 found - just fill in the new addresses in
+			 * the existing fc. */
+			if (pdt_entry.function_number == 0x01) {
+				struct rmi_function_container *f01_fc =
+					driver_data->f01_container;
+				fn01found = true;
+				f01_fc->fd.query_base_addr =
+					pdt_entry.query_base_addr;
+				f01_fc->fd.command_base_addr =
+				  pdt_entry.command_base_addr;
+				f01_fc->fd.control_base_addr =
+				  pdt_entry.control_base_addr;
+				f01_fc->fd.data_base_addr =
+				  pdt_entry.data_base_addr;
+				f01_fc->fd.function_number =
+				  pdt_entry.function_number;
+				f01_fc->fd.interrupt_source_count =
+				  pdt_entry.interrupt_source_count;
+				f01_fc->num_of_irqs =
+				  pdt_entry.interrupt_source_count;
+				f01_fc->irq_pos = irq_count;
+
+				irq_count += f01_fc->num_of_irqs;
+
+				if (fn34found)
+					break;
+			}
+
+			/* f34 found - just fill in the new addresses in
+			 * the existing fc. */
+			if (pdt_entry.function_number == 0x34) {
+				fn34found = true;
+				fc->fd.query_base_addr =
+				  pdt_entry.query_base_addr;
+				fc->fd.command_base_addr =
+				  pdt_entry.command_base_addr;
+				fc->fd.control_base_addr =
+				  pdt_entry.control_base_addr;
+				fc->fd.data_base_addr =
+				  pdt_entry.data_base_addr;
+				fc->fd.function_number =
+				  pdt_entry.function_number;
+				fc->fd.interrupt_source_count =
+				  pdt_entry.interrupt_source_count;
+				fc->num_of_irqs =
+				  pdt_entry.interrupt_source_count;
+				fc->irq_pos = irq_count;
+
+				irq_count += fc->num_of_irqs;
+
+				if (fn01found)
+					break;
+			}
+
+		}
+
+		if (!fn01found || !fn34found) {
+			dev_err(dev, "%s: failed to find fn$01 or fn$34 trying "
+				"to do rescan PDT.\n"
+				, __func__);
+			return -EINVAL;
+		}
+	}
+
+	return count;
+}
+
+static ssize_t rmi_fn_34_data_read(struct file *data_file,
+				struct kobject *kobj,
+				struct bin_attribute *attributes,
+				char *buf,
+				loff_t pos,
+				size_t count)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct rmi_function_container *fc;
+	struct rmi_fn_34_data *instance_data;
+	u16 data_base_addr;
+	int error;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	data_base_addr = fc->fd.data_base_addr;
+
+	if (count != instance_data->blocksize) {
+		dev_err(dev,
+			"%s : Incorrect F34 block size %d. "
+			"Expected size %d.\n",
+			__func__, count, instance_data->blocksize);
+		return -EINVAL;
+	}
+
+	/* Read the data from flash into buf.  The app layer will be blocked
+	 * at reading from the sysfs file.  When we return the count (or
+	 * error if we fail) the app will resume. */
+	error = rmi_read_block(fc->rmi_dev, data_base_addr + BLK_NUM_OFF,
+			(unsigned char *)buf, count);
+
+	if (error < 0) {
+		dev_err(dev, "%s : Could not read data from 0x%04x\n",
+		       __func__, data_base_addr + BLK_NUM_OFF);
+		return error;
+	}
+
+	return count;
+}
+
+static ssize_t rmi_fn_34_data_write(struct file *data_file,
+				struct kobject *kobj,
+				struct bin_attribute *attributes,
+				char *buf,
+				loff_t pos,
+				size_t count)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct rmi_function_container *fc;
+	struct rmi_fn_34_data *instance_data;
+	u16 data_base_addr;
+	int error;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	data_base_addr = fc->fd.data_base_addr;
+
+	/* Write the data from buf to flash. The app layer will be
+	 * blocked at writing to the sysfs file.  When we return the
+	 * count (or error if we fail) the app will resume. */
+
+	if (count != instance_data->blocksize) {
+		dev_err(dev,
+			"%s : Incorrect F34 block size %d. "
+			"Expected size %d.\n",
+			__func__, count, instance_data->blocksize);
+		return -EINVAL;
+	}
+
+	/* Write the data block - only if the count is non-zero  */
+	if (count) {
+		error = rmi_write_block(fc->rmi_dev,
+				data_base_addr + BLK_NUM_OFF,
+				(unsigned char *)buf,
+				count);
+
+		if (error < 0) {
+			dev_err(dev, "%s : Could not write block data "
+				"to 0x%x\n", __func__,
+				data_base_addr + BLK_NUM_OFF);
+			return error;
+		}
+	}
+
+	return count;
+}
+
+static int __init rmi_f34_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_f34_module_exit(void)
+{
+	rmi_unregister_function_driver(&function_handler);
+}
+
+module_init(rmi_f34_module_init);
+module_exit(rmi_f34_module_exit);
+
+MODULE_AUTHOR("William Manson <wmanson@...aptics.com");
+MODULE_DESCRIPTION("RMI F34 module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);
diff --git a/drivers/input/rmi4/rmi_f34.h b/drivers/input/rmi4/rmi_f34.h
new file mode 100644
index 0000000..85f3343
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f34.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2011, 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.
+ */
+
+
+#ifndef _RMI_F34_H
+#define _RMI_F34_H
+
+/* F34 image file offsets. */
+#define F34_FW_IMAGE_OFFSET 0x100
+
+/* F34 register offsets. */
+#define F34_BLOCK_DATA_OFFSET	2
+
+/* F34 commands */
+#define F34_WRITE_FW_BLOCK        0x2
+#define F34_ERASE_ALL             0x3
+#define F34_READ_CONFIG_BLOCK     0x5
+#define F34_WRITE_CONFIG_BLOCK    0x6
+#define F34_ERASE_CONFIG          0x7
+#define F34_ENABLE_FLASH_PROG     0xf
+
+#define F34_STATUS_IN_PROGRESS    0xff
+#define F34_STATUS_IDLE		  0x80
+
+#define F34_IDLE_WAIT_MS 500
+#define F34_ENABLE_WAIT_MS 300
+#define F34_ERASE_WAIT_MS (5 * 1000)
+
+union f34_query_regs {
+	struct {
+		u16 reg_map:1;
+		u16 unlocked:1;
+		u16 has_config_id:1;
+		u16 reserved:5;
+		u16 block_size:16;
+		u16 fw_block_count:16;
+		u16 config_block_count:16;
+	} __attribute__ ((__packed__));
+	struct {
+		u8 regs[7];
+		u16 address;
+	};
+};
+
+union f34_control_status {
+	struct {
+		u8 command:4;
+		u8 status:3;
+		u8 program_enabled:1;
+	} __attribute__ ((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	};
+};
+
+#define IS_IDLE(ctl_ptr) ((!ctl_ptr->status) && (!ctl_ptr->command))
+
+#endif
--
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