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:41 UTC

[incubator-nuttx] branch master updated (0df3139 -> 8c471db)

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

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


    from 0df3139  drivers/timers: rename oneshot to periodic notification parameter
     new bc80bbd  Add Tiva CAN driver
     new 8c471db  Corrections to Tiva KConfig

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 arch/arm/src/tiva/Kconfig                          |  151 +-
 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 ++++
 arch/arm/src/{sama5/sam_ssc.h => tiva/tiva_can.h}  |   50 +-
 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 +
 .../tm4c123g-launchpad/src/tm4c_can.c}             |  106 +-
 11 files changed, 3206 insertions(+), 53 deletions(-)
 create mode 100644 arch/arm/src/tiva/common/tiva_can.c
 create mode 100644 arch/arm/src/tiva/hardware/tiva_can.h
 copy arch/arm/src/{sama5/sam_ssc.h => tiva/tiva_can.h} (77%)
 copy boards/arm/{stm32l4/nucleo-l476rg/src/stm32_as726x.c => tiva/tm4c123g-launchpad/src/tm4c_can.c} (58%)

[incubator-nuttx] 02/02: Corrections to Tiva KConfig

Posted by ha...@apache.org.
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 8c471db932d7c76e64c3ffca145752329d6909e1
Author: Matthew Trescott <ma...@gmail.com>
AuthorDate: Mon Mar 14 18:08:24 2022 -0400

    Corrections to Tiva KConfig
---
 arch/arm/src/tiva/Kconfig | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/arch/arm/src/tiva/Kconfig b/arch/arm/src/tiva/Kconfig
index 4558c85..3d344b5 100644
--- a/arch/arm/src/tiva/Kconfig
+++ b/arch/arm/src/tiva/Kconfig
@@ -97,6 +97,7 @@ config ARCH_CHIP_TM4C123AH6PM
 	select TIVA_HAVE_EEPROM
 	select TIVA_HAVE_I2C4
 	select TIVA_HAVE_I2C5
+	select TIVA_HAVE_CAN1
 	select TIVA_HAVE_QEI0
 	select TIVA_HAVE_QEI1
 
@@ -109,6 +110,7 @@ config ARCH_CHIP_TM4C123GH6ZRB
 	select TIVA_HAVE_GPIOQ_IRQS
 	select TIVA_HAVE_I2C4
 	select TIVA_HAVE_I2C5
+	select TIVA_HAVE_CAN1
 	select TIVA_HAVE_QEI0
 	select TIVA_HAVE_QEI1
 
@@ -146,6 +148,7 @@ config ARCH_CHIP_TM4C123GH6PZ
 	select TIVA_HAVE_GPIOK_IRQS
 	select TIVA_HAVE_ADC0
 	select TIVA_HAVE_ADC1
+	select TIVA_HAVE_CAN1
 	select TIVA_HAVE_QEI0
 	select TIVA_HAVE_QEI1
 
@@ -170,10 +173,10 @@ config ARCH_CHIP_TM4C123GH6PGE
 	select TIVA_HAVE_GPIOP_IRQS
 	select TIVA_HAVE_ADC0
 	select TIVA_HAVE_ADC1
+	select TIVA_HAVE_CAN1
 	select TIVA_HAVE_QEI0
 	select TIVA_HAVE_QEI1
 
-config ARCH_CHIP_TM4C123GH6PGE
 config ARCH_CHIP_TM4C1294NCPDT
 	bool "TM4C1294NCPDT"
 	depends on ARCH_CHIP_TIVA
@@ -190,6 +193,7 @@ config ARCH_CHIP_TM4C1294NCPDT
 	select TIVA_HAVE_GPION_IRQS
 	select TIVA_HAVE_GPIOP_IRQS
 	select TIVA_HAVE_GPIOQ_IRQS
+	select TIVA_HAVE_CAN1
 	select TIVA_HAVE_QEI0
 
 config ARCH_CHIP_TM4C129ENCPDT
@@ -208,6 +212,7 @@ config ARCH_CHIP_TM4C129ENCPDT
 	select TIVA_HAVE_GPION_IRQS
 	select TIVA_HAVE_GPIOP_IRQS
 	select TIVA_HAVE_GPIOQ_IRQS
+	select TIVA_HAVE_CAN1
 	select TIVA_HAVE_QEI0
 
 config ARCH_CHIP_TM4C129ENCZAD
@@ -229,6 +234,7 @@ config ARCH_CHIP_TM4C129ENCZAD
 	select TIVA_HAVE_GPIOR_IRQS
 	select TIVA_HAVE_GPIOS_IRQS
 	select TIVA_HAVE_GPIOT_IRQS
+	select TIVA_HAVE_CAN1
 	select TIVA_HAVE_QEI0
 
 config ARCH_CHIP_TM4C129XNCZAD
@@ -249,6 +255,7 @@ config ARCH_CHIP_TM4C129XNCZAD
 	select TIVA_HAVE_GPIOR_IRQS
 	select TIVA_HAVE_GPIOS_IRQS
 	select TIVA_HAVE_GPIOT_IRQS
+	select TIVA_HAVE_CAN1
 	select TIVA_HAVE_QEI0
 
 config ARCH_CHIP_CC1310

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

Posted by ha...@apache.org.
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 */
+