* arch/arm/src/at32/at32_can.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 <inttypes.h>
#include <stdio.h>
#include <sys/types.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 "arm_internal.h"
#include "chip.h"
#include "at32.h"
#include "at32_rcc.h"
#include "at32_can.h"
#if defined(CONFIG_CAN) && \
(defined(CONFIG_AT32_CAN1) || defined(CONFIG_AT32_CAN2))
* Pre-processor Definitions
****************************************************************************/
#define INAK_TIMEOUT 65535
#define CAN_BIT_QUANTA (CONFIG_AT32_CAN_TSEG1 + CONFIG_AT32_CAN_TSEG2 + 1)
#ifndef CONFIG_DEBUG_CAN_INFO
# undef CONFIG_AT32_CAN_REGDEBUG
#endif
#ifdef CONFIG_CAN_ERRORS
# define AT32_CAN_ERRINT (CAN_INTEN_ETRIEN | CAN_INTEN_EOIEN | \
CAN_INTEN_BOIEN | CAN_INTEN_EPIEN | \
CAN_INTEN_EAIEN)
#endif
* Private Types
****************************************************************************/
struct at32_can_s
{
uint8_t port;
uint8_t canrx[2];
uint8_t cantx;
#ifdef CONFIG_CAN_ERRORS
uint8_t cansce;
#endif
uint8_t filter;
uint32_t base;
uint32_t fbase;
uint32_t baud;
};
* Private Function Prototypes
****************************************************************************/
static uint32_t at32can_getreg(struct at32_can_s *priv,
int offset);
static uint32_t at32can_getfreg(struct at32_can_s *priv,
int offset);
static void at32can_putreg(struct at32_can_s *priv, int offset,
uint32_t value);
static void at32can_putfreg(struct at32_can_s *priv, int offset,
uint32_t value);
#ifdef CONFIG_AT32_CAN_REGDEBUG
static void at32can_dumpctrlregs(struct at32_can_s *priv,
const char *msg);
static void at32can_dumpmbregs(struct at32_can_s *priv,
const char *msg);
static void at32can_dumpfiltregs(struct at32_can_s *priv,
const char *msg);
#else
# define at32can_dumpctrlregs(priv,msg)
# define at32can_dumpmbregs(priv,msg)
# define at32can_dumpfiltregs(priv,msg)
#endif
#ifdef CONFIG_CAN_EXTID
static int at32can_addextfilter(struct at32_can_s *priv,
struct canioc_extfilter_s *arg);
static int at32can_delextfilter(struct at32_can_s *priv,
int arg);
#endif
static int at32can_addstdfilter(struct at32_can_s *priv,
struct canioc_stdfilter_s *arg);
static int at32can_delstdfilter(struct at32_can_s *priv,
int arg);
static void at32can_reset(struct can_dev_s *dev);
static int at32can_setup(struct can_dev_s *dev);
static void at32can_shutdown(struct can_dev_s *dev);
static void at32can_rxint(struct can_dev_s *dev, bool enable);
static void at32can_txint(struct can_dev_s *dev, bool enable);
static int at32can_ioctl(struct can_dev_s *dev, int cmd,
unsigned long arg);
static int at32can_remoterequest(struct can_dev_s *dev,
uint16_t id);
static int at32can_send(struct can_dev_s *dev,
struct can_msg_s *msg);
static bool at32can_txready(struct can_dev_s *dev);
static bool at32can_txempty(struct can_dev_s *dev);
#ifdef CONFIG_CAN_ERRORS
static void at32can_errint(struct can_dev_s *dev, bool enable);
#endif
static int at32can_rxinterrupt(struct can_dev_s *dev, int rxmb);
static int at32can_rx0interrupt(int irq, void *context, void *arg);
static int at32can_rx1interrupt(int irq, void *context, void *arg);
static int at32can_txinterrupt(int irq, void *context, void *arg);
#ifdef CONFIG_CAN_ERRORS
static int at32can_sceinterrupt(int irq, void *context, void *arg);
#endif
static int at32can_enterinitmode(struct at32_can_s *priv);
static int at32can_exitinitmode(struct at32_can_s *priv);
static int at32can_bittiming(struct at32_can_s *priv);
static int at32can_cellinit(struct at32_can_s *priv);
static int at32can_filterinit(struct at32_can_s *priv);
static bool at32can_txmb0empty(uint32_t tsr_regval);
static bool at32can_txmb1empty(uint32_t tsr_regval);
static bool at32can_txmb2empty(uint32_t tsr_regval);
* Private Data
****************************************************************************/
static const struct can_ops_s g_canops =
{
.co_reset = at32can_reset,
.co_setup = at32can_setup,
.co_shutdown = at32can_shutdown,
.co_rxint = at32can_rxint,
.co_txint = at32can_txint,
.co_ioctl = at32can_ioctl,
.co_remoterequest = at32can_remoterequest,
.co_send = at32can_send,
.co_txready = at32can_txready,
.co_txempty = at32can_txempty,
};
#ifdef CONFIG_AT32_CAN1
static struct at32_can_s g_can1priv =
{
.port = 1,
.canrx =
{
AT32_IRQ_CAN1RX0,
AT32_IRQ_CAN1RX1,
},
.cantx = AT32_IRQ_CAN1TX,
#ifdef CONFIG_CAN_ERRORS
.cansce = AT32_IRQ_CAN1SCE,
#endif
.filter = 0,
.base = AT32_CAN1_BASE,
.fbase = AT32_CAN1_BASE,
.baud = CONFIG_AT32_CAN1_BAUD,
};
static struct can_dev_s g_can1dev =
{
.cd_ops = &g_canops,
.cd_priv = &g_can1priv,
};
#endif
#ifdef CONFIG_AT32_CAN2
static struct at32_can_s g_can2priv =
{
.port = 2,
.canrx =
{
AT32_IRQ_CAN2RX0,
AT32_IRQ_CAN2RX1,
},
.cantx = AT32_IRQ_CAN2TX,
#ifdef CONFIG_CAN_ERRORS
.cansce = AT32_IRQ_CAN2SCE,
#endif
.filter = CAN_NFILTERS / 2,
.base = AT32_CAN2_BASE,
.fbase = AT32_CAN1_BASE,
.baud = CONFIG_AT32_CAN2_BAUD,
};
static struct can_dev_s g_can2dev =
{
.cd_ops = &g_canops,
.cd_priv = &g_can2priv,
};
#endif
* Private Functions
****************************************************************************/
* Name: at32can_getreg
* Name: at32can_getfreg
*
* Description:
* Read the value of a CAN register or filter block register.
*
* Input Parameters:
* priv - A reference to the CAN block status
* offset - The offset to the register to read
*
* Returned Value:
*
****************************************************************************/
#ifdef CONFIG_AT32_CAN_REGDEBUG
static uint32_t at32can_vgetreg(uint32_t addr)
{
static uint32_t prevaddr = 0;
static uint32_t preval = 0;
static uint32_t count = 0;
uint32_t val = getreg32(addr);
* Are we polling the register? If so, suppress some of the output.
*/
if (addr == prevaddr && val == preval)
{
if (count == 0xffffffff || ++count > 3)
{
if (count == 4)
{
caninfo("...\n");
}
return val;
}
}
else
{
if (count > 3)
{
caninfo("[repeats %" PRIu32 " more times]\n", count - 3);
}
prevaddr = addr;
preval = val;
count = 1;
}
caninfo("%08" PRIx32 "->%08" PRIx32 "\n", addr, val);
return val;
}
static uint32_t at32can_getreg(struct at32_can_s *priv, int offset)
{
return at32can_vgetreg(priv->base + offset);
}
static uint32_t at32can_getfreg(struct at32_can_s *priv, int offset)
{
return at32can_vgetreg(priv->fbase + offset);
}
#else
static uint32_t at32can_getreg(struct at32_can_s *priv, int offset)
{
return getreg32(priv->base + offset);
}
static uint32_t at32can_getfreg(struct at32_can_s *priv, int offset)
{
return getreg32(priv->fbase + offset);
}
#endif
* Name: at32can_putreg
* Name: at32can_putfreg
*
* Description:
* Set the value of a CAN register or filter block register.
*
* Input Parameters:
* priv - A reference to the CAN block status
* offset - The offset to the register to write
* value - The value to write to the register
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_AT32_CAN_REGDEBUG
static void at32can_vputreg(uint32_t addr, uint32_t value)
{
caninfo("%08" PRIx32 "->%08" PRIx32 "\n", addr, val);
putreg32(value, addr);
}
static void at32can_putreg(struct at32_can_s *priv, int offset,
uint32_t value)
{
at32can_vputreg(priv->base + offset, value);
}
static void at32can_putfreg(struct at32_can_s *priv, int offset,
uint32_t value)
{
at32can_vputreg(priv->fbase + offset, value);
}
#else
static void at32can_putreg(struct at32_can_s *priv, int offset,
uint32_t value)
{
putreg32(value, priv->base + offset);
}
static void at32can_putfreg(struct at32_can_s *priv, int offset,
uint32_t value)
{
putreg32(value, priv->fbase + offset);
}
#endif
* Name: at32can_dumpctrlregs
*
* Description:
* Dump the contents of all CAN control registers
*
* Input Parameters:
* priv - A reference to the CAN block status
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_AT32_CAN_REGDEBUG
static void at32can_dumpctrlregs(struct at32_can_s *priv,
const char *msg)
{
if (msg)
{
caninfo("Control Registers: %s\n", msg);
}
else
{
caninfo("Control Registers:\n");
}
caninfo(" MCTRL: %08" PRIx32 " MSTS: %08" PRIx32 " \
TSTS: %08" PRIx32 "\n",
getreg32(priv->base + AT32_CAN_MCTRL_OFFSET),
getreg32(priv->base + AT32_CAN_MSTS_OFFSET),
getreg32(priv->base + AT32_CAN_TSTS_OFFSET));
caninfo(" RF0: %08" PRIx32 " RF1: %08" PRIx32 "\n",
getreg32(priv->base + AT32_CAN_RF0_OFFSET),
getreg32(priv->base + AT32_CAN_RF1_OFFSET));
caninfo(" INTEN: %08" PRIx32 " ESTS: %08" PRIx32 " \
BTMG: %08" PRIx32 "\n",
getreg32(priv->base + AT32_CAN_INTEN_OFFSET),
getreg32(priv->base + AT32_CAN_ESTS_OFFSET),
getreg32(priv->base + AT32_CAN_BTMG_OFFSET));
}
#endif
* Name: at32can_dumpmbregs
*
* Description:
* Dump the contents of all CAN mailbox registers
*
* Input Parameters:
* priv - A reference to the CAN block status
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_AT32_CAN_REGDEBUG
static void at32can_dumpmbregs(struct at32_can_s *priv,
const char *msg)
{
if (msg)
{
caninfo("Mailbox Registers: %s\n", msg);
}
else
{
caninfo("Mailbox Registers:\n");
}
caninfo(" TMI0: %08" PRIx32 " TMC0: %08" PRIx32 " TMDTL0: %08"
PRIx32 " TMDTH0: %08" PRIx32 "\n",
getreg32(priv->base + AT32_CAN_TMI0_FFSET),
getreg32(priv->base + AT32_CAN_TMC0_OFFSET),
getreg32(priv->base + AT32_CAN_TMDTL0_OFFSET),
getreg32(priv->base + AT32_CAN_TMDTH0_OFFSET));
caninfo(" TMI1: %08" PRIx32 " TMC1: %08" PRIx32 " TMDTL1: %08"
PRIx32 " TMDTH1: %08" PRIx32 "\n",
getreg32(priv->base + AT32_CAN_TMI1_FFSET),
getreg32(priv->base + AT32_CAN_TMC1_OFFSET),
getreg32(priv->base + AT32_CAN_TMDTL1_OFFSET),
getreg32(priv->base + AT32_CAN_TMDTH1_OFFSET));
caninfo(" TMI2: %08" PRIx32 " TMC2: %08" PRIx32 " TMDTL2: %08"
PRIx32 " TMDTH2: %08" PRIx32 "\n",
getreg32(priv->base + AT32_CAN_TMI2_FFSET),
getreg32(priv->base + AT32_CAN_TMC2_OFFSET),
getreg32(priv->base + AT32_CAN_TMDTL2_OFFSET),
getreg32(priv->base + AT32_CAN_TMDTH2_OFFSET));
caninfo(" RFI0: %08" PRIx32 " RFC0: %08" PRIx32 " RFDTL0: %08"
PRIx32 " RFDTH0: %08" PRIx32 "\n",
getreg32(priv->base + AT32_CAN_RFI0_OFFSET),
getreg32(priv->base + AT32_CAN_RFC0_OFFSET),
getreg32(priv->base + AT32_CAN_RFDTL0_OFFSET),
getreg32(priv->base + AT32_CAN_RFDTH0_OFFSET));
caninfo(" RFI1: %08" PRIx32 " RFC1: %08" PRIx32 " RFDTL1: %08"
PRIx32 " RFDTH1: %08" PRIx32 "\n",
getreg32(priv->base + AT32_CAN_RFI1_OFFSET),
getreg32(priv->base + AT32_CAN_RFC1_OFFSET),
getreg32(priv->base + AT32_CAN_RFDTL1_OFFSET),
getreg32(priv->base + AT32_CAN_RFDTH1_OFFSET));
}
#endif
* Name: at32can_dumpfiltregs
*
* Description:
* Dump the contents of all CAN filter registers
*
* Input Parameters:
* priv - A reference to the CAN block status
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_AT32_CAN_REGDEBUG
static void at32can_dumpfiltregs(struct at32_can_s *priv,
const char *msg)
{
int i;
if (msg)
{
caninfo("Filter Registers: %s\n", msg);
}
else
{
caninfo("Filter Registers:\n");
}
caninfo(" FCTRL: %08" PRIx32 " FMCFG: %08" PRIx32 " FSCFG: %08"
PRIx32 " FRF: %08" PRIx32 " FACFG: %08" PRIx32 "\n",
getreg32(priv->base + AT32_CAN_FCTRL_OFFSET),
getreg32(priv->base + AT32_CAN_FMCFG_OFFSET),
getreg32(priv->base + AT32_CAN_FSCFG_OFFSET),
getreg32(priv->base + AT32_CAN_FRF_OFFSET),
getreg32(priv->base + AT32_CAN_FACFG_OFFSET));
for (i = 0; i < CAN_NFILTERS; i++)
{
caninfo(" F%dR1: %08" PRIx32 " F%dR2: %08" PRIx32 "\n",
i, getreg32(priv->base + AT32_CAN_FBF_OFFSET(i, 1)),
i, getreg32(priv->base + AT32_CAN_FBF_OFFSET(i, 2)));
}
}
#endif
* Name: at32can_reset
*
* Description:
* Reset the CAN device. Called early to initialize the hardware. This
* function is called, before at32can_setup() and on error conditions.
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* None
*
****************************************************************************/
static void at32can_reset(struct can_dev_s *dev)
{
struct at32_can_s *priv = dev->cd_priv;
uint32_t regval;
uint32_t regbit = 0;
irqstate_t flags;
caninfo("CAN%" PRIu8 "\n", priv->port);
#ifdef CONFIG_AT32_CAN1
if (priv->port == 1)
{
regbit = CRM_APB1RST_CAN1RST;
}
else
#endif
#ifdef CONFIG_AT32_CAN2
if (priv->port == 2)
{
regbit = CRM_APB1RST_CAN1RST;
}
else
#endif
{
canerr("ERROR: Unsupported port %d\n", priv->port);
return;
}
* and to prevent any concurrent access to the AHB1RSTR register.
*/
flags = enter_critical_section();
regval = getreg32(AT32_CRM_APB1RST);
regval |= regbit;
putreg32(regval, AT32_CRM_APB1RST);
regval &= ~regbit;
putreg32(regval, AT32_CRM_APB1RST);
leave_critical_section(flags);
}
* Name: at32can_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 at32can_setup(struct can_dev_s *dev)
{
struct at32_can_s *priv = dev->cd_priv;
int ret;
#ifdef CONFIG_CAN_ERRORS
ninfo("CAN%" PRIu8 " RX0 irq: %" PRIu8 " RX1 irq: %" PRIu8
" TX irq: %" PRIu8 " SCE irq: %" PRIu8 "\n",
priv->port, priv->canrx[0], priv->canrx[1], priv->cantx,
priv->cansce);
#else
ninfo("CAN%" PRIu8 " RX0 irq: %" PRIu8 " RX1 irq: %" PRIu8
" TX irq: %" PRIu8 "\n",
priv->port, priv->canrx[0], priv->canrx[1], priv->cantx);
#endif
ret = at32can_cellinit(priv);
if (ret < 0)
{
canerr("ERROR: CAN%" PRId8 " cell initialization failed: %d\n",
priv->port, ret);
return ret;
}
at32can_dumpctrlregs(priv, "After cell initialization");
at32can_dumpmbregs(priv, NULL);
ret = at32can_filterinit(priv);
if (ret < 0)
{
canerr("ERROR: CAN%" PRIu8 " filter initialization failed: %d\n",
priv->port, ret);
return ret;
}
at32can_dumpfiltregs(priv, "After filter initialization");
* The others are not used.
*/
ret = irq_attach(priv->canrx[0], at32can_rx0interrupt, dev);
if (ret < 0)
{
canerr("ERROR: Failed to attach CAN%" PRIu8 " RX0 IRQ (%" PRIu8 ")",
priv->port, priv->canrx[0]);
return ret;
}
ret = irq_attach(priv->canrx[1], at32can_rx1interrupt, dev);
if (ret < 0)
{
canerr("ERROR: Failed to attach CAN%" PRIu8 " RX1 IRQ (%" PRIu8 ")",
priv->port, priv->canrx[1]);
return ret;
}
ret = irq_attach(priv->cantx, at32can_txinterrupt, dev);
if (ret < 0)
{
canerr("ERROR: Failed to attach CAN%" PRIu8 " TX IRQ (%" PRIu8 ")",
priv->port, priv->cantx);
return ret;
}
#ifdef CONFIG_CAN_ERRORS
ret = irq_attach(priv->cansce, at32can_sceinterrupt, dev);
if (ret < 0)
{
nerr("ERROR: Failed to attach CAN%" PRIu8 " SCE IRQ (%" PRIu8 ")",
priv->port, priv->cansce);
return ret;
}
at32can_errint(dev, true);
#endif
* the CAN module. Since we coming out of reset here, there should be
* no pending interrupts.
*/
up_enable_irq(priv->canrx[0]);
up_enable_irq(priv->canrx[1]);
up_enable_irq(priv->cantx);
#ifdef CONFIG_CAN_ERRORS
up_enable_irq(priv->cansce);
#endif
return OK;
}
* Name: at32can_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 at32can_shutdown(struct can_dev_s *dev)
{
struct at32_can_s *priv = dev->cd_priv;
caninfo("CAN%" PRIu8 "\n", priv->port);
up_disable_irq(priv->canrx[0]);
up_disable_irq(priv->canrx[1]);
up_disable_irq(priv->cantx);
#ifdef CONFIG_CAN_ERRORS
up_disable_irq(priv->cansce);
#endif
irq_detach(priv->canrx[0]);
irq_detach(priv->canrx[1]);
irq_detach(priv->cantx);
#ifdef CONFIG_CAN_ERRORS
irq_detach(priv->cansce);
#endif
at32can_reset(dev);
}
* Name: at32can_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 at32can_rxint(struct can_dev_s *dev, bool enable)
{
struct at32_can_s *priv = dev->cd_priv;
uint32_t regval;
caninfo("CAN%" PRIu8 " rxint enable: %d\n", priv->port, enable);
regval = at32can_getreg(priv, AT32_CAN_INTEN_OFFSET);
if (enable)
{
regval |= CAN_INTEN_RF0MIEN | CAN_INTEN_RF1MIEN;
}
else
{
regval &= ~(CAN_INTEN_RF0MIEN | CAN_INTEN_RF1MIEN);
}
at32can_putreg(priv, AT32_CAN_INTEN_OFFSET, regval);
}
* Name: at32can_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 at32can_txint(struct can_dev_s *dev, bool enable)
{
struct at32_can_s *priv = dev->cd_priv;
uint32_t regval;
caninfo("CAN%" PRIu8 " txint enable: %d\n", priv->port, enable);
if (!enable)
{
regval = at32can_getreg(priv, AT32_CAN_INTEN_OFFSET);
regval &= ~CAN_INTEN_TCIEN;
at32can_putreg(priv, AT32_CAN_INTEN_OFFSET, regval);
}
}
#ifdef CONFIG_CAN_ERRORS
* Name: at32can_errint
*
* Description:
* Call to enable or disable CAN error interrupts.
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* None
*
****************************************************************************/
static void at32can_errint(struct can_dev_s *dev, bool enable)
{
struct at32_can_s *priv = dev->cd_priv;
uint32_t regval = 0;
caninfo("CAN%" PRIu8 " errint enable: %d\n", priv->port, enable);
regval = at32can_getreg(priv, AT32_CAN_INTEN_OFFSET);
if (enable)
{
regval |= AT32_CAN_ERRINT;
}
else
{
regval &= ~AT32_CAN_ERRINT;
}
at32can_putreg(priv, AT32_CAN_INTEN_OFFSET, regval);
}
#endif
* Name: at32can_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 at32can_ioctl(struct can_dev_s *dev, int cmd,
unsigned long arg)
{
struct at32_can_s *priv;
int ret = -ENOTTY;
caninfo("cmd=%04x arg=%lu\n", cmd, arg);
DEBUGASSERT(dev && dev->cd_priv);
priv = dev->cd_priv;
switch (cmd)
{
* 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 regval;
uint32_t brp;
DEBUGASSERT(bt != NULL);
regval = at32can_getreg(priv, AT32_CAN_BTMG_OFFSET);
bt->bt_sjw = ((regval & CAN_BTMG_RSAW_MASK) >>
CAN_BTMG_RSAW_SHIFT) + 1;
bt->bt_tseg1 = ((regval & CAN_BTMG_BTS1_MASK) >>
CAN_BTMG_BTS1_SHIFT) + 1;
bt->bt_tseg2 = ((regval & CAN_BTMG_BTS2_MASK) >>
CAN_BTMG_BTS2_SHIFT) + 1;
brp = ((regval & CAN_BTMG_BRDIV_MASK) >>
CAN_BTMG_BRDIV_SHIFT) + 1;
bt->bt_baud = AT32_PCLK1_FREQUENCY /
(brp * (bt->bt_tseg1 + bt->bt_tseg2 + 1));
ret = OK;
}
break;
* Description: Set new current bit timing values
* Argument: A pointer to a read-able instance of struct
* canioc_bittiming_s in which the new bit timing
* values are provided.
* Returned Value: Zero (OK) is returned on success. Otherwise -1
* (ERROR)is returned with the errno variable set
* to indicate thenature of the error.
* Dependencies: None
*
* REVISIT: There is probably a limitation here: If there are
* multiple threads trying to send CAN packets, when one of these
* threads reconfigures the bitrate, the MCAN hardware will be reset
* and the context of operation will be lost. Hence, this IOCTL can
* only safely be executed in quiescent time periods.
*/
case CANIOC_SET_BITTIMING:
{
const struct canioc_bittiming_s *bt =
(const struct canioc_bittiming_s *)arg;
uint32_t brp;
uint32_t can_bit_quanta;
uint32_t tmp;
uint32_t regval;
DEBUGASSERT(bt != NULL);
DEBUGASSERT(bt->bt_baud < AT32_PCLK1_FREQUENCY);
DEBUGASSERT(bt->bt_sjw > 0 && bt->bt_sjw <= 4);
DEBUGASSERT(bt->bt_tseg1 > 0 && bt->bt_tseg1 <= 16);
DEBUGASSERT(bt->bt_tseg2 > 0 && bt->bt_tseg2 <= 8);
regval = at32can_getreg(priv, AT32_CAN_BTMG_OFFSET);
* tmp is in clocks per bit time
*/
tmp = AT32_PCLK1_FREQUENCY / bt->bt_baud;
can_bit_quanta = bt->bt_tseg1 + bt->bt_tseg2 + 1;
if (tmp < can_bit_quanta)
{
ret = -EINVAL;
break;
}
* provided by the user and we calculate brp to achieve
* can_bit_quanta quanta in the bit times
*/
else
{
brp = (tmp + (can_bit_quanta / 2)) / can_bit_quanta;
DEBUGASSERT(brp >= 1 && brp <= CAN_BTR_BRP_MAX);
}
caninfo("TS1: %"PRIu8 " TS2: %" PRIu8 " BRP: %" PRIu32 "\n",
bt->bt_tseg1, bt->bt_tseg2, brp);
regval &= ~(CAN_BTMG_BRDIV_MASK | CAN_BTMG_BTS1_MASK |
CAN_BTMG_BTS2_MASK | CAN_BTMG_RSAW_MASK);
regval |= ((brp - 1) << CAN_BTMG_BRDIV_SHIFT) |
((bt->bt_tseg1 - 1) << CAN_BTMG_BTS1_SHIFT) |
((bt->bt_tseg2 - 1) << CAN_BTMG_BTS2_SHIFT) |
((bt->bt_sjw - 1) << CAN_BTMG_RSAW_SHIFT);
ret = at32can_enterinitmode(priv);
if (ret < 0)
{
break;
}
at32can_putreg(priv, AT32_CAN_BTMG_OFFSET, regval);
ret = at32can_exitinitmode(priv);
if (ret >= 0)
{
priv->baud = AT32_PCLK1_FREQUENCY /
(brp * (bt->bt_tseg1 + bt->bt_tseg2 + 1));
}
}
break;
* Description: Get the current bus connection modes
* Argument: A pointer to a write-able instance of struct
* canioc_connmodes_s in which the new bus modes 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_CONNMODES:
{
struct canioc_connmodes_s *bm =
(struct canioc_connmodes_s *)arg;
uint32_t regval;
DEBUGASSERT(bm != NULL);
regval = at32can_getreg(priv, AT32_CAN_BTMG_OFFSET);
bm->bm_loopback = ((regval & CAN_BTMG_LBEN) == CAN_BTMG_LBEN);
bm->bm_silent = ((regval & CAN_BTMG_LOEN) == CAN_BTMG_LOEN);
ret = OK;
break;
}
* Description: Set new bus connection modes values
* Argument: A pointer to a read-able instance of struct
* canioc_connmodes_s in which the new bus modes
* are provided.
* 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_SET_CONNMODES:
{
struct canioc_connmodes_s *bm =
(struct canioc_connmodes_s *)arg;
uint32_t regval;
DEBUGASSERT(bm != NULL);
regval = at32can_getreg(priv, AT32_CAN_BTMG_OFFSET);
if (bm->bm_loopback)
{
regval |= CAN_BTMG_LBEN;
}
else
{
regval &= ~CAN_BTMG_LBEN;
}
if (bm->bm_silent)
{
regval |= CAN_BTMG_LOEN;
}
else
{
regval &= ~CAN_BTMG_LOEN;
}
ret = at32can_enterinitmode(priv);
if (ret < 0)
{
break;
}
at32can_putreg(priv, AT32_CAN_BTMG_OFFSET, regval);
ret = at32can_exitinitmode(priv);
}
break;
#ifdef CONFIG_CAN_EXTID
* Description: Add an address filter for a extended 29 bit
* address.
* Argument: A reference to struct canioc_extfilter_s
* Returned Value: A non-negative filter ID is returned on success.
* Otherwise -1 (ERROR) is returned with the errno
* variable set to indicate the nature of the error.
*/
case CANIOC_ADD_EXTFILTER:
{
DEBUGASSERT(arg != 0);
ret = at32can_addextfilter(priv,
(struct canioc_extfilter_s *)arg);
}
break;
* Description: Remove an address filter for a standard 29 bit
* address.
* Argument: The filter index previously returned by the
* CANIOC_ADD_EXTFILTER command
* 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.
*/
case CANIOC_DEL_EXTFILTER:
{
#if 0
DEBUGASSERT(arg <= priv->config->nextfilters);
#endif
ret = at32can_delextfilter(priv, (int)arg);
}
break;
#endif
* Description: Add an address filter for a standard 11 bit
* address.
* Argument: A reference to struct canioc_stdfilter_s
* Returned Value: A non-negative filter ID is returned on success.
* Otherwise -1 (ERROR) is returned with the errno
* variable set to indicate the nature of the error.
*/
case CANIOC_ADD_STDFILTER:
{
DEBUGASSERT(arg != 0);
ret = at32can_addstdfilter(priv,
(struct canioc_stdfilter_s *)arg);
}
break;
* Description: Remove an address filter for a standard 11 bit
* address.
* Argument: The filter index previously returned by the
* CANIOC_ADD_STDFILTER command
* 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.
*/
case CANIOC_DEL_STDFILTER:
{
#if 0
DEBUGASSERT(arg <= priv->config->nstdfilters);
#endif
ret = at32can_delstdfilter(priv, (int)arg);
}
break;
case CANIOC_SET_NART:
{
uint32_t regval;
ret = at32can_enterinitmode(priv);
if (ret != 0)
{
return ret;
}
regval = at32can_getreg(priv, AT32_CAN_MCTRL_OFFSET);
if (arg == 1)
{
regval |= CAN_MCTRL_PRSFEN;
}
else
{
regval &= ~CAN_MCTRL_PRSFEN;
}
at32can_putreg(priv, AT32_CAN_MCTRL_OFFSET, regval);
return at32can_exitinitmode(priv);
}
break;
case CANIOC_SET_ABOM:
{
uint32_t regval;
ret = at32can_enterinitmode(priv);
if (ret != 0)
{
return ret;
}
regval = at32can_getreg(priv, AT32_CAN_MCTRL_OFFSET);
if (arg == 1)
{
regval |= CAN_MCTRL_AEBOEN;
}
else
{
regval &= ~CAN_MCTRL_AEBOEN;
}
at32can_putreg(priv, AT32_CAN_MCTRL_OFFSET, regval);
return at32can_exitinitmode(priv);
}
break;
default:
canerr("ERROR: Unrecognized command: %04x\n", cmd);
break;
}
return ret;
}
* Name: at32can_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 at32can_remoterequest(struct can_dev_s *dev, uint16_t id)
{
#warning "Remote request not implemented"
return -ENOSYS;
}
* Name: at32can_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 at32can_send(struct can_dev_s *dev,
struct can_msg_s *msg)
{
struct at32_can_s *priv = dev->cd_priv;
uint8_t *ptr;
uint32_t regval;
uint32_t tmp;
int dlc;
int txmb;
caninfo("CAN%" PRIu8 " ID: %" PRIu32 " DLC: %" PRIu8 "\n",
priv->port, (uint32_t)msg->cm_hdr.ch_id, msg->cm_hdr.ch_dlc);
regval = at32can_getreg(priv, AT32_CAN_TSTS_OFFSET);
if (at32can_txmb0empty(regval))
{
txmb = 0;
}
else if (at32can_txmb1empty(regval))
{
txmb = 1;
}
else if (at32can_txmb2empty(regval))
{
txmb = 2;
}
else
{
canerr("ERROR: No available mailbox\n");
return -EBUSY;
}
regval = at32can_getreg(priv, AT32_CAN_TMI_OFFSET(txmb));
regval &= ~(CAN_TMI_TMSR | CAN_TMI_TMFRSEL | CAN_TMI_TMIDSEL |
CAN_TMI_TMEID_MASK | CAN_TMI_TMSID_TMEID_MASK);
at32can_putreg(priv, AT32_CAN_TMI_OFFSET(txmb), regval);
#ifdef CONFIG_CAN_EXTID
regval &= ~CAN_TMI_TMEID_MASK;
if (msg->cm_hdr.ch_extid)
{
DEBUGASSERT(msg->cm_hdr.ch_id < (1 << 29));
regval |= (msg->cm_hdr.ch_id << CAN_TMI_TMEID_SHIFT) | CAN_TMI_TMIDSEL;
}
else
{
DEBUGASSERT(msg->cm_hdr.ch_id < (1 << 11));
regval |= msg->cm_hdr.ch_id << CAN_TMI_TMSID_TMEID_SHIFT;
}
#else
regval |= (((uint32_t) msg->cm_hdr.ch_id << CAN_TMI_TMSID_TMEID_SHIFT) &
CAN_TMI_TMSID_TMEID_MASK);
#endif
#ifdef CONFIG_CAN_USE_RTR
regval |= (msg->cm_hdr.ch_rtr ? CAN_TMI_TMFRSEL : 0);
#endif
at32can_putreg(priv, AT32_CAN_TMI_OFFSET(txmb), regval);
dlc = msg->cm_hdr.ch_dlc;
regval = at32can_getreg(priv, AT32_CAN_TMC_OFFSET(txmb));
regval &= ~(CAN_TMC_TMDTBL_MASK | CAN_TMC_TMTSTEN);
regval |= (uint32_t)dlc << CAN_TMC_TMDTBL_SHIFT;
at32can_putreg(priv, AT32_CAN_TMC_OFFSET(txmb), regval);
ptr = msg->cm_data;
regval = 0;
if (dlc > 0)
{
tmp = (uint32_t)*ptr++;
regval = tmp << CAN_TMDTL_TMDT0_SHIFT;
if (dlc > 1)
{
tmp = (uint32_t)*ptr++;
regval |= tmp << CAN_TMDTL_TMDT1_SHIFT;
if (dlc > 2)
{
tmp = (uint32_t)*ptr++;
regval |= tmp << CAN_TMDTL_TMDT2_SHIFT;
if (dlc > 3)
{
tmp = (uint32_t)*ptr++;
regval |= tmp << CAN_TMDTL_TMDT3_SHIFT;
}
}
}
}
at32can_putreg(priv, AT32_CAN_TMDTL_OFFSET(txmb), regval);
regval = 0;
if (dlc > 4)
{
tmp = (uint32_t)*ptr++;
regval = tmp << CAN_TMDTH_TMDT4_SHIFT;
if (dlc > 5)
{
tmp = (uint32_t)*ptr++;
regval |= tmp << CAN_TMDTH_TMDT5_SHIFT;
if (dlc > 6)
{
tmp = (uint32_t)*ptr++;
regval |= tmp << CAN_TMDTH_TMDT6_SHIFT;
if (dlc > 7)
{
tmp = (uint32_t)*ptr++;
regval |= tmp << CAN_TMDTH_TMDT7_SHIFT;
}
}
}
}
at32can_putreg(priv, AT32_CAN_TMDTH_OFFSET(txmb), regval);
regval = at32can_getreg(priv, AT32_CAN_INTEN_OFFSET);
regval |= CAN_INTEN_TCIEN;
at32can_putreg(priv, AT32_CAN_INTEN_OFFSET, regval);
regval = at32can_getreg(priv, AT32_CAN_TMI_OFFSET(txmb));
regval |= CAN_TMI_TMSR;
at32can_putreg(priv, AT32_CAN_TMI_OFFSET(txmb), regval);
at32can_dumpmbregs(priv, "After send");
return OK;
}
* Name: at32can_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 at32can_txready(struct can_dev_s *dev)
{
struct at32_can_s *priv = dev->cd_priv;
uint32_t regval;
regval = at32can_getreg(priv, AT32_CAN_TSTS_OFFSET);
caninfo("CAN%" PRIu8 " TSTS: %08" PRIx32 "\n", priv->port, regval);
return at32can_txmb0empty(regval) || at32can_txmb1empty(regval) ||
at32can_txmb2empty(regval);
}
* Name: at32can_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 at32can_txempty(struct can_dev_s *dev)
{
struct at32_can_s *priv = dev->cd_priv;
uint32_t regval;
regval = at32can_getreg(priv, AT32_CAN_TSTS_OFFSET);
caninfo("CAN%" PRIu8 " TSTS: %08" PRIx32 "\n", priv->port, regval);
return at32can_txmb0empty(regval) && at32can_txmb1empty(regval) &&
at32can_txmb2empty(regval);
}
* Name: at32can_rxinterrupt
*
* Description:
* CAN RX FIFO 0/1 interrupt handler
*
* Input Parameters:
* irq - The IRQ number of the interrupt.
* context - The register state save array at the time of the interrupt.
* rxmb - The RX mailbox number.
*
* Returned Value:
* Zero on success; a negated errno on failure
*
****************************************************************************/
static int at32can_rxinterrupt(struct can_dev_s *dev, int rxmb)
{
struct at32_can_s *priv;
struct can_hdr_s hdr;
uint8_t data[CAN_MAXDATALEN];
uint32_t regval;
int npending;
int ret;
DEBUGASSERT(dev != NULL && dev->cd_priv != NULL);
priv = dev->cd_priv;
regval = at32can_getreg(priv, AT32_CAN_RF_OFFSET(rxmb));
npending = (regval & CAN_RF_RFMN_MASK) >> CAN_RF_RFMN_SHIFT;
if (npending < 1)
{
canwarn("WARNING: No messages pending\n");
return OK;
}
if (rxmb == 0)
{
at32can_dumpmbregs(priv, "RX0 interrupt");
}
else
{
at32can_dumpmbregs(priv, "RX1 interrupt");
}
regval = at32can_getreg(priv, AT32_CAN_RFI_OFFSET(rxmb));
#ifdef CONFIG_CAN_EXTID
if ((regval & CAN_RDI_RFIDI) != 0)
{
hdr.ch_id = \
(regval & CAN_RDI_RFEID_MASK) >> CAN_RDI_RFEID_SHIFT;
hdr.ch_extid = true;
}
else
{
hdr.ch_id = \
(regval & CAN_RDI_RFSID_RFEID_MASK) >> CAN_RDI_RFSID_RFEID_SHIFT;
hdr.ch_extid = false;
}
#else
if ((regval & CAN_RDI_RFIDI) != 0)
{
canerr("ERROR: Received message with extended identifier. Dropped\n");
ret = -ENOSYS;
goto errout;
}
hdr.ch_id = \
(regval & CAN_RDI_RFSID_RFEID_MASK) >> CAN_RDI_RFSID_RFEID_SHIFT;
#endif
#ifdef CONFIG_CAN_ERRORS
hdr.ch_error = 0;
#endif
hdr.ch_tcf = 0;
hdr.ch_rtr = (regval & CAN_RDI_RFFRI) != 0;
regval = at32can_getreg(priv, AT32_CAN_RFC_OFFSET(rxmb));
hdr.ch_dlc = (regval & CAN_RFC_RFDTL_MASK) >> CAN_RFC_RFDTL_SHIFT;
regval = at32can_getreg(priv, AT32_CAN_RFDTL_OFFSET(rxmb));
data[0] = (regval & CAN_RFDTL_RFDT0_MASK) >> CAN_RFDTL_RFDT0_SHIFT;
data[1] = (regval & CAN_RFDTL_RFDT1_MASK) >> CAN_RFDTL_RFDT1_SHIFT;
data[2] = (regval & CAN_RFDTL_RFDT2_MASK) >> CAN_RFDTL_RFDT2_SHIFT;
data[3] = (regval & CAN_RFDTL_RFDT3_MASK) >> CAN_RFDTL_RFDT3_SHIFT;
regval = at32can_getreg(priv, AT32_CAN_RFDTH_OFFSET(rxmb));
data[4] = (regval & CAN_RFDTH_RFDT4_MASK) >> CAN_RFDTH_RFDT4_SHIFT;
data[5] = (regval & CAN_RFDTH_RFDT5_MASK) >> CAN_RFDTH_RFDT5_SHIFT;
data[6] = (regval & CAN_RFDTH_RFDT6_MASK) >> CAN_RFDTH_RFDT6_SHIFT;
data[7] = (regval & CAN_RFDTH_RFDT7_MASK) >> CAN_RFDTH_RFDT7_SHIFT;
ret = can_receive(dev, &hdr, data);
#ifndef CONFIG_CAN_EXTID
errout:
#endif
regval = at32can_getreg(priv, AT32_CAN_RF_OFFSET(rxmb));
regval |= CAN_RF_RFR;
at32can_putreg(priv, AT32_CAN_RF_OFFSET(rxmb), regval);
return ret;
}
* Name: at32can_rx0interrupt
*
* Description:
* CAN RX FIFO 0 interrupt handler
*
* Input Parameters:
* irq - The IRQ number of the interrupt.
* context - The register state save array at the time of the interrupt.
*
* Returned Value:
* Zero on success; a negated errno on failure
*
****************************************************************************/
static int at32can_rx0interrupt(int irq, void *context, void *arg)
{
struct can_dev_s *dev = (struct can_dev_s *)arg;
return at32can_rxinterrupt(dev, 0);
}
* Name: at32can_rx1interrupt
*
* Description:
* CAN RX FIFO 1 interrupt handler
*
* Input Parameters:
* irq - The IRQ number of the interrupt.
* context - The register state save array at the time of the interrupt.
*
* Returned Value:
* Zero on success; a negated errno on failure
*
****************************************************************************/
static int at32can_rx1interrupt(int irq, void *context, void *arg)
{
struct can_dev_s *dev = (struct can_dev_s *)arg;
return at32can_rxinterrupt(dev, 1);
}
* Name: at32can_txinterrupt
*
* Description:
* CAN TX mailbox complete interrupt handler
*
* Input Parameters:
* irq - The IRQ number of the interrupt.
* context - The register state save array at the time of the interrupt.
*
* Returned Value:
* Zero on success; a negated errno on failure
*
****************************************************************************/
static int at32can_txinterrupt(int irq, void *context, void *arg)
{
struct can_dev_s *dev = (struct can_dev_s *)arg;
struct at32_can_s *priv;
uint32_t regval;
DEBUGASSERT(dev != NULL && dev->cd_priv != NULL);
priv = dev->cd_priv;
regval = at32can_getreg(priv, AT32_CAN_TSTS_OFFSET);
if ((regval & CAN_TSTS_TM0TCF) != 0)
{
* ALST0 and TERR0) for Mailbox 0.
*/
at32can_putreg(priv, AT32_CAN_TSTS_OFFSET, CAN_TSTS_TM0TCF);
can_txdone(dev);
}
if ((regval & CAN_TSTS_TM1TCF) != 0)
{
* ALST1 and TERR1) for Mailbox 1.
*/
at32can_putreg(priv, AT32_CAN_TSTS_OFFSET, CAN_TSTS_TM1TCF);
can_txdone(dev);
}
if ((regval & CAN_TSTS_TM2TCF) != 0)
{
* ALST2 and TERR2) for Mailbox 2.
*/
at32can_putreg(priv, AT32_CAN_TSTS_OFFSET, CAN_TSTS_TM2TCF);
can_txdone(dev);
}
return OK;
}
#ifdef CONFIG_CAN_ERRORS
* Name: at32can_sceinterrupt
*
* Description:
* CAN status change interrupt handler
*
* Input Parameters:
* irq - The IRQ number of the interrupt.
* context - The register state save array at the time of the interrupt.
*
* Returned Value:
* Zero on success; a negated errno on failure
*
****************************************************************************/
static int at32can_sceinterrupt(int irq, void *context, void *arg)
{
struct can_dev_s *dev = (struct can_dev_s *)arg;
struct at32_can_s *priv = NULL;
struct can_hdr_s hdr;
uint32_t regval = 0;
uint16_t errbits = 0;
uint8_t data[CAN_ERROR_DLC];
int ret = OK;
DEBUGASSERT(dev != NULL && dev->cd_priv != NULL);
priv = dev->cd_priv;
regval = at32can_getreg(priv, AT32_CAN_MSTS_OFFSET);
if (regval & CAN_MSTS_EOIF)
{
errbits = 0;
memset(data, 0, sizeof(data));
regval = at32can_getreg(priv, AT32_CAN_ESTS_OFFSET);
if (regval & CAN_ESTS_EAF)
{
data[1] |= (CAN_ERROR1_RXWARNING | CAN_ERROR1_TXWARNING);
errbits |= CAN_ERROR_CONTROLLER;
}
if (regval & CAN_ESTS_EPF)
{
data[1] |= (CAN_ERROR1_RXPASSIVE | CAN_ERROR1_TXPASSIVE);
errbits |= CAN_ERROR_CONTROLLER;
}
if (regval & CAN_ESTS_BOF)
{
errbits |= CAN_ERROR_BUSOFF;
}
if (regval & CAN_ESTS_ETR_MASK)
{
if (regval & CAN_ESTS_ETR_STUFF)
{
errbits |= CAN_ERROR_PROTOCOL;
data[2] |= CAN_ERROR2_STUFF;
}
else if (regval & CAN_ESTS_ETR_FORM)
{
errbits |= CAN_ERROR_PROTOCOL;
data[2] |= CAN_ERROR2_FORM;
}
else if (regval & CAN_ESTS_ETR_ACK)
{
errbits |= CAN_ERROR_NOACK;
}
else if (regval & CAN_ESTS_ETR_BREC)
{
errbits |= CAN_ERROR_PROTOCOL;
data[2] |= CAN_ERROR2_BIT1;
}
else if (regval & CAN_ESTS_ETR_BDOM)
{
errbits |= CAN_ERROR_PROTOCOL;
data[2] |= CAN_ERROR2_BIT0;
}
else if (regval & CAN_ESTS_ETR_CRC)
{
errbits |= CAN_ERROR_PROTOCOL;
data[3] |= CAN_ERROR3_CRCSEQ;
}
}
regval = at32can_getreg(priv, AT32_CAN_TSTS_OFFSET);
if (regval & CAN_TSTS_TM0ALF || regval & CAN_TSTS_TM1ALF ||
regval & CAN_TSTS_TM2ALF)
{
errbits |= CAN_ERROR_LOSTARB;
}
at32can_putreg(priv, AT32_CAN_TSTS_OFFSET, regval);
at32can_putreg(priv, AT32_CAN_MSTS_OFFSET, CAN_MSTS_EOIF);
}
if (errbits != 0)
{
canerr("ERROR: errbits = %08" PRIx16 "\n", errbits);
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;
ret = can_receive(dev, &hdr, data);
if (ret < 0)
{
canerr("ERROR: can_receive failed: %d\n", ret);
}
}
return ret;
}
#endif
* Name: at32can_bittiming
*
* Description:
* Set the CAN bit timing register (BTR) based on the configured BAUD.
*
* "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 * Tpclk1
* baud = Fpclk1 / (brp * (1 + ts1 + ts2))
*
* Where:
* Tpclk1 is the period of the APB1 clock (PCLK1).
*
* Input Parameters:
* priv - A reference to the CAN block status
*
* Returned Value:
* Zero on success; a negated errno on failure
*
****************************************************************************/
static int at32can_bittiming(struct at32_can_s *priv)
{
uint32_t tmp;
uint32_t brp;
uint32_t ts1;
uint32_t ts2;
caninfo("CAN%" PRIu8 " PCLK1: %lu baud: %" PRIu32 "\n",
priv->port, (unsigned long) AT32_PCLK1_FREQUENCY, priv->baud);
*
* bit_time = Tq*(ts1 + ts2 + 1)
* nquanta = bit_time / Tq
* nquanta = (ts1 + ts2 + 1)
*
* bit_time = brp * Tpclk1 * (ts1 + ts2 + 1)
* nquanta = bit_time / brp / Tpclk1
* = PCLK1 / baud / brp
* brp = PCLK1 / baud / nquanta;
*
* Example:
* PCLK1 = 42,000,000 baud = 1,000,000 nquanta = 14 : brp = 3
* PCLK1 = 42,000,000 baud = 700,000 nquanta = 14 : brp = 4
*/
tmp = AT32_PCLK1_FREQUENCY / priv->baud;
if (tmp < CAN_BIT_QUANTA)
{
* (PCLCK1 / baud) to meet our goal. brp must be one and we need
* make some reasonable guesses about ts1 and ts2.
*/
brp = 1;
ts1 = (tmp - 1) >> 1;
ts2 = tmp - ts1 - 1;
if (ts1 == ts2 && ts1 > 1 && ts2 < CAN_BTR_TSEG2_MAX)
{
ts1--;
ts2++;
}
}
* ts2 is CONFIG_AT32_CAN_TSEG2 and we calculate brp to achieve
* CAN_BIT_QUANTA quanta in the bit time
*/
else
{
ts1 = CONFIG_AT32_CAN_TSEG1;
ts2 = CONFIG_AT32_CAN_TSEG2;
brp = (tmp + (CAN_BIT_QUANTA / 2)) / CAN_BIT_QUANTA;
DEBUGASSERT(brp >= 1 && brp <= CAN_BTR_BRP_MAX);
}
caninfo("TS1: %" PRIu32 " TS2: %" PRIu32 " BRP: %" PRIu32 "\n",
ts1, ts2, brp);
* things. Unless loopback mode is enabled, it:
*
* - Disables silent mode.
* - Disables loopback mode.
*
* NOTE that for the time being, SJW is set to 1 just because I don't
* know any better.
*/
tmp = ((brp - 1) << CAN_BTMG_BRDIV_SHIFT) | \
((ts1 - 1) << CAN_BTMG_BTS1_SHIFT) | \
((ts2 - 1) << CAN_BTMG_BTS2_SHIFT) | \
((1 - 1) << CAN_BTMG_RSAW_SHIFT);
#ifdef CONFIG_CAN_LOOPBACK
tmp |= CAN_BTMG_LBEN;
#endif
at32can_putreg(priv, AT32_CAN_BTMG_OFFSET, tmp);
return OK;
}
* Name: at32can_enterinitmode
*
* Description:
* Put the CAN cell in Initialization mode. This only disconnects the CAN
* peripheral, no registers are changed. The initialization mode is
* required to change the baud rate.
*
* Input Parameters:
* priv - A pointer to the private data structure for this CAN block
*
* Returned Value:
* Zero on success; a negated errno value on failure.
*
****************************************************************************/
static int at32can_enterinitmode(struct at32_can_s *priv)
{
uint32_t regval;
volatile uint32_t timeout;
caninfo("CAN%" PRIu8 "\n", priv->port);
regval = at32can_getreg(priv, AT32_CAN_MCTRL_OFFSET);
regval |= CAN_MCTRL_FZEN;
at32can_putreg(priv, AT32_CAN_MCTRL_OFFSET, regval);
for (timeout = INAK_TIMEOUT; timeout > 0; timeout--)
{
regval = at32can_getreg(priv, AT32_CAN_MSTS_OFFSET);
if ((regval & CAN_MSTS_FZC) != 0)
{
break;
}
}
if (timeout < 1)
{
canerr("ERROR: Timed out waiting to enter initialization mode\n");
return -ETIMEDOUT;
}
return OK;
}
* Name: at32can_exitinitmode
*
* Description:
* Put the CAN cell out of the Initialization mode (to Normal mode)
*
* Input Parameters:
* priv - A pointer to the private data structure for this CAN block
*
* Returned Value:
* Zero on success; a negated errno value on failure.
*
****************************************************************************/
static int at32can_exitinitmode(struct at32_can_s *priv)
{
uint32_t regval;
volatile uint32_t timeout;
regval = at32can_getreg(priv, AT32_CAN_MCTRL_OFFSET);
regval &= ~CAN_MCTRL_FZEN;
at32can_putreg(priv, AT32_CAN_MCTRL_OFFSET, regval);
for (timeout = INAK_TIMEOUT; timeout > 0; timeout--)
{
regval = at32can_getreg(priv, AT32_CAN_MSTS_OFFSET);
if ((regval & CAN_MSTS_FZC) == 0)
{
break;
}
}
if (timeout < 1)
{
canerr("ERROR: Timed out waiting to exit initialization mode: %08"
PRIx32 "\n", regval);
return -ETIMEDOUT;
}
return OK;
}
* Name: at32can_cellinit
*
* Description:
* CAN cell initialization
*
* Input Parameters:
* priv - A pointer to the private data structure for this CAN block
*
* Returned Value:
* Zero on success; a negated errno value on failure.
*
****************************************************************************/
static int at32can_cellinit(struct at32_can_s *priv)
{
uint32_t regval;
int ret;
caninfo("CAN%" PRIu8 "\n", priv->port);
regval = at32can_getreg(priv, AT32_CAN_MCTRL_OFFSET);
regval &= ~CAN_MCTRL_DZEN;
at32can_putreg(priv, AT32_CAN_MCTRL_OFFSET, regval);
ret = at32can_enterinitmode(priv);
if (ret != 0)
{
return ret;
}
*
* - Time triggered communication mode
* - Automatic bus-off management
* - Automatic wake-up mode
* - No automatic retransmission
* - Receive FIFO locked mode
*
* Enable:
*
* - Transmit FIFO priority
*/
regval = at32can_getreg(priv, AT32_CAN_MCTRL_OFFSET);
regval &= ~(CAN_MCTRL_MDRSEL | CAN_MCTRL_PRSFEN | CAN_MCTRL_AEDEN |
CAN_MCTRL_AEBOEN | CAN_MCTRL_TTCEN);
regval |= CAN_MCTRL_MMSSR;
at32can_putreg(priv, AT32_CAN_MCTRL_OFFSET, regval);
ret = at32can_bittiming(priv);
if (ret < 0)
{
canerr("ERROR: Failed to set bit timing: %d\n", ret);
return ret;
}
return at32can_exitinitmode(priv);
}
* Name: at32can_filterinit
*
* Description:
* CAN filter initialization. CAN filters are not currently used by this
* driver. The CAN filters can be configured in a different way:
*
* 1. As a match of specific IDs in a list (IdList mode), or as
* 2. And ID and a mask (IdMask mode).
*
* Filters can also be configured as:
*
* 3. 16- or 32-bit. The advantage of 16-bit filters is that you get
* more filters; The advantage of 32-bit filters is that you get
* finer control of the filtering.
*
* One filter is set up for each CAN. The filter resources are shared
* between the two CAN modules: CAN1 uses only filter 0 (but reserves
* 0 through CAN_NFILTERS/2-1); CAN2 uses only filter CAN_NFILTERS/2
* (but reserves CAN_NFILTERS/2 through CAN_NFILTERS-1).
*
* 32-bit IdMask mode is configured. However, both the ID and the MASK
* are set to zero thus suppressing all filtering because anything masked
* with zero matches zero.
*
* Input Parameters:
* priv - A pointer to the private data structure for this CAN block
*
* Returned Value:
* Zero on success; a negated errno value on failure.
*
****************************************************************************/
static int at32can_filterinit(struct at32_can_s *priv)
{
uint32_t regval;
uint32_t bitmask;
caninfo("CAN%" PRIu8 " filter: %" PRIu8 "\n", priv->port, priv->filter);
bitmask = (uint32_t)1 << priv->filter;
regval = at32can_getfreg(priv, AT32_CAN_FCTRL_OFFSET);
regval |= CAN_FCTRL_FCS;
at32can_putfreg(priv, AT32_CAN_FCTRL_OFFSET, regval);
regval = at32can_getfreg(priv, AT32_CAN_FACFG_OFFSET);
regval &= ~bitmask;
at32can_putfreg(priv, AT32_CAN_FACFG_OFFSET, regval);
regval = at32can_getfreg(priv, AT32_CAN_FSCFG_OFFSET);
regval |= bitmask;
at32can_putfreg(priv, AT32_CAN_FSCFG_OFFSET, regval);
* Each filter bank is composed of two 32-bit registers, CAN_FiR:
*/
at32can_putfreg(priv, AT32_CAN_FBF_OFFSET(priv->filter, 1), 0);
at32can_putfreg(priv, AT32_CAN_FBF_OFFSET(priv->filter, 2), 0);
regval = at32can_getfreg(priv, AT32_CAN_FMCFG_OFFSET);
regval &= ~bitmask;
at32can_putfreg(priv, AT32_CAN_FMCFG_OFFSET, regval);
regval = at32can_getfreg(priv, AT32_CAN_FRF_OFFSET);
regval &= ~bitmask;
at32can_putfreg(priv, AT32_CAN_FRF_OFFSET, regval);
regval = at32can_getfreg(priv, AT32_CAN_FACFG_OFFSET);
regval |= bitmask;
at32can_putfreg(priv, AT32_CAN_FACFG_OFFSET, regval);
regval = at32can_getfreg(priv, AT32_CAN_FCTRL_OFFSET);
regval &= ~CAN_FCTRL_FCS;
at32can_putfreg(priv, AT32_CAN_FCTRL_OFFSET, regval);
return OK;
}
* Name: at32can_addextfilter
*
* Description:
* Add a filter for extended CAN IDs
*
* Input Parameters:
* priv - A pointer to the private data structure for this CAN block
* arg - A pointer to a structure describing the filter
*
* Returned Value:
* A non-negative filter ID is returned on success.
* Otherwise -1 (ERROR) is returned with the errno
* set to indicate the nature of the error.
*
****************************************************************************/
#ifdef CONFIG_CAN_EXTID
static int at32can_addextfilter(struct at32_can_s *priv,
struct canioc_extfilter_s *arg)
{
return -ENOTTY;
}
#endif
* Name: at32can_delextfilter
*
* Description:
* Remove a filter for extended CAN IDs
*
* Input Parameters:
* priv - A pointer to the private data structure for this CAN block
* arg - The filter index previously returned by the
* CANIOC_ADD_EXTFILTER command
*
* Returned Value:
* Zero (OK) is returned on success. Otherwise -1 (ERROR)
* returned with the errno variable set to indicate the
* of the error.
*
****************************************************************************/
#ifdef CONFIG_CAN_EXTID
static int at32can_delextfilter(struct at32_can_s *priv, int arg)
{
return -ENOTTY;
}
#endif
* Name: at32can_addstdfilter
*
* Description:
* Add a filter for standard CAN IDs
*
* Input Parameters:
* priv - A pointer to the private data structure for this CAN block
* arg - A pointer to a structure describing the filter
*
* Returned Value:
* A non-negative filter ID is returned on success.
* Otherwise -1 (ERROR) is returned with the errno
* set to indicate the nature of the error.
*
****************************************************************************/
static int at32can_addstdfilter(struct at32_can_s *priv,
struct canioc_stdfilter_s *arg)
{
return -ENOTTY;
}
* Name: at32can_delstdfilter
*
* Description:
* Remove a filter for standard CAN IDs
*
* Input Parameters:
* priv - A pointer to the private data structure for this CAN block
* arg - The filter index previously returned by the
* CANIOC_ADD_STDFILTER command
*
* Returned Value:
* Zero (OK) is returned on success. Otherwise -1 (ERROR)
* returned with the errno variable set to indicate the
* of the error.
*
****************************************************************************/
static int at32can_delstdfilter(struct at32_can_s *priv, int arg)
{
return -ENOTTY;
}
* Name: at32can_txmb0empty
*
* Input Parameters:
* tsr_regval - value of CAN transmit status register
*
* Returned Value:
* Returns true if mailbox 0 is empty and can be used for sending.
*
****************************************************************************/
static bool at32can_txmb0empty(uint32_t tsr_regval)
{
return (tsr_regval & CAN_TSTS_TM0EF) != 0 &&
(tsr_regval & CAN_TSTS_TM0TCF) == 0;
}
* Name: at32can_txmb1empty
*
* Input Parameters:
* tsr_regval - value of CAN transmit status register
*
* Returned Value:
* Returns true if mailbox 1 is empty and can be used for sending.
*
****************************************************************************/
static bool at32can_txmb1empty(uint32_t tsr_regval)
{
return (tsr_regval & CAN_TSTS_TM1EF) != 0 &&
(tsr_regval & CAN_TSTS_TM1TCF) == 0;
}
* Name: at32can_txmb2empty
*
* Input Parameters:
* tsr_regval - value of CAN transmit status register
*
* Returned Value:
* Returns true if mailbox 2 is empty and can be used for sending.
*
****************************************************************************/
static bool at32can_txmb2empty(uint32_t tsr_regval)
{
return (tsr_regval & CAN_TSTS_TM2EF) != 0 &&
(tsr_regval & CAN_TSTS_TM2TCF) == 0;
}
* Public Functions
****************************************************************************/
* Name: at32_caninitialize
*
* Description:
* Initialize the selected CAN port
*
* Input Parameters:
* Port number (for hardware that has multiple CAN interfaces)
*
* Returned Value:
* Valid CAN device structure reference on success; a NULL on failure
*
****************************************************************************/
struct can_dev_s *at32_caninitialize(int port)
{
struct can_dev_s *dev = NULL;
caninfo("CAN%" PRIu8 "\n", port);
* by at32_clockconfig() early in the reset sequence.
*/
#ifdef CONFIG_AT32_CAN1
if (port == 1)
{
dev = &g_can1dev;
* file must have been disambiguated in the board.h file.
*/
at32_configgpio(GPIO_CAN1_RX);
at32_configgpio(GPIO_CAN1_TX);
}
else
#endif
#ifdef CONFIG_AT32_CAN2
if (port == 2)
{
dev = &g_can2dev;
* file must have been disambiguated in the board.h file.
*/
at32_configgpio(GPIO_CAN2_RX);
at32_configgpio(GPIO_CAN2_TX);
}
else
#endif
{
canerr("ERROR: Unsupported port %d\n", port);
return NULL;
}
return dev;
}
#endif