diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2014-04-11 04:13:22 -0400 |
---|---|---|
committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2014-04-24 16:09:01 -0400 |
commit | 939415973fdfb2c16a474e2575ba2581b828ccac (patch) | |
tree | b1103f78dc0d410d4a5129a147e7311a1e8da2ea | |
parent | 35bdafb576c5c0a06815e7a681571c3ab950ff7e (diff) |
can: c_can: Speed up tx buffer invalidation
It's suffcient to kill the TXIE bit in the message control register
even if the documentation of C and D CAN says that it's not allowed to
do that while MSGVAL is set. Reality tells a different story and this
change gives us another 2% of CPU back for not waiting on I/O.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Alexander Stein <alexander.stein@systec-electronic.com>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
-rw-r--r-- | drivers/net/can/c_can/c_can.c | 54 | ||||
-rw-r--r-- | drivers/net/can/c_can/c_can.h | 1 |
2 files changed, 40 insertions, 15 deletions
diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c index 99d36bf1ba21..a2ca820b5373 100644 --- a/drivers/net/can/c_can/c_can.c +++ b/drivers/net/can/c_can/c_can.c | |||
@@ -276,11 +276,34 @@ static inline void c_can_object_put(struct net_device *dev, int iface, | |||
276 | c_can_obj_update(dev, iface, cmd | IF_COMM_WR, obj); | 276 | c_can_obj_update(dev, iface, cmd | IF_COMM_WR, obj); |
277 | } | 277 | } |
278 | 278 | ||
279 | /* | ||
280 | * Note: According to documentation clearing TXIE while MSGVAL is set | ||
281 | * is not allowed, but works nicely on C/DCAN. And that lowers the I/O | ||
282 | * load significantly. | ||
283 | */ | ||
284 | static void c_can_inval_tx_object(struct net_device *dev, int iface, int obj) | ||
285 | { | ||
286 | struct c_can_priv *priv = netdev_priv(dev); | ||
287 | |||
288 | priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), 0); | ||
289 | c_can_object_put(dev, iface, obj, IF_COMM_INVAL); | ||
290 | } | ||
291 | |||
292 | static void c_can_inval_msg_object(struct net_device *dev, int iface, int obj) | ||
293 | { | ||
294 | struct c_can_priv *priv = netdev_priv(dev); | ||
295 | |||
296 | priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), 0); | ||
297 | priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), 0); | ||
298 | c_can_inval_tx_object(dev, iface, obj); | ||
299 | } | ||
300 | |||
279 | static void c_can_setup_tx_object(struct net_device *dev, int iface, | 301 | static void c_can_setup_tx_object(struct net_device *dev, int iface, |
280 | struct can_frame *frame, int obj) | 302 | struct can_frame *frame, int idx) |
281 | { | 303 | { |
282 | struct c_can_priv *priv = netdev_priv(dev); | 304 | struct c_can_priv *priv = netdev_priv(dev); |
283 | u16 ctrl = IF_MCONT_TX | frame->can_dlc; | 305 | u16 ctrl = IF_MCONT_TX | frame->can_dlc; |
306 | bool rtr = frame->can_id & CAN_RTR_FLAG; | ||
284 | u32 arb = IF_ARB_MSGVAL; | 307 | u32 arb = IF_ARB_MSGVAL; |
285 | int i; | 308 | int i; |
286 | 309 | ||
@@ -291,9 +314,20 @@ static void c_can_setup_tx_object(struct net_device *dev, int iface, | |||
291 | arb |= (frame->can_id & CAN_SFF_MASK) << 18; | 314 | arb |= (frame->can_id & CAN_SFF_MASK) << 18; |
292 | } | 315 | } |
293 | 316 | ||
294 | if (!(frame->can_id & CAN_RTR_FLAG)) | 317 | if (!rtr) |
295 | arb |= IF_ARB_TRANSMIT; | 318 | arb |= IF_ARB_TRANSMIT; |
296 | 319 | ||
320 | /* | ||
321 | * If we change the DIR bit, we need to invalidate the buffer | ||
322 | * first, i.e. clear the MSGVAL flag in the arbiter. | ||
323 | */ | ||
324 | if (rtr != (bool)test_bit(idx, &priv->tx_dir)) { | ||
325 | u32 obj = idx + C_CAN_MSG_OBJ_TX_FIRST; | ||
326 | |||
327 | c_can_inval_msg_object(dev, iface, obj); | ||
328 | change_bit(idx, &priv->tx_dir); | ||
329 | } | ||
330 | |||
297 | priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), arb); | 331 | priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), arb); |
298 | priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), arb >> 16); | 332 | priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), arb >> 16); |
299 | 333 | ||
@@ -401,17 +435,6 @@ static void c_can_setup_receive_object(struct net_device *dev, int iface, | |||
401 | c_can_object_put(dev, iface, obj, IF_COMM_RCV_SETUP); | 435 | c_can_object_put(dev, iface, obj, IF_COMM_RCV_SETUP); |
402 | } | 436 | } |
403 | 437 | ||
404 | static void c_can_inval_msg_object(struct net_device *dev, int iface, int obj) | ||
405 | { | ||
406 | struct c_can_priv *priv = netdev_priv(dev); | ||
407 | |||
408 | priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), 0); | ||
409 | priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), 0); | ||
410 | priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), 0); | ||
411 | |||
412 | c_can_object_put(dev, iface, obj, IF_COMM_INVAL); | ||
413 | } | ||
414 | |||
415 | static netdev_tx_t c_can_start_xmit(struct sk_buff *skb, | 438 | static netdev_tx_t c_can_start_xmit(struct sk_buff *skb, |
416 | struct net_device *dev) | 439 | struct net_device *dev) |
417 | { | 440 | { |
@@ -436,7 +459,7 @@ static netdev_tx_t c_can_start_xmit(struct sk_buff *skb, | |||
436 | * can_put_echo_skb(). We must do this before we enable | 459 | * can_put_echo_skb(). We must do this before we enable |
437 | * transmit as we might race against do_tx(). | 460 | * transmit as we might race against do_tx(). |
438 | */ | 461 | */ |
439 | c_can_setup_tx_object(dev, IF_TX, frame, obj); | 462 | c_can_setup_tx_object(dev, IF_TX, frame, idx); |
440 | priv->dlc[idx] = frame->can_dlc; | 463 | priv->dlc[idx] = frame->can_dlc; |
441 | can_put_echo_skb(skb, dev, idx); | 464 | can_put_echo_skb(skb, dev, idx); |
442 | 465 | ||
@@ -563,6 +586,7 @@ static int c_can_chip_config(struct net_device *dev) | |||
563 | /* Clear all internal status */ | 586 | /* Clear all internal status */ |
564 | atomic_set(&priv->tx_active, 0); | 587 | atomic_set(&priv->tx_active, 0); |
565 | priv->rxmasked = 0; | 588 | priv->rxmasked = 0; |
589 | priv->tx_dir = 0; | ||
566 | 590 | ||
567 | /* set bittiming params */ | 591 | /* set bittiming params */ |
568 | return c_can_set_bittiming(dev); | 592 | return c_can_set_bittiming(dev); |
@@ -654,7 +678,7 @@ static void c_can_do_tx(struct net_device *dev) | |||
654 | idx--; | 678 | idx--; |
655 | pend &= ~(1 << idx); | 679 | pend &= ~(1 << idx); |
656 | obj = idx + C_CAN_MSG_OBJ_TX_FIRST; | 680 | obj = idx + C_CAN_MSG_OBJ_TX_FIRST; |
657 | c_can_inval_msg_object(dev, IF_RX, obj); | 681 | c_can_inval_tx_object(dev, IF_RX, obj); |
658 | can_get_echo_skb(dev, idx); | 682 | can_get_echo_skb(dev, idx); |
659 | bytes += priv->dlc[idx]; | 683 | bytes += priv->dlc[idx]; |
660 | pkts++; | 684 | pkts++; |
diff --git a/drivers/net/can/c_can/c_can.h b/drivers/net/can/c_can/c_can.h index a5f10a01e49f..e7de2b9f26bb 100644 --- a/drivers/net/can/c_can/c_can.h +++ b/drivers/net/can/c_can/c_can.h | |||
@@ -174,6 +174,7 @@ struct c_can_priv { | |||
174 | struct net_device *dev; | 174 | struct net_device *dev; |
175 | struct device *device; | 175 | struct device *device; |
176 | atomic_t tx_active; | 176 | atomic_t tx_active; |
177 | unsigned long tx_dir; | ||
177 | int last_status; | 178 | int last_status; |
178 | u16 (*read_reg) (struct c_can_priv *priv, enum reg index); | 179 | u16 (*read_reg) (struct c_can_priv *priv, enum reg index); |
179 | void (*write_reg) (struct c_can_priv *priv, enum reg index, u16 val); | 180 | void (*write_reg) (struct c_can_priv *priv, enum reg index, u16 val); |