2021 lines
57 KiB
C
2021 lines
57 KiB
C
/****************************************************************************
|
||
* arch/arm/src/sama5/sam_can.c
|
||
*
|
||
* Copyright (C) 2013-2014 Gregory Nutt. All rights reserved.
|
||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||
*
|
||
* References:
|
||
*
|
||
* SAMA5D3 Series Data Sheet
|
||
* Atmel NoOS sample code (for bit timing configuration).
|
||
*
|
||
* The Atmel sample code has a BSD compatible license that requires this
|
||
* copyright notice:
|
||
*
|
||
* Copyright (c) 2012, Atmel Corporation
|
||
*
|
||
* Redistribution and use in source and binary forms, with or without
|
||
* modification, are permitted provided that the following conditions
|
||
* are met:
|
||
*
|
||
* 1. Redistributions of source code must retain the above copyright
|
||
* notice, this list of conditions and the following disclaimer.
|
||
* 2. Redistributions in binary form must reproduce the above copyright
|
||
* notice, this list of conditions and the following disclaimer in
|
||
* the documentation and/or other materials provided with the
|
||
* distribution.
|
||
* 3. Neither the name NuttX, Atmel, nor the names of its contributors may
|
||
* be used to endorse or promote products derived from this software
|
||
* without specific prior written permission.
|
||
*
|
||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
* POSSIBILITY OF SUCH DAMAGE.
|
||
*
|
||
****************************************************************************/
|
||
|
||
/****************************************************************************
|
||
* Included Files
|
||
****************************************************************************/
|
||
|
||
#include <nuttx/config.h>
|
||
|
||
#include <stdio.h>
|
||
#include <sys/types.h>
|
||
#include <stdint.h>
|
||
#include <stdbool.h>
|
||
#include <string.h>
|
||
#include <semaphore.h>
|
||
#include <errno.h>
|
||
#include <debug.h>
|
||
|
||
#include <arch/board/board.h>
|
||
#include <nuttx/arch.h>
|
||
#include <nuttx/can.h>
|
||
|
||
#include "up_internal.h"
|
||
#include "up_arch.h"
|
||
|
||
|
||
#include "chip/sam_pinmap.h"
|
||
#include "sam_periphclks.h"
|
||
#include "sam_pio.h"
|
||
#include "sam_can.h"
|
||
|
||
#if defined(CONFIG_CAN) && (defined(CONFIG_SAMA5_CAN0) || defined(CONFIG_SAMA5_CAN1))
|
||
|
||
/****************************************************************************
|
||
* Pre-processor Definitions
|
||
****************************************************************************/
|
||
/* Common definitions *******************************************************/
|
||
|
||
#ifndef MIN
|
||
# define MIN(a,b) ((a < b) ? a : b)
|
||
#endif
|
||
|
||
#ifndef MAX
|
||
# define MAX(a,b) ((a > b) ? a : b)
|
||
#endif
|
||
|
||
/* Mailboxes ****************************************************************/
|
||
|
||
#define SAMA5_CAN_NRECVMB MAX(CONFIG_SAMA5_CAN0_NRECVMB, CONFIG_SAMA5_CAN1_NRECVMB)
|
||
|
||
/* The set of all mailboxes */
|
||
|
||
#if SAM_CAN_NMAILBOXES == 8
|
||
# define CAN_ALL_MAILBOXES 0xff /* 8 mailboxes */
|
||
#else
|
||
# error Unsupport/undefined number of mailboxes
|
||
#endif
|
||
|
||
/* Interrupts ***************************************************************/
|
||
/* If debug is enabled, then print some diagnostic info if any of these
|
||
* events occur:
|
||
*
|
||
* CAN_INT_ERRA YES Bit 16: Error Active Mode
|
||
* CAN_INT_WARN YES Bit 17: Warning Limit
|
||
* CAN_INT_ERRP NO Bit 18: Error Passive Mode
|
||
* CAN_INT_BOFF NO Bit 19: Bus Off Mode
|
||
*
|
||
* CAN_INT_SLEEP NO Bit 20: CAN Controller in Low-power Mode
|
||
* CAN_INT_WAKEUP NO Bit 21: Wake-up Interrupt
|
||
* CAN_INT_TOVF NO Bit 22: Timer Overflow
|
||
* CAN_INT_TSTP NO Bit 23: Timestamp
|
||
*
|
||
* CAN_INT_CERR YES Bit 24: Mailbox CRC Error
|
||
* CAN_INT_SERR YES Bit 25: Mailbox Stuffing Error
|
||
* CAN_INT_AERR NO Bit 26: Acknowledgment Error (uusally means no CAN bus)
|
||
* CAN_INT_FERR YES Bit 27: Form Error
|
||
*
|
||
* CAN_INT_BERR YES Bit 28: Bit Error
|
||
*/
|
||
|
||
#define CAN_DEBUG_INTS (CAN_INT_ERRA | CAN_INT_WARN | CAN_INT_CERR | \
|
||
CAN_INT_SERR | CAN_INT_FERR | CAN_INT_BERR)
|
||
|
||
/* Debug ********************************************************************/
|
||
/* Non-standard debug that may be enabled just for testing CAN */
|
||
|
||
#ifdef CONFIG_DEBUG_CAN
|
||
# define candbg dbg
|
||
# define canvdbg vdbg
|
||
# define canlldbg lldbg
|
||
# define canllvdbg llvdbg
|
||
#else
|
||
# define candbg(x...)
|
||
# define canvdbg(x...)
|
||
# define canlldbg(x...)
|
||
# define canllvdbg(x...)
|
||
#endif
|
||
|
||
#if !defined(CONFIG_DEBUG) || !defined(CONFIG_DEBUG_CAN)
|
||
# undef CONFIG_SAMA5_CAN_REGDEBUG
|
||
#endif
|
||
|
||
/****************************************************************************
|
||
* Private Types
|
||
****************************************************************************/
|
||
/* This structure describes receive mailbox filtering */
|
||
|
||
struct sam_filter_s
|
||
{
|
||
#ifdef CONFIG_CAN_EXTID
|
||
uint32_t addr; /* 29-bit address to match */
|
||
uint32_t mask; /* 29-bit address mask */
|
||
#else
|
||
uint16_t addr; /* 11-bit address to match */
|
||
uint16_t mask; /* 11-bit address mask */
|
||
#endif
|
||
};
|
||
|
||
/* This structure provides the constant configuration of a CAN peripheral */
|
||
|
||
struct sam_config_s
|
||
{
|
||
uint8_t port; /* CAN port number (1 or 2) */
|
||
uint8_t pid; /* CAN periperal ID/IRQ number */
|
||
uint8_t nrecvmb; /* Number of receive mailboxes */
|
||
xcpt_t handler; /* CAN interrupt handler */
|
||
uintptr_t base; /* Base address of the CAN control registers */
|
||
uint32_t baud; /* Configured baud */
|
||
pio_pinset_t rxpinset; /* RX pin configuration */
|
||
pio_pinset_t txpinset; /* TX pin configuration */
|
||
|
||
/* Mailbox filters */
|
||
|
||
struct sam_filter_s filter[SAMA5_CAN_NRECVMB];
|
||
};
|
||
|
||
/* This structure provides the current state of a CAN peripheral */
|
||
|
||
struct sam_can_s
|
||
{
|
||
const struct sam_config_s *config; /* The constant configuration */
|
||
bool initialized; /* TRUE: Device has been initialized */
|
||
uint8_t freemb; /* Rhe set of unalloated mailboxes */
|
||
uint8_t rxmbset; /* The set of mailboxes configured for receive */
|
||
volatile uint8_t txmbset; /* The set of mailboxes actively transmitting */
|
||
bool txdisabled; /* TRUE: Keep TX interrupts disabled */
|
||
sem_t exclsem; /* Enforces mutually exclusive access */
|
||
uint32_t frequency; /* CAN clock frequency */
|
||
|
||
#ifdef CONFIG_SAMA5_CAN_REGDEBUG
|
||
uintptr_t regaddr; /* Last register address read */
|
||
uint32_t regval; /* Last value read from the register */
|
||
unsigned int count; /* Number of times that the value was read */
|
||
#endif
|
||
};
|
||
|
||
/****************************************************************************
|
||
* Private Function Prototypes
|
||
****************************************************************************/
|
||
|
||
/* CAN Register access */
|
||
|
||
static uint32_t can_getreg(FAR struct sam_can_s *priv, int offset);
|
||
static void can_putreg(FAR struct sam_can_s *priv, int offset, uint32_t regval);
|
||
#ifdef CONFIG_SAMA5_CAN_REGDEBUG
|
||
static void can_dumpctrlregs(FAR struct sam_can_s *priv, FAR const char *msg);
|
||
static void can_dumpmbregs(FAR struct sam_can_s *priv, FAR const char *msg);
|
||
#else
|
||
# define can_dumpctrlregs(priv,msg)
|
||
# define can_dumpmbregs(priv,msg)
|
||
#endif
|
||
|
||
/* Semaphore helpers */
|
||
|
||
static void can_semtake(FAR struct sam_can_s *priv);
|
||
#define can_semgive(priv) sem_post(&priv->exclsem)
|
||
|
||
/* Mailboxes */
|
||
|
||
static int can_recvsetup(FAR struct sam_can_s *priv);
|
||
|
||
/* CAN driver methods */
|
||
|
||
static void can_reset(FAR struct can_dev_s *dev);
|
||
static int can_setup(FAR struct can_dev_s *dev);
|
||
static void can_shutdown(FAR struct can_dev_s *dev);
|
||
static void can_rxint(FAR struct can_dev_s *dev, bool enable);
|
||
static void can_txint(FAR struct can_dev_s *dev, bool enable);
|
||
static int can_ioctl(FAR struct can_dev_s *dev, int cmd, unsigned long arg);
|
||
static int can_remoterequest(FAR struct can_dev_s *dev, uint16_t id);
|
||
static int can_send(FAR struct can_dev_s *dev, FAR struct can_msg_s *msg);
|
||
static bool can_txready(FAR struct can_dev_s *dev);
|
||
static bool can_txempty(FAR struct can_dev_s *dev);
|
||
|
||
/* CAN interrupt handling */
|
||
|
||
static inline void can_rxinterrupt(FAR struct can_dev_s *dev, int mbndx,
|
||
uint32_t msr);
|
||
static inline void can_txinterrupt(FAR struct can_dev_s *dev, int mbndx);
|
||
static inline void can_mbinterrupt(FAR struct can_dev_s *dev, int mbndx);
|
||
static void can_interrupt(FAR struct can_dev_s *dev);
|
||
#ifdef CONFIG_SAMA5_CAN0
|
||
static int can0_interrupt(int irq, void *context);
|
||
#endif
|
||
#ifdef CONFIG_SAMA5_CAN1
|
||
static int can1_interrupt(int irq, void *context);
|
||
#endif
|
||
|
||
/* Hardware initialization */
|
||
|
||
static int can_bittiming(FAR struct sam_can_s *priv);
|
||
#ifdef CONFIG_SAMA5_CAN_AUTOBAUD
|
||
static int can_autobaud(FAR struct sam_can_s *priv);
|
||
#endif
|
||
static int can_hwinitialize(FAR struct sam_can_s *priv);
|
||
|
||
/****************************************************************************
|
||
* Private Data
|
||
****************************************************************************/
|
||
|
||
static const struct can_ops_s g_canops =
|
||
{
|
||
.co_reset = can_reset,
|
||
.co_setup = can_setup,
|
||
.co_shutdown = can_shutdown,
|
||
.co_rxint = can_rxint,
|
||
.co_txint = can_txint,
|
||
.co_ioctl = can_ioctl,
|
||
.co_remoterequest = can_remoterequest,
|
||
.co_send = can_send,
|
||
.co_txready = can_txready,
|
||
.co_txempty = can_txempty,
|
||
};
|
||
|
||
#ifdef CONFIG_SAMA5_CAN0
|
||
static const struct sam_config_s g_can0const =
|
||
{
|
||
.port = 0,
|
||
.pid = SAM_PID_CAN0,
|
||
.nrecvmb = CONFIG_SAMA5_CAN0_NRECVMB,
|
||
.handler = can0_interrupt,
|
||
.base = SAM_CAN0_VBASE,
|
||
.baud = CONFIG_SAMA5_CAN0_BAUD,
|
||
.rxpinset = PIO_CAN0_RX,
|
||
.txpinset = PIO_CAN0_TX,
|
||
.filter =
|
||
{
|
||
{
|
||
.addr = CONFIG_SAMA5_CAN0_ADDR0,
|
||
.mask = CONFIG_SAMA5_CAN0_MASK0,
|
||
},
|
||
#if CONFIG_SAMA5_CAN0_NRECVMB > 1
|
||
{
|
||
.addr = CONFIG_SAMA5_CAN0_ADDR1,
|
||
.mask = CONFIG_SAMA5_CAN0_MASK1,
|
||
},
|
||
#if CONFIG_SAMA5_CAN0_NRECVMB > 1
|
||
{
|
||
.addr = CONFIG_SAMA5_CAN0_ADDR2,
|
||
.mask = CONFIG_SAMA5_CAN0_MASK2,
|
||
},
|
||
#endif
|
||
#endif
|
||
},
|
||
};
|
||
|
||
static struct sam_can_s g_can0priv;
|
||
static struct can_dev_s g_can0dev;
|
||
#endif
|
||
|
||
#ifdef CONFIG_SAMA5_CAN1
|
||
static const struct sam_config_s g_can1const =
|
||
{
|
||
.port = 1,
|
||
.pid = SAM_PID_CAN1,
|
||
.nrecvmb = CONFIG_SAMA5_CAN1_NRECVMB,
|
||
.handler = can1_interrupt,
|
||
.base = SAM_CAN1_VBASE,
|
||
.baud = CONFIG_SAMA5_CAN1_BAUD,
|
||
.rxpinset = PIO_CAN1_RX,
|
||
.txpinset = PIO_CAN1_TX,
|
||
.filter =
|
||
{
|
||
{
|
||
.addr = CONFIG_SAMA5_CAN1_ADDR0,
|
||
.mask = CONFIG_SAMA5_CAN1_MASK0,
|
||
},
|
||
#if CONFIG_SAMA5_CAN1_NRECVMB > 1
|
||
{
|
||
.addr = CONFIG_SAMA5_CAN1_ADDR1,
|
||
.mask = CONFIG_SAMA5_CAN1_MASK1,
|
||
},
|
||
#if CONFIG_SAMA5_CAN1_NRECVMB > 1
|
||
{
|
||
.addr = CONFIG_SAMA5_CAN1_ADDR2,
|
||
.mask = CONFIG_SAMA5_CAN1_MASK2,
|
||
},
|
||
#endif
|
||
#endif
|
||
},
|
||
};
|
||
|
||
static struct sam_can_s g_can1priv;
|
||
static struct can_dev_s g_can1dev;
|
||
#endif
|
||
|
||
/****************************************************************************
|
||
* Private Functions
|
||
****************************************************************************/
|
||
|
||
/****************************************************************************
|
||
* Name: can_getreg
|
||
*
|
||
* Description:
|
||
* Read the value of a CAN register.
|
||
*
|
||
* Input Parameters:
|
||
* priv - A reference to the CAN peripheral state
|
||
* offset - The offset to the register to read
|
||
*
|
||
* Returned Value:
|
||
*
|
||
****************************************************************************/
|
||
|
||
#ifdef CONFIG_SAMA5_CAN_REGDEBUG
|
||
static uint32_t can_getreg(FAR struct sam_can_s *priv, int offset)
|
||
{
|
||
FAR const struct sam_config_s *config = priv->config;
|
||
uintptr_t regaddr;
|
||
uint32_t regval;
|
||
|
||
/* Read the value from the register */
|
||
|
||
regaddr = config->base + offset;
|
||
regval = getreg32(regaddr);
|
||
|
||
/* 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 (regaddr == priv->regaddr && regval == priv->regval)
|
||
{
|
||
if (priv->count == 0xffffffff || ++priv->count > 3)
|
||
{
|
||
if (priv->count == 4)
|
||
{
|
||
lldbg("...\n");
|
||
}
|
||
|
||
return regval;
|
||
}
|
||
}
|
||
|
||
/* No this is a new address or value */
|
||
|
||
else
|
||
{
|
||
/* Did we print "..." for the previous value? */
|
||
|
||
if (priv->count > 3)
|
||
{
|
||
/* Yes.. then show how many times the value repeated */
|
||
|
||
lldbg("[repeats %d more times]\n", priv->count - 3);
|
||
}
|
||
|
||
/* Save the new address, value, and count */
|
||
|
||
priv->regaddr = regaddr;
|
||
priv->regval = regval;
|
||
priv->count = 1;
|
||
}
|
||
|
||
/* Show the register value read */
|
||
|
||
lldbg("%08x->%08x\n", regaddr, regval);
|
||
return regval;
|
||
}
|
||
|
||
#else
|
||
static uint32_t can_getreg(FAR struct sam_can_s *priv, int offset)
|
||
{
|
||
FAR const struct sam_config_s *config = priv->config;
|
||
return getreg32(config->base + offset);
|
||
}
|
||
|
||
#endif
|
||
|
||
/****************************************************************************
|
||
* Name: can_putreg
|
||
*
|
||
* Description:
|
||
* Set the value of a CAN register.
|
||
*
|
||
* Input Parameters:
|
||
* priv - A reference to the CAN peripheral state
|
||
* offset - The offset to the register to write
|
||
* regval - The value to write to the register
|
||
*
|
||
* Returned Value:
|
||
* None
|
||
*
|
||
****************************************************************************/
|
||
|
||
#ifdef CONFIG_SAMA5_CAN_REGDEBUG
|
||
static void can_putreg(FAR struct sam_can_s *priv, int offset, uint32_t regval)
|
||
{
|
||
FAR const struct sam_config_s *config = priv->config;
|
||
uintptr_t regaddr = config->base + offset;
|
||
|
||
/* Show the register value being written */
|
||
|
||
lldbg("%08x<-%08x\n", regaddr, regval);
|
||
|
||
/* Write the value */
|
||
|
||
putreg32(regval, regaddr);
|
||
}
|
||
|
||
#else
|
||
static void can_putreg(FAR struct sam_can_s *priv, int offset, uint32_t regval)
|
||
{
|
||
FAR const struct sam_config_s *config = priv->config;
|
||
putreg32(regval, config->base + offset);
|
||
}
|
||
|
||
#endif
|
||
|
||
/****************************************************************************
|
||
* Name: can_dumpctrlregs
|
||
*
|
||
* Description:
|
||
* Dump the contents of all CAN control registers
|
||
*
|
||
* Input Parameters:
|
||
* priv - A reference to the CAN peripheral state
|
||
*
|
||
* Returned Value:
|
||
* None
|
||
*
|
||
****************************************************************************/
|
||
|
||
#ifdef CONFIG_SAMA5_CAN_REGDEBUG
|
||
static void can_dumpctrlregs(FAR struct sam_can_s *priv, FAR const char *msg)
|
||
{
|
||
FAR const struct sam_config_s *config = priv->config;
|
||
|
||
if (msg)
|
||
{
|
||
canlldbg("Control Registers: %s\n", msg);
|
||
}
|
||
else
|
||
{
|
||
canlldbg("Control Registers:\n");
|
||
}
|
||
|
||
/* CAN control and status registers */
|
||
|
||
lldbg(" MR: %08x IMR: %08x SR: %08x\n",
|
||
getreg32(config->base + SAM_CAN_MR_OFFSET),
|
||
getreg32(config->base + SAM_CAN_IMR_OFFSET),
|
||
getreg32(config->base + SAM_CAN_SR_OFFSET));
|
||
|
||
lldbg(" BR: %08x TIM: %08x TIMESTP: %08x\n",
|
||
getreg32(config->base + SAM_CAN_BR_OFFSET),
|
||
getreg32(config->base + SAM_CAN_TIM_OFFSET),
|
||
getreg32(config->base + SAM_CAN_TIMESTP_OFFSET));
|
||
|
||
lldbg(" ECR: %08x WPMR: %08x WPSR: %08x\n",
|
||
getreg32(config->base + SAM_CAN_ECR_OFFSET),
|
||
getreg32(config->base + SAM_CAN_TCR_OFFSET),
|
||
getreg32(config->base + SAM_CAN_ACR_OFFSET));
|
||
}
|
||
#endif
|
||
|
||
/****************************************************************************
|
||
* Name: can_dumpmbregs
|
||
*
|
||
* Description:
|
||
* Dump the contents of all CAN mailbox registers
|
||
*
|
||
* Input Parameters:
|
||
* priv - A reference to the CAN peripheral state
|
||
*
|
||
* Returned Value:
|
||
* None
|
||
*
|
||
****************************************************************************/
|
||
|
||
#ifdef CONFIG_SAMA5_CAN_REGDEBUG
|
||
static void can_dumpmbregs(FAR struct sam_can_s *priv, FAR const char *msg)
|
||
{
|
||
FAR const struct sam_config_s *config = priv->config;
|
||
uintptr_t mbbase;
|
||
int i;
|
||
|
||
if (msg)
|
||
{
|
||
canlldbg("Mailbox Registers: %s\n", msg);
|
||
}
|
||
else
|
||
{
|
||
canlldbg("Mailbox Registers:\n");
|
||
}
|
||
|
||
for (i = 0; i < SAM_CAN_NMAILBOXES; i++)
|
||
{
|
||
mbbase = config->base + SAM_CAN_MBn_OFFSET(i);
|
||
lldbg(" MB%d:\n", i);
|
||
|
||
/* CAN mailbox registers */
|
||
|
||
lldbg(" MMR: %08x MAM: %08x MID: %08x MFID: %08x\n",
|
||
getreg32(mbbase + SAM_CAN_MMR_OFFSET),
|
||
getreg32(mbbase + SAM_CAN_MAM_OFFSET),
|
||
getreg32(mbbase + SAM_CAN_MID_OFFSET),
|
||
getreg32(mbbase + SAM_CAN_MFID_OFFSET));
|
||
|
||
lldbg(" MSR: %08x MDL: %08x MDH: %08x\n",
|
||
getreg32(mbbase + SAM_CAN_MSR_OFFSET),
|
||
getreg32(mbbase + SAM_CAN_MDL_OFFSET),
|
||
getreg32(mbbase + SAM_CAN_MDH_OFFSET));
|
||
}
|
||
}
|
||
#endif
|
||
|
||
/****************************************************************************
|
||
* Name: can_semtake
|
||
*
|
||
* Description:
|
||
* Take a semaphore handling any exceptional conditions
|
||
*
|
||
* Input Parameters:
|
||
* priv - A reference to the CAN peripheral state
|
||
*
|
||
* Returned Value:
|
||
* None
|
||
*
|
||
****************************************************************************/
|
||
|
||
static void can_semtake(FAR struct sam_can_s *priv)
|
||
{
|
||
int ret;
|
||
|
||
/* Wait until we successfully get the semaphore. EINTR is the only
|
||
* expected 'failure' (meaning that the wait for the semaphore was
|
||
* interrupted by a signal.
|
||
*/
|
||
|
||
do
|
||
{
|
||
ret = sem_wait(&priv->exclsem);
|
||
DEBUGASSERT(ret == 0 || errno == EINTR);
|
||
}
|
||
while (ret < 0);
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: can_mballoc
|
||
*
|
||
* Description:
|
||
* Allocate one mailbox
|
||
*
|
||
* Input Parameter:
|
||
* priv - A pointer to the private data structure for this CAN peripheral
|
||
*
|
||
* Returned Value:
|
||
* A non-negative mailbox index; a negated errno value on failure.
|
||
*
|
||
* Assumptions:
|
||
* The caller has exclusive access to the can data structures
|
||
*
|
||
****************************************************************************/
|
||
|
||
static int can_mballoc(FAR struct sam_can_s *priv)
|
||
{
|
||
int i;
|
||
|
||
/* There any mailboxes free? */
|
||
|
||
if (priv->freemb)
|
||
{
|
||
/* Yes.. There are free mailboxes... pick one */
|
||
|
||
for (i = 0; i < SAM_CAN_NMAILBOXES; i++)
|
||
{
|
||
/* Is mailbox i availalbe? */
|
||
|
||
uint8_t bit = (1 << i);
|
||
if ((priv->freemb & bit) != 0)
|
||
{
|
||
/* No any more. Mark it allocated and return its index */
|
||
|
||
priv->freemb &= ~bit;
|
||
return i;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* No available mailboxes */
|
||
|
||
return -ENOMEM;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: can_mbfree
|
||
*
|
||
* Description:
|
||
* Free one mailbox
|
||
*
|
||
* Input Parameter:
|
||
* priv - A pointer to the private data structure for this CAN peripheral
|
||
* mbndx - Index of the mailbox to be freed
|
||
*
|
||
* Returned Value:
|
||
* None
|
||
*
|
||
* Assumptions:
|
||
* The caller has exclusive access to the can data structures
|
||
*
|
||
****************************************************************************/
|
||
|
||
static void can_mbfree(FAR struct sam_can_s *priv, int mbndx)
|
||
{
|
||
uint8_t bit;
|
||
|
||
DEBUGASSERT(priv && (unsigned)mbndx < SAM_CAN_NMAILBOXES);
|
||
|
||
/* Disable mailbox interrupts */
|
||
|
||
can_putreg(priv, SAM_CAN_IDR_OFFSET, CAN_INT_MB(mbndx));
|
||
|
||
/* Disable the mailbox */
|
||
|
||
can_putreg(priv, SAM_CAN_MnMR_OFFSET(mbndx), 0);
|
||
|
||
/* Free the mailbox by clearing the corresponding bit in the freemb and
|
||
* txmbset (only TX mailboxes are freed in this way.
|
||
*/
|
||
|
||
bit = (1 << mbndx);
|
||
DEBUGASSERT((priv->freemb & bit) != 0);
|
||
DEBUGASSERT((priv->txmbset & bit) != 0);
|
||
|
||
priv->freemb &= ~bit;
|
||
priv->txmbset &= ~bit;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: can_recvsetup
|
||
*
|
||
* Description:
|
||
* Configure and enable mailbox(es) for reception
|
||
*
|
||
* Input Parameter:
|
||
* priv - A pointer to the private data structure for this CAN peripheral
|
||
*
|
||
* Returned Value:
|
||
* Zero on success; a negated errno value on failure.
|
||
*
|
||
* Assumptions:
|
||
* Caller has exclusive access to the CAN data structures
|
||
* CAN interrupts are disabled at the AIC
|
||
*
|
||
****************************************************************************/
|
||
|
||
static int can_recvsetup(FAR struct sam_can_s *priv)
|
||
{
|
||
FAR const struct sam_config_s *config = priv->config;
|
||
int mbndx;
|
||
int mbno;
|
||
|
||
/* Setup the configured number of receive mailboxes */
|
||
|
||
priv->rxmbset = 0;
|
||
for (mbno = 0; mbno < config->nrecvmb; mbno++)
|
||
{
|
||
/* Allocate a(nother) receive mailbox */
|
||
|
||
mbndx = can_mballoc(priv);
|
||
if (mbndx < 0)
|
||
{
|
||
candbg("ERROR: Failed to allocate mailbox %d: %d\n", mbno, mbndx);
|
||
return mbndx;
|
||
}
|
||
|
||
/* Add the allocated mailbox to the set of receive mailboxes */
|
||
|
||
priv->rxmbset |= (1 << mbndx);
|
||
|
||
canvdbg("CAN%d Mailbox %d: Index=%d rxmbset=%02x\n",
|
||
config->port, mbno, mbndx, priv->rxmbset);
|
||
|
||
/* Set up the message ID and filter mask */
|
||
|
||
#ifdef CONFIG_CAN_EXTID
|
||
can_putreg(priv, SAM_CAN_MnID_OFFSET(mbndx),
|
||
CAN_MID_EXTID(config->filter[mbno].addr));
|
||
can_putreg(priv, SAM_CAN_MnAM_OFFSET(mbndx),
|
||
CAN_MAM_EXTID(config->filter[mbno].mask));
|
||
#else
|
||
can_putreg(priv, SAM_CAN_MnID_OFFSET(mbndx),
|
||
CAN_MID_STDID(config->filter[mbno].addr));
|
||
can_putreg(priv, SAM_CAN_MnAM_OFFSET(mbndx),
|
||
CAN_MAM_STDID(config->filter[mbno].mask));
|
||
#endif
|
||
|
||
/* Note: Chaining is not supported. All receive mailboxes are
|
||
* configured in normal receive mode.
|
||
*
|
||
* REVISIT: Chaining would be needed if you want to support
|
||
* multipart messages.
|
||
*/
|
||
|
||
can_putreg(priv, SAM_CAN_MnMR_OFFSET(mbndx), CAN_MMR_MOT_RX);
|
||
|
||
/* Clear pending interrupts and start reception of the next message */
|
||
|
||
can_putreg(priv, SAM_CAN_MnCR_OFFSET(mbndx), CAN_MCR_MTCR);
|
||
|
||
/* Enable interrupts from this mailbox */
|
||
|
||
can_putreg(priv, SAM_CAN_IER_OFFSET, CAN_INT_MB(mbndx));
|
||
}
|
||
|
||
return OK;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: can_reset
|
||
*
|
||
* Description:
|
||
* Reset the CAN device. Called early to initialize the hardware. This
|
||
* function is called, before can_setup() and on error conditions.
|
||
*
|
||
* Input Parameters:
|
||
* dev - An instance of the "upper half" can driver state structure.
|
||
*
|
||
* Returned Value:
|
||
* None
|
||
*
|
||
****************************************************************************/
|
||
|
||
static void can_reset(FAR struct can_dev_s *dev)
|
||
{
|
||
FAR struct sam_can_s *priv;
|
||
FAR const struct sam_config_s *config;
|
||
int i;
|
||
|
||
DEBUGASSERT(dev);
|
||
priv = dev->cd_priv;
|
||
DEBUGASSERT(priv);
|
||
config = priv->config;
|
||
DEBUGASSERT(config);
|
||
|
||
canllvdbg("CAN%d\n", config->port);
|
||
UNUSED(config);
|
||
|
||
/* Get exclusive access to the CAN peripheral */
|
||
|
||
can_semtake(priv);
|
||
|
||
/* Disable all interrupts */
|
||
|
||
can_putreg(priv, SAM_CAN_IDR_OFFSET, CAN_INT_ALL);
|
||
|
||
/* Disable all mailboxes */
|
||
|
||
for (i = 0; i < SAM_CAN_NMAILBOXES; i++)
|
||
{
|
||
can_putreg(priv, SAM_CAN_MnMR_OFFSET(i), 0);
|
||
}
|
||
|
||
/* All mailboxes are again available */
|
||
|
||
priv->freemb = CAN_ALL_MAILBOXES;
|
||
|
||
/* Disable the CAN controller */
|
||
|
||
can_putreg(priv, SAM_CAN_MR_OFFSET, 0);
|
||
can_semgive(priv);
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: can_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 can_setup(FAR struct can_dev_s *dev)
|
||
{
|
||
FAR struct sam_can_s *priv;
|
||
FAR const struct sam_config_s *config;
|
||
int ret;
|
||
|
||
DEBUGASSERT(dev);
|
||
priv = dev->cd_priv;
|
||
DEBUGASSERT(priv);
|
||
config = priv->config;
|
||
DEBUGASSERT(config);
|
||
|
||
canllvdbg("CAN%d pid: %d\n", config->port, config->pid);
|
||
|
||
/* Get exclusive access to the CAN peripheral */
|
||
|
||
can_semtake(priv);
|
||
|
||
/* CAN hardware initialization */
|
||
|
||
ret = can_hwinitialize(priv);
|
||
if (ret < 0)
|
||
{
|
||
canlldbg("CAN%d H/W initialization failed: %d\n", config->port, ret);
|
||
return ret;
|
||
}
|
||
|
||
can_dumpctrlregs(priv, "After hardware initialization");
|
||
can_dumpmbregs(priv, NULL);
|
||
|
||
/* Attach the CAN interrupt handler */
|
||
|
||
ret = irq_attach(config->pid, config->handler);
|
||
if (ret < 0)
|
||
{
|
||
canlldbg("Failed to attach CAN%d IRQ (%d)", config->port, config->pid);
|
||
return ret;
|
||
}
|
||
|
||
/* Setup receive mailbox(es) (enabling receive interrupts) */
|
||
|
||
ret = can_recvsetup(priv);
|
||
if (ret < 0)
|
||
{
|
||
canlldbg("CAN%d H/W initialization failed: %d\n", config->port, ret);
|
||
return ret;
|
||
}
|
||
|
||
/* Enable all error interrupts */
|
||
|
||
#ifdef CONFIG_DEBUG
|
||
can_putreg(priv, SAM_CAN_IER_OFFSET, CAN_DEBUG_INTS);
|
||
#endif
|
||
|
||
can_dumpctrlregs(priv, "After receive setup");
|
||
can_dumpmbregs(priv, NULL);
|
||
|
||
/* Enable the interrupts at the AIC. */
|
||
|
||
up_enable_irq(config->pid);
|
||
can_semgive(priv);
|
||
return OK;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: can_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 can_shutdown(FAR struct can_dev_s *dev)
|
||
{
|
||
FAR struct sam_can_s *priv;
|
||
FAR const struct sam_config_s *config;
|
||
|
||
DEBUGASSERT(dev);
|
||
priv = dev->cd_priv;
|
||
DEBUGASSERT(priv);
|
||
config = priv->config;
|
||
DEBUGASSERT(config);
|
||
|
||
canllvdbg("CAN%d\n", config->port);
|
||
|
||
/* Get exclusive access to the CAN peripheral */
|
||
|
||
can_semtake(priv);
|
||
|
||
/* Disable the CAN interrupts */
|
||
|
||
up_disable_irq(config->pid);
|
||
|
||
/* Detach the CAN interrupt handler */
|
||
|
||
irq_detach(config->pid);
|
||
|
||
/* And reset the hardware */
|
||
|
||
can_reset(dev);
|
||
can_semgive(priv);
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: can_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 can_rxint(FAR struct can_dev_s *dev, bool enable)
|
||
{
|
||
FAR struct sam_can_s *priv = dev->cd_priv;
|
||
DEBUGASSERT(priv && priv->config);
|
||
|
||
canllvdbg("CAN%d enable: %d\n", priv->config->port, enable);
|
||
|
||
/* Enable/disable the mailbox interrupts from all receive mailboxes */
|
||
|
||
if (enable)
|
||
{
|
||
can_putreg(priv, SAM_CAN_IER_OFFSET, priv->rxmbset);
|
||
}
|
||
else
|
||
{
|
||
can_putreg(priv, SAM_CAN_IDR_OFFSET, priv->rxmbset);
|
||
}
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: can_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 can_txint(FAR struct can_dev_s *dev, bool enable)
|
||
{
|
||
FAR struct sam_can_s *priv = dev->cd_priv;
|
||
DEBUGASSERT(priv && priv->config);
|
||
|
||
canllvdbg("CAN%d enable: %d\n", priv->config->port, enable);
|
||
|
||
/* Get exclusive access to the CAN peripheral */
|
||
|
||
can_semtake(priv);
|
||
|
||
/* Support disabling interrupts on any mailboxes that are actively
|
||
* transmitting (txmbset); also suppress enabling new TX mailbox until
|
||
* txdisabled is reset by this function.
|
||
*/
|
||
|
||
if (enable)
|
||
{
|
||
can_putreg(priv, SAM_CAN_IER_OFFSET, priv->txmbset);
|
||
priv->txdisabled = false;
|
||
}
|
||
else
|
||
{
|
||
can_putreg(priv, SAM_CAN_IDR_OFFSET, priv->txmbset);
|
||
priv->txdisabled = true;
|
||
}
|
||
|
||
can_semgive(priv);
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: can_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 can_ioctl(FAR struct can_dev_s *dev, int cmd, unsigned long arg)
|
||
{
|
||
/* No CAN ioctls are supported */
|
||
|
||
return -ENOTTY;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: can_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 can_remoterequest(FAR struct can_dev_s *dev, uint16_t id)
|
||
{
|
||
/* REVISIT: Remote request not implemented */
|
||
|
||
return -ENOSYS;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: can_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 Tranmission 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 can_send(FAR struct can_dev_s *dev, FAR struct can_msg_s *msg)
|
||
{
|
||
FAR struct sam_can_s *priv;
|
||
FAR uint8_t *ptr;
|
||
uint32_t regval;
|
||
int mbndx;
|
||
|
||
DEBUGASSERT(dev);
|
||
priv = dev->cd_priv;
|
||
DEBUGASSERT(priv && priv->config);
|
||
|
||
canllvdbg("CAN%d\n", priv->config->port);
|
||
canllvdbg("CAN%d ID: %d DLC: %d\n",
|
||
priv->config->port, msg->cm_hdr.ch_id, msg->cm_hdr.ch_dlc);
|
||
|
||
/* Get exclusive access to the CAN peripheral */
|
||
|
||
can_semtake(priv);
|
||
|
||
/* Allocate a mailbox */
|
||
|
||
mbndx = can_mballoc(priv);
|
||
if (mbndx < 0)
|
||
{
|
||
candbg("ERROR: CAN%d failed to allocate a mailbox: %d\n",
|
||
priv->config->port, mbndx);
|
||
return mbndx;
|
||
}
|
||
|
||
priv->txmbset |= (1 << mbndx);
|
||
|
||
canvdbg("Mailbox Index=%d txmbset=%02x\n", mbndx, priv->txmbset);
|
||
|
||
/* Set up the ID and mask, standard 11-bit or extended 29-bit. */
|
||
|
||
#ifdef CONFIG_CAN_EXTID
|
||
DEBUGASSERT(msg->cm_hdr.ch_extid);
|
||
DEBUGASSERT(msg->cm_hdr.ch_id < (1 << 29));
|
||
can_putreg(priv, SAM_CAN_MnID_OFFSET(mbndx), CAN_MID_EXTID(msg->cm_hdr.ch_id));
|
||
#else
|
||
DEBUGASSERT(!msg->cm_hdr.ch_extid);
|
||
DEBUGASSERT(msg->cm_hdr.ch_id < (1 << 11));
|
||
can_putreg(priv, SAM_CAN_MnID_OFFSET(mbndx), CAN_MID_STDID(msg->cm_hdr.ch_id));
|
||
#endif
|
||
|
||
/* Enable transmit mode */
|
||
|
||
can_putreg(priv, SAM_CAN_MnMR_OFFSET(mbndx), CAN_MMR_MOT_TX);
|
||
|
||
/* After Transmit Mode is enabled, the MRDY flag in the CAN_MSR register
|
||
* is automatically set until the first command is sent. When the MRDY
|
||
* flag is set, the software application can prepare a message to be sent
|
||
* by writing to the CAN_MDx registers. The message is sent once the
|
||
* software asks for a transfer command setting the MTCR bit and the
|
||
* message data length in the CAN_MCRx register.
|
||
*/
|
||
|
||
DEBUGASSERT((can_getreg(priv, SAM_CAN_MnSR_OFFSET(mbndx)) & CAN_MSR_MRDY) != 0);
|
||
|
||
/* Bytes are received/sent on the bus in the following order:
|
||
*
|
||
* 1. CAN_MDL[7:0]
|
||
* 2. CAN_MDL[15:8]
|
||
* 3. CAN_MDL[23:16]
|
||
* 4. CAN_MDL[31:24]
|
||
* 5. CAN_MDH[7:0]
|
||
* 6. CAN_MDH[15:8]
|
||
* 7. CAN_MDH[23:16]
|
||
* 8. CAN_MDH[31:24]
|
||
*/
|
||
|
||
#ifdef CONFIG_ENDIAN_BIG
|
||
# warning REVISIT
|
||
#endif
|
||
|
||
/* The message buffer is probably not properaly aligned for 32-bit accesses */
|
||
|
||
ptr = msg->cm_data;
|
||
regval = CAN_MDL0(ptr[0]) | CAN_MDL1(ptr[1]) | CAN_MDL2(ptr[1]) | CAN_MDL3(ptr[1]);
|
||
can_putreg(priv, SAM_CAN_MnDL_OFFSET(mbndx), regval);
|
||
|
||
regval = CAN_MDH4(ptr[4]) | CAN_MDH5(ptr[5]) | CAN_MDH6(ptr[6]) | CAN_MDH7(ptr[7]);
|
||
can_putreg(priv, SAM_CAN_MnDH_OFFSET(mbndx), regval);
|
||
|
||
/* Set the DLC value in the CAN_MCRx register. Set the MTCR register
|
||
* clearing MRDY, and indicating that the message is ready to be sent.
|
||
*/
|
||
|
||
regval = CAN_MCR_MDLC(msg->cm_hdr.ch_dlc) | CAN_MCR_MTCR;
|
||
can_putreg(priv, SAM_CAN_MnCR_OFFSET(mbndx), regval);
|
||
|
||
/* If we have not been asked to suppress TX interrupts, then dnable
|
||
* interrupts from this mailbox now.
|
||
*/
|
||
|
||
if (!priv->txdisabled)
|
||
{
|
||
can_putreg(priv, SAM_CAN_IER_OFFSET, CAN_INT_MB(mbndx));
|
||
}
|
||
|
||
can_dumpmbregs(priv, "After send");
|
||
can_semgive(priv);
|
||
return OK;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: can_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 can_txready(FAR struct can_dev_s *dev)
|
||
{
|
||
FAR struct sam_can_s *priv = dev->cd_priv;
|
||
bool txready;
|
||
|
||
/* Get exclusive access to the CAN peripheral */
|
||
|
||
can_semtake(priv);
|
||
|
||
/* Return true not all mailboxes are in-use */
|
||
|
||
txready = ((priv->rxmbset | priv->txmbset) != CAN_ALL_MAILBOXES);
|
||
|
||
can_semgive(priv);
|
||
return txready;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: can_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 can_txempty(FAR struct can_dev_s *dev)
|
||
{
|
||
FAR struct sam_can_s *priv = dev->cd_priv;
|
||
return (priv->txmbset == 0);
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: can_rxinterrupt
|
||
*
|
||
* Description:
|
||
* CAN RX mailbox interrupt handler
|
||
*
|
||
* Input Parameters:
|
||
* priv - CAN-specific private data
|
||
* mbndx - The index of the mailbox that generated the interrupt
|
||
* msr - Applicable value from the mailbox status register
|
||
*
|
||
* Returned Value:
|
||
* None
|
||
*
|
||
****************************************************************************/
|
||
|
||
static inline void can_rxinterrupt(FAR struct can_dev_s *dev, int mbndx,
|
||
uint32_t msr)
|
||
{
|
||
FAR struct sam_can_s *priv = dev->cd_priv;
|
||
struct can_hdr_s hdr;
|
||
uint32_t md[2];
|
||
uint32_t mid;
|
||
int ret;
|
||
|
||
/* REVISIT: Check the MMI bit in CAN_MSRx to determine messages have been
|
||
* lost.
|
||
*/
|
||
|
||
/* Read the mailbox data. Bytes are received/sent on the bus in the
|
||
* following order:
|
||
*
|
||
* 1. CAN_MDL[7:0]
|
||
* 2. CAN_MDL[15:8]
|
||
* 3. CAN_MDL[23:16]
|
||
* 4. CAN_MDL[31:24]
|
||
* 5. CAN_MDH[7:0]
|
||
* 6. CAN_MDH[15:8]
|
||
* 7. CAN_MDH[23:16]
|
||
* 8. CAN_MDH[31:24]
|
||
*/
|
||
|
||
#ifdef CONFIG_ENDIAN_BIG
|
||
# warning REVISIT
|
||
#endif
|
||
|
||
md[0] = can_getreg(priv, SAM_CAN_MnDH_OFFSET(mbndx));
|
||
md[1] = can_getreg(priv, SAM_CAN_MnDL_OFFSET(mbndx));
|
||
|
||
/* Get the ID associated with the newly received message: )nce a new message
|
||
* is received, its ID is masked with the CAN_MAMx value and compared
|
||
* with the CAN_MIDx value. If accepted, the message ID is copied to the
|
||
* CAN_MIDx register.
|
||
*/
|
||
|
||
mid = can_getreg(priv, SAM_CAN_MnID_OFFSET(mbndx));
|
||
|
||
/* Format the CAN header */
|
||
|
||
#ifdef CONFIG_CAN_EXTID
|
||
/* Save the extended ID of the newly received message */
|
||
|
||
hdr.ch_id = (mid & CAN_MAM_EXTID_MASK) >> CAN_MAM_EXTID_SHIFT;
|
||
hdr.ch_dlc = (msr & CAN_MSR_MDLC_MASK) >> CAN_MSR_MDLC_SHIFT;
|
||
hdr.ch_rtr = 0;
|
||
hdr.ch_extid = true;
|
||
hdr.ch_unused = 0;
|
||
#else
|
||
/* Save the standard ID of the newly received message */
|
||
|
||
hdr.ch_dlc = (msr & CAN_MSR_MDLC_MASK) >> CAN_MSR_MDLC_SHIFT;
|
||
hdr.ch_rtr = 0;
|
||
hdr.ch_id = (mid & CAN_MAM_STDID_MASK) >> CAN_MAM_STDID_SHIFT;
|
||
#endif
|
||
|
||
/* And provide the CAN message to the upper half logic */
|
||
|
||
ret = can_receive(dev, &hdr, (FAR uint8_t *)md);
|
||
if (ret < 0)
|
||
{
|
||
canlldbg("ERROR: can_receive failed: %d\n", ret);
|
||
}
|
||
|
||
/* Set the MTCR flag in the CAN_MCRx register. This clears the
|
||
* MRDY bit, notifices the hardware that processing has ended, and
|
||
* requests a new RX transfer.
|
||
*/
|
||
|
||
can_putreg(priv, SAM_CAN_MnCR_OFFSET(mbndx), CAN_MCR_MTCR);
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: can_txinterrupt
|
||
*
|
||
* Description:
|
||
* CAN TX mailbox interrupt handler
|
||
*
|
||
* Input Parameters:
|
||
* priv - CAN-specific private data
|
||
* mbndx - The index of the mailbox that generated the interrupt
|
||
*
|
||
* Returned Value:
|
||
* None
|
||
*
|
||
****************************************************************************/
|
||
|
||
static inline void can_txinterrupt(FAR struct can_dev_s *dev, int mbndx)
|
||
{
|
||
FAR struct sam_can_s *priv = dev->cd_priv;
|
||
|
||
/* REVISIT: Check the MABT bit in CAN_MSRx to determine if the transfer
|
||
* was aborted.
|
||
*/
|
||
|
||
/* Disable and free the mailbox */
|
||
|
||
can_mbfree(priv, mbndx);
|
||
|
||
/* Report that the TX transfer is complete to the upper half logic */
|
||
|
||
can_txdone(dev);
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: can_mbinterrupt
|
||
*
|
||
* Description:
|
||
* CAN mailbox interrupt handler
|
||
*
|
||
* Input Parameters:
|
||
* priv - CAN-specific private data
|
||
* mbndx - The index of the mailbox that generated the interrupt
|
||
*
|
||
* Returned Value:
|
||
* None
|
||
*
|
||
****************************************************************************/
|
||
|
||
static inline void can_mbinterrupt(FAR struct can_dev_s *dev, int mbndx)
|
||
{
|
||
FAR struct sam_can_s *priv = dev->cd_priv;
|
||
uint32_t msr;
|
||
uint32_t mmr;
|
||
|
||
/* There are two causes of mailbox interrupts:
|
||
*
|
||
* - Data registers in the mailbox object are available to the
|
||
* application. In Receive Mode, a new message was received. In Transmit
|
||
* Mode, a message was transmitted successfully.
|
||
* - A sent transmission was aborted.
|
||
*
|
||
* Both conditions are are reported by the MRDY bit in the CAN_MSR
|
||
* register.
|
||
*/
|
||
|
||
msr = can_getreg(priv, SAM_CAN_MnSR_OFFSET(mbndx));
|
||
if ((msr & (CAN_MSR_MRDY | CAN_MSR_MABT)) != 0)
|
||
{
|
||
/* Handle the result based on how the mailbox was configured */
|
||
|
||
mmr = can_getreg(priv, SAM_CAN_MnMR_OFFSET(mbndx));
|
||
switch (mmr & CAN_MMR_MOT_MASK)
|
||
{
|
||
case CAN_MMR_MOT_RX: /* Reception Mailbox */
|
||
can_rxinterrupt(dev, mbndx, msr);
|
||
break;
|
||
|
||
case CAN_MMR_MOT_TX: /* Transmit mailbox */
|
||
can_txinterrupt(dev, mbndx);
|
||
break;
|
||
|
||
case CAN_MMR_MOT_RXOVRWR: /* Reception mailbox with overwrite */
|
||
case CAN_MMR_MOT_CONSUMER: /* Consumer Mailbox */
|
||
case CAN_MMR_MOT_PRODUCER: /* Producer Mailbox */
|
||
case CAN_MMR_MOT_DISABLED: /* Mailbox is disabled */
|
||
canlldbg("ERROR: CAN%d MB%d: Unsupported or invalid mailbox type\n",
|
||
priv->config->port, mbndx);
|
||
canlldbg(" MSR: %08x MMR: %08x\n", msr, mmr);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: can_interrupt
|
||
*
|
||
* Description:
|
||
* Common CAN interrupt handler
|
||
*
|
||
* Input Parameters:
|
||
* priv - CAN-specific private data
|
||
*
|
||
* Returned Value:
|
||
* None
|
||
*
|
||
****************************************************************************/
|
||
|
||
static void can_interrupt(FAR struct can_dev_s *dev)
|
||
{
|
||
FAR struct sam_can_s *priv = dev->cd_priv;
|
||
uint32_t sr;
|
||
uint32_t imr;
|
||
uint32_t pending;
|
||
|
||
DEBUGASSERT(priv && priv->config);
|
||
|
||
/* Get the set of pending interrupts.
|
||
*
|
||
* All interrupts are cleared by clearing the interrupt source except for
|
||
* the internal timer counter overflow interrupt and the timestamp interrupt.
|
||
* These interrupts are cleared by reading the CAN_SR register.
|
||
*/
|
||
|
||
sr = can_getreg(priv, SAM_CAN_SR_OFFSET);
|
||
imr = can_getreg(priv, SAM_CAN_IMR_OFFSET);
|
||
pending = sr & imr;
|
||
|
||
/* There are two different types of interrupts. One type of interrupt is a
|
||
* message-object related interrupt, the other is a system interrupt that
|
||
* handles errors or system-related interrupt sources.
|
||
*/
|
||
|
||
/* Check for message related interrupts
|
||
*
|
||
* - Data registers in the mailbox object are available to the
|
||
* application. In Receive Mode, a new message was received. In Transmit
|
||
* Mode, a message was transmitted successfully.
|
||
* - A sent transmission was aborted.
|
||
*/
|
||
|
||
if ((pending & CAN_INT_MBALL) != 0)
|
||
{
|
||
int mbndx;
|
||
|
||
/* Check for pending interrupts from each mailbox */
|
||
|
||
for (mbndx = 0; mbndx < SAM_CAN_NMAILBOXES; mbndx++)
|
||
{
|
||
/* Check for a pending interrupt for this mailbox */
|
||
|
||
if ((pending & CAN_INT_MB(mbndx)) != 0)
|
||
{
|
||
can_mbinterrupt(dev, mbndx);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Check for system interrupts
|
||
*
|
||
* - Bus off interrupt: The CAN module enters the bus off state.
|
||
* - Error passive interrupt: The CAN module enters Error Passive Mode.
|
||
* - Error Active Mode: The CAN module is neither in Error Passive Mode
|
||
* nor in Bus Off mode.
|
||
* - Warn Limit interrupt: The CAN module is in Error-active Mode, but at
|
||
* least one of its error counter value exceeds 96.
|
||
* - Wake-up interrupt: This interrupt is generated after a wake-up and a
|
||
* bus synchronization.
|
||
* - Sleep interrupt: This interrupt is generated after a Low-power Mode
|
||
* enable once all pending messages in transmission have been sent.
|
||
* - Internal timer counter overflow interrupt: This interrupt is
|
||
* generated when the internal timer rolls over.
|
||
* - Timestamp interrupt: This interrupt is generated after the reception
|
||
* or the transmission of a start of frame or an end of frame. The value
|
||
* of the internal counter is copied in the CAN_TIMESTP register.
|
||
*/
|
||
|
||
if ((pending & ~CAN_INT_MBALL) != 0)
|
||
{
|
||
canlldbg("ERROR: CAN%d system interrupt, SR=%08x IMR=%08x\n",
|
||
priv->config->port, sr, imr);
|
||
}
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: can0_interrupt
|
||
*
|
||
* Description:
|
||
* CAN0 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
|
||
*
|
||
****************************************************************************/
|
||
|
||
#ifdef CONFIG_SAMA5_CAN0
|
||
static int can0_interrupt(int irq, void *context)
|
||
{
|
||
can_interrupt(&g_can0dev);
|
||
return OK;
|
||
}
|
||
#endif
|
||
|
||
/****************************************************************************
|
||
* Name: can0_interrupt
|
||
*
|
||
* Description:
|
||
* CAN0 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
|
||
*
|
||
****************************************************************************/
|
||
|
||
#ifdef CONFIG_SAMA5_CAN1
|
||
static int can1_interrupt(int irq, void *context)
|
||
{
|
||
can_interrupt(&g_can1dev);
|
||
return OK;
|
||
}
|
||
#endif
|
||
|
||
/****************************************************************************
|
||
* Name: can_bittiming
|
||
*
|
||
* Description:
|
||
* Set the CAN baudrate register (BR) based on the configured BAUD.
|
||
*
|
||
* Definitions:
|
||
*
|
||
* TIME QUANTUM. The TIME QUANTUM (Tq) is a fixed unit of time derived
|
||
* from the MCK period. The total number of TIME QUANTA in a bit time is
|
||
* programmable from 8 to 25.
|
||
*
|
||
* INFORMATION PROCESSING TIME. The Information Processing Time (IPT)
|
||
* is the time required for the logic to determine the bit level of a
|
||
* sampled bit. The IPT begins at the sample point, is measured in Tq
|
||
* and is fixed at 2 Tq for the Atmel CAN.
|
||
*
|
||
* SAMPLE POINT. The SAMPLE POINT is the point in time at which the
|
||
* bus level is read and interpreted as the value of that respective
|
||
* bit. Its location is at the end of PHASE_SEG1.
|
||
*
|
||
* The CAN protocol specification partitions the nominal bit time into
|
||
* four different segments:
|
||
*
|
||
* 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. Propogation segment (PROP_SEG): This part of the bit time is used
|
||
* to compensate for the physical delay times within the network. It is
|
||
* twice the sum of the signal<61>s propagation time on the bus line, the
|
||
* input comparator delay, and the output driver delay. It is
|
||
* programmable to be 1 to 8 Tq long. This parameter is defined in the
|
||
* PROPAG field of the CAN Baudrate Register.
|
||
* 3. Phase segment 1 (PHASE_SEG1): defines the location of the sample
|
||
* point. Phase Segment 1 is programmable to be 1-8 Tq long.
|
||
* 4. Phase segement 2 (PHASE_SEG2): defines the location of the transmit
|
||
* point.Phase Segment 2 length has to be at least as long as the
|
||
* Information Processing Time (IPT) and may not be more than the
|
||
* length of Phase Segment 1 (since Phase Segment 2 also begins at the
|
||
* sample point and is the last segment in the bit time).
|
||
*
|
||
* The Phase-Buffer-Segments are used to compensate for edge phase errors.
|
||
* These segments can be lengthened (PHASE SEG1) or shortened (PHASE SEG2)
|
||
* by resynchronization:
|
||
*
|
||
* SJW: ReSynchronization Jump Width. The ReSynchronization Jump Width
|
||
* defines the limit to the amount of lengthening or shortening of the
|
||
* Phase Segments. SJW is programmable to be the minimum of PHASE SEG1
|
||
* and 4 Tq.
|
||
*
|
||
* In the CAN controller, the length of a bit on the CAN bus is determined
|
||
* by the parameters (BRP, PROPAG, PHASE1 and PHASE2).
|
||
*
|
||
* Tbit = Tcsc + Tprs + Tphs1 + Tphs2
|
||
*
|
||
* Pictorially:
|
||
*
|
||
* |<----------------------- NOMINAL BIT TIME ------------------------>|
|
||
* |<- SYNC_SEG ->|<- PROP_SEG ->|<-- PHASE_SEG1 -->|<-- PHASE_SEG2 -->|
|
||
* |<--- Tcsc --->|<--- Tprs --->|<---- Tphs1 ----->|<---- Tphs2 ----->|
|
||
* |<--- 1 Tq --->|<-- 1-8 Tq -->|<---- 1-8 Tq ---->|<--- <= Tphs1 --->|
|
||
*
|
||
* Where
|
||
* Tcsc is the duration of the SYNC_SEG segment
|
||
* Tprs is the duration of the PROP_SEG segment
|
||
* Tphs1 is the duration of the PHASE_SEG1 segment
|
||
* Tphs2 is the duration of the PHASE_SEG2 segment
|
||
* Tq is the "Time Quantum"
|
||
*
|
||
* Relationships:
|
||
*
|
||
* baud = 1 / Tbit
|
||
* Tbit = Tq + Tprs + Tphs1 + Tphs2
|
||
* Tq = (BRP + 1) / MCK
|
||
* Tprs = Tq * (PROPAG + 1)
|
||
* Tphs1 = Tq * (PHASE1 + 1)
|
||
* Tphs2 = Tq * (PHASE2 + 1)
|
||
*
|
||
* Input Parameter:
|
||
* config - A reference to the CAN constant configuration
|
||
*
|
||
* Returned Value:
|
||
* Zero on success; a negated errno on failure
|
||
*
|
||
****************************************************************************/
|
||
|
||
static int can_bittiming(struct sam_can_s *priv)
|
||
{
|
||
FAR const struct sam_config_s *config = priv->config;
|
||
uint32_t regval;
|
||
uint32_t brp;
|
||
uint32_t propag;
|
||
uint32_t phase1;
|
||
uint32_t phase2;
|
||
uint32_t sjw;
|
||
uint32_t t1t2;
|
||
uint8_t tq;
|
||
|
||
/* Select the time quantum
|
||
*
|
||
* REVISIT: We could probably do a better job than this.
|
||
*/
|
||
|
||
if (config->baud >= 1000)
|
||
{
|
||
tq = 8;
|
||
}
|
||
else
|
||
{
|
||
tq = 16;
|
||
}
|
||
|
||
/* Calculate the baudrate prescaler (BRP). This depends only on the
|
||
* selected Tq value, the desired BAUD and the CAN peripheral clock
|
||
* frequency.
|
||
*
|
||
* Tq = (BRP + 1) / CAN_FRQUENCY
|
||
* Tbit = Nquanta * (BRP + 1) / Fcan
|
||
* baud = Fcan / (Nquanta * (brp + 1))
|
||
* brp = Fcan / (baud * nquanta) - 1
|
||
*/
|
||
|
||
brp = (priv->frequency / (config->baud * 1000 * tq)) - 1;
|
||
if (brp == 0)
|
||
{
|
||
/* The BRP field must be within the range 1 - 0x7f */
|
||
|
||
candbg("CAN%d: baud %d too high\n", config->port, config->baud);
|
||
return -EINVAL;
|
||
}
|
||
|
||
/* Propagation delay:
|
||
*
|
||
* Delay Bus Driver - 50ns
|
||
* Delay Receiver - 30ns
|
||
* Delay Bus Line (20m) - 110ns
|
||
*/
|
||
|
||
propag = tq * config->baud * 2 * (50 + 30 + 110) / 1000000;
|
||
if (propag >= 1)
|
||
{
|
||
propag--;
|
||
}
|
||
else
|
||
{
|
||
propag = 0;
|
||
}
|
||
|
||
/* This the time of the first two segments */
|
||
|
||
t1t2 = tq - 1 - (propag + 1);
|
||
|
||
/* Calcuate phase1 and phase2 */
|
||
|
||
phase1 = (t1t2 >> 1) - 1;
|
||
phase2 = phase1;
|
||
|
||
if ((t1t2 & 1) != 0)
|
||
{
|
||
phase2++;
|
||
}
|
||
|
||
/* Calculate SJW */
|
||
|
||
if (1 > (4 / (phase1 + 1)))
|
||
{
|
||
sjw = 3;
|
||
}
|
||
else
|
||
{
|
||
sjw = phase1;
|
||
}
|
||
|
||
if ((propag + phase1 + phase2) != (uint32_t)(tq - 4))
|
||
{
|
||
candbg("CAN%d: Could not realize baud %d\n", config->port, config->baud);
|
||
return -EINVAL;
|
||
}
|
||
|
||
regval = CAN_BR_PHASE2(phase2) | CAN_BR_PHASE1(phase1) |
|
||
CAN_BR_PROPAG(propag) | CAN_BR_SJW(sjw) | CAN_BR_BRP(brp) |
|
||
CAN_BR_ONCE;
|
||
can_putreg(priv, SAM_CAN_BR_OFFSET, regval);
|
||
return OK;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: can_autobaud
|
||
*
|
||
* Description:
|
||
* Use the SAMA5 auto-baud feature to correct the initial timing
|
||
*
|
||
* Input Parameter:
|
||
* priv - A pointer to the private data structure for this CAN block
|
||
*
|
||
* Returned Value:
|
||
* Zero on success; a negated errno value on failure.
|
||
*
|
||
****************************************************************************/
|
||
|
||
#ifdef CONFIG_SAMA5_CAN_AUTOBAUD
|
||
static int can_autobaud(struct sam_can_s *priv)
|
||
{
|
||
volatile uint32_t timeout;
|
||
uint32_t regval;
|
||
int ret;
|
||
|
||
canllvdbg("CAN%d\n", config->port);
|
||
|
||
/* The CAN controller can start listening to the network in Autobaud Mode.
|
||
* In this case, the error counters are locked and a mailbox may be
|
||
* configured in Receive Mode. By scanning error flags, the CAN_BR
|
||
* register values synchronized with the network.
|
||
*/
|
||
|
||
/* Configure a Mailbox in Reception Mode */
|
||
#warning Missing Logic
|
||
|
||
/* Loop, adjusting bit rate parameters until no errors are reported in
|
||
* either CAR_SR or the CAN_MSRx registers.
|
||
*/
|
||
|
||
do
|
||
{
|
||
/* Adjust baud rate setting */
|
||
#warning Missing Logic
|
||
|
||
/* Autobaud Mode. The autobaud feature is enabled by setting the ABM
|
||
* field in the CAN_MR register. In this mode, the CAN controller is only
|
||
* listening to the line without acknowledging the received messages. It
|
||
* can not send any message. The errors flags are updated. The bit timing
|
||
* can be adjusted until no error occurs (good configuration found). In
|
||
* this mode, the error counters are frozen.
|
||
*/
|
||
|
||
regval = can_getreg(priv, SAM_CAN_MR_OFFSET);
|
||
regval |= (CAN_MR_CANEN | CAN_MR_ABM);
|
||
can_putreg(priv, SAM_CAN_MR_OFFSET, regval);
|
||
|
||
#warning Missing logic
|
||
}
|
||
while ( no errors reported );
|
||
|
||
/* Once no error has been detected, the application disables the Autobaud
|
||
* Mode, clearing the ABM field in the CAN_MR register. To go back to the
|
||
* standard mode, the ABM bit must be cleared in the CAN_MR register.
|
||
*/
|
||
|
||
regval = can_getreg(priv, SAM_CAN_MR_OFFSET);
|
||
regval &= ~(CAN_MR_CANEN | CAN_MR_ABM);
|
||
can_putreg(priv, SAM_CAN_MR_OFFSET, regval);
|
||
|
||
return OK;
|
||
}
|
||
#endif
|
||
|
||
/****************************************************************************
|
||
* Name: can_hwinitialize
|
||
*
|
||
* Description:
|
||
* CAN cell initialization
|
||
*
|
||
* Input Parameter:
|
||
* priv - A pointer to the private data structure for this CAN peripheral
|
||
*
|
||
* Returned Value:
|
||
* Zero on success; a negated errno value on failure.
|
||
*
|
||
****************************************************************************/
|
||
|
||
static int can_hwinitialize(struct sam_can_s *priv)
|
||
{
|
||
FAR const struct sam_config_s *config = priv->config;
|
||
uint32_t regval;
|
||
uint32_t mck;
|
||
int ret;
|
||
|
||
canllvdbg("CAN%d\n", config->port);
|
||
|
||
/* Configure CAN pins */
|
||
|
||
sam_configpio(config->rxpinset);
|
||
sam_configpio(config->txpinset);
|
||
|
||
/* Determine the maximum CAN peripheral clock frequency */
|
||
|
||
mck = BOARD_MCK_FREQUENCY;
|
||
if (mck <= SAM_CAN_MAXPERCLK)
|
||
{
|
||
priv->frequency = mck;
|
||
regval = PMC_PCR_DIV1;
|
||
}
|
||
else if ((mck >> 1) <= SAM_CAN_MAXPERCLK)
|
||
{
|
||
priv->frequency = (mck >> 1);
|
||
regval = PMC_PCR_DIV2;
|
||
}
|
||
else if ((mck >> 2) <= SAM_CAN_MAXPERCLK)
|
||
{
|
||
priv->frequency = (mck >> 2);
|
||
regval = PMC_PCR_DIV4;
|
||
}
|
||
else if ((mck >> 3) <= SAM_CAN_MAXPERCLK)
|
||
{
|
||
priv->frequency = (mck >> 3);
|
||
regval = PMC_PCR_DIV8;
|
||
}
|
||
else
|
||
{
|
||
candbg("ERROR: Cannot realize CAN input frequency\n");
|
||
return -EINVAL;
|
||
}
|
||
|
||
/* Set the maximum CAN peripheral clock frequency */
|
||
|
||
regval |= PMC_PCR_PID(config->pid) | PMC_PCR_CMD | PMC_PCR_EN;
|
||
can_putreg(priv, SAM_PMC_PCR, regval);
|
||
|
||
/* Enable peripheral clocking */
|
||
|
||
sam_enableperiph1(config->pid);
|
||
|
||
/* Disable all CAN interrupts */
|
||
|
||
can_putreg(priv, SAM_CAN_IDR_OFFSET, CAN_INT_ALL);
|
||
|
||
/* Configure bit timing. */
|
||
|
||
ret = can_bittiming(priv);
|
||
if (ret < 0)
|
||
{
|
||
candbg("ERROR: Failed to set bit timing: %d\n", ret);
|
||
return ret;
|
||
}
|
||
|
||
#ifdef CONFIG_SAMA5_CAN_AUTOBAUD
|
||
/* Optimize/correct bit timing */
|
||
|
||
ret = can_autobaud(priv);
|
||
if (ret < 0)
|
||
{
|
||
candbg("ERROR: can_autobaud failed: %d\n", ret);
|
||
return ret;
|
||
}
|
||
#endif
|
||
|
||
/* The CAN controller is enabled by setting the CANEN flag in the CAN_MR
|
||
* register. At this stage, the internal CAN controller state machine is
|
||
* reset, error counters are reset to 0, error flags are reset to 0.
|
||
*/
|
||
|
||
regval = can_getreg(priv, SAM_CAN_MR_OFFSET);
|
||
regval |= CAN_MR_CANEN;
|
||
can_putreg(priv, SAM_CAN_MR_OFFSET, regval);
|
||
|
||
/* Once the CAN controller is enabled, bus synchronization is done
|
||
* automatically by scanning eleven recessive bits. The WAKEUP bit in
|
||
* the CAN_SR register is automatically set to 1 when the CAN controller
|
||
* is synchronized (WAKEUP and SLEEP are stuck at 0 after a reset).
|
||
*/
|
||
|
||
while ((can_getreg(priv, SAM_CAN_SR_OFFSET) & CAN_INT_WAKEUP) == 0);
|
||
return OK;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Public Functions
|
||
****************************************************************************/
|
||
|
||
/****************************************************************************
|
||
* Name: sam_caninitialize
|
||
*
|
||
* Description:
|
||
* Initialize the selected CAN port
|
||
*
|
||
* Input Parameter:
|
||
* Port number (for hardware that has mutiple CAN interfaces)
|
||
*
|
||
* Returned Value:
|
||
* Valid CAN device structure reference on succcess; a NULL on failure
|
||
*
|
||
****************************************************************************/
|
||
|
||
FAR struct can_dev_s *sam_caninitialize(int port)
|
||
{
|
||
FAR struct can_dev_s *dev;
|
||
FAR struct sam_can_s *priv;
|
||
FAR const struct sam_config_s *config;
|
||
|
||
canvdbg("CAN%d\n", port);
|
||
|
||
/* NOTE: Peripherical clocking for CAN0 and/or CAN1 was already provided
|
||
* by sam_clockconfig() early in the reset sequence.
|
||
*/
|
||
|
||
#ifdef CONFIG_SAMA5_CAN0
|
||
if (port == 0)
|
||
{
|
||
/* Select the CAN0 device structure */
|
||
|
||
dev = &g_can0dev;
|
||
priv = &g_can0priv;
|
||
config = &g_can0const;
|
||
}
|
||
else
|
||
#endif
|
||
#ifdef CONFIG_SAMA5_CAN1
|
||
if (port == 1)
|
||
{
|
||
/* Select the CAN1 device structure */
|
||
|
||
dev = &g_can1dev;
|
||
priv = &g_can1priv;
|
||
config = &g_can1const;
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
candbg("ERROR: Unsupported port %d\n", port);
|
||
return NULL;
|
||
}
|
||
|
||
/* Is this the first time that we have handed out this device? */
|
||
|
||
if (!priv->initialized)
|
||
{
|
||
/* Yes, then perform one time data initialization */
|
||
|
||
memset(priv, 0, sizeof(struct sam_can_s));
|
||
priv->config = config;
|
||
priv->freemb = CAN_ALL_MAILBOXES;
|
||
priv->initialized = true;
|
||
|
||
sem_init(&priv->exclsem, 0, 1);
|
||
|
||
dev->cd_ops = &g_canops;
|
||
dev->cd_priv = (FAR void *)priv;
|
||
|
||
/* And put the hardware in the intial state */
|
||
|
||
can_reset(dev);
|
||
}
|
||
|
||
return dev;
|
||
}
|
||
|
||
#endif /* CONFIG_CAN && (CONFIG_SAMA5_CAN0 || CONFIG_SAMA5_CAN1) */
|