summaryrefslogtreecommitdiffstats
path: root/drivers/net/can/flexcan.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/can/flexcan.c')
-rw-r--r--drivers/net/can/flexcan.c120
1 files changed, 107 insertions, 13 deletions
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index f425ec2..2700865 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -92,6 +92,27 @@
#define FLEXCAN_CTRL_ERR_ALL \
(FLEXCAN_CTRL_ERR_BUS | FLEXCAN_CTRL_ERR_STATE)
+/* FLEXCAN control register 2 (CTRL2) bits */
+#define FLEXCAN_CRL2_ECRWRE BIT(29)
+#define FLEXCAN_CRL2_WRMFRZ BIT(28)
+#define FLEXCAN_CRL2_RFFN(x) (((x) & 0x0f) << 24)
+#define FLEXCAN_CRL2_TASD(x) (((x) & 0x1f) << 19)
+#define FLEXCAN_CRL2_MRP BIT(18)
+#define FLEXCAN_CRL2_RRS BIT(17)
+#define FLEXCAN_CRL2_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)
@@ -150,18 +171,20 @@
* 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 Memory err
+ * Filter? connected? detection
+ * MX25 FlexCAN2 03.00.00.00 no no no
+ * MX28 FlexCAN2 03.00.04.00 yes yes no
+ * MX35 FlexCAN2 03.00.00.00 no no no
+ * MX53 FlexCAN2 03.00.00.00 yes no no
+ * MX6s FlexCAN3 10.00.12.00 yes yes no
+ * VF610 FlexCAN3 ? 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_HAS_MECR_FEATURES BIT(3) /* Memory error detection */
/* Structure of the message buffer */
struct flexcan_mb {
@@ -192,8 +215,17 @@ struct flexcan_regs {
u32 crcr; /* 0x44 */
u32 rxfgmask; /* 0x48 */
u32 rxfir; /* 0x4c */
- u32 _reserved3[12];
- struct flexcan_mb cantxfg[64];
+ u32 _reserved3[12]; /* 0x50 */
+ struct flexcan_mb cantxfg[64]; /* 0x80 */
+ u32 _reserved4[408];
+ 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 {
@@ -223,6 +255,9 @@ static struct flexcan_devtype_data fsl_imx28_devtype_data;
static struct flexcan_devtype_data fsl_imx6q_devtype_data = {
.features = FLEXCAN_HAS_V10_FEATURES,
};
+static struct flexcan_devtype_data fsl_vf610_devtype_data = {
+ .features = FLEXCAN_HAS_V10_FEATURES | FLEXCAN_HAS_MECR_FEATURES,
+};
static const struct can_bittiming_const flexcan_bittiming_const = {
.name = DRV_NAME,
@@ -378,8 +413,9 @@ static int flexcan_chip_softreset(struct flexcan_priv *priv)
return 0;
}
-static int flexcan_get_berr_counter(const struct net_device *dev,
- struct can_berr_counter *bec)
+
+static int __flexcan_get_berr_counter(const struct net_device *dev,
+ struct can_berr_counter *bec)
{
const struct flexcan_priv *priv = netdev_priv(dev);
struct flexcan_regs __iomem *regs = priv->base;
@@ -391,6 +427,29 @@ static int flexcan_get_berr_counter(const struct net_device *dev,
return 0;
}
+static int flexcan_get_berr_counter(const struct net_device *dev,
+ struct can_berr_counter *bec)
+{
+ const struct flexcan_priv *priv = netdev_priv(dev);
+ int err;
+
+ err = clk_prepare_enable(priv->clk_ipg);
+ if (err)
+ return err;
+
+ err = clk_prepare_enable(priv->clk_per);
+ if (err)
+ goto out_disable_ipg;
+
+ err = __flexcan_get_berr_counter(dev, bec);
+
+ clk_disable_unprepare(priv->clk_per);
+ out_disable_ipg:
+ clk_disable_unprepare(priv->clk_ipg);
+
+ return err;
+}
+
static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
const struct flexcan_priv *priv = netdev_priv(dev);
@@ -503,7 +562,7 @@ static void do_state(struct net_device *dev,
struct flexcan_priv *priv = netdev_priv(dev);
struct can_berr_counter bec;
- flexcan_get_berr_counter(dev, &bec);
+ __flexcan_get_berr_counter(dev, &bec);
switch (priv->can.state) {
case CAN_STATE_ERROR_ACTIVE:
@@ -549,6 +608,13 @@ static void do_state(struct net_device *dev,
/* process state changes depending on the new state */
switch (new_state) {
+ case CAN_STATE_ERROR_WARNING:
+ netdev_dbg(dev, "Error Warning\n");
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = (bec.txerr > bec.rxerr) ?
+ CAN_ERR_CRTL_TX_WARNING :
+ CAN_ERR_CRTL_RX_WARNING;
+ break;
case CAN_STATE_ERROR_ACTIVE:
netdev_dbg(dev, "Error Active\n");
cf->can_id |= CAN_ERR_PROT;
@@ -793,7 +859,7 @@ static int flexcan_chip_start(struct net_device *dev)
struct flexcan_priv *priv = netdev_priv(dev);
struct flexcan_regs __iomem *regs = priv->base;
int err;
- u32 reg_mcr, reg_ctrl;
+ u32 reg_mcr, reg_ctrl, reg_crl2, reg_mecr;
/* enable module */
err = flexcan_chip_enable(priv);
@@ -852,6 +918,8 @@ static int flexcan_chip_start(struct net_device *dev)
if (priv->devtype_data->features & FLEXCAN_HAS_BROKEN_ERR_STATE ||
priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
reg_ctrl |= FLEXCAN_CTRL_ERR_MSK;
+ else
+ reg_ctrl &= ~FLEXCAN_CTRL_ERR_MSK;
/* save for later use */
priv->reg_ctrl_default = reg_ctrl;
@@ -870,6 +938,31 @@ static int flexcan_chip_start(struct net_device *dev)
if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES)
flexcan_write(0x0, &regs->rxfgmask);
+ /*
+ * On Vybrid, disable memory error detection interrupts
+ * and freeze mode.
+ * This also works around errata e5295 which generates
+ * false positive memory errors and put the device in
+ * freeze mode.
+ */
+ if (priv->devtype_data->features & FLEXCAN_HAS_MECR_FEATURES) {
+ /*
+ * Follow the protocol as described in "Detection
+ * and Correction of Memory Errors" to write to
+ * MECR register
+ */
+ reg_crl2 = flexcan_read(&regs->crl2);
+ reg_crl2 |= FLEXCAN_CRL2_ECRWRE;
+ flexcan_write(reg_crl2, &regs->crl2);
+
+ reg_mecr = flexcan_read(&regs->mecr);
+ reg_mecr &= ~FLEXCAN_MECR_ECRWRDIS;
+ flexcan_write(reg_mecr, &regs->mecr);
+ reg_mecr &= ~(FLEXCAN_MECR_NCEFAFRZ | FLEXCAN_MECR_HANCEI_MSK |
+ FLEXCAN_MECR_FANCEI_MSK);
+ flexcan_write(reg_mecr, &regs->mecr);
+ }
+
err = flexcan_transceiver_enable(priv);
if (err)
goto out_chip_disable;
@@ -1080,6 +1173,7 @@ static const struct of_device_id flexcan_of_match[] = {
{ .compatible = "fsl,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, },
{ .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, },
{ .compatible = "fsl,p1010-flexcan", .data = &fsl_p1010_devtype_data, },
+ { .compatible = "fsl,vf610-flexcan", .data = &fsl_vf610_devtype_data, },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, flexcan_of_match);
OpenPOWER on IntegriCloud