Use can FD frames for Tx/Rx operations. This would be needed in upcoming SOC LX2160A, which supports CAN FD protocol Signed-off-by: Pankaj Bansal <pankaj.bansal@xxxxxxx> --- Notes: V3: - introduced fd_enable in rx_offload structure. This boolean indicates if the FD mode is enabled or not. - Based on fd_enable, send the CAN FD or CAN 2.0 frame to mailbox_read. - in mailbox_read, detect the type of frame from fd_enable and fill the values accordingly. - in xmit function, detect the type of SKB received. if CAN FD SKB is detected, tread its data as canfd_frame otherwise can_frame. Then fill the values accordingly. V2: - No change drivers/net/can/flexcan.c | 80 +++++++++++++++++++++++--------- drivers/net/can/rx-offload.c | 35 +++++++++++--- include/linux/can/rx-offload.h | 4 +- 3 files changed, 89 insertions(+), 30 deletions(-) diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index c9edfc808f8f..52b0d19c80f0 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -540,30 +540,48 @@ static int flexcan_get_berr_counter(const struct net_device *dev, static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev) { const struct flexcan_priv *priv = netdev_priv(dev); - struct can_frame *cf = (struct can_frame *)skb->data; + struct can_frame *cf; + struct canfd_frame *cf_fd; u32 can_id; u32 data; - u32 ctrl = FLEXCAN_MB_CODE_TX_DATA | (cf->can_dlc << 16); - u8 can_dlc_dword, i; + u32 ctrl = FLEXCAN_MB_CODE_TX_DATA; + u8 can_dlc_dword, i, *pframe_data; if (can_dropped_invalid_skb(dev, skb)) return NETDEV_TX_OK; netif_stop_queue(dev); - if (cf->can_id & CAN_EFF_FLAG) { - can_id = cf->can_id & CAN_EFF_MASK; - ctrl |= FLEXCAN_MB_CNT_IDE | FLEXCAN_MB_CNT_SRR; + if (can_is_canfd_skb(skb)) { + cf_fd = (struct canfd_frame *)skb->data; + if (cf_fd->can_id & CAN_EFF_FLAG) { + can_id = cf_fd->can_id & CAN_EFF_MASK; + ctrl |= FLEXCAN_MB_CNT_IDE | FLEXCAN_MB_CNT_SRR; + } else { + can_id = (cf_fd->can_id & CAN_SFF_MASK) << 18; + } + + ctrl |= (can_len2dlc(cf_fd->len) << 16); + can_dlc_dword = (cf_fd->len + sizeof(u32) - 1) / sizeof(u32); + pframe_data = cf_fd->data; } else { - can_id = (cf->can_id & CAN_SFF_MASK) << 18; - } + cf = (struct can_frame *)skb->data; + if (cf->can_id & CAN_EFF_FLAG) { + can_id = cf->can_id & CAN_EFF_MASK; + ctrl |= FLEXCAN_MB_CNT_IDE | FLEXCAN_MB_CNT_SRR; + } else { + can_id = (cf->can_id & CAN_SFF_MASK) << 18; + } + if (cf->can_id & CAN_RTR_FLAG) + ctrl |= FLEXCAN_MB_CNT_RTR; - if (cf->can_id & CAN_RTR_FLAG) - ctrl |= FLEXCAN_MB_CNT_RTR; + ctrl |= (cf->can_dlc << 16); + can_dlc_dword = (cf->can_dlc + sizeof(u32) - 1) / sizeof(u32); + pframe_data = cf->data; + } - can_dlc_dword = (cf->can_dlc + sizeof(u32) - 1) / sizeof(u32); for (i = 0; i < can_dlc_dword; i++) { - data = be32_to_cpup((__be32 *)&cf->data[(i * sizeof(u32))]); + data = be32_to_cpup((__be32 *)&pframe_data[(i * sizeof(u32))]); priv->write(data, &priv->tx_mb->data[i]); } @@ -684,15 +702,18 @@ static inline struct flexcan_priv *rx_offload_to_priv(struct can_rx_offload *off } static unsigned int flexcan_mailbox_read(struct can_rx_offload *offload, - struct can_frame *cf, + void *frame, u32 *timestamp, unsigned int n) { struct flexcan_priv *priv = rx_offload_to_priv(offload); struct flexcan_regs __iomem *regs = priv->regs; struct flexcan_mb __iomem *mb; + struct can_frame *cf; + struct canfd_frame *cf_fd; u32 reg_ctrl, reg_id, reg_iflag1; __be32 data; - u8 can_dlc_dword, i; + u8 can_dlc_dword, i, *pframe_data; + canid_t can_id; mb = flexcan_get_mb(priv, n); @@ -727,18 +748,30 @@ static unsigned int flexcan_mailbox_read(struct can_rx_offload *offload, reg_id = priv->read(&mb->can_id); if (reg_ctrl & FLEXCAN_MB_CNT_IDE) - cf->can_id = ((reg_id >> 0) & CAN_EFF_MASK) | CAN_EFF_FLAG; + can_id = ((reg_id >> 0) & CAN_EFF_MASK) | CAN_EFF_FLAG; else - cf->can_id = (reg_id >> 18) & CAN_SFF_MASK; + can_id = (reg_id >> 18) & CAN_SFF_MASK; if (reg_ctrl & FLEXCAN_MB_CNT_RTR) - cf->can_id |= CAN_RTR_FLAG; - cf->can_dlc = get_can_dlc((reg_ctrl >> 16) & 0xf); + can_id |= CAN_RTR_FLAG; + + if (offload->fd_enable) { + cf_fd = (struct canfd_frame *)frame; + cf_fd->can_id = can_id; + cf_fd->len = can_dlc2len((reg_ctrl >> 16) & 0xf); + can_dlc_dword = (cf_fd->len + sizeof(u32) - 1) / sizeof(u32); + pframe_data = cf_fd->data; + } else { + cf = (struct can_frame *)frame; + cf->can_id = can_id; + cf->can_dlc = get_can_dlc((reg_ctrl >> 16) & 0xf); + can_dlc_dword = (cf->can_dlc + sizeof(u32) - 1) / sizeof(u32); + pframe_data = cf->data; + } - can_dlc_dword = (cf->can_dlc + sizeof(u32) - 1) / sizeof(u32); for (i = 0; i < can_dlc_dword; i++) { data = cpu_to_be32(priv->read(&mb->data[i])); - *(__be32 *)(cf->data + (i * sizeof(u32))) = data; + *(__be32 *)(pframe_data + (i * sizeof(u32))) = data; } /* mark as read */ @@ -1152,10 +1185,13 @@ static int flexcan_open(struct net_device *dev) if (err) goto out_close; - if (priv->can.ctrlmode & CAN_CTRLMODE_FD) + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { + priv->offload.fd_enable = true; mb_size = sizeof(struct flexcan_mb) + CANFD_MAX_DLEN; - else + } else { + priv->offload.fd_enable = false; mb_size = sizeof(struct flexcan_mb) + CAN_MAX_DLEN; + } if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) { priv->tx_mb_idx = FLEXCAN_TX_MB_OFF_TIMESTAMP; diff --git a/drivers/net/can/rx-offload.c b/drivers/net/can/rx-offload.c index d94dae216820..18a23f919771 100644 --- a/drivers/net/can/rx-offload.c +++ b/drivers/net/can/rx-offload.c @@ -55,11 +55,19 @@ static int can_rx_offload_napi_poll(struct napi_struct *napi, int quota) while ((work_done < quota) && (skb = skb_dequeue(&offload->skb_queue))) { - struct can_frame *cf = (struct can_frame *)skb->data; + struct can_frame *cf; + struct canfd_frame *cf_fd; + + if (can_is_canfd_skb(skb)) { + cf_fd = (struct canfd_frame *)skb->data; + stats->rx_bytes += cf_fd->len; + } else { + cf = (struct can_frame *)skb->data; + stats->rx_bytes += cf->can_dlc; + } work_done++; stats->rx_packets++; - stats->rx_bytes += cf->can_dlc; netif_receive_skb(skb); } @@ -121,19 +129,29 @@ static struct sk_buff *can_rx_offload_offload_one(struct can_rx_offload *offload struct sk_buff *skb = NULL; struct can_rx_offload_cb *cb; struct can_frame *cf; + struct canfd_frame *cf_fd; int ret; /* If queue is full or skb not available, read to discard mailbox */ if (likely(skb_queue_len(&offload->skb_queue) <= - offload->skb_queue_len_max)) - skb = alloc_can_skb(offload->dev, &cf); + offload->skb_queue_len_max)) { + if (offload->fd_enable) + skb = alloc_canfd_skb(offload->dev, &cf_fd); + else + skb = alloc_can_skb(offload->dev, &cf); + } if (!skb) { struct can_frame cf_overflow; + struct canfd_frame cf_fd_overflow; u32 timestamp; - ret = offload->mailbox_read(offload, &cf_overflow, - ×tamp, n); + if (offload->fd_enable) + ret = offload->mailbox_read(offload, &cf_fd_overflow, + ×tamp, n); + else + ret = offload->mailbox_read(offload, &cf_overflow, + ×tamp, n); if (ret) offload->dev->stats.rx_dropped++; @@ -141,7 +159,10 @@ static struct sk_buff *can_rx_offload_offload_one(struct can_rx_offload *offload } cb = can_rx_offload_get_cb(skb); - ret = offload->mailbox_read(offload, cf, &cb->timestamp, n); + if (offload->fd_enable) + ret = offload->mailbox_read(offload, cf_fd, &cb->timestamp, n); + else + ret = offload->mailbox_read(offload, cf, &cb->timestamp, n); if (!ret) { kfree_skb(skb); return NULL; diff --git a/include/linux/can/rx-offload.h b/include/linux/can/rx-offload.h index cb31683bbe15..306a807ba93f 100644 --- a/include/linux/can/rx-offload.h +++ b/include/linux/can/rx-offload.h @@ -23,7 +23,8 @@ struct can_rx_offload { struct net_device *dev; - unsigned int (*mailbox_read)(struct can_rx_offload *offload, struct can_frame *cf, + unsigned int (*mailbox_read)(struct can_rx_offload *offload, + void *frame, u32 *timestamp, unsigned int mb); struct sk_buff_head skb_queue; @@ -34,6 +35,7 @@ struct can_rx_offload { struct napi_struct napi; + bool fd_enable; bool inc; }; -- 2.17.1 -- To unsubscribe from this list: send the line "unsubscribe linux-can" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html