972a260391
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2539 lines
72 KiB
C
2539 lines
72 KiB
C
/****************************************************************************
|
|
* arch/arm/src/stm32/stm32_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 "stm32.h"
|
|
#include "stm32_rcc.h"
|
|
#include "stm32_can.h"
|
|
|
|
#if defined(CONFIG_CAN) && \
|
|
(defined(CONFIG_STM32_CAN1) || defined(CONFIG_STM32_CAN2))
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/* Delays *******************************************************************/
|
|
|
|
/* Time out for INAK bit */
|
|
|
|
#define INAK_TIMEOUT 65535
|
|
|
|
/* Bit timing ***************************************************************/
|
|
|
|
#define CAN_BIT_QUANTA (CONFIG_STM32_CAN_TSEG1 + CONFIG_STM32_CAN_TSEG2 + 1)
|
|
|
|
#ifndef CONFIG_DEBUG_CAN_INFO
|
|
# undef CONFIG_STM32_CAN_REGDEBUG
|
|
#endif
|
|
|
|
/* CAN error interrupts */
|
|
|
|
#ifdef CONFIG_CAN_ERRORS
|
|
# define STM32_CAN_ERRINT (CAN_IER_LECIE | CAN_IER_ERRIE | \
|
|
CAN_IER_BOFIE | CAN_IER_EPVIE | \
|
|
CAN_IER_EWGIE)
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
struct stm32_can_s
|
|
{
|
|
uint8_t port; /* CAN port number (1 or 2) */
|
|
uint8_t canrx[2]; /* CAN RX FIFO 0/1 IRQ number */
|
|
uint8_t cantx; /* CAN TX IRQ number */
|
|
#ifdef CONFIG_CAN_ERRORS
|
|
uint8_t cansce; /* CAN SCE IRQ number */
|
|
#endif
|
|
uint8_t filter; /* Filter number */
|
|
uint32_t base; /* Base address of the CAN control registers */
|
|
uint32_t fbase; /* Base address of the CAN filter registers */
|
|
uint32_t baud; /* Configured baud */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
/* CAN Register access */
|
|
|
|
static uint32_t stm32can_getreg(struct stm32_can_s *priv,
|
|
int offset);
|
|
static uint32_t stm32can_getfreg(struct stm32_can_s *priv,
|
|
int offset);
|
|
static void stm32can_putreg(struct stm32_can_s *priv, int offset,
|
|
uint32_t value);
|
|
static void stm32can_putfreg(struct stm32_can_s *priv, int offset,
|
|
uint32_t value);
|
|
#ifdef CONFIG_STM32_CAN_REGDEBUG
|
|
static void stm32can_dumpctrlregs(struct stm32_can_s *priv,
|
|
const char *msg);
|
|
static void stm32can_dumpmbregs(struct stm32_can_s *priv,
|
|
const char *msg);
|
|
static void stm32can_dumpfiltregs(struct stm32_can_s *priv,
|
|
const char *msg);
|
|
#else
|
|
# define stm32can_dumpctrlregs(priv,msg)
|
|
# define stm32can_dumpmbregs(priv,msg)
|
|
# define stm32can_dumpfiltregs(priv,msg)
|
|
#endif
|
|
|
|
/* Filtering (todo) */
|
|
|
|
#ifdef CONFIG_CAN_EXTID
|
|
static int stm32can_addextfilter(struct stm32_can_s *priv,
|
|
struct canioc_extfilter_s *arg);
|
|
static int stm32can_delextfilter(struct stm32_can_s *priv,
|
|
int arg);
|
|
#endif
|
|
static int stm32can_addstdfilter(struct stm32_can_s *priv,
|
|
struct canioc_stdfilter_s *arg);
|
|
static int stm32can_delstdfilter(struct stm32_can_s *priv,
|
|
int arg);
|
|
|
|
/* CAN driver methods */
|
|
|
|
static void stm32can_reset(struct can_dev_s *dev);
|
|
static int stm32can_setup(struct can_dev_s *dev);
|
|
static void stm32can_shutdown(struct can_dev_s *dev);
|
|
static void stm32can_rxint(struct can_dev_s *dev, bool enable);
|
|
static void stm32can_txint(struct can_dev_s *dev, bool enable);
|
|
static int stm32can_ioctl(struct can_dev_s *dev, int cmd,
|
|
unsigned long arg);
|
|
static int stm32can_remoterequest(struct can_dev_s *dev,
|
|
uint16_t id);
|
|
static int stm32can_send(struct can_dev_s *dev,
|
|
struct can_msg_s *msg);
|
|
static bool stm32can_txready(struct can_dev_s *dev);
|
|
static bool stm32can_txempty(struct can_dev_s *dev);
|
|
|
|
#ifdef CONFIG_CAN_ERRORS
|
|
static void stm32can_errint(struct can_dev_s *dev, bool enable);
|
|
#endif
|
|
|
|
/* CAN interrupt handling */
|
|
|
|
static int stm32can_rxinterrupt(struct can_dev_s *dev, int rxmb);
|
|
static int stm32can_rx0interrupt(int irq, void *context, void *arg);
|
|
static int stm32can_rx1interrupt(int irq, void *context, void *arg);
|
|
static int stm32can_txinterrupt(int irq, void *context, void *arg);
|
|
#ifdef CONFIG_CAN_ERRORS
|
|
static int stm32can_sceinterrupt(int irq, void *context, void *arg);
|
|
#endif
|
|
|
|
/* Initialization */
|
|
|
|
static int stm32can_enterinitmode(struct stm32_can_s *priv);
|
|
static int stm32can_exitinitmode(struct stm32_can_s *priv);
|
|
static int stm32can_bittiming(struct stm32_can_s *priv);
|
|
static int stm32can_cellinit(struct stm32_can_s *priv);
|
|
static int stm32can_filterinit(struct stm32_can_s *priv);
|
|
|
|
/* TX mailbox status */
|
|
|
|
static bool stm32can_txmb0empty(uint32_t tsr_regval);
|
|
static bool stm32can_txmb1empty(uint32_t tsr_regval);
|
|
static bool stm32can_txmb2empty(uint32_t tsr_regval);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const struct can_ops_s g_canops =
|
|
{
|
|
.co_reset = stm32can_reset,
|
|
.co_setup = stm32can_setup,
|
|
.co_shutdown = stm32can_shutdown,
|
|
.co_rxint = stm32can_rxint,
|
|
.co_txint = stm32can_txint,
|
|
.co_ioctl = stm32can_ioctl,
|
|
.co_remoterequest = stm32can_remoterequest,
|
|
.co_send = stm32can_send,
|
|
.co_txready = stm32can_txready,
|
|
.co_txempty = stm32can_txempty,
|
|
};
|
|
|
|
#ifdef CONFIG_STM32_CAN1
|
|
static struct stm32_can_s g_can1priv =
|
|
{
|
|
.port = 1,
|
|
.canrx =
|
|
{
|
|
STM32_IRQ_CAN1RX0,
|
|
STM32_IRQ_CAN1RX1,
|
|
},
|
|
.cantx = STM32_IRQ_CAN1TX,
|
|
#ifdef CONFIG_CAN_ERRORS
|
|
.cansce = STM32_IRQ_CAN1SCE,
|
|
#endif
|
|
.filter = 0,
|
|
.base = STM32_CAN1_BASE,
|
|
.fbase = STM32_CAN1_BASE,
|
|
.baud = CONFIG_STM32_CAN1_BAUD,
|
|
};
|
|
|
|
static struct can_dev_s g_can1dev =
|
|
{
|
|
.cd_ops = &g_canops,
|
|
.cd_priv = &g_can1priv,
|
|
};
|
|
#endif
|
|
|
|
#ifdef CONFIG_STM32_CAN2
|
|
static struct stm32_can_s g_can2priv =
|
|
{
|
|
.port = 2,
|
|
.canrx =
|
|
{
|
|
STM32_IRQ_CAN2RX0,
|
|
STM32_IRQ_CAN2RX1,
|
|
},
|
|
.cantx = STM32_IRQ_CAN2TX,
|
|
#ifdef CONFIG_CAN_ERRORS
|
|
.cansce = STM32_IRQ_CAN2SCE,
|
|
#endif
|
|
.filter = CAN_NFILTERS / 2,
|
|
.base = STM32_CAN2_BASE,
|
|
.fbase = STM32_CAN1_BASE,
|
|
.baud = CONFIG_STM32_CAN2_BAUD,
|
|
};
|
|
|
|
static struct can_dev_s g_can2dev =
|
|
{
|
|
.cd_ops = &g_canops,
|
|
.cd_priv = &g_can2priv,
|
|
};
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_getreg
|
|
* Name: stm32can_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_STM32_CAN_REGDEBUG
|
|
static uint32_t stm32can_vgetreg(uint32_t addr)
|
|
{
|
|
static uint32_t prevaddr = 0;
|
|
static uint32_t preval = 0;
|
|
static uint32_t count = 0;
|
|
|
|
/* Read the value from the register */
|
|
|
|
uint32_t val = getreg32(addr);
|
|
|
|
/* 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 && val == preval)
|
|
{
|
|
if (count == 0xffffffff || ++count > 3)
|
|
{
|
|
if (count == 4)
|
|
{
|
|
caninfo("...\n");
|
|
}
|
|
|
|
return val;
|
|
}
|
|
}
|
|
|
|
/* 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 %" PRIu32 " more times]\n", count - 3);
|
|
}
|
|
|
|
/* Save the new address, value, and count */
|
|
|
|
prevaddr = addr;
|
|
preval = val;
|
|
count = 1;
|
|
}
|
|
|
|
/* Show the register value read */
|
|
|
|
caninfo("%08" PRIx32 "->%08" PRIx32 "\n", addr, val);
|
|
return val;
|
|
}
|
|
|
|
static uint32_t stm32can_getreg(struct stm32_can_s *priv, int offset)
|
|
{
|
|
return stm32can_vgetreg(priv->base + offset);
|
|
}
|
|
|
|
static uint32_t stm32can_getfreg(struct stm32_can_s *priv, int offset)
|
|
{
|
|
return stm32can_vgetreg(priv->fbase + offset);
|
|
}
|
|
|
|
#else
|
|
static uint32_t stm32can_getreg(struct stm32_can_s *priv, int offset)
|
|
{
|
|
return getreg32(priv->base + offset);
|
|
}
|
|
|
|
static uint32_t stm32can_getfreg(struct stm32_can_s *priv, int offset)
|
|
{
|
|
return getreg32(priv->fbase + offset);
|
|
}
|
|
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_putreg
|
|
* Name: stm32can_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_STM32_CAN_REGDEBUG
|
|
static void stm32can_vputreg(uint32_t addr, uint32_t value)
|
|
{
|
|
/* Show the register value being written */
|
|
|
|
caninfo("%08" PRIx32 "->%08" PRIx32 "\n", addr, val);
|
|
|
|
/* Write the value */
|
|
|
|
putreg32(value, addr);
|
|
}
|
|
|
|
static void stm32can_putreg(struct stm32_can_s *priv, int offset,
|
|
uint32_t value)
|
|
{
|
|
stm32can_vputreg(priv->base + offset, value);
|
|
}
|
|
|
|
static void stm32can_putfreg(struct stm32_can_s *priv, int offset,
|
|
uint32_t value)
|
|
{
|
|
stm32can_vputreg(priv->fbase + offset, value);
|
|
}
|
|
|
|
#else
|
|
static void stm32can_putreg(struct stm32_can_s *priv, int offset,
|
|
uint32_t value)
|
|
{
|
|
putreg32(value, priv->base + offset);
|
|
}
|
|
|
|
static void stm32can_putfreg(struct stm32_can_s *priv, int offset,
|
|
uint32_t value)
|
|
{
|
|
putreg32(value, priv->fbase + offset);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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_STM32_CAN_REGDEBUG
|
|
static void stm32can_dumpctrlregs(struct stm32_can_s *priv,
|
|
const char *msg)
|
|
{
|
|
if (msg)
|
|
{
|
|
caninfo("Control Registers: %s\n", msg);
|
|
}
|
|
else
|
|
{
|
|
caninfo("Control Registers:\n");
|
|
}
|
|
|
|
/* CAN control and status registers */
|
|
|
|
caninfo(" MCR: %08" PRIx32 " MSR: %08" PRIx32 " TSR: %08" PRIx32 "\n",
|
|
getreg32(priv->base + STM32_CAN_MCR_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_MSR_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_TSR_OFFSET));
|
|
|
|
caninfo(" RF0R: %08" PRIx32 " RF1R: %08" PRIx32 "\n",
|
|
getreg32(priv->base + STM32_CAN_RF0R_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_RF1R_OFFSET));
|
|
|
|
caninfo(" IER: %08" PRIx32 " ESR: %08" PRIx32 " BTR: %08" PRIx32 "\n",
|
|
getreg32(priv->base + STM32_CAN_IER_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_ESR_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_BTR_OFFSET));
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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_STM32_CAN_REGDEBUG
|
|
static void stm32can_dumpmbregs(struct stm32_can_s *priv,
|
|
const char *msg)
|
|
{
|
|
if (msg)
|
|
{
|
|
caninfo("Mailbox Registers: %s\n", msg);
|
|
}
|
|
else
|
|
{
|
|
caninfo("Mailbox Registers:\n");
|
|
}
|
|
|
|
/* CAN mailbox registers (3 TX and 2 RX) */
|
|
|
|
caninfo(" TI0R: %08" PRIx32 " TDT0R: %08" PRIx32 " TDL0R: %08"
|
|
PRIx32 " TDH0R: %08" PRIx32 "\n",
|
|
getreg32(priv->base + STM32_CAN_TI0R_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_TDT0R_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_TDL0R_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_TDH0R_OFFSET));
|
|
|
|
caninfo(" TI1R: %08" PRIx32 " TDT1R: %08" PRIx32 " TDL1R: %08"
|
|
PRIx32 " TDH1R: %08" PRIx32 "\n",
|
|
getreg32(priv->base + STM32_CAN_TI1R_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_TDT1R_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_TDL1R_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_TDH1R_OFFSET));
|
|
|
|
caninfo(" TI2R: %08" PRIx32 " TDT2R: %08" PRIx32 " TDL2R: %08"
|
|
PRIx32 " TDH2R: %08" PRIx32 "\n",
|
|
getreg32(priv->base + STM32_CAN_TI2R_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_TDT2R_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_TDL2R_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_TDH2R_OFFSET));
|
|
|
|
caninfo(" RI0R: %08" PRIx32 " RDT0R: %08" PRIx32 " RDL0R: %08"
|
|
PRIx32 " RDH0R: %08" PRIx32 "\n",
|
|
getreg32(priv->base + STM32_CAN_RI0R_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_RDT0R_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_RDL0R_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_RDH0R_OFFSET));
|
|
|
|
caninfo(" RI1R: %08" PRIx32 " RDT1R: %08" PRIx32 " RDL1R: %08"
|
|
PRIx32 " RDH1R: %08" PRIx32 "\n",
|
|
getreg32(priv->base + STM32_CAN_RI1R_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_RDT1R_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_RDL1R_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_RDH1R_OFFSET));
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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_STM32_CAN_REGDEBUG
|
|
static void stm32can_dumpfiltregs(struct stm32_can_s *priv,
|
|
const char *msg)
|
|
{
|
|
int i;
|
|
|
|
if (msg)
|
|
{
|
|
caninfo("Filter Registers: %s\n", msg);
|
|
}
|
|
else
|
|
{
|
|
caninfo("Filter Registers:\n");
|
|
}
|
|
|
|
caninfo(" FMR: %08" PRIx32 " FM1R: %08" PRIx32 " FS1R: %08"
|
|
PRIx32 " FFA1R: %08" PRIx32 " FA1R: %08" PRIx32 "\n",
|
|
getreg32(priv->base + STM32_CAN_FMR_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_FM1R_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_FS1R_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_FFA1R_OFFSET),
|
|
getreg32(priv->base + STM32_CAN_FA1R_OFFSET));
|
|
|
|
for (i = 0; i < CAN_NFILTERS; i++)
|
|
{
|
|
caninfo(" F%dR1: %08" PRIx32 " F%dR2: %08" PRIx32 "\n",
|
|
i, getreg32(priv->base + STM32_CAN_FIR_OFFSET(i, 1)),
|
|
i, getreg32(priv->base + STM32_CAN_FIR_OFFSET(i, 2)));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_reset
|
|
*
|
|
* Description:
|
|
* Reset the CAN device. Called early to initialize the hardware. This
|
|
* function is called, before stm32can_setup() and on error conditions.
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the "upper half" can driver state structure.
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void stm32can_reset(struct can_dev_s *dev)
|
|
{
|
|
struct stm32_can_s *priv = dev->cd_priv;
|
|
uint32_t regval;
|
|
uint32_t regbit = 0;
|
|
irqstate_t flags;
|
|
|
|
caninfo("CAN%" PRIu8 "\n", priv->port);
|
|
|
|
/* Get the bits in the AHB1RSTR register needed to reset this CAN device */
|
|
|
|
#ifdef CONFIG_STM32_CAN1
|
|
if (priv->port == 1)
|
|
{
|
|
regbit = RCC_APB1RSTR_CAN1RST;
|
|
}
|
|
else
|
|
#endif
|
|
#ifdef CONFIG_STM32_CAN2
|
|
if (priv->port == 2)
|
|
{
|
|
regbit = RCC_APB1RSTR_CAN2RST;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
canerr("ERROR: Unsupported port %d\n", priv->port);
|
|
return;
|
|
}
|
|
|
|
/* Disable interrupts momentarily to stop any ongoing CAN event processing
|
|
* and to prevent any concurrent access to the AHB1RSTR register.
|
|
*/
|
|
|
|
flags = enter_critical_section();
|
|
|
|
/* Reset the CAN */
|
|
|
|
regval = getreg32(STM32_RCC_APB1RSTR);
|
|
regval |= regbit;
|
|
putreg32(regval, STM32_RCC_APB1RSTR);
|
|
|
|
regval &= ~regbit;
|
|
putreg32(regval, STM32_RCC_APB1RSTR);
|
|
leave_critical_section(flags);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_setup(struct can_dev_s *dev)
|
|
{
|
|
struct stm32_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
|
|
|
|
/* CAN cell initialization */
|
|
|
|
ret = stm32can_cellinit(priv);
|
|
if (ret < 0)
|
|
{
|
|
canerr("ERROR: CAN%" PRId8 " cell initialization failed: %d\n",
|
|
priv->port, ret);
|
|
return ret;
|
|
}
|
|
|
|
stm32can_dumpctrlregs(priv, "After cell initialization");
|
|
stm32can_dumpmbregs(priv, NULL);
|
|
|
|
/* CAN filter initialization */
|
|
|
|
ret = stm32can_filterinit(priv);
|
|
if (ret < 0)
|
|
{
|
|
canerr("ERROR: CAN%" PRIu8 " filter initialization failed: %d\n",
|
|
priv->port, ret);
|
|
return ret;
|
|
}
|
|
|
|
stm32can_dumpfiltregs(priv, "After filter initialization");
|
|
|
|
/* Attach the CAN RX FIFO 0/1 interrupts and TX interrupts.
|
|
* The others are not used.
|
|
*/
|
|
|
|
ret = irq_attach(priv->canrx[0], stm32can_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], stm32can_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, stm32can_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, stm32can_sceinterrupt, dev);
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: Failed to attach CAN%" PRIu8 " SCE IRQ (%" PRIu8 ")",
|
|
priv->port, priv->cansce);
|
|
return ret;
|
|
}
|
|
|
|
/* Enable CAN error interrupts */
|
|
|
|
stm32can_errint(dev, true);
|
|
#endif
|
|
|
|
/* Enable the interrupts at the NVIC. Interrupts are still disabled in
|
|
* 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: stm32can_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 stm32can_shutdown(struct can_dev_s *dev)
|
|
{
|
|
struct stm32_can_s *priv = dev->cd_priv;
|
|
|
|
caninfo("CAN%" PRIu8 "\n", priv->port);
|
|
|
|
/* Disable the RX FIFO 0/1, TX and SCE interrupts */
|
|
|
|
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
|
|
|
|
/* Detach the RX FIFO 0/1, TX and SCE interrupts */
|
|
|
|
irq_detach(priv->canrx[0]);
|
|
irq_detach(priv->canrx[1]);
|
|
irq_detach(priv->cantx);
|
|
#ifdef CONFIG_CAN_ERRORS
|
|
irq_detach(priv->cansce);
|
|
#endif
|
|
|
|
/* And reset the hardware */
|
|
|
|
stm32can_reset(dev);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_rxint(struct can_dev_s *dev, bool enable)
|
|
{
|
|
struct stm32_can_s *priv = dev->cd_priv;
|
|
uint32_t regval;
|
|
|
|
caninfo("CAN%" PRIu8 " rxint enable: %d\n", priv->port, enable);
|
|
|
|
/* Enable/disable the FIFO 0/1 message pending interrupt */
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_IER_OFFSET);
|
|
if (enable)
|
|
{
|
|
regval |= CAN_IER_FMPIE0 | CAN_IER_FMPIE1;
|
|
}
|
|
else
|
|
{
|
|
regval &= ~(CAN_IER_FMPIE0 | CAN_IER_FMPIE1);
|
|
}
|
|
|
|
stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_txint(struct can_dev_s *dev, bool enable)
|
|
{
|
|
struct stm32_can_s *priv = dev->cd_priv;
|
|
uint32_t regval;
|
|
|
|
caninfo("CAN%" PRIu8 " txint enable: %d\n", priv->port, enable);
|
|
|
|
/* Support only disabling the transmit mailbox interrupt */
|
|
|
|
if (!enable)
|
|
{
|
|
regval = stm32can_getreg(priv, STM32_CAN_IER_OFFSET);
|
|
regval &= ~CAN_IER_TMEIE;
|
|
stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_CAN_ERRORS
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_errint(struct can_dev_s *dev, bool enable)
|
|
{
|
|
struct stm32_can_s *priv = dev->cd_priv;
|
|
uint32_t regval = 0;
|
|
|
|
caninfo("CAN%" PRIu8 " errint enable: %d\n", priv->port, enable);
|
|
|
|
/* Enable/disable the transmit mailbox interrupt */
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_IER_OFFSET);
|
|
if (enable)
|
|
{
|
|
regval |= STM32_CAN_ERRINT;
|
|
}
|
|
else
|
|
{
|
|
regval &= ~STM32_CAN_ERRINT;
|
|
}
|
|
|
|
stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_ioctl(struct can_dev_s *dev, int cmd,
|
|
unsigned long arg)
|
|
{
|
|
struct stm32_can_s *priv;
|
|
int ret = -ENOTTY;
|
|
|
|
caninfo("cmd=%04x arg=%lu\n", cmd, arg);
|
|
|
|
DEBUGASSERT(dev && dev->cd_priv);
|
|
priv = dev->cd_priv;
|
|
|
|
/* 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 regval;
|
|
uint32_t brp;
|
|
|
|
DEBUGASSERT(bt != NULL);
|
|
regval = stm32can_getreg(priv, STM32_CAN_BTR_OFFSET);
|
|
bt->bt_sjw = ((regval & CAN_BTR_SJW_MASK) >>
|
|
CAN_BTR_SJW_SHIFT) + 1;
|
|
bt->bt_tseg1 = ((regval & CAN_BTR_TS1_MASK) >>
|
|
CAN_BTR_TS1_SHIFT) + 1;
|
|
bt->bt_tseg2 = ((regval & CAN_BTR_TS2_MASK) >>
|
|
CAN_BTR_TS2_SHIFT) + 1;
|
|
|
|
brp = ((regval & CAN_BTR_BRP_MASK) >>
|
|
CAN_BTR_BRP_SHIFT) + 1;
|
|
bt->bt_baud = STM32_PCLK1_FREQUENCY /
|
|
(brp * (bt->bt_tseg1 + bt->bt_tseg2 + 1));
|
|
ret = OK;
|
|
}
|
|
break;
|
|
|
|
/* CANIOC_SET_BITTIMING:
|
|
* 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 < STM32_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 = stm32can_getreg(priv, STM32_CAN_BTR_OFFSET);
|
|
|
|
/* Extract bit timing data
|
|
* tmp is in clocks per bit time
|
|
*/
|
|
|
|
tmp = STM32_PCLK1_FREQUENCY / bt->bt_baud;
|
|
|
|
/* This value is dynamic as requested by user */
|
|
|
|
can_bit_quanta = bt->bt_tseg1 + bt->bt_tseg2 + 1;
|
|
|
|
if (tmp < can_bit_quanta)
|
|
{
|
|
/* This timing is not possible */
|
|
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
/* Otherwise, nquanta is can_bit_quanta, ts1 and ts2 are
|
|
* 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);
|
|
|
|
/* Configure bit timing. */
|
|
|
|
regval &= ~(CAN_BTR_BRP_MASK | CAN_BTR_TS1_MASK |
|
|
CAN_BTR_TS2_MASK | CAN_BTR_SJW_MASK);
|
|
regval |= ((brp - 1) << CAN_BTR_BRP_SHIFT) |
|
|
((bt->bt_tseg1 - 1) << CAN_BTR_TS1_SHIFT) |
|
|
((bt->bt_tseg2 - 1) << CAN_BTR_TS2_SHIFT) |
|
|
((bt->bt_sjw - 1) << CAN_BTR_SJW_SHIFT);
|
|
|
|
/* Bit timing can only be configured in init mode. */
|
|
|
|
ret = stm32can_enterinitmode(priv);
|
|
if (ret < 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
stm32can_putreg(priv, STM32_CAN_BTR_OFFSET, regval);
|
|
|
|
ret = stm32can_exitinitmode(priv);
|
|
if (ret >= 0)
|
|
{
|
|
priv->baud = STM32_PCLK1_FREQUENCY /
|
|
(brp * (bt->bt_tseg1 + bt->bt_tseg2 + 1));
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* CANIOC_GET_CONNMODES:
|
|
* 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 = stm32can_getreg(priv, STM32_CAN_BTR_OFFSET);
|
|
|
|
bm->bm_loopback = ((regval & CAN_BTR_LBKM) == CAN_BTR_LBKM);
|
|
bm->bm_silent = ((regval & CAN_BTR_SILM) == CAN_BTR_SILM);
|
|
ret = OK;
|
|
break;
|
|
}
|
|
|
|
/* CANIOC_SET_CONNMODES:
|
|
* 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 = stm32can_getreg(priv, STM32_CAN_BTR_OFFSET);
|
|
|
|
if (bm->bm_loopback)
|
|
{
|
|
regval |= CAN_BTR_LBKM;
|
|
}
|
|
else
|
|
{
|
|
regval &= ~CAN_BTR_LBKM;
|
|
}
|
|
|
|
if (bm->bm_silent)
|
|
{
|
|
regval |= CAN_BTR_SILM;
|
|
}
|
|
else
|
|
{
|
|
regval &= ~CAN_BTR_SILM;
|
|
}
|
|
|
|
/* This register can only be configured in init mode. */
|
|
|
|
ret = stm32can_enterinitmode(priv);
|
|
if (ret < 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
stm32can_putreg(priv, STM32_CAN_BTR_OFFSET, regval);
|
|
|
|
ret = stm32can_exitinitmode(priv);
|
|
}
|
|
break;
|
|
|
|
#ifdef CONFIG_CAN_EXTID
|
|
/* CANIOC_ADD_EXTFILTER:
|
|
* 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 = stm32can_addextfilter(priv,
|
|
(struct canioc_extfilter_s *)arg);
|
|
}
|
|
break;
|
|
|
|
/* CANIOC_DEL_EXTFILTER:
|
|
* 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 /* Unimplemented */
|
|
DEBUGASSERT(arg <= priv->config->nextfilters);
|
|
#endif
|
|
ret = stm32can_delextfilter(priv, (int)arg);
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
/* CANIOC_ADD_STDFILTER:
|
|
* 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 = stm32can_addstdfilter(priv,
|
|
(struct canioc_stdfilter_s *)arg);
|
|
}
|
|
break;
|
|
|
|
/* CANIOC_DEL_STDFILTER:
|
|
* 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 /* Unimplemented */
|
|
DEBUGASSERT(arg <= priv->config->nstdfilters);
|
|
#endif
|
|
ret = stm32can_delstdfilter(priv, (int)arg);
|
|
}
|
|
break;
|
|
|
|
case CANIOC_SET_NART:
|
|
{
|
|
uint32_t regval;
|
|
|
|
ret = stm32can_enterinitmode(priv);
|
|
if (ret != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_MCR_OFFSET);
|
|
if (arg == 1)
|
|
{
|
|
regval |= CAN_MCR_NART;
|
|
}
|
|
else
|
|
{
|
|
regval &= ~CAN_MCR_NART;
|
|
}
|
|
|
|
stm32can_putreg(priv, STM32_CAN_MCR_OFFSET, regval);
|
|
return stm32can_exitinitmode(priv);
|
|
}
|
|
break;
|
|
|
|
case CANIOC_SET_ABOM:
|
|
{
|
|
uint32_t regval;
|
|
|
|
ret = stm32can_enterinitmode(priv);
|
|
if (ret != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_MCR_OFFSET);
|
|
if (arg == 1)
|
|
{
|
|
regval |= CAN_MCR_ABOM;
|
|
}
|
|
else
|
|
{
|
|
regval &= ~CAN_MCR_ABOM;
|
|
}
|
|
|
|
stm32can_putreg(priv, STM32_CAN_MCR_OFFSET, regval);
|
|
return stm32can_exitinitmode(priv);
|
|
}
|
|
break;
|
|
|
|
/* Unsupported/unrecognized command */
|
|
|
|
default:
|
|
canerr("ERROR: Unrecognized command: %04x\n", cmd);
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_remoterequest(struct can_dev_s *dev, uint16_t id)
|
|
{
|
|
#warning "Remote request not implemented"
|
|
return -ENOSYS;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_send(struct can_dev_s *dev,
|
|
struct can_msg_s *msg)
|
|
{
|
|
struct stm32_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);
|
|
|
|
/* Select one empty transmit mailbox */
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET);
|
|
if (stm32can_txmb0empty(regval))
|
|
{
|
|
txmb = 0;
|
|
}
|
|
else if (stm32can_txmb1empty(regval))
|
|
{
|
|
txmb = 1;
|
|
}
|
|
else if (stm32can_txmb2empty(regval))
|
|
{
|
|
txmb = 2;
|
|
}
|
|
else
|
|
{
|
|
canerr("ERROR: No available mailbox\n");
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* Clear TXRQ, RTR, IDE, EXID, and STID fields */
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_TIR_OFFSET(txmb));
|
|
regval &= ~(CAN_TIR_TXRQ | CAN_TIR_RTR | CAN_TIR_IDE |
|
|
CAN_TIR_EXID_MASK | CAN_TIR_STID_MASK);
|
|
stm32can_putreg(priv, STM32_CAN_TIR_OFFSET(txmb), regval);
|
|
|
|
/* Set up the ID, standard 11-bit or extended 29-bit. */
|
|
|
|
#ifdef CONFIG_CAN_EXTID
|
|
regval &= ~CAN_TIR_EXID_MASK;
|
|
if (msg->cm_hdr.ch_extid)
|
|
{
|
|
DEBUGASSERT(msg->cm_hdr.ch_id < (1 << 29));
|
|
regval |= (msg->cm_hdr.ch_id << CAN_TIR_EXID_SHIFT) | CAN_TIR_IDE;
|
|
}
|
|
else
|
|
{
|
|
DEBUGASSERT(msg->cm_hdr.ch_id < (1 << 11));
|
|
regval |= msg->cm_hdr.ch_id << CAN_TIR_STID_SHIFT;
|
|
}
|
|
|
|
#else
|
|
regval |= (((uint32_t) msg->cm_hdr.ch_id << CAN_TIR_STID_SHIFT) &
|
|
CAN_TIR_STID_MASK);
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_CAN_USE_RTR
|
|
regval |= (msg->cm_hdr.ch_rtr ? CAN_TIR_RTR : 0);
|
|
#endif
|
|
|
|
stm32can_putreg(priv, STM32_CAN_TIR_OFFSET(txmb), regval);
|
|
|
|
/* Set up the DLC */
|
|
|
|
dlc = msg->cm_hdr.ch_dlc;
|
|
regval = stm32can_getreg(priv, STM32_CAN_TDTR_OFFSET(txmb));
|
|
regval &= ~(CAN_TDTR_DLC_MASK | CAN_TDTR_TGT);
|
|
regval |= (uint32_t)dlc << CAN_TDTR_DLC_SHIFT;
|
|
stm32can_putreg(priv, STM32_CAN_TDTR_OFFSET(txmb), regval);
|
|
|
|
/* Set up the data fields */
|
|
|
|
ptr = msg->cm_data;
|
|
regval = 0;
|
|
|
|
if (dlc > 0)
|
|
{
|
|
tmp = (uint32_t)*ptr++;
|
|
regval = tmp << CAN_TDLR_DATA0_SHIFT;
|
|
|
|
if (dlc > 1)
|
|
{
|
|
tmp = (uint32_t)*ptr++;
|
|
regval |= tmp << CAN_TDLR_DATA1_SHIFT;
|
|
|
|
if (dlc > 2)
|
|
{
|
|
tmp = (uint32_t)*ptr++;
|
|
regval |= tmp << CAN_TDLR_DATA2_SHIFT;
|
|
|
|
if (dlc > 3)
|
|
{
|
|
tmp = (uint32_t)*ptr++;
|
|
regval |= tmp << CAN_TDLR_DATA3_SHIFT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stm32can_putreg(priv, STM32_CAN_TDLR_OFFSET(txmb), regval);
|
|
|
|
regval = 0;
|
|
if (dlc > 4)
|
|
{
|
|
tmp = (uint32_t)*ptr++;
|
|
regval = tmp << CAN_TDHR_DATA4_SHIFT;
|
|
|
|
if (dlc > 5)
|
|
{
|
|
tmp = (uint32_t)*ptr++;
|
|
regval |= tmp << CAN_TDHR_DATA5_SHIFT;
|
|
|
|
if (dlc > 6)
|
|
{
|
|
tmp = (uint32_t)*ptr++;
|
|
regval |= tmp << CAN_TDHR_DATA6_SHIFT;
|
|
|
|
if (dlc > 7)
|
|
{
|
|
tmp = (uint32_t)*ptr++;
|
|
regval |= tmp << CAN_TDHR_DATA7_SHIFT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stm32can_putreg(priv, STM32_CAN_TDHR_OFFSET(txmb), regval);
|
|
|
|
/* Enable the transmit mailbox empty interrupt (may already be enabled) */
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_IER_OFFSET);
|
|
regval |= CAN_IER_TMEIE;
|
|
stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval);
|
|
|
|
/* Request transmission */
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_TIR_OFFSET(txmb));
|
|
regval |= CAN_TIR_TXRQ; /* Transmit Mailbox Request */
|
|
stm32can_putreg(priv, STM32_CAN_TIR_OFFSET(txmb), regval);
|
|
|
|
stm32can_dumpmbregs(priv, "After send");
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_txready(struct can_dev_s *dev)
|
|
{
|
|
struct stm32_can_s *priv = dev->cd_priv;
|
|
uint32_t regval;
|
|
|
|
/* Return true if any mailbox is available */
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET);
|
|
caninfo("CAN%" PRIu8 " TSR: %08" PRIx32 "\n", priv->port, regval);
|
|
|
|
return stm32can_txmb0empty(regval) || stm32can_txmb1empty(regval) ||
|
|
stm32can_txmb2empty(regval);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_txempty(struct can_dev_s *dev)
|
|
{
|
|
struct stm32_can_s *priv = dev->cd_priv;
|
|
uint32_t regval;
|
|
|
|
/* Return true if all mailboxes are available */
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET);
|
|
caninfo("CAN%" PRIu8 " TSR: %08" PRIx32 "\n", priv->port, regval);
|
|
|
|
return stm32can_txmb0empty(regval) && stm32can_txmb1empty(regval) &&
|
|
stm32can_txmb2empty(regval);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_rxinterrupt(struct can_dev_s *dev, int rxmb)
|
|
{
|
|
struct stm32_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;
|
|
|
|
/* Verify that a message is pending in the FIFO */
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_RFR_OFFSET(rxmb));
|
|
npending = (regval & CAN_RFR_FMP_MASK) >> CAN_RFR_FMP_SHIFT;
|
|
if (npending < 1)
|
|
{
|
|
canwarn("WARNING: No messages pending\n");
|
|
return OK;
|
|
}
|
|
|
|
if (rxmb == 0)
|
|
{
|
|
stm32can_dumpmbregs(priv, "RX0 interrupt");
|
|
}
|
|
else
|
|
{
|
|
stm32can_dumpmbregs(priv, "RX1 interrupt");
|
|
}
|
|
|
|
/* Get the CAN identifier. */
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_RIR_OFFSET(rxmb));
|
|
|
|
#ifdef CONFIG_CAN_EXTID
|
|
if ((regval & CAN_RIR_IDE) != 0)
|
|
{
|
|
hdr.ch_id = (regval & CAN_RIR_EXID_MASK) >> CAN_RIR_EXID_SHIFT;
|
|
hdr.ch_extid = true;
|
|
}
|
|
else
|
|
{
|
|
hdr.ch_id = (regval & CAN_RIR_STID_MASK) >> CAN_RIR_STID_SHIFT;
|
|
hdr.ch_extid = false;
|
|
}
|
|
#else
|
|
if ((regval & CAN_RIR_IDE) != 0)
|
|
{
|
|
canerr("ERROR: Received message with extended identifier. Dropped\n");
|
|
ret = -ENOSYS;
|
|
goto errout;
|
|
}
|
|
|
|
hdr.ch_id = (regval & CAN_RIR_STID_MASK) >> CAN_RIR_STID_SHIFT;
|
|
#endif
|
|
|
|
/* Clear the error indication and unused bits */
|
|
|
|
#ifdef CONFIG_CAN_ERRORS
|
|
hdr.ch_error = 0; /* Error reporting not supported */
|
|
#endif
|
|
hdr.ch_unused = 0;
|
|
|
|
/* Extract the RTR bit */
|
|
|
|
hdr.ch_rtr = (regval & CAN_RIR_RTR) != 0;
|
|
|
|
/* Get the DLC */
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_RDTR_OFFSET(rxmb));
|
|
hdr.ch_dlc = (regval & CAN_RDTR_DLC_MASK) >> CAN_RDTR_DLC_SHIFT;
|
|
|
|
/* Save the message data */
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_RDLR_OFFSET(rxmb));
|
|
data[0] = (regval & CAN_RDLR_DATA0_MASK) >> CAN_RDLR_DATA0_SHIFT;
|
|
data[1] = (regval & CAN_RDLR_DATA1_MASK) >> CAN_RDLR_DATA1_SHIFT;
|
|
data[2] = (regval & CAN_RDLR_DATA2_MASK) >> CAN_RDLR_DATA2_SHIFT;
|
|
data[3] = (regval & CAN_RDLR_DATA3_MASK) >> CAN_RDLR_DATA3_SHIFT;
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_RDHR_OFFSET(rxmb));
|
|
data[4] = (regval & CAN_RDHR_DATA4_MASK) >> CAN_RDHR_DATA4_SHIFT;
|
|
data[5] = (regval & CAN_RDHR_DATA5_MASK) >> CAN_RDHR_DATA5_SHIFT;
|
|
data[6] = (regval & CAN_RDHR_DATA6_MASK) >> CAN_RDHR_DATA6_SHIFT;
|
|
data[7] = (regval & CAN_RDHR_DATA7_MASK) >> CAN_RDHR_DATA7_SHIFT;
|
|
|
|
/* Provide the data to the upper half driver */
|
|
|
|
ret = can_receive(dev, &hdr, data);
|
|
|
|
/* Release the FIFO */
|
|
|
|
#ifndef CONFIG_CAN_EXTID
|
|
errout:
|
|
#endif
|
|
regval = stm32can_getreg(priv, STM32_CAN_RFR_OFFSET(rxmb));
|
|
regval |= CAN_RFR_RFOM;
|
|
stm32can_putreg(priv, STM32_CAN_RFR_OFFSET(rxmb), regval);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_rx0interrupt(int irq, void *context, void *arg)
|
|
{
|
|
struct can_dev_s *dev = (struct can_dev_s *)arg;
|
|
return stm32can_rxinterrupt(dev, 0);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_rx1interrupt(int irq, void *context, void *arg)
|
|
{
|
|
struct can_dev_s *dev = (struct can_dev_s *)arg;
|
|
return stm32can_rxinterrupt(dev, 1);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_txinterrupt(int irq, void *context, void *arg)
|
|
{
|
|
struct can_dev_s *dev = (struct can_dev_s *)arg;
|
|
struct stm32_can_s *priv;
|
|
uint32_t regval;
|
|
|
|
DEBUGASSERT(dev != NULL && dev->cd_priv != NULL);
|
|
priv = dev->cd_priv;
|
|
|
|
/* Get the transmit status */
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET);
|
|
|
|
/* Check for RQCP0: Request completed mailbox 0 */
|
|
|
|
if ((regval & CAN_TSR_RQCP0) != 0)
|
|
{
|
|
/* Writing '1' to RCP0 clears RCP0 and all the status bits (TXOK0,
|
|
* ALST0 and TERR0) for Mailbox 0.
|
|
*/
|
|
|
|
stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, CAN_TSR_RQCP0);
|
|
|
|
/* Tell the upper half that the transfer is finished. */
|
|
|
|
can_txdone(dev);
|
|
}
|
|
|
|
/* Check for RQCP1: Request completed mailbox 1 */
|
|
|
|
if ((regval & CAN_TSR_RQCP1) != 0)
|
|
{
|
|
/* Writing '1' to RCP1 clears RCP1 and all the status bits (TXOK1,
|
|
* ALST1 and TERR1) for Mailbox 1.
|
|
*/
|
|
|
|
stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, CAN_TSR_RQCP1);
|
|
|
|
/* Tell the upper half that the transfer is finished. */
|
|
|
|
can_txdone(dev);
|
|
}
|
|
|
|
/* Check for RQCP2: Request completed mailbox 2 */
|
|
|
|
if ((regval & CAN_TSR_RQCP2) != 0)
|
|
{
|
|
/* Writing '1' to RCP2 clears RCP2 and all the status bits (TXOK2,
|
|
* ALST2 and TERR2) for Mailbox 2.
|
|
*/
|
|
|
|
stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, CAN_TSR_RQCP2);
|
|
|
|
/* Tell the upper half that the transfer is finished. */
|
|
|
|
can_txdone(dev);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
#ifdef CONFIG_CAN_ERRORS
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_sceinterrupt(int irq, void *context, void *arg)
|
|
{
|
|
struct can_dev_s *dev = (struct can_dev_s *)arg;
|
|
struct stm32_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;
|
|
|
|
/* Check Error Interrupt flag */
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_MSR_OFFSET);
|
|
if (regval & CAN_MSR_ERRI)
|
|
{
|
|
/* Encode error bits */
|
|
|
|
errbits = 0;
|
|
memset(data, 0, sizeof(data));
|
|
|
|
/* Get Error statur register */
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_ESR_OFFSET);
|
|
|
|
if (regval & CAN_ESR_EWGF)
|
|
{
|
|
/* Error warning flag */
|
|
|
|
data[1] |= (CAN_ERROR1_RXWARNING | CAN_ERROR1_TXWARNING);
|
|
errbits |= CAN_ERROR_CONTROLLER;
|
|
}
|
|
|
|
if (regval & CAN_ESR_EPVF)
|
|
{
|
|
/* Error passive flag */
|
|
|
|
data[1] |= (CAN_ERROR1_RXPASSIVE | CAN_ERROR1_TXPASSIVE);
|
|
errbits |= CAN_ERROR_CONTROLLER;
|
|
}
|
|
|
|
if (regval & CAN_ESR_BOFF)
|
|
{
|
|
/* Bus-off flag */
|
|
|
|
errbits |= CAN_ERROR_BUSOFF;
|
|
}
|
|
|
|
/* Last error code */
|
|
|
|
if (regval & CAN_ESR_LEC_MASK)
|
|
{
|
|
if (regval & CAN_ESR_STUFFERROR)
|
|
{
|
|
/* Stuff Error */
|
|
|
|
errbits |= CAN_ERROR_PROTOCOL;
|
|
data[2] |= CAN_ERROR2_STUFF;
|
|
}
|
|
else if (regval & CAN_ESR_FORMERROR)
|
|
{
|
|
/* Format Error */
|
|
|
|
errbits |= CAN_ERROR_PROTOCOL;
|
|
data[2] |= CAN_ERROR2_FORM;
|
|
}
|
|
else if (regval & CAN_ESR_ACKERROR)
|
|
{
|
|
/* Acknowledge Error */
|
|
|
|
errbits |= CAN_ERROR_NOACK;
|
|
}
|
|
else if (regval & CAN_ESR_BRECERROR)
|
|
{
|
|
/* Bit recessive Error */
|
|
|
|
errbits |= CAN_ERROR_PROTOCOL;
|
|
data[2] |= CAN_ERROR2_BIT1;
|
|
}
|
|
else if (regval & CAN_ESR_BDOMERROR)
|
|
{
|
|
/* Bit dominant Error */
|
|
|
|
errbits |= CAN_ERROR_PROTOCOL;
|
|
data[2] |= CAN_ERROR2_BIT0;
|
|
}
|
|
else if (regval & CAN_ESR_CRCERRPR)
|
|
{
|
|
/* Receive CRC Error */
|
|
|
|
errbits |= CAN_ERROR_PROTOCOL;
|
|
data[3] |= CAN_ERROR3_CRCSEQ;
|
|
}
|
|
}
|
|
|
|
/* Get transmit status register */
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET);
|
|
|
|
if (regval & CAN_TSR_ALST0 || regval & CAN_TSR_ALST1 ||
|
|
regval & CAN_TSR_ALST2)
|
|
{
|
|
/* Lost arbitration Error */
|
|
|
|
errbits |= CAN_ERROR_LOSTARB;
|
|
}
|
|
|
|
/* Clear TSR register */
|
|
|
|
stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, regval);
|
|
|
|
/* Clear ERRI flag */
|
|
|
|
stm32can_putreg(priv, STM32_CAN_MSR_OFFSET, CAN_MSR_ERRI);
|
|
}
|
|
|
|
/* TODO: RX overflow and TX overflow */
|
|
|
|
/* Report a CAN error */
|
|
|
|
if (errbits != 0)
|
|
{
|
|
canerr("ERROR: errbits = %08" PRIx16 "\n", errbits);
|
|
|
|
/* Format the CAN header for the error report. */
|
|
|
|
hdr.ch_id = errbits;
|
|
hdr.ch_dlc = CAN_ERROR_DLC;
|
|
hdr.ch_rtr = 0;
|
|
hdr.ch_error = 1;
|
|
#ifdef CONFIG_CAN_EXTID
|
|
hdr.ch_extid = 0;
|
|
#endif
|
|
hdr.ch_unused = 0;
|
|
|
|
/* And provide the error report to the upper half logic */
|
|
|
|
ret = can_receive(dev, &hdr, data);
|
|
if (ret < 0)
|
|
{
|
|
canerr("ERROR: can_receive failed: %d\n", ret);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_bittiming(struct stm32_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) STM32_PCLK1_FREQUENCY, priv->baud);
|
|
|
|
/* Try to get CAN_BIT_QUANTA quanta in one bit_time.
|
|
*
|
|
* 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 = STM32_PCLK1_FREQUENCY / priv->baud;
|
|
if (tmp < CAN_BIT_QUANTA)
|
|
{
|
|
/* At the smallest brp value (1), there are already too few bit times
|
|
* (PCLCK1 / baud) to meet our goal. brp must be one and we need
|
|
* make some reasonable guesses about ts1 and ts2.
|
|
*/
|
|
|
|
brp = 1;
|
|
|
|
/* In this case, we have to guess a good value for ts1 and ts2 */
|
|
|
|
ts1 = (tmp - 1) >> 1;
|
|
ts2 = tmp - ts1 - 1;
|
|
if (ts1 == ts2 && ts1 > 1 && ts2 < CAN_BTR_TSEG2_MAX)
|
|
{
|
|
ts1--;
|
|
ts2++;
|
|
}
|
|
}
|
|
|
|
/* Otherwise, nquanta is CAN_BIT_QUANTA, ts1 is CONFIG_STM32_CAN_TSEG1,
|
|
* ts2 is CONFIG_STM32_CAN_TSEG2 and we calculate brp to achieve
|
|
* CAN_BIT_QUANTA quanta in the bit time
|
|
*/
|
|
|
|
else
|
|
{
|
|
ts1 = CONFIG_STM32_CAN_TSEG1;
|
|
ts2 = CONFIG_STM32_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);
|
|
|
|
/* Configure bit timing. This also does the following, less obvious
|
|
* 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_BTR_BRP_SHIFT) | ((ts1 - 1) << CAN_BTR_TS1_SHIFT) |
|
|
((ts2 - 1) << CAN_BTR_TS2_SHIFT) | ((1 - 1) << CAN_BTR_SJW_SHIFT);
|
|
#ifdef CONFIG_CAN_LOOPBACK
|
|
/* tmp |= (CAN_BTR_LBKM | CAN_BTR_SILM); */
|
|
|
|
tmp |= CAN_BTR_LBKM;
|
|
#endif
|
|
|
|
stm32can_putreg(priv, STM32_CAN_BTR_OFFSET, tmp);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_enterinitmode(struct stm32_can_s *priv)
|
|
{
|
|
uint32_t regval;
|
|
volatile uint32_t timeout;
|
|
|
|
caninfo("CAN%" PRIu8 "\n", priv->port);
|
|
|
|
/* Enter initialization mode */
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_MCR_OFFSET);
|
|
regval |= CAN_MCR_INRQ;
|
|
stm32can_putreg(priv, STM32_CAN_MCR_OFFSET, regval);
|
|
|
|
/* Wait until initialization mode is acknowledged */
|
|
|
|
for (timeout = INAK_TIMEOUT; timeout > 0; timeout--)
|
|
{
|
|
regval = stm32can_getreg(priv, STM32_CAN_MSR_OFFSET);
|
|
if ((regval & CAN_MSR_INAK) != 0)
|
|
{
|
|
/* We are in initialization mode */
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Check for a timeout */
|
|
|
|
if (timeout < 1)
|
|
{
|
|
canerr("ERROR: Timed out waiting to enter initialization mode\n");
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_exitinitmode(struct stm32_can_s *priv)
|
|
{
|
|
uint32_t regval;
|
|
volatile uint32_t timeout;
|
|
|
|
/* Exit Initialization mode, enter Normal mode */
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_MCR_OFFSET);
|
|
regval &= ~CAN_MCR_INRQ;
|
|
stm32can_putreg(priv, STM32_CAN_MCR_OFFSET, regval);
|
|
|
|
/* Wait until the initialization mode exit is acknowledged */
|
|
|
|
for (timeout = INAK_TIMEOUT; timeout > 0; timeout--)
|
|
{
|
|
regval = stm32can_getreg(priv, STM32_CAN_MSR_OFFSET);
|
|
if ((regval & CAN_MSR_INAK) == 0)
|
|
{
|
|
/* We are out of initialization mode */
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Check for a timeout */
|
|
|
|
if (timeout < 1)
|
|
{
|
|
canerr("ERROR: Timed out waiting to exit initialization mode: %08"
|
|
PRIx32 "\n", regval);
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_cellinit(struct stm32_can_s *priv)
|
|
{
|
|
uint32_t regval;
|
|
int ret;
|
|
|
|
caninfo("CAN%" PRIu8 "\n", priv->port);
|
|
|
|
/* Exit from sleep mode */
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_MCR_OFFSET);
|
|
regval &= ~CAN_MCR_SLEEP;
|
|
stm32can_putreg(priv, STM32_CAN_MCR_OFFSET, regval);
|
|
|
|
ret = stm32can_enterinitmode(priv);
|
|
if (ret != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Disable the following modes:
|
|
*
|
|
* - Time triggered communication mode
|
|
* - Automatic bus-off management
|
|
* - Automatic wake-up mode
|
|
* - No automatic retransmission
|
|
* - Receive FIFO locked mode
|
|
*
|
|
* Enable:
|
|
*
|
|
* - Transmit FIFO priority
|
|
*/
|
|
|
|
regval = stm32can_getreg(priv, STM32_CAN_MCR_OFFSET);
|
|
regval &= ~(CAN_MCR_RFLM | CAN_MCR_NART | CAN_MCR_AWUM |
|
|
CAN_MCR_ABOM | CAN_MCR_TTCM);
|
|
regval |= CAN_MCR_TXFP;
|
|
stm32can_putreg(priv, STM32_CAN_MCR_OFFSET, regval);
|
|
|
|
/* Configure bit timing. */
|
|
|
|
ret = stm32can_bittiming(priv);
|
|
if (ret < 0)
|
|
{
|
|
canerr("ERROR: Failed to set bit timing: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return stm32can_exitinitmode(priv);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_filterinit(struct stm32_can_s *priv)
|
|
{
|
|
uint32_t regval;
|
|
uint32_t bitmask;
|
|
|
|
caninfo("CAN%" PRIu8 " filter: %" PRIu8 "\n", priv->port, priv->filter);
|
|
|
|
/* Get the bitmask associated with the filter used by this CAN block */
|
|
|
|
bitmask = (uint32_t)1 << priv->filter;
|
|
|
|
/* Enter filter initialization mode */
|
|
|
|
regval = stm32can_getfreg(priv, STM32_CAN_FMR_OFFSET);
|
|
regval |= CAN_FMR_FINIT;
|
|
stm32can_putfreg(priv, STM32_CAN_FMR_OFFSET, regval);
|
|
|
|
/* Assign half the filters to CAN1, half to CAN2 */
|
|
|
|
#if defined(CONFIG_STM32_CONNECTIVITYLINE) || \
|
|
defined(CONFIG_STM32_STM32F20XX) || \
|
|
defined(CONFIG_STM32_STM32F4XXX)
|
|
regval = stm32can_getfreg(priv, STM32_CAN_FMR_OFFSET);
|
|
regval &= CAN_FMR_CAN2SB_MASK;
|
|
regval |= (CAN_NFILTERS / 2) << CAN_FMR_CAN2SB_SHIFT;
|
|
stm32can_putfreg(priv, STM32_CAN_FMR_OFFSET, regval);
|
|
#endif
|
|
|
|
/* Disable the filter */
|
|
|
|
regval = stm32can_getfreg(priv, STM32_CAN_FA1R_OFFSET);
|
|
regval &= ~bitmask;
|
|
stm32can_putfreg(priv, STM32_CAN_FA1R_OFFSET, regval);
|
|
|
|
/* Select the 32-bit scale for the filter */
|
|
|
|
regval = stm32can_getfreg(priv, STM32_CAN_FS1R_OFFSET);
|
|
regval |= bitmask;
|
|
stm32can_putfreg(priv, STM32_CAN_FS1R_OFFSET, regval);
|
|
|
|
/* There are 14 or 28 filter banks (depending) on the device.
|
|
* Each filter bank is composed of two 32-bit registers, CAN_FiR:
|
|
*/
|
|
|
|
stm32can_putfreg(priv, STM32_CAN_FIR_OFFSET(priv->filter, 1), 0);
|
|
stm32can_putfreg(priv, STM32_CAN_FIR_OFFSET(priv->filter, 2), 0);
|
|
|
|
/* Set Id/Mask mode for the filter */
|
|
|
|
regval = stm32can_getfreg(priv, STM32_CAN_FM1R_OFFSET);
|
|
regval &= ~bitmask;
|
|
stm32can_putfreg(priv, STM32_CAN_FM1R_OFFSET, regval);
|
|
|
|
/* Assign FIFO 0 for the filter */
|
|
|
|
regval = stm32can_getfreg(priv, STM32_CAN_FFA1R_OFFSET);
|
|
regval &= ~bitmask;
|
|
stm32can_putfreg(priv, STM32_CAN_FFA1R_OFFSET, regval);
|
|
|
|
/* Enable the filter */
|
|
|
|
regval = stm32can_getfreg(priv, STM32_CAN_FA1R_OFFSET);
|
|
regval |= bitmask;
|
|
stm32can_putfreg(priv, STM32_CAN_FA1R_OFFSET, regval);
|
|
|
|
/* Exit filter initialization mode */
|
|
|
|
regval = stm32can_getfreg(priv, STM32_CAN_FMR_OFFSET);
|
|
regval &= ~CAN_FMR_FINIT;
|
|
stm32can_putfreg(priv, STM32_CAN_FMR_OFFSET, regval);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_addextfilter(struct stm32_can_s *priv,
|
|
struct canioc_extfilter_s *arg)
|
|
{
|
|
return -ENOTTY;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_delextfilter(struct stm32_can_s *priv, int arg)
|
|
{
|
|
return -ENOTTY;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_addstdfilter(struct stm32_can_s *priv,
|
|
struct canioc_stdfilter_s *arg)
|
|
{
|
|
return -ENOTTY;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_delstdfilter(struct stm32_can_s *priv, int arg)
|
|
{
|
|
return -ENOTTY;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_txmb0empty(uint32_t tsr_regval)
|
|
{
|
|
return (tsr_regval & CAN_TSR_TME0) != 0 &&
|
|
(tsr_regval & CAN_TSR_RQCP0) == 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_txmb1empty(uint32_t tsr_regval)
|
|
{
|
|
return (tsr_regval & CAN_TSR_TME1) != 0 &&
|
|
(tsr_regval & CAN_TSR_RQCP1) == 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32can_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 stm32can_txmb2empty(uint32_t tsr_regval)
|
|
{
|
|
return (tsr_regval & CAN_TSR_TME2) != 0 &&
|
|
(tsr_regval & CAN_TSR_RQCP2) == 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_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 *stm32_caninitialize(int port)
|
|
{
|
|
struct can_dev_s *dev = NULL;
|
|
|
|
caninfo("CAN%" PRIu8 "\n", port);
|
|
|
|
/* NOTE: Peripherical clocking for CAN1 and/or CAN2 was already provided
|
|
* by stm32_clockconfig() early in the reset sequence.
|
|
*/
|
|
|
|
#ifdef CONFIG_STM32_CAN1
|
|
if (port == 1)
|
|
{
|
|
/* Select the CAN1 device structure */
|
|
|
|
dev = &g_can1dev;
|
|
|
|
/* Configure CAN1 pins. The ambiguous settings in the stm32*_pinmap.h
|
|
* file must have been disambiguated in the board.h file.
|
|
*/
|
|
|
|
stm32_configgpio(GPIO_CAN1_RX);
|
|
stm32_configgpio(GPIO_CAN1_TX);
|
|
}
|
|
else
|
|
#endif
|
|
#ifdef CONFIG_STM32_CAN2
|
|
if (port == 2)
|
|
{
|
|
/* Select the CAN2 device structure */
|
|
|
|
dev = &g_can2dev;
|
|
|
|
/* Configure CAN2 pins. The ambiguous settings in the stm32*_pinmap.h
|
|
* file must have been disambiguated in the board.h file.
|
|
*/
|
|
|
|
stm32_configgpio(GPIO_CAN2_RX);
|
|
stm32_configgpio(GPIO_CAN2_TX);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
canerr("ERROR: Unsupported port %d\n", port);
|
|
return NULL;
|
|
}
|
|
|
|
return dev;
|
|
}
|
|
|
|
#endif /* CONFIG_CAN && (CONFIG_STM32_CAN1 || CONFIG_STM32_CAN2) */
|