Commit c6f278d6 authored by Philippe Gerum's avatar Philippe Gerum Committed by Jan Kiszka

drivers/can: flexcan: rebase on mainline v4.14.58

Usable with latest Flexcan controller revisions available from recent
i.MX series. The older driver would break when initializing the chip
(FIFO activation), e.g.:

[  957.052272] rtcan0: real bitrate 1000000, sampling point 75.0%
[  957.058325] rtcan0: writing ctrl=0x011a2003
[  957.062670] rtcan0: flexcan_set_bit_time: mcr=0x5980000f ctrl=0x011a2003
[  957.069403] rtcan0: flexcan_chip_start: writing mcr=0x79a2020f
[  957.069421] rtcan0: flexcan_chip_start: writing ctrl=0x011aac53
[  957.075359] Unhandled fault: imprecise external abort (0x1c06) at 0xaec71a2c
[  957.088371] pgd = cae8c000
[  957.091106] [aec71a2c] *pgd=9d843835, *pte=ba49d75f, *ppte=ba49dc7f
[  957.097447] Internal error: : 1c06 [#1] SMP ARM
[  957.108324] CPU: 1 PID: 843 Comm: rtcanconfig Not tainted 4.14.34 #1
[  957.115391] Hardware name: Freescale i.MX7 Dual (Device Tree)
[  957.121155] I-pipe domain: Linux
[  957.124404] task: caee1900 task.stack: ca5d0000
[  957.128968] PC is at flexcan_chip_start+0x180/0x34c
[  957.133869] LR is at flexcan_chip_start+0x178/0x34c

As a bonus, the latest work arounds addressing silicon bugs are now
included (e.g. use of timestamp-based offloading when the FIFO
ordering does not match the time of arrival).
Signed-off-by: Philippe Gerum's avatarPhilippe Gerum <rpm@xenomai.org>
Signed-off-by: Jan Kiszka's avatarJan Kiszka <jan.kiszka@siemens.com>
parent 8cf9ebf2
/*
* flexcan.c - FLEXCAN RTCAN controller driver
* RTDM-based FLEXCAN CAN controller driver
*
* Copyright (c) 2012 Wolfgang Grandegger <wg@denx.de>
* Rebased on linux 4.14.58 flexcan driver:
* Copyright (c) 2018 Philippe Gerum <rpm@xenomai.org>
*
* Derived from the Linux-CAN driver flexcan.c:
* Original port to RTDM:
* Copyright (c) 2012 Wolfgang Grandegger <wg@denx.de>
*
* Copyright (c) 2005-2006 Varma Electronics Oy
* Copyright (c) 2009 Sascha Hauer, Pengutronix
* Copyright (c) 2010 Marc Kleine-Budde, Pengutronix
* Copyright (c) 2010-2017 Pengutronix, Marc Kleine-Budde <kernel@pengutronix.de>
* Copyright (c) 2014 David Jander, Protonic Holland
*
* Based on code originally by Andrey Volkov <avolkov@varma-el.com>
*
* LICENCE:
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
......@@ -17,41 +23,29 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/regulator/consumer.h>
#include <asm/unaligned.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 <asm/unaligned.h>
#define DEV_NAME "rtcan%d"
#define DRV_NAME "flexcan"
#define DEV_NAME "rtcan%d"
enum flexcan_ip_version {
FLEXCAN_VER_3_0_0,
FLEXCAN_VER_3_0_4,
FLEXCAN_VER_10_0_12,
};
#define CAN_MAX_DLC 8
#define get_can_dlc(i) (min_t(__u8, (i), CAN_MAX_DLC))
/* 8 for RX fifo and 2 error handling */
#define FLEXCAN_NAPI_WEIGHT (8 + 2)
......@@ -72,14 +66,15 @@ enum flexcan_ip_version {
#define FLEXCAN_MCR_WAK_SRC BIT(19)
#define FLEXCAN_MCR_DOZE BIT(18)
#define FLEXCAN_MCR_SRX_DIS BIT(17)
#define FLEXCAN_MCR_BCC BIT(16)
#define FLEXCAN_MCR_IRMQ BIT(16)
#define FLEXCAN_MCR_LPRIO_EN BIT(13)
#define FLEXCAN_MCR_AEN BIT(12)
#define FLEXCAN_MCR_MAXMB(x) ((x) & 0xf)
#define FLEXCAN_MCR_IDAM_A (0 << 8)
#define FLEXCAN_MCR_IDAM_B (1 << 8)
#define FLEXCAN_MCR_IDAM_C (2 << 8)
#define FLEXCAN_MCR_IDAM_D (3 << 8)
/* MCR_MAXMB: maximum used MBs is MAXMB + 1 */
#define FLEXCAN_MCR_MAXMB(x) ((x) & 0x7f)
#define FLEXCAN_MCR_IDAM_A (0x0 << 8)
#define FLEXCAN_MCR_IDAM_B (0x1 << 8)
#define FLEXCAN_MCR_IDAM_C (0x2 << 8)
#define FLEXCAN_MCR_IDAM_D (0x3 << 8)
/* FLEXCAN control register (CANCTRL) bits */
#define FLEXCAN_CTRL_PRESDIV(x) (((x) & 0xff) << 24)
......@@ -105,6 +100,27 @@ enum flexcan_ip_version {
#define FLEXCAN_CTRL_ERR_ALL \
(FLEXCAN_CTRL_ERR_BUS | FLEXCAN_CTRL_ERR_STATE)
/* FLEXCAN control register 2 (CTRL2) bits */
#define FLEXCAN_CTRL2_ECRWRE BIT(29)
#define FLEXCAN_CTRL2_WRMFRZ BIT(28)
#define FLEXCAN_CTRL2_RFFN(x) (((x) & 0x0f) << 24)
#define FLEXCAN_CTRL2_TASD(x) (((x) & 0x1f) << 19)
#define FLEXCAN_CTRL2_MRP BIT(18)
#define FLEXCAN_CTRL2_RRS BIT(17)
#define FLEXCAN_CTRL2_EACEN BIT(16)
/* FLEXCAN memory error control register (MECR) bits */
#define FLEXCAN_MECR_ECRWRDIS BIT(31)
#define FLEXCAN_MECR_HANCEI_MSK BIT(19)
#define FLEXCAN_MECR_FANCEI_MSK BIT(18)
#define FLEXCAN_MECR_CEI_MSK BIT(16)
#define FLEXCAN_MECR_HAERRIE BIT(15)
#define FLEXCAN_MECR_FAERRIE BIT(14)
#define FLEXCAN_MECR_EXTERRIE BIT(13)
#define FLEXCAN_MECR_RERRDIS BIT(9)
#define FLEXCAN_MECR_ECCDIS BIT(8)
#define FLEXCAN_MECR_NCEFAFRZ BIT(7)
/* FLEXCAN error and status register (ESR) bits */
#define FLEXCAN_ESR_TWRN_INT BIT(17)
#define FLEXCAN_ESR_RWRN_INT BIT(16)
......@@ -138,41 +154,62 @@ enum flexcan_ip_version {
FLEXCAN_ESR_BOFF_INT | FLEXCAN_ESR_ERR_INT)
/* FLEXCAN interrupt flag register (IFLAG) bits */
#define FLEXCAN_TX_BUF_ID 8
#define FLEXCAN_IFLAG_BUF(x) BIT(x)
/* Errata ERR005829 step7: Reserve first valid MB */
#define FLEXCAN_TX_MB_RESERVED_OFF_FIFO 8
#define FLEXCAN_TX_MB_OFF_FIFO 9
#define FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP 0
#define FLEXCAN_TX_MB_OFF_TIMESTAMP 1
#define FLEXCAN_RX_MB_OFF_TIMESTAMP_FIRST (FLEXCAN_TX_MB_OFF_TIMESTAMP + 1)
#define FLEXCAN_RX_MB_OFF_TIMESTAMP_LAST 63
#define FLEXCAN_RX_MB_TIMESTAMP_COUNT (FLEXCAN_RX_MB_OFF_TIMESTAMP_LAST - \
FLEXCAN_RX_MB_OFF_TIMESTAMP_FIRST + 1)
#define FLEXCAN_IFLAG_MB(x) BIT(x)
#define FLEXCAN_IFLAG_RX_FIFO_OVERFLOW BIT(7)
#define FLEXCAN_IFLAG_RX_FIFO_WARN BIT(6)
#define FLEXCAN_IFLAG_RX_FIFO_AVAILABLE BIT(5)
#define FLEXCAN_IFLAG_DEFAULT \
(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW | FLEXCAN_IFLAG_RX_FIFO_AVAILABLE | \
FLEXCAN_IFLAG_BUF(FLEXCAN_TX_BUF_ID))
/* FLEXCAN message buffers */
#define FLEXCAN_MB_CNT_CODE(x) (((x) & 0xf) << 24)
#define FLEXCAN_MB_CODE_MASK (0xf << 24)
#define FLEXCAN_MB_CODE_RX_BUSY_BIT (0x1 << 24)
#define FLEXCAN_MB_CODE_RX_INACTIVE (0x0 << 24)
#define FLEXCAN_MB_CODE_RX_EMPTY (0x4 << 24)
#define FLEXCAN_MB_CODE_RX_FULL (0x2 << 24)
#define FLEXCAN_MB_CODE_RX_OVERRUN (0x6 << 24)
#define FLEXCAN_MB_CODE_RX_RANSWER (0xa << 24)
#define FLEXCAN_MB_CODE_TX_INACTIVE (0x8 << 24)
#define FLEXCAN_MB_CODE_TX_ABORT (0x9 << 24)
#define FLEXCAN_MB_CODE_TX_DATA (0xc << 24)
#define FLEXCAN_MB_CODE_TX_TANSWER (0xe << 24)
#define FLEXCAN_MB_CNT_SRR BIT(22)
#define FLEXCAN_MB_CNT_IDE BIT(21)
#define FLEXCAN_MB_CNT_RTR BIT(20)
#define FLEXCAN_MB_CNT_LENGTH(x) (((x) & 0xf) << 16)
#define FLEXCAN_MB_CNT_TIMESTAMP(x) ((x) & 0xffff)
#define FLEXCAN_MB_CODE_MASK (0xf0ffffff)
#define FLEXCAN_TIMEOUT_US (50)
/*
* FLEXCAN hardware feature flags
/* FLEXCAN hardware feature flags
*
* Below is some version info we got:
* SOC Version IP-Version Glitch- [TR]WRN_INT
* Filter? connected?
* MX25 FlexCAN2 03.00.00.00 no no
* MX28 FlexCAN2 03.00.04.00 yes yes
* MX35 FlexCAN2 03.00.00.00 no no
* MX53 FlexCAN2 03.00.00.00 yes no
* MX6s FlexCAN3 10.00.12.00 yes yes
* SOC Version IP-Version Glitch- [TR]WRN_INT IRQ Err Memory err RTR re-
* Filter? connected? Passive detection ception in MB
* MX25 FlexCAN2 03.00.00.00 no no ? no no
* MX28 FlexCAN2 03.00.04.00 yes yes no no no
* MX35 FlexCAN2 03.00.00.00 no no ? no no
* MX53 FlexCAN2 03.00.00.00 yes no no no no
* MX6s FlexCAN3 10.00.12.00 yes yes no no yes
* VF610 FlexCAN3 ? no yes no yes yes?
*
* Some SOCs do not have the RX_WARN & TX_WARN interrupt line connected.
*/
#define FLEXCAN_HAS_V10_FEATURES BIT(1) /* For core version >= 10 */
#define FLEXCAN_HAS_BROKEN_ERR_STATE BIT(2) /* [TR]WRN_INT not connected */
#define FLEXCAN_QUIRK_BROKEN_WERR_STATE BIT(1) /* [TR]WRN_INT not connected */
#define FLEXCAN_QUIRK_DISABLE_RXFG BIT(2) /* Disable RX FIFO Global mask */
#define FLEXCAN_QUIRK_ENABLE_EACEN_RRS BIT(3) /* Enable EACEN and RRS bit in ctrl2 */
#define FLEXCAN_QUIRK_DISABLE_MECR BIT(4) /* Disable Memory error detection */
#define FLEXCAN_QUIRK_USE_OFF_TIMESTAMP BIT(5) /* Use timestamp based offloading */
#define FLEXCAN_QUIRK_BROKEN_PERR_STATE BIT(6) /* No interrupt for error passive */
/* Structure of the message buffer */
struct flexcan_mb {
......@@ -196,49 +233,98 @@ struct flexcan_regs {
u32 imask1; /* 0x28 */
u32 iflag2; /* 0x2c */
u32 iflag1; /* 0x30 */
u32 crl2; /* 0x34 */
union { /* 0x34 */
u32 gfwr_mx28; /* MX28, MX53 */
u32 ctrl2; /* MX6, VF610 */
};
u32 esr2; /* 0x38 */
u32 _reserved2[2];
u32 imeur; /* 0x3c */
u32 lrfr; /* 0x40 */
u32 crcr; /* 0x44 */
u32 rxfgmask; /* 0x48 */
u32 rxfir; /* 0x4c */
u32 _reserved3[12];
struct flexcan_mb cantxfg[64];
u32 _reserved3[12]; /* 0x50 */
struct flexcan_mb mb[64]; /* 0x80 */
/* FIFO-mode:
* MB
* 0x080...0x08f 0 RX message buffer
* 0x090...0x0df 1-5 reserverd
* 0x0e0...0x0ff 6-7 8 entry ID table
* (mx25, mx28, mx35, mx53)
* 0x0e0...0x2df 6-7..37 8..128 entry ID table
* size conf'ed via ctrl2::RFFN
* (mx6, vf610)
*/
u32 _reserved4[256]; /* 0x480 */
u32 rximr[64]; /* 0x880 */
u32 _reserved5[24]; /* 0x980 */
u32 gfwr_mx6; /* 0x9e0 - MX6 */
u32 _reserved6[63]; /* 0x9e4 */
u32 mecr; /* 0xae0 */
u32 erriar; /* 0xae4 */
u32 erridpr; /* 0xae8 */
u32 errippr; /* 0xaec */
u32 rerrar; /* 0xaf0 */
u32 rerrdr; /* 0xaf4 */
u32 rerrsynr; /* 0xaf8 */
u32 errsr; /* 0xafc */
};
struct flexcan_devtype_data {
u32 features; /* hardware controller features */
u32 quirks; /* quirks needed for different IP cores */
};
struct flexcan_priv {
struct rtcan_device *dev;
struct flexcan_timestamped_frame {
struct rtcan_skb skb;
u32 timestamp;
struct list_head next;
};
int irq;
void __iomem *base;
u32 reg_esr;
struct flexcan_priv {
unsigned int irq;
unsigned int mb_first;
unsigned int mb_last;
struct can_bittime bittiming;
struct flexcan_timestamped_frame *ts_frames;
struct flexcan_regs __iomem *regs;
struct flexcan_mb __iomem *tx_mb;
struct flexcan_mb __iomem *tx_mb_reserved;
u8 tx_mb_idx;
u32 reg_ctrl_default;
u32 reg_imask1_default;
u32 reg_imask2_default;
struct can_bittime bit_time;
const struct flexcan_devtype_data *devtype_data;
struct regulator *reg_xceiver;
struct clk *clk_ipg;
struct clk *clk_per;
const struct flexcan_devtype_data *devtype_data;
struct regulator *reg_xceiver;
unsigned long bus_errors;
};
static struct flexcan_devtype_data fsl_p1010_devtype_data = {
.features = FLEXCAN_HAS_BROKEN_ERR_STATE,
static const struct flexcan_devtype_data fsl_p1010_devtype_data = {
.quirks = FLEXCAN_QUIRK_BROKEN_WERR_STATE |
FLEXCAN_QUIRK_BROKEN_PERR_STATE,
};
static struct flexcan_devtype_data fsl_imx28_devtype_data;
static const struct flexcan_devtype_data fsl_imx28_devtype_data = {
.quirks = FLEXCAN_QUIRK_BROKEN_PERR_STATE,
};
static struct flexcan_devtype_data fsl_imx6q_devtype_data = {
.features = FLEXCAN_HAS_V10_FEATURES,
static const struct flexcan_devtype_data fsl_imx6q_devtype_data = {
.quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS |
FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | FLEXCAN_QUIRK_BROKEN_PERR_STATE,
};
static char *flexcan_ctrl_name = "FLEXCAN";
static const struct flexcan_devtype_data fsl_vf610_devtype_data = {
.quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS |
FLEXCAN_QUIRK_DISABLE_MECR | FLEXCAN_QUIRK_USE_OFF_TIMESTAMP |
FLEXCAN_QUIRK_BROKEN_PERR_STATE,
};
static struct can_bittiming_const flexcan_bittiming_const = {
.name = "flexcan",
static const struct can_bittiming_const flexcan_bittiming_const = {
.name = DRV_NAME,
.tseg1_min = 4,
.tseg1_max = 16,
.tseg2_min = 2,
......@@ -249,10 +335,12 @@ static struct can_bittiming_const flexcan_bittiming_const = {
.brp_inc = 1,
};
/*
* Abstract off the read/write for arm versus ppc.
/* Abstract off the read/write for arm versus ppc. This
* assumes that PPC uses big-endian registers and everything
* else uses little-endian registers, independent of CPU
* endianness.
*/
#if defined(__BIG_ENDIAN)
#if defined(CONFIG_PPC)
static inline u32 flexcan_read(void __iomem *addr)
{
return in_be32(addr);
......@@ -274,66 +362,136 @@ static inline void flexcan_write(u32 val, void __iomem *addr)
}
#endif
static inline void flexcan_clk_enable(struct flexcan_priv *priv)
static inline void flexcan_error_irq_enable(const struct flexcan_priv *priv)
{
clk_prepare_enable(priv->clk_ipg);
clk_prepare_enable(priv->clk_per);
struct flexcan_regs __iomem *regs = priv->regs;
u32 reg_ctrl = (priv->reg_ctrl_default | FLEXCAN_CTRL_ERR_MSK);
flexcan_write(reg_ctrl, &regs->ctrl);
}
static inline void flexcan_clk_disable(struct flexcan_priv *priv)
static inline void flexcan_error_irq_disable(const struct flexcan_priv *priv)
{
clk_disable_unprepare(priv->clk_ipg);
clk_disable_unprepare(priv->clk_per);
struct flexcan_regs __iomem *regs = priv->regs;
u32 reg_ctrl = (priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_MSK);
flexcan_write(reg_ctrl, &regs->ctrl);
}
/*
* Switch transceiver on or off
*/
static int flexcan_transceiver_switch(const struct flexcan_priv *priv, int on)
static inline int flexcan_transceiver_enable(const struct flexcan_priv *priv)
{
if (priv->reg_xceiver == NULL)
if (!priv->reg_xceiver)
return 0;
if (on)
return regulator_enable(priv->reg_xceiver);
return regulator_enable(priv->reg_xceiver);
}
static inline int flexcan_transceiver_disable(const struct flexcan_priv *priv)
{
if (!priv->reg_xceiver)
return 0;
return regulator_disable(priv->reg_xceiver);
}
static inline void flexcan_chip_enable(struct flexcan_priv *priv)
static int flexcan_chip_enable(struct flexcan_priv *priv)
{
struct flexcan_regs __iomem *regs = priv->base;
struct flexcan_regs __iomem *regs = priv->regs;
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
u32 reg;
reg = flexcan_read(&regs->mcr);
reg &= ~FLEXCAN_MCR_MDIS;
flexcan_write(reg, &regs->mcr);
udelay(10);
while (timeout-- && (flexcan_read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK))
udelay(10);
if (flexcan_read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK)
return -ETIMEDOUT;
return 0;
}
static inline void flexcan_chip_disable(struct flexcan_priv *priv)
static int flexcan_chip_disable(struct flexcan_priv *priv)
{
struct flexcan_regs __iomem *regs = priv->base;
struct flexcan_regs __iomem *regs = priv->regs;
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
u32 reg;
reg = flexcan_read(&regs->mcr);
reg |= FLEXCAN_MCR_MDIS;
flexcan_write(reg, &regs->mcr);
while (timeout-- && !(flexcan_read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK))
udelay(10);
if (!(flexcan_read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK))
return -ETIMEDOUT;
return 0;
}
static int flexcan_start_xmit(struct rtcan_device *dev, struct can_frame *cf)
static int flexcan_chip_freeze(struct rtcan_device *dev)
{
const struct flexcan_priv *priv = rtcan_priv(dev);
struct flexcan_regs __iomem *regs = priv->base;
u32 can_id;
u32 ctrl;
struct flexcan_priv *priv = rtcan_priv(dev);
struct flexcan_regs __iomem *regs = priv->regs;
unsigned int timeout = 1000 * 1000 * 10 / dev->baudrate;
u32 reg;
reg = flexcan_read(&regs->mcr);
reg |= FLEXCAN_MCR_HALT;
flexcan_write(reg, &regs->mcr);
/* If DLC exceeds 8 bytes adjust it to 8 (for the payload) */
if (cf->can_dlc > 8)
cf->can_dlc = 8;
while (timeout-- && !(flexcan_read(&regs->mcr) & FLEXCAN_MCR_FRZ_ACK))
udelay(100);
ctrl = FLEXCAN_MB_CNT_CODE(0xc) | (cf->can_dlc << 16);
if (!(flexcan_read(&regs->mcr) & FLEXCAN_MCR_FRZ_ACK))
return -ETIMEDOUT;
return 0;
}
static int flexcan_chip_unfreeze(struct flexcan_priv *priv)
{
struct flexcan_regs __iomem *regs = priv->regs;
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
u32 reg;
reg = flexcan_read(&regs->mcr);
reg &= ~FLEXCAN_MCR_HALT;
flexcan_write(reg, &regs->mcr);
while (timeout-- && (flexcan_read(&regs->mcr) & FLEXCAN_MCR_FRZ_ACK))
udelay(10);
if (flexcan_read(&regs->mcr) & FLEXCAN_MCR_FRZ_ACK)
return -ETIMEDOUT;
return 0;
}
static int flexcan_chip_softreset(struct flexcan_priv *priv)
{
struct flexcan_regs __iomem *regs = priv->regs;
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
flexcan_write(FLEXCAN_MCR_SOFTRST, &regs->mcr);
while (timeout-- && (flexcan_read(&regs->mcr) & FLEXCAN_MCR_SOFTRST))
udelay(10);
if (flexcan_read(&regs->mcr) & FLEXCAN_MCR_SOFTRST)
return -ETIMEDOUT;
return 0;
}
static int flexcan_start_xmit(struct rtcan_device *dev, struct can_frame *cf)
{
const struct flexcan_priv *priv = rtcan_priv(dev);
u32 can_id, data, ctrl;
ctrl = FLEXCAN_MB_CODE_TX_DATA | (cf->can_dlc << 16);
if (cf->can_id & CAN_EFF_FLAG) {
can_id = cf->can_id & CAN_EFF_MASK;
ctrl |= FLEXCAN_MB_CNT_IDE | FLEXCAN_MB_CNT_SRR;
......@@ -344,72 +502,92 @@ static int flexcan_start_xmit(struct rtcan_device *dev, struct can_frame *cf)
if (cf->can_id & CAN_RTR_FLAG)
ctrl |= FLEXCAN_MB_CNT_RTR;
if (cf->can_dlc > CAN_MAX_DLC)
cf->can_dlc = CAN_MAX_DLC;
if (cf->can_dlc > 0) {
u32 data = be32_to_cpup((__be32 *)&cf->data[0]);
flexcan_write(data, &regs->cantxfg[FLEXCAN_TX_BUF_ID].data[0]);
data = be32_to_cpup((__be32 *)&cf->data[0]);
flexcan_write(data, &priv->tx_mb->data[0]);
}
if (cf->can_dlc > 3) {
u32 data = be32_to_cpup((__be32 *)&cf->data[4]);
flexcan_write(data, &regs->cantxfg[FLEXCAN_TX_BUF_ID].data[1]);
if (cf->can_dlc > 4) {
data = be32_to_cpup((__be32 *)&cf->data[4]);
flexcan_write(data, &priv->tx_mb->data[1]);
}
flexcan_write(can_id, &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_id);
flexcan_write(ctrl, &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl);
flexcan_write(can_id, &priv->tx_mb->can_id);
flexcan_write(ctrl, &priv->tx_mb->can_ctrl);
/* Errata ERR005829 step8:
* Write twice INACTIVE(0x8) code to first MB.
*/
flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
&priv->tx_mb_reserved->can_ctrl);
flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
&priv->tx_mb_reserved->can_ctrl);
return 0;
}
static void flexcan_do_bus_err(struct rtcan_device *dev,
struct rtcan_rb_frame *cf, u32 reg_esr)
static void init_err_skb(struct rtcan_skb *skb)
{
struct rtcan_rb_frame *cf = &skb->rb_frame;
skb->rb_frame_size = EMPTY_RB_FRAME_SIZE + CAN_ERR_DLC;
cf->can_id = CAN_ERR_FLAG;
cf->can_dlc = CAN_ERR_DLC;
memset(&cf->data[0], 0, cf->can_dlc);
}
static void flexcan_irq_bus_err(struct rtcan_device *dev,
u32 reg_esr, struct rtcan_skb *skb)
{
struct flexcan_priv *priv = rtcan_priv(dev);
struct rtcan_rb_frame *cf = &skb->rb_frame;
init_err_skb(skb);
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
if (reg_esr & FLEXCAN_ESR_BIT1_ERR)
if (reg_esr & FLEXCAN_ESR_BIT1_ERR) {
rtcandev_dbg(dev, "BIT1_ERR irq\n");
cf->data[2] |= CAN_ERR_PROT_BIT1;
if (reg_esr & FLEXCAN_ESR_BIT0_ERR)
}
if (reg_esr & FLEXCAN_ESR_BIT0_ERR) {
rtcandev_dbg(dev, "BIT0_ERR irq\n");
cf->data[2] |= CAN_ERR_PROT_BIT0;
}
if (reg_esr & FLEXCAN_ESR_ACK_ERR) {
rtcandev_dbg(dev, "ACK_ERR irq\n");
cf->can_id |= CAN_ERR_ACK;
cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
cf->data[3] = CAN_ERR_PROT_LOC_ACK;
}
if (reg_esr & FLEXCAN_ESR_CRC_ERR) {
rtcandev_dbg(dev, "CRC_ERR irq\n");
cf->data[2] |= CAN_ERR_PROT_BIT;
cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
}
if (reg_esr & FLEXCAN_ESR_FRM_ERR)
if (reg_esr & FLEXCAN_ESR_FRM_ERR) {
rtcandev_dbg(dev, "FRM_ERR irq\n");
cf->data[2] |= CAN_ERR_PROT_FORM;
if (reg_esr & FLEXCAN_ESR_STF_ERR)
}
if (reg_esr & FLEXCAN_ESR_STF_ERR) {
rtcandev_dbg(dev, "STF_ERR irq\n");
cf->data[2] |= CAN_ERR_PROT_STUFF;
}
static can_state_t flexcan_get_state(struct rtcan_device *dev, u32 reg_esr)
{
int flt;
flt = reg_esr & FLEXCAN_ESR_FLT_CONF_MASK;
if (likely(flt == FLEXCAN_ESR_FLT_CONF_ACTIVE)) {
if (likely(!(reg_esr & (FLEXCAN_ESR_TX_WRN |
FLEXCAN_ESR_RX_WRN))))
return CAN_STATE_ERROR_ACTIVE;
else
return CAN_STATE_ERROR_WARNING;
} else if (unlikely(flt == FLEXCAN_ESR_FLT_CONF_PASSIVE)) {
return CAN_STATE_ERROR_PASSIVE;
} else {
return CAN_STATE_BUS_OFF;
}
priv->bus_errors++;
}
static void flexcan_do_state(struct rtcan_device *dev,
struct rtcan_rb_frame *cf,
can_state_t new_state)
{
struct flexcan_priv *priv = rtcan_priv(dev);
struct flexcan_regs __iomem *regs = priv->base;
u32 reg = flexcan_read(&regs->ecr);
u8 txerr = reg & 0xff;
u8 rxerr = (reg >> 8) & 0xff;
struct berr_counter {
u16 txerr;
u16 rxerr;
};
static void flexcan_change_state(struct rtcan_device *dev,
struct rtcan_rb_frame *cf,
struct berr_counter *bec,
can_state_t new_state)
{
switch (dev->state) {
case CAN_STATE_ERROR_ACTIVE:
/*
......@@ -422,7 +600,7 @@ static void flexcan_do_state(struct rtcan_device *dev,
rtcandev_dbg(dev, "Error Warning IRQ\n");
cf->can_id |= CAN_ERR_CRTL;
cf->data[1] = (txerr > rxerr) ?
cf->data[1] = (bec->txerr > bec->rxerr) ?
CAN_ERR_CRTL_TX_WARNING :
CAN_ERR_CRTL_RX_WARNING;
}
......@@ -437,7 +615,7 @@ static void flexcan_do_state(struct rtcan_device *dev,
rtcandev_dbg(dev, "Error Passive IRQ\n");
cf->can_id |= CAN_ERR_CRTL;
cf->data[1] = (txerr > rxerr) ?
cf->data[1] = (bec->txerr > bec->rxerr) ?
CAN_ERR_CRTL_TX_PASSIVE :
CAN_ERR_CRTL_RX_PASSIVE;
}
......@@ -465,186 +643,307 @@ static void flexcan_do_state(struct rtcan_device *dev,
default:
break;
}
dev->state = new_state;
}
static void flexcan_rx_interrupt(struct rtcan_device *dev,
struct rtcan_skb *skb)
static bool flexcan_irq_state(struct rtcan_device *dev, u32 reg_esr,
struct rtcan_skb *skb)
{
const struct flexcan_priv *priv = rtcan_priv(dev);
struct flexcan_regs __iomem *regs = priv->base;
struct flexcan_mb __iomem *mb = &regs->cantxfg[0];
struct flexcan_priv *priv = rtcan_priv(dev);
struct flexcan_regs __iomem *regs = priv->regs;
enum CAN_STATE new_state, rx_state, tx_state;
struct rtcan_rb_frame *cf = &skb->rb_frame;
struct berr_counter bec;
u32 reg;
int flt;
reg = flexcan_read(&regs->ecr);
bec.txerr = (reg >> 0) & 0xff;
bec.rxerr = (reg >> 8) & 0xff;
flt = reg_esr & FLEXCAN_ESR_FLT_CONF_MASK;
if (likely(flt == FLEXCAN_ESR_FLT_CONF_ACTIVE)) {
tx_state = unlikely(reg_esr & FLEXCAN_ESR_TX_WRN) ?
CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE;
rx_state = unlikely(reg_esr & FLEXCAN_ESR_RX_WRN) ?
CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE;
new_state = max(tx_state, rx_state);
} else
new_state = flt == FLEXCAN_ESR_FLT_CONF_PASSIVE ?
CAN_STATE_ERROR_PASSIVE : CAN_STATE_BUS_OFF;
/* state hasn't changed */
if (likely(new_state == dev->state))
return false;
init_err_skb(skb);
flexcan_change_state(dev, cf, &bec, new_state);
return true;
}
static unsigned int flexcan_mailbox_read(struct rtcan_device *dev,
struct rtcan_skb *skb,
u32 *timestamp, unsigned int n)
{
struct flexcan_priv *priv = rtcan_priv(dev);
struct flexcan_regs __iomem *regs = priv->regs;
struct flexcan_mb __iomem *mb = &regs->mb[n];
u32 reg_ctrl, reg_id, reg_iflag1, code;
struct rtcan_rb_frame *cf = &skb->rb_frame;
u32 reg_ctrl, reg_id;
reg_ctrl = flexcan_read(&mb->can_ctrl);
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
do {
reg_ctrl = flexcan_read(&mb->can_ctrl);
} while (reg_ctrl & FLEXCAN_MB_CODE_RX_BUSY_BIT);
/* is this MB empty? */
code = reg_ctrl & FLEXCAN_MB_CODE_MASK;
if ((code != FLEXCAN_MB_CODE_RX_FULL) &&
(code != FLEXCAN_MB_CODE_RX_OVERRUN))
return 0;
} else {
reg_iflag1 = flexcan_read(&regs->iflag1);
if (!(reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE))
return 0;
reg_ctrl = flexcan_read(&mb->can_ctrl);
}