Netdev List
 help / color / mirror / Atom feed
* [PATCH v2 3/5] char: implement companion-char driver
From: Mark Jonas @ 2018-06-13 14:37 UTC (permalink / raw)
  To: Wolfgang Grandegger, Marc Kleine-Budde
  Cc: linux-can, netdev, linux-kernel, hs, yi.zhu5, petar.petrovic2,
	stephan.baetge, andy.shevchenko, socketcan, o.rempel, Mark Jonas
In-Reply-To: <1528900641-18677-1-git-send-email-mark.jonas@de.bosch.com>

From: Zhu Yi <yi.zhu5@cn.bosch.com>

The upper level companion-char driver provides character device
interface to userspace for communicate IO messages with the
companion processor.

Signed-off-by: Zhu Yi <yi.zhu5@cn.bosch.com>
Signed-off-by: Mark Jonas <mark.jonas@de.bosch.com>
---
 drivers/char/Kconfig     |   7 +
 drivers/char/Makefile    |   3 +
 drivers/char/companion.c | 360 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 370 insertions(+)
 create mode 100644 drivers/char/companion.c

diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index c28dca0..e878d56 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -588,5 +588,12 @@ config TILE_SROM
 
 source "drivers/char/xillybus/Kconfig"
 
+config COMPANION_CHAR
+	tristate "Character device for companion communication (Bosch)"
+	depends on COMPANION_SPI
+	help
+	  The character device allows the userspace to exchange IO messages
+	  with the Bosch companion processor via the companion SPI driver.
+
 endmenu
 
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 7dc3abe..0dae572 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -60,3 +60,6 @@ js-rtc-y = rtc.o
 obj-$(CONFIG_TILE_SROM)		+= tile-srom.o
 obj-$(CONFIG_XILLYBUS)		+= xillybus/
 obj-$(CONFIG_POWERNV_OP_PANEL)	+= powernv-op-panel.o
+
+obj-$(CONFIG_COMPANION_CHAR)	+= companion-char.o
+companion-char-objs := companion.o
diff --git a/drivers/char/companion.c b/drivers/char/companion.c
new file mode 100644
index 0000000..2e26f01
--- /dev/null
+++ b/drivers/char/companion.c
@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Companion upper level character device
+ *
+ * Copyright (C) 2015-2018 Bosch Sicherheitssysteme GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/cdev.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/companion.h>
+
+#define DRIVER_NAME "companion-char"
+
+static struct class *companion_char_class;
+static dev_t         devt;
+
+/**
+ * struct companion_char_minor - companion-char minor structure
+ * @dev:       address of the associated device
+ * @writelock: mutex to protect write
+ * @readlock:  mutex to protect read
+ * @writewait: wait queue head of write
+ * @readwait:  wait queue head of read
+ */
+struct companion_char_minor {
+	struct device    *dev;
+	struct mutex      writelock;
+	struct mutex      readlock;
+	wait_queue_head_t writewait;
+	wait_queue_head_t readwait;
+};
+
+/**
+ * struct companion_char_priv - companion-char private data structure
+ * @cdev:   char device
+ * @parent: address of the associated parent device
+ * @minors: address of the companion-char minor
+ */
+struct companion_char_priv {
+	struct cdev                  cdev;
+	struct device               *parent;
+	struct companion_char_minor *minors;
+};
+
+/**
+ * companion_char_read() - read callback
+ * @filp:   address of the associated virtual file
+ * @buf:    address of the user space buffer to receive
+ * @count:  number of bytes to read
+ * @offset: address of the read offset
+ */
+static ssize_t companion_char_read(struct file *filp,
+				   char __user *buf,
+				   size_t       count,
+				   loff_t      *offset)
+{
+	unsigned int                 number = MINOR(file_inode(filp)->i_rdev);
+	struct companion_char_priv  *priv   = filp->private_data;
+	struct companion_char_minor *minor  = &priv->minors[number];
+	int                          err;
+
+	if (count != COMPANION_PACKET_SIZE)
+		return -EMSGSIZE;
+
+	if (mutex_lock_interruptible(&minor->readlock))
+		return -ERESTARTSYS;
+
+	while (companion_io_rxq_is_empty(priv->parent)) {
+		mutex_unlock(&minor->readlock);
+		if (filp->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		if (wait_event_interruptible(minor->readwait,
+				!companion_io_rxq_is_empty(priv->parent)))
+			return -ERESTARTSYS;
+		if (mutex_lock_interruptible(&minor->readlock))
+			return -ERESTARTSYS;
+	}
+
+	err = companion_do_io_rx(priv->parent, buf, count);
+	mutex_unlock(&minor->readlock);
+	return err;
+}
+
+/**
+ * companion_char_write() - write callback
+ * @filp:   address of the associated virtual file
+ * @buf:    address of the user space buffer to transfer
+ * @count:  number of bytes to write
+ * @offset: address of the write offset
+ */
+static ssize_t companion_char_write(struct file       *filp,
+				    const char __user *buf,
+				    size_t             count,
+				    loff_t            *offset)
+{
+	unsigned int                 number = MINOR(file_inode(filp)->i_rdev);
+	struct companion_char_priv  *priv   = filp->private_data;
+	struct companion_char_minor *minor  = &priv->minors[number];
+	int                          err;
+
+	if (count != COMPANION_PACKET_SIZE)
+		return -EMSGSIZE;
+
+	if (mutex_lock_interruptible(&minor->writelock))
+		return -ERESTARTSYS;
+
+	while (companion_io_txq_is_full(priv->parent)) {
+		mutex_unlock(&minor->writelock);
+		if (filp->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		if (wait_event_interruptible(minor->writewait,
+				!companion_io_txq_is_full(priv->parent)))
+			return -ERESTARTSYS;
+		if (mutex_lock_interruptible(&minor->writelock))
+			return -ERESTARTSYS;
+	}
+
+	err = companion_do_io_tx(priv->parent, buf, count);
+	mutex_unlock(&minor->writelock);
+	return err;
+}
+
+/**
+ * companion_char_poll() - poll callback
+ * @filp: address of the associated virtual file
+ * @wait: address of the associated poll table
+ */
+static unsigned int companion_char_poll(struct file *filp, poll_table *wait)
+{
+	unsigned int                 number = MINOR(file_inode(filp)->i_rdev);
+	struct companion_char_priv  *priv   = filp->private_data;
+	struct companion_char_minor *minor  = &priv->minors[number];
+	unsigned int                 mask   = 0;
+
+	poll_wait(filp, &minor->writewait, wait);
+	poll_wait(filp, &minor->readwait,  wait);
+
+	mutex_lock(&minor->writelock);
+	if (!companion_io_txq_is_full(priv->parent))
+		mask |= POLLOUT | POLLWRNORM;
+	mutex_unlock(&minor->writelock);
+
+	mutex_lock(&minor->readlock);
+	if (!companion_io_rxq_is_empty(priv->parent))
+		mask |= POLLIN | POLLRDNORM;
+	mutex_unlock(&minor->readlock);
+
+	return mask;
+}
+
+/**
+ * companion_char_open() - open callback
+ * @inode: address of the associated inode
+ * @filp:  address of the associated virtual file
+ */
+static int companion_char_open(struct inode *inode, struct file *filp)
+{
+	struct companion_char_priv *priv = container_of(
+						inode->i_cdev,
+						struct companion_char_priv,
+						cdev);
+
+	filp->private_data = priv;
+	nonseekable_open(inode, filp);
+	return 0;
+}
+
+/**
+ * companion_char_release() - release callback
+ * @inode: address of the associated inode
+ * @filp:  address of the associated virtual file
+ */
+static int companion_char_release(struct inode *inode, struct file *filp)
+{
+	filp->private_data = NULL;
+	return 0;
+}
+
+static const struct file_operations companion_char_ops = {
+	.owner   = THIS_MODULE,
+	.llseek  = no_llseek,
+	.read    = companion_char_read,
+	.write   = companion_char_write,
+	.poll    = companion_char_poll,
+	.open    = companion_char_open,
+	.release = companion_char_release,
+};
+
+/**
+ * companion_char_on_tx_done() - tx done callback
+ * @data: address of user supplied callback data
+ */
+static void companion_char_on_tx_done(void *data)
+{
+	struct companion_char_priv  *priv = data;
+	struct companion_char_minor *minor = &priv->minors[0];
+
+	wake_up_interruptible(&minor->writewait);
+}
+
+/**
+ * companion_char_on_rx_done() - rx done callback
+ * @data: address of user supplied callback data
+ */
+static void companion_char_on_rx_done(void *data)
+{
+	struct companion_char_priv  *priv  = data;
+	struct companion_char_minor *minor = &priv->minors[0];
+
+	wake_up_interruptible(&minor->readwait);
+}
+
+static struct companion_io_ops companion_char_io_ops = {
+	.on_tx_done = companion_char_on_tx_done,
+	.on_rx_done = companion_char_on_rx_done,
+};
+
+/**
+ * companion_char_probe() - probe callback
+ * @pdev: address of the platform device
+ */
+static int companion_char_probe(struct platform_device *pdev)
+{
+	struct companion_char_priv  *priv;
+	struct companion_char_minor *minors;
+	int                          err;
+
+	if (!pdev->dev.parent) {
+		dev_err(&pdev->dev, "no parent device found\n");
+		return -ENODEV;
+	}
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	priv->parent = pdev->dev.parent;
+
+	minors = devm_kzalloc(&pdev->dev, sizeof(*minors), GFP_KERNEL);
+	if (!minors)
+		return -ENOMEM;
+
+	minors->dev = device_create(companion_char_class,
+				    &pdev->dev,
+				    MKDEV(MAJOR(devt), 0),
+				    priv,
+				    "companion%d",
+				    0);
+	if (IS_ERR_OR_NULL(minors->dev))
+		return PTR_ERR_OR_ZERO(minors->dev);
+	priv->minors = minors;
+
+	mutex_init(&minors->writelock);
+	mutex_init(&minors->readlock);
+	init_waitqueue_head(&minors->writewait);
+	init_waitqueue_head(&minors->readwait);
+
+	cdev_init(&priv->cdev, &companion_char_ops);
+	err = cdev_add(&priv->cdev, MKDEV(MAJOR(devt), 0), 1);
+	if (err) {
+		dev_err(&pdev->dev, "cdev_add() failed: %d\n", err);
+		goto on_error;
+	}
+
+	dev_set_drvdata(&pdev->dev, priv);
+
+	err = companion_io_ops_register(priv->parent,
+					&companion_char_io_ops,
+					priv);
+	if (err) {
+		dev_err(&pdev->dev, "companion_io_ops_register() failed: %d\n",
+			err);
+		goto on_error;
+	}
+	return 0;
+
+on_error:
+	device_destroy(companion_char_class, MKDEV(MAJOR(devt), 0));
+	cdev_del(&priv->cdev);
+	return err;
+}
+
+/**
+ * companion_char_remove() - remove callback
+ * @pdev: address of the platform device
+ */
+static int companion_char_remove(struct platform_device *pdev)
+{
+	struct companion_char_priv *priv = dev_get_drvdata(&pdev->dev);
+
+	companion_io_ops_unregister(priv->parent);
+	device_destroy(companion_char_class, MKDEV(MAJOR(devt), 0));
+	cdev_del(&priv->cdev);
+	return 0;
+}
+
+static const struct of_device_id companion_char_of_match[] = {
+	{ .compatible = "bosch,companion-char", .data = NULL, },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, companion_char_of_match);
+
+static struct platform_driver companion_char_driver = {
+	.driver = {
+		.name           = DRIVER_NAME,
+		.of_match_table = of_match_ptr(companion_char_of_match),
+	},
+	.probe  = companion_char_probe,
+	.remove = companion_char_remove,
+};
+
+/**
+ * companion_char_init() - module init
+ */
+static int __init companion_char_init(void)
+{
+	int err;
+
+	companion_char_class = class_create(THIS_MODULE, DRIVER_NAME);
+	if (IS_ERR_OR_NULL(companion_char_class))
+		return PTR_ERR_OR_ZERO(companion_char_class);
+
+	err = alloc_chrdev_region(&devt, 0, 1, DRIVER_NAME);
+	if (err) {
+		class_destroy(companion_char_class);
+		return err;
+	}
+
+	err = platform_driver_register(&companion_char_driver);
+	if (err) {
+		class_destroy(companion_char_class);
+		unregister_chrdev_region(devt, 1);
+	}
+
+	return err;
+}
+
+/**
+ * companion_char_exit() - module exit
+ */
+static void __exit companion_char_exit(void)
+{
+	platform_driver_unregister(&companion_char_driver);
+	class_destroy(companion_char_class);
+	unregister_chrdev_region(devt, 1);
+}
+
+module_init(companion_char_init);
+module_exit(companion_char_exit);
+
+MODULE_AUTHOR("Zhu Yi <yi.zhu5@cn.bosch.com>");
+MODULE_DESCRIPTION("Companion upper level character device");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 2/5] spi: implement companion-spi driver
From: Mark Jonas @ 2018-06-13 14:37 UTC (permalink / raw)
  To: Wolfgang Grandegger, Marc Kleine-Budde
  Cc: linux-can, netdev, linux-kernel, hs, yi.zhu5, petar.petrovic2,
	stephan.baetge, andy.shevchenko, socketcan, o.rempel, Mark Jonas
In-Reply-To: <1528900641-18677-1-git-send-email-mark.jonas@de.bosch.com>

From: Zhu Yi <yi.zhu5@cn.bosch.com>

The low level companion-spi driver encapsulates the communication
details with the companion processor, and provides interface for
the upper level drivers to access.

Signed-off-by: Zhu Yi <yi.zhu5@cn.bosch.com>
Signed-off-by: Mark Jonas <mark.jonas@de.bosch.com>
---
 drivers/spi/Kconfig                      |    2 +
 drivers/spi/Makefile                     |    2 +
 drivers/spi/companion/Kconfig            |    5 +
 drivers/spi/companion/Makefile           |    2 +
 drivers/spi/companion/core.c             | 1185 ++++++++++++++++++++++++++++++
 drivers/spi/companion/protocol-manager.c | 1032 ++++++++++++++++++++++++++
 drivers/spi/companion/protocol-manager.h |  341 +++++++++
 drivers/spi/companion/protocol.h         |  273 +++++++
 drivers/spi/companion/queue-manager.c    |  144 ++++
 drivers/spi/companion/queue-manager.h    |  245 ++++++
 include/linux/companion.h                |  259 +++++++
 11 files changed, 3490 insertions(+)
 create mode 100644 drivers/spi/companion/Kconfig
 create mode 100644 drivers/spi/companion/Makefile
 create mode 100644 drivers/spi/companion/core.c
 create mode 100644 drivers/spi/companion/protocol-manager.c
 create mode 100644 drivers/spi/companion/protocol-manager.h
 create mode 100644 drivers/spi/companion/protocol.h
 create mode 100644 drivers/spi/companion/queue-manager.c
 create mode 100644 drivers/spi/companion/queue-manager.h
 create mode 100644 include/linux/companion.h

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index a75f2a2..8b575ec 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -799,6 +799,8 @@ config SPI_TLE62X0
 # Add new SPI protocol masters in alphabetical order above this line
 #
 
+source "drivers/spi/companion/Kconfig"
+
 endif # SPI_MASTER
 
 #
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 8e0cda7..ae369d9 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -112,3 +112,5 @@ obj-$(CONFIG_SPI_ZYNQMP_GQSPI)		+= spi-zynqmp-gqspi.o
 # SPI slave protocol handlers
 obj-$(CONFIG_SPI_SLAVE_TIME)		+= spi-slave-time.o
 obj-$(CONFIG_SPI_SLAVE_SYSTEM_CONTROL)	+= spi-slave-system-control.o
+
+obj-y                                   += companion/
diff --git a/drivers/spi/companion/Kconfig b/drivers/spi/companion/Kconfig
new file mode 100644
index 0000000..490a273
--- /dev/null
+++ b/drivers/spi/companion/Kconfig
@@ -0,0 +1,5 @@
+config COMPANION_SPI
+	tristate "Low level driver for companion communication (Bosch)"
+	depends on SPI
+	help
+	  This driver communicates with the companion processor via SPI.
diff --git a/drivers/spi/companion/Makefile b/drivers/spi/companion/Makefile
new file mode 100644
index 0000000..e60e733
--- /dev/null
+++ b/drivers/spi/companion/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_COMPANION_SPI) += companion-spi.o
+companion-spi-objs := core.o protocol-manager.o queue-manager.o
diff --git a/drivers/spi/companion/core.c b/drivers/spi/companion/core.c
new file mode 100644
index 0000000..515e5ec
--- /dev/null
+++ b/drivers/spi/companion/core.c
@@ -0,0 +1,1185 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Companion low level init/core code
+ *
+ * Copyright (C) 2015-2018 Bosch Sicherheitssysteme GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/spi/spi.h>
+#include <linux/kfifo.h>
+
+#include "protocol-manager.h"
+
+#define DRIVER_NAME "companion-spi"
+
+#define READY_POLL_US      80
+#define READY_POLL_US_GRAN 1
+#define READY_POLL_MS      100
+#define READY_POLL_MS_GRAN 10
+
+/**
+ * struct busy_signal_statistics - spi busy signal statistics
+ * @while_busy_ext:        how many times while_busy loop been waited
+ * @while_busy_fail:       how many times while_busy been timed out
+ * @until_busy_ext:        how many times until_busy loop been waited
+ * @until_busy_fail:       how many times until_busy been timed out
+ * @force_started:         how many times of force started
+ * @force_started_failure: how many times of force started failure
+ * @ready_failure:         how many times of ready failure
+ */
+struct busy_signal_statistics {
+	u32 while_busy_ext;
+	u32 while_busy_fail;
+	u32 until_busy_ext;
+	u32 until_busy_fail;
+	u32 force_started;
+	u32 force_started_failure;
+	u32 ready_failure;
+};
+
+/**
+ * struct companion_spi_priv - companion-spi private data structure
+ * @spi:                  address of spi device
+ * @task:                 address of task struct
+ * @wait:                 wait queue head
+ * @gpiod_request:        gpio line connect to request signal
+ * @gpiod_busy:           gpio line connect to busy signal
+ * @gpiod_cs:             gpio line connect to cs signal
+ * @dump_packet:          flag to control dump spi packet
+ * @stats:                spi busy signal statistics
+ * @pm:                   companion protocol manager
+ */
+struct companion_spi_priv {
+	struct spi_device                *spi;
+	struct task_struct               *task;
+	wait_queue_head_t                 wait;
+
+	struct gpio_desc                 *gpiod_request;
+	struct gpio_desc                 *gpiod_busy;
+	struct gpio_desc                 *gpiod_cs;
+
+	bool                              dump_packets;
+	struct busy_signal_statistics     stats;
+	struct companion_protocol_manager pm;
+};
+
+/**
+ * companion_io_ops_register() - register companion IO packets handler
+ * @parent: address of the parent device
+ * @ops:    address of the IO callbacks
+ * @data:   address of the data passed to the IO callbacks
+ */
+int companion_io_ops_register(struct device           *parent,
+			      struct companion_io_ops *ops,
+			      void                    *data)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+	return pm_io_ops_register(&priv->pm, ops, data);
+}
+EXPORT_SYMBOL_GPL(companion_io_ops_register);
+
+/**
+ * companion_io_ops_unregister() - unregister companion IO packets handler
+ * @parent: address of the parent device
+ */
+int companion_io_ops_unregister(struct device *parent)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+	return pm_io_ops_unregister(&priv->pm);
+}
+EXPORT_SYMBOL_GPL(companion_io_ops_unregister);
+
+/**
+ * companion_can_ops_register() - register companion CAN packets handler
+ * @parent: address of the parent device
+ * @port:   which CAN port to register
+ * @ops:    address of the CAN callbacks
+ * @data:   address of the data passed to the CAN callbacks
+ */
+int companion_can_ops_register(struct device            *parent,
+			       u8                        port,
+			       struct companion_can_ops *ops,
+			       void                     *data)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+	return pm_can_ops_register(&priv->pm, port, ops, data);
+}
+EXPORT_SYMBOL_GPL(companion_can_ops_register);
+
+/**
+ * companion_can_ops_unregister() - unregister companion CAN packets handler
+ * @parent: address of the parent device
+ * @port:   which CAN port to unregister
+ */
+int companion_can_ops_unregister(struct device *parent, u8 port)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+	return pm_can_ops_unregister(&priv->pm, port);
+}
+EXPORT_SYMBOL_GPL(companion_can_ops_unregister);
+
+/**
+ * companion_io_txq_is_full() - return true if IO tx queue is full
+ * @parent: address of the parent device
+ */
+bool companion_io_txq_is_full(struct device *parent)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+	return qm_io_txq_is_full(&priv->pm.qm);
+}
+EXPORT_SYMBOL_GPL(companion_io_txq_is_full);
+
+/**
+ * companion_io_rxq_is_empty() - return true if IO rx queue is empty
+ * @parent: address of the parent device
+ */
+bool companion_io_rxq_is_empty(struct device *parent)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+	return qm_io_rxq_is_empty(&priv->pm.qm);
+}
+EXPORT_SYMBOL_GPL(companion_io_rxq_is_empty);
+
+/**
+ * companion_do_io_tx() - send IO packet
+ * @parent: address of the parent device
+ * @buf:    address of the user space buffer to send
+ * @count:  number of bytes to copy
+ */
+int companion_do_io_tx(struct device     *parent,
+		       const char __user *buf,
+		       size_t             count)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+	unsigned int               copied;
+	int                        err;
+	struct companion_packet    p;
+
+	if (copy_from_user(p.data, buf, sizeof(p))) {
+		dev_err(parent, "copy from user not succeed in one call\n");
+		return -EFAULT;
+	}
+
+	if (is_can_type(&p))
+		return -EINVAL;
+
+	err = qm_io_txq_in(&priv->pm.qm, buf, count, &copied);
+	if (err) {
+		priv->pm.stats.io_tx_overflows++;
+		return err;
+	}
+
+	wake_up_interruptible(&priv->wait);
+	priv->pm.stats.io_tx++;
+	return copied;
+}
+EXPORT_SYMBOL_GPL(companion_do_io_tx);
+
+/**
+ * companion_do_io_rx() - receive IO packet
+ * @parent: address of the parent device
+ * @buf:    address of the user space buffer to receive
+ * @count:  number of bytes to copy
+ */
+int companion_do_io_rx(struct device *parent,
+		       char __user   *buf,
+		       size_t         count)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+	unsigned int               copied;
+	int                        err;
+
+	err = qm_io_rxq_out(&priv->pm.qm, buf, count, &copied);
+	return err ? err : copied;
+}
+EXPORT_SYMBOL_GPL(companion_do_io_rx);
+
+/**
+ * companion_do_can_tx() - send CAN packet
+ * @parent: address of the parent device
+ * @port:   which CAN port to send
+ * @prio:   priority of the CAN frame
+ * @cf:     address of the CAN frame to send
+ */
+int companion_do_can_tx(struct device          *parent,
+			u8                      port,
+			u8                      prio,
+			const struct can_frame *cf)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+	int                        err;
+
+	err = pm_can_data_tx(&priv->pm, port, prio, cf);
+	if (err)
+		return err;
+
+	wake_up_interruptible(&priv->wait);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(companion_do_can_tx);
+
+/**
+ * companion_do_can_rx() - receive CAN packet
+ * @parent: address of the parent device
+ * @port:   which CAN port to receive
+ * @cf:     address of the CAN frame to receive
+ */
+int companion_do_can_rx(struct device    *parent,
+			u8                port,
+			struct can_frame *cf)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+	return pm_can_data_rx(&priv->pm, port, cf);
+}
+EXPORT_SYMBOL_GPL(companion_do_can_rx);
+
+/**
+ * companion_do_can_err() - receive CAN error packet
+ * @parent: address of the parent device
+ * @port:   which CAN port to receive
+ * @bec:    address to store CAN error counter
+ * @state:  address to store CAN state
+ * @code:   address to store CAN error code
+ */
+int companion_do_can_err(struct device           *parent,
+			 u8                       port,
+			 struct can_berr_counter *bec,
+			 u8                      *state,
+			 u8                      *code)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+	return pm_can_err(&priv->pm, port, bec, state, code);
+}
+EXPORT_SYMBOL_GPL(companion_do_can_err);
+
+/**
+ * companion_do_set_can_bittiming() - set CAN bittiming
+ * @parent:    address of the parent device
+ * @port:      which CAN port to set
+ * @bittiming: address of the bittiming to set
+ */
+int companion_do_set_can_bittiming(struct device              *parent,
+				   u8                          port,
+				   const struct can_bittiming *bittiming)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+	int                        err;
+
+	err = pm_can_set_bittiming(&priv->pm, port, bittiming);
+	if (err)
+		return err;
+
+	wake_up_interruptible(&priv->wait);
+	return pm_wait_for_response(&priv->pm, port, bcp_can_bittiming);
+}
+EXPORT_SYMBOL_GPL(companion_do_set_can_bittiming);
+
+/**
+ * companion_do_set_can_mode() - set CAN mode
+ * @parent: address of the parent device
+ * @port:   which CAN port to set
+ * @mode:   the CAN mode to set
+ */
+int companion_do_set_can_mode(struct device *parent,
+			      u8             port,
+			      enum can_mode  mode)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+	int                        err;
+
+	err = pm_can_set_mode(&priv->pm, port, mode);
+	if (err)
+		return err;
+
+	wake_up_interruptible(&priv->wait);
+	return pm_wait_for_response(&priv->pm, port, bcp_can_mode);
+}
+EXPORT_SYMBOL_GPL(companion_do_set_can_mode);
+
+/**
+ * companion_do_set_can_ctrlmode() - set CAN control mode
+ * @parent: address of the parent device
+ * @port:   which CAN port to set
+ * @ctrl:   the CAN control mode to set
+ */
+int companion_do_set_can_ctrlmode(struct device *parent,
+				  u8             port,
+				  u32            ctrl)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+	int                        err;
+
+	err = pm_can_set_ctrlmode(&priv->pm, port, ctrl);
+	if (err)
+		return err;
+
+	wake_up_interruptible(&priv->wait);
+	return pm_wait_for_response(&priv->pm, port, bcp_can_mode);
+}
+EXPORT_SYMBOL_GPL(companion_do_set_can_ctrlmode);
+
+/**
+ * companion_do_get_can_status() - get CAN status
+ * @parent: address of the parent device
+ * @port:   which CAN port to receive
+ * @bec:    address of the CAN error counter to store
+ */
+int companion_do_get_can_status(struct device           *parent,
+				u8                       port,
+				struct can_berr_counter *bec)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+	int                        err;
+
+	err = pm_can_get_status(&priv->pm, port);
+	if (err)
+		return err;
+
+	wake_up_interruptible(&priv->wait);
+	err = pm_wait_for_response(&priv->pm, port, bcp_can_status);
+	if (err)
+		return err;
+
+	bec->rxerr = priv->pm.rx_err[port];
+	bec->txerr = priv->pm.tx_err[port];
+	return 0;
+}
+EXPORT_SYMBOL_GPL(companion_do_get_can_status);
+
+/**
+ * companion_do_get_can_txq_status() - get single CAN tx queue status
+ * @parent:        address of the parent device
+ * @port:          which CAN port to inquiry
+ * @prio:          which CAN queue to inquiry
+ * @lost_txq_sync: address of flag to store whether tx queue lost sync
+ */
+int companion_do_get_can_txq_status(struct device *parent,
+				    u8             port,
+				    u8             prio,
+				    bool          *lost_txq_sync)
+{
+	struct companion_spi_priv         *priv = dev_get_drvdata(parent);
+	struct companion_protocol_manager *pm   = &priv->pm;
+	u8                                 local, remote;
+	int                                err;
+
+	if (prio >= BCP_CAN_PRIOS)
+		return -EINVAL;
+
+	err = pm_can_get_txq_status(pm, port);
+	if (err)
+		return err;
+
+	wake_up_interruptible(&priv->wait);
+	err = pm_wait_for_response(pm, port, bcp_can_txq_status);
+	if (err)
+		return err;
+
+	local  = pm->local_txq[port][prio];
+	remote = pm->remote_txq[port][prio];
+
+	if (local != remote) {
+		*lost_txq_sync = true;
+		pm->stats.can_lost_txq_sync[port][prio]++;
+	} else {
+		*lost_txq_sync = false;
+		pm->stats.can_ack_timeout[port][prio]++;
+	}
+
+	pm->local_txq[port][prio] = remote;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(companion_do_get_can_txq_status);
+
+/**
+ * companion_do_get_can_txq_status_all() - get all CAN tx queue status
+ * @parent: address of the parent device
+ * @port:   which CAN port to inquiry
+ */
+int companion_do_get_can_txq_status_all(struct device *parent, u8 port)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+	int                        err;
+
+	err = pm_can_get_txq_status(&priv->pm, port);
+	if (err)
+		return err;
+
+	wake_up_interruptible(&priv->wait);
+	err = pm_wait_for_response(&priv->pm, port, bcp_can_txq_status);
+	if (err)
+		return err;
+
+	memcpy(priv->pm.local_txq[port],
+	       priv->pm.remote_txq[port],
+	       BCP_CAN_PRIOS);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(companion_do_get_can_txq_status_all);
+
+/**
+ * companion_do_can_txq_is_full() - inquiry CAN tx queue is full
+ * @parent:  address of the parent device
+ * @port:    which CAN port to inquiry
+ * @prio:    which CAN queue to inquiry
+ * @is_full: address of flag to store is full or not
+ */
+int companion_do_can_txq_is_full(struct device *parent,
+				 u8             port,
+				 u8             prio,
+				 bool          *is_full)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+	return pm_can_txq_is_full(&priv->pm, port, prio, is_full);
+}
+EXPORT_SYMBOL_GPL(companion_do_can_txq_is_full);
+
+/**
+ * companion_do_can_txq_has_space() - inquiry CAN tx queue has space
+ * @parent:    address of the parent device
+ * @port:      which CAN port to inquiry
+ * @prio:      which CAN queue to inquiry
+ * @has_space: address of flag to store has space or not
+ */
+int companion_do_can_txq_has_space(struct device *parent,
+				   u8             port,
+				   u8             prio,
+				   bool          *has_space)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+	return pm_can_txq_has_space(&priv->pm, port, prio, has_space);
+}
+EXPORT_SYMBOL_GPL(companion_do_can_txq_has_space);
+
+/**
+ * companion_do_can_start_tx_timer() - start CAN tx timeout detection
+ * @parent: address of the parent device
+ * @port:   which CAN port to start
+ * @prio:   which CAN queue to start
+ */
+int companion_do_can_start_tx_timer(struct device *parent,
+				    u8             port,
+				    u8             prio)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+	return pm_can_start_tx_timer(&priv->pm, port, prio);
+}
+EXPORT_SYMBOL_GPL(companion_do_can_start_tx_timer);
+
+/**
+ * companion_do_can_stop_tx_timer() - stop CAN tx timeout detection
+ * @parent: address of the parent device
+ * @port:   which CAN port to stop
+ * @prio:   which CAN queue to stop
+ */
+int companion_do_can_stop_tx_timer(struct device *parent,
+				   u8             port,
+				   u8             prio)
+{
+	struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+	return pm_can_stop_tx_timer(&priv->pm, port, prio);
+}
+EXPORT_SYMBOL_GPL(companion_do_can_stop_tx_timer);
+
+/**
+ * dump_packets_show() - display dump_packets value in sysfs entry
+ * @dev:  address of the device associated with sysfs entry
+ * @attr: address of the device attribute
+ * @buf:  address of the buffer to encode value
+ */
+static ssize_t dump_packets_show(struct device           *dev,
+				 struct device_attribute *attr,
+				 char                    *buf)
+{
+	struct spi_device         *spi  = to_spi_device(dev);
+	struct companion_spi_priv *priv = spi_get_drvdata(spi);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", priv->dump_packets);
+}
+
+/**
+ * dump_packets_store() - store dump_packets value from sysfs entry
+ * @dev:   address of the device associated with sysfs entry
+ * @attr:  address of the device attribute
+ * @buf:   address of the buffer to decode value
+ * @count: number of bytes in the buffer
+ */
+static ssize_t dump_packets_store(struct device           *dev,
+				  struct device_attribute *attr,
+				  const char              *buf,
+				  size_t                   count)
+{
+	struct spi_device         *spi  = to_spi_device(dev);
+	struct companion_spi_priv *priv = spi_get_drvdata(spi);
+	int                        err;
+
+	err = kstrtobool(buf, &priv->dump_packets);
+	if (err) {
+		dev_err(&spi->dev, "input invalid value: %s\n", buf);
+		return err;
+	}
+
+	return count;
+}
+static DEVICE_ATTR_RW(dump_packets);
+
+/**
+ * overflows_show() - display overflows value in sysfs entry
+ * @dev:  address of the device associated with sysfs entry
+ * @attr: address of the device attribute
+ * @buf:  address of the buffer to encode value
+ */
+static ssize_t overflows_show(struct device           *dev,
+			      struct device_attribute *attr,
+			      char                    *buf)
+{
+	struct spi_device         *spi  = to_spi_device(dev);
+	struct companion_spi_priv *priv = spi_get_drvdata(spi);
+	int                        ret, pos, i, j, total = 0;
+
+	ret = snprintf(buf, PAGE_SIZE, "io\ntx: %u, rx: %u\n\n",
+		       priv->pm.stats.io_tx_overflows,
+		       priv->pm.stats.io_rx_overflows);
+	pos = ret;
+
+	for (i = 0; i < BCP_CAN_PORTS; ++i) {
+		ret  = snprintf(buf + pos, PAGE_SIZE - pos, "can%u\n", i);
+		pos += ret;
+
+		for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+			ret = snprintf(buf + pos, PAGE_SIZE - pos,
+				       "[%u]:%u ", j,
+				       priv->pm.stats.can_tx_overflows[i][j]);
+			total += priv->pm.stats.can_tx_overflows[i][j];
+			pos   += ret;
+		}
+
+		ret  = snprintf(buf + pos, PAGE_SIZE - pos,
+				"\ntx: %u, rx: %u, err: %u\n\n",
+				total,
+				priv->pm.stats.can_rx_overflows[i],
+				priv->pm.stats.can_err_overflows[i]);
+		pos += ret;
+	}
+	return pos;
+}
+static DEVICE_ATTR_RO(overflows);
+
+/**
+ * traffic_show() - display traffic of IO and CAN in sysfs entry
+ * @dev:  address of the device associated with sysfs entry
+ * @attr: address of the device attribute
+ * @buf:  address of the buffer to encode value
+ */
+static ssize_t traffic_show(struct device           *dev,
+			    struct device_attribute *attr,
+			    char                    *buf)
+{
+	struct spi_device         *spi  = to_spi_device(dev);
+	struct companion_spi_priv *priv = spi_get_drvdata(spi);
+	int                        ret, pos, i, j;
+
+	ret = snprintf(buf, PAGE_SIZE, "io\ntx: %u, rx: %u\n\n",
+		       priv->pm.stats.io_tx, priv->pm.stats.io_rx);
+	pos = ret;
+
+	for (i = 0; i < BCP_CAN_PORTS; ++i) {
+		ret  = snprintf(buf + pos, PAGE_SIZE - pos, "can%u\n", i);
+		pos += ret;
+
+		ret  = snprintf(buf + pos, PAGE_SIZE - pos, "tx         : ");
+		pos += ret;
+		for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+			ret  = snprintf(buf + pos, PAGE_SIZE - pos,
+					"[%u]:%u ", j,
+					priv->pm.stats.can_tx[i][j]);
+			pos += ret;
+		}
+
+		ret  = snprintf(buf + pos, PAGE_SIZE - pos, "\nack success: ");
+		pos += ret;
+		for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+			ret  = snprintf(buf + pos, PAGE_SIZE - pos,
+					"[%u]:%u ", j,
+					priv->pm.stats.can_ack_success[i][j]);
+			pos += ret;
+		}
+
+		ret  = snprintf(buf + pos, PAGE_SIZE - pos, "\nack failure: ");
+		pos += ret;
+		for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+			ret  = snprintf(buf + pos, PAGE_SIZE - pos,
+					"[%u]:%u ", j,
+					priv->pm.stats.can_ack_failure[i][j]);
+			pos += ret;
+		}
+
+		ret  = snprintf(buf + pos, PAGE_SIZE - pos, "\nlost seq   : ");
+		pos += ret;
+		for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+			ret  = snprintf(buf + pos, PAGE_SIZE - pos,
+					"[%u]:%u ", j,
+					priv->pm.stats.can_lost_seq_sync[i][j]);
+			pos += ret;
+		}
+
+		ret  = snprintf(buf + pos, PAGE_SIZE - pos, "\nlost txq   : ");
+		pos += ret;
+		for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+			ret  = snprintf(buf + pos, PAGE_SIZE - pos,
+					"[%u]:%u ", j,
+					priv->pm.stats.can_lost_txq_sync[i][j]);
+			pos += ret;
+		}
+
+		ret  = snprintf(buf + pos, PAGE_SIZE - pos, "\nack timeout: ");
+		pos += ret;
+		for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+			ret  = snprintf(buf + pos, PAGE_SIZE - pos,
+					"[%u]:%u ", j,
+					priv->pm.stats.can_ack_timeout[i][j]);
+			pos += ret;
+		}
+
+		ret  = snprintf(buf + pos, PAGE_SIZE - pos, "\nack unexpect:");
+		pos += ret;
+		for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+			ret  = snprintf(buf + pos, PAGE_SIZE - pos,
+					"[%u]:%u ", j,
+					priv->pm.stats.can_ack_unexpect[i][j]);
+			pos += ret;
+		}
+
+		ret  = snprintf(buf + pos, PAGE_SIZE - pos,
+				"\nrx         : %u\nerr        : %u\n\n",
+				priv->pm.stats.can_rx[i],
+				priv->pm.stats.can_err[i]);
+		pos += ret;
+	}
+	return pos;
+}
+static DEVICE_ATTR_RO(traffic);
+
+/**
+ * can_space_show() - display CAN queue space in sysfs entry
+ * @dev:  address of the device associated with sysfs entry
+ * @attr: address of the device attribute
+ * @buf:  address of the buffer to encode value
+ */
+static ssize_t can_space_show(struct device           *dev,
+			      struct device_attribute *attr,
+			      char                    *buf)
+{
+	struct spi_device         *spi  = to_spi_device(dev);
+	struct companion_spi_priv *priv = spi_get_drvdata(spi);
+	int                        i, j, ret, pos = 0;
+
+	for (i = 0; i < BCP_CAN_PORTS; ++i) {
+		ret  = snprintf(buf + pos, PAGE_SIZE - pos, "can%u\n", i);
+		pos += ret;
+
+		ret  = snprintf(buf + pos, PAGE_SIZE - pos, "local : ");
+		pos += ret;
+		for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+			ret  = snprintf(buf + pos, PAGE_SIZE - pos,
+					"[%u]:%u ", j,
+					priv->pm.local_txq[i][j]);
+			pos += ret;
+		}
+
+		ret  = snprintf(buf + pos, PAGE_SIZE - pos, "\nremote: ");
+		pos += ret;
+		for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+			ret  = snprintf(buf + pos, PAGE_SIZE - pos,
+					"[%u]:%u ", j,
+					priv->pm.remote_txq[i][j]);
+			pos += ret;
+		}
+
+		ret  = snprintf(buf + pos, PAGE_SIZE - pos, "\n\n");
+		pos += ret;
+	}
+	return pos;
+}
+static DEVICE_ATTR_RO(can_space);
+
+/**
+ * busy_show() - display busy signal statisitics in sysfs entry
+ * @dev:  address of the device associated with sysfs entry
+ * @attr: address of the device attribute
+ * @buf:  address of the buffer to encode value
+ */
+static ssize_t busy_show(struct device           *dev,
+			 struct device_attribute *attr,
+			 char                    *buf)
+{
+	struct spi_device         *spi  = to_spi_device(dev);
+	struct companion_spi_priv *priv = spi_get_drvdata(spi);
+
+	return snprintf(buf, PAGE_SIZE,
+			"while_busy_ext       : %u\n"
+			"while_busy_fail      : %u\n"
+			"until_busy_ext       : %u\n"
+			"until_busy_fail      : %u\n"
+			"force_started        : %u\n"
+			"force_started_failure: %u\n"
+			"ready_failure        : %u\n",
+			priv->stats.while_busy_ext,
+			priv->stats.while_busy_fail,
+			priv->stats.until_busy_ext,
+			priv->stats.until_busy_fail,
+			priv->stats.force_started,
+			priv->stats.force_started_failure,
+			priv->stats.ready_failure);
+}
+static DEVICE_ATTR_RO(busy);
+
+static struct attribute *companion_spi_sysfs_attrs[] = {
+	&dev_attr_dump_packets.attr,
+	&dev_attr_overflows.attr,
+	&dev_attr_traffic.attr,
+	&dev_attr_can_space.attr,
+	&dev_attr_busy.attr,
+	NULL
+};
+
+static struct attribute_group companion_spi_attribute_group = {
+	.attrs = companion_spi_sysfs_attrs
+};
+
+/**
+ * slave_has_request() - inquiry spi slave has request
+ * @priv: address of companion-spi private data
+ */
+static inline bool slave_has_request(struct companion_spi_priv *priv)
+{
+	return gpiod_get_value_cansleep(priv->gpiod_request) != 0;
+}
+
+/**
+ * slave_is_busy() - inquiry spi slave is busy
+ * @priv: address of companion-spi private data
+ */
+static inline bool slave_is_busy(struct companion_spi_priv *priv)
+{
+	return gpiod_get_value_cansleep(priv->gpiod_busy) != 0;
+}
+
+/**
+ * slave_is_not_busy() - inquiry spi slave is not busy
+ * @priv: address of companion-spi private data
+ */
+static inline bool slave_is_not_busy(struct companion_spi_priv *priv)
+{
+	return gpiod_get_value_cansleep(priv->gpiod_busy) == 0;
+}
+
+/**
+ * slave_select() - select spi slave
+ * @priv: address of companion-spi private data
+ */
+static inline void slave_select(struct companion_spi_priv *priv)
+{
+	gpiod_set_value_cansleep(priv->gpiod_cs, 1);
+}
+
+/**
+ * slave_deselect() - deselect spi slave
+ * @priv: address of companion-spi private data
+ */
+static inline void slave_deselect(struct companion_spi_priv *priv)
+{
+	gpiod_set_value_cansleep(priv->gpiod_cs, 0);
+}
+
+/**
+ * companion_spi_wait_while_busy() - wait while spi slave is busy
+ * @priv: address of companion-spi private data
+ */
+static int companion_spi_wait_while_busy(struct companion_spi_priv *priv)
+{
+	unsigned int count;
+
+	/*
+	 * as short as possible wait while busy polling which shall
+	 * succeed most of the times
+	 */
+	count = READY_POLL_US / READY_POLL_US_GRAN;
+	do {
+		if (slave_is_not_busy(priv))
+			return 0;
+
+		udelay(READY_POLL_US_GRAN);
+	} while (--count);
+
+	/*
+	 * wait while busy polling with sleeping, in case companion
+	 * is busy with other things, this shall happen rarely
+	 */
+	count = READY_POLL_MS / READY_POLL_MS_GRAN;
+	do {
+		if (slave_is_not_busy(priv)) {
+			priv->stats.while_busy_ext++;
+			dev_info(&priv->spi->dev,
+				 "waited long while busy (%u)\n",
+				 priv->stats.while_busy_ext);
+			return 0;
+		}
+
+		msleep(READY_POLL_MS_GRAN);
+	} while (--count);
+
+	priv->stats.while_busy_fail++;
+	dev_err(&priv->spi->dev,
+		"time out waiting for busy deassertion (%u)\n",
+		priv->stats.while_busy_fail);
+	return -EBUSY;
+}
+
+/**
+ * companion_spi_wait_until_busy() - wait until spi slave is busy
+ * @priv: address of companion-spi private data
+ */
+static int companion_spi_wait_until_busy(struct companion_spi_priv *priv)
+{
+	unsigned int count;
+
+	/*
+	 * as short as possible wait until busy polling which shall
+	 * succeed most of the times
+	 */
+	count = READY_POLL_US / READY_POLL_US_GRAN;
+	do {
+		if (slave_is_busy(priv))
+			return 0;
+
+		udelay(READY_POLL_US_GRAN);
+	} while (--count);
+
+	/*
+	 * wait until busy polling with sleeping, in case companion
+	 * is busy with other things, this shall happen rarely
+	 */
+	count = READY_POLL_MS / READY_POLL_MS_GRAN;
+	do {
+		if (slave_is_busy(priv)) {
+			priv->stats.until_busy_ext++;
+			dev_info(&priv->spi->dev,
+				 "waited long until busy (%u)\n",
+				 priv->stats.until_busy_ext);
+			return 0;
+		}
+
+		msleep(READY_POLL_MS_GRAN);
+	} while (--count);
+
+	priv->stats.until_busy_fail++;
+	dev_err(&priv->spi->dev,
+		"time out waiting for busy assertion (%u)\n",
+		priv->stats.until_busy_fail);
+	return -EBUSY;
+}
+
+/**
+ * companion_spi_cpu_to_be32() - convert companion packet to big endian 32 bit
+ * @buf: address of the packet to convert
+ */
+static void companion_spi_cpu_to_be32(char *buf)
+{
+	u32 *buf32 = (u32 *)buf;
+	int  i;
+
+	for (i = 0; i < (BCP_PACKET_SIZE / sizeof(u32)); i++, buf32++)
+		*buf32 = cpu_to_be32(*buf32);
+}
+
+/**
+ * companion_spi_be32_to_cpu() - convert companion packet from big endian 32 bit
+ * @buf: address of the packet to convert
+ */
+static void companion_spi_be32_to_cpu(char *buf)
+{
+	u32 *buf32 = (u32 *)buf;
+	int  i;
+
+	for (i = 0; i < (BCP_PACKET_SIZE / sizeof(u32)); i++, buf32++)
+		*buf32 = be32_to_cpu(*buf32);
+}
+
+/**
+ * companion_spi_transceive() - transceive spi message
+ * @priv:     address of companion-spi private data
+ * @message:  address of the spi message to transceive
+ * @transfer: address of the spi transfer
+ */
+static void companion_spi_transceive(struct companion_spi_priv *priv,
+				     struct spi_message        *message,
+				     struct spi_transfer       *transfer)
+{
+	const struct companion_packet *p;
+	int                            err;
+
+	if (priv->dump_packets) {
+		p = transfer->tx_buf;
+		dump_packet(p, KERN_INFO, DRIVER_NAME" Tx: ");
+	}
+
+	companion_spi_cpu_to_be32((char *)transfer->tx_buf);
+
+	spi_message_init_with_transfers(message, transfer, 1);
+
+	err = companion_spi_wait_while_busy(priv);
+
+	slave_select(priv);
+
+	if (err) {
+		priv->stats.force_started++;
+		dev_err(&priv->spi->dev,
+			"force started transfer (%u)\n",
+			priv->stats.force_started);
+
+		/* wait slave to pull up busy line in case force started */
+		err = companion_spi_wait_while_busy(priv);
+		if (err) {
+			priv->stats.force_started_failure++;
+			dev_err(&priv->spi->dev,
+				"force started failed, continuing (%u)\n",
+				priv->stats.force_started_failure);
+		}
+	}
+
+	err = companion_spi_wait_until_busy(priv);
+	if (err) {
+		priv->stats.ready_failure++;
+		dev_err(&priv->spi->dev,
+			"started transfer in case not ready (%u)\n",
+			priv->stats.ready_failure);
+	}
+
+	err = spi_sync(priv->spi, message);
+	if (err)
+		dev_err(&priv->spi->dev,
+			"sending spi message failed: %d\n",
+			message->status);
+
+	slave_deselect(priv);
+
+	companion_spi_be32_to_cpu(transfer->rx_buf);
+
+	if (priv->dump_packets) {
+		p = transfer->rx_buf;
+		dump_packet(p, KERN_INFO, DRIVER_NAME" Rx: ");
+	}
+}
+
+/**
+ * companion_spi_request_irq() - irq handler of request signal
+ * @irq:  irq number of request signal
+ * @data: address of user supplied data for irq handler
+ */
+static irqreturn_t companion_spi_request_irq(int irq, void *data)
+{
+	struct companion_spi_priv *priv = data;
+
+	wake_up_interruptible(&priv->wait);
+	return IRQ_HANDLED;
+}
+
+/**
+ * companion_spi_thread() - main thread to drive spi communication
+ * @data: address of user supplied data for thread
+ */
+static int companion_spi_thread(void *data)
+{
+	struct companion_spi_priv *priv = data;
+	struct companion_packet    tx_packet;
+	struct companion_packet    rx_packet;
+	struct spi_message         message;
+	struct spi_transfer        transfer;
+
+	memset(&transfer, 0, sizeof(transfer));
+	transfer.tx_buf        = tx_packet.data;
+	transfer.rx_buf        = rx_packet.data;
+	transfer.len           = sizeof(struct companion_packet);
+	transfer.cs_change     = 0;
+	transfer.bits_per_word = 32;
+
+	do {
+		if (wait_event_interruptible(priv->wait,
+					     kthread_should_stop()   ||
+					     slave_has_request(priv) ||
+					     qm_has_tx_data(&priv->pm.qm)))
+			continue;
+
+		if (kthread_should_stop())
+			break;
+
+		pm_prepare_tx(&priv->pm, &tx_packet);
+		companion_spi_transceive(priv, &message, &transfer);
+		pm_on_tx_done(&priv->pm);
+		pm_on_rx_done(&priv->pm, &rx_packet);
+	} while (!kthread_should_stop());
+
+	return 0;
+}
+
+static const struct of_device_id companion_spi_of_match[] = {
+	{ .compatible = "bosch,companion-spi", .data = NULL, },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, companion_spi_of_match);
+
+/**
+ * companion_spi_parse_dt() - parse device tree
+ * @priv: address of companion-spi private data
+ */
+static int companion_spi_parse_dt(struct companion_spi_priv *priv)
+{
+	struct device      *dev = &priv->spi->dev;
+	struct device_node *np  = dev->of_node;
+	int                 err;
+
+	if (!np) {
+		dev_err(dev, "no device tree data\n");
+		return -EINVAL;
+	}
+
+	priv->gpiod_request = devm_gpiod_get(dev, "request", GPIOD_IN);
+	if (IS_ERR(priv->gpiod_request)) {
+		err = PTR_ERR(priv->gpiod_request);
+		if (err != -EPROBE_DEFER)
+			dev_err(dev, "invalid 'request-gpios':%d\n", err);
+		return err;
+	}
+
+	priv->gpiod_busy = devm_gpiod_get(dev, "busy", GPIOD_IN);
+	if (IS_ERR(priv->gpiod_busy)) {
+		err = PTR_ERR(priv->gpiod_busy);
+		if (err != -EPROBE_DEFER)
+			dev_err(dev, "invalid 'busy-gpios':%d\n", err);
+		return err;
+	}
+
+	priv->gpiod_cs = devm_gpiod_get(dev, "cs", GPIOD_OUT_HIGH);
+	if (IS_ERR(priv->gpiod_cs)) {
+		err = PTR_ERR(priv->gpiod_cs);
+		if (err != -EPROBE_DEFER)
+			dev_err(dev, "invalid 'cs-gpios':%d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+/**
+ * companion_spi_probe() - probe callback
+ * @spi: address of the spi device
+ */
+static int companion_spi_probe(struct spi_device *spi)
+{
+	struct companion_spi_priv *priv;
+	u8                         null_packet[BCP_PACKET_SIZE] = {0};
+	int                        err;
+
+	priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->spi = spi;
+	init_waitqueue_head(&priv->wait);
+	pm_init(&priv->pm);
+
+	err = companion_spi_parse_dt(priv);
+	if (err)
+		return err;
+
+	spi->mode = SPI_MODE_1;
+
+	err = spi_setup(spi);
+	if (err) {
+		dev_err(&spi->dev, "spi_setup() returns: %d\n", err);
+		return err;
+	}
+
+	err = spi_write(spi, null_packet, sizeof(null_packet));
+	if (err) {
+		dev_err(&spi->dev, "dummy transfer failed: %d\n", err);
+		return err;
+	}
+
+	spi_set_drvdata(spi, priv);
+
+	err = sysfs_create_group(&spi->dev.kobj,
+				 &companion_spi_attribute_group);
+	if (err) {
+		dev_err(&spi->dev, "sysfs_create_group() returns: %d\n", err);
+		return err;
+	}
+
+	priv->task = kthread_run(companion_spi_thread, priv, DRIVER_NAME);
+	if (!priv->task)
+		return -EIO;
+
+	err = devm_request_irq(&spi->dev,
+			       gpiod_to_irq(priv->gpiod_request),
+			       companion_spi_request_irq,
+			       IRQF_TRIGGER_FALLING,
+			       "companion-spi-request",
+			       priv);
+	if (err)
+		return -ENODEV;
+
+	return of_platform_populate(spi->dev.of_node, NULL, NULL, &spi->dev);
+}
+
+/**
+ * companion_spi_remove() - remove callback
+ * @spi: address of the spi device
+ */
+static int companion_spi_remove(struct spi_device *spi)
+{
+	struct companion_spi_priv *priv = spi_get_drvdata(spi);
+
+	qm_reset(&priv->pm.qm);
+	kthread_stop(priv->task);
+	sysfs_remove_group(&spi->dev.kobj, &companion_spi_attribute_group);
+	of_platform_depopulate(&spi->dev);
+	return 0;
+}
+
+static struct spi_driver companion_spi_driver = {
+	.driver = {
+		.name           = DRIVER_NAME,
+		.of_match_table = of_match_ptr(companion_spi_of_match),
+	},
+	.probe  = companion_spi_probe,
+	.remove = companion_spi_remove,
+};
+module_spi_driver(companion_spi_driver);
+
+MODULE_AUTHOR("Zhu Yi <yi.zhu5@cn.bosch.com>");
+MODULE_DESCRIPTION("Companion low level init/core code");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/companion/protocol-manager.c b/drivers/spi/companion/protocol-manager.c
new file mode 100644
index 0000000..1b09c27
--- /dev/null
+++ b/drivers/spi/companion/protocol-manager.c
@@ -0,0 +1,1032 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Companion protocol manager code
+ *
+ * Copyright (C) 2015-2018 Bosch Sicherheitssysteme GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "protocol-manager.h"
+
+#define PM_RESPONSE_TIMEOUT HZ
+#define PM_CAN_TX_TIMEOUT   msecs_to_jiffies(30000)
+
+/**
+ * struct companion_filter - companion packet filter
+ * @node:    filter list node
+ * @match:   address of match callback
+ * @process: address of process callback
+ */
+struct companion_filter {
+	struct list_head node;
+	bool (*match)(const struct companion_packet *p);
+	void (*process)(struct companion_protocol_manager *pm,
+			const struct companion_packet     *p);
+};
+
+/**
+ * null_match() - match null packet
+ * @p: address of the packet to handle
+ */
+static bool null_match(const struct companion_packet *p)
+{
+	return is_null_type(p);
+}
+
+/**
+ * io_match() - match IO packet
+ * @p: address of the packet to handle
+ */
+static bool io_match(const struct companion_packet *p)
+{
+	return is_io_type(p);
+}
+
+/**
+ * io_process() - process IO packet
+ * @pm: address of the protocol manager
+ * @p:  address of the packet to handle
+ */
+static void io_process(struct companion_protocol_manager *pm,
+		       const struct companion_packet     *p)
+{
+	if (qm_io_rxq_in(&pm->qm, p)) {
+		down_read(&pm->io_lock);
+		if (pm->io_ops && pm->io_ops->on_rx_done)
+			pm->io_ops->on_rx_done(pm->io_data);
+		up_read(&pm->io_lock);
+
+		pm->stats.io_rx++;
+	} else {
+		pm->stats.io_rx_overflows++;
+	}
+}
+
+/**
+ * can_data_match() - match CAN data packet
+ * @p: address of the packet to handle
+ */
+static bool can_data_match(const struct companion_packet *p)
+{
+	return ((const struct can_data_frame *)p)->type == BCP_CAN_DATA;
+}
+
+/**
+ * can_data_process() - process CAN data packet
+ * @pm: address of the protocol manager
+ * @p:  address of the packet to handle
+ */
+static void can_data_process(struct companion_protocol_manager *pm,
+			     const struct companion_packet     *p)
+{
+	u8 port = ((const struct can_data_frame *)p)->port - 1;
+
+	if (port >= BCP_CAN_PORTS)
+		return;
+
+	if (qm_can_rxq_in(&pm->qm, p, port)) {
+		down_read(&pm->can_lock[port]);
+		if (pm->can_ops[port] && pm->can_ops[port]->on_rx_done)
+			pm->can_ops[port]->on_rx_done(pm->can_data[port]);
+		up_read(&pm->can_lock[port]);
+
+		pm->stats.can_rx[port]++;
+	} else {
+		pm->stats.can_rx_overflows[port]++;
+	}
+}
+
+/**
+ * can_bittiming_match() - match CAN bittiming packet
+ * @p: address of the packet to handle
+ */
+static bool can_bittiming_match(const struct companion_packet *p)
+{
+	return ((const struct can_bittiming_response *)p)->type ==
+		BCP_CAN_BITTIMING;
+}
+
+/**
+ * can_bittiming_process() - process CAN bittiming packet
+ * @pm: address of the protocol manager
+ * @p:  address of the packet to handle
+ */
+static void can_bittiming_process(struct companion_protocol_manager *pm,
+				  const struct companion_packet     *p)
+{
+	u8 port   = ((const struct can_bittiming_response *)p)->port - 1;
+	u8 status = ((const struct can_bittiming_response *)p)->status;
+
+	if (port >= BCP_CAN_PORTS)
+		return;
+
+	if (status == BCP_STATUS_SUCCESS) {
+		pm->response[port][bcp_can_bittiming] = 0;
+		pm->stats.can_ack_success[port][0]++;
+	} else {
+		pm->response[port][bcp_can_bittiming] = -EINVAL;
+		pm->stats.can_ack_failure[port][0]++;
+	}
+
+	if (test_and_clear_bit(bcp_can_bittiming, &pm->flags[port]))
+		wake_up_interruptible_all(&pm->wait[port]);
+}
+
+/**
+ * can_mode_match() - match CAN mode packet
+ * @p: address of the packet to handle
+ */
+static bool can_mode_match(const struct companion_packet *p)
+{
+	return ((const struct can_mode_response *)p)->type ==
+		BCP_CAN_MODE;
+}
+
+/**
+ * can_mode_process() - process CAN mode packet
+ * @pm: address of the protocol manager
+ * @p:  address of the packet to handle
+ */
+static void can_mode_process(struct companion_protocol_manager *pm,
+			     const struct companion_packet     *p)
+{
+	u8 port   = ((const struct can_mode_response *)p)->port - 1;
+	u8 status = ((const struct can_mode_response *)p)->status;
+
+	if (port >= BCP_CAN_PORTS)
+		return;
+
+	if (status == BCP_STATUS_SUCCESS) {
+		pm->response[port][bcp_can_mode] = 0;
+		pm->stats.can_ack_success[port][0]++;
+	} else {
+		pm->response[port][bcp_can_mode] = -EINVAL;
+		pm->stats.can_ack_failure[port][0]++;
+	}
+
+	if (test_and_clear_bit(bcp_can_mode, &pm->flags[port]))
+		wake_up_interruptible_all(&pm->wait[port]);
+}
+
+/**
+ * can_status_match() - match CAN status packet
+ * @p: address of the packet to handle
+ */
+static bool can_status_match(const struct companion_packet *p)
+{
+	return ((const struct can_status_response *)p)->type ==
+		BCP_CAN_STATUS;
+}
+
+/**
+ * can_status_process() - process CAN status packet
+ * @pm: address of the protocol manager
+ * @p:  address of the packet to handle
+ */
+static void can_status_process(struct companion_protocol_manager *pm,
+			       const struct companion_packet     *p)
+{
+	u8 port   = ((const struct can_status_response *)p)->port - 1;
+	u8 rx_err = ((const struct can_status_response *)p)->rx_err;
+	u8 tx_err = ((const struct can_status_response *)p)->tx_err;
+	u8 status = ((const struct can_status_response *)p)->status;
+
+	if (port >= BCP_CAN_PORTS)
+		return;
+
+	if (status == BCP_STATUS_SUCCESS) {
+		pm->response[port][bcp_can_status] = 0;
+		pm->rx_err[port]                   = rx_err;
+		pm->tx_err[port]                   = tx_err;
+
+		if (test_bit(bcp_can_status, &pm->flags[port])) {
+			pm->stats.can_ack_success[port][0]++;
+			goto polling_out;
+		}
+
+		if (qm_can_err_in(&pm->qm, p, port)) {
+			down_read(&pm->can_lock[port]);
+			if (pm->can_ops[port] && pm->can_ops[port]->on_error)
+				pm->can_ops[port]->on_error(pm->can_data[port]);
+			up_read(&pm->can_lock[port]);
+
+			pm->stats.can_err[port]++;
+		} else {
+			pm->stats.can_err_overflows[port]++;
+		}
+	} else {
+		pm->response[port][bcp_can_status] = -EINVAL;
+		if (test_bit(bcp_can_status, &pm->flags[port]))
+			pm->stats.can_ack_failure[port][0]++;
+	}
+
+polling_out:
+	if (test_and_clear_bit(bcp_can_status, &pm->flags[port]))
+		wake_up_interruptible_all(&pm->wait[port]);
+}
+
+/**
+ * can_tx_ack_match() - match CAN tx acknowledge packet
+ * @p: address of the packet to handle
+ */
+static bool can_tx_ack_match(const struct companion_packet *p)
+{
+	return ((const struct can_tx_acknowledge *)p)->type ==
+		BCP_CAN_TX_ACK;
+}
+
+/**
+ * can_tx_ack_process() - process CAN tx acknowledge packet
+ * @pm: address of the protocol manager
+ * @p:  address of the packet to handle
+ */
+static void can_tx_ack_process(struct companion_protocol_manager *pm,
+			       const struct companion_packet     *p)
+{
+	u8   port     = ((const struct can_tx_acknowledge *)p)->port - 1;
+	u8   prio     = ((const struct can_tx_acknowledge *)p)->prio;
+	u8   sequence = ((const struct can_tx_acknowledge *)p)->sequence;
+	u8   status   = ((const struct can_tx_acknowledge *)p)->status;
+	u8   space    = ((const struct can_tx_acknowledge *)p)->space;
+	bool lost_seq = false;
+
+	if (port >= BCP_CAN_PORTS || prio >= BCP_CAN_PRIOS)
+		return;
+
+	/* local_txq will be decreased after kernel sent data to companion,
+	 * remote_txq will be increased after companion send ack to kernel,
+	 * so local_txq < remote_txq should be guaranteed. Otherwise, kernel
+	 * received unexpected ack, hence ignore it.
+	 */
+	pm->remote_txq[port][prio] = space;
+	if (pm->local_txq[port][prio] >= space) {
+		pm->stats.can_ack_unexpect[port][prio]++;
+		return;
+	}
+
+	pm->local_txq[port][prio]++;
+	pm->sequence[port][prio]++;
+	if (pm->sequence[port][prio] != sequence) {
+		lost_seq                 = true;
+		pm->sequence[port][prio] = sequence;
+		pm->stats.can_lost_seq_sync[port][prio]++;
+	}
+
+	down_read(&pm->can_lock[port]);
+	if (pm->can_ops[port] && pm->can_ops[port]->on_tx_done)
+		pm->can_ops[port]->on_tx_done(pm->can_data[port],
+					      prio,
+					      lost_seq,
+					      status == BCP_STATUS_SUCCESS);
+	up_read(&pm->can_lock[port]);
+
+	if (status == BCP_STATUS_SUCCESS)
+		pm->stats.can_ack_success[port][prio]++;
+	else
+		pm->stats.can_ack_failure[port][prio]++;
+}
+
+/**
+ * can_txq_status_match() - match CAN tx queue status packet
+ * @p: address of the packet to handle
+ */
+static bool can_txq_status_match(const struct companion_packet *p)
+{
+	return ((const struct can_txq_status_response *)p)->type ==
+		BCP_CAN_TX_QUEUE_STATUS;
+}
+
+/**
+ * can_txq_status_process() - process CAN tx queue status packet
+ * @pm: address of the protocol manager
+ * @p:  address of the packet to handle
+ */
+static void can_txq_status_process(struct companion_protocol_manager *pm,
+				   const struct companion_packet     *p)
+{
+	u8        port  = ((const struct can_txq_status_response *)p)->port - 1;
+	const u8 *space = ((const struct can_txq_status_response *)p)->space;
+	u8       status = ((const struct can_txq_status_response *)p)->status;
+
+	if (port >= BCP_CAN_PORTS)
+		return;
+
+	if (status == BCP_STATUS_SUCCESS) {
+		memcpy(pm->remote_txq[port], space, BCP_CAN_PRIOS);
+		pm->response[port][bcp_can_txq_status] = 0;
+		pm->stats.can_ack_success[port][0]++;
+	} else {
+		pm->response[port][bcp_can_txq_status] = -EINVAL;
+		pm->stats.can_ack_failure[port][0]++;
+	}
+
+	if (test_and_clear_bit(bcp_can_txq_status, &pm->flags[port]))
+		wake_up_interruptible_all(&pm->wait[port]);
+}
+
+/**
+ * unknown_match() - match unknown packet
+ * @p: address of the packet to handle
+ */
+static bool unknown_match(const struct companion_packet *p)
+{
+	return true;
+}
+
+/**
+ * unknown_process() - process unknown packet
+ * @pm: address of the protocol manager
+ * @p:  address of the packet to handle
+ */
+static void unknown_process(struct companion_protocol_manager *pm,
+			    const struct companion_packet     *p)
+{
+	dump_packet(p, KERN_ERR, "Unknown packet: ");
+}
+
+static struct companion_filter null_filter = {
+	.node    = LIST_HEAD_INIT(null_filter.node),
+	.match   = null_match,
+	.process = NULL,
+};
+
+static struct companion_filter io_filter = {
+	.node    = LIST_HEAD_INIT(io_filter.node),
+	.match   = io_match,
+	.process = io_process,
+};
+
+static struct companion_filter can_data_filter = {
+	.node    = LIST_HEAD_INIT(can_data_filter.node),
+	.match   = can_data_match,
+	.process = can_data_process,
+};
+
+static struct companion_filter can_bittiming_filter = {
+	.node    = LIST_HEAD_INIT(can_bittiming_filter.node),
+	.match   = can_bittiming_match,
+	.process = can_bittiming_process,
+};
+
+static struct companion_filter can_mode_filter = {
+	.node    = LIST_HEAD_INIT(can_mode_filter.node),
+	.match   = can_mode_match,
+	.process = can_mode_process,
+};
+
+static struct companion_filter can_status_filter = {
+	.node    = LIST_HEAD_INIT(can_status_filter.node),
+	.match   = can_status_match,
+	.process = can_status_process,
+};
+
+static struct companion_filter can_tx_ack_filter = {
+	.node    = LIST_HEAD_INIT(can_tx_ack_filter.node),
+	.match   = can_tx_ack_match,
+	.process = can_tx_ack_process,
+};
+
+static struct companion_filter can_txq_status_filter = {
+	.node    = LIST_HEAD_INIT(can_txq_status_filter.node),
+	.match   = can_txq_status_match,
+	.process = can_txq_status_process,
+};
+
+static struct companion_filter unknown_filter = {
+	.node    = LIST_HEAD_INIT(unknown_filter.node),
+	.match   = unknown_match,
+	.process = unknown_process,
+};
+
+/**
+ * to_timer_data() - helper to access companion_timer_data in work_struct
+ * @ws: address of the work_struct object
+ */
+static struct companion_timer_data *to_timer_data(struct work_struct *ws)
+{
+	return container_of(ws, struct companion_timer_data, work);
+}
+
+/**
+ * pm_can_tx_timeout_callback() - CAN tx timeout callback
+ * @ws: address of the work_struct object
+ */
+static void pm_can_tx_timeout_callback(struct work_struct *ws)
+{
+	struct companion_timer_data       *td   = to_timer_data(ws);
+	struct companion_protocol_manager *pm   = td->pm;
+	u8                                 port = td->port;
+	u8                                 prio = td->prio;
+
+	down_read(&pm->can_lock[port]);
+	if (pm->can_ops[port] && pm->can_ops[port]->on_tx_timeout)
+		pm->can_ops[port]->on_tx_timeout(pm->can_data[port], prio);
+	up_read(&pm->can_lock[port]);
+}
+
+/**
+ * pm_can_on_tx_timeout() - CAN tx timeout handler
+ * @tl: address of the timer_list object
+ */
+static void pm_can_on_tx_timeout(struct timer_list *tl)
+{
+	struct companion_timer *ct = from_timer(ct, tl, timer);
+
+	schedule_work(&ct->data.work);
+}
+
+#define CHECK_SIZE(x) \
+	BUILD_BUG_ON(sizeof(struct companion_packet) != sizeof(x))
+
+/**
+ * pm_init() - initialize the protocol manager
+ * @pm: address of the protocol manager to be initialized
+ */
+void pm_init(struct companion_protocol_manager *pm)
+{
+	int i;
+
+	/* sanity check for correct packet size at compile time */
+	CHECK_SIZE(struct can_data_frame);
+	CHECK_SIZE(struct can_bittiming_request);
+	CHECK_SIZE(struct can_bittiming_response);
+	CHECK_SIZE(struct can_mode_request);
+	CHECK_SIZE(struct can_mode_response);
+	CHECK_SIZE(struct can_status_request);
+	CHECK_SIZE(struct can_status_response);
+	CHECK_SIZE(struct can_tx_acknowledge);
+	CHECK_SIZE(struct can_txq_status_request);
+	CHECK_SIZE(struct can_txq_status_response);
+
+
+	init_rwsem(&pm->io_lock);
+	for (i = 0; i < BCP_CAN_PORTS; ++i) {
+		init_rwsem(&pm->can_lock[i]);
+		init_waitqueue_head(&pm->wait[i]);
+	}
+
+	qm_init(&pm->qm);
+
+	INIT_LIST_HEAD(&pm->filters);
+	list_add_tail(&null_filter.node,           &pm->filters);
+	list_add_tail(&io_filter.node,             &pm->filters);
+	list_add_tail(&can_data_filter.node,       &pm->filters);
+	list_add_tail(&can_tx_ack_filter.node,     &pm->filters);
+	list_add_tail(&can_bittiming_filter.node,  &pm->filters);
+	list_add_tail(&can_mode_filter.node,       &pm->filters);
+	list_add_tail(&can_status_filter.node,     &pm->filters);
+	list_add_tail(&can_txq_status_filter.node, &pm->filters);
+	list_add_tail(&unknown_filter.node,        &pm->filters);
+}
+
+/**
+ * pm_io_ops_register() - register companion IO packets handler
+ * @pm:   address of the protocol manager to be registered
+ * @ops:  address of the IO packets callback
+ * @data: address of the IO packets callback argument
+ */
+int pm_io_ops_register(struct companion_protocol_manager *pm,
+		       struct companion_io_ops           *ops,
+		       void                              *data)
+{
+	int result = 0;
+
+	down_write(&pm->io_lock);
+	if (pm->io_ops) {
+		result = -EEXIST;
+		goto out;
+	}
+
+	qm_reset_io(&pm->qm);
+	pm->io_ops  = ops;
+	pm->io_data = data;
+
+out:
+	up_write(&pm->io_lock);
+	return result;
+}
+
+/**
+ * pm_io_ops_unregister() - unregister companion IO packets handler
+ * @pm: address of the protocol manager to be unregistered
+ */
+int pm_io_ops_unregister(struct companion_protocol_manager *pm)
+{
+	int result = 0;
+
+	down_write(&pm->io_lock);
+	if (!pm->io_ops) {
+		result = -ENODEV;
+		goto out;
+	}
+
+	pm->io_ops  = NULL;
+	pm->io_data = NULL;
+	qm_reset_io(&pm->qm);
+
+out:
+	up_write(&pm->io_lock);
+	return result;
+}
+
+/**
+ * pm_can_ops_register() -  register companion CAN packets handler
+ * @pm:   address of the protocol manager to be registered
+ * @port: port number of which CAN to be registered
+ * @ops:  address of the CAN packets callback
+ * @data: address of the CAN packets callback argument
+ */
+int pm_can_ops_register(struct companion_protocol_manager *pm,
+			u8                                 port,
+			struct companion_can_ops          *ops,
+			void                              *data)
+{
+	int i, result = 0;
+
+	if (port >= BCP_CAN_PORTS)
+		return -EINVAL;
+
+	down_write(&pm->can_lock[port]);
+	if (pm->can_ops[port]) {
+		result = -EEXIST;
+		goto out;
+	}
+
+	qm_reset_can(&pm->qm, port);
+	pm->can_ops[port]  = ops;
+	pm->can_data[port] = data;
+
+	for (i = 0; i < BCP_CAN_PRIOS; ++i) {
+		pm->timer[port][i].data.pm   = pm;
+		pm->timer[port][i].data.port = port;
+		pm->timer[port][i].data.prio = i;
+		INIT_WORK(&pm->timer[port][i].data.work,
+			  pm_can_tx_timeout_callback);
+		timer_setup(&pm->timer[port][i].timer, pm_can_on_tx_timeout, 0);
+	}
+
+out:
+	up_write(&pm->can_lock[port]);
+	return result;
+}
+
+/**
+ * pm_can_ops_unregister() - unregister companion CAN packets handler
+ * @pm:   address of the protocol manager to be unregistered
+ * @port: port number of which CAN to be unregistered
+ */
+int pm_can_ops_unregister(struct companion_protocol_manager *pm, u8 port)
+{
+	int i, result = 0;
+
+	if (port >= BCP_CAN_PORTS)
+		return -EINVAL;
+
+	down_write(&pm->can_lock[port]);
+	if (!pm->can_ops[port]) {
+		result = -ENODEV;
+		goto out;
+	}
+
+	pm->can_ops[port]  = NULL;
+	pm->can_data[port] = NULL;
+	qm_reset_can(&pm->qm, port);
+
+	for (i = 0; i < BCP_CAN_PRIOS; ++i) {
+		del_timer_sync(&pm->timer[port][i].timer);
+		cancel_work_sync(&pm->timer[port][i].data.work);
+		pm->timer[port][i].data.pm   = NULL;
+		pm->timer[port][i].data.port = 0;
+		pm->timer[port][i].data.prio = 0;
+	}
+
+out:
+	up_write(&pm->can_lock[port]);
+	return result;
+}
+
+/**
+ * pm_prepare_tx() - prepare tx data
+ * @pm: address of the protocol manager to be used
+ * @p:  address of the data to be sent
+ */
+void pm_prepare_tx(struct companion_protocol_manager *pm,
+		   struct companion_packet           *p)
+{
+	pm->is_io_type = false;
+
+	if (qm_get_tx_data(&pm->qm, p)) {
+		if (is_io_type(p))
+			pm->is_io_type = true;
+	} else {
+		memset(p, BCP_NOOP, sizeof(*p));
+	}
+}
+
+/**
+ * pm_on_tx_done() - handle tx done
+ * @pm: address of the protocol manager to be used
+ */
+void pm_on_tx_done(struct companion_protocol_manager *pm)
+{
+	if (!pm->is_io_type)
+		return;
+
+	down_read(&pm->io_lock);
+	if (pm->io_ops && pm->io_ops->on_tx_done)
+		pm->io_ops->on_tx_done(pm->io_data);
+	up_read(&pm->io_lock);
+}
+
+/**
+ * pm_on_rx_done() - handle rx done
+ * @pm: address of the protocol manager to be used
+ * @p:  address of the recevied data
+ */
+void pm_on_rx_done(struct companion_protocol_manager *pm,
+		   const struct companion_packet     *p)
+{
+	struct companion_filter *filter;
+
+	list_for_each_entry(filter, &pm->filters, node) {
+		if (filter->match && filter->match(p)) {
+			if (filter->process)
+				filter->process(pm, p);
+
+			break;
+		}
+	}
+}
+
+/**
+ * pm_can_data_tx() - send CAN data according to protocol
+ * @pm:   address of the protocol manager to be used
+ * @port: port number of which CAN to be sent
+ * @prio: priority of the data to be sent
+ * @cf:   the raw CAN frame to be send
+ */
+int pm_can_data_tx(struct companion_protocol_manager *pm,
+		   u8                                 port,
+		   u8                                 prio,
+		   const struct can_frame            *cf)
+{
+	struct can_data_frame p;
+
+	if (port >= BCP_CAN_PORTS || prio >= BCP_CAN_PRIOS)
+		return -EINVAL;
+
+	if (pm->local_txq[port][prio] == 0)
+		return -ENOSPC;
+
+	p.type = BCP_CAN_DATA;
+	p.port = port + 1;
+	p.prio = prio;
+	p.dlc  = cf->can_dlc;
+	p.id   = cf->can_id;
+	memcpy(p.data, cf->data, sizeof(cf->data));
+
+	if (!qm_can_txq_in(&pm->qm,
+			   (struct companion_packet *)&p,
+			   port,
+			   prio)) {
+		pm->stats.can_tx_overflows[port][prio]++;
+		return -ENOSPC;
+	}
+
+	pm->local_txq[port][prio]--;
+
+	pm->stats.can_tx[port][prio]++;
+	return 0;
+}
+
+/**
+ * pm_can_data_rx() - receive CAN data according to protocol
+ * @pm:   address of the protocol manager to be used
+ * @port: port number of which CAN to be received
+ * @cf:   address of the raw CAN frame to be copied
+ */
+int pm_can_data_rx(struct companion_protocol_manager *pm,
+		   u8                                 port,
+		   struct can_frame                  *cf)
+{
+	struct can_data_frame p;
+
+	if (port >= BCP_CAN_PORTS)
+		return -EINVAL;
+
+	if (!qm_can_rxq_out(&pm->qm, (struct companion_packet *)&p, port))
+		return -EIO;
+
+	cf->can_id  = p.id;
+	cf->can_dlc = p.dlc;
+	memcpy(cf->data, p.data, sizeof(cf->data));
+	return 0;
+}
+
+/**
+ * pm_can_err() - receive CAN error according to protocol
+ * @pm:    address of the protocol manager to be used
+ * @port:  port number of which CAN to be received
+ * @bec:   address of the error counter to be copied
+ * @state: address of the error state to be copied
+ * @code:  address of the error code to be copied
+ */
+int pm_can_err(struct companion_protocol_manager *pm,
+	       u8                                 port,
+	       struct can_berr_counter           *berr,
+	       u8                                *state,
+	       u8                                *code)
+{
+	struct can_status_response p;
+
+	if (port >= BCP_CAN_PORTS)
+		return -EINVAL;
+
+	if (!qm_can_err_out(&pm->qm, (struct companion_packet *)&p, port))
+		return -EIO;
+
+	berr->rxerr = p.rx_err;
+	berr->txerr = p.tx_err;
+	*state      = p.state;
+	*code       = p.code;
+	return 0;
+}
+
+/**
+ * pm_wait_for_response() - wait for CAN packets response
+ * @pm:   address of the protocol manager to be used
+ * @port: port number of which CAN to be wait
+ * @flag: flag to be wait
+ */
+int pm_wait_for_response(struct companion_protocol_manager *pm,
+			 u8                                 port,
+			 enum bcp_can_flag                  flag)
+{
+	unsigned long *flags = &pm->flags[port];
+	long           ret;
+
+	if (test_bit(flag, flags)) {
+		ret = wait_event_interruptible_timeout(pm->wait[port],
+						       !test_bit(flag, flags),
+						       PM_RESPONSE_TIMEOUT);
+
+		if (ret < 0)
+			return ret;
+
+		if (ret == 0)
+			return -ETIMEDOUT;
+	}
+
+	return pm->response[port][flag];
+}
+
+/**
+ * pm_can_set_bittiming() - set CAN bittiming according to protocol
+ * @pm:        address of the protocol manager to be used
+ * @port:      port number of which CAN to be set
+ * @bittiming: the bittiming to be set
+ */
+int pm_can_set_bittiming(struct companion_protocol_manager *pm,
+			 u8                                 port,
+			 const struct can_bittiming        *bittiming)
+{
+	struct can_bittiming_request p;
+
+	if (port >= BCP_CAN_PORTS)
+		return -EINVAL;
+
+	memset(&p, 0, sizeof(p));
+	p.type       = BCP_CAN_BITTIMING;
+	p.port       = port + 1;
+	p.prescaler  = bittiming->brp;
+	p.prop_seg   = bittiming->prop_seg;
+	p.phase_seg1 = bittiming->phase_seg1;
+	p.phase_seg2 = bittiming->phase_seg2;
+	p.sjw        = bittiming->sjw;
+
+	if (!qm_can_txq_in(&pm->qm,
+			   (struct companion_packet *)&p,
+			   port,
+			   0)) {
+		pm->stats.can_tx_overflows[port][0]++;
+		return -ENOSPC;
+	}
+
+	set_bit(bcp_can_bittiming, &pm->flags[port]);
+	pm->stats.can_tx[port][0]++;
+	return 0;
+}
+
+/**
+ * pm_can_set_mode() - set CAN mode according to protocol
+ * @pm:   address of the protocol manager to be used
+ * @port: port number of which CAN to be set
+ * @mode: the mode to be set
+ */
+int pm_can_set_mode(struct companion_protocol_manager *pm,
+		    u8                                 port,
+		    enum can_mode                      mode)
+{
+	struct can_mode_request p;
+
+	if (port >= BCP_CAN_PORTS)
+		return -EINVAL;
+
+	memset(&p, 0, sizeof(p));
+	p.type = BCP_CAN_MODE;
+	p.port = port + 1;
+
+	switch (mode) {
+	case CAN_MODE_START:
+		p.mode = BCP_CAN_MODE_NORMAL;
+		break;
+
+	case CAN_MODE_STOP:
+		p.mode = BCP_CAN_MODE_OFF;
+		break;
+
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	if (!qm_can_txq_in(&pm->qm,
+			   (struct companion_packet *)&p,
+			   port,
+			   0)) {
+		pm->stats.can_tx_overflows[port][0]++;
+		return -ENOSPC;
+	}
+
+	set_bit(bcp_can_mode, &pm->flags[port]);
+	pm->stats.can_tx[port][0]++;
+	return 0;
+}
+
+/**
+ * pm_can_set_ctrlmode() - set CAN control mode according to protocol
+ * @pm:   address of the protocol manager to be used
+ * @port: port number of which CAN to be set
+ * @ctrl: the control mode to be set
+ */
+int pm_can_set_ctrlmode(struct companion_protocol_manager *pm,
+			u8                                 port,
+			u32                                ctrl)
+{
+	struct can_mode_request p;
+
+	if (port >= BCP_CAN_PORTS)
+		return -EINVAL;
+
+	if (!(ctrl & CAN_CTRLMODE_LISTENONLY))
+		return -EOPNOTSUPP;
+
+	memset(&p, 0, sizeof(p));
+	p.type = BCP_CAN_MODE;
+	p.port = port + 1;
+	p.mode = BCP_CAN_MODE_LISTEN;
+
+	if (!qm_can_txq_in(&pm->qm,
+			   (struct companion_packet *)&p,
+			   port,
+			   0)) {
+		pm->stats.can_tx_overflows[port][0]++;
+		return -ENOSPC;
+	}
+
+	set_bit(bcp_can_mode, &pm->flags[port]);
+	pm->stats.can_tx[port][0]++;
+	return 0;
+}
+
+/**
+ * pm_can_get_status() - get CAN status according to protocol
+ * @pm:   address of the protocol manager to be used
+ * @port: port number of which CAN to be inquiry
+ */
+int pm_can_get_status(struct companion_protocol_manager *pm, u8 port)
+{
+	struct can_status_request p;
+
+	if (port >= BCP_CAN_PORTS)
+		return -EINVAL;
+
+	memset(&p, 0, sizeof(p));
+	p.type = BCP_CAN_STATUS;
+	p.port = port + 1;
+
+	if (!qm_can_txq_in(&pm->qm,
+			   (struct companion_packet *)&p,
+			   port,
+			   0)) {
+		pm->stats.can_tx_overflows[port][0]++;
+		return -ENOSPC;
+	}
+
+	set_bit(bcp_can_status, &pm->flags[port]);
+	pm->stats.can_tx[port][0]++;
+	return 0;
+}
+
+/**
+ * pm_can_get_txq_status() - get CAN tx queue status according to protocol
+ * @pm:   address of the protocol manager to be used
+ * @port: port number of which CAN to be inquiry
+ */
+int pm_can_get_txq_status(struct companion_protocol_manager *pm, u8 port)
+{
+	struct can_txq_status_request p;
+
+	if (port >= BCP_CAN_PORTS)
+		return -EINVAL;
+
+	memset(&p, 0, sizeof(p));
+	p.type = BCP_CAN_TX_QUEUE_STATUS;
+	p.port = port + 1;
+
+	if (!qm_can_txq_in(&pm->qm,
+			   (struct companion_packet *)&p,
+			   port,
+			   0)) {
+		pm->stats.can_tx_overflows[port][0]++;
+		return -ENOSPC;
+	}
+
+	set_bit(bcp_can_txq_status, &pm->flags[port]);
+	pm->stats.can_tx[port][0]++;
+	return 0;
+}
+
+/**
+ * pm_can_txq_is_full() - inquiry CAN tx queue is full
+ * @pm:      address of the protocol manager to be used
+ * @port:    port number of which CAN to be inquiry
+ * @prio:    queue number of which tx queue to be inquiry
+ * @is_full: address of the is full result to be copied
+ */
+int pm_can_txq_is_full(struct companion_protocol_manager *pm,
+		       u8                                 port,
+		       u8                                 prio,
+		       bool                              *is_full)
+{
+	if (port >= BCP_CAN_PORTS || prio >= BCP_CAN_PRIOS)
+		return -EINVAL;
+
+	*is_full = (pm->local_txq[port][prio] == 0);
+	return 0;
+}
+
+/**
+ * pm_can_txq_has_space() - inquiry CAN tx queue has space
+ * @pm:        address of the protocol manager to be used
+ * @port:      port number of which CAN to be inquiry
+ * @prio:      queue number of which tx queue to be inquiry
+ * @has_space: address of the has space result to be copied
+ */
+int pm_can_txq_has_space(struct companion_protocol_manager *pm,
+			 u8                                 port,
+			 u8                                 prio,
+			 bool                              *has_space)
+{
+	if (port >= BCP_CAN_PORTS || prio >= BCP_CAN_PRIOS)
+		return -EINVAL;
+
+	*has_space = (pm->local_txq[port][prio] > 0);
+	return 0;
+}
+
+/**
+ * pm_can_start_tx_timer() - start CAN tx timeout detection
+ * @pm:   address of the protocol manager to be used
+ * @port: port number of which CAN to be used
+ * @prio: queue number of which tx queue's timer to be used
+ */
+int pm_can_start_tx_timer(struct companion_protocol_manager *pm,
+			  u8                                 port,
+			  u8                                 prio)
+{
+	if (port >= BCP_CAN_PORTS || prio >= BCP_CAN_PRIOS)
+		return -EINVAL;
+
+	mod_timer(&pm->timer[port][prio].timer, jiffies + PM_CAN_TX_TIMEOUT);
+	return 0;
+}
+
+/**
+ * pm_can_stop_tx_timer() - stop CAN tx timeout detection
+ * @pm:   address of the protocol manager to be used
+ * @port: port number of which CAN to be used
+ * @prio: queue number of which tx queue's timer to be used
+ */
+int pm_can_stop_tx_timer(struct companion_protocol_manager *pm,
+			 u8                                 port,
+			 u8                                 prio)
+{
+	if (port >= BCP_CAN_PORTS || prio >= BCP_CAN_PRIOS)
+		return -EINVAL;
+
+	del_timer_sync(&pm->timer[port][prio].timer);
+	return 0;
+}
diff --git a/drivers/spi/companion/protocol-manager.h b/drivers/spi/companion/protocol-manager.h
new file mode 100644
index 0000000..2cf10fa
--- /dev/null
+++ b/drivers/spi/companion/protocol-manager.h
@@ -0,0 +1,341 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Companion protocol manager code
+ *
+ * Copyright (C) 2015-2018 Bosch Sicherheitssysteme GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _BOSCH_COMPANION_PROTOCOL_MANAGER_H
+#define _BOSCH_COMPANION_PROTOCOL_MANAGER_H
+
+#include <linux/can/dev.h>
+#include <linux/list.h>
+#include <linux/rwsem.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <linux/companion.h>
+#include "queue-manager.h"
+
+/**
+ * enum bcp_can_flag - companion CAN packet event flag
+ */
+enum bcp_can_flag {
+	bcp_can_bittiming,
+	bcp_can_mode,
+	bcp_can_status,
+	bcp_can_txq_status,
+
+	bcp_can_max
+};
+
+/**
+ * struct companion_statistics - statistics information of companion
+ * @io_tx_overflows:   counter of IO packets transmit overflow
+ * @io_rx_overflows:   counter of IO packets receive overflow
+ * @can_tx_overflows:  counter of CAN packets transmit overflow
+ * @can_rx_overflows:  counter of CAN packets receive overflow
+ * @can_err_overflows: counter of CAN error packets receive overflow
+ * @io_tx:             counter of IO packets transmitted
+ * @io_rx:             counter of IO packets received
+ * @can_tx:            counter of CAN packets transmitted
+ * @can_ack_success:   counter of CAN response success packets received
+ * @can_ack_failure:   counter of CAN response failure packets received
+ * @can_rx:            counter of CAN packets received
+ * @can_err:           counter of CAN error packets received
+ * @can_lost_seq_sync: counter of CAN packets sequence lost sync
+ * @can_lost_txq_sync: counter of CAN tx queue status lost sync
+ * @can_ack_timeout:   counter of CAN tx ack timeout
+ * @can_ack_unexpect:  counter of CAN unexpected tx ack
+ */
+struct companion_statistics {
+	u32 io_tx_overflows;
+	u32 io_rx_overflows;
+	u32 can_tx_overflows[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+	u32 can_rx_overflows[BCP_CAN_PORTS];
+	u32 can_err_overflows[BCP_CAN_PORTS];
+
+	u32 io_tx;
+	u32 io_rx;
+	u32 can_tx[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+	u32 can_ack_success[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+	u32 can_ack_failure[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+	u32 can_rx[BCP_CAN_PORTS];
+	u32 can_err[BCP_CAN_PORTS];
+	u32 can_lost_seq_sync[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+	u32 can_lost_txq_sync[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+	u32 can_ack_timeout[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+	u32 can_ack_unexpect[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+};
+
+struct companion_protocol_manager;
+/**
+ * struct companion_timer_data - encapsulate data passed to timer
+ * @pm:   companion protocol manager
+ * @port: port number of which CAN associated with the timer
+ * @prio: priority of which CAN tx queue associated with the timer
+ * @work: work for execute timeout callback in thread context
+ */
+struct companion_timer_data {
+	struct companion_protocol_manager *pm;
+	u8                                 port;
+	u8                                 prio;
+	struct work_struct                 work;
+};
+
+/**
+ * struct companion_timer - encapsulate timer and data
+ * @timer: timer_list object for timeout
+ * @data:  data associated with the timer
+ */
+struct companion_timer {
+	struct timer_list           timer;
+	struct companion_timer_data data;
+};
+
+/**
+ * struct companion_protocol_manager - encapsulate companion protocol handling
+ * @io_ops:     callback of IO packet handling
+ * @io_data:    callback argument of io_ops
+ * @io_lock:    lock to protect IO callback
+ * @can_ops:    callback of CAN packet handling
+ * @can_data:   callback argument of can_ops
+ * @can_lock:   lock to protect CAN callback
+ * @wait:       wait queue head for CAN packet response
+ * @flags:      event flags for CAN packet response
+ * @response:   response result of each CAN packet event flag
+ * @rx_err:     receive error counter of CAN port
+ * @tx_err:     transmit error counter of CAN port
+ * @sequence:   sequence counter of each CAN tx queue
+ * @remote_txq: CAN tx queue space status at remote
+ * @local_txq:  CAN tx queue space status at local
+ * @timer:      timer for detect CAN tx ack timeout for each CAN tx queue
+ * @stats:      statistics information of companion
+ * @is_io_type: flag to record the sent packet is IO type or not
+ * @qm:         queue manager
+ * @filters:    filter list to handle receveid companion packets
+ */
+struct companion_protocol_manager {
+	struct companion_io_ops       *io_ops;
+	void                          *io_data;
+	struct rw_semaphore            io_lock;
+	struct companion_can_ops      *can_ops[BCP_CAN_PORTS];
+	void                          *can_data[BCP_CAN_PORTS];
+	struct rw_semaphore            can_lock[BCP_CAN_PORTS];
+	wait_queue_head_t              wait[BCP_CAN_PORTS];
+	unsigned long                  flags[BCP_CAN_PORTS];
+	int                            response[BCP_CAN_PORTS][bcp_can_max];
+	u8                             rx_err[BCP_CAN_PORTS];
+	u8                             tx_err[BCP_CAN_PORTS];
+	u8                             sequence[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+	u8                             remote_txq[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+	u8                             local_txq[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+	struct companion_timer         timer[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+
+	struct companion_statistics    stats;
+	bool                           is_io_type;
+
+	struct companion_queue_manager qm;
+	struct list_head               filters;
+};
+
+/**
+ * pm_init() - initialize the protocol manager
+ * @pm: address of the protocol manager to be initialized
+ */
+void pm_init(struct companion_protocol_manager *pm);
+
+/**
+ * pm_io_ops_register() - register companion IO packets handler
+ * @pm:   address of the protocol manager to be registered
+ * @ops:  address of the IO packets callback
+ * @data: address of the IO packets callback argument
+ */
+int pm_io_ops_register(struct companion_protocol_manager *pm,
+		       struct companion_io_ops           *ops,
+		       void                              *data);
+
+/**
+ * pm_io_ops_unregister() - unregister companion IO packets handler
+ * @pm: address of the protocol manager to be unregistered
+ */
+int pm_io_ops_unregister(struct companion_protocol_manager *pm);
+
+/**
+ * pm_can_ops_register() -  register companion CAN packets hanler
+ * @pm:   address of the protocol manager to be registered
+ * @port: port number of which CAN to be registered
+ * @ops:  address of the CAN packets callback
+ * @data: address of the CAN packets callback argument
+ */
+int pm_can_ops_register(struct companion_protocol_manager *pm,
+			u8                                 port,
+			struct companion_can_ops          *ops,
+			void                              *data);
+
+/**
+ * pm_can_ops_unregister() - unregister companion CAN packets handler
+ * @pm:   address of the protocol manager to be unregistered
+ * @port: port number of which CAN to be unregistered
+ */
+int pm_can_ops_unregister(struct companion_protocol_manager *pm, u8 port);
+
+/**
+ * pm_prepare_tx() - prepare tx data
+ * @pm: address of the protocol manager to be used
+ * @p:  address of the data to be sent
+ */
+void pm_prepare_tx(struct companion_protocol_manager *pm,
+		   struct companion_packet           *p);
+
+/**
+ * pm_on_tx_done() - handle tx done
+ * @pm: address of the protocol manager to be used
+ */
+void pm_on_tx_done(struct companion_protocol_manager *pm);
+
+/**
+ * pm_on_rx_done() - handle rx done
+ * @pm: address of the protocol manager to be used
+ * @p:  address of the recevied data
+ */
+void pm_on_rx_done(struct companion_protocol_manager *pm,
+		   const struct companion_packet     *p);
+
+/**
+ * pm_can_data_tx() - send CAN data according to protocol
+ * @pm:   address of the protocol manager to be used
+ * @port: port number of which CAN to be sent
+ * @prio: priority of the data to be sent
+ * @cf:   the raw CAN frame to be send
+ */
+int pm_can_data_tx(struct companion_protocol_manager *pm,
+		   u8                                 port,
+		   u8                                 prio,
+		   const struct can_frame            *cf);
+
+/**
+ * pm_can_data_rx() - receive CAN data according to protocol
+ * @pm:   address of the protocol manager to be used
+ * @port: port number of which CAN to be received
+ * @cf:   address of the raw CAN frame to be copied
+ */
+int pm_can_data_rx(struct companion_protocol_manager *pm,
+		   u8                                 port,
+		   struct can_frame                  *cf);
+
+/**
+ * pm_can_err() - receive CAN error according to protocol
+ * @pm:    address of the protocol manager to be used
+ * @port:  port number of which CAN to be received
+ * @bec:   address of the error counter to be copied
+ * @state: address of the error state to be copied
+ * @code:  address of the error code to be copied
+ */
+int pm_can_err(struct companion_protocol_manager *pm,
+	       u8                                 port,
+	       struct can_berr_counter           *bec,
+	       u8                                *state,
+	       u8                                *code);
+
+/**
+ * pm_wait_for_response() - wait for CAN packets response
+ * @pm:   address of the protocol manager to be used
+ * @port: port number of which CAN to be wait
+ * @flag: flag to be wait
+ */
+int pm_wait_for_response(struct companion_protocol_manager *pm,
+			 u8                                 port,
+			 enum bcp_can_flag                  flag);
+
+/**
+ * pm_can_set_bittiming() - set CAN bittiming according to protocol
+ * @pm:        address of the protocol manager to be used
+ * @port:      port number of which CAN to be set
+ * @bittiming: the bittiming to be set
+ */
+int pm_can_set_bittiming(struct companion_protocol_manager *pm,
+			 u8                                 port,
+			 const struct can_bittiming        *bittiming);
+
+/**
+ * pm_can_set_mode() - set CAN mode according to protocol
+ * @pm:   address of the protocol manager to be used
+ * @port: port number of which CAN to be set
+ * @mode: the mode to be set
+ */
+int pm_can_set_mode(struct companion_protocol_manager *pm,
+		    u8                                 port,
+		    enum can_mode                      mode);
+
+/**
+ * pm_can_set_ctrlmode() - set CAN control mode according to protocol
+ * @pm:   address of the protocol manager to be used
+ * @port: port number of which CAN to be set
+ * @ctrl: the control mode to be set
+ */
+int pm_can_set_ctrlmode(struct companion_protocol_manager *pm,
+			u8                                 port,
+			u32                                ctrl);
+
+/**
+ * pm_can_get_status() - get CAN status according to protocol
+ * @pm:   address of the protocol manager to be used
+ * @port: port number of which CAN to be inquiry
+ */
+int pm_can_get_status(struct companion_protocol_manager *pm, u8 port);
+
+/**
+ * pm_can_get_txq_status() - get CAN tx queue status according to protocol
+ * @pm:   address of the protocol manager to be used
+ * @port: port number of which CAN to be inquiry
+ */
+int pm_can_get_txq_status(struct companion_protocol_manager *pm, u8 port);
+
+/**
+ * pm_can_txq_is_full() - inquiry CAN tx queue is full
+ * @pm:      address of the protocol manager to be used
+ * @port:    port number of which CAN to be inquiry
+ * @prio:    queue number of which tx queue to be inquiry
+ * @is_full: address of the is full result to be copied
+ */
+int pm_can_txq_is_full(struct companion_protocol_manager *pm,
+		       u8                                 port,
+		       u8                                 prio,
+		       bool                              *is_full);
+
+/**
+ * pm_can_txq_has_space() - inquiry CAN tx queue has space
+ * @pm:        address of the protocol manager to be used
+ * @port:      port number of which CAN to be inquiry
+ * @prio:      queue number of which tx queue to be inquiry
+ * @has_space: address of the has space result to be copied
+ */
+int pm_can_txq_has_space(struct companion_protocol_manager *pm,
+			 u8                                 port,
+			 u8                                 prio,
+			 bool                              *has_space);
+
+/**
+ * pm_can_start_tx_timer() - start CAN tx timeout detection
+ * @pm:   address of the protocol manager to be used
+ * @port: port number of which CAN to be used
+ * @prio: queue number of which tx queue's timer to be used
+ */
+int pm_can_start_tx_timer(struct companion_protocol_manager *pm,
+			  u8                                 port,
+			  u8                                 prio);
+
+/**
+ * pm_can_stop_tx_timer() - stop CAN tx timeout detection
+ * @pm:   address of the protocol manager to be used
+ * @port: port number of which CAN to be used
+ * @prio: queue number of which tx queue's timer to be used
+ */
+int pm_can_stop_tx_timer(struct companion_protocol_manager *pm,
+			 u8                                 port,
+			 u8                                 prio);
+#endif
diff --git a/drivers/spi/companion/protocol.h b/drivers/spi/companion/protocol.h
new file mode 100644
index 0000000..7f70ed6
--- /dev/null
+++ b/drivers/spi/companion/protocol.h
@@ -0,0 +1,273 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Companion protocol kernel space API
+ *
+ * Copyright (C) 2015-2018 Bosch Sicherheitssysteme GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _BOSCH_COMPANION_PROTOCOL_KERNEL_H
+#define _BOSCH_COMPANION_PROTOCOL_KERNEL_H
+
+#include <linux/kernel.h>
+
+#define BCP_CAN_PORTS 2u
+#define BCP_CAN_PRIOS 8u
+
+/**
+ * BCP type field definitions (CAN)
+ */
+#define BCP_NOOP                0x00u
+#define BCP_CAN_DATA            0x01u
+#define BCP_CAN_BITTIMING       0x03u
+#define BCP_CAN_MODE            0x04u
+#define BCP_CAN_STATUS          0x06u
+#define BCP_CAN_TX_ACK          0x07u
+#define BCP_CAN_TX_QUEUE_STATUS 0x0Fu
+
+/**
+ * BCP status field definitions
+ */
+#define BCP_STATUS_SUCCESS 0x00u
+#define BCP_STATUS_UNKNOWN 0x01u
+#define BCP_STATUS_OTHER   0x02u
+
+/**
+ * BCP packet size definition
+ */
+#define BCP_PACKET_SIZE    16u
+
+/**
+ * struct companion_packet - companion packet general format
+ * @data: contents of the packet
+ */
+struct companion_packet {
+	u8 data[BCP_PACKET_SIZE];
+};
+
+/**
+ * struct can_data_frame - companion can data frame packet
+ * @type: packet type
+ * @port: can port
+ * @prio: priority of the can frame
+ * @dlc:  can frame payload in bytes
+ * @id:   can frame id
+ * @data: can frame payload
+ */
+struct can_data_frame {
+	u8  type;
+	u8  port;
+	u8  prio;
+	u8  dlc;
+	u32 id;
+	u8  data[8];
+};
+
+/**
+ * struct can_bittiming_request - companion can bittiming request packet
+ * @type:       packet type
+ * @port:       can port
+ * @prescaler:  bitrate prescaler
+ * @prop_seg:   propagation segment in TQs
+ * @phase_seg1: phase buffer segment 1 in TQs
+ * @phase_seg2: phase buffer segment 2 in TQs
+ * @sjw:        synchronisation jump width in TQs
+ * @reserved:   reserved
+ */
+struct can_bittiming_request {
+	u8  type;
+	u8  port;
+	u16 prescaler;
+	u8  prop_seg;
+	u8  phase_seg1;
+	u8  phase_seg2;
+	u8  sjw;
+	u8  reserved[8];
+};
+
+/**
+ * struct can_bittiming_response - companion can bittiming response packet
+ * @type:       packet type
+ * @port:       can port
+ * @prescaler:  bitrate prescaler
+ * @prop_seg:   propagation segment in TQs
+ * @phase_seg1: phase buffer segment 1 in TQs
+ * @phase_seg2: phase buffer segment 2 in TQs
+ * @sjw:        synchronisation jump width in TQs
+ * @reserved:   reserved
+ * @status:     process status
+ */
+struct can_bittiming_response {
+	u8  type;
+	u8  port;
+	u16 prescaler;
+	u8  prop_seg;
+	u8  phase_seg1;
+	u8  phase_seg2;
+	u8  sjw;
+	u8  reserved[7];
+	u8  status;
+};
+
+/**
+ * struct can_mode_request - companion can mode request packet
+ * @type:     packet type
+ * @port:     can port
+ * @mode:     can mode
+ * @reserved: reserved
+ */
+struct can_mode_request {
+	u8 type;
+	u8 port;
+	u8 mode;
+	u8 reserved[13];
+};
+#define BCP_CAN_MODE_OFF    0x00u
+#define BCP_CAN_MODE_NORMAL 0x01u
+#define BCP_CAN_MODE_LISTEN 0x02u
+
+/**
+ * struct can_mode_response - companion can mode response packet
+ * @type:     packet type
+ * @port:     can port
+ * @mode:     can mode
+ * @reserved: reserved
+ * @status:   process status
+ */
+struct can_mode_response {
+	u8 type;
+	u8 port;
+	u8 mode;
+	u8 reserved[12];
+	u8 status;
+};
+
+/**
+ * struct can_status_request - companion can status request packet
+ * @type:     packet type
+ * @port:     can port
+ * @reserved: reserved
+ */
+struct can_status_request {
+	u8 type;
+	u8 port;
+	u8 reserved[14];
+};
+
+/**
+ * struct can_status_response - companion can status response packet
+ * @type:     packet type
+ * @port:     can port
+ * @rx_err:   rx error counter
+ * @tx_err:   tx error counter
+ * @err1:     can controller error status 1
+ * @err2:     can controller error status 2
+ * @reserved: reserved
+ * @status:   process status
+ */
+struct can_status_response {
+	u8 type;
+	u8 port;
+	u8 rx_err;
+	u8 tx_err;
+	u8 state;
+	u8 code;
+	u8 reserved[9];
+	u8 status;
+};
+
+/**
+ * struct can_tx_acknowledge - companion can tx acknowledge packet
+ * @type:     packet type
+ * @port:     can port
+ * @prio:     priority of the can frame
+ * @dlc:      payload length of the can frame
+ * @sequence: monotonic increasing sequence counter of sent can frames
+ * @space:    queue space left of this priority
+ * @reserved: reserved
+ * @status:   process status
+ */
+struct can_tx_acknowledge {
+	u8 type;
+	u8 port;
+	u8 prio;
+	u8 dlc;
+	u8 sequence;
+	u8 space;
+	u8 reserved[9];
+	u8 status;
+};
+
+/**
+ * struct can_txq_status_request - companion can txq status request packet
+ * @type:     packet type
+ * @port:     can port
+ * @reserved: reserved
+ */
+struct can_txq_status_request {
+	u8 type;
+	u8 port;
+	u8 reserved[14];
+};
+
+/**
+ * struct can_txq_status_response - companion can txq status response packet
+ * @type:     packet type
+ * @port:     can port
+ * @space:    queue space left of each priority
+ * @reserved: reserved
+ * @status:   process status
+ */
+struct can_txq_status_response {
+	u8 type;
+	u8 port;
+	u8 space[8];
+	u8 reserved[5];
+	u8 status;
+};
+
+/**
+ * is_null_type() - return true if the packet is null type
+ * @p: the packet to test
+ */
+static inline bool is_null_type(const struct companion_packet *p)
+{
+	return p->data[0] == BCP_NOOP;
+}
+
+/**
+ * is_io_type() - return true if the packet is io type
+ * @p: the packet to test
+ */
+static inline bool is_io_type(const struct companion_packet *p)
+{
+	return (p->data[0] & 0x80);
+}
+
+/**
+ * is_can_type() - return true if the packet is can type
+ * @p: the packet to test
+ */
+static inline bool is_can_type(const struct companion_packet *p)
+{
+	return !is_io_type(p) && !is_null_type(p);
+}
+
+/**
+ * dump_packet() - dump raw packet data in hexadecimal format
+ * @p:      the packet to dump
+ * @level:  the log level of the dump
+ * @prefix: the prefix string of the dump
+ */
+static inline void dump_packet(const struct companion_packet *p,
+			       const char                    *level,
+			       const char                    *prefix)
+{
+	print_hex_dump(level, prefix, DUMP_PREFIX_NONE, 16, 1,
+		       p->data, sizeof(p->data), false);
+}
+
+#endif
diff --git a/drivers/spi/companion/queue-manager.c b/drivers/spi/companion/queue-manager.c
new file mode 100644
index 0000000..f72696f
--- /dev/null
+++ b/drivers/spi/companion/queue-manager.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Companion queue management code
+ *
+ * Copyright (C) 2015-2018 Bosch Sicherheitssysteme GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "queue-manager.h"
+
+/**
+ * qm_init() - initialize all managed queues
+ * @qm: address of the queue manager to be initialized
+ */
+void qm_init(struct companion_queue_manager *qm)
+{
+	int i, j;
+
+	INIT_KFIFO(qm->io_txq.fifo);
+	INIT_KFIFO(qm->io_rxq.fifo);
+
+	for (i = 0; i < BCP_CAN_PORTS; ++i) {
+		for (j = 0; j < BCP_CAN_PRIOS; ++j)
+			INIT_KFIFO(qm->can_txq[i][j].fifo);
+
+		INIT_KFIFO(qm->can_rxq[i].fifo);
+		INIT_KFIFO(qm->can_err[i].fifo);
+		mutex_init(&qm->can_txq_lock[i]);
+	}
+}
+
+/**
+ * qm_reset() - reset all managed queues
+ * @qm: address of the queue manager to be reset
+ */
+void qm_reset(struct companion_queue_manager *qm)
+{
+	int i;
+
+	qm_reset_io(qm);
+
+	for (i = 0; i < BCP_CAN_PORTS; ++i)
+		qm_reset_can(qm, i);
+}
+
+/**
+ * qm_reset_io() - reset managed IO queues
+ * @qm: address of the queue manager to be reset
+ */
+void qm_reset_io(struct companion_queue_manager *qm)
+{
+	kfifo_reset(&qm->io_txq.fifo);
+	kfifo_reset(&qm->io_rxq.fifo);
+}
+
+/**
+ * qm_reset_can() - reset managed CAN queues
+ * @qm:   address of the queue manager to be reset
+ * @port: port number of which CAN queue should be reset
+ */
+void qm_reset_can(struct companion_queue_manager *qm, u8 port)
+{
+	int i;
+
+	for (i = 0; i < BCP_CAN_PRIOS; ++i)
+		kfifo_reset(&qm->can_txq[port][i].fifo);
+
+	kfifo_reset(&qm->can_rxq[port].fifo);
+	kfifo_reset(&qm->can_err[port].fifo);
+}
+
+/**
+ * qm_has_tx_data() - return true if has tx data
+ * @qm: address of the queue manager to be used
+ */
+bool qm_has_tx_data(struct companion_queue_manager *qm)
+{
+	int i, j;
+
+	for (i = 0; i < BCP_CAN_PORTS; ++i)
+		for (j = 0; j < BCP_CAN_PRIOS; ++j)
+			if (!kfifo_is_empty(&qm->can_txq[i][j].fifo))
+				return true;
+
+	return !kfifo_is_empty(&qm->io_txq.fifo);
+}
+
+/*
+ * Define maximum CAN packets can be sent in a row in case there is IO packet
+ * pending or coming, which specifies the minimal bandwidth for IO packets.
+ */
+#define CAN_MAX_IN_A_ROW 8
+
+/**
+ * qm_get_tx_data() - return true if got the tx data
+ * @qm: address of the queue manager to be used
+ * @p:  where the data to be copied
+ */
+bool qm_get_tx_data(struct companion_queue_manager *qm,
+		    struct companion_packet        *p)
+{
+	int i, j;
+
+	/*
+	 * Implement the companion packet scheduling algorithm which guarantees
+	 * IO packets share minimal 1 / (CAN_MAX_IN_A_ROW + 1) bandwidth, and
+	 * the rest bandwidth is shared equally for all CAN ports.
+	 *
+	 * The purpose is to ensure fairness between all CAN ports and also keep
+	 * CAN packets have higher priority than IO packets in general, but
+	 * avoid IO packets starvation in case CAN is very busy.
+	 *
+	 * The bandwidth is not statically allocated, so the active user (IO or
+	 * CAN) can use up to 100% bandwidth if there are no other active users.
+	 */
+
+	if (qm->io_promoted && qm_io_txq_out(qm, p)) {
+		qm->io_promoted = false;
+		return true;
+	}
+
+	for (i = 0; i < BCP_CAN_PORTS; ++i) {
+		/* ensure fairness for all can ports */
+		qm->can_current++;
+		if (qm->can_current >= BCP_CAN_PORTS)
+			qm->can_current = 0;
+
+		for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+			if (qm_can_txq_out(qm, p, qm->can_current, j)) {
+				qm->can_sched++;
+				if (qm->can_sched >= CAN_MAX_IN_A_ROW) {
+					qm->io_promoted = true;
+					qm->can_sched   = 0;
+				}
+				return true;
+			}
+		}
+	}
+
+	return qm_io_txq_out(qm, p);
+}
diff --git a/drivers/spi/companion/queue-manager.h b/drivers/spi/companion/queue-manager.h
new file mode 100644
index 0000000..aa5d31f
--- /dev/null
+++ b/drivers/spi/companion/queue-manager.h
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Companion queue management code
+ *
+ * Copyright (C) 2015-2018 Bosch Sicherheitssysteme GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _BOSCH_COMPANION_QUEUE_MANAGEMENT_H
+#define _BOSCH_COMPANION_QUEUE_MANAGEMENT_H
+
+#include <linux/kernel.h>
+#include <linux/kfifo.h>
+#include "protocol.h"
+
+#define QUEUE_SIZE 16u
+
+/**
+ * struct companion_queue - encapsulate kfifo as companion queue
+ * @fifo: the kfifo object for companion packets
+ */
+struct companion_queue {
+	DECLARE_KFIFO(fifo, struct companion_packet, QUEUE_SIZE);
+};
+
+/**
+ * struct companion_queue_manager - manage all queues for companion
+ * @io_txq:       the tx queue for IO messages
+ * @io_rxq:       the rx queue for IO messages
+ * @can_txq:      the tx queues for CAN messages
+ * @can_rxq:      the rx queues for CAN messages
+ * @can_err:      the queues for CAN error messages
+ * @can_txq_lock: lock for protect CAN tx queue 0
+ * @io_promoted:  flag to indicate promoted IO messages priority
+ * @can_current:  the currently scheduled CAN port
+ * @can_sched:    counter of how many times CAN messages be scheduled
+ */
+struct companion_queue_manager {
+	struct companion_queue io_txq;
+	struct companion_queue io_rxq;
+	struct companion_queue can_txq[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+	struct companion_queue can_rxq[BCP_CAN_PORTS];
+	struct companion_queue can_err[BCP_CAN_PORTS];
+	struct mutex           can_txq_lock[BCP_CAN_PORTS];
+
+	bool                   io_promoted;
+	u8                     can_current;
+	u32                    can_sched;
+};
+
+/**
+ * qm_init() - initialize all managed queues
+ * @qm: address of the queue manager to be initialized
+ */
+void qm_init(struct companion_queue_manager *qm);
+
+/**
+ * qm_reset() - reset all managed queues
+ * @qm: address of the queue manager to be reset
+ */
+void qm_reset(struct companion_queue_manager *qm);
+
+/**
+ * qm_reset_io() - reset managed IO queues
+ * @qm: address of the queue manager to be reset
+ */
+void qm_reset_io(struct companion_queue_manager *qm);
+
+/**
+ * qm_reset_can() - reset managed CAN queues
+ * @qm:   address of the queue manager to be reset
+ * @port: port number of which CAN queue should be reset
+ */
+void qm_reset_can(struct companion_queue_manager *qm, u8 port);
+
+/**
+ * qm_has_tx_data() - return true if has tx data
+ * @qm: address of the queue manager to be used
+ */
+bool qm_has_tx_data(struct companion_queue_manager *qm);
+
+/**
+ * qm_get_tx_data() - return true if got the tx data
+ * @qm: address of the queue manager to be used
+ * @p:  where the data to be copied
+ */
+bool qm_get_tx_data(struct companion_queue_manager *qm,
+		    struct companion_packet        *p);
+
+/**
+ * qm_io_txq_is_full() - return true if IO tx queue is full
+ * @qm: address of the queue manager to be used
+ */
+static inline bool qm_io_txq_is_full(struct companion_queue_manager *qm)
+{
+	return kfifo_is_full(&qm->io_txq.fifo);
+}
+
+/**
+ * qm_io_rxq_is_empty() - return true if IO rx queue is empty
+ * @qm: address of the queue manager to be used
+ */
+static inline bool qm_io_rxq_is_empty(struct companion_queue_manager *qm)
+{
+	return kfifo_is_empty(&qm->io_rxq.fifo);
+}
+
+/**
+ * qm_io_txq_in() - put data from user sapce into IO tx queue
+ * @qm:     address of the queue manager to be used
+ * @buf:    address of the data to be put
+ * @count:  number of bytes to be put
+ * @copied: address to store the number of copied bytes
+ */
+static inline int qm_io_txq_in(struct companion_queue_manager *qm,
+			       const char __user              *buf,
+			       size_t                          count,
+			       unsigned int                   *copied)
+{
+	return kfifo_from_user(&qm->io_txq.fifo, buf, count, copied);
+}
+
+/**
+ * qm_io_txq_out() - get data from the IO tx queue
+ * @qm: address of the queue manager to be used
+ * @p:  address of the data to be copied
+ */
+static inline bool qm_io_txq_out(struct companion_queue_manager *qm,
+				 struct companion_packet        *p)
+{
+	return kfifo_out(&qm->io_txq.fifo, p, 1) == 1;
+}
+
+/**
+ * qm_io_rxq_in() - put data into IO rx queue
+ * @qm: address of the queue manager to be used
+ * @p:  address of the data to be put
+ */
+static inline bool qm_io_rxq_in(struct companion_queue_manager *qm,
+				const struct companion_packet  *p)
+{
+	return kfifo_in(&qm->io_rxq.fifo, p, 1) == 1;
+}
+
+/**
+ * qm_io_rxq_out() - copy data from the IO rx queue into user space
+ * @qm:     address of the queue manager to be used
+ * @buf:    address of the data to be copied
+ * @count:  number of the bytes to be copied
+ * @copied: address to store the number of copied bytes
+ */
+static inline int qm_io_rxq_out(struct companion_queue_manager *qm,
+				char __user                    *buf,
+				size_t                          count,
+				unsigned int                   *copied)
+{
+	return kfifo_to_user(&qm->io_rxq.fifo, buf, count, copied);
+}
+
+/**
+ * qm_can_txq_in() - put data into CAN tx queue
+ * @qm:   address of the queue manager to be used
+ * @p:    address of the data to be put
+ * @port: port number of which CAN queue array to be put
+ * @prio: priority of which CAN queue to be put
+ */
+static inline bool qm_can_txq_in(struct companion_queue_manager *qm,
+				 const struct companion_packet  *p,
+				 u8                              port,
+				 u8                              prio)
+{
+	bool result = false;
+
+	if (prio > 0)
+		return kfifo_in(&qm->can_txq[port][prio].fifo, p, 1) == 1;
+
+	/* queue 0 has multiple writers due to it sends both data and
+	 * administrative frames, while queue 1-7 only send data frame
+	 * (single writer), hence only queue 0 needs lock.
+	 */
+	mutex_lock(&qm->can_txq_lock[port]);
+	result = (kfifo_in(&qm->can_txq[port][prio].fifo, p, 1) == 1);
+	mutex_unlock(&qm->can_txq_lock[port]);
+	return result;
+}
+
+/**
+ * qm_can_txq_out() - get data from the CAN tx queue
+ * @qm:   address of the queue manager to be used
+ * @p:    address of the data to be copied
+ * @port: port number of which CAN queue array to be copied
+ * @prio: priority of which CAN queue to be copied
+ */
+static inline bool qm_can_txq_out(struct companion_queue_manager *qm,
+				  struct companion_packet        *p,
+				  u8                             port,
+				  u8                             prio)
+{
+	return kfifo_out(&qm->can_txq[port][prio].fifo, p, 1) == 1;
+}
+
+/**
+ * qm_can_rxq_in() - put data into CAN rx queue
+ * @qm:   address of the queue manager to be used
+ * @p:    address of the data to be put
+ * @port: port number of which CAN queue to be put
+ */
+static inline bool qm_can_rxq_in(struct companion_queue_manager *qm,
+				 const struct companion_packet  *p,
+				 u8                              port)
+{
+	return kfifo_in(&qm->can_rxq[port].fifo, p, 1) == 1;
+}
+
+/**
+ * qm_can_rxq_out() - get data from the CAN rx queue
+ * @qm:   address of the queue manager to be used
+ * @p:    address of the data to be copied
+ * @port: port number of which CAN queue to be copied
+ */
+static inline bool qm_can_rxq_out(struct companion_queue_manager *qm,
+				  struct companion_packet        *p,
+				  u8                              port)
+{
+	return kfifo_out(&qm->can_rxq[port].fifo, p, 1) == 1;
+}
+
+static inline bool qm_can_err_in(struct companion_queue_manager *qm,
+				 const struct companion_packet  *p,
+				 u8                              port)
+{
+	return kfifo_in(&qm->can_err[port].fifo, p, 1) == 1;
+}
+
+static inline bool qm_can_err_out(struct companion_queue_manager *qm,
+				  struct companion_packet        *p,
+				  u8                              port)
+{
+	return kfifo_out(&qm->can_err[port].fifo, p, 1) == 1;
+}
+
+#endif
diff --git a/include/linux/companion.h b/include/linux/companion.h
new file mode 100644
index 0000000..fdc6258
--- /dev/null
+++ b/include/linux/companion.h
@@ -0,0 +1,259 @@
+/*
+ * Companion low level driver interface
+ *
+ * Copyright (C) 2017 Bosch Sicherheitssysteme GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _BOSCH_COMPANION_H
+#define _BOSCH_COMPANION_H
+
+#include <linux/can/dev.h>
+#include <linux/device.h>
+
+#define COMPANION_PACKET_SIZE 16
+
+/**
+ * struct companion_io_ops - callbacks of companion IO packets handling
+ * @on_tx_done: called when IO packets tx is done
+ * @on_rx_done: called when IO packets rx is done
+ */
+struct companion_io_ops {
+	void (*on_tx_done)(void *data);
+	void (*on_rx_done)(void *data);
+};
+
+/**
+ * struct companion_can_ops - callbacks of companion CAN packets handling
+ * @on_tx_done:    called when CAN packets tx is done
+ * @on_rx_done:    called when CAN packets rx is done
+ * @on_error:      called when CAN error detected
+ * @on_tx_timeout: called when CAN packets tx timeout
+ */
+struct companion_can_ops {
+	void (*on_tx_done)(void *data, u8 prio, bool lost_seq, bool success);
+	void (*on_rx_done)(void *data);
+	void (*on_error)(void *data);
+	void (*on_tx_timeout)(void *data, u8 prio);
+};
+
+/**
+ * companion_io_ops_register() - register companion IO packets handler
+ * @parent: address of the caller parent device to be registered
+ * @ops:    address of the IO packets callback
+ * @data:   address of the IO packets callback argument
+ */
+int companion_io_ops_register(struct device           *parent,
+			      struct companion_io_ops *ops,
+			      void                    *data);
+
+/**
+ * companion_io_ops_unregister() - unregister companion IO packets handler
+ * @parent: address of the caller parent device to be unregistered
+ */
+int companion_io_ops_unregister(struct device *parent);
+
+/**
+ * companion_can_ops_register() - register companion CAN packets handler
+ * @parent: address of the caller parent device to be registered
+ * @ops:    address of the CAN packets callback
+ * @data:   address of the CAN packets callback argument
+ */
+int companion_can_ops_register(struct device            *parent,
+			       u8                        port,
+			       struct companion_can_ops *ops,
+			       void                     *data);
+
+/**
+ * companion_can_ops_unregister() - unregister comapnion CAN packets handler
+ * @parent: address of the caller parent device to be unregistered
+ * @port:   port number of which CAN to be unregistered
+ */
+int companion_can_ops_unregister(struct device *parent, u8 port);
+
+/**
+ * companion_io_txq_is_full() - return true if IO tx queue is full
+ * @parent: address of the caller parent device to be used
+ */
+bool companion_io_txq_is_full(struct device *parent);
+
+/**
+ * companion_io_rxq_is_empty() - return true if IO rx queue is empty
+ * @parent: address of the caller parent device to be used
+ */
+bool companion_io_rxq_is_empty(struct device *parent);
+
+/**
+ * companion_do_io_tx() - send IO packets from user space to companion
+ * @parent: address of the caller parent device to be used
+ * @buf:    address of the user space data to be sent
+ * @count:  number of bytes to be sent
+ */
+int companion_do_io_tx(struct device     *parent,
+		       const char __user *buf,
+		       size_t             count);
+
+/**
+ * companion_do_io_rx() - receive IO packets from companion to user space
+ * @parent: address of the caller parent device to be used
+ * @buf:    address of the data to be copied
+ * @count:  number of bytes to be copied
+ */
+int companion_do_io_rx(struct device *parent,
+		       char __user   *buf,
+		       size_t         count);
+
+/**
+ * companion_do_can_tx() - send CAN data to companion
+ * @parent: address of the caller parent device to be used
+ * @port:   port number of which CAN to be sent
+ * @prio:   priority of the raw CAN frame to be sent
+ * @cf:     address of the raw CAN frame to be sent
+ */
+int companion_do_can_tx(struct device          *parent,
+			u8                      port,
+			u8                      prio,
+			const struct can_frame *cf);
+
+/**
+ * companion_do_can_rx() - receive CAN data from companion
+ * @parent: address of the caller parent device to be used
+ * @port:   port number of which CAN to be received
+ * @cf:     address of the raw CAN frame to be copied
+ */
+int companion_do_can_rx(struct device    *parent,
+			u8                port,
+			struct can_frame *cf);
+
+/**
+ * companion_do_can_err() - receive CAN error from companion
+ * @parent: address of the caller parent device to be used
+ * @port:   port number of which CAN to be received
+ * @bec:    address of the error counter to be copied
+ * @state:  address of the error state to be copied
+ * @code:   address of the error code to be copied
+ */
+int companion_do_can_err(struct device           *parent,
+			 u8                       port,
+			 struct can_berr_counter *bec,
+			 u8                      *state,
+			 u8                      *code);
+
+#define COMPANION_CAN_STATE_WARNING BIT(0)
+#define COMPANION_CAN_STATE_PASSIVE BIT(1)
+#define COMPANION_CAN_STATE_BUS_OFF BIT(2)
+#define COMPANION_CAN_ERROR_STUFF   BIT(0)
+#define COMPANION_CAN_ERROR_FORM    BIT(1)
+#define COMPANION_CAN_ERROR_ACK     BIT(2)
+#define COMPANION_CAN_ERROR_BIT1    BIT(3)
+#define COMPANION_CAN_ERROR_BIT0    BIT(4)
+#define COMPANION_CAN_ERROR_CRC     BIT(5)
+#define COMPANION_CAN_ERROR_RXOV    BIT(7)
+
+/**
+ * companion_do_set_can_bittiming() - set companion CAN bittiming
+ * @parent:    address of the caller parent device to be used
+ * @port:      port number of which CAN to be set
+ * @bittiming: the bittiming to be set
+ */
+int companion_do_set_can_bittiming(struct device              *parent,
+				   u8                          port,
+				   const struct can_bittiming *bittiming);
+
+/**
+ * companion_do_set_can_mode() - set companion CAN mode
+ * @parent: address of the caller parent device to be used
+ * @port:   port number of which CAN to be set
+ * @mode:   the mode to be set
+ */
+int companion_do_set_can_mode(struct device *parent,
+			      u8             port,
+			      enum can_mode  mode);
+
+/**
+ * companion_do_set_can_ctrlmode() - set companion CAN control mode
+ * @parent: address of the caller parent device to be used
+ * @port:   port number of which CAN to be set
+ * @ctrl:   the control mode to be set
+ */
+int companion_do_set_can_ctrlmode(struct device *parent,
+				  u8             port,
+				  u32            ctrl);
+
+/**
+ * companion_do_get_can_status() - get companion CAN status
+ * @parent: address of the caller parent device to be used
+ * @port:   port number of which CAN to be inquiry
+ * @bec:    address of the error counter to be copied
+ */
+int companion_do_get_can_status(struct device           *parent,
+				u8                       port,
+				struct can_berr_counter *bec);
+
+/**
+ * companion_do_get_can_txq_status() - get companion CAN tx queue status
+ * @parent:        address of the caller parent device to be used
+ * @port:          port number of which CAN to be inquiry
+ * @prio:          queue number of which tx queue to be inquiry
+ * @lost_txq_sync: flag of the given CAN tx queue lost sync or not
+ */
+int companion_do_get_can_txq_status(struct device *parent,
+				    u8             port,
+				    u8             prio,
+				    bool          *lost_txq_sync);
+
+/**
+ * companion_do_get_can_txq_status_all() - get all companion CAN tx queue status
+ * @parent: address of the caller parent device to be used
+ * @port:   port number of which CAN to be inquiry
+ */
+int companion_do_get_can_txq_status_all(struct device *parent,
+					u8             port);
+
+/**
+ * companion_do_can_txq_is_full() - inquiry companion CAN tx queue is full
+ * @parent:  address of the caller parent device to be used
+ * @port:    port number of which CAN to be inquiry
+ * @prio:    queue number of which tx queue to be inquiry
+ * @is_full: address of the is full result to be copied
+ */
+int companion_do_can_txq_is_full(struct device *parent,
+				 u8             port,
+				 u8             prio,
+				 bool          *is_full);
+
+/**
+ * companion_do_can_txq_has_space() - inquiry companion CAN tx queue has space
+ * @parent:    address of the caller parent device to be used
+ * @port:      port number of which CAN to be inquiry
+ * @prio:      queue number of which tx queue to be inquiry
+ * @has_space: address of the has_space result to be copied
+ */
+int companion_do_can_txq_has_space(struct device *parent,
+				   u8             port,
+				   u8             prio,
+				   bool          *has_space);
+
+/**
+ * companion_do_can_start_tx_timer() - start companioin CAN tx timeout detection
+ * @parent: address of the caller parent device to be used
+ * @port:   port number of which CAN to be used
+ * @prio:   queue number of which tx queue's timer to be used
+ */
+int companion_do_can_start_tx_timer(struct device *parent,
+				    u8             port,
+				    u8             prio);
+
+/**
+ * companion_do_can_stop_tx_timer() - stop companion CAN tx timeout detection
+ * @parent: address of the caller parent device to be used
+ * @port:   port number of which CAN to be used
+ * @prio:   queue number of which tx queue's timer to be used
+ */
+int companion_do_can_stop_tx_timer(struct device *parent,
+				   u8             port,
+				   u8             prio);
+#endif
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 1/5] can: enable multi-queue for SocketCAN devices
From: Mark Jonas @ 2018-06-13 14:37 UTC (permalink / raw)
  To: Wolfgang Grandegger, Marc Kleine-Budde
  Cc: linux-can, netdev, linux-kernel, hs, yi.zhu5, petar.petrovic2,
	stephan.baetge, andy.shevchenko, socketcan, o.rempel, Mark Jonas
In-Reply-To: <1528900641-18677-1-git-send-email-mark.jonas@de.bosch.com>

From: Zhu Yi <yi.zhu5@cn.bosch.com>

The existing SocketCAN implementation provides alloc_candev() to
allocate a CAN device using a single Tx and Rx queue. This can lead to
priority inversion in case the single Tx queue is already full with low
priority messages and a high priority message needs to be sent while the
bus is fully loaded with medium priority messages.

This problem can be solved by using the existing multi-queue support of
the network subsytem. The commit makes it possible to use multi-queue in
the CAN subsystem in the same way it is used in the Ethernet subsystem
by adding an alloc_candev_mqs() call and accompanying macros. With this
support a CAN device can use multi-queue qdisc (e.g. mqprio) to avoid
the aforementioned priority inversion.

The exisiting functionality of alloc_candev() is the same as before.

CAN devices need to have prioritized multiple hardware queues or are
able to abort waiting for arbitration to make sensible use of
multi-queues.

Signed-off-by: Zhu Yi <yi.zhu5@cn.bosch.com>
Signed-off-by: Mark Jonas <mark.jonas@de.bosch.com>
Reviewed-by: Heiko Schocher <hs@denx.de>
---
 drivers/net/can/dev.c   | 8 +++++---
 include/linux/can/dev.h | 7 ++++++-
 2 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index 365a8cc..ac8270c 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -702,7 +702,8 @@ EXPORT_SYMBOL_GPL(alloc_can_err_skb);
 /*
  * Allocate and setup space for the CAN network device
  */
-struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
+struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max,
+				    unsigned int txqs, unsigned int rxqs)
 {
 	struct net_device *dev;
 	struct can_priv *priv;
@@ -714,7 +715,8 @@ struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
 	else
 		size = sizeof_priv;
 
-	dev = alloc_netdev(size, "can%d", NET_NAME_UNKNOWN, can_setup);
+	dev = alloc_netdev_mqs(size, "can%d", NET_NAME_UNKNOWN, can_setup,
+			       txqs, rxqs);
 	if (!dev)
 		return NULL;
 
@@ -733,7 +735,7 @@ struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
 
 	return dev;
 }
-EXPORT_SYMBOL_GPL(alloc_candev);
+EXPORT_SYMBOL_GPL(alloc_candev_mqs);
 
 /*
  * Free space of the CAN network device
diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h
index 61f1cf2..07b73d2 100644
--- a/include/linux/can/dev.h
+++ b/include/linux/can/dev.h
@@ -142,7 +142,12 @@ u8 can_dlc2len(u8 can_dlc);
 /* map the sanitized data length to an appropriate data length code */
 u8 can_len2dlc(u8 len);
 
-struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max);
+struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max,
+				    unsigned int txqs, unsigned int rxqs);
+#define alloc_candev(sizeof_priv, echo_skb_max) \
+	alloc_candev_mqs(sizeof_priv, echo_skb_max, 1, 1)
+#define alloc_candev_mq(sizeof_priv, echo_skb_max, count) \
+	alloc_candev_mqs(sizeof_priv, echo_skb_max, count, count)
 void free_candev(struct net_device *dev);
 
 /* a candev safe wrapper around netdev_priv */
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 0/5] can: enable multi-queue for SocketCAN devices
From: Mark Jonas @ 2018-06-13 14:37 UTC (permalink / raw)
  To: Wolfgang Grandegger, Marc Kleine-Budde
  Cc: linux-can, netdev, linux-kernel, hs, yi.zhu5, petar.petrovic2,
	stephan.baetge, andy.shevchenko, socketcan, o.rempel, Mark Jonas
In-Reply-To: <1528224240-30786-1-git-send-email-mark.jonas@de.bosch.com>

Changes in v2:
- use GPIO descriptor API
- make error handling pattern consistent
- use more kernel helper macros and functions
- fix coding style issues
- remove superfluous subsystem name from filename

---

Upon request by Marc Kleine-Budde this patch series does not only
contain our patch to enable enable multi-queue for SocketCAN devices
but also a driver (Companion driver suite) which makes active use of
this feature.

The driver suite implements
  - two CAN interfaces
  - one generic command interfaces
and offers a SocketCAN as well as a char device interface. The
SocketCAN interface supports multi-queue.

The functionality bases on an external peripheral chip named Companion.
It offers two CAN interfaces, each has 8 prioritized transmit FIFOs as
well as one receive FIFO. Besides CAN, undisclosed additional functions
can be accessed through the char device.

A standard SPI interface with two additional lines for flow control is
used. The Companion chip is the SPI slave.

The driver suite consists of three separate drivers. The following
diagram illustrates the dependencies in layers.

           /dev/companion       SocketCAN                User Space
-------------------------------------------------------------------
         +----------------+ +---------------+
         | companion-char | | companion-can |
         +----------------+ +---------------+
         +----------------------------------+
         |          companion-spi           |
         +----------------------------------+
         +----------------------------------+
         |     standard SPI subsystem       |
         +----------------------------------+          Linux Kernel
-------------------------------------------------------------------
               | | | |      | |                            Hardware
            CS-+ | | |      | +-BUSY
            CLK--+ | |      +---REQUEST
            MOSI---+ |
            MISO-----+

companion-spi
   core.c: handles SPI, sysfs entry and interface to upper layer
   protocol-manager.c: handles protocol with the SPI HW
   queue-manager.c: handles buffering and packets scheduling

companion-can
   makes use of multi-queue support and allows to use tc to configure
   the queuing discipline (e.g. mqprio). Together with the SO_PRIORITY
   socket option this allows to specify the FIFO a CAN frame shall be
   sent to.

companion-char
   handles messages to other undisclosed functionality beyond CAN.

Zhu Yi (5):
  can: enable multi-queue for SocketCAN devices
  spi: implement companion-spi driver
  char: implement companion-char driver
  can: implement companion-can driver
  spi,can,char: add companion DT binding documentation

 .../devicetree/bindings/spi/bosch,companion.txt    |   82 ++
 drivers/char/Kconfig                               |    7 +
 drivers/char/Makefile                              |    3 +
 drivers/char/companion.c                           |  360 ++++++
 drivers/net/can/Kconfig                            |    8 +
 drivers/net/can/Makefile                           |    2 +
 drivers/net/can/companion.c                        |  693 ++++++++++++
 drivers/net/can/dev.c                              |    8 +-
 drivers/spi/Kconfig                                |    2 +
 drivers/spi/Makefile                               |    2 +
 drivers/spi/companion/Kconfig                      |    5 +
 drivers/spi/companion/Makefile                     |    2 +
 drivers/spi/companion/core.c                       | 1185 ++++++++++++++++++++
 drivers/spi/companion/protocol-manager.c           | 1032 +++++++++++++++++
 drivers/spi/companion/protocol-manager.h           |  341 ++++++
 drivers/spi/companion/protocol.h                   |  273 +++++
 drivers/spi/companion/queue-manager.c              |  144 +++
 drivers/spi/companion/queue-manager.h              |  245 ++++
 include/linux/can/dev.h                            |    7 +-
 include/linux/companion.h                          |  259 +++++
 20 files changed, 4656 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/spi/bosch,companion.txt
 create mode 100644 drivers/char/companion.c
 create mode 100644 drivers/net/can/companion.c
 create mode 100644 drivers/spi/companion/Kconfig
 create mode 100644 drivers/spi/companion/Makefile
 create mode 100644 drivers/spi/companion/core.c
 create mode 100644 drivers/spi/companion/protocol-manager.c
 create mode 100644 drivers/spi/companion/protocol-manager.h
 create mode 100644 drivers/spi/companion/protocol.h
 create mode 100644 drivers/spi/companion/queue-manager.c
 create mode 100644 drivers/spi/companion/queue-manager.h
 create mode 100644 include/linux/companion.h

-- 
2.7.4

^ permalink raw reply

* Re: [PATCH net-next 0/10] xfrm: remove flow cache
From: Kristian Evensen @ 2018-06-13 13:43 UTC (permalink / raw)
  To: Florian Westphal
  Cc: David Miller, Network Development, Steffen Klassert, ilant
In-Reply-To: <20180613124042.zjmfm4rgrvfpgkbs@breakpoint.cc>

Hi,

On Wed, Jun 13, 2018 at 2:40 PM, Florian Westphal <fw@strlen.de> wrote:
> Can you test attached patch?
>
> I'd like to see how much the pcpu cache helps or if it actually hurts
> in your setup.
>
> Subject: [TEST PATCH 4.14.y] xfrm: remove pcpu policy cache
>
> We need to re-evaluate if this still buys anything after indirect calls
> got more expensive (retpolines).
> When pcpu xdst exists, it has to be validated first (which needs
> indirect calls).  So even if hit rate is good, it might be cheaper to
> allocate a new xdst entry.
>
> Furthermore, the current xdst cache needs to run with BH off, which
> is also not needed when its removed.
>
> Compile tested only.

Thanks! I will prepare a firmware for one of my devices tonight, start
testing tomorrow and report back when I have some results.

BR,
Kristian

^ permalink raw reply

* Re: [PATCH 2/2] ktime: helpers to convert between ktime and jiffies
From: Tejaswi Tanikella @ 2018-06-13 13:35 UTC (permalink / raw)
  To: Andrew Lunn; +Cc: netdev, f.fainelli, davem
In-Reply-To: <20180612163032.GB12251@lunn.ch>

On Tue, Jun 12, 2018 at 06:30:32PM +0200, Andrew Lunn wrote:
> On Mon, Jun 11, 2018 at 05:22:28PM +0530, Tejaswi Tanikella wrote:
> > Signed-off-by: Tejaswi Tanikella <tejaswit@codeaurora.org>
> > ---
> >  include/linux/ktime.h | 4 ++++
> >  1 file changed, 4 insertions(+)
> > 
> > diff --git a/include/linux/ktime.h b/include/linux/ktime.h
> > index 5b9fddb..4881483 100644
> > --- a/include/linux/ktime.h
> > +++ b/include/linux/ktime.h
> > @@ -96,6 +96,10 @@ static inline ktime_t timeval_to_ktime(struct timeval tv)
> >  /* Convert ktime_t to nanoseconds - NOP in the scalar storage format: */
> >  #define ktime_to_ns(kt)			(kt)
> >  
> > +/* ktime to jiffies and back */
> > +#define ktime_to_jiffies(kt)		nsecs_to_jiffies(kt)
> > +#define jiffies_to_ktime(j)		jiffies_to_nsecs(j)
> 
> Hi Tejaswi
> 
> You should also add some users of these new helpers.
> 
>     Andrew

Hi Andrew,

I used them in the first patch.

Thanks,
Tejaswi

^ permalink raw reply

* Re: [PATCH net 1/2] ipv4: igmp: use alarmtimer to prevent delayed reports
From: Tejaswi Tanikella @ 2018-06-13 13:32 UTC (permalink / raw)
  To: Andrew Lunn; +Cc: netdev, f.fainelli, davem
In-Reply-To: <20180612162818.GA12251@lunn.ch>

On Tue, Jun 12, 2018 at 06:28:18PM +0200, Andrew Lunn wrote:
> On Mon, Jun 11, 2018 at 05:21:05PM +0530, Tejaswi Tanikella wrote:
> > On receiving a IGMPv2/v3 query, based on max_delay set in the header a
> > timer is started to send out a response after a random time within
> > max_delay. If the system then moves into suspend state, Report is
> > delayed until system wakes up.
> > 
> > Use a alarmtimer instead of using a timer. Alarmtimer will wake the
> > system up from suspend to send out the IGMP report.
> 
> Hi Tejaswi
> 
> I think i must be missing something here. If we are suspended, we are
> not receiving multicast frames. If we are not receiving frames, why do
> we need to reply to the query?
> 
> Once we resume, i expect we will reply to the next query. You could
> optimise restarting the flow by immediately sending a membership
> report, same as when the setsockopt is used to join the group.
> 
> 	Andrew

Hi Andrew,


I'll try to explain my scenario. This was observed on a arm64 device.
An application registers for a mcast group, and just listens to mcast
packets. The connection is setup and mcast packets are being forwarded
by the router. Multicast packets are sent out every few minutes.
Not a very busy connection.

After some time the router sends out a IGMPv2 query. The max delay in
the header is set to 10s. The system starts a timer to send out the
response at 9s. But the device suspends and wakes up after 60s.
The response is sent out 50s late.

ftrace logs with boottime trace_clock and my igmp_logs:

4740693      kworker/0:3-395   [000] ..s2  4716.425695: igmp_log: skbaddr=ffffffc156fe6600 daddr=224.0.0.1, id=0, rc=4295217721
4740694      kworker/0:3-395   [000] d.s4  4716.425717: timer_start:timer=ffffffc217763140 function=igmp_timer_expire expires=4295218678 [timeout=957] flags=0x00000000
        timer set for 9.57 seconds.  957 jiffies

< device suspends >

< wakes up after ~60s >
4781289           <idle>-0     [000] .ns2  4779.170886: timer_expire_entry: timer=ffffffc217763140 function=igmp_timer_expire now=4295218678
4781290           <idle>-0     [000] .ns2  4779.171045: igmp_log: skbaddr=ffffffc1559d0e00 daddr=227.226.228.225, id=0, rc=4295218678

Since the response was delayed, mcast packets are not forwarded by the
router.

My changes use a alarmtimer, this will wake the system up if the timer
expires.

Thanks,
Tejaswi

^ permalink raw reply

* Re: FW: [PATCH 2/2] ath10k: allow ATH10K_SNOC with COMPILE_TEST
From: Niklas Cassel @ 2018-06-13 13:28 UTC (permalink / raw)
  To: Govind Singh, bjorn.andersson
  Cc: kvalo, davem, netdev, linux-wireless, linux-kernel, ath10k
In-Reply-To: <20180612124403.GA26986@centauri.lan>

On Tue, Jun 12, 2018 at 02:44:03PM +0200, Niklas Cassel wrote:
> On Tue, Jun 12, 2018 at 06:02:48PM +0530, Govind Singh wrote:
> > On 2018-06-12 17:45, Govind Singh wrote:
> > > -----Original Message-----
> > > From: ath10k <ath10k-bounces@lists.infradead.org> On Behalf Of Niklas
> > > Cassel
> > > Sent: Tuesday, June 12, 2018 5:09 PM
> > > To: Kalle Valo <kvalo@codeaurora.org>; David S. Miller
> > > <davem@davemloft.net>
> > > Cc: Niklas Cassel <niklas.cassel@linaro.org>; netdev@vger.kernel.org;
> > > linux-wireless@vger.kernel.org; linux-kernel@vger.kernel.org;
> > > ath10k@lists.infradead.org
> > > Subject: [PATCH 2/2] ath10k: allow ATH10K_SNOC with COMPILE_TEST
> > > 
> > > ATH10K_SNOC builds just fine with COMPILE_TEST, so make that possible.
> > > 
> > > Signed-off-by: Niklas Cassel <niklas.cassel@linaro.org>
> > > ---
> > >  drivers/net/wireless/ath/ath10k/Kconfig | 3 ++-
> > >  1 file changed, 2 insertions(+), 1 deletion(-)
> > > 
> > > diff --git a/drivers/net/wireless/ath/ath10k/Kconfig
> > > b/drivers/net/wireless/ath/ath10k/Kconfig
> > > index 54ff5930126c..6572a43590a8 100644
> > > --- a/drivers/net/wireless/ath/ath10k/Kconfig
> > > +++ b/drivers/net/wireless/ath/ath10k/Kconfig
> > > @@ -42,7 +42,8 @@ config ATH10K_USB
> > > 
> > >  config ATH10K_SNOC
> > >  	tristate "Qualcomm ath10k SNOC support (EXPERIMENTAL)"
> > > -	depends on ATH10K && ARCH_QCOM
> > > +	depends on ATH10K
> > > +	depends on ARCH_QCOM || COMPILE_TEST
> > >  	---help---
> > >  	  This module adds support for integrated WCN3990 chip connected
> > >  	  to system NOC(SNOC). Currently work in progress and will not
> > 
> > Thanks Niklas for enabling COMPILE_TEST. With QMI set of
> > changes(https://patchwork.kernel.org/patch/10448183/), we need to enable
> > COMPILE_TEST for
> > QCOM_SCM/QMI_HELPERS which seems broken today. Are you planning to fix the
> > same.
> 
> 

This patch is good as is.

However, Govind's QMI patch set together with this patch
resulted in build errors.

FTR, these are fixed by:
https://marc.info/?l=linux-kernel&m=152880985402356
https://marc.info/?l=linux-kernel&m=152889452326350


Regards,
Niklas

^ permalink raw reply

* Re: [PATCH net/jkirsher] bpf, xdp, i40e: fix i40e_build_skb skb reserve and truesize
From: Keith Busch @ 2018-06-13 13:30 UTC (permalink / raw)
  To: Daniel Borkmann
  Cc: jeffrey.t.kirsher, intel-wired-lan, makita.toshiaki, bjorn.topel,
	john.fastabend, netdev
In-Reply-To: <20180613090436.4266-1-daniel@iogearbox.net>

On Wed, Jun 13, 2018 at 11:04:36AM +0200, Daniel Borkmann wrote:
> Using skb_reserve(skb, I40E_SKB_PAD + (xdp->data - xdp->data_hard_start))
> is clearly wrong since I40E_SKB_PAD already points to the offset where
> the original xdp->data was sitting since xdp->data_hard_start is defined
> as xdp->data - i40e_rx_offset(rx_ring) where latter offsets to I40E_SKB_PAD
> when build skb is used.
> 
> However, also before cc5b114dcf98 ("bpf, i40e: add meta data support")
> this seems broken since bpf_xdp_adjust_head() helper could have been used
> to alter headroom and enlarge / shrink the frame and with that the assumption
> that the xdp->data remains unchanged does not hold and would push a bogus
> packet to upper stack.
> 
> ixgbe got this right in 924708081629 ("ixgbe: add XDP support for pass and
> drop actions"). In any case, fix it by removing the I40E_SKB_PAD from both
> skb_reserve() and truesize calculation.
> 
> Fixes: cc5b114dcf98 ("bpf, i40e: add meta data support")
> Fixes: 0c8493d90b6b ("i40e: add XDP support for pass and drop actions")
> Reported-by: Keith Busch <keith.busch@linux.intel.com>
> Reported-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
> Cc: Björn Töpel <bjorn.topel@intel.com>
> Cc: John Fastabend <john.fastabend@gmail.com>

Thanks for the quick fix! This works for me.

Tested-by: Keith Busch <keith.busch@linux.intel.com>

^ permalink raw reply

* Hello My Dear Friend,
From: Mrs.Zainab Ahmed @ 2018-06-13 13:25 UTC (permalink / raw)

In-Reply-To: <1487300945.4163989.1528896325939.ref@mail.yahoo.com>



I have a business proposal in the tune of $10.2 Million USD for you to handle with me. I have opportunity to transfer this abandon fund to your bank account in your country which belongs to our client.

I am inviting you in this transaction where this money can be shared between us at ratio of 60/40% and help the needy around us don’t be afraid of anything I am with you I will instruct you what you will do to maintain this fund.

Please kindly contact me with your information's if you are interested in this transaction for more details.

Your Name:..............
Your Bank Name:.............
Your Account Number:...........
Your Telephone Number:............
Your Country And Address:............
Your Age And Sex:.......................

Thanks
Mrs.Zainab Ahmed,

^ permalink raw reply

* [PATCH net 3/4] l2tp: prevent pppol2tp_connect() from creating kernel sockets
From: Guillaume Nault @ 2018-06-13 13:09 UTC (permalink / raw)
  To: netdev; +Cc: James Chapman
In-Reply-To: <cover.1528887257.git.g.nault@alphalink.fr>

If 'fd' is negative, l2tp_tunnel_create() creates a tunnel socket using
the configuration passed in 'tcfg'. Currently, pppol2tp_connect() sets
the relevant fields to zero, tricking l2tp_tunnel_create() into setting
up an unusable kernel socket.

We can't set 'tcfg' with the required fields because there's no way to
get them from the current connect() parameters. So let's restrict
kernel sockets creation to the netlink API, which is the original use
case.

Fixes: 789a4a2c61d8 ("l2tp: Add support for static unmanaged L2TPv3 tunnels")
Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
---
 net/l2tp/l2tp_ppp.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index 8b3b6947a07d..1b24f76ae210 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -701,6 +701,15 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
 				.encap = L2TP_ENCAPTYPE_UDP,
 				.debug = 0,
 			};
+
+			/* Prevent l2tp_tunnel_register() from trying to set up
+			 * a kernel socket.
+			 */
+			if (fd < 0) {
+				error = -EBADF;
+				goto end;
+			}
+
 			error = l2tp_tunnel_create(sock_net(sk), fd, ver, tunnel_id, peer_tunnel_id, &tcfg, &tunnel);
 			if (error < 0)
 				goto end;
-- 
2.17.1

^ permalink raw reply related

* [PATCH net 4/4] l2tp: clean up stale tunnel or session in pppol2tp_connect's error path
From: Guillaume Nault @ 2018-06-13 13:09 UTC (permalink / raw)
  To: netdev; +Cc: James Chapman
In-Reply-To: <cover.1528887257.git.g.nault@alphalink.fr>

pppol2tp_connect() may create a tunnel or a session. Remove them in
case of error.

Fixes: fd558d186df2 ("l2tp: Split pppol2tp patch into separate l2tp and ppp parts")
Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
---
 net/l2tp/l2tp_ppp.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index 1b24f76ae210..f429fed06a1e 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -612,6 +612,8 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
 	u32 session_id, peer_session_id;
 	bool drop_refcnt = false;
 	bool drop_tunnel = false;
+	bool new_session = false;
+	bool new_tunnel = false;
 	int ver = 2;
 	int fd;
 
@@ -722,6 +724,7 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
 				goto end;
 			}
 			drop_tunnel = true;
+			new_tunnel = true;
 		}
 	} else {
 		/* Error if we can't find the tunnel */
@@ -788,6 +791,7 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
 			goto end;
 		}
 		drop_refcnt = true;
+		new_session = true;
 	}
 
 	/* Special case: if source & dest session_id == 0x0000, this
@@ -834,6 +838,12 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
 		  session->name);
 
 end:
+	if (error) {
+		if (new_session)
+			l2tp_session_delete(session);
+		if (new_tunnel)
+			l2tp_tunnel_delete(tunnel);
+	}
 	if (drop_refcnt)
 		l2tp_session_dec_refcount(session);
 	if (drop_tunnel)
-- 
2.17.1

^ permalink raw reply related

* [PATCH net 2/4] l2tp: only accept PPP sessions in pppol2tp_connect()
From: Guillaume Nault @ 2018-06-13 13:09 UTC (permalink / raw)
  To: netdev; +Cc: James Chapman
In-Reply-To: <cover.1528887257.git.g.nault@alphalink.fr>

l2tp_session_priv() returns a struct pppol2tp_session pointer only for
PPPoL2TP sessions. In particular, if the session is an L2TP_PWTYPE_ETH
pseudo-wire, l2tp_session_priv() returns a pointer to an l2tp_eth_sess
structure, which is much smaller than struct pppol2tp_session. This
leads to invalid memory dereference when trying to lock ps->sk_lock.

Fixes: d9e31d17ceba ("l2tp: Add L2TP ethernet pseudowire support")
Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
---
 net/l2tp/l2tp_ppp.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index 270a0a999eaf..8b3b6947a07d 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -734,6 +734,12 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
 	session = l2tp_session_get(sock_net(sk), tunnel, session_id);
 	if (session) {
 		drop_refcnt = true;
+
+		if (session->pwtype != L2TP_PWTYPE_PPP) {
+			error = -EPROTOTYPE;
+			goto end;
+		}
+
 		ps = l2tp_session_priv(session);
 
 		/* Using a pre-existing session is fine as long as it hasn't
-- 
2.17.1

^ permalink raw reply related

* [PATCH net 1/4] l2tp: fix pseudo-wire type for sessions created by pppol2tp_connect()
From: Guillaume Nault @ 2018-06-13 13:09 UTC (permalink / raw)
  To: netdev; +Cc: James Chapman
In-Reply-To: <cover.1528887257.git.g.nault@alphalink.fr>

Define cfg.pw_type so that the new session is created with its .pwtype
field properly set (L2TP_PWTYPE_PPP).

Not setting the pseudo-wire type had several annoying effects:

  * Invalid value returned in the L2TP_ATTR_PW_TYPE attribute when
    dumping sessions with the netlink API.

  * Impossibility to delete the session using the netlink API (because
    l2tp_nl_cmd_session_delete() gets the deletion callback function
    from an array indexed by the session's pseudo-wire type).

Also, there are several cases where we should check a session's
pseudo-wire type. For example, pppol2tp_connect() should refuse to
connect a session that is not PPPoL2TP, but that requires the session's
.pwtype field to be properly set.

Fixes: f7faffa3ff8e ("l2tp: Add L2TPv3 protocol support")
Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
---
 net/l2tp/l2tp_ppp.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index b56cb1df4fc0..270a0a999eaf 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -751,6 +751,7 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
 		/* Default MTU must allow space for UDP/L2TP/PPP headers */
 		cfg.mtu = 1500 - PPPOL2TP_HEADER_OVERHEAD;
 		cfg.mru = cfg.mtu;
+		cfg.pw_type = L2TP_PWTYPE_PPP;
 
 		session = l2tp_session_create(sizeof(struct pppol2tp_session),
 					      tunnel, session_id,
-- 
2.17.1

^ permalink raw reply related

* [PATCH net 0/4] l2tp: pppol2tp_connect() fixes
From: Guillaume Nault @ 2018-06-13 13:09 UTC (permalink / raw)
  To: netdev; +Cc: James Chapman

This series fixes a few remaining issues with pppol2tp_connect().

It doesn't try to prevent invalid configurations that have no effect on
kernel's reliability. That would be work for a future patch set.

Patch 2 is the most important as it avoids an invalid pointer
dereference crashing the kernel. It depends on patch 1 for correctly
identifying L2TP session types.

Patches 3 and 4 avoid creating stale tunnels and sessions.

Guillaume Nault (4):
  l2tp: fix pseudo-wire type for sessions created by pppol2tp_connect()
  l2tp: only accept PPP sessions in pppol2tp_connect()
  l2tp: prevent pppol2tp_connect() from creating kernel sockets
  l2tp: clean up stale tunnel or session in pppol2tp_connect's error
    path

 net/l2tp/l2tp_ppp.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

-- 
2.17.1

^ permalink raw reply

* Re: [PATCH net-next 0/10] xfrm: remove flow cache
From: Florian Westphal @ 2018-06-13 12:40 UTC (permalink / raw)
  To: Kristian Evensen
  Cc: David Miller, Florian Westphal, Network Development,
	Steffen Klassert, ilant
In-Reply-To: <CAKfDRXgL+KHsO78fpD12t1o_P3LZV_RAkOPdzW26f4DXH7PoMA@mail.gmail.com>

Kristian Evensen <kristian.evensen@gmail.com> wrote:
> Hello,
> 
> On Tue, Jul 18, 2017 at 8:15 PM, David Miller <davem@davemloft.net> wrote:
> > Steffen, I know you have some level of trepidation about this because
> > there is obviously some performance cost immediately for removing this
> > DoS problem.
> 
> In a project I am involved in, we are running ipsec (Strongswan) on
> different mt7621-based routers. Each router is configured as an
> initiator and has around ~30 tunnels to different responders (running
> on misc. devices). Before the flow cache was removed (kernel 4.9), we
> got a combined throughput of around 70Mbit/s for all tunnels on one
> router. However, we recently switched to kernel 4.14 (4.14.48), and
> the total throughput is somewhere around 57Mbit/s (best-case). I.e., a
> drop of around 20%. Reverting the flow cache removal restores, as
> expected, performance levels to that of kernel 4.9.

Can you test attached patch?

I'd like to see how much the pcpu cache helps or if it actually hurts
in your setup.

Subject: [TEST PATCH 4.14.y] xfrm: remove pcpu policy cache

We need to re-evaluate if this still buys anything after indirect calls
got more expensive (retpolines).
When pcpu xdst exists, it has to be validated first (which needs
indirect calls).  So even if hit rate is good, it might be cheaper to
allocate a new xdst entry.

Furthermore, the current xdst cache needs to run with BH off, which
is also not needed when its removed.

Compile tested only.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/net/xfrm.h     |   1 -
 net/xfrm/xfrm_device.c |  10 ----
 net/xfrm/xfrm_policy.c | 138 +------------------------------------------------
 net/xfrm/xfrm_state.c  |   5 +-
 4 files changed, 3 insertions(+), 151 deletions(-)

diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index db99efb2d1d0..bdf185ae93db 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -323,7 +323,6 @@ int xfrm_policy_register_afinfo(const struct xfrm_policy_afinfo *afinfo, int fam
 void xfrm_policy_unregister_afinfo(const struct xfrm_policy_afinfo *afinfo);
 void km_policy_notify(struct xfrm_policy *xp, int dir,
 		      const struct km_event *c);
-void xfrm_policy_cache_flush(void);
 void km_state_notify(struct xfrm_state *x, const struct km_event *c);
 
 struct xfrm_tmpl;
diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
index 30e5746085b8..4e458fd9236a 100644
--- a/net/xfrm/xfrm_device.c
+++ b/net/xfrm/xfrm_device.c
@@ -153,12 +153,6 @@ static int xfrm_dev_register(struct net_device *dev)
 	return NOTIFY_DONE;
 }
 
-static int xfrm_dev_unregister(struct net_device *dev)
-{
-	xfrm_policy_cache_flush();
-	return NOTIFY_DONE;
-}
-
 static int xfrm_dev_feat_change(struct net_device *dev)
 {
 	if ((dev->features & NETIF_F_HW_ESP) && !dev->xfrmdev_ops)
@@ -178,7 +172,6 @@ static int xfrm_dev_down(struct net_device *dev)
 	if (dev->features & NETIF_F_HW_ESP)
 		xfrm_dev_state_flush(dev_net(dev), dev, true);
 
-	xfrm_policy_cache_flush();
 	return NOTIFY_DONE;
 }
 
@@ -190,9 +183,6 @@ static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void
 	case NETDEV_REGISTER:
 		return xfrm_dev_register(dev);
 
-	case NETDEV_UNREGISTER:
-		return xfrm_dev_unregister(dev);
-
 	case NETDEV_FEAT_CHANGE:
 		return xfrm_dev_feat_change(dev);
 
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 9c57d6a5816c..cdfb60a9820b 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -45,8 +45,6 @@ struct xfrm_flo {
 	u8 flags;
 };
 
-static DEFINE_PER_CPU(struct xfrm_dst *, xfrm_last_dst);
-static struct work_struct *xfrm_pcpu_work __read_mostly;
 static DEFINE_SPINLOCK(xfrm_policy_afinfo_lock);
 static struct xfrm_policy_afinfo const __rcu *xfrm_policy_afinfo[AF_INET6 + 1]
 						__read_mostly;
@@ -1715,108 +1713,6 @@ static int xfrm_expand_policies(const struct flowi *fl, u16 family,
 
 }
 
-static void xfrm_last_dst_update(struct xfrm_dst *xdst, struct xfrm_dst *old)
-{
-	this_cpu_write(xfrm_last_dst, xdst);
-	if (old)
-		dst_release(&old->u.dst);
-}
-
-static void __xfrm_pcpu_work_fn(void)
-{
-	struct xfrm_dst *old;
-
-	old = this_cpu_read(xfrm_last_dst);
-	if (old && !xfrm_bundle_ok(old))
-		xfrm_last_dst_update(NULL, old);
-}
-
-static void xfrm_pcpu_work_fn(struct work_struct *work)
-{
-	local_bh_disable();
-	rcu_read_lock();
-	__xfrm_pcpu_work_fn();
-	rcu_read_unlock();
-	local_bh_enable();
-}
-
-void xfrm_policy_cache_flush(void)
-{
-	struct xfrm_dst *old;
-	bool found = 0;
-	int cpu;
-
-	might_sleep();
-
-	local_bh_disable();
-	rcu_read_lock();
-	for_each_possible_cpu(cpu) {
-		old = per_cpu(xfrm_last_dst, cpu);
-		if (old && !xfrm_bundle_ok(old)) {
-			if (smp_processor_id() == cpu) {
-				__xfrm_pcpu_work_fn();
-				continue;
-			}
-			found = true;
-			break;
-		}
-	}
-
-	rcu_read_unlock();
-	local_bh_enable();
-
-	if (!found)
-		return;
-
-	get_online_cpus();
-
-	for_each_possible_cpu(cpu) {
-		bool bundle_release;
-
-		rcu_read_lock();
-		old = per_cpu(xfrm_last_dst, cpu);
-		bundle_release = old && !xfrm_bundle_ok(old);
-		rcu_read_unlock();
-
-		if (!bundle_release)
-			continue;
-
-		if (cpu_online(cpu)) {
-			schedule_work_on(cpu, &xfrm_pcpu_work[cpu]);
-			continue;
-		}
-
-		rcu_read_lock();
-		old = per_cpu(xfrm_last_dst, cpu);
-		if (old && !xfrm_bundle_ok(old)) {
-			per_cpu(xfrm_last_dst, cpu) = NULL;
-			dst_release(&old->u.dst);
-		}
-		rcu_read_unlock();
-	}
-
-	put_online_cpus();
-}
-
-static bool xfrm_xdst_can_reuse(struct xfrm_dst *xdst,
-				struct xfrm_state * const xfrm[],
-				int num)
-{
-	const struct dst_entry *dst = &xdst->u.dst;
-	int i;
-
-	if (xdst->num_xfrms != num)
-		return false;
-
-	for (i = 0; i < num; i++) {
-		if (!dst || dst->xfrm != xfrm[i])
-			return false;
-		dst = dst->child;
-	}
-
-	return xfrm_bundle_ok(xdst);
-}
-
 static struct xfrm_dst *
 xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols,
 			       const struct flowi *fl, u16 family,
@@ -1824,7 +1720,7 @@ xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols,
 {
 	struct net *net = xp_net(pols[0]);
 	struct xfrm_state *xfrm[XFRM_MAX_DEPTH];
-	struct xfrm_dst *xdst, *old;
+	struct xfrm_dst *xdst;
 	struct dst_entry *dst;
 	int err;
 
@@ -1836,21 +1732,6 @@ xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols,
 		return ERR_PTR(err);
 	}
 
-	xdst = this_cpu_read(xfrm_last_dst);
-	if (xdst &&
-	    xdst->u.dst.dev == dst_orig->dev &&
-	    xdst->num_pols == num_pols &&
-	    memcmp(xdst->pols, pols,
-		   sizeof(struct xfrm_policy *) * num_pols) == 0 &&
-	    xfrm_xdst_can_reuse(xdst, xfrm, err)) {
-		dst_hold(&xdst->u.dst);
-		while (err > 0)
-			xfrm_state_put(xfrm[--err]);
-		return xdst;
-	}
-
-	old = xdst;
-
 	dst = xfrm_bundle_create(pols[0], xfrm, err, fl, dst_orig);
 	if (IS_ERR(dst)) {
 		XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTBUNDLEGENERROR);
@@ -1863,9 +1744,6 @@ xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols,
 	memcpy(xdst->pols, pols, sizeof(struct xfrm_policy *) * num_pols);
 	xdst->policy_genid = atomic_read(&pols[0]->genid);
 
-	atomic_set(&xdst->u.dst.__refcnt, 2);
-	xfrm_last_dst_update(xdst, old);
-
 	return xdst;
 }
 
@@ -2066,11 +1944,8 @@ xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir,
 	if (num_xfrms <= 0)
 		goto make_dummy_bundle;
 
-	local_bh_disable();
 	xdst = xfrm_resolve_and_create_bundle(pols, num_pols, fl, family,
 					      xflo->dst_orig);
-	local_bh_enable();
-
 	if (IS_ERR(xdst)) {
 		err = PTR_ERR(xdst);
 		if (err != -EAGAIN)
@@ -2157,11 +2032,9 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
 				goto no_transform;
 			}
 
-			local_bh_disable();
 			xdst = xfrm_resolve_and_create_bundle(
 					pols, num_pols, fl,
 					family, dst_orig);
-			local_bh_enable();
 
 			if (IS_ERR(xdst)) {
 				xfrm_pols_put(pols, num_pols);
@@ -2982,15 +2855,6 @@ static struct pernet_operations __net_initdata xfrm_net_ops = {
 
 void __init xfrm_init(void)
 {
-	int i;
-
-	xfrm_pcpu_work = kmalloc_array(NR_CPUS, sizeof(*xfrm_pcpu_work),
-				       GFP_KERNEL);
-	BUG_ON(!xfrm_pcpu_work);
-
-	for (i = 0; i < NR_CPUS; i++)
-		INIT_WORK(&xfrm_pcpu_work[i], xfrm_pcpu_work_fn);
-
 	register_pernet_subsys(&xfrm_net_ops);
 	seqcount_init(&xfrm_policy_hash_generation);
 	xfrm_input_init();
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 6c4ec69e11a0..bff47fd1519b 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -735,10 +735,9 @@ int xfrm_state_flush(struct net *net, u8 proto, bool task_valid)
 	}
 out:
 	spin_unlock_bh(&net->xfrm.xfrm_state_lock);
-	if (cnt) {
+	if (cnt)
 		err = 0;
-		xfrm_policy_cache_flush();
-	}
+
 	return err;
 }
 EXPORT_SYMBOL(xfrm_state_flush);
-- 
2.16.4

> Carrying around a fairly large revert patch is not something we want,
> we are more interested in trying to fix at least some of the
> performance problems. However, we are not very experienced when it
> comes to profiling the kernel code or the xfrm-code itself. Are there
> any known areas we should take a special look at, or should we just
> read-up on different profiling tools and get started?
> 
> Also, the revert went very smooth, which always makes me a bit
> nervous. Are there any parts of the flow cache removal that should or
> would require a bit of special care when reverted?
> 
> Thanks in advance for any help.
> 
> BR,
> Kristian

-- 
Florian Westphal <fw@strlen.de>
4096R/AD5FF600  2015-09-13
Key fingerprint = 80A9 20C5 B203 E069 F586  AE9F 7091 A8D9 AD5F F600
Phone: +49 151 11132303

^ permalink raw reply related

* [PATCH 1/1] selftest: check tunnel type more accurately
From: Wang Jian @ 2018-06-13 12:03 UTC (permalink / raw)
  To: ast, daniel, shuah, netdev

Grep tunnel type directly to make sure 'ip' command supports it.

Signed-off-by: Jian Wang <jianjian.wang1@gmail.com>
---
 tools/testing/selftests/bpf/test_tunnel.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/testing/selftests/bpf/test_tunnel.sh
b/tools/testing/selftests/bpf/test_tunnel.sh
index aeb2901..c4b5fbb 100755
--- a/tools/testing/selftests/bpf/test_tunnel.sh
+++ b/tools/testing/selftests/bpf/test_tunnel.sh
@@ -668,7 +668,7 @@ cleanup_exit()

 check()
 {
-       ip link help $1 2>&1 | grep -q "^Usage:"
+       ip link help 2>&1 | grep -q "\s$1\s"
        if [ $? -ne 0 ];then
                echo "SKIP $1: iproute2 not support"
        cleanup

^ permalink raw reply related

* [PATCH] SUNRPC: Move inline xprt_alloc_xid() up to fix compiler warning
From: Geert Uytterhoeven @ 2018-06-13 12:01 UTC (permalink / raw)
  To: Chuck Lever, J . Bruce Fields, Jeff Layton, Trond Myklebust,
	Anna Schumaker, David S . Miller
  Cc: linux-nfs, netdev, linux-kernel, Geert Uytterhoeven

With gcc 4.1.2:

    net/sunrpc/xprt.c:69: warning: ‘xprt_alloc_xid’ declared inline after being called
    net/sunrpc/xprt.c:69: warning: previous declaration of ‘xprt_alloc_xid’ was here

To fix this, move the function up, before its caller, and remove the no
longer needed forward declaration.

Fixes: 37ac86c3a76c1136 ("SUNRPC: Initialize rpc_rqst outside of xprt->reserve_lock")
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
 net/sunrpc/xprt.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
index 3c85af058227d14b..60a8b9f91cf94b54 100644
--- a/net/sunrpc/xprt.c
+++ b/net/sunrpc/xprt.c
@@ -66,7 +66,6 @@
  * Local functions
  */
 static void	 xprt_init(struct rpc_xprt *xprt, struct net *net);
-static __be32	xprt_alloc_xid(struct rpc_xprt *xprt);
 static void	xprt_connect_status(struct rpc_task *task);
 static int      __xprt_get_cong(struct rpc_xprt *, struct rpc_task *);
 static void     __xprt_put_cong(struct rpc_xprt *, struct rpc_rqst *);
@@ -956,6 +955,11 @@ static void xprt_timer(struct rpc_task *task)
 		task->tk_status = 0;
 }
 
+static inline __be32 xprt_alloc_xid(struct rpc_xprt *xprt)
+{
+	return (__force __be32)xprt->xid++;
+}
+
 /**
  * xprt_prepare_transmit - reserve the transport before sending a request
  * @task: RPC task about to send a request
@@ -1296,11 +1300,6 @@ void xprt_retry_reserve(struct rpc_task *task)
 	xprt->ops->alloc_slot(xprt, task);
 }
 
-static inline __be32 xprt_alloc_xid(struct rpc_xprt *xprt)
-{
-	return (__force __be32)xprt->xid++;
-}
-
 static inline void xprt_init_xid(struct rpc_xprt *xprt)
 {
 	xprt->xid = prandom_u32();
-- 
2.7.4

^ permalink raw reply related

* REPLY URGENLY.
From: Matthias @ 2018-06-13 11:59 UTC (permalink / raw)

In-Reply-To: <1713973028.4792225.1528891175685.ref@mail.yahoo.com>

 Dear Friend,

Mr. john Matthias ouedraogo, the manager in charge of auditing and accounting section
of Bank of Africa (BOA) Ouagadougou Burkina-Faso West-Africa. I would like you
to indicate your interest to receive the transfer of $19.3 Million Dollars. I
will like you to stand as the next of kin to our late customer whose account
is presently dormant for claims. Please once you are interested kindly send
me the following details information below,

1.Your full name:...........
2.Resident address:........
3.Private phone........
4.fax numbers:...............
5.Country :................
6.Occupation:..............
7.Age:.........
8.sex........ 

I shall send you more details as soon as i hear from you.

Regards,
My Regards,
Mr. john Matthias ouedraogo



REPLY URGENTLY.

^ permalink raw reply

* Re: ath10k: fix memory leak of tpc_stats
From: Kalle Valo @ 2018-06-13 11:51 UTC (permalink / raw)
  To: Colin Ian King
  Cc: linux-wireless, netdev, kernel-janitors, linux-kernel, ath10k,
	David S . Miller
In-Reply-To: <20180527211702.23778-1-colin.king@canonical.com>

Colin Ian King <colin.king@canonical.com> wrote:

> Currently tpc_stats is allocated and is leaked on the return
> path if num_tx_chain is greater than WMI_TPC_TX_N_CHAIN. Avoid
> this leak by performing the check on num_tx_chain before the
> allocation of tpc_stats.
> 
> Detected by CoverityScan, CID#1469422 ("Resource Leak")
> Fixes: 4b190675ad06 ("ath10k: fix kernel panic while reading tpc_stats")
> 
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>

Patch applied to ath-next branch of ath.git, thanks.

260e629bbf44 ath10k: fix memory leak of tpc_stats

-- 
https://patchwork.kernel.org/patch/10429553/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply

* Re: ath10k: fix memory leak of tpc_stats
From: Kalle Valo @ 2018-06-13 11:51 UTC (permalink / raw)
  To: Colin Ian King
  Cc: David S . Miller, ath10k, linux-wireless, netdev, kernel-janitors,
	linux-kernel
In-Reply-To: <20180527211702.23778-1-colin.king@canonical.com>

Colin Ian King <colin.king@canonical.com> wrote:

> Currently tpc_stats is allocated and is leaked on the return
> path if num_tx_chain is greater than WMI_TPC_TX_N_CHAIN. Avoid
> this leak by performing the check on num_tx_chain before the
> allocation of tpc_stats.
> 
> Detected by CoverityScan, CID#1469422 ("Resource Leak")
> Fixes: 4b190675ad06 ("ath10k: fix kernel panic while reading tpc_stats")
> 
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>

Patch applied to ath-next branch of ath.git, thanks.

260e629bbf44 ath10k: fix memory leak of tpc_stats

-- 
https://patchwork.kernel.org/patch/10429553/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply

* Re: ath9k: mark expected switch fall-throughs
From: Kalle Valo @ 2018-06-13 11:50 UTC (permalink / raw)
  To: Gustavo A. R. Silva
  Cc: QCA ath9k Development, David S. Miller, linux-wireless, netdev,
	linux-kernel, Gustavo A. R. Silva
In-Reply-To: <20180525212207.GA10681@embeddedor.com>

"Gustavo A. R. Silva" <gustavo@embeddedor.com> wrote:

> In preparation to enabling -Wimplicit-fallthrough, mark switch cases
> where we are expecting to fall through.
> 
> Signed-off-by: Gustavo A. R. Silva <gustavo@embeddedor.com>
> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>

Patch applied to ath-next branch of ath.git, thanks.

12b67b0d6bcb ath9k: mark expected switch fall-throughs

-- 
https://patchwork.kernel.org/patch/10428521/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply

* [Fwd: avahi-daemon.service startup failure post kernel commit f396922d862a]
From: Mike Galbraith @ 2018-06-13 11:45 UTC (permalink / raw)
  To: LKML; +Cc: netdev
In-Reply-To: <1528889545.30527.6.camel@gmx.de>

Well, the folks at "To:" below apparently don't want bug reports from
non-subscribers (no mediation, simply rejected).  Posting here simply
because it may save some other busy person a bisection. 

-------- Forwarded Message --------
From: Mike Galbraith <efault@gmx.de>
To: avahi@lists.freedesktop.org
Subject: avahi-daemon.service startup failure post kernel commit
f396922d862a
Date: Wed, 13 Jun 2018 13:32:25 +0200

Greetings,

Service startup failure bisected to a kernel commit, but that commit
points the finger at userspace, ergo an attempt to report it.  Let's
see if it bounces.

homer:~ # systemctl status avahi-daemon
● avahi-daemon.service - Avahi mDNS/DNS-SD Stack
   Loaded: loaded (/usr/lib/systemd/system/avahi-daemon.service; enabled; vendor preset: enabled)
   Active: failed (Result: exit-code) since Wed 2018-06-13 09:49:58 CEST; 1min 54s ago
  Process: 1930 ExecStart=/usr/sbin/avahi-daemon -s (code=exited, status=255)
 Main PID: 1930 (code=exited, status=255)
   Status: "avahi-daemon 0.6.32 exiting."

Jun 13 09:49:58 homer systemd[1]: Started Avahi mDNS/DNS-SD Stack.
Jun 13 09:49:58 homer avahi-daemon[1930]: Loading service file /etc/avahi/services/sftp-ssh.service.
Jun 13 09:49:58 homer avahi-daemon[1930]: Loading service file /etc/avahi/services/ssh.service.
Jun 13 09:49:58 homer avahi-daemon[1930]: SO_REUSEADDR failed: Structure needs cleaning
Jun 13 09:49:58 homer avahi-daemon[1930]: SO_REUSEADDR failed: Structure needs cleaning
Jun 13 09:49:58 homer avahi-daemon[1930]: Failed to create server: No suitable network protocol available
Jun 13 09:49:58 homer avahi-daemon[1930]: avahi-daemon 0.6.32 exiting.
Jun 13 09:49:58 homer systemd[1]: avahi-daemon.service: Main process exited, code=exited, status=255/n/a
Jun 13 09:49:58 homer systemd[1]: avahi-daemon.service: Unit entered failed state.
Jun 13 09:49:58 homer systemd[1]: avahi-daemon.service: Failed with result 'exit-code'.
homer:~ #

f396922d862aa05b53ad740596652691a723ee23 is the first bad commit
commit f396922d862aa05b53ad740596652691a723ee23
Author: Maciej Żenczykowski <maze@google.com>
Date:   Sun Jun 3 10:47:05 2018 -0700

    net: do not allow changing SO_REUSEADDR/SO_REUSEPORT on bound sockets
    
    It is not safe to do so because such sockets are already in the
    hash tables and changing these options can result in invalidating
    the tb->fastreuse(port) caching.
    
    This can have later far reaching consequences wrt. bind conflict checks
    which rely on these caches (for optimization purposes).
    
    Not to mention that you can currently end up with two identical
    non-reuseport listening sockets bound to the same local ip:port
    by clearing reuseport on them after they've already both been bound.
    
    There is unfortunately no EISBOUND error or anything similar,
    and EISCONN seems to be misleading for a bound-but-not-connected
    socket, so use EUCLEAN 'Structure needs cleaning' which AFAICT
    is the closest you can get to meaning 'socket in bad state'.
    (although perhaps EINVAL wouldn't be a bad choice either?)
    
    This does unfortunately run the risk of breaking buggy
    userspace programs...
    
    Signed-off-by: Maciej Żenczykowski <maze@google.com>
    Cc: Eric Dumazet <edumazet@google.com>
    Change-Id: I77c2b3429b2fdf42671eee0fa7a8ba721c94963b
    Reviewed-by: Eric Dumazet <edumazet@google.com>
    Signed-off-by: David S. Miller <davem@davemloft.net>

:040000 040000 39b702bc132c8aa812fbd452822a7047331553a1 e0ed7194986fd828073702d5346a4f91fbd6ea01 M	net

^ permalink raw reply

* Re: [v2] ath6kl: mark expected switch fall-throughs
From: Kalle Valo @ 2018-06-13 11:44 UTC (permalink / raw)
  To: Gustavo A. R. Silva
  Cc: Kalle Valo, David S. Miller, Sergei Shtylyov, linux-wireless,
	netdev, linux-kernel, Gustavo A. R. Silva
In-Reply-To: <20180525182311.GA3000@embeddedor.com>

"Gustavo A. R. Silva" <gustavo@embeddedor.com> wrote:

> In preparation to enabling -Wimplicit-fallthrough, mark switch cases
> where we are expecting to fall through.
> 
> Signed-off-by: Gustavo A. R. Silva <gustavo@embeddedor.com>
> Reviewed-by: Steve deRosier <derosier@cal-sierra.com>
> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>

Patch applied to ath-next branch of ath.git, thanks.

4de30c906ef0 ath6kl: mark expected switch fall-throughs

-- 
https://patchwork.kernel.org/patch/10428239/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply

* Re: ath5k: mark expected switch fall-through
From: Kalle Valo @ 2018-06-13 11:42 UTC (permalink / raw)
  To: Gustavo A. R. Silva
  Cc: Jiri Slaby, Nick Kossifidis, Luis R. Rodriguez, David S. Miller,
	linux-wireless, netdev, linux-kernel, Gustavo A. R. Silva
In-Reply-To: <20180524230700.GA21433@embeddedor.com>

"Gustavo A. R. Silva" <gustavo@embeddedor.com> wrote:

> In preparation to enabling -Wimplicit-fallthrough, mark switch cases
> where we are expecting to fall through.
> 
> Signed-off-by: Gustavo A. R. Silva <gustavo@embeddedor.com>
> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>

Patch applied to ath-next branch of ath.git, thanks.

aae28cefc279 ath5k: mark expected switch fall-through

-- 
https://patchwork.kernel.org/patch/10425693/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox