Skip to content

Commit

Permalink
can: m_can: Batch FIFO reads during CAN receive
Browse files Browse the repository at this point in the history
On peripherals communicating over a relatively slow SPI line
(e.g. tcan4x5x), individual transfers have high fixed costs.
This causes the driver to spend most of its time waiting between
transfers and severely limits throughput.

Reduce these overheads by reading more than one word at a time.
Writing could get a similar treatment in follow-on commits.

Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Matt Kline <[email protected]>
[mkl: remove __packed from struct id_and_dlc]
Signed-off-by: Marc Kleine-Budde <[email protected]>
  • Loading branch information
mrkline authored and marckleinebudde committed Aug 19, 2021
1 parent e393817 commit 1aa6772
Showing 1 changed file with 28 additions and 23 deletions.
51 changes: 28 additions & 23 deletions drivers/net/can/m_can/m_can.c
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,15 @@ enum m_can_reg {
#define TX_EVENT_MM_MASK GENMASK(31, 24)
#define TX_EVENT_TXTS_MASK GENMASK(15, 0)

/* The ID and DLC registers are adjacent in M_CAN FIFO memory,
* and we can save a (potentially slow) bus round trip by combining
* reads and writes to them.
*/
struct id_and_dlc {
u32 id;
u32 dlc;
};

static inline u32 m_can_read(struct m_can_classdev *cdev, enum m_can_reg reg)
{
return cdev->ops->read_reg(cdev, reg);
Expand Down Expand Up @@ -464,17 +473,18 @@ static int m_can_read_fifo(struct net_device *dev, u32 rxfs)
struct m_can_classdev *cdev = netdev_priv(dev);
struct canfd_frame *cf;
struct sk_buff *skb;
u32 id, fgi, dlc;
struct id_and_dlc fifo_header;
u32 fgi;
u32 timestamp = 0;
int i, err;
int err;

/* calculate the fifo get index for where to read data */
fgi = FIELD_GET(RXFS_FGI_MASK, rxfs);
err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_DLC, &dlc, 1);
err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_ID, &fifo_header, 2);
if (err)
goto out_fail;

if (dlc & RX_BUF_FDF)
if (fifo_header.dlc & RX_BUF_FDF)
skb = alloc_canfd_skb(dev, &cf);
else
skb = alloc_can_skb(dev, (struct can_frame **)&cf);
Expand All @@ -483,36 +493,31 @@ static int m_can_read_fifo(struct net_device *dev, u32 rxfs)
return 0;
}

if (dlc & RX_BUF_FDF)
cf->len = can_fd_dlc2len((dlc >> 16) & 0x0F);
if (fifo_header.dlc & RX_BUF_FDF)
cf->len = can_fd_dlc2len((fifo_header.dlc >> 16) & 0x0F);
else
cf->len = can_cc_dlc2len((dlc >> 16) & 0x0F);

err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_ID, &id, 1);
if (err)
goto out_fail;
cf->len = can_cc_dlc2len((fifo_header.dlc >> 16) & 0x0F);

if (id & RX_BUF_XTD)
cf->can_id = (id & CAN_EFF_MASK) | CAN_EFF_FLAG;
if (fifo_header.id & RX_BUF_XTD)
cf->can_id = (fifo_header.id & CAN_EFF_MASK) | CAN_EFF_FLAG;
else
cf->can_id = (id >> 18) & CAN_SFF_MASK;
cf->can_id = (fifo_header.id >> 18) & CAN_SFF_MASK;

if (id & RX_BUF_ESI) {
if (fifo_header.id & RX_BUF_ESI) {
cf->flags |= CANFD_ESI;
netdev_dbg(dev, "ESI Error\n");
}

if (!(dlc & RX_BUF_FDF) && (id & RX_BUF_RTR)) {
if (!(fifo_header.dlc & RX_BUF_FDF) && (fifo_header.id & RX_BUF_RTR)) {
cf->can_id |= CAN_RTR_FLAG;
} else {
if (dlc & RX_BUF_BRS)
if (fifo_header.dlc & RX_BUF_BRS)
cf->flags |= CANFD_BRS;

for (i = 0; i < cf->len; i += 4) {
err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_DATA(i / 4), cf->data + i, 1);
if (err)
goto out_fail;
}
err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_DATA(0),
cf->data, DIV_ROUND_UP(cf->len, 4));
if (err)
goto out_fail;
}

/* acknowledge rx fifo 0 */
Expand All @@ -521,7 +526,7 @@ static int m_can_read_fifo(struct net_device *dev, u32 rxfs)
stats->rx_packets++;
stats->rx_bytes += cf->len;

timestamp = FIELD_GET(RX_BUF_RXTS_MASK, dlc);
timestamp = FIELD_GET(RX_BUF_RXTS_MASK, fifo_header.dlc);

m_can_receive_skb(cdev, skb, timestamp);

Expand Down

0 comments on commit 1aa6772

Please sign in to comment.