* [PATCH v7 3/6] can: ctucanfd: add support for CTU CAN FD open-source IP core - bus independent part.
2020-10-30 22:19 [PATCH v7 0/6] CTU CAN FD open-source IP core SocketCAN driver, PCI, platform integration and documentation Pavel Pisa
2020-10-30 22:19 ` [PATCH v7 1/6] dt-bindings: vendor-prefix: add prefix for the Czech Technical University in Prague Pavel Pisa
2020-10-30 22:19 ` [PATCH v7 2/6] dt-bindings: net: can: binding for CTU CAN FD open-source IP core Pavel Pisa
@ 2020-10-30 22:19 ` Pavel Pisa
2020-10-30 22:19 ` [PATCH v7 4/6] can: ctucanfd: CTU CAN FD open-source IP core - PCI bus support Pavel Pisa
` (4 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Pavel Pisa @ 2020-10-30 22:19 UTC (permalink / raw)
To: linux-can, devicetree, Marc Kleine-Budde, Oliver Hartkopp
Cc: Wolfgang Grandegger, David Miller, Rob Herring, mark.rutland,
Carsten Emde, armbru, netdev, linux-kernel, Marin Jerabek,
Ondrej Ille, Jiri Novak, Jaroslav Beran, Petr Porazil,
Pavel Machek, Drew Fustini, Pavel Pisa
From: Martin Jerabek <martin.jerabek01@gmail.com>
This driver adds support for the CTU CAN FD open-source IP core.
More documentation and core sources at project page
(https://gitlab.fel.cvut.cz/canbus/ctucanfd_ip_core).
The core integration to Xilinx Zynq system as platform driver
is available (https://gitlab.fel.cvut.cz/canbus/zynq/zynq-can-sja1000-top).
Implementation on Intel FPGA based PCI Express board is available
from project (https://gitlab.fel.cvut.cz/canbus/pcie-ctucanfd).
More about CAN bus related projects used and developed at CTU FEE at
http://canbus.pages.fel.cvut.cz/ .
Signed-off-by: Martin Jerabek <martin.jerabek01@gmail.com>
Signed-off-by: Ondrej Ille <ondrej.ille@gmail.com>
Signed-off-by: Pavel Pisa <pisa@cmp.felk.cvut.cz>
---
drivers/net/can/Kconfig | 1 +
drivers/net/can/Makefile | 1 +
drivers/net/can/ctucanfd/Kconfig | 12 +
drivers/net/can/ctucanfd/Makefile | 7 +
drivers/net/can/ctucanfd/ctucanfd.h | 87 ++
drivers/net/can/ctucanfd/ctucanfd_base.c | 1142 +++++++++++++++++++++
drivers/net/can/ctucanfd/ctucanfd_frame.h | 189 ++++
drivers/net/can/ctucanfd/ctucanfd_hw.c | 751 ++++++++++++++
drivers/net/can/ctucanfd/ctucanfd_hw.h | 935 +++++++++++++++++
drivers/net/can/ctucanfd/ctucanfd_regs.h | 971 ++++++++++++++++++
10 files changed, 4096 insertions(+)
create mode 100644 drivers/net/can/ctucanfd/Kconfig
create mode 100644 drivers/net/can/ctucanfd/Makefile
create mode 100644 drivers/net/can/ctucanfd/ctucanfd.h
create mode 100644 drivers/net/can/ctucanfd/ctucanfd_base.c
create mode 100644 drivers/net/can/ctucanfd/ctucanfd_frame.h
create mode 100644 drivers/net/can/ctucanfd/ctucanfd_hw.c
create mode 100644 drivers/net/can/ctucanfd/ctucanfd_hw.h
create mode 100644 drivers/net/can/ctucanfd/ctucanfd_regs.h
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index 17c166cc8482..458afc4b81f2 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -168,6 +168,7 @@ config PCH_CAN
source "drivers/net/can/c_can/Kconfig"
source "drivers/net/can/cc770/Kconfig"
+source "drivers/net/can/ctucanfd/Kconfig"
source "drivers/net/can/ifi_canfd/Kconfig"
source "drivers/net/can/m_can/Kconfig"
source "drivers/net/can/mscan/Kconfig"
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index 22164300122d..28b39cd122f0 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -21,6 +21,7 @@ obj-y += softing/
obj-$(CONFIG_CAN_AT91) += at91_can.o
obj-$(CONFIG_CAN_CC770) += cc770/
obj-$(CONFIG_CAN_C_CAN) += c_can/
+obj-$(CONFIG_CAN_CTUCANFD) += ctucanfd/
obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o
obj-$(CONFIG_CAN_GRCAN) += grcan.o
obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd/
diff --git a/drivers/net/can/ctucanfd/Kconfig b/drivers/net/can/ctucanfd/Kconfig
new file mode 100644
index 000000000000..8210a5fcd444
--- /dev/null
+++ b/drivers/net/can/ctucanfd/Kconfig
@@ -0,0 +1,12 @@
+config CAN_CTUCANFD
+ tristate "CTU CAN-FD IP core"
+ help
+ This driver adds support for the CTU CAN FD open-source IP core.
+ More documentation and core sources at project page
+ (https://gitlab.fel.cvut.cz/canbus/ctucanfd_ip_core).
+ The core integration to Xilinx Zynq system as platform driver
+ is available (https://gitlab.fel.cvut.cz/canbus/zynq/zynq-can-sja1000-top).
+ Implementation on Intel FPGA-based PCI Express board is available
+ from project (https://gitlab.fel.cvut.cz/canbus/pcie-ctucanfd) and
+ on Intel SoC from project (https://gitlab.fel.cvut.cz/canbus/intel-soc-ctucanfd).
+ Guidepost CTU FEE CAN bus projects page http://canbus.pages.fel.cvut.cz/ .
diff --git a/drivers/net/can/ctucanfd/Makefile b/drivers/net/can/ctucanfd/Makefile
new file mode 100644
index 000000000000..1fd0610964f0
--- /dev/null
+++ b/drivers/net/can/ctucanfd/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Makefile for the CTU CAN-FD IP module drivers
+#
+
+obj-$(CONFIG_CAN_CTUCANFD) := ctucanfd.o
+ctucanfd-y := ctucanfd_base.o ctucanfd_hw.o
diff --git a/drivers/net/can/ctucanfd/ctucanfd.h b/drivers/net/can/ctucanfd/ctucanfd.h
new file mode 100644
index 000000000000..6ea1482d1807
--- /dev/null
+++ b/drivers/net/can/ctucanfd/ctucanfd.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*******************************************************************************
+ *
+ * CTU CAN FD IP Core
+ *
+ * Copyright (C) 2015-2018 Ondrej Ille <ondrej.ille@gmail.com> FEE CTU
+ * Copyright (C) 2018-2020 Ondrej Ille <ondrej.ille@gmail.com> self-funded
+ * Copyright (C) 2018-2019 Martin Jerabek <martin.jerabek01@gmail.com> FEE CTU
+ * Copyright (C) 2018-2020 Pavel Pisa <pisa@cmp.felk.cvut.cz> FEE CTU/self-funded
+ *
+ * Project advisors:
+ * Jiri Novak <jnovak@fel.cvut.cz>
+ * Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ *
+ * Department of Measurement (http://meas.fel.cvut.cz/)
+ * Faculty of Electrical Engineering (http://www.fel.cvut.cz)
+ * Czech Technical University (http://www.cvut.cz/)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#ifndef __CTUCANFD__
+#define __CTUCANFD__
+
+#include <linux/netdevice.h>
+#include <linux/can/dev.h>
+#include <linux/list.h>
+
+#include "ctucanfd_hw.h"
+
+struct ctucan_priv {
+ struct can_priv can; /* must be first member! */
+ struct ctucan_hw_priv p;
+
+ unsigned int txb_head;
+ unsigned int txb_tail;
+ u32 txb_prio;
+ unsigned int txb_mask;
+ spinlock_t tx_lock; /* spinlock to serialize allocation and processing of TX buffers */
+
+ struct napi_struct napi;
+ struct device *dev;
+ struct clk *can_clk;
+
+ int irq_flags;
+ unsigned long drv_flags;
+
+ union ctu_can_fd_frame_format_w rxfrm_first_word;
+
+ struct list_head peers_on_pdev;
+};
+
+/**
+ * ctucan_probe_common - Device type independent registration call
+ *
+ * This function does all the memory allocation and registration for the CAN
+ * device.
+ *
+ * @dev: Handle to the generic device structure
+ * @addr: Base address of CTU CAN FD core address
+ * @irq: Interrupt number
+ * @ntxbufs: Number of implemented Tx buffers
+ * @can_clk_rate: Clock rate, if 0 then clock are taken from device node
+ * @pm_enable_call: Whether pm_runtime_enable should be called
+ * @set_drvdata_fnc: Function to set network driver data for physical device
+ *
+ * Return: 0 on success and failure value on error
+ */
+int ctucan_probe_common(struct device *dev, void __iomem *addr,
+ int irq, unsigned int ntxbufs,
+ unsigned long can_clk_rate,
+ int pm_enable_call,
+ void (*set_drvdata_fnc)(struct device *dev,
+ struct net_device *ndev));
+
+int ctucan_suspend(struct device *dev) __maybe_unused;
+int ctucan_resume(struct device *dev) __maybe_unused;
+
+#endif /*__CTUCANFD__*/
diff --git a/drivers/net/can/ctucanfd/ctucanfd_base.c b/drivers/net/can/ctucanfd/ctucanfd_base.c
new file mode 100644
index 000000000000..f855f90f7e61
--- /dev/null
+++ b/drivers/net/can/ctucanfd/ctucanfd_base.c
@@ -0,0 +1,1142 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*******************************************************************************
+ *
+ * CTU CAN FD IP Core
+ *
+ * Copyright (C) 2015-2018 Ondrej Ille <ondrej.ille@gmail.com> FEE CTU
+ * Copyright (C) 2018-2020 Ondrej Ille <ondrej.ille@gmail.com> self-funded
+ * Copyright (C) 2018-2019 Martin Jerabek <martin.jerabek01@gmail.com> FEE CTU
+ * Copyright (C) 2018-2020 Pavel Pisa <pisa@cmp.felk.cvut.cz> FEE CTU/self-funded
+ *
+ * Project advisors:
+ * Jiri Novak <jnovak@fel.cvut.cz>
+ * Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ *
+ * Department of Measurement (http://meas.fel.cvut.cz/)
+ * Faculty of Electrical Engineering (http://www.fel.cvut.cz)
+ * Czech Technical University (http://www.cvut.cz/)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/clk.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/can/error.h>
+#include <linux/can/led.h>
+#include <linux/pm_runtime.h>
+
+#include "ctucanfd.h"
+#include "ctucanfd_regs.h"
+
+#define DRV_NAME "ctucanfd"
+
+#ifdef DEBUG
+#define ctucan_netdev_dbg(ndev, args...) \
+ netdev_dbg(ndev, args)
+#else
+#define ctucan_netdev_dbg(...) do { } while (0)
+#endif
+
+/* TX buffer rotation:
+ * - when a buffer transitions to empty state, rotate order and priorities
+ * - if more buffers seem to transition at the same time, rotate
+ * by the number of buffers
+ * - it may be assumed that buffers transition to empty state in FIFO order
+ * (because we manage priorities that way)
+ * - at frame filling, do not rotate anything, just increment buffer modulo
+ * counter
+ */
+
+#define CTUCAN_FLAG_RX_FFW_BUFFERED 1
+
+#define CTUCAN_STATE_TO_TEXT_ENTRY(st) \
+ [st] = #st
+
+static const char * const ctucan_state_strings[CAN_STATE_MAX] = {
+ CTUCAN_STATE_TO_TEXT_ENTRY(CAN_STATE_ERROR_ACTIVE),
+ CTUCAN_STATE_TO_TEXT_ENTRY(CAN_STATE_ERROR_WARNING),
+ CTUCAN_STATE_TO_TEXT_ENTRY(CAN_STATE_ERROR_PASSIVE),
+ CTUCAN_STATE_TO_TEXT_ENTRY(CAN_STATE_BUS_OFF),
+ CTUCAN_STATE_TO_TEXT_ENTRY(CAN_STATE_STOPPED),
+ CTUCAN_STATE_TO_TEXT_ENTRY(CAN_STATE_SLEEPING)
+};
+
+/**
+ * ctucan_state_to_str - Convert CAN controller state code to corresponding text
+ * @state: CAN controller state code
+ *
+ * Return: pointer to string representation of the error state
+ */
+static const char *ctucan_state_to_str(enum can_state state)
+{
+ const char *txt = NULL;
+
+ if (state >= 0 && state < CAN_STATE_MAX)
+ txt = ctucan_state_strings[state];
+ return txt ? txt : "UNKNOWN";
+}
+
+/**
+ * ctucan_reset - Issue software reset request to CTU CAN FD IP core
+ * @ndev: Pointer to net_device structure
+ *
+ * Return: 0 for success, -%ETIMEDOUT if CAN controller does not leave reset
+ */
+static int ctucan_reset(struct net_device *ndev)
+{
+ int i;
+ struct ctucan_priv *priv = netdev_priv(ndev);
+
+ ctucan_netdev_dbg(ndev, "%s\n", __func__);
+
+ ctucan_hw_reset(&priv->p);
+ clear_bit(CTUCAN_FLAG_RX_FFW_BUFFERED, &priv->drv_flags);
+ i = 100;
+ while (!ctucan_hw_check_access(&priv->p)) {
+ if (!i--) {
+ netdev_warn(ndev, "device did not leave reset\n");
+ return -ETIMEDOUT;
+ }
+ usleep_range(100, 200);
+ }
+
+ return 0;
+}
+
+/**
+ * ctucan_set_bittiming - CAN set bit timing routine
+ * @ndev: Pointer to net_device structure
+ *
+ * This is the driver set bittiming routine.
+ * Return: 0 on success and failure value on error
+ */
+static int ctucan_set_bittiming(struct net_device *ndev)
+{
+ struct ctucan_priv *priv = netdev_priv(ndev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+
+ ctucan_netdev_dbg(ndev, "%s\n", __func__);
+
+ if (ctucan_hw_is_enabled(&priv->p)) {
+ netdev_err(ndev,
+ "BUG! Cannot set bittiming - CAN is enabled\n");
+ return -EPERM;
+ }
+
+ /* Note that bt may be modified here */
+ ctucan_hw_set_nom_bittiming(&priv->p, bt);
+
+ return 0;
+}
+
+/**
+ * ctucan_set_data_bittiming - CAN set data bit timing routine
+ * @ndev: Pointer to net_device structure
+ *
+ * This is the driver set data bittiming routine.
+ * Return: 0 on success and failure value on error
+ */
+static int ctucan_set_data_bittiming(struct net_device *ndev)
+{
+ struct ctucan_priv *priv = netdev_priv(ndev);
+ struct can_bittiming *dbt = &priv->can.data_bittiming;
+
+ ctucan_netdev_dbg(ndev, "%s\n", __func__);
+
+ if (ctucan_hw_is_enabled(&priv->p)) {
+ netdev_err(ndev,
+ "BUG! Cannot set bittiming - CAN is enabled\n");
+ return -EPERM;
+ }
+
+ /* Note that dbt may be modified here */
+ ctucan_hw_set_data_bittiming(&priv->p, dbt);
+
+ return 0;
+}
+
+/**
+ * ctucan_set_secondary_sample_point - CAN set secondary sample point routine
+ * @ndev: Pointer to net_device structure
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int ctucan_set_secondary_sample_point(struct net_device *ndev)
+{
+ struct ctucan_priv *priv = netdev_priv(ndev);
+ struct can_bittiming *dbt = &priv->can.data_bittiming;
+ int ssp_offset = 0;
+ bool ssp_ena;
+
+ ctucan_netdev_dbg(ndev, "%s\n", __func__);
+
+ if (ctucan_hw_is_enabled(&priv->p)) {
+ netdev_err(ndev,
+ "BUG! Cannot set SSP - CAN is enabled\n");
+ return -EPERM;
+ }
+
+ ssp_ena = false;
+
+ /* Use SSP for bit-rates above 1 Mbits/s */
+ if (dbt->bitrate > 1000000) {
+ ssp_ena = true;
+
+ /* Calculate SSP in minimal time quanta */
+ ssp_offset = (priv->can.clock.freq / 1000) *
+ dbt->sample_point / dbt->bitrate;
+
+ if (ssp_offset > 127) {
+ netdev_warn(ndev, "SSP offset saturated to 127\n");
+ ssp_offset = 127;
+ }
+ }
+
+ ctucan_hw_configure_ssp(&priv->p, ssp_ena, true, ssp_offset);
+
+ return 0;
+}
+
+/**
+ * ctucan_chip_start - This routine starts the driver
+ * @ndev: Pointer to net_device structure
+ *
+ * Routine expects that chip is in reset state. It setups initial
+ * Tx buffers for FIFO priorities, sets bittiming, enables interrupts,
+ * switches core to operational mode and changes controller
+ * state to %CAN_STATE_STOPPED.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int ctucan_chip_start(struct net_device *ndev)
+{
+ struct ctucan_priv *priv = netdev_priv(ndev);
+ union ctu_can_fd_int_stat int_ena, int_msk;
+ int err;
+ struct can_ctrlmode mode;
+
+ ctucan_netdev_dbg(ndev, "%s\n", __func__);
+
+ priv->txb_prio = 0x01234567;
+ priv->txb_head = 0;
+ priv->txb_tail = 0;
+ priv->p.write_reg(&priv->p, CTU_CAN_FD_TX_PRIORITY, priv->txb_prio);
+
+ err = ctucan_set_bittiming(ndev);
+ if (err < 0)
+ return err;
+
+ err = ctucan_set_data_bittiming(ndev);
+ if (err < 0)
+ return err;
+
+ err = ctucan_set_secondary_sample_point(ndev);
+ if (err < 0)
+ return err;
+
+ /* Enable interrupts */
+ int_ena.u32 = 0;
+ int_ena.s.rbnei = 1;
+ int_ena.s.txbhci = 1;
+
+ int_ena.s.ewli = 1;
+ int_ena.s.fcsi = 1;
+
+ mode.flags = priv->can.ctrlmode;
+ mode.mask = 0xFFFFFFFF;
+ ctucan_hw_set_mode_reg(&priv->p, &mode);
+
+ /* One shot mode supported indirectly via Retransmit limit */
+ if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
+ ctucan_hw_set_ret_limit(&priv->p, true, 0);
+
+ /* Bus error reporting -> Allow Error interrupt */
+ if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) {
+ int_ena.s.ali = 1;
+ int_ena.s.bei = 1;
+ }
+
+ int_msk.u32 = ~int_ena.u32; /* mask all disabled interrupts */
+
+ /* It's after reset, so there is no need to clear anything */
+ ctucan_hw_int_mask_set(&priv->p, int_msk);
+ ctucan_hw_int_ena_set(&priv->p, int_ena);
+
+ /* Controller enters ERROR_ACTIVE on initial FCSI */
+ priv->can.state = CAN_STATE_STOPPED;
+
+ /* Enable the controller */
+ ctucan_hw_enable(&priv->p, true);
+
+ return 0;
+}
+
+/**
+ * ctucan_do_set_mode - This sets the mode of the driver
+ * @ndev: Pointer to net_device structure
+ * @mode: Tells the mode of the driver
+ *
+ * This check the drivers state and calls the
+ * the corresponding modes to set.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int ctucan_do_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+ int ret;
+
+ ctucan_netdev_dbg(ndev, "%s\n", __func__);
+
+ switch (mode) {
+ case CAN_MODE_START:
+ ret = ctucan_reset(ndev);
+ if (ret < 0)
+ return ret;
+ ret = ctucan_chip_start(ndev);
+ if (ret < 0) {
+ netdev_err(ndev, "ctucan_chip_start failed!\n");
+ return ret;
+ }
+ netif_wake_queue(ndev);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * ctucan_start_xmit - Starts the transmission
+ * @skb: sk_buff pointer that contains data to be Txed
+ * @ndev: Pointer to net_device structure
+ *
+ * This function is invoked from upper layers to initiate transmission. This
+ * function uses the next available free txbuf and populates their fields to
+ * start the transmission.
+ *
+ * Return: %NETDEV_TX_OK on success, %NETDEV_TX_BUSY when no free Tx buffer is available,
+ * negative return values reserver for error cases
+ */
+static netdev_tx_t ctucan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct ctucan_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ struct canfd_frame *cf = (struct canfd_frame *)skb->data;
+ u32 txb_id;
+ bool ok;
+ unsigned long flags;
+
+ if (can_dropped_invalid_skb(ndev, skb))
+ return NETDEV_TX_OK;
+
+ /* Check if the TX buffer is full */
+ if (unlikely(!CTU_CAN_FD_TXTNF(ctu_can_get_status(&priv->p)))) {
+ netif_stop_queue(ndev);
+ netdev_err(ndev, "BUG!, no TXB free when queue awake!\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ txb_id = priv->txb_head & priv->txb_mask;
+ ctucan_netdev_dbg(ndev, "%s: using TXB#%u\n", __func__, txb_id);
+ ok = ctucan_hw_insert_frame(&priv->p, cf, 0, txb_id,
+ can_is_canfd_skb(skb));
+
+ if (!ok) {
+ netdev_err(ndev,
+ "BUG! TXNF set but cannot insert frame into TXTB! HW Bug?");
+ kfree_skb(skb);
+ ndev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+ }
+ can_put_echo_skb(skb, ndev, txb_id);
+
+ if (!(cf->can_id & CAN_RTR_FLAG))
+ stats->tx_bytes += cf->len;
+
+ spin_lock_irqsave(&priv->tx_lock, flags);
+
+ ctucan_hw_txt_set_rdy(&priv->p, txb_id);
+
+ priv->txb_head++;
+
+ /* Check if all TX buffers are full */
+ if (!CTU_CAN_FD_TXTNF(ctu_can_get_status(&priv->p)))
+ netif_stop_queue(ndev);
+
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+
+ return NETDEV_TX_OK;
+}
+
+/**
+ * ctucan_rx - Is called from CAN isr to complete the received
+ * frame processing
+ * @ndev: Pointer to net_device structure
+ *
+ * This function is invoked from the CAN isr(poll) to process the Rx frames. It
+ * does minimal processing and invokes "netif_receive_skb" to complete further
+ * processing.
+ * Return: 1 when frame is passed to the network layer, 0 when the first frame word
+ * is read but system is out of free SKBs temporally and left code to resolve
+ * SKB allocation later and -%EAGAIN in a case of empty Rx FIFO.
+ */
+static int ctucan_rx(struct net_device *ndev)
+{
+ struct ctucan_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ struct canfd_frame *cf;
+ struct sk_buff *skb;
+ u64 ts;
+ union ctu_can_fd_frame_format_w ffw;
+
+ if (test_bit(CTUCAN_FLAG_RX_FFW_BUFFERED, &priv->drv_flags)) {
+ ffw = priv->rxfrm_first_word;
+ clear_bit(CTUCAN_FLAG_RX_FFW_BUFFERED, &priv->drv_flags);
+ } else {
+ ffw = ctu_can_fd_read_rx_ffw(&priv->p);
+ }
+
+ if (!ffw.s.rwcnt)
+ return -EAGAIN;
+
+ if (ffw.s.fdf == FD_CAN)
+ skb = alloc_canfd_skb(ndev, &cf);
+ else
+ skb = alloc_can_skb(ndev, (struct can_frame **)&cf);
+
+ if (unlikely(!skb)) {
+ priv->rxfrm_first_word = ffw;
+ set_bit(CTUCAN_FLAG_RX_FFW_BUFFERED, &priv->drv_flags);
+ return 0;
+ }
+
+ ctucan_hw_read_rx_frame_ffw(&priv->p, cf, &ts, ffw);
+
+ stats->rx_bytes += cf->len;
+ stats->rx_packets++;
+ netif_receive_skb(skb);
+
+ return 1;
+}
+
+/**
+ * ctucan_err_interrupt - error frame Isr
+ * @ndev: net_device pointer
+ * @isr: interrupt status register value
+ *
+ * This is the CAN error interrupt and it will check the the type of error
+ * and forward the error frame to upper layers.
+ */
+static void ctucan_err_interrupt(struct net_device *ndev,
+ union ctu_can_fd_int_stat isr)
+{
+ struct ctucan_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ enum can_state state;
+ struct can_berr_counter berr;
+ union ctu_can_fd_err_capt_alc err_capt_alc;
+ int dologerr = net_ratelimit();
+
+ ctucan_hw_read_err_ctrs(&priv->p, &berr);
+ state = ctucan_hw_read_error_state(&priv->p);
+ err_capt_alc = ctu_can_fd_read_err_capt_alc(&priv->p);
+
+ if (dologerr)
+ netdev_info(ndev, "%s: ISR = 0x%08x, rxerr %d, txerr %d,"
+ " error type %u, pos %u, ALC id_field %u, bit %u\n",
+ __func__, isr.u32, berr.rxerr, berr.txerr,
+ err_capt_alc.s.err_type, err_capt_alc.s.err_pos,
+ err_capt_alc.s.alc_id_field, err_capt_alc.s.alc_bit);
+
+ skb = alloc_can_err_skb(ndev, &cf);
+
+ /* EWLI: error warning limit condition met
+ * FCSI: fault confinement state changed
+ * ALI: arbitration lost (just informative)
+ * BEI: bus error interrupt
+ */
+
+ if (isr.s.fcsi || isr.s.ewli) {
+ netdev_info(ndev, "state changes from %s to %s\n",
+ ctucan_state_to_str(priv->can.state),
+ ctucan_state_to_str(state));
+
+ if (priv->can.state == state)
+ netdev_warn(ndev,
+ "current and previous state is the same! (missed interrupt?)\n");
+
+ priv->can.state = state;
+ switch (state) {
+ case CAN_STATE_BUS_OFF:
+ priv->can.can_stats.bus_off++;
+ can_bus_off(ndev);
+ if (skb)
+ cf->can_id |= CAN_ERR_BUSOFF;
+ break;
+ case CAN_STATE_ERROR_PASSIVE:
+ priv->can.can_stats.error_passive++;
+ if (skb) {
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = (berr.rxerr > 127) ?
+ CAN_ERR_CRTL_RX_PASSIVE :
+ CAN_ERR_CRTL_TX_PASSIVE;
+ cf->data[6] = berr.txerr;
+ cf->data[7] = berr.rxerr;
+ }
+ break;
+ case CAN_STATE_ERROR_WARNING:
+ priv->can.can_stats.error_warning++;
+ if (skb) {
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] |= (berr.txerr > berr.rxerr) ?
+ CAN_ERR_CRTL_TX_WARNING :
+ CAN_ERR_CRTL_RX_WARNING;
+ cf->data[6] = berr.txerr;
+ cf->data[7] = berr.rxerr;
+ }
+ break;
+ case CAN_STATE_ERROR_ACTIVE:
+ cf->data[1] = CAN_ERR_CRTL_ACTIVE;
+ cf->data[6] = berr.txerr;
+ cf->data[7] = berr.rxerr;
+ break;
+ default:
+ netdev_warn(ndev, "unhandled error state (%d:%s)!\n",
+ state, ctucan_state_to_str(state));
+ break;
+ }
+ }
+
+ /* Check for Arbitration Lost interrupt */
+ if (isr.s.ali) {
+ if (dologerr)
+ netdev_info(ndev, "arbitration lost\n");
+ priv->can.can_stats.arbitration_lost++;
+ if (skb) {
+ cf->can_id |= CAN_ERR_LOSTARB;
+ cf->data[0] = CAN_ERR_LOSTARB_UNSPEC;
+ }
+ }
+
+ /* Check for Bus Error interrupt */
+ if (isr.s.bei) {
+ netdev_info(ndev, "bus error\n");
+ priv->can.can_stats.bus_error++;
+ stats->rx_errors++;
+ if (skb) {
+ cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+ cf->data[2] = CAN_ERR_PROT_UNSPEC;
+ cf->data[3] = CAN_ERR_PROT_LOC_UNSPEC;
+ }
+ }
+
+ if (skb) {
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+ netif_rx(skb);
+ }
+}
+
+/**
+ * ctucan_rx_poll - Poll routine for rx packets (NAPI)
+ * @napi: napi structure pointer
+ * @quota: Max number of rx packets to be processed.
+ *
+ * This is the poll routine for rx part.
+ * It will process the packets maximux quota value.
+ *
+ * Return: number of packets received
+ */
+static int ctucan_rx_poll(struct napi_struct *napi, int quota)
+{
+ struct net_device *ndev = napi->dev;
+ struct ctucan_priv *priv = netdev_priv(ndev);
+ int work_done = 0;
+ union ctu_can_fd_status status;
+ u32 framecnt;
+ int res;
+
+ framecnt = ctucan_hw_get_rx_frame_count(&priv->p);
+ res = 1;
+ while (framecnt && work_done < quota && res > 0) {
+ res = ctucan_rx(ndev);
+ work_done++;
+ framecnt = ctucan_hw_get_rx_frame_count(&priv->p);
+ }
+
+ /* Check for RX FIFO Overflow */
+ status = ctu_can_get_status(&priv->p);
+ if (status.s.dor) {
+ struct net_device_stats *stats = &ndev->stats;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+
+ netdev_info(ndev, "rx_poll: rx fifo overflow\n");
+ stats->rx_over_errors++;
+ stats->rx_errors++;
+ skb = alloc_can_err_skb(ndev, &cf);
+ if (skb) {
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+ netif_rx(skb);
+ }
+
+ /* Clear Data Overrun */
+ ctucan_hw_clr_overrun_flag(&priv->p);
+ }
+
+ if (work_done)
+ can_led_event(ndev, CAN_LED_EVENT_RX);
+
+ if (!framecnt && res != 0) {
+ if (napi_complete_done(napi, work_done)) {
+ union ctu_can_fd_int_stat iec;
+ /* Clear and enable RBNEI. It is level-triggered, so
+ * there is no race condition.
+ */
+ iec.u32 = 0;
+ iec.s.rbnei = 1;
+ ctucan_hw_int_clr(&priv->p, iec);
+ ctucan_hw_int_mask_clr(&priv->p, iec);
+ }
+ }
+
+ return work_done;
+}
+
+static void ctucan_rotate_txb_prio(struct net_device *ndev)
+{
+ struct ctucan_priv *priv = netdev_priv(ndev);
+ u32 prio = priv->txb_prio;
+ u32 nbuffersm1 = priv->txb_mask; /* nbuffers - 1 */
+
+ prio = (prio << 4) | ((prio >> (nbuffersm1 * 4)) & 0xF);
+ ctucan_netdev_dbg(ndev, "%s: from 0x%08x to 0x%08x\n",
+ __func__, priv->txb_prio, prio);
+ priv->txb_prio = prio;
+ priv->p.write_reg(&priv->p, CTU_CAN_FD_TX_PRIORITY, prio);
+}
+
+/**
+ * ctucan_tx_interrupt - Tx Done Isr
+ * @ndev: net_device pointer
+ */
+static void ctucan_tx_interrupt(struct net_device *ndev)
+{
+ struct ctucan_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ bool first = true;
+ union ctu_can_fd_int_stat icr;
+ bool some_buffers_processed;
+ unsigned long flags;
+
+ /* read tx_status
+ * if txb[n].finished (bit 2)
+ * if ok -> echo
+ * if error / aborted -> ?? (find how to handle oneshot mode)
+ * txb_tail++
+ */
+
+ icr.u32 = 0;
+ icr.s.txbhci = 1;
+ do {
+ spin_lock_irqsave(&priv->tx_lock, flags);
+
+ some_buffers_processed = false;
+ while ((int)(priv->txb_head - priv->txb_tail) > 0) {
+ u32 txb_idx = priv->txb_tail & priv->txb_mask;
+ u32 status = ctucan_hw_get_tx_status(&priv->p, txb_idx);
+
+ ctucan_netdev_dbg(ndev, "TXI: TXB#%u: status 0x%x\n",
+ txb_idx, status);
+
+ switch (status) {
+ case TXT_TOK:
+ ctucan_netdev_dbg(ndev, "TXT_OK\n");
+ can_get_echo_skb(ndev, txb_idx);
+ stats->tx_packets++;
+ break;
+ case TXT_ERR:
+ /* This indicated that retransmit limit has been
+ * reached. Obviously we should not echo the
+ * frame, but also not indicate any kind
+ * of error. If desired, it was already reported
+ * (possible multiple times) on each arbitration
+ * lost.
+ */
+ netdev_warn(ndev, "TXB in Error state\n");
+ can_free_echo_skb(ndev, txb_idx);
+ stats->tx_dropped++;
+ break;
+ case TXT_ABT:
+ /* Same as for TXT_ERR, only with different
+ * cause. We *could* re-queue the frame, but
+ * multiqueue/abort is not supported yet anyway.
+ */
+ netdev_warn(ndev, "TXB in Aborted state\n");
+ can_free_echo_skb(ndev, txb_idx);
+ stats->tx_dropped++;
+ break;
+ default:
+ /* Bug only if the first buffer is not finished,
+ * otherwise it is pretty much expected
+ */
+ if (first) {
+ netdev_err(ndev, "BUG: TXB#%u not in a finished state (0x%x)!\n",
+ txb_idx, status);
+ spin_unlock_irqrestore(&priv->tx_lock,
+ flags);
+ /* do not clear nor wake */
+ return;
+ }
+ goto clear;
+ }
+ priv->txb_tail++;
+ first = false;
+ some_buffers_processed = true;
+ /* Adjust priorities *before* marking the buffer
+ * as empty.
+ */
+ ctucan_rotate_txb_prio(ndev);
+ ctucan_hw_txt_set_empty(&priv->p, txb_idx);
+ }
+clear:
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+
+ /* If no buffers were processed this time, we cannot
+ * clear - that would introduce a race condition.
+ */
+ if (some_buffers_processed) {
+ /* Clear the interrupt again. We do not want to receive
+ * again interrupt for the buffer already handled.
+ * If it is the last finished one then it would cause
+ * log of spurious interrupt.
+ */
+ ctucan_hw_int_clr(&priv->p, icr);
+ }
+ } while (some_buffers_processed);
+
+ can_led_event(ndev, CAN_LED_EVENT_TX);
+
+ spin_lock_irqsave(&priv->tx_lock, flags);
+
+ /* Check if at least one TX buffer is free */
+ if (CTU_CAN_FD_TXTNF(ctu_can_get_status(&priv->p)))
+ netif_wake_queue(ndev);
+
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+}
+
+/**
+ * ctucan_interrupt - CAN Isr
+ * @irq: irq number
+ * @dev_id: device id poniter
+ *
+ * This is the CTU CAN FD ISR. It checks for the type of interrupt
+ * and invokes the corresponding ISR.
+ *
+ * Return:
+ * IRQ_NONE - If CAN device is in sleep mode, IRQ_HANDLED otherwise
+ */
+static irqreturn_t ctucan_interrupt(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ctucan_priv *priv = netdev_priv(ndev);
+ union ctu_can_fd_int_stat isr, icr;
+ union ctu_can_fd_int_stat imask;
+ int irq_loops;
+
+ ctucan_netdev_dbg(ndev, "%s\n", __func__);
+
+ for (irq_loops = 0; irq_loops < 10000; irq_loops++) {
+ /* Get the interrupt status */
+ isr = ctu_can_fd_int_sts(&priv->p);
+
+ if (!isr.u32)
+ return irq_loops ? IRQ_HANDLED : IRQ_NONE;
+
+ /* Receive Buffer Not Empty Interrupt */
+ if (isr.s.rbnei) {
+ ctucan_netdev_dbg(ndev, "RXBNEI\n");
+ icr.u32 = 0;
+ icr.s.rbnei = 1;
+ /* Mask RXBNEI the first then clear interrupt,
+ * then schedule NAPI. Even if another IRQ fires,
+ * isr.s.rbnei will always be 0 (masked).
+ */
+ ctucan_hw_int_mask_set(&priv->p, icr);
+ ctucan_hw_int_clr(&priv->p, icr);
+ napi_schedule(&priv->napi);
+ }
+
+ /* TX Buffer HW Command Interrupt */
+ if (isr.s.txbhci) {
+ ctucan_netdev_dbg(ndev, "TXBHCI\n");
+ /* Cleared inside */
+ ctucan_tx_interrupt(ndev);
+ }
+
+ /* Error interrupts */
+ if (isr.s.ewli || isr.s.fcsi || isr.s.ali) {
+ union ctu_can_fd_int_stat ierrmask = { .s = {
+ .ewli = 1, .fcsi = 1, .ali = 1, .bei = 1 } };
+ icr.u32 = isr.u32 & ierrmask.u32;
+
+ ctucan_netdev_dbg(ndev, "some ERR interrupt: clearing 0x%08x\n",
+ icr.u32);
+ ctucan_hw_int_clr(&priv->p, icr);
+ ctucan_err_interrupt(ndev, isr);
+ }
+ /* Ignore RI, TI, LFI, RFI, BSI */
+ }
+
+ netdev_err(ndev, "%s: stuck interrupt (isr=0x%08x), stopping\n",
+ __func__, isr.u32);
+
+ if (isr.s.txbhci) {
+ int i;
+
+ netdev_err(ndev, "txb_head=0x%08x txb_tail=0x%08x\n",
+ priv->txb_head, priv->txb_tail);
+ for (i = 0; i <= priv->txb_mask; i++) {
+ u32 status = ctucan_hw_get_tx_status(&priv->p, i);
+
+ netdev_err(ndev, "txb[%d] txb status=0x%08x\n",
+ i, status);
+ }
+ }
+
+ imask.u32 = 0xffffffff;
+ ctucan_hw_int_ena_clr(&priv->p, imask);
+ ctucan_hw_int_mask_set(&priv->p, imask);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ctucan_chip_stop - Driver stop routine
+ * @ndev: Pointer to net_device structure
+ *
+ * This is the drivers stop routine. It will disable the
+ * interrupts and disable the controller.
+ */
+static void ctucan_chip_stop(struct net_device *ndev)
+{
+ struct ctucan_priv *priv = netdev_priv(ndev);
+ union ctu_can_fd_int_stat mask;
+
+ ctucan_netdev_dbg(ndev, "%s\n", __func__);
+
+ mask.u32 = 0xffffffff;
+
+ /* Disable interrupts and disable CAN */
+ ctucan_hw_int_ena_clr(&priv->p, mask);
+ ctucan_hw_int_mask_set(&priv->p, mask);
+ ctucan_hw_enable(&priv->p, false);
+ priv->can.state = CAN_STATE_STOPPED;
+}
+
+/**
+ * ctucan_open - Driver open routine
+ * @ndev: Pointer to net_device structure
+ *
+ * This is the driver open routine.
+ * Return: 0 on success and failure value on error
+ */
+static int ctucan_open(struct net_device *ndev)
+{
+ struct ctucan_priv *priv = netdev_priv(ndev);
+ int ret;
+
+ ctucan_netdev_dbg(ndev, "%s\n", __func__);
+
+ ret = pm_runtime_get_sync(priv->dev);
+ if (ret < 0) {
+ netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n",
+ __func__, ret);
+ pm_runtime_put_noidle(priv->dev);
+ return ret;
+ }
+
+ ret = ctucan_reset(ndev);
+ if (ret < 0)
+ goto err_reset;
+
+ /* Common open */
+ ret = open_candev(ndev);
+ if (ret) {
+ netdev_warn(ndev, "open_candev failed!\n");
+ goto err_open;
+ }
+
+ ret = request_irq(ndev->irq, ctucan_interrupt, priv->irq_flags,
+ ndev->name, ndev);
+ if (ret < 0) {
+ netdev_err(ndev, "irq allocation for CAN failed\n");
+ goto err_irq;
+ }
+
+ ret = ctucan_chip_start(ndev);
+ if (ret < 0) {
+ netdev_err(ndev, "ctucan_chip_start failed!\n");
+ goto err_chip_start;
+ }
+
+ netdev_info(ndev, "ctu_can_fd device registered\n");
+ can_led_event(ndev, CAN_LED_EVENT_OPEN);
+ napi_enable(&priv->napi);
+ netif_start_queue(ndev);
+
+ return 0;
+
+err_chip_start:
+ free_irq(ndev->irq, ndev);
+err_irq:
+ close_candev(ndev);
+err_open:
+err_reset:
+ pm_runtime_put(priv->dev);
+
+ return ret;
+}
+
+/**
+ * ctucan_close - Driver close routine
+ * @ndev: Pointer to net_device structure
+ *
+ * Return: 0 always
+ */
+static int ctucan_close(struct net_device *ndev)
+{
+ struct ctucan_priv *priv = netdev_priv(ndev);
+
+ ctucan_netdev_dbg(ndev, "%s\n", __func__);
+
+ netif_stop_queue(ndev);
+ napi_disable(&priv->napi);
+ ctucan_chip_stop(ndev);
+ free_irq(ndev->irq, ndev);
+ close_candev(ndev);
+
+ can_led_event(ndev, CAN_LED_EVENT_STOP);
+ pm_runtime_put(priv->dev);
+
+ return 0;
+}
+
+/**
+ * ctucan_get_berr_counter - error counter routine
+ * @ndev: Pointer to net_device structure
+ * @bec: Pointer to can_berr_counter structure
+ *
+ * This is the driver error counter routine.
+ * Return: 0 on success and failure value on error
+ */
+static int ctucan_get_berr_counter(const struct net_device *ndev,
+ struct can_berr_counter *bec)
+{
+ struct ctucan_priv *priv = netdev_priv(ndev);
+ int ret;
+
+ ctucan_netdev_dbg(ndev, "%s\n", __func__);
+
+ ret = pm_runtime_get_sync(priv->dev);
+ if (ret < 0) {
+ netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n",
+ __func__, ret);
+ pm_runtime_put_noidle(priv->dev);
+ return ret;
+ }
+
+ ctucan_hw_read_err_ctrs(&priv->p, bec);
+
+ pm_runtime_put(priv->dev);
+
+ return 0;
+}
+
+static const struct net_device_ops ctucan_netdev_ops = {
+ .ndo_open = ctucan_open,
+ .ndo_stop = ctucan_close,
+ .ndo_start_xmit = ctucan_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
+};
+
+int ctucan_suspend(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct ctucan_priv *priv = netdev_priv(ndev);
+
+ ctucan_netdev_dbg(ndev, "%s\n", __func__);
+
+ if (netif_running(ndev)) {
+ netif_stop_queue(ndev);
+ netif_device_detach(ndev);
+ }
+
+ priv->can.state = CAN_STATE_SLEEPING;
+
+ return 0;
+}
+EXPORT_SYMBOL(ctucan_suspend);
+
+int ctucan_resume(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct ctucan_priv *priv = netdev_priv(ndev);
+
+ ctucan_netdev_dbg(ndev, "%s\n", __func__);
+
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ if (netif_running(ndev)) {
+ netif_device_attach(ndev);
+ netif_start_queue(ndev);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ctucan_resume);
+
+int ctucan_probe_common(struct device *dev, void __iomem *addr, int irq, unsigned int ntxbufs,
+ unsigned long can_clk_rate, int pm_enable_call,
+ void (*set_drvdata_fnc)(struct device *dev, struct net_device *ndev))
+{
+ struct ctucan_priv *priv;
+ struct net_device *ndev;
+ int ret;
+
+ /* Create a CAN device instance */
+ ndev = alloc_candev(sizeof(struct ctucan_priv), ntxbufs);
+ if (!ndev)
+ return -ENOMEM;
+
+ priv = netdev_priv(ndev);
+ spin_lock_init(&priv->tx_lock);
+ INIT_LIST_HEAD(&priv->peers_on_pdev);
+ priv->txb_mask = ntxbufs - 1;
+ priv->dev = dev;
+ priv->can.bittiming_const = &ctu_can_fd_bit_timing_max;
+ priv->can.data_bittiming_const = &ctu_can_fd_bit_timing_data_max;
+ priv->can.do_set_mode = ctucan_do_set_mode;
+
+ /* Needed for timing adjustment to be performed as soon as possible */
+ priv->can.do_set_bittiming = ctucan_set_bittiming;
+ priv->can.do_set_data_bittiming = ctucan_set_data_bittiming;
+
+ priv->can.do_get_berr_counter = ctucan_get_berr_counter;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK
+ | CAN_CTRLMODE_LISTENONLY
+ | CAN_CTRLMODE_FD
+ | CAN_CTRLMODE_PRESUME_ACK
+ | CAN_CTRLMODE_BERR_REPORTING
+ | CAN_CTRLMODE_FD_NON_ISO
+ | CAN_CTRLMODE_ONE_SHOT;
+ priv->p.mem_base = addr;
+
+ /* Get IRQ for the device */
+ ndev->irq = irq;
+ ndev->flags |= IFF_ECHO; /* We support local echo */
+
+ if (set_drvdata_fnc)
+ set_drvdata_fnc(dev, ndev);
+ SET_NETDEV_DEV(ndev, dev);
+ ndev->netdev_ops = &ctucan_netdev_ops;
+
+ /* Getting the CAN can_clk info */
+ if (!can_clk_rate) {
+ priv->can_clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->can_clk)) {
+ dev_err(dev, "Device clock not found.\n");
+ ret = PTR_ERR(priv->can_clk);
+ goto err_free;
+ }
+ can_clk_rate = clk_get_rate(priv->can_clk);
+ }
+
+ priv->p.write_reg = ctucan_hw_write32;
+ priv->p.read_reg = ctucan_hw_read32;
+
+ if (pm_enable_call)
+ pm_runtime_enable(dev);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n",
+ __func__, ret);
+ pm_runtime_put_noidle(priv->dev);
+ goto err_pmdisable;
+ }
+
+ if ((priv->p.read_reg(&priv->p, CTU_CAN_FD_DEVICE_ID) &
+ 0xFFFF) != CTU_CAN_FD_ID) {
+ priv->p.write_reg = ctucan_hw_write32_be;
+ priv->p.read_reg = ctucan_hw_read32_be;
+ if ((priv->p.read_reg(&priv->p, CTU_CAN_FD_DEVICE_ID) &
+ 0xFFFF) != CTU_CAN_FD_ID) {
+ netdev_err(ndev, "CTU_CAN_FD signature not found\n");
+ ret = -ENODEV;
+ goto err_deviceoff;
+ }
+ }
+
+ ret = ctucan_reset(ndev);
+ if (ret < 0)
+ goto err_deviceoff;
+
+ priv->can.clock.freq = can_clk_rate;
+
+ netif_napi_add(ndev, &priv->napi, ctucan_rx_poll, NAPI_POLL_WEIGHT);
+
+ ret = register_candev(ndev);
+ if (ret) {
+ dev_err(dev, "fail to register failed (err=%d)\n", ret);
+ goto err_deviceoff;
+ }
+
+ devm_can_led_init(ndev);
+
+ pm_runtime_put(dev);
+
+ netdev_dbg(ndev, "mem_base=0x%p irq=%d clock=%d, txb mask:%d\n",
+ priv->p.mem_base, ndev->irq, priv->can.clock.freq,
+ priv->txb_mask);
+
+ return 0;
+
+err_deviceoff:
+ pm_runtime_put(priv->dev);
+err_pmdisable:
+ if (pm_enable_call)
+ pm_runtime_disable(dev);
+err_free:
+ list_del_init(&priv->peers_on_pdev);
+ free_candev(ndev);
+ return ret;
+}
+EXPORT_SYMBOL(ctucan_probe_common);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Martin Jerabek");
+MODULE_DESCRIPTION("CTU CAN FD interface");
diff --git a/drivers/net/can/ctucanfd/ctucanfd_frame.h b/drivers/net/can/ctucanfd/ctucanfd_frame.h
new file mode 100644
index 000000000000..330e269d3f03
--- /dev/null
+++ b/drivers/net/can/ctucanfd/ctucanfd_frame.h
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*******************************************************************************
+ *
+ * CTU CAN FD IP Core
+ *
+ * Copyright (C) 2015-2018 Ondrej Ille <ondrej.ille@gmail.com> FEE CTU
+ * Copyright (C) 2018-2020 Ondrej Ille <ondrej.ille@gmail.com> self-funded
+ * Copyright (C) 2018-2019 Martin Jerabek <martin.jerabek01@gmail.com> FEE CTU
+ * Copyright (C) 2018-2020 Pavel Pisa <pisa@cmp.felk.cvut.cz> FEE CTU/self-funded
+ *
+ * Project advisors:
+ * Jiri Novak <jnovak@fel.cvut.cz>
+ * Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ *
+ * Department of Measurement (http://meas.fel.cvut.cz/)
+ * Faculty of Electrical Engineering (http://www.fel.cvut.cz)
+ * Czech Technical University (http://www.cvut.cz/)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+/* This file is autogenerated, DO NOT EDIT! */
+
+#ifndef __CTU_CAN_FD_CAN_FD_FRAME_FORMAT__
+#define __CTU_CAN_FD_CAN_FD_FRAME_FORMAT__
+
+/* CAN_Frame_format memory map */
+enum ctu_can_fd_can_frame_format {
+ CTU_CAN_FD_FRAME_FORMAT_W = 0x0,
+ CTU_CAN_FD_IDENTIFIER_W = 0x4,
+ CTU_CAN_FD_TIMESTAMP_L_W = 0x8,
+ CTU_CAN_FD_TIMESTAMP_U_W = 0xc,
+ CTU_CAN_FD_DATA_1_4_W = 0x10,
+ CTU_CAN_FD_DATA_5_8_W = 0x14,
+ CTU_CAN_FD_DATA_61_64_W = 0x4c,
+};
+
+
+/* Register descriptions: */
+union ctu_can_fd_frame_format_w {
+ uint32_t u32;
+ struct ctu_can_fd_frame_format_w_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FRAME_FORMAT_W */
+ uint32_t dlc : 4;
+ uint32_t reserved_4 : 1;
+ uint32_t rtr : 1;
+ uint32_t ide : 1;
+ uint32_t fdf : 1;
+ uint32_t reserved_8 : 1;
+ uint32_t brs : 1;
+ uint32_t esi_rsv : 1;
+ uint32_t rwcnt : 5;
+ uint32_t reserved_31_16 : 16;
+#else
+ uint32_t reserved_31_16 : 16;
+ uint32_t rwcnt : 5;
+ uint32_t esi_rsv : 1;
+ uint32_t brs : 1;
+ uint32_t reserved_8 : 1;
+ uint32_t fdf : 1;
+ uint32_t ide : 1;
+ uint32_t rtr : 1;
+ uint32_t reserved_4 : 1;
+ uint32_t dlc : 4;
+#endif
+ } s;
+};
+
+enum ctu_can_fd_frame_format_w_rtr {
+ NO_RTR_FRAME = 0x0,
+ RTR_FRAME = 0x1,
+};
+
+enum ctu_can_fd_frame_format_w_ide {
+ BASE = 0x0,
+ EXTENDED = 0x1,
+};
+
+enum ctu_can_fd_frame_format_w_fdf {
+ NORMAL_CAN = 0x0,
+ FD_CAN = 0x1,
+};
+
+enum ctu_can_fd_frame_format_w_brs {
+ BR_NO_SHIFT = 0x0,
+ BR_SHIFT = 0x1,
+};
+
+enum ctu_can_fd_frame_format_w_esi_rsv {
+ ESI_ERR_ACTIVE = 0x0,
+ ESI_ERR_PASIVE = 0x1,
+};
+
+union ctu_can_fd_identifier_w {
+ uint32_t u32;
+ struct ctu_can_fd_identifier_w_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* IDENTIFIER_W */
+ uint32_t identifier_ext : 18;
+ uint32_t identifier_base : 11;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t identifier_base : 11;
+ uint32_t identifier_ext : 18;
+#endif
+ } s;
+};
+
+union ctu_can_fd_timestamp_l_w {
+ uint32_t u32;
+ struct ctu_can_fd_timestamp_l_w_s {
+ /* TIMESTAMP_L_W */
+ uint32_t time_stamp_l_w : 32;
+ } s;
+};
+
+union ctu_can_fd_timestamp_u_w {
+ uint32_t u32;
+ struct ctu_can_fd_timestamp_u_w_s {
+ /* TIMESTAMP_U_W */
+ uint32_t timestamp_u_w : 32;
+ } s;
+};
+
+union ctu_can_fd_data_1_4_w {
+ uint32_t u32;
+ struct ctu_can_fd_data_1_4_w_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* DATA_1_4_W */
+ uint32_t data_1 : 8;
+ uint32_t data_2 : 8;
+ uint32_t data_3 : 8;
+ uint32_t data_4 : 8;
+#else
+ uint32_t data_4 : 8;
+ uint32_t data_3 : 8;
+ uint32_t data_2 : 8;
+ uint32_t data_1 : 8;
+#endif
+ } s;
+};
+
+union ctu_can_fd_data_5_8_w {
+ uint32_t u32;
+ struct ctu_can_fd_data_5_8_w_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* DATA_5_8_W */
+ uint32_t data_5 : 8;
+ uint32_t data_6 : 8;
+ uint32_t data_7 : 8;
+ uint32_t data_8 : 8;
+#else
+ uint32_t data_8 : 8;
+ uint32_t data_7 : 8;
+ uint32_t data_6 : 8;
+ uint32_t data_5 : 8;
+#endif
+ } s;
+};
+
+union ctu_can_fd_data_61_64_w {
+ uint32_t u32;
+ struct ctu_can_fd_data_61_64_w_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* DATA_61_64_W */
+ uint32_t data_61 : 8;
+ uint32_t data_62 : 8;
+ uint32_t data_63 : 8;
+ uint32_t data_64 : 8;
+#else
+ uint32_t data_64 : 8;
+ uint32_t data_63 : 8;
+ uint32_t data_62 : 8;
+ uint32_t data_61 : 8;
+#endif
+ } s;
+};
+
+#endif
diff --git a/drivers/net/can/ctucanfd/ctucanfd_hw.c b/drivers/net/can/ctucanfd/ctucanfd_hw.c
new file mode 100644
index 000000000000..17dee00c600c
--- /dev/null
+++ b/drivers/net/can/ctucanfd/ctucanfd_hw.c
@@ -0,0 +1,751 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*******************************************************************************
+ *
+ * CTU CAN FD IP Core
+ *
+ * Copyright (C) 2015-2018 Ondrej Ille <ondrej.ille@gmail.com> FEE CTU
+ * Copyright (C) 2018-2020 Ondrej Ille <ondrej.ille@gmail.com> self-funded
+ * Copyright (C) 2018-2019 Martin Jerabek <martin.jerabek01@gmail.com> FEE CTU
+ * Copyright (C) 2018-2020 Pavel Pisa <pisa@cmp.felk.cvut.cz> FEE CTU/self-funded
+ *
+ * Project advisors:
+ * Jiri Novak <jnovak@fel.cvut.cz>
+ * Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ *
+ * Department of Measurement (http://meas.fel.cvut.cz/)
+ * Faculty of Electrical Engineering (http://www.fel.cvut.cz)
+ * Czech Technical University (http://www.cvut.cz/)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+
+#ifdef __KERNEL__
+# include <linux/can/dev.h>
+#else
+/* The hardware registers mapping and low level layer should build
+ * in userspace to allow development and verification of CTU CAN IP
+ * core VHDL design when loaded into hardware. Debugging hardware
+ * from kernel driver is really difficult, leads to system stucks
+ * by error reporting etc. Testing of exactly the same code
+ * in userspace together with headers generated automatically
+ * generated from from IP-XACT/cactus helps to driver to hardware
+ * and QEMU emulation model consistency keeping.
+ */
+# include "ctucanfd_linux_defs.h"
+#endif
+
+#include "ctucanfd_frame.h"
+#include "ctucanfd_hw.h"
+
+void ctucan_hw_write32(struct ctucan_hw_priv *priv,
+ enum ctu_can_fd_can_registers reg, u32 val)
+{
+ iowrite32(val, priv->mem_base + reg);
+}
+
+void ctucan_hw_write32_be(struct ctucan_hw_priv *priv,
+ enum ctu_can_fd_can_registers reg, u32 val)
+{
+ iowrite32be(val, priv->mem_base + reg);
+}
+
+u32 ctucan_hw_read32(struct ctucan_hw_priv *priv,
+ enum ctu_can_fd_can_registers reg)
+{
+ return ioread32(priv->mem_base + reg);
+}
+
+u32 ctucan_hw_read32_be(struct ctucan_hw_priv *priv,
+ enum ctu_can_fd_can_registers reg)
+{
+ return ioread32be(priv->mem_base + reg);
+}
+
+static void ctucan_hw_write_txt_buf(struct ctucan_hw_priv *priv,
+ enum ctu_can_fd_can_registers buf_base,
+ u32 offset, u32 val)
+{
+ priv->write_reg(priv, buf_base + offset, val);
+}
+
+static union ctu_can_fd_identifier_w ctucan_hw_id_to_hwid(canid_t id)
+{
+ union ctu_can_fd_identifier_w hwid;
+
+ hwid.u32 = 0;
+
+ if (id & CAN_EFF_FLAG) {
+ hwid.s.identifier_base = (id & CAN_EFF_MASK) >> 18;
+
+ /* getting lowest 18 bits, replace with sth nicer... */
+ hwid.s.identifier_ext = (id & 0x3FFFF);
+ } else {
+ hwid.s.identifier_base = id & CAN_SFF_MASK;
+ }
+ return hwid;
+}
+
+static u32 ctucan_hw_hwid_to_id(union ctu_can_fd_identifier_w hwid,
+ enum ctu_can_fd_frame_format_w_ide type)
+{
+ u32 id;
+
+ if (type == EXTENDED) {
+ id = CAN_EFF_FLAG;
+ id |= hwid.s.identifier_base << 18;
+ id |= hwid.s.identifier_ext;
+ } else {
+ id = hwid.s.identifier_base;
+ }
+
+ return id;
+}
+
+bool ctucan_hw_check_access(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_device_id_version reg;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_DEVICE_ID);
+
+ if (reg.s.device_id != CTU_CAN_FD_ID)
+ return false;
+
+ return true;
+}
+
+u32 ctucan_hw_get_version(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_device_id_version reg;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_DEVICE_ID);
+ return reg.s.ver_major * 10 + reg.s.ver_minor;
+}
+
+void ctucan_hw_enable(struct ctucan_hw_priv *priv, bool enable)
+{
+ union ctu_can_fd_mode_settings reg;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_MODE);
+ reg.s.ena = enable ? CTU_CAN_ENABLED : CTU_CAN_DISABLED;
+ priv->write_reg(priv, CTU_CAN_FD_MODE, reg.u32);
+}
+
+void ctucan_hw_reset(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_mode_settings mode;
+
+ mode.u32 = 0;
+ mode.s.rst = 1;
+ /* it does not matter that we overwrite the rest of the reg
+ * - we're resetting
+ */
+ priv->write_reg(priv, CTU_CAN_FD_MODE, mode.u32);
+}
+
+bool ctucan_hw_set_ret_limit(struct ctucan_hw_priv *priv, bool enable, u8 limit)
+{
+ union ctu_can_fd_mode_settings reg;
+
+ if (limit > CTU_CAN_FD_RETR_MAX)
+ return false;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_MODE);
+ reg.s.rtrle = enable ? RTRLE_ENABLED : RTRLE_DISABLED;
+ reg.s.rtrth = limit & 0xF;
+ priv->write_reg(priv, CTU_CAN_FD_MODE, reg.u32);
+ return true;
+}
+
+void ctucan_hw_set_mode_reg(struct ctucan_hw_priv *priv,
+ const struct can_ctrlmode *mode)
+{
+ u32 flags = mode->flags;
+ union ctu_can_fd_mode_settings reg;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_MODE);
+
+ if (mode->mask & CAN_CTRLMODE_LOOPBACK)
+ reg.s.ilbp = flags & CAN_CTRLMODE_LOOPBACK ?
+ INT_LOOP_ENABLED : INT_LOOP_DISABLED;
+
+ if (mode->mask & CAN_CTRLMODE_LISTENONLY)
+ reg.s.lom = flags & CAN_CTRLMODE_LISTENONLY ?
+ LOM_ENABLED : LOM_DISABLED;
+
+ if (mode->mask & CAN_CTRLMODE_FD)
+ reg.s.fde = flags & CAN_CTRLMODE_FD ?
+ FDE_ENABLE : FDE_DISABLE;
+
+ if (mode->mask & CAN_CTRLMODE_PRESUME_ACK)
+ reg.s.stm = flags & CAN_CTRLMODE_PRESUME_ACK ?
+ STM_ENABLED : STM_DISABLED;
+
+ if (mode->mask & CAN_CTRLMODE_FD_NON_ISO)
+ reg.s.nisofd = flags & CAN_CTRLMODE_FD_NON_ISO ?
+ NON_ISO_FD : ISO_FD;
+
+ priv->write_reg(priv, CTU_CAN_FD_MODE, reg.u32);
+}
+
+void ctucan_hw_rel_rx_buf(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_command reg;
+
+ reg.u32 = 0;
+ reg.s.rrb = 1;
+ priv->write_reg(priv, CTU_CAN_FD_COMMAND, reg.u32);
+}
+
+void ctucan_hw_clr_overrun_flag(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_command reg;
+
+ reg.u32 = 0;
+ reg.s.cdo = 1;
+ priv->write_reg(priv, CTU_CAN_FD_COMMAND, reg.u32);
+}
+
+static void ctucan_hw_int_conf(struct ctucan_hw_priv *priv,
+ enum ctu_can_fd_can_registers sreg,
+ enum ctu_can_fd_can_registers creg,
+ union ctu_can_fd_int_stat mask,
+ union ctu_can_fd_int_stat val)
+{
+ priv->write_reg(priv, sreg, mask.u32 & val.u32);
+ priv->write_reg(priv, creg, mask.u32 & (~val.u32));
+}
+
+void ctucan_hw_int_ena(struct ctucan_hw_priv *priv,
+ union ctu_can_fd_int_stat mask,
+ union ctu_can_fd_int_stat val)
+{
+ ctucan_hw_int_conf(priv, CTU_CAN_FD_INT_ENA_SET,
+ CTU_CAN_FD_INT_ENA_CLR, mask, val);
+}
+
+void ctucan_hw_int_mask(struct ctucan_hw_priv *priv,
+ union ctu_can_fd_int_stat mask,
+ union ctu_can_fd_int_stat val)
+{
+ ctucan_hw_int_conf(priv, CTU_CAN_FD_INT_MASK_SET,
+ CTU_CAN_FD_INT_MASK_CLR, mask, val);
+}
+
+void ctucan_hw_set_mode(struct ctucan_hw_priv *priv,
+ const struct can_ctrlmode *mode)
+{
+ ctucan_hw_set_mode_reg(priv, mode);
+
+ /* One shot mode supported indirectly via Retransmitt limit */
+ if (mode->mask & CAN_CTRLMODE_ONE_SHOT)
+ ctucan_hw_set_ret_limit(priv, !!(mode->flags &
+ CAN_CTRLMODE_ONE_SHOT), 0);
+
+ /* Bus error reporting -> Allow Error interrupt */
+ if (mode->mask & CAN_CTRLMODE_BERR_REPORTING) {
+ union ctu_can_fd_int_stat ena, mask;
+
+ ena.u32 = 0;
+ mask.u32 = 0;
+ ena.s.bei = !!(mode->flags & CAN_CTRLMODE_ONE_SHOT);
+ mask.s.bei = 1;
+ ctucan_hw_int_ena(priv, ena, mask);
+ }
+}
+
+const struct can_bittiming_const ctu_can_fd_bit_timing_max = {
+ .name = "ctu_can_fd",
+ .tseg1_min = 2,
+ .tseg1_max = 190,
+ .tseg2_min = 1,
+ .tseg2_max = 63,
+ .sjw_max = 31,
+ .brp_min = 1,
+ .brp_max = 8,
+ .brp_inc = 1,
+};
+
+const struct can_bittiming_const ctu_can_fd_bit_timing_data_max = {
+ .name = "ctu_can_fd",
+ .tseg1_min = 2,
+ .tseg1_max = 94,
+ .tseg2_min = 1,
+ .tseg2_max = 31,
+ .sjw_max = 31,
+ .brp_min = 1,
+ .brp_max = 2,
+ .brp_inc = 1,
+};
+
+void ctucan_hw_set_nom_bittiming(struct ctucan_hw_priv *priv,
+ struct can_bittiming *nbt)
+{
+ union ctu_can_fd_btr btr;
+
+ /* The timing calculation functions have only constraints on tseg1,
+ * which is prop_seg + phase1_seg combined. tseg1 is then split in half
+ * and stored into prog_seg and phase_seg1. In CTU CAN FD, PROP is
+ * 7 bits wide but PH1 only 6, so we must re-distribute the values here.
+ */
+ u32 prop_seg = nbt->prop_seg;
+ u32 phase_seg1 = nbt->phase_seg1;
+
+ if (phase_seg1 > 63) {
+ prop_seg += phase_seg1 - 63;
+ phase_seg1 = 63;
+ nbt->prop_seg = prop_seg;
+ nbt->phase_seg1 = phase_seg1;
+ }
+
+ btr.u32 = 0;
+ btr.s.prop = prop_seg;
+ btr.s.ph1 = phase_seg1;
+ btr.s.ph2 = nbt->phase_seg2;
+ btr.s.brp = nbt->brp;
+ btr.s.sjw = nbt->sjw;
+
+ priv->write_reg(priv, CTU_CAN_FD_BTR, btr.u32);
+}
+
+void ctucan_hw_set_data_bittiming(struct ctucan_hw_priv *priv,
+ struct can_bittiming *dbt)
+{
+ union ctu_can_fd_btr_fd btr_fd;
+
+ /* The timing calculation functions have only constraints on tseg1,
+ * which is prop_seg + phase1_seg combined. tseg1 is then split in half
+ * and stored into prog_seg and phase_seg1. In CTU CAN FD, PROP_FD is
+ * 6 bits wide but PH1_FD only 5, so we must re-distribute the values
+ * here.
+ */
+ u32 prop_seg = dbt->prop_seg;
+ u32 phase_seg1 = dbt->phase_seg1;
+
+ if (phase_seg1 > 31) {
+ prop_seg += phase_seg1 - 31;
+ phase_seg1 = 31;
+ dbt->prop_seg = prop_seg;
+ dbt->phase_seg1 = phase_seg1;
+ }
+
+ btr_fd.u32 = 0;
+ btr_fd.s.prop_fd = prop_seg;
+ btr_fd.s.ph1_fd = phase_seg1;
+ btr_fd.s.ph2_fd = dbt->phase_seg2;
+ btr_fd.s.brp_fd = dbt->brp;
+ btr_fd.s.sjw_fd = dbt->sjw;
+
+ priv->write_reg(priv, CTU_CAN_FD_BTR_FD, btr_fd.u32);
+}
+
+void ctucan_hw_set_err_limits(struct ctucan_hw_priv *priv, u8 ewl, u8 erp)
+{
+ union ctu_can_fd_ewl_erp_fault_state reg;
+
+ reg.u32 = 0;
+ reg.s.ew_limit = ewl;
+ reg.s.erp_limit = erp;
+ // era, bof, erp are read-only
+
+ priv->write_reg(priv, CTU_CAN_FD_EWL, reg.u32);
+}
+
+void ctucan_hw_read_err_ctrs(struct ctucan_hw_priv *priv,
+ struct can_berr_counter *ctr)
+{
+ union ctu_can_fd_rec_tec reg;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_REC);
+ ctr->txerr = reg.s.tec_val;
+ ctr->rxerr = reg.s.rec_val;
+}
+
+enum can_state ctucan_hw_read_error_state(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_ewl_erp_fault_state reg;
+ union ctu_can_fd_rec_tec err;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_EWL);
+ err.u32 = priv->read_reg(priv, CTU_CAN_FD_REC);
+
+ if (reg.s.era) {
+ if (reg.s.ew_limit > err.s.rec_val &&
+ reg.s.ew_limit > err.s.tec_val)
+ return CAN_STATE_ERROR_ACTIVE;
+ else
+ return CAN_STATE_ERROR_WARNING;
+ } else if (reg.s.erp) {
+ return CAN_STATE_ERROR_PASSIVE;
+ } else if (reg.s.bof) {
+ return CAN_STATE_BUS_OFF;
+ }
+ WARN(true, "Invalid error state");
+ return CAN_STATE_ERROR_PASSIVE;
+}
+
+void ctucan_hw_set_err_ctrs(struct ctucan_hw_priv *priv,
+ const struct can_berr_counter *ctr)
+{
+ union ctu_can_fd_ctr_pres reg;
+
+ reg.u32 = 0;
+
+ reg.s.ctpv = ctr->txerr;
+ reg.s.ptx = 1;
+ priv->write_reg(priv, CTU_CAN_FD_CTR_PRES, reg.u32);
+
+ reg.s.ctpv = ctr->rxerr;
+ reg.s.ptx = 0;
+ reg.s.prx = 1;
+ priv->write_reg(priv, CTU_CAN_FD_CTR_PRES, reg.u32);
+}
+
+bool ctucan_hw_get_mask_filter_support(struct ctucan_hw_priv *priv, u8 fnum)
+{
+ union ctu_can_fd_filter_control_filter_status reg;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_FILTER_CONTROL);
+
+ switch (fnum) {
+ case CTU_CAN_FD_FILTER_A:
+ if (reg.s.sfa)
+ return true;
+ break;
+ case CTU_CAN_FD_FILTER_B:
+ if (reg.s.sfb)
+ return true;
+ break;
+ case CTU_CAN_FD_FILTER_C:
+ if (reg.s.sfc)
+ return true;
+ break;
+ }
+
+ return false;
+}
+
+bool ctucan_hw_get_range_filter_support(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_filter_control_filter_status reg;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_FILTER_CONTROL);
+
+ return !!reg.s.sfr;
+}
+
+bool ctucan_hw_set_mask_filter(struct ctucan_hw_priv *priv, u8 fnum,
+ bool enable, const struct can_filter *filter)
+{
+ union ctu_can_fd_filter_control_filter_status creg;
+ enum ctu_can_fd_can_registers maddr, vaddr;
+ union ctu_can_fd_identifier_w hwid_mask;
+ union ctu_can_fd_identifier_w hwid_val;
+ u8 val = 0;
+
+ if (!ctucan_hw_get_mask_filter_support(priv, fnum))
+ return false;
+
+ if (enable)
+ val = 1;
+
+ creg.u32 = priv->read_reg(priv, CTU_CAN_FD_FILTER_CONTROL);
+
+ switch (fnum) {
+ case CTU_CAN_FD_FILTER_A:
+ maddr = CTU_CAN_FD_FILTER_A_MASK;
+ vaddr = CTU_CAN_FD_FILTER_A_VAL;
+ creg.s.fanb = val;
+ creg.s.fane = val;
+ creg.s.fafb = val;
+ creg.s.fafe = val;
+ break;
+ case CTU_CAN_FD_FILTER_B:
+ maddr = CTU_CAN_FD_FILTER_B_MASK;
+ vaddr = CTU_CAN_FD_FILTER_B_VAL;
+ creg.s.fbnb = val;
+ creg.s.fbne = val;
+ creg.s.fbfb = val;
+ creg.s.fbfe = val;
+ break;
+ case CTU_CAN_FD_FILTER_C:
+ maddr = CTU_CAN_FD_FILTER_C_MASK;
+ vaddr = CTU_CAN_FD_FILTER_C_VAL;
+ creg.s.fcnb = val;
+ creg.s.fcne = val;
+ creg.s.fcfb = val;
+ creg.s.fcfe = val;
+ break;
+ default:
+ return false;
+ }
+
+ hwid_mask = ctucan_hw_id_to_hwid(filter->can_id);
+ hwid_val = ctucan_hw_id_to_hwid(filter->can_mask);
+ priv->write_reg(priv, CTU_CAN_FD_FILTER_CONTROL, creg.u32);
+ priv->write_reg(priv, maddr, hwid_mask.u32);
+ priv->write_reg(priv, vaddr, hwid_val.u32);
+ return true;
+}
+
+void ctucan_hw_set_range_filter(struct ctucan_hw_priv *priv, canid_t low_th,
+ canid_t high_th, bool enable)
+{
+ union ctu_can_fd_identifier_w hwid_low;
+ union ctu_can_fd_identifier_w hwid_high;
+ union ctu_can_fd_filter_control_filter_status creg;
+
+ hwid_low = ctucan_hw_id_to_hwid(low_th);
+ hwid_high = ctucan_hw_id_to_hwid(high_th);
+
+ creg.u32 = priv->read_reg(priv, CTU_CAN_FD_FILTER_CONTROL);
+
+ creg.s.frnb = enable;
+ creg.s.frne = enable;
+ creg.s.frfb = enable;
+ creg.s.frfe = enable;
+
+ priv->write_reg(priv, CTU_CAN_FD_FILTER_CONTROL, creg.u32);
+ priv->write_reg(priv, CTU_CAN_FD_FILTER_RAN_LOW, hwid_low.u32);
+ priv->write_reg(priv, CTU_CAN_FD_FILTER_RAN_HIGH, hwid_high.u32);
+}
+
+void ctucan_hw_set_rx_tsop(struct ctucan_hw_priv *priv,
+ enum ctu_can_fd_rx_settings_rtsop val)
+{
+ union ctu_can_fd_rx_status_rx_settings reg;
+
+ reg.u32 = 0;
+ reg.s.rtsop = val;
+ priv->write_reg(priv, CTU_CAN_FD_RX_STATUS, reg.u32);
+}
+
+void ctucan_hw_read_rx_frame(struct ctucan_hw_priv *priv,
+ struct canfd_frame *cf, u64 *ts)
+{
+ union ctu_can_fd_frame_format_w ffw;
+
+ ffw.u32 = priv->read_reg(priv, CTU_CAN_FD_RX_DATA);
+ ctucan_hw_read_rx_frame_ffw(priv, cf, ts, ffw);
+}
+
+void ctucan_hw_read_rx_frame_ffw(struct ctucan_hw_priv *priv,
+ struct canfd_frame *cf, u64 *ts,
+ union ctu_can_fd_frame_format_w ffw)
+{
+ union ctu_can_fd_identifier_w idw;
+ unsigned int i;
+ unsigned int wc;
+ unsigned int len;
+ enum ctu_can_fd_frame_format_w_ide ide;
+
+ idw.u32 = priv->read_reg(priv, CTU_CAN_FD_RX_DATA);
+
+ ide = (enum ctu_can_fd_frame_format_w_ide)ffw.s.ide;
+ cf->can_id = ctucan_hw_hwid_to_id(idw, ide);
+
+ /* BRS, ESI, RTR Flags */
+ cf->flags = 0;
+ if (ffw.s.fdf == FD_CAN) {
+ if (ffw.s.brs == BR_SHIFT)
+ cf->flags |= CANFD_BRS;
+ if (ffw.s.esi_rsv == ESI_ERR_PASIVE)
+ cf->flags |= CANFD_ESI;
+ } else if (ffw.s.rtr == RTR_FRAME) {
+ cf->can_id |= CAN_RTR_FLAG;
+ }
+
+ wc = ffw.s.rwcnt - 3;
+
+ /* DLC */
+ if (ffw.s.dlc <= 8) {
+ len = ffw.s.dlc;
+ } else {
+ if (ffw.s.fdf == FD_CAN)
+ len = wc << 2;
+ else
+ len = 8;
+ }
+ cf->len = len;
+ if (unlikely(len > wc * 4))
+ len = wc * 4;
+
+ /* Timestamp */
+ *ts = (u64)(priv->read_reg(priv, CTU_CAN_FD_RX_DATA));
+ *ts |= ((u64)priv->read_reg(priv, CTU_CAN_FD_RX_DATA) << 32);
+
+ /* Data */
+ for (i = 0; i < len; i += 4) {
+ u32 data = priv->read_reg(priv, CTU_CAN_FD_RX_DATA);
+ *(__le32 *)(cf->data + i) = cpu_to_le32(data);
+ }
+ while (unlikely(i < wc * 4)) {
+ priv->read_reg(priv, CTU_CAN_FD_RX_DATA);
+ i += 4;
+ }
+}
+
+enum ctu_can_fd_tx_status_tx1s ctucan_hw_get_tx_status(struct ctucan_hw_priv
+ *priv, u8 buf)
+{
+ union ctu_can_fd_tx_status reg;
+ u32 status;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_TX_STATUS);
+
+ switch (buf) {
+ case CTU_CAN_FD_TXT_BUFFER_1:
+ status = reg.s.tx1s;
+ break;
+ case CTU_CAN_FD_TXT_BUFFER_2:
+ status = reg.s.tx2s;
+ break;
+ case CTU_CAN_FD_TXT_BUFFER_3:
+ status = reg.s.tx3s;
+ break;
+ case CTU_CAN_FD_TXT_BUFFER_4:
+ status = reg.s.tx4s;
+ break;
+ default:
+ status = ~0;
+ }
+ return (enum ctu_can_fd_tx_status_tx1s)status;
+}
+
+bool ctucan_hw_is_txt_buf_accessible(struct ctucan_hw_priv *priv, u8 buf)
+{
+ enum ctu_can_fd_tx_status_tx1s buf_status;
+
+ buf_status = ctucan_hw_get_tx_status(priv, buf);
+ if (buf_status == TXT_RDY || buf_status == TXT_TRAN ||
+ buf_status == TXT_ABTP)
+ return false;
+
+ return true;
+}
+
+void ctucan_hw_set_txt_priority(struct ctucan_hw_priv *priv, const u8 *prio)
+{
+ union ctu_can_fd_tx_priority reg;
+
+ reg.u32 = 0;
+ reg.s.txt1p = prio[0];
+ reg.s.txt2p = prio[1];
+ reg.s.txt3p = prio[2];
+ reg.s.txt4p = prio[3];
+
+ priv->write_reg(priv, CTU_CAN_FD_TX_PRIORITY, reg.u32);
+}
+
+static const enum ctu_can_fd_can_registers
+ tx_buf_bases[CTU_CAN_FD_TXT_BUFFER_COUNT] = {
+ CTU_CAN_FD_TXTB1_DATA_1, CTU_CAN_FD_TXTB2_DATA_1,
+ CTU_CAN_FD_TXTB3_DATA_1, CTU_CAN_FD_TXTB4_DATA_1
+};
+
+bool ctucan_hw_insert_frame(struct ctucan_hw_priv *priv,
+ const struct canfd_frame *cf, u64 ts, u8 buf,
+ bool isfdf)
+{
+ enum ctu_can_fd_can_registers buf_base;
+ union ctu_can_fd_frame_format_w ffw;
+ union ctu_can_fd_identifier_w idw;
+ unsigned int i;
+
+ ffw.u32 = 0;
+ idw.u32 = 0;
+
+ if (buf >= CTU_CAN_FD_TXT_BUFFER_COUNT)
+ return false;
+ buf_base = tx_buf_bases[buf];
+
+ if (!ctucan_hw_is_txt_buf_accessible(priv, buf))
+ return false;
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ ffw.s.rtr = RTR_FRAME;
+
+ if (cf->can_id & CAN_EFF_FLAG)
+ ffw.s.ide = EXTENDED;
+ else
+ ffw.s.ide = BASE;
+
+ idw = ctucan_hw_id_to_hwid(cf->can_id);
+
+ if (cf->len > CANFD_MAX_DLEN)
+ return false;
+
+ ffw.s.dlc = can_len2dlc(cf->len);
+
+ if (isfdf) {
+ ffw.s.fdf = FD_CAN;
+ if (cf->flags & CANFD_BRS)
+ ffw.s.brs = BR_SHIFT;
+ }
+
+ ctucan_hw_write_txt_buf(priv, buf_base,
+ CTU_CAN_FD_FRAME_FORMAT_W, ffw.u32);
+
+ ctucan_hw_write_txt_buf(priv, buf_base,
+ CTU_CAN_FD_IDENTIFIER_W, idw.u32);
+
+ ctucan_hw_write_txt_buf(priv, buf_base,
+ CTU_CAN_FD_TIMESTAMP_L_W, (u32)(ts));
+
+ ctucan_hw_write_txt_buf(priv, buf_base,
+ CTU_CAN_FD_TIMESTAMP_U_W, (u32)(ts >> 32));
+
+ if (!(cf->can_id & CAN_RTR_FLAG)) {
+ for (i = 0; i < cf->len; i += 4) {
+ u32 data = le32_to_cpu(*(__le32 *)(cf->data + i));
+
+ ctucan_hw_write_txt_buf(priv, buf_base,
+ CTU_CAN_FD_DATA_1_4_W + i, data);
+ }
+ }
+
+ return true;
+}
+
+u64 ctucan_hw_read_timestamp(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_timestamp_low ts_low;
+ union ctu_can_fd_timestamp_high ts_high;
+ union ctu_can_fd_timestamp_high ts_high_2;
+
+ ts_high.u32 = priv->read_reg(priv, CTU_CAN_FD_TIMESTAMP_HIGH);
+ ts_low.u32 = priv->read_reg(priv, CTU_CAN_FD_TIMESTAMP_LOW);
+ ts_high_2.u32 = priv->read_reg(priv, CTU_CAN_FD_TIMESTAMP_HIGH);
+
+ if (ts_high.u32 != ts_high_2.u32)
+ ts_low.u32 = priv->read_reg(priv, CTU_CAN_FD_TIMESTAMP_LOW);
+
+ return (((u64)ts_high_2.u32) << 32) | ((u64)ts_low.u32);
+}
+
+void ctucan_hw_configure_ssp(struct ctucan_hw_priv *priv, bool enable_ssp,
+ bool use_trv_delay, int ssp_offset)
+{
+ union ctu_can_fd_trv_delay_ssp_cfg ssp_cfg;
+
+ ssp_cfg.u32 = 0;
+ if (enable_ssp) {
+ if (use_trv_delay)
+ ssp_cfg.s.ssp_src = SSP_SRC_MEAS_N_OFFSET;
+ else
+ ssp_cfg.s.ssp_src = SSP_SRC_OFFSET;
+ } else {
+ ssp_cfg.s.ssp_src = SSP_SRC_NO_SSP;
+ }
+
+ ssp_cfg.s.ssp_offset = (uint32_t)ssp_offset;
+ priv->write_reg(priv, CTU_CAN_FD_TRV_DELAY, ssp_cfg.u32);
+}
diff --git a/drivers/net/can/ctucanfd/ctucanfd_hw.h b/drivers/net/can/ctucanfd/ctucanfd_hw.h
new file mode 100644
index 000000000000..7d562f41ca52
--- /dev/null
+++ b/drivers/net/can/ctucanfd/ctucanfd_hw.h
@@ -0,0 +1,935 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*******************************************************************************
+ *
+ * CTU CAN FD IP Core
+ *
+ * Copyright (C) 2015-2018 Ondrej Ille <ondrej.ille@gmail.com> FEE CTU
+ * Copyright (C) 2018-2020 Ondrej Ille <ondrej.ille@gmail.com> self-funded
+ * Copyright (C) 2018-2019 Martin Jerabek <martin.jerabek01@gmail.com> FEE CTU
+ * Copyright (C) 2018-2020 Pavel Pisa <pisa@cmp.felk.cvut.cz> FEE CTU/self-funded
+ *
+ * Project advisors:
+ * Jiri Novak <jnovak@fel.cvut.cz>
+ * Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ *
+ * Department of Measurement (http://meas.fel.cvut.cz/)
+ * Faculty of Electrical Engineering (http://www.fel.cvut.cz)
+ * Czech Technical University (http://www.cvut.cz/)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#ifndef __CTUCANFD_HW__
+#define __CTUCANFD_HW__
+
+#include <asm/byteorder.h>
+
+#if defined(__LITTLE_ENDIAN_BITFIELD) == defined(__BIG_ENDIAN_BITFIELD)
+# error __BIG_ENDIAN_BITFIELD or __LITTLE_ENDIAN_BITFIELD must be defined.
+#endif
+
+#include "ctucanfd_regs.h"
+#include "ctucanfd_frame.h"
+
+#define CTU_CAN_FD_RETR_MAX 15
+
+#define CTU_CAN_FD_FILTER_A 0
+#define CTU_CAN_FD_FILTER_B 1
+#define CTU_CAN_FD_FILTER_C 2
+
+#define CTU_CAN_FD_TXT_BUFFER_COUNT 4
+
+#define CTU_CAN_FD_TXT_BUFFER_1 0
+#define CTU_CAN_FD_TXT_BUFFER_2 1
+#define CTU_CAN_FD_TXT_BUFFER_3 2
+#define CTU_CAN_FD_TXT_BUFFER_4 3
+
+/*
+ * Status macros -> pass "ctu_can_get_status" result
+ */
+
+/* True if Core is transceiver of current frame */
+#define CTU_CAN_FD_IS_TRANSMITTER(stat) (!!(stat).ts)
+
+/* True if Core is receiver of current frame */
+#define CTU_CAN_FD_IS_RECEIVER(stat) (!!(stat).s.rxs)
+
+/* True if Core is idle (integrating or interfame space) */
+#define CTU_CAN_FD_IS_IDLE(stat) (!!(stat).s.idle)
+
+/* True if Core is transmitting error frame */
+#define CTU_CAN_FD_ERR_FRAME(stat) (!!(stat).s.eft)
+
+/* True if Error warning limit was reached */
+#define CTU_CAN_FD_EWL(stat) (!!(stat).s.ewl)
+
+/* True if at least one TXT Buffer is empty */
+#define CTU_CAN_FD_TXTNF(stat) (!!(stat).s.txnf)
+
+/* True if data overrun flag of RX Buffer occurred */
+#define CTU_CAN_FD_DATA_OVERRUN(stat) (!!(stat).s.dor)
+
+/* True if RX Buffer is not empty */
+#define CTU_CAN_FD_RX_BUF_NEMPTY(stat) (!!(stat).s.rxne)
+
+/*
+ * Interrupt macros -> pass "ctu_can_fd_int_sts" result
+ */
+
+/* Frame reveived interrupt */
+#define CTU_CAN_FD_RX_INT(int_stat) (!!(int_stat).s.rxi)
+
+/* Frame transceived interrupt */
+#define CTU_CAN_FD_TX_INT(int_stat) (!!(int_stat).s.txi)
+
+/* Error warning limit reached interrupt */
+#define CTU_CAN_FD_EWL_INT(int_stat) (!!(int_stat).s.ewli)
+
+/* RX Buffer data overrun interrupt */
+#define CTU_CAN_FD_OVERRUN_INT(int_stat) (!!(int_stat).s.doi)
+
+/* Fault confinement changed interrupt */
+#define CTU_CAN_FD_FAULT_STATE_CHANGED_INT(int_stat) (!!(int_stat).s.fcsi)
+
+/* Error frame transmission started interrupt */
+#define CTU_CAN_FD_BUS_ERROR_INT(int_stat) (!!(int_stat).s.bei)
+
+/* Event logger finished interrupt */
+#define CTU_CAN_FD_LOGGER_FIN_INT(int_stat) (!!(int_stat).s.lfi)
+
+/* RX Buffer full interrupt */
+#define CTU_CAN_FD_RX_FULL_INT(int_stat) (!!(int_stat).s.rxfi)
+
+/* Bit-rate shifted interrupt */
+#define CTU_CAN_FD_BIT_RATE_SHIFT_INT(int_stat) (!!(int_stat).s.bsi)
+
+/* Receive buffer not empty interrupt */
+#define CTU_CAN_FD_RX_BUF_NEPMTY_INT(int_stat) (!!(int_stat).s.rbnei)
+
+/* TX Buffer received HW command interrupt */
+#define CTU_CAN_FD_TXT_BUF_HWCMD_INT(int_stat) (!!(int_stat).s.txbhci)
+
+static inline bool CTU_CAN_FD_INT_ERROR(union ctu_can_fd_int_stat i)
+{
+ return i.s.ewli || i.s.doi || i.s.fcsi || i.s.ali;
+}
+
+struct ctucan_hw_priv {
+ void __iomem *mem_base;
+ u32 (*read_reg)(struct ctucan_hw_priv *priv,
+ enum ctu_can_fd_can_registers reg);
+ void (*write_reg)(struct ctucan_hw_priv *priv,
+ enum ctu_can_fd_can_registers reg, u32 val);
+};
+
+void ctucan_hw_write32(struct ctucan_hw_priv *priv,
+ enum ctu_can_fd_can_registers reg, u32 val);
+void ctucan_hw_write32_be(struct ctucan_hw_priv *priv,
+ enum ctu_can_fd_can_registers reg, u32 val);
+u32 ctucan_hw_read32(struct ctucan_hw_priv *priv,
+ enum ctu_can_fd_can_registers reg);
+u32 ctucan_hw_read32_be(struct ctucan_hw_priv *priv,
+ enum ctu_can_fd_can_registers reg);
+
+/**
+ * ctucan_hw_check_access - Checks whether the core is mapped correctly
+ * at it's base address.
+ *
+ * @priv: Private info
+ *
+ * Return: true if the core is accessible correctly, false otherwise.
+ */
+bool ctucan_hw_check_access(struct ctucan_hw_priv *priv);
+
+/**
+ * ctucan_hw_get_version - Returns version of CTU CAN FD IP Core.
+ *
+ * @priv: Private info
+ *
+ * Return: IP Core version in format major*10 + minor
+ */
+u32 ctucan_hw_get_version(struct ctucan_hw_priv *priv);
+
+/**
+ * ctucan_hw_enable - Enables/disables the operation of CTU CAN FD Core.
+ *
+ * If disabled, the Core will never start transmitting on the CAN bus,
+ * nor receiving.
+ *
+ * @priv: Private info
+ * @enable: Enable/disable the core.
+ */
+void ctucan_hw_enable(struct ctucan_hw_priv *priv, bool enable);
+
+/**
+ * ctucan_hw_reset - Resets the CTU CAN FD Core.
+ *
+ * NOTE: After resetting, you must wait until ctucan_hw_check_access()
+ * succeeds!
+ *
+ * @priv: Private info
+ */
+void ctucan_hw_reset(struct ctucan_hw_priv *priv);
+
+/**
+ * ctucan_hw_set_ret_limit - Set retransmit limit for sent messages
+ *
+ * Configures CTU CAN FD Core to limit the amount of retransmit attempts after
+ * occurrence of error (Error frame, Arbitration lost). If retransmit limit is
+ * disabled, the Core will attempt to retransmit inifinitely. If retransmit
+ * limit is reached, the Core will finish and according TXT buffer will end up
+ * in TX Error state.
+ *
+ * @priv: Private info
+ * @enable: Enable/disable the retransmit limitation
+ * @limit: Number to which limit the retransmission (1-CTU_CAN_FD_RETR_MAX)
+ * Return: True if set correctly. False if "limit" is too high.
+ */
+bool ctucan_hw_set_ret_limit(struct ctucan_hw_priv *priv, bool enable,
+ u8 limit);
+
+/**
+ * ctucan_hw_set_mode_reg - Configures CTU CAN FD Core for special operating
+ * modes by access to MODEregister.
+ *
+ * Following flags from "mode" are not configured by this function:
+ * CAN_CTRLMODE_ONE_SHOT, CAN_CTRLMODE_BERR_REPORTING.
+ *
+ * Following flags are configured:
+ *
+ * CAN_CTRLMODE_LOOPBACK - Bit loopback mode. Every dominant bit is
+ * re-routed internally and not send on the bus.
+ *
+ * CAN_CTRLMODE_LISTENONLY - No frame is transmitted, no dominant bit is
+ * sent on the bus.
+ *
+ * CAN_CTRLMODE_3_SAMPLES - Tripple sampling mode
+ *
+ * CAN_CTRLMODE_FD - Flexible data-rate support. When not set, Core
+ * does not accept CAN FD Frames and interprets,
+ * them as form error. Capability to transmit
+ * CAN FD Frames is not affected by this setting.
+ *
+ * CAN_CTRLMODE_PRESUME_ACK - When set, Core does not require dominant bit
+ * in ACK field to consider the transmission as
+ * valid.
+ *
+ * CAN_CTRLMODE_FD_NON_ISO - When set, the Core transmits the frames
+ * according to NON-ISO FD standard.
+ *
+ * @priv: Private info
+ * @mode: CAN mode to be set to on the Core.
+ */
+void ctucan_hw_set_mode_reg(struct ctucan_hw_priv *priv,
+ const struct can_ctrlmode *mode);
+
+/**
+ * ctucan_hw_rel_rx_buf - Gives command to CTU CAN FD Core to erase
+ * and reset the RX FIFO.
+ *
+ * This action is finished immediately and does not need waiting.
+ *
+ * @priv: Private info
+ */
+void ctucan_hw_rel_rx_buf(struct ctucan_hw_priv *priv);
+
+/**
+ * ctucan_hw_clr_overrun_flag - Gives command to CTU CAN FD Core to clear
+ * the Data overrun flag on the RX FIFO Buffer.
+ *
+ * @priv: Private info
+ */
+void ctucan_hw_clr_overrun_flag(struct ctucan_hw_priv *priv);
+
+/**
+ * ctu_can_get_status - Returns mode/status vector of CTU CAN FD Core.
+ *
+ * @priv: Private info
+ * Return: Mode/status structure with multiple mode flags.
+ */
+static inline union ctu_can_fd_status
+ ctu_can_get_status(struct ctucan_hw_priv *priv)
+{
+ /* MODE and STATUS are within the same word */
+ union ctu_can_fd_status res;
+
+ res.u32 = priv->read_reg(priv, CTU_CAN_FD_STATUS);
+ return res;
+}
+
+/**
+ * ctucan_hw_is_enabled - Test if core is enabled..
+ *
+ * @priv: Private info
+ *
+ * Return: Return true if core is in enabled/active state..
+ */
+static inline bool ctucan_hw_is_enabled(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_mode_settings reg;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_MODE);
+ return reg.s.ena == CTU_CAN_ENABLED;
+}
+
+/**
+ * ctu_can_fd_int_sts - Reads the interrupt status vector from CTU CAN FD Core.
+ *
+ * @priv: Private info
+ * Return: Interrupt status vector.
+ */
+static inline union ctu_can_fd_int_stat
+ ctu_can_fd_int_sts(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_int_stat res;
+
+ res.u32 = priv->read_reg(priv, CTU_CAN_FD_INT_STAT);
+ return res;
+}
+
+/**
+ * ctucan_hw_int_clr - Clears the interrupts from CTU CAN FD Core.
+ *
+ * @priv: Private info
+ * @mask: Mask of interrupts which should be cleared.
+ */
+static inline void ctucan_hw_int_clr(struct ctucan_hw_priv *priv,
+ union ctu_can_fd_int_stat mask)
+{
+ priv->write_reg(priv, CTU_CAN_FD_INT_STAT, mask.u32);
+}
+
+/**
+ * ctucan_hw_int_ena_set - Sets enable interrupt bits.
+ *
+ * @priv: Private info
+ * @mask: Mask of interrupts which should be disabled.
+ */
+static inline void ctucan_hw_int_ena_set(struct ctucan_hw_priv *priv,
+ union ctu_can_fd_int_stat mask)
+{
+ priv->write_reg(priv, CTU_CAN_FD_INT_ENA_SET, mask.u32);
+}
+
+/**
+ * ctucan_hw_int_ena_clr - Clears enable interrupt bits.
+ *
+ * @priv: Private info
+ * @mask: Mask of interrupts which should be disabled.
+ */
+static inline void ctucan_hw_int_ena_clr(struct ctucan_hw_priv *priv,
+ union ctu_can_fd_int_stat mask)
+{
+ priv->write_reg(priv, CTU_CAN_FD_INT_ENA_CLR, mask.u32);
+}
+
+/**
+ * ctucan_hw_int_ena - Enable/Disable interrupts of CTU CAN FD Core.
+ *
+ * @priv: Private info
+ * @mask: Mask of interrupts which should be enabled/disabled.
+ * @val: 0 - disable, 1 - enable the interrupt.
+ */
+void ctucan_hw_int_ena(struct ctucan_hw_priv *priv,
+ union ctu_can_fd_int_stat mask,
+ union ctu_can_fd_int_stat val);
+
+/**
+ * ctucan_hw_int_mask_set - Mask interrupts of CTU CAN FD Core.
+ *
+ * @priv: Private info
+ * @mask: Mask of interrupts which should be masked.
+ */
+static inline void ctucan_hw_int_mask_set(struct ctucan_hw_priv *priv,
+ union ctu_can_fd_int_stat mask)
+{
+ priv->write_reg(priv, CTU_CAN_FD_INT_MASK_SET, mask.u32);
+}
+
+/**
+ * ctucan_hw_int_mask_clr - Unmask interrupts of CTU CAN FD Core.
+ *
+ * @priv: Private info
+ * @mask: Mask of interrupts which should be unmasked.
+ */
+static inline void ctucan_hw_int_mask_clr(struct ctucan_hw_priv *priv,
+ union ctu_can_fd_int_stat mask)
+{
+ priv->write_reg(priv, CTU_CAN_FD_INT_MASK_CLR, mask.u32);
+}
+
+/**
+ * ctucan_hw_int_mask - Mask/Unmask interrupts of CTU CAN FD Core.
+ *
+ * @priv: Private info
+ * @mask: Mask of interrupts which should be enabled/disabled.
+ * @val: 0 - unmask, 1 - mask the interrupt.
+ */
+void ctucan_hw_int_mask(struct ctucan_hw_priv *priv,
+ union ctu_can_fd_int_stat mask,
+ union ctu_can_fd_int_stat val);
+
+/**
+ * ctucan_hw_set_mode - Set the modes of CTU CAN FD IP Core.
+ *
+ *All flags from "ctucan_hw_set_mode_reg" are configured,
+ * plus CAN_CTRLMODE_ONE_SHOT, CAN_CTRLMODE_BERR_REPORTING,
+ * which are configured via "retransmit limit" and enabling error interrupts.
+ *
+ * @priv: Private info
+ * @mode: Mode of the controller from Socket CAN.
+ */
+void ctucan_hw_set_mode(struct ctucan_hw_priv *priv,
+ const struct can_ctrlmode *mode);
+
+/**
+ * ctucan_hw_set_nom_bittiming - Set Nominal bit timing of CTU CAN FD Core.
+ *
+ * NOTE: phase_seg1 and prop_seg may be modified if phase_seg1 > 63
+ * This is because in Linux, the constraints are only
+ * on phase_seg1+prop_seg.
+ *
+ * @priv: Private info
+ * @nbt: Nominal bit timing settings of CAN Controller.
+ */
+void ctucan_hw_set_nom_bittiming(struct ctucan_hw_priv *priv,
+ struct can_bittiming *nbt);
+
+/**
+ * ctucan_hw_set_data_bittiming - Set Data bit timing of CTU CAN FD Core.
+ *
+ * NOTE: phase_seg1 and prop_seg may be modified if phase_seg1 > 63
+ * This is because in Linux, the constraints are only
+ * on phase_seg1+prop_seg.
+ *
+ * @priv: Private info
+ * @dbt: Data bit timing settings of CAN Controller.
+ */
+void ctucan_hw_set_data_bittiming(struct ctucan_hw_priv *priv,
+ struct can_bittiming *dbt);
+
+/**
+ * ctucan_hw_set_err_limits - Set limits for error warning and passive
+ * transition
+ *
+ * Set error limit when CTU CAN FD Core should transfer to Error warning
+ * and error passive states. If any of RX/TX counters reach this value
+ * according state is changed. By default these counters are set as in
+ * CAN Standard (96, 128).
+ *
+ * @priv: Private info
+ * @ewl: Error warning limit
+ * @erp: Error passive limit
+ */
+void ctucan_hw_set_err_limits(struct ctucan_hw_priv *priv, u8 ewl, u8 erp);
+
+/**
+ * ctucan_hw_set_def_err_limits - Set default error limits
+ * to the CTU CAN FD Core.
+ *
+ * @priv: Private info
+ */
+static inline void ctucan_hw_set_def_err_limits(struct ctucan_hw_priv *priv)
+{
+ ctucan_hw_set_err_limits(priv, 96, 128);
+}
+
+/**
+ * ctucan_hw_read_err_ctrs - Read TX/RX error counters of CTU CAN FD IP Core.
+ *
+ * @priv: Private info
+ * @ctr: Pointer to error counter structure to fill
+ */
+void ctucan_hw_read_err_ctrs(struct ctucan_hw_priv *priv,
+ struct can_berr_counter *ctr);
+
+/**
+ * ctucan_hw_read_nom_errs - Read special error counter which returns number
+ * of Errors which were detected during Nominal Bit-rate.
+ *
+ * @priv: Private info
+ * Return: Number of Error frames detected during Nominal Bit-rate
+ */
+static inline u16 ctucan_hw_read_nom_errs(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_err_norm_err_fd reg;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_ERR_NORM);
+ return reg.s.err_norm_val;
+}
+
+/**
+ * ctucan_hw_erase_nom_errs - Give command to CTU CAN FD Core to erase
+ * the nominal error counter.
+ *
+ * @priv: Private info
+ */
+static inline void ctucan_hw_erase_nom_errs(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_ctr_pres reg;
+
+ reg.u32 = 0;
+ reg.s.enorm = 1;
+ priv->write_reg(priv, CTU_CAN_FD_CTR_PRES, reg.u32);
+}
+
+/**
+ * ctucan_hw_read_fd_errs - Read special error counter which returns number
+ * of Errors which were detected during Data Bit-rate.
+ *
+ * @priv: Private info
+ * Return: Number of Error frames detected during Data Bit-rate
+ */
+static inline u16 ctucan_hw_read_fd_errs(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_err_norm_err_fd reg;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_ERR_NORM);
+ return reg.s.err_fd_val;
+}
+
+/**
+ * ctucan_hw_erase_fd_errs - Give command to CTU CAN FD Core to erase the Data
+ * error counter.
+ *
+ * @priv: Private info
+ */
+static inline void ctucan_hw_erase_fd_errs(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_ctr_pres reg;
+
+ reg.u32 = 0;
+ reg.s.efd = 1;
+ priv->write_reg(priv, CTU_CAN_FD_CTR_PRES, reg.u32);
+}
+
+/**
+ * ctucan_hw_read_error_state - Read fault confinement state of CTU CAN FD Core
+ * (determined by TX/RX Counters).
+ *
+ * @priv: Private info
+ * Return: Error state of the CTU CAN FD Core.
+ */
+enum can_state ctucan_hw_read_error_state(struct ctucan_hw_priv *priv);
+
+/**
+ * ctucan_hw_set_err_ctrs - Set value to TX/RX error counters
+ * of CTU CAN FD Core.
+ *
+ * @priv: Private info
+ * @ctr: Value to be set into counters
+ * Return: Error state of the CTU CAN FD Core.
+ */
+void ctucan_hw_set_err_ctrs(struct ctucan_hw_priv *priv,
+ const struct can_berr_counter *ctr);
+
+/**
+ * ctu_can_fd_read_err_capt_alc - Read core captured last error or arbitration
+ * lost reason.
+ *
+ * @priv: Private info
+ * Return: Error state of the CTU CAN FD.
+ */
+static inline union ctu_can_fd_err_capt_alc
+ ctu_can_fd_read_err_capt_alc(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_err_capt_alc res;
+
+ res.u32 = priv->read_reg(priv, CTU_CAN_FD_ERR_CAPT);
+ return res;
+}
+
+/**
+ * ctucan_hw_get_mask_filter_support - Check Mask filters support
+ * of given filter.
+ *
+ * @priv: Private info
+ * @fnum: Filter number.
+ * Return: True if filter is present and can be used, False otherwise.
+ */
+bool ctucan_hw_get_mask_filter_support(struct ctucan_hw_priv *priv, u8 fnum);
+
+/**
+ * ctucan_hw_get_range_filter_support - Check Range filter support
+ * of given filter.
+ *
+ * @priv: Private info
+ * Return: True if Range filter is present and can be used, False otherwise.
+ */
+bool ctucan_hw_get_range_filter_support(struct ctucan_hw_priv *priv);
+
+/**
+ * ctucan_hw_set_mask_filter - Configure mask filter of CTU CAN FD Core.
+ *
+ * @priv: Private info
+ * @fnum: Filter number.
+ * @enable: True if filter should be enabled.
+ * @filter: Filter configuration.
+ * Return: True if mask filter was configured properly, false otherwise.
+ */
+bool ctucan_hw_set_mask_filter(struct ctucan_hw_priv *priv, u8 fnum,
+ bool enable, const struct can_filter *filter);
+
+/**
+ * ctucan_hw_set_range_filter - Configure range filter of CTU CAN FD Core.
+ *
+ * An identifier of RX Frame will pass the Range filter if its decimal value
+ * is between lower and upper threshold of range filter.
+ *
+ * @priv: Private info
+ * @low_th: Lower threshold of identifiers which should be accepted
+ * @high_th: Upper threshold of identifiers which should be accepted
+ * @enable: Enable the range filter.
+ */
+void ctucan_hw_set_range_filter(struct ctucan_hw_priv *priv, canid_t low_th,
+ canid_t high_th, bool enable);
+
+/**
+ * ctucan_hw_get_rx_fifo_size - Get size of the RX FIFO Buffer
+ * of CTU CAN FD Core.
+ *
+ * @priv: Private info
+ * Return: Size of the RX Buffer in words (32 bit)
+ */
+static inline u16 ctucan_hw_get_rx_fifo_size(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_rx_mem_info reg;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_RX_MEM_INFO);
+ return reg.s.rx_buff_size;
+}
+
+/**
+ * ctucan_hw_get_rx_fifo_mem_free - Get number of free words in RX FIFO Buffer
+ * of CTU CAN FD Core.
+ *
+ * @priv: Private info
+ * Return: Number of free words (32 bit) in RX Buffer.
+ */
+static inline u16 ctucan_hw_get_rx_fifo_mem_free(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_rx_mem_info reg;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_RX_MEM_INFO);
+ return reg.s.rx_mem_free;
+}
+
+/**
+ * ctucan_hw_is_rx_fifo_empty - Check if RX FIFO Buffer is empty.
+ *
+ * @priv: Private info
+ * Return: True if empty, false otherwise.
+ */
+static inline bool ctucan_hw_is_rx_fifo_empty(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_rx_status_rx_settings reg;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_RX_STATUS);
+ return reg.s.rxe;
+}
+
+/**
+ * ctucan_hw_is_rx_fifo_full - Check if RX FIFO Buffer is full.
+ *
+ * @priv: Private info
+ * Return: True if Full, false otherwise.
+ */
+static inline bool ctucan_hw_is_rx_fifo_full(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_rx_status_rx_settings reg;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_RX_STATUS);
+ return reg.s.rxf;
+}
+
+/**
+ * ctucan_hw_get_rx_frame_count - Get number of CAN Frames stored in RX Buffer
+ * of CTU CAN FD Core.
+ *
+ * @priv: Private info
+ * Return: True if Full, false otherwise.
+ */
+static inline u16 ctucan_hw_get_rx_frame_count(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_rx_status_rx_settings reg;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_RX_STATUS);
+ return reg.s.rxfrc;
+}
+
+/**
+ * ctucan_hw_set_rx_tsop - Set timestamp option on RX Frame.
+ *
+ * @priv: Private info
+ * @val: Timestamp option settings.
+ */
+void ctucan_hw_set_rx_tsop(struct ctucan_hw_priv *priv,
+ enum ctu_can_fd_rx_settings_rtsop val);
+
+/**
+ * ctu_can_fd_read_rx_ffw - Reads the first word of CAN Frame from RX FIFO
+ * Buffer.
+ *
+ * @priv: Private info
+ *
+ * Return: The firts word of received frame
+ */
+static inline union ctu_can_fd_frame_format_w
+ ctu_can_fd_read_rx_ffw(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_frame_format_w ffw;
+
+ ffw.u32 = priv->read_reg(priv, CTU_CAN_FD_RX_DATA);
+ return ffw;
+}
+
+/**
+ * ctucan_hw_read_rx_word - Reads one word of CAN Frame from RX FIFO Buffer.
+ *
+ * @priv: Private info
+ *
+ * Return: One word of received frame
+ */
+static inline u32 ctucan_hw_read_rx_word(struct ctucan_hw_priv *priv)
+{
+ return priv->read_reg(priv, CTU_CAN_FD_RX_DATA);
+}
+
+/**
+ * ctucan_hw_read_rx_frame - Reads CAN Frame from RX FIFO Buffer and stores it
+ * to a buffer.
+ *
+ * @priv: Private info
+ * @data: Pointer to buffer where the CAN Frame should be stored.
+ * @ts: Pointer to u64 where RX Timestamp should be stored.
+ */
+void ctucan_hw_read_rx_frame(struct ctucan_hw_priv *priv,
+ struct canfd_frame *data, u64 *ts);
+
+/**
+ * ctucan_hw_read_rx_frame_ffw - Reads rest of CAN Frame from RX FIFO Buffer
+ * and stores it to a buffer.
+ *
+ * @priv: Private info
+ * @cf: Pointer to buffer where the CAN Frame should be stored.
+ * @ts: Pointer to u64 where RX Timestamp should be stored.
+ * @ffw: Already read the first frame control word by the caller
+ */
+void ctucan_hw_read_rx_frame_ffw(struct ctucan_hw_priv *priv,
+ struct canfd_frame *cf, u64 *ts,
+ union ctu_can_fd_frame_format_w ffw);
+
+/**
+ * ctucan_hw_get_tx_status - Returns status of TXT Buffer.
+ *
+ * @priv: Private info
+ * @buf: TXT Buffer index (1 to CTU_CAN_FD_TXT_BUFFER_COUNT)
+ * Return: Status of the TXT Buffer.
+ */
+enum ctu_can_fd_tx_status_tx1s
+ ctucan_hw_get_tx_status(struct ctucan_hw_priv *priv, u8 buf);
+
+/**
+ * ctucan_hw_is_txt_buf_accessible - Checks if TXT Buffer is accessible
+ * and can be written to.
+ *
+ * @priv: Private info
+ * @buf: TXT Buffer index (1 to CTU_CAN_FD_TXT_BUFFER_COUNT)
+ * Return: Status of the TXT Buffer.
+ */
+bool ctucan_hw_is_txt_buf_accessible(struct ctucan_hw_priv *priv, u8 buf);
+
+/**
+ * ctucan_hw_txt_buf_give_command - Give command to TXT Buffer
+ * of CTU CAN FD Core.
+ *
+ * @priv: Private info
+ * @cmd: Command to issue for given Tx buffer.
+ * @buf: TXT Buffer index (1 to CTU_CAN_FD_TXT_BUFFER_COUNT)
+ */
+static inline void ctucan_hw_txt_buf_give_command(struct ctucan_hw_priv *priv,
+ union ctu_can_fd_tx_command cmd, u8 buf)
+{
+ union ctu_can_fd_tx_command reg;
+
+ reg.u32 = 0;
+ reg.s.txb1 = 1;
+
+ reg.u32 <<= buf - CTU_CAN_FD_TXT_BUFFER_1;
+ reg.u32 |= cmd.u32;
+
+ priv->write_reg(priv, CTU_CAN_FD_TX_COMMAND, reg.u32);
+}
+
+/**
+ * ctucan_hw_txt_set_empty - Give "set_empty" command to TXT Buffer.
+ *
+ * @priv: Private info
+ * @buf: TXT Buffer index (1 to CTU_CAN_FD_TXT_BUFFER_COUNT)
+ */
+static inline void ctucan_hw_txt_set_empty(struct ctucan_hw_priv *priv, u8 buf)
+{
+ union ctu_can_fd_tx_command cmd;
+
+ cmd.u32 = 0;
+ cmd.s.txce = 1;
+
+ ctucan_hw_txt_buf_give_command(priv, cmd, buf);
+}
+
+/**
+ * ctucan_hw_txt_set_rdy - Give "set_ready" command to TXT Buffer.
+ *
+ * @priv: Private info
+ * @buf: TXT Buffer index (1 to CTU_CAN_FD_TXT_BUFFER_COUNT)
+ */
+static inline void ctucan_hw_txt_set_rdy(struct ctucan_hw_priv *priv, u8 buf)
+{
+ union ctu_can_fd_tx_command cmd;
+
+ cmd.u32 = 0;
+ cmd.s.txcr = 1;
+
+ ctucan_hw_txt_buf_give_command(priv, cmd, buf);
+}
+
+/**
+ * ctucan_hw_txt_set_abort - Give "set_abort" command to TXT Buffer.
+ *
+ * @priv: Private info
+ * @buf: TXT Buffer index (1 to CTU_CAN_FD_TXT_BUFFER_COUNT)
+ */
+static inline void ctucan_hw_txt_set_abort(struct ctucan_hw_priv *priv, u8 buf)
+{
+ union ctu_can_fd_tx_command cmd;
+
+ cmd.u32 = 0;
+ cmd.s.txca = 1;
+
+ ctucan_hw_txt_buf_give_command(priv, cmd, buf);
+}
+
+/**
+ * ctucan_hw_set_txt_priority - Set priority of TXT Buffers in CTU CAN FD Core.
+ *
+ * @priv: Private info
+ * @prio: Pointer to array with CTU_CAN_FD_TXT_BUFFER_COUNT number
+ * of elements with TXT Buffer priorities.
+ */
+void ctucan_hw_set_txt_priority(struct ctucan_hw_priv *priv, const u8 *prio);
+
+/**
+ * ctucan_hw_insert_frame - Insert CAN FD frame to TXT Buffer
+ * of CTU CAN FD Core.
+ *
+ * @priv: Private info
+ * @data: Pointer to CAN Frame buffer.
+ * @ts: Timestamp when the buffer should be sent.
+ * @buf: Index of TXT Buffer where to insert the CAN Frame.
+ * @isfdf: True if the frame is a FD frame.
+ * Return: True if the frame was inserted successfully, False otherwise.
+ */
+bool ctucan_hw_insert_frame(struct ctucan_hw_priv *priv,
+ const struct canfd_frame *data, u64 ts,
+ u8 buf, bool isfdf);
+
+/**
+ * ctucan_hw_get_tran_delay - Read transceiver delay as measured
+ * by CTU CAN FD Core.
+ *
+ * Note that transceiver delay can be measured only after at least
+ * one CAN FD Frame with BRS bit was sent since the last re-start of the Core.
+ *
+ * @priv: Private info
+ * Return: True if the frame was inserted successfully, False otherwise.
+ */
+static inline u16 ctucan_hw_get_tran_delay(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_trv_delay_ssp_cfg reg;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_TRV_DELAY);
+ return reg.s.trv_delay_value;
+}
+
+/**
+ * ctucan_hw_get_tx_frame_ctr - Read number of transmitted CAN/CAN FD Frames
+ * by CTU CAN FD Core.
+ *
+ * @priv: Private info
+ * Return: Number of received CAN/CAN FD frames.
+ */
+static inline u32 ctucan_hw_get_tx_frame_ctr(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_tx_fr_ctr reg;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_TX_FR_CTR);
+ return reg.s.tx_fr_ctr_val;
+}
+
+/**
+ * ctucan_hw_get_rx_frame_ctr - Read number of received CAN/CAN FD Frames
+ * by CTU CAN FD Core.
+ *
+ * @priv: Private info
+ * Return: Number of received CAN/CAN FD frames.
+ */
+static inline u32 ctucan_hw_get_rx_frame_ctr(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_rx_fr_ctr reg;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_RX_FR_CTR);
+ return reg.s.rx_fr_ctr_val;
+}
+
+/**
+ * ctu_can_fd_read_debug_info - Returns debug information of CTU CAN FD Core.
+ *
+ * @priv: Private info
+ * Return: Content of Debug register.
+ */
+static inline union ctu_can_fd_debug_register
+ ctu_can_fd_read_debug_info(struct ctucan_hw_priv *priv)
+{
+ union ctu_can_fd_debug_register reg;
+
+ reg.u32 = priv->read_reg(priv, CTU_CAN_FD_DEBUG_REGISTER);
+ return reg;
+}
+
+/**
+ * ctucan_hw_read_timestamp - Read timestamp value which is used internally
+ * by CTU CAN FD Core.
+ *
+ * Reads timestamp twice and checks consistency betwen upper and
+ * lower timestamp word.
+ *
+ * @priv: Private info
+ * Return: Value of timestamp in CTU CAN FD Core
+ */
+u64 ctucan_hw_read_timestamp(struct ctucan_hw_priv *priv);
+
+/**
+ * ctucan_hw_configure_ssp - Configure Secondary sample point usage and
+ * position.
+ *
+ * @priv: Private info
+ * @enable_ssp: Enable Secondary Sampling point. When false, regular sampling
+ * point is used.
+ * @use_trv_delay: Add Transmitter delay to secondary sampling point position.
+ * @ssp_offset: Position of secondary sampling point.
+ */
+void ctucan_hw_configure_ssp(struct ctucan_hw_priv *priv, bool enable_ssp,
+ bool use_trv_delay, int ssp_offset);
+
+extern const struct can_bittiming_const ctu_can_fd_bit_timing_max;
+extern const struct can_bittiming_const ctu_can_fd_bit_timing_data_max;
+
+#endif
diff --git a/drivers/net/can/ctucanfd/ctucanfd_regs.h b/drivers/net/can/ctucanfd/ctucanfd_regs.h
new file mode 100644
index 000000000000..450f4b9fb3c4
--- /dev/null
+++ b/drivers/net/can/ctucanfd/ctucanfd_regs.h
@@ -0,0 +1,971 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*******************************************************************************
+ *
+ * CTU CAN FD IP Core
+ *
+ * Copyright (C) 2015-2018 Ondrej Ille <ondrej.ille@gmail.com> FEE CTU
+ * Copyright (C) 2018-2020 Ondrej Ille <ondrej.ille@gmail.com> self-funded
+ * Copyright (C) 2018-2019 Martin Jerabek <martin.jerabek01@gmail.com> FEE CTU
+ * Copyright (C) 2018-2020 Pavel Pisa <pisa@cmp.felk.cvut.cz> FEE CTU/self-funded
+ *
+ * Project advisors:
+ * Jiri Novak <jnovak@fel.cvut.cz>
+ * Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ *
+ * Department of Measurement (http://meas.fel.cvut.cz/)
+ * Faculty of Electrical Engineering (http://www.fel.cvut.cz)
+ * Czech Technical University (http://www.cvut.cz/)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+/* This file is autogenerated, DO NOT EDIT! */
+
+#ifndef __CTU_CAN_FD_CAN_FD_REGISTER_MAP__
+#define __CTU_CAN_FD_CAN_FD_REGISTER_MAP__
+
+/* CAN_Registers memory map */
+enum ctu_can_fd_can_registers {
+ CTU_CAN_FD_DEVICE_ID = 0x0,
+ CTU_CAN_FD_VERSION = 0x2,
+ CTU_CAN_FD_MODE = 0x4,
+ CTU_CAN_FD_SETTINGS = 0x6,
+ CTU_CAN_FD_STATUS = 0x8,
+ CTU_CAN_FD_COMMAND = 0xc,
+ CTU_CAN_FD_INT_STAT = 0x10,
+ CTU_CAN_FD_INT_ENA_SET = 0x14,
+ CTU_CAN_FD_INT_ENA_CLR = 0x18,
+ CTU_CAN_FD_INT_MASK_SET = 0x1c,
+ CTU_CAN_FD_INT_MASK_CLR = 0x20,
+ CTU_CAN_FD_BTR = 0x24,
+ CTU_CAN_FD_BTR_FD = 0x28,
+ CTU_CAN_FD_EWL = 0x2c,
+ CTU_CAN_FD_ERP = 0x2d,
+ CTU_CAN_FD_FAULT_STATE = 0x2e,
+ CTU_CAN_FD_REC = 0x30,
+ CTU_CAN_FD_TEC = 0x32,
+ CTU_CAN_FD_ERR_NORM = 0x34,
+ CTU_CAN_FD_ERR_FD = 0x36,
+ CTU_CAN_FD_CTR_PRES = 0x38,
+ CTU_CAN_FD_FILTER_A_MASK = 0x3c,
+ CTU_CAN_FD_FILTER_A_VAL = 0x40,
+ CTU_CAN_FD_FILTER_B_MASK = 0x44,
+ CTU_CAN_FD_FILTER_B_VAL = 0x48,
+ CTU_CAN_FD_FILTER_C_MASK = 0x4c,
+ CTU_CAN_FD_FILTER_C_VAL = 0x50,
+ CTU_CAN_FD_FILTER_RAN_LOW = 0x54,
+ CTU_CAN_FD_FILTER_RAN_HIGH = 0x58,
+ CTU_CAN_FD_FILTER_CONTROL = 0x5c,
+ CTU_CAN_FD_FILTER_STATUS = 0x5e,
+ CTU_CAN_FD_RX_MEM_INFO = 0x60,
+ CTU_CAN_FD_RX_POINTERS = 0x64,
+ CTU_CAN_FD_RX_STATUS = 0x68,
+ CTU_CAN_FD_RX_SETTINGS = 0x6a,
+ CTU_CAN_FD_RX_DATA = 0x6c,
+ CTU_CAN_FD_TX_STATUS = 0x70,
+ CTU_CAN_FD_TX_COMMAND = 0x74,
+ CTU_CAN_FD_TX_PRIORITY = 0x78,
+ CTU_CAN_FD_ERR_CAPT = 0x7c,
+ CTU_CAN_FD_ALC = 0x7e,
+ CTU_CAN_FD_TRV_DELAY = 0x80,
+ CTU_CAN_FD_SSP_CFG = 0x82,
+ CTU_CAN_FD_RX_FR_CTR = 0x84,
+ CTU_CAN_FD_TX_FR_CTR = 0x88,
+ CTU_CAN_FD_DEBUG_REGISTER = 0x8c,
+ CTU_CAN_FD_YOLO_REG = 0x90,
+ CTU_CAN_FD_TIMESTAMP_LOW = 0x94,
+ CTU_CAN_FD_TIMESTAMP_HIGH = 0x98,
+ CTU_CAN_FD_TXTB1_DATA_1 = 0x100,
+ CTU_CAN_FD_TXTB1_DATA_2 = 0x104,
+ CTU_CAN_FD_TXTB1_DATA_20 = 0x14c,
+ CTU_CAN_FD_TXTB2_DATA_1 = 0x200,
+ CTU_CAN_FD_TXTB2_DATA_2 = 0x204,
+ CTU_CAN_FD_TXTB2_DATA_20 = 0x24c,
+ CTU_CAN_FD_TXTB3_DATA_1 = 0x300,
+ CTU_CAN_FD_TXTB3_DATA_2 = 0x304,
+ CTU_CAN_FD_TXTB3_DATA_20 = 0x34c,
+ CTU_CAN_FD_TXTB4_DATA_1 = 0x400,
+ CTU_CAN_FD_TXTB4_DATA_2 = 0x404,
+ CTU_CAN_FD_TXTB4_DATA_20 = 0x44c,
+};
+
+
+/* Register descriptions: */
+union ctu_can_fd_device_id_version {
+ uint32_t u32;
+ struct ctu_can_fd_device_id_version_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* DEVICE_ID */
+ uint32_t device_id : 16;
+ /* VERSION */
+ uint32_t ver_minor : 8;
+ uint32_t ver_major : 8;
+#else
+ uint32_t ver_major : 8;
+ uint32_t ver_minor : 8;
+ uint32_t device_id : 16;
+#endif
+ } s;
+};
+
+enum ctu_can_fd_device_id_device_id {
+ CTU_CAN_FD_ID = 0xcafd,
+};
+
+union ctu_can_fd_mode_settings {
+ uint32_t u32;
+ struct ctu_can_fd_mode_settings_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* MODE */
+ uint32_t rst : 1;
+ uint32_t lom : 1;
+ uint32_t stm : 1;
+ uint32_t afm : 1;
+ uint32_t fde : 1;
+ uint32_t reserved_6_5 : 2;
+ uint32_t acf : 1;
+ uint32_t tstm : 1;
+ uint32_t reserved_15_9 : 7;
+ /* SETTINGS */
+ uint32_t rtrle : 1;
+ uint32_t rtrth : 4;
+ uint32_t ilbp : 1;
+ uint32_t ena : 1;
+ uint32_t nisofd : 1;
+ uint32_t pex : 1;
+ uint32_t reserved_31_25 : 7;
+#else
+ uint32_t reserved_31_25 : 7;
+ uint32_t pex : 1;
+ uint32_t nisofd : 1;
+ uint32_t ena : 1;
+ uint32_t ilbp : 1;
+ uint32_t rtrth : 4;
+ uint32_t rtrle : 1;
+ uint32_t reserved_15_9 : 7;
+ uint32_t tstm : 1;
+ uint32_t acf : 1;
+ uint32_t reserved_6_5 : 2;
+ uint32_t fde : 1;
+ uint32_t afm : 1;
+ uint32_t stm : 1;
+ uint32_t lom : 1;
+ uint32_t rst : 1;
+#endif
+ } s;
+};
+
+enum ctu_can_fd_mode_lom {
+ LOM_DISABLED = 0x0,
+ LOM_ENABLED = 0x1,
+};
+
+enum ctu_can_fd_mode_stm {
+ STM_DISABLED = 0x0,
+ STM_ENABLED = 0x1,
+};
+
+enum ctu_can_fd_mode_afm {
+ AFM_DISABLED = 0x0,
+ AFM_ENABLED = 0x1,
+};
+
+enum ctu_can_fd_mode_fde {
+ FDE_DISABLE = 0x0,
+ FDE_ENABLE = 0x1,
+};
+
+enum ctu_can_fd_mode_acf {
+ ACF_DISABLED = 0x0,
+ ACF_ENABLED = 0x1,
+};
+
+enum ctu_can_fd_settings_rtrle {
+ RTRLE_DISABLED = 0x0,
+ RTRLE_ENABLED = 0x1,
+};
+
+enum ctu_can_fd_settings_ilbp {
+ INT_LOOP_DISABLED = 0x0,
+ INT_LOOP_ENABLED = 0x1,
+};
+
+enum ctu_can_fd_settings_ena {
+ CTU_CAN_DISABLED = 0x0,
+ CTU_CAN_ENABLED = 0x1,
+};
+
+enum ctu_can_fd_settings_nisofd {
+ ISO_FD = 0x0,
+ NON_ISO_FD = 0x1,
+};
+
+enum ctu_can_fd_settings_pex {
+ PROTOCOL_EXCEPTION_DISABLED = 0x0,
+ PROTOCOL_EXCEPTION_ENABLED = 0x1,
+};
+
+union ctu_can_fd_status {
+ uint32_t u32;
+ struct ctu_can_fd_status_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* STATUS */
+ uint32_t rxne : 1;
+ uint32_t dor : 1;
+ uint32_t txnf : 1;
+ uint32_t eft : 1;
+ uint32_t rxs : 1;
+ uint32_t txs : 1;
+ uint32_t ewl : 1;
+ uint32_t idle : 1;
+ uint32_t reserved_31_8 : 24;
+#else
+ uint32_t reserved_31_8 : 24;
+ uint32_t idle : 1;
+ uint32_t ewl : 1;
+ uint32_t txs : 1;
+ uint32_t rxs : 1;
+ uint32_t eft : 1;
+ uint32_t txnf : 1;
+ uint32_t dor : 1;
+ uint32_t rxne : 1;
+#endif
+ } s;
+};
+
+union ctu_can_fd_command {
+ uint32_t u32;
+ struct ctu_can_fd_command_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ uint32_t reserved_1_0 : 2;
+ /* COMMAND */
+ uint32_t rrb : 1;
+ uint32_t cdo : 1;
+ uint32_t ercrst : 1;
+ uint32_t rxfcrst : 1;
+ uint32_t txfcrst : 1;
+ uint32_t reserved_31_7 : 25;
+#else
+ uint32_t reserved_31_7 : 25;
+ uint32_t txfcrst : 1;
+ uint32_t rxfcrst : 1;
+ uint32_t ercrst : 1;
+ uint32_t cdo : 1;
+ uint32_t rrb : 1;
+ uint32_t reserved_1_0 : 2;
+#endif
+ } s;
+};
+
+union ctu_can_fd_int_stat {
+ uint32_t u32;
+ struct ctu_can_fd_int_stat_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* INT_STAT */
+ uint32_t rxi : 1;
+ uint32_t txi : 1;
+ uint32_t ewli : 1;
+ uint32_t doi : 1;
+ uint32_t fcsi : 1;
+ uint32_t ali : 1;
+ uint32_t bei : 1;
+ uint32_t ofi : 1;
+ uint32_t rxfi : 1;
+ uint32_t bsi : 1;
+ uint32_t rbnei : 1;
+ uint32_t txbhci : 1;
+ uint32_t reserved_31_12 : 20;
+#else
+ uint32_t reserved_31_12 : 20;
+ uint32_t txbhci : 1;
+ uint32_t rbnei : 1;
+ uint32_t bsi : 1;
+ uint32_t rxfi : 1;
+ uint32_t ofi : 1;
+ uint32_t bei : 1;
+ uint32_t ali : 1;
+ uint32_t fcsi : 1;
+ uint32_t doi : 1;
+ uint32_t ewli : 1;
+ uint32_t txi : 1;
+ uint32_t rxi : 1;
+#endif
+ } s;
+};
+
+union ctu_can_fd_int_ena_set {
+ uint32_t u32;
+ struct ctu_can_fd_int_ena_set_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* INT_ENA_SET */
+ uint32_t int_ena_set : 12;
+ uint32_t reserved_31_12 : 20;
+#else
+ uint32_t reserved_31_12 : 20;
+ uint32_t int_ena_set : 12;
+#endif
+ } s;
+};
+
+union ctu_can_fd_int_ena_clr {
+ uint32_t u32;
+ struct ctu_can_fd_int_ena_clr_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* INT_ENA_CLR */
+ uint32_t int_ena_clr : 12;
+ uint32_t reserved_31_12 : 20;
+#else
+ uint32_t reserved_31_12 : 20;
+ uint32_t int_ena_clr : 12;
+#endif
+ } s;
+};
+
+union ctu_can_fd_int_mask_set {
+ uint32_t u32;
+ struct ctu_can_fd_int_mask_set_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* INT_MASK_SET */
+ uint32_t int_mask_set : 12;
+ uint32_t reserved_31_12 : 20;
+#else
+ uint32_t reserved_31_12 : 20;
+ uint32_t int_mask_set : 12;
+#endif
+ } s;
+};
+
+union ctu_can_fd_int_mask_clr {
+ uint32_t u32;
+ struct ctu_can_fd_int_mask_clr_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* INT_MASK_CLR */
+ uint32_t int_mask_clr : 12;
+ uint32_t reserved_31_12 : 20;
+#else
+ uint32_t reserved_31_12 : 20;
+ uint32_t int_mask_clr : 12;
+#endif
+ } s;
+};
+
+union ctu_can_fd_btr {
+ uint32_t u32;
+ struct ctu_can_fd_btr_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* BTR */
+ uint32_t prop : 7;
+ uint32_t ph1 : 6;
+ uint32_t ph2 : 6;
+ uint32_t brp : 8;
+ uint32_t sjw : 5;
+#else
+ uint32_t sjw : 5;
+ uint32_t brp : 8;
+ uint32_t ph2 : 6;
+ uint32_t ph1 : 6;
+ uint32_t prop : 7;
+#endif
+ } s;
+};
+
+union ctu_can_fd_btr_fd {
+ uint32_t u32;
+ struct ctu_can_fd_btr_fd_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* BTR_FD */
+ uint32_t prop_fd : 6;
+ uint32_t reserved_6 : 1;
+ uint32_t ph1_fd : 5;
+ uint32_t reserved_12 : 1;
+ uint32_t ph2_fd : 5;
+ uint32_t reserved_18 : 1;
+ uint32_t brp_fd : 8;
+ uint32_t sjw_fd : 5;
+#else
+ uint32_t sjw_fd : 5;
+ uint32_t brp_fd : 8;
+ uint32_t reserved_18 : 1;
+ uint32_t ph2_fd : 5;
+ uint32_t reserved_12 : 1;
+ uint32_t ph1_fd : 5;
+ uint32_t reserved_6 : 1;
+ uint32_t prop_fd : 6;
+#endif
+ } s;
+};
+
+union ctu_can_fd_ewl_erp_fault_state {
+ uint32_t u32;
+ struct ctu_can_fd_ewl_erp_fault_state_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* EWL */
+ uint32_t ew_limit : 8;
+ /* ERP */
+ uint32_t erp_limit : 8;
+ /* FAULT_STATE */
+ uint32_t era : 1;
+ uint32_t erp : 1;
+ uint32_t bof : 1;
+ uint32_t reserved_31_19 : 13;
+#else
+ uint32_t reserved_31_19 : 13;
+ uint32_t bof : 1;
+ uint32_t erp : 1;
+ uint32_t era : 1;
+ uint32_t erp_limit : 8;
+ uint32_t ew_limit : 8;
+#endif
+ } s;
+};
+
+union ctu_can_fd_rec_tec {
+ uint32_t u32;
+ struct ctu_can_fd_rec_tec_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* REC */
+ uint32_t rec_val : 9;
+ uint32_t reserved_15_9 : 7;
+ /* TEC */
+ uint32_t tec_val : 9;
+ uint32_t reserved_31_25 : 7;
+#else
+ uint32_t reserved_31_25 : 7;
+ uint32_t tec_val : 9;
+ uint32_t reserved_15_9 : 7;
+ uint32_t rec_val : 9;
+#endif
+ } s;
+};
+
+union ctu_can_fd_err_norm_err_fd {
+ uint32_t u32;
+ struct ctu_can_fd_err_norm_err_fd_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* ERR_NORM */
+ uint32_t err_norm_val : 16;
+ /* ERR_FD */
+ uint32_t err_fd_val : 16;
+#else
+ uint32_t err_fd_val : 16;
+ uint32_t err_norm_val : 16;
+#endif
+ } s;
+};
+
+union ctu_can_fd_ctr_pres {
+ uint32_t u32;
+ struct ctu_can_fd_ctr_pres_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* CTR_PRES */
+ uint32_t ctpv : 9;
+ uint32_t ptx : 1;
+ uint32_t prx : 1;
+ uint32_t enorm : 1;
+ uint32_t efd : 1;
+ uint32_t reserved_31_13 : 19;
+#else
+ uint32_t reserved_31_13 : 19;
+ uint32_t efd : 1;
+ uint32_t enorm : 1;
+ uint32_t prx : 1;
+ uint32_t ptx : 1;
+ uint32_t ctpv : 9;
+#endif
+ } s;
+};
+
+union ctu_can_fd_filter_a_mask {
+ uint32_t u32;
+ struct ctu_can_fd_filter_a_mask_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FILTER_A_MASK */
+ uint32_t bit_mask_a_val : 29;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t bit_mask_a_val : 29;
+#endif
+ } s;
+};
+
+union ctu_can_fd_filter_a_val {
+ uint32_t u32;
+ struct ctu_can_fd_filter_a_val_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FILTER_A_VAL */
+ uint32_t bit_val_a_val : 29;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t bit_val_a_val : 29;
+#endif
+ } s;
+};
+
+union ctu_can_fd_filter_b_mask {
+ uint32_t u32;
+ struct ctu_can_fd_filter_b_mask_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FILTER_B_MASK */
+ uint32_t bit_mask_b_val : 29;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t bit_mask_b_val : 29;
+#endif
+ } s;
+};
+
+union ctu_can_fd_filter_b_val {
+ uint32_t u32;
+ struct ctu_can_fd_filter_b_val_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FILTER_B_VAL */
+ uint32_t bit_val_b_val : 29;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t bit_val_b_val : 29;
+#endif
+ } s;
+};
+
+union ctu_can_fd_filter_c_mask {
+ uint32_t u32;
+ struct ctu_can_fd_filter_c_mask_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FILTER_C_MASK */
+ uint32_t bit_mask_c_val : 29;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t bit_mask_c_val : 29;
+#endif
+ } s;
+};
+
+union ctu_can_fd_filter_c_val {
+ uint32_t u32;
+ struct ctu_can_fd_filter_c_val_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FILTER_C_VAL */
+ uint32_t bit_val_c_val : 29;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t bit_val_c_val : 29;
+#endif
+ } s;
+};
+
+union ctu_can_fd_filter_ran_low {
+ uint32_t u32;
+ struct ctu_can_fd_filter_ran_low_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FILTER_RAN_LOW */
+ uint32_t bit_ran_low_val : 29;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t bit_ran_low_val : 29;
+#endif
+ } s;
+};
+
+union ctu_can_fd_filter_ran_high {
+ uint32_t u32;
+ struct ctu_can_fd_filter_ran_high_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FILTER_RAN_HIGH */
+ uint32_t bit_ran_high_val : 29;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t bit_ran_high_val : 29;
+#endif
+ } s;
+};
+
+union ctu_can_fd_filter_control_filter_status {
+ uint32_t u32;
+ struct ctu_can_fd_filter_control_filter_status_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FILTER_CONTROL */
+ uint32_t fanb : 1;
+ uint32_t fane : 1;
+ uint32_t fafb : 1;
+ uint32_t fafe : 1;
+ uint32_t fbnb : 1;
+ uint32_t fbne : 1;
+ uint32_t fbfb : 1;
+ uint32_t fbfe : 1;
+ uint32_t fcnb : 1;
+ uint32_t fcne : 1;
+ uint32_t fcfb : 1;
+ uint32_t fcfe : 1;
+ uint32_t frnb : 1;
+ uint32_t frne : 1;
+ uint32_t frfb : 1;
+ uint32_t frfe : 1;
+ /* FILTER_STATUS */
+ uint32_t sfa : 1;
+ uint32_t sfb : 1;
+ uint32_t sfc : 1;
+ uint32_t sfr : 1;
+ uint32_t reserved_31_20 : 12;
+#else
+ uint32_t reserved_31_20 : 12;
+ uint32_t sfr : 1;
+ uint32_t sfc : 1;
+ uint32_t sfb : 1;
+ uint32_t sfa : 1;
+ uint32_t frfe : 1;
+ uint32_t frfb : 1;
+ uint32_t frne : 1;
+ uint32_t frnb : 1;
+ uint32_t fcfe : 1;
+ uint32_t fcfb : 1;
+ uint32_t fcne : 1;
+ uint32_t fcnb : 1;
+ uint32_t fbfe : 1;
+ uint32_t fbfb : 1;
+ uint32_t fbne : 1;
+ uint32_t fbnb : 1;
+ uint32_t fafe : 1;
+ uint32_t fafb : 1;
+ uint32_t fane : 1;
+ uint32_t fanb : 1;
+#endif
+ } s;
+};
+
+union ctu_can_fd_rx_mem_info {
+ uint32_t u32;
+ struct ctu_can_fd_rx_mem_info_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* RX_MEM_INFO */
+ uint32_t rx_buff_size : 13;
+ uint32_t reserved_15_13 : 3;
+ uint32_t rx_mem_free : 13;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t rx_mem_free : 13;
+ uint32_t reserved_15_13 : 3;
+ uint32_t rx_buff_size : 13;
+#endif
+ } s;
+};
+
+union ctu_can_fd_rx_pointers {
+ uint32_t u32;
+ struct ctu_can_fd_rx_pointers_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* RX_POINTERS */
+ uint32_t rx_wpp : 12;
+ uint32_t reserved_15_12 : 4;
+ uint32_t rx_rpp : 12;
+ uint32_t reserved_31_28 : 4;
+#else
+ uint32_t reserved_31_28 : 4;
+ uint32_t rx_rpp : 12;
+ uint32_t reserved_15_12 : 4;
+ uint32_t rx_wpp : 12;
+#endif
+ } s;
+};
+
+union ctu_can_fd_rx_status_rx_settings {
+ uint32_t u32;
+ struct ctu_can_fd_rx_status_rx_settings_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* RX_STATUS */
+ uint32_t rxe : 1;
+ uint32_t rxf : 1;
+ uint32_t reserved_3_2 : 2;
+ uint32_t rxfrc : 11;
+ uint32_t reserved_15 : 1;
+ /* RX_SETTINGS */
+ uint32_t rtsop : 1;
+ uint32_t reserved_31_17 : 15;
+#else
+ uint32_t reserved_31_17 : 15;
+ uint32_t rtsop : 1;
+ uint32_t reserved_15 : 1;
+ uint32_t rxfrc : 11;
+ uint32_t reserved_3_2 : 2;
+ uint32_t rxf : 1;
+ uint32_t rxe : 1;
+#endif
+ } s;
+};
+
+enum ctu_can_fd_rx_settings_rtsop {
+ RTS_END = 0x0,
+ RTS_BEG = 0x1,
+};
+
+union ctu_can_fd_rx_data {
+ uint32_t u32;
+ struct ctu_can_fd_rx_data_s {
+ /* RX_DATA */
+ uint32_t rx_data : 32;
+ } s;
+};
+
+union ctu_can_fd_tx_status {
+ uint32_t u32;
+ struct ctu_can_fd_tx_status_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* TX_STATUS */
+ uint32_t tx1s : 4;
+ uint32_t tx2s : 4;
+ uint32_t tx3s : 4;
+ uint32_t tx4s : 4;
+ uint32_t reserved_31_16 : 16;
+#else
+ uint32_t reserved_31_16 : 16;
+ uint32_t tx4s : 4;
+ uint32_t tx3s : 4;
+ uint32_t tx2s : 4;
+ uint32_t tx1s : 4;
+#endif
+ } s;
+};
+
+enum ctu_can_fd_tx_status_tx1s {
+ TXT_RDY = 0x1,
+ TXT_TRAN = 0x2,
+ TXT_ABTP = 0x3,
+ TXT_TOK = 0x4,
+ TXT_ERR = 0x6,
+ TXT_ABT = 0x7,
+ TXT_ETY = 0x8,
+};
+
+union ctu_can_fd_tx_command {
+ uint32_t u32;
+ struct ctu_can_fd_tx_command_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* TX_COMMAND */
+ uint32_t txce : 1;
+ uint32_t txcr : 1;
+ uint32_t txca : 1;
+ uint32_t reserved_7_3 : 5;
+ uint32_t txb1 : 1;
+ uint32_t txb2 : 1;
+ uint32_t txb3 : 1;
+ uint32_t txb4 : 1;
+ uint32_t reserved_31_12 : 20;
+#else
+ uint32_t reserved_31_12 : 20;
+ uint32_t txb4 : 1;
+ uint32_t txb3 : 1;
+ uint32_t txb2 : 1;
+ uint32_t txb1 : 1;
+ uint32_t reserved_7_3 : 5;
+ uint32_t txca : 1;
+ uint32_t txcr : 1;
+ uint32_t txce : 1;
+#endif
+ } s;
+};
+
+union ctu_can_fd_tx_priority {
+ uint32_t u32;
+ struct ctu_can_fd_tx_priority_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* TX_PRIORITY */
+ uint32_t txt1p : 3;
+ uint32_t reserved_3 : 1;
+ uint32_t txt2p : 3;
+ uint32_t reserved_7 : 1;
+ uint32_t txt3p : 3;
+ uint32_t reserved_11 : 1;
+ uint32_t txt4p : 3;
+ uint32_t reserved_31_15 : 17;
+#else
+ uint32_t reserved_31_15 : 17;
+ uint32_t txt4p : 3;
+ uint32_t reserved_11 : 1;
+ uint32_t txt3p : 3;
+ uint32_t reserved_7 : 1;
+ uint32_t txt2p : 3;
+ uint32_t reserved_3 : 1;
+ uint32_t txt1p : 3;
+#endif
+ } s;
+};
+
+union ctu_can_fd_err_capt_alc {
+ uint32_t u32;
+ struct ctu_can_fd_err_capt_alc_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* ERR_CAPT */
+ uint32_t err_pos : 5;
+ uint32_t err_type : 3;
+ uint32_t reserved_15_8 : 8;
+ /* ALC */
+ uint32_t alc_bit : 5;
+ uint32_t alc_id_field : 3;
+ uint32_t reserved_31_24 : 8;
+#else
+ uint32_t reserved_31_24 : 8;
+ uint32_t alc_id_field : 3;
+ uint32_t alc_bit : 5;
+ uint32_t reserved_15_8 : 8;
+ uint32_t err_type : 3;
+ uint32_t err_pos : 5;
+#endif
+ } s;
+};
+
+enum ctu_can_fd_err_capt_err_pos {
+ ERC_POS_SOF = 0x0,
+ ERC_POS_ARB = 0x1,
+ ERC_POS_CTRL = 0x2,
+ ERC_POS_DATA = 0x3,
+ ERC_POS_CRC = 0x4,
+ ERC_POS_ACK = 0x5,
+ ERC_POS_EOF = 0x6,
+ ERC_POS_ERR = 0x7,
+ ERC_POS_OVRL = 0x8,
+ ERC_POS_OTHER = 0x1f,
+};
+
+enum ctu_can_fd_err_capt_err_type {
+ ERC_BIT_ERR = 0x0,
+ ERC_CRC_ERR = 0x1,
+ ERC_FRM_ERR = 0x2,
+ ERC_ACK_ERR = 0x3,
+ ERC_STUF_ERR = 0x4,
+};
+
+enum ctu_can_fd_alc_alc_id_field {
+ ALC_RSVD = 0x0,
+ ALC_BASE_ID = 0x1,
+ ALC_SRR_RTR = 0x2,
+ ALC_IDE = 0x3,
+ ALC_EXTENSION = 0x4,
+ ALC_RTR = 0x5,
+};
+
+union ctu_can_fd_trv_delay_ssp_cfg {
+ uint32_t u32;
+ struct ctu_can_fd_trv_delay_ssp_cfg_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* TRV_DELAY */
+ uint32_t trv_delay_value : 7;
+ uint32_t reserved_15_7 : 9;
+ /* SSP_CFG */
+ uint32_t ssp_offset : 8;
+ uint32_t ssp_src : 2;
+ uint32_t reserved_31_26 : 6;
+#else
+ uint32_t reserved_31_26 : 6;
+ uint32_t ssp_src : 2;
+ uint32_t ssp_offset : 8;
+ uint32_t reserved_15_7 : 9;
+ uint32_t trv_delay_value : 7;
+#endif
+ } s;
+};
+
+enum ctu_can_fd_ssp_cfg_ssp_src {
+ SSP_SRC_MEAS_N_OFFSET = 0x0,
+ SSP_SRC_NO_SSP = 0x1,
+ SSP_SRC_OFFSET = 0x2,
+};
+
+union ctu_can_fd_rx_fr_ctr {
+ uint32_t u32;
+ struct ctu_can_fd_rx_fr_ctr_s {
+ /* RX_FR_CTR */
+ uint32_t rx_fr_ctr_val : 32;
+ } s;
+};
+
+union ctu_can_fd_tx_fr_ctr {
+ uint32_t u32;
+ struct ctu_can_fd_tx_fr_ctr_s {
+ /* TX_FR_CTR */
+ uint32_t tx_fr_ctr_val : 32;
+ } s;
+};
+
+union ctu_can_fd_debug_register {
+ uint32_t u32;
+ struct ctu_can_fd_debug_register_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* DEBUG_REGISTER */
+ uint32_t stuff_count : 3;
+ uint32_t destuff_count : 3;
+ uint32_t pc_arb : 1;
+ uint32_t pc_con : 1;
+ uint32_t pc_dat : 1;
+ uint32_t pc_stc : 1;
+ uint32_t pc_crc : 1;
+ uint32_t pc_crcd : 1;
+ uint32_t pc_ack : 1;
+ uint32_t pc_ackd : 1;
+ uint32_t pc_eof : 1;
+ uint32_t pc_int : 1;
+ uint32_t pc_susp : 1;
+ uint32_t pc_ovr : 1;
+ uint32_t pc_sof : 1;
+ uint32_t reserved_31_19 : 13;
+#else
+ uint32_t reserved_31_19 : 13;
+ uint32_t pc_sof : 1;
+ uint32_t pc_ovr : 1;
+ uint32_t pc_susp : 1;
+ uint32_t pc_int : 1;
+ uint32_t pc_eof : 1;
+ uint32_t pc_ackd : 1;
+ uint32_t pc_ack : 1;
+ uint32_t pc_crcd : 1;
+ uint32_t pc_crc : 1;
+ uint32_t pc_stc : 1;
+ uint32_t pc_dat : 1;
+ uint32_t pc_con : 1;
+ uint32_t pc_arb : 1;
+ uint32_t destuff_count : 3;
+ uint32_t stuff_count : 3;
+#endif
+ } s;
+};
+
+union ctu_can_fd_yolo_reg {
+ uint32_t u32;
+ struct ctu_can_fd_yolo_reg_s {
+ /* YOLO_REG */
+ uint32_t yolo_val : 32;
+ } s;
+};
+
+union ctu_can_fd_timestamp_low {
+ uint32_t u32;
+ struct ctu_can_fd_timestamp_low_s {
+ /* TIMESTAMP_LOW */
+ uint32_t timestamp_low : 32;
+ } s;
+};
+
+union ctu_can_fd_timestamp_high {
+ uint32_t u32;
+ struct ctu_can_fd_timestamp_high_s {
+ /* TIMESTAMP_HIGH */
+ uint32_t timestamp_high : 32;
+ } s;
+};
+
+#endif
--
2.20.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH v7 6/6] docs: ctucanfd: CTU CAN FD open-source IP core documentation.
2020-10-30 22:19 [PATCH v7 0/6] CTU CAN FD open-source IP core SocketCAN driver, PCI, platform integration and documentation Pavel Pisa
` (4 preceding siblings ...)
2020-10-30 22:19 ` [PATCH v7 5/6] can: ctucanfd: CTU CAN FD open-source IP core - platform/SoC support Pavel Pisa
@ 2020-10-30 22:19 ` Pavel Pisa
2020-10-31 11:35 ` [PATCH v7 0/6] CTU CAN FD open-source IP core SocketCAN driver, PCI, platform integration and documentation Marc Kleine-Budde
2020-10-31 11:40 ` Marc Kleine-Budde
7 siblings, 0 replies; 12+ messages in thread
From: Pavel Pisa @ 2020-10-30 22:19 UTC (permalink / raw)
To: linux-can, devicetree, Marc Kleine-Budde, Oliver Hartkopp
Cc: Wolfgang Grandegger, David Miller, Rob Herring, mark.rutland,
Carsten Emde, armbru, netdev, linux-kernel, Marin Jerabek,
Ondrej Ille, Jiri Novak, Jaroslav Beran, Petr Porazil,
Pavel Machek, Drew Fustini, Pavel Pisa
CTU CAN FD IP core documentation based on Martin Jeřábek's diploma theses
Open-source and Open-hardware CAN FD Protocol Support
https://dspace.cvut.cz/handle/10467/80366
.
Signed-off-by: Pavel Pisa <pisa@cmp.felk.cvut.cz>
Signed-off-by: Martin Jerabek <martin.jerabek01@gmail.com>
Signed-off-by: Ondrej Ille <ondrej.ille@gmail.com>
---
.../device_drivers/ctu/ctucanfd-driver.rst | 638 ++++++++++++++++++
.../ctu/fsm_txt_buffer_user.svg | 151 +++++
2 files changed, 789 insertions(+)
create mode 100644 Documentation/networking/device_drivers/ctu/ctucanfd-driver.rst
create mode 100644 Documentation/networking/device_drivers/ctu/fsm_txt_buffer_user.svg
diff --git a/Documentation/networking/device_drivers/ctu/ctucanfd-driver.rst b/Documentation/networking/device_drivers/ctu/ctucanfd-driver.rst
new file mode 100644
index 000000000000..da5b0002e358
--- /dev/null
+++ b/Documentation/networking/device_drivers/ctu/ctucanfd-driver.rst
@@ -0,0 +1,638 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+CTU CAN FD Driver
+=================
+
+Author: Martin Jerabek <martin.jerabek01@gmail.com>
+
+
+About CTU CAN FD IP Core
+------------------------
+
+`CTU CAN FD <https://gitlab.fel.cvut.cz/canbus/ctucanfd_ip_core>`_
+is an open source soft core written in VHDL.
+It originated in 2015 as Ondrej Ille's project
+at the `Department of Measurement <https://meas.fel.cvut.cz/>`_
+of `FEE <http://www.fel.cvut.cz/en/>`_ at `CTU <http://www.fel.cvut.cz/en/>`_.
+
+The SocketCAN driver for Xilinx Zynq SoC based MicroZed board
+`Vivado integration <https://gitlab.fel.cvut.cz/canbus/zynq/zynq-can-sja1000-top>`_
+and Intel Cyclone V 5CSEMA4U23C6 based DE0-Nano-SoC Terasic board
+`QSys integration <https://gitlab.fel.cvut.cz/canbus/intel-soc-ctucanfd>`_
+has been developed as well as support for
+`PCIe integration <https://gitlab.fel.cvut.cz/canbus/pcie-ctucanfd>`_ of the core.
+
+In the case of Zynq, the core is connected via the APB system bus, which does
+not have enumeration support, and the device must be specified in Device Tree.
+This kind of devices is called platform device in the kernel and is
+handled by a platform device driver.
+
+The basic functional model of the CTU CAN FD peripheral has been
+accepted into QEMU mainline. See QEMU `CAN emulation support <https://git.qemu.org/?p=qemu.git;a=blob;f=docs/can.txt>`_
+for CAN FD buses, host connection and CTU CAN FD core emulation. The development
+version of emulation support can be cloned from ctu-canfd branch of QEMU local
+development `repository <https://gitlab.fel.cvut.cz/canbus/qemu-canbus>`_.
+
+
+About SocketCAN
+---------------
+
+SocketCAN is a standard common interface for CAN devices in the Linux
+kernel. As the name suggests, the bus is accessed via sockets, similarly
+to common network devices. The reasoning behind this is in depth
+described in `Linux SocketCAN <https://www.kernel.org/doc/html/latest/networking/can.html>`_.
+In short, it offers a
+natural way to implement and work with higher layer protocols over CAN,
+in the same way as, e.g., UDP/IP over Ethernet.
+
+Device probe
+~~~~~~~~~~~~
+
+Before going into detail about the structure of a CAN bus device driver,
+let's reiterate how the kernel gets to know about the device at all.
+Some buses, like PCI or PCIe, support device enumeration. That is, when
+the system boots, it discovers all the devices on the bus and reads
+their configuration. The kernel identifies the device via its vendor ID
+and device ID, and if there is a driver registered for this identifier
+combination, its probe method is invoked to populate the driver's
+instance for the given hardware. A similar situation goes with USB, only
+it allows for device hot-plug.
+
+The situation is different for peripherals which are directly embedded
+in the SoC and connected to an internal system bus (AXI, APB, Avalon,
+and others). These buses do not support enumeration, and thus the kernel
+has to learn about the devices from elsewhere. This is exactly what the
+Device Tree was made for.
+
+Device tree
+~~~~~~~~~~~
+
+An entry in device tree states that a device exists in the system, how
+it is reachable (on which bus it resides) and its configuration –
+registers address, interrupts and so on. An example of such a device
+tree is given in .
+
+.. code:: raw
+
+ / {
+ /* ... */
+ amba: amba {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+
+ CTU_CAN_FD_0: CTU_CAN_FD@43c30000 {
+ compatible = "ctu,ctucanfd";
+ interrupt-parent = <&intc>;
+ interrupts = <0 30 4>;
+ clocks = <&clkc 15>;
+ reg = <0x43c30000 0x10000>;
+ };
+ };
+ };
+
+
+.. _sec:socketcan:drv:
+
+Driver structure
+~~~~~~~~~~~~~~~~
+
+The driver can be divided into two parts – platform-dependent device
+discovery and set up, and platform-independent CAN network device
+implementation.
+
+.. _sec:socketcan:platdev:
+
+Platform device driver
+^^^^^^^^^^^^^^^^^^^^^^
+
+In the case of Zynq, the core is connected via the AXI system bus, which
+does not have enumeration support, and the device must be specified in
+Device Tree. This kind of devices is called *platform device* in the
+kernel and is handled by a *platform device driver*\ [1]_.
+
+A platform device driver provides the following things:
+
+- A *probe* function
+
+- A *remove* function
+
+- A table of *compatible* devices that the driver can handle
+
+The *probe* function is called exactly once when the device appears (or
+the driver is loaded, whichever happens later). If there are more
+devices handled by the same driver, the *probe* function is called for
+each one of them. Its role is to allocate and initialize resources
+required for handling the device, as well as set up low-level functions
+for the platform-independent layer, e.g., *read_reg* and *write_reg*.
+After that, the driver registers the device to a higher layer, in our
+case as a *network device*.
+
+The *remove* function is called when the device disappears, or the
+driver is about to be unloaded. It serves to free the resources
+allocated in *probe* and to unregister the device from higher layers.
+
+Finally, the table of *compatible* devices states which devices the
+driver can handle. The Device Tree entry ``compatible`` is matched
+against the tables of all *platform drivers*.
+
+.. code:: c
+
+ /* Match table for OF platform binding */
+ static const struct of_device_id ctucan_of_match[] = {
+ { .compatible = "ctu,canfd-2", },
+ { .compatible = "ctu,ctucanfd", },
+ { /* end of list */ },
+ };
+ MODULE_DEVICE_TABLE(of, ctucan_of_match);
+
+ static int ctucan_probe(struct platform_device *pdev);
+ static int ctucan_remove(struct platform_device *pdev);
+
+ static struct platform_driver ctucanfd_driver = {
+ .probe = ctucan_probe,
+ .remove = ctucan_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = ctucan_of_match,
+ },
+ };
+ module_platform_driver(ctucanfd_driver);
+
+
+.. _sec:socketcan:netdev:
+
+Network device driver
+^^^^^^^^^^^^^^^^^^^^^
+
+Each network device must support at least these operations:
+
+- Bring the device up: ``ndo_open``
+
+- Bring the device down: ``ndo_close``
+
+- Submit TX frames to the device: ``ndo_start_xmit``
+
+- Signal TX completion and errors to the network subsystem: ISR
+
+- Submit RX frames to the network subsystem: ISR and NAPI
+
+There are two possible event sources: the device and the network
+subsystem. Device events are usually signaled via an interrupt, handled
+in an Interrupt Service Routine (ISR). Handlers for the events
+originating in the network subsystem are then specified in
+``struct net_device_ops``.
+
+When the device is brought up, e.g., by calling ``ip link set can0 up``,
+the driver’s function ``ndo_open`` is called. It should validate the
+interface configuration and configure and enable the device. The
+analogous opposite is ``ndo_close``, called when the device is being
+brought down, be it explicitly or implicitly.
+
+When the system should transmit a frame, it does so by calling
+``ndo_start_xmit``, which enqueues the frame into the device. If the
+device HW queue (FIFO, mailboxes or whatever the implementation is)
+becomes full, the ``ndo_start_xmit`` implementation informs the network
+subsystem that it should stop the TX queue (via ``netif_stop_queue``).
+It is then re-enabled later in ISR when the device has some space
+available again and is able to enqueue another frame.
+
+All the device events are handled in ISR, namely:
+
+#. **TX completion**. When the device successfully finishes transmitting
+ a frame, the frame is echoed locally. On error, an informative error
+ frame [2]_ is sent to the network subsystem instead. In both cases,
+ the software TX queue is resumed so that more frames may be sent.
+
+#. **Error condition**. If something goes wrong (e.g., the device goes
+ bus-off or RX overrun happens), error counters are updated, and
+ informative error frames are enqueued to SW RX queue.
+
+#. **RX buffer not empty**. In this case, read the RX frames and enqueue
+ them to SW RX queue. Usually NAPI is used as a middle layer (see ).
+
+.. _sec:socketcan:napi:
+
+NAPI
+~~~~
+
+The frequency of incoming frames can be high and the overhead to invoke
+the interrupt service routine for each frame can cause significant
+system load. There are multiple mechanisms in the Linux kernel to deal
+with this situation. They evolved over the years of Linux kernel
+development and enhancements. For network devices, the current standard
+is NAPI – *the New API*. It is similar to classical top-half/bottom-half
+interrupt handling in that it only acknowledges the interrupt in the ISR
+and signals that the rest of the processing should be done in softirq
+context. On top of that, it offers the possibility to *poll* for new
+frames for a while. This has a potential to avoid the costly round of
+enabling interrupts, handling an incoming IRQ in ISR, re-enabling the
+softirq and switching context back to softirq.
+
+More detailed documentation of NAPI may be found on the pages of Linux
+Foundation `<https://wiki.linuxfoundation.org/networking/napi>`_.
+
+Integrating the core to Xilinx Zynq
+-----------------------------------
+
+The core interfaces a simple subset of the Avalon
+`Avalon Interface Specifications <https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/manual/mnl_avalon_spec.pdf>`_
+bus as it was originally used on
+Alterra FPGA chips, yet Xilinx natively interfaces with AXI
+`AMBA AXI and ACE Protocol Specification AXI3, AXI4, and AXI4-Lite, ACE and ACE-Lite <https://static.docs.arm.com/ihi0022/d/IHI0022D_amba_axi_protocol_spec.pdf>`_.
+The most obvious solution would be to use
+an Avalon/AXI bridge or implement some simple conversion entity.
+However, the core’s interface is half-duplex with no handshake
+signaling, whereas AXI is full duplex with two-way signaling. Moreover,
+even AXI-Lite slave interface is quite resource-intensive, and the
+flexibility and speed of AXI are not required for a CAN core.
+
+Thus a much simpler bus was chosen – APB (Advanced Peripheral Bus)
+`AMBA APB Protocol Specification v2.0 <https://static.docs.arm.com/ihi0024/c/IHI0024C_amba_apb_protocol_spec.pdf>`_.
+APB-AXI bridge is directly available in
+Xilinx Vivado, and the interface adaptor entity is just a few simple
+combinatorial assignments.
+
+Finally, to be able to include the core in a block diagram as a custom
+IP, the core, together with the APB interface, has been packaged as a
+Vivado component.
+
+CTU CAN FD Driver design
+------------------------
+
+The general structure of a CAN device driver has already been examined
+in . The next paragraphs provide a more detailed description of the CTU
+CAN FD core driver in particular.
+
+Low-level driver
+~~~~~~~~~~~~~~~~
+
+The core is not intended to be used solely with SocketCAN, and thus it
+is desirable to have an OS-independent low-level driver. This low-level
+driver can then be used in implementations of OS driver or directly
+either on bare metal or in a user-space application. Another advantage
+is that if the hardware slightly changes, only the low-level driver
+needs to be modified.
+
+The code [3]_ is in part automatically generated and in part written
+manually by the core author, with contributions of the thesis’ author.
+The low-level driver supports operations such as: set bit timing, set
+controller mode, enable/disable, read RX frame, write TX frame, and so
+on.
+
+Configuring bit timing
+~~~~~~~~~~~~~~~~~~~~~~
+
+On CAN, each bit is divided into four segments: SYNC, PROP, PHASE1, and
+PHASE2. Their duration is expressed in multiples of a Time Quantum
+(details in `CAN Specification, Version 2.0 <http://esd.cs.ucr.edu/webres/can20.pdf>`_, chapter 8).
+When configuring
+bitrate, the durations of all the segments (and time quantum) must be
+computed from the bitrate and Sample Point. This is performed
+independently for both the Nominal bitrate and Data bitrate for CAN FD.
+
+SocketCAN is fairly flexible and offers either highly customized
+configuration by setting all the segment durations manually, or a
+convenient configuration by setting just the bitrate and sample point
+(and even that is chosen automatically per Bosch recommendation if not
+specified). However, each CAN controller may have different base clock
+frequency and different width of segment duration registers. The
+algorithm thus needs the minimum and maximum values for the durations
+(and clock prescaler) and tries to optimize the numbers to fit both the
+constraints and the requested parameters.
+
+.. code:: c
+
+ struct can_bittiming_const {
+ char name[16]; /* Name of the CAN controller hardware */
+ __u32 tseg1_min; /* Time segment 1 = prop_seg + phase_seg1 */
+ __u32 tseg1_max;
+ __u32 tseg2_min; /* Time segment 2 = phase_seg2 */
+ __u32 tseg2_max;
+ __u32 sjw_max; /* Synchronisation jump width */
+ __u32 brp_min; /* Bit-rate prescaler */
+ __u32 brp_max;
+ __u32 brp_inc;
+ };
+
+
+[lst:can_bittiming_const]
+
+A curious reader will notice that the durations of the segments PROP_SEG
+and PHASE_SEG1 are not determined separately but rather combined and
+then, by default, the resulting TSEG1 is evenly divided between PROP_SEG
+and PHASE_SEG1. In practice, this has virtually no consequences as the
+sample point is between PHASE_SEG1 and PHASE_SEG2. In CTU CAN FD,
+however, the duration registers ``PROP`` and ``PH1`` have different
+widths (6 and 7 bits, respectively), so the auto-computed values might
+overflow the shorter register and must thus be redistributed among the
+two [4]_.
+
+Handling RX
+~~~~~~~~~~~
+
+Frame reception is handled in NAPI queue, which is enabled from ISR when
+the RXNE (RX FIFO Not Empty) bit is set. Frames are read one by one
+until either no frame is left in the RX FIFO or the maximum work quota
+has been reached for the NAPI poll run (see ). Each frame is then passed
+to the network interface RX queue.
+
+An incoming frame may be either a CAN 2.0 frame or a CAN FD frame. The
+way to distinguish between these two in the kernel is to allocate either
+``struct can_frame`` or ``struct canfd_frame``, the two having different
+sizes. In the controller, the information about the frame type is stored
+in the first word of RX FIFO.
+
+This brings us a chicken-egg problem: we want to allocate the ``skb``
+for the frame, and only if it succeeds, fetch the frame from FIFO;
+otherwise keep it there for later. But to be able to allocate the
+correct ``skb``, we have to fetch the first work of FIFO. There are
+several possible solutions:
+
+#. Read the word, then allocate. If it fails, discard the rest of the
+ frame. When the system is low on memory, the situation is bad anyway.
+
+#. Always allocate ``skb`` big enough for an FD frame beforehand. Then
+ tweak the ``skb`` internals to look like it has been allocated for
+ the smaller CAN 2.0 frame.
+
+#. Add option to peek into the FIFO instead of consuming the word.
+
+#. If the allocation fails, store the read word into driver’s data. On
+ the next try, use the stored word instead of reading it again.
+
+Option 1 is simple enough, but not very satisfying if we could do
+better. Option 2 is not acceptable, as it would require modifying the
+private state of an integral kernel structure. The slightly higher
+memory consumption is just a virtual cherry on top of the “cake”. Option
+3 requires non-trivial HW changes and is not ideal from the HW point of
+view.
+
+Option 4 seems like a good compromise, with its disadvantage being that
+a partial frame may stay in the FIFO for a prolonged time. Nonetheless,
+there may be just one owner of the RX FIFO, and thus no one else should
+see the partial frame (disregarding some exotic debugging scenarios).
+Basides, the driver resets the core on its initialization, so the
+partial frame cannot be “adopted” either. In the end, option 4 was
+selected [5]_.
+
+.. _subsec:ctucanfd:rxtimestamp:
+
+Timestamping RX frames
+^^^^^^^^^^^^^^^^^^^^^^
+
+The CTU CAN FD core reports the exact timestamp when the frame has been
+received. The timestamp is by default captured at the sample point of
+the last bit of EOF but is configurable to be captured at the SOF bit.
+The timestamp source is external to the core and may be up to 64 bits
+wide. At the time of writing, passing the timestamp from kernel to
+userspace is not yet implemented, but is planned in the future.
+
+Handling TX
+~~~~~~~~~~~
+
+The CTU CAN FD core has 4 independent TX buffers, each with its own
+state and priority. When the core wants to transmit, a TX buffer in
+Ready state with the highest priority is selected.
+
+The priorities are 3bit numbers in register TX_PRIORITY
+(nibble-aligned). This should be flexible enough for most use cases.
+SocketCAN, however, supports only one FIFO queue for outgoing
+frames [6]_. The buffer priorities may be used to simulate the FIFO
+behavior by assigning each buffer a distinct priority and *rotating* the
+priorities after a frame transmission is completed.
+
+In addition to priority rotation, the SW must maintain head and tail
+pointers into the FIFO formed by the TX buffers to be able to determine
+which buffer should be used for next frame (``txb_head``) and which
+should be the first completed one (``txb_tail``). The actual buffer
+indices are (obviously) modulo 4 (number of TX buffers), but the
+pointers must be at least one bit wider to be able to distinguish
+between FIFO full and FIFO empty – in this situation,
+:math:`txb\_head \equiv txb\_tail\ (\textrm{mod}\ 4)`. An example of how
+the FIFO is maintained, together with priority rotation, is depicted in
+
+|
+
++------+---+---+---+---+
+| TXB# | 0 | 1 | 2 | 3 |
++======+===+===+===+===+
+| Seq | A | B | C | |
++------+---+---+---+---+
+| Prio | 7 | 6 | 5 | 4 |
++------+---+---+---+---+
+| | | T | | H |
++------+---+---+---+---+
+
+|
+
++------+---+---+---+---+
+| TXB# | 0 | 1 | 2 | 3 |
++======+===+===+===+===+
+| Seq | | B | C | |
++------+---+---+---+---+
+| Prio | 4 | 7 | 6 | 5 |
++------+---+---+---+---+
+| | | T | | H |
++------+---+---+---+---+
+
+|
+
++------+---+---+---+---+----+
+| TXB# | 0 | 1 | 2 | 3 | 0’ |
++======+===+===+===+===+====+
+| Seq | E | B | C | D | |
++------+---+---+---+---+----+
+| Prio | 4 | 7 | 6 | 5 | |
++------+---+---+---+---+----+
+| | | T | | | H |
++------+---+---+---+---+----+
+
+|
+
+.. figure:: fsm_txt_buffer_user.svg
+
+ TX Buffer states with possible transitions
+
+.. _subsec:ctucanfd:txtimestamp:
+
+Timestamping TX frames
+^^^^^^^^^^^^^^^^^^^^^^
+
+When submitting a frame to a TX buffer, one may specify the timestamp at
+which the frame should be transmitted. The frame transmission may start
+later, but not sooner. Note that the timestamp does not participate in
+buffer prioritization – that is decided solely by the mechanism
+described above.
+
+Support for time-based packet transmission was recently merged to Linux
+v4.19 `Time-based packet transmission <https://lwn.net/Articles/748879/>`_,
+but it remains yet to be researched
+whether this functionality will be practical for CAN.
+
+Also similarly to retrieving the timestamp of RX frames, the core
+supports retrieving the timestamp of TX frames – that is the time when
+the frame was successfully delivered. The particulars are very similar
+to timestamping RX frames and are described in .
+
+Handling RX buffer overrun
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When a received frame does no more fit into the hardware RX FIFO in its
+entirety, RX FIFO overrun flag (STATUS[DOR]) is set and Data Overrun
+Interrupt (DOI) is triggered. When servicing the interrupt, care must be
+taken first to clear the DOR flag (via COMMAND[CDO]) and after that
+clear the DOI interrupt flag. Otherwise, the interrupt would be
+immediately [7]_ rearmed.
+
+**Note**: During development, it was discussed whether the internal HW
+pipelining cannot disrupt this clear sequence and whether an additional
+dummy cycle is necessary between clearing the flag and the interrupt. On
+the Avalon interface, it indeed proved to be the case, but APB being
+safe because it uses 2-cycle transactions. Essentially, the DOR flag
+would be cleared, but DOI register’s Preset input would still be high
+the cycle when the DOI clear request would also be applied (by setting
+the register’s Reset input high). As Set had higher priority than Reset,
+the DOI flag would not be reset. This has been already fixed by swapping
+the Set/Reset priority (see issue #187).
+
+Reporting Error Passive and Bus Off conditions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It may be desirable to report when the node reaches *Error Passive*,
+*Error Warning*, and *Bus Off* conditions. The driver is notified about
+error state change by an interrupt (EPI, EWLI), and then proceeds to
+determine the core’s error state by reading its error counters.
+
+There is, however, a slight race condition here – there is a delay
+between the time when the state transition occurs (and the interrupt is
+triggered) and when the error counters are read. When EPI is received,
+the node may be either *Error Passive* or *Bus Off*. If the node goes
+*Bus Off*, it obviously remains in the state until it is reset.
+Otherwise, the node is *or was* *Error Passive*. However, it may happen
+that the read state is *Error Warning* or even *Error Active*. It may be
+unclear whether and what exactly to report in that case, but I
+personally entertain the idea that the past error condition should still
+be reported. Similarly, when EWLI is received but the state is later
+detected to be *Error Passive*, *Error Passive* should be reported.
+
+
+CTU CAN FD Driver Sources Reference
+-----------------------------------
+
+.. kernel-doc:: drivers/net/can/ctucanfd/ctucanfd_hw.h
+ :internal:
+
+.. kernel-doc:: drivers/net/can/ctucanfd/ctucanfd_base.c
+ :internal:
+
+.. kernel-doc:: drivers/net/can/ctucanfd/ctucanfd_pci.c
+ :internal:
+
+.. kernel-doc:: drivers/net/can/ctucanfd/ctucanfd_platform.c
+ :internal:
+
+CTU CAN FD IP Core and Driver Development Acknowledgment
+---------------------------------------------------------
+
+* Odrej Ille <illeondr@fel.cvut.cz>
+
+ * started the project as student at Department of Measurement, FEE, CTU
+ * invested great amount of personal time and enthusiasm to the project over years
+ * worked on more funded tasks
+
+* `Department of Measurement <https://meas.fel.cvut.cz/>`_,
+ `Faculty of Electrical Engineering <http://www.fel.cvut.cz/en/>`_,
+ `Czech Technical University <https://www.cvut.cz/en>`_
+
+ * is the main investor into the project over many years
+ * uses project in their CAN/CAN FD diagnostics framework for `Skoda Auto <https://www.skoda-auto.cz/>`_
+
+* `Digiteq Automotive <https://www.digiteqautomotive.com/en>`_
+
+ * funding of the project CAN FD Open Cores Support Linux Kernel Based Systems
+ * negotiated and paid CTU to allow public access to the project
+ * provided additional funding of the work
+
+* `Department of Control Engineering <https://dce.fel.cvut.cz/en>`_,
+ `Faculty of Electrical Engineering <http://www.fel.cvut.cz/en/>`_,
+ `Czech Technical University <https://www.cvut.cz/en>`_
+
+ * solving the project CAN FD Open Cores Support Linux Kernel Based Systems
+ * providing GitLab management
+ * virtual servers and computational power for continuous integration
+ * providing hardware for HIL continuous integration tests
+
+* `PiKRON Ltd. <http://pikron.com/>`_
+
+ * minor funding to initiate preparation of the project open-sourcing
+
+* Petr Porazil <porazil@pikron.com>
+
+ * design of PCIe transceiver addon board and assembly of boards
+ * design and assembly of MZ_APO baseboard for MicroZed/Zynq based system
+
+* Martin Jerabek <martin.jerabek01@gmail.com>
+
+ * Linux driver development
+ * continuous integration platform architect and GHDL updates
+ * theses `Open-source and Open-hardware CAN FD Protocol Support <https://dspace.cvut.cz/bitstream/handle/10467/80366/F3-DP-2019-Jerabek-Martin-Jerabek-thesis-2019-canfd.pdf>`_
+
+* Jiri Novak <jnovak@fel.cvut.cz>
+
+ * project initiation, management and use at Department of Measurement, FEE, CTU
+
+* Pavel Pisa <pisa@cmp.felk.cvut.cz>
+
+ * initiate open-sourcing, project coordination, management at Department of Control Engineering, FEE, CTU
+
+* Jaroslav Beran<jara.beran@gmail.com>
+
+ * system integration for Intel SoC, core and driver testing and updates
+
+* Carsten Emde (`OSADL <https://www.osadl.org/>`_)
+
+ * provided OSADL expertise to discuss IP core licensing
+ * pointed to possible deadlock for LGPL and CAN bus possible patent case which lead to relicense IP core design to BSD like license
+
+* Reiner Zitzmann and Holger Zeltwanger (`CAN in Automation <https://www.can-cia.org/>`_)
+
+ * provided suggestions and help to inform community about the project and invited us to events focused on CAN bus future development directions
+
+* Jan Charvat
+
+ * implemented CTU CAN FD functional model for QEMU which has been integrated into QEMU mainline (`docs/can.txt <https://git.qemu.org/?p=qemu.git;a=blob;f=docs/can.txt>`_)
+ * Bachelor theses Model of CAN FD Communication Controller for QEMU Emulator
+
+Notes
+-----
+
+
+.. [1]
+ Other buses have their own specific driver interface to set up the
+ device.
+
+.. [2]
+ Not to be mistaken with CAN Error Frame. This is a ``can_frame`` with
+ ``CAN_ERR_FLAG`` set and some error info in its ``data`` field.
+
+.. [3]
+ Available in in CTU CAN FD repository
+ `<https://gitlab.fel.cvut.cz/canbus/ctucanfd_ip_core>`_
+
+.. [4]
+ As is done in the low-level driver functions
+ ``ctucan_hw_set_nom_bittiming`` and
+ ``ctucan_hw_set_data_bittiming``.
+
+.. [5]
+ At the time of writing this thesis, option 1 is still being used and
+ the modification is queued in gitlab issue #222
+
+.. [6]
+ Strictly speaking, multiple CAN TX queues are supported since v4.19
+ `can: enable multi-queue for SocketCAN devices <https://lore.kernel.org/patchwork/patch/913526/>`_ but no mainline driver is using
+ them yet.
+
+.. [7]
+ Or rather in the next clock cycle
diff --git a/Documentation/networking/device_drivers/ctu/fsm_txt_buffer_user.svg b/Documentation/networking/device_drivers/ctu/fsm_txt_buffer_user.svg
new file mode 100644
index 000000000000..b371650788f4
--- /dev/null
+++ b/Documentation/networking/device_drivers/ctu/fsm_txt_buffer_user.svg
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="113.611mm" height="86.6873mm" version="1.1" viewBox="0 0 113.611 86.6873" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <defs>
+ <marker id="marker3667" overflow="visible" orient="auto">
+ <path transform="scale(-.6)" d="m8.71859 4.03374-10.9259-4.01772 10.9259-4.01772c-1.7455 2.37206-1.73544 5.61745-6e-7 8.03544z" fill="#28a4ff" fill-rule="evenodd" stroke="#28a4ff" stroke-linejoin="round" stroke-width=".625"/>
+ </marker>
+ <marker id="marker3517" overflow="visible" orient="auto">
+ <path transform="scale(-.6)" d="m8.71859 4.03374-10.9259-4.01772 10.9259-4.01772c-1.7455 2.37206-1.73544 5.61745-6e-7 8.03544z" fill-rule="evenodd" stroke="#000" stroke-linejoin="round" stroke-width=".625"/>
+ </marker>
+ <marker id="marker3373" overflow="visible" orient="auto">
+ <path transform="scale(-.6)" d="m8.71859 4.03374-10.9259-4.01772 10.9259-4.01772c-1.7455 2.37206-1.73544 5.61745-6e-7 8.03544z" fill-rule="evenodd" stroke="#000" stroke-linejoin="round" stroke-width=".625"/>
+ </marker>
+ <marker id="marker3199" overflow="visible" orient="auto">
+ <path transform="scale(-.6)" d="m8.71859 4.03374-10.9259-4.01772 10.9259-4.01772c-1.7455 2.37206-1.73544 5.61745-6e-7 8.03544z" fill="#28a4ff" fill-rule="evenodd" stroke="#28a4ff" stroke-linejoin="round" stroke-width=".625"/>
+ </marker>
+ <marker id="marker3037" overflow="visible" orient="auto">
+ <path transform="scale(-.6)" d="m8.71859 4.03374-10.9259-4.01772 10.9259-4.01772c-1.7455 2.37206-1.73544 5.61745-6e-7 8.03544z" fill="#28a4ff" fill-rule="evenodd" stroke="#28a4ff" stroke-linejoin="round" stroke-width=".625"/>
+ </marker>
+ <marker id="marker2779" overflow="visible" orient="auto">
+ <path transform="scale(-.6)" d="m8.71859 4.03374-10.9259-4.01772 10.9259-4.01772c-1.7455 2.37206-1.73544 5.61745-6e-7 8.03544z" fill="#28a4ff" fill-rule="evenodd" stroke="#28a4ff" stroke-linejoin="round" stroke-width=".625"/>
+ </marker>
+ <marker id="marker2477" overflow="visible" orient="auto">
+ <path transform="scale(.6) rotate(180) translate(0)" d="m8.71859 4.03374-10.9259-4.01772 10.9259-4.01772c-1.7455 2.37206-1.73544 5.61745-6e-7 8.03544z" fill="#28a4ff" fill-rule="evenodd" stroke="#28a4ff" stroke-linejoin="round" stroke-width=".625"/>
+ </marker>
+ <marker id="marker2074" overflow="visible" orient="auto">
+ <path transform="scale(.6) rotate(180) translate(0)" d="m8.71859 4.03374-10.9259-4.01772 10.9259-4.01772c-1.7455 2.37206-1.73544 5.61745-6e-7 8.03544z" fill-rule="evenodd" stroke="#000" stroke-linejoin="round" stroke-width=".625"/>
+ </marker>
+ <marker id="marker1964" overflow="visible" orient="auto">
+ <path transform="scale(.6) rotate(180) translate(0)" d="m8.71859 4.03374-10.9259-4.01772 10.9259-4.01772c-1.7455 2.37206-1.73544 5.61745-6e-7 8.03544z" fill-rule="evenodd" stroke="#000" stroke-linejoin="round" stroke-width=".625"/>
+ </marker>
+ <marker id="marker1856" overflow="visible" orient="auto">
+ <path transform="scale(.6) rotate(180) translate(0)" d="m8.71859 4.03374-10.9259-4.01772 10.9259-4.01772c-1.7455 2.37206-1.73544 5.61745-6e-7 8.03544z" fill-rule="evenodd" stroke="#000" stroke-linejoin="round" stroke-width=".625"/>
+ </marker>
+ <marker id="Arrow2Mend" overflow="visible" orient="auto">
+ <path transform="scale(.6) rotate(180) translate(0)" d="m8.71859 4.03374-10.9259-4.01772 10.9259-4.01772c-1.7455 2.37206-1.73544 5.61745-6e-7 8.03544z" fill-rule="evenodd" stroke="#000" stroke-linejoin="round" stroke-width=".625"/>
+ </marker>
+ <filter id="filter1204" x="-4.19953e-6" y="-5.60084e-6" width="1.00001" height="1.00001" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.00018829868"/>
+ </filter>
+ <marker id="marker2074-3" overflow="visible" orient="auto">
+ <path transform="scale(-.6)" d="m8.71859 4.03374-10.9259-4.01772 10.9259-4.01772c-1.7455 2.37206-1.73544 5.61745-6e-7 8.03544z" fill="#28a4ff" fill-rule="evenodd" stroke="#28a4ff" stroke-linejoin="round" stroke-width=".625"/>
+ </marker>
+ <filter id="filter1204-6" x="-4.19953e-6" y="-5.60084e-6" width="1.00001" height="1.00001" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.00018829868"/>
+ </filter>
+ <filter id="filter1204-6-9" x="-4.19953e-6" y="-5.60084e-6" width="1.00001" height="1.00001" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.00018829868"/>
+ </filter>
+ <filter id="filter1204-6-2" x="-4.19953e-6" y="-5.60084e-6" width="1.00001" height="1.00001" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.00018829868"/>
+ </filter>
+ <filter id="filter1204-6-2-9" x="-4.19953e-6" y="-5.60084e-6" width="1.00001" height="1.00001" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.00018829868"/>
+ </filter>
+ <filter id="filter1204-6-2-9-4" x="-4.19953e-6" y="-5.60084e-6" width="1.00001" height="1.00001" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.00018829868"/>
+ </filter>
+ <filter id="filter1204-6-2-9-1" x="-4.19953e-6" y="-5.60084e-6" width="1.00001" height="1.00001" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.00018829868"/>
+ </filter>
+ <filter id="filter1204-6-2-9-1-3" x="-4.19953e-6" y="-5.60084e-6" width="1.00001" height="1.00001" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.00018829868"/>
+ </filter>
+ <filter id="filter1204-6-2-9-1-3-1" x="-4.19953e-6" y="-5.60084e-6" width="1.00001" height="1.00001" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.00018829868"/>
+ </filter>
+ </defs>
+ <metadata>
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g transform="translate(-49.0277 -104.823)">
+ <g>
+ <path d="m130.534 165.429h-71.1816v-17.5315" fill="none" marker-end="url(#marker2477)" stroke="#28a4ff" stroke-width=".6"/>
+ <path d="m145.034 122.959v-11.5914h-43.1215" fill="none" marker-end="url(#marker3037)" stroke="#28a4ff" stroke-width=".6"/>
+ <rect x="130.679" y="122.933" width="28.2965" height="45.2319" rx="0" ry="0" fill="#e5e5e5" stroke="#717171" stroke-linecap="square" stroke-width=".499999"/>
+ <path d="m102.044 116.236h23.3126l-0.13388 18.8185h19.9383v3.66603" fill="none" marker-end="url(#marker3199)" stroke="#28a4ff" stroke-width=".6"/>
+ <path d="m59.5006 138.391v-24.2517h20.6338" fill="none" marker-end="url(#marker2779)" stroke="#28a4ff" stroke-width=".6"/>
+ <rect x="78.1389" y="126.411" width="28.0037" height="35.0443" rx="0" ry="0" fill="#e5e5e5" stroke="#717171" stroke-linecap="square" stroke-width=".5"/>
+ </g>
+ <g fill="#ffcb35" stroke="#000" stroke-linecap="square">
+ <ellipse cx="92.1408" cy="114.239" rx="10.8866" ry="4.39308" stroke-width=".5"/>
+ <ellipse cx="92.1408" cy="134.185" rx="10.8866" ry="4.39308" stroke-width=".499999"/>
+ <ellipse cx="92.1408" cy="152.199" rx="10.8866" ry="4.39308" stroke-width=".499999"/>
+ </g>
+ <g fill="#28a4ff" stroke="#000" stroke-linecap="square" stroke-width=".499999">
+ <ellipse cx="144.827" cy="143.316" rx="10.8866" ry="4.39308"/>
+ <ellipse cx="144.827" cy="159.143" rx="10.8866" ry="4.39308"/>
+ <ellipse cx="59.4364" cy="142.823" rx="7.36455" ry="4.39308"/>
+ <ellipse cx="144.827" cy="129.196" rx="10.8866" ry="4.39308"/>
+ <ellipse cx="143.077" cy="180.53" rx="10.8866" ry="4.39308"/>
+ </g>
+ <ellipse cx="110.386" cy="180.53" rx="10.8866" ry="4.39308" fill="#ffcb35" stroke="#000" stroke-linecap="square" stroke-width=".499999"/>
+ <text x="110.90907" y="179.42688" font-size="3.175px" xml:space="preserve"><tspan x="110.90907" y="179.42688" dy="0.60000002" text-align="center" text-anchor="middle">Accessible</tspan><tspan x="110.90907" y="183.39563"><tspan font-size="3.175px" text-align="center" text-anchor="middle">for S</tspan>W</tspan></text>
+ <text x="143.5869" y="179.52795" xml:space="preserve"><tspan x="143.5869" y="179.52795" dy="1 0 0 0 0 0" font-family="sans-serif" font-size="2.82222px" text-align="center" text-anchor="middle" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal">Inaccessible</tspan><tspan x="143.5869" y="183.36786" font-size="3.175px"><tspan font-size="3.175px" text-align="center" text-anchor="middle">for S</tspan>W</tspan></text>
+ <g font-size="3.175px">
+ <text x="91.95018" y="115.29005" xml:space="preserve"><tspan x="91.95018" y="115.29005" font-size="3.175px"><tspan font-size="3.175px" text-align="center" text-anchor="middle">Ready</tspan></tspan></text>
+ <text x="145.25127" y="130.49019" xml:space="preserve"><tspan x="145.25127" y="130.49019" font-size="3.175px"><tspan font-size="3.175px" text-align="center" text-anchor="middle">TX OK</tspan></tspan></text>
+ <text x="145.31845" y="144.43121" xml:space="preserve"><tspan x="145.31845" y="144.43121" font-size="3.175px"><tspan font-size="3.175px" text-align="center" text-anchor="middle">Aborted</tspan></tspan></text>
+ <text x="145.40399" y="160.36035" xml:space="preserve"><tspan x="145.40399" y="160.36035" font-size="3.175px"><tspan font-size="3.175px" text-align="center" text-anchor="middle">TX failed</tspan></tspan></text>
+ <text x="91.823967" y="133.53941" text-align="center" text-anchor="middle" style="line-height:0.9" xml:space="preserve"><tspan x="91.823967" y="133.53941" text-align="center"><tspan font-size="3.175px" text-align="center" text-anchor="middle">TX in</tspan></tspan><tspan x="91.823967" y="136.39691" text-align="center">progress</tspan></text>
+ <text x="91.648918" y="151.84813" text-align="center" text-anchor="middle" style="line-height:0.9" xml:space="preserve"><tspan x="91.648918" y="151.84813" text-align="center"><tspan font-size="3.175px" text-align="center" text-anchor="middle">Abort in</tspan></tspan><tspan x="91.648918" y="154.70563" text-align="center">progress</tspan></text>
+ <text x="59.456043" y="143.91658" xml:space="preserve"><tspan x="59.456043" y="143.91658" font-size="3.175px"><tspan font-size="3.175px" text-align="center" text-anchor="middle">Empty</tspan></tspan></text>
+ </g>
+ <g fill="none">
+ <g stroke="#000">
+ <rect x="52.3943" y="171.63" width="106.581" height="16.601" rx="0" ry="0" stroke-linecap="square" stroke-width=".499999"/>
+ <g stroke-width=".6">
+ <path d="m106.383 159.046h26.4967" marker-end="url(#Arrow2Mend)"/>
+ <path d="m103.138 152.268h41.5564v-3.92426" marker-end="url(#marker1856)"/>
+ <path d="m106.38 129.354h17.7785"/>
+ <path d="m125.818 129.359h7.2418" marker-end="url(#marker1964)"/>
+ </g>
+ <path d="m124.169 129.354a0.959514 0.97091 0 0 1 0.47587-0.84557 0.959514 0.97091 0 0 1 0.96164-3e-3 0.959514 0.97091 0 0 1 0.48149 0.84231" stroke-linecap="square" stroke-width=".600001"/>
+ <path d="m55.7026 180.832h34.8131" marker-end="url(#marker2074)" stroke-width=".6"/>
+ </g>
+ <g>
+ <path d="m55.6464 185.744h34.8131" marker-end="url(#marker2074-3)" stroke="#28a4ff" stroke-width=".600001"/>
+ <g stroke-width=".6">
+ <path d="m94.0487 129.889v-10.6493" marker-end="url(#marker3373)" stroke="#000"/>
+ <path d="m89.7534 118.621v10.662" marker-end="url(#marker3517)" stroke="#000"/>
+ <path d="m92.119 138.812v7.9718" marker-end="url(#marker3667)" stroke="#28a4ff"/>
+ </g>
+ </g>
+ </g>
+ <text transform="matrix(.264583 0 0 .264583 91.8919 139.964)" x="26.959213" y="9.11724" fill="#2aa1ff" filter="url(#filter1204-6-2-9-1-3-1)" font-size="12px" stroke-width="3.77953" text-align="center" text-anchor="middle" style="line-height:1.1" xml:space="preserve"><tspan x="26.959213" y="9.11724" text-align="center">Set</tspan><tspan x="26.959213" y="22.31724" text-align="center">abort</tspan></text>
+ <text transform="translate(49.0277 104.823)" x="57.620724" y="16.855087" filter="url(#filter1204)" font-size="3.175px" text-align="center" text-anchor="middle" style="line-height:1.1" xml:space="preserve"><tspan x="57.620724" y="16.855087" text-align="center">Transmission</tspan><tspan x="57.620724" y="20.347588" text-align="center">unsuccesfull</tspan></text>
+ <g font-size="12px" stroke-width="3.77953" text-anchor="middle">
+ <text transform="matrix(.264583 0 0 .264583 68.5988 118.913)" x="38.824219" y="9.1171875" filter="url(#filter1204)" text-align="center" style="line-height:1.1" xml:space="preserve"><tspan x="38.824219" y="9.1171875" text-align="center">Transmission</tspan><tspan x="38.824219" y="22.317188" text-align="center">starts</tspan></text>
+ <text transform="matrix(.264583 0 0 .264583 106.802 130.509)" x="38.824219" y="9.1171875" filter="url(#filter1204)" text-align="center" style="line-height:1.1" xml:space="preserve"><tspan x="38.824219" y="9.1171875" text-align="center">Transmission</tspan><tspan x="38.824219" y="22.317188" text-align="center">succesfull</tspan></text>
+ <text transform="matrix(.264583 0 0 .264583 107.77 145.476)" x="38.824219" y="9.1171875" filter="url(#filter1204)" text-align="center" style="line-height:1.1" xml:space="preserve"><tspan x="38.824219" y="9.1171875" text-align="center">Transmission</tspan><tspan x="38.824219" y="22.317188" text-align="center">sborted</tspan></text>
+ </g>
+ <g stroke-width="3.77953" text-anchor="middle">
+ <text transform="matrix(.264583 0 0 .264583 107.574 155.948)" x="38.824219" y="9.1171875" filter="url(#filter1204)" font-size="10.6667px" text-align="center" style="line-height:1.1" xml:space="preserve"><tspan x="38.824219" y="9.1171875" text-align="center">Retransmit</tspan><tspan x="38.824219" y="20.850557" text-align="center">limit reached or</tspan><tspan x="38.824219" y="32.583927" text-align="center">node went bus off</tspan><tspan x="38.824219" y="44.317299" text-align="center"/></text>
+ <text transform="matrix(.264583 0 0 .264583 60.7127 177.384)" x="38.824539" y="9.1173134" filter="url(#filter1204-6)" font-size="12px" text-align="center" style="line-height:1.1" xml:space="preserve"><tspan x="38.824539" y="9.1173134" font-size="12px" stroke-width="3.77953" text-align="center" text-anchor="middle">Transmission result</tspan></text>
+ <text transform="matrix(.264583 0 0 .264583 45.6885 173.226)" x="57.727047" y="9.11724" filter="url(#filter1204-6-9)" font-size="12px" text-align="center" style="line-height:1.1" xml:space="preserve"><tspan x="57.727047" y="9.11724" font-size="12px" stroke-width="3.77953" text-align="center" text-anchor="middle">Legend:</tspan></text>
+ </g>
+ <g fill="#2aa1ff" font-size="12px" stroke-width="3.77953" text-anchor="middle">
+ <text transform="matrix(.264583 0 0 .264583 57.0045 182.079)" x="57.727047" y="9.11724" filter="url(#filter1204-6-2)" text-align="center" style="line-height:1.1" xml:space="preserve"><tspan x="57.727047" y="9.11724" fill="#2aa1ff" font-size="12px" stroke-width="3.77953" text-align="center" text-anchor="middle">SW command</tspan></text>
+ <text transform="matrix(.264583 0 0 .264583 57.7865 110.104)" x="40.822609" y="9.11724" filter="url(#filter1204-6-2-9)" text-align="center" style="line-height:1.1" xml:space="preserve"><tspan x="40.822609" y="9.11724" fill="#2aa1ff" font-size="12px" stroke-width="3.77953" text-align="center" text-anchor="middle">Set ready</tspan></text>
+ <text transform="matrix(.264583 0 0 .264583 116.893 107.491)" x="28.049065" y="9.1172523" filter="url(#filter1204-6-2-9-4)" text-align="center" style="line-height:1.1" xml:space="preserve"><tspan x="28.049065" y="9.1172523" fill="#2aa1ff" font-size="12px" stroke-width="3.77953" text-align="center" text-anchor="middle">Set ready</tspan></text>
+ <text transform="matrix(.264583 0 0 .264583 87.5687 166.324)" x="28.049065" y="9.1172523" filter="url(#filter1204-6-2-9-1)" text-align="center" style="line-height:1.1" xml:space="preserve"><tspan x="28.049065" y="9.1172523" fill="#2aa1ff" font-size="12px" stroke-width="3.77953" text-align="center" text-anchor="middle">Set empty</tspan></text>
+ <text transform="matrix(.264583 0 0 .264583 106.53 113.074)" x="30.228771" y="8.9063139" filter="url(#filter1204-6-2-9-1-3)" text-align="center" style="line-height:1.1" xml:space="preserve"><tspan x="30.228771" y="8.9063139" fill="#2aa1ff" font-size="12px" stroke-width="3.77953" text-align="center" text-anchor="middle">Set abort</tspan></text>
+ </g>
+ </g>
+</svg>
--
2.20.1
^ permalink raw reply related [flat|nested] 12+ messages in thread