summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTroy Kisky <troy.kisky@boundarydevices.com>2012-07-03 17:04:57 -0700
committerTroy Kisky <troy.kisky@boundarydevices.com>2012-07-03 17:04:57 -0700
commit2f0c84b3619deaaebeb3daa0166bed54bfdac190 (patch)
tree121ecf51a8d628f9f9af642f859dcf8956a2d508
parentbfecaa2c2dc7583a42523c2ab136d21a97887289 (diff)
parente06baef465e2ee37e286b264182d4a23f8f2c067 (diff)
Merge branch 'boundary-L3.0.15_12.04.01' of github.com:boundarydevices/linux-imx6 into boundary-L3.0.15_12.04.013.0-boundary-imx6-201207031704
-rw-r--r--arch/arm/configs/nitrogen6x_defconfig3
-rw-r--r--arch/arm/mach-mx6/board-mx6q_sabrelite.c10
-rw-r--r--drivers/input/touchscreen/Kconfig27
-rw-r--r--drivers/input/touchscreen/Makefile2
-rw-r--r--drivers/input/touchscreen/egalax_ts.c7
-rw-r--r--drivers/input/touchscreen/ft5x06_ts.c550
-rw-r--r--drivers/tty/serial/imx.c90
7 files changed, 641 insertions, 48 deletions
diff --git a/arch/arm/configs/nitrogen6x_defconfig b/arch/arm/configs/nitrogen6x_defconfig
index 0c3b5151b863..f387d81f50ec 100644
--- a/arch/arm/configs/nitrogen6x_defconfig
+++ b/arch/arm/configs/nitrogen6x_defconfig
@@ -1078,6 +1078,7 @@ CONFIG_INPUT_TOUCHSCREEN=y
# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set
# CONFIG_TOUCHSCREEN_EETI is not set
CONFIG_TOUCHSCREEN_EGALAX=y
+CONFIG_TOUCHSCREEN_EGALAX_SINGLE_TOUCH=y
# CONFIG_TOUCHSCREEN_FUJITSU is not set
# CONFIG_TOUCHSCREEN_GUNZE is not set
# CONFIG_TOUCHSCREEN_ELO is not set
@@ -1100,6 +1101,8 @@ CONFIG_TOUCHSCREEN_TSC2004=y
# CONFIG_TOUCHSCREEN_ST1232 is not set
# CONFIG_TOUCHSCREEN_P1003 is not set
# CONFIG_TOUCHSCREEN_TPS6507X is not set
+CONFIG_TOUCHSCREEN_FT5X06=y
+CONFIG_TOUCHSCREEN_FT5X06_SINGLE_TOUCH=y
CONFIG_INPUT_MISC=y
# CONFIG_INPUT_AD714X is not set
# CONFIG_INPUT_ATI_REMOTE is not set
diff --git a/arch/arm/mach-mx6/board-mx6q_sabrelite.c b/arch/arm/mach-mx6/board-mx6q_sabrelite.c
index fed83bd95a20..c0065bfc24d4 100644
--- a/arch/arm/mach-mx6/board-mx6q_sabrelite.c
+++ b/arch/arm/mach-mx6/board-mx6q_sabrelite.c
@@ -694,6 +694,13 @@ static struct i2c_board_info mxc_i2c2_board_info[] __initdata = {
.platform_data = &tsc2007_info,
.irq = gpio_to_irq(MX6Q_SABRELITE_DRGB_IRQGPIO),
},
+#if defined(CONFIG_TOUCHSCREEN_FT5X06) \
+ || defined(CONFIG_TOUCHSCREEN_FT5X06_MODULE)
+ {
+ I2C_BOARD_INFO("ft5x06-ts", 0x38),
+ .irq = gpio_to_irq(MX6Q_SABRELITE_CAP_TCH_INT1),
+ },
+#endif
};
static void imx6q_sabrelite_usbotg_vbus(bool on)
@@ -970,6 +977,7 @@ static void __init sabrelite_add_device_buttons(void) {}
static iomux_v3_cfg_t n6x_sd2_pads[] = {
MX6Q_USDHC_PAD_SETTING(2, 50),
+ MX6Q_PAD_SD1_CLK__OSC32K_32K_OUT,
};
#ifdef CONFIG_WL12XX_PLATFORM_DATA
@@ -1257,8 +1265,8 @@ static void __init mx6_sabrelite_board_init(void)
imx6q_add_anatop_thermal_imx(1, &mx6q_sabrelite_anatop_thermal_data);
imx6_init_fec(fec_data);
imx6q_add_pm_imx(0, &mx6q_sabrelite_pm_data);
- imx6q_add_sdhci_usdhc_imx(3, &mx6q_sabrelite_sd4_data);
imx6q_add_sdhci_usdhc_imx(2, &mx6q_sabrelite_sd3_data);
+ imx6q_add_sdhci_usdhc_imx(3, &mx6q_sabrelite_sd4_data);
imx_add_viv_gpu(&imx6_gpu_data, &imx6q_gpu_pdata);
imx6q_sabrelite_init_usb();
imx6q_add_ahci(0, &mx6q_sabrelite_sata_data);
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index a9a3cabd0add..4ae5f8c7b850 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -187,6 +187,16 @@ config TOUCHSCREEN_EGALAX
To compile this driver as a module, choose M here: the
module will be called egalax_ts.
+config TOUCHSCREEN_EGALAX_SINGLE_TOUCH
+ bool "EETI eGalax touchscreen as single-touch"
+ default N
+ depends on TOUCHSCREEN_EGALAX
+ help
+ If you say yes here you get single-touch touchscreen support
+ on the eGalax I2C controller.
+ If you say "no", you'll get the normal multi-touch.
+
+
config TOUCHSCREEN_FUJITSU
tristate "Fujitsu serial touchscreen"
select SERIO
@@ -778,4 +788,21 @@ config TOUCHSCREEN_MAX11801
To compile this driver as a module, choose M here: the
module will be called max11801_ts
+
+config TOUCHSCREEN_FT5X06
+ tristate "Focaltech FT5X06 5 point touchscreen"
+ select I2C
+ help
+ If you say yes here you get touchscreen support through
+ FocalTech's FT5X06 controller.
+
+config TOUCHSCREEN_FT5X06_SINGLE_TOUCH
+ bool "FT5X06 touchscreen as single-touch"
+ default N
+ depends on TOUCHSCREEN_FT5X06
+ help
+ If you say yes here you get single-touch touchscreen support
+ on the FT5X06 I2C controller.
+ If you say "no", you'll get the normal 5-finger goodness.
+
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index a453dcd562e4..e9b438d8c386 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -66,3 +66,5 @@ obj-$(CONFIG_TOUCHSCREEN_P1003) += p1003_ts.o
obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o
obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
+obj-$(CONFIG_TOUCHSCREEN_FT5X06) += ft5x06_ts.o
+
diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c
index fff8afe1b41a..0b6cde77c421 100644
--- a/drivers/input/touchscreen/egalax_ts.c
+++ b/drivers/input/touchscreen/egalax_ts.c
@@ -139,7 +139,7 @@ retry:
events[id].x = x;
events[id].y = y;
-#ifdef FORCE_SINGLE_POINTER_SUPPORT
+#ifdef CONFIG_TOUCHSCREEN_EGALAX_SINGLE_TOUCH
input_report_abs(input_dev, ABS_X, x);
input_report_abs(input_dev, ABS_Y, y);
input_event(data->input_dev, EV_KEY, BTN_TOUCH, 1);
@@ -166,7 +166,7 @@ retry:
dev_dbg(&client->dev, "release id:%d\n", id);
events[id].valid = 0;
events[id].status = 0;
-#ifdef FORCE_SINGLE_POINTER_SUPPORT
+#ifdef CONFIG_TOUCHSCREEN_EGALAX_SINGLE_TOUCH
input_report_key(input_dev, BTN_TOUCH, 0);
input_report_abs(input_dev, ABS_PRESSURE, 0);
#else
@@ -256,8 +256,9 @@ static int __devinit egalax_ts_probe(struct i2c_client *client,
__set_bit(ABS_PRESSURE, input_dev->absbit);
input_set_abs_params(input_dev, ABS_X, 0, 32767, 0, 0);
input_set_abs_params(input_dev, ABS_Y, 0, 32767, 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1, 0, 0);
-#ifndef FORCE_SINGLE_POINTER_SUPPORT
+#ifndef CONFIG_TOUCHSCREEN_EGALAX_SINGLE_TOUCH
input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 32767, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, 32767, 0, 0);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c
new file mode 100644
index 000000000000..24cfe3fab381
--- /dev/null
+++ b/drivers/input/touchscreen/ft5x06_ts.c
@@ -0,0 +1,550 @@
+/*
+ * Boundary Devices FTx06 touch screen controller.
+ *
+ * Copyright (c) by Boundary Devices <info@boundarydevices.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+
+#ifdef CONFIG_TOUCHSCREEN_FT5X06_SINGLE_TOUCH
+#else
+#define USE_ABS_MT
+#endif
+
+struct point {
+ int x;
+ int y;
+};
+
+struct ft5x06_ts {
+ struct i2c_client *client;
+ struct input_dev *idev;
+ wait_queue_head_t sample_waitq;
+ struct semaphore sem;
+ struct completion init_exit;
+ struct task_struct *rtask;
+ int use_count;
+ int bReady;
+ int irq;
+ unsigned gp;
+ struct proc_dir_entry *procentry;
+};
+static const char *client_name = "ft5x06";
+
+struct ft5x06_ts *gts;
+
+static char const procentryname[] = {
+ "ft5x06"
+};
+
+static int ts_startup(struct ft5x06_ts *ts);
+static void ts_shutdown(struct ft5x06_ts *ts);
+
+static int ft5x06_proc_read
+ (char *page,
+ char **start,
+ off_t off,
+ int count,
+ int *eof,
+ void *data)
+{
+ printk(KERN_ERR "%s\n", __func__);
+ return 0 ;
+}
+
+static int
+ft5x06_proc_write
+ (struct file *file,
+ const char __user *buffer,
+ unsigned long count,
+ void *data)
+{
+ printk(KERN_ERR "%s\n", __func__);
+ return count ;
+}
+
+/*-----------------------------------------------------------------------*/
+static inline void ts_evt_add(struct ft5x06_ts *ts,
+ unsigned buttons, struct point *p)
+{
+ struct input_dev *idev = ts->idev;
+ int i;
+ if (!buttons) {
+ /* send release to user space. */
+#ifdef USE_ABS_MT
+ input_event(idev, EV_ABS, ABS_MT_TOUCH_MAJOR, 0);
+ input_event(idev, EV_KEY, BTN_TOUCH, 0);
+ input_mt_sync(idev);
+#else
+ input_report_abs(idev, ABS_PRESSURE, 0);
+ input_report_key(idev, BTN_TOUCH, 0);
+ input_sync(idev);
+#endif
+ } else {
+ for (i = 0; i < buttons; i++) {
+#ifdef USE_ABS_MT
+ input_event(idev, EV_ABS, ABS_MT_POSITION_X, p[i].x);
+ input_event(idev, EV_ABS, ABS_MT_POSITION_Y, p[i].y);
+ input_event(idev, EV_ABS, ABS_MT_TOUCH_MAJOR, 1);
+ input_mt_sync(idev);
+#else
+ input_report_abs(idev, ABS_X, p[i].x);
+ input_report_abs(idev, ABS_Y, p[i].y);
+ input_report_abs(idev, ABS_PRESSURE, 1);
+ input_report_key(idev, BTN_TOUCH, 1);
+ input_sync(idev);
+#endif
+ }
+ input_event(idev, EV_KEY, BTN_TOUCH, 1);
+ }
+#ifdef USE_ABS_MT
+ input_sync(idev);
+#endif
+}
+
+static int ts_open(struct input_dev *idev)
+{
+ struct ft5x06_ts *ts = input_get_drvdata(idev);
+ return ts_startup(ts);
+}
+
+static void ts_close(struct input_dev *idev)
+{
+ struct ft5x06_ts *ts = input_get_drvdata(idev);
+ ts_shutdown(ts);
+}
+
+static inline int ts_register(struct ft5x06_ts *ts)
+{
+ struct input_dev *idev;
+ idev = input_allocate_device();
+ if (idev == NULL)
+ return -ENOMEM;
+
+ ts->idev = idev;
+ idev->name = procentryname ;
+ idev->id.product = ts->client->addr;
+ idev->open = ts_open;
+ idev->close = ts_close;
+
+ __set_bit(EV_ABS, idev->evbit);
+ __set_bit(EV_KEY, idev->evbit);
+ __set_bit(BTN_TOUCH, idev->keybit);
+
+#ifdef USE_ABS_MT
+ input_set_abs_params(idev, ABS_MT_POSITION_X, 0, 1023, 0, 0);
+ input_set_abs_params(idev, ABS_MT_POSITION_Y, 0, 0x255, 0, 0);
+ input_set_abs_params(idev, ABS_MT_TOUCH_MAJOR, 0, 1, 0, 0);
+#else
+ __set_bit(EV_SYN, idev->evbit);
+ input_set_abs_params(idev, ABS_X, 0, 1023, 0, 0);
+ input_set_abs_params(idev, ABS_Y, 0, 0x255, 0, 0);
+ input_set_abs_params(idev, ABS_PRESSURE, 0, 1, 0, 0);
+#endif
+
+ input_set_drvdata(idev, ts);
+ return input_register_device(idev);
+}
+
+static inline void ts_deregister(struct ft5x06_ts *ts)
+{
+ if (ts->idev) {
+ input_unregister_device(ts->idev);
+ input_free_device(ts->idev);
+ ts->idev = NULL;
+ }
+}
+
+#ifdef DEBUG
+static void printHex(u8 const *buf, unsigned len)
+{
+ char hex[512];
+ char *next = hex ;
+ char *end = hex+sizeof(hex);
+
+ while (len--) {
+ next += snprintf(next, end-next, "%02x", *buf++);
+ if (next >= end) {
+ hex[sizeof(hex)-1] = '\0' ;
+ break;
+ }
+ }
+ printk(KERN_ERR "%s\n", hex);
+}
+#endif
+
+static void write_reg(struct ft5x06_ts *ts, int regnum, int value)
+{
+ u8 regnval[] = {
+ regnum,
+ value
+ };
+ struct i2c_msg pkt = {
+ ts->client->addr, 0, sizeof(regnval), regnval
+ };
+ int ret = i2c_transfer(ts->client->adapter, &pkt, 1);
+ if (ret != 1)
+ printk(KERN_WARNING "%s: i2c_transfer failed\n", __func__);
+ else
+ printk(KERN_DEBUG "%s: set register 0x%02x to 0x%02x\n",
+ __func__, regnum, value);
+}
+
+static void set_mode(struct ft5x06_ts *ts, int mode)
+{
+ write_reg(ts, 0, (mode&7)<<4);
+ printk(KERN_DEBUG "%s: changed mode to 0x%02x\n", __func__, mode);
+}
+
+#define WORK_MODE 0
+#define FACTORY_MODE 4
+
+/*-----------------------------------------------------------------------*/
+
+/*
+ * This is a RT kernel thread that handles the I2c accesses
+ * The I2c access functions are expected to be able to sleep.
+ */
+static int ts_thread(void *_ts)
+{
+ int ret;
+ struct point points[5];
+ unsigned char buf[33];
+ struct ft5x06_ts *ts = _ts;
+ unsigned char startch[1] = { 0 };
+ struct i2c_msg readpkt[2] = {
+ {ts->client->addr, 0, 1, startch},
+ {ts->client->addr, I2C_M_RD, sizeof(buf), buf}
+ };
+
+ struct task_struct *tsk = current;
+
+ ts->rtask = tsk;
+
+ daemonize("ft5x06tsd");
+ /* only want to receive SIGKILL */
+ allow_signal(SIGKILL);
+
+ complete(&ts->init_exit);
+
+ do {
+ int buttons = 0 ;
+ ts->bReady = 0;
+ ret = i2c_transfer(ts->client->adapter, readpkt,
+ ARRAY_SIZE(readpkt));
+ if (ret != ARRAY_SIZE(readpkt)) {
+ printk(KERN_WARNING "%s: i2c_transfer failed\n",
+ client_name);
+ msleep(1000);
+ } else {
+ int i;
+ unsigned char *p = buf+3;
+#ifdef DEBUG
+ printHex(buf, sizeof(buf));
+#endif
+ buttons = buf[2];
+ if (buttons > 5) {
+ printk(KERN_ERR
+ "%s: invalid button count %02x\n",
+ __func__, buttons);
+ buttons = 0 ;
+ } else {
+ for (i = 0; i < buttons; i++) {
+ points[i].x = ((p[0] << 8)
+ | p[1]) & 0x7ff;
+ points[i].y = ((p[2] << 8)
+ | p[3]) & 0x7ff;
+ p += 6;
+ }
+ }
+ }
+
+ if (signal_pending(tsk))
+ break;
+#ifdef DEBUG
+ printk(KERN_ERR "%s: buttons = %d, "
+ "points[0].x = %d, "
+ "points[0].y = %d\n",
+ client_name, buttons, points[0].x, points[0].y);
+#endif
+ ts_evt_add(ts, buttons, points);
+ if (0 < buttons)
+ wait_event_interruptible_timeout(ts->sample_waitq,
+ ts->bReady, HZ/20);
+ else
+ wait_event_interruptible(ts->sample_waitq, ts->bReady);
+ if (gpio_get_value(ts->gp)) {
+ if (buttons) {
+ buttons = 0;
+ ts_evt_add(ts, buttons, points);
+ }
+ if (signal_pending(tsk))
+ break;
+ }
+ } while (1);
+
+ ts->rtask = NULL;
+ complete_and_exit(&ts->init_exit, 0);
+}
+
+/*
+ * We only detect samples ready with this interrupt
+ * handler, and even then we just schedule our task.
+ */
+static irqreturn_t ts_interrupt(int irq, void *id)
+{
+ struct ft5x06_ts *ts = id;
+ int bit = gpio_get_value(ts->gp);
+ if (bit == 0) {
+ ts->bReady = 1;
+ wmb(); /* flush bReady */
+ wake_up(&ts->sample_waitq);
+ }
+ return IRQ_HANDLED;
+}
+
+#define ID_G_THGROUP 0x80
+#define ID_G_PERIODMONITOR 0x89
+#define FT5X0X_REG_HEIGHT_B 0x8a
+#define FT5X0X_REG_MAX_FRAME 0x8b
+#define FT5X0X_REG_FEG_FRAME 0x8e
+#define FT5X0X_REG_LEFT_RIGHT_OFFSET 0x92
+#define FT5X0X_REG_UP_DOWN_OFFSET 0x93
+#define FT5X0X_REG_DISTANCE_LEFT_RIGHT 0x94
+#define FT5X0X_REG_DISTANCE_UP_DOWN 0x95
+#define FT5X0X_REG_MAX_X_HIGH 0x98
+#define FT5X0X_REG_MAX_X_LOW 0x99
+#define FT5X0X_REG_MAX_Y_HIGH 0x9a
+#define FT5X0X_REG_MAX_Y_LOW 0x9b
+#define FT5X0X_REG_K_X_HIGH 0x9c
+#define FT5X0X_REG_K_X_LOW 0x9d
+#define FT5X0X_REG_K_Y_HIGH 0x9e
+#define FT5X0X_REG_K_Y_LOW 0x9f
+
+#define ID_G_AUTO_CLB 0xa0
+#define ID_G_B_AREA_TH 0xae
+
+#ifdef DEBUG
+static void dumpRegs(struct ft5x06_ts *ts, unsigned start, unsigned end)
+{
+ u8 regbuf[512];
+ unsigned char startch[1] = { start };
+ int ret ;
+ struct i2c_msg readpkt[2] = {
+ {ts->client->addr, 0, 1, startch},
+ {ts->client->addr, I2C_M_RD, end-start+1, regbuf}
+ };
+ ret = i2c_transfer(ts->client->adapter, readpkt, ARRAY_SIZE(readpkt));
+ if (ret != ARRAY_SIZE(readpkt)) {
+ printk(KERN_WARNING "%s: i2c_transfer failed\n", client_name);
+ } else {
+ printk(KERN_ERR "registers %02x..%02x\n", start, end);
+ printHex(regbuf, end-start+1);
+ }
+}
+#endif
+
+static int ts_startup(struct ft5x06_ts *ts)
+{
+ int ret = 0;
+ if (ts == NULL)
+ return -EIO;
+
+ if (down_interruptible(&ts->sem))
+ return -EINTR;
+
+ if (ts->use_count++ != 0)
+ goto out;
+
+ if (ts->rtask)
+ panic("ft5x06tsd: rtask running?");
+
+ ret = request_irq(ts->irq, &ts_interrupt, IRQF_TRIGGER_FALLING,
+ client_name, ts);
+ if (ret) {
+ printk(KERN_ERR "%s: request_irq failed, irq:%i\n",
+ client_name, ts->irq);
+ goto out;
+ }
+
+#ifdef DEBUG
+ set_mode(ts, FACTORY_MODE);
+ dumpRegs(ts, 0x4c, 0x4C);
+ write_reg(ts, 0x4C, 0x05);
+ dumpRegs(ts, 0, 0x4C);
+#endif
+ set_mode(ts, WORK_MODE);
+#ifdef DEBUG
+ dumpRegs(ts, 0x3b, 0x3b);
+ dumpRegs(ts, 0x6a, 0x6a);
+ dumpRegs(ts, ID_G_THGROUP, ID_G_PERIODMONITOR);
+ dumpRegs(ts, FT5X0X_REG_HEIGHT_B, FT5X0X_REG_K_Y_LOW);
+ dumpRegs(ts, ID_G_AUTO_CLB, ID_G_B_AREA_TH);
+#endif
+ set_mode(ts, WORK_MODE);
+
+ init_completion(&ts->init_exit);
+ ret = kernel_thread(ts_thread, ts, CLONE_KERNEL);
+ if (ret >= 0) {
+ wait_for_completion(&ts->init_exit);
+ ret = 0;
+ } else {
+ free_irq(ts->irq, ts);
+ }
+
+ out:
+ if (ret)
+ ts->use_count--;
+ up(&ts->sem);
+ return ret;
+}
+
+/*
+ * Release touchscreen resources. Disable IRQs.
+ */
+static void ts_shutdown(struct ft5x06_ts *ts)
+{
+ if (ts) {
+ down(&ts->sem);
+ if (--ts->use_count == 0) {
+ if (ts->rtask) {
+ send_sig(SIGKILL, ts->rtask, 1);
+ wait_for_completion(&ts->init_exit);
+ }
+ free_irq(ts->irq, ts);
+ }
+ up(&ts->sem);
+ }
+}
+/*-----------------------------------------------------------------------*/
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int ts_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+ strlcpy(info->type, "ft5x06-ts", I2C_NAME_SIZE);
+ return 0;
+}
+
+static int ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int err = 0;
+ struct ft5x06_ts *ts;
+ struct device *dev = &client->dev;
+ if (gts) {
+ printk(KERN_ERR "%s: Error gts is already allocated\n",
+ client_name);
+ return -ENOMEM;
+ }
+ ts = kzalloc(sizeof(struct ft5x06_ts), GFP_KERNEL);
+ if (!ts) {
+ dev_err(dev, "Couldn't allocate memory for %s\n", client_name);
+ return -ENOMEM;
+ }
+ init_waitqueue_head(&ts->sample_waitq);
+ sema_init(&ts->sem, 1);
+ ts->client = client;
+ ts->irq = client->irq ;
+ ts->gp = irq_to_gpio(client->irq);
+ printk(KERN_INFO "%s: %s touchscreen irq=%i, gp=%i\n", __func__,
+ client_name, ts->irq, ts->gp);
+ i2c_set_clientdata(client, ts);
+ err = ts_register(ts);
+ if (err == 0) {
+ gts = ts;
+ ts->procentry = create_proc_entry(procentryname, 0, NULL);
+ if (ts->procentry) {
+ ts->procentry->read_proc = ft5x06_proc_read ;
+ ts->procentry->write_proc = ft5x06_proc_write ;
+ }
+ } else {
+ printk(KERN_WARNING "%s: ts_register failed\n", client_name);
+ ts_deregister(ts);
+ kfree(ts);
+ }
+ return err;
+}
+
+static int ts_remove(struct i2c_client *client)
+{
+ struct ft5x06_ts *ts = i2c_get_clientdata(client);
+ remove_proc_entry(procentryname, 0);
+ if (ts == gts) {
+ gts = NULL;
+ ts_deregister(ts);
+ } else {
+ printk(KERN_ERR "%s: Error ts!=gts\n", client_name);
+ }
+ kfree(ts);
+ return 0;
+}
+
+
+/*-----------------------------------------------------------------------*/
+
+static const struct i2c_device_id ts_idtable[] = {
+ { "ft5x06-ts", 0 },
+ { }
+};
+
+static struct i2c_driver ts_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ft5x06-ts",
+ },
+ .id_table = ts_idtable,
+ .probe = ts_probe,
+ .remove = __devexit_p(ts_remove),
+ .detect = ts_detect,
+};
+
+static int __init ts_init(void)
+{
+ int res = i2c_add_driver(&ts_driver);
+ if (res) {
+ printk(KERN_WARNING "%s: i2c_add_driver failed\n", client_name);
+ return res;
+ }
+ printk(KERN_INFO "%s: " __DATE__ "\n", client_name);
+ return 0;
+}
+
+static void __exit ts_exit(void)
+{
+ i2c_del_driver(&ts_driver);
+}
+
+MODULE_AUTHOR("Boundary Devices <info@boundarydevices.com>");
+MODULE_DESCRIPTION("I2C interface for FocalTech ft5x06 touch screen controller.");
+MODULE_LICENSE("GPL");
+
+module_init(ts_init)
+module_exit(ts_exit)
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index d65c589cef60..a961f1df190b 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -1172,9 +1172,12 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long flags;
- unsigned int ucr2, old_ucr1, old_txrxen, baud, quot;
+ unsigned new_ucr2, old_ucr2;
+ unsigned new_ufcr, old_ufcr;
+ unsigned old_ubir, old_ubmr;
+ unsigned int baud, quot;
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
- unsigned int div, ufcr;
+ unsigned int div;
unsigned long num, denom;
uint64_t tdiv64;
@@ -1197,26 +1200,25 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
old_csize = CS8;
}
+ new_ucr2 = UCR2_SRST | UCR2_IRTS;
if ((termios->c_cflag & CSIZE) == CS8)
- ucr2 = UCR2_WS | UCR2_SRST | UCR2_IRTS;
- else
- ucr2 = UCR2_SRST | UCR2_IRTS;
+ new_ucr2 |= UCR2_WS;
if (termios->c_cflag & CRTSCTS) {
if( sport->have_rtscts ) {
- ucr2 &= ~UCR2_IRTS;
- ucr2 |= UCR2_CTSC;
+ new_ucr2 &= ~UCR2_IRTS;
+ new_ucr2 |= UCR2_CTSC;
} else {
termios->c_cflag &= ~CRTSCTS;
}
}
if (termios->c_cflag & CSTOPB)
- ucr2 |= UCR2_STPB;
+ new_ucr2 |= UCR2_STPB;
if (termios->c_cflag & PARENB) {
- ucr2 |= UCR2_PREN;
+ new_ucr2 |= UCR2_PREN;
if (termios->c_cflag & PARODD)
- ucr2 |= UCR2_PROE;
+ new_ucr2 |= UCR2_PROE;
}
/*
@@ -1229,7 +1231,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
spin_lock_irqsave(&sport->port.lock, flags);
- sport->port.read_status_mask = 0;
+ sport->port.read_status_mask = 0xff;
if (termios->c_iflag & INPCK)
sport->port.read_status_mask |= (URXD_FRMERR | URXD_PRERR);
if (termios->c_iflag & (BRKINT | PARMRK))
@@ -1256,22 +1258,6 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
*/
uart_update_timeout(port, termios->c_cflag, baud);
- /*
- * disable interrupts and drain transmitter
- */
- old_ucr1 = readl(sport->port.membase + UCR1);
- writel(old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN),
- sport->port.membase + UCR1);
-
- while ( !(readl(sport->port.membase + USR2) & USR2_TXDC))
- barrier();
-
- /* then, disable everything */
- old_txrxen = readl(sport->port.membase + UCR2);
- writel(old_txrxen & ~( UCR2_TXEN | UCR2_RXEN),
- sport->port.membase + UCR2);
- old_txrxen &= (UCR2_TXEN | UCR2_RXEN);
-
if (USE_IRDA(sport)) {
/*
* use maximum available submodule frequency to
@@ -1298,31 +1284,47 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
num -= 1;
denom -= 1;
- ufcr = readl(sport->port.membase + UFCR);
- ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
+ old_ufcr = readl(sport->port.membase + UFCR);
+ new_ufcr = (old_ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
- if (sport->use_dcedte)
- ufcr |= UFCR_DCEDTE;
+ old_ubir = readl(sport->port.membase + UBIR);
+ old_ubmr = readl(sport->port.membase + UBMR);
+ old_ucr2 = readl(sport->port.membase + UCR2) & ~UCR2_CTS;
+ new_ucr2 |= old_ucr2 & (UCR2_TXEN | UCR2_RXEN);
- writel(ufcr, sport->port.membase + UFCR);
-
- writel(num, sport->port.membase + UBIR);
- writel(denom, sport->port.membase + UBMR);
+ if (sport->use_dcedte)
+ new_ufcr |= UFCR_DCEDTE;
+ if ((old_ufcr != new_ufcr) || (old_ucr2 != new_ucr2) ||
+ (old_ubir != num) || (old_ubmr != denom)) {
+ int i;
+ /* software reset */
+ writel(readl(sport->port.membase + UCR2) &
+ ~(UCR2_TXEN | UCR2_RXEN | UCR2_SRST | UCR2_CTS),
+ sport->port.membase + UCR2);
+ for (i = 0; i < 2000; i++) {
+ unsigned uts = readl(sport->port.membase + UTS);
+ if (!(uts & UTS_SOFTRST))
+ break;
+ }
+ writel(new_ufcr, sport->port.membase + UFCR);
+ writel(num, sport->port.membase + UBIR);
+ writel(denom, sport->port.membase + UBMR);
- if (!cpu_is_mx1())
- writel(sport->port.uartclk / div / 1000,
+ if (!cpu_is_mx1())
+ writel(sport->port.uartclk / div / 1000,
sport->port.membase + MX2_ONEMS);
- writel(old_ucr1, sport->port.membase + UCR1);
+ /* set the parity, stop bits and data size */
+ writel(new_ucr2, sport->port.membase + UCR2);
- /* set the parity, stop bits and data size */
- writel(ucr2 | old_txrxen, sport->port.membase + UCR2);
+ if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
+ imx_enable_ms(&sport->port);
+ pr_info("old_ufcr=%x new_ufcr=%x, old_ucr2=%x new_ucr2=%x, old_ubir=%x num=%lx, old_ubmr=%x denom=%lx\n",
+ old_ufcr, new_ufcr, old_ucr2, new_ucr2, old_ubir, num, old_ubmr, denom);
+ pr_info("clk=%i div=%i num=%li denom=%li baud=%i\n", sport->port.uartclk, div, num+1, denom+1, baud);
+ }
spin_unlock_irqrestore(&sport->port.lock, flags);
-
- if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
- imx_enable_ms(&sport->port);
-
}
static const char *imx_type(struct uart_port *port)