On 25.11.21 00:46, Dario Binacchi wrote:
> Bosch C_CAN controller is a full-CAN implementation which is compliant
> to CAN protocol version 2.0 part A and B.
> 
> Starting from the RTDM porting [1] by Stephen Battazzo, Michael applied
> some fixes for proper driver operation. Then I ported the commits from
> Linux kernel version 5.12 and finally I added support for 64 message
> objects [2] and improved the FIFO management [2] to get the best
> possible RX/TX throughput. Features [2] and [3] led to the creation of
> patches applied to the Linux kernel too.
> 
> The patch was tested on a custom board mounting an AM335x SOC.
> 
> PCI support has not been developed.
> 
> [1] http://xenomai.org/pipermail/xenomai/2015-July/034690.html
> [2] 132f2d45fb23 ("can: c_can: add support to 64 message objects")
> [3] 387da6bc7a82 ("can: c_can: cache frames to operate as a true FIFO")
> 
> Signed-off-by: Stephen J. Battazzo <stephen.j.batta...@nasa.gov>
> Signed-off-by: Michael Trimarchi <mich...@amarulasolutions.com>
> Signed-off-by: Dario Binacchi <dario...@libero.it>
> Signed-off-by: Gianluca Falavigna <gianluca.falavi...@inwind.it>
> Tested-by: Gianluca Falavigna <gianluca.falavi...@inwind.it>
> 
> ---
> 
>  kernel/drivers/can/Kconfig                    |    1 +
>  kernel/drivers/can/Makefile                   |    2 +-
>  kernel/drivers/can/README                     |    1 +
>  kernel/drivers/can/c_can/Kconfig              |   16 +
>  kernel/drivers/can/c_can/Makefile             |    8 +
>  kernel/drivers/can/c_can/rtcan_c_can.c        | 1388 +++++++++++++++++
>  kernel/drivers/can/c_can/rtcan_c_can.h        |  258 +++
>  .../drivers/can/c_can/rtcan_c_can_ethtool.c   |   47 +
>  .../drivers/can/c_can/rtcan_c_can_platform.c  |  461 ++++++
>  9 files changed, 2181 insertions(+), 1 deletion(-)
>  create mode 100644 kernel/drivers/can/c_can/Kconfig
>  create mode 100644 kernel/drivers/can/c_can/Makefile
>  create mode 100644 kernel/drivers/can/c_can/rtcan_c_can.c
>  create mode 100644 kernel/drivers/can/c_can/rtcan_c_can.h
>  create mode 100644 kernel/drivers/can/c_can/rtcan_c_can_ethtool.c
>  create mode 100644 kernel/drivers/can/c_can/rtcan_c_can_platform.c
> 
> diff --git a/kernel/drivers/can/Kconfig b/kernel/drivers/can/Kconfig
> index 1c055494d..c9ff36d94 100644
> --- a/kernel/drivers/can/Kconfig
> +++ b/kernel/drivers/can/Kconfig
> @@ -85,6 +85,7 @@ config XENO_DRIVERS_CAN_FLEXCAN
>  
>       Say Y here if you want to support for Freescale FlexCAN.
>  
> +source "drivers/xenomai/can/c_can/Kconfig"
>  source "drivers/xenomai/can/mscan/Kconfig"
>  source "drivers/xenomai/can/peak_canfd/Kconfig"
>  source "drivers/xenomai/can/sja1000/Kconfig"
> diff --git a/kernel/drivers/can/Makefile b/kernel/drivers/can/Makefile
> index 65761a010..87bd0d070 100644
> --- a/kernel/drivers/can/Makefile
> +++ b/kernel/drivers/can/Makefile
> @@ -1,7 +1,7 @@
>  
>  ccflags-y += -I$(srctree)/drivers/xenomai/can
>  
> -obj-$(CONFIG_XENO_DRIVERS_CAN) += xeno_can.o mscan/ sja1000/ peak_canfd/
> +obj-$(CONFIG_XENO_DRIVERS_CAN) += xeno_can.o mscan/ sja1000/ peak_canfd/ 
> c_can/
>  obj-$(CONFIG_XENO_DRIVERS_CAN_FLEXCAN) += xeno_can_flexcan.o
>  obj-$(CONFIG_XENO_DRIVERS_CAN_VIRT) += xeno_can_virt.o
>  
> diff --git a/kernel/drivers/can/README b/kernel/drivers/can/README
> index cb0ef37ae..44aff9b1c 100644
> --- a/kernel/drivers/can/README
> +++ b/kernel/drivers/can/README
> @@ -20,6 +20,7 @@ devices:
>     SJA1000 PEAK parallel port Dongle
>     SJA1000 IXXAT PCI card
>     MSCAN for MPC5200 boards
> +   C_CAN/D_CAN for AM335x/AM437x Sitara processors and DRA7 SOC
>  
>  Utilities for RT-Socket-CAN are available in "src/utils/can".
>  
> diff --git a/kernel/drivers/can/c_can/Kconfig 
> b/kernel/drivers/can/c_can/Kconfig
> new file mode 100644
> index 000000000..dda5f7517
> --- /dev/null
> +++ b/kernel/drivers/can/c_can/Kconfig
> @@ -0,0 +1,16 @@
> +config XENO_DRIVERS_CAN_C_CAN
> +     depends on XENO_DRIVERS_CAN && OF
> +     tristate "Bosch C-CAN based chips"
> +     default n

"default n" is already default.

> +
> +config XENO_DRIVERS_CAN_C_CAN_PLATFORM
> +     depends on XENO_DRIVERS_CAN_C_CAN
> +     tristate "Generic Platform Bus based controller"
> +     help
> +
> +     This driver adds support for the C_CAN/D_CAN chips connected
> +     to the "platform bus" (Linux abstraction for directly to the
> +     processor attached devices) which can be found on various
> +     boards from ST Microelectronics (http://www.st.com) like the
> +     SPEAr1310 and SPEAr320 evaluation boards & TI (www.ti.com)
> +     boards like am335x, dm814x, dm813x and dm811x.

Please enable the driver also for our CI builds, on targets where it can
be compiled.

> diff --git a/kernel/drivers/can/c_can/Makefile 
> b/kernel/drivers/can/c_can/Makefile
> new file mode 100644
> index 000000000..888e51a10
> --- /dev/null
> +++ b/kernel/drivers/can/c_can/Makefile
> @@ -0,0 +1,8 @@
> +
> +ccflags-y += -Idrivers/xenomai/can -Idrivers/xenomai/can/c_can
> +
> +obj-$(CONFIG_XENO_DRIVERS_CAN_C_CAN) += xeno_can_c_can.o
> +obj-$(CONFIG_XENO_DRIVERS_CAN_C_CAN_PLATFORM) += xeno_can_c_can_platform.o
> +
> +xeno_can_c_can-y := rtcan_c_can.o rtcan_c_can_ethtool.o
> +xeno_can_c_can_platform-y := rtcan_c_can_platform.o
> diff --git a/kernel/drivers/can/c_can/rtcan_c_can.c 
> b/kernel/drivers/can/c_can/rtcan_c_can.c
> new file mode 100644
> index 000000000..d327336cd
> --- /dev/null
> +++ b/kernel/drivers/can/c_can/rtcan_c_can.c
> @@ -0,0 +1,1388 @@
> +/*
> + * CAN bus driver for Bosch C_CAN controller, ported to Xenomai RTDM
> + *
> + * Copyright 2021, Dario Binacchi <dario...@libero.it>
> + *
> + * Stephen J. Battazzo <stephen.j.batta...@nasa.gov>,
> + * MEI Services/NASA Ames Research Center
> + *
> + * Borrowed original driver from:
> + *
> + * Bhupesh Sharma <bhupesh.sha...@st.com>, ST Microelectronics
> + * Borrowed heavily from the C_CAN driver originally written by:
> + * Copyright (C) 2007
> + * - Sascha Hauer, Marc Kleine-Budde, Pengutronix <s.ha...@pengutronix.de>
> + * - Simon Kallweit, intefo AG <simon.kallw...@intefo.ch>
> + *
> + * TX and RX NAPI implementation has been removed and replaced with RT 
> Socket CAN implementation.
> + * RT Socket CAN implementation inspired by Flexcan RTDM port by Wolfgang 
> Grandegger <w...@denx.de>
> + *
> + * Bosch C_CAN controller is compliant to CAN protocol version 2.0 part A 
> and B.
> + * Bosch C_CAN user manual can be obtained from:
> + * http://www.semiconductors.bosch.de/media/en/pdf/ipmodules_1/c_can/
> + * users_manual_c_can.pdf
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/if_arp.h>
> +#include <linux/if_ether.h>
> +#include <linux/list.h>
> +#include <linux/io.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/pinctrl/consumer.h>
> +#ifdef CONFIG_OF
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#endif
> +#include <linux/clk.h>
> +#include <linux/platform_device.h>
> +#include <linux/pinctrl/consumer.h>
> +
> +#include <rtdm/driver.h>
> +
> +/* CAN device profile */
> +#include <rtdm/can.h>
> +
> +#include "rtcan_dev.h"
> +#include "rtcan_raw.h"
> +#include "rtcan_internal.h"
> +
> +#include "rtcan_c_can.h"
> +
> +//borrowed this from linux/can/dev.h for now:
> +#define CAN_MAX_DLEN 8
> +/*
> + * can_cc_dlc2len(value) - convert a given data length code (dlc) of a
> + * Classical CAN frame into a valid data length of max. 8 bytes.
> + *
> + * To be used in the CAN netdriver receive path to ensure conformance with
> + * ISO 11898-1 Chapter 8.4.2.3 (DLC field)
> + */
> +#define can_cc_dlc2len(dlc)  (min_t(u8, (dlc), CAN_MAX_DLEN))
> +
> +/* Number of interface registers */
> +#define IF_ENUM_REG_LEN              11
> +#define C_CAN_IFACE(reg, iface)      (C_CAN_IF1_##reg + (iface) * 
> IF_ENUM_REG_LEN)
> +
> +/* control extension register D_CAN specific */
> +#define CONTROL_EX_PDR               BIT(8)
> +
> +/* control register */
> +#define CONTROL_SWR          BIT(15)
> +#define CONTROL_TEST         BIT(7)
> +#define CONTROL_CCE          BIT(6)
> +#define CONTROL_DISABLE_AR   BIT(5)
> +#define CONTROL_ENABLE_AR    (0 << 5)
> +#define CONTROL_EIE          BIT(3)
> +#define CONTROL_SIE          BIT(2)
> +#define CONTROL_IE           BIT(1)
> +#define CONTROL_INIT         BIT(0)
> +
> +#define CONTROL_IRQMSK               (CONTROL_EIE | CONTROL_IE | CONTROL_SIE)
> +
> +/* test register */
> +#define TEST_RX                      BIT(7)
> +#define TEST_TX1             BIT(6)
> +#define TEST_TX2             BIT(5)
> +#define TEST_LBACK           BIT(4)
> +#define TEST_SILENT          BIT(3)
> +#define TEST_BASIC           BIT(2)
> +
> +/* status register */
> +#define STATUS_PDA           BIT(10)
> +#define STATUS_BOFF          BIT(7)
> +#define STATUS_EWARN         BIT(6)
> +#define STATUS_EPASS         BIT(5)
> +#define STATUS_RXOK          BIT(4)
> +#define STATUS_TXOK          BIT(3)
> +
> +/* error counter register */
> +#define ERR_CNT_TEC_MASK     0xff
> +#define ERR_CNT_TEC_SHIFT    0
> +#define ERR_CNT_REC_SHIFT    8
> +#define ERR_CNT_REC_MASK     (0x7f << ERR_CNT_REC_SHIFT)
> +#define ERR_CNT_RP_SHIFT     15
> +#define ERR_CNT_RP_MASK              (0x1 << ERR_CNT_RP_SHIFT)
> +
> +/* bit-timing register */
> +#define BTR_BRP_MASK         0x3f
> +#define BTR_BRP_SHIFT                0
> +#define BTR_SJW_SHIFT                6
> +#define BTR_SJW_MASK         (0x3 << BTR_SJW_SHIFT)
> +#define BTR_TSEG1_SHIFT              8
> +#define BTR_TSEG1_MASK               (0xf << BTR_TSEG1_SHIFT)
> +#define BTR_TSEG2_SHIFT              12
> +#define BTR_TSEG2_MASK               (0x7 << BTR_TSEG2_SHIFT)
> +
> +/* interrupt register */
> +#define INT_STS_PENDING              0x8000
> +
> +/* brp extension register */
> +#define BRP_EXT_BRPE_MASK    0x0f
> +#define BRP_EXT_BRPE_SHIFT   0
> +
> +/* IFx command request */
> +#define IF_COMR_BUSY         BIT(15)
> +
> +/* IFx command mask */
> +#define IF_COMM_WR           BIT(7)
> +#define IF_COMM_MASK         BIT(6)
> +#define IF_COMM_ARB          BIT(5)
> +#define IF_COMM_CONTROL              BIT(4)
> +#define IF_COMM_CLR_INT_PND  BIT(3)
> +#define IF_COMM_TXRQST               BIT(2)
> +#define IF_COMM_CLR_NEWDAT   IF_COMM_TXRQST
> +#define IF_COMM_DATAA                BIT(1)
> +#define IF_COMM_DATAB                BIT(0)
> +
> +/* TX buffer setup */
> +#define IF_COMM_TX           (IF_COMM_ARB | IF_COMM_CONTROL | \
> +                              IF_COMM_TXRQST |                \
> +                              IF_COMM_DATAA | IF_COMM_DATAB)
> +
> +#define IF_COMM_TX_FRAME     (IF_COMM_ARB | IF_COMM_CONTROL | \
> +                              IF_COMM_DATAA | IF_COMM_DATAB)
> +
> +/* For the low buffers we clear the interrupt bit, but keep newdat */
> +#define IF_COMM_RCV_LOW              (IF_COMM_MASK | IF_COMM_ARB | \
> +                              IF_COMM_CONTROL | IF_COMM_CLR_INT_PND | \
> +                              IF_COMM_DATAA | IF_COMM_DATAB)
> +
> +/* For the high buffers we clear the interrupt bit and newdat */
> +#define IF_COMM_RCV_HIGH     (IF_COMM_RCV_LOW | IF_COMM_CLR_NEWDAT)
> +
> +/* Receive setup of message objects */
> +#define IF_COMM_RCV_SETUP    (IF_COMM_MASK | IF_COMM_ARB | IF_COMM_CONTROL)
> +
> +/* Invalidation of message objects */
> +#define IF_COMM_INVAL                (IF_COMM_ARB | IF_COMM_CONTROL)
> +
> +/* IFx arbitration */
> +#define IF_ARB_MSGVAL                BIT(31)
> +#define IF_ARB_MSGXTD                BIT(30)
> +#define IF_ARB_TRANSMIT              BIT(29)
> +
> +/* IFx message control */
> +#define IF_MCONT_NEWDAT              BIT(15)
> +#define IF_MCONT_MSGLST              BIT(14)
> +#define IF_MCONT_INTPND              BIT(13)
> +#define IF_MCONT_UMASK               BIT(12)
> +#define IF_MCONT_TXIE                BIT(11)
> +#define IF_MCONT_RXIE                BIT(10)
> +#define IF_MCONT_RMTEN               BIT(9)
> +#define IF_MCONT_TXRQST              BIT(8)
> +#define IF_MCONT_EOB         BIT(7)
> +#define IF_MCONT_DLC_MASK    0xf
> +
> +#define IF_MCONT_RCV         (IF_MCONT_RXIE | IF_MCONT_UMASK)
> +#define IF_MCONT_RCV_EOB     (IF_MCONT_RCV | IF_MCONT_EOB)
> +
> +#define IF_MCONT_TX          (IF_MCONT_TXIE | IF_MCONT_EOB)
> +
> +/*
> + * Use IF1 for RX and IF2 for TX
> + */
> +#define IF_RX                        0
> +#define IF_TX                        1
> +
> +/* minimum timeout for checking BUSY status */
> +#define MIN_TIMEOUT_VALUE    6
> +
> +/* Wait for ~1 sec for INIT bit */
> +#define INIT_WAIT_MS         1000
> +
> +/* c_can lec values */
> +enum c_can_lec_type {
> +     LEC_NO_ERROR = 0,
> +     LEC_STUFF_ERROR,
> +     LEC_FORM_ERROR,
> +     LEC_ACK_ERROR,
> +     LEC_BIT1_ERROR,
> +     LEC_BIT0_ERROR,
> +     LEC_CRC_ERROR,
> +     LEC_UNUSED,
> +     LEC_MASK = LEC_UNUSED,
> +};
> +
> +static const struct can_bittiming_const c_can_bittiming_const = {
> +     .name = DRV_NAME,
> +     .tseg1_min = 2,         /* Time segment 1 = prop_seg + phase_seg1 */
> +     .tseg1_max = 16,
> +     .tseg2_min = 1,         /* Time segment 2 = phase_seg2 */
> +     .tseg2_max = 8,
> +     .sjw_max = 4,
> +     .brp_min = 1,
> +     .brp_max = 1024,        /* 6-bit BRP field + 4-bit BRPE field */
> +     .brp_inc = 1,
> +};
> +
> +static inline void c_can_pm_runtime_enable(const struct c_can_priv *priv)
> +{
> +     if (priv->device)
> +             pm_runtime_enable(priv->device);
> +}
> +
> +static inline void c_can_pm_runtime_disable(const struct c_can_priv *priv)
> +{
> +     if (priv->device)
> +             pm_runtime_disable(priv->device);
> +}
> +
> +static inline void c_can_pm_runtime_get_sync(const struct c_can_priv *priv)
> +{
> +     if (priv->device)
> +             pm_runtime_get_sync(priv->device);
> +}
> +
> +static inline void c_can_pm_runtime_put_sync(const struct c_can_priv *priv)
> +{
> +     if (priv->device)
> +             pm_runtime_put_sync(priv->device);
> +}
> +
> +static inline void c_can_reset_ram(const struct c_can_priv *priv, bool 
> enable)
> +{
> +     if (priv->raminit)
> +             priv->raminit(priv, enable);
> +}
> +
> +static void c_can_irq_control(struct c_can_priv *priv, bool enable)
> +{
> +     u32 ctrl = priv->read_reg(priv, C_CAN_CTRL_REG) & ~CONTROL_IRQMSK;
> +
> +     if (enable)
> +             ctrl |= CONTROL_IRQMSK;
> +
> +     priv->write_reg(priv, C_CAN_CTRL_REG, ctrl);
> +}
> +
> +static void c_can_obj_update(struct rtcan_device *dev, int iface, u32 cmd,
> +                          u32 obj)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +     int cnt, reg = C_CAN_IFACE(COMREQ_REG, iface);
> +
> +     priv->write_reg32(priv, reg, (cmd << 16) | obj);
> +
> +     for (cnt = MIN_TIMEOUT_VALUE; cnt; cnt--) {
> +             if (!(priv->read_reg(priv, reg) & IF_COMR_BUSY))
> +                     return;
> +             udelay(1);
> +     }
> +     rtcandev_err(dev, "Updating object timed out\n");
> +
> +}
> +
> +static inline void c_can_object_get(struct rtcan_device *dev, int iface,
> +                                 u32 obj, u32 cmd)
> +{
> +     c_can_obj_update(dev, iface, cmd, obj);
> +}
> +
> +static inline void c_can_object_put(struct rtcan_device *dev, int iface,
> +                                 u32 obj, u32 cmd)
> +{
> +     c_can_obj_update(dev, iface, cmd | IF_COMM_WR, obj);
> +}
> +
> +/*
> + * Note: According to documentation clearing TXIE while MSGVAL is set
> + * is not allowed, but works nicely on C/DCAN. And that lowers the I/O
> + * load significantly.
> + */
> +static void c_can_inval_tx_object(struct rtcan_device *dev, int iface, int 
> obj)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +
> +     priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), 0);
> +     c_can_object_put(dev, iface, obj, IF_COMM_INVAL);
> +}
> +
> +static void c_can_inval_msg_object(struct rtcan_device *dev, int iface, int 
> obj)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +
> +     priv->write_reg32(priv, C_CAN_IFACE(ARB1_REG, iface), 0);
> +     c_can_inval_tx_object(dev, iface, obj);
> +     //rtcandev_dbg(dev, "invalidate obj no:%d, msgval:0x%08x\n", objno,
> +     //              c_can_read_reg32(priv, C_CAN_MSGVAL1_REG));

Can be dropped? Please don't leave dead code behind after debugging.

> +}
> +
> +static void c_can_setup_tx_object(struct rtcan_device *dev, int iface,
> +                               struct can_frame *frame, int idx)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +     u16 ctrl = IF_MCONT_TX | frame->len;
> +     bool rtr = frame->can_id & CAN_RTR_FLAG;
> +     u32 arb = IF_ARB_MSGVAL;
> +     int i;
> +
> +     if (frame->can_id & CAN_EFF_FLAG) {
> +             arb |= frame->can_id & CAN_EFF_MASK;
> +             arb |= IF_ARB_MSGXTD;
> +     } else {
> +             arb |= (frame->can_id & CAN_SFF_MASK) << 18;
> +     }
> +
> +     if (!rtr)
> +             arb |= IF_ARB_TRANSMIT;
> +
> +     /*
> +      * If we change the DIR bit, we need to invalidate the buffer
> +      * first, i.e. clear the MSGVAL flag in the arbiter.
> +      */
> +     if (rtr != (bool)test_bit(idx, &priv->tx_dir)) {
> +             u32 obj = idx + priv->msg_obj_tx_first;
> +
> +             c_can_inval_msg_object(dev, iface, obj);
> +             change_bit(idx, &priv->tx_dir);
> +     }
> +
> +     priv->write_reg32(priv, C_CAN_IFACE(ARB1_REG, iface), arb);
> +
> +     priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), ctrl);
> +
> +     if (priv->type == BOSCH_D_CAN) {
> +             u32 data = 0, dreg = C_CAN_IFACE(DATA1_REG, iface);
> +
> +             for (i = 0; i < frame->len; i += 4, dreg += 2) {
> +                     data = (u32) frame->data[i];
> +                     data |= (u32) frame->data[i + 1] << 8;
> +                     data |= (u32) frame->data[i + 2] << 16;
> +                     data |= (u32) frame->data[i + 3] << 24;
> +                     priv->write_reg32(priv, dreg, data);
> +             }
> +     } else {
> +
> +             for (i = 0; i < frame->len; i += 2) {
> +                     priv->write_reg(priv,
> +                                     C_CAN_IFACE(DATA1_REG, iface) + i / 2,
> +                                     frame->data[i] |
> +                                     (frame->data[i + 1] << 8));
> +             }
> +     }
> +}
> +
> +static int c_can_handle_lost_msg_obj(struct rtcan_device *dev, int iface,
> +                                  int objno, u32 ctrl, struct rtcan_skb *skb)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +     struct rtcan_rb_frame *cf = &skb->rb_frame;
> +
> +     rtcandev_err(dev, "msg lost in buffer %d\n", objno);
> +
> +     ctrl &= ~(IF_MCONT_MSGLST | IF_MCONT_INTPND | IF_MCONT_NEWDAT);
> +     priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), ctrl);
> +     c_can_object_put(dev, iface, objno, IF_COMM_CONTROL);
> +
> +     cf->can_id |= CAN_ERR_CRTL;
> +     cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> +     return 1;
> +}
> +
> +static int c_can_read_msg_object(struct rtcan_device *dev, int iface, u32 
> ctrl,
> +                              struct rtcan_skb *skb)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +     struct rtcan_rb_frame *frame = &skb->rb_frame;
> +     u32 arb, data;
> +
> +     frame->len = can_cc_dlc2len(ctrl & 0x0F);
> +     frame->can_ifindex = dev->ifindex;
> +
> +     arb = priv->read_reg32(priv, C_CAN_IFACE(ARB1_REG, iface));
> +
> +     if (arb & IF_ARB_MSGXTD)
> +             frame->can_id = (arb & CAN_EFF_MASK) | CAN_EFF_FLAG;
> +     else
> +             frame->can_id = (arb >> 18) & CAN_SFF_MASK;
> +
> +     if (arb & IF_ARB_TRANSMIT) {
> +             frame->can_id |= CAN_RTR_FLAG;
> +             skb->rb_frame_size = EMPTY_RB_FRAME_SIZE;
> +     } else {
> +             int i, dreg = C_CAN_IFACE(DATA1_REG, iface);
> +
> +             if (priv->type == BOSCH_D_CAN) {
> +                     for (i = 0; i < frame->len; i += 4, dreg += 2) {
> +                             data = priv->read_reg32(priv, dreg);
> +                             frame->data[i] = data;
> +                             frame->data[i + 1] = data >> 8;
> +                             frame->data[i + 2] = data >> 16;
> +                             frame->data[i + 3] = data >> 24;
> +                     }
> +             } else {
> +
> +                     for (i = 0; i < frame->len; i += 2, dreg++) {
> +                             data = priv->read_reg(priv, dreg);
> +                             frame->data[i] = data;
> +                             frame->data[i + 1] = data >> 8;
> +                     }
> +             }
> +
> +             skb->rb_frame_size = EMPTY_RB_FRAME_SIZE + frame->len;
> +     }
> +
> +     return 0;
> +}
> +
> +static void c_can_setup_receive_object(struct rtcan_device *dev, int iface,
> +                                    u32 obj, u32 mask, u32 id, u32 mcont)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +
> +     mask |= BIT(29);
> +     priv->write_reg32(priv, C_CAN_IFACE(MASK1_REG, iface), mask);
> +
> +     id |= IF_ARB_MSGVAL;
> +     priv->write_reg32(priv, C_CAN_IFACE(ARB1_REG, iface), id);
> +
> +     priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), mcont);
> +     c_can_object_put(dev, iface, obj, IF_COMM_RCV_SETUP);
> +
> +     //rtcandev_dbg(dev, "setup obj no:%d, msgval:0x%08x\n", objno,
> +     //              c_can_read_reg32(priv, C_CAN_MSGVAL1_REG));

See above.

> +}
> +
> +static int c_can_start_xmit(struct rtcan_device *dev, struct can_frame *cf)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +     u32 idx, obj, tx_active, tx_cached;
> +     rtdm_lockctx_t lock_ctx;
> +
> +     rtdm_lock_get_irqsave(&priv->tx_cached_lock, lock_ctx);
> +
> +     tx_active = atomic_read(&priv->tx_active);
> +     tx_cached = atomic_read(&priv->tx_cached);
> +     idx = fls(tx_active);
> +     if (idx > priv->msg_obj_tx_num - 1) {
> +             idx = fls(tx_cached);
> +             if (idx > priv->msg_obj_tx_num - 1) {
> +                     rtdm_lock_put_irqrestore(&priv->tx_cached_lock,
> +                                              lock_ctx);
> +                     rtcandev_err(dev, "FIFO full\n");
> +                     return -EIO;
> +             }
> +
> +             obj = idx + priv->msg_obj_tx_first;
> +             /* prepare message object for transmission */
> +             c_can_setup_tx_object(dev, IF_TX, cf, idx);
> +             c_can_object_put(dev, IF_TX, obj, IF_COMM_TX_FRAME);
> +             priv->dlc[idx] = cf->len;
> +             atomic_add((1 << idx), &priv->tx_cached);
> +             rtdm_lock_put_irqrestore(&priv->tx_cached_lock, lock_ctx);
> +             return 0;
> +     }
> +
> +     rtdm_lock_put_irqrestore(&priv->tx_cached_lock, lock_ctx);
> +
> +     obj = idx + priv->msg_obj_tx_first;
> +
> +     /* prepare message object for transmission */
> +     c_can_setup_tx_object(dev, IF_TX, cf, idx);
> +     priv->dlc[idx] = cf->len;
> +
> +     /* Update the active bits */
> +     atomic_add((1 << idx), &priv->tx_active);
> +     /* Start transmission */
> +     c_can_object_put(dev, IF_TX, obj, IF_COMM_TX);
> +     return 0;
> +}
> +
> +static int c_can_wait_for_ctrl_init(struct rtcan_device *dev,
> +                                 struct c_can_priv *priv, u32 init)
> +{
> +     int retry = 0;
> +
> +     while (init != (priv->read_reg(priv, C_CAN_CTRL_REG) & CONTROL_INIT)) {
> +             udelay(10);

This will never run under any lock or from IRQ handler context?

> +             if (retry++ > 1000) {
> +                     rtcandev_err(dev, "CCTRL: set CONTROL_INIT failed\n");
> +                     return -EIO;
> +             }
> +     }
> +     return 0;
> +}
> +
> +static int c_can_set_bittiming(struct rtcan_device *dev)
> +{
> +     unsigned int reg_btr, reg_brpe, ctrl_save;
> +     u8 brp, brpe, sjw, tseg1, tseg2;
> +     u32 ten_bit_brp;
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +     struct can_bittime *bt = &priv->bit_time;
> +     int res;
> +
> +     /* c_can provides a 6-bit brp and 4-bit brpe fields */
> +     ten_bit_brp = bt->std.brp - 1;
> +     brp = ten_bit_brp & BTR_BRP_MASK;
> +     brpe = ten_bit_brp >> 6;
> +
> +     sjw = bt->std.sjw - 1;
> +     tseg1 = bt->std.prop_seg + bt->std.phase_seg1 - 1;
> +     tseg2 = bt->std.phase_seg2 - 1;
> +     reg_btr = brp | (sjw << BTR_SJW_SHIFT) | (tseg1 << BTR_TSEG1_SHIFT) |
> +         (tseg2 << BTR_TSEG2_SHIFT);
> +     reg_brpe = brpe & BRP_EXT_BRPE_MASK;
> +
> +     rtcandev_info(dev, "setting BTR=%04x BRPE=%04x\n", reg_btr, reg_brpe);
> +
> +     ctrl_save = priv->read_reg(priv, C_CAN_CTRL_REG);
> +     ctrl_save &= ~CONTROL_INIT;
> +     priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_CCE | CONTROL_INIT);
> +     res = c_can_wait_for_ctrl_init(dev, priv, CONTROL_INIT);
> +     if (res)
> +             return res;
> +
> +     priv->write_reg(priv, C_CAN_BTR_REG, reg_btr);
> +     priv->write_reg(priv, C_CAN_BRPEXT_REG, reg_brpe);
> +     priv->write_reg(priv, C_CAN_CTRL_REG, ctrl_save);
> +
> +     return c_can_wait_for_ctrl_init(dev, priv, 0);
> +}
> +
> +/*
> + * Configure C_CAN message objects for Tx and Rx purposes:
> + * C_CAN provides a total of 32 message objects that can be configured
> + * either for Tx or Rx purposes. Here the first 16 message objects are used 
> as
> + * a reception FIFO. The end of reception FIFO is signified by the EoB bit
> + * being SET. The remaining 16 message objects are kept aside for Tx 
> purposes.
> + * See user guide document for further details on configuring message
> + * objects.
> + */
> +static void c_can_configure_msg_objects(struct rtcan_device *dev)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +     int i;
> +
> +     /* first invalidate all message objects */
> +     for (i = priv->msg_obj_rx_first; i <= priv->msg_obj_num; i++)
> +             c_can_inval_msg_object(dev, IF_RX, i);
> +
> +     /* setup receive message objects */
> +     for (i = priv->msg_obj_rx_first; i < priv->msg_obj_rx_last; i++)
> +             c_can_setup_receive_object(dev, IF_RX, i, 0, 0, IF_MCONT_RCV);
> +
> +     c_can_setup_receive_object(dev, IF_RX, priv->msg_obj_rx_last, 0, 0,
> +                                IF_MCONT_RCV_EOB);
> +}
> +
> +static int c_can_software_reset(struct rtcan_device *dev)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +     int retry = 0;
> +
> +     if (priv->type != BOSCH_D_CAN)
> +             return 0;
> +
> +     priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_SWR | CONTROL_INIT);
> +     while (priv->read_reg(priv, C_CAN_CTRL_REG) & CONTROL_SWR) {
> +             msleep(20);

Same question as above.

> +             if (retry++ > 100) {
> +                     rtcandev_err(dev, "CCTRL: software reset failed\n");
> +                     return -EIO;
> +             }
> +     }
> +
> +     return 0;
> +}
> +
> +/*
> + * Configure C_CAN chip:
> + * - enable/disable auto-retransmission
> + * - set operating mode
> + * - configure message objects
> + */
> +static int c_can_chip_config(struct rtcan_device *dev)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +     int err;
> +
> +     err = c_can_software_reset(dev);
> +     if (err)
> +             return err;
> +
> +     /* enable automatic retransmission */
> +     priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_ENABLE_AR);
> +
> +     if ((dev->ctrl_mode & CAN_CTRLMODE_LISTENONLY) &&
> +         (dev->ctrl_mode & CAN_CTRLMODE_LOOPBACK)) {
> +             /* loopback + silent mode : useful for hot self-test */
> +             priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST);
> +             priv->write_reg(priv, C_CAN_TEST_REG, TEST_LBACK | TEST_SILENT);
> +     } else if (dev->ctrl_mode & CAN_CTRLMODE_LOOPBACK) {
> +             /* loopback mode : useful for self-test function */
> +             priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST);
> +             priv->write_reg(priv, C_CAN_TEST_REG, TEST_LBACK);
> +     } else if (dev->ctrl_mode & CAN_CTRLMODE_LISTENONLY) {
> +             /* silent mode : bus-monitoring mode */
> +             priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST);
> +             priv->write_reg(priv, C_CAN_TEST_REG, TEST_SILENT);
> +     }
> +
> +     /* configure message objects */
> +     c_can_configure_msg_objects(dev);
> +
> +     /* set a `lec` value so that we can check for updates later */
> +     priv->write_reg(priv, C_CAN_STS_REG, LEC_UNUSED);
> +
> +     /* Clear all internal status */
> +     atomic_set(&priv->tx_active, 0);
> +     atomic_set(&priv->tx_cached, 0);
> +     priv->rxmasked = 0;
> +     priv->tx_dir = 0;
> +
> +     /* set bittiming params */
> +     return c_can_set_bittiming(dev);
> +}
> +
> +static int c_can_save_bit_time(struct rtcan_device *dev, struct can_bittime 
> *bt,
> +                            rtdm_lockctx_t *lock_ctx)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +
> +     memcpy(&priv->bit_time, bt, sizeof(*bt));
> +
> +     return 0;
> +}
> +
> +#ifdef CONFIG_XENO_DRIVERS_CAN_BUS_ERR
> +static void c_can_enable_bus_err(struct rtcan_device *dev)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +
> +     if (priv->bus_err_on < 2) {
> +             priv->write_reg(priv, C_CAN_STS_REG, LEC_UNUSED);
> +             priv->bus_err_on = 2;
> +     }
> +}
> +#endif
> +
> +static void c_can_do_tx(struct rtcan_device *dev)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +     u32 idx, obj, pend, clr;
> +
> +     if (priv->msg_obj_tx_last > 32)
> +             pend = priv->read_reg32(priv, C_CAN_INTPND3_REG);
> +     else
> +             pend = priv->read_reg(priv, C_CAN_INTPND2_REG);
> +
> +     clr = pend;
> +
> +     while ((idx = ffs(pend))) {
> +             idx--;
> +             pend &= ~(1 << idx);
> +             obj = idx + priv->msg_obj_tx_first;
> +             c_can_inval_tx_object(dev, IF_RX, obj);
> +             rtdm_sem_up(&dev->tx_sem);
> +             dev->tx_count++;
> +     }
> +
> +     /* Clear the bits in the tx_active mask */
> +     atomic_sub(clr, &priv->tx_active);
> +
> +     pend = atomic_read(&priv->tx_cached);
> +     if (pend && atomic_read(&priv->tx_active) == 0) {
> +             clr = pend;
> +             while ((idx = ffs(pend))) {
> +                     idx--;
> +                     pend &= ~(1 << idx);
> +
> +                     obj = idx + priv->msg_obj_tx_first;
> +                     c_can_object_put(dev, IF_RX, obj, IF_COMM_TXRQST);
> +             }
> +
> +             atomic_sub(clr, &priv->tx_cached);
> +             atomic_add(clr, &priv->tx_active);
> +     }
> +}
> +
> +/*
> + * If we have a gap in the pending bits, that means we either
> + * raced with the hardware or failed to readout all upper
> + * objects in the last run due to quota limit.
> + */
> +static u32 c_can_adjust_pending(u32 pend, u32 rx_mask)
> +{
> +     u32 weight, lasts;
> +
> +     if (pend == rx_mask)
> +             return pend;
> +
> +     /*
> +      * If the last set bit is larger than the number of pending
> +      * bits we have a gap.
> +      */
> +     weight = hweight32(pend);
> +     lasts = fls(pend);
> +
> +     /* If the bits are linear, nothing to do */
> +     if (lasts == weight)
> +             return pend;
> +
> +     /*
> +      * Find the first set bit after the gap. We walk backwards
> +      * from the last set bit.
> +      */
> +     for (lasts--; pend & (1 << (lasts - 1)); lasts--)
> +             ;
> +
> +     return pend & ~((1 << lasts) - 1);
> +}
> +
> +static inline void c_can_rx_object_get(struct rtcan_device *dev,
> +                                    struct c_can_priv *priv, u32 obj)
> +{
> +     c_can_object_get(dev, IF_RX, obj, priv->comm_rcv_high);
> +}
> +
> +static inline void c_can_rx_finalize(struct rtcan_device *dev,
> +                                  struct c_can_priv *priv, u32 obj)
> +{
> +     if (priv->type != BOSCH_D_CAN)
> +             c_can_object_get(dev, IF_RX, obj, IF_COMM_CLR_NEWDAT);
> +}
> +
> +static int c_can_read_objects(struct rtcan_device *dev, struct c_can_priv 
> *priv,
> +                           u32 pend, int quota)
> +{
> +     struct rtcan_skb skb;
> +     u32 pkts = 0, ctrl, obj;
> +
> +     while ((obj = ffs(pend)) && quota > 0) {
> +             pend &= ~BIT(obj - 1);
> +
> +             c_can_rx_object_get(dev, priv, obj);
> +             ctrl = priv->read_reg(priv, C_CAN_IFACE(MSGCTRL_REG, IF_RX));
> +
> +             if (ctrl & IF_MCONT_MSGLST) {
> +                     int n = c_can_handle_lost_msg_obj(dev, IF_RX, obj, ctrl,
> +                                                       &skb);
> +
> +                     pkts += n;
> +                     quota -= n;
> +                     continue;
> +             }
> +
> +             /*
> +              * This really should not happen, but this covers some
> +              * odd HW behaviour. Do not remove that unless you
> +              * want to brick your machine.
> +              */
> +             if (!(ctrl & IF_MCONT_NEWDAT))
> +                     continue;
> +
> +             /* read the data from the message object */
> +             c_can_read_msg_object(dev, IF_RX, ctrl, &skb);
> +
> +             c_can_rx_finalize(dev, priv, obj);
> +
> +             rtcan_rcv(dev, &skb);
> +             quota--;
> +             pkts++;
> +     }
> +
> +     return pkts;
> +}
> +
> +static inline u32 c_can_get_pending(struct c_can_priv *priv)
> +{
> +     u32 pend = priv->read_reg32(priv, C_CAN_NEWDAT1_REG);
> +
> +     return pend;
> +}
> +
> +/*
> + * theory of operation:
> + *
> + * c_can core saves a received CAN message into the first free message
> + * object it finds free (starting with the lowest). Bits NEWDAT and
> + * INTPND are set for this message object indicating that a new message
> + * has arrived. To work-around this issue, we keep two groups of message
> + * objects whose partitioning is defined by C_CAN_MSG_OBJ_RX_SPLIT.
> + *
> + * We clear the newdat bit right away.
> + *
> + * This can result in packet reordering when the readout is slow.
> + */
> +static int c_can_do_rx_poll(struct rtcan_device *dev, int quota)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +     u32 pkts = 0, pend = 0, toread, n;
> +
> +     BUG_ON(priv->msg_obj_rx_last > 32);
> +
> +     while (quota > 0) {
> +
> +             if (!pend) {
> +                     pend = c_can_get_pending(priv);
> +                     if (!pend)
> +                             break;
> +                     /*
> +                      * If the pending field has a gap, handle the
> +                      * bits above the gap first.
> +                      */
> +                     toread = c_can_adjust_pending(pend,
> +                                                   priv->msg_obj_rx_mask);
> +             } else {
> +                     toread = pend;
> +             }
> +             /* Remove the bits from pend */
> +             pend &= ~toread;
> +             /* Read the objects */
> +             n = c_can_read_objects(dev, priv, toread, quota);
> +             pkts += n;
> +             quota -= n;
> +     }
> +     return pkts;
> +}
> +
> +static int c_can_handle_state_change(struct rtcan_device *dev,
> +                                  enum CAN_STATE error_type)
> +{
> +     unsigned int reg_err_counter;
> +     u8 txerr;
> +     u8 rxerr;
> +     unsigned int rx_err_passive;
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +     struct rtcan_skb skb;
> +     struct rtcan_rb_frame *cf = &skb.rb_frame;
> +
> +     /* propagate the error condition to the CAN stack */
> +     reg_err_counter = priv->read_reg(priv, C_CAN_ERR_CNT_REG);
> +     rxerr = (reg_err_counter & ERR_CNT_REC_MASK) >> ERR_CNT_REC_SHIFT;
> +     txerr = reg_err_counter & ERR_CNT_TEC_MASK;
> +     rx_err_passive = (reg_err_counter & ERR_CNT_RP_MASK) >>
> +         ERR_CNT_RP_SHIFT;
> +
> +     skb.rb_frame_size = EMPTY_RB_FRAME_SIZE + CAN_ERR_DLC;
> +     cf->can_id = CAN_ERR_FLAG;
> +     cf->len = CAN_ERR_DLC;
> +     memset(&cf->data[0], 0, cf->len);
> +
> +     switch (error_type) {
> +     case CAN_STATE_ERROR_ACTIVE:
> +             /* RX/TX error count < 96 */
> +             dev->state = CAN_STATE_ERROR_ACTIVE;
> +             cf->can_id |= CAN_ERR_CRTL;
> +             cf->data[1] = CAN_ERR_CRTL_ACTIVE;
> +             cf->data[6] = txerr;
> +             cf->data[7] = rxerr;
> +             break;
> +     case CAN_STATE_ERROR_WARNING:
> +             /* error warning state */
> +             dev->state = CAN_STATE_ERROR_WARNING;
> +             cf->can_id |= CAN_ERR_CRTL;
> +             cf->data[1] = (txerr > rxerr) ?
> +                 CAN_ERR_CRTL_TX_WARNING : CAN_ERR_CRTL_RX_WARNING;
> +             cf->data[6] = txerr;
> +             cf->data[7] = rxerr;
> +
> +             break;
> +     case CAN_STATE_ERROR_PASSIVE:
> +             /* error passive state */
> +             dev->state = CAN_STATE_ERROR_PASSIVE;
> +             cf->can_id |= CAN_ERR_CRTL;
> +             if (rx_err_passive)
> +                     cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
> +             if (txerr > 127)
> +                     cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
> +
> +             cf->data[6] = txerr;
> +             cf->data[7] = rxerr;
> +             break;
> +     case CAN_STATE_BUS_OFF:
> +             /* bus-off state */
> +             dev->state = CAN_STATE_BUS_OFF;
> +             cf->can_id |= CAN_ERR_BUSOFF;
> +             /* Wake up waiting senders */
> +             rtdm_sem_destroy(&dev->tx_sem);
> +             break;
> +     default:
> +             break;
> +     }
> +
> +     /* Store the interface index */
> +     cf->can_ifindex = dev->ifindex;
> +     rtcan_rcv(dev, &skb);
> +
> +     return 1;
> +}
> +
> +static int c_can_handle_bus_err(struct rtcan_device *dev,
> +                             enum c_can_lec_type lec_type)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +     struct rtcan_skb skb;
> +     struct rtcan_rb_frame *cf = &skb.rb_frame;
> +     skb.rb_frame_size = EMPTY_RB_FRAME_SIZE + CAN_ERR_DLC;
> +
> +     /*
> +      * early exit if no lec update or no error.
> +      * no lec update means that no CAN bus event has been detected
> +      * since CPU wrote 0x7 value to status reg.
> +      */
> +     if (lec_type == LEC_UNUSED || lec_type == LEC_NO_ERROR)
> +             return 0;
> +
> +     if (priv->bus_err_on < 2)
> +             return 0;
> +
> +     priv->bus_err_on--;
> +     /*
> +      * check for 'last error code' which tells us the
> +      * type of the last error to occur on the CAN bus
> +      */
> +
> +     /* common for all type of bus errors */
> +
> +     cf->can_id = CAN_ERR_PROT | CAN_ERR_BUSERROR;
> +     cf->len = CAN_ERR_DLC;
> +     memset(&cf->data[0], 0, cf->len);
> +
> +     switch (lec_type) {
> +     case LEC_STUFF_ERROR:
> +             rtcandev_dbg(dev, "stuff error\n");
> +             cf->data[2] |= CAN_ERR_PROT_STUFF;
> +             break;
> +     case LEC_FORM_ERROR:
> +             rtcandev_dbg(dev, "form error\n");
> +             cf->data[2] |= CAN_ERR_PROT_FORM;
> +             break;
> +     case LEC_ACK_ERROR:
> +             rtcandev_dbg(dev, "ack error\n");
> +             cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
> +             break;
> +     case LEC_BIT1_ERROR:
> +             rtcandev_dbg(dev, "bit1 error\n");
> +             cf->data[2] |= CAN_ERR_PROT_BIT1;
> +             break;
> +     case LEC_BIT0_ERROR:
> +             rtcandev_dbg(dev, "bit0 error\n");
> +             cf->data[2] |= CAN_ERR_PROT_BIT0;
> +             break;
> +     case LEC_CRC_ERROR:
> +             rtcandev_dbg(dev, "CRC error\n");
> +             cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
> +             break;
> +     default:
> +             break;
> +     }
> +
> +     cf->can_ifindex = dev->ifindex;
> +     rtcan_rcv(dev, &skb);
> +
> +     return 1;
> +}
> +
> +static int c_can_interrupt(rtdm_irq_t *irq_handle)
> +{
> +     struct rtcan_device *dev = rtdm_irq_get_arg(irq_handle, void);
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +     u16 reg_int, curr, last = priv->last_status;
> +
> +     reg_int = priv->read_reg(priv, C_CAN_INT_REG);
> +     if (!reg_int)
> +             return RTDM_IRQ_NONE;
> +
> +     c_can_irq_control(priv, false);
> +
> +     rtdm_lock_get(&dev->device_lock);
> +     rtdm_lock_get(&rtcan_recv_list_lock);
> +     rtdm_lock_get(&rtcan_socket_lock);
> +
> +     /* Only read the status register if a status interrupt was pending */
> +     if (reg_int & INT_STS_PENDING) {
> +             priv->last_status = curr = priv->read_reg(priv, C_CAN_STS_REG);
> +             /* Ack status on C_CAN. D_CAN is self clearing */
> +             if (priv->type != BOSCH_D_CAN)
> +                     priv->write_reg(priv, C_CAN_STS_REG, LEC_UNUSED);
> +     } else {
> +             /* no change detected ... */
> +             curr = last;
> +     }
> +
> +     /* handle state changes */
> +     if ((curr & STATUS_EWARN) && (!(last & STATUS_EWARN))) {
> +             rtcandev_dbg(dev, "entered error warning state\n");
> +             c_can_handle_state_change(dev, CAN_STATE_ERROR_WARNING);
> +
> +     }
> +
> +     if ((curr & STATUS_EPASS) && (!(last & STATUS_EPASS))) {
> +             rtcandev_dbg(dev, "entered error passive state\n");
> +             c_can_handle_state_change(dev, CAN_STATE_ERROR_PASSIVE);
> +     }
> +
> +     if ((curr & STATUS_BOFF) && (!(last & STATUS_BOFF))) {
> +             rtcandev_dbg(dev, "entered bus off state\n");
> +             c_can_handle_state_change(dev, CAN_STATE_BUS_OFF);
> +             goto end;
> +     }
> +
> +     /* handle bus recovery events */
> +     if ((!(curr & STATUS_BOFF)) && (last & STATUS_BOFF)) {
> +             rtcandev_dbg(dev, "left bus off state\n");
> +             c_can_handle_state_change(dev, CAN_STATE_ERROR_PASSIVE);
> +     }
> +
> +     if ((!(curr & STATUS_EPASS)) && (last & STATUS_EPASS)) {
> +             rtcandev_dbg(dev, "left error passive state\n");
> +             c_can_handle_state_change(dev, CAN_STATE_ERROR_WARNING);
> +     }
> +
> +     if ((!(curr & STATUS_EWARN)) && (last & STATUS_EWARN)) {
> +             rtcandev_dbg(dev, "left error warning state\n");
> +             c_can_handle_state_change(dev, CAN_STATE_ERROR_ACTIVE);
> +     }
> +
> +     /* handle lec errors on the bus */
> +     c_can_handle_bus_err(dev, curr & LEC_MASK);
> +
> +     /* Handle Tx/Rx events. We do this unconditionally */
> +     c_can_do_rx_poll(dev, priv->msg_obj_rx_num);
> +     c_can_do_tx(dev);
> +
> +     if (curr >= priv->msg_obj_rx_first && curr <= priv->msg_obj_rx_last &&
> +         rtcan_loopback_pending(dev))
> +             rtcan_loopback(dev);
> +
> +end:
> +     rtdm_lock_put(&rtcan_socket_lock);
> +     rtdm_lock_put(&rtcan_recv_list_lock);
> +     rtdm_lock_put(&dev->device_lock);
> +
> +     /* enable all IRQs if we are not in bus off state */
> +     if (dev->state != CAN_STATE_BUS_OFF)
> +             c_can_irq_control(priv, true);
> +
> +     return RTDM_IRQ_HANDLED;
> +}
> +
> +static int c_can_mode_start(struct rtcan_device *dev, rtdm_lockctx_t 
> *lock_ctx)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +     struct pinctrl *p;
> +     int err;
> +
> +     switch (dev->state) {
> +
> +     case CAN_STATE_ACTIVE:
> +     case CAN_STATE_BUS_WARNING:
> +     case CAN_STATE_BUS_PASSIVE:
> +             //rtcandev_info(dev, "Mode start: state active, bus warning, or 
> passive\n");
> +             break;
> +
> +     case CAN_STATE_STOPPED:
> +             /* Register IRQ handler and pass device structure as arg */
> +             err = rtdm_irq_request(&dev->irq_handle, priv->irq,
> +                                    c_can_interrupt, 0, DRV_NAME,
> +                                    (void *)dev);
> +             if (err) {
> +                     rtcandev_err(dev, "couldn't request irq %d\n",
> +                                  priv->irq);
> +                     goto out;
> +             }
> +
> +             c_can_pm_runtime_get_sync(priv);
> +             c_can_reset_ram(priv, true);
> +
> +             /* start chip and queuing */
> +             err = c_can_chip_config(dev);
> +             if (err)
> +                     goto out;
> +
> +             /* Setup the command for new messages */
> +             priv->comm_rcv_high = priv->type != BOSCH_D_CAN ?
> +                 IF_COMM_RCV_LOW : IF_COMM_RCV_HIGH;
> +
> +             dev->state = CAN_STATE_ERROR_ACTIVE;
> +
> +             /* enable status change, error and module interrupts */
> +             c_can_irq_control(priv, true);
> +
> +             /* Set up sender "mutex" */
> +             rtdm_sem_init(&dev->tx_sem, priv->msg_obj_tx_num);
> +
> +             break;
> +
> +     case CAN_STATE_BUS_OFF:
> +             /* Set up sender "mutex" */
> +             rtdm_sem_init(&dev->tx_sem, priv->msg_obj_tx_num);
> +             /* start chip and queuing */
> +             c_can_pm_runtime_get_sync(priv);
> +             c_can_reset_ram(priv, true);
> +             err = c_can_chip_config(dev);
> +             if (err)
> +                     goto out;
> +
> +             dev->state = CAN_STATE_ERROR_ACTIVE;
> +             /* enable status change, error and module interrupts */
> +             c_can_irq_control(priv, true);
> +             break;
> +
> +     case CAN_STATE_SLEEPING:
> +             //rtcandev_info(dev, "Mode start: state sleeping\n");
> +     default:
> +             /* Never reached, but we don't want nasty compiler warnings ... 
> */
> +             break;
> +     }
> +
> +     return 0;
> +out:
> +     /* Attempt to use "active" if available else use "default" */
> +     p = pinctrl_get_select(priv->device, "active");
> +     if (!IS_ERR(p))
> +             pinctrl_put(p);
> +     else
> +             pinctrl_pm_select_default_state(priv->device);
> +
> +     c_can_pm_runtime_put_sync(priv);
> +     return err;
> +}
> +
> +static void c_can_mode_stop(struct rtcan_device *dev, rtdm_lockctx_t 
> *lock_ctx)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +     can_state_t state;
> +
> +     state = dev->state;
> +     /* If controller is not operating anyway, go out */
> +     if (!CAN_STATE_OPERATING(state))
> +             return;
> +     /* put ctrl to init on stop to end ongoing transmission */
> +     priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_INIT);
> +
> +     //rtcandev_info(dev, "Mode stop.\n");
> +     /* disable all interrupts */
> +     c_can_irq_control(priv, false);
> +
> +     /* deactivate pins */
> +     pinctrl_pm_select_sleep_state(priv->device->parent);
> +
> +     /* set the state as STOPPED */
> +     dev->state = CAN_STATE_STOPPED;
> +
> +     /* Wake up waiting senders */
> +     rtdm_sem_destroy(&dev->tx_sem);
> +
> +     rtdm_irq_free(&dev->irq_handle);
> +     c_can_pm_runtime_put_sync(priv);
> +}
> +
> +static int c_can_set_mode(struct rtcan_device *dev, can_mode_t mode,
> +                       rtdm_lockctx_t *lock_ctx)
> +{
> +     int err = 0;
> +     //rtcandev_info(dev, "Set mode.\n");
> +
> +     switch (mode) {
> +
> +     case CAN_MODE_STOP:
> +             //rtcandev_info(dev, "Set mode: stop\n");
> +             c_can_mode_stop(dev, lock_ctx);
> +             break;
> +
> +     case CAN_MODE_START:
> +             //rtcandev_info(dev, "Set mode: start\n");
> +             err = c_can_mode_start(dev, lock_ctx);
> +             break;
> +
> +     case CAN_MODE_SLEEP:
> +     default:
> +             err = -EOPNOTSUPP;
> +             break;
> +     }
> +
> +     return err;
> +}
> +
> +struct rtcan_device *rtcan_c_can_dev_alloc(int msg_obj_num)
> +{
> +     struct rtcan_device *dev;
> +     struct c_can_priv *priv;
> +
> +     dev = rtcan_dev_alloc(sizeof(struct c_can_priv), 0);
> +     if (!dev)
> +             return NULL;
> +
> +     priv = rtcan_priv(dev);
> +
> +     priv->dev = dev;
> +     priv->bus_err_on = 1;
> +     dev->hard_start_xmit = c_can_start_xmit;
> +     dev->do_set_mode = c_can_set_mode;
> +     dev->do_set_bit_time = c_can_save_bit_time;
> +     dev->bittiming_const = &c_can_bittiming_const;
> +#ifdef CONFIG_XENO_DRIVERS_CAN_BUS_ERR
> +     dev->do_enable_bus_err = c_can_enable_bus_err;
> +#endif
> +     rtcan_c_can_set_ethtool_ops(dev);
> +
> +     rtdm_lock_init(&priv->tx_cached_lock);
> +     priv->msg_obj_num = msg_obj_num;
> +     priv->msg_obj_rx_num = msg_obj_num / 2;
> +     priv->msg_obj_rx_first = 1;
> +     priv->msg_obj_rx_last =
> +         priv->msg_obj_rx_first + priv->msg_obj_rx_num - 1;
> +     priv->msg_obj_rx_low_last =
> +         (priv->msg_obj_rx_first + priv->msg_obj_rx_last) / 2;
> +     priv->msg_obj_rx_mask = ((u64) 1 << priv->msg_obj_rx_num) - 1;
> +
> +     priv->msg_obj_tx_num = msg_obj_num - priv->msg_obj_rx_num;
> +     priv->msg_obj_tx_first = priv->msg_obj_rx_last + 1;
> +     priv->msg_obj_tx_last =
> +         priv->msg_obj_tx_first + priv->msg_obj_tx_num - 1;
> +     priv->msg_obj_tx_next_mask = priv->msg_obj_tx_num - 1;
> +
> +     priv->dlc = kcalloc(priv->msg_obj_tx_num, sizeof(*priv->dlc),
> +                         GFP_KERNEL);
> +     if (!priv->dlc) {
> +             rtcan_dev_free(dev);
> +             return NULL;
> +     }
> +
> +     return dev;
> +}
> +EXPORT_SYMBOL_GPL(rtcan_c_can_dev_alloc);
> +
> +void rtcan_c_can_dev_free(struct rtcan_device *dev)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +
> +     kfree(priv->dlc);
> +     rtcan_dev_free(dev);
> +}
> +EXPORT_SYMBOL_GPL(rtcan_c_can_dev_free);
> +
> +#ifdef CONFIG_PM
> +int rtcan_c_can_power_down(struct rtcan_device *dev)
> +{
> +     u32 val;
> +     unsigned long time_out;
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +
> +     WARN_ON(priv->type != BOSCH_D_CAN);
> +
> +     /* set PDR value so the device goes to power down mode */
> +     val = priv->read_reg(priv, C_CAN_CTRL_EX_REG);
> +     val |= CONTROL_EX_PDR;
> +     priv->write_reg(priv, C_CAN_CTRL_EX_REG, val);
> +
> +     /* Wait for the PDA bit to get set */
> +     time_out = jiffies + msecs_to_jiffies(INIT_WAIT_MS);
> +     while (!(priv->read_reg(priv, C_CAN_STS_REG) & STATUS_PDA) &&
> +            time_after(time_out, jiffies))
> +             cpu_relax();
> +
> +     if (time_after(jiffies, time_out))
> +             return -ETIMEDOUT;
> +
> +     /* disable all interrupts */
> +     c_can_irq_control(priv, false);
> +
> +     /* set the state as STOPPED */
> +     dev->state = CAN_STATE_STOPPED;
> +
> +     c_can_reset_ram(priv, false);
> +     c_can_pm_runtime_put_sync(priv);
> +
> +     return 0;
> +}
> +EXPORT_SYMBOL_GPL(rtcan_c_can_power_down);
> +
> +int rtcan_c_can_power_up(struct rtcan_device *dev)
> +{
> +     u32 val;
> +     unsigned long time_out;
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +     int ret;
> +
> +     WARN_ON(priv->type != BOSCH_D_CAN);
> +
> +     c_can_pm_runtime_get_sync(priv);
> +     c_can_reset_ram(priv, true);
> +
> +     /* Clear PDR and INIT bits */
> +     val = priv->read_reg(priv, C_CAN_CTRL_EX_REG);
> +     val &= ~CONTROL_EX_PDR;
> +     priv->write_reg(priv, C_CAN_CTRL_EX_REG, val);
> +     val = priv->read_reg(priv, C_CAN_CTRL_REG);
> +     val &= ~CONTROL_INIT;
> +     priv->write_reg(priv, C_CAN_CTRL_REG, val);
> +
> +     /* Wait for the PDA bit to get clear */
> +     time_out = jiffies + msecs_to_jiffies(INIT_WAIT_MS);
> +     while ((priv->read_reg(priv, C_CAN_STS_REG) & STATUS_PDA) &&
> +            time_after(time_out, jiffies))
> +             cpu_relax();
> +
> +     if (time_after(jiffies, time_out)) {
> +             ret = -ETIMEDOUT;
> +             goto err_out;
> +     }
> +
> +     ret = c_can_mode_start(dev, NULL);
> +     if (ret)
> +             goto err_out;
> +
> +     c_can_irq_control(priv, true);
> +
> +     return 0;
> +
> +err_out:
> +     c_can_reset_ram(priv, false);
> +     c_can_pm_runtime_put_sync(priv);
> +
> +     return ret;
> +}
> +EXPORT_SYMBOL_GPL(rtcan_c_can_power_up);
> +

Everything under CONFIG_PM looks like dead code to me, no?

> +#endif
> +
> +int rtcan_c_can_register(struct rtcan_device *dev)
> +{
> +     int err;
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +
> +     /* Deactivate pins to prevent DRA7 DCAN IP from being
> +      * stuck in transition when module is disabled.
> +      * Pins are activated in c_can_start() and deactivated
> +      * in c_can_stop()
> +      */
> +     pinctrl_pm_select_sleep_state(priv->device->parent);
> +
> +     c_can_pm_runtime_enable(priv);
> +
> +     err = rtcan_dev_register(dev);
> +     if (err)
> +             goto out_chip_disable;
> +
> +     return 0;
> +
> +out_chip_disable:
> +     c_can_pm_runtime_disable(priv);
> +
> +     return err;
> +}
> +EXPORT_SYMBOL_GPL(rtcan_c_can_register);
> +
> +void rtcan_c_can_unregister(struct rtcan_device *dev)
> +{
> +     c_can_mode_stop(dev, NULL);
> +     rtcan_dev_unregister(dev);
> +}
> +EXPORT_SYMBOL_GPL(rtcan_c_can_unregister);
> +
> +MODULE_AUTHOR("Stephen J. Battazzo <stephen.j.batta...@nasa.gov>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("RT-Socket-CAN driver for Bosch C_CAN");
> +MODULE_SUPPORTED_DEVICE("Boch C_CAN CAN controller");
> diff --git a/kernel/drivers/can/c_can/rtcan_c_can.h 
> b/kernel/drivers/can/c_can/rtcan_c_can.h
> new file mode 100644
> index 000000000..8cc7ef494
> --- /dev/null
> +++ b/kernel/drivers/can/c_can/rtcan_c_can.h
> @@ -0,0 +1,258 @@
> +/*
> + * CAN bus driver for Bosch C_CAN controller, ported to Xenomai RTDM
> + *
> + * Copyright (C) 2010 ST Microelectronics
> + * Bhupesh Sharma <bhupesh.sha...@st.com>
> + *
> + * Stephen J. Battazzo <stephen.j.batta...@nasa.gov>,
> + * MEI Services/NASA Ames Research Center
> + *
> + * Borrowed original driver from:
> + *
> + * Bhupesh Sharma <bhupesh.sha...@st.com>, ST Microelectronics
> + * Borrowed heavily from the C_CAN driver originally written by:
> + * Copyright (C) 2007
> + * - Sascha Hauer, Marc Kleine-Budde, Pengutronix <s.ha...@pengutronix.de>
> + * - Simon Kallweit, intefo AG <simon.kallw...@intefo.ch>
> + *
> + * TX and RX NAPI implementation has been removed and replaced with RT 
> Socket CAN implementation.
> + * RT Socket CAN implementation inspired by Flexcan RTDM port by Wolfgang 
> Grandegger <w...@denx.de>
> + *
> + * Bosch C_CAN controller is compliant to CAN protocol version 2.0 part A 
> and B.
> + * Bosch C_CAN user manual can be obtained from:
> + * http://www.semiconductors.bosch.de/media/en/pdf/ipmodules_1/c_can/
> + * users_manual_c_can.pdf
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#ifndef RTCAN_C_CAN_H
> +#define RTCAN_C_CAN_H
> +
> +enum reg {
> +     C_CAN_CTRL_REG = 0,
> +     C_CAN_CTRL_EX_REG,
> +     C_CAN_STS_REG,
> +     C_CAN_ERR_CNT_REG,
> +     C_CAN_BTR_REG,
> +     C_CAN_INT_REG,
> +     C_CAN_TEST_REG,
> +     C_CAN_BRPEXT_REG,
> +     C_CAN_IF1_COMREQ_REG,
> +     C_CAN_IF1_COMMSK_REG,
> +     C_CAN_IF1_MASK1_REG,
> +     C_CAN_IF1_MASK2_REG,
> +     C_CAN_IF1_ARB1_REG,
> +     C_CAN_IF1_ARB2_REG,
> +     C_CAN_IF1_MSGCTRL_REG,
> +     C_CAN_IF1_DATA1_REG,
> +     C_CAN_IF1_DATA2_REG,
> +     C_CAN_IF1_DATA3_REG,
> +     C_CAN_IF1_DATA4_REG,
> +     C_CAN_IF2_COMREQ_REG,
> +     C_CAN_IF2_COMMSK_REG,
> +     C_CAN_IF2_MASK1_REG,
> +     C_CAN_IF2_MASK2_REG,
> +     C_CAN_IF2_ARB1_REG,
> +     C_CAN_IF2_ARB2_REG,
> +     C_CAN_IF2_MSGCTRL_REG,
> +     C_CAN_IF2_DATA1_REG,
> +     C_CAN_IF2_DATA2_REG,
> +     C_CAN_IF2_DATA3_REG,
> +     C_CAN_IF2_DATA4_REG,
> +     C_CAN_TXRQST1_REG,
> +     C_CAN_TXRQST2_REG,
> +     C_CAN_TXRQST3_REG,
> +     C_CAN_TXRQST4_REG,
> +     C_CAN_NEWDAT1_REG,
> +     C_CAN_NEWDAT2_REG,
> +     C_CAN_INTPND1_REG,
> +     C_CAN_INTPND2_REG,
> +     C_CAN_INTPND3_REG,
> +     C_CAN_MSGVAL1_REG,
> +     C_CAN_MSGVAL2_REG,
> +     C_CAN_FUNCTION_REG,
> +};
> +
> +static const u16 reg_map_c_can[] = {
> +     [C_CAN_CTRL_REG] = 0x00,
> +     [C_CAN_STS_REG] = 0x02,
> +     [C_CAN_ERR_CNT_REG] = 0x04,
> +     [C_CAN_BTR_REG] = 0x06,
> +     [C_CAN_INT_REG] = 0x08,
> +     [C_CAN_TEST_REG] = 0x0A,
> +     [C_CAN_BRPEXT_REG] = 0x0C,
> +     [C_CAN_IF1_COMREQ_REG] = 0x10,
> +     [C_CAN_IF1_COMMSK_REG] = 0x12,
> +     [C_CAN_IF1_MASK1_REG] = 0x14,
> +     [C_CAN_IF1_MASK2_REG] = 0x16,
> +     [C_CAN_IF1_ARB1_REG] = 0x18,
> +     [C_CAN_IF1_ARB2_REG] = 0x1A,
> +     [C_CAN_IF1_MSGCTRL_REG] = 0x1C,
> +     [C_CAN_IF1_DATA1_REG] = 0x1E,
> +     [C_CAN_IF1_DATA2_REG] = 0x20,
> +     [C_CAN_IF1_DATA3_REG] = 0x22,
> +     [C_CAN_IF1_DATA4_REG] = 0x24,
> +     [C_CAN_IF2_COMREQ_REG] = 0x40,
> +     [C_CAN_IF2_COMMSK_REG] = 0x42,
> +     [C_CAN_IF2_MASK1_REG] = 0x44,
> +     [C_CAN_IF2_MASK2_REG] = 0x46,
> +     [C_CAN_IF2_ARB1_REG] = 0x48,
> +     [C_CAN_IF2_ARB2_REG] = 0x4A,
> +     [C_CAN_IF2_MSGCTRL_REG] = 0x4C,
> +     [C_CAN_IF2_DATA1_REG] = 0x4E,
> +     [C_CAN_IF2_DATA2_REG] = 0x50,
> +     [C_CAN_IF2_DATA3_REG] = 0x52,
> +     [C_CAN_IF2_DATA4_REG] = 0x54,
> +     [C_CAN_TXRQST1_REG] = 0x80,
> +     [C_CAN_TXRQST2_REG] = 0x82,
> +     [C_CAN_NEWDAT1_REG] = 0x90,
> +     [C_CAN_NEWDAT2_REG] = 0x92,
> +     [C_CAN_INTPND1_REG] = 0xA0,
> +     [C_CAN_INTPND2_REG] = 0xA2,
> +     [C_CAN_MSGVAL1_REG] = 0xB0,
> +     [C_CAN_MSGVAL2_REG] = 0xB2,
> +};
> +
> +static const u16 reg_map_d_can[] = {
> +     [C_CAN_CTRL_REG] = 0x00,
> +     [C_CAN_CTRL_EX_REG] = 0x02,
> +     [C_CAN_STS_REG] = 0x04,
> +     [C_CAN_ERR_CNT_REG] = 0x08,
> +     [C_CAN_BTR_REG] = 0x0C,
> +     [C_CAN_BRPEXT_REG] = 0x0E,
> +     [C_CAN_INT_REG] = 0x10,
> +     [C_CAN_TEST_REG] = 0x14,
> +     [C_CAN_FUNCTION_REG] = 0x18,
> +     [C_CAN_TXRQST1_REG] = 0x88,
> +     [C_CAN_TXRQST2_REG] = 0x8A,
> +     [C_CAN_TXRQST3_REG] = 0x8C,
> +     [C_CAN_TXRQST4_REG] = 0x8E,
> +     [C_CAN_NEWDAT1_REG] = 0x9C,
> +     [C_CAN_NEWDAT2_REG] = 0x9E,
> +     [C_CAN_INTPND1_REG] = 0xB0,
> +     [C_CAN_INTPND2_REG] = 0xB2,
> +     [C_CAN_INTPND3_REG] = 0xB4,
> +     [C_CAN_MSGVAL1_REG] = 0xC4,
> +     [C_CAN_MSGVAL2_REG] = 0xC6,
> +     [C_CAN_IF1_COMREQ_REG] = 0x100,
> +     [C_CAN_IF1_COMMSK_REG] = 0x102,
> +     [C_CAN_IF1_MASK1_REG] = 0x104,
> +     [C_CAN_IF1_MASK2_REG] = 0x106,
> +     [C_CAN_IF1_ARB1_REG] = 0x108,
> +     [C_CAN_IF1_ARB2_REG] = 0x10A,
> +     [C_CAN_IF1_MSGCTRL_REG] = 0x10C,
> +     [C_CAN_IF1_DATA1_REG] = 0x110,
> +     [C_CAN_IF1_DATA2_REG] = 0x112,
> +     [C_CAN_IF1_DATA3_REG] = 0x114,
> +     [C_CAN_IF1_DATA4_REG] = 0x116,
> +     [C_CAN_IF2_COMREQ_REG] = 0x120,
> +     [C_CAN_IF2_COMMSK_REG] = 0x122,
> +     [C_CAN_IF2_MASK1_REG] = 0x124,
> +     [C_CAN_IF2_MASK2_REG] = 0x126,
> +     [C_CAN_IF2_ARB1_REG] = 0x128,
> +     [C_CAN_IF2_ARB2_REG] = 0x12A,
> +     [C_CAN_IF2_MSGCTRL_REG] = 0x12C,
> +     [C_CAN_IF2_DATA1_REG] = 0x130,
> +     [C_CAN_IF2_DATA2_REG] = 0x132,
> +     [C_CAN_IF2_DATA3_REG] = 0x134,
> +     [C_CAN_IF2_DATA4_REG] = 0x136,
> +};
> +
> +enum c_can_dev_id {
> +     BOSCH_C_CAN_PLATFORM,
> +     BOSCH_C_CAN,
> +     BOSCH_D_CAN,
> +};
> +
> +struct raminit_bits {
> +     u8 start;
> +     u8 done;
> +};
> +
> +struct c_can_driver_data {
> +     enum c_can_dev_id id;
> +
> +     int msg_obj_num;
> +
> +     /* RAMINIT register description. Optional. */
> +     const struct raminit_bits *raminit_bits;        /* Array of START/DONE 
> bit positions */
> +     u8 raminit_num;         /* Number of CAN instances on the SoC */
> +     bool raminit_pulse;     /* If set, sets and clears START bit (pulse) */
> +};
> +
> +/* Out of band RAMINIT register access via syscon regmap */
> +struct c_can_raminit {
> +     struct regmap *syscon;  /* for raminit ctrl. reg. access */
> +     unsigned int reg;       /* register index within syscon */
> +     struct raminit_bits bits;
> +     bool needs_pulse;
> +};
> +
> +#define DEV_NAME     "rtcan%d"
> +#define DRV_NAME     "c_can"
> +
> +/* c_can private data structure */
> +struct c_can_priv {
> +     struct rtcan_device *dev;
> +
> +     int irq;
> +
> +     struct device *device;
> +
> +     struct can_bittime bit_time;
> +     char bus_err_on;
> +     int tx_object;
> +     int current_status;
> +     int last_status;
> +     u16 (*read_reg)(const struct c_can_priv *priv, enum reg index);
> +     void (*write_reg)(const struct c_can_priv *priv, enum reg index,
> +                       u16 val);
> +     u32 (*read_reg32)(const struct c_can_priv *priv, enum reg index);
> +     void (*write_reg32)(const struct c_can_priv *priv, enum reg index,
> +                         u32 val);
> +     void __iomem *base;
> +     const u16 *regs;
> +     unsigned long irq_flags;        /* for request_irq() */
> +     atomic_t tx_active;
> +     atomic_t tx_cached;
> +     rtdm_lock_t tx_cached_lock;
> +     unsigned long tx_dir;
> +     void *priv;             /* for board-specific data */
> +     u16 irqstatus;
> +     enum c_can_dev_id type;
> +     int msg_obj_num;
> +     int msg_obj_rx_num;
> +     int msg_obj_tx_num;
> +     int msg_obj_rx_first;
> +     int msg_obj_rx_last;
> +     int msg_obj_rx_low_last;
> +     u32 msg_obj_rx_mask;
> +     u32 rxmasked;
> +     int msg_obj_tx_first;
> +     int msg_obj_tx_last;
> +     int msg_obj_tx_next_mask;
> +     u32 comm_rcv_high;
> +     u32 *dlc;
> +     struct c_can_raminit raminit_sys;       /* RAMINIT via syscon regmap */
> +     u32 __iomem *raminit_ctrlreg;
> +     unsigned int instance;
> +     void (*raminit)(const struct c_can_priv *priv, bool enable);
> +};
> +
> +struct rtcan_device *rtcan_c_can_dev_alloc(int msg_obj_num);
> +void rtcan_c_can_dev_free(struct rtcan_device *dev);
> +
> +int rtcan_c_can_register(struct rtcan_device *dev);
> +void rtcan_c_can_unregister(struct rtcan_device *dev);
> +
> +#ifdef CONFIG_PM
> +int rtcan_c_can_power_up(struct rtcan_device *dev);
> +int rtcan_c_can_power_down(struct rtcan_device *dev);
> +#endif
> +
> +void rtcan_c_can_set_ethtool_ops(struct rtcan_device *dev);
> +
> +#endif
> diff --git a/kernel/drivers/can/c_can/rtcan_c_can_ethtool.c 
> b/kernel/drivers/can/c_can/rtcan_c_can_ethtool.c
> new file mode 100644
> index 000000000..7945ce4d8
> --- /dev/null
> +++ b/kernel/drivers/can/c_can/rtcan_c_can_ethtool.c
> @@ -0,0 +1,47 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright 2021, Dario Binacchi <dario...@libero.it>
> + */
> +
> +#include <linux/platform_device.h>
> +
> +/* CAN device profile */
> +#include <rtdm/can.h>
> +
> +#include "rtcan_dev.h"
> +#include "rtcan_raw.h"
> +#include "rtcan_internal.h"
> +#include "rtcan_ethtool.h"
> +#include "rtcan_c_can.h"
> +
> +static void rtcan_c_can_get_drvinfo(struct rtcan_device *dev,
> +                                 struct ethtool_drvinfo *info)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +     struct platform_device *pdev = to_platform_device(priv->device);
> +
> +     strlcpy(info->driver, "c_can", sizeof(info->driver));
> +     strlcpy(info->version, "1.0", sizeof(info->version));
> +     strlcpy(info->bus_info, pdev->name, sizeof(info->bus_info));
> +}
> +
> +static void rtcan_c_can_get_ringparam(struct rtcan_device *dev,
> +                                   struct ethtool_ringparam *ring)
> +{
> +     struct c_can_priv *priv = rtcan_priv(dev);
> +
> +     ring->rx_max_pending = priv->msg_obj_num;
> +     ring->tx_max_pending = priv->msg_obj_num;
> +     ring->rx_pending = priv->msg_obj_rx_num;
> +     ring->tx_pending = priv->msg_obj_tx_num;
> +}
> +
> +static const struct rtcan_ethtool_ops rtcan_c_can_ethtool_ops = {
> +     .get_drvinfo = rtcan_c_can_get_drvinfo,
> +     .get_ringparam = rtcan_c_can_get_ringparam,
> +};
> +
> +void rtcan_c_can_set_ethtool_ops(struct rtcan_device *dev)
> +{
> +     dev->ethtool_ops = &rtcan_c_can_ethtool_ops;
> +}
> diff --git a/kernel/drivers/can/c_can/rtcan_c_can_platform.c 
> b/kernel/drivers/can/c_can/rtcan_c_can_platform.c
> new file mode 100644
> index 000000000..0d29d9877
> --- /dev/null
> +++ b/kernel/drivers/can/c_can/rtcan_c_can_platform.c
> @@ -0,0 +1,461 @@
> +/*
> + * CAN bus driver for Bosch C_CAN controller, ported to Xenomai RTDM
> + *
> + * Copyright 2021, Dario Binacchi <dario...@libero.it>
> + *
> + * Stephen J. Battazzo <stephen.j.batta...@nasa.gov>,
> + * MEI Services/NASA Ames Research Center
> + *
> + * Borrowed original driver from:
> + *
> + * Bhupesh Sharma <bhupesh.sha...@st.com>, ST Microelectronics
> + * Borrowed heavily from the C_CAN driver originally written by:
> + * Copyright (C) 2007
> + * - Sascha Hauer, Marc Kleine-Budde, Pengutronix <s.ha...@pengutronix.de>
> + * - Simon Kallweit, intefo AG <simon.kallw...@intefo.ch>
> + *
> + * TX and RX NAPI implementation has been removed and replaced with RT 
> Socket CAN implementation.
> + * RT Socket CAN implementation inspired by Flexcan RTDM port by Wolfgang 
> Grandegger <w...@denx.de>
> + *
> + * Bosch C_CAN controller is compliant to CAN protocol version 2.0 part A 
> and B.
> + * Bosch C_CAN user manual can be obtained from:
> + * http://www.semiconductors.bosch.de/media/en/pdf/ipmodules_1/c_can/
> + * users_manual_c_can.pdf
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/netdevice.h>
> +#include <linux/if_arp.h>
> +#include <linux/if_ether.h>
> +#include <linux/list.h>
> +#include <linux/io.h>
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +#include <linux/device.h>
> +
> +#include <rtdm/driver.h>
> +
> +/* CAN device profile */
> +#include <rtdm/can.h>
> +
> +#include "rtcan_internal.h"
> +#include "rtcan_dev.h"
> +
> +#include "rtcan_c_can.h"
> +
> +static char *board_name = "platform-c_can";
> +
> +#define DCAN_RAM_INIT_BIT            (1 << 3)
> +static DEFINE_SPINLOCK(raminit_lock);

None of the code using this lock is running in RT context?

> +/*
> + * 16-bit c_can registers can be arranged differently in the memory
> + * architecture of different implementations. For example: 16-bit
> + * registers can be aligned to a 16-bit boundary or 32-bit boundary etc.
> + * Handle the same by providing a common read/write interface.
> + */
> +static u16 c_can_plat_read_reg_aligned_to_16bit(const struct c_can_priv 
> *priv,
> +                                             enum reg index)
> +{
> +     return readw(priv->base + priv->regs[index]);
> +}
> +
> +static void c_can_plat_write_reg_aligned_to_16bit(const struct c_can_priv 
> *priv,
> +                                               enum reg index, u16 val)
> +{
> +     writew(val, priv->base + priv->regs[index]);
> +}
> +
> +static u16 c_can_plat_read_reg_aligned_to_32bit(const struct c_can_priv 
> *priv,
> +                                             enum reg index)
> +{
> +     return readw(priv->base + 2 * priv->regs[index]);
> +}
> +
> +static void c_can_plat_write_reg_aligned_to_32bit(const struct c_can_priv 
> *priv,
> +                                               enum reg index, u16 val)
> +{
> +     writew(val, priv->base + 2 * priv->regs[index]);
> +}
> +
> +static void c_can_hw_raminit_wait_syscon(const struct c_can_priv *priv,
> +                                      u32 mask, u32 val)
> +{
> +     const struct c_can_raminit *raminit = &priv->raminit_sys;
> +     int timeout = 0;
> +     u32 ctrl = 0;
> +
> +     /* We look only at the bits of our instance. */
> +     val &= mask;
> +     do {
> +             udelay(1);
> +             timeout++;
> +
> +             regmap_read(raminit->syscon, raminit->reg, &ctrl);
> +             if (timeout == 1000) {
> +                     dev_err(priv->device, "%s: time out\n", __func__);
> +                     break;
> +             }
> +     } while ((ctrl & mask) != val);
> +}
> +
> +static void c_can_hw_raminit_syscon(const struct c_can_priv *priv, bool 
> enable)
> +{
> +     const struct c_can_raminit *raminit = &priv->raminit_sys;
> +     u32 ctrl = 0;
> +     u32 mask;
> +
> +     spin_lock(&raminit_lock);
> +
> +     mask = 1 << raminit->bits.start | 1 << raminit->bits.done;
> +     regmap_read(raminit->syscon, raminit->reg, &ctrl);
> +
> +     /* We clear the start bit first. The start bit is
> +      * looking at the 0 -> transition, but is not self clearing;
> +      * NOTE: DONE must be written with 1 to clear it.
> +      * We can't clear the DONE bit here using regmap_update_bits()
> +      * as it will bypass the write if initial condition is START:0 DONE:1
> +      * e.g. on DRA7 which needs START pulse.
> +      */
> +     ctrl &= ~mask;          /* START = 0, DONE = 0 */
> +     regmap_update_bits(raminit->syscon, raminit->reg, mask, ctrl);
> +
> +     /* check if START bit is 0. Ignore DONE bit for now
> +      * as it can be either 0 or 1.
> +      */
> +     c_can_hw_raminit_wait_syscon(priv, 1 << raminit->bits.start, ctrl);
> +
> +     if (enable) {
> +             /* Clear DONE bit & set START bit. */
> +             ctrl |= 1 << raminit->bits.start;
> +             /* DONE must be written with 1 to clear it */
> +             ctrl |= 1 << raminit->bits.done;
> +             regmap_update_bits(raminit->syscon, raminit->reg, mask, ctrl);
> +             /* prevent further clearing of DONE bit */
> +             ctrl &= ~(1 << raminit->bits.done);
> +             /* clear START bit if start pulse is needed */
> +             if (raminit->needs_pulse) {
> +                     ctrl &= ~(1 << raminit->bits.start);
> +                     regmap_update_bits(raminit->syscon, raminit->reg,
> +                                        mask, ctrl);
> +             }
> +
> +             ctrl |= 1 << raminit->bits.done;
> +             c_can_hw_raminit_wait_syscon(priv, mask, ctrl);
> +     }
> +     spin_unlock(&raminit_lock);
> +}
> +
> +static u32 c_can_plat_read_reg32(const struct c_can_priv *priv, enum reg 
> index)
> +{
> +     u32 val;
> +
> +     val = priv->read_reg(priv, index);
> +     val |= ((u32) priv->read_reg(priv, index + 1)) << 16;
> +
> +     return val;
> +}
> +
> +static void c_can_plat_write_reg32(const struct c_can_priv *priv,
> +                                enum reg index, u32 val)
> +{
> +     priv->write_reg(priv, index + 1, val >> 16);
> +     priv->write_reg(priv, index, val);
> +}
> +
> +static u32 d_can_plat_read_reg32(const struct c_can_priv *priv, enum reg 
> index)
> +{
> +     return readl(priv->base + priv->regs[index]);
> +}
> +
> +static void d_can_plat_write_reg32(const struct c_can_priv *priv,
> +                                enum reg index, u32 val)
> +{
> +     writel(val, priv->base + priv->regs[index]);
> +}
> +
> +static void c_can_hw_raminit_wait(const struct c_can_priv *priv, u32 mask)
> +{
> +     while (priv->read_reg32(priv, C_CAN_FUNCTION_REG) & mask)
> +             udelay(1);
> +}
> +
> +static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable)
> +{
> +     u32 ctrl;
> +
> +     ctrl = priv->read_reg32(priv, C_CAN_FUNCTION_REG);
> +     ctrl &= ~DCAN_RAM_INIT_BIT;
> +     priv->write_reg32(priv, C_CAN_FUNCTION_REG, ctrl);
> +     c_can_hw_raminit_wait(priv, ctrl);
> +
> +     if (enable) {
> +             ctrl |= DCAN_RAM_INIT_BIT;
> +             priv->write_reg32(priv, C_CAN_FUNCTION_REG, ctrl);
> +             c_can_hw_raminit_wait(priv, ctrl);
> +     }
> +}
> +
> +static const struct c_can_driver_data c_can_drvdata = {
> +     .id = BOSCH_C_CAN,
> +     .msg_obj_num = 32,
> +};
> +
> +static const struct c_can_driver_data d_can_drvdata = {
> +     .id = BOSCH_D_CAN,
> +     .msg_obj_num = 32,
> +};
> +
> +static const struct raminit_bits dra7_raminit_bits[] = {
> +     [0] = {.start = 3, .done = 1, },
> +     [1] = {.start = 5, .done = 2, },
> +};
> +
> +static const struct c_can_driver_data dra7_dcan_drvdata = {
> +     .id = BOSCH_D_CAN,
> +     .msg_obj_num = 64,
> +     .raminit_num = ARRAY_SIZE(dra7_raminit_bits),
> +     .raminit_bits = dra7_raminit_bits,
> +     .raminit_pulse = true,
> +};
> +
> +static const struct raminit_bits am3352_raminit_bits[] = {
> +     [0] = {.start = 0, .done = 8, },
> +     [1] = {.start = 1, .done = 9, },
> +};
> +
> +static const struct c_can_driver_data am3352_dcan_drvdata = {
> +     .id = BOSCH_D_CAN,
> +     .msg_obj_num = 64,
> +     .raminit_num = ARRAY_SIZE(am3352_raminit_bits),
> +     .raminit_bits = am3352_raminit_bits,
> +};
> +
> +static const struct platform_device_id c_can_id_table[] = {
> +     {
> +      .name = DRV_NAME,
> +      .driver_data = (kernel_ulong_t) &c_can_drvdata,
> +      },
> +     {
> +      .name = "c_can",
> +      .driver_data = (kernel_ulong_t) &c_can_drvdata,
> +      },
> +     {
> +      .name = "d_can",
> +      .driver_data = (kernel_ulong_t) &d_can_drvdata,
> +      },
> +     { /* sentinel */ },
> +};
> +
> +MODULE_DEVICE_TABLE(platform, c_can_id_table);
> +
> +static const struct of_device_id c_can_of_table[] = {
> +     {.compatible = "bosch,c_can", .data = &c_can_drvdata},
> +     {.compatible = "bosch,d_can", .data = &d_can_drvdata},
> +     {.compatible = "ti,dra7-d_can", .data = &dra7_dcan_drvdata},
> +     {.compatible = "ti,am3352-d_can", .data = &am3352_dcan_drvdata},
> +     {.compatible = "ti,am4372-d_can", .data = &am3352_dcan_drvdata},
> +     { /* sentinel */ },
> +};
> +
> +MODULE_DEVICE_TABLE(of, c_can_of_table);
> +
> +static int c_can_plat_probe(struct platform_device *pdev)
> +{
> +     int ret;
> +     void __iomem *addr;
> +     struct rtcan_device *dev;
> +     struct c_can_priv *priv;
> +     const struct of_device_id *match;
> +     struct resource *mem;
> +     int irq;
> +     struct clk *clk;
> +     const struct c_can_driver_data *drvdata;
> +     struct device_node *np = pdev->dev.of_node;
> +
> +     match = of_match_device(c_can_of_table, &pdev->dev);
> +     if (match) {
> +             drvdata = match->data;
> +     } else if (pdev->id_entry->driver_data) {
> +             drvdata = (struct c_can_driver_data *)
> +                 platform_get_device_id(pdev)->driver_data;
> +     } else {
> +             return -ENODEV;
> +     }
> +
> +     /* get the appropriate clk */
> +     clk = devm_clk_get(&pdev->dev, NULL);
> +     if (IS_ERR(clk)) {
> +             ret = PTR_ERR(clk);
> +             goto exit;
> +     }
> +
> +     /* get the platform data */
> +     irq = platform_get_irq(pdev, 0);
> +     if (irq <= 0) {
> +             ret = -ENODEV;
> +             goto exit;
> +     }
> +
> +     mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +     addr = devm_ioremap_resource(&pdev->dev, mem);
> +     if (IS_ERR(addr)) {
> +             ret = PTR_ERR(addr);
> +             goto exit;
> +     }
> +
> +     /* allocate the c_can device */
> +     dev = rtcan_c_can_dev_alloc(drvdata->msg_obj_num);
> +     if (!dev) {
> +             ret = -ENOMEM;
> +             goto exit;
> +     }
> +
> +     priv = rtcan_priv(dev);
> +     switch (drvdata->id) {
> +     case BOSCH_C_CAN:
> +             priv->regs = reg_map_c_can;
> +             switch (mem->flags & IORESOURCE_MEM_TYPE_MASK) {
> +             case IORESOURCE_MEM_32BIT:
> +                     priv->read_reg = c_can_plat_read_reg_aligned_to_32bit;
> +                     priv->write_reg = c_can_plat_write_reg_aligned_to_32bit;
> +                     priv->read_reg32 = c_can_plat_read_reg32;
> +                     priv->write_reg32 = c_can_plat_write_reg32;
> +                     break;
> +             case IORESOURCE_MEM_16BIT:
> +             default:
> +                     priv->read_reg = c_can_plat_read_reg_aligned_to_16bit;
> +                     priv->write_reg = c_can_plat_write_reg_aligned_to_16bit;
> +                     priv->read_reg32 = c_can_plat_read_reg32;
> +                     priv->write_reg32 = c_can_plat_write_reg32;
> +                     break;
> +             }
> +             break;
> +     case BOSCH_D_CAN:
> +             priv->regs = reg_map_d_can;
> +             priv->read_reg = c_can_plat_read_reg_aligned_to_16bit;
> +             priv->write_reg = c_can_plat_write_reg_aligned_to_16bit;
> +             priv->read_reg32 = d_can_plat_read_reg32;
> +             priv->write_reg32 = d_can_plat_write_reg32;
> +
> +             /* Check if we need custom RAMINIT via syscon. Mostly for TI
> +              * platforms. Only supported with DT boot.
> +              */
> +             if (np && of_property_read_bool(np, "syscon-raminit")) {
> +                     u32 id;
> +                     struct c_can_raminit *raminit = &priv->raminit_sys;
> +
> +                     ret = -EINVAL;
> +                     raminit->syscon = syscon_regmap_lookup_by_phandle(np,
> +                                                                       
> "syscon-raminit");
> +                     if (IS_ERR(raminit->syscon)) {
> +                             /* can fail with -EPROBE_DEFER */
> +                             ret = PTR_ERR(raminit->syscon);
> +                             goto exit_free_device;
> +                     }
> +
> +                     if (of_property_read_u32_index(np, "syscon-raminit", 1,
> +                                                    &raminit->reg)) {
> +                             dev_err(&pdev->dev,
> +                                     "couldn't get the RAMINIT reg. 
> offset!\n");
> +                             goto exit_free_device;
> +                     }
> +
> +                     if (of_property_read_u32_index(np, "syscon-raminit", 2,
> +                                                    &id)) {
> +                             dev_err(&pdev->dev,
> +                                     "couldn't get the CAN instance ID\n");
> +                             goto exit_free_device;
> +                     }
> +
> +                     if (id >= drvdata->raminit_num) {
> +                             dev_err(&pdev->dev,
> +                                     "Invalid CAN instance ID\n");
> +                             goto exit_free_device;
> +                     }
> +
> +                     raminit->bits = drvdata->raminit_bits[id];
> +                     raminit->needs_pulse = drvdata->raminit_pulse;
> +
> +                     priv->raminit = c_can_hw_raminit_syscon;
> +             } else {
> +                     priv->raminit = c_can_hw_raminit;
> +             }
> +             break;
> +     default:
> +             ret = -EINVAL;
> +             goto exit_free_device;
> +     }
> +
> +     priv->irq = irq;
> +     priv->base = addr;
> +     priv->device = &pdev->dev;
> +     priv->priv = clk;
> +     priv->type = drvdata->id;
> +     platform_set_drvdata(pdev, dev);
> +
> +     dev->ctrl_name = drvdata->id == BOSCH_C_CAN ? "C_CAN" : "D_CAN";
> +     dev->board_name = board_name;
> +     dev->base_addr = (unsigned long)addr;
> +     dev->can_sys_clock = clk_get_rate(clk);
> +     dev->state = CAN_STATE_STOPPED;
> +
> +     /* Give device an interface name */
> +     strncpy(dev->name, DEV_NAME, IFNAMSIZ);
> +
> +     ret = rtcan_c_can_register(dev);
> +     if (ret) {
> +             dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
> +                     DRV_NAME, ret);
> +             goto exit_free_device;
> +     }
> +
> +     dev_info(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n",
> +              DRV_NAME, priv->base, priv->irq);
> +     return 0;
> +
> +exit_free_device:
> +     rtcan_c_can_dev_free(dev);
> +exit:
> +     dev_err(&pdev->dev, "probe failed\n");
> +
> +     return ret;
> +}
> +
> +static int c_can_plat_remove(struct platform_device *pdev)
> +{
> +     struct rtcan_device *dev = platform_get_drvdata(pdev);
> +
> +     rtcan_c_can_unregister(dev);
> +     rtcan_c_can_dev_free(dev);
> +     return 0;
> +}
> +
> +static struct platform_driver c_can_plat_driver = {
> +     .driver = {
> +                .name = DRV_NAME,
> +                .owner = THIS_MODULE,
> +#ifdef CONFIG_OF
> +                .of_match_table = of_match_ptr(c_can_of_table),
> +#endif
> +                },
> +     .id_table = c_can_id_table,
> +     .probe = c_can_plat_probe,
> +     .remove = c_can_plat_remove,
> +};
> +
> +module_platform_driver(c_can_plat_driver);
> +
> +MODULE_AUTHOR("Stephen J. Battazzo <stephen.j.batta...@nasa.gov>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("CAN bus RTDM driver for Bosch C_CAN controller");
> 

Jan

-- 
Siemens AG, T RDA IOT
Corporate Competence Center Embedded Linux

Reply via email to