summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt10
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.txt1
-rw-r--r--drivers/input/misc/ots_pat9125/pat9125_linux_driver.c470
-rw-r--r--drivers/input/misc/ots_pat9125/pixart_ots.c71
-rw-r--r--drivers/input/misc/ots_pat9125/pixart_ots.h16
-rw-r--r--drivers/input/misc/ots_pat9125/pixart_platform.h25
6 files changed, 593 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt b/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt
new file mode 100644
index 000000000000..02f21835f870
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt
@@ -0,0 +1,10 @@
+PixArt pat9125 rotating switch
+
+The Pixart's PAT9125 controller is connected to the host processor via I2C.
+It detects the rotation when user rotates the switch and generates interrupt
+to the Host processor. The host processor reads the direction and number of
+steps over I2C and passes the data to the rest of the system.
+
+Required properties:
+
+ - compatible : should be "pixart,pat9125".
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 1d7e54f68ee4..91412a10bf65 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -175,6 +175,7 @@ parade Parade Technologies Inc.
pericom Pericom Technology Inc.
phytec PHYTEC Messtechnik GmbH
picochip Picochip Ltd
+pixart PixArt Imaging Inc
plathome Plat'Home Co., Ltd.
plda PLDA
pixcir PIXCIR MICROELECTRONICS Co., Ltd
diff --git a/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c b/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c
new file mode 100644
index 000000000000..0a93f11e2b7e
--- /dev/null
+++ b/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c
@@ -0,0 +1,470 @@
+/* drivers/input/misc/ots_pat9125/pat9125_linux_driver.c
+ *
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/miscdevice.h>
+
+#include "pixart_ots.h"
+#include "pixart_platform.h"
+
+static int pat9125_init_input_data(void);
+
+#define pat9125_name "pixart_pat9125"
+
+#define pat9125_DEV_NAME pat9125_name
+
+static struct pat9125_linux_data_t pat9125data;
+
+static int pat9125_i2c_write(u8 reg, u8 *data, int len);
+static int pat9125_i2c_read(u8 reg, u8 *data);
+
+extern unsigned char ReadData(unsigned char addr)
+{
+ u8 data = 0xff;
+
+ pat9125_i2c_read(addr, &data);
+ return data;
+}
+extern void WriteData(unsigned char addr, unsigned char data)
+{
+ pat9125_i2c_write(addr, &data, 1);
+}
+extern void delay_ms(int ms)
+{
+ msleep(ms);
+}
+static int pat9125_i2c_write(u8 reg, u8 *data, int len)
+{
+ u8 buf[20];
+ int rc;
+ int ret = 0;
+ int i;
+
+ buf[0] = reg;
+ if (len >= 20) {
+ pr_debug(
+ "%s (%d) : FAILED: buffer size is limitted(20) %d\n",
+ __func__, __LINE__, len);
+ dev_err(&pat9125data.client->dev, "pat9125_i2c_write FAILED: buffer size is limitted(20)\n");
+ return -ENODEV;
+ }
+
+ for (i = 0 ; i < len; i++)
+ buf[i+1] = data[i];
+
+ /* Returns negative errno, or else the number of bytes written. */
+ rc = i2c_master_send(pat9125data.client, buf, len+1);
+
+ if (rc != len+1) {
+ pr_debug(
+ "%s (%d) : FAILED: writing to reg 0x%x\n",
+ __func__, __LINE__, reg);
+
+ ret = -ENODEV;
+ }
+
+ return ret;
+}
+
+static int pat9125_i2c_read(u8 reg, u8 *data)
+{
+
+ u8 buf[20];
+ int rc;
+
+ buf[0] = reg;
+
+ /*
+ * If everything went ok (i.e. 1 msg transmitted),
+ *return #bytes transmitted, else error code.
+ * thus if transmit is ok return value 1
+ */
+ rc = i2c_master_send(pat9125data.client, buf, 1);
+ if (rc != 1) {
+ pr_debug(
+ "%s (%d) : FAILED: writing to address 0x%x\n",
+ __func__, __LINE__, reg);
+ return -ENODEV;
+ }
+
+ /* returns negative errno, or else the number of bytes read */
+ rc = i2c_master_recv(pat9125data.client, buf, 1);
+ if (rc != 1) {
+ pr_debug(
+ "%s (%d) : FAILED: reading data\n",
+ __func__, __LINE__);
+ return -ENODEV;
+ }
+
+ *data = buf[0];
+ return 0;
+}
+
+void pixart_pat9125_ist(void)
+{
+
+}
+
+static irqreturn_t pixart_pat9125_irq(int irq, void *handle)
+{
+ pixart_pat9125_ist();
+ return IRQ_HANDLED;
+}
+
+static int pat9125_start(void)
+{
+ int err = (-1);
+ pr_debug(">>> %s (%d)\n", __func__, __LINE__);
+
+ err = request_threaded_irq(pat9125data.irq, NULL, pixart_pat9125_irq,
+ pat9125data.irq_flags,
+ "pixart_pat9125_irq",
+ &pat9125data);
+ if (err)
+ pr_debug("irq %d busy?\n", pat9125data.irq);
+
+ pat9125data.last_jiffies = jiffies_64;
+
+ return err;
+}
+
+static void pat9125_stop(void)
+{
+ free_irq(pat9125data.irq, &pat9125data);
+}
+
+static ssize_t pat9125_fops_read(struct file *filp,
+ char *buf, size_t count, loff_t *l)
+{
+ return 0;
+}
+
+static ssize_t pat9125_fops_write(struct file *filp,
+ const char *buf, size_t count, loff_t *f_ops)
+{
+ return 0;
+}
+
+static long pat9125_fops_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return 0;
+}
+
+static int pat9125_fops_open(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static int pat9125_fops_release(struct inode *inode, struct file *filp)
+{
+ pr_debug(">>> %s (%d)\n", __func__, __LINE__);
+ return 0;
+}
+static const struct file_operations pat9125_fops = {
+owner: THIS_MODULE,
+ read : pat9125_fops_read,
+ write : pat9125_fops_write,
+ /* ioctl : pat9125_fops_ioctl, */
+ unlocked_ioctl : pat9125_fops_ioctl,
+ open : pat9125_fops_open,
+ release : pat9125_fops_release,
+};
+
+/*----------------------------------------------------------------------------*/
+struct miscdevice pat9125_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = pat9125_name,
+ .fops = &pat9125_fops,
+};
+static ssize_t pat9125_test_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ char s[256];
+ char *p = s;
+
+ pr_debug("%s (%d) : write_reg_store\n", __func__, __LINE__);
+
+ memcpy(s, buf, sizeof(s));
+
+ *(s+1) = '\0';
+ *(s+4) = '\0';
+ *(s+7) = '\0';
+ /* example(in console): echo w 12 34 > rw_reg */
+ if (*p == 'w') {
+ long write_addr, write_data;
+
+ p += 2;
+ if (!kstrtol(p, 16, &write_addr)) {
+ p += 3;
+ if (!kstrtol(p, 16, &write_data)) {
+ pr_debug(
+ "w 0x%x 0x%x\n",
+ (u8)write_addr, (u8)write_data);
+ WriteData((u8)write_addr, (u8)write_data);
+ }
+ }
+ /* example(in console): echo r 12 > rw_reg */
+ } else if (*p == 'r') {
+ long read_addr;
+
+ p += 2;
+
+ if (!kstrtol(p, 16, &read_addr)) {
+ int data = 0;
+
+ data = ReadData((u8)read_addr);
+ pr_debug(
+ "r 0x%x 0x%x\n",
+ (unsigned int)read_addr, data);
+ }
+ }
+ return count;
+}
+
+static ssize_t pat9125_test_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+
+ /* cat */
+ pr_debug("%s (%d) :\n", __func__, __LINE__);
+
+ return 0;
+}
+static DEVICE_ATTR(
+ test,
+ S_IRUGO | S_IWUSR | S_IWGRP, pat9125_test_show, pat9125_test_store);
+static struct device_attribute *pat9125_attr_list[] = {
+ &dev_attr_test,
+};
+
+static int pat9125_create_attr(struct device *dev)
+{
+ int idx, err = 0;
+ int num = ARRAY_SIZE(pat9125_attr_list);
+
+ if (!dev)
+ return -EINVAL;
+ for (idx = 0; idx < num; idx++) {
+ err = device_create_file(dev, pat9125_attr_list[idx]);
+ if (err) {
+ pr_debug(
+ "device_create_file (%s) = %d\n",
+ pat9125_attr_list[idx]->attr.name, err);
+ break;
+ }
+ }
+ return err;
+}
+
+static int pat9125_i2c_probe(
+ struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err = 0;
+ struct device_node *np;
+
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+
+ pr_debug("%s (%d) : probe module....\n", __func__, __LINE__);
+
+ memset(&pat9125data, 0, sizeof(pat9125data));
+ err = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE);
+ if (err < 0)
+ goto error_return;
+
+ pat9125data.client = client;
+ err = misc_register(&pat9125_device);
+ if (err) {
+ pr_debug("pat9125_device register failed\n");
+ goto error_return;
+ }
+
+ pat9125data.pat9125_device = pat9125_device.this_device;
+ err = pat9125_create_attr(pat9125data.pat9125_device);
+ if (err) {
+ pr_debug("create attribute err = %d\n", err);
+ goto error_return;
+ }
+
+ if (pat9125_init_input_data() < 0)
+ goto error_return;
+
+ /* interrupt initialization */
+ pat9125data.i2c_dev = &client->dev;
+
+ np = pat9125data.i2c_dev->of_node;
+ pat9125data.irq_gpio = of_get_named_gpio_flags(np,
+ "pixart_pat9125,irq-gpio", 0, &pat9125data.irq_flags);
+
+ pr_debug(
+ "irq_gpio: %d, irq_flags: 0x%x\n",
+ pat9125data.irq_gpio, pat9125data.irq_flags);
+
+ if (!gpio_is_valid(pat9125data.irq_gpio)) {
+ err = (-1);
+ pr_debug(
+ "invalid irq_gpio: %d\n",
+ pat9125data.irq_gpio);
+ goto error_return;
+ }
+
+ err = gpio_request(pat9125data.irq_gpio, "pixart_pat9125_irq_gpio");
+ if (err) {
+ pr_debug(
+ "unable to request gpio [%d], [%d]\n",
+ pat9125data.irq_gpio, err);
+ goto error_return;
+ }
+
+ err = gpio_direction_input(pat9125data.irq_gpio);
+ if (err) {
+ pr_debug("unable to set dir for gpio[%d], [%d]\n",
+ pat9125data.irq_gpio, err);
+ goto error_return;
+ }
+
+ pat9125data.irq = gpio_to_irq(pat9125data.irq_gpio);
+
+ if (!OTS_Sensor_Init())
+ goto error_return;
+
+ if (!pat9125_start())
+ goto error_return;
+
+ return 0;
+
+error_return:
+
+ return err;
+
+}
+
+
+static int pat9125_i2c_remove(struct i2c_client *client)
+{
+
+ return 0;
+}
+
+static int pat9125_suspend(struct device *dev)
+{ pr_debug("%s (%d) : pat9125 suspend\n", __func__, __LINE__);
+ return 0;
+}
+
+static int pat9125_resume(struct device *dev)
+{
+ pr_debug("%s (%d) : pat9125 resume\n", __func__, __LINE__);
+ return 0;
+}
+
+static const struct i2c_device_id pat9125_device_id[] = {
+ {pat9125_DEV_NAME, 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, pat9125_device_id);
+
+static const struct dev_pm_ops pat9125_pm_ops = {
+ .suspend = pat9125_suspend,
+ .resume = pat9125_resume
+};
+
+static const struct of_device_id pixart_pat9125_match_table[] = {
+ { .compatible = "pixart,pat9125",},
+ { },
+};
+
+static struct i2c_driver pat9125_i2c_driver = {
+ .driver = {
+ .name = pat9125_DEV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &pat9125_pm_ops,
+ .of_match_table = pixart_pat9125_match_table,
+ },
+ .probe = pat9125_i2c_probe,
+ .remove = pat9125_i2c_remove,
+ .id_table = pat9125_device_id,
+};
+static int pat9125_open(struct input_dev *dev)
+{
+ pr_debug(">>> %s (%d)\n", __func__, __LINE__);
+ return 0;
+}
+
+static void pat9125_close(struct input_dev *dev)
+{
+ pr_debug(">>> %s (%d)\n", __func__, __LINE__);
+}
+
+static int pat9125_init_input_data(void)
+{
+ int ret = 0;
+
+ pr_debug("%s (%d) : initialize data\n", __func__, __LINE__);
+
+ pat9125data.pat9125_input_dev = input_allocate_device();
+
+ if (!pat9125data.pat9125_input_dev) {
+ pr_debug(
+ "%s (%d) : could not allocate mouse input device\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ input_set_drvdata(pat9125data.pat9125_input_dev, &pat9125data);
+ pat9125data.pat9125_input_dev->name = "Pixart pat9125";
+
+ pat9125data.pat9125_input_dev->open = pat9125_open;
+ pat9125data.pat9125_input_dev->close = pat9125_close;
+
+ ret = input_register_device(pat9125data.pat9125_input_dev);
+ if (ret < 0) {
+ input_free_device(pat9125data.pat9125_input_dev);
+ pr_debug(
+ "%s (%d) : could not register input device\n",
+ __func__, __LINE__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __init pat9125_linux_init(void)
+{
+ return i2c_add_driver(&pat9125_i2c_driver);
+}
+
+
+
+
+static void __exit pat9125_linux_exit(void)
+{
+ pr_debug("%s (%d) : exit module\n", __func__, __LINE__);
+ pat9125_stop();
+ misc_register(&pat9125_device);
+ i2c_del_driver(&pat9125_i2c_driver);
+}
+
+
+module_init(pat9125_linux_init);
+module_exit(pat9125_linux_exit);
+MODULE_AUTHOR("pixart");
+MODULE_DESCRIPTION("pixart pat9125 driver");
+MODULE_LICENSE("GPL");
+
+
+
diff --git a/drivers/input/misc/ots_pat9125/pixart_ots.c b/drivers/input/misc/ots_pat9125/pixart_ots.c
new file mode 100644
index 000000000000..70736197de3c
--- /dev/null
+++ b/drivers/input/misc/ots_pat9125/pixart_ots.c
@@ -0,0 +1,71 @@
+/* drivers/input/misc/ots_pat9125/pixart_ots.c
+ *
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ */
+
+#include "pixart_ots.h"
+
+static void OTS_WriteRead(uint8_t address, uint8_t wdata);
+
+bool OTS_Sensor_Init(void)
+{
+ unsigned char sensor_pid = 0, read_id_ok = 0;
+
+ /*
+ * Read sensor_pid in address 0x00 to check if the
+ * serial link is valid, read value should be 0x31.
+ */
+ sensor_pid = ReadData(0x00);
+
+ if (sensor_pid == 0x31) {
+ read_id_ok = 1;
+
+ /*
+ *PAT9125 sensor recommended settings:
+ * switch to bank0, not allowed to perform OTS_RegWriteRead
+ */
+ WriteData(0x7F, 0x00);
+ /*
+ * software reset (i.e. set bit7 to 1).
+ * It will reset to 0 automatically
+ * so perform OTS_RegWriteRead is not allowed.
+ */
+ WriteData(0x06, 0x97);
+
+ /* delay 1ms */
+ delay_ms(1);
+
+ /* disable write protect */
+ OTS_WriteRead(0x09, 0x5A);
+ /* set X-axis resolution (depends on application) */
+ OTS_WriteRead(0x0D, 0x65);
+ /* set Y-axis resolution (depends on application) */
+ OTS_WriteRead(0x0E, 0xFF);
+ /* set 12-bit X/Y data format (depends on application) */
+ OTS_WriteRead(0x19, 0x04);
+ /* ONLY for VDD=VDDA=1.7~1.9V: for power saving */
+ OTS_WriteRead(0x4B, 0x04);
+
+ if (ReadData(0x5E) == 0x04) {
+ OTS_WriteRead(0x5E, 0x08);
+ if (ReadData(0x5D) == 0x10)
+ OTS_WriteRead(0x5D, 0x19);
+ }
+ OTS_WriteRead(0x09, 0x00);/* enable write protect */
+ }
+ return read_id_ok;
+}
+
+static void OTS_WriteRead(uint8_t address, uint8_t wdata)
+{
+ uint8_t read_value;
+
+ do {
+ /* Write data to specified address */
+ WriteData(address, wdata);
+ /* Read back previous written data */
+ read_value = ReadData(address);
+ /* Check if the data is correctly written */
+ } while (read_value != wdata);
+}
diff --git a/drivers/input/misc/ots_pat9125/pixart_ots.h b/drivers/input/misc/ots_pat9125/pixart_ots.h
new file mode 100644
index 000000000000..ba1da1396ad1
--- /dev/null
+++ b/drivers/input/misc/ots_pat9125/pixart_ots.h
@@ -0,0 +1,16 @@
+/* drivers/input/misc/ots_pat9125/pixart_ots.h
+ *
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ */
+
+#ifndef _PIXART_OTS_H_
+#define _PIXART_OTS_H_
+
+#include "pixart_platform.h"
+
+/* export funtions */
+bool OTS_Sensor_Init(void);
+void OTS_Sensor_ReadMotion(int16_t *dx, int16_t *dy);
+
+#endif
diff --git a/drivers/input/misc/ots_pat9125/pixart_platform.h b/drivers/input/misc/ots_pat9125/pixart_platform.h
new file mode 100644
index 000000000000..a025fd06343e
--- /dev/null
+++ b/drivers/input/misc/ots_pat9125/pixart_platform.h
@@ -0,0 +1,25 @@
+/* drivers/input/misc/ots_pat9125/pixart_platform.h
+ *
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ */
+
+#ifndef _PIXART_PLATFORM_
+#define _PIXART_PLATFORM_
+
+#include <linux/input.h>
+#include <linux/pm.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+
+/* extern functions */
+extern unsigned char ReadData(unsigned char addr);
+extern void WriteData(unsigned char addr, unsigned char data);
+
+#endif