/* * 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 #include #include #include #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 "); MODULE_DESCRIPTION("RMI F09 module"); MODULE_LICENSE("GPL");