net: can: rockchip: canfd: Support extended frames transmit for rk3568

Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
Change-Id: I097fd66d34b56d9e6104f21da40e5b41bf2b4109
This commit is contained in:
Elaine Zhang
2022-09-23 10:06:01 +08:00
committed by Tao Huang
parent ce59362f58
commit aed0776380
2 changed files with 100 additions and 9 deletions

View File

@@ -4,7 +4,8 @@ Rockchip CANFD controller Device Tree Bindings
Required properties: Required properties:
- compatible : Should be: - compatible : Should be:
- "rockchip,canfd-1.0" for CANFD controllers 1.0 - "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 - reg : Physical base address and size of the controller
registers map. registers map.
- interrupts : Property with a value describing the interrupt - interrupts : Property with a value describing the interrupt

View File

@@ -41,6 +41,7 @@ enum rockchip_canfd_reg {
CAN_TX_ERR_CNT = 0x38, CAN_TX_ERR_CNT = 0x38,
CAN_IDCODE = 0x3c, CAN_IDCODE = 0x3c,
CAN_IDMASK = 0x40, CAN_IDMASK = 0x40,
CAN_TX_CHECK_FIC = 0x50,
CAN_NBTP = 0x100, CAN_NBTP = 0x100,
CAN_DBTP = 0x104, CAN_DBTP = 0x104,
CAN_TDCR = 0x108, CAN_TDCR = 0x108,
@@ -103,6 +104,7 @@ enum rockchip_canfd_reg {
enum { enum {
ROCKCHIP_CANFD_MODE = 0, ROCKCHIP_CANFD_MODE = 0,
ROCKCHIP_CAN_MODE, ROCKCHIP_CAN_MODE,
ROCKCHIP_RK3568_CAN_MODE,
}; };
#define DATE_LENGTH_12_BYTE (0x9) #define DATE_LENGTH_12_BYTE (0x9)
@@ -188,6 +190,10 @@ enum {
#define TX_FD_BRS_ENABLE BIT(4) #define TX_FD_BRS_ENABLE BIT(4)
#define FIFO_ENABLE BIT(0) #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_SHIFT 7
#define FORMAT_MASK (0x1 << FORMAT_SHIFT) #define FORMAT_MASK (0x1 << FORMAT_SHIFT)
@@ -220,6 +226,10 @@ struct rockchip_canfd {
void __iomem *base; void __iomem *base;
u32 irqstatus; u32 irqstatus;
unsigned long mode; 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, 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 = rockchip_canfd_read(rcan, CAN_MODE);
val |= WORK_MODE; val |= WORK_MODE;
if (rcan->mode >= ROCKCHIP_CAN_MODE && rcan->txtorx)
val |= MODE_RXSTX;
rockchip_canfd_write(rcan, CAN_MODE, val); rockchip_canfd_write(rcan, CAN_MODE, val);
netdev_dbg(ndev, "%s MODE=0x%08x\n", __func__, netdev_dbg(ndev, "%s MODE=0x%08x\n", __func__,
@@ -409,9 +421,6 @@ static int rockchip_canfd_start(struct net_device *ndev)
/* Mode */ /* Mode */
val |= MODE_FDOE; val |= MODE_FDOE;
rockchip_canfd_write(rcan, CAN_TXFIC,
rockchip_canfd_read(rcan, CAN_TXFIC) |
TX_FD_ENABLE);
/* Loopback Mode */ /* Loopback Mode */
if (rcan->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) 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 id, dlc;
u32 cmd = CAN_TX0_REQ; u32 cmd = CAN_TX0_REQ;
int i; int i;
unsigned long flags;
if (can_dropped_invalid_skb(ndev, skb)) if (can_dropped_invalid_skb(ndev, skb))
return NETDEV_TX_OK; return NETDEV_TX_OK;
@@ -522,6 +532,30 @@ static int rockchip_canfd_start_xmit(struct sk_buff *skb,
dlc |= TX_FD_BRS_ENABLE; 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_TXID, id);
rockchip_canfd_write(rcan, CAN_TXFIC, dlc); 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, rockchip_canfd_write(rcan, CAN_TXDAT0 + i,
*(u32 *)(cf->data + 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; return NETDEV_TX_OK;
} }
@@ -553,6 +587,23 @@ static int rockchip_canfd_rx(struct net_device *ndev)
for (i = 0; i < 16; i++) for (i = 0; i < 16; i++)
data[i] = rockchip_canfd_read(rcan, CAN_RXFRD); 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 */ /* create zero'ed CAN frame buffer */
if (dlc & FDF_MASK) if (dlc & FDF_MASK)
skb = alloc_canfd_skb(ndev, &cf); 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); skb = alloc_can_skb(ndev, (struct can_frame **)&cf);
if (!skb) { if (!skb) {
stats->rx_dropped++; stats->rx_dropped++;
return 0; return 1;
} }
/* Change CAN data length format to socketCAN data format */ /* 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; TX_LOSTARB_INT | BUS_ERR_INT | BUS_OFF_INT;
u32 isr; u32 isr;
u32 dlc = 0; u32 dlc = 0;
u32 quota, work_done = 0;
isr = rockchip_canfd_read(rcan, CAN_INT); isr = rockchip_canfd_read(rcan, CAN_INT);
if (isr & TX_FINISH_INT) { if (isr & TX_FINISH_INT) {
@@ -679,14 +731,33 @@ static irqreturn_t rockchip_canfd_interrupt(int irq, void *dev_id)
else else
stats->tx_bytes += (dlc & DLC_MASK); stats->tx_bytes += (dlc & DLC_MASK);
stats->tx_packets++; 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); rockchip_canfd_write(rcan, CAN_CMD, 0);
can_get_echo_skb(ndev, 0); can_get_echo_skb(ndev, 0);
netif_wake_queue(ndev); netif_wake_queue(ndev);
can_led_event(ndev, CAN_LED_EVENT_TX); can_led_event(ndev, CAN_LED_EVENT_TX);
} }
if (isr & RX_FINISH_INT) if (isr & RX_FINISH_INT) {
rockchip_canfd_rx(ndev); 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) { if (isr & err_int) {
/* error interrupt */ /* error interrupt */
@@ -861,6 +932,10 @@ static const struct of_device_id rockchip_canfd_of_match[] = {
.compatible = "rockchip,can-2.0", .compatible = "rockchip,can-2.0",
.data = (void *)ROCKCHIP_CAN_MODE .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); 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 */ /* IFI CANFD can do both Bosch FD and ISO FD */
rcan->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | rcan->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
CAN_CTRLMODE_FD; CAN_CTRLMODE_FD;
rcan->rx_fifo_shift = RX_FIFO_CNT0_SHIFT;
rcan->rx_fifo_mask = RX_FIFO_CNT0_MASK;
break; break;
case ROCKCHIP_CAN_MODE: case ROCKCHIP_CAN_MODE:
case ROCKCHIP_RK3568_CAN_MODE:
rcan->can.bittiming_const = &rockchip_canfd_bittiming_const; rcan->can.bittiming_const = &rockchip_canfd_bittiming_const;
rcan->can.do_set_mode = rockchip_canfd_set_mode; rcan->can.do_set_mode = rockchip_canfd_set_mode;
rcan->can.do_get_berr_counter = rockchip_canfd_get_berr_counter; 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_LISTENONLY |
CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LOOPBACK |
CAN_CTRLMODE_3_SAMPLES; CAN_CTRLMODE_3_SAMPLES;
rcan->rx_fifo_shift = RX_FIFO_CNT0_SHIFT;
rcan->rx_fifo_mask = RX_FIFO_CNT0_MASK;
break; break;
default: default:
return -EINVAL; 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->netdev_ops = &rockchip_canfd_netdev_ops;
ndev->irq = irq; ndev->irq = irq;
ndev->flags |= IFF_ECHO; ndev->flags |= IFF_ECHO;