aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/input/touchscreen/rmi4/rmi_f09.c
blob: 0ec980d7db074dc7fb34b243bd66e97b40ccc658 (plain) (tree)









































































































































































































































































































                                                                                
/*
 * Copyright (c) 2011 Synaptics Incorporated
 * Copyright (c) 2011 Unixphere
 *
 * 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/rmi.h>
#include <linux/input.h>
#include <linux/slab.h>

#define QUERY_BASE_INDEX 1
#define MAX_LEN 256

/* data specific to fn $09 that needs to be kept around */
struct f09_query {
	u8 Limit_Register_Count;
	union {
		struct {
			u8 Result_Register_Count:3;
			u8 Reserved:3;
			u8 InternalLimits:1;
			u8 HostTestEn:1;
		};
		u8 f09_bist_query1;
	};
};

struct f09_control {
	/* test1 */
	u8 Test1LimitLo;
	u8 Test1LimitHi;
	u8 Test1LimitDiff;
	/* test2 */
	u8 Test2LimitLo;
	u8 Test2LimitHi;
	u8 Test2LimitDiff;
};

struct f09_data {
	u8 TestNumberControl;
	u8 Overall_BIST_Result;
	u8 TestResult1;
	u8 TestResult2;
	u8 Transmitter_Number;

	union {
		struct {
			u8 Receiver_Number:6;
			u8 Limit_Failure_Code:2;
		};
		u8 f09_bist_data2;
	};
};

struct f09_cmd {
	union {
		struct {
			u8 RunBIST:1;
		};
		u8 f09_bist_cmd0;
	};
};

struct rmi_fn_09_data {
	struct f09_query query;
};

static ssize_t rmi_f09_Limit_Register_Count_show(struct device *dev,
				      struct device_attribute *attr, char *buf);

static ssize_t rmi_f09_HostTestEn_show(struct device *dev,
				      struct device_attribute *attr, char *buf);

static ssize_t rmi_f09_HostTestEn_store(struct device *dev,
				      struct device_attribute *attr,
					char *buf, size_t count);

static ssize_t rmi_f09_InternalLimits_show(struct device *dev,
				      struct device_attribute *attr, char *buf);

static ssize_t rmi_f09_Result_Register_Count_show(struct device *dev,
				      struct device_attribute *attr, char *buf);

static ssize_t rmi_f09_Overall_BIST_Result_show(struct device *dev,
				      struct device_attribute *attr, char *buf);

static ssize_t rmi_f09_Overall_BIST_Result_store(struct device *dev,
				       struct device_attribute *attr,
				       const char *buf, size_t count);

static struct device_attribute attrs[] = {
	__ATTR(Limit_Register_Count, RMI_RO_ATTR,
	       rmi_f09_Limit_Register_Count_show, rmi_store_error),
	__ATTR(HostTestEn, RMI_RW_ATTR,
	       rmi_f09_HostTestEn_show, rmi_f09_HostTestEn_store),
	__ATTR(InternalLimits, RMI_RO_ATTR,
	       rmi_f09_Limit_Register_Count_show, rmi_store_error),
	__ATTR(Result_Register_Count, RMI_RO_ATTR,
	       rmi_f09_Result_Register_Count_show, rmi_store_error),
};

static int rmi_f09_init(struct rmi_function_container *fc)
{
	struct rmi_device *rmi_dev = fc->rmi_dev;
	struct rmi_device_platform_data *pdata;
	struct rmi_fn_09_data  *f09;
	u8 query_base_addr;
	int rc;
	int i;
	int attr_count = 0;
	int retval = 0;

	dev_info(&fc->dev, "Intializing F09 values.");

	f09 = kzalloc(sizeof(struct rmi_fn_09_data), GFP_KERNEL);
	if (!f09) {
		dev_err(&fc->dev, "Failed to allocate rmi_fn_09_data.\n");
		retval = -ENOMEM;
		goto error_exit;
	}
	fc->data = f09;

	pdata = to_rmi_platform_data(rmi_dev);
	query_base_addr = fc->fd.query_base_addr;

	/* initial all default values for f09 query here */
	rc = rmi_read_block(rmi_dev, query_base_addr,
		(u8 *)&f09->query, sizeof(f09->query));
	if (rc < 0) {
		dev_err(&fc->dev, "Failed to read query register."
			" from 0x%04x\n", query_base_addr);
		goto error_exit;
	}

	dev_dbg(&fc->dev, "Creating sysfs files.");
	/* 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);
			retval = -ENODEV;
			goto error_exit;
		}
	}
	return 0;

error_exit:
	dev_err(&fc->dev, "An error occured in F09 init!\n");
	for (attr_count--; attr_count >= 0; attr_count--)
		sysfs_remove_file(&fc->dev.kobj,
				  &attrs[attr_count].attr);
	kfree(f09);
	return retval;
}

static void rmi_f09_remove(struct rmi_function_container *fc)
{
	struct rmi_fn_09_data *data = fc->data;
	if (data) {
		kfree(data->query.Limit_Register_Count);
		kfree(data->query.f09_bist_query1);
	}
	kfree(fc->data);
}

static struct rmi_function_handler function_handler = {
	.func = 0x09,
	.init = rmi_f09_init,
	.remove = rmi_f09_remove
};

static int __init rmi_f09_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_f09_module_exit(void)
{
	rmi_unregister_function_driver(&function_handler);
}


static ssize_t rmi_f09_Limit_Register_Count_show(struct device *dev,
					struct device_attribute *attr,
					char *buf)
{
	struct rmi_function_container *fc;
	struct rmi_fn_09_data *data;

	fc = to_rmi_function_container(dev);
	data = fc->data;
	return snprintf(buf, PAGE_SIZE, "%u\n",
			data->query.Limit_Register_Count);
}

static ssize_t rmi_f09_HostTestEn_show(struct device *dev,
					struct device_attribute *attr,
					char *buf)
{
	struct rmi_function_container *fc;
	struct rmi_fn_09_data *data;

	fc = to_rmi_function_container(dev);
	data = fc->data;
	return snprintf(buf, PAGE_SIZE, "%u\n",
			data->query.HostTestEn);
}

static ssize_t rmi_f09_HostTestEn_store(struct device *dev,
				      struct device_attribute *attr,
					char *buf, size_t count)
{
	struct rmi_function_container *fc;
	struct rmi_fn_09_data *data;
	unsigned int new_value;
	int result;

	fc = to_rmi_function_container(dev);
	data = fc->data;
	if (sscanf(buf, "%u", &new_value) != 1) {
		dev_err(dev,
		"%s: Error - HostTestEn_store has an "
		"invalid len.\n",
		__func__);
		return -EINVAL;
	}

	if (new_value < 0 || new_value > 1) {
		dev_err(dev, "%s: Invalid HostTestEn bit %s.", __func__, buf);
		return -EINVAL;
	}
	data->query.HostTestEn = new_value;
	result = rmi_write(fc->rmi_dev, fc->fd.query_base_addr,
		data->query.HostTestEn);
	if (result < 0) {
		dev_err(dev, "%s : Could not write HostTestEn_store to 0x%x\n",
				__func__, fc->fd.query_base_addr);
		return result;
	}

	return count;

}

static ssize_t rmi_f09_InternalLimits_show(struct device *dev,
					struct device_attribute *attr,
					char *buf)
{
	struct rmi_function_container *fc;
	struct rmi_fn_09_data *data;

	fc = to_rmi_function_container(dev);
	data = fc->data;
	return snprintf(buf, PAGE_SIZE, "%u\n",
			data->query.InternalLimits);
}

static ssize_t rmi_f09_Result_Register_Count_show(struct device *dev,
					struct device_attribute *attr,
					char *buf)
{
	struct rmi_function_container *fc;
	struct rmi_fn_09_data *data;

	fc = to_rmi_function_container(dev);
	data = fc->data;
	return snprintf(buf, PAGE_SIZE, "%u\n",
			data->query.Result_Register_Count);
}

module_init(rmi_f09_module_init);
module_exit(rmi_f09_module_exit);

MODULE_AUTHOR("Allie Xiong <axiong@Synaptics.com>");
MODULE_DESCRIPTION("RMI F09 module");
MODULE_LICENSE("GPL");