[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1309497556-7344-6-git-send-email-cheiny@synaptics.com>
Date: Thu, 30 Jun 2011 22:19:12 -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: [PATCH 5/9] input/touchscreen: Synaptics RMI4 Touchscreen Driver
Driver for Synaptics touchscreens using RMI4 protocol.
Please see the email 0/9 for a description of this patch.
Signed-off-by: Christopher Heiny <cheiny@...aptics.com>
Signed-off-by: William Manson <wmanson@...aptics.com>
Signed-off-by: Allie Xiong <axiong@...aptics.com>
Signed-off-by: Peichen Chang <peichen.chang@...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>
---
diff --git a/drivers/input/touchscreen/rmi_f01.h b/drivers/input/touchscreen/rmi_f01.h
new file mode 100644
index 0000000..dad5615
--- /dev/null
+++ b/drivers/input/touchscreen/rmi_f01.h
@@ -0,0 +1,121 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) Function $01 header.
+ * Copyright (c) 2007 - 2011, Synaptics Incorporated
+ *
+ * There is only one function $01 for each RMI4 sensor. This will be
+ * the function that is used to set sensor control and configurations
+ * and check the interrupts to find the source function that is interrupting.
+ *
+ *
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * 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.
+ *
+ * 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.
+ *
+ *#############################################################################
+ */
+
+#if !defined(_RMI_F01_H)
+#define _RMI_F01_H
+
+/* This encapsulates the information found using the RMI4 Function $01
+ * query registers. There is only one Function $01 per device.
+ *
+ * Assuming appropriate endian-ness, you can populate most of this
+ * structure by reading query registers starting at the query base address
+ * that was obtained from RMI4 function 0x01 function descriptor info read
+ * from the Page Descriptor Table.
+ *
+ * Specific register information is provided in the comments for each field.
+ * For further reference, please see the "Synaptics RMI 4 Interfacing
+ * Guide" document : go to http://www.synaptics.com/developers/manuals - and
+ * select "Synaptics RMI 4 Interfacting Guide".
+ */
+#define F01_PRODUCT_INFO_LENGTH 2
+#define F01_DATE_CODE_LENGTH 3
+#define F01_PRODUCT_ID_LENGTH 10
+struct rmi_F01_query {
+ /* The manufacturer identification byte. */
+ unsigned char mfgid;
+
+ /* The Product Properties information. */
+ unsigned char properties;
+
+ /* The product info bytes. */
+ unsigned char prod_info[F01_PRODUCT_INFO_LENGTH];
+
+ /* Date Code - Year, Month, Day. */
+ unsigned char date_code[F01_DATE_CODE_LENGTH];
+
+ /* Tester ID (14 bits). */
+ unsigned short tester_id;
+
+ /* Serial Number (14 bits). */
+ unsigned short serial_num;
+
+ /* A null-terminated string that identifies this particular product. */
+ char prod_id[F01_PRODUCT_ID_LENGTH];
+};
+
+/* This encapsulates the F01 Device Control control registers.
+ * TODO: This isn't right. The number of interrupt enables needs to be
+ * determined dynamically as the sensor is initialized. Fix this.
+ */
+struct rmi_F01_control {
+ unsigned char device_control;
+ unsigned char interrupt_enable[1];
+};
+
+
+/* This encapsulates the F01 Device Control data registers.
+ * TODO: This isn't right. The number of irqs needs to be determined
+ * dynamically as the sensor is initialized. Fix this.
+ */
+struct rmi_F01_data {
+ unsigned char device_status;
+ unsigned char irqs[1];
+};
+
+
+void FN_01_inthandler(struct rmi_function_info *rmifninfo,
+ unsigned int asserted_IRQs);
+int FN_01_config(struct rmi_function_info *rmifninfo);
+int FN_01_init(struct rmi_function_device *function_device);
+int FN_01_detect(struct rmi_function_info *rmifninfo);
+void FN_01_attention(struct rmi_function_info *rmifninfo);
+int FN_01_suspend(struct rmi_function_info *rmifninfo);
+void FN_01_resume(struct rmi_function_info *rmifninfo);
+
+#define RMI_F01_SLEEP_MODE_MASK 0x03
+/* Position of bits in control register. */
+#define RMI_F01_SLEEP_MODE_OFFSET 0
+
+#define RMI_SLEEP_MODE_NORMAL (0x00)
+#define RMI_SLEEP_MODE_SENSOR_SLEEP (0x01)
+#define RMI_SLEEP_MODE_RESERVED0 (0x02)
+#define RMI_SLEEP_MODE_RESERVED1 (0x03)
+
+#define RMI_IS_VALID_SLEEPMODE(mode) \
+ (mode < RMI_SLEEP_MODE_NORMAL || mode > RMI_SLEEP_MODE_RESERVED1)
+
+/* This bit is used to disable current sleep mode. */
+#define RMI_F01_NO_SLEEP_MASK 0x04
+/* Position of bits in control register. */
+#define RMI_F01_NO_SLEEP_OFFSET 2
+
+#define RMI_NO_SLEEP_ENABLE (0x01)
+#define RMI_NO_SLEEP_DISABLE (0x00)
+
+#endif
diff --git a/drivers/input/touchscreen/rmi_f01.c b/drivers/input/touchscreen/rmi_f01.c
new file mode 100644
index 0000000..48395e4
--- /dev/null
+++ b/drivers/input/touchscreen/rmi_f01.c
@@ -0,0 +1,825 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) Function $01 support for sensor
+ * control and configuration.
+ *
+ * Copyright (c) 2007 - 2011, Synaptics Incorporated
+ *
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * 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.
+ *
+ * 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.
+ *
+ *#############################################################################
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/param.h>
+#include <linux/stat.h>
+
+#include "rmi.h"
+#include "rmi_drvr.h"
+#include "rmi_bus.h"
+#include "rmi_platformdata.h"
+#include "rmi_sensor.h"
+#include "rmi_function.h"
+#include "rmi_f01.h"
+
+/* Query register field positions. */
+#define F01_MFG_ID_POS 0
+#define F01_PROPERTIES_POS 1
+#define F01_PRODUCT_INFO_POS 2
+#define F01_DATE_CODE_POS 4
+#define F01_TESTER_ID_POS 7
+#define F01_SERIAL_NUMBER_POS 9
+#define F01_PRODUCT_ID_POS 11
+#define F01_DATE_CODE_YEAR 0
+#define F01_DATE_CODE_MONTH 1
+#define F01_DATE_CODE_DAY 2
+
+/* Control register bits. */
+#define F01_CONFIGURED (1 << 7)
+#define NONSTANDARD_REPORT_RATE (1 << 6)
+
+/* Command register bits. */
+#define F01_RESET 1
+#define F01_SHUTDOWN (1 << 1)
+
+/* Data register 0 bits. */
+#define F01_UNCONFIGURED (1 << 7)
+#define F01_FLASH_PROGRAMMING_MODE (1 << 6)
+#define F01_STATUS_MASK 0x0F
+
+/** Context data for each F01 we find.
+ */
+struct f01_instance_data {
+ struct rmi_F01_control *control_registers;
+ struct rmi_F01_data *data_registers;
+ struct rmi_F01_query *query_registers;
+
+ bool nonstandard_report_rate;
+ /* original mode before suspend */
+ unsigned char original_sleepmode;
+ /* original no sleep setting */
+ unsigned char original_nosleep;
+};
+static void set_sensor_sleepmode(struct rmi_function_info *functionInfo,
+ unsigned char sleepmode, unsigned char nosleep);
+
+static ssize_t rmi_fn_01_productinfo_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_productid_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_manufacturer_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_datecode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_reportrate_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_reportrate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_reset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_testerid_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_serialnumber_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_sleepmode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_sleepmode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t rmi_fn_01_nosleep_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_nosleep_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static struct device_attribute attrs[] = {
+ __ATTR(productinfo, 0444,
+ rmi_fn_01_productinfo_show, rmi_store_error), /* RO attr */
+ __ATTR(productid, 0444,
+ rmi_fn_01_productid_show, rmi_store_error), /* RO attr */
+ __ATTR(manufacturer, 0444,
+ rmi_fn_01_manufacturer_show, rmi_store_error), /* RO attr */
+ __ATTR(datecode, 0444,
+ rmi_fn_01_datecode_show, rmi_store_error), /* RO attr */
+ __ATTR(reportrate, 0644,
+ rmi_fn_01_reportrate_show, rmi_fn_01_reportrate_store), /* RW */
+ __ATTR(reset, 0200,
+ rmi_show_error, rmi_fn_01_reset_store), /* WO attr */
+ __ATTR(testerid, 0444,
+ rmi_fn_01_testerid_show, rmi_store_error), /* RO attr */
+ __ATTR(serialnumber, 0444,
+ rmi_fn_01_serialnumber_show, rmi_store_error), /* RO attr */
+ __ATTR(sleepmode, (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH),
+ rmi_fn_01_sleepmode_show, rmi_fn_01_sleepmode_store), /* RW */
+ __ATTR(nosleep, (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH),
+ rmi_fn_01_nosleep_show, rmi_fn_01_nosleep_store) /*RW*/
+};
+
+static int set_report_rate(struct rmi_function_info *function_info,
+ bool nonstandard)
+{
+ if (nonstandard) {
+ return rmi_set_bits(function_info->sensor,
+ function_info->function_descriptor.control_base_addr,
+ NONSTANDARD_REPORT_RATE);
+ } else {
+ return rmi_clear_bits(function_info->sensor,
+ function_info->function_descriptor.control_base_addr,
+ NONSTANDARD_REPORT_RATE);
+ }
+}
+
+static void read_query_registers(struct rmi_function_info *rmifninfo)
+{
+ unsigned char query_buffer[21];
+ int retval;
+ struct f01_instance_data *instance_data = rmifninfo->fndata;
+ struct rmi_F01_query *query_registers = instance_data->query_registers;
+
+ /* Read the query info and unpack it. */
+ retval = rmi_read_multiple(rmifninfo->sensor,
+ rmifninfo->function_descriptor.query_base_addr,
+ query_buffer, ARRAY_SIZE(query_buffer));
+ if (retval) {
+ pr_err("%s : Could not read F01 query registers at "
+ "0x%02x. Error %d.\n", __func__,
+ rmifninfo->function_descriptor.query_base_addr,
+ retval);
+ /* Presumably if the read fails, the buffer should be all
+ * zeros, so we're OK to continue. */
+ }
+ query_registers->mfgid = query_buffer[F01_MFG_ID_POS];
+ query_registers->properties = query_buffer[F01_PROPERTIES_POS];
+ query_registers->prod_info[0] =
+ query_buffer[F01_PRODUCT_INFO_POS] & 0x7F;
+ query_registers->prod_info[1] =
+ query_buffer[F01_PRODUCT_INFO_POS + 1] & 0x7F;
+ query_registers->date_code[F01_DATE_CODE_YEAR] =
+ query_buffer[F01_DATE_CODE_POS] & 0x1F;
+ query_registers->date_code[F01_DATE_CODE_MONTH] =
+ query_buffer[F01_DATE_CODE_POS + 1] & 0x0F;
+ query_registers->date_code[F01_DATE_CODE_DAY] =
+ query_buffer[F01_DATE_CODE_POS + 2] & 0x1F;
+ query_registers->tester_id =
+ (((unsigned short)query_buffer[F01_TESTER_ID_POS] & 0x7F) << 7)
+ | (query_buffer[F01_TESTER_ID_POS + 1] & 0x7F);
+ query_registers->serial_num =
+ (((unsigned short)query_buffer[F01_SERIAL_NUMBER_POS] & 0x7F)
+ << 7)
+ | (query_buffer[F01_SERIAL_NUMBER_POS + 1] & 0x7F);
+ memcpy(query_registers->prod_id, &query_buffer[F01_PRODUCT_ID_POS],
+ F01_PRODUCT_ID_LENGTH);
+
+ pr_debug("%s: RMI4 Protocol Function $01 Query information\n",
+ __func__);
+ pr_debug("%s: Manufacturer ID: %d %s\n", __func__,
+ query_registers->mfgid,
+ query_registers->mfgid == 1 ? "(Synaptics)" : "");
+ pr_debug("%s: Product Properties: 0x%x\n", __func__,
+ query_registers->properties);
+ pr_debug("%s: Product Info: 0x%x 0x%x\n", __func__,
+ query_registers->prod_info[0], query_registers->prod_info[1]);
+ pr_debug("%s: Date Code: Year : %d Month: %d Day: %d\n", __func__,
+ query_registers->date_code[F01_DATE_CODE_YEAR],
+ query_registers->date_code[F01_DATE_CODE_MONTH],
+ query_registers->date_code[F01_DATE_CODE_DAY]);
+ pr_debug("%s: Tester ID: %d\n", __func__, query_registers->tester_id);
+ pr_debug("%s: Serial Number: 0x%x\n",
+ __func__, query_registers->serial_num);
+ pr_debug("%s: Product ID: %s\n", __func__, query_registers->prod_id);
+}
+
+
+void FN_01_inthandler(struct rmi_function_info *rmifninfo,
+ unsigned int asserted_IRQs)
+{
+ struct f01_instance_data *instance_data = rmifninfo->fndata;
+
+ pr_debug("%s: Read device status.", __func__);
+
+ if (rmi_read
+ (rmifninfo->sensor, rmifninfo->function_descriptor.data_base_addr,
+ &instance_data->data_registers->device_status)) {
+ pr_err("%s : Could not read F01 device status.\n", __func__);
+ }
+ pr_info("%s: read device status register. Value 0x%02X.", __func__,
+ instance_data->data_registers->device_status);
+
+ if (instance_data->data_registers->device_status & F01_UNCONFIGURED) {
+ pr_info("%s: ++++ Device reset detected.", __func__);
+ /* TODO: Handle device reset appropriately.
+ */
+ }
+}
+EXPORT_SYMBOL(FN_01_inthandler);
+
+/*
+ * This reads in the function $01 source data.
+ *
+ */
+void FN_01_attention(struct rmi_function_info *rmifninfo)
+{
+ struct f01_instance_data *instance_data = rmifninfo->fndata;
+ int retval;
+
+ /* TODO: Compute size to read and number of IRQ registers to processors
+ * dynamically. See comments in rmi.h. */
+ retval =
+ rmi_read_multiple(rmifninfo->sensor,
+ rmifninfo->function_descriptor.data_base_addr + 1,
+ instance_data->data_registers->irqs, 1);
+ if (retval) {
+ pr_err("%s: Could not read interrupt status registers "
+ "at 0x%02x; code=%d.", __func__,
+ rmifninfo->function_descriptor.data_base_addr + 1,
+ retval);
+ return;
+ }
+
+ if (instance_data->data_registers->irqs[0] &
+ instance_data->control_registers->interrupt_enable[0]) {
+ /* call down to the sensors irq dispatcher to dispatch
+ * all enabled IRQs */
+ rmifninfo->sensor->dispatchIRQs(rmifninfo->sensor,
+ instance_data->data_registers->
+ irqs[0]);
+ }
+
+}
+EXPORT_SYMBOL(FN_01_attention);
+
+int FN_01_config(struct rmi_function_info *rmifninfo)
+{
+ int retval = 0;
+ struct f01_instance_data *instance_data = rmifninfo->fndata;
+
+ pr_debug("%s: RMI4 function $01 config\n", __func__);
+
+ /* First thing to do is set the configuration bit. We'll check this at
+ * the end to determine if the device has reset during the config
+ * process.
+ */
+ retval = rmi_set_bits(rmifninfo->sensor,
+ rmifninfo->function_descriptor.control_base_addr,
+ F01_CONFIGURED);
+ if (retval)
+ pr_warning("%s: failed to set configured bit, errno = %d.",
+ __func__, retval);
+
+ /* At config time, the device is presumably in its default state, so we
+ * only need to write non-default configuration settings.
+ */
+ if (instance_data->nonstandard_report_rate) {
+ retval = set_report_rate(rmifninfo, true);
+ if (!retval)
+ pr_warning
+ ("%s: failed to configure report rate, errno = %d.",
+ __func__, retval);
+ }
+
+ /* TODO: Check for reset! */
+
+ return retval;
+}
+EXPORT_SYMBOL(FN_01_config);
+
+/* Initialize any function $01 specific params and settings - input
+ * settings, device settings, etc.
+ */
+int FN_01_init(struct rmi_function_device *function_device)
+{
+ int retval;
+ int attr_count = 0;
+ struct rmi_f01_functiondata *functiondata =
+ rmi_sensor_get_functiondata(function_device->sensor, RMI_F01_INDEX);
+ struct f01_instance_data *instance_data = function_device->rfi->fndata;
+
+ pr_debug("%s: RMI4 function $01 init\n", __func__);
+
+ if (functiondata)
+ instance_data->nonstandard_report_rate =
+ functiondata->nonstandard_report_rate;
+
+ pr_debug("%s: Creating sysfs files.", __func__);
+ /* Set up sysfs device attributes. */
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ if (sysfs_create_file
+ (&function_device->dev.kobj, &attrs[attr_count].attr) < 0) {
+ pr_err
+ ("%s: Failed to create sysfs file for %s.",
+ __func__, attrs[attr_count].attr.name);
+ retval = -ENODEV;
+ goto error_exit;
+ }
+ }
+
+ return 0;
+
+error_exit:
+ for (attr_count--; attr_count >= 0; attr_count--)
+ sysfs_remove_file(&function_device->dev.kobj,
+ &attrs[attr_count].attr);
+ /* If you've allocated anything, free it here. */
+ return retval;
+}
+EXPORT_SYMBOL(FN_01_init);
+
+int FN_01_detect(struct rmi_function_info *rmifninfo)
+{
+ int retval = 0;
+ struct f01_instance_data *instance_data = NULL;
+ struct rmi_F01_control *control_registers = NULL;
+ struct rmi_F01_data *data_registers = NULL;
+ struct rmi_F01_query *query_registers = NULL;
+
+ pr_debug("%s: RMI4 function $01 detect\n", __func__);
+
+ /* Set up context data. */
+ if (rmifninfo->fndata) {
+ /* detect routine should only ever be called once
+ * per rmifninfo. */
+ pr_err("%s: WTF?!? F01 instance data is already present!",
+ __func__);
+ return -EINVAL;
+ }
+ instance_data = kzalloc(sizeof(*instance_data), GFP_KERNEL);
+ if (!instance_data) {
+ pr_err("%s: Error allocating memory for F01 context data.\n",
+ __func__);
+ retval = -ENOMEM;
+ goto error_exit;
+ }
+ rmifninfo->fndata = instance_data;
+
+ query_registers = kzalloc(sizeof(*query_registers), GFP_KERNEL);
+ if (!query_registers) {
+ pr_err("%s: Error allocating memory for F01 query registers.",
+ __func__);
+ retval = -ENOMEM;
+ goto error_exit;
+ }
+ instance_data->query_registers = query_registers;
+ read_query_registers(rmifninfo);
+
+ /* TODO: size of control registers needs to be computed dynamically.
+ * See comment in rmi.h. */
+ control_registers = kzalloc(sizeof(*control_registers), GFP_KERNEL);
+ if (!control_registers) {
+ pr_err
+ ("%s: Error allocating memory for F01 control registers.\n",
+ __func__);
+ retval = -ENOMEM;
+ goto error_exit;
+ }
+ instance_data->control_registers = control_registers;
+ retval =
+ rmi_read_multiple(rmifninfo->sensor,
+ rmifninfo->function_descriptor.control_base_addr,
+ (char *)instance_data->control_registers,
+ sizeof(struct rmi_F01_control));
+ if (retval) {
+ pr_err
+ ("%s: Could not read F01 control registers at 0x%02x. "
+ "Error %d.\n", __func__,
+ rmifninfo->function_descriptor.control_base_addr,
+ retval);
+ }
+
+ /* TODO: size of data registers needs to be computed dynamically.
+ * See comment in rmi.h. */
+ data_registers = kzalloc(sizeof(*data_registers), GFP_KERNEL);
+ if (!data_registers) {
+ pr_err("%s: Error allocating memory for F01 data registers.\n",
+ __func__);
+ retval = -ENOMEM;
+ goto error_exit;
+ }
+ instance_data->data_registers = data_registers;
+
+ /* initialize original_sleepmode */
+ instance_data->original_sleepmode = RMI_SLEEP_MODE_NORMAL;
+ /* initialize of original_nosleep */
+ instance_data->original_nosleep = RMI_NO_SLEEP_DISABLE;
+
+ return retval;
+
+error_exit:
+ kfree(instance_data);
+ kfree(query_registers);
+ kfree(control_registers);
+ kfree(data_registers);
+ rmifninfo->fndata = NULL;
+ return retval;
+}
+EXPORT_SYMBOL(FN_01_detect);
+
+/**
+ * suspend handler for F01, this will be invoked in
+ * suspend routine from sensor
+ */
+int FN_01_suspend(struct rmi_function_info *rmifninfo)
+{
+ struct f01_instance_data *instance = rmifninfo->fndata;
+ /*store original sleep mode */
+ instance->original_sleepmode =
+ instance->control_registers->
+ device_control & RMI_F01_SLEEP_MODE_MASK;
+ /*store original no sleep setting */
+ instance->original_nosleep =
+ (instance->control_registers->
+ device_control & RMI_F01_NO_SLEEP_MASK) >> 2;
+ /*sleep:1 normal:0 */
+ set_sensor_sleepmode(rmifninfo, RMI_SLEEP_MODE_SENSOR_SLEEP,
+ RMI_NO_SLEEP_DISABLE);
+ return 0;
+}
+EXPORT_SYMBOL(FN_01_suspend);
+
+/*
+ * resume handler for F01, this will be invoked in
+ * resume routine from sensor
+ */
+void FN_01_resume(struct rmi_function_info *rmifninfo)
+{
+ struct f01_instance_data *instance = rmifninfo->fndata;
+ /*sleep:1 normal:0 */
+ set_sensor_sleepmode(rmifninfo, instance->original_sleepmode,
+ instance->original_nosleep);
+}
+EXPORT_SYMBOL(FN_01_resume);
+
+
+static ssize_t rmi_fn_01_productinfo_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_device *fn = dev_get_drvdata(dev);
+ struct f01_instance_data *instance_data = fn->rfi->fndata;
+
+ if (instance_data && instance_data->query_registers
+ && instance_data->query_registers->prod_info)
+ return snprintf(buf, PAGE_SIZE, "0x%02X 0x%02X\n",
+ instance_data->query_registers->prod_info[0],
+ instance_data->query_registers->prod_info[1]);
+
+ return snprintf(buf, PAGE_SIZE, "unknown\n");
+}
+
+static ssize_t rmi_fn_01_productid_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_device *fn = dev_get_drvdata(dev);
+ struct f01_instance_data *instance_data = fn->rfi->fndata;
+
+ if (instance_data && instance_data->query_registers
+ && instance_data->query_registers->prod_id)
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ instance_data->query_registers->prod_id);
+
+ return snprintf(buf, PAGE_SIZE, "unknown\n");
+}
+
+static ssize_t rmi_fn_01_manufacturer_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_device *fn = dev_get_drvdata(dev);
+ struct f01_instance_data *instance_data = fn->rfi->fndata;
+
+ if (instance_data && instance_data->query_registers)
+ return snprintf(buf, PAGE_SIZE, "0x%02X\n",
+ instance_data->query_registers->mfgid);
+
+ return snprintf(buf, PAGE_SIZE, "unknown\n");
+}
+
+static ssize_t rmi_fn_01_datecode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_device *fn = dev_get_drvdata(dev);
+ struct f01_instance_data *instance_data = fn->rfi->fndata;
+
+ if (instance_data && instance_data->query_registers
+ && instance_data->query_registers->date_code)
+ return snprintf(buf, PAGE_SIZE, "20%02u-%02u-%02u\n",
+ instance_data->query_registers->date_code[0],
+ instance_data->query_registers->date_code[1],
+ instance_data->query_registers->date_code[2]);
+
+ return snprintf(buf, PAGE_SIZE, "unknown\n");
+}
+
+static ssize_t rmi_fn_01_reportrate_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_device *fn = dev_get_drvdata(dev);
+ struct f01_instance_data *instance_data = fn->rfi->fndata;
+
+ if (instance_data && instance_data->query_registers
+ && instance_data->query_registers->date_code)
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ instance_data->nonstandard_report_rate);
+
+ return snprintf(buf, PAGE_SIZE, "unknown\n");
+}
+
+static ssize_t rmi_fn_01_reportrate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_device *fn = dev_get_drvdata(dev);
+ struct f01_instance_data *instance_data = fn->rfi->fndata;
+ unsigned int new_rate;
+ int retval;
+
+ if (sscanf(buf, "%u", &new_rate) != 1)
+ return -EINVAL;
+ if (new_rate < 0 || new_rate > 1)
+ return -EINVAL;
+ instance_data->nonstandard_report_rate = new_rate;
+
+ retval = set_report_rate(fn->rfi, new_rate);
+ if (retval < 0) {
+ pr_err("%s: failed to set report rate bit, error = %d.",
+ __func__, retval);
+ return retval;
+ }
+
+ return count;
+}
+
+static ssize_t rmi_fn_01_reset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_device *fn = dev_get_drvdata(dev);
+ unsigned int reset;
+ int retval;
+
+ if (sscanf(buf, "%u", &reset) != 1)
+ return -EINVAL;
+ if (reset < 0 || reset > 1)
+ return -EINVAL;
+
+ /* Per spec, 0 has no effect, so we skip it entirely. */
+ if (reset) {
+ retval = rmi_set_bits(fn->sensor,
+ fn->rfi->function_descriptor.command_base_addr,
+ F01_RESET);
+ if (retval < 0) {
+ dev_err(dev, "%s: failed to issue reset command, "
+ "error = %d.", __func__, retval);
+ return retval;
+ }
+ }
+
+ return count;
+}
+
+static ssize_t rmi_fn_01_serialnumber_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_device *fn = dev_get_drvdata(dev);
+ struct f01_instance_data *instance_data = fn->rfi->fndata;
+
+ if (instance_data && instance_data->query_registers)
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ instance_data->query_registers->serial_num);
+
+ return snprintf(buf, PAGE_SIZE, "unknown\n");
+}
+
+static ssize_t rmi_fn_01_testerid_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_device *fn = dev_get_drvdata(dev);
+ struct f01_instance_data *instance_data = fn->rfi->fndata;
+
+ if (instance_data && instance_data->query_registers)
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ instance_data->query_registers->tester_id);
+
+ return snprintf(buf, PAGE_SIZE, "unknown\n");
+}
+
+/*
+ * update content of device control into hardware and sync the status
+ * @param control_base_address base address for F01 device control
+ * @param mask mask of the field that will be changed
+ * @param field_target_value target value for the field
+ */
+static int update_device_control(struct rmi_sensor_driver *sensor,
+ struct rmi_F01_control *control_register,
+ unsigned char control_base_address,
+ unsigned char mask,
+ unsigned char field_target_value)
+{
+
+ unsigned char control_target_value =
+ control_register->device_control & (~mask);
+ field_target_value &= mask;
+ control_target_value |= field_target_value;
+ if (control_register->device_control != control_target_value) {
+ /* update device_control*/
+ control_register->device_control = control_target_value;
+ return rmi_set_bit_field(sensor, control_base_address,
+ mask, field_target_value);
+ }
+ return 0;
+}
+
+/*
+ * shows the status bit provided by device_control
+ * @param mask mask to retrieve the information ex. 0x3 for bit 0 and bit 1
+ * @param offset the offset of the information ex. 0: bit 0 1: bit 1
+ */
+static ssize_t device_control_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf,
+ unsigned char mask,
+ unsigned char offset)
+{
+ struct rmi_function_device *fn = dev_get_drvdata(dev);
+ struct f01_instance_data *instance_data = fn->rfi->fndata;
+ unsigned char controlRegister = 0;
+
+ if (!instance_data || !instance_data->control_registers)
+ return snprintf(buf, PAGE_SIZE, "unknown\n");
+
+ controlRegister =
+ (instance_data->control_registers->device_control & mask)
+ >> offset;
+ return snprintf(buf, PAGE_SIZE, "%u\n", controlRegister);
+}
+
+/*
+ * store the value into device_control
+ * @param mask mask to retrieve the information ex. 0x3 for bit 1 and bit 1
+ * @param offset the offset of the information ex. 0: bit 0 1: bit 1
+ */
+static ssize_t device_control_store(struct device *dev,
+ struct device_attribute *attr,
+ unsigned int new_value,
+ unsigned char mask,
+ unsigned char offset)
+{
+ struct rmi_function_device *fn = dev_get_drvdata(dev);
+ struct f01_instance_data *instance_data = fn->rfi->fndata;
+ unsigned char target_setting;
+ int retval;
+
+ target_setting = (unsigned char) new_value;
+ if (!instance_data || !instance_data->control_registers)
+ return -EINVAL;
+
+ /*update hardware and device_control status*/
+ retval = update_device_control(fn->sensor,
+ instance_data->control_registers,
+ fn->rfi->function_descriptor.control_base_addr,
+ mask, (target_setting<<offset));
+
+ if (retval < 0)
+ dev_err(dev, "%s: failed to write control register, "
+ "error = %d.", __func__, retval);
+
+ return retval;
+}
+
+/*
+ * show status for sleep 0:normal 1:sleep 2,3: reserved
+ */
+static ssize_t rmi_fn_01_sleepmode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return device_control_show(dev, attr, buf,
+ RMI_F01_SLEEP_MODE_MASK, RMI_F01_SLEEP_MODE_OFFSET);
+}
+
+/*
+ * setup status for sleep mode 0:normal 1:sleep 2,3: reserved
+ */
+static ssize_t rmi_fn_01_sleepmode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ unsigned long new_value;
+ int retval;
+
+ retval = strict_strtoul(buf, 10, &new_value);
+ if (retval < 0 || !RMI_IS_VALID_SLEEPMODE(new_value)) {
+ dev_err(dev, "%s: Invalid sleep mode %s.", __func__, buf);
+ return -EINVAL;
+ }
+
+ retval = device_control_store(dev, attr, (unsigned int) new_value,
+ RMI_F01_SLEEP_MODE_MASK, RMI_F01_SLEEP_MODE_OFFSET);
+ if (!retval)
+ retval = count;
+ return retval;
+}
+
+/*
+ * show current setting of no sleep, 0:disable 1:enable
+ */
+static ssize_t rmi_fn_01_nosleep_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return device_control_show(dev, attr, buf,
+ RMI_F01_NO_SLEEP_MASK, RMI_F01_NO_SLEEP_OFFSET);
+}
+
+/*
+ * setup no sleep, 0:disable 1:enable
+ */
+static ssize_t rmi_fn_01_nosleep_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ unsigned long new_value;
+ int retval;
+
+ retval = strict_strtoul(buf, 10, &new_value);
+ if (retval < 0 || new_value < 0 || new_value > 1) {
+ dev_err(dev, "%s: Invalid no sleep setting %s.", __func__, buf);
+ return -EINVAL;
+ }
+
+ retval = (device_control_store(dev, attr, (int) new_value,
+ RMI_F01_NO_SLEEP_MASK, RMI_F01_NO_SLEEP_OFFSET));
+ if (!retval)
+ retval = count;
+ return retval;
+}
+
+/* setup sleep mode via F01. we will store original_mode before sleepmode
+ * and nosleep setting is changed.
+ */
+static void set_sensor_sleepmode(struct rmi_function_info *functionInfo,
+ unsigned char sleepmode,
+ unsigned char nosleep)
+{
+ struct f01_instance_data *instance_data =
+ functionInfo->function_device->rfi->fndata;
+
+ if (instance_data && instance_data->control_registers) {
+ /*update hardware and device_control status*/
+ update_device_control(functionInfo->sensor,
+ instance_data->control_registers,
+ functionInfo->function_descriptor.control_base_addr,
+ (RMI_F01_SLEEP_MODE_MASK | RMI_F01_NO_SLEEP_MASK),
+ (sleepmode | (nosleep << 2))
+ );
+ }
+}
--
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