summaryrefslogtreecommitdiffstats
path: root/drivers/net/can/c_can/c_can.c
diff options
context:
space:
mode:
authorThomas Gleixner2014-04-11 10:13:15 +0200
committerMarc Kleine-Budde2014-04-24 22:08:57 +0200
commitfa39b54ccf28a0a85256f04881297cd75b8ef204 (patch)
tree6e98a32816a07adfa875f9a9acb8d241221b5634 /drivers/net/can/c_can/c_can.c
parentcan: c_can: Avoid status register update for D_CAN (diff)
downloadkernel-qcow2-linux-fa39b54ccf28a0a85256f04881297cd75b8ef204.tar.gz
kernel-qcow2-linux-fa39b54ccf28a0a85256f04881297cd75b8ef204.tar.xz
kernel-qcow2-linux-fa39b54ccf28a0a85256f04881297cd75b8ef204.zip
can: c_can: Get rid of pointless interrupts
The driver handles pointlessly TWO interrupts per packet. The reason is that it enables the status interrupt which fires for each rx and tx packet and it enables the per message object interrupts as well. The status interrupt merily acks or in case of D_CAN ignores the TX/RX state and then the message object interrupt fires. The message objects interrupts are only useful if all message objects have hardware filters activated. But we don't have that and its not simple to implement in that driver without rewriting it completely. So we can ditch the message object interrupts and handle the RX/TX right away from the status interrupt. Instead of TWO we handle ONE. Note: We must keep the TXIE/RXIE bits in the message buffers because the status interrupt alone is not reliable enough in corner cases. If we ever have the need for HW filtering, then this code needs a complete overhaul and we can think about it then. For now we prefer a lower interrupt load. 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>
Diffstat (limited to 'drivers/net/can/c_can/c_can.c')
-rw-r--r--drivers/net/can/c_can/c_can.c122
1 files changed, 47 insertions, 75 deletions
diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c
index b381f7bfb895..09cb68772737 100644
--- a/drivers/net/can/c_can/c_can.c
+++ b/drivers/net/can/c_can/c_can.c
@@ -593,7 +593,7 @@ static void c_can_configure_msg_objects(struct net_device *dev)
/* setup receive message objects */
for (i = C_CAN_MSG_OBJ_RX_FIRST; i < C_CAN_MSG_OBJ_RX_LAST; i++)
c_can_setup_receive_object(dev, IF_RX, i, 0, 0,
- (IF_MCONT_RXIE | IF_MCONT_UMASK) & ~IF_MCONT_EOB);
+ IF_MCONT_RXIE | IF_MCONT_UMASK);
c_can_setup_receive_object(dev, IF_RX, C_CAN_MSG_OBJ_RX_LAST, 0, 0,
IF_MCONT_EOB | IF_MCONT_RXIE | IF_MCONT_UMASK);
@@ -649,8 +649,9 @@ static int c_can_start(struct net_device *dev)
priv->can.state = CAN_STATE_ERROR_ACTIVE;
- /* reset tx helper pointers */
+ /* reset tx helper pointers and the rx mask */
priv->tx_next = priv->tx_echo = 0;
+ priv->rxmasked = 0;
return 0;
}
@@ -823,9 +824,13 @@ static int c_can_read_objects(struct net_device *dev, struct c_can_priv *priv,
/* read the data from the message object */
c_can_read_msg_object(dev, IF_RX, ctrl);
- if (obj == C_CAN_MSG_RX_LOW_LAST)
+ if (obj < C_CAN_MSG_RX_LOW_LAST)
+ priv->rxmasked |= BIT(obj - 1);
+ else if (obj == C_CAN_MSG_RX_LOW_LAST) {
+ priv->rxmasked = 0;
/* activate all lower message objects */
c_can_activate_all_lower_rx_msg_obj(dev, IF_RX);
+ }
pkts++;
quota--;
@@ -870,7 +875,8 @@ static int c_can_do_rx_poll(struct net_device *dev, int quota)
while (quota > 0) {
if (!pend) {
- pend = priv->read_reg(priv, C_CAN_INTPND1_REG);
+ pend = priv->read_reg(priv, C_CAN_NEWDAT1_REG);
+ pend &= ~priv->rxmasked;
if (!pend)
break;
/*
@@ -1040,10 +1046,6 @@ static int c_can_handle_bus_err(struct net_device *dev,
break;
}
- /* set a `lec` value so that we can check for updates later */
- if (priv->type != BOSCH_D_CAN)
- priv->write_reg(priv, C_CAN_STS_REG, LEC_UNUSED);
-
stats->rx_packets++;
stats->rx_bytes += cf->can_dlc;
netif_receive_skb(skb);
@@ -1052,79 +1054,50 @@ static int c_can_handle_bus_err(struct net_device *dev,
static int c_can_poll(struct napi_struct *napi, int quota)
{
- u16 irqstatus;
- int work_done = 0;
struct net_device *dev = napi->dev;
struct c_can_priv *priv = netdev_priv(dev);
+ u16 curr, last = priv->last_status;
+ int work_done = 0;
- irqstatus = priv->irqstatus;
- if (!irqstatus)
- goto end;
+ priv->last_status = curr = priv->read_reg(priv, C_CAN_STS_REG);
+ /* Ack status on C_CAN. D_CAN is self clearing */
+ if (priv->type != BOSCH_D_CAN)
+ priv->write_reg(priv, C_CAN_STS_REG, LEC_UNUSED);
- /* status events have the highest priority */
- if (irqstatus == STATUS_INTERRUPT) {
- priv->current_status = priv->read_reg(priv,
- C_CAN_STS_REG);
-
- /* handle Tx/Rx events */
- if (priv->current_status & STATUS_TXOK &&
- priv->type != BOSCH_D_CAN)
- priv->write_reg(priv, C_CAN_STS_REG,
- priv->current_status & ~STATUS_TXOK);
-
- if (priv->current_status & STATUS_RXOK &&
- priv->type != BOSCH_D_CAN)
- priv->write_reg(priv, C_CAN_STS_REG,
- priv->current_status & ~STATUS_RXOK);
-
- /* handle state changes */
- if ((priv->current_status & STATUS_EWARN) &&
- (!(priv->last_status & STATUS_EWARN))) {
- netdev_dbg(dev, "entered error warning state\n");
- work_done += c_can_handle_state_change(dev,
- C_CAN_ERROR_WARNING);
- }
- if ((priv->current_status & STATUS_EPASS) &&
- (!(priv->last_status & STATUS_EPASS))) {
- netdev_dbg(dev, "entered error passive state\n");
- work_done += c_can_handle_state_change(dev,
- C_CAN_ERROR_PASSIVE);
- }
- if ((priv->current_status & STATUS_BOFF) &&
- (!(priv->last_status & STATUS_BOFF))) {
- netdev_dbg(dev, "entered bus off state\n");
- work_done += c_can_handle_state_change(dev,
- C_CAN_BUS_OFF);
- goto end;
- }
+ /* handle state changes */
+ if ((curr & STATUS_EWARN) && (!(last & STATUS_EWARN))) {
+ netdev_dbg(dev, "entered error warning state\n");
+ work_done += c_can_handle_state_change(dev, C_CAN_ERROR_WARNING);
+ }
- /* handle bus recovery events */
- if ((!(priv->current_status & STATUS_BOFF)) &&
- (priv->last_status & STATUS_BOFF)) {
- netdev_dbg(dev, "left bus off state\n");
- priv->can.state = CAN_STATE_ERROR_ACTIVE;
- }
- if ((!(priv->current_status & STATUS_EPASS)) &&
- (priv->last_status & STATUS_EPASS)) {
- netdev_dbg(dev, "left error passive state\n");
- priv->can.state = CAN_STATE_ERROR_ACTIVE;
- }
+ if ((curr & STATUS_EPASS) && (!(last & STATUS_EPASS))) {
+ netdev_dbg(dev, "entered error passive state\n");
+ work_done += c_can_handle_state_change(dev, C_CAN_ERROR_PASSIVE);
+ }
- priv->last_status = priv->current_status;
-
- /* handle lec errors on the bus */
- work_done += c_can_handle_bus_err(dev,
- priv->current_status & LEC_MASK);
- } else if ((irqstatus >= C_CAN_MSG_OBJ_RX_FIRST) &&
- (irqstatus <= C_CAN_MSG_OBJ_RX_LAST)) {
- /* handle events corresponding to receive message objects */
- work_done += c_can_do_rx_poll(dev, (quota - work_done));
- } else if ((irqstatus >= C_CAN_MSG_OBJ_TX_FIRST) &&
- (irqstatus <= C_CAN_MSG_OBJ_TX_LAST)) {
- /* handle events corresponding to transmit message objects */
- c_can_do_tx(dev);
+ if ((curr & STATUS_BOFF) && (!(last & STATUS_BOFF))) {
+ netdev_dbg(dev, "entered bus off state\n");
+ work_done += c_can_handle_state_change(dev, C_CAN_BUS_OFF);
+ goto end;
}
+ /* handle bus recovery events */
+ if ((!(curr & STATUS_BOFF)) && (last & STATUS_BOFF)) {
+ netdev_dbg(dev, "left bus off state\n");
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ }
+ if ((!(curr & STATUS_EPASS)) && (last & STATUS_EPASS)) {
+ netdev_dbg(dev, "left error passive state\n");
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ }
+
+ /* handle lec errors on the bus */
+ work_done += c_can_handle_bus_err(dev, curr & LEC_MASK);
+
+ /* Handle Tx/Rx events. We do this unconditionally */
+ work_done += c_can_do_rx_poll(dev, (quota - work_done));
+ c_can_do_tx(dev);
+
end:
if (work_done < quota) {
napi_complete(napi);
@@ -1141,8 +1114,7 @@ static irqreturn_t c_can_isr(int irq, void *dev_id)
struct net_device *dev = (struct net_device *)dev_id;
struct c_can_priv *priv = netdev_priv(dev);
- priv->irqstatus = priv->read_reg(priv, C_CAN_INT_REG);
- if (!priv->irqstatus)
+ if (!priv->read_reg(priv, C_CAN_INT_REG))
return IRQ_NONE;
/* disable all interrupts and schedule the NAPI */