Subject: [PATCH 0/5] can: enable multi-queue for SocketCAN devices

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 | 2 +
drivers/char/companion-char.c | 367 ++++++
drivers/net/can/Kconfig | 8 +
drivers/net/can/Makefile | 1 +
drivers/net/can/companion-can.c | 694 ++++++++++++
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 | 1189 ++++++++++++++++++++
drivers/spi/companion/protocol-manager.c | 1035 +++++++++++++++++
drivers/spi/companion/protocol-manager.h | 348 ++++++
drivers/spi/companion/protocol.h | 273 +++++
drivers/spi/companion/queue-manager.c | 146 +++
drivers/spi/companion/queue-manager.h | 245 ++++
include/linux/can/dev.h | 7 +-
include/linux/companion.h | 258 +++++
20 files changed, 4677 insertions(+), 4 deletions(-)
create mode 100644 Documentation/devicetree/bindings/spi/bosch,companion.txt
create mode 100644 drivers/char/companion-char.c
create mode 100644 drivers/net/can/companion-can.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



Subject: [PATCH 4/5] can: implement companion-can driver

From: Zhu Yi <[email protected]>

The upper level companion-can driver provides SocketCAN interface to
userspace for communicate CAN messages with the companion processor.

Signed-off-by: Zhu Yi <[email protected]>
Signed-off-by: Mark Jonas <[email protected]>
---
drivers/net/can/Kconfig | 8 +
drivers/net/can/Makefile | 1 +
drivers/net/can/companion-can.c | 694 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 703 insertions(+)
create mode 100644 drivers/net/can/companion-can.c

diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index ac4ff39..e403a7e 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -155,6 +155,14 @@ config PCH_CAN
is an IOH for x86 embedded processor (Intel Atom E6xx series).
This driver can access CAN bus.

+config COMPANION_CAN
+ tristate "Network device for companion communication (Bosch)"
+ depends on COMPANION_SPI
+ ---help---
+ The network device allows the userspace to use SocketCAN interface
+ to communicate with the Bosch companion processor via the companion
+ SPI driver.
+
source "drivers/net/can/c_can/Kconfig"
source "drivers/net/can/cc770/Kconfig"
source "drivers/net/can/ifi_canfd/Kconfig"
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index 02b8ed7..a66a1f9 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -34,5 +34,6 @@ obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o
obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o
obj-$(CONFIG_PCH_CAN) += pch_can.o
+obj-$(CONFIG_COMPANION_CAN) += companion-can.o

subdir-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) += -DDEBUG
diff --git a/drivers/net/can/companion-can.c b/drivers/net/can/companion-can.c
new file mode 100644
index 0000000..5078640
--- /dev/null
+++ b/drivers/net/can/companion-can.c
@@ -0,0 +1,694 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Companion upper level can network 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/can/dev.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/companion.h>
+
+#define TX_QUEUE_DEPTH 16
+#define NUM_TX_QUEUES 8
+#define NUM_RX_QUEUES 1
+#define TX_ECHO_SKB_MAX NUM_TX_QUEUES * TX_QUEUE_DEPTH
+#define DRIVER_NAME "bosch,companion-can"
+
+/**
+ * struct companion_can_priv - companion-can private data structure
+ * @can: standard common CAN private data, must be first member
+ * @parent: address of the associated parent device
+ * @dev: address of the associated network device
+ * @port: the companion CAN port number
+ * @tx_head: array of all tx queue head
+ * @tx_tail: arrat of all tx queue tail
+ */
+struct companion_can_priv {
+ struct can_priv can;
+ struct device *parent;
+ struct net_device *dev;
+ u8 port;
+ u8 tx_head[NUM_TX_QUEUES];
+ u8 tx_tail[NUM_TX_QUEUES];
+};
+
+/**
+ * companion_can_put_echo_skb() - put echo skb into ring buffer
+ * @priv: address of companion-can private data
+ * @prio: which CAN queue to put
+ * @skb: address of the packet to put
+ */
+static void companion_can_put_echo_skb(struct companion_can_priv *priv,
+ u8 prio,
+ struct sk_buff *skb)
+{
+ u8 offset = prio * TX_QUEUE_DEPTH;
+ u8 index = priv->tx_head[prio] % TX_QUEUE_DEPTH;
+ can_put_echo_skb(skb, priv->dev, offset + index);
+ priv->tx_head[prio]++;
+}
+
+/**
+ * companion_can_get_echo_skb() - get echo skb from ring buffer
+ * @priv: address of companion-can private data
+ * @prio: which CAN queue to get
+ */
+static u8 companion_can_get_echo_skb(struct companion_can_priv *priv,
+ u8 prio)
+{
+ u8 offset, index, result = 0;
+
+ if (priv->tx_head[prio] != priv->tx_tail[prio]) {
+ offset = prio * TX_QUEUE_DEPTH;
+ index = priv->tx_tail[prio] % TX_QUEUE_DEPTH;
+ result = can_get_echo_skb(priv->dev, offset + index);
+ priv->tx_tail[prio]++;
+ }
+ return result;
+}
+
+/**
+ * companion_can_free_echo_skb() - free echo skb from ring buffer
+ * @priv: address of companion-can private data
+ * @prio: which CAN queue to free
+ */
+static void companion_can_free_echo_skb(struct companion_can_priv *priv,
+ u8 prio)
+{
+ u8 offset, index;
+
+ if (priv->tx_head[prio] != priv->tx_tail[prio]) {
+ offset = prio * TX_QUEUE_DEPTH;
+ index = priv->tx_tail[prio] % TX_QUEUE_DEPTH;
+ can_free_echo_skb(priv->dev, offset + index);
+ priv->tx_tail[prio]++;
+ }
+}
+
+/**
+ * companion_can_set_bittiming() - set CAN bittiming
+ * @dev: address of the associated network device
+ */
+static int companion_can_set_bittiming(struct net_device *dev)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ const struct can_bittiming *bt = &priv->can.bittiming;
+ u32 ctrl = priv->can.ctrlmode;
+ int err;
+
+ err = companion_do_set_can_bittiming(priv->parent, priv->port, bt);
+ if (err)
+ return err;
+
+ if (ctrl & CAN_CTRLMODE_LISTENONLY) {
+ err = companion_do_set_can_ctrlmode(priv->parent,
+ priv->port,
+ ctrl);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+/**
+ * companion_can_set_mode() - set CAN mode
+ * @dev: address of the associated network device
+ * @mode: the CAN mode to set
+ */
+static int companion_can_set_mode(struct net_device *dev,
+ enum can_mode mode)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ int err;
+
+ switch (mode) {
+ case CAN_MODE_START:
+ err = companion_can_set_bittiming(dev);
+ if (err)
+ return err;
+ /* fall through */
+
+ case CAN_MODE_STOP:
+ err = companion_do_set_can_mode(priv->parent,
+ priv->port,
+ mode);
+ if (err)
+ return err;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+/**
+ * companion_can_get_berr_counter() - get CAN error counter
+ * @dev: address of the associated network device
+ * @bec: address of the CAN error counter to store
+ */
+static int companion_can_get_berr_counter(const struct net_device *dev,
+ struct can_berr_counter *bec)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ return companion_do_get_can_status(priv->parent, priv->port, bec);
+}
+
+/**
+ * companion_can_handle_state() - handle CAN state transition
+ * @dev: address of the associated network device
+ * @cf: address of the CAN frame to store CAN state
+ * @bec: address of the CAN error counter
+ * @state: the companion CAN state
+ */
+static void companion_can_handle_state(struct net_device *dev,
+ struct can_frame *cf,
+ struct can_berr_counter *bec,
+ u8 state)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ enum can_state new_state = CAN_STATE_ERROR_ACTIVE;
+ enum can_state rx_state = CAN_STATE_ERROR_ACTIVE;
+ enum can_state tx_state = CAN_STATE_ERROR_ACTIVE;
+
+ if (state & COMPANION_CAN_STATE_BUS_OFF) {
+ new_state = CAN_STATE_BUS_OFF;
+ rx_state = bec->rxerr >= bec->txerr ? new_state : rx_state;
+ tx_state = bec->txerr >= bec->rxerr ? new_state : tx_state;
+ } else if (state & COMPANION_CAN_STATE_PASSIVE) {
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ rx_state = bec->rxerr > 127 ? new_state : rx_state;
+ tx_state = bec->txerr > 127 ? new_state : tx_state;
+ } else if (state & COMPANION_CAN_STATE_WARNING) {
+ new_state = CAN_STATE_ERROR_WARNING;
+ rx_state = bec->rxerr >= bec->txerr ? new_state : rx_state;
+ tx_state = bec->txerr >= bec->rxerr ? new_state : tx_state;
+ }
+
+ if (new_state != priv->can.state) {
+ can_change_state(dev, cf, tx_state, rx_state);
+
+ if (new_state == CAN_STATE_BUS_OFF)
+ can_bus_off(dev);
+ }
+}
+
+/**
+ * companion_can_handle_error() - handle CAN error
+ * @dev: address of the associated network device
+ * @cf: address of the CAN frame to store CAN error
+ * @code: the companion CAN error code
+ */
+static void companion_can_handle_error(struct net_device *dev,
+ struct can_frame *cf,
+ u8 code)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+
+ if (code & COMPANION_CAN_ERROR_RXOV) {
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+ dev->stats.rx_over_errors++;
+ dev->stats.rx_errors++;
+ }
+
+ if (code & (COMPANION_CAN_ERROR_STUFF |
+ COMPANION_CAN_ERROR_FORM |
+ COMPANION_CAN_ERROR_ACK |
+ COMPANION_CAN_ERROR_BIT1 |
+ COMPANION_CAN_ERROR_BIT0 |
+ COMPANION_CAN_ERROR_CRC))
+ {
+ cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+ if (code & COMPANION_CAN_ERROR_STUFF) {
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ dev->stats.rx_errors++;
+ }
+
+ if (code & COMPANION_CAN_ERROR_FORM) {
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ dev->stats.rx_errors++;
+ }
+
+ if (code & COMPANION_CAN_ERROR_ACK) {
+ cf->can_id |= CAN_ERR_ACK;
+ cf->data[3] = CAN_ERR_PROT_LOC_ACK;
+ dev->stats.tx_errors++;
+ }
+
+ if (code & COMPANION_CAN_ERROR_BIT1) {
+ cf->data[2] |= CAN_ERR_PROT_BIT1;
+ dev->stats.tx_errors++;
+ }
+
+ if (code & COMPANION_CAN_ERROR_BIT0) {
+ cf->data[2] |= CAN_ERR_PROT_BIT0;
+ dev->stats.tx_errors++;
+ }
+
+ if (code & COMPANION_CAN_ERROR_CRC) {
+ cf->data[2] |= CAN_ERR_PROT_BIT;
+ cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
+ dev->stats.rx_errors++;
+ }
+
+ priv->can.can_stats.bus_error++;
+ }
+}
+
+/**
+ * companion_can_poll_err() - poll CAN error packet from companion
+ * @dev: address of the associated network device
+ */
+static bool companion_can_poll_err(struct net_device *dev)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ struct can_berr_counter bec;
+ u8 state;
+ u8 code;
+ struct sk_buff *skb;
+ struct can_frame *cf;
+
+ if (companion_do_can_err(priv->parent,
+ priv->port,
+ &bec,
+ &state,
+ &code) != 0)
+ return false;
+
+ skb = alloc_can_err_skb(dev, &cf);
+ if (!skb) {
+ dev_err(&dev->dev, "cannot alloc err skb\n");
+ return false;
+ }
+
+ companion_can_handle_state(dev, cf, &bec, state);
+ companion_can_handle_error(dev, cf, code);
+
+ dev->stats.rx_bytes += cf->can_dlc;
+ dev->stats.rx_packets++;
+ netif_rx(skb);
+ return true;
+}
+
+/**
+ * companion_can_poll_data() - poll CAN data packet from companion
+ * @dev: address of the associated network device
+ */
+static bool companion_can_poll_data(struct net_device *dev)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ struct sk_buff *skb;
+ struct can_frame *cf;
+
+ skb = alloc_can_skb(dev, &cf);
+ if (!skb) {
+ dev_err(&dev->dev, "cannot alloc rx skb\n");
+ dev->stats.rx_dropped++;
+ return false;
+ }
+
+ if (companion_do_can_rx(priv->parent, priv->port, cf) != 0) {
+ dev_kfree_skb_any(skb);
+ return false;
+ }
+
+ dev->stats.rx_bytes += cf->can_dlc;
+ dev->stats.rx_packets++;
+ netif_rx(skb);
+ can_led_event(dev, CAN_LED_EVENT_RX);
+ return true;
+}
+
+/**
+ * companion_can_on_tx_done() - CAN tx done callback
+ * @data: address of user supplied callback data
+ * @prio: which CAN queue is done
+ * @lost_seq_sync: flag indicate lost sequence happened
+ * @success: flag indicate last send is succeed or not
+ */
+static void companion_can_on_tx_done(void *data,
+ u8 prio,
+ bool lost_seq_sync,
+ bool success)
+{
+ struct companion_can_priv *priv = data;
+ struct net_device *dev = priv->dev;
+ struct net_device_stats *stats = &dev->stats;
+ int err;
+
+ if (success) {
+ stats->tx_bytes += companion_can_get_echo_skb(priv, prio);
+ stats->tx_packets++;
+ can_led_event(dev, CAN_LED_EVENT_TX);
+ } else {
+ companion_can_free_echo_skb(priv, prio);
+ dev_err(&dev->dev, "on_tx_done(%d) failed\n", prio);
+ }
+
+ /*TODO: what else action should take in case lost sequence?*/
+ if (lost_seq_sync)
+ dev_err(&dev->dev, "txq[%d] lost sequence sync\n", prio);
+
+ err = companion_do_can_stop_tx_timer(priv->parent, priv->port, prio);
+ if (err)
+ dev_err(&dev->dev,
+ "stop txq[%d] tx timer failed: %d\n",
+ prio, err);
+
+ netif_wake_subqueue(dev, prio);
+}
+
+/**
+ * companion_can_on_rx_done() - CAN rx done callback
+ * @data: address of user supplied callback data
+ */
+static void companion_can_on_rx_done(void *data)
+{
+ struct companion_can_priv *priv = data;
+ while (companion_can_poll_data(priv->dev));
+}
+
+/**
+ * companion_can_on_error() - CAN error callback
+ * @data: address of user supplied callback data
+ */
+static void companion_can_on_error(void *data)
+{
+ struct companion_can_priv *priv = data;
+ while (companion_can_poll_err(priv->dev));
+}
+
+/**
+ * companion_can_on_tx_timeout() - CAN tx timeout callback
+ * @data: address of user supplied callback data
+ * @prio: which CAN queue tx timed out
+ */
+static void companion_can_on_tx_timeout(void *data, u8 prio)
+{
+ struct companion_can_priv *priv = data;
+ bool lost_txq_sync = false;
+ int err;
+
+ err = companion_do_get_can_txq_status(priv->parent,
+ priv->port,
+ prio,
+ &lost_txq_sync);
+ if (err) {
+ dev_err(&priv->dev->dev,
+ "get can txq[%d] status failed: %d\n", prio, err);
+
+ if (err != -EINVAL)
+ companion_do_can_start_tx_timer(priv->parent,
+ priv->port,
+ prio);
+ return;
+ }
+
+ if (lost_txq_sync) {
+ dev_err(&priv->dev->dev,
+ "txq[%d] out of sync, restart data flow\n", prio);
+ companion_can_free_echo_skb(priv, prio);
+ netif_wake_subqueue(priv->dev, prio);
+ } else {
+ dev_err(&priv->dev->dev,
+ "txq[%d] is sync'd, but no ack, wait again\n", prio);
+ companion_do_can_start_tx_timer(priv->parent, priv->port, prio);
+ }
+}
+
+static struct companion_can_ops companion_can_can_ops = {
+ .on_tx_done = companion_can_on_tx_done,
+ .on_rx_done = companion_can_on_rx_done,
+ .on_error = companion_can_on_error,
+ .on_tx_timeout = companion_can_on_tx_timeout,
+};
+
+/**
+ * companion_can_open() - ndo_open callback
+ * @dev: address of the associated network device
+ */
+static int companion_can_open(struct net_device *dev)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ bool has_space = false;
+ int err, i;
+
+ err = companion_can_ops_register(priv->parent,
+ priv->port,
+ &companion_can_can_ops,
+ priv);
+ if (err) {
+ dev_err(&dev->dev,
+ "companion_can_ops_register() failed: %d\n", err);
+ goto out;
+ }
+
+ err = companion_can_set_mode(dev, CAN_MODE_START);
+ if (err) {
+ dev_err(&dev->dev,
+ "companion_can_set_mode() failed: %d\n", err);
+ goto out_register;
+ }
+
+ err = companion_do_get_can_txq_status_all(priv->parent, priv->port);
+ if (err) {
+ dev_err(&dev->dev,
+ "companion_do_get_can_txq_status_all() failed: %d\n",
+ err);
+ goto out_mode;
+ }
+
+ err = open_candev(dev);
+ if (err) {
+ dev_err(&dev->dev, "open_candev() failed: %d\n", err);
+ goto out_mode;
+ }
+
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ can_led_event(dev, CAN_LED_EVENT_OPEN);
+
+ /*TODO: start all here or start depends on queue space?*/
+ for (i = 0; i < NUM_TX_QUEUES; ++i) {
+ err = companion_do_can_txq_has_space(priv->parent,
+ priv->port,
+ i,
+ &has_space);
+
+ if (!err && has_space) {
+ netif_tx_start_queue(netdev_get_tx_queue(dev, i));
+ } else {
+ netif_tx_stop_queue(netdev_get_tx_queue(dev, i));
+ dev_err(&dev->dev, "txq[%d] is not started\n", i);
+ }
+ }
+
+ return 0;
+
+out_mode:
+ companion_can_set_mode(dev, CAN_MODE_STOP);
+out_register:
+ companion_can_ops_unregister(priv->parent, priv->port);
+out:
+ return err;
+}
+
+/**
+ * companion_can_release() - ndo_close callback
+ * @dev: address of the associated network device
+ */
+static int companion_can_release(struct net_device *dev)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ int result;
+
+ netif_tx_stop_all_queues(dev);
+ can_led_event(dev, CAN_LED_EVENT_STOP);
+ priv->can.state = CAN_STATE_STOPPED;
+ close_candev(dev);
+ result = companion_can_set_mode(dev, CAN_MODE_STOP);
+ companion_can_ops_unregister(priv->parent, priv->port);
+ return result;
+}
+
+/**
+ * companion_can_start_xmit() - ndo_start_xmit callback
+ * @skb: address of the packet to send
+ * @dev: address of the associated network device
+ */
+static int companion_can_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ struct can_frame *cf = (struct can_frame*)skb->data;
+ u16 prio = skb_get_queue_mapping(skb);
+ bool is_full = false;
+ int err;
+
+ if (can_dropped_invalid_skb(dev, skb)) {
+ dev_err(&dev->dev, "dropped invalid skb on txq[%d]\n", prio);
+ return NETDEV_TX_OK;
+ }
+
+ err = companion_do_can_tx(priv->parent, priv->port, prio, cf);
+ if (err) {
+ dev_err(&dev->dev, "dropped packet on txq[%d]\n", prio);
+ dev_kfree_skb_any(skb);
+ dev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+ }
+
+ err = companion_do_can_txq_is_full(priv->parent,
+ priv->port,
+ prio,
+ &is_full);
+ if (!err && is_full) {
+ netif_stop_subqueue(dev, prio);
+ err = companion_do_can_start_tx_timer(priv->parent,
+ priv->port,
+ prio);
+ if (err)
+ dev_err(&dev->dev,
+ "start txq[%d] tx timer failed: %d\n",
+ prio, err);
+ }
+
+ companion_can_put_echo_skb(priv, prio, skb);
+ return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops companion_can_netdev_ops = {
+ .ndo_open = companion_can_open,
+ .ndo_stop = companion_can_release,
+ .ndo_start_xmit = companion_can_start_xmit,
+};
+
+static const struct of_device_id companion_can_of_match[] = {
+ { .compatible = DRIVER_NAME, .data = NULL, },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, companion_can_of_match);
+
+static const struct can_bittiming_const companion_can_bittiming_const = {
+ .name = "bosch,companion",
+ .tseg1_min = 2,
+ .tseg1_max = 16,
+ .tseg2_min = 1,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 1024,
+ .brp_inc = 1,
+};
+
+/**
+ * companion_can_probe() - probe callback
+ * @pdev: address of the platform device
+ */
+static int companion_can_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct net_device *dev;
+ struct companion_can_priv *priv;
+ u32 port, freq;
+ int err;
+
+ if (!node) {
+ dev_err(&pdev->dev, "no device tree data\n");
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32(node, "port", &port)) {
+ dev_err(&pdev->dev, "no port property\n");
+ return -ENODEV;
+ }
+
+ if ((port != 0) && (port != 1)) {
+ dev_err(&pdev->dev,
+ "invalid port %d, valid range is [0,1]\n", port);
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "clock-frequency", &freq)) {
+ dev_err(&pdev->dev, "no clock-frequency property\n");
+ return -ENODEV;
+ }
+
+ if (!pdev->dev.parent) {
+ dev_err(&pdev->dev, "no parent device\n");
+ return -ENODEV;
+ }
+
+ dev = alloc_candev_mqs(sizeof(*priv),
+ TX_ECHO_SKB_MAX,
+ NUM_TX_QUEUES,
+ NUM_RX_QUEUES);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->netdev_ops = &companion_can_netdev_ops;
+ dev->flags |= IFF_ECHO;
+ dev->real_num_tx_queues = NUM_TX_QUEUES;
+
+ priv = netdev_priv(dev);
+ priv->can.clock.freq = freq;
+ priv->can.bittiming_const = &companion_can_bittiming_const;
+ priv->can.do_set_mode = companion_can_set_mode;
+ priv->can.do_get_berr_counter = companion_can_get_berr_counter;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY |
+ CAN_CTRLMODE_BERR_REPORTING;
+ priv->parent = pdev->dev.parent;
+ priv->dev = dev;
+ priv->port = port;
+
+ platform_set_drvdata(pdev, dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ err = register_candev(dev);
+ if (err) {
+ dev_err(&pdev->dev, "register_candev() failed: %d\n", err);
+ free_candev(dev);
+ return err;
+ }
+
+ devm_can_led_init(dev);
+ return 0;
+}
+
+/**
+ * companion_can_remove() - remove callback
+ * @pdev: address of the platform device
+ */
+static int companion_can_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+
+ unregister_candev(dev);
+ free_candev(dev);
+ return 0;
+}
+
+static struct platform_driver companion_can_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(companion_can_of_match),
+ },
+ .probe = companion_can_probe,
+ .remove = companion_can_remove,
+};
+module_platform_driver(companion_can_driver);
+
+MODULE_AUTHOR("Zhu Yi <[email protected]>");
+MODULE_DESCRIPTION("Companion upper level can network device");
+MODULE_LICENSE("GPL v2");
--
2.7.4


Subject: [PATCH 1/5] can: enable multi-queue for SocketCAN devices

From: Zhu Yi <[email protected]>

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 <[email protected]>
Signed-off-by: Mark Jonas <[email protected]>
Reviewed-by: Heiko Schocher <[email protected]>
---
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


Subject: [PATCH 5/5] spi,can,char: add companion DT binding documentation

From: Zhu Yi <[email protected]>

Signed-off-by: Zhu Yi <[email protected]>
Signed-off-by: Mark Jonas <[email protected]>
---
.../devicetree/bindings/spi/bosch,companion.txt | 82 ++++++++++++++++++++++
1 file changed, 82 insertions(+)
create mode 100644 Documentation/devicetree/bindings/spi/bosch,companion.txt

diff --git a/Documentation/devicetree/bindings/spi/bosch,companion.txt b/Documentation/devicetree/bindings/spi/bosch,companion.txt
new file mode 100644
index 0000000..5ded325
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/bosch,companion.txt
@@ -0,0 +1,82 @@
+Bosch Companion SPI slave device
+
+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-----+
+
+Required properties:
+
+- compatible : must be "bosch,companion-spi"
+- interrupt-parent : the phandle of the GPIO controller
+- interrupts : (GPIO) interrupt to which 'request-gpios' is
+ connected to
+- request-gpios : GPIO pin to request SPI master to receive data
+- busy-gpios : GPIO pin to indicate SPI slave is busy
+- cs-gpios : GPIO pin to select SPI slave
+
+Optional properties:
+
+The controller supports at most 2 CAN and 1 char device subnodes. When
+optionally specify the subnodes, the following properties are required:
+
+- CAN subnode
+ - compatible : must be "bosch,companion-can"
+ - clock-frequency: CAN device clock in Hz
+ - port : must be 0 or 1
+
+- Char device subnode
+ - compatible : must be "bosch,companion-char"
+
+Example:
+
+&ecspi1 {
+ companion-spi@0 {
+ compatible = "bosch,companion-spi";
+ interrupt-parent = <&gpio1>;
+ interrupts = <26 IRQ_TYPE_EDGE_FALLING>;
+ request-gpios = <&gpio1 26 GPIO_ACTIVE_LOW>;
+ busy-gpios = <&gpio1 27 GPIO_ACTIVE_LOW>;
+ cs-gpios = <&gpio4 9 GPIO_ACTIVE_LOW>;
+
+ companion-can0 {
+ compatible = "bosch,companion-can";
+ clock-frequency = <28000000>;
+ port = <0>;
+ };
+
+ companion-can1 {
+ compatible = "bosch,companion-can";
+ clock-frequency = <28000000>;
+ port = <1>;
+ };
+
+ companion-char {
+ compatible = "bosch,companion-char";
+ };
+ };
+};
--
2.7.4


Subject: [PATCH 3/5] char: implement companion-char driver

From: Zhu Yi <[email protected]>

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 <[email protected]>
Signed-off-by: Mark Jonas <[email protected]>
---
drivers/char/Kconfig | 7 +
drivers/char/Makefile | 2 +
drivers/char/companion-char.c | 367 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 376 insertions(+)
create mode 100644 drivers/char/companion-char.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..dfe4fc1 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -60,3 +60,5 @@ 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
diff --git a/drivers/char/companion-char.c b/drivers/char/companion-char.c
new file mode 100644
index 0000000..3c198f2
--- /dev/null
+++ b/drivers/char/companion-char.c
@@ -0,0 +1,367 @@
+// 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 "bosch,companion-char"
+
+/*TODO: get from protocol.h*/
+#define COMPANION_PACKET_SIZE 16
+
+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 status;
+
+ 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;
+ }
+
+ status = companion_do_io_rx(priv->parent, buf, count);
+ mutex_unlock(&minor->readlock);
+ return status;
+}
+
+/**
+ * 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 status;
+
+ 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;
+ }
+
+ status = companion_do_io_tx(priv->parent, buf, count);
+ mutex_unlock(&minor->writelock);
+ return status;
+}
+
+/**
+ * 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 = DRIVER_NAME, .data = NULL, },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, companion_char_of_match);
+
+static struct platform_driver companion_char_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .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) {
+ printk("companion:alloc_chrdev_region() failed: %d\n", err);
+ class_destroy(companion_char_class);
+ return err;
+ }
+
+ err = platform_driver_register(&companion_char_driver);
+ if (err) {
+ printk("companion:platform_driver_register() failed: %d\n",
+ 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 <[email protected]>");
+MODULE_DESCRIPTION("Companion upper level character device");
+MODULE_LICENSE("GPL v2");
--
2.7.4


Subject: [PATCH 2/5] spi: implement companion-spi driver

From: Zhu Yi <[email protected]>

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 <[email protected]>
Signed-off-by: Mark Jonas <[email protected]>
---
drivers/spi/Kconfig | 2 +
drivers/spi/Makefile | 2 +
drivers/spi/companion/Kconfig | 5 +
drivers/spi/companion/Makefile | 2 +
drivers/spi/companion/core.c | 1189 ++++++++++++++++++++++++++++++
drivers/spi/companion/protocol-manager.c | 1035 ++++++++++++++++++++++++++
drivers/spi/companion/protocol-manager.h | 348 +++++++++
drivers/spi/companion/protocol.h | 273 +++++++
drivers/spi/companion/queue-manager.c | 146 ++++
drivers/spi/companion/queue-manager.h | 245 ++++++
include/linux/companion.h | 258 +++++++
11 files changed, 3505 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..435b215
--- /dev/null
+++ b/drivers/spi/companion/core.c
@@ -0,0 +1,1189 @@
+// 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.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/kfifo.h>
+
+#include "protocol-manager.h"
+
+#define DRIVER_NAME "bosch,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
+ * @request_gpios: gpio line connect to request signal
+ * @request_gpios_assert: polarity of request signal
+ * @busy_gpios: gpio line connect to busy signal
+ * @busy_gpios_assert: polarity of busy signal
+ * @cs_gpios: gpio line connect to cs signal
+ * @cs_gpios_assert: polarity of 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;
+
+ u32 request_gpios;
+ u32 request_gpios_assert;
+ u32 busy_gpios;
+ u32 busy_gpios_assert;
+ u32 cs_gpios;
+ u32 cs_gpios_assert;
+
+ 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 error;
+ struct companion_packet p;
+
+ /*TODO: support mutiple packets in one write in future*/
+ if (copy_from_user(p.data, buf, sizeof(p)) == 0) {
+ if (is_can_type(&p))
+ return -EINVAL;
+ } else {
+ dev_info(parent, "copy from user not succeed in one call\n");
+ }
+
+ error = qm_io_txq_in(&priv->pm.qm, buf, count, &copied);
+ if (!error) {
+ wake_up_interruptible(&priv->wait);
+ priv->pm.stats.io_tx++;
+ return copied;
+ } else {
+ priv->pm.stats.io_tx_overflows++;
+ }
+ return error;
+}
+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 error;
+
+ error = qm_io_rxq_out(&priv->pm.qm, buf, count, &copied);
+ return error ? error : 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 = pm_can_data_tx(&priv->pm, port, prio, cf);
+ if (!err)
+ wake_up_interruptible(&priv->wait);
+ return err;
+}
+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 = pm_can_set_bittiming(&priv->pm, port, bittiming);
+ if (!err) {
+ wake_up_interruptible(&priv->wait);
+ err = pm_wait_for_response(&priv->pm, port, bcp_can_bittiming);
+ }
+ return err;
+}
+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 = pm_can_set_mode(&priv->pm, port, mode);
+ if (!err) {
+ wake_up_interruptible(&priv->wait);
+ err = pm_wait_for_response(&priv->pm, port, bcp_can_mode);
+ }
+ return err;
+}
+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 = pm_can_set_ctrlmode(&priv->pm, port, ctrl);
+ if (!err) {
+ wake_up_interruptible(&priv->wait);
+ err = pm_wait_for_response(&priv->pm, port, bcp_can_mode);
+ }
+ return err;
+}
+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 = pm_can_get_status(&priv->pm, port);
+ if (!err) {
+ wake_up_interruptible(&priv->wait);
+ err = pm_wait_for_response(&priv->pm, port, bcp_can_status);
+ if (!err) {
+ bec->rxerr = priv->pm.rx_err[port];
+ bec->txerr = priv->pm.tx_err[port];
+ }
+ }
+ return err;
+}
+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) {
+ wake_up_interruptible(&priv->wait);
+ err = pm_wait_for_response(pm, port, bcp_can_txq_status);
+ if (!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 err;
+}
+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) {
+ wake_up_interruptible(&priv->wait);
+ err = pm_wait_for_response(&priv->pm, port, bcp_can_txq_status);
+ if (!err)
+ memcpy(priv->pm.local_txq[port],
+ priv->pm.remote_txq[port],
+ BCP_CAN_PRIOS);
+ }
+ return err;
+}
+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);
+
+/**
+ * show_dump_packets() - 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 show_dump_packets(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);
+}
+
+/**
+ * store_dump_packets() - 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 store_dump_packets(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 ret, value;
+
+ ret = sscanf(buf, "%d", &value);
+ if (ret != 1) {
+ dev_err(&spi->dev, "input invalid value: %s\n", buf);
+ return -EINVAL;
+ }
+
+ priv->dump_packets = (value != 0);
+ return count;
+}
+static DEVICE_ATTR(dump_packets, S_IRUGO | S_IWUSR,
+ show_dump_packets, store_dump_packets);
+
+/**
+ * show_overflows() - 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 show_overflows(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(overflows, S_IRUGO, show_overflows, NULL);
+
+/**
+ * show_traffic() - 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 show_traffic(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(traffic, S_IRUGO, show_traffic, NULL);
+
+/**
+ * show_can_space() - 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 show_can_space(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(can_space, S_IRUGO, show_can_space, NULL);
+
+/**
+ * show_busy() - 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 show_busy(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(busy, S_IRUGO, show_busy, NULL);
+
+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)
+{
+ int value = gpio_get_value(priv->request_gpios);
+ return value == priv->request_gpios_assert;
+}
+
+/**
+ * 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)
+{
+ int value = gpio_get_value(priv->busy_gpios);
+ return value == priv->busy_gpios_assert;
+}
+
+/**
+ * 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)
+{
+ int value = gpio_get_value(priv->busy_gpios);
+ return value != priv->busy_gpios_assert;
+}
+
+/**
+ * slave_select() - select spi slave
+ * @priv: address of companion-spi private data
+ */
+static inline void slave_select(struct companion_spi_priv *priv)
+{
+ gpio_set_value(priv->cs_gpios, priv->cs_gpios_assert);
+}
+
+/**
+ * slave_deselect() - deselect spi slave
+ * @priv: address of companion-spi private data
+ */
+static inline void slave_deselect(struct companion_spi_priv *priv)
+{
+ gpio_set_value(priv->cs_gpios, !priv->cs_gpios_assert);
+}
+
+/**
+ * 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)
+{
+ /*
+ * as short as possible wait while busy polling which shall
+ * succeed most of the times
+ */
+ unsigned int count = READY_POLL_US / READY_POLL_US_GRAN;
+ while (count--) {
+ if (slave_is_not_busy(priv))
+ return 0;
+
+ udelay(READY_POLL_US_GRAN);
+ }
+
+ /*
+ * 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;
+ while (count--) {
+ 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);
+ }
+
+ 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)
+{
+ /*
+ * as short as possible wait until busy polling which shall
+ * succeed most of the times
+ */
+ unsigned int count = READY_POLL_US / READY_POLL_US_GRAN;
+ while (count--) {
+ if (slave_is_busy(priv))
+ return 0;
+
+ udelay(READY_POLL_US_GRAN);
+ }
+
+ /*
+ * 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;
+ while (count--) {
+ 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);
+ }
+
+ 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 status;
+
+ if (priv->dump_packets) {
+ p = (const struct companion_packet*)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);
+
+ status = companion_spi_wait_while_busy(priv);
+
+ slave_select(priv);
+
+ if (status != 0) {
+ 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 */
+ status = companion_spi_wait_while_busy(priv);
+ if (status != 0) {
+ priv->stats.force_started_failure++;
+ dev_err(&priv->spi->dev,
+ "force started failed, continuing (%u)\n",
+ priv->stats.force_started_failure);
+ }
+ }
+
+ status = companion_spi_wait_until_busy(priv);
+ if (status != 0) {
+ priv->stats.ready_failure++;
+ dev_err(&priv->spi->dev,
+ "started transfer in case not ready (%u)\n",
+ priv->stats.ready_failure);
+ }
+
+ if (spi_sync(priv->spi, message) != 0)
+ 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 = (const struct companion_packet*)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;
+
+ for (;;) {
+ 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);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id companion_spi_of_match[] = {
+ { .compatible = DRIVER_NAME, .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 gpio;
+ enum of_gpio_flags flags;
+
+ if (!np) {
+ dev_err(dev, "no device tree data\n");
+ return -EINVAL;
+ }
+
+ gpio = of_get_named_gpio_flags(np, "request-gpios", 0, &flags);
+ if (!gpio_is_valid(gpio)) {
+ dev_err(dev, "invalid 'request-gpios' supplied\n");
+ return -EINVAL;
+ }
+ priv->request_gpios = gpio;
+ priv->request_gpios_assert = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1;
+
+ gpio = of_get_named_gpio_flags(np, "busy-gpios", 0, &flags);
+ if (!gpio_is_valid(gpio)) {
+ dev_err(dev, "invalid 'busy-gpios' supplied\n");
+ return -EINVAL;
+ }
+ priv->busy_gpios = gpio;
+ priv->busy_gpios_assert = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1;
+
+ gpio = of_get_named_gpio_flags(np, "cs-gpios", 0, &flags);
+ if (!gpio_is_valid(gpio)) {
+ dev_err(dev, "invalid 'cs-gpios' supplied\n");
+ return -EINVAL;
+ }
+ priv->cs_gpios = gpio;
+ priv->cs_gpios_assert = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1;
+
+ 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;
+
+ err = devm_gpio_request_one(&spi->dev,
+ priv->request_gpios,
+ GPIOF_IN,
+ DRIVER_NAME);
+ if (err) {
+ dev_err(&spi->dev, "request 'request-gpios' failed: %d\n", err);
+ return err;
+ }
+
+ err = devm_gpio_request_one(&spi->dev,
+ priv->busy_gpios,
+ GPIOF_IN,
+ DRIVER_NAME);
+ if (err) {
+ dev_err(&spi->dev, "request 'busy-gpios' failed: %d\n", err);
+ return err;
+ }
+
+ err = devm_gpio_request_one(&spi->dev,
+ priv->cs_gpios,
+ GPIOF_OUT_INIT_HIGH,
+ DRIVER_NAME);
+ if (err) {
+ dev_err(&spi->dev, "request 'cs-gpios'failed: %d\n", 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,
+ gpio_to_irq(priv->request_gpios),
+ 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,
+ .owner = THIS_MODULE,
+ .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 <[email protected]>");
+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..3a9dc40
--- /dev/null
+++ b/drivers/spi/companion/protocol-manager.c
@@ -0,0 +1,1035 @@
+// 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,
+ BCP_STATUS_SUCCESS == status);
+ up_read(&pm->can_lock[port]);
+
+ if (BCP_STATUS_SUCCESS == status)
+ 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 (BCP_STATUS_SUCCESS == status) {
+ 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, "Unkown 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;
+
+ memset(&p, 0, sizeof(p));
+ p.type = BCP_CAN_MODE;
+ p.port = port + 1;
+
+ if (ctrl & CAN_CTRLMODE_LISTENONLY)
+ p.mode = BCP_CAN_MODE_LISTEN;
+ else
+ 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_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..09d32ad
--- /dev/null
+++ b/drivers/spi/companion/protocol-manager.h
@@ -0,0 +1,348 @@
+// 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
+ *
+ * TODO: add more statistics fields and export to sysfs
+ */
+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
+ *
+ * TODO: re-think the data structure for handle CAN response
+ */
+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..f426cf0
--- /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..a50646c
--- /dev/null
+++ b/drivers/spi/companion/queue-manager.c
@@ -0,0 +1,146 @@
+// 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..3bd77d7
--- /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
+ * adminstrative 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..8f9c876
--- /dev/null
+++ b/include/linux/companion.h
@@ -0,0 +1,258 @@
+/*
+ * 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>
+
+/**
+ * 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 0x01u
+#define COMPANION_CAN_STATE_PASSIVE 0x02u
+#define COMPANION_CAN_STATE_BUS_OFF 0x04u
+#define COMPANION_CAN_ERROR_STUFF 0x01u
+#define COMPANION_CAN_ERROR_FORM 0x02u
+#define COMPANION_CAN_ERROR_ACK 0x04u
+#define COMPANION_CAN_ERROR_BIT1 0x08u
+#define COMPANION_CAN_ERROR_BIT0 0x10u
+#define COMPANION_CAN_ERROR_CRC 0x20u
+#define COMPANION_CAN_ERROR_RXOV 0x80u
+
+/**
+ * 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


2018-06-06 19:13:51

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH 0/5] can: enable multi-queue for SocketCAN devices

On Tue, Jun 5, 2018 at 9:43 PM, Mark Jonas <[email protected]> wrote:
> 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.

Can remoteproc API be utilized here?

>
> 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 | 2 +
> drivers/char/companion-char.c | 367 ++++++
> drivers/net/can/Kconfig | 8 +
> drivers/net/can/Makefile | 1 +
> drivers/net/can/companion-can.c | 694 ++++++++++++
> 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 | 1189 ++++++++++++++++++++
> drivers/spi/companion/protocol-manager.c | 1035 +++++++++++++++++
> drivers/spi/companion/protocol-manager.h | 348 ++++++
> drivers/spi/companion/protocol.h | 273 +++++
> drivers/spi/companion/queue-manager.c | 146 +++
> drivers/spi/companion/queue-manager.h | 245 ++++
> include/linux/can/dev.h | 7 +-
> include/linux/companion.h | 258 +++++
> 20 files changed, 4677 insertions(+), 4 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/spi/bosch,companion.txt
> create mode 100644 drivers/char/companion-char.c
> create mode 100644 drivers/net/can/companion-can.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
>



--
With Best Regards,
Andy Shevchenko

2018-06-06 19:28:47

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH 2/5] spi: implement companion-spi driver

On Tue, Jun 5, 2018 at 9:43 PM, Mark Jonas <[email protected]> wrote:

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

> +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 error;
> + struct companion_packet p;
> +

> + /*TODO: support mutiple packets in one write in future*/

Hmm... Either fix this, or remove comment?

> + if (copy_from_user(p.data, buf, sizeof(p)) == 0) {
> + if (is_can_type(&p))
> + return -EINVAL;
> + } else {
> + dev_info(parent, "copy from user not succeed in one call\n");

Shouldn't it return immediately?

> + }

> +
> + error = qm_io_txq_in(&priv->pm.qm, buf, count, &copied);

...what the point to call if we got garbage from user space?

> + if (!error) {

The usual pattern is to check for errors first.

> + wake_up_interruptible(&priv->wait);
> + priv->pm.stats.io_tx++;
> + return copied;
> + } else {
> + priv->pm.stats.io_tx_overflows++;
> + }
> + return error;
> +}

> + ... qm_io_rxq_out(&priv->pm.qm, buf, count, &copied);

> + ... pm_can_data_tx(&priv->pm, port, prio, cf);

Oh, oh, oh.

These namespaces are too generic, moreover pm is kinda occupied by
power management. You bring a lot of confusion here, I think.

> + err = pm_can_get_txq_status(pm, port);
> + if (!err) {

if (err)
return err;

> + }
> + return err;


> + int ret, value;
> +
> + ret = sscanf(buf, "%d", &value);
> + if (ret != 1) {

> + }

You have to be consistent in your code.

I've already noticed

err
error

and now

ret

Choose one and stick with it.

Also check your entire patch series' code for consistency.

> +static DEVICE_ATTR(dump_packets, S_IRUGO | S_IWUSR,
> + show_dump_packets, store_dump_packets);

We have helpers, like DEVICE_ATTR_RW().

> + 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]);

Please, avoid leading '\n'.

> + gpio_set_value(priv->cs_gpios, priv->cs_gpios_assert);

> + gpio_set_value(priv->cs_gpios, !priv->cs_gpios_assert);

Can you switch to GPIO descriptor API?

> + unsigned int count = READY_POLL_US / READY_POLL_US_GRAN;
> + while (count--) {

For counting like this better to have
do {
} while (--count);

The rationale, reader at first glance will know that the loop will
iterate at least once.

> + if (slave_is_not_busy(priv))
> + return 0;
> +

> + udelay(READY_POLL_US_GRAN);

Should it be atomic?
Can it use read_poll_* type of helpers instead?

> + }

Above comments applicable to entire code you have.

> +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);
> +}

This entire function should be replaced by one of the helpers from
include/linux/byteorder/generic.h

I guess cpu_to_be32_array() is a right one.

> +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);
> +}

Ditto.

Recommendation: check your code against existing helpers.

> + p = (const struct companion_packet*)transfer->tx_buf;

> + companion_spi_cpu_to_be32((char*)transfer->tx_buf);

If tx_buf is type of void * all these castings are redundant.

Also looking at the function, did you consider to use whatever SPI
core provides, like CS handling, messages handling and so on?

> +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;

Redundant.

> + transfer.bits_per_word = 32;
> +

> + for (;;) {

Infinite loops are evil in most of the cases.
I see here

do {
} while (kthread_should_stop());

> + 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);
> + }
> +
> + return 0;
> +}

> +static const struct of_device_id companion_spi_of_match[] = {
> + { .compatible = DRIVER_NAME, .data = NULL, },
> + { /* sentinel */ },

terminators better without commas.

> +};

> +static struct spi_driver companion_spi_driver = {
> + .driver = {
> + .name = DRIVER_NAME,

> + .owner = THIS_MODULE,

This is redundant, macro you are using below sets that.

> + .of_match_table = of_match_ptr(companion_spi_of_match),
> + },
> + .probe = companion_spi_probe,
> + .remove = companion_spi_remove,
> +};
> +module_spi_driver(companion_spi_driver);

> +static void io_process(struct companion_protocol_manager *pm,
> + const struct companion_packet *p)

Something wrong with indentation in this file.

> +#define CHECK_SIZE(x) BUILD_BUG_ON(sizeof(struct companion_packet) != \
> + sizeof(x))

Better to split like
_SIZE(x) \
BUILD_BUG_ON()

> +void pm_init(struct companion_protocol_manager *pm)

Unfortunately, horrible name for the function.
Namespace is kinda occupied, name itself way too generic.

> + if (ctrl & CAN_CTRLMODE_LISTENONLY)
> + p.mode = BCP_CAN_MODE_LISTEN;
> + else
> + return -EOPNOTSUPP;

if (!(cond))
return -ERRNO;

?

> + * 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.

Do you still need this text?

> + * TODO: add more statistics fields and export to sysfs

Do it or remove the comment?

> + * TODO: re-think the data structure for handle CAN response

Ditto.

> +/**
> + * BCP status field definitions
> + */
> +#define BCP_STATUS_SUCCESS 0x00u
> +#define BCP_STATUS_UNKNOWN 0x01u
> +#define BCP_STATUS_OTHER 0x02u

BIT() ?


> +struct companion_packet {
> + __u8 data[BCP_PACKET_SIZE];
> +};

Is it going from / to user space? Otherwise why __ kind of type?

> +#define CAN_MAX_IN_A_ROW 8
> +
> +
> +

Too many blank lines.

> +int companion_do_can_err(struct device *parent,
> + u8 port,
> + struct can_berr_counter *bec,
> + u8 *state,
> + u8 *code);

+ blank line here.

> +#define COMPANION_CAN_STATE_WARNING 0x01u
> +#define COMPANION_CAN_STATE_PASSIVE 0x02u
> +#define COMPANION_CAN_STATE_BUS_OFF 0x04u
> +#define COMPANION_CAN_ERROR_STUFF 0x01u
> +#define COMPANION_CAN_ERROR_FORM 0x02u
> +#define COMPANION_CAN_ERROR_ACK 0x04u
> +#define COMPANION_CAN_ERROR_BIT1 0x08u
> +#define COMPANION_CAN_ERROR_BIT0 0x10u
> +#define COMPANION_CAN_ERROR_CRC 0x20u
> +#define COMPANION_CAN_ERROR_RXOV 0x80u

BIT() ?

--
With Best Regards,
Andy Shevchenko

2018-06-07 07:56:39

by Oliver Hartkopp

[permalink] [raw]
Subject: Re: [PATCH 0/5] can: enable multi-queue for SocketCAN devices



On 06/05/2018 08:43 PM, Mark Jonas wrote:
> 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 | 2 +
> drivers/char/companion-char.c | 367 ++++++
> drivers/net/can/Kconfig | 8 +
> drivers/net/can/Makefile | 1 +
> drivers/net/can/companion-can.c | 694 ++++++++++++

Please place the companion driver in

drivers/net/can/spi/companion.c

It also makes more sense in the Kconfig structure.

Probably this naming scheme also makes sense for

linux/drivers/char/spi/companion.c

then ...

If not it should be named at least

drivers/char/companion-spi.c

or

drivers/char/spi-companion.c

BR Oliver

> 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 | 1189 ++++++++++++++++++++
> drivers/spi/companion/protocol-manager.c | 1035 +++++++++++++++++
> drivers/spi/companion/protocol-manager.h | 348 ++++++
> drivers/spi/companion/protocol.h | 273 +++++
> drivers/spi/companion/queue-manager.c | 146 +++
> drivers/spi/companion/queue-manager.h | 245 ++++
> include/linux/can/dev.h | 7 +-
> include/linux/companion.h | 258 +++++
> 20 files changed, 4677 insertions(+), 4 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/spi/bosch,companion.txt
> create mode 100644 drivers/char/companion-char.c
> create mode 100644 drivers/net/can/companion-can.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
>

Subject: Re: [PATCH 0/5] can: enable multi-queue for SocketCAN devices

Hi Oliver,

> > 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.

> > .../devicetree/bindings/spi/bosch,companion.txt | 82 ++
> > drivers/char/Kconfig | 7 +
> > drivers/char/Makefile | 2 +
> > drivers/char/companion-char.c | 367 ++++++
> > drivers/net/can/Kconfig | 8 +
> > drivers/net/can/Makefile | 1 +
> > drivers/net/can/companion-can.c | 694 ++++++++++++
>
> Please place the companion driver in
>
> drivers/net/can/spi/companion.c
>
> It also makes more sense in the Kconfig structure.
>
> Probably this naming scheme also makes sense for
>
> linux/drivers/char/spi/companion.c
>
> then ...
>
> If not it should be named at least
>
> drivers/char/companion-spi.c
>
> or
>
> drivers/char/spi-companion.c

We intentionally left out the spi in the driver path / name because
only the drivers/spi/companion/* driver knows that that it is connected
to SPI. The others (drivers/net/can/companion-can.c and
drivers/char/companion-char.c) only know the API. This could also be
supplied by a driver which talks to the Companion via a different
interface. Actually, we started with a UART connection but switched to
SPI due to latency issues.

Should we still change it?

> > 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 | 1189 ++++++++++++++++++++
> > drivers/spi/companion/protocol-manager.c | 1035 +++++++++++++++++
> > drivers/spi/companion/protocol-manager.h | 348 ++++++
> > drivers/spi/companion/protocol.h | 273 +++++
> > drivers/spi/companion/queue-manager.c | 146 +++
> > drivers/spi/companion/queue-manager.h | 245 ++++
> > include/linux/can/dev.h | 7 +-
> > include/linux/companion.h | 258 +++++
> > 20 files changed, 4677 insertions(+), 4 deletions(-)
> > create mode 100644
> Documentation/devicetree/bindings/spi/bosch,companion.txt
> > create mode 100644 drivers/char/companion-char.c
> > create mode 100644 drivers/net/can/companion-can.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

Greetings,
Mark

Building Technologies, Panel Software Fire (BT-FIR/ENG1)
Bosch Sicherheitssysteme GmbH | Postfach 11 11 | 85626 Grasbrunn | GERMANY | http://www.boschsecurity.com

Sitz: Stuttgart, Registergericht: Amtsgericht Stuttgart HRB 23118
Aufsichtsratsvorsitzender: Stefan Hartung; Geschäftsführung: Gert van Iperen, Andreas Bartz, Thomas Quante, Bernhard Schuster

2018-06-07 10:33:08

by Oliver Hartkopp

[permalink] [raw]
Subject: Re: [PATCH 0/5] can: enable multi-queue for SocketCAN devices



On 06/07/2018 10:06 AM, Jonas Mark (BT-FIR/ENG1) wrote:
> Hi Oliver,
>
>>> 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.
>
>>> .../devicetree/bindings/spi/bosch,companion.txt | 82 ++
>>> drivers/char/Kconfig | 7 +
>>> drivers/char/Makefile | 2 +
>>> drivers/char/companion-char.c | 367 ++++++
>>> drivers/net/can/Kconfig | 8 +
>>> drivers/net/can/Makefile | 1 +
>>> drivers/net/can/companion-can.c | 694 ++++++++++++
>>
>> Please place the companion driver in
>>
>> drivers/net/can/spi/companion.c
>>
>> It also makes more sense in the Kconfig structure.
>>
>> Probably this naming scheme also makes sense for
>>
>> linux/drivers/char/spi/companion.c
>>
>> then ...
>>
>> If not it should be named at least
>>
>> drivers/char/companion-spi.c
>>
>> or
>>
>> drivers/char/spi-companion.c
>
> We intentionally left out the spi in the driver path / name because
> only the drivers/spi/companion/* driver knows that that it is connected
> to SPI. The others (drivers/net/can/companion-can.c and
> drivers/char/companion-char.c) only know the API. This could also be
> supplied by a driver which talks to the Companion via a different
> interface. Actually, we started with a UART connection but switched to
> SPI due to latency issues.

Ok, got it.

> Should we still change it?

At least I would then vote for

drivers/char/companion.c
drivers/net/can/companion.c

instead of

drivers/char/companion-char.c
drivers/net/can/companion-can.c

as you would have companion-users in different driver subsystems that
are already clearly referenced by their path.

The modules itself should still be named with companion-can of course
(as-is right now).

Btw.

+#define DRIVER_NAME "bosch,companion-can"

+static const struct can_bittiming_const companion_can_bittiming_const = {
+ .name = "bosch,companion",


Is there any reason why it's not only "companion-can" or "companion"?
The fact that the driver is provided by Bosch is visible in the source code.

Best regards,
Oliver


>
>>> 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 | 1189 ++++++++++++++++++++
>>> drivers/spi/companion/protocol-manager.c | 1035 +++++++++++++++++
>>> drivers/spi/companion/protocol-manager.h | 348 ++++++
>>> drivers/spi/companion/protocol.h | 273 +++++
>>> drivers/spi/companion/queue-manager.c | 146 +++
>>> drivers/spi/companion/queue-manager.h | 245 ++++
>>> include/linux/can/dev.h | 7 +-
>>> include/linux/companion.h | 258 +++++
>>> 20 files changed, 4677 insertions(+), 4 deletions(-)
>>> create mode 100644
>> Documentation/devicetree/bindings/spi/bosch,companion.txt
>>> create mode 100644 drivers/char/companion-char.c
>>> create mode 100644 drivers/net/can/companion-can.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
>
> Greetings,
> Mark
>
> Building Technologies, Panel Software Fire (BT-FIR/ENG1)
> Bosch Sicherheitssysteme GmbH | Postfach 11 11 | 85626 Grasbrunn | GERMANY | http://www.boschsecurity.com
>
> Sitz: Stuttgart, Registergericht: Amtsgericht Stuttgart HRB 23118
> Aufsichtsratsvorsitzender: Stefan Hartung; Geschäftsführung: Gert van Iperen, Andreas Bartz, Thomas Quante, Bernhard Schuster
>

Subject: Re: [PATCH 0/5] can: enable multi-queue for SocketCAN devices

Hi Andy,

> > 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.
>
> Can remoteproc API be utilized here?

So far I wasn't aware of the remoteproc API. It appears to me that is
limited to power on/off and loading firmware in an AMP scenario. Here,
the Companion has a fixed firmware in it. It must already be running
quickly after power-up, even before the boot loader.

Does remoteproc also contain a communication framework?

Do you mean rpmsg? Here, I do not see how we could benefit from it.

Can you point me to an example where rpmsg is used over SPI?

Greetings,
Mark

Building Technologies, Panel Software Fire (BT-FIR/ENG1)
Bosch Sicherheitssysteme GmbH | Postfach 11 11 | 85626 Grasbrunn | GERMANY | http://www.boschsecurity.com

Sitz: Stuttgart, Registergericht: Amtsgericht Stuttgart HRB 23118
Aufsichtsratsvorsitzender: Stefan Hartung; Geschäftsführung: Gert van Iperen, Andreas Bartz, Thomas Quante, Bernhard Schuster

Subject: AW: [PATCH 2/5] spi: implement companion-spi driver

Hello Andy,

Thank you very much for your feedback.

> > + /*TODO: support mutiple packets in one write in future*/
>
> Hmm... Either fix this, or remove comment?

Agreed, we will manage ideas for future changes at a different place.

> > + if (copy_from_user(p.data, buf, sizeof(p)) == 0) {
> > + if (is_can_type(&p))
> > + return -EINVAL;
> > + } else {
> > + dev_info(parent, "copy from user not succeed in one call\n");
>
> Shouldn't it return immediately?

Yes, it should. Will be changed.

>
> > + }
>
> > +
> > + error = qm_io_txq_in(&priv->pm.qm, buf, count, &copied);
>
> ...what the point to call if we got garbage from user space?

Will be changed with the code above.

> > + if (!error) {
>
> The usual pattern is to check for errors first.

Understood, will be changed.

> > + wake_up_interruptible(&priv->wait);
> > + priv->pm.stats.io_tx++;
> > + return copied;
> > + } else {
> > + priv->pm.stats.io_tx_overflows++;
> > + }
> > + return error;
> > +}
>
> > + ... qm_io_rxq_out(&priv->pm.qm, buf, count, &copied);
>
> > + ... pm_can_data_tx(&priv->pm, port, prio, cf);
>
> Oh, oh, oh.
>
> These namespaces are too generic, moreover pm is kinda occupied by
> power management. You bring a lot of confusion here, I think.

We agree and we started thinking about better names. We will send a proposal.

> > + err = pm_can_get_txq_status(pm, port);
> > + if (!err) {
>
> if (err)
> return err;

Yes, will be changed.

> > + }
> > + return err;
>
>
> > + int ret, value;
> > +
> > + ret = sscanf(buf, "%d", &value);
> > + if (ret != 1) {
>
> > + }
>
> You have to be consistent in your code.
>
> I've already noticed
>
> err
> error
>
> and now
>
> ret
>
> Choose one and stick with it.

Yes, will be changed.

> Also check your entire patch series' code for consistency.

We will have a look.

> > +static DEVICE_ATTR(dump_packets, S_IRUGO | S_IWUSR,
> > + show_dump_packets, store_dump_packets);
>
> We have helpers, like DEVICE_ATTR_RW().

Will be changed.

> > + 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]);
>
> Please, avoid leading '\n'.

We think we will stick to the existing. Otherweise we would have to
insert another pair of sprint() and pos += ret. Is it worth that?

>
> > + gpio_set_value(priv->cs_gpios, priv->cs_gpios_assert);
>
> > + gpio_set_value(priv->cs_gpios, !priv->cs_gpios_assert);
>
> Can you switch to GPIO descriptor API?

Yes, we are working on it.

> > + unsigned int count = READY_POLL_US / READY_POLL_US_GRAN;
> > + while (count--) {
>
> For counting like this better to have
> do {
> } while (--count);
>
> The rationale, reader at first glance will know that the loop will
> iterate at least once.

Agreed, will be changed.

> > + if (slave_is_not_busy(priv))
> > + return 0;
> > +
>
> > + udelay(READY_POLL_US_GRAN);
>
> Should it be atomic?
> Can it use read_poll_* type of helpers instead?

Yes, it shall be atomic because we need to reduce the latency at
detecting the de-assertion of the busy signal. We accept that this will
cost CPU time.

In case the Companion itself is very busy and does not reply quickly we
are have second polling loop below which sleeps longer and is non-atomic.

> Above comments applicable to entire code you have.

We will look at it.

> > +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);
> > +}
>
> This entire function should be replaced by one of the helpers from
> include/linux/byteorder/generic.h
>
> I guess cpu_to_be32_array() is a right one.

Correct. We are testing the driver against 4.14 and that function is not
available there. It will be changed later.

> > +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);
> > +}
>
> Ditto.
>
> Recommendation: check your code against existing helpers.

Yes, every kernel release brings new helpers. We will have to learn how
to keep track of the additions.

> > + p = (const struct companion_packet*)transfer->tx_buf;
>
> > + companion_spi_cpu_to_be32((char*)transfer->tx_buf);
>
> If tx_buf is type of void * all these castings are redundant.

The type is const void*. So the first cast is superfluous, the second
is not.

> Also looking at the function, did you consider to use whatever SPI
> core provides, like CS handling, messages handling and so on?

SPI CS has to be done manually in our case because we need to wait
until the Companion signals that it is ready for the transfer.

Do you have concrete proposals regarding messages handling?

> > +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;
>
> Redundant.

Yes, it is redundant. We want to explicitly show here that the CS
handling is done manually.

> > + for (;;) {
>
> Infinite loops are evil in most of the cases.
> I see here
>
> do {
> } while (kthread_should_stop());

Yes, will be fixed.

> > +static const struct of_device_id companion_spi_of_match[] = {
> > + { .compatible = DRIVER_NAME, .data = NULL, },
> > + { /* sentinel */ },
>
> terminators better without commas.

Yes, will be fixed.

> > + .owner = THIS_MODULE,
>
> This is redundant, macro you are using below sets that.

Will be fixed.

> Something wrong with indentation in this file.

Yes, we will need to work on the indentation. We will do it in a
following round.

> > +#define CHECK_SIZE(x) BUILD_BUG_ON(sizeof(struct companion_packet) != \
> > + sizeof(x))
>
> Better to split like
> _SIZE(x) \
> BUILD_BUG_ON()

Will be fixed.

> > +void pm_init(struct companion_protocol_manager *pm)
>
> Unfortunately, horrible name for the function.
> Namespace is kinda occupied, name itself way too generic.

Yes, we will send a proposal.

> > + if (ctrl & CAN_CTRLMODE_LISTENONLY)
> > + p.mode = BCP_CAN_MODE_LISTEN;
> > + else
> > + return -EOPNOTSUPP;
>
> if (!(cond))
> return -ERRNO;

Will be fixed.

> > + * 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.
>
> Do you still need this text?

Do you mean the SPDX is enough? Then, yes, we can remove it.

> > + * TODO: add more statistics fields and export to sysfs
>
> Do it or remove the comment?
>
> > + * TODO: re-think the data structure for handle CAN response
>
> Ditto.

We will handle future improvement ideas in a different place.

> > +/**
> > + * BCP status field definitions
> > + */
> > +#define BCP_STATUS_SUCCESS 0x00u
> > +#define BCP_STATUS_UNKNOWN 0x01u
> > +#define BCP_STATUS_OTHER 0x02u
>
> BIT() ?

No, these are fixed numbers, not bit fields.

> > +struct companion_packet {
> > + __u8 data[BCP_PACKET_SIZE];
> > +};
>
> Is it going from / to user space? Otherwise why __ kind of type?

Will be fixed.

> > +#define CAN_MAX_IN_A_ROW 8
> > +
> > +
> > +
>
> Too many blank lines.

Yes. Will be fixed.

> > +int companion_do_can_err(struct device *parent,
> > + u8 port,
> > + struct can_berr_counter *bec,
> > + u8 *state,
> > + u8 *code);
>
> + blank line here.

Will be fixed.

>
> > +#define COMPANION_CAN_STATE_WARNING 0x01u
> > +#define COMPANION_CAN_STATE_PASSIVE 0x02u
> > +#define COMPANION_CAN_STATE_BUS_OFF 0x04u
> > +#define COMPANION_CAN_ERROR_STUFF 0x01u
> > +#define COMPANION_CAN_ERROR_FORM 0x02u
> > +#define COMPANION_CAN_ERROR_ACK 0x04u
> > +#define COMPANION_CAN_ERROR_BIT1 0x08u
> > +#define COMPANION_CAN_ERROR_BIT0 0x10u
> > +#define COMPANION_CAN_ERROR_CRC 0x20u
> > +#define COMPANION_CAN_ERROR_RXOV 0x80u
>
> BIT() ?

Yes, this is a bit field. Will be fixed.

Greetings,
Mark

Building Technologies, Panel Software Fire (BT-FIR/ENG1)
Bosch Sicherheitssysteme GmbH | Postfach 11 11 | 85626 Grasbrunn | GERMANY | http://www.boschsecurity.com

Sitz: Stuttgart, Registergericht: Amtsgericht Stuttgart HRB 23118
Aufsichtsratsvorsitzender: Stefan Hartung; Geschäftsführung: Gert van Iperen, Andreas Bartz, Thomas Quante, Bernhard Schuster

Subject: Re: [PATCH 0/5] can: enable multi-queue for SocketCAN devices

Hi Oliver,

> >> Please place the companion driver in
> >>
> >> drivers/net/can/spi/companion.c
> >>
> >> It also makes more sense in the Kconfig structure.
> >>
> >> Probably this naming scheme also makes sense for
> >>
> >> linux/drivers/char/spi/companion.c
> >>
> >> then ...
> >>
> >> If not it should be named at least
> >>
> >> drivers/char/companion-spi.c
> >>
> >> or
> >>
> >> drivers/char/spi-companion.c
> >
> > We intentionally left out the spi in the driver path / name because
> > only the drivers/spi/companion/* driver knows that that it is connected
> > to SPI. The others (drivers/net/can/companion-can.c and
> > drivers/char/companion-char.c) only know the API. This could also be
> > supplied by a driver which talks to the Companion via a different
> > interface. Actually, we started with a UART connection but switched to
> > SPI due to latency issues.
>
> Ok, got it.
>
> > Should we still change it?
>
> At least I would then vote for
>
> drivers/char/companion.c
> drivers/net/can/companion.c
>
> instead of
>
> drivers/char/companion-char.c
> drivers/net/can/companion-can.c

Sounds good, will be changed.

> as you would have companion-users in different driver subsystems that
> are already clearly referenced by their path.
>
> The modules itself should still be named with companion-can of course
> (as-is right now).
>
> Btw.
>
> +#define DRIVER_NAME "bosch,companion-can"
>
> +static const struct can_bittiming_const companion_can_bittiming_const = {
> + .name = "bosch,companion",
>
>
> Is there any reason why it's not only "companion-can" or "companion"?
> The fact that the driver is provided by Bosch is visible in the source code.

Hmm, I guess we mixed up the naming scheme used in devicetree. We will
sleep a night over it and then clean it up. I think the result will be
that the devicetree entry is "bosch,companion-can" and all other uses
are "companion-can".

Greetings,
Mark

Building Technologies, Panel Software Fire (BT-FIR/ENG1)
Bosch Sicherheitssysteme GmbH | Postfach 11 11 | 85626 Grasbrunn | GERMANY | http://www.boschsecurity.com

Sitz: Stuttgart, Registergericht: Amtsgericht Stuttgart HRB 23118
Aufsichtsratsvorsitzender: Stefan Hartung; Geschäftsführung: Gert van Iperen, Andreas Bartz, Thomas Quante, Bernhard Schuster

2018-06-08 06:05:28

by Oleksij Rempel

[permalink] [raw]
Subject: Re: [PATCH 2/5] spi: implement companion-spi driver

Hi Mark,

On Thu, Jun 07, 2018 at 02:58:24PM +0000, Jonas Mark (BT-FIR/ENG1) wrote:
> Hello Andy,
>
> Thank you very much for your feedback.
>
> > > + /*TODO: support mutiple packets in one write in future*/
> >
> > Hmm... Either fix this, or remove comment?
>
> Agreed, we will manage ideas for future changes at a different place.
>
> > > + if (copy_from_user(p.data, buf, sizeof(p)) == 0) {
> > > + if (is_can_type(&p))
> > > + return -EINVAL;
> > > + } else {
> > > + dev_info(parent, "copy from user not succeed in one call\n");
> >
> > Shouldn't it return immediately?
>
> Yes, it should. Will be changed.
>
> >
> > > + }
> >
> > > +
> > > + error = qm_io_txq_in(&priv->pm.qm, buf, count, &copied);
> >
> > ...what the point to call if we got garbage from user space?
>
> Will be changed with the code above.
>
> > > + if (!error) {
> >
> > The usual pattern is to check for errors first.
>
> Understood, will be changed.
>
> > > + wake_up_interruptible(&priv->wait);
> > > + priv->pm.stats.io_tx++;
> > > + return copied;
> > > + } else {
> > > + priv->pm.stats.io_tx_overflows++;
> > > + }
> > > + return error;
> > > +}
> >
> > > + ... qm_io_rxq_out(&priv->pm.qm, buf, count, &copied);
> >
> > > + ... pm_can_data_tx(&priv->pm, port, prio, cf);
> >
> > Oh, oh, oh.
> >
> > These namespaces are too generic, moreover pm is kinda occupied by
> > power management. You bring a lot of confusion here, I think.
>
> We agree and we started thinking about better names. We will send a proposal.
>
> > > + err = pm_can_get_txq_status(pm, port);
> > > + if (!err) {
> >
> > if (err)
> > return err;
>
> Yes, will be changed.
>
> > > + }
> > > + return err;
> >
> >
> > > + int ret, value;
> > > +
> > > + ret = sscanf(buf, "%d", &value);
> > > + if (ret != 1) {
> >
> > > + }
> >
> > You have to be consistent in your code.
> >
> > I've already noticed
> >
> > err
> > error
> >
> > and now
> >
> > ret
> >
> > Choose one and stick with it.
>
> Yes, will be changed.
>
> > Also check your entire patch series' code for consistency.
>
> We will have a look.
>
> > > +static DEVICE_ATTR(dump_packets, S_IRUGO | S_IWUSR,
> > > + show_dump_packets, store_dump_packets);
> >
> > We have helpers, like DEVICE_ATTR_RW().
>
> Will be changed.
>
> > > + 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]);
> >
> > Please, avoid leading '\n'.
>
> We think we will stick to the existing. Otherweise we would have to
> insert another pair of sprint() and pos += ret. Is it worth that?
>
> >
> > > + gpio_set_value(priv->cs_gpios, priv->cs_gpios_assert);
> >
> > > + gpio_set_value(priv->cs_gpios, !priv->cs_gpios_assert);
> >
> > Can you switch to GPIO descriptor API?
>
> Yes, we are working on it.
>
> > > + unsigned int count = READY_POLL_US / READY_POLL_US_GRAN;
> > > + while (count--) {
> >
> > For counting like this better to have
> > do {
> > } while (--count);
> >
> > The rationale, reader at first glance will know that the loop will
> > iterate at least once.
>
> Agreed, will be changed.
>
> > > + if (slave_is_not_busy(priv))
> > > + return 0;
> > > +
> >
> > > + udelay(READY_POLL_US_GRAN);
> >
> > Should it be atomic?
> > Can it use read_poll_* type of helpers instead?
>
> Yes, it shall be atomic because we need to reduce the latency at
> detecting the de-assertion of the busy signal. We accept that this will
> cost CPU time.
>
> In case the Companion itself is very busy and does not reply quickly we
> are have second polling loop below which sleeps longer and is non-atomic.

I can confirm, this make huge impact on protocol performance. And this
protocol is not really the speed runner.

> > Above comments applicable to entire code you have.
>
> We will look at it.
>
> > > +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);
> > > +}
> >
> > This entire function should be replaced by one of the helpers from
> > include/linux/byteorder/generic.h
> >
> > I guess cpu_to_be32_array() is a right one.
>
> Correct. We are testing the driver against 4.14 and that function is not
> available there. It will be changed later.
>
> > > +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);
> > > +}
> >
> > Ditto.
> >
> > Recommendation: check your code against existing helpers.
>
> Yes, every kernel release brings new helpers. We will have to learn how
> to keep track of the additions.
>
> > > + p = (const struct companion_packet*)transfer->tx_buf;
> >
> > > + companion_spi_cpu_to_be32((char*)transfer->tx_buf);
> >
> > If tx_buf is type of void * all these castings are redundant.
>
> The type is const void*. So the first cast is superfluous, the second
> is not.
>
> > Also looking at the function, did you consider to use whatever SPI
> > core provides, like CS handling, messages handling and so on?
>
> SPI CS has to be done manually in our case because we need to wait
> until the Companion signals that it is ready for the transfer.
>
> Do you have concrete proposals regarding messages handling?

you can send dummy message to set CS.
+ struct spi_transfer t = {
+ .len = 0,
+ .cs_change = 1,
+ };

+ /* send dummy to trigger CS */
+ reinit_completion(&priv->fc_complete);
+ spi_sync_locked(spi, &m);

see my ancient unfinished code:
https://patchwork.kernel.org/patch/9418511/

In general, this part of the code, can be provided as separate driver
which should be called as "SPI 5wire protocol". 3 wires for data, 1 -
chip select, 1 - busy state. Since, the slave cant fit to normal SPI
requirements, and can't be ready within predictable time, busy signal is
needed. Providing this as separate driver or part of SPI framework
should reduce the code for many different drivers.

The actual datagram on top of your spi companion should go to
separate driver. There are similar (almost identical designs)

can :+
char:+
dw: +
gpio:+--->some_multi_end_mux_protocol--->spi_5wire_protocol->spi--->

In my knowledge, only data format on top of spi_5wire_protocol is
different. See my notes for similar topics:
https://github.com/olerem/icc
https://github.com/olerem/spi-5wire

--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |


Attachments:
(No filename) (7.48 kB)
signature.asc (499.00 B)
Download all attachments
Subject: Re: [PATCH 2/5] spi: implement companion-spi driver

Hi Oleksij,

> > > > + if (slave_is_not_busy(priv))
> > > > + return 0;
> > > > +
> > >
> > > > + udelay(READY_POLL_US_GRAN);
> > >
> > > Should it be atomic?
> > > Can it use read_poll_* type of helpers instead?
> >
> > Yes, it shall be atomic because we need to reduce the latency at
> > detecting the de-assertion of the busy signal. We accept that this will
> > cost CPU time.
> >
> > In case the Companion itself is very busy and does not reply quickly we
> > are have second polling loop below which sleeps longer and is non-atomic.
>
> I can confirm, this make huge impact on protocol performance. And this
> protocol is not really the speed runner.

The challenge is that the protocol is synchronous and without back to
back transfers.

> you can send dummy message to set CS.
> + struct spi_transfer t = {
> + .len = 0,
> + .cs_change = 1,
> + };
>
> + /* send dummy to trigger CS */
> + reinit_completion(&priv->fc_complete);
> + spi_sync_locked(spi, &m);
>
> see my ancient unfinished code:
> https://patchwork.kernel.org/patch/9418511/

We will check it out.

> In general, this part of the code, can be provided as separate driver
> which should be called as "SPI 5wire protocol". 3 wires for data, 1 -
> chip select, 1 - busy state. Since, the slave cant fit to normal SPI
> requirements, and can't be ready within predictable time, busy signal is
> needed. Providing this as separate driver or part of SPI framework
> should reduce the code for many different drivers.
>
> The actual datagram on top of your spi companion should go to
> separate driver. There are similar (almost identical designs)
>
> can :+
> char:+
> dw: +
> gpio:+--->some_multi_end_mux_protocol--->spi_5wire_protocol->spi--->
>
> In my knowledge, only data format on top of spi_5wire_protocol is
> different. See my notes for similar topics:
> https://github.com/olerem/icc
> https://github.com/olerem/spi-5wire

With 5-wire protocol you are referencing to CLK, MISO, MOSI, CS (all
standard SPI signals) and an extra BUSY signal. What we implemented is
a 6-wire protocol. There is an additional REQUEST line where the SPI
slave requests a transfer. It is like a level triggered interrupt
request.

Yes, for making it more generic the code in drivers/spi/companion
could be split into a generic 6-wire protocol driver and a multiplexing
protocol on top of it.

How does a slave signal that it has data to be picked up with the
5-wire protocol?

Greetings,
Mark

Building Technologies, Panel Software Fire (BT-FIR/ENG1)
Bosch Sicherheitssysteme GmbH | Postfach 11 11 | 85626 Grasbrunn | GERMANY | http://www.boschsecurity.com

Sitz: Stuttgart, Registergericht: Amtsgericht Stuttgart HRB 23118
Aufsichtsratsvorsitzender: Stefan Hartung; Gesch?ftsf?hrung: Gert van Iperen, Andreas Bartz, Thomas Quante, Bernhard Schuster

2018-06-08 07:06:04

by Oleksij Rempel

[permalink] [raw]
Subject: Re: [PATCH 2/5] spi: implement companion-spi driver

On Fri, Jun 08, 2018 at 06:56:23AM +0000, Jonas Mark (BT-FIR/ENG1) wrote:
> Hi Oleksij,
>
> > > > > + if (slave_is_not_busy(priv))
> > > > > + return 0;
> > > > > +
> > > >
> > > > > + udelay(READY_POLL_US_GRAN);
> > > >
> > > > Should it be atomic?
> > > > Can it use read_poll_* type of helpers instead?
> > >
> > > Yes, it shall be atomic because we need to reduce the latency at
> > > detecting the de-assertion of the busy signal. We accept that this will
> > > cost CPU time.
> > >
> > > In case the Companion itself is very busy and does not reply quickly we
> > > are have second polling loop below which sleeps longer and is non-atomic.
> >
> > I can confirm, this make huge impact on protocol performance. And this
> > protocol is not really the speed runner.
>
> The challenge is that the protocol is synchronous and without back to
> back transfers.
>
> > you can send dummy message to set CS.
> > + struct spi_transfer t = {
> > + .len = 0,
> > + .cs_change = 1,
> > + };
> >
> > + /* send dummy to trigger CS */
> > + reinit_completion(&priv->fc_complete);
> > + spi_sync_locked(spi, &m);
> >
> > see my ancient unfinished code:
> > https://patchwork.kernel.org/patch/9418511/
>
> We will check it out.
>
> > In general, this part of the code, can be provided as separate driver
> > which should be called as "SPI 5wire protocol". 3 wires for data, 1 -
> > chip select, 1 - busy state. Since, the slave cant fit to normal SPI
> > requirements, and can't be ready within predictable time, busy signal is
> > needed. Providing this as separate driver or part of SPI framework
> > should reduce the code for many different drivers.
> >
> > The actual datagram on top of your spi companion should go to
> > separate driver. There are similar (almost identical designs)
> >
> > can :+
> > char:+
> > dw: +
> > gpio:+--->some_multi_end_mux_protocol--->spi_5wire_protocol->spi--->
> >
> > In my knowledge, only data format on top of spi_5wire_protocol is
> > different. See my notes for similar topics:
> > https://github.com/olerem/icc
> > https://github.com/olerem/spi-5wire
>
> With 5-wire protocol you are referencing to CLK, MISO, MOSI, CS (all
> standard SPI signals) and an extra BUSY signal. What we implemented is
> a 6-wire protocol. There is an additional REQUEST line where the SPI
> slave requests a transfer. It is like a level triggered interrupt
> request.
>
> Yes, for making it more generic the code in drivers/spi/companion
> could be split into a generic 6-wire protocol driver and a multiplexing
> protocol on top of it.
>
> How does a slave signal that it has data to be picked up with the
> 5-wire protocol?

The request and busy signals are multiplexed to one wire. So, it should
be easy to implement both, 5 and 6 wire protocol in one driver.

--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |


Attachments:
(No filename) (3.15 kB)
signature.asc (499.00 B)
Download all attachments

2018-06-11 04:42:51

by Oleksij Rempel

[permalink] [raw]
Subject: Re: [PATCH 0/5] can: enable multi-queue for SocketCAN devices

Hi,


On 07.06.2018 17:14, Jonas Mark (BT-FIR/ENG1) wrote:
> Hi Andy,
>
>>> 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.
>>
>> Can remoteproc API be utilized here?
>
> So far I wasn't aware of the remoteproc API. It appears to me that is
> limited to power on/off and loading firmware in an AMP scenario. Here,
> the Companion has a fixed firmware in it. It must already be running
> quickly after power-up, even before the boot loader.

yes, remoteproc is not quite suitable for this task.

> Does remoteproc also contain a communication framework?

it is using VirtIO

> Do you mean rpmsg? Here, I do not see how we could benefit from it.

using same message format instead of inventing new one will be really
good step:
https://github.com/OpenAMP/open-amp/wiki/RPMsg-Messaging-Protocol
(less code duplicating same functionality)

Looks like every company trying to solve the same problem over and over
again. We have point to point link between two systems. Each system has
multiple functionalities/applications so we should be able to address
this functionality. So we end to some thing with source address and
destination address. In all protocols used for inter processor/chip
communication, the difference is only the layout of 3 common fields:
source, destination and size. In many cases the ISO/OSI layer model is
badly broken and

> Can you point me to an example where rpmsg is used over SPI?

RPMsg is just transport layer, 5 or 6 wire SPI is in this case Physical
layer with flow control support. Currently i'm not sure if VirtIO with
queue support do make sense here.

> Greetings,
> Mark


Attachments:
signature.asc (499.00 B)
OpenPGP digital signature
Subject: [PATCH v2 1/5] can: enable multi-queue for SocketCAN devices

From: Zhu Yi <[email protected]>

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 <[email protected]>
Signed-off-by: Mark Jonas <[email protected]>
Reviewed-by: Heiko Schocher <[email protected]>
---
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


Subject: [PATCH v2 2/5] spi: implement companion-spi driver

From: Zhu Yi <[email protected]>

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 <[email protected]>
Signed-off-by: Mark Jonas <[email protected]>
---
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 <[email protected]>");
+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


Subject: [PATCH v2 5/5] spi,can,char: add companion DT binding documentation

From: Zhu Yi <[email protected]>

Signed-off-by: Zhu Yi <[email protected]>
Signed-off-by: Mark Jonas <[email protected]>
---
.../devicetree/bindings/spi/bosch,companion.txt | 82 ++++++++++++++++++++++
1 file changed, 82 insertions(+)
create mode 100644 Documentation/devicetree/bindings/spi/bosch,companion.txt

diff --git a/Documentation/devicetree/bindings/spi/bosch,companion.txt b/Documentation/devicetree/bindings/spi/bosch,companion.txt
new file mode 100644
index 0000000..5ded325
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/bosch,companion.txt
@@ -0,0 +1,82 @@
+Bosch Companion SPI slave device
+
+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-----+
+
+Required properties:
+
+- compatible : must be "bosch,companion-spi"
+- interrupt-parent : the phandle of the GPIO controller
+- interrupts : (GPIO) interrupt to which 'request-gpios' is
+ connected to
+- request-gpios : GPIO pin to request SPI master to receive data
+- busy-gpios : GPIO pin to indicate SPI slave is busy
+- cs-gpios : GPIO pin to select SPI slave
+
+Optional properties:
+
+The controller supports at most 2 CAN and 1 char device subnodes. When
+optionally specify the subnodes, the following properties are required:
+
+- CAN subnode
+ - compatible : must be "bosch,companion-can"
+ - clock-frequency: CAN device clock in Hz
+ - port : must be 0 or 1
+
+- Char device subnode
+ - compatible : must be "bosch,companion-char"
+
+Example:
+
+&ecspi1 {
+ companion-spi@0 {
+ compatible = "bosch,companion-spi";
+ interrupt-parent = <&gpio1>;
+ interrupts = <26 IRQ_TYPE_EDGE_FALLING>;
+ request-gpios = <&gpio1 26 GPIO_ACTIVE_LOW>;
+ busy-gpios = <&gpio1 27 GPIO_ACTIVE_LOW>;
+ cs-gpios = <&gpio4 9 GPIO_ACTIVE_LOW>;
+
+ companion-can0 {
+ compatible = "bosch,companion-can";
+ clock-frequency = <28000000>;
+ port = <0>;
+ };
+
+ companion-can1 {
+ compatible = "bosch,companion-can";
+ clock-frequency = <28000000>;
+ port = <1>;
+ };
+
+ companion-char {
+ compatible = "bosch,companion-char";
+ };
+ };
+};
--
2.7.4


Subject: [PATCH v2 0/5] can: enable multi-queue for SocketCAN devices

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


Subject: [PATCH v2 4/5] can: implement companion-can driver

From: Zhu Yi <[email protected]>

The upper level companion-can driver provides SocketCAN interface to
userspace for communicate CAN messages with the companion processor.

Signed-off-by: Zhu Yi <[email protected]>
Signed-off-by: Mark Jonas <[email protected]>
---
drivers/net/can/Kconfig | 8 +
drivers/net/can/Makefile | 2 +
drivers/net/can/companion.c | 693 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 703 insertions(+)
create mode 100644 drivers/net/can/companion.c

diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index ac4ff39..e403a7e 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -155,6 +155,14 @@ config PCH_CAN
is an IOH for x86 embedded processor (Intel Atom E6xx series).
This driver can access CAN bus.

+config COMPANION_CAN
+ tristate "Network device for companion communication (Bosch)"
+ depends on COMPANION_SPI
+ ---help---
+ The network device allows the userspace to use SocketCAN interface
+ to communicate with the Bosch companion processor via the companion
+ SPI driver.
+
source "drivers/net/can/c_can/Kconfig"
source "drivers/net/can/cc770/Kconfig"
source "drivers/net/can/ifi_canfd/Kconfig"
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index 02b8ed7..575ea63 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -34,5 +34,7 @@ obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o
obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o
obj-$(CONFIG_PCH_CAN) += pch_can.o
+obj-$(CONFIG_COMPANION_CAN) += companion-can.o
+companion-can-objs := companion.o

subdir-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) += -DDEBUG
diff --git a/drivers/net/can/companion.c b/drivers/net/can/companion.c
new file mode 100644
index 0000000..0702d24
--- /dev/null
+++ b/drivers/net/can/companion.c
@@ -0,0 +1,693 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Companion upper level can network 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/can/dev.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/companion.h>
+
+#define TX_QUEUE_DEPTH 16
+#define NUM_TX_QUEUES 8
+#define NUM_RX_QUEUES 1
+#define TX_ECHO_SKB_MAX (NUM_TX_QUEUES * TX_QUEUE_DEPTH)
+#define DRIVER_NAME "companion-can"
+
+/**
+ * struct companion_can_priv - companion-can private data structure
+ * @can: standard common CAN private data, must be first member
+ * @parent: address of the associated parent device
+ * @dev: address of the associated network device
+ * @port: the companion CAN port number
+ * @tx_head: array of all tx queue head
+ * @tx_tail: arrat of all tx queue tail
+ */
+struct companion_can_priv {
+ struct can_priv can;
+ struct device *parent;
+ struct net_device *dev;
+ u8 port;
+ u8 tx_head[NUM_TX_QUEUES];
+ u8 tx_tail[NUM_TX_QUEUES];
+};
+
+/**
+ * companion_can_put_echo_skb() - put echo skb into ring buffer
+ * @priv: address of companion-can private data
+ * @prio: which CAN queue to put
+ * @skb: address of the packet to put
+ */
+static void companion_can_put_echo_skb(struct companion_can_priv *priv,
+ u8 prio,
+ struct sk_buff *skb)
+{
+ u8 offset = prio * TX_QUEUE_DEPTH;
+ u8 index = priv->tx_head[prio] % TX_QUEUE_DEPTH;
+
+ can_put_echo_skb(skb, priv->dev, offset + index);
+ priv->tx_head[prio]++;
+}
+
+/**
+ * companion_can_get_echo_skb() - get echo skb from ring buffer
+ * @priv: address of companion-can private data
+ * @prio: which CAN queue to get
+ */
+static u8 companion_can_get_echo_skb(struct companion_can_priv *priv, u8 prio)
+{
+ u8 offset, index, result = 0;
+
+ if (priv->tx_head[prio] != priv->tx_tail[prio]) {
+ offset = prio * TX_QUEUE_DEPTH;
+ index = priv->tx_tail[prio] % TX_QUEUE_DEPTH;
+ result = can_get_echo_skb(priv->dev, offset + index);
+ priv->tx_tail[prio]++;
+ }
+ return result;
+}
+
+/**
+ * companion_can_free_echo_skb() - free echo skb from ring buffer
+ * @priv: address of companion-can private data
+ * @prio: which CAN queue to free
+ */
+static void companion_can_free_echo_skb(struct companion_can_priv *priv,
+ u8 prio)
+{
+ u8 offset, index;
+
+ if (priv->tx_head[prio] != priv->tx_tail[prio]) {
+ offset = prio * TX_QUEUE_DEPTH;
+ index = priv->tx_tail[prio] % TX_QUEUE_DEPTH;
+ can_free_echo_skb(priv->dev, offset + index);
+ priv->tx_tail[prio]++;
+ }
+}
+
+/**
+ * companion_can_set_bittiming() - set CAN bittiming
+ * @dev: address of the associated network device
+ */
+static int companion_can_set_bittiming(struct net_device *dev)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ const struct can_bittiming *bt = &priv->can.bittiming;
+ u32 ctrl = priv->can.ctrlmode;
+ int err;
+
+ err = companion_do_set_can_bittiming(priv->parent, priv->port, bt);
+ if (err)
+ return err;
+
+ if (ctrl & CAN_CTRLMODE_LISTENONLY) {
+ err = companion_do_set_can_ctrlmode(priv->parent,
+ priv->port,
+ ctrl);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+/**
+ * companion_can_set_mode() - set CAN mode
+ * @dev: address of the associated network device
+ * @mode: the CAN mode to set
+ */
+static int companion_can_set_mode(struct net_device *dev, enum can_mode mode)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ int err;
+
+ switch (mode) {
+ case CAN_MODE_START:
+ err = companion_can_set_bittiming(dev);
+ if (err)
+ return err;
+ /* fall through */
+
+ case CAN_MODE_STOP:
+ err = companion_do_set_can_mode(priv->parent,
+ priv->port,
+ mode);
+ if (err)
+ return err;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+/**
+ * companion_can_get_berr_counter() - get CAN error counter
+ * @dev: address of the associated network device
+ * @bec: address of the CAN error counter to store
+ */
+static int companion_can_get_berr_counter(const struct net_device *dev,
+ struct can_berr_counter *bec)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+
+ return companion_do_get_can_status(priv->parent, priv->port, bec);
+}
+
+/**
+ * companion_can_handle_state() - handle CAN state transition
+ * @dev: address of the associated network device
+ * @cf: address of the CAN frame to store CAN state
+ * @bec: address of the CAN error counter
+ * @state: the companion CAN state
+ */
+static void companion_can_handle_state(struct net_device *dev,
+ struct can_frame *cf,
+ struct can_berr_counter *bec,
+ u8 state)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ enum can_state new_state = CAN_STATE_ERROR_ACTIVE;
+ enum can_state rx_state = CAN_STATE_ERROR_ACTIVE;
+ enum can_state tx_state = CAN_STATE_ERROR_ACTIVE;
+
+ if (state & COMPANION_CAN_STATE_BUS_OFF) {
+ new_state = CAN_STATE_BUS_OFF;
+ rx_state = bec->rxerr >= bec->txerr ? new_state : rx_state;
+ tx_state = bec->txerr >= bec->rxerr ? new_state : tx_state;
+ } else if (state & COMPANION_CAN_STATE_PASSIVE) {
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ rx_state = bec->rxerr > 127 ? new_state : rx_state;
+ tx_state = bec->txerr > 127 ? new_state : tx_state;
+ } else if (state & COMPANION_CAN_STATE_WARNING) {
+ new_state = CAN_STATE_ERROR_WARNING;
+ rx_state = bec->rxerr >= bec->txerr ? new_state : rx_state;
+ tx_state = bec->txerr >= bec->rxerr ? new_state : tx_state;
+ }
+
+ if (new_state != priv->can.state) {
+ can_change_state(dev, cf, tx_state, rx_state);
+
+ if (new_state == CAN_STATE_BUS_OFF)
+ can_bus_off(dev);
+ }
+}
+
+/**
+ * companion_can_handle_error() - handle CAN error
+ * @dev: address of the associated network device
+ * @cf: address of the CAN frame to store CAN error
+ * @code: the companion CAN error code
+ */
+static void companion_can_handle_error(struct net_device *dev,
+ struct can_frame *cf,
+ u8 code)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+
+ if (code & COMPANION_CAN_ERROR_RXOV) {
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+ dev->stats.rx_over_errors++;
+ dev->stats.rx_errors++;
+ }
+
+ if (code & (COMPANION_CAN_ERROR_STUFF |
+ COMPANION_CAN_ERROR_FORM |
+ COMPANION_CAN_ERROR_ACK |
+ COMPANION_CAN_ERROR_BIT1 |
+ COMPANION_CAN_ERROR_BIT0 |
+ COMPANION_CAN_ERROR_CRC)) {
+ cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+ if (code & COMPANION_CAN_ERROR_STUFF) {
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ dev->stats.rx_errors++;
+ }
+
+ if (code & COMPANION_CAN_ERROR_FORM) {
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ dev->stats.rx_errors++;
+ }
+
+ if (code & COMPANION_CAN_ERROR_ACK) {
+ cf->can_id |= CAN_ERR_ACK;
+ cf->data[3] = CAN_ERR_PROT_LOC_ACK;
+ dev->stats.tx_errors++;
+ }
+
+ if (code & COMPANION_CAN_ERROR_BIT1) {
+ cf->data[2] |= CAN_ERR_PROT_BIT1;
+ dev->stats.tx_errors++;
+ }
+
+ if (code & COMPANION_CAN_ERROR_BIT0) {
+ cf->data[2] |= CAN_ERR_PROT_BIT0;
+ dev->stats.tx_errors++;
+ }
+
+ if (code & COMPANION_CAN_ERROR_CRC) {
+ cf->data[2] |= CAN_ERR_PROT_BIT;
+ cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
+ dev->stats.rx_errors++;
+ }
+
+ priv->can.can_stats.bus_error++;
+ }
+}
+
+/**
+ * companion_can_poll_err() - poll CAN error packet from companion
+ * @dev: address of the associated network device
+ */
+static bool companion_can_poll_err(struct net_device *dev)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ struct can_berr_counter bec;
+ u8 state;
+ u8 code;
+ struct sk_buff *skb;
+ struct can_frame *cf;
+
+ if (companion_do_can_err(priv->parent,
+ priv->port,
+ &bec,
+ &state,
+ &code) != 0)
+ return false;
+
+ skb = alloc_can_err_skb(dev, &cf);
+ if (!skb) {
+ dev_err(&dev->dev, "cannot alloc err skb\n");
+ return false;
+ }
+
+ companion_can_handle_state(dev, cf, &bec, state);
+ companion_can_handle_error(dev, cf, code);
+
+ dev->stats.rx_bytes += cf->can_dlc;
+ dev->stats.rx_packets++;
+ netif_rx(skb);
+ return true;
+}
+
+/**
+ * companion_can_poll_data() - poll CAN data packet from companion
+ * @dev: address of the associated network device
+ */
+static bool companion_can_poll_data(struct net_device *dev)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ struct sk_buff *skb;
+ struct can_frame *cf;
+
+ skb = alloc_can_skb(dev, &cf);
+ if (!skb) {
+ dev_err(&dev->dev, "cannot alloc rx skb\n");
+ dev->stats.rx_dropped++;
+ return false;
+ }
+
+ if (companion_do_can_rx(priv->parent, priv->port, cf) != 0) {
+ dev_kfree_skb_any(skb);
+ return false;
+ }
+
+ dev->stats.rx_bytes += cf->can_dlc;
+ dev->stats.rx_packets++;
+ netif_rx(skb);
+ can_led_event(dev, CAN_LED_EVENT_RX);
+ return true;
+}
+
+/**
+ * companion_can_on_tx_done() - CAN tx done callback
+ * @data: address of user supplied callback data
+ * @prio: which CAN queue is done
+ * @lost_seq_sync: flag indicate lost sequence happened
+ * @success: flag indicate last send is succeed or not
+ */
+static void companion_can_on_tx_done(void *data,
+ u8 prio,
+ bool lost_seq_sync,
+ bool success)
+{
+ struct companion_can_priv *priv = data;
+ struct net_device *dev = priv->dev;
+ struct net_device_stats *stats = &dev->stats;
+ int err;
+
+ if (success) {
+ stats->tx_bytes += companion_can_get_echo_skb(priv, prio);
+ stats->tx_packets++;
+ can_led_event(dev, CAN_LED_EVENT_TX);
+ } else {
+ companion_can_free_echo_skb(priv, prio);
+ dev_err(&dev->dev, "on_tx_done(%d) failed\n", prio);
+ }
+
+ if (lost_seq_sync)
+ dev_err(&dev->dev, "txq[%d] lost sequence sync\n", prio);
+
+ err = companion_do_can_stop_tx_timer(priv->parent, priv->port, prio);
+ if (err)
+ dev_err(&dev->dev,
+ "stop txq[%d] tx timer failed: %d\n",
+ prio, err);
+
+ netif_wake_subqueue(dev, prio);
+}
+
+/**
+ * companion_can_on_rx_done() - CAN rx done callback
+ * @data: address of user supplied callback data
+ */
+static void companion_can_on_rx_done(void *data)
+{
+ struct companion_can_priv *priv = data;
+
+ while (companion_can_poll_data(priv->dev))
+ ;
+}
+
+/**
+ * companion_can_on_error() - CAN error callback
+ * @data: address of user supplied callback data
+ */
+static void companion_can_on_error(void *data)
+{
+ struct companion_can_priv *priv = data;
+
+ while (companion_can_poll_err(priv->dev))
+ ;
+}
+
+/**
+ * companion_can_on_tx_timeout() - CAN tx timeout callback
+ * @data: address of user supplied callback data
+ * @prio: which CAN queue tx timed out
+ */
+static void companion_can_on_tx_timeout(void *data, u8 prio)
+{
+ struct companion_can_priv *priv = data;
+ bool lost_txq_sync = false;
+ int err;
+
+ err = companion_do_get_can_txq_status(priv->parent,
+ priv->port,
+ prio,
+ &lost_txq_sync);
+ if (err) {
+ dev_err(&priv->dev->dev,
+ "get can txq[%d] status failed: %d\n", prio, err);
+
+ if (err != -EINVAL)
+ companion_do_can_start_tx_timer(priv->parent,
+ priv->port,
+ prio);
+ return;
+ }
+
+ if (lost_txq_sync) {
+ dev_err(&priv->dev->dev,
+ "txq[%d] out of sync, restart data flow\n", prio);
+ companion_can_free_echo_skb(priv, prio);
+ netif_wake_subqueue(priv->dev, prio);
+ } else {
+ dev_err(&priv->dev->dev,
+ "txq[%d] is sync'd, but no ack, wait again\n", prio);
+ companion_do_can_start_tx_timer(priv->parent, priv->port, prio);
+ }
+}
+
+static struct companion_can_ops companion_can_can_ops = {
+ .on_tx_done = companion_can_on_tx_done,
+ .on_rx_done = companion_can_on_rx_done,
+ .on_error = companion_can_on_error,
+ .on_tx_timeout = companion_can_on_tx_timeout,
+};
+
+/**
+ * companion_can_open() - ndo_open callback
+ * @dev: address of the associated network device
+ */
+static int companion_can_open(struct net_device *dev)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ bool has_space = false;
+ int err, i;
+
+ err = companion_can_ops_register(priv->parent,
+ priv->port,
+ &companion_can_can_ops,
+ priv);
+ if (err) {
+ dev_err(&dev->dev,
+ "companion_can_ops_register() failed: %d\n", err);
+ goto out;
+ }
+
+ err = companion_can_set_mode(dev, CAN_MODE_START);
+ if (err) {
+ dev_err(&dev->dev,
+ "companion_can_set_mode() failed: %d\n", err);
+ goto out_register;
+ }
+
+ err = companion_do_get_can_txq_status_all(priv->parent, priv->port);
+ if (err) {
+ dev_err(&dev->dev,
+ "companion_do_get_can_txq_status_all() failed: %d\n",
+ err);
+ goto out_mode;
+ }
+
+ err = open_candev(dev);
+ if (err) {
+ dev_err(&dev->dev, "open_candev() failed: %d\n", err);
+ goto out_mode;
+ }
+
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ can_led_event(dev, CAN_LED_EVENT_OPEN);
+
+ for (i = 0; i < NUM_TX_QUEUES; ++i) {
+ err = companion_do_can_txq_has_space(priv->parent,
+ priv->port,
+ i,
+ &has_space);
+
+ if (!err && has_space) {
+ netif_tx_start_queue(netdev_get_tx_queue(dev, i));
+ } else {
+ netif_tx_stop_queue(netdev_get_tx_queue(dev, i));
+ dev_err(&dev->dev, "txq[%d] is not started\n", i);
+ }
+ }
+
+ return 0;
+
+out_mode:
+ companion_can_set_mode(dev, CAN_MODE_STOP);
+out_register:
+ companion_can_ops_unregister(priv->parent, priv->port);
+out:
+ return err;
+}
+
+/**
+ * companion_can_release() - ndo_close callback
+ * @dev: address of the associated network device
+ */
+static int companion_can_release(struct net_device *dev)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ int err;
+
+ netif_tx_stop_all_queues(dev);
+ can_led_event(dev, CAN_LED_EVENT_STOP);
+ priv->can.state = CAN_STATE_STOPPED;
+ close_candev(dev);
+ err = companion_can_set_mode(dev, CAN_MODE_STOP);
+ companion_can_ops_unregister(priv->parent, priv->port);
+ return err;
+}
+
+/**
+ * companion_can_start_xmit() - ndo_start_xmit callback
+ * @skb: address of the packet to send
+ * @dev: address of the associated network device
+ */
+static int companion_can_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ u16 prio = skb_get_queue_mapping(skb);
+ bool is_full = false;
+ int err;
+
+ if (can_dropped_invalid_skb(dev, skb)) {
+ dev_err(&dev->dev, "dropped invalid skb on txq[%d]\n", prio);
+ return NETDEV_TX_OK;
+ }
+
+ err = companion_do_can_tx(priv->parent, priv->port, prio, cf);
+ if (err) {
+ dev_err(&dev->dev, "dropped packet on txq[%d]\n", prio);
+ dev_kfree_skb_any(skb);
+ dev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+ }
+
+ err = companion_do_can_txq_is_full(priv->parent,
+ priv->port,
+ prio,
+ &is_full);
+ if (!err && is_full) {
+ netif_stop_subqueue(dev, prio);
+ err = companion_do_can_start_tx_timer(priv->parent,
+ priv->port,
+ prio);
+ if (err)
+ dev_err(&dev->dev,
+ "start txq[%d] tx timer failed: %d\n",
+ prio, err);
+ }
+
+ companion_can_put_echo_skb(priv, prio, skb);
+ return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops companion_can_netdev_ops = {
+ .ndo_open = companion_can_open,
+ .ndo_stop = companion_can_release,
+ .ndo_start_xmit = companion_can_start_xmit,
+};
+
+static const struct of_device_id companion_can_of_match[] = {
+ { .compatible = "bosch,companion-can", .data = NULL, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, companion_can_of_match);
+
+static const struct can_bittiming_const companion_can_bittiming_const = {
+ .name = DRIVER_NAME,
+ .tseg1_min = 2,
+ .tseg1_max = 16,
+ .tseg2_min = 1,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 1024,
+ .brp_inc = 1,
+};
+
+/**
+ * companion_can_probe() - probe callback
+ * @pdev: address of the platform device
+ */
+static int companion_can_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct net_device *dev;
+ struct companion_can_priv *priv;
+ u32 port, freq;
+ int err;
+
+ if (!node) {
+ dev_err(&pdev->dev, "no device tree data\n");
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32(node, "port", &port)) {
+ dev_err(&pdev->dev, "no port property\n");
+ return -ENODEV;
+ }
+
+ if (port != 0 && port != 1) {
+ dev_err(&pdev->dev,
+ "invalid port %d, valid range is [0,1]\n", port);
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "clock-frequency", &freq)) {
+ dev_err(&pdev->dev, "no clock-frequency property\n");
+ return -ENODEV;
+ }
+
+ if (!pdev->dev.parent) {
+ dev_err(&pdev->dev, "no parent device\n");
+ return -ENODEV;
+ }
+
+ dev = alloc_candev_mqs(sizeof(*priv),
+ TX_ECHO_SKB_MAX,
+ NUM_TX_QUEUES,
+ NUM_RX_QUEUES);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->netdev_ops = &companion_can_netdev_ops;
+ dev->flags |= IFF_ECHO;
+ dev->real_num_tx_queues = NUM_TX_QUEUES;
+
+ priv = netdev_priv(dev);
+ priv->can.clock.freq = freq;
+ priv->can.bittiming_const = &companion_can_bittiming_const;
+ priv->can.do_set_mode = companion_can_set_mode;
+ priv->can.do_get_berr_counter = companion_can_get_berr_counter;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY |
+ CAN_CTRLMODE_BERR_REPORTING;
+ priv->parent = pdev->dev.parent;
+ priv->dev = dev;
+ priv->port = port;
+
+ platform_set_drvdata(pdev, dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ err = register_candev(dev);
+ if (err) {
+ dev_err(&pdev->dev, "register_candev() failed: %d\n", err);
+ free_candev(dev);
+ return err;
+ }
+
+ devm_can_led_init(dev);
+ return 0;
+}
+
+/**
+ * companion_can_remove() - remove callback
+ * @pdev: address of the platform device
+ */
+static int companion_can_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+
+ unregister_candev(dev);
+ free_candev(dev);
+ return 0;
+}
+
+static struct platform_driver companion_can_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(companion_can_of_match),
+ },
+ .probe = companion_can_probe,
+ .remove = companion_can_remove,
+};
+module_platform_driver(companion_can_driver);
+
+MODULE_AUTHOR("Zhu Yi <[email protected]>");
+MODULE_DESCRIPTION("Companion upper level can network device");
+MODULE_LICENSE("GPL v2");
--
2.7.4


Subject: [PATCH v2 3/5] char: implement companion-char driver

From: Zhu Yi <[email protected]>

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 <[email protected]>
Signed-off-by: Mark Jonas <[email protected]>
---
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 <[email protected]>");
+MODULE_DESCRIPTION("Companion upper level character device");
+MODULE_LICENSE("GPL v2");
--
2.7.4


Subject: Re: [PATCH v2 0/5] can: enable multi-queue for SocketCAN devices

Hi Marc,

> Betreff: [PATCH v2 0/5] can: enable multi-queue for SocketCAN devices

You have been interested in seeing the reason why we are proposing the
"can: enable multi-queue for SocketCAN devices" patch for the Linux
mainline. So far I have not heard from you. Do you now see a chance of
getting the patch accepted?

>
> 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

Greetings,
Mark

Building Technologies, Panel Software Fire (BT-FIR/ENG1)
Bosch Sicherheitssysteme GmbH | Postfach 11 11 | 85626 Grasbrunn | GERMANY | http://www.boschsecurity.com

Sitz: Stuttgart, Registergericht: Amtsgericht Stuttgart HRB 23118
Aufsichtsratsvorsitzender: Stefan Hartung; Gesch?ftsf?hrung: Gert van Iperen, Andreas Bartz, Thomas Quante, Bernhard Schuster

2018-07-20 14:37:09

by Marc Kleine-Budde

[permalink] [raw]
Subject: Re: [PATCH v2 1/5] can: enable multi-queue for SocketCAN devices

On 06/13/2018 04:37 PM, Mark Jonas wrote:
> From: Zhu Yi <[email protected]>
>
> 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 <[email protected]>
> Signed-off-by: Mark Jonas <[email protected]>
> Reviewed-by: Heiko Schocher <[email protected]>

Applied to linux-can-next.

Marc

--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |


Attachments:
signature.asc (499.00 B)
OpenPGP digital signature

2018-07-20 14:37:33

by Marc Kleine-Budde

[permalink] [raw]
Subject: Re: [PATCH v2 0/5] can: enable multi-queue for SocketCAN devices

On 07/11/2018 07:00 PM, Jonas Mark (BT-FIR/ENG1) wrote:
> Hi Marc,
>
>> Betreff: [PATCH v2 0/5] can: enable multi-queue for SocketCAN devices
>
> You have been interested in seeing the reason why we are proposing the
> "can: enable multi-queue for SocketCAN devices" patch for the Linux
> mainline. So far I have not heard from you. Do you now see a chance of
> getting the patch accepted?

I've applied patch 1.

Marc

--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |


Attachments:
signature.asc (499.00 B)
OpenPGP digital signature