Commit 880dd464 authored by Marek Vasut's avatar Marek Vasut Committed by Marc Kleine-Budde

can: ifi: Repair the error handling

The new version of the IFI CANFD core has significantly less complex
error state indication logic. In particular, the warning/error state
bits are no longer all over the place, but are all present in the
STATUS register. Moreover, there is a new IRQ register bit indicating
transition between error states (active/warning/passive/busoff).

This patch makes use of this bit to weed out the obscure selective
INTERRUPT register clearing, which was used to carry over the error
state indication into the poll function. While at it, this patch
fixes the handling of the ACTIVE state, since the hardware provides
indication of the core being in ACTIVE state and that in turn fixes
the state transition indication toward userspace. Finally, register
reads in the poll function are moved to the matching subfunctions
since those are also no longer needed in the poll function.
Signed-off-by: default avatarMarek Vasut <marex@denx.de>
Cc: Heiko Schocher <hs@denx.de>
Cc: Markus Marb <markus@marb.org>
Cc: Marc Kleine-Budde <mkl@pengutronix.de>
Cc: linux-stable <stable@vger.kernel.org>
Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 591d65d5
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#define IFI_CANFD_STCMD_ERROR_ACTIVE BIT(2) #define IFI_CANFD_STCMD_ERROR_ACTIVE BIT(2)
#define IFI_CANFD_STCMD_ERROR_PASSIVE BIT(3) #define IFI_CANFD_STCMD_ERROR_PASSIVE BIT(3)
#define IFI_CANFD_STCMD_BUSOFF BIT(4) #define IFI_CANFD_STCMD_BUSOFF BIT(4)
#define IFI_CANFD_STCMD_ERROR_WARNING BIT(5)
#define IFI_CANFD_STCMD_BUSMONITOR BIT(16) #define IFI_CANFD_STCMD_BUSMONITOR BIT(16)
#define IFI_CANFD_STCMD_LOOPBACK BIT(18) #define IFI_CANFD_STCMD_LOOPBACK BIT(18)
#define IFI_CANFD_STCMD_DISABLE_CANFD BIT(24) #define IFI_CANFD_STCMD_DISABLE_CANFD BIT(24)
...@@ -52,7 +53,10 @@ ...@@ -52,7 +53,10 @@
#define IFI_CANFD_TXSTCMD_OVERFLOW BIT(13) #define IFI_CANFD_TXSTCMD_OVERFLOW BIT(13)
#define IFI_CANFD_INTERRUPT 0xc #define IFI_CANFD_INTERRUPT 0xc
#define IFI_CANFD_INTERRUPT_ERROR_BUSOFF BIT(0)
#define IFI_CANFD_INTERRUPT_ERROR_WARNING BIT(1) #define IFI_CANFD_INTERRUPT_ERROR_WARNING BIT(1)
#define IFI_CANFD_INTERRUPT_ERROR_STATE_CHG BIT(2)
#define IFI_CANFD_INTERRUPT_ERROR_REC_TEC_INC BIT(3)
#define IFI_CANFD_INTERRUPT_ERROR_COUNTER BIT(10) #define IFI_CANFD_INTERRUPT_ERROR_COUNTER BIT(10)
#define IFI_CANFD_INTERRUPT_TXFIFO_EMPTY BIT(16) #define IFI_CANFD_INTERRUPT_TXFIFO_EMPTY BIT(16)
#define IFI_CANFD_INTERRUPT_TXFIFO_REMOVE BIT(22) #define IFI_CANFD_INTERRUPT_TXFIFO_REMOVE BIT(22)
...@@ -61,6 +65,10 @@ ...@@ -61,6 +65,10 @@
#define IFI_CANFD_INTERRUPT_SET_IRQ ((u32)BIT(31)) #define IFI_CANFD_INTERRUPT_SET_IRQ ((u32)BIT(31))
#define IFI_CANFD_IRQMASK 0x10 #define IFI_CANFD_IRQMASK 0x10
#define IFI_CANFD_IRQMASK_ERROR_BUSOFF BIT(0)
#define IFI_CANFD_IRQMASK_ERROR_WARNING BIT(1)
#define IFI_CANFD_IRQMASK_ERROR_STATE_CHG BIT(2)
#define IFI_CANFD_IRQMASK_ERROR_REC_TEC_INC BIT(3)
#define IFI_CANFD_IRQMASK_SET_ERR BIT(7) #define IFI_CANFD_IRQMASK_SET_ERR BIT(7)
#define IFI_CANFD_IRQMASK_SET_TS BIT(15) #define IFI_CANFD_IRQMASK_SET_TS BIT(15)
#define IFI_CANFD_IRQMASK_TXFIFO_EMPTY BIT(16) #define IFI_CANFD_IRQMASK_TXFIFO_EMPTY BIT(16)
...@@ -222,7 +230,10 @@ static void ifi_canfd_irq_enable(struct net_device *ndev, bool enable) ...@@ -222,7 +230,10 @@ static void ifi_canfd_irq_enable(struct net_device *ndev, bool enable)
if (enable) { if (enable) {
enirq = IFI_CANFD_IRQMASK_TXFIFO_EMPTY | enirq = IFI_CANFD_IRQMASK_TXFIFO_EMPTY |
IFI_CANFD_IRQMASK_RXFIFO_NEMPTY; IFI_CANFD_IRQMASK_RXFIFO_NEMPTY |
IFI_CANFD_IRQMASK_ERROR_STATE_CHG |
IFI_CANFD_IRQMASK_ERROR_WARNING |
IFI_CANFD_IRQMASK_ERROR_BUSOFF;
if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
enirq |= IFI_CANFD_INTERRUPT_ERROR_COUNTER; enirq |= IFI_CANFD_INTERRUPT_ERROR_COUNTER;
} }
...@@ -363,12 +374,13 @@ static int ifi_canfd_handle_lost_msg(struct net_device *ndev) ...@@ -363,12 +374,13 @@ static int ifi_canfd_handle_lost_msg(struct net_device *ndev)
return 1; return 1;
} }
static int ifi_canfd_handle_lec_err(struct net_device *ndev, const u32 errctr) static int ifi_canfd_handle_lec_err(struct net_device *ndev)
{ {
struct ifi_canfd_priv *priv = netdev_priv(ndev); struct ifi_canfd_priv *priv = netdev_priv(ndev);
struct net_device_stats *stats = &ndev->stats; struct net_device_stats *stats = &ndev->stats;
struct can_frame *cf; struct can_frame *cf;
struct sk_buff *skb; struct sk_buff *skb;
u32 errctr = readl(priv->base + IFI_CANFD_ERROR_CTR);
const u32 errmask = IFI_CANFD_ERROR_CTR_OVERLOAD_FIRST | const u32 errmask = IFI_CANFD_ERROR_CTR_OVERLOAD_FIRST |
IFI_CANFD_ERROR_CTR_ACK_ERROR_FIRST | IFI_CANFD_ERROR_CTR_ACK_ERROR_FIRST |
IFI_CANFD_ERROR_CTR_BIT0_ERROR_FIRST | IFI_CANFD_ERROR_CTR_BIT0_ERROR_FIRST |
...@@ -451,6 +463,11 @@ static int ifi_canfd_handle_state_change(struct net_device *ndev, ...@@ -451,6 +463,11 @@ static int ifi_canfd_handle_state_change(struct net_device *ndev,
switch (new_state) { switch (new_state) {
case CAN_STATE_ERROR_ACTIVE: case CAN_STATE_ERROR_ACTIVE:
/* error active state */
priv->can.can_stats.error_warning++;
priv->can.state = CAN_STATE_ERROR_ACTIVE;
break;
case CAN_STATE_ERROR_WARNING:
/* error warning state */ /* error warning state */
priv->can.can_stats.error_warning++; priv->can.can_stats.error_warning++;
priv->can.state = CAN_STATE_ERROR_WARNING; priv->can.state = CAN_STATE_ERROR_WARNING;
...@@ -479,7 +496,7 @@ static int ifi_canfd_handle_state_change(struct net_device *ndev, ...@@ -479,7 +496,7 @@ static int ifi_canfd_handle_state_change(struct net_device *ndev,
ifi_canfd_get_berr_counter(ndev, &bec); ifi_canfd_get_berr_counter(ndev, &bec);
switch (new_state) { switch (new_state) {
case CAN_STATE_ERROR_ACTIVE: case CAN_STATE_ERROR_WARNING:
/* error warning state */ /* error warning state */
cf->can_id |= CAN_ERR_CRTL; cf->can_id |= CAN_ERR_CRTL;
cf->data[1] = (bec.txerr > bec.rxerr) ? cf->data[1] = (bec.txerr > bec.rxerr) ?
...@@ -512,22 +529,21 @@ static int ifi_canfd_handle_state_change(struct net_device *ndev, ...@@ -512,22 +529,21 @@ static int ifi_canfd_handle_state_change(struct net_device *ndev,
return 1; return 1;
} }
static int ifi_canfd_handle_state_errors(struct net_device *ndev, u32 stcmd) static int ifi_canfd_handle_state_errors(struct net_device *ndev)
{ {
struct ifi_canfd_priv *priv = netdev_priv(ndev); struct ifi_canfd_priv *priv = netdev_priv(ndev);
u32 stcmd = readl(priv->base + IFI_CANFD_STCMD);
int work_done = 0; int work_done = 0;
u32 isr;
/* if ((stcmd & IFI_CANFD_STCMD_ERROR_ACTIVE) &&
* The ErrWarn condition is a little special, since the bit is (priv->can.state != CAN_STATE_ERROR_ACTIVE)) {
* located in the INTERRUPT register instead of STCMD register. netdev_dbg(ndev, "Error, entered active state\n");
*/ work_done += ifi_canfd_handle_state_change(ndev,
isr = readl(priv->base + IFI_CANFD_INTERRUPT); CAN_STATE_ERROR_ACTIVE);
if ((isr & IFI_CANFD_INTERRUPT_ERROR_WARNING) && }
if ((stcmd & IFI_CANFD_STCMD_ERROR_WARNING) &&
(priv->can.state != CAN_STATE_ERROR_WARNING)) { (priv->can.state != CAN_STATE_ERROR_WARNING)) {
/* Clear the interrupt */
writel(IFI_CANFD_INTERRUPT_ERROR_WARNING,
priv->base + IFI_CANFD_INTERRUPT);
netdev_dbg(ndev, "Error, entered warning state\n"); netdev_dbg(ndev, "Error, entered warning state\n");
work_done += ifi_canfd_handle_state_change(ndev, work_done += ifi_canfd_handle_state_change(ndev,
CAN_STATE_ERROR_WARNING); CAN_STATE_ERROR_WARNING);
...@@ -554,18 +570,11 @@ static int ifi_canfd_poll(struct napi_struct *napi, int quota) ...@@ -554,18 +570,11 @@ static int ifi_canfd_poll(struct napi_struct *napi, int quota)
{ {
struct net_device *ndev = napi->dev; struct net_device *ndev = napi->dev;
struct ifi_canfd_priv *priv = netdev_priv(ndev); struct ifi_canfd_priv *priv = netdev_priv(ndev);
const u32 stcmd_state_mask = IFI_CANFD_STCMD_ERROR_PASSIVE |
IFI_CANFD_STCMD_BUSOFF;
int work_done = 0;
u32 stcmd = readl(priv->base + IFI_CANFD_STCMD);
u32 rxstcmd = readl(priv->base + IFI_CANFD_RXSTCMD); u32 rxstcmd = readl(priv->base + IFI_CANFD_RXSTCMD);
u32 errctr = readl(priv->base + IFI_CANFD_ERROR_CTR); int work_done = 0;
/* Handle bus state changes */ /* Handle bus state changes */
if ((stcmd & stcmd_state_mask) || work_done += ifi_canfd_handle_state_errors(ndev);
((stcmd & IFI_CANFD_STCMD_ERROR_ACTIVE) == 0))
work_done += ifi_canfd_handle_state_errors(ndev, stcmd);
/* Handle lost messages on RX */ /* Handle lost messages on RX */
if (rxstcmd & IFI_CANFD_RXSTCMD_OVERFLOW) if (rxstcmd & IFI_CANFD_RXSTCMD_OVERFLOW)
...@@ -573,7 +582,7 @@ static int ifi_canfd_poll(struct napi_struct *napi, int quota) ...@@ -573,7 +582,7 @@ static int ifi_canfd_poll(struct napi_struct *napi, int quota)
/* Handle lec errors on the bus */ /* Handle lec errors on the bus */
if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
work_done += ifi_canfd_handle_lec_err(ndev, errctr); work_done += ifi_canfd_handle_lec_err(ndev);
/* Handle normal messages on RX */ /* Handle normal messages on RX */
if (!(rxstcmd & IFI_CANFD_RXSTCMD_EMPTY)) if (!(rxstcmd & IFI_CANFD_RXSTCMD_EMPTY))
...@@ -594,12 +603,13 @@ static irqreturn_t ifi_canfd_isr(int irq, void *dev_id) ...@@ -594,12 +603,13 @@ static irqreturn_t ifi_canfd_isr(int irq, void *dev_id)
struct net_device_stats *stats = &ndev->stats; struct net_device_stats *stats = &ndev->stats;
const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY | const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY |
IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER | IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER |
IFI_CANFD_INTERRUPT_ERROR_COUNTER |
IFI_CANFD_INTERRUPT_ERROR_STATE_CHG |
IFI_CANFD_INTERRUPT_ERROR_WARNING | IFI_CANFD_INTERRUPT_ERROR_WARNING |
IFI_CANFD_INTERRUPT_ERROR_COUNTER; IFI_CANFD_INTERRUPT_ERROR_BUSOFF;
const u32 tx_irq_mask = IFI_CANFD_INTERRUPT_TXFIFO_EMPTY | const u32 tx_irq_mask = IFI_CANFD_INTERRUPT_TXFIFO_EMPTY |
IFI_CANFD_INTERRUPT_TXFIFO_REMOVE; IFI_CANFD_INTERRUPT_TXFIFO_REMOVE;
const u32 clr_irq_mask = ~((u32)(IFI_CANFD_INTERRUPT_SET_IRQ | const u32 clr_irq_mask = ~((u32)IFI_CANFD_INTERRUPT_SET_IRQ);
IFI_CANFD_INTERRUPT_ERROR_WARNING));
u32 isr; u32 isr;
isr = readl(priv->base + IFI_CANFD_INTERRUPT); isr = readl(priv->base + IFI_CANFD_INTERRUPT);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment