summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-10-22 15:47:52 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-10-22 15:47:52 -0600
commit64aa8aa31a0c9c425996e1f6a1d6c4137ff5cb23 (patch)
tree679f85db3541f2c780824ade3506deb65795270c
parent7be7e670323816f671e6bed7ec282251754602b3 (diff)
downloadnuttx-64aa8aa31a0c9c425996e1f6a1d6c4137ff5cb23.tar.gz
nuttx-64aa8aa31a0c9c425996e1f6a1d6c4137ff5cb23.tar.bz2
nuttx-64aa8aa31a0c9c425996e1f6a1d6c4137ff5cb23.zip
SAMA5 CAN: Driver is now code complete but still untested
-rw-r--r--nuttx/ChangeLog5
-rw-r--r--nuttx/arch/arm/src/lpc17xx/lpc17_can.c16
-rw-r--r--nuttx/arch/arm/src/sama5/Kconfig222
-rw-r--r--nuttx/arch/arm/src/sama5/Make.defs8
-rw-r--r--nuttx/arch/arm/src/sama5/chip/sam_can.h38
-rw-r--r--nuttx/arch/arm/src/sama5/sam_can.c1196
-rw-r--r--nuttx/arch/arm/src/sama5/sam_can.h42
-rw-r--r--nuttx/drivers/Kconfig6
-rw-r--r--nuttx/include/nuttx/can.h6
9 files changed, 1228 insertions, 311 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog
index c1de17d56..deba6a4ed 100644
--- a/nuttx/ChangeLog
+++ b/nuttx/ChangeLog
@@ -5841,6 +5841,7 @@
warnings from GCC (2013-10-21).
* arch/arm/src/sama5/chip/sam_can.h: SAMA5D3X CAN register definition
header file (2013-10-21)
- * arch/arm/src/sama5/sam_can.c and .h: Framework for a SAMA5 CAN drvier.
+ * arch/arm/src/sama5/sam_can.c and .h: Framework for a SAMA5 CAN driver.
Initial checkin is the STM32 CAN driver with name changes (2013-10-21).
-
+ * arch/arm/src/sama5/sam_can.c and .h: SAMA5 CAN driver is code complete
+ but still untested (2013-10-22).
diff --git a/nuttx/arch/arm/src/lpc17xx/lpc17_can.c b/nuttx/arch/arm/src/lpc17xx/lpc17_can.c
index abdf8c7b7..8334e01ab 100644
--- a/nuttx/arch/arm/src/lpc17xx/lpc17_can.c
+++ b/nuttx/arch/arm/src/lpc17xx/lpc17_can.c
@@ -6,11 +6,11 @@
* Authors:
* Li Zhuoyi <lzyy.cn@gmail.com>
* Gregory Nutt <gnutt@nuttx.org>
- * History:
+ * History:
* 2011-07-12: Initial version (Li Zhuoyi)
* 2011-08-03: Support CAN1/CAN2 (Li Zhuoyi)
* 2012-01-02: Add support for CAN loopback mode (Gregory Nutt)
- *
+ *
* This file is a part of NuttX:
*
* Copyright (C) 2010 Gregory Nutt. All rights reserved.
@@ -675,7 +675,7 @@ static void can_txint(FAR struct can_dev_s *dev, bool enable)
can_putreg(priv, LPC17_CAN_IER_OFFSET, regval);
irqrestore(flags);
}
-
+
}
/****************************************************************************
@@ -1167,13 +1167,13 @@ static int can_bittiming(struct up_dev_s *priv)
if (ts1 == ts2 && ts1 > 1 && ts2 < CAN_BTR_TSEG2_MAX)
{
ts1--;
- ts2++;
+ ts2++;
}
}
/* Otherwise, nquanta is CAN_BIT_QUANTA, ts1 is CONFIG_CAN_TSEG1, ts2 is
* CONFIG_CAN_TSEG2 and we calculate brp to achieve CAN_BIT_QUANTA quanta
- * in the bit time
+ * in the bit time
*/
else
@@ -1183,7 +1183,7 @@ static int can_bittiming(struct up_dev_s *priv)
brp = (nclks + (CAN_BIT_QUANTA/2)) / CAN_BIT_QUANTA;
DEBUGASSERT(brp >=1 && brp <= CAN_BTR_BRP_MAX);
}
-
+
sjw = 1;
canllvdbg("TS1: %d TS2: %d BRP: %d SJW= %d\n", ts1, ts2, brp, sjw);
@@ -1235,7 +1235,7 @@ FAR struct can_dev_s *lpc17_caninitialize(int port)
flags = irqsave();
-#ifdef CONFIG_LPC17_CAN1
+#ifdef CONFIG_LPC17_CAN1
if (port == 1)
{
/* Enable power to the CAN module */
@@ -1262,7 +1262,7 @@ FAR struct can_dev_s *lpc17_caninitialize(int port)
}
else
#endif
-#ifdef CONFIG_LPC17_CAN2
+#ifdef CONFIG_LPC17_CAN2
if (port == 2)
{
/* Enable power to the CAN module */
diff --git a/nuttx/arch/arm/src/sama5/Kconfig b/nuttx/arch/arm/src/sama5/Kconfig
index 2d71c1e85..99d7a8e2b 100644
--- a/nuttx/arch/arm/src/sama5/Kconfig
+++ b/nuttx/arch/arm/src/sama5/Kconfig
@@ -256,10 +256,12 @@ config SAMA5_SSC1
config SAMA5_CAN0
bool "CAN controller 0 (CAN0)"
default n
+ select CAN
config SAMA5_CAN1
bool "CAN controller 1 (CAN1)"
default n
+ select CAN
config SAMA5_SHA
bool "Secure Hash Algorithm (SHA)"
@@ -1081,13 +1083,224 @@ config SAMA5_EMAC_REGDEBUG
Enable very low-level register access debug. Depends on DEBUG.
config SAMA5_EMAC_ISETH0
- bool
- default y if !SAMA5_EMAC || !SAMA5_GMAC_ISETH0
- default n if SAMA5_EMAC && SAMA5_GMAC_ISETH0
+ bool
+ default y if !SAMA5_EMAC || !SAMA5_GMAC_ISETH0
+ default n if SAMA5_EMAC && SAMA5_GMAC_ISETH0
endmenu # EMAC device driver options
endif # SAMA5_EMAC
+if SAMA5_CAN0 || SAMA5_CAN1
+
+menu "CAN device driver options"
+
+if SAMA5_CAN0
+
+config SAMA5_CAN0_BAUD
+ int "CAN0 BAUD"
+ default 250000
+ depends on SAMA5_CAN0
+ ---help---
+ CAN0 BAUD rate. Required if SAMA5_CAN0 is defined.
+
+config SAMA5_CAN0_NRECVMB
+ int "Number of receive mailboxes"
+ default 1
+ range 1 3
+ ---help---
+ The SAMA5 CAN0 peripheral supports 8 mailboxes that can be used for
+ sending and receiving messages. Up the three of these can be set
+ aside statically for message reception. The remainder can be
+ configured dynamically to send CAN messages. Multiple receive
+ mailboxes might needed to either (1) receive bursts of messages, or
+ (2) to support multiple groups of messages filtered on message ID.
+
+ NOTE: The maximum of 3 is a completely arbitrary design decision
+ and can certainly be changed if you need more.
+
+config SAMA5_CAN0_ADDR0
+ hex "Mailbox 0 address"
+ ---help---
+ This setting defines the address for receive mailbox 0. If CAN_EXTID
+ is defined, this should be a 29-bit extended CAN address; otherwise
+ it should be an 11-bit standard CAN address.
+
+config SAMA5_CAN0_MASK0
+ hex "Mailbox 0 address mask"
+ default 0x7fff if !CAN_EXTID
+ default 0x1fffffff if CAN_EXTID
+ ---help---
+ This setting defines the address mask for receive mailbox 0. And
+ address matching SAMA5_CAN0_ADDR0 under this mask are accepted. The
+ default, all ones, forces an exact match. A value of zero will accept
+ any address.
+
+ If CAN_EXTID is defined, this should be a 29-bit extended CAN address
+ mask; otherwise it should be an 11-bit standard CAN address.
+
+config SAMA5_CAN0_ADDR1
+ hex "Mailbox 1 address"
+ ---help---
+ This setting defines the address for receive mailbox 1. If CAN_EXTID
+ is defined, this should be a 29-bit extended CAN address; otherwise
+ it should be an 11-bit standard CAN address.
+
+ This setting is ignored if SAMA5_CAN0_NRECVMB is less than 2.
+
+config SAMA5_CAN0_MASK1
+ hex "Mailbox 1 address mask"
+ default 0x7fff if !CAN_EXTID
+ default 0x1fffffff if CAN_EXTID
+ ---help---
+ This setting defines the address mask for receive mailbox 1. And
+ address matching SAMA5_CAN0_ADDR1 under this mask are accepted. The
+ default, all ones, forces an exact match. A value of zero will accept
+ any address.
+
+ If CAN_EXTID is defined, this should be a 29-bit extended CAN address
+ mask; otherwise it should be an 11-bit standard CAN address.
+
+ This setting is ignored if SAMA5_CAN0_NRECVMB is less than 2.
+
+config SAMA5_CAN0_ADDR2
+ hex "Mailbox 2 address"
+ ---help---
+ This setting defines the address for receive mailbox 2. If CAN_EXTID
+ is defined, this should be a 29-bit extended CAN address; otherwise
+ it should be an 11-bit standard CAN address.
+
+ This setting is ignored if SAMA5_CAN0_NRECVMB is less than 3.
+
+config SAMA5_CAN0_MASK2
+ hex "Mailbox 1 address mask"
+ default 0x7fff if !CAN_EXTID
+ default 0x1fffffff if CAN_EXTID
+ ---help---
+ This setting defines the address mask for receive mailbox 2. And
+ address matching SAMA5_CAN0_ADDR2 under this mask are accepted. The
+ default, all ones, forces an exact match. A value of zero will accept
+ any address.
+
+ If CAN_EXTID is defined, this should be a 29-bit extended CAN address
+ mask; otherwise it should be an 11-bit standard CAN address.
+
+ This setting is ignored if SAMA5_CAN0_NRECVMB is less than 2.
+
+endif # SAMA5_CAN0
+
+if SAMA5_CAN1
+
+config SAMA5_CAN1_BAUD
+ int "CAN1 BAUD"
+ default 250000
+ depends on SAMA5_CAN1
+ ---help---
+ CAN1 BAUD rate. Required if SAMA5_CAN1 is defined.
+
+config SAMA5_CAN1_NRECVMB
+ int "Number of receive mailboxes"
+ default 1
+ range 1 3
+ ---help---
+ The SAMA5 CAN1 peripheral supports 8 mailboxes that can be used for
+ sending and receiving messages. Up the three of these can be set
+ aside statically for message reception. The remainder can be
+ configured dynamically to send CAN messages. Multiple receive
+ mailboxes might needed to either (1) receive bursts of messages, or
+ (2) to support multiple groups of messages filtered on message ID.
+
+ NOTE: The maximum of 3 is a completely arbitrary design decision
+ and can certainly be changed if you need more.
+
+config SAMA5_CAN1_ADDR0
+ hex "Mailbox 0 address"
+ ---help---
+ This setting defines the address for receive mailbox 0. If CAN_EXTID
+ is defined, this should be a 29-bit extended CAN address; otherwise
+ it should be an 11-bit standard CAN address.
+
+config SAMA5_CAN1_MASK0
+ hex "Mailbox 0 address mask"
+ default 0x7fff if !CAN_EXTID
+ default 0x1fffffff if CAN_EXTID
+ ---help---
+ This setting defines the address mask for receive mailbox 0. And
+ address matching SAMA5_CAN1_ADDR0 under this mask are accepted. The
+ default, all ones, forces an exact match. A value of zero will accept
+ any address.
+
+ If CAN_EXTID is defined, this should be a 29-bit extended CAN address
+ mask; otherwise it should be an 11-bit standard CAN address.
+
+config SAMA5_CAN1_ADDR1
+ hex "Mailbox 1 address"
+ ---help---
+ This setting defines the address for receive mailbox 1. If CAN_EXTID
+ is defined, this should be a 29-bit extended CAN address; otherwise
+ it should be an 11-bit standard CAN address.
+
+ This setting is ignored if SAMA5_CAN0_NRECVMB is less than 2.
+
+config SAMA5_CAN1_MASK1
+ hex "Mailbox 1 address mask"
+ default 0x7fff if !CAN_EXTID
+ default 0x1fffffff if CAN_EXTID
+ ---help---
+ This setting defines the address mask for receive mailbox 1. And
+ address matching SAMA5_CAN1_ADDR1 under this mask are accepted. The
+ default, all ones, forces an exact match. A value of zero will accept
+ any address.
+
+ If CAN_EXTID is defined, this should be a 29-bit extended CAN address
+ mask; otherwise it should be an 11-bit standard CAN address.
+
+ This setting is ignored if SAMA5_CAN1_NRECVMB is less than 2.
+
+config SAMA5_CAN1_ADDR2
+ hex "Mailbox 2 address"
+ ---help---
+ This setting defines the address for receive mailbox 2. If CAN_EXTID
+ is defined, this should be a 29-bit extended CAN address; otherwise
+ it should be an 11-bit standard CAN address.
+
+ This setting is ignored if SAMA5_CAN1_NRECVMB is less than 3.
+
+config SAMA5_CAN1_MASK2
+ hex "Mailbox 2 address mask"
+ default 0x7fff if !CAN_EXTID
+ default 0x1fffffff if CAN_EXTID
+ ---help---
+ This setting defines the address mask for receive mailbox 2. And
+ address matching SAMA5_CAN1_ADDR2 under this mask are accepted. The
+ default, all ones, forces an exact match. A value of zero will accept
+ any address.
+
+ If CAN_EXTID is defined, this should be a 29-bit extended CAN address
+ mask; otherwise it should be an 11-bit standard CAN address.
+
+ This setting is ignored if SAMA5_CAN1_NRECVMB is less than 3.
+
+endif # SAMA5_CAN1
+
+config SAMA5_CAN_AUTOBAUD
+ bool "Enable auto-baud"
+ default n
+ depends on EXPERIMENTAL
+ ---help---
+ Enable the SAMA5 auto-baud feature. NOTE: This feature is not yet
+ fully implemented.
+
+config SAMA5_CAN_REGDEBUG
+ bool "CAN Register level debug"
+ depends on DEBUG
+ default n
+ ---help---
+ Output detailed register-level CAN device debug information.
+ Requires also DEBUG.
+
+endmenu # SPI device driver options
+endif # SAMA5_CAN0 || SAMA5_CAN1
+
if SAMA5_SPI0 || SAMA5_SPI1
menu "SPI device driver options"
@@ -1123,7 +1336,8 @@ config SAMA5_SPI_REGDEBUG
depends on DEBUG
default n
---help---
- Output detailed register-level SPI device debug information. Requires also DEBUG.
+ Output detailed register-level SPI device debug information.
+ Requires also DEBUG.
endmenu # SPI device driver options
endif # SAMA5_SPI0 || SAMA5_SPI1
diff --git a/nuttx/arch/arm/src/sama5/Make.defs b/nuttx/arch/arm/src/sama5/Make.defs
index 848cf4fb3..de978e2c3 100644
--- a/nuttx/arch/arm/src/sama5/Make.defs
+++ b/nuttx/arch/arm/src/sama5/Make.defs
@@ -178,6 +178,14 @@ CHIP_CSRCS += sam_gmac.c
endif
endif
+ifeq ($(CONFIG_SAMA5_CAN0),y)
+CHIP_CSRCS += sam_can.c
+else
+ifeq ($(CONFIG_SAMA5_CAN1),y)
+CHIP_CSRCS += sam_can.c
+endif
+endif
+
ifeq ($(CONFIG_SAMA5_TWI0),y)
CHIP_CSRCS += sam_twi.c
else
diff --git a/nuttx/arch/arm/src/sama5/chip/sam_can.h b/nuttx/arch/arm/src/sama5/chip/sam_can.h
index 04e45736b..00632eb43 100644
--- a/nuttx/arch/arm/src/sama5/chip/sam_can.h
+++ b/nuttx/arch/arm/src/sama5/chip/sam_can.h
@@ -77,14 +77,14 @@
#define SAM_CAN_MDH_OFFSET 0x0018 /* Mailbox Data High Register */
#define SAM_CAN_MCR_OFFSET 0x001c /* Mailbox Control Register */
-#define SAM_CAN_MnMR_OFFSET(n) (SAM_CAN_MAILBOX_OFFSET(n)+SAM_CAN_MMR_OFFSET)
-#define SAM_CAN_MnAM_OFFSET(n) (SAM_CAN_MAILBOX_OFFSET(n)+SAM_CAN_MAM_OFFSET)
-#define SAM_CAN_MnID_OFFSET(n) (SAM_CAN_MAILBOX_OFFSET(n)+SAM_CAN_MID_OFFSET)
-#define SAM_CAN_MnFID_OFFSET(n) (SAM_CAN_MAILBOX_OFFSET(n)+SAM_CAN_MFID_OFFSET)
-#define SAM_CAN_MnSR_OFFSET(n) (SAM_CAN_MAILBOX_OFFSET(n)+SAM_CAN_MSR_OFFSET)
-#define SAM_CAN_MnDL_OFFSET(n) (SAM_CAN_MAILBOX_OFFSET(n)+SAM_CAN_MDL_OFFSET)
-#define SAM_CAN_MnDH_OFFSET(n) (SAM_CAN_MAILBOX_OFFSET(n)+SAM_CAN_MDH_OFFSET)
-#define SAM_CAN_MnCR_OFFSET(n) (SAM_CAN_MAILBOX_OFFSET(n)+SAM_CAN_MCR_OFFSET)
+#define SAM_CAN_MnMR_OFFSET(n) (SAM_CAN_MBn_OFFSET(n)+SAM_CAN_MMR_OFFSET)
+#define SAM_CAN_MnAM_OFFSET(n) (SAM_CAN_MBn_OFFSET(n)+SAM_CAN_MAM_OFFSET)
+#define SAM_CAN_MnID_OFFSET(n) (SAM_CAN_MBn_OFFSET(n)+SAM_CAN_MID_OFFSET)
+#define SAM_CAN_MnFID_OFFSET(n) (SAM_CAN_MBn_OFFSET(n)+SAM_CAN_MFID_OFFSET)
+#define SAM_CAN_MnSR_OFFSET(n) (SAM_CAN_MBn_OFFSET(n)+SAM_CAN_MSR_OFFSET)
+#define SAM_CAN_MnDL_OFFSET(n) (SAM_CAN_MBn_OFFSET(n)+SAM_CAN_MDL_OFFSET)
+#define SAM_CAN_MnDH_OFFSET(n) (SAM_CAN_MBn_OFFSET(n)+SAM_CAN_MDH_OFFSET)
+#define SAM_CAN_MnCR_OFFSET(n) (SAM_CAN_MBn_OFFSET(n)+SAM_CAN_MCR_OFFSET)
/* CAN Register Addresses ***********************************************************/
@@ -149,7 +149,7 @@
#define CAN_MR_TEOF (1 << 4) /* Bit 4: Timestamp Messages at each End of Frame */
#define CAN_MR_TTM (1 << 5) /* Bit 5: Disable/Enable Time Triggered Mode */
#define CAN_MR_TIMFRZ (1 << 6) /* Bit 6: Enable Timer Freeze */
-#define CAN_MR_DRPT (1 << 7) /* Bit 7: Disable Repeat */
+#define CAN_MR_DRPT (1 << 7) /* Bit 7: Disable Repeat */
/* Interrupt Enable Register, Interrupt Disable Register, Interrupt Mask Register,
* and Status Register
@@ -171,7 +171,7 @@
#define CAN_INT_ERRP (1 << 18) /* Bit 18: Error Passive Mode */
#define CAN_INT_BOFF (1 << 19) /* Bit 19: Bus Off Mode */
#define CAN_INT_SLEEP (1 << 20) /* Bit 20: CAN Controller in Low-power Mode */
-#define CAN_INT_WAKEUP (1 << 21) /* Bit 21: Wake-up Interrupt Enable */
+#define CAN_INT_WAKEUP (1 << 21) /* Bit 21: Wake-up Interrupt */
#define CAN_INT_TOVF (1 << 22) /* Bit 22: Timer Overflow */
#define CAN_INT_TSTP (1 << 23) /* Bit 23: Timestamp */
#define CAN_INT_CERR (1 << 24) /* Bit 24: Mailbox CRC Error */
@@ -179,6 +179,8 @@
#define CAN_INT_AERR (1 << 26) /* Bit 26: Acknowledgment Error */
#define CAN_INT_FERR (1 << 27) /* Bit 27: Form Error */
#define CAN_INT_BERR (1 << 28) /* Bit 28: Bit Error */
+#define CAN_INT_ALLERRORS (0x1f000000)
+#define CAN_INT_ALL (0x1fff00ff)
#define CAN_SR_RBSY (1 << 29) /* Bit 29: Receiver busy */
#define CAN_SR_TBSY (1 << 30) /* Bit 30: Transmitter busy */
@@ -251,7 +253,7 @@
#define CAN_WPMR_WPKEY_SHIFT (8) /* Bits 8-31: CAN Write Protection Key Password */
#define CAN_WPMR_WPKEY_MASK (0xffffff << CAN_WPMR_WPKEY_SHIFT)
# define CAN_WPMR_WPKEY (0x43414e << CAN_WPMR_WPKEY_SHIFT) /* "CAN" in ASCII */
-
+
/* Write Protect Status Register */
#define CAN_WPSR_WPVS (1 << 0) /* Bit 0: Write Protection Violation Status */
@@ -285,6 +287,13 @@
# define CAN_MAM_MIDvA(n) ((uint32_t)(n) << CAN_MAM_MIDvA_SHIFT)
#define CAN_MAM_MIDE (1 << 29) /* Bit 29: Identifier Version */
+#define CAN_MAM_EXTID_SHIFT (0) /* Bits 0-28: 29-bit extended address */
+#define CAN_MAM_EXTID_MASK (0x1fffffff << CAN_MAM_EXTID_SHIFT)
+# define CAN_MAM_EXTID(n) (((uint32_t)(n) << CAN_MAM_EXTID_SHIFT) | CAN_MAM_MIDE)
+#define CAN_MAM_STDID_SHIFT (18) /* Bits 18-28: 11-bit standard address */
+#define CAN_MAM_STDID_MASK (0x7ff << CAN_MAM_STDID_SHIFT)
+# define CAN_MAM_STDIE(n) ((uint32_t)(n) << CAN_MAM_STDID_SHIFT)
+
/* Mailbox ID Register */
#define CAN_MID_MIDvB_SHIFT (0) /* Bits 0-17: Complementary bits for identifier */
@@ -295,6 +304,13 @@
# define CAN_MID_MIDvA(n) ((uint32_t)(n) << CAN_MID_MIDvA_SHIFT)
#define CAN_MID_MIDE (1 << 29) /* Bit 19: Identifier Version */
+#define CAN_MID_EXTID_SHIFT (0) /* Bits 0-28: 29-bit extended address */
+#define CAN_MID_EXTID_MASK (0x1fffffff << CAN_MID_EXTID_SHIFT)
+# define CAN_MID_EXTID(n) (((uint32_t)(n) << CAN_MID_EXTID_SHIFT) | CAN_MID_MIDE)
+#define CAN_MID_STDID_SHIFT (18) /* Bits 18-28: 11-bit standard address */
+#define CAN_MID_STDID_MASK (0x7ff << CAN_MID_STDIF_SHIFT)
+# define CAN_MID_STDID(n) ((uint32_t)(n) << CAN_MID_STDIF_SHIFT)
+
/* Mailbox Family ID Register */
#define CAN_MFID_MASK (0x1fffffff)
diff --git a/nuttx/arch/arm/src/sama5/sam_can.c b/nuttx/arch/arm/src/sama5/sam_can.c
index dc76e0e96..d23ad4fdc 100644
--- a/nuttx/arch/arm/src/sama5/sam_can.c
+++ b/nuttx/arch/arm/src/sama5/sam_can.c
@@ -53,6 +53,7 @@
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
+#include <string.h>
#include <semaphore.h>
#include <errno.h>
#include <debug.h>
@@ -76,13 +77,28 @@
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
-/* Delays *******************************************************************/
-/* Time out for INAK bit */
+/* Common definitions *******************************************************/
-#define INAK_TIMEOUT 65535
+#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
+
/* Bit timing ***************************************************************/
/* Clocking */
@@ -103,8 +119,6 @@
# error Cannot realize ADC input frequency
#endif
-#define CAN_BIT_QUANTA (CONFIG_CAN_TSEG1 + CONFIG_CAN_TSEG2 + 1)
-
/* Debug ********************************************************************/
/* Non-standard debug that may be enabled just for testing CAN */
@@ -121,22 +135,60 @@
#endif
#if !defined(CONFIG_DEBUG) || !defined(CONFIG_DEBUG_CAN)
-# undef CONFIG_CAN_REGDEBUG
+# 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
{
- uint8_t port; /* CAN port number (1 or 2) */
- uint8_t pid; /* CAN periperal ID/IRQ number */
- 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 */
+ 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 */
+
+#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
};
/****************************************************************************
@@ -145,16 +197,25 @@ struct sam_can_s
/* CAN Register access */
-static uint32_t can_getreg(struct sam_can_s *priv, int offset);
-static void can_putreg(struct sam_can_s *priv, int offset, uint32_t regval);
-#ifdef CONFIG_CAN_REGDEBUG
-static void can_dumpctrlregs(struct sam_can_s *priv, FAR const char *msg);
-static void can_dumpmbregs(struct sam_can_s *priv, FAR const char *msg);
+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);
@@ -170,19 +231,25 @@ static bool can_txempty(FAR struct can_dev_s *dev);
/* CAN interrupt handling */
-static void can_interrupt(struct sam_can_s *priv);
+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
-#if defined(CONFIG_CAN) && (defined() || defined())
-/* Initialization */
+/* Hardware initialization */
-static int can_bittiming(struct sam_can_s *priv);
-static int can_hwinitialize(struct sam_can_s *priv);
+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
@@ -203,41 +270,75 @@ static const struct can_ops_s g_canops =
};
#ifdef CONFIG_SAMA5_CAN0
-static const struct sam_can_s g_can0priv =
+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_CAN0_BAUD,
- .rxpinset = PIO_CAN0_RX;
- .txpinset = PIO_CAN0_TX;
+ .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 can_dev_s g_can0dev =
-{
- .cd_ops = &g_canops,
- .cd_priv = (void *)&g_can0priv,
-};
+static struct sam_can_s g_can0priv;
+static struct can_dev_s g_can0dev;
#endif
#ifdef CONFIG_SAMA5_CAN1
-static const struct sam_can_s g_can1priv =
+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_CAN1_BAUD,
- .rxpinset = PIO_CAN1_RX;
- .txpinset = PIO_CAN1_TX;
+ .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 can_dev_s g_can1dev =
-{
- .cd_ops = &g_canops,
- .cd_priv = (void *)&g_can1priv,
-};
+static struct sam_can_s g_can1priv;
+static struct can_dev_s g_can1dev;
#endif
/****************************************************************************
@@ -251,39 +352,38 @@ static struct can_dev_s g_can1dev =
* Read the value of a CAN register.
*
* Input Parameters:
- * priv - A reference to the CAN block status
+ * priv - A reference to the CAN peripheral state
* offset - The offset to the register to read
*
* Returned Value:
*
****************************************************************************/
-#ifdef CONFIG_CAN_REGDEBUG
-static uint32_t can_getreg(struct sam_can_s *priv, int offset)
+#ifdef CONFIG_SAMA5_CAN_REGDEBUG
+static uint32_t can_getreg(FAR struct sam_can_s *priv, int offset)
{
- static uintptr_t prevaddr = 0;
- static uint32_t preval = 0;
- static unsigned int count = 0;
+ FAR const struct sam_config_s *config = priv->config;
uintptr_t regaddr;
uint32_t regval;
/* Read the value from the register */
- regaddr = priv->base + offset;
+ 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 == prevaddr && regval == preval)
+ if (regaddr == priv->regaddr && regval == priv->regval)
{
- if (count == 0xffffffff || ++count > 3)
+ if (priv->count == 0xffffffff || ++priv->count > 3)
{
- if (count == 4)
+ if (priv->count == 4)
{
lldbg("...\n");
}
+
return regval;
}
}
@@ -294,30 +394,31 @@ static uint32_t can_getreg(struct sam_can_s *priv, int offset)
{
/* Did we print "..." for the previous value? */
- if (count > 3)
+ if (priv->count > 3)
{
/* Yes.. then show how many times the value repeated */
- lldbg("[repeats %d more times]\n", count-3);
+ lldbg("[repeats %d more times]\n", priv->count - 3);
}
/* Save the new address, value, and count */
- prevaddr = regaddr;
- preval = regval;
- count = 1;
+ priv->regaddr = regaddr;
+ priv->regval = regval;
+ priv->count = 1;
}
/* Show the register value read */
- lldbg("%08x->%08x\n", addr, regval);
+ lldbg("%08x->%08x\n", regaddr, regval);
return regval;
}
#else
-static uint32_t can_getreg(struct sam_can_s *priv, int offset)
+static uint32_t can_getreg(FAR struct sam_can_s *priv, int offset)
{
- return getreg32(priv->base + offset);
+ FAR const struct sam_config_s *config = priv->config;
+ return getreg32(config->base + offset);
}
#endif
@@ -329,7 +430,7 @@ static uint32_t can_getreg(struct sam_can_s *priv, int offset)
* Set the value of a CAN register.
*
* Input Parameters:
- * priv - A reference to the CAN block status
+ * priv - A reference to the CAN peripheral state
* offset - The offset to the register to write
* regval - The value to write to the register
*
@@ -338,10 +439,11 @@ static uint32_t can_getreg(struct sam_can_s *priv, int offset)
*
****************************************************************************/
-#ifdef CONFIG_CAN_REGDEBUG
-static void can_putreg(struct sam_can_s *priv, int offset, uint32_t regval)
+#ifdef CONFIG_SAMA5_CAN_REGDEBUG
+static void can_putreg(FAR struct sam_can_s *priv, int offset, uint32_t regval)
{
- uintptr_t regaddr = priv->base + offset;
+ FAR const struct sam_config_s *config = priv->config;
+ uintptr_t regaddr = config->base + offset;
/* Show the register value being written */
@@ -353,9 +455,10 @@ static void can_putreg(struct sam_can_s *priv, int offset, uint32_t regval)
}
#else
-static void can_putreg(struct sam_can_s *priv, int offset, uint32_t regval)
+static void can_putreg(FAR struct sam_can_s *priv, int offset, uint32_t regval)
{
- putreg32(regval, priv->base + offset);
+ FAR const struct sam_config_s *config = priv->config;
+ putreg32(regval, config->base + offset);
}
#endif
@@ -367,16 +470,18 @@ static void can_putreg(struct sam_can_s *priv, int offset, uint32_t regval)
* Dump the contents of all CAN control registers
*
* Input Parameters:
- * priv - A reference to the CAN block status
+ * priv - A reference to the CAN peripheral state
*
* Returned Value:
* None
*
****************************************************************************/
-#ifdef CONFIG_CAN_REGDEBUG
-static void can_dumpctrlregs(struct sam_can_s *priv, FAR const char *msg)
+#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);
@@ -389,19 +494,19 @@ static void can_dumpctrlregs(struct sam_can_s *priv, FAR const char *msg)
/* CAN control and status registers */
lldbg(" MR: %08x IMR: %08x SR: %08x\n",
- getreg32(priv->base + SAM_CAN_MR_OFFSET),
- getreg32(priv->base + SAM_CAN_IMR_OFFSET),
- getreg32(priv->base + SAM_CAN_SR_OFFSET));
+ 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(priv->base + SAM_CAN_BR_OFFSET),
- getreg32(priv->base + SAM_CAN_TIM_OFFSET),
- getreg32(priv->base + SAM_CAN_TIMESTP_OFFSET));
+ 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(priv->base + SAM_CAN_ECR_OFFSET),
- getreg32(priv->base + SAM_CAN_TCR_OFFSET),
- getreg32(priv->base + SAM_CAN_ACR_OFFSET));
+ getreg32(config->base + SAM_CAN_ECR_OFFSET),
+ getreg32(config->base + SAM_CAN_TCR_OFFSET),
+ getreg32(config->base + SAM_CAN_ACR_OFFSET));
}
#endif
@@ -412,16 +517,17 @@ static void can_dumpctrlregs(struct sam_can_s *priv, FAR const char *msg)
* Dump the contents of all CAN mailbox registers
*
* Input Parameters:
- * priv - A reference to the CAN block status
+ * priv - A reference to the CAN peripheral state
*
* Returned Value:
* None
*
****************************************************************************/
-#ifdef CONFIG_CAN_REGDEBUG
-static void can_dumpmbregs(struct sam_can_s *priv, FAR const char *msg)
+#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;
@@ -436,7 +542,7 @@ static void can_dumpmbregs(struct sam_can_s *priv, FAR const char *msg)
for (i = 0; i < SAM_CAN_NMAILBOXES; i++)
{
- mbbase = priv->base + SAM_CAN_MBn_OFFSET(i);
+ mbbase = config->base + SAM_CAN_MBn_OFFSET(i);
lldbg(" MB%d:\n", i);
/* CAN mailbox registers */
@@ -456,6 +562,208 @@ static void can_dumpmbregs(struct sam_can_s *priv, FAR const char *msg)
#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:
@@ -472,44 +780,41 @@ static void can_dumpmbregs(struct sam_can_s *priv, FAR const char *msg)
static void can_reset(FAR struct can_dev_s *dev)
{
- FAR struct sam_can_s *priv = dev->cd_priv;
- uint32_t regval;
- uint32_t regbit = 0;
- irqstate_t flags;
+ FAR struct sam_can_s *priv;
+ FAR const struct sam_config_s *config;
+ int i;
- canllvdbg("CAN%d\n", priv->port);
+ DEBUGASSERT(dev);
+ priv = dev->cd_priv;
+ DEBUGASSERT(priv);
+ config = priv->config;
+ DEBUGASSERT(config);
- /* Get the bits in the AHB1RSTR register needed to reset this CAN device */
+ canllvdbg("CAN%d\n", config->port);
-#ifdef CONFIG_SAMA5_CAN0
- if (priv->port == 1)
- {
-#warning Missing logic
- }
- else
-#endif
-#ifdef CONFIG_SAMA5_CAN1
- if (priv->port == 2)
- {
-#warning Missing logic
- }
- else
-#endif
+ /* 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++)
{
- canlldbg("Unsupported port %d\n", priv->port);
- return;
+ can_putreg(priv, SAM_CAN_MnMR_OFFSET(i), 0);
}
- /* Disable interrupts momentary to stop any ongoing CAN event processing and
- * to prevent any concurrent access to the AHB1RSTR register.
- */
+ /* All mailboxes are again available */
- flags = irqsave();
+ priv->freemb = CAN_ALL_MAILBOXES;
- /* Reset the CAN */
-#warning Missing logic
+ /* Disable the CAN controller */
- irqrestore(flags);
+ can_putreg(priv, SAM_CAN_MR_OFFSET, 0);
+ can_semgive(priv);
}
/****************************************************************************
@@ -531,38 +836,63 @@ static void can_reset(FAR struct can_dev_s *dev)
static int can_setup(FAR struct can_dev_s *dev)
{
- FAR struct sam_can_s *priv = dev->cd_priv;
+ FAR struct sam_can_s *priv;
+ FAR const struct sam_config_s *config;
int ret;
- canllvdbg("CAN%d pid: %d\n", priv->port, priv->pid);
+ 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", priv->port, ret);
+ canlldbg("CAN%d H/W initialization failed: %d\n", config->port, ret);
return ret;
}
- can_dumpctrlregs(priv, "After cell initialization");
+ can_dumpctrlregs(priv, "After hardware initialization");
can_dumpmbregs(priv, NULL);
/* Attach the CAN interrupt handler */
- ret = irq_attach(priv->pid, priv->handler);
+ ret = irq_attach(config->pid, config->handler);
if (ret < 0)
{
- canlldbg("Failed to attach CAN%d IRQ (%d)", priv->port, priv->pid);
+ canlldbg("Failed to attach CAN%d IRQ (%d)", config->port, config->pid);
return ret;
}
- /* Enable the interrupts at the NVIC. Interrupts arestill disabled in
- * the CAN module. Since we coming out of reset here, there should be
- * no pending interrupts.
- */
+ /* 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 */
+
+ can_putreg(priv, SAM_CAN_IER_OFFSET, CAN_INT_ALLERRORS);
+
+ can_dumpctrlregs(priv, "After receive setup");
+ can_dumpmbregs(priv, NULL);
+
+ /* Enable the interrupts at the AIC. */
- up_enable_irq(priv->pid);
+ up_enable_irq(config->pid);
+ can_semgive(priv);
return OK;
}
@@ -583,21 +913,33 @@ static int can_setup(FAR struct can_dev_s *dev)
static void can_shutdown(FAR struct can_dev_s *dev)
{
- FAR struct sam_can_s *priv = dev->cd_priv;
+ 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 */
- canllvdbg("CAN%d\n", priv->port);
+ can_semtake(priv);
/* Disable the CAN interrupts */
- up_disable_irq(priv->pid);
+ up_disable_irq(config->pid);
/* Detach the CAN interrupt handler */
- irq_detach(priv->pid);
+ irq_detach(config->pid);
/* And reset the hardware */
can_reset(dev);
+ can_semgive(priv);
}
/****************************************************************************
@@ -617,20 +959,19 @@ static void can_shutdown(FAR struct can_dev_s *dev)
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->port, enable);
+ canllvdbg("CAN%d enable: %d\n", priv->config->port, enable);
- /* Enable/disable the message pending interrupt */
+ /* Enable/disable the mailbox interrupts from all receive mailboxes */
if (enable)
{
- sam_putreg(xxxx, SAM_CAN_IER_OFFSET);
-#warning Missing logic
+ can_putreg(priv, SAM_CAN_IER_OFFSET, priv->rxmbset);
}
else
{
- sam_putreg(xxxx, SAM_CAN_IDR_OFFSET);
-#warning Missing logic
+ can_putreg(priv, SAM_CAN_IDR_OFFSET, priv->rxmbset);
}
}
@@ -651,22 +992,31 @@ static void can_rxint(FAR struct can_dev_s *dev, bool enable)
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->port, enable);
+ canllvdbg("CAN%d enable: %d\n", priv->config->port, enable);
- /* Support only disabling the transmit mailbox interrupt */
-#warning Missing logic
+ /* 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)
{
- sam_putreg(xxxx, SAM_CAN_IER_OFFSET);
-#warning Missing logic
+ can_putreg(priv, SAM_CAN_IER_OFFSET, priv->txmbset);
+ priv->txdisabled = false;
}
else
{
- sam_putreg(xxxx, SAM_CAN_IDR_OFFSET);
-#warning Missing logic
+ can_putreg(priv, SAM_CAN_IDR_OFFSET, priv->txmbset);
+ priv->txdisabled = true;
}
+
+ can_semgive(priv);
}
/****************************************************************************
@@ -706,7 +1056,8 @@ 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)
{
-#warning "Remote request not implemented"
+ /* REVISIT: Remote request not implemented */
+
return -ENOSYS;
}
@@ -735,98 +1086,103 @@ 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)
{
- FAR struct sam_can_s *priv = dev->cd_priv;
- FAR uint8_t *ptr;
+ FAR struct sam_can_s *priv;
+ FAR uint32_t *md;
uint32_t regval;
- uint32_t tmp;
- int dlc;
- int txmb;
+ int mbndx;
- canllvdbg("CAN%d ID: %d DLC: %d\n", priv->port, msg->cm_hdr.ch_id, msg->cm_hdr.ch_dlc);
+ DEBUGASSERT(dev);
+ priv = dev->cd_priv;
+ DEBUGASSERT(priv && priv->config);
- /* Select one empty transmit mailbox */
+ 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);
-#warning Missing logic
+ /* Get exclusive access to the CAN peripheral */
+
+ can_semtake(priv);
+
+ /* Allocate a mailbox */
+
+ mbndx = can_mballoc(priv);
+ if (mbndx < 0)
{
- canlldbg("ERROR: No available mailbox\n");
- return -EBUSY;
+ candbg("ERROR: CAN%d failed to allocate a mailbox: %d\n",
+ priv->config->port, mbndx);
+ return mbndx;
}
- /* Set up the ID, standard 11-bit or extended 29-bit. */
+ 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
- if (msg->cm_hdr.ch_extid)
- {
- DEBUGASSERT(msg->cm_hdr.ch_id < (1 << 29));
-#warning Missing logic
- }
- else
- {
- DEBUGASSERT(msg->cm_hdr.ch_id < (1 << 11));
-#warning Missing logic
- }
+ 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
-#warning Missing logic
+ 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
- /* Set up the DLC */
+ /* Enable transmit mode */
- dlc = msg->cm_hdr.ch_dlc;
-#warning Missing logic
+ can_putreg(priv, SAM_CAN_MnMR_OFFSET(mbndx), CAN_MMR_MOT_TX);
- /* Set up the data fields */
+ /* 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.
+ */
- ptr = msg->cm_data;
- regval = 0;
+ DEBUGASSERT((can_getreg(priv, SAM_CAN_MnSR_OFFSET(mbndx)) & CAN_MSR_MRDY) != 0);
- if (dlc > 0)
- {
-#warning Missing logic
+ /* 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]
+ */
- if (dlc > 1)
- {
-#warning Missing logic
+#ifdef CONFIG_ENDIAN_BIG
+# warning REVISIT
+#endif
- if (dlc > 2)
- {
-#warning Missing logic
+ DEBUGASSERT(((uintptr_t)msg->cm_data & 3) == 0);
+ md = (FAR uint32_t *)msg->cm_data;
- if (dlc > 3)
- {
-#warning Missing logic
- }
- }
- }
- }
+ can_putreg(priv, SAM_CAN_MnDL_OFFSET(mbndx), md[0]);
+ can_putreg(priv, SAM_CAN_MnDH_OFFSET(mbndx), md[1]);
- regval = 0;
- if (dlc > 4)
- {
-#warning Missing logic
+ /* 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.
+ */
- if (dlc > 5)
- {
-#warning Missing logic
+ regval = CAN_MCR_MDLC(msg->cm_hdr.ch_dlc) | CAN_MCR_MTCR;
+ can_putreg(priv, SAM_CAN_MnCR_OFFSET(mbndx), regval);
- if (dlc > 6)
- {
-#warning Missing logic
+ /* If we have not been asked to suppress TX interrupts, then dnable
+ * interrupts from this mailbox now.
+ */
- if (dlc > 7)
- {
-#warning Missing logic
- }
- }
- }
+ if (!priv->txdisabled)
+ {
+ can_putreg(priv, SAM_CAN_IER_OFFSET, CAN_INT_MB(mbndx));
}
- /* Enable the transmit mailbox empty interrupt (may already be enabled) */
-#warning Missing logic
-
- /* Request transmission */
-#warning Missing logic
-
can_dumpmbregs(priv, "After send");
+ can_semgive(priv);
return OK;
}
@@ -847,11 +1203,18 @@ 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)
{
FAR struct sam_can_s *priv = dev->cd_priv;
+ bool txready;
- /* Return true if any mailbox is available */
-#warning Missing logic
+ /* Get exclusive access to the CAN peripheral */
- return false;
+ can_semtake(priv);
+
+ /* Return true not all mailboxes are in-use */
+
+ txready = ((priv->rxmbset | priv->txmbset) != CAN_ALL_MAILBOXES);
+
+ can_semgive(priv);
+ return txready;
}
/****************************************************************************
@@ -875,11 +1238,190 @@ static bool can_txready(FAR struct can_dev_s *dev)
static bool can_txempty(FAR struct can_dev_s *dev)
{
FAR struct sam_can_s *priv = dev->cd_priv;
+ return (priv->txmbset == 0);
+}
- /* Return true if all mailboxes are available */
-#warning Missing logic
+/****************************************************************************
+ * 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
- return false;
+ 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;
+ }
+ }
}
/****************************************************************************
@@ -896,14 +1438,21 @@ static bool can_txempty(FAR struct can_dev_s *dev)
*
****************************************************************************/
-static void can_interrupt(struct sam_can_s *priv)
+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;
- uint32_t regval;
- /* Get the set of pending interrupts */
+ 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);
@@ -924,7 +1473,19 @@ static void can_interrupt(struct sam_can_s *priv)
if ((pending & CAN_INT_MBALL) != 0)
{
-#warning Missing logic
+ 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
@@ -948,7 +1509,8 @@ static void can_interrupt(struct sam_can_s *priv)
if ((pending & ~CAN_INT_MBALL) != 0)
{
-#warning Missing logic
+ canlldbg("ERROR: CAN%d system interrupt, SR=%08x IMR=%08x\n",
+ priv->config->port, sr, imr);
}
}
@@ -1013,7 +1575,7 @@ static int can1_interrupt(int irq, void *context)
* 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.
+ * 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
@@ -1077,7 +1639,7 @@ static int can1_interrupt(int irq, void *context)
* Tphs2 = Tq * (PHASE2 + 1)
*
* Input Parameter:
- * priv - A reference to the CAN block status
+ * config - A reference to the CAN constant configuration
*
* Returned Value:
* Zero on success; a negated errno on failure
@@ -1086,6 +1648,7 @@ static int can1_interrupt(int irq, void *context)
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;
@@ -1100,7 +1663,7 @@ static int can_bittiming(struct sam_can_s *priv)
* REVISIT: We could probably do a better job than this.
*/
- if (priv->baud >= 1000)
+ if (config->baud >= 1000)
{
tq = 8;
}
@@ -1119,12 +1682,12 @@ static int can_bittiming(struct sam_can_s *priv)
* brp = CAN_FREQUENCY / (baud * nquanta) - 1
*/
- brp = (CAN_FREQUENCY / (priv->baud * 1000 * tq)) - 1;
+ brp = (CAN_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", priv->port, priv->baud);
+ candbg("CAN%d: baud %d too high\n", config->port, config->baud);
return -EINVAL;
}
@@ -1135,8 +1698,8 @@ static int can_bittiming(struct sam_can_s *priv)
* Delay Bus Line (20m) - 110ns
*/
- propag = tq * priv->baud * 2 * (50 + 30 + 110) / 1000000
- if ((propag >= 1)
+ propag = tq * config->baud * 2 * (50 + 30 + 110) / 1000000;
+ if (propag >= 1)
{
propag--;
}
@@ -1152,7 +1715,7 @@ static int can_bittiming(struct sam_can_s *priv)
/* Calcuate phase1 and phase2 */
phase1 = (t1t2 >> 1) - 1;
- phase2 = tphase1;
+ phase2 = phase1;
if ((t1t2 & 1) != 0)
{
@@ -1160,7 +1723,7 @@ static int can_bittiming(struct sam_can_s *priv)
}
/* Calculate SJW */
-
+
if (1 > (4 / (phase1 + 1)))
{
sjw = 3;
@@ -1172,21 +1735,22 @@ static int can_bittiming(struct sam_can_s *priv)
if ((propag + phase1 + phase2) != (uint32_t)(tq - 4))
{
- candbg("CAN%d: Could not realize baud %d\n", priv->port, priv->baud);
+ 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);
+ can_putreg(priv, SAM_CAN_BR_OFFSET, regval);
+ return OK;
}
/****************************************************************************
- * Name: can_hwinitialize
+ * Name: can_autobaud
*
* Description:
- * CAN cell initialization
+ * 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
@@ -1196,57 +1760,101 @@ static int can_bittiming(struct sam_can_s *priv)
*
****************************************************************************/
-static int can_hwinitialize(struct sam_can_s *priv)
+#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", priv->port);
+ canllvdbg("CAN%d\n", config->port);
- /* Configure CAN pins */
+ /* 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.
+ */
- sam_configpio(priv->rxpinset);
- sam_configpio(priv->txpinset);
+ /* Configure a Mailbox in Reception Mode */
+#warning Missing Logic
- /* Set the maximum CAN peripheral clock frequency */
+ /* Loop, adjusting bit rate parameters until no errors are reported in
+ * either CAR_SR or the CAN_MSRx registers.
+ */
- regval = PMC_PCR_PID(priv->pid) | PMC_PCR_CMD | CAN_PCR_DIV | PMC_PCR_EN;
- sam_adc_putreg(priv, SAM_PMC_PCR, regval);
+ do
+ {
+ /* Adjust baud rate setting */
+#warning Missing Logic
- /* Enable peripheral clocking */
+ /* 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.
+ */
- sam_enableperiph1(priv->pid);
+ regval = can_getreg(priv, SAM_CAN_MR_OFFSET);
+ regval |= (CAN_MR_CANEN | CAN_MR_ABM);
+ can_putreg(priv, SAM_CAN_MR_OFFSET, regval);
- /* Exit from sleep mode */
-#warning Missing Logic
+#warning Missing logic
+ }
+ while ( no errors reported );
- /* Configure CAN behavior. Priority driven request order, not message ID. */
-#warning Missing Logic
+ /* 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.
+ */
- /* Enter initialization mode */
-#warning Missing Logic
+ regval = can_getreg(priv, SAM_CAN_MR_OFFSET);
+ regval &= ~(CAN_MR_CANEN | CAN_MR_ABM);
+ can_putreg(priv, SAM_CAN_MR_OFFSET, regval);
- /* Wait until initialization mode is acknowledged */
+ return OK;
+}
+#endif
- for (timeout = INAK_TIMEOUT; timeout > 0; timeout--)
- {
-#warning Missing Logic
- }
+/****************************************************************************
+ * 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.
+ *
+ ****************************************************************************/
- /* Check for a timeout */
-#warning Missing Logic
+static int can_hwinitialize(struct sam_can_s *priv)
+{
+ FAR const struct sam_config_s *config = priv->config;
+ uint32_t regval;
+ int ret;
- /* Disable the following modes:
- *
- * - Time triggered communication mode
- * - Automatic bus-off management
- * - Automatic wake-up mode
- * - No automatic retransmission
- * - Receive FIFO locked mode
- * - Transmit FIFO priority
- */
-#warning Missing Logic
+ canllvdbg("CAN%d\n", config->port);
+
+ /* Configure CAN pins */
+
+ sam_configpio(config->rxpinset);
+ sam_configpio(config->txpinset);
+
+ /* Set the maximum CAN peripheral clock frequency */
+
+ regval = PMC_PCR_PID(config->pid) | PMC_PCR_CMD | CAN_PCR_DIV | 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. */
@@ -1257,19 +1865,33 @@ static int can_hwinitialize(struct sam_can_s *priv)
return ret;
}
- /* Exit initialization mode */
-#warning Missing Logic
-
- /* Wait until the initialization mode exit is acknowledged */
+#ifdef CONFIG_SAMA5_CAN_AUTOBAUD
+ /* Optimize/correct bit timing */
- for (timeout = INAK_TIMEOUT; timeout > 0; timeout--)
+ ret = can_autobaud(priv);
+ if (ret < 0)
{
-#warning Missing Logic
+ candbg("ERROR: can_autobaud failed: %d\n", ret);
+ return ret;
}
+#endif
- /* Check for a timeout */
-#warning Missing Logic
+ /* 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;
}
@@ -1293,7 +1915,9 @@ static int can_hwinitialize(struct sam_can_s *priv)
FAR struct can_dev_s *sam_caninitialize(int port)
{
- struct can_dev_s *dev = NULL;
+ FAR struct can_dev_s *dev;
+ FAR struct sam_can_s *priv;
+ FAR const struct sam_config_s *config;
canvdbg("CAN%d\n", port);
@@ -1306,7 +1930,9 @@ FAR struct can_dev_s *sam_caninitialize(int port)
{
/* Select the CAN0 device structure */
- dev = &g_can0dev;
+ dev = &g_can0dev;
+ priv = &g_can0priv;
+ config = &g_can0const;
}
else
#endif
@@ -1315,7 +1941,9 @@ FAR struct can_dev_s *sam_caninitialize(int port)
{
/* Select the CAN1 device structure */
- dev = &g_can1dev;
+ dev = &g_can1dev;
+ priv = &g_can1priv;
+ config = &g_can1const;
}
else
#endif
@@ -1324,8 +1952,28 @@ FAR struct can_dev_s *sam_caninitialize(int 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) */
-
diff --git a/nuttx/arch/arm/src/sama5/sam_can.h b/nuttx/arch/arm/src/sama5/sam_can.h
index 6ac429a77..6cf0ae4bc 100644
--- a/nuttx/arch/arm/src/sama5/sam_can.h
+++ b/nuttx/arch/arm/src/sama5/sam_can.h
@@ -56,12 +56,44 @@
/* CAN BAUD */
-#if defined(CONFIG_SAMA5_CAN0) && !defined(CONFIG_CAN0_BAUD)
-# error "CONFIG_CAN0_BAUD is not defined"
+#if defined(CONFIG_SAMA5_CAN0) && !defined(CONFIG_SAMA5_CAN0_BAUD)
+# error "CONFIG_SAMA5_CAN0_BAUD is not defined"
#endif
-#if defined(CONFIG_SAMA5_CAN1) && !defined(CONFIG_CAN1_BAUD)
-# error "CONFIG_CAN1_BAUD is not defined"
+#if defined(CONFIG_SAMA5_CAN1) && !defined(CONFIG_SAMA5_CAN1_BAUD)
+# error "CONFIG_SAMA5_CAN1_BAUD is not defined"
+#endif
+
+/* There must be at least one but not more than three receive mailboxes */
+
+#ifdef CONFIG_SAMA5_CAN0
+# if !defined(CONFIG_SAMA5_CAN0_NRECVMB) || CONFIG_SAMA5_CAN0_NRECVMB < 1
+# undef CONFIG_SAMA5_CAN0_NRECVMB
+# define CONFIG_SAMA5_CAN0_NRECVMB 1
+# endif
+# if CONFIG_SAMA5_CAN0_NRECVMB > 3
+# warning Current implementation only supports up to three receive mailboxes
+# undef CONFIG_SAMA5_CAN0_NRECVMB
+# define CONFIG_SAMA5_CAN0_NRECVMB 3
+# endif
+#else
+# undef CONFIG_SAMA5_CAN0_NRECVMB
+# define CONFIG_SAMA5_CAN0_NRECVMB 0
+#endif
+
+#ifdef CONFIG_SAMA5_CAN1
+# if !defined(CONFIG_SAMA5_CAN1_NRECVMB) || CONFIG_SAMA5_CAN1_NRECVMB < 1
+# undef CONFIG_SAMA5_CAN1_NRECVMB
+# define CONFIG_SAMA5_CAN1_NRECVMB 1
+# endif
+# if CONFIG_SAMA5_CAN1_NRECVMB > 3
+# warning Current implementation only supports up to three receive mailboxes
+# undef CONFIG_SAMA5_CAN1_NRECVMB
+# define CONFIG_SAMA5_CAN1_NRECVMB 3
+# endif
+#else
+# undef CONFIG_SAMA5_CAN1_NRECVMB
+# define CONFIG_SAMA5_CAN1_NRECVMB 0
#endif
/************************************************************************************
@@ -101,7 +133,7 @@ extern "C" {
****************************************************************************/
struct can_dev_s;
-FAR struct can_dev_s *sama5_caninitialize(int port);
+FAR struct can_dev_s *sam_caninitialize(int port);
#undef EXTERN
#if defined(__cplusplus)
diff --git a/nuttx/drivers/Kconfig b/nuttx/drivers/Kconfig
index 2f022b8d5..c10fb063e 100644
--- a/nuttx/drivers/Kconfig
+++ b/nuttx/drivers/Kconfig
@@ -22,7 +22,7 @@ config DEV_ZERO
config ARCH_HAVE_RNG
bool
-config DEV_RANDOM
+config DEV_RANDOM
bool "Enable /dev/random"
default n
depends on ARCH_HAVE_RNG
@@ -71,7 +71,7 @@ config CAN_NPENDINGRTR
The size of the list of pending RTR requests. Default: 4
config CAN_LOOPBACK
- bool "CAN extended IDs"
+ bool "CAN loopback mode"
default n
---help---
A CAN driver may or may not support a loopback mode for testing. If the
@@ -202,7 +202,7 @@ config RTC_FREQUENCY
must be provided. If RTC_HIRES is not defined, RTC_FREQUENCY is
assumed to be one Hz.
-config RTC_ALARM
+config RTC_ALARM
bool "RTC Alarm Support"
default n
depends on RTC
diff --git a/nuttx/include/nuttx/can.h b/nuttx/include/nuttx/can.h
index 31d34478e..3db7746ee 100644
--- a/nuttx/include/nuttx/can.h
+++ b/nuttx/include/nuttx/can.h
@@ -335,7 +335,7 @@ EXTERN int can_register(FAR const char *path, FAR struct can_dev_s *dev);
* Called from the CAN interrupt handler when new read data is available
*
* Parameters:
- * dev - The specifi CAN device
+ * dev - The specific CAN device
* hdr - The 16-bit CAN header
* data - An array contain the CAN data.
*
@@ -354,9 +354,7 @@ EXTERN int can_receive(FAR struct can_dev_s *dev, FAR struct can_hdr_s *hdr,
* Called from the CAN interrupt handler at the completion of a send operation.
*
* Parameters:
- * dev - The specifi CAN device
- * hdr - The 16-bit CAN header
- * data - An array contain the CAN data.
+ * dev - The specific CAN device
*
* Return:
* OK on success; a negated errno on failure.