diff mbox

[RFC,17/17] input: RMI4 F54 analog data reporting

Message ID 1345241877-16200-18-git-send-email-cheiny@synaptics.com (mailing list archive)
State New, archived
Headers show

Commit Message

Christopher Heiny Aug. 17, 2012, 10:17 p.m. UTC
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>

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

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

---

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

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Linus Walleij Aug. 27, 2012, 11:01 p.m. UTC | #1
On Fri, Aug 17, 2012 at 3:17 PM, Christopher Heiny <cheiny@synaptics.com> wrote:

Verbose commit message.

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

> +#include <linux/hrtimer.h>
> +#include <linux/kernel.h>
> +#include <linux/rmi.h>
> +#include <linux/slab.h>
> +#include <linux/version.h>
> +#include <linux/delay.h>

This is not using the existing in-kernel framework for ADC (which I think is
what you're doing), in this case use the IIO subsystem so consult
drivers/iio/adc and be inspired.

Just exposing a set of weird sysfs files to userspace is not proper.

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Christopher Heiny Sept. 5, 2012, 12:38 a.m. UTC | #2
On 08/27/2012 04:01 PM, Linus Walleij wrote:
>
>> >Signed-off-by: Christopher Heiny<cheiny@synaptics.com>
>> >+#include <linux/hrtimer.h>
>> >+#include <linux/kernel.h>
>> >+#include <linux/rmi.h>
>> >+#include <linux/slab.h>
>> >+#include <linux/version.h>
>> >+#include <linux/delay.h>
> This is not using the existing in-kernel framework for ADC (which I think is
> what you're doing), in this case use the IIO subsystem so consult
> drivers/iio/adc and be inspired.
>
> Just exposing a set of weird sysfs files to userspace is not proper.

I don't think the Industrial I/O ADC framework is appropriate for this. 
  F54 isn't intended to provide access to a general purpose ADC feature, 
but rather for diagnostic access to the touchscreen's capacitive sense 
features, which results in a fairly weird (as you noticed) interface. 
Trying to hammer on it to fit into iio/adc would require a lot of 
effort, and wind up making it look like something it really isn't.

However, it makes sense to move this whole interface to debugfs, given 
its intended model of use.


--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c
new file mode 100644
index 0000000..9c19799
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f54.c
@@ -0,0 +1,2247 @@ 
+/*
+ * 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/hrtimer.h>
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/delay.h>
+
+#define FUNCTION_DATA rmi_fn_54_data
+#define FNUM 54
+
+#include "rmi_driver.h"
+
+/* Set this to 1 for raw hex dump of returned data. */
+#define RAW_HEX 0
+/* Set this to 1 for human readable dump of returned data. */
+#define HUMAN_READABLE 0
+/* The watchdog timer can be useful when debugging certain firmware related
+ * issues.
+ */
+#define F54_WATCHDOG 1
+
+/* define fn $54 commands */
+#define GET_REPORT                1
+#define FORCE_CAL                 2
+
+#define NO_AUTO_CAL_MASK 1
+/* status */
+#define BUSY 1
+#define IDLE 0
+
+/* Offsets for data */
+#define RMI_F54_REPORT_DATA_OFFSET	3
+#define RMI_F54_FIFO_OFFSET		1
+#define RMI_F54_NUM_TX_OFFSET		1
+#define RMI_F54_NUM_RX_OFFSET		0
+
+/* Fixed sizes of reports */
+#define RMI_54_FULL_RAW_CAP_MIN_MAX_SIZE 4
+#define RMI_54_HIGH_RESISTANCE_SIZE 6
+
+/* definitions for F54 Query Registers */
+union f54_ad_query {
+	struct {
+		/* query 0 */
+		u8 num_of_rx_electrodes;
+
+		/* query 1 */
+		u8 num_of_tx_electrodes;
+
+		/* query2 */
+		u8 f54_ad_query2_b0__1:2;
+		u8 has_baseline:1;
+		u8 has_image8:1;
+		u8 f54_ad_query2_b4__5:2;
+		u8 has_image16:1;
+		u8 f54_ad_query2_b7:1;
+
+		/* query 3.0 and 3.1 */
+		u16 clock_rate;
+
+		/* query 4 */
+		u8 touch_controller_family;
+
+		/* query 5 */
+		u8 has_pixel_touch_threshold_adjustment:1;
+		u8 f54_ad_query5_b1__7:7;
+
+		/* query 6 */
+		u8 has_sensor_assignment:1;
+		u8 has_interference_metric:1;
+		u8 has_sense_frequency_control:1;
+		u8 has_firmware_noise_mitigation:1;
+		u8 f54_ad_query6_b4:1;
+		u8 has_two_byte_report_rate:1;
+		u8 has_one_byte_report_rate:1;
+		u8 has_relaxation_control:1;
+
+		/* query 7 */
+		u8 curve_compensation_mode:2;
+		u8 f54_ad_query7_b2__7:6;
+
+		/* query 8 */
+		u8 f54_ad_query2_b0:1;
+		u8 has_iir_filter:1;
+		u8 has_cmn_removal:1;
+		u8 has_cmn_maximum:1;
+		u8 has_touch_hysteresis:1;
+		u8 has_edge_compensation:1;
+		u8 has_per_frequency_noise_control:1;
+		u8 f54_ad_query8_b7:1;
+
+		u8 f54_ad_query9;
+		u8 f54_ad_query10;
+
+		/* query 11 */
+		u8 f54_ad_query11_b0__6:7;
+		u8 has_query_15:1;
+
+		/* query 12 */
+		u8 number_of_sensing_frequencies:4;
+		u8 f54_ad_query12_b4__7:4;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[14];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+/* And now for the very large amount of control registers */
+
+/* Ctrl registers */
+
+union f54_ad_control_0 {
+	/* control 0 */
+	struct {
+		u8 no_relax:1;
+		u8 no_scan:1;
+		u8 f54_ad_ctrl0_b2__7:6;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_1 {
+	/* control 1 */
+	struct {
+		/* control 1 */
+		u8 bursts_per_cluster:4;
+		u8 f54_ad_ctrl1_b4__7:4;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_2 {
+	/* control 2 */
+	struct {
+		u16 saturation_cap;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[2];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_3 {
+	/* control 3 */
+	struct {
+		u16 pixel_touch_threshold;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[2];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_4__6 {
+	struct {
+		/* control 4 */
+		u8 rx_feedback_cap:2;
+		u8 f54_ad_ctrl4_b2__7:6;
+
+		/* control 5 */
+		u8 low_ref_cap:2;
+		u8 low_ref_feedback_cap:2;
+		u8 low_ref_polarity:1;
+		u8 f54_ad_ctrl5_b5__7:3;
+
+		/* control 6 */
+		u8 high_ref_cap:2;
+		u8 high_ref_feedback_cap:2;
+		u8 high_ref_polarity:1;
+		u8 f54_ad_ctrl6_b5__7:3;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[3];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_7 {
+	struct {
+		/* control 7 */
+		u8 cbc_cap:2;
+		u8 cbc_polarity:2;
+		u8 cbc_tx_carrier_selection:1;
+		u8 f54_ad_ctrl6_b5__7:3;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_8__9 {
+	struct {
+		/* control 8 */
+		u16 integration_duration:10;
+		u16 f54_ad_ctrl8_b10__15:6;
+		/* control 9 */
+		u8 reset_duration;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[3];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_10 {
+	struct {
+		/* control 10 */
+		u8 noise_sensing_bursts_per_image:4;
+		u8 f54_ad_ctrl10_b4__7:4;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_11 {
+	struct {
+		/* control 11 */
+		u8 reserved;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_12__13 {
+	struct {
+		/* control 12 */
+		u8 slow_relaxation_rate;
+
+		/* control 13 */
+		u8 fast_relaxation_rate;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[2];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_14 {
+	struct {
+		/* control 14 */
+			u8 rxs_on_xaxis:1;
+			u8 curve_comp_on_txs:1;
+			u8 f54_ad_ctrl14b2__7:6;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+struct f54_ad_control_15n {
+	/*Control 15.* */
+	u8 sensor_rx_assignment;
+};
+
+struct f54_ad_control_15 {
+		struct f54_ad_control_15n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f54_ad_control_16n {
+	/*Control 16.* */
+	u8 sensor_tx_assignment;
+};
+
+struct f54_ad_control_16 {
+		struct f54_ad_control_16n *regs;
+		u16 address;
+		u8 length;
+};
+
+
+/* control 17 */
+struct f54_ad_control_17n {
+	u8 burst_countb10__8:3;
+	u8 disable:1;
+	u8 f54_ad_ctrlb4:1;
+	u8 filter_bandwidth:3;
+};
+
+struct f54_ad_control_17 {
+		struct f54_ad_control_17n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f54_ad_control_18n {
+	/*Control 18.* */
+	u8 burst_countb7__0n;
+};
+
+struct f54_ad_control_18 {
+		struct f54_ad_control_18n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f54_ad_control_19n {
+	/*Control 19.* */
+	u8 stretch_duration;
+};
+
+struct f54_ad_control_19 {
+		struct f54_ad_control_19n *regs;
+		u16 address;
+		u8 length;
+};
+
+union f54_ad_control_20 {
+	struct {
+		/* control 20 */
+		u8 disable_noise_mitigation:1;
+		u8 f54_ad_ctrl20b2__7:7;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_21 {
+	struct {
+		/* control 21 */
+		u16 freq_shift_noise_threshold;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[2];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_22__26 {
+	struct {
+		/* control 22 */
+		/* u8 noise_density_threshold; */
+		u8 f54_ad_ctrl22;
+
+		/* control 23 */
+		u16 medium_noise_threshold;
+
+		/* control 24 */
+		u16 high_noise_threshold;
+
+		/* control 25 */
+		u8 noise_density;
+
+		/* control 26 */
+		u8 frame_count;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[7];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_27 {
+	struct {
+		/* control 27 */
+		u8 iir_filter_coef;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_28 {
+	struct {
+		/* control 28 */
+		u16 quiet_threshold;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[2];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+
+union f54_ad_control_29 {
+	struct {
+		/* control 29 */
+		u8 f54_ad_ctrl20b0__6:7;
+		u8 cmn_filter_disable:1;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_30 {
+	struct {
+		/* control 30 */
+		u8 cmn_filter_max;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_31 {
+	struct {
+		/* control 31 */
+		u8 touch_hysteresis;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_32__35 {
+	struct {
+		/* control 32 */
+		u16 rx_low_edge_comp;
+
+		/* control 33 */
+		u16 rx_high_edge_comp;
+
+		/* control 34 */
+		u16 tx_low_edge_comp;
+
+		/* control 35 */
+		u16 tx_high_edge_comp;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[8];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+struct f54_ad_control_36n {
+	/*Control 36.* */
+	u8 axis1_comp;
+};
+
+struct f54_ad_control_36 {
+		struct f54_ad_control_36n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f54_ad_control_37n {
+	/*Control 37.* */
+	u8 axis2_comp;
+};
+
+struct f54_ad_control_37 {
+		struct f54_ad_control_37n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f54_ad_control_38n {
+	/*Control 38.* */
+	u8 noise_control_1;
+};
+
+struct f54_ad_control_38 {
+		struct f54_ad_control_38n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f54_ad_control_39n {
+	/*Control 39.* */
+	u8 noise_control_2;
+};
+
+struct f54_ad_control_39 {
+		struct f54_ad_control_39n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f54_ad_control_40n {
+	/*Control 40.* */
+	u8 noise_control_3;
+};
+
+struct f54_ad_control_40 {
+		struct f54_ad_control_40n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f54_ad_control {
+	union f54_ad_control_0 *reg_0;
+	union f54_ad_control_1 *reg_1;
+	union f54_ad_control_2 *reg_2;
+	union f54_ad_control_3 *reg_3;
+	union f54_ad_control_4__6 *reg_4__6;
+	union f54_ad_control_7 *reg_7;
+	union f54_ad_control_8__9 *reg_8__9;
+	union f54_ad_control_10 *reg_10;
+	union f54_ad_control_11 *reg_11;
+	union f54_ad_control_12__13 *reg_12__13;
+	union f54_ad_control_14 *reg_14;
+	/* control 15 */
+	struct f54_ad_control_15 *reg_15;
+	/* control 16 */
+	struct f54_ad_control_16 *reg_16;
+
+	/* This register is n repetitions of f54_ad_control_17 */
+	struct f54_ad_control_17 *reg_17;
+
+	/* control 18 */
+	struct f54_ad_control_18 *reg_18;
+
+	/* control 19 */
+	struct f54_ad_control_19 *reg_19;
+
+	union f54_ad_control_20 *reg_20;
+	union f54_ad_control_21 *reg_21;
+	union f54_ad_control_22__26 *reg_22__26;
+	union f54_ad_control_27 *reg_27;
+	union f54_ad_control_28 *reg_28;
+	union f54_ad_control_29 *reg_29;
+	union f54_ad_control_30 *reg_30;
+	union f54_ad_control_31 *reg_31;
+	union f54_ad_control_32__35 *reg_32__35;
+	/* control 36 */
+	struct f54_ad_control_36 *reg_36;
+
+	/* control 37 */
+	struct f54_ad_control_37 *reg_37;
+
+	/* control 38 */
+	struct f54_ad_control_38 *reg_38;
+
+	/* control 39 */
+	struct f54_ad_control_39 *reg_39;
+
+	/* control 40 */
+	struct f54_ad_control_40 *reg_40;
+};
+
+/* define report types */
+enum f54_report_types {
+	F54_8BIT_IMAGE = 1,
+	F54_16BIT_IMAGE = 2,
+	F54_RAW_16BIT_IMAGE = 3,
+	F54_HIGH_RESISTANCE = 4,
+	F54_TX_TO_TX_SHORT = 5,
+	F54_RX_TO_RX1 = 7,
+	F54_TRUE_BASELINE = 9,
+	F54_FULL_RAW_CAP_MIN_MAX = 13,
+	F54_RX_OPENS1 = 14,
+	F54_TX_OPEN = 15,
+	F54_TX_TO_GROUND = 16,
+	F54_RX_TO_RX2 = 17,
+	F54_RX_OPENS2 = 18,
+	F54_FULL_RAW_CAP = 19,
+	F54_FULL_RAW_CAP_RX_COUPLING_COMP = 20
+};
+
+/* sysfs functions */
+show_store_union_struct_prototype(report_type)
+
+store_union_struct_prototype(get_report)
+
+store_union_struct_prototype(force_cal)
+
+show_union_struct_prototype(status)
+
+static ssize_t rmi_fn_54_data_read(struct file *data_file, struct kobject *kobj,
+					struct bin_attribute *attributes,
+					char *buf, loff_t pos, size_t count);
+
+show_union_struct_prototype(num_of_rx_electrodes)
+show_union_struct_prototype(num_of_tx_electrodes)
+show_union_struct_prototype(has_image16)
+show_union_struct_prototype(has_image8)
+show_union_struct_prototype(has_baseline)
+show_union_struct_prototype(clock_rate)
+show_union_struct_prototype(touch_controller_family)
+show_union_struct_prototype(has_pixel_touch_threshold_adjustment)
+show_union_struct_prototype(has_sensor_assignment)
+show_union_struct_prototype(has_interference_metric)
+show_union_struct_prototype(has_sense_frequency_control)
+show_union_struct_prototype(has_firmware_noise_mitigation)
+show_union_struct_prototype(has_two_byte_report_rate)
+show_union_struct_prototype(has_one_byte_report_rate)
+show_union_struct_prototype(has_relaxation_control)
+show_union_struct_prototype(curve_compensation_mode)
+show_union_struct_prototype(has_iir_filter)
+show_union_struct_prototype(has_cmn_removal)
+show_union_struct_prototype(has_cmn_maximum)
+show_union_struct_prototype(has_touch_hysteresis)
+show_union_struct_prototype(has_edge_compensation)
+show_union_struct_prototype(has_per_frequency_noise_control)
+show_union_struct_prototype(number_of_sensing_frequencies)
+show_store_union_struct_prototype(no_auto_cal)
+show_store_union_struct_prototype(fifoindex)
+
+/* Repeated Control Registers */
+show_union_struct_prototype(sensor_rx_assignment)
+show_union_struct_prototype(sensor_tx_assignment)
+show_union_struct_prototype(filter_bandwidth)
+show_union_struct_prototype(disable)
+show_union_struct_prototype(burst_count)
+show_union_struct_prototype(stretch_duration)
+show_store_union_struct_prototype(axis1_comp)
+show_store_union_struct_prototype(axis2_comp)
+show_union_struct_prototype(noise_control_1)
+show_union_struct_prototype(noise_control_2)
+show_union_struct_prototype(noise_control_3)
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+show_store_union_struct_prototype(no_relax)
+show_store_union_struct_prototype(no_scan)
+show_store_union_struct_prototype(bursts_per_cluster)
+show_store_union_struct_prototype(saturation_cap)
+show_store_union_struct_prototype(pixel_touch_threshold)
+show_store_union_struct_prototype(rx_feedback_cap)
+show_store_union_struct_prototype(low_ref_cap)
+show_store_union_struct_prototype(low_ref_feedback_cap)
+show_store_union_struct_prototype(low_ref_polarity)
+show_store_union_struct_prototype(high_ref_cap)
+show_store_union_struct_prototype(high_ref_feedback_cap)
+show_store_union_struct_prototype(high_ref_polarity)
+show_store_union_struct_prototype(cbc_cap)
+show_store_union_struct_prototype(cbc_polarity)
+show_store_union_struct_prototype(cbc_tx_carrier_selection)
+show_store_union_struct_prototype(integration_duration)
+show_store_union_struct_prototype(reset_duration)
+show_store_union_struct_prototype(noise_sensing_bursts_per_image)
+show_store_union_struct_prototype(slow_relaxation_rate)
+show_store_union_struct_prototype(fast_relaxation_rate)
+show_store_union_struct_prototype(rxs_on_xaxis)
+show_store_union_struct_prototype(curve_comp_on_txs)
+show_store_union_struct_prototype(disable_noise_mitigation)
+show_store_union_struct_prototype(freq_shift_noise_threshold)
+/*show_store_union_struct_prototype(noise_density_threshold)*/
+show_store_union_struct_prototype(medium_noise_threshold)
+show_store_union_struct_prototype(high_noise_threshold)
+show_store_union_struct_prototype(noise_density)
+show_store_union_struct_prototype(frame_count)
+show_store_union_struct_prototype(iir_filter_coef)
+show_store_union_struct_prototype(quiet_threshold)
+show_store_union_struct_prototype(cmn_filter_disable)
+show_store_union_struct_prototype(cmn_filter_max)
+show_store_union_struct_prototype(touch_hysteresis)
+show_store_union_struct_prototype(rx_low_edge_comp)
+show_store_union_struct_prototype(rx_high_edge_comp)
+show_store_union_struct_prototype(tx_low_edge_comp)
+show_store_union_struct_prototype(tx_high_edge_comp)
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+static struct attribute *attrs[] = {
+	attrify(report_type),
+	attrify(get_report),
+	attrify(force_cal),
+	attrify(status),
+	attrify(num_of_rx_electrodes),
+	attrify(num_of_tx_electrodes),
+	attrify(has_image16),
+	attrify(has_image8),
+	attrify(has_baseline),
+	attrify(clock_rate),
+	attrify(touch_controller_family),
+	attrify(has_pixel_touch_threshold_adjustment),
+	attrify(has_sensor_assignment),
+	attrify(has_interference_metric),
+	attrify(has_sense_frequency_control),
+	attrify(has_firmware_noise_mitigation),
+	attrify(has_two_byte_report_rate),
+	attrify(has_one_byte_report_rate),
+	attrify(has_relaxation_control),
+	attrify(curve_compensation_mode),
+	attrify(has_iir_filter),
+	attrify(has_cmn_removal),
+	attrify(has_cmn_maximum),
+	attrify(has_touch_hysteresis),
+	attrify(has_edge_compensation),
+	attrify(has_per_frequency_noise_control),
+	attrify(number_of_sensing_frequencies),
+	attrify(no_auto_cal),
+	attrify(fifoindex),
+	NULL
+};
+
+static struct attribute_group attrs_query = GROUP(attrs);
+
+struct bin_attribute dev_rep_data = {
+	.attr = {
+		 .name = "rep_data",
+		 .mode = RMI_RO_ATTR},
+	.size = 0,
+	.read = rmi_fn_54_data_read,
+};
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+static struct attribute *attrs_reg_0[] = {
+	attrify(no_relax),
+	attrify(no_scan),
+	NULL
+};
+
+static struct attribute *attrs_reg_1[] = {
+	attrify(bursts_per_cluster),
+	NULL
+};
+
+static struct attribute *attrs_reg_2[] = {
+	attrify(saturation_cap),
+	NULL
+};
+
+static struct attribute *attrs_reg_3[] = {
+	attrify(pixel_touch_threshold),
+	NULL
+};
+
+static struct attribute *attrs_reg_4__6[] = {
+	attrify(rx_feedback_cap),
+	attrify(low_ref_cap),
+	attrify(low_ref_feedback_cap),
+	attrify(low_ref_polarity),
+	attrify(high_ref_cap),
+	attrify(high_ref_feedback_cap),
+	attrify(high_ref_polarity),
+	NULL
+};
+
+static struct attribute *attrs_reg_7[] = {
+	attrify(cbc_cap),
+	attrify(cbc_polarity),
+	attrify(cbc_tx_carrier_selection),
+	NULL
+};
+
+static struct attribute *attrs_reg_8__9[] = {
+	attrify(integration_duration),
+	attrify(reset_duration),
+	NULL
+};
+
+static struct attribute *attrs_reg_10[] = {
+	attrify(noise_sensing_bursts_per_image),
+	NULL
+};
+
+static struct attribute *attrs_reg_12__13[] = {
+	attrify(slow_relaxation_rate),
+	attrify(fast_relaxation_rate),
+	NULL
+};
+
+static struct attribute *attrs_reg_14__16[] = {
+	attrify(rxs_on_xaxis),
+	attrify(curve_comp_on_txs),
+	attrify(sensor_rx_assignment),
+	attrify(sensor_tx_assignment),
+	NULL
+};
+
+static struct attribute *attrs_reg_17__19[] = {
+	attrify(filter_bandwidth),
+	attrify(disable),
+	attrify(burst_count),
+	attrify(stretch_duration),
+	NULL
+};
+
+static struct attribute *attrs_reg_20[] = {
+	attrify(disable_noise_mitigation),
+	NULL
+};
+
+static struct attribute *attrs_reg_21[] = {
+	attrify(freq_shift_noise_threshold),
+	NULL
+};
+
+static struct attribute *attrs_reg_22__26[] = {
+	/*attrify(noise_density_threshold),*/
+	attrify(medium_noise_threshold),
+	attrify(high_noise_threshold),
+	attrify(noise_density),
+	attrify(frame_count),
+	NULL
+};
+
+static struct attribute *attrs_reg_27[] = {
+	attrify(iir_filter_coef),
+	NULL
+};
+
+static struct attribute *attrs_reg_28[] = {
+	attrify(quiet_threshold),
+	NULL
+};
+
+static struct attribute *attrs_reg_29[] = {
+	attrify(cmn_filter_disable),
+	NULL
+};
+
+static struct attribute *attrs_reg_30[] = {
+	attrify(cmn_filter_max),
+	NULL
+};
+
+static struct attribute *attrs_reg_31[] = {
+	attrify(touch_hysteresis),
+	NULL
+};
+
+static struct attribute *attrs_reg_32__35[] = {
+	attrify(rx_low_edge_comp),
+	attrify(rx_high_edge_comp),
+	attrify(tx_low_edge_comp),
+	attrify(tx_high_edge_comp),
+	NULL
+};
+
+static struct attribute *attrs_reg_36[] = {
+	attrify(axis1_comp),
+	NULL
+};
+
+static struct attribute *attrs_reg_37[] = {
+	attrify(axis2_comp),
+	NULL
+};
+
+static struct attribute *attrs_reg_38__40[] = {
+	attrify(noise_control_1),
+	attrify(noise_control_2),
+	attrify(noise_control_3),
+	NULL
+};
+
+static struct attribute_group attrs_ctrl_regs[] = {
+	GROUP(attrs_reg_0),
+	GROUP(attrs_reg_1),
+	GROUP(attrs_reg_2),
+	GROUP(attrs_reg_3),
+	GROUP(attrs_reg_4__6),
+	GROUP(attrs_reg_7),
+	GROUP(attrs_reg_8__9),
+	GROUP(attrs_reg_10),
+	GROUP(attrs_reg_12__13),
+	GROUP(attrs_reg_14__16),
+	GROUP(attrs_reg_17__19),
+	GROUP(attrs_reg_20),
+	GROUP(attrs_reg_21),
+	GROUP(attrs_reg_22__26),
+	GROUP(attrs_reg_27),
+	GROUP(attrs_reg_28),
+	GROUP(attrs_reg_29),
+	GROUP(attrs_reg_30),
+	GROUP(attrs_reg_31),
+	GROUP(attrs_reg_32__35),
+	GROUP(attrs_reg_36),
+	GROUP(attrs_reg_37),
+	GROUP(attrs_reg_38__40)
+};
+
+/* data specific to fn $54 that needs to be kept around */
+struct rmi_fn_54_data {
+	union f54_ad_query query;
+	struct f54_ad_control control;
+	bool attrs_ctrl_regs_exist[ARRAY_SIZE(attrs_ctrl_regs)];
+	enum f54_report_types report_type;
+	u16 fifoindex;
+	signed char status;
+	bool no_auto_cal;
+	unsigned int report_size;
+	unsigned char *report_data;
+	unsigned int bufsize;
+	struct mutex data_mutex;
+	struct mutex status_mutex;
+	struct mutex control_mutex;
+#if F54_WATCHDOG
+	struct hrtimer watchdog;
+#endif
+	struct rmi_function_container *fc;
+	struct work_struct work;
+};
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+#if F54_WATCHDOG
+static enum hrtimer_restart clear_status(struct hrtimer *timer);
+
+static void clear_status_worker(struct work_struct *work);
+#endif
+
+static int rmi_f54_alloc_memory(struct rmi_function_container *fc);
+
+static void rmi_f54_free_memory(struct rmi_function_container *fc);
+
+static int rmi_f54_initialize(struct rmi_function_container *fc);
+
+static int rmi_f54_reset(struct rmi_function_container *fc);
+
+static int rmi_f54_create_sysfs(struct rmi_function_container *fc);
+
+static int rmi_f54_init(struct rmi_function_container *fc)
+{
+	int retval = 0;
+	struct rmi_fn_54_data *f54;
+
+	retval = rmi_f54_alloc_memory(fc);
+	if (retval < 0)
+		goto error_exit;
+
+	retval = rmi_f54_initialize(fc);
+	if (retval < 0)
+		goto error_exit;
+
+	retval = rmi_f54_create_sysfs(fc);
+	if (retval < 0)
+		goto error_exit;
+	f54 = fc->data;
+	f54->status = IDLE;
+	return retval;
+
+error_exit:
+	rmi_f54_free_memory(fc);
+
+	return retval;
+}
+
+static int rmi_f54_alloc_memory(struct rmi_function_container *fc)
+{
+	struct rmi_fn_54_data *f54;
+
+	f54 = kzalloc(sizeof(struct rmi_fn_54_data), GFP_KERNEL);
+	if (!f54) {
+		dev_err(&fc->dev, "Failed to allocate rmi_fn_54_data.\n");
+		return -ENOMEM;
+	}
+	fc->data = f54;
+	f54->fc = fc;
+
+
+	return 0;
+}
+
+static void rmi_f54_free_memory(struct rmi_function_container *fc)
+{
+	int reg_num;
+	struct rmi_fn_54_data *f54 = fc->data;
+	sysfs_remove_group(&fc->dev.kobj, &attrs_query);
+	for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_ctrl_regs); reg_num++)
+		sysfs_remove_group(&fc->dev.kobj, &attrs_ctrl_regs[reg_num]);
+	sysfs_remove_bin_file(&fc->dev.kobj, &dev_rep_data);
+	if (f54) {
+		kfree(f54->report_data);
+		kfree(f54);
+		fc->data = NULL;
+	}
+}
+
+static int rmi_f54_reset(struct rmi_function_container *fc)
+{
+	struct rmi_fn_54_data *data = fc->data;
+	struct rmi_driver *driver = fc->rmi_dev->driver;
+
+#if F54_WATCHDOG
+	hrtimer_cancel(&data->watchdog);
+#endif
+
+	mutex_lock(&data->status_mutex);
+	if (driver->restore_irq_mask) {
+		dev_dbg(&fc->dev, "Restoring interupts!\n");
+		driver->restore_irq_mask(fc->rmi_dev);
+	} else {
+		dev_err(&fc->dev, "No way to restore interrupts!\n");
+	}
+	data->status = -ECONNRESET;
+	mutex_unlock(&data->status_mutex);
+
+	return 0;
+}
+
+static void rmi_f54_remove(struct rmi_function_container *fc)
+{
+	struct rmi_fn_54_data *data = fc->data;
+
+	dev_info(&fc->dev, "Removing F54.");
+
+	#if F54_WATCHDOG
+	/* Stop timer */
+	hrtimer_cancel(&data->watchdog);
+	#endif
+
+	rmi_f54_free_memory(fc);
+}
+
+static int rmi_f54_create_sysfs(struct rmi_function_container *fc)
+{
+	int reg_num;
+	int retval;
+	struct rmi_fn_54_data *f54 = fc->data;
+	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;
+	}
+	for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_ctrl_regs); reg_num++) {
+		if (f54->attrs_ctrl_regs_exist[reg_num]) {
+			retval = sysfs_create_group(&fc->dev.kobj,
+					&attrs_ctrl_regs[reg_num]);
+			if (retval < 0) {
+				dev_err(&fc->dev, "Failed to create sysfs file group for reg group %d, error = %d.",
+							reg_num, retval);
+				return -ENODEV;
+			}
+		}
+	}
+
+	/* Binary sysfs file to report the data back */
+	retval = sysfs_create_bin_file(&fc->dev.kobj, &dev_rep_data);
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to create sysfs file for F54 data (error = %d).\n",
+				retval);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+
+
+static int rmi_f54_initialize(struct rmi_function_container *fc)
+{
+	struct rmi_fn_54_data *f54 = fc->data;
+	struct f54_ad_control *control;
+	int retval = 0;
+	u8 size = 0;
+	u16 next_loc;
+	u8 reg_num;
+
+#if F54_WATCHDOG
+	/* Set up watchdog timer to catch unanswered get_report commands */
+	hrtimer_init(&f54->watchdog, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	f54->watchdog.function = clear_status;
+
+	/* work function to do unlocking */
+	INIT_WORK(&f54->work, clear_status_worker);
+#endif
+
+	/* Read F54 Query Data */
+	retval = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr,
+		(u8 *)&f54->query, sizeof(f54->query));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not read query registers from 0x%04x\n",
+				fc->fd.query_base_addr);
+		return retval;
+	}
+
+	/* Initialize the control registers */
+	next_loc = fc->fd.control_base_addr;
+	reg_num = 0;
+	control = &f54->control;
+
+	f54->attrs_ctrl_regs_exist[reg_num] = true;
+	reg_num++;
+	control->reg_0 = kzalloc(sizeof(union f54_ad_control_0), GFP_KERNEL);
+	if (!control->reg_0) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_0->address = next_loc;
+	next_loc += sizeof(control->reg_0->regs);
+
+	if (f54->query.touch_controller_family == 0
+			|| f54->query.touch_controller_family == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_1 = kzalloc(sizeof(union f54_ad_control_1),
+								GFP_KERNEL);
+		if (!control->reg_1) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_1->address = next_loc;
+		next_loc += sizeof(control->reg_1->regs);
+	}
+	reg_num++;
+
+	f54->attrs_ctrl_regs_exist[reg_num] = true;
+	reg_num++;
+	control->reg_2 = kzalloc(sizeof(union f54_ad_control_2), GFP_KERNEL);
+	if (!control->reg_2) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_2->address = next_loc;
+	next_loc += sizeof(control->reg_2->regs);
+
+	if (f54->query.has_pixel_touch_threshold_adjustment == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+
+		control->reg_3 = kzalloc(sizeof(union f54_ad_control_3),
+								GFP_KERNEL);
+		if (!control->reg_3) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_3->address = next_loc;
+		next_loc += sizeof(control->reg_3->regs);
+	}
+	reg_num++;
+
+	if (f54->query.touch_controller_family == 0
+		|| f54->query.touch_controller_family == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_4__6 = kzalloc(sizeof(union f54_ad_control_4__6),
+								GFP_KERNEL);
+		if (!control->reg_4__6) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_4__6->address = next_loc;
+		next_loc += sizeof(control->reg_4__6->regs);
+	}
+	reg_num++;
+
+	if (f54->query.touch_controller_family == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_7 = kzalloc(sizeof(union f54_ad_control_7),
+								GFP_KERNEL);
+		if (!control->reg_7) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_7->address = next_loc;
+		next_loc += sizeof(control->reg_7->regs);
+	}
+	reg_num++;
+
+	if (f54->query.touch_controller_family == 0
+		|| f54->query.touch_controller_family == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_8__9 = kzalloc(sizeof(union f54_ad_control_8__9),
+								GFP_KERNEL);
+		if (!control->reg_8__9) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_8__9->address = next_loc;
+		next_loc += sizeof(control->reg_8__9->regs);
+	}
+	reg_num++;
+
+	if (f54->query.has_interference_metric == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_10 = kzalloc(sizeof(union f54_ad_control_10),
+								GFP_KERNEL);
+		if (!control->reg_10) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_10->address = next_loc;
+		next_loc += sizeof(control->reg_10->regs);
+	}
+	reg_num++;
+
+	/* F54 Control Register 11 is reserved */
+	next_loc++;
+
+	if (f54->query.has_relaxation_control == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_12__13 = kzalloc(
+			sizeof(union f54_ad_control_12__13), GFP_KERNEL);
+		if (!control->reg_12__13) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_12__13->address = next_loc;
+		next_loc += sizeof(control->reg_12__13->regs);
+	}
+	reg_num++;
+
+	if (f54->query.has_sensor_assignment == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_14 = kzalloc(sizeof(union f54_ad_control_14),
+								GFP_KERNEL);
+		if (!control->reg_14) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_15 =
+			kzalloc(sizeof(struct f54_ad_control_15), GFP_KERNEL);
+		if (!control->reg_15) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_15->length = f54->query.num_of_rx_electrodes;
+		control->reg_15->regs =
+				kzalloc(control->reg_15->length *
+					sizeof(struct f54_ad_control_15n),
+					GFP_KERNEL);
+		if (!control->reg_15->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_16 =
+			kzalloc(sizeof(struct f54_ad_control_16), GFP_KERNEL);
+		if (!control->reg_16) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_16->length = f54->query.num_of_tx_electrodes;
+		control->reg_16->regs =
+				kzalloc(control->reg_16->length *
+					sizeof(struct f54_ad_control_16n),
+					GFP_KERNEL);
+		if (!control->reg_16->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_14->address = next_loc;
+		next_loc += sizeof(control->reg_14->regs);
+		control->reg_15->address = next_loc;
+		next_loc += control->reg_15->length;
+		control->reg_16->address = next_loc;
+		next_loc += control->reg_16->length;
+	}
+	reg_num++;
+
+	if (f54->query.has_sense_frequency_control == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		size = f54->query.number_of_sensing_frequencies;
+
+		control->reg_17 =
+			kzalloc(sizeof(struct f54_ad_control_17), GFP_KERNEL);
+		if (!control->reg_17) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_17->length = size;
+		control->reg_17->regs = kzalloc(size *
+				sizeof(struct f54_ad_control_17n), GFP_KERNEL);
+		if (!control->reg_17->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_18 =
+			kzalloc(sizeof(struct f54_ad_control_18), GFP_KERNEL);
+		if (!control->reg_18) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_18->length = size;
+		control->reg_18->regs = kzalloc(size *
+				sizeof(struct f54_ad_control_18n), GFP_KERNEL);
+		if (!control->reg_18->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_19 =
+			kzalloc(sizeof(struct f54_ad_control_19), GFP_KERNEL);
+		if (!control->reg_19) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_19->length = size;
+		control->reg_19->regs = kzalloc(size *
+				sizeof(struct f54_ad_control_19n), GFP_KERNEL);
+		if (!control->reg_19->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_17->address = next_loc;
+		next_loc += size;
+		control->reg_18->address = next_loc;
+		next_loc += size;
+		control->reg_19->address = next_loc;
+		next_loc += size;
+	}
+	reg_num++;
+
+	f54->attrs_ctrl_regs_exist[reg_num] = true;
+	control->reg_20 = kzalloc(sizeof(union f54_ad_control_20), GFP_KERNEL);
+	control->reg_20->address = next_loc;
+	next_loc += sizeof(control->reg_20->regs);
+	reg_num++;
+
+	if (f54->query.has_sense_frequency_control == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_21 = kzalloc(sizeof(union f54_ad_control_21),
+								GFP_KERNEL);
+		control->reg_21->address = next_loc;
+		next_loc += sizeof(control->reg_21->regs);
+	}
+	reg_num++;
+
+	if (f54->query.has_sense_frequency_control == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_22__26 = kzalloc(
+			sizeof(union f54_ad_control_22__26), GFP_KERNEL);
+		control->reg_22__26->address = next_loc;
+		next_loc += sizeof(control->reg_22__26->regs);
+	}
+	reg_num++;
+
+	if (f54->query.has_iir_filter == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_27 = kzalloc(sizeof(union f54_ad_control_27),
+								GFP_KERNEL);
+		control->reg_27->address = next_loc;
+		next_loc += sizeof(control->reg_27->regs);
+	}
+	reg_num++;
+
+	if (f54->query.has_firmware_noise_mitigation == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_28 = kzalloc(sizeof(union f54_ad_control_28),
+								GFP_KERNEL);
+		control->reg_28->address = next_loc;
+		next_loc += sizeof(control->reg_28->regs);
+	}
+	reg_num++;
+
+	if (f54->query.has_cmn_removal == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_29 = kzalloc(sizeof(union f54_ad_control_29),
+								GFP_KERNEL);
+		control->reg_29->address = next_loc;
+		next_loc += sizeof(control->reg_29->regs);
+	}
+	reg_num++;
+
+	if (f54->query.has_cmn_maximum == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_30 = kzalloc(sizeof(union f54_ad_control_30),
+								GFP_KERNEL);
+		control->reg_30->address = next_loc;
+		next_loc += sizeof(control->reg_30->regs);
+	}
+	reg_num++;
+
+	if (f54->query.has_touch_hysteresis == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_31 = kzalloc(sizeof(union f54_ad_control_31),
+								GFP_KERNEL);
+		control->reg_31->address = next_loc;
+		next_loc += sizeof(control->reg_31->regs);
+	}
+	reg_num++;
+
+	if (f54->query.has_interference_metric == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_32__35 = kzalloc(
+			sizeof(union f54_ad_control_32__35), GFP_KERNEL);
+		control->reg_32__35->address = next_loc;
+		next_loc += sizeof(control->reg_32__35->regs);
+	}
+	reg_num++;
+
+	if (f54->query.curve_compensation_mode == 1) {
+		size = max(f54->query.num_of_rx_electrodes,
+				f54->query.num_of_tx_electrodes);
+	}
+	if (f54->query.curve_compensation_mode == 2)
+		size = f54->query.num_of_rx_electrodes;
+	if (f54->query.curve_compensation_mode == 1
+			|| f54->query.curve_compensation_mode == 2) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+
+		control->reg_36 =
+			kzalloc(sizeof(struct f54_ad_control_36), GFP_KERNEL);
+		if (!control->reg_36) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_36->length = size;
+		control->reg_36->regs =
+			kzalloc(size * sizeof(struct f54_ad_control_36n),
+				GFP_KERNEL);
+		if (!control->reg_36->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_36->address = next_loc;
+		next_loc += size;
+	}
+	reg_num++;
+
+	if (f54->query.curve_compensation_mode == 2) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+
+		control->reg_37 =
+			kzalloc(sizeof(struct f54_ad_control_37), GFP_KERNEL);
+		if (!control->reg_37) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_37->length = f54->query.num_of_tx_electrodes;
+		control->reg_37->regs = kzalloc(control->reg_37->length *
+				sizeof(struct f54_ad_control_37n),
+				GFP_KERNEL);
+		if (!control->reg_37->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_37->address = next_loc;
+		next_loc += control->reg_37->length;
+	}
+	reg_num++;
+
+	if (f54->query.has_per_frequency_noise_control == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+
+		control->reg_38 =
+			kzalloc(sizeof(struct f54_ad_control_38), GFP_KERNEL);
+		if (!control->reg_38) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_38->length =
+				f54->query.number_of_sensing_frequencies;
+		control->reg_38->regs = kzalloc(control->reg_38->length *
+					sizeof(struct f54_ad_control_38n),
+					GFP_KERNEL);
+		if (!control->reg_38->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_39 =
+			kzalloc(sizeof(struct f54_ad_control_39), GFP_KERNEL);
+		if (!control->reg_39) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_39->length =
+				f54->query.number_of_sensing_frequencies;
+		control->reg_39->regs = kzalloc(control->reg_39->length *
+					sizeof(struct f54_ad_control_39n),
+					GFP_KERNEL);
+		if (!control->reg_39->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_40 =
+			kzalloc(sizeof(struct f54_ad_control_40), GFP_KERNEL);
+		if (!control->reg_40) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_40->length =
+				f54->query.number_of_sensing_frequencies;
+		control->reg_40->regs = kzalloc(control->reg_40->length *
+				sizeof(struct f54_ad_control_40n), GFP_KERNEL);
+		if (!control->reg_40->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_38->address = next_loc;
+		next_loc += control->reg_38->length;
+		control->reg_39->address = next_loc;
+		next_loc += control->reg_39->length;
+		control->reg_40->address = next_loc;
+		next_loc += control->reg_40->length;
+	}
+	reg_num++;
+
+	mutex_init(&f54->data_mutex);
+
+	mutex_init(&f54->status_mutex);
+
+	mutex_init(&f54->control_mutex);
+
+	return retval;
+}
+
+static void set_report_size(struct rmi_fn_54_data *data)
+{
+	u8 rx = data->query.num_of_rx_electrodes;
+	u8 tx = data->query.num_of_tx_electrodes;
+	switch (data->report_type) {
+	case F54_8BIT_IMAGE:
+		data->report_size = rx * tx;
+		break;
+	case F54_16BIT_IMAGE:
+	case F54_RAW_16BIT_IMAGE:
+	case F54_TRUE_BASELINE:
+	case F54_FULL_RAW_CAP:
+	case F54_FULL_RAW_CAP_RX_COUPLING_COMP:
+		data->report_size = 2 * rx * tx;
+		break;
+	case F54_HIGH_RESISTANCE:
+		data->report_size = RMI_54_HIGH_RESISTANCE_SIZE;
+		break;
+	case F54_FULL_RAW_CAP_MIN_MAX:
+		data->report_size = RMI_54_FULL_RAW_CAP_MIN_MAX_SIZE;
+		break;
+	case F54_TX_TO_TX_SHORT:
+	case F54_TX_OPEN:
+	case F54_TX_TO_GROUND:
+		data->report_size =  (tx + 7) / 8;
+		break;
+	case F54_RX_TO_RX1:
+	case F54_RX_OPENS1:
+		if (rx < tx)
+			data->report_size = 2 * rx * rx;
+		else
+			data->report_size = 2 * rx * tx;
+		break;
+	case F54_RX_TO_RX2:
+	case F54_RX_OPENS2:
+		if (rx <= tx)
+			data->report_size = 0;
+		else
+			data->report_size = 2 * rx * (rx - tx);
+		break;
+	default:
+		data->report_size = 0;
+	}
+}
+
+int rmi_f54_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+	struct rmi_driver *driver = fc->rmi_dev->driver;
+	char fifo[2];
+	struct rmi_fn_54_data *data = fc->data;
+	int error = 0;
+
+	set_report_size(data);
+	if (data->report_size == 0) {
+		dev_err(&fc->dev, "Invalid report type set in %s. "
+				"This should never happen.\n", __func__);
+		error = -EINVAL;
+		goto error_exit;
+	}
+	/*
+	 * We need to ensure the buffer is big enough. A Buffer size of 0 means
+	 * that the buffer has not been allocated.
+	 */
+	if (data->bufsize < data->report_size) {
+		mutex_lock(&data->data_mutex);
+		if (data->bufsize > 0)
+			kfree(data->report_data);
+		data->report_data = kzalloc(data->report_size, GFP_KERNEL);
+		if (!data->report_data) {
+			dev_err(&fc->dev, "Failed to allocate report_data.\n");
+			error = -ENOMEM;
+			data->bufsize = 0;
+			mutex_unlock(&data->data_mutex);
+			goto error_exit;
+		}
+		data->bufsize = data->report_size;
+		mutex_unlock(&data->data_mutex);
+	}
+	dev_vdbg(&fc->dev, "F54 Interrupt handler is running.\nSize: %d\n",
+		 data->report_size);
+	/* Write 0 to fifohi and fifolo. */
+	fifo[0] = 0;
+	fifo[1] = 0;
+	error = rmi_write_block(fc->rmi_dev, fc->fd.data_base_addr
+				+ RMI_F54_FIFO_OFFSET, fifo,	sizeof(fifo));
+	if (error < 0)
+		dev_err(&fc->dev, "Failed to write fifo to zero!\n");
+	else
+		error = rmi_read_block(fc->rmi_dev,
+			fc->fd.data_base_addr + RMI_F54_REPORT_DATA_OFFSET,
+			data->report_data, data->report_size);
+	if (error < 0)
+		dev_err(&fc->dev, "F54 data read failed. Code: %d.\n", error);
+	else if (error != data->report_size) {
+		error = -EINVAL;
+		goto error_exit;
+	}
+#if RAW_HEX
+	int l;
+	/* Debugging: Print out the file in hex. */
+	pr_info("Report data (raw hex):\n");
+	for (l = 0; l < data->report_size; l += 2) {
+		pr_info("%03d: 0x%02x%02x\n", l/2,
+			data->report_data[l+1], data->report_data[l]);
+	}
+#endif
+#if HUMAN_READABLE
+	/* Debugging: Print out file in human understandable image */
+	switch (data->report_type) {
+	case F54_16BIT_IMAGE:
+	case F54_RAW_16BIT_IMAGE:
+	case F54_TRUE_BASELINE:
+	case F54_FULL_RAW_CAP:
+	case F54_FULL_RAW_CAP_RX_COUPLING_COMP:
+		pr_info("Report data (Image):\n");
+		int i, j, k;
+		char c[2];
+		short s;
+		k = 0;
+		for (i = 0; i < data->query.num_of_tx_electrodes;
+									i++) {
+			for (j = 0; j <
+			     data->query.num_of_rx_electrodes; j++) {
+				c[0] = data->report_data[k];
+				c[1] = data->report_data[k+1];
+				memcpy(&s, &c, 2);
+				if (s < -64)
+					printk(".");
+				else if (s < 0)
+					printk("-");
+				else if (s > 64)
+					printk("*");
+				else if (s > 0)
+					printk("+");
+				else
+					printk("0");
+				k += 2;
+			}
+			pr_info("\n");
+		}
+		pr_info("EOF\n");
+		break;
+	default:
+		pr_info("Report type %d debug image not supported",
+							data->report_type);
+	}
+#endif
+	error = IDLE;
+error_exit:
+	mutex_lock(&data->status_mutex);
+	/* Turn back on other interupts, if it
+	 * appears that we turned them off. */
+	if (driver->restore_irq_mask) {
+		dev_dbg(&fc->dev, "Restoring interupts!\n");
+		driver->restore_irq_mask(fc->rmi_dev);
+	} else {
+		dev_err(&fc->dev, "No way to restore interrupts!\n");
+	}
+	data->status = error;
+	mutex_unlock(&data->status_mutex);
+	return data->status;
+}
+
+
+#if F54_WATCHDOG
+static void clear_status_worker(struct work_struct *work)
+{
+	struct rmi_fn_54_data *data = container_of(work,
+					struct rmi_fn_54_data, work);
+	struct rmi_function_container *fc = data->fc;
+	struct rmi_driver *driver = fc->rmi_dev->driver;
+	char command;
+	int result;
+
+	mutex_lock(&data->status_mutex);
+	if (data->status == BUSY) {
+		result = rmi_read_block(fc->rmi_dev, fc->fd.command_base_addr,
+								&command, 1);
+		if (result < 0) {
+			dev_err(&fc->dev, "Could not read get_report register from %#06x.\n",
+						fc->fd.command_base_addr);
+			data->status = -ETIMEDOUT;
+		} else {
+			if (command & GET_REPORT) {
+				dev_warn(&fc->dev, "Report type unsupported!");
+				data->status = -EINVAL;
+			} else {
+				data->status = -ETIMEDOUT;
+			}
+		}
+		if (driver->restore_irq_mask) {
+			dev_dbg(&fc->dev, "Restoring interupts!\n");
+			driver->restore_irq_mask(fc->rmi_dev);
+		} else {
+			dev_err(&fc->dev, "No way to restore interrupts!\n");
+		}
+	}
+	mutex_unlock(&data->status_mutex);
+}
+
+static enum hrtimer_restart clear_status(struct hrtimer *timer)
+{
+	struct rmi_fn_54_data *data = container_of(timer,
+					struct rmi_fn_54_data, watchdog);
+	schedule_work(&(data->work));
+	return HRTIMER_NORESTART;
+}
+#endif
+
+/* Check if report_type is valid */
+static bool is_report_type_valid(enum f54_report_types reptype)
+{
+	/* Basic checks on report_type to ensure we write a valid type
+	 * to the sensor.
+	 * TODO: Check Query3 to see if some specific reports are
+	 * available. This is currently listed as a reserved register.
+	 */
+	switch (reptype) {
+	case F54_8BIT_IMAGE:
+	case F54_16BIT_IMAGE:
+	case F54_RAW_16BIT_IMAGE:
+	case F54_HIGH_RESISTANCE:
+	case F54_TX_TO_TX_SHORT:
+	case F54_RX_TO_RX1:
+	case F54_TRUE_BASELINE:
+	case F54_FULL_RAW_CAP_MIN_MAX:
+	case F54_RX_OPENS1:
+	case F54_TX_OPEN:
+	case F54_TX_TO_GROUND:
+	case F54_RX_TO_RX2:
+	case F54_RX_OPENS2:
+	case F54_FULL_RAW_CAP:
+	case F54_FULL_RAW_CAP_RX_COUPLING_COMP:
+		return true;
+		break;
+	default:
+		return false;
+	}
+}
+
+/* SYSFS file show/store functions */
+static ssize_t rmi_fn_54_report_type_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *f54;
+
+	fc = to_rmi_function_container(dev);
+	f54 = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", f54->report_type);
+}
+
+static ssize_t rmi_fn_54_report_type_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count) {
+	int result;
+	unsigned long val;
+	unsigned char data;
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	/* need to convert the string data to an actual value */
+	result = strict_strtoul(buf, 10, &val);
+	if (result)
+		return result;
+	if (!is_report_type_valid(val)) {
+		dev_err(dev, "%s : Report type %d is invalid.\n",
+					__func__, (u8) val);
+		return -EINVAL;
+	}
+	mutex_lock(&instance_data->status_mutex);
+	if (instance_data->status != BUSY) {
+		instance_data->report_type = (enum f54_report_types)val;
+		data = (char)val;
+		/* Write the Report Type back to the first Block
+		 * Data registers (F54_AD_Data0). */
+		result =
+		    rmi_write_block(fc->rmi_dev, fc->fd.data_base_addr,
+								&data, 1);
+		mutex_unlock(&instance_data->status_mutex);
+		if (result < 0) {
+			dev_err(dev, "%s : Could not write report type to"
+				" 0x%x\n", __func__, fc->fd.data_base_addr);
+			return result;
+		}
+		return count;
+	} else {
+		dev_err(dev, "%s : Report type cannot be changed in the middle"
+				" of command.\n", __func__);
+		mutex_unlock(&instance_data->status_mutex);
+		return -EINVAL;
+	}
+}
+
+static ssize_t rmi_fn_54_get_report_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count) {
+	unsigned long val;
+	int error, result;
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+	struct rmi_driver *driver;
+	u8 command;
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+	driver = fc->rmi_dev->driver;
+
+	/* need to convert the string data to an actual value */
+	error = strict_strtoul(buf, 10, &val);
+	if (error)
+		return error;
+	/* Do nothing if not set to 1. This prevents accidental commands. */
+	if (val != 1)
+		return count;
+	command = (unsigned char)GET_REPORT;
+	/* Basic checks on report_type to ensure we write a valid type
+	 * to the sensor.
+	 * TODO: Check Query3 to see if some specific reports are
+	 * available. This is currently listed as a reserved register.
+	 */
+	if (!is_report_type_valid(instance_data->report_type)) {
+		dev_err(dev, "%s : Report type %d is invalid.\n",
+				__func__, instance_data->report_type);
+		return -EINVAL;
+	}
+	mutex_lock(&instance_data->status_mutex);
+	if (instance_data->status != IDLE) {
+		if (instance_data->status != BUSY) {
+			dev_err(dev, "F54 status is in an abnormal state: 0x%x",
+							instance_data->status);
+		}
+		mutex_unlock(&instance_data->status_mutex);
+		return count;
+	}
+	/* Store interrupts */
+	/* Do not exit if we fail to turn off interupts. We are likely
+	 * to still get useful data. The report data can, however, be
+	 * corrupted, and there may be unexpected behavior.
+	 */
+	dev_dbg(dev, "Storing and overriding interupts\n");
+	if (driver->store_irq_mask)
+		driver->store_irq_mask(fc->rmi_dev,
+					fc->irq_mask);
+	else
+		dev_err(dev, "No way to store interupts!\n");
+	instance_data->status = BUSY;
+
+	/* small delay to avoid race condition in firmare. This value is a bit
+	 * higher than absolutely necessary. Should be removed once issue is
+	 * resolved in firmware. */
+
+	mdelay(2);
+
+	/* Write the command to the command register */
+	result = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
+						&command, 1);
+	mutex_unlock(&instance_data->status_mutex);
+	if (result < 0) {
+		dev_err(dev, "%s : Could not write command to 0x%x\n",
+				__func__, fc->fd.command_base_addr);
+		return result;
+	}
+#if F54_WATCHDOG
+	/* start watchdog timer */
+	hrtimer_start(&instance_data->watchdog, ktime_set(1, 0),
+							HRTIMER_MODE_REL);
+#endif
+	return count;
+}
+
+static ssize_t rmi_fn_54_force_cal_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count) {
+	unsigned long val;
+	int error, result;
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+	struct rmi_driver *driver;
+	u8 command;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+	driver = fc->rmi_dev->driver;
+
+	/* need to convert the string data to an actual value */
+	error = strict_strtoul(buf, 10, &val);
+	if (error)
+		return error;
+	/* Do nothing if not set to 1. This prevents accidental commands. */
+	if (val != 1)
+		return count;
+
+	command = (unsigned char)FORCE_CAL;
+
+	if (instance_data->status == BUSY)
+		return -EBUSY;
+	/* Write the command to the command register */
+	result = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
+						&command, 1);
+	if (result < 0) {
+		dev_err(dev, "%s : Could not write command to 0x%x\n",
+				__func__, fc->fd.command_base_addr);
+		return result;
+	}
+	return count;
+}
+
+static ssize_t rmi_fn_54_status_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", instance_data->status);
+}
+
+simple_show_union_struct_unsigned(query, num_of_rx_electrodes)
+simple_show_union_struct_unsigned(query, num_of_tx_electrodes)
+simple_show_union_struct_unsigned(query, has_image16)
+simple_show_union_struct_unsigned(query, has_image8)
+simple_show_union_struct_unsigned(query, has_baseline)
+simple_show_union_struct_unsigned(query, clock_rate)
+simple_show_union_struct_unsigned(query, touch_controller_family)
+simple_show_union_struct_unsigned(query, has_pixel_touch_threshold_adjustment)
+simple_show_union_struct_unsigned(query, has_sensor_assignment)
+simple_show_union_struct_unsigned(query, has_interference_metric)
+simple_show_union_struct_unsigned(query, has_sense_frequency_control)
+simple_show_union_struct_unsigned(query, has_firmware_noise_mitigation)
+simple_show_union_struct_unsigned(query, has_two_byte_report_rate)
+simple_show_union_struct_unsigned(query, has_one_byte_report_rate)
+simple_show_union_struct_unsigned(query, has_relaxation_control)
+simple_show_union_struct_unsigned(query, curve_compensation_mode)
+simple_show_union_struct_unsigned(query, has_iir_filter)
+simple_show_union_struct_unsigned(query, has_cmn_removal)
+simple_show_union_struct_unsigned(query, has_cmn_maximum)
+simple_show_union_struct_unsigned(query, has_touch_hysteresis)
+simple_show_union_struct_unsigned(query, has_edge_compensation)
+simple_show_union_struct_unsigned(query, has_per_frequency_noise_control)
+simple_show_union_struct_unsigned(query, number_of_sensing_frequencies)
+
+static ssize_t rmi_fn_54_no_auto_cal_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+				data->no_auto_cal ? 1 : 0);
+}
+
+static ssize_t rmi_fn_54_no_auto_cal_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count) {
+	int result;
+	unsigned long val;
+	unsigned char data;
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	/* need to convert the string data to an actual value */
+	result = strict_strtoul(buf, 10, &val);
+
+	/* if an error occured, return it */
+	if (result)
+		return result;
+	/* Do nothing if not 0 or 1. This prevents accidental commands. */
+	if (val > 1)
+		return -EINVAL;
+	/* Read current control values */
+	result =
+	    rmi_read_block(fc->rmi_dev, fc->fd.control_base_addr, &data, 1);
+
+	/* if the current control registers are already set as we want them, do
+	 * nothing to them */
+	if ((data & NO_AUTO_CAL_MASK) == val)
+		return count;
+	/* Write the control back to the control register (F54_AD_Ctrl0)
+	 * Ignores everything but bit 0 */
+	data = (data & ~NO_AUTO_CAL_MASK) | (val & NO_AUTO_CAL_MASK);
+	result =
+	    rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr, &data, 1);
+	if (result < 0) {
+		dev_err(dev, "%s : Could not write control to 0x%x\n",
+		       __func__, fc->fd.control_base_addr);
+		return result;
+	}
+	/* update our internal representation iff the write succeeds */
+	instance_data->no_auto_cal = (val == 1);
+	return count;
+}
+
+static ssize_t rmi_fn_54_fifoindex_show(struct device *dev,
+				  struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+	struct rmi_driver *driver;
+	unsigned char temp_buf[2];
+	int retval;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+	driver = fc->rmi_dev->driver;
+
+	/* Read fifoindex from device */
+	retval = rmi_read_block(fc->rmi_dev,
+				fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET,
+				temp_buf, ARRAY_SIZE(temp_buf));
+
+	if (retval < 0) {
+		dev_err(dev, "Could not read fifoindex from 0x%04x\n",
+		       fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET);
+		return retval;
+	}
+	batohs(&instance_data->fifoindex, temp_buf);
+	return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->fifoindex);
+}
+static ssize_t rmi_fn_54_fifoindex_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_54_data *instance_data;
+
+	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->fifoindex = val;
+
+	/* Write the FifoIndex back to the first data registers. */
+	hstoba(data, (unsigned short)val);
+
+	error = rmi_write_block(fc->rmi_dev,
+				fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET,
+				data,
+				ARRAY_SIZE(data));
+
+	if (error < 0) {
+		dev_err(dev, "%s : Could not write fifoindex to 0x%x\n",
+		       __func__, fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET);
+		return error;
+	}
+	return count;
+}
+
+/* Provide access to last report */
+static ssize_t rmi_fn_54_data_read(struct file *data_file, struct kobject *kobj,
+				struct bin_attribute *attributes,
+				char *buf, loff_t pos, size_t count)
+{
+	struct device *dev;
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+
+	dev = container_of(kobj, struct device, kobj);
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+	mutex_lock(&instance_data->data_mutex);
+	if (count < instance_data->report_size) {
+		dev_err(dev,
+			"%s: F54 report size too large for buffer: %d."
+				" Need at least: %d for Report type: %d.\n",
+			__func__, count, instance_data->report_size,
+			instance_data->report_type);
+		mutex_unlock(&instance_data->data_mutex);
+		return -EINVAL;
+	}
+	if (instance_data->report_data) {
+		/* Copy data from instance_data to buffer */
+		memcpy(buf, instance_data->report_data,
+					instance_data->report_size);
+		mutex_unlock(&instance_data->data_mutex);
+		dev_dbg(dev, "%s: Presumably successful.", __func__);
+		return instance_data->report_size;
+	} else {
+		dev_err(dev, "%s: F54 report_data does not exist!\n", __func__);
+		mutex_unlock(&instance_data->data_mutex);
+		return -EINVAL;
+	}
+}
+
+/* Repeated Register sysfs functions */
+show_repeated_union_struct_unsigned(control, reg_15, sensor_rx_assignment)
+show_repeated_union_struct_unsigned(control, reg_16, sensor_tx_assignment)
+
+show_repeated_union_struct_unsigned(control, reg_17, filter_bandwidth)
+show_repeated_union_struct_unsigned(control, reg_17, disable)
+
+
+static ssize_t rmi_fn_54_burst_count_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+	int result, size = 0;
+	char *temp;
+	int i;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	mutex_lock(&data->control_mutex);
+	/* Read current control values */
+	result = rmi_read_block(fc->rmi_dev, data->control.reg_17->address,
+			(u8 *) data->control.reg_17->regs,
+			data->control.reg_17->length * sizeof(u8));
+	if (result < 0) {
+		dev_err(dev, "%s : Could not read control at 0x%x\n"
+					"Data may be outdated.", __func__,
+					data->control.reg_17->address);
+	}
+
+	result = rmi_read_block(fc->rmi_dev, data->control.reg_18->address,
+			(u8 *)data->control.reg_18->regs,
+			data->control.reg_18->length * sizeof(u8));
+	if (result < 0) {
+		dev_err(dev, "%s : Could not read control at 0x%x\n"
+					"Data may be outdated.", __func__,
+					data->control.reg_18->address);
+	}
+	mutex_unlock(&data->control_mutex);
+	temp = buf;
+	for (i = 0; i < data->control.reg_17->length; i++) {
+		result = snprintf(temp, PAGE_SIZE - size, "%u ", (1<<8) *
+			data->control.reg_17->regs[i].burst_countb10__8 +
+			data->control.reg_18->regs[i].burst_countb7__0n);
+		size += result;
+		temp += result;
+	}
+	return size + snprintf(temp, PAGE_SIZE - size, "\n");
+}
+
+show_repeated_union_struct_unsigned(control, reg_19, stretch_duration)
+show_store_repeated_union_struct_unsigned(control, reg_36, axis1_comp)
+show_store_repeated_union_struct_unsigned(control, reg_37, axis2_comp)
+
+show_repeated_union_struct_unsigned(control, reg_38, noise_control_1)
+show_repeated_union_struct_unsigned(control, reg_39, noise_control_2)
+show_repeated_union_struct_unsigned(control, reg_40, noise_control_3)
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+show_store_union_struct_unsigned(control, reg_0, no_relax)
+show_store_union_struct_unsigned(control, reg_0, no_scan)
+show_store_union_struct_unsigned(control, reg_1, bursts_per_cluster)
+show_store_union_struct_unsigned(control, reg_2, saturation_cap)
+show_store_union_struct_unsigned(control, reg_3, pixel_touch_threshold)
+show_store_union_struct_unsigned(control, reg_4__6, rx_feedback_cap)
+show_store_union_struct_unsigned(control, reg_4__6, low_ref_cap)
+show_store_union_struct_unsigned(control, reg_4__6, low_ref_feedback_cap)
+show_store_union_struct_unsigned(control, reg_4__6, low_ref_polarity)
+show_store_union_struct_unsigned(control, reg_4__6, high_ref_cap)
+show_store_union_struct_unsigned(control, reg_4__6, high_ref_feedback_cap)
+show_store_union_struct_unsigned(control, reg_4__6, high_ref_polarity)
+show_store_union_struct_unsigned(control, reg_7, cbc_cap)
+show_store_union_struct_unsigned(control, reg_7, cbc_polarity)
+show_store_union_struct_unsigned(control, reg_7, cbc_tx_carrier_selection)
+show_store_union_struct_unsigned(control, reg_8__9, integration_duration)
+show_store_union_struct_unsigned(control, reg_8__9, reset_duration)
+show_store_union_struct_unsigned(control, reg_10,
+						noise_sensing_bursts_per_image)
+show_store_union_struct_unsigned(control, reg_12__13, slow_relaxation_rate)
+show_store_union_struct_unsigned(control, reg_12__13, fast_relaxation_rate)
+show_store_union_struct_unsigned(control, reg_14, rxs_on_xaxis)
+show_store_union_struct_unsigned(control, reg_14, curve_comp_on_txs)
+show_store_union_struct_unsigned(control, reg_20, disable_noise_mitigation)
+show_store_union_struct_unsigned(control, reg_21, freq_shift_noise_threshold)
+show_store_union_struct_unsigned(control, reg_22__26, medium_noise_threshold)
+show_store_union_struct_unsigned(control, reg_22__26, high_noise_threshold)
+show_store_union_struct_unsigned(control, reg_22__26, noise_density)
+show_store_union_struct_unsigned(control, reg_22__26, frame_count)
+show_store_union_struct_unsigned(control, reg_27, iir_filter_coef)
+show_store_union_struct_unsigned(control, reg_28, quiet_threshold)
+show_store_union_struct_unsigned(control, reg_29, cmn_filter_disable)
+show_store_union_struct_unsigned(control, reg_30, cmn_filter_max)
+show_store_union_struct_unsigned(control, reg_31, touch_hysteresis)
+show_store_union_struct_unsigned(control, reg_32__35, rx_low_edge_comp)
+show_store_union_struct_unsigned(control, reg_32__35, rx_high_edge_comp)
+show_store_union_struct_unsigned(control, reg_32__35, tx_low_edge_comp)
+show_store_union_struct_unsigned(control, reg_32__35, tx_high_edge_comp)
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+static struct rmi_function_handler function_handler = {
+	.func = 0x54,
+	.init = rmi_f54_init,
+	.config = NULL,
+	.reset = rmi_f54_reset,
+	.attention = rmi_f54_attention,
+	.remove = rmi_f54_remove
+};
+
+static int __init rmi_f54_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_f54_module_exit(void)
+{
+	rmi_unregister_function_driver(&function_handler);
+}
+
+module_init(rmi_f54_module_init);
+module_exit(rmi_f54_module_exit);
+
+MODULE_AUTHOR("Daniel Rosenberg <daniel.rosenberg@synaptics.com>");
+MODULE_DESCRIPTION("RMI F54 module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);