All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/7] add FlexRay support
@ 2013-08-13  9:08 Benedikt Spranger
  2013-08-13  9:08 ` Benedikt Spranger
                   ` (7 more replies)
  0 siblings, 8 replies; 28+ messages in thread
From: Benedikt Spranger @ 2013-08-13  9:08 UTC (permalink / raw)
  To: netdev; +Cc: Alexander Frank, Sebastian Andrzej Siewior, Benedikt Spranger

This series adds FlexRay support to the Linux Kernel. FlexRay is designed and
used as a successor of Controller Area Network (CAN) in the automotive fields.
FlexRay is specified in ISO 10681. A quick overview is available here:

http://en.wikipedia.org/wiki/FlexRay

The driver implementation adds support for the Eberspaecher Electronics
FlexCard. The FlexCard is a PCI Mezzanine Card with piggybacks for automotive
buses like CAN, LIN and FlexRay. An implementation based on multifunctional
devices (MFD) was choosen. The independant IP cores (FlexRay, CAN, etc) can be
supported by separate drivers. In addition to the automotive busses the
FlexCard provides some generic functions coverd by the clocksoure and the UIO
drivers. The AF_FLEXRAY protocol can be used to send and receive flexray frames
from the userland.

Benedikt Spranger (7):
  uio: add module owner to prevent inappropriate module unloading
  uio: Allow to create custom UIO attributes
  mfd: core: copy DMA mask and params from parent
  mfd: add MFD based flexcard driver
  clocksource: Add flexcard support
  net: add the AF_FLEXRAY protocol
  net: add a flexray driver

 Documentation/networking/00-INDEX             |    2 +
 Documentation/networking/flexray.txt          |   24 +
 drivers/clocksource/Kconfig                   |   13 +
 drivers/clocksource/Makefile                  |    2 +
 drivers/clocksource/flexcard_clk.c            |  297 +++
 drivers/clocksource/flexcard_evt.c            |  161 ++
 drivers/mfd/Kconfig                           |   42 +
 drivers/mfd/Makefile                          |    2 +
 drivers/mfd/flexcard-core.c                   | 1059 +++++++++++
 drivers/mfd/flexcard-dma.c                    |  561 ++++++
 drivers/mfd/flexcard-irq.c                    |  333 ++++
 drivers/mfd/flexcard.h                        |    8 +
 drivers/mfd/mfd-core.c                        |    2 +
 drivers/net/Makefile                          |    1 +
 drivers/net/flexray/Kconfig                   |   41 +
 drivers/net/flexray/Makefile                  |   12 +
 drivers/net/flexray/dev.c                     |  700 +++++++
 drivers/net/flexray/flexcard_fr.c             | 2480 +++++++++++++++++++++++++
 drivers/net/flexray/vflexray.c                |   99 +
 drivers/uio/uio.c                             |  115 +-
 include/linux/eray.h                          |  650 +++++++
 include/linux/flexcard.h                      |   95 +
 include/linux/flexray.h                       |  168 ++
 include/linux/flexray/core.h                  |   45 +
 include/linux/flexray/dev.h                   |   64 +
 include/linux/socket.h                        |    4 +-
 include/linux/uio_driver.h                    |   38 +-
 include/uapi/linux/Kbuild                     |    2 +
 include/uapi/linux/eray.h                     |   34 +
 include/uapi/linux/flexcard.h                 |  429 +++++
 include/uapi/linux/flexray/Kbuild             |    4 +
 include/uapi/linux/flexray/flexcard_netlink.h |   53 +
 include/uapi/linux/flexray/netlink.h          |  203 ++
 include/uapi/linux/flexray/raw.h              |   16 +
 include/uapi/linux/if_arp.h                   |    1 +
 include/uapi/linux/if_ether.h                 |    1 +
 net/Kconfig                                   |    1 +
 net/Makefile                                  |    1 +
 net/core/sock.c                               |    9 +-
 net/flexray/Kconfig                           |   30 +
 net/flexray/Makefile                          |    9 +
 net/flexray/af_flexray.c                      |  548 ++++++
 net/flexray/af_flexray.h                      |   59 +
 net/flexray/proc.c                            |  196 ++
 net/flexray/raw.c                             |  446 +++++
 45 files changed, 9047 insertions(+), 13 deletions(-)
 create mode 100644 Documentation/networking/flexray.txt
 create mode 100644 drivers/clocksource/flexcard_clk.c
 create mode 100644 drivers/clocksource/flexcard_evt.c
 create mode 100644 drivers/mfd/flexcard-core.c
 create mode 100644 drivers/mfd/flexcard-dma.c
 create mode 100644 drivers/mfd/flexcard-irq.c
 create mode 100644 drivers/mfd/flexcard.h
 create mode 100644 drivers/net/flexray/Kconfig
 create mode 100644 drivers/net/flexray/Makefile
 create mode 100644 drivers/net/flexray/dev.c
 create mode 100644 drivers/net/flexray/flexcard_fr.c
 create mode 100644 drivers/net/flexray/vflexray.c
 create mode 100644 include/linux/eray.h
 create mode 100644 include/linux/flexcard.h
 create mode 100644 include/linux/flexray.h
 create mode 100644 include/linux/flexray/core.h
 create mode 100644 include/linux/flexray/dev.h
 create mode 100644 include/uapi/linux/eray.h
 create mode 100644 include/uapi/linux/flexcard.h
 create mode 100644 include/uapi/linux/flexray/Kbuild
 create mode 100644 include/uapi/linux/flexray/flexcard_netlink.h
 create mode 100644 include/uapi/linux/flexray/netlink.h
 create mode 100644 include/uapi/linux/flexray/raw.h
 create mode 100644 net/flexray/Kconfig
 create mode 100644 net/flexray/Makefile
 create mode 100644 net/flexray/af_flexray.c
 create mode 100644 net/flexray/af_flexray.h
 create mode 100644 net/flexray/proc.c
 create mode 100644 net/flexray/raw.c

-- 
1.8.4.rc2

^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH 0/7] add FlexRay support
  2013-08-13  9:08 [PATCH 0/7] add FlexRay support Benedikt Spranger
@ 2013-08-13  9:08 ` Benedikt Spranger
  2013-08-13  9:08 ` [PATCH 1/7] uio: add module owner to prevent inappropriate module unloading Benedikt Spranger
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 28+ messages in thread
From: Benedikt Spranger @ 2013-08-13  9:08 UTC (permalink / raw)
  To: netdev; +Cc: Alexander Frank, Sebastian Andrzej Siewior, Benedikt Spranger

This series adds FlexRay support to the Linux Kernel. FlexRay is designed and
used as a successor of Controller Area Network (CAN) in the automotive fields.
FlexRay is specified in ISO 10681. A quick overview is available here:

http://en.wikipedia.org/wiki/FlexRay

The driver implementation adds support for the Eberspaecher Electronics
FlexCard. The FlexCard is a PCI Mezzanine Card with piggybacks for automotive
buses like CAN, LIN and FlexRay. A implementation based on multifunctional
devices (MFD) was choosen. The independant IP cores (FlexRay, CAN, etc) can be
supported by separate drivers. In addition to the automotive busses the
FlexCard provides some generic functions coverd by the clocksoure and the UIO
drivers. The AF_FLEXRAY protocol can be used to send and receive flexray frames
from the userland.

Benedikt Spranger (7):
  uio: add module owner to prevent inappropriate module unloading
  uio: Allow to create custom UIO attributes
  mfd: core: copy DMA mask and params from parent
  mfd: add MFD based flexcard driver
  clocksource: Add flexcard support
  net: add the AF_FLEXRAY protocol
  net: add a flexray driver

 Documentation/networking/00-INDEX             |    2 +
 Documentation/networking/flexray.txt          |   24 +
 drivers/clocksource/Kconfig                   |   13 +
 drivers/clocksource/Makefile                  |    2 +
 drivers/clocksource/flexcard_clk.c            |  297 +++
 drivers/clocksource/flexcard_evt.c            |  161 ++
 drivers/mfd/Kconfig                           |   42 +
 drivers/mfd/Makefile                          |    2 +
 drivers/mfd/flexcard-core.c                   | 1059 +++++++++++
 drivers/mfd/flexcard-dma.c                    |  561 ++++++
 drivers/mfd/flexcard-irq.c                    |  333 ++++
 drivers/mfd/flexcard.h                        |    8 +
 drivers/mfd/mfd-core.c                        |    2 +
 drivers/net/Makefile                          |    1 +
 drivers/net/flexray/Kconfig                   |   41 +
 drivers/net/flexray/Makefile                  |   12 +
 drivers/net/flexray/dev.c                     |  700 +++++++
 drivers/net/flexray/flexcard_fr.c             | 2480 +++++++++++++++++++++++++
 drivers/net/flexray/vflexray.c                |   99 +
 drivers/uio/uio.c                             |  115 +-
 include/linux/eray.h                          |  650 +++++++
 include/linux/flexcard.h                      |   95 +
 include/linux/flexray.h                       |  168 ++
 include/linux/flexray/core.h                  |   45 +
 include/linux/flexray/dev.h                   |   64 +
 include/linux/socket.h                        |    4 +-
 include/linux/uio_driver.h                    |   38 +-
 include/uapi/linux/Kbuild                     |    2 +
 include/uapi/linux/eray.h                     |   34 +
 include/uapi/linux/flexcard.h                 |  429 +++++
 include/uapi/linux/flexray/Kbuild             |    4 +
 include/uapi/linux/flexray/flexcard_netlink.h |   53 +
 include/uapi/linux/flexray/netlink.h          |  203 ++
 include/uapi/linux/flexray/raw.h              |   16 +
 include/uapi/linux/if_arp.h                   |    1 +
 include/uapi/linux/if_ether.h                 |    1 +
 net/Kconfig                                   |    1 +
 net/Makefile                                  |    1 +
 net/core/sock.c                               |    9 +-
 net/flexray/Kconfig                           |   30 +
 net/flexray/Makefile                          |    9 +
 net/flexray/af_flexray.c                      |  548 ++++++
 net/flexray/af_flexray.h                      |   59 +
 net/flexray/proc.c                            |  196 ++
 net/flexray/raw.c                             |  446 +++++
 45 files changed, 9047 insertions(+), 13 deletions(-)
 create mode 100644 Documentation/networking/flexray.txt
 create mode 100644 drivers/clocksource/flexcard_clk.c
 create mode 100644 drivers/clocksource/flexcard_evt.c
 create mode 100644 drivers/mfd/flexcard-core.c
 create mode 100644 drivers/mfd/flexcard-dma.c
 create mode 100644 drivers/mfd/flexcard-irq.c
 create mode 100644 drivers/mfd/flexcard.h
 create mode 100644 drivers/net/flexray/Kconfig
 create mode 100644 drivers/net/flexray/Makefile
 create mode 100644 drivers/net/flexray/dev.c
 create mode 100644 drivers/net/flexray/flexcard_fr.c
 create mode 100644 drivers/net/flexray/vflexray.c
 create mode 100644 include/linux/eray.h
 create mode 100644 include/linux/flexcard.h
 create mode 100644 include/linux/flexray.h
 create mode 100644 include/linux/flexray/core.h
 create mode 100644 include/linux/flexray/dev.h
 create mode 100644 include/uapi/linux/eray.h
 create mode 100644 include/uapi/linux/flexcard.h
 create mode 100644 include/uapi/linux/flexray/Kbuild
 create mode 100644 include/uapi/linux/flexray/flexcard_netlink.h
 create mode 100644 include/uapi/linux/flexray/netlink.h
 create mode 100644 include/uapi/linux/flexray/raw.h
 create mode 100644 net/flexray/Kconfig
 create mode 100644 net/flexray/Makefile
 create mode 100644 net/flexray/af_flexray.c
 create mode 100644 net/flexray/af_flexray.h
 create mode 100644 net/flexray/proc.c
 create mode 100644 net/flexray/raw.c

-- 
1.8.4.rc2

^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH 1/7] uio: add module owner to prevent inappropriate module unloading
  2013-08-13  9:08 [PATCH 0/7] add FlexRay support Benedikt Spranger
  2013-08-13  9:08 ` Benedikt Spranger
@ 2013-08-13  9:08 ` Benedikt Spranger
  2013-08-13 17:48   ` Greg Kroah-Hartman
  2013-08-13  9:08 ` [PATCH 2/7] uio: Allow to create custom UIO attributes Benedikt Spranger
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 28+ messages in thread
From: Benedikt Spranger @ 2013-08-13  9:08 UTC (permalink / raw)
  To: netdev
  Cc: Alexander Frank, Sebastian Andrzej Siewior, Benedikt Spranger,
	Hans J. Koch, Greg Kroah-Hartman, Holger Dengler

If an UIO device is created by another driver (MFD for instance) it has to be
ensured that the MFD driver isn't removed while the UIO is still in use.
This patch adds a reference to the parent driver which does module refcounting
in order to avoid an too early removal.

Cc: "Hans J. Koch" <hjk@hansjkoch.de>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Benedikt Spranger <b.spranger@linutronix.de>
Signed-off-by: Holger Dengler <dengler@linutronix.de>
---
 drivers/uio/uio.c          | 9 +++++++++
 include/linux/uio_driver.h | 2 ++
 2 files changed, 11 insertions(+)

diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c
index 3b96f18..9a95220 100644
--- a/drivers/uio/uio.c
+++ b/drivers/uio/uio.c
@@ -444,6 +444,11 @@ static int uio_open(struct inode *inode, struct file *filep)
 		goto out;
 	}
 
+	if (!try_module_get(idev->info->owner)) {
+		ret = -ENODEV;
+		goto err_get_module;
+	}
+
 	listener = kmalloc(sizeof(*listener), GFP_KERNEL);
 	if (!listener) {
 		ret = -ENOMEM;
@@ -465,6 +470,9 @@ err_infoopen:
 	kfree(listener);
 
 err_alloc_listener:
+	module_put(idev->info->owner);
+
+err_get_module:
 	module_put(idev->owner);
 
 out:
@@ -488,6 +496,7 @@ static int uio_release(struct inode *inode, struct file *filep)
 	if (idev->info->release)
 		ret = idev->info->release(idev->info, inode);
 
+	module_put(idev->info->owner);
 	module_put(idev->owner);
 	kfree(listener);
 	return ret;
diff --git a/include/linux/uio_driver.h b/include/linux/uio_driver.h
index 1ad4724..99f1696 100644
--- a/include/linux/uio_driver.h
+++ b/include/linux/uio_driver.h
@@ -80,6 +80,7 @@ struct uio_device;
  * @open:		open operation for this uio device
  * @release:		release operation for this uio device
  * @irqcontrol:		disable/enable irqs when 0/1 is written to /dev/uioX
+ * @owner:		module which created the uio device incase
  */
 struct uio_info {
 	struct uio_device	*uio_dev;
@@ -95,6 +96,7 @@ struct uio_info {
 	int (*open)(struct uio_info *info, struct inode *inode);
 	int (*release)(struct uio_info *info, struct inode *inode);
 	int (*irqcontrol)(struct uio_info *info, s32 irq_on);
+	struct module			*owner;
 };
 
 extern int __must_check
-- 
1.8.4.rc2

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 2/7] uio: Allow to create custom UIO attributes
  2013-08-13  9:08 [PATCH 0/7] add FlexRay support Benedikt Spranger
  2013-08-13  9:08 ` Benedikt Spranger
  2013-08-13  9:08 ` [PATCH 1/7] uio: add module owner to prevent inappropriate module unloading Benedikt Spranger
@ 2013-08-13  9:08 ` Benedikt Spranger
  2013-08-13 17:54   ` Greg Kroah-Hartman
  2013-08-13  9:08 ` [PATCH 3/7] mfd: core: copy DMA mask and params from parent Benedikt Spranger
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 28+ messages in thread
From: Benedikt Spranger @ 2013-08-13  9:08 UTC (permalink / raw)
  To: netdev
  Cc: Alexander Frank, Sebastian Andrzej Siewior, Benedikt Spranger,
	Hans J. Koch, Greg Kroah-Hartman, Holger Dengler

This patch the struct uio_attribute which represents a custom UIO
attribute. The non-standard attributes are stored in a "attr" directory.
This will be used by the flexcard driver which creates a UIO device that
is using the "uio_pdrv" and requires one additional value to be read /
written by the user.

Cc: "Hans J. Koch" <hjk@hansjkoch.de>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Benedikt Spranger <b.spranger@linutronix.de>
Signed-off-by: Holger Dengler <dengler@linutronix.de>
---
 drivers/uio/uio.c          | 106 ++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/uio_driver.h |  36 +++++++++++----
 2 files changed, 133 insertions(+), 9 deletions(-)

diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c
index 9a95220..d66784a 100644
--- a/drivers/uio/uio.c
+++ b/drivers/uio/uio.c
@@ -39,6 +39,7 @@ struct uio_device {
 	struct uio_info		*info;
 	struct kobject		*map_dir;
 	struct kobject		*portio_dir;
+	struct kobject		attr_dir;
 };
 
 static int uio_major;
@@ -252,6 +253,49 @@ static struct device_attribute uio_class_attributes[] = {
 	{}
 };
 
+static ssize_t uio_type_show(struct kobject *kobj, struct attribute *attr,
+			     char *buf)
+{
+	struct uio_device *idev;
+	struct uio_info *info;
+	struct uio_attribute *entry;
+
+	idev = container_of(kobj, struct uio_device, attr_dir);
+	info = idev->info;
+	entry = container_of(attr, struct uio_attribute, attr);
+
+	if (!entry->show)
+		return -EIO;
+
+	return entry->show(info, buf);
+}
+
+static ssize_t uio_type_store(struct kobject *kobj, struct attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct uio_device *idev;
+	struct uio_info *info;
+	struct uio_attribute *entry;
+
+	idev = container_of(kobj, struct uio_device, attr_dir);
+	info = idev->info;
+	entry = container_of(attr, struct uio_attribute, attr);
+
+	if (!entry->store)
+		return -EIO;
+
+	return entry->store(info, buf, count);
+}
+
+static const struct sysfs_ops uio_sysfs_ops = {
+	.show = uio_type_show,
+	.store = uio_type_store,
+};
+
+static struct kobj_type uio_attr_type = {
+	.sysfs_ops	= &uio_sysfs_ops,
+};
+
 /* UIO class infrastructure */
 static struct class uio_class = {
 	.name = "uio",
@@ -323,8 +367,16 @@ static int uio_dev_add_attributes(struct uio_device *idev)
 			goto err_portio;
 	}
 
+	ret = kobject_init_and_add(&idev->attr_dir, &uio_attr_type,
+				   &idev->dev->kobj, "attr");
+	if (ret)
+		goto err_attr;
+
 	return 0;
 
+err_attr:
+	kobject_put(&idev->attr_dir);
+
 err_portio:
 	for (pi--; pi >= 0; pi--) {
 		port = &idev->info->port[pi];
@@ -364,6 +416,7 @@ static void uio_dev_del_attributes(struct uio_device *idev)
 		kobject_put(&port->portio->kobj);
 	}
 	kobject_put(idev->portio_dir);
+	kobject_put(&idev->attr_dir);
 }
 
 static int uio_get_minor(struct uio_device *idev)
@@ -391,6 +444,50 @@ static void uio_free_minor(struct uio_device *idev)
 }
 
 /**
+ * uio_add_user_attributes - add an extra UIO attribute
+ * @info: UIO device capabilities
+ */
+static int uio_add_user_attributes(struct uio_info *info)
+{
+	struct uio_device *idev = info->uio_dev;
+	const struct uio_attribute **uio_attr;
+	int i;
+	int ret = 0;
+
+	uio_attr = info->attributes;
+	if (!uio_attr)
+		return 0;
+
+	for (i = 0; uio_attr[i]; i++) {
+
+		ret = sysfs_create_file(&idev->attr_dir, &uio_attr[i]->attr);
+		if (ret)
+			break;
+	}
+	if (ret) {
+		while (--i >= 0)
+			sysfs_remove_file(&idev->attr_dir, &uio_attr[i]->attr);
+	}
+	return ret;
+}
+
+/**
+ * uio_del_user_attributes - delete an extra UIO attribute
+ * @info: UIO device capabilities
+ */
+static void uio_del_user_attributes(struct uio_info *info)
+{
+	struct uio_device *idev = info->uio_dev;
+	const struct uio_attribute **uio_attr;
+	int i;
+
+	uio_attr = info->attributes;
+
+	for (i = 0; uio_attr[i]; i++)
+		sysfs_remove_file(&idev->attr_dir, &uio_attr[i]->attr);
+}
+
+/**
  * uio_event_notify - trigger an interrupt event
  * @info: UIO device capabilities
  */
@@ -846,8 +943,14 @@ int __uio_register_device(struct module *owner,
 			goto err_request_irq;
 	}
 
-	return 0;
+	ret = uio_add_user_attributes(info);
+	if (ret)
+		goto err_free_irq;
 
+	return 0;
+err_free_irq:
+	if (info->irq && (info->irq != UIO_IRQ_CUSTOM))
+		free_irq(info->irq, idev);
 err_request_irq:
 	uio_dev_del_attributes(idev);
 err_uio_dev_add_attributes:
@@ -881,6 +984,7 @@ void uio_unregister_device(struct uio_info *info)
 		free_irq(info->irq, idev);
 
 	uio_dev_del_attributes(idev);
+	uio_del_user_attributes(idev->info);
 
 	device_destroy(&uio_class, MKDEV(uio_major, idev->minor));
 	kfree(idev);
diff --git a/include/linux/uio_driver.h b/include/linux/uio_driver.h
index 99f1696..2e7543e 100644
--- a/include/linux/uio_driver.h
+++ b/include/linux/uio_driver.h
@@ -64,6 +64,7 @@ struct uio_port {
 #define MAX_UIO_PORT_REGIONS	5
 
 struct uio_device;
+struct uio_attribute;
 
 /**
  * struct uio_info - UIO device capabilities
@@ -72,6 +73,7 @@ struct uio_device;
  * @version:		device driver version
  * @mem:		list of mappable memory regions, size==0 for end of list
  * @port:		list of port regions, size==0 for end of list
+ * @attributes:		list of additional attributes, NULL for end of list
  * @irq:		interrupt number or UIO_IRQ_CUSTOM
  * @irq_flags:		flags for request_irq()
  * @priv:		optional private data
@@ -83,14 +85,15 @@ struct uio_device;
  * @owner:		module which created the uio device incase
  */
 struct uio_info {
-	struct uio_device	*uio_dev;
-	const char		*name;
-	const char		*version;
-	struct uio_mem		mem[MAX_UIO_MAPS];
-	struct uio_port		port[MAX_UIO_PORT_REGIONS];
-	long			irq;
-	unsigned long		irq_flags;
-	void			*priv;
+	struct uio_device		*uio_dev;
+	const char			*name;
+	const char			*version;
+	struct uio_mem			mem[MAX_UIO_MAPS];
+	struct uio_port			port[MAX_UIO_PORT_REGIONS];
+	const struct uio_attribute	**attributes;
+	long				irq;
+	unsigned long			irq_flags;
+	void				*priv;
 	irqreturn_t (*handler)(int irq, struct uio_info *dev_info);
 	int (*mmap)(struct uio_info *info, struct vm_area_struct *vma);
 	int (*open)(struct uio_info *info, struct inode *inode);
@@ -99,6 +102,23 @@ struct uio_info {
 	struct module			*owner;
 };
 
+/**
+ * struct uio_attribute - UIO attribute
+ * @attr:		the attribute
+ * @show:		attribute show function
+ * @store:		attribute store function
+ */
+struct uio_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct uio_info *info, char *buf);
+	ssize_t (*store)(struct uio_info *info, const char *buf, size_t count);
+};
+
+#define UIO_ATTR(_name, _mode, _show, _store)   \
+	struct uio_attribute uio_attr_##_name = \
+		__ATTR(_name, _mode, _show, _store)
+
+
 extern int __must_check
 	__uio_register_device(struct module *owner,
 			      struct device *parent,
-- 
1.8.4.rc2

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 3/7] mfd: core: copy DMA mask and params from parent
  2013-08-13  9:08 [PATCH 0/7] add FlexRay support Benedikt Spranger
                   ` (2 preceding siblings ...)
  2013-08-13  9:08 ` [PATCH 2/7] uio: Allow to create custom UIO attributes Benedikt Spranger
@ 2013-08-13  9:08 ` Benedikt Spranger
  2013-08-13 10:03   ` Lee Jones
  2013-08-13  9:08 ` [PATCH 4/7] mfd: add MFD based flexcard driver Benedikt Spranger
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 28+ messages in thread
From: Benedikt Spranger @ 2013-08-13  9:08 UTC (permalink / raw)
  To: netdev
  Cc: Alexander Frank, Sebastian Andrzej Siewior, Benedikt Spranger,
	Samuel Ortiz, Lee Jones, Holger Dengler

The child device intends to perform DMA operations then it needs a dma
mask and params set. This patches copies them from the parent device.

Cc: Samuel Ortiz <sameo@linux.intel.com>
Cc: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Benedikt Spranger <b.spranger@linutronix.de>
Signed-off-by: Holger Dengler <dengler@linutronix.de>
---
 drivers/mfd/mfd-core.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index 7604f4e..f421586 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -96,6 +96,8 @@ static int mfd_add_device(struct device *parent, int id,
 
 	pdev->dev.parent = parent;
 	pdev->dev.type = &mfd_dev_type;
+	pdev->dev.dma_mask = parent->dma_mask;
+	pdev->dev.dma_parms = parent->dma_parms;
 
 	if (parent->of_node && cell->of_compatible) {
 		for_each_child_of_node(parent->of_node, np) {
-- 
1.8.4.rc2

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 4/7] mfd: add MFD based flexcard driver
  2013-08-13  9:08 [PATCH 0/7] add FlexRay support Benedikt Spranger
                   ` (3 preceding siblings ...)
  2013-08-13  9:08 ` [PATCH 3/7] mfd: core: copy DMA mask and params from parent Benedikt Spranger
@ 2013-08-13  9:08 ` Benedikt Spranger
  2013-08-13  9:55   ` Lee Jones
  2013-08-13  9:08 ` [PATCH 5/7] clocksource: Add flexcard support Benedikt Spranger
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 28+ messages in thread
From: Benedikt Spranger @ 2013-08-13  9:08 UTC (permalink / raw)
  To: netdev
  Cc: Alexander Frank, Sebastian Andrzej Siewior, Benedikt Spranger,
	Samuel Ortiz, Lee Jones, Holger Dengler

This patch provides support for Eberpächer Flexcard. The Flexcard comes
with two PCI bars, one for the glue layer and one for the "IPs". The
second bar may have multiple other devices like flexray or d_can
devices. The driver utilites the MFD framework and creates various
subdevices. Not only for the additional "IPs" but also for the clock
source and clockevent provided by the card.

Cc: Samuel Ortiz <sameo@linux.intel.com>
Cc: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Benedikt Spranger <b.spranger@linutronix.de>
Signed-off-by: Holger Dengler <dengler@linutronix.de>
---
 drivers/mfd/Kconfig           |   40 ++
 drivers/mfd/Makefile          |    2 +
 drivers/mfd/flexcard-core.c   | 1059 +++++++++++++++++++++++++++++++++++++++++
 drivers/mfd/flexcard-dma.c    |  561 ++++++++++++++++++++++
 drivers/mfd/flexcard-irq.c    |  333 +++++++++++++
 drivers/mfd/flexcard.h        |    8 +
 include/linux/eray.h          |  650 +++++++++++++++++++++++++
 include/linux/flexcard.h      |   95 ++++
 include/uapi/linux/Kbuild     |    2 +
 include/uapi/linux/eray.h     |   34 ++
 include/uapi/linux/flexcard.h |  429 +++++++++++++++++
 11 files changed, 3213 insertions(+)
 create mode 100644 drivers/mfd/flexcard-core.c
 create mode 100644 drivers/mfd/flexcard-dma.c
 create mode 100644 drivers/mfd/flexcard-irq.c
 create mode 100644 drivers/mfd/flexcard.h
 create mode 100644 include/linux/eray.h
 create mode 100644 include/linux/flexcard.h
 create mode 100644 include/uapi/linux/eray.h
 create mode 100644 include/uapi/linux/flexcard.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index aecd6dd..f847d52 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -223,6 +223,46 @@ config MFD_INTEL_MSIC
 	  Passage) chip. This chip embeds audio, battery, GPIO, etc.
 	  devices used in Intel Medfield platforms.
 
+config MFD_EBEL_FLEXCARD
+	tristate "Support for the Eberspächer Electronic Flexcard"
+	select MFD_CORE
+	select UIO
+	select UIO_PDRV
+	select UIO_PDRV_GENIRQ
+	depends on PCI
+	help
+	  If you say yes here you get support for the Eberspächer Electronic
+	  Flexcard. This driver provides common support for accessing the
+	  device, additional drivers must be enabled in order to use the
+	  functionality of the device.
+
+config MFD_EBEL_FLEXCARD_DEBUG
+	bool "extended debug information"
+	depends on MFD_EBEL_FLEXCARD
+	help
+	  enable extended debug information
+
+config MFD_EBEL_FLEXCARD_DMA_POLL
+	bool "poll DMA interrupts"
+	depends on MFD_EBEL_FLEXCARD
+	default y
+	help
+	  poll Flexcard DMA interrupts
+
+config MFD_EBEL_FLEXCARD_DMA_POLL_INTERVAL
+	int "Flexcard DMA poll intervall (us)"
+	depends on MFD_EBEL_FLEXCARD_DMA_POLL
+	default 500
+
+config MFD_EBEL_FLEXCARD_PROTPARAM
+	bool "Eray-specific Flexray Protocol Parameter ranges"
+	depends on MFD_EBEL_FLEXCARD
+	default y
+	help
+	  Say Y if you want to use Eray-specific Flexray Protocol Parameter
+	  ranges instead of the Protocol Parameter ranges that are specified in
+	  Flexray Protocol definition v2 and/or v3.
+
 config MFD_JANZ_CMODIO
 	tristate "Janz CMOD-IO PCI MODULbus Carrier Board"
 	select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 3c90051..1e22660 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -150,6 +150,8 @@ obj-$(CONFIG_TPS65911_COMPARATOR)	+= tps65911-comparator.o
 obj-$(CONFIG_MFD_TPS65090)	+= tps65090.o
 obj-$(CONFIG_MFD_AAT2870_CORE)	+= aat2870-core.o
 obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
+flexcard-objs			:= flexcard-core.o flexcard-irq.o
+obj-$(CONFIG_MFD_EBEL_FLEXCARD)		+= flexcard.o flexcard-dma.o
 obj-$(CONFIG_MFD_PALMAS)	+= palmas.o
 obj-$(CONFIG_MFD_VIPERBOARD)    += viperboard.o
 obj-$(CONFIG_MFD_RC5T583)	+= rc5t583.o rc5t583-irq.o
diff --git a/drivers/mfd/flexcard-core.c b/drivers/mfd/flexcard-core.c
new file mode 100644
index 0000000..c5924b5
--- /dev/null
+++ b/drivers/mfd/flexcard-core.c
@@ -0,0 +1,1059 @@
+/*
+ * Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved.
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/stringify.h>
+#include <linux/uio_driver.h>
+
+#include <linux/flexcard.h>
+
+#include "flexcard.h"
+
+#define FC_UIO_VERSION  "1.0"
+#define FC_MAX_CARDS	32
+#define FC_MAX_DCAN	32
+#define FC_MAX_ERAY	32
+
+static DECLARE_BITMAP(fc_cards, FC_MAX_CARDS);
+
+static const char fc_dcan_string[] = "d_can";
+static const char fc_eray_string[] = "flexcard-eray";
+
+/* Clock Function */
+static struct resource fc_evt_res[] = {
+	{
+		.name   = "flexcard-clkevt",
+		.start  = 0x154,
+		.end    = 0x157,
+		.flags  = IORESOURCE_MEM,
+	},
+};
+
+static struct mfd_cell fc_evt_dev[] = {
+	{
+		.id = 0,
+		.name = "flexcard-clkevt",
+		.num_resources = ARRAY_SIZE(fc_evt_res),
+		.resources = fc_evt_res,
+	},
+};
+
+static struct resource fc_clk_res[] = {
+	{
+		.name   = "flexcard-clksrc",
+		.start  = 0x700,
+		.end    = 0x70c,
+		.flags  = IORESOURCE_MEM,
+	},
+};
+
+static struct mfd_cell fc_clk_dev[] = {
+	{
+		.id = 0,
+		.name = "flexcard-clksrc",
+		.num_resources = ARRAY_SIZE(fc_clk_res),
+		.resources = fc_clk_res,
+	},
+};
+
+static struct resource fc_dma_res[] = {
+	{
+		.name   = "flexcard-dma",
+		.start  = 0x0,
+		.end    = 0x600,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
+		.name   = "flexcard-dma",
+		.start  = FC_IRQ_DMA_CBL_OFF,
+		.end    = FC_IRQ_DMA_CBL_OFF,
+		.flags  = IORESOURCE_IRQ,
+	},
+	{
+		.name   = "flexcard-dma",
+		.start  = FC_IRQ_DMA_CO_OFF,
+		.end    = FC_IRQ_DMA_CO_OFF,
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell fc_dma_dev[] = {
+	{
+		.id = 0,
+		.name = "flexcard-dma",
+		.num_resources = ARRAY_SIZE(fc_dma_res),
+		.resources = fc_dma_res,
+	},
+};
+
+static ssize_t show_userid(struct uio_info *uio, char *buf)
+{
+	void __iomem *internal_addr;
+	u32 reg;
+
+	internal_addr = ioremap(uio->mem[0].addr, uio->mem[0].size);
+	if (!internal_addr)
+		return -EIO;
+	reg = readl(internal_addr + FC_FC_UID);
+	iounmap(internal_addr);
+
+	return sprintf(buf, "0x%x\n", reg);
+}
+
+static ssize_t store_userid(struct uio_info *uio, const char *buf, size_t count)
+{
+	void __iomem *internal_addr;
+	u32 reg;
+	int ret;
+
+	ret = kstrtos32(buf, 0, &reg);
+	if (ret)
+		return ret;
+
+	internal_addr = ioremap(uio->mem[0].addr, uio->mem[0].size);
+	if (!internal_addr)
+		return -EIO;
+
+	writel(reg, internal_addr + FC_FC_UID);
+	iounmap(internal_addr);
+
+	return count;
+}
+static UIO_ATTR(userid, 0644, show_userid, store_userid);
+
+static const struct uio_attribute *fc_uio_attr[] = {
+	&uio_attr_userid,
+	NULL,
+};
+
+static struct uio_info fc_uio_pdata = {
+	.name = "fc_fc_uio",
+	.version = FC_UIO_VERSION,
+	.irq = 0,
+	.attributes = fc_uio_attr,
+	.owner = THIS_MODULE,
+};
+
+static struct resource fc_uio_res[] = {
+	/* FlexCard PCI bar 0 */
+	{
+		.name   = "flexcard-uio",
+		.start  = 0x000,
+		.end    = 0xfff,
+		.flags  = IORESOURCE_MEM,
+	},
+	/* FlexCard PCI bar 1 */
+	{
+		.name   = "flexcard-uio",
+		.start  = 0x000,
+		.end    = 0xffff,
+		.flags  = IORESOURCE_MEM,
+	},
+};
+
+static struct mfd_cell fc_uio_dev[] = {
+	{
+		.id = 0,
+		.name = "uio_pdrv",
+		.platform_data = &fc_uio_pdata,
+		.pdata_size = sizeof(fc_uio_pdata),
+		.num_resources = ARRAY_SIZE(fc_uio_res),
+		.resources = fc_uio_res,
+	},
+};
+
+#define irq_res(irq_name) \
+	static struct resource fc_irq_res_##irq_name = {	\
+		.name = __stringify(fc_irq_##irq_name##_off),	\
+		.start  = FC_IRQ_##irq_name##_OFF,		\
+		.end  = FC_IRQ_##irq_name##_OFF,		\
+		.flags  = IORESOURCE_IRQ			\
+	};							\
+								\
+	static struct uio_info fc_irq_pdata_##irq_name = {	\
+		.name   = __stringify(irq_name),		\
+		.version = "0",					\
+	}
+
+irq_res(CC3CYCS);
+irq_res(CC4CYCS);
+irq_res(WAKE4A);
+irq_res(WAKE4B);
+irq_res(WAKE3A);
+irq_res(WAKE3B);
+irq_res(WAKE2A);
+irq_res(WAKE2B);
+irq_res(WAKE1A);
+irq_res(WAKE1B);
+irq_res(CC1CYCS);
+irq_res(CC2CYCS);
+irq_res(CC1T0);
+irq_res(CC2T0);
+irq_res(CC3T0);
+irq_res(CC4T0);
+
+#define fc_irq_cell(irq_name, irq_id)			\
+{							\
+	.id = irq_id,					\
+	.name = "uio_pdrv_genirq",			\
+	.platform_data = &fc_irq_pdata_##irq_name,	\
+	.pdata_size = sizeof(fc_irq_pdata_##irq_name),	\
+	.num_resources = 1,				\
+	.resources = &fc_irq_res_##irq_name		\
+}
+
+static struct mfd_cell fc_irq_dev[] = {
+	fc_irq_cell(CC3CYCS, 0),
+	fc_irq_cell(CC4CYCS, 1),
+	fc_irq_cell(WAKE4A, 2),
+	fc_irq_cell(WAKE4B, 3),
+	fc_irq_cell(WAKE3A, 4),
+	fc_irq_cell(WAKE3B, 5),
+	fc_irq_cell(WAKE2A, 6),
+	fc_irq_cell(WAKE2B, 7),
+	fc_irq_cell(WAKE1A, 8),
+	fc_irq_cell(WAKE1B, 9),
+	fc_irq_cell(CC1CYCS, 10),
+	fc_irq_cell(CC2CYCS, 11),
+	fc_irq_cell(CC1T0, 12),
+	fc_irq_cell(CC2T0, 13),
+	fc_irq_cell(CC3T0, 14),
+	fc_irq_cell(CC4T0, 15),
+};
+
+/* sysfs info */
+static ssize_t show_fw_ver(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 fw_ver;
+
+	fw_ver = readl(priv->conf + FC_FC_FW_VER);
+
+	return sprintf(buf, "%d.%d.%d\n",
+		       fw_ver >> 16 & 0xff, fw_ver >> 8 & 0xff, fw_ver & 0xff);
+}
+static DEVICE_ATTR(fw_ver, S_IRUGO, show_fw_ver, NULL);
+
+static ssize_t show_hw_ver(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 hw_ver;
+
+	hw_ver = readl(priv->conf + FC_FC_HW_VER);
+
+	return sprintf(buf, "%d.%d.%d\n",
+		       hw_ver >> 16 & 0xff, hw_ver >> 8 & 0xff, hw_ver & 0xff);
+}
+static DEVICE_ATTR(hw_ver, S_IRUGO, show_hw_ver, NULL);
+
+static ssize_t show_fw_cur(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 fw_cur;
+
+	fw_cur = readl(priv->conf + FC_ACTIMG);
+
+	return sprintf(buf, "%d\n", fw_cur & 0xff);
+
+}
+static DEVICE_ATTR(fw_cur, S_IRUGO, show_fw_cur, NULL);
+
+static ssize_t show_apl_mode(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 apl_mode[8];
+	int i;
+
+	writel(0x1, priv->conf + FC_UPDIMGINF);
+	mdelay(1);
+	for (i = 0; i < 8; i++)
+		apl_mode[i] = readl(priv->conf + 0xAE0 + i*4);
+
+	return sprintf(buf, "0x%08x 0x%08x 0x%08x 0x%08x\n"
+		       "0x%08x 0x%08x 0x%08x 0x%08x\n",
+		       apl_mode[0], apl_mode[1], apl_mode[2], apl_mode[3],
+		       apl_mode[4], apl_mode[5], apl_mode[6], apl_mode[7]);
+}
+static DEVICE_ATTR(apl_mode, S_IRUGO, show_apl_mode, NULL);
+
+#ifdef CONFIG_MFD_EBEL_FLEXCARD_DEBUG
+static ssize_t show_dma_stat(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 status;
+
+	status = readl(priv->conf + FC_DMA_STAT);
+
+	return sprintf(buf, "%08x\n", status);
+}
+static DEVICE_ATTR(dma_stat, S_IRUGO, show_dma_stat, NULL);
+
+static ssize_t show_dma_ctrl(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 status;
+
+	status = readl(priv->conf + FC_DMA_CTRL);
+
+	return sprintf(buf, "%08x\n", status);
+}
+static DEVICE_ATTR(dma_ctrl, S_IRUGO, show_dma_ctrl, NULL);
+
+static ssize_t show_dma_irer(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 status;
+
+	status = readl(priv->conf + FC_DMA_IRER);
+
+	return sprintf(buf, "%08x\n", status);
+}
+static DEVICE_ATTR(dma_irer, S_IRUGO, show_dma_irer, NULL);
+
+static ssize_t show_dma_irsr(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 status;
+
+	status = readl(priv->conf + FC_DMA_IRSR);
+
+	return sprintf(buf, "%08x\n", status);
+}
+static DEVICE_ATTR(dma_irsr, S_IRUGO, show_dma_irsr, NULL);
+
+static ssize_t show_dma_cblc(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 status;
+
+	status = readl(priv->conf + FC_DMA_CBCR);
+
+	return sprintf(buf, "%08x\n", status);
+}
+static ssize_t store_dma_cblc(struct device *dev, struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 cblc;
+	int ret;
+
+	ret = kstrtouint(buf, 0, &cblc);
+	if (ret)
+		return ret;
+	if (cblc >= 2 * 1024 * 1024)
+		return -ERANGE;
+
+	writel(cblc, priv->conf + FC_DMA_CBCR);
+
+	return count;
+}
+static DEVICE_ATTR(dma_cblc, 0644, show_dma_cblc, store_dma_cblc);
+
+static ssize_t show_dma_cbl(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 status;
+
+	status = readl(priv->conf + FC_DMA_CBLR);
+
+	return sprintf(buf, "%08x\n", status);
+}
+static DEVICE_ATTR(dma_cbl, S_IRUGO, show_dma_cbl, NULL);
+
+static ssize_t show_dma_rptr(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 status;
+
+	status = readl(priv->conf + FC_DMA_RPTR);
+
+	return sprintf(buf, "%08x\n", status);
+}
+static DEVICE_ATTR(dma_rptr, S_IRUGO, show_dma_rptr, NULL);
+
+static ssize_t show_dma_wptr(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 status;
+
+	status = readl(priv->conf + FC_DMA_RPTR);
+
+	return sprintf(buf, "%08x\n", status);
+}
+static DEVICE_ATTR(dma_wptr, S_IRUGO, show_dma_wptr, NULL);
+
+static ssize_t show_fc_rocr(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 rocr;
+
+	rocr = readl(priv->conf + FC_FC_ROCR);
+
+	return sprintf(buf, "%08x\n", rocr);
+}
+static ssize_t store_fc_rocr(struct device *dev, struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 rocr;
+	int ret;
+
+	ret = kstrtouint(buf, 0, &rocr);
+	if (ret)
+		return ret;
+	if (rocr & ~3)
+		return -ERANGE;
+	rocr <<= 30;
+	writel(rocr, priv->conf + FC_FC_ROCR);
+
+	return count;
+}
+static DEVICE_ATTR(fc_rocr, 0644, show_fc_rocr, store_fc_rocr);
+
+static ssize_t show_eray_mtccv(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 mtccv;
+
+	mtccv = readl(priv->mmio + 0x114);
+
+	return sprintf(buf, "%08x\n", mtccv);
+}
+static DEVICE_ATTR(eray_mtccv, S_IRUGO, show_eray_mtccv, NULL);
+#endif
+
+static ssize_t show_ier(struct device *dev, struct device_attribute *attr,
+			char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 status;
+
+	status = readl(priv->conf + FC_IER);
+
+	return sprintf(buf, "%08x\n", status);
+}
+static DEVICE_ATTR(ier, S_IRUGO, show_ier, NULL);
+
+static ssize_t show_isr(struct device *dev, struct device_attribute *attr,
+			char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 status;
+
+	status = readl(priv->conf + FC_ISR);
+
+	return sprintf(buf, "%08x\n", status);
+}
+static DEVICE_ATTR(isr, S_IRUGO, show_isr, NULL);
+
+static ssize_t show_amreg(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 status;
+
+	status = readl(priv->conf + FC_AMREG);
+
+	return sprintf(buf, "%08x\n", status);
+}
+static ssize_t store_amreg(struct device *dev, struct device_attribute *attr,
+			   const char *buf, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 amreg;
+	int ret;
+
+	ret = kstrtouint(buf, 0, &amreg);
+	if (ret)
+		return ret;
+	if (amreg & ~0x3)
+		return -ERANGE;
+	writel(amreg, priv->conf + FC_AMREG);
+
+	return count;
+}
+static DEVICE_ATTR(amreg, 0644, show_amreg, store_amreg);
+
+#ifdef CONFIG_MFD_EBEL_FLEXCARD_DMA_POLL
+
+static ssize_t show_poll_interval(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d us\n", fc_poll_interval / 1000);
+}
+static ssize_t store_poll_interval(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	unsigned int tmp;
+	int ret;
+
+	ret = kstrtouint(buf, 0, &tmp);
+	if (ret)
+		return ret;
+
+	if (tmp > USEC_PER_SEC)
+		return -ERANGE;
+
+	tmp *= NSEC_PER_USEC;
+	fc_poll_interval = tmp;
+	return count;
+}
+static DEVICE_ATTR(poll_interval, 0644, show_poll_interval,
+		store_poll_interval);
+
+static ssize_t show_dma_on_irq(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+
+	return sprintf(buf, "%d\n", priv->dma_on_irq);
+}
+static ssize_t store_dma_on_irq(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	int ret;
+	unsigned int tmp;
+
+	ret = kstrtouint(buf, 0, &tmp);
+	if (ret)
+		return ret;
+	if (tmp)
+		priv->dma_on_irq = 1;
+	else
+		priv->dma_on_irq = 0;
+
+	return count;
+}
+static DEVICE_ATTR(dma_on_irq, 0644, show_dma_on_irq, store_dma_on_irq);
+
+#endif
+
+static void reset_flexcard(struct fc_priv *priv)
+{
+	writel(FC_RST_FC, priv->conf + FC_FC_ROCR);
+	writel(0, priv->conf + FC_TIRQIR);
+	writel(0, priv->conf + FC_FC_NFCTRL);
+	writel(UINT_MAX, priv->conf + FC_FC_RESET);
+	writel(0, priv->conf + FC_PL_TERM);
+}
+
+static int fc_init_uio(struct pci_dev *pdev)
+{
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	int ret;
+
+	fc_uio_res[0].start = pci_resource_start(pdev, 0);
+	fc_uio_res[0].end = fc_uio_res[0].start + 0xfff;
+	fc_uio_res[0].parent = &pdev->resource[0];
+	fc_uio_res[1].start = pci_resource_start(pdev, 1);
+	fc_uio_res[1].end = fc_uio_res[1].start + 0xffff;
+	fc_uio_res[1].parent = &pdev->resource[1];
+
+	ret = mfd_add_devices(&pdev->dev,
+			priv->card_nr * ARRAY_SIZE(fc_uio_dev), fc_uio_dev,
+			ARRAY_SIZE(fc_uio_dev), NULL, priv->irq_start, NULL);
+	if (ret)
+		mfd_remove_devices(&pdev->dev);
+
+	return ret;
+}
+
+static int fc_init_dma(struct pci_dev *pdev)
+{
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	int ret;
+
+	ret = mfd_add_devices(&pdev->dev,
+			priv->card_nr * ARRAY_SIZE(fc_dma_dev), fc_dma_dev,
+			ARRAY_SIZE(fc_dma_dev), &pdev->resource[0],
+			priv->irq_start, NULL);
+	if (ret)
+		mfd_remove_devices(&pdev->dev);
+
+	return ret;
+}
+
+static int fc_init_event(struct pci_dev *pdev)
+{
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	int ret;
+
+	ret = mfd_add_devices(&pdev->dev,
+			priv->card_nr * ARRAY_SIZE(fc_irq_dev), fc_irq_dev,
+			ARRAY_SIZE(fc_irq_dev), NULL, priv->irq_start, NULL);
+	if (ret)
+		mfd_remove_devices(&pdev->dev);
+
+	return ret;
+}
+
+static int fc_init_clk(struct pci_dev *pdev)
+{
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	int ret;
+
+	ret = mfd_add_devices(&pdev->dev,
+			priv->card_nr * ARRAY_SIZE(fc_evt_dev), fc_evt_dev,
+			ARRAY_SIZE(fc_evt_dev), &pdev->resource[0],
+			priv->irq_start, NULL);
+	if (ret)
+		goto out;
+
+	ret = mfd_add_devices(&pdev->dev,
+			priv->card_nr * ARRAY_SIZE(fc_clk_dev), fc_clk_dev,
+			ARRAY_SIZE(fc_clk_dev), &pdev->resource[0],
+			priv->irq_start, NULL);
+	if (ret)
+		goto out;
+
+	return 0;
+out:
+	mfd_remove_devices(&pdev->dev);
+	return ret;
+}
+
+static int fc_init_bus(struct pci_dev *pdev)
+{
+	struct resource res[3];
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	u32 avail, sup;
+	int i, ret = -ENOMEM;
+
+	avail = readl(priv->conf + FC_FC_LIC0) & FC_LIC0_CAN_MASK;
+	avail >>= FC_LIC0_CAN_SHIFT;
+	sup = readl(priv->conf + FC_FC_SLIC0) & FC_SLIC0_CAN_MASK;
+	sup >>= FC_SLIC0_CAN_SHIFT;
+
+	priv->nrdcan = min(avail, sup);
+	priv->dcan = kzalloc(priv->nrdcan*sizeof(struct mfd_cell), GFP_KERNEL);
+	if (!priv->dcan)
+		goto out;
+
+	avail = readl(priv->conf + FC_FC_LIC0) & FC_LIC0_FLEXRAY_MASK;
+	sup = readl(priv->conf + FC_FC_SLIC0) & FC_SLIC0_FLEXRAY_MASK;
+
+	priv->nreray = min(avail, sup);
+	priv->eray = kzalloc(priv->nreray*sizeof(struct mfd_cell), GFP_KERNEL);
+	if (!priv->eray)
+		goto out_dcan;
+
+	for (i = 0; i < priv->nrdcan; i++) {
+		priv->dcan[i].name = fc_dcan_string;
+		priv->dcan[i].id = PLATFORM_DEVID_AUTO;
+		priv->dcan[i].resources = res;
+		priv->dcan[i].num_resources = ARRAY_SIZE(res);
+		res[0].name = "flexcard-dcan";
+		res[0].start = pci_resource_start(pdev, 1) +
+			priv->nreray * 0x4000 + i * 0x2000;
+		res[0].end = res[0].start + 0x1fff;
+		res[0].parent = &pdev->resource[1];
+		res[0].flags = IORESOURCE_MEM;
+		res[1].name = "flexcard-dcan";
+		res[1].start = pci_resource_start(pdev, 0);
+		res[1].end = res[1].start + 0xfff;
+		res[1].parent = &pdev->resource[0];
+		res[1].flags = IORESOURCE_MEM;
+		res[2].name = "flexcard-dcan";
+		res[2].start = FC_IRQ_DMA_RX_OFF(priv->nreray + i);
+		res[2].end = FC_IRQ_DMA_RX_OFF(priv->nreray + i);
+		res[2].flags = IORESOURCE_IRQ;
+
+		ret = mfd_add_devices(&pdev->dev, priv->card_nr * FC_MAX_DCAN,
+				&priv->dcan[i], 1, NULL, priv->irq_start,
+				NULL);
+		if (ret)
+			goto out_eray;
+	}
+
+	for (i = 0; i < priv->nreray; i++) {
+		priv->eray[i].name = fc_eray_string;
+		priv->eray[i].id = PLATFORM_DEVID_AUTO;
+		priv->eray[i].resources = res;
+		priv->eray[i].num_resources = ARRAY_SIZE(res);
+		res[0].name = "flexcard-eray";
+		res[0].start = pci_resource_start(pdev, 0);
+		res[0].end = res[0].start + 0xfff;
+		res[0].parent = &pdev->resource[0];
+		res[0].flags = IORESOURCE_MEM;
+		res[1].name = "flexcard-eray";
+		res[1].start = pci_resource_start(pdev, 1) + i * 0x4000;
+		res[1].end = res[1].start + 0x3fff;
+		res[1].parent = &pdev->resource[1];
+		res[1].flags = IORESOURCE_MEM;
+		res[2].name = "flexcard-eray";
+		res[2].start = FC_IRQ_DMA_RX_OFF(i);
+		res[2].end = FC_IRQ_DMA_RX_OFF(i);
+		res[2].flags = IORESOURCE_IRQ;
+
+		ret = mfd_add_devices(&pdev->dev, priv->card_nr * FC_MAX_ERAY,
+				&priv->eray[i], 1, NULL, priv->irq_start,
+				NULL);
+		if (ret)
+			goto out_eray;
+	}
+	return 0;
+
+out_eray:
+	kfree(priv->eray);
+out_dcan:
+	kfree(priv->dcan);
+out:
+	mfd_remove_devices(&pdev->dev);
+	return ret;
+}
+
+static int fc_register_attr(struct pci_dev *pdev)
+{
+	int ret;
+#ifdef CONFIG_MFD_EBEL_FLEXCARD_DMA_POLL
+	ret = device_create_file(&pdev->dev, &dev_attr_poll_interval);
+	if (ret)
+		goto out;
+
+	ret = device_create_file(&pdev->dev, &dev_attr_dma_on_irq);
+	if (ret)
+		goto out_poll;
+#endif
+	ret = device_create_file(&pdev->dev, &dev_attr_isr);
+	if (ret)
+		goto out_dma_on_irq;
+	ret = device_create_file(&pdev->dev, &dev_attr_ier);
+	if (ret)
+		goto out_isr;
+	ret = device_create_file(&pdev->dev, &dev_attr_amreg);
+	if (ret)
+		goto out_ier;
+	ret = device_create_file(&pdev->dev, &dev_attr_fw_ver);
+	if (ret)
+		goto out_amreg;
+	ret = device_create_file(&pdev->dev, &dev_attr_hw_ver);
+	if (ret)
+		goto out_fw_attr;
+	ret = device_create_file(&pdev->dev, &dev_attr_fw_cur);
+	if (ret)
+		goto out_hw_attr;
+	ret = device_create_file(&pdev->dev, &dev_attr_apl_mode);
+	if (ret)
+		goto out_cur_attr;
+
+#ifdef CONFIG_MFD_EBEL_FLEXCARD_DEBUG
+	ret = device_create_file(&pdev->dev, &dev_attr_dma_irsr);
+	if (ret)
+		goto out_apl_attr;
+	ret = device_create_file(&pdev->dev, &dev_attr_dma_irer);
+	if (ret)
+		goto out_dma_irsr;
+	ret = device_create_file(&pdev->dev, &dev_attr_dma_ctrl);
+	if (ret)
+		goto out_dma_irer;
+	ret = device_create_file(&pdev->dev, &dev_attr_dma_stat);
+	if (ret)
+		goto out_dma_ctrl;
+	ret = device_create_file(&pdev->dev, &dev_attr_dma_cbl);
+	if (ret)
+		goto out_dma_stat;
+	ret = device_create_file(&pdev->dev, &dev_attr_dma_cblc);
+	if (ret)
+		goto out_dma_cbl;
+	ret = device_create_file(&pdev->dev, &dev_attr_dma_rptr);
+	if (ret)
+		goto out_dma_cblc;
+	ret = device_create_file(&pdev->dev, &dev_attr_dma_wptr);
+	if (ret)
+		goto out_dma_rptr;
+	ret = device_create_file(&pdev->dev, &dev_attr_fc_rocr);
+	if (ret)
+		goto out_dma_wptr;
+	ret = device_create_file(&pdev->dev, &dev_attr_eray_mtccv);
+	if (ret)
+		goto out_fc_rocr;
+#endif
+
+	return 0;
+
+#ifdef CONFIG_MFD_EBEL_FLEXCARD_DEBUG
+out_fc_rocr:
+	device_remove_file(&pdev->dev, &dev_attr_fc_rocr);
+out_dma_wptr:
+	device_remove_file(&pdev->dev, &dev_attr_dma_wptr);
+out_dma_rptr:
+	device_remove_file(&pdev->dev, &dev_attr_dma_rptr);
+out_dma_cblc:
+	device_remove_file(&pdev->dev, &dev_attr_dma_cblc);
+out_dma_cbl:
+	device_remove_file(&pdev->dev, &dev_attr_dma_cbl);
+out_dma_stat:
+	device_remove_file(&pdev->dev, &dev_attr_dma_stat);
+out_dma_ctrl:
+	device_remove_file(&pdev->dev, &dev_attr_dma_ctrl);
+out_dma_irer:
+	device_remove_file(&pdev->dev, &dev_attr_dma_irer);
+out_dma_irsr:
+	device_remove_file(&pdev->dev, &dev_attr_dma_irsr);
+out_apl_attr:
+	device_remove_file(&pdev->dev, &dev_attr_apl_mode);
+#endif
+out_cur_attr:
+	device_remove_file(&pdev->dev, &dev_attr_fw_cur);
+out_hw_attr:
+	device_remove_file(&pdev->dev, &dev_attr_hw_ver);
+out_fw_attr:
+	device_remove_file(&pdev->dev, &dev_attr_fw_ver);
+out_amreg:
+	device_remove_file(&pdev->dev, &dev_attr_amreg);
+out_ier:
+	device_remove_file(&pdev->dev, &dev_attr_ier);
+out_isr:
+	device_remove_file(&pdev->dev, &dev_attr_isr);
+out_dma_on_irq:
+#ifdef CONFIG_MFD_EBEL_FLEXCARD_DMA_POLL
+	device_remove_file(&pdev->dev, &dev_attr_dma_on_irq);
+out_poll:
+	device_remove_file(&pdev->dev, &dev_attr_poll_interval);
+out:
+#endif
+	return ret;
+}
+
+static void fc_unregister_attr(struct pci_dev *pdev)
+{
+#ifdef CONFIG_MFD_EBEL_FLEXCARD_DEBUG
+	device_remove_file(&pdev->dev, &dev_attr_eray_mtccv);
+	device_remove_file(&pdev->dev, &dev_attr_fc_rocr);
+	device_remove_file(&pdev->dev, &dev_attr_dma_wptr);
+	device_remove_file(&pdev->dev, &dev_attr_dma_rptr);
+	device_remove_file(&pdev->dev, &dev_attr_dma_cblc);
+	device_remove_file(&pdev->dev, &dev_attr_dma_cbl);
+	device_remove_file(&pdev->dev, &dev_attr_dma_stat);
+	device_remove_file(&pdev->dev, &dev_attr_dma_ctrl);
+	device_remove_file(&pdev->dev, &dev_attr_dma_irer);
+	device_remove_file(&pdev->dev, &dev_attr_dma_irsr);
+#endif
+	device_remove_file(&pdev->dev, &dev_attr_apl_mode);
+	device_remove_file(&pdev->dev, &dev_attr_fw_cur);
+	device_remove_file(&pdev->dev, &dev_attr_hw_ver);
+	device_remove_file(&pdev->dev, &dev_attr_fw_ver);
+	device_remove_file(&pdev->dev, &dev_attr_amreg);
+	device_remove_file(&pdev->dev, &dev_attr_ier);
+	device_remove_file(&pdev->dev, &dev_attr_isr);
+#ifdef CONFIG_MFD_EBEL_FLEXCARD_DMA_POLL
+	device_remove_file(&pdev->dev, &dev_attr_poll_interval);
+	device_remove_file(&pdev->dev, &dev_attr_dma_on_irq);
+#endif
+}
+
+static int fc_pci_probe(struct pci_dev *pdev,
+			const struct pci_device_id *id)
+{
+	struct fc_priv *priv;
+	u32 fw_ver, hw_ver;
+	int ret = -ENOMEM;
+
+	if (bitmap_full(fc_cards, FC_MAX_CARDS)) {
+		dev_err(&pdev->dev, "could not handle more then %d cards\n",
+			FC_MAX_CARDS);
+		goto out;
+	}
+
+	priv = kzalloc(sizeof(struct fc_priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(&pdev->dev, "out of memory\n");
+		goto out;
+	}
+
+	priv->dev = pdev;
+	priv->card_nr = find_first_zero_bit(fc_cards, FC_MAX_CARDS);
+	set_bit(priv->card_nr, fc_cards);
+
+	pci_set_drvdata(pdev, priv);
+	pci_set_master(pdev);
+
+	if (pci_enable_device(pdev)) {
+		dev_err(&pdev->dev, "could not enable device\n");
+		goto out_free_priv;
+	}
+
+	if (pci_request_regions(pdev, "flexcard")) {
+		dev_err(&pdev->dev, "could not request mem regions\n");
+		goto out_disable;
+	}
+
+	priv->conf = ioremap(pci_resource_start(pdev, 0),
+			     pci_resource_len(pdev, 0));
+	if (!priv->conf) {
+		dev_err(&pdev->dev, "could not remap BAR0\n");
+		goto out_release;
+	}
+
+	priv->mmio = ioremap(pci_resource_start(pdev, 1),
+			     pci_resource_len(pdev, 1));
+	if (!priv->mmio) {
+		dev_err(&pdev->dev, "could not remap BAR1\n");
+		goto out_unmap;
+	}
+
+	ret = fc_irq_add(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "could not add IRQ controller\n");
+		goto out_unmap_mmio;
+	}
+
+	reset_flexcard(priv);
+
+	ret = fc_register_attr(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "could not register devive attributes\n");
+		goto out_irq;
+	}
+
+	ret = fc_init_dma(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "could not register DMA\n");
+		goto out_attr;
+	}
+
+	ret = fc_init_uio(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "could not register UIO\n");
+		goto out_attr;
+	}
+
+	ret = fc_init_event(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "could not register events\n");
+		goto out_attr;
+	}
+
+	ret = fc_init_clk(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "could not register clock\n");
+		goto out_attr;
+	}
+
+	ret = fc_init_bus(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "could not register CAN/FlexRay\n");
+		goto out_attr;
+	}
+
+	fw_ver = readl(priv->conf + FC_FC_FW_VER);
+	hw_ver = readl(priv->conf + FC_FC_HW_VER);
+	dev_info(&pdev->dev, "Flexcard HW ver. %d.%d.%d FW ver. %d.%d.%d\n",
+		 hw_ver >> 16 & 0xff, hw_ver >> 8 & 0xff, hw_ver & 0xff,
+		 fw_ver >> 16 & 0xff, fw_ver >> 8 & 0xff, fw_ver & 0xff);
+
+	return 0;
+
+out_attr:
+	fc_unregister_attr(pdev);
+out_irq:
+	fc_irq_remove(pdev);
+out_unmap_mmio:
+	iounmap(priv->mmio);
+out_unmap:
+	iounmap(priv->conf);
+out_release:
+	pci_release_regions(pdev);
+out_disable:
+	pci_disable_device(pdev);
+out_free_priv:
+	clear_bit(priv->card_nr, fc_cards);
+	kfree(priv);
+out:
+	return -ENODEV;
+}
+
+static void fc_pci_remove(struct pci_dev *pdev)
+{
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+
+	/*
+	 * Reset FlexCard to avoid serious problems due
+	 * to spurious packet generation.
+	 */
+	reset_flexcard(priv);
+	mfd_remove_devices(&pdev->dev);
+
+	fc_unregister_attr(pdev);
+	fc_irq_remove(pdev);
+
+	pci_clear_master(pdev);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+
+	iounmap(priv->mmio);
+	iounmap(priv->conf);
+
+	kfree(priv->dcan);
+	kfree(priv->eray);
+
+	clear_bit(priv->card_nr, fc_cards);
+
+	kfree(priv);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(fc_pci_ids) = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_EBEL, PCI_DEVICE_ID_FC_PMC2) },
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, fc_pci_ids);
+
+static struct pci_driver fc_pci_driver = {
+	.name = "flexcard",
+	.id_table = fc_pci_ids,
+	.probe = fc_pci_probe,
+	.remove = fc_pci_remove,
+};
+module_pci_driver(fc_pci_driver);
+
+MODULE_DESCRIPTION("Driver for the Eberspächer Electronics CAN/FlexRay cards");
+MODULE_AUTHOR("Benedikt Spranger <b.spranger@linutronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/flexcard-dma.c b/drivers/mfd/flexcard-dma.c
new file mode 100644
index 0000000..06cf919
--- /dev/null
+++ b/drivers/mfd/flexcard-dma.c
@@ -0,0 +1,561 @@
+/*
+ * Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved.
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/hrtimer.h>
+#include <linux/netdevice.h>
+#include <linux/uio_driver.h>
+
+#include <linux/flexcard.h>
+
+#undef DEBUG_DMA_BUFFER_OUTPUT
+
+#define FC_DEFAULT_CBLC		0x1000
+#define FC_PACKET_OFF(x)	(0xe0000000 + x * 0x200000)
+
+/* The first supported FW Version is 6.4.0 */
+#define MIN_FW_MAJOR	6
+#define MIN_FW_MINOR	4
+#define MIN_FW_UPDATE	0
+
+static LIST_HEAD(rx_cb_list);
+static DEFINE_SPINLOCK(rx_cb_lock);
+
+struct dma_flexcard {
+	int act;
+	int irq;
+	int irq_ovr;
+	u32 cnt_old;
+	u32 rptr;
+	u32 dmaptr;
+	void *buf;
+	dma_addr_t phys;
+	void __iomem *reg;
+	struct uio_info info;
+	int nr_eray;
+};
+
+struct fc_rx_cb {
+	struct list_head list;
+	int (*rx_cb) (void *priv, void *data, size_t len);
+	int cc;
+	void *priv;
+};
+
+
+
+int fc_register_rx_pkt(int cc, void *priv,
+		       int (*rx_cb)(void *priv, void *data, size_t len))
+{
+	unsigned long flags;
+	struct fc_rx_cb *cb, *next;
+
+	if (!rx_cb)
+		return -EINVAL;
+
+	cb = kmalloc(sizeof(*cb), GFP_ATOMIC);
+	if (!cb)
+		return -ENOMEM;
+
+	cb->cc = cc;
+	cb->priv = priv;
+	cb->rx_cb = rx_cb;
+
+	spin_lock_irqsave(&rx_cb_lock, flags);
+	list_for_each_entry(next, &rx_cb_list, list)
+		if (next->cc == cc)
+			goto out;
+
+	list_add_tail(&cb->list, &rx_cb_list);
+	spin_unlock_irqrestore(&rx_cb_lock, flags);
+
+	return 0;
+out:
+	spin_unlock_irqrestore(&rx_cb_lock, flags);
+	kfree(cb);
+
+	return -EBUSY;
+}
+EXPORT_SYMBOL_GPL(fc_register_rx_pkt);
+
+void fc_unregister_rx_pkt(int cc)
+{
+	unsigned long flags;
+	struct fc_rx_cb *cur, *next;
+	int found = 0;
+
+	spin_lock_irqsave(&rx_cb_lock, flags);
+	list_for_each_entry_safe(cur, next, &rx_cb_list, list) {
+		if (cur->cc == cc) {
+			list_del(&cur->list);
+			kfree(cur);
+			found = 1;
+			break;
+		}
+	}
+
+	WARN_ON(!found);
+
+	spin_unlock_irqrestore(&rx_cb_lock, flags);
+}
+EXPORT_SYMBOL_GPL(fc_unregister_rx_pkt);
+
+static int fc_send_pkt(int cc, void *buf, size_t len)
+{
+	struct fc_rx_cb *next;
+	int ret = -ENODEV;
+
+	spin_lock(&rx_cb_lock);
+	list_for_each_entry(next, &rx_cb_list, list)
+		if (next->cc == cc)
+			ret = next->rx_cb(next->priv, buf, len);
+	spin_unlock(&rx_cb_lock);
+
+	return ret;
+}
+
+u32 fc_get_packet_len(u32 header)
+{
+	u32 len;
+
+	/*
+	 * header contains the number of transmitted 16bit words in bits 30-16.
+	 * if the number is odd the DMA engine padded with zero to 32bit.
+	 * calculate the number of transmitted bytes.
+	 */
+
+	len = le32_to_cpu(header);
+
+	len >>= FC_BUF_HEADER_LEN_SHIFT;
+	len &= FC_BUF_HEADER_LEN_MASK;
+
+	len = roundup(len, 4);
+
+	return len;
+}
+EXPORT_SYMBOL_GPL(fc_get_packet_len);
+
+static u32 fc_parse_packet(struct fc_packet_buf *pb, u32 avail,
+			   struct dma_flexcard *priv)
+{
+	u32 l, cc, len = sizeof(struct fc_packet);
+	union fc_packet_types *pt = &pb->packet;
+
+	switch (le32_to_cpu(pb->header.type)) {
+	case fc_packet_type_info:
+		len += sizeof(struct fc_info_packet);
+		cc = pt->info_packet.cc;
+		break;
+	case fc_packet_type_error:
+		len += sizeof(struct fc_error_packet);
+		cc = pt->error_packet.cc;
+		break;
+	case fc_packet_type_status:
+		len += sizeof(struct fc_status_packet);
+		cc = pt->status_packet.cc;
+		if ((priv->nr_eray == 1) && (cc == 1))
+			cc = 0;		/* self sync status */
+		break;
+	case fc_packet_type_nmv_vector:
+		len += sizeof(struct fc_nm_vector_packet);
+		cc = pt->nm_vector_packet.cc;
+		break;
+	case fc_packet_type_notification:
+		len += sizeof(struct fc_notification_packet);
+		cc = 0;
+		break;
+	case fc_packet_type_trigger_ex:
+		len += sizeof(struct fc_trigger_ex_info_packet);
+		cc = 0;
+		break;
+	case fc_packet_type_can:
+		len += sizeof(struct fc_can_packet);
+		cc = FC_CANIF_OFFSET + pt->can_packet.cc;
+		break;
+	case fc_packet_type_can_error:
+		len += sizeof(struct fc_can_error_packet);
+		cc = FC_CANIF_OFFSET + pt->can_error_packet.cc;
+		break;
+	case fc_packet_type_flexray_frame:
+		len += sizeof(struct fc_flexray_frame);
+		pt->flexray_frame.pdata = len;
+		l = fc_get_packet_len(pt->flexray_frame.header);
+		len += l;
+		cc = pt->flexray_frame.cc;
+		break;
+	case fc_packet_type_tx_ack:
+		len += sizeof(struct fc_tx_ack_packet);
+		pt->tx_ack_packet.pdata = len;
+		l = fc_get_packet_len(pt->tx_ack_packet.header);
+		len += l;
+		cc = pt->tx_ack_packet.cc;
+		if ((priv->nr_eray == 1) && (cc == 1))
+			cc = 0;		/* self sync tx ack */
+		break;
+	case fc_packet_type_trigger:
+	default:
+		return 0;
+	}
+
+	if (len > avail)
+		return 0;
+
+	fc_send_pkt(cc, pb, len);
+	return len;
+}
+
+static int poll_dma_until_idle(struct dma_flexcard *priv)
+{
+	unsigned int retry = 500;
+
+	while (1) {
+		u32 idle;
+
+		idle = readl(priv->reg + FC_DMA_CTRL) & FC_DMA_CTRL_DMA_IDLE;
+		if (idle)
+			return 0;
+		retry--;
+		if (!retry)
+			return -ETIMEDOUT;
+		udelay(10);
+	};
+}
+
+static irqreturn_t flexcard_isr(int irq, void *dev_id)
+{
+	struct platform_device *pdev = dev_id;
+	struct dma_flexcard *priv = platform_get_drvdata(pdev);
+	u32 avail, parsed, rptr = priv->rptr;
+
+#ifdef CONFIG_MFD_EBEL_FLEXCARD_DMA_POLL
+	u32 newcnt, cnt;
+	int ret;
+	/*
+	 * Calculate the number of newly available bytes and kick the
+	 * DMA engine.
+	 *
+	 * CHECKME: Is it guaranteed, that the last transfer has
+	 * finished? This really wants a sanity check.
+	 */
+	ret = poll_dma_until_idle(priv);
+	if (ret)
+		dev_err(&pdev->dev, "Could not stop the DMA engine.\n");
+
+	/* If there is new data, we kick the DMA engine */
+	cnt = readl(priv->reg + FC_FC_DATA_CNT);
+	newcnt = cnt - priv->cnt_old;
+	if (newcnt) {
+		writel(newcnt, priv->reg + FC_DMA_TXR);
+		writel(FC_DMA_TXR_TX_ENA | newcnt, priv->reg + FC_DMA_TXR);
+	}
+
+	/*
+	 * Now check if we have data to process.
+	 *
+	 * priv->dmaptr: The offset into the dma buffer on which the
+	 *		 previous DMA transfer ends
+	 * priv->rptr:	 The offset into the buffer on which our last
+	 *		 parser call finished
+	 */
+	avail = (priv->dmaptr - rptr) & FC_DMA_BUF_MASK;
+
+	/* Update the data for the next round */
+	priv->cnt_old = cnt;
+	priv->dmaptr = (priv->dmaptr + newcnt) & FC_DMA_BUF_MASK;
+#else
+	/* Get amount of available data from common buffer level*/
+	avail = readl(priv->reg + FC_DMA_CBLR);
+#endif
+
+	if (!avail)
+		return IRQ_NONE;
+
+	do {
+		u32 tocp = rptr + FC_MAX_PAKET_SIZE;
+		/*
+		 * For simplicity the parser always looks at contiguous
+		 * buffer space.
+		 *
+		 * We ensure that by copying the eventually wrapped
+		 * bytes of the next message from the bottom of the
+		 * dma buffer to the space right after the dma buffer
+		 * which has been allocated just for that reason.
+		 */
+		if (tocp > FC_DMA_BUF_SIZE) {
+			tocp &= FC_DMA_BUF_MASK;
+			memcpy(priv->buf + FC_DMA_BUF_SIZE, priv->buf, tocp);
+		}
+
+		parsed = fc_parse_packet(priv->buf + rptr, avail, priv);
+		if (parsed > avail) {
+			dev_err(&pdev->dev, "Parser overrun\n");
+			rptr = (rptr + parsed) & FC_DMA_BUF_MASK;
+			break;
+		}
+		avail -= parsed;
+		rptr = (rptr + parsed) & FC_DMA_BUF_MASK;
+	} while (parsed && avail);
+
+	/* Update the read pointer in the device if we processed data */
+	if (priv->rptr != rptr) {
+		u32 *p = priv->buf + 2 * FC_DMA_BUF_SIZE - 4;
+		*p = priv->rptr = rptr;
+		writel(rptr, priv->reg + FC_DMA_RPTR);
+	} else {
+		/* This should not happen. Or can it ? */
+		dev_err(&pdev->dev, "rptr unchanged\n");
+	}
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t flexcard_ovr_isr(int irq, void *dev_id)
+{
+	struct platform_device *pdev = dev_id;
+	struct dma_flexcard *priv = platform_get_drvdata(pdev);
+	u32 stat;
+
+	/* check overflow flag */
+	stat = readl(priv->reg + FC_DMA_STAT);
+	if (!(stat & FC_DMA_STAT_OFL))
+		return IRQ_NONE;
+
+	dev_err(&pdev->dev, "DMA buffer overflow\n");
+	writel(0, priv->reg + FC_DMA_RPTR);
+
+	/* reset overflow flag */
+	writel(FC_DMA_STAT_OFL, priv->reg + FC_DMA_STAT);
+
+	return IRQ_HANDLED;
+}
+
+static int check_flex_fw_version(u32 fw_version)
+{
+	u8 fw_major, fw_minor, fw_update;
+
+	fw_major = (fw_version >> 16) & 0xff;
+	fw_minor = (fw_version >> 8) & 0xff;
+	fw_update = fw_version & 0xff;
+
+	if ((fw_major == 0xFF) && (fw_minor == 0xFF) && (fw_update == 0xFF))
+		/* error while reading informations */
+		return -EINVAL;
+	if (fw_major > MIN_FW_MAJOR)
+		return 0;
+	if (fw_major < MIN_FW_MAJOR)
+		return -EINVAL;
+	if (fw_minor > MIN_FW_MINOR)
+		return 0;
+	if (fw_minor < MIN_FW_MINOR)
+		return -EINVAL;
+	if (fw_update < MIN_FW_UPDATE)
+		return -EINVAL;
+	return 0;
+}
+
+static int flexcard_dma_probe(struct platform_device *pdev)
+{
+	struct dma_flexcard *priv;
+	struct resource *res;
+	int irq_ovr, irq, ret = -ENXIO;
+	u32 fw_version;
+	u32 avail, sup;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "failed to get I/O memory\n");
+		goto out;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "failed to get CBL IRQ\n");
+		goto out;
+	}
+
+	irq_ovr = platform_get_irq(pdev, 1);
+	if (irq_ovr < 0) {
+		dev_err(&pdev->dev, "failed to get CO IRQ\n");
+		goto out;
+	}
+
+	priv = kzalloc(sizeof(struct dma_flexcard), GFP_KERNEL);
+	if (!priv) {
+		dev_err(&pdev->dev, "out of memory\n");
+		goto out;
+	}
+
+	priv->buf = dma_alloc_coherent(&pdev->dev,
+				       2 * FC_DMA_BUF_SIZE,
+				       &priv->phys, GFP_KERNEL);
+	if (!priv->buf) {
+		dev_err(&pdev->dev, "could not allocate DMA memory\n");
+		goto out_free_priv;
+	}
+
+	priv->reg = ioremap_nocache(res->start, resource_size(res));
+	if (!priv->reg) {
+		dev_err(&pdev->dev, "failed to remap I/O memory\n");
+		goto out_free_buf;
+	}
+
+	fw_version = readl(priv->reg + FC_FC_FW_VER);
+	ret = check_flex_fw_version(fw_version);
+	if (ret) {
+		dev_err(&pdev->dev, "minimum FW Version %d.%d.%d required.",
+			MIN_FW_MAJOR, MIN_FW_MINOR, MIN_FW_UPDATE);
+		goto out_unmap;
+	}
+
+	avail = readl(priv->reg + FC_FC_LIC0) & FC_LIC0_FLEXRAY_MASK;
+	sup = readl(priv->reg + FC_FC_SLIC0) & FC_SLIC0_FLEXRAY_MASK;
+
+	priv->nr_eray = min(avail, sup);
+
+	priv->info.mem[0].addr = priv->phys;
+	priv->info.mem[0].size = 2 * FC_DMA_BUF_SIZE;
+	priv->info.mem[0].memtype = UIO_MEM_PHYS;
+	priv->info.name = "FlexCard_DMA_buffer";
+	priv->info.version = "0.0.1";
+	priv->info.irq = 0;
+	if (uio_register_device(&pdev->dev, &priv->info)) {
+		dev_err(&pdev->dev, "could not register debug device\n");
+		goto out_unmap;
+	}
+
+	priv->irq = irq;
+	priv->irq_ovr = irq_ovr;
+	priv->cnt_old = readl(priv->reg + FC_FC_DATA_CNT);
+	writel(0, priv->reg + FC_DMA_IRER);
+
+	/* reset dma controller */
+	writel(FC_DMA_CTRL_RST_DMA, priv->reg + FC_DMA_CTRL);
+
+	ret = poll_dma_until_idle(priv);
+	if (ret) {
+		dev_err(&pdev->dev, "could not reset Flexcard DMA\n");
+		goto out_uio;
+	}
+
+	platform_set_drvdata(pdev, priv);
+
+	writel(0x0, priv->reg + FC_DMA_WPTR);
+	writel(0x0, priv->reg + FC_DMA_RPTR);
+	writel(0x0, priv->reg + FC_DMA_CTRL);
+
+	writel(priv->phys, priv->reg + FC_DMA_CBAL);
+	writel((u64) priv->phys >> 32, priv->reg + FC_DMA_CBAH);
+	writel(FC_DMA_BUF_SIZE, priv->reg + FC_DMA_CBS);
+
+	ret = request_threaded_irq(irq, NULL, flexcard_isr, IRQF_ONESHOT,
+				   "flexcard-CBL", pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "could not request Flexcard DMA CBL IRQ\n");
+		goto out_unmap;
+	}
+
+	ret = request_irq(irq_ovr, flexcard_ovr_isr, 0, "flexcard-CO", pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "could not request Flexcard DMA CO IRQ\n");
+		goto out_free_irq;
+	}
+
+#ifdef CONFIG_MFD_EBEL_FLEXCARD_DMA_POLL
+	writel((FC_DMA_CTRL_DMA_ENA | FC_DMA_CTRL_MAN_ENA),
+			priv->reg + FC_DMA_CTRL);
+#else
+	/* enable dma but not manual dma mode */
+	writel(FC_DMA_CTRL_DMA_ENA, priv->reg + FC_DMA_CTRL);
+	/* set common buffer level configuration to an initial value */
+	writel(0x300, priv->reg + FC_DMA_CBCR);
+#endif
+
+	dev_info(&pdev->dev, "Flexcard DMA registered");
+	return 0;
+
+out_free_irq:
+	writel(0x0, priv->reg + FC_DMA_CTRL);
+	free_irq(irq, pdev);
+	platform_set_drvdata(pdev, NULL);
+out_uio:
+	uio_unregister_device(&priv->info);
+out_unmap:
+	iounmap(priv->reg);
+out_free_buf:
+	dma_free_coherent(&pdev->dev, 2 * FC_DMA_BUF_SIZE, priv->buf,
+			  priv->phys);
+out_free_priv:
+	kfree(priv);
+out:
+	return ret;
+}
+
+static int flexcard_dma_remove(struct platform_device *pdev)
+{
+	struct dma_flexcard *priv = platform_get_drvdata(pdev);
+	int retry = 200;
+	int ret;
+
+	WARN_ON(!list_empty(&rx_cb_list));
+
+	uio_unregister_device(&priv->info);
+
+	free_irq(priv->irq, pdev);
+	free_irq(priv->irq_ovr, pdev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	writel(FC_DMA_CTRL_STOP_REQ, priv->reg + FC_DMA_CTRL);
+	ret = poll_dma_until_idle(priv);
+	if (ret)
+		dev_err(&pdev->dev, "could not stop DMA state machine\n");
+
+	while (1) {
+		u32 busy;
+
+		busy = readl(priv->reg + FC_DMA_STAT);
+		busy &= FC_DMA_STAT_DMA_DSTAT_BUSY;
+		if (!busy)
+			break;
+		retry--;
+		if (!retry)
+			break;
+		udelay(1);
+	};
+	if (!retry)
+		dev_err(&pdev->dev, "could not stop DMA\n");
+
+	/* reset dma controller */
+	writel(FC_DMA_CTRL_RST_DMA, priv->reg + FC_DMA_CTRL);
+	writel(0x0, priv->reg + FC_DMA_CBCR);
+
+	/* disable dma controller */
+	writel(0x0, priv->reg + FC_DMA_CTRL);
+	iounmap(priv->reg);
+
+	dma_free_coherent(&pdev->dev, 2 * FC_DMA_BUF_SIZE, priv->buf,
+			  priv->phys);
+	kfree(priv);
+
+	return 0;
+}
+
+static struct platform_driver flexcard_dma_driver = {
+	.probe		= flexcard_dma_probe,
+	.remove		= flexcard_dma_remove,
+	.driver		= {
+		.name   = "flexcard-dma",
+		.owner = THIS_MODULE,
+	}
+};
+module_platform_driver(flexcard_dma_driver);
+MODULE_ALIAS("platform:flexcard-dma");
+
+MODULE_DESCRIPTION("Flexcard DMA Platform Driver");
+MODULE_AUTHOR("Benedikt Spranger <b.spranger@linutronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/flexcard-irq.c b/drivers/mfd/flexcard-irq.c
new file mode 100644
index 0000000..c566841
--- /dev/null
+++ b/drivers/mfd/flexcard-irq.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved.
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/uio_driver.h>
+#include <linux/flexcard.h>
+#include <linux/eray.h>
+
+#include "flexcard.h"
+
+#define to_irq_tab(e, r, s) \
+{	\
+	.enable = (1 << e),	\
+	.reset  = (1 << r),	\
+	.status = (1 << s),	\
+}
+
+/*
+ * Interrupt Controler Register S-Box
+ * Unlike other irq controllers the FlexCard bits for enable, reset and status
+ * look more like a cryptographic S-box. Make a const table to have a more
+ * easier access to this bits in the irqchip callback functions.
+ * The table contains the registers for PMC2-cards.
+ */
+static const struct fc_irq_tab fc_irq_tab[] = {
+	to_irq_tab(18,  2, 30), /* CC3YCS */
+	to_irq_tab(19,  6, 25), /* CC4YCS */
+	to_irq_tab(20, 14, 17), /* WAKE4A */
+	to_irq_tab(21, 15, 16), /* WAKE4B */
+	to_irq_tab(22, 12, 19), /* WAKE3A */
+	to_irq_tab(23, 13, 18), /* WAKE3B */
+	to_irq_tab(24,  8, 23), /* WAKE2A */
+	to_irq_tab(25,  9, 22), /* WAKE2B */
+	to_irq_tab(26,  4, 26), /* WAKE1A */
+	to_irq_tab(27,  5, 27), /* WAKE1B */
+	to_irq_tab(28,  0, 28), /* TIMER  */
+	to_irq_tab(29,  1, 29), /* CC1YCS */
+	to_irq_tab(30, 10, 21), /* CC2YCS */
+	to_irq_tab( 0,  0,  0), /* NOP    */
+	to_irq_tab(15 , 0, 31), /* CC1T0  */
+	to_irq_tab(14 , 0,  3), /* CC2T0  */
+	to_irq_tab(16 , 0, 24), /* CC3T0  */
+	to_irq_tab(17 , 0, 20), /* CC4T0  */
+	to_irq_tab( 0 , 0,  0), /* DMA_C0 */
+	to_irq_tab( 1 , 1,  1), /* DMA_TE */
+	to_irq_tab( 4 , 4,  4), /* DMA_TI */
+	to_irq_tab( 5 , 5,  5), /* DMA_CBL*/
+};
+
+/* reset interrupt */
+static void fc_irq_ack(struct irq_data *d)
+{
+	struct fc_priv *priv = irq_data_get_irq_chip_data(d);
+	int irq = d->irq - priv->irq_start;
+
+	if (irq < FC_IRQ_BANK1_NR) {
+		writel(fc_irq_tab[irq].reset, priv->conf + FC_ISR);
+	} else if (irq < FC_IRQ_BANK2_NR) {
+		writel(ERAY_SIR_BIT(TI0), priv->mmio + ERAY_SIR +
+		       (irq - FC_IRQ_BANK1_NR) * 0x4000);
+	} else if (irq < FC_IRQ_BANK3_NR) {
+		writel(fc_irq_tab[irq].reset, priv->conf + FC_DMA_IRSR);
+	}
+}
+
+/* disable interrupt */
+static void fc_irq_mask(struct irq_data *d)
+{
+	struct fc_priv *priv = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	int irq = d->irq - priv->irq_start;
+	u32 irc;
+
+	if (irq < FC_IRQ_BANK2_NR) {
+		raw_spin_lock_irqsave(&priv->irq_lock, flags);
+		irc = readl(priv->conf + FC_IER);
+		irc &= ~fc_irq_tab[irq].enable;
+		writel(irc, priv->conf + FC_IER);
+		raw_spin_unlock_irqrestore(&priv->irq_lock, flags);
+	} else if (irq < FC_IRQ_BANK3_NR) {
+		raw_spin_lock_irqsave(&priv->irq_lock, flags);
+#ifdef CONFIG_MFD_EBEL_FLEXCARD_DMA_POLL
+		priv->active &= ~(1 << irq);
+#else
+		irc = readl(priv->conf + FC_DMA_IRER);
+		irc &= ~fc_irq_tab[irq].enable;
+		writel(irc, priv->conf + FC_DMA_IRER);
+#endif
+		raw_spin_unlock_irqrestore(&priv->irq_lock, flags);
+	}
+}
+
+/* enable interrupt */
+static void fc_irq_unmask(struct irq_data *d)
+{
+	struct fc_priv *priv = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	int irq = d->irq - priv->irq_start;
+	u32 irc;
+
+	if (irq < FC_IRQ_BANK2_NR) {
+		raw_spin_lock_irqsave(&priv->irq_lock, flags);
+		irc = readl(priv->conf + FC_IER);
+		irc |= fc_irq_tab[irq].enable;
+		writel(irc, priv->conf + FC_IER);
+		raw_spin_unlock_irqrestore(&priv->irq_lock, flags);
+	} else if (irq < FC_IRQ_BANK3_NR) {
+		raw_spin_lock_irqsave(&priv->irq_lock, flags);
+#ifdef CONFIG_MFD_EBEL_FLEXCARD_DMA_POLL
+		priv->active |= 1 << irq;
+#else
+		irc = readl(priv->conf + FC_DMA_IRER);
+		irc |= FC_DMA_IRER_DIRE;
+		irc |= fc_irq_tab[irq].enable;
+		writel(irc, priv->conf + FC_DMA_IRER);
+#endif
+		raw_spin_unlock_irqrestore(&priv->irq_lock, flags);
+	}
+}
+
+static struct irq_chip fc_irq_chip = {
+	.name		= "fc_irq",
+	.irq_ack	= fc_irq_ack,
+	.irq_mask	= fc_irq_mask,
+	.irq_unmask	= fc_irq_unmask,
+};
+
+/*
+ * the firmware multiplexes the interrupts and saves the interrupt reason in
+ * two registers FC_ISR and FC_DMA_IRSR
+ * here we distribute the interrupts.
+*/
+static irqreturn_t fc_demux(int irq, void *data)
+{
+	struct fc_priv *priv = data;
+	int i;
+	u32 stat, stat_dma;
+	irqreturn_t ret = IRQ_NONE;
+	int err;
+
+	stat = readl(priv->conf + FC_ISR);
+	stat_dma = readl(priv->conf + FC_DMA_IRSR);
+	if (!stat && !stat_dma)
+		return IRQ_NONE;
+
+	/* FC_IRQ_BANK1/FC_IRQ_BANK2 */
+	for (i = 0; i < FC_IRQ_BANK2_NR; i++)
+		if (stat & fc_irq_tab[i].status) {
+			err = generic_handle_irq(priv->irq_start + i);
+			if (!err)
+				ret = IRQ_HANDLED;
+		}
+
+#ifdef CONFIG_MFD_EBEL_FLEXCARD_DMA_POLL
+	if (stat & fc_irq_tab[FC_IRQ_TIMER_OFF].status ||
+	    stat & fc_irq_tab[FC_IRQ_CC1CYCS_OFF].status ||
+	    stat & fc_irq_tab[FC_IRQ_CC2CYCS_OFF].status ||
+	    stat & fc_irq_tab[FC_IRQ_CC3CYCS_OFF].status ||
+	    stat & fc_irq_tab[FC_IRQ_CC4CYCS_OFF].status) {
+		if (priv->dma_on_irq) {
+			err = generic_handle_irq(priv->irq_start +
+					FC_IRQ_DMA_CBL_OFF);
+			if (!err)
+				ret = IRQ_HANDLED;
+		}
+	}
+#endif
+
+	/* DMA */
+	if (stat_dma & FC_DMA_IRSR_COISR) {
+		generic_handle_irq(priv->irq_start + FC_IRQ_DMA_CO_OFF);
+		ret = IRQ_HANDLED;
+	}
+
+	if (stat_dma & FC_DMA_IRSR_TEISR) {
+		generic_handle_irq(priv->irq_start + FC_IRQ_DMA_TE_OFF);
+		ret = IRQ_HANDLED;
+	}
+
+	if (stat_dma & FC_DMA_IRSR_TISR) {
+		generic_handle_irq(priv->irq_start + FC_IRQ_DMA_TI_OFF);
+		ret = IRQ_HANDLED;
+	}
+
+	if (stat_dma & FC_DMA_IRSR_CBLISR) {
+		generic_handle_irq(priv->irq_start + FC_IRQ_DMA_CBL_OFF);
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_MFD_EBEL_FLEXCARD_DMA_POLL
+#define FC_DMA_INITIAL_POLL_TIME	1000000
+
+unsigned int fc_poll_interval = CONFIG_MFD_EBEL_FLEXCARD_DMA_POLL_INTERVAL*1000;
+EXPORT_SYMBOL_GPL(fc_poll_interval);
+
+/*
+ * Timer callback function. Called with interrupts disabled.
+ */
+static enum hrtimer_restart dma_irq_poll(struct hrtimer *handle)
+{
+	struct fc_priv *priv = container_of(handle, struct fc_priv, timer);
+	u32 irsr, txr;
+
+	irsr = readl(priv->conf + FC_DMA_IRSR);
+	txr = readl(priv->conf + FC_DMA_TXR);
+
+	/*
+	 * poll the FC_DMA_IRSR_COISR flag in FC_DMA_IRSR and
+	 * the FC_DMA_TXR_TX_ENA flag in FC_DMA_TXR and eject an
+	 * FC_IRQ_DMA_CO or FC_IRQ_DMA_CBL interrupt if needed.
+	 */
+	if ((irsr & FC_DMA_IRSR_COISR) &&
+	    (priv->active & (1 << FC_IRQ_DMA_CO_OFF)))
+		generic_handle_irq(priv->irq_start + FC_IRQ_DMA_CO_OFF);
+
+	if (!(txr & FC_DMA_TXR_TX_ENA) &&
+	    (priv->active & (1 << FC_IRQ_DMA_CBL_OFF)))
+		generic_handle_irq(priv->irq_start + FC_IRQ_DMA_CBL_OFF);
+
+	hrtimer_forward_now(&priv->timer,
+			    ktime_set(0, fc_poll_interval));
+
+	return HRTIMER_RESTART;
+}
+#endif
+
+static int fc_request_msi_irq(struct pci_dev *pdev)
+{
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	int ret;
+
+	ret = pci_enable_msi(pdev);
+	if (ret) {
+		dev_warn(&pdev->dev, "could not enable MSI\n");
+		return ret;
+	}
+	dev_info(&pdev->dev, "MSI enabled\n");
+
+	ret = request_irq(pdev->irq, fc_demux, IRQF_NO_THREAD, "flexcard",
+			  priv);
+	if (ret) {
+		pci_disable_msi(pdev);
+		dev_warn(&pdev->dev, "request MSI irq failed. MSI disabled\n");
+	}
+
+	return ret;
+}
+
+int fc_irq_add(struct pci_dev *pdev)
+{
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	int i, ret;
+
+	/* Make sure none of the subirqs is enabled */
+	writel(0, priv->conf + FC_IER);
+	writel(0, priv->conf + FC_DMA_IRER);
+
+	raw_spin_lock_init(&priv->irq_lock);
+
+	ret = irq_alloc_descs(-1, 0, NR_FC_INTS, numa_node_id());
+	if (ret < 0) {
+		dev_err(&pdev->dev, "could not request irq_descs(%d)\n", ret);
+		goto out;
+	}
+	priv->irq_start = ret;
+
+	for (i = 0; i < NR_FC_INTS; i++) {
+		irq_set_chip_and_handler_name(priv->irq_start + i,
+					      &fc_irq_chip,
+					      handle_level_irq,
+					      "fc_demux");
+		irq_set_chip_data(priv->irq_start + i, priv);
+		irq_modify_status(priv->irq_start + i,
+				  IRQ_NOREQUEST | IRQ_NOAUTOEN,
+				  IRQ_NOPROBE);
+	}
+
+	if (fc_request_msi_irq(pdev)) {
+		/* shared PCI irq fallback */
+		ret = request_irq(pdev->irq, fc_demux, IRQF_NO_THREAD |
+				IRQF_SHARED, "flexcard", priv);
+		if (ret) {
+			dev_err(&pdev->dev, "could not request IRQ\n");
+			goto out_free_irq_descs;
+		}
+	}
+
+#ifdef CONFIG_MFD_EBEL_FLEXCARD_DMA_POLL
+	priv->active = 0;
+	hrtimer_init(&priv->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	priv->timer.function = dma_irq_poll;
+
+	hrtimer_start(&priv->timer, ktime_set(0, FC_DMA_INITIAL_POLL_TIME),
+		      HRTIMER_MODE_REL);
+#endif
+	return 0;
+
+out_free_irq_descs:
+	irq_free_descs(priv->irq_start, NR_FC_INTS);
+out:
+	return ret;
+}
+
+void fc_irq_remove(struct pci_dev *pdev)
+{
+	struct fc_priv *priv = pci_get_drvdata(pdev);
+	int i;
+
+	writel(0, priv->conf + FC_IER);
+	writel(0, priv->conf + FC_DMA_IRER);
+
+#ifdef CONFIG_MFD_EBEL_FLEXCARD_DMA_POLL
+	hrtimer_cancel(&priv->timer);
+#endif
+
+	free_irq(pdev->irq, priv);
+	pci_disable_msi(pdev);
+
+	for (i = 0; i < NR_FC_INTS; i++)
+		irq_set_chip(priv->irq_start + i, NULL);
+
+	irq_free_descs(priv->irq_start, NR_FC_INTS);
+}
diff --git a/drivers/mfd/flexcard.h b/drivers/mfd/flexcard.h
new file mode 100644
index 0000000..764f50e
--- /dev/null
+++ b/drivers/mfd/flexcard.h
@@ -0,0 +1,8 @@
+#ifndef _FLEXCARD_H_
+#define _FLEXCARD_H_
+
+extern unsigned int fc_poll_interval;
+int fc_irq_add(struct pci_dev *pdev);
+void fc_irq_remove(struct pci_dev *pdev);
+
+#endif
diff --git a/include/linux/eray.h b/include/linux/eray.h
new file mode 100644
index 0000000..9ce9c9b
--- /dev/null
+++ b/include/linux/eray.h
@@ -0,0 +1,650 @@
+/*
+ * Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved.
+ */
+
+#ifndef _LINUX_ERAY_H
+#define _LINUX_ERAY_H
+
+#include <linux/delay.h>
+#include <uapi/linux/eray.h>
+
+#define ERAY_CIF1	0x000
+#define ERAY_CIF2	0x004
+#define ERAY_CIF3	0x008
+#define ERAY_CIF4	0x00c
+
+#define ERAY_LCK	0x01c
+#define ERAY_EIR	0x020
+#define ERAY_SIR	0x024
+#define ERAY_EILS	0x028
+#define ERAY_SILS	0x02c
+#define ERAY_EIES	0x030
+#define ERAY_EIER	0x034
+#define ERAY_SIES	0x038
+#define ERAY_SIER	0x03c
+#define ERAY_ILE	0x040
+#define ERAY_T0C	0x044
+#define ERAY_T1C	0x048
+#define ERAY_STPW1	0x04c
+#define ERAY_STPW2	0x050
+
+#define ERAY_SUCC1	0x080
+#define ERAY_SUCC2	0x084
+#define ERAY_SUCC3	0x088
+#define ERAY_NEMC	0x08c
+#define ERAY_PRTC1	0x090
+#define ERAY_PRTC2	0x094
+#define ERAY_MHDC	0x098
+
+#define ERAY_GTUC1	0x0a0
+#define ERAY_GTUC2	0x0a4
+#define ERAY_GTUC3	0x0a8
+#define ERAY_GTUC4	0x0ac
+#define ERAY_GTUC5	0x0b0
+#define ERAY_GTUC6	0x0b4
+#define ERAY_GTUC7	0x0b8
+#define ERAY_GTUC8	0x0bc
+#define ERAY_GTUC9	0x0c0
+#define ERAY_GTUC10	0x0c4
+#define ERAY_GTUC11	0x0c8
+
+#define ERAY_CCSV	0x100
+#define ERAY_CCEV	0x104
+
+#define ERAY_SCV	0x110
+#define ERAY_MTCCV	0x114
+#define ERAY_RCV	0x118
+#define ERAY_OCV	0x11c
+#define ERAY_SFS	0x120
+#define ERAY_SWNIT	0x124
+#define ERAY_ACS	0x128
+
+#define ERAY_ESID1	0x130
+#define ERAY_ESID2	0x134
+#define ERAY_ESID3	0x138
+#define ERAY_ESID4	0x13c
+#define ERAY_ESID5	0x140
+#define ERAY_ESID6	0x144
+#define ERAY_ESID7	0x148
+#define ERAY_ESID8	0x14c
+#define ERAY_ESID9	0x150
+#define ERAY_ESID10	0x154
+#define ERAY_ESID11	0x158
+#define ERAY_ESID12	0x15c
+#define ERAY_ESID13	0x160
+#define ERAY_ESID14	0x164
+#define ERAY_ESID15	0x168
+
+#define ERAY_OSID1	0x170
+#define ERAY_OSID2	0x174
+#define ERAY_OSID3	0x178
+#define ERAY_OSID4	0x17c
+#define ERAY_OSID5	0x180
+#define ERAY_OSID6	0x184
+#define ERAY_OSID7	0x188
+#define ERAY_OSID8	0x18c
+#define ERAY_OSID9	0x190
+#define ERAY_OSID10	0x194
+#define ERAY_OSID11	0x198
+#define ERAY_OSID12	0x19c
+#define ERAY_OSID13	0x1a0
+#define ERAY_OSID14	0x1a4
+#define ERAY_OSID15	0x1a8
+
+#define ERAY_NMV1	0x1b0
+#define ERAY_NMV2	0x1b4
+#define ERAY_NMV3	0x1b8
+
+#define ERAY_MRC	0x300
+#define ERAY_FRF	0x304
+#define ERAY_FRFM	0x308
+#define ERAY_FCL	0x30c
+#define ERAY_MHDS	0x310
+#define ERAY_LDTS	0x314
+#define ERAY_FSR	0x318
+#define ERAY_MHDF	0x31c
+#define ERAY_TXRQ1	0x320
+#define ERAY_TXRQ2	0x324
+#define ERAY_TXRQ3	0x328
+#define ERAY_TXRQ4	0x32c
+#define ERAY_NDAT1	0x330
+#define ERAY_NDAT2	0x334
+#define ERAY_NDAT3	0x338
+#define ERAY_NDAT4	0x33c
+#define ERAY_MBSC1	0x340
+#define ERAY_MBSC2	0x344
+#define ERAY_MBSC3	0x348
+#define ERAY_MBSC4	0x34c
+
+#define ERAY_CREL	0x3f0
+#define ERAY_ENDN	0x3f4
+
+#define ERAY_WRDS(x)	(0x400 + (x * 4))
+#define ERAY_WRHS1	0x500
+#define ERAY_WRHS2	0x504
+#define ERAY_WRHS3	0x508
+
+#define ERAY_IBCM	0x510
+#define ERAY_IBCR	0x514
+
+#define ERAY_RDDS(x)	(0x600 + (x * 4))
+#define ERAY_RDHS1	0x700
+#define ERAY_RDHS2	0x704
+#define ERAY_RDHS3	0x708
+#define ERAY_MBS	0x70c
+#define ERAY_OBCM	0x710
+#define ERAY_OBCR	0x714
+
+/* SIR / SILS / SIES / SIER */
+#define ERAY_SIR_WST	(1 << 0)
+#define ERAY_SIR_CAS	(1 << 1)
+#define ERAY_SIR_CYCS	(1 << 2)
+#define ERAY_SIR_RXI	(1 << 3)
+#define ERAY_SIR_TXI	(1 << 4)
+#define ERAY_SIR_RFNE	(1 << 5)
+#define ERAY_SIR_RFCL	(1 << 6)
+#define ERAY_SIR_NMVC	(1 << 7)
+#define ERAY_SIR_TI0	(1 << 8)
+#define ERAY_SIR_TI1	(1 << 9)
+#define ERAY_SIR_TIBC	(1 << 10)
+#define ERAY_SIR_TOBC	(1 << 11)
+#define ERAY_SIR_SWE	(1 << 12)
+#define ERAY_SIR_SUCS	(1 << 13)
+#define ERAY_SIR_MBSI	(1 << 14)
+#define ERAY_SIR_SDS	(1 << 15)
+#define ERAY_SIR_WUPA	(1 << 16)
+#define ERAY_SIR_MTSA	(1 << 17)
+#define ERAY_SIR_WUPB	(1 << 24)
+#define ERAY_SIR_MTSB	(1 << 25)
+
+#define ERAY_SIR_BIT(x)		ERAY_SIR_##x
+#define ERAY_SIES_BIT(x)	ERAY_SIR_##x
+#define ERAY_SILS_BIT(x)	ERAY_SIR_##x
+#define ERAY_SIER_BIT(x)	ERAY_SIR_##x
+
+#define ERAY_SIR_MASK						      \
+	ERAY_SIR_WST | ERAY_SIR_CAS | ERAY_SIR_CYCS |		\
+	ERAY_SIR_RXI | ERAY_SIR_TXI | ERAY_SIR_RFNE |		\
+	ERAY_SIR_RFCL | ERAY_SIR_NMVC | ERAY_SIR_TI0 |		\
+	ERAY_SIR_TI1 | ERAY_SIR_TIBC | ERAY_SIR_TOBC |		\
+	ERAY_SIR_SWE | ERAY_SIR_SUCS | ERAY_SIR_MBSI |		\
+	ERAY_SIR_SDS | ERAY_SIR_WUPA | ERAY_SIR_MTSA |		\
+	ERAY_SIR_WUPB | ERAY_SIR_MTSB
+
+/* ILE */
+#define ERAY_ILE_EINT0_MASK	(0x1 << 0)
+#define ERAY_ILE_EINT0_SHIFT	0
+#define ERAY_ILE_EINT1_MASK	(0x1 << 1)
+#define ERAY_ILE_EINT1_SHIFT	1
+#define ERAY_ILE_MASK		(ERAY_ILE_EINT0_MASK | ERAY_ILE_EINT1_MASK)
+#define ERAY_ILE_SHIFT		ERAY_ILE_EINT0_SHIFT
+
+/* SUCC1 */
+#define ERAY_SUCC1_CMD_MASK	(0xf << 0)
+#define ERAY_SUCC1_CMD_SHIFT	0
+#define ERAY_SUCC1_PBSY_MASK	(1 << 7)
+#define ERAY_SUCC1_PBSY_SHIFT	7
+#define ERAY_SUCC1_TXST_MASK	(1 << 8)
+#define ERAY_SUCC1_TXST_SHIFT	8
+#define ERAY_SUCC1_TXSY_MASK	(1 << 9)
+#define ERAY_SUCC1_TXSY_SHIFT	9
+#define ERAY_SUCC1_CSA_MASK	(0x1f << 11)
+#define ERAY_SUCC1_CSA_SHIFT	11
+#define ERAY_SUCC1_PTA_MASK	(0x1f << 16)
+#define ERAY_SUCC1_PTA_SHIFT	16
+#define ERAY_SUCC1_WUCS_MASK	(1 << 21)
+#define ERAY_SUCC1_WUCS_SHIFT	21
+#define ERAY_SUCC1_TSM_MASK	(1 << 22)
+#define ERAY_SUCC1_TSM_SHIFT	22
+#define ERAY_SUCC1_HCSE_MASK	(1 << 23)
+#define ERAY_SUCC1_HCSE_SHIFT	23
+#define ERAY_SUCC1_MTSA_MASK	(1 << 24)
+#define ERAY_SUCC1_MTSA_SHIFT	24
+#define ERAY_SUCC1_MTSB_MASK	(1 << 25)
+#define ERAY_SUCC1_MTSB_SHIFT	25
+#define ERAY_SUCC1_MTS_MASK	(ERAY_SUCC1_MTSA_MASK | ERAY_SUCC1_MTSB_MASK)
+#define ERAY_SUCC1_MTS_SHIFT	ERAY_SUCC1_MTSA_SHIFT
+#define ERAY_SUCC1_CCHA_MASK	(1 << 26)
+#define ERAY_SUCC1_CCHA_SHIFT	26
+#define ERAY_SUCC1_CCHB_MASK	(1 << 27)
+#define ERAY_SUCC1_CCHB_SHIFT	27
+#define ERAY_SUCC1_CCH_MASK	(ERAY_SUCC1_CCHA_MASK | ERAY_SUCC1_CCHB_MASK)
+#define ERAY_SUCC1_CCH_SHIFT	ERAY_SUCC1_CCHA_SHIFT
+
+/* SUCC2 */
+#define ERAY_SUCC2_LT_MASK	(0x1fffff << 0)
+#define ERAY_SUCC2_LT_SHIFT	0
+#define ERAY_SUCC2_LTN_MASK	(0xf << 24)
+#define ERAY_SUCC2_LTN_SHIFT	24
+
+/* SUCC3 */
+#define ERAY_SUCC3_WCP_MASK	(0xf << 0)
+#define ERAY_SUCC3_WCP_SHIFT	0
+#define ERAY_SUCC3_WCF_MASK	(0xf << 4)
+#define ERAY_SUCC3_WCF_SHIFT	4
+
+/* NEMC */
+#define ERAY_NEMC_NML_MASK	(0xf << 0)
+#define ERAY_NEMC_NML_SHIFT	0
+
+/* PRTC1 */
+#define ERAY_PRTC1_TSST_MASK	(0xf << 0)
+#define ERAY_PRTC1_TSST_SHIFT	0
+#define ERAY_PRTC1_CASM_MASK	(0x7f << 4)
+#define ERAY_PRTC1_CASM_SHIFT	4
+#define ERAY_PRTC1_SPP_MASK	(0x3 << 12)
+#define ERAY_PRTC1_SPP_SHIFT	12
+#define ERAY_PRTC1_BRP_MASK	(0x3 << 14)
+#define ERAY_PRTC1_BRP_SHIFT	14
+#define ERAY_PRTC1_RXW_MASK	(0x1ff << 16)
+#define ERAY_PRTC1_RXW_SHIFT	16
+#define ERAY_PRTC1_RWP_MASK	(0x3f << 26)
+#define ERAY_PRTC1_RWP_SHIFT	26
+
+/* PRTC2 */
+#define ERAY_PRTC2_RXI_MASK	(0x3f << 0)
+#define ERAY_PRTC2_RXI_SHIFT	0
+#define ERAY_PRTC2_RXL_MASK	(0x3f << 8)
+#define ERAY_PRTC2_RXL_SHIFT	8
+#define ERAY_PRTC2_TXI_MASK	(0xff << 16)
+#define ERAY_PRTC2_TXI_SHIFT	16
+#define ERAY_PRTC2_TXL_MASK	(0x3f << 24)
+#define ERAY_PRTC2_TXL_SHIFT	24
+
+/* OBCM */
+#define ERAY_OBCM_RHSS_MASK	(0x1 << 0)
+#define ERAY_OBCM_RHSS_SHIFT	0
+#define ERAY_OBCM_RDSS_MASK	(0x1 << 1)
+#define ERAY_OBCM_RDSS_SHIFT	1
+#define ERAY_OBCM_RHSH_MASK	(0x1 << 16)
+#define ERAY_OBCM_RHSH_SHIFT	16
+#define ERAY_OBCM_RDSH_MASK	(0x1 << 17)
+#define ERAY_OBCM_RDSH_SHIFT	17
+
+/* OBCR */
+#define ERAY_OBCR_OBRS_MASK	(0x7f << 0)
+#define ERAY_OBCR_OBRS_SHIFT	0
+#define ERAY_OBCR_VIEW_MASK	(0x1 << 8)
+#define ERAY_OBCR_VIEW_SHIFT	8
+#define ERAY_OBCR_REQ_MASK	(0x1 << 9)
+#define ERAY_OBCR_REQ_SHIFT	9
+#define ERAY_OBCR_OBSYS_MASK	(0x1 << 15)
+#define ERAY_OBCR_OBSYS_SHIFT	15
+#define ERAY_OBCR_OBRH_MASK	(0x7f << 16)
+#define ERAY_OBCR_OBRH_SHIFT	16
+
+/* WRHS1 */
+#define ERAY_WRHS1_FID_MASK	(0x7ff << 0)
+#define ERAY_WRHS1_FID_SHIFT	0
+#define ERAY_WRHS1_CYC_MASK	(0x7f << 16)
+#define ERAY_WRHS1_CYC_SHIFT	16
+#define ERAY_WRHS1_CHA_MASK	(0x1 << 24)
+#define ERAY_WRHS1_CHA_SHIFT	24
+#define ERAY_WRHS1_CHB_MASK	(0x1 << 25)
+#define ERAY_WRHS1_CHB_SHIFT	25
+#define ERAY_WRHS1_CH_MASK	(ERAY_WRHS1_CHA_MASK | ERAY_WRHS1_CHB_MASK)
+#define ERAY_WRHS1_CH_SHIFT	ERAY_WRHS1_CHA_SHIFT
+#define ERAY_WRHS1_CFG_MASK	(0x1 << 26)
+#define ERAY_WRHS1_CFG_SHIFT	26
+#define ERAY_WRHS1_PPIT_MASK	(0x1 << 27)
+#define ERAY_WRHS1_PPIT_SHIFT	27
+#define ERAY_WRHS1_TXM_MASK	(0x1 << 28)
+#define ERAY_WRHS1_TXM_SHIFT	28
+#define ERAY_WRHS1_MBI_MASK	(0x1 << 29)
+#define ERAY_WRHS1_MBI_SHIFT	29
+
+/* WRHS2 */
+#define ERAY_WRHS2_CRC_MASK	(0x7ff << 0)
+#define ERAY_WRHS2_CRC_SHIFT	0
+#define ERAY_WRHS2_PLC_MASK	(0x7f << 16)
+#define ERAY_WRHS2_PLC_SHIFT	16
+
+/* WRHS3 */
+#define ERAY_WRHS3_DP_MASK	(0x7ff << 0)
+#define ERAY_WRHS3_DP_SHIFT	0
+
+/* IBCR */
+#define ERAY_IBCR_IBRH_MASK	(0x7f << 0)
+#define ERAY_IBCR_IBRH_SHIFT	0
+#define ERAY_IBCR_IBSYH_MASK	(0x1 << 15)
+#define ERAY_IBCR_IBSYH_SHIFT	15
+#define ERAY_IBCR_IBRS_MASK	(0x7f << 16)
+#define ERAY_IBCR_IBRS_SHIFT	16
+#define ERAY_IBCR_IBSYS_MASK	(0x1 << 31)
+#define ERAY_IBCR_IBSYS_SHIFT	31
+
+/* RDHS1 */
+#define ERAY_RDHS1_FID_MASK	(0x7ff << 0)
+#define ERAY_RDHS1_FID_SHIFT	0
+#define ERAY_RDHS1_CYC_MASK	(0x7f << 16)
+#define ERAY_RDHS1_CYC_SHIFT	16
+#define ERAY_RDHS1_CHA_MASK	(0x1 << 24)
+#define ERAY_RDHS1_CHA_SHIFT	24
+#define ERAY_RDHS1_CHB_MASK	(0x1 << 25)
+#define ERAY_RDHS1_CHB_SHIFT	25
+#define ERAY_RDHS1_CH_MASK	(ERAY_RDHS1_CHA_MASK | ERAY_RDHS1_CHB_MASK)
+#define ERAY_RDHS1_CH_SHIFT	ERAY_RDHS1_CHA_SHIFT
+#define ERAY_RDHS1_CFG_MASK	(0x1 << 26)
+#define ERAY_RDHS1_CFG_SHIFT	26
+#define ERAY_RDHS1_PPIT_MASK	(0x1 << 27)
+#define ERAY_RDHS1_PPIT_SHIFT	27
+#define ERAY_RDHS1_TXM_MASK	(0x1 << 28)
+#define ERAY_RDHS1_TXM_SHIFT	28
+#define ERAY_RDHS1_MBI_MASK	(0x1 << 29)
+#define ERAY_RDHS1_MBI_SHIFT	29
+
+/* RDHS2 */
+#define ERAY_RDHS2_CRC_MASK	(0x7ff << 0)
+#define ERAY_RDHS2_CRC_SHIFT	0
+#define ERAY_RDHS2_PLC_MASK	(0x7f << 16)
+#define ERAY_RDHS2_PLC_SHIFT	16
+#define ERAY_RDHS2_PLR_MASK	(0x7f << 24)
+#define ERAY_RDHS2_PLR_SHIFT	24
+
+/* RDHS3 */
+#define ERAY_RDHS3_DP_MASK	(0x7ff << 0)
+#define ERAY_RDHS3_DP_SHIFT	0
+#define ERAY_RDHS3_RCC_MASK	(0x3f << 16)
+#define ERAY_RDHS3_RCC_SHIFT	16
+#define ERAY_RDHS3_RCI_MASK	(0x1 << 24)
+#define ERAY_RDHS3_RCI_SHIFT	24
+#define ERAY_RDHS3_SFI_MASK	(0x1 << 25)
+#define ERAY_RDHS3_SFI_SHIFT	25
+#define ERAY_RDHS3_SYN_MASK	(0x1 << 26)
+#define ERAY_RDHS3_SYN_SHIFT	26
+#define ERAY_RDHS3_NFI_MASK	(0x1 << 27)
+#define ERAY_RDHS3_NFI_SHIFT	27
+#define ERAY_RDHS3_PPI_MASK	(0x1 << 28)
+#define ERAY_RDHS3_PPI_SHIFT	28
+#define ERAY_RDHS3_RES_MASK	(0x1 << 29)
+#define ERAY_RDHS3_RES_SHIFT	29
+
+/* IBCM */
+#define ERAY_IBCM_LHSH_MASK	(0x1 << 0)
+#define ERAY_IBCM_LHSH_SHIFT	0
+#define ERAY_IBCM_LDSH_MASK	(0x1 << 1)
+#define ERAY_IBCM_LDSH_SHIFT	1
+#define ERAY_IBCM_STXRH_MASK	(0x1 << 2)
+#define ERAY_IBCM_STXRH_SHIFT	2
+#define ERAY_IBCM_LHSS_MASK	(0x1 << 16)
+#define ERAY_IBCM_LHSS_SHIFT	16
+#define ERAY_IBCM_LDSS_MASK	(0x1 << 17)
+#define ERAY_IBCM_LDSS_SHIFT	17
+#define ERAY_IBCM_STXRS_MASK	(0x1 << 18)
+#define ERAY_IBCM_STXRS_SHIFT	18
+
+/* MHDC */
+#define ERAY_MHDC_SFDL_MASK	(0x7f << 0)
+#define ERAY_MHDC_SFDL_SHIFT	0
+#define ERAY_MHDC_SLT_MASK	(0x1fff << 16)
+#define ERAY_MHDC_SLT_SHIFT	16
+
+/* GTUC1 */
+#define ERAY_GTUC1_UT_MASK	(0xfffff << 0)
+#define ERAY_GTUC1_UT_SHIFT	0
+
+/* GTUC2 */
+#define ERAY_GTUC2_MPC_MASK	(0x3fff << 0)
+#define ERAY_GTUC2_MPC_SHIFT	0
+#define ERAY_GTUC2_SNM_MASK	(0xf << 16)
+#define ERAY_GTUC2_SNM_SHIFT	16
+
+/* GTUC3 */
+#define ERAY_GTUC3_UIOA_MASK	(0xff << 0)
+#define ERAY_GTUC3_UIOA_SHIFT	0
+#define ERAY_GTUC3_UIOB_MASK	(0xff << 8)
+#define ERAY_GTUC3_UIOB_SHIFT	8
+#define ERAY_GTUC3_MIOA_MASK	(0x7f << 16)
+#define ERAY_GTUC3_MIOA_SHIFT	16
+#define ERAY_GTUC3_MIOB_MASK	(0x7f << 24)
+#define ERAY_GTUC3_MIOB_SHIFT	24
+
+/* GTUC4 */
+#define ERAY_GTUC4_NIT_MASK	(0x3fff << 0)
+#define ERAY_GTUC4_NIT_SHIFT	0
+#define ERAY_GTUC4_OCS_MASK	(0x3fff << 16)
+#define ERAY_GTUC4_OCS_SHIFT	16
+
+/* GTUC5 */
+#define ERAY_GTUC5_DCA_MASK	(0xff << 0)
+#define ERAY_GTUC5_DCA_SHIFT	0
+#define ERAY_GTUC5_DCB_MASK	(0xff << 8)
+#define ERAY_GTUC5_DCB_SHIFT	8
+#define ERAY_GTUC5_CDD_MASK	(0x1f << 16)
+#define ERAY_GTUC5_CDD_SHIFT	16
+#define ERAY_GTUC5_DEC_MASK	(0xff << 24)
+#define ERAY_GTUC5_DEC_SHIFT	24
+
+/* GTUC6 */
+#define ERAY_GTUC6_ASR_MASK	(0x7ff << 0)
+#define ERAY_GTUC6_ASR_SHIFT	0
+#define ERAY_GTUC6_MOD_MASK	(0x7ff << 16)
+#define ERAY_GTUC6_MOD_SHIFT	16
+
+/* GTUC7 */
+#define ERAY_GTUC7_SSL_MASK	(0x3ff << 0)
+#define ERAY_GTUC7_SSL_SHIFT	0
+#define ERAY_GTUC7_NSS_MASK	(0x3ff << 16)
+#define ERAY_GTUC7_NSS_SHIFT	16
+
+/* GTUC8 */
+#define ERAY_GTUC8_MSL_MASK	(0x3f << 0)
+#define ERAY_GTUC8_MSL_SHIFT	0
+#define ERAY_GTUC8_NMS_MASK	(0x1fff << 16)
+#define ERAY_GTUC8_NMS_SHIFT	16
+
+/* GTUC9 */
+#define ERAY_GTUC9_APO_MASK	(0x3f << 0)
+#define ERAY_GTUC9_APO_SHIFT	0
+#define ERAY_GTUC9_MAPO_MASK	(0x1f << 8)
+#define ERAY_GTUC9_MAPO_SHIFT	8
+#define ERAY_GTUC9_DSI_MASK	(0x3 << 16)
+#define ERAY_GTUC9_DSI_SHIFT	16
+
+/* GTUC10 */
+#define ERAY_GTUC10_MOC_MASK	(0x3fff << 0)
+#define ERAY_GTUC10_MOC_SHIFT	0
+#define ERAY_GTUC10_MRC_MASK	(0x7ff << 16)
+#define ERAY_GTUC10_MRC_SHIFT	16
+
+/* GTUC11 */
+#define ERAY_GTUC11_EOCC_MASK	(0x3 << 0)
+#define ERAY_GTUC11_EOCC_SHIFT	0
+#define ERAY_GTUC11_ERCC_MASK	(0x3 << 8)
+#define ERAY_GTUC11_ERCC_SHIFT	8
+#define ERAY_GTUC11_EOC_MASK	(0x7 << 16)
+#define ERAY_GTUC11_EOC_SHIFT	16
+#define ERAY_GTUC11_ERC_MASK	(0x7 << 24)
+#define ERAY_GTUC11_ERC_SHIFT	24
+
+/* FRF */
+#define ERAY_FRF_CH_MASK	(0x3 << 0)
+#define ERAY_FRF_CH_SHIFT	0
+#define ERAY_FRF_FID_MASK	(0x3ff << 2)
+#define ERAY_FRF_FID_SHIFT	2
+#define ERAY_FRF_CYC_MASK	(0x7f << 16)
+#define ERAY_FRF_CYC_SHIFT	16
+#define ERAY_FRF_RSS_MASK	(0x1 << 23)
+#define ERAY_FRF_RSS_SHIFT	23
+#define ERAY_FRF_RNF_MASK	(0x1 << 24)
+#define ERAY_FRF_RNF_SHIFT	24
+
+/* FRFM */
+#define ERAY_FRFM_MFID_MASK	(0x3ff << 2)
+#define ERAY_FRFM_MFID_SHIFT	2
+
+/* MRC */
+#define ERAY_MRC_FDB_MASK	(0xff << 0)
+#define ERAY_MRC_FDB_SHIFT	0
+#define ERAY_MRC_FFB_MASK	(0xff << 8)
+#define ERAY_MRC_FFB_SHIFT	8
+#define ERAY_MRC_LCB_MASK	(0xff << 16)
+#define ERAY_MRC_LCB_SHIFT	16
+#define ERAY_MRC_SEC_MASK	(0x03 << 24)
+#define ERAY_MRC_SEC_SHIFT	24
+#define ERAY_MRC_SPLM_MASK	(0x01 << 26)
+#define ERAY_MRC_SPLM_SHIFT	26
+
+#define ERAY_MAX_BUFS		128
+#define ERAY_MAX_BUFS_SSYNC	2
+#define ERAY_MAX_MEM		2048
+
+#define ERAY_MSGBUF_CFG_LEN	4
+
+#define ERAY_FIFO_THRESHOLD	5
+
+enum eray_msgbuf_type {
+	eray_msgbuf_type_none,
+	eray_msgbuf_type_fifo,
+	eray_msgbuf_type_rx,
+	eray_msgbuf_type_tx,
+};
+
+enum eray_msgbuf_channel {
+	eray_msgbuf_ch_none,
+	eray_msgbuf_ch_a,
+	eray_msgbuf_ch_b,
+	eray_msgbuf_ch_both,
+};
+
+struct eray_msgbuf_cfg {
+	u32 flags;
+	u8 id;
+	u8 cyc;
+	u16 len;
+	u16 max;
+	u16 start;
+	u32 frame_id;
+	u32 reject_mask;
+	u32 wrhs1;
+	u32 wrhs2;
+	u32 wrhs3;
+	enum eray_msgbuf_type type;
+	enum eray_msgbuf_channel channel;
+	u8 queued;
+	u8 tx_cont_len;
+	unsigned char tx_cont_data[256];
+	spinlock_t lock;
+} __packed;
+
+struct eray_cc {
+	struct eray_msgbuf_cfg cfg[ERAY_MAX_BUFS];
+	DECLARE_BITMAP(memmap, ERAY_MAX_MEM);
+	u8 rev_id[ERAY_MAX_BUFS];
+	int act_cfg;
+	int sync_start;
+	struct eray_msgbuf_cfg sync_cfg;
+	int sync_num;
+	int ssync_start;
+	struct eray_msgbuf_cfg ssync_cfg[ERAY_MAX_BUFS_SSYNC];
+	int ssync_num;
+	int ready;
+	u16 fifo_len;
+	u8 fifo_threshold;
+	u16 static_id;
+	u8 static_len;
+	spinlock_t lock;
+	void __iomem *base;
+};
+
+static inline u32 eray_readl(struct eray_cc *cc, int offset)
+{
+	return readl(cc->base + offset);
+}
+
+static inline void eray_writel(u32 val, struct eray_cc *cc, int offset)
+{
+	writel(val, cc->base + offset);
+}
+
+static inline void eray_get_val8(u8 *val, struct eray_cc *cc, int offset,
+				 u32 mask, u32 shift)
+{
+	u32 tmp;
+
+	tmp = eray_readl(cc, offset);
+	tmp &= mask;
+	tmp >>= shift;
+	*val = tmp & 0xff;
+}
+
+static inline void eray_get_val16(u16 *val, struct eray_cc *cc, int offset,
+				  u32 mask, u32 shift)
+{
+	u32 tmp;
+
+	tmp = eray_readl(cc, offset);
+	tmp &= mask;
+	tmp >>= shift;
+	*val = tmp & 0xffff;
+}
+
+static inline void eray_get_val32(u32 *val, struct eray_cc *cc, int offset,
+				  u32 mask, u32 shift)
+{
+	u32 tmp;
+
+	tmp = eray_readl(cc, offset);
+	tmp &= mask;
+	tmp >>= shift;
+	*val = tmp;
+}
+
+static inline void eray_chg_reg(u32 val, struct eray_cc *cc, int offset,
+				u32 mask, u32 shift)
+{
+	u32 tmp;
+
+	tmp = eray_readl(cc, offset);
+	tmp &= ~mask;
+	tmp |= (val << shift) & mask;
+	writel(tmp, cc->base + offset);
+}
+
+static inline void eray_mask_reg(u32 *val, struct eray_cc *cc, int offset,
+				 u32 mask, u32 shift)
+{
+	u32 tmp;
+
+	tmp = eray_readl(cc, offset);
+	*val = (tmp & mask) >> shift;
+}
+
+static inline int eray_read_succ1(struct eray_cc *cc, int retry)
+{
+	/* SUCC1 use only 31 bit */
+	u32 succ1, i;
+
+	for (i = 0; i < retry; i++) {
+		succ1 = eray_readl(cc, ERAY_SUCC1);
+
+		if (!(succ1 & ERAY_SUCC1_PBSY_MASK))
+			return succ1;
+
+		udelay(10);
+	}
+
+	return -EBUSY;
+}
+
+static inline int eray_wait_clear(struct eray_cc *cc, u32 reg, u32 mask,
+				  int retry)
+{
+	int i;
+
+	for (i = 0; i < retry; i++) {
+		if (!(eray_readl(cc, reg) & mask))
+			return 0;
+
+		udelay(5);
+	}
+
+	return -EBUSY;
+}
+
+static inline int eray_wait_for_obsys(struct eray_cc *cc, int retry)
+{
+	return eray_wait_clear(cc, ERAY_OBCR, ERAY_OBCR_OBSYS_MASK, retry);
+}
+
+#endif
diff --git a/include/linux/flexcard.h b/include/linux/flexcard.h
new file mode 100644
index 0000000..1a4ec8b
--- /dev/null
+++ b/include/linux/flexcard.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved.
+ */
+
+#ifndef LINUX_FLEXCARD_H
+#define LINUX_FLEXCARD_H
+
+#include <linux/types.h>
+#include <uapi/linux/flexcard.h>
+
+#define PCI_VENDOR_ID_EBEL	0x1974
+#define PCI_DEVICE_ID_FC_PMC2	0x0009
+
+#define FC_MAX_CC		8
+#define FC_DMA_BUF_SIZE		0x200000
+#define FC_DMA_BUF_MASK		(FC_DMA_BUF_SIZE - 1)
+#define FC_DMA_INFO_SPACE	0x1000
+#define FC_MAX_PAKET_SIZE	0x200
+#define FC_MAX_XFER_SIZE	(FC_DMA_BUF_SIZE - FC_MAX_PAKET_SIZE)
+#define FC_CARRY_OFFSET		0x20
+
+#define FC_CANIF_OFFSET		0x20
+
+/* IRQ */
+#define FC_IRQ_BANK1_NR	14
+#define FC_IRQ_BANK2_NR	18
+#define FC_IRQ_BANK3_NR	22
+#define FC_IRQ_BANK4_NR	30
+#define NR_FC_INTS	FC_IRQ_BANK4_NR
+
+/* offset */
+#define FC_IRQ_CC3CYCS_OFF	0
+#define FC_IRQ_CC4CYCS_OFF	1
+#define FC_IRQ_WAKE4A_OFF	2
+#define FC_IRQ_WAKE4B_OFF	3
+#define FC_IRQ_WAKE3A_OFF	4
+#define FC_IRQ_WAKE3B_OFF	5
+#define FC_IRQ_WAKE2A_OFF	6
+#define FC_IRQ_WAKE2B_OFF	7
+#define FC_IRQ_WAKE1A_OFF	8
+#define FC_IRQ_WAKE1B_OFF	9
+#define FC_IRQ_TIMER_OFF	10
+#define FC_IRQ_CC1CYCS_OFF	11
+#define FC_IRQ_CC2CYCS_OFF	12
+/* Flexcard IRQ 13 is not in use */
+
+#define FC_IRQ_CC1T0_OFF	14
+#define FC_IRQ_CC2T0_OFF	15
+#define FC_IRQ_CC3T0_OFF	16
+#define FC_IRQ_CC4T0_OFF	17
+
+/* DMA */
+#define FC_IRQ_DMA_CO_OFF	18
+#define FC_IRQ_DMA_TE_OFF	19
+#define FC_IRQ_DMA_TI_OFF	20
+#define FC_IRQ_DMA_CBL_OFF	21
+
+/* virt. DMA IRQ */
+#define FC_IRQ_DMA_RX_OFF(irq)	(FC_IRQ_BANK3_NR + irq)
+
+/* self sync register offset */
+#define FC_SSYNC_OFFSET		0x800
+#define FC_SSYNC_TXACK_OFFSET	0x200
+
+struct fc_priv {
+	raw_spinlock_t irq_lock;
+	struct pci_dev *dev;
+	void __iomem *conf;
+	void __iomem *mmio;
+	int nrdcan;
+	int nreray;
+	int card_nr;
+	int irq_start;
+	struct mfd_cell *dcan;
+	struct mfd_cell *eray;
+
+#ifdef CONFIG_MFD_EBEL_FLEXCARD_DMA_POLL
+	u32 active;
+	struct hrtimer timer;
+	int dma_on_irq;
+#endif
+};
+
+struct fc_irq_tab {
+	u32 enable;
+	u32 reset;
+	u32 status;
+};
+
+u32 fc_get_packet_len(u32 header);
+int fc_register_rx_pkt(int cc, void *priv,
+		int (*rx_cb) (void *priv, void *data, size_t len));
+void fc_unregister_rx_pkt(int cc);
+
+#endif
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 997f9f2..8d2be7a 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -106,6 +106,7 @@ header-y += elf-em.h
 header-y += elf-fdpic.h
 header-y += elf.h
 header-y += elfcore.h
+header-y += eray.h
 header-y += errno.h
 header-y += errqueue.h
 header-y += ethtool.h
@@ -123,6 +124,7 @@ header-y += filter.h
 header-y += firewire-cdev.h
 header-y += firewire-constants.h
 header-y += flat.h
+header-y += flexcard.h
 header-y += fs.h
 header-y += fsl_hypervisor.h
 header-y += fuse.h
diff --git a/include/uapi/linux/eray.h b/include/uapi/linux/eray.h
new file mode 100644
index 0000000..9d03cfd
--- /dev/null
+++ b/include/uapi/linux/eray.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved.
+ */
+
+#ifndef UAPI_LINUX_ERAY_H
+#define UAPI_LINUX_ERAY_H
+
+/* The lower 16 bit in message buffer flags are reserved for eray */
+#define ERAY_MSGBUF_USED		0x0001
+#define ERAY_MSGBUF_STARTUP		0x0002
+#define ERAY_MSGBUF_SYNC		0x0004
+#define ERAY_MSGBUF_PPIT		0x0008
+#define ERAY_MSGBUF_TXCONT		0x0010
+#define ERAY_MSGBUF_FIFOREJ_NULL	0x0020
+#define ERAY_MSGBUF_FIFOREJ_INSEG	0x0040
+#define ERAY_MSGBUF_FLAGS_END		0x8000
+
+enum eray_cc_state {
+	ERAY_CMD_INVALID,
+	ERAY_CMD_CONFIG,
+	ERAY_CMD_READY,
+	ERAY_CMD_WAKEUP,
+	ERAY_CMD_RUN,
+	ERAY_CMD_ALL_SLOTS,
+	ERAY_CMD_HALT,
+	ERAY_CMD_FREEZE,
+	ERAY_CMD_SEND_MTS,
+	ERAY_CMD_ALLOW_COLDSTART,
+	ERAY_CMD_RESET_STATUS_INDICATORS,
+	ERAY_CMD_MONITOR_MODE,
+	ERAY_CMD_CLEAR_RAMS,
+};
+
+#endif
diff --git a/include/uapi/linux/flexcard.h b/include/uapi/linux/flexcard.h
new file mode 100644
index 0000000..a48806a
--- /dev/null
+++ b/include/uapi/linux/flexcard.h
@@ -0,0 +1,429 @@
+/*
+ * Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved.
+ */
+
+#ifndef UAPI_LINUX_FLEXCARD_H
+#define UAPI_LINUX_FLEXCARD_H
+
+#include <linux/types.h>
+
+/* BAR0 memory map */
+#define FC_FC_FW_VER	0x004
+#define FC_FC_HW_VER	0x008
+#define FC_FC_SN	0x018
+#define FC_FC_UID	0x020
+#define FC_FC_LIC0	0x040
+#define FC_FC_LIC1	0x044
+#define FC_FC_LIC2	0x048
+#define FC_FC_LIC3	0x04c
+#define FC_FC_LIC4	0x050
+#define FC_FC_LIC5	0x054
+#define FC_FC_SLIC0	0x058
+#define FC_FC_SLIC1	0x05c
+#define FC_FC_SLIC2	0x060
+#define FC_FC_SLIC3	0x064
+#define FC_FC_SLIC4	0x068
+#define FC_FC_SLIC5	0x06c
+#define FC_TRIG_CTRL1	0x070
+#define FC_TRIG_CTRL2	0x078
+#define FC_AMREG	0x0d4
+#define FC_TINY_STAT	0x0d8
+#define FC_FC_DATA_CNT	0x0f8
+#define FC_FC_ROCR	0x100
+#define FC_PL_TERM	0x10c
+#define FC_ISR		0x114
+#define FC_IER		0x11c
+#define FC_FC_TS	0x140
+#define FC_FC_RESET	0x144
+#define FC_TRIG_SC_CTRL	0x148
+#define FC_CTRL		0x14c
+#define FC_TIRQIR	0x154
+#define FC_FC_NFCTRL	0x170
+#define FC_NF_CNT	0x174
+
+#define FC_DMA_CTRL	0x500
+#define FC_DMA_STAT	0x504
+#define FC_DMA_CBAL	0x510
+#define FC_DMA_CBAH	0x514
+#define FC_DMA_CBS	0x518
+#define FC_DMA_TXR	0x51c
+#define FC_DMA_IRER	0x520
+#define FC_DMA_IRSR	0x524
+#define FC_DMA_CBCR	0x550
+#define FC_DMA_CBLR	0x554
+#define FC_DMA_ITCR	0x560
+#define FC_DMA_ITR	0x564
+#define FC_DMA_WPTR	0x570
+#define FC_DMA_RPTR	0x574
+
+#define FC_RXFILTID	0x780
+#define FC_RXFILTCH	0x784
+
+#define FC_TXFILTID	0x788
+#define FC_TXFILTCH	0x78C
+
+#define FC_ACTIMG	0xa10
+#define FC_UPDIMGINF	0xa14
+
+#define FC_COOV		0x80000000
+#define FC_RST_FR	0x00000001
+#define FC_RST_FC	0x40000000
+#define FC_RST_TS	0x00008000
+
+#define FC_FC_RESET_OFF	0x5bc
+
+#define FC_LIC0_FLEXRAY_MASK		0x000f
+#define FC_LIC0_FLEXRAY_SHIFT		0
+#define FC_LIC0_CAN_MASK		0x00f0
+#define FC_LIC0_CAN_SHIFT		4
+#define FC_LIC0_SELFSYNC_MASK		0x0f00
+#define FC_LIC0_SELFSYNC_SHIFT		8
+
+#define FC_SLIC0_FLEXRAY_MASK		0x000f
+#define FC_SLIC0_FLEXRAY_SHIFT		0
+#define FC_SLIC0_CAN_MASK		0x00f0
+#define FC_SLIC0_CAN_SHIFT		4
+#define FC_SLIC0_SELFSYNC_MASK		0x0f00
+#define FC_SLIC0_SELFSYNC_SHIFT		8
+
+#define FC_LIC5_LINUX_MASK		(1 << 31)
+#define FC_LIC5_LINUX_SHIFT		31
+#define FC_LIC5_XENOMAI_MASK		(1 << 30)
+#define FC_LIC5_XENOMAI_SHIFT		30
+#define FC_LIC5_WINDOWS_MASK		(1 << 29)
+#define FC_LIC5_WINDOWS_SHIFT		29
+#define FC_LIC5_LABVIEW_MASK		(1 << 28)
+#define FC_LIC5_LABVIEW_SHIFT		28
+
+#define FC_DMA_TXR_TX_ENA		0x80000000
+
+#define FC_DMA_CTRL_DMA_ENA (1 << 0)
+#define FC_DMA_CTRL_MAN_ENA (1 << 1)
+#define FC_DMA_CTRL_STOP_REQ (1 << 16)
+#define FC_DMA_CTRL_DMA_IDLE (1 << 17)
+#define FC_DMA_CTRL_RST_DMA (1 << 31)
+
+#define FC_DMA_IRER_DIRE (1 << 31)
+
+#define FC_ISR_CC1T0IRQ (1 << 31)
+#define FC_ISR_CC2T0IRQ (1 << 3)
+#define FC_ISR_CC3T0IRQ (1 << 24)
+#define FC_ISR_CC4T0IRQ (1 << 20)
+
+#define FC_DMA_IRSR_COISR (1 << 0)
+#define FC_DMA_IRSR_TEISR (1 << 1)
+#define FC_DMA_IRSR_TISR (1 << 4)
+#define FC_DMA_IRSR_CBLISR (1 << 5)
+
+#define FC_DMA_STAT_DMA_DSTAT_BUSY (1 << 15)
+#define FC_DMA_STAT_OFL (1 << 31)
+
+#define FC_BUFFER_INFO_TABLE		0x1000
+#define FC_BUF_INFO_ENABLE_PAYLOAD	(1 << 28)
+#define FC_BUF_INFO_ENABLE_NULLFRAMES	(1 << 27)
+#define FC_BUF_INFO_IS_TX		(1 << 13)
+#define FC_BUF_INFO_CYC_SHIFT		14
+#define FC_BUF_INFO_CHANNEL_SHIFT	11
+#define FC_BUF_HEADER_LEN_SHIFT		15
+#define FC_BUF_HEADER_LEN_MASK		0xfe
+
+/* message buffer ID flag for self sync */
+#define FC_FLEX_ID_SSYNC_FLAG	0x80000000
+
+/* FlexCard CAN TX fifo */
+#define FC_TXFIFO_MO		127
+
+#define FC_TXFIFO_FLAG		(1 << 7)
+#define FC_TXACKOFF_FLAG	(1 << 6)
+#define FC_TXFIFO_DLC_MASK	0xf
+#define FC_TXFIFO_SFF_SHIFT	16
+
+#define FC_TXFIFO_MSGID		0x800
+#define FC_TXFIFO_MSGID_STDID	0x1FFC0000
+#define FC_TXFIFO_MSGID_STDID_SHIFT	18
+#define FC_TXFIFO_MSGID_EXTID	0x1FFFFFFF
+#define FC_TXFIFO_MSGID_EXT	(1 << 30)
+
+#define FC_TXFIFO_MSGCTRL	0x804
+
+#define FC_TXFIFO_MSGDA		0x808
+#define FC_TXFIFO_MSGDB		0x80c
+
+#define FC_TXFIFO_CONF		0x820
+#define FC_TXFIFO_CONF_MO_MASK	0xff
+#define FC_TXFIFO_CONF_TXACK	(1 << 16)
+#define FC_TXFIFO_CONF_EAR	(1 << 17)
+#define FC_TXFIFO_CONF_EN	(1 << 31)
+
+#define FC_TXFIFO_STAT		0x824
+#define FC_TXFIFO_STAT_FULL	(1 << 9)
+#define FC_TXFIFO_STAT_EMPTY	(1 << 31)
+
+#define FC_TXFIFO_CTRL		0x828
+#define FC_TXFIFO_CTRL_REQ	(1 << 0)
+#define FC_TXFIFO_CTRL_CLEAR	(1 << 31)
+
+#define FC_TXFIFO_TIMER		0x830
+
+struct fc_conf_bar {
+	__u32 r1;			/* 000 */
+	__u32 fc_fw_ver;		/* 004 */
+	__u32 fc_hw_ver;		/* 008 */
+	__u32 r2[3];			/* 00c */
+	__u64 fc_sn;			/* 018 */
+	__u32 fc_uid;			/* 020 */
+	__u32 r3[7];			/* 024 */
+	__u32 fc_lic[6];		/* 040 */
+	__u32 fc_slic[6];		/* 058 */
+	__u32 trig_ctrl1;		/* 070 */
+	__u32 r4;			/* 074 */
+	__u32 trig_ctrl2;		/* 078 */
+	__u32 r5[22];			/* 07c */
+	__u32 amreg;			/* 0d4 */
+	__u32 tiny_stat;		/* 0d8 */
+	__u32 r6[5];			/* 0dc */
+	__u32 can_dat_cnt;		/* 0f0 */
+	__u32 can_err_cnt;		/* 0f4 */
+	__u32 fc_data_cnt;		/* 0f8 */
+	__u32 r7;			/* 0fc */
+	__u32 fc_rocr;			/* 100 */
+	__u32 r8;			/* 104 */
+	__u32 pg_ctrl;			/* 108 */
+	__u32 pg_term;			/* 10c */
+	__u32 r9;			/* 110 */
+	__u32 irs;			/* 114 */
+	__u32 fr_tx_cnt;		/* 118 */
+	__u32 irc;			/* 11c */
+	__u64 pcnt;			/* 120 */
+	__u32 r10;			/* 128 */
+	__u32 nmv_cnt;			/* 12c */
+	__u32 info_cnt;			/* 130 */
+	__u32 stat_trg_cnt;		/* 134 */
+	__u32 r11;			/* 138 */
+	__u32 fr_rx_cnt;		/* 13c */
+	__u32 fc_ts;			/* 140 */
+	__u32 fc_reset;			/* 144 */
+	__u32 trig_sc_ctrl;		/* 148 */
+	__u32 trig_ctrl;		/* 14c */
+	__u32 r12;			/* 150 */
+	__u32 tirqir;			/* 154 */
+	__u32 pccr1;			/* 158 */
+	__u32 pccr2;			/* 15c */
+	__u32 r13[4];			/* 160 */
+	__u32 fc_nfctrl;		/* 170 */
+	__u32 nf_cnt;			/* 174 */
+	__u32 r14;			/* 178 */
+	__u32 pl_ctrl;			/* 17c */
+	__u32 r15[0xe0];		/* 180 */
+	__u32 dma_ctrl;			/* 500 */
+	__u32 dma_stat;			/* 504 */
+	__u32 r16[2];			/* 508 */
+	__u64 dma_cba;			/* 510 */
+	__u32 dma_cbs;			/* 518 */
+	__u32 dma_txr;			/* 51c */
+	__u32 dma_irer;			/* 520 */
+	__u32 dma_irsr;			/* 524 */
+	__u32 r17[10];			/* 528 */
+	__u32 dma_cbcr;			/* 550 */
+	__u32 dma_cblr;			/* 554 */
+	__u32 r18[2];			/* 558 */
+	__u32 dma_itcr;			/* 560 */
+	__u32 dma_itr;			/* 564 */
+	__u32 r19[2];			/* 568 */
+	__u32 dma_wptr;			/* 570 */
+	__u32 dma_rptr;			/* 574 */
+	__u32 r20[0xe2];		/* 578 */
+	__u32 faddr;			/* 900 */
+	__u32 fwdat;			/* 904 */
+	__u32 fctrl;			/* 908 */
+	__u32 frdat;			/* 90c */
+	__u32 bwdat[16];		/* 910 */
+	__u32 brdat[16];		/* 950 */
+	__u32 r21[28];			/* 990 */
+	__u32 fwmode;			/* a00 */
+	__u32 recond;			/* a04 */
+	__u32 wdtctrl;			/* a08 */
+	__u32 imgsel;			/* a0c */
+	__u32 actimg;			/* a10 */
+	__u32 updimginf;		/* a14 */
+	__u32 r22[0x32];		/* a18 */
+	__u32 factory_image_info[8];	/* ae0 */
+	__u32 app_image0_info[8];	/* b00 */
+	__u32 app_image1_info[8];	/* b20 */
+	__u32 app_image2_info[8];	/* b40 */
+	__u32 app_image3_info[8];	/* b60 */
+	__u32 app_image4_info[8];	/* b80 */
+	__u32 app_image5_info[8];	/* ba0 */
+	__u32 app_image6_info[8];	/* bc0 */
+	__u32 app_image7_info[8];	/* be0 */
+	__u32 r23[0x100];		/* c00 */
+} __packed;
+
+enum fc_packet_type {
+	fc_packet_type_info = 1,
+	fc_packet_type_flexray_frame = 2,
+	fc_packet_type_error = 3,
+	fc_packet_type_status = 4,
+	fc_packet_type_trigger = 5,
+	fc_packet_type_tx_ack = 6,
+	fc_packet_type_nmv_vector = 7,
+	fc_packet_type_notification = 8,
+	fc_packet_type_trigger_ex = 9,
+	fc_packet_type_can = 10,
+	fc_packet_type_can_error = 11,
+};
+
+struct fc_packet {
+	__u32 type;
+	__u32 p_packet;
+	__u32 p_next_packet;
+} __packed;
+
+struct fc_info_packet {
+	__u32 current_cycle;
+	__u32 timestamp;
+	__u32 offset_rate_correction;
+	__u32 pta_ccf_count;
+	__u32 cc;
+} __packed;
+
+struct fc_flexray_frame {
+	__u32 header;
+	__u32 header_crc;
+	__u32 pdata;
+	__u32 channel;
+	__u32 frame_crc;
+	__u32 timestamp;
+	__u32 cc;
+} __packed;
+
+struct fc_error_packet {
+	__u32 flag;
+	__u32 timestamp;
+	__u32 cycle_count;
+	__u64 additional_info;
+	__u32 cc;
+	__u32 reserved;
+} __packed;
+
+struct fc_status_packet {
+	__u32 flag;
+	__u32 timestamp;
+	__u32 cycle_count;
+	__u32 additional_info;
+	__u32 cc;
+	__u32 reserved[2];
+} __packed;
+
+struct fc_tx_ack_packet {
+	__u32 bufferid;
+	__u32 timestamp;
+	__u32 cycle_count;
+	__u32 header;
+	__u32 header_crc;
+	__u32 pdata;
+	__u32 channel;
+	__u32 cc;
+} __packed;
+
+struct fc_nm_vector_packet {
+	__u32 timestamp;
+	__u32 cycle_count;
+	__u32 nmv_vector_length;
+	__u32 nmv_vector[3];
+	__u32 cc;
+	__u32 reserved;
+} __packed;
+
+struct fc_notification_packet {
+	__u32 timestamp;
+	__u32 sequence_count;
+	__u32 reserved;
+} __packed;
+
+struct fc_trigger_ex_info_packet {
+	__u32 condition;
+	__u32 timestamp;
+	__u32 sequence_count;
+	__u32 reserved1;
+	__u64 performance_counter;
+	__u32 edge;
+	__u32 trigger_line;
+	__u32 reserved[4];
+} __packed;
+
+struct fc_can_packet {
+	__u32 id;
+	__u32 timestamp;
+	__u32 flags;
+	__u32 reserved;
+	__u32 cc;
+	__u8 data[8];
+} __packed;
+
+struct fc_can_error_packet {
+	__u32 type;
+	__u32 state;
+	__u32 timestamp;
+	__u32 rx_error_counter;
+	__u32 tx_error_counter;
+	__u32 cc;
+	__u32 reserved[2];
+} __packed;
+
+enum fc_can_cc_state {
+	fc_can_state_unknown = 0,
+	fc_can_state_config,
+	fc_can_state_normalActive,
+	fc_can_state_warning,
+	fc_can_state_error_passive,
+	fc_can_state_bus_off,
+};
+
+enum fc_can_error_type {
+	fc_can_error_none = 0,
+	fc_can_error_stuff,
+	fc_can_error_form,
+	fc_can_error_acknowledge,
+	fc_can_error_bit1,
+	fc_can_error_bit0,
+	fc_can_error_crc,
+	fc_can_error_parity,
+};
+
+union fc_packet_types {
+	struct fc_info_packet		info_packet;
+	struct fc_flexray_frame		flexray_frame;
+	struct fc_error_packet		error_packet;
+	struct fc_status_packet		status_packet;
+	struct fc_tx_ack_packet		tx_ack_packet;
+	struct fc_nm_vector_packet	nm_vector_packet;
+	struct fc_notification_packet	notification_packet;
+	struct fc_trigger_ex_info_packet ex_info_packet;
+	struct fc_can_packet		can_packet;
+	struct fc_can_error_packet	can_error_packet;
+};
+
+struct fc_packet_buf {
+	struct	fc_packet	header;
+	union	fc_packet_types	packet;
+} __packed;
+
+enum fc_clksrc_type {
+	FC_CLK_INTERNAL_1MHZ = 0x0,
+	FC_CLK_INTERNAL_10MHZ = 0x1,
+	FC_CLK_INTERNAL_100MHZ = 0x2,
+	FC_CLK_INTERNAL_TRIGGER1 = 0x11,
+	FC_CLK_INTERNAL_TRIGGER2 = 0x12,
+};
+
+struct fc_clksrc {
+	enum fc_clksrc_type type;
+	__u32 freq;
+	__u32 mul;
+};
+
+#define FCGCLKSRC	_IOR(0xeb, 0, struct fc_clksrc)
+#define FCSCLKSRC	_IOW(0xeb, 1, struct fc_clksrc)
+
+#endif
-- 
1.8.4.rc2

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 5/7] clocksource: Add flexcard support
  2013-08-13  9:08 [PATCH 0/7] add FlexRay support Benedikt Spranger
                   ` (4 preceding siblings ...)
  2013-08-13  9:08 ` [PATCH 4/7] mfd: add MFD based flexcard driver Benedikt Spranger
@ 2013-08-13  9:08 ` Benedikt Spranger
  2013-08-13  9:08 ` [PATCH 6/7] net: add the AF_FLEXRAY protocol Benedikt Spranger
  2013-08-13  9:08 ` [PATCH 7/7] net: add a flexray driver Benedikt Spranger
  7 siblings, 0 replies; 28+ messages in thread
From: Benedikt Spranger @ 2013-08-13  9:08 UTC (permalink / raw)
  To: netdev
  Cc: Alexander Frank, Sebastian Andrzej Siewior, Benedikt Spranger,
	Daniel Lezcano, John Stultz, Thomas Gleixner, Holger Dengler

This patch adds the a clocksource and a clock event device which is
available on the FlexCard.

Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: John Stultz <john.stultz@linaro.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Benedikt Spranger <b.spranger@linutronix.de>
Signed-off-by: Holger Dengler <dengler@linutronix.de>
---
 drivers/clocksource/Kconfig        |  13 ++
 drivers/clocksource/Makefile       |   2 +
 drivers/clocksource/flexcard_clk.c | 297 +++++++++++++++++++++++++++++++++++++
 drivers/clocksource/flexcard_evt.c | 161 ++++++++++++++++++++
 drivers/mfd/Kconfig                |   2 +
 5 files changed, 475 insertions(+)
 create mode 100644 drivers/clocksource/flexcard_clk.c
 create mode 100644 drivers/clocksource/flexcard_evt.c

diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index b7b9b04..0dd5ccf 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -70,6 +70,19 @@ config CLKSRC_DBX500_PRCMU_SCHED_CLOCK
 	help
 	  Use the always on PRCMU Timer as sched_clock
 
+config FLEXCARD_CLK
+	tristate
+	select CLKSRC_MMIO
+	depends on MFD_EBEL_FLEXCARD
+	help
+	  Support for Eberspächer Flexcard clock function.
+
+config FLEXCARD_EVT
+	tristate
+	depends on MFD_EBEL_FLEXCARD
+	help
+	  Support for Eberspächer Flexcard timer function.
+
 config ARM_ARCH_TIMER
 	bool
 	select CLKSRC_OF if OF
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 8b00c5c..329e65a 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -12,6 +12,8 @@ obj-$(CONFIG_CLKBLD_I8253)	+= i8253.o
 obj-$(CONFIG_CLKSRC_MMIO)	+= mmio.o
 obj-$(CONFIG_DW_APB_TIMER)	+= dw_apb_timer.o
 obj-$(CONFIG_DW_APB_TIMER_OF)	+= dw_apb_timer_of.o
+obj-$(CONFIG_FLEXCARD_CLK)	+= flexcard_clk.o
+obj-$(CONFIG_FLEXCARD_EVT)	+= flexcard_evt.o
 obj-$(CONFIG_CLKSRC_NOMADIK_MTU)	+= nomadik-mtu.o
 obj-$(CONFIG_CLKSRC_DBX500_PRCMU)	+= clksrc-dbx500-prcmu.o
 obj-$(CONFIG_ARMADA_370_XP_TIMER)	+= time-armada-370-xp.o
diff --git a/drivers/clocksource/flexcard_clk.c b/drivers/clocksource/flexcard_clk.c
new file mode 100644
index 0000000..09f5ecb
--- /dev/null
+++ b/drivers/clocksource/flexcard_clk.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved.
+ */
+
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/posix-clock.h>
+#include <linux/slab.h>
+#include <linux/flexcard.h>
+#include <linux/io.h>
+#include <linux/export.h>
+#include <linux/module.h>
+
+#define FC_MAX_CLOCKS	16
+#define FC_CLKSEL_OFF	0x10
+
+static DECLARE_BITMAP(fc_clocks_map, FC_MAX_CLOCKS);
+static DEFINE_MUTEX(fc_clocks_mutex);
+static dev_t fc_devt;
+static struct class *fc_class;
+
+struct fc_clock {
+	struct posix_clock clock;
+	struct fc_clksrc src;
+	struct device *dev;
+	void __iomem *reg;
+	unsigned long ovr;
+	dev_t devid;
+	int index;
+	u64 old;
+};
+
+static int fc_clock_gettime(struct posix_clock *pc, struct timespec *tp)
+{
+	struct fc_clock *clk = container_of(pc, struct fc_clock, clock);
+	u64 val;
+	u64 now;
+
+	now = (u64)readl(clk->reg) << 32;
+	now |= readl(clk->reg + 0x4);
+
+	if (now < clk->old)
+		clk->ovr++;
+	clk->old = now;
+
+	val = clk->ovr * 0xffffffff + now;
+
+	tp->tv_nsec = do_div(val, clk->src.freq) * clk->src.mul;
+	tp->tv_sec = val;
+
+	return 0;
+}
+
+static int fc_clock_getres(struct posix_clock *pc, struct timespec *tp)
+{
+	struct fc_clock *clk = container_of(pc, struct fc_clock, clock);
+	tp->tv_sec = 0;
+	tp->tv_nsec = clk->src.mul;
+	return 0;
+}
+
+static int fc_clock_settime(struct posix_clock *pc, const struct timespec *tp)
+{
+	struct fc_clock *clk = container_of(pc, struct fc_clock, clock);
+
+	/* The FlexCard clock could only be reset to 0 and not set */
+	if (tp->tv_sec || tp->tv_nsec)
+		return -EINVAL;
+
+	clk->ovr = 0;
+	clk->old = 0;
+
+	/* Reset Timestamp Bit 15 (RST_TS), 0x700-0x5BC=0x144 (FC_RESET) */
+	writel(FC_RST_TS, clk->reg - FC_FC_RESET_OFF);
+
+	return 0;
+}
+
+static long fc_clock_ioctl(struct posix_clock *pc, unsigned int cmd,
+		    unsigned long arg)
+{
+	struct fc_clock *clk = container_of(pc, struct fc_clock, clock);
+	struct fc_clksrc clk_src;
+	int ret = 0;
+
+	switch (cmd) {
+	case FCGCLKSRC:
+		memset(&clk_src, 0, sizeof(clk_src));
+		clk_src.type = clk->src.type;
+		clk_src.freq = clk->src.freq;
+
+		if (copy_to_user((void __user *)arg, &clk_src,
+				 sizeof(clk_src)))
+			ret = -EFAULT;
+		break;
+	case FCSCLKSRC:
+		if (copy_from_user(&clk_src, (void __user *)arg,
+				   sizeof(clk_src))) {
+			ret = -EFAULT;
+			break;
+		}
+
+		switch (clk_src.type) {
+		case FC_CLK_INTERNAL_1MHZ:
+			clk->src.freq = 1000000;
+			clk->src.mul = NSEC_PER_USEC;
+			break;
+
+		case FC_CLK_INTERNAL_10MHZ:
+			clk->src.freq = 10000000;
+			clk->src.mul = 100;
+			break;
+
+		case FC_CLK_INTERNAL_100MHZ:
+			clk->src.freq = 100000000;
+			clk->src.mul = 10;
+			break;
+
+		case FC_CLK_INTERNAL_TRIGGER1:
+		case FC_CLK_INTERNAL_TRIGGER2:
+			if (clk_src.freq < 100000 ||
+			    clk_src.freq > NSEC_PER_SEC) {
+				ret = -EINVAL;
+				break;
+			}
+			clk->src.freq = clk_src.freq;
+			clk->src.mul = NSEC_PER_SEC/clk_src.freq;
+
+			break;
+		default:
+			ret = -EINVAL;
+		}
+
+		clk->src.type = clk_src.type;
+		writel(clk_src.type, clk->reg + FC_CLKSEL_OFF);
+		break;
+	default:
+		ret = -ENOTTY;
+		break;
+	}
+	return ret;
+}
+
+static struct posix_clock_operations fc_clock_ops = {
+	.owner		= THIS_MODULE,
+	.clock_gettime	= fc_clock_gettime,
+	.clock_getres	= fc_clock_getres,
+	.clock_settime	= fc_clock_settime,
+	.ioctl		= fc_clock_ioctl,
+};
+
+static int fc_clksrc_probe(struct platform_device *pdev)
+{
+	struct fc_clock *clk;
+	struct resource *res;
+	int index, major, ret = -ENXIO;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "failed to get I/O memory\n");
+		goto out;
+	}
+
+	mutex_lock(&fc_clocks_mutex);
+	major = MAJOR(fc_devt);
+	ret = -EBUSY;
+	index = find_first_zero_bit(fc_clocks_map, FC_MAX_CLOCKS);
+	if (index >= FC_MAX_CLOCKS) {
+		dev_err(&pdev->dev, "failed to request clock\n");
+		goto out_unlock;
+	}
+	set_bit(index, fc_clocks_map);
+
+	clk = kzalloc(sizeof(struct fc_clock), GFP_KERNEL);
+	if (!clk) {
+		dev_err(&pdev->dev, "out of memory\n");
+		goto out_clear;
+	}
+
+	clk->reg = ioremap_nocache(res->start, resource_size(res));
+	if (!clk->reg) {
+		dev_err(&pdev->dev, "failed to remap I/O memory\n");
+		goto out_free;
+	}
+
+	clk->clock.ops = fc_clock_ops;
+	clk->devid = MKDEV(major, index);
+	clk->index = index;
+
+	clk->src.type = FC_CLK_INTERNAL_1MHZ;
+	clk->src.freq = 1000000;
+	clk->src.mul = NSEC_PER_USEC;
+	writel(clk->src.type, clk->reg + FC_CLKSEL_OFF);
+
+	clk->dev = device_create(fc_class, &pdev->dev, clk->devid, clk,
+				 "fc_clk%d", clk->index);
+	if (IS_ERR(clk->dev))
+		goto out_unmap;
+
+	platform_set_drvdata(pdev, clk);
+
+	ret = posix_clock_register(&clk->clock, clk->devid);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to create posix clock\n");
+		goto out_destroy;
+	}
+
+	mutex_unlock(&fc_clocks_mutex);
+
+	dev_info(&pdev->dev, "Flexcard clocksource registered");
+	return 0;
+
+out_destroy:
+	device_destroy(fc_class, clk->devid);
+out_unmap:
+	iounmap(clk->reg);
+out_free:
+	kfree(clk);
+out_clear:
+	clear_bit(index, fc_clocks_map);
+out_unlock:
+	mutex_unlock(&fc_clocks_mutex);
+out:
+	return ret;
+}
+
+static int fc_clksrc_remove(struct platform_device *pdev)
+{
+	struct fc_clock *clk = platform_get_drvdata(pdev);
+
+	mutex_lock(&fc_clocks_mutex);
+	posix_clock_unregister(&clk->clock);
+	device_destroy(fc_class, clk->devid);
+	iounmap(clk->reg);
+	clear_bit(clk->index, fc_clocks_map);
+	platform_set_drvdata(pdev, NULL);
+	kfree(clk);
+	mutex_unlock(&fc_clocks_mutex);
+
+	return 0;
+}
+
+static struct platform_driver fc_clksrc_driver = {
+	.probe		= fc_clksrc_probe,
+	.remove		= fc_clksrc_remove,
+	.driver		= {
+		.name	= "flexcard-clksrc",
+		.owner	= THIS_MODULE,
+	}
+};
+MODULE_ALIAS("platform:flexcard-clksrc");
+
+static int __init fc_clock_init(void)
+{
+	int ret;
+
+	fc_class = class_create(THIS_MODULE, "fc_clk");
+	if (IS_ERR(fc_class)) {
+		pr_err("fc_clk: failed to allocate class\n");
+		return PTR_ERR(fc_class);
+	}
+
+	ret = alloc_chrdev_region(&fc_devt, 0, FC_MAX_CLOCKS,
+				  "flexcard_clk");
+	if (ret < 0) {
+		pr_err("failed to allocate device region\n");
+		goto out;
+	}
+
+	ret = platform_driver_register(&fc_clksrc_driver);
+	if (ret < 0)
+		goto out_unregister;
+
+	return 0;
+
+out_unregister:
+	unregister_chrdev_region(fc_devt, FC_MAX_CLOCKS);
+out:
+	class_destroy(fc_class);
+	return ret;
+}
+module_init(fc_clock_init);
+
+static void __exit fc_clock_exit(void)
+{
+	platform_driver_unregister(&fc_clksrc_driver);
+	unregister_chrdev_region(fc_devt, FC_MAX_CLOCKS);
+	class_destroy(fc_class);
+}
+module_exit(fc_clock_exit);
+
+MODULE_DESCRIPTION("Flexcard Clock Function Driver");
+MODULE_AUTHOR("Benedikt Spranger <b.spranger@linutronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clocksource/flexcard_evt.c b/drivers/clocksource/flexcard_evt.c
new file mode 100644
index 0000000..2e11c35
--- /dev/null
+++ b/drivers/clocksource/flexcard_evt.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved.
+ */
+
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/slab.h>
+#include <linux/flexcard.h>
+#include <linux/module.h>
+
+struct fc_clkevt_device {
+	struct clock_event_device	clkevt;
+	void __iomem			*tirqir;
+	bool				oneshot;
+};
+
+static struct fc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt)
+{
+	return container_of(clkevt, struct fc_clkevt_device, clkevt);
+}
+
+static void flexcard_mode(enum clock_event_mode mode,
+			  struct clock_event_device *evt)
+{
+	struct fc_clkevt_device *dev = to_tc_clkevt(evt);
+	void __iomem *tirqir = dev->tirqir;
+
+	switch (mode) {
+	case CLOCK_EVT_MODE_PERIODIC:
+		dev->oneshot = false;
+		writel(1000, tirqir);
+		break;
+
+	case CLOCK_EVT_MODE_SHUTDOWN:
+	case CLOCK_EVT_MODE_UNUSED:
+		writel(0, tirqir);
+		break;
+
+	case CLOCK_EVT_MODE_ONESHOT:
+		writel(0, tirqir);
+		dev->oneshot = true;
+		break;
+	case CLOCK_EVT_MODE_RESUME:
+		/* Nothing to do here */
+		break;
+	}
+}
+
+static int flexcard_next_event(unsigned long delta,
+			       struct clock_event_device *evt)
+{
+	struct fc_clkevt_device *dev = to_tc_clkevt(evt);
+	void __iomem *tirqir = dev->tirqir;
+
+	writel(delta, tirqir);
+
+	return 0;
+}
+
+static const struct fc_clkevt_device clkevt_templ = {
+	.clkevt = {
+		.name		= "flexcard_clkevt",
+		.features	= CLOCK_EVT_FEAT_PERIODIC |
+				  CLOCK_EVT_FEAT_ONESHOT,
+		.rating		= 300,
+		.set_next_event	= flexcard_next_event,
+		.set_mode       = flexcard_mode,
+	},
+};
+
+static irqreturn_t flexcard_evt_handler(int irq, void *dev_id)
+{
+	struct fc_clkevt_device *dev = dev_id;
+	void __iomem *tirqir = dev->tirqir;
+
+	if (dev->oneshot)
+		writel(0, tirqir);
+
+	dev->clkevt.event_handler(&dev->clkevt);
+
+	return IRQ_HANDLED;
+}
+
+static int flexcard_clkevt_probe(struct platform_device *pdev)
+{
+	struct fc_clkevt_device *clkevt = NULL;
+	struct resource *res;
+	int irq, ret = -ENXIO;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "failed to get IRQ number\n");
+		goto out;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "failed to get I/O memory\n");
+		goto out;
+	}
+
+	if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+		dev_err(&pdev->dev, "failed to request memory region\n");
+		goto out;
+	}
+
+	clkevt = kmalloc(sizeof(*clkevt), GFP_KERNEL);
+	if (!clkevt)
+		goto out_release;
+	memcpy(clkevt, &clkevt_templ, sizeof(*clkevt));
+
+	clkevt->tirqir = ioremap_nocache(res->start, resource_size(res));
+	if (!clkevt->tirqir) {
+		dev_err(&pdev->dev, "failed to remap I/O memory\n");
+		goto out_release;
+	}
+
+	ret = request_irq(irq, flexcard_evt_handler, IRQF_TIMER,
+			  "flexcard_timer", clkevt);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to request IRQ\n");
+		goto out_unmap;
+	}
+
+	clockevents_config_and_register(&clkevt->clkevt, 1000000,
+				       0xF, 0xFFFFFF);
+	dev_info(&pdev->dev, "Flexcard clockevent registered");
+	return 0;
+
+out_unmap:
+	iounmap(clkevt->tirqir);
+out_release:
+	release_mem_region(res->start, resource_size(res));
+	kfree(clkevt);
+out:
+	return ret;
+}
+
+static struct platform_driver flexcard_clkevt_driver = {
+	.probe		= flexcard_clkevt_probe,
+	.driver		= {
+		.name	= "flexcard-clkevt",
+		.owner	= THIS_MODULE,
+	}
+};
+MODULE_ALIAS("platform:flexcard-clkevt");
+
+static int __init flexcard_evt_init(void)
+{
+	return platform_driver_register(&flexcard_clkevt_driver);
+}
+module_init(flexcard_evt_init);
+
+MODULE_DESCRIPTION("Flexcard Timer Function Driver");
+MODULE_AUTHOR("Benedikt Spranger <b.spranger@linutronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index f847d52..12c60e7 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -226,6 +226,8 @@ config MFD_INTEL_MSIC
 config MFD_EBEL_FLEXCARD
 	tristate "Support for the Eberspächer Electronic Flexcard"
 	select MFD_CORE
+	select FLEXCARD_CLK
+	select FLEXCARD_EVT
 	select UIO
 	select UIO_PDRV
 	select UIO_PDRV_GENIRQ
-- 
1.8.4.rc2

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 6/7] net: add the AF_FLEXRAY protocol
  2013-08-13  9:08 [PATCH 0/7] add FlexRay support Benedikt Spranger
                   ` (5 preceding siblings ...)
  2013-08-13  9:08 ` [PATCH 5/7] clocksource: Add flexcard support Benedikt Spranger
@ 2013-08-13  9:08 ` Benedikt Spranger
  2013-08-18 18:50   ` Oliver Hartkopp
  2013-08-13  9:08 ` [PATCH 7/7] net: add a flexray driver Benedikt Spranger
  7 siblings, 1 reply; 28+ messages in thread
From: Benedikt Spranger @ 2013-08-13  9:08 UTC (permalink / raw)
  To: netdev; +Cc: Alexander Frank, Sebastian Andrzej Siewior, Benedikt Spranger

FlexRay is a networking technology used in automotive fields as
successor of the Controller Area Network (CAN). It provides the
core functionality and a RAW protocol driver.

Signed-off-by: Benedikt Spranger <b.spranger@linutronix.de>
---
 Documentation/networking/00-INDEX             |   2 +
 Documentation/networking/flexray.txt          |  24 ++
 include/linux/flexray.h                       | 168 ++++++++
 include/linux/flexray/core.h                  |  45 +++
 include/linux/flexray/dev.h                   |  64 +++
 include/linux/socket.h                        |   4 +-
 include/uapi/linux/flexray/Kbuild             |   4 +
 include/uapi/linux/flexray/flexcard_netlink.h |  53 +++
 include/uapi/linux/flexray/netlink.h          | 203 ++++++++++
 include/uapi/linux/flexray/raw.h              |  16 +
 include/uapi/linux/if_arp.h                   |   1 +
 include/uapi/linux/if_ether.h                 |   1 +
 net/Kconfig                                   |   1 +
 net/Makefile                                  |   1 +
 net/core/sock.c                               |   9 +-
 net/flexray/Kconfig                           |  28 ++
 net/flexray/Makefile                          |   9 +
 net/flexray/af_flexray.c                      | 548 ++++++++++++++++++++++++++
 net/flexray/af_flexray.h                      |  59 +++
 net/flexray/proc.c                            | 196 +++++++++
 net/flexray/raw.c                             | 446 +++++++++++++++++++++
 21 files changed, 1878 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/networking/flexray.txt
 create mode 100644 include/linux/flexray.h
 create mode 100644 include/linux/flexray/core.h
 create mode 100644 include/linux/flexray/dev.h
 create mode 100644 include/uapi/linux/flexray/Kbuild
 create mode 100644 include/uapi/linux/flexray/flexcard_netlink.h
 create mode 100644 include/uapi/linux/flexray/netlink.h
 create mode 100644 include/uapi/linux/flexray/raw.h
 create mode 100644 net/flexray/Kconfig
 create mode 100644 net/flexray/Makefile
 create mode 100644 net/flexray/af_flexray.c
 create mode 100644 net/flexray/af_flexray.h
 create mode 100644 net/flexray/proc.c
 create mode 100644 net/flexray/raw.c

diff --git a/Documentation/networking/00-INDEX b/Documentation/networking/00-INDEX
index 18b64b2..1cb180e 100644
--- a/Documentation/networking/00-INDEX
+++ b/Documentation/networking/00-INDEX
@@ -74,6 +74,8 @@ fib_trie.txt
 	- Level Compressed Trie (LC-trie) notes: a structure for routing.
 filter.txt
 	- Linux Socket Filtering
+flexray.txt
+	- FlexRay Protocol Family
 fore200e.txt
 	- FORE Systems PCA-200E/SBA-200E ATM NIC driver info.
 framerelay.txt
diff --git a/Documentation/networking/flexray.txt b/Documentation/networking/flexray.txt
new file mode 100644
index 0000000..9c0b844
--- /dev/null
+++ b/Documentation/networking/flexray.txt
@@ -0,0 +1,24 @@
+============================================================================
+
+flexray.txt
+
+Readme file for the FlexRay Protocol Family (aka Socket FlexRay)
+
+This file contains
+
+  1 Overview / What is Socket FlexRay
+
+============================================================================
+
+1 Overview / What is Socket FlexRay
+-----------------------------------
+
+The socket FlexRay package is an implementation of FlexRay protocols
+for Linux. FlexRay is a networking technology used in automotive fields as
+successor of the Controller Area Network (CAN). It is designed to be faster
+and more reliable than CAN.
+The Socket FlexRay implementation uses the Berkeley socket API, the Linux
+network stack and implements the FlexRay device drivers as network interfaces.
+The FlexRay socket API has been designed as similar as possible to the TCP/IP
+protocols to allow programmers, familiar with network programming, to easily
+learn how to use FlexRay sockets.
diff --git a/include/linux/flexray.h b/include/linux/flexray.h
new file mode 100644
index 0000000..75e07e8
--- /dev/null
+++ b/include/linux/flexray.h
@@ -0,0 +1,168 @@
+/*
+ * linux/flexray.h
+ *
+ * Definitions for FlexRay network layer (socket addr / FlexRay frame)
+ *
+ */
+
+#ifndef FLEXRAY_H
+#define FLEXRAY_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* FlexRay kernel definitions */
+
+/* special address description flags for the FLEXRAY_ID */
+#define FLEXRAY_PPI_FLAG	0x40 /* Payload preamble indicator */
+#define FLEXRAY_NFI_FLAG	0x20 /* Null frame indikator */
+#define FLEXRAY_SYN_FLAG	0x10 /* Sync frame indikator */
+#define FLEXRAY_SFI_FLAG	0x08 /* Startup frame indikator */
+
+/*
+ * FlexRay Frame structure
+ *
+ * Bit 0     reserved
+ * Bit 1     Payload preamble indicator
+ * Bit 2     Null frame indikator
+ * Bit 3     Sync frame indikator
+ * Bit 4     Startup frame indikator
+ * Bit 5-15  Frame ID
+ * Bit 16-22 Payload length
+ * Bit 23-33 Header CRC
+ * Bit 34-39 Cycle count
+ * PAYLOAD [0-254 Bytes]
+ * 24 Bit CRC
+ */
+
+/* define Flexray Header Type for Filters */
+typedef __u64 flexray_header_t;
+
+/**
+ * struct flexray_frame - basic FlexRay frame structure
+ * @head:	Header
+ * @data:       the FlexRay frame payload.
+ * @crc:	the FlexRay frame CRC
+ */
+
+struct flexray_frame {
+	__u8		head[5];
+	__u8		data[254] __aligned(8);
+	__u8		crc[3];
+};
+
+static inline int flexray_get_ppi(__u8 *head)
+{
+	return !!(head[0] & FLEXRAY_PPI_FLAG);
+}
+
+static inline void flexray_set_ppi(int ppi, __u8 *head)
+{
+	if (ppi)
+		head[0] |= FLEXRAY_PPI_FLAG;
+	else
+		head[0] &= ~FLEXRAY_PPI_FLAG;
+}
+
+static inline int flexray_get_nfi(__u8 *head)
+{
+	return !!(head[0] & FLEXRAY_NFI_FLAG);
+}
+
+static inline void flexray_set_nfi(int nfi, __u8 *head)
+{
+	if (nfi)
+		head[0] |= FLEXRAY_NFI_FLAG;
+	else
+		head[0] &= ~FLEXRAY_NFI_FLAG;
+}
+
+static inline int flexray_get_syn(__u8 *head)
+{
+	return !!(head[0] & FLEXRAY_SYN_FLAG);
+}
+
+static inline void flexray_set_syn(int syn, __u8 *head)
+{
+	if (syn)
+		head[0] |= FLEXRAY_SYN_FLAG;
+	else
+		head[0] &= ~FLEXRAY_SYN_FLAG;
+}
+
+static inline int flexray_get_sfi(__u8 *head)
+{
+	return !!(head[0] & FLEXRAY_SFI_FLAG);
+}
+
+static inline void flexray_set_sfi(int sfi, __u8 *head)
+{
+	if (sfi)
+		head[0] |= FLEXRAY_SFI_FLAG;
+	else
+		head[0] &= ~FLEXRAY_SFI_FLAG;
+}
+
+static inline __u16 flexray_get_id(__u8 *head)
+{
+	return ((head[0] & 0x3) << 8) | head[1];
+}
+
+static inline void flexray_set_id(__u16 id, __u8 *head)
+{
+	head[0] &= ~0x3;
+	head[0] |= (id >> 8) & 0x3;
+	head[1] = id & 0xff;
+}
+
+static inline __u8 flexray_get_pl(__u8 *head)
+{
+	return head[2] >> 1;
+}
+
+static inline void flexray_set_pl(__u8 pl, __u8 *head)
+{
+	head[2] &= 0x1;
+	head[2] |= pl << 1;
+}
+
+static inline __u16 flexray_get_hcrc(__u8 *head)
+{
+	return ((head[2] & 0x1) << 10) | (head[3] << 2) | (head[4] >> 6);
+}
+
+static inline void flexray_set_hcrc(__u16 hcrc, __u8 *head)
+{
+	head[2] &= 0xfe;
+	head[4] &= 0x3f;
+	head[2] |= (hcrc >> 10) & 0x1;
+	head[3] = (hcrc >> 2) & 0xff;
+	head[4] |= (hcrc & 0x3) << 6;
+}
+
+static inline __u8 flexray_get_cc(__u8 *head)
+{
+	return head[4] & 0x3f;
+}
+
+static inline void flexray_set_cc(__u8 cc, __u8 *head)
+{
+	head[4] &= ~0x3f;
+	head[4] |= cc & 0x3f;
+}
+
+/* particular protocols of the protocol family PF_FLEXRAY */
+#define FLEXRAY_RAW	1 /* RAW sockets */
+#define FLEXRAY_NPROTO	2
+
+struct sockaddr_flexray {
+	sa_family_t	flexray_family;
+	int		flexray_ifindex;
+};
+
+struct flexray_filter {
+	flexray_header_t flexray_id;
+	flexray_header_t flexray_mask;
+};
+
+#endif /* FLEXRAY_H */
diff --git a/include/linux/flexray/core.h b/include/linux/flexray/core.h
new file mode 100644
index 0000000..90a0f06
--- /dev/null
+++ b/include/linux/flexray/core.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved.
+ */
+
+#ifndef FLEXRAY_CORE_H
+#define FLEXRAY_CORE_H
+
+#include <linux/flexray.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#define DNAME(dev) ((dev) ? (dev)->name : "any")
+
+/**
+ * struct flexray_proto - FlexRay protocol structure
+ * @type:       type argument in socket() syscall, e.g. SOCK_DGRAM.
+ * @protocol:   protocol number in socket() syscall.
+ * @ops:        pointer to struct proto_ops for sock->ops.
+ * @prot:       pointer to struct proto structure.
+ */
+struct flexray_proto {
+	int type;
+	int protocol;
+	const struct proto_ops *ops;
+	struct proto *prot;
+};
+
+/* function prototypes for the FlexRay networklayer core (af_flexray.c) */
+
+extern int  flexray_proto_register(const struct flexray_proto *frp);
+extern void flexray_proto_unregister(const struct flexray_proto *frp);
+
+extern int  flexray_rx_register(struct net_device *dev,
+				void (*func)(struct sk_buff *, void *),
+				void *data, char *ident);
+
+extern void flexray_rx_unregister(struct net_device *dev,
+				  void (*func)(struct sk_buff *, void *),
+				  void *data);
+
+extern int flexray_send(struct sk_buff *skb);
+extern int flexray_ioctl(struct socket *sock, unsigned int cmd,
+			 unsigned long arg);
+
+#endif /* FLEXRAY_CORE_H */
diff --git a/include/linux/flexray/dev.h b/include/linux/flexray/dev.h
new file mode 100644
index 0000000..999ebab
--- /dev/null
+++ b/include/linux/flexray/dev.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved.
+ */
+
+#ifndef FLEXRAY_DEV_H
+#define FLEXRAY_DEV_H
+
+#include <linux/flexray.h>
+#include <linux/flexray/netlink.h>
+
+/* FlexRay common private data */
+
+struct flexray_priv {
+	struct flexray_device_stats flexray_stats;
+
+	enum flexray_state state;
+
+	int (*do_get_state)(const struct net_device *dev,
+			    enum flexray_state *state);
+	int (*do_set_state)(struct net_device *dev,
+			    enum flexray_state state);
+	int (*do_validate)(struct sk_buff *skb);
+
+	struct flexray_cluster_param cluster;
+	struct flexray_node_param node;
+	struct flexray_symbol_param symbol;
+	__u32 sw_filter[FLEXRAY_MAX_SW_FILTER];
+	u8 version;
+};
+
+/* Drop a given socketbuffer if it does not contain a valid FlexRay frame. */
+static inline int flexray_dropped_invalid_skb(struct net_device *dev,
+					      struct sk_buff *skb)
+{
+	const struct flexray_frame *frf = (struct flexray_frame *)skb->data;
+
+	if (unlikely(skb->len != sizeof(*frf))) {
+		kfree_skb(skb);
+		dev->stats.tx_dropped++;
+		return 1;
+	}
+
+	return 0;
+}
+
+struct net_device *alloc_flexraydev(int sizeof_priv, u8 version);
+void free_flexraydev(struct net_device *dev);
+
+int open_flexraydev(struct net_device *dev);
+void close_flexraydev(struct net_device *dev);
+
+int register_flexraydev(struct net_device *dev);
+void unregister_flexraydev(struct net_device *dev);
+
+int flexray_restart_now(struct net_device *dev);
+
+struct sk_buff *alloc_flexray_skb(struct net_device *dev,
+				  struct flexray_frame **cf);
+
+void print_cluster_config(struct flexray_cluster_param *cp, int is_v3);
+void print_node_config(struct flexray_node_param *np, int is_v3);
+void print_symbol_config(struct flexray_symbol_param *sp);
+
+#endif /* FLEXRAY_DEV_H */
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 230c04b..a7c316b 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -180,7 +180,8 @@ struct ucred {
 #define AF_ALG		38	/* Algorithm sockets		*/
 #define AF_NFC		39	/* NFC sockets			*/
 #define AF_VSOCK	40	/* vSockets			*/
-#define AF_MAX		41	/* For now.. */
+#define AF_FLEXRAY	41	/* Flexray			*/
+#define AF_MAX		42	/* For now.. */
 
 /* Protocol families, same as address families. */
 #define PF_UNSPEC	AF_UNSPEC
@@ -225,6 +226,7 @@ struct ucred {
 #define PF_ALG		AF_ALG
 #define PF_NFC		AF_NFC
 #define PF_VSOCK	AF_VSOCK
+#define PF_FLEXRAY	AF_FLEXRAY
 #define PF_MAX		AF_MAX
 
 /* Maximum queue length specifiable by listen.  */
diff --git a/include/uapi/linux/flexray/Kbuild b/include/uapi/linux/flexray/Kbuild
new file mode 100644
index 0000000..8c45658
--- /dev/null
+++ b/include/uapi/linux/flexray/Kbuild
@@ -0,0 +1,4 @@
+header-y += error.h
+header-y += netlink.h
+header-y += raw.h
+header-y += flexcard_netlink.h
diff --git a/include/uapi/linux/flexray/flexcard_netlink.h b/include/uapi/linux/flexray/flexcard_netlink.h
new file mode 100644
index 0000000..d04e2dd
--- /dev/null
+++ b/include/uapi/linux/flexray/flexcard_netlink.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved.
+ */
+
+#ifndef _FLEXCARD_NETLINK_H
+#define _FLEXCARD_NETLINK_H
+
+#include <linux/types.h>
+
+#define FC_MSGBUF_VERSION	1
+
+/* Start Flexcard flags beyond ERAY flags */
+#define FC_MSGBUF_ACK		0x00010000
+#define FC_MSGBUF_ACK_NULL	0x00020000
+#define FC_MSGBUF_ACK_PAYLOAD	0x00040000
+#define FC_MSGBUF_SELFSYNC	0x00080000
+
+struct fc_msgbuf_cfg {
+	__u32 flags;
+	__u8 buf_id;
+	__u8 cyc;
+	__u16 len;
+	__u16 max;
+	__u16 start;
+	__u32 frame_id;
+	__u32 reject_mask;
+	enum eray_msgbuf_type type;
+	enum eray_msgbuf_channel channel;
+} __packed;
+
+/* commands */
+enum fc_msgbuf_cmd {
+	FC_MSGBUF_CMD_UNSPEC,
+	FC_MSGBUF_CMD_GET_CFG,
+	FC_MSGBUF_CMD_SET_CFG,
+	FC_MSGBUF_CMD_RESET_CFG,
+	FC_MSGBUF_CMD_READ_CFG,
+	__FC_MSGBUF_CMD_MAX,
+};
+#define FC_MSGBUF_CMD_MAX (__FC_MSGBUF_CMD_MAX - 1)
+
+/* attributes */
+enum fc_msgbuf_attr {
+	FC_MSGBUF_ATTR_UNSPEC,
+	FC_MSGBUF_ATTR_BUF_ID,
+	FC_MSGBUF_ATTR_DEV_ID,
+	FC_MSGBUF_ATTR_DEV_NAME,
+	FC_MSGBUF_ATTR_CFG,
+	__FC_MSGBUF_ATTR_MAX,
+};
+#define FC_MSGBUF_ATTR_MAX (__FC_MSGBUF_ATTR_MAX - 1)
+
+#endif /* _FLEXCARD_NETLINK_H */
diff --git a/include/uapi/linux/flexray/netlink.h b/include/uapi/linux/flexray/netlink.h
new file mode 100644
index 0000000..3d1dbaf
--- /dev/null
+++ b/include/uapi/linux/flexray/netlink.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved.
+ */
+
+#ifndef FLEXRAY_NETLINK_H
+#define FLEXRAY_NETLINK_H
+
+#include <linux/types.h>
+
+#define FLEXRAY_MAX_SW_FILTER	2048
+
+/*
+ * FlexRay operational and error states
+ */
+enum flexray_state {
+	FLEXRAY_STATE_UNSPEC,
+	FLEXRAY_STATE_DEFAULT_CONFIG,
+	FLEXRAY_STATE_CONFIG,
+	FLEXRAY_STATE_READY,
+	FLEXRAY_STATE_WAKEUP,
+	FLEXRAY_STATE_STARTUP,
+	FLEXRAY_STATE_NORMAL_ACTIVE,
+	FLEXRAY_STATE_NORMAL_PASSIVE,
+	FLEXRAY_STATE_HALT,
+	FLEXRAY_STATE_MONITOR_MODE,
+	FLEXRAY_STATE_COLDSTART,
+	FLEXRAY_STATE_MAX
+};
+
+/*
+ * FlexRay device statistics
+ */
+struct flexray_device_stats {
+	__u32 txack;
+	__u32 manage;
+	__u32 info;
+	__u32 status;
+	__u32 notify;
+};
+
+/*
+ * FlexRay netlink interface
+ */
+enum {
+	IFLA_FLEXRAY_UNSPEC,
+	IFLA_FLEXRAY_STATE,
+	IFLA_FLEXRAY_VERSION,
+	IFLA_FLEXRAY_CLUSTER,
+	IFLA_FLEXRAY_NODE,
+	IFLA_FLEXRAY_SYMBOL,
+	IFLA_FLEXRAY_SW_FILTER,
+	__IFLA_FLEXRAY_MAX
+};
+
+#define IFLA_FLEXRAY_MAX    (__IFLA_FLEXRAY_MAX - 1)
+
+/*
+ * FlexRay filter netlink interface
+ */
+enum {
+	IFLA_FLEXRAY_FILTER_UNSPEC,
+	IFLA_FLEXRAY_FILTER_ENTRY,
+	__IFLA_FLEXRAY_FILTER_MAX
+};
+
+#define IFLA_FLEXRAY_FILTER_MAX    (__IFLA_FLEXRAY_FILTER_MAX - 1)
+
+struct cluster_v2 {
+	__u16 gAssumedPrecision;
+	__u8 gdMaxInitializationError;
+	__u8 gdMaxMicrotick;
+	__u8 gdMaxPropagationDelay;
+	__u8 gdMinPropagationDelay;
+	__u8 gdWakeupSymbolRxIdle;
+	__u8 gdWakeupSymbolRxLow;
+	__u16 gdWakeupSymbolRxWindow;
+	__u8 gdWakeupSymbolTxIdle;
+	__u8 gdWakeupSymbolTxLow;
+	__u32 gOffsetCorrectionMax;
+	__u16 gOffsetCorrectionStart;
+	__u8 gSyncNodeMax;
+};
+
+struct cluster_v3 {
+	__u16 gClockDeviationMax;
+	__u8 gCycleCountMax;
+	__u8 gdIgnoreAfterTx;
+	__u8 gdSymbolWindowActionPointOffset;
+	__u8 gdWakeupRxIdle;
+	__u8 gdWakeupRxLow;
+	__u16 gdWakeupRxWindow;
+	__u8 gdWakeupTxActive;
+	__u8 gdWakeupTxIdle;
+	__u8 gExternOffsetCorrection;
+	__u8 gExternRateCorrection;
+	__u8 gSyncFrameIDCountMax;
+};
+
+struct flexray_cluster_param {
+	/* Protocol relevant */
+	__u8 gColdstartAttempts;
+	__u8 gdActionPointOffset;
+	__u8 gdCASRxLowMax;
+	__u8 gdDynamicSlotIdlePhase;
+	__u8 gdMinislot;
+	__u8 gdMinislotActionPointOffset;
+	__u16 gdStaticSlot;
+	__u8 gdSymbolWindow;
+	__u8 gdTSSTransmitter;
+	__u8 gListenNoise;
+	__u16 gMacroPerCycle;
+	__u8 gMaxWithoutClockCorrectionFatal;
+	__u8 gMaxWithoutClockCorrectionPassive;
+	__u16 gNumberOfMinislots;
+	__u16 gNumberOfStaticSlots;
+	__u8 gPayloadLengthStatic;
+
+	/* Protocol related */
+	__u8 gChannels;
+	__u8 gClusterDriftDamping;
+	__u8 gdBit;
+	__u16 gdCycle;
+	__u8 gdMacrotick;
+	__u16 gdNIT;
+	__u8 gdSampleClockPeriod;
+	__u8 gNetworkManagementVectorLength;
+	union {
+		struct cluster_v2 v2;
+		struct cluster_v3 v3;
+	};
+};
+
+struct node_v2 {
+	__u16 pdMaxDrift;
+	__u8 pMicroPerMacroNom;
+	__u8 pSingleSlotEnabled;
+};
+
+struct node_v3 {
+	__u8 pExternalSync;
+	__u8 pFallBackInternal;
+	__u8 pKeySlotOnlyEnabled;
+	__u8 pNMVectorEarlyUpdate;
+	__u8 pOffsetCorrectionStart;
+	__u8 pSecondKeySlotID;
+	__u8 pTwoKeySlotMode;
+};
+
+struct flexray_node_param {
+	/* Protocol relevant */
+	__u8 pAllowHaltDueToClock;
+	__u8 pAllowPassiveToActive;
+	__u8 pChannels;
+	__u8 pClusterDriftDamping;
+	__u16 pdAcceptedStartupRange;
+	__u8 pDecodingCorrection;
+	__u8 pDelayCompensationA;
+	__u8 pDelayCompensationB;
+	__u32 pdListenTimeout;
+	__s8 vExternOffsetControl;
+	__s8 vExternRateControl;
+	__u8 pExternOffsetCorrection;
+	__u8 pExternRateCorrection;
+	__u16 pKeySlotID;
+	__u8 pKeySlotUsedForStartup;
+	__u8 pKeySlotUsedForSync;
+	__u16 pLatestTx;
+	__u8 pMacroInitialOffsetA;
+	__u8 pMacroInitialOffsetB;
+	__u16 pMicroInitialOffsetA;
+	__u16 pMicroInitialOffsetB;
+	__u32 pMicroPerCycle;
+	__u16 pOffsetCorrectionOut;
+	__u16 pRateCorrectionOut;
+	__u8 pWakeupChannel;
+	__u8 pWakeupPattern;
+
+	/* Protocol related */
+	__u8 pdMicrotick;
+	__u8 pPayloadLengthDynMax;
+	__u8 pSamplesPerMicrotick;
+
+	union {
+		struct node_v2 v2;
+		struct node_v3 v3;
+	};
+};
+
+struct flexray_symbol_param {
+	/* Protocol related */
+	__u8 pChannelsMTS;
+};
+
+/*
+ * FlexRay software ID filter
+ */
+
+struct flexray_sw_filter {
+	__u32 pos;
+	__u32 id;
+};
+
+#endif /* FLEXRAY_NETLINK_H */
diff --git a/include/uapi/linux/flexray/raw.h b/include/uapi/linux/flexray/raw.h
new file mode 100644
index 0000000..0b12068
--- /dev/null
+++ b/include/uapi/linux/flexray/raw.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved.
+ */
+
+#ifndef FLEXRAY_RAW_H
+#define FLEXRAY_RAW_H
+
+#include <linux/flexray.h>
+
+/* for socket options affecting the socket (not the global system) */
+
+enum {
+	FLEXRAY_RAW_FILTER = 1,     /* set 0 .. n can_filter(s) */
+};
+
+#endif
diff --git a/include/uapi/linux/if_arp.h b/include/uapi/linux/if_arp.h
index d7fea34..d64971f 100644
--- a/include/uapi/linux/if_arp.h
+++ b/include/uapi/linux/if_arp.h
@@ -53,6 +53,7 @@
 #define ARPHRD_X25	271		/* CCITT X.25			*/
 #define ARPHRD_HWX25	272		/* Boards with X.25 in firmware	*/
 #define ARPHRD_CAN	280		/* Controller Area Network      */
+#define ARPHRD_FLEXRAY	290		/* FlexRay			*/
 #define ARPHRD_PPP	512
 #define ARPHRD_CISCO	513		/* Cisco HDLC	 		*/
 #define ARPHRD_HDLC	ARPHRD_CISCO
diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h
index ade07f1..05e886e 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/uapi/linux/if_ether.h
@@ -112,6 +112,7 @@
 #define ETH_P_LOCALTALK 0x0009		/* Localtalk pseudo type 	*/
 #define ETH_P_CAN	0x000C		/* CAN: Controller Area Network */
 #define ETH_P_CANFD	0x000D		/* CANFD: CAN flexible data rate*/
+#define ETH_P_FLEXRAY	0x000F		/* Flexray			*/
 #define ETH_P_PPPTALK	0x0010		/* Dummy type for Atalk over PPP*/
 #define ETH_P_TR_802_2	0x0011		/* 802.2 frames 		*/
 #define ETH_P_MOBITEX	0x0015		/* Mobitex (kaz@cafe.net)	*/
diff --git a/net/Kconfig b/net/Kconfig
index ee02136..8b7e604 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -328,6 +328,7 @@ endmenu
 
 source "net/ax25/Kconfig"
 source "net/can/Kconfig"
+source "net/flexray/Kconfig"
 source "net/irda/Kconfig"
 source "net/bluetooth/Kconfig"
 source "net/rxrpc/Kconfig"
diff --git a/net/Makefile b/net/Makefile
index 9492e8c..8d03e63 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_NETROM)		+= netrom/
 obj-$(CONFIG_ROSE)		+= rose/
 obj-$(CONFIG_AX25)		+= ax25/
 obj-$(CONFIG_CAN)		+= can/
+obj-$(CONFIG_FLEXRAY)		+= flexray/
 obj-$(CONFIG_IRDA)		+= irda/
 obj-$(CONFIG_BT)		+= bluetooth/
 obj-$(CONFIG_SUNRPC)		+= sunrpc/
diff --git a/net/core/sock.c b/net/core/sock.c
index 83667de..820e44e 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -213,7 +213,8 @@ static const char *const af_family_key_strings[AF_MAX+1] = {
   "sk_lock-AF_TIPC"  , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV"        ,
   "sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN"     , "sk_lock-AF_PHONET"   ,
   "sk_lock-AF_IEEE802154", "sk_lock-AF_CAIF" , "sk_lock-AF_ALG"      ,
-  "sk_lock-AF_NFC"   , "sk_lock-AF_VSOCK"    , "sk_lock-AF_MAX"
+  "sk_lock-AF_NFC"   , "sk_lock-AF_VSOCK"    , "sk_lock-AF_FLEXRAY"  ,
+  "sk_lock-AF_MAX"
 };
 static const char *const af_family_slock_key_strings[AF_MAX+1] = {
   "slock-AF_UNSPEC", "slock-AF_UNIX"     , "slock-AF_INET"     ,
@@ -229,7 +230,8 @@ static const char *const af_family_slock_key_strings[AF_MAX+1] = {
   "slock-AF_TIPC"  , "slock-AF_BLUETOOTH", "slock-AF_IUCV"     ,
   "slock-AF_RXRPC" , "slock-AF_ISDN"     , "slock-AF_PHONET"   ,
   "slock-AF_IEEE802154", "slock-AF_CAIF" , "slock-AF_ALG"      ,
-  "slock-AF_NFC"   , "slock-AF_VSOCK"    ,"slock-AF_MAX"
+  "slock-AF_NFC"   , "slock-AF_VSOCK"    , "slock-AF_FLEXRAY"  ,
+  "slock-AF_MAX"
 };
 static const char *const af_family_clock_key_strings[AF_MAX+1] = {
   "clock-AF_UNSPEC", "clock-AF_UNIX"     , "clock-AF_INET"     ,
@@ -245,7 +247,8 @@ static const char *const af_family_clock_key_strings[AF_MAX+1] = {
   "clock-AF_TIPC"  , "clock-AF_BLUETOOTH", "clock-AF_IUCV"     ,
   "clock-AF_RXRPC" , "clock-AF_ISDN"     , "clock-AF_PHONET"   ,
   "clock-AF_IEEE802154", "clock-AF_CAIF" , "clock-AF_ALG"      ,
-  "clock-AF_NFC"   , "clock-AF_VSOCK"    , "clock-AF_MAX"
+  "clock-AF_NFC"   , "clock-AF_VSOCK"    , "clock-AF_FLEXRAY"  ,
+  "clock-AF_MAX"
 };
 
 /*
diff --git a/net/flexray/Kconfig b/net/flexray/Kconfig
new file mode 100644
index 0000000..18d9d69
--- /dev/null
+++ b/net/flexray/Kconfig
@@ -0,0 +1,28 @@
+#
+# FlexRay network layer core configuration
+#
+
+menuconfig FLEXRAY
+	depends on NET
+	tristate "FlexRay bus subsystem support"
+	---help---
+	  FlexRay is a slow (up to 10Mbit/s) serial communications
+	  protocol which was developed by the FlexRay Consortium, a
+	  cooperation of leading companies in the automotive industry, from
+	  2000 till 2010, which result in ISO 10681.
+	  More information on the FlexRay network protocol family PF_FLEXRAY
+	  is contained in <Documentation/networking/flexray.txt>.
+
+	  If you want FLEXRAY support you should say Y here and also to the
+	  specific driver for your controller(s) below.
+
+config FLEXRAY_RAW
+	tristate "Raw FlexRay Protocol"
+	depends on FLEXRAY
+	default N
+	---help---
+	  The raw FlexRay protocol option offers access to the FlexRay bus
+	  via the BSD socket API. You probably want to use the raw socket in
+	  most cases where no higher level protocol is being used.
+	  To receive/send raw FLEXRAY messages, use AF_FLEXRAY with protocol
+	  FLEXRAY_RAW.
diff --git a/net/flexray/Makefile b/net/flexray/Makefile
new file mode 100644
index 0000000..f5e05b6c
--- /dev/null
+++ b/net/flexray/Makefile
@@ -0,0 +1,9 @@
+#
+#  Makefile for the Linux FlexRay core.
+#
+
+obj-$(CONFIG_FLEXRAY)		+= flexray.o
+flexray-y			:= af_flexray.o proc.o
+
+obj-$(CONFIG_FLEXRAY_RAW)	+= flexray-raw.o
+flexray-raw-y			:= raw.o
diff --git a/net/flexray/af_flexray.c b/net/flexray/af_flexray.c
new file mode 100644
index 0000000..e5fd1c4
--- /dev/null
+++ b/net/flexray/af_flexray.c
@@ -0,0 +1,548 @@
+/* Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/rcupdate.h>
+#include <linux/uaccess.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/socket.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/flexray.h>
+#include <linux/flexray/core.h>
+#include <linux/flexray/dev.h>
+#include <linux/ratelimit.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "af_flexray.h"
+
+/* receive filters subscribed for 'all' FlexRay devices */
+struct dev_rcv_lists flexray_rx_alldev_list;
+static DEFINE_SPINLOCK(flexray_rcvlists_lock);
+
+static struct kmem_cache *rcv_cache __read_mostly;
+
+/* table of registered FlexRay protocols */
+static const struct flexray_proto *proto_tab[FLEXRAY_NPROTO] __read_mostly;
+static DEFINE_MUTEX(proto_tab_lock);
+
+struct s_stats	flexray_stats;	/* packet statistics */
+struct s_pstats	flexray_pstats;	/* receive list statistics */
+
+/* af_flexray socket functions */
+
+int flexray_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct sock *sk = sock->sk;
+
+	switch (cmd) {
+
+	case SIOCGSTAMP:
+		return sock_get_timestamp(sk, (struct timeval __user *)arg);
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+EXPORT_SYMBOL(flexray_ioctl);
+
+static void flexray_sock_destruct(struct sock *sk)
+{
+	skb_queue_purge(&sk->sk_receive_queue);
+}
+
+static const struct flexray_proto *flexray_get_proto(int protocol)
+{
+	const struct flexray_proto *frp;
+
+	rcu_read_lock();
+	frp = rcu_dereference(proto_tab[protocol]);
+	if (frp && !try_module_get(frp->prot->owner))
+		frp = NULL;
+	rcu_read_unlock();
+
+	return frp;
+}
+
+static inline void flexray_put_proto(const struct flexray_proto *frp)
+{
+	module_put(frp->prot->owner);
+}
+
+static int flexray_create(struct net *net, struct socket *sock, int protocol,
+			  int kern)
+{
+	struct sock *sk;
+	const struct flexray_proto *frp;
+	int err = 0;
+
+	sock->state = SS_UNCONNECTED;
+
+	if (protocol < 0 || protocol >= FLEXRAY_NPROTO)
+		return -EINVAL;
+
+	if (!net_eq(net, &init_net))
+		return -EAFNOSUPPORT;
+
+	frp = flexray_get_proto(protocol);
+
+#ifdef CONFIG_MODULES
+	if (!frp) {
+		request_module("flexray-proto-%d", protocol);
+		frp = flexray_get_proto(protocol);
+	}
+#endif
+	/* check for available protocol and correct usage */
+	if (!frp)
+		return -EPROTONOSUPPORT;
+
+	if (frp->type != sock->type) {
+		err = -EPROTOTYPE;
+		goto errout;
+	}
+
+	sock->ops = frp->ops;
+
+	sk = sk_alloc(net, PF_FLEXRAY, GFP_KERNEL, frp->prot);
+	if (!sk) {
+		err = -ENOMEM;
+		goto errout;
+	}
+
+	sock_init_data(sock, sk);
+	sk->sk_destruct = flexray_sock_destruct;
+
+	if (sk->sk_prot->init)
+		err = sk->sk_prot->init(sk);
+
+	if (err) {
+		/* release sk on errors */
+		sock_orphan(sk);
+		sock_put(sk);
+	}
+
+errout:
+	flexray_put_proto(frp);
+	return err;
+}
+
+/**
+ * flexray_send - transmit a FlexRay frame
+ * @skb: pointer to socket buffer with FlexRay frame in data section
+ *
+ * Return:
+ *  0 on success
+ *  -ENETDOWN when the selected interface is down
+ *  -ENOBUFS on full driver queue (see net_xmit_errno())
+ *  -EPERM when trying to send on a non-FlexRay interface
+ *  -EINVAL when the skb->data does not contain a valid FlexRay frame
+ *  The skb is always consumed.
+ */
+int flexray_send(struct sk_buff *skb)
+{
+	struct flexray_priv *priv;
+	int err;
+
+	priv = netdev_priv(skb->dev);
+
+	if (skb->dev->type != ARPHRD_FLEXRAY) {
+		err = -EPERM;
+		goto err_skb;
+	}
+
+	if (!(skb->dev->flags & IFF_UP)) {
+		err = -ENETDOWN;
+		goto err_skb;
+	}
+
+	if (!priv->do_validate) {
+		err = -EINVAL;
+		goto err_skb;
+	}
+
+	err = priv->do_validate(skb);
+	if (err)
+		goto err_skb;
+
+	skb->protocol = htons(ETH_P_FLEXRAY);
+	skb_reset_network_header(skb);
+	skb_reset_transport_header(skb);
+
+	skb->pkt_type = PACKET_HOST;
+
+	/* send to netdevice */
+	err = dev_queue_xmit(skb);
+	if (err > 0) {
+		err = net_xmit_errno(err);
+		return err;
+	}
+
+	/* update statistics */
+	flexray_stats.tx_frames++;
+
+	return 0;
+err_skb:
+	kfree_skb(skb);
+	return err;
+}
+EXPORT_SYMBOL(flexray_send);
+
+/* af_flexray rx path */
+
+static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
+{
+	if (!dev)
+		return &flexray_rx_alldev_list;
+	else
+		return (struct dev_rcv_lists *)dev->ml_priv;
+}
+
+int flexray_rx_register(struct net_device *dev,
+			void (*func)(struct sk_buff *, void *),
+			void *data, char *ident)
+{
+	struct receiver *r;
+	struct dev_rcv_lists *d;
+	int err = 0;
+
+	if (dev && dev->type != ARPHRD_FLEXRAY)
+		return -ENODEV;
+
+	r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
+	if (!r)
+		return -ENOMEM;
+
+	spin_lock(&flexray_rcvlists_lock);
+
+	d = find_dev_rcv_lists(dev);
+	if (d) {
+		r->func		= func;
+		r->data		= data;
+		r->ident	= ident;
+
+		hlist_add_head_rcu(&r->list, &d->rx[RX_ALL]);
+		d->entries++;
+
+		flexray_pstats.rcv_entries++;
+		if (flexray_pstats.rcv_entries_max < flexray_pstats.rcv_entries)
+			flexray_pstats.rcv_entries_max =
+				flexray_pstats.rcv_entries;
+	} else {
+		kmem_cache_free(rcv_cache, r);
+		err = -ENODEV;
+	}
+
+	spin_unlock(&flexray_rcvlists_lock);
+
+	return err;
+}
+EXPORT_SYMBOL(flexray_rx_register);
+
+static void flexray_rx_delete_receiver(struct rcu_head *rp)
+{
+	struct receiver *r = container_of(rp, struct receiver, rcu);
+
+	kmem_cache_free(rcv_cache, r);
+}
+
+void flexray_rx_unregister(struct net_device *dev,
+			   void (*func)(struct sk_buff *, void *), void *data)
+{
+	struct receiver *r = NULL;
+	struct hlist_head *rl;
+	struct dev_rcv_lists *d;
+	bool found = false;
+
+	if (dev && dev->type != ARPHRD_FLEXRAY)
+		return;
+
+	spin_lock(&flexray_rcvlists_lock);
+
+	d = find_dev_rcv_lists(dev);
+	if (!d) {
+		netdev_err(dev, "BUG: receive list not found\n");
+		goto out;
+	}
+
+	rl = &d->rx[RX_ALL];
+
+	/* Search the receiver list for the item to delete. This should
+	 * exist, since no receiver may be unregistered that hasn't
+	 * been registered before.
+	 */
+
+	hlist_for_each_entry_rcu(r, rl, list) {
+		if (r->func == func && r->data == data) {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found) {
+		netdev_err(dev, "BUG: receive list entry not found\n");
+		r = NULL;
+		goto out;
+	}
+
+	hlist_del_rcu(&r->list);
+	d->entries--;
+
+	if (flexray_pstats.rcv_entries > 0)
+		flexray_pstats.rcv_entries--;
+
+	/* remove device structure requested by NETDEV_UNREGISTER */
+	if (d->remove_on_zero_entries && !d->entries) {
+		kfree(d);
+		dev->ml_priv = NULL;
+	}
+
+out:
+	spin_unlock(&flexray_rcvlists_lock);
+
+	/* schedule the receiver item for deletion */
+	if (r)
+		call_rcu(&r->rcu, flexray_rx_delete_receiver);
+}
+EXPORT_SYMBOL(flexray_rx_unregister);
+
+static inline void deliver(struct sk_buff *skb, struct receiver *r)
+{
+	r->func(skb, r->data);
+}
+
+static int flexray_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
+{
+	struct receiver *r;
+	int matches = 0;
+
+	if (d->entries == 0)
+		return 0;
+
+	/* check for unfiltered entries */
+	hlist_for_each_entry_rcu(r, &d->rx[RX_ALL], list) {
+		deliver(skb, r);
+		matches++;
+	}
+
+	return matches;
+}
+
+static int flexray_rcv(struct sk_buff *skb, struct net_device *dev,
+		       struct packet_type *pt, struct net_device *orig_dev)
+{
+	struct dev_rcv_lists *d;
+	struct flexray_frame *frf = (struct flexray_frame *)skb->data;
+	int matches;
+
+	if (!net_eq(dev_net(dev), &init_net))
+		goto drop;
+
+	if (WARN_ONCE(dev->type != ARPHRD_FLEXRAY,
+		      "PF_FLEXRAY: dropped non conform skbuf: "
+		      "dev type %d, len %d, payload %d\n",
+		      dev->type, skb->len, flexray_get_pl(frf->head)))
+		goto drop;
+
+	/* update statistics */
+	flexray_stats.rx_frames++;
+
+	rcu_read_lock();
+
+	/* deliver the packet to sockets listening on all devices */
+	matches = flexray_rcv_filter(&flexray_rx_alldev_list, skb);
+
+	/* find receive list for this device */
+	d = find_dev_rcv_lists(dev);
+	if (d)
+		matches += flexray_rcv_filter(d, skb);
+
+	rcu_read_unlock();
+
+	/* consume the skbuff allocated by the netdevice driver */
+	consume_skb(skb);
+
+	if (matches > 0)
+		flexray_stats.matches++;
+
+	return NET_RX_SUCCESS;
+
+drop:
+	kfree_skb(skb);
+	return NET_RX_DROP;
+}
+
+/* af_flexray protocol functions */
+int flexray_proto_register(const struct flexray_proto *frp)
+{
+	int proto = frp->protocol;
+	int err = 0;
+
+	if (proto < 0 || proto >= FLEXRAY_NPROTO) {
+		pr_err("flexray: protocol number %d out of range\n", proto);
+		return -EINVAL;
+	}
+
+	err = proto_register(frp->prot, 0);
+	if (err < 0)
+		return err;
+
+	mutex_lock(&proto_tab_lock);
+
+	if (proto_tab[proto]) {
+		pr_err("flexray: protocol %d already registered\n",
+		       proto);
+		err = -EBUSY;
+	} else
+		rcu_assign_pointer(proto_tab[proto], frp);
+
+	mutex_unlock(&proto_tab_lock);
+
+	if (err < 0)
+		proto_unregister(frp->prot);
+
+	return err;
+}
+EXPORT_SYMBOL(flexray_proto_register);
+
+void flexray_proto_unregister(const struct flexray_proto *frp)
+{
+	int proto = frp->protocol;
+
+	mutex_lock(&proto_tab_lock);
+	BUG_ON(proto_tab[proto] != frp);
+	rcu_assign_pointer(proto_tab[proto], NULL);
+	mutex_unlock(&proto_tab_lock);
+
+	synchronize_rcu();
+
+	proto_unregister(frp->prot);
+}
+EXPORT_SYMBOL(flexray_proto_unregister);
+
+/* af_flexray notifier to create/remove FlexRay netdevice specific structs */
+static int flexray_notifier(struct notifier_block *nb, unsigned long msg,
+			    void *data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct dev_rcv_lists *d;
+
+	if (!net_eq(dev_net(dev), &init_net))
+		return NOTIFY_DONE;
+
+	if (dev->type != ARPHRD_FLEXRAY)
+		return NOTIFY_DONE;
+
+	switch (msg) {
+
+	case NETDEV_REGISTER:
+		/* create new dev_rcv_lists for this device */
+		d = kzalloc(sizeof(*d), GFP_KERNEL);
+		if (!d) {
+			netdev_err(dev, "allocation of receive list failed\n");
+			return NOTIFY_DONE;
+		}
+		BUG_ON(dev->ml_priv);
+		dev->ml_priv = d;
+
+		break;
+
+	case NETDEV_UNREGISTER:
+		spin_lock(&flexray_rcvlists_lock);
+
+		d = dev->ml_priv;
+		if (d) {
+			if (d->entries)
+				d->remove_on_zero_entries = 1;
+			else {
+				kfree(d);
+				dev->ml_priv = NULL;
+			}
+		} else
+			netdev_err(dev, "notifier: receive list not found\n");
+
+		spin_unlock(&flexray_rcvlists_lock);
+		break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+/* af_flexray module init/exit functions */
+
+static struct packet_type flexray_packet __read_mostly = {
+	.type = cpu_to_be16(ETH_P_FLEXRAY),
+	.func = flexray_rcv,
+};
+
+static const struct net_proto_family flexray_family_ops = {
+	.family = PF_FLEXRAY,
+	.create = flexray_create,
+	.owner  = THIS_MODULE,
+};
+
+/* notifier block for netdevice event */
+static struct notifier_block flexray_netdev_notifier __read_mostly = {
+	.notifier_call = flexray_notifier,
+};
+
+static __init int flexray_init(void)
+{
+	memset(&flexray_rx_alldev_list, 0, sizeof(flexray_rx_alldev_list));
+
+	rcv_cache = kmem_cache_create("flexray_receiver",
+				      sizeof(struct receiver), 0, 0, NULL);
+	if (!rcv_cache)
+		return -ENOMEM;
+
+	flexray_init_proc();
+
+	/* protocol register */
+	sock_register(&flexray_family_ops);
+	register_netdevice_notifier(&flexray_netdev_notifier);
+	dev_add_pack(&flexray_packet);
+	return 0;
+}
+module_init(flexray_init);
+
+static __exit void flexray_exit(void)
+{
+	struct net_device *dev;
+
+	flexray_remove_proc();
+
+	/* protocol unregister */
+	dev_remove_pack(&flexray_packet);
+	unregister_netdevice_notifier(&flexray_netdev_notifier);
+	sock_unregister(PF_FLEXRAY);
+
+	/* remove created dev_rcv_lists from still registered FlexRay devices */
+	rcu_read_lock();
+	for_each_netdev_rcu(&init_net, dev) {
+		if (dev->type == ARPHRD_FLEXRAY && dev->ml_priv) {
+			struct dev_rcv_lists *d = dev->ml_priv;
+
+			BUG_ON(d->entries);
+			kfree(d);
+			dev->ml_priv = NULL;
+		}
+	}
+	rcu_read_unlock();
+
+	rcu_barrier(); /* Wait for completion of call_rcu()'s */
+
+	kmem_cache_destroy(rcv_cache);
+}
+module_exit(flexray_exit);
+
+MODULE_DESCRIPTION("FlexRay PF_FLEXRAY core");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Benedikt Spranger <b.spranger@linutronix.de>");
+
+MODULE_ALIAS_NETPROTO(PF_FLEXRAY);
diff --git a/net/flexray/af_flexray.h b/net/flexray/af_flexray.h
new file mode 100644
index 0000000..5aec894
--- /dev/null
+++ b/net/flexray/af_flexray.h
@@ -0,0 +1,59 @@
+/* Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved */
+
+#ifndef AF_FLEXRAY_H
+#define AF_FLEXRAY_H
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/list.h>
+#include <linux/rcupdate.h>
+#include <linux/flexray.h>
+
+/* af_flexray rx dispatcher structures */
+
+struct receiver {
+	struct hlist_node list;
+	struct rcu_head rcu;
+	void (*func)(struct sk_buff *, void *);
+	void *data;
+	char *ident;
+};
+
+enum {RX_ALL, RX_MAX};
+
+/* per device receive filters linked at dev->ml_priv */
+struct dev_rcv_lists {
+	struct hlist_head rx[RX_MAX];
+	struct hlist_head rx_sff[0x800];
+	int remove_on_zero_entries;
+	int entries;
+};
+
+/* can be reset e.g. by flexray_init_stats() */
+struct s_stats {
+	unsigned long rx_frames;
+	unsigned long tx_frames;
+	unsigned long matches;
+};
+
+/* persistent statistics */
+struct s_pstats {
+	unsigned long stats_reset;
+	unsigned long user_reset;
+	unsigned long rcv_entries;
+	unsigned long rcv_entries_max;
+};
+
+/* function prototypes for the FlexRay networklayer procfs (proc.c) */
+extern void flexray_init_proc(void);
+extern void flexray_remove_proc(void);
+extern void flexray_stat_update(unsigned long data);
+
+/* structures and variables from af_flexray.c needed in proc.c for reading */
+extern struct s_stats	flexray_stats;		/* packet statistics */
+extern struct s_pstats	flexray_pstats;		/* receive list statistics */
+extern struct hlist_head flexray_rx_dev_list;	/* rx dispatcher structures */
+/* receive filters subscribed for 'all' FlexRay devices */
+extern struct dev_rcv_lists flexray_rx_alldev_list;
+
+#endif /* AF_FLEXRAY_H */
diff --git a/net/flexray/proc.c b/net/flexray/proc.c
new file mode 100644
index 0000000..e82f844
--- /dev/null
+++ b/net/flexray/proc.c
@@ -0,0 +1,196 @@
+/* Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <linux/rcupdate.h>
+#include <linux/if_arp.h>
+#include <linux/flexray/core.h>
+
+#include "af_flexray.h"
+
+/* proc filenames for the PF_FLEXRAY core */
+
+#define FLEXRAY_PROC_STATS       "stats"
+#define FLEXRAY_PROC_RESET_STATS "reset_stats"
+#define FLEXRAY_PROC_RCVLIST_ALL "rcvlist_all"
+#define FLEXRAY_PROC_RCVLIST_FIL "rcvlist_fil"
+
+static struct proc_dir_entry *flexray_dir;
+static struct proc_dir_entry *pde_stats;
+static struct proc_dir_entry *pde_reset_stats;
+static struct proc_dir_entry *pde_rcvlist_all;
+
+static const char rx_list_name[][8] = {
+	[RX_ALL] = "rx_all",
+};
+
+static void flexray_print_rcvlist(struct seq_file *m,
+				  struct hlist_head *rx_list,
+				  struct net_device *dev)
+{
+	struct receiver *r;
+
+	hlist_for_each_entry_rcu(r, rx_list, list)
+		seq_printf(m, "   %-5s     %pK  %pK  %s\n",
+				DNAME(dev), r->func, r->data, r->ident);
+}
+
+static void flexray_print_recv_banner(struct seq_file *m)
+{
+	seq_puts(m, "  device   flexray_id   flexray_mask  function"
+		 "  userdata   matches  ident\n");
+}
+
+static int flexray_stats_proc_show(struct seq_file *m, void *v)
+{
+	seq_putc(m, '\n');
+	seq_printf(m, " %8ld transmitted frames (TXF)\n",
+		   flexray_stats.tx_frames);
+	seq_printf(m, " %8ld received frames (RXF)\n",
+		   flexray_stats.rx_frames);
+	seq_printf(m, " %8ld matched frames (RXMF)\n",
+		   flexray_stats.matches);
+
+	seq_putc(m, '\n');
+
+	seq_printf(m, " %8ld current receive list entries (CRCV)\n",
+		   flexray_pstats.rcv_entries);
+	seq_printf(m, " %8ld maximum receive list entries (MRCV)\n",
+		   flexray_pstats.rcv_entries_max);
+
+	if (flexray_pstats.stats_reset)
+		seq_printf(m, "\n %8ld statistic resets (STR)\n",
+			   flexray_pstats.stats_reset);
+
+	if (flexray_pstats.user_reset)
+		seq_printf(m, " %8ld user statistic resets (USTR)\n",
+			   flexray_pstats.user_reset);
+
+	seq_putc(m, '\n');
+	return 0;
+}
+
+static int flexray_stats_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, flexray_stats_proc_show, NULL);
+}
+
+static const struct file_operations flexray_stats_proc_fops = {
+	.owner		= THIS_MODULE,
+	.open		= flexray_stats_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int flexray_reset_stats_proc_show(struct seq_file *m, void *v)
+{
+	seq_printf(m, "Performed statistic reset #%ld.\n",
+		   flexray_pstats.stats_reset);
+
+	return 0;
+}
+
+static int flexray_reset_stats_proc_open(struct inode *inode,
+					 struct file *file)
+{
+	return single_open(file, flexray_reset_stats_proc_show, NULL);
+}
+
+static const struct file_operations flexray_reset_stats_proc_fops = {
+	.owner		= THIS_MODULE,
+	.open		= flexray_reset_stats_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void flexray_rcvlist_proc_show_one(struct seq_file *m, int idx,
+						 struct net_device *dev,
+						 struct dev_rcv_lists *d)
+{
+	if (!hlist_empty(&d->rx[idx])) {
+		flexray_print_recv_banner(m);
+		flexray_print_rcvlist(m, &d->rx[idx], dev);
+	} else
+		seq_printf(m, "  (%s: no entry)\n", DNAME(dev));
+}
+
+static int flexray_rcvlist_proc_show(struct seq_file *m, void *v)
+{
+	int idx = (int)(long)m->private;
+	struct net_device *dev;
+	struct dev_rcv_lists *d;
+
+	seq_printf(m, "\nreceive list '%s':\n", rx_list_name[idx]);
+
+	rcu_read_lock();
+
+	/* receive list for 'all' FlexRay devices (dev == NULL) */
+	d = &flexray_rx_alldev_list;
+	flexray_rcvlist_proc_show_one(m, idx, NULL, d);
+
+	/* receive list for registered FlexRay devices */
+	for_each_netdev_rcu(&init_net, dev) {
+		if (dev->type == ARPHRD_FLEXRAY && dev->ml_priv)
+			flexray_rcvlist_proc_show_one(m, idx, dev,
+						      dev->ml_priv);
+	}
+
+	rcu_read_unlock();
+
+	seq_putc(m, '\n');
+	return 0;
+}
+
+static int flexray_rcvlist_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, flexray_rcvlist_proc_show, PDE_DATA(inode));
+}
+
+static const struct file_operations flexray_rcvlist_proc_fops = {
+	.owner		= THIS_MODULE,
+	.open		= flexray_rcvlist_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void flexray_remove_proc_readentry(const char *name)
+{
+	remove_proc_entry(name, flexray_dir);
+}
+
+void flexray_init_proc(void)
+{
+	int perm = S_IRUGO | S_IWUSR;
+
+	flexray_dir = proc_mkdir("flexray", init_net.proc_net);
+	if (!flexray_dir)
+		return;
+
+	pde_stats = proc_create(FLEXRAY_PROC_STATS, perm, flexray_dir,
+			&flexray_stats_proc_fops);
+	pde_reset_stats = proc_create(FLEXRAY_PROC_RESET_STATS, perm,
+			flexray_dir, &flexray_reset_stats_proc_fops);
+	pde_rcvlist_all = proc_create_data(FLEXRAY_PROC_RCVLIST_ALL, perm,
+			flexray_dir, &flexray_rcvlist_proc_fops,
+			(void *)RX_ALL);
+}
+
+void flexray_remove_proc(void)
+{
+	if (!flexray_dir)
+		return;
+	if (pde_stats)
+		flexray_remove_proc_readentry(FLEXRAY_PROC_STATS);
+
+	if (pde_reset_stats)
+		flexray_remove_proc_readentry(FLEXRAY_PROC_RESET_STATS);
+
+	if (pde_rcvlist_all)
+		flexray_remove_proc_readentry(FLEXRAY_PROC_RCVLIST_ALL);
+
+	remove_proc_entry("flexray", init_net.proc_net);
+}
diff --git a/net/flexray/raw.c b/net/flexray/raw.c
new file mode 100644
index 0000000..78fd0de
--- /dev/null
+++ b/net/flexray/raw.c
@@ -0,0 +1,446 @@
+/* Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/uio.h>
+#include <linux/net.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/socket.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/flexray.h>
+#include <linux/flexray/core.h>
+#include <linux/flexray/raw.h>
+#include <net/sock.h>
+#include <net/net_namespace.h>
+
+#define MASK_ALL 0
+
+struct flexray_raw_sock {
+	struct sock sk;
+	int bound;
+	int ifindex;
+	struct notifier_block notifier;
+	struct flexray_filter dfilter;	/* default/single filter */
+};
+
+static inline unsigned int *raw_flags(struct sk_buff *skb)
+{
+	BUILD_BUG_ON(sizeof(skb->cb) <= (sizeof(struct sockaddr_flexray) +
+					 sizeof(unsigned int)));
+
+	/* return pointer after struct sockaddr_flexray */
+	return (unsigned int *)(&((struct sockaddr_flexray *)skb->cb)[1]);
+}
+
+static inline struct flexray_raw_sock *raw_sk(const struct sock *sk)
+{
+	return (struct flexray_raw_sock *)sk;
+}
+
+static void raw_rcv(struct sk_buff *oskb, void *data)
+{
+	struct sock *sk = (struct sock *)data;
+	struct sockaddr_flexray *addr;
+	struct sk_buff *skb;
+	unsigned int *pflags;
+
+	/* check the received tx sock reference */
+	if (oskb->sk == sk)
+		return;
+
+	/* clone the given skb to be able to enqueue it into the rcv queue */
+	skb = skb_clone(oskb, GFP_ATOMIC);
+	if (!skb)
+		return;
+
+	/*  Put the datagram to the queue so that raw_recvmsg() can
+	 *  get it from there.  We need to pass the interface index to
+	 *  raw_recvmsg().  We pass a whole struct sockaddr_flexray in skb->cb
+	 *  containing the interface index.
+	 */
+
+	BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct sockaddr_flexray));
+	addr = (struct sockaddr_flexray *)skb->cb;
+	memset(addr, 0, sizeof(*addr));
+	addr->flexray_family  = AF_FLEXRAY;
+	addr->flexray_ifindex = skb->dev->ifindex;
+
+	/* add FlexRay specific message flags for raw_recvmsg() */
+	pflags = raw_flags(skb);
+	*pflags = 0;
+	if (oskb->sk)
+		*pflags |= MSG_DONTROUTE;
+	if (oskb->sk == sk)
+		*pflags |= MSG_CONFIRM;
+
+	if (sock_queue_rcv_skb(sk, skb) < 0)
+		kfree_skb(skb);
+}
+
+static int raw_enable_allfilters(struct net_device *dev, struct sock *sk)
+{
+	return flexray_rx_register(dev, raw_rcv, sk, "raw");
+}
+
+static inline void raw_disable_allfilters(struct net_device *dev,
+					  struct sock *sk)
+{
+	flexray_rx_unregister(dev, raw_rcv, sk);
+}
+
+static struct flexray_raw_sock *nb_to_raw_sock(struct notifier_block *nb)
+{
+	return container_of(nb, struct flexray_raw_sock, notifier);
+}
+
+static int raw_notifier(struct notifier_block *nb,
+			unsigned long msg, void *data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct flexray_raw_sock *ro = nb_to_raw_sock(nb);
+	struct sock *sk = &ro->sk;
+
+	if (!net_eq(dev_net(dev), &init_net))
+		return NOTIFY_DONE;
+
+	if (dev->type != ARPHRD_FLEXRAY)
+		return NOTIFY_DONE;
+
+	if (ro->ifindex != dev->ifindex)
+		return NOTIFY_DONE;
+
+	switch (msg) {
+
+	case NETDEV_UNREGISTER:
+		lock_sock(sk);
+		/* remove current filters & unregister */
+		if (ro->bound)
+			raw_disable_allfilters(dev, sk);
+
+		ro->ifindex = 0;
+		ro->bound   = 0;
+		release_sock(sk);
+
+		sk->sk_err = ENODEV;
+		if (!sock_flag(sk, SOCK_DEAD))
+			sk->sk_error_report(sk);
+		break;
+
+	case NETDEV_DOWN:
+		sk->sk_err = ENETDOWN;
+		if (!sock_flag(sk, SOCK_DEAD))
+			sk->sk_error_report(sk);
+		break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+static int raw_init(struct sock *sk)
+{
+	struct flexray_raw_sock *ro = raw_sk(sk);
+
+	ro->bound			= 0;
+	ro->ifindex			= 0;
+
+	/* set notifier */
+	ro->notifier.notifier_call	= raw_notifier;
+
+	register_netdevice_notifier(&ro->notifier);
+
+	return 0;
+}
+
+static int raw_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+	struct flexray_raw_sock *ro;
+
+	if (!sk)
+		return 0;
+
+	ro = raw_sk(sk);
+
+	unregister_netdevice_notifier(&ro->notifier);
+
+	lock_sock(sk);
+
+	/* remove current filters & unregister */
+	if (ro->bound) {
+		if (ro->ifindex) {
+			struct net_device *dev;
+
+			dev = dev_get_by_index(&init_net, ro->ifindex);
+			if (dev) {
+				raw_disable_allfilters(dev, sk);
+				dev_put(dev);
+			}
+		} else
+			raw_disable_allfilters(NULL, sk);
+	}
+
+	ro->ifindex = 0;
+	ro->bound   = 0;
+
+	sock_orphan(sk);
+	sock->sk = NULL;
+
+	release_sock(sk);
+	sock_put(sk);
+
+	return 0;
+}
+
+static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+{
+	struct sockaddr_flexray *addr = (struct sockaddr_flexray *)uaddr;
+	struct sock *sk = sock->sk;
+	struct flexray_raw_sock *ro = raw_sk(sk);
+	int ifindex;
+	int err = 0;
+	int notify_enetdown = 0;
+
+	if (len < sizeof(*addr))
+		return -EINVAL;
+
+	lock_sock(sk);
+
+	if (ro->bound && addr->flexray_ifindex == ro->ifindex)
+		goto out;
+
+	if (addr->flexray_ifindex) {
+		struct net_device *dev;
+
+		dev = dev_get_by_index(&init_net, addr->flexray_ifindex);
+		if (!dev) {
+			err = -ENODEV;
+			goto out;
+		}
+
+		if (dev->type != ARPHRD_FLEXRAY) {
+			dev_put(dev);
+			err = -ENODEV;
+			goto out;
+		}
+
+		if (!(dev->flags & IFF_UP))
+			notify_enetdown = 1;
+
+		ifindex = dev->ifindex;
+
+		/* filters set by default/setsockopt */
+		err = raw_enable_allfilters(dev, sk);
+
+		dev_put(dev);
+	} else {
+		ifindex = 0;
+
+		/* filters set by default/setsockopt */
+		err = raw_enable_allfilters(NULL, sk);
+	}
+
+	if (!err) {
+		if (ro->bound) {
+			/* unregister old filters */
+			if (ro->ifindex) {
+				struct net_device *dev;
+
+				dev = dev_get_by_index(&init_net, ro->ifindex);
+				if (dev) {
+					raw_disable_allfilters(dev, sk);
+					dev_put(dev);
+				}
+			} else
+				raw_disable_allfilters(NULL, sk);
+		}
+
+		ro->ifindex = ifindex;
+		ro->bound = 1;
+	}
+
+out:
+	release_sock(sk);
+
+	if (notify_enetdown) {
+		sk->sk_err = ENETDOWN;
+		if (!sock_flag(sk, SOCK_DEAD))
+			sk->sk_error_report(sk);
+	}
+
+	return err;
+}
+
+static int raw_getname(struct socket *sock, struct sockaddr *uaddr,
+		       int *len, int peer)
+{
+	struct sockaddr_flexray *addr = (struct sockaddr_flexray *)uaddr;
+	struct sock *sk = sock->sk;
+	struct flexray_raw_sock *ro = raw_sk(sk);
+
+	if (peer)
+		return -EOPNOTSUPP;
+
+	memset(addr, 0, sizeof(*addr));
+	addr->flexray_family  = AF_FLEXRAY;
+	addr->flexray_ifindex = ro->ifindex;
+
+	*len = sizeof(*addr);
+
+	return 0;
+}
+
+static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
+		       struct msghdr *msg, size_t size)
+{
+	struct sock *sk = sock->sk;
+	struct flexray_raw_sock *ro = raw_sk(sk);
+	struct sk_buff *skb;
+	struct net_device *dev;
+	int ifindex;
+	int err;
+
+	if (msg->msg_name) {
+		struct sockaddr_flexray *addr =
+			(struct sockaddr_flexray *)msg->msg_name;
+
+		if (msg->msg_namelen < sizeof(*addr))
+			return -EINVAL;
+
+		if (addr->flexray_family != AF_FLEXRAY)
+			return -EINVAL;
+
+		ifindex = addr->flexray_ifindex;
+	} else
+		ifindex = ro->ifindex;
+
+	dev = dev_get_by_index(&init_net, ifindex);
+	if (!dev)
+		return -ENXIO;
+
+	skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT,
+				  &err);
+	if (!skb)
+		goto put_dev;
+
+	err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+	if (err < 0)
+		goto free_skb;
+
+	sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags);
+
+	skb->dev = dev;
+	skb->sk  = sk;
+
+	err = flexray_send(skb);
+	if (err)
+		goto put_dev;
+
+	dev_put(dev);
+
+	return size;
+
+free_skb:
+	kfree_skb(skb);
+put_dev:
+	dev_put(dev);
+
+	return err;
+}
+
+static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
+		       struct msghdr *msg, size_t size, int flags)
+{
+	struct sock *sk = sock->sk;
+	struct sk_buff *skb;
+	int err = 0;
+	int noblock;
+
+	noblock =  flags & MSG_DONTWAIT;
+	flags   &= ~MSG_DONTWAIT;
+
+	skb = skb_recv_datagram(sk, flags, noblock, &err);
+	if (!skb)
+		return err;
+
+	if (size < skb->len)
+		msg->msg_flags |= MSG_TRUNC;
+	else
+		size = skb->len;
+
+	err = memcpy_toiovec(msg->msg_iov, skb->data, size);
+	if (err < 0) {
+		skb_free_datagram(sk, skb);
+		return err;
+	}
+
+	sock_recv_ts_and_drops(msg, sk, skb);
+
+	if (msg->msg_name) {
+		msg->msg_namelen = sizeof(struct sockaddr_flexray);
+		memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
+	}
+
+	/* assign the flags that have been recorded in raw_rcv() */
+	msg->msg_flags |= *(raw_flags(skb));
+
+	skb_free_datagram(sk, skb);
+
+	return size;
+}
+
+static const struct proto_ops raw_ops = {
+	.family		= PF_FLEXRAY,
+	.release	= raw_release,
+	.bind		= raw_bind,
+	.connect	= sock_no_connect,
+	.socketpair	= sock_no_socketpair,
+	.accept		= sock_no_accept,
+	.getname	= raw_getname,
+	.poll		= datagram_poll,
+	.ioctl		= flexray_ioctl,
+	.listen		= sock_no_listen,
+	.shutdown	= sock_no_shutdown,
+	.sendmsg	= raw_sendmsg,
+	.recvmsg	= raw_recvmsg,
+	.mmap		= sock_no_mmap,
+	.sendpage	= sock_no_sendpage,
+};
+
+static struct proto raw_proto __read_mostly = {
+	.name		= "FLEXRAY_RAW",
+	.owner		= THIS_MODULE,
+	.obj_size	= sizeof(struct flexray_raw_sock),
+	.init		= raw_init,
+};
+
+static const struct flexray_proto raw_flexray_proto = {
+	.type		= SOCK_RAW,
+	.protocol	= FLEXRAY_RAW,
+	.ops		= &raw_ops,
+	.prot		= &raw_proto,
+};
+
+static __init int raw_module_init(void)
+{
+	int err;
+
+	err = flexray_proto_register(&raw_flexray_proto);
+	if (err < 0)
+		pr_err("flexray: registration of raw protocol failed\n");
+
+	return err;
+}
+module_init(raw_module_init);
+
+static __exit void raw_module_exit(void)
+{
+	flexray_proto_unregister(&raw_flexray_proto);
+}
+module_exit(raw_module_exit);
+
+MODULE_DESCRIPTION("PF_FLEXRAY raw protocol");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Benedikt Spranger <b.spranger@linutronix.de>");
+MODULE_ALIAS("flexray-proto-1");
-- 
1.8.4.rc2

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH 7/7] net: add a flexray driver
  2013-08-13  9:08 [PATCH 0/7] add FlexRay support Benedikt Spranger
                   ` (6 preceding siblings ...)
  2013-08-13  9:08 ` [PATCH 6/7] net: add the AF_FLEXRAY protocol Benedikt Spranger
@ 2013-08-13  9:08 ` Benedikt Spranger
  7 siblings, 0 replies; 28+ messages in thread
From: Benedikt Spranger @ 2013-08-13  9:08 UTC (permalink / raw)
  To: netdev; +Cc: Alexander Frank, Sebastian Andrzej Siewior, Benedikt Spranger

This patch provides the netlink interface support for flexray, the first
hardware based driver and a virtual flexray driver which has been
modelled after the virtual can driver.

Signed-off-by: Benedikt Spranger <b.spranger@linutronix.de>
---
 drivers/net/Makefile              |    1 +
 drivers/net/flexray/Kconfig       |   41 +
 drivers/net/flexray/Makefile      |   12 +
 drivers/net/flexray/dev.c         |  700 +++++++++++
 drivers/net/flexray/flexcard_fr.c | 2480 +++++++++++++++++++++++++++++++++++++
 drivers/net/flexray/vflexray.c    |   99 ++
 net/flexray/Kconfig               |    2 +
 7 files changed, 3335 insertions(+)
 create mode 100644 drivers/net/flexray/Kconfig
 create mode 100644 drivers/net/flexray/Makefile
 create mode 100644 drivers/net/flexray/dev.c
 create mode 100644 drivers/net/flexray/flexcard_fr.c
 create mode 100644 drivers/net/flexray/vflexray.c

diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 3fef8a8..22d9018 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_ETRAX_ETHERNET) += cris/
 obj-$(CONFIG_NET_DSA) += dsa/
 obj-$(CONFIG_ETHERNET) += ethernet/
 obj-$(CONFIG_FDDI) += fddi/
+obj-$(CONFIG_FLEXRAY) += flexray/
 obj-$(CONFIG_HIPPI) += hippi/
 obj-$(CONFIG_HAMRADIO) += hamradio/
 obj-$(CONFIG_IRDA) += irda/
diff --git a/drivers/net/flexray/Kconfig b/drivers/net/flexray/Kconfig
new file mode 100644
index 0000000..091e6e4
--- /dev/null
+++ b/drivers/net/flexray/Kconfig
@@ -0,0 +1,41 @@
+menu "FlexRay Device Drivers"
+	depends on FLEXRAY
+
+config FLEXRAY_VFLEXRAY
+	tristate "Virtual Local FlexRay Interface (vflexray)"
+	depends on FLEXRAY
+	---help---
+	  Similar to the network loopback devices, vflexray offers a
+	  virtual local FlexRay interface.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called vflexray.
+
+config FLEXRAY_DEV
+	tristate "Platform FlexRay drivers with Netlink support"
+	depends on FLEXRAY
+	default y
+	---help---
+	  Enables the common framework for platform FlexRay drivers with
+	  Netlink support. This is the standard library for FlexRay drivers.
+	  If unsure, say Y.
+
+config FLEXRAY_FLEXCARD
+	tristate "Support for the Flexcard FlexRay function"
+	depends on FLEXRAY_DEV
+	---help---
+	  Driver for Eberspächer FlexCard PMC II, it supports Flexray and
+	  CAN busses.
+	  This driver can also be built as a module. If so, the module
+	  will be called flexcard_fr.
+
+config FLEXRAY_DEBUG_DEVICES
+	bool "FlexRay devices debugging messages"
+	depends on FLEXRAY
+	---help---
+	  Say Y here if you want the Flexray device drivers to produce a bunch
+	  of debug messages to the system log.  Select this if you are having
+	  a problem with FlexRay support and want to see more of what is going
+	  on.
+
+endmenu
diff --git a/drivers/net/flexray/Makefile b/drivers/net/flexray/Makefile
new file mode 100644
index 0000000..97e2538
--- /dev/null
+++ b/drivers/net/flexray/Makefile
@@ -0,0 +1,12 @@
+#
+#  Makefile for the Linux FlexRay drivers.
+#
+
+obj-$(CONFIG_FLEXRAY_VFLEXRAY)		+= vflexray.o
+
+obj-$(CONFIG_FLEXRAY_DEV)		+= flexray-dev.o
+flexray-dev-y				:= dev.o
+
+obj-$(CONFIG_FLEXRAY_FLEXCARD)		+= flexcard_fr.o
+
+ccflags-$(CONFIG_FLEXRAY_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/flexray/dev.c b/drivers/net/flexray/dev.c
new file mode 100644
index 0000000..e882d16
--- /dev/null
+++ b/drivers/net/flexray/dev.c
@@ -0,0 +1,700 @@
+/* Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved.
+ *
+ * valid flexray paramter ranges are from
+ * FlexRay - Protocol Specification - V2.1rev A
+ * FlexRay - Protocol Specification - V3.0.1
+ * eray-specific: Bosch E-Ray FlexRay IP-Module, User's Manual,
+ *		  Revision 1.2.7 (06.02.2009)
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/flexray.h>
+#include <linux/flexray/dev.h>
+#include <linux/flexray/netlink.h>
+#include <net/rtnetlink.h>
+
+static int flexray_check_cluster_params(struct flexray_cluster_param *cp,
+					struct net_device *dev);
+static int flexray_check_node_params(struct flexray_node_param *np,
+				     struct net_device *dev);
+
+static void flexray_setup(struct net_device *dev)
+{
+	dev->type = ARPHRD_FLEXRAY;
+	dev->mtu = sizeof(struct flexray_frame);
+	dev->hard_header_len = 0;
+	dev->addr_len = 0;
+	dev->tx_queue_len = 10;
+	dev->flags = IFF_NOARP;
+	dev->features = NETIF_F_HW_CSUM;
+}
+
+struct sk_buff *alloc_flexray_skb(struct net_device *dev,
+				  struct flexray_frame **cf)
+{
+	struct sk_buff *skb;
+
+	skb = netdev_alloc_skb(dev, sizeof(struct flexray_frame));
+	if (unlikely(!skb))
+		return NULL;
+
+	skb->protocol = htons(ETH_P_FLEXRAY);
+	skb->pkt_type = PACKET_BROADCAST;
+	skb->ip_summed = CHECKSUM_UNNECESSARY;
+	*cf = (struct flexray_frame *)skb_put(skb,
+					      sizeof(struct flexray_frame));
+	memset(*cf, 0, sizeof(struct flexray_frame));
+
+	return skb;
+}
+EXPORT_SYMBOL_GPL(alloc_flexray_skb);
+
+/* Allocate and setup space for the FLEXRAY network device */
+struct net_device *alloc_flexraydev(int sizeof_priv, u8 version)
+{
+	struct net_device *dev;
+	struct flexray_priv *priv;
+
+	if (version != 2 && version != 3)
+		return NULL;
+
+	/* 3 TXs queues: Key, Fixed, Dynamic */
+	dev = alloc_netdev_mqs(sizeof_priv, "flexray%d", flexray_setup, 3, 1);
+	if (!dev)
+		return NULL;
+
+	priv = netdev_priv(dev);
+	priv->state = FLEXRAY_STATE_UNSPEC;
+	priv->version = version;
+
+	return dev;
+}
+EXPORT_SYMBOL_GPL(alloc_flexraydev);
+
+/* Free space of the FLEXRAY network device */
+void free_flexraydev(struct net_device *dev)
+{
+	free_netdev(dev);
+}
+EXPORT_SYMBOL_GPL(free_flexraydev);
+
+int open_flexraydev(struct net_device *dev)
+{
+	/* Switch carrier on if device was stopped while in bus-off state */
+	if (!netif_carrier_ok(dev))
+		netif_carrier_on(dev);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(open_flexraydev);
+
+void close_flexraydev(struct net_device *dev)
+{
+}
+EXPORT_SYMBOL_GPL(close_flexraydev);
+
+/* debug functionality */
+
+#define PRINT_PARAM(name, p)	pr_debug(#name" = %d\n", p->name);
+
+void print_cluster_config(struct flexray_cluster_param *cp, int is_v3)
+{
+	pr_debug("Cluster configuration:\n");
+
+	PRINT_PARAM(gColdstartAttempts, cp);
+	PRINT_PARAM(gdActionPointOffset, cp);
+	PRINT_PARAM(gdCASRxLowMax, cp);
+	PRINT_PARAM(gdDynamicSlotIdlePhase, cp);
+	PRINT_PARAM(gdMinislot, cp);
+	PRINT_PARAM(gdMinislotActionPointOffset, cp);
+	PRINT_PARAM(gdStaticSlot, cp);
+	PRINT_PARAM(gdSymbolWindow, cp);
+	PRINT_PARAM(gdTSSTransmitter, cp);
+	PRINT_PARAM(gListenNoise, cp);
+	PRINT_PARAM(gMacroPerCycle, cp);
+	PRINT_PARAM(gMaxWithoutClockCorrectionFatal, cp);
+	PRINT_PARAM(gMaxWithoutClockCorrectionPassive, cp);
+	PRINT_PARAM(gNumberOfMinislots, cp);
+	PRINT_PARAM(gNumberOfStaticSlots, cp);
+	PRINT_PARAM(gPayloadLengthStatic, cp);
+	PRINT_PARAM(gChannels, cp);
+	PRINT_PARAM(gClusterDriftDamping, cp);
+	PRINT_PARAM(gdBit, cp);
+	PRINT_PARAM(gdCycle, cp);
+	PRINT_PARAM(gdMacrotick, cp);
+	PRINT_PARAM(gdNIT, cp);
+	PRINT_PARAM(gdSampleClockPeriod, cp);
+	PRINT_PARAM(gNetworkManagementVectorLength, cp);
+	if (!is_v3) {
+		PRINT_PARAM(v2.gAssumedPrecision, cp);
+		PRINT_PARAM(v2.gdMaxInitializationError, cp);
+		PRINT_PARAM(v2.gdMaxMicrotick, cp);
+		PRINT_PARAM(v2.gdMaxPropagationDelay, cp);
+		PRINT_PARAM(v2.gdMinPropagationDelay, cp);
+		PRINT_PARAM(v2.gdWakeupSymbolRxIdle, cp);
+		PRINT_PARAM(v2.gdWakeupSymbolRxLow, cp);
+		PRINT_PARAM(v2.gdWakeupSymbolRxWindow, cp);
+		PRINT_PARAM(v2.gdWakeupSymbolTxIdle, cp);
+		PRINT_PARAM(v2.gdWakeupSymbolTxLow, cp);
+		PRINT_PARAM(v2.gOffsetCorrectionMax, cp);
+		PRINT_PARAM(v2.gOffsetCorrectionStart, cp);
+		PRINT_PARAM(v2.gSyncNodeMax, cp);
+	} else {
+		PRINT_PARAM(v3.gClockDeviationMax, cp);
+		PRINT_PARAM(v3.gCycleCountMax, cp);
+		PRINT_PARAM(v3.gdIgnoreAfterTx, cp);
+		PRINT_PARAM(v3.gdSymbolWindowActionPointOffset, cp);
+		PRINT_PARAM(v3.gdWakeupRxIdle, cp);
+		PRINT_PARAM(v3.gdWakeupRxLow, cp);
+		PRINT_PARAM(v3.gdWakeupRxWindow, cp);
+		PRINT_PARAM(v3.gdWakeupTxActive, cp);
+		PRINT_PARAM(v3.gdWakeupTxIdle, cp);
+		PRINT_PARAM(v3.gExternOffsetCorrection, cp);
+		PRINT_PARAM(v3.gExternRateCorrection, cp);
+		PRINT_PARAM(v3.gSyncFrameIDCountMax, cp);
+	}
+}
+EXPORT_SYMBOL_GPL(print_cluster_config);
+
+void print_node_config(struct flexray_node_param *np, int is_v3)
+{
+	pr_debug("Node configuration:\n");
+
+	PRINT_PARAM(pAllowHaltDueToClock, np);
+	PRINT_PARAM(pAllowPassiveToActive, np);
+	PRINT_PARAM(pChannels, np);
+	PRINT_PARAM(pClusterDriftDamping, np);
+	PRINT_PARAM(pdAcceptedStartupRange, np);
+	PRINT_PARAM(pDecodingCorrection, np);
+	PRINT_PARAM(pDelayCompensationA, np);
+	PRINT_PARAM(pDelayCompensationB, np);
+	PRINT_PARAM(pdListenTimeout, np);
+	PRINT_PARAM(pExternOffsetCorrection, np);
+	PRINT_PARAM(pExternRateCorrection, np);
+	PRINT_PARAM(pKeySlotID, np);
+	PRINT_PARAM(pKeySlotUsedForStartup, np);
+	PRINT_PARAM(pKeySlotUsedForSync, np);
+	PRINT_PARAM(pLatestTx, np);
+	PRINT_PARAM(pMacroInitialOffsetA, np);
+	PRINT_PARAM(pMacroInitialOffsetB, np);
+	PRINT_PARAM(pMicroInitialOffsetA, np);
+	PRINT_PARAM(pMicroInitialOffsetB, np);
+	PRINT_PARAM(pMicroPerCycle, np);
+	PRINT_PARAM(vExternOffsetControl, np);
+	PRINT_PARAM(pOffsetCorrectionOut, np);
+	PRINT_PARAM(vExternRateControl, np);
+	PRINT_PARAM(pRateCorrectionOut, np);
+	PRINT_PARAM(pWakeupChannel, np);
+	PRINT_PARAM(pWakeupPattern, np);
+	PRINT_PARAM(pdMicrotick, np);
+	PRINT_PARAM(pPayloadLengthDynMax, np);
+	PRINT_PARAM(pSamplesPerMicrotick, np);
+
+	if (is_v3) {
+		PRINT_PARAM(v3.pExternalSync, np);
+		PRINT_PARAM(v3.pFallBackInternal, np);
+		PRINT_PARAM(v3.pKeySlotOnlyEnabled, np);
+		PRINT_PARAM(v3.pNMVectorEarlyUpdate, np);
+		PRINT_PARAM(v3.pOffsetCorrectionStart, np);
+		PRINT_PARAM(v3.pSecondKeySlotID, np);
+		PRINT_PARAM(v3.pTwoKeySlotMode, np);
+	} else {
+		PRINT_PARAM(v2.pdMaxDrift, np);
+		PRINT_PARAM(v2.pMicroPerMacroNom, np);
+		PRINT_PARAM(v2.pSingleSlotEnabled, np);
+	}
+}
+EXPORT_SYMBOL_GPL(print_node_config);
+
+void print_symbol_config(struct flexray_symbol_param *sp)
+{
+	pr_debug("Symbol configuration:\n");
+
+	PRINT_PARAM(pChannelsMTS, sp);
+}
+EXPORT_SYMBOL_GPL(print_symbol_config);
+
+/* Parameter check and set functions */
+
+#define FR_CHECK_PARAM(p, name, min, max)				\
+	do {								\
+		if (p->name < min || p->name > max) {			\
+			netdev_info(dev, #name" (0x%x) out of range\n",	\
+					p->name);			\
+			return -EINVAL;					\
+		}							\
+	} while (0)
+
+static int flexray_check_cluster_params(struct flexray_cluster_param *cp,
+					struct net_device *dev)
+{
+	struct flexray_priv *priv = netdev_priv(dev);
+
+	FR_CHECK_PARAM(cp, gColdstartAttempts, 2, 31);
+	FR_CHECK_PARAM(cp, gdActionPointOffset, 1, 63);
+	FR_CHECK_PARAM(cp, gdMinislot, 2, 63);
+	FR_CHECK_PARAM(cp, gdMinislotActionPointOffset, 1, 31);
+	FR_CHECK_PARAM(cp, gListenNoise, 2, 16);
+	FR_CHECK_PARAM(cp, gMacroPerCycle, 10, 16000);
+	FR_CHECK_PARAM(cp, gMaxWithoutClockCorrectionFatal, 1, 15);
+	FR_CHECK_PARAM(cp, gMaxWithoutClockCorrectionPassive, 1, 15);
+	FR_CHECK_PARAM(cp, gNumberOfMinislots, 0, 7986);
+	FR_CHECK_PARAM(cp, gNumberOfStaticSlots, 2, 1023);
+	FR_CHECK_PARAM(cp, gPayloadLengthStatic, 0, 127);
+	FR_CHECK_PARAM(cp, gClusterDriftDamping, 0, 5);
+	FR_CHECK_PARAM(cp, gdNIT, 2, 805);
+	FR_CHECK_PARAM(cp, gNetworkManagementVectorLength, 0, 12);
+	FR_CHECK_PARAM(cp, gdDynamicSlotIdlePhase, 0, 2);
+
+	if (priv->version == 2) {
+		FR_CHECK_PARAM(cp, v2.gdMaxInitializationError, 0, 117);
+		FR_CHECK_PARAM(cp, v2.gdMinPropagationDelay, 0,
+			       cp->v2.gdMaxPropagationDelay);
+		FR_CHECK_PARAM(cp, v2.gdWakeupSymbolRxIdle, 14, 59);
+		FR_CHECK_PARAM(cp, v2.gdWakeupSymbolRxWindow, 76, 301);
+		FR_CHECK_PARAM(cp, v2.gdWakeupSymbolTxIdle, 45, 180);
+		FR_CHECK_PARAM(cp, v2.gdWakeupSymbolTxLow, 15, 60);
+		FR_CHECK_PARAM(cp, v2.gOffsetCorrectionStart, 9, 15999);
+		FR_CHECK_PARAM(cp, v2.gSyncNodeMax, 2, 15);
+		FR_CHECK_PARAM(cp, gdCASRxLowMax, 67, 99);
+		FR_CHECK_PARAM(cp, gdTSSTransmitter, 3, 15);
+#ifndef CONFIG_MFD_EBEL_FLEXCARD_PROTPARAM
+		FR_CHECK_PARAM(cp, v2.gdWakeupSymbolRxLow, 11, 59);
+		FR_CHECK_PARAM(cp, gdStaticSlot, 4, 661);
+#else
+		FR_CHECK_PARAM(cp, v2.gdWakeupSymbolRxLow, 10, 55);
+		FR_CHECK_PARAM(cp, gdStaticSlot, 4, 659);
+#endif /* CONFIG_MFD_EBEL_FLEXCARD_PROTPARAM */
+	}
+
+	if (priv->version == 3) {
+		FR_CHECK_PARAM(cp, v3.gCycleCountMax, 7, 63);
+		FR_CHECK_PARAM(cp, v3.gdSymbolWindowActionPointOffset, 1, 63);
+		FR_CHECK_PARAM(cp, v3.gdWakeupRxIdle, 8, 59);
+		FR_CHECK_PARAM(cp, v3.gdWakeupRxLow, 8, 59);
+		FR_CHECK_PARAM(cp, v3.gdWakeupRxWindow, 76, 485);
+		FR_CHECK_PARAM(cp, v3.gdWakeupTxActive, 15, 60);
+		FR_CHECK_PARAM(cp, v3.gdWakeupTxIdle, 45, 180);
+		FR_CHECK_PARAM(cp, v3.gSyncFrameIDCountMax, 2, 15);
+		FR_CHECK_PARAM(cp, v3.gClockDeviationMax, 1, 1500);
+		FR_CHECK_PARAM(cp, v3.gExternOffsetCorrection, 0, 35);
+		FR_CHECK_PARAM(cp, v3.gdIgnoreAfterTx, 0, 15);
+		FR_CHECK_PARAM(cp, gdCASRxLowMax, 28, 254);
+		FR_CHECK_PARAM(cp, gdStaticSlot, 3, 664);
+		FR_CHECK_PARAM(cp, gdTSSTransmitter, 1, 15);
+		if (!(cp->v3.gCycleCountMax % 2)) {
+			netdev_info(dev, "gCycleCountMax is even\n");
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int flexray_set_cluster_params(struct flexray_cluster_param *cp,
+				      struct net_device *dev)
+{
+	struct flexray_priv *priv = netdev_priv(dev);
+
+	memcpy(&priv->cluster, cp, sizeof(*cp));
+
+	return 0;
+}
+
+static int
+flexray_check_and_set_cluster_params(struct flexray_cluster_param *cp,
+				     struct net_device *dev)
+{
+	int ret;
+
+	ret = flexray_check_cluster_params(cp, dev);
+	if (ret)
+		return ret;
+
+	return flexray_set_cluster_params(cp, dev);
+}
+
+static int flexray_check_node_params(struct flexray_node_param *np,
+				     struct net_device *dev)
+{
+	struct flexray_priv *priv = netdev_priv(dev);
+
+	FR_CHECK_PARAM(np, pAllowPassiveToActive, 0, 31);
+	FR_CHECK_PARAM(np, pClusterDriftDamping, 0, 20);
+	FR_CHECK_PARAM(np, pExternRateCorrection, 0, 28);
+	FR_CHECK_PARAM(np, vExternOffsetControl, -1, 1);
+	FR_CHECK_PARAM(np, vExternRateControl, -1, 1);
+	FR_CHECK_PARAM(np, pWakeupPattern, 2, 63);
+	FR_CHECK_PARAM(np, pPayloadLengthDynMax, 0, 254);
+	FR_CHECK_PARAM(np, pAllowPassiveToActive, 0, 31);
+	FR_CHECK_PARAM(np, pChannels, 0, 3);
+	FR_CHECK_PARAM(np, pWakeupChannel, 0, 3);
+	FR_CHECK_PARAM(np, pKeySlotUsedForStartup, 0, 1);
+	FR_CHECK_PARAM(np, pKeySlotUsedForSync, 0, 1);
+#ifndef CONFIG_MFD_EBEL_FLEXCARD_PROTPARAM
+	FR_CHECK_PARAM(np, pMacroInitialOffsetA, 2, 68);
+	FR_CHECK_PARAM(np, pMacroInitialOffsetB, 2, 68);
+	FR_CHECK_PARAM(np, pMicroInitialOffsetA, 0, 239);
+	FR_CHECK_PARAM(np, pMicroInitialOffsetB, 0, 239);
+#else
+	FR_CHECK_PARAM(np, pMacroInitialOffsetA, 2, 72);
+	FR_CHECK_PARAM(np, pMacroInitialOffsetB, 2, 72);
+	FR_CHECK_PARAM(np, pMicroInitialOffsetA, 0, 240);
+	FR_CHECK_PARAM(np, pMicroInitialOffsetB, 0, 240);
+#endif /* CONFIG_MFD_EBEL_FLEXCARD_PROTPARAM */
+
+	if (priv->version == 2) {
+		FR_CHECK_PARAM(np, v2.pdMaxDrift, 2, 1923);
+		FR_CHECK_PARAM(np, pdAcceptedStartupRange, 0, 1875);
+		FR_CHECK_PARAM(np, pDecodingCorrection, 14, 143);
+		FR_CHECK_PARAM(np, pdListenTimeout, 1284, 1283846);
+		FR_CHECK_PARAM(np, pExternOffsetCorrection, 0, 7);
+		FR_CHECK_PARAM(np, pKeySlotID, 0, 1023);
+		FR_CHECK_PARAM(np, pMicroPerCycle, 640, 640000);
+		FR_CHECK_PARAM(np, pRateCorrectionOut, 2, 1923);
+#ifndef CONFIG_MFD_EBEL_FLEXCARD_PROTPARAM
+		FR_CHECK_PARAM(np, pLatestTx, 0, 7980);
+		FR_CHECK_PARAM(np, pOffsetCorrectionOut, 13, 15567);
+#else
+		FR_CHECK_PARAM(np, pLatestTx, 0, 7981);
+		FR_CHECK_PARAM(np, pOffsetCorrectionOut, 5, 15266);
+#endif /* CONFIG_MFD_EBEL_FLEXCARD_PROTPARAM */
+	}
+	if (priv->version == 3) {
+		FR_CHECK_PARAM(np, v3.pOffsetCorrectionStart, 7, 15999);
+		FR_CHECK_PARAM(np, v3.pSecondKeySlotID, 0, 1023);
+		FR_CHECK_PARAM(np, pdAcceptedStartupRange, 29, 2743);
+		FR_CHECK_PARAM(np, pDecodingCorrection, 12, 136);
+		FR_CHECK_PARAM(np, pdListenTimeout, 1926, 2567692);
+		FR_CHECK_PARAM(np, pExternOffsetCorrection, 0, 28);
+		FR_CHECK_PARAM(np, pKeySlotID, 0, 1023);
+		FR_CHECK_PARAM(np, pMicroPerCycle, 960, 1280000);
+		FR_CHECK_PARAM(np, pRateCorrectionOut, 3, 3846);
+		FR_CHECK_PARAM(np, pLatestTx, 0, 7988);
+		FR_CHECK_PARAM(np, pOffsetCorrectionOut, 15, 16082);
+	}
+
+	return 0;
+}
+
+static int flexray_set_node_params(struct flexray_node_param *np,
+			    struct net_device *dev)
+{
+	struct flexray_priv *priv = netdev_priv(dev);
+
+	memcpy(&priv->node, np, sizeof(*np));
+
+	return 0;
+}
+
+static int flexray_check_and_set_node_params(struct flexray_node_param *np,
+					     struct net_device *dev)
+{
+	int ret;
+
+	ret = flexray_check_node_params(np, dev);
+	if (ret)
+		return ret;
+
+	return flexray_set_node_params(np, dev);
+}
+
+static int flexray_check_symbol_params(struct flexray_symbol_param *sp,
+				       struct net_device *dev)
+{
+	FR_CHECK_PARAM(sp, pChannelsMTS, 0, 3);
+
+	return 0;
+}
+
+static int flexray_set_symbol_params(struct flexray_symbol_param *sp,
+				     struct net_device *dev)
+{
+	struct flexray_priv *priv = netdev_priv(dev);
+
+	memcpy(&priv->symbol, sp, sizeof(*sp));
+
+	return 0;
+}
+
+static int flexray_check_and_set_symbol_params(struct flexray_symbol_param *sp,
+					       struct net_device *dev)
+{
+	int ret;
+
+	ret = flexray_check_symbol_params(sp, dev);
+	if (ret)
+		return ret;
+
+	return flexray_set_symbol_params(sp, dev);
+}
+
+/* FLEXRAY netlink interface */
+static const struct nla_policy flexray_policy[IFLA_FLEXRAY_MAX + 1] = {
+	[IFLA_FLEXRAY_STATE] = {.type = NLA_U32},
+	[IFLA_FLEXRAY_VERSION] = {.type = NLA_U8},
+	[IFLA_FLEXRAY_CLUSTER] = {.len = sizeof(struct flexray_cluster_param)},
+	[IFLA_FLEXRAY_NODE] = {.len = sizeof(struct flexray_node_param)},
+	[IFLA_FLEXRAY_SYMBOL] = {.len = sizeof(struct flexray_symbol_param)},
+	[IFLA_FLEXRAY_SW_FILTER] = {.type = NLA_NESTED},
+};
+
+static const struct nla_policy flexray_filt_pol[IFLA_FLEXRAY_FILTER_MAX + 1] = {
+	[IFLA_FLEXRAY_FILTER_ENTRY] = { .len = sizeof(struct flexray_filter) },
+};
+
+static int validate_and_set_sw_filter(struct flexray_sw_filter *sw, u32 *id)
+{
+	int i;
+
+	if (sw->pos >= FLEXRAY_MAX_SW_FILTER)
+		return -EINVAL;
+
+	if (sw->id != 0)
+		for (i = 0; i < sw->pos; i++)
+			if (id[i] > sw->id)
+				return -EINVAL;
+
+	id[sw->pos] = sw->id;
+
+	return 0;
+}
+
+static int flexray_changelink(struct net_device *dev,
+			      struct nlattr *tb[], struct nlattr *data[])
+{
+	struct flexray_priv *priv = netdev_priv(dev);
+	struct nlattr *attr;
+	int rem, ret = 0;
+
+	/* We need synchronization with dev->stop() */
+	ASSERT_RTNL();
+
+	if (data[IFLA_FLEXRAY_STATE]) {
+		enum flexray_state state;
+
+		state = nla_get_u32(data[IFLA_FLEXRAY_STATE]);
+		if (priv->do_set_state)
+			ret = priv->do_set_state(dev, state);
+		if (ret)
+			return ret;
+		priv->state = state;
+	}
+
+	if (data[IFLA_FLEXRAY_CLUSTER]) {
+		struct flexray_cluster_param cp;
+
+		memcpy(&cp, nla_data(data[IFLA_FLEXRAY_CLUSTER]), sizeof(cp));
+		ret = flexray_check_and_set_cluster_params(&cp, dev);
+		if (ret)
+			return ret;
+	}
+
+	if (data[IFLA_FLEXRAY_NODE]) {
+		struct flexray_node_param np;
+
+		memcpy(&np, nla_data(data[IFLA_FLEXRAY_NODE]), sizeof(np));
+		ret = flexray_check_and_set_node_params(&np, dev);
+		if (ret)
+			return ret;
+	}
+
+	if (data[IFLA_FLEXRAY_SYMBOL]) {
+		struct flexray_symbol_param sp;
+
+		memcpy(&sp, nla_data(data[IFLA_FLEXRAY_SYMBOL]), sizeof(sp));
+		ret = flexray_check_and_set_symbol_params(&sp, dev);
+		if (ret)
+			return ret;
+	}
+
+	if (data[IFLA_FLEXRAY_SW_FILTER]) {
+		struct flexray_sw_filter *sw;
+
+		nla_for_each_nested(attr, data[IFLA_FLEXRAY_SW_FILTER], rem) {
+			sw = nla_data(attr);
+			ret = validate_and_set_sw_filter(sw, priv->sw_filter);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return ret;
+}
+
+static inline int flexray_validate_sw_filter(struct nlattr *nest)
+{
+	struct flexray_sw_filter *sw;
+	struct nlattr *attr;
+	int rem, ret;
+	u32 id = 0;
+
+	if (!nest)
+		return 0;
+
+	ret = nla_validate_nested(nest, FLEXRAY_MAX_SW_FILTER,
+				  flexray_filt_pol);
+	if (ret)
+		return ret;
+
+	nla_for_each_nested(attr, nest, rem) {
+		sw = nla_data(attr);
+		if (sw->id < id)
+			return -EINVAL;
+
+		id = sw->id;
+	}
+
+	return 0;
+}
+
+static int flexray_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+	int ret;
+
+	ret = flexray_validate_sw_filter(data[IFLA_FLEXRAY_SW_FILTER]);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static size_t flexray_get_size(const struct net_device *dev)
+{
+	size_t size;
+
+	size = nla_total_size(sizeof(u32));
+	size += nla_total_size(sizeof(struct flexray_cluster_param));
+	size += nla_total_size(sizeof(struct flexray_node_param));
+
+	return size;
+}
+
+static int flexray_fill_info(struct sk_buff *skb, const struct net_device *dev)
+{
+	struct flexray_priv *priv = netdev_priv(dev);
+	enum flexray_state state = priv->state;
+	struct flexray_cluster_param *cp = &priv->cluster;
+	struct flexray_node_param *np = &priv->node;
+	struct flexray_symbol_param *sp = &priv->symbol;
+	struct flexray_sw_filter sw;
+	struct nlattr *nest;
+	int i, ret = 0;
+
+	if (priv->do_get_state)
+		ret = priv->do_get_state(dev, &state);
+	if (nla_put_u32(skb, IFLA_FLEXRAY_STATE, state))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, IFLA_FLEXRAY_VERSION, priv->version))
+		goto nla_put_failure;
+	if (nla_put(skb, IFLA_FLEXRAY_CLUSTER, sizeof(*cp), cp))
+		goto nla_put_failure;
+	if (nla_put(skb, IFLA_FLEXRAY_NODE, sizeof(*np), np))
+		goto nla_put_failure;
+	if (nla_put(skb, IFLA_FLEXRAY_SYMBOL, sizeof(*sp), sp))
+		goto nla_put_failure;
+
+	nest = nla_nest_start(skb, IFLA_FLEXRAY_SW_FILTER);
+	if (nest == NULL)
+		goto nla_put_failure;
+
+	for (i = 0; i < FLEXRAY_MAX_SW_FILTER; i++) {
+		sw.pos = i;
+		sw.id = priv->sw_filter[i];
+		if (nla_put(skb, IFLA_FLEXRAY_FILTER_ENTRY, sizeof(sw), &sw))
+			goto nla_put_failure;
+
+		if (sw.id == 0)
+			break;
+	}
+	nla_nest_end(skb, nest);
+
+	return ret;
+
+nla_put_failure:
+
+	return -EMSGSIZE;
+}
+
+static size_t flexray_get_xstats_size(const struct net_device *dev)
+{
+	return sizeof(struct flexray_device_stats);
+}
+
+static int flexray_fill_xstats(struct sk_buff *skb,
+			       const struct net_device *dev)
+{
+	struct flexray_priv *priv = netdev_priv(dev);
+
+	if (nla_put(skb, IFLA_INFO_XSTATS,
+		    sizeof(priv->flexray_stats), &priv->flexray_stats))
+		goto nla_put_failure;
+
+	return 0;
+
+nla_put_failure:
+	return -EMSGSIZE;
+}
+
+static int flexray_newlink(struct net *src_net, struct net_device *dev,
+		       struct nlattr *tb[], struct nlattr *data[])
+{
+	return -EOPNOTSUPP;
+}
+
+static struct rtnl_link_ops flexray_link_ops __read_mostly = {
+	.kind		= "flexray",
+	.maxtype	= IFLA_FLEXRAY_MAX,
+	.policy		= flexray_policy,
+	.setup		= flexray_setup,
+	.validate	= flexray_validate,
+	.newlink	= flexray_newlink,
+	.changelink	= flexray_changelink,
+	.get_size	= flexray_get_size,
+	.fill_info	= flexray_fill_info,
+	.get_xstats_size = flexray_get_xstats_size,
+	.fill_xstats	= flexray_fill_xstats,
+};
+
+/* Register the FLEXRAY network device */
+int register_flexraydev(struct net_device *dev)
+{
+	dev->rtnl_link_ops = &flexray_link_ops;
+	return register_netdev(dev);
+}
+EXPORT_SYMBOL_GPL(register_flexraydev);
+
+/* Unregister the FLEXRAY network device */
+void unregister_flexraydev(struct net_device *dev)
+{
+	unregister_netdev(dev);
+}
+EXPORT_SYMBOL_GPL(unregister_flexraydev);
+
+static __init int flexray_dev_init(void)
+{
+	int ret;
+
+	ret = rtnl_link_register(&flexray_link_ops);
+	if (!ret)
+		pr_info("FlexRay netlink interface\n");
+
+	return ret;
+}
+module_init(flexray_dev_init);
+
+static __exit void flexray_dev_exit(void)
+{
+	rtnl_link_unregister(&flexray_link_ops);
+}
+module_exit(flexray_dev_exit);
+
+MODULE_ALIAS_RTNL_LINK("flexray");
+
+MODULE_DESCRIPTION("FlexRay device driver interface");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Benedikt Spranger <b.spranger@linutronix.de>");
diff --git a/drivers/net/flexray/flexcard_fr.c b/drivers/net/flexray/flexcard_fr.c
new file mode 100644
index 0000000..1f879ae
--- /dev/null
+++ b/drivers/net/flexray/flexcard_fr.c
@@ -0,0 +1,2480 @@
+/* Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved */
+
+#include <linux/netdevice.h>
+#include <linux/flexray.h>
+#include <linux/flexray/dev.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/eray.h>
+#include <linux/flexcard.h>
+#include <linux/flexray/flexcard_netlink.h>
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#define FC_MSGBUF_DUMP_CFG
+
+static int cc_change_state(struct eray_cc *cc, enum eray_cc_state state,
+			   int retry);
+static void cc_get_conf(struct net_device *dev);
+
+struct fc_enum2name {
+	int type;
+	char *name;
+};
+#define FC_ENUM2NAME(x)		{x, #x}
+
+static struct fc_enum2name flexray_state[] = {
+	FC_ENUM2NAME(FLEXRAY_STATE_UNSPEC),
+	FC_ENUM2NAME(FLEXRAY_STATE_DEFAULT_CONFIG),
+	FC_ENUM2NAME(FLEXRAY_STATE_CONFIG),
+	FC_ENUM2NAME(FLEXRAY_STATE_READY),
+	FC_ENUM2NAME(FLEXRAY_STATE_WAKEUP),
+	FC_ENUM2NAME(FLEXRAY_STATE_STARTUP),
+	FC_ENUM2NAME(FLEXRAY_STATE_NORMAL_ACTIVE),
+	FC_ENUM2NAME(FLEXRAY_STATE_NORMAL_PASSIVE),
+	FC_ENUM2NAME(FLEXRAY_STATE_HALT),
+	FC_ENUM2NAME(FLEXRAY_STATE_MONITOR_MODE),
+	FC_ENUM2NAME(FLEXRAY_STATE_COLDSTART),
+	FC_ENUM2NAME(FLEXRAY_STATE_MAX),
+};
+
+static inline char *fc_flexray_state_name(enum flexray_state cmd)
+{
+	if (cmd < 0)
+		cmd = FLEXRAY_STATE_UNSPEC;
+	if (cmd > FLEXRAY_STATE_MAX)
+		cmd = FLEXRAY_STATE_UNSPEC;
+
+	return flexray_state[cmd].name;
+}
+
+static struct fc_enum2name eray_state[] = {
+	FC_ENUM2NAME(ERAY_CMD_INVALID),
+	FC_ENUM2NAME(ERAY_CMD_CONFIG),
+	FC_ENUM2NAME(ERAY_CMD_READY),
+	FC_ENUM2NAME(ERAY_CMD_WAKEUP),
+	FC_ENUM2NAME(ERAY_CMD_RUN),
+	FC_ENUM2NAME(ERAY_CMD_ALL_SLOTS),
+	FC_ENUM2NAME(ERAY_CMD_HALT),
+	FC_ENUM2NAME(ERAY_CMD_FREEZE),
+	FC_ENUM2NAME(ERAY_CMD_SEND_MTS),
+	FC_ENUM2NAME(ERAY_CMD_ALLOW_COLDSTART),
+	FC_ENUM2NAME(ERAY_CMD_RESET_STATUS_INDICATORS),
+	FC_ENUM2NAME(ERAY_CMD_MONITOR_MODE),
+	FC_ENUM2NAME(ERAY_CMD_CLEAR_RAMS),
+};
+
+static inline char *fc_eray_cmd_name(enum eray_cc_state cmd)
+{
+	if (cmd < 0)
+		cmd = ERAY_CMD_INVALID;
+	if (cmd > ERAY_CMD_CLEAR_RAMS)
+		cmd = ERAY_CMD_INVALID;
+
+	return eray_state[cmd].name;
+}
+
+struct fc_msg {
+	u32 buf_id;
+	u8 data[254];
+} __packed;
+
+struct flexcard_priv {
+	struct flexray_priv flexray;
+	struct net_device *dev;
+	struct eray_cc *cc;
+	int id;
+	void __iomem *conf;
+};
+
+static int _fc_write_data(struct flexcard_priv *priv, unsigned int msgbuf_id,
+			  unsigned char *payload, size_t byte_len);
+
+static struct nla_policy fc_msgbuf_genl_policy[__FC_MSGBUF_ATTR_MAX] = {
+	[FC_MSGBUF_ATTR_BUF_ID] = {
+		.type = NLA_U8,
+	},
+	[FC_MSGBUF_ATTR_DEV_ID] = {
+		.type = NLA_U32,
+	},
+	[FC_MSGBUF_ATTR_DEV_NAME] = {
+		.type = NLA_NUL_STRING,
+	},
+	[FC_MSGBUF_ATTR_CFG] = {
+		.type = NLA_BINARY,
+		.len = sizeof(struct fc_msgbuf_cfg),
+	},
+};
+
+static struct genl_family fc_msgbuf_genl_family = {
+	.id = GENL_ID_GENERATE,
+	.hdrsize = 0,
+	.name = "FC_MSGBUF",
+	.version = FC_MSGBUF_VERSION,
+	.maxattr = FC_MSGBUF_ATTR_MAX,
+};
+
+#ifdef FC_MSGBUF_DUMP_CFG
+
+#define ERAY_MSGBUF_PRINT_FLAGS(x)			\
+	do {						\
+		if (cfg->flags & x)			\
+			pr_cont(#x " ");		\
+	} while (0)
+
+static void eray_dump_msg_cfg(struct eray_msgbuf_cfg *cfg, int buf,
+		const char *func)
+{
+	pr_debug("%s: msg. buffer dump %03d\n", func, buf);
+	pr_debug("eray_msgbuf_cfg: cfg = %p\n", cfg);
+	if (!cfg)
+		return;
+	pr_debug("flags   : ");
+	ERAY_MSGBUF_PRINT_FLAGS(ERAY_MSGBUF_USED);
+	ERAY_MSGBUF_PRINT_FLAGS(ERAY_MSGBUF_STARTUP);
+	ERAY_MSGBUF_PRINT_FLAGS(ERAY_MSGBUF_SYNC);
+	ERAY_MSGBUF_PRINT_FLAGS(ERAY_MSGBUF_PPIT);
+	ERAY_MSGBUF_PRINT_FLAGS(ERAY_MSGBUF_TXCONT);
+	ERAY_MSGBUF_PRINT_FLAGS(ERAY_MSGBUF_FIFOREJ_NULL);
+	ERAY_MSGBUF_PRINT_FLAGS(ERAY_MSGBUF_FIFOREJ_INSEG);
+	pr_cont("\n");
+	pr_debug("id      : %d\n", cfg->id);
+	pr_debug("cyc     : %d\n", cfg->cyc);
+	pr_debug("len     : %d\n", cfg->len);
+	pr_debug("max     : %d\n", cfg->max);
+	pr_debug("frame_id: %d\n", cfg->frame_id);
+	pr_debug("wrhs1   : 0x%08x\n", cfg->wrhs1);
+	pr_debug("wrhs2   : 0x%08x\n", cfg->wrhs2);
+	pr_debug("wrhs3   : 0x%08x\n", cfg->wrhs3);
+	pr_debug("type    : ");
+	switch (cfg->type) {
+	case eray_msgbuf_type_none:
+		pr_cont("NONE\n");
+		break;
+	case eray_msgbuf_type_fifo:
+		pr_cont("FIFO\n");
+		break;
+	case eray_msgbuf_type_rx:
+		pr_cont("RX\n");
+		break;
+	case eray_msgbuf_type_tx:
+		pr_cont("TX\n");
+		break;
+	default:
+		pr_cont("UNKNOWN (%d)\n", cfg->type);
+		break;
+	}
+	pr_debug("channel : ");
+	switch (cfg->channel) {
+	case eray_msgbuf_ch_none:
+		pr_cont("NONE\n");
+		break;
+	case eray_msgbuf_ch_a:
+		pr_cont("CH A\n");
+		break;
+	case eray_msgbuf_ch_b:
+		pr_cont("CH B\n");
+		break;
+	case eray_msgbuf_ch_both:
+		pr_cont("BOTH\n");
+		break;
+	default:
+		pr_cont("UNKNOWN (%d)\n", cfg->channel);
+		break;
+	}
+}
+#else
+static inline void eray_dump_msg_cfg(struct eray_msgbuf_cfg *cfg, int buf,
+		const char *func)
+{ }
+#endif
+
+static struct net_device *get_dev(struct genl_info *info)
+{
+	struct net_device *dev = NULL;
+	struct nlattr *nla;
+
+	nla = info->attrs[FC_MSGBUF_ATTR_DEV_NAME];
+	if (nla)
+		dev = dev_get_by_name(&init_net, nla_data(nla));
+	if (dev)
+		return dev;
+	nla = info->attrs[FC_MSGBUF_ATTR_DEV_ID];
+	if (nla)
+		dev = dev_get_by_index(&init_net, nla_get_u32(nla));
+
+	return dev;
+}
+
+static void fc2eray(struct fc_msgbuf_cfg *src, struct eray_msgbuf_cfg *dest)
+{
+	dest->flags = src->flags;
+	dest->cyc = src->cyc;
+	dest->len = src->len;
+	dest->max = src->max;
+	dest->frame_id = src->frame_id;
+	dest->reject_mask = src->reject_mask;
+	dest->type = src->type;
+	dest->channel = src->channel;
+}
+
+static void eray2fc(struct eray_msgbuf_cfg *src, struct fc_msgbuf_cfg *dest)
+{
+	dest->flags = src->flags;
+	dest->cyc = src->cyc;
+	dest->len = src->len;
+	dest->max = src->max;
+	dest->frame_id = src->frame_id;
+	dest->reject_mask = src->reject_mask;
+	dest->type = src->type;
+	dest->channel = src->channel;
+}
+
+static int remain_buffer_entries(struct eray_cc *cc)
+{
+	int i, fill;
+
+	/* calculate buffer fill value */
+	fill = 0;
+	for (i = 0; i < cc->act_cfg; i++) {
+		fill += ERAY_MSGBUF_CFG_LEN;
+		if (cc->cfg[i].type == eray_msgbuf_type_rx ||
+		    cc->cfg[i].type == eray_msgbuf_type_tx)
+			fill += DIV_ROUND_UP(cc->cfg[i].max, 2);
+		else
+			fill += DIV_ROUND_UP(cc->fifo_len, 2);
+	}
+
+	return ERAY_MAX_MEM - fill;
+}
+
+static int fc_msgbuf_get_cfg(struct sk_buff *_skb, struct genl_info *info)
+{
+	struct fc_msgbuf_cfg cfg;
+	struct fc_msgbuf_cfg *nlcfg;
+	struct flexcard_priv *priv;
+	struct net_device *dev;
+	struct eray_cc *cc;
+	struct sk_buff *skb;
+	struct nlattr *nla;
+	unsigned long flags;
+	void *msg_head;
+	int ret;
+	u8 buf_id;
+
+	dev = get_dev(info);
+	if (!dev)
+		return -ENODEV;
+
+	priv = netdev_priv(dev);
+	cc = priv->cc;
+
+	if (dev->type != ARPHRD_FLEXRAY) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	nla = info->attrs[FC_MSGBUF_ATTR_BUF_ID];
+	if (!nla) {
+		ret = -ENOMSG;
+		goto out;
+	}
+	buf_id = nla_get_u8(nla);
+
+	nla = info->attrs[FC_MSGBUF_ATTR_CFG];
+	if (!nla) {
+		netdev_warn(dev, "no config\n");
+		ret = -ENOMSG;
+		goto out;
+	}
+	nlcfg = nla_data(nla);
+
+	spin_lock_irqsave(&cc->lock, flags);
+
+	if (nlcfg->flags & FC_MSGBUF_SELFSYNC) {
+		if (buf_id >= cc->ssync_num) {
+			netdev_warn(dev, "invalid self sync buffer id %d\n",
+					buf_id);
+			ret = -ENOMSG;
+			spin_unlock_irqrestore(&cc->lock, flags);
+			goto out;
+		}
+
+		eray2fc(&cc->ssync_cfg[buf_id], &cfg);
+		cfg.buf_id = buf_id;
+	} else {
+		if (buf_id >= cc->act_cfg) {
+			netdev_warn(dev, "invalid buffer id %d\n", buf_id);
+			ret = -ENOMSG;
+			spin_unlock_irqrestore(&cc->lock, flags);
+			goto out;
+		}
+
+		eray2fc(&cc->cfg[buf_id], &cfg);
+		cfg.buf_id = buf_id;
+		/* re-adjust length of fifo buffers */
+		if (cfg.type == eray_msgbuf_type_fifo)
+			cfg.len = cc->fifo_len;
+	}
+	spin_unlock_irqrestore(&cc->lock, flags);
+
+	skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!skb) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	msg_head = genlmsg_put_reply(skb, info, &fc_msgbuf_genl_family,
+				      0, FC_MSGBUF_CMD_GET_CFG);
+	if (!msg_head) {
+		ret = -ENOMEM;
+		goto free_skb_out;
+	}
+
+	ret = nla_put_u8(skb, FC_MSGBUF_ATTR_BUF_ID, buf_id);
+	if (ret)
+		goto free_skb_out;
+
+	ret = nla_put(skb, FC_MSGBUF_ATTR_CFG, sizeof(cfg), &cfg);
+	if (ret)
+		goto free_skb_out;
+
+	genlmsg_end(skb, msg_head);
+
+	ret = genlmsg_reply(skb, info);
+out:
+	dev_put(dev);
+
+	return ret;
+
+free_skb_out:
+	kfree_skb(skb);
+	goto out;
+}
+
+static int fc_msgbuf_reset_cfg(struct sk_buff *_skb, struct genl_info *info)
+{
+	struct eray_msgbuf_cfg *cfg;
+	struct fc_msgbuf_cfg *nlcfg;
+	struct flexcard_priv *priv;
+	struct net_device *dev;
+	struct nlattr *nla;
+	struct eray_cc *cc;
+	unsigned long flags;
+	int i, ret = -EINVAL;
+
+	dev = get_dev(info);
+	if (!dev)
+		return -ENODEV;
+
+	if (dev->type != ARPHRD_FLEXRAY)
+		goto out;
+
+	priv = netdev_priv(dev);
+	cc = priv->cc;
+
+	nla = info->attrs[FC_MSGBUF_ATTR_CFG];
+	if (!nla) {
+		netdev_warn(dev, "no config\n");
+		ret = -ENOMSG;
+		goto out;
+	}
+	nlcfg = nla_data(nla);
+
+	spin_lock_irqsave(&cc->lock, flags);
+
+	if (nlcfg->flags & FC_MSGBUF_SELFSYNC) {
+		cc->ssync_start = 0;
+		cc->ssync_num = 0;
+		memset(&cc->ssync_cfg, 0x0, sizeof(cc->ssync_cfg));
+
+		ret = 0;
+		goto out_unlock;
+	}
+
+	cc->ready = 0;
+
+	for (i = 0; i < ERAY_MAX_BUFS; i++) {
+		cc->cfg[i].flags = 0;
+		cc->cfg[i].len = 0;
+		cc->cfg[i].max = 0;
+		cc->cfg[i].cyc = 0;
+		cc->cfg[i].channel = eray_msgbuf_ch_none;
+	}
+	cc->act_cfg = 1;
+	cc->fifo_len = 0;
+	cc->sync_start = 0;
+	cc->sync_num = 0;
+	memset(&cc->sync_cfg, 0x0, sizeof(cc->sync_cfg));
+
+	cc->fifo_threshold = ERAY_FIFO_THRESHOLD;
+
+	cfg = &cc->cfg[0];
+	cfg->flags |= ERAY_MSGBUF_USED;
+	cfg->type = eray_msgbuf_type_fifo;
+	cfg->max = 127;
+
+	/* CLEAR_RAMS can only be called in DEFAULT_CONFIG or CONFIG mode */
+	ret = cc_change_state(cc, ERAY_CMD_CONFIG, 5);
+	if (ret < 0) {
+		netdev_err(dev, "CC DEFAULT_CONFIG failed\n");
+		goto out_unlock;
+	}
+
+	ret = cc_change_state(cc, ERAY_CMD_CLEAR_RAMS, 15);
+	if (ret < 0)
+		netdev_err(dev, "%s: CC CLEAR_RAMS failed\n", __func__);
+out_unlock:
+	spin_unlock_irqrestore(&cc->lock, flags);
+out:
+	dev_put(dev);
+	return ret;
+}
+
+static int fc_msgbuf_read_cfg(struct sk_buff *_skb, struct genl_info *info)
+{
+	struct eray_msgbuf_cfg *cfg;
+	struct flexcard_priv *priv;
+	struct net_device *dev;
+	struct eray_cc *cc;
+	unsigned long flags;
+	u32 reg;
+	u8 fdb, ffb, lcb, splm, act = 1;
+	int i, ret = -EINVAL;
+
+	dev = get_dev(info);
+	if (!dev)
+		return -ENODEV;
+
+	if (dev->type != ARPHRD_FLEXRAY)
+		goto out;
+
+	priv = netdev_priv(dev);
+	cc = priv->cc;
+
+	spin_lock_irqsave(&cc->lock, flags);
+
+	for (i = 0; i < ERAY_MAX_BUFS; i++) {
+		cc->cfg[i].flags = 0;
+		cc->cfg[i].len = 0;
+		cc->cfg[i].max = 0;
+		cc->cfg[i].cyc = 0;
+		cc->cfg[i].channel = eray_msgbuf_ch_both;
+	}
+
+	cc->fifo_threshold = ERAY_FIFO_THRESHOLD;
+
+	/* The FlexCard firmware needs one or more configured FIFO buffer
+	 * to receive FlexRay frames.
+	 */
+
+	cfg = &cc->cfg[0];
+	cfg->flags |= ERAY_MSGBUF_USED;
+	cfg->type = eray_msgbuf_type_fifo;
+	cfg->max = 127;
+
+	reg = eray_readl(cc, ERAY_FRF);
+	cfg->channel = (reg & ERAY_FRF_CH_MASK) >> ERAY_FRF_CH_SHIFT;
+	cfg->frame_id = (reg & ERAY_FRF_FID_MASK) >> ERAY_FRF_FID_SHIFT;
+	cfg->cyc = (reg & ERAY_FRF_CYC_MASK) >> ERAY_FRF_CYC_SHIFT;
+	if (reg & ERAY_FRF_RNF_MASK)
+		cfg->flags |= ERAY_MSGBUF_FIFOREJ_NULL;
+	if (reg & ERAY_FRF_RSS_MASK)
+		cfg->flags |= ERAY_MSGBUF_FIFOREJ_INSEG;
+
+	reg = eray_readl(cc, ERAY_FRFM);
+	cfg->reject_mask = (reg & ERAY_FRFM_MFID_MASK) >> ERAY_FRFM_MFID_SHIFT;
+
+	cc->act_cfg = 1;
+
+	reg = eray_readl(cc, ERAY_MRC);
+	lcb = (reg & ERAY_MRC_LCB_MASK) >> ERAY_MRC_LCB_SHIFT;
+	ffb = (reg & ERAY_MRC_FFB_MASK) >> ERAY_MRC_FFB_SHIFT;
+	fdb = (reg & ERAY_MRC_FDB_MASK) >> ERAY_MRC_FDB_SHIFT;
+	splm = (reg & ERAY_MRC_SPLM_MASK) >> ERAY_MRC_SPLM_SHIFT;
+
+	for (i = 0; i < lcb + 1; i++) {
+		eray_writel(ERAY_OBCM_RHSS_MASK, cc, ERAY_OBCM);
+		eray_writel(i | ERAY_OBCR_REQ_MASK, cc, ERAY_OBCR);
+		ret = eray_wait_clear(cc, ERAY_OBCR, ERAY_OBCR_OBSYS_MASK, 10);
+		if (ret)
+			goto out_unlock;
+
+		eray_writel(ERAY_OBCR_VIEW_MASK, cc, ERAY_OBCR);
+		eray_readl(cc, ERAY_OBCR);
+
+		cfg = &cc->cfg[act];
+
+		cfg->wrhs1 = eray_readl(cc, ERAY_RDHS1);
+		cfg->wrhs2 = eray_readl(cc, ERAY_RDHS2);
+		cfg->wrhs3 = eray_readl(cc, ERAY_RDHS3);
+
+		cfg->id = i;
+		cc->rev_id[i] = cfg->id;
+		cfg->flags |= ERAY_MSGBUF_USED;
+
+		if (i > ffb) {
+			cfg->type = eray_msgbuf_type_fifo;
+			cfg->max = (cfg->wrhs2 >> ERAY_WRHS2_PLC_SHIFT) &
+				ERAY_WRHS2_PLC_MASK;
+			if (!cc->fifo_len)
+				cc->fifo_len = cfg->max;
+
+			/* copy fifo reject configuration from message buffer 0
+			 */
+			cfg->channel = cc->cfg[0].channel;
+			cfg->frame_id = cc->cfg[0].frame_id;
+			cfg->cyc = cc->cfg[0].cyc;
+			cfg->reject_mask = cc->cfg[0].reject_mask;
+
+			if (cc->fifo_threshold)
+				cc->fifo_threshold--;
+		} else {
+			if (cfg->wrhs1 & ERAY_WRHS1_CFG_MASK)
+				cfg->type = eray_msgbuf_type_tx;
+			else
+				cfg->type = eray_msgbuf_type_rx;
+
+			/* WRHS1:
+			 * 31-16: MBI:TXM:PPIT:CFG:CHB:CHA:0:CYC[6..0]
+			 * 15- 0: 00000:FID[10..0]
+			 */
+			cfg->frame_id = (cfg->wrhs1 & ERAY_WRHS1_FID_MASK) >>
+				ERAY_WRHS1_FID_SHIFT;
+
+			if (!cfg->frame_id)
+				continue;
+
+			cfg->cyc = (cfg->wrhs1 & ERAY_WRHS1_CYC_MASK) >>
+				ERAY_WRHS1_CYC_SHIFT;
+			cfg->channel = (cfg->wrhs1 & ERAY_WRHS1_CH_MASK) >>
+				ERAY_WRHS1_CH_SHIFT;
+			if (cfg->wrhs1 & ERAY_WRHS1_PPIT_MASK)
+				cfg->flags |= ERAY_MSGBUF_PPIT;
+
+			/* if ERAY_WRHS1_TXM is *not* set the ERAY core
+			 * works in continous mode.
+			 */
+			if ((cfg->type == eray_msgbuf_type_tx) &&
+			    !(cfg->wrhs1 & ERAY_WRHS1_TXM_MASK))
+				cfg->flags |= ERAY_MSGBUF_TXCONT;
+
+			/* WRHS2:
+			 * 31- 0: 000000000:PLC[6-0]:00000:CRC[10..0]
+			 */
+			cfg->max = (cfg->wrhs2 & ERAY_WRHS2_PLC_MASK) >>
+				ERAY_WRHS2_PLC_SHIFT;
+		}
+		cfg->len = cfg->max;
+		act++;
+
+		eray_dump_msg_cfg(cfg, i, __func__);
+	}
+
+	reg = eray_read_succ1(cc, 10);
+	if (reg < 0) {
+		ret = -EBUSY;
+		goto out_unlock;
+	}
+
+	if (reg & ERAY_SUCC1_TXST_MASK) {
+		cc->sync_start |= ERAY_MSGBUF_STARTUP;
+		cc->cfg[1].flags |= ERAY_MSGBUF_STARTUP;
+		if (splm)
+			cc->cfg[2].flags |= ERAY_MSGBUF_STARTUP;
+	}
+
+	if (reg & ERAY_SUCC1_TXSY_MASK) {
+		cc->sync_start |= ERAY_MSGBUF_SYNC;
+		cc->cfg[1].flags |= ERAY_MSGBUF_SYNC;
+		if (splm)
+			cc->cfg[2].flags |= ERAY_MSGBUF_SYNC;
+	}
+
+	cc_get_conf(dev);
+	ret = 0;
+	cc->ready = 1;
+	cc->act_cfg = act;
+
+out_unlock:
+	spin_unlock_irqrestore(&cc->lock, flags);
+out:
+	dev_put(dev);
+	return ret;
+}
+
+static int is_double_sync(struct fc_msgbuf_cfg *cfg,
+		struct eray_msgbuf_cfg *sync_cfg, int sync_num)
+{
+	if (!sync_num)
+		return 0;
+
+	if (sync_num > 1)
+		return 1;
+
+	if ((cfg->cyc != sync_cfg->cyc) ||
+	    (cfg->len != sync_cfg->len) ||
+	    (cfg->max != sync_cfg->max) ||
+	    (cfg->frame_id != sync_cfg->frame_id))
+		return 1;
+
+	if (cfg->channel == eray_msgbuf_ch_both)
+		return 1;
+
+	if ((sync_cfg->channel == eray_msgbuf_ch_a) &&
+	    (cfg->channel == eray_msgbuf_ch_a))
+		return 1;
+
+	if ((sync_cfg->channel == eray_msgbuf_ch_b) &&
+	    (cfg->channel == eray_msgbuf_ch_b))
+		return 1;
+
+	return 0;
+}
+
+static int fc_msgbuf_set_cfg_ssync(struct net_device *dev,
+		struct fc_msgbuf_cfg *cfg, struct eray_cc *cc, int aquire)
+{
+	u8 buf_id;
+
+	if (aquire) {
+		buf_id = cc->ssync_num;
+	} else {
+		buf_id = cfg->buf_id;
+
+		if (!(cc->ssync_start & ERAY_MSGBUF_SYNC)) {
+			netdev_warn(dev, "self sync not yet configured\n");
+			return -ENOMSG;
+		}
+
+		if (buf_id > cc->ssync_num) {
+			netdev_warn(dev, "unused buffer id %d\n", buf_id);
+			return -ENOMSG;
+		}
+	}
+
+	if (buf_id > ERAY_MAX_BUFS_SSYNC) {
+		netdev_warn(dev, "invalid buffer id %d\n", buf_id);
+		return -EINVAL;
+	}
+
+	if (!(cfg->flags & ERAY_MSGBUF_SYNC) ||
+	    !(cfg->flags & ERAY_MSGBUF_STARTUP)) {
+		netdev_warn(dev, "no sync/start frame\n");
+		return -EINVAL;
+	}
+
+	if (cfg->type != eray_msgbuf_type_tx) {
+		netdev_warn(dev, "wrong type (!tx)\n");
+		return -EINVAL;
+	}
+
+	if (cfg->cyc > 1) {
+		netdev_warn(dev, "cycle counter filter not valid\n");
+		return -EINVAL;
+	}
+
+	if (cfg->channel == eray_msgbuf_ch_none) {
+		netdev_warn(dev, "channel not valid for sync (none)\n");
+		return -EINVAL;
+	}
+
+	/* cc->ssync_num == 0 is coverd by is_double_sync() */
+	if (is_double_sync(cfg, &cc->ssync_cfg[0], cc->ssync_num)) {
+		netdev_warn(dev, "double sync frame\n");
+		return -EINVAL;
+	}
+
+	if (cfg->frame_id > cc->static_id) {
+		netdev_warn(dev, "sync frame not in static segment\n");
+		return -EINVAL;
+	}
+
+	cfg->buf_id = buf_id;
+
+	cc->ssync_start |= ERAY_MSGBUF_SYNC;
+	cc->ssync_start |= ERAY_MSGBUF_STARTUP;
+
+	fc2eray(cfg, &cc->ssync_cfg[buf_id]);
+	cc->ssync_cfg[buf_id].flags |= ERAY_MSGBUF_USED;
+
+	if (aquire)
+		cc->ssync_num++;
+
+	return 0;
+}
+
+static int fc_msgbuf_set_cfg(struct sk_buff *_skb, struct genl_info *info)
+{
+	struct fc_msgbuf_cfg *cfg;
+	struct flexcard_priv *priv;
+	struct net_device *dev;
+	struct eray_cc *cc;
+	struct sk_buff *skb;
+	struct nlattr *nla;
+	unsigned long flags;
+	void *msg_head;
+	int aquire, ret;
+	int i;
+	u8 buf_id;
+
+	dev = get_dev(info);
+	if (!dev)
+		return -ENODEV;
+
+	if (dev->type != ARPHRD_FLEXRAY) {
+		netdev_warn(dev, "device is not a FlexRay device\n");
+		ret = -EINVAL;
+		goto out;
+	}
+	priv = netdev_priv(dev);
+	cc = priv->cc;
+
+	spin_lock_irqsave(&cc->lock, flags);
+	cc->ready = 0;
+	nla = info->attrs[FC_MSGBUF_ATTR_BUF_ID];
+	if (nla) {
+		buf_id = nla_get_u8(nla);
+		aquire = 0;
+	} else {
+		buf_id = cc->act_cfg;
+		aquire = 1;
+	}
+
+	nla = info->attrs[FC_MSGBUF_ATTR_CFG];
+	if (!nla) {
+		netdev_warn(dev, "no config\n");
+		ret = -ENOMSG;
+		goto out_unlock;
+	}
+	cfg = nla_data(nla);
+
+	if (cfg->flags & FC_MSGBUF_SELFSYNC) {
+		ret = fc_msgbuf_set_cfg_ssync(dev, cfg, cc, aquire);
+		if (ret)
+			goto out_unlock;
+
+		buf_id = cfg->buf_id;
+		goto nlreply_unlock;
+	}
+
+	if (aquire) {
+		/* The ERAY core can only allocate memory in 32-bit chunks,
+		 * while FlexRay is based on 16-bit words. To allocate a
+		 * proper amount of chunks use M = (N + 1)/2
+		 * M = number of chunks (32bit)
+		 * N = number of FlexRay words (16bit)
+		 */
+
+		if (remain_buffer_entries(cc) < 0) {
+			netdev_warn(dev, "no room for header\n");
+			ret = -ENOMEM;
+			goto out_unlock;
+		}
+	}
+
+	if (buf_id >= ERAY_MAX_BUFS) {
+		netdev_warn(dev, "invalid buffer id %d\n", buf_id);
+		ret = -ERANGE;
+		goto out_unlock;
+	}
+
+	if (!aquire && !(cc->cfg[buf_id].flags & ERAY_MSGBUF_USED)) {
+		netdev_warn(dev, "buffer %d not in use\n", buf_id);
+		ret = -ENOMSG;
+		goto out_unlock;
+	}
+
+	if (cfg->max > 254) {
+		netdev_warn(dev,
+			  "payload len %d no valid\n", cfg->max);
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
+	if (cfg->type == eray_msgbuf_type_fifo) {
+		cfg->flags &= ~ERAY_MSGBUF_STARTUP;
+		cfg->flags &= ~ERAY_MSGBUF_SYNC;
+
+		if (cc->fifo_threshold)
+			cc->fifo_threshold--;
+	}
+
+	if ((cc->act_cfg + cc->fifo_threshold) >= ERAY_MAX_BUFS) {
+		netdev_warn(dev, "min fifo limit reached\n");
+		ret = -E2BIG;
+		goto out_unlock;
+	}
+
+	if (cfg->flags & ERAY_MSGBUF_SYNC) {
+		if (cfg->channel == eray_msgbuf_ch_none) {
+			netdev_warn(dev,
+				    "channel not valid for sync (none)\n");
+			ret = -EINVAL;
+			goto out_unlock;
+		}
+		if (is_double_sync(cfg, &cc->sync_cfg, cc->sync_num)) {
+			netdev_warn(dev, "double sync frame\n");
+			ret = -EINVAL;
+			goto out_unlock;
+		}
+		cc->sync_start |= ERAY_MSGBUF_SYNC;
+		cc->sync_num++;
+		fc2eray(cfg, &cc->sync_cfg);
+	}
+
+	if (cfg->flags & ERAY_MSGBUF_STARTUP) {
+		if (!(cfg->flags & ERAY_MSGBUF_SYNC)) {
+			netdev_warn(dev,
+				    "startup frame is not sync frame\n");
+			ret = -EINVAL;
+			goto out_unlock;
+		}
+		cc->sync_start |= ERAY_MSGBUF_STARTUP;
+	}
+
+	if ((cfg->flags & ERAY_MSGBUF_SYNC) &&
+	    (cfg->frame_id > cc->static_id)) {
+		netdev_warn(dev, "sync frame not in static segment\n");
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
+
+	/* avoid duplicate configuration */
+	if (aquire && cfg->type != eray_msgbuf_type_fifo) {
+		for (i = 0; i < cc->act_cfg; i++) {
+			if ((cc->cfg[i].frame_id == cfg->frame_id) &&
+			    (cc->cfg[i].channel & cfg->channel) &&
+			    (cc->cfg[i].cyc ==  cfg->cyc)) {
+				netdev_warn(dev, "duplicate configuration\n");
+				ret = -EINVAL;
+				goto out_unlock;
+			}
+		}
+	}
+
+	if (cfg->type == eray_msgbuf_type_fifo) {
+		if (!cc->fifo_len)
+			cc->fifo_len = cfg->max;
+		else if (cfg->max != cc->fifo_len) {
+			netdev_warn(dev, "payload len %d != %d\n",
+				  cfg->max, cc->fifo_len);
+			ret = -EINVAL;
+			goto out_unlock;
+		}
+		/* copy fifo reject configuration to message buffer 0 */
+		cc->cfg[0].channel = cfg->channel;
+		cc->cfg[0].frame_id = cfg->frame_id;
+		cc->cfg[0].cyc = cfg->cyc;
+		cc->cfg[0].reject_mask = cfg->reject_mask;
+	}
+
+	if (cfg->type == eray_msgbuf_type_tx) {
+		if (cfg->len > cfg->max) {
+			netdev_warn(dev, "payload len %d > payload len %d\n",
+					cfg->len, cfg->max);
+			ret = -EINVAL;
+			goto out_unlock;
+		}
+		/* reject messagebuffer if it's static and the length is bigger
+		 * than the cluster wide static messagebuffer length
+		 */
+		if (cfg->frame_id <= cc->static_id &&
+				cfg->len > cc->static_len) {
+			netdev_warn(dev, "payload len %d > static_len %d\n",
+					cfg->len, cc->static_len);
+			ret = -EINVAL;
+			goto out_unlock;
+		}
+		if (cfg->channel == eray_msgbuf_ch_both &&
+				cfg->frame_id > cc->static_id) {
+			netdev_warn(dev, "both channel in dynamic frame\n");
+			ret = -EINVAL;
+			goto out_unlock;
+		}
+	}
+
+	if ((remain_buffer_entries(cc) - ERAY_MSGBUF_CFG_LEN -
+				DIV_ROUND_UP(cfg->max, 2)) < 0) {
+		netdev_warn(dev, "no room for buffer\n");
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
+	fc2eray(cfg, &cc->cfg[buf_id]);
+
+	cc->cfg[buf_id].flags |= ERAY_MSGBUF_USED;
+	if (aquire)
+		cc->act_cfg++;
+nlreply_unlock:
+	spin_unlock_irqrestore(&cc->lock, flags);
+
+	skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!skb) {
+		netdev_warn(dev, "could not allocate skb\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	msg_head = genlmsg_put_reply(skb, info, &fc_msgbuf_genl_family, 0,
+			FC_MSGBUF_CMD_SET_CFG);
+	if (!msg_head) {
+		netdev_warn(dev, "generating NL reply failed\n");
+		ret = -ENOMEM;
+		goto out_free_skb;
+	}
+	ret = nla_put_u8(skb, FC_MSGBUF_ATTR_BUF_ID, buf_id);
+	if (ret) {
+		netdev_warn(dev, "could not add buffer id\n");
+		ret = -EINVAL;
+		goto out_free_skb;
+	}
+	genlmsg_end(skb, msg_head);
+
+	ret = genlmsg_reply(skb, info);
+out:
+	dev_put(dev);
+	return ret;
+
+out_free_skb:
+	kfree(skb);
+	goto out;
+
+out_unlock:
+	spin_unlock_irqrestore(&cc->lock, flags);
+	dev_put(dev);
+	return ret;
+}
+
+static struct genl_ops fc_msgbuf_genl_ops[] = {
+	{
+		.cmd = FC_MSGBUF_CMD_GET_CFG,
+		.policy = fc_msgbuf_genl_policy,
+		.doit = fc_msgbuf_get_cfg,
+	},
+	{
+		.cmd = FC_MSGBUF_CMD_SET_CFG,
+		.policy = fc_msgbuf_genl_policy,
+		.doit = fc_msgbuf_set_cfg,
+	},
+	{
+		.cmd = FC_MSGBUF_CMD_RESET_CFG,
+		.policy = fc_msgbuf_genl_policy,
+		.doit = fc_msgbuf_reset_cfg,
+	},
+	{
+		.cmd = FC_MSGBUF_CMD_READ_CFG,
+		.policy = fc_msgbuf_genl_policy,
+		.doit = fc_msgbuf_read_cfg,
+	},
+};
+
+/* calculate header CRC as specified in FlexRay Protocol Specification V2.1
+ * Rev.A chapter 4.2.8
+ */
+static u32 crc_header(struct eray_msgbuf_cfg *cfg)
+{
+	u32 val = 0x1a;
+	u32 crc_next;
+	u32 next_bit;
+	u32 data;
+	int i;
+
+	data = cfg->len & (ERAY_WRHS2_PLC_MASK >> ERAY_WRHS2_PLC_SHIFT);
+	data |= (cfg->frame_id & ERAY_WRHS1_FID_MASK) << 7;
+	if (cfg->flags & ERAY_MSGBUF_STARTUP)
+		data |= 1 << 18;
+	if (cfg->flags & ERAY_MSGBUF_SYNC)
+		data |= 1 << 19;
+
+	for (i = 19; i >= 0; i--) {
+		next_bit = (data >> i) & 0x1;
+		crc_next = next_bit ^ ((val >> 10) & 0x1);
+
+		val <<= 1;
+		val &= 0xfffffffe;
+
+		if (crc_next)
+			val ^= 0x385;
+
+		val &= 0x7ff;
+	}
+
+	return val & ERAY_WRHS2_CRC_MASK;
+}
+
+static int fc_wait_for_ram_offset(struct eray_cc *cc, uint32_t offset)
+{
+	int ret;
+
+	/* wait until host and shadow RAM actions are finished */
+	ret = eray_wait_clear(cc, ERAY_IBCR + offset, ERAY_IBCR_IBSYH_MASK, 10);
+	if (ret)
+		goto out;
+
+	ret = eray_wait_clear(cc, ERAY_IBCR + offset, ERAY_IBCR_IBSYS_MASK, 10);
+out:
+	return ret;
+}
+
+static int fc_wait_for_ram(struct eray_cc *cc)
+{
+	return fc_wait_for_ram_offset(cc, 0);
+}
+
+static int fc_prepare_msgbuf_data_ssync(struct net_device *dev)
+{
+	struct flexcard_priv *priv = netdev_priv(dev);
+	struct eray_cc *cc = priv->cc;
+	struct eray_msgbuf_cfg *cfg;
+	u8 fdb, ffb, lcb, splm;
+	u32 reg, dp = ERAY_MAX_MEM;
+	int i, ret;
+
+	if (!cc->ssync_num)
+		return 0;
+
+	fdb = 0x80;	/* only static msgbuf in self sync */
+	ffb = 0x80;	/* non FIFO in self sync */
+	lcb = cc->ssync_num ? cc->ssync_num - 1 : 0x80;
+	splm = (cc->ssync_num > 1) ? 1 : 0;
+
+	eray_chg_reg(fdb, cc, ERAY_MRC + FC_SSYNC_OFFSET,
+		     ERAY_MRC_FDB_MASK, ERAY_MRC_FDB_SHIFT);
+	eray_chg_reg(ffb, cc, ERAY_MRC + FC_SSYNC_OFFSET,
+		     ERAY_MRC_FFB_MASK, ERAY_MRC_FFB_SHIFT);
+	eray_chg_reg(lcb, cc, ERAY_MRC + FC_SSYNC_OFFSET,
+		     ERAY_MRC_LCB_MASK, ERAY_MRC_LCB_SHIFT);
+	eray_chg_reg(splm, cc, ERAY_MRC + FC_SSYNC_OFFSET,
+		     ERAY_MRC_SPLM_MASK, ERAY_MRC_SPLM_SHIFT);
+
+	for (i = 0; i < cc->ssync_num; i++) {
+		cfg = &cc->ssync_cfg[i];
+
+		/* self sync need no mapping */
+		cfg->id = i;
+
+		cfg->len = cc->static_len;
+
+		/* WRHS1 */
+		reg = (cfg->frame_id << ERAY_WRHS1_FID_SHIFT) &
+			ERAY_WRHS1_FID_MASK;
+		reg |= (cfg->cyc << ERAY_WRHS1_CYC_SHIFT) &
+			ERAY_WRHS1_CYC_MASK;
+		reg |= (cfg->channel << ERAY_WRHS1_CH_SHIFT) &
+			ERAY_WRHS1_CH_MASK;
+		reg |= ERAY_WRHS1_CFG_MASK;
+		if (cfg->flags & ERAY_MSGBUF_PPIT)
+			reg |= ERAY_WRHS1_PPIT_MASK;
+		if (cfg->flags & FC_MSGBUF_ACK)
+			reg |= ERAY_WRHS1_MBI_MASK;
+
+		/* if ERAY_WRHS1_TXM is *not* set the ERAY core
+		 * works in continous mode.
+		 */
+		if (!(cfg->flags & ERAY_MSGBUF_TXCONT))
+			reg |= ERAY_WRHS1_TXM_MASK;
+		cfg->wrhs1 = reg;
+
+		/* WRHS2 */
+		reg = crc_header(cfg) & ERAY_WRHS2_CRC_MASK;
+		reg |= (cfg->max << ERAY_WRHS2_PLC_SHIFT) &
+			ERAY_WRHS2_PLC_MASK;
+		cfg->wrhs2 = reg;
+
+		/* WRHS3 */
+		dp -= DIV_ROUND_UP(cfg->max, 2);
+		reg = dp & ERAY_WRHS3_DP_MASK;
+		cfg->wrhs3 = reg;
+
+		eray_dump_msg_cfg(cfg, i, __func__);
+
+		ret = fc_wait_for_ram_offset(cc, FC_SSYNC_OFFSET);
+		if (ret)
+			goto out;
+
+		eray_writel(cfg->wrhs1, cc, ERAY_WRHS1 + FC_SSYNC_OFFSET);
+		eray_writel(cfg->wrhs2, cc, ERAY_WRHS2 + FC_SSYNC_OFFSET);
+		eray_writel(cfg->wrhs3, cc, ERAY_WRHS3 + FC_SSYNC_OFFSET);
+		eray_writel(ERAY_IBCM_LHSH_MASK, cc,
+				ERAY_IBCM + FC_SSYNC_OFFSET);
+
+		/* set the new configuration */
+		eray_writel(cfg->id, cc, ERAY_IBCR + FC_SSYNC_OFFSET);
+
+		ret = fc_wait_for_ram_offset(cc, FC_SSYNC_OFFSET);
+		if (ret)
+			goto out;
+
+		reg = 0x0;
+		if (cfg->flags & FC_MSGBUF_ACK) {
+			reg |= cfg->cyc << FC_BUF_INFO_CYC_SHIFT;
+			reg |= cfg->channel << FC_BUF_INFO_CHANNEL_SHIFT;
+			reg |= cfg->frame_id;
+
+			if (cfg->flags & FC_MSGBUF_ACK_PAYLOAD)
+				reg |= FC_BUF_INFO_ENABLE_PAYLOAD;
+			if (cfg->flags & FC_MSGBUF_ACK_NULL)
+				reg |= FC_BUF_INFO_ENABLE_NULLFRAMES;
+			if (cfg->type == eray_msgbuf_type_tx)
+				reg |= FC_BUF_INFO_IS_TX;
+		}
+
+		eray_writel(reg, cc, (FC_BUFFER_INFO_TABLE + i * 4) +
+				FC_SSYNC_TXACK_OFFSET);
+	}
+
+	for (i = 0; i < cc->ssync_num; i++) {
+		int tx_buf_id;
+
+		cfg = &cc->ssync_cfg[i];
+		if (cfg->flags & ERAY_MSGBUF_TXCONT) {
+			tx_buf_id = i | FC_FLEX_ID_SSYNC_FLAG;
+			ret = _fc_write_data(priv, tx_buf_id,
+					     cfg->tx_cont_data,
+					     cfg->tx_cont_len);
+			if (ret)
+				goto out;
+		}
+	}
+
+out:
+	return ret;
+}
+
+static int fc_prepare_msgbuf_data(struct net_device *dev)
+{
+	struct flexcard_priv *priv = netdev_priv(dev);
+	struct eray_cc *cc = priv->cc;
+	struct eray_msgbuf_cfg *cfg;
+	unsigned long flags;
+	u32 reg, sum_flags = 0, dp = ERAY_MAX_MEM;
+	u8 fdb, ffb, lcb, splm, ndyn = 0, nfifo = 0;
+	int i, remain, ret = -EINVAL, map_i = 0;
+	unsigned int remain_nr;
+
+	spin_lock_irqsave(&cc->lock, flags);
+
+	if (cc->act_cfg > ERAY_MAX_BUFS) {
+		netdev_err(dev, "too many msg buffers (%d)\n", cc->act_cfg);
+		goto out;
+	}
+
+	for (i = 0; i < cc->act_cfg; i++) {
+		cc->cfg[i].id = 0xff;
+		cc->cfg[i].len = 0;
+	}
+
+	if (!cc->fifo_len)
+		cc->fifo_len = 127;
+
+	remain = remain_buffer_entries(cc);
+	if (remain < 0) {
+		netdev_err(dev, "buffer memory exhausted (%d)\n",
+			   ERAY_MAX_MEM + remain);
+		goto out;
+	}
+
+	/* map all remaining message buffers as fifo buffer */
+	remain_nr = DIV_ROUND_UP(cc->fifo_len, 2) + ERAY_MSGBUF_CFG_LEN;
+	remain_nr = remain / remain_nr;
+	if (remain_nr)
+		remain_nr--;
+
+	if (cc->act_cfg + remain_nr > ERAY_MAX_BUFS)
+		remain_nr = ERAY_MAX_BUFS - cc->act_cfg;
+
+	for (i = cc->act_cfg; i < cc->act_cfg + remain_nr; i++) {
+		cc->cfg[i].flags |= ERAY_MSGBUF_USED;
+		cc->cfg[i].id = 0xff;
+		cc->cfg[i].type = eray_msgbuf_type_fifo;
+
+		/* copy fifo reject configuration from message buffer 0 */
+		cc->cfg[i].channel = cc->cfg[0].channel;
+		cc->cfg[i].frame_id = cc->cfg[0].frame_id;
+		cc->cfg[i].cyc = cc->cfg[0].cyc;
+		cc->cfg[i].reject_mask = cc->cfg[0].reject_mask;
+	}
+	cc->act_cfg += remain_nr;
+
+	eray_get_val16(&cc->static_id, cc,
+		      ERAY_GTUC7, ERAY_GTUC7_NSS_MASK, ERAY_GTUC7_NSS_SHIFT);
+
+	eray_get_val8(&cc->static_len, cc,
+		      ERAY_MHDC, ERAY_MHDC_SFDL_MASK, ERAY_MHDC_SFDL_SHIFT);
+
+	/* Map sync buffers first */
+	for (i = 0; i < cc->act_cfg; i++) {
+		cfg = &cc->cfg[i];
+
+		if (cfg->type != eray_msgbuf_type_tx)
+			continue;
+		if (cfg->frame_id == 0)
+			continue;
+		if (!(cfg->flags & ERAY_MSGBUF_SYNC))
+			continue;
+
+		cfg->id = map_i;
+		cc->rev_id[map_i] = cfg->id;
+		map_i++;
+		ndyn++;
+	}
+
+	/* then map tx buffers */
+	for (i = 0; i < cc->act_cfg; i++) {
+		cfg = &cc->cfg[i];
+
+		if (cfg->type != eray_msgbuf_type_tx)
+			continue;
+		if (cfg->frame_id == 0)
+			continue;
+		if (cfg->flags & ERAY_MSGBUF_SYNC)
+			continue;
+
+		cfg->id = map_i;
+		cc->rev_id[map_i] = cfg->id;
+		map_i++;
+		ndyn++;
+	}
+
+	/* then map RX buffers */
+	for (i = 0; i < cc->act_cfg; i++) {
+		cfg = &cc->cfg[i];
+
+		if (cfg->type != eray_msgbuf_type_rx)
+			continue;
+
+		cfg->id = map_i;
+		cc->rev_id[map_i] = cfg->id;
+		map_i++;
+		ndyn++;
+	}
+
+	/* then map fifo buffers */
+	for (i = 0; i < cc->act_cfg; i++) {
+
+		cfg = &cc->cfg[i];
+
+		if (cfg->type != eray_msgbuf_type_fifo)
+			continue;
+
+		/* assign mapping */
+		cfg->len = cfg->max = cc->fifo_len;
+		cfg->id = map_i;
+
+		/* summarize all flags */
+		cc->rev_id[map_i] = cfg->id;
+		sum_flags |= cfg->flags;
+
+		map_i++;
+		nfifo++;
+	}
+
+	/* CLEAR_RAMS can only be called in DEFAULT_CONFIG or CONFIG mode */
+	ret = cc_change_state(cc, ERAY_CMD_CONFIG, 5);
+	if (ret < 0) {
+		netdev_err(dev, "CC DEFAULT_CONFIG failed\n");
+		goto out;
+	}
+
+	ret = cc_change_state(cc, ERAY_CMD_CLEAR_RAMS, 15);
+	if (ret < 0)
+		netdev_err(dev, "%s: CC CLEAR_RAMS failed\n", __func__);
+
+	fdb = ndyn ? 0 : 0x80;
+	ffb = nfifo ? ndyn : 0x80;
+	lcb = map_i ? map_i - 1 : 0x80;
+	splm = (cc->sync_num > 1) ? 1 : 0;
+
+	if (ndyn + nfifo != map_i)
+		netdev_warn(dev, "invalid msg buffer configuration\n");
+
+	eray_chg_reg(fdb, cc, ERAY_MRC, ERAY_MRC_FDB_MASK, ERAY_MRC_FDB_SHIFT);
+	eray_chg_reg(ffb, cc, ERAY_MRC, ERAY_MRC_FFB_MASK, ERAY_MRC_FFB_SHIFT);
+	eray_chg_reg(lcb, cc, ERAY_MRC, ERAY_MRC_LCB_MASK, ERAY_MRC_LCB_SHIFT);
+	eray_chg_reg(splm, cc, ERAY_MRC, ERAY_MRC_SPLM_MASK,
+			ERAY_MRC_SPLM_SHIFT);
+	/* setup data for registers */
+	for (i = 0; i < cc->act_cfg; i++) {
+		cfg = &cc->cfg[i];
+
+		switch (cfg->type) {
+		case eray_msgbuf_type_none:
+			continue;
+
+		case eray_msgbuf_type_fifo:
+			cfg->wrhs1 = 0;
+			cfg->wrhs2 = (cc->fifo_len << ERAY_WRHS2_PLC_SHIFT) &
+				ERAY_WRHS2_PLC_MASK;
+
+			reg = (cfg->frame_id << ERAY_FRF_FID_SHIFT) &
+				ERAY_FRF_FID_MASK;
+			reg |= (cfg->cyc << ERAY_FRF_CYC_SHIFT) &
+				ERAY_FRF_CYC_MASK;
+			reg |= (cfg->channel << ERAY_FRF_CH_SHIFT) &
+				ERAY_FRF_CH_MASK;
+			if (sum_flags & ERAY_MSGBUF_FIFOREJ_NULL)
+				reg |= ERAY_FRF_RNF_MASK;
+			if (sum_flags & ERAY_MSGBUF_FIFOREJ_INSEG)
+				reg |= ERAY_FRF_RSS_MASK;
+
+			eray_writel(reg, cc, ERAY_FRF);
+
+			reg = (cfg->reject_mask << ERAY_FRFM_MFID_SHIFT) &
+				ERAY_FRFM_MFID_MASK;
+
+			eray_writel(reg, cc, ERAY_FRFM);
+
+			break;
+
+		case eray_msgbuf_type_rx:
+			reg = (cfg->frame_id << ERAY_WRHS1_FID_SHIFT) &
+				ERAY_WRHS1_FID_MASK;
+			reg |= (cfg->cyc << ERAY_WRHS1_CYC_SHIFT) &
+				ERAY_WRHS1_CYC_MASK;
+			reg |= (cfg->channel << ERAY_WRHS1_CH_SHIFT) &
+				ERAY_WRHS1_CH_MASK;
+			cfg->wrhs1 = reg;
+
+			reg = (cfg->max << ERAY_WRHS2_PLC_SHIFT) &
+				ERAY_WRHS2_PLC_MASK;
+			cfg->wrhs2 = reg;
+			break;
+
+		case eray_msgbuf_type_tx:
+			reg = (cfg->frame_id << ERAY_WRHS1_FID_SHIFT) &
+				ERAY_WRHS1_FID_MASK;
+			reg |= (cfg->cyc << ERAY_WRHS1_CYC_SHIFT) &
+				ERAY_WRHS1_CYC_MASK;
+			reg |= (cfg->channel << ERAY_WRHS1_CH_SHIFT) &
+				ERAY_WRHS1_CH_MASK;
+			reg |= ERAY_WRHS1_CFG_MASK;
+			if (cfg->flags & ERAY_MSGBUF_PPIT)
+				reg |= ERAY_WRHS1_PPIT_MASK;
+			if (cfg->flags & FC_MSGBUF_ACK)
+				reg |= ERAY_WRHS1_MBI_MASK;
+
+			/* if ERAY_WRHS1_TXM is *not* set the ERAY core
+			 * works in continous mode.
+			 */
+			if (!(cfg->flags & ERAY_MSGBUF_TXCONT))
+				reg |= ERAY_WRHS1_TXM_MASK;
+			cfg->wrhs1 = reg;
+
+			if (cfg->frame_id <= cc->static_id)
+				cfg->len = cc->static_len;
+			else
+				cfg->len = cfg->max;
+
+			reg = crc_header(cfg) & ERAY_WRHS2_CRC_MASK;
+			reg |= (cfg->max << ERAY_WRHS2_PLC_SHIFT) &
+				ERAY_WRHS2_PLC_MASK;
+			cfg->wrhs2 = reg;
+			break;
+
+		default:
+			netdev_warn(dev, "unknown msgbuf type %d ignored\n",
+				    cfg->type);
+			continue;
+		}
+
+		/* WRHS3 */
+		dp -= DIV_ROUND_UP(cfg->max, 2);
+		reg = dp & ERAY_WRHS3_DP_MASK;
+		cfg->wrhs3 = reg;
+
+		eray_dump_msg_cfg(cfg, i, __func__);
+
+		ret = fc_wait_for_ram(cc);
+		if (ret)
+			goto out;
+
+		eray_writel(cfg->wrhs1, cc, ERAY_WRHS1);
+		eray_writel(cfg->wrhs2, cc, ERAY_WRHS2);
+		eray_writel(cfg->wrhs3, cc, ERAY_WRHS3);
+		eray_writel(ERAY_IBCM_LHSH_MASK, cc, ERAY_IBCM);
+
+		/* set the new configuration */
+		eray_writel(cfg->id, cc, ERAY_IBCR);
+
+		ret = fc_wait_for_ram(cc);
+		if (ret)
+			goto out;
+
+		reg = 0x0;
+		if (cfg->flags & FC_MSGBUF_ACK) {
+			reg |= cfg->cyc << FC_BUF_INFO_CYC_SHIFT;
+			reg |= cfg->channel << FC_BUF_INFO_CHANNEL_SHIFT;
+			reg |= cfg->frame_id;
+
+			if (cfg->flags & FC_MSGBUF_ACK_PAYLOAD)
+				reg |= FC_BUF_INFO_ENABLE_PAYLOAD;
+			if (cfg->flags & FC_MSGBUF_ACK_NULL)
+				reg |= FC_BUF_INFO_ENABLE_NULLFRAMES;
+			if (cfg->type == eray_msgbuf_type_tx)
+				reg |= FC_BUF_INFO_IS_TX;
+		}
+
+		eray_writel(reg, cc, FC_BUFFER_INFO_TABLE + i*4);
+	}
+
+	/* To setup E-Ray to send startup/sync frames the appropriate
+	 * bits in the SUCC1 must be set. To carry out the configuration
+	 * a ALLOW_COLDSTART command must be executed.
+	 */
+	if ((cc->sync_start & ERAY_MSGBUF_STARTUP) ||
+	    (cc->sync_start & ERAY_MSGBUF_SYNC)) {
+		reg = eray_read_succ1(cc, 10);
+		if (reg < 0) {
+			ret = -EBUSY;
+			goto out;
+		}
+
+		if (cc->sync_start & ERAY_MSGBUF_STARTUP)
+			reg |= ERAY_SUCC1_TXST_MASK;
+
+		if (cc->sync_start & ERAY_MSGBUF_SYNC)
+			reg |= ERAY_SUCC1_TXSY_MASK;
+
+		reg &= ~ERAY_SUCC1_CMD_MASK;
+		reg |= ERAY_CMD_ALLOW_COLDSTART;
+
+		eray_writel(reg, cc, ERAY_SUCC1);
+	}
+
+	cc->ready = 1;
+
+	for (i = 0; i < cc->act_cfg; i++) {
+		cfg = &cc->cfg[i];
+
+		if ((cfg->type == eray_msgbuf_type_tx) &&
+		    cfg->flags & ERAY_MSGBUF_TXCONT) {
+			ret = _fc_write_data(priv, i, cfg->tx_cont_data,
+					     cfg->tx_cont_len);
+			if (ret)
+				goto out;
+		}
+	}
+
+	ret = fc_prepare_msgbuf_data_ssync(dev);
+	if (ret)
+		goto out;
+
+	ret = 0;
+out:
+	spin_unlock_irqrestore(&cc->lock, flags);
+
+	return ret;
+}
+
+static int fc_write_data(struct flexcard_priv *priv, unsigned int msgbuf_id,
+			 unsigned char *payload, size_t byte_len)
+{
+	struct eray_cc *cc = priv->cc;
+	int ret;
+
+	spin_lock(&cc->lock);
+	ret = _fc_write_data(priv, msgbuf_id, payload, byte_len);
+	spin_unlock(&cc->lock);
+
+	return ret;
+}
+
+static int _fc_write_data(struct flexcard_priv *priv, unsigned int msgbuf_id,
+			  unsigned char *payload, size_t byte_len)
+{
+	struct eray_cc *cc = priv->cc;
+	struct eray_msgbuf_cfg *cfg;
+	u32 txrq, *data = (u32 *) payload;
+	int i, count, rem, err;
+	u32 offset = 0;
+	int ssync;
+
+	ssync = (msgbuf_id & FC_FLEX_ID_SSYNC_FLAG) ? 1 : 0;
+	msgbuf_id &= ~(FC_FLEX_ID_SSYNC_FLAG);
+	if (ssync) {
+		offset = FC_SSYNC_OFFSET;
+		cfg = &cc->ssync_cfg[msgbuf_id];
+	} else
+		cfg = &cc->cfg[msgbuf_id];
+
+	if (cfg->flags & ERAY_MSGBUF_TXCONT) {
+		if (byte_len > 256)
+			byte_len = 256;
+		cfg->tx_cont_len = byte_len;
+
+		memcpy(cfg->tx_cont_data, payload, cfg->tx_cont_len);
+	}
+
+	if (!cc->ready) {
+		/* Discard packets if CC is not ready. If we return an error
+		 * here we never make progress and recover from this state.
+		 */
+		err = 0;
+		goto out;
+	}
+
+	/* ignore TXRQ bit in continous mode since it is allways set */
+	if (!(cfg->flags & ERAY_MSGBUF_TXCONT)) {
+		txrq = eray_readl(cc, (ERAY_TXRQ1 + cfg->id/32) + offset);
+		if (txrq & 1 << (cfg->id%32)) {
+			err = -EBUSY;
+			goto out;
+		}
+	}
+
+	err = fc_wait_for_ram_offset(cc, offset);
+	if (err)
+		goto out;
+
+	/* check packet length to decide if message buffer configuration
+	 * needs to be reprogrammed.
+	 */
+	if (((byte_len + 1) / 2) != cfg->len) {
+		u32 reg;
+
+		/* setup wrhs2 again */
+		cfg->len  = (byte_len + 1) / 2;
+
+		if (cfg->frame_id < cc->static_id)
+			reg = cfg->wrhs2 & ERAY_WRHS2_CRC_MASK;
+		else
+			reg = crc_header(cfg) & ERAY_WRHS2_CRC_MASK;
+		reg |= (cfg->len << ERAY_WRHS2_PLC_SHIFT) &
+			ERAY_WRHS2_PLC_MASK;
+		cfg->wrhs2 = reg;
+
+		/* write msgbuf config */
+		eray_writel(cfg->wrhs1, cc, ERAY_WRHS1 + offset);
+		eray_writel(cfg->wrhs2, cc, ERAY_WRHS2 + offset);
+		eray_writel(cfg->wrhs3, cc, ERAY_WRHS3 + offset);
+
+		eray_writel(ERAY_IBCM_LHSH_MASK, cc, ERAY_IBCM + offset);
+		eray_writel(cfg->id & ERAY_IBCR_IBRH_MASK, cc,
+				ERAY_IBCR + offset);
+		err = fc_wait_for_ram_offset(cc, offset);
+		if (err)
+			goto out;
+
+	}
+
+	count = byte_len >> 2;
+	rem = byte_len & 0x3;
+
+	/* write 32-bit data words */
+	for (i = 0; i < count; i++)
+		eray_writel(cpu_to_le32(data[i]), cc, ERAY_WRDS(i) + offset);
+
+	/* write remaining data bytes */
+	if (rem) {
+		u32 wrd = 0;
+		memcpy(&wrd, &payload[byte_len - rem], rem);
+		eray_writel(cpu_to_le32(wrd), cc, ERAY_WRDS(i) + offset);
+	}
+
+	eray_writel(ERAY_IBCM_LDSH_MASK | ERAY_IBCM_STXRH_MASK, cc,
+			ERAY_IBCM + offset);
+	eray_writel(cfg->id & ERAY_IBCR_IBRH_MASK, cc, ERAY_IBCR + offset);
+
+	err = fc_wait_for_ram_offset(cc, offset);
+	if (!err) {
+		spin_lock(&cfg->lock);
+		cfg->queued = 0;
+		spin_unlock(&cfg->lock);
+	}
+out:
+	return err;
+}
+
+static enum eray_cc_state e_state(enum flexray_state state)
+{
+	switch (state) {
+	case FLEXRAY_STATE_DEFAULT_CONFIG:
+		return ERAY_CMD_FREEZE;
+	case FLEXRAY_STATE_CONFIG:
+		return ERAY_CMD_CONFIG;
+	case FLEXRAY_STATE_READY:
+		return ERAY_CMD_READY;
+	case FLEXRAY_STATE_WAKEUP:
+		return ERAY_CMD_WAKEUP;
+	case FLEXRAY_STATE_STARTUP:
+		return ERAY_CMD_RUN;
+	case FLEXRAY_STATE_HALT:
+		return ERAY_CMD_HALT;
+	case FLEXRAY_STATE_MONITOR_MODE:
+		return ERAY_CMD_MONITOR_MODE;
+	case FLEXRAY_STATE_COLDSTART:
+		return ERAY_CMD_ALLOW_COLDSTART;
+	case FLEXRAY_STATE_NORMAL_ACTIVE:
+	case FLEXRAY_STATE_NORMAL_PASSIVE:
+	case FLEXRAY_STATE_MAX:
+	case FLEXRAY_STATE_UNSPEC:
+	default:
+		return ERAY_CMD_INVALID;
+	}
+}
+
+static int cc_change_state(struct eray_cc *cc, enum eray_cc_state state,
+			   int retry)
+{
+	u32 stat;
+
+	stat = eray_read_succ1(cc, 10);
+	if (stat < 0)
+		return -EBUSY;
+
+	stat &= ~ERAY_SUCC1_CMD_MASK;
+	stat |= state;
+
+	if (state == ERAY_CMD_READY || state == ERAY_CMD_MONITOR_MODE) {
+		eray_writel(0xCE, cc, ERAY_LCK);
+		eray_writel(0x31, cc, ERAY_LCK);
+	}
+
+	eray_writel(stat, cc, ERAY_SUCC1);
+
+	stat = eray_read_succ1(cc, retry);
+	if (!(stat & ERAY_SUCC1_CMD_MASK))
+		return -EINVAL;
+
+	if (state == ERAY_CMD_FREEZE) {
+		stat &= ~ERAY_SUCC1_CMD_MASK;
+		stat |= ERAY_CMD_CONFIG;
+		eray_writel(stat, cc, ERAY_SUCC1);
+
+		stat = eray_read_succ1(cc, retry);
+		if (!(stat & ERAY_SUCC1_CMD_MASK))
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int cc_reset(struct net_device *dev)
+{
+	struct flexcard_priv *priv = netdev_priv(dev);
+	struct eray_cc *cc = priv->cc;
+	int ret;
+
+	/* Set FR CCs to a well-defined state,
+	 * because the cc reset doesn't work always
+	 * 1. send freeze command
+	 * 2. wait for halt state
+	 * 3. send config command
+	 * 4. wait for default config state
+	 * 5. send clear_rams command
+	 * 6. wait max. 150 us for end of ram initialization
+	 * (calculated time 62 us)
+	 * 7. Flexcard reset (50us settle time)
+	 * 8 Flexcard Filter reset (70us settle time)
+	 */
+
+	ret = cc_change_state(cc, ERAY_CMD_FREEZE, 5);
+	if (ret < 0) {
+		dev_err(&dev->dev, "CC FREEZE failed\n");
+		goto out;
+	}
+
+	ret = cc_change_state(cc, ERAY_CMD_CONFIG, 5);
+	if (ret < 0) {
+		dev_err(&dev->dev, "CC DEFAULT_CONFIG failed\n");
+		goto out;
+	}
+
+	ret = cc_change_state(cc, ERAY_CMD_CLEAR_RAMS, 15);
+	if (ret < 0) {
+		netdev_err(dev, "%s: CC CLEAR_RAMS failed\n", __func__);
+		goto out;
+	}
+
+	writel(1<<priv->id, priv->conf + FC_FC_RESET);
+	udelay(50);
+
+	eray_writel(0, cc, FC_RXFILTID);
+	eray_writel(3, cc, FC_RXFILTCH);
+	eray_writel(0, cc, FC_TXFILTID);
+	eray_writel(0, cc, FC_TXFILTCH);
+	udelay(70);
+
+	ret = cc_change_state(cc, ERAY_CMD_CONFIG, 5);
+	if (ret < 0) {
+		dev_err(&dev->dev, "CC CONFIG failed\n");
+		goto out;
+	}
+
+	eray_writel(0, cc, ERAY_MHDC);
+	eray_writel(0, cc, ERAY_GTUC7);
+
+out:
+	return ret;
+}
+
+static void cc_get_conf(struct net_device *dev)
+{
+	struct flexcard_priv *priv = netdev_priv(dev);
+	struct eray_cc *cc = priv->cc;
+	struct flexray_priv *flexray = &priv->flexray;
+	u8 BRP, EOCC, ERCC;
+
+	eray_get_val16(&cc->static_id, cc,
+		      ERAY_GTUC7, ERAY_GTUC7_NSS_MASK, ERAY_GTUC7_NSS_SHIFT);
+
+	eray_get_val8(&cc->static_len, cc,
+		      ERAY_MHDC, ERAY_MHDC_SFDL_MASK, ERAY_MHDC_SFDL_SHIFT);
+
+	/* cluster */
+	eray_get_val8(&flexray->cluster.gColdstartAttempts, cc,
+		      ERAY_SUCC1, ERAY_SUCC1_CSA_MASK, ERAY_SUCC1_CSA_SHIFT);
+	eray_get_val8(&flexray->cluster.gdCASRxLowMax, cc,
+		      ERAY_PRTC1, ERAY_PRTC1_CASM_MASK, ERAY_PRTC1_CASM_SHIFT);
+	eray_get_val8(&flexray->cluster.gdTSSTransmitter, cc,
+		      ERAY_PRTC1, ERAY_PRTC1_TSST_MASK, ERAY_PRTC1_TSST_SHIFT);
+	eray_get_val8(&flexray->cluster.gListenNoise, cc,
+		      ERAY_SUCC2, ERAY_SUCC2_LTN_MASK, ERAY_SUCC2_LTN_SHIFT);
+	flexray->cluster.gListenNoise += 1;
+	eray_get_val8(&flexray->cluster.gMaxWithoutClockCorrectionFatal, cc,
+		      ERAY_SUCC3, ERAY_SUCC3_WCF_MASK, ERAY_SUCC3_WCF_SHIFT);
+	eray_get_val8(&flexray->cluster.gMaxWithoutClockCorrectionPassive, cc,
+		      ERAY_SUCC3, ERAY_SUCC3_WCP_MASK, ERAY_SUCC3_WCP_SHIFT);
+	eray_get_val8(&BRP, cc,
+		      ERAY_PRTC1, ERAY_PRTC1_BRP_MASK, ERAY_PRTC1_BRP_SHIFT);
+	switch (BRP) {
+	case 0:
+		flexray->cluster.gdSampleClockPeriod = 1;
+		flexray->node.pSamplesPerMicrotick = 2;
+		break;
+	case 1:
+		flexray->cluster.gdSampleClockPeriod = 2;
+		flexray->node.pSamplesPerMicrotick = 1;
+		break;
+	case 2:
+	case 3:
+		flexray->cluster.gdSampleClockPeriod = 4;
+		flexray->node.pSamplesPerMicrotick = 1;
+		break;
+	}
+	eray_get_val8(&flexray->cluster.gNetworkManagementVectorLength, cc,
+		      ERAY_NEMC, ERAY_NEMC_NML_MASK, ERAY_NEMC_NML_SHIFT);
+	eray_get_val16(&flexray->cluster.v2.gdWakeupSymbolRxWindow, cc,
+		       ERAY_PRTC1, ERAY_PRTC1_RXW_MASK, ERAY_PRTC1_RXW_SHIFT);
+	eray_get_val8(&flexray->cluster.v2.gdWakeupSymbolRxIdle, cc,
+		      ERAY_PRTC2, ERAY_PRTC2_RXI_MASK, ERAY_PRTC2_RXI_SHIFT);
+	eray_get_val8(&flexray->cluster.v2.gdWakeupSymbolRxLow, cc,
+		      ERAY_PRTC2, ERAY_PRTC2_RXL_MASK, ERAY_PRTC2_RXL_SHIFT);
+	eray_get_val8(&flexray->cluster.v2.gdWakeupSymbolTxIdle, cc,
+		      ERAY_PRTC2, ERAY_PRTC2_TXI_MASK, ERAY_PRTC2_TXI_SHIFT);
+	eray_get_val8(&flexray->cluster.v2.gdWakeupSymbolTxLow, cc,
+		      ERAY_PRTC2, ERAY_PRTC2_TXL_MASK, ERAY_PRTC2_TXL_SHIFT);
+	eray_get_val8(&flexray->cluster.gPayloadLengthStatic, cc,
+		      ERAY_MHDC, ERAY_MHDC_SFDL_MASK, ERAY_MHDC_SFDL_SHIFT);
+	eray_get_val16(&flexray->cluster.gMacroPerCycle, cc,
+		       ERAY_GTUC2, ERAY_GTUC2_MPC_MASK, ERAY_GTUC2_MPC_SHIFT);
+	eray_get_val8(&flexray->cluster.v2.gSyncNodeMax, cc,
+		      ERAY_GTUC2, ERAY_GTUC2_SNM_MASK, ERAY_GTUC2_SNM_SHIFT);
+	eray_get_val16(&flexray->cluster.gdNIT, cc,
+		      ERAY_GTUC4, ERAY_GTUC4_NIT_MASK, ERAY_GTUC4_NIT_SHIFT);
+	flexray->cluster.gdNIT = flexray->cluster.gMacroPerCycle - 1 -
+		flexray->cluster.gdNIT;
+	eray_get_val16(&flexray->cluster.v2.gOffsetCorrectionStart, cc,
+		       ERAY_GTUC4, ERAY_GTUC4_OCS_MASK, ERAY_GTUC4_OCS_SHIFT);
+	flexray->cluster.v2.gOffsetCorrectionStart += 1;
+	eray_get_val16(&flexray->cluster.gdStaticSlot, cc,
+		       ERAY_GTUC7, ERAY_GTUC7_SSL_MASK, ERAY_GTUC7_SSL_SHIFT);
+	eray_get_val16(&flexray->cluster.gNumberOfStaticSlots, cc,
+		       ERAY_GTUC7, ERAY_GTUC7_NSS_MASK, ERAY_GTUC7_NSS_SHIFT);
+	eray_get_val8(&flexray->cluster.gdMinislot, cc,
+		      ERAY_GTUC8, ERAY_GTUC8_MSL_MASK, ERAY_GTUC8_MSL_SHIFT);
+	eray_get_val16(&flexray->cluster.gNumberOfMinislots, cc,
+		       ERAY_GTUC8, ERAY_GTUC8_NMS_MASK, ERAY_GTUC8_NMS_SHIFT);
+	eray_get_val8(&flexray->cluster.gdActionPointOffset, cc,
+		      ERAY_GTUC9, ERAY_GTUC9_APO_MASK, ERAY_GTUC9_APO_SHIFT);
+	eray_get_val8(&flexray->cluster.gdMinislotActionPointOffset, cc,
+		      ERAY_GTUC9, ERAY_GTUC9_MAPO_MASK, ERAY_GTUC9_MAPO_SHIFT);
+	eray_get_val8(&flexray->cluster.gdDynamicSlotIdlePhase, cc,
+		     ERAY_GTUC9, ERAY_GTUC9_DSI_MASK, ERAY_GTUC9_DSI_SHIFT);
+
+	/* node */
+	eray_get_val8(&flexray->node.pAllowHaltDueToClock, cc,
+		      ERAY_SUCC1, ERAY_SUCC1_HCSE_MASK, ERAY_SUCC1_HCSE_SHIFT);
+	eray_get_val8(&flexray->node.pAllowPassiveToActive, cc,
+		      ERAY_SUCC1, ERAY_SUCC1_PTA_MASK, ERAY_SUCC1_PTA_SHIFT);
+	eray_get_val8(&flexray->node.pChannels, cc,
+		      ERAY_SUCC1, ERAY_SUCC1_CCH_MASK, ERAY_SUCC1_CCH_SHIFT);
+	eray_get_val32(&flexray->node.pdListenTimeout, cc,
+		       ERAY_SUCC2, ERAY_SUCC2_LT_MASK, ERAY_SUCC2_LT_SHIFT);
+	eray_get_val8(&flexray->node.v2.pSingleSlotEnabled, cc,
+		      ERAY_SUCC1, ERAY_SUCC1_TSM_MASK, ERAY_SUCC1_TSM_SHIFT);
+	eray_get_val8(&flexray->node.pKeySlotUsedForStartup, cc,
+		      ERAY_SUCC1, ERAY_SUCC1_TXST_MASK, ERAY_SUCC1_TXST_SHIFT);
+	eray_get_val8(&flexray->node.pKeySlotUsedForSync, cc,
+		      ERAY_SUCC1, ERAY_SUCC1_TXSY_MASK, ERAY_SUCC1_TXSY_SHIFT);
+	eray_get_val8(&flexray->node.pWakeupChannel, cc,
+		      ERAY_SUCC1, ERAY_SUCC1_WUCS_MASK, ERAY_SUCC1_WUCS_SHIFT);
+	eray_get_val8(&flexray->node.pWakeupPattern, cc,
+		      ERAY_PRTC1, ERAY_PRTC1_RWP_MASK, ERAY_PRTC1_RWP_SHIFT);
+	eray_get_val16(&flexray->node.pLatestTx, cc,
+		       ERAY_MHDC, ERAY_MHDC_SLT_MASK, ERAY_MHDC_SLT_SHIFT);
+	eray_get_val32(&flexray->node.pMicroPerCycle, cc,
+		       ERAY_GTUC1, ERAY_GTUC1_UT_MASK, ERAY_GTUC1_UT_SHIFT);
+	eray_get_val16(&flexray->node.pMicroInitialOffsetA, cc,
+		       ERAY_GTUC3, ERAY_GTUC3_UIOA_MASK, ERAY_GTUC3_UIOA_SHIFT);
+	eray_get_val16(&flexray->node.pMicroInitialOffsetB, cc,
+		       ERAY_GTUC3, ERAY_GTUC3_UIOB_MASK, ERAY_GTUC3_UIOB_SHIFT);
+	eray_get_val8(&flexray->node.pMacroInitialOffsetA, cc,
+		      ERAY_GTUC3, ERAY_GTUC3_MIOA_MASK, ERAY_GTUC3_MIOA_SHIFT);
+	eray_get_val8(&flexray->node.pMacroInitialOffsetB, cc,
+		      ERAY_GTUC3, ERAY_GTUC3_MIOB_MASK, ERAY_GTUC3_MIOB_SHIFT);
+	eray_get_val8(&flexray->node.pDelayCompensationA, cc,
+		      ERAY_GTUC5, ERAY_GTUC5_DCA_MASK, ERAY_GTUC5_DCA_SHIFT);
+	eray_get_val8(&flexray->node.pDelayCompensationB, cc,
+		      ERAY_GTUC5, ERAY_GTUC5_DCB_MASK, ERAY_GTUC5_DCB_SHIFT);
+	eray_get_val8(&flexray->node.pClusterDriftDamping, cc,
+		      ERAY_GTUC5, ERAY_GTUC5_CDD_MASK, ERAY_GTUC5_CDD_SHIFT);
+	eray_get_val8(&flexray->node.pDecodingCorrection, cc,
+		      ERAY_GTUC5, ERAY_GTUC5_DEC_MASK, ERAY_GTUC5_DEC_SHIFT);
+	eray_get_val16(&flexray->node.pdAcceptedStartupRange, cc,
+		       ERAY_GTUC6, ERAY_GTUC6_ASR_MASK, ERAY_GTUC6_ASR_SHIFT);
+	eray_get_val16(&flexray->node.v2.pdMaxDrift, cc,
+		       ERAY_GTUC6, ERAY_GTUC6_MOD_MASK, ERAY_GTUC6_MOD_SHIFT);
+	eray_get_val16(&flexray->node.pOffsetCorrectionOut, cc, ERAY_GTUC10,
+			ERAY_GTUC10_MOC_MASK, ERAY_GTUC10_MOC_SHIFT);
+	eray_get_val16(&flexray->node.pRateCorrectionOut, cc, ERAY_GTUC10,
+			ERAY_GTUC10_MRC_MASK, ERAY_GTUC10_MRC_SHIFT);
+	eray_get_val8(&flexray->node.pExternOffsetCorrection, cc,
+		      ERAY_GTUC11, ERAY_GTUC11_EOC_MASK, ERAY_GTUC11_EOC_SHIFT);
+	eray_get_val8(&flexray->node.pExternRateCorrection, cc,
+		      ERAY_GTUC11, ERAY_GTUC11_ERC_MASK, ERAY_GTUC11_ERC_SHIFT);
+	eray_get_val8(&EOCC, cc, ERAY_GTUC11, ERAY_GTUC11_EOCC_MASK,
+			ERAY_GTUC11_EOCC_SHIFT);
+
+	/* symbol */
+	eray_get_val8(&flexray->symbol.pChannelsMTS, cc,
+		      ERAY_SUCC1, ERAY_SUCC1_MTS_MASK, ERAY_SUCC1_MTS_SHIFT);
+
+	switch (EOCC) {
+	case 3:
+		flexray->node.vExternOffsetControl = 1;
+		break;
+	case 2:
+		flexray->node.vExternOffsetControl = -1;
+		break;
+	default:
+		flexray->node.vExternOffsetControl = 0;
+	}
+	eray_get_val8(&ERCC, cc, ERAY_GTUC11, ERAY_GTUC11_ERCC_MASK,
+			ERAY_GTUC11_ERCC_SHIFT);
+	switch (ERCC) {
+	case 3:
+		flexray->node.vExternRateControl = 1;
+		break;
+	case 2:
+		flexray->node.vExternRateControl = -1;
+		break;
+	default:
+		flexray->node.vExternRateControl = 0;
+	}
+}
+
+static void cc_irq_setup(struct net_device *dev)
+{
+	struct flexcard_priv *priv = netdev_priv(dev);
+	struct eray_cc *cc = priv->cc;
+
+	/* error eray_int0 */
+	eray_writel(0, cc, ERAY_EILS);
+
+	/* status eray_int1 */
+	eray_writel(ERAY_SIR_MASK, cc, ERAY_SILS);
+
+	/* disable error interrupts */
+	eray_writel(0, cc, ERAY_EIES);
+
+	/* enable receive interrupts */
+	eray_writel(ERAY_SIR_RXI, cc, ERAY_SIES);
+
+	/* enable eray_int0 and eray_int1 line */
+	eray_writel(ERAY_ILE_MASK, cc, ERAY_ILE);
+}
+
+static void cc_stopwatch_setup(struct net_device *dev, u32 reg)
+{
+	struct flexcard_priv *priv = netdev_priv(dev);
+	struct eray_cc *cc = priv->cc;
+
+	eray_writel(reg & 0x3f, cc, ERAY_STPW1);
+}
+
+static struct sk_buff *alloc_flexcard_skb(struct net_device *dev,
+					  struct fc_packet_buf **cf,
+					  size_t len)
+{
+	struct sk_buff *skb;
+
+	skb = netdev_alloc_skb(dev, len);
+	if (unlikely(!skb))
+		return NULL;
+
+	skb->protocol = htons(ETH_P_FLEXRAY);
+	skb->pkt_type = PACKET_BROADCAST;
+	skb->ip_summed = CHECKSUM_UNNECESSARY;
+	*cf = (struct fc_packet_buf *)skb_put(skb, len);
+
+	return skb;
+}
+
+static int fc_rx_pkt(void *p, void *data, size_t len)
+{
+	struct net_device *dev = p;
+	struct net_device_stats *stats = &dev->stats;
+	struct flexcard_priv *priv = netdev_priv(dev);
+	struct eray_cc *cc = priv->cc;
+	struct fc_packet_buf *pb = data;
+	union fc_packet_types *pt = &pb->packet;
+	struct fc_packet_buf *frf;
+	struct sk_buff *skb;
+	u32 l;
+
+	switch (le32_to_cpu(pb->header.type)) {
+	case fc_packet_type_flexray_frame:
+		l = fc_get_packet_len(pt->flexray_frame.header);
+		break;
+	case fc_packet_type_tx_ack:
+		/* The buffer id is visible to userspace. Msg. buffer in
+		 * kernel starts with 0, in oposite to userspace starting
+		 * with 1. Add 1 to buffer id make it even.
+		 */
+		pt->tx_ack_packet.bufferid =
+			cc->rev_id[pt->tx_ack_packet.bufferid] + 1;
+		l = fc_get_packet_len(pt->tx_ack_packet.header);
+		break;
+	default:
+		l = 0;
+	}
+
+	if (len > sizeof(struct fc_packet_buf) + l) {
+		len = sizeof(struct fc_packet_buf) + l;
+		WARN(1, "FlexRay payload to large: truncate.");
+	}
+
+	skb = alloc_flexcard_skb(dev, &frf, len);
+	if (!skb)
+		return -ENOMEM;
+
+	memcpy(frf, data, len);
+	netif_receive_skb(skb);
+
+	stats->rx_packets++;
+	stats->rx_bytes += len;
+
+	return 0;
+}
+
+static int flexcard_open(struct net_device *dev)
+{
+	int ret = -ENOMEM;
+
+	ret = cc_reset(dev);
+	if (ret < 0) {
+		netdev_err(dev, "CC reset failed\n");
+		goto out;
+	}
+
+	cc_stopwatch_setup(dev, 0);
+
+	ret = open_flexraydev(dev);
+	if (ret) {
+		netdev_err(dev, "open flexray device failed\n");
+		goto out;
+	}
+
+	cc_irq_setup(dev);
+
+	return 0;
+
+out:
+	return ret;
+}
+
+static int flexcard_close(struct net_device *dev)
+{
+	struct flexcard_priv *priv = netdev_priv(dev);
+	struct eray_cc *cc = priv->cc;
+
+	cc_change_state(cc, ERAY_CMD_FREEZE, 10);
+	netif_stop_queue(dev);
+
+	close_flexraydev(dev);
+
+	return 0;
+}
+
+static int flexcard_get_state(const struct net_device *dev,
+			       enum flexray_state *state)
+{
+	struct flexcard_priv *priv = netdev_priv(dev);
+	struct eray_cc *cc = priv->cc;
+	int pocs;
+
+	pocs = eray_readl(cc, ERAY_CCSV) & 0x3f;
+	switch (pocs) {
+	case 0x00:
+		*state = FLEXRAY_STATE_DEFAULT_CONFIG;
+		break;
+	case 0x01:
+		*state = FLEXRAY_STATE_READY;
+		break;
+	case 0x02:
+		*state = FLEXRAY_STATE_NORMAL_ACTIVE;
+		break;
+	case 0x03:
+		*state = FLEXRAY_STATE_NORMAL_PASSIVE;
+		break;
+	case 0x04:
+		*state = FLEXRAY_STATE_HALT;
+		break;
+	case 0x05:
+		*state = FLEXRAY_STATE_MONITOR_MODE;
+		break;
+	case 0x0f:
+		*state = FLEXRAY_STATE_CONFIG;
+		break;
+	case 0x10:
+	case 0x11:
+	case 0x12:
+	case 0x13:
+		*state = FLEXRAY_STATE_WAKEUP;
+		break;
+	case 0x20:
+	case 0x21:
+	case 0x22:
+	case 0x24:
+	case 0x25:
+	case 0x26:
+	case 0x27:
+	case 0x28:
+	case 0x29:
+	case 0x2a:
+	case 0x2b:
+		*state = FLEXRAY_STATE_STARTUP;
+		break;
+	default:
+		*state = FLEXRAY_STATE_UNSPEC;
+	}
+
+	return 0;
+}
+
+static int flexcard_set_state(struct net_device *dev,
+			      enum flexray_state state)
+{
+	struct flexcard_priv *priv = netdev_priv(dev);
+	struct eray_cc *cc = priv->cc;
+	int ret;
+
+	switch (state) {
+	case FLEXRAY_STATE_READY:
+		ret = fc_prepare_msgbuf_data(dev);
+		if (ret)
+			return ret;
+	case FLEXRAY_STATE_WAKEUP:
+	case FLEXRAY_STATE_STARTUP:
+	case FLEXRAY_STATE_NORMAL_ACTIVE:
+	case FLEXRAY_STATE_NORMAL_PASSIVE:
+	case FLEXRAY_STATE_MONITOR_MODE:
+	case FLEXRAY_STATE_COLDSTART:
+		netif_start_queue(dev);
+		break;
+	default:
+		cc->ready = 0;
+	}
+
+	return cc_change_state(cc, e_state(state), 10);
+}
+
+static int flexcard_validate(struct sk_buff *skb)
+{
+	struct fc_msg *fcm = (struct fc_msg *)skb->data;
+	struct eray_msgbuf_cfg *cfg;
+	struct flexcard_priv *priv = netdev_priv(skb->dev);
+	struct eray_cc *cc = priv->cc;
+	unsigned long flags;
+	u32 txrq;
+	int ret = 0;
+	u32 buf_id;
+	int ssync;
+
+	ssync = (fcm->buf_id & FC_FLEX_ID_SSYNC_FLAG) ? 1 : 0;
+	buf_id = fcm->buf_id & ~(FC_FLEX_ID_SSYNC_FLAG);
+
+	if (ssync) {
+		if (buf_id >= cc->ssync_num)
+			return -EFAULT;
+		cfg = &cc->ssync_cfg[buf_id];
+	} else {
+		if (buf_id >= cc->act_cfg)
+			return -EFAULT;
+		cfg = &cc->cfg[buf_id];
+	}
+
+	if (!(cfg->flags & ERAY_MSGBUF_USED))
+		return -EFAULT;
+
+	if (cfg->type != eray_msgbuf_type_tx)
+		return -EINVAL;
+
+	if (cfg->channel == eray_msgbuf_ch_none)
+		return -EINVAL;
+
+	if (skb->len > (2 * cfg->max + 4))
+		return -EINVAL;
+
+	/* queue frame, if message buffer is in continuos mode */
+	if (cfg->flags & ERAY_MSGBUF_TXCONT)
+		return 0;
+
+	if ((cfg->frame_id <= cc->static_id) &&
+	    ((skb->len - 4) / 2 != cc->static_len))
+		return -EINVAL;
+
+	if (!cc->ready)
+		return -EBUSY;
+
+	spin_lock_irqsave(&cfg->lock, flags);
+	if (!ssync) {
+		txrq = eray_readl(cc, ERAY_TXRQ1 + cfg->id/32);
+
+		if (txrq & (1 << (cfg->id%32)))
+			ret = -ENOBUFS;
+	}
+
+	if (cfg->queued)
+		ret = -ENOBUFS;
+
+	if (!ret)
+		cfg->queued = 1;
+
+	spin_unlock_irqrestore(&cfg->lock, flags);
+
+	return ret;
+}
+
+static int flexcard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct net_device_stats *stats = &dev->stats;
+	struct flexcard_priv *priv = netdev_priv(dev);
+	struct fc_msg *fcm = (struct fc_msg *)skb->data;
+
+	if (fc_write_data(priv, fcm->buf_id, fcm->data,
+			  skb->len - sizeof(fcm->buf_id))) {
+		net_info_ratelimited("%s() fc_write_data() failed.\n",
+				dev->name);
+		return NETDEV_TX_BUSY;
+	}
+
+	stats->tx_bytes += skb->len - sizeof(fcm->buf_id);
+
+	kfree_skb(skb);
+
+	return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops flexcard_netdev_ops = {
+	.ndo_open       = flexcard_open,
+	.ndo_stop       = flexcard_close,
+	.ndo_start_xmit = flexcard_start_xmit,
+};
+
+static ssize_t fw_ver_show(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct flexcard_priv *priv = netdev_priv(ndev);
+	struct eray_cc *cc = priv->cc;
+	u32 fw_ver;
+
+	fw_ver = eray_readl(cc, ERAY_CREL);
+
+	return sprintf(buf, "%x.%02x %02x.%02x.200%x\n",
+		       fw_ver >> 28 & 0xf, fw_ver >> 20 & 0xff,
+		       fw_ver & 0xff, fw_ver >> 8 & 0xff, fw_ver >> 16 & 0xf);
+}
+
+static ssize_t succ1_show(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct flexcard_priv *priv = netdev_priv(ndev);
+	struct eray_cc *cc = priv->cc;
+	u32 reg;
+
+	reg = eray_read_succ1(cc, 20);
+
+	return sprintf(buf, "0x%08x\n", reg);
+}
+
+#define FC_SHOW_REG(x, y)						\
+	static ssize_t x##_show(struct device *dev,			\
+				struct device_attribute *attr,		\
+				char *buf)				\
+	{								\
+		struct platform_device *pdev = to_platform_device(dev);	\
+		struct net_device *ndev = platform_get_drvdata(pdev);	\
+		struct flexcard_priv *priv = netdev_priv(ndev);		\
+		struct eray_cc *cc = priv->cc;				\
+		u32 reg;						\
+		reg = eray_readl(cc, y);				\
+									\
+		return sprintf(buf, "0x%08x\n", reg);			\
+	}
+
+#define FC_SHOW_REGS(x, y) \
+	static ssize_t x##_show(struct device *dev,			\
+				struct device_attribute *attr,		\
+				char *buf)				\
+	{								\
+		struct platform_device *pdev = to_platform_device(dev);	\
+		struct net_device *ndev = platform_get_drvdata(pdev);	\
+		struct flexcard_priv *priv = netdev_priv(ndev);		\
+		struct eray_cc *cc = priv->cc;				\
+		u32 reg[4];						\
+									\
+		reg[0] = eray_readl(cc, y);				\
+		reg[1] = eray_readl(cc, y + 4);				\
+		reg[2] = eray_readl(cc, y + 8);				\
+		reg[3] = eray_readl(cc, y + 12);			\
+									\
+		return sprintf(buf, "0x%08x%08x%08x%08x\n",		\
+			       reg[0], reg[1], reg[2], reg[3]);		\
+	}
+
+FC_SHOW_REG(ccsv, ERAY_CCSV);
+FC_SHOW_REG(eir, ERAY_EIR);
+FC_SHOW_REG(sir, ERAY_SIR);
+FC_SHOW_REGS(txrq, ERAY_TXRQ1);
+FC_SHOW_REGS(ndat, ERAY_NDAT1);
+FC_SHOW_REGS(mbsc, ERAY_MBSC1);
+
+static struct device_attribute flex_dev_attrs[] = {
+	__ATTR_RO(fw_ver),
+	__ATTR_RO(succ1),
+	__ATTR_RO(ccsv),
+	__ATTR_RO(eir),
+	__ATTR_RO(sir),
+	__ATTR_RO(txrq),
+	__ATTR_RO(ndat),
+	__ATTR_RO(mbsc),
+	__ATTR_NULL,
+};
+
+static int device_add_attributes(struct device *dev,
+				 struct device_attribute *attrs)
+{
+	int error = 0;
+	int i;
+
+	for (i = 0; attr_name(attrs[i]); i++) {
+		error = device_create_file(dev, &attrs[i]);
+		if (error)
+			break;
+	}
+	if (error)
+		while (--i >= 0)
+			device_remove_file(dev, &attrs[i]);
+	return error;
+}
+
+static void device_remove_attributes(struct device *dev,
+				     struct device_attribute *attrs)
+{
+	int i;
+
+	for (i = 0; attr_name(attrs[i]); i++)
+		device_remove_file(dev, &attrs[i]);
+}
+
+static int flexcard_probe(struct platform_device *pdev)
+{
+	struct eray_msgbuf_cfg *cfg;
+	struct flexcard_priv *priv;
+	struct eray_cc *cc;
+	struct net_device *dev;
+	struct resource *res, *res_conf;
+	int i, irq, ret = -ENXIO;
+	u32 fw_ver;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "failed to get IRQ number\n");
+		goto out;
+	}
+
+	res_conf = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res_conf) {
+		dev_err(&pdev->dev, "failed to get conf I/O memory\n");
+		goto out;
+	}
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res) {
+		dev_err(&pdev->dev, "failed to get mmio I/O memory\n");
+		goto out;
+	}
+
+	cc = kzalloc(sizeof(*cc), GFP_KERNEL);
+	if (!cc) {
+		dev_err(&pdev->dev, "failed to alloc memory\n");
+		goto out_release;
+	}
+
+	spin_lock_init(&cc->lock);
+	for (i = 0; i < ERAY_MAX_BUFS; i++)
+		spin_lock_init(&cc->cfg[i].lock);
+
+	cc->fifo_threshold = ERAY_FIFO_THRESHOLD;
+
+	dev = alloc_flexraydev(sizeof(struct flexcard_priv), 2);
+	if (!dev) {
+		dev_err(&pdev->dev, "failed to alloc netdevice\n");
+		goto out_free;
+	}
+
+	dev->netdev_ops = &flexcard_netdev_ops;
+	dev->irq = irq;
+	dev->flags |= IFF_ECHO;
+
+	priv = netdev_priv(dev);
+	priv->cc = cc;
+	priv->dev = dev;
+	priv->id = pdev->id;
+	priv->flexray.do_get_state = flexcard_get_state;
+	priv->flexray.do_set_state = flexcard_set_state;
+	priv->flexray.do_validate = flexcard_validate;
+
+	cfg = &cc->cfg[0];
+	cfg->flags |= ERAY_MSGBUF_USED;
+	cfg->type = eray_msgbuf_type_fifo;
+	cfg->max = 127;
+
+	dev_set_drvdata(&pdev->dev, dev);
+	SET_NETDEV_DEV(dev, &pdev->dev);
+
+	priv->conf = ioremap_nocache(res_conf->start, resource_size(res_conf));
+	if (!priv->conf) {
+		dev_err(&pdev->dev, "failed to remap conf I/O memory\n");
+		goto out_free_dev;
+	}
+
+	cc->base = ioremap_nocache(res->start, resource_size(res));
+	if (!cc->base) {
+		dev_err(&pdev->dev, "failed to remap mmio I/O memory\n");
+		goto out_unmap_conf;
+	}
+
+	ret = register_flexraydev(dev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to register netdevice\n");
+		goto out_unmap_mmio;
+	}
+
+	ret = device_add_attributes(&pdev->dev, flex_dev_attrs);
+	if (ret)
+		goto out_unregister;
+
+	ret = fc_register_rx_pkt(priv->id, dev, fc_rx_pkt);
+	if (ret) {
+		netdev_err(dev, "register RX DMA callback failed: %d\n", ret);
+		goto out_add_attr;
+	}
+
+	fw_ver = eray_readl(cc, ERAY_CREL);
+	dev_info(&pdev->dev, "E-Ray FW ver.%x.%02x %02x.%02x.200%x\n",
+		 fw_ver >> 28 & 0xf, fw_ver >> 20 & 0xff,
+		 fw_ver & 0xff, fw_ver >> 8 & 0xff, fw_ver >> 16 & 0xf);
+
+	return 0;
+out_add_attr:
+	device_remove_attributes(&pdev->dev, flex_dev_attrs);
+out_unregister:
+	unregister_flexraydev(dev);
+out_unmap_mmio:
+	iounmap(cc->base);
+out_unmap_conf:
+	iounmap(priv->conf);
+out_free_dev:
+	free_flexraydev(dev);
+out_free:
+	kfree(cc);
+out_release:
+	release_mem_region(res->start, resource_size(res));
+out:
+	return ret;
+}
+
+static int flexcard_remove(struct platform_device *pdev)
+{
+	struct net_device *dev = platform_get_drvdata(pdev);
+	struct flexcard_priv *priv = netdev_priv(dev);
+	struct eray_cc *cc = priv->cc;
+
+	fc_unregister_rx_pkt(priv->id);
+	device_remove_attributes(&pdev->dev, flex_dev_attrs);
+
+	unregister_flexraydev(dev);
+	free_flexraydev(dev);
+	iounmap(cc->base);
+	iounmap(priv->conf);
+	kfree(cc);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver flexcard_driver = {
+	.probe = flexcard_probe,
+	.remove = flexcard_remove,
+	.driver = {
+		.name = "flexcard-eray",
+		.owner = THIS_MODULE,
+	},
+};
+MODULE_ALIAS("platform:flexcard-eray");
+
+static int __init flexcard_init(void)
+{
+	int ret;
+
+	ret = genl_register_family_with_ops(&fc_msgbuf_genl_family,
+					    fc_msgbuf_genl_ops,
+					    ARRAY_SIZE(fc_msgbuf_genl_ops));
+	if (ret) {
+		pr_err("flexcard: register genl failed %d\n", ret);
+		goto out;
+	}
+
+	ret = platform_driver_register(&flexcard_driver);
+out:
+	return ret;
+}
+module_init(flexcard_init);
+
+static void __exit flexcard_exit(void)
+{
+	genl_unregister_family(&fc_msgbuf_genl_family);
+	platform_driver_unregister(&flexcard_driver);
+}
+module_exit(flexcard_exit);
+
+MODULE_AUTHOR("Benedikt Spranger <b.spranger@linutronix.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Eberspächer Flexcard FlexRay driver");
diff --git a/drivers/net/flexray/vflexray.c b/drivers/net/flexray/vflexray.c
new file mode 100644
index 0000000..6b73624
--- /dev/null
+++ b/drivers/net/flexray/vflexray.c
@@ -0,0 +1,99 @@
+/* Copyright 2012 Eberspächer Electronics GmbH & Co. KG. All Rights Reserved */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/flexray.h>
+#include <linux/flexray/dev.h>
+#include <linux/slab.h>
+#include <net/rtnetlink.h>
+
+static void vflexray_rx(struct sk_buff *skb, struct net_device *dev)
+{
+	struct flexray_frame *frf = (struct flexray_frame *)skb->data;
+	struct net_device_stats *stats = &dev->stats;
+
+	stats->rx_packets++;
+	stats->rx_bytes += flexray_get_pl(frf->head);
+
+	skb->protocol  = htons(ETH_P_FLEXRAY);
+	skb->pkt_type  = PACKET_BROADCAST;
+	skb->dev       = dev;
+	skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+	netif_rx_ni(skb);
+}
+
+static netdev_tx_t vflexray_tx(struct sk_buff *skb, struct net_device *dev)
+{
+	struct flexray_frame *frf = (struct flexray_frame *)skb->data;
+	struct net_device_stats *stats = &dev->stats;
+	int loop;
+
+	if (flexray_dropped_invalid_skb(dev, skb))
+		return NETDEV_TX_OK;
+
+	stats->tx_packets++;
+	stats->tx_bytes += flexray_get_pl(frf->head);
+
+	/* set flag whether this packet has to be looped back */
+	loop = skb->pkt_type == PACKET_LOOPBACK;
+	if (loop) {
+		struct sock *srcsk = skb->sk;
+
+		skb = skb_share_check(skb, GFP_ATOMIC);
+		if (!skb)
+			return NETDEV_TX_OK;
+
+		/* receive with packet counting */
+		skb->sk = srcsk;
+		vflexray_rx(skb, dev);
+	} else {
+		kfree_skb(skb);
+	}
+
+	return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops vflexray_netdev_ops = {
+	.ndo_start_xmit = vflexray_tx,
+};
+
+static void vflexray_setup(struct net_device *dev)
+{
+	dev->type		= ARPHRD_FLEXRAY;
+	dev->mtu		= sizeof(struct flexray_frame);
+	dev->hard_header_len	= 0;
+	dev->addr_len		= 0;
+	dev->tx_queue_len	= 0;
+	dev->flags		= IFF_NOARP;
+
+	dev->netdev_ops		= &vflexray_netdev_ops;
+	dev->destructor		= free_netdev;
+}
+
+static struct rtnl_link_ops vflexray_link_ops __read_mostly = {
+	.kind   = "vflexray",
+	.setup  = vflexray_setup,
+};
+
+static __init int vflexray_init_module(void)
+{
+	pr_info("vflexray: Virtual FlexRay interface driver\n");
+
+	return rtnl_link_register(&vflexray_link_ops);
+}
+
+static __exit void vflexray_cleanup_module(void)
+{
+	rtnl_link_unregister(&vflexray_link_ops);
+}
+
+module_init(vflexray_init_module);
+module_exit(vflexray_cleanup_module);
+
+MODULE_DESCRIPTION("virtual FlexRay interface");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Benedikt Spranger <b.spranger@linutronix.de>");
diff --git a/net/flexray/Kconfig b/net/flexray/Kconfig
index 18d9d69..1f497e5 100644
--- a/net/flexray/Kconfig
+++ b/net/flexray/Kconfig
@@ -26,3 +26,5 @@ config FLEXRAY_RAW
 	  most cases where no higher level protocol is being used.
 	  To receive/send raw FLEXRAY messages, use AF_FLEXRAY with protocol
 	  FLEXRAY_RAW.
+
+source "drivers/net/flexray/Kconfig"
-- 
1.8.4.rc2

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* Re: [PATCH 4/7] mfd: add MFD based flexcard driver
  2013-08-13  9:08 ` [PATCH 4/7] mfd: add MFD based flexcard driver Benedikt Spranger
@ 2013-08-13  9:55   ` Lee Jones
  2013-08-14  8:12     ` Benedikt Spranger
  0 siblings, 1 reply; 28+ messages in thread
From: Lee Jones @ 2013-08-13  9:55 UTC (permalink / raw)
  To: Benedikt Spranger
  Cc: netdev, Alexander Frank, Sebastian Andrzej Siewior, Samuel Ortiz,
	Holger Dengler

On Tue, 13 Aug 2013, Benedikt Spranger wrote:

> This patch provides support for Eberpächer Flexcard. The Flexcard comes
> with two PCI bars, one for the glue layer and one for the "IPs". The
> second bar may have multiple other devices like flexray or d_can
> devices. The driver utilites the MFD framework and creates various
> subdevices. Not only for the additional "IPs" but also for the clock
> source and clockevent provided by the card.
> 
> Cc: Samuel Ortiz <sameo@linux.intel.com>
> Cc: Lee Jones <lee.jones@linaro.org>
> Signed-off-by: Benedikt Spranger <b.spranger@linutronix.de>
> Signed-off-by: Holger Dengler <dengler@linutronix.de>
> ---
>  drivers/mfd/Kconfig           |   40 ++
>  drivers/mfd/Makefile          |    2 +
>  drivers/mfd/flexcard-core.c   | 1059 +++++++++++++++++++++++++++++++++++++++++
>  drivers/mfd/flexcard-dma.c    |  561 ++++++++++++++++++++++
>  drivers/mfd/flexcard-irq.c    |  333 +++++++++++++
>  drivers/mfd/flexcard.h        |    8 +
>  include/linux/eray.h          |  650 +++++++++++++++++++++++++
>  include/linux/flexcard.h      |   95 ++++
>  include/uapi/linux/Kbuild     |    2 +
>  include/uapi/linux/eray.h     |   34 ++
>  include/uapi/linux/flexcard.h |  429 +++++++++++++++++
>  11 files changed, 3213 insertions(+)
>  create mode 100644 drivers/mfd/flexcard-core.c
>  create mode 100644 drivers/mfd/flexcard-dma.c
>  create mode 100644 drivers/mfd/flexcard-irq.c
>  create mode 100644 drivers/mfd/flexcard.h
>  create mode 100644 include/linux/eray.h
>  create mode 100644 include/linux/flexcard.h
>  create mode 100644 include/uapi/linux/eray.h
>  create mode 100644 include/uapi/linux/flexcard.h

Wow, this is a huge patch which is going to be an insanely long,
laborious and complex task to review it all.

Can you split this up into many patches? Adding functionality
incrementally instead of rolling it up into this ball of wax?

-- 
Lee Jones
Linaro ST-Ericsson Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 3/7] mfd: core: copy DMA mask and params from parent
  2013-08-13  9:08 ` [PATCH 3/7] mfd: core: copy DMA mask and params from parent Benedikt Spranger
@ 2013-08-13 10:03   ` Lee Jones
  0 siblings, 0 replies; 28+ messages in thread
From: Lee Jones @ 2013-08-13 10:03 UTC (permalink / raw)
  To: Benedikt Spranger
  Cc: netdev, Alexander Frank, Sebastian Andrzej Siewior, Samuel Ortiz,
	Holger Dengler

On Tue, 13 Aug 2013, Benedikt Spranger wrote:

> The child device intends to perform DMA operations then it needs a dma
> mask and params set. This patches copies them from the parent device.
> 
> Cc: Samuel Ortiz <sameo@linux.intel.com>
> Cc: Lee Jones <lee.jones@linaro.org>
> Signed-off-by: Benedikt Spranger <b.spranger@linutronix.de>
> Signed-off-by: Holger Dengler <dengler@linutronix.de>
> ---
>  drivers/mfd/mfd-core.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
> index 7604f4e..f421586 100644
> --- a/drivers/mfd/mfd-core.c
> +++ b/drivers/mfd/mfd-core.c
> @@ -96,6 +96,8 @@ static int mfd_add_device(struct device *parent, int id,
>  
>  	pdev->dev.parent = parent;
>  	pdev->dev.type = &mfd_dev_type;
> +	pdev->dev.dma_mask = parent->dma_mask;
> +	pdev->dev.dma_parms = parent->dma_parms;
>  
>  	if (parent->of_node && cell->of_compatible) {
>  		for_each_child_of_node(parent->of_node, np) {

Patch applied, thanks.

-- 
Lee Jones
Linaro ST-Ericsson Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/7] uio: add module owner to prevent inappropriate module unloading
  2013-08-13  9:08 ` [PATCH 1/7] uio: add module owner to prevent inappropriate module unloading Benedikt Spranger
@ 2013-08-13 17:48   ` Greg Kroah-Hartman
  2013-08-14  7:19     ` Benedikt Spranger
  0 siblings, 1 reply; 28+ messages in thread
From: Greg Kroah-Hartman @ 2013-08-13 17:48 UTC (permalink / raw)
  To: Benedikt Spranger
  Cc: netdev, Alexander Frank, Sebastian Andrzej Siewior, Hans J. Koch,
	Holger Dengler

On Tue, Aug 13, 2013 at 11:08:36AM +0200, Benedikt Spranger wrote:
> If an UIO device is created by another driver (MFD for instance) it has to be
> ensured that the MFD driver isn't removed while the UIO is still in use.

Shouldn't if the MFD driver is removed, the UIO device will be cleaned
up and also removed?  You shouldn't need a module reference for this
type of thing.

And why did I only get 2 of the 7 patches here?

greg k-h

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 2/7] uio: Allow to create custom UIO attributes
  2013-08-13  9:08 ` [PATCH 2/7] uio: Allow to create custom UIO attributes Benedikt Spranger
@ 2013-08-13 17:54   ` Greg Kroah-Hartman
  0 siblings, 0 replies; 28+ messages in thread
From: Greg Kroah-Hartman @ 2013-08-13 17:54 UTC (permalink / raw)
  To: Benedikt Spranger
  Cc: netdev, Alexander Frank, Sebastian Andrzej Siewior, Hans J. Koch,
	Holger Dengler

On Tue, Aug 13, 2013 at 11:08:37AM +0200, Benedikt Spranger wrote:
> This patch the struct uio_attribute which represents a custom UIO
> attribute. The non-standard attributes are stored in a "attr" directory.
> This will be used by the flexcard driver which creates a UIO device that
> is using the "uio_pdrv" and requires one additional value to be read /
> written by the user.
> 
> Cc: "Hans J. Koch" <hjk@hansjkoch.de>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Signed-off-by: Benedikt Spranger <b.spranger@linutronix.de>
> Signed-off-by: Holger Dengler <dengler@linutronix.de>
> ---
>  drivers/uio/uio.c          | 106 ++++++++++++++++++++++++++++++++++++++++++++-
>  include/linux/uio_driver.h |  36 +++++++++++----
>  2 files changed, 133 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c
> index 9a95220..d66784a 100644
> --- a/drivers/uio/uio.c
> +++ b/drivers/uio/uio.c
> @@ -39,6 +39,7 @@ struct uio_device {
>  	struct uio_info		*info;
>  	struct kobject		*map_dir;
>  	struct kobject		*portio_dir;
> +	struct kobject		attr_dir;

If you embed a kobject into a structure, it is now in charge of the
memory reference counting for that object.

Which you did not do correctly:

> +static struct kobj_type uio_attr_type = {
> +	.sysfs_ops	= &uio_sysfs_ops,
> +};

As per the documentation in the kernel, I now get to make fun of the
fact that you obviously didn't test this code (hint, the kernel would
have told you bad things happened if you did...)

But I don't understand your main goal of a new kobject for attributes,
why?  What problem are you trying to solve here that a new kobject, and
new sysfs files attached to the UIO object are going to solve?

>  /**
> + * uio_add_user_attributes - add an extra UIO attribute
> + * @info: UIO device capabilities
> + */
> +static int uio_add_user_attributes(struct uio_info *info)
> +{
> +	struct uio_device *idev = info->uio_dev;
> +	const struct uio_attribute **uio_attr;
> +	int i;
> +	int ret = 0;
> +
> +	uio_attr = info->attributes;
> +	if (!uio_attr)
> +		return 0;
> +
> +	for (i = 0; uio_attr[i]; i++) {
> +
> +		ret = sysfs_create_file(&idev->attr_dir, &uio_attr[i]->attr);
> +		if (ret)
> +			break;
> +	}
> +	if (ret) {
> +		while (--i >= 0)
> +			sysfs_remove_file(&idev->attr_dir, &uio_attr[i]->attr);
> +	}
> +	return ret;
> +}

Ick, you just raced with userspace and blew up any tool that was wanting
to watch for your new kobject to be created with the attributes attached
to it :(

Sending code that doesn't work is fine, for review if you have questions
about things, but please, mark it with a big "I HAVEN'T TESTED THIS"
statement somewhere.

greg k-h

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/7] uio: add module owner to prevent inappropriate module unloading
  2013-08-13 17:48   ` Greg Kroah-Hartman
@ 2013-08-14  7:19     ` Benedikt Spranger
  2013-08-14 16:33       ` Greg Kroah-Hartman
  0 siblings, 1 reply; 28+ messages in thread
From: Benedikt Spranger @ 2013-08-14  7:19 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: netdev, Alexander Frank, Sebastian Andrzej Siewior, Hans J. Koch,
	Holger Dengler

Am Tue, 13 Aug 2013 10:48:14 -0700
schrieb Greg Kroah-Hartman <gregkh@linuxfoundation.org>:

> On Tue, Aug 13, 2013 at 11:08:36AM +0200, Benedikt Spranger wrote:
> > If an UIO device is created by another driver (MFD for instance) it has to be
> > ensured that the MFD driver isn't removed while the UIO is still in use.
> 
> Shouldn't if the MFD driver is removed, the UIO device will be cleaned
> up and also removed?
That is part of the problem:

1) MFD driver creates platform device "uio_pdrv"
2) uio_pdrv creates "UIOX"
3) userspace opens "UIOX"
4) MFD driver unload (remove platform device "uio_pdrv")
5) userspace reads from "UIOX" --> crash

> You shouldn't need a module reference for this type of thing.
The driver uio_pdrv has no chance to recognize that the underlaying platform
device has gone.

Regards
    Benedikt Spranger

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 4/7] mfd: add MFD based flexcard driver
  2013-08-13  9:55   ` Lee Jones
@ 2013-08-14  8:12     ` Benedikt Spranger
  2013-08-14  9:45       ` Lee Jones
  0 siblings, 1 reply; 28+ messages in thread
From: Benedikt Spranger @ 2013-08-14  8:12 UTC (permalink / raw)
  To: Lee Jones
  Cc: netdev, Alexander Frank, Sebastian Andrzej Siewior, Samuel Ortiz,
	Holger Dengler

On Tue, 13 Aug 2013 10:55:08 +0100 Lee Jones <lee.jones@linaro.org> wrote:

> Wow, this is a huge patch which is going to be an insanely long,
> laborious and complex task to review it all.
This is a card with a "insane" amount of features:

1) a Interrupt controller
2) a DMA engine
3) up to 8 CAN devices
4) up to 4 FlexRay devices
5) a dozen different trigger sources
6) timer
7) timestamp counter

> Can you split this up into many patches?
Not sure how to split this up into a smaller and workable series of patches.
Maybe rip out the header and the DMA part into a separate patch.

Regards
    Benedikt Spranger

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 4/7] mfd: add MFD based flexcard driver
  2013-08-14  8:12     ` Benedikt Spranger
@ 2013-08-14  9:45       ` Lee Jones
  0 siblings, 0 replies; 28+ messages in thread
From: Lee Jones @ 2013-08-14  9:45 UTC (permalink / raw)
  To: Benedikt Spranger
  Cc: netdev, Alexander Frank, Sebastian Andrzej Siewior, Samuel Ortiz,
	Holger Dengler

> > Wow, this is a huge patch which is going to be an insanely long,
> > laborious and complex task to review it all.
> This is a card with a "insane" amount of features:
> 
> 1) a Interrupt controller
> 2) a DMA engine
> 3) up to 8 CAN devices
> 4) up to 4 FlexRay devices
> 5) a dozen different trigger sources
> 6) timer
> 7) timestamp counter

Right. Even more reason for the the patch to be broken up into many.

> > Can you split this up into many patches?
> Not sure how to split this up into a smaller and workable series of patches.
> Maybe rip out the header and the DMA part into a separate patch.

I was thinking more along the lines of:

    Patch 1: Basic compilation support
     - Core driver Kconfig entry
     - Introduction of the core driver
       - Basic probe(): memory allocation, register as MFD device
       - Basic remove(): unregister as MFD device
    
    Patch 2: Basic PCI support
     - pci_enable_device()
     - pci_request_regions()
     - Memory mapping
    
    Patch 3:
     - fc_init_dma()
    
    Patch 4:
     - fc_init_uio()
    
    Patch 5:
     - fc_init_event()
    
    Patch 6:
     - fc_init_clk()
    
    Patch 7:
     - fc_init_bus()
    
    Patch 8 - 8+n: Start adding sysfs entries
    
    Patch 8+n+1: Basic IRQ support
    
    Etc etc etc

Remember, the smaller the patches, the more chance that they'll be
accepted. I already see quite a few issues with this patch. Breaking
it up will allow for more thorough review and finer grain feedback.

Issues I see after first glance:

  - Lack of Device Tree support
  - Lack of documentation for new sysfs entries
  - Different IRQs with same .name property
  - Memory allocations and mapping should be managed (devm_*)

-- 
Lee Jones
Linaro ST-Ericsson Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/7] uio: add module owner to prevent inappropriate module unloading
  2013-08-14  7:19     ` Benedikt Spranger
@ 2013-08-14 16:33       ` Greg Kroah-Hartman
  2013-08-15  6:42         ` Benedikt Spranger
  0 siblings, 1 reply; 28+ messages in thread
From: Greg Kroah-Hartman @ 2013-08-14 16:33 UTC (permalink / raw)
  To: Benedikt Spranger
  Cc: netdev, Alexander Frank, Sebastian Andrzej Siewior, Hans J. Koch,
	Holger Dengler

On Wed, Aug 14, 2013 at 09:19:46AM +0200, Benedikt Spranger wrote:
> Am Tue, 13 Aug 2013 10:48:14 -0700
> schrieb Greg Kroah-Hartman <gregkh@linuxfoundation.org>:
> 
> > On Tue, Aug 13, 2013 at 11:08:36AM +0200, Benedikt Spranger wrote:
> > > If an UIO device is created by another driver (MFD for instance) it has to be
> > > ensured that the MFD driver isn't removed while the UIO is still in use.
> > 
> > Shouldn't if the MFD driver is removed, the UIO device will be cleaned
> > up and also removed?
> That is part of the problem:
> 
> 1) MFD driver creates platform device "uio_pdrv"
> 2) uio_pdrv creates "UIOX"
> 3) userspace opens "UIOX"
> 4) MFD driver unload (remove platform device "uio_pdrv")

How can this happen in a normal situation?  Modules do not simply unload
themselves :)

> 5) userspace reads from "UIOX" --> crash

Step 4 should have told UIO that it was gone and had it shut everything
down properly, so that there would not be a crash.

> > You shouldn't need a module reference for this type of thing.
> The driver uio_pdrv has no chance to recognize that the underlaying platform
> device has gone.

The mfd driver could tell it that it is gone, right?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/7] uio: add module owner to prevent inappropriate module unloading
  2013-08-14 16:33       ` Greg Kroah-Hartman
@ 2013-08-15  6:42         ` Benedikt Spranger
  2013-08-15  6:59           ` Greg Kroah-Hartman
  0 siblings, 1 reply; 28+ messages in thread
From: Benedikt Spranger @ 2013-08-15  6:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: netdev, Alexander Frank, Sebastian Andrzej Siewior, Hans J. Koch,
	Holger Dengler

On Wed, 14 Aug 2013 09:33:11 -0700
Greg Kroah-Hartman <gregkh@linuxfoundation.org> wrote:

> Step 4 should have told UIO that it was gone and had it shut everything
> down properly, so that there would not be a crash.
The MFD driver only knows about a specific MFD cell. Through
enable/disable callbacks the driver could tell UIO ...hm... whom? what?

Neither the MFD driver nor the MFD core knows something about a specific
UIO driver. But only that specific UIO driver knows about the device
node activities. 

> > > You shouldn't need a module reference for this type of thing.
> > The driver uio_pdrv has no chance to recognize that the underlaying platform
> > device has gone.
> The mfd driver could tell it that it is gone, right?
It could tell, but whom and how?

Regards
    Benedikt Spranger

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/7] uio: add module owner to prevent inappropriate module unloading
  2013-08-15  6:42         ` Benedikt Spranger
@ 2013-08-15  6:59           ` Greg Kroah-Hartman
  2013-08-15  7:27             ` Benedikt Spranger
  0 siblings, 1 reply; 28+ messages in thread
From: Greg Kroah-Hartman @ 2013-08-15  6:59 UTC (permalink / raw)
  To: Benedikt Spranger
  Cc: netdev, Alexander Frank, Sebastian Andrzej Siewior, Hans J. Koch,
	Holger Dengler

On Thu, Aug 15, 2013 at 08:42:21AM +0200, Benedikt Spranger wrote:
> On Wed, 14 Aug 2013 09:33:11 -0700
> Greg Kroah-Hartman <gregkh@linuxfoundation.org> wrote:
> 
> > Step 4 should have told UIO that it was gone and had it shut everything
> > down properly, so that there would not be a crash.
> The MFD driver only knows about a specific MFD cell. Through
> enable/disable callbacks the driver could tell UIO ...hm... whom? what?
> 
> Neither the MFD driver nor the MFD core knows something about a specific
> UIO driver. But only that specific UIO driver knows about the device
> node activities. 
> 
> > > > You shouldn't need a module reference for this type of thing.
> > > The driver uio_pdrv has no chance to recognize that the underlaying platform
> > > device has gone.
> > The mfd driver could tell it that it is gone, right?
> It could tell, but whom and how?

Hm.  Ah, doesn't this work like PCI, when a PCI device is removed from
the system, reads just start returning all 0xFF, so the userspace UIO
driver now knows the device is gone from the system.  Doesn't MFD
hardware work the same way?  Why would removing the MFD driver affect
UIO at all, as it's just an interrupt and memory, both of which are
controlled by UIO, not MFD at all.

confused,

greg k-h

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/7] uio: add module owner to prevent inappropriate module unloading
  2013-08-15  6:59           ` Greg Kroah-Hartman
@ 2013-08-15  7:27             ` Benedikt Spranger
  2013-08-15  8:09               ` Greg Kroah-Hartman
  0 siblings, 1 reply; 28+ messages in thread
From: Benedikt Spranger @ 2013-08-15  7:27 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: netdev, Alexander Frank, Sebastian Andrzej Siewior, Hans J. Koch,
	Holger Dengler

On Wed, 14 Aug 2013 23:59:36 -0700
Greg Kroah-Hartman <gregkh@linuxfoundation.org> wrote:

> Hm.  Ah, doesn't this work like PCI, when a PCI device is removed from
> the system, reads just start returning all 0xFF, so the userspace UIO
> driver now knows the device is gone from the system.  Doesn't MFD
> hardware work the same way?  Why would removing the MFD driver affect
> UIO at all, as it's just an interrupt and memory, both of which are
> controlled by UIO, not MFD at all.
> 
> confused,
Sorry for the confusion.

MFD core allocates the platform device structure and platform data
dynamicaly and fill it up with appropriate data from the MFD cell data.
And MFD core frees this memory on unregistering.

Therefore the UIO driver needs to know that the underlaying platform device
struct and platform device data are invalid and should not be used any more.
The whole point are dynamicaly allocated devices and UIO.

Regards
    Benedikt Spranger

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/7] uio: add module owner to prevent inappropriate module unloading
  2013-08-15  7:27             ` Benedikt Spranger
@ 2013-08-15  8:09               ` Greg Kroah-Hartman
  2013-08-15  8:18                 ` Sebastian Andrzej Siewior
  0 siblings, 1 reply; 28+ messages in thread
From: Greg Kroah-Hartman @ 2013-08-15  8:09 UTC (permalink / raw)
  To: Benedikt Spranger
  Cc: netdev, Alexander Frank, Sebastian Andrzej Siewior, Hans J. Koch,
	Holger Dengler

On Thu, Aug 15, 2013 at 09:27:53AM +0200, Benedikt Spranger wrote:
> On Wed, 14 Aug 2013 23:59:36 -0700
> Greg Kroah-Hartman <gregkh@linuxfoundation.org> wrote:
> 
> > Hm.  Ah, doesn't this work like PCI, when a PCI device is removed from
> > the system, reads just start returning all 0xFF, so the userspace UIO
> > driver now knows the device is gone from the system.  Doesn't MFD
> > hardware work the same way?  Why would removing the MFD driver affect
> > UIO at all, as it's just an interrupt and memory, both of which are
> > controlled by UIO, not MFD at all.
> > 
> > confused,
> Sorry for the confusion.
> 
> MFD core allocates the platform device structure and platform data
> dynamicaly and fill it up with appropriate data from the MFD cell data.
> And MFD core frees this memory on unregistering.
> 
> Therefore the UIO driver needs to know that the underlaying platform device
> struct and platform device data are invalid and should not be used any more.
> The whole point are dynamicaly allocated devices and UIO.

Given that a UIO driver does need to specificly bind to a device (be it
a PCI, OF, or something else), it should be the one that is notified
when a device is removed.

Again, just like PCI is handled today, why is MFD so "special" that it
can't tell the drivers bound to its devices that it is going away?

Do you have a specific example of an in-tree UIO driver that has this
problem that I can look at to try to understand this better?

Still confused,

greg k-h

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/7] uio: add module owner to prevent inappropriate module unloading
  2013-08-15  8:09               ` Greg Kroah-Hartman
@ 2013-08-15  8:18                 ` Sebastian Andrzej Siewior
  2013-08-15 15:55                   ` Greg Kroah-Hartman
  0 siblings, 1 reply; 28+ messages in thread
From: Sebastian Andrzej Siewior @ 2013-08-15  8:18 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Benedikt Spranger, netdev, Alexander Frank, Hans J. Koch, Holger Dengler

On 08/15/2013 10:09 AM, Greg Kroah-Hartman wrote:
> Do you have a specific example of an in-tree UIO driver that has this
> problem that I can look at to try to understand this better?

grep for "uio_pdrv" and you find a few devices in arm and sh tree. Each
one is created once at boot time and never removed. With mfd the device
can be removed.
If you look now at uio_write() then you will notice that it will
deference idev->info->irqcontrol but once the device is gone the memory
starting at info is gone, not to mention the code behind irqcontrol.

> Still confused,
> 
> greg k-h

Sebastian

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/7] uio: add module owner to prevent inappropriate module unloading
  2013-08-15  8:18                 ` Sebastian Andrzej Siewior
@ 2013-08-15 15:55                   ` Greg Kroah-Hartman
  2013-08-15 16:03                     ` Sebastian Andrzej Siewior
  0 siblings, 1 reply; 28+ messages in thread
From: Greg Kroah-Hartman @ 2013-08-15 15:55 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior
  Cc: Benedikt Spranger, netdev, Alexander Frank, Hans J. Koch, Holger Dengler

On Thu, Aug 15, 2013 at 10:18:17AM +0200, Sebastian Andrzej Siewior wrote:
> On 08/15/2013 10:09 AM, Greg Kroah-Hartman wrote:
> > Do you have a specific example of an in-tree UIO driver that has this
> > problem that I can look at to try to understand this better?
> 
> grep for "uio_pdrv" and you find a few devices in arm and sh tree. Each
> one is created once at boot time and never removed. With mfd the device
> can be removed.

But that's a "platform" device, for a resource that is described as not
going away.

If this is really a mfd device, then make your uio driver a mfd driver,
not a platform driver for a resource that isn't under your control.

> If you look now at uio_write() then you will notice that it will
> deference idev->info->irqcontrol but once the device is gone the memory
> starting at info is gone, not to mention the code behind irqcontrol.

It sounds like the wrong uio driver is binding to this device, fix the
uio driver and you should be fine, right?

A module reference count will not "save" you from a device going away,
only a code chunk going away.  That is why no other subsystem has this
type of thing.  If you dynamically remove the mfd device, but not remove
the module (i.e. through the sysfs files to do that), then you would
still have this same problem, right?

There's a reason the driver core doesn't deal with module reference
counts, it's not the proper thing for devices.  So I'm not willing to
add it to the UIO code either, as it's not the correct thing for it.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/7] uio: add module owner to prevent inappropriate module unloading
  2013-08-15 15:55                   ` Greg Kroah-Hartman
@ 2013-08-15 16:03                     ` Sebastian Andrzej Siewior
  2013-08-15 16:42                       ` Greg Kroah-Hartman
  0 siblings, 1 reply; 28+ messages in thread
From: Sebastian Andrzej Siewior @ 2013-08-15 16:03 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Benedikt Spranger, netdev, Alexander Frank, Hans J. Koch, Holger Dengler

On 08/15/2013 05:55 PM, Greg Kroah-Hartman wrote:
> But that's a "platform" device, for a resource that is described as not
> going away.
> 
> If this is really a mfd device, then make your uio driver a mfd driver,
> not a platform driver for a resource that isn't under your control.

As you described it later yourself: You have the same problem if you
manually unbind the platform_device from the driver while the device
node is open.

>> If you look now at uio_write() then you will notice that it will
>> deference idev->info->irqcontrol but once the device is gone the memory
>> starting at info is gone, not to mention the code behind irqcontrol.
> 
> It sounds like the wrong uio driver is binding to this device, fix the
> uio driver and you should be fine, right?

For this to happen you would need a refcount in uio-core which learns
that the device is gone and does not invoke any callbacks because the
device is gone. Something like you have in USB where you return 0 on
reads from ttyUSB after someone pulled the cable.

> A module reference count will not "save" you from a device going away,
> only a code chunk going away.  That is why no other subsystem has this
> type of thing.  If you dynamically remove the mfd device, but not remove
> the module (i.e. through the sysfs files to do that), then you would
> still have this same problem, right?

Yes, I think so.

> There's a reason the driver core doesn't deal with module reference
> counts, it's not the proper thing for devices.  So I'm not willing to
> add it to the UIO code either, as it's not the correct thing for it.

okay.

> 
> thanks,
> 
> greg k-h

Sebastian

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/7] uio: add module owner to prevent inappropriate module unloading
  2013-08-15 16:03                     ` Sebastian Andrzej Siewior
@ 2013-08-15 16:42                       ` Greg Kroah-Hartman
  2013-08-15 16:54                         ` Sebastian Andrzej Siewior
  0 siblings, 1 reply; 28+ messages in thread
From: Greg Kroah-Hartman @ 2013-08-15 16:42 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior
  Cc: Benedikt Spranger, netdev, Alexander Frank, Hans J. Koch, Holger Dengler

On Thu, Aug 15, 2013 at 06:03:44PM +0200, Sebastian Andrzej Siewior wrote:
> On 08/15/2013 05:55 PM, Greg Kroah-Hartman wrote:
> > But that's a "platform" device, for a resource that is described as not
> > going away.
> > 
> > If this is really a mfd device, then make your uio driver a mfd driver,
> > not a platform driver for a resource that isn't under your control.
> 
> As you described it later yourself: You have the same problem if you
> manually unbind the platform_device from the driver while the device
> node is open.

No, at that point in time the remove function of the uio driver should
be called, and you can invalidate everything then.  Or the driver should
be doing that, odds are, it needs to be fixed because no one checks for
that :)

> >> If you look now at uio_write() then you will notice that it will
> >> deference idev->info->irqcontrol but once the device is gone the memory
> >> starting at info is gone, not to mention the code behind irqcontrol.
> > 
> > It sounds like the wrong uio driver is binding to this device, fix the
> > uio driver and you should be fine, right?
> 
> For this to happen you would need a refcount in uio-core which learns
> that the device is gone and does not invoke any callbacks because the
> device is gone. Something like you have in USB where you return 0 on
> reads from ttyUSB after someone pulled the cable.

That happens because we invalidate the filehandle in the tty layer by
tearing everything down in the usb serial driver.  And yes, uio also
needs to do the same thing, if it doesn't already.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/7] uio: add module owner to prevent inappropriate module unloading
  2013-08-15 16:42                       ` Greg Kroah-Hartman
@ 2013-08-15 16:54                         ` Sebastian Andrzej Siewior
  2013-08-15 17:13                           ` Greg Kroah-Hartman
  0 siblings, 1 reply; 28+ messages in thread
From: Sebastian Andrzej Siewior @ 2013-08-15 16:54 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Benedikt Spranger, netdev, Alexander Frank, Hans J. Koch, Holger Dengler

On 08/15/2013 06:42 PM, Greg Kroah-Hartman wrote:
>> For this to happen you would need a refcount in uio-core which learns
>> that the device is gone and does not invoke any callbacks because the
>> device is gone. Something like you have in USB where you return 0 on
>> reads from ttyUSB after someone pulled the cable.
> 
> That happens because we invalidate the filehandle in the tty layer by
> tearing everything down in the usb serial driver.  And yes, uio also
> needs to do the same thing, if it doesn't already.

Ah. Do you have a handy pointer where / how you do this?

> 
> thanks,
> 
> greg k-h

Sebastian

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/7] uio: add module owner to prevent inappropriate module unloading
  2013-08-15 16:54                         ` Sebastian Andrzej Siewior
@ 2013-08-15 17:13                           ` Greg Kroah-Hartman
  0 siblings, 0 replies; 28+ messages in thread
From: Greg Kroah-Hartman @ 2013-08-15 17:13 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior
  Cc: Benedikt Spranger, netdev, Alexander Frank, Hans J. Koch, Holger Dengler

On Thu, Aug 15, 2013 at 06:54:46PM +0200, Sebastian Andrzej Siewior wrote:
> On 08/15/2013 06:42 PM, Greg Kroah-Hartman wrote:
> >> For this to happen you would need a refcount in uio-core which learns
> >> that the device is gone and does not invoke any callbacks because the
> >> device is gone. Something like you have in USB where you return 0 on
> >> reads from ttyUSB after someone pulled the cable.
> > 
> > That happens because we invalidate the filehandle in the tty layer by
> > tearing everything down in the usb serial driver.  And yes, uio also
> > needs to do the same thing, if it doesn't already.
> 
> Ah. Do you have a handy pointer where / how you do this?

The tty layer isn't exactly "handy" :)

Somewhere we send a HANGUP signal to userspace, and I think we
invalidate the file handle somehow, it's been years since I last looked
at that code, sorry.

greg k-h

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 6/7] net: add the AF_FLEXRAY protocol
  2013-08-13  9:08 ` [PATCH 6/7] net: add the AF_FLEXRAY protocol Benedikt Spranger
@ 2013-08-18 18:50   ` Oliver Hartkopp
  0 siblings, 0 replies; 28+ messages in thread
From: Oliver Hartkopp @ 2013-08-18 18:50 UTC (permalink / raw)
  To: Benedikt Spranger; +Cc: netdev, Alexander Frank, Sebastian Andrzej Siewior

On 13.08.2013 11:08, Benedikt Spranger wrote:
> FlexRay is a networking technology used in automotive fields as
> successor of the Controller Area Network (CAN). It provides the
> core functionality and a RAW protocol driver.
> 
> Signed-off-by: Benedikt Spranger <b.spranger@linutronix.de>

Hello Benedikt,

first thanks for picking this FlexRay topic together with Eberspaecher.

There was a first attempt getting FlexRay into mainline by Michael Arndt:

http://www.scriptkiller.de/en/c27/computer_electronics/linux/flexray_4_linux/

You probably know that implementation too - it has a nice FlexRay Linux Logo.
My comment on that former implementation was if AF_FLEXRAY is really needed or
if a PF_PACKET socket would make it too.

I think to have the local echo of frames and if you intend to add the FlexRay
specific transport protocol it would make sense to create an AF_FLEXRAY.

The ISO15765-2 implementation would be a good starting point then:
https://gitorious.org/linux-can/can-modules/blobs/master/net/can/isotp.c

Some general remarks to your patch series:

- please write FlexRay and not Flexray in the comments

- patch 4/7 creates

 include/linux/eray.h          |  650 +++++++++++++++++++++++++
 include/linux/flexcard.h      |   95 ++++
 include/uapi/linux/eray.h     |   34 ++
 include/uapi/linux/flexcard.h |  429 +++++++++++++++++

I wonder, if this header pollution is really needed on this level

E.g. eray.h could go into drivers/net/flexray/eray/eray.h ...

- It's not clear where flexcard begins and what is part of the Eray IP core.
  I would suggest to have a Eray core driver (like we have the SJA1000 at CAN)
  and create some Flexcard specific glue code to access the Eray core.
  E.g. there is surely other HW available that uses the Eray core, right?
  You have a clear separation from the Flexcard to the D_CAN controller too.

- Why do you need a

>  include/uapi/linux/flexray/flexcard_netlink.h |  53 +++

???

Please try to find a generic approach that fits for all FlexRay hardware.

- some remarks on the file headers:

  - Better write Eberspaecher than EberspÀcher which fits their website :-)
  - there's no License information in the header
  - a reference that this code is based on the CAN code is appropriate

- struct can_frame cf -> struct flexray_frame ff (you should rename the cf's)

I built your patches on the latest 3.11-rc5. Are there any simple tools to
play with the vflexray created devices, like candump or cansend for CAN?

>  include/linux/flexray.h                       | 168 ++++++++

Looks good.

How are the FlexRay real-time requirements handled?
Is this done by the Eray core?

Best regards,
Oliver

^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2013-08-18 18:50 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-08-13  9:08 [PATCH 0/7] add FlexRay support Benedikt Spranger
2013-08-13  9:08 ` Benedikt Spranger
2013-08-13  9:08 ` [PATCH 1/7] uio: add module owner to prevent inappropriate module unloading Benedikt Spranger
2013-08-13 17:48   ` Greg Kroah-Hartman
2013-08-14  7:19     ` Benedikt Spranger
2013-08-14 16:33       ` Greg Kroah-Hartman
2013-08-15  6:42         ` Benedikt Spranger
2013-08-15  6:59           ` Greg Kroah-Hartman
2013-08-15  7:27             ` Benedikt Spranger
2013-08-15  8:09               ` Greg Kroah-Hartman
2013-08-15  8:18                 ` Sebastian Andrzej Siewior
2013-08-15 15:55                   ` Greg Kroah-Hartman
2013-08-15 16:03                     ` Sebastian Andrzej Siewior
2013-08-15 16:42                       ` Greg Kroah-Hartman
2013-08-15 16:54                         ` Sebastian Andrzej Siewior
2013-08-15 17:13                           ` Greg Kroah-Hartman
2013-08-13  9:08 ` [PATCH 2/7] uio: Allow to create custom UIO attributes Benedikt Spranger
2013-08-13 17:54   ` Greg Kroah-Hartman
2013-08-13  9:08 ` [PATCH 3/7] mfd: core: copy DMA mask and params from parent Benedikt Spranger
2013-08-13 10:03   ` Lee Jones
2013-08-13  9:08 ` [PATCH 4/7] mfd: add MFD based flexcard driver Benedikt Spranger
2013-08-13  9:55   ` Lee Jones
2013-08-14  8:12     ` Benedikt Spranger
2013-08-14  9:45       ` Lee Jones
2013-08-13  9:08 ` [PATCH 5/7] clocksource: Add flexcard support Benedikt Spranger
2013-08-13  9:08 ` [PATCH 6/7] net: add the AF_FLEXRAY protocol Benedikt Spranger
2013-08-18 18:50   ` Oliver Hartkopp
2013-08-13  9:08 ` [PATCH 7/7] net: add a flexray driver Benedikt Spranger

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.