diff --git a/drivers/net/can/rockchip/rockchip_canfd.c b/drivers/net/can/rockchip/rockchip_canfd.c index abdcdb580eff..f616f3b98803 100644 --- a/drivers/net/can/rockchip/rockchip_canfd.c +++ b/drivers/net/can/rockchip/rockchip_canfd.c @@ -214,6 +214,7 @@ enum { #define CAN_RXFRD_OFFSET(n) (CAN_RXFRD + CAN_RF_SIZE * (n)) #define CAN_RX_FILTER_MASK 0x1fffffff +#define NOACK_ERR_FLAG 0xc200800 #define DRV_NAME "rockchip_canfd" @@ -234,6 +235,7 @@ struct rockchip_canfd { bool txtorx; u32 tx_invalid[4]; struct delayed_work tx_err_work; + u32 delay_time_ms; }; static inline u32 rockchip_canfd_read(const struct rockchip_canfd *priv, @@ -357,6 +359,12 @@ static int rockchip_canfd_set_bittiming(struct net_device *ndev) rockchip_canfd_write(rcan, CAN_DBTP, reg_btp); } + if (bt->bitrate > 200000) + rcan->delay_time_ms = 1; + else if (bt->bitrate > 50000) + rcan->delay_time_ms = 5; + else + rcan->delay_time_ms = 20; netdev_dbg(ndev, "%s NBTP=0x%08x, DBTP=0x%08x, TDCR=0x%08x\n", __func__, rockchip_canfd_read(rcan, CAN_NBTP), @@ -487,13 +495,27 @@ static void rockchip_canfd_tx_err_delay_work(struct work_struct *work) { struct rockchip_canfd *rcan = container_of(work, struct rockchip_canfd, tx_err_work.work); - u32 mode; + u32 mode, err_code; mode = rockchip_canfd_read(rcan, CAN_MODE); - rockchip_canfd_write(rcan, CAN_MODE, 0); - rockchip_canfd_write(rcan, CAN_MODE, mode); - rockchip_canfd_write(rcan, CAN_CMD, CAN_TX0_REQ); - schedule_delayed_work(&rcan->tx_err_work, 1); + err_code = rockchip_canfd_read(rcan, CAN_ERR_CODE); + if ((err_code & NOACK_ERR_FLAG) == NOACK_ERR_FLAG) { + rockchip_canfd_write(rcan, CAN_MODE, + rockchip_canfd_read(rcan, CAN_MODE) | MODE_SPACE_RX); + rockchip_canfd_write(rcan, CAN_CMD, CAN_TX0_REQ); + rockchip_canfd_write(rcan, CAN_MODE, + rockchip_canfd_read(rcan, CAN_MODE) & (~MODE_SPACE_RX)); + schedule_delayed_work(&rcan->tx_err_work, msecs_to_jiffies(rcan->delay_time_ms)); + } else { + rockchip_canfd_write(rcan, CAN_MODE, 0); + rockchip_canfd_write(rcan, CAN_MODE, mode); + rockchip_canfd_write(rcan, CAN_MODE, + rockchip_canfd_read(rcan, CAN_MODE) | MODE_SPACE_RX); + rockchip_canfd_write(rcan, CAN_CMD, CAN_TX0_REQ); + rockchip_canfd_write(rcan, CAN_MODE, + rockchip_canfd_read(rcan, CAN_MODE) & (~MODE_SPACE_RX)); + schedule_delayed_work(&rcan->tx_err_work, msecs_to_jiffies(rcan->delay_time_ms)); + } } /* transmit a CAN message @@ -569,10 +591,9 @@ static int rockchip_canfd_start_xmit(struct sk_buff *skb, for (i = 0; i < cf->len; i += 4) rockchip_canfd_write(rcan, CAN_TXDAT0 + i, *(u32 *)(cf->data + i)); + can_put_echo_skb(skb, ndev, 0); rockchip_canfd_write(rcan, CAN_CMD, CAN_TX1_REQ); local_irq_restore(flags); - can_put_echo_skb(skb, ndev, 0); - return NETDEV_TX_OK; } @@ -583,12 +604,13 @@ static int rockchip_canfd_start_xmit(struct sk_buff *skb, rockchip_canfd_write(rcan, CAN_TXDAT0 + i, *(u32 *)(cf->data + i)); - rockchip_canfd_write(rcan, CAN_CMD, cmd); - - schedule_delayed_work(&rcan->tx_err_work, 1); - can_put_echo_skb(skb, ndev, 0); - + rockchip_canfd_write(rcan, CAN_MODE, + rockchip_canfd_read(rcan, CAN_MODE) | MODE_SPACE_RX); + rockchip_canfd_write(rcan, CAN_CMD, cmd); + rockchip_canfd_write(rcan, CAN_MODE, + rockchip_canfd_read(rcan, CAN_MODE) & (~MODE_SPACE_RX)); + schedule_delayed_work(&rcan->tx_err_work, msecs_to_jiffies(rcan->delay_time_ms)); return NETDEV_TX_OK; } @@ -740,9 +762,6 @@ static int rockchip_canfd_err(struct net_device *ndev, u32 isr) cf->data[7] = rxerr; } - if (isr & TX_LOSTARB_INT) - schedule_delayed_work(&rcan->tx_err_work, 1); - if (isr & BUS_OFF_INT) { rcan->can.state = CAN_STATE_BUS_OFF; rcan->can.can_stats.bus_off++; @@ -788,13 +807,14 @@ static irqreturn_t rockchip_canfd_interrupt(int irq, void *dev_id) struct rockchip_canfd *rcan = netdev_priv(ndev); struct net_device_stats *stats = &ndev->stats; u32 err_int = ERR_WARN_INT | RX_BUF_OV_INT | PASSIVE_ERR_INT | - TX_LOSTARB_INT | BUS_ERR_INT | BUS_OFF_INT; + BUS_ERR_INT | BUS_OFF_INT; u32 isr; u32 dlc = 0; u32 quota, work_done = 0; isr = rockchip_canfd_read(rcan, CAN_INT); if (isr & TX_FINISH_INT) { + cancel_delayed_work(&rcan->tx_err_work); dlc = rockchip_canfd_read(rcan, CAN_TXFIC); /* transmission complete interrupt */ if (dlc & FDF_MASK) @@ -802,7 +822,6 @@ static irqreturn_t rockchip_canfd_interrupt(int irq, void *dev_id) else stats->tx_bytes += (dlc & DLC_MASK); stats->tx_packets++; - cancel_delayed_work(&rcan->tx_err_work); if (rcan->txtorx && rcan->mode <= ROCKCHIP_RK3568_CAN_MODE && dlc & FORMAT_MASK) { rockchip_canfd_write(rcan, CAN_TX_CHECK_FIC, FORMAT_MASK); quota = rockchip_canfd_get_rx_fifo_cnt(ndev); @@ -814,6 +833,10 @@ static irqreturn_t rockchip_canfd_interrupt(int irq, void *dev_id) rockchip_canfd_write(rcan, CAN_CMD, CAN_TX1_REQ); rockchip_canfd_write(rcan, CAN_TX_CHECK_FIC, 0); } + if (read_poll_timeout_atomic(rockchip_canfd_read, quota, + !(quota & 0x3), + 0, 5000000, false, rcan, CAN_CMD)) + netdev_err(ndev, "Warning: wait tx req timeout!\n"); rockchip_canfd_write(rcan, CAN_CMD, 0); can_get_echo_skb(ndev, 0); netif_wake_queue(ndev);