You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ha...@apache.org on 2022/03/15 15:32:42 UTC

[incubator-nuttx] 01/02: Add Tiva CAN driver

This is an automated email from the ASF dual-hosted git repository.

hartmannathan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit bc80bbddc7bffe1375475473be472bb4121ddb65
Author: Matthew Trescott <ma...@gmail.com>
AuthorDate: Mon Feb 28 20:46:58 2022 -0500

    Add Tiva CAN driver
---
 arch/arm/src/tiva/Kconfig                          |  142 ++
 arch/arm/src/tiva/Make.defs                        |    4 +
 arch/arm/src/tiva/common/tiva_can.c                | 2409 ++++++++++++++++++++
 arch/arm/src/tiva/hardware/tiva_can.h              |  459 ++++
 .../tm4c_bringup.c => arch/arm/src/tiva/tiva_can.h |  101 +-
 boards/arm/tiva/tm4c123g-launchpad/Kconfig         |   32 +
 boards/arm/tiva/tm4c123g-launchpad/include/board.h |   22 +
 boards/arm/tiva/tm4c123g-launchpad/src/Makefile    |    4 +
 .../tm4c123g-launchpad/src/tm4c123g-launchpad.h    |   12 +
 .../arm/tiva/tm4c123g-launchpad/src/tm4c_bringup.c |   10 +
 boards/arm/tiva/tm4c123g-launchpad/src/tm4c_can.c  |  153 ++
 11 files changed, 3294 insertions(+), 54 deletions(-)

diff --git a/arch/arm/src/tiva/Kconfig b/arch/arm/src/tiva/Kconfig
index 582786d..4558c85 100644
--- a/arch/arm/src/tiva/Kconfig
+++ b/arch/arm/src/tiva/Kconfig
@@ -125,6 +125,7 @@ config ARCH_CHIP_TM4C123GH6PM
 	select TIVA_HAVE_GPIOF_IRQS
 	select TIVA_HAVE_ADC0
 	select TIVA_HAVE_ADC1
+	select TIVA_HAVE_CAN1
 	select TIVA_HAVE_QEI0
 	select TIVA_HAVE_QEI1
 
@@ -350,6 +351,7 @@ config ARCH_CHIP_TM4C
 	select ARCH_HAVE_FPU
 	select TIVA_HAVE_ADC0
 	select TIVA_HAVE_ADC1
+	select TIVA_HAVE_CAN0
 	select TIVA_HAVE_I2C1
 	select TIVA_HAVE_I2C2
 	select TIVA_HAVE_I2C3
@@ -423,6 +425,21 @@ config TIVA_HAVE_ADC1
 	bool
 	default n
 
+config TIVA_CAN
+	bool
+	default n
+	select ARCH_HAVE_CAN_ERRORS
+	select CAN_TXREADY
+	select CAN_USE_RTR
+
+config TIVA_HAVE_CAN0
+	bool
+	default n
+
+config TIVA_HAVE_CAN1
+	bool
+	default n
+
 config TIVA_QEI
 	bool
 	default n
@@ -582,6 +599,40 @@ config TIVA_ADC1
 	depends on TIVA_HAVE_ADC0
 	select TIVA_ADC
 
+config TIVA_CAN0
+	bool "CAN0"
+	default n
+	depends on TIVA_HAVE_CAN0
+	select CAN
+	select TIVA_CAN
+
+config TIVA_CAN0_PRIO
+	int "CAN0 kthread priority"
+	default 300
+	depends on TIVA_CAN0
+	---help---
+		The Tiva CAN driver retrieves messages using a kthread rather
+		than in the ISR or using a work queue. The ISR signals the
+		kthread, but the kthread can be preempted if needed. This
+		option sets the thread priority for CAN module 0.
+
+config TIVA_CAN1
+	bool "CAN1"
+	default n
+	depends on TIVA_HAVE_CAN1
+	select CAN
+	select TIVA_CAN
+
+config TIVA_CAN1_PRIO
+	int "CAN1 kthread priority"
+	default 300
+	depends on TIVA_CAN1
+	---help---
+		The Tiva CAN driver retrieves messages using a kthread rather
+		than in the ISR or using a work queue. The ISR signals the
+		kthread, but the kthread can be preempted if needed. This
+		option sets the thread priority for CAN module 1.
+
 config TIVA_QEI0
 	bool "QEI0"
 	default n
@@ -1461,6 +1512,97 @@ config TIVA_ADC_REGDEBUG
 endmenu # Tiva ADC Configuration
 endif # TIVA_ADC
 
+menu "CAN Driver Configuration"
+	depends on TIVA_CAN
+
+config TIVA_CAN_REGDEBUG
+	bool "CAN register level debug"
+	depends on  DEBUG_CAN_INFO
+	default n
+	---help---
+		Output detailed register-level CAN device debug information.
+		Requires also CONFIG_DEBUG_CAN_INFO.
+
+config TIVA_CAN_TX_FIFO_DEPTH
+	int "Size of the hardware TX FIFO"
+	range 1 31
+	default 8
+	---help---
+		This number determines the depth of the hardware transmit
+		FIFO, which cannot be resized. Using a transmit FIFO allows
+		the application to transmit messages at maximum speed and
+		reduces jitter caused by excessive interrupts. However, it
+		occupies extra mailboxes that might be used for more fine-
+		grained filtering; hence there is a trade-off.
+
+		Because the mailboxes on the Tiva chips cannot be used as
+		a ring buffer for outbound messages, a larger TX FIFO
+		directly increases the maximum number of messages that
+		can be sent in a "burst" at maximum speed.
+
+		However, the TX FIFO makes implementation of
+		"pre-arbitration" more difficult. Although the classic
+		character device CAN model does not support this anyway,
+		transmission of a higher-priority message would require
+		cancelling and reenqueuing of the low-priority messages
+		already in the hardware.
+
+config TIVA_CAN_DEFAULT_FIFO_DEPTH
+	int "Default CAN message RX FIFO depth"
+	range 1 31
+	default 6
+	---help---
+		This number determines the default depth for all RX
+		hardware FIFOs in the CAN module. The size of a FIFO can be
+		adjusted at any time (and made deeper than this number if
+		needed) via the various CANIOC_TIVA ioctls. However, this
+		option is limited to 31 because the driver must allocate
+		at least one mailbox for outbound (TX) messages. The default
+		RX filter FIFO is set to allow any messages, but is
+		reconfigured as soon as an application uses the
+		CANIOC_ADD_STDFILTER or CANIOC_ADD_EXTFILTER.
+
+config TIVA_CAN_FILTERS_MAX
+	int "Maximum number of CAN RX filters"
+	range 1 31
+	default 4
+	---help---
+		This number applies to both CAN0 and CAN1, if the chip has
+		both. (This is the number of filters for each module, not the
+		total for both CAN modules.)
+		The Tiva CAN modules have 32 message objects. Only one is
+		used for TX, so it is possible to have up to 31 unique
+		RX filters. However, increasing the FIFO depth of a filter
+		uses up message objects and reduces this number. Additionally,
+		since the driver has to keep track of the message objects
+		assigned to each filter, increasing the number of available
+		filters increases the memory footprint even if the filters
+		are not used (internally, it is a statically-allocated array).
+
+config TIVA_CAN_ERR_HANDLER_PER
+	int "Rate-limited error handling period (milliseconds)"
+	range 10 1000
+	default 100
+	depends on CAN_ERRORS
+	---help---
+		When error messages (CAN_ERRORS) are enabled, the Tiva
+		CAN driver will disable interrupts for individual errors
+		when the application fails to call read() quickly enough
+		to keep up. This can easily happen during testing if no
+		transceiver is connected and the application is trying
+		to print error messages to a serial console.
+
+		When this happens, the driver defers error reporting to
+		a periodic task in the high-priority work queue. The task
+		checks for errors and reports them at the specified
+		interval. It is unlikely that the bus would be disturbed
+		badly enough to cause interrupt overload and yet be usable
+		for real-time communication, so successful transmission or
+		reception of a message will return the driver to the
+		normal interrupt handling mode.
+
+endmenu
+
 if TIVA_ETHERNET
 
 menu "Stellaris Ethernet Configuration"
diff --git a/arch/arm/src/tiva/Make.defs b/arch/arm/src/tiva/Make.defs
index b9d0d7b..692e6a0 100644
--- a/arch/arm/src/tiva/Make.defs
+++ b/arch/arm/src/tiva/Make.defs
@@ -138,6 +138,10 @@ ifeq ($(CONFIG_TIVA_ADC),y)
   CHIP_CSRCS += tiva_adclib.c
 endif
 
+ifeq ($(CONFIG_TIVA_CAN),y)
+  CHIP_CSRCS += tiva_can.c
+endif
+
 ifeq ($(CONFIG_TIVA_ETHERNET),y)
 ifeq ($(CONFIG_ARCH_CHIP_LM3S),y)
   CHIP_CSRCS += lm3s_ethernet.c
diff --git a/arch/arm/src/tiva/common/tiva_can.c b/arch/arm/src/tiva/common/tiva_can.c
new file mode 100644
index 0000000..0221f57
--- /dev/null
+++ b/arch/arm/src/tiva/common/tiva_can.c
@@ -0,0 +1,2409 @@
+/****************************************************************************
+ * arch/arm/src/tiva/common/tiva_can.c
+ * Classic (character-device) lower-half driver for the Tiva CAN modules.
+ *
+ * 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 <stdio.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <arch/board/board.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/kthread.h>
+#include <nuttx/mutex.h>
+#include <nuttx/semaphore.h>
+
+#include <nuttx/can/can.h>
+
+#ifdef CONFIG_CAN_ERRORS
+/* A self-reenqueuing worker is used to report errors periodically when
+ * status interrupts are disabled due to overload.
+ */
+
+#include <nuttx/wqueue.h>
+#endif
+
+#include "arm_internal.h"
+
+#include "chip.h"
+#include "tiva_can.h"
+#include "hardware/tiva_can.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#if defined(CONFIG_TIVA_CAN) && (defined(CONFIG_TIVA_CAN0) || defined(CONFIG_TIVA_CAN1))
+
+#if defined(CONFIG_CAN_ERRORS) && !defined(CONFIG_SCHED_LPWORK)
+#  error "Error reporting requires the low-priority work queue to be enabled."
+#endif
+
+/* Combines two 16-bit registers into a single 32 bit value */
+#define tivacan_readsplitreg32(base, r1, r2) ((getreg32((base) + (r1)) & 0xffff) | (getreg32((base) + (r2)) << 16))
+
+#define tivacan_get_nwda32(base) tivacan_readsplitreg32((base), TIVA_CAN_OFFSET_NWDA1, TIVA_CAN_OFFSET_NWDA2)
+#define tivacan_get_txrq32(base) tivacan_readsplitreg32((base), TIVA_CAN_OFFSET_TXRQ1, TIVA_CAN_OFFSET_TXRQ2)
+#define tivacan_get_msgval32(base) tivacan_readsplitreg32((base), TIVA_CAN_OFFSET_MSG1VAL, TIVA_CAN_OFFSET_MSG2VAL)
+
+/* Only the last few mailboxes are used for TX to help ensure that responses
+ * to remote request frames we send are always collected by an RX FIFO and
+ * not by the mailbox used to send the remote frame. (The Tiva hardware uses
+ * the mailbox with the lowest number first.)
+ *
+ * This number is zero-indexed, althought the Command Request Register isn't.
+ */
+#define TIVA_CAN_TX_FIFO_START (TIVA_CAN_NUM_MBOXES - CONFIG_TIVA_CAN_TX_FIFO_DEPTH)
+
+/* Frequency of async error message reporting when rate-limited */
+#define TIVA_CAN_ERR_HANDLER_TICKS MSEC2TICK(CONFIG_TIVA_CAN_ERR_HANDLER_PER)
+
+/* For the RX receive kthread */
+#define TIVA_CAN_KTHREAD_STACKSIZE 1024
+
+/* For tivacan_initfilter */
+#define TIVA_CAN_FILTER_TYPE_STD 0
+#define TIVA_CAN_FILTER_TYPE_EXT 1
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Bitfield representing the mailboxes belonging to this FIFO. The
+ * high bit (mask: 1 << 31) corresponds to mailbox 31, the low bit
+ * (mask: 1 << 0) corresponds to mailbox 0.
+ *
+ * NOTE: If a new chip came along with more mailboxes, this would
+ * need to be reworked.
+ */
+
+typedef uint32_t tiva_can_fifo_t;
+
+/* This structure represents a CAN module on the microcontroller */
+
+struct tiva_canmod_s
+{
+  /* Module number, mostly for syscon register fields */
+
+  int       modnum;
+
+  /* Registers base address */
+
+  uint32_t  base;
+
+  /* Contents of the CANSTS reg the last time errors were processed. */
+
+  uint32_t  status;
+
+  /* Contents of the CANERR reg the last time errors were processed. */
+
+  uint32_t  errors;
+
+  /* kthread message handler waits on this; ISR posts */
+
+  sem_t     rxsem;
+
+  /* Mailbox number (zero-indexed) that the kthread message handler is
+   * currently retrieving from the CAN module SRAM or in the process of
+   * sending to the application. The kthread works through messages in
+   * ascending order, so if the ISR will only post the semaphore for received
+   * messages that the kthread has already "passed by."
+   */
+
+  int       kthd_cur_mbox;
+
+  /* kthread message handler thread ID */
+
+  int       kthd_id;
+
+#ifdef CONFIG_CAN_ERRORS
+  /* Asynchronously report errors when status interrupts are disabled */
+
+  struct work_s error_work;
+#endif
+
+  /* Whether to autorecover from bus-off conditions */
+
+  bool      autorecover;
+
+  /* Mutex for threads accessing the interface registers */
+
+  mutex_t   thd_iface_mtx;
+
+  /* Interface registers base address for threads threads */
+
+  uint32_t  thd_iface_base;
+
+  /* Interface registers base address reserved exclusively for the ISR */
+
+  uint32_t  isr_iface_base;
+
+  /* Mutex for claiming, freeing, and potentially resizing RX FIFOs.
+   * The TX FIFO should never be resized at runtime.
+   */
+
+  mutex_t   fifo_mtx;
+
+  /* All RX FIFOs + 1 TX FIFO */
+
+  tiva_can_fifo_t fifos[CONFIG_TIVA_CAN_FILTERS_MAX + 1];
+
+  /* Pointer to default catch-all RX FIFO initialized by the driver. */
+
+  FAR tiva_can_fifo_t *rxdefault_fifo;
+
+  /* Pointer to the permanent, fixed-size TX FIFO. This is always located at
+   * the end of the series of mailboxes to help ensure that responses to
+   * remote request frames go to an RX (filter) FIFO and don't interfere.
+   */
+
+  FAR tiva_can_fifo_t *tx_fifo;
+
+  /* Index in the TX FIFO where new messages should be added. The Tiva CAN
+   * module doesn't support a ring buffer, so new messages are only added
+   * when the TX FIFO is empty (tx_fifo_tail == 0).
+   */
+
+  int tx_fifo_tail;
+};
+
+/* This structure represents the CAN bit timing settings as seen by the hw. */
+
+struct tiva_can_timing_s
+{
+  int       prescaler;
+  int       tseg1;
+  int       tseg2;
+  int       sjw;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Callbacks for upper-half CAN driver */
+
+static void tivacan_reset(FAR struct can_dev_s *dev);
+static int  tivacan_setup(FAR struct can_dev_s *dev);
+static void tivacan_shutdown(FAR struct can_dev_s *dev);
+static void tivacan_rxintctl(FAR struct can_dev_s *dev, bool enable);
+static void tivacan_txintctl(FAR struct can_dev_s *dev, bool enable);
+static int  tivacan_ioctl(FAR struct can_dev_s *dev,
+                          int cmd, unsigned long arg);
+static int  tivacan_send(FAR struct can_dev_s *dev,
+                         FAR struct can_msg_s *msg);
+static bool tivacan_txready(FAR struct can_dev_s *dev);
+static bool tivacan_txempty(FAR struct can_dev_s *dev);
+
+/* ISR */
+
+static int  tivacan_isr(int irq, FAR void *context, FAR void *dev);
+
+/* Internal utility functions */
+
+static struct tiva_can_timing_s
+tivacan_bittiming_get(FAR struct can_dev_s *dev);
+
+static void tivacan_bittiming_set(FAR struct can_dev_s *dev,
+                                  FAR struct tiva_can_timing_s *timing);
+
+int tivacan_alloc_fifo(FAR struct can_dev_s *dev, int depth);
+static void tivacan_free_fifo(FAR struct can_dev_s *dev,
+                              FAR tiva_can_fifo_t *fifo);
+static void tivacan_disable_mbox(FAR struct can_dev_s *dev,
+                                 uint32_t iface_base,
+                                 int num);
+static int  tivacan_initfilter(FAR struct can_dev_s       *dev,
+                               FAR tiva_can_fifo_t        *fifo,
+                               FAR void                   *filter,
+                               int                         type);
+static int  tivacan_rxhandler(int argc, char** argv);
+int tivacan_handle_errors(FAR struct can_dev_s *dev);
+
+#ifdef CONFIG_CAN_ERRORS
+void tivacan_handle_errors_wqueue(FAR void * dev);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* List of callbacks provided to can_register via g_tivacanNdev */
+
+static const struct can_ops_s g_tivacanops =
+{
+  .co_reset         = tivacan_reset,
+  .co_setup         = tivacan_setup,
+  .co_shutdown      = tivacan_shutdown,
+  .co_rxint         = tivacan_rxintctl,
+  .co_txint         = tivacan_txintctl,
+  .co_ioctl         = tivacan_ioctl,
+  .co_remoterequest = NULL,               /* Use CONFIG_CAN_USE_RTR instead */
+  .co_send          = tivacan_send,
+  .co_txready       = tivacan_txready,
+  .co_txempty       = tivacan_txempty,
+};
+
+#ifdef CONFIG_TIVA_CAN0
+static struct tiva_canmod_s g_tivacan0priv =
+{
+  .modnum           = 0,
+  .base             = TIVA_CAN_BASE(0),
+  .thd_iface_base   = TIVA_CAN_IFACE_BASE(0, 0),
+  .isr_iface_base   = TIVA_CAN_IFACE_BASE(0, 1),
+  .rxdefault_fifo   = NULL,
+};
+
+static struct can_dev_s g_tivacan0dev =
+{
+  .cd_ops           = &g_tivacanops,
+  .cd_priv          = &g_tivacan0priv,
+};
+#endif /* CONFIG_TIVA_CAN0 */
+
+#ifdef CONFIG_TIVA_CAN1
+static struct tiva_canmod_s g_tivacan1priv =
+{
+  .modnum           = 1,
+  .base             = TIVA_CAN_BASE(1),
+  .thd_iface_base   = TIVA_CAN_IFACE_BASE(1, 0),
+  .isr_iface_base   = TIVA_CAN_IFACE_BASE(1, 1),
+  .rxdefault_fifo   = NULL,
+};
+
+static struct can_dev_s g_tivacan1dev =
+{
+  .cd_ops           = &g_tivacanops,
+  .cd_priv          = &g_tivacan1priv,
+};
+#endif /* CONFIG_TIVA_CAN1 */
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: tivacan_reset
+ *
+ * Description:
+ *   Software-reset the CAN module using the SYSCON registers.
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure
+ *
+ * Returned value: None
+ ****************************************************************************/
+
+static void tivacan_reset(FAR struct can_dev_s *dev)
+{
+  FAR struct tiva_canmod_s *canmod = dev->cd_priv;
+  int modnum = canmod->modnum;
+#ifndef CONFIG_TIVA_CAN0
+  if (modnum == 0)
+    {
+      canerr("ERROR: tried to reset disabled module CAN0\n");
+      return;
+    }
+
+#endif
+#ifndef CONFIG_TIVA_CAN1
+  if (modnum == 1)
+    {
+      canerr("ERROR: tried to reset disabled module CAN1\n");
+    }
+
+#endif
+  if (modnum > 1)
+    {
+      canerr("ERROR: tried to reset nonexistant module CAN%d\n",
+             canmod->modnum);
+    }
+
+  modifyreg32(TIVA_SYSCON_SRCAN, 0, SYSCON_SRCAN(modnum));
+
+  /* Spin for a few clock cycles */
+
+  for (volatile int i = 0; i < 16; ++i);
+
+  modifyreg32(TIVA_SYSCON_SRCAN, SYSCON_SRCAN(modnum), 0);
+
+  /* Wait for peripheral-ready signal */
+
+  while (!(getreg32(TIVA_SYSCON_PRCAN) & (1 << modnum)));
+}
+
+/****************************************************************************
+ * Name: tivacan_setup
+ *
+ * Description:
+ *   Set up the CAN module. Register the ISR and enable the interrupt in the
+ *   NVIC. Set default bit-timing and set filters to a consistent state. On
+ *   return, the INIT bit is cleared in the CANCTL register, and interrupts
+ *   are enabled on the side of the CAN module. (can_ops_s.txint and
+ *   can_ops_s.rxint are unsuitable because the Tiva CAN modules do not allow
+ *   disabling TX and RX interrupts separately).
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure
+ *
+ * Returned value:
+ *   Zero on success, or a negated errno on failure.
+ ****************************************************************************/
+
+static int tivacan_setup(FAR struct can_dev_s *dev)
+{
+  uint32_t  irq;
+  int       ret;
+  uint32_t  reg;
+  FAR struct tiva_canmod_s    *canmod = dev->cd_priv;
+  char     *kthd_argv[2];
+  kthd_argv[1] = NULL;
+
+  ret = nxsem_init(&canmod->rxsem, 0, 0);
+
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  switch (canmod->modnum)
+    {
+#ifdef CONFIG_TIVA_CAN0
+      case 0:
+        {
+          kthd_argv[0] = "0";
+          ret = kthread_create("Tiva CAN0",
+                               CONFIG_TIVA_CAN0_PRIO,
+                               TIVA_CAN_KTHREAD_STACKSIZE,
+                               tivacan_rxhandler,
+                               kthd_argv);
+        }
+      break;
+#endif
+#ifdef CONFIG_TIVA_CAN1
+      case 1:
+        {
+          kthd_argv[0] = "1";
+          ret = kthread_create("Tiva CAN1",
+                               CONFIG_TIVA_CAN1_PRIO,
+                               TIVA_CAN_KTHREAD_STACKSIZE,
+                               tivacan_rxhandler,
+                               kthd_argv);
+        }
+      break;
+#endif
+      default:
+        return -EINVAL;
+    }
+
+  if (ret < 0)
+    {
+      canerr("tiva_can: failed to create kthread\n");
+      return ret;
+    }
+  else
+    {
+      canmod->kthd_id = ret;
+    }
+
+#ifdef CONFIG_CAN_EXTID
+  struct canioc_extfilter_s default_filter =
+  {
+    .xf_id1   = 0x123,
+    .xf_id2   = 0,
+    .xf_type  = CAN_FILTER_MASK,
+    .xf_prio  = CAN_MSGPRIO_HIGH,
+  };
+#else
+  struct canioc_stdfilter_s default_filter =
+  {
+    .sf_id1   = 0x123,
+    .sf_id2   = 0,
+    .sf_type  = CAN_FILTER_MASK,
+    .sf_prio  = CAN_MSGPRIO_HIGH,
+  };
+#endif
+
+  /* REVISIT: This will not necessarily come out nicely on boards with
+   * different crystals.
+   */
+
+  struct canioc_bittiming_s default_timing =
+  {
+    .bt_baud  = 1000000,
+    .bt_tseg1    = 6,
+    .bt_tseg2    = 3,
+    .bt_sjw      = 3,
+  };
+
+  /* Clear the status interrupt, just in case */
+
+  getreg32(canmod->base + TIVA_CAN_OFFSET_STS);
+
+  /* Set default bit timing */
+
+  tivacan_ioctl(dev, CANIOC_SET_BITTIMING, (unsigned long)&default_timing);
+
+  nxmutex_lock(&canmod->thd_iface_mtx);
+
+  /* Ensure a consistent state */
+
+  for (int i = 0; i < TIVA_CAN_NUM_MBOXES; ++i)
+    {
+      tivacan_disable_mbox(dev, canmod->thd_iface_base, i);
+    }
+
+  /* Ensure the TX mailboxes have filtering enabled. Since higher mailbox
+   * numbers are lower-priority for receiving messages, this is might not be
+   * necessary - if the message matches an existing filter FIFO, the hardware
+   * _shouldn't_ let it "fall through" to the next mailbox if the
+   * End of Buffer bit is set (which tivacan_initfilter guarantees). However,
+   * the datasheet does not explicitly guarantee this.
+   *
+   * The other case to be considered is if the incoming message doesn't match
+   * any of the mailboxes in the existing filter FIFOs. In this case it could
+   * fall through to the TX mailbox. The datasheet doesn't specify what
+   * happens if an incoming message matches a mailbox with TXRQST set. It
+   * is possible that the message would overwrite the contents of the TX
+   * mailbox, which would be very bad.
+   *
+   * There are two parts to the filtering rules:
+   *  - MDIR: filter-on-direction. This prevents received messages (of any
+   *          type - RTR or normal data messages) from matching the TX
+   *          mailbox whenever we're transmitting a data frame
+   *          (i.e. ARB2.DIR = 1)
+   *
+   *  - ID filtering: Since MDIR does not protect the TX mailbox when a
+   *                  remote request frame is being transmitted
+   *                  (ARB2.DIR = 0), we require an exact match of the ID
+   *                  and ID type (i.e. standard/extended) to limit the
+   *                  likelihood of the remote request being corrupted before
+   *                  transmission. Since remote requests contain no data,
+   *                  the only thing that would be changed by a received
+   *                  message would be the DLC.
+   *
+   * The MCTL.UMASK bit is set in tivacan_send.
+   */
+
+  for (int i = TIVA_CAN_TX_FIFO_START; i < TIVA_CAN_NUM_MBOXES; ++i)
+    {
+      reg = TIVA_CANIF_MSK1_IDMSK_EXT_MASK;
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_MSK1);
+      reg = TIVA_CANIF_MSK2_IDMSK_EXT_MASK
+            | TIVA_CANIF_MSK2_MDIR
+            | TIVA_CANIF_MSK2_MXTD;
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_MSK2);
+
+      putreg32(TIVA_CANIF_CMSK_MASK,
+              canmod->thd_iface_base + TIVA_CANIF_OFFSET_CMSK);
+
+      /* One-indexed */
+
+      putreg32(i + 1, canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ);
+
+      /* Wait for request to process */
+
+      while (getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ)
+              & TIVA_CANIF_CRQ_BUSY);
+    }
+
+  nxmutex_unlock(&canmod->thd_iface_mtx);
+
+  /* Register the ISR */
+
+  switch (canmod->modnum)
+    {
+      case 0:
+        {
+          irq = TIVA_IRQ_CAN0;
+        }
+        break;
+      case 1:
+        {
+          irq = TIVA_IRQ_CAN1;
+        }
+        break;
+      default:
+        {
+          canerr("ERROR: no such CAN module\n");
+          return -ENODEV;
+        }
+    }
+
+  ret = irq_attach(irq, tivacan_isr, dev);
+  if (ret < 0)
+    {
+      canerr("ERROR: Failed to register CAN ISR.\n");
+      return ret;
+    }
+
+  up_enable_irq(irq);
+
+  /* Exit init mode and enable interrupts from the CAN module */
+
+  modifyreg32(canmod->base + TIVA_CAN_OFFSET_CTL, TIVA_CAN_CTL_INIT,
+              TIVA_CAN_CTL_EIE | TIVA_CAN_CTL_IE);
+
+#ifdef CONFIG_CAN_ERRORS
+  /* Status interrupts are used for reporting lost arbitration, etc. */
+
+  modifyreg32(canmod->base + TIVA_CAN_OFFSET_CTL, 0, TIVA_CAN_CTL_SIE);
+#endif
+
+  /* The TX FIFO is manually allocated first because it must be located at
+   * the end of the series of mailboxes to make sure responses to remote
+   * request frames go to the correct place.
+   */
+
+  canmod->fifos[CONFIG_TIVA_CAN_FILTERS_MAX] =
+                          0xffffffff << (32 - CONFIG_TIVA_CAN_TX_FIFO_DEPTH);
+  canmod->tx_fifo = &canmod->fifos[CONFIG_TIVA_CAN_FILTERS_MAX];
+
+  ret = tivacan_alloc_fifo(dev, CONFIG_TIVA_CAN_DEFAULT_FIFO_DEPTH);
+  if (ret < 0)
+    {
+      canerr("ERROR: Failed to allocate default RX FIFO.\n");
+      return ret;
+    }
+  else
+    {
+      canmod->rxdefault_fifo = &canmod->fifos[ret];
+    }
+
+#ifdef CONFIG_CAN_EXTID
+  tivacan_initfilter(dev,
+                     canmod->rxdefault_fifo,
+                     &default_filter,
+                     TIVA_CAN_FILTER_TYPE_EXT);
+#else
+  tivacan_initfilter(dev,
+                     canmod->rxdefault_fifo,
+                     &default_filter,
+                     TIVA_CAN_FILTER_TYPE_STD);
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: tivacan_shutdown
+ *
+ * Description:
+ *   Shut down the CAN module. This function does the reverse of
+ *   tivacan_setup. The ISR is disabled in the NVIC, unregistered from the
+ *   NuttX dispatcher, and the CAN module is reset.
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure
+ *
+ * Returned value: None
+ ****************************************************************************/
+
+static void tivacan_shutdown(FAR struct can_dev_s *dev)
+{
+  int irq;
+  FAR struct tiva_canmod_s    *canmod = dev->cd_priv;
+
+  switch (canmod->modnum)
+    {
+      case 0:
+        {
+          irq = TIVA_IRQ_CAN0;
+        }
+        break;
+      case 1:
+        {
+          irq = TIVA_IRQ_CAN1;
+        }
+        break;
+      default:
+        canerr("ERROR: No such CAN module to disable interrupt\n");
+    }
+
+  /* Free all allocated RX filter FIFOs */
+
+  for (int i = 0; i < CONFIG_TIVA_CAN_FILTERS_MAX; ++i)
+  {
+    if (canmod->fifos[i])
+    {
+      tivacan_free_fifo(dev, &canmod->fifos[i]);
+    }
+  }
+
+  /* Free the TX FIFO */
+
+  tivacan_free_fifo(dev, canmod->tx_fifo);
+
+  /* Disable interrupts from the CAN module */
+
+  modifyreg32(canmod->base + TIVA_CAN_OFFSET_CTL,
+              TIVA_CAN_CTL_EIE | TIVA_CAN_CTL_SIE | TIVA_CAN_CTL_IE, 0);
+
+  up_disable_irq(irq);
+  irq_detach(irq);
+
+  /* Stop processing messages */
+
+  kthread_delete(canmod->kthd_id);
+
+  tivacan_reset(dev);
+}
+
+/****************************************************************************
+ * Name: tivacan_rxhandler
+ *
+ * Description: Kthread RX message handler.
+ ****************************************************************************/
+
+int tivacan_rxhandler(int argc, char** argv)
+{
+  FAR struct can_dev_s     *dev;
+  FAR struct tiva_canmod_s *canmod;
+  struct can_msg_s msg;
+
+#ifdef CONFIG_CAN_ERRORS
+  int      ret;
+#endif
+
+  /* argv[0] contains the thread name */
+
+  switch (argv[1][0])
+    {
+#ifdef CONFIG_TIVA_CAN0
+      case '0':
+        {
+          dev = &g_tivacan0dev;
+        }
+        break;
+#endif
+#ifdef CONFIG_TIVA_CAN1
+      case '1':
+        {
+          dev = &g_tivacan1dev;
+        }
+        break;
+#endif
+      default:
+        canerr("Incorrect Tiva CAN module passed to kthread.\n");
+        return -EINVAL;
+    }
+
+  canmod = dev->cd_priv;
+  int     *mbox = &canmod->kthd_cur_mbox;
+
+  /* Must clear the garbage */
+
+  memset(&msg, 0, sizeof(struct can_msg_s));
+
+  while (true)
+    {
+      nxsem_wait(&canmod->rxsem);
+      nxmutex_lock(&canmod->thd_iface_mtx);
+
+      /* Process the received message(s). Since hardware RX FIFOS are used
+       * and new messages are received into the mailbox with the lowest
+       * number, we can't use CANINT since a new message might arrive in a
+       * mailbox we'd already read from before the higher-address mailboxes
+       * in the FIFO were emptied. (Besides, the ISR clears CANINT).
+       */
+
+      for (*mbox = 0;
+           *mbox < TIVA_CAN_NUM_MBOXES - CONFIG_TIVA_CAN_TX_FIFO_DEPTH;
+          ++*mbox)
+        {
+          uint32_t reg;
+
+          /* New Data registers */
+
+          reg = tivacan_get_nwda32(canmod->base);
+
+          if (!(reg & ~*(canmod->tx_fifo)))
+            {
+              /* No new messages to process in the RX FIFOs */
+
+              break;
+            }
+          else if (! (reg & 1 << *mbox))
+            {
+              /* No new message in this mailbox */
+
+              continue;
+            }
+
+          /* Command Mask register
+           * Pull arbitration bits, control bits, and data bits from the
+           * message SRAM. Clear the new data bit; the ISR cleared the IRQ.
+           */
+
+          reg =   TIVA_CANIF_CMSK_ARB       | TIVA_CANIF_CMSK_CONTROL
+                | TIVA_CANIF_CMSK_NEWDAT    | TIVA_CANIF_CMSK_DATAA
+                | TIVA_CANIF_CMSK_DATAB;
+          putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_CMSK);
+
+          /* Submit request (1-indexed) */
+
+          putreg32(*mbox + 1,
+                   canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ);
+
+          /* Wait for response */
+
+          while (getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ)
+                                                    & TIVA_CANIF_CRQ_BUSY);
+
+          /* ARB2 - upper chunk of the message ID, "direction," and extended
+           * message indication. (NOTE: sadly, DIR is always 0 (receive) even
+           * for RTR messages. There is no way to determine whether the RTR
+           * bit was set on a received frame.
+           */
+
+          reg = getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB2);
+
+#ifdef CONFIG_CAN_EXTID
+          msg.cm_hdr.ch_extid = (0 != (reg & TIVA_CANIF_ARB2_XTD));
+
+          if (msg.cm_hdr.ch_extid)
+            {
+              msg.cm_hdr.ch_id = (reg & TIVA_CANIF_ARB2_ID_EXT_MASK)
+                                      << TIVA_CANIF_ARB2_ID_EXT_PRESHIFT;
+
+              /* ARB1 - lower chunk of the message ID for extended IDs */
+
+              reg = getreg32(
+                      canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB1);
+
+              msg.cm_hdr.ch_id |= reg & TIVA_CANIF_ARB1_ID_EXT_MASK;
+            }
+          else
+            {
+              msg.cm_hdr.ch_id = (reg & TIVA_CANIF_ARB2_ID_STD_MASK)
+                                        >> TIVA_CANIF_ARB2_ID_STD_SHIFT;
+            }
+#else
+          msg.cm_hdr.ch_id = (reg & TIVA_CANIF_ARB2_ID_STD_MASK)
+                                    >> TIVA_CANIF_ARB2_ID_STD_SHIFT;
+#endif /* CONFIG_CAN_EXTID */
+
+          /* Message Control - remote enable (RMTEN) & data length code */
+
+          reg = getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_MCTL);
+          msg.cm_hdr.ch_dlc = reg & TIVA_CANIF_MCTL_DLC_MASK;
+
+          /* Data registers - these are little endian but the uint8_t
+           * array in can_msg_s is big endian.
+           */
+
+          for (int j = 0; j < (msg.cm_hdr.ch_dlc + 1) / 2; ++j)
+            {
+              reg = getreg32(canmod->thd_iface_base
+                              + TIVA_CANIF_OFFSET_DATA(j));
+
+              msg.cm_data[j * 2]      = (reg & TIVA_CANIF_DATA_HBYTE_MASK)
+                                        >> TIVA_CANIF_DATA_HBYTE_SHIFT;
+              msg.cm_data[j * 2 + 1]  = (reg & TIVA_CANIF_DATA_LBYTE_MASK)
+                                        >> TIVA_CANIF_DATA_LBYTE_SHIFT;
+            }
+
+#ifdef CONFIG_CAN_ERRORS
+          ret = can_receive(dev, &msg.cm_hdr, msg.cm_data);
+#else
+          can_receive(dev, &msg.cm_hdr, msg.cm_data);
+#endif
+        }
+
+        nxmutex_unlock(&canmod->thd_iface_mtx);
+
+#ifdef CONFIG_CAN_ERRORS
+        if (ret == OK)
+        {
+          /* Switch to processing errors by interrupt when the application
+           * is able to keep up with the message volume and messages are
+           * being received successfully.
+           */
+
+          work_cancel(LPWORK, &canmod->error_work);
+          modifyreg32(canmod->base + TIVA_CAN_OFFSET_CTL,
+                      0, TIVA_CAN_CTL_SIE);
+        }
+#endif
+    }
+
+    return OK;
+}
+
+/****************************************************************************
+ * Name: tivacan_rxintctl
+ *
+ * Description:
+ *   The upper-half driver does not make meaningful use of this function,
+ *   so there is no need to implement it. In theory it might be used so that
+ *   messages are not received before the upper-half software FIFOs are
+ *   initialized, but that code executes in a critical section so it
+ *   doesn't make any difference anyway. And continuing to receive messages
+ *   while the TX FIFO drains is not a problem.
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure
+ *   enable - True to enable the interrupt, or false to disable
+ *
+ * Returned value: None
+ ****************************************************************************/
+
+static void tivacan_rxintctl(FAR struct can_dev_s *dev, bool enable)
+{
+  return;
+}
+
+/****************************************************************************
+ * Name tivacan_txintctl
+ *
+ * Description:
+ *   The upper-half driver does not make meaningful use of this function
+ *   (at least when CONFIG_CAN_TXREADY is enabled), so there is no need to
+ *   implement it.
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure
+ *   enable - True to enable the interrupt, or false to disable
+ *
+ * Returned value: None
+ ****************************************************************************/
+
+static void tivacan_txintctl(FAR struct can_dev_s *dev, bool enable)
+{
+  return;
+}
+
+/****************************************************************************
+ * Name: tivacan_ioctl
+ *
+ * Description:
+ *   Perform the action requested by an ioctl syscall that was not handled by
+ *   the "upper half" CAN driver.
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure
+ *   cmd - One of the CANIOC_* ioctls
+ *   arg - Second argument to the ioctl call.
+ *
+ * Returned value:
+ *   Zero on success; a negated errno on failure
+ ****************************************************************************/
+
+static int tivacan_ioctl(FAR struct can_dev_s *dev, int cmd,
+                         unsigned long arg)
+{
+  FAR struct tiva_canmod_s *canmod = dev->cd_priv;
+  int ret;
+
+  switch (cmd)
+    {
+      case CANIOC_GET_BITTIMING:
+      {
+        FAR struct canioc_bittiming_s *bt =
+          (FAR struct canioc_bittiming_s *)arg;
+        struct tiva_can_timing_s timing;
+
+        timing = tivacan_bittiming_get(dev);
+        bt->bt_tseg1 = timing.tseg1;
+        bt->bt_tseg2 = timing.tseg2;
+        bt->bt_sjw   = timing.sjw;
+        bt->bt_baud  = (SYSCLK_FREQUENCY / (unsigned long)timing.prescaler)
+                        / (timing.tseg1 + timing.tseg2 + 1);
+        ret = OK;
+      }
+      break;
+
+      case CANIOC_SET_BITTIMING:
+        {
+          /* REVIST: This is VERY crude--there's no guarantees that rounding
+           * won't cause problems with the prescaler selection.
+           * However, NuttX's interface here is kinda sub-optimal anyway--
+           * the choice of TSEG1, TSEG2, and SJW depend on the system clock
+           * and the prescaler value, so there should be an ioctl for
+           * the driver to take physical parameters like delay time and
+           * crystal frequency tolerances into account and calculate the
+           * timing parameters automagically... This could be implemented
+           * in the upper half driver.
+           */
+
+          FAR const struct canioc_bittiming_s *bt =
+            (FAR struct canioc_bittiming_s *)arg;
+          struct tiva_can_timing_s timing;
+
+          timing.tseg1 = bt->bt_tseg1;
+          timing.tseg2 = bt->bt_tseg2;
+          timing.sjw   = bt->bt_sjw;
+          timing.prescaler = SYSCLK_FREQUENCY
+            / (bt->bt_baud * (bt->bt_tseg1 + bt->bt_tseg2 + 1));
+          DEBUGASSERT(timing.tseg1 <= 16 && timing.tseg1 >= 1);
+          DEBUGASSERT(timing.tseg2 <= 8 && timing.tseg1 >= 1);
+          DEBUGASSERT(timing.sjw <= 4 && timing.sjw >= 1);
+          DEBUGASSERT(timing.prescaler <= 1024 && timing.prescaler >= 1);
+
+          tivacan_bittiming_set(dev, &timing);
+          ret = OK;
+        }
+        break;
+
+      case CANIOC_GET_CONNMODES:
+        {
+          FAR struct canioc_connmodes_s *modes =
+                                        (FAR struct canioc_connmodes_s *)arg;
+          uint32_t reg = getreg32(canmod->base + TIVA_CAN_OFFSET_CTL);
+
+          if (reg & TIVA_CAN_CTL_TEST)
+            {
+              reg = getreg32(canmod->base + TIVA_CAN_OFFSET_TST);
+              modes->bm_loopback = reg & TIVA_CAN_TST_LBACK;
+              modes->bm_silent   = reg & TIVA_CAN_TST_SILENT;
+            }
+          else
+            {
+              modes->bm_loopback = false;
+              modes->bm_silent   = false;
+            }
+
+          ret = OK;
+        }
+        break;
+
+      case CANIOC_SET_CONNMODES:
+        {
+          uint32_t reg;
+          FAR struct canioc_connmodes_s *modes =
+                                        (FAR struct canioc_connmodes_s *)arg;
+          irqstate_t flags = enter_critical_section();
+
+          if (modes->bm_loopback || modes->bm_silent)
+            {
+              modreg32(TIVA_CAN_CTL_TEST, 0,
+                      canmod->base + TIVA_CAN_OFFSET_CTL);
+
+              reg = getreg32(canmod->base + TIVA_CAN_OFFSET_TST);
+
+              if (modes->bm_loopback)
+                {
+                  reg |= TIVA_CAN_TST_LBACK;
+                }
+
+              if (modes->bm_silent)
+                {
+                  reg |= TIVA_CAN_TST_SILENT;
+                }
+
+              putreg32(reg, canmod->base + TIVA_CAN_OFFSET_TST);
+            }
+          else
+            {
+              modreg32(0, TIVA_CAN_CTL_TEST,
+                      canmod->base + TIVA_CAN_OFFSET_CTL);
+            }
+
+          leave_critical_section(flags);
+
+          ret = OK;
+        }
+        break;
+
+      case CANIOC_ADD_EXTFILTER:
+      case CANIOC_ADD_STDFILTER:
+        {
+          int fifo_idx = tivacan_alloc_fifo(dev,
+                                        CONFIG_TIVA_CAN_DEFAULT_FIFO_DEPTH);
+
+          if (fifo_idx < 0)
+            {
+              /* Probably -ENOSPC or -ENOMEM */
+
+              ret = fifo_idx;
+              break;
+            }
+
+          if (cmd == CANIOC_ADD_EXTFILTER)
+            {
+              tivacan_initfilter(dev, &canmod->fifos[fifo_idx],
+                                  (FAR void *)arg, TIVA_CAN_FILTER_TYPE_EXT);
+            }
+          else
+            {
+              tivacan_initfilter(dev, &canmod->fifos[fifo_idx],
+                                  (FAR void *)arg, TIVA_CAN_FILTER_TYPE_STD);
+            }
+
+          if (canmod->rxdefault_fifo != NULL)
+            {
+              tivacan_free_fifo(dev, canmod->rxdefault_fifo);
+              canmod->rxdefault_fifo = NULL;
+            }
+
+          ret = fifo_idx;
+        }
+        break;
+
+      case CANIOC_DEL_EXTFILTER:
+      case CANIOC_DEL_STDFILTER:
+        {
+          /* RX default fifo pointer removed when a filter is added. Do not
+           * allow filters to be "removed" before they're added. Also do not
+           * allow unusued filter FIFOs to be added.
+           */
+
+          if (arg < 0
+                || arg >= CONFIG_TIVA_CAN_FILTERS_MAX
+                || canmod->rxdefault_fifo != NULL
+                || canmod->fifos[arg] == 0
+                || arg == (unsigned long)canmod->tx_fifo
+            )
+            {
+              ret = -EINVAL;
+            }
+          else
+            {
+              tivacan_free_fifo(dev, &canmod->fifos[arg]);
+              ret = OK;
+            }
+        }
+        break;
+
+      case CANIOC_BUSOFF_RECOVERY:
+        {
+          uint32_t init = getreg32(canmod->base + TIVA_CAN_OFFSET_CTL)
+                          & TIVA_CAN_CTL_INIT;
+          uint32_t boff = canmod->status & TIVA_CAN_STS_BOFF;
+
+          if (!boff && !init)
+            {
+              caninfo("The CAN module is not in bus-off mode.\n");
+              ret = -ENMFILE;
+            }
+          else if (!boff && init)
+            {
+              canwarn("The CAN module is in INIT mode but not bus-off! "
+                      "Exiting init mode.\n");
+              modifyreg32(canmod->base + TIVA_CAN_OFFSET_CTL,
+                          TIVA_CAN_CTL_INIT, 0);
+              ret = -EIO;
+            }
+          else if (boff && !init)
+            {
+              caninfo("Already recovering from bus-off.\n");
+              ret = -EINPROGRESS;
+            }
+          else
+            {
+              modifyreg32(canmod->base + TIVA_CAN_OFFSET_CTL,
+                          TIVA_CAN_CTL_INIT, 0);
+              ret = OK;
+            }
+        }
+        break;
+
+      case CANIOC_SET_NART:
+        {
+          modifyreg32(canmod->base + TIVA_CAN_OFFSET_CTL,
+                      0, TIVA_CAN_CTL_DAR);
+          ret = OK;
+        }
+        break;
+
+      case CANIOC_SET_ABOM:
+        {
+          canmod->autorecover = true;
+          ret = OK;
+        }
+        break;
+      default:
+        canerr("ERROR: Unrecognized ioctl\n");
+        ret = -ENOSYS;
+        break;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: tivacan_send
+ *
+ * Description:
+ *   Enqueue a message to transmit into the hardware FIFO. Returns
+ *   immediately if the FIFO is full. This function must not fail when
+ *   tivacan_txready returns true, or the message will be lost.
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure
+ *   msg - The CAN message to enqueue
+ *
+ * Returned value:
+ *   Zero on success; a negated errorno on failure.
+ ****************************************************************************/
+
+static int tivacan_send(FAR struct can_dev_s *dev, FAR struct can_msg_s *msg)
+{
+  FAR struct tiva_canmod_s *canmod = dev->cd_priv;
+  uint32_t reg = 0;
+
+  /* REVISIT: Should can_txdone be called to advance the FIFO buffer
+   * head when dev_send is called when it shouldn't be?
+   */
+
+  if (canmod->status & TIVA_CAN_STS_BOFF)
+    {
+      can_txdone(dev);
+      return -ENETDOWN;
+    }
+
+  if (canmod->tx_fifo_tail >= CONFIG_TIVA_CAN_TX_FIFO_DEPTH)
+    {
+      can_txdone(dev);
+      return -EBUSY;
+    }
+
+  nxmutex_lock(&canmod->thd_iface_mtx);
+
+  /* Protect the message object due the minute chance that the mailbox was
+   * previously used for a remote frame and could receive messages, causing
+   * an SRAM conflict.
+   */
+
+  tivacan_disable_mbox(dev, canmod->thd_iface_base,
+                       TIVA_CAN_TX_FIFO_START + canmod->tx_fifo_tail);
+
+  /* Command mask register */
+
+  reg = TIVA_CANIF_CMSK_WRNRD | TIVA_CANIF_CMSK_ARB | TIVA_CANIF_CMSK_CONTROL
+        | TIVA_CANIF_CMSK_CLRINTPND
+        | TIVA_CANIF_CMSK_DATAA | TIVA_CANIF_CMSK_DATAB;
+  putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_CMSK);
+
+  /* Arbitration registers (ARB1 and ARB2) */
+
+#ifdef CONFIG_CAN_EXTID
+  if (msg->cm_hdr.ch_extid)
+    {
+      reg = msg->cm_hdr.ch_id & TIVA_CANIF_ARB1_ID_EXT_MASK;
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB1);
+
+      reg = (msg->cm_hdr.ch_id >> TIVA_CANIF_ARB2_ID_EXT_PRESHIFT)
+            & TIVA_CANIF_ARB2_ID_EXT_MASK;
+      reg |= TIVA_CANIF_ARB2_MSGVAL | TIVA_CANIF_ARB2_XTD;
+
+    #ifdef CONFIG_CAN_USE_RTR
+      reg |= (msg->cm_hdr.ch_rtr ? 0 : TIVA_CANIF_ARB2_DIR);
+    #else
+      reg |= TIVA_CANIF_ARB2_DIR;
+    #endif
+
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB2);
+    }
+  else
+    {
+#endif /* CONFIG_CAN_EXTID */
+      reg = (msg->cm_hdr.ch_id << TIVA_CANIF_ARB2_ID_STD_SHIFT)
+            & TIVA_CANIF_ARB2_ID_STD_MASK;
+
+      reg |= TIVA_CANIF_ARB2_MSGVAL;
+
+    #ifdef CONFIG_CAN_USE_RTR
+      reg |= (msg->cm_hdr.ch_rtr ? 0 : TIVA_CANIF_ARB2_DIR);
+    #else
+      reg |= TIVA_CANIF_ARB2_DIR;
+    #endif
+
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_ARB2);
+#ifdef CONFIG_CAN_EXTID
+    }
+#endif
+
+  /* Message Control (MCTL) register */
+
+  reg = msg->cm_hdr.ch_dlc & TIVA_CANIF_MCTL_DLC_MASK;
+
+  /* Protect the mailbox from rx messages; see comment in tivacan_setup */
+
+  reg |= TIVA_CANIF_MCTL_UMASK;
+
+  /* NOTE: Not sure whether the End Of Buffer bit means anything here */
+
+  reg |= TIVA_CANIF_MCTL_NEWDAT | TIVA_CANIF_MCTL_TXIE
+          | TIVA_CANIF_MCTL_TXRQST | TIVA_CANIF_MCTL_EOB;
+  putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_MCTL);
+
+  /* Data registers, there are four of them. */
+
+  for (int i = 0; i < (msg->cm_hdr.ch_dlc + 1) / 2; ++i)
+    {
+      reg = (uint32_t)msg->cm_data[i * 2]
+              << TIVA_CANIF_DATA_HBYTE_SHIFT;
+      reg |= (uint32_t)msg->cm_data[i * 2 + 1]
+              << TIVA_CANIF_DATA_LBYTE_SHIFT;
+
+      putreg32(reg, canmod->thd_iface_base + TIVA_CANIF_OFFSET_DATA(i));
+    }
+
+  /* Submit request; this register is one-indexed. */
+
+  putreg32(TIVA_CAN_TX_FIFO_START + canmod->tx_fifo_tail + 1,
+           canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ);
+
+  /* Wait for completion */
+
+  while (getreg32(canmod->thd_iface_base + TIVA_CANIF_OFFSET_CRQ)
+          & TIVA_CANIF_CRQ_BUSY);
+
+  nxmutex_unlock(&canmod->thd_iface_mtx);
+
+  /* Move to the next message in the h/w TX FIFO.
+   * Tell the upper-half the message has been submitted... this recurses
+   * back on us until the software TX FIFO is empty.
+   */
+
+  ++canmod->tx_fifo_tail;
+  can_txdone(dev);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: tivacan_txready
+ *
+ * Description:
+ *   Determine whether the hardware is ready to transmit another TX message.
+ *   This checks for bus-off conditions as well as if the FIFO is full.
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure
+ *
+ * Returned value:
+ *   True if the hardware can accept another TX message, false otherwise.
+ ****************************************************************************/
+
+static bool tivacan_txready(FAR struct can_dev_s *dev)
+{
+  FAR struct tiva_canmod_s *canmod = dev->cd_priv;
+
+  if (canmod->status & TIVA_CAN_STS_BOFF)
+    {
+      return false;
+    }
+
+  return (canmod->tx_fifo_tail < CONFIG_TIVA_CAN_TX_FIFO_DEPTH);
+}
+
+/****************************************************************************
+ * Name: tivacan_txempty
+ *
+ * Description:
+ *   Determines whether the TX FIFO is empty. Because the CAN_TXREADY
+ *   interface is used, this function has a special connotation: it indicates
+ *   whether can_txready is going to be called in the future to start the
+ *   work queue that feeds the TX hardware FIFO.
+ *
+ *   A pointer to this function is passed to can_register.
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure
+ *
+ * Returned value:
+ *   True if the FIFO is empty, false otherwise.
+ ****************************************************************************/
+
+static bool tivacan_txempty(FAR struct can_dev_s *dev)
+{
+  FAR struct tiva_canmod_s *canmod = dev->cd_priv;
+
+  return (canmod->tx_fifo_tail == 0);
+}
+
+/****************************************************************************
+ * Name: tivacan_handle_errors_wqueue
+ *
+ * Description:
+ *   Periodically handle errors when status interrupts are disabled due to
+ *   overload. Only applicable when CAN error reporting is enabled.
+ *
+ *   Since error messages are processed directly in the ISR, it is easy to
+ *   overwhelm the application with error messages. If tivacan_handle_errors
+ *   fails to send an error message in the ISR, status interrupts are
+ *   disabled and the ISR adds this function to the work queue.
+ *
+ *   On the other hand, when a message is successfully sent, or successfully
+ *   passes through the filters and is received by the application, the ISR
+ *   or the RX kthread will call work_cancel and re-enable status interrupts
+ *   if they are disabled.
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure (typed as
+ *         void* for compatibility with worker_t.
+ ****************************************************************************/
+
+#ifdef CONFIG_CAN_ERRORS
+void tivacan_handle_errors_wqueue(FAR void * dev)
+{
+  irqstate_t flags;
+  FAR struct tiva_canmod_s *canmod = ((FAR struct can_dev_s *)dev)->cd_priv;
+
+  flags = enter_critical_section();
+  tivacan_handle_errors((FAR struct can_dev_s *)dev);
+  work_queue(LPWORK,
+             &canmod->error_work,
+             tivacan_handle_errors_wqueue,
+             dev,
+             TIVA_CAN_ERR_HANDLER_TICKS);
+  leave_critical_section(flags);
+}
+#endif /* CONFIG_CAN_ERRORS */
+
+/****************************************************************************
+ * Name: tivacan_handle_errors(FAR struct can_dev_s *dev)
+ *
+ * Description:
+ *   Process everything in the status register; i.e. initiate bus-off
+ *   recovery and send error messages if desired.  This function may be
+ *   called from either the main ISR or (when error messages are desired but
+ *   the application is unable to handle the volume of errors) from the
+ *   high-priority work queue. However, interrupts must be disabled when
+ *   called from the work queue context to ensure that error messages are
+ *   sent to the application in the correct order.
+ *
+ *   An IRQ is always raised for bus-off and error counter "warning" level,
+ *   so this function may be executed from the ISR while still waiting in
+ *   the work queue. In this case, the call to work_queue in the ISR
+ *   will just reset the timer, which is fine.
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure
+ *
+ * Returned value:
+ *   1 if an error message was successfully sent to the application, the
+ *   return value of can_receive() (a negated errno) if it failed, and
+ *   zero if no error message was needed.
+ ****************************************************************************/
+
+int tivacan_handle_errors(FAR struct can_dev_s *dev)
+{
+  uint32_t cansts;
+  uint32_t canerr;
+  FAR struct tiva_canmod_s *canmod = dev->cd_priv;
+  int      ret = 0;
+
+#ifdef CONFIG_CAN_ERRORS
+  int      errcnt;
+  int      prev_errcnt;
+  struct can_msg_s msg;
+
+  memset(&msg, 0, sizeof(struct can_msg_s));
+  msg.cm_hdr.ch_error = true;
+  msg.cm_hdr.ch_dlc   = CAN_ERROR_DLC;
+#endif
+
+  /* Clear the status interrupt by reading CANSTS */
+
+  cansts = getreg32(canmod->base + TIVA_CAN_OFFSET_STS);
+
+  /* Get the contents of the error counter register */
+
+  canerr = getreg32(canmod->base + TIVA_CAN_OFFSET_ERR);
+
+  /* Bus-off handling *******************************************************/
+
+  /* This is a new bus-off event. Proceed with error processing (if error
+   * reporting is enabled) so that the cause of the "last straw" error is
+   * included with the bus-off error message sent to the app.
+   * Clear the INIT bit if autorecovery is enabled.
+   */
+
+  if (cansts & TIVA_CAN_STS_BOFF
+      && ! (canmod->status & TIVA_CAN_STS_BOFF))
+    {
+      if (canmod->autorecover)
+        {
+          modreg32(0, TIVA_CAN_CTL_INIT,
+                    canmod->base + TIVA_CAN_OFFSET_CTL);
+        }
+
+#ifdef CONFIG_CAN_ERRORS
+      msg.cm_hdr.ch_id = CAN_ERROR_BUSOFF;
+#endif
+    }
+#ifdef CONFIG_CAN_ERRORS
+
+  /* If this an existing bus-off event and the module is recovering, don't do
+   * anything. The interrupt is caused by the Last Error Code being updated
+   * with a bit-0 error to indicate that the module is recovering.
+   */
+
+  else if (cansts & TIVA_CAN_STS_BOFF
+            && canmod->status & TIVA_CAN_STS_BOFF)
+    {
+      goto save_regs_and_return;
+    }
+
+  /* The module has just finished recovering from bus-off; indicate this to
+   * the app. If the LEC is a BIT0 error it must be ignored (see below).
+   * We still check for other errors though because if we're running from the
+   * work queue, the module could have recovered and then immediately
+   * encountered another error without us being notified in between.
+   */
+
+  else if (! (cansts & TIVA_CAN_STS_BOFF)
+              && canmod->status & TIVA_CAN_STS_BOFF)
+    {
+      msg.cm_hdr.ch_id = CAN_ERROR_RESTARTED;
+    }
+
+  /* Lost arbitration detection *********************************************/
+
+  if (tivacan_get_txrq32(canmod->base) & ~tivacan_get_nwda32(canmod->base)
+      && cansts & TIVA_CAN_STS_RXOK)
+    {
+      /* Received a message successfually but sending a message failed */
+
+      msg.cm_hdr.ch_id |= CAN_ERROR_LOSTARB;
+    }
+
+  /* Electrical fault/framing errors ****************************************/
+
+  /* This is not a bus-off event, or it is a brand-new bus-off event
+   * that we need to indicate the cause of the error for.
+   */
+
+  switch (canmod->status & TIVA_CAN_STS_LEC_MASK)
+    {
+      case TIVA_CAN_STS_LEC_NONE:
+
+        /* Skip unnecessary comparisons for the most common case */
+
+        break;
+
+      case TIVA_CAN_STS_LEC_STUFF:
+        {
+          msg.cm_hdr.ch_id  |= CAN_ERROR_PROTOCOL;
+          msg.cm_data[2]    = CAN_ERROR2_STUFF;
+        }
+        break;
+      case TIVA_CAN_STS_LEC_FORM:
+        {
+          msg.cm_hdr.ch_id  |= CAN_ERROR_PROTOCOL;
+          msg.cm_data[2]    = CAN_ERROR2_FORM;
+        }
+        break;
+      case TIVA_CAN_STS_LEC_ACK:
+        {
+          msg.cm_hdr.ch_id  |= CAN_ERROR_NOACK | CAN_ERROR_PROTOCOL;
+          msg.cm_data[2]    = CAN_ERROR2_TX;
+        }
+        break;
+      case TIVA_CAN_STS_LEC_BIT1:
+        {
+          msg.cm_hdr.ch_id  |=  CAN_ERROR_PROTOCOL;
+          msg.cm_data[2]     =  CAN_ERROR2_BIT | CAN_ERROR2_BIT1
+                              | CAN_ERROR2_TX;
+        }
+        break;
+      case TIVA_CAN_STS_LEC_BIT0:
+        {
+          /* Note: this error code is set every time there is a sequence
+           * of 11 recessive bits during bus-off recovery, the LEC is not
+           * checked in that case.
+           */
+
+          msg.cm_hdr.ch_id |=  CAN_ERROR_PROTOCOL;
+          msg.cm_data[2]    =  CAN_ERROR2_BIT | CAN_ERROR2_BIT0
+                              | CAN_ERROR2_TX;
+        }
+        break;
+      case TIVA_CAN_STS_LEC_CRC:
+        {
+          msg.cm_hdr.ch_id  |= CAN_ERROR_PROTOCOL;
+          msg.cm_data[2]    = CAN_ERROR2_UNSPEC;
+          msg.cm_data[3]    = CAN_ERROR3_CRCSEQ;
+        }
+        break;
+      default:
+        {
+          /* Possible other LEC values:
+          * TIVA_CAN_STS_LEC_NONE: normal conditions, including lost
+          *                        arbitration; no action required.
+          * TIVA_CAN_STS_NOEVENT:  possible if running in the work queue;
+          *                        no action required.
+          * Something else:        should never happen
+          */
+        }
+    }
+
+  /* Error counters
+   * --------------
+   * Rules for sending error messages to the application for error counters:
+   * (1) Reaching the warning or passive level on their own don't generate
+   *     error messages, but the information will be included in the error
+   *     when an actual event has occurred.
+   * (2) The application will be notified if the error goes away with an
+   *     error message of type CAN_ERROR_RESTARTED.
+   */
+
+  errcnt = (canerr & (TIVA_CAN_ERR_RP | TIVA_CAN_ERR_REC_MASK))
+            >> TIVA_CAN_ERR_REC_SHIFT;
+  prev_errcnt = (canmod->errors
+                & (TIVA_CAN_ERR_RP | TIVA_CAN_ERR_REC_MASK))
+                >> TIVA_CAN_ERR_REC_SHIFT;
+
+  if (errcnt >= 96)
+    {
+      if (errcnt >= 128)
+        {
+          msg.cm_data[1] |= CAN_ERROR1_RXPASSIVE;
+        }
+      else if (prev_errcnt >= 128)
+        {
+          msg.cm_hdr.ch_id |= CAN_ERROR_RESTARTED;
+        }
+
+      if (msg.cm_hdr.ch_id)
+        {
+          msg.cm_hdr.ch_id |= CAN_ERROR_CONTROLLER;
+          msg.cm_data[1] |= CAN_ERROR1_RXWARNING;
+        }
+    }
+  else if (prev_errcnt >= 96)
+    {
+      msg.cm_hdr.ch_id |= CAN_ERROR_RESTARTED;
+    }
+
+  errcnt = (canerr & TIVA_CAN_ERR_TEC_MASK) >> TIVA_CAN_ERR_TEC_SHIFT;
+  prev_errcnt = (canmod->errors & TIVA_CAN_ERR_TEC_MASK)
+                >> TIVA_CAN_ERR_TEC_SHIFT;
+
+  if (errcnt >= 96)
+    {
+      if (errcnt >= 128)
+        {
+          msg.cm_data[1] |= CAN_ERROR1_TXPASSIVE;
+        }
+      else if (prev_errcnt >= 128)
+        {
+          msg.cm_hdr.ch_id |= CAN_ERROR_RESTARTED;
+        }
+
+      if (msg.cm_hdr.ch_id)
+        {
+          msg.cm_hdr.ch_id |= CAN_ERROR_CONTROLLER;
+          msg.cm_data[1] |= CAN_ERROR1_TXWARNING;
+        }
+    }
+  else if (prev_errcnt >= 96)
+    {
+      msg.cm_hdr.ch_id |= CAN_ERROR_RESTARTED;
+    }
+
+  /* Forwarding an error message to the app */
+
+  if (msg.cm_hdr.ch_id)
+    {
+      /* If there was a good reason to send an error message, send it */
+
+      ret = can_receive(dev, &msg.cm_hdr, msg.cm_data);
+
+      /* Differentiate between "error message successfully sent" and
+       * "no error message required" for the caller.
+       */
+
+      if (ret >= 0)
+        {
+          ret = 1;
+        }
+    }
+
+  /* Reset the Last Error Code and TXOK/RXOK bits */
+
+  modreg32(TIVA_CAN_STS_LEC_NOEVENT,
+           TIVA_CAN_STS_LEC_MASK | TIVA_CAN_STS_TXOK | TIVA_CAN_STS_RXOK,
+           canmod->base + TIVA_CAN_OFFSET_STS);
+
+save_regs_and_return:
+
+#endif /* CONFIG_CAN_ERRORS */
+
+  /* Save contents of CANSTS and CANERR for other functions to use
+   * and for comparison next time
+   */
+
+  canmod->status = cansts;
+  canmod->errors = canerr;
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: tivacan_isr
+ *
+ * Description:
+ *   ISR executed for all interrupts generated by the CAN module.
+ *
+ *   This ISR is registered in tivacan_setup.
+ *
+ * Returned value: Zero on success, negated errno if an error is encountered.
+ ****************************************************************************/
+
+static int  tivacan_isr(int irq, FAR void *context, FAR void *dev)
+{
+  FAR struct tiva_canmod_s *canmod = ((FAR struct can_dev_s *)dev)->cd_priv;
+  uint32_t canint;
+  int      ret = OK;
+
+#ifdef CONFIG_CAN_ERRORS
+  uint32_t reg;
+#endif
+
+  while ((canint = getreg32(canmod->base + TIVA_CAN_OFFSET_INT)
+                                      & TIVA_CAN_INT_INTID_MASK))
+    {
+      if (canint == TIVA_CAN_INT_INTID_STATUS)
+        {
+          ret = tivacan_handle_errors((FAR struct can_dev_s *)dev);
+
+#ifdef CONFIG_CAN_ERRORS
+          if (ret < 0)
+            {
+              /* We are spamming the application and it can't keep up...
+               * disable status interrupts and send error messages
+               * periodically.
+               */
+
+              modreg32(0, TIVA_CAN_CTL_SIE,
+                       canmod->base + TIVA_CAN_OFFSET_CTL);
+              work_queue(LPWORK,
+                         &canmod->error_work,
+                         tivacan_handle_errors_wqueue,
+                         dev,
+                         TIVA_CAN_ERR_HANDLER_TICKS);
+            }
+
+#endif
+          continue;
+        }
+      else
+        {
+          /* Note: the datasheet incorrectly states "A message interrupt is
+           * cleared by clearing the message object's INTPND bit in the
+           * CANIFnMCTL register or by reading the CAN Status (CANSTS)
+           * register."
+           *
+           * In fact, a message interrupt can only be cleared via the
+           * interface registers. Here, we use the "shortcut" in the
+           * command mask register to avoid a read-modify-write of MCTL.
+           */
+
+          putreg32(TIVA_CANIF_CMSK_CLRINTPND,
+                   canmod->isr_iface_base + TIVA_CANIF_OFFSET_CMSK);
+
+          /* Submit request and wait for completion */
+
+          putreg32(canint, canmod->isr_iface_base + TIVA_CANIF_OFFSET_CRQ);
+          while (getreg32(canmod->isr_iface_base + TIVA_CANIF_OFFSET_CRQ)
+                  & TIVA_CANIF_CRQ_BUSY);
+
+          if (canint >= TIVA_CAN_TX_FIFO_START + 1)
+            {
+              /* tx_fifo_tail contains the index of the next mailbox in the
+               * hardware TX FIFO; i.e. where the next message will be put.
+               * It is always one more than the last message to be added to
+               * the FIFO, so even though canint is one-indexed and
+               * TIVA_CAN_TX_FIFO_START is zero-indexed we do not add 1 here.
+               */
+
+              if (canint == TIVA_CAN_TX_FIFO_START + canmod->tx_fifo_tail)
+                {
+                  /* Only wake up the write thread when we've emptied the
+                   * FIFO; the Tiva CAN module doesn't support a TX ring
+                   * buffer.
+                   */
+
+                  can_txready(dev);
+                  canmod->tx_fifo_tail = 0;
+                }
+
+#ifdef CONFIG_CAN_ERRORS
+              reg = getreg32(canmod->base + TIVA_CAN_OFFSET_CTL);
+
+              if (! (reg & TIVA_CAN_CTL_SIE))
+                {
+                  /* The application is not overwhelmed (it was able to call
+                   * write()) and the message was successfully sent.
+                   * Re-enable status interrupts to immediately notify of
+                   * e.g. lost arbitration.
+                   */
+
+                  work_cancel(LPWORK, &canmod->error_work);
+                  putreg32(reg | TIVA_CAN_CTL_SIE,
+                          canmod->base + TIVA_CAN_OFFSET_CTL);
+                }
+#endif
+            }
+          else
+            {
+              int sval;
+              nxsem_get_value(&canmod->rxsem, &sval);
+
+              /* Unblock the message retrieval thread if blocked; signal it
+               * if it needs to loop through all the mailboxes again once
+               * it finishes its current iteration.
+               */
+
+              if (sval == -1
+                  || (sval == 0 && canint - 1 < canmod->kthd_cur_mbox))
+                {
+                  nxsem_post(&canmod->rxsem);
+                }
+            }
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: tivacan_bittiming_get
+ *
+ * Description:
+ *   Get the CAN bit timing parameters from the hardware (prescaler, TSEG1,
+ *   TSEG2, and SJW). The values returned are the actual values in time
+ *   quanta, not the values stored in the register (which are off by one).
+ *
+ *   This is a driver-internal utility function.
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure
+ *
+ * Returned value:
+ *   struct tiva_can_timing_s containing the four parameters listed above.
+ ****************************************************************************/
+
+static struct tiva_can_timing_s
+tivacan_bittiming_get(FAR struct can_dev_s *dev)
+{
+  struct tiva_can_timing_s timing;
+  uint32_t canbase = ((struct tiva_canmod_s *)dev->cd_priv)->base;
+
+  uint32_t canbit;
+  uint32_t brpe;
+  canbit = getreg32(canbase + TIVA_CAN_OFFSET_BIT);
+  brpe   = getreg32(canbase + TIVA_CAN_OFFSET_BRPE);
+
+  timing.prescaler = (canbit & TIVA_CAN_BIT_BRP_MASK)
+                      >> TIVA_CAN_BIT_BRP_SHIFT;
+  timing.prescaler |= ((brpe & TIVA_CAN_BRPE_BRPE_MASK)
+                        >> TIVA_CAN_BRPE_BRPE_SHIFT)
+                        << TIVA_CAN_BIT_BRP_LENGTH;
+
+  timing.tseg1 = (canbit & TIVA_CAN_BIT_TSEG1_MASK)
+                  >> TIVA_CAN_BIT_TSEG1_SHIFT;
+  timing.tseg2 = (canbit & TIVA_CAN_BIT_TSEG2_MASK)
+                  >> TIVA_CAN_BIT_TSEG2_SHIFT;
+  timing.sjw   = (canbit & TIVA_CAN_BIT_SJW_MASK)
+                  >> TIVA_CAN_BIT_SJW_SHIFT;
+
+  /* Values stored in registers are 1 less than the values used */
+
+  ++timing.prescaler;
+  ++timing.tseg1;
+  ++timing.tseg2;
+  ++timing.sjw;
+
+  return timing;
+}
+
+/****************************************************************************
+ * Name: tivacan_bittiming_set
+ *
+ * Description:
+ *   Set the CAN bit timing parameters to the hardware registers. The values
+ *   used should be the actual values in time quanta, not the values stored
+ *   in the registers (which are off by one).
+ *
+ *   This is a driver-internal utility function.
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure
+ *   timing - An instance of tiva_can_timing_s, with the prescaler, TSEG1,
+ *            TSEG2, and SJW set to their real (not off-by-one) values.
+ *
+ * Returned value: None
+ ****************************************************************************/
+
+static void tivacan_bittiming_set(FAR struct can_dev_s *dev,
+                                  FAR struct tiva_can_timing_s *timing)
+{
+  irqstate_t flags;
+  uint32_t    canbit;
+  uint32_t    brpe;
+  uint32_t    ctl;
+  bool        init_mode;
+  uint32_t    canbase = ((struct tiva_canmod_s *)dev->cd_priv)->base;
+
+  DEBUGASSERT(timing->prescaler > TIVA_CAN_PRESCALER_MIN &&
+              timing->prescaler < TIVA_CAN_PRESCALER_MAX);
+  DEBUGASSERT(timing->tseg1 > TIVA_CAN_TSEG1_MIN &&
+              timing->tseg1 < TIVA_CAN_PRESCALER_MAX);
+  DEBUGASSERT(timing->tseg2 > TIVA_CAN_TSEG2_MIN &&
+              timing->tseg2 < TIVA_CAN_TSEG2_MAX);
+  DEBUGASSERT(timing->sjw > TIVA_CAN_SJW_MIN &&
+              timing->sjw < TIVA_CAN_SJW_MAX);
+
+  flags = enter_critical_section();
+
+  ctl = getreg32(canbase + TIVA_CAN_OFFSET_CTL);
+
+  /* Save the current state of the init bit */
+
+  init_mode = ctl & TIVA_CAN_CTL_INIT;
+
+  /* Enable writes to CANBIT and BRPE - set INIT and Configuration Change
+   * Enable (CCE)
+   */
+
+  ctl |= TIVA_CAN_CTL_INIT | TIVA_CAN_CTL_CCE;
+  putreg32(ctl, canbase + TIVA_CAN_OFFSET_CTL);
+
+  canbit = getreg32(canbase + TIVA_CAN_OFFSET_BIT);
+  brpe   = getreg32(canbase + TIVA_CAN_OFFSET_BRPE);
+
+  canbit &= ~(TIVA_CAN_BIT_BRP_MASK | TIVA_CAN_BIT_TSEG1_MASK
+              | TIVA_CAN_BIT_TSEG2_MASK | TIVA_CAN_BIT_SJW_MASK);
+
+  /* Masking bits that belong in the baud rate prescaler extension register.
+   * Values stored in registers are 1 less than the value used.
+   */
+
+  canbit |= ((timing->prescaler - 1) << TIVA_CAN_BIT_BRP_SHIFT)
+              & TIVA_CAN_BIT_BRP_MASK;
+  brpe   |= ((timing->prescaler - 1) >> TIVA_CAN_BIT_BRP_LENGTH)
+                                      << TIVA_CAN_BRPE_BRPE_SHIFT;
+
+  canbit |= (timing->tseg1 - 1) << TIVA_CAN_BIT_TSEG1_SHIFT;
+  canbit |= (timing->tseg2 - 1) << TIVA_CAN_BIT_TSEG2_SHIFT;
+  canbit |= (timing->sjw - 1) << TIVA_CAN_BIT_SJW_SHIFT;
+
+  putreg32(canbit, canbase + TIVA_CAN_OFFSET_BIT);
+  putreg32(brpe, canbase + TIVA_CAN_OFFSET_BRPE);
+
+  /* Lock changes to CANBIT and BRPE (clear Configuration Change Enable) */
+
+  ctl &= ~TIVA_CAN_CTL_CCE;
+
+  /* Exit init mode if the baud rate was changed on-the-fly */
+
+  if (!init_mode)
+    {
+      ctl &= ~TIVA_CAN_CTL_INIT;
+    }
+
+  putreg32(ctl, canbase + TIVA_CAN_OFFSET_CTL);
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: tivacan_alloc_fifo
+ *
+ * Description:
+ *   Try to allocate a FIFO of the specified depth in the CAN module SRAM.
+ *   (FIFOs in the Tiva CAN modules don't need to be contiguous)
+ *
+ *   This is a driver-internal utility function.
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure
+ *   depth - The desired FIFO depth to allocate
+ *
+ * Return value: The index of a tiva_can_fifo_t in dev->cd_priv which
+ *               contains a bit-field of the mailboxes that may be
+ *               used, -ENOSPC if there is not enough room in the CAN module
+ *               SRAM (i.e. too many FIFOs already allocated), or -ENOMEM if
+ *               the limit CONFIG_TIVA_CAN_FILTERS_MAX has been reached.
+ ****************************************************************************/
+
+int tivacan_alloc_fifo(FAR struct can_dev_s *dev, int depth)
+{
+  tiva_can_fifo_t claimed = 0;
+  int i;
+  int numclaimed = 0;
+  int free_fifo_idx = -1;
+  FAR struct tiva_canmod_s *canmod = dev->cd_priv;
+
+  nxmutex_lock(&canmod->fifo_mtx);
+
+  /* Mailboxes allocated other RX FIFOs or the TX FIFO */
+
+  for (i = 0; i < CONFIG_TIVA_CAN_FILTERS_MAX + 1; ++i)
+    {
+      if (canmod->fifos[i] == 0)
+        {
+          free_fifo_idx = i;
+        }
+
+      claimed |= canmod->fifos[i];
+    }
+
+  if (free_fifo_idx < 0)
+    {
+      canwarn("Max number of filters allocated.\n");
+      return -ENOMEM;
+    }
+
+  /* Mailboxes that are available to be allocated to this FIFO */
+
+  claimed = ~claimed;
+
+  for (i = 0; i < (TIVA_CAN_NUM_MBOXES - CONFIG_TIVA_CAN_TX_FIFO_DEPTH)
+              && numclaimed < depth; ++i)
+    {
+      if (claimed & 1 << i)
+        {
+          ++numclaimed;
+        }
+    }
+
+  if (numclaimed != depth)
+    {
+      nxmutex_unlock(&canmod->fifo_mtx);
+      return -ENOSPC;
+    }
+  else
+    {
+      /* Clear the bits for mailboxes we aren't claiming right now. */
+
+      claimed &= 0xffffffff >> (32 - i);
+      canmod->fifos[free_fifo_idx] = claimed;
+
+      nxmutex_unlock(&canmod->fifo_mtx);
+      return free_fifo_idx;
+    }
+}
+
+/****************************************************************************
+ * Name: tivacan_free_fifo
+ *
+ * Description:
+ *   Disable all the mailboxes in the specified FIFO and make them
+ *   available for use by another filter. This function waits for pending
+ *   transmissions to complete (for the TX FIFO) or new messages to be
+ *   read (for RX filter FIFOs) before disabling a mailbox.
+ *
+ *   This is a driver-internal utility function.
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure
+ *   fifo - The FIFO to free
+ *
+ * Return value: None
+ ****************************************************************************/
+
+static void tivacan_free_fifo(FAR struct can_dev_s *dev,
+                                 FAR tiva_can_fifo_t *fifo)
+{
+  FAR struct tiva_canmod_s * canmod = dev->cd_priv;
+  nxmutex_lock(&canmod->thd_iface_mtx);
+
+  for (int i = 0; i < TIVA_CAN_NUM_MBOXES; ++i)
+    {
+      if (*fifo & 1 << i)
+        {
+          tivacan_disable_mbox(dev, canmod->thd_iface_base, i);
+
+          *fifo &= ~(1 << i);
+        }
+    }
+
+  nxmutex_unlock(&canmod->thd_iface_mtx);
+}
+
+/****************************************************************************
+ * Name: tivacan_disable_mbox
+ *
+ * Description:
+ *   Disable a mailbox by clearing the MSGVAL bit. This function
+ *   doesn't do any checks before disabling it. Don't call it without
+ *   checking whether there's a pending message in the object! It takes the
+ *   raw base interface register base address, so it can be called from
+ *   thread or ISR context. Zeros out the entire ARB2 register.
+ *
+ *   This function guarantees that the mailbox is actually disabled by
+ *   reading back the value of the register. As briefly mentioned in the
+ *   datasheet and further explained on the TI forums [1], writes to the
+ *   message RAM can be lost if the CAN peripheral is performing a
+ *   read-modify-write operation (i.e. actively receiving a CAN message).
+ *
+ *   Yes, the thread is for the C2000 microcontrollers but the CAN peripheral
+ *   appears to be very similar. However, this approach accomplishes the same
+ *   thing and doesn't take the risk of missing a CAN message by setting the
+ *   INIT bit as suggested in the forum.
+ *
+ *   This is a driver-internal utility function.
+ *
+ *   [1] https://e2e.ti.com/support/microcontrollers/c2000/f/171/t/916169
+ *
+ * Input parameters:
+ *   dev - An instance of the "upper half" CAN driver structure
+ *   iface_base - The interface registers base address to use
+ *   num - The mailbox number to disable (zero-indexed)
+ *
+ * Return value: none
+ ****************************************************************************/
+
+static void tivacan_disable_mbox(FAR struct can_dev_s *dev,
+                                 uint32_t iface_base,
+                                 int num)
+{
+  uint32_t reg;
+  FAR struct tiva_canmod_s *canmod = dev->cd_priv;
+
+  while (true)
+    {
+      /* Read (back) the ARB2 register. Our write could have been lost if we
+       * caught the peripheral in the middle of a read-modify-write operation
+       * with a received message.
+       */
+
+      reg = tivacan_readsplitreg32(canmod->base, TIVA_CAN_OFFSET_MSG1VAL,
+                                   TIVA_CAN_OFFSET_MSG2VAL);
+      if (!(reg & 1 << num))
+        {
+          break;
+        }
+
+      /* Same as before, but select a write (WRNRD) */
+
+      putreg32(TIVA_CANIF_CMSK_WRNRD | TIVA_CANIF_CMSK_ARB,
+              iface_base + TIVA_CANIF_OFFSET_CMSK);
+
+      /* Mark mailbox as invalid */
+
+      reg &= ~TIVA_CANIF_ARB2_MSGVAL;
+      putreg32(0, iface_base + TIVA_CANIF_OFFSET_ARB2);
+
+      /* Submit request & wait for completion */
+
+      putreg32(num + 1, iface_base + TIVA_CANIF_OFFSET_CRQ);
+      while (getreg32(iface_base + TIVA_CANIF_OFFSET_CRQ)
+              & TIVA_CANIF_CRQ_BUSY);
+    }
+}
+
+/****************************************************************************
+ * Name: tivacan_initfilter
+ *
+ * Description:
+ *   Initialize the mailboxes in the specified FIFO to store messages
+ *   matching the given filter. On return, the FIFO will generate interrupts
+ *   on matching messages (if the CAN hardware is initialized).
+ *
+ *   Note that standard filters only match standard messages, but extended
+ *   filters will match both standard and extended message IDs.
+ *
+ *   REVISIT: it might be nice to have an ioctl to control this behavior.
+ *
+ *   This is a driver-internal utility function.
+ *
+ * Input parameters:
+ *   dev    - An instance of the "upper-half" CAN driver structure
+ *   fifo   - A FIFO in the obtained by calling tivacan_alloc_fifo()
+ *   filter - A canioc_stdfilter_s or canioc_extfilter_s
+ *            of type CAN_FILTER_MASK
+ *   type   - One of TIVA_CAN_FILTER_TYPE_STD or TIVA_CAN_FILTER_TYPE_EXT
+ *
+ * Return value: 0 on success, a negated errno on failure.
+ ****************************************************************************/
+
+static int  tivacan_initfilter(FAR struct can_dev_s *dev,
+                               FAR tiva_can_fifo_t  *fifo,
+                               FAR void             *filter,
+                               int                   type)
+{
+  uint32_t  reg;
+
+  /* Whether the End of Buffer bit has been set - the Tiva hardware
+   * overwrites the mailbox with this bit even if there is new data in it
+   * when the FIFO is full.
+   */
+
+  bool      eob_set     = false;
+
+  FAR struct tiva_canmod_s *canmod = dev->cd_priv;
+  uint32_t  iface_base  = canmod->thd_iface_base;
+
+  FAR struct canioc_stdfilter_s *sfilter = NULL;
+#ifdef CONFIG_CAN_EXTID
+  FAR struct canioc_extfilter_s *xfilter = NULL;
+#endif
+
+  if (type == TIVA_CAN_FILTER_TYPE_STD)
+    {
+      sfilter = filter;
+
+      if (sfilter->sf_type != CAN_FILTER_MASK)
+        {
+          return -EINVAL;
+        }
+    }
+#ifdef CONFIG_CAN_EXTID
+  else if (type == TIVA_CAN_FILTER_TYPE_EXT)
+    {
+      xfilter = filter;
+
+      if (xfilter->xf_type != CAN_FILTER_MASK)
+        {
+          return -EINVAL;
+        }
+    }
+#endif
+  else
+    {
+      canerr("Invalid filter type parameter.\n");
+      return -EINVAL;
+    }
+
+  for (int i = TIVA_CAN_NUM_MBOXES - 1; i >= 0; --i)
+    {
+      if (*fifo & (1 << i))
+        {
+          /* Disable the message object to prevent message SRAM conflicts */
+
+          tivacan_disable_mbox(dev, canmod->thd_iface_base, i);
+
+          /* Command Mask Register
+           *
+           * Write to message SRAM and access mask, control, and arbitration
+           * bits. Clear any interrupts (which might be spurious due to
+           * the hardware being uninitialized
+           */
+
+          reg =   TIVA_CANIF_CMSK_WRNRD     | TIVA_CANIF_CMSK_MASK
+                | TIVA_CANIF_CMSK_CONTROL   | TIVA_CANIF_CMSK_ARB
+                | TIVA_CANIF_CMSK_CLRINTPND;
+          putreg32(reg, iface_base + TIVA_CANIF_OFFSET_CMSK);
+
+#ifdef CONFIG_CAN_EXTID
+          /* Mask 1 Register - lower 16 bits of extended ID mask */
+
+          if (type == TIVA_CAN_FILTER_TYPE_EXT)
+            {
+              reg = xfilter->xf_id2 & TIVA_CANIF_MSK1_IDMSK_EXT_MASK;
+              putreg32(reg, iface_base + TIVA_CANIF_OFFSET_MSK1);
+            }
+#endif
+
+          /* Mask 2 Register
+           * Allow remote request frames through. This is done by disabling
+           * filter-on-direction (MDIR = 0)
+           */
+
+#ifdef CONFIG_CAN_EXTID
+          if (type == TIVA_CAN_FILTER_TYPE_EXT)
+            {
+              reg = (xfilter->xf_id2 >> TIVA_CANIF_MSK2_IDMSK_EXT_PRESHIFT)
+                    & TIVA_CANIF_MSK2_IDMSK_EXT_MASK;
+            }
+          else
+            {
+#endif
+              /* Filter out extended ID messages by default with standard
+               * filters.
+               */
+
+              reg = TIVA_CANIF_MSK2_MXTD;
+              reg |= sfilter->sf_id2 << TIVA_CANIF_MSK2_IDMSK_STD_SHIFT;
+#ifdef CONFIG_CAN_EXTID
+            }
+#endif
+
+          putreg32(reg, iface_base + TIVA_CANIF_OFFSET_MSK2);
+
+#ifdef CONFIG_CAN_EXTID
+          /* Arbitration 1 Register - lower 16 bits of extended ID */
+
+          if (type == TIVA_CAN_FILTER_TYPE_EXT)
+            {
+              reg = xfilter->xf_id1 & TIVA_CANIF_ARB1_ID_EXT_MASK;
+              putreg32(reg, iface_base + TIVA_CANIF_OFFSET_ARB1);
+            }
+#endif
+
+          /* Arbitration 2 Register - upper extended & all standard ID bits
+           * Also mark the mailbox as valid.
+           */
+
+          reg = TIVA_CANIF_ARB2_MSGVAL;
+
+#ifdef CONFIG_CAN_EXTID
+          if (type == TIVA_CAN_FILTER_TYPE_EXT)
+            {
+              reg |= xfilter->xf_id1 & TIVA_CANIF_ARB2_ID_EXT_MASK;
+            }
+          else
+            {
+#endif
+              /* Leave the XTD bit clear to indicate that only standard
+               * messages are allowed through the filter (MXTD is set).
+               */
+
+              reg |= sfilter->sf_id1 << TIVA_CANIF_ARB2_ID_STD_SHIFT;
+#ifdef CONFIG_CAN_EXTID
+            }
+#endif
+
+          putreg32(reg, iface_base + TIVA_CANIF_OFFSET_ARB2);
+
+          /* Message Control Register
+           * Use acceptance filter masks, enable RX interrupts for this
+           * mailbox. DLC is initialized to zero but updated by received
+           * frames.
+           */
+
+          reg = TIVA_CANIF_MCTL_UMASK | TIVA_CANIF_MCTL_RXIE;
+          if (!eob_set)
+            {
+              reg |= TIVA_CANIF_MCTL_EOB;
+              eob_set = true;
+            }
+
+          putreg32(reg, iface_base + TIVA_CANIF_OFFSET_MCTL);
+
+          /* Request the registers be transferred to the message RAM and wait
+           * for the transfer to complete. Mailboxes are 1-indexed in
+           * hardware.
+           */
+
+          putreg32(i + 1, iface_base + TIVA_CANIF_OFFSET_CRQ);
+          while (getreg32(iface_base + TIVA_CANIF_OFFSET_CRQ)
+                                        & TIVA_CANIF_CRQ_BUSY);
+        }
+    }
+
+  nxmutex_unlock(&canmod->thd_iface_mtx);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: tiva_can_initialize
+ *
+ * Description:
+ *   Initialize the selected CAN module
+ *
+ * Input Parameters:
+ *   Device path, a string of the form "/dev/can0" or "/dev/can1"
+ *   Module number, for chips with multiple modules (typically 0 or 1)
+ *
+ * Returned Value:
+ *   Pointer to can_dev_s (CAN device structure), or NULL on failure.
+ *
+ ****************************************************************************/
+
+int tiva_can_initialize(FAR char *devpath, int modnum)
+{
+  FAR struct can_dev_s *dev;
+  FAR struct tiva_canmod_s *canmod;
+  int ret;
+  caninfo("tiva_can_initialize module %d\n", modnum);
+
+#ifdef CONFIG_TIVA_CAN0
+  if (modnum == 0)
+    {
+      dev = &g_tivacan0dev;
+    }
+#else
+  if (modnum == 0)
+    {
+      return -ENODEV;
+    }
+#endif
+
+#ifdef CONFIG_TIVA_CAN1
+  if (modnum == 1)
+    {
+      dev = &g_tivacan1dev;
+    }
+#else
+  if (modnum == 1)
+    {
+      return -ENODEV;
+    }
+#endif
+
+  canmod = dev->cd_priv;
+
+  /* Initialize concurrancy objects for accessing interfaces */
+
+  ret = nxmutex_init(&canmod->thd_iface_mtx);
+  if (ret < 0)
+    {
+      canerr("ERROR: failed to initialize mutex: %d\n", ret);
+      return ret;
+    }
+
+  ret = nxmutex_init(&canmod->fifo_mtx);
+  if (ret < 0)
+    {
+      canerr("ERROR: failed to initialize mutex: %d\n", ret);
+      return ret;
+    }
+
+  /* Register the driver */
+
+  ret = can_register(devpath, dev);
+  if (ret < 0)
+    {
+      canerr("ERROR: can_register failed: %d\n", ret);
+    }
+
+  return ret;
+}
+
+#endif /* CONFIG_TIVA_CAN && (CONFIG_TIVA_CAN0 || CONFIG_TIVA_CAN1) */
diff --git a/arch/arm/src/tiva/hardware/tiva_can.h b/arch/arm/src/tiva/hardware/tiva_can.h
new file mode 100644
index 0000000..fe80c28
--- /dev/null
+++ b/arch/arm/src/tiva/hardware/tiva_can.h
@@ -0,0 +1,459 @@
+/****************************************************************************
+ * arch/arm/src/tiva/hardware/tiva_can.h
+ *
+ *   Copyright (C) 2006-2020 Texas Instruments Incorporated.
+ *                           All rights reserved.
+ *   Author: Matthew Trescott <ma...@gmail.com>
+ *
+ * From the TivaWare Peripheral Driver Library, with minor changes for
+ * clarity and style.
+ *
+ * The TivaWare sample code has a BSD compatible license that requires this
+ * copyright notice:
+ *
+ * Copyright (c) 2005-2020 Texas Instruments Incorporated.
+ * All rights reserved.
+ * Software License Agreement
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *   Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ *   Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the
+ *   distribution.
+ *
+ *   Neither the name of Texas Instruments Incorporated nor the names of
+ *   its contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This is part of revision 2.2.0295 of the Tiva Peripheral Driver Library.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_TIVA_HARDWARE_TIVA_CAN_H
+#define __ARCH_ARM_SRC_TIVA_HARDWARE_TIVA_CAN_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * The following are defines describing the CAN controller modules
+ ****************************************************************************/
+
+#define TIVA_CAN_NUM_IFACES        2            /* Number of sets of CANIF registers */
+
+/* Note: driver code uses a 32-bit bitmask to represent the message objects */
+
+#define TIVA_CAN_NUM_MBOXES      32             /* Number of slots in the message SRAM */
+
+/****************************************************************************
+ * The following are defines for the CAN register offsets.
+ ****************************************************************************/
+
+#define TIVA_CAN_OFFSET_CTL         0x00000000  /* CAN Control                  */
+#define TIVA_CAN_OFFSET_STS         0x00000004  /* CAN Status                   */
+#define TIVA_CAN_OFFSET_ERR         0x00000008  /* CAN Error Counter            */
+#define TIVA_CAN_OFFSET_BIT         0x0000000C  /* CAN Bit Timing               */
+#define TIVA_CAN_OFFSET_INT         0x00000010  /* CAN Interrupt                */
+#define TIVA_CAN_OFFSET_TST         0x00000014  /* CAN Test                     */
+#define TIVA_CAN_OFFSET_BRPE        0x00000018  /* CAN Baud Rate Prescaler      *
+                                                 * Extension                    */
+
+#define TIVA_CAN_OFFSET_IFACE1_BASE 0x00000020  /* CAN Interface 1 base         */
+#define TIVA_CAN_OFFSET_IF1CRQ      0x00000020  /* CAN IF1 Command Request      */
+#define TIVA_CAN_OFFSET_IF1CMSK     0x00000024  /* CAN IF1 Command Mask         */
+#define TIVA_CAN_OFFSET_IF1MSK1     0x00000028  /* CAN IF1 Mask 1               */
+#define TIVA_CAN_OFFSET_IF1MSK2     0x0000002C  /* CAN IF1 Mask 2               */
+#define TIVA_CAN_OFFSET_IF1ARB1     0x00000030  /* CAN IF1 Arbitration 1        */
+#define TIVA_CAN_OFFSET_IF1ARB2     0x00000034  /* CAN IF1 Arbitration 2        */
+#define TIVA_CAN_OFFSET_IF1MCTL     0x00000038  /* CAN IF1 Message Control      */
+#define TIVA_CAN_OFFSET_IF1DA1      0x0000003C  /* CAN IF1 Data A1              */
+#define TIVA_CAN_OFFSET_IF1DA2      0x00000040  /* CAN IF1 Data A2              */
+#define TIVA_CAN_OFFSET_IF1DB1      0x00000044  /* CAN IF1 Data B1              */
+#define TIVA_CAN_OFFSET_IF1DB2      0x00000048  /* CAN IF1 Data B2              */
+#define TIVA_CAN_OFFSET_IFACE2_BASE 0x00000080  /* CAN Interface 2 base         */
+#define TIVA_CAN_OFFSET_IF2CRQ      0x00000080  /* CAN IF2 Command Request      */
+#define TIVA_CAN_OFFSET_IF2CMSK     0x00000084  /* CAN IF2 Command Mask         */
+#define TIVA_CAN_OFFSET_IF2MSK1     0x00000088  /* CAN IF2 Mask 1               */
+#define TIVA_CAN_OFFSET_IF2MSK2     0x0000008C  /* CAN IF2 Mask 2               */
+#define TIVA_CAN_OFFSET_IF2ARB1     0x00000090  /* CAN IF2 Arbitration 1        */
+#define TIVA_CAN_OFFSET_IF2ARB2     0x00000094  /* CAN IF2 Arbitration 2        */
+#define TIVA_CAN_OFFSET_IF2MCTL     0x00000098  /* CAN IF2 Message Control      */
+#define TIVA_CAN_OFFSET_IF2DA1      0x0000009C  /* CAN IF2 Data A1              */
+#define TIVA_CAN_OFFSET_IF2DA2      0x000000A0  /* CAN IF2 Data A2              */
+#define TIVA_CAN_OFFSET_IF2DB1      0x000000A4  /* CAN IF2 Data B1              */
+#define TIVA_CAN_OFFSET_IF2DB2      0x000000A8  /* CAN IF2 Data B2              */
+#define TIVA_CAN_OFFSET_TXRQ1       0x00000100  /* CAN Transmission Request 1   */
+#define TIVA_CAN_OFFSET_TXRQ2       0x00000104  /* CAN Transmission Request 2   */
+#define TIVA_CAN_OFFSET_NWDA1       0x00000120  /* CAN New Data 1               */
+#define TIVA_CAN_OFFSET_NWDA2       0x00000124  /* CAN New Data 2               */
+#define TIVA_CAN_OFFSET_MSG1INT     0x00000140  /* CAN Message 1                *
+                                                 * Interrupt Pending            */
+
+#define TIVA_CAN_OFFSET_MSG2INT     0x00000144  /* CAN Message 2                *
+                                                 * Interrupt Pending            */
+
+#define TIVA_CAN_OFFSET_MSG1VAL     0x00000160  /* CAN Message 1 Valid          */
+#define TIVA_CAN_OFFSET_MSG2VAL     0x00000164  /* CAN Message 2 Valid          */
+
+/****************************************************************************
+ * The following are defines for CAN registers
+ ****************************************************************************/
+
+#define TIVA_CAN_BASE(n)      (TIVA_CAN0_BASE + (n)*0x1000)
+
+#define TIVA_CAN_CTL(n)       (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_CTL)      /* CAN Control                  */
+#define TIVA_CAN_STS(n)       (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_STS)      /* CAN Status                   */
+#define TIVA_CAN_ERR(n)       (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_ERR)      /* CAN Error Counter            */
+#define TIVA_CAN_BIT(n)       (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_BIT)      /* CAN Bit Timing               */
+#define TIVA_CAN_INT(n)       (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_INT)      /* CAN Interrupt                */
+#define TIVA_CAN_TST(n)       (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_TST)      /* CAN Test                     */
+#define TIVA_CAN_BRPE(n)      (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_BRPE)     /* CAN Baud Rate Prescaler      *
+                                                                           * Extension                    */
+
+#define TIVA_CAN_IF1CRQ(n)    (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF1CRQ)   /* CAN IF1 Command Request      */
+#define TIVA_CAN_IF1CMSK(n)   (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF1CMSK)  /* CAN IF1 Command Mask         */
+#define TIVA_CAN_IF1MSK1(n)   (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF1MSK1)  /* CAN IF1 Mask 1               */
+#define TIVA_CAN_IF1MSK2(n)   (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF1MSK2)  /* CAN IF1 Mask 2               */
+#define TIVA_CAN_IF1ARB1(n)   (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF1ARB1)  /* CAN IF1 Arbitration 1        */
+#define TIVA_CAN_IF1ARB2(n)   (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF1ARB2)  /* CAN IF1 Arbitration 2        */
+#define TIVA_CAN_IF1MCTL(n)   (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF1MCTL)  /* CAN IF1 Message Control      */
+#define TIVA_CAN_IF1DA1(n)    (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF1DA1)   /* CAN IF1 Data A1              */
+#define TIVA_CAN_IF1DA2(n)    (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF1DA2)   /* CAN IF1 Data A2              */
+#define TIVA_CAN_IF1DB1(n)    (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF1DB1)   /* CAN IF1 Data B1              */
+#define TIVA_CAN_IF1DB2(n)    (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF1DB2)   /* CAN IF1 Data B2              */
+#define TIVA_CAN_IF2CRQ(n)    (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF2CRQ)   /* CAN IF2 Command Request      */
+#define TIVA_CAN_IF2CMSK(n)   (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF2CMSK)  /* CAN IF2 Command Mask         */
+#define TIVA_CAN_IF2MSK1(n)   (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF2MSK1)  /* CAN IF2 Mask 1               */
+#define TIVA_CAN_IF2MSK2(n)   (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF2MSK2)  /* CAN IF2 Mask 2               */
+#define TIVA_CAN_IF2ARB1(n)   (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF2ARB1)  /* CAN IF2 Arbitration 1        */
+#define TIVA_CAN_IF2ARB2(n)   (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF2ARB2)  /* CAN IF2 Arbitration 2        */
+#define TIVA_CAN_IF2MCTL(n)   (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF2MCTL)  /* CAN IF2 Message Control      */
+#define TIVA_CAN_IF2DA1(n)    (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF2DA1)   /* CAN IF2 Data A1              */
+#define TIVA_CAN_IF2DA2(n)    (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF2DA2)   /* CAN IF2 Data A2              */
+#define TIVA_CAN_IF2DB1(n)    (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF2DB1)   /* CAN IF2 Data B1              */
+#define TIVA_CAN_IF2DB2(n)    (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IF2DB2)   /* CAN IF2 Data B2              */
+#define TIVA_CAN_TXRQ1(n)     (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_TXRQ1)    /* CAN Transmission Request 1   */
+#define TIVA_CAN_TXRQ2(n)     (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_TXRQ2)    /* CAN Transmission Request 2   */
+#define TIVA_CAN_NWDA1(n)     (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_NWDA1)    /* CAN New Data 1               */
+#define TIVA_CAN_NWDA2(n)     (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_NWDA2)    /* CAN New Data 2               */
+#define TIVA_CAN_MSG1INT(n)   (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_MSG1INT)  /* CAN Message                  *
+                                                                           * Interrupt Pending 2          */
+
+#define TIVA_CAN_MSG2INT(n)   (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_MSG2INT)  /* CAN Message                  *
+                                                                           * Interrupt Pending 2          */
+
+#define TIVA_CAN_MSG1VAL(n) (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_MSG1VAL)    /* CAN Message 1 Valid          */
+#define TIVA_CAN_MSG2VAL(n) (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_MSG2VAL)    /* CAN Message 2 Valid          */
+
+/****************************************************************************
+ * The following are defines for CAN interface (CANIFn) register offsets.
+ * Note that in these defines, the CAN interfaces are indexed from zero.
+ ****************************************************************************/
+
+#define TIVA_CANIF_OFFSET_CRQ   0x00000000
+#define TIVA_CANIF_OFFSET_CMSK  0x00000004
+#define TIVA_CANIF_OFFSET_MSK1  0x00000008
+#define TIVA_CANIF_OFFSET_MSK2  0x0000000C
+#define TIVA_CANIF_OFFSET_ARB1  0x00000010
+#define TIVA_CANIF_OFFSET_ARB2  0x00000014
+#define TIVA_CANIF_OFFSET_MCTL  0x00000018
+#define TIVA_CANIF_OFFSET_DA1   0x0000001C
+#define TIVA_CANIF_OFFSET_DA2   0x00000020
+#define TIVA_CANIF_OFFSET_DB1   0x00000024
+#define TIVA_CANIF_OFFSET_DB2   0x00000028
+
+#define TIVA_CANIF_OFFSET_DATA(n) (TIVA_CANIF_OFFSET_DA1 + (n) * 0x4)
+
+#define TIVA_CAN_OFFSET_IFACE(i) (TIVA_CAN_OFFSET_IFACE1_BASE + (i) * 0x60)
+#define TIVA_CAN_IFACE_BASE(n,i) (TIVA_CAN_BASE(n)+TIVA_CAN_OFFSET_IFACE(i))
+
+#define TIVA_CANIF_CRQ(n,i)   (TIVA_CAN_IFACE_BASE((n),(i))+TIVA_CANIF_OFFSET_CRQ)
+#define TIVA_CANIF_CMSK(n,i)  (TIVA_CAN_IFACE_BASE((n),(i))+TIVA_CANIF_OFFSET_CMSK)
+#define TIVA_CANIF_MSK1(n,i)  (TIVA_CAN_IFACE_BASE((n),(i))+TIVA_CANIF_OFFSET_MSK1)
+#define TIVA_CANIF_MSK2(n,i)  (TIVA_CAN_IFACE_BASE((n),(i))+TIVA_CANIF_OFFSET_MSK2)
+#define TIVA_CANIF_ARB1(n,i)  (TIVA_CAN_IFACE_BASE((n),(i))+TIVA_CANIF_OFFSET_ARB1)
+#define TIVA_CANIF_ARB2(n,i)  (TIVA_CAN_IFACE_BASE((n),(i))+TIVA_CANIF_OFFSET_ARB2)
+#define TIVA_CANIF_MCTL(n,i)  (TIVA_CAN_IFACE_BASE((n),(i))+TIVA_CANIF_OFFSET_MCTL)
+#define TIVA_CANIF_DA1(n,i)   (TIVA_CAN_IFACE_BASE((n),(i))+TIVA_CANIF_OFFSET_DA1)
+#define TIVA_CANIF_DA2(n,i)   (TIVA_CAN_IFACE_BASE((n),(i))+TIVA_CANIF_OFFSET_DA2)
+#define TIVA_CANIF_DB1(n,i)   (TIVA_CAN_IFACE_BASE((n),(i))+TIVA_CANIF_OFFSET_DB1)
+#define TIVA_CANIF_DB2(n,i)   (TIVA_CAN_IFACE_BASE((n),(i))+TIVA_CANIF_OFFSET_DB2)
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANCTL register.
+ ****************************************************************************/
+
+#define TIVA_CAN_CTL_TEST       0x00000080  /* Test Mode Enable                 */
+#define TIVA_CAN_CTL_CCE        0x00000040  /* Configuration Change Enable      */
+#define TIVA_CAN_CTL_DAR        0x00000020  /* Disable Automatic-Retransmission */
+#define TIVA_CAN_CTL_EIE        0x00000008  /* Error Interrupt Enable           */
+#define TIVA_CAN_CTL_SIE        0x00000004  /* Status Interrupt Enable          */
+#define TIVA_CAN_CTL_IE         0x00000002  /* CAN Interrupt Enable             */
+#define TIVA_CAN_CTL_INIT       0x00000001  /* Initialization                   */
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANSTS register
+ ****************************************************************************/
+
+#define TIVA_CAN_STS_BOFF           0x00000080  /* Bus-Off Status                   */
+#define TIVA_CAN_STS_EWARN          0x00000040  /* Warning Status                   */
+#define TIVA_CAN_STS_EPASS          0x00000020  /* Error Passive                    */
+#define TIVA_CAN_STS_RXOK           0x00000010  /* Received a Message Successfully  */
+#define TIVA_CAN_STS_TXOK           0x00000008  /* Transmitted a Message            */
+                                                /* Successfully                     */
+#define TIVA_CAN_STS_LEC_MASK       0x00000007  /* Last Error Code                  */
+#define TIVA_CAN_STS_LEC_NONE       0x00000000  /* No Error                         */
+#define TIVA_CAN_STS_LEC_STUFF      0x00000001  /* Stuff Error                      */
+#define TIVA_CAN_STS_LEC_FORM       0x00000002  /* Format Error                     */
+#define TIVA_CAN_STS_LEC_ACK        0x00000003  /* ACK Error                        */
+#define TIVA_CAN_STS_LEC_BIT1       0x00000004  /* Bit 1 Error                      */
+#define TIVA_CAN_STS_LEC_BIT0       0x00000005  /* Bit 0 Error                      */
+#define TIVA_CAN_STS_LEC_CRC        0x00000006  /* CRC Error                        */
+#define TIVA_CAN_STS_LEC_NOEVENT    0x00000007  /* No Event                         */
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANERR register.
+ ****************************************************************************/
+
+#define TIVA_CAN_ERR_RP             0x00008000  /* Received Error Passive           */
+#define TIVA_CAN_ERR_REC_MASK       0x00007F00  /* Receive Error Counter            */
+#define TIVA_CAN_ERR_TEC_MASK       0x000000FF  /* Transmit Error Counter           */
+#define TIVA_CAN_ERR_REC_SHIFT      8
+#define TIVA_CAN_ERR_TEC_SHIFT      0
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANBIT register.
+ ****************************************************************************/
+
+#define TIVA_CAN_BIT_TSEG2_MASK    0x00007000  /* Time Segment after Sample Point   */
+#define TIVA_CAN_BIT_TSEG1_MASK    0x00000F00  /* Time Segment Before Sample Point  */
+#define TIVA_CAN_BIT_SJW_MASK      0x000000C0  /* (Re)Synchronization Jump Width    */
+#define TIVA_CAN_BIT_BRP_MASK      0x0000003F  /* Baud Rate Prescaler               */
+#define TIVA_CAN_BIT_TSEG2_SHIFT    12
+#define TIVA_CAN_BIT_TSEG1_SHIFT    8
+#define TIVA_CAN_BIT_SJW_SHIFT      6
+#define TIVA_CAN_BIT_BRP_SHIFT      0
+#define TIVA_CAN_BIT_BRP_LENGTH     6          /* This is also the number of bits to shift the BRPE by */
+
+#define TIVA_CAN_PRESCALER_MIN      1
+#define TIVA_CAN_PRESCALER_MAX      1024
+#define TIVA_CAN_TSEG1_MIN          1
+#define TIVA_CAN_TSEG1_MAX          16
+#define TIVA_CAN_TSEG2_MIN          1
+#define TIVA_CAN_TSEG2_MAX          8
+#define TIVA_CAN_SJW_MIN            1
+#define TIVA_CAN_SJW_MAX            4
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANINT register.
+ ****************************************************************************/
+
+#define TIVA_CAN_INT_INTID_MASK     0x0000FFFF  /* Interrupt Identifier */
+#define TIVA_CAN_INT_INTID_NONE     0x00000000  /* No interrupt pending */
+#define TIVA_CAN_INT_INTID_STATUS   0x00008000  /* Status Interrupt     */
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANTST register.
+ ****************************************************************************/
+
+#define TIVA_CAN_TST_RX             0x00000080  /* Receive Observation  */
+#define TIVA_CAN_TST_TX_MASK        0x00000060  /* Transmit Control     */
+#define TIVA_CAN_TST_TX_CANCTL      0x00000000  /* CAN Module Control   */
+#define TIVA_CAN_TST_TX_SAMPLE      0x00000020  /* Sample Point         */
+#define TIVA_CAN_TST_TX_DOMINANT    0x00000040  /* Driven Low           */
+#define TIVA_CAN_TST_TX_RECESSIVE   0x00000060  /* Driven High          */
+#define TIVA_CAN_TST_LBACK          0x00000010  /* Loopback Mode        */
+#define TIVA_CAN_TST_SILENT         0x00000008  /* Silent Mode          */
+#define TIVA_CAN_TST_BASIC          0x00000004  /* Basic Mode           */
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANBRPE register.
+ ****************************************************************************/
+
+#define TIVA_CAN_BRPE_BRPE_MASK     0x0000000F  /* Baud Rate Prescaler Extension */
+#define TIVA_CAN_BRPE_BRPE_SHIFT    0
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANIF1CRQ register.
+ ****************************************************************************/
+#define TIVA_CANIF_CRQ_BUSY         0x00008000  /* Busy Flag        */
+#define TIVA_CANIF_CRQ_MNUM_MASK    0x0000003F  /* Message Number   */
+#define TIVA_CANIF_CRQ_MNUM_SHIFT   0
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANIF1CMSK register.
+ ****************************************************************************/
+
+#define TIVA_CANIF_CMSK_WRNRD       0x00000080  /* Write, Not Read              */
+#define TIVA_CANIF_CMSK_MASK        0x00000040  /* Access Mask Bits             */
+#define TIVA_CANIF_CMSK_ARB         0x00000020  /* Access Arbitration Bits      */
+#define TIVA_CANIF_CMSK_CONTROL     0x00000010  /* Access Control Bits          */
+#define TIVA_CANIF_CMSK_CLRINTPND   0x00000008  /* Clear Interrupt Pending Bit  */
+#define TIVA_CANIF_CMSK_NEWDAT      0x00000004  /* Access New Data              */
+#define TIVA_CANIF_CMSK_TXRQST      0x00000004  /* Access Transmission Request  */
+#define TIVA_CANIF_CMSK_DATAA       0x00000002  /* Access Data Byte 0 to 3      */
+#define TIVA_CANIF_CMSK_DATAB       0x00000001  /* Access Data Byte 4 to 7      */
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANIF1MSK1 register.
+ ****************************************************************************/
+
+#define TIVA_CANIF_MSK1_IDMSK_EXT_MASK  0x0000FFFF  /* Identifier mask for lower 16 bits of extended ID */
+#define TIVA_CANIF_MSK1_IDMSK_EXT_SHIFT 0
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANIF1MSK2 register.
+ ****************************************************************************/
+
+#define TIVA_CANIF_MSK2_MXTD                0x00008000  /* Mask Extended Identifier                                           */
+#define TIVA_CANIF_MSK2_MDIR                0x00004000  /* Mask Message Direction                                             */
+#define TIVA_CANIF_MSK2_IDMSK_EXT_MASK      0x00001FFF  /* Identifier Mask for upper 13 bits of extended ID                   */
+#define TIVA_CANIF_MSK2_IDMSK_EXT_SHIFT     0           /* Shift LEFT by this number to place a value in this field           */
+#define TIVA_CANIF_MSK2_IDMSK_EXT_PRESHIFT  16          /* Shift RIGHT by this number to get the chunk this register wants    */
+
+#define TIVA_CANIF_MSK2_IDMSK_STD_MASK      0x00001FFC  /* MSK2 contains all 11 bits of a standard ID, but not aligned with 0 */
+#define TIVA_CANIF_MSK2_IDMSK_STD_SHIFT     2           /* Shift LEFT by this number to place a value in this field           */
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANIF1ARB1 register.
+ ****************************************************************************/
+
+#define TIVA_CANIF_ARB1_ID_EXT_MASK     0x0000FFFF  /* Identifier for lower 16 bits of extended ID */
+#define TIVA_CANIF_ARB1_ID_EXT_SHIFT    0
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANIF1ARB2 register.
+ ****************************************************************************/
+
+#define TIVA_CANIF_ARB2_MSGVAL          0x00008000  /* Message Valid                                                      */
+#define TIVA_CANIF_ARB2_XTD             0x00004000  /* Extended Identifier                                                */
+#define TIVA_CANIF_ARB2_DIR             0x00002000  /* Message Direction                                                  */
+#define TIVA_CANIF_ARB2_ID_EXT_MASK     0x00001FFF  /* Message Identifier                                                 */
+#define TIVA_CANIF_ARB2_ID_EXT_SHIFT    0           /* Shift LEFT by this number to place a value in this filed           */
+#define TIVA_CANIF_ARB2_ID_EXT_PRESHIFT 16          /* Shift RIGHT by this number to get the chunk this register wants    */
+
+#define TIVA_CANIF_ARB2_ID_STD_MASK     0x00001FFC  /* ARB2 contains all 11 bits of a standard ID, but not aligned with 0 */
+#define TIVA_CANIF_ARB2_ID_STD_SHIFT    2           /* Shift LEFT by this number to place a value in this field           */
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANIF1MCTL register.
+ ****************************************************************************/
+
+#define TIVA_CANIF_MCTL_NEWDAT      0x00008000  /* New Data                     */
+#define TIVA_CANIF_MCTL_MSGLST      0x00004000  /* Message Lost                 */
+#define TIVA_CANIF_MCTL_INTPND      0x00002000  /* Interrupt Pending            */
+#define TIVA_CANIF_MCTL_UMASK       0x00001000  /* Use Acceptance Mask          */
+#define TIVA_CANIF_MCTL_TXIE        0x00000800  /* Transmit Interrupt Enable    */
+#define TIVA_CANIF_MCTL_RXIE        0x00000400  /* Receive Interrupt Enable     */
+#define TIVA_CANIF_MCTL_RMTEN       0x00000200  /* Remote Enable                */
+#define TIVA_CANIF_MCTL_TXRQST      0x00000100  /* Transmit Request             */
+#define TIVA_CANIF_MCTL_EOB         0x00000080  /* End of Buffer                */
+#define TIVA_CANIF_MCTL_DLC_MASK    0x0000000F  /* Data Length Code             */
+#define TIVA_CANIF_MCTL_DLC_SHIFT   0
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANIF1D(A,B)(1,2)
+ * registers. H and L refer to high and low bytes in network byte order.
+ ****************************************************************************/
+
+#define TIVA_CANIF_DATA_HBYTE_MASK     0x000000FF
+#define TIVA_CANIF_DATA_HBYTE_SHIFT 0
+#define TIVA_CANIF_DATA_LBYTE_MASK     0x0000FF00
+#define TIVA_CANIF_DATA_LBYTE_SHIFT 8
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANTXRQ1 register.
+ ****************************************************************************/
+
+#define TIVA_CAN_TXRQ1_TXRQST_MASK  0x0000FFFF  /* Transmission Request Bits */
+#define TIVA_CAN_TXRQ1_TXRQST_SHIFT 0
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANTXRQ2 register.
+ ****************************************************************************/
+
+#define TIVA_CAN_TXRQ2_TXRQST_MASK  0x0000FFFF  /* Transmission Request Bits */
+#define TIVA_CAN_TXRQ2_TXRQST_SHIFT 0
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANNWDA1 register.
+ ****************************************************************************/
+
+#define TIVA_CAN_NWDA1_NEWDAT_MASK  0x0000FFFF  /* New Data Bits */
+#define TIVA_CAN_NWDA1_NEWDAT_SHIFT 0
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANNWDA2 register.
+ ****************************************************************************/
+
+#define TIVA_CAN_NWDA2_NEWDAT_MASK  0x0000FFFF  /* New Data Bits */
+#define TIVA_CAN_NWDA2_NEWDAT_SHIFT 0
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANMSG1INT register.
+ ****************************************************************************/
+
+#define TIVA_CAN_MSG1INT_INTPND_MASK        0x0000FFFF  /* Interrupt Pending Bits */
+#define TIVA_CAN_MSG1INT_INTPND_SHIFT       0
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANMSG2INT register.
+ ****************************************************************************/
+
+#define TIVA_CAN_MSG2INT_INTPND_MASK        0x0000FFFF  /* Interrupt Pending Bits */
+#define TIVA_CAN_MSG2INT_INTPND_SHIFT       0
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANMSG1VAL register.
+ ****************************************************************************/
+
+#define TIVA_CAN_MSG1VAL_MSGVAL_MASK        0x0000FFFF  /* Message Valid Bits */
+#define TIVA_CAN_MSG1VAL_MSGVAL_SHIFT       0
+
+/****************************************************************************
+ * The following are defines for the bit fields in the CANMSG2VAL register.
+ ****************************************************************************/
+
+#define TIVA_CAN_MSG2VAL_MSGVAL_MASK        0x0000FFFF  /* Message Valid Bits */
+#define TIVA_CAN_MSG2VAL_MSGVAL_SHIFT       0
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Inline Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#endif /* __ARCH_ARM_SRC_TIVA_HARDWARE_TIVA_CAN_H */
diff --git a/boards/arm/tiva/tm4c123g-launchpad/src/tm4c_bringup.c b/arch/arm/src/tiva/tiva_can.h
similarity index 52%
copy from boards/arm/tiva/tm4c123g-launchpad/src/tm4c_bringup.c
copy to arch/arm/src/tiva/tiva_can.h
index c141d19..443e16a 100644
--- a/boards/arm/tiva/tm4c123g-launchpad/src/tm4c_bringup.c
+++ b/arch/arm/src/tiva/tiva_can.h
@@ -1,5 +1,6 @@
 /****************************************************************************
- * boards/arm/tiva/tm4c123g-launchpad/src/tm4c_bringup.c
+ * arch/arm/src/tiva/tiva_can.h
+ * Classic (character-device) lower-half driver for the Tiva CAN modules.
  *
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -18,79 +19,71 @@
  *
  ****************************************************************************/
 
+#ifndef __ARCH_ARM_SRC_TIVA_TIVA_CAN_H
+#define __ARCH_ARM_SRC_TIVA_TIVA_CAN_H
+
 /****************************************************************************
  * Included Files
  ****************************************************************************/
 
 #include <nuttx/config.h>
+#include <nuttx/can/can.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#if defined(CONFIG_CAN) && (defined(CONFIG_TIVA_CAN0) || defined(CONFIG_TIVA_CAN1))
+
+#ifndef __ASSEMBLY__
 
-#include <syslog.h>
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
 
-#include "tm4c123g-launchpad.h"
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
 
 /****************************************************************************
- * Pre-processor Definitions
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Inline Functions
  ****************************************************************************/
 
 /****************************************************************************
- * Public Functions
+ * Public Function Prototypes
  ****************************************************************************/
 
 /****************************************************************************
- * Name: tm4c_bringup
+ * Name: tiva_can_initialize
  *
  * Description:
- *   Bring up board features
+ *   Initialize the selected CAN module
+ *
+ * Input Parameters:
+ *   Device path, a string of the form "/dev/can0" or "/dev/can1"
+ *   Module number, for chips with multiple modules (typically 0 or 1)
+ *
+ * Returned Value:
+ *   Pointer to can_dev_s (CAN device structure), or NULL on failure.
  *
  ****************************************************************************/
 
-int tm4c_bringup(void)
-{
-  int ret = OK;
-
-#ifdef CONFIG_TIVA_ADC
-  /* Initialize ADC and register the ADC driver. */
-
-  ret = tm4c_adc_setup();
-  if (ret < 0)
-    {
-      syslog(LOG_ERR, "ERROR: tm4c_adc_setup failed: %d\n", ret);
-    }
-#endif
-
-#ifdef HAVE_AT24
-  /* Initialize the AT24 driver */
-
-  ret = tm4c_at24_automount(AT24_MINOR);
-  if (ret < 0)
-    {
-      syslog(LOG_ERR, "ERROR: tm4c_at24_automount failed: %d\n", ret);
-      return ret;
-    }
-#endif /* HAVE_AT24 */
-
-#ifdef CONFIG_TIVA_TIMER
-  /* Initialize the timer driver */
-
-  ret = tiva_timer_configure();
-  if (ret < 0)
-    {
-      syslog(LOG_ERR, "ERROR: tiva_timer_configure failed: %d\n", ret);
-      return ret;
-    }
-#endif /* CONFIG_TIVA_TIMER */
+int tiva_can_initialize(FAR char *devpath, int modnum);
 
-#ifdef CONFIG_CAN_MCP2515
-  /* Configure and initialize the MCP2515 CAN device */
-
-  ret = tiva_mcp2515initialize("/dev/can0");
-  if (ret < 0)
-    {
-      syslog(LOG_ERR, "ERROR: stm32_mcp2515initialize() failed: %d\n", ret);
-      return ret;
-    }
+#undef EXTERN
+#if defined(__cplusplus)
+}
 #endif
 
-  UNUSED(ret);
-  return ret;
-}
+#endif /* __ASSEMBLY__ */
+#endif /* CONFIG_CAN && (CONFIG_TIVA_CAN0 || CONFIG_TIVA_CAN1) */
+#endif /* __ARCH_ARM_SRC_TIVA_TIVA_CAN_H */
diff --git a/boards/arm/tiva/tm4c123g-launchpad/Kconfig b/boards/arm/tiva/tm4c123g-launchpad/Kconfig
index 7a135ff..9d646ba 100644
--- a/boards/arm/tiva/tm4c123g-launchpad/Kconfig
+++ b/boards/arm/tiva/tm4c123g-launchpad/Kconfig
@@ -43,4 +43,36 @@ config TM4C123G_LAUNCHPAD_AT24_NXFFS
 
 endchoice # AT24 serial EPPROM configuration
 
+choice
+	prompt "CAN0 RX pin selection"
+	default TM4C123G_LAUNCHPAD_CAN0RX_PE4
+	depends on TIVA_CAN0
+
+config TM4C123G_LAUNCHPAD_CAN0RX_PF0
+	bool "Use pin 0 on GPIO port F"
+
+config TM4C123G_LAUNCHPAD_CAN0RX_PB4
+	bool "Use pin 4 on GPIO port B"
+
+config TM4C123G_LAUNCHPAD_CAN0RX_PE4
+	bool "Use pin 4 on GPIO port E"
+
+endchoice # CAN0 RX pin selection
+
+choice
+	prompt "CAN0 TX pin selection"
+	default TM4C123G_LAUNCHPAD_CAN0TX_PE5
+	depends on TIVA_CAN0
+
+config TM4C123G_LAUNCHPAD_CAN0TX_PF3
+	bool "Use pin 3 on GPIO port F"
+
+config TM4C123G_LAUNCHPAD_CAN0TX_PB5
+	bool "Use pin 5 on GPIO port B"
+
+config TM4C123G_LAUNCHPAD_CAN0TX_PE5
+	bool "Use pin 5 on GPIO port E"
+
+endchoice # CAN0 TX pin selection
+
 endif
diff --git a/boards/arm/tiva/tm4c123g-launchpad/include/board.h b/boards/arm/tiva/tm4c123g-launchpad/include/board.h
index d9a27f3..dc5ab3c 100644
--- a/boards/arm/tiva/tm4c123g-launchpad/include/board.h
+++ b/boards/arm/tiva/tm4c123g-launchpad/include/board.h
@@ -180,4 +180,26 @@
 #define GPIO_UART1_RX     GPIO_UART1_RX_1
 #define GPIO_UART1_TX     GPIO_UART1_TX_1
 
+/* CAN0 pin mux selection - defaults to port E in Kconfig */
+#ifdef CONFIG_TIVA_CAN0
+#  ifdef CONFIG_TM4C123G_LAUNCHPAD_CAN0RX_PF0
+#    define GPIO_CAN0_RX GPIO_CAN0_RX_1
+#  endif
+#  ifdef CONFIG_TM4C123G_LAUNCHPAD_CAN0RX_PB4
+#    define GPIO_CAN0_RX GPIO_CAN0_RX_2
+#  endif
+#  ifdef CONFIG_TM4C123G_LAUNCHPAD_CAN0RX_PE4
+#    define GPIO_CAN0_RX GPIO_CAN0_RX_3
+#  endif
+#  ifdef CONFIG_TM4C123G_LAUNCHPAD_CAN0TX_PF3
+#    define GPIO_CAN0_TX GPIO_CAN0_TX_1
+#  endif
+#  ifdef CONFIG_TM4C123G_LAUNCHPAD_CAN0TX_PB5
+#    define GPIO_CAN0_TX GPIO_CAN0_TX_2
+#  endif
+#  ifdef CONFIG_TM4C123G_LAUNCHPAD_CAN0TX_PE5
+#    define GPIO_CAN0_TX GPIO_CAN0_TX_3
+#  endif
+#endif
+
 #endif /* __BOARDS_ARM_TMC4C123G_LAUNCHPAD_INCLUDE_BOARD_H */
diff --git a/boards/arm/tiva/tm4c123g-launchpad/src/Makefile b/boards/arm/tiva/tm4c123g-launchpad/src/Makefile
index 9497bb9..daf16ed 100644
--- a/boards/arm/tiva/tm4c123g-launchpad/src/Makefile
+++ b/boards/arm/tiva/tm4c123g-launchpad/src/Makefile
@@ -38,6 +38,10 @@ ifeq ($(CONFIG_TIVA_ADC),y)
 CSRCS += tm4c_adc.c
 endif
 
+ifeq ($(CONFIG_TIVA_CAN),y)
+  CSRCS += tm4c_can.c
+endif
+
 ifeq ($(CONFIG_CAN_MCP2515),y)
   CSRCS += tm4c_mcp2515.c
 endif
diff --git a/boards/arm/tiva/tm4c123g-launchpad/src/tm4c123g-launchpad.h b/boards/arm/tiva/tm4c123g-launchpad/src/tm4c123g-launchpad.h
index 2f326f7..b29e1ec 100644
--- a/boards/arm/tiva/tm4c123g-launchpad/src/tm4c123g-launchpad.h
+++ b/boards/arm/tiva/tm4c123g-launchpad/src/tm4c123g-launchpad.h
@@ -234,6 +234,18 @@ int tm4c_adc_setup(void);
 #endif
 
 /****************************************************************************
+ * Name: tm4c_can_setup
+ *
+ * Description:
+ *   Initialize CAN modules and register the CAN driver.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_TIVA_CAN
+int tm4c_can_setup(void);
+#endif
+
+/****************************************************************************
  * Name: tm4c_at24_automount
  *
  * Description:
diff --git a/boards/arm/tiva/tm4c123g-launchpad/src/tm4c_bringup.c b/boards/arm/tiva/tm4c123g-launchpad/src/tm4c_bringup.c
index c141d19..236c208 100644
--- a/boards/arm/tiva/tm4c123g-launchpad/src/tm4c_bringup.c
+++ b/boards/arm/tiva/tm4c123g-launchpad/src/tm4c_bringup.c
@@ -58,6 +58,16 @@ int tm4c_bringup(void)
     }
 #endif
 
+#ifdef CONFIG_TIVA_CAN
+  /* Initialize CAN module and register the CAN driver(s) */
+
+  ret = tm4c_can_setup();
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: tm4c_can_setup failed %d\n", ret);
+    }
+#endif
+
 #ifdef HAVE_AT24
   /* Initialize the AT24 driver */
 
diff --git a/boards/arm/tiva/tm4c123g-launchpad/src/tm4c_can.c b/boards/arm/tiva/tm4c123g-launchpad/src/tm4c_can.c
new file mode 100644
index 0000000..82c651d
--- /dev/null
+++ b/boards/arm/tiva/tm4c123g-launchpad/src/tm4c_can.c
@@ -0,0 +1,153 @@
+/****************************************************************************
+ * boards/arm/tiva/tm4c123g-launchpad/src/tm4c_can.c
+ *
+ *   Copyright (C) 2016 Gregory Nutt. All rights reserved.
+ *   Copyright (C) 2020 Matthew Trescott
+ *   Author: Gregory Nutt <gn...@nuttx.org>
+ *
+ * Based heavily on stm32_can.c from the boards directory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/can/can.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "arm_internal.h"
+
+#include "tiva_can.h"
+#include "tm4c123g-launchpad.h"
+
+#include "tiva_enableclks.h"
+#include "tiva_gpio.h"
+#include "hardware/tiva_pinmap.h"
+
+#ifdef CONFIG_CAN
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: tm4c_can_setup
+ *
+ * Description:
+ *  Initialize CAN and register the CAN device
+ *
+ ****************************************************************************/
+
+int tm4c_can_setup(void)
+{
+#ifdef CONFIG_TIVA_CAN
+  int ret;
+
+#  ifdef CONFIG_TIVA_CAN0
+  tiva_can0_enableclk();
+
+  ret = tiva_configgpio(GPIO_CAN0_RX);
+
+  if (ret < 0)
+    {
+      goto configgpio_error;
+    }
+
+  ret = tiva_configgpio(GPIO_CAN0_TX);
+
+  if (ret < 0)
+    {
+      goto configgpio_error;
+    }
+
+  /* Call tiva_can_initialize() to get an instance of CAN interface 0
+   * and register it.
+   */
+
+  ret = tiva_can_initialize("/dev/can0", 0);
+  if (ret < 0)
+    {
+      canerr("ERROR:  Failed to get/register CAN interface 0\n");
+      return ret;
+    }
+#  endif /* CONFIG_TIVA_CAN0 */
+
+#  ifdef CONFIG_TIVA_CAN1
+  tiva_can1_enableclk();
+
+  ret = tiva_configgpio(GPIO_CAN1_RX);
+
+  if (ret < 0)
+    {
+      goto configgpio_error;
+    }
+
+  ret = tiva_configgpio(GPIO_CAN1_TX);
+
+  if (ret < 0)
+    {
+      goto configgpio_error;
+    }
+
+  /* Call tiva_can_initialize() to get an instance of CAN interface 1
+   * and register it.
+   */
+
+  ret = tiva_can_initialize("/dev/can1", 1);
+  if (ret < 0)
+    {
+      canerr("ERROR:  Failed to get/register CAN interface 1\n");
+      return ret;
+    }
+#  endif /* CONFIG_TIVA_CAN1 */
+
+  return OK;
+
+configgpio_error:
+  canerr("ERROR: failed to configure CAN GPIO pin.\n");
+  return ret;
+#else
+  return -ENODEV;
+#endif
+}
+
+#endif /* CONFIG_CAN */
+