From 033f203e2ac35c0ab4de466b80a4f6982ced7f13 Mon Sep 17 00:00:00 2001 From: p-szafonimateusz Date: Thu, 24 Oct 2024 13:25:47 +0200 Subject: [PATCH] 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 | 18 +- 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, 1952 insertions(+), 10 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 2b60bc044b..b8d33cef3b 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) - - if(CONFIG_CAN_MCP2515) - list(APPEND SRCS mcp2515.c) - endif() - - target_sources(drivers PRIVATE ${SRCS}) endif() + +if(CONFIG_CAN_MCP2515) + list(APPEND SRCS mcp2515.c) +endif() + +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 9e321b8218..45d658ad16 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 d093a19955..741b570172 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 0000000000..bf19a79177 --- /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 80895020bc..559e82a537 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 67ade5b515..74fdc2297d 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 0000000000..2c8e41d236 --- /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 */