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> --- Changes in v2: - Drop 'default n' Kconfig - Enable the driver for Xenomai CI builds - Drop commented code - Drop power managemet (pm) code .gitlab-ci.yml | 2 + kernel/drivers/can/Kconfig | 1 + kernel/drivers/can/Makefile | 2 +- kernel/drivers/can/README | 1 + kernel/drivers/can/c_can/Kconfig | 15 + kernel/drivers/can/c_can/Makefile | 8 + kernel/drivers/can/c_can/rtcan_c_can.c | 1298 +++++++++++++++++ kernel/drivers/can/c_can/rtcan_c_can.h | 255 ++++ .../drivers/can/c_can/rtcan_c_can_ethtool.c | 47 + .../drivers/can/c_can/rtcan_c_can_platform.c | 461 ++++++ 10 files changed, 2089 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/.gitlab-ci.yml b/.gitlab-ci.yml index 602a7682a..b54baf25b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -118,6 +118,8 @@ variables: - ./scripts/config -e XENO_DRIVERS_CAN_SJA1000_ESD_PCI - ./scripts/config -e XENO_DRIVERS_CAN_SJA1000_PEAK_DNG - ./scripts/config -e XENO_DRIVERS_CAN_PEAK_CANFD + - ./scripts/config -e XENO_DRIVERS_CAN_C_CAN + - ./scripts/config -e XENO_DRIVERS_CAN_C_CAN_PLATFORM - ./scripts/config -m XENO_DRIVERS_NET - ./scripts/config -e XENO_DRIVERS_RTNET_CHECKED - ./scripts/config -e XENO_DRIVERS_NET_ETH_P_ALL 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..6d9b37921 --- /dev/null +++ b/kernel/drivers/can/c_can/Kconfig @@ -0,0 +1,15 @@ +config XENO_DRIVERS_CAN_C_CAN + depends on XENO_DRIVERS_CAN && OF + tristate "Bosch C-CAN based chips" + +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. 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..0f7bc3e53 --- /dev/null +++ b/kernel/drivers/can/c_can/rtcan_c_can.c @@ -0,0 +1,1298 @@ +/* + * 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); +} + +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); +} + +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); + 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); + 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); + +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..2edbb13ea --- /dev/null +++ b/kernel/drivers/can/c_can/rtcan_c_can.h @@ -0,0 +1,255 @@ +/* + * CAN bus driver for Bosch C_CAN controller, ported to Xenomai RTDM + * + * Copyright 2021, Dario Binacchi <dario...@libero.it> + * + * 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); + +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); +/* + * 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"); -- 2.17.1