nuttx/arch/arm/src/sama5/sam_can.c

2021 lines
57 KiB
C
Raw Blame History

/****************************************************************************
* 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) */