mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 02:50:49 +09:00
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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user