mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 03:40:35 +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:
|
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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user