From patchwork Thu Oct 22 08:36:18 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Pisa X-Patchwork-Id: 11850597 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9D64DC4363A for ; Thu, 22 Oct 2020 08:41:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DE4462168B for ; Thu, 22 Oct 2020 08:41:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2509582AbgJVIly (ORCPT ); Thu, 22 Oct 2020 04:41:54 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36054 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2508267AbgJVIlw (ORCPT ); Thu, 22 Oct 2020 04:41:52 -0400 Received: from relay.felk.cvut.cz (relay.felk.cvut.cz [IPv6:2001:718:2:1611:0:1:0:70]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id A07ABC0613CE for ; Thu, 22 Oct 2020 01:41:51 -0700 (PDT) Received: from cmp.felk.cvut.cz (haar.felk.cvut.cz [147.32.84.19]) by relay.felk.cvut.cz (8.15.2/8.15.2) with ESMTP id 09M8el9k060132; Thu, 22 Oct 2020 10:40:47 +0200 (CEST) (envelope-from pisa@cmp.felk.cvut.cz) Received: from haar.felk.cvut.cz (localhost [127.0.0.1]) by cmp.felk.cvut.cz (8.14.0/8.12.3/SuSE Linux 0.6) with ESMTP id 09M8ej3B006113; Thu, 22 Oct 2020 10:40:45 +0200 Received: (from pisa@localhost) by haar.felk.cvut.cz (8.14.0/8.13.7/Submit) id 09M8ejgo006110; Thu, 22 Oct 2020 10:40:45 +0200 From: Pavel Pisa To: linux-can@vger.kernel.org, devicetree@vger.kernel.org, "Marc Kleine-Budde" , Oliver Hartkopp Cc: Wolfgang Grandegger , David Miller , Rob Herring , mark.rutland@arm.com, Carsten Emde , armbru@redhat.com, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Marin Jerabek , Ondrej Ille , Jiri Novak , Jaroslav Beran , Petr Porazil , Pavel Machek , Drew Fustini , Pavel Pisa Subject: [PATCH v6 3/6] can: ctucanfd: add support for CTU CAN FD open-source IP core - bus independent part. Date: Thu, 22 Oct 2020 10:36:18 +0200 Message-Id: <886a8e0749e0521bf83a88313008a3f38031590b.1603354744.git.pisa@cmp.felk.cvut.cz> X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 X-FELK-MailScanner-Information: X-MailScanner-ID: 09M8el9k060132 X-FELK-MailScanner: Found to be clean X-FELK-MailScanner-SpamCheck: not spam, SpamAssassin (not cached, score=-0.098, required 6, BAYES_00 -0.50, KHOP_HELO_FCRDNS 0.40, SPF_HELO_NONE 0.00, SPF_NONE 0.00, URIBL_BLOCKED 0.00) X-FELK-MailScanner-From: pisa@cmp.felk.cvut.cz X-FELK-MailScanner-Watermark: 1603960847.84375@graviW0q9kYskriACrVM6g Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org From: Martin Jerabek 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 FGA based PCI Express board is available from project (https://gitlab.fel.cvut.cz/canbus/pcie-ctu_can_fd). More about CAN bus related projects used and developed at CTU FEE at http://canbus.pages.fel.cvut.cz/ . Signed-off-by: Martin Jerabek Signed-off-by: Ondrej Ille Signed-off-by: Pavel Pisa --- drivers/net/can/Kconfig | 1 + drivers/net/can/Makefile | 1 + drivers/net/can/ctucanfd/Kconfig | 15 + drivers/net/can/ctucanfd/Makefile | 7 + drivers/net/can/ctucanfd/ctu_can_fd.c | 1105 +++++++++++++++++++ drivers/net/can/ctucanfd/ctu_can_fd.h | 87 ++ drivers/net/can/ctucanfd/ctu_can_fd_frame.h | 189 ++++ drivers/net/can/ctucanfd/ctu_can_fd_hw.c | 790 +++++++++++++ drivers/net/can/ctucanfd/ctu_can_fd_hw.h | 916 +++++++++++++++ drivers/net/can/ctucanfd/ctu_can_fd_regs.h | 971 ++++++++++++++++ 10 files changed, 4082 insertions(+) create mode 100644 drivers/net/can/ctucanfd/Kconfig create mode 100644 drivers/net/can/ctucanfd/Makefile create mode 100644 drivers/net/can/ctucanfd/ctu_can_fd.c create mode 100644 drivers/net/can/ctucanfd/ctu_can_fd.h create mode 100644 drivers/net/can/ctucanfd/ctu_can_fd_frame.h create mode 100644 drivers/net/can/ctucanfd/ctu_can_fd_hw.c create mode 100644 drivers/net/can/ctucanfd/ctu_can_fd_hw.h create mode 100644 drivers/net/can/ctucanfd/ctu_can_fd_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..b6d47bba7150 --- /dev/null +++ b/drivers/net/can/ctucanfd/Kconfig @@ -0,0 +1,15 @@ +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-ctu_can_fd). + Guidepost CTU FEE CAN bus projects page http://canbus.pages.fel.cvut.cz/ . + +if CAN_CTUCANFD + +endif diff --git a/drivers/net/can/ctucanfd/Makefile b/drivers/net/can/ctucanfd/Makefile new file mode 100644 index 000000000000..8d47008d0076 --- /dev/null +++ b/drivers/net/can/ctucanfd/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the CTU CAN-FD IP module drivers +# + +obj-$(CONFIG_CAN_CTUCANFD) := ctucanfd.o +ctucanfd-y := ctu_can_fd.o ctu_can_fd_hw.o diff --git a/drivers/net/can/ctucanfd/ctu_can_fd.c b/drivers/net/can/ctucanfd/ctu_can_fd.c new file mode 100644 index 000000000000..c8953627a9f9 --- /dev/null +++ b/drivers/net/can/ctucanfd/ctu_can_fd.c @@ -0,0 +1,1105 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/******************************************************************************* + * + * CTU CAN FD IP Core + * + * Copyright (C) 2015-2018 Ondrej Ille FEE CTU + * Copyright (C) 2018-2020 Ondrej Ille self-funded + * Copyright (C) 2018-2019 Martin Jerabek FEE CTU + * Copyright (C) 2018-2020 Pavel Pisa FEE CTU/self-funded + * + * Project advisors: + * Jiri Novak + * Pavel Pisa + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ctu_can_fd.h" +#include "ctu_can_fd_regs.h" + +#define DRV_NAME "ctucanfd" + +static const char * const ctucan_state_strings[] = { + "CAN_STATE_ERROR_ACTIVE", + "CAN_STATE_ERROR_WARNING", + "CAN_STATE_ERROR_PASSIVE", + "CAN_STATE_BUS_OFF", + "CAN_STATE_STOPPED", + "CAN_STATE_SLEEPING" +}; + +/* 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 + +static int ctucan_reset(struct net_device *ndev) +{ + int i; + struct ctucan_priv *priv = netdev_priv(ndev); + + netdev_dbg(ndev, "ctucan_reset"); + + ctucan_hw_reset(&priv->p); + 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; + + netdev_dbg(ndev, "ctucan_set_bittiming"); + + 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; + + netdev_dbg(ndev, "ctucan_set_data_bittiming"); + + 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; + + netdev_dbg(ndev, "ctucan_set_secondary_sample_point"); + + 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 + * + * This is the drivers start routine. + * + * 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; + + netdev_dbg(ndev, "ctucan_chip_start"); + + err = ctucan_reset(ndev); + if (err < 0) + return err; + + 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; + + netdev_dbg(ndev, "ctucan_do_set_mode"); + + switch (mode) { + case CAN_MODE_START: + 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 and failure value on error + */ +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; + netdev_dbg(ndev, "%s: using TXB#%u", __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?"); + return NETDEV_TX_BUSY; + } + 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; +} + +/** + * xcan_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 on success and 0 on failure. + */ +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_form_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.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; +} + +static const char *ctucan_state_to_str(enum can_state state) +{ + if (state >= 0 && state < CAN_STATE_MAX) + return ctucan_state_strings[state]; + return "UNKNOWN"; +} + +/** + * 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", + 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?)"); + + 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)!", + state, ctucan_state_to_str(state)); + break; + } + } + + /* Check for Arbitration Lost interrupt */ + if (isr.s.ali) { + if (dologerr) + netdev_info(ndev, " arbitration lost"); + 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"); + 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; + + framecnt = ctucan_hw_get_rx_frame_count(&priv->p); + netdev_dbg(ndev, "rx_poll: %d frames in RX FIFO", framecnt); + while (framecnt && work_done < quota) { + 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 fifo overflow"); + 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) { + 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); + netdev_dbg(ndev, "%s: from 0x%08x to 0x%08x", + __func__, priv->txb_prio, prio); + priv->txb_prio = prio; + priv->p.write_reg(&priv->p, CTU_CAN_FD_TX_PRIORITY, prio); +} + +/** + * xcan_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; + + netdev_dbg(ndev, "%s", __func__); + + /* 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); + + netdev_dbg(ndev, "TXI: TXB#%u: status 0x%x", + txb_idx, status); + + switch (status) { + case TXT_TOK: + netdev_dbg(ndev, "TXT_OK"); + 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"); + 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"); + 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)!", + 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); +} + +/** + * xcan_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; + int irq_loops; + + netdev_dbg(ndev, "ctucan_interrupt"); + + 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) { + netdev_dbg(ndev, "RXBNEI"); + 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) { + netdev_dbg(ndev, "TXBHCI"); + /* 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; + + netdev_dbg(ndev, "some ERR interrupt: clearing 0x%08x", + 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); + } + } + + { + union ctu_can_fd_int_stat imask; + + 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; + + netdev_dbg(ndev, "ctucan_chip_stop"); + + mask.u32 = 0xffffffff; + + /* Disable interrupts and disable CAN */ + 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; + + netdev_dbg(ndev, "ctucan_open"); + + 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 = 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; + } + + /* Common open */ + ret = open_candev(ndev); + if (ret) { + netdev_warn(ndev, "open_candev failed!\n"); + goto err_irq; + } + + ret = ctucan_chip_start(ndev); + if (ret < 0) { + netdev_err(ndev, "ctucan_chip_start failed!\n"); + goto err_candev; + } + + netdev_info(ndev, "ctu_can_fd device registered"); + can_led_event(ndev, CAN_LED_EVENT_OPEN); + napi_enable(&priv->napi); + netif_start_queue(ndev); + + return 0; + +err_candev: + close_candev(ndev); +err_irq: + free_irq(ndev->irq, ndev); +err: + 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); + + netdev_dbg(ndev, "ctucan_close"); + + 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; + + netdev_dbg(ndev, "ctucan_get_berr_counter"); + + 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); + + netdev_dbg(ndev, "ctucan_suspend"); + + 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); + + netdev_dbg(ndev, "ctucan_resume"); + + 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/ctu_can_fd.h b/drivers/net/can/ctucanfd/ctu_can_fd.h new file mode 100644 index 000000000000..0407c3a3a215 --- /dev/null +++ b/drivers/net/can/ctucanfd/ctu_can_fd.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/******************************************************************************* + * + * CTU CAN FD IP Core + * + * Copyright (C) 2015-2018 Ondrej Ille FEE CTU + * Copyright (C) 2018-2020 Ondrej Ille self-funded + * Copyright (C) 2018-2019 Martin Jerabek FEE CTU + * Copyright (C) 2018-2020 Pavel Pisa FEE CTU/self-funded + * + * Project advisors: + * Jiri Novak + * Pavel Pisa + * + * 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 __CTU_CAN_FD__ +#define __CTU_CAN_FD__ + +#include +#include +#include + +#include "ctu_can_fd_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_form_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 /*__CTU_CAN_FD__*/ diff --git a/drivers/net/can/ctucanfd/ctu_can_fd_frame.h b/drivers/net/can/ctucanfd/ctu_can_fd_frame.h new file mode 100644 index 000000000000..04d956c84ee7 --- /dev/null +++ b/drivers/net/can/ctucanfd/ctu_can_fd_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 FEE CTU + * Copyright (C) 2018-2020 Ondrej Ille self-funded + * Copyright (C) 2018-2019 Martin Jerabek FEE CTU + * Copyright (C) 2018-2020 Pavel Pisa FEE CTU/self-funded + * + * Project advisors: + * Jiri Novak + * Pavel Pisa + * + * 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_FORM_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_form_w { + uint32_t u32; + struct ctu_can_fd_frame_form_w_s { +#ifdef __LITTLE_ENDIAN_BITFIELD + /* FRAME_FORM_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_form_w_rtr { + NO_RTR_FRAME = 0x0, + RTR_FRAME = 0x1, +}; + +enum ctu_can_fd_frame_form_w_ide { + BASE = 0x0, + EXTENDED = 0x1, +}; + +enum ctu_can_fd_frame_form_w_fdf { + NORMAL_CAN = 0x0, + FD_CAN = 0x1, +}; + +enum ctu_can_fd_frame_form_w_brs { + BR_NO_SHIFT = 0x0, + BR_SHIFT = 0x1, +}; + +enum ctu_can_fd_frame_form_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_31_0 : 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_l_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/ctu_can_fd_hw.c b/drivers/net/can/ctucanfd/ctu_can_fd_hw.c new file mode 100644 index 000000000000..46944af86fa9 --- /dev/null +++ b/drivers/net/can/ctucanfd/ctu_can_fd_hw.c @@ -0,0 +1,790 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/******************************************************************************* + * + * CTU CAN FD IP Core + * + * Copyright (C) 2015-2018 Ondrej Ille FEE CTU + * Copyright (C) 2018-2020 Ondrej Ille self-funded + * Copyright (C) 2018-2019 Martin Jerabek FEE CTU + * Copyright (C) 2018-2020 Pavel Pisa FEE CTU/self-funded + * + * Project advisors: + * Jiri Novak + * Pavel Pisa + * + * 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 +#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 "ctu_can_fd_linux_defs.h" +#endif + +#include "ctu_can_fd_frame.h" +#include "ctu_can_fd_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; +} + +// TODO: rename or do not depend on previous value of id +static void ctucan_hw_hwid_to_id(union ctu_can_fd_identifier_w hwid, + canid_t *id, + enum ctu_can_fd_frame_form_w_ide type) +{ + /* Preserve flags which we dont set */ + *id &= ~(CAN_EFF_FLAG | CAN_EFF_MASK); + + if (type == EXTENDED) { + *id |= CAN_EFF_FLAG; + *id |= hwid.s.identifier_base << 18; + *id |= hwid.s.identifier_ext; + } else { + *id = hwid.s.identifier_base; + } +} + +static bool ctucan_hw_len_to_dlc(u8 len, u8 *dlc) +{ + *dlc = can_len2dlc(len); + return true; +} + +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); + + if (reg.s.sfr) + return true; + + return false; +} + +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_form_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_form_w ffw) +{ + union ctu_can_fd_identifier_w idw; + unsigned int i; + enum ctu_can_fd_frame_form_w_ide ide; + + idw.u32 = priv->read_reg(priv, CTU_CAN_FD_RX_DATA); + cf->can_id = 0; + cf->flags = 0; + + /* BRS, ESI, RTR Flags */ + 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; + } + + /* DLC */ + if (ffw.s.dlc <= 8) { + cf->len = ffw.s.dlc; + } else { + if (ffw.s.fdf == FD_CAN) + cf->len = (ffw.s.rwcnt - 3) << 2; + else + cf->len = 8; + } + + ide = (enum ctu_can_fd_frame_form_w_ide)ffw.s.ide; + ctucan_hw_hwid_to_id(idw, &cf->can_id, ide); + + /* 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 < cf->len; i += 4) { + u32 data = priv->read_reg(priv, CTU_CAN_FD_RX_DATA); + *(__le32 *)(cf->data + i) = cpu_to_le32(data); + } +} + +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; +} + +bool ctucan_hw_txt_buf_give_command(struct ctucan_hw_priv *priv, u8 cmd, u8 buf) +{ + union ctu_can_fd_tx_command reg; + + reg.u32 = 0; + + switch (buf) { + case CTU_CAN_FD_TXT_BUFFER_1: + reg.s.txb1 = 1; + break; + case CTU_CAN_FD_TXT_BUFFER_2: + reg.s.txb2 = 1; + break; + case CTU_CAN_FD_TXT_BUFFER_3: + reg.s.txb3 = 1; + break; + case CTU_CAN_FD_TXT_BUFFER_4: + reg.s.txb4 = 1; + break; + default: + return false; + } + + // TODO: use named constants for the command + if (cmd & 0x1) + reg.s.txce = 1; + else if (cmd & 0x2) + reg.s.txcr = 1; + else if (cmd & 0x4) + reg.s.txca = 1; + else + return false; + + priv->write_reg(priv, CTU_CAN_FD_TX_COMMAND, reg.u32); + 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_form_w ffw; + union ctu_can_fd_identifier_w idw; + u8 dlc; + 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 (!ctucan_hw_len_to_dlc(cf->len, &dlc)) + return false; + ffw.s.dlc = dlc; + + 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_FORM_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); +} + +// TODO: AL_CAPTURE and ERROR_CAPTURE diff --git a/drivers/net/can/ctucanfd/ctu_can_fd_hw.h b/drivers/net/can/ctucanfd/ctu_can_fd_hw.h new file mode 100644 index 000000000000..66995689290e --- /dev/null +++ b/drivers/net/can/ctucanfd/ctu_can_fd_hw.h @@ -0,0 +1,916 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/******************************************************************************* + * + * CTU CAN FD IP Core + * + * Copyright (C) 2015-2018 Ondrej Ille FEE CTU + * Copyright (C) 2018-2020 Ondrej Ille self-funded + * Copyright (C) 2018-2019 Martin Jerabek FEE CTU + * Copyright (C) 2018-2020 Pavel Pisa FEE CTU/self-funded + * + * Project advisors: + * Jiri Novak + * Pavel Pisa + * + * 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 __CTU_CAN_FD_HW__ +#define __CTU_CAN_FD_HW__ + +#include + +#if defined(__LITTLE_ENDIAN_BITFIELD) == defined(__BIG_ENDIAN_BITFIELD) +# error __BIG_ENDIAN_BITFIELD or __LITTLE_ENDIAN_BITFIELD must be defined. +#endif + +#include "ctu_can_fd_regs.h" +#include "ctu_can_fd_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; +#ifndef ctucan_hw_priv +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); +}; +#endif + +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_form_w + ctu_can_fd_read_rx_ffw(struct ctucan_hw_priv *priv) +{ + union ctu_can_fd_frame_form_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 wword 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_form_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 line buffer. + * @buf: TXT Buffer index (1 to CTU_CAN_FD_TXT_BUFFER_COUNT) + * Return: Status of the TXT Buffer. + */ +bool ctucan_hw_txt_buf_give_command(struct ctucan_hw_priv *priv, u8 cmd, + u8 buf); + +/** + * 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) + * Return: Status of the TXT Buffer. + */ +static inline void ctucan_hw_txt_set_empty(struct ctucan_hw_priv *priv, u8 buf) +{ + ctucan_hw_txt_buf_give_command(priv, 0x1, 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) + * Return: Status of the TXT Buffer. + */ +static inline void ctucan_hw_txt_set_rdy(struct ctucan_hw_priv *priv, u8 buf) +{ + ctucan_hw_txt_buf_give_command(priv, 0x2, 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) + * Return: Status of the TXT Buffer. + */ +static inline void ctucan_hw_txt_set_abort(struct ctucan_hw_priv *priv, u8 buf) +{ + ctucan_hw_txt_buf_give_command(priv, 0x4, 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/ctu_can_fd_regs.h b/drivers/net/can/ctucanfd/ctu_can_fd_regs.h new file mode 100644 index 000000000000..450f4b9fb3c4 --- /dev/null +++ b/drivers/net/can/ctucanfd/ctu_can_fd_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 FEE CTU + * Copyright (C) 2018-2020 Ondrej Ille self-funded + * Copyright (C) 2018-2019 Martin Jerabek FEE CTU + * Copyright (C) 2018-2020 Pavel Pisa FEE CTU/self-funded + * + * Project advisors: + * Jiri Novak + * Pavel Pisa + * + * 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