blob: 6620fe8d6c94d2b25220e98966583afef27bc4e5 [file] [log] [blame]
/****************************************************************************
* arch/arm/src/am335x/am335x_can.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <assert.h>
#include <arch/board/board.h>
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <nuttx/can/can.h>
#include "arm_internal.h"
#include "hardware/am335x_pinmux.h"
#include "hardware/am335x_prcm.h"
#include "hardware/am335x_dcan.h"
#include "am335x_can.h"
#include "am335x_gpio.h"
#include "am335x_pinmux.h"
#include "am335x_sysclk.h"
#if defined(CONFIG_AM335X_CAN0) || defined(CONFIG_AM335X_CAN1)
#define CAN_MSG_OBJECTS_NUM 0x40
#define CAN_RX_OBJ_NUM 0x20
#define CAN_TX_OBJ_NUM 0x20
#define CAN_RX_OBJ_FIRST 0x01
#define CAN_RX_OBJ_LAST (CAN_RX_OBJ_FIRST + CAN_RX_OBJ_NUM - 0x01)
#define CAN_TX_OBJ_FIRST (CAN_RX_OBJ_LAST + 0x01)
#define CAN_TX_OBJ_LAST (CAN_TX_OBJ_FIRST + CAN_TX_OBJ_NUM - 0x01)
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
#ifdef CONFIG_AM335X_CAN0
/* A CAN bit rate must be provided */
# ifndef CONFIG_AM335X_CAN0_BAUD
# define CONFIG_AM335X_CAN0_BAUD 1000000
# endif
#endif
#ifdef CONFIG_AM335X_CAN1
/* A CAN bit rate must be provided */
# ifndef CONFIG_AM335X_CAN1_BAUD
# define CONFIG_AM335X_CAN1_BAUD 1000000
# endif
#endif
/* User-defined TSEG1 and TSEG2 settings may be used.
*
* CONFIG_AM335X_CAN_TSEG1 = the number of CAN time quanta in segment 1
* CONFIG_AM335X_CAN_TSEG2 = the number of CAN time quanta in segment 2
* CAN_BIT_QUANTA = The number of CAN time quanta in on bit time
*/
#ifndef CONFIG_AM335X_CAN_TSEG1
# define CONFIG_AM335X_CAN_TSEG1 6
#endif
#ifndef CONFIG_AM335X_CAN_TSEG2
# define CONFIG_AM335X_CAN_TSEG2 1
#endif
#define CAN_BIT_QUANTA (CONFIG_AM335X_CAN_TSEG1 + CONFIG_AM335X_CAN_TSEG2 + 1)
/* Timing *******************************************************************/
/* CAN clock source */
#define CAN_CLOCK_FREQUENCY (am335x_get_sysclk())
/****************************************************************************
* Private Types
****************************************************************************/
struct up_dev_s
{
uint8_t port; /* CAN port number */
uint32_t baud; /* Configured baud */
uint32_t base; /* CAN register base address */
uint8_t irq; /* IRQ associated with this CAN */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* CAN Register access */
#ifdef CONFIG_AM335X_CAN_REGDEBUG
static void can_printreg(uint32_t addr, uint32_t value);
#endif
static uint32_t can_getreg(struct up_dev_s *priv, int offset);
static void can_putreg(struct up_dev_s *priv, int offset, uint32_t value);
/* CAN methods */
static void can_reset(struct can_dev_s *dev);
static int can_setup(struct can_dev_s *dev);
static void can_shutdown(struct can_dev_s *dev);
static void can_rxint(struct can_dev_s *dev, bool enable);
static void can_txint(struct can_dev_s *dev, bool enable);
static int can_ioctl(struct can_dev_s *dev, int cmd, unsigned long arg);
static int can_remoterequest(struct can_dev_s *dev, uint16_t id);
static int can_send(struct can_dev_s *dev, struct can_msg_s *msg);
static bool candev_txready(struct can_dev_s *dev);
static bool candev_txempty(struct can_dev_s *dev);
/* CAN interrupts */
static int can_interrupt(int irq, void *context, void *arg);
/* Message Processing */
static void can_savemsg(struct up_dev_s *priv, struct can_hdr_s *hdr,
uint32_t *data);
static void can_readobj(struct up_dev_s *priv, uint32_t index);
static void can_invalobj(struct up_dev_s *priv, uint32_t index);
static void can_setuprxobj(struct up_dev_s *priv);
/* Initialization */
static int can_bittiming(struct up_dev_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 = candev_txready,
.co_txempty = candev_txempty,
};
#ifdef CONFIG_AM335X_CAN0
static struct up_dev_s g_can0priv =
{
.port = 0,
.baud = CONFIG_AM335X_CAN0_BAUD,
.base = AM335X_DCAN0_VADDR,
.irq = AM335X_IRQ_DCAN0_LINE0,
};
static struct can_dev_s g_can0dev =
{
.cd_ops = &g_canops,
.cd_priv = &g_can0priv,
};
#endif
#ifdef CONFIG_AM335X_CAN1
static struct up_dev_s g_can1priv =
{
.port = 1,
.baud = CONFIG_AM335X_CAN1_BAUD,
.base = AM335X_DCAN1_VADDR,
.irq = AM335X_IRQ_DCAN1_LINE0,
};
static struct can_dev_s g_can1dev =
{
.cd_ops = &g_canops,
.cd_priv = &g_can1priv,
};
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: can_printreg
*
* Description:
* Print the value read from a register.
*
* Input Parameters:
* addr - The register address
* value - The register value
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_AM335X_CAN_REGDEBUG
static void can_printreg(uint32_t addr, uint32_t value)
{
static uint32_t prevaddr = 0;
static uint32_t preval = 0;
static uint32_t count = 0;
/* Is this the same value that we read from the same register last time?
* Are we polling the register? If so, suppress some of the output.
*/
if (addr == prevaddr && value == preval)
{
if (count == 0xffffffff || ++count > 3)
{
if (count == 4)
{
caninfo("...\n");
}
return;
}
}
/* No this is a new address or value */
else
{
/* Did we print "..." for the previous value? */
if (count > 3)
{
/* Yes.. then show how many times the value repeated */
caninfo("[repeats %d more times]\n", count - 3);
}
/* Save the new address, value, and count */
prevaddr = addr;
preval = value;
count = 1;
}
/* Show the register value read */
caninfo("%08x->%08x\n", addr, value);
}
#endif /* CONFIG_AM335X_CAN_REGDEBUG */
/****************************************************************************
* Name: can_getreg
*
* Description:
* Read the value of an CAN1/2 register.
*
* Input Parameters:
* priv - A reference to the CAN block status
* offset - The offset to the register to read
*
* Returned Value:
*
****************************************************************************/
#ifdef CONFIG_AM335X_CAN_REGDEBUG
static uint32_t can_getreg(struct up_dev_s *priv, int offset)
{
uint32_t addr;
uint32_t value;
/* Read the value from the register */
addr = priv->base + offset;
value = getreg32(addr);
can_printreg(addr, value);
return value;
}
#else
static uint32_t can_getreg(struct up_dev_s *priv, int offset)
{
return getreg32(priv->base + offset);
}
#endif /* CONFIG_AM335X_CAN_REGDEBUG */
/****************************************************************************
* Name: can_putreg
*
* Description:
* Set the value of an CAN1/2 register.
*
* Input Parameters:
* priv - A reference to the CAN block status
* offset - The offset to the register to write
* value - The value to write to the register
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_AM335X_CAN_REGDEBUG
static void can_putreg(struct up_dev_s *priv, int offset, uint32_t value)
{
uint32_t addr = priv->base + offset;
/* Show the register value being written */
caninfo("%08x<-%08x\n", addr, value);
/* Write the value */
putreg32(value, addr);
}
#else
static void can_putreg(struct up_dev_s *priv, int offset, uint32_t value)
{
putreg32(value, priv->base + offset);
}
#endif /* CONFIG_AM335X_CAN_REGDEBUG */
/****************************************************************************
* 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(struct can_dev_s *dev)
{
struct up_dev_s *priv = (struct up_dev_s *)dev->cd_priv;
caninfo("CAN%d reset\n", priv->port);
can_putreg(priv, AM335X_DCAN_CTL_OFFSET, DCAN_CTL_INIT | DCAN_CTL_SWR);
while (can_getreg(priv, AM335X_DCAN_CTL_OFFSET) & DCAN_CTL_SWR)
{
}
}
/****************************************************************************
* 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(struct can_dev_s *dev)
{
struct up_dev_s *priv = (struct up_dev_s *)dev->cd_priv;
uint32_t regval;
uint8_t i;
int ret = ERROR;
caninfo("CAN%d setup\n", priv->port);
can_bittiming(priv);
/* Invalidate all message objects */
for (i = 1; i <= CAN_MSG_OBJECTS_NUM; ++i)
{
can_invalobj(priv, i);
}
/* Initialization finished, normal operation now. */
regval = can_getreg(priv, AM335X_DCAN_CTL_OFFSET);
regval &= ~DCAN_CTL_INIT;
can_putreg(priv, AM335X_DCAN_CTL_OFFSET, regval);
while (can_getreg(priv, AM335X_DCAN_CTL_OFFSET) & DCAN_CTL_INIT)
{
}
ret = irq_attach(priv->irq, can_interrupt, (void *)dev);
if (ret == OK)
{
up_enable_irq(priv->irq);
/* Enable CAN interrupts within CAN module */
regval = can_getreg(priv, AM335X_DCAN_CTL_OFFSET);
regval |= (DCAN_CTL_IE0 | DCAN_CTL_SIE | DCAN_CTL_EIE);
can_putreg(priv, AM335X_DCAN_CTL_OFFSET, regval);
}
return ret;
}
/****************************************************************************
* 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(struct can_dev_s *dev)
{
struct up_dev_s *priv = (struct up_dev_s *)dev->cd_priv;
uint32_t regval;
caninfo("CAN%d\n", priv->port);
/* Stop operation mode */
regval = can_getreg(priv, AM335X_DCAN_CTL_OFFSET);
regval |= DCAN_CTL_INIT;
can_putreg(priv, AM335X_DCAN_CTL_OFFSET, regval);
/* Disable CAN interrupts */
regval = can_getreg(priv, AM335X_DCAN_CTL_OFFSET);
regval &= ~(DCAN_CTL_IE0 | DCAN_CTL_IE1 | DCAN_CTL_SIE | DCAN_CTL_EIE);
can_putreg(priv, AM335X_DCAN_CTL_OFFSET, regval);
up_disable_irq(priv->irq);
/* Then detach the CAN interrupt handler. */
irq_detach(priv->irq);
}
/****************************************************************************
* Name: can_rxint
*
* Description:
* Call to enable or disable RX interrupts.
* This function is called two times: from can_open and can_close.
* Therefore this function enables and disables not only RX interrupts
* but all message objects.
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* None
*
****************************************************************************/
static void can_rxint(struct can_dev_s *dev, bool enable)
{
struct up_dev_s *priv = (struct up_dev_s *)dev->cd_priv;
if (enable == true)
{
can_setuprxobj(priv);
}
else
{
uint8_t i = 0;
for (i = CAN_RX_OBJ_FIRST; i <= CAN_RX_OBJ_LAST; ++i)
{
can_invalobj(priv, i);
}
}
}
/****************************************************************************
* 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(struct can_dev_s *dev, bool enable)
{
/* The TX interrupt is automatically enabled in can_send within a
* message object.
*/
}
/****************************************************************************
* 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(struct can_dev_s *dev, int cmd, unsigned long arg)
{
caninfo("Fix me:Not Implemented\n");
return 0;
}
/****************************************************************************
* 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(struct can_dev_s *dev, uint16_t id)
{
caninfo("Fix me:Not Implemented\n");
return 0;
}
/****************************************************************************
* 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 Transmission Request (RTR)
* Bits 0-3: Data Length Code (DLC)
* Bytes 2-10: CAN data
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* Zero on success; a negated errno on failure
*
****************************************************************************/
static int can_send(struct can_dev_s *dev, struct can_msg_s *msg)
{
struct up_dev_s *priv = (struct up_dev_s *)dev->cd_priv;
uint32_t regval;
uint32_t num;
uint32_t id;
uint32_t dlc;
uint8_t txobj;
if (msg == NULL)
{
return ERROR;
}
regval = can_getreg(priv, AM335X_DCAN_TXRQ34_OFFSET);
for (num = 0; num < 32; num++)
{
if ((regval & (1 << num)) == 0)
{
break;
}
}
if (num == 32)
{
return ERROR;
}
txobj = CAN_TX_OBJ_LAST - num + CAN_TX_OBJ_FIRST;
DEBUGASSERT((txobj >= CAN_TX_OBJ_FIRST) && (txobj <= CAN_TX_OBJ_LAST));
id = (uint32_t) msg->cm_hdr.ch_id;
dlc = (uint32_t) msg->cm_hdr.ch_dlc;
caninfo("CAN%d ID: %d DLC: %d\n",
priv->port, msg->cm_hdr.ch_id, msg->cm_hdr.ch_dlc);
can_putreg(priv, AM335X_DCAN_IF1MSK_OFFSET, 0xffff);
regval = ((dlc & DCAN_IFMCTL_DLC_MASK) |
DCAN_IFMCTL_EOB | DCAN_IFMCTL_TX_RQST |
DCAN_IFMCTL_TX_IE);
can_putreg(priv, AM335X_DCAN_IF1MCTL_OFFSET, regval);
/* Write data to IF1 data registers */
regval = msg->cm_data[0] + (msg->cm_data[1] << 8) +
(msg->cm_data[2] << 16) + (msg->cm_data[3] << 24);
can_putreg(priv, AM335X_DCAN_IF1DATA_OFFSET, regval);
regval = msg->cm_data[4] + (msg->cm_data[5] << 8) +
(msg->cm_data[6] << 16) + (msg->cm_data[7] << 24);
can_putreg(priv, AM335X_DCAN_IF1DATB_OFFSET, regval);
#ifdef CONFIG_CAN_EXTID
can_putreg(priv,
AM335X_DCAN_IF1ARB_OFFSET,
DCAN_IFARB_DIR | DCAN_IFARB_MSG_VAL |
DCAN_IFARB_XTD | (id << DCAN_IFARB_ID_SHIFT));
#else
can_putreg(priv,
AM335X_DCAN_IF1ARB_OFFSET,
DCAN_IFARB_DIR | DCAN_IFARB_MSG_VAL |
(id << DCAN_IFARB_ID_SHIFT));
#endif
/* Write to Message RAM */
regval = (DCAN_IFCMD_WR_RD | DCAN_IFCMD_MASK |
DCAN_IFCMD_ARB | DCAN_IFCMD_CTL |
DCAN_IFCMD_CLR_INTPND | DCAN_IFCMD_TX_RQST_NEWDAT |
DCAN_IFCMD_DATAA | DCAN_IFCMD_DATAB |
DCAN_IFCMD_MSG_NUM(txobj));
can_putreg(priv, AM335X_DCAN_IF1CMD_OFFSET, regval);
#ifdef CONFIG_CAN_TXREADY
can_txdone(dev);
#endif
return OK;
}
/****************************************************************************
* Name: candev_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 candev_txready(struct can_dev_s *dev)
{
struct up_dev_s *priv = (struct up_dev_s *)dev->cd_priv;
if (can_getreg(priv, AM335X_DCAN_TXRQ34_OFFSET) != 0xffffffff)
{
return false;
}
if (can_getreg(priv, AM335X_DCAN_IF1CMD_OFFSET) & DCAN_IFCMD_BUSY)
{
return false;
}
return true;
}
/****************************************************************************
* Name: candev_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 candev_txempty(struct can_dev_s *dev)
{
struct up_dev_s *priv = (struct up_dev_s *)dev->cd_priv;
return (!((can_getreg(priv, AM335X_DCAN_TXRQ_X_OFFSET)) & 0xffff));
}
/****************************************************************************
* Name: can_interrupt
*
* Description:
* CAN interrupt handler. There is a single interrupt for both CAN0 and
* CAN1.
*
* Input Parameters:
* irq - The IRQ number of the interrupt.
* context - The register state save array at the time of the interrupt.
* arg - pointer to up_dev_s
*
* Returned Value:
* Zero on success; a negated errno on failure
*
****************************************************************************/
static int can_interrupt(int irq, void *context, void *arg)
{
struct can_dev_s *dev = (struct can_dev_s *)arg;
struct up_dev_s *priv;
uint32_t regval = 0;
DEBUGASSERT(dev != NULL && dev->cd_priv != NULL);
priv = (struct up_dev_s *)dev->cd_priv;
uint32_t msgindex = 0;
/* Read CAN interrupt register */
uint32_t interrupt = can_getreg(priv, AM335X_DCAN_INT_OFFSET) &
DCAN_INT_LINE0_MASK;
/* Read CAN status register */
uint32_t stat = can_getreg(priv, AM335X_DCAN_ES_OFFSET);
if (interrupt & DCAN_INT_LINE0_STATUS)
{
/* Clear all warning/error states except RXOK/TXOK */
regval = can_getreg(priv, AM335X_DCAN_ES_OFFSET);
regval &= DCAN_ES_RX_OK | DCAN_ES_TX_OK;
can_putreg(priv, AM335X_DCAN_ES_OFFSET, regval);
}
else
{
msgindex = interrupt & 0x7fff;
/* if no error detected */
if (((stat & DCAN_ES_LEC_MASK) == 0) ||
((stat & DCAN_ES_LEC_MASK) == DCAN_ES_LEC_MASK))
{
if (msgindex <= CAN_RX_OBJ_LAST)
{
struct can_hdr_s hdr;
uint32_t data[2];
regval = can_getreg(priv, AM335X_DCAN_ES_OFFSET);
regval &= ~DCAN_ES_RX_OK;
can_putreg(priv, AM335X_DCAN_ES_OFFSET, regval);
can_readobj(priv, msgindex);
can_savemsg(priv, &hdr, data);
can_invalobj(priv, msgindex);
can_receive(dev, &hdr, (uint8_t *)data);
}
else
{
regval = can_getreg(priv, AM335X_DCAN_ES_OFFSET);
regval &= ~DCAN_ES_TX_OK;
can_putreg(priv, AM335X_DCAN_ES_OFFSET, regval);
can_invalobj(priv, msgindex);
#ifdef CONFIG_CAN_TXREADY
can_txready(dev);
#else
can_txdone(dev);
#endif
}
}
else
{
can_invalobj(priv, msgindex);
}
can_putreg(priv, AM335X_DCAN_ES_OFFSET, 0);
if (msgindex == CAN_RX_OBJ_LAST)
{
can_setuprxobj(priv);
}
}
return OK;
}
/****************************************************************************
* Name: can_savemsg
*
* Description:
* Save received message.
*
* Input Parameters:
* priv - A reference to the CAN block status
* hdr - A reference to the message header
* data - A reference to the data block
*
* Returned Value:
* None
*
****************************************************************************/
static void can_savemsg(struct up_dev_s *priv, struct can_hdr_s *hdr,
uint32_t *data)
{
uint32_t regval;
regval = can_getreg(priv, AM335X_DCAN_IF2ARB_OFFSET);
hdr->ch_id = (regval >> DCAN_IFARB_ID_SHIFT) & DCAN_IFARB_ID_MASK;
hdr->ch_rtr = ((regval & DCAN_IFARB_DIR) != 0);
#ifdef CONFIG_CAN_EXTID
hdr->ch_extid = ((regval & DCAN_IFARB_XTD) != 0);
#endif
regval = can_getreg(priv, AM335X_DCAN_IF2MCTL_OFFSET);
hdr->ch_dlc = (regval >> DCAN_IFMCTL_DLC_SHIFT) & DCAN_IFMCTL_DLC_MASK;
data[0] = can_getreg(priv, AM335X_DCAN_IF2DATA_OFFSET);
data[1] = can_getreg(priv, AM335X_DCAN_IF2DATB_OFFSET);
}
/****************************************************************************
* Name: can_readobj
*
* Description:
* Transfer Message Object into IF registers.
*
* Input Parameters:
* priv - A reference to the CAN block status
* index - Message Object number
*
* Returned Value:
* None
*
****************************************************************************/
static void can_readobj(struct up_dev_s *priv, uint32_t index)
{
while (can_getreg(priv, AM335X_DCAN_IF2CMD_OFFSET) & DCAN_IFCMD_BUSY);
can_putreg(priv, AM335X_DCAN_IF2CMD_OFFSET,
DCAN_IFCMD_MASK | DCAN_IFCMD_ARB |
DCAN_IFCMD_CTL | DCAN_IFCMD_CLR_INTPND | DCAN_IFCMD_DATAA |
DCAN_IFCMD_DATAB | DCAN_IFCMD_MSG_NUM(index));
}
/****************************************************************************
* Name: can_invalobj
*
* Description:
* Invalidate Message Object.
*
* Input Parameters:
* priv - A reference to the CAN block status
* index - Message Object number
*
* Returned Value:
* None
*
****************************************************************************/
static void can_invalobj(struct up_dev_s *priv, uint32_t index)
{
while (can_getreg(priv, AM335X_DCAN_IF2CMD_OFFSET) & DCAN_IFCMD_BUSY);
can_putreg(priv, AM335X_DCAN_IF2ARB_OFFSET, 0);
/* Disable reception and transmission interrupts, clear transmit request */
can_putreg(priv, AM335X_DCAN_IF2MCTL_OFFSET, DCAN_IFMCTL_EOB);
can_putreg(priv, AM335X_DCAN_IF2CMD_OFFSET, DCAN_IFCMD_WR_RD |
DCAN_IFCMD_CLR_INTPND | DCAN_IFCMD_CTL | DCAN_IFCMD_ARB |
DCAN_IFCMD_MSG_NUM(index));
}
/****************************************************************************
* Name: can_setuprxobj
*
* Description:
* Setup Message Object as buffer for received messages.
*
* Input Parameters:
* priv - A reference to the CAN block status
*
* Returned Value:
* None
*
****************************************************************************/
static void can_setuprxobj(struct up_dev_s *priv)
{
uint8_t i;
while (can_getreg(priv, AM335X_DCAN_IF2CMD_OFFSET) & DCAN_IFCMD_BUSY);
can_putreg(priv, AM335X_DCAN_IF2MSK_OFFSET,
DCAN_IFMSK_MXTD | DCAN_IFMSK_MDIR);
can_putreg(priv, AM335X_DCAN_IF2MCTL_OFFSET, DCAN_IFMCTL_DLC_MASK |
DCAN_IFMCTL_EOB | DCAN_IFMCTL_RX_IE | DCAN_IFMCTL_UMASK);
#ifdef CONFIG_CAN_EXTID
can_putreg(priv, AM335X_DCAN_IF2ARB_OFFSET,
DCAN_IFARB_MSG_VAL | DCAN_IFARB_XTD);
#else
can_putreg(priv, AM335X_DCAN_IF2ARB_OFFSET, DCAN_IFARB_MSG_VAL);
#endif
for (i = CAN_RX_OBJ_FIRST; i <= CAN_RX_OBJ_LAST; ++i)
{
while (can_getreg(priv, AM335X_DCAN_IF2CMD_OFFSET) & DCAN_IFCMD_BUSY);
can_putreg(priv, AM335X_DCAN_IF2CMD_OFFSET,
DCAN_IFCMD_WR_RD | DCAN_IFCMD_MASK |
DCAN_IFCMD_ARB | DCAN_IFCMD_CTL |
DCAN_IFCMD_CLR_INTPND |
DCAN_IFCMD_DATAA | DCAN_IFCMD_DATAB |
DCAN_IFCMD_MSG_NUM(i));
}
}
/****************************************************************************
* Name: can_bittiming
*
* Description:
* Set the CAN bit timing register (BTR) based on the configured BAUD.
*
* The bit timing logic monitors the serial bus-line and performs sampling
* and adjustment of the sample point by synchronizing on the start-bit edge
* and resynchronizing on the following edges.
*
* Its operation may be explained simply by splitting nominal bit time into
* three segments as follows:
*
* 1. Synchronization segment (SYNC_SEG): a bit change is expected to occur
* within this time segment. It has a fixed length of one time quantum
* (1 x tCAN).
* 2. Bit segment 1 (BS1): defines the location of the sample point. It
* includes the PROP_SEG and PHASE_SEG1 of the CAN standard. Its duration
* is programmable between 1 and 16 time quanta but may be automatically
* lengthened to compensate for positive phase drifts due to differences
* in the frequency of the various nodes of the network.
* 3. Bit segment 2 (BS2): defines the location of the transmit point. It
* represents the PHASE_SEG2 of the CAN standard. Its duration is
* programmable between 1 and 8 time quanta but may also be automatically
* shortened to compensate for negative phase drifts.
*
* Pictorially:
*
* |<----------------- NOMINAL BIT TIME ----------------->|
* |<- SYNC_SEG ->|<------ BS1 ------>|<------ BS2 ------>|
* |<---- Tq ---->|<----- Tbs1 ------>|<----- Tbs2 ------>|
*
* Where
* Tbs1 is the duration of the BS1 segment
* Tbs2 is the duration of the BS2 segment
* Tq is the "Time Quantum"
*
* Relationships:
*
* baud = 1 / bit_time
* bit_time = Tq + Tbs1 + Tbs2
* Tbs1 = Tq * ts1
* Tbs2 = Tq * ts2
* Tq = brp * Tcan
*
* Where:
* Tcan is the period of the APB clock.
*
* Input Parameters:
* priv - A reference to the CAN block status
*
* Returned Value:
* Zero on success; a negated errno on failure
*
****************************************************************************/
static int can_bittiming(struct up_dev_s *priv)
{
uint32_t ts1 = CONFIG_AM335X_CAN_TSEG1;
uint32_t ts2 = CONFIG_AM335X_CAN_TSEG2;
uint32_t sjw = 1;
uint32_t brp = CAN_CLOCK_FREQUENCY / (priv->baud * CAN_BIT_QUANTA);
uint32_t regval;
syslog(LOG_DEBUG, "CAN%d PCLK: %d baud: %d\n",
priv->port, CAN_CLOCK_FREQUENCY, priv->baud);
syslog(LOG_DEBUG, "TS1: %d TS2: %d BRP: %d SJW= %d\n", ts1, ts2, brp, sjw);
/* Start configuring bit timing */
regval = can_getreg(priv, AM335X_DCAN_CTL_OFFSET);
regval |= DCAN_CTL_CCE;
can_putreg(priv, AM335X_DCAN_CTL_OFFSET, regval);
regval = (((brp - 1) << DCAN_BTR_BRP_SHIFT)
| ((ts1 - 1) << DCAN_BTR_TSEG1_SHIFT)
| ((ts2 - 1) << DCAN_BTR_TSEG2_SHIFT)
| ((sjw - 1) << DCAN_BTR_SJW_SHIFT));
syslog(LOG_DEBUG, "Setting CANxBTR= 0x%08x\n", regval);
/* Set bit timing */
can_putreg(priv, AM335X_DCAN_BTR_OFFSET, regval);
/* Stop configuring bit timing */
regval = can_getreg(priv, AM335X_DCAN_CTL_OFFSET);
regval &= ~DCAN_CTL_CCE;
can_putreg(priv, AM335X_DCAN_CTL_OFFSET, regval);
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: am335x_can_initialize
*
* Description:
* Initialize the selected CAN port
*
* Input Parameters:
* Port number (for hardware that has multiple CAN interfaces)
*
* Returned Value:
* Valid CAN device structure reference on success; a NULL on failure
*
****************************************************************************/
struct can_dev_s *am335x_can_initialize(int port)
{
struct can_dev_s *candev;
irqstate_t flags;
syslog(LOG_DEBUG, "CAN%d\n", port);
flags = enter_critical_section();
#ifdef CONFIG_AM335X_CAN0
if (port == 0)
{
/* Configure CAN IO pins */
am335x_gpio_config(GPIO_DCAN0_RX);
am335x_gpio_config(GPIO_DCAN0_TX);
candev = &g_can0dev;
}
else
#endif /* CONFIG_AM335X_CAN0 */
#ifdef CONFIG_AM335X_CAN1
if (port == 1)
{
/* Configure CAN IO pins */
am335x_gpio_config(GPIO_DCAN1_RX);
am335x_gpio_config(GPIO_DCAN1_TX);
candev = &g_can1dev;
}
else
#endif /* CONFIG_AM335X_CAN1 */
{
canerr("Unsupported port: %d\n", port);
leave_critical_section(flags);
return NULL;
}
leave_critical_section(flags);
return candev;
}
void am335x_can_uninitialize(struct can_dev_s *dev)
{
irqstate_t flags;
DEBUGASSERT(dev);
flags = enter_critical_section();
#ifdef CONFIG_AM335X_CAN0
if (dev == &g_can0dev)
{
/* Configure CAN IO pins to GPIO */
am335x_gpio_config(am335x_periph_gpio(GPIO_DCAN0_RX));
am335x_gpio_config(am335x_periph_gpio(GPIO_DCAN0_TX));
}
else
#endif /* CONFIG_AM335X_CAN0 */
#ifdef CONFIG_AM335X_CAN1
if (dev == &g_can1dev)
{
/* Configure CAN IO pins to GPIO */
am335x_gpio_config(am335x_periph_gpio(GPIO_DCAN1_RX));
am335x_gpio_config(am335x_periph_gpio(GPIO_DCAN1_TX));
}
else
#endif /* CONFIG_AM335X_CAN1 */
{
canerr("Not a CAN device: %p\n", dev);
}
leave_critical_section(flags);
}
#endif