From 5582d48beaf72896e73ce53fe44c2f719907d583 Mon Sep 17 00:00:00 2001 From: p-szafonimateusz Date: Thu, 24 Oct 2024 13:25:47 +0200 Subject: [PATCH 1/3] drivers/can: add Kvaser PCI card driver (qemu only) add Kvaser PCI card driver support, works only with QEMU now: https://www.qemu.org/docs/master/system/devices/can.html#examples-how-to-use-can-emulation-for-sja1000-based-boards Signed-off-by: p-szafonimateusz --- drivers/can/CMakeLists.txt | 14 +- drivers/can/Kconfig | 37 +- drivers/can/Make.defs | 7 +- drivers/can/kvaser_pci.c | 1814 ++++++++++++++++++++++++++++++++ drivers/can/sja1000.h | 16 + drivers/pci/pci_drivers.c | 11 + include/nuttx/can/kvaser_pci.h | 59 ++ 7 files changed, 1950 insertions(+), 8 deletions(-) create mode 100644 drivers/can/kvaser_pci.c create mode 100644 include/nuttx/can/kvaser_pci.h diff --git a/drivers/can/CMakeLists.txt b/drivers/can/CMakeLists.txt index 2b60bc044b563..b8d33cef3b093 100644 --- a/drivers/can/CMakeLists.txt +++ b/drivers/can/CMakeLists.txt @@ -20,12 +20,18 @@ # # ############################################################################## +set(SRCS) + if(CONFIG_CAN) set(SRCS can.c can_sender.c) +endif() - if(CONFIG_CAN_MCP2515) - list(APPEND SRCS mcp2515.c) - endif() +if(CONFIG_CAN_MCP2515) + list(APPEND SRCS mcp2515.c) +endif() - target_sources(drivers PRIVATE ${SRCS}) +if(CONFIG_CAN_KVASER) + list(APPEND SRCS kvaser_pci.c) endif() + +target_sources(drivers PRIVATE ${SRCS}) diff --git a/drivers/can/Kconfig b/drivers/can/Kconfig index 9e321b8218c53..45d658ad16bdc 100644 --- a/drivers/can/Kconfig +++ b/drivers/can/Kconfig @@ -7,8 +7,10 @@ config ARCH_HAVE_CAN_ERRORS bool default n -menuconfig CAN - bool "CAN Driver Support" +menu "CAN Driver Support" + +config CAN + bool "CAN Character Driver Support" default n ---help--- This selection enables building of the "upper-half" CAN driver. @@ -267,3 +269,34 @@ config CAN_SJA1000_DEBUG endif # CAN_SJA1000 endif # CAN + +config CAN_KVASER + bool "Kvaser PCI CAN card" + default n + depends on PCI + ---help--- + Enable driver support for Kvase PCI CAN card. + NOTE: for now works only with QEMU + +if CAN_KVASER + +choice + prompt "Kvaser PCI CAN device type" + default CAN_KVASER_CHARDEV if CAN + default CAN_KVASER_SOCKET if NET_CAN + +config CAN_KVASER_CHARDEV + bool "Kvaser PCI can device as chardev" + depends on CAN + select ARCH_HAVE_CAN_ERRORS + +config CAN_KVASER_SOCKET + bool "Kvaser PCI can device as socketCAN" + depends on NET_CAN + select NET_CAN_HAVE_ERRORS + +endchoice # "Kvaser PCI CAN device type" + +endif # CAN_KVASER + +endmenu # CAN Driver Support diff --git a/drivers/can/Make.defs b/drivers/can/Make.defs index d093a199557c3..741b57017260c 100644 --- a/drivers/can/Make.defs +++ b/drivers/can/Make.defs @@ -23,8 +23,8 @@ # Don't build anything if there is no CAN support ifeq ($(CONFIG_CAN),y) - CSRCS += can.c can_sender.c +endif ifeq ($(CONFIG_CAN_MCP2515),y) CSRCS += mcp2515.c @@ -34,9 +34,12 @@ ifeq ($(CONFIG_CAN_SJA1000),y) CSRCS += sja1000.c endif +ifeq ($(CONFIG_CAN_KVASER),y) +CSRCS += kvaser_pci.c +endif + # Include CAN device driver build support DEPPATH += --dep-path can VPATH += :can CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)drivers$(DELIM)can -endif diff --git a/drivers/can/kvaser_pci.c b/drivers/can/kvaser_pci.c new file mode 100644 index 0000000000000..bf19a79177a41 --- /dev/null +++ b/drivers/can/kvaser_pci.c @@ -0,0 +1,1814 @@ +/***************************************************************************** + * drivers/can/kvaser_pci.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + *****************************************************************************/ + +/***************************************************************************** + * Included Files + *****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#ifdef CONFIG_CAN_KVASER_SOCKET +# include +# include +# include +#endif + +#include "sja1000.h" + +/***************************************************************************** + * Pre-processor Definitions + *****************************************************************************/ + +/* Kvaser card constants */ + +#define KVASER_SJA_MAX 4 +#define KVASER_XTAL_FREQ 16000000 + +/* PCI BARs */ + +#define KVASER_S5920_BAR 0 +#define KVASER_SJA_BAR 1 +#define KVASER_XILINX_BAR 2 + +/* Kvaser registers */ + +#define KVASER_SJA_REGS 0x20 + +#define KVASER_S5920_INTCSR 0x38 +#define KVASER_S5920_INTCSR_INT_ASSERT (1 << 23) /* Interrupt asserted bit */ +#define KVASER_S5920_INTCSR_INT_ADDON (1 << 13) /* Add-on interrupt pin enable */ + +/* Interrupts */ + +#define KVASER_INT_ERR (SJA1000_OVERRUN_INT_ENA | \ + SJA1000_ERR_PASSIVE_INT_ENA | \ + SJA1000_ARB_LOST_INT_ENA | \ + SJA1000_BUS_ERR_INT_ENA) + +/* SocketCAN specific */ + +#ifdef CONFIG_CAN_KVASER_SOCKET + +# define KVASER_POOL_SIZE 1 + +/* Work queue support is required. */ + +# if !defined(CONFIG_SCHED_WORKQUEUE) +# error Work queue support is required +# endif + +# define KVASER_CANWORK LPWORK + +#endif /* CONFIG_CAN_KVASER_SOCKET */ + +/***************************************************************************** + * Private Types + *****************************************************************************/ + +/* SJA1000 channel private data */ + +struct kvaser_sja_s +{ +#ifdef CONFIG_CAN_KVASER_CHARDEV + struct can_dev_s dev; +#endif + +#ifdef CONFIG_CAN_KVASER_SOCKET + struct net_driver_s dev; /* Interface understood by the network */ + struct work_s pollwork; /* For deferring poll work to the work wq */ + + /* TX/RX pool */ + + struct can_frame tx_pool[KVASER_POOL_SIZE]; + struct can_frame rx_pool[KVASER_POOL_SIZE]; +#endif + + /* PCI data */ + + FAR struct pci_device_s *pcidev; + uint64_t base; +}; + +/* KVASER private data */ + +struct kvaser_driver_s +{ + FAR struct kvaser_sja_s *sja; + uint8_t count; + + /* PCI data */ + + FAR struct pci_device_s *pcidev; + int irq; + uintptr_t sja_base; + uintptr_t s5920_base; + uintptr_t xilinx_base; + +#ifdef CONFIG_CAN_KVASER_SOCKET + /* For deferring interrupt work to the wq */ + + struct work_s irqwork; +#endif +}; + +/***************************************************************************** + * Private Functions Definitions + *****************************************************************************/ + +/* Helpers */ + +static uint8_t kvaser_getreg_sja(FAR struct kvaser_sja_s *priv, + unsigned int offset); +static void kvaser_putreg_sja(FAR struct kvaser_sja_s *priv, + unsigned int offset, + uint8_t value); + +static uint32_t kvaser_getreg_s5920(FAR struct kvaser_driver_s *priv, + unsigned int offset); +static void kvaser_putreg_s5920(FAR struct kvaser_driver_s *priv, + unsigned int offset, + uint32_t value); + +/* Common methods */ + +static void kvaser_reset(FAR struct kvaser_sja_s *priv); +static void kvaser_sleep(FAR struct kvaser_sja_s *priv); +static void kvaser_setup(FAR struct kvaser_sja_s *priv); +static void kvaser_rxint(FAR struct kvaser_sja_s *priv, bool enable); +static void kvaser_txint(FAR struct kvaser_sja_s *priv, bool enable); +static bool kvaser_txready(FAR struct kvaser_sja_s *priv); + +#ifdef CONFIG_CAN_KVASER_CHARDEV +/* CAN character device methods */ + +static void kvaser_chrdev_reset(FAR struct can_dev_s *dev); +static int kvaser_chrdev_setup(FAR struct can_dev_s *dev); +static void kvaser_chrdev_shutdown(FAR struct can_dev_s *dev); +static void kvaser_chrdev_rxint(FAR struct can_dev_s *dev, bool enable); +static void kvaser_chrdev_txint(FAR struct can_dev_s *dev, bool enable); +static int kvaser_chrdev_ioctl(FAR struct can_dev_s *dev, int cmd, + unsigned long arg); +static int kvaser_chrdev_remoterequest(FAR struct can_dev_s *dev, + uint16_t id); +static int kvaser_chrdev_send(FAR struct can_dev_s *dev, + FAR struct can_msg_s *msg); +static bool kvaser_chrdev_txready(FAR struct can_dev_s *dev); +static bool kvaser_chrdev_txempty(FAR struct can_dev_s *dev); +static void kvaser_chardev_receive(FAR struct kvaser_sja_s *priv); +static void kvaser_chardev_interrupt(FAR struct kvaser_driver_s *priv); +#endif + +#ifdef CONFIG_CAN_KVASER_SOCKET +/* SocketCAN methods */ + +static int kvaser_sock_ifup(FAR struct net_driver_s *dev); +static int kvaser_sock_ifdown(FAR struct net_driver_s *dev); + +static void kvaser_sock_txavail_work(FAR void *arg); +static int kvaser_sock_txavail(FAR struct net_driver_s *dev); + +# ifdef CONFIG_NETDEV_IOCTL +static int kvaser_sock_ioctl(FAR struct net_driver_s *dev, int cmd, + unsigned long arg); +# endif + +static int kvaser_sock_txpoll(FAR struct net_driver_s *dev); +static int kvaser_sock_send(FAR struct kvaser_sja_s *priv); +static void kvaser_sock_receive(FAR struct kvaser_sja_s *priv); +static void kvaser_sock_interrupt_work(FAR void *arg); +#endif + +/* Interrupts */ + +static int kvaser_interrupt(int irq, FAR void *context, FAR void *arg); + +/* PCI */ + +static void kvaser_init(FAR struct kvaser_driver_s *priv); +static uint8_t kvaser_count_sja(FAR struct kvaser_driver_s *priv); +static int kvaser_probe(FAR struct pci_device_s *dev); + +/***************************************************************************** + * Private Data + *****************************************************************************/ + +#ifdef CONFIG_CAN_KVASER_CHARDEV +static const struct can_ops_s g_kvaser_can_ops = +{ + .co_reset = kvaser_chrdev_reset, + .co_setup = kvaser_chrdev_setup, + .co_shutdown = kvaser_chrdev_shutdown, + .co_rxint = kvaser_chrdev_rxint, + .co_txint = kvaser_chrdev_txint, + .co_ioctl = kvaser_chrdev_ioctl, + .co_remoterequest = kvaser_chrdev_remoterequest, + .co_send = kvaser_chrdev_send, + .co_txready = kvaser_chrdev_txready, + .co_txempty = kvaser_chrdev_txempty, +}; +#endif + +static const struct pci_device_id_s g_kvaser_id_table[] = +{ + { + PCI_DEVICE(0x10e8, 0x8406), + .driver_data = 0 + }, + { } +}; + +static struct pci_driver_s g_kvaser_drv = +{ + .id_table = g_kvaser_id_table, + .probe = kvaser_probe, +}; + +#ifdef CONFIG_CAN_KVASER_CHARDEV +static uint8_t g_kvaser_count = 0; +#endif + +/***************************************************************************** + * Private Functions + *****************************************************************************/ + +/***************************************************************************** + * Name: kvaser_getreg_sja + *****************************************************************************/ + +static uint8_t kvaser_getreg_sja(FAR struct kvaser_sja_s *priv, + unsigned int offset) +{ + uintptr_t addr = priv->base + offset; + uint8_t ret = 0; + + pci_read_io_byte(priv->pcidev, addr, &ret); + return ret; +} + +/***************************************************************************** + * Name: kvaser_putreg_sja + *****************************************************************************/ + +static void kvaser_putreg_sja(FAR struct kvaser_sja_s *priv, + unsigned int offset, + uint8_t value) +{ + uintptr_t addr = priv->base + offset; + pci_write_io_byte(priv->pcidev, addr, value); +} + +/***************************************************************************** + * Name: kvaser_getreg_s5920 + *****************************************************************************/ + +static uint32_t kvaser_getreg_s5920(FAR struct kvaser_driver_s *priv, + unsigned int offset) +{ + uintptr_t addr = priv->s5920_base + offset; + uint32_t ret = 0; + + pci_read_io_dword(priv->pcidev, addr, &ret); + return ret; +} + +/***************************************************************************** + * Name: kvaser_putreg_s5920 + *****************************************************************************/ + +static void kvaser_putreg_s5920(FAR struct kvaser_driver_s *priv, + unsigned int offset, + uint32_t value) +{ + uintptr_t addr = priv->s5920_base + offset; + pci_write_io_dword(priv->pcidev, addr, value); +} + +/***************************************************************************** + * Name: kvaser_reset + *****************************************************************************/ + +static void kvaser_reset(FAR struct kvaser_sja_s *priv) +{ + uint8_t regval = 0; + + /* Reset mode */ + + kvaser_putreg_sja(priv, SJA1000_MODE_REG, SJA1000_RESET_MODE); + + /* Enter PeliCAN mode */ + + regval = kvaser_getreg_sja(priv, SJA1000_CLOCK_DIVIDER_REG); + regval |= SJA1000_EXT_MODE; + kvaser_putreg_sja(priv, SJA1000_CLOCK_DIVIDER_REG, regval); + + /* Clear counters */ + + kvaser_putreg_sja(priv, SJA1000_RX_ERR_CNT_REG, 0); + kvaser_putreg_sja(priv, SJA1000_TX_ERR_CNT_REG, 0); + kvaser_putreg_sja(priv, SJA1000_RX_MESSAGE_CNT_REG, 0); + + /* Accept all */ + + kvaser_putreg_sja(priv, SJA1000_DATA_0_REG, 0); + kvaser_putreg_sja(priv, SJA1000_DATA_1_REG, 0); + kvaser_putreg_sja(priv, SJA1000_DATA_2_REG, 0); + kvaser_putreg_sja(priv, SJA1000_DATA_3_REG, 0); + + kvaser_putreg_sja(priv, SJA1000_DATA_4_REG, 0xff); + kvaser_putreg_sja(priv, SJA1000_DATA_5_REG, 0xff); + kvaser_putreg_sja(priv, SJA1000_DATA_6_REG, 0xff); + kvaser_putreg_sja(priv, SJA1000_DATA_7_REG, 0xff); +} + +/***************************************************************************** + * Name: kvaser_sleep + *****************************************************************************/ + +static void kvaser_sleep(FAR struct kvaser_sja_s *priv) +{ + /* Sleep mode */ + + kvaser_putreg_sja(priv, SJA1000_MODE_REG, SJA1000_SLEEP_MODE); +} + +/***************************************************************************** + * Name: kvaser_setup + *****************************************************************************/ + +static void kvaser_setup(FAR struct kvaser_sja_s *priv) +{ + /* REVISIT: missing bus timings configuration and output control. + * + * This driver was verified on QEMU with virtual host CAN netwrok, + * which doesn't need bus timings and output control registers set. + * For real hardware, these registers must be properly configured ! + */ + + kvaser_putreg_sja(priv, SJA1000_BUS_TIMING_0_REG, 0); + kvaser_putreg_sja(priv, SJA1000_BUS_TIMING_1_REG, 0); + kvaser_putreg_sja(priv, SJA1000_OUTCTRL_REG, 0); + + /* Enable interrupts */ + +#if defined(CONFIG_CAN_ERRORS) || defined(CONFIG_NET_CAN_ERRORS) + kvaser_putreg_sja(priv, SJA1000_INT_ENA_REG, KVASER_INT_ERR); +#else + kvaser_putreg_sja(priv, SJA1000_INT_ENA_REG, 0); +#endif + + /* Exit from reset mode and set `Dual filter mode`. + * + * NOTE: `Single filter mode` is broken in older versions of QEMU: + * https://gitlab.com/qemu-project/qemu/-/issues/2028 + */ + + kvaser_putreg_sja(priv, SJA1000_MODE_REG, 0); +} + +/***************************************************************************** + * Name: kvaser_rxint + *****************************************************************************/ + +static void kvaser_rxint(FAR struct kvaser_sja_s *priv, bool enable) +{ + uint8_t regval = 0; + + regval = kvaser_getreg_sja(priv, SJA1000_INT_ENA_REG); + if (enable) + { + regval |= SJA1000_RX_INT_ST; + } + else + { + regval &= ~SJA1000_RX_INT_ST; + } + + kvaser_putreg_sja(priv, SJA1000_INT_ENA_REG, regval); +} + +/***************************************************************************** + * Name: kvaser_txint + *****************************************************************************/ + +static void kvaser_txint(FAR struct kvaser_sja_s *priv, bool enable) +{ + uint8_t regval = 0; + + regval = kvaser_getreg_sja(priv, SJA1000_INT_ENA_REG); + if (enable) + { + regval |= SJA1000_TX_INT_ST; + } + else + { + regval &= ~SJA1000_TX_INT_ST; + } + + kvaser_putreg_sja(priv, SJA1000_INT_ENA_REG, regval); +} + +/***************************************************************************** + * Name: kvaser_txready + *****************************************************************************/ + +static bool kvaser_txready(FAR struct kvaser_sja_s *priv) +{ + return kvaser_getreg_sja(priv, SJA1000_STATUS_REG) & SJA1000_TX_BUF_ST; +} + +#ifdef CONFIG_CAN_KVASER_CHARDEV +/***************************************************************************** + * Name: kvaser_chrdev_reset + * + * Description: + * Reset the CAN device. Called early to initialize the hardware. This + * function is called, before kvaser_setup() and on error conditions. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + *****************************************************************************/ + +static void kvaser_chrdev_reset(FAR struct can_dev_s *dev) +{ + FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev; + + kvaser_reset(priv); +} + +/***************************************************************************** + * Name: kvaser_chrdev_setup + * + * Description: + * Configure the CAN. This method is called the first time that the CAN + * device is opened. This will occur when the port is first opened. + * This setup includes configuring and attaching CAN interrupts. + * All CAN interrupts are disabled upon return. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + *****************************************************************************/ + +static int kvaser_chrdev_setup(FAR struct can_dev_s *dev) +{ + FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev; + + kvaser_setup(priv); + + return OK; +} + +/***************************************************************************** + * Name: kvaser_chrdev_shutdown + * + * Description: + * Disable the CAN. This method is called when the CAN device is closed. + * This method reverses the operation the setup method. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + *****************************************************************************/ + +static void kvaser_chrdev_shutdown(FAR struct can_dev_s *dev) +{ + FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev; + + kvaser_sleep(priv); +} + +/***************************************************************************** + * Name: kvaser_chrdev_rxint + * + * Description: + * Call to enable or disable RX interrupts. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + *****************************************************************************/ + +static void kvaser_chrdev_rxint(FAR struct can_dev_s *dev, bool enable) +{ + FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev; + + kvaser_rxint(priv, enable); +} + +/***************************************************************************** + * Name: kvaser_chrdev_txint + * + * Description: + * Call to enable or disable TX interrupts. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + *****************************************************************************/ + +static void kvaser_chrdev_txint(FAR struct can_dev_s *dev, bool enable) +{ + FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev; + + kvaser_txint(priv, enable); +} + +/***************************************************************************** + * Name: kvaser_chrdev_ioctl + * + * Description: + * All ioctl calls will be routed through this method + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + *****************************************************************************/ + +static int kvaser_chrdev_ioctl(FAR struct can_dev_s *dev, int cmd, + unsigned long arg) +{ + return -ENOTTY; +} + +/***************************************************************************** + * Name: kvaser_chrdev_remoterequest + * + * Description: + * Send a remote request + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + *****************************************************************************/ + +static int kvaser_chrdev_remoterequest(FAR struct can_dev_s *dev, + uint16_t id) +{ + return -ENOTSUP; +} + +/***************************************************************************** + * Name: kvaser_chrdev_send + * + * Description: + * Send one can message. + * + * One CAN-message consists of a maximum of 10 bytes. A message is + * composed of at least the first 2 bytes (when there are no data bytes). + * + * Byte 0: Bits 0-7: Bits 3-10 of the 11-bit CAN identifier + * Byte 1: Bits 5-7: Bits 0-2 of the 11-bit CAN identifier + * Bit 4: Remote Transmission Request (RTR) + * Bits 0-3: Data Length Code (DLC) + * Bytes 2-10: CAN data + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + *****************************************************************************/ + +static int kvaser_chrdev_send(FAR struct can_dev_s *dev, + FAR struct can_msg_s *msg) +{ + FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev; + uint8_t regval = 0; + uint8_t dreg = 0; + uint8_t fi = 0; + uint8_t bytes = 0; + int i = 0; + + /* Check TX buffer status */ + + if (!kvaser_txready(priv)) + { + canerr("ERROR: TX buffer not available\n"); + return -EBUSY; + } + + /* Get data bytes for this frame */ + + bytes = can_dlc2bytes(msg->cm_hdr.ch_dlc); + +#ifdef CONFIG_CAN_FD + /* Drop CAN FD frames */ + + if (bytes > 8 || msg->cm_hdr.ch_edl || + msg->cm_hdr.ch_brs || msg->cm_hdr.ch_esi) + { + canerr("ERROR: CAN FD frames not supported\n"); + return -ENOTSUP; + } +#endif + + /* Set up the DLC */ + + fi = msg->cm_hdr.ch_dlc; + +#ifdef CONFIG_CAN_USE_RTR + fi |= (msg->cm_hdr.ch_rtr ? SJA1000_FI_RTR : 0); +#endif + +#ifdef CONFIG_CAN_EXTID + if (msg->cm_hdr.ch_extid) + { + fi |= SJA1000_FI_FF; + kvaser_putreg_sja(priv, SJA1000_DATA_0_REG, fi); + + /* Write ID */ + + regval = (msg->cm_hdr.ch_id & 0x1fe00000) >> 21; + kvaser_putreg_sja(priv, SJA1000_DATA_1_REG, regval); + regval = (msg->cm_hdr.ch_id & 0x001fe000) >> 13; + kvaser_putreg_sja(priv, SJA1000_DATA_2_REG, regval); + regval = (msg->cm_hdr.ch_id & 0x00001fe0) >> 5; + kvaser_putreg_sja(priv, SJA1000_DATA_3_REG, regval); + regval = (msg->cm_hdr.ch_id & 0x0000001f) << 3; + kvaser_putreg_sja(priv, SJA1000_DATA_4_REG, regval); + + dreg = SJA1000_DATA_5_REG; + } + else +#endif + { + kvaser_putreg_sja(priv, SJA1000_DATA_0_REG, fi); + + /* Write ID */ + + regval = (msg->cm_hdr.ch_id & 0x07f8) >> 3; + kvaser_putreg_sja(priv, SJA1000_DATA_1_REG, regval); + regval = (msg->cm_hdr.ch_id & 0x0007) << 5; + kvaser_putreg_sja(priv, SJA1000_DATA_2_REG, regval); + + dreg = SJA1000_DATA_3_REG; + } + + /* Set up the data fields */ + + for (i = 0; i < bytes; i++) + { + kvaser_putreg_sja(priv, dreg + i, msg->cm_data[i]); + } + + /* Transmit */ + + regval = SJA1000_TX_REQ; +#ifdef CONFIG_CAN_LOOPBACK + regval |= SJA1000_SELF_RX_REQ; +#endif + + kvaser_putreg_sja(priv, SJA1000_CMD_REG, regval); + + return OK; +} + +/***************************************************************************** + * Name: kvaser_chrdev_txready + * + * Description: + * Return true if the CAN hardware can accept another TX message. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * True if the CAN hardware is ready to accept another TX message. + * + *****************************************************************************/ + +static bool kvaser_chrdev_txready(FAR struct can_dev_s *dev) +{ + FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev; + + return kvaser_txready(priv); +} + +/***************************************************************************** + * Name: kvaser_chrdev_txempty + * + * Description: + * Return true if all message have been sent. If for example, the CAN + * hardware implements FIFOs, then this would mean the transmit FIFO is + * empty. This method is called when the driver needs to make sure that + * all characters are "drained" from the TX hardware before calling + * co_shutdown(). + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * True if there are no pending TX transfers in the CAN hardware. + * + *****************************************************************************/ + +static bool kvaser_chrdev_txempty(FAR struct can_dev_s *dev) +{ + FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev; + + return kvaser_txready(priv); +} + +/***************************************************************************** + * Name: kvaser_chardev_receive + * + * Description: + * Receive CAN frame + * + *****************************************************************************/ + +static void kvaser_chardev_receive(FAR struct kvaser_sja_s *priv) +{ + FAR struct can_dev_s *dev = (FAR struct can_dev_s *)priv; + struct can_hdr_s hdr; + uint8_t data[CAN_MAXDATALEN]; + uint8_t dreg; + uint8_t regval; + uint8_t i; + int ret; + + /* Wait until data in RXFIFO */ + + while (kvaser_getreg_sja(priv, SJA1000_STATUS_REG) & SJA1000_RX_BUF_ST) + { + regval = kvaser_getreg_sja(priv, SJA1000_DATA_0_REG); + + /* Get the DLC */ + + hdr.ch_dlc = (regval & SJA1000_FI_DLC_MASK); + + /* Get RTR bit */ + + hdr.ch_rtr = (regval & SJA1000_FI_RTR ? 1 : 0); + + if (!(regval & SJA1000_FI_FF)) + { +#ifdef CONFIG_CAN_EXTID + hdr.ch_extid = 0; +#endif + + hdr.ch_id = ((kvaser_getreg_sja(priv, SJA1000_DATA_1_REG) << 3) | + (kvaser_getreg_sja(priv, SJA1000_DATA_2_REG) >> 5)); + + dreg = SJA1000_DATA_3_REG; + } + else + { +#ifdef CONFIG_CAN_EXTID + hdr.ch_extid = 1; + + hdr.ch_id = ((kvaser_getreg_sja(priv, SJA1000_DATA_1_REG) << 21) | + (kvaser_getreg_sja(priv, SJA1000_DATA_2_REG) << 13) | + (kvaser_getreg_sja(priv, SJA1000_DATA_3_REG) << 5) | + (kvaser_getreg_sja(priv, SJA1000_DATA_4_REG) >> 3)); + + dreg = SJA1000_DATA_5_REG; +#else + canerr("ERROR: Received message with extended" + " identifier. Dropped\n"); + + /* Relese RX buffer */ + + kvaser_putreg_sja(priv, SJA1000_CMD_REG, SJA1000_RELEASE_BUF); + + continue; +#endif + } + + /* Clear the error indication and unused bits */ + +#ifdef CONFIG_CAN_ERRORS + hdr.ch_error = 0; +#endif + hdr.ch_tcf = 0; + + /* Get data */ + + for (i = 0; i < can_dlc2bytes(hdr.ch_dlc); i++) + { + data[i] = kvaser_getreg_sja(priv, dreg + i); + } + + /* Release RX buffer */ + + kvaser_putreg_sja(priv, SJA1000_CMD_REG, SJA1000_RELEASE_BUF); + + /* Provide the data to the upper half driver */ + + ret = can_receive(dev, &hdr, data); + if (ret < 0) + { + canerr("ERROR: can_receive failed %d\n", ret); + } + } +} + +#ifdef CONFIG_CAN_ERRORS +/***************************************************************************** + * Name: kvaser_chardev_error + *****************************************************************************/ + +static void kvaser_chardev_error(FAR struct kvaser_sja_s *priv, uint8_t st) +{ + struct can_hdr_s hdr; + uint16_t errbits = 0; + uint8_t data[CAN_ERROR_DLC]; + uint8_t regval; + int ret; + + memset(data, 0, sizeof(data)); + + /* Data overrun interrupt */ + + if (st & SJA1000_OVERRUN_INT_ST) + { + data[1] |= CAN_ERROR1_RXOVERFLOW; + errbits |= CAN_ERROR_CONTROLLER; + + /* Clear data overrun */ + + kvaser_putreg_sja(priv, SJA1000_CMD_REG, SJA1000_CLR_OVERRUN); + } + + /* Error passive interrupt */ + + if (st & SJA1000_ERR_PASSIVE_INT_ST) + { + data[1] |= (CAN_ERROR1_RXPASSIVE | CAN_ERROR1_TXPASSIVE); + errbits |= CAN_ERROR_CONTROLLER; + } + + /* Arbitration lost interrupt */ + + if (st & SJA1000_ARB_LOST_INT_ST) + { + errbits |= CAN_ERROR_LOSTARB; + regval = (kvaser_getreg_sja(priv, SJA1000_ARB_LOST_CAP_REG) & + SJA1000_ARB_LOST_CAP_M); + data[0] = CAN_ERROR0_BIT(regval); + } + + /* BUS error interrupt */ + + if (st & SJA1000_BUS_ERR_INT_ST) + { + errbits |= CAN_ERROR_BUSERROR; + } + + /* Report a CAN error */ + + if (errbits != 0) + { + canerr("ERROR: errbits = %08" PRIx16 "\n", errbits); + + /* Format the CAN header for the error report. */ + + hdr.ch_id = errbits; + hdr.ch_dlc = CAN_ERROR_DLC; + hdr.ch_rtr = 0; + hdr.ch_error = 1; +#ifdef CONFIG_CAN_EXTID + hdr.ch_extid = 0; +#endif + hdr.ch_tcf = 0; + + /* And provide the error report to the upper half logic */ + + ret = can_receive(&priv->dev, &hdr, data); + if (ret < 0) + { + canerr("ERROR: can_receive failed: %d\n", ret); + } + } +} +#endif + +/***************************************************************************** + * Name: kvaser_chardev_interrupt + *****************************************************************************/ + +static void kvaser_chardev_interrupt(FAR struct kvaser_driver_s *priv) +{ + uint8_t st = 0; + int i = 0; + + for (i = 0; i < priv->count; i++) + { + st = kvaser_getreg_sja(&priv->sja[i], SJA1000_INT_RAW_REG); + if (st == 0) + { + continue; + } + + /* Receive interrupt */ + + if (st & SJA1000_RX_INT_ST) + { + kvaser_chardev_receive(&priv->sja[i]); + } + + /* Transmit interrupt */ + + if (st & SJA1000_TX_INT_ST) + { + /* Tell the upper half that the transfer is finished. */ + + can_txdone(&priv->sja[i].dev); + } + +#ifdef CONFIG_CAN_ERRORS + /* Handle errors */ + + kvaser_chardev_error(&priv->sja[i], st); +#endif + } +} +#endif /* CONFIG_CAN_KVASER_CHARDEV */ + +#ifdef CONFIG_CAN_KVASER_SOCKET +/***************************************************************************** + * Name: kvaser_sock_ifup + * + * Description: + * NuttX Callback: Bring up the Ethernet interface when an IP address is + * provided + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + *****************************************************************************/ + +static int kvaser_sock_ifup(FAR struct net_driver_s *dev) +{ + FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev->d_private; + + priv->dev.d_buf = (FAR uint8_t *)priv->tx_pool; + + kvaser_reset(priv); + kvaser_setup(priv); + + kvaser_txint(priv, true); + kvaser_rxint(priv, true); + + return OK; +} + +/***************************************************************************** + * Name: kvaser_sock_ifdown + * + * Description: + * NuttX Callback: Stop the interface. + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + *****************************************************************************/ + +static int kvaser_sock_ifdown(FAR struct net_driver_s *dev) +{ + FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev->d_private; + + kvaser_txint(priv, false); + kvaser_rxint(priv, false); + + /* Sleep mode */ + + kvaser_sleep(priv); + + return OK; +} + +/***************************************************************************** + * Name: kvaser_sock_txavail_work + * + * Description: + * Perform an out-of-cycle poll on the worker thread. + * + * Input Parameters: + * arg - Reference to the NuttX driver state structure (cast to void*) + * + * Returned Value: + * None + * + * Assumptions: + * Called on the higher priority worker thread. + * + *****************************************************************************/ + +static void kvaser_sock_txavail_work(FAR void *arg) +{ + FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)arg; + + /* Ignore the notification if the interface is not yet up */ + + net_lock(); + if (IFF_IS_UP(priv->dev.d_flags)) + { + /* Check if there is room in the hardware to hold another outgoing + * packet. + */ + + if (kvaser_txready(priv)) + { + /* No, there is space for another transfer. Poll the network for + * new XMIT data. + */ + + devif_poll(&priv->dev, kvaser_sock_txpoll); + } + } + + net_unlock(); +} + +/***************************************************************************** + * Name: kvaser_sock_txavail + * + * Description: + * Driver callback invoked when new TX data is available. This is a + * stimulus perform an out-of-cycle poll and, thereby, reduce the TX + * latency. + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Called in normal user mode + * + *****************************************************************************/ + +static int kvaser_sock_txavail(FAR struct net_driver_s *dev) +{ + FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev->d_private; + + /* Is our single work structure available? It may not be if there are + * pending interrupt actions and we will have to ignore the Tx + * availability action. + */ + + if (work_available(&priv->pollwork)) + { + /* Schedule to serialize the poll on the worker thread. */ + + kvaser_sock_txavail_work(priv); + } + + return OK; +} + +# ifdef CONFIG_NETDEV_IOCTL +/***************************************************************************** + * Name: kvaser_sock_ioctl + * + * Description: + * PHY ioctl command handler + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * cmd - ioctl command + * arg - Argument accompanying the command + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + * Assumptions: + * + *****************************************************************************/ + +static int kvaser_sock_ioctl(FAR struct net_driver_s *dev, int cmd, + unsigned long arg) +{ + return -ENOTTY; +} + +# endif /* CONFIG_NETDEV_IOCTL */ + +/***************************************************************************** + * Name: kvaser_sock_txpoll + * + * Description: + * The transmitter is available, check if the network has any outgoing + * packets ready to send. This is a callback from devif_poll(). + * devif_poll() may be called: + * + * 1. When the preceding TX packet send is complete, + * 2. When the preceding TX packet send timesout and the interface is reset + * 3. During normal TX polling + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * OK on success; a negated errno on failure + * + * Assumptions: + * May or may not be called from an interrupt handler. In either case, + * global interrupts are disabled, either explicitly or indirectly through + * interrupt handling logic. + * + *****************************************************************************/ + +static int kvaser_sock_txpoll(FAR struct net_driver_s *dev) +{ + FAR struct kvaser_sja_s *priv = (FAR struct kvaser_sja_s *)dev->d_private; + + /* If the polling resulted in data that should be sent out on the network, + * the field d_len is set to a value > 0. + */ + + if (priv->dev.d_len > 0) + { + /* Send the packet */ + + kvaser_sock_send(priv); + + /* Check if there is room in the device to hold another packet. If + * not, return a non-zero value to terminate the poll. + */ + + if (!kvaser_txready(priv)) + { + return -EBUSY; + } + } + + /* If zero is returned, the polling will continue until all connections + * have been examined. + */ + + return 0; +} + +/***************************************************************************** + * Name: kvaser_sock_send + *****************************************************************************/ + +static int kvaser_sock_send(FAR struct kvaser_sja_s *priv) +{ + FAR struct can_frame *frame = (FAR struct can_frame *)priv->dev.d_buf; + uint8_t regval = 0; + uint8_t dreg = 0; + uint8_t fi = 0; + int i = 0; + + /* Check TX buffer status */ + + if (!kvaser_txready(priv)) + { + canerr("ERROR: TX buffer not available\n"); + return -EBUSY; + } + +#ifdef CONFIG_CAN_FD + /* Drop CAN FD frames */ + + if (priv->dev.d_len != sizeof(struct can_frame)) + { + canerr("ERROR: CAN FD frames not supported\n"); + return -ENOTSUP; + } +#endif + + /* Set up the DLC */ + + fi = frame->can_dlc; + + /* Set RTR bit */ + + fi |= (frame->can_id & CAN_RTR_FLAG ? SJA1000_FI_RTR : 0); + +#ifdef CONFIG_NET_CAN_EXTID + if (frame->can_id & CAN_EFF_FLAG) + { + fi |= SJA1000_FI_FF; + kvaser_putreg_sja(priv, SJA1000_DATA_0_REG, fi); + + /* Write ID */ + + regval = (frame->can_id & 0x1fe00000) >> 21; + kvaser_putreg_sja(priv, SJA1000_DATA_1_REG, regval); + regval = (frame->can_id & 0x001fe000) >> 13; + kvaser_putreg_sja(priv, SJA1000_DATA_2_REG, regval); + regval = (frame->can_id & 0x00001fe0) >> 5; + kvaser_putreg_sja(priv, SJA1000_DATA_3_REG, regval); + regval = (frame->can_id & 0x0000001f) << 3; + kvaser_putreg_sja(priv, SJA1000_DATA_4_REG, regval); + + dreg = SJA1000_DATA_5_REG; + } + else +#endif + { + kvaser_putreg_sja(priv, SJA1000_DATA_0_REG, fi); + + /* Write ID */ + + regval = (frame->can_id & 0x07f8) >> 3; + kvaser_putreg_sja(priv, SJA1000_DATA_1_REG, regval); + regval = (frame->can_id & 0x0007) << 5; + kvaser_putreg_sja(priv, SJA1000_DATA_2_REG, regval); + + dreg = SJA1000_DATA_3_REG; + } + + /* Set up the data fields */ + + if (frame->can_dlc > 8) + { + frame->can_dlc = 8; + } + + for (i = 0; i < frame->can_dlc; i++) + { + kvaser_putreg_sja(priv, dreg + i, frame->data[i]); + } + + /* Transmit */ + + regval = SJA1000_TX_REQ; + kvaser_putreg_sja(priv, SJA1000_CMD_REG, regval); + + return OK; +} + +/***************************************************************************** + * Name: kvaser_sock_receive + *****************************************************************************/ + +static void kvaser_sock_receive(FAR struct kvaser_sja_s *priv) +{ + FAR struct can_frame *frame = (FAR struct can_frame *)priv->rx_pool; + uint8_t dreg = 0; + int i = 0; + uint8_t regval; + + /* Wait until data in RXFIFO */ + + while (kvaser_getreg_sja(priv, SJA1000_STATUS_REG) & SJA1000_RX_BUF_ST) + { + regval = kvaser_getreg_sja(priv, SJA1000_DATA_0_REG); + + /* Get the DLC */ + + frame->can_dlc = (regval & SJA1000_FI_DLC_MASK); + + /* Get the CAN identifier. */ + + if (!(regval & SJA1000_FI_FF)) + { + frame->can_id = + ((kvaser_getreg_sja(priv, SJA1000_DATA_1_REG) << 3) | + (kvaser_getreg_sja(priv, SJA1000_DATA_2_REG) >> 5)); + + dreg = SJA1000_DATA_3_REG; + } + else + { +#ifdef CONFIG_NET_CAN_EXTID + frame->can_id = + ((kvaser_getreg_sja(priv, SJA1000_DATA_1_REG) << 21) | + (kvaser_getreg_sja(priv, SJA1000_DATA_2_REG) << 13) | + (kvaser_getreg_sja(priv, SJA1000_DATA_3_REG) << 5) | + (kvaser_getreg_sja(priv, SJA1000_DATA_4_REG) >> 3)); + frame->can_id |= CAN_EFF_FLAG; + + dreg = SJA1000_DATA_5_REG; +#else + canerr("ERROR: Received message with extended" + " identifier. Dropped\n"); + + /* Release RX buffer */ + + kvaser_putreg_sja(priv, SJA1000_CMD_REG, SJA1000_RELEASE_BUF); + + continue; +#endif + } + + /* Extract the RTR bit */ + + if (regval & SJA1000_FI_RTR) + { + frame->can_id |= CAN_RTR_FLAG; + } + + /* Get data */ + + for (i = 0; i < can_dlc2bytes(frame->can_dlc); i++) + { + frame->data[i] = kvaser_getreg_sja(priv, dreg + i); + } + + /* Release RX buffer */ + + kvaser_putreg_sja(priv, SJA1000_CMD_REG, SJA1000_RELEASE_BUF); + + /* Copy the buffer pointer to priv->dev.. Set amount of data + * in priv->dev.d_len + */ + + priv->dev.d_len = sizeof(struct can_frame); + priv->dev.d_buf = (FAR uint8_t *)frame; + + /* Send to socket interface */ + + NETDEV_RXPACKETS(&priv->dev); + + can_input(&priv->dev); + + /* Point the packet buffer back to the next Tx buffer that will be + * used during the next write. If the write queue is full, then + * this will point at an active buffer, which must not be written + * to. This is OK because devif_poll won't be called unless the + * queue is not full. + */ + + priv->dev.d_buf = (FAR uint8_t *)priv->tx_pool; + } +} + +#ifdef CONFIG_NET_CAN_ERRORS +/***************************************************************************** + * Name: kvaser_sock_error + *****************************************************************************/ + +static void kvaser_sock_error(FAR struct kvaser_sja_s *priv, uint8_t st) +{ + FAR struct can_frame *frame = NULL; + uint8_t regval; + uint16_t errbits = 0; + + /* Data overrun interrupt */ + + if (st & SJA1000_OVERRUN_INT_ST) + { + frame->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + errbits |= CAN_ERR_CRTL; + + /* Clear data overrun */ + + kvaser_putreg_sja(priv, SJA1000_CMD_REG, SJA1000_CLR_OVERRUN); + } + + /* Error passive interrupt */ + + if (st & SJA1000_ERR_PASSIVE_INT_ST) + { + frame->data[1] |= (CAN_ERR_CRTL_RX_PASSIVE | CAN_ERR_CRTL_TX_PASSIVE); + errbits |= CAN_ERR_CRTL; + } + + /* Arbitration lost interrupt */ + + if (st & SJA1000_ARB_LOST_INT_ST) + { + errbits |= CAN_ERR_LOSTARB; + regval = (kvaser_getreg_sja(priv, SJA1000_ARB_LOST_CAP_REG) & + SJA1000_ARB_LOST_CAP_M); + frame->data[0] = CAN_ERR_LOSTARB_BIT(regval); + } + + /* BUS error interrupt */ + + if (st & SJA1000_BUS_ERR_INT_ST) + { + errbits |= CAN_ERR_BUSERROR; + } + + if (errbits != 0) + { + frame = (FAR struct can_frame *)priv->rx_pool; + + canerr("ERROR: errbits = %08" PRIx16 "\n", errbits); + + /* Copy frame */ + + frame->can_id = errbits; + frame->can_dlc = CAN_ERR_DLC; + + net_lock(); + + /* Copy the buffer pointer to priv->dev.. Set amount of data + * in priv->dev.d_len + */ + + priv->dev.d_len = sizeof(struct can_frame); + priv->dev.d_buf = (FAR uint8_t *)frame; + + /* Send to socket interface */ + + NETDEV_ERRORS(&priv->dev); + + can_input(&priv->dev); + + /* Point the packet buffer back to the next Tx buffer that will be + * used during the next write. If the write queue is full, then + * this will point at an active buffer, which must not be written + * to. This is OK because devif_poll won't be called unless the + * queue is not full. + */ + + priv->dev.d_buf = (FAR uint8_t *)priv->tx_pool; + net_unlock(); + } +} +#endif + +/***************************************************************************** + * Name: kvaser_sock_interrupt_work + *****************************************************************************/ + +static void kvaser_sock_interrupt_work(FAR void *arg) +{ + FAR struct kvaser_driver_s *priv = arg; + uint8_t st = 0; + uint8_t i = 0; + + for (i = 0; i < priv->count; i++) + { + st = kvaser_getreg_sja(&priv->sja[i], SJA1000_INT_RAW_REG); + if (st == 0) + { + continue; + } + + /* Receive interrupt */ + + if (st & SJA1000_RX_INT_ST) + { + kvaser_sock_receive(&priv->sja[i]); + } + + /* Transmit interrupt */ + + if (st & SJA1000_TX_INT_ST) + { + NETDEV_TXDONE(&priv->sja[i].dev); + + /* There should be space for a new TX in any event. + * Poll the network for new XMIT data. + */ + + net_lock(); + devif_poll(&priv->sja[i].dev, kvaser_sock_txpoll); + net_unlock(); + } + +#ifdef CONFIG_NET_CAN_ERRORS + /* Handle errors */ + + kvaser_sock_error(&priv->sja[i], st); +#endif + } +} +#endif /* CONFIG_CAN_KVASER_SOCKET */ + +/***************************************************************************** + * Name: kvaser_interrupt + * + * Description: + * Initialize SJA1000 device + * + *****************************************************************************/ + +static int kvaser_interrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct kvaser_driver_s *priv = (FAR struct kvaser_driver_s *)arg; + + DEBUGASSERT(priv != NULL); + + if (kvaser_getreg_s5920(priv, KVASER_S5920_INTCSR) & + KVASER_S5920_INTCSR_INT_ASSERT) + { +#ifdef CONFIG_CAN_KVASER_CHARDEV + /* Handle character device interrupt */ + + kvaser_chardev_interrupt(priv); +#endif + +#ifdef CONFIG_CAN_KVASER_SOCKET + /* Derefer SocketCAN interrupt to work queue */ + + work_queue(KVASER_CANWORK, &priv->irqwork, + kvaser_sock_interrupt_work, priv, 0); +#endif + } + + return OK; +} + +/***************************************************************************** + * Name: kvaser_init + * + * Description: + * Initialize SJA1000 device + * + *****************************************************************************/ + +static void kvaser_init(FAR struct kvaser_driver_s *priv) +{ + uint32_t regval = 0; + + /* Only legacy IRQ supported by kvaser */ + + priv->irq = pci_get_irq(priv->pcidev); + irq_attach(priv->irq, kvaser_interrupt, priv); + + /* Enable card interrupts */ + + regval = kvaser_getreg_s5920(priv, KVASER_S5920_INTCSR); + regval |= KVASER_S5920_INTCSR_INT_ADDON; + kvaser_putreg_s5920(priv, KVASER_S5920_INTCSR, regval); + + /* Enable interrupts */ + + up_enable_irq(priv->irq); +} + +/***************************************************************************** + * Name: kvaser_count_sja + * + * Description: + * Proble SJA1000 devices on board and return the number of vailalbe chips. + * + *****************************************************************************/ + +static uint8_t kvaser_count_sja(FAR struct kvaser_driver_s *priv) +{ + uint32_t offset; + uint8_t regval; + uint8_t i; + + /* Reset chip and check if reset bit has changed */ + + for (i = 0; i < KVASER_SJA_MAX; i++) + { + offset = priv->sja_base + SJA1000_MODE_REG + (i * KVASER_SJA_REGS); + + pci_write_io_byte(priv->pcidev, offset, SJA1000_RESET_MODE); + pci_read_io_byte(priv->pcidev, offset, ®val); + + if (regval != SJA1000_RESET_MODE) + { + break; + } + } + + return i; +} + +/***************************************************************************** + * Name: kvaser_probe + * + * Description: + * Probe device + * + *****************************************************************************/ + +static int kvaser_probe(FAR struct pci_device_s *dev) +{ + FAR struct kvaser_driver_s *priv = NULL; + uint8_t i = 0; + int ret; +#ifdef CONFIG_CAN_KVASER_CHARDEV + uint8_t count; + char devpath[PATH_MAX]; +#endif + + /* Allocate the interface structure */ + + priv = kmm_zalloc(sizeof(*priv)); + if (priv == NULL) + { + return -ENOMEM; + } + + /* Initialzie PCI dev */ + + priv->pcidev = dev; + + pci_set_master(dev); + pciinfo("Enabled bus mastering\n"); + pci_enable_device(dev); + pciinfo("Enabled memory resources\n"); + + /* Kvaser cards support only IO access */ + + if (pci_resource_flags(dev, KVASER_SJA_BAR) != PCI_RESOURCE_IO) + { + ret = -ENOTSUP; + goto errout; + } + + /* Get S5920 base */ + + priv->s5920_base = (uintptr_t)pci_map_bar(dev, KVASER_S5920_BAR); + if (!priv->s5920_base) + { + pcierr("Not found S5920 bar\n"); + ret = -ENXIO; + goto errout; + } + + /* Get SJA1000 base */ + + priv->sja_base = (uintptr_t)pci_map_bar(dev, KVASER_SJA_BAR); + if (!priv->sja_base) + { + pcierr("Not found SJA bar\n"); + ret = -ENXIO; + goto errout; + } + + /* Get XILINX base */ + + priv->xilinx_base = (uintptr_t)pci_map_bar(dev, KVASER_XILINX_BAR); + if (!priv->xilinx_base) + { + pcierr("Not found XILINX bar\n"); + ret = -ENXIO; + goto errout; + } + + /* Get number of SJA1000 chips */ + + priv->count = kvaser_count_sja(priv); + + pciinfo("detected %d SJA1000 deviced\n", priv->count); + + /* Allocate SJA1000 devices */ + + priv->sja = kmm_zalloc(sizeof(struct kvaser_sja_s) * priv->count); + if (priv->sja == NULL) + { + ret = -ENOMEM; + goto errout; + } + + /* Common initialziation for all channels */ + + kvaser_init(priv); + + /* Handle all SJA1000 devices */ + + for (i = 0; i < priv->count; i++) + { + /* Common initialization */ + + priv->sja[i].base = priv->sja_base + (i * KVASER_SJA_REGS); + priv->sja[i].pcidev = dev; + +#ifdef CONFIG_CAN_KVASER_CHARDEV + count = g_kvaser_count++; + + /* Get devpath for this SJA1000 device */ + + snprintf(devpath, PATH_MAX, "/dev/can%d", count); + + /* Initialize SJA1000 channel */ + + priv->sja[i].dev.cd_ops = &g_kvaser_can_ops; + priv->sja[i].dev.cd_priv = &priv->sja[i]; + + /* Register CAN device */ + + ret = can_register(devpath, &priv->sja[i].dev); + if (ret < 0) + { + pcierr("ERROR: failed to register count=%d, %d\n", i, ret); + goto errout; + } +#endif + +#ifdef CONFIG_CAN_KVASER_SOCKET + /* Initialize the driver structure */ + + priv->sja[i].dev.d_ifup = kvaser_sock_ifup; + priv->sja[i].dev.d_ifdown = kvaser_sock_ifdown; + priv->sja[i].dev.d_txavail = kvaser_sock_txavail; +# ifdef CONFIG_NETDEV_IOCTL + priv->sja[i].dev.d_ioctl = kvaser_sock_ioctl; +# endif + priv->sja[i].dev.d_private = &priv->sja[i]; + + /* Put the interface in the down state. This usually amounts to + * resetting the device and/or calling kvaser_sock_ifdown(). + */ + + kvaser_sock_ifdown(&priv->sja[i].dev); + + /* Register the device with the OS so that socket IOCTLs can be + * performed + */ + + ret = netdev_register(&priv->sja[i].dev, NET_LL_CAN); + if (ret < 0) + { + pcierr("ERROR: failed to register count=%d, %d\n", i, ret); + goto errout; + } +#endif + } + + return OK; + +errout: + for (i = 0; i < priv->count; i++) + { + if (priv->sja[i].pcidev) + { +#ifdef CONFIG_CAN_KVASER_SOCKET + netdev_unregister(&priv->sja[i].dev); +#endif + +#ifdef CONFIG_CAN_KVASER_CHARDEV + snprintf(devpath, PATH_MAX, "/dev/can%d", i); + unregister_driver(devpath); +#endif + } + } + + kmm_free(priv->sja); + kmm_free(priv); + + return ret; +} + +/***************************************************************************** + * Public Functions + *****************************************************************************/ + +/***************************************************************************** + * Name: pci_kvaser_init + * + * Description: + * Register a pci driver + * + *****************************************************************************/ + +int pci_kvaser_init(void) +{ + return pci_register_driver(&g_kvaser_drv); +} diff --git a/drivers/can/sja1000.h b/drivers/can/sja1000.h index 80895020bc14a..559e82a537cb2 100644 --- a/drivers/can/sja1000.h +++ b/drivers/can/sja1000.h @@ -29,6 +29,8 @@ #include +#include + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -48,6 +50,10 @@ #define SJA1000_MODE_REG (0x00) +/* Sleep mode */ + +#define SJA1000_SLEEP_MODE (BIT(4)) + /* SJA1000_RX_FILTER_MODE : R/W; bitpos: [3]; default: 0; * This bit is used to configure the filter mode. 0: Dual filter mode; 1: * Single filter mode. @@ -458,6 +464,10 @@ #define SJA1000_TIME_SEG1_V 0x0000000F #define SJA1000_TIME_SEG1_S 0 +/* Output control */ + +#define SJA1000_OUTCTRL_REG 0x08 + /* SJA1000_ARB_LOST_CAP_REG register * Arbitration Lost Capture Register */ @@ -906,4 +916,10 @@ #define SJA1000_CD_V 0x00000007 #define SJA1000_CD_S 0 +/* Frame information */ + +#define SJA1000_FI_DLC_MASK (0xf) /* Data length code bit */ +#define SJA1000_FI_RTR (BIT(6)) /* Remote transmission request */ +#define SJA1000_FI_FF (BIT(7)) /* Frame format */ + #endif /* __DRIVERS_CAN_SJA1000_H */ diff --git a/drivers/pci/pci_drivers.c b/drivers/pci/pci_drivers.c index 67ade5b515289..74fdc2297d2ce 100644 --- a/drivers/pci/pci_drivers.c +++ b/drivers/pci/pci_drivers.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "pci_drivers.h" @@ -160,6 +161,16 @@ int pci_register_drivers(void) } #endif + /* Initialzie Kvaser pci driver */ + +#ifdef CONFIG_CAN_KVASER + ret = pci_kvaser_init(); + if (ret < 0) + { + pcierr("pci_kvaser_init failed, ret=%d\n", ret); + } +#endif + ret = pci_dev_register(); if (ret < 0) { diff --git a/include/nuttx/can/kvaser_pci.h b/include/nuttx/can/kvaser_pci.h new file mode 100644 index 0000000000000..2c8e41d236134 --- /dev/null +++ b/include/nuttx/can/kvaser_pci.h @@ -0,0 +1,59 @@ +/**************************************************************************** + * include/nuttx/can/kvaser_pci.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_CAN_KVASER_H +#define __INCLUDE_NUTTX_CAN_KVASER_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: pci_kvaser_init + * + * Description: + * Register a pci driver + * + ****************************************************************************/ + +int pci_kvaser_init(void); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_CAN_KVASER_H */ From 5c5e27d4a183dea1f4b8025023b69d381e2154df Mon Sep 17 00:00:00 2001 From: p-szafonimateusz Date: Wed, 6 Nov 2024 11:06:03 +0100 Subject: [PATCH 2/3] drivers/can: add CTU CAN FD driver (qemu only) Add CTU CAN FD driver for qemu only: https://www.qemu.org/docs/master/system/devices/can.html#ctu-can-fd-support-examples Signed-off-by: p-szafonimateusz --- drivers/can/CMakeLists.txt | 4 + drivers/can/Kconfig | 30 + drivers/can/Make.defs | 4 + drivers/can/ctucanfd.h | 264 ++++ drivers/can/ctucanfd_pci.c | 1956 ++++++++++++++++++++++++++++++ drivers/pci/pci_drivers.c | 11 + include/nuttx/can/ctucanfd_pci.h | 59 + 7 files changed, 2328 insertions(+) create mode 100644 drivers/can/ctucanfd.h create mode 100644 drivers/can/ctucanfd_pci.c create mode 100644 include/nuttx/can/ctucanfd_pci.h diff --git a/drivers/can/CMakeLists.txt b/drivers/can/CMakeLists.txt index b8d33cef3b093..e6d6ea3137890 100644 --- a/drivers/can/CMakeLists.txt +++ b/drivers/can/CMakeLists.txt @@ -34,4 +34,8 @@ if(CONFIG_CAN_KVASER) list(APPEND SRCS kvaser_pci.c) endif() +if(CONFIG_CAN_CTUCANFD) + list(APPEND SRCS ctucanfd_pci.c) +endif() + target_sources(drivers PRIVATE ${SRCS}) diff --git a/drivers/can/Kconfig b/drivers/can/Kconfig index 45d658ad16bdc..a612d519c5fb2 100644 --- a/drivers/can/Kconfig +++ b/drivers/can/Kconfig @@ -299,4 +299,34 @@ endchoice # "Kvaser PCI CAN device type" endif # CAN_KVASER +config CAN_CTUCANFD + bool "CTUCANFD PCI CAN card" + default n + depends on PCI + ---help--- + Enable driver support for CTU CAN FD PCI card. + NOTE: for now works only with QEMU + +if CAN_CTUCANFD + +choice + prompt "CTU CAN FD PCI CAN device type" + default CAN_CTUCANFD_CHARDEV if CAN + default CAN_CTUCANFD_SOCKET if NET_CAN + +config CAN_CTUCANFD_CHARDEV + bool "CTU CAN FD PCI can device as chardev" + depends on CAN + select ARCH_HAVE_CAN_ERRORS + +config CAN_CTUCANFD_SOCKET + bool "CTU CAN FD PCI can device as socketCAN" + depends on NET_CAN + select NET_CAN_HAVE_ERRORS + select NET_CAN_HAVE_CANFD + +endchoice # "CTU CAN FD PCI CAN device type" + +endif # CAN_CTUCANFD + endmenu # CAN Driver Support diff --git a/drivers/can/Make.defs b/drivers/can/Make.defs index 741b57017260c..1a0e16b1341a8 100644 --- a/drivers/can/Make.defs +++ b/drivers/can/Make.defs @@ -38,6 +38,10 @@ ifeq ($(CONFIG_CAN_KVASER),y) CSRCS += kvaser_pci.c endif +ifeq ($(CONFIG_CAN_CTUCANFD),y) +CSRCS += ctucanfd_pci.c +endif + # Include CAN device driver build support DEPPATH += --dep-path can diff --git a/drivers/can/ctucanfd.h b/drivers/can/ctucanfd.h new file mode 100644 index 0000000000000..3f7ada9fb54f4 --- /dev/null +++ b/drivers/can/ctucanfd.h @@ -0,0 +1,264 @@ +/**************************************************************************** + * drivers/can/ctucanfd.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __DRIVERS_CAN_CTUCANFD_H +#define __DRIVERS_CAN_CTUCANFD_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* CAN FD Core memory map */ + +#define CTUCANFD_CTRL (0x000) +#define CTUCANFD_TXT1 (0x100) +#define CTUCANFD_TXT2 (0x200) +#define CTUCANFD_TXT3 (0x300) +#define CTUCANFD_TXT4 (0x400) +#define CTUCANFD_TXT5 (0x500) +#define CTUCANFD_TXT6 (0x600) +#define CTUCANFD_TXT7 (0x700) +#define CTUCANFD_TXT8 (0x800) +#define CTUCANFD_TXT_SIZE (0x100) + +/* Control registers */ + +#define CTUCANFD_VERID (0x00) +#define CTUCANFD_SET_MODE (0x04) +#define CTUCANFD_STATUS (0x08) +#define CTUCANFD_CMD (0x0c) +#define CTUCANFD_INTSTAT (0x10) +#define CTUCANFD_INTENSET (0x14) +#define CTUCANFD_INTENCLR (0x18) +#define CTUCANFD_INTMASKSET (0x1c) +#define CTUCANFD_INTMASKCLR (0x20) +#define CTUCANFD_BTR (0x24) +#define CTUCANFD_BTRFD (0x28) +#define CTUCANFD_FAULT (0x2c) +#define CTUCANFD_ERRCNT (0x30) +#define CTUCANFD_ERR (0x34) +#define CTUCANFD_CTRPRES (0x38) +#define CTUCANFD_FLTR_A_MSK (0x3c) +#define CTUCANFD_FLTR_A_VAL (0x40) +#define CTUCANFD_FLTR_B_MSK (0x44) +#define CTUCANFD_FLTR_B_VAL (0x48) +#define CTUCANFD_FLTR_C_MSK (0x4c) +#define CTUCANFD_FLTR_C_VAL (0x50) +#define CTUCANFD_FLTR_RAN_L (0x54) +#define CTUCANFD_FLTR_RAN_H (0x58) +#define CTUCANFD_FLTR (0x5c) +#define CTUCANFD_RXMEMINFO (0x60) +#define CTUCANFD_RXPOINTERS (0x64) +#define CTUCANFD_RXSETSTAT (0x68) +#define CTUCANFD_RXDATA (0x6c) +#define CTUCANFD_TXSTAT (0x70) +#define CTUCANFD_TXINFOCMD (0x74) +#define CTUCANFD_TXPRIO (0x78) +#define CTUCANFD_MISC (0x7c) +#define CTUCANFD_SSP (0x80) +#define CTUCANFD_RXFRCTR (0x84) +#define CTUCANFD_TXFRCTR (0x88) +#define CTUCANFD_DEBUG (0x8c) +#define CTUCANFD_YOLO (0x90) +#define CTUCANFD_TS_L (0x94) +#define CTUCANFD_TS_H (0x98) + +/* Mode */ + +#define CTUCANFD_MODE_RST (1 << 0) /* Soft reset */ +#define CTUCANFD_MODE_BMM (1 << 1) /* Bus monitoring mode */ +#define CTUCANFD_MODE_STM (1 << 2) /* Self test mode */ +#define CTUCANFD_MODE_AFM (1 << 3) /* Acceptance filters mode */ +#define CTUCANFD_MODE_FDE (1 << 4) /* Flexible data rate mode */ +#define CTUCANFD_MODE_TTTM (1 << 5) /* Time triggered transmission mode */ +#define CTUCANFD_MODE_ROM (1 << 6) /* Restricted operation mode */ +#define CTUCANFD_MODE_ACF (1 << 7) /* Acknowledge forbidden mode */ +#define CTUCANFD_MODE_TSTM (1 << 8) /* Test mode */ +#define CTUCANFD_MODE_RXBAM (1 << 9) /* TX buffer automatic mode */ +#define CTUCANFD_MODE_TXBBM (1 << 10) /* TXT buffer backup mode */ +#define CTUCANFD_MODE_SAM (1 << 11) /* Self-acknowledge mode */ +#define CTUCANFD_MODE_ERFM (1 << 12) /* Error frame receive mode */ + +/* Settings */ + +#define CTUCANFD_SET_SHFIT (16) /* Shift in 32-bit access mode */ + +#define CTUCANFD_SET_RTRLE (1 << 0) /* Retransmit limit enable */ +#define CTUCANFD_SET_RTRTH_SHIFT (1) /* Retransmit limit threshold shift */ +#define CTUCANFD_SET_ILBP (1 << 5) /* Internal loop back mode */ +#define CTUCANFD_SET_ENA (1 << 6) /* Main enable bif of CTU CAN FD */ +#define CTUCANFD_SET_NISOFD (1 << 7) /* Non ISO FD */ +#define CTUCANFD_SET_PEX (1 << 8) /* Protocol exception handling */ +#define CTUCANFD_SET_TBFBO (1 << 9) /* TX failed for all buffers when bus-off */ +#define CTUCANFD_SET_FDRF (1 << 10) /* Frame filter drop remote frames */ +#define CTUCANFD_SET_PCHKE (1 << 11) /* Enable parity checks in TXT and RX buffers */ + +/* Command */ + +#define CTUCANFD_CMD_RXRPMV (1 << 1) /* RX buffer read pointer move */ +#define CTUCANFD_CMD_RRB (1 << 2) /* Release RX buffer */ +#define CTUCANFD_CMD_CDO (1 << 3) /* Clear data overrun flag in RX buffer */ +#define CTUCANFD_CMD_ERCRST (1 << 4) /* Error counters reset */ +#define CTUCANFD_CMD_RXFCRST (1 << 5) /* Clear RX bus traffic counter */ +#define CTUCANFD_CMD_TXFCRST (1 << 6) /* Clear TX bus traffic counter */ +#define CTUCANFD_CMD_CPEXS (1 << 7) /* Clear protocol exception status */ +#define CTUCANFD_CMD_CRXPE (1 << 8) /* Clear STATUS[RXPE] flag */ +#define CTUCANFD_CMD_CTXPE (1 << 9) /* Clear STATUS[TXPE] flag */ +#define CTUCANFD_CMD_CTXDPE (1 << 10) /* Clear STATUS[TXDPE] flag */ + +/* Interrupts */ + +#define CTUCANFD_INT_RXI (1 << 0) /* Frame received interrupt */ +#define CTUCANFD_INT_TXI (1 << 1) /* Frame transmitted interrupt */ +#define CTUCANFD_INT_EWLI (1 << 2) /* Error warning limit interrupt */ +#define CTUCANFD_INT_DOI (1 << 3) /* Data overrun interrupt */ +#define CTUCANFD_INT_FCSI (1 << 4) /* Fault confinement state changed interrupt */ +#define CTUCANFD_INT_ALI (1 << 5) /* Arbitration lost interrupt */ +#define CTUCANFD_INT_BEI (1 << 6) /* Bus error interrupt */ +#define CTUCANFD_INT_OFI (1 << 7) /* Overload frame interrupt */ +#define CTUCANFD_INT_RXFI (1 << 8) /* RX buffer full interrupt */ +#define CTUCANFD_INT_BSI (1 << 9) /* Bit rate shifted interrupt */ +#define CTUCANFD_INT_RBNEI (1 << 10) /* RX buffer not empty interrupt */ +#define CTUCANFD_INT_TXBHCI (1 << 11) /* TXT buffer HW command interrupt */ + +/* RX_STATUS and RX_SETTINGS */ + +#define CTUCANFD_RXSTAT_RXE (1 << 0) /* RX buffer is empty */ +#define CTUCANFD_RXSTAT_RXF (1 << 1) /* RX buffer is full */ +#define CTUCANFD_RXSTAT_RXMOF (1 << 2) /* RX buffer middle of frame */ +#define CTUCANFD_RXSTAT_RXFRC_SHIFT (4) /* RX buffer frame count */ +#define CTUCANFD_RXSTAT_RXFRC_MASK (0x7ff << CTUCANFD_RXSTAT_RXFRC_SHIFT) + +/* TX_STATUS */ + +#define CTUCANFD_TXSTAT_SHIFT (4) /* TXyS shift */ +#define CTUCANFD_TXSTAT_MASK (0xf) /* TXyS mask */ +#define CTUCANFD_TXSTAT_GET(val, i) (((val) >> (CTUCANFD_TXSTAT_SHIFT * (i))) & \ + CTUCANFD_TXSTAT_MASK) +#define CTUCANFD_TXSTAT_NOTEXIST (0) /* TXT buffer doesn't exist */ +#define CTUCANFD_TXSTAT_RDY (1) /* "Ready" state */ +#define CTUCANFD_TXSTAT_TRAN (2) /* "TX in porgress" state */ +#define CTUCANFD_TXSTAT_ABTP (3) /* "Abort in progress" state */ +#define CTUCANFD_TXSTAT_TOK (4) /* "TX OK" state */ +#define CTUCANFD_TXSTAT_ERR (6) /* "Failed" state */ +#define CTUCANFD_TXSTAT_ABT (7) /* "Aborted" state */ +#define CTUCANFD_TXSTAT_ETY (8) /* "Empty" state */ +#define CTUCANFD_TXSTAT_PER (9) /* "Parity error" state */ + +/* TX_COMMAND and TXTB_INFO */ + +#define CTUCANFD_TXCMD_TXCE (1 << 0) /* "set empty" */ +#define CTUCANFD_TXCMD_TXCR (1 << 1) /* "set ready" */ +#define CTUCANFD_TXCMD_TXCA (1 << 2) /* "set abort" */ +#define CTUCANFD_TXCMD_TXB_SHIFT (8) +#define CTUCANFD_TXCMD_TXB1 (1 << 8) /* TXT buffer 1 */ +#define CTUCANFD_TXCMD_TXB2 (1 << 9) /* TXT buffer 2 */ +#define CTUCANFD_TXCMD_TXB3 (1 << 10) /* TXT buffer 3 */ +#define CTUCANFD_TXCMD_TXB4 (1 << 11) /* TXT buffer 4 */ +#define CTUCANFD_TXCMD_TXB5 (1 << 12) /* TXT buffer 5 */ +#define CTUCANFD_TXCMD_TXB6 (1 << 13) /* TXT buffer 6 */ +#define CTUCANFD_TXCMD_TXB7 (1 << 14) /* TXT buffer 7 */ +#define CTUCANFD_TXCMD_TXB8 (1 << 15) /* TXT buffer 8 */ + +/* TX_PRIORITY */ + +#define CTUCANFD_TXPRIO_SHIFT (4) + +/* TX buffer */ + +#define CTUCANFD_TXBUF_FMT (0x00) +#define CTUCANFD_TXBUF_ID (0x04) +#define CTUCANFD_TXBUF_TSL (0x08) +#define CTUCANFD_TXBUF_TSU (0x0c) +#define CTUCANFD_TXBUF_DATA (0x10) +#define CTUCANFD_TXBUF_TEST (0x05) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* CAN FD frame format */ + +begin_packed_struct struct ctucanfd_frame_fmt_s +{ + uint32_t dlc:4; /* DLC */ + uint32_t erf:1; /* Error frame flag */ + uint32_t rtr:1; /* Remote frame flag */ + uint32_t ide:1; /* Extended identifier type */ + uint32_t fdf:1; /* Flexible data-rate format */ + uint32_t lbpf:1; /* Loop-back frame */ + uint32_t brs:1; /* Bit rate shift */ + uint32_t esi_rsv:1; /* Error state indicator */ + uint32_t rwcnt:4; /* Size without FRAME_FORMAT WORD */ + uint32_t erf_pos:4; /* Error frame position */ + uint32_t erf_erp:1; /* Error pasive state */ + uint32_t erf_type:3; /* Error frame type */ + uint32_t ivld:1; /* Valid identifier */ + uint32_t lbtbi:3; /* Loop-back TXT index */ + uint32_t _reserved:4; /* Reserved */ +} end_packed_struct; + +union ctucanfd_frame_fmt_u +{ + struct ctucanfd_frame_fmt_s s; + uint32_t u32; +}; + +/* CAN FD frame ID */ + +begin_packed_struct struct ctucanfd_frame_id_s +{ + uint32_t id_ext:18; /* Extended identifier */ + uint32_t id:11; /* Base identifier */ + uint32_t _res:3; /* Reserved */ +} end_packed_struct; + +union ctucanfd_frame_id_u +{ + struct ctucanfd_frame_id_s s; + uint32_t u32; +}; + +/* CAN FD frame test */ + +begin_packed_struct struct ctucanfd_frame_tst_s +{ + uint32_t fstc:1; /* Flip stuff count field bit */ + uint32_t fcrc:1; /* Flip CRC field bit */ + uint32_t sdlc:1; /* Swap DLC in TX */ + uint32_t _res1:4; /* Reserved */ + uint32_t tprm:4; /* Test parameter */ + uint32_t _res2:3; /* Reserved */ +} end_packed_struct; + +/* CAN FD frame format */ + +begin_packed_struct struct ctucanfd_frame_s +{ + struct ctucanfd_frame_fmt_s fmt; /* Frame format */ + struct ctucanfd_frame_id_s id; /* Frame ID */ + uint64_t timestamp; /* Frame timestamp */ + uint8_t data[64]; /* Frame data */ + uint32_t test; /* Frame test */ +} end_packed_struct; + +#endif /* __DRIVERS_CAN_CTUCANFD_H */ diff --git a/drivers/can/ctucanfd_pci.c b/drivers/can/ctucanfd_pci.c new file mode 100644 index 0000000000000..789c01b0da638 --- /dev/null +++ b/drivers/can/ctucanfd_pci.c @@ -0,0 +1,1956 @@ +/***************************************************************************** + * drivers/can/ctucanfd_pci.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + *****************************************************************************/ + +/***************************************************************************** + * Included Files + *****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#ifdef CONFIG_CAN_CTUCANFD_SOCKET +# include +# include +# include +#endif + +#include "ctucanfd.h" + +/***************************************************************************** + * Pre-processor Definitions + *****************************************************************************/ + +/* PCI BARs */ + +#define CTUCANFD_BAR0 0 +#define CTUCANFD_CTUCAN_BAR1 1 + +/* Registers per channel */ + +#define CTUCANFD_CTUCAN_REGS 0x4000 + +/* ID register in BAR0 */ + +#define CTUCANFD_BAR0_ID 0 + +/* Interrupts */ + +#define CTUCANFD_INT_ERR (CTUCANFD_INT_DOI | CTUCANFD_INT_ALI | \ + CTUCANFD_INT_BEI) + +/* Supported TX buffers */ + +#define CTUCANFD_TXBUF_CNT 4 + +/* SocketCAN specific */ + +#ifdef CONFIG_CAN_CTUCANFD_SOCKET + +# define CTUCANFD_POOL_SIZE 1 + +/* Work queue support is required. */ + +# if !defined(CONFIG_SCHED_WORKQUEUE) +# error Work queue support is required +# endif + +# define CTUCANFD_CANWORK LPWORK + +#endif /* CONFIG_CAN_CTUCANFD_SOCKET */ + +/***************************************************************************** + * Private Types + *****************************************************************************/ + +/* CTUCANFD channel private data */ + +struct ctucanfd_can_s +{ +#ifdef CONFIG_CAN_CTUCANFD_CHARDEV + struct can_dev_s dev; +#endif + +#ifdef CONFIG_CAN_CTUCANFD_SOCKET + struct net_driver_s dev; /* Interface understood by the network */ + struct work_s pollwork; /* For deferring poll work to the work wq */ + + /* TX/RX pool */ + +# ifdef CONFIG_NET_CAN_CANFD + struct canfd_frame tx_pool[CTUCANFD_POOL_SIZE]; + struct canfd_frame rx_pool[CTUCANFD_POOL_SIZE]; +# else + struct can_frame tx_pool[CTUCANFD_POOL_SIZE]; + struct can_frame rx_pool[CTUCANFD_POOL_SIZE]; +# endif +#endif + + FAR struct pci_device_s *pcidev; + uint64_t base; + uint8_t txbufcnt; +}; + +/* CTUCANFD private data */ + +struct ctucanfd_driver_s +{ + FAR struct ctucanfd_can_s *devs; + uint8_t count; + uintptr_t bar0_base; + uintptr_t canfd_base; + + /* PCI data */ + + FAR struct pci_device_s *pcidev; + int irq; + uintptr_t base; + +#ifdef CONFIG_CAN_CTUCANFD_SOCKET + /* For deferring interrupt work to the wq */ + + struct work_s irqwork; +#endif +}; + +/***************************************************************************** + * Private Functions Definitions + *****************************************************************************/ + +/* Helpers */ + +static uint32_t ctucanfd_getreg(FAR struct ctucanfd_can_s *priv, + unsigned int offset); +static void ctucanfd_putreg(FAR struct ctucanfd_can_s *priv, + unsigned int offset, + uint32_t value); + +/* Common methods */ + +static void ctucanfd_reset(FAR struct ctucanfd_can_s *priv); +static void ctucanfd_shutdown(FAR struct ctucanfd_can_s *priv); +static void ctucanfd_setup(FAR struct ctucanfd_can_s *priv); +static void ctucanfd_rxint(FAR struct ctucanfd_can_s *priv, bool enable); +static void ctucanfd_txint(FAR struct ctucanfd_can_s *priv, bool enable); +static bool ctucanfd_txready(FAR struct ctucanfd_can_s *priv); + +#ifdef CONFIG_CAN_CTUCANFD_CHARDEV +/* CAN character device methods */ + +static void ctucanfd_chrdev_reset(FAR struct can_dev_s *dev); +static int ctucanfd_chrdev_setup(FAR struct can_dev_s *dev); +static void ctucanfd_chrdev_shutdown(FAR struct can_dev_s *dev); +static void ctucanfd_chrdev_rxint(FAR struct can_dev_s *dev, bool enable); +static void ctucanfd_chrdev_txint(FAR struct can_dev_s *dev, bool enable); +static int ctucanfd_chrdev_ioctl(FAR struct can_dev_s *dev, int cmd, + unsigned long arg); +static int ctucanfd_chrdev_remoterequest(FAR struct can_dev_s *dev, + uint16_t id); +static int ctucanfd_chrdev_send(FAR struct can_dev_s *dev, + struct can_msg_s *msg); +static bool ctucanfd_chrdev_txready(FAR struct can_dev_s *dev); +static bool ctucanfd_chrdev_txempty(FAR struct can_dev_s *dev); +static void ctucanfd_chardev_receive(FAR struct ctucanfd_can_s *priv); +static void ctucanfd_chardev_interrupt(FAR struct ctucanfd_driver_s *priv); +#endif + +#ifdef CONFIG_CAN_CTUCANFD_SOCKET +/* SocketCAN methods */ + +static int ctucanfd_sock_ifup(FAR struct net_driver_s *dev); +static int ctucanfd_sock_ifdown(FAR struct net_driver_s *dev); + +static void ctucanfd_sock_txavail_work(FAR void *arg); +static int ctucanfd_sock_txavail(FAR struct net_driver_s *dev); + +# ifdef CONFIG_NETDEV_IOCTL +static int ctucanfd_sock_ioctl(FAR struct net_driver_s *dev, int cmd, + unsigned long arg); +# endif + +static int ctucanfd_sock_txpoll(FAR struct net_driver_s *dev); +static int ctucanfd_sock_send(FAR struct ctucanfd_can_s *priv); +static void ctucanfd_sock_receive(FAR struct ctucanfd_can_s *priv); +static void ctucanfd_sock_interrupt_work(FAR void *arg); +#endif + +/* Interrupts */ + +static int ctucanfd_interrupt(int irq, FAR void *context, FAR void *arg); + +/* PCI */ + +static void ctucanfd_init(FAR struct ctucanfd_driver_s *priv); +static uint8_t ctucanfd_ctucanfd_probe(FAR struct ctucanfd_driver_s *priv); +static int ctucanfd_probe(FAR struct pci_device_s *dev); + +/***************************************************************************** + * Private Data + *****************************************************************************/ + +#ifdef CONFIG_CAN_CTUCANFD_CHARDEV +static const struct can_ops_s g_ctucanfd_can_ops = +{ + .co_reset = ctucanfd_chrdev_reset, + .co_setup = ctucanfd_chrdev_setup, + .co_shutdown = ctucanfd_chrdev_shutdown, + .co_rxint = ctucanfd_chrdev_rxint, + .co_txint = ctucanfd_chrdev_txint, + .co_ioctl = ctucanfd_chrdev_ioctl, + .co_remoterequest = ctucanfd_chrdev_remoterequest, + .co_send = ctucanfd_chrdev_send, + .co_txready = ctucanfd_chrdev_txready, + .co_txempty = ctucanfd_chrdev_txempty, +}; +#endif + +static const struct pci_device_id_s g_ctucanfd_id_table[] = +{ + { + PCI_DEVICE(0x1760, 0xff00), + .driver_data = 0 + }, + { } +}; + +static struct pci_driver_s g_ctucanfd_drv = +{ + .id_table = g_ctucanfd_id_table, + .probe = ctucanfd_probe, +}; + +#ifdef CONFIG_CAN_CTUCANFD_CHARDEV +static uint8_t g_ctucanfd_count = 0; +#endif + +/***************************************************************************** + * Private Functions + *****************************************************************************/ + +/***************************************************************************** + * Name: ctucanfd_getreg + *****************************************************************************/ + +static uint32_t ctucanfd_getreg(FAR struct ctucanfd_can_s *priv, + unsigned int offset) +{ + uintptr_t addr = priv->base + offset; + return *((FAR volatile uint32_t *)addr); +} + +/***************************************************************************** + * Name: ctucanfd_putreg + *****************************************************************************/ + +static void ctucanfd_putreg(FAR struct ctucanfd_can_s *priv, + unsigned int offset, + uint32_t value) +{ + uintptr_t addr = priv->base + offset; + *((FAR volatile uint32_t *)addr) = value; +} + +/***************************************************************************** + * Name: ctucanfd_reset + *****************************************************************************/ + +static void ctucanfd_reset(FAR struct ctucanfd_can_s *priv) +{ + /* Soft reset */ + + ctucanfd_putreg(priv, CTUCANFD_SET_MODE, CTUCANFD_MODE_RST); +} + +/***************************************************************************** + * Name: ctucanfd_shutdown + *****************************************************************************/ + +static void ctucanfd_shutdown(FAR struct ctucanfd_can_s *priv) +{ + ctucanfd_putreg(priv, CTUCANFD_SET_MODE, 0); +} + +/***************************************************************************** + * Name: ctucanfd_setup + *****************************************************************************/ + +static void ctucanfd_setup(FAR struct ctucanfd_can_s *priv) +{ + uint32_t regval = 0; + int i; + + /* REVISIT: missing bus timings configuration. + * + * This driver was verified on QEMU with virtual host CAN network, + * which doesn't need bus timings. + * For real hardware, these registers must be properly configured ! + */ + + ctucanfd_putreg(priv, CTUCANFD_BTR, 0); + ctucanfd_putreg(priv, CTUCANFD_BTRFD, 0); + + /* Enable interrupts */ + +#if defined(CONFIG_CAN_ERRORS) || defined(CONFIG_NET_CAN_ERRORS) + ctucanfd_putreg(priv, CTUCANFD_INTENSET, CTUCANFD_INT_ERR); +#else + ctucanfd_putreg(priv, CTUCANFD_INTENCLR, CTUCANFD_INT_ERR); +#endif + + /* Configure TX priority */ + + for (i = 0; i < priv->txbufcnt; i++) + { + regval |= 1 << (CTUCANFD_TXPRIO_SHIFT * i); + } + + ctucanfd_putreg(priv, CTUCANFD_TXPRIO, regval); + + /* Set MODE register */ + + regval = ctucanfd_getreg(priv, CTUCANFD_SET_MODE); + + /* Enable CTU CAN FD */ + + regval |= CTUCANFD_SET_ENA << CTUCANFD_SET_SHFIT; + + /* RX buffer automatic mode */ + + regval |= CTUCANFD_MODE_RXBAM; + + /* Write SETTINGS and MODE */ + + ctucanfd_putreg(priv, CTUCANFD_SET_MODE, regval); +} + +/***************************************************************************** + * Name: ctucanfd_rxint + *****************************************************************************/ + +static void ctucanfd_rxint(FAR struct ctucanfd_can_s *priv, bool enable) +{ + if (enable) + { + ctucanfd_putreg(priv, CTUCANFD_INTENSET, CTUCANFD_INT_RBNEI); + } + else + { + ctucanfd_putreg(priv, CTUCANFD_INTENCLR, CTUCANFD_INT_RBNEI); + } +} + +/***************************************************************************** + * Name: ctucanfd_txint + *****************************************************************************/ + +static void ctucanfd_txint(FAR struct ctucanfd_can_s *priv, bool enable) +{ + if (enable) + { + ctucanfd_putreg(priv, CTUCANFD_INTENSET, CTUCANFD_INT_TXI); + } + else + { + ctucanfd_putreg(priv, CTUCANFD_INTENCLR, CTUCANFD_INT_TXI); + } +} + +/***************************************************************************** + * Name: ctucanfd_txready + *****************************************************************************/ + +static bool ctucanfd_txready(FAR struct ctucanfd_can_s *priv) +{ + uint32_t regval; + int i; + + regval = ctucanfd_getreg(priv, CTUCANFD_TXSTAT); + + for (i = 0; i < priv->txbufcnt; i++) + { + if (CTUCANFD_TXSTAT_GET(regval, i) == CTUCANFD_TXSTAT_ETY) + { + return true; + } + } + + return false; +} + +#ifdef CONFIG_CAN_CTUCANFD_CHARDEV +/***************************************************************************** + * Name: ctucanfd_chrdev_reset + * + * Description: + * Reset the CAN device. Called early to initialize the hardware. This + * function is called, before ctucanfd_setup() and on error conditions. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + *****************************************************************************/ + +static void ctucanfd_chrdev_reset(FAR struct can_dev_s *dev) +{ + FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev; + + ctucanfd_reset(priv); +} + +/***************************************************************************** + * Name: ctucanfd_chrdev_setup + * + * Description: + * Configure the CAN. This method is called the first time that the CAN + * device is opened. This will occur when the port is first opened. + * This setup includes configuring and attaching CAN interrupts. + * All CAN interrupts are disabled upon return. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + *****************************************************************************/ + +static int ctucanfd_chrdev_setup(FAR struct can_dev_s *dev) +{ + FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev; + + ctucanfd_setup(priv); + + return OK; +} + +/***************************************************************************** + * Name: ctucanfd_chrdev_shutdown + * + * Description: + * Disable the CAN. This method is called when the CAN device is closed. + * This method reverses the operation the setup method. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + *****************************************************************************/ + +static void ctucanfd_chrdev_shutdown(FAR struct can_dev_s *dev) +{ + FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev; + + ctucanfd_shutdown(priv); +} + +/***************************************************************************** + * Name: ctucanfd_chrdev_rxint + * + * Description: + * Call to enable or disable RX interrupts. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + *****************************************************************************/ + +static void ctucanfd_chrdev_rxint(FAR struct can_dev_s *dev, bool enable) +{ + FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev; + + ctucanfd_rxint(priv, enable); +} + +/***************************************************************************** + * Name: ctucanfd_chrdev_txint + * + * Description: + * Call to enable or disable TX interrupts. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + *****************************************************************************/ + +static void ctucanfd_chrdev_txint(FAR struct can_dev_s *dev, bool enable) +{ + FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev; + + ctucanfd_txint(priv, enable); +} + +/***************************************************************************** + * Name: ctucanfd_chrdev_ioctl + * + * Description: + * All ioctl calls will be routed through this method + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + *****************************************************************************/ + +static int ctucanfd_chrdev_ioctl(FAR struct can_dev_s *dev, int cmd, + unsigned long arg) +{ + return -ENOTTY; +} + +/***************************************************************************** + * Name: ctucanfd_chrdev_remoterequest + * + * Description: + * Send a remote request + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + *****************************************************************************/ + +static int ctucanfd_chrdev_remoterequest(FAR struct can_dev_s *dev, + uint16_t id) +{ + return -ENOTSUP; +} + +/***************************************************************************** + * Name: ctucanfd_chrdev_send + * + * Description: + * Send one can message. + * + * One CAN-message consists of a maximum of 10 bytes. A message is + * composed of at least the first 2 bytes (when there are no data bytes). + * + * Byte 0: Bits 0-7: Bits 3-10 of the 11-bit CAN identifier + * Byte 1: Bits 5-7: Bits 0-2 of the 11-bit CAN identifier + * Bit 4: Remote Transmission Request (RTR) + * Bits 0-3: Data Length Code (DLC) + * Bytes 2-10: CAN data + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + *****************************************************************************/ + +static int ctucanfd_chrdev_send(FAR struct can_dev_s *dev, + FAR struct can_msg_s *msg) +{ + FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev; + union ctucanfd_frame_fmt_u fmt; + union ctucanfd_frame_id_u id; + uint32_t regval; + uint32_t offset = 0; + int txidx; + int i; + uint8_t bytes; + + /* Get TX empty buffer */ + + regval = ctucanfd_getreg(priv, CTUCANFD_TXSTAT); + + for (i = 0; i < priv->txbufcnt; i++) + { + if (CTUCANFD_TXSTAT_GET(regval, i) == CTUCANFD_TXSTAT_ETY) + { + offset = CTUCANFD_TXT1 + CTUCANFD_TXT_SIZE * i; + txidx = i; + break; + } + } + + if (offset == 0) + { + canerr("ERROR: TX buffer not available\n"); + return -EBUSY; + } + + /* Reset data */ + + fmt.u32 = 0; + id.u32 = 0; + + /* Set up the DLC */ + + fmt.s.dlc = msg->cm_hdr.ch_dlc; + + /* Set RTR bit */ + + fmt.s.rtr = msg->cm_hdr.ch_rtr; + +#ifdef CONFIG_NET_CAN_EXTID + if (msg->cm_hdr.ch_extid) + { + fmt.s.ide = 1; + id.s.id_ext = msg->cm_hdr.ch_id; + } + else +#endif + { + fmt.s.ide = 0; + id.s.id = msg->cm_hdr.ch_id; + } + + /* Write frame */ + + ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_FMT, fmt.u32); + ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_ID, id.u32); + ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_TSL, 0); + ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_TSU, 0); + + bytes = can_dlc2bytes(msg->cm_hdr.ch_dlc); + for (i = 0; i < bytes; i++) + { + ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_DATA + i, + msg->cm_data[i]); + } + + /* Set TX ready */ + + regval = CTUCANFD_TXCMD_TXCR + (1 << (CTUCANFD_TXCMD_TXB_SHIFT + txidx)); + ctucanfd_putreg(priv, CTUCANFD_TXINFOCMD, regval); + + return OK; +} + +/***************************************************************************** + * Name: ctucanfd_chrdev_txready + * + * Description: + * Return true if the CAN hardware can accept another TX message. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * True if the CAN hardware is ready to accept another TX message. + * + *****************************************************************************/ + +static bool ctucanfd_chrdev_txready(FAR struct can_dev_s *dev) +{ + FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev; + + return ctucanfd_txready(priv); +} + +/***************************************************************************** + * Name: ctucanfd_chrdev_txempty + * + * Description: + * Return true if all message have been sent. If for example, the CAN + * hardware implements FIFOs, then this would mean the transmit FIFO is + * empty. This method is called when the driver needs to make sure that + * all characters are "drained" from the TX hardware before calling + * co_shutdown(). + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * True if there are no pending TX transfers in the CAN hardware. + * + *****************************************************************************/ + +static bool ctucanfd_chrdev_txempty(FAR struct can_dev_s *dev) +{ + FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev; + uint32_t regval = 0; + bool ret = true; + int i; + + regval = ctucanfd_getreg(priv, CTUCANFD_TXSTAT); + + for (i = 0; i < priv->txbufcnt; i++) + { + if (CTUCANFD_TXSTAT_GET(regval, i) != CTUCANFD_TXSTAT_ETY) + { + ret = false; + break; + } + } + + return ret; +} + +/***************************************************************************** + * Name: ctucanfd_chardev_receive + * + * Description: + * Receive CAN frame + * + *****************************************************************************/ + +static void ctucanfd_chardev_receive(FAR struct ctucanfd_can_s *priv) +{ + FAR struct can_dev_s *dev = (FAR struct can_dev_s *)priv; + FAR uint32_t *ptr = NULL; + struct ctucanfd_frame_s frame; + struct can_hdr_s hdr; + int i = 0; + int ret = 0; + uint16_t frc = 0; + uint32_t regval = 0; + + /* Get frame count */ + + regval = ctucanfd_getreg(priv, CTUCANFD_RXSETSTAT); + frc = (regval & CTUCANFD_RXSTAT_RXFRC_MASK) >> CTUCANFD_RXSTAT_RXFRC_SHIFT; + + /* Read frames */ + + while (frc-- > 0) + { + ptr = (FAR uint32_t *)&frame; + + for (i = 0; i < sizeof(struct ctucanfd_frame_s) / 4; i++) + { + /* RX buffer in automatic mode */ + + ptr[i] = ctucanfd_getreg(priv, CTUCANFD_RXDATA); + } + + /* Get the DLC */ + + hdr.ch_dlc = frame.fmt.dlc; + + /* Get RTR bit */ + + hdr.ch_rtr = frame.fmt.rtr; + +#ifdef CONFIG_CAN_EXTID + /* Get the CAN identifier. */ + + hdr.ch_extid = frame.fmt.ide; + + if (hdr.ch_extid) + { + hdr.ch_id = frame.id.id_ext; + } + else + { + hdr.ch_id = frame.id.id; + } +#else + if (frame.fmt.ide) + { + canerr("ERROR: Received message with extended" + " identifier. Dropped\n"); + + continue; + } + + hdr.ch_id = frame.id.id; +#endif + + /* Clear the error indication and unused bits */ + +#ifdef CONFIG_CAN_ERRORS + hdr.ch_error = 0; +#endif + hdr.ch_tcf = 0; + +#ifdef CONFIG_CAN_FD + hdr.ch_esi = frame.fmt.esi_rsv; + hdr.ch_edl = 0; + hdr.ch_brs = frame.fmt.brs; +#else + if (frame.fmt.fdf) + { + /* Drop any FD CAN messages if not supported */ + + canerr("ERROR: Received CAN FD message. Dropped\n"); + + return; + } +#endif + + /* Provide the data to the upper half driver */ + + ret = can_receive(dev, &hdr, (FAR uint8_t *)&frame.data); + if (ret < 0) + { + canerr("ERROR: can_receive failed %d\n", ret); + } + } +} + +#ifdef CONFIG_CAN_ERRORS +/***************************************************************************** + * Name: ctucanfd_chardev_error + *****************************************************************************/ + +static void ctucanfd_chardev_error(FAR struct ctucanfd_can_s *priv, + uint32_t stat) +{ + struct can_hdr_s hdr; + uint16_t errbits = 0; + uint8_t data[CAN_ERROR_DLC]; + int ret; + + memset(data, 0, sizeof(data)); + + /* Data overrun interrupt */ + + if (stat & CTUCANFD_INT_DOI) + { + data[1] |= CAN_ERROR1_RXOVERFLOW; + errbits |= CAN_ERROR_CONTROLLER; + + /* Clear data overrun */ + + ctucanfd_putreg(priv, CTUCANFD_CMD, + CTUCANFD_CMD_RRB | CTUCANFD_CMD_CDO); + } + + /* Arbitration lost interrupt */ + + if (stat & CTUCANFD_INT_ALI) + { + errbits |= CAN_ERROR_LOSTARB; + data[0] = CAN_ERROR0_UNSPEC; + } + + /* BUS error interrupt */ + + if (stat & CTUCANFD_INT_BEI) + { + errbits |= CAN_ERROR_BUSERROR; + } + + /* Report a CAN error */ + + if (errbits != 0) + { + canerr("ERROR: errbits = %08" PRIx16 "\n", errbits); + + /* Format the CAN header for the error report. */ + + hdr.ch_id = errbits; + hdr.ch_dlc = CAN_ERROR_DLC; + hdr.ch_rtr = 0; + hdr.ch_error = 1; +#ifdef CONFIG_CAN_EXTID + hdr.ch_extid = 0; +#endif + hdr.ch_tcf = 0; + + /* And provide the error report to the upper half logic */ + + ret = can_receive(&priv->dev, &hdr, data); + if (ret < 0) + { + canerr("ERROR: can_receive failed: %d\n", ret); + } + } +} +#endif + +/***************************************************************************** + * Name: ctucanfd_chardev_interrupt + *****************************************************************************/ + +static void ctucanfd_chardev_interrupt(FAR struct ctucanfd_driver_s *priv) +{ + uint32_t stat = 0; + uint32_t regval = 0; + int i = 0; + int txidx = 0; + + for (i = 0; i < priv->count; i++) + { + stat = ctucanfd_getreg(&priv->devs[i], CTUCANFD_INTSTAT); + if (stat == 0) + { + continue; + } + + /* Frame received */ + + if (stat & CTUCANFD_INT_RXI) + { + ctucanfd_chardev_receive(&priv->devs[i]); + } + + /* Frame transmitted */ + + if (stat & CTUCANFD_INT_TXI) + { + regval = ctucanfd_getreg(&priv->devs[i], CTUCANFD_TXSTAT); + + for (txidx = 0; txidx < priv->devs[i].txbufcnt; txidx++) + { + if (CTUCANFD_TXSTAT_GET(regval, txidx) == + CTUCANFD_TXSTAT_TOK) + { + can_txdone(&priv->devs[i].dev); + + /* Mark buffer as empty */ + + regval = (CTUCANFD_TXCMD_TXCE + + (1 << (CTUCANFD_TXCMD_TXB_SHIFT + txidx))); + ctucanfd_putreg(&priv->devs[i], CTUCANFD_TXINFOCMD, regval); + } + } + } + +#ifdef CONFIG_CAN_ERRORS + /* Handle errors */ + + if (stat & CTUCANFD_INT_ERR) + { + ctucanfd_chardev_error(&priv->devs[i], stat); + } +#endif + + /* Clear interrupts */ + + ctucanfd_putreg(&priv->devs[i], CTUCANFD_INTSTAT, stat); + + /* Re-enable RX interrupts */ + + ctucanfd_rxint(&priv->devs[i], true); + } +} +#endif /* CONFIG_CAN_CTUCANFD_CHARDEV */ + +#ifdef CONFIG_CAN_CTUCANFD_SOCKET +/***************************************************************************** + * Name: ctucanfd_sock_ifup + * + * Description: + * NuttX Callback: Bring up the Ethernet interface when an IP address is + * provided + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + *****************************************************************************/ + +static int ctucanfd_sock_ifup(FAR struct net_driver_s *dev) +{ + FAR struct ctucanfd_can_s *priv = + (FAR struct ctucanfd_can_s *)dev->d_private; + + priv->dev.d_buf = (FAR uint8_t *)priv->tx_pool; + + ctucanfd_reset(priv); + ctucanfd_setup(priv); + + ctucanfd_txint(priv, true); + ctucanfd_rxint(priv, true); + + return OK; +} + +/***************************************************************************** + * Name: ctucanfd_sock_ifdown + * + * Description: + * NuttX Callback: Stop the interface. + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + *****************************************************************************/ + +static int ctucanfd_sock_ifdown(FAR struct net_driver_s *dev) +{ + FAR struct ctucanfd_can_s *priv = + (FAR struct ctucanfd_can_s *)dev->d_private; + + ctucanfd_txint(priv, false); + ctucanfd_rxint(priv, false); + + /* Shutdown */ + + ctucanfd_shutdown(priv); + + return OK; +} + +/***************************************************************************** + * Name: ctucanfd_sock_txavail_work + * + * Description: + * Perform an out-of-cycle poll on the worker thread. + * + * Input Parameters: + * arg - Reference to the NuttX driver state structure (cast to void*) + * + * Returned Value: + * None + * + * Assumptions: + * Called on the higher priority worker thread. + * + *****************************************************************************/ + +static void ctucanfd_sock_txavail_work(FAR void *arg) +{ + FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)arg; + + /* Ignore the notification if the interface is not yet up */ + + net_lock(); + if (IFF_IS_UP(priv->dev.d_flags)) + { + /* Check if there is room in the hardware to hold another outgoing + * packet. + */ + + if (ctucanfd_txready(priv)) + { + /* No, there is space for another transfer. Poll the network for + * new XMIT data. + */ + + devif_poll(&priv->dev, ctucanfd_sock_txpoll); + } + } + + net_unlock(); +} + +/***************************************************************************** + * Name: ctucanfd_sock_txavail + * + * Description: + * Driver callback invoked when new TX data is available. This is a + * stimulus perform an out-of-cycle poll and, thereby, reduce the TX + * latency. + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Called in normal user mode + * + *****************************************************************************/ + +static int ctucanfd_sock_txavail(FAR struct net_driver_s *dev) +{ + FAR struct ctucanfd_can_s *priv = + (FAR struct ctucanfd_can_s *)dev->d_private; + + /* Is our single work structure available? It may not be if there are + * pending interrupt actions and we will have to ignore the Tx + * availability action. + */ + + if (work_available(&priv->pollwork)) + { + /* Schedule to serialize the poll on the worker thread. */ + + ctucanfd_sock_txavail_work(priv); + } + + return OK; +} + +# ifdef CONFIG_NETDEV_IOCTL +/***************************************************************************** + * Name: ctucanfd_sock_ioctl + * + * Description: + * PHY ioctl command handler + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * cmd - ioctl command + * arg - Argument accompanying the command + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + * Assumptions: + * + *****************************************************************************/ + +static int ctucanfd_sock_ioctl(FAR struct net_driver_s *dev, int cmd, + unsigned long arg) +{ + return -ENOTTY; +} + +# endif /* CONFIG_NETDEV_IOCTL */ + +/***************************************************************************** + * Name: ctucanfd_sock_txpoll + * + * Description: + * The transmitter is available, check if the network has any outgoing + * packets ready to send. This is a callback from devif_poll(). + * devif_poll() may be called: + * + * 1. When the preceding TX packet send is complete, + * 2. When the preceding TX packet send timesout and the interface is reset + * 3. During normal TX polling + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * OK on success; a negated errno on failure + * + * Assumptions: + * May or may not be called from an interrupt handler. In either case, + * global interrupts are disabled, either explicitly or indirectly through + * interrupt handling logic. + * + *****************************************************************************/ + +static int ctucanfd_sock_txpoll(FAR struct net_driver_s *dev) +{ + FAR struct ctucanfd_can_s *priv = + (FAR struct ctucanfd_can_s *)dev->d_private; + + /* If the polling resulted in data that should be sent out on the network, + * the field d_len is set to a value > 0. + */ + + if (priv->dev.d_len > 0) + { + /* Send the packet */ + + ctucanfd_sock_send(priv); + + /* Check if there is room in the device to hold another packet. If + * not, return a non-zero value to terminate the poll. + */ + + if (!ctucanfd_txready(priv)) + { + return -EBUSY; + } + } + + /* If zero is returned, the polling will continue until all connections + * have been examined. + */ + + return 0; +} + +/***************************************************************************** + * Name: ctucanfd_sock_send + *****************************************************************************/ + +static int ctucanfd_sock_send(FAR struct ctucanfd_can_s *priv) +{ + union ctucanfd_frame_fmt_u fmt; + union ctucanfd_frame_id_u id; + uint32_t regval; + uint32_t offset; + int txidx; + int i; + + /* Get TX empty buffer */ + + regval = ctucanfd_getreg(priv, CTUCANFD_TXSTAT); + + offset = 0; + for (i = 0; i < priv->txbufcnt; i++) + { + if (CTUCANFD_TXSTAT_GET(regval, i) == CTUCANFD_TXSTAT_ETY) + { + offset = CTUCANFD_TXT1 + CTUCANFD_TXT_SIZE * i; + txidx = i; + break; + } + } + + if (offset == 0) + { + canerr("ERROR: TX buffer not available\n"); + return -EBUSY; + } + + /* Reset data */ + + fmt.u32 = 0; + id.u32 = 0; + + /* CAN 2.0 or CAN FD */ + + if (priv->dev.d_len == sizeof(struct can_frame)) + { + FAR struct can_frame *frame = frame = + (FAR struct can_frame *)priv->dev.d_buf; + + /* Set up the DLC */ + + fmt.s.dlc = frame->can_dlc; + + /* Set RTR bit */ + + fmt.s.rtr = (frame->can_id & CAN_RTR_FLAG); + +#ifdef CONFIG_NET_CAN_EXTID + if (frame->can_id & CAN_EFF_FLAG) + { + fmt.s.ide = 1; + id.s.id_ext = (frame->can_id & 0x1ffffff); + } + else +#endif + { + fmt.s.ide = 0; + id.s.id = (frame->can_id & 0x07ff); + } + + /* Write frame */ + + ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_FMT, fmt.u32); + ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_ID, id.u32); + ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_TSL, 0); + ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_TSU, 0); + + for (i = 0; i < frame->can_dlc; i++) + { + ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_DATA + i, + frame->data[i]); + } + } +#ifdef CONFIG_NET_CAN_CANFD + else /* CAN FD frame */ + { + FAR struct canfd_frame *frame = + (FAR struct canfd_frame *)priv->dev.d_buf; + + /* CAN FD frame */ + + fmt.s.fdf = 1; + + /* Set up the DLC */ + + fmt.s.dlc = can_bytes2dlc(frame->len); + + /* Set flags */ + + fmt.s.rtr = (frame->can_id & CAN_RTR_FLAG); + fmt.s.brs = (frame->flags & CANFD_BRS); + fmt.s.esi_rsv = (frame->flags & CANFD_ESI); + +#ifdef CONFIG_NET_CAN_EXTID + if (frame->can_id & CAN_EFF_FLAG) + { + fmt.s.ide = 1; + id.s.id_ext = (frame->can_id & 0x1ffffff); + } + else +#endif + { + fmt.s.ide = 0; + id.s.id = (frame->can_id & 0x07ff); + } + + /* Write frame */ + + ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_FMT, fmt.u32); + ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_ID, id.u32); + ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_TSL, 0); + ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_TSU, 0); + + for (i = 0; i < frame->len; i++) + { + ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_DATA + i, + frame->data[i]); + } + } +#endif + + /* Set TX ready */ + + regval = CTUCANFD_TXCMD_TXCR + (1 << (CTUCANFD_TXCMD_TXB_SHIFT + txidx)); + ctucanfd_putreg(priv, CTUCANFD_TXINFOCMD, regval); + + return OK; +} + +/***************************************************************************** + * Name: ctucanfd_sock_receive + *****************************************************************************/ + +static void ctucanfd_sock_receive(FAR struct ctucanfd_can_s *priv) +{ + struct ctucanfd_frame_s rxframe; + FAR uint32_t *ptr = NULL; + int i = 0; + uint16_t frc = 0; + uint32_t regval = 0; + uint8_t bytes; + + /* Get frame count */ + + regval = ctucanfd_getreg(priv, CTUCANFD_RXSETSTAT); + frc = (regval & CTUCANFD_RXSTAT_RXFRC_MASK) >> CTUCANFD_RXSTAT_RXFRC_SHIFT; + + /* Read frames */ + + while (frc-- > 0) + { + ptr = (FAR uint32_t *)&rxframe; + + for (i = 0; i < sizeof(struct ctucanfd_frame_s) / 4; i++) + { + /* RX buffer in automatic mode */ + + ptr[i] = ctucanfd_getreg(priv, CTUCANFD_RXDATA); + } + + /* CAN 2.0 or CAN FD */ + +#ifdef CONFIG_NET_CAN_CANFD + if (rxframe.fmt.fdf) + { + struct canfd_frame *frame = (struct canfd_frame *)priv->rx_pool; + + /* Get the DLC */ + + frame->len = can_dlc2bytes(rxframe.fmt.dlc); + +#ifdef CONFIG_NET_CAN_EXTID + /* Get the CAN identifier. */ + + if (rxframe.fmt.ide) + { + frame->can_id = rxframe.id.id_ext; + frame->can_id |= CAN_EFF_FLAG; + } + else + { + frame->can_id = rxframe.id.id; + } +#else + if (rxframe.fmt.ide) + { + canerr("ERROR: Received message with extended" + " identifier. Dropped\n"); + continue; + } + + frame->can_id = rxframe.id.id; +#endif + + /* Extract the RTR bit */ + + if (rxframe.fmt.rtr) + { + frame->can_id |= CAN_RTR_FLAG; + } + + /* Get CANFD flags */ + + frame->flags = 0; + + if (rxframe.fmt.esi_rsv) + { + frame->flags |= CANFD_ESI; + } + + if (rxframe.fmt.brs) + { + frame->flags |= CANFD_BRS; + } + + /* Get data */ + + bytes = can_dlc2bytes(rxframe.fmt.dlc); + for (i = 0; i < bytes; i++) + { + frame->data[i] = rxframe.data[i]; + } + + /* Copy the buffer pointer to priv->dev.. Set amount of data + * in priv->dev.d_len + */ + + priv->dev.d_len = sizeof(struct canfd_frame); + priv->dev.d_buf = (FAR uint8_t *)frame; + + /* Send to socket interface */ + + NETDEV_RXPACKETS(&priv->dev); + + can_input(&priv->dev); + + /* Point the packet buffer back to the next Tx buffer that will be + * used during the next write. If the write queue is full, then + * this will point at an active buffer, which must not be written + * to. This is OK because devif_poll won't be called unless the + * queue is not full. + */ + + priv->dev.d_buf = (FAR uint8_t *)priv->tx_pool; + } + else +#endif + { + FAR struct can_frame *frame = + (FAR struct can_frame *)priv->rx_pool; + + /* Get the DLC */ + + frame->can_dlc = rxframe.fmt.dlc; + +#ifdef CONFIG_NET_CAN_EXTID + /* Get the CAN identifier. */ + + if (rxframe.fmt.ide) + { + frame->can_id = rxframe.id.id_ext; + frame->can_id |= CAN_EFF_FLAG; + } + else + { + frame->can_id = rxframe.id.id; + } +#else + if (rxframe.fmt.ide) + { + canerr("ERROR: Received message with extended" + " identifier. Dropped\n"); + continue; + } + + frame->can_id = rxframe.id.id; +#endif + + /* Extract the RTR bit */ + + if (rxframe.fmt.rtr) + { + frame->can_id |= CAN_RTR_FLAG; + } + + /* Get data */ + + for (i = 0; i < rxframe.fmt.dlc; i++) + { + frame->data[i] = rxframe.data[i]; + } + + /* Copy the buffer pointer to priv->dev.. Set amount of data + * in priv->dev.d_len + */ + + priv->dev.d_len = sizeof(struct can_frame); + priv->dev.d_buf = (FAR uint8_t *)frame; + + /* Send to socket interface */ + + NETDEV_RXPACKETS(&priv->dev); + + can_input(&priv->dev); + + /* Point the packet buffer back to the next Tx buffer that will be + * used during the next write. If the write queue is full, then + * this will point at an active buffer, which must not be written + * to. This is OK because devif_poll won't be called unless the + * queue is not full. + */ + + priv->dev.d_buf = (FAR uint8_t *)priv->tx_pool; + } + } +} + +#ifdef CONFIG_NET_CAN_ERRORS +/***************************************************************************** + * Name: ctucanfd_sock_error + *****************************************************************************/ + +static void ctucanfd_sock_error(FAR struct ctucanfd_can_s *priv, + uint32_t stat) +{ + FAR struct can_frame *frame = NULL; + uint16_t errbits = 0; + + /* Data overrun interrupt */ + + if (stat & CTUCANFD_INT_DOI) + { + frame->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + errbits |= CAN_ERR_CRTL; + + /* Clear data overrun */ + + ctucanfd_putreg(priv, CTUCANFD_CMD, + (CTUCANFD_CMD_RRB | CTUCANFD_CMD_CDO)); + } + + /* Arbitration lost interrupt */ + + if (stat & CTUCANFD_INT_ALI) + { + errbits |= CAN_ERR_LOSTARB; + frame->data[0] = CAN_ERR_LOSTARB_UNSPEC; + } + + /* BUS error interrupt */ + + if (stat & CTUCANFD_INT_BEI) + { + errbits |= CAN_ERR_BUSERROR; + } + + if (errbits != 0) + { + frame = (FAR struct can_frame *)priv->rx_pool; + + canerr("ERROR: errbits = %08" PRIx16 "\n", errbits); + + /* Copy frame */ + + frame->can_id = errbits; + frame->can_dlc = CAN_ERR_DLC; + + net_lock(); + + /* Copy the buffer pointer to priv->dev.. Set amount of data + * in priv->dev.d_len + */ + + priv->dev.d_len = sizeof(struct can_frame); + priv->dev.d_buf = (FAR uint8_t *)frame; + + /* Send to socket interface */ + + NETDEV_ERRORS(&priv->dev); + + can_input(&priv->dev); + + /* Point the packet buffer back to the next Tx buffer that will be + * used during the next write. If the write queue is full, then + * this will point at an active buffer, which must not be written + * to. This is OK because devif_poll won't be called unless the + * queue is not full. + */ + + priv->dev.d_buf = (FAR uint8_t *)priv->tx_pool; + net_unlock(); + } +} +#endif + +/***************************************************************************** + * Name: ctucanfd_sock_interrupt_work + *****************************************************************************/ + +static void ctucanfd_sock_interrupt_work(FAR void *arg) +{ + FAR struct ctucanfd_driver_s *priv = arg; + uint32_t stat = 0; + uint32_t regval = 0; + int i = 0; + int txidx = 0; + + for (i = 0; i < priv->count; i++) + { + stat = ctucanfd_getreg(&priv->devs[i], CTUCANFD_INTSTAT); + if (stat == 0) + { + continue; + } + + /* RX buffer not empty interrupt */ + + if (stat & CTUCANFD_INT_RBNEI) + { + ctucanfd_sock_receive(&priv->devs[i]); + } + + /* Transmit interrupt */ + + if (stat & CTUCANFD_INT_TXI) + { + regval = ctucanfd_getreg(&priv->devs[i], CTUCANFD_TXSTAT); + + for (txidx = 0; txidx < priv->devs[i].txbufcnt; txidx++) + { + if (CTUCANFD_TXSTAT_GET(regval, txidx) == + CTUCANFD_TXSTAT_TOK) + { + NETDEV_TXDONE(&priv->devs[i].dev); + + /* There should be space for a new TX in any event. + * Poll the network for new XMIT data. + */ + + net_lock(); + devif_poll(&priv->devs[i].dev, ctucanfd_sock_txpoll); + net_unlock(); + + /* Mark buffer as empty */ + + regval = (CTUCANFD_TXCMD_TXCE + + (1 << (CTUCANFD_TXCMD_TXB_SHIFT + txidx))); + ctucanfd_putreg(&priv->devs[i], CTUCANFD_TXINFOCMD, regval); + } + } + } + +#ifdef CONFIG_NET_CAN_ERRORS + /* Handle errors */ + + if (stat & CTUCANFD_INT_ERR) + { + ctucanfd_sock_error(&priv->devs[i], stat); + } +#endif + + /* Clear interrupts */ + + ctucanfd_putreg(&priv->devs[i], CTUCANFD_INTSTAT, stat); + + /* Re-enable RX interrupts */ + + ctucanfd_rxint(&priv->devs[i], true); + } +} +#endif /* CONFIG_CAN_CTUCANFD_SOCKET */ + +/***************************************************************************** + * Name: ctucanfd_interrupt + * + * Description: + * Initialize CTUCANFD device + * + *****************************************************************************/ + +static int ctucanfd_interrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct ctucanfd_driver_s *priv = (FAR struct ctucanfd_driver_s *)arg; + uint32_t regval = 0; + int i = 0; + + DEBUGASSERT(priv != NULL); + + for (i = 0; i < priv->count; i++) + { + regval |= ctucanfd_getreg(&priv->devs[i], CTUCANFD_INTSTAT); + + /* Break now if interrupt is pending */ + + if (regval != 0) + { + /* Disable RX interrupts until we handle all interrups */ + + ctucanfd_rxint(&priv->devs[i], false); + + break; + } + } + + /* Check for pending interrupts for this card */ + + if (regval == 0) + { + return OK; + } + +#ifdef CONFIG_CAN_CTUCANFD_CHARDEV + /* Handle character device interrupt */ + + ctucanfd_chardev_interrupt(priv); +#endif + +#ifdef CONFIG_CAN_CTUCANFD_SOCKET + /* Derefer SocketCAN interrupt to work queue */ + + work_queue(CTUCANFD_CANWORK, &priv->irqwork, + ctucanfd_sock_interrupt_work, priv, 0); +#endif + + return OK; +} + +/***************************************************************************** + * Name: ctucanfd_init + * + * Description: + * Initialize CTUCANFD device + * + *****************************************************************************/ + +static void ctucanfd_init(FAR struct ctucanfd_driver_s *priv) +{ + /* REVISIT: Only legacy IRQ supported in QEMU driver */ + + priv->irq = pci_get_irq(priv->pcidev); + irq_attach(priv->irq, ctucanfd_interrupt, priv); + + /* REVISIT: Enable card interrupts (not implemented in QEMU) */ + + /* Enable interrupts */ + + up_enable_irq(priv->irq); +} + +/***************************************************************************** + * Name: ctucanfd_ctucanfd_proble + * + * Description: + * Proble CTUCANFD devices on board and return the number of vailalbe chips. + * + *****************************************************************************/ + +static uint8_t ctucanfd_ctucanfd_probe(FAR struct ctucanfd_driver_s *priv) +{ + uint32_t offset; + uint8_t regval; + + offset = priv->bar0_base + CTUCANFD_BAR0_ID; + pci_read_io_byte(priv->pcidev, offset, ®val); + + /* REVISIT: what is the maximum number of channels ? */ + + return regval; +} + +/***************************************************************************** + * Name: ctucanfd_probe + * + * Description: + * Probe device + * + *****************************************************************************/ + +static int ctucanfd_probe(FAR struct pci_device_s *dev) +{ + FAR struct ctucanfd_driver_s *priv = NULL; + uint8_t i = 0; + int ret; +#ifdef CONFIG_CAN_CTUCANFD_CHARDEV + uint8_t count; + char devpath[PATH_MAX]; +#endif + + /* Allocate the interface structure */ + + priv = kmm_zalloc(sizeof(*priv)); + if (priv == NULL) + { + ret = -ENOMEM; + return ret; + } + + /* Initialzie PCI dev */ + + priv->pcidev = dev; + + pci_set_master(dev); + pciinfo("Enabled bus mastering\n"); + pci_enable_device(dev); + pciinfo("Enabled memory resources\n"); + + /* Control register BAR */ + + priv->bar0_base = (uintptr_t)pci_map_bar(dev, CTUCANFD_BAR0); + if (!priv->bar0_base) + { + pcierr("Not found BAR0\n"); + goto errout; + } + + /* MEM access only supported */ + + if (pci_resource_flags(dev, CTUCANFD_BAR0) != PCI_RESOURCE_MEM) + { + ret = -ENOTSUP; + goto errout; + } + + priv->canfd_base = (uintptr_t)pci_map_bar(dev, CTUCANFD_CTUCAN_BAR1); + if (!priv->canfd_base) + { + pcierr("Not found CANFD bar\n"); + goto errout; + } + + /* MEM access only supported */ + + if (pci_resource_flags(dev, CTUCANFD_CTUCAN_BAR1) != PCI_RESOURCE_MEM) + { + ret = -ENOTSUP; + goto errout; + } + + /* Get number of CTUCANFD chips */ + + priv->count = ctucanfd_ctucanfd_probe(priv); + + pciinfo("detected %d CTUCANFD channels\n", priv->count); + + /* Allocate CTUCANFD devices */ + + priv->devs = kmm_zalloc(sizeof(struct ctucanfd_can_s) * priv->count); + if (priv->devs == NULL) + { + ret = -ENOMEM; + goto errout; + } + + /* Common initialziation for all channels */ + + ctucanfd_init(priv); + + /* Handle all CTUCANFD devices */ + + for (i = 0; i < priv->count; i++) + { + /* Common initialization */ + + priv->devs[i].base = priv->canfd_base + (CTUCANFD_CTUCAN_REGS * i); + priv->devs[i].pcidev = dev; + priv->devs[i].txbufcnt = CTUCANFD_TXBUF_CNT; + +#ifdef CONFIG_CAN_CTUCANFD_CHARDEV + count = g_ctucanfd_count++; + + /* Get devpath for this CTUCANFD device */ + + snprintf(devpath, PATH_MAX, "/dev/can%d", count); + + /* Initialize CTUCANFD channel */ + + priv->devs[i].dev.cd_ops = &g_ctucanfd_can_ops; + priv->devs[i].dev.cd_priv = &priv->devs[i]; + + /* Register CAN device */ + + ret = can_register(devpath, &priv->devs[i].dev); + if (ret < 0) + { + pcierr("ERROR: failed to register count=%d, %d\n", i, ret); + goto errout; + } +#endif + +#ifdef CONFIG_CAN_CTUCANFD_SOCKET + /* Initialize the driver structure */ + + priv->devs[i].dev.d_ifup = ctucanfd_sock_ifup; + priv->devs[i].dev.d_ifdown = ctucanfd_sock_ifdown; + priv->devs[i].dev.d_txavail = ctucanfd_sock_txavail; +# ifdef CONFIG_NETDEV_IOCTL + priv->devs[i].dev.d_ioctl = ctucanfd_sock_ioctl; +# endif + priv->devs[i].dev.d_private = &priv->devs[i]; + + /* Put the interface in the down state. This usually amounts to + * resetting the device and/or calling ctucanfd_sock_ifdown(). + */ + + ctucanfd_sock_ifdown(&priv->devs[i].dev); + + /* Register the device with the OS so that socket IOCTLs can be + * performed + */ + + ret = netdev_register(&priv->devs[i].dev, NET_LL_CAN); + if (ret < 0) + { + pcierr("ERROR: failed to register count=%d, %d\n", i, ret); + goto errout; + } +#endif + } + + return OK; + +errout: + for (i = 0; i < priv->count; i++) + { + if (priv->devs[i].pcidev) + { +#ifdef CONFIG_CAN_KVASER_SOCKET + netdev_unregister(&priv->devs[i].dev); +#endif + +#ifdef CONFIG_CAN_KVASER_CHARDEV + unregister_driver(devpath); +#endif + } + } + + kmm_free(priv->devs); + kmm_free(priv); + + return ret; +} + +/***************************************************************************** + * Public Functions + *****************************************************************************/ + +/***************************************************************************** + * Name: pci_ctucanfd_init + * + * Description: + * Register a pci driver + * + *****************************************************************************/ + +int pci_ctucanfd_init(void) +{ + return pci_register_driver(&g_ctucanfd_drv); +} diff --git a/drivers/pci/pci_drivers.c b/drivers/pci/pci_drivers.c index 74fdc2297d2ce..bc33144e493d8 100644 --- a/drivers/pci/pci_drivers.c +++ b/drivers/pci/pci_drivers.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "pci_drivers.h" @@ -171,6 +172,16 @@ int pci_register_drivers(void) } #endif + /* Initialzie CTU CAN FD pci driver */ + +#ifdef CONFIG_CAN_CTUCANFD + ret = pci_ctucanfd_init(); + if (ret < 0) + { + pcierr("pci_ctucanfd_init failed, ret=%d\n", ret); + } +#endif + ret = pci_dev_register(); if (ret < 0) { diff --git a/include/nuttx/can/ctucanfd_pci.h b/include/nuttx/can/ctucanfd_pci.h new file mode 100644 index 0000000000000..a24fa82d5f8ad --- /dev/null +++ b/include/nuttx/can/ctucanfd_pci.h @@ -0,0 +1,59 @@ +/**************************************************************************** + * include/nuttx/can/ctucanfd_pci.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_CAN_CTUCANFD_H +#define __INCLUDE_NUTTX_CAN_CTUCANFD_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: pci_ctucanfd_init + * + * Description: + * Register a pci driver + * + ****************************************************************************/ + +int pci_ctucanfd_init(void); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_CAN_CTUCANFD_H */ From 661eb313e178d917b2e6ae4556f8947c9742596b Mon Sep 17 00:00:00 2001 From: p-szafonimateusz Date: Wed, 11 Dec 2024 12:08:05 +0100 Subject: [PATCH 3/3] Documentation: add PCI CAN cards doc Documentation: add PCI CAN cards doc Signed-off-by: p-szafonimateusz --- .../components/drivers/special/pci/index.rst | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/Documentation/components/drivers/special/pci/index.rst b/Documentation/components/drivers/special/pci/index.rst index 04185b31c577e..1c598a910eecc 100644 --- a/Documentation/components/drivers/special/pci/index.rst +++ b/Documentation/components/drivers/special/pci/index.rst @@ -59,3 +59,50 @@ Supported devices: - Intel I225LM - Intel I226V + +Kvaser PCI CAN card +------------------- + +At the moment the card only works with QEMU. + +The driver supports both SocketCAN interface and character driver. + +The driver requires, ``vcan`` to run on the host: + +.. code:: shell + + sudo ip link add dev can0 type vcan + sudo ip link set can0 up + +An example command to run the driver on ``x86_64`` looks like this: + +.. code:: shell + + qemu-system-x86_64 -m 2G -cpu host -enable-kvm -kernel nuttx \ + -nographic -serial mon:stdio -object can-bus,id=canbus0 \ + -object can-host-socketcan,id=canhost0,if=can0,canbus=canbus0 \ + -device kvaser_pci,canbus=canbus0 + + +CTUCANFD PCI CAN card +--------------------- + +At the moment the card only works with QEMU. + +The driver supports both SocketCAN interface and character driver. + +The driver requires, ``vcan`` to run on the host: + +.. code:: shell + + sudo ip link add dev can0 type vcan + sudo ip link set can0 up + +An example command to run the driver on ``x86_64`` looks like this: + +.. code:: shell + + qemu-system-x86_64 -m 2G -cpu host -enable-kvm -kernel nuttx \ + -nographic -serial mon:stdio -object can-bus,id=canbus0-bus \ + -object can-host-socketcan,if=can0,canbus=canbus0-bus,id=canbus0-socketcan \ + -device ctucan_pci,canbus0=canbus0-bus,canbus1=canbus0-bus