blob: d7fb0adb0ee5adba84d980f6f5ebc4d3d01f8d7e [file] [log] [blame]
/****************************************************************************
* arch/xtensa/src/esp32s3/esp32s3_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/spinlock.h>
#include "xtensa.h"
#include "esp32s3_gpio.h"
#include "esp32s3_twai.h"
#include "esp32s3_irq.h"
#include "esp32s3_clockconfig.h"
#include "esp32s3_periph.h"
#include "hardware/esp32s3_system.h"
#include "hardware/esp32s3_gpio_sigmap.h"
#if defined(CONFIG_ESP32S3_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
/* Exclude data overrun (bit[3]) and brp_div (bit[4]) */
#define TWAI_DEFAULT_INTERRUPTS 0x1e7
#define TWAI_ACCEPTANCE_CODE 0x0 /* 32-bit address to match */
#define TWAI_ACCEPTANCE_MASK 0xffffffff /* 32-bit address mask */
#ifdef CONFIG_ESP32S3_TWAI
/* A TWAI bit rate must be provided */
# ifndef CONFIG_ESP32S3_TWAI_BITRATE
# error "CONFIG_ESP32S3_TWAI_BITRATE is not defined"
# endif
/* If no sample point is provided, use a sample point of 80 */
# ifndef CONFIG_ESP32S3_TWAI_SAMPLEP
# define CONFIG_ESP32S3_TWAI_SAMPLEP 80
# endif
#endif
/* If no Synchronization Jump Width is provided, use a SJW of 13 */
#ifndef CONFIG_ESP32S3_TWAI_SJW
# define CONFIG_ESP32S3_TWAI_SJW 3
#endif
/* Debug ********************************************************************/
/* Non-standard debug that may be enabled just for testing TWAI */
#ifndef CONFIG_DEBUG_CAN_INFO
# undef CONFIG_ESP32S3_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 */
uint8_t cpu; /* CPU ID */
uint8_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 */
spinlock_t lock; /* Device specific lock */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* TWAI Register access */
#ifdef CONFIG_ESP32S3_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 esp32s3twai_reset(struct can_dev_s *dev);
static int esp32s3twai_setup(struct can_dev_s *dev);
static void esp32s3twai_shutdown(struct can_dev_s *dev);
static void esp32s3twai_rxint(struct can_dev_s *dev, bool enable);
static void esp32s3twai_txint(struct can_dev_s *dev, bool enable);
static int esp32s3twai_ioctl(struct can_dev_s *dev, int cmd,
unsigned long arg);
static int esp32s3twai_remoterequest(struct can_dev_s *dev,
uint16_t id);
static int esp32s3twai_send(struct can_dev_s *dev,
struct can_msg_s *msg);
static bool esp32s3twai_txready(struct can_dev_s *dev);
static bool esp32s3twai_txempty(struct can_dev_s *dev);
/* TWAI interrupts */
static int esp32s3twai_interrupt(int irq, void *context, void *arg);
/* TWAI acceptance filter */
static void esp32s3twai_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 esp32s3_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 = 8192,
.brp_inc = 1,
};
static const struct can_ops_s g_twaiops =
{
.co_reset = esp32s3twai_reset,
.co_setup = esp32s3twai_setup,
.co_shutdown = esp32s3twai_shutdown,
.co_rxint = esp32s3twai_rxint,
.co_txint = esp32s3twai_txint,
.co_ioctl = esp32s3twai_ioctl,
.co_remoterequest = esp32s3twai_remoterequest,
.co_send = esp32s3twai_send,
.co_txready = esp32s3twai_txready,
.co_txempty = esp32s3twai_txempty,
};
#ifdef CONFIG_ESP32S3_TWAI
static struct twai_dev_s g_twaipriv =
{
.bittiming_const = &esp32s3_twai_bittiming_const,
.port = 0,
.periph = ESP32S3_PERIPH_TWAI,
.irq = ESP32S3_IRQ_TWAI,
.cpuint = -ENOMEM,
.bitrate = CONFIG_ESP32S3_TWAI_BITRATE,
.samplep = CONFIG_ESP32S3_TWAI_SAMPLEP,
.sjw = CONFIG_ESP32S3_TWAI_SJW,
.base = DR_REG_TWAI_BASE,
.lock = SP_UNLOCKED,
};
static struct can_dev_s g_twaidev =
{
.cd_ops = &g_twaiops,
.cd_priv = &g_twaipriv,
};
#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_ESP32S3_TWAI_REGDEBUG
static void twai_printreg(uint32_t addr, uint32_t value)
{
static uint32_t prevaddr = 0;
static uint32_t preval = 0;
static uint32_t count = 0;
/* 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("%08x->%08x\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:
*
****************************************************************************/
#ifdef CONFIG_ESP32S3_TWAI_REGDEBUG
static uint32_t twai_getreg(uint32_t addr)
{
uint32_t value;
/* Read the value from the register */
value = getreg32(addr);
twai_printreg(addr, value);
return value;
}
#else
static uint32_t twai_getreg(uint32_t addr)
{
return getreg32(addr);
}
#endif
/****************************************************************************
* 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
*
****************************************************************************/
#ifdef CONFIG_ESP32S3_TWAI_REGDEBUG
static void twai_putreg(uint32_t addr, uint32_t value)
{
/* Show the register value being written */
caninfo("%08x<-%08x\n", addr, value);
/* Write the value */
putreg32(value, addr);
}
#else
static void twai_putreg(uint32_t addr, uint32_t value)
{
putreg32(value, addr);
}
#endif
/****************************************************************************
* Name: esp32s3twai_reset
*
* Description:
* Reset the TWAI device. Called early to initialize the hardware. This
* function is called, before esp32s3_twai_setup() and on error conditions.
*
* Input Parameters:
* dev - An instance of the "upper half" CAN driver state structure.
*
* Returned Value:
* None
*
****************************************************************************/
static void esp32s3twai_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\n");
flags = spin_lock_irqsave(&priv->lock);
/* 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 */
esp32s3twai_set_acc_filter(TWAI_ACCEPTANCE_CODE,
TWAI_ACCEPTANCE_MASK, true);
/* Set bit timing */
ret = twai_baud_rate(priv, priv->bitrate, esp_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);
spin_unlock_irqrestore(&priv->lock, flags);
}
/****************************************************************************
* Name: esp32s3twai_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 esp32s3twai_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\n");
flags = spin_lock_irqsave(&priv->lock);
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->irq);
}
priv->cpu = up_cpu_index();
priv->cpuint = esp32s3_setup_irq(priv->cpu, priv->periph,
1, ESP32S3_CPUINT_LEVEL);
if (priv->cpuint < 0)
{
/* Failed to allocate a CPU interrupt of this type. */
ret = priv->cpuint;
spin_unlock_irqrestore(&priv->lock, flags);
return ret;
}
ret = irq_attach(priv->irq, esp32s3twai_interrupt, dev);
if (ret != OK)
{
/* Failed to attach IRQ, so CPU interrupt must be freed. */
esp32s3_teardown_irq(priv->cpu, priv->periph, priv->cpuint);
priv->cpuint = -ENOMEM;
spin_unlock_irqrestore(&priv->lock, flags);
return ret;
}
/* Enable the CPU interrupt that is linked to the TWAI device. */
up_enable_irq(priv->irq);
spin_unlock_irqrestore(&priv->lock, flags);
return ret;
}
/****************************************************************************
* Name: esp32s3twai_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 esp32s3twai_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\n");
#endif
if (priv->cpuint != -ENOMEM)
{
/* Disable cpu interrupt */
up_disable_irq(priv->irq);
/* Dissociate the IRQ from the ISR */
irq_detach(priv->irq);
/* Free cpu interrupt that is attached to this peripheral */
esp32s3_teardown_irq(priv->cpu, priv->periph, priv->cpuint);
priv->cpuint = -ENOMEM;
}
}
/****************************************************************************
* Name: esp32s3twai_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 esp32s3twai_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 enable: %d\n", enable);
/* The INT_ENA register is also modified from the interrupt handler,
* so we have to protect this code section.
*/
flags = spin_lock_irqsave(&priv->lock);
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);
spin_unlock_irqrestore(&priv->lock, flags);
}
/****************************************************************************
* Name: esp32s3twai_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 esp32s3twai_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 enable: %d\n", 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 = spin_lock_irqsave(&priv->lock);
/* Disable all TX interrupts */
regval = twai_getreg(TWAI_INT_ENA_REG);
regval &= ~(TWAI_TX_INT_ENA_M);
twai_putreg(TWAI_INT_ENA_REG, regval);
spin_unlock_irqrestore(&priv->lock, flags);
}
}
/****************************************************************************
* Name: esp32s3twai_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 esp32s3twai_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 cmd=%04x arg=%lu\n", 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 = esp_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;
}
/****************************************************************************
* Name: esp32s3twai_remoterequest
*
* Description:
* Send a remote request
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
* id - Requested 11-bit data frame identifier
*
* Returned Value:
* Zero on success; a negated errno on failure
*
****************************************************************************/
static int esp32s3twai_remoterequest(struct can_dev_s *dev, uint16_t id)
{
canwarn("Remote request not implemented\n");
return -ENOSYS;
}
/****************************************************************************
* Name: esp32s3twai_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 esp32s3twai_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 ID: %" PRIu32 " DLC: %" PRIu8 "\n",
(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 = spin_lock_irqsave(&priv->lock);
/* 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
spin_unlock_irqrestore(&priv->lock, flags);
return ret;
}
/****************************************************************************
* Name: esp32s3twai_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 esp32s3twai_txready(struct can_dev_s *dev)
{
struct twai_dev_s *priv = dev->cd_priv;
uint32_t regval = twai_getreg(TWAI_STATUS_REG);
caninfo("TWAI txready: %d\n", ((regval & TWAI_TX_BUF_ST_M) != 0));
return ((regval & TWAI_TX_BUF_ST_M) != 0);
}
/****************************************************************************
* Name: esp32s3twai_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 esp32s3twai_txempty(struct can_dev_s *dev)
{
struct twai_dev_s *priv = dev->cd_priv;
uint32_t regval = twai_getreg(TWAI_STATUS_REG);
caninfo("TWAI txempty: %d\n", ((regval & TWAI_TX_BUF_ST_M) != 0));
return ((regval & TWAI_TX_BUF_ST_M) != 0);
}
/****************************************************************************
* Name: esp32s3twai_interrupt
*
* Description:
* TWAI 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 esp32s3twai_interrupt(int irq, void *context, void *arg)
{
#ifdef CONFIG_ESP32S3_TWAI
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: esp32s3twai_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 esp32s3twai_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
* rate - The resolution of one timing quanta, in Hz
* clock - Clock source frequency
* sjw - Max time quanta jump for synchronize
* sampl_pt - Enables triple sampling
* flags - Flag for configuration
*
* 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 =
&esp32s3_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))
{
canerr("baud rate %d is not possible with %d Hz clock\n",
rate, clock);
canerr("%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 / 2) - 1;
timing0 |= (sjw - 1) << TWAI_SYNC_JUMP_WIDTH_S;
timing1 = tseg1 - 1;
timing1 |= (tseg2 - 1) << TWAI_TIME_SEG2_S;
#ifdef CONFIG_ESP32S3_TWAI_SAM
/* The bus is sampled 3 times (recommended for low to medium speed buses
* to spikes on the bus-line).
*/
timing1 |= CONFIG_ESP32S3_TWAI_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: esp32s3_twaiinitialize
*
* Description:
* Initialize TWAI peripheral
*
* Input Parameters:
* None
*
* Returned Value:
* Valid TWAI device structure reference on success; a NULL on failure
*
****************************************************************************/
struct can_dev_s *esp32s3_twaiinitialize(void)
{
struct can_dev_s *twaidev;
irqstate_t flags;
caninfo("TWAI\n");
#ifdef CONFIG_ESP32S3_TWAI
twaidev = &g_twaidev;
flags = spin_lock_irqsave(&g_twaipriv.lock);
/* Enable power to the TWAI module and
* Enable clocking to the TWAI module
*/
esp32s3_periph_module_enable(PERIPH_TWAI_MODULE);
/* Configure CAN GPIO pins */
esp32s3_configgpio(CONFIG_ESP32S3_TWAI_TXPIN, OUTPUT_FUNCTION_2);
esp32s3_gpio_matrix_out(CONFIG_ESP32S3_TWAI_TXPIN, TWAI_TX_IDX, 0, 0);
esp32s3_configgpio(CONFIG_ESP32S3_TWAI_RXPIN, INPUT_FUNCTION_2);
esp32s3_gpio_matrix_in(CONFIG_ESP32S3_TWAI_RXPIN, TWAI_RX_IDX, 0);
spin_unlock_irqrestore(&g_twaipriv.lock, flags);
#endif
/* Then just perform a TWAI reset operation */
esp32s3twai_reset(twaidev);
return twaidev;
}
#endif