|
@@ -30,6 +30,7 @@
|
|
|
#define IFI_CANFD_STCMD_ERROR_ACTIVE BIT(2)
|
|
|
#define IFI_CANFD_STCMD_ERROR_PASSIVE BIT(3)
|
|
|
#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_LOOPBACK BIT(18)
|
|
|
#define IFI_CANFD_STCMD_DISABLE_CANFD BIT(24)
|
|
@@ -52,7 +53,10 @@
|
|
|
#define IFI_CANFD_TXSTCMD_OVERFLOW BIT(13)
|
|
|
|
|
|
#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_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_TXFIFO_EMPTY BIT(16)
|
|
|
#define IFI_CANFD_INTERRUPT_TXFIFO_REMOVE BIT(22)
|
|
@@ -61,6 +65,10 @@
|
|
|
#define IFI_CANFD_INTERRUPT_SET_IRQ ((u32)BIT(31))
|
|
|
|
|
|
#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_TS BIT(15)
|
|
|
#define IFI_CANFD_IRQMASK_TXFIFO_EMPTY BIT(16)
|
|
@@ -222,7 +230,10 @@ static void ifi_canfd_irq_enable(struct net_device *ndev, bool enable)
|
|
|
|
|
|
if (enable) {
|
|
|
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)
|
|
|
enirq |= IFI_CANFD_INTERRUPT_ERROR_COUNTER;
|
|
|
}
|
|
@@ -363,12 +374,13 @@ static int ifi_canfd_handle_lost_msg(struct net_device *ndev)
|
|
|
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 net_device_stats *stats = &ndev->stats;
|
|
|
struct can_frame *cf;
|
|
|
struct sk_buff *skb;
|
|
|
+ u32 errctr = readl(priv->base + IFI_CANFD_ERROR_CTR);
|
|
|
const u32 errmask = IFI_CANFD_ERROR_CTR_OVERLOAD_FIRST |
|
|
|
IFI_CANFD_ERROR_CTR_ACK_ERROR_FIRST |
|
|
|
IFI_CANFD_ERROR_CTR_BIT0_ERROR_FIRST |
|
|
@@ -451,6 +463,11 @@ static int ifi_canfd_handle_state_change(struct net_device *ndev,
|
|
|
|
|
|
switch (new_state) {
|
|
|
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 */
|
|
|
priv->can.can_stats.error_warning++;
|
|
|
priv->can.state = CAN_STATE_ERROR_WARNING;
|
|
@@ -479,7 +496,7 @@ static int ifi_canfd_handle_state_change(struct net_device *ndev,
|
|
|
ifi_canfd_get_berr_counter(ndev, &bec);
|
|
|
|
|
|
switch (new_state) {
|
|
|
- case CAN_STATE_ERROR_ACTIVE:
|
|
|
+ case CAN_STATE_ERROR_WARNING:
|
|
|
/* error warning state */
|
|
|
cf->can_id |= CAN_ERR_CRTL;
|
|
|
cf->data[1] = (bec.txerr > bec.rxerr) ?
|
|
@@ -512,22 +529,21 @@ static int ifi_canfd_handle_state_change(struct net_device *ndev,
|
|
|
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);
|
|
|
+ u32 stcmd = readl(priv->base + IFI_CANFD_STCMD);
|
|
|
int work_done = 0;
|
|
|
- u32 isr;
|
|
|
|
|
|
- /*
|
|
|
- * The ErrWarn condition is a little special, since the bit is
|
|
|
- * located in the INTERRUPT register instead of STCMD register.
|
|
|
- */
|
|
|
- isr = readl(priv->base + IFI_CANFD_INTERRUPT);
|
|
|
- if ((isr & IFI_CANFD_INTERRUPT_ERROR_WARNING) &&
|
|
|
+ if ((stcmd & IFI_CANFD_STCMD_ERROR_ACTIVE) &&
|
|
|
+ (priv->can.state != CAN_STATE_ERROR_ACTIVE)) {
|
|
|
+ netdev_dbg(ndev, "Error, entered active state\n");
|
|
|
+ work_done += ifi_canfd_handle_state_change(ndev,
|
|
|
+ CAN_STATE_ERROR_ACTIVE);
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((stcmd & IFI_CANFD_STCMD_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");
|
|
|
work_done += ifi_canfd_handle_state_change(ndev,
|
|
|
CAN_STATE_ERROR_WARNING);
|
|
@@ -554,18 +570,11 @@ static int ifi_canfd_poll(struct napi_struct *napi, int quota)
|
|
|
{
|
|
|
struct net_device *ndev = napi->dev;
|
|
|
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 errctr = readl(priv->base + IFI_CANFD_ERROR_CTR);
|
|
|
+ int work_done = 0;
|
|
|
|
|
|
/* Handle bus state changes */
|
|
|
- if ((stcmd & stcmd_state_mask) ||
|
|
|
- ((stcmd & IFI_CANFD_STCMD_ERROR_ACTIVE) == 0))
|
|
|
- work_done += ifi_canfd_handle_state_errors(ndev, stcmd);
|
|
|
+ work_done += ifi_canfd_handle_state_errors(ndev);
|
|
|
|
|
|
/* Handle lost messages on RX */
|
|
|
if (rxstcmd & IFI_CANFD_RXSTCMD_OVERFLOW)
|
|
@@ -573,7 +582,7 @@ static int ifi_canfd_poll(struct napi_struct *napi, int quota)
|
|
|
|
|
|
/* Handle lec errors on the bus */
|
|
|
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 */
|
|
|
if (!(rxstcmd & IFI_CANFD_RXSTCMD_EMPTY))
|
|
@@ -594,12 +603,13 @@ static irqreturn_t ifi_canfd_isr(int irq, void *dev_id)
|
|
|
struct net_device_stats *stats = &ndev->stats;
|
|
|
const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY |
|
|
|
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_COUNTER;
|
|
|
+ IFI_CANFD_INTERRUPT_ERROR_BUSOFF;
|
|
|
const u32 tx_irq_mask = IFI_CANFD_INTERRUPT_TXFIFO_EMPTY |
|
|
|
IFI_CANFD_INTERRUPT_TXFIFO_REMOVE;
|
|
|
- const u32 clr_irq_mask = ~((u32)(IFI_CANFD_INTERRUPT_SET_IRQ |
|
|
|
- IFI_CANFD_INTERRUPT_ERROR_WARNING));
|
|
|
+ const u32 clr_irq_mask = ~((u32)IFI_CANFD_INTERRUPT_SET_IRQ);
|
|
|
u32 isr;
|
|
|
|
|
|
isr = readl(priv->base + IFI_CANFD_INTERRUPT);
|