diff mbox

[1/4] Input: cyttsp4 - bus driver for Cypress TMA4XX touchscreen devices

Message ID 1344344978-30453-1-git-send-email-fery@cypress.com (mailing list archive)
State New, archived
Headers show

Commit Message

Ferruh Yigit Aug. 7, 2012, 1:09 p.m. UTC
From: Ferruh YIGIT <fery@cypress.com>

This driver is for Cypress TrueTouch(tm) Standard Product controllers,
Generation4 devices.

Driver consist of four main modules:

Bus driver: Linux bus driver implementation, binds other modules.
Core driver: Core module that communicate with TTSP controller.
MT driver: MultiTouch driver, converts touch information to host specific
touch events
Adapter driver: Communication adapter between host and controller, like
I2C or SPI.

This is Cyttsp4 TTSP Bus Driver,
Provides binding between Adapter, Core, and TTSP Modules.

A complete set of corresponding Adapter, Core, and TTSP module
devices and drivers must be registered with the TTSP Bus handler

Signed-off-by: Ferruh YIGIT <fery@cypress.com>
---
 drivers/input/touchscreen/Kconfig       |    9 +
 drivers/input/touchscreen/Makefile      |    1 +
 drivers/input/touchscreen/cyttsp4_bus.c |  608 +++++++++++++++++++++++++++++++
 include/linux/cyttsp4_bus.h             |  271 ++++++++++++++
 4 files changed, 889 insertions(+)
 create mode 100644 drivers/input/touchscreen/cyttsp4_bus.c
 create mode 100644 include/linux/cyttsp4_bus.h

--
1.7.9.5


This message and any attachments may contain Cypress (or its subsidiaries) confidential information. If it has been received in error, please advise the sender and immediately delete this message.
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Javier Martinez Canillas Aug. 7, 2012, 8:05 p.m. UTC | #1
On Tue, Aug 7, 2012 at 3:09 PM, Ferruh Yigit <fery@cypress.com> wrote:
> From: Ferruh YIGIT <fery@cypress.com>
>
> This driver is for Cypress TrueTouch(tm) Standard Product controllers,
> Generation4 devices.
>
> Driver consist of four main modules:
>
> Bus driver: Linux bus driver implementation, binds other modules.
> Core driver: Core module that communicate with TTSP controller.
> MT driver: MultiTouch driver, converts touch information to host specific
> touch events
> Adapter driver: Communication adapter between host and controller, like
> I2C or SPI.
>
> This is Cyttsp4 TTSP Bus Driver,
> Provides binding between Adapter, Core, and TTSP Modules.
>
> A complete set of corresponding Adapter, Core, and TTSP module
> devices and drivers must be registered with the TTSP Bus handler
>

Hi Ferruh,

There is already a driver in the kernel that supports Cypress
TrueTouch(TM) Standard Product (TTSP) controllers Generation3 (Cypress
Txx3xx parts).

The driver has a similar architecture that yours and it has a generic
driver to control the device and a driver for each communication bus
used to communicate with the controller. Drivers for SPI and I2C data
buses are already implemented.

The drivers are:

drivers/input/touchscreen/cyttsp_core.c
drivers/input/touchscreen/cyttsp_i2c.c
drivers/input/touchscreen/cyttsp_spi.c

This driver was original developed by Kevin for Android and used
multi-touch protocol type A. Since the hardware is able to track
contacts by hardware I added protocol type B support and cleaned the
driver to be merged on mainline.

I wonder how big is the delta between cyttsp Gen3 and cyttsp Gen4 and
if both drivers could be merged or at least refactored to reuse some
common code. I don't have the specification for any of the device
families but by looking at your code it seems that this may be
possible.

Thanks a lot and best regards,
Javier
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ferruh Yigit Aug. 8, 2012, 5:53 a.m. UTC | #2
Hello Javier,

We needed a new modular driver mechanism so that we can handle the very
flexible new generation of Cypress multi-touch devices (TMA4XX and TMA5XX).

With this initial set of patches we are providing the primary function
of multi-touch processing, according to protocol B, as the foundation
for future enhancements and future generations of our parts.

We plan to submit patches in the near future for additional modules such
as handling of integrated button codes and firmware class loader
capability.

The Gen3 memory map is very different from the Gen4 and Gen5 products,
however it is not difficult to upgrade the current Gen3 code into a new
set of modules that can fit into this model.

Adding a module patchset for Gen3 also will allow us to add Gen2 support
in that module as well.

Thanks and Best Regards,
ferruh

On 08/07/2012 11:05 PM, Javier Martinez Canillas wrote:
> On Tue, Aug 7, 2012 at 3:09 PM, Ferruh Yigit <fery@cypress.com> wrote:
>> From: Ferruh YIGIT <fery@cypress.com>
>>
>> This driver is for Cypress TrueTouch(tm) Standard Product controllers,
>> Generation4 devices.
>>
>> Driver consist of four main modules:
>>
>> Bus driver: Linux bus driver implementation, binds other modules.
>> Core driver: Core module that communicate with TTSP controller.
>> MT driver: MultiTouch driver, converts touch information to host specific
>> touch events
>> Adapter driver: Communication adapter between host and controller, like
>> I2C or SPI.
>>
>> This is Cyttsp4 TTSP Bus Driver,
>> Provides binding between Adapter, Core, and TTSP Modules.
>>
>> A complete set of corresponding Adapter, Core, and TTSP module
>> devices and drivers must be registered with the TTSP Bus handler
>>
> Hi Ferruh,
>
> There is already a driver in the kernel that supports Cypress
> TrueTouch(TM) Standard Product (TTSP) controllers Generation3 (Cypress
> Txx3xx parts).
>
> The driver has a similar architecture that yours and it has a generic
> driver to control the device and a driver for each communication bus
> used to communicate with the controller. Drivers for SPI and I2C data
> buses are already implemented.
>
> The drivers are:
>
> drivers/input/touchscreen/cyttsp_core.c
> drivers/input/touchscreen/cyttsp_i2c.c
> drivers/input/touchscreen/cyttsp_spi.c
>
> This driver was original developed by Kevin for Android and used
> multi-touch protocol type A. Since the hardware is able to track
> contacts by hardware I added protocol type B support and cleaned the
> driver to be merged on mainline.
>
> I wonder how big is the delta between cyttsp Gen3 and cyttsp Gen4 and
> if both drivers could be merged or at least refactored to reuse some
> common code. I don't have the specification for any of the device
> families but by looking at your code it seems that this may be
> possible.
>
> Thanks a lot and best regards,
> Javier


This message and any attachments may contain Cypress (or its subsidiaries) confidential information. If it has been received in error, please advise the sender and immediately delete this message.
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ferruh Yigit Aug. 24, 2012, 1:06 p.m. UTC | #3
On 08/07/2012 04:09 PM, Ferruh Yigit wrote:
> From: Ferruh YIGIT <fery@cypress.com>
>
> This driver is for Cypress TrueTouch(tm) Standard Product controllers,
> Generation4 devices.
>
> Driver consist of four main modules:
>
> Bus driver: Linux bus driver implementation, binds other modules.
> Core driver: Core module that communicate with TTSP controller.
> MT driver: MultiTouch driver, converts touch information to host specific
> touch events
> Adapter driver: Communication adapter between host and controller, like
> I2C or SPI.
>
> This is Cyttsp4 TTSP Bus Driver,
> Provides binding between Adapter, Core, and TTSP Modules.
>
> A complete set of corresponding Adapter, Core, and TTSP module
> devices and drivers must be registered with the TTSP Bus handler

Hi Henrik,

Is driver reviewed and is there anything we can help on it?

--

Thanks and Regards,
ferruh


This message and any attachments may contain Cypress (or its subsidiaries) confidential information. If it has been received in error, please advise the sender and immediately delete this message.
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Henrik Rydberg Aug. 24, 2012, 5:34 p.m. UTC | #4
Hi Ferruh,

> Hi Henrik,
> 
> Is driver reviewed and is there anything we can help on it?

As Javier mentioned, there is already a framework for the Cypress
devices in the kernel. Please make sure your patches build on what is
already there. Until then, I have nothing further to add.

Thanks,
Henrik
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ferruh Yigit Sept. 12, 2012, 1:16 p.m. UTC | #5
On 08/27/2012 08:46 AM, Ferruh Yigit wrote:
> On 08/24/2012 08:34 PM, Henrik Rydberg wrote:
>> Hi Ferruh,
>>
>> As Javier mentioned, there is already a framework for the Cypress
>> devices in the kernel. Please make sure your patches build on what is
>> already there. Until then, I have nothing further to add.
>>
>> Thanks,
>> Henrik
>
> Hi Henrik,
>
> Thank you for your response.
>
> We now understand the requirement for our driver and we will submit a
> new patchset as soon as possible.
>
> Best Regards,
> ferruh
>

Hello Henrik,

After Javier's and your comment, we started an effort to update driver
and re-structured it similar to the existing Gen3 driver. We are close
to submit a patch-set for updated code, but this effort showed that this
new patch-set also won't be an incremental commit to existing driver but
will have to introduce new files.

Existing driver in the kernel is for Cypress Gen3 devices and it is not
a framework to cover Gen4 devices, Gen4 devices has different host
interface.

This re-structure made driver simpler with cost of removing some
features like ttsp bus or merging modular layers that work with
subscription mechanism into one file with direct function calls.
The concern here for our side is, the next generation touchscreen
controllers may cause similar problem with this Gen3 structure while
adding support to Linux kernel for them.

Before finalizing the work, we want to learn if introducing new files
for new driver is acceptable and our effort is inline with your request,
can you please comment on it?

Thanks and Regards,
ferruh

This message and any attachments may contain Cypress (or its subsidiaries) confidential information. If it has been received in error, please advise the sender and immediately delete this message.
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Henrik Rydberg Sept. 12, 2012, 1:34 p.m. UTC | #6
Hi Ferruh,

> Before finalizing the work, we want to learn if introducing new files
> for new driver is acceptable and our effort is inline with your request,
> can you please comment on it?

New files are certainly alright when they are warranted, but of course
it depends on the details. We will know once you have submitted the
new version. :-)

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

Patch

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 1ba232c..4a65736 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -179,6 +179,15 @@  config TOUCHSCREEN_CYTTSP_SPI
          To compile this driver as a module, choose M here: the
          module will be called cyttsp_spi.

+config CYPRESS_CYTTSP4_BUS
+       bool "Cypress TTSP core bus"
+       default n
+       help
+         This option enables support Cypress TTSP core bus.
+         This support is needed for various device and drivers
+         using Cypress TrueTouch(TM) Standard Product
+         protocol.
+
 config TOUCHSCREEN_DA9034
        tristate "Touchscreen support for Dialog Semiconductor DA9034"
        depends on PMIC_DA903X
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 178eb12..ab84aec 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -73,3 +73,4 @@  obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE)    += mainstone-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)      += zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)      += w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)     += tps6507x-ts.o
+obj-$(CONFIG_CYPRESS_CYTTSP4_BUS) += cyttsp4_bus.o
diff --git a/drivers/input/touchscreen/cyttsp4_bus.c b/drivers/input/touchscreen/cyttsp4_bus.c
new file mode 100644
index 0000000..2e97088
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_bus.c
@@ -0,0 +1,608 @@ 
+/*
+ * cyttsp4_bus.c
+ * Cypress TrueTouch(TM) Standard Product V4 Bus Driver.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ * Copyright (C) 2011 Sony Ericsson Mobile Communications AB.
+ *
+ * Author: Aleksej Makarov aleksej.makarov@sonyericsson.com
+ * Modified by: Cypress Semiconductor for complete set of TTSP Bus interfaces.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#include <linux/cyttsp4_bus.h>
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/limits.h>
+
+static DEFINE_MUTEX(core_lock);
+static LIST_HEAD(adapter_list);
+static LIST_HEAD(core_dev_list);
+static LIST_HEAD(cyttsp4_dev_list);
+
+struct bus_type cyttsp4_bus_type;
+
+static void cyttsp4_dev_release(struct device *dev)
+{
+       dev_vdbg(dev, "%s: Enter\n", __func__);
+       put_device(dev->parent);
+}
+
+static struct device_type cyttsp4_dev_type = {
+       .release = cyttsp4_dev_release
+};
+
+static struct device_type cyttsp4_core_type = {
+       .release = cyttsp4_dev_release
+};
+
+static int _cyttsp4_register_dev(struct cyttsp4_device *pdev,
+               struct cyttsp4_core *core)
+{
+       int ret;
+
+       if (!pdev->dev.parent)
+               pdev->dev.parent = get_device(&core->dev);
+       /* Assign (new) core */
+       pdev->core = core;
+       /* Check whether this device is registered before */
+       if (pdev->dev.bus == &cyttsp4_bus_type &&
+                       pdev->dev.type == &cyttsp4_dev_type)
+               return 0;
+
+       pdev->dev.bus = &cyttsp4_bus_type;
+       pdev->dev.type = &cyttsp4_dev_type;
+       dev_set_name(&pdev->dev, "%s.%s", pdev->name,  core->id);
+
+       ret = device_register(&pdev->dev);
+       dev_dbg(&pdev->dev,
+               "%s: Registering device '%s'. Parent at '%s', err = %d\n",
+                __func__, dev_name(&pdev->dev),
+                dev_name(pdev->dev.parent), ret);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to register device, err %d\n",
+                       __func__, ret);
+               pdev->dev.bus = NULL;
+               pdev->dev.type = NULL;
+               pdev->core = NULL;
+       }
+       return ret;
+}
+
+static int _cyttsp4_register_core(struct cyttsp4_core *pdev,
+               struct cyttsp4_adapter *adap)
+{
+       int ret;
+
+       if (!pdev->dev.parent)
+               pdev->dev.parent = get_device(adap->dev);
+       /* Assign (new) adapter */
+       pdev->adap = adap;
+       /* Check whether this core is registered before */
+       if (pdev->dev.bus == &cyttsp4_bus_type &&
+                       pdev->dev.type == &cyttsp4_core_type)
+               return 0;
+
+       pdev->dev.bus = &cyttsp4_bus_type;
+       pdev->dev.type = &cyttsp4_core_type;
+       dev_set_name(&pdev->dev, "%s.%s", pdev->id,  adap->id);
+
+       ret = device_register(&pdev->dev);
+       dev_dbg(&pdev->dev,
+               "%s: Registering device '%s'. Parent at '%s', err = %d\n",
+                __func__, dev_name(&pdev->dev),
+                dev_name(pdev->dev.parent), ret);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to register device, err %d\n",
+                       __func__, ret);
+               pdev->dev.bus = NULL;
+               pdev->dev.type = NULL;
+               pdev->adap = NULL;
+       }
+       return ret;
+}
+
+static struct cyttsp4_adapter *find_adapter(char const *adap_id)
+{
+       struct cyttsp4_adapter *a;
+
+       list_for_each_entry(a, &adapter_list, node)
+               if (!strncmp(a->id, adap_id, NAME_MAX))
+                       return a;
+       return NULL;
+}
+
+static struct cyttsp4_core *find_core(char const *core_id)
+{
+       struct cyttsp4_core *d;
+
+       list_for_each_entry(d, &core_dev_list, node)
+               if (!strncmp(d->id, core_id, NAME_MAX) && d->dev.driver)
+                       return d;
+       return NULL;
+}
+
+static void rescan_devices(struct cyttsp4_core *core)
+{
+       struct cyttsp4_device *d;
+
+       list_for_each_entry(d, &cyttsp4_dev_list, node)
+               if (!d->core && !strncmp(core->id, d->core_id, NAME_MAX))
+                       _cyttsp4_register_dev(d, core);
+}
+
+static void rescan_cores(struct cyttsp4_adapter *adap)
+{
+       struct cyttsp4_core *d;
+
+       list_for_each_entry(d, &core_dev_list, node)
+               if (!d->adap && !strncmp(adap->id, d->adap_id, NAME_MAX))
+                       _cyttsp4_register_core(d, adap);
+}
+
+int cyttsp4_register_device(struct cyttsp4_device *pdev)
+{
+       int ret = 0;
+       struct cyttsp4_core *core;
+
+       if (!pdev)
+               return -EINVAL;
+       mutex_lock(&core_lock);
+       list_add(&pdev->node, &cyttsp4_dev_list);
+       pr_debug("%s: '%s' added to cyttsp4_dev_list\n", __func__, pdev->name);
+       core = find_core(pdev->core_id);
+       if (core)
+               ret = _cyttsp4_register_dev(pdev, core);
+       mutex_unlock(&core_lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(cyttsp4_register_device);
+
+static int cyttsp4_match_dev(struct device *dev, void *data)
+{
+       return dev == (struct device *)data;
+}
+
+void cyttsp4_unregister_device(struct cyttsp4_device *pdev)
+{
+       if (!pdev)
+               return;
+       mutex_lock(&core_lock);
+       if (bus_find_device(&cyttsp4_bus_type, NULL, &pdev->dev,
+                       cyttsp4_match_dev)) {
+               dev_dbg(&pdev->dev, "%s: Unregistering device '%s'.\n",
+                       __func__, dev_name(&pdev->dev));
+               /* Put reference taken by bus_find_device() */
+               put_device(&pdev->dev);
+               device_unregister(&pdev->dev);
+       }
+       list_del(&pdev->node);
+       pr_debug("%s: '%s' removed from cyttsp4_dev_list\n", __func__,
+               pdev->name);
+       mutex_unlock(&core_lock);
+}
+EXPORT_SYMBOL_GPL(cyttsp4_unregister_device);
+
+int cyttsp4_register_core_device(struct cyttsp4_core *pdev)
+{
+       int ret = 0;
+       struct cyttsp4_adapter *adap;
+
+       if (!pdev)
+               return -EINVAL;
+       mutex_lock(&core_lock);
+       if (find_core(pdev->id)) {
+               pr_debug("%s: core id '%s' already exists\n",
+                               __func__, pdev->id);
+               ret = -EINVAL;
+               goto fail;
+       }
+       list_add(&pdev->node, &core_dev_list);
+       pr_debug("%s: '%s' added to core_dev_list\n", __func__, pdev->name);
+       adap = find_adapter(pdev->adap_id);
+       if (adap) {
+               pr_debug("%s: adapter for '%s' is '%s'\n", __func__,
+                               pdev->id, dev_name(adap->dev));
+               ret = _cyttsp4_register_core(pdev, adap);
+               if (!ret)
+                       rescan_devices(pdev);
+       }
+fail:
+       mutex_unlock(&core_lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(cyttsp4_register_core_device);
+
+int cyttsp4_add_adapter(char const *id, struct cyttsp4_ops const *ops,
+               struct device *parent)
+{
+       int rc = 0;
+       struct cyttsp4_adapter *a;
+
+       if (!parent) {
+               dev_err(parent, "%s: need parent for '%s'\n", __func__, id);
+               return -EINVAL;
+       }
+       mutex_lock(&core_lock);
+       if (find_adapter(id)) {
+               dev_err(parent, "%s: adapter '%s' already exists\n",
+                               __func__, id);
+               rc = -EINVAL;
+               goto fail;
+       }
+       a = kzalloc(sizeof(*a), GFP_KERNEL);
+       if (!a) {
+               dev_err(parent, "%s: failed to allocate adapter '%s'\n",
+                               __func__, id);
+               rc = -ENOMEM;
+               goto fail;
+       }
+       memcpy(a->id, id, sizeof(a->id));
+       a->id[sizeof(a->id) - 1] = 0;
+       a->read = ops->read;
+       a->write = ops->write;
+       a->dev = parent;
+       list_add(&a->node, &adapter_list);
+       dev_dbg(parent, "%s: '%s' added to adapter_list\n", __func__, id);
+       rescan_cores(a);
+fail:
+       mutex_unlock(&core_lock);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(cyttsp4_add_adapter);
+
+int cyttsp4_del_adapter(char const *id)
+{
+       int rc = 0;
+       struct cyttsp4_adapter *adap;
+       struct cyttsp4_core *core_dev;
+
+       mutex_lock(&core_lock);
+       adap = find_adapter(id);
+       if (!adap) {
+               pr_err("%s: adapter '%s' does not exist\n",
+                       __func__, id);
+               rc = -EINVAL;
+               goto fail;
+       }
+
+       list_for_each_entry(core_dev, &core_dev_list, node)
+               if (core_dev->adap == adap)
+                       core_dev->adap = NULL;
+
+       list_del(&adap->node);
+       kfree(adap);
+       pr_debug("%s: '%s' removed from adapter_list\n", __func__, id);
+fail:
+       mutex_unlock(&core_lock);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(cyttsp4_del_adapter);
+
+static struct cyttsp4_device *verify_device_type(struct device *dev)
+{
+       return dev->type == &cyttsp4_dev_type ? to_cyttsp4_device(dev) : NULL;
+}
+
+static struct cyttsp4_core *verify_core_type(struct device *dev)
+{
+       return dev->type == &cyttsp4_core_type ? to_cyttsp4_core(dev) : NULL;
+}
+
+static int cyttsp4_match_device(struct cyttsp4_device *dev, const char *name)
+{
+       return strncmp(dev->name, name, NAME_MAX) == 0;
+}
+
+static int cyttsp4_match_core_device(struct cyttsp4_core *core,
+               const char *name)
+{
+       return strncmp(core->name, name, NAME_MAX) == 0;
+}
+
+static int cyttsp4_device_match(struct device *dev, struct device_driver *drv)
+{
+       struct cyttsp4_device *cyttsp4_dev = verify_device_type(dev);
+       struct cyttsp4_core *cyttsp4_core;
+       int match;
+
+       if (cyttsp4_dev) {
+               match = cyttsp4_match_device(cyttsp4_dev, drv->name);
+               goto exit;
+       }
+       cyttsp4_core = verify_core_type(dev);
+       if (cyttsp4_core) {
+               match = cyttsp4_match_core_device(cyttsp4_core, drv->name);
+               goto exit;
+       }
+       match = 0;
+exit:
+       dev_dbg(dev, "%s: %s matching '%s' driver\n", __func__,
+                       match ? "is" : "isn't", drv->name);
+       return match;
+}
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
+                            char *buf)
+{
+       struct cyttsp4_device *cyttsp4_dev = verify_device_type(dev);
+       struct cyttsp4_core *cyttsp4_core;
+
+       char const *name;
+       int len;
+
+       if (cyttsp4_dev) {
+               name = cyttsp4_dev->name;
+               goto exit;
+       }
+       cyttsp4_core = verify_core_type(dev);
+       if (cyttsp4_core) {
+               name = cyttsp4_core->id;
+               goto exit;
+       }
+       name = "none";
+exit:
+       len = snprintf(buf, PAGE_SIZE, "ttsp4:%s\n", name);
+       return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+}
+
+static struct device_attribute cyttsp4_dev_attrs[] = {
+       __ATTR_RO(modalias),
+       __ATTR_NULL,
+};
+
+#ifdef CONFIG_SUSPEND
+static int cyttsp4_pm_suspend(struct device *dev)
+{
+       struct device_driver *drv = dev->driver;
+
+       dev_dbg(dev, "%s\n", __func__);
+       if (drv && drv->pm && drv->pm->suspend)
+               return drv->pm->suspend(dev);
+       return 0;
+}
+
+static int cyttsp4_pm_resume(struct device *dev)
+{
+       struct device_driver *drv = dev->driver;
+
+       dev_dbg(dev, "%s\n", __func__);
+       if (drv && drv->pm && drv->pm->resume)
+               return drv->pm->suspend(dev);
+       return 0;
+}
+#else /* !CONFIG_SUSPEND */
+#define cyttsp4_pm_suspend             NULL
+#define cyttsp4_pm_resume              NULL
+#endif /* !CONFIG_SUSPEND */
+
+#ifdef CONFIG_PM_RUNTIME
+#define cyttsp4_pm_rt_suspend          pm_generic_runtime_suspend
+#define cyytsp4_pm_rt_resume           pm_generic_runtime_resume
+#define cyytsp4_pm_rt_idle             pm_generic_runtime_idle
+#else /* !CONFIG_PM_RUNTIME */
+#define cyttsp4_pm_rt_suspend          NULL
+#define cyytsp4_pm_rt_resume           NULL
+#define cyytsp4_pm_rt_idle             NULL
+#endif /* !CONFIG_PM_RUNTIME */
+
+static const struct dev_pm_ops cyttsp4_dev_pm_ops = {
+       .suspend = cyttsp4_pm_suspend,
+       .resume = cyttsp4_pm_resume,
+       .runtime_suspend = cyttsp4_pm_rt_suspend,
+       .runtime_resume = cyytsp4_pm_rt_resume,
+       .runtime_idle = cyytsp4_pm_rt_idle,
+};
+
+struct bus_type cyttsp4_bus_type = {
+       .name           = "ttsp4",
+       .dev_attrs      = cyttsp4_dev_attrs,
+       .match          = cyttsp4_device_match,
+       .uevent         = NULL,
+       .pm             = &cyttsp4_dev_pm_ops,
+};
+EXPORT_SYMBOL_GPL(cyttsp4_bus_type);
+
+static int cyttsp4_drv_remove(struct device *_dev)
+{
+       struct cyttsp4_driver *drv = to_cyttsp4_driver(_dev->driver);
+       struct cyttsp4_device *dev = to_cyttsp4_device(_dev);
+       return drv->remove(dev);
+}
+
+static int cyttsp4_core_drv_remove(struct device *_dev)
+{
+       struct cyttsp4_core_driver *drv = to_cyttsp4_core_driver(_dev->driver);
+       struct cyttsp4_core *dev = to_cyttsp4_core(_dev);
+       return drv->remove(dev);
+}
+
+static int cyttsp4_drv_probe(struct device *_dev)
+{
+       int rc = -ENODEV;
+       struct cyttsp4_driver *drv = to_cyttsp4_driver(_dev->driver);
+       struct cyttsp4_device *dev = to_cyttsp4_device(_dev);
+
+       rc = drv->probe(dev);
+       dev_dbg(_dev, "%s: for %s = %d\n", __func__, dev->name, rc);
+       return rc;
+}
+
+static int cyttsp4_core_drv_probe(struct device *_dev)
+{
+       int rc = -ENODEV;
+       struct cyttsp4_core_driver *drv = to_cyttsp4_core_driver(_dev->driver);
+       struct cyttsp4_core *dev = to_cyttsp4_core(_dev);
+
+       rc = drv->probe(dev);
+       dev_dbg(_dev, "%s: for %s = %d\n", __func__, dev->name, rc);
+       if (!rc)
+               rescan_devices(dev);
+       return rc;
+}
+
+int cyttsp4_register_driver(struct cyttsp4_driver *drv)
+{
+       int ret = 0;
+
+#ifdef CONFIG_MODULES
+       struct cyttsp4_device *d;
+
+       /*
+        * We need to ensure that the driver of this device's
+        * core device should exist (dependency)
+        * To do so, we traverse through the device, its core
+        * device and the driver of its core device, which requires
+        * the device itself should be registered with the system
+        */
+       mutex_lock(&core_lock);
+       list_for_each_entry(d, &cyttsp4_dev_list, node) {
+               if (!cyttsp4_match_device(d, drv->driver.name))
+                       continue;
+               if (d->core) {
+                       if (d->core->dev.driver)
+                               ret = ref_module(drv->driver.owner,
+                                       d->core->dev.driver->owner);
+                       else
+                               /* Core device exists but not core driver */
+                               ret = -ENODEV;
+               }
+               break;
+       }
+       mutex_unlock(&core_lock);
+
+       if (ret) {
+               if (ret == -ENODEV)
+                       pr_err("%s: Core driver module does not exist\n",
+                               __func__);
+               else
+                       pr_err("%s: Error getting ref to core driver module\n",
+                               __func__);
+               goto fail;
+       }
+#endif
+
+       drv->driver.bus = &cyttsp4_bus_type;
+       if (drv->probe)
+               drv->driver.probe = cyttsp4_drv_probe;
+       if (drv->remove)
+               drv->driver.remove = cyttsp4_drv_remove;
+       ret = driver_register(&drv->driver);
+fail:
+       pr_debug("%s: '%s' returned %d\n", __func__, drv->driver.name, ret);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(cyttsp4_register_driver);
+
+int cyttsp4_register_core_driver(struct cyttsp4_core_driver *drv)
+{
+       int ret = 0;
+
+#ifdef CONFIG_MODULES
+       struct cyttsp4_core *d;
+
+       /*
+        * We need to ensure that the driver of this core device's
+        * adapter should exist (dependency)
+        * To do so, we traverse through the core device, its adapter
+        * and the driver of its adapter, which requires the core
+        * device itself should be registered with the system
+        */
+       mutex_lock(&core_lock);
+       list_for_each_entry(d, &core_dev_list, node) {
+               if (!cyttsp4_match_core_device(d, drv->driver.name))
+                       continue;
+               if (d->adap) {
+                       if (d->adap->dev && d->adap->dev->driver) {
+                               ret = ref_module(drv->driver.owner,
+                                       d->adap->dev->driver->owner);
+                       } else {
+                               /* Core dev exist but not adap device
+                                * Do not let until adap module inserted */
+                               ret = -ENODEV;
+                       }
+               }
+               break;
+       }
+       mutex_unlock(&core_lock);
+
+       if (ret) {
+               if (ret == -ENODEV)
+                       pr_err("%s: Adapter driver module does not exist\n",
+                               __func__);
+               else
+                       pr_err("%s: Error getting ref to adapter driver module\n",
+                               __func__);
+               goto fail;
+       }
+#endif
+
+       drv->driver.bus = &cyttsp4_bus_type;
+       if (drv->probe)
+               drv->driver.probe = cyttsp4_core_drv_probe;
+       if (drv->remove)
+               drv->driver.remove = cyttsp4_core_drv_remove;
+       ret = driver_register(&drv->driver);
+fail:
+       pr_debug("%s: '%s' returned %d\n", __func__, drv->driver.name, ret);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(cyttsp4_register_core_driver);
+
+void cyttsp4_unregister_driver(struct cyttsp4_driver *drv)
+{
+       driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(cyttsp4_unregister_driver);
+
+void cyttsp4_unregister_core_driver(struct cyttsp4_core_driver *drv)
+{
+       driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(cyttsp4_unregister_core_driver);
+
+int __init cyttsp4_bus_init(void)
+{
+       int error;
+       error =  bus_register(&cyttsp4_bus_type);
+       if (error)
+               pr_err("%s: error %d\n", __func__, error);
+       else
+               pr_debug("%s: ok\n", __func__);
+       return error;
+}
+
+static void __exit cyttsp4_bus_exit(void)
+{
+       pr_debug("%s: ok\n", __func__);
+}
+
+subsys_initcall(cyttsp4_bus_init);
+module_exit(cyttsp4_bus_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Aleksej Makarov <aleksej.makarov@sonyericsson.com>");
diff --git a/include/linux/cyttsp4_bus.h b/include/linux/cyttsp4_bus.h
new file mode 100644
index 0000000..b1f64ef
--- /dev/null
+++ b/include/linux/cyttsp4_bus.h
@@ -0,0 +1,271 @@ 
+/*
+ * cyttsp4_bus.h
+ * Cypress TrueTouch(TM) Standard Product V4 Bus Driver.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ * Copyright (C) 2011 Sony Ericsson Mobile Communications AB.
+ *
+ * Author: Aleksej Makarov <aleksej.makarov@sonyericsson.com>
+ * Modified by: Cypress Semiconductor to add device functions
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#ifndef _LINUX_CYTTSP4_BUS_H
+#define _LINUX_CYTTSP4_BUS_H
+
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/limits.h>
+
+
+extern struct bus_type cyttsp4_bus_type;
+
+struct cyttsp4_driver;
+struct cyttsp4_device;
+struct cyttsp4_adapter;
+
+enum cyttsp4_atten_type {
+       CY_ATTEN_IRQ,
+       CY_ATTEN_STARTUP,
+       CY_ATTEN_EXCLUSIVE,
+       CY_ATTEN_NUM_ATTEN,
+};
+
+typedef int (*cyttsp4_atten_func) (struct cyttsp4_device *);
+
+struct cyttsp4_ops {
+       int (*write)(struct cyttsp4_adapter *dev, u8 addr,
+               const void *buf, int size);
+       int (*read)(struct cyttsp4_adapter *dev, u8 addr, void *buf, int size);
+};
+
+struct cyttsp4_adapter {
+       struct list_head node;
+       char id[NAME_MAX];
+       struct device *dev;
+       int (*write)(struct cyttsp4_adapter *dev, u8 addr,
+               const void *buf, int size);
+       int (*read)(struct cyttsp4_adapter *dev, u8 addr, void *buf, int size);
+};
+#define to_cyttsp4_adapter(d) container_of(d, struct cyttsp4_adapter, dev)
+
+struct cyttsp4_core {
+       struct list_head node;
+       char const *name;
+       char const *id;
+       char const *adap_id;
+       struct device dev;
+       struct cyttsp4_adapter *adap;
+};
+#define to_cyttsp4_core(d) container_of(d, struct cyttsp4_core, dev)
+
+struct cyttsp4_device {
+       struct list_head node;
+       char const *name;
+       char const *core_id;
+       struct device dev;
+       struct cyttsp4_core *core;
+};
+#define to_cyttsp4_device(d) container_of(d, struct cyttsp4_device, dev)
+
+struct cyttsp4_core_driver {
+       struct device_driver driver;
+       int (*probe)(struct cyttsp4_core *core);
+       int (*remove)(struct cyttsp4_core *core);
+       int (*subscribe_attention)(struct cyttsp4_device *ttsp,
+                               enum cyttsp4_atten_type type,
+                               cyttsp4_atten_func func,
+                               int flags);
+       int (*unsubscribe_attention)(struct cyttsp4_device *ttsp,
+                               enum cyttsp4_atten_type type,
+                               cyttsp4_atten_func func,
+                               int flags);
+       int (*request_exclusive)(struct cyttsp4_device *ttsp, int t);
+       int (*release_exclusive)(struct cyttsp4_device *ttsp);
+       int (*request_reset)(struct cyttsp4_device *ttsp);
+       int (*request_restart)(struct cyttsp4_device *ttsp);
+       int (*request_set_mode)(struct cyttsp4_device *ttsp, int mode);
+       struct cyttsp4_sysinfo *(*request_sysinfo)(struct cyttsp4_device *ttsp);
+       int (*request_handshake)(struct cyttsp4_device *ttsp, u8 mode);
+       int (*request_exec_cmd)(struct cyttsp4_device *ttsp, u8 mode,
+                       u8 *cmd_buf, size_t cmd_size, u8 *return_buf,
+                       size_t return_buf_size, int timeout);
+       int (*request_stop_wd)(struct cyttsp4_device *ttsp);
+       int (*request_toggle_lowpower)(struct cyttsp4_device *ttsp, u8 mode);
+       int (*write)(struct cyttsp4_device *ttsp, int mode,
+               u8 addr, const void *buf, int size);
+       int (*read)(struct cyttsp4_device *ttsp, int mode,
+               u8 addr, void *buf, int size);
+};
+#define to_cyttsp4_core_driver(d) \
+       container_of(d, struct cyttsp4_core_driver, driver)
+
+struct cyttsp4_driver {
+       struct device_driver driver;
+       int (*probe)(struct cyttsp4_device *dev);
+       int (*remove)(struct cyttsp4_device *fev);
+};
+#define to_cyttsp4_driver(d) container_of(d, struct cyttsp4_driver, driver)
+
+extern int cyttsp4_register_driver(struct cyttsp4_driver *drv);
+extern void cyttsp4_unregister_driver(struct cyttsp4_driver *drv);
+
+extern int cyttsp4_register_core_driver(struct cyttsp4_core_driver *drv);
+extern void cyttsp4_unregister_core_driver(struct cyttsp4_core_driver *drv);
+
+extern int cyttsp4_register_device(struct cyttsp4_device *pdev);
+extern void cyttsp4_unregister_device(struct cyttsp4_device *pdev);
+
+extern int cyttsp4_register_core_device(struct cyttsp4_core *pdev);
+
+extern int cyttsp4_add_adapter(char const *id, struct cyttsp4_ops const *ops,
+               struct device *parent);
+
+extern int cyttsp4_del_adapter(char const *id);
+
+static inline int cyttsp4_read(struct cyttsp4_device *ttsp, int mode, u8 addr,
+               void *buf, int size)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->read(ttsp, mode, addr, buf, size);
+}
+
+static inline int cyttsp4_write(struct cyttsp4_device *ttsp, int mode, u8 addr,
+               const void *buf, int size)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->write(ttsp, mode, addr, buf, size);
+}
+
+static inline int cyttsp4_adap_read(struct cyttsp4_adapter *adap, u8 addr,
+               void *buf, int size)
+{
+       return adap->read(adap, addr, buf, size);
+}
+
+static inline int cyttsp4_adap_write(struct cyttsp4_adapter *adap, u8 addr,
+               const void *buf, int size)
+{
+       return adap->write(adap, addr, buf, size);
+}
+
+static inline int cyttsp4_subscribe_attention(struct cyttsp4_device *ttsp,
+               enum cyttsp4_atten_type type, cyttsp4_atten_func func,
+               int flags)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->subscribe_attention(ttsp, type, func, flags);
+}
+
+static inline int cyttsp4_unsubscribe_attention(struct cyttsp4_device *ttsp,
+               enum cyttsp4_atten_type type, cyttsp4_atten_func func,
+               int flags)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->unsubscribe_attention(ttsp, type, func, flags);
+}
+
+static inline int cyttsp4_request_exclusive(struct cyttsp4_device *ttsp, int t)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->request_exclusive(ttsp, t);
+}
+
+static inline int cyttsp4_release_exclusive(struct cyttsp4_device *ttsp)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->release_exclusive(ttsp);
+}
+
+static inline int cyttsp4_request_reset(struct cyttsp4_device *ttsp)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->request_reset(ttsp);
+}
+
+static inline int cyttsp4_request_restart(struct cyttsp4_device *ttsp)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->request_restart(ttsp);
+}
+
+static inline int cyttsp4_request_set_mode(struct cyttsp4_device *ttsp,
+               int mode)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->request_set_mode(ttsp, mode);
+}
+
+static inline struct cyttsp4_sysinfo *cyttsp4_request_sysinfo(
+               struct cyttsp4_device *ttsp)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->request_sysinfo(ttsp);
+}
+
+static inline int cyttsp4_request_handshake(struct cyttsp4_device *ttsp,
+               u8 mode)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->request_handshake(ttsp, mode);
+}
+
+static inline int cyttsp4_request_exec_cmd(struct cyttsp4_device *ttsp,
+               u8 mode, u8 *cmd_buf, size_t cmd_size, u8 *return_buf,
+               size_t return_buf_size, int timeout)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->request_exec_cmd(ttsp, mode, cmd_buf, cmd_size, return_buf,
+                       return_buf_size, timeout);
+}
+
+static inline int cyttsp4_request_stop_wd(struct cyttsp4_device *ttsp)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->request_stop_wd(ttsp);
+}
+
+static inline int cyttsp4_request_toggle_lowpower(struct cyttsp4_device *ttsp,
+               u8 mode)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->request_toggle_lowpower(ttsp, mode);
+}
+
+#endif /* _LINUX_CYTTSP4_BUS_H */