obj-$(CONFIG_INPUT_YEALINK) += yealink.o
obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o
obj-$(CONFIG_INPUT_S2MU106_HAPTIC) += s2mu106_haptic.o
-obj-$(CONFIG_SARSENSOR_SX9325) += sx932x.o
\ No newline at end of file
+obj-$(CONFIG_SARSENSOR_SX9325) += sx9325_sar.o
--- /dev/null
+/*
+ * file sx9325_sar.c
+ * brief SX9325 Driver for two channel SAP using
+ *
+ * Driver for the SX9325
+ * Copyright (c) 2015-2016 Semtech Corp
+ *
+ * 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.
+ */
+#define DEBUG
+#define DRIVER_NAME "sx9325"
+#define USE_SENSORS_CLASS
+#define USE_KERNEL_SUSPEND
+
+#define MAX_WRITE_ARRAY_SIZE 32
+
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/sensors.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
+#include <linux/notifier.h>
+#include <linux/usb.h>
+#include <linux/power_supply.h>
+
+#if defined(CONFIG_FB)
+#include <linux/fb.h>
+#endif
+
+/* main struct, interrupt,init,pointers */
+#include "sx9325_sar.h"
+
+
+#define IDLE 0
+#define ACTIVE 1
+#define S_PROX 1
+#define S_BODY 2
+
+#define SX9325_DEBUG 1
+#define LOG_TAG "SX9325 "
+
+#if SX9325_DEBUG
+#define LOG_INFO(fmt, args...) pr_info(LOG_TAG fmt, ##args)
+#else
+#define LOG_INFO(fmt, args...)
+#endif
+
+#define LOG_DBG(fmt, args...) pr_debug(LOG_TAG fmt, ##args)
+#define LOG_ERR(fmt, args...) pr_err(LOG_TAG fmt, ##args)
+
+static int sx9325_debug_enable = 1;
+
+static int last_val;
+static int mEnabled;
+static int programming_done;
+psx93XX_t sx9325_sar_ptr;
+
+/**
+ * struct sx9325
+ * Specialized struct containing input event data, platform data, and
+ * last cap state read if needed.
+ */
+typedef struct sx9325 {
+ pbuttonInformation_t pbuttonInformation;
+ psx9325_platform_data_t hw; /* specific platform data settings */
+} sx9325_t, *psx9325_t;
+
+static void ForcetoTouched(psx93XX_t this)
+{
+ psx9325_t pDevice = NULL;
+ struct input_dev *input_top = NULL;
+ struct input_dev *input_bottom = NULL;
+ struct _buttonInfo *pCurrentButton = NULL;
+
+ pDevice = this->pDevice;
+ if (this && pDevice) {
+ LOG_INFO("ForcetoTouched()\n");
+
+ pCurrentButton = pDevice->pbuttonInformation->buttons;
+ input_top = pDevice->pbuttonInformation->input_top;
+ input_bottom = pDevice->pbuttonInformation->input_bottom;
+ pCurrentButton->state = ACTIVE;
+ last_val = 1;
+ if (mEnabled) {
+ input_report_abs(input_top, ABS_DISTANCE, 1);
+ input_sync(input_top);
+ input_report_abs(input_bottom, ABS_DISTANCE, 1);
+ input_sync(input_bottom);
+ }
+ LOG_INFO("Leaving ForcetoTouched()\n");
+ }
+}
+
+/**
+ * fn static int write_register(psx93XX_t this, u8 address, u8 value)
+ * brief Sends a write register to the device
+ * param this Pointer to main parent struct
+ * param address 8-bit register address
+ * param value 8-bit register value to write to address
+ * return Value from i2c_master_send
+ */
+static int write_register(psx93XX_t this, u8 address, u8 value)
+{
+ struct i2c_client *i2c = 0;
+ char buffer[2];
+ int returnValue = 0;
+
+ buffer[0] = address;
+ buffer[1] = value;
+ returnValue = -ENOMEM;
+ if (this && this->bus) {
+ i2c = this->bus;
+
+ returnValue = i2c_master_send(i2c, buffer, 2);
+ if (sx9325_debug_enable)
+ LOG_DBG("write_register Addr: \
+ 0x%x Val: 0x%x Return: %d\n",
+ address, value, returnValue);
+ }
+ if (returnValue < 0) {
+ ForcetoTouched(this);
+ LOG_INFO("Write_register-ForcetoTouched()\n");
+ }
+ return returnValue;
+}
+
+/**
+ * fn static int read_register(psx93XX_t this, u8 address, u8 *value)
+ * brief Reads a register's value from the device
+ * param this Pointer to main parent struct
+ * param address 8-Bit address to read from
+ * param value Pointer to 8-bit value to save register value to
+ * return Value from i2c_smbus_read_byte_data if < 0. else 0
+ */
+static int read_register(psx93XX_t this, u8 address, u8 *value)
+{
+ struct i2c_client *i2c = 0;
+ s32 returnValue = 0;
+
+ if (this && value && this->bus) {
+ i2c = this->bus;
+ returnValue = i2c_smbus_read_byte_data(i2c, address);
+ if (sx9325_debug_enable)
+ LOG_DBG("read_register Addr: 0x%x Return: 0x%x\n",
+ address, returnValue);
+ if (returnValue >= 0) {
+ *value = returnValue;
+ return 0;
+ } else {
+ return returnValue;
+ }
+ }
+ ForcetoTouched(this);
+ LOG_INFO("read_register-ForcetoTouched()\n");
+ return -ENOMEM;
+}
+
+/**
+ * detect if sx9325 exist or not
+ * return 1 if chip exist else return 0
+ */
+static int sx9325_detect(struct i2c_client *client)
+{
+ s32 returnValue = 0, i;
+ u8 address = SX932x_WHOAMI_REG;
+ u8 value_9325 = SX932x_WHOAMI_VALUE;
+
+ if (client) {
+ for (i = 0; i < 3; i++) {
+ returnValue = i2c_smbus_read_byte_data(client, address);
+ LOG_INFO("sx9325 read_register for %d time Addr:\
+ 0x%x Return: 0x%x\n",
+ i, address, returnValue);
+ if (value_9325 == returnValue){
+ LOG_INFO("sx9325 detect success !\n");
+ return 1;
+ }
+ }
+ }
+ LOG_ERR("sx9325 detect failed !!!\n");
+ return 0;
+}
+
+/**
+ * brief Perform a manual offset calibration
+ * param this Pointer to main parent struct
+ * return Value return value from the write register
+ */
+static int manual_offset_calibration(psx93XX_t this)
+{
+ s32 returnValue = 0;
+
+ returnValue = write_register(this,SX932x_STAT2_REG,0x0F);
+ return returnValue;
+}
+
+/**
+ * brief sysfs show function for manual calibration which currently just
+ * returns register value.
+ */
+
+static ssize_t manual_offset_calibration_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u8 reg_value = 0;
+ psx93XX_t this = dev_get_drvdata(dev);
+
+ LOG_INFO("Reading IRQSTAT_REG\n");
+ read_register(this, SX932x_IRQSTAT_REG, ®_value);
+ return scnprintf(buf, PAGE_SIZE, "%d\n", reg_value);
+}
+
+/* brief sysfs store function for manual calibration */
+static ssize_t manual_offset_calibration_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ psx93XX_t this = dev_get_drvdata(dev);
+ unsigned long val;
+
+ if (kstrtoul(buf, 0, &val))
+ return -EINVAL;
+ if (val) {
+ LOG_INFO("Performing manual_offset_calibration()\n");
+ manual_offset_calibration(this);
+ }
+ return count;
+}
+
+static DEVICE_ATTR(calibrate, 0644, manual_offset_calibration_show,
+ manual_offset_calibration_store);
+static struct attribute *sx9325_attributes[] = {
+ &dev_attr_calibrate.attr,
+ NULL,
+};
+static struct attribute_group sx9325_attr_group = {
+ .attrs = sx9325_attributes,
+};
+
+/**
+ * fn static int read_regStat(psx93XX_t this)
+ * brief Shortcut to read what caused interrupt.
+ * details This is to keep the drivers a unified
+ * function that will read whatever register(s)
+ * provide information on why the interrupt was caused.
+ * param this Pointer to main parent struct
+ * return If successful, Value of bit(s) that cause interrupt, else 0
+ */
+static int read_regStat(psx93XX_t this)
+{
+ u8 data = 0;
+
+ if (this) {
+ if (read_register(this, SX932x_IRQSTAT_REG, &data) == 0)
+ return (data & 0x00FF);
+ }
+ return 0;
+}
+
+static void read_rawData(psx93XX_t this)
+{
+ u8 msb = 0, lsb = 0;
+ unsigned int ii;
+
+ if (this) {
+ for (ii = 0; ii < USE_CHANNEL_NUM; ii++) {
+ /* here to check the CSx */
+ write_register(this, SX932x_CPSRD, ii);
+ msleep(100);
+ read_register(this, SX932x_USEMSB, &msb);
+ read_register(this, SX932x_USELSB, &lsb);
+ LOG_INFO("sx9325 cs%d USEFUL msb = 0x%x, lsb = 0x%x\n",
+ ii, msb, lsb);
+ read_register(this, SX932x_AVGMSB, &msb);
+ read_register(this, SX932x_AVGLSB, &lsb);
+ LOG_INFO("sx9325 cs%d AVERAGE msb = 0x%x, lsb = 0x%x\n",
+ ii, msb, lsb);
+ read_register(this, SX932x_DIFFMSB, &msb);
+ read_register(this, SX932x_DIFFLSB, &lsb);
+ LOG_INFO("sx9325 cs%d DIFF msb = 0x%x, lsb = 0x%x\n",
+ ii, msb, lsb);
+ read_register(this, SX932x_OFFSETMSB, &msb);
+ read_register(this, SX932x_OFFSETLSB, &lsb);
+ LOG_INFO("sx9325 cs%d OFFSET msb = 0x%x, lsb = 0x%x\n",
+ ii, msb, lsb);
+ }
+ }
+}
+
+/**
+ * brief Initialize I2C config from platform data
+ * param this Pointer to main parent struct
+ *
+ */
+static void hw_init(psx93XX_t this)
+{
+ psx9325_t pDevice = 0;
+ psx9325_platform_data_t pdata = 0;
+ int i = 0;
+ /* configure device */
+ LOG_INFO("Going to Setup I2C Registers\n");
+ pDevice = this->pDevice;
+ pdata = pDevice->hw;
+ if (this && pDevice && pdata) {
+ while (i < pdata->i2c_reg_num) {
+ /* Write all registers/values contained in i2c_reg */
+ if (sx9325_debug_enable)
+ LOG_DBG("Going to Write Reg: \
+ 0x%x Value: 0x%x\n",
+ pdata->pi2c_reg[i].reg,
+ pdata->pi2c_reg[i].val);
+ write_register(this, pdata->pi2c_reg[i].reg,
+ pdata->pi2c_reg[i].val);
+ i++;
+ }
+
+ write_register(this, SX932x_CTRL1_REG,
+ this->board->cust_prox_ctrl0);
+ write_register(this, SX932x_CPSRD,
+ this->board->cust_raw_data_channel);
+ } else {
+ LOG_ERR("ERROR! platform data 0x%p\n", pDevice->hw);
+ /* Force to touched if error */
+ ForcetoTouched(this);
+ LOG_INFO("Hardware_init-ForcetoTouched()\n");
+ }
+}
+
+/**
+ * fn static int initialize(psx93XX_t this)
+ * brief Performs all initialization needed to configure the device
+ * param this Pointer to main parent struct
+ * return Last used command's return value (negative if error)
+ */
+static int initialize(psx93XX_t this)
+{
+ int ret;
+
+ if (this) {
+ /* prepare reset by disabling any irq handling */
+ this->irq_disabled = 1;
+ disable_irq(this->irq);
+ /* perform a reset */
+ ret = write_register(this, SX932x_SOFTRESET_REG,
+ SX932x_SOFTRESET);
+ if (ret < 0)
+ goto error_exit;
+ /* wait until the reset has finished by monitoring NIRQ */
+ LOG_INFO("Software Reset. Waiting device back to continue.\n");
+ /* just sleep for awhile instead of using a loop
+ * with reading irq status */
+ msleep(300);
+ /*
+ * while(this->get_nirq_low && this->get_nirq_low())
+ * { read_regStat(this); }
+ */
+ LOG_INFO("Device back from the reset, continuing. NIRQ = %d\n",
+ this->get_nirq_low(this->board->irq_gpio));
+ hw_init(this);
+ msleep(100); /* make sure everything is running */
+ ret = manual_offset_calibration(this);
+ if (ret < 0)
+ goto error_exit;
+ /* re-enable interrupt handling */
+ enable_irq(this->irq);
+ this->irq_disabled = 0;
+
+ /* make sure no interrupts are pending
+ * since enabling irq will only
+ * work on next falling edge */
+ read_regStat(this);
+ LOG_INFO("Exiting initialize(). NIRQ = %d\n",
+ this->get_nirq_low(this->board->irq_gpio));
+ programming_done = ACTIVE;
+ return 0;
+ }
+ return -ENOMEM;
+
+error_exit:
+ programming_done = IDLE;
+ return ret;
+}
+
+/**
+ * brief Handle what to do when a touch occurs
+ * param this Pointer to main parent struct
+ */
+static void touchProcess(psx93XX_t this)
+{
+ int counter = 0;
+ u8 i = 0;
+ u8 Lsb,Msb;
+ int numberOfButtons = 0;
+ psx9325_t pDevice = NULL;
+ struct _buttonInfo *buttons = NULL;
+ struct input_dev *input_top = NULL;
+ struct input_dev *input_bottom = NULL;
+ struct _buttonInfo *pCurrentButton = NULL;
+ struct sx9325_platform_data *board;
+
+ pDevice = this->pDevice;
+ board = this->board;
+ if (this && pDevice) {
+ if (sx9325_debug_enable)
+ LOG_DBG("Inside touchProcess()\n");
+
+ read_register(this, SX932x_STAT0_REG, &Lsb);
+ read_register(this, SX932x_STAT1_REG, &Msb);
+ Lsb = (u8)(Lsb&0x0F);
+ Msb = (u8)(Msb&0x0F);
+ i = (u8)((Msb<<4)|Lsb);
+
+ buttons = pDevice->pbuttonInformation->buttons;
+ input_top = pDevice->pbuttonInformation->input_top;
+ input_bottom = pDevice->pbuttonInformation->input_bottom;
+ numberOfButtons = pDevice->pbuttonInformation->buttonSize;
+
+ if (unlikely((buttons == NULL) || (input_top == NULL) ||
+ (input_bottom == NULL))) {
+ LOG_ERR("ERROR!! buttons or input NULL!!!\n");
+ return;
+ }
+
+ for (counter = 0; counter < numberOfButtons; counter++) {
+ pCurrentButton = &buttons[counter];
+ if (pCurrentButton == NULL) {
+ LOG_ERR("ERR!current button index: %d NULL!\n",
+ counter);
+ return; /* ERRORR!!!! */
+ }
+ switch (pCurrentButton->state) {
+ case IDLE: /* Button is being in far state! */
+ if ((i & pCurrentButton->mask) ==
+ pCurrentButton->mask) {
+ LOG_INFO("CS %d State=BODY.\n",
+ counter);
+ if (board->cap_channel_top == counter) {
+ input_report_abs(input_top,
+ ABS_DISTANCE, 2);
+ input_sync(input_top);
+ } else if (board->cap_channel_bottom ==
+ counter) {
+ input_report_abs(input_bottom,
+ ABS_DISTANCE, 2);
+ input_sync(input_bottom);
+ }
+ pCurrentButton->state = S_BODY;
+ last_val = 2;
+ } else if ((i & pCurrentButton->mask) ==
+ (pCurrentButton->mask & 0x0f)) {
+ LOG_INFO("CS %d State=PROX.\n",
+ counter);
+ if (board->cap_channel_top == counter) {
+ input_report_abs(input_top,
+ ABS_DISTANCE, 1);
+ input_sync(input_top);
+ } else if (board->cap_channel_bottom ==
+ counter) {
+ input_report_abs(input_bottom,
+ ABS_DISTANCE, 1);
+ input_sync(input_bottom);
+ }
+ pCurrentButton->state = S_PROX;
+ last_val = 0;
+ } else {
+ if (sx9325_debug_enable)
+ LOG_DBG("CS %d still in"
+ "IDLE State.\n",
+ counter);
+ }
+ break;
+ case S_PROX: /* Button is being in proximity! */
+ if ((i & pCurrentButton->mask) ==
+ pCurrentButton->mask) {
+ LOG_INFO("CS %d State=BODY.\n",
+ counter);
+ if (board->cap_channel_top == counter) {
+ input_report_abs(input_top,
+ ABS_DISTANCE, 2);
+ input_sync(input_top);
+ } else if (board->cap_channel_bottom ==
+ counter) {
+ input_report_abs(input_bottom,
+ ABS_DISTANCE, 2);
+ input_sync(input_bottom);
+ }
+ pCurrentButton->state = S_BODY;
+ last_val = 2;
+ } else if ((i & pCurrentButton->mask) ==
+ (pCurrentButton->mask & 0x0f)) {
+ if (sx9325_debug_enable)
+ LOG_INFO("CS %d still in PROX State.\n",
+ counter);
+ } else{
+ if (sx9325_debug_enable)
+ LOG_DBG("CS %d State=IDLE.\n",
+ counter);
+ if (board->cap_channel_top == counter) {
+ input_report_abs(input_top,
+ ABS_DISTANCE, 0);
+ input_sync(input_top);
+ } else if (board->cap_channel_bottom ==
+ counter) {
+ input_report_abs(input_bottom,
+ ABS_DISTANCE, 0);
+ input_sync(input_bottom);
+ }
+ pCurrentButton->state = IDLE;
+ last_val = 0;
+ }
+ break;
+ case S_BODY: /* Button is being in 0mm! */
+ if ((i & pCurrentButton->mask) ==
+ pCurrentButton->mask) {
+ if (sx9325_debug_enable)
+ LOG_INFO("CS %d still in BODY State.\n",
+ counter);
+ } else if ((i & pCurrentButton->mask) ==
+ (pCurrentButton->mask & 0x0f)) {
+ LOG_INFO("CS %d State=PROX.\n",
+ counter);
+ if (board->cap_channel_top == counter) {
+ input_report_abs(input_top,
+ ABS_DISTANCE, 1);
+ input_sync(input_top);
+ } else if (board->cap_channel_bottom
+ == counter) {
+ input_report_abs(input_bottom,
+ ABS_DISTANCE, 1);
+ input_sync(input_bottom);
+ }
+ pCurrentButton->state = S_PROX;
+ last_val = 1;
+ } else{
+ if (sx9325_debug_enable)
+ LOG_DBG("CS %d State=IDLE.\n",
+ counter);
+ if (board->cap_channel_top == counter) {
+ input_report_abs(input_top,
+ ABS_DISTANCE, 0);
+ input_sync(input_top);
+ } else if (board->cap_channel_bottom
+ == counter) {
+ input_report_abs(input_bottom,
+ ABS_DISTANCE, 0);
+ input_sync(input_bottom);
+ }
+ pCurrentButton->state = IDLE;
+ last_val = 0;
+ }
+ break;
+ /* Shouldn't be here, device only
+ * allowed ACTIVE or IDLE */
+ default:
+ break;
+ };
+ }
+ if (sx9325_debug_enable)
+ LOG_DBG("Leaving touchProcess()\n");
+ }
+}
+
+static int sx9325_get_nirq_state(unsigned irq_gpio)
+{
+ if (irq_gpio)
+ return !gpio_get_value(irq_gpio);
+
+ LOG_ERR("sx9325 irq_gpio is not set.");
+ return -EINVAL;
+}
+
+static struct _totalButtonInformation smtcButtonInformation = {
+ .buttons = psmtcButtons,
+ .buttonSize = ARRAY_SIZE(psmtcButtons),
+};
+
+/**
+ *fn static void sx9325_reg_setup_init(struct i2c_client *client)
+ *brief read reg val form dts
+ * reg_array_len for regs needed change num
+ * data_array_val's format <reg val ...>
+ */
+static void sx9325_reg_setup_init(struct i2c_client *client)
+{
+ u32 data_array_len = 0;
+ u32 *data_array;
+ int ret, i, j;
+ struct device_node *np = client->dev.of_node;
+
+ ret = of_property_read_u32(np, "reg_array_len", &data_array_len);
+ if (ret < 0) {
+ LOG_ERR("data_array_len read error");
+ return;
+ }
+ data_array = kmalloc(data_array_len * 2 * sizeof(u32), GFP_KERNEL);
+ ret = of_property_read_u32_array(np, "reg_array_val",
+ data_array,
+ data_array_len*2);
+ if (ret < 0) {
+ LOG_ERR("data_array_val read error");
+ return;
+ }
+ for (i = 0; i < ARRAY_SIZE(sx9325_i2c_reg_setup); i++) {
+ for (j = 0; j < data_array_len*2; j += 2) {
+ if (data_array[j] == sx9325_i2c_reg_setup[i].reg) {
+ sx9325_i2c_reg_setup[i].val = data_array[j+1];
+ LOG_INFO("read dtsi 0x%02x:0x%02x set reg\n",
+ data_array[j], data_array[j+1]);
+ }
+ }
+ }
+ kfree(data_array);
+ /*for test*/
+ /*for (i = 0; i < ARRAY_SIZE(sx9325_i2c_reg_setup); i++) {
+ dev_err(&client->dev, "%x:%x",
+ sx9325_i2c_reg_setup[i].reg,
+ sx9325_i2c_reg_setup[i].val);
+ }*/
+}
+
+static void sx9325_platform_data_of_init(struct i2c_client *client,
+ psx9325_platform_data_t pplatData)
+{
+ struct device_node *np = client->dev.of_node;
+ u32 scan_period, sensor_en, raw_data_channel;
+ u32 cap_channel_top, cap_channel_bottom;
+ int ret;
+
+ client->irq = of_get_gpio(np, 0);
+ pplatData->irq_gpio = client->irq;
+
+ ret = of_property_read_u32(np, "cap,use_channel", &sensor_en);
+ if (ret)
+ sensor_en = DUMMY_USE_CHANNEL;
+
+ ret = of_property_read_u32(np, "cap,use_channel_top", &cap_channel_top);
+ if (ret)
+ sensor_en = DUMMY_USE_CHANNEL;
+
+ ret = of_property_read_u32(np, "cap,use_channel_bottom",
+ &cap_channel_bottom);
+ if (ret)
+ sensor_en = DUMMY_USE_CHANNEL;
+
+ ret = of_property_read_u32(np, "cap,scan_period", &scan_period);
+ if (ret)
+ scan_period = DUMMY_SCAN_PERIOD;
+
+ ret = of_property_read_u32(np, "cap,raw_data_channel",
+ &raw_data_channel);
+ if (ret)
+ raw_data_channel = DUMMY_RAW_DATA_CHANNEL;
+
+ pplatData->cust_prox_ctrl0 = (scan_period << 4) | sensor_en;
+ pplatData->cust_raw_data_channel = raw_data_channel;
+ pplatData->cap_channel_top = (int)cap_channel_top;
+ pplatData->cap_channel_bottom = (int)cap_channel_bottom;
+
+ pplatData->get_is_nirq_low = sx9325_get_nirq_state;
+ pplatData->init_platform_hw = NULL;
+ /* pointer to an exit function. Here in case needed in the future */
+ /*
+ *.exit_platform_hw = sx9325_exit_ts,
+ */
+ pplatData->exit_platform_hw = NULL;
+ sx9325_reg_setup_init(client);
+ pplatData->pi2c_reg = sx9325_i2c_reg_setup;
+ pplatData->i2c_reg_num = ARRAY_SIZE(sx9325_i2c_reg_setup);
+
+ pplatData->pbuttonInformation = &smtcButtonInformation;
+}
+
+static ssize_t reset_show(struct class *class,
+ struct class_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, 8, "%d\n", programming_done);
+}
+
+static ssize_t reset_store(struct class *class,
+ struct class_attribute *attr,
+ const char *buf, size_t count)
+{
+ psx93XX_t this = sx9325_sar_ptr;
+ psx9325_t pDevice = NULL;
+ struct input_dev *input_top = NULL;
+ struct input_dev *input_bottom = NULL;
+
+ pDevice = this->pDevice;
+ input_top = pDevice->pbuttonInformation->input_top;
+ input_bottom = pDevice->pbuttonInformation->input_bottom;
+
+ if (!count || (this == NULL))
+ return -EINVAL;
+
+ if (!strncmp(buf, "reset", 5) || !strncmp(buf, "1", 1))
+ write_register(this, SX932x_STAT2_REG, 0x0F);
+
+ input_report_abs(input_top, ABS_DISTANCE, 0);
+ input_sync(input_top);
+ input_report_abs(input_bottom, ABS_DISTANCE, 0);
+ input_sync(input_bottom);
+
+ return count;
+}
+
+static CLASS_ATTR_RW(reset);
+
+static ssize_t enable_show(struct class *class,
+ struct class_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, 8, "%d\n", mEnabled);
+}
+
+static ssize_t enable_store(struct class *class,
+ struct class_attribute *attr,
+ const char *buf, size_t count)
+{
+ psx93XX_t this = sx9325_sar_ptr;
+ psx9325_t pDevice = NULL;
+ struct input_dev *input_top = NULL;
+ struct input_dev *input_bottom = NULL;
+
+ pDevice = this->pDevice;
+ input_top = pDevice->pbuttonInformation->input_top;
+ input_bottom = pDevice->pbuttonInformation->input_bottom;
+
+ if (!count || (this == NULL))
+ return -EINVAL;
+
+ if ((!strncmp(buf, "1", 1)) && (mEnabled == 0)) {
+ LOG_INFO("enable cap sensor\n");
+ initialize(this);
+
+ input_report_abs(input_top, ABS_DISTANCE, 0);
+ input_sync(input_top);
+ input_report_abs(input_bottom, ABS_DISTANCE, 0);
+ input_sync(input_bottom);
+ mEnabled = 1;
+ } else if ((!strncmp(buf, "0", 1)) && (mEnabled == 1)) {
+ LOG_INFO("disable cap sensor\n");
+
+ write_register(this, SX932x_CTRL1_REG, 0x10);
+ write_register(this, SX932x_IRQ_ENABLE_REG, 0x00);
+
+ input_report_abs(input_top, ABS_DISTANCE, -1);
+ input_sync(input_top);
+ input_report_abs(input_bottom, ABS_DISTANCE, -1);
+ input_sync(input_bottom);
+ mEnabled = 0;
+ } else {
+ LOG_ERR("unknown enable symbol\n");
+ }
+
+ return count;
+}
+
+#ifdef USE_SENSORS_CLASS
+static int capsensor_set_enable(struct sensors_classdev *sensors_cdev,
+ unsigned int enable)
+{
+ psx93XX_t this = sx9325_sar_ptr;
+ psx9325_t pDevice = NULL;
+ struct input_dev *input_top = NULL;
+ struct input_dev *input_bottom = NULL;
+
+ pDevice = this->pDevice;
+ input_top = pDevice->pbuttonInformation->input_top;
+ input_bottom = pDevice->pbuttonInformation->input_bottom;
+
+ if ((enable == 1) && (mEnabled == 0)) {
+ LOG_INFO("enable cap sensor\n");
+ initialize(this);
+
+ input_report_abs(input_top, ABS_DISTANCE, 0);
+ input_sync(input_top);
+ input_report_abs(input_bottom, ABS_DISTANCE, 0);
+ input_sync(input_bottom);
+ mEnabled = 1;
+ } else if ((enable == 0) && (mEnabled == 1)) {
+ LOG_INFO("disable cap sensor\n");
+
+ write_register(this, SX932x_CTRL1_REG, 0x00);
+ write_register(this, SX932x_IRQ_ENABLE_REG, 0x00);
+
+ input_report_abs(input_top, ABS_DISTANCE, -1);
+ input_sync(input_top);
+ input_report_abs(input_bottom, ABS_DISTANCE, -1);
+ input_sync(input_bottom);
+ mEnabled = 0;
+ } else {
+ LOG_ERR("unknown enable symbol\n");
+ }
+
+ return 0;
+}
+#endif
+
+static CLASS_ATTR_RW(enable);
+static ssize_t reg_show(struct class *class,
+ struct class_attribute *attr,
+ char *buf)
+{
+ u16 i = 0;
+ u8 reg_value = 0;
+ u16 register_number = 0;
+ psx93XX_t this = sx9325_sar_ptr;
+ char *p = buf;
+
+ if (this->read_flag) {
+ this->read_flag = 0;
+ read_register(this, this->read_reg, ®_value);
+ p += snprintf(p, PAGE_SIZE, "%02x\n", reg_value);
+ return (p-buf);
+ }
+
+ read_register(this, SX932x_IRQSTAT_REG, ®_value);
+ p += snprintf(p, PAGE_SIZE, "IRQSTAT(0x%02x)=0x%02x\n",
+ SX932x_IRQSTAT_REG, reg_value);
+
+ read_register(this, SX932x_STAT0_REG, ®_value);
+ p += snprintf(p, PAGE_SIZE, "STAT0(0x%02x)=0x%02x\n",
+ SX932x_STAT0_REG, reg_value);
+
+ read_register(this, SX932x_STAT1_REG, ®_value);
+ p += snprintf(p, PAGE_SIZE, "STAT1(0x%02x)=0x%02x\n",
+ SX932x_STAT1_REG, reg_value);
+
+ read_register(this, SX932x_STAT2_REG, ®_value);
+ p += snprintf(p, PAGE_SIZE, "STAT2(0x%02x)=0x%02x\n",
+ SX932x_STAT2_REG, reg_value);
+
+ read_register(this, SX932x_STAT3_REG, ®_value);
+ p += snprintf(p, PAGE_SIZE, "STAT3(0x%02x)=0x%02x\n",
+ SX932x_STAT3_REG, reg_value);
+
+ register_number = sizeof(sx9325_i2c_reg_setup)/sizeof(smtc_reg_data_t);
+
+ for (i = 0; i < register_number; i++)
+ {
+ read_register(this, sx9325_i2c_reg_setup[i].reg, ®_value);
+ p += snprintf(p, PAGE_SIZE, "ENABLE(0x%02x)=0x%02x\n",
+ sx9325_i2c_reg_setup[i].reg, reg_value);
+ }
+
+ read_register(this, SX932x_CPSRD, ®_value);
+ p += snprintf(p, PAGE_SIZE, "CPSRD(0x%02x)=0x%02x\n",
+ SX932x_CPSRD, reg_value);
+
+ read_register(this, SX932x_USEMSB, ®_value);
+ p += snprintf(p, PAGE_SIZE, "USEMSB(0x%02x)=0x%02x\n",
+ SX932x_USEMSB, reg_value);
+
+ read_register(this, SX932x_USELSB, ®_value);
+ p += snprintf(p, PAGE_SIZE, "USELSB(0x%02x)=0x%02x\n",
+ SX932x_USELSB, reg_value);
+
+ read_register(this, SX932x_AVGMSB, ®_value);
+ p += snprintf(p, PAGE_SIZE, "AVGMSB(0x%02x)=0x%02x\n",
+ SX932x_AVGMSB, reg_value);
+
+ read_register(this, SX932x_AVGLSB, ®_value);
+ p += snprintf(p, PAGE_SIZE, "AVGLSB(0x%02x)=0x%02x\n",
+ SX932x_AVGLSB, reg_value);
+
+ read_register(this, SX932x_DIFFMSB, ®_value);
+ p += snprintf(p, PAGE_SIZE, "DIFFMSB(0x%02x)=0x%02x\n",
+ SX932x_DIFFMSB, reg_value);
+
+ read_register(this, SX932x_DIFFLSB, ®_value);
+ p += snprintf(p, PAGE_SIZE, "DIFFLSB(0x%02x)=0x%02x\n",
+ SX932x_DIFFLSB, reg_value);
+
+ read_register(this, SX932x_OFFSETMSB, ®_value);
+ p += snprintf(p, PAGE_SIZE, "OFFSETMSB(0x%02x)=0x%02x\n",
+ SX932x_OFFSETMSB, reg_value);
+
+ read_register(this, SX932x_OFFSETLSB, ®_value);
+ p += snprintf(p, PAGE_SIZE, "OFFSETLSB(0x%02x)=0x%02x\n",
+ SX932x_OFFSETLSB, reg_value);
+
+ reg_value = gpio_get_value(this->board->irq_gpio);
+ p += snprintf(p, PAGE_SIZE, "NIRQ=%d\n", reg_value);
+
+ return (p-buf);
+}
+
+static ssize_t reg_store(struct class *class,
+ struct class_attribute *attr,
+ const char *buf, size_t count)
+{
+ psx93XX_t this = sx9325_sar_ptr;
+ unsigned int val, reg, opt;
+
+ if (strcmp("select_ch0\n", buf) == 0) {
+ write_register(this, SX932x_CPSRD, 0x00);
+ } else if (strcmp("select_ch1\n", buf) == 0) {
+ write_register(this, SX932x_CPSRD, 0x01);
+ } else if (strcmp("select_ch2\n", buf) == 0) {
+ write_register(this, SX932x_CPSRD, 0x02);
+ } else if (strcmp("calibrate\n", buf) == 0) {
+ write_register(this, SX932x_STAT2_REG, 0x0f);
+ } else if (sscanf(buf, "%x,%x,%x", ®, &val, &opt) == 3) {
+ LOG_DBG("%s, read reg = 0x%02x\n", __func__, *(u8 *)®);
+ this->read_reg = *((u8 *)®);
+ this->read_flag = 1;
+ } else if (sscanf(buf, "%x,%x", ®, &val) == 2) {
+ LOG_DBG("%s,reg = 0x%02x, val = 0x%02x\n",
+ __func__, *(u8 *)®, *(u8 *)&val);
+ write_register(this, *((u8 *)®), *((u8 *)&val));
+ }
+
+ return count;
+}
+
+static CLASS_ATTR_RW(reg);
+
+static void ps_notify_callback_work(struct work_struct *work)
+{
+ psx93XX_t this = container_of(work, sx93XX_t, ps_notify_work);
+ psx9325_t pDevice = NULL;
+ struct input_dev *input_top = NULL;
+ struct input_dev *input_bottom = NULL;
+ int ret = 0;
+
+ pDevice = this->pDevice;
+ input_top = pDevice->pbuttonInformation->input_top;
+ input_bottom = pDevice->pbuttonInformation->input_bottom;
+
+ mutex_lock(&this->mutex);
+ if (mEnabled) {
+ LOG_INFO("Usb insert,going to force calibrate\n");
+ ret = write_register(this, SX932x_CTRL1_REG, 0x00);
+ if (ret < 0)
+ LOG_ERR(" Usb insert,calibrate cap sensor failed\n");
+ msleep(100);
+ ret = write_register(this, SX932x_CTRL1_REG,
+ this->board->cust_prox_ctrl0);
+ if (ret < 0)
+ LOG_ERR(" Usb insert,enabel cap sensor failed\n");
+ }
+ mutex_unlock(&this->mutex);
+ input_report_abs(input_top, ABS_DISTANCE, 0);
+ input_sync(input_top);
+ input_report_abs(input_bottom, ABS_DISTANCE, 0);
+ input_sync(input_bottom);
+}
+
+static int ps_get_state(struct power_supply *psy, bool *present)
+{
+ union power_supply_propval pval = { 0 };
+ int retval;
+
+ retval = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,
+ &pval);
+ if (retval) {
+ LOG_ERR("%s psy get property failed\n", psy->desc->name);
+ return retval;
+ }
+ *present = (pval.intval) ? true : false;
+ LOG_INFO("%s is %s\n", psy->desc->name,
+ (*present) ? "present" : "not present");
+ return 0;
+}
+
+static int ps_notify_callback(struct notifier_block *self,
+ unsigned long event, void *p)
+{
+ psx93XX_t this = container_of(self, sx93XX_t, ps_notif);
+ struct power_supply *psy = p;
+ bool present;
+ int retval;
+
+ if ((event == PSY_EVENT_PROP_ADDED || event == PSY_EVENT_PROP_CHANGED)
+ && psy && psy->desc->get_property && psy->desc->name &&
+ !strncmp(psy->desc->name, "usb", sizeof("usb"))) {
+ LOG_INFO("ps notification: event = %lu\n", event);
+ retval = ps_get_state(psy, &present);
+ if (retval) {
+ LOG_ERR("psy get property failed\n");
+ return retval;
+ }
+
+ if (event == PSY_EVENT_PROP_CHANGED) {
+ if (this->ps_is_present == present) {
+ LOG_INFO("ps present state not change\n");
+ return 0;
+ }
+ }
+ this->ps_is_present = present;
+ schedule_work(&this->ps_notify_work);
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_FB)
+static void fb_notify_resume_work(struct work_struct *work)
+{
+ psx93XX_t this = container_of(work, sx93XX_t, fb_notify_work);
+ psx9325_t pDevice = NULL;
+ struct input_dev *input_top = NULL;
+ struct input_dev *input_bottom = NULL;
+ int ret = 0;
+
+ pDevice = this->pDevice;
+ input_top = pDevice->pbuttonInformation->input_top;
+ input_bottom = pDevice->pbuttonInformation->input_bottom;
+
+ if (sx9325_debug_enable)
+ LOG_INFO("Lcd suspend/resume event,going to force reset\n");
+ ret = write_register(this, SX932x_STAT2_REG, 0x0f);
+ if (ret < 0)
+ LOG_ERR(" Lcd suspend/resume,reset cap sensor failed\n");
+}
+
+static int fb_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct fb_event *evdata = data;
+ int *blank;
+ psx93XX_t this = container_of(self, sx93XX_t, fb_notif);
+
+ if ((event == FB_EVENT_BLANK) &&
+ evdata && evdata->data) {
+ blank = evdata->data;
+ if ((*blank == FB_BLANK_POWERDOWN)
+ || (*blank == FB_BLANK_UNBLANK)) {
+ if (sx9325_debug_enable)
+ LOG_INFO("fb event = %lu blank = %d\n",
+ event, *blank);
+ schedule_work(&this->fb_notify_work);
+ }
+ }
+
+ return 0;
+}
+#endif
+
+static struct class capsense_class = {
+ .name = "capsense",
+ .owner = THIS_MODULE,
+};
+
+/**
+ * fn static int sx9325_probe(struct i2c_client *client, const struct i2c_device_id *id)
+ * brief Probe function
+ * param client pointer to i2c_client
+ * param id pointer to i2c_device_id
+ * return Whether probe was successful
+ */
+static int sx9325_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ psx93XX_t this = 0;
+ psx9325_t pDevice = 0;
+ psx9325_platform_data_t pplatData = 0;
+ int ret;
+ struct input_dev *input_top = NULL;
+ struct input_dev *input_bottom = NULL;
+ struct power_supply *psy = NULL;
+
+ LOG_INFO("sx9325_probe()\n");
+
+ /* detect if sx9325 exist or not */
+ if (sx9325_detect(client) == 0)
+ return -ENODEV;
+
+ pplatData = kzalloc(sizeof(pplatData), GFP_KERNEL);
+ sx9325_platform_data_of_init(client, pplatData);
+ client->dev.platform_data = pplatData;
+
+ if (!pplatData) {
+ LOG_ERR("platform data is required!\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -EIO;
+
+ /* create memory for main struct */
+ this = kzalloc(sizeof(sx93XX_t), GFP_KERNEL);
+ LOG_INFO("\t Initialized Main Memory: 0x%p\n", this);
+
+ if (this) {
+ /* In case we need to reinitialize data
+ * (e.q. if suspend reset device) */
+ this->init = initialize;
+ /* shortcut to read status of interrupt */
+ this->refreshStatus = read_regStat;
+ /* pointer to function from platform data to get pendown
+ * (1->NIRQ=0, 0->NIRQ=1) */
+ this->get_nirq_low = pplatData->get_is_nirq_low;
+ /* save irq in case we need to reference it */
+ this->irq = gpio_to_irq(client->irq);
+ /* do we need to create an irq timer after interrupt ? */
+ this->useIrqTimer = 0;
+ this->board = pplatData;
+ /* Setup function to call on corresponding reg irq source bit */
+ if (MAX_NUM_STATUS_BITS >= 8) {
+ this->statusFunc[0] = 0; /* TXEN_STAT */
+ this->statusFunc[1] = 0; /* UNUSED */
+ this->statusFunc[2] = touchProcess; /* BODY/TABLE */
+ this->statusFunc[3] = read_rawData; /* CONV_STAT */
+ this->statusFunc[4] = 0; /* COMP_STAT */
+ this->statusFunc[5] = touchProcess; /* RELEASE_STAT */
+ this->statusFunc[6] = touchProcess; /* TOUCH_STAT */
+ this->statusFunc[7] = 0; /* RESET_STAT */
+ }
+
+ /* setup i2c communication */
+ this->bus = client;
+ i2c_set_clientdata(client, this);
+
+ /* record device struct */
+ this->pdev = &client->dev;
+
+ /* create memory for device specific struct */
+ this->pDevice = pDevice = kzalloc(sizeof(sx9325_t), GFP_KERNEL);
+ LOG_INFO("\t Initialized Device Specific Memory: 0x%p\n",
+ pDevice);
+ sx9325_sar_ptr = this;
+ if (pDevice) {
+ /* for accessing items in user data (e.g. calibrate) */
+ ret = sysfs_create_group(&client->dev.kobj,
+ &sx9325_attr_group);
+
+
+ /* Check if we hava a platform
+ * initialization function to call*/
+ if (pplatData->init_platform_hw)
+ pplatData->init_platform_hw();
+
+ /* Add Pointer to main platform data struct */
+ pDevice->hw = pplatData;
+
+ /* Initialize the button information
+ * initialized with keycodes */
+ pDevice->pbuttonInformation =
+ pplatData->pbuttonInformation;
+
+ /* Create the input device */
+ input_top = input_allocate_device();
+ if (!input_top)
+ return -ENOMEM;
+
+ /* Set all the keycodes */
+ __set_bit(EV_ABS, input_top->evbit);
+ input_set_abs_params(input_top, ABS_DISTANCE,
+ -1, 100, 0, 0);
+ /* save the input pointer and finish initialization */
+ pDevice->pbuttonInformation->input_top =
+ input_top;
+ input_top->name = "SX9325 Cap Touch top";
+ if (input_register_device(input_top)) {
+ LOG_ERR("add top cap touch unsuccess\n");
+ return -ENOMEM;
+ }
+ /* Create the input device */
+ input_bottom = input_allocate_device();
+ if (!input_bottom)
+ return -ENOMEM;
+ /* Set all the keycodes */
+ __set_bit(EV_ABS, input_bottom->evbit);
+ input_set_abs_params(input_bottom, ABS_DISTANCE,
+ -1, 100, 0, 0);
+ /* save the input pointer and finish initialization */
+ pDevice->pbuttonInformation->input_bottom =
+ input_bottom;
+ /* save the input pointer and finish initialization */
+ input_bottom->name = "SX9325 Cap Touch bottom";
+ if (input_register_device(input_bottom)) {
+ LOG_ERR("add bottom cap touch unsuccess\n");
+ return -ENOMEM;
+ }
+ }
+
+ ret = class_register(&capsense_class);
+ if (ret < 0) {
+ LOG_ERR("Create fsys class failed (%d)\n", ret);
+ return ret;
+ }
+
+ ret = class_create_file(&capsense_class, &class_attr_reset);
+ if (ret < 0) {
+ LOG_ERR("Create reset file failed (%d)\n", ret);
+ return ret;
+ }
+
+ ret = class_create_file(&capsense_class, &class_attr_enable);
+ if (ret < 0) {
+ LOG_ERR("Create enable file failed (%d)\n", ret);
+ return ret;
+ }
+
+ ret = class_create_file(&capsense_class, &class_attr_reg);
+ if (ret < 0) {
+ LOG_ERR("Create reg file failed (%d)\n", ret);
+ return ret;
+ }
+
+#ifdef USE_SENSORS_CLASS
+ sensors_capsensor_top_cdev.sensors_enable =
+ capsensor_set_enable;
+ sensors_capsensor_top_cdev.sensors_poll_delay = NULL;
+ ret = sensors_classdev_register(&input_top->dev,
+ &sensors_capsensor_top_cdev);
+ if (ret < 0)
+ LOG_ERR("create top cap sensor_class file \
+ failed (%d)\n", ret);
+ sensors_capsensor_bottom_cdev.sensors_enable =
+ capsensor_set_enable;
+ sensors_capsensor_bottom_cdev.sensors_poll_delay = NULL;
+ ret = sensors_classdev_register(&input_bottom->dev,
+ &sensors_capsensor_bottom_cdev);
+ if (ret < 0)
+ LOG_ERR("create bottom cap sensor_class file \
+ failed (%d)\n", ret);
+#endif
+ pplatData->cap_vdd = regulator_get(&client->dev, "cap_vdd");
+ if (IS_ERR(pplatData->cap_vdd)) {
+ if (PTR_ERR(pplatData->cap_vdd) == -EPROBE_DEFER) {
+ ret = PTR_ERR(pplatData->cap_vdd);
+ goto err_vdd_defer;
+ }
+ LOG_ERR("%s: Failed to get regulator\n",
+ __func__);
+ } else {
+ int error = regulator_enable(pplatData->cap_vdd);
+
+ if (error) {
+ regulator_put(pplatData->cap_vdd);
+ LOG_ERR("%s: Error %d enable regulator\n",
+ __func__, error);
+ return error;
+ }
+ pplatData->cap_vdd_en = true;
+ LOG_INFO("cap_vdd regulator is %s\n",
+ regulator_is_enabled(pplatData->cap_vdd) ?
+ "on" : "off");
+ }
+#if 0
+ pplatData->cap_svdd = regulator_get(&client->dev, "cap_svdd");
+ if (!IS_ERR(pplatData->cap_svdd)) {
+ ret = regulator_enable(pplatData->cap_svdd);
+ if (ret) {
+ regulator_put(pplatData->cap_svdd);
+ LOG_ERR("Failed to enable cap_svdd\n");
+ goto err_svdd_error;
+ }
+ pplatData->cap_svdd_en = true;
+ LOG_INFO("cap_svdd regulator is %s\n",
+ regulator_is_enabled(pplatData->cap_svdd) ?
+ "on" : "off");
+ } else {
+ ret = PTR_ERR(pplatData->cap_vdd);
+ if (ret == -EPROBE_DEFER)
+ goto err_svdd_error;
+ }
+#endif
+ sx93XX_sar_init(this);
+
+ write_register(this, SX932x_CTRL1_REG, 0x10);
+ write_register(this, SX932x_IRQ_ENABLE_REG, 0x00);
+ mEnabled = 0;
+
+ INIT_WORK(&this->ps_notify_work, ps_notify_callback_work);
+ this->ps_notif.notifier_call = ps_notify_callback;
+ ret = power_supply_reg_notifier(&this->ps_notif);
+ if (ret) {
+ LOG_ERR(
+ "Unable to register ps_notifier: %d\n", ret);
+ goto free_ps_notifier;
+ }
+
+ psy = power_supply_get_by_name("usb");
+ if (psy) {
+ ret = ps_get_state(psy, &this->ps_is_present);
+ if (ret) {
+ LOG_ERR(
+ "psy get property failed rc=%d\n",
+ ret);
+ goto free_ps_notifier;
+ }
+ }
+
+#if defined(CONFIG_FB)
+ INIT_WORK(&this->fb_notify_work, fb_notify_resume_work);
+ this->fb_notif.notifier_call = fb_notifier_callback;
+ ret = fb_register_client(&this->fb_notif);
+ if (ret) {
+ LOG_ERR("Unable to register fb_notifier: %d\n", ret);
+ goto free_fb_notifier;
+ }
+#endif
+ return 0;
+ }
+ return -ENOMEM;
+
+free_fb_notifier:
+ power_supply_unreg_notifier(&this->ps_notif);
+
+free_ps_notifier:
+ LOG_ERR("%s free ps notifier:.\n", __func__);
+ regulator_disable(pplatData->cap_svdd);
+ regulator_put(pplatData->cap_svdd);
+#if 0
+err_svdd_error:
+ LOG_ERR("%s svdd defer.\n", __func__);
+ regulator_disable(pplatData->cap_vdd);
+ regulator_put(pplatData->cap_vdd);
+#endif
+err_vdd_defer:
+ LOG_ERR("%s input free device.\n", __func__);
+ input_free_device(input_top);
+ input_free_device(input_bottom);
+
+ return ret;
+}
+
+/**
+ * fn static int sx9325_remove(struct i2c_client *client)
+ * brief Called when device is to be removed
+ * param client Pointer to i2c_client struct
+ * return Value from sx93XX_sar_remove()
+ */
+static int sx9325_remove(struct i2c_client *client)
+{
+ psx9325_platform_data_t pplatData = 0;
+ psx9325_t pDevice = 0;
+ psx93XX_t this = i2c_get_clientdata(client);
+
+ pDevice = this->pDevice;
+ if (this && pDevice) {
+#if defined(CONFIG_FB)
+ fb_unregister_client(&this->fb_notif);
+#endif
+ power_supply_unreg_notifier(&this->ps_notif);
+
+#ifdef USE_SENSORS_CLASS
+ sensors_classdev_unregister(&sensors_capsensor_top_cdev);
+ sensors_classdev_unregister(&sensors_capsensor_bottom_cdev);
+#endif
+ input_unregister_device(pDevice->pbuttonInformation->input_top);
+ input_unregister_device(
+ pDevice->pbuttonInformation->input_bottom);
+
+ if (this->board->cap_svdd_en) {
+ regulator_disable(this->board->cap_svdd);
+ regulator_put(this->board->cap_svdd);
+ }
+
+ if (this->board->cap_vdd_en) {
+ regulator_disable(this->board->cap_vdd);
+ regulator_put(this->board->cap_vdd);
+ }
+#ifdef USE_SENSORS_CLASS
+ sensors_classdev_unregister(&sensors_capsensor_top_cdev);
+ sensors_classdev_unregister(&sensors_capsensor_bottom_cdev);
+#endif
+ sysfs_remove_group(&client->dev.kobj, &sx9325_attr_group);
+ pplatData = client->dev.platform_data;
+ if (pplatData && pplatData->exit_platform_hw)
+ pplatData->exit_platform_hw();
+ kfree(this->pDevice);
+ }
+ return sx93XX_sar_remove(this);
+}
+
+#if defined(USE_KERNEL_SUSPEND)
+/*====================================================*/
+/***** Kernel Suspend *****/
+static int sx9325_suspend(struct device *dev, pm_message_t mesg)
+{
+ psx93XX_t this = dev_get_drvdata(dev);
+
+ sx9325_irq_disable(this);
+ return 0;
+}
+/***** Kernel Resume *****/
+static int sx9325_resume(struct device *dev)
+{
+ psx93XX_t this = dev_get_drvdata(dev);
+
+ sx9325_irq_enable(this);
+ return 0;
+}
+/*====================================================*/
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id sx9325_match_tbl[] = {
+ { .compatible = "semtech,sx9325" ,},
+ { },
+};
+#endif
+
+static struct i2c_device_id sx9325_idtable[] = {
+ { DRIVER_NAME, 0 },
+ { }
+};
+
+static struct i2c_driver sx9325_driver = {
+
+ .probe = sx9325_probe,
+ .remove = sx9325_remove,
+ .id_table = sx9325_idtable,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+#if defined(USE_KERNEL_SUSPEND)
+ .suspend = sx9325_suspend,
+ .resume = sx9325_resume,
+#endif
+#ifdef CONFIG_OF
+ .of_match_table = sx9325_match_tbl,
+#endif
+ },
+
+};
+
+static int __init sx9325_init(void)
+{
+ int ret = 0;
+ ret = i2c_add_driver(&sx9325_driver);
+ if (ret) {
+ LOG_ERR("%s: failed to add i2c driver", __func__);
+ goto err_driver;
+ }
+
+ LOG_DBG("%s: finished\n", __func__);
+
+err_driver:
+ return ret;
+}
+static void __exit sx9325_exit(void)
+{
+ i2c_del_driver(&sx9325_driver);
+}
+
+#ifdef USE_THREADED_IRQ
+static void sx93XX_process_interrupt(psx93XX_t this, u8 nirqlow)
+{
+ int status = 0;
+ int counter = 0;
+
+ if (!this) {
+ LOG_ERR("sx93XX_worker_func, NULL sx93XX_t\n");
+ return;
+ }
+ /* since we are not in an interrupt don't need to disable irq. */
+ status = this->refreshStatus(this);
+ counter = -1;
+ if (sx9325_debug_enable)
+ LOG_DBG("Worker - Refresh Status %d\n", status);
+
+ while ((++counter) < MAX_NUM_STATUS_BITS) { /* counter start from MSB */
+ if (((status >> counter) & 0x01)
+ && (this->statusFunc[counter])) {
+ if (sx9325_debug_enable)
+ LOG_DBG("Function %d Pointer Found. Calling\n"
+ ,counter);
+ this->statusFunc[counter](this);
+ }
+ }
+ if (unlikely(this->useIrqTimer && nirqlow)) {
+ /* In case we need to send a timer for example on a touchscreen
+ * checking penup, perform this here
+ */
+ cancel_delayed_work(&this->dworker);
+ schedule_delayed_work(&this->dworker,
+ msecs_to_jiffies(this->irqTimeout));
+ LOG_INFO("Schedule Irq timer");
+ }
+}
+
+
+static void sx93XX_worker_func(struct work_struct *work)
+{
+ psx93XX_t this = 0;
+
+ if (work) {
+ this = container_of(work, sx93XX_t, dworker.work);
+ if (!this) {
+ LOG_ERR("sx93XX_worker_func, NULL sx93XX_t\n");
+ return;
+ }
+ if ((!this->get_nirq_low)
+ || (!this->get_nirq_low(this->board->irq_gpio))) {
+ /* only run if nirq is high */
+ sx93XX_process_interrupt(this, 0);
+ }
+ } else {
+ LOG_ERR("sx93XX_worker_func, NULL work_struct\n");
+ }
+}
+static irqreturn_t sx93XX_interrupt_thread(int irq, void *data)
+{
+ psx93XX_t this = 0;
+
+ this = data;
+
+ mutex_lock(&this->mutex);
+ if (sx9325_debug_enable)
+ LOG_DBG("sx93XX_irq\n");
+ if ((!this->get_nirq_low) || this->get_nirq_low(this->board->irq_gpio))
+ sx93XX_process_interrupt(this, 1);
+ else {
+ if (sx9325_debug_enable)
+ LOG_DBG("sx93XX_irq - nirq read high\n");
+ }
+ mutex_unlock(&this->mutex);
+ return IRQ_HANDLED;
+}
+#else
+static void sx93XX_schedule_work(psx93XX_t this, unsigned long delay)
+{
+ unsigned long flags;
+
+ if (this) {
+ LOG_INFO("sx93XX_schedule_work()\n");
+ spin_lock_irqsave(&this->lock, flags);
+ /* Stop any pending penup queues */
+ cancel_delayed_work(&this->dworker);
+ /*
+ * after waiting for a delay, this put the job
+ * in the kernel-global workqueue.
+ * so no need to create new thread in work queue.
+ */
+ schedule_delayed_work(&this->dworker, delay);
+ spin_unlock_irqrestore(&this->lock, flags);
+ } else
+ LOG_ERR("sx93XX_schedule_work, NULL psx93XX_t\n");
+}
+
+static irqreturn_t sx93XX_irq(int irq, void *pvoid)
+{
+ psx93XX_t this = 0;
+
+ if (pvoid) {
+ this = (psx93XX_t)pvoid;
+ if (sx9325_debug_enable)
+ LOG_DBG("sx93XX_irq\n");
+ if ((!this->get_nirq_low)
+ || this->get_nirq_low(this->board->irq_gpio)) {
+ if (sx9325_debug_enable)
+ LOG_DBG("sx93XX_irq - Schedule Work\n");
+ sx93XX_schedule_work(this, 0);
+ } else {
+ if (sx9325_debug_enable)
+ LOG_DBG("sx93XX_irq - nirq read high\n");
+ }
+ } else
+ LOG_ERR("sx93XX_irq, NULL pvoid\n");
+ return IRQ_HANDLED;
+}
+
+static void sx93XX_worker_func(struct work_struct *work)
+{
+ psx93XX_t this = 0;
+ int status = 0;
+ int counter = 0;
+ u8 nirqLow = 0;
+
+ if (work) {
+ this = container_of(work, sx93XX_t, dworker.work);
+
+ if (!this) {
+ LOG_ERR("sx93XX_worker_func, NULL sx93XX_t\n");
+ return;
+ }
+ if (unlikely(this->useIrqTimer)) {
+ if ((!this->get_nirq_low)
+ || this->get_nirq_low(this->board->irq_gpio))
+ nirqLow = 1;
+ }
+ /* since we are not in an interrupt
+ * don't need to disable irq. */
+ status = this->refreshStatus(this);
+ counter = -1;
+ if (sx9325_debug_enable)
+ LOG_DBG("Worker - Refresh Status %d\n", status);
+ /* counter start from MSB */
+ while ((++counter) < MAX_NUM_STATUS_BITS) {
+ if (((status >> counter) & 0x01)
+ && (this->statusFunc[counter])) {
+ LOG_INFO("Function %d Pointer Found. Calling\n"
+ ,counter);
+ this->statusFunc[counter](this);
+ }
+ }
+ if (unlikely(this->useIrqTimer && nirqLow)) {
+ /* Early models and if RATE=0 for newer models
+ * require a penup timer;
+ * Queue up the function again for checking on penup */
+ sx93XX_schedule_work(this,
+ msecs_to_jiffies(this->irqTimeout));
+ }
+ } else {
+ LOG_ERR("sx93XX_worker_func, NULL work_struct\n");
+ }
+}
+#endif
+
+void sx9325_irq_disable(psx93XX_t this)
+{
+ if (this) {
+ write_register(this, SX932x_CTRL1_REG,
+ this->board->cust_prox_ctrl0&0xdf);
+ if (sx9325_debug_enable)
+ LOG_INFO("sx9325 suspend: disable irq!\n");
+ disable_irq(this->irq);
+ }
+}
+void sx9325_irq_enable(psx93XX_t this)
+{
+ if (this) {
+ if (sx9325_debug_enable)
+ LOG_INFO("sx9325 resume: enable irq!\n");
+#ifdef USE_THREADED_IRQ
+ mutex_lock(&this->mutex);
+ /* Just in case need to reset any uncaught interrupts */
+ sx93XX_process_interrupt(this, 0);
+ mutex_unlock(&this->mutex);
+#else
+ sx93XX_schedule_work(this, 0);
+#endif
+ enable_irq(this->irq);
+ write_register(this, SX932x_CTRL1_REG,
+ this->board->cust_prox_ctrl0|0x20);
+ LOG_INFO("sx9325 resume: end!\n");
+ }
+}
+
+int sx93XX_sar_init(psx93XX_t this)
+{
+ int err = 0;
+
+ if (this && this->pDevice) {
+#ifdef USE_THREADED_IRQ
+
+ /* initialize worker function */
+ INIT_DELAYED_WORK(&this->dworker, sx93XX_worker_func);
+
+
+ /* initialize mutex */
+ mutex_init(&this->mutex);
+ /* initailize interrupt reporting */
+ this->irq_disabled = 0;
+ err = request_threaded_irq(this->irq, NULL,
+ sx93XX_interrupt_thread,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ this->pdev->driver->name,
+ this);
+#else
+ /* initialize spin lock */
+ spin_lock_init(&this->lock);
+
+ /* initialize worker function */
+ INIT_DELAYED_WORK(&this->dworker, sx93XX_worker_func);
+
+ /* initailize interrupt reporting */
+ this->irq_disabled = 0;
+ err = request_irq(this->irq, sx93XX_irq, IRQF_TRIGGER_FALLING,
+ this->pdev->driver->name, this);
+#endif
+ if (err) {
+ LOG_ERR("irq %d busy?\n", this->irq);
+ return err;
+ }
+#ifdef USE_THREADED_IRQ
+ LOG_INFO("registered with threaded irq (%d)\n", this->irq);
+#else
+ LOG_INFO("registered with irq (%d)\n", this->irq);
+#endif
+ /* call init function pointer:
+ * this should initialize all registers */
+ if (this->init)
+ return this->init(this);
+ LOG_ERR("No init function!!!!\n");
+ }
+ return -ENOMEM;
+}
+
+int sx93XX_sar_remove(psx93XX_t this)
+{
+ if (this) {
+ /* Cancel the Worker Func */
+ cancel_delayed_work_sync(&this->dworker);
+ free_irq(this->irq, this);
+ kfree(this);
+ return 0;
+ }
+ return -ENOMEM;
+}
+module_init(sx9325_init);
+module_exit(sx9325_exit);
+
+MODULE_AUTHOR("Semtech Corp. (http://www.semtech.com/)");
+MODULE_DESCRIPTION("SX9325 Capacitive Touch Controller Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.1");
--- /dev/null
+/*
+ * 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.
+ */
+#ifndef SX9325_TRIPLE_H
+#define SX9325_TRIPLE_H
+
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/sensors.h>
+#include <linux/regulator/consumer.h>
+/*
+ * I2C Registers
+ */
+
+#define SX932x_IRQSTAT_REG 0x00
+#define SX932x_STAT0_REG 0x01
+#define SX932x_STAT1_REG 0x02
+#define SX932x_STAT2_REG 0x03
+#define SX932x_STAT3_REG 0x04
+#define SX932x_IRQ_ENABLE_REG 0x05
+#define SX932x_IRQCFG0_REG 0x06
+#define SX932x_IRQCFG1_REG 0x07
+#define SX932x_IRQCFG2_REG 0x08
+/*
+ *-General control
+ */
+#define SX932x_CTRL0_REG 0x10
+#define SX932x_CTRL1_REG 0x11
+#define SX932x_I2CADDR_REG 0x14
+#define SX932x_CLKSPRD 0x15
+/*
+ *-AFE Control
+ */
+#define SX932x_AFE_CTRL0_REG 0x20
+#define SX932x_AFE_CTRL1_REG 0x21
+#define SX932x_AFE_CTRL2_REG 0x22
+#define SX932x_AFE_CTRL3_REG 0x23
+#define SX932x_AFE_CTRL4_REG 0x24
+#define SX932x_AFE_CTRL5_REG 0x25
+#define SX932x_AFE_CTRL6_REG 0x26
+#define SX932x_AFE_CTRL7_REG 0x27
+#define SX932x_AFE_PH0_REG 0x28
+#define SX932x_AFE_PH1_REG 0x29
+#define SX932x_AFE_PH2_REG 0x2A
+#define SX932x_AFE_PH3_REG 0x2B
+#define SX932x_AFE_CTRL8 0x2C
+#define SX932x_AFE_CTRL9 0x2D
+/*
+ *-Main Digital Processing (Prox) control
+ */
+#define SX932x_PROX_CTRL0_REG 0x30
+#define SX932x_PROX_CTRL1_REG 0x31
+#define SX932x_PROX_CTRL2_REG 0x32
+#define SX932x_PROX_CTRL3_REG 0x33
+#define SX932x_PROX_CTRL4_REG 0x34
+#define SX932x_PROX_CTRL5_REG 0x35
+#define SX932x_PROX_CTRL6_REG 0x36
+#define SX932x_PROX_CTRL7_REG 0x37
+/*
+ *-Advanced Digital Processing control
+ */
+#define SX932x_ADV_CTRL0_REG 0x40
+#define SX932x_ADV_CTRL1_REG 0x41
+#define SX932x_ADV_CTRL2_REG 0x42
+#define SX932x_ADV_CTRL3_REG 0x43
+#define SX932x_ADV_CTRL4_REG 0x44
+#define SX932x_ADV_CTRL5_REG 0x45
+#define SX932x_ADV_CTRL6_REG 0x46
+#define SX932x_ADV_CTRL7_REG 0x47
+#define SX932x_ADV_CTRL8_REG 0x48
+#define SX932x_ADV_CTRL9_REG 0x49
+#define SX932x_ADV_CTRL10_REG 0x4A
+#define SX932x_ADV_CTRL11_REG 0x4B
+#define SX932x_ADV_CTRL12_REG 0x4C
+#define SX932x_ADV_CTRL13_REG 0x4D
+#define SX932x_ADV_CTRL14_REG 0x4E
+#define SX932x_ADV_CTRL15_REG 0x4F
+#define SX932x_ADV_CTRL16_REG 0x50
+#define SX932x_ADV_CTRL17_REG 0x51
+#define SX932x_ADV_CTRL18_REG 0x52
+#define SX932x_ADV_CTRL19_REG 0x53
+#define SX932x_ADV_CTRL20_REG 0x54
+/* Sensor Readback */
+#define SX932x_CPSRD 0x60
+#define SX932x_USEMSB 0x61
+#define SX932x_USELSB 0x62
+#define SX932x_AVGMSB 0x63
+#define SX932x_AVGLSB 0x64
+#define SX932x_DIFFMSB 0x65
+#define SX932x_DIFFLSB 0x66
+#define SX932x_OFFSETMSB 0x67
+#define SX932x_OFFSETLSB 0x68
+#define SX932x_SARMSB 0x69
+#define SX932x_SARLSB 0x6A
+
+#define SX932x_SOFTRESET_REG 0x9F
+#define SX932x_WHOAMI_REG 0xFA
+#define SX932x_REV_REG 0xFB
+
+/* IrqStat 0:Inactive 1:Active */
+#define SX932x_IRQSTAT_RESET_FLAG 0x80
+#define SX932x_IRQSTAT_TOUCH_FLAG 0x40
+#define SX932x_IRQSTAT_RELEASE_FLAG 0x20
+#define SX932x_IRQSTAT_COMPDONE_FLAG 0x10
+#define SX932x_IRQSTAT_CONV_FLAG 0x08
+#define SX932x_IRQSTAT_PROG2_FLAG 0x04
+#define SX932x_IRQSTAT_PROG1_FLAG 0x02
+#define SX932x_IRQSTAT_PROG0_FLAG 0x01
+
+
+/* RegStat0 */
+#define SX932x_PROXSTAT_PH3_FLAG 0x08
+#define SX932x_PROXSTAT_PH2_FLAG 0x04
+#define SX932x_PROXSTAT_PH1_FLAG 0x02
+#define SX932x_PROXSTAT_PH0_FLAG 0x01
+
+/* SoftReset */
+#define SX932x_SOFTRESET 0xDE
+#define SX932x_WHOAMI_VALUE 0x22
+#define SX932x_REV_VALUE 0x22
+
+
+/* CpsStat */
+#define SX932x_TCHCMPSTAT_TCHCOMB_FLAG 0x88
+/* enable body stat */
+#define SX932x_TCHCMPSTAT_TCHSTAT2_FLAG 0x44
+/* enable body stat */
+#define SX932x_TCHCMPSTAT_TCHSTAT1_FLAG 0x22
+/* enable body stat */
+#define SX932x_TCHCMPSTAT_TCHSTAT0_FLAG 0x11
+
+
+
+/* useful channel number */
+#define USE_CHANNEL_NUM 3
+
+/* default settings */
+/* Channel enable: CS0:1,CS1:2,CS2:4,COMB:8
+ * Defines the Active scan period :
+ * 0000: Min (no idle time)
+ * 0001: 15ms
+ * 0010: 30 ms (Typ.)
+ * 0011: 45 ms
+ * 0100: 60 ms
+ * 0101: 90 ms
+ * 0110: 120 ms
+ * 0111: 200 ms
+ * 1000: 400 ms
+ * 1001: 600 ms
+ * 1010: 800 ms
+ * 1011: 1 s
+ * 1100: 2 s
+ * 1101: 3 s
+ * 1110: 4 s
+ * 1111: 5 s
+ */
+#define DUMMY_USE_CHANNEL 0x1
+#define DUMMY_SCAN_PERIOD 0x2
+#define DUMMY_RAW_DATA_CHANNEL 0x00
+
+/* Cap sensor report key, including cs0, cs1, cs2 and comb */
+#define KEY_CAP_CS0 0x270
+#define KEY_CAP_CS1 0x271
+#define KEY_CAP_CS2 0x272
+#define KEY_CAP_COMB 0x272
+
+/**************************************
+* define platform data
+*
+**************************************/
+struct smtc_reg_data {
+ unsigned char reg;
+ unsigned char val;
+};
+
+typedef struct smtc_reg_data smtc_reg_data_t;
+typedef struct smtc_reg_data *psmtc_reg_data_t;
+
+
+struct _buttonInfo {
+ /* The Key to send to the input */
+ int keycode;
+ /* Mask to look for on Touch Status */
+ int mask;
+ /* Current state of button. */
+ int state;
+};
+
+struct _totalButtonInformation {
+ struct _buttonInfo *buttons;
+ int buttonSize;
+ struct input_dev *input_top;
+ struct input_dev *input_bottom;
+};
+
+typedef struct _totalButtonInformation buttonInformation_t;
+typedef struct _totalButtonInformation *pbuttonInformation_t;
+
+/* Define Registers that need to be initialized to values different than
+ * default
+ */
+static struct smtc_reg_data sx9325_i2c_reg_setup[] = {
+/*Interrupt and config*/
+ {
+ .reg = SX932x_IRQ_ENABLE_REG, //0x05
+ .val = 0x64, // Enavle Close and Far -> enable compensation interrupt
+ },
+ {
+ .reg = SX932x_IRQCFG0_REG, //0x06
+ .val = 0x00, //
+ },
+ {
+ .reg = SX932x_IRQCFG1_REG, //0x07
+ .val = 0x00,
+ },
+ {
+ .reg = SX932x_IRQCFG2_REG, //0x08
+ .val = 0x00, //Activ Low
+ },
+ //--------General control
+ {
+ .reg = SX932x_CTRL0_REG, //0x10
+ .val = 0x09, // Scanperiod : 100ms(10110)
+ },
+ {
+ .reg = SX932x_I2CADDR_REG, //0x14
+ .val = 0x00, //I2C Address : 0x28
+ },
+ {
+ .reg = SX932x_CLKSPRD, //0x15
+ .val = 0x00, //
+ },
+ //--------AFE Control
+ {
+ .reg = SX932x_AFE_CTRL0_REG, //0x20
+ .val = 0x00, // CSx pin during sleep mode : HZ
+ },
+ {
+ .reg = SX932x_AFE_CTRL1_REG, //0x21
+ .val = 0x10, //reserved
+ },
+ {
+ .reg = SX932x_AFE_CTRL2_REG, //0x22
+ .val = 0x00, //reserved
+ },
+ {
+ .reg = SX932x_AFE_CTRL3_REG, //0x23
+ .val = 0x00, //Analog Range(ph0/1) : Small
+ },
+ {
+ .reg = SX932x_AFE_CTRL4_REG, //0x24
+ .val = 0x44, //Sampling Freq(ph0/1) : 83.33khz(01000), Resolution(ph0/1) : 128(100)
+ },
+ {
+ .reg = SX932x_AFE_CTRL5_REG, //0x25
+ .val = 0x00, //reserved
+ },
+ {
+ .reg = SX932x_AFE_CTRL6_REG, //0x26
+ .val = 0x01, //big//Analog Range(ph2/3) : Small
+ },
+ {
+ .reg = SX932x_AFE_CTRL7_REG, //0x27
+ .val = 0x44, //Sampling Freq(ph2/3) : 83.33khz(01000), Resolution(ph2/3) : 128(100)
+ },
+ {
+ .reg = SX932x_AFE_PH0_REG, //0x28
+ .val = 0x01,//0x04, // CS2:HZ CS1:Input CS0 :HZ
+ },
+ {
+ .reg = SX932x_AFE_PH1_REG, //0x29
+ .val = 0x04,//0x10, // CS2:Input CS1:HZ Shield CS0 :HZ
+ },
+ {
+ .reg = SX932x_AFE_PH2_REG, //0x2A
+ .val = 0x10,//0x1B, //CS2:HZ CS1:HZ CS0 :HZ
+ },
+ {
+ .reg = SX932x_AFE_PH3_REG, //0x2B
+ .val = 0x00, //CS2:HZ CS1:HZ CS0 :HZ
+ },
+ {
+ .reg = SX932x_AFE_CTRL8, //0x2C
+ .val = 0x12, // input register(kohm) 4(0010)
+ },
+ {
+ .reg = SX932x_AFE_CTRL9, //0x2D
+ .val = 0x08, // Analg gain : x1(1000)
+ },
+ /*--------PROX control*/
+ {
+ .reg = SX932x_PROX_CTRL0_REG, //0x30
+ .val = 0x13, // Digital Gain(ph0/1) : off(001) Digital Filter(ph0/1) : 1-1/2(001)
+ },
+ {
+ .reg = SX932x_PROX_CTRL1_REG, //0x31
+ .val = 0x0a, // Digital Gain(ph2/3) : off(001) Digital Filter(ph2/3) : 1-1/2(001)
+ },
+ {
+ .reg = SX932x_PROX_CTRL2_REG, //0x32
+ .val = 0x08, //AVGNEGTHRESH : 16384
+ },
+ {
+ .reg = SX932x_PROX_CTRL3_REG, // 0x33
+ .val = 0x20, //AVGPOSTHRESH : 16384
+ },
+ {
+ .reg = SX932x_PROX_CTRL4_REG, //0x34
+ .val = 0x0a, //AVGFREEZEDIS : on(0) ,AVGNEGFILT :1-1/2(001) ,AVGPOSFILT : 1-1/256(100)
+ },
+ {
+ .reg = SX932x_PROX_CTRL5_REG, //0x35
+ .val = 0x0a, //FARCOND: PROXDIFF < (THRESH.HYST), HYST : None, CLOSEDEB : off ,FARDEB : off
+ },
+ {
+ .reg = SX932x_PROX_CTRL6_REG, //0x36
+ .val = 0x1e, // Prox Theshold(ph0/1) : 200
+ },
+ {
+ .reg = SX932x_PROX_CTRL7_REG, //0x37
+ .val = 0x15, // Prox Theshold(ph2/3) : 200
+ },
+ /*Advanced control (defult)*/
+ {
+ .reg = SX932x_ADV_CTRL0_REG,
+ .val = 0x00,
+ },
+ {
+ .reg = SX932x_ADV_CTRL1_REG,
+ .val = 0x00,
+ },
+ {
+ .reg = SX932x_ADV_CTRL2_REG,
+ .val = 0x18,//0x10,
+ },
+ {
+ .reg = SX932x_ADV_CTRL3_REG,
+ .val = 0x2a,
+ },
+ {
+ .reg = SX932x_ADV_CTRL4_REG,
+ .val = 0x02,
+ },
+ {
+ .reg = SX932x_ADV_CTRL5_REG,
+ .val = 0x05,
+ },
+ {
+ .reg = SX932x_ADV_CTRL6_REG,
+ .val = 0x00,
+ },
+ {
+ .reg = SX932x_ADV_CTRL7_REG,
+ .val = 0x00,
+ },
+ {
+ .reg = SX932x_ADV_CTRL8_REG,
+ .val = 0x00,
+ },
+ {
+ .reg = SX932x_ADV_CTRL9_REG,
+ .val = 0x80,
+ },
+ {
+ .reg = SX932x_ADV_CTRL10_REG,
+ .val = 0x11,
+ },
+ {
+ .reg = SX932x_ADV_CTRL11_REG,
+ .val = 0x00,
+ },
+ {
+ .reg = SX932x_ADV_CTRL12_REG,
+ .val = 0x00,
+ },
+ {
+ .reg = SX932x_ADV_CTRL13_REG,
+ .val = 0x00,
+ },
+ {
+ .reg = SX932x_ADV_CTRL14_REG,
+ .val = 0x80,
+ },
+ {
+ .reg = SX932x_ADV_CTRL15_REG,
+ .val = 0x0C,
+ },
+ {
+ .reg = SX932x_ADV_CTRL16_REG,
+ .val = 0x00,
+ },
+ {
+ .reg = SX932x_ADV_CTRL17_REG,
+ .val = 0x00,
+ },
+ {
+ .reg = SX932x_ADV_CTRL18_REG,
+ .val = 0x00,
+ },
+ {
+ .reg = SX932x_ADV_CTRL19_REG,
+ .val = 0xF0,
+ },
+ {
+ .reg = SX932x_ADV_CTRL20_REG,
+ .val = 0xF0,
+ },
+ /*Sensor enable*/
+ {
+ .reg = SX932x_CTRL1_REG, //0x11
+ .val = 0x24, //enable PH2
+ },
+};
+
+
+
+
+static struct _buttonInfo psmtcButtons[] = {
+ {
+ .keycode = KEY_CAP_CS0,
+ .mask = SX932x_TCHCMPSTAT_TCHSTAT0_FLAG,
+ },
+ {
+ .keycode = KEY_CAP_CS1,
+ .mask = SX932x_TCHCMPSTAT_TCHSTAT1_FLAG,
+ },
+ {
+ .keycode = KEY_CAP_CS2,
+ .mask = SX932x_TCHCMPSTAT_TCHSTAT2_FLAG,
+ },
+ {
+ .keycode = KEY_CAP_COMB,
+ .mask = SX932x_TCHCMPSTAT_TCHCOMB_FLAG,
+ },
+};
+
+struct sx9325_platform_data {
+ int i2c_reg_num;
+ struct smtc_reg_data *pi2c_reg;
+ struct regulator *cap_vdd;
+ struct regulator *cap_svdd;
+ bool cap_vdd_en;
+ bool cap_svdd_en;
+ unsigned irq_gpio;
+ /* used for custom setting for channel and scan period */
+ u32 cust_prox_ctrl0;
+ u32 cust_raw_data_channel;
+ int cap_channel_top;
+ int cap_channel_bottom;
+ pbuttonInformation_t pbuttonInformation;
+
+ int (*get_is_nirq_low)(unsigned irq_gpio);
+ int (*init_platform_hw)(void);
+ void (*exit_platform_hw)(void);
+};
+typedef struct sx9325_platform_data sx9325_platform_data_t;
+typedef struct sx9325_platform_data *psx9325_platform_data_t;
+
+#ifdef USE_SENSORS_CLASS
+static struct sensors_classdev sensors_capsensor_top_cdev = {
+ .name = "capsense_top",
+ .vendor = "semtech",
+ .version = 1,
+ .type = SENSOR_TYPE_MOTO_CAPSENSE,
+ .max_range = "5",
+ .resolution = "5.0",
+ .sensor_power = "3",
+ .min_delay = 0, /* in microseconds */
+ .fifo_reserved_event_count = 0,
+ .fifo_max_event_count = 0,
+ .enabled = 0,
+ .delay_msec = 100,
+ .sensors_enable = NULL,
+ .sensors_poll_delay = NULL,
+};
+static struct sensors_classdev sensors_capsensor_bottom_cdev = {
+ .name = "capsense_bottom",
+ .vendor = "semtech",
+ .version = 1,
+ .type = SENSOR_TYPE_MOTO_CAPSENSE,
+ .max_range = "5",
+ .resolution = "5.0",
+ .sensor_power = "3",
+ .min_delay = 0, /* in microseconds */
+ .fifo_reserved_event_count = 0,
+ .fifo_max_event_count = 0,
+ .enabled = 0,
+ .delay_msec = 100,
+ .sensors_enable = NULL,
+ .sensors_poll_delay = NULL,
+};
+#endif
+/***************************************
+* define data struct/interrupt
+* @pdev: pdev common device struction for linux
+* @dworker: work struct for worker function
+* @board: constant pointer to platform data
+* @mutex: mutex for interrupt process
+* @lock: Spin Lock used for nirq worker function
+* @bus: either i2c_client or spi_client
+* @pDevice: device specific struct pointer
+*@read_flag : used for dump specified register
+* @irq: irq number used
+* @irqTimeout: msecs only set if useIrqTimer is true
+* @irq_disabled: whether irq should be ignored
+* @irq_gpio: irq gpio number
+* @useIrqTimer: older models need irq timer for pen up cases
+* @read_reg: record reg address which want to read
+*@cust_prox_ctrl0 : used for custom setting for channel and scan period
+* @init: (re)initialize device
+* @refreshStatus: read register status
+* @get_nirq_low: get whether nirq is low (platform data)
+* @statusFunc: array of functions to call for corresponding status bit
+***************************************/
+#define USE_THREADED_IRQ
+
+#define MAX_NUM_STATUS_BITS (8)
+
+typedef struct sx93XX sx93XX_t, *psx93XX_t;
+struct sx93XX {
+ struct device *pdev;
+ struct delayed_work dworker;
+ struct sx9325_platform_data *board;
+#if defined(USE_THREADED_IRQ)
+ struct mutex mutex;
+#else
+ spinlock_t lock;
+#endif
+ void *bus;
+ void *pDevice;
+ int read_flag;
+ int irq;
+ int irqTimeout;
+ char irq_disabled;
+ /* whether irq should be ignored..
+ * cases if enable/disable irq is not used
+ * or does not work properly */
+ u8 useIrqTimer;
+ u8 read_reg;
+
+ struct work_struct ps_notify_work;
+ struct notifier_block ps_notif;
+ bool ps_is_present;
+
+#if defined(CONFIG_FB)
+ struct work_struct fb_notify_work;
+ struct notifier_block fb_notif;
+#endif
+
+ /* Function Pointers */
+ int (*init)(psx93XX_t this);
+ /* since we are trying to avoid knowing registers, create a pointer to a
+ * common read register which would be to read what the interrupt source
+ * is from
+ */
+ int (*refreshStatus)(psx93XX_t this);
+ int (*get_nirq_low)(unsigned irq_gpio);
+
+ void (*statusFunc[MAX_NUM_STATUS_BITS])(psx93XX_t this);
+
+};
+
+void sx9325_irq_disable(psx93XX_t this);
+void sx9325_irq_enable(psx93XX_t this);
+int sx93XX_sar_init(psx93XX_t this);
+int sx93XX_sar_remove(psx93XX_t this);
+
+#endif
+++ /dev/null
-/*! \file sx932x.c\r
- * \brief SX9320 Driver\r
- *\r
- * Driver for the SX9320\r
- * Copyright (c) 2011 Semtech Corp\r
- *\r
- * This program is free software; you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License version 2 as\r
- * published by the Free Software Foundation.\r
- */\r
-#define DEBUG\r
-#define DRIVER_NAME "sx932x"\r
-\r
-#define MAX_WRITE_ARRAY_SIZE 32\r
-\r
-#include <linux/module.h>\r
-#include <linux/slab.h>\r
-#include <linux/i2c.h>\r
-#include <linux/init.h>\r
-#include <linux/delay.h>\r
-#include <linux/input.h>\r
-#include <linux/fs.h>\r
-#include <linux/device.h>\r
-#include <linux/interrupt.h>\r
-#include <linux/syscalls.h>\r
-#include <linux/wakelock.h>\r
-#include <linux/uaccess.h>\r
-#include <linux/sort.h>\r
-#include <linux/gpio.h>\r
-#include <linux/of_gpio.h>\r
-#include <linux/regulator/consumer.h>\r
-#include "sx932x.h" /* main struct, interrupt,init,pointers */\r
-\r
-#define IDLE 0\r
-#define ACTIVE 1\r
-\r
-#define SX932x_NIRQ 34\r
-\r
-#define MAIN_SENSOR 1 //CS1\r
-\r
-/* Failer Index */\r
-#define SX932x_ID_ERROR 1\r
-#define SX932x_NIRQ_ERROR 2\r
-#define SX932x_CONN_ERROR 3\r
-#define SX932x_I2C_ERROR 4\r
-\r
-#define PROXOFFSET_LOW 1500\r
-\r
-#define SX932x_ANALOG_GAIN 1\r
-#define SX932x_DIGITAL_GAIN 1\r
-#define SX932x_ANALOG_RANGE 2.65\r
-\r
-#define TOUCH_CHECK_REF_AMB 0 // 44523\r
-#define TOUCH_CHECK_SLOPE 0 // 50\r
-#define TOUCH_CHECK_MAIN_AMB 0 // 151282\r
-\r
-/*! \struct sx932x\r
- * Specialized struct containing input event data, platform data, and\r
- * last cap state read if needed.\r
- */\r
-typedef struct sx932x\r
-{\r
- pbuttonInformation_t pbuttonInformation;\r
- psx932x_platform_data_t hw; /* specific platform data settings */\r
-} sx932x_t, *psx932x_t;\r
-\r
-static int irq_gpio_num;\r
-\r
-/*! \fn static int write_register(psx93XX_t this, u8 address, u8 value)\r
- * \brief Sends a write register to the device\r
- * \param this Pointer to main parent struct\r
- * \param address 8-bit register address\r
- * \param value 8-bit register value to write to address\r
- * \return Value from i2c_master_send\r
- */\r
-\r
-static int write_register(psx93XX_t this, u8 address, u8 value)\r
-{\r
- struct i2c_client *i2c = 0;\r
- char buffer[2];\r
- int returnValue = 0;\r
-\r
- buffer[0] = address;\r
- buffer[1] = value;\r
- returnValue = -ENOMEM;\r
-\r
- if (this && this->bus) {\r
- i2c = this->bus;\r
- returnValue = i2c_master_send(i2c,buffer,2);\r
- #ifdef DEBUG\r
- dev_info(&i2c->dev,"write_register Address: 0x%x Value: 0x%x Return: %d\n",\r
- address,value,returnValue);\r
- #endif\r
- }\r
- return returnValue;\r
-}\r
-\r
-/*! \fn static int read_register(psx93XX_t this, u8 address, u8 *value)\r
-* \brief Reads a register's value from the device\r
-* \param this Pointer to main parent struct\r
-* \param address 8-Bit address to read from\r
-* \param value Pointer to 8-bit value to save register value to\r
-* \return Value from i2c_smbus_read_byte_data if < 0. else 0\r
-*/\r
-static int read_register(psx93XX_t this, u8 address, u8 *value)\r
-{\r
- struct i2c_client *i2c = 0;\r
- s32 returnValue = 0;\r
-\r
- if (this && value && this->bus) {\r
- i2c = this->bus;\r
- dev_info(&i2c->dev, "read_register Address:i2c:%s 0x%x Return: 0x%x\n",this->bus,\r
- address,returnValue);\r
- returnValue = i2c_smbus_read_byte_data(i2c,address);\r
-\r
- #ifdef DEBUG\r
- dev_info(&i2c->dev, "read_register Address: 0x%x Return: 0x%x\n",\r
- address,returnValue);\r
- #endif\r
-\r
- if (returnValue >= 0) {\r
- *value = returnValue;\r
- return 0;\r
- }\r
- else {\r
- return returnValue;\r
- }\r
- }\r
- return -ENOMEM;\r
-}\r
-\r
-//static int sx9320_set_mode(psx93XX_t this, unsigned char mode);\r
-\r
-/*! \fn static int read_regStat(psx93XX_t this)\r
- * \brief Shortcut to read what caused interrupt.\r
- * \details This is to keep the drivers a unified\r
- * function that will read whatever register(s)\r
- * provide information on why the interrupt was caused.\r
- * \param this Pointer to main parent struct\r
- * \return If successful, Value of bit(s) that cause interrupt, else 0\r
- */\r
-static int read_regStat(psx93XX_t this)\r
-{\r
- u8 data = 0;\r
- if (this) {\r
- if (read_register(this,SX932x_IRQSTAT_REG,&data) == 0)\r
- return (data & 0x00FF);\r
- }\r
- return 0;\r
-}\r
-\r
-/*********************************************************************/\r
-/*! \brief Perform a manual offset calibration\r
-* \param this Pointer to main parent struct \r
-* \return Value return value from the write register\r
- */\r
-static int manual_offset_calibration(psx93XX_t this)\r
-{\r
- s32 returnValue = 0;\r
- returnValue = write_register(this,SX932x_STAT2_REG,0x0F);\r
- return returnValue;\r
-}\r
-/*! \brief sysfs show function for manual calibration which currently just\r
- * returns register value.\r
- */\r
-static ssize_t manual_offset_calibration_show(struct device *dev,\r
- struct device_attribute *attr, char *buf)\r
-{\r
- u8 reg_value = 0;\r
- psx93XX_t this = dev_get_drvdata(dev);\r
-\r
- dev_info(this->pdev, "Reading IRQSTAT_REG\n");\r
- read_register(this,SX932x_IRQSTAT_REG,®_value);\r
- return sprintf(buf, "%d\n", reg_value);\r
-}\r
-\r
-/*! \brief sysfs store function for manual calibration\r
- */\r
-static ssize_t manual_offset_calibration_store(struct device *dev,\r
- struct device_attribute *attr,const char *buf, size_t count)\r
-{\r
- psx93XX_t this = dev_get_drvdata(dev);\r
- unsigned long val;\r
- if (kstrtoul(buf, 0, &val))\r
- return -EINVAL;\r
- if (val) {\r
- dev_info( this->pdev, "Performing manual_offset_calibration()\n");\r
- manual_offset_calibration(this);\r
- }\r
- return count;\r
-}\r
-\r
-static int sx932x_Hardware_Check(psx93XX_t this)\r
-{\r
- int ret;\r
- u8 failcode;\r
- u8 loop = 0;\r
- this->failStatusCode = 0;\r
-\r
- //Check th IRQ Status\r
- while(this->get_nirq_low && this->get_nirq_low()){\r
- read_regStat(this);\r
- msleep(100);\r
- if(++loop >10){\r
- this->failStatusCode = SX932x_NIRQ_ERROR;\r
- break;\r
- }\r
- }\r
-\r
- //Check I2C Connection\r
- ret = read_register(this, SX932x_WHOAMI_REG, &failcode);\r
- if(ret < 0){\r
- this->failStatusCode = SX932x_I2C_ERROR;\r
- }\r
-\r
- if(failcode!= SX932x_WHOAMI_VALUE){\r
- this->failStatusCode = SX932x_ID_ERROR;\r
- }\r
-\r
- dev_info(this->pdev, "sx932x failcode = 0x%x\n",this->failStatusCode);\r
- return (int)this->failStatusCode;\r
-}\r
-\r
-\r
-/*********************************************************************/\r
-static int sx932x_global_variable_init(psx93XX_t this)\r
-{\r
- this->irq_disabled = 0;\r
- this->failStatusCode = 0;\r
- this->reg_in_dts = true;\r
- return 0;\r
-}\r
-\r
-static ssize_t sx932x_register_write_store(struct device *dev,\r
- struct device_attribute *attr, const char *buf, size_t count)\r
-{\r
- int reg_address = 0, val = 0;\r
- psx93XX_t this = dev_get_drvdata(dev);\r
-\r
- if (sscanf(buf, "%x,%x", ®_address, &val) != 2) {\r
- pr_err("[SX9320]: %s - The number of data are wrong\n",__func__);\r
- return -EINVAL;\r
- }\r
-\r
- write_register(this, (unsigned char)reg_address, (unsigned char)val);\r
- pr_info("[SX9320]: %s - Register(0x%x) data(0x%x)\n",__func__, reg_address, val);\r
-\r
- return count;\r
-}\r
-//read registers not include the advanced one\r
-static ssize_t sx932x_register_read_store(struct device *dev,\r
- struct device_attribute *attr, const char *buf, size_t count)\r
-{\r
- u8 val=0;\r
- int regist = 0;\r
- psx93XX_t this = dev_get_drvdata(dev);\r
-\r
- dev_info(this->pdev, "Reading register\n");\r
-\r
- if (sscanf(buf, "%x", ®ist) != 1) {\r
- pr_err("[SX9320]: %s - The number of data are wrong\n",__func__);\r
- return -EINVAL;\r
- }\r
- dev_info(this->pdev, "regist = 0x%x\n",regist);\r
- read_register(this, regist, &val);\r
- pr_info("[SX9320]: %s - Register(0x%2x) data(0x%2x)\n",__func__, regist, val);\r
-\r
- return count;\r
-}\r
-\r
-static ssize_t sx932x_register_read_show(struct device *dev,\r
- struct device_attribute *attr, char *buf)\r
-{\r
- u8 val=0;\r
- u8 val1=0;\r
- psx93XX_t this = dev_get_drvdata(dev);\r
-\r
- dev_info(this->pdev, "Reading register\n");\r
- read_register(this, SX932x_DIFFMSB, &val);\r
- read_register(this, SX932x_DIFFLSB, &val1);\r
- dev_info(this->pdev,"[SX9320]: %s - Register(0x%2x) data(0x%2x)\n",__func__, SX932x_DIFFMSB, val);\r
- dev_info(this->pdev,"[SX9320]: %s - Register(0x%2x) data(0x%2x)\n",__func__, SX932x_DIFFLSB, val1);\r
- return sprintf(buf, "%x%x\n", val,val1);\r
-}\r
-static void read_rawData(psx93XX_t this)\r
-{\r
- u8 msb=0, lsb=0;\r
- u8 csx;\r
- s32 useful;\r
- s32 average;\r
- s32 diff;\r
- u16 offset;\r
- if(this){\r
- for(csx =0; csx<4; csx++){\r
- write_register(this,SX932x_CPSRD,csx);//here to check the CS1, also can read other channel\r
- read_register(this,SX932x_USEMSB,&msb);\r
- read_register(this,SX932x_USELSB,&lsb);\r
- useful = (s32)((msb << 8) | lsb);\r
-\r
- read_register(this,SX932x_AVGMSB,&msb);\r
- read_register(this,SX932x_AVGLSB,&lsb);\r
- average = (s32)((msb << 8) | lsb);\r
-\r
- read_register(this,SX932x_DIFFMSB,&msb);\r
- read_register(this,SX932x_DIFFLSB,&lsb);\r
- diff = (s32)((msb << 8) | lsb);\r
-\r
- read_register(this,SX932x_OFFSETMSB,&msb);\r
- read_register(this,SX932x_OFFSETLSB,&lsb);\r
- offset = (u16)((msb << 8) | lsb);\r
- if (useful > 32767)\r
- useful -= 65536;\r
- if (average > 32767)\r
- average -= 65536;\r
- if (diff > 32767)\r
- diff -= 65536;\r
- dev_info(this->pdev, " [CS: %d] Useful = %d Average = %d, DIFF = %d Offset = %d \n",csx,useful,average,diff,offset);\r
- }\r
- }\r
-}\r
-\r
-static ssize_t sx932x_raw_data_show(struct device *dev,\r
- struct device_attribute *attr, char *buf)\r
-{\r
- psx93XX_t this = dev_get_drvdata(dev);\r
- read_rawData(this);\r
- return 0;\r
-}\r
-\r
-static DEVICE_ATTR(manual_calibrate, 0664, manual_offset_calibration_show,manual_offset_calibration_store);\r
-static DEVICE_ATTR(register_write, 0664, NULL,sx932x_register_write_store);\r
-static DEVICE_ATTR(register_read,0664, sx932x_register_read_show,sx932x_register_read_store);\r
-static DEVICE_ATTR(raw_data,0664,sx932x_raw_data_show,NULL);\r
-static struct attribute *sx932x_attributes[] = {\r
- &dev_attr_manual_calibrate.attr,\r
- &dev_attr_register_write.attr,\r
- &dev_attr_register_read.attr,\r
- &dev_attr_raw_data.attr,\r
- NULL,\r
-};\r
-static struct attribute_group sx932x_attr_group = {\r
- .attrs = sx932x_attributes,\r
-};\r
-\r
-/****************************************************/\r
-/*! \brief Initialize I2C config from platform data\r
- * \param this Pointer to main parent struct \r
- */\r
-static void sx932x_reg_init(psx93XX_t this)\r
-{\r
- psx932x_t pDevice = 0;\r
- psx932x_platform_data_t pdata = 0;\r
- int i = 0;\r
- /* configure device */\r
- dev_info(this->pdev, "Going to Setup I2C Registers\n");\r
- if (this && (pDevice = this->pDevice) && (pdata = pDevice->hw))\r
- {\r
- /*******************************************************************************/\r
- // try to initialize from device tree!\r
- /*******************************************************************************/\r
- if (this->reg_in_dts == true) {\r
- while ( i < pdata->i2c_reg_num) {\r
- /* Write all registers/values contained in i2c_reg */\r
- dev_info(this->pdev, "Going to Write Reg from dts: 0x%x Value: 0x%x\n",\r
- pdata->pi2c_reg[i].reg,pdata->pi2c_reg[i].val);\r
- write_register(this, pdata->pi2c_reg[i].reg,pdata->pi2c_reg[i].val);\r
- i++;\r
- }\r
- } else { // use static ones!!\r
- while ( i < ARRAY_SIZE(sx932x_i2c_reg_setup)) {\r
- /* Write all registers/values contained in i2c_reg */\r
- dev_info(this->pdev, "Going to Write Reg: 0x%x Value: 0x%x\n",\r
- sx932x_i2c_reg_setup[i].reg,sx932x_i2c_reg_setup[i].val);\r
- write_register(this, sx932x_i2c_reg_setup[i].reg,sx932x_i2c_reg_setup[i].val);\r
- i++;\r
- }\r
- }\r
- /*******************************************************************************/\r
- } else {\r
- dev_err(this->pdev, "ERROR! platform data 0x%p\n",pDevice->hw);\r
- }\r
-\r
-}\r
-\r
-\r
-/*! \fn static int initialize(psx93XX_t this)\r
- * \brief Performs all initialization needed to configure the device\r
- * \param this Pointer to main parent struct\r
- * \return Last used command's return value (negative if error)\r
- */\r
-static int initialize(psx93XX_t this)\r
-{\r
- int ret;\r
- if (this) {\r
- pr_info("SX9320 income initialize\n");\r
- /* prepare reset by disabling any irq handling */\r
- this->irq_disabled = 1;\r
- disable_irq(this->irq);\r
- /* perform a reset */\r
- write_register(this,SX932x_SOFTRESET_REG,SX932x_SOFTRESET);\r
- /* wait until the reset has finished by monitoring NIRQ */\r
- dev_info(this->pdev, "Sent Software Reset. Waiting until device is back from reset to continue.\n");\r
- /* just sleep for awhile instead of using a loop with reading irq status */\r
- msleep(100);\r
-\r
- ret = sx932x_global_variable_init(this);\r
-\r
- sx932x_reg_init(this);\r
- msleep(100); /* make sure everything is running */\r
- manual_offset_calibration(this);\r
-\r
- /* re-enable interrupt handling */\r
- enable_irq(this->irq);\r
-\r
- /* make sure no interrupts are pending since enabling irq will only\r
- * work on next falling edge */\r
- read_regStat(this);\r
- return 0;\r
- }\r
- return -ENOMEM;\r
-}\r
-\r
-/*!\r
- * \brief Handle what to do when a touch occurs\r
- * \param this Pointer to main parent struct\r
- */\r
-static void touchProcess(psx93XX_t this)\r
-{\r
- int counter = 0;\r
- u8 i = 0;\r
- int numberOfButtons = 0;\r
- psx932x_t pDevice = NULL;\r
- struct _buttonInfo *buttons = NULL;\r
- struct input_dev *input = NULL;\r
-\r
- struct _buttonInfo *pCurrentButton = NULL;\r
-\r
- if (this && (pDevice = this->pDevice))\r
- {\r
- //dev_info(this->pdev, "Inside touchProcess()\n");\r
- read_register(this, SX932x_STAT0_REG, &i);\r
-\r
- buttons = pDevice->pbuttonInformation->buttons;\r
- input = pDevice->pbuttonInformation->input;\r
- numberOfButtons = pDevice->pbuttonInformation->buttonSize;\r
-\r
- if (unlikely( (buttons==NULL) || (input==NULL) )) {\r
- dev_err(this->pdev, "ERROR!! buttons or input NULL!!!\n");\r
- return;\r
- }\r
-\r
- for (counter = 0; counter < numberOfButtons; counter++) {\r
- pCurrentButton = &buttons[counter];\r
- if (pCurrentButton==NULL) {\r
- dev_err(this->pdev,"ERROR!! current button at index: %d NULL!!!\n", counter);\r
- return; // ERRORR!!!!\r
- }\r
- switch (pCurrentButton->state) {\r
- case IDLE: /* Button is not being touched! */\r
- if (((i & pCurrentButton->mask) == pCurrentButton->mask)) {\r
- /* User pressed button */\r
- dev_info(this->pdev, "cap button %d touched\n", counter);\r
- input_report_key(input, pCurrentButton->keycode, 1);\r
- pCurrentButton->state = ACTIVE;\r
- } else {\r
- dev_info(this->pdev, "Button %d already released.\n",counter);\r
- }\r
- break;\r
- case ACTIVE: /* Button is being touched! */ \r
- if (((i & pCurrentButton->mask) != pCurrentButton->mask)) {\r
- /* User released button */\r
- dev_info(this->pdev, "cap button %d released\n",counter);\r
- input_report_key(input, pCurrentButton->keycode, 0);\r
- pCurrentButton->state = IDLE;\r
- } else {\r
- dev_info(this->pdev, "Button %d still touched.\n",counter);\r
- }\r
- break;\r
- default: /* Shouldn't be here, device only allowed ACTIVE or IDLE */\r
- break;\r
- };\r
- }\r
- input_sync(input);\r
-\r
- //dev_info(this->pdev, "Leaving touchProcess()\n");\r
- }\r
-}\r
-\r
-static int sx932x_parse_dt(struct sx932x_platform_data *pdata, struct device *dev)\r
-{\r
- struct device_node *dNode = dev->of_node;\r
- enum of_gpio_flags flags;\r
- if (dNode == NULL)\r
- return -ENODEV;\r
-\r
- pdata->irq_gpio= of_get_named_gpio_flags(dNode,\r
- "Semtech,nirq-gpio", 0, &flags);\r
- irq_gpio_num = pdata->irq_gpio;\r
- if (pdata->irq_gpio < 0) {\r
- pr_err("[SENSOR]: %s - get irq_gpio error\n", __func__);\r
- return -ENODEV;\r
- }\r
-\r
- /***********************************************************************/\r
- // load in registers from device tree\r
- of_property_read_u32(dNode,"Semtech,reg-num",&pdata->i2c_reg_num);\r
- // layout is register, value, register, value....\r
- // if an extra item is after just ignore it. reading the array in will cause it to fail anyway\r
- pr_info("[SX9320]:%s - size of elements %d \n", __func__,pdata->i2c_reg_num);\r
- if (pdata->i2c_reg_num > 0) {\r
- // initialize platform reg data array\r
- pdata->pi2c_reg = devm_kzalloc(dev,sizeof(struct smtc_reg_data)*pdata->i2c_reg_num, GFP_KERNEL);\r
- if (unlikely(pdata->pi2c_reg == NULL)) {\r
- return -ENOMEM;\r
- }\r
-\r
- // initialize the array\r
- if (of_property_read_u8_array(dNode,"Semtech,reg-init",(u8*)&(pdata->pi2c_reg[0]),sizeof(struct smtc_reg_data)*pdata->i2c_reg_num))\r
- return -ENOMEM;\r
- }\r
- /***********************************************************************/\r
- pr_info("[SX9320]: %s -[%d] parse_dt complete\n", __func__,pdata->irq_gpio);\r
- return 0;\r
-}\r
- struct pinctrl *pinctrl_int;\r
-/* get the NIRQ state (1->NIRQ-low, 0->NIRQ-high) */\r
-static int sx932x_init_platform_hw(struct i2c_client *client)\r
-{\r
- psx93XX_t this = i2c_get_clientdata(client);\r
- struct sx932x *pDevice = NULL;\r
- struct sx932x_platform_data *pdata = NULL;\r
-\r
- int rc;\r
-\r
- pr_info("[SX9320] : %s init_platform_hw start!",__func__);\r
-\r
- if (this && (pDevice = this->pDevice) && (pdata = pDevice->hw)) {\r
- if (gpio_is_valid(pdata->irq_gpio)) {\r
- rc = gpio_request(pdata->irq_gpio, "sx9320_irq_gpio");\r
- if (rc < 0) {\r
- dev_err(this->pdev, "SX9320 Request gpio. Fail![%d]\n", rc);\r
- return rc;\r
- }\r
- rc = gpio_direction_input(pdata->irq_gpio);\r
- if (rc < 0) {\r
- dev_err(this->pdev, "SX9320 Set gpio direction. Fail![%d]\n", rc);\r
- return rc;\r
- }\r
- pinctrl_int = devm_pinctrl_get_select(this->pdev, "default");\r
-\r
- this->irq = client->irq = gpio_to_irq(pdata->irq_gpio);\r
- }\r
- else {\r
- dev_err(this->pdev, "SX9320 Invalid irq gpio num.(init)\n");\r
- }\r
- }\r
- else {\r
- pr_err("[SX9320] : %s - Do not init platform HW", __func__);\r
- }\r
-\r
- pr_err("[SX9320]: %s - sx9320_irq_debug\n",__func__);\r
- return rc;\r
-}\r
-\r
-static void sx932x_exit_platform_hw(struct i2c_client *client)\r
-{\r
- psx93XX_t this = i2c_get_clientdata(client);\r
- struct sx932x *pDevice = NULL;\r
- struct sx932x_platform_data *pdata = NULL;\r
- printk("%s:test\n",__func__);\r
- if (this && (pDevice = this->pDevice) && (pdata = pDevice->hw)) {\r
- if (gpio_is_valid(pdata->irq_gpio)) {\r
- gpio_free(pdata->irq_gpio);\r
- }\r
- else {\r
- dev_err(this->pdev, "Invalid irq gpio num.(exit)\n");\r
- }\r
- }\r
- return;\r
-}\r
-\r
-static int sx932x_get_nirq_state(void)\r
-{\r
- return !gpio_get_value(irq_gpio_num);\r
-}\r
-\r
-/*! \fn static int sx932x_probe(struct i2c_client *client, const struct i2c_device_id *id)\r
- * \brief Probe function\r
- * \param client pointer to i2c_client\r
- * \param id pointer to i2c_device_id\r
- * \return Whether probe was successful\r
- */\r
-static int sx932x_probe(struct i2c_client *client, const struct i2c_device_id *id)\r
-{\r
- int i = 0;\r
- int err = 0;\r
- int ret = 0;\r
- psx93XX_t this = 0;\r
- psx932x_t pDevice = 0;\r
- psx932x_platform_data_t pplatData = 0;\r
- struct totalButtonInformation *pButtonInformationData = NULL;\r
- struct input_dev *input = NULL;\r
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);\r
-\r
- dev_info(&client->dev, "sx932x_probe()\n");\r
-\r
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) {\r
- dev_err(&client->dev, "Check i2c functionality.Fail!\n");\r
- err = -EIO;\r
- return err;\r
- }\r
-\r
- this = devm_kzalloc(&client->dev,sizeof(sx93XX_t), GFP_KERNEL); /* create memory for main struct */\r
- dev_info(&client->dev, "\t Initialized Main Memory: 0x%p\n",this);\r
-\r
- pButtonInformationData = devm_kzalloc(&client->dev , sizeof(struct totalButtonInformation), GFP_KERNEL);\r
- if (!pButtonInformationData) {\r
- dev_err(&client->dev, "Failed to allocate memory(totalButtonInformation)\n");\r
- err = -ENOMEM;\r
- return err;\r
- }\r
-\r
- pButtonInformationData->buttonSize = ARRAY_SIZE(psmtcButtons);\r
- pButtonInformationData->buttons = psmtcButtons;\r
- pplatData = devm_kzalloc(&client->dev,sizeof(struct sx932x_platform_data), GFP_KERNEL);\r
- if (!pplatData) {\r
- dev_err(&client->dev, "platform data is required!\n");\r
- return -EINVAL;\r
- }\r
- pplatData->cap_vdd = regulator_get(&client->dev, "cap_vdd");\r
- if (IS_ERR(pplatData->cap_vdd)) {\r
- if (PTR_ERR(pplatData->cap_vdd) == -EPROBE_DEFER) {\r
- ret = PTR_ERR(pplatData->cap_vdd);\r
- //goto err_vdd_defer;\r
- }\r
- dev_err(&client->dev,"%s: Failed to get regulator\n",\r
- __func__);\r
- } else {\r
- int error = regulator_enable(pplatData->cap_vdd);\r
-\r
- if (error) {\r
- regulator_put(pplatData->cap_vdd);\r
- dev_err(&client->dev,"%s: Error %d enable regulator\n",\r
- __func__, error);\r
- return error;\r
- }\r
- pplatData->cap_vdd_en = true;\r
- dev_info(&client->dev,"cap_vdd regulator is %s\n",\r
- regulator_is_enabled(pplatData->cap_vdd) ?\r
- "on" : "off");\r
- }\r
- pplatData->get_is_nirq_low = sx932x_get_nirq_state;\r
- pplatData->pbuttonInformation = pButtonInformationData;\r
-\r
- client->dev.platform_data = pplatData;\r
- err = sx932x_parse_dt(pplatData, &client->dev);\r
- if (err) {\r
- dev_err(&client->dev, "could not setup pin\n");\r
- return ENODEV;\r
- }\r
-\r
- pplatData->init_platform_hw = sx932x_init_platform_hw;\r
- dev_err(&client->dev, "SX9320 init_platform_hw done!\n");\r
-\r
- if (this){\r
- dev_info(&client->dev, "SX9320 initialize start!!");\r
- /* In case we need to reinitialize data\r
- * (e.q. if suspend reset device) */\r
- this->init = initialize;\r
- /* shortcut to read status of interrupt */\r
- this->refreshStatus = read_regStat;\r
- /* pointer to function from platform data to get pendown\r
- * (1->NIRQ=0, 0->NIRQ=1) */\r
- this->get_nirq_low = pplatData->get_is_nirq_low;\r
- /* save irq in case we need to reference it */\r
- this->irq = client->irq;\r
- /* do we need to create an irq timer after interrupt ? */\r
- this->useIrqTimer = 0;\r
-\r
- /* Setup function to call on corresponding reg irq source bit */\r
- if (MAX_NUM_STATUS_BITS>= 8)\r
- {\r
- this->statusFunc[0] = 0; /* TXEN_STAT */\r
- this->statusFunc[1] = 0; /* UNUSED */\r
- this->statusFunc[2] = 0; /* UNUSED */\r
- this->statusFunc[3] = read_rawData; /* CONV_STAT */\r
- this->statusFunc[4] = 0; /* COMP_STAT */\r
- this->statusFunc[5] = touchProcess; /* RELEASE_STAT */\r
- this->statusFunc[6] = touchProcess; /* TOUCH_STAT */\r
- this->statusFunc[7] = 0; /* RESET_STAT */\r
- }\r
-\r
- /* setup i2c communication */\r
- this->bus = client;\r
- i2c_set_clientdata(client, this);\r
-\r
- /* record device struct */\r
- this->pdev = &client->dev;\r
-\r
- /* create memory for device specific struct */\r
- this->pDevice = pDevice = devm_kzalloc(&client->dev,sizeof(sx932x_t), GFP_KERNEL);\r
- dev_info(&client->dev, "\t Initialized Device Specific Memory: 0x%p\n",pDevice);\r
-\r
- if (pDevice){\r
- /* for accessing items in user data (e.g. calibrate) */\r
- err = sysfs_create_group(&client->dev.kobj, &sx932x_attr_group);\r
- //sysfs_create_group(client, &sx932x_attr_group);\r
-\r
- /* Add Pointer to main platform data struct */\r
- pDevice->hw = pplatData;\r
-\r
- /* Check if we hava a platform initialization function to call*/\r
- if (pplatData->init_platform_hw)\r
- pplatData->init_platform_hw(client);\r
-\r
- /* Initialize the button information initialized with keycodes */\r
- pDevice->pbuttonInformation = pplatData->pbuttonInformation;\r
- /* Create the input device */\r
- input = input_allocate_device();\r
- if (!input) {\r
- return -ENOMEM;\r
- }\r
- /* Set all the keycodes */\r
- __set_bit(EV_KEY, input->evbit);\r
- #if 1\r
- for (i = 0; i < pButtonInformationData->buttonSize; i++) {\r
- __set_bit(pButtonInformationData->buttons[i].keycode,input->keybit);\r
- pButtonInformationData->buttons[i].state = IDLE;\r
- }\r
- #endif\r
- /* save the input pointer and finish initialization */\r
- pButtonInformationData->input = input;\r
- input->name = "SX9320 Cap Touch";\r
- input->id.bustype = BUS_I2C;\r
- if(input_register_device(input)){\r
- return -ENOMEM;\r
- }\r
- }\r
-\r
- sx93XX_IRQ_init(this);\r
- /* call init function pointer (this should initialize all registers */\r
- if (this->init){\r
- this->init(this);\r
- }else{\r
- dev_err(this->pdev,"No init function!!!!\n");\r
- return -ENOMEM;\r
- }\r
- }else{\r
- return -1;\r
- }\r
-\r
- sx932x_Hardware_Check(this);\r
- pplatData->exit_platform_hw = sx932x_exit_platform_hw;\r
- dev_info(&client->dev, "sx932x_probe() Done\n");\r
-\r
- return 0;\r
-}\r
-\r
-/*! \fn static int sx932x_remove(struct i2c_client *client)\r
- * \brief Called when device is to be removed\r
- * \param client Pointer to i2c_client struct\r
- * \return Value from sx93XX_remove()\r
- */\r
-//static int __devexit sx932x_remove(struct i2c_client *client)\r
-static int sx932x_remove(struct i2c_client *client)\r
-{\r
- psx932x_platform_data_t pplatData =0;\r
- psx932x_t pDevice = 0;\r
- psx93XX_t this = i2c_get_clientdata(client);\r
- if (this && (pDevice = this->pDevice))\r
- {\r
- input_unregister_device(pDevice->pbuttonInformation->input);\r
-\r
- sysfs_remove_group(&client->dev.kobj, &sx932x_attr_group);\r
- pplatData = client->dev.platform_data;\r
- if (pplatData && pplatData->exit_platform_hw)\r
- pplatData->exit_platform_hw(client);\r
- kfree(this->pDevice);\r
- }\r
- return sx93XX_remove(this);\r
-}\r
-#if 1//def CONFIG_PM\r
-/*====================================================*/\r
-/***** Kernel Suspend *****/\r
-static int sx932x_suspend(struct device *dev)\r
-{\r
- psx93XX_t this = dev_get_drvdata(dev);\r
- sx93XX_suspend(this);\r
- return 0;\r
-}\r
-/***** Kernel Resume *****/\r
-static int sx932x_resume(struct device *dev)\r
-{\r
- psx93XX_t this = dev_get_drvdata(dev);\r
- sx93XX_resume(this);\r
- return 0;\r
-}\r
-/*====================================================*/\r
-#else\r
-#define sx932x_suspend NULL\r
-#define sx932x_resume NULL\r
-#endif /* CONFIG_PM */\r
-\r
-static struct i2c_device_id sx932x_idtable[] = {\r
- { DRIVER_NAME, 0 },\r
- { }\r
-};\r
-MODULE_DEVICE_TABLE(i2c, sx932x_idtable);\r
-#ifdef CONFIG_OF\r
-static struct of_device_id sx932x_match_table[] = {\r
- { .compatible = "Semtech,sx932x",},\r
- { },\r
-};\r
-#else\r
-#define sx932x_match_table NULL\r
-#endif\r
-static const struct dev_pm_ops sx932x_pm_ops = {\r
- .suspend = sx932x_suspend,\r
- .resume = sx932x_resume,\r
-};\r
-static struct i2c_driver sx932x_driver = {\r
- .driver = {\r
- .owner = THIS_MODULE,\r
- .name = DRIVER_NAME,\r
- .of_match_table = sx932x_match_table,\r
- .pm = &sx932x_pm_ops,\r
- },\r
- .id_table = sx932x_idtable,\r
- .probe = sx932x_probe,\r
- .remove = sx932x_remove,\r
-};\r
-static int __init sx932x_I2C_init(void)\r
-{\r
- return i2c_add_driver(&sx932x_driver);\r
-}\r
-static void __exit sx932x_I2C_exit(void)\r
-{\r
- i2c_del_driver(&sx932x_driver);\r
-}\r
-\r
-module_init(sx932x_I2C_init);\r
-module_exit(sx932x_I2C_exit);\r
-\r
-MODULE_AUTHOR("Semtech Corp. (http://www.semtech.com/)");\r
-MODULE_DESCRIPTION("SX9320 Capacitive Touch Controller Driver");\r
-MODULE_LICENSE("GPL");\r
-MODULE_VERSION("0.1");\r
-\r
-static void sx93XX_schedule_work(psx93XX_t this, unsigned long delay)\r
-{\r
- unsigned long flags;\r
- if (this) {\r
- dev_info(this->pdev, "sx93XX_schedule_work()\n");\r
- spin_lock_irqsave(&this->lock,flags);\r
- /* Stop any pending penup queues */\r
- cancel_delayed_work(&this->dworker);\r
- //after waiting for a delay, this put the job in the kernel-global workqueue. so no need to create new thread in work queue.\r
- schedule_delayed_work(&this->dworker,delay);\r
- spin_unlock_irqrestore(&this->lock,flags);\r
- }\r
- else\r
- printk(KERN_ERR "sx93XX_schedule_work, NULL psx93XX_t\n");\r
-} \r
-\r
-static irqreturn_t sx93XX_irq(int irq, void *pvoid)\r
-{\r
- psx93XX_t this = 0;\r
- if (pvoid) {\r
- this = (psx93XX_t)pvoid;\r
- if ((!this->get_nirq_low) || this->get_nirq_low()) {\r
- sx93XX_schedule_work(this,0);\r
- }\r
- else{\r
- dev_err(this->pdev, "sx93XX_irq - nirq read high\n");\r
- }\r
- }\r
- else{\r
- printk(KERN_ERR "sx93XX_irq, NULL pvoid\n");\r
- }\r
- return IRQ_HANDLED;\r
-}\r
-\r
-static void sx93XX_worker_func(struct work_struct *work)\r
-{\r
- psx93XX_t this = 0;\r
- int status = 0;\r
- int counter = 0;\r
- u8 nirqLow = 0;\r
- if (work) {\r
- this = container_of(work,sx93XX_t,dworker.work);\r
-\r
- if (!this) {\r
- printk(KERN_ERR "sx93XX_worker_func, NULL sx93XX_t\n");\r
- return;\r
- }\r
- if (unlikely(this->useIrqTimer)) {\r
- if ((!this->get_nirq_low) || this->get_nirq_low()) {\r
- nirqLow = 1;\r
- }\r
- }\r
- /* since we are not in an interrupt don't need to disable irq. */\r
- status = this->refreshStatus(this);\r
- counter = -1;\r
- dev_dbg(this->pdev, "Worker - Refresh Status %d\n",status);\r
-\r
- while((++counter) < MAX_NUM_STATUS_BITS) { /* counter start from MSB */\r
- if (((status>>counter) & 0x01) && (this->statusFunc[counter])) {\r
- dev_info(this->pdev, "SX9320 Function Pointer Found. Calling\n");\r
- this->statusFunc[counter](this);\r
- }\r
- }\r
- if (unlikely(this->useIrqTimer && nirqLow))\r
- { /* Early models and if RATE=0 for newer models require a penup timer */\r
- /* Queue up the function again for checking on penup */\r
- sx93XX_schedule_work(this,msecs_to_jiffies(this->irqTimeout));\r
- }\r
- } else {\r
- printk(KERN_ERR "sx93XX_worker_func, NULL work_struct\n");\r
- }\r
-}\r
-\r
-int sx93XX_remove(psx93XX_t this)\r
-{\r
- if (this) {\r
- cancel_delayed_work_sync(&this->dworker); /* Cancel the Worker Func */\r
- /*destroy_workqueue(this->workq); */\r
- free_irq(this->irq, this);\r
- kfree(this);\r
- return 0;\r
- }\r
- return -ENOMEM;\r
-}\r
-void sx93XX_suspend(psx93XX_t this)\r
-{\r
- if (this)\r
- disable_irq(this->irq);\r
-\r
- //write_register(this,SX932x_CTRL1_REG,0x20);//make sx932x in Sleep mode\r
-}\r
-void sx93XX_resume(psx93XX_t this)\r
-{\r
- if (this) {\r
- sx93XX_schedule_work(this,0);\r
- if (this->init)\r
- this->init(this);\r
- enable_irq(this->irq);\r
- }\r
-}\r
-\r
-int sx93XX_IRQ_init(psx93XX_t this)\r
-{\r
- int err = 0;\r
- if (this && this->pDevice)\r
- {\r
- /* initialize spin lock */\r
- spin_lock_init(&this->lock);\r
- /* initialize worker function */\r
- INIT_DELAYED_WORK(&this->dworker, sx93XX_worker_func);\r
- /* initailize interrupt reporting */\r
- this->irq_disabled = 0;\r
- err = request_irq(this->irq, sx93XX_irq, IRQF_TRIGGER_FALLING,\r
- this->pdev->driver->name, this);\r
- if (err) {\r
- dev_err(this->pdev, "irq %d busy?\n", this->irq);\r
- return err;\r
- }\r
- printk("%s:test registered with irq \n",__func__);\r
- dev_info(this->pdev, "registered with irq (%d)\n", this->irq);\r
- }\r
- return -ENOMEM;\r
-}\r
+++ /dev/null
-/*\r
-* This program is free software; you can redistribute it and/or\r
-* modify it under the terms of the GNU General Public License\r
-* version 2 as published by the Free Software Foundation.\r
-*/\r
-#ifndef SX932x_H\r
-#define SX932x_H\r
-\r
-#include <linux/device.h>\r
-#include <linux/slab.h>\r
-#include <linux/interrupt.h>\r
-\r
-#if 0\r
-#include <linux/wakelock.h>\r
-#include <linux/earlysuspend.h>\r
-#include <linux/suspend.h>\r
-#endif\r
-\r
-/*\r
-* I2C Registers\r
-*/\r
-//-Interrupt and status\r
-#define SX932x_IRQSTAT_REG 0x00\r
-#define SX932x_STAT0_REG 0x01\r
-#define SX932x_STAT1_REG 0x02\r
-#define SX932x_STAT2_REG 0x03\r
-#define SX932x_STAT3_REG 0x04\r
-#define SX932x_IRQ_ENABLE_REG 0x05\r
-#define SX932x_IRQCFG0_REG 0x06\r
-#define SX932x_IRQCFG1_REG 0x07\r
-#define SX932x_IRQCFG2_REG 0x08\r
-//-General control\r
-#define SX932x_CTRL0_REG 0x10\r
-#define SX932x_CTRL1_REG 0x11\r
-#define SX932x_I2CADDR_REG 0x14\r
-#define SX932x_CLKSPRD 0x15\r
-//-AFE Control\r
-#define SX932x_AFE_CTRL0_REG 0x20\r
-#define SX932x_AFE_CTRL1_REG 0x21\r
-#define SX932x_AFE_CTRL2_REG 0x22\r
-#define SX932x_AFE_CTRL3_REG 0x23\r
-#define SX932x_AFE_CTRL4_REG 0x24\r
-#define SX932x_AFE_CTRL5_REG 0x25\r
-#define SX932x_AFE_CTRL6_REG 0x26\r
-#define SX932x_AFE_CTRL7_REG 0x27\r
-#define SX932x_AFE_PH0_REG 0x28\r
-#define SX932x_AFE_PH1_REG 0x29\r
-#define SX932x_AFE_PH2_REG 0x2A\r
-#define SX932x_AFE_PH3_REG 0x2B\r
-#define SX932x_AFE_CTRL8 0x2C\r
-#define SX932x_AFE_CTRL9 0x2D\r
-//-Main Digital Processing (Prox) control\r
-#define SX932x_PROX_CTRL0_REG 0x30\r
-#define SX932x_PROX_CTRL1_REG 0x31\r
-#define SX932x_PROX_CTRL2_REG 0x32\r
-#define SX932x_PROX_CTRL3_REG 0x33\r
-#define SX932x_PROX_CTRL4_REG 0x34\r
-#define SX932x_PROX_CTRL5_REG 0x35\r
-#define SX932x_PROX_CTRL6_REG 0x36\r
-#define SX932x_PROX_CTRL7_REG 0x37\r
-//-Advanced Digital Processing control\r
-#define SX932x_ADV_CTRL0_REG 0x40\r
-#define SX932x_ADV_CTRL1_REG 0x41\r
-#define SX932x_ADV_CTRL2_REG 0x42\r
-#define SX932x_ADV_CTRL3_REG 0x43\r
-#define SX932x_ADV_CTRL4_REG 0x44\r
-#define SX932x_ADV_CTRL5_REG 0x45\r
-#define SX932x_ADV_CTRL6_REG 0x46\r
-#define SX932x_ADV_CTRL7_REG 0x47\r
-#define SX932x_ADV_CTRL8_REG 0x48\r
-#define SX932x_ADV_CTRL9_REG 0x49\r
-#define SX932x_ADV_CTRL10_REG 0x4A\r
-#define SX932x_ADV_CTRL11_REG 0x4B\r
-#define SX932x_ADV_CTRL12_REG 0x4C\r
-#define SX932x_ADV_CTRL13_REG 0x4D\r
-#define SX932x_ADV_CTRL14_REG 0x4E\r
-#define SX932x_ADV_CTRL15_REG 0x4F\r
-#define SX932x_ADV_CTRL16_REG 0x50\r
-#define SX932x_ADV_CTRL17_REG 0x51\r
-#define SX932x_ADV_CTRL18_REG 0x52\r
-#define SX932x_ADV_CTRL19_REG 0x53\r
-#define SX932x_ADV_CTRL20_REG 0x54\r
-/* Sensor Readback */\r
-#define SX932x_CPSRD 0x60\r
-#define SX932x_USEMSB 0x61\r
-#define SX932x_USELSB 0x62\r
-#define SX932x_AVGMSB 0x63\r
-#define SX932x_AVGLSB 0x64\r
-#define SX932x_DIFFMSB 0x65\r
-#define SX932x_DIFFLSB 0x66\r
-#define SX932x_OFFSETMSB 0x67\r
-#define SX932x_OFFSETLSB 0x68\r
-#define SX932x_SARMSB 0x69\r
-#define SX932x_SARLSB 0x6A\r
-\r
-#define SX932x_SOFTRESET_REG 0x9F\r
-#define SX932x_WHOAMI_REG 0xFA\r
-#define SX932x_REV_REG 0xFB\r
-\r
-/* IrqStat 0:Inactive 1:Active */\r
-#define SX932x_IRQSTAT_RESET_FLAG 0x80\r
-#define SX932x_IRQSTAT_TOUCH_FLAG 0x40\r
-#define SX932x_IRQSTAT_RELEASE_FLAG 0x20\r
-#define SX932x_IRQSTAT_COMPDONE_FLAG 0x10\r
-#define SX932x_IRQSTAT_CONV_FLAG 0x08\r
-#define SX932x_IRQSTAT_PROG2_FLAG 0x04\r
-#define SX932x_IRQSTAT_PROG1_FLAG 0x02\r
-#define SX932x_IRQSTAT_PROG0_FLAG 0x01\r
-\r
-\r
-/* RegStat0 */\r
-#define SX932x_PROXSTAT_PH3_FLAG 0x08\r
-#define SX932x_PROXSTAT_PH2_FLAG 0x04\r
-#define SX932x_PROXSTAT_PH1_FLAG 0x02\r
-#define SX932x_PROXSTAT_PH0_FLAG 0x01\r
-\r
-/* SoftReset */\r
-#define SX932x_SOFTRESET 0xDE\r
-#define SX932x_WHOAMI_VALUE 0x22 //just for sx9325\r
-#define SX932x_REV_VALUE 0x22 //just for sx9325\r
-\r
-#define LGE_SENSOR\r
-\r
-/**************************************\r
-* define platform data\r
-*\r
-**************************************/\r
-struct smtc_reg_data {\r
- unsigned char reg;\r
- unsigned char val;\r
-};\r
-typedef struct smtc_reg_data smtc_reg_data_t;\r
-typedef struct smtc_reg_data *psmtc_reg_data_t;\r
-\r
-\r
-struct _buttonInfo {\r
- /*! The Key to send to the input */\r
- int keycode;\r
- /*! Mask to look for on Touch Status */\r
- int mask;\r
- /*! Current state of button. */\r
- int state;\r
-};\r
-\r
-struct totalButtonInformation {\r
- struct _buttonInfo *buttons;\r
- int buttonSize;\r
- struct input_dev *input;\r
-};\r
-\r
-typedef struct totalButtonInformation buttonInformation_t;\r
-typedef struct totalButtonInformation *pbuttonInformation_t;\r
-\r
-/* Define Registers that need to be initialized to values different than\r
-* default\r
-*/\r
-static struct smtc_reg_data sx932x_i2c_reg_setup[] = {\r
-//Interrupt and config\r
- {\r
- .reg = SX932x_IRQ_ENABLE_REG, //0x05\r
- .val = 0x70, // Enavle Close and Far -> enable compensation interrupt\r
- },\r
- {\r
- .reg = SX932x_IRQCFG0_REG, //0x06\r
- .val = 0x00, //\r
- },\r
- {\r
- .reg = SX932x_IRQCFG1_REG, //0x07 \r
- .val = 0x00,\r
- },\r
- {\r
- .reg = SX932x_IRQCFG2_REG, //0x08\r
- .val = 0x00, //Activ Low\r
- },\r
- //--------General control\r
- {\r
- .reg = SX932x_CTRL0_REG, //0x10\r
- .val = 0x16, // Scanperiod : 100ms(10110)\r
- },\r
- {\r
- .reg = SX932x_I2CADDR_REG, //0x14\r
- .val = 0x00, //I2C Address : 0x28\r
- },\r
- {\r
- .reg = SX932x_CLKSPRD, //0x15\r
- .val = 0x00, //\r
- },\r
- //--------AFE Control\r
- {\r
- .reg = SX932x_AFE_CTRL0_REG, //0x20\r
- .val = 0x00, // CSx pin during sleep mode : HZ\r
- },\r
- {\r
- .reg = SX932x_AFE_CTRL1_REG, //0x21\r
- .val = 0x10, //reserved\r
- },\r
- {\r
- .reg = SX932x_AFE_CTRL2_REG, //0x22\r
- .val = 0x00, //reserved\r
- },\r
- {\r
- .reg = SX932x_AFE_CTRL3_REG, //0x23\r
- .val = 0x00, //Analog Range(ph0/1) : Small\r
- },\r
- {\r
- .reg = SX932x_AFE_CTRL4_REG, //0x24\r
- .val = 0x44, //Sampling Freq(ph0/1) : 83.33khz(01000), Resolution(ph0/1) : 128(100)\r
- },\r
- {\r
- .reg = SX932x_AFE_CTRL5_REG, //0x25\r
- .val = 0x00, //reserved\r
- },\r
- {\r
- .reg = SX932x_AFE_CTRL6_REG, //0x26\r
- .val = 0x01, //big//Analog Range(ph2/3) : Small\r
- },\r
- {\r
- .reg = SX932x_AFE_CTRL7_REG, //0x27\r
- .val = 0x44, //Sampling Freq(ph2/3) : 83.33khz(01000), Resolution(ph2/3) : 128(100)\r
- },\r
- {\r
- .reg = SX932x_AFE_PH0_REG, //0x28\r
- .val = 0x01, // CS2:HZ CS1:Input CS0 :HZ\r
- },\r
- {\r
- .reg = SX932x_AFE_PH1_REG, //0x29\r
- .val = 0x4, // CS2:Input CS1:HZ Shield CS0 :HZ\r
- },\r
- {\r
- .reg = SX932x_AFE_PH2_REG, //0x2A\r
- .val = 0x10, //CS2:HZ CS1:HZ CS0 :HZ \r
- },\r
- {\r
- .reg = SX932x_AFE_PH3_REG, //0x2B\r
- .val = 0x00, //CS2:HZ CS1:HZ CS0 :HZ\r
- },\r
- {\r
- .reg = SX932x_AFE_CTRL8, //0x2C\r
- .val = 0x12, // input register(kohm) 4(0010)\r
- },\r
- {\r
- .reg = SX932x_AFE_CTRL9, //0x2D\r
- .val = 0x08, // Analg gain : x1(1000)\r
- },\r
- //--------PROX control\r
- {\r
- .reg = SX932x_PROX_CTRL0_REG, //0x30\r
- .val = 0x09, // Digital Gain(ph0/1) : off(001) Digital Filter(ph0/1) : 1-1/2(001)\r
- },\r
- {\r
- .reg = SX932x_PROX_CTRL1_REG, //0x31\r
- .val = 0x09, // Digital Gain(ph2/3) : off(001) Digital Filter(ph2/3) : 1-1/2(001)\r
- },\r
- {\r
- .reg = SX932x_PROX_CTRL2_REG, //0x32\r
- .val = 0x08, //AVGNEGTHRESH : 16384\r
- },\r
- {\r
- .reg = SX932x_PROX_CTRL3_REG, // 0x33 \r
- .val = 0x20, //AVGPOSTHRESH : 16384\r
- },\r
- {\r
- .reg = SX932x_PROX_CTRL4_REG, //0x34 \r
- .val = 0x0C, //AVGFREEZEDIS : on(0) ,AVGNEGFILT :1-1/2(001) ,AVGPOSFILT : 1-1/256(100)\r
- },\r
- {\r
- .reg = SX932x_PROX_CTRL5_REG, //0x35\r
- .val = 0x00, //FARCOND: PROXDIFF < (THRESH.HYST), HYST : None, CLOSEDEB : off ,FARDEB : off\r
- },\r
- {\r
- .reg = SX932x_PROX_CTRL6_REG, //0x36\r
- .val = 0x1B, // Prox Theshold(ph0/1) : 200\r
- },\r
- {\r
- .reg = SX932x_PROX_CTRL7_REG, //0x37\r
- .val = 0x1B, // Prox Theshold(ph2/3) : 200\r
- },\r
- //--------Advanced control (defult)\r
- {\r
- .reg = SX932x_ADV_CTRL0_REG,\r
- .val = 0x00,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL1_REG,\r
- .val = 0x00,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL2_REG,\r
- .val = 0x00,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL3_REG,\r
- .val = 0x00,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL4_REG,\r
- .val = 0x00,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL5_REG,\r
- .val = 0x05,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL6_REG,\r
- .val = 0x00,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL7_REG,\r
- .val = 0x00,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL8_REG,\r
- .val = 0x00,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL9_REG,\r
- .val = 0x80,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL10_REG,\r
- .val = 0x00,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL11_REG,\r
- .val = 0x00,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL12_REG,\r
- .val = 0x00,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL13_REG,\r
- .val = 0x00,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL14_REG,\r
- .val = 0x80,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL15_REG,\r
- .val = 0x0C,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL16_REG,\r
- .val = 0x00,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL17_REG,\r
- .val = 0x70,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL18_REG,\r
- .val = 0x20,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL19_REG,\r
- .val = 0xe1,\r
- },\r
- {\r
- .reg = SX932x_ADV_CTRL20_REG,\r
- .val = 0xF0,\r
- },\r
- //--------Sensor enable\r
- {\r
- .reg = SX932x_CTRL1_REG, //0x11\r
- .val = 0x27, //enable PH2\r
- },\r
-};\r
-\r
-static struct _buttonInfo psmtcButtons[] = {\r
- {\r
- .keycode = KEY_0,\r
- .mask = SX932x_PROXSTAT_PH0_FLAG,\r
- },\r
- {\r
- .keycode = KEY_1,\r
- .mask = SX932x_PROXSTAT_PH1_FLAG,\r
- },\r
- {\r
- .keycode = KEY_2,\r
- .mask = SX932x_PROXSTAT_PH2_FLAG,\r
- },\r
- {\r
- .keycode = KEY_3,\r
- .mask = SX932x_PROXSTAT_PH3_FLAG,\r
- },\r
-};\r
-\r
-struct sx932x_platform_data {\r
- int i2c_reg_num;\r
- struct smtc_reg_data *pi2c_reg;\r
- int irq_gpio;\r
- struct regulator *cap_vdd;\r
- bool cap_vdd_en;\r
- pbuttonInformation_t pbuttonInformation;\r
-\r
- int (*get_is_nirq_low)(void);\r
- u8 input_mainsensor; // 0x01 ~ 0x02,//Startup function\r
- u8 input_refsensor; // 0x00 ~ 0x02,//Startup function\r
- \r
- int (*init_platform_hw)(struct i2c_client *client);\r
- void (*exit_platform_hw)(struct i2c_client *client);\r
-};\r
-typedef struct sx932x_platform_data sx932x_platform_data_t;\r
-typedef struct sx932x_platform_data *psx932x_platform_data_t;\r
-\r
-/***************************************\r
-* define data struct/interrupt\r
-*\r
-***************************************/\r
-\r
-#define MAX_NUM_STATUS_BITS (8)\r
-\r
-typedef struct sx93XX sx93XX_t, *psx93XX_t;\r
-struct sx93XX \r
-{\r
- void * bus; /* either i2c_client or spi_client */\r
-\r
- struct device *pdev; /* common device struction for linux */\r
-\r
- void *pDevice; /* device specific struct pointer */\r
-\r
- /* Function Pointers */\r
- int (*init)(psx93XX_t this); /* (re)initialize device */\r
-\r
- /* since we are trying to avoid knowing registers, create a pointer to a\r
- * common read register which would be to read what the interrupt source\r
- * is from \r
- */\r
- int (*refreshStatus)(psx93XX_t this); /* read register status */\r
-\r
- int (*get_nirq_low)(void); /* get whether nirq is low (platform data) */\r
-\r
- /* array of functions to call for corresponding status bit */\r
- void (*statusFunc[MAX_NUM_STATUS_BITS])(psx93XX_t this); \r
- \r
- /* Global variable */\r
- u8 failStatusCode; /*Fail status code*/\r
- bool reg_in_dts;\r
-\r
- spinlock_t lock; /* Spin Lock used for nirq worker function */\r
- int irq; /* irq number used */\r
-\r
- /* whether irq should be ignored.. cases if enable/disable irq is not used\r
- * or does not work properly */\r
- char irq_disabled;\r
-\r
- u8 useIrqTimer; /* older models need irq timer for pen up cases */\r
-\r
- int irqTimeout; /* msecs only set if useIrqTimer is true */\r
-\r
- /* struct workqueue_struct *ts_workq; */ /* if want to use non default */\r
- struct delayed_work dworker; /* work struct for worker function */\r
-};\r
-\r
-void sx93XX_suspend(psx93XX_t this);\r
-void sx93XX_resume(psx93XX_t this);\r
-int sx93XX_IRQ_init(psx93XX_t this);\r
-int sx93XX_remove(psx93XX_t this);\r
-\r
-#endif\r
#define SENSOR_TYPE_DEVICE_PRIVATE_BASE 0x10000
#define SENSOR_TYPE_CAP_PROX (SENSOR_TYPE_DEVICE_PRIVATE_BASE + 2)
+#define SENSOR_TYPE_MOTO_CAPSENSE (SENSOR_TYPE_DEVICE_PRIVATE_BASE + 16)
enum LIS3DH_AXIS {
AXIS_X = 0,