You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2022/04/15 14:55:34 UTC
[incubator-nuttx] 02/05: arch/riscv/esp32c3: ESP32C3 TWAI (CAN) controller driver.
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit e28584131dc5b3d39fd9c103eb7e2615da275c40
Author: Jan Charvat <ja...@gmail.com>
AuthorDate: Wed Apr 6 12:10:45 2022 +0200
arch/riscv/esp32c3: ESP32C3 TWAI (CAN) controller driver.
Signed-off-by: Jan Charvat <ja...@gmail.com>
---
arch/risc-v/src/esp32c3/esp32c3_twai.c | 1252 ++++++++++++++++++++++++++++++++
arch/risc-v/src/esp32c3/esp32c3_twai.h | 77 ++
2 files changed, 1329 insertions(+)
diff --git a/arch/risc-v/src/esp32c3/esp32c3_twai.c b/arch/risc-v/src/esp32c3/esp32c3_twai.c
new file mode 100644
index 0000000000..585befe377
--- /dev/null
+++ b/arch/risc-v/src/esp32c3/esp32c3_twai.c
@@ -0,0 +1,1252 @@
+/****************************************************************************
+ * arch/risc-v/src/esp32c3/esp32c3_twai.c
+ *
+ * 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 <nuttx/config.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <arch/board/board.h>
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/can/can.h>
+
+#include "riscv_internal.h"
+
+#include "esp32c3_gpio.h"
+#include "esp32c3_twai.h"
+#include "esp32c3_irq.h"
+#include "esp32c3_clockconfig.h"
+
+#include "hardware/esp32c3_system.h"
+#include "hardware/esp32c3_gpio_sigmap.h"
+
+#if defined(CONFIG_ESP32C3_TWAI)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* Default values written to various registers on initialization */
+#define TWAI_INIT_TEC 0
+#define TWAI_INIT_REC 0
+#define TWAI_INIT_EWL 96
+
+#define TWAI_DEFAULT_INTERRUPTS 0xe7 /* Exclude data overrun (bit[3]) and brp_div (bit[4]) */
+
+#define TWAI_ACCEPTANCE_CODE 0x0 /* 32-bit address to match */
+#define TWAI_ACCEPTANCE_MASK 0xffffffff /* 32-bit address mask */
+
+#ifdef CONFIG_ESP32C3_TWAI0
+
+/* A TWAI bit rate must be provided */
+
+# ifndef CONFIG_ESP32C3_TWAI0_BITRATE
+# error "CONFIG_ESP32C3_TWAI0_BITRATE is not defined"
+# endif
+
+/* If no sample point is provided, use a sample point of 80 */
+
+# ifndef CONFIG_ESP32C3_TWAI0_SAMPLEP
+# define CONFIG_ESP32C3_TWAI0_SAMPLEP 80
+# endif
+#endif
+
+/* If no Synchronization Jump Width is provided, use a SJW of 3 */
+
+#ifndef CONFIG_ESP32C3_TWAI0_SJW
+# define CONFIG_ESP32C3_TWAI0_SJW 3
+#endif
+
+/* Debug ********************************************************************/
+
+/* Non-standard debug that may be enabled just for testing TWAI */
+
+#ifndef CONFIG_DEBUG_CAN_INFO
+# undef CONFIG_ESP32C3_TWAI_REGDEBUG
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* CAN hardware-dependent bit-timing constant
+ * Used for calculating and checking bit-timing parameters
+ */
+
+struct can_bittiming_const
+{
+ uint32_t tseg1_min; /* Time segment 1 */
+ uint32_t tseg1_max;
+ uint32_t tseg2_min; /* Time segment 2 */
+ uint32_t tseg2_max;
+ uint32_t sjw_min; /* Synchronization jump width */
+ uint32_t sjw_max;
+ uint32_t brp_min; /* Bit-rate prescaler */
+ uint32_t brp_max;
+ uint32_t brp_inc;
+};
+
+struct twai_dev_s
+{
+ /* Device configuration */
+
+ const struct can_bittiming_const *bittiming_const;
+ uint8_t port; /* TWAI port number */
+ uint8_t periph; /* Peripheral ID */
+ uint8_t irq; /* IRQ associated with this TWAI */
+ int8_t cpuint; /* CPU interrupt assigned to this TWAI */
+ uint32_t bitrate; /* Configured bit rate */
+ uint32_t samplep; /* Configured sample point */
+ uint32_t sjw; /* Synchronization jump width */
+ uint32_t base; /* TWAI register base address */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* TWAI Register access */
+
+#ifdef CONFIG_ESP32C3_TWAI_REGDEBUG
+static void twai_printreg(uint32_t addr, uint32_t value);
+#endif
+
+static uint32_t twai_getreg(uint32_t addr);
+static void twai_putreg(uint32_t addr, uint32_t value);
+
+/* TWAI methods */
+
+static void esp32c3twai_reset(struct can_dev_s *dev);
+static int esp32c3twai_setup(struct can_dev_s *dev);
+static void esp32c3twai_shutdown(struct can_dev_s *dev);
+static void esp32c3twai_rxint(struct can_dev_s *dev, bool enable);
+static void esp32c3twai_txint(struct can_dev_s *dev, bool enable);
+static int esp32c3twai_ioctl(struct can_dev_s *dev, int cmd,
+ unsigned long arg);
+static int esp32c3twai_remoterequest(struct can_dev_s *dev,
+ uint16_t id);
+static int esp32c3twai_send(struct can_dev_s *dev,
+ struct can_msg_s *msg);
+static bool esp32c3twai_txready(struct can_dev_s *dev);
+static bool esp32c3twai_txempty(struct can_dev_s *dev);
+
+/* TWAI interrupts */
+
+static int esp32c3twai_interrupt(int irq, void *context, void *arg);
+
+/* TWAI acceptance filter */
+
+static void esp32c3twai_set_acc_filter(uint32_t code, uint32_t mask,
+ bool single_filter);
+
+/* TWAI bit-timing initialization */
+
+static int twai_baud_rate(struct twai_dev_s *priv, int rate, int clock,
+ int sjw, int sampl_pt, int flags);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct can_bittiming_const esp32c3_twai_bittiming_const =
+{
+ .tseg1_min = 1,
+ .tseg1_max = 16,
+ .tseg2_min = 1,
+ .tseg2_max = 8,
+ .sjw_min = 1,
+ .sjw_max = 3,
+ .brp_min = 1,
+ .brp_max = 64,
+ .brp_inc = 1,
+};
+
+static const struct can_ops_s g_twaiops =
+{
+ .co_reset = esp32c3twai_reset,
+ .co_setup = esp32c3twai_setup,
+ .co_shutdown = esp32c3twai_shutdown,
+ .co_rxint = esp32c3twai_rxint,
+ .co_txint = esp32c3twai_txint,
+ .co_ioctl = esp32c3twai_ioctl,
+ .co_remoterequest = esp32c3twai_remoterequest,
+ .co_send = esp32c3twai_send,
+ .co_txready = esp32c3twai_txready,
+ .co_txempty = esp32c3twai_txempty,
+};
+
+#ifdef CONFIG_ESP32C3_TWAI0
+static struct twai_dev_s g_twai0priv =
+{
+ .bittiming_const = &esp32c3_twai_bittiming_const,
+ .port = 0,
+ .periph = ESP32C3_PERIPH_TWAI,
+ .irq = ESP32C3_IRQ_TWAI,
+ .cpuint = -ENOMEM,
+ .bitrate = CONFIG_ESP32C3_TWAI0_BITRATE,
+ .samplep = CONFIG_ESP32C3_TWAI0_SAMPLEP,
+ .sjw = CONFIG_ESP32C3_TWAI0_SJW,
+ .base = DR_REG_TWAI_BASE,
+};
+
+static struct can_dev_s g_twai0dev =
+{
+ .cd_ops = &g_twaiops,
+ .cd_priv = &g_twai0priv,
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: twai_printreg
+ *
+ * Description:
+ * Print the value read from a register.
+ *
+ * Input Parameters:
+ * addr - The register address
+ * value - The register value
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP32C3_TWAI_REGDEBUG
+static void twai_printreg(uint32_t addr, uint32_t value)
+{
+ static uint32_t prevaddr;
+ static uint32_t preval;
+ static uint32_t count;
+
+ /* Is this the same value that we read from the same register last time?
+ * Are we polling the register? If so, suppress some of the output.
+ */
+
+ if (addr == prevaddr && value == preval)
+ {
+ if (count == 0xffffffff || ++count > 3)
+ {
+ if (count == 4)
+ {
+ caninfo("...\n");
+ }
+
+ return;
+ }
+ }
+
+ /* No this is a new address or value */
+
+ else
+ {
+ /* Did we print "..." for the previous value? */
+
+ if (count > 3)
+ {
+ /* Yes.. then show how many times the value repeated */
+
+ caninfo("[repeats %d more times]\n", count - 3);
+ }
+
+ /* Save the new address, value, and count */
+
+ prevaddr = addr;
+ preval = value;
+ count = 1;
+ }
+
+ /* Show the register value read */
+
+ caninfo("%08" PRIx32 "<-%08" PRIx32 "\n", addr, value);
+}
+#endif
+
+/****************************************************************************
+ * Name: twai_getreg
+ *
+ * Description:
+ * Read the value of an TWAI register.
+ *
+ * Input Parameters:
+ * addr - The address to the register to read
+ *
+ * Returned Value:
+ *
+ ****************************************************************************/
+
+static uint32_t twai_getreg(uint32_t addr)
+{
+ uint32_t value;
+
+ /* Read the value from the register */
+
+ value = getreg32(addr);
+#ifdef CONFIG_ESP32C3_TWAI_REGDEBUG
+ twai_printreg(addr, value);
+#endif
+ return value;
+}
+
+/****************************************************************************
+ * Name: twai_putreg
+ *
+ * Description:
+ * Set the value of an TWAI register.
+ *
+ * Input Parameters:
+ * addr - The address to the register to write
+ * value - The value to write to the register
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void twai_putreg(uint32_t addr, uint32_t value)
+{
+#ifdef CONFIG_ESP32C3_TWAI_REGDEBUG
+ /* Show the register value being written */
+
+ caninfo("%08" PRIx32 "<-%08" PRIx32 "\n", addr, value);
+#endif
+
+ /* Write the value */
+
+ putreg32(value, addr);
+}
+
+/****************************************************************************
+ * Name: esp32c3twai_reset
+ *
+ * Description:
+ * Reset the TWAI device. Called early to initialize the hardware. This
+ * function is called, before esp32_twai_setup() and on error conditions.
+ *
+ * Input Parameters:
+ * dev - An instance of the "upper half" CAN driver state structure.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void esp32c3twai_reset(struct can_dev_s *dev)
+{
+ struct twai_dev_s *priv = (struct twai_dev_s *)dev->cd_priv;
+ irqstate_t flags;
+ int ret;
+
+ caninfo("TWAI%" PRIu8 "\n", priv->port);
+
+ flags = enter_critical_section();
+
+ /* Disable the TWAI and stop ongoing transmissions */
+
+ uint32_t mode_value = TWAI_RESET_MODE_M | TWAI_LISTEN_ONLY_MODE_M;
+ twai_putreg(TWAI_MODE_REG, mode_value); /* Enter Reset Mode */
+
+ twai_putreg(TWAI_INT_ENA_REG, 0); /* Disable interrupts */
+ twai_getreg(TWAI_STATUS_REG); /* Clear status bits */
+
+ twai_putreg(TWAI_TX_ERR_CNT_REG, TWAI_INIT_TEC); /* TEC */
+ twai_putreg(TWAI_RX_ERR_CNT_REG, TWAI_INIT_REC); /* REC */
+ twai_putreg(TWAI_ERR_WARNING_LIMIT_REG, TWAI_INIT_EWL); /* EWL */
+
+ esp32c3twai_set_acc_filter(TWAI_ACCEPTANCE_CODE,
+ TWAI_ACCEPTANCE_MASK, true);
+
+ /* Set bit timing */
+
+ ret = twai_baud_rate(priv, priv->bitrate, esp32c3_clk_apb_freq(),
+ priv->sjw, priv->samplep, 0);
+
+ if (ret != OK)
+ {
+ canerr("ERROR: Failed to set bit timing: %d\n", ret);
+ }
+
+ /* Restart the TWAI */
+
+#ifdef CONFIG_CAN_LOOPBACK
+ twai_putreg(TWAI_MODE_REG, TWAI_SELF_TEST_MODE_M); /* Leave Reset Mode, enter Test Mode */
+#else
+ twai_putreg(TWAI_MODE_REG, 0); /* Leave Reset Mode */
+#endif
+
+ /* Abort transmission, release RX buffer and clear overrun.
+ * Command register can only be modified when in Operation Mode.
+ */
+
+ twai_putreg(TWAI_CMD_REG, TWAI_ABORT_TX_M | TWAI_RELEASE_BUF_M |
+ TWAI_CLR_OVERRUN_M);
+
+ leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: esp32c3twai_setup
+ *
+ * Description:
+ * Configure the TWAI. This method is called the first time that the TWAI
+ * device is opened. This will occur when the port is first opened.
+ * This setup includes configuring and attaching TWAI interrupts.
+ *
+ * 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 esp32c3twai_setup(struct can_dev_s *dev)
+{
+ struct twai_dev_s *priv = (struct twai_dev_s *)dev->cd_priv;
+ irqstate_t flags;
+ int ret = OK;
+
+ caninfo("TWAI%" PRIu8 "\n", priv->port);
+
+ flags = enter_critical_section();
+
+ twai_putreg(TWAI_INT_ENA_REG, TWAI_DEFAULT_INTERRUPTS);
+
+ twai_getreg(TWAI_INT_RAW_REG); /* clear latched interrupts */
+
+ if (priv->cpuint != -ENOMEM)
+ {
+ /* Disable the provided CPU Interrupt to configure it. */
+
+ up_disable_irq(priv->cpuint);
+ }
+
+ priv->cpuint = esp32c3_request_irq(priv->periph,
+ ESP32C3_INT_PRIO_DEF,
+ ESP32C3_INT_LEVEL);
+ if (priv->cpuint < 0)
+ {
+ /* Failed to allocate a CPU interrupt of this type. */
+
+ ret = priv->cpuint;
+ leave_critical_section(flags);
+
+ return ret;
+ }
+
+ ret = irq_attach(priv->irq, esp32c3twai_interrupt, dev);
+ if (ret != OK)
+ {
+ /* Failed to attach IRQ, so CPU interrupt must be freed. */
+
+ esp32c3_free_cpuint(priv->periph);
+ priv->cpuint = -ENOMEM;
+ leave_critical_section(flags);
+
+ return ret;
+ }
+
+ /* Enable the CPU interrupt that is linked to the TWAI device. */
+
+ up_enable_irq(priv->cpuint);
+
+ leave_critical_section(flags);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3twai_shutdown
+ *
+ * Description:
+ * Disable the TWAI. This method is called when the TWAI 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 esp32c3twai_shutdown(struct can_dev_s *dev)
+{
+ struct twai_dev_s *priv = (struct twai_dev_s *)dev->cd_priv;
+
+#ifdef CONFIG_DEBUG_CAN_INFO
+ caninfo("shutdown TWAI%" PRIu8 "\n", priv->port);
+#endif
+
+ if (priv->cpuint != -ENOMEM)
+ {
+ /* Disable cpu interrupt */
+
+ up_disable_irq(priv->cpuint);
+
+ /* Dissociate the IRQ from the ISR */
+
+ irq_detach(priv->irq);
+
+ /* Free cpu interrupt that is attached to this peripheral */
+
+ esp32c3_free_cpuint(priv->periph);
+ priv->cpuint = -ENOMEM;
+ }
+}
+
+/****************************************************************************
+ * Name: esp32c3twai_rxint
+ *
+ * Description:
+ * Call to enable or disable RX interrupts.
+ *
+ * Input Parameters:
+ * dev - An instance of the "upper half" CAN driver state structure.
+ * enable - Enable or disable receive interrupt.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void esp32c3twai_rxint(struct can_dev_s *dev, bool enable)
+{
+ struct twai_dev_s *priv = (struct twai_dev_s *)dev->cd_priv;
+ uint32_t regval;
+ irqstate_t flags;
+
+ caninfo("TWAI%" PRIu8 " enable: %d\n", priv->port, enable);
+
+ /* The INT_ENA register is also modified from the interrupt handler,
+ * so we have to protect this code section.
+ */
+
+ flags = enter_critical_section();
+ regval = twai_getreg(TWAI_INT_ENA_REG);
+ if (enable)
+ {
+ regval |= TWAI_RX_INT_ENA_M;
+ }
+ else
+ {
+ regval &= ~TWAI_RX_INT_ENA_M;
+ }
+
+ twai_putreg(TWAI_INT_ENA_REG, regval);
+ leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: esp32c3twai_txint
+ *
+ * Description:
+ * Call to enable or disable TX interrupts.
+ *
+ * Input Parameters:
+ * dev - An instance of the "upper half" CAN driver state structure.
+ * enable - Enable or disable transmit interrupt.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void esp32c3twai_txint(struct can_dev_s *dev, bool enable)
+{
+ struct twai_dev_s *priv = (struct twai_dev_s *)dev->cd_priv;
+ uint32_t regval;
+ irqstate_t flags;
+
+ caninfo("TWAI%" PRIu8 " enable: %d\n", priv->port, enable);
+
+ /* Only disabling of the TX interrupt is supported here. The TX interrupt
+ * is automatically enabled just before a message is sent in order to avoid
+ * lost TX interrupts.
+ */
+
+ if (!enable)
+ {
+ /* TX interrupts are also disabled from the interrupt handler, so we
+ * have to protect this code section.
+ */
+
+ flags = enter_critical_section();
+
+ /* Disable all TX interrupts */
+
+ regval = twai_getreg(TWAI_INT_ENA_REG);
+ regval &= ~(TWAI_TX_INT_ENA_M);
+ twai_putreg(TWAI_INT_ENA_REG, regval);
+ leave_critical_section(flags);
+ }
+}
+
+/****************************************************************************
+ * Name: esp32c3twai_ioctl
+ *
+ * Description:
+ * All ioctl calls will be routed through this method
+ *
+ * Input Parameters:
+ * dev - An instance of the "upper half" CAN driver state structure.
+ * cmd - A ioctl command.
+ * arg - A ioctl argument.
+ *
+ * Returned Value:
+ * Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int esp32c3twai_ioctl(struct can_dev_s *dev, int cmd,
+ unsigned long arg)
+{
+ struct twai_dev_s *priv = (struct twai_dev_s *)dev->cd_priv;
+ int ret = -ENOTTY;
+
+ caninfo("TWAI%" PRIu8 " cmd=%04x arg=%lu\n", priv->port, cmd, arg);
+
+ /* Handle the command */
+
+ switch (cmd)
+ {
+ /* CANIOC_GET_BITTIMING:
+ * Description: Return the current bit timing settings
+ * Argument: A pointer to a write-able instance of struct
+ * canioc_bittiming_s in which current bit timing
+ * values will be returned.
+ * Returned Value: Zero (OK) is returned on success. Otherwise -1
+ * (ERROR) is returned with the errno variable set
+ * to indicate the nature of the error.
+ * Dependencies: None
+ */
+
+ case CANIOC_GET_BITTIMING:
+ {
+ struct canioc_bittiming_s *bt =
+ (struct canioc_bittiming_s *)arg;
+ uint32_t timing0;
+ uint32_t timing1;
+ uint32_t brp;
+
+ DEBUGASSERT(bt != NULL);
+
+ timing0 = twai_getreg(TWAI_BUS_TIMING_0_REG);
+ timing1 = twai_getreg(TWAI_BUS_TIMING_1_REG);
+
+ brp = ((timing0 & TWAI_BAUD_PRESC_M) + 1) * 2;
+ bt->bt_sjw = ((timing0 & TWAI_SYNC_JUMP_WIDTH_M) >>
+ TWAI_SYNC_JUMP_WIDTH_S) + 1;
+
+ bt->bt_tseg1 = ((timing1 & TWAI_TIME_SEG1_M) >>
+ TWAI_TIME_SEG1_S) + 1;
+ bt->bt_tseg2 = ((timing1 & TWAI_TIME_SEG2_M) >>
+ TWAI_TIME_SEG2_S)+ 1;
+ bt->bt_baud = esp32c3_clk_apb_freq() /
+ (brp * (bt->bt_tseg1 + bt->bt_tseg2 + 1));
+
+ ret = OK;
+ }
+ break;
+
+ /* Unsupported/unrecognized command */
+
+ default:
+ canerr("ERROR: Unrecognized command: %04x\n", cmd);
+ break;
+ }
+
+ return ret;
+}
+
+static int esp32c3twai_remoterequest(struct can_dev_s *dev, uint16_t id)
+{
+ canwarn("Remote request not implemented\n");
+ return -ENOSYS;
+}
+
+/****************************************************************************
+ * Name: esp32c3twai_send
+ *
+ * Description:
+ * Send one TWAI message.
+ *
+ * One TWAI-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 TWAI identifier
+ * Byte 1: Bits 5-7: Bits 0-2 of the 11-bit TWAI identifier
+ * Bit 4: Remote Transmission Request (RTR)
+ * Bits 0-3: Data Length Code (DLC)
+ * Bytes 2-10: TWAI data
+ *
+ * Input Parameters:
+ * dev - An instance of the "upper half" CAN driver state structure.
+ * msg - A message to send.
+ *
+ * Returned Value:
+ * Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int esp32c3twai_send(struct can_dev_s *dev,
+ struct can_msg_s *msg)
+{
+ struct twai_dev_s *priv = (struct twai_dev_s *)dev->cd_priv;
+ uint32_t regval;
+ uint32_t i;
+ uint32_t len;
+ uint32_t id;
+ uint32_t frame_info;
+ irqstate_t flags;
+ int ret = OK;
+
+ caninfo("TWAI%" PRIu8 " ID: %" PRIu32 " DLC: %" PRIu8 "\n",
+ priv->port, (uint32_t)msg->cm_hdr.ch_id, msg->cm_hdr.ch_dlc);
+
+ len = (uint32_t)msg->cm_hdr.ch_dlc;
+ if (len > CAN_MAXDATALEN)
+ {
+ len = CAN_MAXDATALEN;
+ }
+
+ frame_info = len;
+
+ if (msg->cm_hdr.ch_rtr)
+ {
+ frame_info |= (1 << 6);
+ }
+
+ flags = enter_critical_section();
+
+ /* Make sure that TX interrupts are enabled BEFORE sending the
+ * message.
+ *
+ * NOTE: The INT_ENA is also modified from the interrupt handler, but the
+ * following is safe because interrupts are disabled here.
+ */
+
+ regval = twai_getreg(TWAI_INT_ENA_REG);
+ regval |= TWAI_TX_INT_ENA_M;
+ twai_putreg(TWAI_INT_ENA_REG, regval);
+
+ /* Set up the transfer */
+
+#ifdef CONFIG_CAN_EXTID
+ if (msg->cm_hdr.ch_extid)
+ {
+ /* The provided ID should be 29 bits */
+
+ id = (uint32_t)msg->cm_hdr.ch_id;
+ DEBUGASSERT((id & ~CAN_MAX_EXTMSGID) == 0);
+ frame_info |= (1 << 7);
+ twai_putreg(TWAI_DATA_0_REG, frame_info);
+
+ id <<= 3;
+ twai_putreg(TWAI_DATA_4_REG, id & 0xff);
+ id >>= 8;
+ twai_putreg(TWAI_DATA_3_REG, id & 0xff);
+ id >>= 8;
+ twai_putreg(TWAI_DATA_2_REG, id & 0xff);
+ id >>= 8;
+ twai_putreg(TWAI_DATA_1_REG, id & 0xff);
+ for (i = 0; i < len; i++)
+ {
+ twai_putreg(TWAI_DATA_5_REG + (i * 4), msg->cm_data[i]);
+ }
+ }
+ else
+#endif
+ {
+ /* The provided ID should be 11 bits */
+
+ id = (uint32_t)msg->cm_hdr.ch_id;
+ DEBUGASSERT((id & ~CAN_MAX_STDMSGID) == 0);
+ twai_putreg(TWAI_DATA_0_REG, frame_info);
+ id <<= 5;
+ twai_putreg(TWAI_DATA_1_REG, (id >> 8) & 0xff);
+ twai_putreg(TWAI_DATA_2_REG, id & 0xff);
+ for (i = 0; i < len; i++)
+ {
+ twai_putreg(TWAI_DATA_3_REG + (i * 4), msg->cm_data[i]);
+ }
+ }
+
+ /* Send the message */
+
+#ifdef CONFIG_CAN_LOOPBACK
+ twai_putreg(TWAI_CMD_REG, TWAI_SELF_RX_REQ_M | TWAI_ABORT_TX_M);
+#else
+ twai_putreg(TWAI_CMD_REG, TWAI_TX_REQ_M);
+#endif
+
+ leave_critical_section(flags);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: esp32c3twai_txready
+ *
+ * Description:
+ * Return true if the TWAI hardware can accept another TX message.
+ *
+ * Input Parameters:
+ * dev - An instance of the "upper half" CAN driver state structure.
+ *
+ * Returned Value:
+ * True if the TWAI hardware is ready to accept another TX message.
+ *
+ ****************************************************************************/
+
+static bool esp32c3twai_txready(struct can_dev_s *dev)
+{
+ struct twai_dev_s *priv = dev->cd_priv;
+ uint32_t regval = twai_getreg(TWAI_STATUS_REG);
+
+ caninfo("TWAI%" PRIu8 " txready: %d\n", priv->port,
+ ((regval & TWAI_TX_BUF_ST_M) != 0));
+ return ((regval & TWAI_TX_BUF_ST_M) != 0);
+}
+
+/****************************************************************************
+ * Name: esp32c3twai_txempty
+ *
+ * Description:
+ * Return true if all message have been sent. If for example, the TWAI
+ * 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 TWAI hardware.
+ *
+ ****************************************************************************/
+
+static bool esp32c3twai_txempty(struct can_dev_s *dev)
+{
+ struct twai_dev_s *priv = dev->cd_priv;
+ uint32_t regval = twai_getreg(TWAI_STATUS_REG);
+
+ caninfo("TWAI%" PRIu8 " txempty: %d\n", priv->port,
+ ((regval & TWAI_TX_BUF_ST_M) != 0));
+ return ((regval & TWAI_TX_BUF_ST_M) != 0);
+}
+
+/****************************************************************************
+ * Name: esp32c3twai_interrupt
+ *
+ * Description:
+ * TWAI0 RX/TX interrupt handler
+ *
+ * Input Parameters:
+ * irq - The IRQ number of the interrupt.
+ * context - The register state save array at the time of the interrupt.
+ * arg - The pointer to driver structure.
+ *
+ * Returned Value:
+ * Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int esp32c3twai_interrupt(int irq, void *context, void *arg)
+{
+#ifdef CONFIG_ESP32C3_TWAI0
+ struct can_dev_s *dev = (struct can_dev_s *)arg;
+ struct can_hdr_s hdr;
+ uint8_t data[8];
+ uint32_t frame_info;
+ uint32_t len;
+ uint32_t datastart;
+ uint32_t regval;
+ uint32_t i;
+
+ /* Read the interrupt register results in clearing bits */
+
+ regval = twai_getreg(TWAI_INT_RAW_REG);
+
+ /* Check for a receive interrupt */
+
+ if ((regval & TWAI_RX_INT_ST_M) != 0)
+ {
+ memset(&hdr, 0, sizeof(hdr));
+ memset(data, 0, sizeof(data));
+
+ frame_info = twai_getreg(TWAI_DATA_0_REG);
+
+ /* Construct the TWAI header */
+
+ if (frame_info & (1 << 6))
+ {
+ hdr.ch_rtr = 1;
+ }
+
+#ifdef CONFIG_CAN_EXTID
+ if (frame_info & (1 << 7))
+ {
+ /* The provided ID should be 29 bits */
+
+ hdr.ch_extid = 1;
+ hdr.ch_id =
+ (twai_getreg(TWAI_DATA_1_REG) << 21) +
+ (twai_getreg(TWAI_DATA_2_REG) << 13) +
+ (twai_getreg(TWAI_DATA_3_REG) << 5) +
+ (twai_getreg(TWAI_DATA_4_REG) >> 3);
+ datastart = TWAI_DATA_5_REG;
+ }
+ else
+#endif
+ {
+ /* The provided ID should be 11 bits */
+
+ hdr.ch_id =
+ (twai_getreg(TWAI_DATA_1_REG) << 3) +
+ (twai_getreg(TWAI_DATA_2_REG) >> 5);
+ datastart = TWAI_DATA_3_REG;
+ }
+
+ len = frame_info & 0xf;
+ if (len > CAN_MAXDATALEN)
+ {
+ len = CAN_MAXDATALEN;
+ }
+
+ hdr.ch_dlc = len;
+
+ for (i = 0; i < len; i++)
+ {
+ data[i] = twai_getreg(datastart + (i * 4));
+ }
+
+ /* Release the receive buffer */
+
+ twai_putreg(TWAI_CMD_REG, TWAI_RELEASE_BUF_M);
+
+#ifdef CONFIG_CAN_ERRORS
+ hdr.ch_error = 0; /* Error reporting not supported */
+#endif
+ can_receive(dev, &hdr, data);
+ }
+
+ /* Check for TX buffer complete */
+
+ if ((regval & TWAI_TX_INT_ST_M) != 0)
+ {
+ /* Disable all further TX buffer interrupts */
+
+ regval = twai_getreg(TWAI_INT_ENA_REG);
+ regval &= ~TWAI_TX_INT_ENA_M;
+ twai_putreg(TWAI_INT_ENA_REG, regval);
+
+ /* Indicate that the TX is done and a new TX buffer is available */
+
+ can_txdone(dev);
+ }
+
+#endif
+ return OK;
+}
+
+/****************************************************************************
+ * Name: esp32c3twai_set_acc_filter
+ *
+ * Description:
+ * Call to set acceptance filter.
+ * Must be called in reset mode.
+ *
+ * Input Parameters:
+ * code - Acceptance Code.
+ * mask - Acceptance Mask.
+ * single_filter - Whether to enable single filter mode.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void esp32c3twai_set_acc_filter(uint32_t code, uint32_t mask,
+ bool single_filter)
+{
+ uint32_t regval;
+ uint32_t code_swapped = __builtin_bswap32(code);
+ uint32_t mask_swapped = __builtin_bswap32(mask);
+
+ regval = twai_getreg(TWAI_MODE_REG);
+ if (single_filter)
+ {
+ regval |= TWAI_RX_FILTER_MODE_M;
+ }
+ else
+ {
+ regval &= ~(TWAI_RX_FILTER_MODE_M);
+ }
+
+ twai_putreg(TWAI_MODE_REG, regval);
+
+ for (int i = 0; i < 4; i++)
+ {
+ twai_putreg(TWAI_DATA_0_REG + (i * 4),
+ ((code_swapped >> (i * 8)) & 0xff));
+ twai_putreg(TWAI_DATA_4_REG + (i * 4),
+ ((mask_swapped >> (i * 8)) & 0xff));
+ }
+}
+
+/****************************************************************************
+ * Name: twai_baud_rate
+ *
+ * Description:
+ * Set the CAN bus timing registers based on the configured bit-rate and
+ * sample point position.
+ *
+ * The bit timing logic monitors the serial bus-line and performs sampling
+ * and adjustment of the sample point by synchronizing on the start-bit edge
+ * and resynchronizing on the following edges.
+ *
+ * Its operation may be explained simply by splitting nominal bit time into
+ * three segments as follows:
+ *
+ * 1. Synchronization segment (SYNC_SEG): a bit change is expected to occur
+ * within this time segment. It has a fixed length of one time quantum
+ * (1 x tCAN).
+ * 2. Bit segment 1 (BS1): defines the location of the sample point. It
+ * includes the PROP_SEG and PHASE_SEG1 of the CAN standard. Its duration
+ * is programmable between 1 and 16 time quanta but may be automatically
+ * lengthened to compensate for positive phase drifts due to differences
+ * in the frequency of the various nodes of the network.
+ * 3. Bit segment 2 (BS2): defines the location of the transmit point. It
+ * represents the PHASE_SEG2 of the CAN standard. Its duration is
+ * programmable between 1 and 8 time quanta but may also be automatically
+ * shortened to compensate for negative phase drifts.
+ *
+ * Pictorially:
+ *
+ * |<----------------- NOMINAL BIT TIME ----------------->|
+ * |<- SYNC_SEG ->|<------ BS1 ------>|<------ BS2 ------>|
+ * |<---- Tq ---->|<----- Tbs1 ------>|<----- Tbs2 ------>|
+ *
+ * Where
+ * Tbs1 is the duration of the BS1 segment
+ * Tbs2 is the duration of the BS2 segment
+ * Tq is the "Time Quantum"
+ *
+ * Relationships:
+ *
+ * baud = 1 / bit_time
+ * bit_time = Tq + Tbs1 + Tbs2
+ * Tbs1 = Tq * ts1
+ * Tbs2 = Tq * ts2
+ * Tq = brp * Tcan
+ *
+ * Where:
+ * Tcan is the period of the APB clock
+ *
+ * Input Parameters:
+ * priv - A reference to the CAN block status
+ *
+ * Returned Value:
+ * Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int twai_baud_rate(struct twai_dev_s *priv, int rate, int clock,
+ int sjw, int sampl_pt, int flags)
+{
+ const struct can_bittiming_const *timing =
+ &esp32c3_twai_bittiming_const;
+ int best_error = 1000000000;
+ int error;
+ int best_tseg = 0;
+ int best_brp = 0;
+ int best_rate = 0;
+ int brp = 0;
+ int tseg = 0;
+ int tseg1 = 0;
+ int tseg2 = 0;
+ uint32_t timing0;
+ uint32_t timing1;
+
+ /* tseg even = round down, odd = round up */
+
+ for (tseg = (0 + 0 + 2) * 2;
+ tseg <= (timing->tseg2_max + timing->tseg1_max + 2) * 2 + 1; tseg++)
+ {
+ brp = clock / ((1 + tseg / 2) * rate) + tseg % 2;
+ if (brp == 0 || brp > 64)
+ {
+ continue;
+ }
+
+ error = rate - clock / (brp * (1 + tseg / 2));
+ if (error < 0)
+ {
+ error = -error;
+ }
+
+ if (error <= best_error)
+ {
+ best_error = error;
+ best_tseg = tseg / 2;
+ best_brp = brp;
+ best_rate = clock / (brp * (1 + tseg / 2));
+ }
+ }
+
+ if (best_error && (rate / best_error < 10))
+ {
+ caninfo("baud rate %d is not possible with %d Hz clock\n",
+ rate, clock);
+ caninfo("%d bps. brp=%d, best_tseg=%d, tseg1=%d, tseg2=%d\n",
+ best_rate, best_brp, best_tseg, tseg1, tseg2);
+ return -EINVAL;
+ }
+
+ tseg2 = best_tseg - (sampl_pt * (best_tseg + 1)) / 100 + 1;
+ if (tseg2 < 0)
+ {
+ tseg2 = 0;
+ }
+
+ if (tseg2 > timing->tseg2_max)
+ {
+ tseg2 = timing->tseg2_max;
+ }
+
+ tseg1 = best_tseg - tseg2;
+ if (tseg1 > timing->tseg1_max)
+ {
+ tseg1 = timing->tseg1_max;
+ tseg2 = best_tseg - tseg1;
+ }
+
+ caninfo("TS1: %d TS2: %d BRP: %d\n", tseg1, tseg2, best_brp);
+
+ /* Configure bit timing */
+
+ timing0 = (best_brp - 1) / 2;
+ timing0 |= (sjw - 1) << TWAI_SYNC_JUMP_WIDTH_S;
+
+ timing1 = tseg1 - 1;
+ timing1 |= (tseg2 - 1) << TWAI_TIME_SEG2_S;
+
+ #ifdef CONFIG_ESP32C3_TWAI0_SAM
+ /* The bus is sampled 3 times (recommended for low to medium speed buses
+ * to spikes on the bus-line).
+ */
+
+ timing1 |= CONFIG_ESP32C3_TWAI0_SAM << TWAI_TIME_SAMP_S;
+ #endif
+
+ twai_putreg(TWAI_BUS_TIMING_0_REG, timing0);
+ twai_putreg(TWAI_BUS_TIMING_1_REG, timing1);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: esp32c3_twaiinitialize
+ *
+ * Description:
+ * Initialize the selected TWAI port
+ *
+ * Input Parameters:
+ * Port number (for hardware that has multiple TWAI interfaces)
+ *
+ * Returned Value:
+ * Valid TWAI device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+struct can_dev_s *esp32c3_twaiinitialize(int port)
+{
+ struct can_dev_s *twaidev;
+ irqstate_t flags;
+
+ caninfo("TWAI%" PRIu8 "\n", port);
+
+ flags = enter_critical_section();
+
+#ifdef CONFIG_ESP32C3_TWAI0
+ if (port == 0)
+ {
+ /* Enable power to the TWAI module and
+ * Enable clocking to the TWAI module
+ */
+
+ modifyreg32(SYSTEM_PERIP_RST_EN0_REG, 0, SYSTEM_TWAI_RST_M);
+ modifyreg32(SYSTEM_PERIP_CLK_EN0_REG, SYSTEM_TWAI_CLK_EN_M, 0);
+
+ usleep(1);
+
+ modifyreg32(SYSTEM_PERIP_CLK_EN0_REG, 0, SYSTEM_TWAI_CLK_EN_M);
+ modifyreg32(SYSTEM_PERIP_RST_EN0_REG, SYSTEM_TWAI_RST_M, 0);
+
+ /* Configure CAN GPIO pins */
+
+ esp32c3_gpio_matrix_out(CONFIG_ESP32C3_TWAI0_TXPIN, TWAI_TX_IDX, 0, 0);
+ esp32c3_configgpio(CONFIG_ESP32C3_TWAI0_TXPIN, OUTPUT_FUNCTION_1);
+
+ esp32c3_configgpio(CONFIG_ESP32C3_TWAI0_RXPIN, INPUT_FUNCTION_1);
+ esp32c3_gpio_matrix_in(CONFIG_ESP32C3_TWAI0_RXPIN, TWAI_RX_IDX, 0);
+
+ twaidev = &g_twai0dev;
+ }
+ else
+#endif
+
+ {
+ canerr("ERROR: Unsupported port: %d\n", port);
+ leave_critical_section(flags);
+ return NULL;
+ }
+
+ /* Then just perform a TWAI reset operation */
+
+ esp32c3twai_reset(twaidev);
+
+ leave_critical_section(flags);
+
+ return twaidev;
+}
+#endif
diff --git a/arch/risc-v/src/esp32c3/esp32c3_twai.h b/arch/risc-v/src/esp32c3/esp32c3_twai.h
new file mode 100644
index 0000000000..d844426a61
--- /dev/null
+++ b/arch/risc-v/src/esp32c3/esp32c3_twai.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+ * arch/risc-v/src/esp32c3/esp32c3_twai.h
+ *
+ * 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 __ARCH_RISCV_SRC_ESP32C3_ESP32C3_TWAI_H
+#define __ARCH_RISCV_SRC_ESP32C3_ESP32C3_TWAI_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "hardware/esp32c3_twai.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/****************************************************************************
+ * Public Functions Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: esp32c3_twaiinitialize
+ *
+ * Description:
+ * Initialize the selected CAN port
+ *
+ * Input Parameters:
+ * Port number (for hardware that has multiple TWAI interfaces)
+ *
+ * Returned Value:
+ * Valid TWAI device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_CAN) && defined(CONFIG_ESP32C3_TWAI)
+struct can_dev_s;
+struct can_dev_s *esp32c3_twaiinitialize(int port);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ARCH_RISCV_SRC_ESP32C3_ESP32C3_TWAI_H */