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:
- 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

View File

@@ -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;