From aed0776380bd745052f2710933486ca2de887e63 Mon Sep 17 00:00:00 2001 From: Elaine Zhang Date: Fri, 23 Sep 2022 10:06:01 +0800 Subject: [PATCH] net: can: rockchip: canfd: Support extended frames transmit for rk3568 Signed-off-by: Elaine Zhang Change-Id: I097fd66d34b56d9e6104f21da40e5b41bf2b4109 --- .../bindings/net/can/rockchip_canfd.txt | 3 +- drivers/net/can/rockchip/rockchip_canfd.c | 106 ++++++++++++++++-- 2 files changed, 100 insertions(+), 9 deletions(-) diff --git a/Documentation/devicetree/bindings/net/can/rockchip_canfd.txt b/Documentation/devicetree/bindings/net/can/rockchip_canfd.txt index e73a8a6a0fc9..ad96a1501b5d 100644 --- a/Documentation/devicetree/bindings/net/can/rockchip_canfd.txt +++ b/Documentation/devicetree/bindings/net/can/rockchip_canfd.txt @@ -4,7 +4,8 @@ Rockchip CANFD controller Device Tree Bindings Required properties: - compatible : Should be: - "rockchip,canfd-1.0" for CANFD controllers 1.0 - - "rockchip,can-2.0" for CAN controllers 2.0 + - "rockchip,can-2.0" for RK3588 CAN controllers 2.0 + - "rockchip,rk3568-can-2.0" for RK3568 CAN controllers 2.0 - reg : Physical base address and size of the controller registers map. - interrupts : Property with a value describing the interrupt diff --git a/drivers/net/can/rockchip/rockchip_canfd.c b/drivers/net/can/rockchip/rockchip_canfd.c index d6bb345a876c..434a7d45c733 100644 --- a/drivers/net/can/rockchip/rockchip_canfd.c +++ b/drivers/net/can/rockchip/rockchip_canfd.c @@ -41,6 +41,7 @@ enum rockchip_canfd_reg { CAN_TX_ERR_CNT = 0x38, CAN_IDCODE = 0x3c, CAN_IDMASK = 0x40, + CAN_TX_CHECK_FIC = 0x50, CAN_NBTP = 0x100, CAN_DBTP = 0x104, CAN_TDCR = 0x108, @@ -103,6 +104,7 @@ enum rockchip_canfd_reg { enum { ROCKCHIP_CANFD_MODE = 0, ROCKCHIP_CAN_MODE, + ROCKCHIP_RK3568_CAN_MODE, }; #define DATE_LENGTH_12_BYTE (0x9) @@ -188,6 +190,10 @@ enum { #define TX_FD_BRS_ENABLE BIT(4) #define FIFO_ENABLE BIT(0) +#define RX_FIFO_CNT0_SHIFT 4 +#define RX_FIFO_CNT0_MASK (0x7 << RX_FIFO_CNT0_SHIFT) +#define RX_FIFO_CNT1_SHIFT 5 +#define RX_FIFO_CNT1_MASK (0x7 << RX_FIFO_CNT1_SHIFT) #define FORMAT_SHIFT 7 #define FORMAT_MASK (0x1 << FORMAT_SHIFT) @@ -220,6 +226,10 @@ struct rockchip_canfd { void __iomem *base; u32 irqstatus; unsigned long mode; + int rx_fifo_shift; + u32 rx_fifo_mask; + bool txtorx; + u32 tx_invalid[4]; }; static inline u32 rockchip_canfd_read(const struct rockchip_canfd *priv, @@ -281,6 +291,8 @@ static int set_normal_mode(struct net_device *ndev) val = rockchip_canfd_read(rcan, CAN_MODE); val |= WORK_MODE; + if (rcan->mode >= ROCKCHIP_CAN_MODE && rcan->txtorx) + val |= MODE_RXSTX; rockchip_canfd_write(rcan, CAN_MODE, val); netdev_dbg(ndev, "%s MODE=0x%08x\n", __func__, @@ -409,9 +421,6 @@ static int rockchip_canfd_start(struct net_device *ndev) /* Mode */ val |= MODE_FDOE; - rockchip_canfd_write(rcan, CAN_TXFIC, - rockchip_canfd_read(rcan, CAN_TXFIC) | - TX_FD_ENABLE); /* Loopback Mode */ if (rcan->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) @@ -487,6 +496,7 @@ static int rockchip_canfd_start_xmit(struct sk_buff *skb, u32 id, dlc; u32 cmd = CAN_TX0_REQ; int i; + unsigned long flags; if (can_dropped_invalid_skb(ndev, skb)) return NETDEV_TX_OK; @@ -522,6 +532,30 @@ static int rockchip_canfd_start_xmit(struct sk_buff *skb, dlc |= TX_FD_BRS_ENABLE; } + if (!rcan->txtorx && rcan->mode >= ROCKCHIP_CAN_MODE && cf->can_id & CAN_EFF_FLAG) { + /* Two frames are sent consecutively. + * Before the first frame is tx finished, + * the register of the second frame is configured. + * Don't be interrupted in the middle. + */ + local_irq_save(flags); + rockchip_canfd_write(rcan, CAN_TXID, rcan->tx_invalid[1]); + rockchip_canfd_write(rcan, CAN_TXFIC, rcan->tx_invalid[0]); + rockchip_canfd_write(rcan, CAN_TXDAT0, rcan->tx_invalid[2]); + rockchip_canfd_write(rcan, CAN_TXDAT1, rcan->tx_invalid[3]); + rockchip_canfd_write(rcan, CAN_CMD, CAN_TX0_REQ); + rockchip_canfd_write(rcan, CAN_TXID, id); + rockchip_canfd_write(rcan, CAN_TXFIC, dlc); + for (i = 0; i < cf->len; i += 4) + rockchip_canfd_write(rcan, CAN_TXDAT0 + i, + *(u32 *)(cf->data + i)); + rockchip_canfd_write(rcan, CAN_CMD, CAN_TX1_REQ); + local_irq_restore(flags); + can_put_echo_skb(skb, ndev, 0); + + return NETDEV_TX_OK; + } + rockchip_canfd_write(rcan, CAN_TXID, id); rockchip_canfd_write(rcan, CAN_TXFIC, dlc); @@ -529,9 +563,9 @@ static int rockchip_canfd_start_xmit(struct sk_buff *skb, 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); - rockchip_canfd_write(rcan, CAN_CMD, cmd); + can_put_echo_skb(skb, ndev, 0); return NETDEV_TX_OK; } @@ -553,6 +587,23 @@ static int rockchip_canfd_rx(struct net_device *ndev) for (i = 0; i < 16; i++) data[i] = rockchip_canfd_read(rcan, CAN_RXFRD); + if (rcan->mode >= ROCKCHIP_CAN_MODE) { + /* may be an empty frame */ + if (!dlc && !id_rockchip_canfd) + return 1; + + if (rcan->txtorx) { + if (rockchip_canfd_read(rcan, CAN_TX_CHECK_FIC) & FORMAT_MASK) { + ret = rockchip_canfd_read(rcan, CAN_TXID) & CAN_SFF_MASK; + if (id_rockchip_canfd == ret) { + rockchip_canfd_write(rcan, CAN_TX_CHECK_FIC, + ts | CAN_TX0_REQ); + return 1; + } + } + } + } + /* create zero'ed CAN frame buffer */ if (dlc & FDF_MASK) skb = alloc_canfd_skb(ndev, &cf); @@ -560,7 +611,7 @@ static int rockchip_canfd_rx(struct net_device *ndev) skb = alloc_can_skb(ndev, (struct can_frame **)&cf); if (!skb) { stats->rx_dropped++; - return 0; + return 1; } /* Change CAN data length format to socketCAN data format */ @@ -669,6 +720,7 @@ static irqreturn_t rockchip_canfd_interrupt(int irq, void *dev_id) TX_LOSTARB_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) { @@ -679,14 +731,33 @@ static irqreturn_t rockchip_canfd_interrupt(int irq, void *dev_id) else stats->tx_bytes += (dlc & DLC_MASK); stats->tx_packets++; + if (rcan->txtorx && rcan->mode >= ROCKCHIP_CAN_MODE && dlc & FORMAT_MASK) { + rockchip_canfd_write(rcan, CAN_TX_CHECK_FIC, FORMAT_MASK); + quota = (rockchip_canfd_read(rcan, CAN_RXFC) & + rcan->rx_fifo_mask) >> + rcan->rx_fifo_shift; + if (quota) { + while (work_done < quota) + work_done += rockchip_canfd_rx(ndev); + } + if (rockchip_canfd_read(rcan, CAN_TX_CHECK_FIC) & CAN_TX0_REQ) + rockchip_canfd_write(rcan, CAN_CMD, CAN_TX1_REQ); + rockchip_canfd_write(rcan, CAN_TX_CHECK_FIC, 0); + } rockchip_canfd_write(rcan, CAN_CMD, 0); can_get_echo_skb(ndev, 0); netif_wake_queue(ndev); can_led_event(ndev, CAN_LED_EVENT_TX); } - if (isr & RX_FINISH_INT) - rockchip_canfd_rx(ndev); + if (isr & RX_FINISH_INT) { + quota = (rockchip_canfd_read(rcan, CAN_RXFC) & rcan->rx_fifo_mask) >> + rcan->rx_fifo_shift; + if (quota) { + while (work_done < quota) + work_done += rockchip_canfd_rx(ndev); + } + } if (isr & err_int) { /* error interrupt */ @@ -861,6 +932,10 @@ static const struct of_device_id rockchip_canfd_of_match[] = { .compatible = "rockchip,can-2.0", .data = (void *)ROCKCHIP_CAN_MODE }, + { + .compatible = "rockchip,rk3568-can-2.0", + .data = (void *)ROCKCHIP_RK3568_CAN_MODE + }, {}, }; MODULE_DEVICE_TABLE(of, rockchip_canfd_of_match); @@ -927,8 +1002,11 @@ static int rockchip_canfd_probe(struct platform_device *pdev) /* IFI CANFD can do both Bosch FD and ISO FD */ rcan->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_FD; + rcan->rx_fifo_shift = RX_FIFO_CNT0_SHIFT; + rcan->rx_fifo_mask = RX_FIFO_CNT0_MASK; break; case ROCKCHIP_CAN_MODE: + case ROCKCHIP_RK3568_CAN_MODE: rcan->can.bittiming_const = &rockchip_canfd_bittiming_const; rcan->can.do_set_mode = rockchip_canfd_set_mode; rcan->can.do_get_berr_counter = rockchip_canfd_get_berr_counter; @@ -936,11 +1014,23 @@ static int rockchip_canfd_probe(struct platform_device *pdev) CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_3_SAMPLES; + rcan->rx_fifo_shift = RX_FIFO_CNT0_SHIFT; + rcan->rx_fifo_mask = RX_FIFO_CNT0_MASK; break; default: return -EINVAL; } + if (rcan->mode == ROCKCHIP_CAN_MODE) { + rcan->rx_fifo_shift = RX_FIFO_CNT1_SHIFT; + rcan->rx_fifo_mask = RX_FIFO_CNT1_MASK; + } + + if (device_property_read_u32_array(&pdev->dev, + "rockchip,tx-invalid-info", + rcan->tx_invalid, 4)) + rcan->txtorx = 1; + ndev->netdev_ops = &rockchip_canfd_netdev_ops; ndev->irq = irq; ndev->flags |= IFF_ECHO;