From patchwork Thu Oct 22 08:36:16 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Pavel Pisa X-Patchwork-Id: 11850593 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.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,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 5FB91C388F2 for ; Thu, 22 Oct 2020 08:40:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1095421775 for ; Thu, 22 Oct 2020 08:40:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2509566AbgJVIku (ORCPT ); Thu, 22 Oct 2020 04:40:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:35884 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2508237AbgJVIkt (ORCPT ); Thu, 22 Oct 2020 04:40:49 -0400 X-Greylist: delayed 172 seconds by postgrey-1.37 at lindbergh.monkeyblade.net; Thu, 22 Oct 2020 01:40:48 PDT 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 7E51AC0613CF for ; Thu, 22 Oct 2020 01:40:48 -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 09M8dfBi060066; Thu, 22 Oct 2020 10:39:41 +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 09M8dehV005891; Thu, 22 Oct 2020 10:39:40 +0200 Received: (from pisa@localhost) by haar.felk.cvut.cz (8.14.0/8.13.7/Submit) id 09M8dewk005890; Thu, 22 Oct 2020 10:39:40 +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 , Rob Herring Subject: [PATCH v6 1/6] dt-bindings: vendor-prefix: add prefix for the Czech Technical University in Prague. Date: Thu, 22 Oct 2020 10:36:16 +0200 Message-Id: <805eb7aa82e31d11a05ce830b40eea5dd0c4f6c0.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: 09M8dfBi060066 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: 1603960790.34189@QdabvQiAxWmlcZVHrDrqGQ Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org The Czech Technical University in Prague (CTU) is one of the biggest and oldest (founded 1707) technical universities in Europe. The abbreviation in Czech language is ČVUT according to official name in Czech language České vysoké učení technické v Praze The English translation The Czech Technical University in Prague The university pages in English https://www.cvut.cz/en Signed-off-by: Pavel Pisa Acked-by: Rob Herring --- Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 967e78c5ec0a..dedb10f1b250 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -215,6 +215,8 @@ patternProperties: description: Hangzhou C-SKY Microsystems Co., Ltd "^csq,.*": description: Shenzen Chuangsiqi Technology Co.,Ltd. + "^ctu,.*": + description: Czech Technical University in Prague "^cubietech,.*": description: Cubietech, Ltd. "^cypress,.*": From patchwork Thu Oct 22 08:36:17 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Pisa X-Patchwork-Id: 11850595 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 331F6C4363A for ; Thu, 22 Oct 2020 08:41:23 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DC4682168B for ; Thu, 22 Oct 2020 08:41:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2508262AbgJVIlW (ORCPT ); Thu, 22 Oct 2020 04:41:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:35974 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2440786AbgJVIlV (ORCPT ); Thu, 22 Oct 2020 04:41:21 -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 656A7C0613CE for ; Thu, 22 Oct 2020 01:41:21 -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 09M8eFrs060102; Thu, 22 Oct 2020 10:40:15 +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 09M8eEVU006012; Thu, 22 Oct 2020 10:40:14 +0200 Received: (from pisa@localhost) by haar.felk.cvut.cz (8.14.0/8.13.7/Submit) id 09M8eEHW006011; Thu, 22 Oct 2020 10:40:14 +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 , Rob Herring Subject: [PATCH v6 2/6] dt-bindings: net: can: binding for CTU CAN FD open-source IP core. Date: Thu, 22 Oct 2020 10:36:17 +0200 Message-Id: <8c2c52067354e9ccb76d7923a0e8b7765902e4a1.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: 09M8eFrs060102 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: 1603960822.17904@l/w1XnyT74tRmycn+bkOcw Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org The device-tree bindings for open-source/open-hardware CAN FD IP core designed at the Czech Technical University in Prague. CTU CAN FD IP core and other CTU CAN bus related projects listing and documentation page http://canbus.pages.fel.cvut.cz/ Signed-off-by: Pavel Pisa Reviewed-by: Rob Herring --- .../bindings/net/can/ctu,ctucanfd.yaml | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/can/ctu,ctucanfd.yaml diff --git a/Documentation/devicetree/bindings/net/can/ctu,ctucanfd.yaml b/Documentation/devicetree/bindings/net/can/ctu,ctucanfd.yaml new file mode 100644 index 000000000000..5113bb419ec1 --- /dev/null +++ b/Documentation/devicetree/bindings/net/can/ctu,ctucanfd.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/can/ctu,ctucanfd.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: CTU CAN FD Open-source IP Core Device Tree Bindings + +description: | + Open-source CAN FD IP core developed at the Czech Technical University in Prague + + The core sources and documentation on project page + [1] sources : https://gitlab.fel.cvut.cz/canbus/ctucanfd_ip_core + [2] datasheet : https://canbus.pages.fel.cvut.cz/ctucanfd_ip_core/Progdokum.pdf + + Integration in Xilinx Zynq SoC based system together with + OpenCores SJA1000 compatible controllers + [3] project : https://gitlab.fel.cvut.cz/canbus/zynq/zynq-can-sja1000-top + Martin Jerabek dimploma thesis with integration and testing + framework description + [4] PDF : https://dspace.cvut.cz/bitstream/handle/10467/80366/F3-DP-2019-Jerabek-Martin-Jerabek-thesis-2019-canfd.pdf + +maintainers: + - Pavel Pisa + - Ondrej Ille + - Martin Jerabek + +properties: + compatible: + oneOf: + - items: + - const: ctu,ctucanfd-2 + - const: ctu,ctucanfd + - const: ctu,ctucanfd + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + description: | + phandle of reference clock (100 MHz is appropriate + for FPGA implementation on Zynq-7000 system). + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + +additionalProperties: false + +examples: + - | + ctu_can_fd_0: can@43c30000 { + compatible = "ctu,ctucanfd"; + interrupts = <0 30 4>; + clocks = <&clkc 15>; + reg = <0x43c30000 0x10000>; + }; 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 From patchwork Thu Oct 22 08:36:19 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Pisa X-Patchwork-Id: 11850599 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 E81FFC4363A for ; Thu, 22 Oct 2020 08:43:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 89ECD21775 for ; Thu, 22 Oct 2020 08:43:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2504818AbgJVInL (ORCPT ); Thu, 22 Oct 2020 04:43:11 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36266 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2504602AbgJVInK (ORCPT ); Thu, 22 Oct 2020 04:43:10 -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 087E7C0613CE for ; Thu, 22 Oct 2020 01:43:09 -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 09M8fuYl060223; Thu, 22 Oct 2020 10:41:56 +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 09M8ftf1006331; Thu, 22 Oct 2020 10:41:55 +0200 Received: (from pisa@localhost) by haar.felk.cvut.cz (8.14.0/8.13.7/Submit) id 09M8ftRN006330; Thu, 22 Oct 2020 10:41:55 +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 4/6] can: ctucanfd: CTU CAN FD open-source IP core - PCI bus support. Date: Thu, 22 Oct 2020 10:36:19 +0200 Message-Id: <9783a6d0a3e79ca4106cf1794aa06c8436700137.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: 09M8fuYl060223 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: 1603960927.81916@mD/oqffjSPSrB6PRRuqqLA Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org PCI bus adaptation for CTU CAN FD open-source IP core. The project providing FPGA design for Intel EP4CGX15 based DB4CGX15 PCIe board with PiKRON.com designed transceiver riser shield is available at https://gitlab.fel.cvut.cz/canbus/pcie-ctu_can_fd . Signed-off-by: Pavel Pisa Signed-off-by: Martin Jerabek Signed-off-by: Ondrej Ille --- drivers/net/can/ctucanfd/Kconfig | 9 + drivers/net/can/ctucanfd/Makefile | 3 + drivers/net/can/ctucanfd/ctu_can_fd_pci.c | 314 ++++++++++++++++++++++ 3 files changed, 326 insertions(+) create mode 100644 drivers/net/can/ctucanfd/ctu_can_fd_pci.c diff --git a/drivers/net/can/ctucanfd/Kconfig b/drivers/net/can/ctucanfd/Kconfig index b6d47bba7150..fb4d3cda6885 100644 --- a/drivers/net/can/ctucanfd/Kconfig +++ b/drivers/net/can/ctucanfd/Kconfig @@ -12,4 +12,13 @@ config CAN_CTUCANFD if CAN_CTUCANFD +config CAN_CTUCANFD_PCI + tristate "CTU CAN-FD IP core PCI/PCIe driver" + depends on PCI + help + This driver adds PCI/PCIe support for CTU CAN-FD IP core. + The project providing FPGA design for Intel EP4CGX15 based DB4CGX15 + PCIe board with PiKRON.com designed transceiver riser shield is available + at https://gitlab.fel.cvut.cz/canbus/pcie-ctu_can_fd . + endif diff --git a/drivers/net/can/ctucanfd/Makefile b/drivers/net/can/ctucanfd/Makefile index 8d47008d0076..eb945260952d 100644 --- a/drivers/net/can/ctucanfd/Makefile +++ b/drivers/net/can/ctucanfd/Makefile @@ -5,3 +5,6 @@ obj-$(CONFIG_CAN_CTUCANFD) := ctucanfd.o ctucanfd-y := ctu_can_fd.o ctu_can_fd_hw.o + +obj-$(CONFIG_CAN_CTUCANFD_PCI) += ctucanfd_pci.o +ctucanfd_pci-y := ctu_can_fd_pci.o diff --git a/drivers/net/can/ctucanfd/ctu_can_fd_pci.c b/drivers/net/can/ctucanfd/ctu_can_fd_pci.c new file mode 100644 index 000000000000..c4542eac2747 --- /dev/null +++ b/drivers/net/can/ctucanfd/ctu_can_fd_pci.c @@ -0,0 +1,314 @@ +// 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 "ctu_can_fd.h" + +#define DRV_NAME "ctucanfd_pci" + +#ifndef PCI_DEVICE_DATA +#define PCI_DEVICE_DATA(vend, dev, data) \ +.vendor = PCI_VENDOR_ID_##vend, \ +.device = PCI_DEVICE_ID_##vend##_##dev, \ +.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0, 0, \ +.driver_data = (kernel_ulong_t)(data) +#endif +#ifndef PCI_VENDOR_ID_TEDIA +#define PCI_VENDOR_ID_TEDIA 0x1760 +#endif + +#define PCI_DEVICE_ID_ALTERA_CTUCAN_TEST 0xCAFD +#define PCI_DEVICE_ID_TEDIA_CTUCAN_VER21 0xff00 + +#define CTUCAN_BAR0_CTUCAN_ID 0x0000 +#define CTUCAN_BAR0_CRA_BASE 0x4000 +#define CYCLONE_IV_CRA_A2P_IE (0x0050) + +#define CTUCAN_WITHOUT_CTUCAN_ID 0 +#define CTUCAN_WITH_CTUCAN_ID 1 + +static bool use_msi = 1; +module_param(use_msi, bool, 0444); +MODULE_PARM_DESC(use_msi, "PCIe implementation use MSI interrupts. Default: 1 (yes)"); + +static bool pci_use_second = 1; +module_param(pci_use_second, bool, 0444); +MODULE_PARM_DESC(pci_use_second, "Use the second CAN core on PCIe card. Default: 1 (yes)"); + +struct ctucan_pci_board_data { + void __iomem *bar0_base; + void __iomem *cra_base; + void __iomem *bar1_base; + struct list_head ndev_list_head; + int use_msi; +}; + +static struct ctucan_pci_board_data *ctucan_pci_get_bdata(struct pci_dev *pdev) +{ + return (struct ctucan_pci_board_data *)pci_get_drvdata(pdev); +} + +static void ctucan_pci_set_drvdata(struct device *dev, + struct net_device *ndev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct ctucan_priv *priv = netdev_priv(ndev); + struct ctucan_pci_board_data *bdata = ctucan_pci_get_bdata(pdev); + + list_add(&priv->peers_on_pdev, &bdata->ndev_list_head); + priv->irq_flags = IRQF_SHARED; +} + +/** + * ctucan_pci_probe - PCI registration call + * @pdev: Handle to the pci device structure + * @ent: Pointer to the entry from ctucan_pci_tbl + * + * This function does all the memory allocation and registration for the CAN + * device. + * + * Return: 0 on success and failure value on error + */ +static int ctucan_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct device *dev = &pdev->dev; + unsigned long driver_data = ent->driver_data; + struct ctucan_pci_board_data *bdata; + void __iomem *addr; + void __iomem *cra_addr; + void __iomem *bar0_base; + u32 cra_a2p_ie; + u32 ctucan_id = 0; + int ret; + unsigned int ntxbufs; + unsigned int num_cores = 1; + unsigned int core_i = 0; + int irq; + int msi_ok = 0; + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(dev, "pci_enable_device FAILED\n"); + goto err; + } + + ret = pci_request_regions(pdev, KBUILD_MODNAME); + if (ret) { + dev_err(dev, "pci_request_regions FAILED\n"); + goto err_disable_device; + } + + if (use_msi) { + ret = pci_enable_msi(pdev); + if (!ret) { + dev_info(dev, "MSI enabled\n"); + pci_set_master(pdev); + msi_ok = 1; + } + } + + dev_info(dev, "ctucan BAR0 0x%08llx 0x%08llx\n", + (long long)pci_resource_start(pdev, 0), + (long long)pci_resource_len(pdev, 0)); + + dev_info(dev, "ctucan BAR1 0x%08llx 0x%08llx\n", + (long long)pci_resource_start(pdev, 1), + (long long)pci_resource_len(pdev, 1)); + + addr = pci_iomap(pdev, 1, pci_resource_len(pdev, 1)); + if (!addr) { + dev_err(dev, "PCI BAR 1 cannot be mapped\n"); + ret = -ENOMEM; + goto err_release_regions; + } + + /* Cyclone IV PCI Express Control Registers Area */ + bar0_base = pci_iomap(pdev, 0, pci_resource_len(pdev, 0)); + if (!bar0_base) { + dev_err(dev, "PCI BAR 0 cannot be mapped\n"); + ret = -EIO; + goto err_pci_iounmap_bar1; + } + + if (driver_data == CTUCAN_WITHOUT_CTUCAN_ID) { + cra_addr = bar0_base; + num_cores = 2; + } else { + cra_addr = bar0_base + CTUCAN_BAR0_CRA_BASE; + ctucan_id = ioread32(bar0_base + CTUCAN_BAR0_CTUCAN_ID); + dev_info(dev, "ctucan_id 0x%08lx\n", (unsigned long)ctucan_id); + num_cores = ctucan_id & 0xf; + } + + irq = pdev->irq; + + ntxbufs = 4; + + bdata = kzalloc(sizeof(*bdata), GFP_KERNEL); + if (!bdata) { + ret = -ENOMEM; + goto err_pci_iounmap_bar0; + } + + INIT_LIST_HEAD(&bdata->ndev_list_head); + bdata->bar0_base = bar0_base; + bdata->cra_base = cra_addr; + bdata->bar1_base = addr; + bdata->use_msi = msi_ok; + + pci_set_drvdata(pdev, bdata); + + ret = ctucan_probe_common(dev, addr, irq, ntxbufs, 100000000, + 0, ctucan_pci_set_drvdata); + if (ret < 0) + goto err_free_board; + + core_i++; + + while (pci_use_second && (core_i < num_cores)) { + addr += 0x4000; + ret = ctucan_probe_common(dev, addr, irq, ntxbufs, 100000000, + 0, ctucan_pci_set_drvdata); + if (ret < 0) { + dev_info(dev, "CTU CAN FD core %d initialization failed\n", + core_i); + break; + } + core_i++; + } + + /* enable interrupt in + * Avalon-MM to PCI Express Interrupt Enable Register + */ + cra_a2p_ie = ioread32(cra_addr + CYCLONE_IV_CRA_A2P_IE); + dev_info(dev, "cra_a2p_ie 0x%08x\n", cra_a2p_ie); + cra_a2p_ie |= 1; + iowrite32(cra_a2p_ie, cra_addr + CYCLONE_IV_CRA_A2P_IE); + cra_a2p_ie = ioread32(cra_addr + CYCLONE_IV_CRA_A2P_IE); + dev_info(dev, "cra_a2p_ie 0x%08x\n", cra_a2p_ie); + + return 0; + +err_free_board: + pci_set_drvdata(pdev, NULL); + kfree(bdata); +err_pci_iounmap_bar0: + pci_iounmap(pdev, cra_addr); +err_pci_iounmap_bar1: + pci_iounmap(pdev, addr); +err_release_regions: + if (msi_ok) { + pci_disable_msi(pdev); + pci_clear_master(pdev); + } + pci_release_regions(pdev); +err_disable_device: + pci_disable_device(pdev); +err: + return ret; +} + +/** + * ctucan_pci_remove - Unregister the device after releasing the resources + * @pdev: Handle to the pci device structure + * + * This function frees all the resources allocated to the device. + * Return: 0 always + */ +static void ctucan_pci_remove(struct pci_dev *pdev) +{ + struct net_device *ndev; + struct ctucan_priv *priv = NULL; + struct ctucan_pci_board_data *bdata = ctucan_pci_get_bdata(pdev); + + dev_dbg(&pdev->dev, "ctucan_remove"); + + if (!bdata) { + dev_err(&pdev->dev, "%s: no list of devices\n", __func__); + return; + } + + /* disable interrupt in + * Avalon-MM to PCI Express Interrupt Enable Register + */ + if (bdata->cra_base) + iowrite32(0, bdata->cra_base + CYCLONE_IV_CRA_A2P_IE); + + while ((priv = list_first_entry_or_null(&bdata->ndev_list_head, struct ctucan_priv, + peers_on_pdev)) != NULL) { + ndev = priv->can.dev; + + unregister_candev(ndev); + + netif_napi_del(&priv->napi); + + list_del_init(&priv->peers_on_pdev); + free_candev(ndev); + } + + pci_iounmap(pdev, bdata->bar1_base); + + if (bdata->use_msi) { + pci_disable_msi(pdev); + pci_clear_master(pdev); + } + + pci_release_regions(pdev); + pci_disable_device(pdev); + + pci_iounmap(pdev, bdata->bar0_base); + + pci_set_drvdata(pdev, NULL); + kfree(bdata); +} + +static SIMPLE_DEV_PM_OPS(ctucan_pci_pm_ops, ctucan_suspend, ctucan_resume); + +static const struct pci_device_id ctucan_pci_tbl[] = { + {PCI_DEVICE_DATA(TEDIA, CTUCAN_VER21, + CTUCAN_WITH_CTUCAN_ID)}, + {}, +}; + +static struct pci_driver ctucan_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = ctucan_pci_tbl, + .probe = ctucan_pci_probe, + .remove = ctucan_pci_remove, + .driver.pm = &ctucan_pci_pm_ops, +}; + +module_pci_driver(ctucan_pci_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Pavel Pisa"); +MODULE_DESCRIPTION("CTU CAN FD for PCI bus"); From patchwork Thu Oct 22 08:36:20 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Pavel Pisa X-Patchwork-Id: 11850601 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=-17.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI, MENTIONS_GIT_HOSTING,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 F0BFCC4363A for ; Thu, 22 Oct 2020 08:43:43 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id AD2D921775 for ; Thu, 22 Oct 2020 08:43:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2509600AbgJVInn (ORCPT ); Thu, 22 Oct 2020 04:43:43 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36354 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2504409AbgJVInm (ORCPT ); Thu, 22 Oct 2020 04:43:42 -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 EDC37C0613CE for ; Thu, 22 Oct 2020 01:43:41 -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 09M8gcDU060272; Thu, 22 Oct 2020 10:42:38 +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 09M8gb3K006478; Thu, 22 Oct 2020 10:42:37 +0200 Received: (from pisa@localhost) by haar.felk.cvut.cz (8.14.0/8.13.7/Submit) id 09M8gbCh006477; Thu, 22 Oct 2020 10:42:37 +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 5/6] can: ctucanfd: CTU CAN FD open-source IP core - platform/SoC support. Date: Thu, 22 Oct 2020 10:36:20 +0200 Message-Id: <2a90e1a7d57f0fec42604cd399acf25af5689148.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: 09M8gcDU060272 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: 1603960965.1245@g3vYV4jPK5oDGRrnpJ5DnA Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org Platform bus adaptation for CTU CAN FD open-source IP core. The core has been tested together with OpenCores SJA1000 modified to be CAN FD frames tolerant on MicroZed Zynq based MZ_APO education kits designed by Petr Porazil from PiKRON.com company. FPGA design https://gitlab.fel.cvut.cz/canbus/zynq/zynq-can-sja1000-top. The kit description at the Computer Architectures course pages https://cw.fel.cvut.cz/wiki/courses/b35apo/documentation/mz_apo/start . Kit carrier board and mechanics design source files https://gitlab.com/pikron/projects/mz_apo/microzed_apo The work is documented in Martin Jeřábek's diploma theses Open-source and Open-hardware CAN FD Protocol Support https://dspace.cvut.cz/handle/10467/80366 . Signed-off-by: Pavel Pisa Signed-off-by: Martin Jerabek Signed-off-by: Ondrej Ille --- drivers/net/can/ctucanfd/Kconfig | 11 ++ drivers/net/can/ctucanfd/Makefile | 3 + .../net/can/ctucanfd/ctu_can_fd_platform.c | 142 ++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 drivers/net/can/ctucanfd/ctu_can_fd_platform.c diff --git a/drivers/net/can/ctucanfd/Kconfig b/drivers/net/can/ctucanfd/Kconfig index fb4d3cda6885..01fcfe5873cc 100644 --- a/drivers/net/can/ctucanfd/Kconfig +++ b/drivers/net/can/ctucanfd/Kconfig @@ -21,4 +21,15 @@ config CAN_CTUCANFD_PCI PCIe board with PiKRON.com designed transceiver riser shield is available at https://gitlab.fel.cvut.cz/canbus/pcie-ctu_can_fd . +config CAN_CTUCANFD_PLATFORM + tristate "CTU CAN-FD IP core platform (FPGA, SoC) driver" + depends on OF || COMPILE_TEST + help + The core has been tested together with OpenCores SJA1000 + modified to be CAN FD frames tolerant on MicroZed Zynq based + MZ_APO education kits designed by Petr Porazil from PiKRON.com + company. FPGA design https://gitlab.fel.cvut.cz/canbus/zynq/zynq-can-sja1000-top. + The kit description at the Computer Architectures course pages + https://cw.fel.cvut.cz/b182/courses/b35apo/documentation/mz_apo/start . + endif diff --git a/drivers/net/can/ctucanfd/Makefile b/drivers/net/can/ctucanfd/Makefile index eb945260952d..a77ca72d534e 100644 --- a/drivers/net/can/ctucanfd/Makefile +++ b/drivers/net/can/ctucanfd/Makefile @@ -8,3 +8,6 @@ ctucanfd-y := ctu_can_fd.o ctu_can_fd_hw.o obj-$(CONFIG_CAN_CTUCANFD_PCI) += ctucanfd_pci.o ctucanfd_pci-y := ctu_can_fd_pci.o + +obj-$(CONFIG_CAN_CTUCANFD_PLATFORM) += ctucanfd_platform.o +ctucanfd_platform-y += ctu_can_fd_platform.o diff --git a/drivers/net/can/ctucanfd/ctu_can_fd_platform.c b/drivers/net/can/ctucanfd/ctu_can_fd_platform.c new file mode 100644 index 000000000000..c35b16b8566b --- /dev/null +++ b/drivers/net/can/ctucanfd/ctu_can_fd_platform.c @@ -0,0 +1,142 @@ +// 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 "ctu_can_fd.h" + +#define DRV_NAME "ctucanfd" + +static void ctucan_platform_set_drvdata(struct device *dev, + struct net_device *ndev) +{ + struct platform_device *pdev = container_of(dev, struct platform_device, + dev); + + platform_set_drvdata(pdev, ndev); +} + +/** + * ctucan_platform_probe - Platform registration call + * @pdev: Handle to the platform device structure + * + * This function does all the memory allocation and registration for the CAN + * device. + * + * Return: 0 on success and failure value on error + */ +static int ctucan_platform_probe(struct platform_device *pdev) +{ + struct resource *res; /* IO mem resources */ + struct device *dev = &pdev->dev; + void __iomem *addr; + int ret; + unsigned int ntxbufs; + int irq; + + /* Get the virtual base address for the device */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = devm_ioremap_resource(dev, res); + if (IS_ERR(addr)) { + dev_err(dev, "Cannot remap address.\n"); + ret = PTR_ERR(addr); + goto err; + } + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "Cannot find interrupt.\n"); + ret = irq; + goto err; + } + + /* Number of tx bufs might be change in HW for future. If so, + * it will be passed as property via device tree + */ + ntxbufs = 4; + ret = ctucan_probe_common(dev, addr, irq, ntxbufs, 0, + 1, ctucan_platform_set_drvdata); + + if (ret < 0) + platform_set_drvdata(pdev, NULL); + +err: + return ret; +} + +/** + * ctucan_platform_remove - Unregister the device after releasing the resources + * @pdev: Handle to the platform device structure + * + * This function frees all the resources allocated to the device. + * Return: 0 always + */ +static int ctucan_platform_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct ctucan_priv *priv = netdev_priv(ndev); + + netdev_dbg(ndev, "ctucan_remove"); + + unregister_candev(ndev); + pm_runtime_disable(&pdev->dev); + netif_napi_del(&priv->napi); + free_candev(ndev); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(ctucan_platform_pm_ops, ctucan_suspend, ctucan_resume); + +/* Match table for OF platform binding */ +static const struct of_device_id ctucan_of_match[] = { + { .compatible = "ctu,ctucanfd-2", }, + { .compatible = "ctu,ctucanfd", }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, ctucan_of_match); + +static struct platform_driver ctucanfd_driver = { + .probe = ctucan_platform_probe, + .remove = ctucan_platform_remove, + .driver = { + .name = DRV_NAME, + .pm = &ctucan_platform_pm_ops, + .of_match_table = ctucan_of_match, + }, +}; + +module_platform_driver(ctucanfd_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Martin Jerabek"); +MODULE_DESCRIPTION("CTU CAN FD for platform"); From patchwork Thu Oct 22 08:36:21 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Pavel Pisa X-Patchwork-Id: 11850603 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=-16.5 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,LOTS_OF_MONEY,MAILING_LIST_MULTI, MENTIONS_GIT_HOSTING,NORMAL_HTTP_TO_IP,NUMERIC_HTTP_ADDR,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 CAB32C4363A for ; Thu, 22 Oct 2020 08:45:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id F3DA821775 for ; Thu, 22 Oct 2020 08:45:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2506323AbgJVIpH (ORCPT ); Thu, 22 Oct 2020 04:45:07 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36566 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2504909AbgJVIpD (ORCPT ); Thu, 22 Oct 2020 04:45:03 -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 44B13C0613CE for ; Thu, 22 Oct 2020 01:45:02 -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 09M8hnkS060340; Thu, 22 Oct 2020 10:43:49 +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 09M8hm1m006789; Thu, 22 Oct 2020 10:43:48 +0200 Received: (from pisa@localhost) by haar.felk.cvut.cz (8.14.0/8.13.7/Submit) id 09M8hmw4006788; Thu, 22 Oct 2020 10:43:48 +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 6/6] docs: ctucanfd: CTU CAN FD open-source IP core documentation. Date: Thu, 22 Oct 2020 10:36:21 +0200 Message-Id: <213155c64da5a97c574cd15de1cb06f8d0acef6a.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: 09M8hnkS060340 X-FELK-MailScanner: Found to be clean X-FELK-MailScanner-SpamCheck: not spam, SpamAssassin (not cached, score=1.146, required 6, BAYES_00 -0.50, KHOP_HELO_FCRDNS 0.40, LOTS_OF_MONEY 0.00, NORMAL_HTTP_TO_IP 0.00, NUMERIC_HTTP_ADDR 1.24, SPF_HELO_NONE 0.00, SPF_NONE 0.00, URIBL_BLOCKED 0.00) X-FELK-MailScanner-SpamScore: s X-FELK-MailScanner-From: pisa@cmp.felk.cvut.cz X-FELK-MailScanner-Watermark: 1603961031.23706@EG/CaUsx3ClLN68BGig6LA Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org CTU CAN FD IP core documentation based on Martin Jeřábek's diploma theses Open-source and Open-hardware CAN FD Protocol Support https://dspace.cvut.cz/handle/10467/80366 . Signed-off-by: Pavel Pisa Signed-off-by: Martin Jerabek Signed-off-by: Ondrej Ille --- .../ctu/FSM_TXT_Buffer_user.png | Bin 0 -> 174807 bytes .../device_drivers/ctu/ctucanfd-driver.rst | 638 ++++++++++++++++++ 2 files changed, 638 insertions(+) create mode 100644 Documentation/networking/device_drivers/ctu/FSM_TXT_Buffer_user.png create mode 100644 Documentation/networking/device_drivers/ctu/ctucanfd-driver.rst diff --git a/Documentation/networking/device_drivers/ctu/FSM_TXT_Buffer_user.png b/Documentation/networking/device_drivers/ctu/FSM_TXT_Buffer_user.png new file mode 100644 index 0000000000000000000000000000000000000000..5dceb594fca1f668fbc2eb7c1fb5706e99137d28 GIT binary patch literal 174807 zcmb5W1yq!8+bs?fN}~uUNJ~q1OM@aI-67pwLwBRnH3%r(-3UY1NXO9KLwEci{Jr1% zz27-c5U^xrBt9b`AYULLAT>Ng1zvH1 z|5gN^P>nuHOCUTv{`=9A7YDq8W-p`Zgn+W7ErtJ>4S{esQBjACXNSge&%p;-CLsIggJb|NAFgF--Xv2WbEL9q=wT8QFT; z|9*QXIrTF5zdwu{7#{@s&kuk8fBLYBAE*Dn2J@=o)3rU%DD671XS0=g`;CI%;w44U ztG93G@ctQJhqJ_$&^0g8z?+NL)d8g<$;QhI5D1YASzzpudxKVVpj-oK(V zqn3%tR#ddf{xRFx=-qXDbYAdN>gmNU2$o^HBUkbY%RgufX*~&;rxh#F4xxYMs}rT- zOoRGi!FBe=eWO>SJ68(V^3UfP^JPUPq|nHn3O=2eDrwh3bD-N6Yi#_994y6?bLeK) z=RQ^gV1U5P|C-7$j1{a*19c|*G z^yD>>l?3sRgNa?MG;Mmj@+D{bw~{E&rv?@!BZtu5mzQpqeYKTrw{|i3gMyo{G|`0!S<_T5po_eVG?H&J*x41YJ~Bu)N&3X#YQ4bA_x{=A7Qq!emHnzfr=nrg zvF(UwkKG>IFwqVZK%e%ipCjhYcur_*upVXj?p0=QN>vX@gX*or{4(~g=2hO1Pb^$t zIEF80)w2dmpOksCEc1gW^&cLT|q7)h}}_EitLwSjBR%LEzqo< zqxRfne9Y2q)Y?UH!hGE>lXO)>7D+@mH5}dC!sXm@o~a~8&rZ4sf>+uwhi-C4KdYiyv(NWL_3rFeYEybRJncQoVAcvdw8W3_igI(qKkPpDimw`_l2oG0=T=g*F`92Rx_7@B_bE#^b!#l(s4?P1evyq6 zuOIRlBP2w|@>RXehVAthoS*oyIQG*OaGeAOQ_04{`H(9G<^}KdxK%%?sr(+Q8q*M_ zo**{PD74;aP>59qMn3t5F7-B~!uT`0Q+FrcjH|+AhD zMFxIlw~LwNB*dN*p6qK{EKG6NPJ3A1J+~|8u~K=%n2SZhE+K7Llj7#O5vM)bIV&Wo zd+^0MAABTE*-KxXH?y;3AWEVfBJI{_t!A5WaX&YR%y$@!yYl3#y@Jk*TGFDmMd67t z)0ij|YfI!TRHlSz0&ILP`}%y+G>D{VO~E3hrDt(qLs3a+7JF~j30u(Q z=L?2Np6&G&A(Gb>hfkF4n^;O=@(jO{k`kMuSd_krq%S!^cH)74IKe1K7+n>=+8>I$ zJQ`?X)g)7Few#Vk48ci-R@F?yEz&drqKaR{0HxG`*eWi1W1GKSDy=xY~*LTsFMw zyyYC->tJ6=$v*s4lFsOp^6-vabb`gGdcqgkLGGbbg{lk(t(I@O^CCZm_BVOOZc`bt znLAa?>h1}tek_^~)+;*F-Z1uvbDKUojnX*vC;Hd@jY_U$GnUo4D?Is58OV<}@`tA($NwX!GKh8{eV{q-z zR-9^)q7}u)sSWEVu^A?HMOuG7p(QLQ$`hf@h!{8^M6(+e@{t^_+TYyT>pN|y$}q-R zt>W|EhYc+ox#_utns|k->7~48dD?_4rb9mJq@d&cuhekA{lBILH&6CewRmh4%DIRq z2OPASmqT5n#2ac){fW35Yw>s_m3TE5%{UY zzGhWORU@`Q5C3Ogh+OPR`C!-H2o{l_iwKz6`TYZfT#;&upz~K&O-({>E=_7`s=2lG z={;zpbznWaq2YDXz==tpX_aAx#;gd0h^YCZM2)eeqy%B>%o7te@FLRM-5pdGxwK?Z zt);*P@$*B7lAVRiB)R~{2=55fWr)(7FivRj*{XRM)*⪚Se7I3 zB_Qh?!B8iM`Z*l|YJ9(JqN)p*XaWKF-n%Wwmi4p!%#k6r9zVFWTz)HT^QjSxc#xZc zs6@kB{*62jx_jutHF!30qNdDFhPH*`#`3;i!VcbABF)$^Y9=p)qM)kl7luo+8Yj#V1XlF>rq< zaR4D}F*8$C<7@4}{xATr)>#znvy1!Sn<@0qZx0K<>9!8nVU=a_2f>F1wrage-ECN| zRB6Oc{hNIjb@(p3{K~aT;u8{-l$1m@HD4zrB*Z5se%92Kb#bZD51SevU&C5ucJXXk zAU@4NyIWaV35$)TrKb-M3POgrg@=bP7$`lXV0JlLMpa{aJv7=!FIaOQ#)GL{b@sAK zy6gG#Y{(v-GYN~$n!Cl9vdNNGO&Roln3fceBxxEOI%p;;1;_8b z)*PXsqKH?LT^1@;ApBuE67(~&RsXJHKaVl(YQAZIGokT4miQO(XE>wTwgMOYpXdCx zix+(4GfxC0w=?fjc)0y}s*W8P(P6Rbo*;Eol8e{h1=I@H&?!2D-#)GYrzNzRk;RT5 z@i!r#N&I-~ReQ%bpOYBpo&)ETOVEr8m!=XT4^;lkRlJH_vP)rQ%BHZ8yl74MkTk=q zZ?C$6tVtOSHnatBOU};{w|U57>XO?pHV*2Q^z+?CzZGlI7Wr6HcC24hA+#Ac~8wn*2#dpowj6`eC?jD{{kpe0;q9&N%(?@o{rq9Ie7< zMMb}PTe9Q_TrP%=XvCmk^JhG$?skYo&oC?=;OuW@#0~s5ImC$bDX0ICehnr@;ypRa zqWfcR*%tQ4$i>jj{eJR|JWAECmOPudnQ%zKA7j@Mb4O3RPo{+OQw`q4R_5kEvapL18qJB>48bbIyqhviq!O{VWeHZQ^CW$uk28@3jng3VHh$1u9wyLKOmEf|3W4~kH};f z*XUPI=rJsf_GmT9&9IW+-Ax@al7_}3F#p1h&@(9`onYPsUe%g!UjFqB{vckR{E?bM z8V60Qe(jCirThFiMMiDxhhIjwUu6#Tg%be`B#C&QQMg}zUjvG4KB*ccCFwyL=<&#m z#2jIB(0KbW?fg9!#?b6ydOwZqh`x(B?OB~YU~eZ4o6z|$NP>c}zP@g3X6AIbBwATj z)i*S>K3<@_DWsxS5>hY26wbC^7i7-lprl19CWNS`Ap`nKNRWMmvCT})q?W{UYQ^4U z&Yp?(prbM|LtkdmraC@bP*}I>deN-cG%s&!%iPe=Kms1yJu@~oW_4VXq4|{B`BOG7 znb(054eiAs1_dhEZeoT*xu!F;PJv6&%92Fo#;Fws)>Ygj2@$Zfe!?S9HoH63ebww{ z68KC1oR)j=y{RR#bOdp4k$G-&yO@m=V>A;u($4X@IuLCI(JnfvMNtH+jRfsX*H7*b z#B^*?P^RP5A>d}BT+G;w+Jx1z1PkA9AKC1>(_d_AF7Yjo6RI^9z(aa@@xZ zsZXnqONA97f6raO>>^Bvrs2krWcw&fg2-hׯ&e`1uR`CITr)`P@fAuwAWzKs- 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 + + +About CTU CAN FD IP Core +------------------------ + +`CTU CAN FD `_ +is an open source soft core written in VHDL. +It originated in 2015 as Ondrej Ille's project +at the `Department of Measurement `_ +of `FEE `_ at `CTU `_. + +The SocketCAN driver for Xilinx Zynq SoC based MicroZed board +`integration `_ +and Intel Cyclone V 5CSEMA4U23C6 based DE0-Nano-SoC Terasic board +`integration `_ +has been developed as well as support for +`PCIe integration `_ of the core. + +In the case of Zynq, the core is connected via the APB system bus, which does +not have enumeration support, and the device must be specified in Device Tree. +This kind of devices is called platform device in the kernel and is +handled by a platform device driver. + +The basic functional model of the CTU CAN FD peripheral has been +accepted into QEMU mainline. See QEMU `CAN emulation support `_ +for CAN FD buses, host connection and CTU CAN FD core emulation. The development +version of emulation support can be cloned from ctu-canfd branch of QEMU local +development `repository `_. + + +About SocketCAN +--------------- + +SocketCAN is a standard common interface for CAN devices in the Linux +kernel. As the name suggests, the bus is accessed via sockets, similarly +to common network devices. The reasoning behind this is in depth +described in `Linux SocketCAN `_. +In short, it offers a +natural way to implement and work with higher layer protocols over CAN, +in the same way as, e.g., UDP/IP over Ethernet. + +Device probe +~~~~~~~~~~~~ + +Before going into detail about the structure of a CAN bus device driver, +let's reiterate how the kernel gets to know about the device at all. +Some buses, like PCI or PCIe, support device enumeration. That is, when +the system boots, it discovers all the devices on the bus and reads +their configuration. The kernel identifies the device via its vendor ID +and device ID, and if there is a driver registered for this identifier +combination, its probe method is invoked to populate the driver's +instance for the given hardware. A similar situation goes with USB, only +it allows for device hot-plug. + +The situation is different for peripherals which are directly embedded +in the SoC and connected to an internal system bus (AXI, APB, Avalon, +and others). These buses do not support enumeration, and thus the kernel +has to learn about the devices from elsewhere. This is exactly what the +Device Tree was made for. + +Device tree +~~~~~~~~~~~ + +An entry in device tree states that a device exists in the system, how +it is reachable (on which bus it resides) and its configuration – +registers address, interrupts and so on. An example of such a device +tree is given in . + +.. code:: raw + + / { + /* ... */ + amba: amba { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + + CTU_CAN_FD_0: CTU_CAN_FD@43c30000 { + compatible = "ctu,ctucanfd"; + interrupt-parent = <&intc>; + interrupts = <0 30 4>; + clocks = <&clkc 15>; + reg = <0x43c30000 0x10000>; + }; + }; + }; + + +.. _sec:socketcan:drv: + +Driver structure +~~~~~~~~~~~~~~~~ + +The driver can be divided into two parts – platform-dependent device +discovery and set up, and platform-independent CAN network device +implementation. + +.. _sec:socketcan:platdev: + +Platform device driver +^^^^^^^^^^^^^^^^^^^^^^ + +In the case of Zynq, the core is connected via the AXI system bus, which +does not have enumeration support, and the device must be specified in +Device Tree. This kind of devices is called *platform device* in the +kernel and is handled by a *platform device driver*\ [1]_. + +A platform device driver provides the following things: + +- A *probe* function + +- A *remove* function + +- A table of *compatible* devices that the driver can handle + +The *probe* function is called exactly once when the device appears (or +the driver is loaded, whichever happens later). If there are more +devices handled by the same driver, the *probe* function is called for +each one of them. Its role is to allocate and initialize resources +required for handling the device, as well as set up low-level functions +for the platform-independent layer, e.g., *read_reg* and *write_reg*. +After that, the driver registers the device to a higher layer, in our +case as a *network device*. + +The *remove* function is called when the device disappears, or the +driver is about to be unloaded. It serves to free the resources +allocated in *probe* and to unregister the device from higher layers. + +Finally, the table of *compatible* devices states which devices the +driver can handle. The Device Tree entry ``compatible`` is matched +against the tables of all *platform drivers*. + +.. code:: c + + /* Match table for OF platform binding */ + static const struct of_device_id ctucan_of_match[] = { + { .compatible = "ctu,canfd-2", }, + { .compatible = "ctu,ctucanfd", }, + { /* end of list */ }, + }; + MODULE_DEVICE_TABLE(of, ctucan_of_match); + + static int ctucan_probe(struct platform_device *pdev); + static int ctucan_remove(struct platform_device *pdev); + + static struct platform_driver ctucanfd_driver = { + .probe = ctucan_probe, + .remove = ctucan_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = ctucan_of_match, + }, + }; + module_platform_driver(ctucanfd_driver); + + +.. _sec:socketcan:netdev: + +Network device driver +^^^^^^^^^^^^^^^^^^^^^ + +Each network device must support at least these operations: + +- Bring the device up: ``ndo_open`` + +- Bring the device down: ``ndo_close`` + +- Submit TX frames to the device: ``ndo_start_xmit`` + +- Signal TX completion and errors to the network subsystem: ISR + +- Submit RX frames to the network subsystem: ISR and NAPI + +There are two possible event sources: the device and the network +subsystem. Device events are usually signaled via an interrupt, handled +in an Interrupt Service Routine (ISR). Handlers for the events +originating in the network subsystem are then specified in +``struct net_device_ops``. + +When the device is brought up, e.g., by calling ``ip link set can0 up``, +the driver’s function ``ndo_open`` is called. It should validate the +interface configuration and configure and enable the device. The +analogous opposite is ``ndo_close``, called when the device is being +brought down, be it explicitly or implicitly. + +When the system should transmit a frame, it does so by calling +``ndo_start_xmit``, which enqueues the frame into the device. If the +device HW queue (FIFO, mailboxes or whatever the implementation is) +becomes full, the ``ndo_start_xmit`` implementation informs the network +subsystem that it should stop the TX queue (via ``netif_stop_queue``). +It is then re-enabled later in ISR when the device has some space +available again and is able to enqueue another frame. + +All the device events are handled in ISR, namely: + +#. **TX completion**. When the device successfully finishes transmitting + a frame, the frame is echoed locally. On error, an informative error + frame [2]_ is sent to the network subsystem instead. In both cases, + the software TX queue is resumed so that more frames may be sent. + +#. **Error condition**. If something goes wrong (e.g., the device goes + bus-off or RX overrun happens), error counters are updated, and + informative error frames are enqueued to SW RX queue. + +#. **RX buffer not empty**. In this case, read the RX frames and enqueue + them to SW RX queue. Usually NAPI is used as a middle layer (see ). + +.. _sec:socketcan:napi: + +NAPI +~~~~ + +The frequency of incoming frames can be high and the overhead to invoke +the interrupt service routine for each frame can cause significant +system load. There are multiple mechanisms in the Linux kernel to deal +with this situation. They evolved over the years of Linux kernel +development and enhancements. For network devices, the current standard +is NAPI – *the New API*. It is similar to classical top-half/bottom-half +interrupt handling in that it only acknowledges the interrupt in the ISR +and signals that the rest of the processing should be done in softirq +context. On top of that, it offers the possibility to *poll* for new +frames for a while. This has a potential to avoid the costly round of +enabling interrupts, handling an incoming IRQ in ISR, re-enabling the +softirq and switching context back to softirq. + +More detailed documentation of NAPI may be found on the pages of Linux +Foundation ``_. + +Integrating the core to Xilinx Zynq +----------------------------------- + +The core interfaces a simple subset of the Avalon +`Avalon Interface Specifications `_ +bus as it was originally used on +Alterra FPGA chips, yet Xilinx natively interfaces with AXI +`AMBA AXI and ACE Protocol Specification AXI3, AXI4, and AXI4-Lite, ACE and ACE-Lite `_. +The most obvious solution would be to use +an Avalon/AXI bridge or implement some simple conversion entity. +However, the core’s interface is half-duplex with no handshake +signaling, whereas AXI is full duplex with two-way signaling. Moreover, +even AXI-Lite slave interface is quite resource-intensive, and the +flexibility and speed of AXI are not required for a CAN core. + +Thus a much simpler bus was chosen – APB (Advanced Peripheral Bus) +`AMBA APB Protocol Specification v2.0 `_. +APB-AXI bridge is directly available in +Xilinx Vivado, and the interface adaptor entity is just a few simple +combinatorial assignments. + +Finally, to be able to include the core in a block diagram as a custom +IP, the core, together with the APB interface, has been packaged as a +Vivado component. + +CTU CAN FD Driver design +------------------------ + +The general structure of a CAN device driver has already been examined +in . The next paragraphs provide a more detailed description of the CTU +CAN FD core driver in particular. + +Low-level driver +~~~~~~~~~~~~~~~~ + +The core is not intended to be used solely with SocketCAN, and thus it +is desirable to have an OS-independent low-level driver. This low-level +driver can then be used in implementations of OS driver or directly +either on bare metal or in a user-space application. Another advantage +is that if the hardware slightly changes, only the low-level driver +needs to be modified. + +The code [3]_ is in part automatically generated and in part written +manually by the core author, with contributions of the thesis’ author. +The low-level driver supports operations such as: set bit timing, set +controller mode, enable/disable, read RX frame, write TX frame, and so +on. + +Configuring bit timing +~~~~~~~~~~~~~~~~~~~~~~ + +On CAN, each bit is divided into four segments: SYNC, PROP, PHASE1, and +PHASE2. Their duration is expressed in multiples of a Time Quantum +(details in `CAN Specification, Version 2.0 `_, chapter 8). +When configuring +bitrate, the durations of all the segments (and time quantum) must be +computed from the bitrate and Sample Point. This is performed +independently for both the Nominal bitrate and Data bitrate for CAN FD. + +SocketCAN is fairly flexible and offers either highly customized +configuration by setting all the segment durations manually, or a +convenient configuration by setting just the bitrate and sample point +(and even that is chosen automatically per Bosch recommendation if not +specified). However, each CAN controller may have different base clock +frequency and different width of segment duration registers. The +algorithm thus needs the minimum and maximum values for the durations +(and clock prescaler) and tries to optimize the numbers to fit both the +constraints and the requested parameters. + +.. code:: c + + struct can_bittiming_const { + char name[16]; /* Name of the CAN controller hardware */ + __u32 tseg1_min; /* Time segment 1 = prop_seg + phase_seg1 */ + __u32 tseg1_max; + __u32 tseg2_min; /* Time segment 2 = phase_seg2 */ + __u32 tseg2_max; + __u32 sjw_max; /* Synchronisation jump width */ + __u32 brp_min; /* Bit-rate prescaler */ + __u32 brp_max; + __u32 brp_inc; + }; + + +[lst:can_bittiming_const] + +A curious reader will notice that the durations of the segments PROP_SEG +and PHASE_SEG1 are not determined separately but rather combined and +then, by default, the resulting TSEG1 is evenly divided between PROP_SEG +and PHASE_SEG1. In practice, this has virtually no consequences as the +sample point is between PHASE_SEG1 and PHASE_SEG2. In CTU CAN FD, +however, the duration registers ``PROP`` and ``PH1`` have different +widths (6 and 7 bits, respectively), so the auto-computed values might +overflow the shorter register and must thus be redistributed among the +two [4]_. + +Handling RX +~~~~~~~~~~~ + +Frame reception is handled in NAPI queue, which is enabled from ISR when +the RXNE (RX FIFO Not Empty) bit is set. Frames are read one by one +until either no frame is left in the RX FIFO or the maximum work quota +has been reached for the NAPI poll run (see ). Each frame is then passed +to the network interface RX queue. + +An incoming frame may be either a CAN 2.0 frame or a CAN FD frame. The +way to distinguish between these two in the kernel is to allocate either +``struct can_frame`` or ``struct canfd_frame``, the two having different +sizes. In the controller, the information about the frame type is stored +in the first word of RX FIFO. + +This brings us a chicken-egg problem: we want to allocate the ``skb`` +for the frame, and only if it succeeds, fetch the frame from FIFO; +otherwise keep it there for later. But to be able to allocate the +correct ``skb``, we have to fetch the first work of FIFO. There are +several possible solutions: + +#. Read the word, then allocate. If it fails, discard the rest of the + frame. When the system is low on memory, the situation is bad anyway. + +#. Always allocate ``skb`` big enough for an FD frame beforehand. Then + tweak the ``skb`` internals to look like it has been allocated for + the smaller CAN 2.0 frame. + +#. Add option to peek into the FIFO instead of consuming the word. + +#. If the allocation fails, store the read word into driver’s data. On + the next try, use the stored word instead of reading it again. + +Option 1 is simple enough, but not very satisfying if we could do +better. Option 2 is not acceptable, as it would require modifying the +private state of an integral kernel structure. The slightly higher +memory consumption is just a virtual cherry on top of the “cake”. Option +3 requires non-trivial HW changes and is not ideal from the HW point of +view. + +Option 4 seems like a good compromise, with its disadvantage being that +a partial frame may stay in the FIFO for a prolonged time. Nonetheless, +there may be just one owner of the RX FIFO, and thus no one else should +see the partial frame (disregarding some exotic debugging scenarios). +Basides, the driver resets the core on its initialization, so the +partial frame cannot be “adopted” either. In the end, option 4 was +selected [5]_. + +.. _subsec:ctucanfd:rxtimestamp: + +Timestamping RX frames +^^^^^^^^^^^^^^^^^^^^^^ + +The CTU CAN FD core reports the exact timestamp when the frame has been +received. The timestamp is by default captured at the sample point of +the last bit of EOF but is configurable to be captured at the SOF bit. +The timestamp source is external to the core and may be up to 64 bits +wide. At the time of writing, passing the timestamp from kernel to +userspace is not yet implemented, but is planned in the future. + +Handling TX +~~~~~~~~~~~ + +The CTU CAN FD core has 4 independent TX buffers, each with its own +state and priority. When the core wants to transmit, a TX buffer in +Ready state with the highest priority is selected. + +The priorities are 3bit numbers in register TX_PRIORITY +(nibble-aligned). This should be flexible enough for most use cases. +SocketCAN, however, supports only one FIFO queue for outgoing +frames [6]_. The buffer priorities may be used to simulate the FIFO +behavior by assigning each buffer a distinct priority and *rotating* the +priorities after a frame transmission is completed. + +In addition to priority rotation, the SW must maintain head and tail +pointers into the FIFO formed by the TX buffers to be able to determine +which buffer should be used for next frame (``txb_head``) and which +should be the first completed one (``txb_tail``). The actual buffer +indices are (obviously) modulo 4 (number of TX buffers), but the +pointers must be at least one bit wider to be able to distinguish +between FIFO full and FIFO empty – in this situation, +:math:`txb\_head \equiv txb\_tail\ (\textrm{mod}\ 4)`. An example of how +the FIFO is maintained, together with priority rotation, is depicted in + +| + ++------+---+---+---+---+ +| TXB# | 0 | 1 | 2 | 3 | ++======+===+===+===+===+ +| Seq | A | B | C | | ++------+---+---+---+---+ +| Prio | 7 | 6 | 5 | 4 | ++------+---+---+---+---+ +| | | T | | H | ++------+---+---+---+---+ + +| + ++------+---+---+---+---+ +| TXB# | 0 | 1 | 2 | 3 | ++======+===+===+===+===+ +| Seq | | B | C | | ++------+---+---+---+---+ +| Prio | 4 | 7 | 6 | 5 | ++------+---+---+---+---+ +| | | T | | H | ++------+---+---+---+---+ + +| + ++------+---+---+---+---+----+ +| TXB# | 0 | 1 | 2 | 3 | 0’ | ++======+===+===+===+===+====+ +| Seq | E | B | C | D | | ++------+---+---+---+---+----+ +| Prio | 4 | 7 | 6 | 5 | | ++------+---+---+---+---+----+ +| | | T | | | H | ++------+---+---+---+---+----+ + +| + +.. figure:: FSM_TXT_Buffer_user.png + + TX Buffer states with possible transitions + +.. _subsec:ctucanfd:txtimestamp: + +Timestamping TX frames +^^^^^^^^^^^^^^^^^^^^^^ + +When submitting a frame to a TX buffer, one may specify the timestamp at +which the frame should be transmitted. The frame transmission may start +later, but not sooner. Note that the timestamp does not participate in +buffer prioritization – that is decided solely by the mechanism +described above. + +Support for time-based packet transmission was recently merged to Linux +v4.19 `Time-based packet transmission `_, +but it remains yet to be researched +whether this functionality will be practical for CAN. + +Also similarly to retrieving the timestamp of RX frames, the core +supports retrieving the timestamp of TX frames – that is the time when +the frame was successfully delivered. The particulars are very similar +to timestamping RX frames and are described in . + +Handling RX buffer overrun +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When a received frame does no more fit into the hardware RX FIFO in its +entirety, RX FIFO overrun flag (STATUS[DOR]) is set and Data Overrun +Interrupt (DOI) is triggered. When servicing the interrupt, care must be +taken first to clear the DOR flag (via COMMAND[CDO]) and after that +clear the DOI interrupt flag. Otherwise, the interrupt would be +immediately [7]_ rearmed. + +**Note**: During development, it was discussed whether the internal HW +pipelining cannot disrupt this clear sequence and whether an additional +dummy cycle is necessary between clearing the flag and the interrupt. On +the Avalon interface, it indeed proved to be the case, but APB being +safe because it uses 2-cycle transactions. Essentially, the DOR flag +would be cleared, but DOI register’s Preset input would still be high +the cycle when the DOI clear request would also be applied (by setting +the register’s Reset input high). As Set had higher priority than Reset, +the DOI flag would not be reset. This has been already fixed by swapping +the Set/Reset priority (see issue #187). + +Reporting Error Passive and Bus Off conditions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It may be desirable to report when the node reaches *Error Passive*, +*Error Warning*, and *Bus Off* conditions. The driver is notified about +error state change by an interrupt (EPI, EWLI), and then proceeds to +determine the core’s error state by reading its error counters. + +There is, however, a slight race condition here – there is a delay +between the time when the state transition occurs (and the interrupt is +triggered) and when the error counters are read. When EPI is received, +the node may be either *Error Passive* or *Bus Off*. If the node goes +*Bus Off*, it obviously remains in the state until it is reset. +Otherwise, the node is *or was* *Error Passive*. However, it may happen +that the read state is *Error Warning* or even *Error Active*. It may be +unclear whether and what exactly to report in that case, but I +personally entertain the idea that the past error condition should still +be reported. Similarly, when EWLI is received but the state is later +detected to be *Error Passive*, *Error Passive* should be reported. + + +CTU CAN FD Driver Sources Reference +----------------------------------- + +.. kernel-doc:: drivers/net/can/ctucanfd/ctu_can_fd_hw.h + :internal: + +.. kernel-doc:: drivers/net/can/ctucanfd/ctu_can_fd.c + :internal: + +.. kernel-doc:: drivers/net/can/ctucanfd/ctu_can_fd_pci.c + :internal: + +.. kernel-doc:: drivers/net/can/ctucanfd/ctu_can_fd_platform.c + :internal: + +CTU CAN FD IP Core and Driver Development Acknowledgment +--------------------------------------------------------- + +* Odrej Ille + + * started the project as student at Department of Measurement, FEE, CTU + * invested great amount of personal time and enthusiasm to the project over years + * worked on more funded tasks + +* `Department of Measurement `_, + `Faculty of Electrical Engineering `_, + `Czech Technical University `_ + + * is the main investor into the project over many years + * uses project in their CAN/CAN FD diagnostics framework for `Skoda Auto `_ + +* `Digiteq Automotive `_ + + * funding of the project CAN FD Open Cores Support Linux Kernel Based Systems + * negotiated and paid CTU to allow public access to the project + * provided additional funding of the work + +* `Department of Control Engineering `_, + `Faculty of Electrical Engineering `_, + `Czech Technical University `_ + + * solving the project CAN FD Open Cores Support Linux Kernel Based Systems + * providing GitLab management + * virtual servers and computational power for continuous integration + * providing hardware for HIL continuous integration tests + +* `PiKRON Ltd. `_ + + * minor funding to initiate preparation of the project open-sourcing + +* Petr Porazil + + * design of PCIe transceiver addon board and assembly of boards + * design and assembly of MZ_APO baseboard for MicroZed/Zynq based system + +* Martin Jerabek + + * Linux driver development + * continuous integration platform architect and GHDL updates + * theses `Open-source and Open-hardware CAN FD Protocol Support `_ + +* Jiri Novak + + * project initiation, management and use at Department of Measurement, FEE, CTU + +* Pavel Pisa + + * initiate open-sourcing, project coordination, management at Department of Control Engineering, FEE, CTU + +* Jaroslav Beran + + * system integration for Intel SoC, core and driver testing and updates + +* Carsten Emde (`OSADL `_) + + * provided OSADL expertise to discuss IP core licensing + * pointed to possible deadlock for LGPL and CAN bus possible patent case which lead to relicense IP core design to BSD like license + +* Reiner Zitzmann and Holger Zeltwanger (`CAN in Automation `_) + + * provided suggestions and help to inform community about the project and invited us to events focused on CAN bus future development directions + +* Jan Charvat + + * implemented CTU CAN FD functional model for QEMU which has been integrated into QEMU mainline (`docs/can.txt `_) + * Bachelor theses Model of CAN FD Communication Controller for QEMU Emulator + +Notes +----- + + +.. [1] + Other buses have their own specific driver interface to set up the + device. + +.. [2] + Not to be mistaken with CAN Error Frame. This is a ``can_frame`` with + ``CAN_ERR_FLAG`` set and some error info in its ``data`` field. + +.. [3] + Available in in CTU CAN FD repository + ``_ + +.. [4] + As is done in the low-level driver functions + ``ctu_can_fd_set_nom_bittiming`` and + ``ctu_can_fd_set_data_bittiming``. + +.. [5] + At the time of writing this thesis, option 1 is still being used and + the modification is queued in gitlab issue #222 + +.. [6] + Strictly speaking, multiple CAN TX queues are supported since v4.19 + `can: enable multi-queue for SocketCAN devices `_ but no mainline driver is using + them yet. + +.. [7] + Or rather in the next clock cycle